/* * See the dyninst/COPYRIGHT file for copyright information. * * We provide the Paradyn 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 "dyninstversion.h" #include "int_process.h" #include "irpc.h" #include "procpool.h" #include "int_handler.h" #include "response.h" #include "int_event.h" #include "processplat.h" #include "Mailbox.h" #include "PCErrors.h" #include "Generator.h" #include "Event.h" #include "Handler.h" #include "ProcessSet.h" #include "PlatFeatures.h" #if defined(os_windows) #include "windows_process.h" #include "windows_thread.h" #endif #include "loadLibrary/injector.h" #include #include #include #include #include #include #include #include using namespace Dyninst; using namespace ProcControlAPI; using namespace std; const map Process::emptyFDs; const vector Process::emptyEnvp; Process::thread_mode_t threadingMode = Process::GeneratorThreading; bool int_process::in_callback = false; std::set int_thread::continue_cbs; SymbolReaderFactory *int_process::user_set_symbol_reader = NULL; static const int ProcControl_major_version = DYNINST_MAJOR_VERSION; static const int ProcControl_minor_version = DYNINST_MINOR_VERSION; static const int ProcControl_maintenance_version = DYNINST_PATCH_VERSION; bool Dyninst::ProcControlAPI::is_restricted_ptrace = false; void Process::version(int& major, int& minor, int& maintenance) { major = ProcControl_major_version; minor = ProcControl_minor_version; maintenance = ProcControl_maintenance_version; } bool int_process::create(int_processSet *ps) { bool had_error = false; set procs; transform(ps->begin(), ps->end(), inserter(procs, procs.end()), ProcToIntProc()); //Should be called with procpool lock held pthrd_printf("Calling plat_create for %d processes\n", (int) procs.size()); for (set::iterator i = procs.begin(); i != procs.end(); ) { int_process *proc = *i; bool result = proc->plat_create(); if (!result) { pthrd_printf("Could not create debuggee, %s\n", proc->executable.c_str()); proc->setLastError(err_noproc, "Could not create process"); i = procs.erase(i); had_error = true; continue; } i++; } pthrd_printf("Creating initial threads for %d processes\n", (int) procs.size()); for (set::iterator i = procs.begin(); i != procs.end(); i++) { int_process *proc = *i; // Because yo, windows processes have threads when they're created... if (proc->threadPool()->empty()) { int_thread::createThread(proc, NULL_THR_ID, NULL_LWP, true, int_thread::as_created_attached); } ProcPool()->addProcess(proc); proc->setState(neonatal_intermediate); pthrd_printf("Created debugged %s on pid %d\n", proc->executable.c_str(), proc->pid); } ProcPool()->condvar()->broadcast(); ProcPool()->condvar()->unlock(); pthrd_printf("Waiting for startup for %d processes\n", (int) procs.size()); for (set::iterator i = procs.begin(); i != procs.end(); ) { int_process *proc = *i; string executable = proc->executable; Dyninst::PID pid = proc->pid; bool result = proc->waitfor_startup(); if (!result) { // At this point, proc has been deleted pthrd_printf("Process %s/%d exited during create\n", executable.c_str(), pid); i = procs.erase(i); had_error = true; continue; } i++; } pthrd_printf("Triggering post-create for %d processes\n", (int) procs.size()); while (!procs.empty()) { set async_responses; bool ret_async = false; for (set::iterator i = procs.begin(); i != procs.end(); ) { int_process *proc = *i; async_ret_t result = proc->post_create(async_responses); if (result == aret_error) { pthrd_printf("Error during post create for %d\n", proc->pid); had_error = true; i = procs.erase(i); } else if (result == aret_success) { assert(proc->getState() == running); pthrd_printf("Finished post-create for %d. Process is ready\n", proc->pid); i = procs.erase(i); } else { pthrd_printf("post-create for %d return async\n", proc->pid); ret_async = true; i++; } } if (ret_async) { waitForAsyncEvent(async_responses); } } return !had_error; } bool int_process::waitfor_startup() { bool proc_exited = false; while (getState() != running) { if (proc_exited || getState() == exited) { pthrd_printf("Error. Process exited during create/attach\n"); globalSetLastError(err_exited, "Process exited during startup"); return false; } pthrd_printf("Waiting for startup to complete for %d\n", pid); bool result = waitAndHandleForProc(true, this, proc_exited); if (!proc_exited && (!result || getState() == errorstate)) { pthrd_printf("Error. Process %d errored during create/attach\n", pid); globalSetLastError(err_internal, "Process failed to startup"); return false; } if (proc_exited || getState() == exited) { pthrd_printf("Error. Proces exited during create/attach\n"); globalSetLastError(err_exited, "Process exited during startup"); return false; } if (getState() == errorstate) { pthrd_printf("Error. Process in error state\n"); globalSetLastError(err_internal, "Process errored during startup"); return false; } } return true; } void int_process::plat_threadAttachDone() { } bool int_process::attachThreads(bool &found_new_threads) { found_new_threads = false; if (!needIndividualThreadAttach()) return true; /** * This OS (linux) needs us to attach to each thread manually. * Get all the thread LWPs and create new thread objects for them. * After we have all the threads, check again to see if there are any * new ones. We're dealing with the race condition where we could get * a list of LWPs, but then new threads are created before we attach to * all the existing threads. **/ bool loop_new_threads; do { loop_new_threads = false; vector lwps; bool result = getThreadLWPs(lwps); if (!result) { pthrd_printf("Failed to get thread LWPs for %d\n", pid); return false; } for (vector::iterator i = lwps.begin(); i != lwps.end(); ++i) { int_thread *thr = threadpool->findThreadByLWP(*i); if (thr) { pthrd_printf("Already have thread %d in process %d\n", *i, pid); continue; } pthrd_printf("Creating new thread for %d/%d during attach\n", pid, *i); thr = int_thread::createThread(this, NULL_THR_ID, *i, false, int_thread::as_needs_attach); found_new_threads = loop_new_threads = true; } } while (loop_new_threads); return true; } bool int_process::attachThreads() { bool found_new_threads = false; return attachThreads(found_new_threads); } bool int_process::plat_attachThreadsSync() { // By default, platforms just call the idempotent attachThreads(). // Some platforms may override, e.g. Linux should sync with all threads. if (!attachThreads()) { pthrd_printf("Failed to attach to threads in %d\n", pid); setLastError(err_internal, "Could not get threads during attach\n"); return false; } return true; } bool int_process::attach(int_processSet *ps, bool reattach) { bool had_error = false, should_sync = false; set procs; vector observedEvents; set async_responses; transform(ps->begin(), ps->end(), inserter(procs, procs.end()), ProcToIntProc()); //Should be called with procpool lock held pthrd_printf("Calling plat_attach for %d processes\n", (int) procs.size()); map, bool> runningStates; for (set::iterator i = procs.begin(); i != procs.end();) { int_process *proc = *i; if (!proc) { i = procs.erase(i); continue; } if (reattach && proc->getState() != int_process::detached) { perr_printf("Attempted to reattach to attached process %d\n", proc->getPid()); proc->setLastError(err_attached, "Cannot reAttach to attached process.\n"); i = procs.erase(i); had_error = true; continue; } // Determine the running state of all threads before attaching map temp_runningStates; if (!proc->plat_getOSRunningStates(temp_runningStates)) { pthrd_printf("Could not get OS running states for %d\n", proc->getPid()); i = procs.erase(i); had_error = true; continue; } //Keep track of the initial running states for each thread. We'll fill them in latter // after we create the int_thread objects. bool allStopped = true; for(map::iterator j = temp_runningStates.begin(); j != temp_runningStates.end(); ++j) { if (j->second) { allStopped = false; } runningStates[pair(proc, j->first)] = j->second; } bool local_should_sync = false; pthrd_printf("Calling plat_attach for process %d\n", proc->getPid()); bool result = proc->plat_attach(allStopped, local_should_sync); if (!result) { pthrd_printf("Failed to plat_attach to %d\n", proc->getPid()); i = procs.erase(i); proc->setLastError(err_noproc, "Could not attach to process"); had_error = true; continue; } if (local_should_sync) should_sync = true; i++; } //Create the int_thread objects via attach_threads if (!reattach) { for (set::iterator i = procs.begin(); i != procs.end(); i++) { int_process *proc = *i; ProcPool()->addProcess(proc); int_thread::createThread(proc, NULL_THR_ID, NULL_LWP, true, int_thread::as_created_attached); //initial thread } } if (should_sync) { ProcPool()->condvar()->broadcast(); ProcPool()->condvar()->unlock(); for (;;) { bool have_neonatal = false; for (set::iterator i = procs.begin(); i != procs.end(); i++) { int_process *proc = *i; if (proc->getState() == neonatal) { have_neonatal = true; break; } } if (!have_neonatal) break; bool result = waitAndHandleEvents(true); if (!result) { pthrd_printf("Error during waitAndHandleEvents during attach\n"); return false; } } ProcPool()->condvar()->lock(); } else { pthrd_printf("Attach done, moving processes to neonatal_intermediate\n"); for (set::iterator i = procs.begin(); i != procs.end(); i++) { int_process *proc = *i; proc->setState(neonatal_intermediate); } } for (set::iterator i = procs.begin(); i != procs.end(); ) { int_process *proc = *i; if (proc->getState() == errorstate) { pthrd_printf("Removing process %d in error state\n", proc->getPid()); i = procs.erase(i); had_error = true; continue; } pthrd_printf("Attaching to threads for %d\n", proc->getPid()); bool result = proc->attachThreads(); if (!result) { pthrd_printf("Failed to attach to threads in %d\n", proc->pid); i = procs.erase(i); had_error = true; continue; } if (reattach) { // Now, go back and set non-existing threads to detached to exclude them // from the bootstrap handling, creating thread destruction events for // these non-existing threads // // Also, at the same time issue attaches to existing threads int_threadPool *tp = proc->threadPool(); for(int_threadPool::iterator j = tp->begin(); j != tp->end(); j++) { int_thread *thr = *j; if (thr->getDetachState().getState() != int_thread::detached) { //Small hack: This thread is new since we're reattaching. Every old //thread has it's DetachState in 'detached', and we'd like to unset //those for the entire process. Temporarily set this thread to detached //so that it gets properly restored with the rest. thr->getDetachState().desyncState(int_thread::detached); continue; } pair key(proc, thr->getLWP()); map, bool>::iterator findIter = runningStates.find(key); pthrd_printf("Re-attaching to thread %d/%d\n", proc->getPid(), thr->getLWP()); if (findIter == runningStates.end() || !thr->attach()) { pthrd_printf("Creating thread destroy event for thread %d/%d\n", proc->getPid(), thr->getLWP()); thr->getGeneratorState().setState(int_thread::detached); thr->getHandlerState().setState(int_thread::detached); thr->getUserState().setState(int_thread::detached); thr->getDetachState().setState(int_thread::detached); Event::ptr destroyEv; if (proc->plat_supportLWPPostDestroy()) { destroyEv = Event::ptr(new EventLWPDestroy(EventType::Post)); } else if (proc->plat_supportThreadEvents()) { destroyEv = Event::ptr(new EventUserThreadDestroy(EventType::Post)); } else { perr_printf("Platform does not support any thread destroy events. Now what?\n"); assert(0); } destroyEv->setProcess(proc->proc()); destroyEv->setThread(thr->thread()); destroyEv->setSyncType(Event::async); destroyEv->setUserEvent(true); observedEvents.push_back(destroyEv); } } proc->threadPool()->initialThread()->getDetachState().restoreStateProc(); } i++; } ProcPool()->condvar()->broadcast(); ProcPool()->condvar()->unlock(); //Wait for each process to make it to the 'running' state. for (set::iterator i = procs.begin(); i != procs.end(); ) { int_process *proc = *i; pthrd_printf("Wait for attach from process %d\n", proc->pid); bool result = proc->waitfor_startup(); if (!result) { pthrd_printf("Error waiting for attach to %d\n", proc->pid); i = procs.erase(i); had_error = true; continue; } i++; } //Some OSs need to do their attachThreads here. Since the operation is supposed to be //idempotent after success, then just do it again. for (set::iterator i = procs.begin(); i != procs.end(); ) { int_process *proc = *i; if (proc->getState() == errorstate) continue; bool result = proc->plat_attachThreadsSync(); if (!result) { pthrd_printf("Failed to attach to threads in %d\n", proc->pid); i = procs.erase(i); had_error = true; continue; } // Now that all the threads are created, set their running states int_threadPool *tp = proc->threadPool(); for(int_threadPool::iterator j = tp->begin(); j != tp->end(); j++) { int_thread *thr = *j; pair key(proc, thr->getLWP()); map, bool>::iterator findIter = runningStates.find(key); // There is a race that could be visible here where we are not // guaranteed to determine the running state of all threads in a process // before we attach -- if for some reason we don't know the running // state, assume it was running thr->setRunningWhenAttached(findIter == runningStates.end() ? true : findIter->second); } pthrd_printf("Thread attach is done for process %d\n", proc->getPid()); proc->plat_threadAttachDone(); i++; } pthrd_printf("Triggering post-attach for %d processes\n", (int) procs.size()); std::set pa_procs = procs; while (!pa_procs.empty()) { async_responses.clear(); bool ret_async = false; for (set::iterator i = pa_procs.begin(); i != pa_procs.end(); ) { int_process *proc = *i; async_ret_t result = proc->post_attach(false, async_responses); if (result == aret_error) { pthrd_printf("Error during post attach for %d\n", proc->pid); had_error = true; i = pa_procs.erase(i); } else if (result == aret_success) { assert(proc->getState() == running); pthrd_printf("Finished post-attach for %d. Process is ready\n", proc->pid); i = pa_procs.erase(i); } else { pthrd_printf("post-attach for %d return async\n", proc->pid); ret_async = true; i++; } } if (ret_async) { waitForAsyncEvent(async_responses); } } // //Everything below this point is targeted at DOTF reattach-- //reinstalling BPs and throwing dead thread events. if (!reattach) return !had_error; async_responses.clear(); for (set::iterator i = procs.begin(); i != procs.end(); i++) { // Resume all breakpoints int_process *proc = *i; for(std::map::iterator j = proc->mem->breakpoints.begin(); j != proc->mem->breakpoints.end(); j++) { pthrd_printf("Resuming breakpoint at 0x%lx in process %d\n", j->first, proc->getPid()); bool result = j->second->resume(proc, async_responses); if(!result) { pthrd_printf("Error resuming breakpoint at %lx\n", j->first); //Drop this error. The library containing this BP was likely unloaded. } } } waitForAsyncEvent(async_responses); // Report all events for observed process state changes for(vector::iterator i = observedEvents.begin(); i!= observedEvents.end(); i++) { int_thread *thrd = (*i)->getThread()->llthrd(); pthrd_printf("Queuing event %s for thread %d/%d\n", (*i)->getEventType().name().c_str(), (*i)->getProcess()->getPid(), (*i)->getThread()->getLWP()); // Make sure the thread is the correct state while in event handling thrd->getGeneratorState().setState(int_thread::detached); thrd->getHandlerState().setState(int_thread::detached); thrd->getUserState().setState(int_thread::detached); thrd->getDetachState().setState(int_thread::detached); mbox()->enqueue(*i); } if (!observedEvents.empty()) { // As a sanity check, don't block bool result = waitAndHandleEvents(false); if (!result) { perr_printf("Internal error in waitAndHandleEvents under reattach\n"); } } return !had_error; } bool int_process::reattach(int_processSet *pset) { return attach(pset, true); } bool int_process::execed() { ProcPool()->condvar()->lock(); bool should_clean = false; mem->rmProc(this, should_clean); if (should_clean) delete mem; mem = new mem_state(this); arch = Dyninst::Arch_none; exec_mem_cache.clear(); int_thread::State user_initial_thrd_state = threadpool->initialThread()->getUserState().getState(); int_thread::State gen_initial_thrd_state = threadpool->initialThread()->getGeneratorState().getState(); int_thread::State handler_initial_thrd_state = threadpool->initialThread()->getHandlerState().getState(); int_threadPool::iterator i = threadpool->begin(); for (; i != threadpool->end(); ++i) { int_thread *thrd = *i; thrd->getUserState().setState(int_thread::exited); thrd->getGeneratorState().setState(int_thread::exited); ProcPool()->rmThread(thrd); delete thrd; } threadpool->clear(); int_thread *initial_thread = int_thread::createThread(this, NULL_THR_ID, NULL_LWP, true, int_thread::as_created_attached); initial_thread->getUserState().setState(user_initial_thrd_state); initial_thread->getGeneratorState().setState(gen_initial_thrd_state); initial_thread->getHandlerState().setState(handler_initial_thrd_state); ProcPool()->condvar()->broadcast(); ProcPool()->condvar()->unlock(); bool result = plat_execed(); return result; } bool int_process::plat_execed() { return true; } bool int_process::forked() { ProcPool()->condvar()->lock(); pthrd_printf("Setting up forked process %d\n", pid); creation_mode = ct_fork; bool result = plat_forked(); if (!result) { pthrd_printf("Could not handle forked debuggee, %d\n", pid); return false; } int_thread *initial_thread; initial_thread = int_thread::createThread(this, NULL_THR_ID, NULL_LWP, true, int_thread::as_created_attached); (void)initial_thread; // suppress unused warning ProcPool()->addProcess(this); result = attachThreads(); if (!result) { pthrd_printf("Failed to attach to threads in %d\n", pid); setLastError(err_internal, "Could not attach to process' threads"); goto error; } ProcPool()->condvar()->broadcast(); ProcPool()->condvar()->unlock(); result = post_forked(); if (!result) { pthrd_printf("Post-fork failed on %d\n", pid); setLastError(err_internal, "Error handling forked process"); goto error; } return true; error: if (getState() == exited) { setLastError(err_exited, "Process exited unexpectedly during attach\n"); return false; } pthrd_printf("Error during process attach for %d\n", pid); return false; } bool int_process::post_forked() { setState(running); return true; } async_ret_t int_process::initializeAddressSpace(std::set &async_responses) { std::set added, rmd; //This call will determine and cache the appropriate symbol reader, or NOP // if that's already been done. getSymReader(); bool have_asyncs = false; bool result = refresh_libraries(added, rmd, have_asyncs, async_responses); if (!result && have_asyncs) { pthrd_printf("Postponing initializeAddressSpace of %d for async operations\n", getPid()); return aret_async; } if (!result) { pthrd_printf("Failure refreshing libraries for %d\n", getPid()); return aret_error; } pthrd_printf("Successfully initialized address space for %d\n", getPid()); return aret_success; } async_ret_t int_process::post_attach(bool, std::set &async_responses) { pthrd_printf("Starting post_attach for process %d\n", getPid()); return initializeAddressSpace(async_responses); } async_ret_t int_process::post_create(std::set &async_responses) { pthrd_printf("Starting post_create for process %d\n", getPid()); return initializeAddressSpace(async_responses); } bool int_process::plat_processGroupContinues() { return false; } bool int_process::getThreadLWPs(std::vector &) { return false; } const char *int_process::stateName(int_process::State s) { switch (s) { case neonatal: return "neonatal"; case neonatal_intermediate: return "neonatal_intermediate"; case detached: return "detached"; case running: return "running"; case exited: return "exited"; case errorstate: return "errorstate"; } assert(0); return NULL; } void int_process::setState(int_process::State s) { int old_state = (int) state; int new_state = (int) s; if (new_state < old_state && new_state != detached && old_state != detached) { perr_printf("Regression of state %s to %s on %d\n", stateName(state), stateName(s), pid); return; } pthrd_printf("Changing state of process from %s to %s on %d\n", stateName(state), stateName(s), pid); state = s; int_thread::State new_thr_state = int_thread::errorstate; switch (s) { case neonatal: new_thr_state = int_thread::neonatal; break; case neonatal_intermediate: new_thr_state = int_thread::neonatal_intermediate; break; case running: new_thr_state = threadpool->initialThread()->getHandlerState().getState(); break; case exited: new_thr_state = int_thread::exited; break; case detached: new_thr_state = int_thread::detached; break; case errorstate: new_thr_state = int_thread::errorstate; break; } pthrd_printf("Setting state of all threads in %d to %s\n", pid, int_thread::stateStr(new_thr_state)); for (int_threadPool::iterator i = threadpool->begin(); i != threadpool->end(); ++i) { (*i)->getUserState().setState(new_thr_state); (*i)->getHandlerState().setState(new_thr_state); (*i)->getGeneratorState().setState(new_thr_state); if(new_thr_state == int_thread::exited) { if((*i)->getPendingStopState().getState() != int_thread::dontcare) { (*i)->getPendingStopState().restoreState(); (*i)->pendingStopsCount().dec(); } } } } int_process::State int_process::getState() const { return state; } int_process::creationMode_t int_process::getCreationMode() const { return creation_mode; } void int_process::setContSignal(int sig) { continueSig = sig; } int int_process::getContSignal() const { return continueSig; } void int_process::setPid(Dyninst::PID p) { pthrd_printf("Setting int_process %p to pid %d\n", (void*)this, p); pid = p; } Dyninst::PID int_process::getPid() const { return pid; } int_threadPool *int_process::threadPool() const { return threadpool; } Process::ptr int_process::proc() const { return up_proc; } bool int_process::syncRunState() { pthrd_printf("At top of syncRunState for %d\n", getPid()); int_threadPool *tp = threadPool(); /** * We can have some events (currently BreakpointClear and RPCLaunch) that * get thrown when a thread is continued. We'll create and throw those * events here. * 1), we'll identify each thread that would be continued. We calculate * this ahead of time as the act of throwing one of these events on a thread * can change the running state of another thread. * 2), for each thread that would run, we check if it has one of these * events and throw it. * 3), Check if any proc stopper events are ready, and if so then re-throw them * 4), we redo the target_state calculation in-case anything changed. **/ //1) for (int_threadPool::iterator i = tp->begin(); i != tp->end(); i++) { int_thread *thr = *i; int_thread::State new_state = thr->getActiveState().getState(); if (new_state == int_thread::ditto) new_state = thr->getHandlerState().getState(); thr->setTargetState(new_state); } //2) for (int_threadPool::iterator i = tp->begin(); i != tp->end(); i++) { int_thread *thr = *i; if (!RUNNING_STATE(thr->getTargetState())) continue; if (thr->getActiveState().getID() == int_thread::PendingStopStateID) { //A pending-stop-state continue isn't a real continue. Don't throw // new events. continue; } thr->throwEventsBeforeContinue(); } //3) pthrd_printf("Checking if any ProcStop events on %d are ready\n", getPid()); getProcStopManager().checkEvents(); //4) for (int_threadPool::iterator i = tp->begin(); i != tp->end(); i++) { int_thread *thr = *i; int_thread::State new_state = thr->getActiveState().getState(); if (new_state == int_thread::ditto) new_state = thr->getHandlerState().getState(); thr->setTargetState(new_state); } if (dyninst_debug_proccontrol) { pc_print_lock(); pthrd_printf("Current Threading State for %d:\n", getPid()); for (int_threadPool::iterator i = tp->begin(); i != tp->end(); i++) { int_thread *thr = *i; int_thread::StateTracker &target = thr->getActiveState(); char state_code[int_thread::NumStateIDs+1]; for (int j = 0; j < int_thread::NumStateIDs; j++) { state_code[j] = int_thread::stateLetter(thr->getStateByID(j).getState()); } state_code[int_thread::NumStateIDs] = '\0'; pthrd_printf("%d/%d hand: %s, gen: %s, %s: %s, code: %s\n", getPid(), thr->getLWP(), int_thread::stateStr(thr->getHandlerState().getState()), int_thread::stateStr(thr->getGeneratorState().getState()), int_thread::stateIDToName(target.getID()).c_str(), int_thread::stateStr(target.getState()), state_code); } if(tp->empty()) { pthrd_printf("Threadpool for %d is empty\n", getPid()); } pc_print_unlock(); } pthrd_printf("Running plat_syncRunState on %d\n", getPid()); bool result = plat_syncRunState(); return result; } bool int_process::waitForAsyncEvent(response::ptr resp) { if (resp) { int_process *proc = resp->getProcess(); if (proc) proc->plat_preAsyncWait(); } return getResponses().waitFor(resp); } bool int_process::waitForAsyncEvent(std::set resp) { for (set::iterator i = resp.begin(); i != resp.end(); i++) { response::ptr r = *i; if (!r) continue; int_process *proc = r->getProcess(); assert(proc); proc->plat_preAsyncWait(); } bool has_error = false; for (set::iterator i = resp.begin(); i != resp.end(); i++) { bool result = getResponses().waitFor(*i); if (!result) has_error = true; } return !has_error; } Counter &int_process::asyncEventCount() { return async_event_count; } Counter &int_process::getForceGeneratorBlockCount() { return force_generator_block_count; } Counter &int_process::getStartupTeardownProcs() { return startupteardown_procs; } bool int_process::plat_waitAndHandleForProc() { return true; } int_process *int_process::in_waitHandleProc = NULL; bool int_process::waitAndHandleForProc(bool block, int_process *proc, bool &proc_exited) { assert(in_waitHandleProc == NULL); in_waitHandleProc = proc; if (!proc->plat_waitAndHandleForProc()) { perr_printf("Failed platform specific waitAndHandle for %d\n", proc->getPid()); return false; } bool result = waitAndHandleEvents(block); if (proc->getState() == int_process::exited) { pthrd_printf("Deleting proc %d from waitAndHandleForProc\n", proc->getPid()); delete proc; proc_exited = true; } else { proc_exited = false; } in_waitHandleProc = NULL; return result; } #define checkHandlerThread (hasHandlerThread = (int) isHandlerThread()) #define checkBlock (hasBlock = (int) block) #define checkGotEvent (hasGotEvent = (int) gotEvent) #define checkAsyncPending (hasAsyncPending = (int) HandlerPool::hasProcAsyncPending()) #define checkRunningThread (hasRunningThread = (int) Counter::global(Counter::HandlerRunningThreads)) #define checkClearingBP (hasClearingBP = (int) Counter::global(Counter::ClearingBPs)) #define checkStopPending (hasStopPending = (int) Counter::global(Counter::PendingStops)) #define checkSyncRPCRunningThrd (hasSyncRPCRunningThrd = (int) Counter::global(Counter::SyncRPCRunningThreads)) #define checkProcStopRPC (hasProcStopRPC = (int) Counter::global(Counter::ProcStopRPCs)) #define checkStartupTeardownProcs (hasStartupTeardownProc = (int) Counter::global(Counter::StartupTeardownProcesses)) #define checkNeonatalThreads (hasNeonatalThreads = (int) Counter::global(Counter::NeonatalThreads)) #define checkAsyncEvents (hasAsyncEvents = (int) Counter::global(Counter::AsyncEvents)) #define UNSET_CHECK -8 #define printCheck(VAL) (((int) VAL) == UNSET_CHECK ? '?' : (VAL ? 'T' : 'F')) bool int_process::waitAndHandleEvents(bool block) { pthrd_printf("Top of waitAndHandleEvents. Block = %s\n", block ? "true" : "false"); bool gotEvent = false; assert(!int_process::in_callback); bool error = false; static bool recurse = false; assert(!recurse); recurse = true; for (;;) { /** * Check for possible blocking combinations using Counters * * The weirdness in the #defines and variables here is to record the result of * each check, which is used if debug printing is turned on, while also letting the * C++ partial evaluator not check parts this expression (as the checks involve an * expensive locking operation). * * For most people, just ignore the #defines. The important bit is that we're really * calling the Counter::global(...) function to determine whether we should block. **/ int hasHandlerThread = UNSET_CHECK, hasAsyncPending = UNSET_CHECK, hasRunningThread = UNSET_CHECK; int hasClearingBP = UNSET_CHECK, hasStopPending = UNSET_CHECK, hasSyncRPCRunningThrd = UNSET_CHECK; int hasProcStopRPC = UNSET_CHECK, hasBlock = UNSET_CHECK, hasGotEvent = UNSET_CHECK; int hasStartupTeardownProc = UNSET_CHECK, hasNeonatalThreads = UNSET_CHECK, hasAsyncEvents = UNSET_CHECK; bool should_block = (!checkHandlerThread && ((checkBlock && !checkGotEvent && checkRunningThread) || (checkSyncRPCRunningThrd) || (checkStopPending) || (checkClearingBP) || (checkProcStopRPC) || (checkAsyncPending) || (checkStartupTeardownProcs) || (checkNeonatalThreads) || (checkAsyncEvents) ) ); //Entry for this print match the above tests in order and one-for-one. pthrd_printf("%s for events = !%c && ((%c && !%c && %c) || %c || %c || %c || %c || %c || %c || %c || %c)\n", should_block ? "Blocking" : "Polling", printCheck(hasHandlerThread), printCheck(hasBlock), printCheck(hasGotEvent), printCheck(hasRunningThread), printCheck(hasSyncRPCRunningThrd), printCheck(hasStopPending), printCheck(hasClearingBP), printCheck(hasProcStopRPC), printCheck(hasAsyncPending), printCheck(hasStartupTeardownProc), printCheck(hasNeonatalThreads), printCheck(hasAsyncEvents)); //TODO: If/When we move to per-process locks, then we'll need a smarter should_block check // We don't want the should_block changing between the above measurement // and the below dequeue. Perhaps dequeue should alway poll, and the user thread loops // over it while (should_block == true), with a condition variable signaling when the // should_block would go to false (so we don't just spin). if (should_block && Counter::globalCount(Counter::ForceGeneratorBlock)) { // Entirely possible we didn't continue anything, but we want the generator to // wake up anyway ProcPool()->condvar()->broadcast(); } Event::ptr ev = mbox()->dequeue(should_block); if (ev == Event::ptr()) { if (gotEvent) { pthrd_printf("Returning after handling events\n"); goto done; } if (should_block) { perr_printf("Blocking wait failed to get events\n"); ProcControlAPI::globalSetLastError(err_internal, "Blocking wait returned without events"); error = true; goto done; } if (hasHandlerThread) { pthrd_printf("Handler thread found nothing to do\n"); goto done; } if (!hasRunningThread) { pthrd_printf("No threads are running to produce events\n"); ProcControlAPI::globalSetLastError(err_notrunning, "No threads are running to produce events\n"); error = true; goto done; } ProcControlAPI::globalSetLastError(err_noevents, "Poll failed to find events"); pthrd_printf("Poll failed to find events\n"); error = true; goto done; } if (mt()->getThreadMode() == Process::NoThreads || mt()->getThreadMode() == Process::GeneratorThreading) { pthrd_printf("Clearing event from pipe after dequeue\n"); notify()->clearEvent(); } gotEvent = true; bool terminating = (ev->getProcess()->isTerminated()); bool exitEvent = (ev->getEventType().time() == EventType::Post && ev->getEventType().code() == EventType::Exit); Process::const_ptr proc = ev->getProcess(); int_process *llproc = proc->llproc(); if (terminating) { if(!exitEvent || !llproc) { // Since the user will never handle this one... pthrd_printf("Received event %s on terminated process, ignoring\n", ev->name().c_str()); if (!isHandlerThread() && ev->noted_event) notify()->clearEvent(); continue; } } HandlerPool *hpool = llproc->handlerpool; if (!ev->handling_started) { llproc->updateSyncState(ev, false); llproc->noteNewDequeuedEvent(ev); ev->handling_started = true; } llproc->plat_preHandleEvent(); bool should_handle_ev = llproc->getProcStopManager().prepEvent(ev); if (should_handle_ev) { hpool->handleEvent(ev); } llproc = proc->llproc(); if (llproc) { bool result = llproc->syncRunState(); if (!result) { pthrd_printf("syncRunState failed. Returning error from waitAndHandleEvents\n"); error = true; goto done; } llproc->plat_postHandleEvent(); } else { //Special case event handling, the process cleaned itself // under this event (likely post-exit or post-crash), but was // unable to clean its handlerpool (as we were using it). // Clean this for the process now. pthrd_printf("Process is gone, skipping syncRunState and deleting handler pool\n"); delete hpool; } } done: pthrd_printf("Leaving WaitAndHandleEvents with return %s, 'cause we're done\n", !error ? "true" : "false"); recurse = false; return !error; } void int_process::throwDetachEvent(bool temporary, bool leaveStopped) { pthrd_printf("%s detaching from process %d\n", temporary ? "Temporary" : "Permanent", getPid()); EventDetach::ptr detach_ev = EventDetach::ptr(new EventDetach()); detach_ev->getInternal()->temporary_detach = temporary; detach_ev->getInternal()->leave_stopped = leaveStopped; detach_ev->setProcess(proc()); detach_ev->setThread(threadPool()->initialThread()->thread()); detach_ev->setSyncType(Event::async); getStartupTeardownProcs().inc(); threadPool()->initialThread()->getDetachState().desyncStateProc(int_thread::stopped); mbox()->enqueue(detach_ev); } bool int_process::plat_detachDone() { return true; } bool int_process::terminate(bool &needs_sync) { //Should be called with the ProcPool lock held. pthrd_printf("Terminate requested on process %d\n", getPid()); getStartupTeardownProcs().inc(); bool result = plat_terminate(needs_sync); if (!result) { pthrd_printf("plat_terminate failed on %d\n", getPid()); return false; } forcedTermination = true; // On windows this leads to doubling up events #if defined(os_windows) // If we're on windows, we want to force the generator thread into waiting for a debug // event _if the process is stopped_. If the process is running then we're already // waiting (or will wait) for a debug event, and forcing the generator to block may // lead to doubling up. // The following code is similar to GeneratorWindows::hasLiveProc. This is not a coincidence. bool all_stopped = true; for (int_threadPool::iterator iter = threadpool->begin(); iter != threadpool->end(); ++iter) { if (RUNNING_STATE((*iter)->getActiveState().getState())) { all_stopped = false; break; } } if (all_stopped) { setForceGeneratorBlock(true); } #else // Do it all the time setForceGeneratorBlock(true); #endif return true; } bool int_process::preTerminate() { return true; } int_libraryTracking *int_process::getLibraryTracking() { if (LibraryTracking_set) return pLibraryTracking; LibraryTracking_set = true; pLibraryTracking = dynamic_cast(this); if (!pLibraryTracking) return NULL; if (!pLibraryTracking->up_ptr) pLibraryTracking->up_ptr = new LibraryTracking(proc()); return pLibraryTracking; } int_LWPTracking *int_process::getLWPTracking() { if (LWPTracking_set){ return pLWPTracking; } LWPTracking_set = true; pLWPTracking = dynamic_cast(this); if (!pLWPTracking) return NULL; if (!pLWPTracking->up_ptr) pLWPTracking->up_ptr = new LWPTracking(proc()); return pLWPTracking; } int_threadTracking *int_process::getThreadTracking() { if (ThreadTracking_set) return pThreadTracking; ThreadTracking_set = true; pThreadTracking = dynamic_cast(this); if (!pThreadTracking) return NULL; if (!pThreadTracking->up_ptr) pThreadTracking->up_ptr = new ThreadTracking(proc()); return pThreadTracking; } int_followFork *int_process::getFollowFork() { if (FollowFork_set) return pFollowFork; FollowFork_set = true; pFollowFork = dynamic_cast(this); if (!pFollowFork) return NULL; if (!pFollowFork->up_ptr) pFollowFork->up_ptr = new FollowFork(proc()); return pFollowFork; } int_callStackUnwinding *int_process::getCallStackUnwinding() { if (CallStackUnwinding_set) return pCallStackUnwinding; CallStackUnwinding_set = true; pCallStackUnwinding = dynamic_cast(this); return pCallStackUnwinding; } int_multiToolControl *int_process::getMultiToolControl() { if (MultiToolControl_set) return pMultiToolControl; MultiToolControl_set = true; pMultiToolControl = dynamic_cast(this); if (!pMultiToolControl) return NULL; if (!pMultiToolControl->up_ptr) pMultiToolControl->up_ptr = new MultiToolControl(proc()); return pMultiToolControl; } int_memUsage *int_process::getMemUsage() { if (MemUsage_set) return pMemUsage; MemUsage_set = true; pMemUsage = dynamic_cast(this); if (!pMemUsage) return NULL; if (!pMemUsage->up_ptr) pMemUsage->up_ptr = new MemoryUsage(proc()); return pMemUsage; } int_signalMask *int_process::getSignalMask() { if (SignalMask_set) return pSignalMask; SignalMask_set = true; pSignalMask = dynamic_cast(this); if (!pSignalMask) return NULL; if (!pSignalMask->up_ptr) pSignalMask->up_ptr = new SignalMask(proc()); return pSignalMask; } int_remoteIO *int_process::getRemoteIO() { if (remoteIO_set) return pRemoteIO; remoteIO_set = true; pRemoteIO = dynamic_cast(this); if (!pRemoteIO) return NULL; if (!pRemoteIO->up_ptr) pRemoteIO->up_ptr = new RemoteIO(proc()); return pRemoteIO; } int_process::int_process(Dyninst::PID p, std::string e, std::vector a, std::vector envp, std::map f) : state(neonatal), pid(p), creation_mode(ct_launch), executable(e), argv(a), env(envp), fds(f), arch(Dyninst::Arch_none), threadpool(NULL), up_proc(Process::ptr()), handlerpool(NULL), hasCrashSignal(false), crashSignal(0), hasExitCode(false), forcedTermination(false), silent_mode(false), exitCode(0), mem(NULL), continueSig(0), mem_cache(this), async_event_count(Counter::AsyncEvents), force_generator_block_count(Counter::ForceGeneratorBlock), startupteardown_procs(Counter::StartupTeardownProcesses), proc_stop_manager(this), user_data(NULL), last_error_string(NULL), symbol_reader(NULL), pLibraryTracking(NULL), pLWPTracking(NULL), pThreadTracking(NULL), pFollowFork(NULL), pMultiToolControl(NULL), pSignalMask(NULL), pCallStackUnwinding(NULL), pMemUsage(NULL), pRemoteIO(NULL), LibraryTracking_set(false), LWPTracking_set(false), ThreadTracking_set(false), FollowFork_set(false), MultiToolControl_set(false), SignalMask_set(false), CallStackUnwinding_set(false), MemUsage_set(false), remoteIO_set(false) { pthrd_printf("New int_process at %p\n", (void*)this); clearLastError(); wasCreatedViaAttach(pid == 0); //Put any object initialization in 'initializeProcess', below. } int_process::int_process(Dyninst::PID pid_, int_process *p) : state(int_process::running), pid(pid_), creation_mode(ct_attach), executable(p->executable), argv(p->argv), env(p->env), arch(p->arch), hasCrashSignal(p->hasCrashSignal), crashSignal(p->crashSignal), hasExitCode(p->hasExitCode), forcedTermination(false), silent_mode(false), exitCode(p->exitCode), continueSig(p->continueSig), mem_cache(this), async_event_count(Counter::AsyncEvents), force_generator_block_count(Counter::ForceGeneratorBlock), startupteardown_procs(Counter::StartupTeardownProcesses), proc_stop_manager(this), user_data(NULL), last_error_string(NULL), symbol_reader(NULL), pLibraryTracking(NULL), pLWPTracking(NULL), pThreadTracking(NULL), pFollowFork(NULL), pMultiToolControl(NULL), pSignalMask(NULL), pCallStackUnwinding(NULL), pMemUsage(NULL), pRemoteIO(NULL), LibraryTracking_set(false), LWPTracking_set(false), ThreadTracking_set(false), FollowFork_set(false), MultiToolControl_set(false), SignalMask_set(false), CallStackUnwinding_set(false), MemUsage_set(false), remoteIO_set(false) { pthrd_printf("New int_process at %p\n", (void*)this); Process::ptr hlproc = Process::ptr(new Process()); clearLastError(); mem = new mem_state(*p->mem, this); initializeProcess(hlproc); } void int_process::initializeProcess(Process::ptr p) { assert(!p->llproc_); p->llproc_ = this; up_proc = p; threadpool = new int_threadPool(this); handlerpool = createDefaultHandlerPool(this); libpool.proc = this; if (!mem) mem = new mem_state(this); Generator::getDefaultGenerator(); //May create generator thread } int_thread *int_process::findStoppedThread() { int_thread *result = NULL; for (int_threadPool::iterator i = threadpool->begin(); i != threadpool->end(); ++i) { int_thread *thr = *i; if (thr->getHandlerState().getState() == int_thread::stopped) { result = thr; break; } } return result; } bool int_process::readMem(Dyninst::Address remote, mem_response::ptr result, int_thread *thr) { if (getAddressWidth() == 4) { Address old = remote; remote &= 0xffffffff; pthrd_printf("Address cropping for 32-bit: 0x%lx to 0x%lx\n", old, remote); } if (!thr && plat_needsThreadForMemOps()) { thr = findStoppedThread(); if (!thr) { setLastError(err_notstopped, "A thread must be stopped to read from memory"); perr_printf("Unable to find a stopped thread for read in process %d\n", getPid()); return false; } } result->setProcess(this); bool bresult; if (!plat_needsAsyncIO()) { pthrd_printf("Reading from remote memory %lx to %p, size = %lu on %d/%d\n", remote, (void*)result->getBuffer(), (unsigned long) result->getSize(), getPid(), thr ? thr->getLWP() : (Dyninst::LWP)(-1)); bresult = plat_readMem(thr, result->getBuffer(), remote, result->getSize()); if (!bresult) { perr_printf("plat_readMem failed!\n"); result->markError(); } result->setResponse(); int_eventAsyncIO *iev = result->getAsyncIOEvent(); if (iev) { pthrd_printf("Enqueueing new EventAsyncRead into mailbox on synchronous platform\n"); EventAsyncRead::ptr ev = EventAsyncRead::ptr(new EventAsyncRead(iev)); ev->setProcess(proc()); ev->setThread(threadPool()->initialThread()->thread()); ev->setSyncType(Event::async); mbox()->enqueue(ev); } } else { pthrd_printf("Async read from remote memory %lx to %p, size = %lu on %d/%d\n", remote, (void*)result->getBuffer(), (unsigned long) result->getSize(), getPid(), thr ? thr->getLWP() : (Dyninst::LWP)(-1)); getResponses().lock(); bresult = plat_readMemAsync(thr, remote, result); if (bresult) { getResponses().addResponse(result, this); } getResponses().unlock(); getResponses().noteResponse(); } return bresult; } bool int_process::writeMem(const void *local, Dyninst::Address remote, size_t size, result_response::ptr result, int_thread *thr, bp_write_t bp_write) { if (getAddressWidth() == 4) { Address old = remote; remote &= 0xffffffff; pthrd_printf("Address cropping for 32-bit: 0x%lx to 0x%lx\n", old, remote); } if (!thr && plat_needsThreadForMemOps()) { thr = findStoppedThread(); if (!thr) { setLastError(err_notstopped, "A thread must be stopped to write to memory"); perr_printf("Unable to find a stopped thread for write in process %d\n", getPid()); return false; } } result->setProcess(this); bool bresult; if (!plat_needsAsyncIO()) { pthrd_printf("Writing to remote memory %lx from %p, size = %lu on %d/%d\n", remote, local, (unsigned long) size, getPid(), thr ? thr->getLWP() : (Dyninst::LWP)(-1)); bresult = plat_writeMem(thr, local, remote, size, bp_write); if (!bresult) { result->markError(); } result->setResponse(bresult); int_eventAsyncIO *iev = result->getAsyncIOEvent(); if (iev) { pthrd_printf("Enqueueing new EventAsyncWrite into mailbox on synchronous platform\n"); EventAsyncWrite::ptr ev = EventAsyncWrite::ptr(new EventAsyncWrite(iev)); ev->setProcess(proc()); ev->setThread(threadPool()->initialThread()->thread()); ev->setSyncType(Event::async); mbox()->enqueue(ev); } } else { pthrd_printf("Async writing to remote memory %lx from %p, size = %lu on %d/%d\n", remote, local, (unsigned long) size, getPid(), thr ? thr->getLWP() : (Dyninst::LWP)(-1)); getResponses().lock(); bresult = plat_writeMemAsync(thr, local, remote, size, result, bp_write); if (bresult) { getResponses().addResponse(result, this); } getResponses().unlock(); getResponses().noteResponse(); } return bresult; } unsigned int_process::plat_getRecommendedReadSize() { return getTargetPageSize(); } Dyninst::Address int_process::mallocExecMemory(unsigned size) { Dyninst::Address max = 0; std::map::iterator i; for (i = exec_mem_cache.begin(); i != exec_mem_cache.end(); ++i) { if (i->first + i->second > max) max = i->first + i->second; } Dyninst::Address addr = plat_mallocExecMemory(max, size); exec_mem_cache[addr] = size; return addr; } void int_process::freeExecMemory(Dyninst::Address addr) { std::map::iterator i; i = exec_mem_cache.find(addr); assert(i != exec_mem_cache.end()); exec_mem_cache.erase(i); } Dyninst::Address int_process::direct_infMalloc(unsigned long, bool, Dyninst::Address) { assert(0); return 0; } bool int_process::direct_infFree(Dyninst::Address) { assert(0); return false; } Address int_process::infMalloc(unsigned long size, bool use_addr, Address addr) { int_addressSet as; as.insert(make_pair(addr, proc())); bool result = int_process::infMalloc(size, &as, use_addr); if (!result) return 0; return as.begin()->first; } bool int_process::infFree(Address addr) { int_addressSet as; as.insert(make_pair(addr, proc())); return int_process::infFree(&as); } bool int_process::infMalloc(unsigned long size, int_addressSet *aset, bool use_addr) { bool had_error = false; set > active_mallocs; int_addressSet direct_results; for (int_addressSet::iterator i = aset->begin(); i != aset->end(); i++) { Process::ptr p = i->second; Address addr = i->first; if (!p) continue; int_process *proc = p->llproc(); if (proc->getState() == int_process::detached) continue; pthrd_printf("Process %d is allocating memory of size %lu at 0x%lx\n", proc->getPid(), size, addr); if (proc->plat_supportDirectAllocation()) { //Platforms like Windows don't need IRPCs for allocation. Just do it and put the results // in direct_results, which will be unioned with aset latter. Dyninst::Address addr_result = proc->direct_infMalloc(size, use_addr, addr); if (addr_result == 0) { had_error = true; continue; } direct_results.insert(make_pair(addr_result, p)); continue; } int_iRPC::ptr rpc = rpcMgr()->createInfMallocRPC(proc, size, use_addr, addr); assert(rpc); bool result = rpcMgr()->postRPCToProc(proc, rpc); if (!result) { p->setLastError(err_internal, "Error posting infMalloc RPC to process\n"); pthrd_printf("Error posting RPC to thread"); had_error = true; continue; } int_thread *thr = rpc->thread(); assert(thr); active_mallocs.insert(make_pair(proc, rpc)); thr->getInternalState().desyncState(int_thread::running); rpc->setRestoreInternal(true); proc->throwNopEvent(); } if (!active_mallocs.empty()) { bool result = int_process::waitAndHandleEvents(false); if (!result) { perr_printf("Internal error calling waitAndHandleEvents\n"); for_each(aset->begin(), aset->end(), setError(err_internal, "Error while calling waitAndHandleForProc from infMalloc\n")); return false; } } if (!use_addr) aset->clear(); if (!direct_results.empty()) aset->insert(direct_results.begin(), direct_results.end()); for (set >::iterator i = active_mallocs.begin(); i != active_mallocs.end(); i++) { int_process *proc = i->first; int_iRPC::ptr rpc = i->second; assert(rpc->getState() == int_iRPC::Finished); Dyninst::Address aresult = rpc->infMallocResult(); pthrd_printf("Inferior malloc returning %lx on %d\n", aresult, proc->getPid()); if (aresult == (unsigned long) -1) { perr_printf("infMalloc returned invalid address\n"); proc->setLastError(err_procread, "Unable to allocate memory at given address"); had_error = true; continue; } proc->memory()->inf_malloced_memory.insert(make_pair(aresult, size)); if (use_addr) continue; aset->insert(make_pair(aresult, proc->proc())); } return !had_error; } bool int_process::infFree(int_addressSet *aset) { bool had_error = false; set > active_frees; for (int_addressSet::iterator i = aset->begin(); i != aset->end(); i++) { Address addr = i->first; Process::ptr p = i->second; if (!p) { had_error = true; continue; } int_process *proc = p->llproc(); if (!proc || proc->getState() == int_process::detached) { had_error = true; continue; } std::map::iterator j = proc->mem->inf_malloced_memory.find(addr); if (j == proc->mem->inf_malloced_memory.end()) { proc->setLastError(err_badparam, "Unknown address passed to freeMemory"); perr_printf("Passed bad address, %lx, to infFree on %d\n", addr, proc->getPid()); had_error = true; continue; } unsigned long size = j->second; if (proc->plat_supportDirectAllocation()) { bool result = proc->direct_infFree(addr); if (!result) had_error = true; continue; } int_iRPC::ptr rpc = rpcMgr()->createInfFreeRPC(proc, size, addr); assert(rpc); pthrd_printf("Process %d is freeing memory of size %lu at 0x%lx with rpc %lu\n", proc->getPid(), size, addr, rpc->id()); bool result = rpcMgr()->postRPCToProc(proc, rpc); if (!result) { pthrd_printf("Failed to post free rpc to process\n"); had_error = true; continue; } int_thread *thr = rpc->thread(); assert(thr); thr->getInternalState().desyncState(int_thread::running); rpc->setRestoreInternal(true); proc->throwNopEvent(); active_frees.insert(make_pair(p, rpc)); } if (!active_frees.empty()) { bool result = int_process::waitAndHandleEvents(false); if (!result) { perr_printf("Internal error calling waitAndHandleEvents\n"); for_each(aset->begin(), aset->end(), setError(err_internal, "Error while calling waitAndHandleForProc from infFree\n")); return false; } } for (set >::iterator i = active_frees.begin(); i != active_frees.end(); i++) { Process::ptr p = i->first; int_iRPC::ptr rpc = i->second; int_process *proc = p->llproc(); if (!proc) { perr_printf("Process %d exited during infFree\n", p->getPid()); p->setLastError(err_exited, "Process exited during infFree\n"); had_error = true; continue; } assert(rpc->getState() == int_iRPC::Finished); Address addr = rpc->getInfFreeTarget(); map::iterator j = proc->mem->inf_malloced_memory.find(addr); proc->mem->inf_malloced_memory.erase(j); } return !had_error; } bool int_process::plat_decodeMemoryRights(Process::mem_perm& rights_internal, unsigned long rights) { (void)rights_internal; (void)rights; perr_printf("Called decodeMemoryRights on unspported platform\n"); setLastError(err_unsupported, "Decode Mem Permission not supported on this platform\n"); return false; } bool int_process::plat_encodeMemoryRights(Process::mem_perm rights_internal, unsigned long& rights) { (void)rights_internal; (void)rights; perr_printf("Called encodeMemoryRights on unspported platform\n"); setLastError(err_unsupported, "Encode Memory Permission not supported on this platform\n"); return false; } bool int_process::getMemoryAccessRights(Dyninst::Address addr, Process::mem_perm& rights) { if (!plat_getMemoryAccessRights(addr, rights)) { pthrd_printf("Error get rights from memory %lx on target process %d\n", addr, getPid()); return false; } return true; } bool int_process::plat_getMemoryAccessRights(Dyninst::Address addr, Process::mem_perm& rights) { (void)addr; (void)rights; perr_printf("Called getMemoryAccessRights on unspported platform\n"); setLastError(err_unsupported, "Get Memory Permission not supported on this platform\n"); return false; } bool int_process::setMemoryAccessRights(Dyninst::Address addr, size_t size, Process::mem_perm rights, Process::mem_perm& oldRights) { if (!plat_setMemoryAccessRights(addr, size, rights, oldRights)) { pthrd_printf("ERROR: set rights to %s from memory %lx on target process %d\n", rights.getPermName().c_str(), addr, getPid()); return false; } return true; } bool int_process::plat_setMemoryAccessRights(Dyninst::Address addr, size_t size, Process::mem_perm rights, Process::mem_perm& oldRights) { (void)addr; (void)size; (void)rights; (void)oldRights; perr_printf("Called setMemoryAccessRights on unspported platform\n"); setLastError(err_unsupported, "Set Memory Permission not supported on this platform\n"); return false; } bool int_process::findAllocatedRegionAround(Dyninst::Address addr, Process::MemoryRegion& memRegion) { if (!plat_findAllocatedRegionAround(addr, memRegion)) { pthrd_printf("Error when find allocated memory region" " for %lx on target process %d\n", addr, getPid()); return false; } return true; } bool int_process::plat_findAllocatedRegionAround(Dyninst::Address addr, Process::MemoryRegion& memRegion) { (void)addr; memRegion.first = 0; memRegion.second = 0; perr_printf("Called findAllocatedRegionAround on unspported platform\n"); setLastError(err_unsupported, "Find Allocated Region Addr not supported on this platform\n"); return false; } SymbolReaderFactory *int_process::getSymReader() { if (symbol_reader) { return symbol_reader; } else if (user_set_symbol_reader) { symbol_reader = user_set_symbol_reader; return symbol_reader; } symbol_reader = plat_defaultSymReader(); return symbol_reader; } void int_process::setSymReader(SymbolReaderFactory *fact) { symbol_reader = fact; } SymbolReaderFactory *int_process::plat_defaultSymReader() { //Default version of function for systems without symbol readers return NULL; } void int_process::setForceGeneratorBlock(bool b) { if (b) { force_generator_block_count.inc(); } else { if (force_generator_block_count.local()) force_generator_block_count.dec(); } pthrd_printf("forceGeneratorBlock - Count is now %d/%d\n", force_generator_block_count.localCount(), Counter::globalCount(Counter::ForceGeneratorBlock)); } int int_process::getAddressWidth() { switch (getTargetArch()) { case Arch_x86: case Arch_ppc32: case Arch_aarch32: return 4; case Arch_x86_64: case Arch_ppc64: case Arch_aarch64: case Arch_cuda: case Arch_intelGen9: return 8; case Arch_amdgpu_gfx908: case Arch_amdgpu_gfx90a: case Arch_amdgpu_gfx940: return 8; case Arch_none: assert(0); } return 0; } HandlerPool *int_process::handlerPool() const { return handlerpool; } bool int_process::addBreakpoint_phase1(bp_install_state *is) { is->ibp = NULL; map::iterator i = mem->breakpoints.find(is->addr); is->do_install = (i == mem->breakpoints.end()); if (!is->do_install) { is->ibp = i->second; assert(is->ibp && is->ibp->isInstalled()); bool result = is->ibp->addToIntBreakpoint(is->bp, this); if (!result) { pthrd_printf("Failed to install new breakpoint\n"); return false; } return true; } is->ibp = new sw_breakpoint(mem, is->addr); if (!is->ibp->checkBreakpoint(is->bp, this)) { pthrd_printf("Failed check breakpoint\n"); delete is->ibp; is->ibp = NULL; return false; } is->mem_resp = mem_response::createMemResponse(); is->mem_resp->markSyncHandled(); bool result = is->ibp->prepBreakpoint(this, is->mem_resp); if (!result) { pthrd_printf("Failed to prep breakpoint\n"); delete is->ibp; return false; } return true; } bool int_process::addBreakpoint_phase2(bp_install_state *is) { if (!is->do_install) return true; if (is->mem_resp->hasError()) { pthrd_printf("Error prepping breakpoint\n"); delete is->ibp; return false; } is->res_resp = result_response::createResultResponse(); is->res_resp->markSyncHandled(); bool result = is->ibp->insertBreakpoint(this, is->res_resp); if (!result) { pthrd_printf("Error writing new breakpoint\n"); delete is->ibp; return false; } return true; } bool int_process::addBreakpoint_phase3(bp_install_state *is) { if (!is->do_install) return true; bool result = is->ibp->addToIntBreakpoint(is->bp, this); if (!result) { pthrd_printf("Failed to install new breakpoint\n"); return false; } if (is->res_resp->hasError()) { pthrd_printf("Error writing new breakpoint\n"); delete is->ibp; return false; } return true; } bool int_process::addBreakpoint(Dyninst::Address addr, int_breakpoint *bp) { if (getState() != running) { perr_printf("Attempted to add breakpoint at %lx to exited process %d\n", addr, getPid()); setLastError(err_exited, "Attempted to insert breakpoint into exited process\n"); return false; } bp_instance *instance = NULL; if (bp->isHW()) { instance = hw_breakpoint::create(this, bp, addr); } else { instance = sw_breakpoint::create(this, bp, addr); } return (instance != NULL); } bool int_process::removeAllBreakpoints() { if (!mem) return true; bool ret = true; std::map::iterator iter = mem->breakpoints.begin(); while (iter != mem->breakpoints.end()) { std::set resps; // uninstall will call erase to remove the item, // so just keep calling begin() to iterate over all elements if (!iter->second->uninstall(this, resps)) { ret = false; break; } assert(resps.empty()); iter = mem->breakpoints.begin(); } return ret; } bool int_process::removeBreakpoint(Dyninst::Address addr, int_breakpoint *bp, set &resps) { pthrd_printf("Removing breakpoint at %lx in %d\n", addr, getPid()); set bps_to_remove; map::iterator i = mem->breakpoints.find(addr); if (i != mem->breakpoints.end()) { sw_breakpoint *swbp = i->second; assert(swbp && swbp->isInstalled()); if (swbp->containsIntBreakpoint(bp)) bps_to_remove.insert(static_cast(swbp)); } for (auto thr : *threadPool()) { hw_breakpoint *hwbp = thr->getHWBreakpoint(addr); if (hwbp && hwbp->containsIntBreakpoint(bp)) { bps_to_remove.insert(static_cast(hwbp)); } } if (bps_to_remove.empty()) { perr_printf("Attempted to removed breakpoint that isn't installed\n"); setLastError(err_notfound, "Tried to uninstall breakpoint that isn't installed.\n"); return false; } for (auto ibp : bps_to_remove) { bool empty; bool result = ibp->rmBreakpoint(this, bp, empty, resps); if (!result) { pthrd_printf("rmBreakpoint failed on breakpoint at %lx in %d\n", addr, getPid()); return false; } if (empty) { delete ibp; } } return true; } sw_breakpoint *int_process::getBreakpoint(Dyninst::Address addr) { std::map::iterator i = mem->breakpoints.find(addr); if (i == mem->breakpoints.end()) return NULL; return i->second; } int_library *int_process::getLibraryByName(std::string s) const { // Exact matches first, but find substring matches and return if unique. // TODO: is this the behavior we actually want? bool substring_unique = true; std::set::iterator substring_match = mem->libs.end(); for (set::iterator i = mem->libs.begin(); i != mem->libs.end(); ++i) { std::string n = (*i)->getName(); if (s == n) return *i; if((n.find(s) != std::string::npos)) { if(substring_match == mem->libs.end()) { substring_match = i; } else { substring_unique = false; } } } if(substring_match != mem->libs.end() && substring_unique) { return *substring_match; } return NULL; } unsigned int int_process::plat_getCapabilities() { return (Process::pc_read | Process::pc_write | Process::pc_irpc | Process::pc_control); } Event::ptr int_process::plat_throwEventsBeforeContinue(int_thread *) { return Event::ptr(); } bool int_process::plat_threadOpsNeedProcStop() { return false; } size_t int_process::numLibs() const { return mem->libs.size(); } std::string int_process::getExecutable() const { return executable; //The name of the exec passed to PC } bool int_process::isInCallback() { return in_callback; } mem_state::ptr int_process::memory() const { return mem; } err_t int_process::getLastError() { return last_error; } const char *int_process::getLastErrorMsg() { return last_error_string; } void int_process::clearLastError() { last_error = err_none; last_error_string = "ok"; } void int_process::setLastError(err_t err, const char *str) { last_error = err; last_error_string = str; ProcControlAPI::globalSetLastError(err, str); } void int_process::setExitCode(int c) { assert(!hasCrashSignal); hasExitCode = true; exitCode = c; } void int_process::setCrashSignal(int s) { assert(!hasExitCode); hasCrashSignal = true; crashSignal = s; } bool int_process::getExitCode(int &c) { c = exitCode; return hasExitCode; } bool int_process::getCrashSignal(int &s) { s = crashSignal; return hasCrashSignal; } bool int_process::wasForcedTerminated() const { return forcedTermination; } bool int_process::plat_individualRegRead(Dyninst::MachRegister, int_thread *) { return plat_individualRegAccess(); } bool int_process::plat_individualRegSet() { return plat_individualRegAccess(); } bool int_process::isInCB() { return in_callback; } void int_process::setInCB(bool b) { assert(in_callback == !b); in_callback = b; } void int_process::throwNopEvent() { EventNop::ptr ev = EventNop::ptr(new EventNop()); ev->setProcess(proc()); ev->setThread(threadPool()->initialThread()->thread()); ev->setSyncType(Event::async); mbox()->enqueue(ev); } async_ret_t int_process::plat_calcTLSAddress(int_thread *, int_library *, Offset, Address &, std::set &) { perr_printf("Unsupported access to plat_calcTLSAddress\n"); setLastError(err_unsupported, "TLS Access not supported on this platform\n"); return aret_error; } bool int_process::plat_needsAsyncIO() const { return false; } bool int_process::plat_readMemAsync(int_thread *, Dyninst::Address, mem_response::ptr ) { assert(0); return false; } bool int_process::plat_writeMemAsync(int_thread *, const void *, Dyninst::Address, size_t, result_response::ptr, bp_write_t) { assert(0); return false; } memCache *int_process::getMemCache() { return &mem_cache; } void int_process::updateSyncState(Event::ptr ev, bool gen) { // This works around a Linux bug where a continue races with a whole-process exit plat_adjustSyncType(ev, gen); EventType etype = ev->getEventType(); switch (ev->getSyncType()) { case Event::async: { break; } case Event::sync_thread: { int_thread *thrd = ev->getThread()->llthrd(); if (!thrd) { pthrd_printf("No thread for sync thread event, assuming thread exited\n"); return; } int_thread::StateTracker &st = gen ? thrd->getGeneratorState() : thrd->getHandlerState(); int_thread::State old_state = st.getState(); if (old_state == int_thread::exited) { //Silly, linux. Giving us events on processes that have exited. pthrd_printf("Recieved events for exited thread, not changing thread state\n"); break; } pthrd_printf("Event %s is thread synchronous, marking thread %d %s stopped\n", etype.name().c_str(), thrd->getLWP(), gen ? "generator" : "handler"); /* * Xiaozhu: this assert causes pc_irpc to fail non-deterministically. * I think it is possible for the generator to receive one sync event one a thread, * and before the handler handles it and unset its status, the generator * receives another sync event on the same thread. * * This is because that even thought we attempt to stop the thread for a sync event, * there is a gap between the sync event and the stop. There could be another sync * event happening. * assert(RUNNING_STATE(old_state) || thrd->llproc()->wasForcedTerminated() || (old_state == int_thread::stopped && (thrd->isExiting() || thrd->isExitingInGenerator()))); */ if (old_state == int_thread::errorstate) break; st.setState(int_thread::stopped); break; } case Event::sync_process: { pthrd_printf("Event %s is process synchronous, marking process %d %s stopped\n", etype.name().c_str(), getPid(), gen ? "generator" : "handler"); int_threadPool *tp = threadPool(); for (int_threadPool::iterator i = tp->begin(); i != tp->end(); ++i) { int_thread *thrd = *i; int_thread::StateTracker &st = gen ? thrd->getGeneratorState() : thrd->getHandlerState(); int_thread::State old_state = st.getState(); if (!RUNNING_STATE(old_state)) continue; st.setState(int_thread::stopped); } break; } case Event::unset: { assert(0); } } } ProcStopEventManager &int_process::getProcStopManager() { return proc_stop_manager; } bool int_process::plat_supportThreadEvents() { return false; } bool int_process::plat_supportLWPCreate() { return false; } bool int_process::plat_supportLWPPreDestroy() { return false; } bool int_process::plat_supportLWPPostDestroy() { return false; } bool int_process::plat_supportHWBreakpoint() { return false; } bool int_process::plat_supportFork() { return false; } bool int_process::plat_supportExec() { return false; } async_ret_t int_process::plat_needsEmulatedSingleStep(int_thread *, std::vector
&) { return aret_success; } void int_process::plat_getEmulatedSingleStepAsyncs(int_thread *, std::set) { assert(0); } bool int_process::plat_needsPCSaveBeforeSingleStep() { return false; } map &int_process::getProcDesyncdStates() { return proc_desyncd_states; } bool int_process::isRunningSilent() { return silent_mode; } void int_process::setRunningSilent(bool b) { silent_mode = b; } bool int_process::plat_supportDOTF() { return true; } void int_process::noteNewDequeuedEvent(Event::ptr) { } bool int_process::plat_preHandleEvent() { return true; } bool int_process::plat_postHandleEvent() { return true; } bool int_process::plat_preAsyncWait() { return true; } int_process::~int_process() { pthrd_printf("Deleting int_process at %p\n", (void*)this); if (up_proc != Process::ptr()) { proc_exitstate *exitstate = new proc_exitstate(); exitstate->pid = pid; exitstate->exited = hasExitCode && !forcedTermination; exitstate->exit_code = exitCode; exitstate->crashed = hasCrashSignal; exitstate->crash_signal = crashSignal; exitstate->user_data = user_data; assert(!up_proc->exitstate_); up_proc->exitstate_ = exitstate; up_proc->llproc_ = NULL; } if (threadpool) { delete threadpool; threadpool = NULL; } //Do not delete handlerpool yet, we're currently under // an event handler. We do want to delete this if called // from detach. bool should_clean; mem->rmProc(this, should_clean); if (should_clean) { delete mem; } mem = NULL; if(ProcPool()->findProcByPid(getPid())) ProcPool()->rmProcess(this); } indep_lwp_control_process::indep_lwp_control_process(Dyninst::PID p, std::string e, std::vector a, std::vector envp, std::map f) : int_process(p, e, a, envp, f) { } indep_lwp_control_process::indep_lwp_control_process(Dyninst::PID pid_, int_process *p) : int_process(pid_, p) { } bool indep_lwp_control_process::plat_syncRunState() { for (int_threadPool::iterator i = threadPool()->begin(); i != threadPool()->end(); i++) { int_thread *thr = *i; int_thread::State handler_state = thr->getHandlerState().getState(); int_thread::State target_state = thr->getTargetState(); bool result = true; pthrd_printf("plat_syncRunState for thread %d/%d\n", thr->proc()->getPid(), thr->getLWP()); if (handler_state == target_state) { pthrd_printf("plat_syncRunState: thread is in desired state\n"); continue; } else if (handler_state == int_thread::stopped && RUNNING_STATE(target_state)) { result = thr->intCont(); pthrd_printf("plat_syncRunState: trying to continue; res=%d\n", result); } else if (RUNNING_STATE(handler_state) && target_state == int_thread::stopped) { result = thr->intStop(); pthrd_printf("plat_syncRunState: trying to stop; res=%d\n", result); } if (!result && getLastError() == err_exited) { pthrd_printf("Suppressing error of continue/stop on exited process\n"); if(thr->plat_handle_ghost_thread()) { thr->getHandlerState().setState(int_thread::running); } } else if (!result) { pthrd_printf("Error changing process state from plat_syncRunState\n"); return false; } } return true; } indep_lwp_control_process::~indep_lwp_control_process() { } unified_lwp_control_process::unified_lwp_control_process(Dyninst::PID p, std::string e, std::vector a, std::vector envp, std::map f) : int_process(p, e, a, envp, f) { } unified_lwp_control_process::unified_lwp_control_process(Dyninst::PID pid_, int_process *p) : int_process(pid_, p) { } unified_lwp_control_process::~unified_lwp_control_process() { } bool unified_lwp_control_process::plat_syncRunState() { bool want_ss_running = false; bool result = true; bool want_all_stopped = true; bool want_all_running = true; bool want_some_stopped = false; bool want_some_running = false; bool is_all_stopped = true; bool is_all_running = true; if (getState() == detached || getState() == exited || getState() == errorstate) { pthrd_printf("Process %d is in state %s, doing nothing in plat_syncRunState\n", getPid(), int_process::stateName(getState())); return true; } for (int_threadPool::iterator i = threadPool()->begin(); i != threadPool()->end(); i++) { int_thread *thr = *i; int_thread::State handler_state = thr->getHandlerState().getState(); int_thread::State target_state = thr->getTargetState(); if (target_state == int_thread::ditto) target_state = thr->getHandlerState().getState(); if (RUNNING_STATE(target_state)) { want_some_running = true; want_all_stopped = false; } else if (target_state == int_thread::stopped) { want_some_stopped = true; want_all_running = false; } else { want_all_stopped = false; want_all_running = false; } if (RUNNING_STATE(handler_state)) { is_all_stopped = false; } else if (handler_state == int_thread::stopped) { is_all_running = false; } else { is_all_stopped = false; is_all_running = false; } if (!RUNNING_STATE(handler_state) && RUNNING_STATE(target_state) && thr->singleStep()) { want_ss_running = true; } } pthrd_printf("In plat_syncRunState: want_all_stopped = %s, want_all_running = %s, want_some_stopped = %s, want_some_running = %s, is_all_stopped = %s, is_all_running = %s, want_ss_running = %s\n", want_all_stopped ? "t" : "f", want_all_running ? "t" : "f", want_some_stopped ? "t" : "f", want_some_running ? "t" : "f", is_all_stopped ? "t" : "f", is_all_running ? "t" : "f", want_ss_running ? "t" : "f"); if (want_ss_running) { pthrd_printf("Process %d is single-stepping. Continuing select threads\n", getPid()); for (int_threadPool::iterator i = threadPool()->begin(); i != threadPool()->end(); i++) { int_thread *thr = *i; if (thr->singleStep() && thr->getHandlerState().getState() == int_thread::stopped && RUNNING_STATE(thr->getTargetState())) { bool tresult = thr->intCont(); if (!tresult) { pthrd_printf("plat_syncRunState single-step failed on %d/%d\n", getPid(), thr->getLWP()); result = false; } } } } else if (want_all_running && is_all_running) { pthrd_printf("Process %d is running, needs to run. Doing nothing\n", getPid()); } else if (want_all_running && is_all_stopped) { pthrd_printf("Process %d is stopped, needs to run. Continuing process\n", getPid()); result = threadPool()->initialThread()->intCont(); } else if (want_all_stopped && is_all_running) { pthrd_printf("Process %d is running, needs to stop. Stopping process\n", getPid()); result = threadPool()->initialThread()->intStop(); } else if (want_all_stopped && is_all_stopped) { pthrd_printf("Process %d is stopped, needs to stop. Doing nothing\n", getPid()); } else if (want_some_stopped && !is_all_stopped) { pthrd_printf("Process %d is partially stopped, needs to stop. Stopping process\n", getPid()); result = threadPool()->initialThread()->intStop(); } else if (want_some_running && !is_all_running) { pthrd_printf("Process %d is partially running, needs to run. Continuing process\n", getPid()); result = threadPool()->initialThread()->intCont(); } else { pthrd_printf("Process %d is in startup or teardown state. Doing nothing\n", getPid()); } if (!result) { pthrd_printf("plat_syncRunState operation failed. Returning false\n"); return false; } return true; } bool unified_lwp_control_process::plat_processGroupContinues() { return true; } hybrid_lwp_control_process::hybrid_lwp_control_process(Dyninst::PID p, std::string e, std::vector a, std::vector envp, std::map f) : int_process(p, e, a, envp, f), debugger_stopped(false) { } hybrid_lwp_control_process::hybrid_lwp_control_process(Dyninst::PID pid_, int_process *p) : int_process(pid_, p), debugger_stopped(false) { // Clone debugger_stopped from parent hybrid_lwp_control_process *par = dynamic_cast(p); assert(par); // Otherwise we have a very, very strange system debugger_stopped = par->debugger_stopped; pthrd_printf("Set debugger stopped to %s on %d, matching parent %d\n", (debugger_stopped ? "true" : "false"), pid_, p->getPid()); } hybrid_lwp_control_process::~hybrid_lwp_control_process() { } bool hybrid_lwp_control_process::suspendThread(int_thread *thr) { if (thr->isSuspended()) return true; bool result = plat_suspendThread(thr); if (!result) return false; thr->setSuspended(true); return true; } bool hybrid_lwp_control_process::resumeThread(int_thread *thr) { if (!thr->isSuspended()) return true; bool result = plat_resumeThread(thr); if (!result) return false; thr->setSuspended(false); return true; } bool hybrid_lwp_control_process::plat_processGroupContinues() { return true; } void hybrid_lwp_control_process::noteNewDequeuedEvent(Event::ptr ev) { if (ev->getSyncType() == Event::sync_process) { pthrd_printf("Marking %d debugger suspended on event: %s\n", getPid(), ev->name().c_str()); debugger_stopped = true; pthrd_printf("Setting, debugger stopped: %d (%p) (%d)\n", debugger_stopped, (void*)this, getPid()); } } bool hybrid_lwp_control_process::plat_debuggerSuspended() { pthrd_printf("Querying, debugger stopped: %d (%p) (%p) (%d)\n", debugger_stopped, (void*)&debugger_stopped, (void*)this, getPid()); return debugger_stopped; } bool hybrid_lwp_control_process::plat_syncRunState() { bool any_target_stopped = false, any_target_running = false; bool any_stopped = false, any_running = false; int_thread *a_running_thread = NULL; if (getState() == exited) { pthrd_printf("Returning from plat_syncRunState for exited process %d\n", getPid()); return true; } int_threadPool *tp = threadPool(); int_threadPool::iterator i; for (i = tp->begin(); i != tp->end(); i++) { int_thread *thr = *i; pthrd_printf("Checking %d/%d: state %s\n", getPid(), thr->getLWP(), int_thread::stateStr(thr->getUserState().getState())); #if defined(os_freebsd) if (thr->getUserState().getState() == int_thread::exited) { // Let it go, man... continue; } #endif if (thr->getDetachState().getState() == int_thread::detached) { pthrd_printf("%d/%d detached, skipping\n", getPid(), thr->getLWP()); continue; } if (!RUNNING_STATE(thr->getHandlerState().getState())) { pthrd_printf("%d/%d not running, any_stopped = true\n", getPid(), thr->getLWP()); any_stopped = true; } else { pthrd_printf("%d/%d running, any_running = true\n", getPid(), thr->getLWP()); any_running = true; if (!a_running_thread) a_running_thread = thr; } if (RUNNING_STATE(thr->getTargetState())) { pthrd_printf("%d/%d target running, any_target_running = true\n", getPid(), thr->getLWP()); any_target_running = true; } if (!RUNNING_STATE(thr->getTargetState())) { pthrd_printf("%d/%d target stopped, any_target_stopped = true\n", getPid(), thr->getLWP()); any_target_stopped = true; } } if(!a_running_thread) { a_running_thread = tp->initialThread(); } if (!any_target_running && !any_running) { pthrd_printf("Target process state %d is stopped and process is stopped, leaving\n", getPid()); return true; } if (!any_target_stopped && !any_stopped) { pthrd_printf("Target process state %d is running and process is running, leaving\n", getPid()); return true; } if (!plat_debuggerSuspended()) { pthrd_printf("Process %d is not debugger suspended, but have changes. Stopping process.\n", getPid()); return a_running_thread->intStop(); } //If we're here, we must be debuggerSuspended, and thus no threads are running (!any_running) // since we didn't trigger the above if statement, we must have some thread we want to run // (any_target_running) assert(!any_running && any_target_running); pthrd_printf("Begin suspend/resume loop\n"); for (i = tp->begin(); i != tp->end(); i++) { int_thread *thr = *i; bool result = true; if (!thr->isUser()) { // Pretend is member of Wu Tang clan -- Bill Williams, 13JUN2012 resumeThread(thr); continue; } if (thr->getDetachState().getState() == int_thread::detached) continue; #if defined(os_freebsd) if (thr->getUserState().getState() == int_thread::exited) { // Let it go, man... continue; } #endif if (thr->isSuspended() && RUNNING_STATE(thr->getTargetState())) { pthrd_printf("Resuming thread %d/%d\n", getPid(), thr->getLWP()); result = resumeThread(thr); } if (thr->getDetachState().getState() == int_thread::detached) continue; if (RUNNING_STATE(thr->getTargetState())) { if (thr->isSuspended()) { pthrd_printf("Resuming thread %d/%d\n", getPid(), thr->getLWP()); result = resumeThread(thr); } else { pthrd_printf("Thread %d/%d is already resumed, not resuming\n", getPid(), thr->getLWP()); } } else if (!RUNNING_STATE(thr->getTargetState())) { if (!thr->isSuspended()) { pthrd_printf("Suspending thread %d/%d\n", getPid(), thr->getLWP()); result = suspendThread(thr); } else { pthrd_printf("Thread %d/%d is already suspended, not suspending\n", getPid(), thr->getLWP()); } } if (!result) { pthrd_printf("Error suspending/resuming threads\n"); return false; } } pthrd_printf("Continuing process %d after suspend/resume of threads\n", getPid()); debugger_stopped = false; return threadPool()->initialThread()->intCont(); } int_thread::int_thread(int_process *p, Dyninst::THR_ID t, Dyninst::LWP l) : tid(t), lwp(l), proc_(p), continueSig_(0), attach_status(as_unknown), handler_running_thrd_count(Counter::HandlerRunningThreads), generator_running_thrd_count(Counter::GeneratorRunningThreads), sync_rpc_count(Counter::SyncRPCs), sync_rpc_running_thr_count(Counter::SyncRPCRunningThreads), pending_stop(Counter::PendingStops), clearing_bp_count(Counter::ClearingBPs), proc_stop_rpc_count(Counter::ProcStopRPCs), generator_nonexited_thrd_count(Counter::GeneratorNonExitedThreads), neonatal_threads(Counter::NeonatalThreads), pending_stackwalk_count(Counter::PendingStackwalks), postponed_syscall_state(this, PostponedSyscallStateID, dontcare), exiting_state(this, ExitingStateID, dontcare), startup_state(this, StartupStateID, dontcare), pending_stop_state(this, PendingStopStateID, dontcare), callback_state(this, CallbackStateID, dontcare), breakpoint_state(this, BreakpointStateID, dontcare), breakpoint_hold_state(this, BreakpointHoldStateID, dontcare), breakpoint_resume_state(this, BreakpointResumeStateID, dontcare), irpc_setup_state(this, IRPCSetupStateID, dontcare), irpc_wait_state(this, IRPCWaitStateID, dontcare), irpc_state(this, IRPCStateID, dontcare), async_state(this, AsyncStateID, dontcare), internal_state(this, InternalStateID, dontcare), detach_state(this, DetachStateID, dontcare), user_irpc_state(this, UserRPCStateID, dontcare), control_authority_state(this, ControlAuthorityStateID, dontcare), user_state(this, UserStateID, neonatal), handler_state(this, HandlerStateID, neonatal), generator_state(this, GeneratorStateID, neonatal), target_state(int_thread::none), saved_user_state(int_thread::none), user_single_step(false), single_step(false), handler_exiting_state(false), generator_exiting_state(false), running_when_attached(true), suspended(false), user_syscall(false), next_syscall_is_exit(false), stopped_on_breakpoint_addr(0x0), postponed_stopped_on_breakpoint_addr(0x0), clearing_breakpoint(NULL), em_singlestep(NULL), user_data(NULL), unwinder(NULL), addr_fakeSyscallExitBp(0), isSet_fakeSyscallExitBp(false) { Thread::ptr new_thr(new Thread()); new_thr->llthread_ = this; up_thread = new_thr; getGeneratorNonExitedThreadCount().inc(); } int_thread::~int_thread() { assert(!up_thread->exitstate_); thread_exitstate *tes = new thread_exitstate(); tes->lwp = lwp; tes->thr_id = tid; tes->proc_ptr = proc(); tes->user_data = user_data; up_thread->exitstate_ = tes; up_thread->llthread_ = NULL; } bool int_thread::intStop() { pthrd_printf("intStop on thread %d/%d\n", llproc()->getPid(), getLWP()); if (!llproc()->plat_processGroupContinues()) { assert(!RUNNING_STATE(target_state)); assert(RUNNING_STATE(getHandlerState().getState())); if (hasPendingStop()) { pthrd_printf("Not throwing a second stop while another is in flight\n"); return true; } } else { int_threadPool *tp = llproc()->threadPool(); for (int_threadPool::iterator i = tp->begin(); i != tp->end(); i++) { if ((*i)->hasPendingStop()) { pthrd_printf("Not throwing a second stop while another is in flight\n"); return true; } } } setPendingStop(true); bool result = plat_stop(); if (!result) { setPendingStop(false); if (getLastError() == err_noproc) { //Swallow this. result = true; } } return result; } bool int_thread::intCont() { pthrd_printf("intCont on thread %d/%d\n", llproc()->getPid(), getLWP()); if (!llproc()->plat_processGroupContinues()) { assert(RUNNING_STATE(target_state)); assert(!RUNNING_STATE(getHandlerState().getState())); } async_ret_t aret = handleSingleStepContinue(); if (aret == aret_async) { pthrd_printf("Postponing intCont on %d/%d due to single-step handling\n", llproc()->getPid(), getLWP()); return true; } else if (aret == aret_error) { pthrd_printf("Error in intCont %d/%d during single-step handling\n", llproc()->getPid(), getLWP()); return false; } ProcPool()->condvar()->lock(); bool result = plat_cont(); if (result) { if (llproc()->plat_processGroupContinues()) { int_threadPool *pool = llproc()->threadPool(); for (int_threadPool::iterator i = pool->begin(); i != pool->end(); i++) { if ((*i)->isSuspended()) continue; (*i)->getHandlerState().setState(int_thread::running); (*i)->getGeneratorState().setState(int_thread::running); } } else { getHandlerState().setState(int_thread::running); getGeneratorState().setState(int_thread::running); } triggerContinueCBs(); } ProcPool()->condvar()->broadcast(); ProcPool()->condvar()->unlock(); if (!result) { pthrd_printf("Failed to plat_cont %d/%d\n", llproc()->getPid(), getLWP()); return false; } return true; } async_ret_t int_thread::handleSingleStepContinue() { async_ret_t ret; set thrds; if (llproc()->plat_processGroupContinues()) { int_threadPool *pool = llproc()->threadPool(); for (int_threadPool::iterator i = pool->begin(); i != pool->end(); i++) { if (!(*i)->isSuspended() && (*i)->singleStepUserMode()) { thrds.insert(*i); } } } else if (singleStepUserMode()) { thrds.insert(this); } if (thrds.empty()) { //No threads are single-steping. return aret_success; } pthrd_printf("Found %d threads doing single step under continue. Handling\n", (int) thrds.size()); if (llproc()->plat_needsAsyncIO()) { /** * We want any async responses associated with an event, but this is under * a continue, which never has an associated event. We'll make an EventNop * to accompany the async request. However, since this isn't going to be * a common thing to have happen, we'll set a flag that just tells the * HandlerPool to create the nop event lazily only if anyone asks for it. **/ llproc()->handlerPool()->setNopAsCurEvent(); } for (set::iterator i = thrds.begin(); i != thrds.end(); i++) { int_thread *thr = *i; vector
addrs; async_ret_t aresult = llproc()->plat_needsEmulatedSingleStep(thr, addrs); if (aresult == aret_async) { pthrd_printf("Async return from plat_needsEmulatedSingleStep on %d/%d\n", llproc()->getPid(), thr->getLWP()); //We're not actually under a handler, so fake all the async handling // the handlerpool would have done if we were. set resps; Event::ptr cur_nop_event = llproc()->handlerPool()->curEvent(); llproc()->plat_getEmulatedSingleStepAsyncs(thr, resps); llproc()->handlerPool()->notifyOfPendingAsyncs(resps, cur_nop_event); llproc()->handlerPool()->markEventAsyncPending(cur_nop_event); ret = aret_async; goto done; } else if (aresult == aret_error) { pthrd_printf("Error in plat_needsEmultatedSingleStep on %d/%d\n", llproc()->getPid(), thr->getLWP()); ret = aret_error; goto done; } else if (addrs.empty()) { pthrd_printf("Thread %d/%d does not need emulated single-step\n", llproc()->getPid(), thr->getLWP()); continue; } pthrd_printf("Creating emulated single step for %d/%d\n", llproc()->getPid(), thr->getLWP()); emulated_singlestep *new_es = thr->getEmulatedSingleStep(); if (!new_es) new_es = new emulated_singlestep(thr); for (vector
::iterator j = addrs.begin(); j != addrs.end(); j++) { Address addr = *j; pthrd_printf("Installing emulated single-step breakpoint for %d/%d at %lx\n", llproc()->getPid(), thr->getLWP(), addr); aresult = new_es->add(addr); if (aresult == aret_async) { pthrd_printf("Async return while installing breakpoint for emulated_singlestep\n"); ret = aret_async; goto done; } if (aresult == aret_error) { pthrd_printf("Error return while installing breakpoint for emulated_singlestep\n"); ret = aret_error; goto done; } } } ret = aret_success; done: if (llproc()->plat_needsAsyncIO()) llproc()->handlerPool()->clearNopAsCurEvent(); return ret; } bool int_thread::isStopped(int state_id) { if (getHandlerState().getState() != stopped) return false; if (getStateByID(state_id).getState() != stopped && getStateByID(state_id).getState() != ditto) return false; for (int i=0; igetPid(), getLWP()); if (b) { pending_stop.inc(); //You may ask why stopping moves the pending stop state to running. // We've sent the process a stop request, and we need that request to // be delivered. The process won't take delivery of the stop request // unless it's running. getPendingStopState().desyncState(int_thread::running); } else { if (getPendingStopState().getState() != int_thread::dontcare) { getPendingStopState().restoreState(); pending_stop.dec(); } else { pthrd_printf("Pending stop state == dontcare, ignoring\n"); } } pthrd_printf("\t Pending stop level is %d\n", pending_stop.localCount()); } bool int_thread::hasPendingStop() const { return pending_stop.local(); } bool int_thread::wasRunningWhenAttached() const { return running_when_attached; } void int_thread::setRunningWhenAttached(bool b) { running_when_attached = b; } Process::ptr int_thread::proc() const { return proc_->proc(); } int_process *int_thread::llproc() const { return proc_; } Dyninst::LWP int_thread::getLWP() const { return lwp; } void int_thread::addContinueCB(continue_cb_t cb) { continue_cbs.insert(cb); } void int_thread::triggerContinueCBs() { bool sync_conts = llproc()->plat_processGroupContinues(); for (set::iterator i = continue_cbs.begin(); i != continue_cbs.end(); i++) { if (!sync_conts) { //Independent lwp control--only do this thread. (*i)(this); } else { //Threads are continued at once--do every thread int_threadPool *tp = llproc()->threadPool(); for (int_threadPool::iterator j = tp->begin(); j != tp->end(); j++) { if ((*j)->isSuspended()) continue; (*i)(*j); } } } } int_thread::StateTracker &int_thread::getPostponedSyscallState() { return postponed_syscall_state; } int_thread::StateTracker &int_thread::getBreakpointState() { return breakpoint_state; } int_thread::StateTracker &int_thread::getBreakpointHoldState() { return breakpoint_hold_state; } int_thread::StateTracker &int_thread::getBreakpointResumeState() { return breakpoint_resume_state; } int_thread::StateTracker &int_thread::getIRPCState() { return irpc_state; } int_thread::StateTracker &int_thread::getIRPCSetupState() { return irpc_setup_state; } int_thread::StateTracker &int_thread::getIRPCWaitState() { return irpc_wait_state; } int_thread::StateTracker &int_thread::getAsyncState() { return async_state; } int_thread::StateTracker &int_thread::getInternalState() { return internal_state; } int_thread::StateTracker &int_thread::getDetachState() { return detach_state; } int_thread::StateTracker &int_thread::getUserRPCState() { return user_irpc_state; } int_thread::StateTracker &int_thread::getControlAuthorityState() { return control_authority_state; } int_thread::StateTracker &int_thread::getUserState() { return user_state; } int_thread::StateTracker &int_thread::getHandlerState() { return handler_state; } int_thread::StateTracker &int_thread::getGeneratorState() { return generator_state; } int_thread::StateTracker &int_thread::getExitingState() { return exiting_state; } int_thread::StateTracker &int_thread::getStartupState() { return startup_state; } int_thread::StateTracker &int_thread::getCallbackState() { return callback_state; } int_thread::StateTracker &int_thread::getPendingStopState() { return pending_stop_state; } int_thread::State int_thread::getTargetState() const { return target_state; } void int_thread::setTargetState(State s) { pthrd_printf("%d/%d: setting target state to %s\n", proc_->getPid(), getLWP(), RUNNING_STATE(s) ? "Running" : "Stopped"); target_state = s; } int_thread::StateTracker &int_thread::getActiveState() { for (int i=0; igetState() != int_thread::dontcare) { return *all_states[i]; } } assert(0); //At least user state should never be 'dontcare' return *all_states[0]; } int_thread::StateTracker &int_thread::getStateByID(int id) { switch (id) { case PostponedSyscallStateID: return postponed_syscall_state; case ExitingStateID: return exiting_state; case StartupStateID: return startup_state; case AsyncStateID: return async_state; case CallbackStateID: return callback_state; case PendingStopStateID: return pending_stop_state; case IRPCStateID: return irpc_state; case IRPCSetupStateID: return irpc_setup_state; case IRPCWaitStateID: return irpc_wait_state; case BreakpointStateID: return breakpoint_state; case BreakpointHoldStateID: return breakpoint_hold_state; case BreakpointResumeStateID: return breakpoint_resume_state; case InternalStateID: return internal_state; case DetachStateID: return detach_state; case UserRPCStateID: return user_irpc_state; case ControlAuthorityStateID: return control_authority_state; case UserStateID: return user_state; case HandlerStateID: return handler_state; case GeneratorStateID: return generator_state; } assert(0); return exiting_state; } std::string int_thread::stateIDToName(int id) { switch (id) { case PostponedSyscallStateID: return "postponed syscall"; case ExitingStateID: return "exiting"; case StartupStateID: return "startup"; case AsyncStateID: return "async"; case CallbackStateID: return "callback"; case PendingStopStateID: return "pending stop"; case IRPCStateID: return "irpc"; case IRPCSetupStateID: return "irpc setup"; case IRPCWaitStateID: return "irpc wait"; case BreakpointStateID: return "breakpoint"; case BreakpointHoldStateID: return "bp hold"; case BreakpointResumeStateID: return "breakpoint resume"; case InternalStateID: return "internal"; case UserRPCStateID: return "irpc user"; case ControlAuthorityStateID: return "control authority"; case UserStateID: return "user"; case DetachStateID: return "detach"; case HandlerStateID: return "handler"; case GeneratorStateID: return "generator"; } assert(0); return ""; } const char *int_thread::stateStr(int_thread::State s) { switch (s) { case none: return "none"; case neonatal: return "neonatal"; case neonatal_intermediate: return "neonatal_intermediate"; case running: return "running"; case stopped: return "stopped"; case dontcare: return "dontcare"; case ditto: return "ditto"; case exited: return "exited"; case detached: return "detached"; case errorstate: return "errorstate"; } assert(0); return NULL; } char int_thread::stateLetter(int_thread::State s) { switch (s) { case none: return '0'; case neonatal: return 'N'; case neonatal_intermediate: return 'I'; case running: return 'R'; case stopped: return 'S'; case dontcare: return '-'; case ditto: return 'H'; case exited: return 'X'; case detached: return 'D'; case errorstate: return 'E'; } assert(0); return '\0'; } Counter &int_thread::handlerRunningThreadsCount() { return handler_running_thrd_count; } Counter &int_thread::generatorRunningThreadsCount() { return generator_running_thrd_count; } Counter &int_thread::syncRPCCount() { return sync_rpc_count; } Counter &int_thread::runningSyncRPCThreadCount() { return sync_rpc_running_thr_count; } Counter &int_thread::pendingStopsCount() { return pending_stop; } Counter &int_thread::clearingBPCount() { return clearing_bp_count; } Counter &int_thread::procStopRPCCount() { return proc_stop_rpc_count; } Counter &int_thread::getGeneratorNonExitedThreadCount() { return generator_nonexited_thrd_count; } Counter &int_thread::neonatalThreadCount() { return neonatal_threads; } Counter &int_thread::pendingStackwalkCount() { return pending_stackwalk_count; } void int_thread::setContSignal(int sig) { continueSig_ = sig; } int int_thread::getContSignal() { return continueSig_; } int_thread *int_thread::createThread(int_process *proc, Dyninst::THR_ID thr_id, Dyninst::LWP lwp_id, bool initial_thrd, attach_status_t astatus) { // See if we already created a skeleton/dummy thread for this thread ID. int_thread *newthr = proc->threadPool()->findThreadByLWP(lwp_id); if (newthr) return newthr; newthr = createThreadPlat(proc, thr_id, lwp_id, initial_thrd); if(!newthr) { pthrd_printf("createThreadPlat failed, returning NULL\n"); return NULL; } pthrd_printf("Creating %s thread %d/%d, thr_id = 0x%lx\n", initial_thrd ? "initial" : "new", proc->getPid(), newthr->getLWP(), (unsigned long)thr_id); proc->threadPool()->addThread(newthr); if (initial_thrd) { proc->threadPool()->setInitialThread(newthr); } ProcPool()->addThread(proc, newthr); newthr->attach_status = astatus; bool result = newthr->attach(); if (!result) { pthrd_printf("Failed to attach to new thread %d/%d\n", proc->getPid(), lwp_id); newthr->getUserState().setState(errorstate); newthr->getHandlerState().setState(errorstate); newthr->getGeneratorState().setState(errorstate); ProcPool()->rmThread(newthr); proc->threadPool()->rmThread(newthr); return NULL; } if (newthr->isUser() && newthr->getUserState().getState() == neonatal) { newthr->getUserState().setState(neonatal_intermediate); newthr->getHandlerState().setState(neonatal_intermediate); newthr->getGeneratorState().setState(neonatal_intermediate); } return newthr; } void int_thread::changeLWP(Dyninst::LWP new_lwp) { pthrd_printf("Changing LWP of %d/%d to %d\n", llproc()->getPid(), lwp, new_lwp); int_threadPool *tpool = llproc()->threadPool(); map::iterator i = tpool->thrds_by_lwp.find(lwp); assert(i != tpool->thrds_by_lwp.end()); tpool->thrds_by_lwp.erase(i); tpool->thrds_by_lwp.insert(make_pair(new_lwp, this)); ProcPool()->condvar()->lock(); ProcPool()->rmThread(this); lwp = new_lwp; ProcPool()->addThread(llproc(), this); ProcPool()->condvar()->unlock(); } void int_thread::throwEventsBeforeContinue() { pthrd_printf("Checking thread %d/%d for events thrown before continue\n", llproc()->getPid(), getLWP()); if (llproc()->wasForcedTerminated()) return; Event::ptr new_ev; int_iRPC::ptr rpc = nextPostedIRPC(); bp_instance *bpi = isStoppedOnBP(); if (rpc && !runningRPC() && rpc->getState() == int_iRPC::Posted) { pthrd_printf("Found thread %d/%d ready to run IRPC, not continuing\n", llproc()->getPid(), getLWP()); if (rpc->isProcStopRPC() || llproc()->plat_threadOpsNeedProcStop()) { getIRPCSetupState().desyncStateProc(int_thread::stopped); } else { getIRPCSetupState().desyncState(int_thread::stopped); } rpc->setState(int_iRPC::Prepping); new_ev = EventRPCLaunch::ptr(new EventRPCLaunch()); } else if (bpi) { if (bpi->swBP()) { //Stop the process to clear a software breakpoint pthrd_printf("Found thread %d/%d to be stopped on a software BP, not continuing\n", llproc()->getPid(), getLWP()); getBreakpointState().desyncStateProc(int_thread::stopped); EventBreakpointClear::ptr evclear = EventBreakpointClear::ptr(new EventBreakpointClear()); evclear->getInternal()->stopped_proc = true; new_ev = evclear; } else { //No process stop needed for a hardware breakpoint pthrd_printf("Found thread %d/%d to be stopped on a hardware BP, not continuing\n", llproc()->getPid(), getLWP()); getBreakpointState().desyncState(int_thread::stopped); new_ev = EventBreakpointClear::ptr(new EventBreakpointClear()); } } else { new_ev = llproc()->plat_throwEventsBeforeContinue(this); } if (new_ev) { new_ev->setProcess(proc()); new_ev->setThread(thread()); new_ev->setSyncType(Event::async); new_ev->setSuppressCB(true); mbox()->enqueue(new_ev); } } bool int_thread::suppressSanityChecks() { return false; } bool int_thread::isExiting() const { return handler_exiting_state; } void int_thread::setExiting(bool b) { handler_exiting_state = b; } bool int_thread::isExitingInGenerator() const { return generator_exiting_state; } void int_thread::setExitingInGenerator(bool b) { generator_exiting_state = b; } void int_thread::cleanFromHandler(int_thread *thrd, bool should_delete) { ProcPool()->condvar()->lock(); #if !defined(os_freebsd) thrd->getUserState().setState(int_thread::exited); #else thrd->setExiting(true); #endif if (should_delete) { thrd->getExitingState().setState(int_thread::exited); if(ProcPool()->findThread(thrd->getLWP()) != NULL) { ProcPool()->rmThread(thrd); } else { pthrd_printf("%d/%d already gone from top level ProcPool(), not removing\n", thrd->llproc()->getPid(), thrd->getLWP()); } thrd->llproc()->threadPool()->rmThread(thrd); delete thrd; } else { //If we're not yet deleting this thread, then we're dealing with // a pre-mature exit event. The thread will be exiting soon, but // isn't there yet. We'll run the thread instead to make sure it // reaches a proper exit. // If we're freeBSD, they don't give us a thread exit event and // so running only the exiting thread can result in a process // that is "executing" with no continued threads. That is bad. #if !defined(os_freebsd) thrd->getExitingState().setState(int_thread::running); #endif } ProcPool()->condvar()->broadcast(); ProcPool()->condvar()->unlock(); } Thread::ptr int_thread::thread() { return up_thread; } bool int_thread::getAllRegisters(allreg_response::ptr response) { response->setThread(this); response->setProcess(llproc()); pthrd_printf("Reading registers for thread %d\n", getLWP()); regpool_lock.lock(); if (cached_regpool.full && 0) { *response->getRegPool() = cached_regpool; response->getRegPool()->thread = this; response->markReady(); pthrd_printf("Returning cached register set\n"); regpool_lock.unlock(); return true; } if (!llproc()->plat_needsAsyncIO()) { pthrd_printf("plat_getAllRegisters on %d/%d\n", llproc()->getPid(), getLWP()); bool result = plat_getAllRegisters(cached_regpool); if (!result) { pthrd_printf("plat_getAllRegisters returned error on %d\n", getLWP()); response->markError(); regpool_lock.unlock(); return false; } cached_regpool.full = true; *(response->getRegPool()) = cached_regpool; response->getRegPool()->thread = this; response->markReady(); regpool_lock.unlock(); int_eventAsyncIO *iev = response->getAsyncIOEvent(); if (iev) { pthrd_printf("Enqueueing new EventAsyncReadAllRegs into mailbox on synchronous platform\n"); EventAsyncReadAllRegs::ptr ev = EventAsyncReadAllRegs::ptr(new EventAsyncReadAllRegs(iev)); ev->setProcess(proc()); ev->setThread(thread()); ev->setSyncType(Event::async); mbox()->enqueue(ev); } pthrd_printf("Successfully retrieved all registers for %d\n", getLWP()); } else { pthrd_printf("Async plat_getAllRegisters on %d/%d\n", llproc()->getPid(), getLWP()); regpool_lock.unlock(); getResponses().lock(); bool result = plat_getAllRegistersAsync(response); if (result) { getResponses().addResponse(response, llproc()); } getResponses().unlock(); getResponses().noteResponse(); if (!result) { pthrd_printf("plat_getAllRegistersAsync returned error on %d\n", getLWP()); return false; } } return true; } bool int_thread::setAllRegisters(int_registerPool &pool, result_response::ptr response) { assert(getHandlerState().getState() == int_thread::stopped); assert(getGeneratorState().getState() == int_thread::stopped); response->setProcess(llproc()); if (!llproc()->plat_needsAsyncIO()) { pthrd_printf("Setting registers for thread %d\n", getLWP()); bool result = plat_setAllRegisters(pool); response->setResponse(result); if (!result) { pthrd_printf("plat_setAllRegisters returned error on %d\n", getLWP()); return false; } int_eventAsyncIO *iev = response->getAsyncIOEvent(); if (iev) { pthrd_printf("Enqueueing new EventAsyncSetAllRegs into mailbox on synchronous platform\n"); EventAsyncSetAllRegs::ptr ev = EventAsyncSetAllRegs::ptr(new EventAsyncSetAllRegs(iev)); ev->setProcess(proc()); ev->setThread(thread()); ev->setSyncType(Event::async); mbox()->enqueue(ev); } pthrd_printf("Successfully set all registers for %d\n", getLWP()); } else { pthrd_printf("Async setting registers for thread %d\n", getLWP()); getResponses().lock(); bool result = plat_setAllRegistersAsync(pool, response); if (result) { getResponses().addResponse(response, llproc()); } getResponses().unlock(); getResponses().noteResponse(); if (!result) { pthrd_printf("Error async setting registers on %d\n", getLWP()); return false; } } regpool_lock.lock(); cached_regpool = pool; cached_regpool.full = true; regpool_lock.unlock(); return true; } bool int_thread::getRegister(Dyninst::MachRegister reg, reg_response::ptr response) { bool ret_result = false; pthrd_printf("Get register value for thread %d, register %s\n", lwp, reg.name().c_str()); response->setRegThread(reg, this); response->setProcess(llproc()); if (!llproc()->plat_individualRegRead(reg, this)) { //Convert the single get register access into a get all registers pthrd_printf("Platform does not support individual register access, " "getting everything\n"); if (!llproc()->plat_needsAsyncIO()) { int_registerPool pool; allreg_response::ptr allreg_resp = allreg_response::createAllRegResponse(&pool); bool result = getAllRegisters(allreg_resp); bool is_ready = allreg_resp->isReady(); if (!result || allreg_resp->hasError()) { pthrd_printf("Unable to access full register set\n"); return false; } assert(is_ready); if(!is_ready) return false; response->setResponse(pool.regs[reg]); } else { allreg_response::ptr allreg_resp = allreg_response::createAllRegResponse(&cached_regpool); allreg_resp->setIndividualRegAccess(response, reg); getResponses().lock(); getResponses().addResponse(response, llproc()); getResponses().unlock(); bool result = getAllRegisters(allreg_resp); if (!result) { pthrd_printf("Error accessing full register set\n"); return false; } allreg_resp->isReady(); } return true; } regpool_lock.lock(); int_registerPool::reg_map_t::iterator i = cached_regpool.regs.find(reg); if (i != cached_regpool.regs.end()) { pthrd_printf("Had cached register value\n"); response->setResponse(i->second); } else if (!llproc()->plat_needsAsyncIO()) { MachRegisterVal val = 0; bool result = plat_getRegister(reg, val); if (!result) { pthrd_printf("Error reading register value for %s on %d\n", reg.name().c_str(), lwp); response->markError(getLastError()); goto done; } response->setResponse(val); } else { pthrd_printf("Async getting register for thread %d\n", getLWP()); getResponses().lock(); bool result = plat_getRegisterAsync(reg, response); if (result && !response->testReady()) { getResponses().addResponse(response, llproc()); } getResponses().unlock(); getResponses().noteResponse(); if (!result) { pthrd_printf("Error getting async register for thread %d\n", getLWP()); goto done; } ret_result = true; goto done; } pthrd_printf("Returning register value %lx for register %s on %d\n", response->getResult(), reg.name().c_str(), lwp); ret_result = true; done: regpool_lock.unlock(); return ret_result; } bool int_thread::setRegister(Dyninst::MachRegister reg, Dyninst::MachRegisterVal val, result_response::ptr response) { if (getGeneratorState().getState() == int_thread::exited) { // Happens when we force terminate someone at a breakpoint... return false; } if (!isGeneratorThread()) { assert(getHandlerState().getState() == int_thread::stopped); assert(getGeneratorState().getState() == int_thread::stopped); } response->setProcess(llproc()); bool ret_result = false; pthrd_printf("%d/%d: Setting %s to 0x%lx...\n", proc()->getPid(), lwp, reg.name().c_str(), val); if (!llproc()->plat_individualRegSet()) { pthrd_printf("Platform does not support individual register access, " "setting everything\n"); assert(!llproc()->plat_needsAsyncIO()); int_registerPool pool; allreg_response::ptr allreg_resp = allreg_response::createAllRegResponse(&pool); bool result = getAllRegisters(allreg_resp); assert(allreg_resp->isReady()); if (!result || allreg_resp->hasError()) { pthrd_printf("Unable to access full register set\n"); return false; } pool.regs[reg] = val; result = setAllRegisters(pool, response); assert(response->isReady()); if (!result || response->hasError()) { pthrd_printf("Unable to set full register set\n"); return false; } return true; } pthrd_printf("Setting register %s for thread %d to %lx\n", reg.name().c_str(), getLWP(), val); regpool_lock.lock(); MachRegister base_register = reg.getBaseRegister(); if (!llproc()->plat_needsAsyncIO()) { bool result = plat_setRegister(base_register, val); response->setResponse(result); if (!result) { response->markError(getLastError()); pthrd_printf("Error setting register %s\n", base_register.name().c_str()); goto done; } } else { pthrd_printf("Async setting register for thread %d\n", getLWP()); getResponses().lock(); bool result = plat_setRegisterAsync(reg, val, response); if (result) { getResponses().addResponse(response, llproc()); } getResponses().unlock(); getResponses().noteResponse(); if (!result) { pthrd_printf("Error setting async register for thread %d\n", getLWP()); goto done; } } cached_regpool.regs[base_register] = val; ret_result = true; done: regpool_lock.unlock(); return ret_result; } void int_thread::updateRegCache(int_registerPool &pool) { regpool_lock.lock(); pool.full = true; cached_regpool = pool; regpool_lock.unlock(); } void int_thread::updateRegCache(Dyninst::MachRegister reg, Dyninst::MachRegisterVal val) { regpool_lock.lock(); cached_regpool.regs[reg] = val; regpool_lock.unlock(); } bool int_thread::plat_getAllRegistersAsync(allreg_response::ptr) { assert(0); return false; } bool int_thread::plat_getRegisterAsync(Dyninst::MachRegister, reg_response::ptr) { assert(0); return false; } bool int_thread::plat_setAllRegistersAsync(int_registerPool &, result_response::ptr) { assert(0); return false; } bool int_thread::plat_setRegisterAsync(Dyninst::MachRegister, Dyninst::MachRegisterVal, result_response::ptr) { assert(0); return false; } bool int_thread::plat_handle_ghost_thread() { return true; } void int_thread::addPostedRPC(int_iRPC::ptr rpc_) { assert(rpc_); if (rpc_->isProcStopRPC() && posted_rpcs.empty()) { proc_stop_rpc_count.inc(); } posted_rpcs.push_back(rpc_); } rpc_list_t *int_thread::getPostedRPCs() { return &posted_rpcs; } bool int_thread::hasPostedRPCs() { return (posted_rpcs.size() != 0); } void int_thread::setRunningRPC(int_iRPC::ptr rpc_) { assert(!running_rpc); running_rpc = rpc_; if (stopped_on_breakpoint_addr) { pthrd_printf("Thread %d/%d is stopped on BP at 0x%lx and about to run RPC. Postponing BP clear.\n", llproc()->getPid(), getLWP(), stopped_on_breakpoint_addr); postponed_stopped_on_breakpoint_addr = stopped_on_breakpoint_addr; stopped_on_breakpoint_addr = 0x0; } if (rpc_->isProcStopRPC() && !proc_stop_rpc_count.local()) { proc_stop_rpc_count.inc(); } } int_iRPC::ptr int_thread::runningRPC() const { return running_rpc; } void int_thread::clearRunningRPC() { if (postponed_stopped_on_breakpoint_addr) { pthrd_printf("Thread %d/%d is moving back to stopped breakpoint at %lx after IRPC completion\n", llproc()->getPid(), getLWP(), postponed_stopped_on_breakpoint_addr); stopped_on_breakpoint_addr = postponed_stopped_on_breakpoint_addr; postponed_stopped_on_breakpoint_addr = 0x0; } if (running_rpc->isProcStopRPC()) { proc_stop_rpc_count.dec(); } running_rpc = int_iRPC::ptr(); } bool int_thread::saveRegsForRPC(allreg_response::ptr response) { assert(!rpc_regs.full); response->setRegPool(&rpc_regs); bool ret = getAllRegisters(response); return ret; } bool int_thread::restoreRegsForRPC(bool clear, result_response::ptr response) { assert(rpc_regs.full); bool result = setAllRegisters(rpc_regs, response); if (clear && result) { rpc_regs.regs.clear(); rpc_regs.full = false; } return result; } bool int_thread::hasSavedRPCRegs() { return rpc_regs.full; } void int_thread::incSyncRPCCount() { if (!sync_rpc_count.local() && RUNNING_STATE(getHandlerState().getState())) { runningSyncRPCThreadCount().inc(); } sync_rpc_count.inc(); } void int_thread::decSyncRPCCount() { sync_rpc_count.dec(); if (!sync_rpc_count.local() && RUNNING_STATE(getHandlerState().getState())) { runningSyncRPCThreadCount().dec(); } } bool int_thread::hasSyncRPC() { return sync_rpc_count.local(); } int_iRPC::ptr int_thread::nextPostedIRPC() const { if (!posted_rpcs.size()) return int_iRPC::ptr(); return posted_rpcs.front(); } bool int_thread::singleStepMode() const { return single_step; } void int_thread::setSingleStepMode(bool s) { single_step = s; } bool int_thread::singleStepUserMode() const { return user_single_step; } void int_thread::setSingleStepUserMode(bool s) { user_single_step = s; } bool int_thread::singleStep() const { return single_step || user_single_step; } void int_thread::setSyscallUserMode(bool s) { user_syscall = s; } bool int_thread::syscallUserMode() const { return user_syscall; } bool int_thread::syscallMode() const { return user_syscall; } bool int_thread::preSyscall() { /* On Linux, we distinguish between system call entry and exit based on the * system call return value. This approach will always correctly label a * system call entry. Only in the cast where the invoked system call does * not exist (0 < syscall_number > __NR_syscall_max) will we incorrectly * label the system call exist as system call entry. */ bool ret = !next_syscall_is_exit; MachRegisterVal syscallReturnValue; if (!plat_getRegister(MachRegister::getSyscallReturnValueReg(llproc()->getTargetArch()), syscallReturnValue)) { ret = true; } if (syscallReturnValue == (MachRegisterVal)(-ENOSYS)) { ret = true; } next_syscall_is_exit = ret; return ret; } void int_thread::markClearingBreakpoint(bp_instance *bp) { assert(!clearing_breakpoint || bp == NULL); pthrd_printf("%d/%d marking clearing bp %p (at 0x%lx)\n", llproc()->getPid(), getLWP(), (void*)bp, bp ? bp->getAddr() : 0); clearing_breakpoint = bp; if (bp) { clearing_bp_count.inc(); } else { clearing_bp_count.dec(); } } void int_thread::markStoppedOnBP(bp_instance *bp) { stopped_on_breakpoint_addr = bp ? bp->getAddr() : 0x0; } bp_instance *int_thread::isStoppedOnBP() { if (!stopped_on_breakpoint_addr) { return NULL; } sw_breakpoint *swbp = llproc()->getBreakpoint(stopped_on_breakpoint_addr); if (swbp) return static_cast(swbp); hw_breakpoint *hwbp = getHWBreakpoint(stopped_on_breakpoint_addr); return static_cast(hwbp); } void int_thread::setTID(Dyninst::THR_ID tid_) { tid = tid_; } bp_instance *int_thread::isClearingBreakpoint() { return clearing_breakpoint; } bool int_thread::haveUserThreadInfo() { return false; } bool int_thread::getTID(Dyninst::THR_ID &) { perr_printf("Unsupported attempt to getTid on %d/%d\n", llproc()->getPid(), getLWP()); setLastError(err_unsupported, "getTid not supported on this platform\n"); return false; } bool int_thread::getStartFuncAddress(Dyninst::Address &) { perr_printf("Unsupported attempt to get start func address on %d/%d\n", llproc()->getPid(), getLWP()); setLastError(err_unsupported, "getStartFuncAddress not supported on this platform\n"); return false; } bool int_thread::getStackBase(Dyninst::Address &) { perr_printf("Unsupported attempt to get stack base on %d/%d\n", llproc()->getPid(), getLWP()); setLastError(err_unsupported, "getStackBase not supported on this platform\n"); return false; } bool int_thread::getStackSize(unsigned long &) { perr_printf("Unsupported attempt to get stack size on %d/%d\n", llproc()->getPid(), getLWP()); setLastError(err_unsupported, "getStackSize not supported on this platform\n"); return false; } bool int_thread::getTLSPtr(Dyninst::Address &) { perr_printf("Unsupported attempt to get TLS on %d/%d\n", llproc()->getPid(), getLWP()); setLastError(err_unsupported, "getTLSPtr not supported on this platform\n"); return false; } Dyninst::Address int_thread::getThreadInfoBlockAddr() { perr_printf("Unsupported attempt to get ThreadInfoBlock Address on %d/%d\n", llproc()->getPid(), getLWP()); setLastError(err_unsupported, "getThreadInfoBlockAddr not supported on this platform\n"); return 0; } unsigned int_thread::hwBPAvail(unsigned) { return 0; } EventBreakpoint::ptr int_thread::decodeHWBreakpoint(response::ptr &, bool, Dyninst::MachRegisterVal) { return EventBreakpoint::ptr(); } bool int_thread::addHWBreakpoint(hw_breakpoint *, bool, std::set &, bool &) { perr_printf("Tried to use hardware breakpoint on unsupported system\n"); setLastError(err_unsupported, "Hardware breakpoints not supported on this platform\n"); return false; } bool int_thread::rmHWBreakpoint(hw_breakpoint *, bool, std::set &, bool &) { perr_printf("Tried to use hardware breakpoint on unsupported system\n"); setLastError(err_unsupported, "Hardware breakpoints not supported on this platform\n"); return false; } bool int_thread::bpNeedsClear(hw_breakpoint *) { return false; } void int_thread::addEmulatedSingleStep(emulated_singlestep *es) { assert(!em_singlestep); em_singlestep = es; } void int_thread::rmEmulatedSingleStep(emulated_singlestep *es) { (void)es; assert(em_singlestep == es); delete em_singlestep; em_singlestep = NULL; } emulated_singlestep *int_thread::getEmulatedSingleStep() { return em_singlestep; } void int_thread::clearRegCache() { regpool_lock.lock(); cached_regpool.regs.clear(); cached_regpool.full = false; regpool_lock.unlock(); } int_thread::StateTracker::StateTracker(int_thread *t, int id_, int_thread::State initial) : state(int_thread::none), id(id_), sync_level(0), up_thr(t) { t->all_states[id] = this; setState(initial); } void int_thread::StateTracker::desyncState(State ns) { sync_level++; pthrd_printf("Desyncing %d/%d %s state to level %d\n", up_thr->llproc()->getPid(), up_thr->getLWP(), getName().c_str(), sync_level); assert(id != int_thread::HandlerStateID && id != int_thread::GeneratorStateID && id != int_thread::UserStateID); if (ns != int_thread::none) { setState(ns); } } void int_thread::StateTracker::desyncStateProc(State ns) { int_threadPool *pool = up_thr->llproc()->threadPool(); for (int_threadPool::iterator i = pool->begin(); i != pool->end(); i++) { (*i)->getStateByID(id).desyncState(ns); } //Track the process level desyncs seperately. This way if a // new thread appears we can initialized its states to be // consistent with the other threads. up_thr->llproc()->getProcDesyncdStates()[id]++; } void int_thread::StateTracker::restoreState() { sync_level--; pthrd_printf("Restoring %d/%d %s state to level %d\n", up_thr->llproc()->getPid(), up_thr->getLWP(), getName().c_str(), sync_level); assert(id != int_thread::HandlerStateID && id != int_thread::GeneratorStateID && id != int_thread::UserStateID); assert(sync_level >= 0); if (sync_level == 0) { setState(int_thread::dontcare); } } void int_thread::StateTracker::restoreStateProc() { int_threadPool *pool = up_thr->llproc()->threadPool(); for (int_threadPool::iterator i = pool->begin(); i != pool->end(); i++) { (*i)->getStateByID(id).restoreState(); } up_thr->llproc()->getProcDesyncdStates()[id]--; } int_thread::State int_thread::StateTracker::getState() const { return state; } bool int_thread::StateTracker::isDesynced() const { return (sync_level != 0); } bool int_thread::StateTracker::setState(State to) { std::string s = int_thread::stateIDToName(id); Dyninst::LWP thisLwp = up_thr->getLWP(); Dyninst::PID pid = up_thr->llproc()->getPid(); if (state == to) { pthrd_printf("Leaving %s state for %d/%d in state %s\n", s.c_str(), pid, thisLwp, stateStr(to)); return true; } if (state == errorstate) { perr_printf("Attempted %s state reversion for %d/%d from errorstate to %s\n", s.c_str(), pid, thisLwp, stateStr(to)); return false; } if (state == exited && to != errorstate) { perr_printf("Attempted %s state reversion for %d/%d from exited to %s\n", s.c_str(), pid, thisLwp, stateStr(to)); return false; } if (to == neonatal && state != none) { perr_printf("Attempted %s state reversion for %d/%d from %s to neonatal\n", s.c_str(), pid, thisLwp, stateStr(state)); return false; } /** * We need to keep track of the running threads counts. We'll do that here. **/ if (RUNNING_STATE(to) && !RUNNING_STATE(state)) { //We're moving a thread into a running state... if (id == int_thread::GeneratorStateID) { up_thr->generatorRunningThreadsCount().inc(); } else if (id == int_thread::HandlerStateID) { up_thr->handlerRunningThreadsCount().inc(); if (up_thr->syncRPCCount().local()) { up_thr->runningSyncRPCThreadCount().inc(); } } } else if (RUNNING_STATE(state) && !RUNNING_STATE(to)) { //We're moving a thread out of a running state... if (id == int_thread::GeneratorStateID) { up_thr->generatorRunningThreadsCount().dec(); } else if (id == int_thread::HandlerStateID) { up_thr->handlerRunningThreadsCount().dec(); if (up_thr->syncRPCCount().local()) { up_thr->runningSyncRPCThreadCount().dec(); } } } if (id == int_thread::GeneratorStateID && to == int_thread::exited) { up_thr->getGeneratorNonExitedThreadCount().dec(); } if (id == int_thread::HandlerStateID) { if ((state == int_thread::neonatal || state == int_thread::neonatal_intermediate) && (to != int_thread::neonatal && to != int_thread::neonatal_intermediate)) { //Moving away from neonatal/neonatal_intermediate up_thr->neonatalThreadCount().dec(); } if ((state != int_thread::neonatal && state != neonatal_intermediate) && (to == int_thread::neonatal || to == int_thread::neonatal_intermediate)) { //Moving into neonatal/neonatal_intermediate up_thr->neonatalThreadCount().inc(); } } pthrd_printf("Changing %s state for %d/%d from %s to %s\n", s.c_str(), pid, thisLwp, stateStr(state), stateStr(to)); state = to; /* Xiaozhu: the asserts can fail legitimately. if (up_thr->up_thread && !up_thr->suppressSanityChecks()) { int_thread::State handler_state = up_thr->getHandlerState().getState(); int_thread::State generator_state = up_thr->getGeneratorState().getState(); * * The handler status and generator status of a thread are never in sync by design. * We will need to revisit such design. * if (handler_state == stopped) assert(generator_state == stopped || generator_state == exited || generator_state == detached ); if (generator_state == running) assert(handler_state == running); } */ return true; } bool int_thread::StateTracker::setStateProc(State ns) { bool had_error = false; int_threadPool *pool = up_thr->llproc()->threadPool(); for (int_threadPool::iterator i = pool->begin(); i != pool->end(); i++) { int_thread *thr = *i; bool result = thr->getStateByID(id).setState(ns); if (!result) had_error = true; } return !had_error; } std::string int_thread::StateTracker::getName() const { return int_thread::stateIDToName(id); } int int_thread::StateTracker::getID() const { return id; } void int_thread::setSuspended(bool b) { suspended = b; } bool int_thread::isSuspended() const { return suspended; } void int_thread::setLastError(err_t ec, const char *es) { if (proc_) proc_->setLastError(ec, es); } hw_breakpoint *int_thread::getHWBreakpoint(Address a) { std::set::iterator i; for (i = hw_breakpoints.begin(); i != hw_breakpoints.end(); i++) { if ((*i)->getAddr() == a) return *i; } return NULL; } int_thread *int_threadPool::findThreadByLWP(Dyninst::LWP lwp) { std::map::iterator i = thrds_by_lwp.find(lwp); if (i == thrds_by_lwp.end()) return NULL; return i->second; } int_thread *int_threadPool::initialThread() const { //if(!initial_thread) { // initial_thread = *(threads.begin()); // } return initial_thread; } bool int_threadPool::allHandlerStopped() { for (iterator i = begin(); i != end(); i++) { if ((*i)->getHandlerState().getState() != int_thread::stopped) return false; } return true; } bool int_threadPool::allStopped(int state_id) { for (iterator i = begin(); i != end(); i++) { if (!(*i)->isStopped(state_id)) return false; } return true; } bool int_threadPool::hadMultipleThreads() const { return had_multiple_threads; } void int_threadPool::addThread(int_thread *thrd) { Dyninst::LWP lwp = thrd->getLWP(); std::map::iterator i = thrds_by_lwp.find(lwp); assert (i == thrds_by_lwp.end()); thrds_by_lwp[lwp] = thrd; threads.push_back(thrd); hl_threads.push_back(thrd->thread()); if( threads.size() > 1 ) { had_multiple_threads = true; } } void int_threadPool::rmThread(int_thread *thrd) { // On Windows we are not guaranteed that initial thread exit == // process exit. So we have to handle the case where the initial thread goes away. // In particular this means we need to have a valid initial thread in the threadpool after // we finish removing this one, so that we have an arbitrary thread available // for continue calls. // FIXME: should probably kill most of the data duplication here. Log search for thread by LWP is // I guess legitimate, but there's AFAICT no reason for the initial thread to be anything other than // pinned to threads[0]. And the data duplication involved in threads by LWP is not a good thing // for consistency... #if !defined(os_windows) assert(thrd != initial_thread); #endif Dyninst::LWP lwp = thrd->getLWP(); std::map::iterator i = thrds_by_lwp.find(lwp); assert (i != thrds_by_lwp.end()); thrds_by_lwp.erase(i); for (unsigned j=0; j::iterator i = thrds_by_lwp.begin(); while(i != thrds_by_lwp.end()) { if(i->second == thrd) { thrds_by_lwp.erase(i); thrds_by_lwp.insert(std::make_pair(thrd->getLWP(), thrd)); return; } ++i; } } void int_threadPool::clear() { threads.clear(); hl_threads.clear(); thrds_by_lwp.clear(); initial_thread = NULL; } void int_threadPool::setInitialThread(int_thread *thrd) { initial_thread = thrd; } int_process *int_threadPool::proc() const { return proc_; } unsigned int_threadPool::size() const { return threads.size(); } ThreadPool *int_threadPool::pool() const { return up_pool; } void int_threadPool::saveUserState(Event::ptr ev) { Event::SyncType et = ev->getSyncType(); switch (et) { case Event::unset: assert(0); break; case Event::async: break; case Event::sync_thread: { int_thread *thr = ev->getThread()->llthrd(); if (!thr) return; assert(thr->saved_user_state == int_thread::none); thr->saved_user_state = thr->getUserState().getState(); thr->getUserState().setState(int_thread::stopped); break; } case Event::sync_process: for (iterator i = begin(); i != end(); i++) { int_thread *thr = *i; assert(thr->saved_user_state == int_thread::none); thr->saved_user_state = thr->getUserState().getState(); thr->getUserState().setState(int_thread::stopped); } break; } } void int_threadPool::restoreUserState() { for (iterator i = begin(); i != end(); i++) { int_thread *thr = *i; if (thr->saved_user_state == int_thread::none) continue; thr->getUserState().setState(thr->saved_user_state); thr->saved_user_state = int_thread::none; } } int_threadPool::int_threadPool(int_process *p) : initial_thread(NULL), proc_(p), had_multiple_threads(false) { up_pool = new ThreadPool(); up_pool->threadpool = this; } int_threadPool::~int_threadPool() { assert(up_pool); delete up_pool; for (vector::iterator i = threads.begin(); i != threads.end(); ++i) { if(ProcPool()->findThread((*i)->getLWP())) { ProcPool()->rmThread(*i); } delete *i; } } int_breakpoint::int_breakpoint(Breakpoint::ptr up) : up_bp(up), to(0x0), isCtrlTransfer_(false), data(NULL), hw(false), hw_perms(0), hw_size(0), onetime_bp(false), onetime_bp_hit(false), procstopper(false), suppress_callbacks(false), offset_transfer(false) { } int_breakpoint::int_breakpoint(Dyninst::Address to_, Breakpoint::ptr up, bool off) : up_bp(up), to(to_), isCtrlTransfer_(true), data(NULL), hw(false), hw_perms(0), hw_size(0), onetime_bp(false), onetime_bp_hit(false), procstopper(false), suppress_callbacks(false), offset_transfer(off) { } int_breakpoint::int_breakpoint(unsigned int hw_prems_, unsigned int hw_size_, Breakpoint::ptr up) : up_bp(up), to(0x0), isCtrlTransfer_(false), data(NULL), hw(true), hw_perms(hw_prems_), hw_size(hw_size_), onetime_bp(false), onetime_bp_hit(false), procstopper(false), suppress_callbacks(false), offset_transfer(false) { } int_breakpoint::~int_breakpoint() { } bool int_breakpoint::isCtrlTransfer() const { return isCtrlTransfer_; } Address int_breakpoint::toAddr() const { return to; } void *int_breakpoint::getData() const { return data; } void int_breakpoint::setData(void *v) { data = v; } Breakpoint::weak_ptr int_breakpoint::upBreakpoint() const { return up_bp; } void int_breakpoint::setThreadSpecific(Thread::const_ptr p) { thread_specific.insert(p); } void int_breakpoint::setOneTimeBreakpoint(bool b) { onetime_bp = b; } void int_breakpoint::markOneTimeHit() { assert(onetime_bp); onetime_bp_hit = true; } bool int_breakpoint::isOneTimeBreakpoint() const { return onetime_bp; } bool int_breakpoint::isOneTimeBreakpointHit() const { return onetime_bp_hit; } bool int_breakpoint::isThreadSpecific() const { return !thread_specific.empty(); } bool int_breakpoint::isThreadSpecificTo(Thread::const_ptr p) const { return thread_specific.find(p) != thread_specific.end(); } void int_breakpoint::setProcessStopper(bool b) { procstopper = b; } bool int_breakpoint::isProcessStopper() const { return procstopper; } bool int_breakpoint::isHW() const { return hw; } unsigned int_breakpoint::getHWSize() const { return hw_size; } unsigned int_breakpoint::getHWPerms() const { return hw_perms; } void int_breakpoint::setSuppressCallbacks(bool b) { suppress_callbacks = b; } bool int_breakpoint::suppressCallbacks() const { return suppress_callbacks; } bool int_breakpoint::isOffsetTransfer() const { return offset_transfer; } bp_instance::bp_instance(Address addr_) : addr(addr_), installed(false), suspend_count(0), swbp(NULL), hwbp(NULL) { } bp_instance::bp_instance(const bp_instance *ip) : bps(ip->bps), hl_bps(ip->hl_bps), addr(ip->addr), installed(ip->installed), suspend_count(ip->suspend_count), swbp(NULL), hwbp(NULL) { } bp_instance::~bp_instance() { } bool bp_instance::checkBreakpoint(int_breakpoint *bp, int_process *p) { if (bp->isCtrlTransfer()) { for (set::iterator i = bps.begin(); i != bps.end(); i++) { if ((*i)->isCtrlTransfer()) { perr_printf("Error. Attempted to add two control transfer breakpoints " "at same place\n"); for (auto j : p->memory()->procs) { j->setLastError(err_badparam, "Attempted two control transfer breakpoints at " "same location\n"); } return false; } } } Breakpoint::ptr upbp = bp->upBreakpoint().lock(); if (upbp != Breakpoint::ptr()) { //We keep the set of installed Breakpoints to keep the breakpoint shared // pointer reference counter from cleaning a Breakpoint while installed. hl_bps.insert(upbp); } bps.insert(bp); return true; } bool bp_instance::rmBreakpoint(int_process *proc, int_breakpoint *bp, bool &empty, set &resps) { empty = false; set::iterator i = bps.find(bp); if (i == bps.end()) { perr_printf("Attempted to remove a non-installed breakpoint\n"); proc->setLastError(err_badparam, "Breakpoint was not installed in process\n"); return false; } bps.erase(i); set::iterator j = hl_bps.find(bp->upBreakpoint().lock()); if (j != hl_bps.end()) { hl_bps.erase(j); } if (bps.empty()) { empty = true; bool result = uninstall(proc, resps); if (!result) { perr_printf("Failed to remove breakpoint at %lx\n", addr); proc->setLastError(err_internal, "Could not remove breakpoint\n"); return false; } } return true; } Address bp_instance::getAddr() const { return addr; } bp_instance::iterator bp_instance::begin() { return bps.begin(); } bp_instance::iterator bp_instance::end() { return bps.end(); } bool bp_instance::containsIntBreakpoint(int_breakpoint *bp) { return (bps.count(bp) > 0); } int_breakpoint *bp_instance::getCtrlTransferBP(int_thread *thrd) { for (iterator i = begin(); i != end(); i++) { int_breakpoint *bp = *i; if (!bp->isCtrlTransfer()) continue; if (thrd && bp->isThreadSpecific() && !bp->isThreadSpecificTo(thrd->thread())) continue; return bp; } return NULL; } bool bp_instance::isInstalled() const { return installed; } sw_breakpoint *bp_instance::swBP() { return swbp; } hw_breakpoint *bp_instance::hwBP() { return hwbp; } bool bp_instance::suspend_common() { suspend_count++; if (suspend_count > 1) { pthrd_printf("Breakpoint already suspended, suspend_count = %d\n", suspend_count); return true; } return false; } bool bp_instance::resume_common() { suspend_count--; assert(suspend_count >= 0); if (suspend_count > 0) { pthrd_printf("Breakpoint remaining suspended, suspend_count = %d\n", suspend_count); return true; } return false; } sw_breakpoint::sw_breakpoint(mem_state::ptr memory_, Address addr_) : bp_instance(addr_), memory(memory_), buffer_size(0), prepped(false), long_breakpoint(false) { swbp = this; hwbp = NULL; } sw_breakpoint::sw_breakpoint(mem_state::ptr memory_, const sw_breakpoint *ip) : bp_instance(ip), memory(memory_), buffer_size(ip->buffer_size), prepped(ip->prepped), long_breakpoint(ip->long_breakpoint) { memcpy(buffer, ip->buffer, sizeof(buffer)); swbp = this; hwbp = NULL; } sw_breakpoint::~sw_breakpoint() { } sw_breakpoint *sw_breakpoint::create(int_process *proc, int_breakpoint *bp, Dyninst::Address addr) { bool result; bp_install_state is; is.addr = addr; is.bp = bp; result = proc->addBreakpoint_phase1(&is); if (!result) return NULL; if (is.mem_resp && is.mem_resp->isPosted()) { result = int_process::waitForAsyncEvent(is.mem_resp); if (!result) { perr_printf("Error waiting for result of memory response\n"); return NULL; } } result = proc->addBreakpoint_phase2(&is); if (!result) return NULL; if (is.res_resp && is.res_resp->isPosted()) { result = int_process::waitForAsyncEvent(is.res_resp); if (!result) { perr_printf("Error waiting for result of result response\n"); return NULL; } } result = proc->addBreakpoint_phase3(&is); if (!result) return NULL; return is.ibp; } bool sw_breakpoint::writeBreakpoint(int_process *proc, result_response::ptr write_response_) { assert(buffer_size != 0); unsigned char bp_insn[BP_BUFFER_SIZE]; proc->plat_breakpointBytes(bp_insn); if (long_breakpoint) { unsigned bp_size = proc->plat_breakpointSize(); for (unsigned i=bp_size; iwriteMem(bp_insn, addr, buffer_size, write_response_, NULL, int_process::bp_install); } bool sw_breakpoint::saveBreakpointData(int_process *proc, mem_response::ptr read_response_) { if (buffer_size != 0) { return true; } buffer_size = proc->plat_breakpointSize(); if (long_breakpoint) { buffer_size += BP_LONG_SIZE; } pthrd_printf("Saving original data for breakpoint insertion at %lx +%u\n", addr, (unsigned) buffer_size); assert(buffer_size <= BP_BUFFER_SIZE); read_response_->setBuffer(buffer, buffer_size); bool ret = proc->readMem(addr, read_response_); if (read_response_->isReady()) { pthrd_printf("Buffer contents from read breakpoint:\n"); for (int i = 0; i < buffer_size; ++i) { pthrd_printf("\t 0x%x\n", (unsigned char)buffer[i]); } } return ret; } bool sw_breakpoint::restoreBreakpointData(int_process *proc, result_response::ptr res_resp) { assert(buffer_size != 0); pthrd_printf("Restoring original code over breakpoint at %lx\n", addr); return proc->writeMem(buffer, addr, buffer_size, res_resp, NULL, int_process::bp_clear); } async_ret_t sw_breakpoint::uninstall(int_process *proc, set &resps) { assert(installed); bool had_success = true; result_response::ptr async_resp = result_response::createResultResponse(); if (proc->getState() != int_process::exited) { bool result = proc->writeMem(&buffer, addr, buffer_size, async_resp); if (!result) { pthrd_printf("Failed to remove breakpoint at %lx from process %d\n", addr, proc->getPid()); had_success = false; } } installed = false; buffer_size = 0; std::map::iterator i; i = memory->breakpoints.find(addr); if (i == memory->breakpoints.end()) { perr_printf("Failed to remove breakpoint from list\n"); proc->setLastError(err_notfound, "Tried to uninstall breakpoint that isn't installed.\n"); return aret_error; } memory->breakpoints.erase(i); if (async_resp->isPosted() && !async_resp->isReady()) { resps.insert(async_resp); return aret_async; } return had_success ? aret_success : aret_error; } async_ret_t sw_breakpoint::suspend(int_process *proc, set &resps) { if (suspend_common()) return aret_success; result_response::ptr result_resp = result_response::createResultResponse(); bool result = restoreBreakpointData(proc, result_resp); if (!result) { pthrd_printf("Failed to suspend breakpoint at %lx from process %d\n", addr, proc->getPid()); return aret_error; } if (result_resp->isPosted() && !result_resp->isReady()) { resps.insert(result_resp); return aret_async; } return aret_success; } async_ret_t sw_breakpoint::resume(int_process *proc, set &resps) { if (resume_common()) return aret_success; result_response::ptr result_resp = result_response::createResultResponse(); bool result = writeBreakpoint(proc, result_resp); if (!result) { pthrd_printf("Failed to install breakpoint at %lx in process %d\n", addr, proc->getPid()); return aret_error; } if (result_resp->isPosted() && !result_resp->isReady()) { resps.insert(result_resp); return aret_async; } return aret_success; } unsigned sw_breakpoint::getNumIntBreakpoints() const { return bps.size(); } bool sw_breakpoint::addToIntBreakpoint(int_breakpoint *bp, int_process *) { memory->breakpoints[addr] = this; Breakpoint::ptr upbp = bp->upBreakpoint().lock(); if (upbp != Breakpoint::ptr()) { //We keep the set of installed Breakpoints to keep the breakpoint shared // pointer reference counter from cleaning a Breakpoint while installed. hl_bps.insert(upbp); } bps.insert(bp); return true; } bool sw_breakpoint::prepBreakpoint(int_process *proc, mem_response::ptr mem_resp) { assert(!prepped); assert(!installed); pthrd_printf("Prepping breakpoint at %lx\n", addr); bool result = saveBreakpointData(proc, mem_resp); if (!result) { pthrd_printf("Error, failed to save breakpoint data at %lx\n", addr); return false; } prepped = true; return true; } bool sw_breakpoint::insertBreakpoint(int_process *proc, result_response::ptr res_resp) { assert(prepped); assert(!installed); bool result = writeBreakpoint(proc, res_resp); if (!result) { pthrd_printf("Error writing breakpoint\n"); return false; } installed = true; return true; } bool sw_breakpoint::needsClear() { return true; } hw_breakpoint::hw_breakpoint(int_thread *thread, unsigned mode, unsigned size, bool pwide, Dyninst::Address addr_) : bp_instance(addr_), hw_perms(mode), hw_size(size), proc_wide(pwide), thr(thread), error(false) { swbp = NULL; hwbp = this; } hw_breakpoint::~hw_breakpoint() { } bool hw_breakpoint::install(bool &done, set &resps) { bool result = thr->addHWBreakpoint(this, false, resps, done); if (!result) return false; if (!done) return true; thr->hw_breakpoints.insert(this); return true; } hw_breakpoint *hw_breakpoint::create(int_process *proc, int_breakpoint *bp, Dyninst::Address addr) { unsigned mode = bp->getHWPerms(); unsigned size = bp->getHWSize(); set thrds; bool proc_wide = bp->thread_specific.empty(); if (!proc_wide) { for (set::iterator i = bp->thread_specific.begin(); i != bp->thread_specific.end(); i++) { int_thread *llthrd = (*i)->llthrd(); thrds.insert(llthrd); pthrd_printf("Adding thread-specific hardware breakpoint to %d/%d at %lx\n", proc->getPid(), llthrd->getLWP(), addr); } } else { int_threadPool *tp = proc->threadPool(); for (int_threadPool::iterator i = tp->begin(); i != tp->end(); i++) { int_thread *llthrd = *i; thrds.insert(llthrd); pthrd_printf("Adding process-wide hardware breakpoint to %d/%d at %lx\n", proc->getPid(), llthrd->getLWP(), addr); } } bool no_space = false; for (set::iterator i = thrds.begin(); i != thrds.end(); i++) { int_thread *thr = *i; if (!thr->hwBPAvail(mode)) { perr_printf("Do not have space for new hardware breakpoint of mode 0%u on thread %d/%d\n", mode, proc->getPid(), thr->getLWP()); no_space = true; } } if (no_space) { proc->setLastError(err_bpfull, "Not enough space for a new hardware breakpoint\n"); return NULL; } set hw_bps; hw_breakpoint *first_bp = NULL; for (set::iterator i = thrds.begin(); i != thrds.end(); i++) { int_thread *thr = *i; hw_breakpoint *hwbp = new hw_breakpoint(thr, mode, size, proc_wide, addr); assert(hwbp); if (!first_bp) first_bp = hwbp; hw_bps.insert(hwbp); hwbp->installed = true; if (!hwbp->checkBreakpoint(bp, proc)) return NULL; } bool had_error = false; while (!hw_bps.empty()) { set resps; for (set::iterator i = hw_bps.begin(); i != hw_bps.end();) { hw_breakpoint *bps = *i; bool done = false; bool result = bps->install(done, resps); if (!result) { pthrd_printf("Error installing HW breakpoint on %d/%d at %lx\n", proc->getPid(), bps->getThread()->getLWP(), addr); had_error = true; break; } if (done) i = hw_bps.erase(i); else i++; } bool result = int_process::waitForAsyncEvent(resps); if (!result || had_error) { pthrd_printf("Error during HW BP install\n"); return NULL; } } return first_bp; } async_ret_t hw_breakpoint::suspend(int_process *, set &resps) { if (suspend_common()) return aret_success; bool done = false; bool result = thr->rmHWBreakpoint(this, true, resps, done); if (!result) { pthrd_printf("Error suspending HWBreakpoint\n"); return aret_error; } if (!done) { assert(!resps.empty()); return aret_async; } return aret_success; } async_ret_t hw_breakpoint::resume(int_process *, set &resps) { if (resume_common()) return aret_success; bool done = false; bool result = thr->addHWBreakpoint(this, true, resps, done); if (!result) { pthrd_printf("Error resuming HWBreakpoint\n"); return aret_error; } if (!done) { assert(!resps.empty()); return aret_error; } return aret_success; } async_ret_t hw_breakpoint::uninstall(int_process *proc, set &resps) { bool done = false; set::iterator ibp = thr->hw_breakpoints.find(this); if (ibp == thr->hw_breakpoints.end()) { perr_printf("Tried to uninstall non-installed hardware breakpoint on %d/%d\n", thr->llproc()->getPid(), thr->getLWP()); proc->setLastError(err_badparam, "Tried to uninstall non-installed hardware breakpoint\n"); return aret_error; } bool result = thr->rmHWBreakpoint(this, false, resps, done); if (!result) { pthrd_printf("Error suspending HWBreakpoint\n"); return aret_error; } thr->hw_breakpoints.erase(ibp); installed = false; if (!done) { assert(!resps.empty()); return aret_async; } return aret_success; } unsigned int hw_breakpoint::getPerms() const { return hw_perms; } unsigned int hw_breakpoint::getSize() const { return hw_size; } int_thread *hw_breakpoint::getThread() const { return thr; } bool hw_breakpoint::needsClear() { return thr->bpNeedsClear(this); } int_library::int_library(std::string n, bool shared_lib, Dyninst::Address load_addr, Dyninst::Address dynamic_load_addr, Dyninst::Address data_load_addr, bool has_data_load_addr) : name(n), load_address(load_addr), data_load_address(data_load_addr), dynamic_address(dynamic_load_addr), sysv_map_address(0), has_data_load(has_data_load_addr), marked(false), user_data(NULL), is_shared_lib(shared_lib), memory(NULL) { // assert(n != ""); up_lib = Library::ptr(new Library()); up_lib->lib = this; } int_library::int_library(int_library *l) : name(l->name), load_address(l->load_address), data_load_address(l->data_load_address), dynamic_address(l->dynamic_address), sysv_map_address(l->sysv_map_address), has_data_load(l->has_data_load), marked(l->marked), user_data(NULL), is_shared_lib(l->is_shared_lib), memory(NULL) { up_lib = Library::ptr(new Library()); up_lib->lib = this; } int_library::~int_library() { } std::string int_library::getName() { return name; } std::string int_library::getAbsName() { if (!abs_name.empty()) return abs_name; abs_name = int_process::plat_canonicalizeFileName(name); return abs_name; } Dyninst::Address int_library::getAddr() { return load_address; } Dyninst::Address int_library::getDataAddr() { return data_load_address; } bool int_library::hasDataAddr() { return has_data_load; } Dyninst::Address int_library::getDynamicAddr() { return dynamic_address; } void int_library::setLoadAddress(Address addr) { load_address = addr; } void int_library::setDynamicAddress(Address addr) { dynamic_address = addr; } Address int_library::mapAddress() { return sysv_map_address; } void int_library::setMapAddress(Address a) { sysv_map_address = a; } bool int_library::inProcess(int_process *p) { return (p->memory() == memory); } bool int_library::isSharedLib() const { return is_shared_lib; } void int_library::setMark(bool b) { marked = b; } bool int_library::isMarked() const { return marked; } void int_library::setUserData(void *d) { user_data = d; } void *int_library::getUserData() { return user_data; } Library::ptr int_library::getUpPtr() const { return up_lib; } void int_library::markAsCleanable() { //The destruction of the Library may destroy 'this', which //can cause an invalid heap write in the 'up_lib = Library::ptr()' //assignment. By keeping a second reference around (delay_any_clean) //we make sure that up_lib is cleaned after it's assigned. Library::ptr delay_any_clean = up_lib; up_lib = Library::ptr(); } mem_state::mem_state(int_process *proc) { procs.insert(proc); } mem_state::mem_state(mem_state &m, int_process *p) { pthrd_printf("Copying mem_state to new process %d\n", p->getPid()); procs.insert(p); // Do not copy over libraries -- need to use refresh_libraries to // maintain consistency with AddressTranslate layer /* set::iterator i; for (i = m.libs.begin(); i != m.libs.end(); i++) { int_library *orig_lib = *i; int_library *new_lib = new int_library(orig_lib); libs.insert(new_lib); } */ map::iterator j; for (j = m.breakpoints.begin(); j != m.breakpoints.end(); j++) { Address orig_addr = j->first; sw_breakpoint *orig_bp = j->second; sw_breakpoint *new_bp = new sw_breakpoint(this, orig_bp); breakpoints[orig_addr] = new_bp; } inf_malloced_memory = m.inf_malloced_memory; } mem_state::~mem_state() { pthrd_printf("Destroy memory image of old process\n"); set::iterator i; for (i = libs.begin(); i != libs.end(); i++) { (*i)->markAsCleanable(); } libs.clear(); map::iterator j; for (j = breakpoints.begin(); j != breakpoints.end(); j++) { sw_breakpoint *ibp = j->second; delete ibp; } breakpoints.clear(); } void mem_state::addProc(int_process *p) { pthrd_printf("Adding process %d as sharing a memory state with existing proc\n", p->getPid()); procs.insert(p); } void mem_state::rmProc(int_process *p, bool &should_clean) { set::iterator i = procs.find(p); assert(i != procs.end()); procs.erase(i); if (procs.empty()) { should_clean = true; pthrd_printf("Removed process %d from memory image, should clean image\n", p->getPid()); } else { should_clean = false; pthrd_printf("Removed process %d from memory image, others remain\n", p->getPid()); } } void mem_state::addLibrary(int_library *lib) { libs.insert(lib); lib->memory = this; } void mem_state::rmLibrary(int_library *lib) { set::iterator i = libs.find(lib); if (i == libs.end()) return; libs.erase(i); lib->memory = NULL; } int_notify *int_notify::the_notify = NULL; int_notify::int_notify() : events_noted(0) { the_notify = this; up_notify.llnotify = this; } int_notify *notify() { if (int_notify::the_notify) return int_notify::the_notify; static Mutex<> init_lock; init_lock.lock(); if (!int_notify::the_notify) { int_notify::the_notify = new int_notify(); } init_lock.unlock(); return int_notify::the_notify; } void int_notify::noteEvent() { //MATT TODO lock around event pipe write/read when/if we move to process locks assert(isHandlerThread()); if (events_noted == 0) my_internals.noteEvent(); events_noted++; pthrd_printf("noteEvent - %d\n", events_noted); set::iterator i; for (i = cbs.begin(); i != cbs.end(); ++i) { pthrd_printf("Calling notification CB\n"); (*i)(); } } static void notifyNewEvent() { notify()->noteEvent(); } void int_notify::clearEvent() { assert(!isHandlerThread()); events_noted--; pthrd_printf("clearEvent - %d\n", events_noted); if (events_noted == 0) my_internals.clearEvent(); } bool int_notify::hasEvents() { return (events_noted > 0); } void int_notify::registerCB(EventNotify::notify_cb_t cb) { cbs.insert(cb); } void int_notify::removeCB(EventNotify::notify_cb_t cb) { set::iterator i = cbs.find(cb); if (i == cbs.end()) return; cbs.erase(i); } int_notify::details_t::wait_object_t int_notify::getWaitable() { if(!my_internals.internalsValid()) { my_internals.createInternals(); } return my_internals.getWaitObject(); } Decoder::Decoder() { } Decoder::~Decoder() { } RegisterPool::RegisterPool() { llregpool = new int_registerPool(); } RegisterPool::RegisterPool(const RegisterPool &rp) { llregpool = new int_registerPool(); *llregpool = *rp.llregpool; } RegisterPool::~RegisterPool() { delete llregpool; } RegisterPool::iterator RegisterPool::begin() { return RegisterPool::iterator(llregpool->regs.begin()); } RegisterPool::iterator RegisterPool::end() { return RegisterPool::iterator(llregpool->regs.end()); } RegisterPool::iterator RegisterPool::find(MachRegister r) { return RegisterPool::iterator(llregpool->regs.find(r)); } bool RegisterPool::iterator::operator==(const iterator &iter) const { return i == iter.i; } bool RegisterPool::iterator::operator!=(const iterator &iter) const { return i != iter.i; } RegisterPool::const_iterator RegisterPool::begin() const { return RegisterPool::const_iterator(llregpool->regs.begin()); } RegisterPool::const_iterator RegisterPool::end() const { return RegisterPool::const_iterator(llregpool->regs.end()); } RegisterPool::const_iterator RegisterPool::find(MachRegister r) const { return RegisterPool::const_iterator(llregpool->regs.find(r)); } bool RegisterPool::const_iterator::operator==(const const_iterator &iter) const { return i == iter.i; } bool RegisterPool::const_iterator::operator!=(const const_iterator &iter) const { return i != iter.i; } MachRegisterVal& RegisterPool::operator[](MachRegister r) { return llregpool->regs[r]; } const MachRegisterVal& RegisterPool::operator[](MachRegister r) const { return llregpool->regs[r]; } size_t RegisterPool::size() const { return llregpool->regs.size(); } Thread::ptr RegisterPool::getThread() { return llregpool->thread->thread(); } Thread::const_ptr RegisterPool::getThread() const { return llregpool->thread->thread(); } RegisterPool::iterator::iterator() { } RegisterPool::iterator::iterator(int_iter i_) : i(i_) { } std::pair RegisterPool::iterator::operator*() { return *i; } RegisterPool::iterator RegisterPool::iterator::operator++() // prefix { i++; return *this; } RegisterPool::iterator RegisterPool::iterator::operator++(int) // postfix { RegisterPool::iterator orig = *this; i++; return orig; } RegisterPool::const_iterator::const_iterator() { } RegisterPool::const_iterator::const_iterator(int_iter i_) : i(i_) { } std::pair RegisterPool::const_iterator::operator*() const { return *i; } RegisterPool::const_iterator RegisterPool::const_iterator::operator++() // prefix { i++; return *this; } RegisterPool::const_iterator RegisterPool::const_iterator::operator++(int) // postfix { RegisterPool::const_iterator orig = *this; i++; return orig; } void regpoolClearOnCont(int_thread *thr) { pthrd_printf("Clearing register pool for %d/%d\n", thr->llproc()->getPid(), thr->getLWP()); thr->clearRegCache(); } int_registerPool::int_registerPool() : full(false), thread(NULL) { static bool is_inited = false; if (!is_inited) { is_inited = true; int_thread::addContinueCB(regpoolClearOnCont); } } Library::Library() : lib(NULL) { } Library::~Library() { MTLock lock_this_func(MTLock::allow_generator); if (lib) { delete lib; lib = NULL; } } std::string Library::getName() const { MTLock lock_this_func; return lib->getName(); } std::string Library::getAbsoluteName() const { MTLock lock_this_func; return lib->getAbsName(); } Dyninst::Address Library::getLoadAddress() const { MTLock lock_this_func; return lib->getAddr(); } Dyninst::Address Library::getDataLoadAddress() const { MTLock lock_this_func; return lib->getDataAddr(); } Dyninst::Address Library::getDynamicAddress() const { return lib->getDynamicAddr(); } bool Library::isSharedLib() const { return lib->isSharedLib(); } void *Library::getData() const { return lib->getUserData(); } void Library::setData(void *p) const { lib->setUserData(p); } LibraryPool::LibraryPool() : proc(NULL) { } LibraryPool::~LibraryPool() { } size_t LibraryPool::size() const { MTLock lock_this_func; if (!proc) { perr_printf("getExecutable on deleted process\n"); globalSetLastError(err_exited, "Process is exited\n"); return -1; } return proc->numLibs(); } Library::ptr LibraryPool::getLibraryByName(std::string s) { MTLock lock_this_func; if (!proc) { perr_printf("getLibraryByName on deleted process\n"); globalSetLastError(err_exited, "Process is exited\n"); return Library::ptr(); } int_library *int_lib = proc->getLibraryByName(s); if (!int_lib) return Library::ptr(); return int_lib->up_lib; } Library::const_ptr LibraryPool::getLibraryByName(std::string s) const { MTLock lock_this_func; if (!proc) { perr_printf("getLibraryByName on deleted process\n"); globalSetLastError(err_exited, "Process is exited\n"); return Library::ptr(); } int_library *int_lib = proc->getLibraryByName(s); if (!int_lib) return Library::const_ptr(); return int_lib->up_lib; } Library::ptr LibraryPool::getExecutable() { MTLock lock_this_func; if (!proc) { perr_printf("getExecutable on deleted process\n"); globalSetLastError(err_exited, "Process is exited\n"); return Library::ptr(); } return proc->plat_getExecutable()->up_lib; } Library::const_ptr LibraryPool::getExecutable() const { MTLock lock_this_func; if (!proc) { perr_printf("getExecutable on deleted process\n"); globalSetLastError(err_exited, "Process is exited\n"); return Library::ptr(); } return proc->plat_getExecutable()->up_lib; } LibraryPool::iterator LibraryPool::find(Library::ptr lib) { LibraryPool::iterator i{}; i.int_iter = proc->memory()->libs.find(lib->debug()); return i; } LibraryPool::const_iterator LibraryPool::find(Library::ptr lib) const { LibraryPool::const_iterator i{}; i.int_iter = proc->memory()->libs.find(lib->debug()); return i; } LibraryPool::iterator::iterator() { } Library::ptr LibraryPool::iterator::operator*() const { return (*int_iter)->up_lib; } LibraryPool::iterator LibraryPool::iterator::operator++() // prefix { ++int_iter; return *this; } LibraryPool::iterator LibraryPool::iterator::operator++(int) // postfix { LibraryPool::iterator orig = *this; ++int_iter; return orig; } LibraryPool::iterator LibraryPool::begin() { LibraryPool::iterator i{}; i.int_iter = proc->memory()->libs.begin(); return i; } LibraryPool::iterator LibraryPool::end() { LibraryPool::iterator i{}; i.int_iter = proc->memory()->libs.end(); return i; } bool LibraryPool::iterator::operator==(const LibraryPool::iterator &i) const { return int_iter == i.int_iter; } bool LibraryPool::iterator::operator!=(const LibraryPool::iterator &i) const { return int_iter != i.int_iter; } LibraryPool::const_iterator LibraryPool::begin() const { LibraryPool::const_iterator i{}; i.int_iter = proc->memory()->libs.begin(); return i; } LibraryPool::const_iterator LibraryPool::end() const { LibraryPool::const_iterator i{}; i.int_iter = proc->memory()->libs.end(); return i; } LibraryPool::const_iterator::const_iterator() { } Library::const_ptr LibraryPool::const_iterator::operator*() const { return (*int_iter)->up_lib; } bool LibraryPool::const_iterator::operator==(const LibraryPool::const_iterator &i) const { return int_iter == i.int_iter; } bool LibraryPool::const_iterator::operator!=(const LibraryPool::const_iterator &i) const { return int_iter != i.int_iter; } LibraryPool::const_iterator LibraryPool::const_iterator::operator++() // prefix { ++int_iter; return *this; } LibraryPool::const_iterator LibraryPool::const_iterator::operator++(int) // postfix { LibraryPool::const_iterator orig = *this; ++int_iter; return orig; } bool Process::registerEventCallback(EventType evt, Process::cb_func_t cbfunc) { MTLock lock_this_func(MTLock::allow_init); HandleCallbacks *cbhandler = HandleCallbacks::getCB(); return cbhandler->registerCallback(evt, cbfunc); } bool Process::removeEventCallback(EventType evt, cb_func_t cbfunc) { MTLock lock_this_func(MTLock::allow_init); HandleCallbacks *cbhandler = HandleCallbacks::getCB(); return cbhandler->removeCallback(evt, cbfunc); } bool Process::removeEventCallback(EventType evt) { MTLock lock_this_func(MTLock::allow_init); HandleCallbacks *cbhandler = HandleCallbacks::getCB(); return cbhandler->removeCallback(evt); } bool Process::removeEventCallback(cb_func_t cbfunc) { MTLock lock_this_func(MTLock::allow_init); HandleCallbacks *cbhandler = HandleCallbacks::getCB(); return cbhandler->removeCallback(cbfunc); } bool Process::handleEvents(bool block) { // One might think we should be delivering callbacks when taking this lock, // but we'll do it under waitAndHandleEvent instead. MTLock lock_this_func(MTLock::allow_init); pthrd_printf("User triggered event handling\n"); if (int_process::isInCB()) { perr_printf("User attempted call on process while in CB, erroring."); ProcControlAPI::globalSetLastError(err_incallback, "Cannot handleEvents from callback\n"); return false; } bool result = int_process::waitAndHandleEvents(block); if (!result) { if (!block && ProcControlAPI::getLastError() == err_noevents) pthrd_printf("Polling Process::handleEvents returning false due to no events\n"); else pthrd_printf("Error handling events for user\n"); return false; } return true; } bool Process::setThreadingMode(thread_mode_t tm) { MTLock lock_this_func(MTLock::allow_init); return mt()->setThreadMode(tm); } Process::ptr Process::createProcess(std::string executable, const std::vector &argv, const std::vector &envp, const std::map &fds) { MTLock lock_this_func(MTLock::allow_init, MTLock::deliver_callbacks); pthrd_printf("User asked to launch executable %s\n", executable.c_str()); if (int_process::isInCB()) { perr_printf("User attempted call on process create while in CB, erroring."); ProcControlAPI::globalSetLastError(err_incallback, "Cannot createProcess from callback\n"); return Process::ptr(); } ProcPool()->condvar()->lock(); Process::ptr newproc(new Process()); int_process *llproc = int_process::createProcess(executable, argv, envp, fds); llproc->initializeProcess(newproc); int_processSet the_proc; the_proc.insert(newproc); bool result = int_process::create(&the_proc); //Releases procpool lock if (!result) { pthrd_printf("Unable to create process %s\n", executable.c_str()); return Process::ptr(); } return newproc; } Process::ptr Process::attachProcess(Dyninst::PID pid, std::string executable) { MTLock lock_this_func(MTLock::allow_init, MTLock::deliver_callbacks); pthrd_printf("User asked to attach to process %d (%s)\n", pid, executable.c_str()); if (int_process::isInCB()) { perr_printf("User attempted call on process attach while in CB, erroring.\n"); ProcControlAPI::globalSetLastError(err_incallback, "Cannot attachProcess from callback\n"); return Process::ptr(); } ProcPool()->condvar()->lock(); Process::ptr newproc(new Process()); int_process *llproc = int_process::createProcess(pid, executable); llproc->initializeProcess(newproc); int_processSet the_proc; the_proc.insert(newproc); bool result = llproc->attach(&the_proc, false); //Releases procpool lock if (!result) { pthrd_printf("Unable to attach to process %d\n", pid); delete llproc; return Process::ptr(); } return newproc; } Process::Process() : llproc_(NULL), exitstate_(NULL) { } Process::~Process() { if (exitstate_) { delete exitstate_; exitstate_ = NULL; } } void *Process::getData() const { MTLock lock_this_func; if (exitstate_) { return exitstate_->user_data; } return llproc_->user_data; } void Process::setData(void *p) const { MTLock lock_this_func; if (exitstate_) { exitstate_->user_data = p; } else { llproc_->user_data = p; } } Dyninst::PID Process::getPid() const { MTLock lock_this_func(MTLock::allow_generator); if (!llproc_) { assert(exitstate_); if(!exitstate_) return 0; return exitstate_->pid; } return llproc_->getPid(); } const ThreadPool &Process::threads() const { MTLock lock_this_func; static ThreadPool *err_pool; if (!llproc_) { perr_printf("threads on deleted process\n"); setLastError(err_exited, "Process is exited\n"); if (!err_pool) { err_pool = new ThreadPool(); } return *err_pool; } return *(llproc_->threadPool()->pool()); } ThreadPool &Process::threads() { MTLock lock_this_func; static ThreadPool *err_pool; if (!llproc_) { perr_printf("threads on deleted process\n"); setLastError(err_exited, "Process is exited\n"); if (!err_pool) { err_pool = new ThreadPool(); } return *err_pool; } return *(llproc_->threadPool()->pool()); } const LibraryPool &Process::libraries() const { MTLock lock_this_func; static LibraryPool *err_pool; if (!llproc_) { perr_printf("libraries on deleted process\n"); setLastError(err_exited, "Process is exited\n"); if (!err_pool) { err_pool = new LibraryPool(); err_pool->proc = NULL; } return *err_pool; } return llproc_->libpool; } LibraryPool &Process::libraries() { MTLock lock_this_func; static LibraryPool *err_pool; if (!llproc_) { perr_printf("libraries on deleted process\n"); setLastError(err_exited, "Process is exited\n"); if (!err_pool) { err_pool = new LibraryPool(); err_pool->proc = NULL; } return *err_pool; } return llproc_->libpool; } bool Process::addLibrary(std::string library) { MTLock lock_this_func; if (!llproc_) { perr_printf("addLibrary on deleted process\n"); setLastError(err_exited, "Process is exited\n"); return false; } if (llproc_->getState() == int_process::detached) { perr_printf("addLibrary on detached process\n"); setLastError(err_detached, "Process is detached\n"); return false; } if (int_process::isInCB()) { perr_printf("User attempted addLibrary while in CB, erroring."); setLastError(err_incallback, "Cannot addLibrary from callback\n"); return false; } if (hasRunningThread()) { perr_printf("User attempted to addLibrary on running process\n"); setLastError(err_notstopped, "Attempted to addLibrary on running process\n"); return false; } Injector inj(this); return inj.inject(library); } bool Process::continueProc() { Process::ptr me = shared_from_this(); ProcessSet::ptr ps = ProcessSet::newProcessSet(me); return ps->continueProcs(); } bool Process::isCrashed() const { MTLock lock_this_func; if (!llproc_) { assert(exitstate_); return exitstate_->crashed; } int crashSignal = 0; return (llproc_->getState() == int_process::exited && llproc_->getCrashSignal(crashSignal)); } bool Process::isExited() const { MTLock lock_this_func; int exitCode = 0; if (!llproc_) { assert(exitstate_); return exitstate_->exited; } return (llproc_->getState() == int_process::exited && llproc_->getExitCode(exitCode)); } int Process::getCrashSignal() const { MTLock lock_this_func; if (!llproc_) { assert(exitstate_); return exitstate_->crashed ? exitstate_->crash_signal : 0; } int crashSignal = 0; if (!(llproc_->getState() == int_process::exited && llproc_->getCrashSignal(crashSignal))) return 0; return crashSignal; } int Process::getExitCode() const { MTLock lock_this_func; if (!llproc_) { assert(exitstate_); return !exitstate_->crashed ? exitstate_->exit_code : 0; } int exitCode = 0; if (!(llproc_->getState() == int_process::exited && llproc_->getExitCode(exitCode))) return 0; return exitCode; } bool Process::isDetached() const { MTLock lock_this_func; PROC_EXIT_TEST("isDetached", false); return llproc_->getState() == int_process::detached; } bool Process::stopProc() { ProcessSet::ptr ps = ProcessSet::newProcessSet(shared_from_this()); return ps->stopProcs(); } bool Process::detach(bool leaveStopped) { ProcessSet::ptr ps = ProcessSet::newProcessSet(shared_from_this()); return ps->detach(leaveStopped); } bool Process::temporaryDetach() { ProcessSet::ptr ps = ProcessSet::newProcessSet(shared_from_this()); return ps->temporaryDetach(); } bool Process::reAttach() { ProcessSet::ptr ps = ProcessSet::newProcessSet(shared_from_this()); return ps->reAttach(); } bool Process::terminate() { if (!llproc_) return true; // Already terminated ProcessSet::ptr ps = ProcessSet::newProcessSet(shared_from_this()); bool ret = ps->terminate(); return ret; } bool Process::isTerminated() const { MTLock lock_this_func; if (!llproc_) { return true; } return (llproc_->getState() == int_process::exited); } bool Process::hasStoppedThread() const { MTLock lock_this_func; PROC_EXIT_TEST("hasStoppedThread", false); int_threadPool::iterator i; for (i = llproc_->threadPool()->begin(); i != llproc_->threadPool()->end(); i++) { if ((*i)->getUserState().getState() == int_thread::stopped) return true; } return false; } bool Process::hasRunningThread() const { MTLock lock_this_func; PROC_EXIT_TEST("hasRunningThread", false); int_threadPool::iterator i; for (i = llproc_->threadPool()->begin(); i != llproc_->threadPool()->end(); ++i) { // Skip truly not-us-system-threads if (!(*i)->isUser() && !(*i)->isRPCEphemeral()) continue; if ((*i)->getUserState().getState() == int_thread::running) return true; } return false; } bool Process::allThreadsStopped() const { MTLock lock_this_func; PROC_EXIT_TEST("allThreadsStopped", false); int_threadPool::iterator i; for (i = llproc_->threadPool()->begin(); i != llproc_->threadPool()->end(); i++) { if ((*i)->getUserState().getState() == int_thread::running || (*i)->getUserState().getState() == int_thread::detached) return false; } return true; } bool Process::allThreadsRunning() const { MTLock lock_this_func; PROC_EXIT_TEST("allThreadsRunning", false); int_threadPool::iterator i; for (i = llproc_->threadPool()->begin(); i != llproc_->threadPool()->end(); i++) { if ((*i)->getUserState().getState() == int_thread::stopped || (*i)->getUserState().getState() == int_thread::detached) return false; } return true; } bool Process::allThreadsRunningWhenAttached() const { MTLock lock_this_func; PROC_EXIT_TEST("allThreadsRunningWhenAttached", false); for(int_threadPool::iterator i = llproc_->threadPool()->begin(); i != llproc_->threadPool()->end(); ++i) { if( !(*i)->wasRunningWhenAttached() ) return false; } return true; } bool Process::runIRPCAsync(IRPC::ptr irpc) { MTLock lock_this_func; PROC_EXIT_DETACH_TEST("runIRPCAsync", false); int_process *proc = llproc(); int_iRPC::ptr rpc = irpc->llrpc()->rpc; rpc->setAsync(false); bool result = false; if (rpc->thread()) { result = rpcMgr()->postRPCToThread(rpc->thread(), rpc); } else { result = rpcMgr()->postRPCToProc(proc, rpc); } if (!result) { pthrd_printf("postRPCToProc failed on %d\n", proc->getPid()); return false; } int_thread::State user_state = rpc->thread()->getUserState().getState(); rpc->setRestoreToState(user_state); result = rpc->thread()->getUserState().setState(int_thread::running); if (!result) { setLastError(err_internal, "Could not continue thread choosen for iRPC\n"); perr_printf("Could not run user thread %d/%d\n", proc->getPid(), rpc->thread()->getLWP()); return false; } //#sasha remove this afterwards //cerr << "Amount of threads in process: " << proc->threadPool()->size() <threadPool()->begin()); //thread->setSingleStepMode(true); llproc_->throwNopEvent(); return true; } bool Process::runIRPCSync(IRPC::ptr irpc) { MTLock lock_this_func; PROC_EXIT_DETACH_CB_TEST("runIRPCSync", false); pthrd_printf("Running SYNC RPC\n"); bool result = runIRPCAsync(irpc); if (!result) return false; bool exited = false; int_process *proc = llproc(); int_iRPC::ptr int_rpc = irpc->llrpc()->rpc; while (irpc->state() != IRPC::Done) { int_thread *thr = int_rpc->thread(); if (thr && thr->isStopped(int_thread::UserStateID)) { pthrd_printf("RPC thread %d/%d was stopped during runIRPCSync, returning notrunning error\n", proc->getPid(), thr->getLWP()); setLastError(err_notrunning, "No threads are running to produce events\n"); return false; } result = int_process::waitAndHandleForProc(true, proc, exited); if (exited) { perr_printf("Process %d exited while waiting for irpc completion\n", getPid()); setLastError(err_exited, "Process exited during IRPC"); return false; } if (!result) { if (getLastError() == err_notrunning) pthrd_printf("RPC thread was stopped during runIRPCSync\n"); else perr_printf("Error waiting for process to finish iRPC\n"); return false; } } return true; } // Apologies for the code duplication; if this works, refactor. bool Thread::runIRPCAsync(IRPC::ptr irpc) { int_iRPC::ptr rpc = irpc->llrpc()->rpc; rpc->setThread(llthrd()); return getProcess()->runIRPCAsync(irpc); } bool Thread::runIRPCSync(IRPC::ptr irpc) { int_iRPC::ptr rpc = irpc->llrpc()->rpc; rpc->setThread(llthrd()); return getProcess()->runIRPCSync(irpc); } bool Process::postIRPC(IRPC::ptr irpc) const { MTLock lock_this_func; PROC_EXIT_DETACH_TEST("postIRPC", false); int_process *proc = llproc(); int_iRPC::ptr rpc = irpc->llrpc()->rpc; bool result = rpcMgr()->postRPCToProc(proc, rpc); if (!result) { pthrd_printf("postRPCToProc failed on %d\n", proc->getPid()); return false; } llproc_->throwNopEvent(); return true; } bool Process::getPostedIRPCs(std::vector &rpcs) const { MTLock lock_this_func; PROC_EXIT_DETACH_TEST("getPostedIRPCs", false); int_threadPool *tp = llproc()->threadPool(); for (int_threadPool::iterator i = tp->begin(); i != tp->end(); ++i) { int_thread *thr = *i; rpc_list_t *rpc_list = thr->getPostedRPCs(); for (rpc_list_t::iterator j = rpc_list->begin(); j != rpc_list->end(); ++j) { IRPC::ptr up_rpc = (*j)->getIRPC().lock(); if (up_rpc == IRPC::ptr()) continue; rpcs.push_back(up_rpc); } } return true; } Dyninst::Architecture Process::getArchitecture() const { MTLock lock_this_func; PROC_EXIT_TEST("getArchitecture", Dyninst::Arch_none); return llproc_->getTargetArch(); } Dyninst::OSType Process::getOS() const { MTLock lock_this_func; PROC_EXIT_TEST("getOS", Dyninst::OSNone); return llproc_->getOS(); } bool Process::supportsLWPEvents() const { MTLock lock_this_func; PROC_EXIT_TEST("supportsLWPEvents", false); //Intentionally not testing plat_supportLWP*Destroy, which is complicated on BG return llproc_->plat_supportLWPCreate(); } bool Process::supportsUserThreadEvents() const { MTLock lock_this_func; PROC_EXIT_TEST("supportsUserThreadEvents", false); return llproc_->plat_supportThreadEvents(); } bool Process::supportsFork() const { MTLock lock_this_func; PROC_EXIT_TEST("supportsFork", false); return llproc_->plat_supportFork(); } bool Process::supportsExec() const { MTLock lock_this_func; PROC_EXIT_TEST("supportsExec", false); return llproc_->plat_supportExec(); } int Process::mem_perm::permVal() const { int tmp = 0; if (read) tmp += 4; if (write) tmp += 2; if (execute) tmp += 1; return tmp; } bool Process::mem_perm::operator<(const mem_perm& p) const { return permVal() < p.permVal(); } bool Process::mem_perm::operator==(const mem_perm& p) const { return (read == p.read) && (write == p.write) && (execute == p.execute); } bool Process::mem_perm::operator!=(const mem_perm& p) const { return !(*this == p); } std::string Process::mem_perm::getPermName() const { if (isNone()) { return "NONE"; } else if (isR()) { return "R"; } else if (isX()) { return "X"; } else if (isRW()) { return "RW"; } else if (isRX()) { return "RX"; } else if (isRWX()) { return "RWX"; } else { return "Unsupported Permission"; } } unsigned Process::getMemoryPageSize() const { if (!llproc_) { perr_printf("getMemoryPageSize on deleted process\n"); setLastError(err_exited, "Process is exited\n"); return false; } if (llproc_->getState() == int_process::detached) { perr_printf("getMemoryPageSize on detached process\n"); setLastError(err_detached, "Process is detached\n"); return false; } return llproc_->getTargetPageSize(); } Dyninst::Address Process::mallocMemory(size_t size, Dyninst::Address addr) { PROC_EXIT_DETACH_CB_TEST("mallocMemory", 0); ProcessSet::ptr pset = ProcessSet::newProcessSet(shared_from_this()); AddressSet::ptr addrset = AddressSet::newAddressSet(pset, addr); bool result = pset->mallocMemory(size, addrset); if (!result) { return 0; } AddressSet::iterator i = addrset->begin(); return i->first; } Dyninst::Address Process::mallocMemory(size_t size) { PROC_EXIT_DETACH_CB_TEST("mallocMemory", 0); ProcessSet::ptr pset = ProcessSet::newProcessSet(shared_from_this()); AddressSet::ptr addr_result = pset->mallocMemory(size); if (addr_result->empty()) { return 0; } return addr_result->begin()->first; } Dyninst::Address Process::findFreeMemory(size_t size) { PROC_EXIT_DETACH_TEST("findFreeMemory", 0); return llproc()->plat_findFreeMemory(size); } bool Process::freeMemory(Dyninst::Address addr) { PROC_EXIT_DETACH_CB_TEST("freeMemory", 0); Process::ptr this_ptr = shared_from_this(); ProcessSet::ptr pset = ProcessSet::newProcessSet(this_ptr); AddressSet::ptr addrs = AddressSet::newAddressSet(pset, addr); return pset->freeMemory(addrs); } bool Process::writeMemory(Dyninst::Address addr, const void *buffer, size_t size) const { MTLock lock_this_func; PROC_EXIT_DETACH_TEST("writeMemory", false); pthrd_printf("User wants to write memory to remote addr 0x%lx from buffer 0x%p of size %lu\n", addr, buffer, (unsigned long) size); result_response::ptr resp = result_response::createResultResponse(); bool result = llproc_->writeMem(buffer, addr, size, resp); if (!result) { pthrd_printf("Error writing to memory\n"); (void)resp->isReady(); return false; } int_process::waitForAsyncEvent(resp); if (!resp->getResult() || resp->hasError()) { pthrd_printf("Error writing to memory async\n"); return false; } return true; } bool Process::readMemory(void *buffer, Dyninst::Address addr, size_t size) const { MTLock lock_this_func; PROC_EXIT_DETACH_TEST("readMemory", false); pthrd_printf("User wants to read memory from 0x%lx to 0x%p of size %lu\n", addr, buffer, (unsigned long) size); mem_response::ptr memresult = mem_response::createMemResponse((char *) buffer, size); bool result = llproc_->readMem(addr, memresult); if (!result) { pthrd_printf("Error reading from memory %lx on target process %d\n", addr, llproc_->getPid()); (void)memresult->isReady(); return false; } int_process::waitForAsyncEvent(memresult); if (memresult->hasError()) { pthrd_printf("Error reading from memory %lx on target process %d\n", addr, llproc_->getPid()); return false; } return true; } bool Process::writeMemoryAsync(Dyninst::Address addr, const void *buffer, size_t size, void *opaque_val) const { MTLock lock_this_func; PROC_EXIT_DETACH_TEST("writeMemoryAsync", false); pthrd_printf("User wants to async write memory to remote addr 0x%lx from buffer 0x%p of size %lu\n", addr, buffer, (unsigned long) size); result_response::ptr resp = result_response::createResultResponse(); int_eventAsyncIO *iev = new int_eventAsyncIO(resp, int_eventAsyncIO::memwrite); iev->local_memory = const_cast(buffer); iev->remote_addr = addr; iev->size = size; iev->opaque_value = opaque_val; resp->setAsyncIOEvent(iev); bool result = llproc_->writeMem(buffer, addr, size, resp); if (!result) { pthrd_printf("Error writing to memory\n"); (void)resp->isReady(); return false; } llproc_->plat_preAsyncWait(); return true; } bool Process::readMemoryAsync(void *buffer, Dyninst::Address addr, size_t size, void *opaque_val) const { MTLock lock_this_func; PROC_EXIT_DETACH_TEST("readMemoryAsync", false); pthrd_printf("User wants to async read memory from 0x%lx to 0x%p of size %lu\n", addr, buffer, (unsigned long) size); mem_response::ptr memresult = mem_response::createMemResponse((char *) buffer, size); int_eventAsyncIO *iev = new int_eventAsyncIO(memresult, int_eventAsyncIO::memread); iev->local_memory = buffer; iev->remote_addr = addr; iev->size = size; iev->opaque_value = opaque_val; memresult->setAsyncIOEvent(iev); bool result = llproc_->readMem(addr, memresult); if (!result) { pthrd_printf("Error reading from memory %lx on target process %d\n", addr, llproc_->getPid()); (void)memresult->isReady(); return false; } llproc_->plat_preAsyncWait(); return true; } bool Process::getMemoryAccessRights(Dyninst::Address addr, mem_perm& rights) { if (!llproc_) { perr_printf("getMemoryAccessRights on deleted process\n"); setLastError(err_exited, "Process is exited\n"); return false; } if (llproc_->getState() == int_process::detached) { perr_printf("getMemoryAccessRights on detached process\n"); setLastError(err_detached, "Process is detached\n"); return false; } pthrd_printf("User wants to get Memory Rights at %lx\n", addr); if (!llproc_->getMemoryAccessRights(addr, rights)) { pthrd_printf("Error get rights from memory %lx on target process %d\n", addr, llproc_->getPid()); return false; } return true; } bool Process::setMemoryAccessRights(Dyninst::Address addr, size_t size, mem_perm rights, mem_perm& oldrights) { MTLock lock_this_func; if (!llproc_) { perr_printf("setMemoryAccessRights on deleted process\n"); setLastError(err_exited, "Process is exited\n"); return false; } if (llproc_->getState() == int_process::detached) { perr_printf("setMemoryAccessRights on detached process\n"); setLastError(err_detached, "Process is detached\n"); return false; } pthrd_printf("User wants to set Memory Rights to %s from [%lx %lx]\n", rights.getPermName().c_str(), addr, addr+size); if (!llproc_->setMemoryAccessRights(addr, size, rights, oldrights)) { pthrd_printf("ERROR: set rights to %s from memory %lx on target process %d\n", rights.getPermName().c_str(), addr, llproc_->getPid()); return false; } return true; } bool Process::findAllocatedRegionAround(Dyninst::Address addr, MemoryRegion& memRegion) { if (!llproc_) { perr_printf("findAllocatedRegionAround on deleted process\n"); setLastError(err_exited, "Process is exited\n"); return false; } if (llproc_->getState() == int_process::detached) { perr_printf("findAllocatedRegionAround on detached process\n"); setLastError(err_detached, "Process is detached\n"); return false; } pthrd_printf("User wants to find Allocated Region contains %lx\n", addr); if (!llproc_->findAllocatedRegionAround(addr, memRegion)) { pthrd_printf("Error to find Allocated Region contains %lx on target process %d\n", addr, llproc_->getPid()); return false; } return true; } bool Process::addBreakpoint(Address addr, Breakpoint::ptr bp) const { MTLock lock_this_func; PROC_EXIT_DETACH_TEST("addBreakpoint", false); if (hasRunningThread()) { perr_printf("User attempted to add breakpoint to running process\n"); setLastError(err_notstopped, "Attempted to insert breakpoint into running process\n"); return false; } if( llproc_->getState() == int_process::detached ) { perr_printf("User attempted to add breakpoint to detached process\n"); setLastError(err_detached, "Attempted to insert breakpoint into detached process\n"); return false; } return llproc_->addBreakpoint(addr, bp->llbp()); } bool Process::rmBreakpoint(Dyninst::Address addr, Breakpoint::ptr bp) const { MTLock lock_this_func; PROC_EXIT_DETACH_TEST("rmBreakpoint", false); if (hasRunningThread()) { perr_printf("User attempted to remove breakpoint on running process\n"); setLastError(err_notstopped, "Attempted to remove breakpoint on running process\n"); return false; } set resps; bool result = llproc_->removeBreakpoint(addr, bp->llbp(), resps); if (!result) { pthrd_printf("Failed to removeBreakpoint\n"); return false; } int_process::waitForAsyncEvent(resps); for (set::iterator i = resps.begin(); i != resps.end(); i++) { response::ptr resp = *i; if (resp->hasError()) { pthrd_printf("Error removing breakpoint\n"); return false; } } return true; } unsigned Process::numHardwareBreakpointsAvail(unsigned mode) { MTLock lock_this_func; PROC_EXIT_DETACH_TEST("numHardwareBreakpointAvail", 0); unsigned min = INT_MAX; int_threadPool *tp = llproc_->threadPool(); for (int_threadPool::iterator i = tp->begin(); i != tp->end(); i++) { int_thread *thr = *i; unsigned cur = thr->hwBPAvail(mode); if (cur < min) min = cur; } return min; } void Process::setDefaultSymbolReader(SymbolReaderFactory *f) { int_process::user_set_symbol_reader = f; } SymbolReaderFactory *Process::getDefaultSymbolReader() { return int_process::user_set_symbol_reader; } void Process::setSymbolReader(SymbolReaderFactory *f) const { MTLock lock_this_func; if (!llproc_) { perr_printf("setSymbolReader on exited process\n"); setLastError(err_exited, "Process is exited\n"); return; } llproc_->setSymReader(f); } SymbolReaderFactory *Process::getSymbolReader() const { MTLock lock_this_func; PROC_EXIT_TEST("getSymbolReader", NULL); return llproc_->getSymReader(); } LibraryTracking *Process::getLibraryTracking() { MTLock lock_this_func; PROC_EXIT_TEST("getLibraryTracking", NULL); int_libraryTracking *proc = llproc_->getLibraryTracking(); if (!proc) return NULL; return proc->up_ptr; } ThreadTracking *Process::getThreadTracking() { MTLock lock_this_func; PROC_EXIT_TEST("getThreadTracking", NULL); int_threadTracking *proc = llproc_->getThreadTracking(); if (!proc) return NULL; return proc->up_ptr; } LWPTracking *Process::getLWPTracking() { MTLock lock_this_func; PROC_EXIT_TEST("getLWPTracking", NULL); int_LWPTracking *proc = llproc_->getLWPTracking(); if (!proc) return NULL; return proc->up_ptr; } CallStackUnwinding *Thread::getCallStackUnwinding() { MTLock lock_this_func; THREAD_EXIT_TEST("getCallStackUnwinding", NULL); int_callStackUnwinding *uwproc = llthread_->llproc()->getCallStackUnwinding(); if (!uwproc) return NULL; if (!llthread_->unwinder) { llthread_->unwinder = new CallStackUnwinding(shared_from_this()); } return llthread_->unwinder; } FollowFork *Process::getFollowFork() { MTLock lock_this_func; PROC_EXIT_TEST("getFollowFork", NULL); int_followFork *proc = llproc_->getFollowFork(); if (!proc) return NULL; return proc->up_ptr; } SignalMask *Process::getSignalMask() { MTLock lock_this_func; PROC_EXIT_TEST("getSignalMask", NULL); int_signalMask *proc = llproc_->getSignalMask(); if (!proc) return NULL; return proc->up_ptr; } RemoteIO *Process::getRemoteIO() { MTLock lock_this_func; PROC_EXIT_TEST("getRemoteIO", NULL); int_remoteIO *proc = llproc_->getRemoteIO(); if (!proc) return NULL; return proc->up_ptr; } MemoryUsage *Process::getMemoryUsage() { MTLock lock_this_func; PROC_EXIT_TEST("getMemoryUsage", NULL); int_memUsage *proc = llproc_->getMemUsage(); if (!proc) return NULL; return proc->up_ptr; } const LibraryTracking *Process::getLibraryTracking() const { MTLock lock_this_func; PROC_EXIT_TEST("getLibraryTracking", NULL); int_libraryTracking *proc = llproc_->getLibraryTracking(); if (!proc) return NULL; return proc->up_ptr; } const ThreadTracking *Process::getThreadTracking() const { MTLock lock_this_func; PROC_EXIT_TEST("getThreadTracking", NULL); int_threadTracking *proc = llproc_->getThreadTracking(); if (!proc) return NULL; return proc->up_ptr; } const LWPTracking *Process::getLWPTracking() const { MTLock lock_this_func; PROC_EXIT_TEST("getLWPTracking", NULL); int_LWPTracking *proc = llproc_->getLWPTracking(); if (!proc) return NULL; return proc->up_ptr; } const SignalMask *Process::getSignalMask() const { MTLock lock_this_func; PROC_EXIT_TEST("getSignalMask", NULL); int_signalMask *proc = llproc_->getSignalMask(); if (!proc) return NULL; return proc->up_ptr; } const RemoteIO *Process::getRemoteIO() const { MTLock lock_this_func; PROC_EXIT_TEST("getRemoteIO", NULL); int_remoteIO *proc = llproc_->getRemoteIO(); if (!proc) return NULL; return proc->up_ptr; } const MemoryUsage *Process::getMemoryUsage() const { MTLock lock_this_func; PROC_EXIT_TEST("getMemoryUsage", NULL); int_memUsage *proc = llproc_->getMemUsage(); if (!proc) return NULL; return proc->up_ptr; } err_t Process::getLastError() const { MTLock lock_this_func; if (!llproc_) { return exitstate_->last_error; } return llproc_->getLastError(); } const char *Process::getLastErrorMsg() const { MTLock lock_this_func; if (!llproc_) { return exitstate_->last_error_msg; } return llproc_->getLastErrorMsg(); } void Process::setLastError(err_t ec, const char *es) const { MTLock lock_this_func; if (!llproc_) { exitstate_->last_error = ec; exitstate_->last_error_msg = es; ProcControlAPI::globalSetLastError(ec, es); } else { llproc_->setLastError(ec, es); } } void Process::clearLastError() const { MTLock lock_this_func; if (!llproc_) { exitstate_->last_error = err_none; exitstate_->last_error_msg = "ok"; } else { llproc_->clearLastError(); } } ExecFileInfo* Process::getExecutableInfo() const { MTLock lock_this_func; PROC_EXIT_TEST("getExecutableInfo", NULL); return llproc()->plat_getExecutableInfo(); } unsigned int Process::getCapabilities() const { MTLock lock_this_func; PROC_EXIT_TEST("getCapabilities", 0); return llproc()->plat_getCapabilities(); } Thread::Thread() : llthread_(NULL), exitstate_(NULL) { } Thread::~Thread() { if (exitstate_) { delete exitstate_; exitstate_ = NULL; } } void *Thread::getData() const { MTLock lock_this_func; if (exitstate_) return exitstate_->user_data; return llthread_->user_data; } void Thread::setData(void *p) const { MTLock lock_this_func; if (exitstate_) { exitstate_->user_data = p; } else { llthread_->user_data = p; } } Process::const_ptr Thread::getProcess() const { MTLock lock_this_func; if (!llthread_) { assert(exitstate_); return exitstate_->proc_ptr; } return llthread_->proc(); } Process::ptr Thread::getProcess() { MTLock lock_this_func; if (!llthread_) { assert(exitstate_); return exitstate_->proc_ptr; } return llthread_->proc(); } int_thread *Thread::llthrd() const { return llthread_; } bool Thread::isStopped() const { MTLock lock_this_func; THREAD_EXIT_TEST("isStopped", false); return llthread_->getUserState().getState() == int_thread::stopped; } bool Thread::isRunning() const { MTLock lock_this_func; THREAD_EXIT_TEST("isRunning", false); return llthread_->getUserState().getState() == int_thread::running; } bool Thread::isLive() const { MTLock lock_this_func; if (!llthread_) { return false; } return (llthread_->getUserState().getState() == int_thread::stopped || llthread_->getUserState().getState() == int_thread::running); } bool Thread::isDetached() const { MTLock lock_this_func; THREAD_EXIT_TEST("isDetached", false); return llthread_->getUserState().getState() == int_thread::detached; } bool Thread::stopThread() { MTLock lock_this_func(MTLock::deliver_callbacks); THREAD_EXIT_DETACH_CB_TEST("stopThread", false); int_thread *thrd = llthrd(); int_process *proc = thrd->llproc(); pthrd_printf("User stopping thread %d/%d\n", proc->getPid(), thrd->getLWP()); bool result = thrd->getUserState().setState(int_thread::running); if (!result) { perr_printf("Thread %d/%d was not in a stoppable state, error return from setState\n", proc->getPid(), thrd->getLWP()); setLastError(err_internal, "Could not set user state while stopping thread\n"); return false; } proc->throwNopEvent(); bool proc_exited = false; result = int_process::waitAndHandleForProc(false, proc, proc_exited); if (proc_exited) { perr_printf("Process exited while waiting for user thread stop, erroring\n"); setLastError(err_exited, "Process exited while thread being stopped.\n"); return false; } if (!result) { perr_printf("Internal error calling waitAndHandleForProc on %d\n", proc->getPid()); setLastError(err_internal, "Error while calling waitAndHandleForProc from thread stop\n"); return false; } return true; } bool Thread::continueThread() { MTLock lock_this_func(MTLock::deliver_callbacks); THREAD_EXIT_DETACH_CB_TEST("continueThread", false); int_thread *thrd = llthrd(); int_process *proc = thrd->llproc(); pthrd_printf("User continuing thread %d/%d\n", proc->getPid(), thrd->getLWP()); bool result = thrd->getUserState().setState(int_thread::running); if (!result) { perr_printf("Thread %d/%d was not in a continuable state, error return from setState\n", proc->getPid(), thrd->getLWP()); setLastError(err_internal, "Could not set user state while continuing thread\n"); return false; } proc->throwNopEvent(); return true; } bool Thread::getAllRegisters(RegisterPool &pool) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("getAllRegisters", false); allreg_response::ptr response = allreg_response::createAllRegResponse(pool.llregpool); bool result = llthread_->getAllRegisters(response); if (!result) { pthrd_printf("Error getting all registers\n"); return false; } result = llthread_->llproc()->waitForAsyncEvent(response); if (!result) { pthrd_printf("Error waiting for async events\n"); return false; } assert(response->isReady()); if (response->hasError()) { pthrd_printf("Async error reading registers\n"); return false; } return true; } bool Thread::setAllRegisters(RegisterPool &pool) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("setAllRegisters", false); result_response::ptr response = result_response::createResultResponse(); bool result = llthread_->setAllRegisters(*pool.llregpool, response); if (!result) { pthrd_printf("Error setting all registers\n"); return false; } result = llthread_->llproc()->waitForAsyncEvent(response); if (!result) { pthrd_printf("Error waiting for async events\n"); return false; } assert(response->isReady()); if (response->hasError()) { pthrd_printf("Async error setting registers\n"); return false; } return true; } bool Thread::getRegister(Dyninst::MachRegister reg, Dyninst::MachRegisterVal &val) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("getRegister", false); reg_response::ptr response = reg_response::createRegResponse(); bool result = llthread_->getRegister(reg, response); if (!result) { pthrd_printf("Error getting register\n"); return false; } result = llthread_->llproc()->waitForAsyncEvent(response); if (!result) { pthrd_printf("Error waiting for async events\n"); return false; } assert(response->isReady()); if (response->hasError()) { pthrd_printf("Async error getting register\n"); return false; } val = response->getResult(); return true; } bool Thread::setRegister(Dyninst::MachRegister reg, Dyninst::MachRegisterVal val) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("setRegister", false); result_response::ptr response = result_response::createResultResponse(); bool result = llthread_->setRegister(reg, val, response); if (!result) { pthrd_printf("Error setting register value\n"); return false; } result = llthread_->llproc()->waitForAsyncEvent(response); if (!result) { pthrd_printf("Error waiting for async events\n"); return false; } assert(response->isReady()); if (response->hasError()) { pthrd_printf("Async error reading registers\n"); return false; } return true; } bool Thread::getAllRegistersAsync(RegisterPool &pool, void *opaque_val) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("getAllRegistersAsync", false); pthrd_printf("User wants to async read registers on %d/%d\n", llthread_->proc()->getPid(), llthread_->getLWP()); allreg_response::ptr response = allreg_response::createAllRegResponse(pool.llregpool); int_eventAsyncIO *iev = new int_eventAsyncIO(response, int_eventAsyncIO::regallread); iev->opaque_value = opaque_val; iev->rpool = &pool; response->setAsyncIOEvent(iev); bool result = llthread_->getAllRegisters(response); if (!result) { pthrd_printf("Error getting all registers async\n"); return false; } llthread_->llproc()->plat_preAsyncWait(); return true; } bool Thread::setAllRegistersAsync(RegisterPool &pool, void *opaque_val) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("getAllRegistersAsync", false); pthrd_printf("User wants to async set registers on %d/%d\n", llthread_->proc()->getPid(), llthread_->getLWP()); result_response::ptr response = result_response::createResultResponse(); int_eventAsyncIO *iev = new int_eventAsyncIO(response, int_eventAsyncIO::regallwrite); iev->opaque_value = opaque_val; response->setAsyncIOEvent(iev); bool result = llthread_->setAllRegisters(*pool.llregpool, response); if (!result) { pthrd_printf("Error setting all registers async\n"); return false; } llthread_->llproc()->plat_preAsyncWait(); return true; } bool Thread::readThreadLocalMemory(void *buffer, Library::const_ptr lib, Dyninst::Offset tls_symbol_offset, size_t size) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("readTLSMemory", false); TRUTH_TEST(buffer, "buffer", false); TRUTH_TEST(lib, "lib", false); int_process *llproc = llthread_->llproc(); int_thread *llthrd = llthread_; int_library *intlib = lib->debug(); if (!intlib || !intlib->inProcess(llproc)) { perr_printf("Library %s is not loaded in process %d\n", lib->getName().c_str(), llproc->getPid()); setLastError(err_badparam, "Library object is not loaded in specified process\n"); return false; } pthrd_printf("User wants to read TLS memory on thread %d/%d from library %s at offset %lu of size %lu\n", llproc->getPid(), llthrd->getLWP(), lib->getName().c_str(), (unsigned long) tls_symbol_offset, (unsigned long) size); Address var_address; async_ret_t ret; do { set resps; ret = llproc->plat_calcTLSAddress(llthrd, intlib, tls_symbol_offset, var_address, resps); if (ret == aret_error) { pthrd_printf("Failed calculate memory address of TLS variable"); return false; } if (ret == aret_async) { llproc->waitForAsyncEvent(resps); } } while (ret != aret_success); mem_response::ptr memresp = mem_response::createMemResponse((char *) buffer, size); bool result = llproc->readMem(var_address, memresp, llthrd); if (result) llproc->waitForAsyncEvent(memresp); if (!result || memresp->hasError()) { pthrd_printf("Failed to read TLS memory at address %lx of size %lu\n", var_address, size); (void)memresp->isReady(); return false; } return true; } bool Thread::writeThreadLocalMemory(Library::const_ptr lib, Dyninst::Offset tls_symbol_offset, const void *buffer, size_t size) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("writeTLSMemory", false); TRUTH_TEST(buffer, "buffer", false); TRUTH_TEST(lib, "lib", false); int_process *llproc = llthread_->llproc(); int_thread *llthrd = llthread_; int_library *intlib = lib->debug(); if (!intlib || !intlib->inProcess(llproc)) { perr_printf("Library %s is not loaded in process %d\n", lib->getName().c_str(), llproc->getPid()); setLastError(err_badparam, "Library object is not loaded in specified process\n"); return false; } pthrd_printf("User wants to write to TLS memory on thread %d/%d in library %s at offset %lu of size %lu\n", llproc->getPid(), llthrd->getLWP(), lib->getName().c_str(), (unsigned long) tls_symbol_offset, (unsigned long) size); Address var_address; async_ret_t ret; do { set resps; ret = llproc->plat_calcTLSAddress(llthrd, intlib, tls_symbol_offset, var_address, resps); if (ret == aret_error) { pthrd_printf("Failed calculate memory address of TLS variable"); return false; } if (ret == aret_async) { llproc->waitForAsyncEvent(resps); } } while (ret != aret_success); result_response::ptr resp = result_response::createResultResponse(); bool result = llproc->writeMem(buffer, var_address, size, resp); if (result) llproc->waitForAsyncEvent(resp); if (!result || !resp->getResult() || resp->hasError()) { pthrd_printf("Failed to write to TLS memory at address %lx of size %lu\n", var_address, size); return false; } return true; } bool Thread::getThreadLocalAddress(Library::const_ptr lib, Dyninst::Offset tls_symbol_offset, Dyninst::Address &result_addr) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("getThreadLocalAddress", false); TRUTH_TEST(lib, "lib", false); int_process *llproc = llthread_->llproc(); int_thread *llthrd = llthread_; int_library *intlib = lib->debug(); if (!intlib || !intlib->inProcess(llproc)) { perr_printf("Library %s is not loaded in process %d\n", lib->getName().c_str(), llproc->getPid()); setLastError(err_badparam, "Library object is not loaded in specified process\n"); return false; } pthrd_printf("User wants to get TLS address on thread %d/%d in library %s at offset %lu\n", llproc->getPid(), llthrd->getLWP(), lib->getName().c_str(), (unsigned long) tls_symbol_offset); Address var_address; async_ret_t ret; do { set resps; ret = llproc->plat_calcTLSAddress(llthrd, intlib, tls_symbol_offset, var_address, resps); if (ret == aret_error) { pthrd_printf("Failed calculate memory address of TLS variable"); return false; } if (ret == aret_async) { llproc->waitForAsyncEvent(resps); } } while (ret != aret_success); result_addr = var_address; return true; } bool Thread::isInitialThread() const { MTLock lock_this_func; THREAD_EXIT_TEST("isInitialThread", false); return llthread_->llproc()->threadPool()->initialThread() == llthread_; } bool Thread::isUser() const { MTLock lock_this_func; THREAD_EXIT_TEST("isUser", false); return llthread_->isUser(); } bool Thread::setSingleStepMode(bool s) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("setSingleStepMode", false); llthread_->setSingleStepUserMode(s); return true; } bool Thread::getSingleStepMode() const { MTLock lock_this_func; THREAD_EXIT_TEST("getSingleStepMode", false); return llthread_->singleStepUserMode(); } bool Thread::setSyscallMode(bool s) const { MTLock lock_this_func; THREAD_EXIT_DETACH_STOP_TEST("getSyscallMode", false); llthread_->setSyscallUserMode(s); return true; } bool Thread::getSyscallMode() const { MTLock lock_this_func; if (!llthread_) { perr_printf("getSyscallMode called on exited thread\n"); setLastError(err_exited, "Thread is exited\n"); return false; } return llthread_->syscallUserMode(); } Dyninst::LWP Thread::getLWP() const { MTLock lock_this_func; if (!llthread_) { assert(exitstate_); return exitstate_->lwp; } return llthread_->getLWP(); } bool Thread::postIRPC(IRPC::ptr irpc) const { MTLock lock_this_func; THREAD_EXIT_DETACH_TEST("postIRPC", false); int_thread *thr = llthread_; int_process *proc = thr->llproc(); int_iRPC::ptr rpc = irpc->llrpc()->rpc; bool result = rpcMgr()->postRPCToThread(thr, rpc); if (!result) { pthrd_printf("postRPCToThread failed on %d\n", proc->getPid()); return false; } proc->throwNopEvent(); return true; } bool Thread::getPostedIRPCs(std::vector &rpcs) const { MTLock lock_this_func; THREAD_EXIT_TEST("getPostedIRPCs", false); rpc_list_t *rpc_list = llthread_->getPostedRPCs(); for (rpc_list_t::iterator j = rpc_list->begin(); j != rpc_list->end(); ++j) { IRPC::ptr up_rpc = (*j)->getIRPC().lock(); if (up_rpc == IRPC::ptr()) continue; rpcs.push_back(up_rpc); } return true; } bool Thread::haveUserThreadInfo() const { MTLock lock_this_func; THREAD_EXIT_TEST("haveUserThreadInfo", false); return llthread_->haveUserThreadInfo(); } Dyninst::THR_ID Thread::getTID() const { MTLock lock_this_func; if (!llthread_) { if (exitstate_ && exitstate_->thr_id != NULL_THR_ID) { return exitstate_->thr_id; } perr_printf("getTID on deleted thread\n"); setLastError(err_exited, "Thread is exited"); return false; } Dyninst::THR_ID tid; bool result = llthread_->getTID(tid); if (!result) { return NULL_THR_ID; } llthread_->setTID(tid); return tid; } Dyninst::Address Thread::getStartFunction() const { MTLock lock_this_func; THREAD_EXIT_TEST("getStartFunction", 0); Dyninst::Address addr; bool result = llthread_->getStartFuncAddress(addr); if (!result) { return 0; } return addr; } Dyninst::Address Thread::getStackBase() const { MTLock lock_this_func; THREAD_EXIT_TEST("getStackBase", 0); Dyninst::Address addr; bool result = llthread_->getStackBase(addr); if (!result) { return 0; } return addr; } unsigned long Thread::getStackSize() const { MTLock lock_this_func; THREAD_EXIT_TEST("getStackSize", 0); unsigned long size; bool result = llthread_->getStackSize(size); if (!result) { return 0; } return size; } Dyninst::Address Thread::getTLS() const { MTLock lock_this_func; THREAD_EXIT_TEST("getTLS", 0); Dyninst::Address addr; bool result = llthread_->getTLSPtr(addr); if (!result) { return 0; } return addr; } Dyninst::Address Thread::getThreadInfoBlockAddr() const { MTLock lock_this_func; if (!llthread_) { perr_printf("getThreadInfoBlockAddr on deleted thread\n"); setLastError(err_exited, "Thread is exited"); return 0; } return llthread_->getThreadInfoBlockAddr(); } IRPC::const_ptr Thread::getRunningIRPC() const { MTLock lock_this_func; THREAD_EXIT_DETACH_TEST("getRunningIRPC", IRPC::const_ptr()); int_iRPC::ptr running = llthread_->runningRPC(); if (running == int_iRPC::ptr()) return IRPC::const_ptr(); IRPC::ptr irpc = running->getIRPC().lock(); return irpc; } void Thread::setLastError(err_t ec, const char *es) const { if (!llthread_) { assert(exitstate_); exitstate_->proc_ptr->setLastError(ec, es); } else { llthread_->setLastError(ec, es); } } ThreadPool::ThreadPool() : threadpool(NULL) { } ThreadPool::~ThreadPool() { } Thread::ptr ThreadPool::getInitialThread() { return threadpool->initialThread()->thread(); } Thread::const_ptr ThreadPool::getInitialThread() const { return threadpool->initialThread()->thread(); } ThreadPool::iterator::iterator() { curp = NULL; curi = uninitialized_val; curh = Thread::ptr(); } bool ThreadPool::iterator::operator==(const iterator &i) const { return (i.curh == curh); } bool ThreadPool::iterator::operator!=(const iterator &i) const { return (i.curh != curh); } Thread::ptr ThreadPool::iterator::operator*() const { MTLock lock_this_func; assert(curp); //assert(curi >= 0 && ((curi < (signed) curp->hl_threads.size()) || !curh->llthrd()) ); //Likely dereferenced bad thread iterator return curh; } ThreadPool::iterator ThreadPool::iterator::operator++() // prefix { MTLock lock_this_func; assert(curi >= 0); //If this fails, you incremented a bad iterator for (;;) { curi++; if (curi >= (signed int) curp->hl_threads.size()) { curh = Thread::ptr(); curi = end_val; return *this; } curh = curp->hl_threads[curi]; if (!curh->llthrd()) continue; if (curh->llthrd()->getUserState().getState() == int_thread::exited) continue; if (!curh->isUser()) continue; return *this; } } ThreadPool::iterator ThreadPool::iterator::operator++(int) // postfix { MTLock lock_this_func; ThreadPool::iterator orig = *this; assert(curi >= 0); //If this fails, you incremented a bad iterator for (;;) { curi++; if (curi >= (signed int) curp->hl_threads.size()) { curh = Thread::ptr(); curi = end_val; return orig; } curh = curp->hl_threads[curi]; if (!curh->llthrd()) continue; if (curh->llthrd()->getUserState().getState() == int_thread::exited) continue; if (!curh->isUser()) continue; return orig; } } ThreadPool::iterator ThreadPool::begin() { MTLock lock_this_func; ThreadPool::iterator i{}; i.curp = threadpool; i.curi = 0; if (!threadpool->hl_threads.empty()) i.curh = i.curp->hl_threads[i.curi]; else i.curh = Thread::ptr(); return i; } ThreadPool::iterator ThreadPool::end() { MTLock lock_this_func; ThreadPool::iterator i{}; i.curp = threadpool; i.curi = iterator::end_val; i.curh = Thread::ptr(); return i; } ThreadPool::iterator ThreadPool::find(Dyninst::LWP lwp) { MTLock lock_this_func; ThreadPool::iterator i{}; int_thread *thread = threadpool->findThreadByLWP(lwp); if( !thread ) return end(); i.curp = threadpool; i.curh = thread->thread(); i.curi = threadpool->hl_threads.size()-1; return i; } ThreadPool::const_iterator::const_iterator() { curp = NULL; curi = uninitialized_val; curh = Thread::ptr(); } bool ThreadPool::const_iterator::operator==(const const_iterator &i) const { return (i.curh == curh); } bool ThreadPool::const_iterator::operator!=(const const_iterator &i) const { return (i.curh != curh); } Thread::const_ptr ThreadPool::const_iterator::operator*() const { MTLock lock_this_func; assert(curp); assert(curi >= 0 && curi < (signed) curp->hl_threads.size()); return curh; } ThreadPool::const_iterator ThreadPool::const_iterator::operator++() // prefix { MTLock lock_this_func; assert(curi >= 0); //If this fails, you incremented a bad iterator for (;;) { curi++; if (curi >= (signed int) curp->hl_threads.size()) { curh = Thread::ptr(); curi = end_val; return *this; } curh = curp->hl_threads[curi]; if (!curh->llthrd()) continue; if (curh->llthrd()->getUserState().getState() == int_thread::exited) continue; if (!curh->isUser()) continue; return *this; } } ThreadPool::const_iterator ThreadPool::const_iterator::operator++(int) // postfix { MTLock lock_this_func; ThreadPool::const_iterator orig = *this; assert(curi >= 0); //If this fails, you incremented a bad iterator for (;;) { curi++; if (curi >= (signed int) curp->hl_threads.size()) { curh = Thread::ptr(); curi = end_val; return orig; } curh = curp->hl_threads[curi]; if (!curh->llthrd()) continue; if (curh->llthrd()->getUserState().getState() == int_thread::exited) continue; if (!curh->isUser()) continue; return orig; } } ThreadPool::const_iterator ThreadPool::begin() const { MTLock lock_this_func; ThreadPool::const_iterator i{}; i.curp = threadpool; i.curi = 0; if (!threadpool->hl_threads.empty()) { while(i.curi < (int)threadpool->hl_threads.size() && i.curp->hl_threads[i.curi] && !i.curp->hl_threads[i.curi]->isUser()) { i.curi++; } i.curh = i.curp->hl_threads[i.curi]; } else i.curh = Thread::ptr(); return i; } ThreadPool::const_iterator ThreadPool::end() const { MTLock lock_this_func; ThreadPool::const_iterator i{}; i.curp = threadpool; i.curi = const_iterator::end_val; i.curh = Thread::ptr(); return i; } ThreadPool::const_iterator ThreadPool::find(Dyninst::LWP lwp) const { MTLock lock_this_func; ThreadPool::const_iterator i{}; int_thread *thread = threadpool->findThreadByLWP(lwp); if( !thread ) return end(); i.curp = threadpool; i.curh = thread->thread(); i.curi = threadpool->hl_threads.size()-1; return i; } Process::const_ptr ThreadPool::getProcess() const { MTLock lock_this_func; return threadpool->proc()->proc(); } Process::ptr ThreadPool::getProcess() { MTLock lock_this_func; return threadpool->proc()->proc(); } size_t ThreadPool::size() const { MTLock lock_this_func; return threadpool->size(); } EventNotify::EventNotify() : llnotify(NULL) { } EventNotify::~EventNotify() { } int EventNotify::getFD() { return (int)(llnotify->getWaitable()); } void EventNotify::registerCB(notify_cb_t cb) { return llnotify->registerCB(cb); } void EventNotify::removeCB(notify_cb_t cb) { return llnotify->removeCB(cb); } EventNotify *Dyninst::ProcControlAPI::evNotify() { return ¬ify()->up_notify; } Breakpoint::Breakpoint() : llbreakpoint_(NULL) { } Breakpoint::~Breakpoint() { delete llbreakpoint_; llbreakpoint_ = NULL; } int_breakpoint *Breakpoint::llbp() const { return llbreakpoint_; } Breakpoint::ptr Breakpoint::newBreakpoint() { Breakpoint::ptr newbp = Breakpoint::ptr(new Breakpoint()); newbp->llbreakpoint_ = new int_breakpoint(newbp); return newbp; } Breakpoint::ptr Breakpoint::newTransferBreakpoint(Dyninst::Address to) { Breakpoint::ptr newbp = Breakpoint::ptr(new Breakpoint()); newbp->llbreakpoint_ = new int_breakpoint(to, newbp, false); return newbp; } Breakpoint::ptr Breakpoint::newTransferOffsetBreakpoint(signed long shift) { Breakpoint::ptr newbp = Breakpoint::ptr(new Breakpoint()); newbp->llbreakpoint_ = new int_breakpoint((Address) shift, newbp, true); return newbp; } Breakpoint::ptr Breakpoint::newHardwareBreakpoint(unsigned int mode, unsigned int size) { Breakpoint::ptr newbp = Breakpoint::ptr(new Breakpoint()); newbp->llbreakpoint_ = new int_breakpoint(mode, size, newbp); return newbp; } Breakpoint::ptr Breakpoint::newSynchronousBreakpoint() { Breakpoint::ptr newbp = Breakpoint::ptr(new Breakpoint()); newbp->llbreakpoint_ = new int_breakpoint(newbp); newbp->llbreakpoint_->makeSynchronous(); return newbp; } void *Breakpoint::getData() const { return llbreakpoint_->getData(); } void Breakpoint::setData(void *d) const { llbreakpoint_->setData(d); } bool Breakpoint::isCtrlTransfer() const { return llbreakpoint_->isCtrlTransfer(); } Dyninst::Address Breakpoint::getToAddress() const { return llbreakpoint_->toAddr(); } void Breakpoint::setSuppressCallbacks(bool b) { return llbreakpoint_->setSuppressCallbacks(b); } bool Breakpoint::suppressCallbacks() const { return llbreakpoint_->suppressCallbacks(); } bool Breakpoint::isSynchronous() const { return llbreakpoint_->isSynchronous(); } // Note: These locks are intentionally indirect and leaked! // This is because we can't guarantee destructor order between compilation // units, and a static array of locks here in process.C may be destroyed before // int_cleanup in generator.C. Thus the handler thread may still be running, // manipulating Counters, which throws an exception when it tries to acquire // the destroyed lock. Mutex * Counter::locks = new Mutex[Counter::NumCounterTypes]; int Counter::global_counts[Counter::NumCounterTypes]; Counter::Counter(CounterType ct_) : local_count(0), ct(ct_) { assert((int) ct < NumCounterTypes); } Counter::~Counter() { if (!local_count) return; adjust(-1 * local_count); } void Counter::adjust(int val) { int index = (int) ct; int orig, after; locks[index].lock(); orig = global_counts[index]; global_counts[index] += val; after = global_counts[index]; assert(global_counts[index] >= 0); locks[index].unlock(); pthrd_printf("Adjusting counter %s by %d, before %d, after %d\n", getNameForCounter(index), val, orig, after); local_count += val; } void Counter::inc() { adjust(1); } void Counter::dec() { adjust(-1); } bool Counter::local() const { return localCount() != 0; } int Counter::localCount() const { return local_count; } bool Counter::global(CounterType ct) { return globalCount(ct) != 0; } int Counter::processCount(CounterType ct, int_process* p) { int sum = 0; switch(ct) { case AsyncEvents: sum += p->asyncEventCount().localCount(); return sum; break; case ForceGeneratorBlock: sum += p->getForceGeneratorBlockCount().localCount(); return sum; break; case StartupTeardownProcesses: sum += p->getStartupTeardownProcs().localCount(); return sum; break; default: break; } for(int_threadPool::iterator i = p->threadPool()->begin(); i != p->threadPool()->end(); ++i) { switch(ct) { case HandlerRunningThreads: sum += (*i)->handlerRunningThreadsCount().localCount(); break; case GeneratorRunningThreads: sum += (*i)->generatorRunningThreadsCount().localCount(); break; case SyncRPCs: sum += (*i)->syncRPCCount().localCount(); break; case SyncRPCRunningThreads: sum += (*i)->runningSyncRPCThreadCount().localCount(); break; case PendingStops: sum += (*i)->pendingStopsCount().localCount(); break; case ClearingBPs: sum += (*i)->clearingBPCount().localCount(); break; case ProcStopRPCs: sum += (*i)->procStopRPCCount().localCount(); break; case GeneratorNonExitedThreads: sum += (*i)->getGeneratorNonExitedThreadCount().localCount(); break; case NeonatalThreads: sum += (*i)->neonatalThreadCount().localCount(); break; case PendingStackwalks: sum += (*i)->pendingStackwalkCount().localCount(); default: break; } } return sum; } int Counter::globalCount(CounterType ct) { //Currently no lock here. We're just reading, and // I'm assuming the updates from 'adjust' will be // just modifying a single int, and thus not ever // leave this in an invalid state. If the count // ever moved to a long, then we may have to lock. return global_counts[(int) ct]; } #if !defined(STR_CASE) #define STR_CASE(X) case X: return #X #endif const char *Counter::getNameForCounter(int counter_type) { switch (counter_type) { STR_CASE(HandlerRunningThreads); STR_CASE(GeneratorRunningThreads); STR_CASE(SyncRPCs); STR_CASE(SyncRPCRunningThreads); STR_CASE(PendingStops); STR_CASE(ClearingBPs); STR_CASE(ProcStopRPCs); STR_CASE(AsyncEvents); STR_CASE(ForceGeneratorBlock); STR_CASE(GeneratorNonExitedThreads); STR_CASE(StartupTeardownProcesses); STR_CASE(NeonatalThreads); STR_CASE(PendingStackwalks); default: assert(0); } return NULL; } MTManager::MTManager() : have_queued_events(false), is_running(false), should_exit(false), threadMode(Process::NoThreads) { } MTManager::~MTManager() { if (is_running) stop(); } MTManager *MTManager::mt_ = NULL; bool MTManager::handlerThreading() { return (threadMode == Process::HandlerThreading || threadMode == Process::CallbackThreading); } Process::thread_mode_t MTManager::getThreadMode() { return threadMode; } bool MTManager::setThreadMode(Process::thread_mode_t tm, bool init) { if (ProcPool()->numProcs()) { perr_printf("Attemted to setThreadMode with running processes\n"); ProcControlAPI::globalSetLastError(err_noproc, "Can't setThreadMode while processes are running\n"); return false; } if (!init && tm == threadMode) { pthrd_printf("Not change in thread mode, leaving as is\n"); return true; } switch (tm) { case Process::NoThreads: pthrd_printf("Setting thread mode to NoThreads\n"); break; case Process::GeneratorThreading: pthrd_printf("Setting thread mode to GeneratorThreading\n"); break; case Process::HandlerThreading: pthrd_printf("Setting thread mode to HandlerThreading\n"); break; case Process::CallbackThreading: pthrd_printf("Setting thread mode to CallbackThreading\n"); break; default: perr_printf("Bad value %d passed to setThreadMode\n", (int) tm); ProcControlAPI::globalSetLastError(err_badparam, "Invalid parameter to setThreadMode\n"); return false; } if ((tm == Process::NoThreads || tm == Process::GeneratorThreading) && (!init) && (threadMode == Process::HandlerThreading || threadMode == Process::CallbackThreading)) { pthrd_printf("New state does not use handler threading, stopping handler thread\n"); stop(); } if ((tm == Process::HandlerThreading || tm == Process::CallbackThreading) && (init || (threadMode == Process::NoThreads || threadMode == Process::GeneratorThreading))) { pthrd_printf("New state uses handler threading, running handler thread\n"); run(); } if (tm == Process::GeneratorThreading) { pthrd_printf("Moving to generator threading, registering notification cb\n"); Generator::registerNewEventCB(notifyNewEvent); } if (!init && threadMode == Process::GeneratorThreading) { pthrd_printf("Moving away from generator threading, removing notification cb\n"); Generator::removeNewEventCB(notifyNewEvent); } threadMode = tm; return true; } void MTManager::run() { if (is_running) return; is_running = true; should_exit = false; evhandler_thread.spawn(MTManager::evhandler_main_wrapper, NULL); } void MTManager::stop() { if( !is_running ) return; if (isHandlerThread()) { should_exit = true; return; } pending_event_lock.lock(); should_exit = true; pending_event_lock.signal(); pending_event_lock.unlock(); evhandler_thread.join(); is_running = false; setHandlerThread(-1); } void MTManager::evhandler_main() { have_queued_events = false; for (;;) { if (should_exit) return; startWork(); pthrd_printf("Handler starting to check for events\n"); if (mbox()->size() && !notify()->hasEvents()) { bool result = int_process::waitAndHandleEvents(false); if (!result) { pthrd_printf("Error handling events in handler thread\n"); } } else pthrd_printf("Events already handled, going back to sleep\n"); endWork(); pending_event_lock.lock(); if (should_exit) { pending_event_lock.unlock(); return; } if (!have_queued_events) { pending_event_lock.wait(); } have_queued_events = false; pending_event_lock.unlock(); } } #if defined(os_windows) unsigned long MTManager::evhandler_main_wrapper(void *) #else void MTManager::evhandler_main_wrapper(void *) #endif { setHandlerThread(DThread::self()); mt()->evhandler_main(); #if defined(os_windows) return 0; #else return; #endif } void MTManager::eventqueue_cb_wrapper() { mt()->eventqueue_cb(); } void MTManager::eventqueue_cb() { pending_event_lock.lock(); have_queued_events = true; pending_event_lock.signal(); pending_event_lock.unlock(); } void MTManager::startWork() { work_lock.lock(); } void MTManager::endWork() { assert(!isGeneratorThread()); work_lock.unlock(); } ProcStopEventManager::ProcStopEventManager(int_process *p) : proc(p) { } ProcStopEventManager::~ProcStopEventManager() { } bool ProcStopEventManager::prepEvent(Event::ptr ev) { if (!ev->procStopper()) { //Most events are not procStoppers and should hit here return true; } pthrd_printf("Adding event %s on %d/%d to pending proc stopper list\n", ev->name().c_str(), ev->getProcess()->llproc()->getPid(), ev->getThread()->llthrd()->getLWP()); pair::iterator, bool> result = held_pstop_events.insert(ev); assert(result.second); return false; } void ProcStopEventManager::checkEvents() { for (set::iterator i = held_pstop_events.begin(); i != held_pstop_events.end();) { Event::ptr ev = *i; if (ev->procStopper()) { i++; continue; } pthrd_printf("ProcStop event %s on %d/%d is ready, adding to queue\n", ev->name().c_str(), ev->getProcess()->llproc()->getPid(), ev->getThread()->llthrd()->getLWP()); i = held_pstop_events.erase(i); mbox()->enqueue(ev); } } bool ProcStopEventManager::processStoppedTo(int state_id) { return proc->threadPool()->allStopped(state_id); } bool ProcStopEventManager::threadStoppedTo(int_thread *thr, int state_id) { return thr->isStopped(state_id); } emulated_singlestep::emulated_singlestep(int_thread *thr_) : thr(thr_) { bp = new int_breakpoint(Breakpoint::ptr()); bp->setOneTimeBreakpoint(true); bp->setThreadSpecific(thr->thread()); saved_user_single_step = thr->singleStepUserMode(); saved_single_step = thr->singleStepMode(); thr->setSingleStepMode(false); thr->setSingleStepUserMode(false); thr->addEmulatedSingleStep(this); } emulated_singlestep::~emulated_singlestep() { delete bp; bp = NULL; } bool emulated_singlestep::containsBreakpoint(Address addr) const { return addrs.find(addr) != addrs.end(); } async_ret_t emulated_singlestep::add(Address addr) { if (addrs.find(addr) != addrs.end()) return aret_success; int_process *proc = thr->llproc(); proc->addBreakpoint(addr, bp); addrs.insert(addr); return aret_success; } async_ret_t emulated_singlestep::clear() { int_process *proc = thr->llproc(); if (clear_resps.empty()) { for (set
::iterator i = addrs.begin(); i != addrs.end(); i++) { result_response::ptr resp = result_response::createResultResponse(); proc->removeBreakpoint(*i, bp, clear_resps); } } for (set::iterator i = clear_resps.begin(); i != clear_resps.end();) { response::ptr resp = *i; if (resp->isReady()) { i = clear_resps.erase(i); continue; } i++; } return clear_resps.empty() ? aret_success : aret_async; } void emulated_singlestep::restoreSSMode() { thr->setSingleStepMode(saved_single_step); thr->setSingleStepUserMode(saved_user_single_step); } void int_thread::terminate() { plat_terminate(); } void int_thread::plat_terminate() { assert(0 && "Unimplemented!"); } /* * non-thread specific solution to arm kernel bug. * not used. syscall_exit_breakpoints::syscall_exit_breakpoints(){ enabled = false; bp = NULL; } bool syscall_exit_breakpoints::contains_breakpoints(Address addr) const{ return addrs.find(addr) != addrs.end(); } bool syscall_exit_breakpoints::is_enabled() const{ return enabled; } */