Mercurial > illumos > illumos-gate
changeset 3988:2365e71eafb7
PSARC/2007/162 Backtrace() and friends for Solaris
6536146 libc should provide glibc-compatible backtrace() functions
author | barts |
---|---|
date | Fri, 06 Apr 2007 16:29:03 -0700 |
parents | 285470c0ea5c |
children | b9e764d3ce4e |
files | usr/src/head/Makefile usr/src/head/execinfo.h usr/src/head/ucontext.h usr/src/lib/libc/port/gen/walkstack.c usr/src/lib/libc/port/mapfile-vers usr/src/pkgdefs/SUNWhea/prototype_com |
diffstat | 6 files changed, 248 insertions(+), 158 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/head/Makefile Fri Apr 06 16:17:21 2007 -0700 +++ b/usr/src/head/Makefile Fri Apr 06 16:29:03 2007 -0700 @@ -65,6 +65,7 @@ euc.h \ exacct.h \ exacct_impl.h \ + execinfo.h \ fatal.h \ fcntl.h \ float.h \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/head/execinfo.h Fri Apr 06 16:29:03 2007 -0700 @@ -0,0 +1,54 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _EXECINFO_H +#define _EXECINFO_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * These functions provide glibc-compatible backtrace functionality. + * Improved functionality is available using Solaris-specific APIs; + * see man page for walkcontext(), printstack() and addtosymstr(). + */ +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__STDC__) +extern int backtrace(void **, int); +extern char **backtrace_symbols(void *const *, int); +extern void backtrace_symbols_fd(void *const *, int, int); +#else +extern int backtrace(); +extern char **backtrace_symbols(); +extern void backtrace_symbols_fd(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _EXECINFO_H */
--- a/usr/src/head/ucontext.h Fri Apr 06 16:17:21 2007 -0700 +++ b/usr/src/head/ucontext.h Fri Apr 06 16:29:03 2007 -0700 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -24,7 +23,7 @@ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,6 +36,7 @@ #if !defined(_XPG4_2) || defined(__EXTENSIONS__) #include <sys/siginfo.h> +#include <execinfo.h> #endif #ifdef __cplusplus @@ -63,7 +63,7 @@ extern int walkcontext(const ucontext_t *, int (*)(uintptr_t, int, void *), void *); extern int printstack(int); - +extern int addrtosymstr(void *, char *, int); extern int getustack(stack_t **); extern int setustack(stack_t *); @@ -84,7 +84,7 @@ #if !defined(_XPG4_2) || defined(__EXTENSIONS__) extern int walkcontext(); extern int printstack(); - +extern int addrtosymstr(); extern int getustack(); extern int setustack();
--- a/usr/src/lib/libc/port/gen/walkstack.c Fri Apr 06 16:17:21 2007 -0700 +++ b/usr/src/lib/libc/port/gen/walkstack.c Fri Apr 06 16:29:03 2007 -0700 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -19,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -105,8 +104,8 @@ * */ -#pragma weak walkcontext = _walkcontext -#pragma weak printstack = _printstack +#pragma weak walkcontext = _walkcontext +#pragma weak printstack = _printstack #include "synonyms.h" #include <assert.h> @@ -129,6 +128,7 @@ #include <stdio.h> #include <alloca.h> #include <limits.h> +#include <stdlib.h> #ifdef _LP64 #define _ELF64 @@ -160,6 +160,7 @@ #error no arch defined #endif +#define MAX_LINE 2048 /* arbitrary large value */ /* * use /proc/self/as to safely dereference pointers so we don't @@ -347,179 +348,58 @@ return (0); } -static size_t -ulongtos(char *buffer, unsigned long x, int base) -{ - char local[80]; - static const char digits[] = "0123456789abcdef"; - - unsigned int n = sizeof (local) - 1; - unsigned long rem; - unsigned int mod; - - local[n] = 0; - - rem = x; - - do { - switch (base) { - case 10: - mod = rem % 10; - rem = rem / 10; - break; - - case 16: - mod = rem & 15; - rem = rem >> 4; - break; - default: - return (0); - } - local[--n] = digits[mod]; - } while (rem != 0); - - (void) strcpy(buffer, local + n); - - return (sizeof (local) - n - 1); -} +/* + * async safe version of fprintf + */ static void async_filenoprintf(int filenum, const char *format, ...) { - const char *src = format; va_list ap; - long i; - struct iovec *iov; - int cnt; - int iter = 0; - - /* - * count # of %'s.. max # of iovs is 2n + 1 - */ - - for (cnt = i = 0; src[i] != '\0'; i++) - if (src[i] == '%') - cnt++; - - iov = alloca((2 * cnt + 1) * sizeof (struct iovec)); + char buffer[MAX_LINE]; va_start(ap, format); - - - while (*src) { - - iov[iter].iov_base = (char *)src; - iov[iter].iov_len = 0; - - while (*src && *src != '%') { - iov[iter].iov_len++; - src++; - } - - if (iov[iter].iov_len != 0) - iter++; - - if (*src == '%') { - switch (*++src) { - case 's': - iov[iter].iov_base = va_arg(ap, char *); - iov[iter].iov_len = strlen(iov[iter].iov_base); - iter++; - break; - case 'd': - iov[iter].iov_base = alloca(24); - - i = va_arg(ap, long); - if (i < 0) { - *iov[iter].iov_base = '-'; - iov[iter].iov_len = - ulongtos(iov[iter].iov_base + 1, - -i, 10) + 1; - } else - iov[iter].iov_len = - ulongtos(iov[iter].iov_base, - i, 10); - iter++; - break; - case 'x': - iov[iter].iov_base = alloca(24); - iov[iter].iov_len = ulongtos(iov[iter].iov_base, - va_arg(ap, unsigned long), 16); - iter++; - break; - - case '%': - iov[iter].iov_base = (char *)src; - iov[iter].iov_len = 1; - iter++; - break; - } - src++; - } - } + (void) vsnprintf(buffer, sizeof (buffer), format, ap); va_end(ap); - (void) writev(filenum, iov, iter); + (void) write(filenum, buffer, strlen(buffer)); } +/* + * print out stack frame info + */ + static int display_stack_info(uintptr_t pc, int signo, void *arg) { - Dl_info info; + char buffer[MAX_LINE]; char sigbuf[SIG2STR_MAX]; - Sym *sym; int filenum = (intptr_t)arg; - if (signo) { - if (sig2str(signo, sigbuf) != 0) - (void) strcpy(sigbuf, "?"); - } + (void) addrtosymstr((void *)pc, buffer, sizeof (buffer)); - if (dladdr1((void *) pc, &info, (void**) &sym, RTLD_DL_SYMENT) == 0) { - /* no info at all */ - if (signo == 0) - async_filenoprintf(filenum, "0x%x\n", pc); - else - async_filenoprintf(filenum, - "0x%x [ Signal %d (%s)]\n", pc, - (ulong_t)signo, sigbuf); + if (signo) { + sigbuf[0] = '?'; + sigbuf[1] = 0; - } else if ((pc - (unsigned long)info.dli_saddr) < - sym->st_size) { - /* found a global symbol */ - if (signo == 0) - async_filenoprintf(filenum, "%s:%s+0x%x\n", - info.dli_fname, - info.dli_sname, - pc - (unsigned long)info.dli_saddr); - else - async_filenoprintf(filenum, - "%s:%s+0x%x [ Signal %d (%s)]\n", - info.dli_fname, - info.dli_sname, - pc - (unsigned long)info.dli_saddr, - (ulong_t)signo, sigbuf); - } else { - /* found a static symbol */ - if (signo == 0) - async_filenoprintf(filenum, "%s:0x%x\n", - info.dli_fname, - pc - (unsigned long)info.dli_fbase); - else - async_filenoprintf(filenum, - "%s:0x%x [ Signal %d (%s)]\n", - info.dli_fname, - pc - (unsigned long)info.dli_fbase, - (ulong_t)signo, sigbuf); - } + (void) sig2str(signo, sigbuf); + + async_filenoprintf(filenum, "%s [Signal %d (%s)]\n", + buffer, (ulong_t)signo, sigbuf); + } else + async_filenoprintf(filenum, "%s\n", buffer); return (0); } +/* + * walk current thread stack, writing symbolic stack trace to specified fd + */ + int printstack(int dofd) { @@ -530,3 +410,153 @@ return (walkcontext(&u, display_stack_info, (void*)(intptr_t)dofd)); } + +/* + * Some routines for better opensource compatibility w/ glibc. + */ + +typedef struct backtrace { + void **bt_buffer; + int bt_maxcount; + int bt_actcount; +} backtrace_t; + +/* ARGSUSED */ +static int +callback(uintptr_t pc, int signo, void *arg) +{ + backtrace_t *bt = (backtrace_t *)arg; + + if (bt->bt_actcount >= bt->bt_maxcount) + return (-1); + + bt->bt_buffer[bt->bt_actcount++] = (void *)pc; + + return (0); +} + +/* + * dump stack trace up to length count into buffer + */ + +int +backtrace(void **buffer, int count) +{ + backtrace_t bt; + ucontext_t u; + + bt.bt_buffer = buffer; + bt.bt_maxcount = count; + bt.bt_actcount = 0; + + if (getcontext(&u) < 0) + return (0); + + (void) walkcontext(&u, callback, &bt); + + return (bt.bt_actcount); +} + +/* + * format backtrace string + */ + +int +addrtosymstr(void *pc, char *buffer, int size) +{ + Dl_info info; + Sym *sym; + + if (dladdr1(pc, &info, (void **)&sym, + RTLD_DL_SYMENT) == 0) { + return (snprintf(buffer, size, "[0x%p]", pc)); + } + + if ((info.dli_fname != NULL && info.dli_sname != NULL) && + ((uintptr_t)pc - (uintptr_t)info.dli_saddr < sym->st_size)) { + /* + * we have containing symbol info + */ + return (snprintf(buffer, size, "%s'%s+0x%x [0x%p]", + info.dli_fname, + info.dli_sname, + (unsigned long)pc - (unsigned long)info.dli_saddr, + pc)); + } else { + /* + * no local symbol info + */ + return (snprintf(buffer, size, "%s'0x%p [0x%p]", + info.dli_fname, + (unsigned long)pc - (unsigned long)info.dli_fbase, + pc)); + } +} + +/* + * This function returns the symbolic representation of stack trace; calls + * malloc so it is NOT async safe! A rather mis-designed and certainly misused + * interface. + */ + +char ** +backtrace_symbols(void *const *array, int size) +{ + int bufferlen, len; + char **ret_buffer; + char **ret; + char linebuffer[MAX_LINE]; + int i; + + bufferlen = size * sizeof (char *); + + /* + * tmp buffer to hold strings while finding all symbol names + */ + + ret_buffer = (char **)alloca(bufferlen); + + for (i = 0; i < size; i++) { + (void) addrtosymstr(array[i], linebuffer, sizeof (linebuffer)); + ret_buffer[i] = strcpy(alloca(len = strlen(linebuffer) + 1), + linebuffer); + bufferlen += len; + } + + /* + * allocate total amount of storage required and copy strings + */ + + if ((ret = (char **)malloc(bufferlen)) == NULL) + return (NULL); + + + for (len = i = 0; i < size; i++) { + ret[i] = (char *)ret + size * sizeof (char *) + len; + strcpy(ret[i], ret_buffer[i]); + len += strlen(ret_buffer[i]) + 1; + } + + return (ret); +} + +/* + * Write out symbolic stack trace in an async-safe way. + */ + +void +backtrace_symbols_fd(void *const *array, int size, int fd) +{ + char linebuffer[MAX_LINE]; + int i; + int len; + + for (i = 0; i < size; i++) { + len = addrtosymstr(array[i], linebuffer, + sizeof (linebuffer) - 1); + if (len >= sizeof (linebuffer)) + len = sizeof (linebuffer) - 1; + linebuffer[len] = '\n'; + (void) write(fd, linebuffer, len + 1); + } +}
--- a/usr/src/lib/libc/port/mapfile-vers Fri Apr 06 16:17:21 2007 -0700 +++ b/usr/src/lib/libc/port/mapfile-vers Fri Apr 06 16:29:03 2007 -0700 @@ -27,6 +27,7 @@ SUNW_1.23 { # SunOS 5.11 (Solaris 11) global: + addrtosymstr; aio_cancel; aiocancel; aio_error; @@ -40,6 +41,9 @@ aio_write; aiowrite; assfail; + backtrace; + backtrace_symbols; + backtrace_symbols_fd; clock_getres; clock_gettime; clock_nanosleep;
--- a/usr/src/pkgdefs/SUNWhea/prototype_com Fri Apr 06 16:17:21 2007 -0700 +++ b/usr/src/pkgdefs/SUNWhea/prototype_com Fri Apr 06 16:29:03 2007 -0700 @@ -111,6 +111,7 @@ f none usr/include/exacct.h 644 root bin f none usr/include/exacct_impl.h 644 root bin f none usr/include/exec_attr.h 644 root bin +f none usr/include/execinfo.h 644 root bin f none usr/include/fatal.h 644 root bin f none usr/include/fcntl.h 644 root bin f none usr/include/float.h 644 root bin