[cfe-commits] [PATCH] call graph building

Zhongxing Xu xuzhongxing at gmail.com
Wed Jul 15 17:37:48 PDT 2009


Thanks for review. I'll commit it.

On Thu, Jul 16, 2009 at 5:36 AM, Ted Kremenek<kremenek at apple.com> wrote:
> I think this looks good, especially for an initial patch.  We obviously will
> refine it over time.
> One nit:
>   llvm::cl::ParseCommandLineOptions(argc, argv, "clang analyzer");
> should be:
>   llvm::cl::ParseCommandLineOptions(argc, argv, "clang-wpa");
>
>
> On Jul 14, 2009, at 10:03 PM, Zhongxing Xu wrote:
>
> New files attached. To print decl names, a new mapping from caller's
> nodes to their ASTContexts is added.
>
> On Wed, Jul 15, 2009 at 8:33 AM, Ted Kremenek<kremenek at apple.com> wrote:
>
> On Jul 13, 2009, at 6:31 AM, Zhongxing Xu wrote:
>
> This is an initial implementation of call graph building based on pch
>
> reader and the new Program/Entity facility. It is very primitive but
>
> functional.
>
> Put the files under clang/tools/wpa (meaning 'whole program
>
> analysis'). After 'make', a new command 'clang-analyze' will be
>
> generated. It takes a list of .ast files and build call graph over
>
> them.
>
> Hi Zhongxing,
>
> I would call the program 'clang-callgraph' so that people don't confuse it
>
> with the "static analyzer".  Comments inline.
>
> #include "CallGraph.h"
>
> #include "clang/AST/ASTContext.h"
>
> #include "clang/AST/StmtVisitor.h"
>
> using namespace clang;
>
> using namespace idx;
>
> CallGraph *CallGraph::G = 0;
>
> namespace {
>
> class CGBuilder : public StmtVisitor<CGBuilder> {
>
>   CallGraph &G;
>
>   FunctionDecl *FD;
>
> public:
>
>   CGBuilder(CallGraph &g, FunctionDecl *fd)
>
>     : G(g), FD(fd) {}
>
>   void VisitCompoundStmt(CompoundStmt *S) {
>
>     VisitChildren(S);
>
>   }
>
>   void VisitCallExpr(CallExpr *CE);
>
>   void VisitChildren(Stmt *S) {
>
>     for (Stmt::child_iterator I=S->child_begin(), E=S->child_end(); I !=
>
> E;++I)
>
>       if (*I)
>
>         static_cast<CGBuilder*>(this)->Visit(*I);
>
>   }
>
> };
>
> }
>
> You can make this a little shorter by using 'CFGRecStmtVisitor' instead of
>
> StmtVisitor.  It basically does the recursion for you.
>
> I did look at 'CFGRecStmtVisitor'. Call graph building does not need
> CFG building. I think a plain StmtVisitor is enough.
>
>
> void CGBuilder::VisitCallExpr(CallExpr *CE) {
>
>   const Entity *CallerEnt = Entity::get(FD, G.getProgram());
>
> Seems like you can make things a little faster by making 'CallerEnt' an
>
> instance variable that is initialized to NULL, and then lazily initialize it
>
> here if it is NULL.  This will speed things up when there are no calls.
>
>   CallGraphNode *Node = G.getOrInsertFunction(CallerEnt);
>
> This too can probably be memoized, since it seems that a CGBuilder is built
>
> on a per-function basis.
>
>   Expr *Callee = CE->getCallee();
>
>   if (CastExpr *CE = dyn_cast<CastExpr>(Callee))
>
>     Callee = CE->getSubExpr();
>
>   if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Callee)) {
>
>     Decl *D = DRE->getDecl();
>
>     if (FunctionDecl *CalleeDecl = dyn_cast<FunctionDecl>(D)) {
>
>       const Entity *Ent = Entity::get(CalleeDecl, G.getProgram());
>
>       CallGraphNode *CalleeNode = G.getOrInsertFunction(Ent);
>
>       Node->addCallee(ASTLocation(FD, CE), CalleeNode);
>
>     }
>
>   }
>
> }
>
> Seems reasonable.
>
> void CallGraph::addTU(const ASTUnit &AST) {
>
>   if (!G)
>
>     G = new CallGraph();
>
>   const ASTContext &Ctx = AST.getASTContext();
>
>   DeclContext *DC = Ctx.getTranslationUnitDecl();
>
>   for (DeclContext::decl_iterator I = DC->decls_begin(), E =
>
> DC->decls_end();
>
>        I != E; ++I) {
>
>     if (FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) {
>
>       if (FD->isThisDeclarationADefinition()) {
>
>         CGBuilder builder(*G, FD);
>
>         builder.Visit(FD->getBody());
>
>       }
>
>     }
>
>   }
>
> }
>
> Seems reasonable.  I'm not certain if this will handle code buried in C++
>
> namespaces, but this implementation right now doesn't handle C++ anyway (so
>
> it can be done later).
>
> CallGraphNode *CallGraph::getOrInsertFunction(const Entity *F) {
>
>   CallGraphNode *&Node = FunctionMap[F];
>
>   if (Node)
>
>     return Node;
>
>   return Node = new CallGraphNode(F);
>
> }
>
> Seems reasonable.
>
> #ifndef LLVM_CLANG_ANALYSIS_CALLGRAPH
>
> #define LLVM_CLANG_ANALYSIS_CALLGRAPH
>
> #include "clang/Index/ASTLocation.h"
>
> #include "clang/Index/Entity.h"
>
> #include "clang/Index/Program.h"
>
> #include "clang/Frontend/ASTUnit.h"
>
> #include <vector>
>
> #include <map>
>
> namespace clang {
>
> class CallGraphNode {
>
>   const idx::Entity *F;
>
>   typedef std::pair<idx::ASTLocation, CallGraphNode*> CallRecord;
>
>   std::vector<CallRecord> CalledFunctions;
>
> public:
>
>   CallGraphNode(const idx::Entity *f) : F(f) {}
>
>   void addCallee(idx::ASTLocation L, CallGraphNode *Node) {
>
>     CalledFunctions.push_back(std::make_pair(L, Node));
>
>   }
>
> };
>
> class CallGraph {
>
>   static CallGraph *G;
>
>   idx::Program Prog;
>
>   typedef std::map<const idx::Entity *, CallGraphNode *> FunctionMapTy;
>
> Eventually, if you care about speed, you'll probably want to use a DenseMap.
>
>  I don't see you using the sortedness property of std::map here.
>
>   FunctionMapTy FunctionMap;
>
> public:
>
>   static CallGraph *get() { return G; }
>
>   static void addTU(const ASTUnit &AST);
>
> Is there a reason you are using static methods/instance variables?  I know
>
> this is a small program, but if you wanted to design this to be more
>
> reusable I think you'll want to to avoid using them in this way.
>
> I originally wanted to use Singleton pattern. Now I changed to a plain
> implementation.
>
>
>   idx::Program &getProgram() { return Prog; }
>
>   CallGraphNode *getOrInsertFunction(const idx::Entity * F);
>
> };
>
> }
>
> #endif
>
> #include "CallGraph.h"
>
> #include "clang/Basic/FileManager.h"
>
> #include "clang/Index/TranslationUnit.h"
>
> #include "llvm/Support/CommandLine.h"
>
> #include "llvm/Support/raw_ostream.h"
>
> using namespace clang;
>
> using namespace idx;
>
> static llvm::cl::list<std::string>
>
> InputFilenames(llvm::cl::Positional, llvm::cl::desc("<input AST files>"));
>
> class TUnit : public TranslationUnit {
>
> public:
>
>   TUnit(ASTUnit *ast, const std::string &filename)
>
>     : AST(ast), Filename(filename) {}
>
>   ASTContext &getASTContext() { return AST->getASTContext(); }
>
>   llvm::OwningPtr<ASTUnit> AST;
>
>   std::string Filename;
>
> };
>
> int main(int argc, char **argv) {
>
>   llvm::cl::ParseCommandLineOptions(argc, argv, "clang analyzer");
>
> "clang analyzer" is the name people associate with the static analyzer.  I'd
>
> just use 'clang-callgraph', since that is what this tool does.
>
>   FileManager FileMgr;
>
>   std::vector<TUnit*> TUnits;
>
>   if (InputFilenames.empty())
>
>     return 0;
>
>   for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i) {
>
>     const std::string &InFile = InputFilenames[i];
>
>     std::string ErrMsg;
>
>     llvm::OwningPtr<ASTUnit> AST;
>
>     AST.reset(ASTUnit::LoadFromPCHFile(InFile, FileMgr, &ErrMsg));
>
>     if (!AST) {
>
>       llvm::errs() << "[" << InFile << "] error: " << ErrMsg << '\n';
>
>       return 1;
>
>     }
>
>     TUnit *TU = new TUnit(AST.take(), InFile);
>
>     TUnits.push_back(TU);
>
>   }
>
>   for (unsigned i = 0, e = TUnits.size(); i != e; ++i)
>
>     CallGraph::addTU(*(TUnits[i]->AST));
>
>   CallGraph *CG = CallGraph::get();
>
> }
>
> I think this has the potentially to be a nice little tool, but I think in
>
> order for it to be useful it should produce some basic output.  I think
>
> printing out the callgraph would be nice.  You can also defined some
>
> GraphTraits for the CallGraph (similar to what we do in
>
> clang/include/AST/CFG.h) to get automatic GraphViz visualization.
>
> All in all I think this is cool, I can look forward to playing around with
>
> it!
>
> <clang-wpa.cpp><CallGraph.cpp><CallGraph.h>
>




More information about the cfe-commits mailing list