author | Jon Tibble <meths@btinternet.com> |
Thu, 09 Dec 2010 22:32:39 +0100 | |
changeset 13255 | 4afa820d78b9 |
parent 11411 | c2fe1bf96826 |
permissions | -rw-r--r-- |
0 | 1 |
/* |
2 |
* CDDL HEADER START |
|
3 |
* |
|
4 |
* The contents of this file are subject to the terms of the |
|
3988 | 5 |
* Common Development and Distribution License (the "License"). |
6 |
* You may not use this file except in compliance with the License. |
|
0 | 7 |
* |
8 |
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
9 |
* or http://www.opensolaris.org/os/licensing. |
|
10 |
* See the License for the specific language governing permissions |
|
11 |
* and limitations under the License. |
|
12 |
* |
|
13 |
* When distributing Covered Code, include this CDDL HEADER in each |
|
14 |
* file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
15 |
* If applicable, add the following below this CDDL HEADER, with the |
|
16 |
* fields enclosed by brackets "[]" replaced with your own identifying |
|
17 |
* information: Portions Copyright [yyyy] [name of copyright owner] |
|
18 |
* |
|
19 |
* CDDL HEADER END |
|
20 |
* |
|
11411
c2fe1bf96826
6894056 libc is not clean
Surya Prakki <Surya.Prakki@Sun.COM>
parents:
6812
diff
changeset
|
21 |
* Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
0 | 22 |
* Use is subject to license terms. |
23 |
*/ |
|
24 |
||
25 |
/* |
|
26 |
* This file provides a general purpose mechanism |
|
27 |
* for a user thread to walk its own call stack, |
|
28 |
* calling a user-specified iterator function for each |
|
29 |
* stack frame. Special handling is provided to indicate |
|
30 |
* kernel-constructed signal handler frames. |
|
31 |
* |
|
32 |
* Adapted from usr/src/lib/libproc/common/Pstack.c: |
|
33 |
* |
|
34 |
* A signal handler frame is essentially a set of data pushed on to the user |
|
35 |
* stack by the kernel prior to returning to the user program in one of the |
|
36 |
* pre-defined signal handlers. The signal handler itself receives the signal |
|
37 |
* number, an optional pointer to a siginfo_t, and a pointer to the interrupted |
|
38 |
* ucontext as arguments. |
|
39 |
* |
|
40 |
* When performing a stack backtrace, we would like to |
|
41 |
* detect these frames so that we can correctly return the interrupted program |
|
42 |
* counter and frame pointer as a separate frame. |
|
43 |
* |
|
44 |
* The stack layout for a signal handler frame is as follows: |
|
45 |
* |
|
46 |
* SPARC v7/v9: Intel ia32: |
|
47 |
* +--------------+ - high +--------------+ - |
|
48 |
* | struct fq | ^ addrs | siginfo_t | optional |
|
49 |
* +--------------+ | ^ +--------------+ - |
|
50 |
* | gwindows_t | | | ucontext_t | ^ |
|
51 |
* +--------------+ optional +--------------+ | |
|
52 |
* | siginfo_t | | ucontext_t * | | |
|
53 |
* +--------------+ | | +--------------+ |
|
54 |
* | xregs data | v v | siginfo_t * | mandatory |
|
55 |
* +--------------+ - low +--------------+ |
|
56 |
* | ucontext_t | ^ addrs | int (signo) | | |
|
57 |
* +--------------+ mandatory +--------------+ | |
|
58 |
* | struct frame | v | struct frame | v |
|
59 |
* +--------------+ - <- %sp on resume +--------------+ - <- %esp on resume |
|
60 |
* |
|
61 |
* amd64 (64-bit) |
|
62 |
* +--------------+ - |
|
63 |
* | siginfo_t | optional |
|
64 |
* +--------------+ - |
|
65 |
* | ucontext_t | ^ |
|
66 |
* +--------------+ | |
|
67 |
* | siginfo_t * | |
|
68 |
* +--------------+ mandatory |
|
69 |
* | int (signo) | |
|
70 |
* +--------------+ | |
|
71 |
* | struct frame | v |
|
72 |
* +--------------+ - <- %rsp on resume |
|
73 |
* |
|
74 |
* The bottom-most struct frame is actually constructed by the kernel by |
|
75 |
* copying the previous stack frame, allowing naive backtrace code to simply |
|
76 |
* skip over the interrupted frame. The copied frame is never really used, |
|
6812 | 77 |
* since it is presumed the signal handler wrapper function |
0 | 78 |
* will explicitly setcontext(2) to the interrupted context if the user |
79 |
* program's handler returns. If we detect a signal handler frame, we simply |
|
80 |
* read the interrupted context structure from the stack, use its embedded |
|
81 |
* gregs to construct the register set for the interrupted frame, and then |
|
82 |
* continue our backtrace. Detecting the frame itself is easy according to |
|
83 |
* the diagram ("oldcontext" represents any element in the uc_link chain): |
|
84 |
* |
|
85 |
* On SPARC v7 or v9: |
|
86 |
* %fp + sizeof (struct frame) == oldcontext |
|
87 |
* |
|
88 |
* On i386: |
|
89 |
* %ebp + sizeof (struct frame) + (3 words) == oldcontext |
|
90 |
* |
|
91 |
* On amd64: |
|
92 |
* %rbp + sizeof (struct frame) + (2 words) == oldcontext |
|
93 |
* |
|
94 |
* Since we want to provide the signal number that generated a signal stack |
|
95 |
* frame and on sparc this information isn't written to the stack by the kernel |
|
96 |
* the way it's done on i386, we're forced to read the signo from the stack as |
|
6812 | 97 |
* one of the arguments to the signal handler. We use the thr_sighndlrinfo |
98 |
* interface to find the correct frame. |
|
0 | 99 |
*/ |
100 |
||
6812 | 101 |
#include "lint.h" |
0 | 102 |
#include <assert.h> |
103 |
#include <dlfcn.h> |
|
104 |
#include <fcntl.h> |
|
105 |
#include <link.h> |
|
106 |
#include <procfs.h> |
|
107 |
#include <strings.h> |
|
108 |
#include <signal.h> |
|
109 |
#include <sys/frame.h> |
|
110 |
#include <sys/regset.h> |
|
111 |
#include <sys/types.h> |
|
112 |
#include <sys/uio.h> |
|
113 |
#include <thread.h> |
|
114 |
#include <ucontext.h> |
|
115 |
#include <unistd.h> |
|
116 |
#include <stdarg.h> |
|
117 |
#include <sys/stack.h> |
|
118 |
#include <errno.h> |
|
119 |
#include <stdio.h> |
|
120 |
#include <alloca.h> |
|
121 |
#include <limits.h> |
|
3988 | 122 |
#include <stdlib.h> |
0 | 123 |
|
124 |
#ifdef _LP64 |
|
125 |
#define _ELF64 |
|
126 |
#endif |
|
127 |
||
128 |
#include <sys/machelf.h> |
|
129 |
||
130 |
||
131 |
#if defined(__sparc) |
|
132 |
#define FRAME_PTR_REGISTER REG_SP |
|
133 |
#define PC_REGISTER REG_PC |
|
134 |
#define CHECK_FOR_SIGFRAME(fp, oldctx) ((fp) + SA(sizeof (struct frame)) \ |
|
135 |
== (oldctx)) |
|
136 |
||
137 |
#elif defined(__amd64) |
|
138 |
#define FRAME_PTR_REGISTER REG_RBP |
|
139 |
#define PC_REGISTER REG_RIP |
|
140 |
#define CHECK_FOR_SIGFRAME(fp, oldctx) ((((fp) + sizeof (struct frame)) + \ |
|
141 |
2 * sizeof (long) == (oldctx)) && \ |
|
142 |
(((struct frame *)fp)->fr_savpc == (greg_t)-1)) |
|
143 |
||
144 |
#elif defined(__i386) |
|
145 |
#define FRAME_PTR_REGISTER EBP |
|
146 |
#define PC_REGISTER EIP |
|
147 |
#define CHECK_FOR_SIGFRAME(fp, oldctx) ((((fp) + sizeof (struct frame)) + \ |
|
148 |
3 * sizeof (int) == (oldctx)) && \ |
|
149 |
(((struct frame *)fp)->fr_savpc == (greg_t)-1)) |
|
150 |
#else |
|
151 |
#error no arch defined |
|
152 |
#endif |
|
153 |
||
3988 | 154 |
#define MAX_LINE 2048 /* arbitrary large value */ |
0 | 155 |
|
156 |
/* |
|
157 |
* use /proc/self/as to safely dereference pointers so we don't |
|
158 |
* die in the case of a stack smash |
|
159 |
*/ |
|
160 |
||
161 |
static int |
|
162 |
read_safe(int fd, struct frame *fp, struct frame **savefp, uintptr_t *savepc) |
|
163 |
{ |
|
164 |
||
165 |
uintptr_t newfp; |
|
166 |
||
167 |
if ((uintptr_t)fp & (sizeof (void *) - 1)) |
|
168 |
return (-1); /* misaligned */ |
|
169 |
||
170 |
if ((pread(fd, (void *)&newfp, sizeof (fp->fr_savfp), |
|
171 |
(off_t)&fp->fr_savfp) != sizeof (fp->fr_savfp)) || |
|
172 |
pread(fd, (void *)savepc, sizeof (fp->fr_savpc), |
|
173 |
(off_t)&fp->fr_savpc) != sizeof (fp->fr_savpc)) |
|
174 |
return (-1); |
|
175 |
||
176 |
/* |
|
177 |
* handle stack bias on sparcv9 |
|
178 |
*/ |
|
179 |
||
180 |
if (newfp != 0) |
|
181 |
newfp += STACK_BIAS; |
|
182 |
||
183 |
*savefp = (struct frame *)newfp; |
|
184 |
||
185 |
return (0); |
|
186 |
} |
|
187 |
||
188 |
int |
|
189 |
walkcontext(const ucontext_t *uptr, int (*operate_func)(uintptr_t, int, void *), |
|
190 |
void *usrarg) |
|
191 |
{ |
|
192 |
ucontext_t *oldctx = uptr->uc_link; |
|
193 |
||
194 |
int fd; |
|
195 |
int sig; |
|
196 |
#if defined(__sparc) |
|
197 |
int signo = 0; |
|
198 |
#endif |
|
199 |
||
200 |
struct frame *savefp; |
|
201 |
uintptr_t savepc; |
|
202 |
||
203 |
/* |
|
204 |
* snag frame point from ucontext... we'll see caller of |
|
205 |
* getucontext since we'll start by working up the call |
|
206 |
* stack by one |
|
207 |
*/ |
|
208 |
||
209 |
struct frame *fp = (struct frame *) |
|
210 |
((uintptr_t)uptr->uc_mcontext.gregs[FRAME_PTR_REGISTER] + |
|
211 |
STACK_BIAS); |
|
212 |
||
213 |
/* |
|
214 |
* Since we don't write signo to the stack on sparc, we need |
|
6812 | 215 |
* to extract signo from the stack frames. |
216 |
* An awkward interface is provided for this purpose: |
|
217 |
* thr_sighndlrinfo; this is documented in |
|
0 | 218 |
* /shared/sac/PSARC/1999/024. When called, this function |
219 |
* returns the PC of a special function (and its size) that |
|
220 |
* will be present in the stack frame if a signal was |
|
221 |
* delivered and will have the following signature |
|
222 |
* __sighndlr(int sig, siginfo_t *si, ucontex_t *uc, |
|
223 |
* void (*hndlr)()) |
|
224 |
* Since this function is written in assembler and doesn't |
|
225 |
* perturb its registers, we can then read sig out of arg0 |
|
226 |
* when the saved pc is inside this function. |
|
227 |
*/ |
|
228 |
#if defined(__sparc) |
|
229 |
||
230 |
uintptr_t special_pc = NULL; |
|
231 |
int special_size = 0; |
|
232 |
||
6812 | 233 |
extern void thr_sighndlrinfo(void (**func)(), int *funcsize); |
0 | 234 |
|
6812 | 235 |
thr_sighndlrinfo((void (**)())&special_pc, &special_size); |
0 | 236 |
#endif /* sparc */ |
237 |
||
238 |
||
239 |
if ((fd = open("/proc/self/as", O_RDONLY)) < 0) |
|
240 |
return (-1); |
|
241 |
||
242 |
while (fp != NULL) { |
|
243 |
||
244 |
sig = 0; |
|
245 |
||
246 |
/* |
|
247 |
* get value of saved fp and pc w/o crashing |
|
248 |
*/ |
|
249 |
||
250 |
if (read_safe(fd, fp, &savefp, &savepc) != 0) { |
|
251 |
(void) close(fd); |
|
252 |
return (-1); |
|
253 |
} |
|
254 |
||
255 |
if (savefp == NULL) |
|
256 |
break; |
|
257 |
||
258 |
/* |
|
259 |
* note that the following checks to see if we've got a |
|
260 |
* special signal stack frame present; this allows us to |
|
261 |
* detect signals and pass that info to the user stack walker |
|
262 |
*/ |
|
263 |
||
264 |
if (oldctx != NULL && |
|
265 |
CHECK_FOR_SIGFRAME((uintptr_t)savefp, (uintptr_t)oldctx)) { |
|
266 |
||
267 |
#if defined(__i386) || defined(__amd64) |
|
268 |
/* |
|
269 |
* i386 and amd64 store signo on stack; |
|
270 |
* simple to detect and use |
|
271 |
*/ |
|
272 |
sig = *((int *)(savefp + 1)); |
|
273 |
#endif |
|
274 |
||
275 |
#if defined(__sparc) |
|
276 |
/* |
|
277 |
* In the case of threads, since there are multiple |
|
278 |
* complex routines between kernel and user handler, |
|
279 |
* we need to figure out where we can read signal from |
|
6812 | 280 |
* using thr_sighndlrinfo - which we've already done |
0 | 281 |
* for this signal, since it appeared on the stack |
282 |
* before the signal frame.... sigh. |
|
283 |
*/ |
|
6812 | 284 |
sig = signo; /* already read - see below */ |
0 | 285 |
#endif |
286 |
/* |
|
287 |
* this is the special signal frame, so cons up |
|
288 |
* the saved fp & pc to pass to user's function |
|
289 |
*/ |
|
290 |
||
291 |
savefp = (struct frame *) |
|
292 |
((uintptr_t)oldctx-> |
|
293 |
uc_mcontext.gregs[FRAME_PTR_REGISTER] + |
|
294 |
STACK_BIAS); |
|
295 |
savepc = oldctx->uc_mcontext.gregs[PC_REGISTER]; |
|
296 |
||
297 |
oldctx = oldctx->uc_link; /* handle nested signals */ |
|
298 |
} |
|
299 |
#if defined(__sparc) |
|
300 |
||
301 |
/* |
|
302 |
* lookahead code to find right spot to read signo from... |
|
303 |
*/ |
|
304 |
||
6812 | 305 |
if (savepc >= special_pc && savepc < |
0 | 306 |
(special_pc + special_size)) |
307 |
signo = fp->fr_arg[0]; |
|
308 |
#endif |
|
309 |
||
310 |
/* |
|
311 |
* call user-supplied function and quit if non-zero return. |
|
312 |
*/ |
|
313 |
||
314 |
if (operate_func((uintptr_t)savepc, sig, usrarg) != 0) |
|
315 |
break; |
|
316 |
||
317 |
fp = savefp; /* up one in the call stack */ |
|
318 |
} |
|
319 |
||
320 |
(void) close(fd); |
|
321 |
return (0); |
|
322 |
} |
|
323 |
||
3988 | 324 |
/* |
325 |
* async safe version of fprintf |
|
326 |
*/ |
|
0 | 327 |
|
328 |
static void |
|
329 |
async_filenoprintf(int filenum, const char *format, ...) |
|
330 |
{ |
|
331 |
va_list ap; |
|
3988 | 332 |
char buffer[MAX_LINE]; |
0 | 333 |
|
334 |
va_start(ap, format); |
|
3988 | 335 |
(void) vsnprintf(buffer, sizeof (buffer), format, ap); |
0 | 336 |
va_end(ap); |
337 |
||
3988 | 338 |
(void) write(filenum, buffer, strlen(buffer)); |
0 | 339 |
|
340 |
} |
|
341 |
||
3988 | 342 |
/* |
343 |
* print out stack frame info |
|
344 |
*/ |
|
345 |
||
0 | 346 |
static int |
347 |
display_stack_info(uintptr_t pc, int signo, void *arg) |
|
348 |
{ |
|
349 |
||
3988 | 350 |
char buffer[MAX_LINE]; |
0 | 351 |
char sigbuf[SIG2STR_MAX]; |
352 |
||
353 |
||
354 |
int filenum = (intptr_t)arg; |
|
355 |
||
3988 | 356 |
(void) addrtosymstr((void *)pc, buffer, sizeof (buffer)); |
0 | 357 |
|
3988 | 358 |
if (signo) { |
359 |
sigbuf[0] = '?'; |
|
360 |
sigbuf[1] = 0; |
|
0 | 361 |
|
3988 | 362 |
(void) sig2str(signo, sigbuf); |
363 |
||
364 |
async_filenoprintf(filenum, "%s [Signal %d (%s)]\n", |
|
6812 | 365 |
buffer, (ulong_t)signo, sigbuf); |
3988 | 366 |
} else |
367 |
async_filenoprintf(filenum, "%s\n", buffer); |
|
0 | 368 |
|
369 |
return (0); |
|
370 |
} |
|
371 |
||
3988 | 372 |
/* |
373 |
* walk current thread stack, writing symbolic stack trace to specified fd |
|
374 |
*/ |
|
375 |
||
0 | 376 |
int |
377 |
printstack(int dofd) |
|
378 |
{ |
|
379 |
ucontext_t u; |
|
380 |
||
381 |
if (getcontext(&u) < 0) |
|
382 |
return (-1); |
|
383 |
||
384 |
return (walkcontext(&u, display_stack_info, (void*)(intptr_t)dofd)); |
|
385 |
} |
|
3988 | 386 |
|
387 |
/* |
|
388 |
* Some routines for better opensource compatibility w/ glibc. |
|
389 |
*/ |
|
390 |
||
391 |
typedef struct backtrace { |
|
392 |
void **bt_buffer; |
|
393 |
int bt_maxcount; |
|
394 |
int bt_actcount; |
|
395 |
} backtrace_t; |
|
396 |
||
397 |
/* ARGSUSED */ |
|
398 |
static int |
|
399 |
callback(uintptr_t pc, int signo, void *arg) |
|
400 |
{ |
|
401 |
backtrace_t *bt = (backtrace_t *)arg; |
|
402 |
||
403 |
if (bt->bt_actcount >= bt->bt_maxcount) |
|
404 |
return (-1); |
|
405 |
||
406 |
bt->bt_buffer[bt->bt_actcount++] = (void *)pc; |
|
407 |
||
408 |
return (0); |
|
409 |
} |
|
410 |
||
411 |
/* |
|
412 |
* dump stack trace up to length count into buffer |
|
413 |
*/ |
|
414 |
||
415 |
int |
|
416 |
backtrace(void **buffer, int count) |
|
417 |
{ |
|
418 |
backtrace_t bt; |
|
419 |
ucontext_t u; |
|
420 |
||
421 |
bt.bt_buffer = buffer; |
|
422 |
bt.bt_maxcount = count; |
|
423 |
bt.bt_actcount = 0; |
|
424 |
||
425 |
if (getcontext(&u) < 0) |
|
426 |
return (0); |
|
427 |
||
428 |
(void) walkcontext(&u, callback, &bt); |
|
429 |
||
430 |
return (bt.bt_actcount); |
|
431 |
} |
|
432 |
||
433 |
/* |
|
434 |
* format backtrace string |
|
435 |
*/ |
|
436 |
||
437 |
int |
|
438 |
addrtosymstr(void *pc, char *buffer, int size) |
|
439 |
{ |
|
440 |
Dl_info info; |
|
441 |
Sym *sym; |
|
442 |
||
443 |
if (dladdr1(pc, &info, (void **)&sym, |
|
444 |
RTLD_DL_SYMENT) == 0) { |
|
445 |
return (snprintf(buffer, size, "[0x%p]", pc)); |
|
446 |
} |
|
447 |
||
448 |
if ((info.dli_fname != NULL && info.dli_sname != NULL) && |
|
449 |
((uintptr_t)pc - (uintptr_t)info.dli_saddr < sym->st_size)) { |
|
450 |
/* |
|
451 |
* we have containing symbol info |
|
452 |
*/ |
|
453 |
return (snprintf(buffer, size, "%s'%s+0x%x [0x%p]", |
|
454 |
info.dli_fname, |
|
455 |
info.dli_sname, |
|
456 |
(unsigned long)pc - (unsigned long)info.dli_saddr, |
|
457 |
pc)); |
|
458 |
} else { |
|
459 |
/* |
|
460 |
* no local symbol info |
|
461 |
*/ |
|
462 |
return (snprintf(buffer, size, "%s'0x%p [0x%p]", |
|
463 |
info.dli_fname, |
|
464 |
(unsigned long)pc - (unsigned long)info.dli_fbase, |
|
465 |
pc)); |
|
466 |
} |
|
467 |
} |
|
468 |
||
469 |
/* |
|
470 |
* This function returns the symbolic representation of stack trace; calls |
|
471 |
* malloc so it is NOT async safe! A rather mis-designed and certainly misused |
|
472 |
* interface. |
|
473 |
*/ |
|
474 |
||
475 |
char ** |
|
476 |
backtrace_symbols(void *const *array, int size) |
|
477 |
{ |
|
478 |
int bufferlen, len; |
|
479 |
char **ret_buffer; |
|
480 |
char **ret; |
|
481 |
char linebuffer[MAX_LINE]; |
|
482 |
int i; |
|
483 |
||
484 |
bufferlen = size * sizeof (char *); |
|
485 |
||
486 |
/* |
|
487 |
* tmp buffer to hold strings while finding all symbol names |
|
488 |
*/ |
|
489 |
||
490 |
ret_buffer = (char **)alloca(bufferlen); |
|
491 |
||
492 |
for (i = 0; i < size; i++) { |
|
493 |
(void) addrtosymstr(array[i], linebuffer, sizeof (linebuffer)); |
|
494 |
ret_buffer[i] = strcpy(alloca(len = strlen(linebuffer) + 1), |
|
495 |
linebuffer); |
|
496 |
bufferlen += len; |
|
497 |
} |
|
498 |
||
499 |
/* |
|
500 |
* allocate total amount of storage required and copy strings |
|
501 |
*/ |
|
502 |
||
503 |
if ((ret = (char **)malloc(bufferlen)) == NULL) |
|
504 |
return (NULL); |
|
505 |
||
506 |
||
507 |
for (len = i = 0; i < size; i++) { |
|
508 |
ret[i] = (char *)ret + size * sizeof (char *) + len; |
|
11411
c2fe1bf96826
6894056 libc is not clean
Surya Prakki <Surya.Prakki@Sun.COM>
parents:
6812
diff
changeset
|
509 |
(void) strcpy(ret[i], ret_buffer[i]); |
3988 | 510 |
len += strlen(ret_buffer[i]) + 1; |
511 |
} |
|
512 |
||
513 |
return (ret); |
|
514 |
} |
|
515 |
||
516 |
/* |
|
517 |
* Write out symbolic stack trace in an async-safe way. |
|
518 |
*/ |
|
519 |
||
520 |
void |
|
521 |
backtrace_symbols_fd(void *const *array, int size, int fd) |
|
522 |
{ |
|
523 |
char linebuffer[MAX_LINE]; |
|
524 |
int i; |
|
525 |
int len; |
|
526 |
||
527 |
for (i = 0; i < size; i++) { |
|
528 |
len = addrtosymstr(array[i], linebuffer, |
|
529 |
sizeof (linebuffer) - 1); |
|
530 |
if (len >= sizeof (linebuffer)) |
|
531 |
len = sizeof (linebuffer) - 1; |
|
532 |
linebuffer[len] = '\n'; |
|
533 |
(void) write(fd, linebuffer, len + 1); |
|
534 |
} |
|
535 |
} |