FreeBSD/Linux Kernel Cross Reference
sys/ddb/db_command.c
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 /*
27 * Author: David B. Golub, Carnegie Mellon University
28 * Date: 7/90
29 */
30 /*
31 * Command dispatcher.
32 */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include <sys/param.h>
38 #include <sys/linker_set.h>
39 #include <sys/lock.h>
40 #include <sys/kdb.h>
41 #include <sys/mutex.h>
42 #include <sys/proc.h>
43 #include <sys/reboot.h>
44 #include <sys/signalvar.h>
45 #include <sys/systm.h>
46 #include <sys/cons.h>
47 #include <sys/watchdog.h>
48
49 #include <ddb/ddb.h>
50 #include <ddb/db_command.h>
51 #include <ddb/db_lex.h>
52 #include <ddb/db_output.h>
53
54 #include <machine/cpu.h>
55 #include <machine/setjmp.h>
56
57 /*
58 * Exported global variables
59 */
60 boolean_t db_cmd_loop_done;
61 db_addr_t db_dot;
62 db_addr_t db_last_addr;
63 db_addr_t db_prev;
64 db_addr_t db_next;
65
66 SET_DECLARE(db_cmd_set, struct command);
67 SET_DECLARE(db_show_cmd_set, struct command);
68
69 static db_cmdfcn_t db_fncall;
70 static db_cmdfcn_t db_gdb;
71 static db_cmdfcn_t db_kill;
72 static db_cmdfcn_t db_reset;
73 static db_cmdfcn_t db_stack_trace;
74 static db_cmdfcn_t db_watchdog;
75
76 /* XXX this is actually forward-static. */
77 extern struct command db_show_cmds[];
78
79 /*
80 * if 'ed' style: 'dot' is set at start of last item printed,
81 * and '+' points to next line.
82 * Otherwise: 'dot' points to next item, '..' points to last.
83 */
84 static boolean_t db_ed_style = TRUE;
85
86 /*
87 * Utility routine - discard tokens through end-of-line.
88 */
89 void
90 db_skip_to_eol()
91 {
92 int t;
93 do {
94 t = db_read_token();
95 } while (t != tEOL);
96 }
97
98 /*
99 * Results of command search.
100 */
101 #define CMD_UNIQUE 0
102 #define CMD_FOUND 1
103 #define CMD_NONE 2
104 #define CMD_AMBIGUOUS 3
105 #define CMD_HELP 4
106
107 static void db_cmd_list(struct command *table, struct command **aux_tablep,
108 struct command **aux_tablep_end);
109 static int db_cmd_search(char *name, struct command *table,
110 struct command **aux_tablep,
111 struct command **aux_tablep_end, struct command **cmdp);
112 static void db_command(struct command **last_cmdp,
113 struct command *cmd_table, struct command **aux_cmd_tablep,
114 struct command **aux_cmd_tablep_end);
115
116 /*
117 * Search for command prefix.
118 */
119 static int
120 db_cmd_search(name, table, aux_tablep, aux_tablep_end, cmdp)
121 char * name;
122 struct command *table;
123 struct command **aux_tablep;
124 struct command **aux_tablep_end;
125 struct command **cmdp; /* out */
126 {
127 struct command *cmd;
128 struct command **aux_cmdp;
129 int result = CMD_NONE;
130
131 for (cmd = table; cmd->name != 0; cmd++) {
132 register char *lp;
133 register char *rp;
134 register int c;
135
136 lp = name;
137 rp = cmd->name;
138 while ((c = *lp) == *rp) {
139 if (c == 0) {
140 /* complete match */
141 *cmdp = cmd;
142 return (CMD_UNIQUE);
143 }
144 lp++;
145 rp++;
146 }
147 if (c == 0) {
148 /* end of name, not end of command -
149 partial match */
150 if (result == CMD_FOUND) {
151 result = CMD_AMBIGUOUS;
152 /* but keep looking for a full match -
153 this lets us match single letters */
154 }
155 else {
156 *cmdp = cmd;
157 result = CMD_FOUND;
158 }
159 }
160 }
161 if (result == CMD_NONE && aux_tablep != 0)
162 /* XXX repeat too much code. */
163 for (aux_cmdp = aux_tablep; aux_cmdp < aux_tablep_end; aux_cmdp++) {
164 register char *lp;
165 register char *rp;
166 register int c;
167
168 lp = name;
169 rp = (*aux_cmdp)->name;
170 while ((c = *lp) == *rp) {
171 if (c == 0) {
172 /* complete match */
173 *cmdp = *aux_cmdp;
174 return (CMD_UNIQUE);
175 }
176 lp++;
177 rp++;
178 }
179 if (c == 0) {
180 /* end of name, not end of command -
181 partial match */
182 if (result == CMD_FOUND) {
183 result = CMD_AMBIGUOUS;
184 /* but keep looking for a full match -
185 this lets us match single letters */
186 }
187 else {
188 *cmdp = *aux_cmdp;
189 result = CMD_FOUND;
190 }
191 }
192 }
193 if (result == CMD_NONE) {
194 /* check for 'help' */
195 if (name[0] == 'h' && name[1] == 'e'
196 && name[2] == 'l' && name[3] == 'p')
197 result = CMD_HELP;
198 }
199 return (result);
200 }
201
202 static void
203 db_cmd_list(table, aux_tablep, aux_tablep_end)
204 struct command *table;
205 struct command **aux_tablep;
206 struct command **aux_tablep_end;
207 {
208 register struct command *cmd;
209 register struct command **aux_cmdp;
210
211 for (cmd = table; cmd->name != 0; cmd++) {
212 db_printf("%-12s", cmd->name);
213 db_end_line();
214 }
215 if (aux_tablep == 0)
216 return;
217 for (aux_cmdp = aux_tablep; aux_cmdp < aux_tablep_end; aux_cmdp++) {
218 db_printf("%-12s", (*aux_cmdp)->name);
219 db_end_line();
220 }
221 }
222
223 static void
224 db_command(last_cmdp, cmd_table, aux_cmd_tablep, aux_cmd_tablep_end)
225 struct command **last_cmdp; /* IN_OUT */
226 struct command *cmd_table;
227 struct command **aux_cmd_tablep;
228 struct command **aux_cmd_tablep_end;
229 {
230 struct command *cmd;
231 int t;
232 char modif[TOK_STRING_SIZE];
233 db_expr_t addr, count;
234 boolean_t have_addr = FALSE;
235 int result;
236
237 t = db_read_token();
238 if (t == tEOL) {
239 /* empty line repeats last command, at 'next' */
240 cmd = *last_cmdp;
241 addr = (db_expr_t)db_next;
242 have_addr = FALSE;
243 count = 1;
244 modif[0] = '\0';
245 }
246 else if (t == tEXCL) {
247 db_fncall((db_expr_t)0, (boolean_t)0, (db_expr_t)0, (char *)0);
248 return;
249 }
250 else if (t != tIDENT) {
251 db_printf("?\n");
252 db_flush_lex();
253 return;
254 }
255 else {
256 /*
257 * Search for command
258 */
259 while (cmd_table) {
260 result = db_cmd_search(db_tok_string,
261 cmd_table,
262 aux_cmd_tablep,
263 aux_cmd_tablep_end,
264 &cmd);
265 switch (result) {
266 case CMD_NONE:
267 db_printf("No such command\n");
268 db_flush_lex();
269 return;
270 case CMD_AMBIGUOUS:
271 db_printf("Ambiguous\n");
272 db_flush_lex();
273 return;
274 case CMD_HELP:
275 db_cmd_list(cmd_table, aux_cmd_tablep, aux_cmd_tablep_end);
276 db_flush_lex();
277 return;
278 default:
279 break;
280 }
281 if ((cmd_table = cmd->more) != 0) {
282 /* XXX usually no more aux's. */
283 aux_cmd_tablep = 0;
284 if (cmd_table == db_show_cmds) {
285 aux_cmd_tablep = SET_BEGIN(db_show_cmd_set);
286 aux_cmd_tablep_end = SET_LIMIT(db_show_cmd_set);
287 }
288
289 t = db_read_token();
290 if (t != tIDENT) {
291 db_cmd_list(cmd_table, aux_cmd_tablep, aux_cmd_tablep_end);
292 db_flush_lex();
293 return;
294 }
295 }
296 }
297
298 if ((cmd->flag & CS_OWN) == 0) {
299 /*
300 * Standard syntax:
301 * command [/modifier] [addr] [,count]
302 */
303 t = db_read_token();
304 if (t == tSLASH) {
305 t = db_read_token();
306 if (t != tIDENT) {
307 db_printf("Bad modifier\n");
308 db_flush_lex();
309 return;
310 }
311 db_strcpy(modif, db_tok_string);
312 }
313 else {
314 db_unread_token(t);
315 modif[0] = '\0';
316 }
317
318 if (db_expression(&addr)) {
319 db_dot = (db_addr_t) addr;
320 db_last_addr = db_dot;
321 have_addr = TRUE;
322 }
323 else {
324 addr = (db_expr_t) db_dot;
325 have_addr = FALSE;
326 }
327 t = db_read_token();
328 if (t == tCOMMA) {
329 if (!db_expression(&count)) {
330 db_printf("Count missing\n");
331 db_flush_lex();
332 return;
333 }
334 }
335 else {
336 db_unread_token(t);
337 count = -1;
338 }
339 if ((cmd->flag & CS_MORE) == 0) {
340 db_skip_to_eol();
341 }
342 }
343 }
344 *last_cmdp = cmd;
345 if (cmd != 0) {
346 /*
347 * Execute the command.
348 */
349 (*cmd->fcn)(addr, have_addr, count, modif);
350 db_setup_paging(NULL, NULL, -1);
351
352 if (cmd->flag & CS_SET_DOT) {
353 /*
354 * If command changes dot, set dot to
355 * previous address displayed (if 'ed' style).
356 */
357 if (db_ed_style) {
358 db_dot = db_prev;
359 }
360 else {
361 db_dot = db_next;
362 }
363 }
364 else {
365 /*
366 * If command does not change dot,
367 * set 'next' location to be the same.
368 */
369 db_next = db_dot;
370 }
371 }
372 }
373
374 /*
375 * 'show' commands
376 */
377
378 static struct command db_show_all_cmds[] = {
379 { "procs", db_ps, 0, 0 },
380 { (char *)0 }
381 };
382
383 static struct command db_show_cmds[] = {
384 { "all", 0, 0, db_show_all_cmds },
385 { "registers", db_show_regs, 0, 0 },
386 { "breaks", db_listbreak_cmd, 0, 0 },
387 { "threads", db_show_threads, 0, 0 },
388 { (char *)0, }
389 };
390
391 static struct command db_command_table[] = {
392 { "print", db_print_cmd, 0, 0 },
393 { "p", db_print_cmd, 0, 0 },
394 { "examine", db_examine_cmd, CS_SET_DOT, 0 },
395 { "x", db_examine_cmd, CS_SET_DOT, 0 },
396 { "search", db_search_cmd, CS_OWN|CS_SET_DOT, 0 },
397 { "set", db_set_cmd, CS_OWN, 0 },
398 { "write", db_write_cmd, CS_MORE|CS_SET_DOT, 0 },
399 { "w", db_write_cmd, CS_MORE|CS_SET_DOT, 0 },
400 { "delete", db_delete_cmd, 0, 0 },
401 { "d", db_delete_cmd, 0, 0 },
402 { "break", db_breakpoint_cmd, 0, 0 },
403 { "dwatch", db_deletewatch_cmd, 0, 0 },
404 { "watch", db_watchpoint_cmd, CS_MORE,0 },
405 { "dhwatch", db_deletehwatch_cmd, 0, 0 },
406 { "hwatch", db_hwatchpoint_cmd, 0, 0 },
407 { "step", db_single_step_cmd, 0, 0 },
408 { "s", db_single_step_cmd, 0, 0 },
409 { "continue", db_continue_cmd, 0, 0 },
410 { "c", db_continue_cmd, 0, 0 },
411 { "until", db_trace_until_call_cmd,0, 0 },
412 { "next", db_trace_until_matching_cmd,0, 0 },
413 { "match", db_trace_until_matching_cmd,0, 0 },
414 { "trace", db_stack_trace, CS_OWN, 0 },
415 { "where", db_stack_trace, CS_OWN, 0 },
416 { "call", db_fncall, CS_OWN, 0 },
417 { "show", 0, 0, db_show_cmds },
418 { "ps", db_ps, 0, 0 },
419 { "gdb", db_gdb, 0, 0 },
420 { "reset", db_reset, 0, 0 },
421 { "kill", db_kill, CS_OWN, 0 },
422 { "watchdog", db_watchdog, 0, 0 },
423 { "thread", db_set_thread, CS_OWN, 0 },
424 { (char *)0, }
425 };
426
427 static struct command *db_last_command = 0;
428
429 /*
430 * At least one non-optional command must be implemented using
431 * DB_COMMAND() so that db_cmd_set gets created. Here is one.
432 */
433 DB_COMMAND(panic, db_panic)
434 {
435 panic("from debugger");
436 }
437
438 void
439 db_command_loop()
440 {
441 /*
442 * Initialize 'prev' and 'next' to dot.
443 */
444 db_prev = db_dot;
445 db_next = db_dot;
446
447 db_cmd_loop_done = 0;
448 while (!db_cmd_loop_done) {
449 if (db_print_position() != 0)
450 db_printf("\n");
451
452 db_printf("db> ");
453 (void) db_read_line();
454
455 db_command(&db_last_command, db_command_table,
456 SET_BEGIN(db_cmd_set), SET_LIMIT(db_cmd_set));
457 }
458 }
459
460 void
461 db_error(s)
462 const char *s;
463 {
464 if (s)
465 db_printf("%s", s);
466 db_flush_lex();
467 kdb_reenter();
468 }
469
470
471 /*
472 * Call random function:
473 * !expr(arg,arg,arg)
474 */
475 static void
476 db_fncall(dummy1, dummy2, dummy3, dummy4)
477 db_expr_t dummy1;
478 boolean_t dummy2;
479 db_expr_t dummy3;
480 char * dummy4;
481 {
482 db_expr_t fn_addr;
483 #define MAXARGS 11 /* XXX only 10 are passed */
484 db_expr_t args[MAXARGS];
485 int nargs = 0;
486 db_expr_t retval;
487 typedef db_expr_t fcn_10args_t(db_expr_t, db_expr_t, db_expr_t,
488 db_expr_t, db_expr_t, db_expr_t, db_expr_t,
489 db_expr_t, db_expr_t, db_expr_t);
490 fcn_10args_t *func;
491 int t;
492
493 if (!db_expression(&fn_addr)) {
494 db_printf("Bad function\n");
495 db_flush_lex();
496 return;
497 }
498 func = (fcn_10args_t *)fn_addr; /* XXX */
499
500 t = db_read_token();
501 if (t == tLPAREN) {
502 if (db_expression(&args[0])) {
503 nargs++;
504 while ((t = db_read_token()) == tCOMMA) {
505 if (nargs == MAXARGS) {
506 db_printf("Too many arguments\n");
507 db_flush_lex();
508 return;
509 }
510 if (!db_expression(&args[nargs])) {
511 db_printf("Argument missing\n");
512 db_flush_lex();
513 return;
514 }
515 nargs++;
516 }
517 db_unread_token(t);
518 }
519 if (db_read_token() != tRPAREN) {
520 db_printf("?\n");
521 db_flush_lex();
522 return;
523 }
524 }
525 db_skip_to_eol();
526
527 while (nargs < MAXARGS) {
528 args[nargs++] = 0;
529 }
530
531 retval = (*func)(args[0], args[1], args[2], args[3], args[4],
532 args[5], args[6], args[7], args[8], args[9] );
533 db_printf("%#lr\n", (long)retval);
534 }
535
536 static void
537 db_kill(dummy1, dummy2, dummy3, dummy4)
538 db_expr_t dummy1;
539 boolean_t dummy2;
540 db_expr_t dummy3;
541 char * dummy4;
542 {
543 db_expr_t old_radix, pid, sig;
544 struct proc *p;
545
546 #define DB_ERROR(f) do { db_printf f; db_flush_lex(); goto out; } while (0)
547
548 /*
549 * PIDs and signal numbers are typically represented in base
550 * 10, so make that the default here. It can, of course, be
551 * overridden by specifying a prefix.
552 */
553 old_radix = db_radix;
554 db_radix = 10;
555 /* Retrieve arguments. */
556 if (!db_expression(&sig))
557 DB_ERROR(("Missing signal number\n"));
558 if (!db_expression(&pid))
559 DB_ERROR(("Missing process ID\n"));
560 db_skip_to_eol();
561 if (sig < 1 || sig > _SIG_MAXSIG)
562 DB_ERROR(("Signal number out of range\n"));
563
564 /*
565 * Find the process in question. allproc_lock is not needed
566 * since we're in DDB.
567 */
568 /* sx_slock(&allproc_lock); */
569 LIST_FOREACH(p, &allproc, p_list)
570 if (p->p_pid == pid)
571 break;
572 /* sx_sunlock(&allproc_lock); */
573 if (p == NULL)
574 DB_ERROR(("Can't find process with pid %ld\n", (long) pid));
575
576 /* If it's already locked, bail; otherwise, do the deed. */
577 if (PROC_TRYLOCK(p) == 0)
578 DB_ERROR(("Can't lock process with pid %ld\n", (long) pid));
579 else {
580 psignal(p, sig);
581 PROC_UNLOCK(p);
582 }
583
584 out:
585 db_radix = old_radix;
586 #undef DB_ERROR
587 }
588
589 static void
590 db_reset(dummy1, dummy2, dummy3, dummy4)
591 db_expr_t dummy1;
592 boolean_t dummy2;
593 db_expr_t dummy3;
594 char * dummy4;
595 {
596
597 cpu_reset();
598 }
599
600 static void
601 db_watchdog(dummy1, dummy2, dummy3, dummy4)
602 db_expr_t dummy1;
603 boolean_t dummy2;
604 db_expr_t dummy3;
605 char * dummy4;
606 {
607 int i;
608
609 /*
610 * XXX: It might make sense to be able to set the watchdog to a
611 * XXX: timeout here so that failure or hang as a result of subsequent
612 * XXX: ddb commands could be recovered by a reset.
613 */
614
615 EVENTHANDLER_INVOKE(watchdog_list, 0, &i);
616 }
617
618 static void
619 db_gdb(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3, char *dummy4)
620 {
621
622 if (kdb_dbbe_select("gdb") != 0)
623 db_printf("The remote GDB backend could not be selected.\n");
624 else
625 db_printf("Step to enter the remote GDB backend.\n");
626 }
627
628 static void
629 db_stack_trace(db_expr_t tid, boolean_t hastid, db_expr_t count, char *modif)
630 {
631 struct thread *td;
632 db_expr_t radix;
633 pid_t pid;
634 int t;
635
636 /*
637 * We parse our own arguments. We don't like the default radix.
638 */
639 radix = db_radix;
640 db_radix = 10;
641 hastid = db_expression(&tid);
642 t = db_read_token();
643 if (t == tCOMMA) {
644 if (!db_expression(&count)) {
645 db_printf("Count missing\n");
646 db_flush_lex();
647 return;
648 }
649 } else {
650 db_unread_token(t);
651 count = -1;
652 }
653 db_skip_to_eol();
654 db_radix = radix;
655
656 if (hastid) {
657 td = kdb_thr_lookup((lwpid_t)tid);
658 if (td == NULL)
659 td = kdb_thr_from_pid((pid_t)tid);
660 if (td == NULL) {
661 db_printf("Thread %d not found\n", (int)tid);
662 return;
663 }
664 } else
665 td = kdb_thread;
666 if (td->td_proc != NULL)
667 pid = td->td_proc->p_pid;
668 else
669 pid = -1;
670 db_printf("Tracing pid %d tid %ld td %p\n", pid, (long)td->td_tid, td);
671 db_trace_thread(td, count);
672 }
Cache object: 7db1f7fff7282f7c9f5ff0b8b0d81517
|