xref: /dragonfly/sys/ddb/db_command.c (revision 6f25fdaba77268fb1736f1ce37e5f75bfdc571be)
1 /*
2  * Mach Operating System
3  * Copyright (c) 1991,1990 Carnegie Mellon University
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify and distribute this software and its
7  * documentation is hereby granted, provided that both the copyright
8  * notice and this permission notice appear in all copies of the
9  * software, derivative works or modified versions, and any portions
10  * thereof, and that both notices appear in supporting documentation.
11  *
12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15  *
16  * Carnegie Mellon requests users of this software to return to
17  *
18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19  *  School of Computer Science
20  *  Carnegie Mellon University
21  *  Pittsburgh PA 15213-3890
22  *
23  * any improvements or extensions that they make and grant Carnegie the
24  * rights to redistribute these changes.
25  *
26  * $FreeBSD: src/sys/ddb/db_command.c,v 1.34.2.2 2001/07/29 22:48:36 kris Exp $
27  */
28 
29 /*
30  *        Author: David B. Golub, Carnegie Mellon University
31  *        Date:     7/90
32  */
33 
34 /*
35  * Command dispatcher.
36  */
37 #include <sys/param.h>
38 #include <sys/linker_set.h>
39 #include <sys/reboot.h>
40 #include <sys/systm.h>
41 #include <sys/cons.h>
42 
43 #include <ddb/ddb.h>
44 #include <ddb/db_command.h>
45 #include <ddb/db_lex.h>
46 #include <ddb/db_output.h>
47 
48 #include <machine/md_var.h>   /* needed for db_reset() */
49 #include <machine/setjmp.h>
50 
51 /*
52  * Exported global variables
53  */
54 boolean_t db_cmd_loop_done;
55 db_addr_t db_dot;
56 jmp_buf             db_jmpbuf;
57 db_addr_t db_last_addr;
58 db_addr_t db_prev;
59 db_addr_t db_next;
60 
61 SET_DECLARE(db_cmd_set, struct command);
62 SET_DECLARE(db_show_cmd_set, struct command);
63 
64 static db_cmdfcn_t  db_fncall;
65 static db_cmdfcn_t  db_gdb;
66 static db_cmdfcn_t  db_reset;
67 
68 /*
69  * 'show' commands
70  */
71 
72 static struct command db_show_all_cmds[] = {
73 #if 0
74           { "threads",        db_show_all_threads,          0,        NULL },
75 #endif
76           { "procs",          db_ps,                        0,        NULL },
77           { NULL }
78 };
79 
80 static struct command db_show_cmds[] = {
81           { "all",  0,                            0,        db_show_all_cmds },
82           { "registers",      db_show_regs,                 0,        NULL },
83           { "breaks",         db_listbreak_cmd,   0,        NULL },
84 #if 0
85           { "thread",         db_show_one_thread, 0,        NULL },
86 #endif
87 #if 0
88           { "port", ipc_port_print,               0,        NULL },
89 #endif
90           { NULL, }
91 };
92 
93 static struct command db_command_table[] = {
94           { "print",          db_print_cmd,                 0,        NULL },
95           { "p",              db_print_cmd,                 0,        NULL },
96           { "examine",        db_examine_cmd,               CS_SET_DOT, NULL },
97           { "x",              db_examine_cmd,               CS_SET_DOT, NULL },
98           { "search",         db_search_cmd,                CS_OWN|CS_SET_DOT, NULL },
99           { "set",  db_set_cmd,                   CS_OWN,   NULL },
100           { "write",          db_write_cmd,                 CS_MORE|CS_SET_DOT, NULL },
101           { "w",              db_write_cmd,                 CS_MORE|CS_SET_DOT, NULL },
102           { "delete",         db_delete_cmd,                0,        NULL },
103           { "d",              db_delete_cmd,                0,        NULL },
104           { "break",          db_breakpoint_cmd,  0,        NULL },
105           { "dwatch",         db_deletewatch_cmd, 0,        NULL },
106           { "watch",          db_watchpoint_cmd,  CS_MORE,NULL },
107           { "dhwatch",        db_deletehwatch_cmd,          0,      NULL },
108           { "hwatch",         db_hwatchpoint_cmd, 0,      NULL },
109           { "step", db_single_step_cmd, 0,        NULL },
110           { "s",              db_single_step_cmd, 0,        NULL },
111           { "continue",       db_continue_cmd,    0,        NULL },
112           { "c",              db_continue_cmd,    0,        NULL },
113           { "i",              db_invltlb_cmd,               0,        NULL },
114           { "until",          db_trace_until_call_cmd,0,    NULL },
115           { "next", db_trace_until_matching_cmd,0,          NULL },
116           { "match",          db_trace_until_matching_cmd,0,          NULL },
117           { "trace",          db_stack_trace_cmd, 0,        NULL },
118           { "where",          db_stack_trace_cmd, 0,        NULL },
119           { "call", db_fncall,                    CS_OWN,   NULL },
120           { "show", 0,                            0,        db_show_cmds },
121           { "ps",             db_ps,                        0,        NULL },
122           { "gdb",  db_gdb,                       0,        NULL },
123           { "reset",          db_reset,           0,        NULL },
124           { NULL, }
125 };
126 
127 static struct command         *db_last_command = NULL;
128 
129 /*
130  * if 'ed' style: 'dot' is set at start of last item printed,
131  * and '+' points to next line.
132  * Otherwise: 'dot' points to next item, '..' points to last.
133  */
134 static boolean_t    db_ed_style = TRUE;
135 
136 /*
137  * Utility routine - discard tokens through end-of-line.
138  */
139 void
db_skip_to_eol(void)140 db_skip_to_eol(void)
141 {
142           int       t;
143           do {
144               t = db_read_token();
145           } while (t != tEOL);
146 }
147 
148 /*
149  * Results of command search.
150  */
151 #define   CMD_UNIQUE          0
152 #define   CMD_FOUND 1
153 #define   CMD_NONE  2
154 #define   CMD_AMBIGUOUS       3
155 #define   CMD_HELP  4
156 
157 static void         db_cmd_list (struct command *table,
158                                          struct command **aux_tablep,
159                                          struct command **aux_tablep_end);
160 static int          db_cmd_search (char *name, struct command *table,
161                                            struct command **aux_tablep,
162                                            struct command **aux_tablep_end,
163                                            struct command **cmdp);
164 static void         db_command (struct command **last_cmdp,
165                                         struct command *cmd_table,
166                                         struct command **aux_cmd_tablep,
167                                         struct command **aux_cmd_tablep_end);
168 
169 /*
170  * Search for command prefix.
171  *
172  * Parameters:
173  *     cmdp:        out
174  */
175 static int
db_cmd_search(char * name,struct command * table,struct command ** aux_tablep,struct command ** aux_tablep_end,struct command ** cmdp)176 db_cmd_search(char *name, struct command *table, struct command **aux_tablep,
177                 struct command **aux_tablep_end, struct command **cmdp)
178 {
179           struct command      *cmd;
180           struct command      **aux_cmdp;
181           int                 result = CMD_NONE;
182 
183           for (cmd = table; cmd->name != 0; cmd++) {
184               char *lp;
185               char *rp;
186               int  c;
187 
188               lp = name;
189               rp = cmd->name;
190               while ((c = *lp) == *rp) {
191                     if (c == 0) {
192                         /* complete match */
193                         *cmdp = cmd;
194                         return (CMD_UNIQUE);
195                     }
196                     lp++;
197                     rp++;
198               }
199               if (c == 0) {
200                     /* end of name, not end of command -
201                        partial match */
202                     if (result == CMD_FOUND) {
203                         result = CMD_AMBIGUOUS;
204                         /* but keep looking for a full match -
205                            this lets us match single letters */
206                     }
207                     else {
208                         *cmdp = cmd;
209                         result = CMD_FOUND;
210                     }
211               }
212           }
213           if (result == CMD_NONE && aux_tablep != NULL)
214               /* XXX repeat too much code. */
215               for (aux_cmdp = aux_tablep; aux_cmdp < aux_tablep_end; aux_cmdp++) {
216                     char *lp;
217                     char *rp;
218                     int  c;
219 
220                     lp = name;
221                     rp = (*aux_cmdp)->name;
222                     while ((c = *lp) == *rp) {
223                         if (c == 0) {
224                               /* complete match */
225                               *cmdp = *aux_cmdp;
226                               return (CMD_UNIQUE);
227                         }
228                         lp++;
229                         rp++;
230                     }
231                     if (c == 0) {
232                         /* end of name, not end of command -
233                            partial match */
234                         if (result == CMD_FOUND) {
235                               result = CMD_AMBIGUOUS;
236                               /* but keep looking for a full match -
237                                  this lets us match single letters */
238                         }
239                         else {
240                               *cmdp = *aux_cmdp;
241                               result = CMD_FOUND;
242                         }
243                     }
244               }
245           if (result == CMD_NONE) {
246               /* check for 'help' */
247                     if (name[0] == 'h' && name[1] == 'e'
248                         && name[2] == 'l' && name[3] == 'p')
249                               result = CMD_HELP;
250           }
251           return (result);
252 }
253 
254 static void
db_cmd_list(struct command * table,struct command ** aux_tablep,struct command ** aux_tablep_end)255 db_cmd_list(struct command *table, struct command **aux_tablep,
256               struct command **aux_tablep_end)
257 {
258           struct command *cmd;
259           struct command **aux_cmdp;
260 
261           for (cmd = table; cmd->name != 0; cmd++) {
262               db_printf("%-12s", cmd->name);
263               db_end_line(12);
264           }
265           if (aux_tablep == NULL)
266               return;
267           for (aux_cmdp = aux_tablep; aux_cmdp < aux_tablep_end; aux_cmdp++) {
268               db_printf("%-12s", (*aux_cmdp)->name);
269               db_end_line(12);
270           }
271 }
272 
273 /*
274  * Parameters:
275  *     last_cmdp:   IN_OUT
276  */
277 static void
db_command(struct command ** last_cmdp,struct command * cmd_table,struct command ** aux_cmd_tablep,struct command ** aux_cmd_tablep_end)278 db_command(struct command **last_cmdp, struct command *cmd_table,
279              struct command **aux_cmd_tablep, struct command **aux_cmd_tablep_end)
280 {
281           struct command      *cmd;
282           int                 t;
283           char                modif[TOK_STRING_SIZE];
284           db_expr_t addr, count;
285           boolean_t have_addr = FALSE;
286           int                 result;
287 
288           cmd = NULL;
289           t = db_read_token();
290           if (t == tEOL) {
291               /* empty line repeats last command, at 'next' */
292               cmd = *last_cmdp;
293               addr = (db_expr_t)db_next;
294               have_addr = FALSE;
295               count = 1;
296               modif[0] = '\0';
297           }
298           else if (t == tEXCL) {
299               db_fncall((db_expr_t)0, (boolean_t)0, (db_expr_t)0, NULL);
300               return;
301           }
302           else if (t != tIDENT) {
303               db_printf("?\n");
304               db_flush_lex();
305               return;
306           }
307           else {
308               /*
309                * Search for command
310                */
311               while (cmd_table) {
312                     result = db_cmd_search(db_tok_string,
313                                                cmd_table,
314                                                aux_cmd_tablep,
315                                                aux_cmd_tablep_end,
316                                                &cmd);
317                     switch (result) {
318                         case CMD_NONE:
319                               db_printf("No such command\n");
320                               db_flush_lex();
321                               return;
322                         case CMD_AMBIGUOUS:
323                               db_printf("Ambiguous\n");
324                               db_flush_lex();
325                               return;
326                         case CMD_HELP:
327                               db_cmd_list(cmd_table, aux_cmd_tablep, aux_cmd_tablep_end);
328                               db_flush_lex();
329                               return;
330                         default:
331                               break;
332                     }
333                     if ((cmd_table = cmd->more) != NULL) {
334                         /* XXX usually no more aux's. */
335                         aux_cmd_tablep = NULL;
336                         if (cmd_table == db_show_cmds) {
337                               aux_cmd_tablep = SET_BEGIN(db_show_cmd_set);
338                               aux_cmd_tablep_end = SET_LIMIT(db_show_cmd_set);
339                         }
340 
341                         t = db_read_token();
342                         if (t != tIDENT) {
343                               db_cmd_list(cmd_table, aux_cmd_tablep, aux_cmd_tablep_end);
344                               db_flush_lex();
345                               return;
346                         }
347                     }
348               }
349 
350               if ((cmd->flag & CS_OWN) == 0) {
351                     /*
352                      * Standard syntax:
353                      * command [/modifier] [addr] [,count]
354                      */
355                     t = db_read_token();
356                     if (t == tSLASH) {
357                         t = db_read_token();
358                         if (t != tIDENT && t != tNUMBER) {
359                               db_printf("Bad modifier\n");
360                               db_flush_lex();
361                               return;
362                         }
363                         db_strcpy(modif, db_tok_string);
364                     }
365                     else {
366                         db_unread_token(t);
367                         modif[0] = '\0';
368                     }
369 
370                     if (db_expression(&addr)) {
371                         db_dot = (db_addr_t) addr;
372                         db_last_addr = db_dot;
373                         have_addr = TRUE;
374                     }
375                     else {
376                         addr = (db_expr_t) db_dot;
377                         have_addr = FALSE;
378                     }
379                     t = db_read_token();
380                     if (t == tCOMMA) {
381                         if (!db_expression(&count)) {
382                               db_printf("Count missing\n");
383                               db_flush_lex();
384                               return;
385                         }
386                     }
387                     else {
388                         db_unread_token(t);
389                         count = -1;
390                     }
391                     if ((cmd->flag & CS_MORE) == 0) {
392                         db_skip_to_eol();
393                     }
394               }
395           }
396           *last_cmdp = cmd;
397           if (cmd != NULL) {
398               /*
399                * Execute the command.
400                */
401               (*cmd->fcn)(addr, have_addr, count, modif);
402 
403               if (cmd->flag & CS_SET_DOT) {
404                     /*
405                      * If command changes dot, set dot to
406                      * previous address displayed (if 'ed' style).
407                      */
408                     if (db_ed_style) {
409                         db_dot = db_prev;
410                     }
411                     else {
412                         db_dot = db_next;
413                     }
414               }
415               else {
416                     /*
417                      * If command does not change dot,
418                      * set 'next' location to be the same.
419                      */
420                     db_next = db_dot;
421               }
422           }
423 }
424 
425 #if 0
426 void
427 db_help_cmd(void)
428 {
429           struct command *cmd = db_command_table;
430 
431           while (cmd->name != 0) {
432               db_printf("%-12s", cmd->name);
433               db_end_line(12);
434               cmd++;
435           }
436 }
437 #endif
438 
439 /*
440  * At least one non-optional command must be implemented using
441  * DB_COMMAND() so that db_cmd_set gets created.  Here is one.
442  */
DB_COMMAND(panic,db_panic)443 DB_COMMAND(panic, db_panic)
444 {
445           panic("from debugger");
446 }
447 
448 void
db_command_loop(void)449 db_command_loop(void)
450 {
451           /*
452            * Initialize 'prev' and 'next' to dot.
453            */
454           cnpoll(TRUE);
455           db_prev = db_dot;
456           db_next = db_dot;
457 
458           db_cmd_loop_done = FALSE;
459           while (!db_cmd_loop_done) {
460 
461               setjmp(db_jmpbuf);
462               if (db_print_position() != 0)
463                     db_printf("\n");
464 
465               db_printf("db> ");
466               db_read_line();
467 
468               db_command(&db_last_command, db_command_table,
469                         SET_BEGIN(db_cmd_set), SET_LIMIT(db_cmd_set));
470           }
471           cnpoll(FALSE);
472 }
473 
474 void
db_error(char * s)475 db_error(char *s)
476 {
477           if (s)
478               db_printf("%s", s);
479           db_flush_lex();
480           longjmp(db_jmpbuf, 1);
481 }
482 
483 
484 /*
485  * Call random function:
486  * !expr(arg,arg,arg)
487  */
488 typedef union {
489           db_expr_t (*a0)(void);
490           db_expr_t (*a1)(db_expr_t);
491           db_expr_t (*a2)(db_expr_t, db_expr_t);
492           db_expr_t (*a3)(db_expr_t, db_expr_t, db_expr_t);
493           db_expr_t (*a4)(db_expr_t, db_expr_t, db_expr_t, db_expr_t);
494           db_expr_t (*a5)(db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t);
495           db_expr_t (*a6)(db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t,
496                                  db_expr_t);
497           db_expr_t (*a7)(db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t,
498                                  db_expr_t, db_expr_t);
499           db_expr_t (*a8)(db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t,
500                                  db_expr_t, db_expr_t, db_expr_t);
501           db_expr_t (*a9)(db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t,
502                                  db_expr_t, db_expr_t, db_expr_t, db_expr_t);
503           db_expr_t (*a10)(db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t,
504                                  db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t);
505 } fcn_multiargs_t;
506 
507 
508 static void
db_fncall(db_expr_t dummy1,boolean_t dummy2,db_expr_t dummy3,char * dummy4)509 db_fncall(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3, char *dummy4)
510 {
511           db_expr_t fn_addr;
512 #define   MAXARGS             11        /* XXX only 10 are passed */
513           db_expr_t args[MAXARGS];
514           int                 nargs = 0;
515           db_expr_t retval;
516           fcn_multiargs_t     func;
517           int                 t;
518 
519           if (!db_expression(&fn_addr)) {
520               db_printf("Bad function\n");
521               db_flush_lex();
522               return;
523           }
524           func.a0 = (void *)fn_addr;
525 
526           t = db_read_token();
527           if (t == tLPAREN) {
528               if (db_expression(&args[0])) {
529                     nargs++;
530                     while ((t = db_read_token()) == tCOMMA) {
531                         if (nargs == MAXARGS) {
532                               db_printf("Too many arguments\n");
533                               db_flush_lex();
534                               return;
535                         }
536                         if (!db_expression(&args[nargs])) {
537                               db_printf("Argument missing\n");
538                               db_flush_lex();
539                               return;
540                         }
541                         nargs++;
542                     }
543                     db_unread_token(t);
544               }
545               if (db_read_token() != tRPAREN) {
546                     db_printf("?\n");
547                     db_flush_lex();
548                     return;
549               }
550           }
551           db_skip_to_eol();
552 
553           /*
554            * This is mainly so kgdb doesn't get confused if ddb> call is
555            * in its backtrace.
556            */
557           switch(nargs) {
558           case 0:
559                     retval = (*func.a0)();
560                     break;
561           case 1:
562                     retval = (*func.a1)(args[0]);
563                     break;
564           case 2:
565                     retval = (*func.a2)(args[0], args[1]);
566                     break;
567           case 3:
568                     retval = (*func.a3)(args[0], args[1], args[2]);
569                     break;
570           case 4:
571                     retval = (*func.a4)(args[0], args[1], args[2], args[3]);
572                     break;
573           case 5:
574                     retval = (*func.a5)(args[0], args[1], args[2], args[3], args[4]);
575                     break;
576           case 6:
577                     retval = (*func.a6)(args[0], args[1], args[2], args[3], args[4],
578                                             args[5]);
579                     break;
580           case 7:
581                     retval = (*func.a7)(args[0], args[1], args[2], args[3], args[4],
582                                             args[5], args[6]);
583                     break;
584           case 8:
585                     retval = (*func.a8)(args[0], args[1], args[2], args[3], args[4],
586                                             args[5], args[6], args[7]);
587                     break;
588           case 9:
589                     retval = (*func.a9)(args[0], args[1], args[2], args[3], args[4],
590                                             args[5], args[6], args[7], args[8]);
591                     break;
592           case 10:
593                     retval = (*func.a10)(args[0], args[1], args[2], args[3], args[4],
594                                              args[5], args[6], args[7], args[8], args[9]);
595                     break;
596           default:
597                     db_printf("too many arguments\n");
598                     retval = 0;
599                     break;
600           }
601           db_printf("%s\n", db_num_to_str(retval));
602 }
603 
604 /* Enter GDB remote protocol debugger on the next trap. */
605 
606 static void
db_gdb(db_expr_t dummy1,boolean_t dummy2,db_expr_t dummy3,char * dummy4)607 db_gdb(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3, char *dummy4)
608 {
609           if (gdb_tab == NULL) {
610                     db_printf("No gdb port enabled. Set flag 0x80 on desired port\n");
611                     db_printf("in your configuration file (currently sio only).\n");
612                     return;
613           }
614           boothowto ^= RB_GDB;
615 
616           db_printf("Next trap will enter %s\n",
617                        boothowto & RB_GDB ? "GDB remote protocol mode"
618                                               : "DDB debugger");
619 }
620 
621 static void
db_reset(db_expr_t dummy1,boolean_t dummy2,db_expr_t dummy3,char * dummy4)622 db_reset(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3, char * dummy4)
623 {
624           cpu_reset();
625 }
626