]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/jdb/ia32/jdb_bp-ia32-ux.cpp
update
[l4.git] / kernel / fiasco / src / jdb / ia32 / jdb_bp-ia32-ux.cpp
1 INTERFACE[ia32,amd64,ux]:
2
3 #include "initcalls.h"
4 #include "l4_types.h"
5
6 class Thread;
7 class Task;
8 class Space;
9
10 class Breakpoint
11 {
12 public:
13   enum Mode { INSTRUCTION=0, WRITE=1, PORTIO=2, ACCESS=3 };
14   enum Log  { BREAK=0, LOG=1 };
15
16 private:
17   typedef struct 
18     {
19       int other;
20       Mword thread; 
21     } Bp_thread_res;
22
23   typedef struct
24     {
25       int other;
26       Mword task;
27     } Bp_task_res;
28
29   typedef struct 
30     {
31       char reg;
32       Address y, z;
33     } Bp_reg_res;
34
35   typedef struct 
36     {
37       unsigned char len;
38       Address addr;
39       Address y, z;
40     } Bp_mem_res;
41   
42   typedef struct 
43     {
44       Bp_thread_res thread;
45       Bp_task_res   task;
46       Bp_reg_res    reg;
47       Bp_mem_res    mem;
48     } Restriction;
49
50   Address      addr;
51   Unsigned8    len;
52   Address_type user;
53   Mode         mode;
54   Log          log;
55   Restriction  restrict;
56   static char const * const mode_names[4];
57 };
58
59 class Jdb_bp 
60 {
61 public:
62   static int            global_breakpoints();
63   static void           init_arch();
64
65 private:
66   static void           at_jdb_enter();
67   static void           at_jdb_leave();
68   static Breakpoint     bps[4];
69 };
70
71
72 IMPLEMENTATION[ia32,amd64,ux]:
73
74 #include <cstdio>
75
76 #include "jdb.h"
77 #include "jdb_input.h"
78 #include "jdb_module.h"
79 #include "jdb_handler_queue.h"
80 #include "jdb_screen.h"
81 #include "jdb_tbuf.h"
82 #include "l4_types.h"
83 #include "static_init.h"
84 #include "thread.h"
85
86 class Jdb_set_bp : public Jdb_module, public Jdb_input_task_addr
87 {
88 public:
89   Jdb_set_bp() FIASCO_INIT;
90 private:
91   static char     breakpoint_cmd;
92   static char     breakpoint_restrict_cmd;
93   static Mword    breakpoint_number;
94   static Mword    breakpoint_length;
95   static Mword    breakpoint_restrict_task;
96   static Mword    breakpoint_restrict_thread;
97   typedef struct
98     {
99       char        reg;
100       Mword       low;
101       Mword       high;
102     } Restrict_reg;
103   static Restrict_reg breakpoint_restrict_reg;
104   typedef struct
105     {
106       Address     addr;
107       Mword       low;
108       Mword       high;
109     } Restrict_addr;
110   static Restrict_addr breakpoint_restrict_addr;
111   static int      state;
112 };
113
114 Breakpoint Jdb_bp::bps[4];
115
116 char const * const Breakpoint::mode_names[4] =
117 {
118   "instruction", "write access", "i/o access", "r/w access"
119 };
120
121 char     Jdb_set_bp::breakpoint_cmd;
122 char     Jdb_set_bp::breakpoint_restrict_cmd;
123 Mword    Jdb_set_bp::breakpoint_number;
124 Mword    Jdb_set_bp::breakpoint_length;
125 Mword    Jdb_set_bp::breakpoint_restrict_task;
126 Mword    Jdb_set_bp::breakpoint_restrict_thread;
127 Jdb_set_bp::Restrict_reg  Jdb_set_bp::breakpoint_restrict_reg;
128 Jdb_set_bp::Restrict_addr Jdb_set_bp::breakpoint_restrict_addr;
129 int      Jdb_set_bp::state;
130
131 PUBLIC
132 Breakpoint::Breakpoint()
133 {
134   restrict.thread.thread = 0;
135   restrict.task.task     = 0;
136 }
137
138 PUBLIC inline NOEXPORT
139 void
140 Breakpoint::kill()
141 {
142   addr = 0;
143 }
144
145 PUBLIC inline NOEXPORT
146 int
147 Breakpoint::unused()
148 {
149   return addr == 0;
150 }
151
152 PUBLIC inline NOEXPORT
153 int
154 Breakpoint::break_at_instruction()
155 {
156   return mode == INSTRUCTION;
157 }
158
159 PUBLIC inline NOEXPORT
160 int
161 Breakpoint::match_addr(Address virt, Mode m)
162 {
163   return !unused() && addr == virt && mode == m;
164 }
165
166 PUBLIC inline NOEXPORT
167 void
168 Breakpoint::set_logmode(char m)
169 {
170   log = (m == '*') ? LOG : BREAK;
171 }
172
173 PUBLIC inline NOEXPORT
174 int
175 Breakpoint::is_break()
176 {
177   return !unused() && log == BREAK;
178 }
179
180 PUBLIC inline
181 void
182 Breakpoint::restrict_task(int other, Mword task)
183 {
184   restrict.task.other = other;
185   restrict.task.task  = task;
186 }
187
188 PUBLIC inline NOEXPORT
189 void
190 Breakpoint::restrict_thread(int other, Mword thread)
191 {
192   restrict.thread.other  = other;
193   restrict.thread.thread = thread;
194 }
195
196 PUBLIC inline NOEXPORT
197 void
198 Breakpoint::restrict_register(char reg, Mword y, Mword z)
199 {
200   restrict.reg.reg = reg;
201   restrict.reg.y   = y;
202   restrict.reg.z   = z;
203 }
204
205 PUBLIC inline NOEXPORT
206 void
207 Breakpoint::restrict_memory(Mword addr, Mword len, Mword y, Mword z)
208 {
209   restrict.mem.addr = addr;
210   restrict.mem.len  = len;
211   restrict.mem.y    = y;
212   restrict.mem.z    = z;
213 }
214
215 PUBLIC inline NOEXPORT
216 void 
217 Breakpoint::clear_restriction()
218 {
219   restrict.thread.thread = 0;
220   restrict.task.task     = 0;
221   restrict.reg.reg       = 0;
222   restrict.mem.len       = 0;
223 }
224
225 PUBLIC
226 void
227 Breakpoint::show()
228 {
229   if (addr)
230     {
231       printf("%5s on %12s at " L4_PTR_FMT,
232              log ? "LOG" : "BREAK", mode_names[mode & 3], addr);
233       if (mode != INSTRUCTION)
234         printf(" len %d", len);
235       else
236         putstr("      ");
237
238       if (   restrict.thread.thread == 0
239           && restrict.task.task == 0
240           && restrict.reg.reg == 0
241           && restrict.mem.len == 0)
242         puts(" (not restricted)");
243       else
244         {
245           int j = 0;
246 #if 0
247           printf("\n%32s", "restricted to ");
248           if (restrict.thread.thread != (GThread_num)-1)
249             {
250               j++;
251               printf("thread%s %x.%x\n",
252                      restrict.thread.other ? " !=" : "",
253                      L4_uid::task_from_gthread (restrict.thread.thread),
254                      L4_uid::lthread_from_gthread (restrict.thread.thread));
255             }
256           if (restrict.task.task)
257             {
258               if (j++)
259                 printf("%32s", "and ");
260               printf("task%s %p\n",
261                      restrict.task.other ? " !=" : "",
262                      restrict.task.task);
263             }
264 #endif
265           if (restrict.reg.reg != 0)
266             {
267               if (j++)
268                 printf("%32s", "and ");
269               printf("register %s in ["L4_PTR_FMT", "L4_PTR_FMT"]\n",
270                   (restrict.reg.reg > 0) && (restrict.reg.reg < 10) 
271                       ? Jdb_screen::Reg_names[restrict.reg.reg-1] 
272                       : "???",
273                    restrict.reg.y, restrict.reg.z);
274             }
275           if (restrict.mem.len != 0)
276             {
277               if (j++)
278                 printf("%32s", "and ");
279               printf("%d-byte var at "L4_PTR_FMT" in ["L4_PTR_FMT", "
280                      L4_PTR_FMT"]\n", 
281                   restrict.mem.len, restrict.mem.addr, 
282                   restrict.mem.y,   restrict.mem.z);
283             }
284         }
285     }
286   else
287     puts("disabled");
288 }
289
290 // return TRUE  if the breakpoint does NOT match
291 // return FALSE if all restrictions do match
292 PUBLIC
293 int
294 Breakpoint::restricted(Thread *t)
295 {
296   Jdb_entry_frame *e = Jdb::get_entry_frame(Jdb::current_cpu);
297
298   Space *task = t->space();
299 #if 0
300   // investigate for thread restriction
301   if (restrict.thread.thread != (GThread_num)-1)
302     {
303       if (restrict.thread.other ^ (restrict.thread.thread != t->id().gthread()))
304         return 1;
305     }
306
307   // investigate for task restriction
308   if (restrict.task.task)
309     {
310       if (restrict.task.other ^ (restrict.task.task != task))
311         return 1;
312     }
313 #endif
314   // investigate for register restriction
315   if (restrict.reg.reg)
316     {
317       Mword val = e->get_reg(restrict.reg.reg);
318       Mword y   = restrict.reg.y;
319       Mword z   = restrict.reg.z;
320
321       // return true if rules do NOT match
322       if (  (y <= z && (val <  y || val >  z))
323           ||(y >  z && (val >= z || val <= y)))
324         return 1;
325     }
326
327   // investigate for variable restriction
328   if (restrict.mem.len)
329     {
330       Mword val = 0;
331       Mword y   = restrict.mem.y;
332       Mword z   = restrict.mem.z;
333
334       if (Jdb::peek_task(restrict.mem.addr, task, &val, restrict.mem.len) != 0)
335         return 0;
336
337       // return true if rules do NOT match
338       if (  (y <= z && (val <  y || val >  z))
339           ||(y >  z && (val >= z || val <= y)))
340         return 1;
341     }
342
343   return 0;
344 }
345
346 PUBLIC
347 int
348 Breakpoint::test_break(Thread *t, char *errbuf, size_t bufsize)
349 {
350   if (restricted(t))
351     return 0;
352
353   Space *task = t->space();
354
355   snprintf(errbuf, bufsize, "break on %s at " L4_PTR_FMT, 
356            mode_names[mode], addr);
357   if (mode==WRITE || mode==ACCESS)
358     {
359       // If it's a write or access (read) breakpoint, we look at the
360       // appropriate place and print the bytes we find there. We do
361       // not need to look if the page is present because the x86 CPU
362       // enters the debug exception immediately _after_ the memory
363       // access was performed.
364       size_t size = strlen(errbuf);
365       errbuf  += size;
366       bufsize -= size;
367       Mword val = 0;
368       if (len > sizeof(Mword))
369         return 0;
370
371       if (Jdb::peek_task(addr, task, &val, len) != 0)
372         return 0;
373
374       snprintf(errbuf, bufsize, " [%08lx]", val);
375     }
376   return 1;
377 }
378
379 // Create log entry if breakpoint matches
380 PUBLIC
381 void
382 Breakpoint::test_log(Thread *t)
383 {
384   Jdb_entry_frame *e = Jdb::get_entry_frame(Jdb::current_cpu);
385
386   if (log && !restricted(t))
387     {
388       Space *task = t->space();
389       // log breakpoint
390       Mword value = 0;
391
392       if (mode == WRITE || mode == ACCESS)
393         {
394           // If it's a write or access (read) breakpoint, we look at the
395           // appropriate place and print the bytes we find there. We do
396           // not need to look if the page is present because the x86 CPU
397           // enters the debug exception immediately _after_ the memory
398           // access was performed.
399           if (len > sizeof(Mword))
400             return;
401
402           if (Jdb::peek_task(addr, task, &value, len) != 0)
403             return;
404         }
405
406       // is called with disabled interrupts
407       Tb_entry_bp *tb = static_cast<Tb_entry_bp*>(Jdb_tbuf::new_entry());
408       tb->set(t, e->ip(), mode, len, value, addr);
409       Jdb_tbuf::commit_entry();
410     }
411 }
412
413
414 STATIC_INITIALIZE_P(Jdb_bp, JDB_MODULE_INIT_PRIO);
415
416 PUBLIC static FIASCO_INIT
417 void
418 Jdb_bp::init()
419 {
420   static Jdb_handler enter(at_jdb_enter);
421   static Jdb_handler leave(at_jdb_leave);
422
423   Jdb::jdb_enter.add(&enter);
424   Jdb::jdb_leave.add(&leave);
425
426   init_arch();
427 }
428
429 static inline
430 void
431 Jdb_bp::clr_dr7(int num, Mword &dr7)
432 {
433   dr7 &= ~(((3 + (3 <<2)) << (16 + 4*num)) + (3 << (2*num)));
434 }
435
436 static inline
437 void
438 Jdb_bp::set_dr7(int num, Mword len, Breakpoint::Mode mode, Mword &dr7)
439 {
440   dr7 |= ((((mode & 3) + ((len-1)<<2)) << (16 + 4*num)) + (2 << (2*num)));
441   dr7 |= 0x200; /* exact breakpoint enable (not available on P6 and below) */
442 }
443
444 PUBLIC static
445 int
446 Jdb_bp::set_breakpoint(int num, Address addr, Mword len,
447                        Breakpoint::Mode mode, Breakpoint::Log log,
448                        Task *task)
449 {
450   if (set_debug_address_register(num, addr, len, mode, task))
451     {
452       bps[num].set(addr, len, mode, log);
453       return 1;
454     }
455
456   return 0;
457 }
458
459 PUBLIC static 
460 void 
461 Jdb_bp::clr_breakpoint(int num)
462 {
463   clr_debug_address_register(num);
464   bps[num].kill();
465 }
466
467 PUBLIC static inline NOEXPORT
468 void
469 Jdb_bp::logmode_breakpoint(int num, char mode)
470 {
471   bps[num].set_logmode(mode);
472 }
473
474 PUBLIC static
475 int
476 Jdb_bp::first_unused()
477 {
478   int i;
479
480   for (i=0; i<4 && !bps[i].unused(); i++)
481     ;
482
483   return i;
484 }
485
486 // Return 1 if a breakpoint hits
487 PUBLIC static
488 int
489 Jdb_bp::test_break(Mword dr6, char *errbuf, size_t bufsize)
490 {
491   Thread *t = Jdb::get_thread(0);
492   Jdb_entry_frame *e = Jdb::get_entry_frame(0);
493
494   for (int i=0; i<4; i++)
495     if (dr6 & (1<<i))
496       {
497         if (bps[i].break_at_instruction())
498           e->flags(e->flags() | EFLAGS_RF);
499         if (bps[i].test_break(t, errbuf, bufsize))
500           return 1;
501       }
502
503   return 0;
504 }
505
506 // Create log entry if breakpoint matches.
507 // Return 1 if debugger should stop
508 PUBLIC static
509 void
510 Jdb_bp::test_log(Mword &dr6)
511 {
512   Thread *t = Jdb::get_thread(0);
513   Jdb_entry_frame *e = Jdb::get_entry_frame(0);
514
515   for (int i=0; i<4; i++)
516     if (dr6 & (1<<i))
517       {
518         if (!bps[i].is_break())
519           {
520             // create log entry
521             bps[i].test_log(t);
522             // consider instruction breakpoints
523             if (bps[i].break_at_instruction())
524               e->flags(e->flags() | EFLAGS_RF);
525             // clear condition
526             dr6 &= ~(1<<i);
527           }
528       }
529 }
530
531 PUBLIC static
532 Mword
533 Jdb_bp::test_match(Address addr, Breakpoint::Mode mode)
534 {
535   for (int i=0; i<4; i++)
536     if (bps[i].match_addr(addr, mode))
537       return i+1;
538
539   return 0;
540 }
541
542 PUBLIC static
543 int
544 Jdb_bp::instruction_bp_at_addr(Address addr)
545 { return test_match(addr, Breakpoint::INSTRUCTION); }
546
547
548 PUBLIC static inline NOEXPORT
549 void
550 Jdb_bp::restrict_task(int num, int other, Mword task)
551 {
552   bps[num].restrict_task(other, task);
553 }
554
555 PUBLIC static inline NOEXPORT
556 void
557 Jdb_bp::restrict_thread(int num, int other, Mword thread)
558 {
559   bps[num].restrict_thread(other, thread);
560 }
561
562 PUBLIC static inline NOEXPORT
563 void
564 Jdb_bp::restrict_register(int num, char reg, Mword y, Mword z)
565 {
566   bps[num].restrict_register(reg, y, z);
567 }
568
569 PUBLIC static inline NOEXPORT
570 void
571 Jdb_bp::restrict_memory(int num, Mword addr, Mword len, Mword y, Mword z)
572 {
573   bps[num].restrict_memory(addr, len, y, z);
574 }
575
576 PUBLIC static inline NOEXPORT
577 void
578 Jdb_bp::clear_restriction(int num)
579 {
580   bps[num].clear_restriction();
581 }
582
583 PUBLIC static
584 void 
585 Jdb_bp::list()
586 {
587   putchar('\n');
588
589   for(int i=0; i<4; i++)
590     {
591       printf("  #%d: ", i+1);
592       bps[i].show();
593     }
594
595   putchar('\n');
596 }
597
598
599 //---------------------------------------------------------------------------//
600
601 IMPLEMENT
602 Jdb_set_bp::Jdb_set_bp()
603   : Jdb_module("DEBUGGING")
604 {}
605
606 PUBLIC
607 Jdb_module::Action_code
608 Jdb_set_bp::action(int cmd, void *&args, char const *&fmt, int &next_char)
609 {
610   Jdb_module::Action_code code;
611   Breakpoint::Mode mode;
612
613   if (cmd == 0)
614     {
615       if (args == &breakpoint_cmd)
616         {
617           switch (breakpoint_cmd)
618             {
619             case 'p':
620               if (!(Cpu::boot_cpu()->features() & FEAT_DE))
621                 {
622                   puts(" I/O breakpoints not supported by this CPU");
623                   return NOTHING;
624                 }
625               // fall through
626             case 'a':
627             case 'i':
628             case 'w':
629               if ((breakpoint_number = Jdb_bp::first_unused()) < 4)
630                 {
631                   fmt   = " addr=%C";
632                   args  = &Jdb_input_task_addr::first_char;
633                   state = 1; // breakpoints are global for all tasks
634                   return EXTRA_INPUT;
635                 }
636               puts(" No breakpoints available");
637               return NOTHING;
638             case 'l':
639               // show all breakpoints
640               Jdb_bp::list();
641               return NOTHING;
642             case '-':
643               // delete breakpoint
644             case '+':
645               // set logmode of breakpoint to <STOP>
646             case '*':
647               // set logmode of breakpoint to <LOG>
648             case 'r':
649               // restrict breakpoint
650               fmt   = " bpn=%1x";
651               args  = &breakpoint_number;
652               state = 2;
653               return EXTRA_INPUT;
654             case 't':
655               Jdb::execute_command("bt");
656               break;
657             default:
658               return ERROR;
659             }
660         }
661       else switch (state)
662         {
663         case 1:
664           code = Jdb_input_task_addr::action(args, fmt, next_char);
665           if (code == ERROR)
666             return ERROR;
667           if (code == NOTHING)
668             // ok, continue
669             goto got_address;
670           // more input for Jdb_input_task_addr
671           return code;
672         case 2:
673           if (breakpoint_number < 1 || breakpoint_number > 4)
674             return ERROR;
675           // input is 1..4 but numbers are 0..3
676           breakpoint_number -= 1;
677           // we know the breakpoint number
678           switch (breakpoint_cmd)
679             {
680             case '-':
681               Jdb_bp::clr_breakpoint(breakpoint_number);
682               putchar('\n');
683               return NOTHING;
684             case '+':
685             case '*':
686               Jdb_bp::logmode_breakpoint(breakpoint_number, breakpoint_cmd);
687               putchar('\n');
688               return NOTHING;
689             case 'r':
690               fmt   = " %C";
691               args  = &breakpoint_restrict_cmd;
692               state = 5;
693               return EXTRA_INPUT;
694             default:
695               return ERROR;
696             }
697           break;
698         case 3:
699 got_address:
700           // address/task read
701           if (breakpoint_cmd != 'i')
702             {
703               fmt   = " len (1, 2 or 4)=%1x";
704               args  = &breakpoint_length;
705               state = 4;
706               return EXTRA_INPUT;
707             }
708           breakpoint_length = 1; // must be 1 for instruction breakpoints
709           // fall through
710         case 4:
711           // length read
712           if (breakpoint_length != 1 && breakpoint_length != 2 && 
713               breakpoint_length != 4)
714             break;
715           switch (breakpoint_cmd)
716             {
717             default : return ERROR;
718             case 'i': mode = Breakpoint::INSTRUCTION; break;
719             case 'w': mode = Breakpoint::WRITE;       break;
720             case 'p': mode = Breakpoint::PORTIO;      break;
721             case 'a': mode = Breakpoint::ACCESS;      break;
722             }
723           // abort if no address was given
724           if (Jdb_input_task_addr::addr() == (Address)-1)
725             return ERROR;
726           Jdb_bp::set_breakpoint(breakpoint_number, Jdb_input_task_addr::addr(),
727                                  breakpoint_length, mode, Breakpoint::BREAK,
728                                  Jdb_input_task_addr::task());
729           putchar('\n');
730           break;
731         case 5:
732           // restrict command read
733           switch (breakpoint_restrict_cmd)
734             {
735             case 'a':
736             case 'A':
737               fmt   = (breakpoint_restrict_cmd=='A')
738                         ? "task!="L4_ADDR_INPUT_FMT"\n"
739                         : "task=="L4_ADDR_INPUT_FMT"\n";
740               args  = &breakpoint_restrict_task;
741               state = 6;
742               return EXTRA_INPUT;
743             case 't':
744             case 'T':
745               fmt   = (breakpoint_restrict_cmd=='T')
746                         ? "thread!=%t\n" : "thread==%t\n";
747               args  = &breakpoint_restrict_thread;
748               state = 7;
749               return EXTRA_INPUT;
750             case 'e':
751               if (!Jdb::get_register(&breakpoint_restrict_reg.reg))
752                 return NOTHING;
753               fmt  = " in ["L4_ADDR_INPUT_FMT"-"L4_ADDR_INPUT_FMT"]\n";
754               args = &breakpoint_restrict_reg.low;
755               state = 8;
756               return EXTRA_INPUT;
757             case '1':
758             case '2':
759             case '4':
760               putchar(breakpoint_restrict_cmd);
761               fmt   = "-byte addr="L4_ADDR_INPUT_FMT
762                       " between["L4_ADDR_INPUT_FMT"-"L4_ADDR_INPUT_FMT"]\n";
763               args  = &breakpoint_restrict_addr;
764               state = 9;
765               return EXTRA_INPUT;
766             case '-':
767               Jdb_bp::clear_restriction(breakpoint_number);
768               putchar('\n');
769               break;
770             default:
771               return ERROR;
772             }
773           break;
774         case 6:
775           // breakpoint restrict task read
776           Jdb_bp::restrict_task(breakpoint_number,
777                                 breakpoint_restrict_cmd == 'A',
778                                 breakpoint_restrict_task);
779           break;
780         case 7:
781           // breakpoint restrict thread read
782           Jdb_bp::restrict_thread(breakpoint_number,
783                                   breakpoint_restrict_cmd == 'T',
784                                   breakpoint_restrict_thread);
785           break;
786         case 8:
787           // breakpoint restrict register in range
788           Jdb_bp::restrict_register(breakpoint_number,
789                                     breakpoint_restrict_reg.reg,
790                                     breakpoint_restrict_reg.low,
791                                     breakpoint_restrict_reg.high);
792           break;
793         case 9:
794           // breakpoint restrict x-byte-value in range
795           Jdb_bp::restrict_memory(breakpoint_number,
796                                   breakpoint_restrict_addr.addr,
797                                   breakpoint_restrict_cmd - '0',
798                                   breakpoint_restrict_addr.low,
799                                   breakpoint_restrict_addr.high);
800           break;
801         }
802     }
803   return NOTHING;
804 }
805
806 PUBLIC
807 Jdb_module::Cmd const *
808 Jdb_set_bp::cmds() const
809 {
810   static Cmd cs[] = 
811     {
812         { 0, "b", "bp", "%c",
813           "b{i|a|w|p}<addr>\tset breakpoint on instruction/access/write/io "
814           "access\n"
815           "b{-|+|*}<num>\tdisable/enable/log breakpoint\n"
816           "bl\tlist breakpoints\n"
817           "br<num>{t|T|a|A|e|1|2|4}\trestrict breakpoint to "
818           "(!)thread/(!)task/reg/mem",
819           &breakpoint_cmd },
820     };
821
822   return cs;
823 }
824
825 PUBLIC
826 int
827 Jdb_set_bp::num_cmds() const
828 {
829   return 1;
830 }
831
832 static Jdb_set_bp jdb_set_bp INIT_PRIORITY(JDB_MODULE_INIT_PRIO);