我有一个大型的C / C ++项目,我想分析函数子集的调用图。
Ex之类的:
void A_Func1(){}
void A_Func2(){}
void IntermediateFunc()
{
A_Func1();
A_Func2();
}
void StartFunc()
{
IntermediateFunc();
}
我想从StartFunc直接或间接调用以“A_”开头的函数列表。
我的第一个想法是使用具有CallGraph动作的clang,但是文档很稀疏,我慢慢得出结论,我不能按照我想要的方式使用它。
所以问题是: 如何使用clangs工具库来执行生成这样的列表?
答案 0 :(得分:3)
Clang outputs .dot
为其调用图文件。它非常simple format,可以使用像PEG.js这样的工具轻松解析。当您接到电话DAG时,您可以运行一个简单的DFS,标记所有带有A_
前缀的节点。
另一种解决方案是将Clang的代码库用于do it yourself。正如您已经发现的那样,这是一个非常复杂的方法。
此外,您应该知道clang会生成一个乐观的调用图,即"可以调用哪些函数"。例如,void f() { if (g()) h(); }
"调用" g
和h
,即使bool g() { return false; }
。
可能,您正在寻找"从那里真正调用的所有功能"。然后,您需要运行some profiler来收集调用堆栈。
最好的解决方案是为某些IDE制作debugger script,但我不知道任何具有此类选项的C ++ IDE。
答案 1 :(得分:1)
我发现 polkovnikov.ph 的答案是更干净的方法。我不知道.dot
是如此简单,我肯定会用这种方式解决类似问题。
不幸的是,我还必须分析其他组件的接口是 C 函数的软件组件 - 这些函数通过extern
关键字使用。由于clang::CallGraph中的内部过滤器(includeInGraph (const Decl *D)
),它们不会显示在 clang 调用图中。
所以我必须复制clang::CallGraph
,删除限制并在clang::ASTConsumer
中使用它:
virtual void HandleTranslationUnit(clang::ASTContext &Context) {
_visitor.TraverseDecl(Context.getTranslationUnitDecl());
for (auto root : _visitor)
{
if (const clang::NamedDecl* namedDecl = llvm::dyn_cast_or_null<clang::NamedDecl>(root.first))
if(namedDecl->getIdentifier() != nullptr && namedDecl->getIdentifier()->getName().startswith("Start"))
{
llvm::outs() << "StartFunc: " << namedDecl->getName() << "\n";
printAFunctions(root.second);
}
}
}
void printAFunctions(const clang::CallGraphNode* node)
{
if (node != nullptr)
{
if (const clang::NamedDecl* namedDecl = llvm::dyn_cast_or_null<clang::NamedDecl>(node->getDecl()))
{
if (namedDecl->getName().startswith("A_"))
{
llvm::outs() << "A_ call: " << namedDecl->getName() << "\n";
}
}
for (auto subNode : *node)
{
printAFunctions(subNode);
}
}
}