更快的调用图生成回溯?

Faster backtrace for call graph generation?

我使用以下方法生成调用图。

https://github.com/tarun27sh/gdb_graphs

但是 gdb 被回溯显着 (x100) 减慢了。有没有更快的方法来生成调用图?

因为你 post 这个问题在 SO 上并用 llvm 标记,我认为这意味着你正在寻找使用 LLVM 的编程解决方案。

编写一个 pass,转换程序中的每个函数,以便在每次调用之前添加三个新指令。像这样:

struct RecordCallGraph : public PassInfoMixin<RecordCallGraph> {
  RecordCallGraph() = default;
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
};

您需要实施 运行(),这大约需要 15 行代码。扫描函数中的基本块,检查每条指令是否isa<CallBase>,如果是,则在CallBase之前插入一点额外的代码。 (CallBase 是调用函数的指令的基础 class。)您插入对新函数的调用,void emitTraceInfo(char* caller, char* called) 或类似的东西。由于 LLVM IR 是类型安全的,因此您需要将调用者 (&F) 和被调用函数 (callBase->getCalledValue()) 转换为适合您的函数的正确类型(示例中的 char*)。

获得该转换的最简单方法可能是 CastInst::Create(CastInst::BitCast, &F, charStarType, "", callBase),它会创建一个从 &FcharStarType 的新转换,并将其插入紧接在 callBase 之前。

最后,您必须将新的 emitTraceInfo 和 link 实施到程序中。每次一个函数调用另一个函数时都会调用它,并且可以记录调用。你会发现它比 gdb 快一百倍。最慢的部分可能是将 16 多个字节写入文件。

Is there a much faster way to generate call graphs?

当然有(为此使用GDB是完全不合适的)。

最简单的解决方案是使用GCC -finstrument-functions 在每个函数入口和出口处插入一个调用,并在这些"injected" 函数中实现数据收集。有一个例子 here.