llvm.org GIT mirror llvm / 420c070
Implemented color coding and Vertex labels in XRay Graph Summary: A patch to enable the llvm-xray graph subcommand to color edges and vertices based on statistics and to annotate vertices with statistics. Depends on D27243 Reviewers: dblaikie, dberris Reviewed By: dberris Subscribers: mgorny, llvm-commits Differential Revision: https://reviews.llvm.org/D28225 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@293031 91177308-0d34-0410-b5e6-96231b3b80d8 Dean Michael Berris 2 years ago
6 changed file(s) with 445 addition(s) and 102 deletion(s). Raw diff Collapse all Expand all
66 - { id: 2, address: 0x3, function: 0x2, kind: function-exit, always-instrument: true}
77 - { id: 3, address: 0x3, function: 0x3, kind: function-enter, always-instrument: true}
88 - { id: 3, address: 0x4, function: 0x3, kind: function-exit, always-instrument: true}
9 - { id: 4, address: 0x4, function: 0x4, kind: function-enter, always-instrument: true}
10 - { id: 4, address: 0x5, function: 0x4, kind: function-exit, always-instrument: true}
11 - { id: 5, address: 0x5, function: 0x5, kind: function-enter, always-instrument: true}
12 - { id: 5, address: 0x6, function: 0x5, kind: function-exit, always-instrument: true}
13 - { id: 6, address: 0x6, function: 0x6, kind: function-enter, always-instrument: true}
14 - { id: 6, address: 0x7, function: 0x6, kind: function-exit, always-instrument: true}
15 - { id: 7, address: 0x7, function: 0x7, kind: function-enter, always-instrument: true}
16 - { id: 7, address: 0x8, function: 0x7, kind: function-exit, always-instrument: true}
17 - { id: 8, address: 0x8, function: 0x8, kind: function-enter, always-instrument: true}
18 - { id: 8, address: 0x9, function: 0x8, kind: function-exit, always-instrument: true}
19 - { id: 9, address: 0x9, function: 0x9, kind: function-enter, always-instrument: true}
20 - { id: 9, address: 0xA, function: 0x9, kind: function-exit, always-instrument: true}
921 ...
0 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -e sum -c sum \
1 #RUN: | FileCheck %s -check-prefix=EDGE
2 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -v sum -b sum \
3 #RUN: | FileCheck %s -check-prefix=VERTEX
4 ---
5 header:
6 version: 1
7 type: 0
8 constant-tsc: true
9 nonstop-tsc: true
10 cycle-frequency: 1
11 records:
12 - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-enter, tsc: 10000 }
13 - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-exit, tsc: 10010 }
14 - { type: 0, func-id: 2, cpu: 1, thread: 111, kind: function-enter, tsc: 10100 }
15 - { type: 0, func-id: 2, cpu: 1, thread: 111, kind: function-exit, tsc: 10120 }
16 - { type: 0, func-id: 3, cpu: 1, thread: 111, kind: function-enter, tsc: 10200 }
17 - { type: 0, func-id: 3, cpu: 1, thread: 111, kind: function-exit, tsc: 10230 }
18 - { type: 0, func-id: 4, cpu: 1, thread: 111, kind: function-enter, tsc: 10300 }
19 - { type: 0, func-id: 4, cpu: 1, thread: 111, kind: function-exit, tsc: 10340 }
20 - { type: 0, func-id: 5, cpu: 1, thread: 111, kind: function-enter, tsc: 10400 }
21 - { type: 0, func-id: 5, cpu: 1, thread: 111, kind: function-exit, tsc: 10450 }
22 - { type: 0, func-id: 6, cpu: 1, thread: 111, kind: function-enter, tsc: 10500 }
23 - { type: 0, func-id: 6, cpu: 1, thread: 111, kind: function-exit, tsc: 10560 }
24 - { type: 0, func-id: 7, cpu: 1, thread: 111, kind: function-enter, tsc: 10600 }
25 - { type: 0, func-id: 7, cpu: 1, thread: 111, kind: function-exit, tsc: 10670 }
26 - { type: 0, func-id: 8, cpu: 1, thread: 111, kind: function-enter, tsc: 10700 }
27 - { type: 0, func-id: 8, cpu: 1, thread: 111, kind: function-exit, tsc: 10780 }
28 - { type: 0, func-id: 9, cpu: 1, thread: 111, kind: function-enter, tsc: 10800 }
29 - { type: 0, func-id: 9, cpu: 1, thread: 111, kind: function-exit, tsc: 10890 }
30 ---
31
32
33 #EDGE: digraph xray {
34 #EDGE-DAG: F0 -> F7 [label="7.{{[0-9]*}}e+01" color="#B00100"];
35 #EDGE-DAG: F0 -> F2 [label="2.{{[0-9]*}}e+01" color="#FD9965"];
36 #EDGE-DAG: F0 -> F9 [label="9.{{[0-9]*}}e+01" color="#7F0000"];
37 #EDGE-DAG: F0 -> F4 [label="4.{{[0-9]*}}e+01" color="#E8543b"];
38 #EDGE-DAG: F0 -> F6 [label="6.{{[0-9]*}}e+01" color="#C5140a"];
39 #EDGE-DAG: F0 -> F1 [label="1.{{[0-9]*}}e+01" color="#FDC58c"];
40 #EDGE-DAG: F0 -> F8 [label="8.{{[0-9]*}}e+01" color="#990101"];
41 #EDGE-DAG: F0 -> F3 [label="3.{{[0-9]*}}e+01" color="#F5744d"];
42 #EDGE-DAG: F0 -> F5 [label="5.{{[0-9]*}}e+01" color="#D83323"];
43 #EDGE-DAG: F7 [label="@(7)"];
44 #EDGE-DAG: F2 [label="@(2)"];
45 #EDGE-DAG: F9 [label="@(9)"];
46 #EDGE-DAG: F4 [label="@(4)"];
47 #EDGE-DAG: F6 [label="@(6)"];
48 #EDGE-DAG: F1 [label="@(1)"];
49 #EDGE-DAG: F8 [label="@(8)"];
50 #EDGE-DAG: F3 [label="@(3)"];
51 #EDGE-DAG: F5 [label="@(5)"];
52 #EDGE-NEXT: }
53 #
54 #VERTEX: digraph xray {
55 #VERTEX-DAG: node [shape=record];
56 #VERTEX-DAG: F0 -> F7 [label=""];
57 #VERTEX-DAG: F0 -> F2 [label=""];
58 #VERTEX-DAG: F0 -> F9 [label=""];
59 #VERTEX-DAG: F0 -> F4 [label=""];
60 #VERTEX-DAG: F0 -> F6 [label=""];
61 #VERTEX-DAG: F0 -> F1 [label=""];
62 #VERTEX-DAG: F0 -> F8 [label=""];
63 #VERTEX-DAG: F0 -> F3 [label=""];
64 #VERTEX-DAG: F0 -> F5 [label=""];
65 #VERTEX-DAG: F7 [label="{@(7)|7.{{[0-9]*}}e+01}" color="#B00100"];
66 #VERTEX-DAG: F2 [label="{@(2)|2.{{[0-9]*}}e+01}" color="#FD9965"];
67 #VERTEX-DAG: F9 [label="{@(9)|9.{{[0-9]*}}e+01}" color="#7F0000"];
68 #VERTEX-DAG: F4 [label="{@(4)|4.{{[0-9]*}}e+01}" color="#E8543b"];
69 #VERTEX-DAG: F6 [label="{@(6)|6.{{[0-9]*}}e+01}" color="#C5140a"];
70 #VERTEX-DAG: F1 [label="{@(1)|1.{{[0-9]*}}e+01}" color="#FDC58c"];
71 #VERTEX-DAG: F8 [label="{@(8)|8.{{[0-9]*}}e+01}" color="#990101"];
72 #VERTEX-DAG: F3 [label="{@(3)|3.{{[0-9]*}}e+01}" color="#F5744d"];
73 #VERTEX-DAG: F5 [label="{@(5)|5.{{[0-9]*}}e+01}" color="#D83323"];
74 #VERTEX-NEXT: }
0 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -d \
1 #RUN: | FileCheck %s -check-prefix=COUNT
1 #RUN: | FileCheck %s -check-prefix=EMPTY
22 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -d -e count \
33 #RUN: | FileCheck %s -check-prefix=COUNT
44 #
0 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml \
1 #RUN: | FileCheck %s -check-prefix=COUNT
1 #RUN: | FileCheck %s -check-prefix=EMPTY
22 #RUN: llvm-xray graph %s -o - -m %S/Inputs/simple-instrmap.yaml -t yaml -e count \
33 #RUN: | FileCheck %s -check-prefix=COUNT
44 #
1212 //===----------------------------------------------------------------------===//
1313 #include
1414 #include
15 #include
1516 #include
1617 #include
1718
1819 #include "xray-extract.h"
1920 #include "xray-graph.h"
2021 #include "xray-registry.h"
21 #include "llvm/Support/Errc.h"
22 #include "llvm/ADT/ArrayRef.h"
2223 #include "llvm/Support/ErrorHandling.h"
2324 #include "llvm/Support/FormatVariadic.h"
2425 #include "llvm/XRay/Trace.h"
3334 cl::desc(""),
3435 cl::Required, cl::sub(Graph));
3536
37 static cl::opt
38 GraphKeepGoing("keep-going", cl::desc("Keep going on errors encountered"),
39 cl::sub(Graph), cl::init(false));
40 static cl::alias GraphKeepGoing2("k", cl::aliasopt(GraphKeepGoing),
41 cl::desc("Alias for -keep-going"),
42 cl::sub(Graph));
43
3644 static cl::opt
3745 GraphOutput("output", cl::value_desc("Output file"), cl::init("-"),
3846 cl::desc("output file; use '-' for stdout"), cl::sub(Graph));
3947 static cl::alias GraphOutput2("o", cl::aliasopt(GraphOutput),
4048 cl::desc("Alias for -output"), cl::sub(Graph));
4149
42 static cl::opt GraphInstrMap(
43 "instr_map", cl::desc("binary with the instrumrntation map, or "
44 "a separate instrumentation map"),
45 cl::value_desc("binary with xray_instr_map"), cl::sub(Graph), cl::init(""));
50 static cl::opt
51 GraphInstrMap("instr_map",
52 cl::desc("binary with the instrumrntation map, or "
53 "a separate instrumentation map"),
54 cl::value_desc("binary with xray_instr_map"), cl::sub(Graph),
55 cl::init(""));
4656 static cl::alias GraphInstrMap2("m", cl::aliasopt(GraphInstrMap),
4757 cl::desc("alias for -instr_map"),
4858 cl::sub(Graph));
7181 GraphEdgeLabel("edge-label",
7282 cl::desc("Output graphs with edges labeled with this field"),
7383 cl::value_desc("field"), cl::sub(Graph),
74 cl::init(GraphRenderer::StatType::COUNT),
75 cl::values(clEnumValN(GraphRenderer::StatType::COUNT,
84 cl::init(GraphRenderer::StatType::NONE),
85 cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
86 "Do not label Edges"),
87 clEnumValN(GraphRenderer::StatType::COUNT,
7688 "count", "function call counts"),
7789 clEnumValN(GraphRenderer::StatType::MIN, "min",
7890 "minimum function durations"),
90102 cl::desc("Alias for -edge-label"),
91103 cl::sub(Graph));
92104
93 namespace {
105 static cl::opt GraphVertexLabel(
106 "vertex-label",
107 cl::desc("Output graphs with vertices labeled with this field"),
108 cl::value_desc("field"), cl::sub(Graph),
109 cl::init(GraphRenderer::StatType::NONE),
110 cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
111 "Do not label Edges"),
112 clEnumValN(GraphRenderer::StatType::COUNT, "count",
113 "function call counts"),
114 clEnumValN(GraphRenderer::StatType::MIN, "min",
115 "minimum function durations"),
116 clEnumValN(GraphRenderer::StatType::MED, "med",
117 "median function durations"),
118 clEnumValN(GraphRenderer::StatType::PCT90, "90p",
119 "90th percentile durations"),
120 clEnumValN(GraphRenderer::StatType::PCT99, "99p",
121 "99th percentile durations"),
122 clEnumValN(GraphRenderer::StatType::MAX, "max",
123 "maximum function durations"),
124 clEnumValN(GraphRenderer::StatType::SUM, "sum",
125 "sum of call durations")));
126 static cl::alias GraphVertexLabel2("v", cl::aliasopt(GraphVertexLabel),
127 cl::desc("Alias for -edge-label"),
128 cl::sub(Graph));
129
130 static cl::opt GraphEdgeColorType(
131 "color-edges",
132 cl::desc("Output graphs with edge colors determined by this field"),
133 cl::value_desc("field"), cl::sub(Graph),
134 cl::init(GraphRenderer::StatType::NONE),
135 cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
136 "Do not label Edges"),
137 clEnumValN(GraphRenderer::StatType::COUNT, "count",
138 "function call counts"),
139 clEnumValN(GraphRenderer::StatType::MIN, "min",
140 "minimum function durations"),
141 clEnumValN(GraphRenderer::StatType::MED, "med",
142 "median function durations"),
143 clEnumValN(GraphRenderer::StatType::PCT90, "90p",
144 "90th percentile durations"),
145 clEnumValN(GraphRenderer::StatType::PCT99, "99p",
146 "99th percentile durations"),
147 clEnumValN(GraphRenderer::StatType::MAX, "max",
148 "maximum function durations"),
149 clEnumValN(GraphRenderer::StatType::SUM, "sum",
150 "sum of call durations")));
151 static cl::alias GraphEdgeColorType2("c", cl::aliasopt(GraphEdgeColorType),
152 cl::desc("Alias for -color-edges"),
153 cl::sub(Graph));
154
155 static cl::opt GraphVertexColorType(
156 "color-vertices",
157 cl::desc("Output graphs with vertex colors determined by this field"),
158 cl::value_desc("field"), cl::sub(Graph),
159 cl::init(GraphRenderer::StatType::NONE),
160 cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
161 "Do not label Edges"),
162 clEnumValN(GraphRenderer::StatType::COUNT, "count",
163 "function call counts"),
164 clEnumValN(GraphRenderer::StatType::MIN, "min",
165 "minimum function durations"),
166 clEnumValN(GraphRenderer::StatType::MED, "med",
167 "median function durations"),
168 clEnumValN(GraphRenderer::StatType::PCT90, "90p",
169 "90th percentile durations"),
170 clEnumValN(GraphRenderer::StatType::PCT99, "99p",
171 "99th percentile durations"),
172 clEnumValN(GraphRenderer::StatType::MAX, "max",
173 "maximum function durations"),
174 clEnumValN(GraphRenderer::StatType::SUM, "sum",
175 "sum of call durations")));
176 static cl::alias GraphVertexColorType2("b", cl::aliasopt(GraphVertexColorType),
177 cl::desc("Alias for -edge-label"),
178 cl::sub(Graph));
179
94180 template T diff(T L, T R) { return std::max(L, R) - std::min(L, R); }
95181
96 void updateStat(GraphRenderer::TimeStat &S, int64_t lat) {
182 // Updates the statistics for a GraphRenderer::TimeStat
183 static void updateStat(GraphRenderer::TimeStat &S, int64_t L) {
97184 S.Count++;
98 if (S.Min > lat || S.Min == 0)
99 S.Min = lat;
100 if (S.Max < lat)
101 S.Max = lat;
102 S.Sum += lat;
103 }
104 }
105
106 // Evaluates an XRay record and performs accounting on it, creating and
107 // decorating a function call graph as it does so. It does this by maintaining
108 // a call stack on a per-thread basis and adding edges and verticies to the
109 // graph as they are seen for the first time.
110 //
111 // There is an immaginary root for functions at the top of their stack with
185 if (S.Min > L || S.Min == 0)
186 S.Min = L;
187 if (S.Max < L)
188 S.Max = L;
189 S.Sum += L;
190 }
191
192 // Evaluates an XRay record and performs accounting on it.
193 //
194 // If the record is an ENTER record it pushes the FuncID and TSC onto a
195 // structure representing the call stack for that function.
196 // If the record is an EXIT record it checks computes computes the ammount of
197 // time the function took to complete and then stores that information in an
198 // edge of the graph. If there is no matching ENTER record the function tries
199 // to recover by assuming that there were EXIT records which were missed, for
200 // example caused by tail call elimination and if the option is enabled then
201 // then tries to recover from this.
202 //
203 // This funciton will also error if the records are out of order, as the trace
204 // is expected to be sorted.
205 //
206 // The graph generated has an immaginary root for functions called by no-one at
112207 // FuncId 0.
113208 //
114 // FIXME: make more robust to errors and
115 // Decorate Graph More Heavily.
116209 // FIXME: Refactor this and account subcommand to reduce code duplication.
117 bool GraphRenderer::accountRecord(const XRayRecord &Record) {
210 Error GraphRenderer::accountRecord(const XRayRecord &Record) {
211 using std::make_error_code;
212 using std::errc;
118213 if (CurrentMaxTSC == 0)
119214 CurrentMaxTSC = Record.TSC;
120215
121216 if (Record.TSC < CurrentMaxTSC)
122 return false;
217 return make_error("Records not in order",
218 make_error_code(errc::invalid_argument));
123219
124220 auto &ThreadStack = PerThreadFunctionStack[Record.TId];
125221 switch (Record.Type) {
135231 // duplication
136232 if (ThreadStack.size() == 0 || ThreadStack.back().FuncId != Record.FuncId) {
137233 if (!DeduceSiblingCalls)
138 return false;
234 return make_error("No matching ENTRY record",
235 make_error_code(errc::invalid_argument));
139236 auto Parent = std::find_if(
140237 ThreadStack.rbegin(), ThreadStack.rend(),
141238 [&](const FunctionAttr &A) { return A.FuncId == Record.FuncId; });
142239 if (Parent == ThreadStack.rend())
143 return false; // There is no matching Function for this exit.
240 return make_error(
241 "No matching Entry record in stack",
242 make_error_code(errc::invalid_argument)); // There is no matching
243 // Function for this exit.
144244 while (ThreadStack.back().FuncId != Record.FuncId) {
145245 uint64_t D = diff(ThreadStack.back().TSC, Record.TSC);
146246 int32_t TopFuncId = ThreadStack.back().FuncId;
163263 }
164264 }
165265
166 return true;
266 return Error::success();
167267 }
168268
169269 template
180280 S.Pct99 = *(begin + Pct99Off);
181281 }
182282
283 void GraphRenderer::updateMaxStats(const GraphRenderer::TimeStat &S,
284 GraphRenderer::TimeStat &M) {
285 M.Count = std::max(M.Count, S.Count);
286 M.Min = std::max(M.Min, S.Min);
287 M.Median = std::max(M.Median, S.Median);
288 M.Pct90 = std::max(M.Pct90, S.Pct90);
289 M.Pct99 = std::max(M.Pct99, S.Pct99);
290 M.Max = std::max(M.Max, S.Max);
291 M.Sum = std::max(M.Sum, S.Sum);
292 }
293
183294 void GraphRenderer::calculateEdgeStatistics() {
184295 for (auto &V : Graph) {
185296 for (auto &E : V.second) {
186297 auto &A = E.second;
187298 getStats(A.Timings.begin(), A.Timings.end(), A.S);
299 updateMaxStats(A.S, GraphEdgeMax);
188300 }
189301 }
190302 }
210322 P->Timings.end());
211323 }
212324 getStats(TempTimings.begin(), TempTimings.end(), VertexAttrs[V.first].S);
325 updateMaxStats(VertexAttrs[V.first].S, GraphVertexMax);
213326 TempTimings.clear();
214327 }
215328 }
216329
217 void GraphRenderer::normaliseStatistics(double CycleFrequency) {
330 // A Helper function for normalizeStatistics which normalises a single
331 // TimeStat element.
332 static void normalizeTimeStat(GraphRenderer::TimeStat &S,
333 double CycleFrequency) {
334 S.Min /= CycleFrequency;
335 S.Median /= CycleFrequency;
336 S.Max /= CycleFrequency;
337 S.Sum /= CycleFrequency;
338 S.Pct90 /= CycleFrequency;
339 S.Pct99 /= CycleFrequency;
340 }
341
342 // Normalises the statistics in the graph for a given TSC frequency.
343 void GraphRenderer::normalizeStatistics(double CycleFrequency) {
218344 for (auto &V : Graph) {
219345 for (auto &E : V.second) {
220346 auto &S = E.second.S;
221 S.Min /= CycleFrequency;
222 S.Median /= CycleFrequency;
223 S.Max /= CycleFrequency;
224 S.Sum /= CycleFrequency;
225 S.Pct90 /= CycleFrequency;
226 S.Pct99 /= CycleFrequency;
347 normalizeTimeStat(S, CycleFrequency);
227348 }
228349 }
229350 for (auto &V : VertexAttrs) {
230351 auto &S = V.second.S;
231 S.Min /= CycleFrequency;
232 S.Median /= CycleFrequency;
233 S.Max /= CycleFrequency;
234 S.Sum /= CycleFrequency;
235 S.Pct90 /= CycleFrequency;
236 S.Pct99 /= CycleFrequency;
237 }
238 }
239
240 namespace {
241 void outputEdgeInfo(const GraphRenderer::TimeStat &S, GraphRenderer::StatType T,
242 raw_ostream &OS) {
352 normalizeTimeStat(S, CycleFrequency);
353 }
354
355 normalizeTimeStat(GraphEdgeMax, CycleFrequency);
356 normalizeTimeStat(GraphVertexMax, CycleFrequency);
357 }
358
359 // Returns a string containing the value of statistic field T
360 std::string
361 GraphRenderer::TimeStat::getAsString(GraphRenderer::StatType T) const {
362 std::string St;
363 raw_string_ostream S{St};
243364 switch (T) {
244365 case GraphRenderer::StatType::COUNT:
245 OS << S.Count;
366 S << Count;
246367 break;
247368 case GraphRenderer::StatType::MIN:
248 OS << S.Min;
369 S << Min;
249370 break;
250371 case GraphRenderer::StatType::MED:
251 OS << S.Median;
372 S << Median;
252373 break;
253374 case GraphRenderer::StatType::PCT90:
254 OS << S.Pct90;
375 S << Pct90;
255376 break;
256377 case GraphRenderer::StatType::PCT99:
257 OS << S.Pct99;
378 S << Pct99;
258379 break;
259380 case GraphRenderer::StatType::MAX:
260 OS << S.Max;
381 S << Max;
261382 break;
262383 case GraphRenderer::StatType::SUM:
263 OS << S.Sum;
264 break;
265 }
266 }
384 S << Sum;
385 break;
386 case GraphRenderer::StatType::NONE:
387 break;
388 }
389 return S.str();
390 }
391
392 // Evaluates a polynomial given the coefficints provided in an ArrayRef
393 // evaluating:
394 //
395 // p(x) = a[n-0]*x^0 + a[n-1]*x^1 + ... a[n-n]*x^n
396 //
397 // at x_0 using Horner's Method for both performance and stability reasons.
398 static double polyEval(ArrayRef a, double x_0) {
399 double B = 0;
400 for (const auto &c : a) {
401 B = c + B * x_0;
402 }
403 return B;
404 }
405
406 // Takes a double precision number, clips it between 0 and 1 and then converts
407 // that to an integer between 0x00 and 0xFF with proxpper rounding.
408 static uint8_t uintIntervalTo8bitChar(double B) {
409 double n = std::max(std::min(B, 1.0), 0.0);
410 return static_cast(255 * n + 0.5);
411 }
412
413 // Gets a color in a gradient given a number in the interval [0,1], it does this
414 // by evaluating a polynomial which maps [0, 1] -> [0, 1] for each of the R G
415 // and B values in the color. It then converts this [0,1] colors to a 24 bit
416 // color.
417 //
418 // In order to calculate these polynomials,
419 // 1. Convert the OrRed9 color scheme from http://colorbrewer2.org/ from sRGB
420 // to LAB color space.
421 // 2. Interpolate between the descrete colors in LAB space using a cubic
422 // spline interpolation.
423 // 3. Sample this interpolation at 100 points and convert to sRGB.
424 // 4. Calculate a polynomial fit for these 100 points for each of R G and B.
425 // We used a polynomial of degree 9 arbitrarily based on a fuzzy goodness
426 // of fit metric (using human judgement);
427 // 5. Extract these polynomial coefficients from matlab as a set of constants.
428 static std::string getColor(double point) {
429 assert(point >= 0.0 && point <= 1);
430 const static double RedPoly[] = {-38.4295, 239.239, -600.108, 790.544,
431 -591.26, 251.304, -58.0983, 6.62999,
432 -0.325899, 1.00173};
433 const static double GreenPoly[] = {-603.634, 2338.15, -3606.74, 2786.16,
434 -1085.19, 165.15, 11.2584, -6.11338,
435 -0.0091078, 0.965469};
436 const static double BluePoly[] = {-325.686, 947.415, -699.079, -513.75,
437 1127.78, -732.617, 228.092, -33.8202,
438 0.732108, 0.913916};
439
440 uint8_t r = uintIntervalTo8bitChar(polyEval(RedPoly, point));
441 uint8_t g = uintIntervalTo8bitChar(polyEval(GreenPoly, point));
442 uint8_t b = uintIntervalTo8bitChar(polyEval(BluePoly, point));
443
444 return llvm::formatv("#{0:X-2}{1:X-2}{2:x-2}", r, g, b);
445 }
446
447 // Returns the quotient between the property T of this and another TimeStat as
448 // a double
449 double GraphRenderer::TimeStat::compare(StatType T, const TimeStat &O) const {
450 double retval = 0;
451 switch (T) {
452 case GraphRenderer::StatType::COUNT:
453 retval = static_cast(Count) / static_cast(O.Count);
454 break;
455 case GraphRenderer::StatType::MIN:
456 retval = Min / O.Min;
457 break;
458 case GraphRenderer::StatType::MED:
459 retval = Median / O.Median;
460 break;
461 case GraphRenderer::StatType::PCT90:
462 retval = Pct90 / O.Pct90;
463 break;
464 case GraphRenderer::StatType::PCT99:
465 retval = Pct99 / O.Pct99;
466 break;
467 case GraphRenderer::StatType::MAX:
468 retval = Max / O.Max;
469 break;
470 case GraphRenderer::StatType::SUM:
471 retval = Sum / O.Sum;
472 break;
473 case GraphRenderer::StatType::NONE:
474 retval = 0.0;
475 break;
476 }
477 return std::sqrt(
478 retval); // the square root here provides more dynamic contrast for
479 // low runtime edges, giving better separation and
480 // coloring lower down the call stack.
267481 }
268482
269483 // Outputs a DOT format version of the Graph embedded in the GraphRenderer
273487 //
274488 // FIXME: output more information, better presented.
275489 void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H,
276 StatType T) {
490 StatType ET, StatType EC, StatType VT,
491 StatType VC) {
277492 calculateEdgeStatistics();
278493 calculateVertexStatistics();
279494 if (H.CycleFrequency)
280 normaliseStatistics(H.CycleFrequency);
495 normalizeStatistics(H.CycleFrequency);
281496
282497 OS << "digraph xray {\n";
498
499 if (VT != StatType::NONE)
500 OS << "node [shape=record];\n";
283501
284502 for (const auto &V : Graph)
285503 for (const auto &E : V.second) {
504 const auto &S = E.second.S;
286505 OS << "F" << V.first << " -> "
287 << "F" << E.first << " [label=\"";
288 outputEdgeInfo(E.second.S, T, OS);
289 OS << "\"];\n";
506 << "F" << E.first << " [label=\"" << S.getAsString(ET) << "\"";
507 if (EC != StatType::NONE)
508 OS << " color=\"" << getColor(S.compare(EC, GraphEdgeMax)) << "\"";
509 OS << "];\n";
290510 }
291511
292 for (const auto &V : VertexAttrs)
293 OS << "F" << V.first << " [label=\""
294 << (V.second.SymbolName.size() > 40
295 ? V.second.SymbolName.substr(0, 40) + "..."
296 : V.second.SymbolName)
297 << "\"];\n";
298
512 for (const auto &V : VertexAttrs) {
513 const auto &VA = V.second;
514 OS << "F" << V.first << " [label=\"" << (VT != StatType::NONE ? "{" : "")
515 << (VA.SymbolName.size() > 40 ? VA.SymbolName.substr(0, 40) + "..."
516 : VA.SymbolName);
517 if (VT != StatType::NONE)
518 OS << "|" << VA.S.getAsString(VT) << "}\"";
519 else
520 OS << "\"";
521 if (VC != StatType::NONE)
522 OS << " color=\"" << getColor(VA.S.compare(VC, GraphVertexMax)) << "\"";
523 OS << "];\n";
524 }
299525 OS << "}\n";
300526 }
301527
351577 auto &Trace = *TraceOrErr;
352578 const auto &Header = Trace.getFileHeader();
353579 for (const auto &Record : Trace) {
354 // Generate graph, FIXME: better error recovery.
355 if (!GR.accountRecord(Record)) {
356 return make_error(
357 Twine("Failed accounting function calls in file '") + GraphInput +
358 "'.",
359 make_error_code(llvm::errc::invalid_argument));
580 // Generate graph.
581 auto E = GR.accountRecord(Record);
582 if (!E)
583 continue;
584
585 for (const auto &ThreadStack : GR.getPerThreadFunctionStack()) {
586 errs() << "Thread ID: " << ThreadStack.first << "\n";
587 auto Level = ThreadStack.second.size();
588 for (const auto &Entry : llvm::reverse(ThreadStack.second))
589 errs() << "#" << Level-- << "\t"
590 << FuncIdHelper.SymbolOrNumber(Entry.FuncId) << '\n';
360591 }
361 }
362
363 GR.exportGraphAsDOT(OS, Header, GraphEdgeLabel);
364 return Error::success();
592
593 if (!GraphKeepGoing)
594 return joinErrors(std::move(E), std::move(Err));
595 handleAllErrors(std::move(E),
596 [&](const ErrorInfoBase &E) { E.log(errs()); });
597 }
598
599 GR.exportGraphAsDOT(OS, Header, GraphEdgeLabel, GraphEdgeColorType,
600 GraphVertexLabel, GraphVertexColorType);
601 return Err;
365602 });
1414 #ifndef XRAY_GRAPH_H
1515 #define XRAY_GRAPH_H
1616
17 #include
1718 #include
1819
1920 #include "func-id-helper.h"
2021 #include "llvm/ADT/DenseMap.h"
2122 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/Support/Errc.h"
24 #include "llvm/Support/Program.h"
2225 #include "llvm/Support/raw_ostream.h"
23 #include "llvm/Support/Program.h"
2426 #include "llvm/XRay/Trace.h"
2527 #include "llvm/XRay/XRayRecord.h"
2628
3133 /// Graphs from them and then exporting those graphs for review.
3234 class GraphRenderer {
3335 public:
36 /// An enum for enumerating the various statistics gathered on latencies
37 enum class StatType { NONE, COUNT, MIN, MED, PCT90, PCT99, MAX, SUM };
38
3439 /// An inner struct for common timing statistics information
3540 struct TimeStat {
36 uint64_t Count;
37 double Min;
38 double Median;
39 double Pct90;
40 double Pct99;
41 double Max;
42 double Sum;
41 uint64_t Count = 0;
42 double Min = 0;
43 double Median = 0;
44 double Pct90 = 0;
45 double Pct99 = 0;
46 double Max = 0;
47 double Sum = 0;
48 std::string getAsString(StatType T) const;
49 double compare(StatType T, const TimeStat &Other) const;
4350 };
4451
4552 /// An inner struct for storing edge attributes for our graph. Here the
6067 TimeStat S;
6168 };
6269
70 struct FunctionAttr {
71 int32_t FuncId;
72 uint64_t TSC;
73 };
74
75 typedef SmallVector FunctionStack;
76
77 typedef DenseMap
78 PerThreadFunctionStackMap;
79
6380 private:
6481 /// The Graph stored in an edge-list like format, with the edges also having
6582 /// An attached set of attributes.
6986 /// main graph.
7087 DenseMap VertexAttrs;
7188
72 struct FunctionAttr {
73 int32_t FuncId;
74 uint64_t TSC;
75 };
89 TimeStat GraphEdgeMax;
90 TimeStat GraphVertexMax;
7691
7792 /// Use a Map to store the Function stack for each thread whilst building the
7893 /// graph.
7994 ///
8095 /// FIXME: Perhaps we can Build this into LatencyAccountant? or vise versa?
81 DenseMap>
82 PerThreadFunctionStack;
96 PerThreadFunctionStackMap PerThreadFunctionStack;
8397
8498 /// Usefull object for getting human readable Symbol Names.
8599 FuncIdConversionHelper &FuncIdHelper;
89103 /// A private function to help implement the statistic generation functions;
90104 template
91105 void getStats(U begin, U end, GraphRenderer::TimeStat &S);
106 void updateMaxStats(const TimeStat &S, TimeStat &M);
92107
93108 /// Calculates latency statistics for each edge and stores the data in the
94109 /// Graph
99114 void calculateVertexStatistics();
100115
101116 /// Normalises latency statistics for each edge and vertex by CycleFrequency;
102 void normaliseStatistics(double CycleFrequency);
117 void normalizeStatistics(double CycleFrequency);
103118
104119 public:
105120 /// Takes in a reference to a FuncIdHelper in order to have ready access to
114129 /// Records should be in order runtime on an ideal system.)
115130 ///
116131 /// FIXME: Make this more robust against small irregularities.
117 bool accountRecord(const XRayRecord &Record);
132 Error accountRecord(const XRayRecord &Record);
118133
119 /// An enum for enumerating the various statistics gathered on latencies
120 enum class StatType { COUNT, MIN, MED, PCT90, PCT99, MAX, SUM };
134 const PerThreadFunctionStackMap getPerThreadFunctionStack() const {
135 return PerThreadFunctionStack;
136 }
121137
122138 /// Output the Embedded graph in DOT format on \p OS, labeling the edges by
123139 /// \p T
124140 void exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H,
125 StatType T = StatType::COUNT);
141 StatType EdgeLabel = StatType::NONE,
142 StatType EdgeColor = StatType::NONE,
143 StatType VertexLabel = StatType::NONE,
144 StatType VertexColor = StatType::NONE);
126145 };
127146 }
128147 }