最近研究了一下用llvm對源代碼進行分析, 如在特定的指定中插入函數, 分析函數訪存次數等, 下面做一些記錄.
1. 首先要配置環境
先到 llvm 官網下載最新版llvm源碼. 我下載的是 3.5.0 版本. 解壓到用戶主目錄. 我的文件名是 llvm-3.5.0.src.
cd ~/llvm-3.5.0.src
./configure
./make
編譯的時間比較長, 耐心等待.
2. 將源文件編譯成bitcode文件
Getting Started with the LLVM System 一文最後的example提供了方法:
假設源文件名爲 hello.c編譯生成 .bc 文件, 也就是中間代碼文件.
% clang -O3 -emit-llvm -c hello.c -o hello.bc
3. 編寫pass文件並編譯
這裏有一篇官方教程: Writing an LLVM Pass
在源碼目錄的lib/Transforms/下新建一個文件夾 my_pass.
在my_pass 目錄中編寫pass文件和Makefile,
pass文件實際上是一個cpp文件, Writing an LLVM Pass中有一個示例:
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace {
struct Hello : public FunctionPass {
static char ID;
Hello() : FunctionPass(ID) {}
virtual bool runOnFunction(Function &F) {
errs() << "Hello: ";
errs().write_escaped(F.getName()) << '\n';
return false;
}
};
}
char Hello::ID = 0;
static RegisterPass<Hello> X("hello", "Hello World Pass", false, false);
Makefile 文件:
# Makefile for hello pass
# Path to top level of LLVM hierarchy
LEVEL = ../../..
# Name of the library to build
LIBRARYNAME = Hello
# Make the shared library become a loadable module so the tools can
# dlopen/dlsym on the resulting library.
LOADABLE_MODULE = 1
# Include the makefile implementation stuff
include $(LEVEL)/Makefile.common
到了這一步之後, 官方教程出了點問題, 官方教程說直接在my_pass目錄執行make命令, 實際上這是錯的, 會出現這個問題. 正確的做法是:
(1) 先修改 llvm-3.5.0.src/lib/Transforms 目錄下的Makefile文件, 在變量PARALLEL_DIRS後加入你新建的目錄名 my_pass, 不加的話make的時候不會編譯你新建的目錄.
(2) 再回到源碼目錄 llvm-3.5.0.src/ 先執行make, 這個時候就會編譯你的my_pass目錄下的pass文件了. 並在 llvm-3.5.0.src/Release+Asserts/lib 目錄下會生成 一個對應的Hello.so文件, 這就是可以被opt命令加載的文件了.
4. run a pass with opt
進入到my_pass目錄, 執行
opt -load ../../../Release+Asserts/lib/Hello.so -hello < hello.bc > /dev/null
5. 記錄特定指令執行次數的方法
http://llvm.org/docs/ProgrammersManual.html#statistic
Often you may run your pass on some big program, and you’re interested to see how many times it makes a certain transformation. Although you can do this with hand inspection, or some ad-hoc method, this is a real pain and not very useful for big programs. Using the Statistic class makes it very easy to keep track of this information, and the calculated information is presented in a uniform manner with the rest of the passes being executed.
There are many examples of Statistic uses, but the basics of using it are as follows:
- Define your statistic like this: s/ProgrammersManual.html#the-statistic-class-stats-option
#define DEBUG_TYPE "mypassname" // This goes before any #includes. #include "llvm/ADT/Statistic.h" STATISTIC(NumXForms, "The # of times I did stuff");
The STATISTIC macro defines a static variable, whose name is specified by the first argument. The pass name is taken from the DEBUG_TYPE macro, and the description is taken from the second argument. The variable defined (“NumXForms” in this case) acts like an unsigned integer.
- Whenever you make a transformation, bump the counter:
++NumXForms; // I did stuff!
That’s all you have to do. To get ‘opt‘ to print out the statistics gathered, use the ‘-stats‘ option:
$ opt -stats -mypassname(這裏好像有問題, 加了mypassname參數會報錯, 去掉就好了) < program.bc > /dev/null
... statistics output ...
參考:
http://llvm.org/docs/ProgrammersManual.html#the-statistic-class-stats-option