summaryrefslogtreecommitdiffstats
path: root/sys/powerpc/powerpc/db_trace.c
blob: 357695575ec1c2b0942345d025d951a3b639aeed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
/*	$FreeBSD$ */
/*	$NetBSD: db_trace.c,v 1.20 2002/05/13 20:30:09 matt Exp $	*/
/*	$OpenBSD: db_trace.c,v 1.3 1997/03/21 02:10:48 niklas Exp $	*/

/*-
 * Mach Operating System
 * Copyright (c) 1992 Carnegie Mellon University
 * All Rights Reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kdb.h>
#include <sys/proc.h>
#include <sys/stack.h>

#include <vm/vm.h>
#include <vm/pmap.h>
#include <vm/vm_extern.h>

#include <machine/db_machdep.h>
#include <machine/pcb.h>
#include <machine/spr.h>
#include <machine/stack.h>
#include <machine/trap.h>

#include <ddb/ddb.h>
#include <ddb/db_access.h>
#include <ddb/db_sym.h>
#include <ddb/db_variables.h>

static db_varfcn_t db_frame;

#define DB_OFFSET(x)	(db_expr_t *)offsetof(struct trapframe, x)

#ifdef __powerpc64__
#define	CALLOFFSET	8	/* Include TOC reload slot */
#else
#define	CALLOFFSET	4
#endif

struct db_variable db_regs[] = {
	{ "r0",	 DB_OFFSET(fixreg[0]),	db_frame },
	{ "r1",	 DB_OFFSET(fixreg[1]),	db_frame },
	{ "r2",	 DB_OFFSET(fixreg[2]),	db_frame },
	{ "r3",	 DB_OFFSET(fixreg[3]),	db_frame },
	{ "r4",	 DB_OFFSET(fixreg[4]),	db_frame },
	{ "r5",	 DB_OFFSET(fixreg[5]),	db_frame },
	{ "r6",	 DB_OFFSET(fixreg[6]),	db_frame },
	{ "r7",	 DB_OFFSET(fixreg[7]),	db_frame },
	{ "r8",	 DB_OFFSET(fixreg[8]),	db_frame },
	{ "r9",	 DB_OFFSET(fixreg[9]),	db_frame },
	{ "r10", DB_OFFSET(fixreg[10]),	db_frame },
	{ "r11", DB_OFFSET(fixreg[11]),	db_frame },
	{ "r12", DB_OFFSET(fixreg[12]),	db_frame },
	{ "r13", DB_OFFSET(fixreg[13]),	db_frame },
	{ "r14", DB_OFFSET(fixreg[14]),	db_frame },
	{ "r15", DB_OFFSET(fixreg[15]),	db_frame },
	{ "r16", DB_OFFSET(fixreg[16]),	db_frame },
	{ "r17", DB_OFFSET(fixreg[17]),	db_frame },
	{ "r18", DB_OFFSET(fixreg[18]),	db_frame },
	{ "r19", DB_OFFSET(fixreg[19]),	db_frame },
	{ "r20", DB_OFFSET(fixreg[20]),	db_frame },
	{ "r21", DB_OFFSET(fixreg[21]),	db_frame },
	{ "r22", DB_OFFSET(fixreg[22]),	db_frame },
	{ "r23", DB_OFFSET(fixreg[23]),	db_frame },
	{ "r24", DB_OFFSET(fixreg[24]),	db_frame },
	{ "r25", DB_OFFSET(fixreg[25]),	db_frame },
	{ "r26", DB_OFFSET(fixreg[26]),	db_frame },
	{ "r27", DB_OFFSET(fixreg[27]),	db_frame },
	{ "r28", DB_OFFSET(fixreg[28]),	db_frame },
	{ "r29", DB_OFFSET(fixreg[29]),	db_frame },
	{ "r30", DB_OFFSET(fixreg[30]),	db_frame },
	{ "r31", DB_OFFSET(fixreg[31]),	db_frame },
	{ "srr0", DB_OFFSET(srr0),	db_frame },
	{ "srr1", DB_OFFSET(srr1),	db_frame },
	{ "lr",	 DB_OFFSET(lr),		db_frame },
	{ "ctr", DB_OFFSET(ctr),	db_frame },
	{ "cr",	 DB_OFFSET(cr),		db_frame },
	{ "xer", DB_OFFSET(xer),	db_frame },
	{ "dar", DB_OFFSET(dar),	db_frame },
#ifdef AIM
	{ "dsisr", DB_OFFSET(cpu.aim.dsisr),	db_frame },
#endif
#if defined(BOOKE)
	{ "esr", DB_OFFSET(cpu.booke.esr),	db_frame },
#endif
};
struct db_variable *db_eregs = db_regs + nitems(db_regs);

/*
 * register variable handling
 */
static int
db_frame(struct db_variable *vp, db_expr_t *valuep, int op)
{
	register_t *reg;

	if (kdb_frame == NULL)
		return (0);
        reg = (register_t*)((uintptr_t)kdb_frame + (uintptr_t)vp->valuep);
        if (op == DB_VAR_GET)
                *valuep = *reg;
        else
                *reg = *valuep;
        return (1);
}


/*
 *	Frame tracing.
 */
static int
db_backtrace(struct thread *td, db_addr_t fp, int count)
{
	db_addr_t stackframe, lr, *args;
	boolean_t kernel_only = TRUE;
	boolean_t full = FALSE;

#if 0
	{
		register char *cp = modif;
		register char c;

		while ((c = *cp++) != 0) {
			if (c == 't')
				trace_thread = TRUE;
			if (c == 'u')
				kernel_only = FALSE;
			if (c == 'f')
				full = TRUE;
		}
	}
#endif

	stackframe = fp;

	while (!db_pager_quit) {
		if (stackframe < PAGE_SIZE)
			break;

		/*
		 * Locate the next frame by grabbing the backchain ptr
		 * from frame[0]
		 */
		stackframe = *(db_addr_t *)stackframe;

	next_frame:
	    #ifdef __powerpc64__
		/* The saved arg values start at frame[6] */
		args = (db_addr_t *)(stackframe + 48);
	    #else
		/* The saved arg values start at frame[2] */
		args = (db_addr_t *)(stackframe + 8);
	    #endif

		if (stackframe < PAGE_SIZE)
			break;

	        if (count-- == 0)
			break;

		/*
		 * Extract link register from frame and subtract
		 * 4 to convert into calling address (as opposed to
		 * return address)
		 */
	    #ifdef __powerpc64__
		lr = *(db_addr_t *)(stackframe + 16) - 4;
	    #else
		lr = *(db_addr_t *)(stackframe + 4) - 4;
	    #endif
		if ((lr & 3) || (lr < 0x100)) {
			db_printf("saved LR(0x%zx) is invalid.", lr);
			break;
		}

		#ifdef __powerpc64__
		db_printf("0x%016lx: ", stackframe);
		#else
		db_printf("0x%08x: ", stackframe);
		#endif

		/*
		 * The trap code labels the return addresses from the
		 * call to C code as 'trapexit' and 'asttrapexit. Use this
		 * to determine if the callframe has to traverse a saved
		 * trap context
		 */
		if ((lr + CALLOFFSET == (db_addr_t) &trapexit) ||
		    (lr + CALLOFFSET == (db_addr_t) &asttrapexit)) {
			const char *trapstr;
			struct trapframe *tf = (struct trapframe *)(args);
			db_printf("%s ", tf->srr1 & PSL_PR ? "user" : "kernel");
			switch (tf->exc) {
			case EXC_DSI:
				/* XXX take advantage of the union. */
				db_printf("DSI %s trap @ %#zx by ",
				    (tf->cpu.aim.dsisr & DSISR_STORE) ? "write"
				    : "read", tf->dar);
				goto print_trap;
			case EXC_ALI:
				/* XXX take advantage of the union. */
				db_printf("ALI trap @ %#zx (xSR %#x) ",
				    tf->dar, (uint32_t)tf->cpu.aim.dsisr);
				goto print_trap;
#ifdef __powerpc64__
			case EXC_DSE:
				db_printf("DSE trap @ %#zx by ", tf->dar);
				goto print_trap;
			case EXC_ISE:
				db_printf("ISE trap @ %#zx by ", tf->srr0);
				goto print_trap;
#endif
			case EXC_ISI: trapstr = "ISI"; break;
			case EXC_PGM: trapstr = "PGM"; break;
			case EXC_SC: trapstr = "SC"; break;
			case EXC_EXI: trapstr = "EXI"; break;
			case EXC_MCHK: trapstr = "MCHK"; break;
#if !defined(BOOKE)
			case EXC_VEC: trapstr = "VEC"; break;
			case EXC_FPA: trapstr = "FPA"; break;
			case EXC_BPT: trapstr = "BPT"; break;
			case EXC_TRC: trapstr = "TRC"; break;
			case EXC_RUNMODETRC: trapstr = "RUNMODETRC"; break;
			case EXC_SMI: trapstr = "SMI"; break;
			case EXC_RST: trapstr = "RST"; break;
#endif
			case EXC_FPU: trapstr = "FPU"; break;
			case EXC_DECR: trapstr = "DECR"; break;
			case EXC_PERF: trapstr = "PERF"; break;
			case EXC_VSX: trapstr = "VSX"; break;
			default: trapstr = NULL; break;
			}
			if (trapstr != NULL) {
				db_printf("%s trap by ", trapstr);
			} else {
				db_printf("trap %#zx by ", tf->exc);
			}

		   print_trap:
			lr = (db_addr_t) tf->srr0;
			db_printsym(lr, DB_STGY_ANY);
			db_printf(": srr1=%#zx\n", tf->srr1);
			db_printf("%-10s  r1=%#zx cr=%#x xer=%#x ctr=%#zx",
			    "", tf->fixreg[1], (uint32_t)tf->cr,
			    (uint32_t)tf->xer, tf->ctr);
#ifdef __powerpc64__
			db_printf(" r2=%#zx", tf->fixreg[2]);
#endif
			if (tf->exc == EXC_DSI)
				db_printf(" sr=%#x",
				    (uint32_t)tf->cpu.aim.dsisr);
			db_printf("\n");
			stackframe = (db_addr_t) tf->fixreg[1];
			if (kernel_only && (tf->srr1 & PSL_PR))
				break;
			goto next_frame;
		}

		db_printf("at ");
		db_printsym(lr, DB_STGY_PROC);
		if (full)
			/* Print all the args stored in that stackframe. */
			db_printf("(%zx, %zx, %zx, %zx, %zx, %zx, %zx, %zx)",
				args[0], args[1], args[2], args[3],
				args[4], args[5], args[6], args[7]);
		db_printf("\n");
	}

	return (0);
}

void
db_trace_self(void)
{
	db_addr_t addr;

	addr = (db_addr_t)__builtin_frame_address(1);
	db_backtrace(curthread, addr, -1);
}

int
db_trace_thread(struct thread *td, int count)
{
	struct pcb *ctx;

	ctx = kdb_thr_ctx(td);
	return (db_backtrace(td, (db_addr_t)ctx->pcb_sp, count));
}
OpenPOWER on IntegriCloud