Files
TheRock/rocm-systems/projects/rocprofiler-systems/external/dyninst/proccontrol/src/handler.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

2674 lines
83 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 "Handler.h"
#include "PCErrors.h"
#include "PCProcess.h"
#include "Mailbox.h"
#include "int_process.h"
#include "int_handler.h"
#include "procpool.h"
#include "irpc.h"
#include "response.h"
#include "int_event.h"
#include "processplat.h"
#include "registers/MachRegister.h"
#if defined(os_windows)
#include "windows_process.h"
#endif
#include <iostream>
using namespace Dyninst;
using namespace std;
#include <assert.h>
#include <cstring>
Handler::Handler(std::string name_) :
name(name_)
{
}
Handler::~Handler()
{
}
int Handler::getPriority() const
{
return DefaultPriority;
}
Event::ptr Handler::convertEventForCB(Event::ptr /*orig*/)
{
return Event::ptr();
}
std::string Handler::getName() const
{
return name;
}
HandlerPool::HandlerPool(int_process *p) :
proc(p),
nop_cur_event(false)
{
}
HandlerPool::~HandlerPool()
{
for (HandlerMap_t::iterator i = handlers.begin(); i != handlers.end(); i++) {
delete (*i).second;
}
//Do not delete actual Handler* objects.
handlers.clear();
}
static bool print_add_handler = true;
void HandlerPool::addHandlerInt(EventType etype, Handler *handler)
{
if (print_add_handler)
pthrd_printf("Handler %s will handle event %s\n", handler->getName().c_str(),
etype.name().c_str());
assert(etype.time() != EventType::Any);
HandlerMap_t::iterator i = handlers.find(etype);
HandlerSet_t *theset = NULL;
if (i == handlers.end()) {
theset = new HandlerSet_t();
handlers[etype] = theset;
}
else {
theset = (*i).second;
}
theset->insert(handler);
HandleCallbacks *cb = HandleCallbacks::getCB();
if (cb != handler) {
cb->alleventtypes.insert(etype);
if (!(etype.code() >= EventType::InternalEvents && etype.code() < EventType::MaxProcCtrlEvent))
addHandlerInt(etype, cb);
}
}
void HandlerPool::addHandler(Handler *handler)
{
std::vector<EventType> etypes;
handler->getEventTypesHandled(etypes);
for (std::vector<EventType>::iterator i = etypes.begin(); i != etypes.end(); i++)
{
if ((*i).time() == EventType::Any) {
addHandlerInt(EventType(EventType::Pre, (*i).code()), handler);
addHandlerInt(EventType(EventType::Post, (*i).code()), handler);
addHandlerInt(EventType(EventType::None, (*i).code()), handler);
}
else {
addHandlerInt(*i, handler);
}
}
}
struct eh_cmp_func
{
bool operator()(const pair<Event::ptr, Handler*> &a,
const pair<Event::ptr, Handler*> &b) const
{
//Async events go first
if (a.first->getEventType().code() != EventType::Async ||
b.first->getEventType().code() != EventType::Async)
{
if (a.first->getEventType().code() == EventType::Async)
return true;
if (b.first->getEventType().code() == EventType::Async)
return false;
}
//Others are run via handler priority
if (a.second->getPriority() != b.second->getPriority())
return a.second->getPriority() < b.second->getPriority();
//Hard-coded rule. UserThreadDestroy always comes before LWPDestroy
if (a.first->getEventType().code() == EventType::LWPDestroy &&
b.first->getEventType().code() == EventType::UserThreadDestroy)
return false;
if (b.first->getEventType().code() == EventType::LWPDestroy &&
a.first->getEventType().code() == EventType::UserThreadDestroy)
return true;
//Subservient events run latter in handler tie
if (a.first->subservientTo().lock() == b.first)
return false;
if (b.first->subservientTo().lock() == a.first)
return true;
//Events are equal in order--just choose a consistent ordering at this point
eventtype_cmp cmp;
if (cmp(a.first->getEventType(), b.first->getEventType()))
return true;
if (cmp(b.first->getEventType(), a.first->getEventType()))
return false;
return a.first < b.first;
}
};
Event::ptr HandlerPool::curEvent()
{
if (!cur_event && nop_cur_event) {
//Lazily create a NOP event as the current event.
EventNop::ptr nop_event = EventNop::ptr(new EventNop());
nop_event->setProcess(proc->proc());
nop_event->setThread(proc->threadPool()->initialThread()->thread());
nop_event->setSyncType(Event::async);
cur_event = nop_event;
}
return cur_event;
}
void HandlerPool::setNopAsCurEvent()
{
assert(!cur_event);
nop_cur_event = true;
}
void HandlerPool::clearNopAsCurEvent()
{
nop_cur_event = false;
cur_event = Event::ptr();
}
void HandlerPool::addLateEvent(Event::ptr ev)
{
late_events.insert(ev);
}
bool HandlerPool::hasLateEvents() const
{
return !late_events.empty();
}
void HandlerPool::collectLateEvents(Event::ptr parent_ev)
{
for (set<Event::ptr>::iterator i = late_events.begin(); i != late_events.end(); i++) {
parent_ev->addSubservientEvent(*i);
}
late_events.clear();
}
Event::ptr HandlerPool::getRealParent(Event::ptr ev) const
{
Event::ptr master_ev = ev;
for (;;) {
Event::ptr parent = master_ev->subservientTo().lock();
if (!parent || parent->getEventType().code() == EventType::Async)
break;
master_ev = parent;
}
return master_ev;
}
void HandlerPool::notifyOfPendingAsyncs(const std::set<response::ptr> &asyncs, Event::ptr ev)
{
Event::ptr master_ev = getRealParent(ev);
for (set<response::ptr>::const_iterator i = asyncs.begin(); i != asyncs.end(); i++) {
(*i)->setEvent(master_ev);
}
}
void HandlerPool::notifyOfPendingAsyncs(response::ptr async, Event::ptr ev)
{
async->setEvent(getRealParent(ev));
}
bool HandlerPool::isEventAsyncPostponed(Event::ptr ev) const
{
return (pending_async_events.find(ev) != pending_async_events.end());
}
bool HandlerPool::hasAsyncEvent() const
{
return !pending_async_events.empty();
}
void HandlerPool::markEventAsyncPending(Event::ptr ev)
{
set<Event::ptr>::iterator i = pending_async_events.find(ev);
if (i != pending_async_events.end()) {
pthrd_printf("Async event %s on %d/%d has already been marked, leaving alone.\n",
ev->name().c_str(), ev->getProcess()->llproc()->getPid(),
ev->getThread()->llthrd()->getLWP());
return;
}
bool was_empty = insertAsyncPendingEvent(ev);
pthrd_printf("Event %s async marked. pending_async_size = %d, (was_empty = %s)\n",
ev->name().c_str(),
(int) pending_async_events.size(),
was_empty ? "true" : "false");
for (set<Event::ptr>::iterator j = pending_async_events.begin(); j != pending_async_events.end(); j++) {
pthrd_printf("\tEvent %s (%p)\n", (*j)->name().c_str(), (void*)(*j).get());
}
if (was_empty) {
markProcAsyncPending(this);
}
}
void HandlerPool::clearEventAsync(Event::ptr ev)
{
bool result = removeAsyncPendingEvent(ev);
if (!result)
return;
pthrd_printf("Erasing event %s (%p) from list\n", ev->name().c_str(), (void*)ev.get());
for (set<Event::ptr>::iterator j = pending_async_events.begin(); j != pending_async_events.end(); j++) {
pthrd_printf("\tEvent %s (%p)\n", (*j)->name().c_str(), (void*)(*j).get());
}
if (pending_async_events.empty()) {
clearProcAsyncPending(this);
}
}
bool HandlerPool::insertAsyncPendingEvent(Event::ptr ev)
{
bool was_empty = pending_async_events.empty();
pair<set<Event::ptr>::iterator, bool> ret = pending_async_events.insert(ev);
if (ret.second) {
//This is a new async event.
proc->asyncEventCount().inc();
/**
* Desync the async state for the thread. This keeps the process or thread
* stopped while we handle the async event.
**/
if (ev->getSyncType() == Event::sync_thread) {
int_thread *thr = ev->getThread()->llthrd();
pthrd_printf("Desync'ing async thread state of %d/%d\n", proc->getPid(), thr->getLWP());
thr->getAsyncState().desyncState(int_thread::ditto);
}
else {
pthrd_printf("Desync'ing async process state of %d\n", proc->getPid());
proc->threadPool()->initialThread()->getAsyncState().desyncStateProc(int_thread::ditto);
}
}
return was_empty;
}
bool HandlerPool::removeAsyncPendingEvent(Event::ptr ev)
{
set<Event::ptr>::iterator i = pending_async_events.find(ev);
if (i == pending_async_events.end()) {
return false;
}
pending_async_events.erase(ev);
proc->asyncEventCount().dec();
/**
* Undo the previous desync. After the last restore operation, then thread/process
* should be able to run again.
**/
if (ev->getSyncType() == Event::sync_thread) {
int_thread *thr = ev->getThread()->llthrd();
pthrd_printf("Restoring'ing async thread state of %d/%d\n", proc->getPid(), thr->getLWP());
thr->getAsyncState().restoreState();
}
else {
pthrd_printf("Restoring'ing async process state of %d\n", proc->getPid());
proc->threadPool()->initialThread()->getAsyncState().restoreStateProc();
}
return true;
}
void HandlerPool::addEventToSet(Event::ptr ev, set<Event::ptr> &ev_set) const
{
ev_set.insert(ev);
for (vector<Event::ptr>::iterator i = ev->subservient_events.begin();
i != ev->subservient_events.end(); i++)
{
addEventToSet(*i, ev_set);
}
}
bool HandlerPool::handleEvent(Event::ptr orig_ev)
{
Event::ptr cb_replacement_ev = Event::ptr();
/**
* An event and its subservient events are a set of events that
* are processed at the same time. As an example, on SysV systems
* a Library event is triggered by a breakpoint. We want to do both the
* breakpoint handling and the library handling at the same time,
* but don't want to seperate the handling of these events. In this
* example the Library event is subservient to the Breakpoint event.
*
* We'll take the event and all its subservient set and run handlers
* for all of them.
**/
set<Event::ptr> all_events;
addEventToSet(orig_ev, all_events);
typedef set<pair<Event::ptr, Handler *>, eh_cmp_func > ev_hndler_set_t;
ev_hndler_set_t events_and_handlers;
for (set<Event::ptr>::iterator i = all_events.begin(); i != all_events.end(); i++)
{
Event::ptr ev = *i;
EventType etype = ev->getEventType();
HandlerMap_t::iterator j = handlers.find(etype);
if (j == handlers.end()) {
perr_printf("Event %s has no handlers registered\n", etype.name().c_str());
continue;
}
HandlerSet_t *hset = j->second;
for (HandlerSet_t::iterator k = hset->begin(); k != hset->end(); k++)
{
Handler *hnd = *k;
bool already_handled = (ev->handled_by.find(hnd) != ev->handled_by.end());
if (already_handled) {
pthrd_printf("Have event %s on %s (already handled)\n",
ev->name().c_str(), hnd->getName().c_str());
continue;
}
pthrd_printf("Event %s added to handle list with handler %s\n",
ev->name().c_str(), hnd->getName().c_str());
events_and_handlers.insert(pair<Event::ptr, Handler*>(ev, hnd));
}
}
/**
* We should have all events and handlers properly sorted into the
* events_and_handlers set in the order we want to run them in.
* Now it's finally time to run the handlers.
**/
bool handled_something = false;
bool had_error = true;
ev_hndler_set_t::iterator i;
if (dyninst_debug_proccontrol) {
for (i = events_and_handlers.begin(); i != events_and_handlers.end(); i++) {
pthrd_printf("Have event %s on %s\n", i->first->name().c_str(), i->second->getName().c_str());
}
}
for (i = events_and_handlers.begin(); i != events_and_handlers.end(); i++)
{
handled_something = true;
Event::ptr event = i->first;
Handler *handler = i->second;
EventType etype = event->getEventType();
if (handler->getPriority() != Handler::CallbackPriority) {
//We don't want the callback handler getting async events
// from user operations in the callback.
cur_event = getRealParent(event);
}
pthrd_printf("Handling event '%s' with handler '%s'\n", etype.name().c_str(),
handler->getName().c_str());
Handler::handler_ret_t result = handler->handleEvent(event);
cur_event = Event::ptr();
if (result == Handler::ret_async) {
pthrd_printf("Handler %s did not complete due to pending asyncs\n",
handler->getName().c_str());
markEventAsyncPending(event);
return true;
}
if (result == Handler::ret_cbdelay) {
pthrd_printf("Handler %s delaying for user thread\n",
handler->getName().c_str());
mbox()->enqueue_user(orig_ev);
return true;
}
if (result == Handler::ret_again) {
pthrd_printf("Handler %s throwing event again\n",
handler->getName().c_str());
mbox()->enqueue(orig_ev);
return true;
}
event->handled_by.insert(handler);
if (result == Handler::ret_error) {
pthrd_printf("Error handling event %s with %s\n", etype.name().c_str(),
handler->getName().c_str());
had_error = true;
}
if (hasLateEvents()) {
/**
* A handler can choose to add new events to the current set by
* calling HandlerPool::addLateEvent(...). We'll check for late
* events after each handle call, add them as new subservient events,
* and then recursively call handleEvents. The recursive call shouldn't
* re-trigger already-run handlers due to the 'handled_by' mechanism.
**/
pthrd_printf("Handler added late events. Recursively calling handleEvent\n");
collectLateEvents(event);
return handleEvent(orig_ev);
}
}
for (auto event : all_events) {
clearEventAsync(event); //nop if ev wasn't async
}
return !had_error && handled_something;
}
std::set<HandlerPool *> HandlerPool::procsAsyncPending;
Mutex<> HandlerPool::asyncPendingLock;
void HandlerPool::markProcAsyncPending(HandlerPool *p)
{
asyncPendingLock.lock();
assert(procsAsyncPending.find(p) == procsAsyncPending.end());
procsAsyncPending.insert(p);
asyncPendingLock.unlock();
}
void HandlerPool::clearProcAsyncPending(HandlerPool *p)
{
asyncPendingLock.lock();
std::set<HandlerPool *>::iterator i = procsAsyncPending.find(p);
assert(i != procsAsyncPending.end());
procsAsyncPending.erase(i);
asyncPendingLock.unlock();
}
bool HandlerPool::hasProcAsyncPending()
{
asyncPendingLock.lock();
bool result = !procsAsyncPending.empty();
if (result) {
pthrd_printf("Pending async proc: %d\n", (*procsAsyncPending.begin())->proc->getPid());
}
asyncPendingLock.unlock();
return result;
}
HandlePreBootstrap::HandlePreBootstrap() :
Handler(std::string("Pre-bootstrap"))
{
}
HandlePreBootstrap::~HandlePreBootstrap()
{
}
void HandlePreBootstrap::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::PreBootstrap));
}
Handler::handler_ret_t HandlePreBootstrap::handleEvent(Event::ptr)
{
//int_process* p = ev->getProcess()->llproc();
//p->setForceGeneratorBlock(true);
return ret_success;
}
HandleBootstrap::HandleBootstrap() :
Handler(std::string("Bootstrap"))
{
}
HandleBootstrap::~HandleBootstrap()
{
}
void HandleBootstrap::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Bootstrap));
}
Handler::handler_ret_t HandleBootstrap::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
int_thread *thrd = ev->getThread()->llthrd();
assert(proc);
pthrd_printf("Handling bootstrap for %d\n", proc->getPid());
if (proc->getState() == int_process::running) {
if (thrd->getUserState().getState() == int_thread::neonatal_intermediate) {
//Bootstrapping a thread on an already running process
int_thread *initial_thread = proc->threadPool()->initialThread();
int_thread::State it_user_state = initial_thread->getUserState().getState();
thrd->getUserState().setState(it_user_state);
}
return ret_success;
}
thrd->getUserState().setState(int_thread::stopped);
bool all_bootstrapped = true;
int_threadPool *tp = proc->threadPool();
for (int_threadPool::iterator i = tp->begin(); i != tp->end(); i++) {
int_thread *thr = *i;
if (thr->getHandlerState().getState() == int_thread::neonatal_intermediate) {
pthrd_printf("Thread %d is not yet bootstrapped\n", thr->getLWP());
all_bootstrapped = false;
break;
}
}
if (all_bootstrapped) {
pthrd_printf("All threads are bootstrapped, marking process bootstrapped\n");
proc->setState(int_process::running);
}
return ret_success;
}
HandleSignal::HandleSignal() :
Handler(std::string("Signal"))
{
}
HandleSignal::~HandleSignal()
{
}
void HandleSignal::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Signal));
}
Handler::handler_ret_t HandleSignal::handleEvent(Event::ptr ev)
{
int_thread *thrd = ev->getThread()->llthrd();
int_process *proc = ev->getProcess()->llproc();
EventSignal *sigev = static_cast<EventSignal *>(ev.get());
int signal_no = sigev->getSignal();
thrd->setContSignal(signal_no);
int_signalMask *sigproc = proc->getSignalMask();
if (sigproc) {
if (!sigproc->allowSignal(signal_no)) {
pthrd_printf("Not giving callback on signal because its not in the SignalMask\n");
ev->setSuppressCB(true);
}
}
return ret_success;
}
HandlePostExit::HandlePostExit() :
Handler("Post Exit")
{
}
HandlePostExit::~HandlePostExit()
{
}
void HandlePostExit::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Post, EventType::Exit));
}
Handler::handler_ret_t HandlePostExit::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
assert(proc);
if(ev->getThread()) {
int_thread *thrd = ev->getThread()->llthrd();
assert(thrd);
pthrd_printf("Handling post-exit for process %d on thread %d\n",
proc->getPid(), thrd->getLWP());
} else {
pthrd_printf("Handling post-exit for process %d, all threads gone\n",
proc->getPid());
}
EventExit *event = static_cast<EventExit *>(ev.get());
ProcPool()->condvar()->lock();
proc->setState(int_process::exited);
ProcPool()->rmProcess(proc);
if(proc->wasForcedTerminated())
{
proc->getStartupTeardownProcs().dec();
ev->setSuppressCB(true);
}
else
{
proc->setExitCode(event->getExitCode());
}
ProcPool()->condvar()->broadcast();
ProcPool()->condvar()->unlock();
return ret_success;
}
HandlePostExitCleanup::HandlePostExitCleanup() :
Handler("Post Exit Cleanup")
{
}
HandlePostExitCleanup::~HandlePostExitCleanup()
{
}
void HandlePostExitCleanup::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Post, EventType::Exit));
etypes.push_back(EventType(EventType::None, EventType::Crash));
}
Handler::handler_ret_t HandlePostExitCleanup::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
int_thread *thrd = ev->getThread() ? ev->getThread()->llthrd() : NULL;
assert(proc);
#if defined(os_windows)
// On Windows, this is the only callback we get, so delay setting exited
// until cleanup
proc->setState(int_process::exited);
#endif
// assert(thrd);
pthrd_printf("Handling post-exit/crash cleanup for process %d on thread %d\n",
proc->getPid(), thrd ? thrd->getLWP() : (Dyninst::LWP)(-1));
for (int_threadPool::iterator i = proc->threadPool()->begin(); i != proc->threadPool()->end(); ++i) {
(*i)->setPendingStop(false);
}
proc->setForceGeneratorBlock(false);
if (int_process::in_waitHandleProc == proc) {
pthrd_printf("Postponing delete due to being in waitAndHandleForProc\n");
} else {
delete proc;
}
return ret_success;
}
int HandlePostExitCleanup::getPriority() const
{
return Handler::PostCallbackPriority;
}
HandleCrash::HandleCrash() :
Handler("Crash")
{
}
HandleCrash::~HandleCrash()
{
}
void HandleCrash::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Crash));
}
Handler::handler_ret_t HandleCrash::handleEvent(Event::ptr ev)
{
pthrd_printf("Handling crash for process %d on thread %d\n",
ev->getProcess()->getPid(), ev->getThread()->getLWP());
// evproc->getPid(), thrd->getLWP());
int_process *proc = ev->getProcess()->llproc();
int_thread *thrd = ev->getThread()->llthrd();
assert(proc);
assert(thrd);
if( !proc || !thrd) return ret_error;
EventCrash *event = static_cast<EventCrash *>(ev.get());
if (proc->wasForcedTerminated()) {
pthrd_printf("Crash was due to process::terminate, not reporting\n");
event->setSuppressCB(true);
}
else {
proc->setCrashSignal(event->getTermSignal());
}
ProcPool()->condvar()->lock();
proc->setState(int_process::exited);
ProcPool()->rmProcess(proc);
ProcPool()->condvar()->broadcast();
ProcPool()->condvar()->unlock();
return ret_success;
}
HandleForceTerminate::HandleForceTerminate() :
Handler("ForceTerminate")
{
}
HandleForceTerminate::~HandleForceTerminate()
{
}
void HandleForceTerminate::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Post, EventType::ForceTerminate));
}
Handler::handler_ret_t HandleForceTerminate::handleEvent(Event::ptr ev) {
int_process *proc = ev->getProcess()->llproc();
Thread::const_ptr t = ev->getThread();
int_thread* thrd = NULL;
if(t)
thrd = t->llthrd();
assert(proc);
// assert(thrd);
pthrd_printf("Handling force terminate for process %d on thread %d\n",
proc->getPid(), thrd ? thrd->getLWP() : (Dyninst::LWP)(-1));
ProcPool()->condvar()->lock();
proc->setState(int_process::exited);
for (int_threadPool::iterator iter = proc->threadPool()->begin();
iter != proc->threadPool()->end(); ++iter) {
ProcPool()->addDeadThread((*iter)->getLWP());
}
ProcPool()->rmProcess(proc);
ProcPool()->condvar()->broadcast();
ProcPool()->condvar()->unlock();
proc->getStartupTeardownProcs().dec();
if (int_process::in_waitHandleProc == proc) {
pthrd_printf("Postponing delete due to being in waitAndHandleForProc\n");
} else {
delete proc;
}
return ret_success;
}
int HandleForceTerminate::getPriority() const {
return PostCallbackPriority;
}
HandlePreExit::HandlePreExit() :
Handler("Pre Exit")
{
}
HandlePreExit::~HandlePreExit()
{
}
void HandlePreExit::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Pre, EventType::Exit));
}
Handler::handler_ret_t HandlePreExit::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
int_thread *thread = ev->getThread()->llthrd();
pthrd_printf("Handling pre-exit for process %d on thread %d\n",
proc->getPid(), thread->getLWP());
// Sometimes when the mutator attempts to stop the mutatee,
// the mutatee exited before the stop is delivered to the mutatee.
// In such case, let the mutator not wait for the exited thread.
// Otherwise, the mutator can be stuck in an infinite loop waiting
// for a SIGSTOP that will never come
if (thread->hasPendingStop())
thread->setPendingStop(false);
thread->setExiting(true);
if (proc->wasForcedTerminated()) {
//Linux sometimes throws an extraneous exit after
// calling ptrace(PTRACE_KILL, ...). It's not a real exit
pthrd_printf("Proc pre-exit was due to process::terminate, not reporting\n");
ev->setSuppressCB(true);
if (ev->getSyncType() == Event::sync_thread)
thread->getExitingState().setState(int_thread::running);
else
thread->getExitingState().setStateProc(int_thread::running);
}
return ret_success;
}
HandleThreadCreate::HandleThreadCreate() :
Handler("Thread Create")
{
}
HandleThreadCreate::~HandleThreadCreate()
{
}
void HandleThreadCreate::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::ThreadCreate));
etypes.push_back(EventType(EventType::None, EventType::UserThreadCreate));
etypes.push_back(EventType(EventType::None, EventType::LWPCreate));
}
Handler::handler_ret_t HandleThreadCreate::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
Thread::const_ptr hl_thrd = ev->getThread();
int_thread* thrd = NULL;
if(hl_thrd) thrd = hl_thrd->llthrd();
EventNewThread *threadev = static_cast<EventNewThread *>(ev.get());
pthrd_printf("Handle thread create for %d/%d with new thread %d\n",
proc->getPid(), thrd ? thrd->getLWP() : (Dyninst::LWP)(-1), threadev->getLWP());
if (thrd && thrd->getPostponedSyscallState().isDesynced())
thrd->getPostponedSyscallState().restoreState();
if (ev->getEventType().code() == EventType::UserThreadCreate) {
//If we support both user and LWP thread creation, and we're doing a user
// creation, then the Thread object may already exist. Do nothing.
int_thread *thr = proc->threadPool()->findThreadByLWP(threadev->getLWP());
if (thr) {
pthrd_printf("Thread object already exists, ThreadCreate handler doing nothing\n");
return ret_success;
}
}
ProcPool()->condvar()->lock();
int_thread::attach_status_t astatus = int_thread::as_unknown;
if (ev->getEventType().code() == EventType::LWPCreate) {
EventNewLWP::ptr lwp_create = ev->getEventNewLWP();
astatus = lwp_create->getInternalEvent()->attach_status;
}
int_thread *newthr = int_thread::createThread(proc, NULL_THR_ID, threadev->getLWP(), false, astatus);
newthr->getGeneratorState().setState(int_thread::stopped);
newthr->getHandlerState().setState(int_thread::stopped);
if (!thrd) {
//This happens on BG/P with user thread events.
pthrd_printf("Setting new event to have occured on new thread\n");
ev->setThread(newthr->thread());
thrd = newthr;
}
int_thread *inherit_from = (thrd == newthr) ? proc->threadPool()->initialThread() : thrd;
newthr->getUserState().setState(inherit_from->getUserState().getState());
pthrd_printf("Initializing new thread states to match rest of process for %d/%d\n",
proc->getPid(), newthr->getLWP());
map<int, int> &states = proc->getProcDesyncdStates();
for (map<int, int>::iterator i = states.begin(); i != states.end(); i++) {
if (!i->second)
continue;
int_thread::StateTracker &statet = newthr->getStateByID(i->first);
int_thread::State ns = proc->threadPool()->initialThread()->getStateByID(i->first).getState();
if (statet.getID() == int_thread::BreakpointResumeStateID) {
//Special case, new threads always go to stopped for breakpoint resume.
pthrd_printf(".... setting state to stopped for BreakpointResumeStateID\n");
ns = int_thread::stopped;
}
for (int j = 0; j < i->second; j++) {
pthrd_printf("desyncing state for %d\n", statet.debugthr()->getLWP());
statet.desyncState(ns);
}
}
pthrd_printf("finished initializing thread %d/%d\n",
proc->getPid(), newthr->getLWP());
ProcPool()->condvar()->broadcast();
ProcPool()->condvar()->unlock();
return ret_success;
}
HandleThreadDestroy::HandleThreadDestroy() :
Handler("Thread Destroy")
{
}
HandleThreadDestroy::~HandleThreadDestroy()
{
}
void HandleThreadDestroy::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Any, EventType::UserThreadDestroy));
etypes.push_back(EventType(EventType::Any, EventType::LWPDestroy));
etypes.push_back(EventType(EventType::Any, EventType::WinStopThreadDestroy));
}
int HandleThreadDestroy::getPriority() const
{
return Handler::PostPlatformPriority + 2; //After thread_db destroy handler
}
Handler::handler_ret_t HandleThreadDestroy::handleEvent(Event::ptr ev)
{
int_thread *thrd = ev->getThread()->llthrd();
/* The internal thread can be NULL if we receive multiple ThreadDestroy events
* for the same thread. This can happen when handling "ghost" threads.
*/
if(!thrd) {
ev->setSuppressCB(true);
pthrd_printf("Encountered an already-destroyed thread\n");
return ret_success;
}
if (!thrd->isUser()) {
ev->setSuppressCB(true);
}
int_process *proc = ev->getProcess()->llproc();
if (ev->getEventType().time() == EventType::Pre && proc->plat_supportLWPPostDestroy()) {
pthrd_printf("Handling pre-thread destroy for %d\n", thrd->getLWP());
return ret_success;
}
if (ev->getEventType().code() == EventType::UserThreadDestroy &&
(proc->plat_supportLWPPostDestroy() || proc->plat_supportLWPPreDestroy()))
{
//This is a user thread delete, but we still have an upcoming LWP
// delete. Don't actually do anything yet.
pthrd_printf("Handling user thread... postponing clean of %d/%d until LWP delete\n",
proc->getPid(), thrd->getLWP());
return ret_success;
}
pthrd_printf("Handling post-thread destroy for %d/%d\n", proc->getPid(), thrd->getLWP());
if (proc->wasForcedTerminated()) {
//Linux sometimes throws an extraneous thread terminate after
// calling ptrace(PTRACE_KILL, ...). It's not a real thread terminate.
pthrd_printf("Thread terminate was due to process::terminate, not reporting\n");
ev->setSuppressCB(true);
}
return ret_success;
}
HandleThreadCleanup::HandleThreadCleanup() :
Handler("Thread Cleanup")
{
}
HandleThreadCleanup::~HandleThreadCleanup()
{
}
void HandleThreadCleanup::getEventTypesHandled(vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Any, EventType::UserThreadDestroy));
etypes.push_back(EventType(EventType::Any, EventType::LWPDestroy));
etypes.push_back(EventType(EventType::Any, EventType::WinStopThreadDestroy));
}
int HandleThreadCleanup::getPriority() const
{
return Handler::PostCallbackPriority + 1;
}
Handler::handler_ret_t HandleThreadCleanup::handleEvent(Event::ptr ev)
{
/**
* This is a seperate handler so that the cleanup happens after any
* user callback.
**/
int_process *proc = ev->getProcess()->llproc();
if(!proc) {
pthrd_printf("Process for thread cleanup event is NULL. We have no work we can do.\n");
return ret_success;
}
if ((ev->getEventType().code() == EventType::UserThreadDestroy) &&
(proc->plat_supportLWPPreDestroy() || proc->plat_supportLWPPostDestroy()))
{
//Have a user thread destroy, but platform supports LWP destroys. Will clean with a latter event.
pthrd_printf("Nothing to do in HandleThreadCleanup\n");
return ret_success;
}
if ((ev->getEventType().code() == EventType::LWPDestroy && ev->getEventType().time() == EventType::Pre) &&
proc->plat_supportLWPPostDestroy())
{
//Have a pre-lwp destroy, but platform supports post-lwp destroy. Will clean with a latter event.
pthrd_printf("Nothing to do in HandleThreadCleanup\n");
return ret_success;
}
bool should_delete = true;
if ((ev->getEventType().code() == EventType::UserThreadDestroy &&
!proc->plat_supportLWPPreDestroy() && !proc->plat_supportLWPPostDestroy()))
{
//Have a user thread destroy, and platform doesn't support LWP destroys. Will partially clean,
// but leave thread object thread throws any events before it deletes.
should_delete = false;
}
int_thread *thrd = ev->getThread()->llthrd();
if(!thrd) {
pthrd_printf("Thread for thread cleanup event is NULL. We have no work we can do.\n");
return ret_success;
}
pthrd_printf("Cleaning thread %d/%d from HandleThreadCleanup handler.\n",
proc->getPid(), thrd->getLWP());
int_thread::cleanFromHandler(thrd, should_delete);
return ret_success;
}
HandleThreadStop::HandleThreadStop() :
Handler(std::string("Thread Stop"))
{
}
HandleThreadStop::~HandleThreadStop()
{
}
int HandleThreadStop::getPriority() const
{
return DefaultPriority;
}
void HandleThreadStop::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Stop));
}
Handler::handler_ret_t HandleThreadStop::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
if (ev->getSyncType() == Event::sync_process) {
pthrd_printf("Handling process stop for %d\n", proc->getPid());
bool found_pending_stop = false;
#if defined(os_windows)
windows_process *wproc = dynamic_cast<windows_process *>(proc);
if (wproc && wproc->pendingDebugBreak()) {
found_pending_stop = true;
wproc->clearPendingDebugBreak();
}
#endif
for (int_threadPool::iterator i = proc->threadPool()->begin(); i != proc->threadPool()->end(); i++) {
int_thread *thrd = *i;
pthrd_printf("Handling process stop for %d/%d\n", proc->getPid(), thrd->getLWP());
if (thrd->hasPendingStop()) {
found_pending_stop = true;
thrd->setPendingStop(false);
}
}
assert(found_pending_stop);
}
else {
int_thread *thrd = ev->getThread()->llthrd();
pthrd_printf("Handling thread stop for %d/%d\n", proc->getPid(), thrd->getLWP());
assert(thrd->hasPendingStop());
thrd->setPendingStop(false);
}
return ret_success;
}
HandlePostFork::HandlePostFork() :
Handler("Post Fork")
{
}
HandlePostFork::~HandlePostFork()
{
}
void HandlePostFork::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Post, EventType::Fork));
}
Handler::handler_ret_t HandlePostFork::handleEvent(Event::ptr ev)
{
EventFork *efork = static_cast<EventFork *>(ev.get());
Dyninst::PID child_pid = efork->getPID();
int_process *parent_proc = ev->getProcess()->llproc();
pthrd_printf("Handling fork for parent %d to child %d\n",
parent_proc->getPid(), child_pid);
int_process *child_proc = ProcPool()->findProcByPid(child_pid);
if( child_proc == NULL ) {
child_proc = int_process::createProcess(child_pid, parent_proc);
}
int_followFork *fork_proc = parent_proc->getFollowFork();
if (fork_proc->fork_isTracking() == FollowFork::DisableBreakpointsDetach) {
//Silence this event. Child will be detached.
ev->setSuppressCB(true);
}
int_thread *thrd = ev->getThread()->llthrd();
if (thrd && thrd->getPostponedSyscallState().isDesynced())
thrd->getPostponedSyscallState().restoreState();
assert(child_proc);
return child_proc->forked() ? ret_success : ret_error;
}
HandlePostForkCont::HandlePostForkCont() :
Handler("Post Fork Continue")
{
}
HandlePostForkCont::~HandlePostForkCont()
{
}
void HandlePostForkCont::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Post, EventType::Fork));
}
Handler::handler_ret_t HandlePostForkCont::handleEvent(Event::ptr ev)
{
EventFork *efork = static_cast<EventFork *>(ev.get());
Dyninst::PID child_pid = efork->getPID();
int_process *child_proc = ProcPool()->findProcByPid(child_pid);
int_process *parent_proc = ev->getProcess()->llproc();
pthrd_printf("Handling post-fork continue for child %d\n", child_pid);
assert(child_proc);
int_followFork *fork_proc = parent_proc->getFollowFork();
if (fork_proc->fork_isTracking() == FollowFork::DisableBreakpointsDetach) {
child_proc->throwDetachEvent(false, false);
}
else {
//We need syncRunState to run for the new process to continue it.
// do so by throwing a NOP event on the new process.
child_proc->throwNopEvent();
}
return ret_success;
}
int HandlePostForkCont::getPriority() const {
return PostCallbackPriority;
}
HandlePostExec::HandlePostExec() :
Handler("Post Exec")
{
}
HandlePostExec::~HandlePostExec()
{
}
void HandlePostExec::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Post, EventType::Exec));
}
Handler::handler_ret_t HandlePostExec::handleEvent(Event::ptr ev)
{
EventExec *eexec = static_cast<EventExec *>(ev.get());
int_process *proc = ev->getProcess()->llproc();
pthrd_printf("Handling exec for process %d\n",
proc->getPid());
bool result = proc->execed();
if (!result)
return ret_error;
eexec->setExecPath(proc->getExecutable());
eexec->setThread(proc->threadPool()->initialThread()->thread());
return ret_success;
}
HandleSingleStep::HandleSingleStep() :
Handler("Single Step")
{
}
HandleSingleStep::~HandleSingleStep()
{
}
void HandleSingleStep::getEventTypesHandled(vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::SingleStep));
}
Handler::handler_ret_t HandleSingleStep::handleEvent(Event::ptr ev)
{
pthrd_printf("Handling event single step on %d/%d\n",
ev->getProcess()->llproc()->getPid(),
ev->getThread()->llthrd()->getLWP());
return ret_success;
}
HandlePreSyscall::HandlePreSyscall() :
Handler("Pre Syscall")
{
}
HandlePreSyscall::~HandlePreSyscall()
{
}
void HandlePreSyscall::getEventTypesHandled(vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Pre, EventType::PreSyscall));
}
Handler::handler_ret_t HandlePreSyscall::handleEvent(Event::ptr ev)
{
pthrd_printf("Handling event pre-syscall on %d/%d\n",
ev->getProcess()->llproc()->getPid(),
ev->getThread()->llthrd()->getLWP());
return ret_success;
}
HandlePostSyscall::HandlePostSyscall() :
Handler("Post Syscall")
{
}
HandlePostSyscall::~HandlePostSyscall()
{
}
void HandlePostSyscall::getEventTypesHandled(vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::Post, EventType::PostSyscall));
}
Handler::handler_ret_t HandlePostSyscall::handleEvent(Event::ptr ev)
{
pthrd_printf("Handling event post-syscall on %d/%d\n",
ev->getProcess()->llproc()->getPid(),
ev->getThread()->llthrd()->getLWP());
return ret_success;
}
/**
* This handler is triggered when a breakpoint is first hit (e.g., on
* the SIGTRAP signal. It's main purpose is to prepare the thread state
* before the user callback
**/
HandleBreakpoint::HandleBreakpoint() :
Handler("Breakpoint")
{
}
HandleBreakpoint::~HandleBreakpoint()
{
}
void HandleBreakpoint::getEventTypesHandled(vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Breakpoint));
}
Handler::handler_ret_t HandleBreakpoint::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
int_thread *thrd = ev->getThread()->llthrd();
EventBreakpoint *ebp = static_cast<EventBreakpoint *>(ev.get());
pthrd_printf("Handling breakpoint at %lx\n", ebp->getAddress());
int_eventBreakpoint *int_ebp = ebp->getInternal();
bp_instance *breakpoint = int_ebp->lookupInstalledBreakpoint();
if (!breakpoint) {
//Likely someone cleaned the breakpoint.
pthrd_printf("No breakpoint installed at location of BP hit.\n");
return ret_success;
}
sw_breakpoint *swbp = breakpoint->swBP();
/**
* Move the PC if it's a control transfer breakpoint, or if the
* current architecture advances the PC forward upon breakpoint
* hit (x86 or x86_64).
**/
bool changePC = false;
Address changePCTo = 0x0;
MachRegister pcreg = MachRegister::getPC(proc->getTargetArch());
int_breakpoint *transferbp = breakpoint->getCtrlTransferBP(thrd);
if (transferbp) {
changePC = true;
if (transferbp->isOffsetTransfer()) {
Address cur_addr = breakpoint->getAddr();
signed long offset = (signed long) transferbp->toAddr();
changePCTo = cur_addr + offset;
}
else {
changePCTo = transferbp->toAddr();
}
pthrd_printf("Breakpoint has control transfer. Moving PC to %lx\n", changePCTo);
}
else if (swbp && proc->plat_breakpointAdvancesPC()) {
changePC = true;
changePCTo = breakpoint->getAddr();
pthrd_printf("Breakpoint shifted PC. Moving PC to %lx\n", changePCTo);
}
if (changePC && !int_ebp->pc_regset) {
int_ebp->pc_regset = result_response::createResultResponse();
bool ok = thrd->setRegister(pcreg, changePCTo, int_ebp->pc_regset);
if(!ok)
{
pthrd_printf("Error setting pc register on breakpoint\n");
ev->setLastError(err_internal, "Could not set pc register upon breakpoint\n");
return ret_error;
}
}
if (int_ebp->pc_regset && int_ebp->pc_regset->hasError()) {
pthrd_printf("Error setting pc register on breakpoint\n");
ev->setLastError(err_internal, "Could not set pc register upon breakpoint\n");
return ret_error;
}
if (int_ebp->pc_regset && !int_ebp->pc_regset->isReady()) {
//We're probably waiting for async to finish.
proc->handlerPool()->notifyOfPendingAsyncs(int_ebp->pc_regset, ev);
pthrd_printf("Returning async from BP handler while setting PC\n");
return ret_async;
}
/**
* Check if any of the breakpoints should trigger a callback
**/
vector<Breakpoint::ptr> hl_bps;
ebp->getBreakpoints(hl_bps);
int_ebp->cb_bps.clear();
for (vector<Breakpoint::ptr>::iterator i = hl_bps.begin(); i != hl_bps.end(); i++) {
int_breakpoint *bp = (*i)->llbp();
if (bp->isOneTimeBreakpoint() && bp->isOneTimeBreakpointHit())
continue;
if (bp->isThreadSpecific() && !bp->isThreadSpecificTo(ev->getThread()))
continue;
int_ebp->cb_bps.insert(*i);
}
pthrd_printf("In breakpoint handler, %ld breakpoints for user callback\n",
(long int) int_ebp->cb_bps.size());
if (int_ebp->cb_bps.empty())
ebp->setSuppressCB(true);
/**
* Handle onetime breakpoints. These are essentially auto-deleted
* the first time they're hit. onetime breakpoints that are thread
* specific are only auto-deleted if they are hit by their thread.
**/
for (bp_instance::iterator i = breakpoint->begin(); i != breakpoint->end(); i++) {
int_breakpoint *bp = *i;
if (!bp->isOneTimeBreakpoint())
continue;
if (bp->isThreadSpecific() && !bp->isThreadSpecificTo(ev->getThread()))
continue;
if (bp->isOneTimeBreakpointHit()) {
pthrd_printf("One time breakpoint was hit twice, should have CB dropped.\n");
continue;
}
pthrd_printf("Marking oneTimeBreakpoint as hit\n");
bp->markOneTimeHit();
}
/**
* Breakpoints show up as permanent stops to the user. If they do a
* default cb_ret_t, then the process remains stopped.
**/
if (!int_ebp->cb_bps.empty() && !ebp->suppressCB())
{
pthrd_printf("BP handler is setting user state to reflect breakpoint\n");
const Event::SyncType effective_sync_type = [&ebp, &hl_bps]() {
/**
* A synchronous breakpoint requires all threads in the process to be
* stopped before it is "executed".
**/
for(auto &&bp : hl_bps) {
if(bp->isSynchronous()) {
pthrd_printf("Found synchronous breakpoint, upgrading to sync_process\n");
return Event::sync_process;
}
}
return ebp->getSyncType();
}();
switch (effective_sync_type) {
case Event::sync_process: {
int_threadPool *pool = proc->threadPool();
for (int_threadPool::iterator i = pool->begin(); i != pool->end(); i++)
(*i)->getUserState().setState(int_thread::stopped);
break;
}
case Event::sync_thread:
thrd->getUserState().setState(int_thread::stopped);
break;
case Event::async:
assert(0); //Async BPs don't make sense to me
break;
case Event::unset:
assert(0);
break;
}
}
/**
* Mark the thread as being stopped on this breakpoint. Control transfer
* bps don't leave their thread stopped at the BP, so don't do them.
**/
if (!transferbp && breakpoint->needsClear()) {
pthrd_printf("Breakpoint handler decided BP needs clearing\n");
thrd->markStoppedOnBP(breakpoint);
}
else {
pthrd_printf("Breakpoint handler decided BP does not need clearing\n");
}
return ret_success;
}
HandleBreakpointContinue::HandleBreakpointContinue() :
Handler("BreakpointContinue")
{
}
HandleBreakpointContinue::~HandleBreakpointContinue()
{
}
void HandleBreakpointContinue::getEventTypesHandled(vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Breakpoint));
}
int HandleBreakpointContinue::getPriority() const
{
return PostCallbackPriority;
}
Handler::handler_ret_t HandleBreakpointContinue::handleEvent(Event::ptr ev)
{
/**
* This is in a seperate handler from Breakpoint to make it run after
* any subservient events associated with Breakpoint. Library or SysV
* events want to operate on a stopped process, so we've postponed this
* continue until after those events are done.
**/
EventBreakpoint *ebp = static_cast<EventBreakpoint *>(ev.get());
int_eventBreakpoint *int_bp = ebp->getInternal();
if (int_bp->stopped_proc) {
ebp->getThread()->llthrd()->getBreakpointHoldState().restoreStateProc();
}
return Handler::ret_success;
}
HandleBreakpointClear::HandleBreakpointClear() :
Handler("BreakpointClear")
{
}
HandleBreakpointClear::~HandleBreakpointClear()
{
}
void HandleBreakpointClear::getEventTypesHandled(vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::BreakpointClear));
}
/**
* The handler triggers when a thread stopped on a breakpoint is continued.
**/
Handler::handler_ret_t HandleBreakpointClear::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
int_thread *thrd = ev->getThread()->llthrd();
EventBreakpointClear *evbpc = static_cast<EventBreakpointClear *>(ev.get());
int_eventBreakpointClear *int_bpc = evbpc->getInternal();
bp_instance *ibp = thrd->isStoppedOnBP();
hw_breakpoint *hwbp = dynamic_cast<hw_breakpoint *>(ibp);
if (!ibp || !ibp->isInstalled()) {
pthrd_printf("HandleBreakpointClear on thread without breakpoint. BP must have been deleted\n");
thrd->getBreakpointState().restoreStateProc();
thrd->markStoppedOnBP(NULL);
return Handler::ret_success;
}
assert(!int_bpc->stopped_proc ||
proc->threadPool()->allStopped(int_thread::BreakpointStateID) ||
proc->threadPool()->allStopped(int_thread::AsyncStateID));
/**
* Suspend breakpoint
**/
if (!int_bpc->started_bp_suspends) {
pthrd_printf("Removing breakpoint from memory under HandleBreakpointClear\n");
int_bpc->started_bp_suspends = true;
bool result = (bool)ibp->suspend(proc, int_bpc->bp_suspend);
if (!result) {
pthrd_printf("Error suspending breakpoint in HandleBreakpointClear\n");
return Handler::ret_error;
}
}
/**
* Handle cases where breakpoint suspends are async and not completed.
**/
for (set<response::ptr>::iterator i = int_bpc->bp_suspend.begin(); i != int_bpc->bp_suspend.end();) {
response::ptr resp = *i;
if (!resp || !resp->isPosted() || resp->isReady())
int_bpc->bp_suspend.erase(i++);
else
i++;
}
if (!int_bpc->bp_suspend.empty()) {
pthrd_printf("Breakpoint clear is pending in HandleBreakpointClear. Returning async\n");
proc->handlerPool()->notifyOfPendingAsyncs(int_bpc->bp_suspend, ev);
return Handler::ret_async;
}
/**
* Check if any of the int_breakpoints are going to be restored
**/
bool restore_bp = false;
for (sw_breakpoint::iterator i = ibp->begin(); i != ibp->end(); i++) {
if ((*i)->isOneTimeBreakpoint() && (*i)->isOneTimeBreakpointHit())
continue;
restore_bp = true;
break;
}
thrd->markStoppedOnBP(NULL);
if (restore_bp) {
pthrd_printf("HandleBreakpointClear restoring BP. Single stepping the process.\n");
thrd->markClearingBreakpoint(ibp);
thrd->setSingleStepMode(true);
pthrd_printf("Making stop/continue decisions for handleBreakpointClear when stepping over BP\n");
//Threads without breakpoint restores get set to stopped. Threads resuming breakpoints
// are set-to/left-at running.
for (int_threadPool::iterator i = proc->threadPool()->begin(); i != proc->threadPool()->end(); i++) {
if (hwbp && *i != hwbp->getThread()) {
//Hardware breakpoints don't desync the whole process
continue;
}
int_thread::StateTracker &st = (*i)->getBreakpointResumeState();
if (st.getState() == int_thread::running || *i == thrd)
st.desyncState(int_thread::running); //This thread is resuming, desync
else if (int_bpc->stopped_proc)
st.desyncState(int_thread::stopped); //Mark this as stopped as we restore the BP
}
if (!hwbp) {
//If new threads come in during a BP clear, then they should be
//created stopped.
int bpr_stateid = int_thread::BreakpointResumeStateID;
proc->getProcDesyncdStates()[bpr_stateid]++;
}
}
else {
pthrd_printf("HandleBreakpointClear will not restore BP. Restoring process state.\n");
}
if (int_bpc->stopped_proc)
thrd->getBreakpointState().restoreStateProc();
else
thrd->getBreakpointState().restoreState();
return Handler::ret_success;
}
HandleBreakpointRestore::HandleBreakpointRestore() :
Handler("Breakpoint Restore")
{
}
HandleBreakpointRestore::~HandleBreakpointRestore()
{
}
void HandleBreakpointRestore::getEventTypesHandled(vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::BreakpointRestore));
}
Handler::handler_ret_t HandleBreakpointRestore::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
int_thread *thrd = ev->getThread()->llthrd();
EventBreakpointRestore *bpc = static_cast<EventBreakpointRestore *>(ev.get());
int_eventBreakpointRestore *int_bpc = bpc->getInternal();
bp_instance *bp = int_bpc->bp;
sw_breakpoint *swbp = bp->swBP();
bool result;
if (!int_bpc->set_states) {
pthrd_printf("Restoring breakpoint at %lx for %d/%d\n", bp->getAddr(), proc->getPid(), thrd->getLWP());
thrd->markClearingBreakpoint(NULL);
thrd->setSingleStepMode(false);
thrd->getBreakpointResumeState().setState(int_thread::stopped);
int_bpc->set_states = true;
}
//Time to restore this bp (maybe, the BP will actually resume when it's last thread is done restoring)
pthrd_printf("Restoring breakpoint to process\n");
if (!int_bpc->bp_resume_started) {
int_bpc->bp_resume_started = true;
result = bp->resume(proc, int_bpc->bp_resume);
if (!result) {
pthrd_printf("Error resuming breakpoint in handler\n");
return ret_error;
}
}
for (set<response::ptr>::iterator i = int_bpc->bp_resume.begin(); i != int_bpc->bp_resume.end();) {
response::ptr resp = *i;
if (!resp || !resp->isPosted() || resp->isReady())
int_bpc->bp_resume.erase(i++);
else
i++;
}
if (!int_bpc->bp_resume.empty()) {
pthrd_printf("Postponing breakpoint clear while waiting for memwrite\n");
proc->handlerPool()->notifyOfPendingAsyncs(int_bpc->bp_resume, ev);
return ret_async;
}
pthrd_printf("Restoring thread state after breakpoint restore\n");
if (swbp)
thrd->getBreakpointResumeState().restoreStateProc();
else
thrd->getBreakpointResumeState().restoreState();
return ret_success;
}
HandleEmulatedSingleStep::HandleEmulatedSingleStep() :
Handler("Emulated Single Step")
{
}
HandleEmulatedSingleStep::~HandleEmulatedSingleStep()
{
}
void HandleEmulatedSingleStep::getEventTypesHandled(vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Breakpoint));
}
Handler::handler_ret_t HandleEmulatedSingleStep::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
int_thread *thrd = ev->getThread()->llthrd();
emulated_singlestep *em_singlestep = thrd->getEmulatedSingleStep();
if (!em_singlestep)
return ret_success;
EventBreakpoint::ptr ev_bp = ev->getEventBreakpoint();
Address addr = ev_bp->getInternal()->addr;
assert(ev_bp);
if (!em_singlestep->containsBreakpoint(addr))
return ret_success;
pthrd_printf("Breakpoint %lx corresponds to emulated single step. Clearing BPs\n", addr);
async_ret_t aresult = em_singlestep->clear();
if (aresult == aret_async) {
proc->handlerPool()->notifyOfPendingAsyncs(em_singlestep->clear_resps, ev);
return ret_async;
}
EventSingleStep::ptr ev_ss = EventSingleStep::ptr(new EventSingleStep());
ev_ss->setProcess(proc->proc());
ev_ss->setThread(thrd->thread());
ev_ss->setSyncType(ev->getSyncType());
proc->handlerPool()->addLateEvent(ev_ss);
em_singlestep->restoreSSMode();
thrd->rmEmulatedSingleStep(em_singlestep);
return ret_success;
}
int HandleEmulatedSingleStep::getPriority() const
{
return PostPlatformPriority;
}
HandleLibrary::HandleLibrary() :
Handler("Library Handler")
{
}
HandleLibrary::~HandleLibrary()
{
}
Handler::handler_ret_t HandleLibrary::handleEvent(Event::ptr ev)
{
pthrd_printf("Handling library load/unload\n");
EventLibrary *lev = static_cast<EventLibrary *>(ev.get());
if(!lev->libsAdded().empty())
{
MTLock lock_this_block;
// this happens on Windows, where we already did the decode when we got the event
for(std::set<Library::ptr>::const_iterator lib = lev->libsAdded().begin();
lib != lev->libsAdded().end();
++lib)
{
ev->getProcess()->llproc()->memory()->addLibrary((*lib)->debug());
}
}
if(!lev->libsRemoved().empty())
{
MTLock lock_this_block;
for(std::set<Library::ptr>::const_iterator lib = lev->libsRemoved().begin();
lib != lev->libsRemoved().end();
++lib)
{
ev->getProcess()->llproc()->memory()->rmLibrary((*lib)->debug());
}
}
if(!lev->libsAdded().empty() || !lev->libsRemoved().empty())
return ret_success;
int_process *proc = ev->getProcess()->llproc();
set<int_library *> ll_added, ll_rmd;
set<response::ptr> async_responses;
bool async_pending = false;
bool result = proc->refresh_libraries(ll_added, ll_rmd, async_pending, async_responses);
if (!result && async_pending) {
proc->handlerPool()->notifyOfPendingAsyncs(async_responses, ev);
return ret_async;
}
if (!result) {
pthrd_printf("Failed to refresh library list\n");
return ret_error;
}
if (ll_added.empty() && ll_rmd.empty()) {
pthrd_printf("Could not find actual changes in lib state\n");
ev->setSuppressCB(true);
return ret_success;
}
set<Library::ptr> added, rmd;
for (set<int_library*>::iterator i = ll_added.begin(); i != ll_added.end(); i++) {
added.insert((*i)->getUpPtr());
}
for (set<int_library*>::iterator i = ll_rmd.begin(); i != ll_rmd.end(); i++) {
rmd.insert((*i)->getUpPtr());
}
for (set<int_library*>::iterator i = ll_rmd.begin(); i != ll_rmd.end(); i++) {
(*i)->markAsCleanable();
}
lev->setLibs(added, rmd);
return ret_success;
}
void HandleLibrary::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Library));
}
HandleDetach::HandleDetach() :
Handler("Detach")
{
}
HandleDetach::~HandleDetach()
{
}
Handler::handler_ret_t HandleDetach::handleEvent(Event::ptr ev)
{
int_process *proc = ev->getProcess()->llproc();
EventDetach::ptr detach_ev = ev->getEventDetach();
int_eventDetach *int_detach_ev = detach_ev->getInternal();
bool temporary = int_detach_ev->temporary_detach;
bool leaveStopped = int_detach_ev->leave_stopped;
bool &removed_bps = int_detach_ev->removed_bps;
result_response::ptr &detach_response = int_detach_ev->detach_response;
std::set<response::ptr> &async_responses = int_detach_ev->async_responses;
mem_state::ptr mem = proc->memory();
bool err = true;
if (!removed_bps)
{
if (!temporary) {
while (!mem->breakpoints.empty())
{
std::map<Dyninst::Address, sw_breakpoint *>::iterator i = mem->breakpoints.begin();
bool result = i->second->uninstall(proc, async_responses);
if (!result) {
perr_printf("Error removing breakpoint at %lx\n", i->first);
ev->setLastError(err_internal, "Error removing breakpoint before detach\n");
goto done;
}
}
}
else {
for(std::map<Dyninst::Address, sw_breakpoint *>::iterator i = mem->breakpoints.begin();
i != mem->breakpoints.end(); ++i)
{
bool result = i->second->suspend(proc, async_responses);
if(!result) {
perr_printf("Error suspending breakpoint at %lx\n", i->first);
ev->setLastError(err_internal, "Error suspending breakpoint before detach\n");
goto done;
}
}
}
removed_bps = true;
}
for (set<response::ptr>::iterator i = async_responses.begin(); i != async_responses.end(); i++) {
if ((*i)->hasError()) {
perr_printf("Failed to remove breakpoints\n");
ev->setLastError(err_internal, "Error removing breakpoint before detach\n");
goto done;
}
if (!(*i)->isReady()) {
pthrd_printf("Suspending detach due to async in breakpoint removal.\n");
proc->handlerPool()->notifyOfPendingAsyncs(async_responses, ev);
return ret_async;
}
}
if (!detach_response) {
pthrd_printf("Detach handler is triggering platform detach\n");
detach_response = result_response::createResultResponse();
bool result = proc->plat_detach(detach_response, leaveStopped);
if (!result) {
pthrd_printf("Error performing platform detach on %d\n", proc->getPid());
goto done;
}
}
if (detach_response->isPosted() && !detach_response->isReady()) {
pthrd_printf("Suspending detach operation while waiting for detach response\n");
proc->handlerPool()->notifyOfPendingAsyncs(detach_response, ev);
return ret_async;
}
if (!proc->plat_detachDone()) {
//Currently used on BG/Q. Its plat_detach is a multi-stage operation,
// and will cause itself to be reinvoked until plat_detachDone is finished
pthrd_printf("Not finishing detach because plat_detachDone reported false on %d\n",
proc->getPid());
return ret_success;
}
if (temporary) {
proc->setState(int_process::detached);
proc->threadPool()->initialThread()->getDetachState().setStateProc(int_thread::detached);
}
else {
ProcPool()->condvar()->lock();
proc->setState(int_process::exited);
ProcPool()->rmProcess(proc);
ProcPool()->condvar()->broadcast();
ProcPool()->condvar()->unlock();
}
err = false;
done:
int_detach_ev->done = true;
proc->getStartupTeardownProcs().dec();
return err ? ret_error : ret_success;
}
void HandleDetach::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Detach));
}
HandleAsync::HandleAsync() :
Handler("Async")
{
}
HandleAsync::~HandleAsync()
{
}
Handler::handler_ret_t HandleAsync::handleEvent(Event::ptr ev)
{
EventAsync::ptr eAsync = ev->getEventAsync();
set<response::ptr> &resps = eAsync->getInternal()->getResponses();
pthrd_printf("Handling %lu async event(s) on %d/%d\n",
(unsigned long) resps.size(),
eAsync->getProcess()->llproc()->getPid(),
eAsync->getThread()->llthrd()->getLWP());
assert(eAsync->getProcess()->llproc()->plat_needsAsyncIO());
for (set<response::ptr>::iterator i = resps.begin(); i != resps.end(); i++) {
response::ptr resp = *i;
resp->markReady();
resp->setEvent(Event::ptr());
}
return ret_success;
}
void HandleAsync::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Async));
}
HandleAsyncIO::HandleAsyncIO() :
Handler("AsyncIO Handler")
{
}
HandleAsyncIO::~HandleAsyncIO()
{
}
Handler::handler_ret_t HandleAsyncIO::handleEvent(Event::ptr ev)
{
pthrd_printf("In AsyncIO Handler for %d\n", ev->getProcess()->getPid());
EventAsyncIO::ptr evio = ev->getEventAsyncIO();
assert(evio);
int_eventAsyncIO *iev = evio->getInternalEvent();
pthrd_printf("Dealing with int_eventAsyncIO %p\n", (void*)iev);
assert(iev);
assert(iev->resp);
(void)iev->resp->isReady();
return ret_success;
}
void HandleAsyncIO::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::AsyncRead));
etypes.push_back(EventType(EventType::None, EventType::AsyncWrite));
etypes.push_back(EventType(EventType::None, EventType::AsyncReadAllRegs));
etypes.push_back(EventType(EventType::None, EventType::AsyncSetAllRegs));
}
HandleAsyncFileRead::HandleAsyncFileRead() :
Handler("HandleAsyncFileRead")
{
}
HandleAsyncFileRead::~HandleAsyncFileRead()
{
}
Handler::handler_ret_t HandleAsyncFileRead::handleEvent(Event::ptr ev)
{
EventAsyncFileRead::ptr fileev = ev->getEventAsyncFileRead();
assert(fileev);
int_eventAsyncFileRead *iev = fileev->getInternal();
int_process *proc = ev->getProcess()->llproc();
if (iev->resp)
delete iev->resp;
if (iev->isComplete())
return ret_success;
//Setup a read on the next part of the file, starting at the offset
// after this read ends.
int_eventAsyncFileRead *new_iev = new int_eventAsyncFileRead();
new_iev->filename = iev->filename;
new_iev->offset = iev->offset + iev->size;
new_iev->whole_file = iev->whole_file;
bool result = proc->getRemoteIO()->plat_getFileDataAsync(new_iev);
if (!result) {
pthrd_printf("Error requesting file data on %d from callback\n", proc->getPid());
return ret_error;
}
return ret_success;
}
void HandleAsyncFileRead::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::AsyncFileRead));
}
HandleNop::HandleNop() :
Handler("Nop Handler")
{
}
HandleNop::~HandleNop()
{
}
Handler::handler_ret_t HandleNop::handleEvent(Event::ptr)
{
//Trivial handler, by definition
return ret_success;
}
void HandleNop::getEventTypesHandled(std::vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::Nop));
}
HandleCallbacks::HandleCallbacks() :
Handler("Callback")
{
}
HandleCallbacks::~HandleCallbacks()
{
}
HandleCallbacks *HandleCallbacks::getCB()
{
static HandleCallbacks *cb = NULL;
if (!cb) {
cb = new HandleCallbacks();
assert(cb);
HandlerPool *hp = createDefaultHandlerPool(NULL);
delete hp;
}
return cb;
}
int HandleCallbacks::getPriority() const
{
return CallbackPriority;
}
void HandleCallbacks::getEventTypesHandled(std::vector<EventType> & /*etypes*/)
{
//Callbacks are special cased, they respond to all event types.
}
bool HandleCallbacks::hasCBs(Event::const_ptr ev)
{
return cbfuncs.find(ev->getEventType()) != cbfuncs.end();
}
bool HandleCallbacks::hasCBs(EventType et)
{
return cbfuncs.find(et) != cbfuncs.end();
}
bool HandleCallbacks::requiresCB(Event::const_ptr ev)
{
return hasCBs(ev) && !ev->suppressCB();
}
Handler::handler_ret_t HandleCallbacks::handleEvent(Event::ptr ev)
{
int_thread *thr = NULL;
if(ev->getThread()) thr = ev->getThread()->llthrd();
int_process *proc = ev->getProcess()->llproc();
if (ev->noted_event) {
//Reset the event status here if the callback already had a delivery attempt
notify()->clearEvent();
if (thr)
thr->getCallbackState().restoreStateProc();
else
proc->threadPool()->initialThread()->getCallbackState().restoreStateProc();
ev->noted_event = false;
}
EventType evtype = ev->getEventType();
std::map<EventType, std::set<Process::cb_func_t>, eventtype_cmp>::iterator i = cbfuncs.find(evtype);
if (i == cbfuncs.end()) {
pthrd_printf("No callback registered for event type '%s'\n", ev->name().c_str());
if (proc->wasForcedTerminated() && thr) {
thr->getUserState().setState(int_thread::running);
}
return ret_success;
}
if (proc &&
(proc->getState() == int_process::neonatal ||
proc->getState() == int_process::neonatal_intermediate))
{
pthrd_printf("No callback for neonatal process %d\n", proc->getPid());
return ret_success;
}
const std::set<Process::cb_func_t> &cbs = i->second;
if (ev->suppressCB()) {
pthrd_printf("Suppressing callbacks for event %s\n", ev->name().c_str());
if (proc->wasForcedTerminated() && thr) {
thr->getUserState().setState(int_thread::running);
}
return ret_success;
}
if (proc->isRunningSilent()) {
pthrd_printf("Suppressing callback for event %s due to process being in silent mode\n",
ev->name().c_str());
if (proc->wasForcedTerminated() && thr) {
thr->getUserState().setState(int_thread::running);
}
return ret_success;
}
return deliverCallback(ev, cbs);
}
static const char *action_str(Process::cb_action_t action)
{
switch (action) {
case Process::cbThreadContinue:
return "cbThreadContinue";
case Process::cbThreadStop:
return "cbThreadStop";
case Process::cbProcContinue:
return "cbProcContinue";
case Process::cbProcStop:
return "cbProcStop";
case Process::cbDefault:
return "cbDefault";
default:
return "cbInvalid";
}
return NULL;
}
bool HandleCallbacks::handleCBReturn(Process::const_ptr proc, Thread::const_ptr thrd,
Process::cb_action_t ret)
{
if (!thrd) {
thrd = proc->llproc()->threadPool()->initialThread()->thread();
}
switch (ret) {
case Process::cbThreadContinue:
if (!thrd->llthrd()->isUser()) break;
if (thrd == Thread::const_ptr()) {
perr_printf("User returned invalid action %s for event\n",
action_str(ret));
return false;
}
pthrd_printf("Callbacks returned thread continue\n");
thrd->llthrd()->getUserState().setState(int_thread::running);
break;
case Process::cbThreadStop:
if (!thrd->llthrd()->isUser()) break;
if (thrd == Thread::const_ptr()) {
perr_printf("User returned invalid action %s for event\n",
action_str(ret));
return false;
}
pthrd_printf("Callbacks returned thread stop\n");
thrd->llthrd()->getUserState().setState(int_thread::stopped);
break;
case Process::cbProcContinue: {
if (proc == Process::const_ptr()) {
perr_printf("User returned invalid action %s for event\n",
action_str(ret));
return false;
}
pthrd_printf("Callbacks returned process continue\n");
thrd->llthrd()->getUserState().setStateProc(int_thread::running);
break;
}
case Process::cbProcStop: {
if (proc == Process::const_ptr()) {
perr_printf("User returned invalid action %s for event\n",
action_str(ret));
return false;
}
pthrd_printf("Callbacks returned process stop\n");
thrd->llthrd()->getUserState().setStateProc(int_thread::stopped);
break;
}
case Process::cbDefault:
pthrd_printf("Callbacks returned default\n");
break;
}
return true;
}
Handler::handler_ret_t HandleCallbacks::deliverCallback(Event::ptr ev, const set<Process::cb_func_t> &cbset)
{
//We want the thread to remain in its appropriate state while the CB is in flight.
int_thread *thr = ev->getThread() ? ev->getThread()->llthrd() : NULL;
int_process *proc = ev->getProcess()->llproc();
assert(proc);
pthrd_printf("Changing callback state of %d before CB\n", proc->getPid());
int_thread::StateTracker &cb_state = thr ? thr->getCallbackState() : proc->threadPool()->initialThread()->getCallbackState();
cb_state.desyncStateProc(int_thread::ditto);
if (isHandlerThread() && mt()->getThreadMode() != Process::CallbackThreading) {
//We're not going to allow a handler thread to deliver a callback in this mode
// trigger the notify, then return a request to delay from the delivery of this
// callback to the user thread.
ev->noted_event = true;
notify()->noteEvent();
return ret_cbdelay;
}
// Make sure the user can operate on the process in the callback
// But if this is a PostCrash or PostExit, the underlying process and
// threads are already gone and thus no operations on them really make sense
if (!ev->getProcess()->isTerminated()) {
ev->getProcess()->llproc()->threadPool()->saveUserState(ev);
}
//The following code loops over each callback registered for this event type
// and triggers the callback for the user. Return results are aggregated.
unsigned k = 0;
pthrd_printf("Triggering callback for event '%s'\n", ev->name().c_str());
Process::cb_action_t parent_result = Process::cbDefault;
Process::cb_action_t child_result = Process::cbDefault;
std::set<Process::cb_func_t>::const_iterator j;
for (j = cbset.begin(); j != cbset.end(); j++, k++) {
pthrd_printf("Triggering callback #%u for event '%s'\n", k, ev->name().c_str());
int_process::setInCB(true);
Process::cb_ret_t ret = (*j)(ev);
int_process::setInCB(false);
if (ret.parent != Process::cbDefault)
parent_result = ret.parent;
if (ret.child != Process::cbDefault)
child_result = ret.child;
pthrd_printf("Callback #%u return %s/%s\n", k, action_str(ret.parent),
action_str(ret.child));
}
// Don't allow the user to change the state of forced terminated processes
if( ev->getProcess()->llproc() && ev->getProcess()->llproc()->wasForcedTerminated() ) {
pthrd_printf("Process is in forced termination, overriding result to cbThreadContinue\n");
parent_result = Process::cbThreadContinue;
child_result = Process::cbDefault;
}
// Now that the callback is over, return the state to what it was before the
// callback so the return value from the callback can be used to update the state
if (!ev->getProcess()->isTerminated()) {
ev->getProcess()->llproc()->threadPool()->restoreUserState();
if (ev->getThread()->llthrd() != NULL) {
//Given the callback return result, change the user state to the appropriate
// setting.
pthrd_printf("Handling return value for main process\n");
handleCBReturn(ev->getProcess(), ev->getThread(), parent_result);
}
}
pthrd_printf("Handling return value for child process/thread\n");
Process::const_ptr child_proc = Process::const_ptr();
Thread::const_ptr child_thread = Thread::const_ptr();
bool event_has_child = false;
switch (ev->getEventType().code()) {
case EventType::Fork:
event_has_child = true;
child_proc = static_cast<EventFork *>(ev.get())->getChildProcess();
break;
case EventType::ThreadCreate:
case EventType::UserThreadCreate:
case EventType::LWPCreate:
event_has_child = true;
child_thread = static_cast<EventNewThread *>(ev.get())->getNewThread();
break;
}
if (event_has_child)
handleCBReturn(child_proc, child_thread, child_result);
pthrd_printf("Restoring callback state of %d/%d after CB\n", ev->getProcess()->getPid(),
ev->getThread()->getLWP());
cb_state.restoreStateProc();
return ret_success;
}
void HandleCallbacks::getRealEvents(EventType ev, std::vector<EventType> &out_evs)
{
switch (ev.code()) {
case EventType::Terminate:
out_evs.push_back(EventType(ev.time(), EventType::Exit));
out_evs.push_back(EventType(ev.time(), EventType::Crash));
out_evs.push_back(EventType(ev.time(), EventType::ForceTerminate));
break;
case EventType::ThreadCreate:
out_evs.push_back(EventType(ev.time(), EventType::UserThreadCreate));
out_evs.push_back(EventType(ev.time(), EventType::LWPCreate));
break;
case EventType::ThreadDestroy:
out_evs.push_back(EventType(ev.time(), EventType::UserThreadDestroy));
out_evs.push_back(EventType(ev.time(), EventType::LWPDestroy));
break;
default:
out_evs.push_back(ev);
}
}
bool HandleCallbacks::registerCallback_int(EventType ev, Process::cb_func_t func)
{
pthrd_printf("Registering event %s with callback function %p\n", ev.name().c_str(), (void*)func);
std::set<EventType, Dyninst::ProcControlAPI::eventtype_cmp>::iterator i = alleventtypes.find(ev);
if (i == alleventtypes.end()) {
pthrd_printf("Event %s does not have any handler\n", ev.name().c_str());
return false;
}
cbfuncs[ev].insert(func);
return true;
}
bool HandleCallbacks::registerCallback(EventType oev, Process::cb_func_t func)
{
bool registered_cb = false;
std::vector<EventType> real_evs;
getRealEvents(oev, real_evs);
for (std::vector<EventType>::iterator i = real_evs.begin(); i != real_evs.end(); i++)
{
EventType ev = *i;
switch (ev.time()) {
case EventType::Pre:
case EventType::Post:
case EventType::None: {
bool result = registerCallback_int(ev, func);
if (result)
registered_cb = true;
break;
}
case EventType::Any: {
bool result1 = registerCallback_int(EventType(EventType::Pre, ev.code()), func);
bool result2 = registerCallback_int(EventType(EventType::Post, ev.code()), func);
bool result3 = registerCallback_int(EventType(EventType::None, ev.code()), func);
if (result1 || result2 || result3)
registered_cb = true;
break;
}
}
}
if (!registered_cb) {
pthrd_printf("Did not register any callbacks for %s\n", oev.name().c_str());
ProcControlAPI::globalSetLastError(err_noevents, "EventType does not exist");
return false;
}
return true;
}
bool HandleCallbacks::removeCallback_int(EventType et, Process::cb_func_t func)
{
cbfuncs_t::iterator i = cbfuncs.find(et);
if (i == cbfuncs.end()) {
return false;
}
set<Process::cb_func_t> &func_set = i->second;
set<Process::cb_func_t>::iterator j = func_set.find(func);
if (j == func_set.end()) {
return false;
}
func_set.erase(j);
return true;
}
bool HandleCallbacks::removeCallback(EventType oet, Process::cb_func_t func)
{
bool removed_cb = false;
std::vector<EventType> real_ets;
getRealEvents(oet, real_ets);
pthrd_printf("Removing event %s callback with function %p\n", oet.name().c_str(), (void*)func);
for (std::vector<EventType>::iterator i = real_ets.begin(); i != real_ets.end(); i++)
{
EventType et = *i;
switch (et.time()) {
case EventType::Pre:
case EventType::Post:
case EventType::None: {
bool result = removeCallback_int(et, func);
if (result)
removed_cb = true;
break;
}
case EventType::Any: {
bool result1 = removeCallback_int(EventType(EventType::Pre, et.code()), func);
bool result2 = removeCallback_int(EventType(EventType::Post,et.code()), func);
bool result3 = removeCallback_int(EventType(EventType::None,et.code()), func);
if (result1 || result2 || result3)
removed_cb = true;
break;
}
}
}
if (!removed_cb) {
perr_printf("Attempted to remove non-existant callback %s\n",
oet.name().c_str());
ProcControlAPI::globalSetLastError(err_badparam, "Callback does not exist");
return false;
}
return true;
}
bool HandleCallbacks::removeCallback_int(EventType et)
{
cbfuncs_t::iterator i = cbfuncs.find(et);
if (i == cbfuncs.end()) {
return false;
}
cbfuncs.erase(i);
return true;
}
bool HandleCallbacks::removeCallback(EventType et)
{
bool result = false;
switch (et.time()) {
case EventType::Pre:
case EventType::Post:
case EventType::None: {
result = removeCallback_int(et);
break;
}
case EventType::Any: {
bool result1 = removeCallback_int(EventType(EventType::Pre, et.code()));
bool result2 = removeCallback_int(EventType(EventType::Post,et.code()));
bool result3 = removeCallback_int(EventType(EventType::None,et.code()));
result = (result1 || result2 || result3);
}
}
if (!result) {
perr_printf("Attempted to remove non-existant callback %s\n",
et.name().c_str());
ProcControlAPI::globalSetLastError(err_badparam, "Callback does not exist");
return false;
}
return true;
}
bool HandleCallbacks::removeCallback(Process::cb_func_t func)
{
bool rmd_something = false;
for (cbfuncs_t::iterator i = cbfuncs.begin(); i != cbfuncs.end(); i++)
{
EventType et = i->first;
bool result = removeCallback_int(et, func);
if (result)
rmd_something = true;
}
if (!rmd_something) {
perr_printf("Attempted to remove non-existant callback %p\n", (void*)func);
ProcControlAPI::globalSetLastError(err_badparam, "Callback does not exist");
return false;
}
return true;
}
HandlePostponedSyscall::HandlePostponedSyscall() :
Handler("Postponed Syscall")
{
}
HandlePostponedSyscall::~HandlePostponedSyscall()
{
}
void HandlePostponedSyscall::getEventTypesHandled(vector<EventType> &etypes)
{
etypes.push_back(EventType(EventType::None, EventType::PostponedSyscall));
}
Handler::handler_ret_t HandlePostponedSyscall::handleEvent(Event::ptr ev)
{
int_thread *thrd = ev->getThread()->llthrd();
thrd->getPostponedSyscallState().desyncState(int_thread::running);
return ret_success;
}
HandlerPool *createDefaultHandlerPool(int_process *p)
{
static bool initialized = false;
static HandleBootstrap *hbootstrap = NULL;
static HandleSignal *hsignal = NULL;
static HandlePostExit *hpostexit = NULL;
static HandlePreExit *hpreexit = NULL;
static HandlePostExitCleanup *hpostexitcleanup = NULL;
static HandleThreadCreate *hthreadcreate = NULL;
static HandleThreadDestroy *hthreaddestroy = NULL;
static HandleThreadCleanup *hthreadcleanup = NULL;
static HandleThreadStop *hthreadstop = NULL;
static HandleSingleStep *hsinglestep = NULL;
static HandlePreSyscall *hpresyscall = NULL;
static HandlePostSyscall *hpostsyscall = NULL;
static HandleCrash *hcrash = NULL;
static HandleBreakpoint *hbpoint = NULL;
static HandleBreakpointContinue *hbpcontinue = NULL;
static HandleBreakpointClear *hbpclear = NULL;
static HandleBreakpointRestore *hbprestore = NULL;
static HandleLibrary *hlibrary = NULL;
static HandlePostFork *hpostfork = NULL;
static HandlePostForkCont *hpostforkcont = NULL;
static HandlePostExec *hpostexec = NULL;
static HandleAsync *hasync = NULL;
static HandleAsyncIO *hasyncio = NULL;
static HandleForceTerminate *hforceterm = NULL;
static HandleNop *hnop = NULL;
static HandleDetach *hdetach = NULL;
static HandleEmulatedSingleStep *hemulatedsinglestep = NULL;
static iRPCHandler *hrpc = NULL;
static iRPCPreCallbackHandler *hprerpc = NULL;
static HandlePreBootstrap* hprebootstrap = NULL;
static iRPCLaunchHandler *hrpclaunch = NULL;
static HandleAsyncFileRead *hasyncfileread = NULL;
static HandlePostponedSyscall *hppsyscall = NULL;
if (!initialized) {
hbootstrap = new HandleBootstrap();
hsignal = new HandleSignal();
hpostexit = new HandlePostExit();
hpostexitcleanup = new HandlePostExitCleanup();
hpreexit = new HandlePreExit();
hthreadcreate = new HandleThreadCreate();
hthreaddestroy = new HandleThreadDestroy();
hthreadcleanup = new HandleThreadCleanup();
hthreadstop = new HandleThreadStop();
hsinglestep = new HandleSingleStep();
hpresyscall = new HandlePreSyscall();
hpostsyscall = new HandlePostSyscall();
hcrash = new HandleCrash();
hbpoint = new HandleBreakpoint();
hbpcontinue = new HandleBreakpointContinue();
hbpclear = new HandleBreakpointClear();
hbprestore = new HandleBreakpointRestore();
hrpc = new iRPCHandler();
hprerpc = new iRPCPreCallbackHandler();
hrpclaunch = new iRPCLaunchHandler();
hlibrary = new HandleLibrary();
hpostfork = new HandlePostFork();
hpostforkcont = new HandlePostForkCont();
hpostexec = new HandlePostExec();
hasync = new HandleAsync();
hasyncio = new HandleAsyncIO();
hforceterm = new HandleForceTerminate();
hprebootstrap = new HandlePreBootstrap();
hnop = new HandleNop();
hdetach = new HandleDetach();
hemulatedsinglestep = new HandleEmulatedSingleStep();
hasyncfileread = new HandleAsyncFileRead();
hppsyscall = new HandlePostponedSyscall();
initialized = true;
}
HandlerPool *hpool = new HandlerPool(p);
hpool->addHandler(hbootstrap);
hpool->addHandler(hsignal);
hpool->addHandler(hpostexit);
hpool->addHandler(hpostexitcleanup);
hpool->addHandler(hpreexit);
hpool->addHandler(hthreadcreate);
hpool->addHandler(hthreaddestroy);
hpool->addHandler(hthreadcleanup);
hpool->addHandler(hthreadstop);
hpool->addHandler(hsinglestep);
hpool->addHandler(hpresyscall);
hpool->addHandler(hpostsyscall);
hpool->addHandler(hcrash);
hpool->addHandler(hbpoint);
hpool->addHandler(hbpcontinue);
hpool->addHandler(hbpclear);
hpool->addHandler(hbprestore);
hpool->addHandler(hrpc);
hpool->addHandler(hprerpc);
hpool->addHandler(hlibrary);
hpool->addHandler(hpostfork);
hpool->addHandler(hpostforkcont);
hpool->addHandler(hpostexec);
hpool->addHandler(hrpclaunch);
hpool->addHandler(hasync);
hpool->addHandler(hasyncio);
hpool->addHandler(hforceterm);
hpool->addHandler(hprebootstrap);
hpool->addHandler(hnop);
hpool->addHandler(hdetach);
hpool->addHandler(hemulatedsinglestep);
hpool->addHandler(hasyncfileread);
hpool->addHandler(hppsyscall);
plat_createDefaultHandlerPool(hpool);
print_add_handler = false;
return hpool;
}