Courses/Computer Science/CPSC 601.29.ISSA/20110218CodeSession
Code for Example Pin Tool (Strace clone)
This simple example Pin tool shows how to create a tool similar to the Linux `strace' command. It is lacking detailed stats and interpretation of the system call arguments, but the basic framework is there, and it shows how to use Pin's API for instrumentation and analysis.
This program uses the Pin API, so it includes pin.H, supplied by Pin. It also prints some information, so it uses stdio, and it uses the system call name symbols, so we include syscall.h
#include "pin.H" #include <stdio.h> #include <syscall.h>
We tell this hybrid C++ / C program to use the standard namespace. Pin tools are really C++ programs, but they often look very much like plain C with some Pin-defined types and structures.
using namespace std; const char * SCLONE_VERSION = "0.0.2"; KNOB<BOOL> KnobPrintVersionShort(KNOB_MODE_WRITEONCE, "pintool", "v", "0", "output version information and exit"); //====================================================================== //========================= Analysis Routines ========================== //====================================================================== /** * Observe system call invocation */ VOID ObserveSyscallEntry(VOID *invoked_at_address, INT32 syscall_number, VOID *arg0, VOID *arg1, VOID *arg2, VOID *arg3, VOID *arg4, VOID *arg5) { if(SYS_read==syscall_number) { fprintf(stderr,"read(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_brk==syscall_number){ fprintf(stderr,"brk(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_munmap==syscall_number){ fprintf(stderr,"munmap(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_mmap2==syscall_number){ fprintf(stderr,"mmap2(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_mprotect==syscall_number){ fprintf(stderr,"mprotect(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_fstat64==syscall_number){ fprintf(stderr,"fstat64(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_uname==syscall_number){ fprintf(stderr,"uname(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_access==syscall_number){ fprintf(stderr,"access(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_open==syscall_number){ fprintf(stderr,"open(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_close==syscall_number){ fprintf(stderr,"close(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_execve==syscall_number){ fprintf(stderr,"execve(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_write==syscall_number){ fprintf(stderr,"write(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_exit_group==syscall_number){ fprintf(stderr,"exit_group(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else if(SYS_exit==syscall_number){ fprintf(stderr,"exit(%p, %p, %p, %p, %p, %p)", arg0, arg1, arg2, arg3, arg4, arg5); }else{ fprintf(stderr, "%d(%p, %p, %p, %p, %p, %p)", syscall_number, arg0, arg1, arg2, arg3, arg4, arg5); } fprintf(stderr, "\n"); }
Instrumentation routines, on the other hand, are only executed the first time that Pin encounters a particular construct; they are most often useful for inserting callbacks to the actual analysis routines, but if you just want to extract "static" data from the executable, you can do so in these functions too.
//====================================================================== //==================== Instrumentation Routines ======================== //====================================================================== /** * This instrumentation routine is invoked by Pin for every * instruction Pin encounters dynamically during execution. This * routine is executed only the 1st time a particular instruction is * seen. Thereafter, the analysis routines that it has injected will * be executed for each execution of the instruction. In this case, * we only inject analysis routines for instructions related to system * call entry/exit. */ VOID InjectInstruction(INS ins, VOID *v) { if(INS_IsSyscall(ins)) { INS_InsertCall(ins, IPOINT_BEFORE, AFUNPTR(ObserveSyscallEntry), IARG_INST_PTR, IARG_SYSCALL_NUMBER, IARG_SYSARG_VALUE, 0, IARG_SYSARG_VALUE, 1, IARG_SYSARG_VALUE, 2, IARG_SYSARG_VALUE, 3, IARG_SYSARG_VALUE, 4, IARG_SYSARG_VALUE, 5, IARG_END); } }
Pin provides the ability to hook a program's shutdown. This is a common feature of many programming and system environments. See, for example, the libc `atexit()' function, and the JVM Runtime classes shutdown hook registration mechanism.
/** * Instrumentation routine that Pin calls when the supervised program * is finished executing (or Pin detaches). */ VOID Fini(INT32 code, VOID *v) { fprintf(stderr, "\nstrace-clone finished with code %d\n", code); }
The starting point for a Pin tool includes asking Pin to initialize itself and read any available symbols from the traced program. We then tell Pin to inject a call to an instrumentation routine every time it first encounters an instruction. We also register the shutdown hook.
/** * Entry point */ int main(int argc, char *argv[]) { PIN_InitSymbols(); if(PIN_Init(argc, argv)) { fprintf(stderr, "strace is a clone of strace(1) written using Pin\n"); fprintf(stderr, "\n"); return -1; } if(KnobPrintVersionShort.Value()) { fprintf(stdout, "strace-clone-%s\n", SCLONE_VERSION); return 0; } INS_AddInstrumentFunction(InjectInstruction, 0); PIN_AddFiniFunction(Fini, 0); PIN_StartProgram(); return 0; }