0%

协议插桩遇到的问题

插桩

1、编译器背景

编译器主要三大块:前端(frontEnd),优化器(Optimizer)和后端(backEnd)。

源码翻译为中间表示,中间表示翻译为特定机器码

GCC(GNU Compiler Collection) 是一个工具集合,包含的预处理器为cpp,还会调用汇编器as、连接器ld

GCC 也将三段式做的比较好,并且实现了很多前端,支持了很多语言。但是致命缺陷是,他们是一个完整的可执行文件,没有给其它语言的开发者提供代码重用的接口。即使 GCC 是开源的,但是源代码重用的难度也比较大。也就是说GCC太庞大了,耦合度太高!!

LLVM (Low Level Virtual Machine),顾名思义是一种中间表示,类似Java虚拟机,降低耦合度,解决重用问题。

clang + llvm ≈ GCC ,其实llvm+gcc/clang/其他,也是可以的。上述的GCC(RTL)与llvm(IR)的中间表达是不一样的。

2、SanitizerCoverage插桩

插桩编译

1
clang -fsanitize=address -fsanitize-coverage=trace-pc-guard -o test test.c

-fsanitize-coverage=trace-pc-guard:对边进行插桩。(默认:edge)
-fsanitize-coverage=func,trace-pc-guard:对每个函数插桩。
-fsanitize-coverage=bb,no-prune,trace-pc-guard:对基本块插桩。

或者直接在makefile文件里加:

1
2
CC = clang
CFLAGS = -fsanitize=address -fsanitize-coverage=trace-pc-guard -g

或者make命令后加:

1
make CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div"

执行程序获取覆盖率数据文件。

1
ASAN_OPTIONS=coverage=1 ./test

.sancov文件是 AddressSanitizer生成的覆盖率文件,这些文件包含了程序在运行时覆盖的代码部分的信息。将该文件转换为覆盖率信息。

1
sancov -symbolize *.sancov ./test > test.symcov

查看覆盖率信息。

1
cat test.symcov

打印覆盖的函数:

1
sancov -covered-functions *.sancov ./test

3、Code Coverage插桩

插桩编译

1
clang -fprofile-instr-generate -fcoverage-mapping test.c -o test

或者直接在makefile文件里加:

1
2
CC = clang
CFLAGS = -fprofile-instr-generate -fcoverage-mapping -g

或者make命令后加:

1
make CC="clang -O2 -fno-omit-frame-pointer -g -fprofile-instr-generate -fcoverage-mapping"

执行程序获取覆盖率数据文件。

LLVM_PROFILE_FILE="test.profraw" ./test

合并生成的覆盖率数据文件。

llvm-profdata merge -sparse test.profraw test2.profraw ... -o test.profdata

查看覆盖率信息:

llvm-cov show ./test -instr-profile=test.profdata

查看覆盖率报告:

llvm-cov report ./test -instr-profile=test.profdata

4、gcov 插桩

插桩编译

1
gcc test.c -c -fprofile-arcs -ftest-coverage -o test.o

或者直接在makefile文件里加:

1
2
CC=gcc
CFLAGS+= -fprofile-arcs -ftest-coverage

或者make命令后加:

1
2
make CC="gcc -g -fprofile-arcs -ftest-coverage"
make CFLAGS="-fprofile-arcs -ftest-coverage" CPPFLAGS="-fprofile-arcs -ftest-coverage" CXXFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs -ftest-coverage" clean all

运行程序后,会生成一个 *.gcda 文件,里面包含代码执行次数等数据。

注意:遇到*.gcda不能生成的情况,大概率是因为程序没有正常结束,比如用ctrl+c

源码中添加信号处理

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <signal.h>
#include <unistd.h>

void sig_handler(int signo){
if (signo == SIGUSR1)
exit(0);
}


int main(int argc, char** argv) {
  signal(SIGUSR1, sig_handler);
    ...
}

再启动:

1
timeout -k 0 -s SIGUSR1 3s ./test //3s控制运行时长

输出覆盖率报告

# 生成覆盖率文本报告
lcov -c -d . -o test.info --rc lcov_branch_coverage=1 
# 生成覆盖率网页报告
genhtml --branch-coverage -o result test.info 
------------- Thank you for reading -------------

Title - Artist
0:00