llvm.org GIT mirror llvm / 061ca19
[XRay] Implement the `llvm-xray graph` subcommand Here we define the `graph` subcommand which generates a graph from the function call information and uses it to present the call information graphically with additional annotations. Reviewers: dblaikie, dberris Differential Revision: https://reviews.llvm.org/D27243 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@292156 91177308-0d34-0410-b5e6-96231b3b80d8 David Blaikie 3 years ago
5 changed file(s) with 616 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -d \
1 #RUN: | FileCheck %s -check-prefix=COUNT
2 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -d -e count \
3 #RUN: | FileCheck %s -check-prefix=COUNT
4 #
5 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -d -e min \
6 #RUN: | FileCheck %s -check-prefix=TIME
7 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -d -e med \
8 #RUN: | FileCheck %s -check-prefix=TIME
9 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -d -e 90p \
10 #RUN: | FileCheck %s -check-prefix=TIME
11 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -d -e 99p \
12 #RUN: | FileCheck %s -check-prefix=TIME
13 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -d -e max \
14 #RUN: | FileCheck %s -check-prefix=TIME
15 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -d -e sum \
16 #RUN: | FileCheck %s -check-prefix=TIME
17 #
18 ---
19 header:
20 version: 1
21 type: 0
22 constant-tsc: true
23 nonstop-tsc: true
24 cycle-frequency: 0
25 records:
26 # Here we reconstruct the following call trace:
27 #
28 # f1()
29 # f2()
30 # f3()
31 #
32 # But we find that we're missing an exit record for f2() because it's
33 # tail-called f3(). We make sure that if we see a trace like this that we can
34 # deduce tail calls, and account the time (potentially wrongly) to f2() when
35 # f1() exits. That is because we don't go back to f3()'s entry record to
36 # properly do the math on the timing of f2().
37 #
38 # Note that by default, tail/sibling call deduction is disabled, and is enabled
39 # with a flag "-d" or "-deduce-sibling-calls".
40 #
41 - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-enter, tsc: 10000 }
42 - { type: 0, func-id: 2, cpu: 1, thread: 111, kind: function-enter, tsc: 10001 }
43 - { type: 0, func-id: 3, cpu: 1, thread: 111, kind: function-enter, tsc: 10002 }
44 - { type: 0, func-id: 3, cpu: 1, thread: 111, kind: function-exit, tsc: 10003 }
45 - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-exit, tsc: 10004 }
46 ...
47
48 #EMPTY: digraph xray {
49 #EMPTY-DAG: F0 -> F1 [label=""];
50 #EMPTY-DAG: F1 -> F2 [label=""];
51 #EMPTY-DAG: F2 -> F3 [label=""];
52 #EMPTY-DAG: F1 [label="@(1)"];
53 #EMPTY-DAG: F2 [label="@(2)"];
54 #EMPTY-DAG: F3 [label="@(3)"];
55 #EMPTY-NEXT: }
56
57 #COUNT: digraph xray {
58 #COUNT-DAG: F0 -> F1 [label="1"];
59 #COUNT-DAG: F1 -> F2 [label="1"];
60 #COUNT-DAG: F2 -> F3 [label="1"];
61 #COUNT-DAG: F1 [label="@(1)"];
62 #COUNT-DAG: F2 [label="@(2)"];
63 #COUNT-DAG: F3 [label="@(3)"];
64 #COUNT-NEXT: }
65
66
67 #TIME: digraph xray {
68 #TIME-DAG: F0 -> F1 [label="4.{{.*}}"];
69 #TIME-DAG: F1 -> F2 [label="3.{{.*}}"];
70 #TIME-DAG: F2 -> F3 [label="1.{{.*}}"];
71 #TIME-DAG: F1 [label="@(1)"];
72 #TIME-DAG: F2 [label="@(2)"];
73 #TIME-DAG: F3 [label="@(3)"];
74 #TIME-NEXT: }
0 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml \
1 #RUN: | FileCheck %s -check-prefix=COUNT
2 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -e count \
3 #RUN: | FileCheck %s -check-prefix=COUNT
4 #
5 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -e min \
6 #RUN: | FileCheck %s -check-prefix=TIME
7 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -e med \
8 #RUN: | FileCheck %s -check-prefix=TIME
9 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -e 90p \
10 #RUN: | FileCheck %s -check-prefix=TIME
11 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -e 99p \
12 #RUN: | FileCheck %s -check-prefix=TIME
13 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -e max \
14 #RUN: | FileCheck %s -check-prefix=TIME
15 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -e sum \
16 #RUN: | FileCheck %s -check-prefix=TIME
17 ---
18 header:
19 version: 1
20 type: 0
21 constant-tsc: true
22 nonstop-tsc: true
23 cycle-frequency: 2601000000
24 records:
25 - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-enter,
26 tsc: 10001 }
27 - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-exit,
28 tsc: 10100 }
29 ...
30
31
32 #EMPTY: digraph xray {
33 #EMPTY-NEXT: F0 -> F1 [label=""];
34 #EMPTY-NEXT: F1 [label="@(1)"];
35 #EMPTY-NEXT: }
36
37 #COUNT: digraph xray {
38 #COUNT-NEXT: F0 -> F1 [label="1"];
39 #COUNT-NEXT: F1 [label="@(1)"];
40 #COUNT-NEXT: }
41
42 #TIME: digraph xray {
43 #TIME-NEXT: F0 -> F1 [label="3.8{{.*}}e-08"];
44 #TIME-NEXT: F1 [label="@(1)"];
45 #TIME-NEXT: }
1111 xray-converter.cc
1212 xray-extract.cc
1313 xray-extract.cc
14 xray-graph.cc
1415 xray-registry.cc)
1516
1617 add_llvm_tool(llvm-xray llvm-xray.cc ${LLVM_XRAY_TOOLS})
0 //===-- xray-graph.c - XRay Function Call Graph Renderer ------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Generate a DOT file to represent the function call graph encountered in
10 // the trace.
11 //
12 //===----------------------------------------------------------------------===//
13 #include
14 #include
15 #include
16 #include
17
18 #include "xray-extract.h"
19 #include "xray-graph.h"
20 #include "xray-registry.h"
21 #include "llvm/Support/ErrorHandling.h"
22 #include "llvm/Support/FormatVariadic.h"
23 #include "llvm/XRay/Trace.h"
24 #include "llvm/XRay/YAMLXRayRecord.h"
25
26 using namespace llvm;
27 using namespace xray;
28
29 // Setup llvm-xray graph subcommand and its options.
30 static cl::SubCommand Graph("graph", "Generate function-call graph");
31 static cl::opt GraphInput(cl::Positional,
32 cl::desc(""),
33 cl::Required, cl::sub(Graph));
34
35 static cl::opt
36 GraphOutput("output", cl::value_desc("Output file"), cl::init("-"),
37 cl::desc("output file; use '-' for stdout"), cl::sub(Graph));
38 static cl::alias GraphOutput2("o", cl::aliasopt(GraphOutput),
39 cl::desc("Alias for -output"), cl::sub(Graph));
40
41 static cl::opt GraphInstrMap(
42 "instr_map", cl::desc("binary with the instrumrntation map, or "
43 "a separate instrumentation map"),
44 cl::value_desc("binary with xray_instr_map"), cl::sub(Graph), cl::init(""));
45 static cl::alias GraphInstrMap2("m", cl::aliasopt(GraphInstrMap),
46 cl::desc("alias for -instr_map"),
47 cl::sub(Graph));
48
49 static cl::opt InstrMapFormat(
50 "instr-map-format", cl::desc("format of instrumentation map"),
51 cl::values(clEnumValN(InstrumentationMapExtractor::InputFormats::ELF, "elf",
52 "instrumentation map in an ELF header"),
53 clEnumValN(InstrumentationMapExtractor::InputFormats::YAML,
54 "yaml", "instrumentation map in YAML")),
55 cl::sub(Graph), cl::init(InstrumentationMapExtractor::InputFormats::ELF));
56 static cl::alias InstrMapFormat2("t", cl::aliasopt(InstrMapFormat),
57 cl::desc("Alias for -instr-map-format"),
58 cl::sub(Graph));
59
60 static cl::opt GraphDeduceSiblingCalls(
61 "deduce-sibling-calls",
62 cl::desc("Deduce sibling calls when unrolling function call stacks"),
63 cl::sub(Graph), cl::init(false));
64 static cl::alias
65 GraphDeduceSiblingCalls2("d", cl::aliasopt(GraphDeduceSiblingCalls),
66 cl::desc("Alias for -deduce-sibling-calls"),
67 cl::sub(Graph));
68
69 static cl::opt
70 GraphEdgeLabel("edge-label",
71 cl::desc("Output graphs with edges labeled with this field"),
72 cl::value_desc("field"), cl::sub(Graph),
73 cl::init(GraphRenderer::StatType::COUNT),
74 cl::values(clEnumValN(GraphRenderer::StatType::COUNT,
75 "count", "function call counts"),
76 clEnumValN(GraphRenderer::StatType::MIN, "min",
77 "minimum function durations"),
78 clEnumValN(GraphRenderer::StatType::MED, "med",
79 "median function durations"),
80 clEnumValN(GraphRenderer::StatType::PCT90, "90p",
81 "90th percentile durations"),
82 clEnumValN(GraphRenderer::StatType::PCT99, "99p",
83 "99th percentile durations"),
84 clEnumValN(GraphRenderer::StatType::MAX, "max",
85 "maximum function durations"),
86 clEnumValN(GraphRenderer::StatType::SUM, "sum",
87 "sum of call durations")));
88 static cl::alias GraphEdgeLabel2("e", cl::aliasopt(GraphEdgeLabel),
89 cl::desc("Alias for -edge-label"),
90 cl::sub(Graph));
91
92 namespace {
93 template T diff(T L, T R) { return std::max(L, R) - std::min(L, R); }
94
95 void updateStat(GraphRenderer::TimeStat &S, int64_t lat) {
96 S.Count++;
97 if (S.Min > lat || S.Min == 0)
98 S.Min = lat;
99 if (S.Max < lat)
100 S.Max = lat;
101 S.Sum += lat;
102 }
103 }
104
105 // Evaluates an XRay record and performs accounting on it, creating and
106 // decorating a function call graph as it does so. It does this by maintaining
107 // a call stack on a per-thread basis and adding edges and verticies to the
108 // graph as they are seen for the first time.
109 //
110 // There is an immaginary root for functions at the top of their stack with
111 // FuncId 0.
112 //
113 // FIXME: make more robust to errors and
114 // Decorate Graph More Heavily.
115 // FIXME: Refactor this and account subcommand to reduce code duplication.
116 bool GraphRenderer::accountRecord(const XRayRecord &Record) {
117 if (CurrentMaxTSC == 0)
118 CurrentMaxTSC = Record.TSC;
119
120 if (Record.TSC < CurrentMaxTSC)
121 return false;
122
123 auto &ThreadStack = PerThreadFunctionStack[Record.TId];
124 switch (Record.Type) {
125 case RecordTypes::ENTER: {
126 if (VertexAttrs.count(Record.FuncId) == 0)
127 VertexAttrs[Record.FuncId].SymbolName =
128 FuncIdHelper.SymbolOrNumber(Record.FuncId);
129 ThreadStack.push_back({Record.FuncId, Record.TSC});
130 break;
131 }
132 case RecordTypes::EXIT: {
133 // FIXME: Refactor this and the account subcommand to reducr code
134 // duplication
135 if (ThreadStack.size() == 0 || ThreadStack.back().FuncId != Record.FuncId) {
136 if (!DeduceSiblingCalls)
137 return false;
138 auto Parent = std::find_if(
139 ThreadStack.rbegin(), ThreadStack.rend(),
140 [&](const FunctionAttr &A) { return A.FuncId == Record.FuncId; });
141 if (Parent == ThreadStack.rend())
142 return false; // There is no matching Function for this exit.
143 while (ThreadStack.back().FuncId != Record.FuncId) {
144 uint64_t D = diff(ThreadStack.back().TSC, Record.TSC);
145 int32_t TopFuncId = ThreadStack.back().FuncId;
146 ThreadStack.pop_back();
147 assert(ThreadStack.size() != 0);
148 auto &EA = Graph[ThreadStack.back().FuncId][TopFuncId];
149 EA.Timings.push_back(D);
150 updateStat(EA.S, D);
151 updateStat(VertexAttrs[TopFuncId].S, D);
152 }
153 }
154 uint64_t D = diff(ThreadStack.back().TSC, Record.TSC);
155 ThreadStack.pop_back();
156 auto &V = Graph[ThreadStack.empty() ? 0 : ThreadStack.back().FuncId];
157 auto &EA = V[Record.FuncId];
158 EA.Timings.push_back(D);
159 updateStat(EA.S, D);
160 updateStat(VertexAttrs[Record.FuncId].S, D);
161 break;
162 }
163 }
164
165 return true;
166 }
167
168 template
169 void GraphRenderer::getStats(U begin, U end, GraphRenderer::TimeStat &S) {
170 assert(begin != end);
171 std::ptrdiff_t MedianOff = S.Count / 2;
172 std::nth_element(begin, begin + MedianOff, end);
173 S.Median = *(begin + MedianOff);
174 std::ptrdiff_t Pct90Off = (S.Count * 9) / 10;
175 std::nth_element(begin, begin + Pct90Off, end);
176 S.Pct90 = *(begin + Pct90Off);
177 std::ptrdiff_t Pct99Off = (S.Count * 99) / 100;
178 std::nth_element(begin, begin + Pct99Off, end);
179 S.Pct99 = *(begin + Pct99Off);
180 }
181
182 void GraphRenderer::calculateEdgeStatistics() {
183 for (auto &V : Graph) {
184 for (auto &E : V.second) {
185 auto &A = E.second;
186 getStats(A.Timings.begin(), A.Timings.end(), A.S);
187 }
188 }
189 }
190
191 void GraphRenderer::calculateVertexStatistics() {
192 DenseMap>>
193 IncommingEdges;
194 uint64_t MaxCount = 0;
195 for (auto &V : Graph) {
196 for (auto &E : V.second) {
197 auto &IEV = IncommingEdges[E.first];
198 IEV.second.push_back(&E.second);
199 IEV.first += E.second.S.Count;
200 if (IEV.first > MaxCount)
201 MaxCount = IEV.first;
202 }
203 }
204 std::vector TempTimings;
205 TempTimings.reserve(MaxCount);
206 for (auto &V : IncommingEdges) {
207 for (auto &P : V.second.second) {
208 TempTimings.insert(TempTimings.end(), P->Timings.begin(),
209 P->Timings.end());
210 }
211 getStats(TempTimings.begin(), TempTimings.end(), VertexAttrs[V.first].S);
212 TempTimings.clear();
213 }
214 }
215
216 void GraphRenderer::normaliseStatistics(double CycleFrequency) {
217 for (auto &V : Graph) {
218 for (auto &E : V.second) {
219 auto &S = E.second.S;
220 S.Min /= CycleFrequency;
221 S.Median /= CycleFrequency;
222 S.Max /= CycleFrequency;
223 S.Sum /= CycleFrequency;
224 S.Pct90 /= CycleFrequency;
225 S.Pct99 /= CycleFrequency;
226 }
227 }
228 for (auto &V : VertexAttrs) {
229 auto &S = V.second.S;
230 S.Min /= CycleFrequency;
231 S.Median /= CycleFrequency;
232 S.Max /= CycleFrequency;
233 S.Sum /= CycleFrequency;
234 S.Pct90 /= CycleFrequency;
235 S.Pct99 /= CycleFrequency;
236 }
237 }
238
239 namespace {
240 void outputEdgeInfo(const GraphRenderer::TimeStat &S, GraphRenderer::StatType T,
241 raw_ostream &OS) {
242 switch (T) {
243 case GraphRenderer::StatType::COUNT:
244 OS << S.Count;
245 break;
246 case GraphRenderer::StatType::MIN:
247 OS << S.Min;
248 break;
249 case GraphRenderer::StatType::MED:
250 OS << S.Median;
251 break;
252 case GraphRenderer::StatType::PCT90:
253 OS << S.Pct90;
254 break;
255 case GraphRenderer::StatType::PCT99:
256 OS << S.Pct99;
257 break;
258 case GraphRenderer::StatType::MAX:
259 OS << S.Max;
260 break;
261 case GraphRenderer::StatType::SUM:
262 OS << S.Sum;
263 break;
264 }
265 }
266 }
267
268 // Outputs a DOT format version of the Graph embedded in the GraphRenderer
269 // object on OS. It does this in the expected way by itterating
270 // through all edges then vertices and then outputting them and their
271 // annotations.
272 //
273 // FIXME: output more information, better presented.
274 void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H,
275 StatType T) {
276 calculateEdgeStatistics();
277 calculateVertexStatistics();
278 if (H.CycleFrequency)
279 normaliseStatistics(H.CycleFrequency);
280
281 OS << "digraph xray {\n";
282
283 for (const auto &V : Graph)
284 for (const auto &E : V.second) {
285 OS << "F" << V.first << " -> "
286 << "F" << E.first << " [label=\"";
287 outputEdgeInfo(E.second.S, T, OS);
288 OS << "\"];\n";
289 }
290
291 for (const auto &V : VertexAttrs)
292 OS << "F" << V.first << " [label=\""
293 << (V.second.SymbolName.size() > 40
294 ? V.second.SymbolName.substr(0, 40) + "..."
295 : V.second.SymbolName)
296 << "\"];\n";
297
298 OS << "}\n";
299 }
300
301 // Here we register and implement the llvm-xray graph subcommand.
302 // The bulk of this code reads in the options, opens the required files, uses
303 // those files to create a context for analysing the xray trace, then there is a
304 // short loop which actually analyses the trace, generates the graph and then
305 // outputs it as a DOT.
306 //
307 // FIXME: include additional filtering and annalysis passes to provide more
308 // specific useful information.
309 static CommandRegistration Unused(&Graph, []() -> Error {
310 int Fd;
311 auto EC = sys::fs::openFileForRead(GraphInput, Fd);
312 if (EC)
313 return make_error(
314 Twine("Cannot open file '") + GraphInput + "'", EC);
315
316 Error Err = Error::success();
317 xray::InstrumentationMapExtractor Extractor(GraphInstrMap, InstrMapFormat,
318 Err);
319 handleAllErrors(std::move(Err),
320 [&](const ErrorInfoBase &E) { E.log(errs()); });
321
322 const auto &FunctionAddresses = Extractor.getFunctionAddresses();
323
324 symbolize::LLVMSymbolizer::Options Opts(
325 symbolize::FunctionNameKind::LinkageName, true, true, false, "");
326
327 symbolize::LLVMSymbolizer Symbolizer(Opts);
328
329 llvm::xray::FuncIdConversionHelper FuncIdHelper(GraphInstrMap, Symbolizer,
330 FunctionAddresses);
331
332 xray::GraphRenderer GR(FuncIdHelper, GraphDeduceSiblingCalls);
333
334 raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::F_Text);
335
336 if (EC)
337 return make_error(
338 Twine("Cannot open file '") + GraphOutput + "' for writing.", EC);
339
340 auto TraceOrErr = loadTraceFile(GraphInput, true);
341
342 if (!TraceOrErr) {
343 return joinErrors(
344 make_error(
345 Twine("Failed loading input file '") + GraphInput + "'",
346 std::make_error_code(std::errc::protocol_error)),
347 std::move(Err));
348 }
349
350 auto &Trace = *TraceOrErr;
351 const auto &Header = Trace.getFileHeader();
352 for (const auto &Record : Trace) {
353 // Generate graph, FIXME: better error recovery.
354 if (!GR.accountRecord(Record)) {
355 return make_error(
356 Twine("Failed accounting function calls in file '") + GraphInput +
357 "'.",
358 std::make_error_code(std::errc::bad_message));
359 }
360 }
361
362 GR.exportGraphAsDOT(OS, Header, GraphEdgeLabel);
363 return Error::success();
364 });
0 //===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Generate a DOT file to represent the function call graph encountered in
10 // the trace.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #ifndef XRAY_GRAPH_H
15 #define XRAY_GRAPH_H
16
17 #include
18
19 #include "func-id-helper.h"
20 #include "llvm/ADT/DenseMap.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/Support/raw_ostream.h"
23 #include "llvm/XRay/Trace.h"
24 #include "llvm/XRay/XRayRecord.h"
25
26 namespace llvm {
27 namespace xray {
28
29 /// A class encapsulating the logic related to analyzing XRay traces, producting
30 /// Graphs from them and then exporting those graphs for review.
31 class GraphRenderer {
32 public:
33 /// An inner struct for common timing statistics information
34 struct TimeStat {
35 uint64_t Count;
36 double Min;
37 double Median;
38 double Pct90;
39 double Pct99;
40 double Max;
41 double Sum;
42 };
43
44 /// An inner struct for storing edge attributes for our graph. Here the
45 /// attributes are mainly function call statistics.
46 ///
47 /// FIXME: expand to contain more information eg call latencies.
48 struct EdgeAttribute {
49 TimeStat S;
50 std::vector Timings;
51 };
52
53 /// An Inner Struct for storing vertex attributes, at the moment just
54 /// SymbolNames, however in future we could store bulk function statistics.
55 ///
56 /// FIXME: Store more attributes based on instrumentation map.
57 struct VertexAttribute {
58 std::string SymbolName;
59 TimeStat S;
60 };
61
62 private:
63 /// The Graph stored in an edge-list like format, with the edges also having
64 /// An attached set of attributes.
65 DenseMap> Graph;
66
67 /// Graph Vertex Attributes. These are presently stored seperate from the
68 /// main graph.
69 DenseMap VertexAttrs;
70
71 struct FunctionAttr {
72 int32_t FuncId;
73 uint64_t TSC;
74 };
75
76 /// Use a Map to store the Function stack for each thread whilst building the
77 /// graph.
78 ///
79 /// FIXME: Perhaps we can Build this into LatencyAccountant? or vise versa?
80 DenseMap> PerThreadFunctionStack;
81
82 /// Usefull object for getting human readable Symbol Names.
83 FuncIdConversionHelper &FuncIdHelper;
84 bool DeduceSiblingCalls = false;
85 uint64_t CurrentMaxTSC = 0;
86
87 /// A private function to help implement the statistic generation functions;
88 template
89 void getStats(U begin, U end, GraphRenderer::TimeStat &S);
90
91 /// Calculates latency statistics for each edge and stores the data in the
92 /// Graph
93 void calculateEdgeStatistics();
94
95 /// Calculates latency statistics for each vertex and stores the data in the
96 /// Graph
97 void calculateVertexStatistics();
98
99 /// Normalises latency statistics for each edge and vertex by CycleFrequency;
100 void normaliseStatistics(double CycleFrequency);
101
102 public:
103 /// Takes in a reference to a FuncIdHelper in order to have ready access to
104 /// Symbol names.
105 explicit GraphRenderer(FuncIdConversionHelper &FuncIdHelper, bool DSC)
106 : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC) {}
107
108 /// Process an Xray record and expand the graph.
109 ///
110 /// This Function will return true on success, or false if records are not
111 /// presented in per-thread call-tree DFS order. (That is for each thread the
112 /// Records should be in order runtime on an ideal system.)
113 ///
114 /// FIXME: Make this more robust against small irregularities.
115 bool accountRecord(const XRayRecord &Record);
116
117 /// An enum for enumerating the various statistics gathered on latencies
118 enum class StatType { COUNT, MIN, MED, PCT90, PCT99, MAX, SUM };
119
120 /// Output the Embedded graph in DOT format on \p OS, labeling the edges by
121 /// \p T
122 void exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H,
123 StatType T = StatType::COUNT);
124 };
125 }
126 }
127
128 #endif // XRAY_GRAPH_H