Files
TheRock/rocm-systems/projects/rocprofiler-systems/external/dyninst/proccontrol/src/process.C
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

8797 lines
243 KiB
C++

/*
* 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 <algorithm>
#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 <climits>
#include <cstring>
#include <cassert>
#include <map>
#include <sstream>
#include <iostream>
#include <iterator>
#include <errno.h>
using namespace Dyninst;
using namespace ProcControlAPI;
using namespace std;
const map<int,int> Process::emptyFDs;
const vector<string> Process::emptyEnvp;
Process::thread_mode_t threadingMode = Process::GeneratorThreading;
bool int_process::in_callback = false;
std::set<int_thread::continue_cb_t> 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<int_process *> 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<int_process *>::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<int_process *>::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<int_process *>::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<response::ptr> async_responses;
bool ret_async = false;
for (set<int_process *>::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<Dyninst::LWP> lwps;
bool result = getThreadLWPs(lwps);
if (!result) {
pthrd_printf("Failed to get thread LWPs for %d\n", pid);
return false;
}
for (vector<Dyninst::LWP>::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<int_process *> procs;
vector<Event::ptr> observedEvents;
set<response::ptr> 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<pair<int_process *, Dyninst::LWP>, bool> runningStates;
for (set<int_process *>::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<Dyninst::LWP, bool> 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<Dyninst::LWP, bool>::iterator j = temp_runningStates.begin(); j != temp_runningStates.end(); ++j) {
if (j->second) {
allStopped = false;
}
runningStates[pair<int_process *, LWP>(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<int_process *>::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<int_process *>::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<int_process *>::iterator i = procs.begin(); i != procs.end(); i++) {
int_process *proc = *i;
proc->setState(neonatal_intermediate);
}
}
for (set<int_process *>::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<int_process *, LWP> key(proc, thr->getLWP());
map<pair<int_process *, LWP>, 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<int_process *>::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<int_process *>::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<int_process *, LWP> key(proc, thr->getLWP());
map<pair<int_process *, LWP>, 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<int_process *> pa_procs = procs;
while (!pa_procs.empty()) {
async_responses.clear();
bool ret_async = false;
for (set<int_process *>::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<int_process *>::iterator i = procs.begin(); i != procs.end(); i++) {
// Resume all breakpoints
int_process *proc = *i;
for(std::map<Dyninst::Address, sw_breakpoint *>::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<Event::ptr>::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<response::ptr> &async_responses)
{
std::set<int_library*> 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<response::ptr> &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<response::ptr> &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<Dyninst::LWP> &)
{
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<response::ptr> resp)
{
for (set<response::ptr>::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<response::ptr>::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<int_libraryTracking *>(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<int_LWPTracking *>(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<int_threadTracking *>(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<int_followFork *>(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<int_callStackUnwinding *>(this);
return pCallStackUnwinding;
}
int_multiToolControl *int_process::getMultiToolControl()
{
if (MultiToolControl_set)
return pMultiToolControl;
MultiToolControl_set = true;
pMultiToolControl = dynamic_cast<int_multiToolControl *>(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<int_memUsage *>(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<int_signalMask *>(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<int_remoteIO *>(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<std::string> a,
std::vector<std::string> envp,
std::map<int,int> 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<Dyninst::Address, unsigned>::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<Dyninst::Address, unsigned>::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<pair<int_process *, int_iRPC::ptr> > 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<pair<int_process *, int_iRPC::ptr> >::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<pair<Process::ptr, int_iRPC::ptr> > 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<Dyninst::Address, unsigned long>::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<pair<Process::ptr, int_iRPC::ptr> >::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<Dyninst::Address, unsigned long>::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<Address, sw_breakpoint *>::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<Dyninst::Address, sw_breakpoint *>::iterator iter = mem->breakpoints.begin();
while (iter != mem->breakpoints.end()) {
std::set<response::ptr> 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<response::ptr> &resps)
{
pthrd_printf("Removing breakpoint at %lx in %d\n", addr, getPid());
set<bp_instance *> bps_to_remove;
map<Address, sw_breakpoint *>::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<bp_instance *>(swbp));
}
for (auto thr : *threadPool()) {
hw_breakpoint *hwbp = thr->getHWBreakpoint(addr);
if (hwbp && hwbp->containsIntBreakpoint(bp)) {
bps_to_remove.insert(static_cast<bp_instance *>(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<Dyninst::Address, sw_breakpoint *>::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<int_library*>::iterator substring_match = mem->libs.end();
for (set<int_library *>::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<response::ptr> &)
{
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<Address> &) {
return aret_success;
}
void int_process::plat_getEmulatedSingleStepAsyncs(int_thread *, std::set<response::ptr>) {
assert(0);
}
bool int_process::plat_needsPCSaveBeforeSingleStep()
{
return false;
}
map<int, int> &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<std::string> a,
std::vector<std::string> envp, std::map<int,int> 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<std::string> a,
std::vector<std::string> envp,
std::map<int,int> 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<std::string> a,
std::vector<std::string> envp,
std::map<int,int> 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<hybrid_lwp_control_process *>(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<int_thread *> 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<int_thread *>::iterator i = thrds.begin(); i != thrds.end(); i++) {
int_thread *thr = *i;
vector<Address> 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<response::ptr> 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<Address>::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; i<state_id; i++) {
if (getStateByID(i).getState() != dontcare)
return false;
}
return true;
}
void int_thread::setPendingStop(bool b)
{
pthrd_printf("Setting pending stop to %s, thread %d/%d\n", b ? "true" : "false", proc()->getPid(), 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<continue_cb_t>::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; i<int_thread::NumTargetStateIDs; i++) {
if (all_states[i]->getState() != 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<Dyninst::LWP, int_thread *>::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<bp_instance *>(swbp);
hw_breakpoint *hwbp = getHWBreakpoint(stopped_on_breakpoint_addr);
return static_cast<bp_instance *>(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<response::ptr> &,
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<response::ptr> &,
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<hw_breakpoint *>::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<Dyninst::LWP, int_thread *>::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<Dyninst::LWP, int_thread *>::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<Dyninst::LWP, int_thread *>::iterator i = thrds_by_lwp.find(lwp);
assert (i != thrds_by_lwp.end());
thrds_by_lwp.erase(i);
for (unsigned j=0; j<threads.size(); j++) {
if (threads[j] != thrd)
continue;
threads[j] = threads[threads.size()-1];
threads.pop_back();
hl_threads[j] = hl_threads[hl_threads.size()-1];
hl_threads.pop_back();
}
if(!threads.empty() && thrd == initial_thread)
{
initial_thread = threads[0];
}
}
void int_threadPool::noteUpdatedLWP(int_thread *thrd)
{
std::map<Dyninst::LWP, int_thread *>::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<int_thread*>::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<int_breakpoint *>::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<response::ptr> &resps)
{
empty = false;
set<int_breakpoint *>::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<Breakpoint::ptr>::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; i<bp_size+BP_LONG_SIZE; i++) {
bp_insn[i] = buffer[i];
}
}
return proc->writeMem(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<response::ptr> &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<Dyninst::Address, sw_breakpoint *>::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<response::ptr> &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<response::ptr> &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<response::ptr> &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<int_thread *> thrds;
bool proc_wide = bp->thread_specific.empty();
if (!proc_wide) {
for (set<Thread::const_ptr>::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<int_thread *>::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_breakpoint *> hw_bps;
hw_breakpoint *first_bp = NULL;
for (set<int_thread *>::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<response::ptr> resps;
for (set<hw_breakpoint *>::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<response::ptr> &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<response::ptr> &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<response::ptr> &resps)
{
bool done = false;
set<hw_breakpoint *>::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<int_library *>::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<Dyninst::Address, sw_breakpoint *>::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<int_library *>::iterator i;
for (i = libs.begin(); i != libs.end(); i++) {
(*i)->markAsCleanable();
}
libs.clear();
map<Dyninst::Address, sw_breakpoint *>::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<int_process *>::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<int_library*>::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<EventNotify::notify_cb_t>::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<EventNotify::notify_cb_t>::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<Dyninst::MachRegister, Dyninst::MachRegisterVal> 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<Dyninst::MachRegister, Dyninst::MachRegisterVal> 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<std::string> &argv,
const std::vector<std::string> &envp,
const std::map<int,int> &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() <<endl;
//auto thread = *(proc->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<IRPC::ptr> &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<void *>(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<response::ptr> 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<response::ptr>::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<response::ptr> 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<response::ptr> 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<response::ptr> 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<IRPC::ptr> &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 &notify()->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<false> * Counter::locks = new Mutex<false>[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<set<Event::ptr>::iterator, bool> result = held_pstop_events.insert(ev);
assert(result.second);
return false;
}
void ProcStopEventManager::checkEvents()
{
for (set<Event::ptr>::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<Address>::iterator i = addrs.begin(); i != addrs.end(); i++) {
result_response::ptr resp = result_response::createResultResponse();
proc->removeBreakpoint(*i, bp, clear_resps);
}
}
for (set<response::ptr>::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;
}
*/