xref: /freebsd-src/contrib/llvm-project/llvm/tools/llvm-xray/xray-graph.h (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
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