【问题标题】:Search C++ call graph recursively for specific functions [closed]递归搜索 C++ 调用图以获取特定函数 [关闭]
【发布时间】:2014-06-04 10:58:00
【问题描述】:

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

例如:

void A_Func1(){}
void A_Func2(){}

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

void StartFunc()
{
  IntermediateFunc();
}

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

我的第一个想法是使用具有 CallGraph 操作的 clang,但文档很少,我慢慢得出结论,我无法按照自己的意愿使用它。

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

【问题讨论】:

  • 嗯,提问很棘手。显然寻求解决上述问题的工具是可以的,询问如何使用clang也可以。只是不要同时问两个。这是题外话。
  • 更新为一个具体的clang问题。

标签: c++ c clang code-analysis call-graph


【解决方案1】:

Clang outputs .dot 为其调用图文件。这是一个相当大的simple format,可以使用PEG.js 之类的工具轻松解析。当你接到DAG的调用时,你可以运行一个简单的DFS,它会标记所有带有A_前缀的节点。

另一个解决方案是使用 Clang 的代码库来do it yourself。正如您已经发现的那样,这是一种相当复杂的方法。

此外,您应该知道 clang 会生成一个乐观的调用图,即“可以调用哪些函数”。例如,void f() { if (g()) h(); }“呼叫”gh,即使 bool g() { return false; }

您可能正在寻找“真正从那里调用的所有函数”。然后你需要运行some profiler 来收集调用堆栈。

最好的解决方案是为某些 IDE 创建一个debugger script,但我不知道有任何 C++ IDE 有这样的选项。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 在我终于记起这个咒语有 48 小时的冷却时间之前,你为什么不接受你自己的答案:) 无论如何,你在 LLVM API 上做了一个很好的例子。跨度>
    • @polkovnikov.ph:谢谢。遗憾的是它不适用于 .dot 文件。但由于无论如何我都必须适应 clang 内部结构,第二个最好的方法是基于 clang::CallGraph 创建我自己的 AST 访问器。
    猜你喜欢
    • 2021-04-28
    • 1970-01-01
    • 1970-01-01
    • 2018-05-06
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 2023-04-05
    • 2017-11-23
    相关资源
    最近更新 更多