usr/src/lib/libc/port/gen/walkstack.c
changeset 0 68f95e015346
child 3988 2365e71eafb7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libc/port/gen/walkstack.c	Tue Jun 14 00:00:00 2005 -0700
@@ -0,0 +1,532 @@
+/*
+ * 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.
+ *
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * This file provides a general purpose mechanism
+ * for a user thread to walk its own call stack,
+ * calling a user-specified iterator function for each
+ * stack frame.  Special handling is provided to indicate
+ * kernel-constructed signal handler frames.
+ *
+ * Adapted from usr/src/lib/libproc/common/Pstack.c:
+ *
+ * A signal handler frame is essentially a set of data pushed on to the user
+ * stack by the kernel prior to returning to the user program in one of the
+ * pre-defined signal handlers.  The signal handler itself receives the signal
+ * number, an optional pointer to a siginfo_t, and a pointer to the interrupted
+ * ucontext as arguments.
+ *
+ * When performing a stack backtrace, we would like to
+ * detect these frames so that we can correctly return the interrupted program
+ * counter and frame pointer as a separate frame.
+ *
+ * The stack layout for a signal handler frame is as follows:
+ *
+ * SPARC v7/v9:                           Intel ia32:
+ * +--------------+ -        high         +--------------+ -
+ * |  struct fq   | ^        addrs        |  siginfo_t   | optional
+ * +--------------+ |          ^          +--------------+ -
+ * |  gwindows_t  |            |          |  ucontext_t  | ^
+ * +--------------+ optional              +--------------+ |
+ * |  siginfo_t   |                       | ucontext_t * | |
+ * +--------------+ |          |          +--------------+
+ * |  xregs data  | v          v          |  siginfo_t * | mandatory
+ * +--------------+ -         low         +--------------+
+ * |  ucontext_t  | ^        addrs        |  int (signo) | |
+ * +--------------+ mandatory             +--------------+ |
+ * | struct frame | v                     | struct frame | v
+ * +--------------+ - <- %sp on resume    +--------------+ - <- %esp on resume
+ *
+ * amd64 (64-bit)
+ * +--------------+ -
+ * |  siginfo_t   | optional
+ * +--------------+ -
+ * |  ucontext_t  | ^
+ * +--------------+ |
+ * |  siginfo_t * |
+ * +--------------+ mandatory
+ * |  int (signo) |
+ * +--------------+ |
+ * | struct frame | v
+ * +--------------+ - <- %rsp on resume
+ *
+ * The bottom-most struct frame is actually constructed by the kernel by
+ * copying the previous stack frame, allowing naive backtrace code to simply
+ * skip over the interrupted frame.  The copied frame is never really used,
+ * since it is presumed the libc or libthread signal handler wrapper function
+ * will explicitly setcontext(2) to the interrupted context if the user
+ * program's handler returns.  If we detect a signal handler frame, we simply
+ * read the interrupted context structure from the stack, use its embedded
+ * gregs to construct the register set for the interrupted frame, and then
+ * continue our backtrace.  Detecting the frame itself is easy according to
+ * the diagram ("oldcontext" represents any element in the uc_link chain):
+ *
+ * On SPARC v7 or v9:
+ * %fp + sizeof (struct frame) == oldcontext
+ *
+ * On i386:
+ * %ebp + sizeof (struct frame) + (3 words) == oldcontext
+ *
+ * On amd64:
+ * %rbp + sizeof (struct frame) + (2 words) == oldcontext
+ *
+ * Since we want to provide the signal number that generated a signal stack
+ * frame and on sparc this information isn't written to the stack by the kernel
+ * the way it's done on i386, we're forced to read the signo from the stack as
+ * one of the arguments to the signal handler.  What we hope is that no one has
+ * used __sigaction directly; if we're not linked with libthread
+ * (_thr_sighndlrinfo is NULL) then we attempt to read the signo directly from
+ * the register window. Otherwise we use the _thr_sighndlrinfo interface to find
+ * the correct frame.
+ *
+ */
+
+#pragma weak walkcontext = _walkcontext
+#pragma weak printstack = _printstack
+
+#include "synonyms.h"
+#include <assert.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <link.h>
+#include <procfs.h>
+#include <strings.h>
+#include <signal.h>
+#include <sys/frame.h>
+#include <sys/regset.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <thread.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/stack.h>
+#include <errno.h>
+#include <stdio.h>
+#include <alloca.h>
+#include <limits.h>
+
+#ifdef _LP64
+#define	_ELF64
+#endif
+
+#include <sys/machelf.h>
+
+
+#if defined(__sparc)
+#define	FRAME_PTR_REGISTER REG_SP
+#define	PC_REGISTER REG_PC
+#define	CHECK_FOR_SIGFRAME(fp, oldctx) ((fp) + SA(sizeof (struct frame)) \
+	== (oldctx))
+
+#elif defined(__amd64)
+#define	FRAME_PTR_REGISTER	REG_RBP
+#define	PC_REGISTER		REG_RIP
+#define	CHECK_FOR_SIGFRAME(fp, oldctx) ((((fp) + sizeof (struct frame)) + \
+	2 * sizeof (long) == (oldctx)) && \
+	(((struct frame *)fp)->fr_savpc == (greg_t)-1))
+
+#elif defined(__i386)
+#define	FRAME_PTR_REGISTER EBP
+#define	PC_REGISTER EIP
+#define	CHECK_FOR_SIGFRAME(fp, oldctx) ((((fp) + sizeof (struct frame)) + \
+	3 * sizeof (int) == (oldctx)) && \
+	(((struct frame *)fp)->fr_savpc == (greg_t)-1))
+#else
+#error no arch defined
+#endif
+
+
+/*
+ * use /proc/self/as to safely dereference pointers so we don't
+ * die in the case of a stack smash
+ */
+
+static int
+read_safe(int fd, struct frame *fp, struct frame **savefp, uintptr_t *savepc)
+{
+
+	uintptr_t newfp;
+
+	if ((uintptr_t)fp & (sizeof (void *) - 1))
+		return (-1); /* misaligned */
+
+	if ((pread(fd, (void *)&newfp, sizeof (fp->fr_savfp),
+	    (off_t)&fp->fr_savfp) != sizeof (fp->fr_savfp)) ||
+	    pread(fd, (void *)savepc, sizeof (fp->fr_savpc),
+	    (off_t)&fp->fr_savpc) != sizeof (fp->fr_savpc))
+		return (-1);
+
+	/*
+	 * handle stack bias on sparcv9
+	 */
+
+	if (newfp != 0)
+		newfp += STACK_BIAS;
+
+	*savefp = (struct frame *)newfp;
+
+	return (0);
+}
+
+int
+walkcontext(const ucontext_t *uptr, int (*operate_func)(uintptr_t, int, void *),
+    void *usrarg)
+{
+	ucontext_t *oldctx = uptr->uc_link;
+
+	int	fd;
+	int 	sig;
+#if defined(__sparc)
+	int 	signo = 0;
+#endif
+
+	struct frame *savefp;
+	uintptr_t savepc;
+
+	/*
+	 * snag frame point from ucontext... we'll see caller of
+	 * getucontext since we'll start by working up the call
+	 * stack by one
+	 */
+
+	struct frame *fp = (struct frame *)
+	    ((uintptr_t)uptr->uc_mcontext.gregs[FRAME_PTR_REGISTER] +
+	    STACK_BIAS);
+
+	/*
+	 * Since we don't write signo to the stack on sparc, we need
+	 * to extract signo from the stack frames.  This is problematic
+	 * in the case of libthread (libc has deterministic behavior)
+	 * since we're not sure where we can do that safely.  An awkward
+	 * interface was provided for this purpose in libthread:
+	 * _thr_sighndlrinfo; this is documented in
+	 * /shared/sac/PSARC/1999/024.  When called, this function
+	 * returns the PC of a special function (and its size) that
+	 * will be present in the stack frame if a signal was
+	 * delivered and will have the following signature
+	 * __sighndlr(int sig, siginfo_t *si, ucontex_t *uc,
+	 *	void (*hndlr)())
+	 * Since this function is written in assembler and doesn't
+	 * perturb its registers, we can then read sig out of arg0
+	 * when the saved pc is inside this function.
+	 *
+	 */
+#if defined(__sparc)
+
+	uintptr_t special_pc = NULL;
+	int special_size = 0;
+
+	extern void _thr_sighndlrinfo(void (**func)(), int *funcsize);
+
+#pragma weak _thr_sighndlrinfo
+
+	if (_thr_sighndlrinfo != NULL) {
+		_thr_sighndlrinfo((void (**)())&special_pc, &special_size);
+	}
+#endif /* sparc */
+
+
+	if ((fd = open("/proc/self/as", O_RDONLY)) < 0)
+		return (-1);
+
+	while (fp != NULL) {
+
+		sig = 0;
+
+		/*
+		 * get value of saved fp and pc w/o crashing
+		 */
+
+		if (read_safe(fd, fp, &savefp, &savepc) != 0) {
+			(void) close(fd);
+			return (-1);
+		}
+
+		if (savefp == NULL)
+			break;
+
+		/*
+		 * note that the following checks to see if we've got a
+		 * special signal stack frame present; this allows us to
+		 * detect signals and pass that info to the user stack walker
+		 */
+
+		if (oldctx != NULL &&
+		    CHECK_FOR_SIGFRAME((uintptr_t)savefp, (uintptr_t)oldctx)) {
+
+#if defined(__i386) || defined(__amd64)
+			/*
+			 * i386 and amd64 store signo on stack;
+			 * simple to detect and use
+			 */
+			sig = *((int *)(savefp + 1));
+#endif
+
+#if defined(__sparc)
+			/*
+			 * with sparc we need to handle
+			 * single and multi-threaded cases
+			 * separately
+			 * If we're single threaded, the trampoline
+			 * in libc will have the signo as the first
+			 * argumment; we can snag that directly.
+			 * In the case of threads, since there are multiple
+			 * complex routines between kernel and user handler,
+			 * we need to figure out where we can read signal from
+			 * using _thr_sighndlrinfo - which we've already done
+			 * for this signal, since it appeared on the stack
+			 * before the signal frame.... sigh.
+			 */
+
+			if (_thr_sighndlrinfo == NULL) /* single threaded */
+				sig = fp->fr_arg[0];
+			else
+				sig = signo; /* already read - see below */
+#endif
+			/*
+			 * this is the special signal frame, so cons up
+			 * the saved fp & pc to pass to user's function
+			 */
+
+			savefp = (struct frame *)
+			    ((uintptr_t)oldctx->
+			    uc_mcontext.gregs[FRAME_PTR_REGISTER] +
+			    STACK_BIAS);
+			savepc = oldctx->uc_mcontext.gregs[PC_REGISTER];
+
+			oldctx = oldctx->uc_link; /* handle nested signals */
+		}
+#if defined(__sparc)
+
+		/*
+		 * lookahead code to find right spot to read signo from...
+		 */
+
+		if (_thr_sighndlrinfo &&
+		    savepc >= special_pc && savepc <
+		    (special_pc + special_size))
+			signo = fp->fr_arg[0];
+#endif
+
+		/*
+		 * call user-supplied function and quit if non-zero return.
+		 */
+
+		if (operate_func((uintptr_t)savepc, sig, usrarg) != 0)
+			break;
+
+		fp = savefp; /* up one in the call stack */
+	}
+
+	(void) close(fd);
+	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);
+}
+
+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));
+
+	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++;
+		}
+	}
+	va_end(ap);
+
+	(void) writev(filenum, iov, iter);
+
+}
+
+static int
+display_stack_info(uintptr_t pc, int signo, void *arg)
+{
+	Dl_info info;
+
+	char sigbuf[SIG2STR_MAX];
+
+	Sym *sym;
+
+	int filenum = (intptr_t)arg;
+
+	if (signo) {
+		if (sig2str(signo, sigbuf) != 0)
+			(void) strcpy(sigbuf, "?");
+	}
+
+	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);
+
+	} 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);
+	}
+
+	return (0);
+}
+
+int
+printstack(int dofd)
+{
+	ucontext_t u;
+
+	if (getcontext(&u) < 0)
+		return (-1);
+
+	return (walkcontext(&u, display_stack_info, (void*)(intptr_t)dofd));
+}