【发布时间】:2019-01-22 04:14:28
【问题描述】:
【问题讨论】:
标签: debugging gdb llvm backtrace call-graph
【问题讨论】:
标签: debugging gdb llvm backtrace call-graph
有没有更快的方法来生成调用图?
当然有(为此使用 GDB 是完全不合适的)。
最简单的解决方案是使用GCC-finstrument-functions在每个函数入口和出口处插入一个调用,并在这些“注入”的函数中实现数据收集。有一个例子here。
【讨论】:
atos 工具,它提供了等效的功能。
dladdr解析:cmdlinelinux.blogspot.com/2020/01/…
既然您在 SO 上发布了这个问题并用 llvm 标记它,我认为这意味着您正在寻找使用 LLVM 的编程解决方案。
编写一个转换程序中每个函数的过程,以便在每次调用之前添加三个新指令。像这样的:
struct RecordCallGraph : public PassInfoMixin<RecordCallGraph> {
RecordCallGraph() = default;
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
};
您需要实现 run(),这将是大约 15 行代码。扫描函数中的基本块,检查每条指令是否isa<CallBase>,如果是,则在CallBase之前插入一点额外的代码。 (CallBase 是调用函数的指令的基类。)您插入一个对新函数的调用,void emitTraceInfo(char* caller, char* called) 或类似的东西。由于 LLVM IR 是类型安全的,因此您需要将调用者 (&F) 和被调用函数 (callBase->getCalledValue()) 转换为函数的正确类型(示例中为 char*)。
获得该演员表的最简单方法可能是CastInst::Create(CastInst::BitCast, &F, charStarType, "", callBase),它会创建一个从&F 到charStarType 的新演员表,并将其插入到callBase 之前。
最后,您必须实现新的emitTraceInfo 并将其链接到程序中。每次一个函数调用另一个函数时都会调用它,并且可以记录调用。你会发现它比 gdb 快一百倍。最慢的部分可能是将 16 多个字节写入文件。
【讨论】:
bitcast 指令和一个call,每个都有两个参数。当然,所有六个参数都取决于环境。
Function&F:for(auto & b : F.getBasicBlockList()) {…