db_trace.c revision 78903
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 * $FreeBSD: head/sys/i386/i386/db_trace.c 78903 2001-06-28 02:08:13Z bsd $
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/linker_set.h>
32#include <sys/proc.h>
33#include <sys/sysent.h>
34#include <sys/user.h>
35
36#include <machine/cpu.h>
37
38#include <vm/vm.h>
39#include <vm/vm_param.h>
40#include <vm/pmap.h>
41#include <ddb/ddb.h>
42
43#include <ddb/db_access.h>
44#include <ddb/db_sym.h>
45#include <ddb/db_variables.h>
46
47db_varfcn_t db_dr0;
48db_varfcn_t db_dr1;
49db_varfcn_t db_dr2;
50db_varfcn_t db_dr3;
51db_varfcn_t db_dr4;
52db_varfcn_t db_dr5;
53db_varfcn_t db_dr6;
54db_varfcn_t db_dr7;
55
56/*
57 * Machine register set.
58 */
59struct db_variable db_regs[] = {
60	{ "cs",		&ddb_regs.tf_cs,     FCN_NULL },
61	{ "ds",		&ddb_regs.tf_ds,     FCN_NULL },
62	{ "es",		&ddb_regs.tf_es,     FCN_NULL },
63	{ "fs",		&ddb_regs.tf_fs,     FCN_NULL },
64#if 0
65	{ "gs",		&ddb_regs.tf_gs,     FCN_NULL },
66#endif
67	{ "ss",		&ddb_regs.tf_ss,     FCN_NULL },
68	{ "eax",	&ddb_regs.tf_eax,    FCN_NULL },
69	{ "ecx",	&ddb_regs.tf_ecx,    FCN_NULL },
70	{ "edx",	&ddb_regs.tf_edx,    FCN_NULL },
71	{ "ebx",	&ddb_regs.tf_ebx,    FCN_NULL },
72	{ "esp",	&ddb_regs.tf_esp,    FCN_NULL },
73	{ "ebp",	&ddb_regs.tf_ebp,    FCN_NULL },
74	{ "esi",	&ddb_regs.tf_esi,    FCN_NULL },
75	{ "edi",	&ddb_regs.tf_edi,    FCN_NULL },
76	{ "eip",	&ddb_regs.tf_eip,    FCN_NULL },
77	{ "efl",	&ddb_regs.tf_eflags, FCN_NULL },
78	{ "dr0",	NULL,		     db_dr0 },
79	{ "dr1",	NULL,		     db_dr1 },
80	{ "dr2",	NULL,		     db_dr2 },
81	{ "dr3",	NULL,		     db_dr3 },
82	{ "dr4",	NULL,		     db_dr4 },
83	{ "dr5",	NULL,		     db_dr5 },
84	{ "dr6",	NULL,		     db_dr6 },
85	{ "dr7",	NULL,		     db_dr7 },
86};
87struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
88
89/*
90 * Stack trace.
91 */
92#define	INKERNEL(va)	(((vm_offset_t)(va)) >= USRSTACK)
93
94struct i386_frame {
95	struct i386_frame	*f_frame;
96	int			f_retaddr;
97	int			f_arg0;
98};
99
100#define NORMAL		0
101#define	TRAP		1
102#define	INTERRUPT	2
103#define	SYSCALL		3
104
105static void db_nextframe __P((struct i386_frame **, db_addr_t *, struct proc *));
106static int db_numargs __P((struct i386_frame *));
107static void db_print_stack_entry __P((const char *, int, char **, int *, db_addr_t));
108static void decode_syscall __P((int, struct proc *));
109
110/*
111 * Figure out how many arguments were passed into the frame at "fp".
112 */
113static int
114db_numargs(fp)
115	struct i386_frame *fp;
116{
117	int	*argp;
118	int	inst;
119	int	args;
120
121	argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE);
122	/*
123	 * XXX etext is wrong for LKMs.  We should attempt to interpret
124	 * the instruction at the return address in all cases.  This
125	 * may require better fault handling.
126	 */
127	if (argp < (int *)btext || argp >= (int *)etext) {
128		args = 5;
129	} else {
130		inst = db_get_value((int)argp, 4, FALSE);
131		if ((inst & 0xff) == 0x59)	/* popl %ecx */
132			args = 1;
133		else if ((inst & 0xffff) == 0xc483)	/* addl $Ibs, %esp */
134			args = ((inst >> 16) & 0xff) / 4;
135		else
136			args = 5;
137	}
138	return (args);
139}
140
141static void
142db_print_stack_entry(name, narg, argnp, argp, callpc)
143	const char *name;
144	int narg;
145	char **argnp;
146	int *argp;
147	db_addr_t callpc;
148{
149	db_printf("%s(", name);
150	while (narg) {
151		if (argnp)
152			db_printf("%s=", *argnp++);
153		db_printf("%r", db_get_value((int)argp, 4, FALSE));
154		argp++;
155		if (--narg != 0)
156			db_printf(",");
157  	}
158	db_printf(") at ");
159	db_printsym(callpc, DB_STGY_PROC);
160	db_printf("\n");
161}
162
163static void
164decode_syscall(number, p)
165	int number;
166	struct proc *p;
167{
168	c_db_sym_t sym;
169	db_expr_t diff;
170	sy_call_t *f;
171	const char *symname;
172
173	db_printf(" (%d", number);
174	if (p != NULL && 0 <= number && number < p->p_sysent->sv_size) {
175		f = p->p_sysent->sv_table[number].sy_call;
176		sym = db_search_symbol((db_addr_t)f, DB_STGY_ANY, &diff);
177		if (sym != DB_SYM_NULL && diff == 0) {
178			db_symbol_values(sym, &symname, NULL);
179			db_printf(", %s, %s", p->p_sysent->sv_name, symname);
180		}
181	}
182	db_printf(")");
183}
184
185/*
186 * Figure out the next frame up in the call stack.
187 */
188static void
189db_nextframe(fp, ip, p)
190	struct i386_frame **fp;		/* in/out */
191	db_addr_t	*ip;		/* out */
192	struct proc	*p;		/* in */
193{
194	struct trapframe *tf;
195	int frame_type;
196	int eip, esp, ebp;
197	db_expr_t offset;
198	const char *sym, *name;
199
200	eip = db_get_value((int) &(*fp)->f_retaddr, 4, FALSE);
201	ebp = db_get_value((int) &(*fp)->f_frame, 4, FALSE);
202
203	/*
204	 * Figure out frame type.
205	 */
206
207	frame_type = NORMAL;
208
209	sym = db_search_symbol(eip, DB_STGY_ANY, &offset);
210	db_symbol_values(sym, &name, NULL);
211	if (name != NULL) {
212		if (!strcmp(name, "calltrap")) {
213			frame_type = TRAP;
214		} else if (!strncmp(name, "Xresume", 7)) {
215			frame_type = INTERRUPT;
216		} else if (!strcmp(name, "syscall_with_err_pushed")) {
217			frame_type = SYSCALL;
218		}
219	}
220
221	/*
222	 * Normal frames need no special processing.
223	 */
224	if (frame_type == NORMAL) {
225		*ip = (db_addr_t) eip;
226		*fp = (struct i386_frame *) ebp;
227		return;
228	}
229
230	db_print_stack_entry(name, 0, 0, 0, eip);
231
232	/*
233	 * Point to base of trapframe which is just above the
234	 * current frame.
235	 */
236	if (frame_type == INTERRUPT)
237		tf = (struct trapframe *)((int)*fp + 12);
238	else
239		tf = (struct trapframe *)((int)*fp + 8);
240
241	if (INKERNEL((int) tf)) {
242		esp = (ISPL(tf->tf_cs) == SEL_UPL) ?
243		    tf->tf_esp : (int)&tf->tf_esp;
244		eip = tf->tf_eip;
245		ebp = tf->tf_ebp;
246
247		switch (frame_type) {
248		case TRAP:
249			db_printf("--- trap %#r", tf->tf_trapno);
250			break;
251		case SYSCALL:
252			db_printf("--- syscall");
253			decode_syscall(tf->tf_eax, p);
254			break;
255		case INTERRUPT:
256			db_printf("--- interrupt");
257			break;
258		default:
259			panic("The moon has moved again.");
260		}
261		db_printf(", eip = %#r, esp = %#r, ebp = %#r ---\n", eip,
262		    esp, ebp);
263	}
264
265	*ip = (db_addr_t) eip;
266	*fp = (struct i386_frame *) ebp;
267}
268
269void
270db_stack_trace_cmd(addr, have_addr, count, modif)
271	db_expr_t addr;
272	boolean_t have_addr;
273	db_expr_t count;
274	char *modif;
275{
276	struct i386_frame *frame;
277	int *argp;
278	db_addr_t callpc;
279	boolean_t first;
280	struct pcb *pcb;
281	struct proc *p;
282	pid_t pid;
283
284	if (count == -1)
285		count = 1024;
286
287	if (!have_addr) {
288		p = curproc;
289		frame = (struct i386_frame *)ddb_regs.tf_ebp;
290		if (frame == NULL)
291			frame = (struct i386_frame *)(ddb_regs.tf_esp - 4);
292		callpc = (db_addr_t)ddb_regs.tf_eip;
293	} else if (!INKERNEL(addr)) {
294		pid = (addr % 16) + ((addr >> 4) % 16) * 10 +
295		    ((addr >> 8) % 16) * 100 + ((addr >> 12) % 16) * 1000 +
296		    ((addr >> 16) % 16) * 10000;
297		/*
298		 * The pcb for curproc is not valid at this point,
299		 * so fall back to the default case.
300		 */
301		if (pid == curproc->p_pid) {
302			p = curproc;
303			frame = (struct i386_frame *)ddb_regs.tf_ebp;
304			if (frame == NULL)
305				frame = (struct i386_frame *)
306				    (ddb_regs.tf_esp - 4);
307			callpc = (db_addr_t)ddb_regs.tf_eip;
308		} else {
309
310			/* sx_slock(&allproc_lock); */
311			LIST_FOREACH(p, &allproc, p_list) {
312				if (p->p_pid == pid)
313					break;
314			}
315			/* sx_sunlock(&allproc_lock); */
316			if (p == NULL) {
317				db_printf("pid %d not found\n", pid);
318				return;
319			}
320			if ((p->p_sflag & PS_INMEM) == 0) {
321				db_printf("pid %d swapped out\n", pid);
322				return;
323			}
324			pcb = &p->p_addr->u_pcb;
325			frame = (struct i386_frame *)pcb->pcb_ebp;
326			if (frame == NULL)
327				frame = (struct i386_frame *)
328				    (pcb->pcb_esp - 4);
329			callpc = (db_addr_t)pcb->pcb_eip;
330		}
331	} else {
332		p = NULL;
333		frame = (struct i386_frame *)addr;
334		callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE);
335	}
336
337	first = TRUE;
338	while (count--) {
339		struct i386_frame *actframe;
340		int		narg;
341		const char *	name;
342		db_expr_t	offset;
343		c_db_sym_t	sym;
344#define MAXNARG	16
345		char	*argnames[MAXNARG], **argnp = NULL;
346
347		sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
348		db_symbol_values(sym, &name, NULL);
349
350		/*
351		 * Attempt to determine a (possibly fake) frame that gives
352		 * the caller's pc.  It may differ from `frame' if the
353		 * current function never sets up a standard frame or hasn't
354		 * set one up yet or has just discarded one.  The last two
355		 * cases can be guessed fairly reliably for code generated
356		 * by gcc.  The first case is too much trouble to handle in
357		 * general because the amount of junk on the stack depends
358		 * on the pc (the special handling of "calltrap", etc. in
359		 * db_nextframe() works because the `next' pc is special).
360		 */
361		actframe = frame;
362		if (first) {
363			if (!have_addr) {
364				int instr;
365
366				instr = db_get_value(callpc, 4, FALSE);
367				if ((instr & 0x00ffffff) == 0x00e58955) {
368					/* pushl %ebp; movl %esp, %ebp */
369					actframe = (struct i386_frame *)
370					    (ddb_regs.tf_esp - 4);
371				} else if ((instr & 0x0000ffff) == 0x0000e589) {
372					/* movl %esp, %ebp */
373					actframe = (struct i386_frame *)
374					    ddb_regs.tf_esp;
375					if (ddb_regs.tf_ebp == 0) {
376						/* Fake caller's frame better. */
377						frame = actframe;
378					}
379				} else if ((instr & 0x000000ff) == 0x000000c3) {
380					/* ret */
381					actframe = (struct i386_frame *)
382					    (ddb_regs.tf_esp - 4);
383				} else if (offset == 0) {
384					/* Probably a symbol in assembler code. */
385					actframe = (struct i386_frame *)
386					    (ddb_regs.tf_esp - 4);
387				}
388			} else if (!strcmp(name, "fork_trampoline")) {
389				/*
390				 * Don't try to walk back on a stack for a
391				 * process that hasn't actually been run yet.
392				 */
393				db_print_stack_entry(name, 0, 0, 0, callpc);
394				break;
395			}
396			first = FALSE;
397		}
398
399		argp = &actframe->f_arg0;
400		narg = MAXNARG;
401		if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) {
402			argnp = argnames;
403		} else {
404			narg = db_numargs(frame);
405		}
406
407		db_print_stack_entry(name, narg, argnp, argp, callpc);
408
409		if (actframe != frame) {
410			/* `frame' belongs to caller. */
411			callpc = (db_addr_t)
412			    db_get_value((int)&actframe->f_retaddr, 4, FALSE);
413			continue;
414		}
415
416		db_nextframe(&frame, &callpc, p);
417
418		if (INKERNEL((int) callpc) && !INKERNEL((int) frame)) {
419			sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
420			db_symbol_values(sym, &name, NULL);
421			db_print_stack_entry(name, 0, 0, 0, callpc);
422			break;
423		}
424		if (!INKERNEL((int) frame)) {
425			break;
426		}
427	}
428}
429
430#define DB_DRX_FUNC(reg)                \
431int					\
432db_ ## reg (vp, valuep, op)		\
433	struct db_variable *vp;		\
434	db_expr_t * valuep;		\
435	int op;				\
436{					\
437	if (op == DB_VAR_GET)		\
438		*valuep = r ## reg ();	\
439	else				\
440		load_ ## reg (*valuep); \
441					\
442	return(0);			\
443}
444
445DB_DRX_FUNC(dr0)
446DB_DRX_FUNC(dr1)
447DB_DRX_FUNC(dr2)
448DB_DRX_FUNC(dr3)
449DB_DRX_FUNC(dr4)
450DB_DRX_FUNC(dr5)
451DB_DRX_FUNC(dr6)
452DB_DRX_FUNC(dr7)
453