1*0b57cec5SDimitry Andric //===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // Generate a DOT file to represent the function call graph encountered in 10*0b57cec5SDimitry Andric // the trace. 11*0b57cec5SDimitry Andric // 12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 13*0b57cec5SDimitry Andric 14*0b57cec5SDimitry Andric #ifndef XRAY_GRAPH_H 15*0b57cec5SDimitry Andric #define XRAY_GRAPH_H 16*0b57cec5SDimitry Andric 17*0b57cec5SDimitry Andric #include <string> 18*0b57cec5SDimitry Andric #include <vector> 19*0b57cec5SDimitry Andric 20*0b57cec5SDimitry Andric #include "func-id-helper.h" 21*0b57cec5SDimitry Andric #include "xray-color-helper.h" 22*0b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h" 23*0b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 24*0b57cec5SDimitry Andric #include "llvm/Support/Errc.h" 25*0b57cec5SDimitry Andric #include "llvm/Support/Program.h" 26*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 27*0b57cec5SDimitry Andric #include "llvm/XRay/Graph.h" 28*0b57cec5SDimitry Andric #include "llvm/XRay/Trace.h" 29*0b57cec5SDimitry Andric #include "llvm/XRay/XRayRecord.h" 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric namespace llvm { 32*0b57cec5SDimitry Andric namespace xray { 33*0b57cec5SDimitry Andric 34*0b57cec5SDimitry Andric /// A class encapsulating the logic related to analyzing XRay traces, producting 35*0b57cec5SDimitry Andric /// Graphs from them and then exporting those graphs for review. 36*0b57cec5SDimitry Andric class GraphRenderer { 37*0b57cec5SDimitry Andric public: 38*0b57cec5SDimitry Andric /// An enum for enumerating the various statistics gathered on latencies 39*0b57cec5SDimitry Andric enum class StatType { NONE, COUNT, MIN, MED, PCT90, PCT99, MAX, SUM }; 40*0b57cec5SDimitry Andric 41*0b57cec5SDimitry Andric /// An inner struct for common timing statistics information 42*0b57cec5SDimitry Andric struct TimeStat { 43*0b57cec5SDimitry Andric int64_t Count; 44*0b57cec5SDimitry Andric double Min; 45*0b57cec5SDimitry Andric double Median; 46*0b57cec5SDimitry Andric double Pct90; 47*0b57cec5SDimitry Andric double Pct99; 48*0b57cec5SDimitry Andric double Max; 49*0b57cec5SDimitry Andric double Sum; 50*0b57cec5SDimitry Andric 51*0b57cec5SDimitry Andric std::string getString(StatType T) const; 52*0b57cec5SDimitry Andric double getDouble(StatType T) const; 53*0b57cec5SDimitry Andric }; 54*0b57cec5SDimitry Andric using TimestampT = uint64_t; 55*0b57cec5SDimitry Andric 56*0b57cec5SDimitry Andric /// An inner struct for storing edge attributes for our graph. Here the 57*0b57cec5SDimitry Andric /// attributes are mainly function call statistics. 58*0b57cec5SDimitry Andric /// 59*0b57cec5SDimitry Andric /// FIXME: expand to contain more information eg call latencies. 60*0b57cec5SDimitry Andric struct CallStats { 61*0b57cec5SDimitry Andric TimeStat S; 62*0b57cec5SDimitry Andric std::vector<TimestampT> Timings; 63*0b57cec5SDimitry Andric }; 64*0b57cec5SDimitry Andric 65*0b57cec5SDimitry Andric /// An Inner Struct for storing vertex attributes, at the moment just 66*0b57cec5SDimitry Andric /// SymbolNames, however in future we could store bulk function statistics. 67*0b57cec5SDimitry Andric /// 68*0b57cec5SDimitry Andric /// FIXME: Store more attributes based on instrumentation map. 69*0b57cec5SDimitry Andric struct FunctionStats { 70*0b57cec5SDimitry Andric std::string SymbolName; 71*0b57cec5SDimitry Andric TimeStat S = {}; 72*0b57cec5SDimitry Andric }; 73*0b57cec5SDimitry Andric 74*0b57cec5SDimitry Andric struct FunctionAttr { 75*0b57cec5SDimitry Andric int32_t FuncId; 76*0b57cec5SDimitry Andric uint64_t TSC; 77*0b57cec5SDimitry Andric }; 78*0b57cec5SDimitry Andric 79*0b57cec5SDimitry Andric using FunctionStack = SmallVector<FunctionAttr, 4>; 80*0b57cec5SDimitry Andric 81*0b57cec5SDimitry Andric using PerThreadFunctionStackMap = DenseMap<uint32_t, FunctionStack>; 82*0b57cec5SDimitry Andric 83*0b57cec5SDimitry Andric class GraphT : public Graph<FunctionStats, CallStats, int32_t> { 84*0b57cec5SDimitry Andric public: 85*0b57cec5SDimitry Andric TimeStat GraphEdgeMax = {}; 86*0b57cec5SDimitry Andric TimeStat GraphVertexMax = {}; 87*0b57cec5SDimitry Andric }; 88*0b57cec5SDimitry Andric 89*0b57cec5SDimitry Andric GraphT G; 90*0b57cec5SDimitry Andric using VertexIdentifier = typename decltype(G)::VertexIdentifier; 91*0b57cec5SDimitry Andric using EdgeIdentifier = decltype(G)::EdgeIdentifier; 92*0b57cec5SDimitry Andric 93*0b57cec5SDimitry Andric /// Use a Map to store the Function stack for each thread whilst building the 94*0b57cec5SDimitry Andric /// graph. 95*0b57cec5SDimitry Andric /// 96*0b57cec5SDimitry Andric /// FIXME: Perhaps we can Build this into LatencyAccountant? or vise versa? 97*0b57cec5SDimitry Andric PerThreadFunctionStackMap PerThreadFunctionStack; 98*0b57cec5SDimitry Andric 99*0b57cec5SDimitry Andric /// Usefull object for getting human readable Symbol Names. 100*0b57cec5SDimitry Andric FuncIdConversionHelper FuncIdHelper; 101*0b57cec5SDimitry Andric bool DeduceSiblingCalls = false; 102*0b57cec5SDimitry Andric TimestampT CurrentMaxTSC = 0; 103*0b57cec5SDimitry Andric 104*0b57cec5SDimitry Andric /// A private function to help implement the statistic generation functions; 105*0b57cec5SDimitry Andric template <typename U> 106*0b57cec5SDimitry Andric void getStats(U begin, U end, GraphRenderer::TimeStat &S); 107*0b57cec5SDimitry Andric void updateMaxStats(const TimeStat &S, TimeStat &M); 108*0b57cec5SDimitry Andric 109*0b57cec5SDimitry Andric /// Calculates latency statistics for each edge and stores the data in the 110*0b57cec5SDimitry Andric /// Graph 111*0b57cec5SDimitry Andric void calculateEdgeStatistics(); 112*0b57cec5SDimitry Andric 113*0b57cec5SDimitry Andric /// Calculates latency statistics for each vertex and stores the data in the 114*0b57cec5SDimitry Andric /// Graph 115*0b57cec5SDimitry Andric void calculateVertexStatistics(); 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric /// Normalises latency statistics for each edge and vertex by CycleFrequency; 118*0b57cec5SDimitry Andric void normalizeStatistics(double CycleFrequency); 119*0b57cec5SDimitry Andric 120*0b57cec5SDimitry Andric /// An object to color gradients 121*0b57cec5SDimitry Andric ColorHelper CHelper; 122*0b57cec5SDimitry Andric 123*0b57cec5SDimitry Andric public: 124*0b57cec5SDimitry Andric /// Takes in a reference to a FuncIdHelper in order to have ready access to 125*0b57cec5SDimitry Andric /// Symbol names. GraphRenderer(const FuncIdConversionHelper & FuncIdHelper,bool DSC)126*0b57cec5SDimitry Andric explicit GraphRenderer(const FuncIdConversionHelper &FuncIdHelper, bool DSC) 127*0b57cec5SDimitry Andric : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC), 128*0b57cec5SDimitry Andric CHelper(ColorHelper::SequentialScheme::OrRd) { 129*0b57cec5SDimitry Andric G[0] = {}; 130*0b57cec5SDimitry Andric } 131*0b57cec5SDimitry Andric 132*0b57cec5SDimitry Andric /// Process an Xray record and expand the graph. 133*0b57cec5SDimitry Andric /// 134*0b57cec5SDimitry Andric /// This Function will return true on success, or false if records are not 135*0b57cec5SDimitry Andric /// presented in per-thread call-tree DFS order. (That is for each thread the 136*0b57cec5SDimitry Andric /// Records should be in order runtime on an ideal system.) 137*0b57cec5SDimitry Andric /// 138*0b57cec5SDimitry Andric /// FIXME: Make this more robust against small irregularities. 139*0b57cec5SDimitry Andric Error accountRecord(const XRayRecord &Record); 140*0b57cec5SDimitry Andric getPerThreadFunctionStack()141*0b57cec5SDimitry Andric const PerThreadFunctionStackMap &getPerThreadFunctionStack() const { 142*0b57cec5SDimitry Andric return PerThreadFunctionStack; 143*0b57cec5SDimitry Andric } 144*0b57cec5SDimitry Andric 145*0b57cec5SDimitry Andric class Factory { 146*0b57cec5SDimitry Andric public: 147*0b57cec5SDimitry Andric bool KeepGoing; 148*0b57cec5SDimitry Andric bool DeduceSiblingCalls; 149*0b57cec5SDimitry Andric std::string InstrMap; 150*0b57cec5SDimitry Andric ::llvm::xray::Trace Trace; 151*0b57cec5SDimitry Andric Expected<GraphRenderer> getGraphRenderer(); 152*0b57cec5SDimitry Andric }; 153*0b57cec5SDimitry Andric 154*0b57cec5SDimitry Andric /// Output the Embedded graph in DOT format on \p OS, labeling the edges by 155*0b57cec5SDimitry Andric /// \p T 156*0b57cec5SDimitry Andric void exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel = StatType::NONE, 157*0b57cec5SDimitry Andric StatType EdgeColor = StatType::NONE, 158*0b57cec5SDimitry Andric StatType VertexLabel = StatType::NONE, 159*0b57cec5SDimitry Andric StatType VertexColor = StatType::NONE); 160*0b57cec5SDimitry Andric 161*0b57cec5SDimitry Andric /// Get a reference to the internal graph. getGraph()162*0b57cec5SDimitry Andric const GraphT &getGraph() { return G; } 163*0b57cec5SDimitry Andric }; 164*0b57cec5SDimitry Andric 165*0b57cec5SDimitry Andric /// Vector Sum of TimeStats 166*0b57cec5SDimitry Andric inline GraphRenderer::TimeStat operator+(const GraphRenderer::TimeStat &A, 167*0b57cec5SDimitry Andric const GraphRenderer::TimeStat &B) { 168*0b57cec5SDimitry Andric return {A.Count + B.Count, A.Min + B.Min, A.Median + B.Median, 169*0b57cec5SDimitry Andric A.Pct90 + B.Pct90, A.Pct99 + B.Pct99, A.Max + B.Max, 170*0b57cec5SDimitry Andric A.Sum + B.Sum}; 171*0b57cec5SDimitry Andric } 172*0b57cec5SDimitry Andric 173*0b57cec5SDimitry Andric /// Vector Difference of Timestats 174*0b57cec5SDimitry Andric inline GraphRenderer::TimeStat operator-(const GraphRenderer::TimeStat &A, 175*0b57cec5SDimitry Andric const GraphRenderer::TimeStat &B) { 176*0b57cec5SDimitry Andric 177*0b57cec5SDimitry Andric return {A.Count - B.Count, A.Min - B.Min, A.Median - B.Median, 178*0b57cec5SDimitry Andric A.Pct90 - B.Pct90, A.Pct99 - B.Pct99, A.Max - B.Max, 179*0b57cec5SDimitry Andric A.Sum - B.Sum}; 180*0b57cec5SDimitry Andric } 181*0b57cec5SDimitry Andric 182*0b57cec5SDimitry Andric /// Scalar Diference of TimeStat and double 183*0b57cec5SDimitry Andric inline GraphRenderer::TimeStat operator/(const GraphRenderer::TimeStat &A, 184*0b57cec5SDimitry Andric double B) { 185*0b57cec5SDimitry Andric 186*0b57cec5SDimitry Andric return {static_cast<int64_t>(A.Count / B), 187*0b57cec5SDimitry Andric A.Min / B, 188*0b57cec5SDimitry Andric A.Median / B, 189*0b57cec5SDimitry Andric A.Pct90 / B, 190*0b57cec5SDimitry Andric A.Pct99 / B, 191*0b57cec5SDimitry Andric A.Max / B, 192*0b57cec5SDimitry Andric A.Sum / B}; 193*0b57cec5SDimitry Andric } 194*0b57cec5SDimitry Andric 195*0b57cec5SDimitry Andric /// Scalar product of TimeStat and Double 196*0b57cec5SDimitry Andric inline GraphRenderer::TimeStat operator*(const GraphRenderer::TimeStat &A, 197*0b57cec5SDimitry Andric double B) { 198*0b57cec5SDimitry Andric return {static_cast<int64_t>(A.Count * B), 199*0b57cec5SDimitry Andric A.Min * B, 200*0b57cec5SDimitry Andric A.Median * B, 201*0b57cec5SDimitry Andric A.Pct90 * B, 202*0b57cec5SDimitry Andric A.Pct99 * B, 203*0b57cec5SDimitry Andric A.Max * B, 204*0b57cec5SDimitry Andric A.Sum * B}; 205*0b57cec5SDimitry Andric } 206*0b57cec5SDimitry Andric 207*0b57cec5SDimitry Andric /// Scalar product of double TimeStat 208*0b57cec5SDimitry Andric inline GraphRenderer::TimeStat operator*(double A, 209*0b57cec5SDimitry Andric const GraphRenderer::TimeStat &B) { 210*0b57cec5SDimitry Andric return B * A; 211*0b57cec5SDimitry Andric } 212*0b57cec5SDimitry Andric 213*0b57cec5SDimitry Andric /// Hadamard Product of TimeStats 214*0b57cec5SDimitry Andric inline GraphRenderer::TimeStat operator*(const GraphRenderer::TimeStat &A, 215*0b57cec5SDimitry Andric const GraphRenderer::TimeStat &B) { 216*0b57cec5SDimitry Andric return {A.Count * B.Count, A.Min * B.Min, A.Median * B.Median, 217*0b57cec5SDimitry Andric A.Pct90 * B.Pct90, A.Pct99 * B.Pct99, A.Max * B.Max, 218*0b57cec5SDimitry Andric A.Sum * B.Sum}; 219*0b57cec5SDimitry Andric } 220*0b57cec5SDimitry Andric 221*0b57cec5SDimitry Andric /// Hadamard Division of TimeStats 222*0b57cec5SDimitry Andric inline GraphRenderer::TimeStat operator/(const GraphRenderer::TimeStat &A, 223*0b57cec5SDimitry Andric const GraphRenderer::TimeStat &B) { 224*0b57cec5SDimitry Andric return {A.Count / B.Count, A.Min / B.Min, A.Median / B.Median, 225*0b57cec5SDimitry Andric A.Pct90 / B.Pct90, A.Pct99 / B.Pct99, A.Max / B.Max, 226*0b57cec5SDimitry Andric A.Sum / B.Sum}; 227*0b57cec5SDimitry Andric } 228*0b57cec5SDimitry Andric } // namespace xray 229*0b57cec5SDimitry Andric } // namespace llvm 230*0b57cec5SDimitry Andric 231*0b57cec5SDimitry Andric #endif // XRAY_GRAPH_H 232