Search code examples
c++cclangcode-analysiscall-graph

Search C++ call graph recursively for specific functions


I have a large C/C++ project where I would like to analyse the call graph for a subset of functions.

Ex for something like:

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

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

void StartFunc()
{
  IntermediateFunc();
}

I would like to get the list of functions that starts with "A_" called directly or indirectly from StartFunc.

My first thought was using clang which has the CallGraph action, but the documentation is sparse and I am slowly getting to the conclusion that I cannot use it how I want.

So the question: How do I use clangs tooling libraries to perform generate such a list?


Solution

  • I find the answer from polkovnikov.ph to be the cleaner method of doing this. I wasn't aware that the .dot is so simple, and I will definitely use that way for similar problems.

    Unfortunately I also have to analyse software components where the interface to other components are C functions - which are used through the extern keyword. They do not show up in the clang call graph because of an internal filter (includeInGraph (const Decl *D)) in clang::CallGraph.

    So I had to copy clang::CallGraph, remove the limitation and use it in an clang::ASTConsumer like:

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