├── .gitignore ├── images ├── coolwarm.png ├── heat-cfg.png ├── heat-cfg-only.png ├── heat-callgraph.png └── heat-callgraph-full.png ├── src ├── CMakeLists.txt ├── HeatCallPrinter.h ├── HeatCFGPrinter.h ├── HeatUtils.h ├── HeatUtils.cpp ├── HeatCallPrinter.cpp └── HeatCFGPrinter.cpp ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | test/* 3 | -------------------------------------------------------------------------------- /images/coolwarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcorcs/llvm-heat-printer/HEAD/images/coolwarm.png -------------------------------------------------------------------------------- /images/heat-cfg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcorcs/llvm-heat-printer/HEAD/images/heat-cfg.png -------------------------------------------------------------------------------- /images/heat-cfg-only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcorcs/llvm-heat-printer/HEAD/images/heat-cfg-only.png -------------------------------------------------------------------------------- /images/heat-callgraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcorcs/llvm-heat-printer/HEAD/images/heat-callgraph.png -------------------------------------------------------------------------------- /images/heat-callgraph-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcorcs/llvm-heat-printer/HEAD/images/heat-callgraph-full.png -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(HeatCFGPrinter MODULE HeatCFGPrinter.cpp HeatUtils.cpp) 2 | add_library(HeatCallPrinter MODULE HeatCallPrinter.cpp HeatUtils.cpp) 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.4.3) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | set(CMAKE_CXX_FLAGS "-Wall -fno-rtti") 6 | 7 | find_package(LLVM REQUIRED CONFIG) 8 | 9 | add_definitions(${LLVM_DEFINITIONS}) 10 | include_directories(${LLVM_INCLUDE_DIRS}) 11 | 12 | add_subdirectory(src) 13 | -------------------------------------------------------------------------------- /src/HeatCallPrinter.h: -------------------------------------------------------------------------------- 1 | //===-- HeatCallPrinter.h - CFG printer external interface ------*- C++ -*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // This file defines a 'dot-heat-cfg' analysis pass, which emits the 11 | // cfg..dot file for each function in the program, with a graph of the 12 | // CFG for that function coloured with heat map depending on the basic block 13 | // frequency. 14 | // 15 | // This file defines external functions that can be called to explicitly 16 | // instantiate the CFG printer. 17 | // 18 | //===----------------------------------------------------------------------===// 19 | 20 | #ifndef LLVM_ANALYSIS_HEATCALLPRINTER_H 21 | #define LLVM_ANALYSIS_HEATCALLPRINTER_H 22 | 23 | #include "llvm/IR/Module.h" 24 | #include "llvm/Pass.h" 25 | 26 | using namespace llvm; 27 | 28 | namespace { 29 | 30 | class HeatCallGraphDOTPrinterPass : public ModulePass { 31 | public: 32 | static char ID; 33 | HeatCallGraphDOTPrinterPass() : ModulePass(ID) {} 34 | 35 | void getAnalysisUsage(AnalysisUsage &AU) const; 36 | bool runOnModule(Module &M) override; 37 | }; 38 | 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/HeatCFGPrinter.h: -------------------------------------------------------------------------------- 1 | //===-- HeatCFGPrinter.h - CFG printer external interface -------*- C++ -*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // This file defines a 'dot-heat-cfg' analysis pass, which emits the 11 | // cfg..dot file for each function in the program, with a graph of the 12 | // CFG for that function coloured with heat map depending on the basic block 13 | // frequency. 14 | // 15 | // This file defines external functions that can be called to explicitly 16 | // instantiate the CFG printer. 17 | // 18 | //===----------------------------------------------------------------------===// 19 | 20 | #ifndef LLVM_ANALYSIS_HEATCFGPRINTER_H 21 | #define LLVM_ANALYSIS_HEATCFGPRINTER_H 22 | 23 | #include "llvm/IR/Module.h" 24 | #include "llvm/Pass.h" 25 | 26 | using namespace llvm; 27 | 28 | namespace { 29 | 30 | class HeatCFGPrinterPass : public ModulePass { 31 | public: 32 | static char ID; 33 | HeatCFGPrinterPass() : ModulePass(ID) {} 34 | 35 | void getAnalysisUsage(AnalysisUsage &AU) const; 36 | bool runOnModule(Module &M) override; 37 | }; 38 | 39 | class HeatCFGOnlyPrinterPass : public ModulePass { 40 | public: 41 | static char ID; 42 | HeatCFGOnlyPrinterPass() : ModulePass(ID) {} 43 | 44 | void getAnalysisUsage(AnalysisUsage &AU) const; 45 | bool runOnModule(Module &M) override; 46 | 47 | }; 48 | 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/HeatUtils.h: -------------------------------------------------------------------------------- 1 | //===-- CFGPrinter.h - CFG printer external interface -----------*- C++ -*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #ifndef LLVM_ANALYSIS_HEATUTILS_H 14 | #define LLVM_ANALYSIS_HEATUTILS_H 15 | 16 | #include "llvm/Analysis/BlockFrequencyInfo.h" 17 | #include "llvm/IR/BasicBlock.h" 18 | #include "llvm/IR/Function.h" 19 | #include "llvm/IR/Module.h" 20 | 21 | #include 22 | 23 | using namespace llvm; 24 | 25 | namespace llvm { 26 | 27 | bool hasProfiling(Module &M); 28 | 29 | uint64_t getBlockFreq(const BasicBlock *BB, BlockFrequencyInfo *BFI, 30 | bool useHeuristic=true); 31 | 32 | uint64_t getNumOfCalls(Function &callerFunction, Function &calledFunction, 33 | function_ref LookupBFI, 34 | bool useHeuristic=true); 35 | 36 | uint64_t getMaxFreq(Function &F, BlockFrequencyInfo *BFI, 37 | bool useHeuristic=true); 38 | 39 | uint64_t getMaxFreq(Module &M, 40 | function_ref LookupBFI, 41 | bool useHeuristic=true); 42 | 43 | std::string getHeatColor(uint64_t freq, uint64_t maxFreq); 44 | 45 | std::string getHeatColor(double percent); 46 | 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/HeatUtils.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "HeatUtils.h" 3 | 4 | #include "llvm/IR/Instructions.h" 5 | 6 | namespace llvm { 7 | 8 | static const std::string heatPalette[100] = {"#3d50c3", "#4055c8", "#4358cb", "#465ecf", "#4961d2", "#4c66d6", "#4f69d9", "#536edd", "#5572df", "#5977e3", "#5b7ae5", "#5f7fe8", "#6282ea", "#6687ed", "#6a8bef", "#6c8ff1", "#7093f3", "#7396f5", "#779af7", "#7a9df8", "#7ea1fa", "#81a4fb", "#85a8fc", "#88abfd", "#8caffe", "#8fb1fe", "#93b5fe", "#96b7ff", "#9abbff", "#9ebeff", "#a1c0ff", "#a5c3fe", "#a7c5fe", "#abc8fd", "#aec9fc", "#b2ccfb", "#b5cdfa", "#b9d0f9", "#bbd1f8", "#bfd3f6", "#c1d4f4", "#c5d6f2", "#c7d7f0", "#cbd8ee", "#cedaeb", "#d1dae9", "#d4dbe6", "#d6dce4", "#d9dce1", "#dbdcde", "#dedcdb", "#e0dbd8", "#e3d9d3", "#e5d8d1", "#e8d6cc", "#ead5c9", "#ecd3c5", "#eed0c0", "#efcebd", "#f1ccb8", "#f2cab5", "#f3c7b1", "#f4c5ad", "#f5c1a9", "#f6bfa6", "#f7bca1", "#f7b99e", "#f7b599", "#f7b396", "#f7af91", "#f7ac8e", "#f7a889", "#f6a385", "#f5a081", "#f59c7d", "#f4987a", "#f39475", "#f29072", "#f08b6e", "#ef886b", "#ed8366", "#ec7f63", "#e97a5f", "#e8765c", "#e57058", "#e36c55", "#e16751", "#de614d", "#dc5d4a", "#d85646", "#d65244", "#d24b40", "#d0473d", "#cc403a", "#ca3b37", "#c53334", "#c32e31", "#be242e", "#bb1b2c", "#b70d28"}; 9 | 10 | static const unsigned heatSize = 100; 11 | 12 | 13 | bool hasProfiling(Module &M){ 14 | for (Function &F : M) { 15 | for (BasicBlock &BB : F) { 16 | Instruction *TI = BB.getTerminator(); 17 | if (TI==nullptr) 18 | continue; 19 | if (TI->getMetadata(llvm::LLVMContext::MD_prof)!=nullptr) 20 | return true; 21 | } 22 | } 23 | return false; 24 | } 25 | 26 | uint64_t getBlockFreq(const BasicBlock *BB, BlockFrequencyInfo *BFI, 27 | bool useHeuristic){ 28 | uint64_t freqVal = 0; 29 | if (!useHeuristic) { 30 | Optional< uint64_t > freq = BFI->getBlockProfileCount(BB); 31 | if (freq.hasValue()) 32 | freqVal = freq.getValue(); 33 | } else { 34 | freqVal = BFI->getBlockFreq(BB).getFrequency(); 35 | } 36 | return freqVal; 37 | } 38 | 39 | uint64_t getNumOfCalls(Function &callerFunction, Function &calledFunction, 40 | function_ref LookupBFI){ 41 | auto *BFI = LookupBFI(callerFunction); 42 | uint64_t counter = 0; 43 | for (BasicBlock &BB : callerFunction) { 44 | uint64_t freq = getBlockFreq(&BB,BFI); 45 | for (Instruction &I : BB) { 46 | if (CallInst *Call = dyn_cast(&I)) { 47 | if (Call->getCalledFunction()==(&calledFunction)) 48 | counter += freq; 49 | } 50 | } 51 | } 52 | return counter; 53 | } 54 | 55 | uint64_t getMaxFreq(Function &F, BlockFrequencyInfo *BFI, bool useHeuristic){ 56 | uint64_t maxFreq = 0; 57 | for (BasicBlock &BB : F) { 58 | uint64_t freqVal = getBlockFreq(&BB,BFI,useHeuristic); 59 | if (freqVal>=maxFreq) 60 | maxFreq = freqVal; 61 | } 62 | return maxFreq; 63 | } 64 | 65 | 66 | uint64_t getMaxFreq(Module &M, 67 | function_ref LookupBFI, 68 | bool useHeuristic){ 69 | uint64_t maxFreq = 0; 70 | for (Function &F : M) { 71 | if (F.isDeclaration()) 72 | continue; 73 | uint64_t localMaxFreq = getMaxFreq(F,LookupBFI(F),useHeuristic); 74 | if (localMaxFreq>=maxFreq) 75 | maxFreq = localMaxFreq; 76 | } 77 | return maxFreq; 78 | } 79 | 80 | std::string getHeatColor(uint64_t freq, uint64_t maxFreq){ 81 | if (freq>maxFreq) freq = maxFreq; 82 | unsigned colorId = unsigned( round((double(freq)/maxFreq)*(heatSize-1.0)) ); 83 | return heatPalette[colorId]; 84 | } 85 | 86 | std::string getHeatColor(double percent){ 87 | if (percent>1.0) 88 | percent = 1.0; 89 | if (percent<0.0) 90 | percent = 0.0; 91 | unsigned colorId = unsigned( round(percent*(heatSize-1.0)) ); 92 | return heatPalette[colorId]; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LLVM Heat Printer 2 | 3 | LLVM Heat Printer provides visualization assistance for profiling. 4 | It implements analysis passes that generate visualization (dot) files that depict the (profiled) execution frequency of a piece of code using a cool/warm color map. 5 | 6 | Cool/Warm color map: 7 | ![CoolWarm Map](https://github.com/rcorcs/llvm-heat-printer/raw/master/images/coolwarm.png) 8 | 9 | LLVM Heat Printer supports profiling annotation. 10 | In order to see how to use profiling information, look at Section [Using Profiling]. 11 | If no profiling is used, the basic block frequencies are estimated by means of heuristics. 12 | 13 | ## Build 14 | 15 | Assuming that you already have LLVM libraries installed (LLVM version 5.x.x). 16 | In a build directory, use the following commands for building the LLVM Heat Printer libraries. 17 | ``` 18 | $> cmake [-DLLVM_DIR=] 19 | $> make 20 | ``` 21 | The argument -DLLVM_DIR is optional, in case you want to specify a directory that contains a build of LLVM. 22 | 23 | ## Heat CFG Printer 24 | 25 | The analysis pass '-dot-heat-cfg' generates the heat map of the CFG (control-flow graph) based on the basic block frequency. 26 | Use '-dot-heat-cfg-only' for the simplified output without the LLVM code for each basic block. 27 | 28 |

29 | 30 | 31 |

32 | 33 | The user can also choose between an intra-function or inter-function maximum frequency reference. 34 | For the intra-function heat map, activated with the flag '-heat-cfg-per-function', the heat scale will consider only the frequencies of the basic blocks inside the current function, i.e., every function will have a basic block with maximum heat. 35 | For the inter-function heat map (default), the heat scale will consider all functions of the current module (translation unit), i.e., it first computes the maximum frequency for all basic blocks in the whole module, such that the heat of each basic block will be scaled in respect of that maximum frequency. 36 | With the inter-function heat map, the CFGs for some functions can be completely cold. 37 | 38 | In order to generate the heat CFG .dot file, use the following command: 39 | ``` 40 | $> opt -load ../build/src/libHeatCFGPrinter.so -dot-heat-cfg <.bc file> >/dev/null 41 | ``` 42 | 43 | ## Heat CallGraph Printer 44 | 45 | The analysis pass '-dot-heat-callgraph' generates the heat map of the call-graph based on either the profiled number of calls or the maximum basic block frequency inside each function. 46 | The following figure illustrates the heat call-graph highlighting the maximum basic block frequency inside each function. 47 | 48 |

49 | 50 |

51 | 52 | In order to generate the heat call-graph .dot file, use the following command: 53 | ``` 54 | $> opt -load ../build/src/libHeatCallPrinter.so -dot-heat-callgraph <.bc file> >/dev/null 55 | ``` 56 | 57 | ## Using Profiling 58 | 59 | In order to use profiling information with the heat map visualizations, you first need to instrument your code for collecting the profiling information, and then annotate the original code with the collected profiling. 60 | 61 | Instrumenting the code for profiling basic block frequencies: 62 | ``` 63 | $> clang -fprofile-generate ... 64 | ``` 65 | or, alternatively, you can use the older profiling implementation: 66 | ``` 67 | $> clang -fprofile-instr-generate ... 68 | ``` 69 | 70 | In both cases, execute the instrumented code with some representative inputs in order to generate profiling information. 71 | After each execution a .profraw file will be created. 72 | Use llvm-profdata to combine all .profraw files: 73 | ``` 74 | llvm-profdata merge -output= 75 | ``` 76 | 77 | In order to annotate the code, re-compile the original code with the profiling information: 78 | ``` 79 | $> clang -fprofile-use= -emit-llvm -c ... 80 | ``` 81 | or, again, you can use the older profiling implementation: 82 | ``` 83 | $> clang -fprofile-instr-use= -emit-llvm -c ... 84 | ``` 85 | This last command will generate LLVM bitcode files with the profiling annotations. 86 | 87 | -------------------------------------------------------------------------------- /src/HeatCallPrinter.cpp: -------------------------------------------------------------------------------- 1 | //===-- HeatCallPrinter.cpp - CFG printer external interface ----*- C++ -*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // This file defines a 'dot-heat-cfg' analysis pass, which emits the 11 | // cfg..dot file for each function in the program, with a graph of the 12 | // CFG for that function coloured with heat map depending on the basic block 13 | // frequency. 14 | // 15 | // This file defines external functions that can be called to explicitly 16 | // instantiate the CFG printer. 17 | // 18 | //===----------------------------------------------------------------------===// 19 | 20 | #include "HeatCallPrinter.h" 21 | #include "HeatUtils.h" 22 | 23 | #include "llvm/Analysis/BlockFrequencyInfo.h" 24 | #include "llvm/Analysis/BranchProbabilityInfo.h" 25 | #include "llvm/Analysis/CallGraph.h" 26 | 27 | #include "llvm/IR/Function.h" 28 | #include "llvm/IR/Module.h" 29 | #include "llvm/Pass.h" 30 | #include "llvm/Support/DOTGraphTraits.h" 31 | #include "llvm/Support/GraphWriter.h" 32 | #include "llvm/Support/FileSystem.h" 33 | #include "llvm/Support/CommandLine.h" 34 | 35 | #include "llvm/Support/raw_ostream.h" 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | using namespace llvm; 46 | 47 | 48 | static cl::opt 49 | EstimateEdgeWeight("heat-callgraph-estimate-weight", cl::init(false), 50 | cl::Hidden, cl::desc("Estimate edge weights")); 51 | 52 | static cl::opt 53 | FullCallGraph("heat-callgraph-full", cl::init(false), cl::Hidden, 54 | cl::desc("Print full call-graph (using external nodes)")); 55 | 56 | static cl::opt 57 | UseCallCounter("heat-callgraph-call-count", cl::init(false), cl::Hidden, 58 | cl::desc("Use function's call counter as a heat metric")); 59 | 60 | 61 | namespace llvm{ 62 | 63 | class HeatCallGraphInfo { 64 | private: 65 | CallGraph *CG; 66 | Module *M; 67 | std::map freq; 68 | uint64_t maxFreq; 69 | public: 70 | std::function LookupBFI; 71 | 72 | HeatCallGraphInfo(Module *M, CallGraph *CG, 73 | function_ref LookupBFI){ 74 | this->M = M; 75 | this->CG = CG; 76 | maxFreq = 0; 77 | 78 | bool useHeuristic = !hasProfiling(*M); 79 | 80 | for(Function &F : *M){ 81 | freq[&F] = 0; 82 | if(F.isDeclaration()) 83 | continue; 84 | uint64_t localMaxFreq = 0; 85 | if (UseCallCounter) { 86 | Optional< uint64_t > freq = F.getEntryCount(); 87 | if (freq.hasValue()) 88 | localMaxFreq = freq.getValue(); 89 | } else { 90 | localMaxFreq = llvm::getMaxFreq(F,LookupBFI(F),useHeuristic); 91 | } 92 | if(localMaxFreq>=maxFreq) maxFreq = localMaxFreq; 93 | freq[&F] = localMaxFreq; 94 | } 95 | this->LookupBFI = LookupBFI; 96 | removeParallelEdges(); 97 | } 98 | 99 | Module *getModule() const { return M; } 100 | CallGraph *getCallGraph() const { return CG; } 101 | 102 | uint64_t getFreq(const Function *F) { return freq[F]; } 103 | 104 | uint64_t getMaxFreq() { return maxFreq; } 105 | 106 | private: 107 | void removeParallelEdges(){ 108 | for (auto &I : (*CG)) { 109 | CallGraphNode *Node = I.second.get(); 110 | 111 | bool foundParallelEdge = true; 112 | while (foundParallelEdge) { 113 | std::set visited; 114 | foundParallelEdge = false; 115 | for (std::vector::iterator CI = 116 | Node->begin(); CI!=Node->end(); CI++) { 117 | if (visited.find(CI->second->getFunction())==visited.end()) 118 | visited.insert(CI->second->getFunction()); 119 | else { 120 | foundParallelEdge = true; 121 | Node->removeCallEdge(CI); 122 | break; 123 | } 124 | } 125 | } 126 | } 127 | } 128 | }; 129 | 130 | 131 | template <> 132 | struct GraphTraits : public GraphTraits< 133 | const CallGraphNode *> { 134 | static NodeRef getEntryNode(HeatCallGraphInfo *HCG) { 135 | // Start at the external node! 136 | return HCG->getCallGraph()->getExternalCallingNode(); 137 | } 138 | 139 | typedef std::pair> 140 | PairTy; 141 | static const CallGraphNode *CGGetValuePtr(const PairTy &P) { 142 | return P.second.get(); 143 | } 144 | 145 | // nodes_iterator/begin/end - Allow iteration over all nodes in the graph 146 | typedef mapped_iterator 147 | nodes_iterator; 148 | 149 | static nodes_iterator nodes_begin(HeatCallGraphInfo *HCG) { 150 | return nodes_iterator(HCG->getCallGraph()->begin(), &CGGetValuePtr); 151 | } 152 | static nodes_iterator nodes_end(HeatCallGraphInfo *HCG) { 153 | return nodes_iterator(HCG->getCallGraph()->end(), &CGGetValuePtr); 154 | } 155 | }; 156 | 157 | 158 | template<> 159 | struct DOTGraphTraits : public DefaultDOTGraphTraits { 160 | 161 | DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} 162 | 163 | static std::string getGraphName(HeatCallGraphInfo *Graph) { 164 | return "Call graph of module "+ 165 | std::string(Graph->getModule()->getModuleIdentifier()); 166 | } 167 | 168 | static bool isNodeHidden(const CallGraphNode *Node) { 169 | if (FullCallGraph) 170 | return false; 171 | 172 | if (Node->getFunction()) 173 | return false; 174 | 175 | return true; 176 | } 177 | 178 | std::string getNodeLabel(const CallGraphNode *Node, HeatCallGraphInfo *Graph){ 179 | 180 | if (Node==Graph->getCallGraph()->getExternalCallingNode()) 181 | return "external caller"; 182 | 183 | if (Node==Graph->getCallGraph()->getCallsExternalNode()) 184 | return "external callee"; 185 | 186 | if (Function *Func = Node->getFunction()) 187 | return Func->getName(); 188 | 189 | return "external node"; 190 | } 191 | 192 | static const CallGraphNode *CGGetValuePtr(CallGraphNode::CallRecord P) { 193 | return P.second; 194 | } 195 | 196 | // nodes_iterator/begin/end - Allow iteration over all nodes in the graph 197 | typedef mapped_iterator 199 | nodes_iterator; 200 | 201 | 202 | std::string getEdgeAttributes(const CallGraphNode *Node, nodes_iterator I, 203 | HeatCallGraphInfo *Graph) { 204 | if (!EstimateEdgeWeight) 205 | return ""; 206 | 207 | Function *F = Node->getFunction(); 208 | if (F==nullptr || F->isDeclaration()) 209 | return ""; 210 | 211 | Function *SuccFunction = (*I)->getFunction(); 212 | if (SuccFunction==nullptr) 213 | return ""; 214 | 215 | uint64_t counter = getNumOfCalls(*F, *SuccFunction, Graph->LookupBFI); 216 | std::string Attrs = "label=\"" + std::to_string(counter) + "\""; 217 | return Attrs; 218 | } 219 | 220 | std::string getNodeAttributes(const CallGraphNode *Node, 221 | HeatCallGraphInfo *Graph) { 222 | Function *F = Node->getFunction(); 223 | if (F==nullptr || F->isDeclaration()) 224 | return ""; 225 | 226 | uint64_t freq = Graph->getFreq(F); 227 | errs() << F->getName() << " " << freq << "\n"; 228 | std::string color = getHeatColor(freq, Graph->getMaxFreq()); 229 | std::string edgeColor = (freq<(Graph->getMaxFreq()/2))? 230 | getHeatColor(0):getHeatColor(1); 231 | 232 | std::string attrs = "color=\"" + edgeColor + 233 | "ff\", style=filled, fillcolor=\"" + color + "80\""; 234 | 235 | return attrs; 236 | } 237 | 238 | }; 239 | 240 | } 241 | 242 | namespace { 243 | 244 | void HeatCallGraphDOTPrinterPass::getAnalysisUsage(AnalysisUsage &AU) const { 245 | ModulePass::getAnalysisUsage(AU); 246 | AU.addRequired(); 247 | AU.setPreservesAll(); 248 | } 249 | 250 | bool HeatCallGraphDOTPrinterPass::runOnModule(Module &M) { 251 | auto LookupBFI = [this](Function &F) { 252 | return &this->getAnalysis(F).getBFI(); 253 | }; 254 | 255 | std::string Filename = (std::string(M.getModuleIdentifier())+ 256 | ".heatcallgraph.dot"); 257 | errs() << "Writing '" << Filename << "'..."; 258 | 259 | std::error_code EC; 260 | raw_fd_ostream File(Filename, EC, sys::fs::F_Text); 261 | 262 | CallGraph CG(M); 263 | HeatCallGraphInfo heatCFGInfo(&M,&CG,LookupBFI); 264 | 265 | if(!EC) 266 | WriteGraph(File, &heatCFGInfo); 267 | else 268 | errs() << " error opening file for writing!"; 269 | errs() << "\n"; 270 | 271 | return false; 272 | } 273 | 274 | } 275 | 276 | char HeatCallGraphDOTPrinterPass::ID = 0; 277 | static RegisterPass X("dot-heat-callgraph", 278 | "Print heat map of call graph to 'dot' file.", false, false); 279 | -------------------------------------------------------------------------------- /src/HeatCFGPrinter.cpp: -------------------------------------------------------------------------------- 1 | //===-- HeatCFGPrinter.cpp - CFG printer external interface -----*- C++ -*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // This file defines a 'dot-heat-cfg' analysis pass, which emits the 11 | // cfg..dot file for each function in the program, with a graph of the 12 | // CFG for that function coloured with heat map depending on the basic block 13 | // frequency. 14 | // 15 | // This file defines external functions that can be called to explicitly 16 | // instantiate the CFG printer. 17 | // 18 | //===----------------------------------------------------------------------===// 19 | 20 | #include "HeatCFGPrinter.h" 21 | #include "HeatUtils.h" 22 | 23 | #include "llvm/Analysis/BlockFrequencyInfo.h" 24 | #include "llvm/Analysis/BranchProbabilityInfo.h" 25 | #include "llvm/Analysis/CFGPrinter.h" 26 | #include "llvm/IR/Function.h" 27 | #include "llvm/IR/Module.h" 28 | #include "llvm/Pass.h" 29 | #include "llvm/Support/DOTGraphTraits.h" 30 | #include "llvm/Support/GraphWriter.h" 31 | #include "llvm/Support/FileSystem.h" 32 | #include "llvm/Support/CommandLine.h" 33 | #include "llvm/Support/raw_ostream.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | using namespace llvm; 42 | 43 | 44 | static cl::opt 45 | HeatCFGPerFunction("heat-cfg-per-function", cl::init(false), cl::Hidden, 46 | cl::desc("Heat CFG per function")); 47 | 48 | static cl::opt 49 | UseRawEdgeWeight("heat-cfg-raw-weight", cl::init(false), cl::Hidden, 50 | cl::desc("Use raw profiling weights")); 51 | 52 | static cl::opt 53 | NoEdgeWeight("heat-cfg-no-weight", cl::init(false), cl::Hidden, 54 | cl::desc("No edge labels with weights")); 55 | 56 | namespace llvm{ 57 | 58 | class HeatCFGInfo { 59 | private: 60 | BlockFrequencyInfo *BFI; 61 | Function *F; 62 | uint64_t maxFreq; 63 | bool useHeuristic; 64 | public: 65 | HeatCFGInfo(Function *F, BlockFrequencyInfo *BFI, uint64_t maxFreq, 66 | bool useHeuristic){ 67 | this->BFI = BFI; 68 | this->F = F; 69 | this->maxFreq = maxFreq; 70 | this->useHeuristic = useHeuristic; 71 | } 72 | 73 | BlockFrequencyInfo *getBFI(){ return BFI; } 74 | 75 | Function *getF(){ return this->F; } 76 | 77 | uint64_t getMaxFreq() { return maxFreq; } 78 | 79 | uint64_t getFreq(const BasicBlock *BB){ 80 | return getBlockFreq(BB,BFI,useHeuristic); 81 | } 82 | }; 83 | 84 | template <> struct GraphTraits : 85 | public GraphTraits { 86 | static NodeRef getEntryNode(HeatCFGInfo *heatCFG) { 87 | return &(heatCFG->getF()->getEntryBlock()); 88 | } 89 | 90 | // nodes_iterator/begin/end - Allow iteration over all nodes in the graph 91 | using nodes_iterator = pointer_iterator; 92 | 93 | static nodes_iterator nodes_begin(HeatCFGInfo *heatCFG) { 94 | return nodes_iterator(heatCFG->getF()->begin()); 95 | } 96 | 97 | static nodes_iterator nodes_end(HeatCFGInfo *heatCFG) { 98 | return nodes_iterator(heatCFG->getF()->end()); 99 | } 100 | 101 | static size_t size(HeatCFGInfo *heatCFG) { return heatCFG->getF()->size(); } 102 | }; 103 | 104 | template<> 105 | struct DOTGraphTraits : public DefaultDOTGraphTraits { 106 | 107 | DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} 108 | 109 | static std::string getGraphName(HeatCFGInfo *heatCFG) { 110 | return "Heat CFG for '" + heatCFG->getF()->getName().str() + "' function"; 111 | } 112 | 113 | static std::string getSimpleNodeLabel(const BasicBlock *Node, 114 | HeatCFGInfo *) { 115 | if (!Node->getName().empty()) 116 | return Node->getName().str(); 117 | 118 | std::string Str; 119 | raw_string_ostream OS(Str); 120 | 121 | Node->printAsOperand(OS, false); 122 | return OS.str(); 123 | } 124 | 125 | static std::string getCompleteNodeLabel(const BasicBlock *Node, 126 | HeatCFGInfo *) { 127 | enum { MaxColumns = 80 }; 128 | std::string Str; 129 | raw_string_ostream OS(Str); 130 | 131 | if (Node->getName().empty()) { 132 | Node->printAsOperand(OS, false); 133 | OS << ":"; 134 | } 135 | 136 | OS << *Node; 137 | std::string OutStr = OS.str(); 138 | if (OutStr[0] == '\n') OutStr.erase(OutStr.begin()); 139 | 140 | // Process string output to make it nicer... 141 | unsigned ColNum = 0; 142 | unsigned LastSpace = 0; 143 | for (unsigned i = 0; i != OutStr.length(); ++i) { 144 | if (OutStr[i] == '\n') { // Left justify 145 | OutStr[i] = '\\'; 146 | OutStr.insert(OutStr.begin()+i+1, 'l'); 147 | ColNum = 0; 148 | LastSpace = 0; 149 | } else if (OutStr[i] == ';') { // Delete comments! 150 | unsigned Idx = OutStr.find('\n', i+1); // Find end of line 151 | OutStr.erase(OutStr.begin()+i, OutStr.begin()+Idx); 152 | --i; 153 | } else if (ColNum == MaxColumns) { // Wrap lines. 154 | // Wrap very long names even though we can't find a space. 155 | if (!LastSpace) 156 | LastSpace = i; 157 | OutStr.insert(LastSpace, "\\l..."); 158 | ColNum = i - LastSpace; 159 | LastSpace = 0; 160 | i += 3; // The loop will advance 'i' again. 161 | } 162 | else 163 | ++ColNum; 164 | if (OutStr[i] == ' ') 165 | LastSpace = i; 166 | } 167 | return OutStr; 168 | } 169 | 170 | std::string getNodeLabel(const BasicBlock *Node, 171 | HeatCFGInfo *Graph) { 172 | if (isSimple()) 173 | return getSimpleNodeLabel(Node, Graph); 174 | else 175 | return getCompleteNodeLabel(Node, Graph); 176 | } 177 | 178 | static std::string getEdgeSourceLabel(const BasicBlock *Node, 179 | succ_const_iterator I) { 180 | // Label source of conditional branches with "T" or "F" 181 | if (const BranchInst *BI = dyn_cast(Node->getTerminator())) 182 | if (BI->isConditional()) 183 | return (I == succ_begin(Node)) ? "T" : "F"; 184 | 185 | // Label source of switch edges with the associated value. 186 | if (const SwitchInst *SI = dyn_cast(Node->getTerminator())) { 187 | unsigned SuccNo = I.getSuccessorIndex(); 188 | 189 | if (SuccNo == 0) return "def"; 190 | 191 | std::string Str; 192 | raw_string_ostream OS(Str); 193 | auto Case = *SwitchInst::ConstCaseIt::fromSuccessorIndex(SI, SuccNo); 194 | OS << Case.getCaseValue()->getValue(); 195 | return OS.str(); 196 | } 197 | return ""; 198 | } 199 | 200 | /// Display the raw branch weights from PGO. 201 | std::string getEdgeAttributes(const BasicBlock *Node, succ_const_iterator I, 202 | HeatCFGInfo *Graph) { 203 | 204 | if (NoEdgeWeight) 205 | return ""; 206 | 207 | const TerminatorInst *TI = Node->getTerminator(); 208 | if (TI->getNumSuccessors() == 1) 209 | return ""; 210 | 211 | std::string Attrs = ""; 212 | 213 | if (UseRawEdgeWeight) { 214 | MDNode *WeightsNode = TI->getMetadata(LLVMContext::MD_prof); 215 | if (!WeightsNode) 216 | return ""; 217 | 218 | MDString *MDName = cast(WeightsNode->getOperand(0)); 219 | if (MDName->getString() != "branch_weights") 220 | return ""; 221 | 222 | unsigned OpNo = I.getSuccessorIndex() + 1; 223 | if (OpNo >= WeightsNode->getNumOperands()) 224 | return ""; 225 | ConstantInt *Weight = 226 | mdconst::dyn_extract(WeightsNode->getOperand(OpNo)); 227 | if (!Weight) 228 | return ""; 229 | 230 | // Prepend a 'W' to indicate that this is a weight rather than the actual 231 | // profile count (due to scaling). 232 | Attrs = "label=\"W:" + std::to_string(Weight->getZExtValue()) + "\""; 233 | } else { 234 | uint64_t total = 0; 235 | for (unsigned i = 0; igetNumSuccessors(); i++){ 236 | total += Graph->getFreq(TI->getSuccessor(i)); 237 | } 238 | 239 | unsigned OpNo = I.getSuccessorIndex(); 240 | 241 | if (OpNo >= TI->getNumSuccessors()) 242 | return ""; 243 | 244 | BasicBlock *SuccBB = TI->getSuccessor(OpNo); 245 | 246 | double val = 0.0; 247 | if (Graph->getFreq(SuccBB)>0) { 248 | double freq = Graph->getFreq(SuccBB); 249 | val = (int(round((freq/double(total))*10000)))/100.0; 250 | } 251 | 252 | std::stringstream ss; 253 | ss.precision(2); 254 | ss << std::fixed << val; 255 | Attrs = "label=\"" + ss.str() + "%\""; 256 | } 257 | return Attrs; 258 | } 259 | 260 | std::string getNodeAttributes(const BasicBlock *Node, HeatCFGInfo *Graph) { 261 | uint64_t freq = Graph->getFreq(Node); 262 | std::string color = getHeatColor(freq, Graph->getMaxFreq()); 263 | std::string edgeColor = (freq<=(Graph->getMaxFreq()/2))? 264 | (getHeatColor(0)):(getHeatColor(1)); 265 | 266 | std::string attrs = "color=\"" + edgeColor + 267 | "ff\", style=filled, fillcolor=\"" + color + "80\""; 268 | 269 | return attrs; 270 | } 271 | }; 272 | 273 | } 274 | 275 | static void writeHeatCFGToDotFile(Function &F, BlockFrequencyInfo *BFI, 276 | uint64_t maxFreq, bool useHeuristic, bool isSimple) { 277 | std::string Filename = ("heatcfg." + F.getName() + ".dot").str(); 278 | errs() << "Writing '" << Filename << "'..."; 279 | 280 | std::error_code EC; 281 | raw_fd_ostream File(Filename, EC, sys::fs::F_Text); 282 | 283 | HeatCFGInfo heatCFGInfo(&F,BFI,maxFreq,useHeuristic); 284 | 285 | if (!EC) 286 | WriteGraph(File, &heatCFGInfo, isSimple); 287 | else 288 | errs() << " error opening file for writing!"; 289 | errs() << "\n"; 290 | } 291 | 292 | static void writeHeatCFGToDotFile(Module &M, 293 | function_ref LookupBFI, bool isSimple){ 294 | uint64_t maxFreq = 0; 295 | 296 | bool useHeuristic = !hasProfiling(M); 297 | 298 | if (!HeatCFGPerFunction) 299 | maxFreq = getMaxFreq(M,LookupBFI,useHeuristic); 300 | 301 | for (Function &F : M) { 302 | if (F.isDeclaration()) 303 | continue; 304 | if (HeatCFGPerFunction) 305 | maxFreq = getMaxFreq(F,LookupBFI(F),useHeuristic); 306 | writeHeatCFGToDotFile(F,LookupBFI(F),maxFreq,useHeuristic,isSimple); 307 | } 308 | } 309 | 310 | namespace { 311 | 312 | void HeatCFGPrinterPass::getAnalysisUsage(AnalysisUsage &AU) const { 313 | ModulePass::getAnalysisUsage(AU); 314 | AU.addRequired(); 315 | AU.setPreservesAll(); 316 | } 317 | 318 | bool HeatCFGPrinterPass::runOnModule(Module &M) { 319 | auto LookupBFI = [this](Function &F) { 320 | return &this->getAnalysis(F).getBFI(); 321 | }; 322 | writeHeatCFGToDotFile(M,LookupBFI,false); 323 | return false; 324 | } 325 | 326 | void HeatCFGOnlyPrinterPass::getAnalysisUsage(AnalysisUsage &AU) const { 327 | ModulePass::getAnalysisUsage(AU); 328 | AU.addRequired(); 329 | AU.setPreservesAll(); 330 | } 331 | 332 | bool HeatCFGOnlyPrinterPass::runOnModule(Module &M) { 333 | auto LookupBFI = [this](Function &F) { 334 | return &this->getAnalysis(F).getBFI(); 335 | }; 336 | writeHeatCFGToDotFile(M,LookupBFI,true); 337 | return false; 338 | } 339 | 340 | } 341 | 342 | char HeatCFGPrinterPass::ID = 0; 343 | static RegisterPass X("dot-heat-cfg", 344 | "Print heat map of CFG of function to 'dot' file", false, false); 345 | 346 | char HeatCFGOnlyPrinterPass::ID = 0; 347 | static RegisterPass XOnly("dot-heat-cfg-only", 348 | "Print heat map of CFG of function to 'dot' file (with no function bodies)", 349 | false, false); 350 | 351 | 352 | --------------------------------------------------------------------------------