Imported Upstream version 5.18.0.207

Former-commit-id: 3b152f462918d427ce18620a2cbe4f8b79650449
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-11-17 08:23:10 +00:00
parent 8e12397d70
commit eb85e2fc17
28480 changed files with 72 additions and 3866936 deletions

View File

@ -1,21 +0,0 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
DebugInfoDWARF
Object
Support
Symbolize
XRay)
set(LLVM_XRAY_TOOLS
func-id-helper.cc
xray-account.cc
xray-color-helper.cc
xray-converter.cc
xray-extract.cc
xray-extract.cc
xray-graph.cc
xray-graph-diff.cc
xray-stacks.cc
xray-registry.cc)
add_llvm_tool(llvm-xray llvm-xray.cc ${LLVM_XRAY_TOOLS})

View File

@ -1,60 +0,0 @@
//===- xray-fc-account.cc - XRay Function Call Accounting Tool ------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation of the helper tools dealing with XRay-generated function ids.
//
//===----------------------------------------------------------------------===//
#include "func-id-helper.h"
#include "llvm/Support/Path.h"
#include <sstream>
using namespace llvm;
using namespace xray;
std::string FuncIdConversionHelper::SymbolOrNumber(int32_t FuncId) const {
std::ostringstream F;
auto It = FunctionAddresses.find(FuncId);
if (It == FunctionAddresses.end()) {
F << "#" << FuncId;
return F.str();
}
if (auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, It->second)) {
auto &DI = *ResOrErr;
if (DI.FunctionName == "<invalid>")
F << "@(" << std::hex << It->second << ")";
else
F << DI.FunctionName;
} else
handleAllErrors(ResOrErr.takeError(), [&](const ErrorInfoBase &) {
F << "@(" << std::hex << It->second << ")";
});
return F.str();
}
std::string FuncIdConversionHelper::FileLineAndColumn(int32_t FuncId) const {
auto It = FunctionAddresses.find(FuncId);
if (It == FunctionAddresses.end())
return "(unknown)";
std::ostringstream F;
auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, It->second);
if (!ResOrErr) {
consumeError(ResOrErr.takeError());
return "(unknown)";
}
auto &DI = *ResOrErr;
F << sys::path::filename(DI.FileName).str() << ":" << DI.Line << ":"
<< DI.Column;
return F.str();
}

View File

@ -1,49 +0,0 @@
//===- func-id-helper.h - XRay Function ID Conversion Helpers -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Defines helper tools dealing with XRay-generated function ids.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H
#define LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include <unordered_map>
namespace llvm {
namespace xray {
// This class consolidates common operations related to Function IDs.
class FuncIdConversionHelper {
public:
using FunctionAddressMap = std::unordered_map<int32_t, uint64_t>;
private:
std::string BinaryInstrMap;
symbolize::LLVMSymbolizer &Symbolizer;
const FunctionAddressMap &FunctionAddresses;
public:
FuncIdConversionHelper(std::string BinaryInstrMap,
symbolize::LLVMSymbolizer &Symbolizer,
const FunctionAddressMap &FunctionAddresses)
: BinaryInstrMap(std::move(BinaryInstrMap)), Symbolizer(Symbolizer),
FunctionAddresses(FunctionAddresses) {}
// Returns the symbol or a string representation of the function id.
std::string SymbolOrNumber(int32_t FuncId) const;
// Returns the file and column from debug info for the given function id.
std::string FileLineAndColumn(int32_t FuncId) const;
};
} // namespace xray
} // namespace llvm
#endif // LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H

View File

@ -1,49 +0,0 @@
//===- llvm-xray.cc - XRay Tool Main Program ------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the main entry point for the suite of XRay tools. All
// additional functionality are implemented as subcommands.
//
//===----------------------------------------------------------------------===//
//
// Basic usage:
//
// llvm-xray [options] <subcommand> [subcommand-specific options]
//
#include "xray-registry.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::xray;
int main(int argc, char *argv[]) {
cl::ParseCommandLineOptions(argc, argv,
"XRay Tools\n\n"
" This program consolidates multiple XRay trace "
"processing tools for convenient access.\n");
for (auto *SC : cl::getRegisteredSubcommands()) {
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);
return 0;
}

View File

@ -1,92 +0,0 @@
//===- trie-node.h - XRay Call Stack Data Structure -----------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file provides a data structure and routines for working with call stacks
// of instrumented functions.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_XRAY_STACK_TRIE_H
#define LLVM_TOOLS_LLVM_XRAY_STACK_TRIE_H
#include <forward_list>
#include <numeric>
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
/// A type to represent a trie of invocations. It is useful to construct a
/// graph of these nodes from reading an XRay trace, such that each function
/// call can be placed in a larger context.
///
/// The template parameter allows users of the template to attach their own
/// data elements to each node in the invocation graph.
template <typename AssociatedData> struct TrieNode {
/// The function ID.
int32_t FuncId;
/// The caller of this function.
TrieNode<AssociatedData> *Parent;
/// The callees from this function.
llvm::SmallVector<TrieNode<AssociatedData> *, 4> Callees;
/// Additional parameterized data on each node.
AssociatedData ExtraData;
};
/// Merges together two TrieNodes with like function ids, aggregating their
/// callee lists and durations. The caller must provide storage where new merged
/// nodes can be allocated in the form of a linked list.
template <typename T, typename Callable>
TrieNode<T> *
mergeTrieNodes(const TrieNode<T> &Left, const TrieNode<T> &Right,
/*Non-deduced pointer type for nullptr compatibility*/
typename std::remove_reference<TrieNode<T> *>::type NewParent,
std::forward_list<TrieNode<T>> &NodeStore,
Callable &&MergeCallable) {
llvm::function_ref<T(const T &, const T &)> MergeFn(
std::forward<Callable>(MergeCallable));
assert(Left.FuncId == Right.FuncId);
NodeStore.push_front(TrieNode<T>{
Left.FuncId, NewParent, {}, MergeFn(Left.ExtraData, Right.ExtraData)});
auto I = NodeStore.begin();
auto *Node = &*I;
// Build a map of callees from the left side.
llvm::DenseMap<int32_t, TrieNode<T> *> LeftCalleesByFuncId;
for (auto *Callee : Left.Callees) {
LeftCalleesByFuncId[Callee->FuncId] = Callee;
}
// Iterate through the right side, either merging with the map values or
// directly adding to the Callees vector. The iteration also removes any
// merged values from the left side map.
// TODO: Unroll into iterative and explicit stack for efficiency.
for (auto *Callee : Right.Callees) {
auto iter = LeftCalleesByFuncId.find(Callee->FuncId);
if (iter != LeftCalleesByFuncId.end()) {
Node->Callees.push_back(
mergeTrieNodes(*(iter->second), *Callee, Node, NodeStore, MergeFn));
LeftCalleesByFuncId.erase(iter);
} else {
Node->Callees.push_back(Callee);
}
}
// Add any callees that weren't found in the right side.
for (auto MapPairIter : LeftCalleesByFuncId) {
Node->Callees.push_back(MapPairIter.second);
}
return Node;
}
#endif // LLVM_TOOLS_LLVM_XRAY_STACK_TRIE_H

File diff suppressed because it is too large Load Diff

View File

@ -1,109 +0,0 @@
//===- xray-account.h - XRay Function Call Accounting ---------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the interface for performing some basic function call
// accounting from an XRay trace.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_ACCOUNT_H
#define LLVM_TOOLS_LLVM_XRAY_XRAY_ACCOUNT_H
#include <map>
#include <utility>
#include <vector>
#include "func-id-helper.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/XRay/XRayRecord.h"
namespace llvm {
namespace xray {
class LatencyAccountant {
public:
typedef std::map<int32_t, std::vector<uint64_t>> FunctionLatencyMap;
typedef std::map<llvm::sys::ProcessInfo::ProcessId,
std::pair<uint64_t, uint64_t>>
PerThreadMinMaxTSCMap;
typedef std::map<uint8_t, std::pair<uint64_t, uint64_t>> PerCPUMinMaxTSCMap;
typedef std::vector<std::pair<int32_t, uint64_t>> FunctionStack;
typedef std::map<llvm::sys::ProcessInfo::ProcessId, FunctionStack>
PerThreadFunctionStackMap;
private:
PerThreadFunctionStackMap PerThreadFunctionStack;
FunctionLatencyMap FunctionLatencies;
PerThreadMinMaxTSCMap PerThreadMinMaxTSC;
PerCPUMinMaxTSCMap PerCPUMinMaxTSC;
FuncIdConversionHelper &FuncIdHelper;
bool DeduceSiblingCalls = false;
uint64_t CurrentMaxTSC = 0;
void recordLatency(int32_t FuncId, uint64_t Latency) {
FunctionLatencies[FuncId].push_back(Latency);
}
public:
explicit LatencyAccountant(FuncIdConversionHelper &FuncIdHelper,
bool DeduceSiblingCalls)
: FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DeduceSiblingCalls) {}
const FunctionLatencyMap &getFunctionLatencies() const {
return FunctionLatencies;
}
const PerThreadMinMaxTSCMap &getPerThreadMinMaxTSC() const {
return PerThreadMinMaxTSC;
}
const PerCPUMinMaxTSCMap &getPerCPUMinMaxTSC() const {
return PerCPUMinMaxTSC;
}
/// Returns false in case we fail to account the provided record. This happens
/// in the following cases:
///
/// - An exit record does not match any entry records for the same function.
/// If we've been set to deduce sibling calls, we try walking up the stack
/// and recording times for the higher level functions.
/// - A record has a TSC that's before the latest TSC that has been
/// recorded. We still record the TSC for the min-max.
///
bool accountRecord(const XRayRecord &Record);
const FunctionStack *
getThreadFunctionStack(llvm::sys::ProcessInfo::ProcessId TId) const {
auto I = PerThreadFunctionStack.find(TId);
if (I == PerThreadFunctionStack.end())
return nullptr;
return &I->second;
}
const PerThreadFunctionStackMap &getPerThreadFunctionStack() const {
return PerThreadFunctionStack;
}
// Output Functions
// ================
void exportStatsAsText(raw_ostream &OS, const XRayFileHeader &Header) const;
void exportStatsAsCSV(raw_ostream &OS, const XRayFileHeader &Header) const;
private:
// Internal helper to implement common parts of the exportStatsAs...
// functions.
template <class F> void exportStats(const XRayFileHeader &Header, F fn) const;
};
} // namespace xray
} // namespace llvm
#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_ACCOUNT_H

View File

@ -1,222 +0,0 @@
//===-- 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 "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));
}

View File

@ -1,89 +0,0 @@
//===-- 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

View File

@ -1,393 +0,0 @@
//===- xray-converter.cc - XRay Trace Conversion --------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implements the trace conversion functions.
//
//===----------------------------------------------------------------------===//
#include "xray-converter.h"
#include "trie-node.h"
#include "xray-registry.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.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"
using namespace llvm;
using namespace xray;
// llvm-xray convert
// ----------------------------------------------------------------------------
static cl::SubCommand Convert("convert", "Trace Format Conversion");
static cl::opt<std::string> ConvertInput(cl::Positional,
cl::desc("<xray log file>"),
cl::Required, cl::sub(Convert));
enum class ConvertFormats { BINARY, YAML, CHROME_TRACE_EVENT };
static cl::opt<ConvertFormats> ConvertOutputFormat(
"output-format", cl::desc("output format"),
cl::values(clEnumValN(ConvertFormats::BINARY, "raw", "output in binary"),
clEnumValN(ConvertFormats::YAML, "yaml", "output in yaml"),
clEnumValN(ConvertFormats::CHROME_TRACE_EVENT, "trace_event",
"Output in chrome's trace event format. "
"May be visualized with the Catapult trace viewer.")),
cl::sub(Convert));
static cl::alias ConvertOutputFormat2("f", cl::aliasopt(ConvertOutputFormat),
cl::desc("Alias for -output-format"),
cl::sub(Convert));
static cl::opt<std::string>
ConvertOutput("output", cl::value_desc("output file"), cl::init("-"),
cl::desc("output file; use '-' for stdout"),
cl::sub(Convert));
static cl::alias ConvertOutput2("o", cl::aliasopt(ConvertOutput),
cl::desc("Alias for -output"),
cl::sub(Convert));
static cl::opt<bool>
ConvertSymbolize("symbolize",
cl::desc("symbolize function ids from the input log"),
cl::init(false), cl::sub(Convert));
static cl::alias ConvertSymbolize2("y", cl::aliasopt(ConvertSymbolize),
cl::desc("Alias for -symbolize"),
cl::sub(Convert));
static cl::opt<std::string>
ConvertInstrMap("instr_map",
cl::desc("binary with the instrumentation map, or "
"a separate instrumentation map"),
cl::value_desc("binary with xray_instr_map"),
cl::sub(Convert), cl::init(""));
static cl::alias ConvertInstrMap2("m", cl::aliasopt(ConvertInstrMap),
cl::desc("Alias for -instr_map"),
cl::sub(Convert));
static cl::opt<bool> ConvertSortInput(
"sort",
cl::desc("determines whether to sort input log records by timestamp"),
cl::sub(Convert), cl::init(true));
static cl::alias ConvertSortInput2("s", cl::aliasopt(ConvertSortInput),
cl::desc("Alias for -sort"),
cl::sub(Convert));
using llvm::yaml::Output;
void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) {
YAMLXRayTrace Trace;
const auto &FH = Records.getFileHeader();
Trace.Header = {FH.Version, FH.Type, FH.ConstantTSC, FH.NonstopTSC,
FH.CycleFrequency};
Trace.Records.reserve(Records.size());
for (const auto &R : Records) {
Trace.Records.push_back({R.RecordType, R.CPU, R.Type, R.FuncId,
Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId)
: llvm::to_string(R.FuncId),
R.TSC, R.TId, R.CallArgs});
}
Output Out(OS, nullptr, 0);
Out << Trace;
}
void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) {
// First write out the file header, in the correct endian-appropriate format
// (XRay assumes currently little endian).
support::endian::Writer<support::endianness::little> Writer(OS);
const auto &FH = Records.getFileHeader();
Writer.write(FH.Version);
Writer.write(FH.Type);
uint32_t Bitfield{0};
if (FH.ConstantTSC)
Bitfield |= 1uL;
if (FH.NonstopTSC)
Bitfield |= 1uL << 1;
Writer.write(Bitfield);
Writer.write(FH.CycleFrequency);
// There's 16 bytes of padding at the end of the file header.
static constexpr uint32_t Padding4B = 0;
Writer.write(Padding4B);
Writer.write(Padding4B);
Writer.write(Padding4B);
Writer.write(Padding4B);
// Then write out the rest of the records, still in an endian-appropriate
// format.
for (const auto &R : Records) {
Writer.write(R.RecordType);
// 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:
case RecordTypes::ENTER_ARG:
Writer.write(uint8_t{0});
break;
case RecordTypes::EXIT:
Writer.write(uint8_t{1});
break;
case RecordTypes::TAIL_EXIT:
Writer.write(uint8_t{2});
break;
}
Writer.write(R.FuncId);
Writer.write(R.TSC);
Writer.write(R.TId);
Writer.write(Padding4B);
Writer.write(Padding4B);
Writer.write(Padding4B);
}
}
namespace {
// A structure that allows building a dictionary of stack ids for the Chrome
// trace event format.
struct StackIdData {
// Each Stack of function calls has a unique ID.
unsigned id;
// Bookkeeping so that IDs can be maintained uniquely across threads.
// Traversal keeps sibling pointers to other threads stacks. This is helpful
// to determine when a thread encounters a new stack and should assign a new
// unique ID.
SmallVector<TrieNode<StackIdData> *, 4> siblings;
};
using StackTrieNode = TrieNode<StackIdData>;
// A helper function to find the sibling nodes for an encountered function in a
// thread of execution. Relies on the invariant that each time a new node is
// traversed in a thread, sibling bidirectional pointers are maintained.
SmallVector<StackTrieNode *, 4>
findSiblings(StackTrieNode *parent, int32_t FnId, uint32_t TId,
const DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>>
&StackRootsByThreadId) {
SmallVector<StackTrieNode *, 4> Siblings{};
if (parent == nullptr) {
for (auto map_iter : StackRootsByThreadId) {
// Only look for siblings in other threads.
if (map_iter.first != TId)
for (auto node_iter : map_iter.second) {
if (node_iter->FuncId == FnId)
Siblings.push_back(node_iter);
}
}
return Siblings;
}
for (auto *ParentSibling : parent->ExtraData.siblings)
for (auto node_iter : ParentSibling->Callees)
if (node_iter->FuncId == FnId)
Siblings.push_back(node_iter);
return Siblings;
}
// Given a function being invoked in a thread with id TId, finds and returns the
// StackTrie representing the function call stack. If no node exists, creates
// the node. Assigns unique IDs to stacks newly encountered among all threads
// and keeps sibling links up to when creating new nodes.
StackTrieNode *findOrCreateStackNode(
StackTrieNode *Parent, int32_t FuncId, uint32_t TId,
DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> &StackRootsByThreadId,
DenseMap<unsigned, StackTrieNode *> &StacksByStackId, unsigned *id_counter,
std::forward_list<StackTrieNode> &NodeStore) {
SmallVector<StackTrieNode *, 4> &ParentCallees =
Parent == nullptr ? StackRootsByThreadId[TId] : Parent->Callees;
auto match = find_if(ParentCallees, [FuncId](StackTrieNode *ParentCallee) {
return FuncId == ParentCallee->FuncId;
});
if (match != ParentCallees.end())
return *match;
SmallVector<StackTrieNode *, 4> siblings =
findSiblings(Parent, FuncId, TId, StackRootsByThreadId);
if (siblings.empty()) {
NodeStore.push_front({FuncId, Parent, {}, {(*id_counter)++, {}}});
StackTrieNode *CurrentStack = &NodeStore.front();
StacksByStackId[*id_counter - 1] = CurrentStack;
ParentCallees.push_back(CurrentStack);
return CurrentStack;
}
unsigned stack_id = siblings[0]->ExtraData.id;
NodeStore.push_front({FuncId, Parent, {}, {stack_id, std::move(siblings)}});
StackTrieNode *CurrentStack = &NodeStore.front();
for (auto *sibling : CurrentStack->ExtraData.siblings)
sibling->ExtraData.siblings.push_back(CurrentStack);
ParentCallees.push_back(CurrentStack);
return CurrentStack;
}
void writeTraceViewerRecord(raw_ostream &OS, int32_t FuncId, uint32_t TId,
bool Symbolize,
const FuncIdConversionHelper &FuncIdHelper,
double EventTimestampUs,
const StackTrieNode &StackCursor,
StringRef FunctionPhenotype) {
OS << " ";
OS << llvm::formatv(
R"({ "name" : "{0}", "ph" : "{1}", "tid" : "{2}", "pid" : "1", )"
R"("ts" : "{3:f3}", "sf" : "{4}" })",
(Symbolize ? FuncIdHelper.SymbolOrNumber(FuncId)
: llvm::to_string(FuncId)),
FunctionPhenotype, TId, EventTimestampUs, StackCursor.ExtraData.id);
}
} // namespace
void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
raw_ostream &OS) {
const auto &FH = Records.getFileHeader();
auto CycleFreq = FH.CycleFrequency;
unsigned id_counter = 0;
OS << "{\n \"traceEvents\": [";
DenseMap<uint32_t, StackTrieNode *> StackCursorByThreadId{};
DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> StackRootsByThreadId{};
DenseMap<unsigned, StackTrieNode *> StacksByStackId{};
std::forward_list<StackTrieNode> NodeStore{};
int loop_count = 0;
for (const auto &R : Records) {
if (loop_count++ == 0)
OS << "\n";
else
OS << ",\n";
// Chrome trace event format always wants data in micros.
// CyclesPerMicro = CycleHertz / 10^6
// TSC / CyclesPerMicro == TSC * 10^6 / CycleHertz == MicroTimestamp
// Could lose some precision here by converting the TSC to a double to
// multiply by the period in micros. 52 bit mantissa is a good start though.
// TODO: Make feature request to Chrome Trace viewer to accept ticks and a
// frequency or do some more involved calculation to avoid dangers of
// conversion.
double EventTimestampUs = double(1000000) / CycleFreq * double(R.TSC);
StackTrieNode *&StackCursor = StackCursorByThreadId[R.TId];
switch (R.Type) {
case RecordTypes::ENTER:
case RecordTypes::ENTER_ARG:
StackCursor = findOrCreateStackNode(StackCursor, R.FuncId, R.TId,
StackRootsByThreadId, StacksByStackId,
&id_counter, NodeStore);
// Each record is represented as a json dictionary with function name,
// type of B for begin or E for end, thread id, process id (faked),
// timestamp in microseconds, and a stack frame id. The ids are logged
// in an id dictionary after the events.
writeTraceViewerRecord(OS, R.FuncId, R.TId, Symbolize, FuncIdHelper,
EventTimestampUs, *StackCursor, "B");
break;
case RecordTypes::EXIT:
case RecordTypes::TAIL_EXIT:
// No entries to record end for.
if (StackCursor == nullptr)
break;
// Should we emit an END record anyway or account this condition?
// (And/Or in loop termination below)
StackTrieNode *PreviousCursor = nullptr;
do {
writeTraceViewerRecord(OS, StackCursor->FuncId, R.TId, Symbolize,
FuncIdHelper, EventTimestampUs, *StackCursor,
"E");
PreviousCursor = StackCursor;
StackCursor = StackCursor->Parent;
} while (PreviousCursor->FuncId != R.FuncId && StackCursor != nullptr);
break;
}
}
OS << "\n ],\n"; // Close the Trace Events array.
OS << " "
<< "\"displayTimeUnit\": \"ns\",\n";
// The stackFrames dictionary substantially reduces size of the output file by
// avoiding repeating the entire call stack of function names for each entry.
OS << R"( "stackFrames": {)";
int stack_frame_count = 0;
for (auto map_iter : StacksByStackId) {
if (stack_frame_count++ == 0)
OS << "\n";
else
OS << ",\n";
OS << " ";
OS << llvm::formatv(
R"("{0}" : { "name" : "{1}")", map_iter.first,
(Symbolize ? FuncIdHelper.SymbolOrNumber(map_iter.second->FuncId)
: llvm::to_string(map_iter.second->FuncId)));
if (map_iter.second->Parent != nullptr)
OS << llvm::formatv(R"(, "parent": "{0}")",
map_iter.second->Parent->ExtraData.id);
OS << " }";
}
OS << "\n }\n"; // Close the stack frames map.
OS << "}\n"; // Close the JSON entry.
}
namespace llvm {
namespace xray {
static CommandRegistration Unused(&Convert, []() -> Error {
// FIXME: Support conversion to BINARY when upgrading XRay trace versions.
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 = 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
: sys::fs::OpenFlags::F_Text);
if (EC)
return make_error<StringError>(
Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC);
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;
case ConvertFormats::CHROME_TRACE_EVENT:
TC.exportAsChromeTraceEventFormat(T, OS);
break;
}
return Error::success();
});
} // namespace xray
} // namespace llvm

View File

@ -1,44 +0,0 @@
//===- xray-converter.h - XRay Trace Conversion ---------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Defines the TraceConverter class for turning binary traces into
// human-readable text and vice versa.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_CONVERTER_H
#define LLVM_TOOLS_LLVM_XRAY_XRAY_CONVERTER_H
#include "func-id-helper.h"
#include "llvm/XRay/Trace.h"
#include "llvm/XRay/XRayRecord.h"
namespace llvm {
namespace xray {
class TraceConverter {
FuncIdConversionHelper &FuncIdHelper;
bool Symbolize;
public:
TraceConverter(FuncIdConversionHelper &FuncIdHelper, bool Symbolize = false)
: FuncIdHelper(FuncIdHelper), Symbolize(Symbolize) {}
void exportAsYAML(const Trace &Records, raw_ostream &OS);
void exportAsRAWv1(const Trace &Records, raw_ostream &OS);
/// For this conversion, the Function records within each thread are expected
/// to be in sorted TSC order. The trace event format encodes stack traces, so
/// the linear history is essential for correct output.
void exportAsChromeTraceEventFormat(const Trace &Records, raw_ostream &OS);
};
} // namespace xray
} // namespace llvm
#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_CONVERTER_H

View File

@ -1,97 +0,0 @@
//===- xray-extract.cc - 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.
//
//===----------------------------------------------------------------------===//
//
// Implementation of the xray-extract.h interface.
//
// FIXME: Support other XRay-instrumented binary formats other than ELF.
//
//===----------------------------------------------------------------------===//
#include "func-id-helper.h"
#include "xray-registry.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/XRay/InstrumentationMap.h"
using namespace llvm;
using namespace llvm::xray;
using namespace llvm::yaml;
// llvm-xray extract
// ----------------------------------------------------------------------------
static cl::SubCommand Extract("extract", "Extract instrumentation maps");
static cl::opt<std::string> ExtractInput(cl::Positional,
cl::desc("<input file>"), cl::Required,
cl::sub(Extract));
static cl::opt<std::string>
ExtractOutput("output", cl::value_desc("output file"), cl::init("-"),
cl::desc("output file; use '-' for stdout"),
cl::sub(Extract));
static cl::alias ExtractOutput2("o", cl::aliasopt(ExtractOutput),
cl::desc("Alias for -output"),
cl::sub(Extract));
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 {
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;
auto Sleds = Map.sleds();
YAMLSleds.reserve(std::distance(Sleds.begin(), Sleds.end()));
for (const auto &Sled : Sleds) {
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 {
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);
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();
});

View File

@ -1,484 +0,0 @@
//===-- 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();
});

View File

@ -1,74 +0,0 @@
//===-- 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

File diff suppressed because it is too large Load Diff

View File

@ -1,233 +0,0 @@
//===-- 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

View File

@ -1,41 +0,0 @@
//===- xray-registry.cc - Implement a command registry. -------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implement a simple subcommand registry.
//
//===----------------------------------------------------------------------===//
#include "xray-registry.h"
#include "llvm/Support/ManagedStatic.h"
#include <unordered_map>
namespace llvm {
namespace xray {
using HandlerType = std::function<Error()>;
ManagedStatic<std::unordered_map<cl::SubCommand *, HandlerType>> Commands;
CommandRegistration::CommandRegistration(cl::SubCommand *SC,
HandlerType Command) {
assert(Commands->count(SC) == 0 &&
"Attempting to overwrite a command handler");
assert(Command && "Attempting to register an empty std::function<Error()>");
(*Commands)[SC] = Command;
}
HandlerType dispatch(cl::SubCommand *SC) {
auto It = Commands->find(SC);
assert(It != Commands->end() &&
"Attempting to dispatch on un-registered SubCommand.");
return It->second;
}
} // namespace xray
} // namespace llvm

View File

@ -1,41 +0,0 @@
//===- xray-registry.h - Define registry mechanism for commands. ----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implement a simple subcommand registry.
//
//===----------------------------------------------------------------------===//
#ifndef TOOLS_LLVM_XRAY_XRAY_REGISTRY_H
#define TOOLS_LLVM_XRAY_XRAY_REGISTRY_H
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
namespace llvm {
namespace xray {
// Use |CommandRegistration| as a global initialiser that registers a function
// and associates it with |SC|. This requires that a command has not been
// registered to a given |SC|.
//
// Usage:
//
// // At namespace scope.
// static CommandRegistration Unused(&MySubCommand, [] { ... });
//
struct CommandRegistration {
CommandRegistration(cl::SubCommand *SC, std::function<Error()> Command);
};
// Requires that |SC| is not null and has an associated function to it.
std::function<Error()> dispatch(cl::SubCommand *SC);
} // namespace xray
} // namespace llvm
#endif // TOOLS_LLVM_XRAY_XRAY_REGISTRY_H

File diff suppressed because it is too large Load Diff