1. 基本介绍
1.1 Pin
pin 是 Intel 开发的一个动态二进制插桩工具,提供了丰富的 API 接口,可以监控一些程序运行状态,也可以模拟一些现有处理器不支持的指令。
Pin is a dynamic binary instrumentation framework for the IA-32, x86-64 and MIC instruction-set architectures that enables the creation of dynamic program analysis tools. Some tools built with Pin are Intel® VTune™ Amplifier, Intel® Inspector, Intel® Advisor and Intel® Software Development Emulator (Intel® SDE).
1.2 Pintool
The tools created using Pin, called Pintools, can be used to perform program analysis on user space applications on Linux*, Windows* and macOS*. As a dynamic binary instrumentation tool, instrumentation is performed at run time on the compiled binary files. Thus, it requires no recompiling of source code and can support instrumenting programs that dynamically generate code.
Pintool 是指经过 Pin 框架开发后的工具,可以用来完成具体的程序分析和仿真验证。
一般来讲,插桩需要两个基本功能:
- 插桩代码 —— A mechanism that decides where and what code is inserted
- 分析代码 —— The code to execute at insertion points
Pin 提供的 API 的粒度包括:
- Trace 粒度:表示一个单入口,多出口指令序列。Pin 经 Trace 分为若干个基本块 BBL,其中一个 BBL 是一个单入口、单出口的指令序列。
TRACE_ADDInstrumentFunction(TRACE_INSTRUMENT_CALLBACK fun, VOID *val);
- IMG 粒度:
IMG_AddInstrumentFunction(IMAGECALLBACK fun, VOID *v)
IMG_AddUnloadFunction(IMAGECALLBACK fun, VOID *v)
- RTN 粒度:
RTN_AddInstrumentFunction(RTN_INSTRUMENT_CALLBACK fun, VOID *val)
- INS 粒度:
INS_AddInstrumentFunction(INS_INSTRUMENT_CALLBACK fun, VOID *val)
2 Pintool 的编写流程
- Pin 初始化
- 注册回调函数
- 启动
2.1 例子
#include <iostream>
#include <fstream>
#include "pin.H"
ofstream OutFile;
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
"o", "inscountmt.out", "specify output file name");
PIN_LOCK lock;
INT32 numThreads = 0;
const INT32 MaxNumThreads = 10000;
// The running count of instructions is kept here
// We let each thread's count be in its own data cache line so that
// multiple threads do not contend for the same data cache line.
// This has shown to improve the performance of inscount2_mt by up to 6X
// on SPECOMP2001.
#define PADSIZE 56 // 64byte linesize : 64 - 8
struct THREAD_DATA
{
UINT64 _count;
UINT8 _pad[PADSIZE];
};
THREAD_DATA icount[MaxNumThreads];
// This function is called before every block
VOID PIN_FAST_ANALYSIS_CALL docount(ADDRINT c, THREADID tid) { icount[tid]._count += c; }
VOID ThreadStart(THREADID threadid, CONTEXT *ctxt, INT32 flags, VOID *v)
{
PIN_InitLock(&lock);
PIN_GetLock(&lock, threadid+1);
numThreads++;
PIN_ReleaseLock(&lock);
ASSERT(numThreads <= MaxNumThreads, "Maximum number of threads exceeded\n");
}
// 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 for every bbl, passing the number of instructions.
// IPOINT_ANYWHERE allows Pin to schedule the call anywhere in the bbl to obtain best performance.
BBL_InsertCall(bbl, IPOINT_ANYWHERE, (AFUNPTR)docount, IARG_FAST_ANALYSIS_CALL, IARG_UINT32,
BBL_NumIns(bbl), IARG_THREAD_ID, IARG_END);
}
}
// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
// Write to a file since cout and cerr maybe closed by the application
OutFile << "Number of threads ever exist = " << numThreads << endl;
for (INT32 t=0; t<numThreads; t++)
{
OutFile << "Count[" << decstr(t) << "]= " << icount[t]._count << endl;
}
OutFile.close();
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
cerr << "This Pintool counts the number of dynamic instructions executed" << endl;
cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
// argc, argv are the entire command line, including pin -t <toolname> -- ...
int main(int argc, char * argv[])
{
// Initialize pin
if (PIN_Init(argc, argv)) return Usage();
OutFile.open(KnobOutputFile.Value().c_str());
// Initialize icount[]
for (INT32 t=0; t<MaxNumThreads; t++)
icount[t]._count = 0;
PIN_InitLock(&lock);
PIN_AddThreadStartFunction(ThreadStart, 0);
// 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;
}