Path: blob/main/contrib/llvm-project/llvm/lib/Analysis/CallPrinter.cpp
35234 views
//===- CallPrinter.cpp - DOT printer for call graph -----------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines '-dot-callgraph', which emit a callgraph.<fnname>.dot9// containing the call graph of a module.10//11// There is also a pass available to directly call dotty ('-view-callgraph').12//13//===----------------------------------------------------------------------===//1415#include "llvm/Analysis/CallPrinter.h"16#include "llvm/ADT/DenseMap.h"17#include "llvm/ADT/SmallSet.h"18#include "llvm/Analysis/BlockFrequencyInfo.h"19#include "llvm/Analysis/CallGraph.h"20#include "llvm/Analysis/HeatUtils.h"21#include "llvm/IR/Instructions.h"22#include "llvm/IR/Module.h"23#include "llvm/InitializePasses.h"24#include "llvm/Support/CommandLine.h"25#include "llvm/Support/DOTGraphTraits.h"26#include "llvm/Support/GraphWriter.h"2728using namespace llvm;2930namespace llvm {31template <class GraphType> struct GraphTraits;32} // namespace llvm3334// This option shows static (relative) call counts.35// FIXME:36// Need to show real counts when profile data is available37static cl::opt<bool> ShowHeatColors("callgraph-heat-colors", cl::init(false),38cl::Hidden,39cl::desc("Show heat colors in call-graph"));4041static cl::opt<bool>42ShowEdgeWeight("callgraph-show-weights", cl::init(false), cl::Hidden,43cl::desc("Show edges labeled with weights"));4445static cl::opt<bool>46CallMultiGraph("callgraph-multigraph", cl::init(false), cl::Hidden,47cl::desc("Show call-multigraph (do not remove parallel edges)"));4849static cl::opt<std::string> CallGraphDotFilenamePrefix(50"callgraph-dot-filename-prefix", cl::Hidden,51cl::desc("The prefix used for the CallGraph dot file names."));5253namespace llvm {5455class CallGraphDOTInfo {56private:57Module *M;58CallGraph *CG;59DenseMap<const Function *, uint64_t> Freq;60uint64_t MaxFreq;6162public:63std::function<BlockFrequencyInfo *(Function &)> LookupBFI;6465CallGraphDOTInfo(Module *M, CallGraph *CG,66function_ref<BlockFrequencyInfo *(Function &)> LookupBFI)67: M(M), CG(CG), LookupBFI(LookupBFI) {68MaxFreq = 0;6970for (Function &F : M->getFunctionList()) {71uint64_t localSumFreq = 0;72SmallSet<Function *, 16> Callers;73for (User *U : F.users())74if (isa<CallInst>(U))75Callers.insert(cast<Instruction>(U)->getFunction());76for (Function *Caller : Callers)77localSumFreq += getNumOfCalls(*Caller, F);78if (localSumFreq >= MaxFreq)79MaxFreq = localSumFreq;80Freq[&F] = localSumFreq;81}82if (!CallMultiGraph)83removeParallelEdges();84}8586Module *getModule() const { return M; }8788CallGraph *getCallGraph() const { return CG; }8990uint64_t getFreq(const Function *F) { return Freq[F]; }9192uint64_t getMaxFreq() { return MaxFreq; }9394private:95void removeParallelEdges() {96for (auto &I : (*CG)) {97CallGraphNode *Node = I.second.get();9899bool FoundParallelEdge = true;100while (FoundParallelEdge) {101SmallSet<Function *, 16> Visited;102FoundParallelEdge = false;103for (auto CI = Node->begin(), CE = Node->end(); CI != CE; CI++) {104if (!(Visited.insert(CI->second->getFunction())).second) {105FoundParallelEdge = true;106Node->removeCallEdge(CI);107break;108}109}110}111}112}113};114115template <>116struct GraphTraits<CallGraphDOTInfo *>117: public GraphTraits<const CallGraphNode *> {118static NodeRef getEntryNode(CallGraphDOTInfo *CGInfo) {119// Start at the external node!120return CGInfo->getCallGraph()->getExternalCallingNode();121}122123typedef std::pair<const Function *const, std::unique_ptr<CallGraphNode>>124PairTy;125static const CallGraphNode *CGGetValuePtr(const PairTy &P) {126return P.second.get();127}128129// nodes_iterator/begin/end - Allow iteration over all nodes in the graph130typedef mapped_iterator<CallGraph::const_iterator, decltype(&CGGetValuePtr)>131nodes_iterator;132133static nodes_iterator nodes_begin(CallGraphDOTInfo *CGInfo) {134return nodes_iterator(CGInfo->getCallGraph()->begin(), &CGGetValuePtr);135}136static nodes_iterator nodes_end(CallGraphDOTInfo *CGInfo) {137return nodes_iterator(CGInfo->getCallGraph()->end(), &CGGetValuePtr);138}139};140141template <>142struct DOTGraphTraits<CallGraphDOTInfo *> : public DefaultDOTGraphTraits {143144DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}145146static std::string getGraphName(CallGraphDOTInfo *CGInfo) {147return "Call graph: " +148std::string(CGInfo->getModule()->getModuleIdentifier());149}150151static bool isNodeHidden(const CallGraphNode *Node,152const CallGraphDOTInfo *CGInfo) {153if (CallMultiGraph || Node->getFunction())154return false;155return true;156}157158std::string getNodeLabel(const CallGraphNode *Node,159CallGraphDOTInfo *CGInfo) {160if (Node == CGInfo->getCallGraph()->getExternalCallingNode())161return "external caller";162if (Node == CGInfo->getCallGraph()->getCallsExternalNode())163return "external callee";164165if (Function *Func = Node->getFunction())166return std::string(Func->getName());167return "external node";168}169static const CallGraphNode *CGGetValuePtr(CallGraphNode::CallRecord P) {170return P.second;171}172173// nodes_iterator/begin/end - Allow iteration over all nodes in the graph174typedef mapped_iterator<CallGraphNode::const_iterator,175decltype(&CGGetValuePtr)>176nodes_iterator;177178std::string getEdgeAttributes(const CallGraphNode *Node, nodes_iterator I,179CallGraphDOTInfo *CGInfo) {180if (!ShowEdgeWeight)181return "";182183Function *Caller = Node->getFunction();184if (Caller == nullptr || Caller->isDeclaration())185return "";186187Function *Callee = (*I)->getFunction();188if (Callee == nullptr)189return "";190191uint64_t Counter = getNumOfCalls(*Caller, *Callee);192double Width =1931 + 2 * (double(Counter) / CGInfo->getMaxFreq());194std::string Attrs = "label=\"" + std::to_string(Counter) +195"\" penwidth=" + std::to_string(Width);196return Attrs;197}198199std::string getNodeAttributes(const CallGraphNode *Node,200CallGraphDOTInfo *CGInfo) {201Function *F = Node->getFunction();202if (F == nullptr)203return "";204std::string attrs;205if (ShowHeatColors) {206uint64_t freq = CGInfo->getFreq(F);207std::string color = getHeatColor(freq, CGInfo->getMaxFreq());208std::string edgeColor = (freq <= (CGInfo->getMaxFreq() / 2))209? getHeatColor(0)210: getHeatColor(1);211attrs = "color=\"" + edgeColor + "ff\", style=filled, fillcolor=\"" +212color + "80\"";213}214return attrs;215}216};217218} // namespace llvm219220namespace {221void doCallGraphDOTPrinting(222Module &M, function_ref<BlockFrequencyInfo *(Function &)> LookupBFI) {223std::string Filename;224if (!CallGraphDotFilenamePrefix.empty())225Filename = (CallGraphDotFilenamePrefix + ".callgraph.dot");226else227Filename = (std::string(M.getModuleIdentifier()) + ".callgraph.dot");228errs() << "Writing '" << Filename << "'...";229230std::error_code EC;231raw_fd_ostream File(Filename, EC, sys::fs::OF_Text);232233CallGraph CG(M);234CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI);235236if (!EC)237WriteGraph(File, &CFGInfo);238else239errs() << " error opening file for writing!";240errs() << "\n";241}242243void viewCallGraph(Module &M,244function_ref<BlockFrequencyInfo *(Function &)> LookupBFI) {245CallGraph CG(M);246CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI);247248std::string Title =249DOTGraphTraits<CallGraphDOTInfo *>::getGraphName(&CFGInfo);250ViewGraph(&CFGInfo, "callgraph", true, Title);251}252} // namespace253254namespace llvm {255PreservedAnalyses CallGraphDOTPrinterPass::run(Module &M,256ModuleAnalysisManager &AM) {257FunctionAnalysisManager &FAM =258AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();259260auto LookupBFI = [&FAM](Function &F) {261return &FAM.getResult<BlockFrequencyAnalysis>(F);262};263264doCallGraphDOTPrinting(M, LookupBFI);265266return PreservedAnalyses::all();267}268269PreservedAnalyses CallGraphViewerPass::run(Module &M,270ModuleAnalysisManager &AM) {271272FunctionAnalysisManager &FAM =273AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();274275auto LookupBFI = [&FAM](Function &F) {276return &FAM.getResult<BlockFrequencyAnalysis>(F);277};278279viewCallGraph(M, LookupBFI);280281return PreservedAnalyses::all();282}283} // namespace llvm284285namespace {286// Viewer287class CallGraphViewer : public ModulePass {288public:289static char ID;290CallGraphViewer() : ModulePass(ID) {}291292void getAnalysisUsage(AnalysisUsage &AU) const override;293bool runOnModule(Module &M) override;294};295296void CallGraphViewer::getAnalysisUsage(AnalysisUsage &AU) const {297ModulePass::getAnalysisUsage(AU);298AU.addRequired<BlockFrequencyInfoWrapperPass>();299AU.setPreservesAll();300}301302bool CallGraphViewer::runOnModule(Module &M) {303auto LookupBFI = [this](Function &F) {304return &this->getAnalysis<BlockFrequencyInfoWrapperPass>(F).getBFI();305};306307viewCallGraph(M, LookupBFI);308309return false;310}311312// DOT Printer313314class CallGraphDOTPrinter : public ModulePass {315public:316static char ID;317CallGraphDOTPrinter() : ModulePass(ID) {}318319void getAnalysisUsage(AnalysisUsage &AU) const override;320bool runOnModule(Module &M) override;321};322323void CallGraphDOTPrinter::getAnalysisUsage(AnalysisUsage &AU) const {324ModulePass::getAnalysisUsage(AU);325AU.addRequired<BlockFrequencyInfoWrapperPass>();326AU.setPreservesAll();327}328329bool CallGraphDOTPrinter::runOnModule(Module &M) {330auto LookupBFI = [this](Function &F) {331return &this->getAnalysis<BlockFrequencyInfoWrapperPass>(F).getBFI();332};333334doCallGraphDOTPrinting(M, LookupBFI);335336return false;337}338339} // end anonymous namespace340341char CallGraphViewer::ID = 0;342INITIALIZE_PASS(CallGraphViewer, "view-callgraph", "View call graph", false,343false)344345char CallGraphDOTPrinter::ID = 0;346INITIALIZE_PASS(CallGraphDOTPrinter, "dot-callgraph",347"Print call graph to 'dot' file", false, false)348349// Create methods available outside of this file, to use them350// "include/llvm/LinkAllPasses.h". Otherwise the pass would be deleted by351// the link time optimization.352353ModulePass *llvm::createCallGraphViewerPass() { return new CallGraphViewer(); }354355ModulePass *llvm::createCallGraphDOTPrinterPass() {356return new CallGraphDOTPrinter();357}358359360