15.2.2. 使用 Dyninst 作为独立库
在将 Dyninst 库用作应用程序的一部分之前,将 DYNINSTAPI_RT_LIB
环境变量的值设置为到运行时库文件的路径:
$ export DYNINSTAPI_RT_LIB=/opt/rh/devtoolset-11/root/usr/lib64/dyninst/libdyninstAPI_RT.so
这会在当前 shell 会话中设置 DYNINSTAPI_RT_LIB
环境变量。
例 15.2 “使用 Dyninst 作为独立应用程序” 演示了如何编写和构建用于监控用户空间进程的执行程序。有关如何使用 Dyninst 的详细解释,请查看 第 15.3 节 “其它资源” 中列出的资源。
例 15.2. 使用 Dyninst 作为独立应用程序
考虑来自 例 15.1 “使用带有 SystemTap 的 Dyninst” 的 exercise.C
源文件:此程序提示用户输入启动号,然后计入 1,为每个迭代调用 print_iteration()
函数,以便将数字输出到标准输出。
现在,考虑另一个名为 count.C
的源文件,其内容如下:
#include <stdio.h> #include <fcntl.h> #include "BPatch.h" #include "BPatch_process.h" #include "BPatch_function.h" #include "BPatch_Vector.h" #include "BPatch_thread.h" #include "BPatch_point.h" void usage() { fprintf(stderr, "Usage: count <process_id> <function>\n"); } // Global information for counter BPatch_variableExpr *counter = NULL; void createCounter(BPatch_process *app, BPatch_image *appImage) { int zero = 0; counter = app->malloc(*appImage->findType("int")); counter->writeValue(&zero); } bool interceptfunc(BPatch_process *app, BPatch_image *appImage, char *funcName) { BPatch_Vector<BPatch_function *> func; appImage->findFunction(funcName, func); if(func.size() == 0) { fprintf(stderr, "Unable to find function to instrument()\n"); exit (-1); } BPatch_Vector<BPatch_snippet *> incCount; BPatch_Vector<BPatch_point *> *points; points = func[0]->findPoint(BPatch_entry); if ((*points).size() == 0) { exit (-1); } BPatch_arithExpr counterPlusOne(BPatch_plus, *counter, BPatch_constExpr(1)); BPatch_arithExpr addCounter(BPatch_assign, *counter, counterPlusOne); return app->insertSnippet(addCounter, *points); } void printCount(BPatch_thread *thread, BPatch_exitType) { int val = 0; counter->readValue(&val, sizeof(int)); fprintf(stderr, "Function executed %d times.\n", val); } int main(int argc, char *argv[]) { int pid; BPatch bpatch; if (argc != 3) { usage(); exit(1); } pid = atoi(argv[1]); BPatch_process *app = bpatch.processAttach(NULL, pid); if (!app) exit (-1); BPatch_image *appImage = app->getImage(); createCounter(app, appImage); fprintf(stderr, "Finding function %s(): ", argv[2]); BPatch_Vector<BPatch_function*> countFuncs; fprintf(stderr, "OK\nInstrumenting function %s(): ", argv[2]); interceptfunc(app, appImage, argv[2]); bpatch.registerExitCallback(printCount); fprintf(stderr, "OK\nWaiting for process %d to exit...\n", pid); app->continueExecution(); while (!app->isTerminated()) bpatch.waitForStatusChange(); return 0; }
请注意,在调用任何 Dyninst 库销毁器之前,客户端应用程序应该销毁所有 Bpatch
对象。否则,mutator 可能会意外终止分段错误。要临时解决这个问题,请将 mutator 的 BPatch
对象设置为 main()
函数中的本地变量。或者,如果您需要使用 BPatch
作为全局变量,请在变异退出前手动分离所有 mutatee 进程。
这个程序接受进程 ID 和函数名称作为命令行参数,然后在执行进程期间显示调用函数的总数量。您可以使用以下 Makefile
构建这两个文件:
DTS = /opt/rh/devtoolset-11/root CXXFLAGS = -g -I$(DTS)/usr/include/dyninst LBITS := $(shell getconf LONG_BIT) ifeq ($(LBITS),64) DYNINSTLIBS = $(DTS)/usr/lib64/dyninst else DYNINSTLIBS = $(DTS)/usr/lib/dyninst endif .PHONY: all all: count exercise count: count.C g++ $(CXXFLAGS) count.C -I /usr/include/dyninst -c g++ $(CXXFLAGS) count.o -L $(DYNINSTLIBS) -ldyninstAPI -o count exercise: exercise.C g++ $(CXXFLAGS) exercise.C -o exercise .PHONY: clean clean: rm -rf *~ *.o count exercise
要使用 Red Hat Developer Toolset 的 g++
编译器在命令行中编译两个程序,请运行 make
工具程序:
$ scl enable devtoolset-11 make
g++ -g -I/opt/rh/devtoolset-11/root/usr/include/dyninst count.C -c
g++ -g -I/opt/rh/devtoolset-11/root/usr/include/dyninst count.o -L /opt/rh/devtoolset-11/root/usr/lib64/dyninst -ldyninstAPI -o count
g++ -g -I/opt/rh/devtoolset-11/root/usr/include/dyninst exercise.C -o exercise
这会创建一个名为 exercise
的新二进制文件,并在当前工作目录中 计数
。
在一个 shell 会话中,按如下所示执行 练习
二进制文件,并等待它提示您进入起始数字:
$ ./exercise
Enter the starting number:
不要输入这个数字。相反,启动另一个 shell 会话并在提示符下键入以下内容来设置 DYNINSTAPI_RT_LIB
环境变量并执行 计数
二进制文件:
$export DYNINSTAPI_RT_LIB=/opt/rh/devtoolset-11/root/usr/lib64/dyninst/libdyninstAPI_RT.so
$./count `pidof exercise` print_iteration
Finding function print_iteration(): OK Instrumenting function print_iteration(): OK Waiting for process 8607 to exit...
现在,切换回第一个 shell 会话,并根据 练习
程序请求的起始数字输入。例如:
Enter the starting number: 5
Iteration number 5
Iteration number 4
Iteration number 3
Iteration number 2
Iteration number 1
当 练习
程序终止时,计数
程序会显示在执行 print_iteration()
函数的次数:
Function executed 5 times.