Using Pin++ To Author Highly Configurable
Pintools for PinDr. James H. Hill
Dept. Computer & Information ScienceIndiana University-Purdue University Indianapolis
[email protected]://www.cs.iupui.edu/~hilljhttp://github.com/SEDS/PinPP
PrefaceThis tutorial is designed to teach developers how to use Pin++ to author Pintools for Pin. We believe Pin is a great dynamic binary instrumentation tool, and one can write powerful analysis tools for it. We, however, have experienced difficulties using Pin in the past. We (and Pin++) aim is to alleviate the many problems we have encountered. In no way is this tutorial meant to negatively critique Pin. Instead, we want this tutorial (and Pin++) to complement Pin, and enable developers to use Pin in new, exciting ways as we are doing.
Happy Coding!
Tutorial Outline
What is All This “Pin” About?!
Creating a Traditional Pintool
Goals of Pin++
Creating our First Pintool using Pin++
Using Data In Our Callbacks
Requesting Contextual Information in Callbacks
Others Features of Pin++
Dynamic Binary Instrumentation
Dynamic Binary Instrumentation (DBI) is a method of analyzing the behavior of a binary application at runtime through the injection of instrumentation code. This instrumentation code executes as part of the normal instruction stream after being injected. In most cases, the instrumentation code will be entirely transparent to the application that it's been injected to. Analyzing an application at runtime makes it possible to gain insight into the behavior and state of an application at various points in execution.
PinPin is a dynamic binary instrumentation tool
http://www.pintool.org
Supported PlatformsLinuxWindowsMacOS XAndroidEtc…
Counting Instructions (1/3)#include <iostream>#include <fstream>#include "pin.H"
ofstream OutFile;
// The running count of instructions is kept here// make it static to help the compiler optimize docountstatic UINT64 icount = 0;
// This function is called before every instruction is executedVOID docount(void) { icount++; } // Pin calls this function every time a new instruction is encounteredVOID Instruction(INS ins, VOID *v) { // Insert a call to docount before every instruction, no arguments are passed INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);}
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "inscount.out", "specify output file name");
https://software.intel.com/sites/landingpage/pintool/docs/71313/Pin/html/index.html#SimpleCount
Counting Instructions (1/3)#include <iostream>#include <fstream>#include "pin.H"
ofstream OutFile;
// The running count of instructions is kept here// make it static to help the compiler optimize docountstatic UINT64 icount = 0;
// This function is called before every instruction is executedVOID docount(void) { icount++; } // Pin calls this function every time a new instruction is encounteredVOID Instruction(INS ins, VOID *v) { // Insert a call to docount before every instruction, no arguments are passed INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);}
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "inscount.out", "specify output file name");
https://software.intel.com/sites/landingpage/pintool/docs/71313/Pin/html/index.html#SimpleCount
Analysis function that increments a
counter variable
Counting Instructions (1/3)#include <iostream>#include <fstream>#include "pin.H"
ofstream OutFile;
// The running count of instructions is kept here// make it static to help the compiler optimize docountstatic UINT64 icount = 0;
// This function is called before every instruction is executedVOID docount(void) { icount++; } // Pin calls this function every time a new instruction is encounteredVOID Instruction(INS ins, VOID *v) { // Insert a call to docount before every instruction, no arguments are passed INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);}
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "inscount.out", "specify output file name");
https://software.intel.com/sites/landingpage/pintool/docs/71313/Pin/html/index.html#SimpleCount
Function that adds analysis function before each new
instruction
Counting Instructions (1/3)#include <iostream>#include <fstream>#include "pin.H"
ofstream OutFile;
// The running count of instructions is kept here// make it static to help the compiler optimize docountstatic UINT64 icount = 0;
// This function is called before every instruction is executedVOID docount(void) { icount++; } // Pin calls this function every time a new instruction is encounteredVOID Instruction(INS ins, VOID *v) { // Insert a call to docount before every instruction, no arguments are passed INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);}
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "inscount.out", "specify output file name");
https://software.intel.com/sites/landingpage/pintool/docs/71313/Pin/html/index.html#SimpleCount
Pintool arguments (i.e., knobs), similar to command-line
arguments
Counting Instructions (2/3)// This function is called when the application exitsVOID Fini(INT32 code, VOID *v){ // Write to a file since cout and cerr maybe closed by the application OutFile.setf(ios::showbase); OutFile << "Count " << icount << endl; OutFile.close();}
/* ===================================================================== *//* Print Help Message *//* ===================================================================== */
INT32 Usage(){ cerr << "This tool counts the number of dynamic instructions executed" << endl; cerr << endl << KNOB_BASE::StringKnobSummary() << endl; return -1;}
https://software.intel.com/sites/landingpage/pintool/docs/71313/Pin/html/index.html#SimpleCount
Counting Instructions (2/3)// This function is called when the application exitsVOID Fini(INT32 code, VOID *v){ // Write to a file since cout and cerr maybe closed by the application OutFile.setf(ios::showbase); OutFile << "Count " << icount << endl; OutFile.close();}
/* ===================================================================== *//* Print Help Message *//* ===================================================================== */
INT32 Usage(){ cerr << "This tool counts the number of dynamic instructions executed" << endl; cerr << endl << KNOB_BASE::StringKnobSummary() << endl; return -1;}
https://software.intel.com/sites/landingpage/pintool/docs/71313/Pin/html/index.html#SimpleCount
A “finalize” function that prints the final count
Counting Instructions (3/3)/* ===================================================================== *//* Main *//* ===================================================================== *//* argc, argv are the entire command line: pin -t <toolname> -- ... *//* ===================================================================== */
int main(int argc, char * argv[]){ // Initialize pin if (PIN_Init(argc, argv)) return Usage();
OutFile.open(KnobOutputFile.Value().c_str());
// Register Instruction to be called to instrument instructions INS_AddInstrumentFunction(Instruction, 0);
// Register Fini to be called when the application exits PIN_AddFiniFunction(Fini, 0); // Start the program, never returns PIN_StartProgram(); return 0;}
https://software.intel.com/sites/landingpage/pintool/docs/71313/Pin/html/index.html#SimpleCount
Counting Instructions (3/3)/* ===================================================================== *//* Main *//* ===================================================================== *//* argc, argv are the entire command line: pin -t <toolname> -- ... *//* ===================================================================== */
int main(int argc, char * argv[]){ // Initialize pin if (PIN_Init(argc, argv)) return Usage();
OutFile.open(KnobOutputFile.Value().c_str());
// Register Instruction to be called to instrument instructions INS_AddInstrumentFunction(Instruction, 0);
// Register Fini to be called when the application exits PIN_AddFiniFunction(Fini, 0); // Start the program, never returns PIN_StartProgram(); return 0;}
https://software.intel.com/sites/landingpage/pintool/docs/71313/Pin/html/index.html#SimpleCount
Required bootstrapping code for
the pintool
Developer Challenges1. It is hard to see the design of a Pintool
2. There are many hidden complexities in a Pintool
3. It is hard to reuse components of a Pintool
4. Constant reinvention of required behavior in a Pintool
5. Bad software engineering practices
Seeing the Designofstream OutFile ("inscount.out");static UINT64 icount = 0;
VOID docount() { icount++; }
VOID Instruction(INS ins, VOID *v) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);}
VOID Fini(INT32 code, VOID *v) { OutFile.setf(ios::showbase); OutFile << "Count " << icount << endl; OutFile.close();}
int main(int argc, char * argv[]) { if (PIN_Init(argc, argv)) return 1; INS_AddInstrumentFunction(Instruction, 0); PIN_AddFiniFunction(Fini, 0); PIN_StartProgram(); return 0;}
Based on this code:• What is the design
of a Pintool?• What entities are
involved in the Pintool, and their relations?
Hidden ComplexitiesVOID docount() { icount++; }
VOID Instruction(INS ins, VOID *v) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);}
Developer must remember the arguments used to register analysis function must match the number of arguments, and type, expected by the analysis function.
Reuse?ofstream OutFile ("inscount.out");static UINT64 icount = 0;
VOID docount() { icount++; }
VOID Instruction(INS ins, VOID *v) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);}
VOID Fini(INT32 code, VOID *v) { OutFile.setf(ios::showbase); OutFile << "Count " << icount << endl; OutFile.close();}
int main(int argc, char * argv[]) { if (PIN_Init(argc, argv)) return 1; INS_AddInstrumentFunction(Instruction, 0); PIN_AddFiniFunction(Fini, 0); PIN_StartProgram(); return 0;}
Tight coupling makes it hard to use any part of Pintool in another Pintool, especially with the global variable! This approach is common in Pintools.
Continuous Reinventionstatic UINT64 icount = 0;
VOID docount() { icount++; }
int main(int argc, char * argv[]) { if (PIN_Init(argc, argv)) return 1; INS_AddInstrumentFunction(Instruction, 0); PIN_AddFiniFunction(Fini, 0); PIN_StartProgram(); return 0;}
Most bootstrapping code is similar in all Pintools.
Lack of reuse also leads to reinvention of core logic.
I want to use this again, must I reinvent
it?
Here lies the reinvented
bootstrapping code
Bad Software Engineer!ofstream OutFile ("inscount.out");static UINT64 icount = 0;
VOID docount() { icount++; }
Current mechanisms guide developers to use bad software engineering practices, like using global variables!
Pin++ Aims to…Be 100% object-oriented
Use design patterns to promote reuse and reduce complexity of Pintools
Uses template-metaprogramming to reduce potential development errors and optimize the performance of a Pintool at compile time
Promote reuse of different components in a Pintool
Codify many requirements of a Pintool so developers to not have to re-implement them for each and every tool e.g., bootstrapping, initialization, registration, & etc
Pin++ Framework
Pin
Pintool
Pin
Pin++
Pintool
Pin++ is a framework that sits between Pin, and the application logic of a Pintool
And We Achieved…
James H. Hill and Dennis C. Feiock. 2014. Pin++: an object-oriented framework for writing pintools. In Proceedings of the 2014 International Conference on Generative Programming: Concepts and Experiences (GPCE 2014). ACM, New York, NY, USA, 133-141
And…
James H. Hill and Dennis C. Feiock. 2014. Pin++: an object-oriented framework for writing pintools. In Proceedings of the 2014 International Conference on Generative Programming: Concepts and Experiences (GPCE 2014). ACM, New York, NY, USA, 133-141
And…
James H. Hill and Dennis C. Feiock. 2014. Pin++: an object-oriented framework for writing pintools. In Proceedings of the 2014 International Conference on Generative Programming: Concepts and Experiences (GPCE 2014). ACM, New York, NY, USA, 133-141
And…
James H. Hill and Dennis C. Feiock. 2014. Pin++: an object-oriented framework for writing pintools. In Proceedings of the 2014 International Conference on Generative Programming: Concepts and Experiences (GPCE 2014). ACM, New York, NY, USA, 133-141
Creating Our First Pintool using Pin++
https://github.com/SEDS/PinPP/wiki/Creating-a-Pintool-using-Pin
System RequirementsPerl, on Windows we suggest ActivePerl
GIT
Pin (build 61206 to 67254)
Makefile, Project, Workspace Creator (master) from its GitHub repository.https://github.com/DOCGroup/MPC
C++11 compliant compiler for examples*
Pin++ is has been tested on Windows, Linux, and MacOS X
* Coming soon. C++11 compliant compiler will be need for all of Pin++.
Environment Setup (1/2)Pin
PIN_ROOT set to location of Pin
PATH (and LD_LIBRARY_PATH or DYLD_LIBRARY_PATH) must be set correctly
Pin++
PINPP_ROOT set to location of Pin++
$PINPP_ROOT/bin in PATH
$PINPP_ROOT/lib in library path
Environment Setup (2/2)Windows
PINPP_ROOT=[location of Pin++]
PATH=%PATH%;%PINPP_ROOT%\bin;%PINPP_ROOT%\lib
Linux
PINPP_ROOT=[location of Pin++]
PATH=$PATH:$PINPP_ROOT/bin
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PINPP_ROOT/lib
MacOS X Users. Use the Linux approach above, but replace LD_LIBRARY_PATH with DYLD_LIBRARY_PATH
Building Pin++We use Makefile, Workspace, Project Creator (MPC)
to assist with building Pin++ on different platformsMPC allows us to write generic project files that are
used to generate build scripts for the target platformE.g., Makefiles, Visual Studio Workspaces, Eclipse,
etc.https://github.com/DOCGroup/MPC
Build Command
%> mwc.pl –type [type] pin++.mwc
%> open/build generated workspace
100% Object-Oriented
Everything in Pin++ is an object, which helps us see the design of the Pintool and address other challenges
e.g., reuse, tight coupling, etc.
Callback ObjectThe callback object is responsible for performing
analysis at the instrumented location
class docount : public OASIS::Pin::Callback <docount (void)> {public: docount (void) : count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
UINT64 count (void) const { return this->count_; }
private: UINT64 count_;};
Callback ObjectThe callback object is responsible for performing
analysis at the instrumented location
class docount : public OASIS::Pin::Callback <docount (void)> {public: docount (void) : count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
UINT64 count (void) const { return this->count_; }
private: UINT64 count_;};
C function is now an object…
Callback ObjectThe callback object is responsible for performing
analysis at the instrumented location
class docount : public OASIS::Pin::Callback <docount (void)> {public: docount (void) : count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
UINT64 count (void) const { return this->count_; }
private: UINT64 count_;};
Parameterized by self (i.e., Curiously recurring template pattern); uses static
polymorphism instead of dynamic polymorphism
Callback ObjectThe callback object is responsible for performing
analysis at the instrumented location
class docount : public OASIS::Pin::Callback <docount (void)> {public: docount (void) : count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
UINT64 count (void) const { return this->count_; }
private: UINT64 count_;};
Function pointer parameter determines signature of analysis
function
Callback ObjectThe callback object is responsible for performing
analysis at the instrumented location
class docount : public OASIS::Pin::Callback <docount (void)> {public: docount (void) : count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
UINT64 count (void) const { return this->count_; }
private: UINT64 count_;};
All callbacks must implement handle_analyze method; parameters
determined by Callback template parameter
Callback ObjectThe callback object is responsible for performing
analysis at the instrumented location
class docount : public OASIS::Pin::Callback <docount (void)> {public: docount (void) : count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
UINT64 count (void) const { return this->count_; }
private: UINT64 count_;};
Allows for type deduction and safety; We will discuss this later.
Callback ObjectThe callback object is responsible for performing
analysis at the instrumented location
class docount : public OASIS::Pin::Callback <docount (void)> {public: docount (void) : count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
UINT64 count (void) const { return this->count_; }
private: UINT64 count_;};
No more global variables; callback object is “self-
contained”
Callback ObjectThe callback object is responsible for performing
analysis at the instrumented location
class docount : public OASIS::Pin::Callback <docount (void)> {public: docount (void) : count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
UINT64 count (void) const { return this->count_; }
private: UINT64 count_;}; Callback can have
configuration/accessor methods, if needed
Instrumentation ObjectThe instrument object is a Bridge between the tool callback
object. The instrument inserts callback objects.
class Instruction : public OASIS::Pin::Instruction_Instrument <Instruction>{public: void handle_instrument (const OASIS::Pin::Ins & ins) { this->callback_.insert (IPOINT_BEFORE, ins); }
UINT64 count (void) const { return this->callback_.count (); }
private: docount callback_; // Analysis routine for instrument};
Instrument is an object, base class parameterized by sublcass
Instrumentation ObjectThe instrument object is a Bridge between the tool callback
object. The instrument inserts callback objects.
class Instruction : public OASIS::Pin::Instruction_Instrument <Instruction>{public: void handle_instrument (const OASIS::Pin::Ins & ins) { this->callback_.insert (IPOINT_BEFORE, ins); }
UINT64 count (void) const { return this->callback_.count (); }
private: docount callback_; // Analysis routine for instrument};
All instruments must implement handle_instrument method.
Instrumentation ObjectThe instrument object is a Bridge between the tool callback
object. The instrument inserts callback objects.
class Instruction : public OASIS::Pin::Instruction_Instrument <Instruction>{public: void handle_instrument (const OASIS::Pin::Ins & ins) { this->callback_.insert (IPOINT_BEFORE, ins); }
UINT64 count (void) const { return this->callback_.count (); }
private: docount callback_; // Analysis routine for instrument};
Instrument contains one or more callback objects
Instrumentation ObjectThe instrument object is a Bridge between the tool callback
object. The instrument inserts callback objects.
class Instruction : public OASIS::Pin::Instruction_Instrument <Instruction>{public: void handle_instrument (const OASIS::Pin::Ins & ins) { this->callback_.insert (IPOINT_BEFORE, ins); }
UINT64 count (void) const { return this->callback_.count (); }
private: docount callback_; // Analysis routine for instrument};
Callback object have insert method for inserting callback at
target
Instrumentation ObjectThe instrument object is a Bridge between the tool callback
object. The instrument inserts callback objects.
class Instruction : public OASIS::Pin::Instruction_Instrument <Instruction>{public: void handle_instrument (const OASIS::Pin::Ins & ins) { this->callback_.insert (IPOINT_BEFORE, ins); }
UINT64 count (void) const { return this->callback_.count (); }
private: docount callback_; // Analysis routine for instrument}; Instrument can have configuration/accessor methods,
if needed
Instrumentation ObjectThe instrument object is a Bridge between the tool callback
object. The instrument inserts callback objects.
class Instruction : public OASIS::Pin::Instruction_Instrument <Instruction>{public: void handle_instrument (const OASIS::Pin::Ins & ins) { this->callback_.insert (IPOINT_BEFORE, ins); }
UINT64 count (void) const { return this->callback_.count (); }
private: docount callback_; // Analysis routine for instrument};
Instrument is self-contained, and can be reused across Pintools since it contains callbacks to make it work correctly
Instrumentation Types
Type Handle Method
Instruction_Instrument handle_instrument (const Ins &)
Trace_Instrument handle_instrument (const Trace &)
Routine_Instrument handle_instrument (const Routine &)
Image_Instrument handle_instrument (const Image &)
Each instrumentation type corresponds to an Pin type that supports instrumentation i.e., INS, TRACE, RTN, and IMAGE
Tool ObjectThe tool object bootstraps the Pintool & registers
its instrumentsclass inscount : public OASIS::Pin::Tool <inscount>{public: inscount (void) { this->enable_fini_callback (); }
void handle_fini (INT32 code) { std::ofstream fout ("inscount.out"); fout.setf (ios::showbase); fout << "Count " << this->instruction_.count () << std::endl;
fout.close (); }
private: Instruction instruction_;};
Tool ObjectThe tool object bootstraps the Pintool & registers
its instrumentsclass inscount : public OASIS::Pin::Tool <inscount>{public: inscount (void) { this->enable_fini_callback (); }
void handle_fini (INT32 code) { std::ofstream fout ("inscount.out"); fout.setf (ios::showbase); fout << "Count " << this->instruction_.count () << std::endl;
fout.close (); }
private: Instruction instruction_;};
Tool base class is parameterized by the
sublcass
Tool ObjectThe tool object bootstraps the Pintool & registers
its instrumentsclass inscount : public OASIS::Pin::Tool <inscount>{public: inscount (void) { this->enable_fini_callback (); }
void handle_fini (INT32 code) { std::ofstream fout ("inscount.out"); fout.setf (ios::showbase); fout << "Count " << this->instruction_.count () << std::endl;
fout.close (); }
private: Instruction instruction_;};
Register tool-specific notifications
Tool ObjectThe tool object bootstraps the Pintool & registers
its instrumentsclass inscount : public OASIS::Pin::Tool <inscount>{public: inscount (void) { this->enable_fini_callback (); }
void handle_fini (INT32 code) { std::ofstream fout ("inscount.out"); fout.setf (ios::showbase); fout << "Count " << this->instruction_.count () << std::endl;
fout.close (); }
private: Instruction instruction_;};
Implement tool-specific notifications
Tool ObjectThe tool object bootstraps the Pintool & registers
its instrumentsclass inscount : public OASIS::Pin::Tool <inscount>{public: inscount (void) { this->enable_fini_callback (); }
void handle_fini (INT32 code) { std::ofstream fout ("inscount.out"); fout.setf (ios::showbase); fout << "Count " << this->instruction_.count () << std::endl;
fout.close (); }
private: Instruction instruction_;};
Instantiating instrument automatically registers its with Pin
Tool ObjectThe tool object bootstraps the Pintool & registers
its instrumentsclass inscount : public OASIS::Pin::Tool <inscount>{public: inscount (void) { this->enable_fini_callback (); }
void handle_fini (INT32 code) { std::ofstream fout ("inscount.out"); fout.setf (ios::showbase); fout << "Count " << this->instruction_.count () << std::endl;
fout.close (); }
private: Instruction instruction_;};
Now, the entire Pintool is “self-contained” and can be reused in-part or in-whole
Required main function We use macros to create the define the main
function, and create the Tool objectDECLARE_PINTOOLDELCARE_PINTOOL_PROBED
// Declare the inscount PintoolDECLARE_PINTOOL (inscount)
Building Your PintoolMPC is also used to build the Pintool
You must use one of the following base projects:oasis_pintool — regular Pintooloasis_static_pintool — static Pintool
// pintool.mwcworkspace { cmdline += -include $PINPP_ROOT/MPC/config}
// inscount0.mpcproject (inscount0) : oasis_pintool { sharedname = inscount0 dllout = ./
Source_Files { inscount0.cpp }}
Building Your PintoolMPC is also used to build the Pintool
You must use one of the following base projects:oasis_pintool — regular Pintooloasis_static_pintool — static Pintool
// pintool.mwcworkspace { cmdline += -include $PINPP_ROOT/MPC/config}
The MPC workspace tells MPC where to locate Pin++ base
projects
// inscount0.mpcproject (inscount0) : oasis_pintool { sharedname = inscount0 dllout = ./
Source_Files { inscount0.cpp }}
Building Your PintoolMPC is also used to build the Pintool
You must use one of the following base projects:oasis_pintool — regular Pintooloasis_static_pintool — static Pintool
// pintool.mwcworkspace { cmdline += -include $PINPP_ROOT/MPC/config}
The MPC project contains the settings for building the Pintool
// inscount0.mpcproject (inscount0) : oasis_pintool { sharedname = inscount0 dllout = ./
Source_Files { inscount0.cpp }}
Building Your PintoolMPC is also used to build the Pintool
You must use one of the following base projects:oasis_pintool — regular Pintooloasis_static_pintool — static Pintool
// pintool.mwcworkspace { cmdline += -include $PINPP_ROOT/MPC/config}
The base project for the project configuration
// inscount0.mpcproject (inscount0) : oasis_pintool { sharedname = inscount0 dllout = ./
Source_Files { inscount0.cpp }}
Building Your PintoolMPC is also used to build the Pintool
You must use one of the following base projects:oasis_pintool — regular Pintooloasis_static_pintool — static Pintool
// inscount0.mpcproject (inscount0) : oasis_pintool { sharedname = inscount0 dllout = ./
Source_Files { inscount0.cpp }}
// pintool.mwcworkspace { cmdline += -include $PINPP_ROOT/MPC/config}
sharedname means we are building a shared library; expected
by Pin
Running Your PintoolLinux
%> pin –t inscount.so -- ls
MacOS X
%> pin –t inscount.dylib -- ls
Windows
%> pin –t inscount.dll -- dir .
MPC manages the extensions of the shared library on each platform
Analysis Function w/ Data
/ The running count of instructions is kept here// make it static to help the compiler optimize docountstatic UINT64 icount = 0;
// This function is called before every blockVOID docount(UINT32 c) { icount += c; }
// Pin calls this function every time a new basic block is encountered// It inserts a call to docountVOID Trace (TRACE trace, VOID *v) { // Visit every basic block in the trace for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) { // Insert a call to docount before every bbl, passing number of instructions BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)docount, IARG_UINT32, BBL_NumIns(bbl), IARG_END); }}
Analysis tools can accept data
Analysis tools can accept data/ The running count of instructions is kept here// make it static to help the compiler optimize docountstatic UINT64 icount = 0;
// This function is called before every blockVOID docount(UINT32 c) { icount += c; }
// Pin calls this function every time a new basic block is encountered// It inserts a call to docountVOID Trace (TRACE trace, VOID *v) { // Visit every basic block in the trace for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) { // Insert a call to docount before every bbl, passing number of instructions BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)docount, IARG_UINT32, BBL_NumIns(bbl), IARG_END); }}
Analysis Function w/ Data
The analysis function is expecting a 32-bit integer
Analysis tools can accept data/ The running count of instructions is kept here// make it static to help the compiler optimize docountstatic UINT64 icount = 0;
// This function is called before every blockVOID docount(UINT32 c) { icount += c; }
// Pin calls this function every time a new basic block is encountered// It inserts a call to docountVOID Trace (TRACE trace, VOID *v) { // Visit every basic block in the trace for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) { // Insert a call to docount before every bbl, passing number of instructions BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)docount, IARG_UINT32, BBL_NumIns(bbl), IARG_END); }}
Analysis Function w/ Data
Insert call specifies callback wants an integer
data
The Pintool is registering a constant with Pine.g., IARG_UINT32, IARG_PTR, IARG_BOOL,
IARG_ADDRINT
Pin must manage this state, and pass to analysis function
Somewhat limits the kind of data that can be registered with analysis function IARG_PTR is void *, but not type safe!
// Insert a call to docount before every bbl, passing number of instructionsBBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)docount, IARG_UINT32, BBL_NumIns(bbl), IARG_END);
Potential Problems
Callbacks w/ DataWe use configuration methods to configure data
in callbacksclass docount : public OASIS::Pin::Callback <docount (void)>{public: docount (void) : count_ (0), ins_count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
void ins_count (UINT64 count) { this->ins_count_ = count; }
UINT64 count (void) const { return this->ins_count_ * this->count_; }
private: UINT64 count_; UINT64 ins_count_;};
Callbacks w/ DataWe use configuration methods to configure data
in callbacksclass docount : public OASIS::Pin::Callback <docount (void)>{public: docount (void) : count_ (0), ins_count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
void ins_count (UINT64 count) { this->ins_count_ = count; }
UINT64 count (void) const { return this->ins_count_ * this->count_; }
private: UINT64 count_; UINT64 ins_count_;};
Data associated with callback is stored in callback, not in Pin
Callbacks w/ DataWe use configuration methods to configure data
in callbacksclass docount : public OASIS::Pin::Callback <docount (void)>{public: docount (void) : count_ (0), ins_count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
void ins_count (UINT64 count) { this->ins_count_ = count; }
UINT64 count (void) const { return this->ins_count_ * this->count_; }
private: UINT64 count_; UINT64 ins_count_;};
We continue incrementing count as
usual
Callbacks w/ DataWe use configuration methods to configure data
in callbacksclass docount : public OASIS::Pin::Callback <docount (void)>{public: docount (void) : count_ (0), ins_count_ (0) { }
void handle_analyze (void) { ++ this->count_; }
void ins_count (UINT64 count) { this->ins_count_ = count; }
UINT64 count (void) const { return this->ins_count_ * this->count_; }
private: UINT64 count_; UINT64 ins_count_;};
We calculate total count when requested
Instruments w/ DataWe must dynamically allocate a new callback for
each new insertionclass Trace : public OASIS::Pin::Trace_Instrument <Trace> {public: void handle_instrument (const OASIS::Pin::Trace & trace){ OASIS::Pin::Buffer <docount> item (trace.num_bbl ()); item_type::iterator callback = item.begin ();
for (const OASIS::Pin::Bbl & bbl : trace) { callback->ins_count (bbl.ins_count ()); callback->insert (IPOINT_BEFORE, bbl);
++ callback; }
this->traces_.push_back (item); }
// ...private: std::list <OASIS::Pin::Buffer <docount>> traces_;};
Allocate a buffer of docount callback objects
Instruments w/ DataWe must dynamically allocate a new callback for
each new insertionclass Trace : public OASIS::Pin::Trace_Instrument <Trace> {public: void handle_instrument (const OASIS::Pin::Trace & trace){ OASIS::Pin::Buffer <docount> item (trace.num_bbl ()); item_type::iterator callback = item.begin ();
for (const OASIS::Pin::Bbl & bbl : trace) { callback->ins_count (bbl.ins_count ()); callback->insert (IPOINT_BEFORE, bbl);
++ callback; }
this->traces_.push_back (item); }
// ...private: std::list <OASIS::Pin::Buffer <docount>> traces_;};
Iterate over each BBL in the Trace, and configure the callback object
Instruments w/ DataWe must dynamically allocate a new callback for
each new insertionclass Trace : public OASIS::Pin::Trace_Instrument <Trace> {public: void handle_instrument (const OASIS::Pin::Trace & trace){ OASIS::Pin::Buffer <docount> item (trace.num_bbl ()); item_type::iterator callback = item.begin ();
for (const OASIS::Pin::Bbl & bbl : trace) { callback->ins_count (bbl.ins_count ()); callback->insert (IPOINT_BEFORE, bbl);
++ callback; }
this->traces_.push_back (item); }
// ...private: std::list <OASIS::Pin::Buffer <docount>> traces_;};
Insert the callback object before the BBL
Instruments w/ DataWe must dynamically allocate a new callback for
each new insertionclass Trace : public OASIS::Pin::Trace_Instrument <Trace> {public: void handle_instrument (const OASIS::Pin::Trace & trace){ OASIS::Pin::Buffer <docount> item (trace.num_bbl ()); item_type::iterator callback = item.begin ();
for (const OASIS::Pin::Bbl & bbl : trace) { callback->ins_count (bbl.ins_count ()); callback->insert (IPOINT_BEFORE, bbl);
++ callback; }
this->traces_.push_back (item); }
// ...private: std::list <OASIS::Pin::Buffer <docount>> traces_;};
Save the callbacks for recall later
class Trace : public OASIS::Pin::Trace_Instrument <Trace> {public: // ... UINT64 count (void) const { UINT64 count = 0;
for (auto trace : this->traces_) for (auto item : trace) count += item.count ();
return count; }
private: std::list <OASIS::Pin::Buffer <docount>> traces_;};
Instruments w/ DataWe must dynamically allocate a new callback for
each new insertion
class Trace : public OASIS::Pin::Trace_Instrument <Trace> {public: // ... UINT64 count (void) const { UINT64 count = 0;
for (auto trace : this->traces_) for (auto item : trace) count += item.count ();
return count; }
private: std::list <OASIS::Pin::Buffer <docount>> traces_;};
Instruments w/ DataWe must dynamically allocate a new callback for
each new insertion
Iterate over callbacks to get current count
Design Question?Does Pin really need the data arguments (e.g.,
IARG_UINT32, IARG_PTR, IARG_BOOL, and IARG_ADDRINT) if we are able to store the data in a callback object?
The allocations performed in the instrument are similar to the “potential” allocations used to manage the data in Pin
This approach would not require Pin to worry about application-level details.
But, it does require an extra allocation…
Requesting Contextual Information in Callbacks
https://github.com/SEDS/PinPP/wiki/Requesting-contextual-data-in-callbacks
Contextual DataContext data is anything about the program
under instrumentation, or the machine running the program, needed by the analysis function to perform its analysisThread idProcess idRegister valuesArguments to a functionReturn valueEtc.
Traditional Approach to Get Context Data
Contextual data is requested similar to how you associate data with an analysis function in Pin i.e., the IARG_* parameters for insertion
// This code is from itrace.cpp in the Pin manual examples.
// This function is called before every instruction is executed// and prints the IPVOID printip (VOID *ip) { fprintf(trace, "%p\n", ip); }
// Pin calls this function every time a new instruction is encounteredVOID Instruction(INS ins, VOID *v) { // Insert a call to printip before every instruction, and pass it the IP INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)printip, IARG_INST_PTR, IARG_END);}
Traditional Approach to Get Context Data
Contextual data is requested similar to how you associate data with an analysis function in Pin i.e., the IARG_* parameters for insertion
// This code is from itrace.cpp in the Pin manual examples.
// This function is called before every instruction is executed// and prints the IPVOID printip (VOID *ip) { fprintf(trace, "%p\n", ip); }
// Pin calls this function every time a new instruction is encounteredVOID Instruction(INS ins, VOID *v) { // Insert a call to printip before every instruction, and pass it the IP INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)printip, IARG_INST_PTR, IARG_END);}
We need the instruction pointer (IP)
Traditional Approach to Get Context Data
Contextual data is requested similar to how you associate data with an analysis function in Pin i.e., the IARG_* parameters for insertion
// This code is from itrace.cpp in the Pin manual examples.
// This function is called before every instruction is executed// and prints the IPVOID printip (VOID *ip) { fprintf(trace, "%p\n", ip); }
// Pin calls this function every time a new instruction is encounteredVOID Instruction(INS ins, VOID *v) { // Insert a call to printip before every instruction, and pass it the IP INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)printip, IARG_INST_PTR, IARG_END);}
So, we request it…
Potential ProblemsThe analysis function must have the correct
number of parameters, and have the correct type
Some contextual data require extra arguments, and making sure the extra argument is included at insertion is not guaranteed
Requesting different context data type requires the developer to make the necessary changes to the analysis function parameters
Context Data in Pin++Context data in Pin++ is as simple as adding
parameters to the function pointer that parameterizes the Callback object
class printip : public OASIS::Pin::Callback < printip (OASIS::Pin::ARG_INST_PTR) >{public: printip (FILE * file) : file_ (file) { }
void handle_analyze (param_type1 addr) { ::fprintf (this->file_, "0x%p\n", addr); }
private: FILE * file_;};
Context Data in Pin++Context data in Pin++ is as simple as adding
parameters to the function pointer that parameterizes the Callback object
class printip : public OASIS::Pin::Callback < printip (OASIS::Pin::ARG_INST_PTR) >{public: printip (FILE * file) : file_ (file) { }
void handle_analyze (param_type1 addr) { ::fprintf (this->file_, "0x%p\n", addr); }
private: FILE * file_;};
This argument means the callback is expecting instruction pointer
Context Data in Pin++Context data in Pin++ is as simple as adding
parameters to the function pointer that parameterizes the Callback object
class printip : public OASIS::Pin::Callback < printip (OASIS::Pin::ARG_INST_PTR) >{public: printip (FILE * file) : file_ (file) { }
void handle_analyze (param_type1 addr) { ::fprintf (this->file_, "0x%p\n", addr); }
private: FILE * file_;};
Callbacks have typedefs that manage the correct type for each parameter
Context Types in Pin++Pin++ has its own argument type system that
maps to the corresponding Pin IARG_* types
Allows Pin++ to control what arguments it supports as context datae.g., non-data arguments
Context Data w/ ParamsSome context data requires an extra parameter
at insertion time
// with PinRTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)Arg1Before, IARG_ADDRINT, MALLOC, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END);
// with Pin++this->arg1_before_.insert (IPOINT_BEFORE, rtn, 0);
Context Data w/ ParamsSome context data requires an extra parameter
at insertion time
// with PinRTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)Arg1Before, IARG_ADDRINT, MALLOC, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END);
// with Pin++this->arg1_before_.insert (IPOINT_BEFORE, rtn, 0);
The extra parameters go after the target object
Context Data w/ ParamsSome context data requires an extra parameter
at insertion time
// with PinRTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)Arg1Before, IARG_ADDRINT, MALLOC, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END);
// with Pin++this->arg1_before_.insert (IPOINT_BEFORE, rtn, 0);
If any extra parameter is missing, there will be compilation errors
Range-based For LoopsRange-based for-loops are easy way to iterate
over a list of valuesNow available in C++11
Supported with Image, Section, Routine, Bbl, and Symbol in Pin++
void handle_instrument (const OASIS::Pin::Image & img){ int count = 0;
for (OASIS::Pin::Section & section : img) { for (OASIS::Pin::Routine & rtn : section) { OASIS::Pin::Routine_Guard guard (rtn); for (OASIS::Pin::Ins & ins : rtn) ++ count; } }
std::cerr << "Image " << img.name () << " has " << count << " instructions" << std::endl;}
To Analyze… Not to Analyze?
Pin supports conditional analysis*_InsertIfCall*_InsertThenCall
Developer must call both functions (in order) to enable conditional analysis
// CountDown() is called for every instruction executedINS_InsertIfCall(ins, IPOINT_BEFORE, (AFUNPTR)CountDown, IARG_END); // PrintIp() is called only when the last CountDown() returns a non-zero value.INS_InsertThenCall(ins, IPOINT_BEFORE, (AFUNPTR)PrintIp, IARG_INST_PTR, IARG_END);
Conditional Analysis in Pin++
Conditional callback has method that determines if Pin is to analysis target
class countdown : public OASIS::Pin::Conditional_Callback < countdown (void) >{public: countdown (void) : counter_ (1) { }
void reset_counter (INT32 counter) { this->counter_ = counter; }
bool do_next (void) { return (-- this->counter_ == 0); }
private: INT32 counter_;};
Conditional Analysis in Pin++
Conditional callback has method that determines if Pin is to analysis target
class countdown : public OASIS::Pin::Conditional_Callback < countdown (void) >{public: countdown (void) : counter_ (1) { }
void reset_counter (INT32 counter) { this->counter_ = counter; }
bool do_next (void) { return (-- this->counter_ == 0); }
private: INT32 counter_;}; All conditional callbacks must
implement the do_next method
Conditional Analysis in Pin++
Conditional callback has method that determines if Pin is to analysis target
class Instrument : public OASIS::Pin::Instruction_Instrument <Instrument>{public: Instrument (FILE * file) : printip_ (file, countdown_) { }
void handle_instrument (const OASIS::Pin::Ins & ins) { this->printip_[this->countdown_].insert (IPOINT_BEFORE, ins); }
private: /// The countdown guard protecting \a printip_. countdown countdown_;
/// The analysis callback object printip printip_;};
Conditional Analysis in Pin++
Conditional callback has method that determines if Pin is to analysis target
class Instrument : public OASIS::Pin::Instruction_Instrument <Instrument>{public: Instrument (FILE * file) : printip_ (file, countdown_) { }
void handle_instrument (const OASIS::Pin::Ins & ins) { this->printip_[this->countdown_].insert (IPOINT_BEFORE, ins); }
private: /// The countdown guard protecting \a printip_. countdown countdown_;
/// The analysis callback object printip printip_;};
Conditional is placed in brackets after the standard callback
object
Conditional Analysis in Pin++
Conditional callback has method that determines if Pin is to analysis target
class Instrument : public OASIS::Pin::Instruction_Instrument <Instrument>{public: Instrument (FILE * file) : printip_ (file, countdown_) { }
void handle_instrument (const OASIS::Pin::Ins & ins) { this->printip_[this->countdown_].insert (IPOINT_BEFORE, ins); }
private: /// The countdown guard protecting printip_. countdown countdown_;
/// The analysis callback object printip printip_;};
This approach allows us to quickly enable/disable conditional analysis
Replacement RoutinesReplacement routines allow the developer to replace
existing functions in the image with functions from the Pintool and intercept calls to the original function
Traditional approach tool too complicated to show on slides See replacesigprobed.cpp in ManualExamples
Replacement Routines in Pin++
class new_malloc : public OASIS::Pin::Replacement_Routine <new_malloc, void * (int)>{public: static return_type execute (param1_type n) { std::cout << "NewMalloc (" << std::hex << ADDRINT (new_malloc::original_funcptr ()) << ", " << std::dec << n << ")" << std::endl << flush;
return new_malloc::call_original (n); }};
class Instrument : public OASIS::Pin::Image_Instrument <Instrument>{public: void handle_instrument (const OASIS::Pin::Image & img) { OASIS::Pin::Routine rtn = img.find_routine ("malloc");
if (rtn.valid ()) rtn.replace_signature_probed <new_malloc> (CALLINGSTD_DEFAULT); }};
Replacement Routines in Pin++
class new_malloc : public OASIS::Pin::Replacement_Routine <new_malloc, void * (int)>{public: static return_type execute (param1_type n) { std::cout << "NewMalloc (" << std::hex << ADDRINT (new_malloc::original_funcptr ()) << ", " << std::dec << n << ")" << std::endl << flush;
return new_malloc::call_original (n); }};
class Instrument : public OASIS::Pin::Image_Instrument <Instrument>{public: void handle_instrument (const OASIS::Pin::Image & img) { OASIS::Pin::Routine rtn = img.find_routine ("malloc");
if (rtn.valid ()) rtn.replace_signature_probed <new_malloc> (CALLINGSTD_DEFAULT); }};
Replacement routine is an object parameterized by subclass, and signature of method to replace
Replacement Routines in Pin++
class new_malloc : public OASIS::Pin::Replacement_Routine <new_malloc, void * (int)>{public: static return_type execute (param1_type n) { std::cout << "NewMalloc (" << std::hex << ADDRINT (new_malloc::original_funcptr ()) << ", " << std::dec << n << ")" << std::endl << flush;
return new_malloc::call_original (n); }};
class Instrument : public OASIS::Pin::Image_Instrument <Instrument>{public: void handle_instrument (const OASIS::Pin::Image & img) { OASIS::Pin::Routine rtn = img.find_routine ("malloc");
if (rtn.valid ()) rtn.replace_signature_probed <new_malloc> (CALLINGSTD_DEFAULT); }};
All replacement routines objects must implement an execute
method; entry point for replaced routine
Replacement Routines in Pin++
class new_malloc : public OASIS::Pin::Replacement_Routine <new_malloc, void * (int)>{public: static return_type execute (param1_type n) { std::cout << "NewMalloc (" << std::hex << ADDRINT (new_malloc::original_funcptr ()) << ", " << std::dec << n << ")" << std::endl << flush;
return new_malloc::call_original (n); }};
class Instrument : public OASIS::Pin::Image_Instrument <Instrument>{public: void handle_instrument (const OASIS::Pin::Image & img) { OASIS::Pin::Routine rtn = img.find_routine ("malloc");
if (rtn.valid ()) rtn.replace_signature_probed <new_malloc> (CALLINGSTD_DEFAULT); }};
Type definitions ensure type safety based on function pointer in
parameter
Replacement Routines in Pin++
class new_malloc : public OASIS::Pin::Replacement_Routine <new_malloc, void * (int)>{public: static return_type execute (param1_type n) { std::cout << "NewMalloc (" << std::hex << ADDRINT (new_malloc::original_funcptr ()) << ", " << std::dec << n << ")" << std::endl << flush;
return new_malloc::call_original (n); }};
class Instrument : public OASIS::Pin::Image_Instrument <Instrument>{public: void handle_instrument (const OASIS::Pin::Image & img) { OASIS::Pin::Routine rtn = img.find_routine ("malloc");
if (rtn.valid ()) rtn.replace_signature_probed <new_malloc> (CALLINGSTD_DEFAULT); }};
It is possible to get the address of the original
function
Replacement Routines in Pin++
class new_malloc : public OASIS::Pin::Replacement_Routine <new_malloc, void * (int)>{public: static return_type execute (param1_type n) { std::cout << "NewMalloc (" << std::hex << ADDRINT (new_malloc::original_funcptr ()) << ", " << std::dec << n << ")" << std::endl << flush;
return new_malloc::call_original (n); }};
class Instrument : public OASIS::Pin::Image_Instrument <Instrument>{public: void handle_instrument (const OASIS::Pin::Image & img) { OASIS::Pin::Routine rtn = img.find_routine ("malloc");
if (rtn.valid ()) rtn.replace_signature_probed <new_malloc> (CALLINGSTD_DEFAULT); }};
It is possible to call the original function that the routine replaced
Replacement Routines in Pin++
class new_malloc : public OASIS::Pin::Replacement_Routine <new_malloc, void * (int)>{public: static return_type execute (param1_type n) { std::cout << "NewMalloc (" << std::hex << ADDRINT (new_malloc::original_funcptr ()) << ", " << std::dec << n << ")" << std::endl << flush;
return new_malloc::call_original (n); }};
class Instrument : public OASIS::Pin::Image_Instrument <Instrument>{public: void handle_instrument (const OASIS::Pin::Image & img) { OASIS::Pin::Routine rtn = img.find_routine ("malloc");
if (rtn.valid ()) rtn.replace_signature_probed <new_malloc> (CALLINGSTD_DEFAULT); }};
The routine is replaced just by calling the replace_* method on the routine
with the replacement routine as a parameter
Locks and GuardsPin has many different locking primitives
LOCK, MUTEX, RW_MUTEX, SEMAPHORE
In Pin++, we created wrapper classes for each locking primitive
We also implement Guard class to automatically release locking primitives when scope is leftAlso have guard for Routine since it must be
opened and closed for usage
Locks and GuardsPin has many different locking primitives
LOCK, MUTEX, RW_MUTEX, SEMAPHORE
In Pin++, we created wrapper classes for each locking primitive
We also implement Guard class to automatically release locking primitives when scope is leftAlso have guard for Routine since it must be
opened and closed for usage
do { OASIS::Pin::Guard <OASIS::Pin::Lock> guard (this->lock_); ++ this->num_threads_;} while (false);
Locks and GuardsPin has many different locking primitives
LOCK, MUTEX, RW_MUTEX, SEMAPHORE
In Pin++, we created wrapper classes for each locking primitive
We also implement Guard class to automatically release locking primitives when scope is leftAlso have guard for Routine since it must be
opened and closed for usage
do { OASIS::Pin::Guard <OASIS::Pin::Lock> guard (this->lock_); ++ this->num_threads_;} while (false);
Acquire lock, and release when scope is
left
Thread-local StoragePin supports Thread-local Storage (TLS) via C
functionsPIN_CreateThreadDataKeyPIN_DeleteThreadDataKeyPIN_SetThreadData PIN_GetThreadData
Pin++ uses a type-safe TLS class to improve the robustness of TLS in Pin
Thread-local Storage, Pin++
Supports registering factory (coming soon) & destruction functionFactory function called if TLS data does not existDestruction function called if TLS data exists when
thread terminates
struct thread_data_t { static void __release (void * data) { delete (thread_data_t *)data; } thread_data_t (UINT64 init_count): count_ (init_count) { } thread_data_t (void): count_ (0) { } UINT64 count_; UINT8 pad_[PADSIZE];};
// UsageTLS <thread_data_t> tls (&thread_data_t::__release);tls.get_with_create ([] (void) { return new thread_data_t (); });tls.get (thr_id)->count_ ++;
Thread-local Storage, Pin++
Supports registering factory (coming soon) & destruction functionFactory function called if TLS data does not existDestruction function called if TLS data exists when
thread terminates
struct thread_data_t { static void __release (void * data) { delete (thread_data_t *)data; } thread_data_t (UINT64 init_count): count_ (init_count) { } thread_data_t (void): count_ (0) { } UINT64 count_; UINT8 pad_[PADSIZE];};
// UsageTLS <thread_data_t> tls (&thread_data_t::__release);tls.get_with_create ([] (void) { return new thread_data_t (); });tls.get (thr_id)->count_ ++;
Destruction function for TLS object type
Thread-local Storage, Pin++
Supports registering factory (coming soon) & destruction functionFactory function called if TLS data does not existDestruction function called if TLS data exists when
thread terminates
struct thread_data_t { static void __release (void * data) { delete (thread_data_t *)data; } thread_data_t (UINT64 init_count): count_ (init_count) { } thread_data_t (void): count_ (0) { } UINT64 count_; UINT8 pad_[PADSIZE];};
// UsageTLS <thread_data_t> tls (&thread_data_t::__release);tls.get_with_create ([] (void) { return new thread_data_t (); });tls.get (thr_id)->count_ ++;
Creating TLS object & registering lifecycle
functions
Thread-local Storage, Pin++
Supports registering factory (coming soon) & destruction functionFactory function called if TLS data does not existDestruction function called if TLS data exists when
thread terminates
struct thread_data_t { static void __release (void * data) { delete (thread_data_t *)data; } thread_data_t (UINT64 init_count): count_ (init_count) { } thread_data_t (void): count_ (0) { } UINT64 count_; UINT8 pad_[PADSIZE];};
// UsageTLS <thread_data_t> tls (&thread_data_t::__release);tls.get_with_create ([] (void) { return new thread_data_t (); });tls.get (thr_id)->count_ ++;
Creating TLS object & registering lifecycle
functions
Thread-local Storage, Pin++
Supports registering factory (coming soon) & destruction functionFactory function called if TLS data does not existDestruction function called if TLS data exists when
thread terminates
struct thread_data_t { static void __release (void * data) { delete (thread_data_t *)data; } thread_data_t (UINT64 init_count): count_ (init_count) { } thread_data_t (void): count_ (0) { } UINT64 count_; UINT8 pad_[PADSIZE];};
// UsageTLS <thread_data_t> tls (&thread_data_t::__release);tls.get_with_create ([] (void) { return new thread_data_t (); });tls.get (thr_id)->count_ ++;
Accessing data, creating if no data exists at time of
access
Thread-local Storage, Pin++
Supports registering factory (coming soon) & destruction functionFactory function called if TLS data does not existDestruction function called if TLS data exists when
thread terminates
struct thread_data_t { static void __release (void * data) { delete (thread_data_t *)data; } thread_data_t (UINT64 init_count): count_ (init_count) { } thread_data_t (void): count_ (0) { } UINT64 count_; UINT8 pad_[PADSIZE];};
// UsageTLS <thread_data_t> tls (&thread_data_t::__release);tls.get_with_create ([] (void) { return new thread_data_t (); });tls.get (thr_id)->count_ ++;
Accessing data, assumes it already exists
Multi-threaded PintoolsPin supports threads in a Pintool
PIN_SpawnInternalThreadPIN_WaitForThreadTerminationPIN_ExitThread (called only by spawned thread)…
Uses a C approach for spawning threads
Threads in Pin++
Pin++ uses Java-like Thread object to spawn threads
class Thread : public Runnable Thread (void); virtual void run (void); Thread (Runnable * runnable); static Thread * current (void);
THREADID id (void); PIN_THREAD_UID uid (void); OS_THREAD_ID os_id (void); OS_THREAD_ID parent_id (void);
static void sleep (UINT32 millis); static void yield (void); static bool is_application_thread (void);
State start (size_t stack_size = DEFAULT_THREAD_STACK_SIZE); bool wait (UINT32 millis = PIN_INFINITE_TIMEOUT, INT32 * exit_code = 0); State state (void) const; protected: void terminate (INT32 exit_code = 0); //...};
Threads in Pin++class Thread : public Runnable Thread (void); virtual void run (void); Thread (Runnable * runnable); static Thread * current (void);
THREADID id (void); PIN_THREAD_UID uid (void); OS_THREAD_ID os_id (void); OS_THREAD_ID parent_id (void);
static void sleep (UINT32 millis); static void yield (void); static bool is_application_thread (void);
State start (size_t stack_size = DEFAULT_THREAD_STACK_SIZE); bool wait (UINT32 millis = PIN_INFINITE_TIMEOUT, INT32 * exit_code = 0); State state (void) const; protected: void terminate (INT32 exit_code = 0); //...};
Pin++ uses Java-like Thread object to spawn threads
Can subclass Thread, must implement run
Threads in Pin++class Thread : public Runnable Thread (void); virtual void run (void); Thread (Runnable * runnable); static Thread * current (void);
THREADID id (void); PIN_THREAD_UID uid (void); OS_THREAD_ID os_id (void); OS_THREAD_ID parent_id (void);
static void sleep (UINT32 millis); static void yield (void); static bool is_application_thread (void);
State start (size_t stack_size = DEFAULT_THREAD_STACK_SIZE); bool wait (UINT32 millis = PIN_INFINITE_TIMEOUT, INT32 * exit_code = 0); State state (void) const; protected: void terminate (INT32 exit_code = 0); //...};
Pin++ uses Java-like Thread object to spawn threads
Can instantiate with object that realizes
Runnable
Threads in Pin++class Thread : public Runnable Thread (void); virtual void run (void); Thread (Runnable * runnable); static Thread * current (void);
THREADID id (void); PIN_THREAD_UID uid (void); OS_THREAD_ID os_id (void); OS_THREAD_ID parent_id (void);
static void sleep (UINT32 millis); static void yield (void); static bool is_application_thread (void);
State start (size_t stack_size = DEFAULT_THREAD_STACK_SIZE); bool wait (UINT32 millis = PIN_INFINITE_TIMEOUT, INT32 * exit_code = 0); State state (void) const; protected: void terminate (INT32 exit_code = 0); //...};
Pin++ uses Java-like Thread object to spawn threads
Can get the current thread as an object, will a subclass if applicable
Threads in Pin++class Thread : public Runnable Thread (void); virtual void run (void); Thread (Runnable * runnable); static Thread * current (void);
THREADID id (void); PIN_THREAD_UID uid (void); OS_THREAD_ID os_id (void); OS_THREAD_ID parent_id (void);
static void sleep (UINT32 millis); static void yield (void); static bool is_application_thread (void);
State start (size_t stack_size = DEFAULT_THREAD_STACK_SIZE); bool wait (UINT32 millis = PIN_INFINITE_TIMEOUT, INT32 * exit_code = 0); State state (void) const; protected: void terminate (INT32 exit_code = 0); //...};
Pin++ uses Java-like Thread object to spawn threads
Can get OS information about the Thread
Threads in Pin++class Thread : public Runnable Thread (void); virtual void run (void); Thread (Runnable * runnable); static Thread * current (void);
THREADID id (void); PIN_THREAD_UID uid (void); OS_THREAD_ID os_id (void); OS_THREAD_ID parent_id (void);
static void sleep (UINT32 millis); static void yield (void); static bool is_application_thread (void);
State start (size_t stack_size = DEFAULT_THREAD_STACK_SIZE); bool wait (UINT32 millis = PIN_INFINITE_TIMEOUT, INT32 * exit_code = 0); State state (void) const; protected: void terminate (INT32 exit_code = 0); //...};
Pin++ uses Java-like Thread object to spawn threads
Global methods are now static methods on Thread
class
Threads in Pin++class Thread : public Runnable Thread (void); virtual void run (void); Thread (Runnable * runnable); static Thread * current (void);
THREADID id (void); PIN_THREAD_UID uid (void); OS_THREAD_ID os_id (void); OS_THREAD_ID parent_id (void);
static void sleep (UINT32 millis); static void yield (void); static bool is_application_thread (void);
State start (size_t stack_size = DEFAULT_THREAD_STACK_SIZE); bool wait (UINT32 millis = PIN_INFINITE_TIMEOUT, INT32 * exit_code = 0); State state (void) const; protected: void terminate (INT32 exit_code = 0); //...};
Pin++ uses Java-like Thread object to spawn threads
Lifecycle methods for the Thread
Threads in Pin++class Thread : public Runnable Thread (void); virtual void run (void); Thread (Runnable * runnable); static Thread * current (void);
THREADID id (void); PIN_THREAD_UID uid (void); OS_THREAD_ID os_id (void); OS_THREAD_ID parent_id (void);
static void sleep (UINT32 millis); static void yield (void); static bool is_application_thread (void);
State start (size_t stack_size = DEFAULT_THREAD_STACK_SIZE); bool wait (UINT32 millis = PIN_INFINITE_TIMEOUT, INT32 * exit_code = 0); State state (void) const; protected: void terminate (INT32 exit_code = 0); //...};
Pin++ uses Java-like Thread object to spawn threads
The terminate method contained to the Thread
subclass
Concluding RemarksPin++ increases the level of abstraction for
authoring Pintools
The 100% object-oriented philosophy allows us to create reusable Callback, Instruction, & Tool components that can be easily composed
Pin++ wants to build a library of reusable components that solve problems
We welcome contributions to Pin++