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.
762 lines
27 KiB
762 lines
27 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.
|
|
*/
|
|
|
|
/*
|
|
* Sample buffering tool using Pin buffering API, with internal-tool threads that processes
|
|
* the buffers.
|
|
*
|
|
* Similar to membuffer_simple, a memory trace (Ip of memory accessing instruction and address
|
|
* of memory access - see struct MEMREF) is collected by inserting Pin buffering API.
|
|
* This tool uses multiple buffers and internal-tool threads to enable the application code to continue
|
|
* to run (and fill buffers), while full buffers are being processed by the internal-tool threads.
|
|
* It is similar to memtrace_threadpool in this sense - but the multiple buffers that are available for filling
|
|
* are kept on the free buffers list of the application thread that used the PIN_AllocateBuffer to allocate
|
|
* them.
|
|
*
|
|
* Each application thread allocates the specified number of buffers (see PIN_AllocateBuffer).
|
|
* The Pin buffering API requires that each application thread can only use the buffers it
|
|
* allocated as buffers to be filled by Pin, but any internal-tool thread can process any buffer.
|
|
* So the internal-processing threads can be used as a thread-pool.
|
|
* This leads to the following model:
|
|
* Buffers that are available to be used by the application thread to be filled by Pin are kept on
|
|
* a free buffers list associated with the application thread.
|
|
* When a buffer becomes full, Pin calls the BufferFull callback function. The BufferFull function uses
|
|
* its thread id parameter to identify which application thread this buffer belongs to, and then
|
|
* calls that threads EnqueueFullAndGetNextToFill function.
|
|
* The EnqueueFullAndGetNextToFill function puts the buffer, along with the identifier of which app-thread
|
|
* it belongs to on a global full buffers list, and then signals that a(nother) full buffer is on the
|
|
* list. After this the EnqueueFullAndGetNextToFill function waits on the free buffers list it's application
|
|
* thread, when this free list is signaled, the EnqueueFullAndGetNextToFill takes a buffer from there
|
|
* and returns it to the BufferFull function, which returns it to Pin as the next buffer to fill.
|
|
* The internal-tool threads all wait on the full buffers list. When that list is signaled, one of them
|
|
* wakes up, takes a buffer from the list, processes it, and then puts it on the free buffers list
|
|
* of the application thread that owns the buffer.
|
|
*
|
|
*
|
|
* While the Pin APIs used here are OS generic, the tool also uses windows counting semaphores to
|
|
* manage the buffer lists - so it is windows specific
|
|
*/
|
|
|
|
|
|
#include <cstdio>
|
|
#include <set>
|
|
#include <list>
|
|
#include "pin.H"
|
|
|
|
namespace WIND
|
|
{
|
|
#include <windows.h>
|
|
}
|
|
using WIND::DWORD;
|
|
|
|
using std::string;
|
|
using std::set;
|
|
using std::list;
|
|
/*
|
|
* Knobs for tool
|
|
*/
|
|
|
|
KNOB<BOOL> KnobProcessBuffer(KNOB_MODE_WRITEONCE, "pintool", "process_buffs", "1", "process the filled buffers");
|
|
KNOB<UINT32> KnobNumProcessingThreads(KNOB_MODE_OVERWRITE, "pintool", "num_processing_threads", "3", "number of processing threads");
|
|
// 256*4096=1048576 - same size buffer in memtrace_simple, membuffer_simple, memtrace_threadpool and membuffer_threadpool
|
|
KNOB<UINT32> KnobNumPagesInBuffer(KNOB_MODE_WRITEONCE, "pintool", "num_pages_in_buffer", "256", "number of pages in buffer");
|
|
KNOB<UINT32> KnobNumBuffersPerAppThread(KNOB_MODE_WRITEONCE, "pintool", "num_buffers_per_app_thread", "3", "number of buffers per thread");
|
|
KNOB<BOOL> KnobStatistics(KNOB_MODE_WRITEONCE, "pintool", "statistics", "0", "gather statistics");
|
|
KNOB<BOOL> KnobLiteStatistics(KNOB_MODE_WRITEONCE, "pintool", "lite_statistics", "0", "gather lite statistics");
|
|
KNOB<string> KnobStatisticsOutputFile(KNOB_MODE_WRITEONCE, "pintool", "stat_file", "membuffer_threadpool_stats.out", "output file");
|
|
extern "C" UINT64 ReadProcessorCycleCounter();
|
|
|
|
|
|
/* Struct of memory references recorded in buffers.
|
|
*/
|
|
struct MEMREF
|
|
{
|
|
ADDRINT pc;
|
|
ADDRINT ea;
|
|
};
|
|
|
|
// the Pin TLS slot that an application-thread will use to hold the APP_THREAD_REPRESENTITVE
|
|
// object that it owns
|
|
TLS_KEY appThreadRepresentitiveKey;
|
|
|
|
// Set of UIDs of all internal-tool threads
|
|
// We use std::set to verify that each thread has a unique UID
|
|
set<PIN_THREAD_UID> uidSet;
|
|
|
|
// The buffer ID returned by the one call to PIN_DefineTraceBuffer
|
|
BUFFER_ID bufId;
|
|
|
|
|
|
#include "threadpool_statistics.h"
|
|
|
|
class BUFFER_LIST_MANAGER;
|
|
|
|
// all full buffers are placed on this list by the app threads.
|
|
// the internal-tool threads pick them up from here,
|
|
// process them, and put them on the owning app thread's
|
|
// free list
|
|
BUFFER_LIST_MANAGER * fullBuffersListManager = NULL;
|
|
|
|
/*
|
|
* APP_THREAD_REPRESENTITVE
|
|
* Each application thread, creates an object of this class and saves it in it's Pin TLS
|
|
* slot (appThreadRepresentitiveKey).
|
|
* This object is used when the BufferFull function is called. It provides the functionality
|
|
* of:
|
|
* 1) Managing the buffers allocated (by Pin) by this thread. It uses it's BUFFER_LIST_MANAGER
|
|
* _freeBufferListManager to do this.
|
|
* 2) Enquening a full buffer on the global full buffers list (fullBuffersListManager) so it
|
|
* will be processed by one of the internal-tool buffer processing threads.
|
|
* 3) If there is no internal-tool buffer processing thread running yet
|
|
* then the ProcessBuffer is used to process the buffer by the application
|
|
* thread. It cannot wait for processing thread to start running
|
|
* because this may cause deadlock - because this app thread may be holding some OS
|
|
* resource that the processing thread needs in order to start running - e.g. the LoaderLock
|
|
*/
|
|
class APP_THREAD_REPRESENTITVE
|
|
{
|
|
|
|
public:
|
|
APP_THREAD_REPRESENTITVE(THREADID tid);
|
|
~APP_THREAD_REPRESENTITVE();
|
|
|
|
// Called from the BufferFull callback
|
|
VOID * EnqueueFullAndGetNextToFill(VOID *buf, UINT64 numElements);
|
|
|
|
// Called from the ThreadFini callback, to know when all the buffers of this app thread
|
|
// have been processed
|
|
BOOL AllBuffersProcessed();
|
|
|
|
APP_THREAD_STATISTICS * Statistics() { return (&_appThreadStatistics);}
|
|
BUFFER_LIST_MANAGER & FreeBufferListManager() { return *_freeBufferListManager;}
|
|
|
|
private:
|
|
// the buffers of this thread are placed on this list when they are available for filling
|
|
BUFFER_LIST_MANAGER *_freeBufferListManager;
|
|
THREADID _myTid;
|
|
UINT32 _numBuffersAllocated;
|
|
VOID * _currentBuf;
|
|
|
|
APP_THREAD_STATISTICS _appThreadStatistics;
|
|
};
|
|
|
|
|
|
/*
|
|
* BUFFER_LIST_MANAGER
|
|
* This class implements buffer list management, both for the global fullBuffers list
|
|
* and for the per-app-thread bufferBuffersList
|
|
*/
|
|
class BUFFER_LIST_MANAGER
|
|
{
|
|
public:
|
|
BUFFER_LIST_MANAGER(BOOL notifyExitRequired = FALSE);
|
|
~BUFFER_LIST_MANAGER();
|
|
|
|
BOOL PutBufferOnList(VOID *buf, UINT64 numElements,
|
|
/* the thread that owns the buffer */
|
|
APP_THREAD_REPRESENTITVE *appThreadRepresentitive,
|
|
/* thread Id of the thread making the call */
|
|
THREADID tid);
|
|
VOID * GetBufferFromList(UINT64 *numElements,
|
|
/* the thread that owns the buffer */
|
|
APP_THREAD_REPRESENTITVE **appThreadRepresentitive,
|
|
/* thread Id of the thread making the call */
|
|
THREADID tid);
|
|
VOID NotifyExit();
|
|
BUFFER_LIST_STATISTICS *Statistics() {return &_bufferListStatistics;}
|
|
|
|
private:
|
|
|
|
// structure of an element of the buffer list
|
|
struct BUFFER_LIST_ELEMENT
|
|
{
|
|
VOID *buf;
|
|
UINT64 numElements;
|
|
// the application thread that puts this buffer on the list
|
|
APP_THREAD_REPRESENTITVE *appThreadRepresentitive;
|
|
};
|
|
|
|
class SCOPED_LOCK {
|
|
PIN_LOCK * _lock;
|
|
public:
|
|
SCOPED_LOCK(PIN_LOCK * lock, THREADID tid) : _lock(lock)
|
|
{
|
|
ASSERTX(_lock != NULL);
|
|
PIN_GetLock(_lock, tid + 1);
|
|
}
|
|
~SCOPED_LOCK()
|
|
{
|
|
PIN_ReleaseLock(_lock);
|
|
}
|
|
};
|
|
|
|
WIND::HANDLE _exitEvent;
|
|
WIND::HANDLE _bufferSem;
|
|
PIN_LOCK _bufferListLock;
|
|
list<BUFFER_LIST_ELEMENT> _bufferList;
|
|
|
|
BUFFER_LIST_STATISTICS _bufferListStatistics;
|
|
};
|
|
|
|
/*
|
|
* Returns single instance of global buffer list manager that supports synchronization
|
|
* and notified on process exit
|
|
*/
|
|
BUFFER_LIST_MANAGER * GetFullBuffersListManager()
|
|
{
|
|
static BUFFER_LIST_MANAGER buffersListManager(TRUE);
|
|
return &buffersListManager;
|
|
}
|
|
|
|
VOID ProcessBuffer(VOID *buf, UINT64 numElements, APP_THREAD_REPRESENTITVE * associatedAppThread)
|
|
{
|
|
if (!KnobProcessBuffer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (KnobStatistics)
|
|
{
|
|
associatedAppThread->Statistics()->StartCyclesProcessingBuffer();
|
|
}
|
|
|
|
struct MEMREF * memref = reinterpret_cast<struct MEMREF*>(buf);
|
|
struct MEMREF * firstMemref = memref;
|
|
for (UINT64 i = 0; i < numElements; i++, memref++)
|
|
{
|
|
firstMemref->pc += memref->pc + memref->ea;
|
|
}
|
|
|
|
associatedAppThread->Statistics()->AddNumElementsProcessed((UINT32)numElements);
|
|
if (KnobStatistics)
|
|
{
|
|
associatedAppThread->Statistics()->UpdateCyclesProcessingBuffer();
|
|
}
|
|
}
|
|
|
|
|
|
/*********** APP_THREAD_REPRESENTITVE implementation *******/
|
|
|
|
APP_THREAD_REPRESENTITVE::APP_THREAD_REPRESENTITVE(THREADID tid) :
|
|
_myTid(tid), _numBuffersAllocated(0), _currentBuf(NULL)
|
|
{
|
|
_freeBufferListManager = new (BUFFER_LIST_MANAGER);
|
|
}
|
|
|
|
APP_THREAD_REPRESENTITVE::~APP_THREAD_REPRESENTITVE()
|
|
{
|
|
delete _freeBufferListManager;
|
|
}
|
|
|
|
|
|
VOID * APP_THREAD_REPRESENTITVE::EnqueueFullAndGetNextToFill(VOID *fullBuf, UINT64 numElements)
|
|
{
|
|
_appThreadStatistics.IncrementNumBuffersFilled();
|
|
|
|
// under some conditions the buffer is processed in this app thread
|
|
if ( (fullBuffersListManager == NULL) // cannot wait for processing thread to start running
|
|
// this may cause deadlock - because this app thread
|
|
// may be holding some OS resource that the processing
|
|
// needs to obtain in order to start - e.g. the LoaderLock
|
|
// heuristic - no available free buffer, so process by this app thread.
|
|
|
|
// Otherwise put the fullBuf on the full buffers list, one the internal-tool processing
|
|
// threads will pick it from there, process it, and then put it on this app-thread's
|
|
// free buffer list
|
|
|| !fullBuffersListManager->PutBufferOnList(fullBuf, numElements, this, _myTid)
|
|
// Full buffers manager may not take the buffer if process exit is started.
|
|
)
|
|
{ // process buffer in this app thread
|
|
_appThreadStatistics.IncrementNumBuffersProcessedInAppThread();
|
|
ProcessBuffer(fullBuf, numElements, this);
|
|
_currentBuf = fullBuf;
|
|
return _currentBuf;
|
|
}
|
|
|
|
if (_numBuffersAllocated == 0)
|
|
{
|
|
// now allocate the rest of the KnobNumBuffersPerAppThread buffers to be used
|
|
// only one buffer definition is used - it is identified by bufId
|
|
// that was returned by the one call to PIN_DefineTraceBuffer
|
|
for (; _numBuffersAllocated < KnobNumBuffersPerAppThread - 1; _numBuffersAllocated++)
|
|
{
|
|
VOID * buf = PIN_AllocateBuffer(bufId);
|
|
_freeBufferListManager->PutBufferOnList(buf, 0, this, _myTid);
|
|
printf (" Allocated buffer %p for thread %d\n", buf, _myTid);
|
|
fflush (stdout);
|
|
}
|
|
}
|
|
|
|
// provide Pin with the next buffer to fill.
|
|
// It is always taken from the free buffers list of this app thread.
|
|
// If the list is empty then this app thread will be blocked until one
|
|
// is placed there (by one of the tool-internal buffer processing threads).
|
|
UINT64 numElementsDummy;
|
|
APP_THREAD_REPRESENTITVE *appThreadRepresentitiveDummy;
|
|
_currentBuf = _freeBufferListManager->GetBufferFromList(
|
|
&numElementsDummy,
|
|
&appThreadRepresentitiveDummy,
|
|
_myTid);
|
|
ASSERTX(_currentBuf != NULL);
|
|
ASSERTX(appThreadRepresentitiveDummy == this);
|
|
return _currentBuf;
|
|
}
|
|
|
|
BOOL APP_THREAD_REPRESENTITVE::AllBuffersProcessed()
|
|
{
|
|
if (_numBuffersAllocated == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
ASSERTX(_currentBuf != NULL);
|
|
|
|
// Reclaim allocated buffers
|
|
for (; _numBuffersAllocated > 0; _numBuffersAllocated--)
|
|
{
|
|
UINT64 numElementsDummy;
|
|
APP_THREAD_REPRESENTITVE *appThreadRepresentitiveDummy = NULL;
|
|
VOID * buf = _freeBufferListManager->GetBufferFromList(
|
|
&numElementsDummy,
|
|
&appThreadRepresentitiveDummy,
|
|
_myTid);
|
|
ASSERTX(buf != NULL);
|
|
ASSERTX(appThreadRepresentitiveDummy == this);
|
|
PIN_DeallocateBuffer(bufId, buf);
|
|
printf (" Deallocated buffer %p for thread %d\n", buf, _myTid);
|
|
fflush (stdout);
|
|
}
|
|
PIN_DeallocateBuffer(bufId, _currentBuf);
|
|
printf (" Deallocated buffer %p for thread %d\n", _currentBuf, _myTid);
|
|
fflush (stdout);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*********** BUFFER_LIST_MANAGER implementation *******/
|
|
|
|
BUFFER_LIST_MANAGER::BUFFER_LIST_MANAGER(BOOL notifyExitRequired) :
|
|
_exitEvent(NULL)
|
|
{
|
|
PIN_InitLock(&_bufferListLock);
|
|
_bufferSem = WIND::CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
|
|
ASSERTX(_bufferSem != NULL);
|
|
if (notifyExitRequired)
|
|
{
|
|
// Create manually reset event in non-signaled state.
|
|
_exitEvent = WIND::CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
ASSERTX(_exitEvent != NULL);
|
|
}
|
|
}
|
|
|
|
BUFFER_LIST_MANAGER::~BUFFER_LIST_MANAGER()
|
|
{
|
|
if (_exitEvent != NULL)
|
|
{
|
|
WIND::CloseHandle(_exitEvent);
|
|
}
|
|
WIND::CloseHandle(_bufferSem);
|
|
}
|
|
|
|
|
|
BOOL BUFFER_LIST_MANAGER::PutBufferOnList(VOID *buf, UINT64 numElements,
|
|
/* the thread that owns the buffer */
|
|
APP_THREAD_REPRESENTITVE *appThreadRepresentitive,
|
|
/* thread Id of the thread making the call */
|
|
THREADID tid)
|
|
{
|
|
if ((_exitEvent != NULL) &&
|
|
(WIND::WaitForSingleObject(_exitEvent, 0) == WAIT_OBJECT_0))
|
|
{
|
|
// Exit event signaled. Do not add new buffers.
|
|
return FALSE;
|
|
}
|
|
|
|
BUFFER_LIST_ELEMENT bufferListElement;
|
|
|
|
bufferListElement.buf = buf;
|
|
bufferListElement.numElements = numElements;
|
|
bufferListElement.appThreadRepresentitive = appThreadRepresentitive;
|
|
|
|
{
|
|
SCOPED_LOCK lock(&_bufferListLock, tid);
|
|
_bufferList.push_back(bufferListElement);
|
|
}
|
|
|
|
WIND::ReleaseSemaphore(_bufferSem, 1, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID* BUFFER_LIST_MANAGER::GetBufferFromList(UINT64 *numElements,
|
|
/* the thread that owns the buffer */
|
|
APP_THREAD_REPRESENTITVE **appThreadRepresentitive,
|
|
/* thread Id of the thread making the call */
|
|
THREADID tid)
|
|
{
|
|
if (KnobStatistics)
|
|
{
|
|
if (_bufferList.empty())
|
|
{
|
|
_bufferListStatistics.IncrementNumTimesWaited();
|
|
}
|
|
_bufferListStatistics.StartCyclesWaitingForBuffer();
|
|
}
|
|
|
|
WIND::DWORD status;
|
|
if (_exitEvent != NULL)
|
|
{
|
|
// Process buffers even after exit notification until the list is empty.
|
|
WIND::HANDLE handles[2] = { _bufferSem, _exitEvent };
|
|
status = WIND::WaitForMultipleObjects (2, handles, FALSE, INFINITE);
|
|
if (status == (WAIT_OBJECT_0 + 1))
|
|
{
|
|
// Process exit flow started and there is no pending buffers to process.
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = WIND::WaitForSingleObject (_bufferSem, INFINITE);
|
|
}
|
|
// Otherwise wait should exit due to semaphore.
|
|
if (status != WAIT_OBJECT_0)
|
|
{
|
|
printf (" WAIT returned %d, last error %x\n", status, WIND::GetLastError());
|
|
fflush (stdout);
|
|
ASSERTX(status == WAIT_OBJECT_0);
|
|
}
|
|
|
|
if (KnobStatistics)
|
|
{
|
|
_bufferListStatistics.UpdateCyclesWaitingForBuffer();
|
|
}
|
|
|
|
{
|
|
SCOPED_LOCK lock(&_bufferListLock, tid);
|
|
ASSERTX(!_bufferList.empty());
|
|
const BUFFER_LIST_ELEMENT &bufferListElement = (_bufferList.front());
|
|
VOID* buf = bufferListElement.buf;
|
|
*numElements = bufferListElement.numElements;
|
|
*appThreadRepresentitive = bufferListElement.appThreadRepresentitive;
|
|
_bufferList.pop_front();
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
VOID BUFFER_LIST_MANAGER::NotifyExit()
|
|
{
|
|
if (_exitEvent != NULL)
|
|
{
|
|
WIND::SetEvent(_exitEvent);
|
|
}
|
|
}
|
|
|
|
/*********** BUFFER_LIST_MANAGER implementation END *******/
|
|
|
|
|
|
/*
|
|
* Trace instrumentation routine invoked by Pin when jitting a trace
|
|
* Insert code to write data to a thread-specific buffer for instructions
|
|
* that access memory.
|
|
*/
|
|
VOID Trace(TRACE trace, VOID *v)
|
|
{
|
|
|
|
// Insert a call to record the effective address.
|
|
for(BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl=BBL_Next(bbl))
|
|
{
|
|
for(INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins=INS_Next(ins))
|
|
{
|
|
UINT32 memOperands = INS_MemoryOperandCount(ins);
|
|
|
|
// Iterate over each memory operand of the instruction.
|
|
for (UINT32 memOp = 0; memOp < memOperands; memOp++)
|
|
{
|
|
INS_InsertFillBuffer(ins, IPOINT_BEFORE, bufId,
|
|
IARG_INST_PTR, offsetof(struct MEMREF, pc),
|
|
IARG_MEMORYOP_EA, memOp,
|
|
offsetof(struct MEMREF, ea),
|
|
IARG_END);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
*
|
|
* Callback Routines
|
|
*
|
|
**************************************************************************/
|
|
|
|
/*!
|
|
* Called when a buffer fills up, or the thread exits, so the buffer can be processed
|
|
* Called in the context of the application thread
|
|
* @param[in] id buffer handle
|
|
* @param[in] tid id of owning thread
|
|
* @param[in] ctxt application context
|
|
* @param[in] buf actual pointer to buffer
|
|
* @param[in] numElements number of records
|
|
* @param[in] v callback value
|
|
* @return A pointer to the buffer to resume filling.
|
|
*/
|
|
VOID * BufferFull(BUFFER_ID id, THREADID tid, const CONTEXT *ctxt, VOID *buf,
|
|
UINT64 numElements, VOID *v)
|
|
{
|
|
|
|
struct MEMREF * reference = (struct MEMREF*)buf;
|
|
|
|
APP_THREAD_REPRESENTITVE * appThreadRepresentitive
|
|
= static_cast<APP_THREAD_REPRESENTITVE*>( PIN_GetThreadData( appThreadRepresentitiveKey, tid ) );
|
|
ASSERTX(appThreadRepresentitive != NULL);
|
|
|
|
printf ("AppThread tid %d GotBuffer %p\n", tid, buf);
|
|
fflush (stdout);
|
|
VOID *nextBuffToFill
|
|
= appThreadRepresentitive->EnqueueFullAndGetNextToFill(buf, numElements);
|
|
printf ("AppThread tid %d NextToFill %p\n", tid, nextBuffToFill);
|
|
fflush (stdout);
|
|
return (nextBuffToFill);
|
|
}
|
|
|
|
|
|
|
|
VOID ThreadStart(THREADID tid, CONTEXT *ctxt, INT32 flags, VOID *v)
|
|
{
|
|
// There is a new APP_THREAD_REPRESENTITVE for every thread.
|
|
APP_THREAD_REPRESENTITVE * appThreadRepresentitive
|
|
= new APP_THREAD_REPRESENTITVE(tid);
|
|
|
|
printf ("Started thread %d\n", tid);
|
|
fflush (stdout);
|
|
|
|
// A thread will need to look up its APP_THREAD_REPRESENTITVE, so save pointer in TLS
|
|
PIN_SetThreadData(appThreadRepresentitiveKey, appThreadRepresentitive, tid);
|
|
|
|
}
|
|
|
|
|
|
VOID ThreadFini(THREADID tid, const CONTEXT *ctxt, INT32 code, VOID *v)
|
|
{
|
|
APP_THREAD_REPRESENTITVE * appThreadRepresentitive
|
|
= static_cast<APP_THREAD_REPRESENTITVE*>(PIN_GetThreadData(appThreadRepresentitiveKey, tid));
|
|
ASSERTX(appThreadRepresentitive != NULL);
|
|
|
|
printf ("Finished thread %d\n", tid);
|
|
fflush (stdout);
|
|
|
|
// wait for all my buffers to be processed
|
|
BOOL ok = appThreadRepresentitive->AllBuffersProcessed();
|
|
ASSERTX(ok);
|
|
|
|
appThreadRepresentitive->Statistics()->DumpNumBuffersFilled();
|
|
appThreadRepresentitive->Statistics()->IncorporateBufferStatistics(appThreadRepresentitive->FreeBufferListManager().Statistics());
|
|
overallStatistics.AccumulateAppThreadStatistics(appThreadRepresentitive->Statistics(), TRUE);
|
|
if (KnobStatistics)
|
|
{
|
|
appThreadRepresentitive->Statistics()->Dump();
|
|
}
|
|
|
|
delete appThreadRepresentitive;
|
|
|
|
PIN_SetThreadData(appThreadRepresentitiveKey, 0, tid);
|
|
}
|
|
|
|
/*!
|
|
* Process exit callback (unlocked).
|
|
*/
|
|
static VOID PrepareForFini(VOID *v)
|
|
{
|
|
printf ("PrepareForFini\n");
|
|
fflush (stdout);
|
|
|
|
if (fullBuffersListManager == NULL)
|
|
{
|
|
printf ("Internal threads were not yet started.\n");
|
|
fflush (stdout);
|
|
// Ensure that global buffer manager exists on process exit,
|
|
// otherwise it could be created later without exit notification.
|
|
fullBuffersListManager = GetFullBuffersListManager();
|
|
}
|
|
// Notify full buffers manager of application exit.
|
|
// It will cause all internal threads to exit once they finish processing of pending buffers.
|
|
fullBuffersListManager->NotifyExit();
|
|
|
|
// Wait until all internal threads exit
|
|
for (set<PIN_THREAD_UID>::iterator it = uidSet.begin(); it != uidSet.end(); ++it)
|
|
{
|
|
printf ("Waiting for exit of thread uid %d.\n", *it);
|
|
fflush (stdout);
|
|
INT32 threadExitCode;
|
|
BOOL waitStatus = PIN_WaitForThreadTermination(*it, PIN_INFINITE_TIMEOUT, &threadExitCode);
|
|
if (!waitStatus)
|
|
{
|
|
fprintf (stderr, "PIN_WaitForThreadTermination(secondary thread) failed");
|
|
}
|
|
}
|
|
}
|
|
|
|
static VOID Fini(INT32 code, VOID *v)
|
|
{
|
|
overallStatistics.DumpNumBuffersFilled();
|
|
if (fullBuffersListManager != NULL)
|
|
{
|
|
overallStatistics.IncorporateBufferStatistics(fullBuffersListManager->Statistics(), TRUE);
|
|
}
|
|
if (KnobStatistics)
|
|
{
|
|
overallStatistics.Dump();
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
* Record the thread's uid
|
|
*/
|
|
static VOID RecordToolThreadCreated(PIN_THREAD_UID threadUid)
|
|
{
|
|
BOOL insertStatus;
|
|
insertStatus = (uidSet.insert(threadUid)).second;
|
|
if (!insertStatus)
|
|
{
|
|
fprintf (stderr, "UID is not unique");
|
|
exit (-1);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Buffer Processing Thread's routine
|
|
*/
|
|
static VOID BufferProcessingThread(VOID * arg)
|
|
{
|
|
fullBuffersListManager = GetFullBuffersListManager();
|
|
THREADID myThreadId = PIN_ThreadId();
|
|
|
|
for (;;)
|
|
{
|
|
UINT64 numElements;
|
|
APP_THREAD_REPRESENTITVE *appThreadRepresentitive;
|
|
printf ("BufferProcessingThread tid %d GetBufferFromList\n", myThreadId);
|
|
fflush (stdout);
|
|
VOID* buf = fullBuffersListManager->GetBufferFromList(&numElements,
|
|
&appThreadRepresentitive, myThreadId);
|
|
if (buf == NULL)
|
|
{
|
|
printf ("BufferProcessingThread tid %d is exiting\n", myThreadId);
|
|
PIN_ExitThread(0); // Doesn't return
|
|
return;
|
|
}
|
|
printf ("BufferProcessingThread tid %d ProcessBuffer %p\n", myThreadId, buf);
|
|
fflush (stdout);
|
|
ProcessBuffer(buf, numElements, appThreadRepresentitive);
|
|
printf ("BufferProcessingThread tid %d return buffer %p to appThreadRepresentitive %p\n", myThreadId, buf, appThreadRepresentitive);
|
|
fflush (stdout);
|
|
appThreadRepresentitive->FreeBufferListManager().PutBufferOnList(buf, 0, appThreadRepresentitive, myThreadId);
|
|
//printf ("BufferProcessingThread tid %d appThreadRepresentitive %p now has %d buffers on it free list\n",
|
|
// myThreadId, appThreadRepresentitive, appThreadRepresentitive->FreeBufferListManager()->NumBuffersOnList());
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Print out help message.
|
|
*/
|
|
|
|
INT32 Usage()
|
|
{
|
|
printf( "This tool demonstrates the advanced use of the buffering API in conjunction \nwith internal-tool threads\n");
|
|
printf ("The following command line options are available:\n");
|
|
printf ("-num_buffers_per_app_thread <num> :number of buffers to allocate per application thread, default 3\n");
|
|
printf ("-num_pages_in_buffer <num> :number of (4096byte) pages allocated in each buffer, default 256\n");
|
|
printf ("-process_buffs <0 or 1> :specify 0 to disable processing of the buffers, default 1\n");
|
|
printf ("-num_processing_threads <num> :number of internal-tool buffer processing threads to create, default 3\n");
|
|
printf ("-lite_statistics <0 or 1> :specify 1 to enable lite statistics gathering, default 0\n");
|
|
printf ("-heavy_statistics <0 or 1> :specify 1 to enable heavy statistics gathering, default 0\n");
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*!
|
|
* The main procedure of the tool.
|
|
* This function is called when the application image is loaded but not yet started.
|
|
* @param[in] argc total number of elements in the argv array
|
|
* @param[in] argv array of command line arguments,
|
|
* including pin -t <toolname> -- ...
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
// Initialize PIN library. Print help message if -h(elp) is specified
|
|
// in the command line or the command line is invalid
|
|
if ( PIN_Init(argc,argv) )
|
|
{
|
|
return Usage();
|
|
}
|
|
|
|
// Define the buffer type to be used
|
|
// The first buffer of this definition is implicitly allocated to each application thread
|
|
// by Pin when the application thread starts. The rest of the buffers are explicitly
|
|
// allocated by the application thread when it has determined that it has an associated
|
|
// internal-tool thread that has started running - see call to PIN_AllocateBuffer
|
|
bufId = PIN_DefineTraceBuffer(sizeof(struct MEMREF), KnobNumPagesInBuffer, BufferFull, 0);
|
|
|
|
if (bufId == BUFFER_ID_INVALID)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
// Initialize Pin TLS slot used by the application threads to store and
|
|
// retrieve the APP_THREAD_REPRESENTITVE object that they own
|
|
appThreadRepresentitiveKey = PIN_CreateThreadDataKey(0);
|
|
|
|
// add an instrumentation function
|
|
TRACE_AddInstrumentFunction(Trace, 0);
|
|
|
|
// add callbacks
|
|
PIN_AddThreadStartFunction(ThreadStart, 0);
|
|
PIN_AddThreadFiniFunction(ThreadFini, 0);
|
|
PIN_AddFiniFunction(Fini, 0);
|
|
PIN_AddPrepareForFiniFunction(PrepareForFini, 0);
|
|
|
|
/* It is safe to create internal threads in the tool's main procedure and spawn new
|
|
* internal threads from existing ones. All other places, like Pin callbacks and
|
|
* analysis routines in application threads, are not safe for creating internal threads.
|
|
*/
|
|
// Spawn the tool's internal threads.
|
|
for (int i = 0; i < KnobNumProcessingThreads; i++)
|
|
{
|
|
PIN_THREAD_UID threadUid;
|
|
THREADID threadId =
|
|
PIN_SpawnInternalThread(BufferProcessingThread,
|
|
NULL,
|
|
0,
|
|
&threadUid);
|
|
if (threadId == INVALID_THREADID)
|
|
{
|
|
fprintf (stderr, "PIN_SpawnInternalThread(BufferProcessingThread) failed");
|
|
exit (-1);
|
|
}
|
|
printf ("created internal-tool BufferProcessingThread\n");
|
|
fflush (stdout);
|
|
RecordToolThreadCreated(threadUid);
|
|
}
|
|
|
|
printf ("buffer size in bytes 0x%x\n", KnobNumPagesInBuffer*4096);
|
|
overallStatistics.Init();
|
|
fflush (stdout);
|
|
|
|
// Start the program, never returns
|
|
PIN_StartProgram();
|
|
|
|
return 0;
|
|
}
|