Index: test/tools/llvm-xray/X86/graph-color-simple-case.yaml =================================================================== --- test/tools/llvm-xray/X86/graph-color-simple-case.yaml +++ test/tools/llvm-xray/X86/graph-color-simple-case.yaml @@ -32,15 +32,15 @@ #EDGE: digraph xray { -#EDGE-DAG: F0 -> F7 [label="7.{{[0-9]*}}e+01" color="#B00100"]; -#EDGE-DAG: F0 -> F2 [label="2.{{[0-9]*}}e+01" color="#FD9965"]; -#EDGE-DAG: F0 -> F9 [label="9.{{[0-9]*}}e+01" color="#7F0000"]; -#EDGE-DAG: F0 -> F4 [label="4.{{[0-9]*}}e+01" color="#E8543b"]; -#EDGE-DAG: F0 -> F6 [label="6.{{[0-9]*}}e+01" color="#C5140a"]; -#EDGE-DAG: F0 -> F1 [label="1.{{[0-9]*}}e+01" color="#FDC58c"]; -#EDGE-DAG: F0 -> F8 [label="8.{{[0-9]*}}e+01" color="#990101"]; -#EDGE-DAG: F0 -> F3 [label="3.{{[0-9]*}}e+01" color="#F5744d"]; -#EDGE-DAG: F0 -> F5 [label="5.{{[0-9]*}}e+01" color="#D83323"]; +#EDGE-DAG: F0 -> F7 [label="7.{{[0-9]*}}e+01" color="#B10101"]; +#EDGE-DAG: F0 -> F2 [label="2.{{[0-9]*}}e+01" color="#FC9862"]; +#EDGE-DAG: F0 -> F9 [label="9.{{[0-9]*}}e+01" color="#7E0000"]; +#EDGE-DAG: F0 -> F4 [label="4.{{[0-9]*}}e+01" color="#E9553D"]; +#EDGE-DAG: F0 -> F6 [label="6.{{[0-9]*}}e+01" color="#C5130A"]; +#EDGE-DAG: F0 -> F1 [label="1.{{[0-9]*}}e+01" color="#FDC58E"]; +#EDGE-DAG: F0 -> F8 [label="8.{{[0-9]*}}e+01" color="#9A0000"]; +#EDGE-DAG: F0 -> F3 [label="3.{{[0-9]*}}e+01" color="#F5744E"]; +#EDGE-DAG: F0 -> F5 [label="5.{{[0-9]*}}e+01" color="#D83321"]; #EDGE-DAG: F7 [label="@(7)"]; #EDGE-DAG: F2 [label="@(2)"]; #EDGE-DAG: F9 [label="@(9)"]; @@ -63,13 +63,13 @@ #VERTEX-DAG: F0 -> F8 [label=""]; #VERTEX-DAG: F0 -> F3 [label=""]; #VERTEX-DAG: F0 -> F5 [label=""]; -#VERTEX-DAG: F7 [label="{@(7)|7.{{[0-9]*}}e+01}" color="#B00100"]; -#VERTEX-DAG: F2 [label="{@(2)|2.{{[0-9]*}}e+01}" color="#FD9965"]; -#VERTEX-DAG: F9 [label="{@(9)|9.{{[0-9]*}}e+01}" color="#7F0000"]; -#VERTEX-DAG: F4 [label="{@(4)|4.{{[0-9]*}}e+01}" color="#E8543b"]; -#VERTEX-DAG: F6 [label="{@(6)|6.{{[0-9]*}}e+01}" color="#C5140a"]; -#VERTEX-DAG: F1 [label="{@(1)|1.{{[0-9]*}}e+01}" color="#FDC58c"]; -#VERTEX-DAG: F8 [label="{@(8)|8.{{[0-9]*}}e+01}" color="#990101"]; -#VERTEX-DAG: F3 [label="{@(3)|3.{{[0-9]*}}e+01}" color="#F5744d"]; -#VERTEX-DAG: F5 [label="{@(5)|5.{{[0-9]*}}e+01}" color="#D83323"]; +#VERTEX-DAG: F7 [label="{@(7)|7.{{[0-9]*}}e+01}" color="#B10101"]; +#VERTEX-DAG: F2 [label="{@(2)|2.{{[0-9]*}}e+01}" color="#FC9862"]; +#VERTEX-DAG: F9 [label="{@(9)|9.{{[0-9]*}}e+01}" color="#7E0000"]; +#VERTEX-DAG: F4 [label="{@(4)|4.{{[0-9]*}}e+01}" color="#E9553D"]; +#VERTEX-DAG: F6 [label="{@(6)|6.{{[0-9]*}}e+01}" color="#C5130A"]; +#VERTEX-DAG: F1 [label="{@(1)|1.{{[0-9]*}}e+01}" color="#FDC58E"]; +#VERTEX-DAG: F8 [label="{@(8)|8.{{[0-9]*}}e+01}" color="#9A0000"]; +#VERTEX-DAG: F3 [label="{@(3)|3.{{[0-9]*}}e+01}" color="#F5744E"]; +#VERTEX-DAG: F5 [label="{@(5)|5.{{[0-9]*}}e+01}" color="#D83321"]; #VERTEX-NEXT: } Index: tools/llvm-xray/CMakeLists.txt =================================================================== --- tools/llvm-xray/CMakeLists.txt +++ tools/llvm-xray/CMakeLists.txt @@ -9,10 +9,12 @@ set(LLVM_XRAY_TOOLS func-id-helper.cc xray-account.cc + xray-color-helper.cc xray-converter.cc xray-extract.cc xray-extract.cc xray-graph.cc + xray-graph-diff.cc xray-registry.cc) add_llvm_tool(llvm-xray llvm-xray.cc ${LLVM_XRAY_TOOLS}) Index: tools/llvm-xray/xray-color-helper.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-color-helper.h @@ -0,0 +1,65 @@ +//===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A class to get a color from a specified gradient. +// +//===----------------------------------------------------------------------===// + +#ifndef XRAY_COLOR_HELPER_H +#define XRAY_COLOR_HELPER_H + +#include "llvm/ADT/ArrayRef.h" + +namespace llvm{ +namespace xray{ + +class ColorHelper{ + private: + ArrayRef RedPoly; + ArrayRef GreenPoly; + ArrayRef BluePoly; + double MinIn; + double MaxIn; + + public: + enum class SequentialScheme{ + Blues, + Greens, + Greys, + Oranges, + Purples, + Reds, + BuGn, + BuPu, + GnBu, + OrRd, + PuBu, + PuBuGn, + PuRd, + RdPu, + YlGn, + YlGnBu, + YlOrBr, + YlOrRd + }; + + ColorHelper(SequentialScheme S); + + enum class DivergingScheme{ +BrBG, PiYG,PRGn, PuOr, RdBu, RdGy, RdYlBu, RdYlGn, +Spectral + }; + + ColorHelper(DivergingScheme S); + std::string getColor(double Point); +}; + +} +} +#endif Index: tools/llvm-xray/xray-color-helper.cc =================================================================== --- /dev/null +++ tools/llvm-xray/xray-color-helper.cc @@ -0,0 +1,488 @@ +//===-- xray-graph.cc - XRay Function Call Graph Renderer -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A class to get a color from a specified gradient. +// +//===----------------------------------------------------------------------===// +#include + +#include "xray-color-helper.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace xray; + +// Static Polynomials for the ColorHelper. +// +// In order to calculate these polynomials, +// 1. Convert the color scheme samples from sRGB to LAB color space. +// 2. Interpolate between the descrete colors in LAB space using a cubic +// spline interpolation. +// 3. Sample this interpolation at 100 points and convert to sRGB. +// 4. Calculate a polynomial fit for these 100 points for each of R G and B. +// We used a polynomial of varying based on the lowest degree polynomial +// having an error less than 1 part in 200. +// 5. Extract these polynomial coefficients from matlab as a set of constants. +// +// Sequential Color Gradient Polynomials. +// Polynomials based on the Blues color scheme from http://colorbrewer2.org/ +const static double BluesRedPoly[] = {-162.9192458,661.1363283,-1073.973166,883.6280368,-380.2475179, + 78.82940273,-6.801561636,-0.5835807573,0.9638753883}; +const static double BluesGreenPoly[] = {12.51361058,-44.07245353,59.38257571,-36.98884162,9.739164654,-0.8614140648, + -0.506944739,0.9859862907}; +const static double BluesBluePoly[] = {78.05122204,-327.7360452,562.5737804,-509.6864294, + 261.0703167,-74.65580898,10.6377515,-0.8401556996,1.005358902}; + +// Polynomials based on the Greens color scheme from http://colorbrewer2.org/ +const static double GreensRedPoly[] = {128509.879,-797823.2846,2.181834797e6,-3.461790283e6,3.534081369e6, + -2.434410039e6,1.154529311e6,-377805.7923,84179.93116,-12388.36474,1140.686431, + -58.23333701,-0.599401113,-0.3391213896,0.9686205448}; +const static double GreensGreenPoly[] = {-7.461951182,21.39595578,-22.84864083,11.23029325,-3.096687569,0.0646894028, + 0.9845938142}; +const static double GreensBluePoly[] = {-107.7554773,433.5238732,-698.2664276,574.4323065,-256.9205948, + 63.64251157,-9.433399058,-0.07598437676,0.9571862056}; + +// Polynomials based on the Greys color scheme from http://colorbrewer2.org/ +const static double GreysRedPoly[] = {-143.5297649,616.5085865,-1070.840946,962.8673283,-476.4855833,127.9805002, + -18.05029511,0.5613221758,0.9901358241}; +const static double GreysGreenPoly[] = {-143.52956,616.5077415,-1070.839522,962.8660699,-476.4849604,127.9803288, + -18.05027,0.5613209065,0.9901351029}; +const static double GreysBluePoly[] = {-143.5294769,616.5074019,-1070.838957,962.8655805,-476.4847253,127.9802671, + -18.05026156,0.5613206095,0.9901347147}; + +// Polynomials based on the Oranges color scheme from http://colorbrewer2.org/ +const static double OrangesRedPoly[] = {-334.8939504,1523.76056,-2877.815926,2928.537768,-1739.551397,607.1101648, + -118.9429881,11.80374269,-0.5137251743,1.005104319}; +const static double OrangesGreenPoly[] = {-42.83615389,143.6574611,-190.3001089,126.6399211,-43.1204304,5.886082421, + -0.7453255295,0.9636056027}; +const static double OrangesBluePoly[] = {-1710.17825,8893.802623,-19585.32854,23759.7782, + -17303.83263,7728.682269,-2073.560697,316.1050081,-26.47997137,0.1130416623, + 0.9153319099}; + +// Polynomials based on the Purples color scheme from http://colorbrewer2.org/ +const static double PurplesRedPoly[] = {-1.696564207,2.884062273,-0.2668357693,-1.489785313, + -0.1753312058,0.9839885824}; +const static double PurplesGreenPoly[] = {2012.965183,-9732.786019,19786.37572,-21967.31599, + 14491.92446,-5806.24908,1394.521531,-193.2258101,13.62438311,-0.8184148389, + 0.9869814287}; +const static double PurplesBluePoly[] = {190.8592861,-989.1880383,2113.220988,-2408.075381,1579.79904, + -599.5450876,125.4220815,-13.32705929,0.334271261,0.986341708}; + +// Polynomials based on the Reds color scheme from http://colorbrewer2.org/ +const static double RedsRedPoly[] = {1098.113616,-4997.251688,9488.138801,-9772.899613,5959.992777,-2209.219326, + 489.0341315,-59.61204391,3.179287412,-0.06669132569,1.000264016}; +const static double RedsGreenPoly[] = {179.5116774,-740.4193895,1219.571844,-1023.549681,462.3195296,-107.1715527, + 9.686352106,-0.9214068996,0.9683767274}; +const static double RedsBluePoly[] = {18.04407896,-68.20215528,102.9001432,-81.5837095,38.48503892,-10.45114102, + -0.07604611136,0.9370323879}; + +// Polynomials based on the OrRed color scheme from http://colorbrewer2.org/ +const static double BuGnRedPoly[] = {-2164.121047,10419.83248,-21150.30331, + 23256.8391,-14583.15966,4763.232503,-373.8566794,-221.1728408,56.11368279, + -3.859531726,-0.5223079228,0.9666711244}; +const static double BuGnGreenPoly[] = {575.2892884,-2681.538786,5175.083266,-5337.088998,3164.275758,-1078.38952, + 199.2966703,-18.08674004,0.4437484859,0.9810499143}; +const static double BuGnBluePoly[] = {41.26409578,-208.352963,399.5400205,-377.152878,187.8052313,-47.71547797, + 3.89067026,-0.1757733931,0.9964244108}; + +// Polynomials based on the BuPu color scheme from http://colorbrewer2.org/ +const static double BuPuRedPoly[] = {565.1449932,-2592.662888,4929.107756,-5014.635769,2939.25807,-999.5456659, + 193.2527746,-20.8497299,0.2708141304,0.9618238442}; +const static double BuPuGreenPoly[] = {847746.7759,-6.117330635e6,1.980683427e7,-3.801842895e7,4.815808516e7, + -4.241669623e7,2.667200228e7,-1.210496666e7,3.962733596e6,-924923.9906,150108.4084, + -16213.24967,1091.247763,-43.49969562,0.4921382396,0.9858848647}; +const static double BuPuBluePoly[] = {49.38780299,-172.295529,234.7533785,-158.3305372,55.00594638,-9.538295383, + 0.3295111754,0.9872924674}; + +// Polynomials based on the GnBu color scheme from http://colorbrewer2.org/ +const static double GnBuRedPoly[] = {32122.60422,-208773.9722,595945.0471, + -983116.3372,1.03802487e6,-734274.9475,354313.1124,-116680.0393,25866.28121, + -3735.817486,321.1547783,-12.18046992,-0.7143529713,0.9669925893}; +const static double GnBuGreenPoly[] = {-8.471893402,-14.3835949,107.8888603,-167.8783294,117.9995112,-42.403984, + 7.241547581,-0.7406211535,0.9926692269}; +const static double GnBuBluePoly[] = {-5011.063942,28498.03802,-69014.48035,92768.62373,-75707.3449,38652.14984, + -12342.84564,2418.81187,-278.2257073,17.00442399,-1.102980359,0.9439447156}; + +// Polynomials based on the OrRed color scheme from http://colorbrewer2.org/ +const static double OrRdRedPoly[] = {-9.741081771,26.59107248,-23.08764657,3.674199917,3.177273882,-1.208968693, + 0.09313645804,0.996555062}; +const static double OrRdGreenPoly[] = {2117.201282,-11230.55463,25067.4414,-30620.1269, + 22288.62892,-9866.377435,2600.647555,-384.3063815,27.68833123,-1.222129796, + 0.9753227379}; +const static double OrRdBluePoly[] = {42863.44555,-331773.5807,1.122972762e6,-2.190041522e6, + 2.726232049e6,-2.265546873e6,1.276524286e6,-485437.6249,121842.3618,-19383.46884, + 1843.423347,-97.52978299,1.350944135,0.9160833744}; + +// Polynomials based on the PuBu color scheme from http://colorbrewer2.org/ +const static double PuBuRedPoly[] = {-397.2314636, 2579.027278, -6876.412443, 10369.26972, -10415.08411, +7749.526816, -4381.051265, 1768.220022, -456.6488373, 64.95443074, +-5.170425306, -0.3872077729, 0.9989333378}; +const static double PuBuGreenPoly[] = {316.2837758, -1386.633741, 2496.773059, -2390.527123, 1323.209576, \ +-435.7961010, 86.11515238, -10.20656272, 0.03642902561, 0.9654562574}; +const static double PuBuBluePoly[] = {29.76240711, -85.94054297, 82.86745772, -19.60045636, -17.19015772, \ +12.66262712, -3.154522976, -0.03853118536, 0.9822950378}; + +// Polynomials based on the PuBuGn color scheme from http://colorbrewer2.org/ +const static double PuBuGnRedPoly[] = {2.092770724e8,-1.338302115e9,3.300134529e9,-3.357656294e9,-2.210526994e8, + 2.925967695e9,-4.120649617e8,-2.612491973e9,5.776442214e8,2.418823238e9, + -9.021859524e8,-2.127317508e9,1.72129189e9,1.079792855e9,-2.665242736e9, + 2.211843331e9,-1.107249356e9,3.738089198e8,-8.795629308e7,1.444528513e7, + -1.623785122e6,119930.5996,-5425.567499,132.1422988,-1.869649136,1.003397947}; +const static double PuBuGnGreenPoly[] = {-133.6970724,709.8725846,-1540.388566,1773.645168,-1178.548252,461.8248624, + -105.1656276,13.1007346,-1.339592315,0.973770807}; +const static double PuBuGnBluePoly[] = {-28187.2393,166072.0474,-425260.2581,620686.1779,-569382.9389,341689.8288, + -135511.682,35113.62424,-5750.706781,558.6236132,-28.50250673,0.2525093956, + 0.9810974095}; + +// Polynomials based on the PuRd color scheme from http://colorbrewer2.org/ +const static double PuRdRedPoly[] = {-2202.762217,11435.69335,-25248.4439,30784.28254, + -22492.44399,9995.426253,-2631.605551,388.1227397,-29.25020442,0.4190472575, + 0.9623440461}; +const static double PuRdGreenPoly[] = {142210.2277,-894659.1155,2.480558555e6,-3.990708815e6, + 4.128435582e6,-2.87652607e6,1.374434749e6,-449663.6624,98764.9872,-13992.74716, + 1205.672796,-61.4587279,1.14907402,0.9520059636}; +const static double PuRdBluePoly[] = {-3379.723288,20105.05624,-51052.20095,72460.36139,-63269.02362,35271.2722, + -12606.97534,2811.356145,-361.9132726,21.61621714,-0.6808906907,0.9814418093}; + +// Polynomials based on the RdPu color scheme from http://colorbrewer2.org/ +const static double RdPuRedPoly[] = {117.4834462,-513.5812302,906.139353,-820.4867199,402.5265722,-105.8782576, + 13.86141359,-0.7870456274,1.007063795}; +const static double RdPuGreenPoly[] = {-5.596279513e7,4.4073891e8,-1.525131874e9,2.987670582e9,-3.459933872e9, + 1.951051377e9,6.09936545e8,-2.292082693e9,2.311280607e9,-1.414858186e9, + 5.852420154e8,-1.652812489e8,3.021181863e7,-2.850393141e6,-86063.08,62960.25987, + -8192.206251,517.9372802,-16.07700273,-0.5076354515,0.9680424486}; +const static double RdPuBluePoly[] = {-45307.28817,270852.2235,-705295.8823,1.048999574e6,-982011.9283,601276.0493, + -242511.3671,63412.64183,-10369.51324,1010.704944,-56.70435084,0.9523501774, + 0.947747698}; + +// Polynomials based on the YlGn color scheme from http://colorbrewer2.org/ +const static double YlGnRedPoly[] = {-153078.3428,1.316295606e6,-4.957612048e6,1.087139478e7, + -1.551710885e7,1.523865765e7,-1.059953714e7,5.291105253e6,-1.898459493e6, + 485301.7326,-86614.36088,10415.09059,-790.7659441,30.34434264,-0.4563433465, + 1.001513624}; +const static double YlGnGreenPoly[] = {456.6173455,-2077.672779,3904.419251,-3917.824313,2268.493273, + -766.0210486,146.1498685,-15.53011837,0.6447190637,0.9939487551}; +const static double YlGnBluePoly[] = {331.0907176,-1422.525203,2506.606949,-2334.566721,1230.819201,-357.9510683, + 45.46460358,2.231195424,-1.903112721,0.8976786982}; + +// Polynomials based on the YlGnBu color scheme from http://colorbrewer2.org/ +const static double YlGnBuRedPoly[] = {-700.2088464,1373.925269,3246.038198,-14121.86699,21000.52579,-16939.73725, + 8084.534973,-2260.39398,342.4561731,-26.57841229,0.3370952789,0.9940301899}; +const static double YlGnBuGreenPoly[] = {592.8478812,-2579.282889,4586.50152,-4269.260281,2210.212644,-621.5322226, + 84.78432376,-5.030640902,-0.1192375895,0.9997904251}; +const static double YlGnBuBluePoly[] = {891.171196,-3972.475184,7308.058168,-7162.505665,4021.255532,-1284.591053, + 208.5762183,-8.259653287,-1.725683675,0.8451676342}; + +// Polynomials based on the YlOrBr color scheme from http://colorbrewer2.org/ +const static double YlOrBrRedPoly[] = {82.17794157,-328.8039801,535.3925617,-449.1168762,201.9412562,-47.0938933, + 5.121928704,-0.2159704436,1.002044464}; +const static double YlOrBrGreenPoly[] = {-0.1279957404,-3.303062964,8.856052316,-6.229222692,0.1706702785,-0.2251488049, + 1.003713704}; +const static double YlOrBrBluePoly[] = {-7097.65965,38202.9136,-87458.95413,110683.6665,-84199.16695, + 39197.02668,-10907.37228,1724.577297,-153.6107682,9.392195243,-1.690001786, + 0.897572307}; + +// Polynomials based on the YlOrRd color scheme from http://colorbrewer2.org/ +const static double YlOrRdRedPoly[] = {347.4416582,-1671.400143,3330.173175,-3551.794875, + 2190.15113,-789.8171836,160.9250929,-16.90546624,0.7331100217,0.9926020022}; +const static double YlOrRdGreenPoly[] = {42050.25699,-254007.3256,669529.9051,-1.009925101e6,960811.4641,-599256.311, + 246929.7118,-66241.02715,11170.03542,-1122.538702,62.08145931,-2.154000337, + 1.00678571}; +const static double YlOrRdBluePoly[] = {-1526.934295,8539.978183,-20130.51964,25980.76253,-19958.39911, + 9290.827185,-2556.416755,389.372372,-28.73642255,-0.5748168267,0.792483576}; + +ColorHelper::ColorHelper(ColorHelper::SequentialScheme S){ + MinIn = 0.0; + MaxIn = 1.0; + switch (S){ + case SequentialScheme::Blues: + RedPoly = BluesRedPoly; + GreenPoly = BluesGreenPoly; + BluePoly = BluesBluePoly; + break; + case SequentialScheme::Greens: + RedPoly = GreensRedPoly; + GreenPoly = GreensGreenPoly; + BluePoly = GreensBluePoly; + break; + case SequentialScheme::Greys : + RedPoly = GreysRedPoly; + GreenPoly = GreysGreenPoly; + BluePoly = GreysBluePoly; + break; + case SequentialScheme::Oranges : + RedPoly = OrangesRedPoly; + GreenPoly = OrangesGreenPoly; + BluePoly = OrangesBluePoly; + break; + case SequentialScheme::Purples : + RedPoly = PurplesRedPoly; + GreenPoly = PurplesGreenPoly; + BluePoly = PurplesBluePoly; + break; + case SequentialScheme::Reds : + RedPoly = RedsRedPoly; + GreenPoly = RedsGreenPoly; + BluePoly = RedsBluePoly; + break; + case SequentialScheme::BuGn : + RedPoly = BuGnRedPoly; + GreenPoly = BuGnGreenPoly; + BluePoly = BuGnBluePoly; + break; + case SequentialScheme::BuPu : + RedPoly = BuPuRedPoly; + GreenPoly = BuPuGreenPoly; + BluePoly = BuPuBluePoly; + break; + case SequentialScheme::GnBu : + RedPoly = GnBuRedPoly; + GreenPoly = GnBuGreenPoly; + BluePoly = GnBuBluePoly; + break; + case SequentialScheme::OrRd: + RedPoly = OrRdRedPoly; + GreenPoly = OrRdGreenPoly; + BluePoly = OrRdBluePoly; + break; + case SequentialScheme::PuBu : + RedPoly = PuBuRedPoly; + GreenPoly = PuBuGreenPoly; + BluePoly = PuBuBluePoly; + break; + case SequentialScheme::PuBuGn : + RedPoly = PuBuGnRedPoly; + GreenPoly = PuBuGnGreenPoly; + BluePoly = PuBuGnBluePoly; + break; + case SequentialScheme::PuRd : + RedPoly = PuRdRedPoly; + GreenPoly = PuRdGreenPoly; + BluePoly = PuRdBluePoly; + break; + case SequentialScheme::RdPu : + RedPoly = RdPuRedPoly; + GreenPoly = RdPuGreenPoly; + BluePoly = RdPuBluePoly; + break; + case SequentialScheme::YlGn : + RedPoly = YlGnRedPoly; + GreenPoly = YlGnGreenPoly; + BluePoly = YlGnBluePoly; + break; + case SequentialScheme::YlGnBu : + RedPoly = YlGnBuRedPoly; + GreenPoly = YlGnBuGreenPoly; + BluePoly = YlGnBuBluePoly; + break; + case SequentialScheme::YlOrBr : + RedPoly = YlOrBrRedPoly; + GreenPoly = YlOrBrGreenPoly; + BluePoly = YlOrBrBluePoly; + break; + case SequentialScheme::YlOrRd : + RedPoly = YlOrRdRedPoly; + GreenPoly = YlOrRdGreenPoly; + BluePoly = YlOrRdBluePoly; + break; + } +} + +//Diverging Polynomials +// Polynomials based on the BrBG color scheme from http://colorbrewer2.org/ +const static double BrBGRedPoly[]{0.5294837763,-3.91737974,-2.002268846,10.1174594,2.010320887, + -9.757575762,-0.1256734439,5.045304415,-0.1253220524,-2.289568173,-0.4521919584, + 0.9583654395}; +const static double BrBGGreenPoly[]{2.373263075,-0.2616693205,-7.195755845,0.9219509198,7.596904178, + -1.132609927,-2.666893375,0.4951814708,-0.851476127,0.00100428138,0.9561152078}; +const static double BrBGBluePoly[]{-0.5740589404,-51.80933673,5.933606842,190.8711475,-17.60515002,-280.2254106, + 23.70627555,208.0906477,-15.53074134,-82.67647274,4.232398336,18.51393498, + -0.4210568904,-3.624562285,0.343325181,0.9556677359}; + +// Polynomials based on the PiYG color scheme from http://colorbrewer2.org/ +const static double PiYGRedPoly[]{2.686315367,0.1818066989,-6.820061012,-0.6240347033,5.764823487, + 1.044017662,-1.500696055,-0.5968454935,-0.743017334,-0.2061763511,0.9741208999}; +const static double PiYGGreenPoly[]{25.82837592,-478.0082591,265.405487,1958.953608,-1322.827865,-3278.514666, + 2295.320802,2888.256833,-1994.993957,-1440.005462,937.8839818,404.6402812, + -230.1148382,-58.76139211,23.44055027,3.37044763,0.535250268,0.05330372069, + -1.240930319,0.2010813874,0.9678130494}; +const static double PiYGBluePoly[]{6.566648214,-4.259643591,-19.97981922,13.4961515,23.17940053,-16.71986064, + -13.50696717,9.90021622,5.309834643,-2.299206712,-2.319199867,-0.233534209, + 0.964818028}; + +// Polynomials based on the PRGn color scheme from http://colorbrewer2.org/ +const static double PRGnRedPoly[]{-5.037268137,7.310676883,18.89034365,-21.3298236, + -28.20969519,23.80097291,21.21189418,-13.26329632,-7.779385768,4.799471123, + 0.9626697387,-2.146239347,-0.1632212632,0.9627271485}; +const static double PRGnGreenPoly[]{27.70376302,-39.1403529,-101.9551342,141.663995,152.6578233,-204.820855, + -120.9124305,151.7675811,54.38831045,-61.20849002,-13.22680327,13.30089839, + 1.231517631,-2.397363546,0.2459120541,0.9661768459}; +const static double PRGnBluePoly[]{0.2890233005,-46.53351847,-3.390212366,170.4163679,9.940905807,-248.563189, + -12.74136855,183.5702915,7.705418595,-72.68456748,-1.611488339,15.84437642, + -0.08662683146,-2.821513911,-0.2000382851,0.9640579906}; + +// Polynomials based on the PuOr color scheme from http://colorbrewer2.org/ +const static double PuOrRedPoly[]{-3.681026454,7.825731699,11.18480902,-23.10450029,-12.02383864,25.40973649, + 4.896892791,-12.64537306,-0.1389082968,2.891324118,-0.0278193421,-0.995118471, + -0.3720954744,0.9628512429}; +const static double PuOrGreenPoly[]{24.01042545,-28.43205999,-134.1576955,105.9071849, + 294.2538664,-161.8152166,-333.0003126,131.2457398,212.5680161,-59.69920063, + -78.00057893,14.09735835,16.48496014,-1.395557329,-3.010405741,-0.0247322439, + 0.9664721897}; +const static double PuOrBluePoly[]{38.74145857,-70.25593327,-150.8970078,264.4538625,233.2417893, + -395.9786502,-181.1930748,299.0183416,74.78498955,-120.5154649,-16.81392037, + 26.92436846,1.793936342,-4.454840269,0.4744096931,0.9634054758}; + +// Polynomials based on the RdBu color scheme from http://colorbrewer2.org/ +const static double RdBuRedPoly[]{6.936194186,1.308207573,-17.85152386,-3.505585415,14.92660355,2.424503364, + -4.399570266,0.5513789831,1.013046025,-0.5583414845,-1.369433672,-0.412403019, + 0.9637843928}; +const static double RdBuGreenPoly[]{582.8393444,-488.9961581,-2707.709964,2151.793689,5414.088282, + -3938.663025,-6173.86839,3888.488885,4493.193764,-2242.273173,-2200.279444, + 758.7630922,730.7208378,-139.6230898,-158.0436507,9.971104545,21.06121406, + 0.5612577187,-2.870266159,0.06627786897,0.9681621401}; +const static double RdBuBluePoly[]{-11.53462595,9.69685208,42.06073501,-30.67405539,-60.12471234,38.24650971, + 42.08578575,-24.89881773,-14.91463228,9.703917704,2.216675589,-2.779351146, + 0.3385093241,0.9628023114}; + +// Polynomials based on the RdGy color scheme from http://colorbrewer2.org/ +const static double RdGyRedPoly[]{-35.77587586,7.874688034,130.7208297, + -27.63317133,-189.8258592,37.51705049,138.8359798,-24.2554428,-53.55141233, + 7.504333242,10.57755037,-0.9022304474,-1.727339517,-0.2545271441,0.9931484001}; +const static double RdGyGreenPoly[]{940.7677018,-420.7050933,-4603.913055,1812.820476,9740.153737,-3230.273468, + -11738.58697,3084.710995,8935.33313,-1713.150687,-4487.738645,561.6920218, + 1499.120736,-103.9671121,-325.1759042,8.579319324,43.66350008,0.3122312879, + -4.565733055,0.02734696098,0.9990447212}; +const static double RdGyBluePoly[]{150.9666995,1.888055114,-646.4507888,-12.40890595,1150.057089,30.82431888, + -1101.085242,-38.91140127,614.7676222,27.3123262,-203.4289258,-10.49240337, + 39.69025184,1.577865733,-5.394577624,0.1998944399,0.9955666392}; + +// Polynomials based on the RdYlBu color scheme from http://colorbrewer2.org/ +const static double RdYlBuRedPoly[]{5.75293564,-3.470298281,-19.4898014,10.52563062,25.83595957,-12.41156083, + -16.88926037,7.37045594,5.8493945,-2.023140922,-1.632715936,-0.2222877025, + 0.9958204171}; +const static double RdYlBuGreenPoly[]{-26.23751254,-2.132653763,99.04038378,6.078051972,-149.3173043, + -6.993327891,114.4727289,4.548900204,-47.14603532,-2.06476332,10.60885391, + 0.5000541272,-2.315902649,0.1693628426,0.9964815152}; +const static double RdYlBuBluePoly[]{-190.0990285,-10.36353267,814.4602394,46.22896242,-1442.882524,-87.21048396, + 1362.064988,91.26331869,-734.0940148,-58.43685625,223.422648,23.59237473, + -34.69009059,-6.080855872,1.425385656,1.224450206,0.7522655595}; + +// Polynomials based on the RdYlGn color scheme from http://colorbrewer2.org/ +const static double RdYlGnRedPoly[]{200.1308503,104.7864437,-843.3602568,-451.1701694,1451.412696,791.4594203, + -1315.236836,-730.5758596,676.6792519,384.0517159,-199.7640908,-116.5912773, + 33.30645382,19.79505982,-3.219914894,-2.429571625,-0.271093805,0.9948888983}; +const static double RdYlGnGreenPoly[]{-0.6581236191,12.78677277,0.9598185188,-39.87472293,-0.04836970727,48.08354771, + 0.08244671772,-28.40787812,-0.9735039293,8.949936926,0.7215694489,-2.319990233, + 0.1201688537,0.9934969619}; +const static double RdYlGnBluePoly[]{-10.63946244,216.0611234,41.17394797,-926.5998391, + -61.07837796,1655.034126,40.12657909,-1598.09658,-6.252193922,906.8493934, + -5.846296915,-307.9753277,2.470443253,61.02816517,0.0858668193,-6.8526992, + -0.007549403018,0.7427008373}; + +// Polynomials based on the Spectral color scheme from http://colorbrewer2.org/ +const static double SpectralRedPoly[]{-5.542248719,-2.308143637,16.06408619,6.145137891,-17.77435106, + -5.566391809,10.21182246,2.564522594,-2.957557806,-1.336926237,-0.1287516798, + 0.9984221365}; +const static double SpectralGreenPoly[]{6.373851354,-5.048892087,-22.28985269,13.87132947,29.8636573, + -12.55479535,-19.47972158,3.7477125,6.785884212,-0.06703107824,-2.087938334, + 0.1949553685,0.9957989692}; +const static double SpectralBluePoly[]{-735.9799141,44.50209142,3631.528714,-177.4034756, + -7619.078878,275.5098917,8845.528239,-196.9908882,-6187.050799,43.43148149, + 2655.16715,26.04923949,-681.1680745,-19.40374992,98.09305618,4.51219457, + -7.343834502,-0.01703847884,0.7461285097}; + +ColorHelper::ColorHelper(ColorHelper::DivergingScheme S){ + MinIn = -1.0; + MaxIn = 1.0; + switch (S){ + case DivergingScheme::BrBG: + RedPoly = BrBGRedPoly; + GreenPoly = BrBGGreenPoly; + BluePoly = BrBGBluePoly; + break; + case DivergingScheme::PiYG: + RedPoly = PiYGRedPoly; + GreenPoly = PiYGGreenPoly; + BluePoly = PiYGBluePoly; + break; + case DivergingScheme::PRGn: + RedPoly = PRGnRedPoly; + GreenPoly = PRGnGreenPoly; + BluePoly = PRGnBluePoly; + break; + case DivergingScheme::PuOr: + RedPoly = PuOrRedPoly; + GreenPoly = PuOrGreenPoly; + BluePoly = PuOrBluePoly; + break; + case DivergingScheme::RdBu: + RedPoly = RdBuRedPoly; + GreenPoly = RdBuGreenPoly; + BluePoly = RdBuBluePoly; + break; + case DivergingScheme::RdGy: + RedPoly = RdGyRedPoly; + GreenPoly = RdGyGreenPoly; + BluePoly = RdGyBluePoly; + break; + case DivergingScheme::RdYlBu: + RedPoly = RdYlBuRedPoly; + GreenPoly = RdYlBuGreenPoly; + BluePoly = RdYlBuBluePoly; + break; + case DivergingScheme::RdYlGn: + RedPoly = RdYlGnRedPoly; + GreenPoly = RdYlGnGreenPoly; + BluePoly = RdYlGnBluePoly; + break; + case DivergingScheme::Spectral: + RedPoly = SpectralRedPoly; + GreenPoly = SpectralGreenPoly; + BluePoly = SpectralBluePoly; + break; + } +} + +// Evaluates a polynomial given the coefficints provided in an ArrayRef +// evaluating: +// +// p(x) = a[n-0]*x^0 + a[n-1]*x^1 + ... a[n-n]*x^n +// +// at x_0 using Horner's Method for both performance and stability reasons. +static double polyEval(ArrayRef a, double x_0) { + double B = 0; + for (const auto &c : a) { + B = c + B * x_0; + } + return B; +} + +// Takes a double precision number, clips it between 0 and 1 and then converts +// that to an integer between 0x00 and 0xFF with proxpper rounding. +static uint8_t uintIntervalTo8bitChar(double B) { + double n = std::max(std::min(B, 1.0), 0.0); + return static_cast(255 * n + 0.5); +} + +// Gets a color in a gradient given a number in the interval [0,1], it does this +// by evaluating a polynomial which maps [0, 1] -> [0, 1] for each of the R G +// and B values in the color. It then converts this [0,1] colors to a 24 bit +// color. +std::string ColorHelper::getColor(double point) { + point = std::max(std::min(point, MaxIn), MinIn); + + uint8_t r = uintIntervalTo8bitChar(polyEval(RedPoly, point)); + uint8_t g = uintIntervalTo8bitChar(polyEval(GreenPoly, point)); + uint8_t b = uintIntervalTo8bitChar(polyEval(BluePoly, point)); + + return llvm::formatv("#{0:X-2}{1:X-2}{2:X-2}", r, g, b); +} + Index: tools/llvm-xray/xray-graph-diff.h =================================================================== --- /dev/null +++ tools/llvm-xray/xray-graph-diff.h @@ -0,0 +1,82 @@ +//===-- xray-graph-diff.h - XRay Function Call Graph Renderer ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Generate a DOT file to represent the function call graph encountered in +// the trace. +// +//===----------------------------------------------------------------------===// + +#ifndef XRAY_GRAPH_DIFF_H +#define XRAY_GRAPH_DIFF_H + +#include "xray-graph.h" +#include "llvm/XRay/Graph.h" +#include "llvm/ADT/StringMap.h" + +namespace llvm { +namespace xray { + +class GraphDiffRenderer{ + public: + using StatType = GraphRenderer::StatType; + using TimeStat = GraphRenderer::TimeStat; + + struct EdgeAttribute { + const GraphRenderer::GraphT::EdgeValueType *A = nullptr; + const GraphRenderer::GraphT::EdgeValueType *B = nullptr; + }; + + struct VertexAttribute { + const GraphRenderer::GraphT::VertexValueType *A = nullptr; + const GraphRenderer::GraphT::VertexValueType *B = nullptr; + }; + + class GraphT : public Graph{ + public: + std::pair EdgeMaxDiff = {{},{}}; + std::pair VertexMaxDiff = {{},{}}; + }; + + class Factory{ + const GraphRenderer::GraphT &G1; + const GraphRenderer::GraphT &G2; + public: + Factory(const GraphRenderer::GraphT &_G1, + const GraphRenderer::GraphT &_G2): G1(_G1), G2(_G2){}; + + Expected getGraphDiffRenderer(); + }; + + + private: + GraphT G; + StringMap IM; + + GraphDiffRenderer() = default; + + void updateEdgeDiff(const TimeStat & A,const TimeStat& B); + void updateVertexDiff(const TimeStat &A,const TimeStat& B); + + public: + void exportGraphAsDOT(raw_ostream &OS, + StatType EdgeLabel = StatType::NONE, + StatType EdgeColor = StatType::NONE, + StatType VertexLabel = StatType::NONE, + StatType VertexColor = StatType::NONE); + + const GraphT &getGraph(){ + return G; + }; +}; + +} +} + + +#endif Index: tools/llvm-xray/xray-graph-diff.cc =================================================================== --- /dev/null +++ tools/llvm-xray/xray-graph-diff.cc @@ -0,0 +1,495 @@ +//===-- xray-graph-diff.cc - XRay Function Call Graph Renderer ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Generate a DOT file to represent the function call graph encountered in +// the trace. +// +//===----------------------------------------------------------------------===// +#include +#include + +#include "xray-graph.h" +#include "xray-graph-diff.h" +#include "xray-registry.h" + +#include "xray-color-helper.h" +#include "llvm/XRay/Trace.h" + +using namespace llvm; +using namespace xray; + +static cl::SubCommand GraphDiff("graph-diff", + "Generate diff of function-call graphs"); +static cl::opt GraphDiffInput1(cl::Positional, + cl::desc(""), + cl::Required, cl::sub(GraphDiff)); +static cl::opt GraphDiffInput2(cl::Positional, + cl::desc(""), + cl::Required, cl::sub(GraphDiff)); + +static cl::opt + GraphDiffKeepGoing("keep-going", + cl::desc("Keep going on errors encountered"), + cl::sub(GraphDiff), + cl::init(false)); +static cl::alias + GraphDiffKeepGoingA("k", cl::aliasopt(GraphDiffKeepGoing), + cl::desc("Alias for -keep-going"), + cl::sub(GraphDiff)); +static cl::opt + GraphDiffKeepGoing1("keep-going-1", + cl::desc("Keep going on errors encountered in trace 1"), + cl::sub(GraphDiff), + cl::init(false)); +static cl::alias + GraphDiffKeepGoing1A("k1", cl::aliasopt(GraphDiffKeepGoing1), + cl::desc("Alias for -keep-going-1"), + cl::sub(GraphDiff)); +static cl::opt + GraphDiffKeepGoing2("keep-going-2", + cl::desc("Keep going on errors encountered in trace 2"), + cl::sub(GraphDiff), + cl::init(false)); +static cl::alias + GraphDiffKeepGoing2A("k2", cl::aliasopt(GraphDiffKeepGoing2), + cl::desc("Alias for -keep-going-2"), + cl::sub(GraphDiff)); + +static cl::opt + GraphDiffInstrMap("instr-map", + cl::desc("binary with the instrumentation map, or " + "a separate instrumentation map for graph"), + cl::value_desc("binary with xray_instr_map or yaml"), + cl::sub(GraphDiff), cl::init("")); +static cl::alias + GraphDiffInstrMapA("m", cl::aliasopt(GraphDiffInstrMap), + cl::desc("Alias for -instr-map"), + cl::sub(GraphDiff)); +static cl::opt + GraphDiffInstrMap1("instr-map-1", + cl::desc("binary with the instrumentation map, or " + "a separate instrumentation map for graph 1"), + cl::value_desc("binary with xray_instr_map or yaml"), + cl::sub(GraphDiff), cl::init("")); +static cl::alias + GraphDiffInstrMap1A("m1", cl::aliasopt(GraphDiffInstrMap1), + cl::desc("Alias for -instr-map-1"), + cl::sub(GraphDiff)); +static cl::opt + GraphDiffInstrMap2("instr-map-2", + cl::desc("binary with the instrumentation map, or " + "a separate instrumentation map for graph 2"), + cl::value_desc("binary with xray_instr_map or yaml"), + cl::sub(GraphDiff), cl::init("")); +static cl::alias + GraphDiffInstrMap2A("m2", cl::aliasopt(GraphDiffInstrMap2), + cl::desc("Alias for -instr-map-2"), + cl::sub(GraphDiff)); + +static cl::opt GraphDiffDeduceSiblingCalls( + "deduce-sibling-calls", + cl::desc("Deduce sibling calls when unrolling function call stacks"), + cl::sub(GraphDiff), cl::init(false)); +static cl::alias + GraphDiffDeduceSiblingCallsA("d", cl::aliasopt(GraphDiffDeduceSiblingCalls), + cl::desc("Alias for -deduce-sibling-calls"), + cl::sub(GraphDiff)); +static cl::opt GraphDiffDeduceSiblingCalls1( + "deduce-sibling-calls-1", + cl::desc("Deduce sibling calls when unrolling function call stacks"), + cl::sub(GraphDiff), cl::init(false)); +static cl::alias + GraphDiffDeduceSiblingCalls1A("d1", cl::aliasopt(GraphDiffDeduceSiblingCalls1), + cl::desc("Alias for -deduce-sibling-calls-1"), + cl::sub(GraphDiff)); +static cl::opt GraphDiffDeduceSiblingCalls2( + "deduce-sibling-calls-2", + cl::desc("Deduce sibling calls when unrolling function call stacks"), + cl::sub(GraphDiff), cl::init(false)); +static cl::alias + GraphDiffDeduceSiblingCalls2A("d2", cl::aliasopt(GraphDiffDeduceSiblingCalls2), + cl::desc("Alias for -deduce-sibling-calls-2"), + cl::sub(GraphDiff)); + +static cl::opt + GraphDiffEdgeLabel("edge-label", + cl::desc("Output graphs with edges labeled with this field"), + cl::value_desc("field"), cl::sub(GraphDiff), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not label Edges"), + clEnumValN(GraphRenderer::StatType::COUNT, + "count", "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias + GraphDiffEdgeLabelA("e", cl::aliasopt(GraphDiffEdgeLabel), + cl::desc("Alias for -edge-label"), + cl::sub(GraphDiff)); + +static cl::opt GraphDiffVertexLabel( + "vertex-label", + cl::desc("Output graphs with vertices labeled with this field"), + cl::value_desc("field"), cl::sub(GraphDiff), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not label Vertices"), + clEnumValN(GraphRenderer::StatType::COUNT, "count", + "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias + GraphDiffVertexLabelA("v", cl::aliasopt(GraphDiffVertexLabel), + cl::desc("Alias for -vertex-label"), + cl::sub(GraphDiff)); + +static cl::opt GraphDiffEdgeColorType( + "color-edges", + cl::desc("Output graphs with edge colors determined by this field"), + cl::value_desc("field"), cl::sub(GraphDiff), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not color Edges"), + clEnumValN(GraphRenderer::StatType::COUNT, "count", + "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias + GraphDiffEdgeColorTypeA("c", cl::aliasopt(GraphDiffEdgeColorType), + cl::desc("Alias for -color-edges"), + cl::sub(GraphDiff)); + + +static cl::opt GraphDiffVertexColorType( + "color-vertices", + cl::desc("Output graphs with vertex colors determined by this field"), + cl::value_desc("field"), cl::sub(GraphDiff), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not color vertices"), + clEnumValN(GraphRenderer::StatType::COUNT, "count", + "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias + GraphDiffVertexColorTypeA("b", cl::aliasopt(GraphDiffVertexColorType), + cl::desc("Alias for -color-vertices"), + cl::sub(GraphDiff)); + +static cl::opt + GraphDiffOutput("output", cl::value_desc("Output file"), cl::init("-"), + cl::desc("output file; use '-' for stdout"), cl::sub(GraphDiff)); +static cl::alias + GraphDiffOutputA("o", cl::aliasopt(GraphDiffOutput), + cl::desc("Alias for -output"), + cl::sub(GraphDiff)); + +Expected GraphDiffRenderer::Factory::getGraphDiffRenderer(){ + GraphDiffRenderer R; + + for (const auto &V : G1.vertices()) + R.G[V.second.SymbolName].A = &V; + + for (const auto &V : G2.vertices()) + R.G[V.second.SymbolName].B = &V; + + for (const auto &E : G1.edges()){ + auto SOrErr = G1.at(E.first.first); + auto EOrErr = G1.at(E.first.second); + if(!SOrErr) + return SOrErr.takeError(); + if(!EOrErr) + return EOrErr.takeError(); + GraphDiffRenderer::GraphT::EdgeIdentifier I + {SOrErr->SymbolName, + EOrErr->SymbolName}; + R.G[I].A = &E; + } + + for (const auto &E : G2.edges()){ + auto SOrErr = G2.at(E.first.first); + auto EOrErr = G2.at(E.first.second); + if(!SOrErr) + return SOrErr.takeError(); + if(!EOrErr) + return EOrErr.takeError(); + GraphDiffRenderer::GraphT::EdgeIdentifier I + {SOrErr->SymbolName, + EOrErr->SymbolName}; + R.G[I].B = &E; + } + + int i = 0; + for (const auto &V : R.G.vertices()){ + R.IM[V.first] = i++; + + if (V.second.A == nullptr || V.second.B == nullptr) + continue; + + R.updateVertexDiff(V.second.A->second.S, V.second.B->second.S); + } + for (const auto &E : R.G.edges()){ + if(E.second.A == nullptr || E.second.B == nullptr) + continue; + R.updateEdgeDiff(E.second.A->second.S, E.second.B->second.S); + } + + return R; +} +static double divF(double A, double B){ + return A/B - B/A; +} +static std::pair maxDiff(double AA, double AB,double BA,double BB){ + if(AA == 0.0 || AB == 0.0 || std::abs(divF(AA, AB)) < std::abs(divF(BA, BB))) + return {BA,BB}; + return {AA, AB}; +} +static std::pair maxDiff(int64_t AA, int64_t AB, int64_t BA, int64_t BB){ + if(AA == 0 || AB == 0 || std::abs(divF(AA, AB)) < std::abs(divF(BA,BB))) + return {BA, BB}; + return {AA, AB}; +} + +using TimeStat = GraphDiffRenderer::TimeStat; + +static void updateMaxDiff(TimeStat &MA, TimeStat &MB, + const TimeStat &A, const TimeStat &B){ + auto IP = maxDiff(MA.Count, MB.Count, A.Count, B.Count); + MA.Count = IP.first; + MB.Count = IP.second; + auto DP = maxDiff(MA.Min, MB.Min, A.Min, B.Min); + MA.Min = DP.first; + MB.Min = DP.second; + DP = maxDiff(MA.Median, MB.Median, A.Median, B.Median); + MA.Median = DP.first; + MA.Median = DP.second; + DP = maxDiff(MA.Pct90, MB.Pct90, A.Pct90, B.Pct90); + MA.Pct90 = DP.first; + MB.Pct90 = DP.second; + DP = maxDiff(MA.Pct99, MB.Pct99, A.Pct99, B.Pct99); + MA.Pct99 = DP.first; + MB.Pct99 = DP.second; + DP = maxDiff(MA.Max, MB.Max, A.Max, B.Max); + MA.Max = DP.first; + MB.Max = DP.second; + DP = maxDiff(MA.Sum, MB.Sum, A.Sum, B.Sum); + MA.Sum = DP.first; + MB.Sum = DP.first; +} + +void GraphDiffRenderer::updateEdgeDiff(const TimeStat &A, + const TimeStat &B){ + updateMaxDiff(G.EdgeMaxDiff.first, G.EdgeMaxDiff.second, A, B); +} + +void GraphDiffRenderer::updateVertexDiff(const TimeStat &A, + const TimeStat &B){ + updateMaxDiff(G.VertexMaxDiff.first, G.VertexMaxDiff.second, A, B); +} + +static std::string getColor(const GraphDiffRenderer::GraphT::EdgeValueType &E, + const GraphDiffRenderer::GraphT &G, + ColorHelper H, + GraphDiffRenderer::StatType T){ + if (E.second.A == nullptr) + return "green"; + if (E.second.B == nullptr) + return "red"; + + if (T == GraphDiffRenderer::StatType::NONE) + return "black"; + double R = divF(E.second.A->second.S.getDouble(T), + E.second.B->second.S.getDouble(T)); + double MR = std::abs(divF(G.EdgeMaxDiff.first.getDouble(T), + G.EdgeMaxDiff.second.getDouble(T))); + if (MR == 0.0 && R == 0.0){ + MR = 1.0; + } + return H.getColor(copysign(std::sqrt(std::abs(R/MR)), R)); +} + +static std::string getColor(const GraphDiffRenderer::GraphT::VertexValueType &E, + const GraphDiffRenderer::GraphT &G, + ColorHelper H, + GraphDiffRenderer::StatType T){ + if (E.second.A == nullptr) + return "green"; + if (E.second.B == nullptr) + return "red"; + + if (T == GraphDiffRenderer::StatType::NONE) + return "black"; + + double R = divF(E.second.A->second.S.getDouble(T), + E.second.B->second.S.getDouble(T)); + + + double MR = divF(G.VertexMaxDiff.first.getDouble(T), + G.VertexMaxDiff.second.getDouble(T)); + + return H.getColor(copysign(std::sqrt(std::abs(R/MR)), R)); +} + +static Twine trunS(const StringRef &S, size_t n){ + return (S.size() > n) ? Twine(S.substr(0, n)) + "..." + : Twine(S); +} + +void GraphDiffRenderer::exportGraphAsDOT(raw_ostream &OS, + StatType EdgeLabel, + StatType EdgeColor, + StatType VertexLabel, + StatType VertexColor){ + ColorHelper H(ColorHelper::DivergingScheme::PiYG); + + OS << "digraph xray {\n"; + + if (VertexLabel != StatType::NONE) + OS << "node [shape=record];\n"; + + for (const auto &E : G.edges()) { + OS << "F" << IM[E.first.first] << " -> " + << "F" << IM[E.first.second] + << " [tooltip=\"" << trunS(E.first.first, 20) << " -> " + << trunS(E.first.second,20) << "\"" + << " color=\"" << getColor(E, G, H, EdgeColor) << "\"]\n"; + } + + for (const auto &V : G.vertices()) { + if (V.first.equals("")) { + OS << "F" << IM[V.first] << " [label=\"F0\"]"; + continue; + } + OS << "F" << IM[V.first] << " [label=\"" << trunS(V.first, 40) << "\"" + << " color=\"" << getColor(V, G, H, VertexColor) << "\"];\n"; + } + OS << "}\n"; +}; + +template +static T &ifSpecified(T &A, cl::alias &AA, T &B){ + if(A.getPosition() == 0 && AA.getPosition() == 0){ + return B; + } + return A; +} + +static CommandRegistration Unused(&GraphDiff, []() -> Error { + GraphRenderer::Factory F1; + GraphRenderer::Factory F2; + + F1.KeepGoing = ifSpecified(GraphDiffKeepGoing1, + GraphDiffKeepGoing1A, + GraphDiffKeepGoing); + F2.KeepGoing = ifSpecified(GraphDiffKeepGoing2, + GraphDiffKeepGoing2A, + GraphDiffKeepGoing); + + F1.DeduceSiblingCalls = ifSpecified(GraphDiffDeduceSiblingCalls1, + GraphDiffDeduceSiblingCalls1A, + GraphDiffDeduceSiblingCalls); + F2.DeduceSiblingCalls = ifSpecified(GraphDiffDeduceSiblingCalls2, + GraphDiffDeduceSiblingCalls2A, + GraphDiffDeduceSiblingCalls); + + F1.InstrMap = ifSpecified(GraphDiffInstrMap1, + GraphDiffInstrMap1A, + GraphDiffInstrMap); + F2.InstrMap = ifSpecified(GraphDiffInstrMap2, + GraphDiffInstrMap2A, + GraphDiffInstrMap); + + auto Trace1OrErr = loadTraceFile(GraphDiffInput1,true); + auto Trace2OrErr = loadTraceFile(GraphDiffInput2,true); + + if(!Trace1OrErr) + return make_error(Twine("Failed Loading Input File '") + + GraphDiffInput1 + "'", + make_error_code(llvm::errc::invalid_argument)); + if(!Trace2OrErr) + return make_error(Twine("Failed Loading Input File '") + + GraphDiffInput2 + "'", + make_error_code(llvm::errc::invalid_argument)); + + F1.Trace = std::move(*Trace1OrErr); + F2.Trace = std::move(*Trace2OrErr); + + auto GR1OrErr = F1.getGraphRenderer(); + auto GR2OrErr = F2.getGraphRenderer(); + + if (!GR1OrErr) + return GR1OrErr.takeError(); + if (!GR2OrErr) + return GR2OrErr.takeError(); + + auto &GR1 = *GR1OrErr; + auto &GR2 = *GR2OrErr; + + auto &G1 = GR1.getGraph(); + auto &G2 = GR2.getGraph(); + + GraphDiffRenderer::Factory DGF(G1, G2); + + auto GDROrErr = DGF.getGraphDiffRenderer(); + if(!GDROrErr) + return GDROrErr.takeError(); + + auto &GDR = *GDROrErr; + + std::error_code EC; + raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::F_Text); + if (EC) + return make_error( + Twine("Cannot open file '") + GraphDiffOutput + "' for writing.", EC); + + GDR.exportGraphAsDOT(OS, GraphDiffEdgeLabel, GraphDiffEdgeColorType, + GraphDiffVertexLabel, GraphDiffVertexColorType); + + return Error::success(); +}); Index: tools/llvm-xray/xray-graph.h =================================================================== --- tools/llvm-xray/xray-graph.h +++ tools/llvm-xray/xray-graph.h @@ -19,6 +19,7 @@ #include #include "func-id-helper.h" +#include "xray-color-helper.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Errc.h" @@ -40,17 +41,29 @@ /// An inner struct for common timing statistics information struct TimeStat { - uint64_t Count = 0; - double Min = 0; - double Median = 0; - double Pct90 = 0; - double Pct99 = 0; - double Max = 0; - double Sum = 0; - std::string getAsString(StatType T) const; - double compare(StatType T, const TimeStat &Other) const; + int64_t Count; + double Min; + double Median; + double Pct90; + double Pct99; + double Max; + double Sum; + + TimeStat(): TimeStat(0,0,0,0,0,0,0){}; + TimeStat(int64_t _Count, + double _Min, + double _Median, + double _Pct90, + double _Pct99, + double _Max, + double _Sum): + Count(_Count), Min(_Min), Median(_Median), Pct90(_Pct90), Pct99(_Pct99), + Max(_Max), Sum(_Sum){} + + std::string getString(StatType T) const; + double getDouble(StatType T) const; }; - typedef uint64_t TimestampT; + using TimestampT = uint64_t; /// An inner struct for storing edge attributes for our graph. Here the /// attributes are mainly function call statistics. @@ -75,10 +88,10 @@ uint64_t TSC; }; - typedef SmallVector FunctionStack; + using FunctionStack = SmallVector; - typedef DenseMap - PerThreadFunctionStackMap; + using PerThreadFunctionStackMap = + DenseMap; class GraphT : public Graph { public: @@ -87,8 +100,8 @@ }; GraphT G; - typedef typename decltype(G)::VertexIdentifier VertexIdentifier; - typedef typename decltype(G)::EdgeIdentifier EdgeIdentifier; + using VertexIdentifier = typename decltype(G)::VertexIdentifier; + using EdgeIdentifier = decltype(G)::EdgeIdentifier; /// Use a Map to store the Function stack for each thread whilst building the /// graph. @@ -97,7 +110,7 @@ PerThreadFunctionStackMap PerThreadFunctionStack; /// Usefull object for getting human readable Symbol Names. - FuncIdConversionHelper &FuncIdHelper; + FuncIdConversionHelper FuncIdHelper; bool DeduceSiblingCalls = false; TimestampT CurrentMaxTSC = 0; @@ -117,11 +130,17 @@ /// Normalises latency statistics for each edge and vertex by CycleFrequency; void normalizeStatistics(double CycleFrequency); + /// An object to color gradients + ColorHelper CHelper; + public: /// Takes in a reference to a FuncIdHelper in order to have ready access to /// Symbol names. - explicit GraphRenderer(FuncIdConversionHelper &FuncIdHelper, bool DSC) - : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC) { + explicit GraphRenderer(const FuncIdConversionHelper &FuncIdHelper, + bool DSC) + : FuncIdHelper(FuncIdHelper), + DeduceSiblingCalls(DSC), + CHelper(ColorHelper::SequentialScheme::OrRd) { G[0] = {}; } @@ -138,20 +157,109 @@ return PerThreadFunctionStack; } + class Factory{ + public: + bool KeepGoing; + bool DeduceSiblingCalls; + std::string InstrMap; + Trace Trace; + Expected getGraphRenderer(); + }; + /// Output the Embedded graph in DOT format on \p OS, labeling the edges by /// \p T - void exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H, + void exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel = StatType::NONE, StatType EdgeColor = StatType::NONE, StatType VertexLabel = StatType::NONE, StatType VertexColor = StatType::NONE); /// Get a reference to the internal graph. const GraphT &getGraph() { - calculateEdgeStatistics(); - calculateVertexStatistics(); return G; } }; + +/// Vector Sum of TimeStats +inline GraphRenderer::TimeStat operator+(const GraphRenderer::TimeStat &A, + const GraphRenderer::TimeStat &B){ + return {A.Count + B.Count, + A.Min + B.Min, + A.Median + B.Median, + A.Pct90 + B.Pct90, + A.Pct99 + B.Pct99, + A.Max + B.Max, + A.Sum + B.Sum}; +} + +/// Vector Difference of Timestats +inline GraphRenderer::TimeStat operator-(const GraphRenderer::TimeStat &A, + const GraphRenderer::TimeStat &B){ + + return {A.Count - B.Count, + A.Min - B.Min, + A.Median - B.Median, + A.Pct90 - B.Pct90, + A.Pct99 - B.Pct99, + A.Max - B.Max, + A.Sum - B.Sum}; +} + +/// Scalar Diference of TimeStat and double +inline GraphRenderer::TimeStat operator/(const GraphRenderer::TimeStat &A, + double B){ + + return {static_cast(A.Count / B), + A.Min / B, + A.Median / B, + A.Pct90 / B, + A.Pct99 / B, + A.Max / B, + A.Sum / B}; +} + +/// Scalar product of TimeStat and Double +inline GraphRenderer::TimeStat operator*(const GraphRenderer::TimeStat &A, + double B){ + return {static_cast(A.Count * B), + A.Min * B, + A.Median * B, + A.Pct90 * B, + A.Pct99 * B, + A.Max * B, + A.Sum * B}; +} + +/// Scalar product of double TimeStat +inline GraphRenderer::TimeStat operator*(double A, + const GraphRenderer::TimeStat &B){ + return B * A; +} + +///Hadamard Product of TimeStats +inline GraphRenderer::TimeStat operator*(const GraphRenderer::TimeStat &A, + const GraphRenderer::TimeStat &B){ + return {A.Count * B.Count, + A.Min * B.Min, + A.Median * B.Median, + A.Pct90 * B.Pct90, + A.Pct99 * B.Pct99, + A.Max * B.Max, + A.Sum * B.Sum}; +} + +/// Hadamard Division of TimeStats +inline GraphRenderer::TimeStat operator/(const GraphRenderer::TimeStat &A, + const GraphRenderer::TimeStat &B){ + return {A.Count * B.Count, + A.Min * B.Min, + A.Median * B.Median, + A.Pct90 * B.Pct90, + A.Pct99 * B.Pct99, + A.Max * B.Max, + A.Sum * B.Sum}; + +} + } } Index: tools/llvm-xray/xray-graph.cc =================================================================== --- tools/llvm-xray/xray-graph.cc +++ tools/llvm-xray/xray-graph.cc @@ -19,7 +19,6 @@ #include "xray-graph.h" #include "xray-registry.h" -#include "llvm/ADT/ArrayRef.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/XRay/InstrumentationMap.h" @@ -96,7 +95,7 @@ cl::value_desc("field"), cl::sub(GraphC), cl::init(GraphRenderer::StatType::NONE), cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", - "Do not label Edges"), + "Do not label Vertices"), clEnumValN(GraphRenderer::StatType::COUNT, "count", "function call counts"), clEnumValN(GraphRenderer::StatType::MIN, "min", @@ -121,7 +120,7 @@ cl::value_desc("field"), cl::sub(GraphC), cl::init(GraphRenderer::StatType::NONE), cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", - "Do not label Edges"), + "Do not color Edges"), clEnumValN(GraphRenderer::StatType::COUNT, "count", "function call counts"), clEnumValN(GraphRenderer::StatType::MIN, "min", @@ -146,7 +145,7 @@ cl::value_desc("field"), cl::sub(GraphC), cl::init(GraphRenderer::StatType::NONE), cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", - "Do not label Edges"), + "Do not color vertices"), clEnumValN(GraphRenderer::StatType::COUNT, "count", "function call counts"), clEnumValN(GraphRenderer::StatType::MIN, "min", @@ -208,7 +207,7 @@ auto &ThreadStack = PerThreadFunctionStack[Record.TId]; switch (Record.Type) { case RecordTypes::ENTER: { - if (G.count(Record.FuncId) == 0) + if (Record.FuncId != 0 && G.count(Record.FuncId) == 0) G[Record.FuncId].SymbolName = FuncIdHelper.SymbolOrNumber(Record.FuncId); ThreadStack.push_back({Record.FuncId, Record.TSC}); break; @@ -314,12 +313,9 @@ // TimeStat element. static void normalizeTimeStat(GraphRenderer::TimeStat &S, double CycleFrequency) { - S.Min /= CycleFrequency; - S.Median /= CycleFrequency; - S.Max /= CycleFrequency; - S.Sum /= CycleFrequency; - S.Pct90 /= CycleFrequency; - S.Pct99 /= CycleFrequency; + int64_t OldCount = S.Count; + S = S / CycleFrequency; + S.Count = OldCount; } // Normalises the statistics in the graph for a given TSC frequency. @@ -339,7 +335,7 @@ // Returns a string containing the value of statistic field T std::string -GraphRenderer::TimeStat::getAsString(GraphRenderer::StatType T) const { +GraphRenderer::TimeStat::getString(GraphRenderer::StatType T) const { std::string St; raw_string_ostream S{St}; switch (T) { @@ -370,95 +366,37 @@ return S.str(); } -// Evaluates a polynomial given the coefficints provided in an ArrayRef -// evaluating: -// -// p(x) = a[n-0]*x^0 + a[n-1]*x^1 + ... a[n-n]*x^n -// -// at x_0 using Horner's Method for both performance and stability reasons. -static double polyEval(ArrayRef a, double x_0) { - double B = 0; - for (const auto &c : a) { - B = c + B * x_0; - } - return B; -} - -// Takes a double precision number, clips it between 0 and 1 and then converts -// that to an integer between 0x00 and 0xFF with proxpper rounding. -static uint8_t uintIntervalTo8bitChar(double B) { - double n = std::max(std::min(B, 1.0), 0.0); - return static_cast(255 * n + 0.5); -} - -// Gets a color in a gradient given a number in the interval [0,1], it does this -// by evaluating a polynomial which maps [0, 1] -> [0, 1] for each of the R G -// and B values in the color. It then converts this [0,1] colors to a 24 bit -// color. -// -// In order to calculate these polynomials, -// 1. Convert the OrRed9 color scheme from http://colorbrewer2.org/ from sRGB -// to LAB color space. -// 2. Interpolate between the descrete colors in LAB space using a cubic -// spline interpolation. -// 3. Sample this interpolation at 100 points and convert to sRGB. -// 4. Calculate a polynomial fit for these 100 points for each of R G and B. -// We used a polynomial of degree 9 arbitrarily based on a fuzzy goodness -// of fit metric (using human judgement); -// 5. Extract these polynomial coefficients from matlab as a set of constants. -static std::string getColor(double point) { - assert(point >= 0.0 && point <= 1); - const static double RedPoly[] = {-38.4295, 239.239, -600.108, 790.544, - -591.26, 251.304, -58.0983, 6.62999, - -0.325899, 1.00173}; - const static double GreenPoly[] = {-603.634, 2338.15, -3606.74, 2786.16, - -1085.19, 165.15, 11.2584, -6.11338, - -0.0091078, 0.965469}; - const static double BluePoly[] = {-325.686, 947.415, -699.079, -513.75, - 1127.78, -732.617, 228.092, -33.8202, - 0.732108, 0.913916}; - - uint8_t r = uintIntervalTo8bitChar(polyEval(RedPoly, point)); - uint8_t g = uintIntervalTo8bitChar(polyEval(GreenPoly, point)); - uint8_t b = uintIntervalTo8bitChar(polyEval(BluePoly, point)); - - return llvm::formatv("#{0:X-2}{1:X-2}{2:x-2}", r, g, b); -} - // Returns the quotient between the property T of this and another TimeStat as // a double -double GraphRenderer::TimeStat::compare(StatType T, const TimeStat &O) const { +double GraphRenderer::TimeStat::getDouble(StatType T) const { double retval = 0; switch (T) { case GraphRenderer::StatType::COUNT: - retval = static_cast(Count) / static_cast(O.Count); + retval = static_cast(Count); break; case GraphRenderer::StatType::MIN: - retval = Min / O.Min; + retval = Min; break; case GraphRenderer::StatType::MED: - retval = Median / O.Median; + retval = Median; break; case GraphRenderer::StatType::PCT90: - retval = Pct90 / O.Pct90; + retval = Pct90; break; case GraphRenderer::StatType::PCT99: - retval = Pct99 / O.Pct99; + retval = Pct99; break; case GraphRenderer::StatType::MAX: - retval = Max / O.Max; + retval = Max; break; case GraphRenderer::StatType::SUM: - retval = Sum / O.Sum; + retval = Sum; break; case GraphRenderer::StatType::NONE: retval = 0.0; break; } - return std::sqrt( - retval); // the square root here provides more dynamic contrast for - // low runtime edges, giving better separation and - // coloring lower down the call stack. + return retval; } // Outputs a DOT format version of the Graph embedded in the GraphRenderer @@ -467,17 +405,9 @@ // annotations. // // FIXME: output more information, better presented. -void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H, +void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, StatType ET, StatType EC, StatType VT, StatType VC) { - G.GraphEdgeMax = {}; - G.GraphVertexMax = {}; - calculateEdgeStatistics(); - - calculateVertexStatistics(); - if (H.CycleFrequency) - normalizeStatistics(H.CycleFrequency); - OS << "digraph xray {\n"; if (VT != StatType::NONE) @@ -486,9 +416,9 @@ for (const auto &E : G.edges()) { const auto &S = E.second.S; OS << "F" << E.first.first << " -> " - << "F" << E.first.second << " [label=\"" << S.getAsString(ET) << "\""; + << "F" << E.first.second << " [label=\"" << S.getString(ET) << "\""; if (EC != StatType::NONE) - OS << " color=\"" << getColor(S.compare(EC, G.GraphEdgeMax)) << "\""; + OS << " color=\"" << CHelper.getColor(std::sqrt(S.getDouble(EC)/G.GraphEdgeMax.getDouble(EC))) << "\""; OS << "];\n"; } @@ -501,25 +431,17 @@ << (VA.SymbolName.size() > 40 ? VA.SymbolName.substr(0, 40) + "..." : VA.SymbolName); if (VT != StatType::NONE) - OS << "|" << VA.S.getAsString(VT) << "}\""; + OS << "|" << VA.S.getString(VT) << "}\""; else OS << "\""; if (VC != StatType::NONE) - OS << " color=\"" << getColor(VA.S.compare(VC, G.GraphVertexMax)) << "\""; + OS << " color=\"" << CHelper.getColor(std::sqrt(VA.S.getDouble(VC)/G.GraphVertexMax.getDouble(VC))) << "\""; OS << "];\n"; } OS << "}\n"; } -// Here we register and implement the llvm-xray graph subcommand. -// The bulk of this code reads in the options, opens the required files, uses -// those files to create a context for analysing the xray trace, then there is a -// short loop which actually analyses the trace, generates the graph and then -// outputs it as a DOT. -// -// FIXME: include additional filtering and annalysis passes to provide more -// specific useful information. -static CommandRegistration Unused(&GraphC, []() -> Error { +Expected GraphRenderer::Factory::getGraphRenderer(){ InstrumentationMap Map; if (!GraphInstrMap.empty()) { auto InstrumentationMapOrError = loadInstrumentationMap(GraphInstrMap); @@ -533,30 +455,16 @@ } const auto &FunctionAddresses = Map.getFunctionAddresses(); + symbolize::LLVMSymbolizer::Options Opts( symbolize::FunctionNameKind::LinkageName, true, true, false, ""); symbolize::LLVMSymbolizer Symbolizer(Opts); - llvm::xray::FuncIdConversionHelper FuncIdHelper(GraphInstrMap, Symbolizer, - FunctionAddresses); - xray::GraphRenderer GR(FuncIdHelper, GraphDeduceSiblingCalls); - std::error_code EC; - raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::F_Text); - if (EC) - return make_error( - Twine("Cannot open file '") + GraphOutput + "' for writing.", EC); - - auto TraceOrErr = loadTraceFile(GraphInput, true); - if (!TraceOrErr) - return joinErrors( - make_error(Twine("Failed loading input file '") + - GraphInput + "'", - make_error_code(llvm::errc::invalid_argument)), - TraceOrErr.takeError()); - - auto &Trace = *TraceOrErr; const auto &Header = Trace.getFileHeader(); - // Here we generate the call graph from entries we find in the trace. + llvm::xray::FuncIdConversionHelper FuncIdHelper(InstrMap, Symbolizer, + FunctionAddresses); + + xray::GraphRenderer GR(FuncIdHelper, DeduceSiblingCalls); for (const auto &Record : Trace) { auto E = GR.accountRecord(Record); if (!E) @@ -579,7 +487,55 @@ handleAllErrors(std::move(E), [&](const ErrorInfoBase &E) { E.log(errs()); }); } - GR.exportGraphAsDOT(OS, Header, GraphEdgeLabel, GraphEdgeColorType, + + GR.G.GraphEdgeMax = {}; + GR.G.GraphVertexMax = {}; + GR.calculateEdgeStatistics(); + + GR.calculateVertexStatistics(); + if (Header.CycleFrequency) + GR.normalizeStatistics(Header.CycleFrequency); + + return GR; +} + +// Here we register and implement the llvm-xray graph subcommand. +// The bulk of this code reads in the options, opens the required files, uses +// those files to create a context for analysing the xray trace, then there is a +// short loop which actually analyses the trace, generates the graph and then +// outputs it as a DOT. +// +// FIXME: include additional filtering and annalysis passes to provide more +// specific useful information. +static CommandRegistration Unused(&GraphC, []() -> Error { + GraphRenderer::Factory F; + + F.KeepGoing = GraphKeepGoing; + F.DeduceSiblingCalls = GraphDeduceSiblingCalls; + F.InstrMap = GraphInstrMap; + + + auto TraceOrErr = loadTraceFile(GraphInput, true); + + if (!TraceOrErr) + return make_error(Twine("Failed loading input file '") + + GraphInput + "'", + make_error_code(llvm::errc::invalid_argument)); + + F.Trace = std::move(*TraceOrErr); + auto GROrError = F.getGraphRenderer(); + if (!GROrError) + return GROrError.takeError(); + auto &GR = *GROrError; + + std::error_code EC; + raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::F_Text); + if (EC) + return make_error( + Twine("Cannot open file '") + GraphOutput + "' for writing.", EC); + + + GR.exportGraphAsDOT(OS, GraphEdgeLabel, GraphEdgeColorType, GraphVertexLabel, GraphVertexColorType); return Error::success(); });