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.
907 lines
26 KiB
907 lines
26 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.
|
|
*/
|
|
|
|
/*! @file
|
|
* A tool that creates private threads and verifies that PIN APIs,
|
|
* like PIN_SafeCopy, TLS APIs, etc. work correctly in tool's threads.
|
|
*/
|
|
|
|
#include <os-apis.h>
|
|
#include <unistd.h>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <set>
|
|
#include "pin.H"
|
|
using std::set;
|
|
using std::string;
|
|
using std::hex;
|
|
using std::ostringstream;
|
|
using std::cerr;
|
|
using std::flush;
|
|
using std::ofstream;
|
|
using std::dec;
|
|
using std::endl;
|
|
|
|
|
|
/* ===================================================================== */
|
|
/* Commandline Switches */
|
|
/* ===================================================================== */
|
|
|
|
KNOB<BOOL> KnobSafeStart(KNOB_MODE_WRITEONCE, "pintool",
|
|
"safestart", "0", "Ensure all internal threads are started prior to application exit");
|
|
static KNOB<string> KnobOutput(KNOB_MODE_WRITEONCE, "pintool", "o", "mt_tool.out", "output file");
|
|
|
|
|
|
/*!
|
|
* Global variables.
|
|
*/
|
|
|
|
PIN_LOCK pinLock; // lock that serializes access to global vars
|
|
|
|
static ofstream out;
|
|
|
|
// Thread counters
|
|
int appThreadsStarted = 0;
|
|
int appThreadsFinished = 0;
|
|
int toolThreadsCreated = 0;
|
|
int toolThreadsFinished = 0;
|
|
volatile int toolThreadsStarted = 0;
|
|
|
|
// TLS slot for passing data to exception handlers.
|
|
TLS_KEY exceptionTlsKey;
|
|
|
|
// Argument of the main internal thread
|
|
void * rootThreadArg = (void *)0xABBA;
|
|
|
|
// Address of the DoFlush function in the application's image
|
|
volatile ADDRINT addrDoFlush = 0;
|
|
|
|
// Flags (events) that synchronize an application thread that executes DoFlush
|
|
// and tool's threads that test RTN API and CodeCache API
|
|
volatile BOOL isDoFlushCalled = FALSE;
|
|
volatile BOOL isRtnTestCompleted = FALSE;
|
|
volatile BOOL isCacheTestCompleted = FALSE;
|
|
|
|
volatile BOOL isCacheFlushHappened = FALSE;
|
|
|
|
// Set in PrepareForFini to signal that process is about to finish.
|
|
volatile BOOL isProcessExiting = FALSE;
|
|
|
|
// IDs of internal threads
|
|
typedef enum {
|
|
idRootThread = 0,
|
|
idThreadSafeCopy,
|
|
idThreadException,
|
|
idThreadRtn,
|
|
idThreadCodeCache,
|
|
idDummyThread,
|
|
numThreadIds
|
|
} ID_TOOL_THREAD;
|
|
|
|
// Names of internal threads
|
|
const CHAR * threadNameStr[] = {
|
|
"RootThread",
|
|
"ThreadSafeCopy",
|
|
"ThreadException",
|
|
"ThreadRtn",
|
|
"ThreadCodeCache",
|
|
"DummyThread"
|
|
};
|
|
|
|
// Internal thread status (for debugging purposes)
|
|
typedef enum {
|
|
statusInit = 0,
|
|
statusCreated,
|
|
statusRun,
|
|
statusTerminated,
|
|
} THREAD_STATUS;
|
|
|
|
THREAD_STATUS threadStatus[numThreadIds];
|
|
PIN_THREAD_UID threadUids[numThreadIds];
|
|
|
|
// Wait timeout in milliseconds used in PIN_SpawnInternalThread().
|
|
const UINT32 waitTimeout = 30000;
|
|
|
|
//==========================================================================
|
|
// Forward declarations
|
|
//==========================================================================
|
|
static void NotifyToolThreadExit(ID_TOOL_THREAD idToolThread);
|
|
|
|
//==========================================================================
|
|
// Utilities
|
|
//==========================================================================
|
|
|
|
/*!
|
|
* Convert a (function) pointer to ADDRINT.
|
|
*/
|
|
template <typename PF> ADDRINT Ptr2Addrint(PF pf)
|
|
{
|
|
union CAST
|
|
{
|
|
PF pf;
|
|
ADDRINT addr;
|
|
} cast;
|
|
cast.pf = pf;
|
|
return cast.addr;
|
|
}
|
|
|
|
/*!
|
|
* Print out the error message and exit the process.
|
|
*/
|
|
static void AbortProcess(const string & msg)
|
|
{
|
|
THREADID myTid = PIN_ThreadId();
|
|
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << "mt_tool test aborted: "<< msg << "." << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
PIN_WriteErrorMessage(msg.c_str(), 1002, PIN_ERR_FATAL, 0);
|
|
}
|
|
|
|
|
|
/*!
|
|
* Print out the error message and exit the thread.
|
|
*/
|
|
static void AbortThread(const string & msg, ID_TOOL_THREAD idToolThread)
|
|
{
|
|
THREADID myTid = PIN_ThreadId();
|
|
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << "mt_tool thread aborted: "<< msg << ". Thread name = " << threadNameStr[idToolThread]
|
|
<< ", tid = " << myTid << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
NotifyToolThreadExit(idToolThread);
|
|
PIN_ExitThread(-1);
|
|
}
|
|
|
|
/*!
|
|
* Print out the thread creation message and increase the count of threads created by tool.
|
|
*/
|
|
static void NotifyToolThreadCreated(THREADID threadId, PIN_THREAD_UID threadUid, ID_TOOL_THREAD idToolThread)
|
|
{
|
|
threadStatus[idToolThread] = statusCreated;
|
|
|
|
THREADID myTid = PIN_ThreadId();
|
|
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
|
|
++toolThreadsCreated;
|
|
out << "Tool spawned a private thread. Thread name = " << threadNameStr[idToolThread]
|
|
<< ", tid = " << threadId << endl << flush;
|
|
|
|
BOOL insertStatus = FALSE;
|
|
if (threadUids[idToolThread] == INVALID_PIN_THREAD_UID)
|
|
{
|
|
threadUids[idToolThread] = threadUid;
|
|
insertStatus = TRUE;
|
|
}
|
|
|
|
PIN_ReleaseLock(&pinLock);
|
|
|
|
if (!insertStatus)
|
|
{
|
|
AbortProcess("UID is not unique");
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Print out the thread start message and increase the count of tool's thread that started running.
|
|
*/
|
|
static void NotifyToolThreadStart(ID_TOOL_THREAD idToolThread)
|
|
{
|
|
threadStatus[idToolThread] = statusRun;
|
|
|
|
if (PIN_IsApplicationThread())
|
|
{
|
|
AbortThread("PIN_IsApplicationThread() returns TRUE for a tool's thread", idToolThread);
|
|
}
|
|
|
|
THREADID myTid = PIN_ThreadId();
|
|
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
++toolThreadsStarted;
|
|
out << "Tool's thread started running, name = " << threadNameStr[idToolThread] << ", tid = " << myTid << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
|
|
/*!
|
|
* Print out the thread exit message and increase the count of finished tool's thread.
|
|
*/
|
|
static void NotifyToolThreadExit(ID_TOOL_THREAD idToolThread)
|
|
{
|
|
threadStatus[idToolThread] = statusTerminated;
|
|
|
|
THREADID myTid = PIN_ThreadId();
|
|
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
++toolThreadsFinished;
|
|
out << "Tool's thread finished, name = " << threadNameStr[idToolThread] << ", tid = " << myTid << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
|
|
/*!
|
|
* Wait until isProcessExiting is set in PrepareForFini().
|
|
*/
|
|
static void WaitForProcessExit()
|
|
{
|
|
unsigned i = 0;
|
|
while (!isProcessExiting)
|
|
{
|
|
PIN_Yield();
|
|
|
|
if ((++i % 10) == 0)
|
|
{
|
|
PIN_Sleep(10);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Ensure all internal threads are started before application enters shutdown phase.
|
|
* Should be called in application thread.
|
|
*/
|
|
static void SafeStart()
|
|
{
|
|
if (!PIN_IsApplicationThread())
|
|
{
|
|
AbortProcess("Safe start should run in application thread");
|
|
}
|
|
|
|
// Postpone run of current application thread until all internal threads are started.
|
|
// Note: Assume other threads are not blocked by OS at this point!
|
|
// Note: RootThread that spawns other internal threads doesn't acquire
|
|
// either Pin VM or Pin client lock and uses only Pin APIs that don't aquire these locks.
|
|
for (int i = 0; i < 30; i++)
|
|
{
|
|
if (toolThreadsStarted == numThreadIds)
|
|
{
|
|
// Success. All internal threads were started.
|
|
THREADID myTid = PIN_ThreadId();
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << "Safe start succeeded" << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
return;
|
|
}
|
|
// Sleep for 1 sec.
|
|
PIN_Sleep(1000);
|
|
}
|
|
// Abort process if all internal threads were not started in 30 sec.
|
|
AbortProcess("Safe start didn't complete in 30 sec.");
|
|
}
|
|
|
|
//==========================================================================
|
|
/*!
|
|
* Exception handler for the ThreadException() procedure
|
|
*/
|
|
EXCEPT_HANDLING_RESULT ExceptionHandler(THREADID tid,
|
|
EXCEPTION_INFO * pExceptInfo,
|
|
PHYSICAL_CONTEXT * pPhysCtxt,
|
|
VOID * arg)
|
|
{
|
|
// Read the page address from the TLS slot. The same address is passed in <arg>.
|
|
VOID * page = PIN_GetThreadData(exceptionTlsKey, tid);
|
|
if (page != arg)
|
|
{
|
|
AbortThread("PIN_GetThreadData failed", idThreadException);
|
|
}
|
|
|
|
// Allow write access to the page. This should fix the problem in the thread that
|
|
// attempts to write into this page.
|
|
OS_RETURN_CODE res = OS_ProtectMemory(NATIVE_PID_CURRENT, page, getpagesize(),
|
|
OS_PAGE_PROTECTION_TYPE_WRITE|OS_PAGE_PROTECTION_TYPE_EXECUTE);
|
|
ASSERTX(OS_RETURN_CODE_IS_SUCCESS(res));
|
|
|
|
return EHR_HANDLED;
|
|
}
|
|
|
|
//==========================================================================
|
|
// Internal thread procedures (ROOT_THREAD_FUNC) spawned by the tool
|
|
//==========================================================================
|
|
|
|
/*!
|
|
* Tool's thread that verifies PIN_SafeCopy
|
|
*/
|
|
static VOID ThreadSafeCopy(VOID * arg)
|
|
{
|
|
NotifyToolThreadStart(idThreadSafeCopy);
|
|
|
|
char dst[16];
|
|
char src[16];
|
|
size_t size;
|
|
|
|
size = PIN_SafeCopy(dst, NULL, sizeof(dst));
|
|
if (size != 0)
|
|
{
|
|
AbortThread("First PIN_SafeCopy failed", idThreadSafeCopy);
|
|
}
|
|
|
|
size = PIN_SafeCopy(dst, src, sizeof(src));
|
|
if (size != sizeof(src))
|
|
{
|
|
AbortThread("Second PIN_SafeCopy failed", idThreadSafeCopy);
|
|
}
|
|
|
|
WaitForProcessExit();
|
|
NotifyToolThreadExit(idThreadSafeCopy);
|
|
PIN_ExitThread(0);
|
|
}
|
|
|
|
/*!
|
|
* Tool's thread that verifies handling of internal exceptions
|
|
*/
|
|
static VOID ThreadException(VOID * arg)
|
|
{
|
|
NotifyToolThreadStart(idThreadException);
|
|
|
|
THREADID myTid = PIN_ThreadId();
|
|
|
|
// Allocate a non-writable page and
|
|
char * page = NULL;
|
|
OS_AllocateMemory(NATIVE_PID_CURRENT, OS_PAGE_PROTECTION_TYPE_READ|OS_PAGE_PROTECTION_TYPE_EXECUTE,
|
|
getpagesize(), OS_MEMORY_FLAGS_PRIVATE, (void**)&page);
|
|
ASSERTX(page != 0);
|
|
|
|
// To verify TLS API, we pass the page address to the exception handler in the TLS slot
|
|
BOOL tlsStatus = PIN_SetThreadData(exceptionTlsKey, page, myTid);
|
|
if (!tlsStatus)
|
|
{
|
|
AbortThread("PIN_SetThreadData failed", idThreadException);
|
|
}
|
|
|
|
// Attempt to write into non-writable page to cause an exception
|
|
const char val = 0xAB;
|
|
PIN_TryStart(myTid, ExceptionHandler, page);
|
|
*page = val; // causes an exception which is handled by the ExceptionHandler() callback
|
|
PIN_TryEnd(myTid);
|
|
|
|
if (*page != val)
|
|
{
|
|
AbortThread("Exception handling failed", idThreadException);
|
|
}
|
|
|
|
WaitForProcessExit();
|
|
NotifyToolThreadExit(idThreadException);
|
|
PIN_ExitThread(0);
|
|
}
|
|
|
|
/*!
|
|
* Tool's thread that verifies RTN API
|
|
*/
|
|
static VOID ThreadRtn(VOID * arg)
|
|
{
|
|
NotifyToolThreadStart(idThreadRtn);
|
|
|
|
string errMsg;
|
|
ADDRINT addr;
|
|
RTN rtn;
|
|
|
|
THREADID myTid = PIN_ThreadId();
|
|
|
|
// Wait until <addrDoFlush> is initialized in the ImageLoad() callback
|
|
while (addrDoFlush == 0)
|
|
{
|
|
PIN_Yield();
|
|
}
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
addr = addrDoFlush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
|
|
if (addr == ADDRINT(-1))
|
|
{
|
|
// There is no DoFlush routine in the application image
|
|
NotifyToolThreadExit(idThreadRtn);
|
|
PIN_ExitThread(0);
|
|
}
|
|
|
|
// Now, wait until DoFlush is called
|
|
while (!isDoFlushCalled)
|
|
{
|
|
PIN_Yield();
|
|
}
|
|
|
|
// Look for a routine at the <addrDoFlush> address and check its name
|
|
PIN_LockClient();
|
|
|
|
rtn = RTN_FindByAddress(addr);
|
|
if (!RTN_Valid(rtn))
|
|
{
|
|
errMsg = "RTN_FindByAddress failed";
|
|
}
|
|
else if (RTN_Name(rtn) != "DoFlush")
|
|
{
|
|
errMsg = "RTN_Name failed";
|
|
}
|
|
|
|
PIN_UnlockClient();
|
|
|
|
// Continue execution of DoFlush
|
|
isRtnTestCompleted = TRUE;
|
|
|
|
if (!errMsg.empty())
|
|
{
|
|
AbortThread(errMsg, idThreadRtn);
|
|
}
|
|
|
|
WaitForProcessExit();
|
|
NotifyToolThreadExit(idThreadRtn);
|
|
PIN_ExitThread(0);
|
|
}
|
|
|
|
/*!
|
|
* Tool's thread that verifies CodeCache API
|
|
*/
|
|
static VOID ThreadCodeCache(VOID * arg)
|
|
{
|
|
NotifyToolThreadStart(idThreadCodeCache);
|
|
|
|
ADDRINT addr;
|
|
THREADID myTid = PIN_ThreadId();
|
|
|
|
// Wait until <addrDoFlush> is initialized in the ImageLoad() callback
|
|
while (addrDoFlush == 0)
|
|
{
|
|
PIN_Yield();
|
|
}
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
addr = addrDoFlush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
|
|
if (addr == ADDRINT(-1))
|
|
{
|
|
// There is no DoFlush routine in the application image
|
|
NotifyToolThreadExit(idThreadCodeCache);
|
|
PIN_ExitThread(0);
|
|
}
|
|
|
|
// Now, wait until DoFlush is called
|
|
while (!isDoFlushCalled)
|
|
{
|
|
PIN_Yield();
|
|
}
|
|
|
|
// Flush the Code Cache
|
|
PIN_RemoveInstrumentation();
|
|
|
|
// Continue execution of DoFlush
|
|
isCacheTestCompleted = TRUE;
|
|
|
|
WaitForProcessExit();
|
|
NotifyToolThreadExit(idThreadCodeCache);
|
|
PIN_ExitThread(0);
|
|
}
|
|
|
|
|
|
/*!
|
|
* Tool's thread that does nothing
|
|
*/
|
|
static VOID DummyThread(VOID * arg)
|
|
{
|
|
NotifyToolThreadStart(idDummyThread);
|
|
NotifyToolThreadExit(idDummyThread);
|
|
}
|
|
|
|
/*!
|
|
* Main tool's thread. It is created in the application thread by the
|
|
* main() tool's procedure.
|
|
*/
|
|
static VOID RootThread(VOID * arg)
|
|
{
|
|
NotifyToolThreadStart(idRootThread);
|
|
|
|
// Verify the argument
|
|
if (arg != rootThreadArg)
|
|
{
|
|
AbortThread("Invalid thread argument", idRootThread);
|
|
}
|
|
|
|
THREADID myTid = PIN_ThreadId();
|
|
|
|
THREADID threadId;
|
|
PIN_THREAD_UID threadUid;
|
|
|
|
// Create an internal thread and wait for exit.
|
|
threadId = PIN_SpawnInternalThread(DummyThread, 0, 0, &threadUid);
|
|
if (threadId == INVALID_THREADID)
|
|
{
|
|
if (!isProcessExiting)
|
|
{
|
|
// Assume that OS rejection to create thread doesn't relate to shutdown.
|
|
AbortThread("PIN_SpawnInternalThread(DummyThread) failed", idRootThread);
|
|
}
|
|
else
|
|
{
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << "PIN_SpawnInternalThread(DummyThread) failed once process exit started" << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NotifyToolThreadCreated(threadId, threadUid, idDummyThread);
|
|
|
|
BOOL waitStatus = PIN_WaitForThreadTermination(threadUid, waitTimeout, 0); // Wait waitTimeout/1000 sec
|
|
if (!waitStatus)
|
|
{
|
|
ostringstream ostr;
|
|
ostr << "DummyThread didn't finish in " << waitTimeout / 1000 << " seconds";
|
|
if (threadStatus[idDummyThread] >= statusRun)
|
|
{
|
|
AbortThread(ostr.str(), idRootThread);
|
|
}
|
|
else
|
|
{
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << ostr.str() << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create an internal thread that verifies PIN_SafeCopy API
|
|
threadId = PIN_SpawnInternalThread(ThreadSafeCopy, 0, 0, &threadUid);
|
|
if (threadId == INVALID_THREADID)
|
|
{
|
|
if (!isProcessExiting)
|
|
{
|
|
// Assume that OS rejection to create thread doesn't relate to shutdown.
|
|
AbortThread("PIN_SpawnInternalThread(ThreadSafeCopy) failed", idRootThread);
|
|
}
|
|
else
|
|
{
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << "PIN_SpawnInternalThread(ThreadSafeCopy) failed once process exit started" << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NotifyToolThreadCreated(threadId, threadUid, idThreadSafeCopy);
|
|
}
|
|
|
|
// Create an internal thread that verifies exception handling API
|
|
threadId = PIN_SpawnInternalThread(ThreadException, 0, 0, &threadUid);
|
|
if (threadId == INVALID_THREADID)
|
|
{
|
|
if (!isProcessExiting)
|
|
{
|
|
// Assume that OS rejection to create thread doesn't relate to shutdown.
|
|
AbortThread("PIN_SpawnInternalThread(ThreadException) failed", idRootThread);
|
|
}
|
|
else
|
|
{
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << "PIN_SpawnInternalThread(ThreadException) failed once process exit started" << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NotifyToolThreadCreated(threadId, threadUid, idThreadException);
|
|
}
|
|
|
|
// Create an internal thread that verifies RTN API
|
|
threadId = PIN_SpawnInternalThread(ThreadRtn, 0, 0, &threadUid);
|
|
if (threadId == INVALID_THREADID)
|
|
{
|
|
if (!isProcessExiting)
|
|
{
|
|
// Assume that OS rejection to create thread doesn't relate to shutdown.
|
|
AbortThread("PIN_SpawnInternalThread(ThreadRtn) failed", idRootThread);
|
|
}
|
|
else
|
|
{
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << "PIN_SpawnInternalThread(ThreadRtn) failed once process exit started" << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NotifyToolThreadCreated(threadId, threadUid, idThreadRtn);
|
|
}
|
|
|
|
// Create an internal thread that verifies CodeCache API
|
|
threadId = PIN_SpawnInternalThread(ThreadCodeCache, 0, 0, &threadUid);
|
|
if (threadId == INVALID_THREADID)
|
|
{
|
|
if (!isProcessExiting)
|
|
{
|
|
// Assume that OS rejection to create thread doesn't relate to shutdown.
|
|
AbortThread("PIN_SpawnInternalThread(ThreadCodeCache) failed", idRootThread);
|
|
}
|
|
else
|
|
{
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << "PIN_SpawnInternalThread(ThreadCodeCache) failed once process exit started" << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NotifyToolThreadCreated(threadId, threadUid, idThreadCodeCache);
|
|
}
|
|
|
|
WaitForProcessExit();
|
|
NotifyToolThreadExit(idRootThread);
|
|
PIN_ExitThread(0);
|
|
}
|
|
|
|
//==========================================================================
|
|
// Analysis routines
|
|
//==========================================================================
|
|
static VOID OnDoFlush()
|
|
{
|
|
if (KnobSafeStart)
|
|
{
|
|
SafeStart();
|
|
}
|
|
|
|
// Notify tool's threads about a call to DoFlush
|
|
isDoFlushCalled = TRUE;
|
|
|
|
// Wait until completion of all dependent tests
|
|
while (!(isRtnTestCompleted && isCacheTestCompleted))
|
|
{
|
|
PIN_Yield();
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
// Instrumentation callbacks
|
|
//==========================================================================
|
|
/*!
|
|
* RTN instrumentation routine.
|
|
*/
|
|
static VOID InstrumentRoutine(RTN rtn, VOID *)
|
|
{
|
|
if (RTN_Name(rtn) == "DoFlush")
|
|
{
|
|
RTN_Open(rtn);
|
|
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(OnDoFlush), IARG_END);
|
|
RTN_Close(rtn);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Trace instrumentation routine.
|
|
*/
|
|
static VOID InstrumentTrace(TRACE trace, VOID *v)
|
|
{
|
|
const ADDRINT traceAddress = TRACE_Address(trace);
|
|
ASSERTX(0 != traceAddress);
|
|
if (traceAddress == Ptr2Addrint(RootThread))
|
|
{
|
|
AbortProcess("Pin attempts to instrument the tool's thread");
|
|
}
|
|
|
|
if (isCacheFlushHappened) return; // only need to record traces until the codecache flush
|
|
|
|
static set<ADDRINT> addresses;
|
|
if (isCacheTestCompleted)
|
|
{
|
|
if (addresses.end() != addresses.find(traceAddress))
|
|
{
|
|
ASSERTX(!isCacheFlushHappened);
|
|
isCacheFlushHappened = TRUE;
|
|
out << "Re-jitting TRACE at address 0x" << hex << traceAddress << dec << ", assuming codecache flush." << endl;
|
|
return;
|
|
}
|
|
}
|
|
addresses.insert(traceAddress);
|
|
}
|
|
|
|
/*!
|
|
* Image load callback.
|
|
*/
|
|
VOID ImageLoad(IMG img, VOID *v)
|
|
{
|
|
if ( IMG_IsMainExecutable(img))
|
|
{
|
|
ADDRINT addr;
|
|
RTN rtn = RTN_FindByName(img, "DoFlush");
|
|
if (RTN_Valid(rtn))
|
|
{
|
|
addr = RTN_Address(rtn);
|
|
}
|
|
else
|
|
{
|
|
addr = ADDRINT(-1);
|
|
}
|
|
|
|
THREADID myTid = PIN_ThreadId();
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
addrDoFlush = addr;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Thread start/exit callbacks in application's threads.
|
|
*/
|
|
static VOID ThreadStart(THREADID threadid, CONTEXT *ctxt, INT32 flags, VOID *v)
|
|
{
|
|
if (!PIN_IsApplicationThread())
|
|
{
|
|
AbortProcess("PIN_IsApplicationThread() returns FALSE for an application's thread");
|
|
}
|
|
|
|
PIN_GetLock(&pinLock, threadid + 1);
|
|
appThreadsStarted++;
|
|
out << "Application's thread started running, tid = " << threadid << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
|
|
static VOID ThreadFini(THREADID threadid, const CONTEXT *ctxt, INT32 code, VOID *v)
|
|
{
|
|
PIN_GetLock(&pinLock, threadid + 1);
|
|
appThreadsFinished++;
|
|
out << "Application's thread finished, tid = " << threadid << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
}
|
|
|
|
/*!
|
|
* Process exit callback (unlocked).
|
|
*/
|
|
static VOID PrepareForFini(VOID *v)
|
|
{
|
|
if (!PIN_IsApplicationThread())
|
|
{
|
|
AbortProcess("Process is terminated by an internal thread");
|
|
}
|
|
|
|
BOOL waitStatus;
|
|
INT32 threadExitCode;
|
|
BOOL threadExitStatus = TRUE;
|
|
|
|
// Signal that process is about to finish.
|
|
isProcessExiting = TRUE;
|
|
|
|
THREADID myTid = PIN_ThreadId();
|
|
|
|
// Wait until all internal threads exit
|
|
for (int i = 0; i < numThreadIds; i++)
|
|
{
|
|
if (threadUids[i] == INVALID_PIN_THREAD_UID)
|
|
{
|
|
// The thread was not created. Already reported in log.
|
|
continue;
|
|
}
|
|
waitStatus = PIN_WaitForThreadTermination(threadUids[i], waitTimeout, &threadExitCode); // Wait waitTimeout/1000 sec
|
|
if (!waitStatus)
|
|
{
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << threadNameStr[i] << " didn't finish in " << waitTimeout / 1000 << " seconds" << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
|
|
if (threadStatus[i] >= statusRun)
|
|
{
|
|
// Report error only if the thread is already started in the tool.
|
|
// Since this point OS cannot terminate it without notification or infinitely suspend it.
|
|
threadExitStatus = FALSE;
|
|
}
|
|
}
|
|
else if (threadExitCode != 0)
|
|
{
|
|
threadExitStatus = FALSE;
|
|
}
|
|
}
|
|
|
|
// If any thread exited abnormally, abort the process
|
|
if (!threadExitStatus)
|
|
{
|
|
AbortProcess("At least one of the tool's threads exited abnormally");
|
|
}
|
|
|
|
PIN_GetLock(&pinLock, myTid + 1);
|
|
out << "mt_tool test: All tool's threads finished successfully." << endl << flush;
|
|
PIN_ReleaseLock(&pinLock);
|
|
|
|
}
|
|
|
|
/*!
|
|
* Process exit callback (locked).
|
|
*/
|
|
static VOID Fini(INT32 code, VOID *v)
|
|
{
|
|
out << "Number of application's threads started: " << appThreadsStarted << endl << flush;
|
|
out << "Number of application's threads finished: " << appThreadsFinished << endl << flush;
|
|
out << "Number of tool's threads created: " << toolThreadsCreated << endl << flush;
|
|
out << "Number of tool's threads started: " << toolThreadsStarted << endl << flush;
|
|
out << "Number of tool's threads finished: " << toolThreadsFinished << endl << flush;
|
|
if (toolThreadsCreated != toolThreadsStarted)
|
|
{
|
|
out << "toolThreadsCreated: " << toolThreadsCreated
|
|
<< ", toolThreadsStarted: " << toolThreadsStarted << endl << flush;
|
|
if (KnobSafeStart)
|
|
{
|
|
// We expect all created internal threads are started if safe start is applied.
|
|
// This state means that safe start was not applied.
|
|
AbortProcess("Safe start was not applied");
|
|
}
|
|
// If threads safe start is not applied it is OK to expect some internal threads
|
|
// to remain not started since OS may still block them when process exit happens.
|
|
}
|
|
if (toolThreadsStarted != toolThreadsFinished)
|
|
{
|
|
out << "toolThreadsStarted: " << toolThreadsStarted
|
|
<< ", toolThreadsFinished: " << toolThreadsFinished << endl << flush;
|
|
PIN_ExitProcess(1);
|
|
}
|
|
|
|
if (isCacheTestCompleted && !isCacheFlushHappened)
|
|
{
|
|
out << "Code Cache flush did not happen" << endl << flush;
|
|
PIN_ExitProcess(1);
|
|
}
|
|
}
|
|
|
|
/* ===================================================================== */
|
|
/* Print Help Message */
|
|
/* ===================================================================== */
|
|
|
|
INT32 Usage()
|
|
{
|
|
cerr << "This tool tests multithreaded tools on multithreaded apps." << endl;
|
|
cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
|
|
return -1;
|
|
}
|
|
|
|
/*!
|
|
* The main procedure of the tool.
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
PIN_InitSymbols();
|
|
|
|
if (PIN_Init(argc, argv)) return Usage();
|
|
|
|
out.open(KnobOutput.Value().c_str());
|
|
|
|
PIN_InitLock(&pinLock);
|
|
|
|
PIN_AddThreadStartFunction(ThreadStart, 0);
|
|
PIN_AddThreadFiniFunction(ThreadFini, 0);
|
|
PIN_AddFiniFunction(Fini, 0);
|
|
PIN_AddPrepareForFiniFunction(PrepareForFini, 0);
|
|
TRACE_AddInstrumentFunction(InstrumentTrace, 0);
|
|
IMG_AddInstrumentFunction(ImageLoad, 0);
|
|
RTN_AddInstrumentFunction(InstrumentRoutine, 0);
|
|
|
|
// In order to verify TLS API in internal threads we create a TLS slot for
|
|
// passing data to exception handlers.
|
|
exceptionTlsKey = PIN_CreateThreadDataKey(0);
|
|
if (exceptionTlsKey == -1)
|
|
{
|
|
AbortProcess("PIN_CreateThreadDataKey failed");
|
|
}
|
|
|
|
for (int i = 0; i < numThreadIds; i++)
|
|
{
|
|
threadStatus[i] = statusInit;
|
|
threadUids[i] = INVALID_PIN_THREAD_UID;
|
|
}
|
|
|
|
// Spawn the main internal thread. When this thread starts it spawns all other internal threads.
|
|
PIN_THREAD_UID rootThreadUid;
|
|
THREADID rootThreadId = PIN_SpawnInternalThread(RootThread, rootThreadArg, 0, &rootThreadUid);
|
|
if (rootThreadId == INVALID_THREADID)
|
|
{
|
|
AbortProcess("PIN_SpawnInternalThread(RootThread) failed");
|
|
}
|
|
NotifyToolThreadCreated(rootThreadId, rootThreadUid, idRootThread);
|
|
|
|
// Never returns
|
|
PIN_StartProgram();
|
|
return 0;
|
|
}
|