Aqu*_*irl 17 c linux parsing multithreading
我希望制作一个跟踪另一个软件的函数调用的软件.
它应该像从"运行时"中调用哪个函数.
例:
int main ()
{
a ();
b ();
c ();
return 0;
}
a ()
{
d ();
e ();
}
b ()
{
e ();
f ();
}
Run Code Online (Sandbox Code Playgroud)
假设我希望在C中为C写这个,我应该如何在运行时(从第一次调用开始)跟踪调用 - 使用线程和没有线程?
提示?
您可以通过运行时之前做到这一点的静态分析,或通过运行动态分析.
在运行时只有两种方法可以执行此操作,它们都相当于检测代码:
修改("仪器")源代码,以便跟踪您想要的内容,例如,修改每个调用站点以传递有效包含调用函数名称的附加参数,以及记录该函数的条目的每个函数条目(例如,有效地调用函数名称)和调用函数名称.检测过程需要从检测点,返回源文件和行构建交叉引用,以便以后报告.
您可以通过使用程序转换系统(PTS)以完全自动化的方式执行此操作,该程序转换系统可以解析源代码以生成AST,修改AST,然后重新生成源.通常,一个PTS会让你写的变化形式的源代码级的模式,"如果你看到这个,通过替换它的是 ".(不,正则表达式不能这样做).
在我的技术论文"任意语言的分支覆盖"中可以找到一个扩展示例.本文展示了PTS中的内容,然后展示了典型的仪器转换.
对于像C这样的语言来说,"正确"完成此操作可能很困难,因为您需要作为基础,一个完整的C解析器,这本身就是一项重大的技术壮举.最好以完整的形式获得这样的野兽(一些PTS有这个,考虑Concinnelle或DMS)或者你永远不会去做仪器部分.(另一个答案表明GCC内置了很多这种仪器功能).
合理地完成,额外仪器的执行成本非常适中(10-30%).这个概念已被用于实现测试覆盖工具和时序分析器,它们通过跟踪动态调用集来运行,这似乎正是您想要的.
修改运行代码的执行引擎以跟踪所需的详细信息.鉴于您希望使用C程序执行此操作,执行引擎本质上是底层指令集.
由于您无法合理地修改处理器芯片本身,因此您必须:
修改目标代码(使用程序转换的对象代码等效,参见上一段的概念,或参见PIN和Valgrind等工具获取实现细节),
或编写某种聪明的指令集解释器,它在遇到调用指令时收集所需的信息(由于其解释性质,这可能相当慢).
指令集本身(英特尔的X86指令集很大),目标文件格式等使实际指令集的实现变得复杂.不要指望这条路径比源仪表路径更容易; 只是期望它有一组不同的问题.
一个标准问题是知道要检测什么 ; 目标代码空间充满了不属于感兴趣的应用程序的代码(库,系统例程......),因此收集测试覆盖率数据并不感兴趣.为此,您需要一个符号表(对)来说明什么是应用程序代码,什么不是.您还需要能够从名称跟踪到原始源文件中的某个点,以便报告结果.
目标代码检测的另一个困难是很难判断已经内联的函数foo是否已被执行("覆盖").源检测方案没有这个问题,因为foo 在编译/内联之前会被检测,因此检测也会被内联.
处理线程是一个正交问题.在您记录事实X-calls-Y的每个地方,您只需附加从OS获得的线程ID(或其等效物)T,以产生X-calls-Y-in-T.从您的问题中不清楚为什么要这样做.
您可以决定跳过运行时实现的所有麻烦,并构建/获取可以生成调用图的静态分析器.使用Clang或DMS可以获得这些C语言.自己建造它可能很困难; 现在你需要一个完整的C解析器,完整的数据流和点到分析,以及调用图构造.细节不适合本段.
这非常依赖于平台.您需要的是或多或少地执行调试器所做的事情.
我接近这个的方式(当我不得不调试在新架构上构建gdb所需的工具时,我实际上实现了所有这些):
读取要调试的程序的符号表.你在评论中说Linux,所以要开始你需要一个读取ELF文件或读取ELF规范并自己实现的库.
使用ptrace系统调用在函数中创建断点main.如果幸运的话,您的系统ptrace可以创建断点并将记账保存在内核中.如果没有,您需要为您的cpu架构找出断点指令并自己实现断点.
接下来,您需要弄清楚动态加载程序中的调试挂钩,以便了解何时加载共享库.您还需要库的符号表.
现在您已经拥有了所有符号(诚然这是在您的程序运行一段时间之后,因为它必须加载动态库,但我们假装程序从那里开始main)在您从中获得的所有函数中创建断点符号表.
让程序运行.每次遇到断点时,反向查找符号表中的指令指针.将函数的名称保存到文件或要保存的位置.您现在可以跟踪函数调用.
或者只使用调试器.gdb可能可以编写脚本来做这样的事情.