Intel PIN Instrumentation
July 10, 2018 | 17 Minute Read
PIN is very useful for instrumenting a program dynamically. PIN can instrument the program without modifying its behavior. here aren't enough examples in the open web if you want to do advanced optimization.
Install the following libraries to run PIN examples or an error might occur.
sudo apt - get install gcc - multilib g ++- multilib
Execution
cd ~/ pin - 3.7 / source / tools / ManualExamples
make all TARGET = ia32
make proccount . test TARGET = intel64
.. / .. / .. / pin - t obj - intel64 / proccount . so -- ~/ thesis / aedem / laplace2d64int proccount . cpp Makefile
Code
/* compile----
make proccount.test TARGET=intel64
run:
../../../pin -t obj-intel64/proccount.so -- ~/thesis/aedem/laplace2d64int proccount.cpp Makefile
*/
// This tool counts the number of times a routine is executed and
// the number of instructions executed in a routine
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string.h>
#include "pin.H"
ofstream outFile ;
static UINT64 icount = 0 ; //static for compiler performance optimization
static UINT64 icount_dup = 0 ; //duplicate to verify
static UINT64 rcount = 0 ; //load/read count
static UINT64 wcount = 0 ; //store/write count
static int block_id ;
struct node {
//char *block_uid;
UINT64 uid ; //block uid
UINT64 ins ; //num instuctions in a block
UINT64 total ; //total nstructions so far
struct node * next ;
};
struct node * head = NULL ;
void insert_node ( int call_num ){ //, char *block_uid) { //insert link at the first location
//create a link
//char *copy_block_uid;
//copy_block_uid = (char*) malloc(sizeof(char) * (strlen(block_uid) + 1));
//strcpy(copy_block_uid, block_uid);
struct node * link = ( struct node * ) malloc ( sizeof ( struct node ));
link -> uid = call_num ; 904 vim pinatrace . cpp
905 make pinatrace . test TARGET = intel64
906 .. / .. / .. / pin - t obj - intel64 / pinatrace . so -- / home / mobai001 / llvm / llvm - pass - tutorial / build / jacobi . out pinatrace . cpp Makefile
907 ls
908 vim inscount . out
link -> total = icount ; //global variable instuction count
//point it to old first node
if ( head ){
head -> ins = icount - head -> total ;}
link -> next = head ;
//point first to new first node
head = link ;
}
struct node * reverse_list ( struct node * ptr ){ //, char *block_uid) { //insert link at the first location
struct node * ptr_prev = NULL ;
while ( ptr != NULL ){
struct node * temp = ptr -> next ;
ptr -> next = ptr_prev ;
ptr_prev = ptr ;
ptr = temp ;
}
return ptr_prev ;
}
// Holds instruction count for a single procedure
typedef struct RtnCount
{
string _name ;
string _image ;
ADDRINT _address ;
RTN _rtn ;
UINT64 _rtnCount ;
UINT64 _icount ;
struct RtnCount * _next ;
} RTN_COUNT ;
// Linked list of instruction counts for each routine
RTN_COUNT * RtnList = 0 ;
// This function is called before every instruction is executed
VOID docount ( UINT64 * counter ) {
( * counter ) ++ ;
}
VOID DoLoad ( REG reg , ADDRINT * addr ) {
rcount += 1 ;
}
VOID DoStore ( REG reg , ADDRINT * addr ) {
wcount += 1 ;
}
//--------------------------------------------------------------
VOID inscounter () { icount ++ ;}
VOID Instruction ( INS ins , VOID * v ) {
icount_dup += 1 ;
// Insert a call to docount before every instruction, no arguments are passed
INS_InsertCall ( ins , IPOINT_BEFORE , ( AFUNPTR ) inscounter , IARG_END );
//if (INS_IsMemoryRead(ins)){
// rcount += 1;
//}
//insert a call before evert memory read
if ( INS_Opcode ( ins ) == XED_ICLASS_MOV &&
INS_IsMemoryRead ( ins ) &&
INS_OperandIsReg ( ins , 0 ) &&
INS_OperandIsMemory ( ins , 1 ))
{
// op0 <- *op1
INS_InsertCall ( ins , IPOINT_BEFORE , AFUNPTR ( DoLoad ),
IARG_UINT32 ,
REG ( INS_OperandReg ( ins , 0 )),
IARG_MEMORYREAD_EA ,
IARG_END );
}
//insert a call before evert memory write
else if ( INS_Opcode ( ins ) == XED_ICLASS_MOV &&
INS_IsMemoryWrite ( ins ) &&
INS_OperandIsReg ( ins , 1 ) &&
INS_OperandIsMemory ( ins , 0 ))
{
// op0 <- *op1
INS_InsertCall ( ins , IPOINT_BEFORE , AFUNPTR ( DoStore ),
IARG_UINT32 ,
REG ( INS_OperandReg ( ins , 0 )),
IARG_MEMORYWRITE_EA ,
IARG_END );
}
}
//--------------------------------------------------------------
UINT64 aedem_counter = 0 ;
//VOID aedem_block_detected(UINT64 *calls)
VOID aedemBlockHandler ( char * name , int num )
{
aedem_counter += 1 ;
//do a linked list addition here
insert_node ( num );
//save the block number to a global variable
block_id = num ;
}
const char * StripPath ( const char * path )
{
const char * file = strrchr ( path , '/' );
if ( file )
return file + 1 ;
else
return path ;
}
// Pin calls this function every time a new rtn is executed, once per routine
VOID Routine ( RTN rtn , VOID * v )
{
// Allocate a counter for this routine
RTN_COUNT * rc = new RTN_COUNT ;
// The RTN goes away when the image is unloaded, so save it now
// because we need it in the fini
rc -> _name = RTN_Name ( rtn );
rc -> _image = StripPath ( IMG_Name ( SEC_Img ( RTN_Sec ( rtn ))). c_str ());
rc -> _address = RTN_Address ( rtn );
rc -> _icount = 0 ;
rc -> _rtnCount = 0 ;
// Add to list of routines
rc -> _next = RtnList ;
RtnList = rc ;
RTN_Open ( rtn );
// Insert a call at the entry point of a routine to increment the call count
RTN_InsertCall ( rtn , IPOINT_BEFORE , ( AFUNPTR ) docount , IARG_PTR , & ( rc -> _rtnCount ), IARG_END );
//RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)aedem_block_detected, IARG_PTR, &(rc->_rtnCount), IARG_END);
/*https://stackoverflow.com/questions/7896581/intel-pin-rtn-insertcall-multiple-function-arguments*/
RTN_InsertCall ( rtn , IPOINT_BEFORE , ( AFUNPTR ) aedemBlockHandler ,
IARG_ADDRINT , "aedem_block" , IARG_FUNCARG_ENTRYPOINT_VALUE ,
0 , IARG_END );
/*
RTN_InsertCall(funcRtn, IPOINT_BEFORE, (AFUNPTR)funcHandler,
IARG_ADDRINT, "funcA", IARG_FUNCARG_ENTRYPOINT_VALUE,
0, IARG_FUNCARG_ENTRYPOINT_VALUE, 1,
IARG_FUNCARG_ENTRYPOINT_VALUE, 2, IARG_END);
*/
//RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)aedem_block_detected, IARG_UINT64,
// IARG_FUNCRET_EXITPOINT_VALUE, IARG_END);
}
// For each instruction of the routine
for ( INS ins = RTN_InsHead ( rtn ); INS_Valid ( ins ); ins = INS_Next ( ins ))
{
// Insert a call to docount to increment the instruction counter for this rtn
INS_InsertCall ( ins , IPOINT_BEFORE , ( AFUNPTR ) docount , IARG_PTR , & ( rc -> _icount ), IARG_END );
}
RTN_Close ( rtn );
}
// This function is called when the application exits
// It prints the name and count for each procedure
VOID Fini ( INT32 code , VOID * v )
{
outFile << setw ( 23 ) << "Procedure" << " "
<< setw ( 15 ) << "Image" << " "
<< setw ( 18 ) << "Address" << " "
<< setw ( 12 ) << "Calls" << " "
<< setw ( 12 ) << "Instructions" << endl ;
for ( RTN_COUNT * rc = RtnList ; rc ; rc = rc -> _next )
{
if ( rc -> _icount > 0 )
outFile << setw ( 23 ) << rc -> _name << " "
<< setw ( 15 ) << rc -> _image << " "
<< setw ( 18 ) << hex << rc -> _address << dec << " "
<< setw ( 12 ) << rc -> _rtnCount << " "
<< setw ( 12 ) << rc -> _icount << endl ;
}
outFile << "###1----------------------------------------------------" << endl ;
outFile << "# Total occurance of aedem_block:" << aedem_counter << endl ;
outFile << "# Instructions icount=" << icount << " ,icount_dup=" << icount_dup << endl ;
outFile << "# Load rcount=" << rcount << endl ;
outFile << "# Store wcount=" << wcount << endl ;
outFile << "###2----------------------------------------------------" << endl ;
outFile << "block-uid,instuctions,total-instructions" << endl ;
head = reverse_list ( head );
for ( node * block = head ; block ; block = block -> next )
{
outFile << block -> uid << "," << block -> ins << "," << block -> total << endl ;
//calls += 1;
}
outFile << "###f---------------------------------------------------" << endl ;
//outFile <<endl<<"Total entry in linked list(node):" << calls<<endl;
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage ()
{
cerr << "This Pintool counts the number of times a routine is executed" << endl ;
cerr << "and the number of instructions executed in a routine" << endl ;
cerr << endl << KNOB_BASE :: StringKnobSummary () << endl ;
return - 1 ;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
int main ( int argc , char * argv [])
{
// Initialize symbol table code, needed for rtn instrumentation
PIN_InitSymbols ();
outFile . open ( "proccount.out" );
// Initialize pin
if ( PIN_Init ( argc , argv )) return Usage ();
// Register Instruction to be called to instrument instructions
INS_AddInstrumentFunction ( Instruction , 0 );
// Register Routine to be called to instrument rtn
RTN_AddInstrumentFunction ( Routine , 0 );
// Register Fini to be called when the application exits
PIN_AddFiniFunction ( Fini , 0 );
// Start the program, never returns
PIN_StartProgram ();
return 0 ;
}
Example 2
Trace a programs memory accesses and specific function calls with parameters.
vim pinatrace.cpp
make pinatrace.test TARGET = intel64
../../../pin -t obj-intel64/pinatrace.so -- /home/mobai001/llvm/llvm-pass-tutorial/build/jacobi.out pinatrace.cpp Makefile
vim inscount.out
Code
/*This file contains an ISA-portable PIN tool for tracing memory accesses. */
#include <stdio.h>
#include "pin.H"
FILE * trace ;
// Print a memory read record
VOID RecordMemRead ( VOID * ip , VOID * addr ){
fprintf ( trace , "%p: R %p \n " , ip , addr );
}
// Print a memory write record
VOID RecordMemWrite ( VOID * ip , VOID * addr ){
fprintf ( trace , "%p: W %p \n " , ip , addr );
}
VOID RecordBlockStart ( char * name , int num ){
fprintf ( trace , "bid=%d \n " , num );
}
VOID RecordKernelStart ( char * name , int num ){
fprintf ( trace , "kid=%d \n " , num );
}
// Is called for every instruction and instruments reads and writes
VOID Instruction ( INS ins , VOID * v )
{
// Instruments memory accesses using a predicated call, i.e.
// the instrumentation is called iff the instruction will actually be executed.
// On the IA-32 and Intel(R) 64 architectures conditional moves and REP
// prefixed instructions appear as predicated instructions in Pin.
UINT32 memOperands = INS_MemoryOperandCount ( ins );
// Iterate over each memory operand of the instruction.
for ( UINT32 memOp = 0 ; memOp < memOperands ; memOp ++ ) {
if ( INS_MemoryOperandIsRead ( ins , memOp )) {
INS_InsertPredicatedCall (
ins , IPOINT_BEFORE , ( AFUNPTR ) RecordMemRead ,
IARG_INST_PTR ,
IARG_MEMORYOP_EA , memOp ,
IARG_END );
}
// Note that in some architectures a single memory operand can be
// both read and written (for instance incl (%eax) on IA-32)
// In that case we instrument it once for read and once for write.
if ( INS_MemoryOperandIsWritten ( ins , memOp )) {
INS_InsertPredicatedCall (
ins , IPOINT_BEFORE , ( AFUNPTR ) RecordMemWrite ,
IARG_INST_PTR ,
IARG_MEMORYOP_EA , memOp ,
IARG_END );
}
}
}
VOID Fini ( INT32 code , VOID * v ){
fprintf ( trace , "#eof \n " );
fclose ( trace );
}
// Pin calls this function every time a new rtn is executed
VOID Routine ( RTN rtn , VOID * v ){
RTN_Open ( rtn );
if ( RTN_Name ( rtn ). compare ( "aedem_block" ) == 0 ) { //check routine name is aedem_block
//RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)aedem_block_detected, IARG_PTR, &(rc->_rtnCount), IARG_END);
/*https://stackoverflow.com/questions/7896581/intel-pin-rtn-insertcall-multiple-function-arguments*/
RTN_InsertCall ( rtn , IPOINT_BEFORE , ( AFUNPTR ) RecordBlockStart ,
IARG_ADDRINT , "aedem_block" , IARG_FUNCARG_ENTRYPOINT_VALUE ,
0 , IARG_END );
}
else if ( RTN_Name ( rtn ). compare ( "aedem_kernel" ) == 0 ) { //check routine name is aedem_block
//RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)aedem_block_detected, IARG_PTR, &(rc->_rtnCount), IARG_END);
/*https://stackoverflow.com/questions/7896581/intel-pin-rtn-insertcall-multiple-function-arguments*/
RTN_InsertCall ( rtn , IPOINT_BEFORE , ( AFUNPTR ) RecordKernelStart ,
IARG_ADDRINT , "aedem_kernel" , IARG_FUNCARG_ENTRYPOINT_VALUE ,
0 , IARG_END );
}
RTN_Close ( rtn );
}
/* Print Help Message */
INT32 Usage (){
PIN_ERROR ( "This Pintool prints a trace of memory addresses with a basic block uid \n "
+ KNOB_BASE :: StringKnobSummary () + " \n " );
return - 1 ;
}
int main ( int argc , char * argv [])
{
// Initialize symbol table code, needed for rtn instrumentation
PIN_InitSymbols ();
//Initialize PIN
if ( PIN_Init ( argc , argv )) return Usage ();
//setup trace file
trace = fopen ( "pinatrace.out" , "w" );
// Register Instruction to be called to instrument instructions
INS_AddInstrumentFunction ( Instruction , 0 );
// Register Routine to be called to instrument rtn
RTN_AddInstrumentFunction ( Routine , 0 );
// Register Fini to be called when the application exits
PIN_AddFiniFunction ( Fini , 0 );
// Never returns
PIN_StartProgram ();
return 0 ;
}