1 | //===-- xray-graph-diff.cpp: XRay Function Call Graph Renderer ------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Generate a DOT file to represent the function call graph encountered in |
10 | // the trace. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | #include <cassert> |
14 | #include <cmath> |
15 | #include <limits> |
16 | #include <string> |
17 | |
18 | #include "xray-graph-diff.h" |
19 | #include "xray-graph.h" |
20 | #include "xray-registry.h" |
21 | |
22 | #include "xray-color-helper.h" |
23 | #include "llvm/Support/FormatVariadic.h" |
24 | #include "llvm/Support/MemoryBuffer.h" |
25 | #include "llvm/XRay/Trace.h" |
26 | |
27 | using namespace llvm; |
28 | using namespace xray; |
29 | |
30 | static cl::SubCommand GraphDiff("graph-diff" , |
31 | "Generate diff of function-call graphs" ); |
32 | static cl::opt<std::string> GraphDiffInput1(cl::Positional, |
33 | cl::desc("<xray log file 1>" ), |
34 | cl::Required, cl::sub(GraphDiff)); |
35 | static cl::opt<std::string> GraphDiffInput2(cl::Positional, |
36 | cl::desc("<xray log file 2>" ), |
37 | cl::Required, cl::sub(GraphDiff)); |
38 | |
39 | static cl::opt<bool> |
40 | GraphDiffKeepGoing("keep-going" , |
41 | cl::desc("Keep going on errors encountered" ), |
42 | cl::sub(GraphDiff), cl::init(Val: false)); |
43 | static cl::alias GraphDiffKeepGoingA("k" , cl::aliasopt(GraphDiffKeepGoing), |
44 | cl::desc("Alias for -keep-going" )); |
45 | static cl::opt<bool> |
46 | GraphDiffKeepGoing1("keep-going-1" , |
47 | cl::desc("Keep going on errors encountered in trace 1" ), |
48 | cl::sub(GraphDiff), cl::init(Val: false)); |
49 | static cl::alias GraphDiffKeepGoing1A("k1" , cl::aliasopt(GraphDiffKeepGoing1), |
50 | cl::desc("Alias for -keep-going-1" )); |
51 | static cl::opt<bool> |
52 | GraphDiffKeepGoing2("keep-going-2" , |
53 | cl::desc("Keep going on errors encountered in trace 2" ), |
54 | cl::sub(GraphDiff), cl::init(Val: false)); |
55 | static cl::alias GraphDiffKeepGoing2A("k2" , cl::aliasopt(GraphDiffKeepGoing2), |
56 | cl::desc("Alias for -keep-going-2" )); |
57 | |
58 | static cl::opt<std::string> |
59 | GraphDiffInstrMap("instr-map" , |
60 | cl::desc("binary with the instrumentation map, or " |
61 | "a separate instrumentation map for graph" ), |
62 | cl::value_desc("binary with xray_instr_map or yaml" ), |
63 | cl::sub(GraphDiff), cl::init(Val: "" )); |
64 | static cl::alias GraphDiffInstrMapA("m" , cl::aliasopt(GraphDiffInstrMap), |
65 | cl::desc("Alias for -instr-map" )); |
66 | static cl::opt<std::string> |
67 | GraphDiffInstrMap1("instr-map-1" , |
68 | cl::desc("binary with the instrumentation map, or " |
69 | "a separate instrumentation map for graph 1" ), |
70 | cl::value_desc("binary with xray_instr_map or yaml" ), |
71 | cl::sub(GraphDiff), cl::init(Val: "" )); |
72 | static cl::alias GraphDiffInstrMap1A("m1" , cl::aliasopt(GraphDiffInstrMap1), |
73 | cl::desc("Alias for -instr-map-1" )); |
74 | static cl::opt<std::string> |
75 | GraphDiffInstrMap2("instr-map-2" , |
76 | cl::desc("binary with the instrumentation map, or " |
77 | "a separate instrumentation map for graph 2" ), |
78 | cl::value_desc("binary with xray_instr_map or yaml" ), |
79 | cl::sub(GraphDiff), cl::init(Val: "" )); |
80 | static cl::alias GraphDiffInstrMap2A("m2" , cl::aliasopt(GraphDiffInstrMap2), |
81 | cl::desc("Alias for -instr-map-2" )); |
82 | |
83 | static cl::opt<bool> GraphDiffDeduceSiblingCalls( |
84 | "deduce-sibling-calls" , |
85 | cl::desc("Deduce sibling calls when unrolling function call stacks" ), |
86 | cl::sub(GraphDiff), cl::init(Val: false)); |
87 | static cl::alias |
88 | GraphDiffDeduceSiblingCallsA("d" , cl::aliasopt(GraphDiffDeduceSiblingCalls), |
89 | cl::desc("Alias for -deduce-sibling-calls" )); |
90 | static cl::opt<bool> GraphDiffDeduceSiblingCalls1( |
91 | "deduce-sibling-calls-1" , |
92 | cl::desc("Deduce sibling calls when unrolling function call stacks" ), |
93 | cl::sub(GraphDiff), cl::init(Val: false)); |
94 | static cl::alias GraphDiffDeduceSiblingCalls1A( |
95 | "d1" , cl::aliasopt(GraphDiffDeduceSiblingCalls1), |
96 | cl::desc("Alias for -deduce-sibling-calls-1" )); |
97 | static cl::opt<bool> GraphDiffDeduceSiblingCalls2( |
98 | "deduce-sibling-calls-2" , |
99 | cl::desc("Deduce sibling calls when unrolling function call stacks" ), |
100 | cl::sub(GraphDiff), cl::init(Val: false)); |
101 | static cl::alias GraphDiffDeduceSiblingCalls2A( |
102 | "d2" , cl::aliasopt(GraphDiffDeduceSiblingCalls2), |
103 | cl::desc("Alias for -deduce-sibling-calls-2" )); |
104 | |
105 | static cl::opt<GraphRenderer::StatType> GraphDiffEdgeLabel( |
106 | "edge-label" , cl::desc("Output graphs with edges labeled with this field" ), |
107 | cl::value_desc("field" ), cl::sub(GraphDiff), |
108 | cl::init(Val: GraphRenderer::StatType::NONE), |
109 | cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none" , |
110 | "Do not label Edges" ), |
111 | clEnumValN(GraphRenderer::StatType::COUNT, "count" , |
112 | "function call counts" ), |
113 | clEnumValN(GraphRenderer::StatType::MIN, "min" , |
114 | "minimum function durations" ), |
115 | clEnumValN(GraphRenderer::StatType::MED, "med" , |
116 | "median function durations" ), |
117 | clEnumValN(GraphRenderer::StatType::PCT90, "90p" , |
118 | "90th percentile durations" ), |
119 | clEnumValN(GraphRenderer::StatType::PCT99, "99p" , |
120 | "99th percentile durations" ), |
121 | clEnumValN(GraphRenderer::StatType::MAX, "max" , |
122 | "maximum function durations" ), |
123 | clEnumValN(GraphRenderer::StatType::SUM, "sum" , |
124 | "sum of call durations" ))); |
125 | static cl::alias GraphDiffEdgeLabelA("e" , cl::aliasopt(GraphDiffEdgeLabel), |
126 | cl::desc("Alias for -edge-label" )); |
127 | |
128 | static cl::opt<GraphRenderer::StatType> GraphDiffEdgeColor( |
129 | "edge-color" , cl::desc("Output graphs with edges colored by this field" ), |
130 | cl::value_desc("field" ), cl::sub(GraphDiff), |
131 | cl::init(Val: GraphRenderer::StatType::NONE), |
132 | cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none" , |
133 | "Do not color Edges" ), |
134 | clEnumValN(GraphRenderer::StatType::COUNT, "count" , |
135 | "function call counts" ), |
136 | clEnumValN(GraphRenderer::StatType::MIN, "min" , |
137 | "minimum function durations" ), |
138 | clEnumValN(GraphRenderer::StatType::MED, "med" , |
139 | "median function durations" ), |
140 | clEnumValN(GraphRenderer::StatType::PCT90, "90p" , |
141 | "90th percentile durations" ), |
142 | clEnumValN(GraphRenderer::StatType::PCT99, "99p" , |
143 | "99th percentile durations" ), |
144 | clEnumValN(GraphRenderer::StatType::MAX, "max" , |
145 | "maximum function durations" ), |
146 | clEnumValN(GraphRenderer::StatType::SUM, "sum" , |
147 | "sum of call durations" ))); |
148 | static cl::alias GraphDiffEdgeColorA("c" , cl::aliasopt(GraphDiffEdgeColor), |
149 | cl::desc("Alias for -edge-color" )); |
150 | |
151 | static cl::opt<GraphRenderer::StatType> GraphDiffVertexLabel( |
152 | "vertex-label" , |
153 | cl::desc("Output graphs with vertices labeled with this field" ), |
154 | cl::value_desc("field" ), cl::sub(GraphDiff), |
155 | cl::init(Val: GraphRenderer::StatType::NONE), |
156 | cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none" , |
157 | "Do not label Vertices" ), |
158 | clEnumValN(GraphRenderer::StatType::COUNT, "count" , |
159 | "function call counts" ), |
160 | clEnumValN(GraphRenderer::StatType::MIN, "min" , |
161 | "minimum function durations" ), |
162 | clEnumValN(GraphRenderer::StatType::MED, "med" , |
163 | "median function durations" ), |
164 | clEnumValN(GraphRenderer::StatType::PCT90, "90p" , |
165 | "90th percentile durations" ), |
166 | clEnumValN(GraphRenderer::StatType::PCT99, "99p" , |
167 | "99th percentile durations" ), |
168 | clEnumValN(GraphRenderer::StatType::MAX, "max" , |
169 | "maximum function durations" ), |
170 | clEnumValN(GraphRenderer::StatType::SUM, "sum" , |
171 | "sum of call durations" ))); |
172 | static cl::alias GraphDiffVertexLabelA("v" , cl::aliasopt(GraphDiffVertexLabel), |
173 | cl::desc("Alias for -vertex-label" )); |
174 | |
175 | static cl::opt<GraphRenderer::StatType> GraphDiffVertexColor( |
176 | "vertex-color" , |
177 | cl::desc("Output graphs with vertices colored by this field" ), |
178 | cl::value_desc("field" ), cl::sub(GraphDiff), |
179 | cl::init(Val: GraphRenderer::StatType::NONE), |
180 | cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none" , |
181 | "Do not color Vertices" ), |
182 | clEnumValN(GraphRenderer::StatType::COUNT, "count" , |
183 | "function call counts" ), |
184 | clEnumValN(GraphRenderer::StatType::MIN, "min" , |
185 | "minimum function durations" ), |
186 | clEnumValN(GraphRenderer::StatType::MED, "med" , |
187 | "median function durations" ), |
188 | clEnumValN(GraphRenderer::StatType::PCT90, "90p" , |
189 | "90th percentile durations" ), |
190 | clEnumValN(GraphRenderer::StatType::PCT99, "99p" , |
191 | "99th percentile durations" ), |
192 | clEnumValN(GraphRenderer::StatType::MAX, "max" , |
193 | "maximum function durations" ), |
194 | clEnumValN(GraphRenderer::StatType::SUM, "sum" , |
195 | "sum of call durations" ))); |
196 | static cl::alias GraphDiffVertexColorA("b" , cl::aliasopt(GraphDiffVertexColor), |
197 | cl::desc("Alias for -vertex-color" )); |
198 | |
199 | static cl::opt<int> GraphDiffVertexLabelTrunc( |
200 | "vertex-label-trun" , cl::desc("What length to truncate vertex labels to " ), |
201 | cl::sub(GraphDiff), cl::init(Val: 40)); |
202 | static cl::alias |
203 | GraphDiffVertexLabelTrunc1("t" , cl::aliasopt(GraphDiffVertexLabelTrunc), |
204 | cl::desc("Alias for -vertex-label-trun" )); |
205 | |
206 | static cl::opt<std::string> |
207 | GraphDiffOutput("output" , cl::value_desc("Output file" ), cl::init(Val: "-" ), |
208 | cl::desc("output file; use '-' for stdout" ), |
209 | cl::sub(GraphDiff)); |
210 | static cl::alias GraphDiffOutputA("o" , cl::aliasopt(GraphDiffOutput), |
211 | cl::desc("Alias for -output" )); |
212 | |
213 | Expected<GraphDiffRenderer> GraphDiffRenderer::Factory::getGraphDiffRenderer() { |
214 | GraphDiffRenderer R; |
215 | |
216 | for (int i = 0; i < N; ++i) { |
217 | const auto &G = this->G[i].get(); |
218 | for (const auto &V : G.vertices()) { |
219 | const auto &VAttr = V.second; |
220 | R.G[VAttr.SymbolName].CorrVertexPtr[i] = &V; |
221 | } |
222 | for (const auto &E : G.edges()) { |
223 | auto &EdgeTailID = E.first.first; |
224 | auto &EdgeHeadID = E.first.second; |
225 | auto EdgeTailAttrOrErr = G.at(I: EdgeTailID); |
226 | auto EdgeHeadAttrOrErr = G.at(I: EdgeHeadID); |
227 | if (!EdgeTailAttrOrErr) |
228 | return EdgeTailAttrOrErr.takeError(); |
229 | if (!EdgeHeadAttrOrErr) |
230 | return EdgeHeadAttrOrErr.takeError(); |
231 | GraphT::EdgeIdentifier ID{EdgeTailAttrOrErr->SymbolName, |
232 | EdgeHeadAttrOrErr->SymbolName}; |
233 | R.G[ID].CorrEdgePtr[i] = &E; |
234 | } |
235 | } |
236 | |
237 | return R; |
238 | } |
239 | // Returns the Relative change With respect to LeftStat between LeftStat |
240 | // and RightStat. |
241 | static double statRelDiff(const GraphDiffRenderer::TimeStat &LeftStat, |
242 | const GraphDiffRenderer::TimeStat &RightStat, |
243 | GraphDiffRenderer::StatType T) { |
244 | double LeftAttr = LeftStat.getDouble(T); |
245 | double RightAttr = RightStat.getDouble(T); |
246 | |
247 | return RightAttr / LeftAttr - 1.0; |
248 | } |
249 | |
250 | static std::string getColor(const GraphDiffRenderer::GraphT::EdgeValueType &E, |
251 | const GraphDiffRenderer::GraphT &G, ColorHelper H, |
252 | GraphDiffRenderer::StatType T) { |
253 | auto &EdgeAttr = E.second; |
254 | if (EdgeAttr.CorrEdgePtr[0] == nullptr) |
255 | return H.getColorString(Point: 2.0); // A number greater than 1.0 |
256 | if (EdgeAttr.CorrEdgePtr[1] == nullptr) |
257 | return H.getColorString(Point: -2.0); // A number less than -1.0 |
258 | |
259 | if (T == GraphDiffRenderer::StatType::NONE) |
260 | return H.getDefaultColorString(); |
261 | |
262 | const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S; |
263 | const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S; |
264 | |
265 | double RelDiff = statRelDiff(LeftStat, RightStat, T); |
266 | double CappedRelDiff = std::clamp(val: RelDiff, lo: -1.0, hi: 1.0); |
267 | |
268 | return H.getColorString(Point: CappedRelDiff); |
269 | } |
270 | |
271 | static std::string getColor(const GraphDiffRenderer::GraphT::VertexValueType &V, |
272 | const GraphDiffRenderer::GraphT &G, ColorHelper H, |
273 | GraphDiffRenderer::StatType T) { |
274 | auto &VertexAttr = V.second; |
275 | if (VertexAttr.CorrVertexPtr[0] == nullptr) |
276 | return H.getColorString(Point: 2.0); // A number greater than 1.0 |
277 | if (VertexAttr.CorrVertexPtr[1] == nullptr) |
278 | return H.getColorString(Point: -2.0); // A number less than -1.0 |
279 | |
280 | if (T == GraphDiffRenderer::StatType::NONE) |
281 | return H.getDefaultColorString(); |
282 | |
283 | const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S; |
284 | const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S; |
285 | |
286 | double RelDiff = statRelDiff(LeftStat, RightStat, T); |
287 | double CappedRelDiff = std::clamp(val: RelDiff, lo: -1.0, hi: 1.0); |
288 | |
289 | return H.getColorString(Point: CappedRelDiff); |
290 | } |
291 | |
292 | static Twine truncateString(const StringRef &S, size_t n) { |
293 | return (S.size() > n) ? Twine(S.substr(Start: 0, N: n)) + "..." : Twine(S); |
294 | } |
295 | |
296 | template <typename T> static bool containsNullptr(const T &Collection) { |
297 | return llvm::is_contained(Collection, nullptr); |
298 | } |
299 | |
300 | static std::string getLabel(const GraphDiffRenderer::GraphT::EdgeValueType &E, |
301 | GraphDiffRenderer::StatType EL) { |
302 | auto &EdgeAttr = E.second; |
303 | switch (EL) { |
304 | case GraphDiffRenderer::StatType::NONE: |
305 | return "" ; |
306 | default: |
307 | if (containsNullptr(Collection: EdgeAttr.CorrEdgePtr)) |
308 | return "" ; |
309 | |
310 | const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S; |
311 | const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S; |
312 | |
313 | double RelDiff = statRelDiff(LeftStat, RightStat, T: EL); |
314 | return std::string(formatv(Fmt: R"({0:P})" , Vals&: RelDiff)); |
315 | } |
316 | } |
317 | |
318 | static std::string getLabel(const GraphDiffRenderer::GraphT::VertexValueType &V, |
319 | GraphDiffRenderer::StatType VL, int TrunLen) { |
320 | const auto &VertexId = V.first; |
321 | const auto &VertexAttr = V.second; |
322 | switch (VL) { |
323 | case GraphDiffRenderer::StatType::NONE: |
324 | return std::string( |
325 | formatv(Fmt: R"({0})" , Vals: truncateString(S: VertexId, n: TrunLen).str())); |
326 | default: |
327 | if (containsNullptr(Collection: VertexAttr.CorrVertexPtr)) |
328 | return std::string( |
329 | formatv(Fmt: R"({0})" , Vals: truncateString(S: VertexId, n: TrunLen).str())); |
330 | |
331 | const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S; |
332 | const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S; |
333 | |
334 | double RelDiff = statRelDiff(LeftStat, RightStat, T: VL); |
335 | return std::string(formatv( |
336 | Fmt: R"({{{0}|{1:P}})" , Vals: truncateString(S: VertexId, n: TrunLen).str(), Vals&: RelDiff)); |
337 | } |
338 | } |
339 | |
340 | static double getLineWidth(const GraphDiffRenderer::GraphT::EdgeValueType &E, |
341 | GraphDiffRenderer::StatType EL) { |
342 | auto &EdgeAttr = E.second; |
343 | switch (EL) { |
344 | case GraphDiffRenderer::StatType::NONE: |
345 | return 1.0; |
346 | default: |
347 | if (containsNullptr(Collection: EdgeAttr.CorrEdgePtr)) |
348 | return 1.0; |
349 | |
350 | const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S; |
351 | const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S; |
352 | |
353 | double RelDiff = statRelDiff(LeftStat, RightStat, T: EL); |
354 | return (RelDiff > 1.0) ? RelDiff : 1.0; |
355 | } |
356 | } |
357 | |
358 | void GraphDiffRenderer::exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel, |
359 | StatType EdgeColor, |
360 | StatType VertexLabel, |
361 | StatType VertexColor, int TruncLen) { |
362 | // Get numbering of vertices for dot output. |
363 | StringMap<int32_t> VertexNo; |
364 | |
365 | int i = 0; |
366 | for (const auto &V : G.vertices()) { |
367 | VertexNo[V.first] = i++; |
368 | } |
369 | |
370 | ColorHelper H(ColorHelper::DivergingScheme::PiYG); |
371 | |
372 | OS << "digraph xrayDiff {\n" ; |
373 | |
374 | if (VertexLabel != StatType::NONE) |
375 | OS << "node [shape=record]\n" ; |
376 | |
377 | for (const auto &E : G.edges()) { |
378 | const auto &HeadId = E.first.first; |
379 | const auto &TailId = E.first.second; |
380 | OS << formatv(Fmt: R"(F{0} -> F{1} [tooltip="{2} -> {3}" label="{4}" )" |
381 | R"(color="{5}" labelfontcolor="{5}" penwidth={6}])" |
382 | "\n" , |
383 | Vals&: VertexNo[HeadId], Vals&: VertexNo[TailId], |
384 | Vals: HeadId.empty() ? static_cast<StringRef>("F0" ) : HeadId, |
385 | Vals: TailId, Vals: getLabel(E, EL: EdgeLabel), Vals: getColor(E, G, H, T: EdgeColor), |
386 | Vals: getLineWidth(E, EL: EdgeColor)); |
387 | } |
388 | |
389 | for (const auto &V : G.vertices()) { |
390 | const auto &VertexId = V.first; |
391 | if (VertexId.empty()) { |
392 | OS << formatv(Fmt: R"(F{0} [label="F0"])" |
393 | "\n" , |
394 | Vals&: VertexNo[VertexId]); |
395 | continue; |
396 | } |
397 | OS << formatv(Fmt: R"(F{0} [label="{1}" color="{2}"])" |
398 | "\n" , |
399 | Vals&: VertexNo[VertexId], Vals: getLabel(V, VL: VertexLabel, TrunLen: TruncLen), |
400 | Vals: getColor(V, G, H, T: VertexColor)); |
401 | } |
402 | |
403 | OS << "}\n" ; |
404 | } |
405 | |
406 | template <typename T> static T &ifSpecified(T &A, cl::alias &AA, T &B) { |
407 | if (A.getPosition() == 0 && AA.getPosition() == 0) |
408 | return B; |
409 | |
410 | return A; |
411 | } |
412 | |
413 | static CommandRegistration Unused(&GraphDiff, []() -> Error { |
414 | std::array<GraphRenderer::Factory, 2> Factories{ |
415 | ._M_elems: {{.KeepGoing: ifSpecified(A&: GraphDiffKeepGoing1, AA&: GraphDiffKeepGoing1A, |
416 | B&: GraphDiffKeepGoing), |
417 | .DeduceSiblingCalls: ifSpecified(A&: GraphDiffDeduceSiblingCalls1, AA&: GraphDiffDeduceSiblingCalls1A, |
418 | B&: GraphDiffDeduceSiblingCalls), |
419 | .InstrMap: ifSpecified(A&: GraphDiffInstrMap1, AA&: GraphDiffInstrMap1A, B&: GraphDiffInstrMap), |
420 | .Trace: Trace()}, |
421 | {.KeepGoing: ifSpecified(A&: GraphDiffKeepGoing2, AA&: GraphDiffKeepGoing2A, |
422 | B&: GraphDiffKeepGoing), |
423 | .DeduceSiblingCalls: ifSpecified(A&: GraphDiffDeduceSiblingCalls2, AA&: GraphDiffDeduceSiblingCalls2A, |
424 | B&: GraphDiffDeduceSiblingCalls), |
425 | .InstrMap: ifSpecified(A&: GraphDiffInstrMap2, AA&: GraphDiffInstrMap2A, B&: GraphDiffInstrMap), |
426 | .Trace: Trace()}}}; |
427 | |
428 | std::array<std::string, 2> Inputs{._M_elems: {GraphDiffInput1, GraphDiffInput2}}; |
429 | |
430 | std::array<GraphRenderer::GraphT, 2> Graphs; |
431 | |
432 | for (int i = 0; i < 2; i++) { |
433 | auto TraceOrErr = loadTraceFile(Filename: Inputs[i], Sort: true); |
434 | if (!TraceOrErr) |
435 | return make_error<StringError>( |
436 | Args: Twine("Failed Loading Input File '" ) + Inputs[i] + "'" , |
437 | Args: make_error_code(E: llvm::errc::invalid_argument)); |
438 | Factories[i].Trace = std::move(*TraceOrErr); |
439 | |
440 | auto GraphRendererOrErr = Factories[i].getGraphRenderer(); |
441 | |
442 | if (!GraphRendererOrErr) |
443 | return GraphRendererOrErr.takeError(); |
444 | |
445 | auto GraphRenderer = *GraphRendererOrErr; |
446 | |
447 | Graphs[i] = GraphRenderer.getGraph(); |
448 | } |
449 | |
450 | GraphDiffRenderer::Factory DGF(Graphs[0], Graphs[1]); |
451 | |
452 | auto GDROrErr = DGF.getGraphDiffRenderer(); |
453 | if (!GDROrErr) |
454 | return GDROrErr.takeError(); |
455 | |
456 | auto &GDR = *GDROrErr; |
457 | |
458 | std::error_code EC; |
459 | raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF); |
460 | if (EC) |
461 | return make_error<StringError>( |
462 | Args: Twine("Cannot open file '" ) + GraphDiffOutput + "' for writing." , Args&: EC); |
463 | |
464 | GDR.exportGraphAsDOT(OS, EdgeLabel: GraphDiffEdgeLabel, EdgeColor: GraphDiffEdgeColor, |
465 | VertexLabel: GraphDiffVertexLabel, VertexColor: GraphDiffVertexColor, |
466 | TruncLen: GraphDiffVertexLabelTrunc); |
467 | |
468 | return Error::success(); |
469 | }); |
470 | |