summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/llvm-xray
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/tools/llvm-xray')
-rw-r--r--contrib/llvm/tools/llvm-xray/llvm-xray.cc10
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-account.cc96
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-color-helper.cc224
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-color-helper.h89
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-converter.cc69
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-extract.cc263
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-extract.h58
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-graph-diff.cc484
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-graph-diff.h74
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-graph.cc521
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-graph.h233
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-sleds.h32
12 files changed, 1743 insertions, 410 deletions
diff --git a/contrib/llvm/tools/llvm-xray/llvm-xray.cc b/contrib/llvm/tools/llvm-xray/llvm-xray.cc
index ac5faaa..98303e7 100644
--- a/contrib/llvm/tools/llvm-xray/llvm-xray.cc
+++ b/contrib/llvm/tools/llvm-xray/llvm-xray.cc
@@ -30,12 +30,20 @@ int main(int argc, char *argv[]) {
" This program consolidates multiple XRay trace "
"processing tools for convenient access.\n");
for (auto *SC : cl::getRegisteredSubcommands()) {
- if (*SC)
+ if (*SC) {
+ // If no subcommand was provided, we need to explicitly check if this is
+ // the top-level subcommand.
+ if (SC == &*cl::TopLevelSubCommand) {
+ cl::PrintHelpMessage(false, true);
+ return 0;
+ }
if (auto C = dispatch(SC)) {
ExitOnError("llvm-xray: ")(C());
return 0;
}
+ }
}
+ // If all else fails, we still print the usage message.
cl::PrintHelpMessage(false, true);
}
diff --git a/contrib/llvm/tools/llvm-xray/xray-account.cc b/contrib/llvm/tools/llvm-xray/xray-account.cc
index 671a5a0..13654c3 100644
--- a/contrib/llvm/tools/llvm-xray/xray-account.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-account.cc
@@ -18,10 +18,10 @@
#include <utility>
#include "xray-account.h"
-#include "xray-extract.h"
#include "xray-registry.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
+#include "llvm/XRay/InstrumentationMap.h"
#include "llvm/XRay/Trace.h"
using namespace llvm;
@@ -120,16 +120,6 @@ static cl::opt<std::string>
static cl::alias AccountInstrMap2("m", cl::aliasopt(AccountInstrMap),
cl::desc("Alias for -instr_map"),
cl::sub(Account));
-static cl::opt<InstrumentationMapExtractor::InputFormats> InstrMapFormat(
- "instr-map-format", cl::desc("format of instrumentation map"),
- cl::values(clEnumValN(InstrumentationMapExtractor::InputFormats::ELF, "elf",
- "instrumentation map in an ELF header"),
- clEnumValN(InstrumentationMapExtractor::InputFormats::YAML,
- "yaml", "instrumentation map in YAML")),
- cl::sub(Account), cl::init(InstrumentationMapExtractor::InputFormats::ELF));
-static cl::alias InstrMapFormat2("t", cl::aliasopt(InstrMapFormat),
- cl::desc("Alias for -instr-map-format"),
- cl::sub(Account));
namespace {
@@ -418,67 +408,63 @@ void LatencyAccountant::exportStatsAsCSV(raw_ostream &OS,
using namespace llvm::xray;
static CommandRegistration Unused(&Account, []() -> Error {
- int Fd;
- auto EC = sys::fs::openFileForRead(AccountInput, Fd);
- if (EC)
- return make_error<StringError>(
- Twine("Cannot open file '") + AccountInput + "'", EC);
-
- Error Err = Error::success();
- xray::InstrumentationMapExtractor Extractor(AccountInstrMap, InstrMapFormat,
- Err);
- if (auto E = handleErrors(
- std::move(Err), [&](std::unique_ptr<StringError> SE) -> Error {
- if (SE->convertToErrorCode() == std::errc::no_such_file_or_directory)
- return Error::success();
- return Error(std::move(SE));
- }))
- return E;
+ InstrumentationMap Map;
+ if (!AccountInstrMap.empty()) {
+ auto InstrumentationMapOrError = loadInstrumentationMap(AccountInstrMap);
+ if (!InstrumentationMapOrError)
+ return joinErrors(make_error<StringError>(
+ Twine("Cannot open instrumentation map '") +
+ AccountInstrMap + "'",
+ std::make_error_code(std::errc::invalid_argument)),
+ InstrumentationMapOrError.takeError());
+ Map = std::move(*InstrumentationMapOrError);
+ }
+ std::error_code EC;
raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::F_Text);
if (EC)
return make_error<StringError>(
Twine("Cannot open file '") + AccountOutput + "' for writing.", EC);
- const auto &FunctionAddresses = Extractor.getFunctionAddresses();
+ const auto &FunctionAddresses = Map.getFunctionAddresses();
symbolize::LLVMSymbolizer::Options Opts(
symbolize::FunctionNameKind::LinkageName, true, true, false, "");
symbolize::LLVMSymbolizer Symbolizer(Opts);
llvm::xray::FuncIdConversionHelper FuncIdHelper(AccountInstrMap, Symbolizer,
FunctionAddresses);
xray::LatencyAccountant FCA(FuncIdHelper, AccountDeduceSiblingCalls);
- if (auto TraceOrErr = loadTraceFile(AccountInput)) {
- auto &T = *TraceOrErr;
- for (const auto &Record : T) {
- if (FCA.accountRecord(Record))
- continue;
- for (const auto &ThreadStack : FCA.getPerThreadFunctionStack()) {
- errs() << "Thread ID: " << ThreadStack.first << "\n";
- auto Level = ThreadStack.second.size();
- for (const auto &Entry : llvm::reverse(ThreadStack.second))
- errs() << "#" << Level-- << "\t"
- << FuncIdHelper.SymbolOrNumber(Entry.first) << '\n';
- }
- if (!AccountKeepGoing)
- return make_error<StringError>(
- Twine("Failed accounting function calls in file '") + AccountInput +
- "'.",
- std::make_error_code(std::errc::executable_format_error));
- }
- switch (AccountOutputFormat) {
- case AccountOutputFormats::TEXT:
- FCA.exportStatsAsText(OS, T.getFileHeader());
- break;
- case AccountOutputFormats::CSV:
- FCA.exportStatsAsCSV(OS, T.getFileHeader());
- break;
- }
- } else {
+ auto TraceOrErr = loadTraceFile(AccountInput);
+ if (!TraceOrErr)
return joinErrors(
make_error<StringError>(
Twine("Failed loading input file '") + AccountInput + "'",
std::make_error_code(std::errc::executable_format_error)),
TraceOrErr.takeError());
+
+ auto &T = *TraceOrErr;
+ for (const auto &Record : T) {
+ if (FCA.accountRecord(Record))
+ continue;
+ for (const auto &ThreadStack : FCA.getPerThreadFunctionStack()) {
+ errs() << "Thread ID: " << ThreadStack.first << "\n";
+ auto Level = ThreadStack.second.size();
+ for (const auto &Entry : llvm::reverse(ThreadStack.second))
+ errs() << "#" << Level-- << "\t"
+ << FuncIdHelper.SymbolOrNumber(Entry.first) << '\n';
+ }
+ if (!AccountKeepGoing)
+ return make_error<StringError>(
+ Twine("Failed accounting function calls in file '") + AccountInput +
+ "'.",
+ std::make_error_code(std::errc::executable_format_error));
+ }
+ switch (AccountOutputFormat) {
+ case AccountOutputFormats::TEXT:
+ FCA.exportStatsAsText(OS, T.getFileHeader());
+ break;
+ case AccountOutputFormats::CSV:
+ FCA.exportStatsAsCSV(OS, T.getFileHeader());
+ break;
}
return Error::success();
diff --git a/contrib/llvm/tools/llvm-xray/xray-color-helper.cc b/contrib/llvm/tools/llvm-xray/xray-color-helper.cc
new file mode 100644
index 0000000..7b6a73a
--- /dev/null
+++ b/contrib/llvm/tools/llvm-xray/xray-color-helper.cc
@@ -0,0 +1,224 @@
+//===-- 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 <algorithm>
+#include <iostream>
+
+#include "xray-color-helper.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace xray;
+
+// Sequential ColorMaps, which are used to represent information
+// from some minimum to some maximum.
+
+static const std::tuple<uint8_t, uint8_t, uint8_t> SequentialMaps[][9] = {
+ {// The greys color scheme from http://colorbrewer2.org/
+ std::make_tuple(255, 255, 255), std::make_tuple(240, 240, 240),
+ std::make_tuple(217, 217, 217), std::make_tuple(189, 189, 189),
+ std::make_tuple(150, 150, 150), std::make_tuple(115, 115, 115),
+ std::make_tuple(82, 82, 82), std::make_tuple(37, 37, 37),
+ std::make_tuple(0, 0, 0)},
+ {// The OrRd color scheme from http://colorbrewer2.org/
+ std::make_tuple(255, 247, 236), std::make_tuple(254, 232, 200),
+ std::make_tuple(253, 212, 158), std::make_tuple(253, 187, 132),
+ std::make_tuple(252, 141, 89), std::make_tuple(239, 101, 72),
+ std::make_tuple(215, 48, 31), std::make_tuple(179, 0, 0),
+ std::make_tuple(127, 0, 0)},
+ {// The PuBu color scheme from http://colorbrewer2.org/
+ std::make_tuple(255, 247, 251), std::make_tuple(236, 231, 242),
+ std::make_tuple(208, 209, 230), std::make_tuple(166, 189, 219),
+ std::make_tuple(116, 169, 207), std::make_tuple(54, 144, 192),
+ std::make_tuple(5, 112, 176), std::make_tuple(4, 90, 141),
+ std::make_tuple(2, 56, 88)}};
+
+// Sequential Maps extend the last colors given out of range inputs.
+static const std::tuple<uint8_t, uint8_t, uint8_t> SequentialBounds[][2] = {
+ {// The Bounds for the greys color scheme
+ std::make_tuple(255, 255, 255), std::make_tuple(0, 0, 0)},
+ {// The Bounds for the OrRd color Scheme
+ std::make_tuple(255, 247, 236), std::make_tuple(127, 0, 0)},
+ {// The Bounds for the PuBu color Scheme
+ std::make_tuple(255, 247, 251), std::make_tuple(2, 56, 88)}};
+
+ColorHelper::ColorHelper(ColorHelper::SequentialScheme S)
+ : MinIn(0.0), MaxIn(1.0), ColorMap(SequentialMaps[static_cast<int>(S)]),
+ BoundMap(SequentialBounds[static_cast<int>(S)]) {}
+
+// Diverging ColorMaps, which are used to represent information
+// representing differenes, or a range that goes from negative to positive.
+// These take an input in the range [-1,1].
+
+static const std::tuple<uint8_t, uint8_t, uint8_t> DivergingCoeffs[][11] = {
+ {// The PiYG color scheme from http://colorbrewer2.org/
+ std::make_tuple(142, 1, 82), std::make_tuple(197, 27, 125),
+ std::make_tuple(222, 119, 174), std::make_tuple(241, 182, 218),
+ std::make_tuple(253, 224, 239), std::make_tuple(247, 247, 247),
+ std::make_tuple(230, 245, 208), std::make_tuple(184, 225, 134),
+ std::make_tuple(127, 188, 65), std::make_tuple(77, 146, 33),
+ std::make_tuple(39, 100, 25)}};
+
+// Diverging maps use out of bounds ranges to show missing data. Missing Right
+// Being below min, and missing left being above max.
+static const std::tuple<uint8_t, uint8_t, uint8_t> DivergingBounds[][2] = {
+ {// The PiYG color scheme has green and red for missing right and left
+ // respectively.
+ std::make_tuple(255, 0, 0), std::make_tuple(0, 255, 0)}};
+
+ColorHelper::ColorHelper(ColorHelper::DivergingScheme S)
+ : MinIn(-1.0), MaxIn(1.0), ColorMap(DivergingCoeffs[static_cast<int>(S)]),
+ BoundMap(DivergingBounds[static_cast<int>(S)]) {}
+
+// Takes a tuple of uint8_ts representing a color in RGB and converts them to
+// HSV represented by a tuple of doubles
+static std::tuple<double, double, double>
+convertToHSV(const std::tuple<uint8_t, uint8_t, uint8_t> &Color) {
+ double Scaled[3] = {std::get<0>(Color) / 255.0, std::get<1>(Color) / 255.0,
+ std::get<2>(Color) / 255.0};
+ int Min = 0;
+ int Max = 0;
+ for (int i = 1; i < 3; ++i) {
+ if (Scaled[i] < Scaled[Min])
+ Min = i;
+ if (Scaled[i] > Scaled[Max])
+ Max = i;
+ }
+
+ double C = Scaled[Max] - Scaled[Min];
+
+ double HPrime =
+ (C == 0) ? 0 : (Scaled[(Max + 1) % 3] - Scaled[(Max + 2) % 3]) / C;
+ HPrime = HPrime + 2.0 * Max;
+
+ double H = (HPrime < 0) ? (HPrime + 6.0) * 60
+ : HPrime * 60; // Scale to between 0 and 360
+ double V = Scaled[Max];
+
+ double S = (V == 0.0) ? 0.0 : C / V;
+
+ return std::make_tuple(H, S, V);
+}
+
+// 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 unitIntervalTo8BitChar(double B) {
+ double n = std::max(std::min(B, 1.0), 0.0);
+ return static_cast<uint8_t>(255 * n + 0.5);
+}
+
+// Takes a typle of doubles representing a color in HSV and converts them to
+// RGB represented as a tuple of uint8_ts
+static std::tuple<uint8_t, uint8_t, uint8_t>
+convertToRGB(const std::tuple<double, double, double> &Color) {
+ const double &H = std::get<0>(Color);
+ const double &S = std::get<1>(Color);
+ const double &V = std::get<2>(Color);
+
+ double C = V * S;
+
+ double HPrime = H / 60;
+ double X = C * (1 - std::abs(std::fmod(HPrime, 2.0) - 1));
+
+ double RGB1[3];
+ int HPrimeInt = static_cast<int>(HPrime);
+ if (HPrimeInt % 2 == 0) {
+ RGB1[(HPrimeInt / 2) % 3] = C;
+ RGB1[(HPrimeInt / 2 + 1) % 3] = X;
+ RGB1[(HPrimeInt / 2 + 2) % 3] = 0.0;
+ } else {
+ RGB1[(HPrimeInt / 2) % 3] = X;
+ RGB1[(HPrimeInt / 2 + 1) % 3] = C;
+ RGB1[(HPrimeInt / 2 + 2) % 3] = 0.0;
+ }
+
+ double Min = V - C;
+ double RGB2[3] = {RGB1[0] + Min, RGB1[1] + Min, RGB1[2] + Min};
+
+ return std::make_tuple(unitIntervalTo8BitChar(RGB2[0]),
+ unitIntervalTo8BitChar(RGB2[1]),
+ unitIntervalTo8BitChar(RGB2[2]));
+}
+
+// The Hue component of the HSV interpolation Routine
+static double interpolateHue(double H0, double H1, double T) {
+ double D = H1 - H0;
+ if (H0 > H1) {
+ std::swap(H0, H1);
+
+ D = -D;
+ T = 1 - T;
+ }
+
+ if (D <= 180) {
+ return H0 + T * (H1 - H0);
+ } else {
+ H0 = H0 + 360;
+ return std::fmod(H0 + T * (H1 - H0) + 720, 360);
+ }
+}
+
+// Interpolates between two HSV Colors both represented as a tuple of doubles
+// Returns an HSV Color represented as a tuple of doubles
+static std::tuple<double, double, double>
+interpolateHSV(const std::tuple<double, double, double> &C0,
+ const std::tuple<double, double, double> &C1, double T) {
+ double H = interpolateHue(std::get<0>(C0), std::get<0>(C1), T);
+ double S = std::get<1>(C0) + T * (std::get<1>(C1) - std::get<1>(C0));
+ double V = std::get<2>(C0) + T * (std::get<2>(C1) - std::get<2>(C0));
+ return std::make_tuple(H, S, V);
+}
+
+// Get the Color as a tuple of uint8_ts
+std::tuple<uint8_t, uint8_t, uint8_t>
+ColorHelper::getColorTuple(double Point) const {
+ assert(!ColorMap.empty() && "ColorMap must not be empty!");
+ assert(!BoundMap.empty() && "BoundMap must not be empty!");
+
+ if (Point < MinIn)
+ return BoundMap[0];
+ if (Point > MaxIn)
+ return BoundMap[1];
+
+ size_t MaxIndex = ColorMap.size() - 1;
+ double IntervalWidth = MaxIn - MinIn;
+ double OffsetP = Point - MinIn;
+ double SectionWidth = IntervalWidth / static_cast<double>(MaxIndex);
+ size_t SectionNo = std::floor(OffsetP / SectionWidth);
+ double T = (OffsetP - SectionNo * SectionWidth) / SectionWidth;
+
+ auto &RGBColor0 = ColorMap[SectionNo];
+ auto &RGBColor1 = ColorMap[std::min(SectionNo + 1, MaxIndex)];
+
+ auto HSVColor0 = convertToHSV(RGBColor0);
+ auto HSVColor1 = convertToHSV(RGBColor1);
+
+ auto InterpolatedHSVColor = interpolateHSV(HSVColor0, HSVColor1, T);
+ return convertToRGB(InterpolatedHSVColor);
+}
+
+// A helper method to convert a color represented as tuple of uint8s to a hex
+// string.
+std::string
+ColorHelper::getColorString(std::tuple<uint8_t, uint8_t, uint8_t> t) {
+ return llvm::formatv("#{0:X-2}{1:X-2}{2:X-2}", std::get<0>(t), std::get<1>(t),
+ std::get<2>(t));
+}
+
+// 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 as a hex string.
+std::string ColorHelper::getColorString(double Point) const {
+ return getColorString(getColorTuple(Point));
+}
diff --git a/contrib/llvm/tools/llvm-xray/xray-color-helper.h b/contrib/llvm/tools/llvm-xray/xray-color-helper.h
new file mode 100644
index 0000000..b2dcf62
--- /dev/null
+++ b/contrib/llvm/tools/llvm-xray/xray-color-helper.h
@@ -0,0 +1,89 @@
+//===-- 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 <tuple>
+
+#include "llvm/ADT/ArrayRef.h"
+
+namespace llvm {
+namespace xray {
+
+/// The color helper class it a healper class which allows you to easily get a
+/// color in a gradient. This is used to color-code edges in XRay-Graph tools.
+///
+/// There are two types of color schemes in this class:
+/// - Sequential schemes, which are used to represent information from some
+/// minimum to some maximum. These take an input in the range [0,1]
+/// - Diverging schemes, which are used to represent information representing
+/// differenes, or a range that goes from negative to positive. These take
+/// an input in the range [-1,1].
+/// Usage;
+/// ColorHelper S(ColorHelper::SequentialScheme::OrRd); //Chose a color scheme.
+/// for (double p = 0.0; p <= 1; p += 0.1){
+/// cout() << S.getColor(p) << " \n"; // Sample the gradient at 0.1 intervals
+/// }
+///
+/// ColorHelper D(ColorHelper::DivergingScheme::Spectral); // Choose a color
+/// // scheme.
+/// for (double p= -1; p <= 1 ; p += 0.1){
+/// cout() << D.getColor(p) << " \n"; // sample the gradient at 0.1 intervals
+/// }
+class ColorHelper {
+ double MinIn;
+ double MaxIn;
+
+ ArrayRef<std::tuple<uint8_t, uint8_t, uint8_t>> ColorMap;
+ ArrayRef<std::tuple<uint8_t, uint8_t, uint8_t>> BoundMap;
+
+public:
+ /// Enum of the availible Sequential Color Schemes
+ enum class SequentialScheme {
+ // Schemes based on the ColorBrewer Color schemes of the same name from
+ // http://www.colorbrewer.org/ by Cynthis A Brewer Penn State University.
+ Greys,
+ OrRd,
+ PuBu
+ };
+
+ ColorHelper(SequentialScheme S);
+
+ /// Enum of the availible Diverging Color Schemes
+ enum class DivergingScheme {
+ // Schemes based on the ColorBrewer Color schemes of the same name from
+ // http://www.colorbrewer.org/ by Cynthis A Brewer Penn State University.
+ PiYG
+ };
+
+ ColorHelper(DivergingScheme S);
+
+ // Sample the gradient at the input point.
+ std::tuple<uint8_t, uint8_t, uint8_t> getColorTuple(double Point) const;
+
+ std::string getColorString(double Point) const;
+
+ // Get the Default color, at the moment allways black.
+ std::tuple<uint8_t, uint8_t, uint8_t> getDefaultColorTuple() const {
+ return std::make_tuple(0, 0, 0);
+ }
+
+ std::string getDefaultColorString() const { return "black"; }
+
+ // Convert a tuple to a string
+ static std::string getColorString(std::tuple<uint8_t, uint8_t, uint8_t> t);
+};
+} // namespace xray
+} // namespace llvm
+#endif
diff --git a/contrib/llvm/tools/llvm-xray/xray-converter.cc b/contrib/llvm/tools/llvm-xray/xray-converter.cc
index 1bc9b15..2583ec9 100644
--- a/contrib/llvm/tools/llvm-xray/xray-converter.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-converter.cc
@@ -12,13 +12,14 @@
//===----------------------------------------------------------------------===//
#include "xray-converter.h"
-#include "xray-extract.h"
#include "xray-registry.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/XRay/InstrumentationMap.h"
#include "llvm/XRay/Trace.h"
#include "llvm/XRay/YAMLXRayRecord.h"
@@ -72,18 +73,7 @@ static cl::opt<bool> ConvertSortInput(
static cl::alias ConvertSortInput2("s", cl::aliasopt(ConvertSortInput),
cl::desc("Alias for -sort"),
cl::sub(Convert));
-static cl::opt<InstrumentationMapExtractor::InputFormats> InstrMapFormat(
- "instr-map-format", cl::desc("format of instrumentation map"),
- cl::values(clEnumValN(InstrumentationMapExtractor::InputFormats::ELF, "elf",
- "instrumentation map in an ELF header"),
- clEnumValN(InstrumentationMapExtractor::InputFormats::YAML,
- "yaml", "instrumentation map in YAML")),
- cl::sub(Convert), cl::init(InstrumentationMapExtractor::InputFormats::ELF));
-static cl::alias InstrMapFormat2("t", cl::aliasopt(InstrMapFormat),
- cl::desc("Alias for -instr-map-format"),
- cl::sub(Convert));
-
-using llvm::yaml::IO;
+
using llvm::yaml::Output;
void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) {
@@ -95,7 +85,7 @@ void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) {
for (const auto &R : Records) {
Trace.Records.push_back({R.RecordType, R.CPU, R.Type, R.FuncId,
Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId)
- : std::to_string(R.FuncId),
+ : llvm::to_string(R.FuncId),
R.TSC, R.TId});
}
Output Out(OS, nullptr, 0);
@@ -128,7 +118,9 @@ void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) {
// format.
for (const auto &R : Records) {
Writer.write(R.RecordType);
- Writer.write(R.CPU);
+ // The on disk naive raw format uses 8 bit CPUs, but the record has 16.
+ // There's no choice but truncation.
+ Writer.write(static_cast<uint8_t>(R.CPU));
switch (R.Type) {
case RecordTypes::ENTER:
Writer.write(uint8_t{0});
@@ -151,25 +143,26 @@ namespace xray {
static CommandRegistration Unused(&Convert, []() -> Error {
// FIXME: Support conversion to BINARY when upgrading XRay trace versions.
- int Fd;
- auto EC = sys::fs::openFileForRead(ConvertInput, Fd);
- if (EC)
- return make_error<StringError>(
- Twine("Cannot open file '") + ConvertInput + "'", EC);
-
- Error Err = Error::success();
- xray::InstrumentationMapExtractor Extractor(ConvertInstrMap, InstrMapFormat,
- Err);
- handleAllErrors(std::move(Err),
- [&](const ErrorInfoBase &E) { E.log(errs()); });
+ InstrumentationMap Map;
+ if (!ConvertInstrMap.empty()) {
+ auto InstrumentationMapOrError = loadInstrumentationMap(ConvertInstrMap);
+ if (!InstrumentationMapOrError)
+ return joinErrors(make_error<StringError>(
+ Twine("Cannot open instrumentation map '") +
+ ConvertInstrMap + "'",
+ std::make_error_code(std::errc::invalid_argument)),
+ InstrumentationMapOrError.takeError());
+ Map = std::move(*InstrumentationMapOrError);
+ }
- const auto &FunctionAddresses = Extractor.getFunctionAddresses();
+ const auto &FunctionAddresses = Map.getFunctionAddresses();
symbolize::LLVMSymbolizer::Options Opts(
symbolize::FunctionNameKind::LinkageName, true, true, false, "");
symbolize::LLVMSymbolizer Symbolizer(Opts);
llvm::xray::FuncIdConversionHelper FuncIdHelper(ConvertInstrMap, Symbolizer,
FunctionAddresses);
llvm::xray::TraceConverter TC(FuncIdHelper, ConvertSymbolize);
+ std::error_code EC;
raw_fd_ostream OS(ConvertOutput, EC,
ConvertOutputFormat == ConvertFormats::BINARY
? sys::fs::OpenFlags::F_None
@@ -178,22 +171,22 @@ static CommandRegistration Unused(&Convert, []() -> Error {
return make_error<StringError>(
Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC);
- if (auto TraceOrErr = loadTraceFile(ConvertInput, ConvertSortInput)) {
- auto &T = *TraceOrErr;
- switch (ConvertOutputFormat) {
- case ConvertFormats::YAML:
- TC.exportAsYAML(T, OS);
- break;
- case ConvertFormats::BINARY:
- TC.exportAsRAWv1(T, OS);
- break;
- }
- } else {
+ auto TraceOrErr = loadTraceFile(ConvertInput, ConvertSortInput);
+ if (!TraceOrErr)
return joinErrors(
make_error<StringError>(
Twine("Failed loading input file '") + ConvertInput + "'.",
std::make_error_code(std::errc::executable_format_error)),
TraceOrErr.takeError());
+
+ auto &T = *TraceOrErr;
+ switch (ConvertOutputFormat) {
+ case ConvertFormats::YAML:
+ TC.exportAsYAML(T, OS);
+ break;
+ case ConvertFormats::BINARY:
+ TC.exportAsRAWv1(T, OS);
+ break;
}
return Error::success();
});
diff --git a/contrib/llvm/tools/llvm-xray/xray-extract.cc b/contrib/llvm/tools/llvm-xray/xray-extract.cc
index ecd5351..6b72b81 100644
--- a/contrib/llvm/tools/llvm-xray/xray-extract.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-extract.cc
@@ -16,20 +16,18 @@
#include <type_traits>
#include <utility>
-#include "xray-extract.h"
-
+#include "func-id-helper.h"
#include "xray-registry.h"
-#include "xray-sleds.h"
+#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/DataExtractor.h"
-#include "llvm/Support/ELF.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
-#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/XRay/InstrumentationMap.h"
using namespace llvm;
using namespace llvm::xray;
@@ -48,244 +46,57 @@ static cl::opt<std::string>
static cl::alias ExtractOutput2("o", cl::aliasopt(ExtractOutput),
cl::desc("Alias for -output"),
cl::sub(Extract));
-
-struct YAMLXRaySledEntry {
- int32_t FuncId;
- Hex64 Address;
- Hex64 Function;
- SledEntry::FunctionKinds Kind;
- bool AlwaysInstrument;
-};
-
-namespace llvm {
-namespace yaml {
-
-template <> struct ScalarEnumerationTraits<SledEntry::FunctionKinds> {
- static void enumeration(IO &IO, SledEntry::FunctionKinds &Kind) {
- IO.enumCase(Kind, "function-enter", SledEntry::FunctionKinds::ENTRY);
- IO.enumCase(Kind, "function-exit", SledEntry::FunctionKinds::EXIT);
- IO.enumCase(Kind, "tail-exit", SledEntry::FunctionKinds::TAIL);
- }
-};
-
-template <> struct MappingTraits<YAMLXRaySledEntry> {
- static void mapping(IO &IO, YAMLXRaySledEntry &Entry) {
- IO.mapRequired("id", Entry.FuncId);
- IO.mapRequired("address", Entry.Address);
- IO.mapRequired("function", Entry.Function);
- IO.mapRequired("kind", Entry.Kind);
- IO.mapRequired("always-instrument", Entry.AlwaysInstrument);
- }
-
- static constexpr bool flow = true;
-};
-}
-}
-
-LLVM_YAML_IS_SEQUENCE_VECTOR(YAMLXRaySledEntry)
+static cl::opt<bool> ExtractSymbolize("symbolize", cl::value_desc("symbolize"),
+ cl::init(false),
+ cl::desc("symbolize functions"),
+ cl::sub(Extract));
+static cl::alias ExtractSymbolize2("s", cl::aliasopt(ExtractSymbolize),
+ cl::desc("alias for -symbolize"),
+ cl::sub(Extract));
namespace {
-llvm::Error LoadBinaryInstrELF(
- StringRef Filename, std::deque<SledEntry> &OutputSleds,
- InstrumentationMapExtractor::FunctionAddressMap &InstrMap,
- InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) {
- auto ObjectFile = object::ObjectFile::createObjectFile(Filename);
-
- if (!ObjectFile)
- return ObjectFile.takeError();
-
- // FIXME: Maybe support other ELF formats. For now, 64-bit Little Endian only.
- if (!ObjectFile->getBinary()->isELF())
- return make_error<StringError>(
- "File format not supported (only does ELF).",
- std::make_error_code(std::errc::not_supported));
- if (ObjectFile->getBinary()->getArch() != Triple::x86_64)
- return make_error<StringError>(
- "File format not supported (only does ELF little endian 64-bit).",
- std::make_error_code(std::errc::not_supported));
-
- // Find the section named "xray_instr_map".
- StringRef Contents = "";
- const auto &Sections = ObjectFile->getBinary()->sections();
- auto I = find_if(Sections, [&](object::SectionRef Section) {
- StringRef Name = "";
- if (Section.getName(Name))
- return false;
- return Name == "xray_instr_map";
- });
- if (I == Sections.end())
- return make_error<StringError>(
- "Failed to find XRay instrumentation map.",
- std::make_error_code(std::errc::not_supported));
- if (I->getContents(Contents))
- return make_error<StringError>(
- "Failed to get contents of 'xray_instr_map' section.",
- std::make_error_code(std::errc::executable_format_error));
-
- // Copy the instrumentation map data into the Sleds data structure.
- auto C = Contents.bytes_begin();
- static constexpr size_t ELF64SledEntrySize = 32;
-
- if ((C - Contents.bytes_end()) % ELF64SledEntrySize != 0)
- return make_error<StringError>(
- "Instrumentation map entries not evenly divisible by size of an XRay "
- "sled entry in ELF64.",
- std::make_error_code(std::errc::executable_format_error));
-
- int32_t FuncId = 1;
- uint64_t CurFn = 0;
- std::deque<SledEntry> Sleds;
- for (; C != Contents.bytes_end(); C += ELF64SledEntrySize) {
- DataExtractor Extractor(
- StringRef(reinterpret_cast<const char *>(C), ELF64SledEntrySize), true,
- 8);
- Sleds.push_back({});
- auto &Entry = Sleds.back();
- uint32_t OffsetPtr = 0;
- Entry.Address = Extractor.getU64(&OffsetPtr);
- Entry.Function = Extractor.getU64(&OffsetPtr);
- auto Kind = Extractor.getU8(&OffsetPtr);
- switch (Kind) {
- case 0: // ENTRY
- Entry.Kind = SledEntry::FunctionKinds::ENTRY;
- break;
- case 1: // EXIT
- Entry.Kind = SledEntry::FunctionKinds::EXIT;
- break;
- case 2: // TAIL
- Entry.Kind = SledEntry::FunctionKinds::TAIL;
- break;
- default:
- return make_error<StringError>(
- Twine("Encountered unknown sled type ") + "'" + Twine(int32_t{Kind}) +
- "'.",
- std::make_error_code(std::errc::executable_format_error));
- }
- Entry.AlwaysInstrument = Extractor.getU8(&OffsetPtr) != 0;
-
- // We replicate the function id generation scheme implemented in the runtime
- // here. Ideally we should be able to break it out, or output this map from
- // the runtime, but that's a design point we can discuss later on. For now,
- // we replicate the logic and move on.
- if (CurFn == 0) {
- CurFn = Entry.Function;
- InstrMap[FuncId] = Entry.Function;
- FunctionIds[Entry.Function] = FuncId;
- }
- if (Entry.Function != CurFn) {
- ++FuncId;
- CurFn = Entry.Function;
- InstrMap[FuncId] = Entry.Function;
- FunctionIds[Entry.Function] = FuncId;
- }
- }
- OutputSleds = std::move(Sleds);
- return llvm::Error::success();
-}
-
-Error LoadYAMLInstrMap(
- StringRef Filename, std::deque<SledEntry> &Sleds,
- InstrumentationMapExtractor::FunctionAddressMap &InstrMap,
- InstrumentationMapExtractor::FunctionAddressReverseMap &FunctionIds) {
- int Fd;
- if (auto EC = sys::fs::openFileForRead(Filename, Fd))
- return make_error<StringError>(
- Twine("Failed opening file '") + Filename + "' for reading.", EC);
-
- uint64_t FileSize;
- if (auto EC = sys::fs::file_size(Filename, FileSize))
- return make_error<StringError>(
- Twine("Failed getting size of file '") + Filename + "'.", EC);
-
- std::error_code EC;
- sys::fs::mapped_file_region MappedFile(
- Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
- if (EC)
- return make_error<StringError>(
- Twine("Failed memory-mapping file '") + Filename + "'.", EC);
-
- std::vector<YAMLXRaySledEntry> YAMLSleds;
- Input In(StringRef(MappedFile.data(), MappedFile.size()));
- In >> YAMLSleds;
- if (In.error())
- return make_error<StringError>(
- Twine("Failed loading YAML document from '") + Filename + "'.",
- In.error());
-
- for (const auto &Y : YAMLSleds) {
- InstrMap[Y.FuncId] = Y.Function;
- FunctionIds[Y.Function] = Y.FuncId;
- Sleds.push_back(
- SledEntry{Y.Address, Y.Function, Y.Kind, Y.AlwaysInstrument});
- }
- return Error::success();
-}
-
-} // namespace
-
-InstrumentationMapExtractor::InstrumentationMapExtractor(std::string Filename,
- InputFormats Format,
- Error &EC) {
- ErrorAsOutParameter ErrAsOutputParam(&EC);
- if (Filename.empty()) {
- EC = Error::success();
- return;
- }
- switch (Format) {
- case InputFormats::ELF: {
- EC = handleErrors(
- LoadBinaryInstrELF(Filename, Sleds, FunctionAddresses, FunctionIds),
- [&](std::unique_ptr<ErrorInfoBase> E) {
- return joinErrors(
- make_error<StringError>(
- Twine("Cannot extract instrumentation map from '") +
- Filename + "'.",
- std::make_error_code(std::errc::executable_format_error)),
- std::move(E));
- });
- break;
- }
- case InputFormats::YAML: {
- EC = handleErrors(
- LoadYAMLInstrMap(Filename, Sleds, FunctionAddresses, FunctionIds),
- [&](std::unique_ptr<ErrorInfoBase> E) {
- return joinErrors(
- make_error<StringError>(
- Twine("Cannot load YAML instrumentation map from '") +
- Filename + "'.",
- std::make_error_code(std::errc::executable_format_error)),
- std::move(E));
- });
- break;
- }
- }
-}
-
-void InstrumentationMapExtractor::exportAsYAML(raw_ostream &OS) {
+void exportAsYAML(const InstrumentationMap &Map, raw_ostream &OS,
+ FuncIdConversionHelper &FH) {
// First we translate the sleds into the YAMLXRaySledEntry objects in a deque.
std::vector<YAMLXRaySledEntry> YAMLSleds;
- YAMLSleds.reserve(Sleds.size());
+ auto Sleds = Map.sleds();
+ YAMLSleds.reserve(std::distance(Sleds.begin(), Sleds.end()));
for (const auto &Sled : Sleds) {
- YAMLSleds.push_back({FunctionIds[Sled.Function], Sled.Address,
- Sled.Function, Sled.Kind, Sled.AlwaysInstrument});
+ auto FuncId = Map.getFunctionId(Sled.Function);
+ if (!FuncId)
+ return;
+ YAMLSleds.push_back({*FuncId, Sled.Address, Sled.Function, Sled.Kind,
+ Sled.AlwaysInstrument,
+ ExtractSymbolize ? FH.SymbolOrNumber(*FuncId) : ""});
}
Output Out(OS, nullptr, 0);
Out << YAMLSleds;
}
+} // namespace
+
static CommandRegistration Unused(&Extract, []() -> Error {
- Error Err = Error::success();
- xray::InstrumentationMapExtractor Extractor(
- ExtractInput, InstrumentationMapExtractor::InputFormats::ELF, Err);
- if (Err)
- return Err;
+ auto InstrumentationMapOrError = loadInstrumentationMap(ExtractInput);
+ if (!InstrumentationMapOrError)
+ return joinErrors(make_error<StringError>(
+ Twine("Cannot extract instrumentation map from '") +
+ ExtractInput + "'.",
+ std::make_error_code(std::errc::invalid_argument)),
+ InstrumentationMapOrError.takeError());
std::error_code EC;
raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text);
if (EC)
return make_error<StringError>(
Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC);
- Extractor.exportAsYAML(OS);
+ const auto &FunctionAddresses =
+ InstrumentationMapOrError->getFunctionAddresses();
+ symbolize::LLVMSymbolizer::Options Opts(
+ symbolize::FunctionNameKind::LinkageName, true, true, false, "");
+ symbolize::LLVMSymbolizer Symbolizer(Opts);
+ llvm::xray::FuncIdConversionHelper FuncIdHelper(ExtractInput, Symbolizer,
+ FunctionAddresses);
+ exportAsYAML(*InstrumentationMapOrError, OS, FuncIdHelper);
return Error::success();
});
diff --git a/contrib/llvm/tools/llvm-xray/xray-extract.h b/contrib/llvm/tools/llvm-xray/xray-extract.h
deleted file mode 100644
index 91e4db3..0000000
--- a/contrib/llvm/tools/llvm-xray/xray-extract.h
+++ /dev/null
@@ -1,58 +0,0 @@
-//===- xray-extract.h - XRay Instrumentation Map Extraction ---------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Defines the interface for extracting the instrumentation map from an
-// XRay-instrumented binary.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_XRAY_EXTRACT_H
-#define LLVM_TOOLS_XRAY_EXTRACT_H
-
-#include <deque>
-#include <map>
-#include <string>
-#include <unordered_map>
-
-#include "xray-sleds.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/raw_ostream.h"
-
-namespace llvm {
-namespace xray {
-
-class InstrumentationMapExtractor {
-public:
- typedef std::unordered_map<int32_t, uint64_t> FunctionAddressMap;
- typedef std::unordered_map<uint64_t, int32_t> FunctionAddressReverseMap;
-
- enum class InputFormats { ELF, YAML };
-
-private:
- std::deque<SledEntry> Sleds;
- FunctionAddressMap FunctionAddresses;
- FunctionAddressReverseMap FunctionIds;
-
-public:
- /// Loads the instrumentation map from |Filename|. Updates |EC| in case there
- /// were errors encountered opening the file. |Format| defines what the input
- /// instrumentation map is in.
- InstrumentationMapExtractor(std::string Filename, InputFormats Format,
- Error &EC);
-
- const FunctionAddressMap &getFunctionAddresses() { return FunctionAddresses; }
-
- /// Exports the loaded function address map as YAML through |OS|.
- void exportAsYAML(raw_ostream &OS);
-};
-
-} // namespace xray
-} // namespace llvm
-
-#endif // LLVM_TOOLS_XRAY_EXTRACT_H
diff --git a/contrib/llvm/tools/llvm-xray/xray-graph-diff.cc b/contrib/llvm/tools/llvm-xray/xray-graph-diff.cc
new file mode 100644
index 0000000..3c69b3f
--- /dev/null
+++ b/contrib/llvm/tools/llvm-xray/xray-graph-diff.cc
@@ -0,0 +1,484 @@
+//===-- 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 <cassert>
+#include <cmath>
+#include <limits>
+#include <string>
+
+#include "xray-graph-diff.h"
+#include "xray-graph.h"
+#include "xray-registry.h"
+
+#include "xray-color-helper.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/FormatVariadic.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<std::string> GraphDiffInput1(cl::Positional,
+ cl::desc("<xray log file 1>"),
+ cl::Required, cl::sub(GraphDiff));
+static cl::opt<std::string> GraphDiffInput2(cl::Positional,
+ cl::desc("<xray log file 2>"),
+ cl::Required, cl::sub(GraphDiff));
+
+static cl::opt<bool>
+ 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<bool>
+ 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<bool>
+ 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<std::string>
+ 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<std::string>
+ 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<std::string>
+ 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<bool> 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<bool> 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<bool> 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<GraphRenderer::StatType> 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<GraphRenderer::StatType> GraphDiffEdgeColor(
+ "edge-color", cl::desc("Output graphs with edges colored 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 GraphDiffEdgeColorA("c", cl::aliasopt(GraphDiffEdgeColor),
+ cl::desc("Alias for -edge-color"),
+ cl::sub(GraphDiff));
+
+static cl::opt<GraphRenderer::StatType> 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<GraphRenderer::StatType> GraphDiffVertexColor(
+ "vertex-color",
+ cl::desc("Output graphs with vertices colored 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 GraphDiffVertexColorA("b", cl::aliasopt(GraphDiffVertexColor),
+ cl::desc("Alias for -vertex-color"),
+ cl::sub(GraphDiff));
+
+static cl::opt<int> GraphDiffVertexLabelTrunc(
+ "vertex-label-trun", cl::desc("What length to truncate vertex labels to "),
+ cl::sub(GraphDiff), cl::init(40));
+static cl::alias
+ GraphDiffVertexLabelTrunc1("t", cl::aliasopt(GraphDiffVertexLabelTrunc),
+ cl::desc("Alias for -vertex-label-trun"),
+ cl::sub(GraphDiff));
+
+static cl::opt<std::string>
+ 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> GraphDiffRenderer::Factory::getGraphDiffRenderer() {
+ GraphDiffRenderer R;
+
+ for (int i = 0; i < N; ++i) {
+ const auto &G = this->G[i].get();
+ for (const auto &V : G.vertices()) {
+ const auto &VAttr = V.second;
+ R.G[VAttr.SymbolName].CorrVertexPtr[i] = &V;
+ }
+ for (const auto &E : G.edges()) {
+ auto &EdgeTailID = E.first.first;
+ auto &EdgeHeadID = E.first.second;
+ auto EdgeTailAttrOrErr = G.at(EdgeTailID);
+ auto EdgeHeadAttrOrErr = G.at(EdgeHeadID);
+ if (!EdgeTailAttrOrErr)
+ return EdgeTailAttrOrErr.takeError();
+ if (!EdgeHeadAttrOrErr)
+ return EdgeHeadAttrOrErr.takeError();
+ GraphT::EdgeIdentifier ID{EdgeTailAttrOrErr->SymbolName,
+ EdgeHeadAttrOrErr->SymbolName};
+ R.G[ID].CorrEdgePtr[i] = &E;
+ }
+ }
+
+ return R;
+}
+// Returns the Relative change With respect to LeftStat between LeftStat
+// and RightStat.
+static double statRelDiff(const GraphDiffRenderer::TimeStat &LeftStat,
+ const GraphDiffRenderer::TimeStat &RightStat,
+ GraphDiffRenderer::StatType T) {
+ double LeftAttr = LeftStat.getDouble(T);
+ double RightAttr = RightStat.getDouble(T);
+
+ return RightAttr / LeftAttr - 1.0;
+}
+
+static std::string getColor(const GraphDiffRenderer::GraphT::EdgeValueType &E,
+ const GraphDiffRenderer::GraphT &G, ColorHelper H,
+ GraphDiffRenderer::StatType T) {
+ auto &EdgeAttr = E.second;
+ if (EdgeAttr.CorrEdgePtr[0] == nullptr)
+ return H.getColorString(2.0); // A number greater than 1.0
+ if (EdgeAttr.CorrEdgePtr[1] == nullptr)
+ return H.getColorString(-2.0); // A number less than -1.0
+
+ if (T == GraphDiffRenderer::StatType::NONE)
+ return H.getDefaultColorString();
+
+ const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S;
+ const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S;
+
+ double RelDiff = statRelDiff(LeftStat, RightStat, T);
+ double CappedRelDiff = std::min(1.0, std::max(-1.0, RelDiff));
+
+ return H.getColorString(CappedRelDiff);
+}
+
+static std::string getColor(const GraphDiffRenderer::GraphT::VertexValueType &V,
+ const GraphDiffRenderer::GraphT &G, ColorHelper H,
+ GraphDiffRenderer::StatType T) {
+ auto &VertexAttr = V.second;
+ if (VertexAttr.CorrVertexPtr[0] == nullptr)
+ return H.getColorString(2.0); // A number greater than 1.0
+ if (VertexAttr.CorrVertexPtr[1] == nullptr)
+ return H.getColorString(-2.0); // A number less than -1.0
+
+ if (T == GraphDiffRenderer::StatType::NONE)
+ return H.getDefaultColorString();
+
+ const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S;
+ const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S;
+
+ double RelDiff = statRelDiff(LeftStat, RightStat, T);
+ double CappedRelDiff = std::min(1.0, std::max(-1.0, RelDiff));
+
+ return H.getColorString(CappedRelDiff);
+}
+
+static Twine truncateString(const StringRef &S, size_t n) {
+ return (S.size() > n) ? Twine(S.substr(0, n)) + "..." : Twine(S);
+}
+
+template <typename T> static bool containsNullptr(const T &Collection) {
+ for (const auto &E : Collection)
+ if (E == nullptr)
+ return true;
+ return false;
+}
+
+static std::string getLabel(const GraphDiffRenderer::GraphT::EdgeValueType &E,
+ GraphDiffRenderer::StatType EL) {
+ auto &EdgeAttr = E.second;
+ switch (EL) {
+ case GraphDiffRenderer::StatType::NONE:
+ return "";
+ default:
+ if (containsNullptr(EdgeAttr.CorrEdgePtr))
+ return "";
+
+ const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S;
+ const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S;
+
+ double RelDiff = statRelDiff(LeftStat, RightStat, EL);
+ return formatv(R"({0:P})", RelDiff);
+ }
+}
+
+static std::string getLabel(const GraphDiffRenderer::GraphT::VertexValueType &V,
+ GraphDiffRenderer::StatType VL, int TrunLen) {
+ const auto &VertexId = V.first;
+ const auto &VertexAttr = V.second;
+ switch (VL) {
+ case GraphDiffRenderer::StatType::NONE:
+ return formatv(R"({0})", truncateString(VertexId, TrunLen).str());
+ default:
+ if (containsNullptr(VertexAttr.CorrVertexPtr))
+ return formatv(R"({0})", truncateString(VertexId, TrunLen).str());
+
+ const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S;
+ const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S;
+
+ double RelDiff = statRelDiff(LeftStat, RightStat, VL);
+ return formatv(R"({{{0}|{1:P}})", truncateString(VertexId, TrunLen).str(),
+ RelDiff);
+ }
+}
+
+static double getLineWidth(const GraphDiffRenderer::GraphT::EdgeValueType &E,
+ GraphDiffRenderer::StatType EL) {
+ auto &EdgeAttr = E.second;
+ switch (EL) {
+ case GraphDiffRenderer::StatType::NONE:
+ return 1.0;
+ default:
+ if (containsNullptr(EdgeAttr.CorrEdgePtr))
+ return 1.0;
+
+ const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S;
+ const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S;
+
+ double RelDiff = statRelDiff(LeftStat, RightStat, EL);
+ return (RelDiff > 1.0) ? RelDiff : 1.0;
+ }
+}
+
+void GraphDiffRenderer::exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel,
+ StatType EdgeColor,
+ StatType VertexLabel,
+ StatType VertexColor, int TruncLen) {
+ // Get numbering of vertices for dot output.
+ StringMap<int32_t> VertexNo;
+
+ int i = 0;
+ for (const auto &V : G.vertices()) {
+ VertexNo[V.first] = i++;
+ }
+
+ ColorHelper H(ColorHelper::DivergingScheme::PiYG);
+
+ OS << "digraph xrayDiff {\n";
+
+ if (VertexLabel != StatType::NONE)
+ OS << "node [shape=record]\n";
+
+ for (const auto &E : G.edges()) {
+ const auto &HeadId = E.first.first;
+ const auto &TailId = E.first.second;
+ OS << formatv(R"(F{0} -> F{1} [tooltip="{2} -> {3}" label="{4}" )"
+ R"(color="{5}" labelfontcolor="{5}" penwidth={6}])"
+ "\n",
+ VertexNo[HeadId], VertexNo[TailId],
+ (HeadId.equals("")) ? static_cast<StringRef>("F0") : HeadId,
+ TailId, getLabel(E, EdgeLabel), getColor(E, G, H, EdgeColor),
+ getLineWidth(E, EdgeColor));
+ }
+
+ for (const auto &V : G.vertices()) {
+ const auto &VertexId = V.first;
+ if (VertexId.equals("")) {
+ OS << formatv(R"(F{0} [label="F0"])"
+ "\n",
+ VertexNo[VertexId]);
+ continue;
+ }
+ OS << formatv(R"(F{0} [label="{1}" color="{2}"])"
+ "\n",
+ VertexNo[VertexId], getLabel(V, VertexLabel, TruncLen),
+ getColor(V, G, H, VertexColor));
+ }
+
+ OS << "}\n";
+}
+
+template <typename T> 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 {
+ std::array<GraphRenderer::Factory, 2> Factories{
+ {{ifSpecified(GraphDiffKeepGoing1, GraphDiffKeepGoing1A,
+ GraphDiffKeepGoing),
+ ifSpecified(GraphDiffDeduceSiblingCalls1, GraphDiffDeduceSiblingCalls1A,
+ GraphDiffDeduceSiblingCalls),
+ ifSpecified(GraphDiffInstrMap1, GraphDiffInstrMap1A, GraphDiffInstrMap),
+ Trace()},
+ {ifSpecified(GraphDiffKeepGoing2, GraphDiffKeepGoing2A,
+ GraphDiffKeepGoing),
+ ifSpecified(GraphDiffDeduceSiblingCalls2, GraphDiffDeduceSiblingCalls2A,
+ GraphDiffDeduceSiblingCalls),
+ ifSpecified(GraphDiffInstrMap2, GraphDiffInstrMap2A, GraphDiffInstrMap),
+ Trace()}}};
+
+ std::array<std::string, 2> Inputs{{GraphDiffInput1, GraphDiffInput2}};
+
+ std::array<GraphRenderer::GraphT, 2> Graphs;
+
+ for (int i = 0; i < 2; i++) {
+ auto TraceOrErr = loadTraceFile(Inputs[i], true);
+ if (!TraceOrErr)
+ return make_error<StringError>(
+ Twine("Failed Loading Input File '") + Inputs[i] + "'",
+ make_error_code(llvm::errc::invalid_argument));
+ Factories[i].Trace = std::move(*TraceOrErr);
+
+ auto GraphRendererOrErr = Factories[i].getGraphRenderer();
+
+ if (!GraphRendererOrErr)
+ return GraphRendererOrErr.takeError();
+
+ auto GraphRenderer = *GraphRendererOrErr;
+
+ Graphs[i] = GraphRenderer.getGraph();
+ }
+
+ GraphDiffRenderer::Factory DGF(Graphs[0], Graphs[1]);
+
+ 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<StringError>(
+ Twine("Cannot open file '") + GraphDiffOutput + "' for writing.", EC);
+
+ GDR.exportGraphAsDOT(OS, GraphDiffEdgeLabel, GraphDiffEdgeColor,
+ GraphDiffVertexLabel, GraphDiffVertexColor,
+ GraphDiffVertexLabelTrunc);
+
+ return Error::success();
+});
diff --git a/contrib/llvm/tools/llvm-xray/xray-graph-diff.h b/contrib/llvm/tools/llvm-xray/xray-graph-diff.h
new file mode 100644
index 0000000..5abec91
--- /dev/null
+++ b/contrib/llvm/tools/llvm-xray/xray-graph-diff.h
@@ -0,0 +1,74 @@
+//===-- xray-graph-diff.h - XRay Graph Diff 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 difference between the function call
+// graph of two differnent traces.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef XRAY_GRAPH_DIFF_H
+#define XRAY_GRAPH_DIFF_H
+
+#include "xray-graph.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/XRay/Graph.h"
+
+namespace llvm {
+namespace xray {
+
+// This class creates a graph representing the difference between two
+// xray-graphs And allows you to print it to a dot file, with optional color
+// coding.
+class GraphDiffRenderer {
+ static const int N = 2;
+
+public:
+ using StatType = GraphRenderer::StatType;
+ using TimeStat = GraphRenderer::TimeStat;
+
+ using GREdgeValueType = GraphRenderer::GraphT::EdgeValueType;
+ using GRVertexValueType = GraphRenderer::GraphT::VertexValueType;
+
+ struct EdgeAttribute {
+ std::array<const GREdgeValueType *, N> CorrEdgePtr = {};
+ };
+
+ struct VertexAttribute {
+ std::array<const GRVertexValueType *, N> CorrVertexPtr = {};
+ };
+
+ using GraphT = Graph<VertexAttribute, EdgeAttribute, StringRef>;
+
+ class Factory {
+ std::array<std::reference_wrapper<const GraphRenderer::GraphT>, N> G;
+
+ public:
+ template <typename... Ts> Factory(Ts &... Args) : G{{Args...}} {}
+
+ Expected<GraphDiffRenderer> getGraphDiffRenderer();
+ };
+
+private:
+ GraphT G;
+
+ GraphDiffRenderer() = default;
+
+public:
+ void exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel = StatType::NONE,
+ StatType EdgeColor = StatType::NONE,
+ StatType VertexLabel = StatType::NONE,
+ StatType VertexColor = StatType::NONE,
+ int TruncLen = 40);
+
+ const GraphT &getGraph() { return G; }
+};
+} // namespace xray
+} // namespace llvm
+
+#endif
diff --git a/contrib/llvm/tools/llvm-xray/xray-graph.cc b/contrib/llvm/tools/llvm-xray/xray-graph.cc
new file mode 100644
index 0000000..685c24c
--- /dev/null
+++ b/contrib/llvm/tools/llvm-xray/xray-graph.cc
@@ -0,0 +1,521 @@
+//===-- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// Generate a DOT file to represent the function call graph encountered in
+// the trace.
+//
+//===----------------------------------------------------------------------===//
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <system_error>
+#include <utility>
+
+#include "xray-graph.h"
+#include "xray-registry.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/XRay/InstrumentationMap.h"
+#include "llvm/XRay/Trace.h"
+#include "llvm/XRay/YAMLXRayRecord.h"
+
+using namespace llvm;
+using namespace llvm::xray;
+
+// Setup llvm-xray graph subcommand and its options.
+static cl::SubCommand GraphC("graph", "Generate function-call graph");
+static cl::opt<std::string> GraphInput(cl::Positional,
+ cl::desc("<xray log file>"),
+ cl::Required, cl::sub(GraphC));
+
+static cl::opt<bool>
+ GraphKeepGoing("keep-going", cl::desc("Keep going on errors encountered"),
+ cl::sub(GraphC), cl::init(false));
+static cl::alias GraphKeepGoing2("k", cl::aliasopt(GraphKeepGoing),
+ cl::desc("Alias for -keep-going"),
+ cl::sub(GraphC));
+
+static cl::opt<std::string>
+ GraphOutput("output", cl::value_desc("Output file"), cl::init("-"),
+ cl::desc("output file; use '-' for stdout"), cl::sub(GraphC));
+static cl::alias GraphOutput2("o", cl::aliasopt(GraphOutput),
+ cl::desc("Alias for -output"), cl::sub(GraphC));
+
+static cl::opt<std::string>
+ GraphInstrMap("instr_map",
+ cl::desc("binary with the instrumrntation map, or "
+ "a separate instrumentation map"),
+ cl::value_desc("binary with xray_instr_map"), cl::sub(GraphC),
+ cl::init(""));
+static cl::alias GraphInstrMap2("m", cl::aliasopt(GraphInstrMap),
+ cl::desc("alias for -instr_map"),
+ cl::sub(GraphC));
+
+static cl::opt<bool> GraphDeduceSiblingCalls(
+ "deduce-sibling-calls",
+ cl::desc("Deduce sibling calls when unrolling function call stacks"),
+ cl::sub(GraphC), cl::init(false));
+static cl::alias
+ GraphDeduceSiblingCalls2("d", cl::aliasopt(GraphDeduceSiblingCalls),
+ cl::desc("Alias for -deduce-sibling-calls"),
+ cl::sub(GraphC));
+
+static cl::opt<GraphRenderer::StatType>
+ GraphEdgeLabel("edge-label",
+ cl::desc("Output graphs with edges labeled with this field"),
+ cl::value_desc("field"), cl::sub(GraphC),
+ 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 GraphEdgeLabel2("e", cl::aliasopt(GraphEdgeLabel),
+ cl::desc("Alias for -edge-label"),
+ cl::sub(GraphC));
+
+static cl::opt<GraphRenderer::StatType> GraphVertexLabel(
+ "vertex-label",
+ cl::desc("Output graphs with vertices labeled with this field"),
+ cl::value_desc("field"), cl::sub(GraphC),
+ 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 GraphVertexLabel2("v", cl::aliasopt(GraphVertexLabel),
+ cl::desc("Alias for -edge-label"),
+ cl::sub(GraphC));
+
+static cl::opt<GraphRenderer::StatType> GraphEdgeColorType(
+ "color-edges",
+ cl::desc("Output graphs with edge colors determined by this field"),
+ cl::value_desc("field"), cl::sub(GraphC),
+ 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 GraphEdgeColorType2("c", cl::aliasopt(GraphEdgeColorType),
+ cl::desc("Alias for -color-edges"),
+ cl::sub(GraphC));
+
+static cl::opt<GraphRenderer::StatType> GraphVertexColorType(
+ "color-vertices",
+ cl::desc("Output graphs with vertex colors determined by this field"),
+ cl::value_desc("field"), cl::sub(GraphC),
+ 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 GraphVertexColorType2("b", cl::aliasopt(GraphVertexColorType),
+ cl::desc("Alias for -edge-label"),
+ cl::sub(GraphC));
+
+template <class T> T diff(T L, T R) { return std::max(L, R) - std::min(L, R); }
+
+// Updates the statistics for a GraphRenderer::TimeStat
+static void updateStat(GraphRenderer::TimeStat &S, int64_t L) {
+ S.Count++;
+ if (S.Min > L || S.Min == 0)
+ S.Min = L;
+ if (S.Max < L)
+ S.Max = L;
+ S.Sum += L;
+}
+
+// Evaluates an XRay record and performs accounting on it.
+//
+// If the record is an ENTER record it pushes the FuncID and TSC onto a
+// structure representing the call stack for that function.
+// If the record is an EXIT record it checks computes computes the ammount of
+// time the function took to complete and then stores that information in an
+// edge of the graph. If there is no matching ENTER record the function tries
+// to recover by assuming that there were EXIT records which were missed, for
+// example caused by tail call elimination and if the option is enabled then
+// then tries to recover from this.
+//
+// This funciton will also error if the records are out of order, as the trace
+// is expected to be sorted.
+//
+// The graph generated has an immaginary root for functions called by no-one at
+// FuncId 0.
+//
+// FIXME: Refactor this and account subcommand to reduce code duplication.
+Error GraphRenderer::accountRecord(const XRayRecord &Record) {
+ using std::make_error_code;
+ using std::errc;
+ if (CurrentMaxTSC == 0)
+ CurrentMaxTSC = Record.TSC;
+
+ if (Record.TSC < CurrentMaxTSC)
+ return make_error<StringError>("Records not in order",
+ make_error_code(errc::invalid_argument));
+
+ auto &ThreadStack = PerThreadFunctionStack[Record.TId];
+ switch (Record.Type) {
+ case RecordTypes::ENTER: {
+ 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;
+ }
+ case RecordTypes::EXIT: {
+ // FIXME: Refactor this and the account subcommand to reduce code
+ // duplication
+ if (ThreadStack.size() == 0 || ThreadStack.back().FuncId != Record.FuncId) {
+ if (!DeduceSiblingCalls)
+ return make_error<StringError>("No matching ENTRY record",
+ make_error_code(errc::invalid_argument));
+ auto Parent = std::find_if(
+ ThreadStack.rbegin(), ThreadStack.rend(),
+ [&](const FunctionAttr &A) { return A.FuncId == Record.FuncId; });
+ if (Parent == ThreadStack.rend())
+ return make_error<StringError>(
+ "No matching Entry record in stack",
+ make_error_code(errc::invalid_argument)); // There is no matching
+ // Function for this exit.
+ while (ThreadStack.back().FuncId != Record.FuncId) {
+ TimestampT D = diff(ThreadStack.back().TSC, Record.TSC);
+ VertexIdentifier TopFuncId = ThreadStack.back().FuncId;
+ ThreadStack.pop_back();
+ assert(ThreadStack.size() != 0);
+ EdgeIdentifier EI(ThreadStack.back().FuncId, TopFuncId);
+ auto &EA = G[EI];
+ EA.Timings.push_back(D);
+ updateStat(EA.S, D);
+ updateStat(G[TopFuncId].S, D);
+ }
+ }
+ uint64_t D = diff(ThreadStack.back().TSC, Record.TSC);
+ ThreadStack.pop_back();
+ VertexIdentifier VI = ThreadStack.empty() ? 0 : ThreadStack.back().FuncId;
+ EdgeIdentifier EI(VI, Record.FuncId);
+ auto &EA = G[EI];
+ EA.Timings.push_back(D);
+ updateStat(EA.S, D);
+ updateStat(G[Record.FuncId].S, D);
+ break;
+ }
+ }
+
+ return Error::success();
+}
+
+template <typename U>
+void GraphRenderer::getStats(U begin, U end, GraphRenderer::TimeStat &S) {
+ if (begin == end) return;
+ std::ptrdiff_t MedianOff = S.Count / 2;
+ std::nth_element(begin, begin + MedianOff, end);
+ S.Median = *(begin + MedianOff);
+ std::ptrdiff_t Pct90Off = (S.Count * 9) / 10;
+ std::nth_element(begin, begin + Pct90Off, end);
+ S.Pct90 = *(begin + Pct90Off);
+ std::ptrdiff_t Pct99Off = (S.Count * 99) / 100;
+ std::nth_element(begin, begin + Pct99Off, end);
+ S.Pct99 = *(begin + Pct99Off);
+}
+
+void GraphRenderer::updateMaxStats(const GraphRenderer::TimeStat &S,
+ GraphRenderer::TimeStat &M) {
+ M.Count = std::max(M.Count, S.Count);
+ M.Min = std::max(M.Min, S.Min);
+ M.Median = std::max(M.Median, S.Median);
+ M.Pct90 = std::max(M.Pct90, S.Pct90);
+ M.Pct99 = std::max(M.Pct99, S.Pct99);
+ M.Max = std::max(M.Max, S.Max);
+ M.Sum = std::max(M.Sum, S.Sum);
+}
+
+void GraphRenderer::calculateEdgeStatistics() {
+ assert(!G.edges().empty());
+ for (auto &E : G.edges()) {
+ auto &A = E.second;
+ assert(!A.Timings.empty());
+ getStats(A.Timings.begin(), A.Timings.end(), A.S);
+ updateMaxStats(A.S, G.GraphEdgeMax);
+ }
+}
+
+void GraphRenderer::calculateVertexStatistics() {
+ std::vector<uint64_t> TempTimings;
+ for (auto &V : G.vertices()) {
+ if (V.first != 0) {
+ for (auto &E : G.inEdges(V.first)) {
+ auto &A = E.second;
+ TempTimings.insert(TempTimings.end(), A.Timings.begin(),
+ A.Timings.end());
+ }
+ getStats(TempTimings.begin(), TempTimings.end(), G[V.first].S);
+ updateMaxStats(G[V.first].S, G.GraphVertexMax);
+ TempTimings.clear();
+ }
+ }
+}
+
+// A Helper function for normalizeStatistics which normalises a single
+// TimeStat element.
+static void normalizeTimeStat(GraphRenderer::TimeStat &S,
+ double CycleFrequency) {
+ int64_t OldCount = S.Count;
+ S = S / CycleFrequency;
+ S.Count = OldCount;
+}
+
+// Normalises the statistics in the graph for a given TSC frequency.
+void GraphRenderer::normalizeStatistics(double CycleFrequency) {
+ for (auto &E : G.edges()) {
+ auto &S = E.second.S;
+ normalizeTimeStat(S, CycleFrequency);
+ }
+ for (auto &V : G.vertices()) {
+ auto &S = V.second.S;
+ normalizeTimeStat(S, CycleFrequency);
+ }
+
+ normalizeTimeStat(G.GraphEdgeMax, CycleFrequency);
+ normalizeTimeStat(G.GraphVertexMax, CycleFrequency);
+}
+
+// Returns a string containing the value of statistic field T
+std::string
+GraphRenderer::TimeStat::getString(GraphRenderer::StatType T) const {
+ std::string St;
+ raw_string_ostream S{St};
+ double TimeStat::*DoubleStatPtrs[] = {&TimeStat::Min, &TimeStat::Median,
+ &TimeStat::Pct90, &TimeStat::Pct99,
+ &TimeStat::Max, &TimeStat::Sum};
+ switch (T) {
+ case GraphRenderer::StatType::NONE:
+ break;
+ case GraphRenderer::StatType::COUNT:
+ S << Count;
+ break;
+ default:
+ S << (*this).*
+ DoubleStatPtrs[static_cast<int>(T) -
+ static_cast<int>(GraphRenderer::StatType::MIN)];
+ break;
+ }
+ return S.str();
+}
+
+// Returns the quotient between the property T of this and another TimeStat as
+// a double
+double GraphRenderer::TimeStat::getDouble(StatType T) const {
+ double retval = 0;
+ double TimeStat::*DoubleStatPtrs[] = {&TimeStat::Min, &TimeStat::Median,
+ &TimeStat::Pct90, &TimeStat::Pct99,
+ &TimeStat::Max, &TimeStat::Sum};
+ switch (T) {
+ case GraphRenderer::StatType::NONE:
+ retval = 0.0;
+ break;
+ case GraphRenderer::StatType::COUNT:
+ retval = static_cast<double>(Count);
+ break;
+ default:
+ retval =
+ (*this).*DoubleStatPtrs[static_cast<int>(T) -
+ static_cast<int>(GraphRenderer::StatType::MIN)];
+ break;
+ }
+ return retval;
+}
+
+// Outputs a DOT format version of the Graph embedded in the GraphRenderer
+// object on OS. It does this in the expected way by itterating
+// through all edges then vertices and then outputting them and their
+// annotations.
+//
+// FIXME: output more information, better presented.
+void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, StatType ET, StatType EC,
+ StatType VT, StatType VC) {
+ OS << "digraph xray {\n";
+
+ if (VT != StatType::NONE)
+ OS << "node [shape=record];\n";
+
+ for (const auto &E : G.edges()) {
+ const auto &S = E.second.S;
+ OS << "F" << E.first.first << " -> "
+ << "F" << E.first.second << " [label=\"" << S.getString(ET) << "\"";
+ if (EC != StatType::NONE)
+ OS << " color=\""
+ << CHelper.getColorString(
+ std::sqrt(S.getDouble(EC) / G.GraphEdgeMax.getDouble(EC)))
+ << "\"";
+ OS << "];\n";
+ }
+
+ for (const auto &V : G.vertices()) {
+ const auto &VA = V.second;
+ if (V.first == 0)
+ continue;
+ OS << "F" << V.first << " [label=\"" << (VT != StatType::NONE ? "{" : "")
+ << (VA.SymbolName.size() > 40 ? VA.SymbolName.substr(0, 40) + "..."
+ : VA.SymbolName);
+ if (VT != StatType::NONE)
+ OS << "|" << VA.S.getString(VT) << "}\"";
+ else
+ OS << "\"";
+ if (VC != StatType::NONE)
+ OS << " color=\""
+ << CHelper.getColorString(
+ std::sqrt(VA.S.getDouble(VC) / G.GraphVertexMax.getDouble(VC)))
+ << "\"";
+ OS << "];\n";
+ }
+ OS << "}\n";
+}
+
+Expected<GraphRenderer> GraphRenderer::Factory::getGraphRenderer() {
+ InstrumentationMap Map;
+ if (!GraphInstrMap.empty()) {
+ auto InstrumentationMapOrError = loadInstrumentationMap(GraphInstrMap);
+ if (!InstrumentationMapOrError)
+ return joinErrors(
+ make_error<StringError>(
+ Twine("Cannot open instrumentation map '") + GraphInstrMap + "'",
+ std::make_error_code(std::errc::invalid_argument)),
+ InstrumentationMapOrError.takeError());
+ Map = std::move(*InstrumentationMapOrError);
+ }
+
+ const auto &FunctionAddresses = Map.getFunctionAddresses();
+
+ symbolize::LLVMSymbolizer::Options Opts(
+ symbolize::FunctionNameKind::LinkageName, true, true, false, "");
+ symbolize::LLVMSymbolizer Symbolizer(Opts);
+ const auto &Header = Trace.getFileHeader();
+
+ llvm::xray::FuncIdConversionHelper FuncIdHelper(InstrMap, Symbolizer,
+ FunctionAddresses);
+
+ xray::GraphRenderer GR(FuncIdHelper, DeduceSiblingCalls);
+ for (const auto &Record : Trace) {
+ auto E = GR.accountRecord(Record);
+ if (!E)
+ continue;
+
+ for (const auto &ThreadStack : GR.getPerThreadFunctionStack()) {
+ errs() << "Thread ID: " << ThreadStack.first << "\n";
+ auto Level = ThreadStack.second.size();
+ for (const auto &Entry : llvm::reverse(ThreadStack.second))
+ errs() << "#" << Level-- << "\t"
+ << FuncIdHelper.SymbolOrNumber(Entry.FuncId) << '\n';
+ }
+
+ if (!GraphKeepGoing)
+ return joinErrors(make_error<StringError>(
+ "Error encountered generating the call graph.",
+ std::make_error_code(std::errc::invalid_argument)),
+ std::move(E));
+
+ handleAllErrors(std::move(E),
+ [&](const ErrorInfoBase &E) { E.log(errs()); });
+ }
+
+ 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<StringError>(
+ 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<StringError>(
+ Twine("Cannot open file '") + GraphOutput + "' for writing.", EC);
+
+ GR.exportGraphAsDOT(OS, GraphEdgeLabel, GraphEdgeColorType, GraphVertexLabel,
+ GraphVertexColorType);
+ return Error::success();
+});
diff --git a/contrib/llvm/tools/llvm-xray/xray-graph.h b/contrib/llvm/tools/llvm-xray/xray-graph.h
new file mode 100644
index 0000000..a43df26
--- /dev/null
+++ b/contrib/llvm/tools/llvm-xray/xray-graph.h
@@ -0,0 +1,233 @@
+//===-- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// Generate a DOT file to represent the function call graph encountered in
+// the trace.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef XRAY_GRAPH_H
+#define XRAY_GRAPH_H
+
+#include <string>
+#include <vector>
+
+#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"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/XRay/Graph.h"
+#include "llvm/XRay/Trace.h"
+#include "llvm/XRay/XRayRecord.h"
+
+namespace llvm {
+namespace xray {
+
+/// A class encapsulating the logic related to analyzing XRay traces, producting
+/// Graphs from them and then exporting those graphs for review.
+class GraphRenderer {
+public:
+ /// An enum for enumerating the various statistics gathered on latencies
+ enum class StatType { NONE, COUNT, MIN, MED, PCT90, PCT99, MAX, SUM };
+
+ /// An inner struct for common timing statistics information
+ struct TimeStat {
+ int64_t Count;
+ double Min;
+ double Median;
+ double Pct90;
+ double Pct99;
+ double Max;
+ double Sum;
+
+ std::string getString(StatType T) const;
+ double getDouble(StatType T) const;
+ };
+ using TimestampT = uint64_t;
+
+ /// An inner struct for storing edge attributes for our graph. Here the
+ /// attributes are mainly function call statistics.
+ ///
+ /// FIXME: expand to contain more information eg call latencies.
+ struct CallStats {
+ TimeStat S;
+ std::vector<TimestampT> Timings;
+ };
+
+ /// An Inner Struct for storing vertex attributes, at the moment just
+ /// SymbolNames, however in future we could store bulk function statistics.
+ ///
+ /// FIXME: Store more attributes based on instrumentation map.
+ struct FunctionStats {
+ std::string SymbolName;
+ TimeStat S = {};
+ };
+
+ struct FunctionAttr {
+ int32_t FuncId;
+ uint64_t TSC;
+ };
+
+ using FunctionStack = SmallVector<FunctionAttr, 4>;
+
+ using PerThreadFunctionStackMap =
+ DenseMap<llvm::sys::ProcessInfo::ProcessId, FunctionStack>;
+
+ class GraphT : public Graph<FunctionStats, CallStats, int32_t> {
+ public:
+ TimeStat GraphEdgeMax = {};
+ TimeStat GraphVertexMax = {};
+ };
+
+ GraphT G;
+ 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.
+ ///
+ /// FIXME: Perhaps we can Build this into LatencyAccountant? or vise versa?
+ PerThreadFunctionStackMap PerThreadFunctionStack;
+
+ /// Usefull object for getting human readable Symbol Names.
+ FuncIdConversionHelper FuncIdHelper;
+ bool DeduceSiblingCalls = false;
+ TimestampT CurrentMaxTSC = 0;
+
+ /// A private function to help implement the statistic generation functions;
+ template <typename U>
+ void getStats(U begin, U end, GraphRenderer::TimeStat &S);
+ void updateMaxStats(const TimeStat &S, TimeStat &M);
+
+ /// Calculates latency statistics for each edge and stores the data in the
+ /// Graph
+ void calculateEdgeStatistics();
+
+ /// Calculates latency statistics for each vertex and stores the data in the
+ /// Graph
+ void calculateVertexStatistics();
+
+ /// 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(const FuncIdConversionHelper &FuncIdHelper, bool DSC)
+ : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC),
+ CHelper(ColorHelper::SequentialScheme::OrRd) {
+ G[0] = {};
+ }
+
+ /// Process an Xray record and expand the graph.
+ ///
+ /// This Function will return true on success, or false if records are not
+ /// presented in per-thread call-tree DFS order. (That is for each thread the
+ /// Records should be in order runtime on an ideal system.)
+ ///
+ /// FIXME: Make this more robust against small irregularities.
+ Error accountRecord(const XRayRecord &Record);
+
+ const PerThreadFunctionStackMap &getPerThreadFunctionStack() const {
+ return PerThreadFunctionStack;
+ }
+
+ class Factory {
+ public:
+ bool KeepGoing;
+ bool DeduceSiblingCalls;
+ std::string InstrMap;
+ ::llvm::xray::Trace Trace;
+ Expected<GraphRenderer> getGraphRenderer();
+ };
+
+ /// Output the Embedded graph in DOT format on \p OS, labeling the edges by
+ /// \p T
+ 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() { 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<int64_t>(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<int64_t>(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};
+}
+} // namespace xray
+} // namespace llvm
+
+#endif // XRAY_GRAPH_H
diff --git a/contrib/llvm/tools/llvm-xray/xray-sleds.h b/contrib/llvm/tools/llvm-xray/xray-sleds.h
deleted file mode 100644
index 9927957..0000000
--- a/contrib/llvm/tools/llvm-xray/xray-sleds.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//===- xray-sleds.h - XRay Sleds Data Structure ---------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Defines the structure used to represent XRay instrumentation map entries.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_SLEDS_H
-#define LLVM_TOOLS_LLVM_XRAY_XRAY_SLEDS_H
-
-namespace llvm {
-namespace xray {
-
-struct SledEntry {
- enum class FunctionKinds { ENTRY, EXIT, TAIL };
-
- uint64_t Address;
- uint64_t Function;
- FunctionKinds Kind;
- bool AlwaysInstrument;
-};
-
-} // namespace xray
-} // namespace llvm
-
-#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_SLEDS_H
OpenPOWER on IntegriCloud