Skip to content

Requesting contextual data in callbacks

James H. Hill edited this page Apr 30, 2014 · 13 revisions

An important feature of analysis routines in Pin, which are Callback objects in Pin++, is the ability to request context data each time the analysis routine is called. The context information can be the current values of a register, the parameters passed into the a routine, or the target address of a jump instruction. The context information can then be used to assist in the analysis.

One of the challenges of using Pin when dealing with context data is ensuring type correctness of the registered context data, and the function that receives the context data. The following code show you request context data using traditional Pin:

// 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, "%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);
}

As you can see in the example above, we must provide the context data type when registering the analysis routine with Pin. In this case, we are requesting the instruction pointer (IARG_INST_PTR) before each instruction is executed. The analysis routine printip has an extra parameter type to account for the context data. Unfortunately, there is no strong connection between the context data type at registration, and the context data argument in the analysis routine.

In Pin++, we address this problem within the template parameter of the callback object. The following code is the Callback and Instruction object for its counterpart in the code above:

class printip : public OASIS::Pin::Callback < printip (OASIS::Pin::ARG_INST_PTR) >
{
public:
  printip (FILE * file)
    : file_ (file) { }

  void handle_analyze (param_type1 addr)
  {
    ::fprintf (this->file_, "0x%p\n", addr);
  }

private:
  FILE * file_;
};

class Instrument : public OASIS::Pin::Instruction_Instrument <Instrument>
{
public:
  Instrument (FILE * file)
    : printip_ (file) { }

  void handle_instrument (const OASIS::Pin::Ins & ins)
  {
    this->printip_.insert (IPOINT_BEFORE, ins);
  }

private:
  printip printip_;
};

As shown in the example above, the function template parameter of the Callback object defines what context data you want to pass the callback. Pin++ defines a context parameter for most context data type supported in Pin. The handle_analyze method then has parameters for each piece of context data it expects to receive. Thanks to C++ templates, and type definitions, we do not have to remember the context data's type when defining handle_analyze.

You will also notice that insert does not take an extra argument like the Pin version. This is because Pin++ automatically builds the argument list based on the function template parameter for the Callback object.

Context Data Requiring an Extra Argument

In some cases, context data requires an extra argument. For example, when requesting one of the arguments passed into the routine. In order to get this argument, we have to pass the correct argument as an extra parameter with the context data parameter.

In Pin++, this is done by passing the extra parameters after the instrumented object parameter in the insert method. Here is an example illustrating this concept:

class Arg1_Before : public OASIS::Pin::Callback <Arg1_Before (OASIS::Pin::ARG_FUNCARG_ENTRYPOINT_VALUE)>
{
public:
  Arg1_Before (std::ofstream & file, const char * name)
    : file_ (file), name_ (name) { }

  void handle_analyze (ADDRINT size)
  {
    this->file_ << this->name_ << "(" << size << ")" << std::endl;
  }

private:
  std::ofstream & file_;
  std::string name_;
};

Here is the code for inserting the analysis callback object, and the extra argument required by the OASIS::Pin::ARG_FUNCARG_ENTRYPOINT_VALUE:

this->malloc_before_.insert (IPOINT_BEFORE, rtn, 0);

If you do not provide the required argument, then the compiler will give a compilation error.

Happy Coding!