db_command.c revision 12473
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 *	$Id: db_command.c,v 1.14 1995/08/27 02:39:39 bde Exp $
27 */
28
29/*
30 *	Author: David B. Golub, Carnegie Mellon University
31 *	Date:	7/90
32 */
33
34/*
35 * Command dispatcher.
36 */
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/proc.h>
40#include <ddb/ddb.h>
41
42#include <ddb/db_command.h>
43#include <ddb/db_lex.h>
44#include <ddb/db_output.h>
45
46#include <setjmp.h>
47
48/*
49 * Exported global variables
50 */
51boolean_t	db_cmd_loop_done;
52jmp_buf		db_jmpbuf;
53db_addr_t	db_dot;
54db_addr_t	db_last_addr;
55db_addr_t	db_prev;
56db_addr_t	db_next;
57
58/*
59 * if 'ed' style: 'dot' is set at start of last item printed,
60 * and '+' points to next line.
61 * Otherwise: 'dot' points to next item, '..' points to last.
62 */
63boolean_t	db_ed_style = TRUE;
64
65/*
66 * Utility routine - discard tokens through end-of-line.
67 */
68void
69db_skip_to_eol()
70{
71	int	t;
72	do {
73	    t = db_read_token();
74	} while (t != tEOL);
75}
76
77/*
78 * Command table
79 */
80struct command {
81	char *	name;		/* command name */
82	db_cmdfcn_t *fcn;	/* function to call */
83	int	flag;		/* extra info: */
84#define	CS_OWN		0x1	    /* non-standard syntax */
85#define	CS_MORE		0x2	    /* standard syntax, but may have other
86				       words at end */
87#define	CS_SET_DOT	0x100	    /* set dot after command */
88	struct command *more;	/* another level of command */
89};
90
91/*
92 * Results of command search.
93 */
94#define	CMD_UNIQUE	0
95#define	CMD_FOUND	1
96#define	CMD_NONE	2
97#define	CMD_AMBIGUOUS	3
98#define	CMD_HELP	4
99
100extern void	db_cmd_list __P((struct command *table));
101extern int	db_cmd_search __P((char *name, struct command *table,
102				   struct command **cmdp));
103extern void	db_command __P((struct command **last_cmdp,
104				struct command *cmd_table));
105
106/*
107 * Search for command prefix.
108 */
109int
110db_cmd_search(name, table, cmdp)
111	char *		name;
112	struct command	*table;
113	struct command	**cmdp;	/* out */
114{
115	struct command	*cmd;
116	int		result = CMD_NONE;
117
118	for (cmd = table; cmd->name != 0; cmd++) {
119	    register char *lp;
120	    register char *rp;
121	    register int  c;
122
123	    lp = name;
124	    rp = cmd->name;
125	    while ((c = *lp) == *rp) {
126		if (c == 0) {
127		    /* complete match */
128		    *cmdp = cmd;
129		    return (CMD_UNIQUE);
130		}
131		lp++;
132		rp++;
133	    }
134	    if (c == 0) {
135		/* end of name, not end of command -
136		   partial match */
137		if (result == CMD_FOUND) {
138		    result = CMD_AMBIGUOUS;
139		    /* but keep looking for a full match -
140		       this lets us match single letters */
141		}
142		else {
143		    *cmdp = cmd;
144		    result = CMD_FOUND;
145		}
146	    }
147	}
148	if (result == CMD_NONE) {
149	    /* check for 'help' */
150		if (name[0] == 'h' && name[1] == 'e'
151		    && name[2] == 'l' && name[3] == 'p')
152			result = CMD_HELP;
153	}
154	return (result);
155}
156
157void
158db_cmd_list(table)
159	struct command *table;
160{
161	register struct command *cmd;
162
163	for (cmd = table; cmd->name != 0; cmd++) {
164	    db_printf("%-12s", cmd->name);
165	    db_end_line();
166	}
167}
168
169void
170db_command(last_cmdp, cmd_table)
171	struct command	**last_cmdp;	/* IN_OUT */
172	struct command	*cmd_table;
173{
174	struct command	*cmd;
175	int		t;
176	char		modif[TOK_STRING_SIZE];
177	db_expr_t	addr, count;
178	boolean_t	have_addr = FALSE;
179	int		result;
180
181	t = db_read_token();
182	if (t == tEOL) {
183	    /* empty line repeats last command, at 'next' */
184	    cmd = *last_cmdp;
185	    addr = (db_expr_t)db_next;
186	    have_addr = FALSE;
187	    count = 1;
188	    modif[0] = '\0';
189	}
190	else if (t == tEXCL) {
191	    db_fncall((db_expr_t)0, (boolean_t)0, (db_expr_t)0, (char *)0);
192	    return;
193	}
194	else if (t != tIDENT) {
195	    db_printf("?\n");
196	    db_flush_lex();
197	    return;
198	}
199	else {
200	    /*
201	     * Search for command
202	     */
203	    while (cmd_table) {
204		result = db_cmd_search(db_tok_string,
205				       cmd_table,
206				       &cmd);
207		switch (result) {
208		    case CMD_NONE:
209			db_printf("No such command\n");
210			db_flush_lex();
211			return;
212		    case CMD_AMBIGUOUS:
213			db_printf("Ambiguous\n");
214			db_flush_lex();
215			return;
216		    case CMD_HELP:
217			db_cmd_list(cmd_table);
218			db_flush_lex();
219			return;
220		    default:
221			break;
222		}
223		if ((cmd_table = cmd->more) != 0) {
224		    t = db_read_token();
225		    if (t != tIDENT) {
226			db_cmd_list(cmd_table);
227			db_flush_lex();
228			return;
229		    }
230		}
231	    }
232
233	    if ((cmd->flag & CS_OWN) == 0) {
234		/*
235		 * Standard syntax:
236		 * command [/modifier] [addr] [,count]
237		 */
238		t = db_read_token();
239		if (t == tSLASH) {
240		    t = db_read_token();
241		    if (t != tIDENT) {
242			db_printf("Bad modifier\n");
243			db_flush_lex();
244			return;
245		    }
246		    db_strcpy(modif, db_tok_string);
247		}
248		else {
249		    db_unread_token(t);
250		    modif[0] = '\0';
251		}
252
253		if (db_expression(&addr)) {
254		    db_dot = (db_addr_t) addr;
255		    db_last_addr = db_dot;
256		    have_addr = TRUE;
257		}
258		else {
259		    addr = (db_expr_t) db_dot;
260		    have_addr = FALSE;
261		}
262		t = db_read_token();
263		if (t == tCOMMA) {
264		    if (!db_expression(&count)) {
265			db_printf("Count missing\n");
266			db_flush_lex();
267			return;
268		    }
269		}
270		else {
271		    db_unread_token(t);
272		    count = -1;
273		}
274		if ((cmd->flag & CS_MORE) == 0) {
275		    db_skip_to_eol();
276		}
277	    }
278	}
279	*last_cmdp = cmd;
280	if (cmd != 0) {
281	    /*
282	     * Execute the command.
283	     */
284	    (*cmd->fcn)(addr, have_addr, count, modif);
285
286	    if (cmd->flag & CS_SET_DOT) {
287		/*
288		 * If command changes dot, set dot to
289		 * previous address displayed (if 'ed' style).
290		 */
291		if (db_ed_style) {
292		    db_dot = db_prev;
293		}
294		else {
295		    db_dot = db_next;
296		}
297	    }
298	    else {
299		/*
300		 * If command does not change dot,
301		 * set 'next' location to be the same.
302		 */
303		db_next = db_dot;
304	    }
305	}
306}
307
308/*
309 * 'show' commands
310 */
311
312struct command db_show_all_cmds[] = {
313#if 0
314	{ "threads",	db_show_all_threads,	0,	0 },
315#endif
316	{ "procs",	db_ps,			0,	0 },
317	{ (char *)0 }
318};
319
320struct command db_show_cmds[] = {
321	{ "all",	0,			0,	db_show_all_cmds },
322	{ "registers",	db_show_regs,		0,	0 },
323	{ "breaks",	db_listbreak_cmd, 	0,	0 },
324	{ "watches",	db_listwatch_cmd, 	0,	0 },
325#if 0
326	{ "thread",	db_show_one_thread,	0,	0 },
327#endif
328	{ "map",	vm_map_print,		0,	0 },
329	{ "object",	vm_object_print,	0,	0 },
330#if 0
331	{ "page",	vm_page_print,		0,	0 },
332#endif
333#if 0
334	{ "port",	ipc_port_print,		0,	0 },
335#endif
336	{ (char *)0, }
337};
338
339struct command db_command_table[] = {
340	{ "print",	db_print_cmd,		0,	0 },
341	{ "p",		db_print_cmd,		0,	0 },
342	{ "examine",	db_examine_cmd,		CS_SET_DOT, 0 },
343	{ "x",		db_examine_cmd,		CS_SET_DOT, 0 },
344	{ "search",	db_search_cmd,		CS_OWN|CS_SET_DOT, 0 },
345	{ "set",	db_set_cmd,		CS_OWN,	0 },
346	{ "write",	db_write_cmd,		CS_MORE|CS_SET_DOT, 0 },
347	{ "w",		db_write_cmd,		CS_MORE|CS_SET_DOT, 0 },
348	{ "delete",	db_delete_cmd,		0,	0 },
349	{ "d",		db_delete_cmd,		0,	0 },
350	{ "break",	db_breakpoint_cmd,	0,	0 },
351	{ "dwatch",	db_deletewatch_cmd,	0,	0 },
352	{ "watch",	db_watchpoint_cmd,	CS_MORE,0 },
353	{ "step",	db_single_step_cmd,	0,	0 },
354	{ "s",		db_single_step_cmd,	0,	0 },
355	{ "continue",	db_continue_cmd,	0,	0 },
356	{ "c",		db_continue_cmd,	0,	0 },
357	{ "until",	db_trace_until_call_cmd,0,	0 },
358	{ "next",	db_trace_until_matching_cmd,0,	0 },
359	{ "match",	db_trace_until_matching_cmd,0,	0 },
360	{ "trace",	db_stack_trace_cmd,	0,	0 },
361	{ "call",	db_fncall,		CS_OWN,	0 },
362	{ "show",	0,			0,	db_show_cmds },
363	{ "ps",		db_ps,			0,	0 },
364	{ "panic",	db_panic,		0,	0 },
365	{ (char *)0, }
366};
367
368struct command	*db_last_command = 0;
369
370#if 0
371void
372db_help_cmd()
373{
374	struct command *cmd = db_command_table;
375
376	while (cmd->name != 0) {
377	    db_printf("%-12s", cmd->name);
378	    db_end_line();
379	    cmd++;
380	}
381}
382#endif
383
384void
385db_panic(dummy1, dummy2, dummy3, dummy4)
386	db_expr_t	dummy1;
387	boolean_t	dummy2;
388	db_expr_t	dummy3;
389	char *		dummy4;
390{
391	panic("from debugger");
392}
393
394void
395db_command_loop()
396{
397	/*
398	 * Initialize 'prev' and 'next' to dot.
399	 */
400	db_prev = db_dot;
401	db_next = db_dot;
402
403	db_cmd_loop_done = 0;
404	while (!db_cmd_loop_done) {
405
406	    (void) setjmp(db_jmpbuf);
407	    if (db_print_position() != 0)
408		db_printf("\n");
409
410	    db_printf("db> ");
411	    (void) db_read_line();
412
413	    db_command(&db_last_command, db_command_table);
414	}
415}
416
417void
418db_error(s)
419	char *s;
420{
421	if (s)
422	    db_printf(s);
423	db_flush_lex();
424	longjmp(db_jmpbuf, 1);
425}
426
427
428/*
429 * Call random function:
430 * !expr(arg,arg,arg)
431 */
432void
433db_fncall(dummy1, dummy2, dummy3, dummy4)
434	db_expr_t	dummy1;
435	boolean_t	dummy2;
436	db_expr_t	dummy3;
437	char *		dummy4;
438{
439	db_expr_t	fn_addr;
440#define	MAXARGS		11	/* XXX only 10 are passed */
441	db_expr_t	args[MAXARGS];
442	int		nargs = 0;
443	db_expr_t	retval;
444	typedef db_expr_t fcn_10args_t __P((db_expr_t, db_expr_t, db_expr_t,
445					    db_expr_t, db_expr_t, db_expr_t,
446					    db_expr_t, db_expr_t, db_expr_t,
447					    db_expr_t));
448	fcn_10args_t	*func;
449	int		t;
450
451	if (!db_expression(&fn_addr)) {
452	    db_printf("Bad function\n");
453	    db_flush_lex();
454	    return;
455	}
456	func = (fcn_10args_t *)fn_addr;	/* XXX */
457
458	t = db_read_token();
459	if (t == tLPAREN) {
460	    if (db_expression(&args[0])) {
461		nargs++;
462		while ((t = db_read_token()) == tCOMMA) {
463		    if (nargs == MAXARGS) {
464			db_printf("Too many arguments\n");
465			db_flush_lex();
466			return;
467		    }
468		    if (!db_expression(&args[nargs])) {
469			db_printf("Argument missing\n");
470			db_flush_lex();
471			return;
472		    }
473		    nargs++;
474		}
475		db_unread_token(t);
476	    }
477	    if (db_read_token() != tRPAREN) {
478		db_printf("?\n");
479		db_flush_lex();
480		return;
481	    }
482	}
483	db_skip_to_eol();
484
485	while (nargs < MAXARGS) {
486	    args[nargs++] = 0;
487	}
488
489	retval = (*func)(args[0], args[1], args[2], args[3], args[4],
490			 args[5], args[6], args[7], args[8], args[9] );
491	db_printf("%#n\n", retval);
492}
493