This tutorial will discuss an open-source framework for creating Pintools, which are analysis tools for the dynamic binary instrumentation tool named Pin, named Pin++. Pin++ is an object-oriented framework that uses template meta-programming to implement Pintools. The goal of Pin++ is to simplify programming a Pintool and promote reuse of its components across different Pintools.
Using Pin++ to Author Highly Configurable Pintools for Pin
1. Using Pin++ To Author
Highly Configurable
Pintools for Pin
Dr. James H. Hill
Dept. Computer & Information Science
Indiana University-Purdue University Indianapolis
hillj@cs.iupui.edu
http://www.cs.iupui.edu/~hillj
http://github.com/SEDS/PinPP
2. Preface
This 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!
3. 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++
4. What is All This “Pin” About?!
http://www.pintool.org
5. 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.
6. Pin
Pin is a dynamic binary instrumentation tool
http://www.pintool.org
Supported Platforms
Linux
Windows
MacOS X
Android
Etc…
8. 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 docount
static UINT64 icount = 0;
// This function is called before every instruction is executed
VOID docount(void) { icount++; }
// Pin calls this function every time a new instruction is encountered
VOID 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
9. 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 docount
static UINT64 icount = 0;
// This function is called before every instruction is executed
VOID docount(void) { icount++; }
// Pin calls this function every time a new instruction is encountered
VOID 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
10. 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 docount
static UINT64 icount = 0;
// This function is called before every instruction is executed
VOID docount(void) { icount++; }
// Pin calls this function every time a new instruction is encountered
VOID 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
11. 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 docount
static UINT64 icount = 0;
// This function is called before every instruction is executed
VOID docount(void) { icount++; }
// Pin calls this function every time a new instruction is encountered
VOID 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
12. Counting Instructions (2/3)
// This function is called when the application exits
VOID 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
13. Counting Instructions (2/3)
// This function is called when the application exits
VOID 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
14. 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
15. 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
16. Developer Challenges
1. 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
17. Seeing the Design
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;
}
Based on this code:
• What is the design of a
Pintool?
• What entities are
involved in the Pintool,
and their relations?
18. Hidden Complexities
VOID 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.
19. 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.
20. Continuous Reinvention
static 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
21. 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!
23. 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
27. 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
28. 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
29. Creating Our First Pintool using
Pin++
https://github.com/SEDS/PinPP/wiki/Creating-a-Pintool-using-Pin
30. System Requirements
Perl, 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++.
31. 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
32. 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
33. Building Pin++
We use Makefile, Workspace, Project Creator (MPC) to
assist with building Pin++ on different platforms
MPC allows us to write generic project files that are used
to generate build scripts for the target platform
E.g., Makefiles, Visual Studio Workspaces, Eclipse, etc.
https://github.com/DOCGroup/MPC
Build Command
%> mwc.pl –type [type] pin++.mwc
%> open/build generated workspace
34. 100% Object-Oriented
Tool$
<Class>$
//$no, fica, on$methods$
Instrument$
<Class>$
//$instrumenta, on$methods$
Callback$
<Class>$
//$analysis$methods$
Pintool$
<SharedLibrary>$
1$
*$
*$
*$
*$
1$
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.
35. Callback Object
The 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_;
};
36. Callback Object
The 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…
37. Callback Object
The 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
38. Callback Object
The 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
39. Callback Object
The 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
40. Callback Object
The 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.
41. Callback Object
The 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”
42. Callback Object
The 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
43. Instrumentation Object
The 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
44. Instrumentation Object
The 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.
45. Instrumentation Object
The 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
46. Instrumentation Object
The 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
47. Instrumentation Object
The 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
48. Instrumentation Object
The 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
49. 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
54. Tool Object
The tool object bootstraps the Pintool & registers its
instruments
class 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
55. Tool Object
The tool object bootstraps the Pintool & registers its
instruments
class 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
56. Required main function
We use macros to create the define the main function,
and create the Tool object
DECLARE_PINTOOL
DELCARE_PINTOOL_PROBED
// Declare the inscount Pintool
DECLARE_PINTOOL (inscount)
57. Building Your Pintool
MPC is also used to build the Pintool
You must use one of the following base projects:
oasis_pintool — regular Pintool
oasis_static_pintool — static Pintool
// pintool.mwc
workspace {
cmdline += -include $PINPP_ROOT/MPC/config
}
// inscount0.mpc
project (inscount0) : oasis_pintool {
sharedname = inscount0
dllout = ./
Source_Files {
inscount0.cpp
}
}
58. Building Your Pintool
MPC is also used to build the Pintool
You must use one of the following base projects:
oasis_pintool — regular Pintool
oasis_static_pintool — static Pintool
// pintool.mwc
workspace {
cmdline += -include $PINPP_ROOT/MPC/config
}
The MPC workspace tells MPC where
to locate Pin++ base projects
// inscount0.mpc
project (inscount0) : oasis_pintool {
sharedname = inscount0
dllout = ./
Source_Files {
inscount0.cpp
}
}
59. Building Your Pintool
MPC is also used to build the Pintool
You must use one of the following base projects:
oasis_pintool — regular Pintool
oasis_static_pintool — static Pintool
// pintool.mwc
workspace {
cmdline += -include $PINPP_ROOT/MPC/config
}
The MPC project contains the settings
for building the Pintool
// inscount0.mpc
project (inscount0) : oasis_pintool {
sharedname = inscount0
dllout = ./
Source_Files {
inscount0.cpp
}
}
60. Building Your Pintool
MPC is also used to build the Pintool
You must use one of the following base projects:
oasis_pintool — regular Pintool
oasis_static_pintool — static Pintool
// pintool.mwc
workspace {
cmdline += -include $PINPP_ROOT/MPC/config
}
The base project for the
project configuration
// inscount0.mpc
project (inscount0) : oasis_pintool {
sharedname = inscount0
dllout = ./
Source_Files {
inscount0.cpp
}
}
61. Building Your Pintool
MPC is also used to build the Pintool
You must use one of the following base projects:
oasis_pintool — regular Pintool
oasis_static_pintool — static Pintool
// inscount0.mpc
project (inscount0) : oasis_pintool {
sharedname = inscount0
dllout = ./
Source_Files {
inscount0.cpp
}
}
// pintool.mwc
workspace {
cmdline += -include $PINPP_ROOT/MPC/config
}
sharedname means we are building a
shared library; expected by Pin
62. Building Your Pintool
%> mwc.pl -type [type] inscount.mwc
%> build workspace/open solution
63. Running Your Pintool
Linux
%> 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
64. Using Data In Our Callbacks
https://github.com/SEDS/PinPP/wiki/Using-data-in-callbacks
65. Analysis Function w/ Data
/ The running count of instructions is kept here
// make it static to help the compiler optimize docount
static UINT64 icount = 0;
// This function is called before every block
VOID docount(UINT32 c) { icount += c; }
// Pin calls this function every time a new basic block is encountered
// It inserts a call to docount
VOID 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
66. Analysis tools can accept data
/ The running count of instructions is kept here
// make it static to help the compiler optimize docount
static UINT64 icount = 0;
// This function is called before every block
VOID docount(UINT32 c) { icount += c; }
// Pin calls this function every time a new basic block is encountered
// It inserts a call to docount
VOID 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
67. Analysis tools can accept data
/ The running count of instructions is kept here
// make it static to help the compiler optimize docount
static UINT64 icount = 0;
// This function is called before every block
VOID docount(UINT32 c) { icount += c; }
// Pin calls this function every time a new basic block is encountered
// It inserts a call to docount
VOID 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
68. The Pintool is registering a constant with Pin
e.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 instructions
BBL_InsertCall(bbl,
IPOINT_BEFORE,
(AFUNPTR)docount, IARG_UINT32, BBL_NumIns(bbl),
IARG_END);
Potential Problems
69. Callbacks w/ Data
We use configuration methods to configure data in
callbacks
class 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_;
};
70. Callbacks w/ Data
We use configuration methods to configure data in
callbacks
class 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
71. Callbacks w/ Data
We use configuration methods to configure data in
callbacks
class 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
72. Callbacks w/ Data
We use configuration methods to configure data in
callbacks
class 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
73. Instruments w/ Data
We must dynamically allocate a new callback for each
new insertion
class 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
74. Instruments w/ Data
We must dynamically allocate a new callback for each
new insertion
class 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
75. Instruments w/ Data
We must dynamically allocate a new callback for each
new insertion
class 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
76. Instruments w/ Data
We must dynamically allocate a new callback for each
new insertion
class 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
77. 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/ Data
We must dynamically allocate a new callback for each
new insertion
78. 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/ Data
We must dynamically allocate a new callback for each
new insertion
Iterate over callbacks to
get current count
79. 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…
81. Contextual Data
Context data is anything about the program under
instrumentation, or the machine running the program,
needed by the analysis function to perform its analysis
Thread id
Process id
Register values
Arguments to a function
Return value
Etc.
82. 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 IP
VOID printip (VOID *ip) { fprintf(trace, "%pn", ip); }
// Pin calls this function every time a new instruction is encountered
VOID 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);
}
83. 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 IP
VOID printip (VOID *ip) { fprintf(trace, "%pn", ip); }
// Pin calls this function every time a new instruction is encountered
VOID 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)
84. 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 IP
VOID printip (VOID *ip) { fprintf(trace, "%pn", ip); }
// Pin calls this function every time a new instruction is encountered
VOID 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…
85. Potential Problems
The 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
86. 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%pn", addr);
}
private:
FILE * file_;
};
87. 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%pn", addr);
}
private:
FILE * file_;
};
This argument means the
callback is expecting
instruction pointer
88. 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%pn", addr);
}
private:
FILE * file_;
};
Callbacks have typedefs that
manage the correct type for
each parameter
89. 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 data
e.g., non-data arguments
90. Context Data w/ Params
Some context data requires an extra parameter at
insertion time
// with Pin
RTN_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);
91. Context Data w/ Params
Some context data requires an extra parameter at
insertion time
// with Pin
RTN_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
92. Context Data w/ Params
Some context data requires an extra parameter at
insertion time
// with Pin
RTN_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
94. Range-based For Loops
Range-based for-loops are easy way to iterate over a
list of values
Now 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;
}
95. 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 executed
INS_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);
96. 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_;
};
97. 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
98. 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_;
};
99. 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
100. 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
101. Replacement Routines
Replacement 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
103. 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
104. 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
105. 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
106. 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
107. 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
108. 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
109. Locks and Guards
Pin 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 left
Also have guard for Routine since it must be opened and
closed for usage
110. Locks and Guards
Pin 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 left
Also 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);
111. Locks and Guards
Pin 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 left
Also 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
112. Thread-local Storage
Pin supports Thread-local Storage (TLS) via C
functions
PIN_CreateThreadDataKey
PIN_DeleteThreadDataKey
PIN_SetThreadData
PIN_GetThreadData
Pin++ uses a type-safe TLS class to improve the
robustness of TLS in Pin
113. Thread-local Storage, Pin++
Supports registering factory (coming soon) &
destruction function
Factory function called if TLS data does not exist
Destruction 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];
};
// Usage
TLS <thread_data_t> tls (&thread_data_t::__release);
tls.get_with_create ([] (void) { return new thread_data_t (); });
tls.get (thr_id)->count_ ++;
114. Thread-local Storage, Pin++
Supports registering factory (coming soon) &
destruction function
Factory function called if TLS data does not exist
Destruction 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];
};
// Usage
TLS <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
115. Thread-local Storage, Pin++
Supports registering factory (coming soon) &
destruction function
Factory function called if TLS data does not exist
Destruction 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];
};
// Usage
TLS <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
116. Thread-local Storage, Pin++
Supports registering factory (coming soon) &
destruction function
Factory function called if TLS data does not exist
Destruction 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];
};
// Usage
TLS <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
117. Thread-local Storage, Pin++
Supports registering factory (coming soon) &
destruction function
Factory function called if TLS data does not exist
Destruction 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];
};
// Usage
TLS <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
118. Thread-local Storage, Pin++
Supports registering factory (coming soon) &
destruction function
Factory function called if TLS data does not exist
Destruction 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];
};
// Usage
TLS <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
119. Multi-threaded Pintools
Pin supports threads in a Pintool
PIN_SpawnInternalThread
PIN_WaitForThreadTermination
PIN_ExitThread (called only by spawned thread)
…
Uses a C approach for spawning threads
120. 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);
//...
};
121. 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
122. 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
123. 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
124. 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
125. 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
126. 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
127. 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
128. Concluding Remarks
Pin++ 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++