db_trace.c revision 43314
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_trace.c,v 1.32 1999/01/27 23:45:38 dillon Exp $
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31
32#include <machine/cpu.h>
33
34#include <vm/vm.h>
35#include <vm/vm_param.h>
36#include <vm/pmap.h>
37#include <ddb/ddb.h>
38
39#include <ddb/db_access.h>
40#include <ddb/db_sym.h>
41#include <ddb/db_variables.h>
42
43/*
44 * Machine register set.
45 */
46struct db_variable db_regs[] = {
47	{ "cs",		&ddb_regs.tf_cs,  FCN_NULL },
48	{ "ds",		&ddb_regs.tf_ds,  FCN_NULL },
49	{ "es",		&ddb_regs.tf_es,  FCN_NULL },
50#if 0
51	{ "fs",		&ddb_regs.tf_fs,  FCN_NULL },
52	{ "gs",		&ddb_regs.tf_gs,  FCN_NULL },
53#endif
54	{ "ss",		&ddb_regs.tf_ss,  FCN_NULL },
55	{ "eax",	&ddb_regs.tf_eax, FCN_NULL },
56	{ "ecx",	&ddb_regs.tf_ecx, FCN_NULL },
57	{ "edx",	&ddb_regs.tf_edx, FCN_NULL },
58	{ "ebx",	&ddb_regs.tf_ebx, FCN_NULL },
59	{ "esp",	&ddb_regs.tf_esp, FCN_NULL },
60	{ "ebp",	&ddb_regs.tf_ebp, FCN_NULL },
61	{ "esi",	&ddb_regs.tf_esi, FCN_NULL },
62	{ "edi",	&ddb_regs.tf_edi, FCN_NULL },
63	{ "eip",	&ddb_regs.tf_eip, FCN_NULL },
64	{ "efl",	&ddb_regs.tf_eflags, FCN_NULL },
65};
66struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
67
68/*
69 * Stack trace.
70 */
71#define	INKERNEL(va)	(((vm_offset_t)(va)) >= USRSTACK)
72
73struct i386_frame {
74	struct i386_frame	*f_frame;
75	int			f_retaddr;
76	int			f_arg0;
77};
78
79#define NORMAL		0
80#define	TRAP		1
81#define	INTERRUPT	2
82#define	SYSCALL		3
83
84static void db_nextframe __P((struct i386_frame **, db_addr_t *));
85static int db_numargs __P((struct i386_frame *));
86static void db_print_stack_entry __P((const char *, int, char **, int *, db_addr_t));
87
88/*
89 * Figure out how many arguments were passed into the frame at "fp".
90 */
91static int
92db_numargs(fp)
93	struct i386_frame *fp;
94{
95	int	*argp;
96	int	inst;
97	int	args;
98
99	argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE);
100	/*
101	 * XXX etext is wrong for LKMs.  We should attempt to interpret
102	 * the instruction at the return address in all cases.  This
103	 * may require better fault handling.
104	 */
105	if (argp < (int *)btext || argp >= (int *)etext) {
106		args = 5;
107	} else {
108		inst = db_get_value((int)argp, 4, FALSE);
109		if ((inst & 0xff) == 0x59)	/* popl %ecx */
110			args = 1;
111		else if ((inst & 0xffff) == 0xc483)	/* addl $Ibs, %esp */
112			args = ((inst >> 16) & 0xff) / 4;
113		else
114			args = 5;
115	}
116	return (args);
117}
118
119static void
120db_print_stack_entry(name, narg, argnp, argp, callpc)
121	const char *name;
122	int narg;
123	char **argnp;
124	int *argp;
125	db_addr_t callpc;
126{
127	db_printf("%s(", name);
128	while (narg) {
129		if (argnp)
130			db_printf("%s=", *argnp++);
131		db_printf("%r", db_get_value((int)argp, 4, FALSE));
132		argp++;
133		if (--narg != 0)
134			db_printf(",");
135  	}
136	db_printf(") at ");
137	db_printsym(callpc, DB_STGY_PROC);
138	db_printf("\n");
139}
140
141/*
142 * Figure out the next frame up in the call stack.
143 */
144static void
145db_nextframe(fp, ip)
146	struct i386_frame **fp;		/* in/out */
147	db_addr_t	*ip;		/* out */
148{
149	struct trapframe *tf;
150	int frame_type;
151	int eip, esp, ebp;
152	db_expr_t offset;
153	const char *sym, *name;
154
155	eip = db_get_value((int) &(*fp)->f_retaddr, 4, FALSE);
156	ebp = db_get_value((int) &(*fp)->f_frame, 4, FALSE);
157
158	/*
159	 * Figure out frame type.
160	 */
161
162	frame_type = NORMAL;
163
164	sym = db_search_symbol(eip, DB_STGY_ANY, &offset);
165	db_symbol_values(sym, &name, NULL);
166	if (name != NULL) {
167		if (!strcmp(name, "calltrap")) {
168			frame_type = TRAP;
169		} else if (!strncmp(name, "Xresume", 7)) {
170			frame_type = INTERRUPT;
171		} else if (!strcmp(name, "_Xsyscall")) {
172			frame_type = SYSCALL;
173		}
174	}
175
176	/*
177	 * Normal frames need no special processing.
178	 */
179	if (frame_type == NORMAL) {
180		*ip = (db_addr_t) eip;
181		*fp = (struct i386_frame *) ebp;
182		return;
183	}
184
185	db_print_stack_entry(name, 0, 0, 0, eip);
186
187	/*
188	 * Point to base of trapframe which is just above the
189	 * current frame.
190	 */
191	tf = (struct trapframe *) ((int)*fp + 8);
192
193	esp = (ISPL(tf->tf_cs) == SEL_UPL) ?  tf->tf_esp : (int)&tf->tf_esp;
194	switch (frame_type) {
195	case TRAP:
196		if (INKERNEL((int) tf)) {
197			eip = tf->tf_eip;
198			ebp = tf->tf_ebp;
199			db_printf(
200		    "--- trap %#r, eip = %#r, esp = %#r, ebp = %#r ---\n",
201			    tf->tf_trapno, eip, esp, ebp);
202		}
203		break;
204	case SYSCALL:
205		if (INKERNEL((int) tf)) {
206			eip = tf->tf_eip;
207			ebp = tf->tf_ebp;
208			db_printf(
209		    "--- syscall %#r, eip = %#r, esp = %#r, ebp = %#r ---\n",
210			    tf->tf_eax, eip, esp, ebp);
211		}
212		break;
213	case INTERRUPT:
214		tf = (struct trapframe *)((int)*fp + 16);
215		if (INKERNEL((int) tf)) {
216			eip = tf->tf_eip;
217			ebp = tf->tf_ebp;
218			db_printf(
219		    "--- interrupt, eip = %#r, esp = %#r, ebp = %#r ---\n",
220			    eip, esp, ebp);
221		}
222		break;
223	default:
224		break;
225	}
226
227	*ip = (db_addr_t) eip;
228	*fp = (struct i386_frame *) ebp;
229}
230
231void
232db_stack_trace_cmd(addr, have_addr, count, modif)
233	db_expr_t addr;
234	boolean_t have_addr;
235	db_expr_t count;
236	char *modif;
237{
238	struct i386_frame *frame;
239	int *argp;
240	db_addr_t callpc;
241	boolean_t first;
242
243	if (count == -1)
244		count = 65535;
245
246	if (!have_addr) {
247		frame = (struct i386_frame *)ddb_regs.tf_ebp;
248		if (frame == NULL)
249			frame = (struct i386_frame *)(ddb_regs.tf_esp - 4);
250		callpc = (db_addr_t)ddb_regs.tf_eip;
251	} else {
252		frame = (struct i386_frame *)addr;
253		callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE);
254	}
255
256	first = TRUE;
257	while (count--) {
258		struct i386_frame *actframe;
259		int		narg;
260		const char *	name;
261		db_expr_t	offset;
262		c_db_sym_t	sym;
263#define MAXNARG	16
264		char	*argnames[MAXNARG], **argnp = NULL;
265
266		sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
267		db_symbol_values(sym, &name, NULL);
268
269		/*
270		 * Attempt to determine a (possibly fake) frame that gives
271		 * the caller's pc.  It may differ from `frame' if the
272		 * current function never sets up a standard frame or hasn't
273		 * set one up yet or has just discarded one.  The last two
274		 * cases can be guessed fairly reliably for code generated
275		 * by gcc.  The first case is too much trouble to handle in
276		 * general because the amount of junk on the stack depends
277		 * on the pc (the special handling of "calltrap", etc. in
278		 * db_nextframe() works because the `next' pc is special).
279		 */
280		actframe = frame;
281		if (first && !have_addr) {
282			int instr;
283
284			instr = db_get_value(callpc, 4, FALSE);
285			if ((instr & 0x00ffffff) == 0x00e58955) {
286				/* pushl %ebp; movl %esp, %ebp */
287				actframe = (struct i386_frame *)
288					   (ddb_regs.tf_esp - 4);
289			} else if ((instr & 0x0000ffff) == 0x0000e589) {
290				/* movl %esp, %ebp */
291				actframe = (struct i386_frame *)
292					   ddb_regs.tf_esp;
293				if (ddb_regs.tf_ebp == 0) {
294					/* Fake the caller's frame better. */
295					frame = actframe;
296				}
297			} else if ((instr & 0x000000ff) == 0x000000c3) {
298				/* ret */
299				actframe = (struct i386_frame *)
300					   (ddb_regs.tf_esp - 4);
301			} else if (offset == 0) {
302				/* Probably a symbol in assembler code. */
303				actframe = (struct i386_frame *)
304					   (ddb_regs.tf_esp - 4);
305			}
306		}
307		first = FALSE;
308
309		argp = &actframe->f_arg0;
310		narg = MAXNARG;
311		if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) {
312			argnp = argnames;
313		} else {
314			narg = db_numargs(frame);
315		}
316
317		db_print_stack_entry(name, narg, argnp, argp, callpc);
318
319		if (actframe != frame) {
320			/* `frame' belongs to caller. */
321			callpc = (db_addr_t)
322			    db_get_value((int)&actframe->f_retaddr, 4, FALSE);
323			continue;
324		}
325
326		db_nextframe(&frame, &callpc);
327
328		if (INKERNEL((int) callpc) && !INKERNEL((int) frame)) {
329			sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
330			db_symbol_values(sym, &name, NULL);
331			db_print_stack_entry(name, 0, 0, 0, callpc);
332			break;
333		}
334		if (!INKERNEL((int) frame)) {
335			break;
336		}
337	}
338}
339