by Robert Cohn and Robert Muth
========================================================================================
Pin is a tool for the instrumentation of programs. It supports Linux executables for Intel (R) Xscale (R), IA-32, and Itanium (R) processors.
Pin was designed to provides functionality similar to the popular ATOM toolkit for Compaq's Tru64 Unix on Alpha (c.f. http://search.hp.com/query.html?qt=atom+toolkit), i.e. arbitrary code (written in C or C++) can be injected at arbitrary places in the executable.
Unlike Atom, Pin does not instrument an executable statically by rewriting it, but rather adds the code dynamically while the executable is running. This also makes it possible to attach Pin to an already running process.
Pin provides a rich API that abstracts away the underlying instruction set idiosyncracies and allows context information such as register contents to be passed to the injected code as parameters. Pin automatically saves and restores the registers that are overwritten by the injected code so the application continues to work. Limited access to symbol and debug information is available as well.
Pin includes the source code for a large number of example instrumentation tools like basic block profilers, cache simulators, instruction trace generators, etc. It is easy to derive new tools using the examples as a template.
Table of Contents
========================================================================================
The only code ever executed is the generated code. The original code is only used for reference. When generating code, Pin gives the user an opportunity to inject their own code (instrumentation).
The Atom terms for the two component are instrumentation and analysis code. Atom actually separates these two components into different files. In Pin this separation is no longer necessary. Both components live in a single executable, a Pintool. Pintools can be thought of as plugins that can modify the code generation process inside Pin.
The Pintool registers callback routines with Pin that are called from Pin whenever new code needs to be generated. This routine represents the instrumentation component. It inspects the code to be generated, investigates its static properties, and decides if and where to inject calls to analysis code. Those calls can target arbitrary functions inside the Pintool. Pin makes sure that register state is saved and restored as necessary and allow arguments to be passed to the functions.
Pin and the Pintool control a program starting with the very first instruction. For executables compiled with shared libraries this implies that the execution of the dynamic loader and all shared libraries will be visible to the Pintool.
When writing tools, it is more important to tune the analysis code than the instrumentation code. This is because the instrumentation is executed once, but analysis code is called many times.
Trace instrumentation lets the Pintool inspect and instrument an executable one trace at a time. Traces usually begin at the target of a taken branch and end with an unconditional branch, including calls and returns. Pin guarantees that a trace is only entered at the top, but it may contain multiple exits. If a branch joins the middle of a trace, Pin constructs a new trace that begins with the branch target. Pin breaks the trace into basic blocks, BBLs. A BBL is a single entrance, single exit sequence of instructions. Branches to the middle of a bbl begin a new trace and hence a new BBL. It is often possible to insert a single analysis call for a BBL, instead of one analysis call for every instruction. Reducing the number of analysis calls makes instrumentation more efficient. Trace instrumentation utilizes the TRACE_AddInstrumentFunction API call.
As a convenience for Pintool writers, Pin also offers an instruction instrumentation mode which lets the tool inspect and instrument an executable a single instruction at a time. This is essentially identical to trace instrumentation where the Pintool writer has been freed from the responsibilty of iterating over the instructions inside a trace. As decribed under trace instrumentation, certain BBLs and the instructions inside of them may be generated (and hence instrumented) multiple times. Instruction instrumentation utilizes the INS_AddInstrumentFunction API call.
Pin's instruction and trace instrumentation modes differ substantially from the way Atom instruments executables. Atom instruments an entire executable ahead of time in one single pass. Pin interleaves execution and instrumentation of an executable and code that is never executed is never instrumented.
Sometimes, however, it can be useful to look at different granularity than a trace. For this purpose Pin offers two additional modes: image and routine instrumentation which are more similar to the ahead of time model used by Atom. These modes are implemented by "caching" instrumentation requests and hence incur a space overhead.
Image instrumentation lets the Pintool inspect and instrument an entire image, IMG, when it is first loaded. A Pintool can walk the sections, SEC, of the image, the routines, RTN, of a section, and the instructions, INS of a routine. Instrumentation can be inserted so that it is executed before or after a routine is executed, or before or after an instruction is executed. Image instrumentation utilizes the IMG_AddInstrumentFunction API call. Image instrumentation depends on symbol information to determine routine boundaries hence PIN_InitSymbols must be called before PIN_Init.
Routine instrumentation lets the Pintool inspect and instrument an entire routine before the first time it is called. A Pintool can walk the instructions of a routine. There is not enough information available to break the instructions into BBLs. Instrumentation can be inserted so that it is executed before or after a routine is executed, or before or after an instruction is executed. Routine instrumentation can be more efficient than image instrumentation in space and time when the only a small number of the routines in an image are executed. Routine instrumentation utilizes the RTN_AddInstrumentFunction API call. Instrumentation of routine exits does not work reliably in the presence of tail calls or when return instructions cannot reliably detected, e.g. for xscale executables .
========================================================================================
To illustrate how to write Pintools, we present some simple examples. In the web based version of the manual, you can click on a function in the Pin API to see its documentation.
docount before every instruction. When the program exits, it prints the count to stderr.Here is how to run it and the output:
$ pin -t inscount0 -- /bin/ls Makefile atrace.o imageload.out itrace proccount Makefile.example imageload inscount0 itrace.o proccount.o atrace imageload.o inscount0.o itrace.out Count 422838 $
The example can be found in ManualExamples/inscount0.C
#include <iostream> #include "pin.H" // The running count of instructions is kept here UINT64 icount = 0; // This function is called before every instruction is executed VOID docount() { 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); } // This function is called when the application exits VOID Fini(INT32 code, VOID *v) { std::cerr << "Count " << icount << endl; } // argc, argv are the entire command line, including pin -t <toolname> -- ... int main(int argc, char * argv[]) { // Initialize pin PIN_Init(argc, argv); // 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; }
docount, the analysis procedure. In this example, we show how to pass arguments. When calling an analysis procedure, Pin allows you to pass the instruction pointer, current value of registers, effective address of memory operations, constants, etc. For a complete list, see IARG_TYPE.With a small change, we can turn the instruction counting example into a Pintool that prints the address of every instruction that is executed. This tool is useful for understanding the control flow of a program for debugging, or in processor design when simulating an instruction cache.
We change the arguments to INS_InsertCall to pass the address of the instruction about to be executed. We replace docount with printip, which prints the instruction address. It writes it output to to the file itrace.out.
This is how to run it and look at the output:
$ pin -t itrace -- /bin/ls Makefile atrace.o imageload.out itrace proccount Makefile.example imageload inscount0 itrace.o proccount.o atrace imageload.o inscount0.o itrace.out $ head itrace.out 0x40001e90 0x40001e91 0x40001ee4 0x40001ee5 0x40001ee7 0x40001ee8 0x40001ee9 0x40001eea 0x40001ef0 0x40001ee0 $
The example can be found in ManualExamples/itrace.C
#include <stdio.h> #include "pin.H" FILE * trace; // This function is called before every instruction is executed // and prints the IP VOID printip(VOID *ip) { fprintf(trace, "%p\n", 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); } // This function is called when the application exits VOID Fini(INT32 code, VOID *v) { fprintf(trace, "#eof\n"); fclose(trace); } // argc, argv are the entire command line, including pin -t <toolname> -- ... int main(int argc, char * argv[]) { trace = fopen("itrace.out", "w"); // Initialize pin PIN_Init(argc, argv); // 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; }
In this example, we show how to do more selective instrumentation by examining the instructions. This tool generates a trace of all memory addresses referenced by a program. This is also useful for debugging and for simulating a data cache in a processor.
We only instrument instructions that read or write memory. We also use INS_InsertPredicatedCall instead of INS_InsertCall to avoid generating references to instructions that are predicated and the predicate is false (predication is only relevant for Xscale and Itanium).
Since the instrumentation functions are only called once and the analysis functions are called every time an instruction is executed, it is much faster to only instrument the memory operations, as compared to the previous instruction trace example that instruments every instruction.
Here is how to run it and the sample output:
$ pin -t atrace -- /bin/ls Makefile atrace.o imageload.o inscount0.o itrace.out Makefile.example atrace.out imageload.out itrace proccount atrace imageload inscount0 itrace.o proccount.o $ head atrace.out 0x40001ee0: R 0xbfffe798 0x40001efd: W 0xbfffe7d4 0x40001f09: W 0xbfffe7d8 0x40001f20: W 0xbfffe864 0x40001f20: W 0xbfffe868 0x40001f20: W 0xbfffe86c 0x40001f20: W 0xbfffe870 0x40001f20: W 0xbfffe874 0x40001f20: W 0xbfffe878 0x40001f20: W 0xbfffe87c $
The example can be found in ManualExamples/atrace.C
/* * 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); } // Is called for every instruction and instruments reads and writes VOID Instruction(INS ins, VOID *v) { // instruments loads using a predicated call, i.e. // the call happens iff the load will be actually executed // (this does not matter for ia32 but arm and ipf have predicated instructions) if (INS_IsMemoryRead(ins)) { INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead, IARG_INST_PTR, IARG_MEMORYREAD_EA, IARG_END); } // instruments stores using a predicated call, i.e. // the call happens iff the store will be actually executed if (INS_IsMemoryWrite(ins)) { INS_InsertPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWrite, IARG_INST_PTR, IARG_MEMORYWRITE_EA, IARG_END); } } VOID Fini(INT32 code, VOID *v) { fprintf(trace, "#eof\n"); fclose(trace); } int main(int argc, char *argv[]) { PIN_Init(argc, argv); trace = fopen("atrace.out", "w"); INS_AddInstrumentFunction(Instruction, 0); PIN_AddFiniFunction(Fini, 0); // Never returns PIN_StartProgram(); return 0; }
If you invoke it on ls, you would see this output:
$ pin -t imageload -- /bin/ls Makefile atrace.o imageload.o inscount0.o proccount Makefile.example atrace.out imageload.out itrace proccount.o atrace imageload inscount0 itrace.o trace.out $ cat imageload.out Loading /bin/ls Loading /lib/ld-linux.so.2 Loading /lib/libtermcap.so.2 Loading /lib/i686/libc.so.6 Unloading /bin/ls Unloading /lib/ld-linux.so.2 Unloading /lib/libtermcap.so.2 Unloading /lib/i686/libc.so.6 $
The example can be found in ManualExamples/imageload.C
// // This tool prints a trace of image load and unload events // #include <stdio.h> #include "pin.H" FILE * trace; // Pin calls this function every time a new img is loaded // It can instrument the image, but this example does not // Note that imgs (including shared libraries) are loaded lazily VOID ImageLoad(IMG img, VOID *v) { fprintf(trace, "Loading %s\n", IMG_Name(img).c_str()); } // Pin calls this function every time a new img is unloaded // You can't instrument an image that is about to be unloaded VOID ImageUnload(IMG img, VOID *v) { fprintf(trace, "Unloading %s\n", IMG_Name(img).c_str()); } // This function is called when the application exits // It prints the name and count for each procedure VOID Fini(INT32 code, VOID *v) { fclose(trace); } // argc, argv are the entire command line, including pin -t <toolname> -- ... int main(int argc, char * argv[]) { trace = fopen("imageload.out", "w"); // Initialize pin PIN_Init(argc, argv); // Register ImageLoad to be called when an image is loaded IMG_AddInstrumentFunction(ImageLoad, 0); // Register ImageUnload to be called when an image is unloaded IMG_AddUnloadFunction(ImageUnload, 0); // Register Fini to be called when the application exits PIN_AddFiniFunction(Fini, 0); // Start the program, never returns PIN_StartProgram(); return 0; }
The example can be found in ManualExamples/inscount1.C
#include <stdio.h> #include "pin.H" // The running count of instructions is kept here UINT64 icount = 0; // This function is called before every block VOID docount(INT32 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 the number of instructions BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)docount, IARG_UINT32, BBL_NumIns(bbl), IARG_END); } } // This function is called when the application exits VOID Fini(INT32 code, VOID *v) { fprintf(stderr, "Count %lld\n", icount); } // argc, argv are the entire command line, including pin -t <toolname> -- ... int main(int argc, char * argv[]) { // Initialize pin PIN_Init(argc, argv); // Register Instruction to be called to instrument instructions TRACE_AddInstrumentFunction(Trace, 0); // Register Fini to be called when the application exits PIN_AddFiniFunction(Fini, 0); // Start the program, never returns PIN_StartProgram(); return 0; }
Executing the tool and sample output:
$ pin -t proccount -- /bin/grep proccount.C Makefile proccount_SOURCES = proccount.C $ head proccount.out Procedure Image Address Calls Instructions _fini libc.so.6 0x40144d00 1 21 __deregister_frame_info libc.so.6 0x40143f60 2 70 __register_frame_info libc.so.6 0x40143df0 2 62 fde_merge libc.so.6 0x40143870 0 8 __init_misc libc.so.6 0x40115824 1 85 __getclktck libc.so.6 0x401157f4 0 2 munmap libc.so.6 0x40112ca0 1 9 mmap libc.so.6 0x40112bb0 1 23 getpagesize libc.so.6 0x4010f934 2 26 $
The example can be found in ManualExamples/proccount.C
// // This tool counts the number of a routine is executed and counts // the number of instructions executed in a routine // #include <fstream> #include <iomanip> #include "pin.H" // Holds instruction count for a single procedure typedef struct RtnCount { 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)++; } // Pin calls this function every time a new rtn is executed VOID Routine(RTN rtn, VOID *v) { // Allocate a counter for this routine RTN_COUNT * rc = new RTN_COUNT; rc->_rtn = 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); // 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); } const char * StripPath(const char * path) { const char * file = rindex(path,'/'); if (file) return file+1; else return path; } // This function is called when the application exits // It prints the name and count for each procedure VOID Fini(INT32 code, VOID *v) { ofstream count("proccount.out"); count << 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) count << setw(23) << RTN_Name(rc->_rtn) << " " << setw(15) << StripPath(IMG_Name(SEC_Img(RTN_Sec(rc->_rtn))).c_str()) << " " << setw(18) << hex << RTN_Address(rc->_rtn) << dec <<" " << setw(12) << rc->_rtnCount << " " << setw(12) << rc->_icount << endl; } } // argc, argv are the entire command line, including pin -t <toolname> -- ... int main(int argc, char * argv[]) { // Initialize symbol table code, needed for rtn instrumentation PIN_InitSymbols(); // Initialize pin PIN_Init(argc, argv); // 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; }
The example can be found in ManualExamples/staticcount.C
// // This tool prints a trace of image load and unload events // #include <stdio.h> #include "pin.H" // Pin calls this function every time a new img is loaded // It can instrument the image, but this example merely // counts the number of static instructions in the image VOID ImageLoad(IMG img, VOID *v) { UINT32 count = 0; for (SEC sec = IMG_SecHead(img); SEC_Valid(sec); sec = SEC_Next(sec)) { for (RTN rtn = SEC_RtnHead(sec); RTN_Valid(rtn); rtn = RTN_Next(rtn)) { // Prepare for processing of RTN, an RTN is not broken up into BBLs, // it is merely a sequence of INSs RTN_Open(rtn); for (INS ins = RTN_InsHead(rtn); INS_Valid(ins); ins = INS_Next(ins)) { count++; } // to preserve space, release data associated with RTN after we have processed it RTN_Close(rtn); } } fprintf(stderr, "Image %s has %d instructions\n", IMG_Name(img).c_str(), count); } // argc, argv are the entire command line, including pin -t <toolname> -- ... int main(int argc, char * argv[]) { // prepare for image instrumentation mode PIN_InitSymbols(); // Initialize pin PIN_Init(argc, argv); // Register ImageLoad to be called when an image is loaded IMG_AddInstrumentFunction(ImageLoad, 0); // Start the program, never returns PIN_StartProgram(); return 0; }
The example can be found in ManualExamples/detach.C
#include <stdio.h> #include "pin.H" #include <iostream> // This tool shows how to detach Pin from an // application that is under Pin's control. UINT64 icount = 0; VOID docount() { icount++; // Release control of application if 10000 // instructions have been executed if ((icount % 10000) == 0) { PIN_Detach(); } } VOID Instruction(INS ins, VOID *v) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END); } VOID HelloWorld(VOID *v) { std::cerr << "Hello world!" << endl; } VOID ByeWorld(VOID *v) { std::cerr << "Byebye world!" << endl; } VOID Fini(INT32 code, VOID *v) { std::cerr << "Count: " << icount << endl; } int main(int argc, char * argv[]) { PIN_Init(argc, argv); // Callback function to invoke for every // execution of an instruction INS_AddInstrumentFunction(Instruction, 0); // Callback functions to invoke before // Pin releases control of the application PIN_AddDetachFunction(HelloWorld, 0); PIN_AddDetachFunction(ByeWorld, 0); PIN_AddFiniFunction(Fini, 0); // Never returns PIN_StartProgram(); return 0; }
========================================================================================
The examples in the previous section have introduced a number of ways to register call back functions via the Pin API:
The extra parameter val (shared by all the registration functions) will be passed to fun as its second argument whenever it is "called back". This is a standard mechanism used in GUI programming with call backs.
If this feature is not needed, it is safe to pass 0 for val when registering a call back. The expected use of val is to pass a pointer to an instance of a class. Since val is a generic pointer, fun must cast it back to an object before dereferencing the pointer.
========================================================================================
An application and a tool are invoked as follows:
pin [pin-option]... -t [toolname] [tool-options]... -- [application] [application-option]..
The following Pin-options are currently available:
The tool-options follow immediately after the tool specification and depend on the tool used.
Everything following the -- is the command line for the application.
For example, to apply the itrace example (Instruction Address Trace (Instruction Instrumentation)) to a run of the "ls" program:
pin -t itrace -- /bin/ls
To get a listing of the available command line options for Pin:
pin -h
To get a listing of the available command line options for the itrace example:
pin -t itrace -h -- /bin/ls
Note that in the last case /bin/ls is necessary on the command line but will not be executed.
========================================================================================
There are 3 different programs residing in the address space. The application, the Pin instrumentation engine, and your Pintool. This section describes how to use gdb to find bugs in a Pintool. You cannot run Pin directly from gdb since Pin uses the debugging API to start the application. Instead, you must invoke Pin from the command line with the -pause_tool switch, and use gdb to attach to the Pin process from another window. The -pause_tool switch makes Pin print out the process identifier (pid) and pause for 5 seconds.
If your tool is called opcodemix and the application is /bin/ls, you can use gdb as follows. Start gdb with your tool, but do not use the run command:
$ gdb opcodemix GNU gdb 5.2.1 Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"... (gdb)
In another window, start your application with the -pause_tool switch:
$ pin -pause_tool -t opcodemix -- /bin/ls Pausing to attach to pid 28769
Then go back to gdb and attach to the process:
(gdb) attach 28769 Attaching to program: .../build-ia32/SimpleExamples/opcodemix, process 28769 0x011ef361 in ?? () (gdb)
Now, instead of using the gdb run command, you use the cont command to continue execution. You can also set breakpoints as normal:
(gdb) break main Breakpoint 1 at 0x5048d30: file .../PinTools/SimpleExamples/opcodemix.C, line 232. (gdb) cont Continuing. Breakpoint 1, main (argc=6, argv=0x4fef534) at .../PinTools/SimpleExamples/opcode.C:232 (gdb)
If the program does not exit, then you should detach so gdb will release control:
(gdb) detach Detaching from program: .../build-ia32/SimpleExamples/opcodemix, process 28769 (gdb)
If you recompile your program and then use the run command, gdb will notice that the binary has been changed and reread the debug information from the file. This does not happen automatically when using attach. You must use the file command to make gdb reread the debug information:
(gdb) file opcodemix Load new symbol table from "opcodemix"? (y or n) y Reading symbols from opcodemix... done. (gdb)
========================================================================================
They way a Pintool is written can have great impact on the performace of the tool, i.e. how much it slows down the applications it is instrumenting. This section demonstrates some techniques that can be used to improve tool performance. Let's start with an example. The following piece of code is derived from the Examples/edgcnt.C:
The instrumentation component of the tool is show below
VOID Instruction(INS ins, void *v) { ... if ( [ins is a branch or a call instruction] ) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR) docount2, IARG_INST_PTR, IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN, IARG_END); } ... }
The analysis component looks like this:
VOID docount2( ADDRINT src, ADDRINT dst, INT32 taken ) { if(!taken) return; COUNTER *pedg = Lookup( src,dst ); pedg->_count++; }
The purpose of the tool is to count how often each controlflow changing edge in the control flowgraph is traversed. The tool considers both calls and branches but for brevity we will not mention branches in our description. The tool works as follows: The instrumentation component instruments each branch with a call to docount2. As parameters we pass in the origin and the target of the branch and whether the branch was taken or not. Branch origin and target represent of the source and destination of the controlflow edges. If a branch is not taken the controlflow does not change and hence the analysis routine returns right away. If the branch is taken we use the src and dst parameters to look up the counter associated with this edge (Lookup will create a new one if this edge has not been seen before) and increment the counter.
VOID docount( COUNTER *pedg, INT32 taken ) { if( !taken ) return; pedg->_count++; }
And the instrumentation will be somewhat more complex:
VOID Instruction(INS ins, void *v) { ... if (INS_IsDirectBranchOrCall(ins)) { COUNTER *pedg = Lookup( INS_Address(ins), INS_DirectBranchOrCallTargetAddress(ins) ); INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR) docount, IARG_ADDRINT, pedg, IARG_BRANCH_TAKEN, IARG_END); } else { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR) docount2, IARG_INST_PTR, IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN, IARG_END); } ... }
VOID docount( COUNTER *pedg, INT32 taken ) { pedg->_count += taken; }
There is now no question whether docount() will be inlined or not.
========================================================================================
To install a kit, unpack a kit and change to the directory:
$ tar zxf /proj/vssad/proj/pin/Kits/pin-2.0-776-ia32.tar.gz $ cd pin-2.0-776-ia32/
Build and test the examples from the manual
$ cd ManualExamples/ $ make test g++ -c -Wall -Werror -Wno-unknown-pragmas -I../Include -DTARGET_IA32 -g1 -o atrace.o atrace.C g++ -static -Wl,-wrap,mmap,-wrap,__mmap,-wrap,brk,-wrap,__brk,--section-start,.interp=0x05048000 -g1 -o atrace atrace.o -L../Lib/ -lpin -ldwarf -lelf -lencp68 -ldecp68 ../Bin/pin -t atrace -- /bin/cp makefile makefile.copy; cmp makefile makefile.copy g++ -c -Wall -Werror -Wno-unknown-pragmas -I../Include -DTARGET_IA32 -g1 -o inscount0.o inscount0.C g++ -static -Wl,-wrap,mmap,-wrap,__mmap,-wrap,brk,-wrap,__brk,--section-start,.interp=0x05048000 -g1 -o inscount0 inscount0.o -L../Lib/ -lpin -ldwarf -lelf -lencp68 -ldecp68 ../Bin/pin -t inscount0 -- /bin/cp makefile makefile.copy; cmp makefile makefile.copy Count 277395 g++ -c -Wall -Werror -Wno-unknown-pragmas -I../Include -DTARGET_IA32 -g1 -o itrace.o itrace.C g++ -static -Wl,-wrap,mmap,-wrap,__mmap,-wrap,brk,-wrap,__brk,--section-start,.interp=0x05048000 -g1 -o itrace itrace.o -L../Lib/ -lpin -ldwarf -lelf -lencp68 -ldecp68 ../Bin/pin -t itrace -- /bin/cp makefile makefile.copy; cmp makefile makefile.copy g++ -c -Wall -Werror -Wno-unknown-pragmas -I../Include -DTARGET_IA32 -g1 -o proccount.o proccount.C g++ -static -Wl,-wrap,mmap,-wrap,__mmap,-wrap,brk,-wrap,__brk,--section-start,.interp=0x05048000 -g1 -o proccount proccount.o -L../Lib/ -lpin -ldwarf -lelf -lencp68 -ldecp68 ../Bin/pin -t proccount -- /bin/cp makefile makefile.copy; cmp makefile makefile.copy $
Run one of the sample tools from the installed directory
$ ../Bin/pin -t atrace -- /bin/ls _insprofiler.C atrace.out inscount0.o itrace.C proccount atrace imageload.C inscount1.C itrace.o proccount.C atrace.C inscount0 insprofiler.C itrace.out proccount.o atrace.o inscount0.C itrace makefile proccount.out $ head atrace.out 0x40001ee0: R 0xbfffe1e8 0x40001efd: W 0xbfffe224 0x40001f09: W 0xbfffe228 0x40001f20: W 0xbfffe2b4 0x40001f20: W 0xbfffe2b8 0x40001f20: W 0xbfffe2bc 0x40001f20: W 0xbfffe2c0 0x40001f20: W 0xbfffe2c4 0x40001f20: W 0xbfffe2c8 0x40001f20: W 0xbfffe2cc $
To write your own tool, copy one of the example directories and edit the makefile to add your tool.
========================================================================================
Each kit contains Pin and libraries for a specific architecture. Make sure the kit you download is for the right architecture. The Pin libraries use C++, and the compiler you use to build the tool must be compatible with the Pin library. This restriction only applies to building tools; you can instrument applications built by any compiler.
See the README file in the kit for specific information about compiler version and other limitations. If your compiler is not compatible with the kit, send mail to Pin.Project@intel.com.
========================================================================================
Send bugs and questions to Pin.Project@intel.com. Complete bug reports that are easy to reproduce are fixed faster, so try to provide as much information as possible. Include: kit number, your OS version, compiler version. Try to reproduce the problem in a simple example that you can send us.
1.3.6