Showing 1 post
As part of the project I'm currently involved in at university, I started (re)writing a Pin tool to gather run-time traces of applications parallelized with OpenMP. This tool has to support two modes: one to generate a single trace for the whole application and one to generate one trace per parallel region of the application.
In the initial versions of my rewrite, I followed the idea of the previous version of the tool: have a -split flag in the frontend that enables or disables the behavior described above. This flag was backed by an abstract class, Tracer, and two implementations: PlainTracer and SplittedTracer. The thread-initialization callback of the tool then allocated one of these objects for every new thread and the per-instruction injected code used a pointer to the interface to call the appropriate specialized instrumentation routine. This pretty much looked like this:
voidI knew from the very beginning that such an implementation was going to be inefficient due to the pointer dereference at each instruction and the vtable lookup for the correct virtual method implementation. However, it was a very quick way to move forward because I could reuse some small parts of the old implementation.
thread_start_callback(int tid, ...)
{
if (splitting)
tracers[tid] = new SplittedTracer();
else
tracers[tid] = new PlainTracer();
}
void
per_instruction_callback(...)
{
Tracer* t = tracers[PIN_ThreadId()];
t->instruction_callback(...);
}
template< class Tracer >What this design also does is force me to have two different Pin tools: one for plain tracing and another one for splitted tracing. Of course, I chose it to be this way because I'm not a fan of run-time options (the -split flag). Having two separate tools with well-defined, non-optional features makes testing much, much easier and... follows the Unix philosophy of having each tool do exactly one thing, but doing it right!
class BasicTool {
Tracer* tracers[MAX_THREADS];
Tracer* allocate_tracer(void) const = 0;
public:
Tracer*
get_tracer(int tid)
{
return tracers[tid];
}
};
class PlainTool : public BasicTool< PlainTracer > {
PlainTracer*
allocate_tracer(void) const
{
return new PlainTracer();
}
public:
...
} the_plain_tool;
// This is tool-specific, non-templated yet.
void
per_instruction_callback(...)
{
the_plain_tool.get_tracer(PIN_ThreadId()).instruction_callback(...);
}
May 7, 2009
·
Tags:
cxx, pin
Continue reading (about
3 minutes)