From 9b70e248fb77a5502d4ac0007d90d306656e3ebe Mon Sep 17 00:00:00 2001 From: Dan McNulty Date: Tue, 22 Jun 2010 13:30:14 -0500 Subject: [PATCH] First pass at thread_db integration for FreeBSD. Also, made some updates so the single thread tests still pass. This includes new classes that are derived from int_process and int_thread, thread_db_process and thread_db_thread. freebsd_process and freebsd_thread now derive from the thread_db classes. The integration includes changes to the FreeBSD generator and decoder to handle the new events provided by thread_db. Small fixes for static binaries in FreeBSD-specific AddressTranslate code. --- common/src/addrtranslate-freebsd.C | 6 + common/src/addrtranslate-sysv.C | 4 +- common/src/freebsdKludges.C | 2 +- dyninstAPI/amd64-unknown-freebsd7.2/Makefile | 2 +- dyninstAPI/i386-unknown-freebsd7.2/Makefile | 2 +- proccontrol/i386-unknown-freebsd7.2/Makefile | 5 +- proccontrol/src/freebsd.C | 415 +++++++++++++---- proccontrol/src/freebsd.h | 79 ++-- proccontrol/src/handler.C | 6 +- proccontrol/src/int_process.h | 4 +- proccontrol/src/int_thread_db.C | 665 +++++++++++++++++++++++++++ proccontrol/src/int_thread_db.h | 147 ++++++ proccontrol/src/sysv.C | 2 - proccontrol/src/sysv.h | 2 - 14 files changed, 1204 insertions(+), 137 deletions(-) create mode 100644 proccontrol/src/int_thread_db.C create mode 100644 proccontrol/src/int_thread_db.h diff --git a/common/src/addrtranslate-freebsd.C b/common/src/addrtranslate-freebsd.C index f4f3ebd..e6dbe30 100644 --- a/common/src/addrtranslate-freebsd.C +++ b/common/src/addrtranslate-freebsd.C @@ -144,6 +144,12 @@ bool AddressTranslateSysV::setInterpreter() { return false; } + if( exe->getInterpreter().empty() ) { + translate_printf("[%s:%u] - No interpreter found\n", + __FILE__, __LINE__); + return true; + } + interpreter = files.getNode(exe->getInterpreter(), symfactory); if( interpreter ) interpreter->markInterpreter(); else{ diff --git a/common/src/addrtranslate-sysv.C b/common/src/addrtranslate-sysv.C index c35a316..918cc71 100644 --- a/common/src/addrtranslate-sysv.C +++ b/common/src/addrtranslate-sysv.C @@ -443,6 +443,7 @@ bool AddressTranslateSysV::parseDTDebug() { break; } } + symfactory->closeSymbolReader(exe); if( !dynAddress || !dynSize ) { // This is okay for static binaries @@ -610,7 +611,8 @@ bool AddressTranslateSysV::refresh() // On systems that use DT_DEBUG to determine r_debug_addr, DT_DEBUG might // not be set right away -- read DT_DEBUG now and see if it is set if( !parseDTDebug() && !interpreter ) { - //Static binary + translate_printf("[%s:%u] - Working with static binary, no libraries to refresh\n", + __FILE__, __LINE__); libs.clear(); if (!exec) { exec = getAOut(); diff --git a/common/src/freebsdKludges.C b/common/src/freebsdKludges.C index 61af7f7..3d84b87 100644 --- a/common/src/freebsdKludges.C +++ b/common/src/freebsdKludges.C @@ -163,7 +163,7 @@ map_entries *getVMMaps(int pid, unsigned &maps_size) { return retMaps; } -int sysctl_computeAddrWidth(pid_t pid) { +int sysctl_computeAddrWidth(pid_t /*pid*/) { int retSize = sizeof(void *); #if defined(arch_64bit) diff --git a/dyninstAPI/amd64-unknown-freebsd7.2/Makefile b/dyninstAPI/amd64-unknown-freebsd7.2/Makefile index d726290..064f024 100644 --- a/dyninstAPI/amd64-unknown-freebsd7.2/Makefile +++ b/dyninstAPI/amd64-unknown-freebsd7.2/Makefile @@ -75,7 +75,7 @@ LIBS += -Wl,-static -lelf -Wl,-call_shared else LIBS += -lelf endif -LIBS += -lgcc -lpthread +LIBS += -lgcc -pthread # InstructionAPI is x86-only at this point LIBS += -linstructionAPI diff --git a/dyninstAPI/i386-unknown-freebsd7.2/Makefile b/dyninstAPI/i386-unknown-freebsd7.2/Makefile index 83d4e4b..0c76039 100644 --- a/dyninstAPI/i386-unknown-freebsd7.2/Makefile +++ b/dyninstAPI/i386-unknown-freebsd7.2/Makefile @@ -76,7 +76,7 @@ LIBS += -Wl,-static -lelf -Wl,-call_shared else LIBS += -lelf endif -LIBS += -lgcc -lpthread +LIBS += -lgcc -pthread # InstructionAPI is x86-only at this point LIBS += -linstructionAPI diff --git a/proccontrol/i386-unknown-freebsd7.2/Makefile b/proccontrol/i386-unknown-freebsd7.2/Makefile index e19e691..e0337e2 100644 --- a/proccontrol/i386-unknown-freebsd7.2/Makefile +++ b/proccontrol/i386-unknown-freebsd7.2/Makefile @@ -16,7 +16,8 @@ TARGET = libpcontrol.so LDFLAGS += $(LIBDIR) -lcommon SRCS = ../src/sysv.C \ - ../src/freebsd.C + ../src/freebsd.C \ + ../src/int_thread_db.C ifdef LIBELF_DIR MYLAGS += -I$(LIBELF_DIR) @@ -32,7 +33,7 @@ endif ifdef LIBELF_LIB LIBS += -L$(LIBELF_LIB) endif -LIBS += -lelf +LIBS += -lelf -lthread_db -pthread # Include the module-specific Makefile, which defines everything about # the module that is common across architectures. diff --git a/proccontrol/src/freebsd.C b/proccontrol/src/freebsd.C index d8b3020..84f1a83 100644 --- a/proccontrol/src/freebsd.C +++ b/proccontrol/src/freebsd.C @@ -53,6 +53,10 @@ #include #include +#if defined(arch_x86) || defined(arch_x86_64) +#include +#endif + #include #include @@ -93,70 +97,88 @@ bool GeneratorFreeBSD::canFastHandle() { } ArchEvent *GeneratorFreeBSD::getEvent(bool block) { - // NOTE: while similar to Linux code, it is not the same - // e.g., __WALL is not defined. - int status, options; - - //Block (or not block) in waitpid to receive a OS event - options = block ? 0 : WNOHANG; - pthrd_printf("%s in waitpid\n", block ? "blocking" : "polling"); - int pid = waitpid(-1, &status, options); - - ArchEventFreeBSD *newevent = NULL; - if (pid == -1) { - int errsv = errno; - if (errsv == EINTR) { - pthrd_printf("waitpid interrupted\n"); - newevent = new ArchEventFreeBSD(true); - return newevent; - } - perr_printf("Error. waitpid recieved error %s\n", strerror(errsv)); - newevent = new ArchEventFreeBSD(errsv); - return newevent; - } - - if (dyninst_debug_proccontrol) - { - pthrd_printf("Waitpid return status %x for pid %d:\n", status, pid); - if (WIFEXITED(status)) - pthrd_printf("Exited with %d\n", WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - pthrd_printf("Exited with signal %d\n", WTERMSIG(status)); - else if (WIFSTOPPED(status)) - pthrd_printf("Stopped with signal %d\n", WSTOPSIG(status)); + int status, options; + + //Block (or not block) in waitpid to receive a OS event + options = block ? 0 : WNOHANG; + pthrd_printf("%s in waitpid\n", block ? "blocking" : "polling"); + int pid = waitpid(-1, &status, options); + lwpid_t lwp = NULL_LWP; + + ArchEventFreeBSD *newevent = NULL; + if (pid == -1) { + int errsv = errno; + if (errsv == EINTR) { + pthrd_printf("waitpid interrupted\n"); + newevent = new ArchEventFreeBSD(true); + return newevent; + } + perr_printf("Error. waitpid recieved error %s\n", strerror(errsv)); + newevent = new ArchEventFreeBSD(errsv); + return newevent; + } + + if (dyninst_debug_proccontrol) + { + pthrd_printf("Waitpid return status %x for pid %d:\n", status, pid); + if (WIFEXITED(status)) + pthrd_printf("Exited with %d\n", WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + pthrd_printf("Exited with signal %d\n", WTERMSIG(status)); + else if (WIFSTOPPED(status)) + pthrd_printf("Stopped with signal %d\n", WSTOPSIG(status)); #if defined(WIFCONTINUED) - else if (WIFCONTINUED(status)) - perr_printf("Continued with signal SIGCONT (Unexpected)\n"); + else if (WIFCONTINUED(status)) + perr_printf("Continued with signal SIGCONT (Unexpected)\n"); #endif - else - pthrd_printf("Unable to interpret waitpid return.\n"); - } + else + pthrd_printf("Unable to interpret waitpid return.\n"); + } - newevent = new ArchEventFreeBSD(pid, status); - return newevent; + // On FreeBSD, we need to get information about the thread that caused the + // stop via ptrace -- try for all cases; the ptrace call will fail for some + struct ptrace_lwpinfo lwpInfo; + if( 0 == ptrace(PT_LWPINFO, pid, (caddr_t)&lwpInfo, sizeof(struct ptrace_lwpinfo)) ) { + lwp = lwpInfo.pl_lwpid; + }else{ + int errsv = errno; + pthrd_printf("Failed to retrieve lwp for pid %d: %s\n", pid, strerror(errsv)); + + // It's an error for a stopped or signaled process + if( WIFSTOPPED(status) || WIFSIGNALED(status) ) { + newevent = new ArchEventFreeBSD(errsv); + return newevent; + } + } + + newevent = new ArchEventFreeBSD(pid, lwp, status); + return newevent; } -ArchEventFreeBSD::ArchEventFreeBSD(bool inter_) : - status(0), - pid(NULL_PID), - interrupted(inter_), - error(0) +ArchEventFreeBSD::ArchEventFreeBSD(bool inter_) : + status(0), + pid(NULL_PID), + lwp(NULL_LWP), + interrupted(inter_), + error(0) { } -ArchEventFreeBSD::ArchEventFreeBSD(pid_t p, int s) : - status(s), - pid(p), - interrupted(false), - error(0) +ArchEventFreeBSD::ArchEventFreeBSD(pid_t p, lwpid_t l, int s) : + status(s), + pid(p), + lwp(l), + interrupted(false), + error(0) { } -ArchEventFreeBSD::ArchEventFreeBSD(int e) : - status(0), - pid(NULL_PID), - interrupted(false), - error(e) +ArchEventFreeBSD::ArchEventFreeBSD(int e) : + status(0), + pid(NULL_PID), + lwp(NULL_LWP), + interrupted(false), + error(e) { } @@ -190,37 +212,43 @@ bool DecoderFreeBSD::decode(ArchEvent *ae, std::vector &events) { ArchEventFreeBSD *archevent = static_cast(ae); int_process *proc = NULL; + int_thread *thread = NULL; freebsd_process *lproc = NULL; freebsd_thread *lthread = NULL; - int_thread *thread = ProcPool()->findThread(archevent->pid); + proc = ProcPool()->findProcByPid(archevent->pid); - if( thread ) { - proc = thread->llproc(); - lthread = static_cast(thread); + /* ignore any events we get for processes we don't know anything about. + * This occurs when for a double exit event as described below near + * WIFEXITED + */ + if( !proc ) { + pthrd_printf("Ignoring ArchEvent for unknown process with PID %d\n", + archevent->pid); + return true; } - if( proc ) { - lproc = static_cast(proc); - } + lproc = static_cast(proc); - assert( (thread || proc) && "Couldn't locate process/thread that created the event"); + thread = ProcPool()->findThread(archevent->lwp); - Event::ptr event = Event::ptr(); + // If the ArchEvent did not set the LWP, assign the event to the initial thread + if( !thread ) { + thread = proc->threadPool()->initialThread(); + } + lthread = static_cast(thread); - // TODO thread events (i.e., creation, exit, ...) + Event::ptr event; pthrd_printf("Decoding event for %d/%d\n", proc ? proc->getPid() : -1, thread ? thread->getLWP() : -1); const int status = archevent->status; - bool result; + bool result = false; if( WIFSTOPPED(status) ) { const int stopsig = WSTOPSIG(status); pthrd_printf("Decoded to signal %s\n", strsignal(stopsig)); - installed_breakpoint *ibp = NULL; - Dyninst::Address adjusted_addr = 0; switch( stopsig ) { case SIGSTOP: if( lthread->hasPendingStop() ) { @@ -230,7 +258,7 @@ bool DecoderFreeBSD::decode(ArchEvent *ae, std::vector &events) { break; } // Relying on fall through for bootstrap - case SIGTRAP: + case SIGTRAP: { if( proc->getState() == int_process::neonatal_intermediate) { pthrd_printf("Decoded event to bootstrap on %d/%d\n", proc->getPid(), thread->getLWP()); @@ -245,8 +273,9 @@ bool DecoderFreeBSD::decode(ArchEvent *ae, std::vector &events) { return false; } - adjusted_addr = adjustTrapAddr(addr, proc->getTargetArch()); + Dyninst::Address adjusted_addr = adjustTrapAddr(addr, proc->getTargetArch()); + // Check if it is a RPC if (rpcMgr()->isRPCTrap(thread, adjusted_addr)) { pthrd_printf("Decoded event to rpc completion on %d/%d at %lx\n", proc->getPid(), thread->getLWP(), adjusted_addr); @@ -254,11 +283,13 @@ bool DecoderFreeBSD::decode(ArchEvent *ae, std::vector &events) { break; } - ibp = proc->getBreakpoint(adjusted_addr); + // Check if it is a breakpoint + installed_breakpoint *ibp = proc->getBreakpoint(adjusted_addr); if( ibp && ibp != thread->isClearingBreakpoint() ) { pthrd_printf("Decoded breakpoint on %d/%d at %lx\n", proc->getPid(), thread->getLWP(), adjusted_addr); event = Event::ptr(new EventBreakpoint(adjusted_addr, ibp)); + event->setSyncType(Event::sync_thread); if( adjusted_addr == lproc->getLibBreakpointAddr() ) { pthrd_printf("Breakpoint is library load/unload\n"); @@ -266,6 +297,40 @@ bool DecoderFreeBSD::decode(ArchEvent *ae, std::vector &events) { lib_event->setThread(thread->thread()); lib_event->setProcess(proc->proc()); event->addSubservientEvent(lib_event); + }else{ + // Check for thread events + vector threadEvents; + if( lproc->getEventsAtAddr(adjusted_addr, lthread, threadEvents) ) { + assert(thread->thread()); + assert(proc->proc()); + + vector::iterator threadEventIter; + for(threadEventIter = threadEvents.begin(); threadEventIter != threadEvents.end(); + ++threadEventIter) + { + // An event is created for the initial thread, but this thread is already + // created during bootstrap. Ignore any create events for threads that + // are already created. + if( (*threadEventIter)->getEventType().code() == EventType::ThreadCreate ) { + Dyninst::LWP createdLWP = (*threadEventIter)->getEventNewThread()->getLWP(); + int_thread * newThread = ProcPool()->findThread(createdLWP); + if( NULL != newThread ) { + freebsd_thread *newlThread = static_cast(newThread); + pthrd_printf("Thread already created for LWP %d\n", createdLWP); + + // Still need to enable events for it -- this can't be done during + // bootstrap because the thread_db library isn't guaranteed to + // be initialized at this point + newlThread->setEventReporting(true); + continue; + } + } + + (*threadEventIter)->setThread(thread->thread()); + (*threadEventIter)->setProcess(proc->proc()); + event->addSubservientEvent(*threadEventIter); + } + } } break; } @@ -284,18 +349,20 @@ bool DecoderFreeBSD::decode(ArchEvent *ae, std::vector &events) { } }else{ // TODO for now, just assume that a call to exec happened - // In the future, if we need to trace syscall entry/exit, we will + // In the future, if we need to trace syscalls, we will // need to differentiate between different syscalls + // + // breadcrumb: + // truss(1) does system call identification after a SIGTRAP pthrd_printf("Decoded event to exec on %d/%d\n", proc->getPid(), thread->getLWP()); event = Event::ptr(new EventExec(EventType::Post)); - event->setSyncType(Event::sync_process); } break; + } default: pthrd_printf("Decoded event to signal %d on %d/%d\n", stopsig, proc->getPid(), thread->getLWP()); - #if 1 //Debugging code if (stopsig == SIGSEGV) { @@ -325,15 +392,13 @@ bool DecoderFreeBSD::decode(ArchEvent *ae, std::vector &events) { event = Event::ptr(new EventSignal(stopsig)); break; } - if( event && event->getSyncType() == Event::unset) - event->setSyncType(Event::sync_thread); } else if (WIFEXITED(status) || WIFSIGNALED(status)) { if( WIFEXITED(status) ) { int exitcode = WEXITSTATUS(status); /* * On FreeBSD, an exit of a traced process is reported to both its original - * parent and the tracing process. When the traced process is created a + * parent and the tracing process. When the traced process is created by a * ProcControlAPI process, ProcControlAPI will receive two copies of the exit * event. The second event should be ignored. */ @@ -352,23 +417,27 @@ bool DecoderFreeBSD::decode(ArchEvent *ae, std::vector &events) { proc->getPid(), thread->getLWP(), termsig); event = Event::ptr(new EventCrash(termsig)); } - event->setSyncType(Event::sync_process); int_threadPool::iterator i = proc->threadPool()->begin(); for(; i != proc->threadPool()->end(); ++i) { (*i)->setGeneratorState(int_thread::exited); } } - // Single event decoded assert(event); assert(proc->proc()); assert(thread->thread()); - delete archevent; + + // Most events on FreeBSD are sync_process - all signals stop the entire + // process + if( event && event->getSyncType() == Event::unset) + event->setSyncType(Event::sync_process); event->setThread(thread->thread()); event->setProcess(proc->proc()); events.push_back(event); + delete archevent; + return true; } @@ -396,7 +465,13 @@ int_thread *int_thread::createThreadPlat(int_process *proc, Dyninst::THR_ID thr_ Dyninst::LWP lwp_id, bool initial_thrd) { if( initial_thrd ) { - lwp_id = proc->getPid(); + freebsd_process *tmpProc = static_cast(proc); + + vector lwps; + tmpProc->getThreadLWPs(lwps); + assert( lwps.size() == 1 ); + + lwp_id = lwps.back(); } freebsd_thread *lthrd = new freebsd_thread(proc, thr_id, lwp_id); assert(lthrd); @@ -404,7 +479,8 @@ int_thread *int_thread::createThreadPlat(int_process *proc, Dyninst::THR_ID thr_ } HandlerPool *plat_createDefaultHandlerPool(HandlerPool *hpool) { - // TODO add any platform-specific handlers + // TODO add special stop handler + thread_db_process::addThreadDBHandlers(hpool); return hpool; } @@ -413,12 +489,12 @@ bool ProcessPool::LWPIDsAreUnique() { } freebsd_process::freebsd_process(Dyninst::PID p, std::string e, std::vector a) - : sysv_process(p, e, a) + : thread_db_process(p, e, a) { } freebsd_process::freebsd_process(Dyninst::PID pid_, int_process *p) - : sysv_process(pid_, p) + : thread_db_process(pid_, p) { } @@ -524,12 +600,24 @@ bool freebsd_process::plat_readMem(int_thread *thr, void *local, return PtraceBulkRead(remote, size, local, thr->llproc()->getPid()); } +bool freebsd_process::plat_readProcMem(void *local, + Dyninst::Address remote, size_t size) +{ + return PtraceBulkRead(remote, size, local, getPid()); +} + bool freebsd_process::plat_writeMem(int_thread *thr, void *local, Dyninst::Address remote, size_t size) { return PtraceBulkWrite(remote, size, local, thr->llproc()->getPid()); } +bool freebsd_process::plat_writeProcMem(void *local, + Dyninst::Address remote, size_t size) +{ + return PtraceBulkWrite(remote, size, local, getPid()); +} + bool freebsd_process::needIndividualThreadAttach() { return false; } @@ -561,8 +649,41 @@ bool freebsd_process::independentLWPControl() { return true; } +bool freebsd_process::plat_getLWPInfo(lwpid_t lwp, void *lwpInfo) { + if( 0 != ptrace(PT_LWPINFO, lwp, (caddr_t)lwpInfo, sizeof(struct ptrace_lwpinfo)) ) { + perr_printf("Failed to get thread info for lwp %d: %s\n", + lwp, strerror(errno)); + setLastError(err_internal, "Failed to get thread info"); + return false; + } + + return true; +} + +bool freebsd_process::plat_contThread(lwpid_t lwp) { + if( 0 != ptrace(PT_RESUME, lwp, (caddr_t)1, 0) ) { + perr_printf("Failed to resume lwp %d: %s\n", + lwp, strerror(errno)); + setLastError(err_internal, "Failed to resume lwp"); + return false; + } + + return true; +} + +bool freebsd_process::plat_stopThread(lwpid_t lwp) { + if( 0 != ptrace(PT_SUSPEND, lwp, (caddr_t)1, 0) ) { + perr_printf("Failed to suspend lwp %d: %s\n", + lwp, strerror(errno)); + setLastError(err_internal, "Failed to suspend lwp"); + return false; + } + + return true; +} + freebsd_thread::freebsd_thread(int_process *p, Dyninst::THR_ID t, Dyninst::LWP l) - : int_thread(p, t, l) + : thread_db_thread(p, t, l) { } @@ -578,19 +699,23 @@ bool freebsd_thread::plat_cont() { // continuing the process. // + pthrd_printf("Continuing thread %d\n", lwp); + int result; if (singleStep()) { - pthrd_printf("Calling PT_STEP with signal %d\n", continueSig_); + pthrd_printf("Calling PT_STEP on %d with signal %d\n", + lwp, continueSig_); result = ptrace(PT_STEP, proc_->getPid(), (caddr_t)1, continueSig_); } else { - pthrd_printf("Calling PT_CONTINUE with signal %d\n", continueSig_); + pthrd_printf("Calling PT_CONTINUE on %d with signal %d\n", + proc_->getPid(), continueSig_); result = ptrace(PT_CONTINUE, proc_->getPid(), (caddr_t)1, continueSig_); } if (0 != result) { int error = errno; perr_printf("low-level continue failed: %s\n", strerror(error)); - setLastError(err_internal, "Low-level continue failed\n"); + setLastError(err_internal, "Low-level continue failed"); return false; } @@ -601,7 +726,20 @@ bool freebsd_thread::plat_cont() { // report kern/142757 -- if we yield to the scheduler, we may avoid // the race condition described in this bug report. // - // I am unsure if this always works, or just works some of the time. + // Specifically, the race condition results from the following sequence + // of events, where P = parent process, C = child process: + // + // P-SIGSTOP C-STOP P-WAITPID P-CONT P-SIGSTOP C-RUN + // + // Because the child doesn't run before the parent sends the second + // stop signal, the child doesn't receive the second SIGSTOP. + // + // A workaround for this problem would guarantee that the C-RUN event + // always occurs before the second P-SIGSTOP. Here, the sched_yield + // attempts to ensure this. + // + // TODO this doesn't always solve the problem + sched_yield(); return true; @@ -732,10 +870,66 @@ static void init_dynreg_to_user() { init_lock.unlock(); } +#if 0 +// Debugging +static void dumpRegisters(struct reg *regs) { +#if defined(arch_x86) + fprintf(stderr, "r_fs = 0x%x\n", regs->r_fs); + fprintf(stderr, "r_es = 0x%x\n", regs->r_es); + fprintf(stderr, "r_ds = 0x%x\n", regs->r_ds); + fprintf(stderr, "r_edi = 0x%x\n", regs->r_edi); + fprintf(stderr, "r_esi = 0x%x\n", regs->r_esi); + fprintf(stderr, "r_ebp = 0x%x\n", regs->r_ebp); + fprintf(stderr, "r_ebx = 0x%x\n", regs->r_ebx); + fprintf(stderr, "r_ecx = 0x%x\n", regs->r_ecx); + fprintf(stderr, "r_eax = 0x%x\n", regs->r_eax); + fprintf(stderr, "r_eip = 0x%x\n", regs->r_eip); + fprintf(stderr, "r_cs = 0x%x\n", regs->r_cs); + fprintf(stderr, "r_eflags = 0x%x\n", regs->r_eflags); + fprintf(stderr, "r_esp = 0x%x\n", regs->r_esp); + fprintf(stderr, "r_ss = 0x%x\n", regs->r_ss); + fprintf(stderr, "r_gs = 0x%x\n", regs->r_gs); + fprintf(stderr, "r_trapno = 0x%x\n", regs->r_trapno); + fprintf(stderr, "r_err = 0x%x\n", regs->r_err); +#endif +} +#endif + bool freebsd_process::plat_individualRegAccess() { return false; } +string freebsd_process::getThreadLibName(const char *symName) { + // XXX + // This hack is needed because the FreeBSD implementation doesn't + // set the object name when looking for a symbol -- instead of + // searching every library for the symbol, make some educated + // guesses + // + // It also assumes that the first symbols thread_db will lookup + // are either _libthr_debug or _libkse_debug + + if( !strcmp(symName, "_libkse_debug") ) { + libThreadName = "libkse.so"; + }else if( !strcmp(symName, "_libthr_debug") || + libThreadName.empty() ) + { + libThreadName = "libthr.so"; + } + + return libThreadName; +} + +bool freebsd_process::isSupportedThreadLib(const string &libName) { + if( libName.find("libthr") != string::npos ) { + return true; + }else if( libName.find("libkse") != string::npos ) { + return true; + } + + return false; +} + bool freebsd_thread::plat_getAllRegisters(int_registerPool ®pool) { struct reg registers; unsigned char *regPtr = (unsigned char *)®isters; @@ -767,13 +961,33 @@ bool freebsd_thread::plat_getAllRegisters(int_registerPool ®pool) { }else{ assert(!"Unknown address width"); } - pthrd_printf("Register %s has value 0x%x, offset 0x%x\n", reg.name(), (unsigned int)val, offset); + pthrd_printf("Register %s has value 0x%lx, offset 0x%x\n", reg.name(), (unsigned long)val, offset); regpool.regs[reg] = val; } return true; } +static bool validateRegisters(struct reg *regs, Dyninst::LWP lwp) { +#if defined(arch_x86) + struct reg old_regs; + if( 0 != ptrace(PT_GETREGS, lwp, (caddr_t)&old_regs, 0) ) { + perr_printf("Error reading registers from LWP %d\n", lwp); + return false; + } + + // Sometimes the resume flag is set in the saved version of the + // registers and not set in the current set of registers -- + // the OS doesn't allow us to change this flag, change it to the + // current value + if( (old_regs.r_eflags & PSL_RF) != (regs->r_eflags & PSL_RF) ) { + if( old_regs.r_eflags & PSL_RF ) regs->r_eflags |= PSL_RF; + else regs->r_eflags &= ~PSL_RF; + } +#endif + return true; +} + bool freebsd_thread::plat_setAllRegisters(int_registerPool ®pool) { init_dynreg_to_user(); @@ -812,7 +1026,7 @@ bool freebsd_thread::plat_setAllRegisters(int_registerPool ®pool) { assert(!"Unknown address width"); } - pthrd_printf("Register %s gets value %lx, offset %d\n", reg.name(), val, offset); + pthrd_printf("Register %s gets value 0x%lx, offset 0x%x\n", reg.name(), (unsigned long)val, offset); } if (num_found != regpool.regs.size()) { @@ -823,9 +1037,26 @@ bool freebsd_thread::plat_setAllRegisters(int_registerPool ®pool) { } if( 0 != ptrace(PT_SETREGS, lwp, (caddr_t)®isters, 0) ) { - perr_printf("Error setting registers for LWP %d: %s\n", lwp, strerror(errno)); - setLastError(err_internal, "Could not set registers in thread"); - return false; + bool success = false; + if( EINVAL == errno ) { + // This usually means that the flag register would change some system status bits + pthrd_printf("Attempting to handle EINVAL caused by PT_SETREGS for LWP %d\n", lwp); + + if( validateRegisters(®isters, lwp) ) { + if( !ptrace(PT_SETREGS, lwp, (caddr_t)®isters, 0) ) { + pthrd_printf("Successfully handled EINVAL caused by PT_SETREGS\n"); + success = true; + }else{ + perr_printf("Failed to handle EINVAL caused by PT_SETREGS\n"); + } + } + } + + if( !success ) { + perr_printf("Error setting registers for LWP %d: %s\n", lwp, strerror(errno)); + setLastError(err_internal, "Could not set registers in thread"); + return false; + } } pthrd_printf("Successfully set the values of all registers for LWP %d\n", lwp); diff --git a/proccontrol/src/freebsd.h b/proccontrol/src/freebsd.h index 9511ca8..02b757d 100644 --- a/proccontrol/src/freebsd.h +++ b/proccontrol/src/freebsd.h @@ -36,7 +36,7 @@ #include "proccontrol/h/Decoder.h" #include "proccontrol/h/Handler.h" #include "proccontrol/src/int_process.h" -#include "proccontrol/src/sysv.h" +#include "proccontrol/src/int_thread_db.h" #include "common/h/dthread.h" using namespace Dyninst; @@ -44,70 +44,85 @@ using namespace ProcControlAPI; class GeneratorFreeBSD : public GeneratorMT { - public: - GeneratorFreeBSD(); - virtual ~GeneratorFreeBSD(); +public: + GeneratorFreeBSD(); + virtual ~GeneratorFreeBSD(); - virtual bool initialize(); - virtual bool canFastHandle(); - virtual ArchEvent *getEvent(bool block); + virtual bool initialize(); + virtual bool canFastHandle(); + virtual ArchEvent *getEvent(bool block); }; class ArchEventFreeBSD : public ArchEvent { - public: - int status; - pid_t pid; - bool interrupted; - int error; - - ArchEventFreeBSD(bool inter_); - ArchEventFreeBSD(pid_t p, int s); - ArchEventFreeBSD(int e); - - virtual ~ArchEventFreeBSD(); +public: + int status; + pid_t pid; + lwpid_t lwp; + bool interrupted; + int error; + + ArchEventFreeBSD(bool inter_); + ArchEventFreeBSD(pid_t p, lwpid_t l, int s); + ArchEventFreeBSD(int e); + + virtual ~ArchEventFreeBSD(); }; class DecoderFreeBSD : public Decoder { - public: - DecoderFreeBSD(); - virtual ~DecoderFreeBSD(); - virtual unsigned getPriority() const; - virtual bool decode(ArchEvent *ae, std::vector &events); - Dyninst::Address adjustTrapAddr(Dyninst::Address address, Dyninst::Architecture arch); +public: + DecoderFreeBSD(); + virtual ~DecoderFreeBSD(); + virtual unsigned getPriority() const; + virtual bool decode(ArchEvent *ae, std::vector &events); + Dyninst::Address adjustTrapAddr(Dyninst::Address address, Dyninst::Architecture arch); }; -class freebsd_process : public sysv_process +class freebsd_process : public thread_db_process { - public: +public: freebsd_process(Dyninst::PID p, std::string e, std::vector a); freebsd_process(Dyninst::PID pid_, int_process *p); virtual ~freebsd_process(); virtual bool plat_create(); - virtual bool plat_attach(); + virtual bool plat_attach(); virtual bool plat_forked(); virtual bool post_forked(); virtual bool plat_execed(); virtual bool plat_detach(); virtual bool plat_terminate(bool &needs_sync); - virtual bool plat_readMem(int_thread *thr, void *local, - Dyninst::Address remote, size_t size); - virtual bool plat_writeMem(int_thread *thr, void *local, + virtual bool plat_readMem(int_thread *thr, void *local, Dyninst::Address remote, size_t size); + virtual bool plat_writeMem(int_thread *thr, void *local, + Dyninst::Address remote, size_t size); virtual bool needIndividualThreadAttach(); virtual bool getThreadLWPs(std::vector &lwps); virtual Dyninst::Architecture getTargetArch(); virtual bool independentLWPControl(); virtual bool plat_individualRegAccess(); + + /* thread_db_process methods */ + virtual string getThreadLibName(const char *symName); + virtual bool isSupportedThreadLib(const string &libName); + virtual bool plat_readProcMem(void *local, + Dyninst::Address remote, size_t size); + virtual bool plat_writeProcMem(void *local, + Dyninst::Address remote, size_t size); + virtual bool plat_getLWPInfo(lwpid_t lwp, void *lwpInfo); + virtual bool plat_contThread(lwpid_t lwp); + virtual bool plat_stopThread(lwpid_t lwp); + +protected: + string libThreadName; }; -class freebsd_thread : public int_thread +class freebsd_thread : public thread_db_thread { - public: +public: freebsd_thread(int_process *p, Dyninst::THR_ID t, Dyninst::LWP l); freebsd_thread(); diff --git a/proccontrol/src/handler.C b/proccontrol/src/handler.C index dd9aeb3..bc1c1e7 100644 --- a/proccontrol/src/handler.C +++ b/proccontrol/src/handler.C @@ -271,6 +271,7 @@ bool HandlePostExit::handleEvent(Event::ptr ev) proc->setState(int_process::exited); ProcPool()->rmProcess(proc); + ProcPool()->rmThread(thrd); ProcPool()->condvar()->signal(); ProcPool()->condvar()->unlock(); @@ -601,8 +602,9 @@ bool HandlePostBreakpoint::handleEvent(Event::ptr ev) proc->getPid()); int_threadPool *pool = proc->threadPool(); for (int_threadPool::iterator i = pool->begin(); i != pool->end(); i++) { - if ((*i)->getInternalState() == int_thread::running) + if ((*i)->getInternalState() == int_thread::running) { (*i)->setInternalState(int_thread::stopped); + } } EventBreakpoint *evbp = static_cast(ev.get()); @@ -783,6 +785,8 @@ static const char *action_str(Process::cb_action_t action) default: assert(0); } + + return NULL; } bool HandleCallbacks::handleCBReturn(Process::const_ptr proc, Thread::const_ptr thrd, diff --git a/proccontrol/src/int_process.h b/proccontrol/src/int_process.h index 53e899a..8008fe2 100644 --- a/proccontrol/src/int_process.h +++ b/proccontrol/src/int_process.h @@ -63,12 +63,12 @@ class int_process protected: bool create(); virtual bool plat_create() = 0; - bool post_create(); + virtual bool post_create(); bool attach(); virtual bool plat_attach() = 0; bool attachThreads(); - bool post_attach(); + virtual bool post_attach(); public: bool forked(); diff --git a/proccontrol/src/int_thread_db.C b/proccontrol/src/int_thread_db.C new file mode 100644 index 0000000..b483824 --- /dev/null +++ b/proccontrol/src/int_thread_db.C @@ -0,0 +1,665 @@ +/* + * Copyright (c) 1996-2009 Barton P. Miller + * + * We provide the Paradyn Parallel Performance Tools (below + * described as "Paradyn") on an AS IS basis, and do not warrant its + * validity or performance. We reserve the right to update, modify, + * or discontinue this software at any time. We shall have no + * obligation to supply such updates or modifications or any other + * form of support to you. + * + * By your use of Paradyn, you understand and agree that we (or any + * other person or entity with proprietary rights in Paradyn) are + * under no obligation to provide either maintenance services, + * update services, notices of latent defects, or correction of + * defects for Paradyn. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include +#include +using std::set; + +#include "common/h/dthread.h" +#include "int_thread_db.h" + +/* + * proc_service interface implementation, needed by libthread_db + */ + +ps_err_e ps_pglobal_lookup(struct ps_prochandle *handle, const char *objName, + const char *symName, psaddr_t *symbolAddr) +{ + return handle->thread_db_proc->getSymbolAddr(objName, symName, symbolAddr); +} + +ps_err_e ps_pread(struct ps_prochandle *handle, psaddr_t remote, void *local, size_t size) { + if( !handle->thread_db_proc->plat_readProcMem(local, (Dyninst::Address)remote, size) ) + return PS_ERR; + + return PS_OK; +} + +ps_err_e ps_pwrite(struct ps_prochandle *handle, psaddr_t remote, const void *local, size_t size) { + if( !handle->thread_db_proc->plat_writeProcMem(const_cast(local), (Dyninst::Address)remote, size) ) + return PS_ERR; + + return PS_OK; +} + +ps_err_e ps_linfo(struct ps_prochandle *handle, lwpid_t lwp, void *lwpInfo) { + if( !handle->thread_db_proc->plat_getLWPInfo(lwp, lwpInfo) ) + return PS_ERR; + + return PS_OK; +} + +ps_err_e ps_lstop(struct ps_prochandle *handle, lwpid_t lwp) { + if( !handle->thread_db_proc->plat_stopThread(lwp) ) { + return PS_ERR; + } + + return PS_OK; +} + +ps_err_e ps_lcontinue(struct ps_prochandle *handle, lwpid_t lwp) { + if( !handle->thread_db_proc->plat_contThread(lwp) ) { + return PS_ERR; + } + + return PS_OK; +} + +void ps_plog(const char *format, ...) { + if( !dyninst_debug_proccontrol ) return; + if( NULL == format ) return; + + va_list va; + va_start(va, format); + vfprintf(pctrl_err_out, format, va); + va_end(va); +} + +#define NA_IMPLEMENTED "This function is not implemented" + +ps_err_e ps_lgetfpregs(struct ps_prochandle *, lwpid_t, prfpregset_t *) { + assert(!NA_IMPLEMENTED); + return PS_ERR; +} + +ps_err_e ps_lgetregs(struct ps_prochandle *, lwpid_t, prgregset_t) { + assert(!NA_IMPLEMENTED); + return PS_ERR; +} + +ps_err_e ps_lsetfpregs(struct ps_prochandle *, lwpid_t, const prfpregset_t *) { + assert(!NA_IMPLEMENTED); + return PS_ERR; +} + +ps_err_e ps_lsetregs(struct ps_prochandle *, lwpid_t, const prgregset_t) { + assert(!NA_IMPLEMENTED); + return PS_ERR; +} + +ps_err_e ps_lgetxmmregs (struct ps_prochandle *, lwpid_t, char *) { + assert(!NA_IMPLEMENTED); + return PS_ERR; +} + +ps_err_e ps_lsetxmmregs (struct ps_prochandle *, lwpid_t, const char *) { + assert(!NA_IMPLEMENTED); + return PS_ERR; +} + +ps_err_e ps_pcontinue(struct ps_prochandle *) { + assert(!NA_IMPLEMENTED); + return PS_ERR; +} + +ps_err_e ps_pdmodel(struct ps_prochandle *, int *) { + assert(!NA_IMPLEMENTED); + return PS_ERR; +} + +ps_err_e ps_pstop(struct ps_prochandle *) { + assert(!NA_IMPLEMENTED); + return PS_ERR; +} + +#ifndef CASE_RETURN_STR +#define CASE_RETURN_STR(x) case x: return #x; +#endif + +static const char *tdErr2Str(td_err_e errVal) { + switch(errVal) { + CASE_RETURN_STR(TD_ERR) + CASE_RETURN_STR(TD_OK) + CASE_RETURN_STR(TD_BADKEY) + CASE_RETURN_STR(TD_BADPH) + CASE_RETURN_STR(TD_BADSH) + CASE_RETURN_STR(TD_BADTA) + CASE_RETURN_STR(TD_BADTH) + CASE_RETURN_STR(TD_DBERR) + CASE_RETURN_STR(TD_MALLOC) + CASE_RETURN_STR(TD_NOAPLIC) + CASE_RETURN_STR(TD_NOCAPAB) + CASE_RETURN_STR(TD_NOEVENT) + CASE_RETURN_STR(TD_NOFPREGS) + CASE_RETURN_STR(TD_NOLIBTHREAD) + CASE_RETURN_STR(TD_NOLWP) + CASE_RETURN_STR(TD_NOMSG) + CASE_RETURN_STR(TD_NOSV) + CASE_RETURN_STR(TD_NOTHR) + CASE_RETURN_STR(TD_NOTSD) + CASE_RETURN_STR(TD_NOXREGS) + CASE_RETURN_STR(TD_PARTIALREG) + default: + return "?"; + } +} + +static +Event::ptr decodeThreadEvent(td_event_msg_t *eventMsg) { + td_thrinfo_t threadInfo; + td_err_e errVal; + if( TD_OK != (errVal = td_thr_get_info(eventMsg->th_p, &threadInfo)) ) { + perr_printf("Failed to get thread event info from event msg: %s(%d)\n", + tdErr2Str(errVal), errVal); + return Event::ptr(); + } + + switch(eventMsg->event) { + case TD_CREATE: + return Event::ptr(new EventNewThread((Dyninst::LWP)threadInfo.ti_lid)); + break; + case TD_DEATH: + return Event::ptr(new EventThreadDestroy(EventType::Pre)); + break; + default: + pthrd_printf("Unimplemented libthread_db event encountered. Skipping for now.\n"); + break; + } + + return Event::ptr(); +} + +volatile bool thread_db_process::thread_db_initialized = false; + +thread_db_process::thread_db_process(Dyninst::PID p, std::string e, std::vector a) + : sysv_process(p, e, a), threadAgent(NULL) +{ + self = new ps_prochandle(); + self->thread_db_proc = this; + assert(self); +} + +thread_db_process::thread_db_process(Dyninst::PID pid_, int_process *p) + : sysv_process(pid_, p), threadAgent(NULL) +{ + self = new ps_prochandle(); + self->thread_db_proc = this; + assert(self); +} + +thread_db_process::~thread_db_process() +{ + delete self; + + if( thread_db_initialized && threadAgent ) { + td_err_e errVal = td_ta_delete(threadAgent); + if( TD_OK != errVal ) { + perr_printf("Failed to delete thread agent: %s(%d)\n", + tdErr2Str(errVal), errVal); + } + assert( TD_OK == errVal && "Failed to delete thread agent" ); + } + + // Free the breakpoints allocated for events + map >::iterator brkptIter; + for(brkptIter = addr2Event.begin(); brkptIter != addr2Event.end(); ++brkptIter) { + delete brkptIter->second.first; + } + + // Close all the symbol readers used + map >::iterator symReaderIter; + for(symReaderIter = symReaders.begin(); symReaderIter != symReaders.end(); + ++symReaderIter) + { + symreader_factory->closeSymbolReader(symReaderIter->second.second); + } +} + +bool thread_db_process::initThreadDB() { + // Q: Why isn't this in the constructor? + // A: This function depends on the corresponding thread library being loaded + // and this event occurs some time after process creation. + + // Make sure thread_db is initialized + if( !thread_db_initialized ) { + td_err_e errVal; + if( TD_OK != (errVal = td_init()) ) { + perr_printf("Failed to initialize libthread_db: %s(%d)\n", + tdErr2Str(errVal), errVal); + setLastError(err_internal, "libthread_db initialization failed"); + return false; + } + pthrd_printf("Sucessfully initialized thread_db\n"); + thread_db_initialized = true; + } + + // Create the thread agent + td_err_e errVal = td_ta_new(self, &threadAgent); + switch(errVal) { + case TD_OK: + break; + case TD_NOLIBTHREAD: + pthrd_printf("Debuggee isn't multithreaded at this point, libthread_db not enabled\n"); + return true; + default: + perr_printf("Failed to create thread agent: %s(%d)\n", + tdErr2Str(errVal), errVal); + setLastError(err_internal, "Failed to create libthread_db agent"); + return false; + } + + // Enable all events + td_thr_events_t eventMask; + td_event_fillset(&eventMask); + + errVal = td_ta_set_event(threadAgent, &eventMask); + if( TD_OK != errVal ) { + perr_printf("Failed to enable events: %s(%d)\n", + tdErr2Str(errVal), errVal); + setLastError(err_internal, "Failed to enable libthread_db events"); + return false; + } + + // Determine the addresses for all events + td_event_e allEvents[] = { TD_CATCHSIG, TD_CONCURRENCY, TD_CREATE, + TD_DEATH, TD_IDLE, TD_LOCK_TRY, TD_PREEMPT, TD_PRI_INHERIT, + TD_READY, TD_REAP, TD_SLEEP, TD_SWITCHFROM, TD_SWITCHTO, + TD_TIMEOUT }; + + for(unsigned i = 0; i < (sizeof(allEvents)/sizeof(td_event_e)); ++i) { + td_notify_t notifyResult; + errVal = td_ta_event_addr(threadAgent, allEvents[i], ¬ifyResult); + + // This indicates that the event isn't supported + if( TD_OK != errVal ) continue; + + assert( notifyResult.type == NOTIFY_BPT && "Untested notify type" ); + + EventType newEvent; + switch(allEvents[i]) { + case TD_CREATE: + newEvent = EventType(EventType::Post, EventType::ThreadCreate); + pthrd_printf("Installing breakpoint for thread creation events\n"); + break; + case TD_DEATH: + newEvent = EventType(EventType::Post, EventType::ThreadDestroy); + pthrd_printf("Installing breakpoint for thread destroy events\n"); + break; + default: + pthrd_printf("Unimplemented libthread_db event encountered. Skipping for now.\n"); + continue; + } + + int_breakpoint *newEventBrkpt = new int_breakpoint(Breakpoint::ptr()); + if( !addBreakpoint((Dyninst::Address)notifyResult.u.bptaddr, + newEventBrkpt)) + { + perr_printf("Failed to install new event breakpoint\n"); + setLastError(err_internal, "Failed to install new thread_db event breakpoint"); + delete newEventBrkpt; + + return false; + } + + pair >::iterator, bool> insertIter; + insertIter = addr2Event.insert(make_pair((Dyninst::Address)notifyResult.u.bptaddr, + make_pair(newEventBrkpt, newEvent))); + + assert( insertIter.second && "event breakpoint address not unique" ); + } + + return true; +} + +bool thread_db_process::getEventsAtAddr(Dyninst::Address addr, + thread_db_thread *eventThread, vector &threadEvents) +{ + unsigned oldSize = threadEvents.size(); + + // Determine what type event occurs at the specified address + map >::iterator addrIter; + addrIter = addr2Event.find(addr); + if( addrIter == addr2Event.end() ) return false; + + switch(addrIter->second.second.code()) { + case EventType::ThreadCreate: + { + pthrd_printf("Address 0x%lx corresponds to a thread create event.\n", + addr); + // Need to ask via the thread_db agent for creation events. This + // could result in getting information about other events. All of + // these events need to be handled. + td_event_msg_t threadMsg; + td_err_e msgErr = TD_OK; + while(msgErr == TD_OK) { + msgErr = td_ta_event_getmsg(threadAgent, &threadMsg); + if( msgErr == TD_OK ) { + Event::ptr threadEvent = decodeThreadEvent(&threadMsg); + if( threadEvent ) { + threadEvents.push_back(threadEvent); + } + } + } + + if( msgErr != TD_NOMSG ) { + perr_printf("Failed to retrieve thread event: %s(%d)\n", + tdErr2Str(msgErr), msgErr); + } + break; + } + case EventType::ThreadDestroy: + { + pthrd_printf("Address 0x%lx corresponds to a thread destroy event.\n", + addr); + assert(eventThread); + Event::ptr threadEvent = eventThread->getThreadEvent(); + if( threadEvent ) { + threadEvents.push_back(threadEvent); + }else{ + perr_printf("Failed to retrieve thread event for LWP %d\n", + eventThread->getLWP()); + } + break; + } + default: + pthrd_printf("Unimplemented libthread_db event encountered. Skipping for now.\n"); + break; + } + + return oldSize != threadEvents.size(); +} + +td_thragent_t *thread_db_process::getThreadDBAgent() { + return threadAgent; +} + +ps_err_e thread_db_process::getSymbolAddr(const char *objName, const char *symName, + psaddr_t *symbolAddr) +{ + SymReader *objSymReader = NULL; + LoadedLib *lib = NULL; + + // For static executables, we need to search the executable instead of the + // thread library. + + // For static executables, breakpoint_addr isn't set + if( !breakpoint_addr ) { + lib = translator->getExecutable(); + if( NULL == lib ) { + perr_printf("Failed to get loaded version of executable\n"); + setLastError(err_internal, "Failed to get loaded version of executable"); + return PS_ERR; + } + + map >::iterator symReaderIter; + symReaderIter = symReaders.find(lib->getName()); + if( symReaderIter == symReaders.end() ) { + objSymReader = symreader_factory->openSymbolReader(lib->getName()); + if( NULL == objSymReader ) { + perr_printf("Failed to open symbol reader for %s\n", + lib->getName().c_str()); + setLastError(err_internal, "Failed to open executable for symbol reading"); + return PS_ERR; + } + symReaders.insert(make_pair(lib->getName(), make_pair(lib, objSymReader))); + }else{ + objSymReader = symReaderIter->second.second; + } + }else{ + // FreeBSD implementation doesn't set objName + string objNameStr; + if( NULL == objName ) { + objNameStr = getThreadLibName(symName); + }else{ + objNameStr = objName; + } + + map >::iterator symReaderIter; + symReaderIter = symReaders.find(objNameStr); + if( symReaderIter == symReaders.end() ) { + vector libs; + if( !translator->getLibs(libs) ) { + perr_printf("Failed to retrieve loaded libraries\n"); + setLastError(err_internal, "Failed to retrieve loaded libraries"); + return PS_ERR; + } + + vector::iterator loadedLibIter; + for(loadedLibIter = libs.begin(); loadedLibIter != libs.end(); + ++loadedLibIter) + { + if( (*loadedLibIter)->getName().find(objNameStr) != string::npos ) { + lib = (*loadedLibIter); + break; + } + } + + if( NULL == lib ) { + perr_printf("Failed to find loaded library for %s\n", objNameStr.c_str()); + setLastError(err_internal, "Failed to find loaded library"); + return PS_ERR; + } + + objSymReader = symreader_factory->openSymbolReader(lib->getName()); + + if( NULL == objSymReader ) { + perr_printf("Failed to open symbol reader for %s\n", objNameStr.c_str()); + setLastError(err_internal, "Failed to open library for symbol reading"); + return PS_ERR; + } + + symReaders.insert(make_pair(objNameStr, make_pair(lib, objSymReader))); + }else{ + lib = symReaderIter->second.first; + objSymReader = symReaderIter->second.second; + } + } + + Symbol_t lookupSym = objSymReader->getSymbolByName(string(symName)); + + if( !objSymReader->isValidSymbol(lookupSym) ) { + return PS_NOSYM; + } + + *symbolAddr = (psaddr_t)lib->offToAddress( + objSymReader->getSymbolOffset(lookupSym)); + + return PS_OK; +} + +bool thread_db_process::post_create() { + if( !int_process::post_create() ) return false; + + return initThreadDB(); +} + +bool thread_db_process::post_attach() { + if( !int_process::post_attach() ) return false; + + return initThreadDB(); +} + +void thread_db_process::addThreadDBHandlers(HandlerPool *hpool) { + static bool initialized = false; + static ThreadDBLibHandler *libHandler = NULL; + static ThreadDBHandleNewThr *thrHandler = NULL; + if( !initialized ) { + libHandler = new ThreadDBLibHandler(); + thrHandler = new ThreadDBHandleNewThr(); + initialized = true; + } + hpool->addHandler(libHandler); + hpool->addHandler(thrHandler); +} + +ThreadDBLibHandler::ThreadDBLibHandler() : + Handler("thread_db Library Handler") +{ +} + +ThreadDBLibHandler::~ThreadDBLibHandler() +{ +} + +bool ThreadDBLibHandler::handleEvent(Event::ptr ev) { + EventLibrary::const_ptr libEv = ev->getEventLibrary(); + + thread_db_process *proc = static_cast(ev->getProcess()->llproc()); + + const set &addLibs = libEv->libsAdded(); + + set::iterator libIter; + for( libIter = addLibs.begin(); libIter != addLibs.end(); ++libIter ) { + if( proc->isSupportedThreadLib((*libIter)->getName()) ) { + pthrd_printf("Enabling thread_db support for pid %d\n", + proc->getPid()); + if( !proc->initThreadDB() ) { + pthrd_printf("Failed to initialize thread_db for pid %d\n", + proc->getPid()); + return false; + } + break; + } + } + + return true; +} + +int ThreadDBLibHandler::getPriority() const { + return PostPlatformPriority; +} + +void ThreadDBLibHandler::getEventTypesHandled(vector &etypes) { + etypes.push_back(EventType(EventType::None, EventType::Library)); +} + +ThreadDBHandleNewThr::ThreadDBHandleNewThr() : + Handler("thread_db New Thread Handler") +{ +} + +ThreadDBHandleNewThr::~ThreadDBHandleNewThr() +{ +} + +bool ThreadDBHandleNewThr::handleEvent(Event::ptr ev) { + EventNewThread::const_ptr threadEv = ev->getEventNewThread(); + + thread_db_thread *thread = static_cast(threadEv->getNewThread()->llthrd()); + + assert(thread && "Thread was not initialized before calling this handler"); + + // Explicitly ignore errors here + thread->setEventReporting(true); + + return true; +} + +int ThreadDBHandleNewThr::getPriority() const { + return PostPlatformPriority; +} + +void ThreadDBHandleNewThr::getEventTypesHandled(vector &etypes) { + etypes.push_back(EventType(EventType::None, EventType::ThreadCreate)); +} + +thread_db_thread::thread_db_thread(int_process *p, Dyninst::THR_ID t, Dyninst::LWP l) + : int_thread(p, t, l) +{ +} + +thread_db_thread::~thread_db_thread() +{ +} + +Event::ptr thread_db_thread::getThreadEvent() { + thread_db_process *lproc = static_cast(proc_); + td_thrhandle_t threadHandle; + + // Get the thread handle + td_err_e errVal = td_ta_map_lwp2thr(lproc->getThreadDBAgent(), + lwp, &threadHandle); + if( TD_OK != errVal ) { + perr_printf("Failed to map lwp to thread_db thread: %s(%d)\n", + tdErr2Str(errVal), errVal); + setLastError(err_internal, "Failed to get thread_db thread handle"); + return Event::ptr(); + } + + td_event_msg_t eventMsg; + + errVal = td_thr_event_getmsg(&threadHandle, &eventMsg); + + if( TD_NOMSG == errVal ) { + pthrd_printf("No message available for LWP %d via thread_db\n", lwp); + return Event::ptr(); + } + + if( TD_OK != errVal ) { + perr_printf("Failed to get thread event message: %s(%d)\n", + tdErr2Str(errVal), errVal); + setLastError(err_internal, "Failed to get thread event message"); + return Event::ptr(); + } + + return decodeThreadEvent(&eventMsg); +} + +bool thread_db_thread::setEventReporting(bool on) { + thread_db_process *lproc = static_cast(proc_); + td_thrhandle_t threadHandle; + + // Get the thread handle + td_err_e errVal = td_ta_map_lwp2thr(lproc->getThreadDBAgent(), + lwp, &threadHandle); + if( TD_OK != errVal ) { + perr_printf("Failed to map lwp to thread_db thread: %s(%d)\n", + tdErr2Str(errVal), errVal); + setLastError(err_internal, "Failed to get thread_db thread handle"); + return false; + } + + errVal = td_thr_event_enable(&threadHandle, (on ? 1 : 0 )); + + if( TD_OK != errVal ) { + perr_printf("Failed to enable events for LWP %d: %s(%d)\n", + lwp, tdErr2Str(errVal), errVal); + setLastError(err_internal, "Failed to enable thread_db events"); + return false; + } + + pthrd_printf("Enabled thread_db events for LWP %d\n", lwp); + + return true; +} diff --git a/proccontrol/src/int_thread_db.h b/proccontrol/src/int_thread_db.h new file mode 100644 index 0000000..57364ea --- /dev/null +++ b/proccontrol/src/int_thread_db.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1996-2009 Barton P. Miller + * + * We provide the Paradyn Parallel Performance Tools (below + * described as "Paradyn") on an AS IS basis, and do not warrant its + * validity or performance. We reserve the right to update, modify, + * or discontinue this software at any time. We shall have no + * obligation to supply such updates or modifications or any other + * form of support to you. + * + * By your use of Paradyn, you understand and agree that we (or any + * other person or entity with proprietary rights in Paradyn) are + * under no obligation to provide either maintenance services, + * update services, notices of latent defects, or correction of + * defects for Paradyn. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#if !defined(INT_THREAD_DB_H_) +#define INT_THREAD_DB_H + +#include "proccontrol/h/Generator.h" +#include "proccontrol/h/Event.h" +#include "proccontrol/h/Decoder.h" +#include "proccontrol/h/Handler.h" +#include "proccontrol/src/int_process.h" +#include "proccontrol/src/sysv.h" +#include "proccontrol/src/int_handler.h" + +#include +#include + +#include +using std::map; +using std::pair; + +#include +using std::vector; + +#include +using std::string; + +using namespace Dyninst; +using namespace ProcControlAPI; + +class thread_db_thread; + +class thread_db_process : public sysv_process +{ +public: + thread_db_process(Dyninst::PID p, std::string e, std::vector a); + thread_db_process(Dyninst::PID pid_, int_process *p); + virtual ~thread_db_process(); + + bool getEventsAtAddr(Dyninst::Address addr, thread_db_thread *eventThread, + vector &threadEvents); + + /* helper functions for thread_db interactions */ + + td_thragent_t *getThreadDBAgent(); + ps_err_e getSymbolAddr(const char *objName, const char *symName, + psaddr_t *symbolAddr); + virtual bool initThreadDB(); + static void addThreadDBHandlers(HandlerPool *hpool); + + /* + * When creating a static executable or attaching to a new process, + * thread_db initialization needs to occur immediately after + * attach or create. + * + * When creating dynamic executables, initialization needs to happen + * when the thread library is loaded. + */ + virtual bool post_attach(); + virtual bool post_create(); + + virtual bool plat_readProcMem(void *local, + Dyninst::Address remote, size_t size) = 0; + virtual bool plat_writeProcMem(void *local, + Dyninst::Address remote, size_t size) = 0; + virtual bool plat_getLWPInfo(lwpid_t lwp, void *lwpInfo) = 0; + virtual bool plat_contThread(lwpid_t lwp) = 0; + virtual bool plat_stopThread(lwpid_t lwp) = 0; + virtual string getThreadLibName(const char *symName) = 0; + virtual bool isSupportedThreadLib(const string &libName) = 0; + +protected: + static volatile bool thread_db_initialized; + + map > addr2Event; + map > symReaders; + td_thragent_t *threadAgent; + + struct ps_prochandle *self; +}; + +/* + * libthread_db defines this as opaque. We need to implement it. + */ +struct ps_prochandle { + thread_db_process *thread_db_proc; +}; + +class thread_db_thread : public int_thread +{ +public: + thread_db_thread(int_process *p, Dyninst::THR_ID t, Dyninst::LWP l); + + thread_db_thread(); + virtual ~thread_db_thread(); + + Event::ptr getThreadEvent(); + bool setEventReporting(bool on); +}; + +class ThreadDBHandleNewThr : public Handler +{ +public: + ThreadDBHandleNewThr(); + virtual ~ThreadDBHandleNewThr(); + virtual bool handleEvent(Event::ptr ev); + virtual int getPriority() const; + void getEventTypesHandled(std::vector &etypes); +}; + +class ThreadDBLibHandler : public Handler +{ +public: + ThreadDBLibHandler(); + virtual ~ThreadDBLibHandler(); + virtual bool handleEvent(Event::ptr ev); + virtual int getPriority() const; + void getEventTypesHandled(std::vector &etypes); +}; +#endif diff --git a/proccontrol/src/sysv.C b/proccontrol/src/sysv.C index 39e276e..21fe5c0 100644 --- a/proccontrol/src/sysv.C +++ b/proccontrol/src/sysv.C @@ -63,7 +63,6 @@ sysv_process::sysv_process(Dyninst::PID pid_, int_process *p) : int_process(pid_, p) { sysv_process *sp = static_cast(p); - loaded_libs = sp->loaded_libs; breakpoint_addr = sp->breakpoint_addr; lib_initialized = sp->lib_initialized; if (sp->procreader) @@ -252,7 +251,6 @@ bool sysv_process::plat_execed() procreader = NULL; } breakpoint_addr = 0x0; - loaded_libs.clear(); lib_initialized = false; return initLibraryMechanism(); } diff --git a/proccontrol/src/sysv.h b/proccontrol/src/sysv.h index 0295c22..7815f24 100644 --- a/proccontrol/src/sysv.h +++ b/proccontrol/src/sysv.h @@ -77,8 +77,6 @@ class sysv_process : public int_process virtual void plat_execv(); - private: - std::set loaded_libs; Address breakpoint_addr; AddressTranslate *translator; bool lib_initialized; -- 1.8.3.1