Pin 2.0 User Guide

by Robert Cohn and Robert Muth

========================================================================================

Introduction

========================================================================================

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

========================================================================================

Overview

========================================================================================

Pin

The best way to think about Pin is as a "just in time" (JIT) compiler. The input to this compiler is not bytecode, however, but a regular executable. Pin intercepts the execution of the first instruction of the executable and generates ("compiles") new code for the straight line code sequence starting at this instruction. It then transfers control to the generated sequence. The generated code sequence is almost identical to the original one, but Pin ensures that it regains control when a branch exits the sequence. After regaining control, Pin generates more code for the branch target and continues execution. Pin makes this efficient by keeping all of the generated code in memory so it can be reused and directly branching from one sequence to another.

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).

Pintools

Conceptually, instrumentation consists of two components:

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.

Observations

Since a Pintool works like a plugin, it must run in the same address space as Pin and the executable to be instrumented. Hence the Pintool has access to all of the executable's data. It also shares file descriptors and other process information with the executable.

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.

Instrumentation Granularity

As described above, Pin's instrumentation is "just in time" (JIT). Instrumentation occurs immediately before a code sequence is executed for the first time. We call this mode of operation trace instrumentation .

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 .

========================================================================================

Examples

========================================================================================

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.

Simple Instruction Count (Instruction Instrumentation)

The example below instruments a program to count the total number of instructions executed. It inserts a call to 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;
}

Instruction Address Trace (Instruction Instrumentation)

In the previous example, we did not pass any arguments to 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;
}

Memory Reference Trace (Instruction Instrumentation)

The previous example instruments all instructions. Sometimes a tool may only want to instrument a class of instructions, like memory operations or branch instructions. A tool can do this by using the Pin API which includes functions that classify and examine instructions. The basic API is common to all instruction sets and is described here. In addition, there is an instruction set specific API for arm, ia32, and ipf.

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;
}

Detecting the Loading and Unloading of Images (Image Instrumentation)

The example below prints a message to a trace file every time and image is loaded or unloaded. It really abuses the image instrumentation mode as the Pintool neither inspects the image nor adds instrumentation code.

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;
}

More Efficient Instruction Counting (Trace Instrumentation)

The example Simple Instruction Count (Instruction Instrumentation) computed the number of executed instructions by inserting a call before every instruction. In this example, we make it more efficient by counting the number of instructions in a BBL at instrumentation time, and incrementing the counter once per BBL, instead of once per instruction.

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;
}

Procedure Instruction Count (Routine Instrumentation)

The example below instruments a program to count the number of times a procedure is called, and the total number of instructions executed in each procedure. When it finishes, it prints a profile to proccount.out

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;
}

Image Instrumentation

It is also possible to use pin to examine binaries without instrumenting them. This is useful when you need to know static properties of an image. The sample tool below counts the number of instructions in an image, but does not insert any instrumentation.

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;
}

Detaching Pin from the Application

Pin can relinquish control of application any time when invoked via PIN_Detach. Control is returned to the original uninstrumented code and the application runs at native speed. Thereafter no instrumented code is ever executed.

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;
}

========================================================================================

Call Backs

========================================================================================

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.

========================================================================================

Applying a Pintool to an Application

========================================================================================

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.

========================================================================================

Tips for Debugging a Pintool

========================================================================================

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)

========================================================================================

Performance Consideration When Writing a Pintool

========================================================================================

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.

Shifting Compuation for Analysis to Instrumentation Code

About every 5th instruction executed in a typical application is a branch. Lookup will called whenever these instruction are executed, causing significant application slowdown. To improve the situation we note that the instrumentation code is typically called only once for every instruction, while the analysis code is called everytime the instruction is executed. If we can somehow shift computation from the analysis code to the instrumentation code we will improve the overall performance. Our example tools offer multiple such opportunites which will explore in turn. The first observation is that for most branches we can find out inside of Instruction() what the branch target will be . For those branches we can call Lookup inside of Instruction() rather than in docount2(), for indirect branches which are relatively rare we still have to use our original approach. All this is reflected in the folling code. We add a second "lighter" analsysis function, docount. While the original docount2() remains unchanged:

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); } ... }

Shifting Compuation for Analysis to Instrumentation Code

The code for docount() is very compact which besides the obvious performance advantages may also cause it to be inlined by pin thereby avoiding the overhead of a call. The heuristics for when a analysis routine is inlined by pin are subject to change. But small routines without any controlflow (single basic block) are almost guaranteed to be inlined. Unfortunately, docount() does have (albeit limited) controlflow. Observing that the parameter, taken, will be zero or one we can eliminate the remaining controlflow as follows:

VOID docount( COUNTER *pedg, INT32 taken ) { pedg->_count += taken; }

There is now no question whether docount() will be inlined or not.

Letting Pin Decide Where to Instrument

[discuss BBL_InsertCall]

========================================================================================

Installing Pin

========================================================================================

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.

========================================================================================

Unsupported features and restrictions

========================================================================================

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.

========================================================================================

Questions? Bugs?

========================================================================================

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.


Generated on Fri Feb 4 00:09:04 2005 for Pin by doxygen 1.3.6