xref: /freebsd/sys/ddb/db_command.c (revision d93a896e)
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 	int have_subcommands;
329 
330 	have_subcommands = 0;
331 	LIST_FOREACH(cmd, table, next) {
332 		if (cmd->more != NULL)
333 			have_subcommands++;
334 		db_printf("%-16s", cmd->name);
335 		db_end_line(16);
336 	}
337 
338 	if (have_subcommands > 0) {
339 		db_printf("\nThe following have subcommands; append \"help\" "
340 		    "to list (e.g. \"show help\"):\n");
341 		LIST_FOREACH(cmd, table, next) {
342 			if (cmd->more == NULL)
343 				continue;
344 			db_printf("%-16s", cmd->name);
345 			db_end_line(16);
346 		}
347 	}
348 }
349 
350 static void
351 db_command(struct command **last_cmdp, struct command_table *cmd_table,
352     int dopager)
353 {
354 	struct command	*cmd = NULL;
355 	int		t;
356 	char		modif[TOK_STRING_SIZE];
357 	db_expr_t	addr, count;
358 	bool		have_addr = false;
359 	int		result;
360 
361 	t = db_read_token();
362 	if (t == tEOL) {
363 	    /* empty line repeats last command, at 'next' */
364 	    cmd = *last_cmdp;
365 	    addr = (db_expr_t)db_next;
366 	    have_addr = false;
367 	    count = 1;
368 	    modif[0] = '\0';
369 	}
370 	else if (t == tEXCL) {
371 	    db_fncall((db_expr_t)0, (bool)false, (db_expr_t)0, (char *)0);
372 	    return;
373 	}
374 	else if (t != tIDENT) {
375 	    db_printf("Unrecognized input; use \"help\" "
376 	        "to list available commands\n");
377 	    db_flush_lex();
378 	    return;
379 	}
380 	else {
381 	    /*
382 	     * Search for command
383 	     */
384 	    while (cmd_table) {
385 		result = db_cmd_search(db_tok_string,
386 				       cmd_table,
387 				       &cmd);
388 		switch (result) {
389 		    case CMD_NONE:
390 			db_printf("No such command; use \"help\" "
391 			    "to list available commands\n");
392 			db_flush_lex();
393 			return;
394 		    case CMD_AMBIGUOUS:
395 			db_printf("Ambiguous\n");
396 			db_flush_lex();
397 			return;
398 		    case CMD_HELP:
399 			if (cmd_table == &db_cmd_table) {
400 			    db_printf("This is ddb(4), the kernel debugger; "
401 			        "see http://man.freebsd.org/ddb/4 for help.\n");
402 			    db_printf("Use \"bt\" for backtrace, \"dump\" for "
403 			        "kernel core dump, \"reset\" to reboot.\n");
404 			    db_printf("Available commands:\n");
405 			}
406 			db_cmd_list(cmd_table);
407 			db_flush_lex();
408 			return;
409 		    default:
410 			break;
411 		}
412 		if ((cmd_table = cmd->more) != NULL) {
413 		    t = db_read_token();
414 		    if (t != tIDENT) {
415 			db_printf("Subcommand required; "
416 			    "available subcommands:\n");
417 			db_cmd_list(cmd_table);
418 			db_flush_lex();
419 			return;
420 		    }
421 		}
422 	    }
423 
424 	    if ((cmd->flag & CS_OWN) == 0) {
425 		/*
426 		 * Standard syntax:
427 		 * command [/modifier] [addr] [,count]
428 		 */
429 		t = db_read_token();
430 		if (t == tSLASH) {
431 		    t = db_read_token();
432 		    if (t != tIDENT) {
433 			db_printf("Bad modifier\n");
434 			db_flush_lex();
435 			return;
436 		    }
437 		    db_strcpy(modif, db_tok_string);
438 		}
439 		else {
440 		    db_unread_token(t);
441 		    modif[0] = '\0';
442 		}
443 
444 		if (db_expression(&addr)) {
445 		    db_dot = (db_addr_t) addr;
446 		    db_last_addr = db_dot;
447 		    have_addr = true;
448 		}
449 		else {
450 		    addr = (db_expr_t) db_dot;
451 		    have_addr = false;
452 		}
453 		t = db_read_token();
454 		if (t == tCOMMA) {
455 		    if (!db_expression(&count)) {
456 			db_printf("Count missing\n");
457 			db_flush_lex();
458 			return;
459 		    }
460 		}
461 		else {
462 		    db_unread_token(t);
463 		    count = -1;
464 		}
465 		if ((cmd->flag & CS_MORE) == 0) {
466 		    db_skip_to_eol();
467 		}
468 	    }
469 	}
470 	*last_cmdp = cmd;
471 	if (cmd != NULL) {
472 	    /*
473 	     * Execute the command.
474 	     */
475 	    if (dopager)
476 		db_enable_pager();
477 	    else
478 		db_disable_pager();
479 	    (*cmd->fcn)(addr, have_addr, count, modif);
480 	    if (dopager)
481 		db_disable_pager();
482 
483 	    if (cmd->flag & CS_SET_DOT) {
484 		/*
485 		 * If command changes dot, set dot to
486 		 * previous address displayed (if 'ed' style).
487 		 */
488 		if (db_ed_style) {
489 		    db_dot = db_prev;
490 		}
491 		else {
492 		    db_dot = db_next;
493 		}
494 	    }
495 	    else {
496 		/*
497 		 * If command does not change dot,
498 		 * set 'next' location to be the same.
499 		 */
500 		db_next = db_dot;
501 	    }
502 	}
503 }
504 
505 /*
506  * At least one non-optional command must be implemented using
507  * DB_COMMAND() so that db_cmd_set gets created.  Here is one.
508  */
509 DB_COMMAND(panic, db_panic)
510 {
511 	db_disable_pager();
512 	panic("from debugger");
513 }
514 
515 void
516 db_command_loop(void)
517 {
518 	/*
519 	 * Initialize 'prev' and 'next' to dot.
520 	 */
521 	db_prev = db_dot;
522 	db_next = db_dot;
523 
524 	db_cmd_loop_done = 0;
525 	while (!db_cmd_loop_done) {
526 	    if (db_print_position() != 0)
527 		db_printf("\n");
528 
529 	    db_printf("db> ");
530 	    (void) db_read_line();
531 
532 	    db_command(&db_last_command, &db_cmd_table, /* dopager */ 1);
533 	}
534 }
535 
536 /*
537  * Execute a command on behalf of a script.  The caller is responsible for
538  * making sure that the command string is < DB_MAXLINE or it will be
539  * truncated.
540  *
541  * XXXRW: Runs by injecting faked input into DDB input stream; it would be
542  * nicer to use an alternative approach that didn't mess with the previous
543  * command buffer.
544  */
545 void
546 db_command_script(const char *command)
547 {
548 	db_prev = db_next = db_dot;
549 	db_inject_line(command);
550 	db_command(&db_last_command, &db_cmd_table, /* dopager */ 0);
551 }
552 
553 void
554 db_error(const char *s)
555 {
556 	if (s)
557 	    db_printf("%s", s);
558 	db_flush_lex();
559 	kdb_reenter();
560 }
561 
562 static void
563 db_dump(db_expr_t dummy, bool dummy2, db_expr_t dummy3, char *dummy4)
564 {
565 	int error;
566 
567 	if (textdump_pending) {
568 		db_printf("textdump_pending set.\n"
569 		    "run \"textdump unset\" first or \"textdump dump\" for a textdump.\n");
570 		return;
571 	}
572 	error = doadump(false);
573 	if (error) {
574 		db_printf("Cannot dump: ");
575 		switch (error) {
576 		case EBUSY:
577 			db_printf("debugger got invoked while dumping.\n");
578 			break;
579 		case ENXIO:
580 			db_printf("no dump device specified.\n");
581 			break;
582 		default:
583 			db_printf("unknown error (error=%d).\n", error);
584 			break;
585 		}
586 	}
587 }
588 
589 /*
590  * Call random function:
591  * !expr(arg,arg,arg)
592  */
593 
594 /* The generic implementation supports a maximum of 10 arguments. */
595 typedef db_expr_t __db_f(db_expr_t, db_expr_t, db_expr_t, db_expr_t,
596     db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t);
597 
598 static __inline int
599 db_fncall_generic(db_expr_t addr, db_expr_t *rv, int nargs, db_expr_t args[])
600 {
601 	__db_f *f = (__db_f *)addr;
602 
603 	if (nargs > 10) {
604 		db_printf("Too many arguments (max 10)\n");
605 		return (0);
606 	}
607 	*rv = (*f)(args[0], args[1], args[2], args[3], args[4], args[5],
608 	    args[6], args[7], args[8], args[9]);
609 	return (1);
610 }
611 
612 static void
613 db_fncall(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
614 {
615 	db_expr_t	fn_addr;
616 	db_expr_t	args[DB_MAXARGS];
617 	int		nargs = 0;
618 	db_expr_t	retval;
619 	int		t;
620 
621 	if (!db_expression(&fn_addr)) {
622 	    db_printf("Bad function\n");
623 	    db_flush_lex();
624 	    return;
625 	}
626 
627 	t = db_read_token();
628 	if (t == tLPAREN) {
629 	    if (db_expression(&args[0])) {
630 		nargs++;
631 		while ((t = db_read_token()) == tCOMMA) {
632 		    if (nargs == DB_MAXARGS) {
633 			db_printf("Too many arguments (max %d)\n", DB_MAXARGS);
634 			db_flush_lex();
635 			return;
636 		    }
637 		    if (!db_expression(&args[nargs])) {
638 			db_printf("Argument missing\n");
639 			db_flush_lex();
640 			return;
641 		    }
642 		    nargs++;
643 		}
644 		db_unread_token(t);
645 	    }
646 	    if (db_read_token() != tRPAREN) {
647 	        db_printf("Mismatched parens\n");
648 		db_flush_lex();
649 		return;
650 	    }
651 	}
652 	db_skip_to_eol();
653 	db_disable_pager();
654 
655 	if (DB_CALL(fn_addr, &retval, nargs, args))
656 		db_printf("= %#lr\n", (long)retval);
657 }
658 
659 static void
660 db_halt(db_expr_t dummy, bool dummy2, db_expr_t dummy3, char *dummy4)
661 {
662 
663 	cpu_halt();
664 }
665 
666 static void
667 db_kill(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
668 {
669 	db_expr_t old_radix, pid, sig;
670 	struct proc *p;
671 
672 #define	DB_ERROR(f)	do { db_printf f; db_flush_lex(); goto out; } while (0)
673 
674 	/*
675 	 * PIDs and signal numbers are typically represented in base
676 	 * 10, so make that the default here.  It can, of course, be
677 	 * overridden by specifying a prefix.
678 	 */
679 	old_radix = db_radix;
680 	db_radix = 10;
681 	/* Retrieve arguments. */
682 	if (!db_expression(&sig))
683 		DB_ERROR(("Missing signal number\n"));
684 	if (!db_expression(&pid))
685 		DB_ERROR(("Missing process ID\n"));
686 	db_skip_to_eol();
687 	if (!_SIG_VALID(sig))
688 		DB_ERROR(("Signal number out of range\n"));
689 
690 	/*
691 	 * Find the process in question.  allproc_lock is not needed
692 	 * since we're in DDB.
693 	 */
694 	/* sx_slock(&allproc_lock); */
695 	FOREACH_PROC_IN_SYSTEM(p)
696 	    if (p->p_pid == pid)
697 		    break;
698 	/* sx_sunlock(&allproc_lock); */
699 	if (p == NULL)
700 		DB_ERROR(("Can't find process with pid %ld\n", (long) pid));
701 
702 	/* If it's already locked, bail; otherwise, do the deed. */
703 	if (PROC_TRYLOCK(p) == 0)
704 		DB_ERROR(("Can't lock process with pid %ld\n", (long) pid));
705 	else {
706 		pksignal(p, sig, NULL);
707 		PROC_UNLOCK(p);
708 	}
709 
710 out:
711 	db_radix = old_radix;
712 #undef DB_ERROR
713 }
714 
715 /*
716  * Reboot.  In case there is an additional argument, take it as delay in
717  * seconds.  Default to 15s if we cannot parse it and make sure we will
718  * never wait longer than 1 week.  Some code is similar to
719  * kern_shutdown.c:shutdown_panic().
720  */
721 #ifndef	DB_RESET_MAXDELAY
722 #define	DB_RESET_MAXDELAY	(3600 * 24 * 7)
723 #endif
724 
725 static void
726 db_reset(db_expr_t addr, bool have_addr, db_expr_t count __unused,
727     char *modif __unused)
728 {
729 	int delay, loop;
730 
731 	if (have_addr) {
732 		delay = (int)db_hex2dec(addr);
733 
734 		/* If we parse to fail, use 15s. */
735 		if (delay == -1)
736 			delay = 15;
737 
738 		/* Cap at one week. */
739 		if ((uintmax_t)delay > (uintmax_t)DB_RESET_MAXDELAY)
740 			delay = DB_RESET_MAXDELAY;
741 
742 		db_printf("Automatic reboot in %d seconds - "
743 		    "press a key on the console to abort\n", delay);
744 		for (loop = delay * 10; loop > 0; --loop) {
745 			DELAY(1000 * 100); /* 1/10th second */
746 			/* Did user type a key? */
747 			if (cncheckc() != -1)
748 				return;
749 		}
750 	}
751 
752 	cpu_reset();
753 }
754 
755 static void
756 db_watchdog(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
757 {
758 	db_expr_t old_radix, tout;
759 	int err, i;
760 
761 	old_radix = db_radix;
762 	db_radix = 10;
763 	err = db_expression(&tout);
764 	db_skip_to_eol();
765 	db_radix = old_radix;
766 
767 	/* If no argument is provided the watchdog will just be disabled. */
768 	if (err == 0) {
769 		db_printf("No argument provided, disabling watchdog\n");
770 		tout = 0;
771 	} else if ((tout & WD_INTERVAL) == WD_TO_NEVER) {
772 		db_error("Out of range watchdog interval\n");
773 		return;
774 	}
775 	EVENTHANDLER_INVOKE(watchdog_list, tout, &i);
776 }
777 
778 static void
779 db_gdb(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
780 {
781 
782 	if (kdb_dbbe_select("gdb") != 0) {
783 		db_printf("The remote GDB backend could not be selected.\n");
784 		return;
785 	}
786 	/*
787 	 * Mark that we are done in the debugger.  kdb_trap()
788 	 * should re-enter with the new backend.
789 	 */
790 	db_cmd_loop_done = 1;
791 	db_printf("(ctrl-c will return control to ddb)\n");
792 }
793 
794 static void
795 db_stack_trace(db_expr_t tid, bool hastid, db_expr_t count, char *modif)
796 {
797 	struct thread *td;
798 	db_expr_t radix;
799 	pid_t pid;
800 	int t;
801 
802 	/*
803 	 * We parse our own arguments. We don't like the default radix.
804 	 */
805 	radix = db_radix;
806 	db_radix = 10;
807 	hastid = db_expression(&tid);
808 	t = db_read_token();
809 	if (t == tCOMMA) {
810 		if (!db_expression(&count)) {
811 			db_printf("Count missing\n");
812 			db_flush_lex();
813 			return;
814 		}
815 	} else {
816 		db_unread_token(t);
817 		count = -1;
818 	}
819 	db_skip_to_eol();
820 	db_radix = radix;
821 
822 	if (hastid) {
823 		td = kdb_thr_lookup((lwpid_t)tid);
824 		if (td == NULL)
825 			td = kdb_thr_from_pid((pid_t)tid);
826 		if (td == NULL) {
827 			db_printf("Thread %d not found\n", (int)tid);
828 			return;
829 		}
830 	} else
831 		td = kdb_thread;
832 	if (td->td_proc != NULL)
833 		pid = td->td_proc->p_pid;
834 	else
835 		pid = -1;
836 	db_printf("Tracing pid %d tid %ld td %p\n", pid, (long)td->td_tid, td);
837 	db_trace_thread(td, count);
838 }
839 
840 static void
841 _db_stack_trace_all(bool active_only)
842 {
843 	struct proc *p;
844 	struct thread *td;
845 	jmp_buf jb;
846 	void *prev_jb;
847 
848 	FOREACH_PROC_IN_SYSTEM(p) {
849 		prev_jb = kdb_jmpbuf(jb);
850 		if (setjmp(jb) == 0) {
851 			FOREACH_THREAD_IN_PROC(p, td) {
852 				if (td->td_state == TDS_RUNNING)
853 					db_printf("\nTracing command %s pid %d"
854 					    " tid %ld td %p (CPU %d)\n",
855 					    p->p_comm, p->p_pid,
856 					    (long)td->td_tid, td,
857 					    td->td_oncpu);
858 				else if (active_only)
859 					continue;
860 				else
861 					db_printf("\nTracing command %s pid %d"
862 					    " tid %ld td %p\n", p->p_comm,
863 					    p->p_pid, (long)td->td_tid, td);
864 				db_trace_thread(td, -1);
865 				if (db_pager_quit) {
866 					kdb_jmpbuf(prev_jb);
867 					return;
868 				}
869 			}
870 		}
871 		kdb_jmpbuf(prev_jb);
872 	}
873 }
874 
875 static void
876 db_stack_trace_active(db_expr_t dummy, bool dummy2, db_expr_t dummy3,
877     char *dummy4)
878 {
879 
880 	_db_stack_trace_all(true);
881 }
882 
883 static void
884 db_stack_trace_all(db_expr_t dummy, bool dummy2, db_expr_t dummy3,
885     char *dummy4)
886 {
887 
888 	_db_stack_trace_all(false);
889 }
890 
891 /*
892  * Take the parsed expression value from the command line that was parsed
893  * as a hexadecimal value and convert it as if the expression was parsed
894  * as a decimal value.  Returns -1 if the expression was not a valid
895  * decimal value.
896  */
897 db_expr_t
898 db_hex2dec(db_expr_t expr)
899 {
900 	uintptr_t x, y;
901 	db_expr_t val;
902 
903 	y = 1;
904 	val = 0;
905 	x = expr;
906 	while (x != 0) {
907 		if (x % 16 > 9)
908 			return (-1);
909 		val += (x % 16) * (y);
910 		x >>= 4;
911 		y *= 10;
912 	}
913 	return (val);
914 }
915