diff -uprN -x '*~' -x '*.o' -x '*.orig' -x '*.rej' -x compile -x CVS -x .svn -I '\$FreeBSD' ./ChangeLog /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/ChangeLog --- ./ChangeLog 2002-11-25 09:43:25.000000000 -0500 +++ /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/ChangeLog 2008-05-19 14:15:53.000000000 -0400 @@ -33,3 +33,11 @@ Use "unique id" field in thread for thread-ids. More user-friendly than pthread_t pointers. + +1.2->1.3 + Abstract thread operations to allow for multiple thread handlers. + Add a thread handler that uses libthread_db.so to fetch thread + information for use with multithreaded applications in 6.x and later. + + Avoid displaying the current thread twice by using (IP, BP) as a unqiue + ID for threads. diff -uprN -x '*~' -x '*.o' -x '*.orig' -x '*.rej' -x compile -x CVS -x .svn -I '\$FreeBSD' ./Makefile /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/Makefile --- ./Makefile 2002-11-26 05:28:31.000000000 -0500 +++ /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/Makefile 2008-05-19 14:15:53.000000000 -0400 @@ -1,13 +1,21 @@ # $Id: Makefile,v 1.2 2002/11/26 10:28:31 pmedwards Exp $ PROG=pstack -SRCS=pstack.c elf.c +SRCS=pstack.c elf.c libc_r.c +VER!=uname -r | sed -e 's/\..*//' +.if ${VER} != "4" +SRCS+=thread_db.c +.endif PREFIX ?= /usr/local BINDIR ?= ${PREFIX}/bin MANDIR = ${PREFIX}/man/man -TARBALL_FILES = ChangeLog Makefile elf.c elfinfo.h pstack.1 pstack.c +# libthread_db.so calls back into gdb for the proc services. Make all the +# global symbols visible. +LDFLAGS+= -Wl,-E -VERSION ?= 1.2 +TARBALL_FILES = ChangeLog Makefile elf.c elfinfo.h pstack.1 pstack.c libc_r.c + +VERSION ?= 1.3 TARBALL = pstack-${VERSION}.tar.gz CLEANFILES += ${TARBALL} ${TARBALL}: @@ -20,5 +28,5 @@ ${TARBALL}: rm -rf pstack-${VERSION} tarball: ${TARBALL} - + .include diff -uprN -x '*~' -x '*.o' -x '*.orig' -x '*.rej' -x compile -x CVS -x .svn -I '\$FreeBSD' ./elf.c /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/elf.c --- ./elf.c 2008-12-11 23:40:38.000000000 -0500 +++ /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/elf.c 2008-05-19 14:15:53.000000000 -0400 @@ -49,6 +49,9 @@ #include "elfinfo.h" +#if __FreeBSD_version > 600000 +#define DT_COUNT DT_BIND_NOW +#endif static unsigned long elf_hash(const unsigned char *name); /* @@ -319,6 +322,47 @@ elfGetNote(struct ElfObject *obj, const } /* + * Fetch the next "note" after the note whose data is pointed to by "datap". + */ +int +elfGetNextNote(struct ElfObject *obj, const char *name, + u_int32_t type, const void **datap, int *lenp) +{ + const Elf_Phdr **phdr; + const Elf_Note *note; + const char *noteName, *data, *s, *e; + int found; + + found = 0; + for (phdr = obj->programHeaders; *phdr; phdr++) { + if ((*phdr)->p_type == PT_NOTE) { + s = obj->fileData + (*phdr)->p_offset; + e = s + (*phdr)->p_filesz; + while (s < e) { + note = (const Elf_Note *)s; + s += sizeof(*note); + noteName = s; + s += roundup2(note->n_namesz, 4); + data = s; + s += roundup2(note->n_descsz, 4); + if (!found) { + if (data == *datap) + found = 1; + continue; + } + if (strcmp(name, noteName) == 0 && + (note->n_type == type || type == -1)) { + *datap = data; + *lenp = note->n_descsz; + return (0); + } + } + } + } + return (-1); +} + +/* * Try to work out the name of the executable from a core file * XXX: This is not particularly useful, because the pathname appears to get * stripped. @@ -613,7 +657,7 @@ elfDumpDynamic(FILE *f, const Elf_Dyn *d }; fprintf(f, "%stag: %d (%s)\n", padding, dyn->d_tag, - dyn->d_tag >= 0 && dyn->d_tag <= DT_BIND_NOW ? + dyn->d_tag >= 0 && dyn->d_tag <= DT_COUNT ? tagNames[dyn->d_tag] : "(unknown)"); fprintf(f, "%sword/addr: %d (%x)\n", padding, dyn->d_un.d_val, dyn->d_un.d_val); diff -uprN -x '*~' -x '*.o' -x '*.orig' -x '*.rej' -x compile -x CVS -x .svn -I '\$FreeBSD' ./elfinfo.h /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/elfinfo.h --- ./elfinfo.h 2002-11-25 07:56:34.000000000 -0500 +++ /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/elfinfo.h 2008-05-19 14:15:53.000000000 -0400 @@ -142,6 +142,8 @@ int elfFindSymbolByName(struct ElfObject int elfLoadObject(const char *fileName, struct ElfObject **objp); int elfGetNote(struct ElfObject *obj, const char *name, u_int32_t type, const void **datap, int *lenp); +int elfGetNextNote(struct ElfObject *obj, const char *name, + u_int32_t type, const void **datap, int *lenp); int elfGetImageFromCore(struct ElfObject *obj, const char **name); int elfUnloadObject(struct ElfObject *obj); const char *elfGetAbiPrefix(struct ElfObject *o); diff -uprN -x '*~' -x '*.o' -x '*.orig' -x '*.rej' -x compile -x CVS -x .svn -I '\$FreeBSD' ./libc_r.c /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/libc_r.c --- ./libc_r.c 1969-12-31 19:00:00.000000000 -0500 +++ /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/libc_r.c 2008-05-19 14:15:53.000000000 -0400 @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2002 Peter Edwards + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * $Id: pstack.c,v 1.3 2002/11/26 10:28:31 pmedwards Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "elfinfo.h" +#include "pstack.h" + +/* + * XXX: extracted from src/lib/libc_r/uthread/pthread_private.h + */ +union ThreadContext { + jmp_buf jb; + sigjmp_buf sjb; + ucontext_t uc; +}; + +enum CtxType { + CTX_JB_NOSIG, CTX_JB, CTX_SJB, CTX_UC +}; + +/* ... end pthread_private.h stuff */ + +struct LibcInfo { + Elf_Addr threadList; + Elf_Addr threadRun; + int hasContextType; + int offThreadNext; + int offThreadState; + int offThreadName; + int offThreadCtxType; + int offThreadCtx; + int offUniqueId; +}; + +static int procGetLibcInfo(struct Process *proc, struct ElfObject *obj, + struct LibcInfo *lci); +static void libcr_startup(void); +static int libcr_probe(struct Process *proc); +static void libcr_free(struct Process *proc); +static void procReadLibcThreads(struct Process *proc); + +struct thread_ops libc_r_ops = { + libcr_startup, + libcr_probe, + procReadLibcThreads, + libcr_free +}; + +/* + * Try to find useful information from libc_r + */ +static int +procGetLibcInfo(struct Process *proc, struct ElfObject *obj, + struct LibcInfo *lci) +{ + + assert(lci->threadList == 0); + if (procReadVar(proc, obj, "_thread_list", &lci->threadList) != 0) + return (0); + /* This appears to be libc_r: get the rest of the details we want */ + procReadVar(proc, obj, "_thread_run", &lci->threadRun); + procReadVar(proc, obj, "_thread_state_offset", &lci->offThreadState); + procReadVar(proc, obj, "_thread_name_offset", &lci->offThreadName); + procReadVar(proc, obj, "_thread_next_offset", &lci->offThreadNext); + procReadVar(proc, obj, "_thread_uniqueid_offset", &lci->offUniqueId); + lci->hasContextType = procReadVar(proc, obj, "_thread_ctxtype_offset", + &lci->offThreadCtxType) != -1; + if (gVerbose > 1 && lci->hasContextType == 0) + warnx("post 4.7 threads library"); + procReadVar(proc, obj, "_thread_ctx_offset", &lci->offThreadCtx); + return (1); +} + +static void +libcr_startup(void) +{ +} + +static int +libcr_probe(struct Process *proc) +{ + struct ElfObject *obj; + struct LibcInfo *lci; + + lci = calloc(1, sizeof(struct LibcInfo)); + + /* Check each object file to see if it is libc_r. */ + for (obj = proc->objectList; obj != NULL; obj = obj->next) { + if (procGetLibcInfo(proc, obj, lci)) { + proc->threadInfo = lci; + return (1); + } + } + free(lci); + return (0); +} + +/* + * Grovel through libc_r's internals to find any threads. + */ +static void +procReadLibcThreads(struct Process *proc) +{ + struct Thread *t; + Elf_Addr ip, bp, thrPtr, id; + struct LibcInfo *libc = proc->threadInfo; + union ThreadContext ctx; + enum CtxType ctxType; + + for (thrPtr = libc->threadList; thrPtr; ) { + /* + * We've already read the currently running thread from the + * machine registers: If we see that thread on the _thread_list, + * we ignore it. + */ + /* + * If the threads library has a concept of a "context + * type", (4.x, where x <= 7), we need to read it to + * decide how to unwind, otherwise, we default to using + * the jump buffer. + */ + if (libc->hasContextType) { + if (procReadMem(proc, &ctxType, + thrPtr + libc->offThreadCtxType, + sizeof(ctxType)) != sizeof(ctxType)) { + warnx("cannot read context type for " + "thread %p", thrPtr); + goto next; + } + } else { + ctxType = CTX_JB; + } + if (procReadMem(proc, &ctx, thrPtr + + libc->offThreadCtx, sizeof(ctx)) != sizeof(ctx)) { + warnx("cannot read context for thread %p", + thrPtr); + goto next; + } + switch (ctxType) { + case CTX_JB_NOSIG: + case CTX_JB: + ip = (Elf_Addr)ctx.jb[0]._jb[0]; + bp = (Elf_Addr)ctx.jb[0]._jb[3]; + break; + case CTX_SJB: + ip = (Elf_Addr)ctx.sjb[0]._sjb[0]; + bp = (Elf_Addr)ctx.sjb[0]._sjb[3]; + break; + case CTX_UC: + ip = (Elf_Addr)ctx.uc.uc_mcontext.mc_eip; + bp = (Elf_Addr)ctx.uc.uc_mcontext.mc_ebp; + break; + default: + /* Don't know enough about thread to trace */ + warnx("cannot get frame for thread %p", thrPtr); + goto next; + } + if ((t = procReadThread(proc, bp, ip)) != NULL) { + procReadMem(proc, &id, thrPtr + libc->offUniqueId, + sizeof(id)); + t->id = id; + if (thrPtr == libc->threadRun) + t->running = 1; + } +next: + if (procReadMem(proc, &thrPtr, thrPtr + libc->offThreadNext, + sizeof(thrPtr)) != sizeof thrPtr) { + warnx("failed to read more threads"); + break; + } + } +} + +static void +libcr_free(struct Process *proc) +{ + + free(proc->threadInfo); + proc->threadInfo = NULL; +} diff -uprN -x '*~' -x '*.o' -x '*.orig' -x '*.rej' -x compile -x CVS -x .svn -I '\$FreeBSD' ./pstack.c /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/pstack.c --- ./pstack.c 2002-11-26 05:28:31.000000000 -0500 +++ /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/pstack.c 2008-05-19 14:15:53.000000000 -0400 @@ -45,17 +45,13 @@ #include #include #include -#include #include -#include +#include #include -#include #include #include #include -#include -#include #include #include #include @@ -63,77 +59,7 @@ #include #include "elfinfo.h" - -/* - * XXX: extracted from src/lib/libc_r/uthread/pthread_private.h - */ -union ThreadContext { - jmp_buf jb; - sigjmp_buf sjb; - ucontext_t uc; -}; - -enum CtxType { - CTX_JB_NOSIG, CTX_JB, CTX_SJB, CTX_UC -}; - -/* ... end pthread_private.h stuff */ - -struct StackFrame { - STAILQ_ENTRY(StackFrame) link; - Elf_Addr ip; - Elf_Addr bp; - int argCount; - Elf_Word args[1]; -}; - -STAILQ_HEAD(StackFrameList, StackFrame); - -struct Thread { - int running; - struct Thread *next; - struct StackFrameList stack; - Elf_Addr id; -}; - -struct LibcInfo { - Elf_Addr threadList; - Elf_Addr threadRun; - int offThreadNext; - int offThreadState; - int offThreadName; - int offThreadCtxType; - int offThreadCtx; - int offUniqueId; -}; - -struct MappedPage { - const char *data; - Elf_Addr address; /* Valid only if data != NULL */ - int lastAccess; -}; - -#define PAGECACHE_SIZE 4 - -struct PageCache { - struct MappedPage pages[PAGECACHE_SIZE]; - int accessGeneration; -}; - -struct Process { - pid_t pid; - int mem; /* File handle to /proc//mem */ - struct LibcInfo libcInfo; - int objectCount; - struct ElfObject *objectList; - struct ElfObject *execImage; - struct ElfObject *coreImage; - int threadCount; - int hasContextType; - struct Thread *threadList; - const char *abiPrefix; - struct PageCache pageCache; -}; +#include "pstack.h" /* * Command-line flags @@ -143,35 +69,35 @@ static int gNostop = 0; /* number of a static int gMaxFrames = 1024; /* max number of frames to read */ static int gDoTiming = 0; /* Report time process was stopped */ static int gShowObjectNames = 0; /* show names of objects for each IP */ -static int gVerbose = 0; +int gVerbose = 0; /* Amount of time process was suspended (if gDoTiming == 1) */ static struct timeval gSuspendTime; +static struct thread_ops *thread_ops[] = { +#if __FreeBSD_version >= 600000 + &thread_db_ops, +#endif + &libc_r_ops, + NULL +}; + static Elf_Addr procFindRDebugAddr(struct Process *proc); static int procOpen(pid_t pid, const char *exeName, const char *coreFile, struct Process **procp); -static int procReadMem(struct Process *proc, void *ptr, - Elf_Addr remoteAddr, size_t size); static int procFindObject(struct Process *proc, Elf_Addr addr, struct ElfObject **objp); static int procDumpStacks(FILE *file, struct Process *proc, int indent); static void procAddElfObject(struct Process *proc, struct ElfObject *obj, Elf_Addr base); -static int procReadVar(struct Process *proc, - struct ElfObject *obj, const char *name, int *value); -static void procGetLibcInfo(struct Process *proc, struct ElfObject *obj); static void procFree(struct Process *proc); static void procFreeThreads(struct Process *proc); static void procFreeObjects(struct Process *proc); static void procLoadSharedObjects(struct Process *proc); -static void procReadLibcThreads(struct Process *proc); static int procGetRegs(struct Process *proc, struct reg *reg); static int procSetupMem(struct Process *proc, pid_t pid, const char *core); static int usage(void); -static struct Thread *procReadThread(struct Process *proc, Elf_Addr bp, - Elf_Addr ip); int main(int argc, char **argv) @@ -182,6 +108,7 @@ main(int argc, char **argv) struct Process *proc; pid_t pid; struct ElfObject *dumpObj; + struct thread_ops **tdops; while ((c = getopt(argc, argv, "a:d:e:f:hnoOs:tv")) != -1) { switch (c) { @@ -233,6 +160,11 @@ main(int argc, char **argv) } if (optind == argc) return (usage()); + tdops = thread_ops; + while (*tdops) { + (*tdops)->startup(); + tdops++; + } for (err = 0, i = optind; i < argc; i++) { pid = atoi(argv[i]); if (pid == 0 || (kill(pid, 0) == -1 && errno == ESRCH)) { @@ -284,18 +216,15 @@ procOpen(pid_t pid, const char *exeName, char tmpBuf[PATH_MAX]; struct Process *proc; struct reg regs; - struct LibcInfo *libc; + struct thread_ops **tdops; proc = malloc(sizeof(*proc)); proc->objectList = NULL; proc->threadList = NULL; proc->objectCount = 0; proc->coreImage = NULL; - proc->mem = -1; proc->pid = -1; - libc = &proc->libcInfo; - libc->threadList = 0; - libc->threadRun = 0; + proc->threadOps = NULL; /* * Get access to the address space via /proc, or the core image */ @@ -377,10 +306,19 @@ procOpen(pid_t pid, const char *exeName, } } } - /* In case libc_r is statically linked */ - procGetLibcInfo(proc, proc->execImage); /* Attach any dynamically-linked libraries */ procLoadSharedObjects(proc); + + /* See if we have any threads. */ + tdops = thread_ops; + while (*tdops != NULL) { + if ((*tdops)->probe(proc)) { + proc->threadOps = *tdops; + break; + } + tdops++; + } + /* * Read the machine registers for the current stack and * instruction pointer @@ -393,8 +331,8 @@ procOpen(pid_t pid, const char *exeName, } /* If we know of more threads, trace those. */ - if (proc->libcInfo.threadList) - procReadLibcThreads(proc); + if (proc->threadOps) + proc->threadOps->read_threads(proc); if (pid != -1 && !gNostop) { /* Resume the process */ #ifndef KERN_35175_FIXED @@ -418,8 +356,6 @@ procOpen(pid_t pid, const char *exeName, gSuspendTime.tv_usec += 1000000; } } - close(proc->mem); - proc->mem = -1; } /* Success */ *procp = proc; @@ -427,11 +363,33 @@ procOpen(pid_t pid, const char *exeName, } /* + * Write data to the target's address space. + */ +size_t +procWriteMem(struct Process *proc, const void *ptr, Elf_Addr remoteAddr, + size_t size) +{ + struct ptrace_io_desc pio; + + if (proc->pid == -1) + return (0); + + pio.piod_op = PIOD_WRITE_D; + pio.piod_offs = (void *)remoteAddr; + pio.piod_addr = (void *)ptr; + pio.piod_len = size; + if (ptrace(PT_IO, proc->pid, (caddr_t)&pio, 0) < 0) + return (0); + return (pio.piod_len); +} + +/* * Read data from the target's address space. */ -static int +size_t procReadMem(struct Process *proc, void *ptr, Elf_Addr remoteAddr, size_t size) { + struct ptrace_io_desc pio; int rc; size_t fragSize, readLen; const Elf_Phdr **hdr; @@ -470,8 +428,12 @@ procReadMem(struct Process *proc, void * * Page not found: read entire page into * least-recently used cache slot */ - rc = pread(proc->mem, p, pagesize, pageLoc); - if (rc != pagesize) { + pio.piod_op = PIOD_READ_D; + pio.piod_offs = (void *)pageLoc; + pio.piod_addr = p; + pio.piod_len = pagesize; + if (ptrace(PT_IO, proc->pid, (caddr_t)&pio, 0) < 0 || + pio.piod_len != pagesize) { free(p); return (readLen); } @@ -519,7 +481,7 @@ procReadMem(struct Process *proc, void * * Given the current ip and bp registers, read each stack frame, and add a * thread structure to the "threadList" of the process. */ -static struct Thread * +struct Thread * procReadThread(struct Process *proc, Elf_Addr bp, Elf_Addr ip) { int frameCount, i; @@ -527,6 +489,13 @@ procReadThread(struct Process *proc, Elf const int frameSize = sizeof(*frame) + sizeof(Elf_Word) * gFrameArgs; struct Thread *thread; + /* Check to see if we have already seen this thread. */ + for (thread = proc->threadList; thread != NULL; thread = thread->next) { + frame = STAILQ_FIRST(&thread->stack); + if (frame->ip == ip && frame->bp == bp) + return (thread); + } + thread = malloc(sizeof(struct Thread)); thread->running = 0; STAILQ_INIT(&thread->stack); @@ -658,7 +627,7 @@ procAddElfObject(struct Process *proc, s /* * Read the value of the named symbol */ -static int +int procReadVar(struct Process *proc, struct ElfObject *obj, const char *name, int *value) { @@ -673,31 +642,6 @@ procReadVar(struct Process *proc, struct } /* - * Try to find useful information from libc_r - */ -static void -procGetLibcInfo(struct Process *proc, struct ElfObject *obj) -{ - struct LibcInfo *lci = &proc->libcInfo; - - if (lci->threadList != NULL) /* Already done? */ - return; - if (procReadVar(proc, obj, "_thread_list", &lci->threadList) != 0) - return; - /* This appears to be libc_r: get the rest of the details we want */ - procReadVar(proc, obj, "_thread_run", &lci->threadRun); - procReadVar(proc, obj, "_thread_state_offset", &lci->offThreadState); - procReadVar(proc, obj, "_thread_name_offset", &lci->offThreadName); - procReadVar(proc, obj, "_thread_next_offset", &lci->offThreadNext); - procReadVar(proc, obj, "_thread_uniqueid_offset", &lci->offUniqueId); - proc->hasContextType = procReadVar(proc, obj, "_thread_ctxtype_offset", - &lci->offThreadCtxType) != -1; - if (gVerbose > 1 && proc->hasContextType == 0) - warnx("post 4.7 threads library"); - procReadVar(proc, obj, "_thread_ctx_offset", &lci->offThreadCtx); -} - -/* * Grovel through the rtld's internals to find any shared libraries. */ static void @@ -758,83 +702,6 @@ procLoadSharedObjects(struct Process *pr if (!loaded) continue; procAddElfObject(proc, obj, lAddr); - procGetLibcInfo(proc, obj); - } -} - -/* - * Grovel through libc_r's internals to find any threads. - */ -static void -procReadLibcThreads(struct Process *proc) -{ - struct Thread *t; - Elf_Addr ip, bp, thrPtr; - struct LibcInfo *libc = &proc->libcInfo; - union ThreadContext ctx; - enum CtxType ctxType; - - for (thrPtr = libc->threadList; thrPtr; ) { - /* - * We've already read the currently running thread from the - * machine registers: If we see that thread on the _thread_list, - * we ignore it. - */ - /* - * If the threads library has a concept of a "context - * type", (4.x, where x <= 7), we need to read it to - * decide how to unwind, otherwise, we default to using - * the jump buffer. - */ - if (proc->hasContextType) { - if (procReadMem(proc, &ctxType, - thrPtr + libc->offThreadCtxType, - sizeof(ctxType)) != sizeof(ctxType)) { - warnx("cannot read context type for " - "thread %p", thrPtr); - goto next; - } - } else { - ctxType = CTX_JB; - } - if (procReadMem(proc, &ctx, thrPtr + - libc->offThreadCtx, sizeof(ctx)) != sizeof(ctx)) { - warnx("cannot read context for thread %p", - thrPtr); - goto next; - } - switch (ctxType) { - case CTX_JB_NOSIG: - case CTX_JB: - ip = (Elf_Addr)ctx.jb[0]._jb[0]; - bp = (Elf_Addr)ctx.jb[0]._jb[3]; - break; - case CTX_SJB: - ip = (Elf_Addr)ctx.sjb[0]._sjb[0]; - bp = (Elf_Addr)ctx.sjb[0]._sjb[3]; - break; - case CTX_UC: - ip = (Elf_Addr)ctx.uc.uc_mcontext.mc_eip; - bp = (Elf_Addr)ctx.uc.uc_mcontext.mc_ebp; - break; - default: - /* Don't know enough about thread to trace */ - warnx("cannot get frame for thread %p", thrPtr); - goto next; - } - if ((t = procReadThread(proc, bp, ip)) != NULL) { - procReadMem(proc, &proc->threadList->id, - thrPtr + libc->offUniqueId, - sizeof proc->threadList->id); - if (thrPtr == libc->threadRun) - t->running = 1; - } -next: - if (procReadMem(proc, &thrPtr, thrPtr + libc->offThreadNext, - sizeof(thrPtr)) != sizeof thrPtr) { - warnx("failed to read more threads"); - break; - } } } @@ -873,21 +740,12 @@ static int procGetRegs(struct Process *proc, struct reg *reg) { const prstatus_t *prstatus; - u_int32_t len; - char tmpBuf[64]; /* Used for /proc/%d/regs: 64 is more than sufficient */ - int fd, rc; + int len, rc; rc = -1; if (proc->pid != -1) { - /* Read from process (from /proc/pid/regs) */ - snprintf(tmpBuf, sizeof(tmpBuf), "/proc/%d/regs", proc->pid); - if ((fd = open(tmpBuf, O_RDONLY)) != -1) { - if ((len = read(fd, reg, sizeof *reg)) == sizeof *reg) - rc = 0; - else - warn("short read on registers"); - close(fd); - } + if (ptrace(PT_GETREGS, proc->pid, (void *)reg, 0) == 0) + rc = 0; } else { /* Read from core file. */ if (!elfGetNote(proc->coreImage, "FreeBSD", NT_PRSTATUS, @@ -905,24 +763,19 @@ procGetRegs(struct Process *proc, struct static int procSetupMem(struct Process *proc, pid_t pid, const char *core) { - char tmpBuf[64]; /* Used for /proc/%d/mem: 64 is more than sufficient */ int i; if (core) { if (!elfLoadObject(core, &proc->coreImage)) return (0); } else if (pid != -1) { - snprintf(tmpBuf, sizeof(tmpBuf), "/proc/%d/mem", pid); - if ((proc->mem = open(tmpBuf, O_RDONLY)) != -1) { - proc->pid = pid; - for (i = 0; i < PAGECACHE_SIZE; i++) { - proc->pageCache.pages[i].lastAccess = 0; - proc->pageCache.pages[i].data = NULL; - } - proc->pageCache.accessGeneration = 0; - return (0); + proc->pid = pid; + for (i = 0; i < PAGECACHE_SIZE; i++) { + proc->pageCache.pages[i].lastAccess = 0; + proc->pageCache.pages[i].data = NULL; } - warn("failed to open '%s'", tmpBuf); + proc->pageCache.accessGeneration = 0; + return (0); } else { warn("no core file or process id!"); } @@ -937,13 +790,14 @@ procFree(struct Process *proc) { size_t i; + if (proc->threadOps) + proc->threadOps->free(proc); procFreeObjects(proc); procFreeThreads(proc); - if (proc->mem != -1) { + if (proc->pid != -1) { for (i = 0; i < PAGECACHE_SIZE; i++) if (proc->pageCache.pages[i].data) free((char *)proc->pageCache.pages[i].data); - close(proc->mem); } if (proc->coreImage) elfUnloadObject(proc->coreImage); diff -uprN -x '*~' -x '*.o' -x '*.orig' -x '*.rej' -x compile -x CVS -x .svn -I '\$FreeBSD' ./pstack.h /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/pstack.h --- ./pstack.h 1969-12-31 19:00:00.000000000 -0500 +++ /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/pstack.h 2008-05-19 14:15:53.000000000 -0400 @@ -0,0 +1,74 @@ +/* $Yahoo: //depot/yahoo/ybsd_common/usr.local/pstack/pstack.h#1 $ */ + +#ifndef __PSTACK_H__ +#define __PSTACK_H__ + +struct StackFrame { + STAILQ_ENTRY(StackFrame) link; + Elf_Addr ip; + Elf_Addr bp; + int argCount; + Elf_Word args[1]; +}; + +STAILQ_HEAD(StackFrameList, StackFrame); + +struct Thread { + int running; + struct Thread *next; + struct StackFrameList stack; + int id; +}; + +struct MappedPage { + const char *data; + Elf_Addr address; /* Valid only if data != NULL */ + int lastAccess; +}; + +#define PAGECACHE_SIZE 4 + +struct PageCache { + struct MappedPage pages[PAGECACHE_SIZE]; + int accessGeneration; +}; + +struct thread_ops; + +struct Process { + pid_t pid; + void *threadInfo; + int objectCount; + struct ElfObject *objectList; + struct ElfObject *execImage; + struct ElfObject *coreImage; + int threadCount; + struct Thread *threadList; + const char *abiPrefix; + struct PageCache pageCache; + struct thread_ops *threadOps; +}; + +struct thread_ops { + void (*startup)(void); + int (*probe)(struct Process *); + void (*read_threads)(struct Process *); + void (*free)(struct Process *); +}; + +extern struct thread_ops libc_r_ops; +#if __FreeBSD_version >= 600000 +extern struct thread_ops thread_db_ops; +#endif +extern int gVerbose; + +size_t procReadMem(struct Process *proc, void *ptr, Elf_Addr remoteAddr, + size_t size); +int procReadVar(struct Process *proc, struct ElfObject *obj, + const char *name, int *value); +struct Thread *procReadThread(struct Process *proc, Elf_Addr bp, + Elf_Addr ip); +size_t procWriteMem(struct Process *proc, const void *ptr, Elf_Addr remoteAddr, + size_t size); + +#endif diff -uprN -x '*~' -x '*.o' -x '*.orig' -x '*.rej' -x compile -x CVS -x .svn -I '\$FreeBSD' ./thread_db.c /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/thread_db.c --- ./thread_db.c 1969-12-31 19:00:00.000000000 -0500 +++ /home/jhb/work/y/p4/ybsd_common/usr.local/pstack/thread_db.c 2008-05-19 14:15:53.000000000 -0400 @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2002 Peter Edwards + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * $Id: pstack.c,v 1.3 2002/11/26 10:28:31 pmedwards Exp $ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "elfinfo.h" +#include "pstack.h" + +#define LIBTHREAD_DB_SO "libthread_db.so" + +static void thread_db_startup(void); +static int thread_db_probe(struct Process *proc); +static void thread_db_free(struct Process *proc); +static void thread_db_read_threads(struct Process *proc); + +struct ps_prochandle { + struct Process *proc; +}; + +struct thread_db_info { + struct ps_prochandle proc_handle; + td_thragent_t *thread_agent; +}; + +struct thread_ops thread_db_ops = { + thread_db_startup, + thread_db_probe, + thread_db_read_threads, + thread_db_free +}; + +/* Pointers to the libthread_db functions. */ + +static td_err_e (*td_init_p) (void); + +static td_err_e (*td_ta_new_p) (struct ps_prochandle *ps, td_thragent_t **ta); +static td_err_e (*td_ta_delete_p) (td_thragent_t *); +static td_err_e (*td_ta_thr_iter_p) (const td_thragent_t *ta, + td_thr_iter_f *callback, + void *cbdata_p, td_thr_state_e state, + int ti_pri, sigset_t *ti_sigmask_p, + unsigned int ti_user_flags); +static td_err_e (*td_thr_get_info_p) (const td_thrhandle_t *th, + td_thrinfo_t *infop); +static td_err_e (*td_thr_getgregs_p) (const td_thrhandle_t *th, + prgregset_t gregs); + +static int thread_db_loaded; + +static void +thread_db_startup(void) +{ + void *handle; + td_err_e err; + + handle = dlopen(LIBTHREAD_DB_SO, RTLD_NOW); + if (handle == NULL) + return; + +#define resolve(X) \ + if (!(X##_p = dlsym(handle, #X))) \ + return; + + resolve(td_init); + resolve(td_ta_new); + resolve(td_ta_delete); + resolve(td_ta_thr_iter); + resolve(td_thr_get_info); + resolve(td_thr_getgregs); + + /* Initialize the library. */ + err = td_init_p(); + if (err != TD_OK) { + warnx("Cannot initialize libthread_db: %d", err); + return; + } + thread_db_loaded = 1; +} + +static int +thread_db_probe(struct Process *proc) +{ + struct ElfObject *obj; + struct thread_db_info *info; + td_err_e err; + char *base; + + if (!thread_db_loaded) + return (0); + + /* Explicitly ignore 4.x binaries. */ + for (obj = proc->objectList; obj != NULL; obj = obj->next) { + base = basename(obj->fileName); + if (base == NULL) + continue; + if (strcmp(base, "libc_r.so.4") == 0) + return (0); + } + + info = malloc(sizeof(struct thread_db_info)); + info->proc_handle.proc = proc; + err = td_ta_new_p(&info->proc_handle, &info->thread_agent); + if (err == TD_OK) { + proc->threadInfo = info; + return (1); + } + free(info); + return (0); +} + +static int +find_new_threads_callback(const td_thrhandle_t *th_p, void *data) +{ + struct Process *proc; + struct Thread *t; + prgregset_t gregset; + td_thrinfo_t ti; + td_err_e err; + + err = td_thr_get_info_p(th_p, &ti); + if (err != TD_OK) { + warnx("Cannot get thread info: %d", err); + return (0); + } + + /* Ignore zombie */ + if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE) + return (0); + + err = td_thr_getgregs_p(th_p, gregset); + if (err != TD_OK) { + warnx("Cannot fetch registers for thread %d: %d", ti.ti_tid, + err); + return (0); + } + + proc = data; + t = procReadThread(proc, gregset[0].r_ebp, gregset[0].r_eip); + if (t != NULL) { + t->id = ti.ti_tid; + if (ti.ti_state = TD_THR_RUN) + t->running = 1; + } + return 0; +} + +static void +thread_db_read_threads(struct Process *proc) +{ + struct thread_db_info *info; + td_err_e err; + + /* Iterate over all user-space threads to discover new threads. */ + info = proc->threadInfo; + err = td_ta_thr_iter_p(info->thread_agent, find_new_threads_callback, + proc, TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, TD_SIGNO_MASK, + TD_THR_ANY_USER_FLAGS); + if (err != TD_OK) + warnx("Cannot find new threads: %d", err); +} + +static void +thread_db_free(struct Process *proc) +{ + struct thread_db_info *info; + + info = proc->threadInfo; + td_ta_delete_p(info->thread_agent); + free(proc->threadInfo); + proc->threadInfo = NULL; +} + +/* proc service functions */ +void +ps_plog(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +ps_err_e +ps_pglobal_lookup(struct ps_prochandle *ph, const char *objname, + const char *name, psaddr_t *sym_addr) +{ + struct ElfObject *obj; + const Elf_Sym *sym; + + obj = ph->proc->objectList; + while (obj != NULL) { + if (elfFindSymbolByName(obj, name, &sym) == 0) { + *sym_addr = (void *)(uintptr_t) + (obj->baseAddr + sym->st_value); + return (PS_OK); + } + obj = obj->next; + } + return (PS_NOSYM); +} + +ps_err_e +ps_pread(struct ps_prochandle *ph, psaddr_t addr, void *buf, size_t len) +{ + + if (procReadMem(ph->proc, buf, (uintptr_t)addr, len) != len) + return (PS_ERR); + return (PS_OK); +} + +ps_err_e +ps_pwrite(struct ps_prochandle *ph, psaddr_t addr, const void *buf, size_t len) +{ + + if (procWriteMem(ph->proc, buf, (uintptr_t)addr, len) != len) + return (PS_ERR); + return (PS_OK); +} + +ps_err_e +ps_lgetregs(struct ps_prochandle *ph, lwpid_t lwpid, prgregset_t gregset) +{ + struct ElfObject *core; + const prstatus_t *prstatus; + const void *regs; + int len; + + if (ph->proc->pid == -1) { + core = ph->proc->coreImage; + if (elfGetNote(core, "FreeBSD", NT_PRSTATUS, + (const void **)&prstatus, &len) == -1) + return (PS_ERR); + while (prstatus->pr_pid != lwpid) { + if (elfGetNextNote(core, "FreeBSD", NT_PRSTATUS, + (const void **)&prstatus, &len) == -1) + return (PS_ERR); + } + memcpy(gregset, &prstatus->pr_reg, sizeof(*gregset)); + return (PS_OK); + } + + if (ptrace(PT_GETREGS, lwpid, (void *)gregset, 0) == -1) + return (PS_ERR); + return (PS_OK); +} + +ps_err_e +ps_lsetregs(struct ps_prochandle *ph, lwpid_t lwpid, const prgregset_t gregset) +{ + + warnx("%s called\n", __func__); + return (PS_ERR); +} + +ps_err_e +ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lwpid, prfpregset_t *fpregset) +{ + + warnx("%s called\n", __func__); + return (PS_ERR); +} + +ps_err_e +ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lwpid, + const prfpregset_t *fpregset) +{ + + warnx("%s called\n", __func__); + return (PS_ERR); +} + +#ifdef PT_GETXMMREGS +ps_err_e +ps_lgetxmmregs(struct ps_prochandle *ph, lwpid_t lwpid, char *xmmregs) +{ + + warnx("%s called\n", __func__); + return (PS_ERR); +} + +ps_err_e +ps_lsetxmmregs(struct ps_prochandle *ph, lwpid_t lwpid, + const char *xmmregs) +{ + + warnx("%s called\n", __func__); + return (PS_ERR); +} +#endif + +ps_err_e +ps_lstop(struct ps_prochandle *ph, lwpid_t lwpid) +{ + + warnx("%s called\n", __func__); + return (PS_ERR); +} + +ps_err_e +ps_lcontinue(struct ps_prochandle *ph, lwpid_t lwpid) +{ + + warnx("%s called\n", __func__); + return (PS_ERR); +} + +ps_err_e +ps_linfo(struct ps_prochandle *ph, lwpid_t lwpid, void *info) +{ + + if (ph->proc->pid == -1) { + /* XXX should verify lwpid and make a pseudo lwp info */ + memset(info, 0, sizeof(struct ptrace_lwpinfo)); + return (PS_OK); + } + + if (ptrace(PT_LWPINFO, lwpid, info, sizeof(struct ptrace_lwpinfo)) == -1) + return (PS_ERR); + return (PS_OK); +}