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