llvm.org GIT mirror llvm / de5a65f
[XRAY] A Color Choosing helper for XRay Graph Summary: In Preparation for graph comparison, this patch breaks out the color choice code from xray-graph into a library and adds polynomials for the Sequential and Difference sets from ColorBrewer. Depends on D29005 Reviewers: dblaikie, chandlerc, dberris Reviewed By: dberris Subscribers: chandlerc, llvm-commits, mgorny Differential Revision: https://reviews.llvm.org/D29363 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@296210 91177308-0d34-0410-b5e6-96231b3b80d8 Dean Michael Berris 3 years ago
6 changed file(s) with 308 addition(s) and 76 deletion(s). Raw diff Collapse all Expand all
3131
3232
3333 #EDGE: digraph xray {
34 #EDGE-DAG: F0 -> F7 [label="7.{{[0-9]*}}e+01" color="#B00100"];
35 #EDGE-DAG: F0 -> F2 [label="2.{{[0-9]*}}e+01" color="#FD9965"];
34 #EDGE-DAG: F0 -> F7 [label="7.{{[0-9]*}}e+01" color="#B00000"];
35 #EDGE-DAG: F0 -> F2 [label="2.{{[0-9]*}}e+01" color="#FC9963"];
3636 #EDGE-DAG: F0 -> F9 [label="9.{{[0-9]*}}e+01" color="#7F0000"];
37 #EDGE-DAG: F0 -> F4 [label="4.{{[0-9]*}}e+01" color="#E8543b"];
38 #EDGE-DAG: F0 -> F6 [label="6.{{[0-9]*}}e+01" color="#C5140a"];
39 #EDGE-DAG: F0 -> F1 [label="1.{{[0-9]*}}e+01" color="#FDC58c"];
40 #EDGE-DAG: F0 -> F8 [label="8.{{[0-9]*}}e+01" color="#990101"];
41 #EDGE-DAG: F0 -> F3 [label="3.{{[0-9]*}}e+01" color="#F5744d"];
42 #EDGE-DAG: F0 -> F5 [label="5.{{[0-9]*}}e+01" color="#D83323"];
37 #EDGE-DAG: F0 -> F4 [label="4.{{[0-9]*}}e+01" color="#E75339"];
38 #EDGE-DAG: F0 -> F6 [label="6.{{[0-9]*}}e+01" color="#C4150D"];
39 #EDGE-DAG: F0 -> F1 [label="1.{{[0-9]*}}e+01" color="#FDC48D"];
40 #EDGE-DAG: F0 -> F8 [label="8.{{[0-9]*}}e+01" color="#970000"];
41 #EDGE-DAG: F0 -> F3 [label="3.{{[0-9]*}}e+01" color="#F4744E"];
42 #EDGE-DAG: F0 -> F5 [label="5.{{[0-9]*}}e+01" color="#D83220"];
4343 #EDGE-DAG: F7 [label="@(7)"];
4444 #EDGE-DAG: F2 [label="@(2)"];
4545 #EDGE-DAG: F9 [label="@(9)"];
6262 #VERTEX-DAG: F0 -> F8 [label=""];
6363 #VERTEX-DAG: F0 -> F3 [label=""];
6464 #VERTEX-DAG: F0 -> F5 [label=""];
65 #VERTEX-DAG: F7 [label="{@(7)|7.{{[0-9]*}}e+01}" color="#B00100"];
66 #VERTEX-DAG: F2 [label="{@(2)|2.{{[0-9]*}}e+01}" color="#FD9965"];
65 #VERTEX-DAG: F7 [label="{@(7)|7.{{[0-9]*}}e+01}" color="#B00000"];
66 #VERTEX-DAG: F2 [label="{@(2)|2.{{[0-9]*}}e+01}" color="#FC9963"];
6767 #VERTEX-DAG: F9 [label="{@(9)|9.{{[0-9]*}}e+01}" color="#7F0000"];
68 #VERTEX-DAG: F4 [label="{@(4)|4.{{[0-9]*}}e+01}" color="#E8543b"];
69 #VERTEX-DAG: F6 [label="{@(6)|6.{{[0-9]*}}e+01}" color="#C5140a"];
70 #VERTEX-DAG: F1 [label="{@(1)|1.{{[0-9]*}}e+01}" color="#FDC58c"];
71 #VERTEX-DAG: F8 [label="{@(8)|8.{{[0-9]*}}e+01}" color="#990101"];
72 #VERTEX-DAG: F3 [label="{@(3)|3.{{[0-9]*}}e+01}" color="#F5744d"];
73 #VERTEX-DAG: F5 [label="{@(5)|5.{{[0-9]*}}e+01}" color="#D83323"];
68 #VERTEX-DAG: F4 [label="{@(4)|4.{{[0-9]*}}e+01}" color="#E75339"];
69 #VERTEX-DAG: F6 [label="{@(6)|6.{{[0-9]*}}e+01}" color="#C4150D"];
70 #VERTEX-DAG: F1 [label="{@(1)|1.{{[0-9]*}}e+01}" color="#FDC48D"];
71 #VERTEX-DAG: F8 [label="{@(8)|8.{{[0-9]*}}e+01}" color="#970000"];
72 #VERTEX-DAG: F3 [label="{@(3)|3.{{[0-9]*}}e+01}" color="#F4744E"];
73 #VERTEX-DAG: F5 [label="{@(5)|5.{{[0-9]*}}e+01}" color="#D83220"];
7474 #VERTEX-NEXT: }
88 set(LLVM_XRAY_TOOLS
99 func-id-helper.cc
1010 xray-account.cc
11 xray-color-helper.cc
1112 xray-converter.cc
1213 xray-extract.cc
1314 xray-extract.cc
0 //===-- xray-graph.cc - XRay Function Call Graph Renderer -----------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // A class to get a color from a specified gradient.
10 //
11 //===----------------------------------------------------------------------===//
12 #include
13
14 #include "xray-color-helper.h"
15 #include "llvm/Support/FormatVariadic.h"
16 #include "llvm/Support/raw_ostream.h"
17
18 using namespace llvm;
19 using namespace xray;
20
21 // Sequential ColorMaps, which are used to represent information
22 // from some minimum to some maximum.
23
24 static const std::tuple SequentialMaps[][9] = {
25 {// The greys color scheme from http://colorbrewer2.org/
26 std::make_tuple(255, 255, 255), std::make_tuple(240, 240, 240),
27 std::make_tuple(217, 217, 217), std::make_tuple(189, 189, 189),
28 std::make_tuple(150, 150, 150), std::make_tuple(115, 115, 115),
29 std::make_tuple(82, 82, 82), std::make_tuple(37, 37, 37),
30 std::make_tuple(0, 0, 0)},
31 {// The OrRd color scheme from http://colorbrewer2.org/
32 std::make_tuple(255, 247, 236), std::make_tuple(254, 232, 200),
33 std::make_tuple(253, 212, 158), std::make_tuple(253, 187, 132),
34 std::make_tuple(252, 141, 89), std::make_tuple(239, 101, 72),
35 std::make_tuple(215, 48, 31), std::make_tuple(179, 0, 0),
36 std::make_tuple(127, 0, 0)},
37 {// The PuBu color scheme from http://colorbrewer2.org/
38 std::make_tuple(255, 247, 251), std::make_tuple(236, 231, 242),
39 std::make_tuple(208, 209, 230), std::make_tuple(166, 189, 219),
40 std::make_tuple(116, 169, 207), std::make_tuple(54, 144, 192),
41 std::make_tuple(5, 112, 176), std::make_tuple(4, 90, 141),
42 std::make_tuple(2, 56, 88)}};
43
44 ColorHelper::ColorHelper(ColorHelper::SequentialScheme S)
45 : MinIn(0.0), MaxIn(1.0), ColorMap(SequentialMaps[static_cast(S)]) {}
46
47 // Diverging ColorMaps, which are used to represent information
48 // representing differenes, or a range that goes from negative to positive.
49 // These take an input in the range [-1,1].
50
51 static const std::tuple DivergingCoeffs[][11] = {
52 {// The PiYG color scheme from http://colorbrewer2.org/
53 std::make_tuple(142, 1, 82), std::make_tuple(197, 27, 125),
54 std::make_tuple(222, 119, 174), std::make_tuple(241, 182, 218),
55 std::make_tuple(253, 224, 239), std::make_tuple(247, 247, 247),
56 std::make_tuple(230, 245, 208), std::make_tuple(184, 225, 134),
57 std::make_tuple(127, 188, 65), std::make_tuple(77, 146, 33),
58 std::make_tuple(39, 100, 25)}};
59
60 ColorHelper::ColorHelper(ColorHelper::DivergingScheme S)
61 : MinIn(-1.0), MaxIn(1.0), ColorMap(DivergingCoeffs[static_cast(S)]) {}
62
63 // Takes a tuple of uint8_ts representing a color in RGB and converts them to
64 // HSV represented by a tuple of doubles
65 static std::tuple
66 convertToHSV(const std::tuple &Color) {
67 double Scaled[3] = {std::get<0>(Color) / 255.0, std::get<1>(Color) / 255.0,
68 std::get<2>(Color) / 255.0};
69 int Min = 0;
70 int Max = 0;
71 for (int i = 1; i < 3; ++i) {
72 if (Scaled[i] < Scaled[Min])
73 Min = i;
74 if (Scaled[i] > Scaled[Max])
75 Max = i;
76 }
77
78 double C = Scaled[Max] - Scaled[Min];
79
80 double HPrime = (Scaled[(Max + 1) % 3] - Scaled[(Max + 2) % 3]) / C;
81 HPrime = HPrime + 2.0 * Max;
82
83 double H = (HPrime < 0) ? (HPrime + 6.0) * 60
84 : HPrime * 60; // Scale to between 0 and 360
85
86 double V = Scaled[Max];
87
88 double S = (V == 0.0) ? 0.0 : C / V;
89
90 return std::make_tuple(H, S, V);
91 }
92
93 // Takes a double precision number, clips it between 0 and 1 and then converts
94 // that to an integer between 0x00 and 0xFF with proxpper rounding.
95 static uint8_t unitIntervalTo8BitChar(double B) {
96 double n = std::max(std::min(B, 1.0), 0.0);
97 return static_cast(255 * n + 0.5);
98 }
99
100 // Takes a typle of doubles representing a color in HSV and converts them to
101 // RGB represented as a tuple of uint8_ts
102 static std::tuple
103 convertToRGB(const std::tuple &Color) {
104 const double &H = std::get<0>(Color);
105 const double &S = std::get<1>(Color);
106 const double &V = std::get<2>(Color);
107
108 double C = V * S;
109
110 double HPrime = H / 60;
111 double X = C * (1 - std::abs(std::fmod(HPrime, 2.0) - 1));
112
113 double RGB1[3];
114 int HPrimeInt = static_cast(HPrime);
115 if (HPrimeInt % 2 == 0) {
116 RGB1[(HPrimeInt / 2) % 3] = C;
117 RGB1[(HPrimeInt / 2 + 1) % 3] = X;
118 RGB1[(HPrimeInt / 2 + 2) % 3] = 0.0;
119 } else {
120 RGB1[(HPrimeInt / 2) % 3] = X;
121 RGB1[(HPrimeInt / 2 + 1) % 3] = C;
122 RGB1[(HPrimeInt / 2 + 2) % 3] = 0.0;
123 }
124
125 double Min = V - C;
126 double RGB2[3] = {RGB1[0] + Min, RGB1[1] + Min, RGB1[2] + Min};
127
128 return std::make_tuple(unitIntervalTo8BitChar(RGB2[0]),
129 unitIntervalTo8BitChar(RGB2[1]),
130 unitIntervalTo8BitChar(RGB2[2]));
131 }
132
133 // The Hue component of the HSV interpolation Routine
134 static double interpolateHue(double H0, double H1, double T) {
135 double D = H1 - H0;
136 if (H0 > H1) {
137 std::swap(H0, H1);
138
139 D = -D;
140 T = 1 - T;
141 }
142
143 if (D <= 180) {
144 return H0 + T * (H1 - H0);
145 } else {
146 H0 = H0 + 360;
147 return std::fmod(H0 + T * (H1 - H0) + 720, 360);
148 }
149 }
150
151 // Interpolates between two HSV Colors both represented as a tuple of doubles
152 // Returns an HSV Color represented as a tuple of doubles
153 static std::tuple
154 interpolateHSV(const std::tuple &C0,
155 const std::tuple &C1, double T) {
156 double H = interpolateHue(std::get<0>(C0), std::get<0>(C1), T);
157 double S = std::get<1>(C0) + T * (std::get<1>(C1) - std::get<1>(C0));
158 double V = std::get<2>(C0) + T * (std::get<2>(C1) - std::get<2>(C0));
159 return std::make_tuple(H, S, V);
160 }
161
162 // Get the Color as a tuple of uint8_ts
163 std::tuple
164 ColorHelper::getColorTuple(double Point) const {
165 assert(!ColorMap.empty() && "ColorMap must not be empty!");
166 size_t MaxIndex = ColorMap.size() - 1;
167 double IntervalWidth = MaxIn - MinIn;
168 double OffsetP = Point - MinIn;
169 double SectionWidth = IntervalWidth / static_cast(MaxIndex);
170 size_t SectionNo = std::floor(OffsetP / SectionWidth);
171 double T = (OffsetP - SectionNo * SectionWidth) / SectionWidth;
172
173 auto &RGBColor0 = ColorMap[SectionNo];
174 auto &RGBColor1 = ColorMap[std::min(SectionNo + 1, MaxIndex)];
175
176 auto HSVColor0 = convertToHSV(RGBColor0);
177 auto HSVColor1 = convertToHSV(RGBColor1);
178
179 auto InterpolatedHSVColor = interpolateHSV(HSVColor0, HSVColor1, T);
180 return convertToRGB(InterpolatedHSVColor);
181 }
182
183 // A helper method to convert a color represented as tuple of uint8s to a hex
184 // string.
185 std::string
186 ColorHelper::getColorString(std::tuple t) {
187 return llvm::formatv("#{0:X-2}{1:X-2}{2:X-2}", std::get<0>(t), std::get<1>(t),
188 std::get<2>(t));
189 }
190
191 // Gets a color in a gradient given a number in the interval [0,1], it does this
192 // by evaluating a polynomial which maps [0, 1] -> [0, 1] for each of the R G
193 // and B values in the color. It then converts this [0,1] colors to a 24 bit
194 // color as a hex string.
195 std::string ColorHelper::getColorString(double Point) const {
196 return getColorString(getColorTuple(Point));
197 }
0 //===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // A class to get a color from a specified gradient.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #ifndef XRAY_COLOR_HELPER_H
14 #define XRAY_COLOR_HELPER_H
15
16 #include
17
18 #include "llvm/ADT/ArrayRef.h"
19
20 namespace llvm {
21 namespace xray {
22
23 /// The color helper class it a healper class which allows you to easily get a
24 /// color in a gradient. This is used to color-code edges in XRay-Graph tools.
25 ///
26 /// There are two types of color schemes in this class:
27 /// - Sequential schemes, which are used to represent information from some
28 /// minimum to some maximum. These take an input in the range [0,1]
29 /// - Diverging schemes, which are used to represent information representing
30 /// differenes, or a range that goes from negative to positive. These take
31 /// an input in the range [-1,1].
32 /// Usage;
33 /// ColorHelper S(ColorHelper::SequentialScheme::OrRd); //Chose a color scheme.
34 /// for (double p = 0.0; p <= 1; p += 0.1){
35 /// cout() << S.getColor(p) << " \n"; // Sample the gradient at 0.1 intervals
36 /// }
37 ///
38 /// ColorHelper D(ColorHelper::DivergingScheme::Spectral); // Choose a color
39 /// // scheme.
40 /// for (double p= -1; p <= 1 ; p += 0.1){
41 /// cout() << D.getColor(p) << " \n"; // sample the gradient at 0.1 intervals
42 /// }
43 class ColorHelper {
44 double MinIn;
45 double MaxIn;
46
47 ArrayRef> ColorMap;
48
49 public:
50 /// Enum of the availible Sequential Color Schemes
51 enum class SequentialScheme {
52 // Schemes based on the ColorBrewer Color schemes of the same name from
53 // http://www.colorbrewer.org/ by Cynthis A Brewer Penn State University.
54 Greys,
55 OrRd,
56 PuBu
57 };
58
59 ColorHelper(SequentialScheme S);
60
61 /// Enum of the availible Diverging Color Schemes
62 enum class DivergingScheme {
63 // Schemes based on the ColorBrewer Color schemes of the same name from
64 // http://www.colorbrewer.org/ by Cynthis A Brewer Penn State University.
65 PiYG
66 };
67
68 ColorHelper(DivergingScheme S);
69
70 // Sample the gradient at the input point.
71 std::tuple getColorTuple(double Point) const;
72
73 std::string getColorString(double Point) const;
74
75 // Convert a tuple to a string
76 static std::string getColorString(std::tuple t);
77 };
78 }
79 }
80 #endif
372372 return S.str();
373373 }
374374
375 // Evaluates a polynomial given the coefficints provided in an ArrayRef
376 // evaluating:
377 //
378 // p(x) = a[n-0]*x^0 + a[n-1]*x^1 + ... a[n-n]*x^n
379 //
380 // at x_0 using Horner's Method for both performance and stability reasons.
381 static double polyEval(ArrayRef a, double x_0) {
382 double B = 0;
383 for (const auto &c : a) {
384 B = c + B * x_0;
385 }
386 return B;
387 }
388
389 // Takes a double precision number, clips it between 0 and 1 and then converts
390 // that to an integer between 0x00 and 0xFF with proxpper rounding.
391 static uint8_t uintIntervalTo8bitChar(double B) {
392 double n = std::max(std::min(B, 1.0), 0.0);
393 return static_cast(255 * n + 0.5);
394 }
395
396 // Gets a color in a gradient given a number in the interval [0,1], it does this
397 // by evaluating a polynomial which maps [0, 1] -> [0, 1] for each of the R G
398 // and B values in the color. It then converts this [0,1] colors to a 24 bit
399 // color.
400 //
401 // In order to calculate these polynomials,
402 // 1. Convert the OrRed9 color scheme from http://colorbrewer2.org/ from sRGB
403 // to LAB color space.
404 // 2. Interpolate between the descrete colors in LAB space using a cubic
405 // spline interpolation.
406 // 3. Sample this interpolation at 100 points and convert to sRGB.
407 // 4. Calculate a polynomial fit for these 100 points for each of R G and B.
408 // We used a polynomial of degree 9 arbitrarily based on a fuzzy goodness
409 // of fit metric (using human judgement);
410 // 5. Extract these polynomial coefficients from matlab as a set of constants.
411 static std::string getColor(double point) {
412 assert(point >= 0.0 && point <= 1);
413 const static double RedPoly[] = {-38.4295, 239.239, -600.108, 790.544,
414 -591.26, 251.304, -58.0983, 6.62999,
415 -0.325899, 1.00173};
416 const static double GreenPoly[] = {-603.634, 2338.15, -3606.74, 2786.16,
417 -1085.19, 165.15, 11.2584, -6.11338,
418 -0.0091078, 0.965469};
419 const static double BluePoly[] = {-325.686, 947.415, -699.079, -513.75,
420 1127.78, -732.617, 228.092, -33.8202,
421 0.732108, 0.913916};
422
423 uint8_t r = uintIntervalTo8bitChar(polyEval(RedPoly, point));
424 uint8_t g = uintIntervalTo8bitChar(polyEval(GreenPoly, point));
425 uint8_t b = uintIntervalTo8bitChar(polyEval(BluePoly, point));
426
427 return llvm::formatv("#{0:X-2}{1:X-2}{2:x-2}", r, g, b);
428 }
429
430375 // Returns the quotient between the property T of this and another TimeStat as
431376 // a double
432377 double GraphRenderer::TimeStat::compare(StatType T, const TimeStat &O) const {
490435 OS << "F" << E.first.first << " -> "
491436 << "F" << E.first.second << " [label=\"" << S.getAsString(ET) << "\"";
492437 if (EC != StatType::NONE)
493 OS << " color=\"" << getColor(S.compare(EC, G.GraphEdgeMax)) << "\"";
438 OS << " color=\"" << CHelper.getColorString(S.compare(EC, G.GraphEdgeMax))
439 << "\"";
494440 OS << "];\n";
495441 }
496442
506452 else
507453 OS << "\"";
508454 if (VC != StatType::NONE)
509 OS << " color=\"" << getColor(VA.S.compare(VC, G.GraphVertexMax)) << "\"";
455 OS << " color=\"" << CHelper.getColorString(VA.S.compare(VC, G.GraphVertexMax))
456 << "\"";
510457 OS << "];\n";
511458 }
512459 OS << "}\n";
1818 #include
1919
2020 #include "func-id-helper.h"
21 #include "xray-color-helper.h"
2122 #include "llvm/ADT/DenseMap.h"
2223 #include "llvm/ADT/SmallVector.h"
2324 #include "llvm/Support/Errc.h"
9697 PerThreadFunctionStackMap PerThreadFunctionStack;
9798
9899 /// Usefull object for getting human readable Symbol Names.
99 FuncIdConversionHelper &FuncIdHelper;
100 const FuncIdConversionHelper &FuncIdHelper;
100101 bool DeduceSiblingCalls = false;
101102 TimestampT CurrentMaxTSC = 0;
102103
116117 /// Normalises latency statistics for each edge and vertex by CycleFrequency;
117118 void normalizeStatistics(double CycleFrequency);
118119
120 /// An object to color gradients
121 ColorHelper CHelper;
122
119123 public:
120124 /// Takes in a reference to a FuncIdHelper in order to have ready access to
121125 /// Symbol names.
122 explicit GraphRenderer(FuncIdConversionHelper &FuncIdHelper, bool DSC)
123 : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC) {
126 explicit GraphRenderer(const FuncIdConversionHelper &FuncIdHelper, bool DSC)
127 : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC),
128 CHelper(ColorHelper::SequentialScheme::OrRd) {
124129 G[0] = {};
125130 }
126131