1 /* $NetBSD: db_trace.c,v 1.33 2018/01/24 09:04:44 skrll Exp $ */
2
3 /*
4 * Copyright (c) 2000, 2001 Ben Harris
5 * Copyright (c) 1996 Scott K. Stevens
6 *
7 * Mach Operating System
8 * Copyright (c) 1991,1990 Carnegie Mellon University
9 * All Rights Reserved.
10 *
11 * Permission to use, copy, modify and distribute this software and its
12 * documentation is hereby granted, provided that both the copyright
13 * notice and this permission notice appear in all copies of the
14 * software, derivative works or modified versions, and any portions
15 * thereof, and that both notices appear in supporting documentation.
16 *
17 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
18 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
19 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
20 *
21 * Carnegie Mellon requests users of this software to return to
22 *
23 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
24 * School of Computer Science
25 * Carnegie Mellon University
26 * Pittsburgh PA 15213-3890
27 *
28 * any improvements or extensions that they make and grant Carnegie the
29 * rights to redistribute these changes.
30 */
31
32 #include <sys/param.h>
33
34 __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.33 2018/01/24 09:04:44 skrll Exp $");
35
36 #include <sys/proc.h>
37 #include <arm/armreg.h>
38 #include <arm/cpufunc.h>
39 #include <arm/pcb.h>
40 #include <machine/db_machdep.h>
41 #include <machine/vmparam.h>
42
43 #include <ddb/db_access.h>
44 #include <ddb/db_interface.h>
45 #include <ddb/db_sym.h>
46 #include <ddb/db_proc.h>
47 #include <ddb/db_output.h>
48
49 #define INKERNEL(va) (((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
50
51 /*
52 * APCS stack frames are awkward beasts, so I don't think even trying to use
53 * a structure to represent them is a good idea.
54 *
55 * Here's the diagram from the APCS. Increasing address is _up_ the page.
56 *
57 * save code pointer [fp] <- fp points to here
58 * return link value [fp, #-4]
59 * return sp value [fp, #-8]
60 * return fp value [fp, #-12]
61 * [saved v7 value]
62 * [saved v6 value]
63 * [saved v5 value]
64 * [saved v4 value]
65 * [saved v3 value]
66 * [saved v2 value]
67 * [saved v1 value]
68 * [saved a4 value]
69 * [saved a3 value]
70 * [saved a2 value]
71 * [saved a1 value]
72 *
73 * The save code pointer points twelve bytes beyond the start of the
74 * code sequence (usually a single STM) that created the stack frame.
75 * We have to disassemble it if we want to know which of the optional
76 * fields are actually present.
77 */
78
79 #define FR_SCP (0)
80 #define FR_RLV (-1)
81 #define FR_RSP (-2)
82 #define FR_RFP (-3)
83
84 void
db_stack_trace_print(db_expr_t addr,bool have_addr,db_expr_t count,const char * modif,void (* pr)(const char *,...))85 db_stack_trace_print(db_expr_t addr, bool have_addr,
86 db_expr_t count, const char *modif,
87 void (*pr)(const char *, ...))
88 {
89 struct trapframe *tf = NULL;
90 uint32_t *frame, *lastframe;
91 const char *cp = modif;
92 char c;
93 bool kernel_only = true;
94 bool trace_thread = false;
95 bool trace_full = false;
96 bool lwpaddr = false;
97 db_addr_t scp, pc;
98 int scp_offset;
99
100 while ((c = *cp++) != 0) {
101 if (c == 'a') {
102 lwpaddr = true;
103 trace_thread = true;
104 }
105 if (c == 'u')
106 kernel_only = false;
107 if (c == 't')
108 trace_thread = true;
109 if (c == 'f')
110 trace_full = true;
111 }
112
113 #ifdef _KERNEL
114 if (!have_addr)
115 frame = (uint32_t *)(DDB_REGS->tf_r11);
116 else
117 #endif
118 {
119 if (trace_thread) {
120 struct pcb *pcb;
121 proc_t p;
122 lwp_t l;
123
124 if (lwpaddr) {
125 db_read_bytes(addr, sizeof(l),
126 (char *)&l);
127 db_read_bytes((db_addr_t)l.l_proc,
128 sizeof(p), (char *)&p);
129 (*pr)("trace: pid %d ", p.p_pid);
130 } else {
131 proc_t *pp;
132
133 (*pr)("trace: pid %d ", (int)addr);
134 if ((pp = db_proc_find((pid_t)addr)) == 0) {
135 (*pr)("not found\n");
136 return;
137 }
138 db_read_bytes((db_addr_t)pp, sizeof(p), (char *)&p);
139 addr = (db_addr_t)p.p_lwps.lh_first;
140 db_read_bytes(addr, sizeof(l), (char *)&l);
141 }
142 (*pr)("lid %d ", l.l_lid);
143 pcb = lwp_getpcb(&l);
144 tf = lwp_trapframe(&l);
145 #ifndef _KERNEL
146 struct pcb pcbb;
147 db_read_bytes((db_addr_t)pcb, sizeof(*pcb),
148 (char *)&pcbb);
149 pcb = &pcbb;
150 #endif
151 frame = (uint32_t *)(pcb->pcb_r11);
152 (*pr)("at %p\n", frame);
153 } else
154 frame = (uint32_t *)(addr);
155 }
156 scp_offset = -(get_pc_str_offset() >> 2);
157
158 if (frame == NULL)
159 return;
160
161 lastframe = frame;
162 #ifndef _KERNEL
163 uint32_t frameb[4];
164 db_read_bytes((db_addr_t)(frame - 3), sizeof(frameb),
165 (char *)frameb);
166 frame = frameb + 3;
167 #endif
168
169 /*
170 * In theory, the SCP isn't guaranteed to be in the function
171 * that generated the stack frame. We hope for the best.
172 */
173 scp = frame[FR_SCP];
174 pc = scp;
175
176 while (count--) {
177 uint32_t savecode;
178 int r;
179 uint32_t *rp;
180 const char *sep;
181
182 scp = frame[FR_SCP];
183 (*pr)("%p: ", lastframe);
184
185 db_printsym(pc, DB_STGY_PROC, pr);
186 if (trace_full) {
187 (*pr)("\n\t");
188 (*pr)("pc =0x%08x rlv=0x%08x (", pc, frame[FR_RLV]);
189 db_printsym(frame[FR_RLV], DB_STGY_PROC, pr);
190 (*pr)(")\n");
191 (*pr)("\trsp=0x%08x rfp=0x%08x", frame[FR_RSP],
192 frame[FR_RFP]);
193 }
194
195 #ifndef _KERNEL
196 db_read_bytes((db_addr_t)((uint32_t *)scp + scp_offset),
197 sizeof(savecode), (void *)&savecode);
198 #else
199 if ((scp & 3) == 0) {
200 savecode = ((uint32_t *)scp)[scp_offset];
201 } else {
202 savecode = 0;
203 }
204 #endif
205 if (trace_full &&
206 (savecode & 0x0e100000) == 0x08000000) {
207 /* Looks like an STM */
208 rp = frame - 4;
209 sep = "\n\t";
210 for (r = 10; r >= 0; r--) {
211 if (savecode & (1 << r)) {
212 (*pr)("%sr%d=0x%08x",
213 sep, r, *rp--);
214 sep = (frame - rp) % 4 == 2 ?
215 "\n\t" : " ";
216 }
217 }
218 }
219
220 (*pr)("\n");
221 /*
222 * Switch to next frame up
223 */
224 if (frame[FR_RFP] == 0)
225 break; /* Top of stack */
226 pc = frame[FR_RLV];
227
228 frame = (uint32_t *)(frame[FR_RFP]);
229
230 if (frame == NULL)
231 break;
232
233 if (INKERNEL((int)frame)) {
234 /* staying in kernel */
235 if (frame <= lastframe) {
236 (*pr)("Bad frame pointer: %p\n", frame);
237 break;
238 }
239 } else if (INKERNEL((int)lastframe)) {
240 if (trace_thread) {
241 (*pr)("--- tf %p ---\n", tf);
242 }
243 /* switch from user to kernel */
244 if (kernel_only)
245 break; /* kernel stack only */
246 } else {
247 /* in user */
248 if (frame <= lastframe) {
249 (*pr)("Bad user frame pointer: %p\n",
250 frame);
251 break;
252 }
253 }
254 lastframe = frame;
255 #ifndef _KERNEL
256 db_read_bytes((db_addr_t)(frame - 3), sizeof(frameb),
257 (char *)frameb);
258 frame = frameb + 3;
259 #endif
260 }
261 }
262