From 1f4a341889938204e61e1bc16cbe4c8df8ddbfe6 Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 19 Sep 2002 01:21:37 +0000 Subject: [PATCH] Add BPatch_thread::oneTimeCodeAsync and other changes to support it. --- dyninstAPI/h/BPatch.h | 5 ++ dyninstAPI/h/BPatch_thread.h | 16 +++--- dyninstAPI/src/BPatch.C | 76 ++++++++++++++++++++++++-- dyninstAPI/src/BPatch_thread.C | 121 ++++++++++++++++++++++++++--------------- dyninstAPI/tests/src/test1.C | 29 +++++++++- 5 files changed, 188 insertions(+), 59 deletions(-) diff --git a/dyninstAPI/h/BPatch.h b/dyninstAPI/h/BPatch.h index d5aca60..a321908 100644 --- a/dyninstAPI/h/BPatch.h +++ b/dyninstAPI/h/BPatch.h @@ -76,6 +76,7 @@ typedef void (*BPatchExitCallback)(BPatch_thread *proc, int code); typedef void (*BPatchSignalCallback)(BPatch_thread *proc, int sigNum); +typedef void (*BPatchOneTimeCodeCallback)(BPatch_thread *proc, void *userData, void *returnValue); #ifdef IBM_BPATCH_COMPAT typedef void *BPatch_Address; @@ -108,12 +109,14 @@ class BPATCH_DLL_EXPORT BPatch { BPatchForkCallback preForkCallback; BPatchExecCallback execCallback; BPatchExitCallback exitCallback; + BPatchOneTimeCodeCallback oneTimeCodeCallback; bool typeCheckOn; int lastError; bool debugParseOn; bool baseTrampDeletionOn; bool getThreadEvent(bool block); + bool getThreadEventOnly(bool block); bool havePendingEvent(); /* If true, trampolines can recurse to their heart's content. @@ -148,6 +151,7 @@ public: void registerExit(BPatch_thread *thread, int code); void registerThread(BPatch_thread *thread); void unRegisterThread(int pid); + void launchDeferredOneTimeCode(); BPatch_thread *getThreadByPid(int pid, bool *exists = NULL); @@ -182,6 +186,7 @@ public: BPatchForkCallback registerPreForkCallback(BPatchForkCallback func); BPatchExecCallback registerExecCallback(BPatchExecCallback func); BPatchExitCallback registerExitCallback(BPatchExitCallback func); + BPatchOneTimeCodeCallback registerOneTimeCodeCallback(BPatchOneTimeCodeCallback func); BPatch_Vector *getThreads(); diff --git a/dyninstAPI/h/BPatch_thread.h b/dyninstAPI/h/BPatch_thread.h index db2091a..13e6063 100644 --- a/dyninstAPI/h/BPatch_thread.h +++ b/dyninstAPI/h/BPatch_thread.h @@ -114,9 +114,6 @@ class BPATCH_DLL_EXPORT BPatch_thread { bool createdViaAttach; bool detached; - bool waitingForOneTimeCode; - void *lastOneTimeCodeReturnValue; - bool unreportedStop; bool unreportedTermination; @@ -137,10 +134,9 @@ class BPATCH_DLL_EXPORT BPatch_thread { void *userData, void *returnValue); - void oneTimeCodeCallback(void *userData, - void *returnValue); - - void *oneTimeCodeInternal(const BPatch_snippet &expr); + void *oneTimeCodeInternal(const BPatch_snippet &expr, + void *userData, + bool synchronous); protected: // for creating a process @@ -216,7 +212,11 @@ public: BPatch_function &newFunc); void oneTimeCode(const BPatch_snippet &expr) { - oneTimeCodeInternal(expr); + oneTimeCodeInternal(expr, NULL, true); + }; + + void oneTimeCodeAsync(const BPatch_snippet &expr, void *userData = NULL) { + oneTimeCodeInternal(expr, userData, false); }; //the reload argument is used by save the world to determine diff --git a/dyninstAPI/src/BPatch.C b/dyninstAPI/src/BPatch.C index 5a9eb44..b3c74d5 100644 --- a/dyninstAPI/src/BPatch.C +++ b/dyninstAPI/src/BPatch.C @@ -39,7 +39,7 @@ * incur to third parties resulting from your use of Paradyn. */ -// $Id: BPatch.C,v 1.47 2002/06/26 21:14:35 schendel Exp $ +// $Id: BPatch.C,v 1.48 2002/09/19 01:21:37 buck Exp $ #include #include @@ -271,6 +271,7 @@ BPatch::BPatch() dynLibraryCallback = NULL; execCallback = NULL; exitCallback = NULL; + oneTimeCodeCallback = NULL; #ifdef DETACH_ON_THE_FLY // Register handler for notification from detached inferiors @@ -430,6 +431,24 @@ BPatchExitCallback BPatch::registerExitCallback(BPatchExitCallback func) #endif } +/* + * BPatch::registerOneTimeCodeCallback + * + * Registers a function that is to be called by the library when a + * oneTimeCode (inferior RPC) is completed. + * + * func The function to be called. + */ +BPatchOneTimeCodeCallback BPatch::registerOneTimeCodeCallback(BPatchOneTimeCodeCallback func) +{ + BPatchOneTimeCodeCallback ret; + + ret = oneTimeCodeCallback; + oneTimeCodeCallback = func; + + return ret; +} + #ifdef IBM_BPATCH_COMPAT BPatchExitCallback BPatch::registerExitCallback(BPatchThreadEventCallback func) { @@ -785,14 +804,16 @@ BPatch_thread *BPatch::attachProcess(char *path, int pid) return ret; } - /* - * getThreadEvent + * BPatch::getThreadEvent * * Checks for changes in any child process, and optionally blocks until such a - * change has occurred. Also updates the process object representing each - * process for which a change is detected. The return value is true if a - * change was detected, otherwise it is false. + * change has occurred. Also performs housekeeping on behalf of Dyninst: + * - Launches pending oneTimeCode calls (aka inferior RPCs) + * - Updates the process object representing each process for which a + * change is detected. + * + * The return value is true if a change was detected, otherwise it is false. * * block Set this parameter to true to block waiting for a change, * set to false to poll and return immediately, whether or not a @@ -800,6 +821,28 @@ BPatch_thread *BPatch::attachProcess(char *path, int pid) */ bool BPatch::getThreadEvent(bool block) { + launchDeferredOneTimeCode(); + + return getThreadEventOnly(block); +} + +/* + * BPatch::getThreadEventOnly + * + * Like getThreadEvent (which actually calls this function), this function + * checks for changes in any child process, optionally blocking until such a + * change occurs. It also updates the process objects with information about + * such changes. Unlike getThreadEvent, it does not perform any additional + * housekeeping like launching deferred RPCs. + * + * Returns true if a change was detected, otherwise returns false. + * + * block Set this parameter to true to block waiting for a change, + * set to false to poll and return immediately, whether or not a + * change occurred. + */ +bool BPatch::getThreadEventOnly(bool block) +{ bool result = false; int pid, status; @@ -1232,3 +1275,24 @@ void setLogging_NP(BPatchLoggingCallback, int) } #endif + +/* + * BPatch::launchDeferredOneTimeCode + * + * Launch any deferred oneTimeCode calls (aka inferior RPCs) that might exist, + * if it is now okay to do so. + */ +void BPatch::launchDeferredOneTimeCode() +{ + for (unsigned int p = 0; p < processVec.size(); p++) { + process *proc = processVec[p]; + + if (proc == NULL) + continue; + + if (proc->status() == exited || proc->status() == neonatal) + continue; + + proc->launchRPCifAppropriate(proc->status() == running, false); + } +} diff --git a/dyninstAPI/src/BPatch_thread.C b/dyninstAPI/src/BPatch_thread.C index acf7a30..5fbe8ea 100644 --- a/dyninstAPI/src/BPatch_thread.C +++ b/dyninstAPI/src/BPatch_thread.C @@ -39,7 +39,7 @@ * incur to third parties resulting from your use of Paradyn. */ -// $Id: BPatch_thread.C,v 1.61 2002/08/29 00:33:27 rchen Exp $ +// $Id: BPatch_thread.C,v 1.62 2002/09/19 01:21:37 buck Exp $ #ifdef sparc_sun_solaris2_4 #include @@ -56,6 +56,32 @@ #include "BPatch_thread.h" #include "LineInformation.h" +/* + * class OneTimeCodeInfo + * + * This is used by the oneTimeCode (inferiorRPC) mechanism to keep per-RPC + * information. + */ +class OneTimeCodeInfo { + bool synchronous; + bool completed; + void *userData; + void *returnValue; +public: + OneTimeCodeInfo(bool _synchronous, void *_userData) : + synchronous(_synchronous), completed(false), userData(_userData) { }; + + bool isSynchronous() { return synchronous; } + + bool isCompleted() const { return completed; } + void setCompleted(bool _completed) { completed = _completed; } + + void *getUserData() { return userData; } + + void setReturnValue(void *_returnValue) { returnValue = _returnValue; } + void *getReturnValue() { return returnValue; } +}; + /* * BPatch_thread::getPid @@ -114,7 +140,7 @@ static void insertVForkInst(BPatch_thread *thread) BPatch_thread::BPatch_thread(char *path, char *argv[], char *envp[], int stdin_fd, int stdout_fd, int stderr_fd) : proc(NULL), image(NULL), lastSignal(-1), exitCode(-1), mutationsActive(true), - createdViaAttach(false), detached(false), waitingForOneTimeCode(false), + createdViaAttach(false), detached(false), unreportedStop(false), unreportedTermination(false) { vector argv_vec; @@ -186,7 +212,7 @@ BPatch_thread::BPatch_thread(char *path, char *argv[], char *envp[], */ BPatch_thread::BPatch_thread(char *path, int pid) : proc(NULL), image(NULL), lastSignal(-1), exitCode(-1), mutationsActive(true), - createdViaAttach(true), detached(false), waitingForOneTimeCode(false), + createdViaAttach(true), detached(false), unreportedStop(false), unreportedTermination(false) { @@ -210,7 +236,7 @@ BPatch_thread::BPatch_thread(char *path, int pid) image = new BPatch_image(proc); while (!proc->isBootstrappedYet() && !statusIsTerminated()) { - BPatch::bpatch->getThreadEvent(false); + BPatch::bpatch->getThreadEventOnly(false); proc->launchRPCifAppropriate(false, false); } @@ -232,7 +258,7 @@ BPatch_thread::BPatch_thread(char *path, int pid) */ BPatch_thread::BPatch_thread(int /*pid*/, process *nProc) : proc(nProc), image(NULL), lastSignal(-1), exitCode(-1), mutationsActive(true), - createdViaAttach(true), detached(false), waitingForOneTimeCode(false), + createdViaAttach(true), detached(false), unreportedStop(false), unreportedTermination(false) { proc->thread = this; @@ -1120,12 +1146,23 @@ void BPatch_thread::oneTimeCodeCallbackDispatch(process *theProc, { assert(BPatch::bpatch != NULL); + OneTimeCodeInfo *info = (OneTimeCodeInfo *)userData; + BPatch_thread *theThread = BPatch::bpatch->getThreadByPid(theProc->getPid()); assert(theThread != NULL); + assert(info && !info->isCompleted()); + + info->setReturnValue(returnValue); + info->setCompleted(true); - theThread->oneTimeCodeCallback(userData, returnValue); + if (!info->isSynchronous()) { + if (BPatch::bpatch->oneTimeCodeCallback) + BPatch::bpatch->oneTimeCodeCallback(theThread, info->getUserData(), returnValue); + + delete info; + } #ifdef IBM_BPATCH_COMPAT if (BPatch::bpatch->RPCdoneCallback) { @@ -1137,37 +1174,27 @@ void BPatch_thread::oneTimeCodeCallbackDispatch(process *theProc, /* - * BPatch_thread::oneTimeCodeCallback - * - * This function is called by BPatch_thread:::oneTimeCodeCallbackDispatch - * when in inferior RPC completes. - * - * userData This is a value that can be set when we invoke an inferior RPC - * and which will be returned to us in this callback. - * returnValue The value returned by the RPC. - */ -void BPatch_thread::oneTimeCodeCallback(void * /*userData*/, void *returnValue) -{ - assert(waitingForOneTimeCode); - - lastOneTimeCodeReturnValue = returnValue; - waitingForOneTimeCode = false; -} - - -/* - * BPatch_thread::oneTimeCode + * BPatch_thread::oneTimeCodeInternal * * Causes a snippet expression to be evaluated once in the mutatee at the next - * available opportunity. - * - * snippet The snippet to evaluate. + * available opportunity. Optionally, Dyninst will call a callback function + * when the snippet has executed in the mutatee, and can wait until the + * snippet has executed to return. + * + * expr The snippet to evaluate. + * userData This value is given to the callback function along with the + * return value for the snippet. Can be used by the caller to + * store per-oneTimeCode information. + * synchronous True means wait until the snippet has executed, false means + * return immediately. */ -void *BPatch_thread::oneTimeCodeInternal(const BPatch_snippet &expr) +void *BPatch_thread::oneTimeCodeInternal(const BPatch_snippet &expr, + void *userData, + bool synchronous) { bool needToResume = false; - if (!statusIsStopped()) { + if (synchronous && !statusIsStopped()) { stopExecution(); if (!statusIsStopped()) { return NULL; @@ -1175,27 +1202,35 @@ void *BPatch_thread::oneTimeCodeInternal(const BPatch_snippet &expr) needToResume = true; } + OneTimeCodeInfo *info = new OneTimeCodeInfo(synchronous, userData); + proc->postRPCtoDo(expr.ast, false, // XXX = calculate cost - is this what we want? BPatch_thread::oneTimeCodeCallbackDispatch, // Callback - NULL, // User data + (void *)info, // User data -1, // This isn't a metric definition - we shouldn't NULL, // No particular thread (yet), 0, // Same -- no kernel thread false); - waitingForOneTimeCode = true; - - do { - proc->launchRPCifAppropriate(false, false); - BPatch::bpatch->getThreadEvent(false); - } while (waitingForOneTimeCode && !statusIsTerminated()); + if (synchronous) { + do { + proc->launchRPCifAppropriate(false, false); + BPatch::bpatch->getThreadEvent(false); + } while (!info->isCompleted() && !statusIsTerminated()); - if (needToResume) { - continueExecution(); - } + void *ret = info->getReturnValue(); + delete info; + + if (needToResume) + continueExecution(); - return lastOneTimeCodeReturnValue; + return ret; + } else { + proc->launchRPCifAppropriate(proc->status() == running, false); + + return NULL; + } } @@ -1228,7 +1263,7 @@ bool BPatch_thread::loadLibrary(char *libname, bool reload) BPatch_funcCallExpr call_dlopen(*dlopen_func, args); - if (!oneTimeCodeInternal(call_dlopen)) { + if (!oneTimeCodeInternal(call_dlopen, NULL, true)) { // dlopen FAILED // find the (global var) error string in the RT Lib and send it to the // error reporting mechanism diff --git a/dyninstAPI/tests/src/test1.C b/dyninstAPI/tests/src/test1.C index 88f42f0..9f87384 100644 --- a/dyninstAPI/tests/src/test1.C +++ b/dyninstAPI/tests/src/test1.C @@ -1648,6 +1648,13 @@ void mutatorTest18(BPatch_thread *appThread, BPatch_image *appImage) expr18_1->writeValue(&n); } +void test19_oneTimeCodeCallback(BPatch_thread *thread, + void *userData, + void *returnValue) +{ + *(int *)userData = 1; +} + // // Start Test Case #19 - mutator side (oneTimeCode) // @@ -1669,8 +1676,11 @@ void mutatorTest19(BPatch_thread *appThread, BPatch_image *appImage) appThread->oneTimeCode(call19_1Expr); + // Let the mutatee run to check the result appThread->continueExecution(); - P_sleep(1); /* wait for child to continue */ + + // Wait for the next test + waitUntilStopped(bpatch, appThread, 19, "oneTimeCode"); BPatch_function *call19_2_func; call19_2_func = appImage->findFunction("call19_2"); @@ -1683,7 +1693,22 @@ void mutatorTest19(BPatch_thread *appThread, BPatch_image *appImage) BPatch_funcCallExpr call19_2Expr(*call19_2_func, nullArgs); checkCost(call19_2Expr); - appThread->oneTimeCode(call19_2Expr); + int callbackFlag = 0; + + // Register a callback that will set the flag callbackFlag + BPatchOneTimeCodeCallback oldCallback = + bpatch->registerOneTimeCodeCallback(test19_oneTimeCodeCallback); + + appThread->oneTimeCodeAsync(call19_2Expr, (void *)&callbackFlag); + + // Wait for the callback to be called + while (!appThread->isTerminated() && !callbackFlag) ; + + // Restore old callback (if there was one) + bpatch->registerOneTimeCodeCallback(oldCallback); + + // Let the mutatee run to check the result and then go on to the next test + appThread->continueExecution(); } // -- 1.8.3.1