xref: /freebsd/sys/ddb/db_command.c (revision 076ad2f836d5f49dc1375f1677335a48fe0d4b82)
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/conf.h>
48 #include <sys/watchdog.h>
49 #include <sys/kernel.h>
50 
51 #include <ddb/ddb.h>
52 #include <ddb/db_command.h>
53 #include <ddb/db_lex.h>
54 #include <ddb/db_output.h>
55 
56 #include <machine/cpu.h>
57 #include <machine/setjmp.h>
58 
59 /*
60  * Exported global variables
61  */
62 int		db_cmd_loop_done;
63 db_addr_t	db_dot;
64 db_addr_t	db_last_addr;
65 db_addr_t	db_prev;
66 db_addr_t	db_next;
67 
68 static db_cmdfcn_t	db_dump;
69 static db_cmdfcn_t	db_fncall;
70 static db_cmdfcn_t	db_gdb;
71 static db_cmdfcn_t	db_halt;
72 static db_cmdfcn_t	db_kill;
73 static db_cmdfcn_t	db_reset;
74 static db_cmdfcn_t	db_stack_trace;
75 static db_cmdfcn_t	db_stack_trace_active;
76 static db_cmdfcn_t	db_stack_trace_all;
77 static db_cmdfcn_t	db_watchdog;
78 
79 /*
80  * 'show' commands
81  */
82 
83 static struct command db_show_active_cmds[] = {
84 	{ "trace",	db_stack_trace_active,	0,	NULL },
85 };
86 struct command_table db_show_active_table =
87     LIST_HEAD_INITIALIZER(db_show_active_table);
88 
89 static struct command db_show_all_cmds[] = {
90 	{ "trace",	db_stack_trace_all,	0,	NULL },
91 };
92 struct command_table db_show_all_table =
93     LIST_HEAD_INITIALIZER(db_show_all_table);
94 
95 static struct command db_show_cmds[] = {
96 	{ "active",	0,			0,	&db_show_active_table },
97 	{ "all",	0,			0,	&db_show_all_table },
98 	{ "registers",	db_show_regs,		0,	NULL },
99 	{ "breaks",	db_listbreak_cmd, 	0,	NULL },
100 	{ "threads",	db_show_threads,	0,	NULL },
101 };
102 struct command_table db_show_table = LIST_HEAD_INITIALIZER(db_show_table);
103 
104 static struct command db_cmds[] = {
105 	{ "print",	db_print_cmd,		0,	NULL },
106 	{ "p",		db_print_cmd,		0,	NULL },
107 	{ "examine",	db_examine_cmd,		CS_SET_DOT, NULL },
108 	{ "x",		db_examine_cmd,		CS_SET_DOT, NULL },
109 	{ "search",	db_search_cmd,		CS_OWN|CS_SET_DOT, NULL },
110 	{ "set",	db_set_cmd,		CS_OWN,	NULL },
111 	{ "write",	db_write_cmd,		CS_MORE|CS_SET_DOT, NULL },
112 	{ "w",		db_write_cmd,		CS_MORE|CS_SET_DOT, NULL },
113 	{ "delete",	db_delete_cmd,		0,	NULL },
114 	{ "d",		db_delete_cmd,		0,	NULL },
115 	{ "dump",	db_dump,		0,	NULL },
116 	{ "break",	db_breakpoint_cmd,	0,	NULL },
117 	{ "b",		db_breakpoint_cmd,	0,	NULL },
118 	{ "dwatch",	db_deletewatch_cmd,	0,	NULL },
119 	{ "watch",	db_watchpoint_cmd,	CS_MORE,NULL },
120 	{ "dhwatch",	db_deletehwatch_cmd,	0,      NULL },
121 	{ "hwatch",	db_hwatchpoint_cmd,	0,      NULL },
122 	{ "step",	db_single_step_cmd,	0,	NULL },
123 	{ "s",		db_single_step_cmd,	0,	NULL },
124 	{ "continue",	db_continue_cmd,	0,	NULL },
125 	{ "c",		db_continue_cmd,	0,	NULL },
126 	{ "until",	db_trace_until_call_cmd,0,	NULL },
127 	{ "next",	db_trace_until_matching_cmd,0,	NULL },
128 	{ "match",	db_trace_until_matching_cmd,0,	NULL },
129 	{ "trace",	db_stack_trace,		CS_OWN,	NULL },
130 	{ "t",		db_stack_trace,		CS_OWN,	NULL },
131 	/* XXX alias for active trace */
132 	{ "acttrace",	db_stack_trace_active,	0,	NULL },
133 	/* XXX alias for all trace */
134 	{ "alltrace",	db_stack_trace_all,	0,	NULL },
135 	{ "where",	db_stack_trace,		CS_OWN,	NULL },
136 	{ "bt",		db_stack_trace,		CS_OWN,	NULL },
137 	{ "call",	db_fncall,		CS_OWN,	NULL },
138 	{ "show",	0,			0,	&db_show_table },
139 	{ "ps",		db_ps,			0,	NULL },
140 	{ "gdb",	db_gdb,			0,	NULL },
141 	{ "halt",	db_halt,		0,	NULL },
142 	{ "reboot",	db_reset,		0,	NULL },
143 	{ "reset",	db_reset,		0,	NULL },
144 	{ "kill",	db_kill,		CS_OWN,	NULL },
145 	{ "watchdog",	db_watchdog,		CS_OWN,	NULL },
146 	{ "thread",	db_set_thread,		CS_OWN,	NULL },
147 	{ "run",	db_run_cmd,		CS_OWN,	NULL },
148 	{ "script",	db_script_cmd,		CS_OWN,	NULL },
149 	{ "scripts",	db_scripts_cmd,		0,	NULL },
150 	{ "unscript",	db_unscript_cmd,	CS_OWN,	NULL },
151 	{ "capture",	db_capture_cmd,		CS_OWN,	NULL },
152 	{ "textdump",	db_textdump_cmd,	CS_OWN, NULL },
153 	{ "findstack",	db_findstack_cmd,	0,	NULL },
154 };
155 struct command_table db_cmd_table = LIST_HEAD_INITIALIZER(db_cmd_table);
156 
157 static struct command	*db_last_command = NULL;
158 
159 /*
160  * if 'ed' style: 'dot' is set at start of last item printed,
161  * and '+' points to next line.
162  * Otherwise: 'dot' points to next item, '..' points to last.
163  */
164 static bool	db_ed_style = true;
165 
166 /*
167  * Utility routine - discard tokens through end-of-line.
168  */
169 void
170 db_skip_to_eol(void)
171 {
172 	int	t;
173 	do {
174 	    t = db_read_token();
175 	} while (t != tEOL);
176 }
177 
178 /*
179  * Results of command search.
180  */
181 #define	CMD_UNIQUE	0
182 #define	CMD_FOUND	1
183 #define	CMD_NONE	2
184 #define	CMD_AMBIGUOUS	3
185 #define	CMD_HELP	4
186 
187 static void	db_cmd_match(char *name, struct command *cmd,
188 		    struct command **cmdp, int *resultp);
189 static void	db_cmd_list(struct command_table *table);
190 static int	db_cmd_search(char *name, struct command_table *table,
191 		    struct command **cmdp);
192 static void	db_command(struct command **last_cmdp,
193 		    struct command_table *cmd_table, int dopager);
194 
195 /*
196  * Initialize the command lists from the static tables.
197  */
198 void
199 db_command_init(void)
200 {
201 #define	N(a)	(sizeof(a) / sizeof(a[0]))
202 	int i;
203 
204 	for (i = 0; i < N(db_cmds); i++)
205 		db_command_register(&db_cmd_table, &db_cmds[i]);
206 	for (i = 0; i < N(db_show_cmds); i++)
207 		db_command_register(&db_show_table, &db_show_cmds[i]);
208 	for (i = 0; i < N(db_show_active_cmds); i++)
209 		db_command_register(&db_show_active_table,
210 		    &db_show_active_cmds[i]);
211 	for (i = 0; i < N(db_show_all_cmds); i++)
212 		db_command_register(&db_show_all_table, &db_show_all_cmds[i]);
213 #undef N
214 }
215 
216 /*
217  * Register a command.
218  */
219 void
220 db_command_register(struct command_table *list, struct command *cmd)
221 {
222 	struct command *c, *last;
223 
224 	last = NULL;
225 	LIST_FOREACH(c, list, next) {
226 		int n = strcmp(cmd->name, c->name);
227 
228 		/* Check that the command is not already present. */
229 		if (n == 0) {
230 			printf("%s: Warning, the command \"%s\" already exists;"
231 			     " ignoring request\n", __func__, cmd->name);
232 			return;
233 		}
234 		if (n < 0) {
235 			/* NB: keep list sorted lexicographically */
236 			LIST_INSERT_BEFORE(c, cmd, next);
237 			return;
238 		}
239 		last = c;
240 	}
241 	if (last == NULL)
242 		LIST_INSERT_HEAD(list, cmd, next);
243 	else
244 		LIST_INSERT_AFTER(last, cmd, next);
245 }
246 
247 /*
248  * Remove a command previously registered with db_command_register.
249  */
250 void
251 db_command_unregister(struct command_table *list, struct command *cmd)
252 {
253 	struct command *c;
254 
255 	LIST_FOREACH(c, list, next) {
256 		if (cmd == c) {
257 			LIST_REMOVE(cmd, next);
258 			return;
259 		}
260 	}
261 	/* NB: intentionally quiet */
262 }
263 
264 /*
265  * Helper function to match a single command.
266  */
267 static void
268 db_cmd_match(char *name, struct command *cmd, struct command **cmdp,
269     int *resultp)
270 {
271 	char *lp, *rp;
272 	int c;
273 
274 	lp = name;
275 	rp = cmd->name;
276 	while ((c = *lp) == *rp) {
277 		if (c == 0) {
278 			/* complete match */
279 			*cmdp = cmd;
280 			*resultp = CMD_UNIQUE;
281 			return;
282 		}
283 		lp++;
284 		rp++;
285 	}
286 	if (c == 0) {
287 		/* end of name, not end of command -
288 		   partial match */
289 		if (*resultp == CMD_FOUND) {
290 			*resultp = CMD_AMBIGUOUS;
291 			/* but keep looking for a full match -
292 			   this lets us match single letters */
293 		} else {
294 			*cmdp = cmd;
295 			*resultp = CMD_FOUND;
296 		}
297 	}
298 }
299 
300 /*
301  * Search for command prefix.
302  */
303 static int
304 db_cmd_search(char *name, struct command_table *table, struct command **cmdp)
305 {
306 	struct command	*cmd;
307 	int		result = CMD_NONE;
308 
309 	LIST_FOREACH(cmd, table, next) {
310 		db_cmd_match(name,cmd,cmdp,&result);
311 		if (result == CMD_UNIQUE)
312 			break;
313 	}
314 
315 	if (result == CMD_NONE) {
316 		/* check for 'help' */
317 		if (name[0] == 'h' && name[1] == 'e'
318 		    && name[2] == 'l' && name[3] == 'p')
319 			result = CMD_HELP;
320 	}
321 	return (result);
322 }
323 
324 static void
325 db_cmd_list(struct command_table *table)
326 {
327 	struct command	*cmd;
328 
329 	LIST_FOREACH(cmd, table, next) {
330 		db_printf("%-16s", cmd->name);
331 		db_end_line(16);
332 	}
333 }
334 
335 static void
336 db_command(struct command **last_cmdp, struct command_table *cmd_table,
337     int dopager)
338 {
339 	struct command	*cmd = NULL;
340 	int		t;
341 	char		modif[TOK_STRING_SIZE];
342 	db_expr_t	addr, count;
343 	bool		have_addr = false;
344 	int		result;
345 
346 	t = db_read_token();
347 	if (t == tEOL) {
348 	    /* empty line repeats last command, at 'next' */
349 	    cmd = *last_cmdp;
350 	    addr = (db_expr_t)db_next;
351 	    have_addr = false;
352 	    count = 1;
353 	    modif[0] = '\0';
354 	}
355 	else if (t == tEXCL) {
356 	    db_fncall((db_expr_t)0, (bool)false, (db_expr_t)0, (char *)0);
357 	    return;
358 	}
359 	else if (t != tIDENT) {
360 	    db_printf("?\n");
361 	    db_flush_lex();
362 	    return;
363 	}
364 	else {
365 	    /*
366 	     * Search for command
367 	     */
368 	    while (cmd_table) {
369 		result = db_cmd_search(db_tok_string,
370 				       cmd_table,
371 				       &cmd);
372 		switch (result) {
373 		    case CMD_NONE:
374 			db_printf("No such command\n");
375 			db_flush_lex();
376 			return;
377 		    case CMD_AMBIGUOUS:
378 			db_printf("Ambiguous\n");
379 			db_flush_lex();
380 			return;
381 		    case CMD_HELP:
382 			db_cmd_list(cmd_table);
383 			db_flush_lex();
384 			return;
385 		    default:
386 			break;
387 		}
388 		if ((cmd_table = cmd->more) != NULL) {
389 		    t = db_read_token();
390 		    if (t != tIDENT) {
391 			db_cmd_list(cmd_table);
392 			db_flush_lex();
393 			return;
394 		    }
395 		}
396 	    }
397 
398 	    if ((cmd->flag & CS_OWN) == 0) {
399 		/*
400 		 * Standard syntax:
401 		 * command [/modifier] [addr] [,count]
402 		 */
403 		t = db_read_token();
404 		if (t == tSLASH) {
405 		    t = db_read_token();
406 		    if (t != tIDENT) {
407 			db_printf("Bad modifier\n");
408 			db_flush_lex();
409 			return;
410 		    }
411 		    db_strcpy(modif, db_tok_string);
412 		}
413 		else {
414 		    db_unread_token(t);
415 		    modif[0] = '\0';
416 		}
417 
418 		if (db_expression(&addr)) {
419 		    db_dot = (db_addr_t) addr;
420 		    db_last_addr = db_dot;
421 		    have_addr = true;
422 		}
423 		else {
424 		    addr = (db_expr_t) db_dot;
425 		    have_addr = false;
426 		}
427 		t = db_read_token();
428 		if (t == tCOMMA) {
429 		    if (!db_expression(&count)) {
430 			db_printf("Count missing\n");
431 			db_flush_lex();
432 			return;
433 		    }
434 		}
435 		else {
436 		    db_unread_token(t);
437 		    count = -1;
438 		}
439 		if ((cmd->flag & CS_MORE) == 0) {
440 		    db_skip_to_eol();
441 		}
442 	    }
443 	}
444 	*last_cmdp = cmd;
445 	if (cmd != NULL) {
446 	    /*
447 	     * Execute the command.
448 	     */
449 	    if (dopager)
450 		db_enable_pager();
451 	    else
452 		db_disable_pager();
453 	    (*cmd->fcn)(addr, have_addr, count, modif);
454 	    if (dopager)
455 		db_disable_pager();
456 
457 	    if (cmd->flag & CS_SET_DOT) {
458 		/*
459 		 * If command changes dot, set dot to
460 		 * previous address displayed (if 'ed' style).
461 		 */
462 		if (db_ed_style) {
463 		    db_dot = db_prev;
464 		}
465 		else {
466 		    db_dot = db_next;
467 		}
468 	    }
469 	    else {
470 		/*
471 		 * If command does not change dot,
472 		 * set 'next' location to be the same.
473 		 */
474 		db_next = db_dot;
475 	    }
476 	}
477 }
478 
479 /*
480  * At least one non-optional command must be implemented using
481  * DB_COMMAND() so that db_cmd_set gets created.  Here is one.
482  */
483 DB_COMMAND(panic, db_panic)
484 {
485 	db_disable_pager();
486 	panic("from debugger");
487 }
488 
489 void
490 db_command_loop(void)
491 {
492 	/*
493 	 * Initialize 'prev' and 'next' to dot.
494 	 */
495 	db_prev = db_dot;
496 	db_next = db_dot;
497 
498 	db_cmd_loop_done = 0;
499 	while (!db_cmd_loop_done) {
500 	    if (db_print_position() != 0)
501 		db_printf("\n");
502 
503 	    db_printf("db> ");
504 	    (void) db_read_line();
505 
506 	    db_command(&db_last_command, &db_cmd_table, /* dopager */ 1);
507 	}
508 }
509 
510 /*
511  * Execute a command on behalf of a script.  The caller is responsible for
512  * making sure that the command string is < DB_MAXLINE or it will be
513  * truncated.
514  *
515  * XXXRW: Runs by injecting faked input into DDB input stream; it would be
516  * nicer to use an alternative approach that didn't mess with the previous
517  * command buffer.
518  */
519 void
520 db_command_script(const char *command)
521 {
522 	db_prev = db_next = db_dot;
523 	db_inject_line(command);
524 	db_command(&db_last_command, &db_cmd_table, /* dopager */ 0);
525 }
526 
527 void
528 db_error(const char *s)
529 {
530 	if (s)
531 	    db_printf("%s", s);
532 	db_flush_lex();
533 	kdb_reenter();
534 }
535 
536 static void
537 db_dump(db_expr_t dummy, bool dummy2, db_expr_t dummy3, char *dummy4)
538 {
539 	int error;
540 
541 	if (textdump_pending) {
542 		db_printf("textdump_pending set.\n"
543 		    "run \"textdump unset\" first or \"textdump dump\" for a textdump.\n");
544 		return;
545 	}
546 	error = doadump(false);
547 	if (error) {
548 		db_printf("Cannot dump: ");
549 		switch (error) {
550 		case EBUSY:
551 			db_printf("debugger got invoked while dumping.\n");
552 			break;
553 		case ENXIO:
554 			db_printf("no dump device specified.\n");
555 			break;
556 		default:
557 			db_printf("unknown error (error=%d).\n", error);
558 			break;
559 		}
560 	}
561 }
562 
563 /*
564  * Call random function:
565  * !expr(arg,arg,arg)
566  */
567 
568 /* The generic implementation supports a maximum of 10 arguments. */
569 typedef db_expr_t __db_f(db_expr_t, db_expr_t, db_expr_t, db_expr_t,
570     db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t);
571 
572 static __inline int
573 db_fncall_generic(db_expr_t addr, db_expr_t *rv, int nargs, db_expr_t args[])
574 {
575 	__db_f *f = (__db_f *)addr;
576 
577 	if (nargs > 10) {
578 		db_printf("Too many arguments (max 10)\n");
579 		return (0);
580 	}
581 	*rv = (*f)(args[0], args[1], args[2], args[3], args[4], args[5],
582 	    args[6], args[7], args[8], args[9]);
583 	return (1);
584 }
585 
586 static void
587 db_fncall(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
588 {
589 	db_expr_t	fn_addr;
590 	db_expr_t	args[DB_MAXARGS];
591 	int		nargs = 0;
592 	db_expr_t	retval;
593 	int		t;
594 
595 	if (!db_expression(&fn_addr)) {
596 	    db_printf("Bad function\n");
597 	    db_flush_lex();
598 	    return;
599 	}
600 
601 	t = db_read_token();
602 	if (t == tLPAREN) {
603 	    if (db_expression(&args[0])) {
604 		nargs++;
605 		while ((t = db_read_token()) == tCOMMA) {
606 		    if (nargs == DB_MAXARGS) {
607 			db_printf("Too many arguments (max %d)\n", DB_MAXARGS);
608 			db_flush_lex();
609 			return;
610 		    }
611 		    if (!db_expression(&args[nargs])) {
612 			db_printf("Argument missing\n");
613 			db_flush_lex();
614 			return;
615 		    }
616 		    nargs++;
617 		}
618 		db_unread_token(t);
619 	    }
620 	    if (db_read_token() != tRPAREN) {
621 		db_printf("?\n");
622 		db_flush_lex();
623 		return;
624 	    }
625 	}
626 	db_skip_to_eol();
627 	db_disable_pager();
628 
629 	if (DB_CALL(fn_addr, &retval, nargs, args))
630 		db_printf("= %#lr\n", (long)retval);
631 }
632 
633 static void
634 db_halt(db_expr_t dummy, bool dummy2, db_expr_t dummy3, char *dummy4)
635 {
636 
637 	cpu_halt();
638 }
639 
640 static void
641 db_kill(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
642 {
643 	db_expr_t old_radix, pid, sig;
644 	struct proc *p;
645 
646 #define	DB_ERROR(f)	do { db_printf f; db_flush_lex(); goto out; } while (0)
647 
648 	/*
649 	 * PIDs and signal numbers are typically represented in base
650 	 * 10, so make that the default here.  It can, of course, be
651 	 * overridden by specifying a prefix.
652 	 */
653 	old_radix = db_radix;
654 	db_radix = 10;
655 	/* Retrieve arguments. */
656 	if (!db_expression(&sig))
657 		DB_ERROR(("Missing signal number\n"));
658 	if (!db_expression(&pid))
659 		DB_ERROR(("Missing process ID\n"));
660 	db_skip_to_eol();
661 	if (!_SIG_VALID(sig))
662 		DB_ERROR(("Signal number out of range\n"));
663 
664 	/*
665 	 * Find the process in question.  allproc_lock is not needed
666 	 * since we're in DDB.
667 	 */
668 	/* sx_slock(&allproc_lock); */
669 	FOREACH_PROC_IN_SYSTEM(p)
670 	    if (p->p_pid == pid)
671 		    break;
672 	/* sx_sunlock(&allproc_lock); */
673 	if (p == NULL)
674 		DB_ERROR(("Can't find process with pid %ld\n", (long) pid));
675 
676 	/* If it's already locked, bail; otherwise, do the deed. */
677 	if (PROC_TRYLOCK(p) == 0)
678 		DB_ERROR(("Can't lock process with pid %ld\n", (long) pid));
679 	else {
680 		pksignal(p, sig, NULL);
681 		PROC_UNLOCK(p);
682 	}
683 
684 out:
685 	db_radix = old_radix;
686 #undef DB_ERROR
687 }
688 
689 /*
690  * Reboot.  In case there is an additional argument, take it as delay in
691  * seconds.  Default to 15s if we cannot parse it and make sure we will
692  * never wait longer than 1 week.  Some code is similar to
693  * kern_shutdown.c:shutdown_panic().
694  */
695 #ifndef	DB_RESET_MAXDELAY
696 #define	DB_RESET_MAXDELAY	(3600 * 24 * 7)
697 #endif
698 
699 static void
700 db_reset(db_expr_t addr, bool have_addr, db_expr_t count __unused,
701     char *modif __unused)
702 {
703 	int delay, loop;
704 
705 	if (have_addr) {
706 		delay = (int)db_hex2dec(addr);
707 
708 		/* If we parse to fail, use 15s. */
709 		if (delay == -1)
710 			delay = 15;
711 
712 		/* Cap at one week. */
713 		if ((uintmax_t)delay > (uintmax_t)DB_RESET_MAXDELAY)
714 			delay = DB_RESET_MAXDELAY;
715 
716 		db_printf("Automatic reboot in %d seconds - "
717 		    "press a key on the console to abort\n", delay);
718 		for (loop = delay * 10; loop > 0; --loop) {
719 			DELAY(1000 * 100); /* 1/10th second */
720 			/* Did user type a key? */
721 			if (cncheckc() != -1)
722 				return;
723 		}
724 	}
725 
726 	cpu_reset();
727 }
728 
729 static void
730 db_watchdog(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
731 {
732 	db_expr_t old_radix, tout;
733 	int err, i;
734 
735 	old_radix = db_radix;
736 	db_radix = 10;
737 	err = db_expression(&tout);
738 	db_skip_to_eol();
739 	db_radix = old_radix;
740 
741 	/* If no argument is provided the watchdog will just be disabled. */
742 	if (err == 0) {
743 		db_printf("No argument provided, disabling watchdog\n");
744 		tout = 0;
745 	} else if ((tout & WD_INTERVAL) == WD_TO_NEVER) {
746 		db_error("Out of range watchdog interval\n");
747 		return;
748 	}
749 	EVENTHANDLER_INVOKE(watchdog_list, tout, &i);
750 }
751 
752 static void
753 db_gdb(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
754 {
755 
756 	if (kdb_dbbe_select("gdb") != 0) {
757 		db_printf("The remote GDB backend could not be selected.\n");
758 		return;
759 	}
760 	/*
761 	 * Mark that we are done in the debugger.  kdb_trap()
762 	 * should re-enter with the new backend.
763 	 */
764 	db_cmd_loop_done = 1;
765 	db_printf("(ctrl-c will return control to ddb)\n");
766 }
767 
768 static void
769 db_stack_trace(db_expr_t tid, bool hastid, db_expr_t count, char *modif)
770 {
771 	struct thread *td;
772 	db_expr_t radix;
773 	pid_t pid;
774 	int t;
775 
776 	/*
777 	 * We parse our own arguments. We don't like the default radix.
778 	 */
779 	radix = db_radix;
780 	db_radix = 10;
781 	hastid = db_expression(&tid);
782 	t = db_read_token();
783 	if (t == tCOMMA) {
784 		if (!db_expression(&count)) {
785 			db_printf("Count missing\n");
786 			db_flush_lex();
787 			return;
788 		}
789 	} else {
790 		db_unread_token(t);
791 		count = -1;
792 	}
793 	db_skip_to_eol();
794 	db_radix = radix;
795 
796 	if (hastid) {
797 		td = kdb_thr_lookup((lwpid_t)tid);
798 		if (td == NULL)
799 			td = kdb_thr_from_pid((pid_t)tid);
800 		if (td == NULL) {
801 			db_printf("Thread %d not found\n", (int)tid);
802 			return;
803 		}
804 	} else
805 		td = kdb_thread;
806 	if (td->td_proc != NULL)
807 		pid = td->td_proc->p_pid;
808 	else
809 		pid = -1;
810 	db_printf("Tracing pid %d tid %ld td %p\n", pid, (long)td->td_tid, td);
811 	db_trace_thread(td, count);
812 }
813 
814 static void
815 _db_stack_trace_all(bool active_only)
816 {
817 	struct proc *p;
818 	struct thread *td;
819 	jmp_buf jb;
820 	void *prev_jb;
821 
822 	FOREACH_PROC_IN_SYSTEM(p) {
823 		prev_jb = kdb_jmpbuf(jb);
824 		if (setjmp(jb) == 0) {
825 			FOREACH_THREAD_IN_PROC(p, td) {
826 				if (td->td_state == TDS_RUNNING)
827 					db_printf("\nTracing command %s pid %d"
828 					    " tid %ld td %p (CPU %d)\n",
829 					    p->p_comm, p->p_pid,
830 					    (long)td->td_tid, td,
831 					    td->td_oncpu);
832 				else if (active_only)
833 					continue;
834 				else
835 					db_printf("\nTracing command %s pid %d"
836 					    " tid %ld td %p\n", p->p_comm,
837 					    p->p_pid, (long)td->td_tid, td);
838 				db_trace_thread(td, -1);
839 				if (db_pager_quit) {
840 					kdb_jmpbuf(prev_jb);
841 					return;
842 				}
843 			}
844 		}
845 		kdb_jmpbuf(prev_jb);
846 	}
847 }
848 
849 static void
850 db_stack_trace_active(db_expr_t dummy, bool dummy2, db_expr_t dummy3,
851     char *dummy4)
852 {
853 
854 	_db_stack_trace_all(true);
855 }
856 
857 static void
858 db_stack_trace_all(db_expr_t dummy, bool dummy2, db_expr_t dummy3,
859     char *dummy4)
860 {
861 
862 	_db_stack_trace_all(false);
863 }
864 
865 /*
866  * Take the parsed expression value from the command line that was parsed
867  * as a hexadecimal value and convert it as if the expression was parsed
868  * as a decimal value.  Returns -1 if the expression was not a valid
869  * decimal value.
870  */
871 db_expr_t
872 db_hex2dec(db_expr_t expr)
873 {
874 	uintptr_t x, y;
875 	db_expr_t val;
876 
877 	y = 1;
878 	val = 0;
879 	x = expr;
880 	while (x != 0) {
881 		if (x % 16 > 9)
882 			return (-1);
883 		val += (x % 16) * (y);
884 		x >>= 4;
885 		y *= 10;
886 	}
887 	return (val);
888 }
889