db_command.c revision 1.27.4.1 1 /* $NetBSD: db_command.c,v 1.27.4.1 1998/11/09 06:06:31 chs Exp $ */
2
3 /*
4 * Mach Operating System
5 * Copyright (c) 1991,1990 Carnegie Mellon University
6 * All Rights Reserved.
7 *
8 * Permission to use, copy, modify and distribute this software and its
9 * documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation.
13 *
14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17 *
18 * Carnegie Mellon requests users of this software to return to
19 *
20 * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU
21 * School of Computer Science
22 * Carnegie Mellon University
23 * Pittsburgh PA 15213-3890
24 *
25 * any improvements or extensions that they make and grant Carnegie the
26 * rights to redistribute these changes.
27 */
28
29 #include "opt_ddb.h"
30 #include "opt_uvm.h"
31
32 /*
33 * Command dispatcher.
34 */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/reboot.h>
38 #include <sys/proc.h>
39 #include <sys/vnode.h>
40
41 #include <machine/db_machdep.h> /* type definitions */
42
43 #include <ddb/db_lex.h>
44 #include <ddb/db_output.h>
45 #include <ddb/db_command.h>
46 #include <ddb/db_break.h>
47 #include <ddb/db_watch.h>
48 #include <ddb/db_run.h>
49 #include <ddb/db_variables.h>
50 #include <ddb/db_interface.h>
51 #include <ddb/db_sym.h>
52 #include <ddb/db_extern.h>
53
54 #include <vm/vm.h>
55
56 #if defined(UVM)
57 #include <uvm/uvm_extern.h>
58 #include <uvm/uvm_ddb.h>
59 #endif
60
61 /*
62 * Exported global variables
63 */
64 boolean_t db_cmd_loop_done;
65 label_t *db_recover;
66
67 /*
68 * if 'ed' style: 'dot' is set at start of last item printed,
69 * and '+' points to next line.
70 * Otherwise: 'dot' points to next item, '..' points to last.
71 */
72 boolean_t db_ed_style = TRUE;
73
74 /*
75 * Utility routine - discard tokens through end-of-line.
76 */
77 void
78 db_skip_to_eol()
79 {
80 int t;
81 do {
82 t = db_read_token();
83 } while (t != tEOL);
84 }
85
86 /*
87 * Results of command search.
88 */
89 #define CMD_UNIQUE 0
90 #define CMD_FOUND 1
91 #define CMD_NONE 2
92 #define CMD_AMBIGUOUS 3
93 #define CMD_HELP 4
94
95 /*
96 * Search for command prefix.
97 */
98 int
99 db_cmd_search(name, table, cmdp)
100 char *name;
101 struct db_command *table;
102 struct db_command **cmdp; /* out */
103 {
104 struct db_command *cmd;
105 int result = CMD_NONE;
106
107 for (cmd = table; cmd->name != 0; cmd++) {
108 register char *lp;
109 register char *rp;
110 register int c;
111
112 lp = name;
113 rp = cmd->name;
114 while ((c = *lp) == *rp) {
115 if (c == 0) {
116 /* complete match */
117 *cmdp = cmd;
118 return (CMD_UNIQUE);
119 }
120 lp++;
121 rp++;
122 }
123 if (c == 0) {
124 /* end of name, not end of command -
125 partial match */
126 if (result == CMD_FOUND) {
127 result = CMD_AMBIGUOUS;
128 /* but keep looking for a full match -
129 this lets us match single letters */
130 }
131 else {
132 *cmdp = cmd;
133 result = CMD_FOUND;
134 }
135 }
136 }
137 if (result == CMD_NONE) {
138 /* check for 'help' */
139 if (name[0] == 'h' && name[1] == 'e'
140 && name[2] == 'l' && name[3] == 'p')
141 result = CMD_HELP;
142 }
143 return (result);
144 }
145
146 void
147 db_cmd_list(table)
148 struct db_command *table;
149 {
150 register struct db_command *cmd;
151
152 for (cmd = table; cmd->name != 0; cmd++) {
153 db_printf("%-12s", cmd->name);
154 db_end_line();
155 }
156 }
157
158 void
159 db_command(last_cmdp, cmd_table)
160 struct db_command **last_cmdp; /* IN_OUT */
161 struct db_command *cmd_table;
162 {
163 struct db_command *cmd;
164 int t;
165 char modif[TOK_STRING_SIZE];
166 db_expr_t addr, count;
167 boolean_t have_addr = FALSE;
168 int result;
169
170 t = db_read_token();
171 if (t == tEOL) {
172 /* empty line repeats last command, at 'next' */
173 cmd = *last_cmdp;
174 addr = (db_expr_t)db_next;
175 have_addr = FALSE;
176 count = 1;
177 modif[0] = '\0';
178 }
179 else if (t == tEXCL) {
180 db_fncall(0, 0, 0, NULL);
181 return;
182 }
183 else if (t != tIDENT) {
184 db_printf("?\n");
185 db_flush_lex();
186 return;
187 }
188 else {
189 /*
190 * Search for command
191 */
192 while (cmd_table) {
193 result = db_cmd_search(db_tok_string,
194 cmd_table,
195 &cmd);
196 switch (result) {
197 case CMD_NONE:
198 db_printf("No such command\n");
199 db_flush_lex();
200 return;
201 case CMD_AMBIGUOUS:
202 db_printf("Ambiguous\n");
203 db_flush_lex();
204 return;
205 case CMD_HELP:
206 db_cmd_list(cmd_table);
207 db_flush_lex();
208 return;
209 default:
210 break;
211 }
212 if ((cmd_table = cmd->more) != 0) {
213 t = db_read_token();
214 if (t != tIDENT) {
215 db_cmd_list(cmd_table);
216 db_flush_lex();
217 return;
218 }
219 }
220 }
221
222 if ((cmd->flag & CS_OWN) == 0) {
223 /*
224 * Standard syntax:
225 * command [/modifier] [addr] [,count]
226 */
227 t = db_read_token();
228 if (t == tSLASH) {
229 t = db_read_token();
230 if (t != tIDENT) {
231 db_printf("Bad modifier\n");
232 db_flush_lex();
233 return;
234 }
235 db_strcpy(modif, db_tok_string);
236 }
237 else {
238 db_unread_token(t);
239 modif[0] = '\0';
240 }
241
242 if (db_expression(&addr)) {
243 db_dot = (db_addr_t) addr;
244 db_last_addr = db_dot;
245 have_addr = TRUE;
246 }
247 else {
248 addr = (db_expr_t) db_dot;
249 have_addr = FALSE;
250 }
251 t = db_read_token();
252 if (t == tCOMMA) {
253 if (!db_expression(&count)) {
254 db_printf("Count missing\n");
255 db_flush_lex();
256 return;
257 }
258 }
259 else {
260 db_unread_token(t);
261 count = -1;
262 }
263 if ((cmd->flag & CS_MORE) == 0) {
264 db_skip_to_eol();
265 }
266 }
267 }
268 *last_cmdp = cmd;
269 if (cmd != 0) {
270 /*
271 * Execute the command.
272 */
273 (*cmd->fcn)(addr, have_addr, count, modif);
274
275 if (cmd->flag & CS_SET_DOT) {
276 /*
277 * If command changes dot, set dot to
278 * previous address displayed (if 'ed' style).
279 */
280 if (db_ed_style) {
281 db_dot = db_prev;
282 }
283 else {
284 db_dot = db_next;
285 }
286 }
287 else {
288 /*
289 * If command does not change dot,
290 * set 'next' location to be the same.
291 */
292 db_next = db_dot;
293 }
294 }
295 }
296
297 /*ARGSUSED*/
298 void
299 db_map_print_cmd(addr, have_addr, count, modif)
300 db_expr_t addr;
301 int have_addr;
302 db_expr_t count;
303 char * modif;
304 {
305 boolean_t full = FALSE;
306
307 if (modif[0] == 'f')
308 full = TRUE;
309
310 #if defined(UVM)
311 uvm_map_printit((vm_map_t) addr, full, db_printf);
312 #else
313 _vm_map_print((vm_map_t) addr, full, db_printf);
314 #endif
315 }
316
317 /*ARGSUSED*/
318 void
319 db_object_print_cmd(addr, have_addr, count, modif)
320 db_expr_t addr;
321 int have_addr;
322 db_expr_t count;
323 char * modif;
324 {
325 boolean_t full = FALSE;
326
327 if (modif[0] == 'f')
328 full = TRUE;
329
330 #if defined(UVM)
331 uvm_object_printit((struct uvm_object *) addr, full, db_printf);
332 #else
333 _vm_object_print((vm_object_t) addr, full, db_printf);
334 #endif
335 }
336
337 /*ARGSUSED*/
338 void
339 db_page_print_cmd(addr, have_addr, count, modif)
340 db_expr_t addr;
341 int have_addr;
342 db_expr_t count;
343 char * modif;
344 {
345 boolean_t full = FALSE;
346
347 if (modif[0] == 'f')
348 full = TRUE;
349
350 #if defined(UVM)
351 uvm_page_printit((struct vm_page *) addr, full, db_printf);
352 #else
353 printf("only supported by UVM\n");
354 #endif
355 }
356
357 /*ARGSUSED*/
358 void
359 db_buf_print_cmd(addr, have_addr, count, modif)
360 db_expr_t addr;
361 int have_addr;
362 db_expr_t count;
363 char * modif;
364 {
365 boolean_t full = FALSE;
366
367 if (modif[0] == 'f')
368 full = TRUE;
369
370 vfs_buf_print((struct buf *)addr, full, db_printf);
371 }
372
373 /*ARGSUSED*/
374 void
375 db_vnode_print_cmd(addr, have_addr, count, modif)
376 db_expr_t addr;
377 int have_addr;
378 db_expr_t count;
379 char * modif;
380 {
381 boolean_t full = FALSE;
382
383 if (modif[0] == 'f')
384 full = TRUE;
385
386 vfs_vnode_print((struct vnode *) addr, full, db_printf);
387 }
388
389 /*
390 * 'show' commands
391 */
392
393 struct db_command db_show_all_cmds[] = {
394 { "procs", db_show_all_procs, 0, NULL },
395 { "callout", db_show_callout, 0, NULL },
396 { NULL, NULL, 0, NULL }
397 };
398
399 struct db_command db_show_cmds[] = {
400 { "all", NULL, 0, db_show_all_cmds },
401 { "registers", db_show_regs, 0, NULL },
402 { "breaks", db_listbreak_cmd, 0, NULL },
403 { "watches", db_listwatch_cmd, 0, NULL },
404 { "map", db_map_print_cmd, 0, NULL },
405 { "object", db_object_print_cmd, 0, NULL },
406 { "page", db_page_print_cmd, 0, NULL },
407 { "buf", db_buf_print_cmd, 0, NULL },
408 { "vnode", db_vnode_print_cmd, 0, NULL },
409 { NULL, NULL, 0, NULL, }
410 };
411
412 struct db_command db_command_table[] = {
413 #ifdef DB_MACHINE_COMMANDS
414 /* this must be the first entry, if it exists */
415 { "machine", NULL, 0, NULL},
416 #endif
417 { "print", db_print_cmd, 0, NULL },
418 { "examine", db_examine_cmd, CS_SET_DOT, NULL },
419 { "x", db_examine_cmd, CS_SET_DOT, NULL },
420 { "search", db_search_cmd, CS_OWN|CS_SET_DOT, NULL },
421 { "set", db_set_cmd, CS_OWN, NULL },
422 { "write", db_write_cmd, CS_MORE|CS_SET_DOT, NULL },
423 { "w", db_write_cmd, CS_MORE|CS_SET_DOT, NULL },
424 { "delete", db_delete_cmd, 0, NULL },
425 { "d", db_delete_cmd, 0, NULL },
426 { "break", db_breakpoint_cmd, 0, NULL },
427 { "dwatch", db_deletewatch_cmd, 0, NULL },
428 { "watch", db_watchpoint_cmd, CS_MORE, NULL },
429 { "step", db_single_step_cmd, 0, NULL },
430 { "s", db_single_step_cmd, 0, NULL },
431 { "continue", db_continue_cmd, 0, NULL },
432 { "c", db_continue_cmd, 0, NULL },
433 { "until", db_trace_until_call_cmd,0, NULL },
434 { "next", db_trace_until_matching_cmd,0, NULL },
435 { "match", db_trace_until_matching_cmd,0, NULL },
436 { "trace", db_stack_trace_cmd, 0, NULL },
437 { "call", db_fncall, CS_OWN, NULL },
438 { "ps", db_show_all_procs, 0, NULL },
439 { "kill", db_kill_proc, CS_OWN, NULL },
440 { "callout", db_show_callout, 0, NULL },
441 { "reboot", db_reboot_cmd, CS_OWN, NULL },
442 { "show", NULL, 0, db_show_cmds },
443 { NULL, NULL, 0, NULL }
444 };
445
446 #ifdef DB_MACHINE_COMMANDS
447
448 /* this function should be called to install the machine dependent
449 commands. It should be called before the debugger is enabled */
450 void db_machine_commands_install(ptr)
451 struct db_command *ptr;
452 {
453 db_command_table[0].more = ptr;
454 return;
455 }
456
457 #endif
458
459 struct db_command *db_last_command = 0;
460
461 void
462 db_help_cmd()
463 {
464 struct db_command *cmd = db_command_table;
465
466 while (cmd->name != 0) {
467 db_printf("%-12s", cmd->name);
468 db_end_line();
469 cmd++;
470 }
471 }
472
473 void
474 db_command_loop()
475 {
476 label_t db_jmpbuf;
477 label_t *savejmp;
478 extern int db_output_line;
479
480 /*
481 * Initialize 'prev' and 'next' to dot.
482 */
483 db_prev = db_dot;
484 db_next = db_dot;
485
486 db_cmd_loop_done = 0;
487
488 savejmp = db_recover;
489 db_recover = &db_jmpbuf;
490 (void) setjmp(&db_jmpbuf);
491
492 while (!db_cmd_loop_done) {
493 if (db_print_position() != 0)
494 db_printf("\n");
495 db_output_line = 0;
496
497 db_printf("db> ");
498 (void) db_read_line();
499
500 db_command(&db_last_command, db_command_table);
501 }
502
503 db_recover = savejmp;
504 }
505
506 void
507 db_error(s)
508 char *s;
509 {
510 if (s)
511 db_printf(s);
512 db_flush_lex();
513 longjmp(db_recover);
514 }
515
516
517 /*
518 * Call random function:
519 * !expr(arg,arg,arg)
520 */
521 /*ARGSUSED*/
522 void
523 db_fncall(addr, have_addr, count, modif)
524 db_expr_t addr;
525 int have_addr;
526 db_expr_t count;
527 char * modif;
528 {
529 db_expr_t fn_addr;
530 #define MAXARGS 11
531 db_expr_t args[MAXARGS];
532 int nargs = 0;
533 db_expr_t retval;
534 db_expr_t (*func) __P((db_expr_t, ...));
535 int t;
536
537 if (!db_expression(&fn_addr)) {
538 db_printf("Bad function\n");
539 db_flush_lex();
540 return;
541 }
542 func = (db_expr_t (*) __P((db_expr_t, ...))) fn_addr;
543
544 t = db_read_token();
545 if (t == tLPAREN) {
546 if (db_expression(&args[0])) {
547 nargs++;
548 while ((t = db_read_token()) == tCOMMA) {
549 if (nargs == MAXARGS) {
550 db_printf("Too many arguments\n");
551 db_flush_lex();
552 return;
553 }
554 if (!db_expression(&args[nargs])) {
555 db_printf("Argument missing\n");
556 db_flush_lex();
557 return;
558 }
559 nargs++;
560 }
561 db_unread_token(t);
562 }
563 if (db_read_token() != tRPAREN) {
564 db_printf("?\n");
565 db_flush_lex();
566 return;
567 }
568 }
569 db_skip_to_eol();
570
571 while (nargs < MAXARGS) {
572 args[nargs++] = 0;
573 }
574
575 retval = (*func)(args[0], args[1], args[2], args[3], args[4],
576 args[5], args[6], args[7], args[8], args[9]);
577 db_printf("%#ln\n", retval);
578 }
579
580 void
581 db_reboot_cmd(addr, have_addr, count, modif)
582 db_expr_t addr;
583 int have_addr;
584 db_expr_t count;
585 char * modif;
586 {
587 db_expr_t bootflags;
588
589 /* Flags, default to RB_AUTOBOOT */
590 if (!db_expression(&bootflags))
591 bootflags = (db_expr_t)RB_AUTOBOOT;
592 if (db_read_token() != tEOL) {
593 db_error("?\n");
594 /*NOTREACHED*/
595 }
596 cpu_reboot((int)bootflags, NULL);
597 }
598