搜索c++调用图递归地查找特定函数

Search C++ call graph recursively for specific functions

本文关键字:查找 函数 递归 c++ 调用 搜索      更新时间:2023-10-16

我有一个大型的C/c++项目,我想分析函数子集的调用图。

例如:

void A_Func1(){}
void A_Func2(){}
void IntermediateFunc()
{
  A_Func1();
  A_Func2();
}
void StartFunc()
{
  IntermediateFunc();
}

我想从StartFunc直接或间接调用以"A_"开头的函数列表。

我的第一个想法是使用clang,它有CallGraph动作,但是文档很少,我慢慢地得出结论,我不能按照我想要的方式使用它。

所以问题是:我如何使用clangs工具库来生成这样的列表?

Clang为它的callgraph输出.dot文件。它是一种非常简单的格式,可以很容易地用像PEG.js这样的工具进行解析。当您得到调用DAG时,您可以运行一个简单的DFS,它用A_前缀标记所有节点。

另一个解决方案是使用Clang的代码库自己完成。正如你已经发现的,这是一个相当复杂的方法。

另外,您应该意识到clang生成一个乐观调用图,即:什么函数可以被称为。例如,void f() { if (g()) h(); }"调用"gh,即使bool g() { return false; }

可能,你正在寻找"所有真正从那里调用的函数"。然后,您需要运行一些分析器来收集调用堆栈。

最好的解决方案是为一些IDE做一个调试器脚本,但我不知道任何IDE的c++有这样的选项。

我从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);
        }
    }
}