summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/llvm-xray
diff options
context:
space:
mode:
authordim <dim@FreeBSD.org>2017-09-26 19:56:36 +0000
committerdim <dim@FreeBSD.org>2017-09-26 19:56:36 +0000
commit12cd91cf4c6b96a24427c0de5374916f2808d263 (patch)
tree6d243b0ccba6738dbbd30767188e2963f90ef18f /contrib/llvm/tools/llvm-xray
parentb60520398f206195e21774c315afb59a0f6d7146 (diff)
downloadFreeBSD-src-12cd91cf4c6b96a24427c0de5374916f2808d263.zip
FreeBSD-src-12cd91cf4c6b96a24427c0de5374916f2808d263.tar.gz
Merge clang, llvm, lld, lldb, compiler-rt and libc++ 5.0.0 release.
MFC r309126 (by emaste): Correct lld llvm-tblgen dependency file name MFC r309169: Get rid of separate Subversion mergeinfo properties for llvm-dwarfdump and llvm-lto. The mergeinfo confuses Subversion enormously, and these directories will just use the mergeinfo for llvm itself. MFC r312765: Pull in r276136 from upstream llvm trunk (by Wei Mi): Use ValueOffsetPair to enhance value reuse during SCEV expansion. In D12090, the ExprValueMap was added to reuse existing value during SCEV expansion. However, const folding and sext/zext distribution can make the reuse still difficult. A simplified case is: suppose we know S1 expands to V1 in ExprValueMap, and S1 = S2 + C_a S3 = S2 + C_b where C_a and C_b are different SCEVConstants. Then we'd like to expand S3 as V1 - C_a + C_b instead of expanding S2 literally. It is helpful when S2 is a complex SCEV expr and S2 has no entry in ExprValueMap, which is usually caused by the fact that S3 is generated from S1 after const folding. In order to do that, we represent ExprValueMap as a mapping from SCEV to ValueOffsetPair. We will save both S1->{V1, 0} and S2->{V1, C_a} into the ExprValueMap when we create SCEV for V1. When S3 is expanded, it will first expand S2 to V1 - C_a because of S2->{V1, C_a} in the map, then expand S3 to V1 - C_a + C_b. Differential Revision: https://reviews.llvm.org/D21313 This should fix assertion failures when building OpenCV >= 3.1. PR: 215649 MFC r312831: Revert r312765 for now, since it causes assertions when building lang/spidermonkey24. Reported by: antoine PR: 215649 MFC r316511 (by jhb): Add an implementation of __ffssi2() derived from __ffsdi2(). Newer versions of GCC include an __ffssi2() symbol in libgcc and the compiler can emit calls to it in generated code. This is true for at least GCC 6.2 when compiling world for mips and mips64. Reviewed by: jmallett, dim Sponsored by: DARPA / AFRL Differential Revision: https://reviews.freebsd.org/D10086 MFC r318601 (by adrian): [libcompiler-rt] add bswapdi2/bswapsi2 This is required for mips gcc 6.3 userland to build/run. Reviewed by: emaste, dim Approved by: emaste Differential Revision: https://reviews.freebsd.org/D10838 MFC r318884 (by emaste): lldb: map TRAP_CAP to a trace trap In the absense of a more specific handler for TRAP_CAP (generated by ENOTCAPABLE or ECAPMODE while in capability mode) treat it as a trace trap. Example usage (testing the bug in PR219173): % proccontrol -m trapcap lldb usr.bin/hexdump/obj/hexdump -- -Cv -s 1 /bin/ls ... (lldb) run Process 12980 launching Process 12980 launched: '.../usr.bin/hexdump/obj/hexdump' (x86_64) Process 12980 stopped * thread #1, stop reason = trace frame #0: 0x0000004b80c65f1a libc.so.7`__sys_lseek + 10 ... In the future we should have LLDB control the trapcap procctl itself (as it does with ASLR), as well as report a specific stop reason. This change eliminates an assertion failure from LLDB for now. MFC r319796: Remove a few unneeded files from libllvm, libclang and liblldb. MFC r319885 (by emaste): lld: ELF: Fix ICF crash on absolute symbol relocations. If two sections contained relocations to absolute symbols with the same value we would crash when trying to access their sections. Add a check that both symbols point to sections before accessing their sections, and treat absolute symbols as equal if their values are equal. Obtained from: LLD commit r292578 MFC r319918: Revert r319796 for now, it can cause undefined references when linking in some circumstances. Reported by: Shawn Webb <shawn.webb@hardenedbsd.org> MFC r319957 (by emaste): lld: Add armelf emulation mode Obtained from: LLD r305375 MFC r321369: Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to 5.0.0 (trunk r308421). Upstream has branched for the 5.0.0 release, which should be in about a month. Please report bugs and regressions, so we can get them into the release. Please note that from 3.5.0 onwards, clang, llvm and lldb require C++11 support to build; see UPDATING for more information. MFC r321420: Add a few more object files to liblldb, which should solve errors when linking the lldb executable in some cases. In particular, when the -ffunction-sections -fdata-sections options are turned off, or ineffective. Reported by: Shawn Webb, Mark Millard MFC r321433: Cleanup stale Options.inc files from the previous libllvm build for clang 4.0.0. Otherwise, these can get included before the two newly generated ones (which are different) for clang 5.0.0. Reported by: Mark Millard MFC r321439 (by bdrewery): Move llvm Options.inc hack from r321433 for NO_CLEAN to lib/clang/libllvm. The files are only ever generated to .OBJDIR, not to WORLDTMP (as a sysroot) and are only ever included from a compilation. So using a beforebuild target here removes the file before the compilation tries to include it. MFC r321664: Pull in r308891 from upstream llvm trunk (by Benjamin Kramer): [CodeGenPrepare] Cut off FindAllMemoryUses if there are too many uses. This avoids excessive compile time. The case I'm looking at is Function.cpp from an old version of LLVM that still had the giant memcmp string matcher in it. Before r308322 this compiled in about 2 minutes, after it, clang takes infinite* time to compile it. With this patch we're at 5 min, which is still bad but this is a pathological case. The cut off at 20 uses was chosen by looking at other cut-offs in LLVM for user scanning. It's probably too high, but does the job and is very unlikely to regress anything. Fixes PR33900. * I'm impatient and aborted after 15 minutes, on the bug report it was killed after 2h. Pull in r308986 from upstream llvm trunk (by Simon Pilgrim): [X86][CGP] Reduce memcmp() expansion to 2 load pairs (PR33914) D35067/rL308322 attempted to support up to 4 load pairs for memcmp inlining which resulted in regressions for some optimized libc memcmp implementations (PR33914). Until we can match these more optimal cases, this patch reduces the memcmp expansion to a maximum of 2 load pairs (which matches what we do for -Os). This patch should be considered for the 5.0.0 release branch as well Differential Revision: https://reviews.llvm.org/D35830 These fix a hang (or extremely long compile time) when building older LLVM ports. Reported by: antoine PR: 219139 MFC r321719: Pull in r309503 from upstream clang trunk (by Richard Smith): PR33902: Invalidate line number cache when adding more text to existing buffer. This led to crashes as the line number cache would report a bogus line number for a line of code, and we'd try to find a nonexistent column within the line when printing diagnostics. This fixes an assertion when building the graphics/champlain port. Reported by: antoine, kwm PR: 219139 MFC r321723: Upgrade our copies of clang, llvm, lld and lldb to r309439 from the upstream release_50 branch. This is just after upstream's 5.0.0-rc1. MFC r322320: Upgrade our copies of clang, llvm and libc++ to r310316 from the upstream release_50 branch. MFC r322326 (by emaste): lldb: Make i386-*-freebsd expression work on JIT path * Enable i386 ABI creation for freebsd * Added an extra argument in ABISysV_i386::PrepareTrivialCall for mmap syscall * Unlike linux, the last argument of mmap is actually 64-bit(off_t). This requires us to push an additional word for the higher order bits. * Prior to this change, ktrace dump will show mmap failures due to invalid argument coming from the 6th mmap argument. Submitted by: Karnajit Wangkhem Differential Revision: https://reviews.llvm.org/D34776 MFC r322360 (by emaste): lldb: Report inferior signals as signals, not exceptions, on FreeBSD This is the FreeBSD equivalent of LLVM r238549. This serves 2 purposes: * LLDB should handle inferior process signals SIGSEGV/SIGILL/SIGBUS/ SIGFPE the way it is suppose to be handled. Prior to this fix these signals will neither create a coredump, nor exit from the debugger or work for signal handling scenario. * eInvalidCrashReason need not report "unknown crash reason" if we have a valid si_signo llvm.org/pr23699 Patch by Karnajit Wangkhem Differential Revision: https://reviews.llvm.org/D35223 Submitted by: Karnajit Wangkhem Obtained from: LLVM r310591 MFC r322474 (by emaste): lld: Add `-z muldefs` option. Obtained from: LLVM r310757 MFC r322740: Upgrade our copies of clang, llvm, lld and libc++ to r311219 from the upstream release_50 branch. MFC r322855: Upgrade our copies of clang, llvm, lldb and compiler-rt to r311606 from the upstream release_50 branch. As of this version, lib/msun's trig test should also work correctly again (see bug 220989 for more information). PR: 220989 MFC r323112: Upgrade our copies of clang, llvm, lldb and compiler-rt to r312293 from the upstream release_50 branch. This corresponds to 5.0.0 rc4. As of this version, the cad/stepcode port should now compile in a more reasonable time on i386 (see bug 221836 for more information). PR: 221836 MFC r323245: Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to 5.0.0 release (upstream r312559). Release notes for llvm, clang and lld will be available here soon: <http://releases.llvm.org/5.0.0/docs/ReleaseNotes.html> <http://releases.llvm.org/5.0.0/tools/clang/docs/ReleaseNotes.html> <http://releases.llvm.org/5.0.0/tools/lld/docs/ReleaseNotes.html> Relnotes: yes
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