本文基于llvm14.0.0
上一篇,已经介绍如何将自定义的 pass
集成到clang
中。
在项目中,我们经常要对App 的启动进行优化或者对一些页面进行曝光采集。这一篇就利用pass 来实现这一功能。今天来实现一个统计函数执行时间的pass
按照上一篇的步骤实现添加一个pass
,FunctionCallTimePass
其具体实现如下:
FunctionCallTime.h
中代码:
#ifndef _FunctionCallTime_H_
#define _FunctionCallTime_H_
// LLVM include
#include "llvm/Pass.h"
using namespace std;
using namespace llvm;
// Namespace
namespace llvm {
Pass *createFunctionCallTimePass ();
Pass *createFunctionCallTimePass (bool flag);
}
#endif
FunctionCallTime.cpp
中代码:
//
// function_call_time.cpp
// function-call-time
//
// Created by king on 2020/12/18.
//
#include <iostream>
#include "llvm/ADT/Statistic.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Value.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
//#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/InitializePasses.h"
#include "llvm/Transforms/FunctionCallTime/FunctionCallTime.h"
using namespace llvm;
namespace {
struct FunctionCallTimePass : public FunctionPass {
static char ID;
bool flag;
static StringRef InsertFuncNamePrefix;
static StringRef BeginFuncName;
static StringRef EndFuncName;
FunctionCallTimePass()
: FunctionPass(ID) {}
FunctionCallTimePass(bool flag): FunctionPass(ID) {
this->flag = flag;
FunctionCallTimePass();
}
bool runOnFunction(Function &F) override {
std::cout << "functionname: " << std::string(F.getName()) << std::endl;
if (F.empty() || !flag) {
return false;
}
// 1. 已经插入的不需要再次插入
if (F.getName().startswith("_ly_fun_"))
return false;
// 2. 记录开始时间
Value* beginTime = nullptr;
if (!insert_begin_inst(F, beginTime))
return false;
// 3. 方法结束时统计方法耗时,开始的时间记录作为参数
insert_return_inst(F, beginTime);
return true;
}
bool insertBeginInst(Function &F) {
// 0.函数最开始的BasicBlock
LLVMContext &context = F.getContext();
BasicBlock &BB = F.getEntryBlock();
// 1. 获取要插入的函数
FunctionCallee beginFun = F.getParent()->getOrInsertFunction(
FunctionCallTimePass::BeginFuncName,
FunctionType::get(Type::getVoidTy(context),
{Type::getInt8PtrTy(context)}, false));
errs() << "function-call-time: " << BB.getParent()->getName() << " begin\n";
// 2. 构造函数
CallInst *inst = nullptr;
IRBuilder<> builder(&BB);
IRBuilder<> callBuilder(context);
Value *name = builder.CreateGlobalStringPtr(BB.getParent()->getName());
inst = callBuilder.CreateCall(beginFun, {name});
if (!inst) {
llvm::errs() << "Create First CallInst Failed\n";
return false;
}
// 3. 获取函数开始的第一条指令
Instruction *beginInst = dyn_cast<Instruction>(BB.begin());
// 4. 将inst插入
inst->insertBefore(beginInst);
return true;
}
void insertEndInst(Function &F) {
LLVMContext &context = F.getContext();
for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I) {
// 函数结尾的BasicBlock
BasicBlock &BB = *I;
for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I) {
ReturnInst *IST = dyn_cast<ReturnInst>(I);
if (!IST)
continue;
// end_func 类型
FunctionType *endFuncType = FunctionType::get(
Type::getVoidTy(context), {Type::getInt8PtrTy(context)}, false);
// end_func
FunctionCallee endFunc = BB.getModule()->getOrInsertFunction(
FunctionCallTimePass::EndFuncName, endFuncType);
// 构造end_func
IRBuilder<> builder(&BB);
IRBuilder<> callBuilder(context);
Value *name = builder.CreateGlobalStringPtr(BB.getParent()->getName());
CallInst *endCI = callBuilder.CreateCall(endFunc, {name});
// 插入end_func(struction)
endCI->insertBefore(IST);
errs() << "function-call-time: " << BB.getParent()->getName()
<< " end\n";
}
}
}
bool insert_begin_inst(Function &F, Value*& beginTime)
{
// 0.函数最开始的BasicBlock
std::cout << "insert_begin_inst" << std::endl;
LLVMContext &context = F.getContext();
BasicBlock &bb = F.getEntryBlock();
// 1. 获取要插入的函数
FunctionCallee beginFun = F.getParent()->getOrInsertFunction("_ly_fun_b",FunctionType::get(Type::getInt64Ty(context), {}, false));
// 2. 构造函数
// Value *beginTime = nullptr;
CallInst *inst = nullptr;
IRBuilder<> builder(context);
inst = builder.CreateCall(beginFun);
if (!inst) {
llvm::errs() << "Create First CallInst Failed\n";
return false;
}
// 3. 获取函数开始的第一条指令
Instruction *beginInst = dyn_cast<Instruction>(bb.begin());
// 4. 将inst插入
inst->insertBefore(beginInst);
// 5.根据返回值记录开始时间
beginTime = inst;
return true;
}
void insert_return_inst(Function &F, Value* beginTime)
{
std::cout << "insert_return_inst: " << std::endl;
LLVMContext &context = F.getContext();
for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I)
{
// 函数结尾的BasicBlock
BasicBlock &BB = *I;
for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I)
{
ReturnInst *IST = dyn_cast<ReturnInst>(I);
if (!IST)
continue;
// end_func 类型
FunctionType *endFuncType = FunctionType::get(
Type::getVoidTy(context),
{Type::getInt8PtrTy(context),Type::getInt64Ty(context)},
false
);
// end_func
FunctionCallee endFunc = BB.getModule()->getOrInsertFunction("_ly_fun_e", endFuncType);
// 构造end_func
IRBuilder<> builder(&BB);
IRBuilder<> callBuilder(context);
CallInst* endCI = callBuilder.CreateCall(endFunc,
{
builder.CreateGlobalStringPtr(BB.getParent()->getName()),
beginTime
}
);
// 插入end_func(struction)
endCI->insertBefore(IST);
}
}
}
bool doFinalization(Module &M) override {
return false;
}
};
} // namespace
char FunctionCallTimePass::ID = 0;
StringRef FunctionCallTimePass::InsertFuncNamePrefix = "_ly_fun";
StringRef FunctionCallTimePass::BeginFuncName = "_ly_fun_b";
StringRef FunctionCallTimePass::EndFuncName = "_ly_fun_e";
//// 注册给 opt
// opt -load LLVMFunctionCallTime.dylib -function-call-time xx.bc
INITIALIZE_PASS(FunctionCallTimePass, "fct", "Function Call Time Pass", false, false)
//static RegisterPass<FunctionCallTimePass>
// X("function-call-time", "Function calls take time to collect", false,
// false);
// 注册给 clang 通过 -Xclang -load -Xclang LLVMFunctionCallTime.dylib
//static void registerFunctionCallTimePass(const PassManagerBuilder &,legacy::PassManagerBase &PM) {
// PM.add(new FunctionCallTimePass());
//}
//static RegisterStandardPasses Y(PassManagerBuilder::EP_EarlyAsPossible,
// [](const PassManagerBuilder &Builder,
// llvm::legacy::PassManagerBase &PM) {
// PM.add(new FunctionCallTimePass());
// });
//static RegisterStandardPasses Z(PassManagerBuilder::EP_EarlyAsPossible, registerFunctionCallTimePass);
namespace llvm {
Pass *createFunctionCallTimePass () {
return new FunctionCallTimePass(true);
}
Pass *createFunctionCallTimePass (bool flag) {
return new FunctionCallTimePass(flag);
}
}
PassManagerBuilder.cpp
中加入:
static cl::opt<bool> FunCallTime("fct", cl::init(false),
cl::desc("Enable basic block function call time"));
严格按照上一篇中介绍的去集成。
编译 clang
。
测试代码 main.c
中实现:
//
// main.m
// llvmdemo2
//
// Created by heyonly on 2023/1/28.
//
#include <stdio.h>
#include <sys/time.h>
long _ly_fun_b(void)
{
struct timeval star;
gettimeofday(&star, NULL);
long b = star.tv_sec * 1000000 + star.tv_usec;
return b;
}
void _ly_fun_e(char *name, long b)
{
struct timeval end;
gettimeofday(&end, NULL);
long e = end.tv_sec * 1000000 + end.tv_usec;
long t = e - b;
printf("%s %ld us\n",name, t);
}
int main(int argc, const char * argv[]) {
printf("hello world!!\n");
if (argc <= 1) {
printf("无参数\n");
return 0;
}
printf("argment count: %d\n",argc);
return 0;
}
编译main.c
./clang -O3 -mllvm -fct -flegacy-pass-manager -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib /Users/heyonly/workspace/practise/llvmdemo2/llvmdemo2/main.c
编译完后测试:
$ ./a.out
hello world!!
无参数
main 19 us
$ ./a.out 1
hello world!!
argment count: 2
main 21 us
完美。
下一篇将介绍如何使用的新的 passmanager
来管理pass