| //===- CallPrinter.cpp - DOT printer for call graph -----------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines '-dot-callgraph', which emit a callgraph.<fnname>.dot |
| // containing the call graph of a module. |
| // |
| // There is also a pass available to directly call dotty ('-view-callgraph'). |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Analysis/CallPrinter.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/SmallSet.h" |
| #include "llvm/Analysis/BlockFrequencyInfo.h" |
| #include "llvm/Analysis/CallGraph.h" |
| #include "llvm/Analysis/HeatUtils.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/InitializePasses.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/DOTGraphTraits.h" |
| #include "llvm/Support/GraphWriter.h" |
| |
| using namespace llvm; |
| |
| namespace llvm { |
| template <class GraphType> struct GraphTraits; |
| } |
| |
| // This option shows static (relative) call counts. |
| // FIXME: |
| // Need to show real counts when profile data is available |
| static cl::opt<bool> ShowHeatColors("callgraph-heat-colors", cl::init(false), |
| cl::Hidden, |
| cl::desc("Show heat colors in call-graph")); |
| |
| static cl::opt<bool> |
| ShowEdgeWeight("callgraph-show-weights", cl::init(false), cl::Hidden, |
| cl::desc("Show edges labeled with weights")); |
| |
| static cl::opt<bool> |
| CallMultiGraph("callgraph-multigraph", cl::init(false), cl::Hidden, |
| cl::desc("Show call-multigraph (do not remove parallel edges)")); |
| |
| static cl::opt<std::string> CallGraphDotFilenamePrefix( |
| "callgraph-dot-filename-prefix", cl::Hidden, |
| cl::desc("The prefix used for the CallGraph dot file names.")); |
| |
| namespace llvm { |
| |
| class CallGraphDOTInfo { |
| private: |
| Module *M; |
| CallGraph *CG; |
| DenseMap<const Function *, uint64_t> Freq; |
| uint64_t MaxFreq; |
| |
| public: |
| std::function<BlockFrequencyInfo *(Function &)> LookupBFI; |
| |
| CallGraphDOTInfo(Module *M, CallGraph *CG, |
| function_ref<BlockFrequencyInfo *(Function &)> LookupBFI) |
| : M(M), CG(CG), LookupBFI(LookupBFI) { |
| MaxFreq = 0; |
| |
| for (Function &F : M->getFunctionList()) { |
| uint64_t localSumFreq = 0; |
| SmallSet<Function *, 16> Callers; |
| for (User *U : F.users()) |
| if (isa<CallInst>(U)) |
| Callers.insert(cast<Instruction>(U)->getFunction()); |
| for (Function *Caller : Callers) |
| localSumFreq += getNumOfCalls(*Caller, F); |
| if (localSumFreq >= MaxFreq) |
| MaxFreq = localSumFreq; |
| Freq[&F] = localSumFreq; |
| } |
| if (!CallMultiGraph) |
| removeParallelEdges(); |
| } |
| |
| Module *getModule() const { return M; } |
| |
| CallGraph *getCallGraph() const { return CG; } |
| |
| uint64_t getFreq(const Function *F) { return Freq[F]; } |
| |
| uint64_t getMaxFreq() { return MaxFreq; } |
| |
| private: |
| void removeParallelEdges() { |
| for (auto &I : (*CG)) { |
| CallGraphNode *Node = I.second.get(); |
| |
| bool FoundParallelEdge = true; |
| while (FoundParallelEdge) { |
| SmallSet<Function *, 16> Visited; |
| FoundParallelEdge = false; |
| for (auto CI = Node->begin(), CE = Node->end(); CI != CE; CI++) { |
| if (!(Visited.insert(CI->second->getFunction())).second) { |
| FoundParallelEdge = true; |
| Node->removeCallEdge(CI); |
| break; |
| } |
| } |
| } |
| } |
| } |
| }; |
| |
| template <> |
| struct GraphTraits<CallGraphDOTInfo *> |
| : public GraphTraits<const CallGraphNode *> { |
| static NodeRef getEntryNode(CallGraphDOTInfo *CGInfo) { |
| // Start at the external node! |
| return CGInfo->getCallGraph()->getExternalCallingNode(); |
| } |
| |
| typedef std::pair<const Function *const, std::unique_ptr<CallGraphNode>> |
| PairTy; |
| static const CallGraphNode *CGGetValuePtr(const PairTy &P) { |
| return P.second.get(); |
| } |
| |
| // nodes_iterator/begin/end - Allow iteration over all nodes in the graph |
| typedef mapped_iterator<CallGraph::const_iterator, decltype(&CGGetValuePtr)> |
| nodes_iterator; |
| |
| static nodes_iterator nodes_begin(CallGraphDOTInfo *CGInfo) { |
| return nodes_iterator(CGInfo->getCallGraph()->begin(), &CGGetValuePtr); |
| } |
| static nodes_iterator nodes_end(CallGraphDOTInfo *CGInfo) { |
| return nodes_iterator(CGInfo->getCallGraph()->end(), &CGGetValuePtr); |
| } |
| }; |
| |
| template <> |
| struct DOTGraphTraits<CallGraphDOTInfo *> : public DefaultDOTGraphTraits { |
| |
| DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} |
| |
| static std::string getGraphName(CallGraphDOTInfo *CGInfo) { |
| return "Call graph: " + |
| std::string(CGInfo->getModule()->getModuleIdentifier()); |
| } |
| |
| static bool isNodeHidden(const CallGraphNode *Node, |
| const CallGraphDOTInfo *CGInfo) { |
| if (CallMultiGraph || Node->getFunction()) |
| return false; |
| return true; |
| } |
| |
| std::string getNodeLabel(const CallGraphNode *Node, |
| CallGraphDOTInfo *CGInfo) { |
| if (Node == CGInfo->getCallGraph()->getExternalCallingNode()) |
| return "external caller"; |
| if (Node == CGInfo->getCallGraph()->getCallsExternalNode()) |
| return "external callee"; |
| |
| if (Function *Func = Node->getFunction()) |
| return std::string(Func->getName()); |
| return "external node"; |
| } |
| static const CallGraphNode *CGGetValuePtr(CallGraphNode::CallRecord P) { |
| return P.second; |
| } |
| |
| // nodes_iterator/begin/end - Allow iteration over all nodes in the graph |
| typedef mapped_iterator<CallGraphNode::const_iterator, |
| decltype(&CGGetValuePtr)> |
| nodes_iterator; |
| |
| std::string getEdgeAttributes(const CallGraphNode *Node, nodes_iterator I, |
| CallGraphDOTInfo *CGInfo) { |
| if (!ShowEdgeWeight) |
| return ""; |
| |
| Function *Caller = Node->getFunction(); |
| if (Caller == nullptr || Caller->isDeclaration()) |
| return ""; |
| |
| Function *Callee = (*I)->getFunction(); |
| if (Callee == nullptr) |
| return ""; |
| |
| uint64_t Counter = getNumOfCalls(*Caller, *Callee); |
| double Width = |
| 1 + 2 * (double(Counter) / CGInfo->getMaxFreq()); |
| std::string Attrs = "label=\"" + std::to_string(Counter) + |
| "\" penwidth=" + std::to_string(Width); |
| return Attrs; |
| } |
| |
| std::string getNodeAttributes(const CallGraphNode *Node, |
| CallGraphDOTInfo *CGInfo) { |
| Function *F = Node->getFunction(); |
| if (F == nullptr) |
| return ""; |
| std::string attrs; |
| if (ShowHeatColors) { |
| uint64_t freq = CGInfo->getFreq(F); |
| std::string color = getHeatColor(freq, CGInfo->getMaxFreq()); |
| std::string edgeColor = (freq <= (CGInfo->getMaxFreq() / 2)) |
| ? getHeatColor(0) |
| : getHeatColor(1); |
| attrs = "color=\"" + edgeColor + "ff\", style=filled, fillcolor=\"" + |
| color + "80\""; |
| } |
| return attrs; |
| } |
| }; |
| |
| } // end llvm namespace |
| |
| namespace { |
| void doCallGraphDOTPrinting( |
| Module &M, function_ref<BlockFrequencyInfo *(Function &)> LookupBFI) { |
| std::string Filename; |
| if (!CallGraphDotFilenamePrefix.empty()) |
| Filename = (CallGraphDotFilenamePrefix + ".callgraph.dot"); |
| else |
| Filename = (std::string(M.getModuleIdentifier()) + ".callgraph.dot"); |
| errs() << "Writing '" << Filename << "'..."; |
| |
| std::error_code EC; |
| raw_fd_ostream File(Filename, EC, sys::fs::OF_Text); |
| |
| CallGraph CG(M); |
| CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); |
| |
| if (!EC) |
| WriteGraph(File, &CFGInfo); |
| else |
| errs() << " error opening file for writing!"; |
| errs() << "\n"; |
| } |
| |
| void viewCallGraph(Module &M, |
| function_ref<BlockFrequencyInfo *(Function &)> LookupBFI) { |
| CallGraph CG(M); |
| CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); |
| |
| std::string Title = |
| DOTGraphTraits<CallGraphDOTInfo *>::getGraphName(&CFGInfo); |
| ViewGraph(&CFGInfo, "callgraph", true, Title); |
| } |
| } // namespace |
| |
| namespace llvm { |
| PreservedAnalyses CallGraphDOTPrinterPass::run(Module &M, |
| ModuleAnalysisManager &AM) { |
| FunctionAnalysisManager &FAM = |
| AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); |
| |
| auto LookupBFI = [&FAM](Function &F) { |
| return &FAM.getResult<BlockFrequencyAnalysis>(F); |
| }; |
| |
| doCallGraphDOTPrinting(M, LookupBFI); |
| |
| return PreservedAnalyses::all(); |
| } |
| |
| PreservedAnalyses CallGraphViewerPass::run(Module &M, |
| ModuleAnalysisManager &AM) { |
| |
| FunctionAnalysisManager &FAM = |
| AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); |
| |
| auto LookupBFI = [&FAM](Function &F) { |
| return &FAM.getResult<BlockFrequencyAnalysis>(F); |
| }; |
| |
| viewCallGraph(M, LookupBFI); |
| |
| return PreservedAnalyses::all(); |
| } |
| } // namespace llvm |
| |
| namespace { |
| // Viewer |
| class CallGraphViewer : public ModulePass { |
| public: |
| static char ID; |
| CallGraphViewer() : ModulePass(ID) {} |
| |
| void getAnalysisUsage(AnalysisUsage &AU) const override; |
| bool runOnModule(Module &M) override; |
| }; |
| |
| void CallGraphViewer::getAnalysisUsage(AnalysisUsage &AU) const { |
| ModulePass::getAnalysisUsage(AU); |
| AU.addRequired<BlockFrequencyInfoWrapperPass>(); |
| AU.setPreservesAll(); |
| } |
| |
| bool CallGraphViewer::runOnModule(Module &M) { |
| auto LookupBFI = [this](Function &F) { |
| return &this->getAnalysis<BlockFrequencyInfoWrapperPass>(F).getBFI(); |
| }; |
| |
| viewCallGraph(M, LookupBFI); |
| |
| return false; |
| } |
| |
| // DOT Printer |
| |
| class CallGraphDOTPrinter : public ModulePass { |
| public: |
| static char ID; |
| CallGraphDOTPrinter() : ModulePass(ID) {} |
| |
| void getAnalysisUsage(AnalysisUsage &AU) const override; |
| bool runOnModule(Module &M) override; |
| }; |
| |
| void CallGraphDOTPrinter::getAnalysisUsage(AnalysisUsage &AU) const { |
| ModulePass::getAnalysisUsage(AU); |
| AU.addRequired<BlockFrequencyInfoWrapperPass>(); |
| AU.setPreservesAll(); |
| } |
| |
| bool CallGraphDOTPrinter::runOnModule(Module &M) { |
| auto LookupBFI = [this](Function &F) { |
| return &this->getAnalysis<BlockFrequencyInfoWrapperPass>(F).getBFI(); |
| }; |
| |
| doCallGraphDOTPrinting(M, LookupBFI); |
| |
| return false; |
| } |
| |
| } // end anonymous namespace |
| |
| char CallGraphViewer::ID = 0; |
| INITIALIZE_PASS(CallGraphViewer, "view-callgraph", "View call graph", false, |
| false) |
| |
| char CallGraphDOTPrinter::ID = 0; |
| INITIALIZE_PASS(CallGraphDOTPrinter, "dot-callgraph", |
| "Print call graph to 'dot' file", false, false) |
| |
| // Create methods available outside of this file, to use them |
| // "include/llvm/LinkAllPasses.h". Otherwise the pass would be deleted by |
| // the link time optimization. |
| |
| ModulePass *llvm::createCallGraphViewerPass() { return new CallGraphViewer(); } |
| |
| ModulePass *llvm::createCallGraphDOTPrinterPass() { |
| return new CallGraphDOTPrinter(); |
| } |