db_trace.c revision 5
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/*
27 * HISTORY
28 * $Log: db_trace.c,v $
29 * Revision 1.1  1992/03/25  21:42:05  pace
30 * Initial revision
31 *
32 * Revision 2.6  91/02/05  17:11:21  mrt
33 * 	Changed to new Mach copyright
34 * 	[91/02/01  17:31:32  mrt]
35 *
36 * Revision 2.5  91/01/09  19:55:27  rpd
37 * 	Fixed stack tracing for threads without kernel stacks.
38 * 	[91/01/09            rpd]
39 *
40 * Revision 2.4  91/01/08  15:10:22  rpd
41 * 	Reorganized the pcb.
42 * 	[90/12/11            rpd]
43 *
44 * Revision 2.3  90/11/05  14:27:07  rpd
45 * 	If we can not guess the number of args to a function, use 5 vs 0.
46 * 	[90/11/02            rvb]
47 *
48 * Revision 2.2  90/08/27  21:56:20  dbg
49 * 	Import db_sym.h.
50 * 	[90/08/21            dbg]
51 * 	Fix includes.
52 * 	[90/08/08            dbg]
53 * 	Created from rvb's code for new debugger.
54 * 	[90/07/11            dbg]
55 *
56 */
57#include "param.h"
58#include "proc.h"
59#include <machine/db_machdep.h>
60
61#include <ddb/db_access.h>
62#include <ddb/db_sym.h>
63#include <ddb/db_variables.h>
64
65/*
66 * Machine register set.
67 */
68struct db_variable db_regs[] = {
69	"cs",	(int *)&ddb_regs.tf_cs,  FCN_NULL,
70	"ds",	(int *)&ddb_regs.tf_ds,  FCN_NULL,
71	"es",	(int *)&ddb_regs.tf_es,  FCN_NULL,
72#if 0
73	"fs",	(int *)&ddb_regs.tf_fs,  FCN_NULL,
74	"gs",	(int *)&ddb_regs.tf_gs,  FCN_NULL,
75#endif
76	"ss",	(int *)&ddb_regs.tf_ss,  FCN_NULL,
77	"eax",	(int *)&ddb_regs.tf_eax, FCN_NULL,
78	"ecx",	(int *)&ddb_regs.tf_ecx, FCN_NULL,
79	"edx",	(int *)&ddb_regs.tf_edx, FCN_NULL,
80	"ebx",	(int *)&ddb_regs.tf_ebx, FCN_NULL,
81	"esp",	(int *)&ddb_regs.tf_esp,FCN_NULL,
82	"ebp",	(int *)&ddb_regs.tf_ebp, FCN_NULL,
83	"esi",	(int *)&ddb_regs.tf_esi, FCN_NULL,
84	"edi",	(int *)&ddb_regs.tf_edi, FCN_NULL,
85	"eip",	(int *)&ddb_regs.tf_eip, FCN_NULL,
86	"efl",	(int *)&ddb_regs.tf_eflags, FCN_NULL,
87};
88struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
89
90/*
91 * Stack trace.
92 */
93#define	INKERNEL(va)	(((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
94
95struct i386_frame {
96	struct i386_frame	*f_frame;
97	int			f_retaddr;
98	int			f_arg0;
99};
100
101#define	TRAP		1
102#define	INTERRUPT	2
103
104db_addr_t	db_trap_symbol_value = 0;
105db_addr_t	db_kdintr_symbol_value = 0;
106boolean_t	db_trace_symbols_found = FALSE;
107
108void
109db_find_trace_symbols()
110{
111	db_expr_t	value;
112	if (db_value_of_name("_trap", &value))
113	    db_trap_symbol_value = (db_addr_t) value;
114	if (db_value_of_name("_kdintr", &value))
115	    db_kdintr_symbol_value = (db_addr_t) value;
116	db_trace_symbols_found = TRUE;
117}
118
119/*
120 * Figure out how many arguments were passed into the frame at "fp".
121 */
122int
123db_numargs(fp)
124	struct i386_frame *fp;
125{
126	int	*argp;
127	int	inst;
128	int	args;
129	extern char	etext[];
130
131	argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE);
132	if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext)
133	    args = 5;
134	else {
135	    inst = db_get_value((int)argp, 4, FALSE);
136	    if ((inst & 0xff) == 0x59)	/* popl %ecx */
137		args = 1;
138	    else if ((inst & 0xffff) == 0xc483)	/* addl %n, %esp */
139		args = ((inst >> 16) & 0xff) / 4;
140	    else
141		args = 5;
142	}
143	return (args);
144}
145
146/*
147 * Figure out the next frame up in the call stack.
148 * For trap(), we print the address of the faulting instruction and
149 *   proceed with the calling frame.  We return the ip that faulted.
150 *   If the trap was caused by jumping through a bogus pointer, then
151 *   the next line in the backtrace will list some random function as
152 *   being called.  It should get the argument list correct, though.
153 *   It might be possible to dig out from the next frame up the name
154 *   of the function that faulted, but that could get hairy.
155 */
156void
157db_nextframe(fp, ip, argp, is_trap)
158	struct i386_frame **fp;		/* in/out */
159	db_addr_t	*ip;		/* out */
160	int *argp;			/* in */
161	int is_trap;			/* in */
162{
163	struct i386_saved_state *saved_regs;
164
165	if (is_trap == 0) {
166	    *ip = (db_addr_t)
167			db_get_value((int) &(*fp)->f_retaddr, 4, FALSE);
168	    *fp = (struct i386_frame *)
169			db_get_value((int) &(*fp)->f_frame, 4, FALSE);
170	} else {
171	    /*
172	     * We know that trap() has 1 argument and we know that
173	     * it is an (int *).
174	     */
175	    saved_regs = (struct i386_saved_state *)
176			db_get_value((int)argp, 4, FALSE);
177	    db_printf("--- trap (number %d) ---\n",
178		      saved_regs->tf_trapno & 0xffff);
179	    db_printsym(saved_regs->tf_eip, DB_STGY_XTRN);
180	    db_printf(":\n");
181	    *fp = (struct i386_frame *)saved_regs->tf_ebp;
182	    *ip = (db_addr_t)saved_regs->tf_eip;
183	}
184
185}
186
187void
188db_stack_trace_cmd(addr, have_addr, count, modif)
189	db_expr_t	addr;
190	boolean_t	have_addr;
191	db_expr_t	count;
192	char		*modif;
193{
194	struct i386_frame *frame, *lastframe;
195	int		*argp;
196	db_addr_t	callpc;
197	int		is_trap;
198	boolean_t	kernel_only = TRUE;
199	boolean_t	trace_thread = FALSE;
200
201	if (!db_trace_symbols_found)
202	    db_find_trace_symbols();
203
204	{
205	    register char *cp = modif;
206	    register char c;
207
208	    while ((c = *cp++) != 0) {
209		if (c == 't')
210		    trace_thread = TRUE;
211		if (c == 'u')
212		    kernel_only = FALSE;
213	    }
214	}
215
216	if (count == -1)
217	    count = 65535;
218
219	if (!have_addr) {
220	    frame = (struct i386_frame *)ddb_regs.tf_ebp;
221	    callpc = (db_addr_t)ddb_regs.tf_eip;
222	}
223	else if (trace_thread) {
224		printf ("db_trace.c: can't trace thread\n");
225	}
226	else {
227	    frame = (struct i386_frame *)addr;
228	    callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE);
229	}
230
231	lastframe = 0;
232	while (count-- && frame != 0) {
233	    register int narg;
234	    char *	name;
235	    db_expr_t	offset;
236
237	    if (INKERNEL((int)frame) && callpc == db_trap_symbol_value) {
238		narg = 1;
239		is_trap = TRAP;
240	    }
241	    else
242	    if (INKERNEL((int)frame) && callpc == db_kdintr_symbol_value) {
243		is_trap = INTERRUPT;
244		narg = 0;
245	    }
246	    else {
247		is_trap = 0;
248		narg = db_numargs(frame);
249	    }
250
251	    db_find_sym_and_offset(callpc, &name, &offset);
252	    db_printf("%s(", name);
253
254	    argp = &frame->f_arg0;
255	    while (narg) {
256		db_printf("%x", db_get_value((int)argp, 4, FALSE));
257		argp++;
258		if (--narg != 0)
259		    db_printf(",");
260	    }
261	    db_printf(") at ");
262	    db_printsym(callpc, DB_STGY_XTRN);
263	    db_printf("\n");
264
265	    lastframe = frame;
266	    db_nextframe(&frame, &callpc, &frame->f_arg0, is_trap);
267
268	    if (frame == 0) {
269		/* end of chain */
270		break;
271	    }
272	    if (INKERNEL((int)frame)) {
273		/* staying in kernel */
274		if (frame <= lastframe) {
275		    db_printf("Bad frame pointer: 0x%x\n", frame);
276		    break;
277		}
278	    }
279	    else if (INKERNEL((int)lastframe)) {
280		/* switch from user to kernel */
281		if (kernel_only)
282		    break;	/* kernel stack only */
283	    }
284	    else {
285		/* in user */
286		if (frame <= lastframe) {
287		    db_printf("Bad frame pointer: 0x%x\n", frame);
288		    break;
289		}
290	    }
291	}
292}
293