You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
9.1 KiB
262 lines
9.1 KiB
/*
|
|
* Copyright 2002-2019 Intel Corporation.
|
|
*
|
|
* This software is provided to you as Sample Source Code as defined in the accompanying
|
|
* End User License Agreement for the Intel(R) Software Development Products ("Agreement")
|
|
* section 1.L.
|
|
*
|
|
* This software and the related documents are provided as is, with no express or implied
|
|
* warranties, other than those that are expressly stated in the License.
|
|
*/
|
|
|
|
/*
|
|
* This tool should instrument the thread-{unix,windows} application, and should test
|
|
* the PIN_IsThreadStoppedInDebugger() functionality using these steps:
|
|
* 1. All started threads call GlobalFunction() which is instrumented by this tool.
|
|
* 2. We order all threads according to an arbitrary order.
|
|
* 3. We active each thread one by one according to the order from step 2 and in each thread we do:
|
|
* a. Check that all the threads that were activated before are stopped in the debugger
|
|
* (using PIN_IsThreadStoppedInDebugger()).
|
|
* b. Check that all the threads that weren't activated yet aren't stopped in the debugger
|
|
* (using PIN_IsThreadStoppedInDebugger()).
|
|
* c. Stop the activated thread in the debugger (by PIN_ApplicationBreakpoint()) and wake
|
|
* up next thread in the order
|
|
*/
|
|
#include <pin.H>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <time.h>
|
|
|
|
#ifdef TARGET_WINDOWS
|
|
namespace WIND {
|
|
# include <Windows.h>
|
|
}
|
|
# define YIELD WIND::SwitchToThread
|
|
#else
|
|
# include <sched.h>
|
|
# define YIELD sched_yield
|
|
#endif
|
|
|
|
KNOB<std::string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
|
|
"o", "check-if-thread-stopped.out", "Output file");
|
|
KNOB<unsigned> KnobThreads(KNOB_MODE_WRITEONCE, "pintool",
|
|
"threads", "4", "Number of threads");
|
|
|
|
// This semaphore is set after all the application threads reached
|
|
// the same (predefined) execution point
|
|
PIN_SEMAPHORE SemAllThreadStarted;
|
|
|
|
// The thread IDs which were created and instrumented in the application
|
|
// The order of the threads in this vector is the order in which the
|
|
// threads will operate
|
|
std::vector<THREADID> VecThreadIds;
|
|
|
|
// This mutex protects the insertion to VecThreadIds from multiple threads
|
|
PIN_MUTEX MtxVecThreadIds;
|
|
|
|
// The index (into VecThreadIds) of the where the thread ID of the current
|
|
// active thread
|
|
volatile unsigned ActiveThreadIndex = 0;
|
|
|
|
// This lock acquired by the active thread and released when the active
|
|
// thread is finished
|
|
PIN_MUTEX MtxActiveThread;
|
|
|
|
// Output file string for this tool's log file
|
|
std::ofstream Out;
|
|
|
|
// This variable assumes the value 'true' when all threads were started
|
|
volatile bool AllThreadsWereStarted = false;
|
|
|
|
/* =====================================================================
|
|
* Print the content of VecThreadIds with the chosen order
|
|
* ===================================================================== */
|
|
void PrintAllThreadIDs()
|
|
{
|
|
Out << "All started threads IDs [" << std::dec;
|
|
for (size_t i = 0; i < VecThreadIds.size(); i++)
|
|
{
|
|
if (i > 0)
|
|
Out << ", ";
|
|
|
|
Out << VecThreadIds[i];
|
|
}
|
|
Out << "]" << std::endl;
|
|
}
|
|
|
|
/* =====================================================================
|
|
* Called on DoBreakpoint() for each thread.
|
|
* Waits for all the threads to reach this point, filling the vector of
|
|
* the created thread IDs, and then release all the threads.
|
|
* ===================================================================== */
|
|
static void WaitForAllThreadsToStart(THREADID tid)
|
|
{
|
|
PIN_MutexLock(&MtxVecThreadIds);
|
|
VecThreadIds.push_back(tid);
|
|
if (VecThreadIds.size() >= KnobThreads.Value())
|
|
{
|
|
PrintAllThreadIDs();
|
|
AllThreadsWereStarted = true;
|
|
PIN_SemaphoreSet(&SemAllThreadStarted);
|
|
}
|
|
PIN_MutexUnlock(&MtxVecThreadIds);
|
|
PIN_SemaphoreWait(&SemAllThreadStarted);
|
|
}
|
|
|
|
/* =====================================================================
|
|
* Wait for the turn of the thread denoted by 'tid' to do its checking
|
|
* ===================================================================== */
|
|
void WaitForThisThreadToBeActive(THREADID tid)
|
|
{
|
|
// Waits for the turn of this thread
|
|
while (VecThreadIds[ActiveThreadIndex] != tid)
|
|
{
|
|
PIN_MutexUnlock(&MtxActiveThread);
|
|
YIELD();
|
|
PIN_MutexLock(&MtxActiveThread);
|
|
}
|
|
|
|
// At this point it's the turn of the current thread to do the checking
|
|
// the previous thread release the mutex MtxActiveThread.
|
|
// We still need to wait (for at most 10 seconds) for the previous thread
|
|
// to stop at the debugger
|
|
time_t startWaitingTime = time(NULL);
|
|
if (ActiveThreadIndex > 0)
|
|
{
|
|
THREADID prevTid = VecThreadIds[ActiveThreadIndex - 1];
|
|
while (!PIN_IsThreadStoppedInDebugger(prevTid))
|
|
{
|
|
ASSERT((time(NULL) - startWaitingTime) < 10, "Timeout waiting for thread " + hexstr(prevTid) + " to stop in debugger");
|
|
YIELD();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* =====================================================================
|
|
* Called on each thread when it calls GlobalFunction()
|
|
* ===================================================================== */
|
|
static void DoBreakpoint(THREADID tid, CONTEXT *context)
|
|
{
|
|
// If all thread were started then this is the second time we're entering
|
|
// this function for the thread 'tid'.
|
|
// We enter this function twice for the same thread because we hit the same
|
|
// instrumentation we the debugger was returned from the break-point that
|
|
// we mock to the same address. In this case we need to return and don't
|
|
// repeat the test (unless we'll end up with an infinite test).
|
|
if (AllThreadsWereStarted)
|
|
return;
|
|
|
|
WaitForAllThreadsToStart(tid);
|
|
// All threads are synchronized to this point
|
|
|
|
PIN_MutexLock(&MtxActiveThread);
|
|
|
|
WaitForThisThreadToBeActive(tid);
|
|
|
|
Out << "Performing check on thread ID " << tid << std::endl;
|
|
|
|
for (unsigned i = 0; i < KnobThreads.Value(); i++)
|
|
{
|
|
THREADID otherTid = VecThreadIds[i];
|
|
if (i < ActiveThreadIndex)
|
|
{
|
|
// All threads that were already active should be stopped in the debugger
|
|
ASSERT(PIN_IsThreadStoppedInDebugger(otherTid), "Thread " + decstr(otherTid) + " should be stopped");
|
|
}
|
|
else
|
|
{
|
|
// All threads that weren't already active should be running
|
|
ASSERT(!PIN_IsThreadStoppedInDebugger(otherTid), "Thread " + decstr(otherTid) + " shouldn't be stopped");
|
|
}
|
|
}
|
|
|
|
ActiveThreadIndex++;
|
|
PIN_MutexUnlock(&MtxActiveThread);
|
|
|
|
// Stop this thread in the debugger
|
|
// Note that because we add the instrumentation at IPOINT_AFTER, the program counter
|
|
// at 'context' will not lead us to run this instrumentation function again
|
|
PIN_ApplicationBreakpoint(context, tid, FALSE, "Stopping in Worker Thread " + decstr(tid) + "\n");
|
|
}
|
|
|
|
/* =====================================================================
|
|
* Called upon image load to instrument the function GlobalFunction
|
|
* ===================================================================== */
|
|
static void Image(IMG img, VOID *)
|
|
{
|
|
if (IMG_IsMainExecutable(img))
|
|
{
|
|
RTN rtn = RTN_FindByName(img, "GlobalFunction");
|
|
ASSERT(RTN_Valid(rtn), "Failed to find GlobalFunction() in main application image");
|
|
|
|
RTN_Open(rtn);
|
|
INS ins = RTN_InsHeadOnly(rtn);
|
|
INS_InsertCall(ins, IPOINT_BEFORE, AFUNPTR(DoBreakpoint),
|
|
IARG_THREAD_ID, IARG_CONTEXT, IARG_END);
|
|
RTN_Close(rtn);
|
|
}
|
|
}
|
|
|
|
/* =====================================================================
|
|
* Called upon program finish
|
|
* ===================================================================== */
|
|
static void OnExit(INT32, VOID *)
|
|
{
|
|
PIN_SemaphoreFini(&SemAllThreadStarted);
|
|
PIN_MutexFini(&MtxVecThreadIds);
|
|
PIN_MutexFini(&MtxActiveThread);
|
|
|
|
ASSERT(!PIN_IsThreadStoppedInDebugger(VecThreadIds[0]),
|
|
"Terminated thread with TID " + decstr(VecThreadIds[0]) + " shouldn't be appeared as stopped");
|
|
|
|
Out << "Finished" << std::endl;
|
|
Out.close();
|
|
}
|
|
|
|
/* =====================================================================
|
|
* Called upon bad command line argument
|
|
* ===================================================================== */
|
|
INT32 Usage()
|
|
{
|
|
std::cerr <<
|
|
"This pin tool tests the functionality of PIN_IsThreadStoppedInDebugger()" << std::endl;
|
|
|
|
std::cerr << KNOB_BASE::StringKnobSummary();
|
|
|
|
std::cerr << std::endl;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* =====================================================================
|
|
* Entry point for the tool
|
|
* ===================================================================== */
|
|
int main(int argc, char * argv[])
|
|
{
|
|
if (PIN_Init(argc, argv))
|
|
{
|
|
return Usage();
|
|
}
|
|
PIN_InitSymbols();
|
|
|
|
PIN_SemaphoreInit(&SemAllThreadStarted);
|
|
PIN_MutexInit(&MtxVecThreadIds);
|
|
PIN_MutexInit(&MtxActiveThread);
|
|
|
|
Out.open(KnobOutputFile.Value().c_str());
|
|
ASSERT(Out, "Failed to open file " + KnobOutputFile.Value());
|
|
|
|
IMG_AddInstrumentFunction(Image, 0);
|
|
|
|
PIN_AddFiniFunction(OnExit, 0);
|
|
|
|
// Never returns
|
|
PIN_StartProgram();
|
|
return 0;
|
|
}
|
|
/* ===================================================================== */
|
|
/* eof */
|
|
/* ===================================================================== */
|