目录
1. 将写好的插桩程序进行make ,参考Intel官网步骤
3. 利用写好的pintools插桩程序来分析bwa的函数调用次数。
2. 在pin的ManualExaples的目录下依次执行以下命令即可:
一、利用pintools分析bwa程序
(1)分析函数的调用次数,给出函数调用次序的排序
1. 将写好的插桩程序进行make ,参考Intel官网步骤
(https://software.intel.com/sites/landingpage/pintool/docs/71313/Pin/html/index.html#ProcInstrCount)
执行如下命令
#make obj-intel64/proccount.so TARGET=intel64
2. 下载bwa程序
并按照GitHub上的提示执行,因为所给的资源文件是Ref_5.fa h和test40a.fq和test40b.fq所以在执行命令是需要替换。(https://github.com/lh3/bwa)
#make
# ./bwa mem Ref_5.fa test40a.fq | gzip -3 > aln-se.sam.gz
./bwa mem Ref_5.fa test40a.fq test40b.fq | gzip -3 > aln-pe.sam.gz
3. 利用写好的pintools插桩程序来分析bwa的函数调用次数。
因为pin 和proccount.so文件是在pin的相关目录下,因此需要进入对应的目录下,同时因为bwa是在bwa的相关目录下,因此也需要找到对应目录下的文件。执行以下命令:
#../../../pin -t obj-intel64/proccount.so -- ../../../../bwa/bwa mem ../../../../bwa/Ref_5.fa ../../../../bwa/test40a.fq | gzip -3 > aln-se.sam.gz
因为head proccount.out 显示条数较少,于是用cat查看,以下截图截取一部分。
执行命令:
# cat proccount.out
下面看第二个数据集test40b.fq。执行命令:
#../../../pin -t obj-intel64/proccount.so -- ../../../../bwa/bwa mem ../../../../bwa/Ref_5.fa ../../../../bwa/test40b.fq | gzip -3 > aln-se.sam.gz
(2)分析函数的执行时间,按照执行时间排序。
1. 问题二的执行操作思路和上述相同。
主要是protime.cpp文件的编译,和利用proctime.so和pin来分析bwa的函数执行时间。因为bwa程序已经编译过了,所以不用执行上述第二步骤。
2. 在pin的ManualExaples的目录下依次执行以下命令即可:
#make obj-intel64/proctime.so TARGET=intel64
#../../../pin -t obj-intel64/proccount.so -- ../../../../bwa/bwa mem ../../../../bwa/Ref_5.fa ../../../../bwa/test40.fq | gzip -3 > aln-se.sam.gz
#cat proctime.out
#../../../pin -t obj-intel64/proccount.so -- ../../../../bwa/bwa mem ../../../../bwa/Ref_5.fa ../../../../bwa/test40b.fq | gzip -3 > aln-se.sam.gz
#cat proctime.out
二、源代码
(1)分析函数的调用次数,给出函数调用次序的排序
分析函数的调用次数就是用在函数调用之前插桩,然后利用docount进行计数
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)docount, IARG_PTR, &(rc->_rtnCount), IARG_END);
这是给指令插桩计数
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_PTR, &(rc->_icount), IARG_END);
源代码:
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string.h>
#include <time.h>
#include "pin.H"
using std::ofstream;
using std::string;
using std::hex;
using std::setw;
using std::cerr;
using std::dec;
using std::endl;
ofstream outFile;
// Holds instruction count for a single procedure
typedef struct RtnCount
{
string _name;
string _image;
ADDRINT _address;
RTN _rtn;
UINT64 _rtnCount; //记录calls的次数
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)++;
}
const char * StripPath(const char * path)
{
const char * file = strrchr(path,'/');
if (file)
return file+1;
else
return path;
}
// 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;
// The RTN goes away when the _image is unloaded, so save it now
// because we need it in the fini
rc->_name = RTN_Name(rtn);
rc->_image = StripPath(IMG_Name(SEC_Img(RTN_Sec(rtn))).c_str());
rc->_address = RTN_Address(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);
}
// This function is called when the application exits
// It prints the _name and count for each procedure
VOID Fini(INT32 code, VOID *v)
{
outFile << setw(23) << "Procedure" << " "
<< setw(15) << "Image" << " "
<< setw(18) << "Address" << " "
<< setw(12) << "Calls" << " "
<< setw(12) << "Instructions" << endl;
//sort call times
RTN_COUNT *out, *temp1, *temp2 = NULL;
RTN_COUNT temp;
for(out = RtnList; out; out= out->_next)
{
temp1 = out;
for(temp2 = temp1; temp2; temp2 = temp2->_next)
{
if(temp1->_rtnCount < temp2->_rtnCount)
temp1 = temp2;
}
if(temp1 != NULL)
{
temp = *temp1;
temp1->_name = out->_name;
temp1->_image = out->_image;
temp1->_address = out->_address;
temp1->_rtnCount = out->_rtnCount;
temp1->_icount = out->_icount;
out->_name = temp._name;
out->_image = temp._image;
out->_address = temp._address;
out->_rtnCount = temp._rtnCount;
out->_icount = temp._icount;
}
}
for (RTN_COUNT * rc = RtnList; rc; rc = rc->_next)
{
if (rc->_icount > 0)
outFile << setw(23) << rc->_name << " "
<< setw(15) << rc->_image << " "
<< setw(18) << hex << rc->_address << dec <<" "
<< setw(12) << rc->_rtnCount << " "
<< setw(12) << rc->_icount << endl;
}
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
cerr << "This Pintool counts the number of times a routine is executed" << endl;
cerr << "and the number of instructions executed in a routine" << endl;
cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
int main(int argc, char * argv[])
{
// Initialize symbol table code, needed for rtn instrumentation
PIN_InitSymbols();
outFile.open("proccount.out");
// Initialize pin
if (PIN_Init(argc, argv)) return Usage();
// 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;
}
(2)分析函数的执行时间,按照执行时间排序。
分析函数执行时间的主要思路是在函数执行前进行插桩和函数执行结束后插桩:
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)getTime_now, IARG_PTR, rc, IARG_END);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)getTime_end, IARG_PTR, rc, IARG_END);
然后将函数执行后的当前系统时间和函数执行结束后的系统时间相减便可得到函数的执行时间,每次插桩都如此统计函数执行的时间:
rc->_useTime += 1000000*(rc->_tv_end.tv_sec-rc->_tv_now.tv_sec)+(rc->_tv_end.tv_usec-rc->_tv_now.tv_usec);
源代码:
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string.h>
#include <sys/time.h>
#include "pin.H"
using std::ofstream;
using std::string;
using std::hex;
using std::setw;
using std::cerr;
using std::dec;
using std::endl;
ofstream outFile;
// Holds instruction count for a single procedure
typedef struct RtnCount
{
string _name;
string _image;
ADDRINT _address;
RTN _rtn;
struct timeval _tv_now, _tv_end; //进入函数时的时间,和函数执行结束的时间
UINT64 _useTime; //函数执行的时间
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 getTime_now(RTN_COUNT * rc)
{
gettimeofday(&(rc->_tv_now),NULL);
}
VOID getTime_end(RTN_COUNT *rc)
{
gettimeofday(&(rc->_tv_end), NULL);
rc->_useTime += 1000000*(rc->_tv_end.tv_sec-rc->_tv_now.tv_sec)+(rc->_tv_end.tv_usec-rc->_tv_now.tv_usec);
}
const char * StripPath(const char * path)
{
const char * file = strrchr(path,'/');
if (file)
return file+1;
else
return path;
}
// 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;
// The RTN goes away when the _image is unloaded, so save it now
// because we need it in the fini
rc->_name = RTN_Name(rtn);
rc->_image = StripPath(IMG_Name(SEC_Img(RTN_Sec(rtn))).c_str());
rc->_address = RTN_Address(rtn);
rc->_useTime = 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)getTime_now, IARG_PTR, rc, IARG_END);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)getTime_end, IARG_PTR, rc, IARG_END);
RTN_Close(rtn);
}
// This function is called when the application exits
// It prints the _name and count for each procedure
VOID Fini(INT32 code, VOID *v)
{
outFile << setw(23) << "Procedure" << " "
<< setw(15) << "Image" << " "
<< setw(18) << "Address" << " "
<< setw(12) << "Usedtime" << endl;
//sort call times
RTN_COUNT *out, *temp1, *temp2 = NULL;
RTN_COUNT temp;
for(out = RtnList; out; out= out->_next)
{
temp1 = out;
for(temp2 = temp1; temp2; temp2 = temp2->_next)
{
if(temp1->_useTime < temp2->_useTime)
temp1 = temp2;
}
if(temp1 != NULL)
{
temp = *temp1;
temp1->_name = out->_name;
temp1->_image = out->_image;
temp1->_address = out->_address;
temp1->_useTime = out->_useTime;
out->_name = temp._name;
out->_image = temp._image;
out->_address = temp._address;
out->_useTime = temp._useTime;
}
}
for (RTN_COUNT * rc = RtnList; rc; rc = rc->_next)
{
outFile << setw(23) << rc->_name << " "
<< setw(15) << rc->_image << " "
<< setw(18) << hex << rc->_address << dec <<" "
<< setw(12) << rc->_useTime << endl;
}
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
cerr << "This Pintool counts the number of times a routine is executed" << endl;
cerr << "and the number of instructions executed in a routine" << endl;
cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
int main(int argc, char * argv[])
{
// Initialize symbol table code, needed for rtn instrumentation
PIN_InitSymbols();
outFile.open("proctime.out");
// Initialize pin
if (PIN_Init(argc, argv)) return Usage();
// 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;
}
三、总结
pintool确实是挺好用的,比起DECAF,Pin适合简单上手,DECAF适合更低层的系统级的分析。
Pin的拓展工具在github有很多,例如JonathanSalwan这位大牛整合的Pin的工具,并且还提供了相关的文档文档,十分的方便。