Pin是Intel公司开发的程序分析工具,类似于Valgrind。
但是!看上去Pin的可定制化程度高得多,可以自己编写Tools作为分析工具,可以想象为Pin是一个更加底层的LLVM[CGO, 2004]。所以体系结构领域常用Pin来写cache或prefetcher的模拟器。
Pin采用及时编译技术(Just-in-Time, JIT)实现动态插桩(instrumentation),即在源程序中插入代码以获得运行时的信息。通常插桩有以下两种方式
JIT是指先将其编译为字节码(Pin中是可执行代码),然后运行时(runtime)再对字节码解释执行。
而Pin不需要对源程序进行重编译,只需对插桩代码进行编译然后动态链接即可。而且由于采用了JIT技术,所以代码也具有可移植性,即自己写的Tools可以在不同平台上执行。
到Pin官网下载Linux版本的Kit(Pin v3.10),说明书可见这里。
$ wget https://software.intel.com/sites/landingpage/pintool/downloads/pin-3.10-97971-gc5e41af74-gcc-linux.tar.gz
$ tar -xzvf pin-3.10-97971-gc5e41af74-gcc-linux.tar.gz
$ cd pin-3.10-97971-gc5e41af74-gcc-linux
$ export PATH=pin-3.10-97971-gc5e41af74-gcc-linux/:$PATH # 导出路径
$ pin -help # 查看是否导出成功
Pin: pin-3.10-97971-c5e41af74
Copyright 2002-2019 Intel Corporation.
pin [OPTION] [-t <tool> [<toolargs>]] -- <command line>
Use -help for a description of options
然后进入到常用工具目录,build
一下
$ cd source/tools/ManualExamples
$ make all TARGET=intel64
接着就可以使用常用的内存分析工具了,如指令计数、指令序列分析等
# 对Clik Plus写的多线程fib程序指令数目进行计数
$ pin -t obj-intel64/inscount0.so -- ~/fib.out
Fibonacci number #39 is 63245986.
Calculated in 8157.508 seconds using 48 workers.
# 注意由于插桩,会导致原程序运行得非常慢
$ cat inscount.out
Count 970195533
# 查看ls指令访存序列
$ pin -t obj-intel64/itrace.so -- /bin/ls
$ head itrace.out
0x40001e90
0x40001e91
0x40001ee4
0x40001ee5
0x40001ee7
0x40001ee8
0x40001ee9
0x40001eea
0x40001ef0
0x40001ee0
和LLVM一样,Pin也有几个层次可以对源代码进行操作。
Traces usually begin at the target of a taken branch and end with an unconditional branch, including calls and returns.
Trace下面又可以划分为基本块(Basic Blocks, BBL),一个基本块只有一个入口一个出口。注意官方文档特别提到BBL和上层编译中BB的区别,即Pin只会生成执行代码的BBL而不理不会被执行到的代码。比如下面的分支语句
switch(i)
{
case 4: total++;
case 3: total++;
case 2: total++;
case 1: total++;
case 0:
default: break;
}
会生成指令(IA-32架构)
.L7:
addl $1, -4(%ebp)
.L6:
addl $1, -4(%ebp)
.L5:
addl $1, -4(%ebp)
.L4:
addl $1, -4(%ebp)
上层编译中每一条addl
就是一个BB,但Pin要看执行。如果.L7
被执行了,则BBL会包含四条指令;.L6
被执行了,则BBL包含三条指令;以此类推。
一些用Pin写模拟器或分析的课程