xref: /netbsd-src/sys/ddb/db_command.c (revision df0caa2637da0538ecdf6b878c4d08e684b43d8f)
1 /*	$NetBSD: db_command.c,v 1.79 2005/06/01 12:25:27 drochner 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 "AS IS"
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@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 /*
30  * Command dispatcher.
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: db_command.c,v 1.79 2005/06/01 12:25:27 drochner Exp $");
35 
36 #include "opt_ddb.h"
37 #include "opt_kgdb.h"
38 #include "opt_inet.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/reboot.h>
43 #include <sys/device.h>
44 #include <sys/malloc.h>
45 #include <sys/namei.h>
46 #include <sys/pool.h>
47 #include <sys/proc.h>
48 #include <sys/vnode.h>
49 
50 #include <machine/db_machdep.h>		/* type definitions */
51 
52 #if defined(_KERNEL_OPT)
53 #include "opt_multiprocessor.h"
54 #endif
55 #ifdef MULTIPROCESSOR
56 #include <machine/cpu.h>
57 #endif
58 
59 #include <ddb/db_lex.h>
60 #include <ddb/db_output.h>
61 #include <ddb/db_command.h>
62 #include <ddb/db_break.h>
63 #include <ddb/db_watch.h>
64 #include <ddb/db_run.h>
65 #include <ddb/db_variables.h>
66 #include <ddb/db_interface.h>
67 #include <ddb/db_sym.h>
68 #include <ddb/db_extern.h>
69 
70 #include <uvm/uvm_extern.h>
71 #include <uvm/uvm_ddb.h>
72 
73 #include "arp.h"
74 
75 /*
76  * Results of command search.
77  */
78 #define	CMD_UNIQUE	0
79 #define	CMD_FOUND	1
80 #define	CMD_NONE	2
81 #define	CMD_AMBIGUOUS	3
82 #define	CMD_HELP	4
83 
84 /*
85  * Exported global variables
86  */
87 boolean_t	db_cmd_loop_done;
88 label_t		*db_recover;
89 db_addr_t	db_dot;
90 db_addr_t	db_last_addr;
91 db_addr_t	db_prev;
92 db_addr_t	db_next;
93 
94 /*
95  * if 'ed' style: 'dot' is set at start of last item printed,
96  * and '+' points to next line.
97  * Otherwise: 'dot' points to next item, '..' points to last.
98  */
99 static boolean_t db_ed_style = TRUE;
100 
101 static void	db_buf_print_cmd(db_expr_t, int, db_expr_t, const char *);
102 static void	db_cmd_list(const struct db_command *);
103 static int	db_cmd_search(const char *, const struct db_command *,
104 		    const struct db_command **);
105 static void	db_command(const struct db_command **,
106 		    const struct db_command *);
107 static void	db_event_print_cmd(db_expr_t, int, db_expr_t, const char *);
108 static void	db_fncall(db_expr_t, int, db_expr_t, const char *);
109 static void	db_malloc_print_cmd(db_expr_t, int, db_expr_t, const char *);
110 static void	db_map_print_cmd(db_expr_t, int, db_expr_t, const char *);
111 static void	db_namecache_print_cmd(db_expr_t, int, db_expr_t, const char *);
112 static void	db_object_print_cmd(db_expr_t, int, db_expr_t, const char *);
113 static void	db_page_print_cmd(db_expr_t, int, db_expr_t, const char *);
114 static void	db_pool_print_cmd(db_expr_t, int, db_expr_t, const char *);
115 static void	db_reboot_cmd(db_expr_t, int, db_expr_t, const char *);
116 static void	db_sifting_cmd(db_expr_t, int, db_expr_t, const char *);
117 static void	db_stack_trace_cmd(db_expr_t, int, db_expr_t, const char *);
118 static void	db_sync_cmd(db_expr_t, int, db_expr_t, const char *);
119 static void	db_uvmexp_print_cmd(db_expr_t, int, db_expr_t, const char *);
120 static void	db_vnode_print_cmd(db_expr_t, int, db_expr_t, const char *);
121 static void	db_mount_print_cmd(db_expr_t, int, db_expr_t, const char *);
122 
123 /*
124  * 'show' commands
125  */
126 
127 static const struct db_command db_show_all_cmds[] = {
128 	{ "callout",	db_show_callout,	0, NULL },
129 	{ "procs",	db_show_all_procs,	0, NULL },
130 	{ NULL, 	NULL, 			0, NULL }
131 };
132 
133 static const struct db_command db_show_cmds[] = {
134 	{ "all",	NULL,			0,	db_show_all_cmds },
135 #if defined(INET) && (NARP > 0)
136 	{ "arptab",	db_show_arptab,		0,	NULL },
137 #endif
138 	{ "breaks",	db_listbreak_cmd, 	0,	NULL },
139 	{ "buf",	db_buf_print_cmd,	0,	NULL },
140 	{ "event",	db_event_print_cmd,	0,	NULL },
141 	{ "malloc",	db_malloc_print_cmd,	0,	NULL },
142 	{ "map",	db_map_print_cmd,	0,	NULL },
143 	{ "mount",	db_mount_print_cmd,	0,	NULL },
144 	{ "ncache",	db_namecache_print_cmd,	0,	NULL },
145 	{ "object",	db_object_print_cmd,	0,	NULL },
146 	{ "page",	db_page_print_cmd,	0,	NULL },
147 	{ "pool",	db_pool_print_cmd,	0,	NULL },
148 	{ "registers",	db_show_regs,		0,	NULL },
149 	{ "sched_qs",	db_show_sched_qs,	0,	NULL },
150 	{ "uvmexp",	db_uvmexp_print_cmd,	0,	NULL },
151 	{ "vnode",	db_vnode_print_cmd,	0,	NULL },
152 	{ "watches",	db_listwatch_cmd, 	0,	NULL },
153 	{ NULL,		NULL,			0,	NULL }
154 };
155 
156 /* arch/<arch>/<arch>/db_interface.c */
157 #ifdef DB_MACHINE_COMMANDS
158 extern const struct db_command db_machine_command_table[];
159 #endif
160 
161 static const struct db_command db_command_table[] = {
162 	{ "b",		db_breakpoint_cmd,	0,		NULL },
163 	{ "break",	db_breakpoint_cmd,	0,		NULL },
164 	{ "bt",		db_stack_trace_cmd,	0,		NULL },
165 	{ "c",		db_continue_cmd,	0,		NULL },
166 	{ "call",	db_fncall,		CS_OWN,		NULL },
167 	{ "callout",	db_show_callout,	0,		NULL },
168 	{ "continue",	db_continue_cmd,	0,		NULL },
169 	{ "d",		db_delete_cmd,		0,		NULL },
170 	{ "delete",	db_delete_cmd,		0,		NULL },
171 	{ "dmesg",	db_dmesg,		0,		NULL },
172 	{ "dwatch",	db_deletewatch_cmd,	0,		NULL },
173 	{ "examine",	db_examine_cmd,		CS_SET_DOT, 	NULL },
174 	{ "kill",	db_kill_proc,		CS_OWN,		NULL },
175 #ifdef KGDB
176 	{ "kgdb",	db_kgdb_cmd,		0,		NULL },
177 #endif
178 #ifdef DB_MACHINE_COMMANDS
179 	{ "machine",	NULL,			0, db_machine_command_table },
180 #endif
181 	{ "match",	db_trace_until_matching_cmd,0,		NULL },
182 	{ "next",	db_trace_until_matching_cmd,0,		NULL },
183 	{ "p",		db_print_cmd,		0,		NULL },
184 	{ "print",	db_print_cmd,		0,		NULL },
185 	{ "ps",		db_show_all_procs,	0,		NULL },
186 	{ "reboot",	db_reboot_cmd,		CS_OWN,		NULL },
187 	{ "s",		db_single_step_cmd,	0,		NULL },
188 	{ "search",	db_search_cmd,		CS_OWN|CS_SET_DOT, NULL },
189 	{ "set",	db_set_cmd,		CS_OWN,		NULL },
190 	{ "show",	NULL,			0,		db_show_cmds },
191 	{ "sifting",	db_sifting_cmd,		CS_OWN,		NULL },
192 	{ "step",	db_single_step_cmd,	0,		NULL },
193 	{ "sync",	db_sync_cmd,		CS_OWN,		NULL },
194 	{ "trace",	db_stack_trace_cmd,	0,		NULL },
195 	{ "until",	db_trace_until_call_cmd,0,		NULL },
196 	{ "w",		db_write_cmd,		CS_MORE|CS_SET_DOT, NULL },
197 	{ "watch",	db_watchpoint_cmd,	CS_MORE,	NULL },
198 	{ "write",	db_write_cmd,		CS_MORE|CS_SET_DOT, NULL },
199 	{ "x",		db_examine_cmd,		CS_SET_DOT, 	NULL },
200 	{ NULL, 	NULL,			0,		NULL }
201 };
202 
203 static const struct db_command	*db_last_command = NULL;
204 
205 /*
206  * Utility routine - discard tokens through end-of-line.
207  */
208 void
209 db_skip_to_eol(void)
210 {
211 	int t;
212 
213 	do {
214 		t = db_read_token();
215 	} while (t != tEOL);
216 }
217 
218 void
219 db_error(s)
220 	const char *s;
221 {
222 
223 	if (s)
224 		db_printf("%s", s);
225 	db_flush_lex();
226 	longjmp(db_recover);
227 }
228 
229 void
230 db_command_loop(void)
231 {
232 	label_t	db_jmpbuf;
233 	label_t	*savejmp;
234 
235 	/*
236 	 * Initialize 'prev' and 'next' to dot.
237 	 */
238 	db_prev = db_dot;
239 	db_next = db_dot;
240 
241 	db_cmd_loop_done = 0;
242 
243 	savejmp = db_recover;
244 	db_recover = &db_jmpbuf;
245 	(void) setjmp(&db_jmpbuf);
246 
247 	while (!db_cmd_loop_done) {
248 		if (db_print_position() != 0)
249 			db_printf("\n");
250 		db_output_line = 0;
251 
252 
253 #ifdef MULTIPROCESSOR
254 		db_printf("db{%ld}> ", (long)cpu_number());
255 #else
256 		db_printf("db> ");
257 #endif
258 		(void) db_read_line();
259 
260 		db_command(&db_last_command, db_command_table);
261 	}
262 
263 	db_recover = savejmp;
264 }
265 
266 /*
267  * Search for command prefix.
268  */
269 static int
270 db_cmd_search(const char *name, const struct db_command *table,
271     const struct db_command **cmdp)
272 {
273 	const struct db_command	*cmd;
274 	int			result = CMD_NONE;
275 
276 	for (cmd = table; cmd->name != 0; cmd++) {
277 		const char *lp;
278 		const char *rp;
279 		int  c;
280 
281 		lp = name;
282 		rp = cmd->name;
283 		while ((c = *lp) == *rp) {
284 			if (c == 0) {
285 				/* complete match */
286 				*cmdp = cmd;
287 				return (CMD_UNIQUE);
288 			}
289 			lp++;
290 			rp++;
291 		}
292 		if (c == 0) {
293 			/* end of name, not end of command -
294 			   partial match */
295 			if (result == CMD_FOUND) {
296 				result = CMD_AMBIGUOUS;
297 				/* but keep looking for a full match -
298 				   this lets us match single letters */
299 			} else {
300 				*cmdp = cmd;
301 				result = CMD_FOUND;
302 			}
303 		}
304 	}
305 	if (result == CMD_NONE) {
306 		/* check for 'help' */
307 		if (name[0] == 'h' && name[1] == 'e'
308 		    && name[2] == 'l' && name[3] == 'p')
309 			result = CMD_HELP;
310 	}
311 	return (result);
312 }
313 
314 static void
315 db_cmd_list(const struct db_command *table)
316 {
317 	int	 i, j, w, columns, lines, width=0, numcmds;
318 	const char	*p;
319 
320 	for (numcmds = 0; table[numcmds].name != NULL; numcmds++) {
321 		w = strlen(table[numcmds].name);
322 		if (w > width)
323 			width = w;
324 	}
325 	width = DB_NEXT_TAB(width);
326 
327 	columns = db_max_width / width;
328 	if (columns == 0)
329 		columns = 1;
330 	lines = (numcmds + columns - 1) / columns;
331 	for (i = 0; i < lines; i++) {
332 		for (j = 0; j < columns; j++) {
333 			p = table[j * lines + i].name;
334 			if (p)
335 				db_printf("%s", p);
336 			if (j * lines + i + lines >= numcmds) {
337 				db_putchar('\n');
338 				break;
339 			}
340 			w = strlen(p);
341 			while (w < width) {
342 				w = DB_NEXT_TAB(w);
343 				db_putchar('\t');
344 			}
345 		}
346 	}
347 }
348 
349 static void
350 db_command(const struct db_command **last_cmdp,
351     const struct db_command *cmd_table)
352 {
353 	const struct db_command	*cmd;
354 	int		t;
355 	char		modif[TOK_STRING_SIZE];
356 	db_expr_t	addr, count;
357 	boolean_t	have_addr = FALSE;
358 	int		result;
359 
360 	static db_expr_t last_count = 0;
361 
362 	t = db_read_token();
363 	if ((t == tEOL) || (t == tCOMMA)) {
364 		/*
365 		 * An empty line repeats last command, at 'next'.
366 		 * Only a count repeats the last command with the new count.
367 		 */
368 		cmd = *last_cmdp;
369 		addr = (db_expr_t)db_next;
370 		if (t == tCOMMA) {
371 			if (!db_expression(&count)) {
372 				db_printf("Count missing\n");
373 				db_flush_lex();
374 				return;
375 			}
376 		} else
377 			count = last_count;
378 		have_addr = FALSE;
379 		modif[0] = '\0';
380 		db_skip_to_eol();
381 	} else if (t == tEXCL) {
382 		db_fncall(0, 0, 0, NULL);
383 		return;
384 	} else if (t != tIDENT) {
385 		db_printf("?\n");
386 		db_flush_lex();
387 		return;
388 	} else {
389 		/*
390 		 * Search for command
391 		 */
392 		while (cmd_table) {
393 			result = db_cmd_search(db_tok_string, cmd_table, &cmd);
394 			switch (result) {
395 			case CMD_NONE:
396 				db_printf("No such command\n");
397 				db_flush_lex();
398 				return;
399 			case CMD_AMBIGUOUS:
400 				db_printf("Ambiguous\n");
401 				db_flush_lex();
402 				return;
403 			case CMD_HELP:
404 				db_cmd_list(cmd_table);
405 				db_flush_lex();
406 				return;
407 			default:
408 				break;
409 			}
410 			if ((cmd_table = cmd->more) != 0) {
411 				t = db_read_token();
412 				if (t != tIDENT) {
413 					db_cmd_list(cmd_table);
414 					db_flush_lex();
415 					return;
416 				}
417 			}
418 		}
419 
420 		if ((cmd->flag & CS_OWN) == 0) {
421 			/*
422 			 * Standard syntax:
423 			 * command [/modifier] [addr] [,count]
424 			 */
425 			t = db_read_token();
426 			if (t == tSLASH) {
427 				t = db_read_token();
428 				if (t != tIDENT) {
429 					db_printf("Bad modifier\n");
430 					db_flush_lex();
431 					return;
432 				}
433 				strlcpy(modif, db_tok_string, sizeof(modif));
434 			} else {
435 				db_unread_token(t);
436 				modif[0] = '\0';
437 			}
438 
439 			if (db_expression(&addr)) {
440 				db_dot = (db_addr_t) addr;
441 				db_last_addr = db_dot;
442 				have_addr = TRUE;
443 			} else {
444 				addr = (db_expr_t) db_dot;
445 				have_addr = FALSE;
446 			}
447 			t = db_read_token();
448 			if (t == tCOMMA) {
449 				if (!db_expression(&count)) {
450 					db_printf("Count missing\n");
451 					db_flush_lex();
452 					return;
453 				}
454 			} else {
455 				db_unread_token(t);
456 				count = -1;
457 			}
458 			if ((cmd->flag & CS_MORE) == 0) {
459 				db_skip_to_eol();
460 			}
461 		}
462 	}
463 	*last_cmdp = cmd;
464 	last_count = count;
465 	if (cmd != 0) {
466 		/*
467 		 * Execute the command.
468 		 */
469 		(*cmd->fcn)(addr, have_addr, count, modif);
470 
471 		if (cmd->flag & CS_SET_DOT) {
472 			/*
473 			 * If command changes dot, set dot to
474 			 * previous address displayed (if 'ed' style).
475 			 */
476 			if (db_ed_style)
477 				db_dot = db_prev;
478 			else
479 				db_dot = db_next;
480 		} else {
481 			/*
482 			 * If command does not change dot,
483 			 * set 'next' location to be the same.
484 			 */
485 			db_next = db_dot;
486 		}
487 	}
488 }
489 
490 /*ARGSUSED*/
491 static void
492 db_map_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
493 {
494 	boolean_t full = FALSE;
495 
496 	if (modif[0] == 'f')
497 		full = TRUE;
498 
499 	if (have_addr == FALSE)
500 		addr = (db_expr_t)(intptr_t) kernel_map;
501 
502 	uvm_map_printit((struct vm_map *)(intptr_t) addr, full, db_printf);
503 }
504 
505 /*ARGSUSED*/
506 static void
507 db_malloc_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
508 {
509 
510 #ifdef MALLOC_DEBUG
511 	if (!have_addr)
512 		addr = 0;
513 
514 	debug_malloc_printit(db_printf, (vaddr_t) addr);
515 #else
516 	db_printf("The kernel is not built with the MALLOC_DEBUG option.\n");
517 #endif /* MALLOC_DEBUG */
518 }
519 
520 /*ARGSUSED*/
521 static void
522 db_object_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
523 {
524 	boolean_t full = FALSE;
525 
526 	if (modif[0] == 'f')
527 		full = TRUE;
528 
529 	uvm_object_printit((struct uvm_object *)(intptr_t) addr, full,
530 	    db_printf);
531 }
532 
533 /*ARGSUSED*/
534 static void
535 db_page_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
536 {
537 	boolean_t full = FALSE;
538 
539 	if (modif[0] == 'f')
540 		full = TRUE;
541 
542 	uvm_page_printit((struct vm_page *)(intptr_t) addr, full, db_printf);
543 }
544 
545 /*ARGSUSED*/
546 static void
547 db_buf_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
548 {
549 	boolean_t full = FALSE;
550 
551 	if (modif[0] == 'f')
552 		full = TRUE;
553 
554 	vfs_buf_print((struct buf *)(intptr_t) addr, full, db_printf);
555 }
556 
557 /*ARGSUSED*/
558 static void
559 db_event_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
560 {
561 	boolean_t full = FALSE;
562 
563 	if (modif[0] == 'f')
564 		full = TRUE;
565 
566 	event_print(full, db_printf);
567 }
568 
569 /*ARGSUSED*/
570 static void
571 db_vnode_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
572 {
573 	boolean_t full = FALSE;
574 
575 	if (modif[0] == 'f')
576 		full = TRUE;
577 
578 	vfs_vnode_print((struct vnode *)(intptr_t) addr, full, db_printf);
579 }
580 
581 static void
582 db_mount_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
583 {
584 	boolean_t full = FALSE;
585 
586 	if (modif[0] == 'f')
587 		full = TRUE;
588 
589 	vfs_mount_print((struct mount *)(intptr_t) addr, full, db_printf);
590 }
591 
592 /*ARGSUSED*/
593 static void
594 db_pool_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
595 {
596 
597 	pool_printit((struct pool *)(intptr_t) addr, modif, db_printf);
598 }
599 
600 /*ARGSUSED*/
601 static void
602 db_namecache_print_cmd(db_expr_t addr, int have_addr, db_expr_t count,
603     const char *modif)
604 {
605 
606 	namecache_print((struct vnode *)(intptr_t) addr, db_printf);
607 }
608 
609 /*ARGSUSED*/
610 static void
611 db_uvmexp_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
612 {
613 
614 	uvmexp_print(db_printf);
615 }
616 
617 /*
618  * Call random function:
619  * !expr(arg,arg,arg)
620  */
621 /*ARGSUSED*/
622 static void
623 db_fncall(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
624 {
625 	db_expr_t	fn_addr;
626 #define	MAXARGS		11
627 	db_expr_t	args[MAXARGS];
628 	int		nargs = 0;
629 	db_expr_t	retval;
630 	db_expr_t	(*func)(db_expr_t, ...);
631 	int		t;
632 
633 	if (!db_expression(&fn_addr)) {
634 		db_printf("Bad function\n");
635 		db_flush_lex();
636 		return;
637 	}
638 	func = (db_expr_t (*)(db_expr_t, ...))(intptr_t) fn_addr;
639 
640 	t = db_read_token();
641 	if (t == tLPAREN) {
642 		if (db_expression(&args[0])) {
643 			nargs++;
644 			while ((t = db_read_token()) == tCOMMA) {
645 				if (nargs == MAXARGS) {
646 					db_printf("Too many arguments\n");
647 					db_flush_lex();
648 					return;
649 				}
650 				if (!db_expression(&args[nargs])) {
651 					db_printf("Argument missing\n");
652 					db_flush_lex();
653 					return;
654 				}
655 				nargs++;
656 			}
657 			db_unread_token(t);
658 		}
659 		if (db_read_token() != tRPAREN) {
660 			db_printf("?\n");
661 			db_flush_lex();
662 			return;
663 		}
664 	}
665 	db_skip_to_eol();
666 
667 	while (nargs < MAXARGS) {
668 		args[nargs++] = 0;
669 	}
670 
671 	retval = (*func)(args[0], args[1], args[2], args[3], args[4],
672 			 args[5], args[6], args[7], args[8], args[9]);
673 	db_printf("%s\n", db_num_to_str(retval));
674 }
675 
676 static void
677 db_reboot_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
678 {
679 	db_expr_t bootflags;
680 
681 	/* Flags, default to RB_AUTOBOOT */
682 	if (!db_expression(&bootflags))
683 		bootflags = (db_expr_t)RB_AUTOBOOT;
684 	if (db_read_token() != tEOL) {
685 		db_error("?\n");
686 		/*NOTREACHED*/
687 	}
688 	/*
689 	 * We are leaving DDB, never to return upward.
690 	 * Clear db_recover so that we can debug faults in functions
691 	 * called from cpu_reboot.
692 	 */
693 	db_recover = 0;
694 	cpu_reboot((int)bootflags, NULL);
695 }
696 
697 static void
698 db_sifting_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
699 {
700 	int	mode, t;
701 
702 	t = db_read_token();
703 	if (t == tSLASH) {
704 		t = db_read_token();
705 		if (t != tIDENT) {
706 			bad_modifier:
707 			db_printf("Bad modifier\n");
708 			db_flush_lex();
709 			return;
710 		}
711 		if (!strcmp(db_tok_string, "F"))
712 			mode = 'F';
713 		else
714 			goto bad_modifier;
715 		t = db_read_token();
716 	} else
717 		mode = 0;
718 
719 	if (t == tIDENT)
720 		db_sifting(db_tok_string, mode);
721 	else {
722 		db_printf("Bad argument (non-string)\n");
723 		db_flush_lex();
724 	}
725 }
726 
727 static void
728 db_stack_trace_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
729 {
730 	register const char *cp = modif;
731 	register char c;
732 	void (*pr)(const char *, ...);
733 
734 	pr = db_printf;
735 	while ((c = *cp++) != 0)
736 		if (c == 'l')
737 			pr = printf;
738 
739 	if (count == -1)
740 		count = 65535;
741 
742 	db_stack_trace_print(addr, have_addr, count, modif, pr);
743 }
744 
745 static void
746 db_sync_cmd(db_expr_t addr, int have_addr, db_expr_t count, const char *modif)
747 {
748 
749 	/*
750 	 * We are leaving DDB, never to return upward.
751 	 * Clear db_recover so that we can debug faults in functions
752 	 * called from cpu_reboot.
753 	 */
754 	db_recover = 0;
755 	cpu_reboot(RB_DUMP, NULL);
756 }
757