10b57cec5SDimitry Andric //===-- xray-graph-diff.cpp: XRay Function Call Graph Renderer ------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // Generate a DOT file to represent the function call graph encountered in 100b57cec5SDimitry Andric // the trace. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric #include <cassert> 140b57cec5SDimitry Andric #include <cmath> 150b57cec5SDimitry Andric #include <limits> 160b57cec5SDimitry Andric #include <string> 170b57cec5SDimitry Andric 180b57cec5SDimitry Andric #include "xray-graph-diff.h" 190b57cec5SDimitry Andric #include "xray-graph.h" 200b57cec5SDimitry Andric #include "xray-registry.h" 210b57cec5SDimitry Andric 220b57cec5SDimitry Andric #include "xray-color-helper.h" 230b57cec5SDimitry Andric #include "llvm/Support/FormatVariadic.h" 2481ad6265SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 250b57cec5SDimitry Andric #include "llvm/XRay/Trace.h" 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric using namespace llvm; 280b57cec5SDimitry Andric using namespace xray; 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric static cl::SubCommand GraphDiff("graph-diff", 310b57cec5SDimitry Andric "Generate diff of function-call graphs"); 320b57cec5SDimitry Andric static cl::opt<std::string> GraphDiffInput1(cl::Positional, 330b57cec5SDimitry Andric cl::desc("<xray log file 1>"), 340b57cec5SDimitry Andric cl::Required, cl::sub(GraphDiff)); 350b57cec5SDimitry Andric static cl::opt<std::string> GraphDiffInput2(cl::Positional, 360b57cec5SDimitry Andric cl::desc("<xray log file 2>"), 370b57cec5SDimitry Andric cl::Required, cl::sub(GraphDiff)); 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric static cl::opt<bool> 400b57cec5SDimitry Andric GraphDiffKeepGoing("keep-going", 410b57cec5SDimitry Andric cl::desc("Keep going on errors encountered"), 420b57cec5SDimitry Andric cl::sub(GraphDiff), cl::init(false)); 430b57cec5SDimitry Andric static cl::alias GraphDiffKeepGoingA("k", cl::aliasopt(GraphDiffKeepGoing), 44480093f4SDimitry Andric cl::desc("Alias for -keep-going")); 450b57cec5SDimitry Andric static cl::opt<bool> 460b57cec5SDimitry Andric GraphDiffKeepGoing1("keep-going-1", 470b57cec5SDimitry Andric cl::desc("Keep going on errors encountered in trace 1"), 480b57cec5SDimitry Andric cl::sub(GraphDiff), cl::init(false)); 490b57cec5SDimitry Andric static cl::alias GraphDiffKeepGoing1A("k1", cl::aliasopt(GraphDiffKeepGoing1), 50480093f4SDimitry Andric cl::desc("Alias for -keep-going-1")); 510b57cec5SDimitry Andric static cl::opt<bool> 520b57cec5SDimitry Andric GraphDiffKeepGoing2("keep-going-2", 530b57cec5SDimitry Andric cl::desc("Keep going on errors encountered in trace 2"), 540b57cec5SDimitry Andric cl::sub(GraphDiff), cl::init(false)); 550b57cec5SDimitry Andric static cl::alias GraphDiffKeepGoing2A("k2", cl::aliasopt(GraphDiffKeepGoing2), 56480093f4SDimitry Andric cl::desc("Alias for -keep-going-2")); 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric static cl::opt<std::string> 590b57cec5SDimitry Andric GraphDiffInstrMap("instr-map", 600b57cec5SDimitry Andric cl::desc("binary with the instrumentation map, or " 610b57cec5SDimitry Andric "a separate instrumentation map for graph"), 620b57cec5SDimitry Andric cl::value_desc("binary with xray_instr_map or yaml"), 630b57cec5SDimitry Andric cl::sub(GraphDiff), cl::init("")); 640b57cec5SDimitry Andric static cl::alias GraphDiffInstrMapA("m", cl::aliasopt(GraphDiffInstrMap), 65480093f4SDimitry Andric cl::desc("Alias for -instr-map")); 660b57cec5SDimitry Andric static cl::opt<std::string> 670b57cec5SDimitry Andric GraphDiffInstrMap1("instr-map-1", 680b57cec5SDimitry Andric cl::desc("binary with the instrumentation map, or " 690b57cec5SDimitry Andric "a separate instrumentation map for graph 1"), 700b57cec5SDimitry Andric cl::value_desc("binary with xray_instr_map or yaml"), 710b57cec5SDimitry Andric cl::sub(GraphDiff), cl::init("")); 720b57cec5SDimitry Andric static cl::alias GraphDiffInstrMap1A("m1", cl::aliasopt(GraphDiffInstrMap1), 73480093f4SDimitry Andric cl::desc("Alias for -instr-map-1")); 740b57cec5SDimitry Andric static cl::opt<std::string> 750b57cec5SDimitry Andric GraphDiffInstrMap2("instr-map-2", 760b57cec5SDimitry Andric cl::desc("binary with the instrumentation map, or " 770b57cec5SDimitry Andric "a separate instrumentation map for graph 2"), 780b57cec5SDimitry Andric cl::value_desc("binary with xray_instr_map or yaml"), 790b57cec5SDimitry Andric cl::sub(GraphDiff), cl::init("")); 800b57cec5SDimitry Andric static cl::alias GraphDiffInstrMap2A("m2", cl::aliasopt(GraphDiffInstrMap2), 81480093f4SDimitry Andric cl::desc("Alias for -instr-map-2")); 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric static cl::opt<bool> GraphDiffDeduceSiblingCalls( 840b57cec5SDimitry Andric "deduce-sibling-calls", 850b57cec5SDimitry Andric cl::desc("Deduce sibling calls when unrolling function call stacks"), 860b57cec5SDimitry Andric cl::sub(GraphDiff), cl::init(false)); 870b57cec5SDimitry Andric static cl::alias 880b57cec5SDimitry Andric GraphDiffDeduceSiblingCallsA("d", cl::aliasopt(GraphDiffDeduceSiblingCalls), 89480093f4SDimitry Andric cl::desc("Alias for -deduce-sibling-calls")); 900b57cec5SDimitry Andric static cl::opt<bool> GraphDiffDeduceSiblingCalls1( 910b57cec5SDimitry Andric "deduce-sibling-calls-1", 920b57cec5SDimitry Andric cl::desc("Deduce sibling calls when unrolling function call stacks"), 930b57cec5SDimitry Andric cl::sub(GraphDiff), cl::init(false)); 940b57cec5SDimitry Andric static cl::alias GraphDiffDeduceSiblingCalls1A( 950b57cec5SDimitry Andric "d1", cl::aliasopt(GraphDiffDeduceSiblingCalls1), 96480093f4SDimitry Andric cl::desc("Alias for -deduce-sibling-calls-1")); 970b57cec5SDimitry Andric static cl::opt<bool> GraphDiffDeduceSiblingCalls2( 980b57cec5SDimitry Andric "deduce-sibling-calls-2", 990b57cec5SDimitry Andric cl::desc("Deduce sibling calls when unrolling function call stacks"), 1000b57cec5SDimitry Andric cl::sub(GraphDiff), cl::init(false)); 1010b57cec5SDimitry Andric static cl::alias GraphDiffDeduceSiblingCalls2A( 1020b57cec5SDimitry Andric "d2", cl::aliasopt(GraphDiffDeduceSiblingCalls2), 103480093f4SDimitry Andric cl::desc("Alias for -deduce-sibling-calls-2")); 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric static cl::opt<GraphRenderer::StatType> GraphDiffEdgeLabel( 1060b57cec5SDimitry Andric "edge-label", cl::desc("Output graphs with edges labeled with this field"), 1070b57cec5SDimitry Andric cl::value_desc("field"), cl::sub(GraphDiff), 1080b57cec5SDimitry Andric cl::init(GraphRenderer::StatType::NONE), 1090b57cec5SDimitry Andric cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", 1100b57cec5SDimitry Andric "Do not label Edges"), 1110b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::COUNT, "count", 1120b57cec5SDimitry Andric "function call counts"), 1130b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MIN, "min", 1140b57cec5SDimitry Andric "minimum function durations"), 1150b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MED, "med", 1160b57cec5SDimitry Andric "median function durations"), 1170b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::PCT90, "90p", 1180b57cec5SDimitry Andric "90th percentile durations"), 1190b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::PCT99, "99p", 1200b57cec5SDimitry Andric "99th percentile durations"), 1210b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MAX, "max", 1220b57cec5SDimitry Andric "maximum function durations"), 1230b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::SUM, "sum", 1240b57cec5SDimitry Andric "sum of call durations"))); 1250b57cec5SDimitry Andric static cl::alias GraphDiffEdgeLabelA("e", cl::aliasopt(GraphDiffEdgeLabel), 126480093f4SDimitry Andric cl::desc("Alias for -edge-label")); 1270b57cec5SDimitry Andric 1280b57cec5SDimitry Andric static cl::opt<GraphRenderer::StatType> GraphDiffEdgeColor( 1290b57cec5SDimitry Andric "edge-color", cl::desc("Output graphs with edges colored by this field"), 1300b57cec5SDimitry Andric cl::value_desc("field"), cl::sub(GraphDiff), 1310b57cec5SDimitry Andric cl::init(GraphRenderer::StatType::NONE), 1320b57cec5SDimitry Andric cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", 1330b57cec5SDimitry Andric "Do not color Edges"), 1340b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::COUNT, "count", 1350b57cec5SDimitry Andric "function call counts"), 1360b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MIN, "min", 1370b57cec5SDimitry Andric "minimum function durations"), 1380b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MED, "med", 1390b57cec5SDimitry Andric "median function durations"), 1400b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::PCT90, "90p", 1410b57cec5SDimitry Andric "90th percentile durations"), 1420b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::PCT99, "99p", 1430b57cec5SDimitry Andric "99th percentile durations"), 1440b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MAX, "max", 1450b57cec5SDimitry Andric "maximum function durations"), 1460b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::SUM, "sum", 1470b57cec5SDimitry Andric "sum of call durations"))); 1480b57cec5SDimitry Andric static cl::alias GraphDiffEdgeColorA("c", cl::aliasopt(GraphDiffEdgeColor), 149480093f4SDimitry Andric cl::desc("Alias for -edge-color")); 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric static cl::opt<GraphRenderer::StatType> GraphDiffVertexLabel( 1520b57cec5SDimitry Andric "vertex-label", 1530b57cec5SDimitry Andric cl::desc("Output graphs with vertices labeled with this field"), 1540b57cec5SDimitry Andric cl::value_desc("field"), cl::sub(GraphDiff), 1550b57cec5SDimitry Andric cl::init(GraphRenderer::StatType::NONE), 1560b57cec5SDimitry Andric cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", 1570b57cec5SDimitry Andric "Do not label Vertices"), 1580b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::COUNT, "count", 1590b57cec5SDimitry Andric "function call counts"), 1600b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MIN, "min", 1610b57cec5SDimitry Andric "minimum function durations"), 1620b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MED, "med", 1630b57cec5SDimitry Andric "median function durations"), 1640b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::PCT90, "90p", 1650b57cec5SDimitry Andric "90th percentile durations"), 1660b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::PCT99, "99p", 1670b57cec5SDimitry Andric "99th percentile durations"), 1680b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MAX, "max", 1690b57cec5SDimitry Andric "maximum function durations"), 1700b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::SUM, "sum", 1710b57cec5SDimitry Andric "sum of call durations"))); 1720b57cec5SDimitry Andric static cl::alias GraphDiffVertexLabelA("v", cl::aliasopt(GraphDiffVertexLabel), 173480093f4SDimitry Andric cl::desc("Alias for -vertex-label")); 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric static cl::opt<GraphRenderer::StatType> GraphDiffVertexColor( 1760b57cec5SDimitry Andric "vertex-color", 1770b57cec5SDimitry Andric cl::desc("Output graphs with vertices colored by this field"), 1780b57cec5SDimitry Andric cl::value_desc("field"), cl::sub(GraphDiff), 1790b57cec5SDimitry Andric cl::init(GraphRenderer::StatType::NONE), 1800b57cec5SDimitry Andric cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", 1810b57cec5SDimitry Andric "Do not color Vertices"), 1820b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::COUNT, "count", 1830b57cec5SDimitry Andric "function call counts"), 1840b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MIN, "min", 1850b57cec5SDimitry Andric "minimum function durations"), 1860b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MED, "med", 1870b57cec5SDimitry Andric "median function durations"), 1880b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::PCT90, "90p", 1890b57cec5SDimitry Andric "90th percentile durations"), 1900b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::PCT99, "99p", 1910b57cec5SDimitry Andric "99th percentile durations"), 1920b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::MAX, "max", 1930b57cec5SDimitry Andric "maximum function durations"), 1940b57cec5SDimitry Andric clEnumValN(GraphRenderer::StatType::SUM, "sum", 1950b57cec5SDimitry Andric "sum of call durations"))); 1960b57cec5SDimitry Andric static cl::alias GraphDiffVertexColorA("b", cl::aliasopt(GraphDiffVertexColor), 197480093f4SDimitry Andric cl::desc("Alias for -vertex-color")); 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric static cl::opt<int> GraphDiffVertexLabelTrunc( 2000b57cec5SDimitry Andric "vertex-label-trun", cl::desc("What length to truncate vertex labels to "), 2010b57cec5SDimitry Andric cl::sub(GraphDiff), cl::init(40)); 2020b57cec5SDimitry Andric static cl::alias 2030b57cec5SDimitry Andric GraphDiffVertexLabelTrunc1("t", cl::aliasopt(GraphDiffVertexLabelTrunc), 204480093f4SDimitry Andric cl::desc("Alias for -vertex-label-trun")); 2050b57cec5SDimitry Andric 2060b57cec5SDimitry Andric static cl::opt<std::string> 2070b57cec5SDimitry Andric GraphDiffOutput("output", cl::value_desc("Output file"), cl::init("-"), 2080b57cec5SDimitry Andric cl::desc("output file; use '-' for stdout"), 2090b57cec5SDimitry Andric cl::sub(GraphDiff)); 2100b57cec5SDimitry Andric static cl::alias GraphDiffOutputA("o", cl::aliasopt(GraphDiffOutput), 211480093f4SDimitry Andric cl::desc("Alias for -output")); 2120b57cec5SDimitry Andric 2130b57cec5SDimitry Andric Expected<GraphDiffRenderer> GraphDiffRenderer::Factory::getGraphDiffRenderer() { 2140b57cec5SDimitry Andric GraphDiffRenderer R; 2150b57cec5SDimitry Andric 2160b57cec5SDimitry Andric for (int i = 0; i < N; ++i) { 2170b57cec5SDimitry Andric const auto &G = this->G[i].get(); 2180b57cec5SDimitry Andric for (const auto &V : G.vertices()) { 2190b57cec5SDimitry Andric const auto &VAttr = V.second; 2200b57cec5SDimitry Andric R.G[VAttr.SymbolName].CorrVertexPtr[i] = &V; 2210b57cec5SDimitry Andric } 2220b57cec5SDimitry Andric for (const auto &E : G.edges()) { 2230b57cec5SDimitry Andric auto &EdgeTailID = E.first.first; 2240b57cec5SDimitry Andric auto &EdgeHeadID = E.first.second; 2250b57cec5SDimitry Andric auto EdgeTailAttrOrErr = G.at(EdgeTailID); 2260b57cec5SDimitry Andric auto EdgeHeadAttrOrErr = G.at(EdgeHeadID); 2270b57cec5SDimitry Andric if (!EdgeTailAttrOrErr) 2280b57cec5SDimitry Andric return EdgeTailAttrOrErr.takeError(); 2290b57cec5SDimitry Andric if (!EdgeHeadAttrOrErr) 2300b57cec5SDimitry Andric return EdgeHeadAttrOrErr.takeError(); 2310b57cec5SDimitry Andric GraphT::EdgeIdentifier ID{EdgeTailAttrOrErr->SymbolName, 2320b57cec5SDimitry Andric EdgeHeadAttrOrErr->SymbolName}; 2330b57cec5SDimitry Andric R.G[ID].CorrEdgePtr[i] = &E; 2340b57cec5SDimitry Andric } 2350b57cec5SDimitry Andric } 2360b57cec5SDimitry Andric 2370b57cec5SDimitry Andric return R; 2380b57cec5SDimitry Andric } 2390b57cec5SDimitry Andric // Returns the Relative change With respect to LeftStat between LeftStat 2400b57cec5SDimitry Andric // and RightStat. 2410b57cec5SDimitry Andric static double statRelDiff(const GraphDiffRenderer::TimeStat &LeftStat, 2420b57cec5SDimitry Andric const GraphDiffRenderer::TimeStat &RightStat, 2430b57cec5SDimitry Andric GraphDiffRenderer::StatType T) { 2440b57cec5SDimitry Andric double LeftAttr = LeftStat.getDouble(T); 2450b57cec5SDimitry Andric double RightAttr = RightStat.getDouble(T); 2460b57cec5SDimitry Andric 2470b57cec5SDimitry Andric return RightAttr / LeftAttr - 1.0; 2480b57cec5SDimitry Andric } 2490b57cec5SDimitry Andric 2500b57cec5SDimitry Andric static std::string getColor(const GraphDiffRenderer::GraphT::EdgeValueType &E, 2510b57cec5SDimitry Andric const GraphDiffRenderer::GraphT &G, ColorHelper H, 2520b57cec5SDimitry Andric GraphDiffRenderer::StatType T) { 2530b57cec5SDimitry Andric auto &EdgeAttr = E.second; 2540b57cec5SDimitry Andric if (EdgeAttr.CorrEdgePtr[0] == nullptr) 2550b57cec5SDimitry Andric return H.getColorString(2.0); // A number greater than 1.0 2560b57cec5SDimitry Andric if (EdgeAttr.CorrEdgePtr[1] == nullptr) 2570b57cec5SDimitry Andric return H.getColorString(-2.0); // A number less than -1.0 2580b57cec5SDimitry Andric 2590b57cec5SDimitry Andric if (T == GraphDiffRenderer::StatType::NONE) 2600b57cec5SDimitry Andric return H.getDefaultColorString(); 2610b57cec5SDimitry Andric 2620b57cec5SDimitry Andric const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S; 2630b57cec5SDimitry Andric const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S; 2640b57cec5SDimitry Andric 2650b57cec5SDimitry Andric double RelDiff = statRelDiff(LeftStat, RightStat, T); 266bdd1243dSDimitry Andric double CappedRelDiff = std::clamp(RelDiff, -1.0, 1.0); 2670b57cec5SDimitry Andric 2680b57cec5SDimitry Andric return H.getColorString(CappedRelDiff); 2690b57cec5SDimitry Andric } 2700b57cec5SDimitry Andric 2710b57cec5SDimitry Andric static std::string getColor(const GraphDiffRenderer::GraphT::VertexValueType &V, 2720b57cec5SDimitry Andric const GraphDiffRenderer::GraphT &G, ColorHelper H, 2730b57cec5SDimitry Andric GraphDiffRenderer::StatType T) { 2740b57cec5SDimitry Andric auto &VertexAttr = V.second; 2750b57cec5SDimitry Andric if (VertexAttr.CorrVertexPtr[0] == nullptr) 2760b57cec5SDimitry Andric return H.getColorString(2.0); // A number greater than 1.0 2770b57cec5SDimitry Andric if (VertexAttr.CorrVertexPtr[1] == nullptr) 2780b57cec5SDimitry Andric return H.getColorString(-2.0); // A number less than -1.0 2790b57cec5SDimitry Andric 2800b57cec5SDimitry Andric if (T == GraphDiffRenderer::StatType::NONE) 2810b57cec5SDimitry Andric return H.getDefaultColorString(); 2820b57cec5SDimitry Andric 2830b57cec5SDimitry Andric const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S; 2840b57cec5SDimitry Andric const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S; 2850b57cec5SDimitry Andric 2860b57cec5SDimitry Andric double RelDiff = statRelDiff(LeftStat, RightStat, T); 287bdd1243dSDimitry Andric double CappedRelDiff = std::clamp(RelDiff, -1.0, 1.0); 2880b57cec5SDimitry Andric 2890b57cec5SDimitry Andric return H.getColorString(CappedRelDiff); 2900b57cec5SDimitry Andric } 2910b57cec5SDimitry Andric 2920b57cec5SDimitry Andric static Twine truncateString(const StringRef &S, size_t n) { 2930b57cec5SDimitry Andric return (S.size() > n) ? Twine(S.substr(0, n)) + "..." : Twine(S); 2940b57cec5SDimitry Andric } 2950b57cec5SDimitry Andric 2960b57cec5SDimitry Andric template <typename T> static bool containsNullptr(const T &Collection) { 297fe6060f1SDimitry Andric return llvm::is_contained(Collection, nullptr); 2980b57cec5SDimitry Andric } 2990b57cec5SDimitry Andric 3000b57cec5SDimitry Andric static std::string getLabel(const GraphDiffRenderer::GraphT::EdgeValueType &E, 3010b57cec5SDimitry Andric GraphDiffRenderer::StatType EL) { 3020b57cec5SDimitry Andric auto &EdgeAttr = E.second; 3030b57cec5SDimitry Andric switch (EL) { 3040b57cec5SDimitry Andric case GraphDiffRenderer::StatType::NONE: 3050b57cec5SDimitry Andric return ""; 3060b57cec5SDimitry Andric default: 3070b57cec5SDimitry Andric if (containsNullptr(EdgeAttr.CorrEdgePtr)) 3080b57cec5SDimitry Andric return ""; 3090b57cec5SDimitry Andric 3100b57cec5SDimitry Andric const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S; 3110b57cec5SDimitry Andric const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S; 3120b57cec5SDimitry Andric 3130b57cec5SDimitry Andric double RelDiff = statRelDiff(LeftStat, RightStat, EL); 3145ffd83dbSDimitry Andric return std::string(formatv(R"({0:P})", RelDiff)); 3150b57cec5SDimitry Andric } 3160b57cec5SDimitry Andric } 3170b57cec5SDimitry Andric 3180b57cec5SDimitry Andric static std::string getLabel(const GraphDiffRenderer::GraphT::VertexValueType &V, 3190b57cec5SDimitry Andric GraphDiffRenderer::StatType VL, int TrunLen) { 3200b57cec5SDimitry Andric const auto &VertexId = V.first; 3210b57cec5SDimitry Andric const auto &VertexAttr = V.second; 3220b57cec5SDimitry Andric switch (VL) { 3230b57cec5SDimitry Andric case GraphDiffRenderer::StatType::NONE: 3245ffd83dbSDimitry Andric return std::string( 3255ffd83dbSDimitry Andric formatv(R"({0})", truncateString(VertexId, TrunLen).str())); 3260b57cec5SDimitry Andric default: 3270b57cec5SDimitry Andric if (containsNullptr(VertexAttr.CorrVertexPtr)) 3285ffd83dbSDimitry Andric return std::string( 3295ffd83dbSDimitry Andric formatv(R"({0})", truncateString(VertexId, TrunLen).str())); 3300b57cec5SDimitry Andric 3310b57cec5SDimitry Andric const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S; 3320b57cec5SDimitry Andric const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S; 3330b57cec5SDimitry Andric 3340b57cec5SDimitry Andric double RelDiff = statRelDiff(LeftStat, RightStat, VL); 3355ffd83dbSDimitry Andric return std::string(formatv( 3365ffd83dbSDimitry Andric R"({{{0}|{1:P}})", truncateString(VertexId, TrunLen).str(), RelDiff)); 3370b57cec5SDimitry Andric } 3380b57cec5SDimitry Andric } 3390b57cec5SDimitry Andric 3400b57cec5SDimitry Andric static double getLineWidth(const GraphDiffRenderer::GraphT::EdgeValueType &E, 3410b57cec5SDimitry Andric GraphDiffRenderer::StatType EL) { 3420b57cec5SDimitry Andric auto &EdgeAttr = E.second; 3430b57cec5SDimitry Andric switch (EL) { 3440b57cec5SDimitry Andric case GraphDiffRenderer::StatType::NONE: 3450b57cec5SDimitry Andric return 1.0; 3460b57cec5SDimitry Andric default: 3470b57cec5SDimitry Andric if (containsNullptr(EdgeAttr.CorrEdgePtr)) 3480b57cec5SDimitry Andric return 1.0; 3490b57cec5SDimitry Andric 3500b57cec5SDimitry Andric const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S; 3510b57cec5SDimitry Andric const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S; 3520b57cec5SDimitry Andric 3530b57cec5SDimitry Andric double RelDiff = statRelDiff(LeftStat, RightStat, EL); 3540b57cec5SDimitry Andric return (RelDiff > 1.0) ? RelDiff : 1.0; 3550b57cec5SDimitry Andric } 3560b57cec5SDimitry Andric } 3570b57cec5SDimitry Andric 3580b57cec5SDimitry Andric void GraphDiffRenderer::exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel, 3590b57cec5SDimitry Andric StatType EdgeColor, 3600b57cec5SDimitry Andric StatType VertexLabel, 3610b57cec5SDimitry Andric StatType VertexColor, int TruncLen) { 3620b57cec5SDimitry Andric // Get numbering of vertices for dot output. 3630b57cec5SDimitry Andric StringMap<int32_t> VertexNo; 3640b57cec5SDimitry Andric 3650b57cec5SDimitry Andric int i = 0; 3660b57cec5SDimitry Andric for (const auto &V : G.vertices()) { 3670b57cec5SDimitry Andric VertexNo[V.first] = i++; 3680b57cec5SDimitry Andric } 3690b57cec5SDimitry Andric 3700b57cec5SDimitry Andric ColorHelper H(ColorHelper::DivergingScheme::PiYG); 3710b57cec5SDimitry Andric 3720b57cec5SDimitry Andric OS << "digraph xrayDiff {\n"; 3730b57cec5SDimitry Andric 3740b57cec5SDimitry Andric if (VertexLabel != StatType::NONE) 3750b57cec5SDimitry Andric OS << "node [shape=record]\n"; 3760b57cec5SDimitry Andric 3770b57cec5SDimitry Andric for (const auto &E : G.edges()) { 3780b57cec5SDimitry Andric const auto &HeadId = E.first.first; 3790b57cec5SDimitry Andric const auto &TailId = E.first.second; 3800b57cec5SDimitry Andric OS << formatv(R"(F{0} -> F{1} [tooltip="{2} -> {3}" label="{4}" )" 3810b57cec5SDimitry Andric R"(color="{5}" labelfontcolor="{5}" penwidth={6}])" 3820b57cec5SDimitry Andric "\n", 3830b57cec5SDimitry Andric VertexNo[HeadId], VertexNo[TailId], 384*0fca6ea1SDimitry Andric HeadId.empty() ? static_cast<StringRef>("F0") : HeadId, 3850b57cec5SDimitry Andric TailId, getLabel(E, EdgeLabel), getColor(E, G, H, EdgeColor), 3860b57cec5SDimitry Andric getLineWidth(E, EdgeColor)); 3870b57cec5SDimitry Andric } 3880b57cec5SDimitry Andric 3890b57cec5SDimitry Andric for (const auto &V : G.vertices()) { 3900b57cec5SDimitry Andric const auto &VertexId = V.first; 391*0fca6ea1SDimitry Andric if (VertexId.empty()) { 3920b57cec5SDimitry Andric OS << formatv(R"(F{0} [label="F0"])" 3930b57cec5SDimitry Andric "\n", 3940b57cec5SDimitry Andric VertexNo[VertexId]); 3950b57cec5SDimitry Andric continue; 3960b57cec5SDimitry Andric } 3970b57cec5SDimitry Andric OS << formatv(R"(F{0} [label="{1}" color="{2}"])" 3980b57cec5SDimitry Andric "\n", 3990b57cec5SDimitry Andric VertexNo[VertexId], getLabel(V, VertexLabel, TruncLen), 4000b57cec5SDimitry Andric getColor(V, G, H, VertexColor)); 4010b57cec5SDimitry Andric } 4020b57cec5SDimitry Andric 4030b57cec5SDimitry Andric OS << "}\n"; 4040b57cec5SDimitry Andric } 4050b57cec5SDimitry Andric 4060b57cec5SDimitry Andric template <typename T> static T &ifSpecified(T &A, cl::alias &AA, T &B) { 4070b57cec5SDimitry Andric if (A.getPosition() == 0 && AA.getPosition() == 0) 4080b57cec5SDimitry Andric return B; 4090b57cec5SDimitry Andric 4100b57cec5SDimitry Andric return A; 4110b57cec5SDimitry Andric } 4120b57cec5SDimitry Andric 4130b57cec5SDimitry Andric static CommandRegistration Unused(&GraphDiff, []() -> Error { 4140b57cec5SDimitry Andric std::array<GraphRenderer::Factory, 2> Factories{ 4150b57cec5SDimitry Andric {{ifSpecified(GraphDiffKeepGoing1, GraphDiffKeepGoing1A, 4160b57cec5SDimitry Andric GraphDiffKeepGoing), 4170b57cec5SDimitry Andric ifSpecified(GraphDiffDeduceSiblingCalls1, GraphDiffDeduceSiblingCalls1A, 4180b57cec5SDimitry Andric GraphDiffDeduceSiblingCalls), 4190b57cec5SDimitry Andric ifSpecified(GraphDiffInstrMap1, GraphDiffInstrMap1A, GraphDiffInstrMap), 4200b57cec5SDimitry Andric Trace()}, 4210b57cec5SDimitry Andric {ifSpecified(GraphDiffKeepGoing2, GraphDiffKeepGoing2A, 4220b57cec5SDimitry Andric GraphDiffKeepGoing), 4230b57cec5SDimitry Andric ifSpecified(GraphDiffDeduceSiblingCalls2, GraphDiffDeduceSiblingCalls2A, 4240b57cec5SDimitry Andric GraphDiffDeduceSiblingCalls), 4250b57cec5SDimitry Andric ifSpecified(GraphDiffInstrMap2, GraphDiffInstrMap2A, GraphDiffInstrMap), 4260b57cec5SDimitry Andric Trace()}}}; 4270b57cec5SDimitry Andric 4280b57cec5SDimitry Andric std::array<std::string, 2> Inputs{{GraphDiffInput1, GraphDiffInput2}}; 4290b57cec5SDimitry Andric 4300b57cec5SDimitry Andric std::array<GraphRenderer::GraphT, 2> Graphs; 4310b57cec5SDimitry Andric 4320b57cec5SDimitry Andric for (int i = 0; i < 2; i++) { 4330b57cec5SDimitry Andric auto TraceOrErr = loadTraceFile(Inputs[i], true); 4340b57cec5SDimitry Andric if (!TraceOrErr) 4350b57cec5SDimitry Andric return make_error<StringError>( 4360b57cec5SDimitry Andric Twine("Failed Loading Input File '") + Inputs[i] + "'", 4370b57cec5SDimitry Andric make_error_code(llvm::errc::invalid_argument)); 4380b57cec5SDimitry Andric Factories[i].Trace = std::move(*TraceOrErr); 4390b57cec5SDimitry Andric 4400b57cec5SDimitry Andric auto GraphRendererOrErr = Factories[i].getGraphRenderer(); 4410b57cec5SDimitry Andric 4420b57cec5SDimitry Andric if (!GraphRendererOrErr) 4430b57cec5SDimitry Andric return GraphRendererOrErr.takeError(); 4440b57cec5SDimitry Andric 4450b57cec5SDimitry Andric auto GraphRenderer = *GraphRendererOrErr; 4460b57cec5SDimitry Andric 4470b57cec5SDimitry Andric Graphs[i] = GraphRenderer.getGraph(); 4480b57cec5SDimitry Andric } 4490b57cec5SDimitry Andric 4500b57cec5SDimitry Andric GraphDiffRenderer::Factory DGF(Graphs[0], Graphs[1]); 4510b57cec5SDimitry Andric 4520b57cec5SDimitry Andric auto GDROrErr = DGF.getGraphDiffRenderer(); 4530b57cec5SDimitry Andric if (!GDROrErr) 4540b57cec5SDimitry Andric return GDROrErr.takeError(); 4550b57cec5SDimitry Andric 4560b57cec5SDimitry Andric auto &GDR = *GDROrErr; 4570b57cec5SDimitry Andric 4580b57cec5SDimitry Andric std::error_code EC; 459fe6060f1SDimitry Andric raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF); 4600b57cec5SDimitry Andric if (EC) 4610b57cec5SDimitry Andric return make_error<StringError>( 4620b57cec5SDimitry Andric Twine("Cannot open file '") + GraphDiffOutput + "' for writing.", EC); 4630b57cec5SDimitry Andric 4640b57cec5SDimitry Andric GDR.exportGraphAsDOT(OS, GraphDiffEdgeLabel, GraphDiffEdgeColor, 4650b57cec5SDimitry Andric GraphDiffVertexLabel, GraphDiffVertexColor, 4660b57cec5SDimitry Andric GraphDiffVertexLabelTrunc); 4670b57cec5SDimitry Andric 4680b57cec5SDimitry Andric return Error::success(); 4690b57cec5SDimitry Andric }); 470