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