summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/LLVMBuild.txt1
-rw-r--r--tools/dsymutil/DwarfLinker.cpp8
-rw-r--r--tools/llc/llc.cpp6
-rw-r--r--tools/llvm-cvtres/CMakeLists.txt13
-rw-r--r--tools/llvm-cvtres/LLVMBuild.txt22
-rw-r--r--tools/llvm-cvtres/Opts.td11
-rw-r--r--tools/llvm-cvtres/llvm-cvtres.cpp86
-rw-r--r--tools/llvm-cvtres/llvm-cvtres.h13
-rw-r--r--tools/llvm-objdump/llvm-objdump.cpp5
-rw-r--r--tools/llvm-pdbdump/CMakeLists.txt1
-rw-r--r--tools/llvm-pdbdump/LinePrinter.cpp2
-rw-r--r--tools/llvm-pdbdump/PdbYaml.cpp92
-rw-r--r--tools/llvm-pdbdump/PdbYaml.h93
-rw-r--r--tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp67
-rw-r--r--tools/llvm-pdbdump/PrettyClassDefinitionDumper.h2
-rw-r--r--tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp142
-rw-r--r--tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h17
-rw-r--r--tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp119
-rw-r--r--tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h44
-rw-r--r--tools/llvm-pdbdump/PrettyTypeDumper.cpp44
-rw-r--r--tools/llvm-pdbdump/PrettyVariableDumper.cpp8
-rw-r--r--tools/llvm-pdbdump/PrettyVariableDumper.h1
-rw-r--r--tools/llvm-pdbdump/YAMLOutputStyle.cpp136
-rw-r--r--tools/llvm-pdbdump/YAMLOutputStyle.h5
-rw-r--r--tools/llvm-pdbdump/llvm-pdbdump.cpp45
-rw-r--r--tools/llvm-pdbdump/llvm-pdbdump.h16
-rw-r--r--tools/llvm-xray/CMakeLists.txt1
-rw-r--r--tools/llvm-xray/xray-color-helper.cc34
-rw-r--r--tools/llvm-xray/xray-color-helper.h12
-rw-r--r--tools/llvm-xray/xray-graph-diff.cc484
-rw-r--r--tools/llvm-xray/xray-graph-diff.h74
-rw-r--r--tools/llvm-xray/xray-graph.cc196
-rw-r--r--tools/llvm-xray/xray-graph.h117
33 files changed, 1504 insertions, 413 deletions
diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt
index 65d3d6fad16eb..e3041a6d40d4e 100644
--- a/tools/LLVMBuild.txt
+++ b/tools/LLVMBuild.txt
@@ -26,6 +26,7 @@ subdirectories =
llvm-bcanalyzer
llvm-cat
llvm-cov
+ llvm-cvtres
llvm-diff
llvm-dis
llvm-dwarfdump
diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp
index 6ee052f101f93..f74d721e61493 100644
--- a/tools/dsymutil/DwarfLinker.cpp
+++ b/tools/dsymutil/DwarfLinker.cpp
@@ -223,7 +223,7 @@ public:
DIE *getOutputUnitDIE() const {
if (NewUnit)
- return &const_cast<DIEUnit &>(*NewUnit).getUnitDie();
+ return &const_cast<BasicDIEUnit &>(*NewUnit).getUnitDie();
return nullptr;
}
@@ -333,7 +333,7 @@ private:
DWARFUnit &OrigUnit;
unsigned ID;
std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index.
- Optional<DIEUnit> NewUnit;
+ Optional<BasicDIEUnit> NewUnit;
uint64_t StartOffset;
uint64_t NextUnitOffset;
@@ -2869,7 +2869,7 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit,
DWARFDebugRangeList RangeList;
const auto &FunctionRanges = Unit.getFunctionRanges();
unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize();
- DataExtractor RangeExtractor(OrigDwarf.getRangeSection(),
+ DataExtractor RangeExtractor(OrigDwarf.getRangeSection().Data,
OrigDwarf.isLittleEndian(), AddressSize);
auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange;
DWARFUnit &OrigUnit = Unit.getOrigUnit();
@@ -2884,7 +2884,7 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit,
for (const auto &RangeAttribute : Unit.getRangesAttributes()) {
uint32_t Offset = RangeAttribute.get();
RangeAttribute.set(Streamer->getRangesSectionSize());
- RangeList.extract(RangeExtractor, &Offset);
+ RangeList.extract(RangeExtractor, &Offset, OrigDwarf.getRangeSection().Relocs);
const auto &Entries = RangeList.getEntries();
if (!Entries.empty()) {
const DWARFDebugRangeList::RangeListEntry &First = Entries.front();
diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp
index 43f97f112f6bc..7c81abaed7551 100644
--- a/tools/llc/llc.cpp
+++ b/tools/llc/llc.cpp
@@ -90,6 +90,11 @@ OptLevel("O",
static cl::opt<std::string>
TargetTriple("mtriple", cl::desc("Override target triple for module"));
+static cl::opt<std::string> SplitDwarfFile(
+ "split-dwarf-file",
+ cl::desc(
+ "Specify the name of the .dwo file to encode in the DWARF output"));
+
static cl::opt<bool> NoVerify("disable-verify", cl::Hidden,
cl::desc("Do not verify input module"));
@@ -450,6 +455,7 @@ static int compileModule(char **argv, LLVMContext &Context) {
Options.MCOptions.AsmVerbose = AsmVerbose;
Options.MCOptions.PreserveAsmComments = PreserveComments;
Options.MCOptions.IASSearchPaths = IncludeDirs;
+ Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
std::unique_ptr<TargetMachine> Target(
TheTarget->createTargetMachine(TheTriple.getTriple(), CPUStr, FeaturesStr,
diff --git a/tools/llvm-cvtres/CMakeLists.txt b/tools/llvm-cvtres/CMakeLists.txt
new file mode 100644
index 0000000000000..52edccac81655
--- /dev/null
+++ b/tools/llvm-cvtres/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS
+ Option
+ Support
+ )
+
+set(LLVM_TARGET_DEFINITIONS Opts.td)
+
+tablegen(LLVM Opts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(CvtResTableGen)
+
+add_llvm_tool(llvm-cvtres
+ llvm-cvtres.cpp
+ )
diff --git a/tools/llvm-cvtres/LLVMBuild.txt b/tools/llvm-cvtres/LLVMBuild.txt
new file mode 100644
index 0000000000000..73693bccb0ea4
--- /dev/null
+++ b/tools/llvm-cvtres/LLVMBuild.txt
@@ -0,0 +1,22 @@
+;===- ./tools/llvm-cvtres/LLVMBuild.txt ------------------------*- Conf -*--===;
+;
+; The LLVM Compiler Infrastructure
+;
+; This file is distributed under the University of Illinois Open Source
+; License. See LICENSE.TXT for details.
+;
+;===------------------------------------------------------------------------===;
+;
+; This is an LLVMBuild description file for the components in this subdirectory.
+;
+; For more information on the LLVMBuild system, please see:
+;
+; http://llvm.org/docs/LLVMBuild.html
+;
+;===------------------------------------------------------------------------===;
+
+[component_0]
+type = Tool
+name = llvm-cvtres
+parent = Tools
+required_libraries = Option Support
diff --git a/tools/llvm-cvtres/Opts.td b/tools/llvm-cvtres/Opts.td
new file mode 100644
index 0000000000000..64041bceb034f
--- /dev/null
+++ b/tools/llvm-cvtres/Opts.td
@@ -0,0 +1,11 @@
+include "llvm/Option/OptParser.td"
+
+def DEFINE : Joined<["/"], "DEFINE:">, HelpText<"">, MetaVarName<"symbol">;
+def FOLDDUPS : Flag<["/"], "FOLDDUPS:">, HelpText<"">;
+def MACHINE : Joined<["/"], "MACHINE:">, HelpText<"">, MetaVarName<"{ARM|EBC|IA64|X64|X86}">;
+def NOLOGO : Flag<["/"], "NOLOGO">, HelpText<"">;
+def OUT : Joined<["/"], "OUT:">, HelpText<"">, MetaVarName<"filename">;
+def READONLY : Flag<["/"], "READONLY">, HelpText<"">;
+def VERBOSE : Flag<["/"], "VERBOSE">, HelpText<"">;
+def HELP : Flag<["/"], "HELP">;
+def H : Flag<["/"], "H">, Alias<HELP>;
diff --git a/tools/llvm-cvtres/llvm-cvtres.cpp b/tools/llvm-cvtres/llvm-cvtres.cpp
new file mode 100644
index 0000000000000..f03e0b772e1b7
--- /dev/null
+++ b/tools/llvm-cvtres/llvm-cvtres.cpp
@@ -0,0 +1,86 @@
+//===- llvm-cvtres.cpp - Serialize .res files into .obj ---------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Serialize .res files into .obj files. This is intended to be a
+// platform-independent port of Microsoft's cvtres.exe.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-cvtres.h"
+
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace {
+
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class CvtResOptTable : public opt::OptTable {
+public:
+ CvtResOptTable() : OptTable(InfoTable, true) {}
+};
+
+static ExitOnError ExitOnErr;
+}
+
+int main(int argc_, const char *argv_[]) {
+ sys::PrintStackTraceOnErrorSignal(argv_[0]);
+ PrettyStackTraceProgram X(argc_, argv_);
+
+ ExitOnErr.setBanner("llvm-cvtres: ");
+
+ SmallVector<const char *, 256> argv;
+ SpecificBumpPtrAllocator<char> ArgAllocator;
+ ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector(
+ argv, makeArrayRef(argv_, argc_), ArgAllocator)));
+
+ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+
+ CvtResOptTable T;
+ unsigned MAI, MAC;
+ ArrayRef<const char *> ArgsArr = makeArrayRef(argv_, argc_);
+ opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
+
+ if (InputArgs.hasArg(OPT_HELP))
+ T.PrintHelp(outs(), "cvtres", "Resource Converter", false);
+
+ return 0;
+}
diff --git a/tools/llvm-cvtres/llvm-cvtres.h b/tools/llvm-cvtres/llvm-cvtres.h
new file mode 100644
index 0000000000000..eeaba19690365
--- /dev/null
+++ b/tools/llvm-cvtres/llvm-cvtres.h
@@ -0,0 +1,13 @@
+//===- llvm-cvtres.h ------------------------------------------ *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMCVTRES_LLVMCVTRES_H
+#define LLVM_TOOLS_LLVMCVTRES_LLVMCVTRES_H
+
+#endif
diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp
index 613d0643b4335..18fa0e0740849 100644
--- a/tools/llvm-objdump/llvm-objdump.cpp
+++ b/tools/llvm-objdump/llvm-objdump.cpp
@@ -485,10 +485,13 @@ void SourcePrinter::printSourceLine(raw_ostream &OS, uint64_t Address,
auto FileBuffer = SourceCache.find(LineInfo.FileName);
if (FileBuffer != SourceCache.end()) {
auto LineBuffer = LineCache.find(LineInfo.FileName);
- if (LineBuffer != LineCache.end())
+ if (LineBuffer != LineCache.end()) {
+ if (LineInfo.Line > LineBuffer->second.size())
+ return;
// Vector begins at 0, line numbers are non-zero
OS << Delimiter << LineBuffer->second[LineInfo.Line - 1].ltrim()
<< "\n";
+ }
}
}
OldLineInfo = LineInfo;
diff --git a/tools/llvm-pdbdump/CMakeLists.txt b/tools/llvm-pdbdump/CMakeLists.txt
index 37c76ab697b46..e3d7b2ef275e4 100644
--- a/tools/llvm-pdbdump/CMakeLists.txt
+++ b/tools/llvm-pdbdump/CMakeLists.txt
@@ -18,7 +18,6 @@ add_llvm_tool(llvm-pdbdump
PdbYaml.cpp
PrettyBuiltinDumper.cpp
PrettyClassDefinitionDumper.cpp
- PrettyClassLayoutTextDumper.cpp
PrettyClassLayoutGraphicalDumper.cpp
PrettyCompilandDumper.cpp
PrettyEnumDumper.cpp
diff --git a/tools/llvm-pdbdump/LinePrinter.cpp b/tools/llvm-pdbdump/LinePrinter.cpp
index d4a5a8d859e5c..7fa524400aef0 100644
--- a/tools/llvm-pdbdump/LinePrinter.cpp
+++ b/tools/llvm-pdbdump/LinePrinter.cpp
@@ -72,7 +72,7 @@ void LinePrinter::NewLine() {
}
bool LinePrinter::IsClassExcluded(const ClassLayout &Class) {
- if (IsTypeExcluded(Class.getUDTName(), Class.getClassSize()))
+ if (IsTypeExcluded(Class.getName(), Class.getSize()))
return true;
if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold)
return true;
diff --git a/tools/llvm-pdbdump/PdbYaml.cpp b/tools/llvm-pdbdump/PdbYaml.cpp
index e2c4ee967ed36..65a5a9142d20a 100644
--- a/tools/llvm-pdbdump/PdbYaml.cpp
+++ b/tools/llvm-pdbdump/PdbYaml.cpp
@@ -13,6 +13,7 @@
#include "YamlSymbolDumper.h"
#include "YamlTypeDumper.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
@@ -35,6 +36,10 @@ LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(uint32_t)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::StringRef)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::NamedStreamMapping)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbDbiModuleInfo)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbSourceFileChecksumEntry)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbSourceLineEntry)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbSourceColumnEntry)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbSourceLineBlock)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbSymbolRecord)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbTpiRecord)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamBlockList)
@@ -145,7 +150,38 @@ template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_FeatureSig> {
io.enumCase(Features, "VC140", PdbRaw_FeatureSig::VC140);
}
};
+
+template <> struct ScalarEnumerationTraits<llvm::codeview::FileChecksumKind> {
+ static void enumeration(IO &io, llvm::codeview::FileChecksumKind &Kind) {
+ io.enumCase(Kind, "None", llvm::codeview::FileChecksumKind::None);
+ io.enumCase(Kind, "MD5", llvm::codeview::FileChecksumKind::MD5);
+ io.enumCase(Kind, "SHA1", llvm::codeview::FileChecksumKind::SHA1);
+ io.enumCase(Kind, "SHA256", llvm::codeview::FileChecksumKind::SHA256);
+ }
+};
+
+template <> struct ScalarBitSetTraits<llvm::codeview::LineFlags> {
+ static void bitset(IO &io, llvm::codeview::LineFlags &Flags) {
+ io.bitSetCase(Flags, "HasColumnInfo",
+ llvm::codeview::LineFlags::HaveColumns);
+ io.enumFallback<Hex16>(Flags);
+ }
+};
+}
}
+
+void ScalarTraits<HexFormattedString>::output(const HexFormattedString &Value,
+ void *ctx, raw_ostream &Out) {
+ StringRef Bytes(reinterpret_cast<const char *>(Value.Bytes.data()),
+ Value.Bytes.size());
+ Out << toHex(Bytes);
+}
+
+StringRef ScalarTraits<HexFormattedString>::input(StringRef Scalar, void *ctxt,
+ HexFormattedString &Value) {
+ std::string H = fromHex(Scalar);
+ Value.Bytes.assign(H.begin(), H.end());
+ return StringRef();
}
void MappingTraits<PdbObject>::mapping(IO &IO, PdbObject &Obj) {
@@ -255,9 +291,65 @@ void MappingContextTraits<PdbDbiModuleInfo, pdb::yaml::SerializationContext>::ma
IO.mapRequired("Module", Obj.Mod);
IO.mapOptional("ObjFile", Obj.Obj, Obj.Mod);
IO.mapOptional("SourceFiles", Obj.SourceFiles);
+ IO.mapOptionalWithContext("LineInfo", Obj.FileLineInfo, Context);
IO.mapOptionalWithContext("Modi", Obj.Modi, Context);
}
+void MappingContextTraits<pdb::yaml::PdbSourceLineEntry,
+ pdb::yaml::SerializationContext>::
+ mapping(IO &IO, PdbSourceLineEntry &Obj,
+ pdb::yaml::SerializationContext &Context) {
+ IO.mapRequired("Offset", Obj.Offset);
+ IO.mapRequired("LineStart", Obj.LineStart);
+ IO.mapRequired("IsStatement", Obj.IsStatement);
+ IO.mapRequired("EndDelta", Obj.EndDelta);
+}
+
+void MappingContextTraits<pdb::yaml::PdbSourceColumnEntry,
+ pdb::yaml::SerializationContext>::
+ mapping(IO &IO, PdbSourceColumnEntry &Obj,
+ pdb::yaml::SerializationContext &Context) {
+ IO.mapRequired("StartColumn", Obj.StartColumn);
+ IO.mapRequired("EndColumn", Obj.EndColumn);
+};
+
+void MappingContextTraits<pdb::yaml::PdbSourceLineBlock,
+ pdb::yaml::SerializationContext>::
+ mapping(IO &IO, PdbSourceLineBlock &Obj,
+ pdb::yaml::SerializationContext &Context) {
+ IO.mapRequired("FileName", Obj.FileName);
+ IO.mapRequired("Lines", Obj.Lines, Context);
+ IO.mapRequired("Columns", Obj.Columns, Context);
+};
+
+void MappingContextTraits<pdb::yaml::PdbSourceFileChecksumEntry,
+ pdb::yaml::SerializationContext>::
+ mapping(IO &IO, PdbSourceFileChecksumEntry &Obj,
+ pdb::yaml::SerializationContext &Context) {
+ IO.mapRequired("FileName", Obj.FileName);
+ IO.mapRequired("Kind", Obj.Kind);
+ IO.mapRequired("Checksum", Obj.ChecksumBytes);
+};
+
+void MappingContextTraits<pdb::yaml::PdbSourceLineInfo,
+ pdb::yaml::SerializationContext>::
+ mapping(IO &IO, PdbSourceLineInfo &Obj,
+ pdb::yaml::SerializationContext &Context) {
+ IO.mapRequired("CodeSize", Obj.CodeSize);
+ IO.mapRequired("Flags", Obj.Flags);
+ IO.mapRequired("RelocOffset", Obj.RelocOffset);
+ IO.mapRequired("RelocSegment", Obj.RelocSegment);
+ IO.mapRequired("LineInfo", Obj.LineInfo, Context);
+};
+
+void MappingContextTraits<pdb::yaml::PdbSourceFileInfo,
+ pdb::yaml::SerializationContext>::
+ mapping(IO &IO, PdbSourceFileInfo &Obj,
+ pdb::yaml::SerializationContext &Context) {
+ IO.mapOptionalWithContext("Lines", Obj.Lines, Context);
+ IO.mapOptionalWithContext("Checksums", Obj.FileChecksums, Context);
+};
+
void MappingContextTraits<PdbTpiRecord, pdb::yaml::SerializationContext>::
mapping(IO &IO, pdb::yaml::PdbTpiRecord &Obj,
pdb::yaml::SerializationContext &Context) {
diff --git a/tools/llvm-pdbdump/PdbYaml.h b/tools/llvm-pdbdump/PdbYaml.h
index 2c4cd237f8d7f..96e0583ca23d1 100644
--- a/tools/llvm-pdbdump/PdbYaml.h
+++ b/tools/llvm-pdbdump/PdbYaml.h
@@ -65,10 +65,53 @@ struct PdbModiStream {
std::vector<PdbSymbolRecord> Symbols;
};
+struct PdbSourceLineEntry {
+ uint32_t Offset;
+ uint32_t LineStart;
+ uint32_t EndDelta;
+ bool IsStatement;
+};
+
+struct PdbSourceColumnEntry {
+ uint16_t StartColumn;
+ uint16_t EndColumn;
+};
+
+struct PdbSourceLineBlock {
+ StringRef FileName;
+ std::vector<PdbSourceLineEntry> Lines;
+ std::vector<PdbSourceColumnEntry> Columns;
+};
+
+struct HexFormattedString {
+ std::vector<uint8_t> Bytes;
+};
+
+struct PdbSourceFileChecksumEntry {
+ StringRef FileName;
+ codeview::FileChecksumKind Kind;
+ HexFormattedString ChecksumBytes;
+};
+
+struct PdbSourceLineInfo {
+ uint32_t RelocOffset;
+ uint32_t RelocSegment;
+ codeview::LineFlags Flags;
+ uint32_t CodeSize;
+
+ std::vector<PdbSourceLineBlock> LineInfo;
+};
+
+struct PdbSourceFileInfo {
+ PdbSourceLineInfo Lines;
+ std::vector<PdbSourceFileChecksumEntry> FileChecksums;
+};
+
struct PdbDbiModuleInfo {
StringRef Obj;
StringRef Mod;
std::vector<StringRef> SourceFiles;
+ Optional<PdbSourceFileInfo> FileLineInfo;
Optional<PdbModiStream> Modi;
};
@@ -166,6 +209,56 @@ template <> struct MappingContextTraits<pdb::yaml::PdbDbiModuleInfo, pdb::yaml::
};
template <>
+struct MappingContextTraits<pdb::yaml::PdbSourceLineEntry,
+ pdb::yaml::SerializationContext> {
+ static void mapping(IO &IO, pdb::yaml::PdbSourceLineEntry &Obj,
+ pdb::yaml::SerializationContext &Context);
+};
+
+template <>
+struct MappingContextTraits<pdb::yaml::PdbSourceColumnEntry,
+ pdb::yaml::SerializationContext> {
+ static void mapping(IO &IO, pdb::yaml::PdbSourceColumnEntry &Obj,
+ pdb::yaml::SerializationContext &Context);
+};
+
+template <>
+struct MappingContextTraits<pdb::yaml::PdbSourceLineBlock,
+ pdb::yaml::SerializationContext> {
+ static void mapping(IO &IO, pdb::yaml::PdbSourceLineBlock &Obj,
+ pdb::yaml::SerializationContext &Context);
+};
+
+template <>
+struct MappingContextTraits<pdb::yaml::PdbSourceFileChecksumEntry,
+ pdb::yaml::SerializationContext> {
+ static void mapping(IO &IO, pdb::yaml::PdbSourceFileChecksumEntry &Obj,
+ pdb::yaml::SerializationContext &Context);
+};
+
+template <> struct ScalarTraits<pdb::yaml::HexFormattedString> {
+ static void output(const pdb::yaml::HexFormattedString &Value, void *ctx,
+ llvm::raw_ostream &Out);
+ static StringRef input(StringRef Scalar, void *ctxt,
+ pdb::yaml::HexFormattedString &Value);
+ static bool mustQuote(StringRef) { return false; }
+};
+
+template <>
+struct MappingContextTraits<pdb::yaml::PdbSourceLineInfo,
+ pdb::yaml::SerializationContext> {
+ static void mapping(IO &IO, pdb::yaml::PdbSourceLineInfo &Obj,
+ pdb::yaml::SerializationContext &Context);
+};
+
+template <>
+struct MappingContextTraits<pdb::yaml::PdbSourceFileInfo,
+ pdb::yaml::SerializationContext> {
+ static void mapping(IO &IO, pdb::yaml::PdbSourceFileInfo &Obj,
+ pdb::yaml::SerializationContext &Context);
+};
+
+template <>
struct MappingContextTraits<pdb::yaml::PdbTpiRecord,
pdb::yaml::SerializationContext> {
static void mapping(IO &IO, pdb::yaml::PdbTpiRecord &Obj,
diff --git a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp
index 9f213a4b4d960..90f7772001d7b 100644
--- a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp
+++ b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp
@@ -11,7 +11,6 @@
#include "LinePrinter.h"
#include "PrettyClassLayoutGraphicalDumper.h"
-#include "PrettyClassLayoutTextDumper.h"
#include "llvm-pdbdump.h"
#include "llvm/ADT/APFloat.h"
@@ -39,44 +38,17 @@ void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) {
void ClassDefinitionDumper::start(const ClassLayout &Layout) {
prettyPrintClassIntro(Layout);
- switch (opts::pretty::ClassFormat) {
- case opts::pretty::ClassDefinitionFormat::Graphical: {
- PrettyClassLayoutGraphicalDumper Dumper(Printer, 0);
- DumpedAnything = Dumper.start(Layout);
- break;
- }
- case opts::pretty::ClassDefinitionFormat::Standard:
- case opts::pretty::ClassDefinitionFormat::Layout: {
- PrettyClassLayoutTextDumper Dumper(Printer);
- DumpedAnything |= Dumper.start(Layout);
- break;
- }
- default:
- llvm_unreachable("Unreachable!");
- }
+ PrettyClassLayoutGraphicalDumper Dumper(Printer, 1, 0);
+ DumpedAnything |= Dumper.start(Layout);
prettyPrintClassOutro(Layout);
}
-static void printBase(LinePrinter &Printer, const PDBSymbolTypeBaseClass &Base,
- uint32_t &CurIndex, uint32_t TotalBaseCount,
- bool IsVirtual) {
- Printer << " ";
- WithColor(Printer, PDB_ColorItem::Keyword).get() << Base.getAccess();
- if (IsVirtual)
- WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual";
- WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base.getName();
- if (++CurIndex < TotalBaseCount) {
- Printer.NewLine();
- Printer << ",";
- }
-}
-
void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) {
DumpedAnything = false;
Printer.NewLine();
- uint32_t Size = Layout.getClassSize();
+ uint32_t Size = Layout.getSize();
const PDBSymbolTypeUDT &Class = Layout.getClass();
WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " ";
@@ -84,19 +56,22 @@ void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) {
WithColor(Printer, PDB_ColorItem::Comment).get() << " [sizeof = " << Size
<< "]";
uint32_t BaseCount = Layout.bases().size();
- uint32_t VBaseCount = Layout.vbases().size();
- uint32_t TotalBaseCount = BaseCount + VBaseCount;
- if (TotalBaseCount > 0) {
+ if (BaseCount > 0) {
Printer.Indent();
- Printer.NewLine();
- Printer << ":";
- uint32_t BaseIndex = 0;
+ char NextSeparator = ':';
for (auto BC : Layout.bases()) {
const auto &Base = BC->getBase();
- printBase(Printer, Base, BaseIndex, TotalBaseCount, false);
- }
- for (auto &BC : Layout.vbases()) {
- printBase(Printer, *BC, BaseIndex, TotalBaseCount, true);
+ if (Base.isIndirectVirtualBaseClass())
+ continue;
+
+ Printer.NewLine();
+ Printer << NextSeparator << " ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << Base.getAccess();
+ if (BC->isVirtualBase())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual";
+
+ WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base.getName();
+ NextSeparator = ',';
}
Printer.Unindent();
@@ -114,12 +89,20 @@ void ClassDefinitionDumper::prettyPrintClassOutro(const ClassLayout &Layout) {
Printer.NewLine();
if (Layout.deepPaddingSize() > 0) {
APFloat Pct(100.0 * (double)Layout.deepPaddingSize() /
- (double)Layout.getClassSize());
+ (double)Layout.getSize());
SmallString<8> PctStr;
Pct.toString(PctStr, 4);
WithColor(Printer, PDB_ColorItem::Padding).get()
<< "Total padding " << Layout.deepPaddingSize() << " bytes (" << PctStr
<< "% of class size)";
Printer.NewLine();
+ APFloat Pct2(100.0 * (double)Layout.immediatePadding() /
+ (double)Layout.getSize());
+ PctStr.clear();
+ Pct2.toString(PctStr, 4);
+ WithColor(Printer, PDB_ColorItem::Padding).get()
+ << "Immediate padding " << Layout.immediatePadding() << " bytes ("
+ << PctStr << "% of class size)";
+ Printer.NewLine();
}
}
diff --git a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h
index 0e27733b3ccb9..6569a1d304f6c 100644
--- a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h
+++ b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h
@@ -39,8 +39,8 @@ private:
void prettyPrintClassIntro(const ClassLayout &Class);
void prettyPrintClassOutro(const ClassLayout &Class);
- bool DumpedAnything = false;
LinePrinter &Printer;
+ bool DumpedAnything = false;
};
}
}
diff --git a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp
index d146ca9d47121..16cec82f718a8 100644
--- a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp
+++ b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp
@@ -11,7 +11,12 @@
#include "LinePrinter.h"
#include "PrettyClassDefinitionDumper.h"
+#include "PrettyEnumDumper.h"
+#include "PrettyFunctionDumper.h"
+#include "PrettyTypedefDumper.h"
#include "PrettyVariableDumper.h"
+#include "PrettyVariableDumper.h"
+#include "llvm-pdbdump.h"
#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h"
@@ -23,11 +28,20 @@ using namespace llvm;
using namespace llvm::pdb;
PrettyClassLayoutGraphicalDumper::PrettyClassLayoutGraphicalDumper(
- LinePrinter &P, uint32_t InitialOffset)
- : PDBSymDumper(true), Printer(P), ClassOffsetZero(InitialOffset),
- CurrentAbsoluteOffset(InitialOffset) {}
+ LinePrinter &P, uint32_t RecurseLevel, uint32_t InitialOffset)
+ : PDBSymDumper(true), Printer(P), RecursionLevel(RecurseLevel),
+ ClassOffsetZero(InitialOffset), CurrentAbsoluteOffset(InitialOffset) {}
bool PrettyClassLayoutGraphicalDumper::start(const UDTLayoutBase &Layout) {
+
+ if (RecursionLevel == 1 &&
+ opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::All) {
+ for (auto &Other : Layout.other_items())
+ Other->dump(*this);
+ for (auto &Func : Layout.funcs())
+ Func->dump(*this);
+ }
+
const BitVector &UseMap = Layout.usedBytes();
int NextPaddingByte = UseMap.find_first_unset();
@@ -53,16 +67,31 @@ bool PrettyClassLayoutGraphicalDumper::start(const UDTLayoutBase &Layout) {
}
}
- CurrentItem = Item.get();
- Item->getSymbol().dump(*this);
+ CurrentItem = Item;
+ if (Item->isVBPtr()) {
+ VTableLayoutItem &Layout = static_cast<VTableLayoutItem &>(*CurrentItem);
+
+ VariableDumper VarDumper(Printer);
+ VarDumper.startVbptr(CurrentAbsoluteOffset, Layout.getSize());
+ } else {
+ if (auto Sym = Item->getSymbol())
+ Sym->dump(*this);
+ }
+
+ if (Item->getLayoutSize() > 0) {
+ uint32_t Prev = RelativeOffset + Item->getLayoutSize() - 1;
+ NextPaddingByte = UseMap.find_next_unset(Prev);
+ }
}
- if (NextPaddingByte >= 0 && Layout.getClassSize() > 1) {
- uint32_t Amount = Layout.getClassSize() - NextPaddingByte;
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount
- << " bytes)";
- DumpedAnything = true;
+ auto TailPadding = Layout.tailPadding();
+ if (TailPadding > 0) {
+ if (TailPadding != 1 || Layout.getSize() != 1) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Padding).get()
+ << "<padding> (" << TailPadding << " bytes)";
+ DumpedAnything = true;
+ }
}
return DumpedAnything;
@@ -85,24 +114,41 @@ void PrettyClassLayoutGraphicalDumper::dump(
Printer.NewLine();
BaseClassLayout &Layout = static_cast<BaseClassLayout &>(*CurrentItem);
- std::string Label = Layout.isVirtualBase() ? "vbase" : "base";
+ std::string Label = "base";
+ if (Layout.isVirtualBase()) {
+ Label.insert(Label.begin(), 'v');
+ if (Layout.getBase().isIndirectVirtualBaseClass())
+ Label.insert(Label.begin(), 'i');
+ }
Printer << Label << " ";
+ uint32_t Size = Layout.isEmptyBase() ? 1 : Layout.getLayoutSize();
+
WithColor(Printer, PDB_ColorItem::Offset).get()
- << "+" << format_hex(CurrentAbsoluteOffset, 4)
- << " [sizeof=" << Layout.getSize() << "] ";
+ << "+" << format_hex(CurrentAbsoluteOffset, 4) << " [sizeof=" << Size
+ << "] ";
WithColor(Printer, PDB_ColorItem::Identifier).get() << Layout.getName();
- Printer.Indent();
- uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent();
- PrettyClassLayoutGraphicalDumper BaseDumper(Printer, ChildOffsetZero);
- BaseDumper.start(Layout);
- Printer.Unindent();
+ if (shouldRecurse()) {
+ Printer.Indent();
+ uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent();
+ PrettyClassLayoutGraphicalDumper BaseDumper(Printer, RecursionLevel + 1,
+ ChildOffsetZero);
+ DumpedAnything |= BaseDumper.start(Layout);
+ Printer.Unindent();
+ }
DumpedAnything = true;
}
+bool PrettyClassLayoutGraphicalDumper::shouldRecurse() const {
+ uint32_t Limit = opts::pretty::ClassRecursionDepth;
+ if (Limit == 0)
+ return true;
+ return RecursionLevel < Limit;
+}
+
void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolData &Symbol) {
assert(CurrentItem != nullptr);
@@ -112,9 +158,11 @@ void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolData &Symbol) {
VariableDumper VarDumper(Printer);
VarDumper.start(Symbol, ClassOffsetZero);
- if (Layout.hasUDTLayout()) {
+ if (Layout.hasUDTLayout() && shouldRecurse()) {
+ uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent();
Printer.Indent();
- PrettyClassLayoutGraphicalDumper TypeDumper(Printer, ClassOffsetZero);
+ PrettyClassLayoutGraphicalDumper TypeDumper(Printer, RecursionLevel + 1,
+ ChildOffsetZero);
TypeDumper.start(Layout.getUDTLayout());
Printer.Unindent();
}
@@ -125,27 +173,43 @@ void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolData &Symbol) {
void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeVTable &Symbol) {
assert(CurrentItem != nullptr);
- VTableLayoutItem &Layout = static_cast<VTableLayoutItem &>(*CurrentItem);
-
VariableDumper VarDumper(Printer);
VarDumper.start(Symbol, ClassOffsetZero);
- Printer.Indent();
- uint32_t Index = 0;
- for (auto &Func : Layout.funcs()) {
- Printer.NewLine();
- std::string Name = Func->getName();
- auto ParentClass =
- unique_dyn_cast<PDBSymbolTypeUDT>(Func->getClassParent());
- assert(ParentClass);
- WithColor(Printer, PDB_ColorItem::Address).get() << " [" << Index << "] ";
- WithColor(Printer, PDB_ColorItem::Identifier).get()
- << "&" << ParentClass->getName();
- Printer << "::";
- WithColor(Printer, PDB_ColorItem::Identifier).get() << Name;
- ++Index;
- }
- Printer.Unindent();
+ DumpedAnything = true;
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeEnum &Symbol) {
+ DumpedAnything = true;
+ Printer.NewLine();
+ EnumDumper Dumper(Printer);
+ Dumper.start(Symbol);
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(
+ const PDBSymbolTypeTypedef &Symbol) {
+ DumpedAnything = true;
+ Printer.NewLine();
+ TypedefDumper Dumper(Printer);
+ Dumper.start(Symbol);
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(
+ const PDBSymbolTypeBuiltin &Symbol) {}
+
+void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeUDT &Symbol) {}
+
+void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolFunc &Symbol) {
+ if (Printer.IsSymbolExcluded(Symbol.getName()))
+ return;
+ if (Symbol.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
+ return;
+ if (Symbol.getLength() == 0 && !Symbol.isPureVirtual() &&
+ !Symbol.isIntroVirtualFunction())
+ return;
DumpedAnything = true;
+ Printer.NewLine();
+ FunctionDumper Dumper(Printer);
+ Dumper.start(Symbol, FunctionDumper::PointerType::None);
}
diff --git a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h
index 7dfb74c4e14b4..f83f1a6c1b34f 100644
--- a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h
+++ b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h
@@ -19,25 +19,36 @@ namespace llvm {
namespace pdb {
class UDTLayoutBase;
-class StorageItemBase;
+class LayoutItemBase;
class LinePrinter;
class PrettyClassLayoutGraphicalDumper : public PDBSymDumper {
public:
- PrettyClassLayoutGraphicalDumper(LinePrinter &P, uint32_t InitialOffset);
+ PrettyClassLayoutGraphicalDumper(LinePrinter &P, uint32_t RecurseLevel,
+ uint32_t InitialOffset);
bool start(const UDTLayoutBase &Layout);
+ // Layout based symbol types.
void dump(const PDBSymbolTypeBaseClass &Symbol) override;
void dump(const PDBSymbolData &Symbol) override;
void dump(const PDBSymbolTypeVTable &Symbol) override;
+ // Non layout-based symbol types.
+ void dump(const PDBSymbolTypeEnum &Symbol) override;
+ void dump(const PDBSymbolFunc &Symbol) override;
+ void dump(const PDBSymbolTypeTypedef &Symbol) override;
+ void dump(const PDBSymbolTypeUDT &Symbol) override;
+ void dump(const PDBSymbolTypeBuiltin &Symbol) override;
+
private:
+ bool shouldRecurse() const;
void printPaddingRow(uint32_t Amount);
LinePrinter &Printer;
- StorageItemBase *CurrentItem = nullptr;
+ LayoutItemBase *CurrentItem = nullptr;
+ uint32_t RecursionLevel = 0;
uint32_t ClassOffsetZero = 0;
uint32_t CurrentAbsoluteOffset = 0;
bool DumpedAnything = false;
diff --git a/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp
deleted file mode 100644
index 02f31108b0dfe..0000000000000
--- a/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-//===- PrettyClassLayoutTextDumper.cpp --------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "PrettyClassLayoutTextDumper.h"
-
-#include "LinePrinter.h"
-#include "PrettyEnumDumper.h"
-#include "PrettyFunctionDumper.h"
-#include "PrettyTypedefDumper.h"
-#include "PrettyVariableDumper.h"
-#include "llvm-pdbdump.h"
-
-#include "llvm/DebugInfo/PDB/IPDBSession.h"
-#include "llvm/DebugInfo/PDB/PDBExtras.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"
-#include "llvm/DebugInfo/PDB/UDTLayout.h"
-
-#include "llvm/Support/Format.h"
-
-using namespace llvm;
-using namespace llvm::pdb;
-
-PrettyClassLayoutTextDumper::PrettyClassLayoutTextDumper(LinePrinter &P)
- : PDBSymDumper(true), Printer(P) {}
-
-bool PrettyClassLayoutTextDumper::start(const ClassLayout &Layout) {
- if (opts::pretty::ClassFormat ==
- opts::pretty::ClassDefinitionFormat::Standard) {
- for (auto &Other : Layout.other_items())
- Other->dump(*this);
- for (auto &Func : Layout.funcs())
- Func->dump(*this);
- }
-
- const BitVector &UseMap = Layout.usedBytes();
- int NextUnusedByte = Layout.usedBytes().find_first_unset();
- // Next dump items which affect class layout.
- for (auto &LayoutItem : Layout.layout_items()) {
- if (NextUnusedByte >= 0) {
- // If there are padding bytes remaining, see if this field is the first to
- // cross a padding boundary, and print a padding field indicator if so.
- int Off = LayoutItem->getOffsetInParent();
- if (Off > NextUnusedByte) {
- uint32_t Amount = Off - NextUnusedByte;
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> ("
- << Amount << " bytes)";
- assert(UseMap.find_next(NextUnusedByte) == Off);
- NextUnusedByte = UseMap.find_next_unset(Off);
- }
- }
- LayoutItem->getSymbol().dump(*this);
- }
-
- if (NextUnusedByte >= 0 && Layout.getClassSize() > 1) {
- uint32_t Amount = Layout.getClassSize() - NextUnusedByte;
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount
- << " bytes)";
- DumpedAnything = true;
- }
-
- return DumpedAnything;
-}
-
-void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeBaseClass &Symbol) {}
-
-void PrettyClassLayoutTextDumper::dump(const PDBSymbolData &Symbol) {
- VariableDumper Dumper(Printer);
- Dumper.start(Symbol);
- DumpedAnything = true;
-}
-
-void PrettyClassLayoutTextDumper::dump(const PDBSymbolFunc &Symbol) {
- if (Printer.IsSymbolExcluded(Symbol.getName()))
- return;
- if (Symbol.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
- return;
- if (Symbol.getLength() == 0 && !Symbol.isPureVirtual() &&
- !Symbol.isIntroVirtualFunction())
- return;
-
- DumpedAnything = true;
- Printer.NewLine();
- FunctionDumper Dumper(Printer);
- Dumper.start(Symbol, FunctionDumper::PointerType::None);
-}
-
-void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeVTable &Symbol) {
- VariableDumper Dumper(Printer);
- Dumper.start(Symbol);
- DumpedAnything = true;
-}
-
-void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeEnum &Symbol) {
- DumpedAnything = true;
- Printer.NewLine();
- EnumDumper Dumper(Printer);
- Dumper.start(Symbol);
-}
-
-void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
- DumpedAnything = true;
- Printer.NewLine();
- TypedefDumper Dumper(Printer);
- Dumper.start(Symbol);
-}
-
-void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeUDT &Symbol) {}
diff --git a/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h
deleted file mode 100644
index 56c20f0e84336..0000000000000
--- a/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h
+++ /dev/null
@@ -1,44 +0,0 @@
-//===- PrettyClassLayoutTextDumper.h ----------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTTEXTDUMPER_H
-#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTTEXTDUMPER_H
-
-#include "llvm/ADT/BitVector.h"
-
-#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
-
-namespace llvm {
-
-namespace pdb {
-
-class ClassLayout;
-class LinePrinter;
-
-class PrettyClassLayoutTextDumper : public PDBSymDumper {
-public:
- PrettyClassLayoutTextDumper(LinePrinter &P);
-
- bool start(const ClassLayout &Layout);
-
- void dump(const PDBSymbolTypeBaseClass &Symbol) override;
- void dump(const PDBSymbolData &Symbol) override;
- void dump(const PDBSymbolTypeEnum &Symbol) override;
- void dump(const PDBSymbolFunc &Symbol) override;
- void dump(const PDBSymbolTypeTypedef &Symbol) override;
- void dump(const PDBSymbolTypeUDT &Symbol) override;
- void dump(const PDBSymbolTypeVTable &Symbol) override;
-
-private:
- bool DumpedAnything = false;
- LinePrinter &Printer;
-};
-}
-}
-#endif
diff --git a/tools/llvm-pdbdump/PrettyTypeDumper.cpp b/tools/llvm-pdbdump/PrettyTypeDumper.cpp
index ffeef72150d26..cd156f051573f 100644
--- a/tools/llvm-pdbdump/PrettyTypeDumper.cpp
+++ b/tools/llvm-pdbdump/PrettyTypeDumper.cpp
@@ -34,17 +34,34 @@ using LayoutPtr = std::unique_ptr<ClassLayout>;
typedef bool (*CompareFunc)(const LayoutPtr &S1, const LayoutPtr &S2);
static bool CompareNames(const LayoutPtr &S1, const LayoutPtr &S2) {
- return S1->getUDTName() < S2->getUDTName();
+ return S1->getName() < S2->getName();
}
static bool CompareSizes(const LayoutPtr &S1, const LayoutPtr &S2) {
- return S1->getClassSize() < S2->getClassSize();
+ return S1->getSize() < S2->getSize();
}
static bool ComparePadding(const LayoutPtr &S1, const LayoutPtr &S2) {
return S1->deepPaddingSize() < S2->deepPaddingSize();
}
+static bool ComparePaddingPct(const LayoutPtr &S1, const LayoutPtr &S2) {
+ double Pct1 = (double)S1->deepPaddingSize() / (double)S1->getSize();
+ double Pct2 = (double)S2->deepPaddingSize() / (double)S2->getSize();
+ return Pct1 < Pct2;
+}
+
+static bool ComparePaddingImmediate(const LayoutPtr &S1, const LayoutPtr &S2) {
+ return S1->immediatePadding() < S2->immediatePadding();
+}
+
+static bool ComparePaddingPctImmediate(const LayoutPtr &S1,
+ const LayoutPtr &S2) {
+ double Pct1 = (double)S1->immediatePadding() / (double)S1->getSize();
+ double Pct2 = (double)S2->immediatePadding() / (double)S2->getSize();
+ return Pct1 < Pct2;
+}
+
static CompareFunc getComparisonFunc(opts::pretty::ClassSortMode Mode) {
switch (Mode) {
case opts::pretty::ClassSortMode::Name:
@@ -53,6 +70,12 @@ static CompareFunc getComparisonFunc(opts::pretty::ClassSortMode Mode) {
return CompareSizes;
case opts::pretty::ClassSortMode::Padding:
return ComparePadding;
+ case opts::pretty::ClassSortMode::PaddingPct:
+ return ComparePaddingPct;
+ case opts::pretty::ClassSortMode::PaddingImmediate:
+ return ComparePaddingImmediate;
+ case opts::pretty::ClassSortMode::PaddingPctImmediate:
+ return ComparePaddingPctImmediate;
default:
return nullptr;
}
@@ -67,14 +90,18 @@ filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E,
Filtered.reserve(UnfilteredCount);
CompareFunc Comp = getComparisonFunc(opts::pretty::ClassOrder);
+ if (UnfilteredCount > 10000) {
+ errs() << formatv("Filtering and sorting {0} types", UnfilteredCount);
+ errs().flush();
+ }
uint32_t Examined = 0;
uint32_t Discarded = 0;
while (auto Class = E.getNext()) {
++Examined;
if (Examined % 10000 == 0) {
- outs() << formatv("Examined {0}/{1} items. {2} items discarded\n",
+ errs() << formatv("Examined {0}/{1} items. {2} items discarded\n",
Examined, UnfilteredCount, Discarded);
- outs().flush();
+ errs().flush();
}
if (Class->getUnmodifiedTypeId() != 0) {
@@ -92,6 +119,10 @@ filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E,
++Discarded;
continue;
}
+ if (Layout->immediatePadding() < opts::pretty::ImmediatePaddingThreshold) {
+ ++Discarded;
+ continue;
+ }
Filtered.push_back(std::move(Layout));
}
@@ -163,6 +194,9 @@ void TypeDumper::start(const PDBSymbolExe &Exe) {
dumpClassLayout(*Class);
} else {
while (auto Class = Classes->getNext()) {
+ if (Class->getUnmodifiedTypeId() != 0)
+ continue;
+
if (Printer.IsTypeExcluded(Class->getName(), Class->getLength()))
continue;
@@ -209,7 +243,7 @@ void TypeDumper::dumpClassLayout(const ClassLayout &Class) {
if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::Keyword).get() << "class ";
- WithColor(Printer, PDB_ColorItem::Identifier).get() << Class.getUDTName();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Class.getName();
} else {
ClassDefinitionDumper Dumper(Printer);
Dumper.start(Class);
diff --git a/tools/llvm-pdbdump/PrettyVariableDumper.cpp b/tools/llvm-pdbdump/PrettyVariableDumper.cpp
index 76a0d23bf87a4..70925f4b03d09 100644
--- a/tools/llvm-pdbdump/PrettyVariableDumper.cpp
+++ b/tools/llvm-pdbdump/PrettyVariableDumper.cpp
@@ -91,6 +91,14 @@ void VariableDumper::start(const PDBSymbolData &Var, uint32_t Offset) {
}
}
+void VariableDumper::startVbptr(uint32_t Offset, uint32_t Size) {
+ Printer.NewLine();
+ Printer << "vbptr ";
+
+ WithColor(Printer, PDB_ColorItem::Offset).get()
+ << "+" << format_hex(Offset, 4) << " [sizeof=" << Size << "] ";
+}
+
void VariableDumper::start(const PDBSymbolTypeVTable &Var, uint32_t Offset) {
Printer.NewLine();
Printer << "vfptr ";
diff --git a/tools/llvm-pdbdump/PrettyVariableDumper.h b/tools/llvm-pdbdump/PrettyVariableDumper.h
index 4ba3bc97d85b6..cacf1ce9577b0 100644
--- a/tools/llvm-pdbdump/PrettyVariableDumper.h
+++ b/tools/llvm-pdbdump/PrettyVariableDumper.h
@@ -26,6 +26,7 @@ public:
void start(const PDBSymbolData &Var, uint32_t Offset = 0);
void start(const PDBSymbolTypeVTable &Var, uint32_t Offset = 0);
+ void startVbptr(uint32_t Offset, uint32_t Size);
void dump(const PDBSymbolTypeArray &Symbol) override;
void dump(const PDBSymbolTypeBuiltin &Symbol) override;
diff --git a/tools/llvm-pdbdump/YAMLOutputStyle.cpp b/tools/llvm-pdbdump/YAMLOutputStyle.cpp
index 5b53d2137166a..b329de265e720 100644
--- a/tools/llvm-pdbdump/YAMLOutputStyle.cpp
+++ b/tools/llvm-pdbdump/YAMLOutputStyle.cpp
@@ -12,6 +12,9 @@
#include "PdbYaml.h"
#include "llvm-pdbdump.h"
+#include "llvm/DebugInfo/CodeView/Line.h"
+#include "llvm/DebugInfo/CodeView/ModuleSubstream.h"
+#include "llvm/DebugInfo/CodeView/ModuleSubstreamVisitor.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
@@ -33,8 +36,13 @@ Error YAMLOutputStyle::dump() {
opts::pdb2yaml::StreamMetadata = true;
if (opts::pdb2yaml::DbiModuleSyms)
opts::pdb2yaml::DbiModuleInfo = true;
+
+ if (opts::pdb2yaml::DbiModuleSourceLineInfo)
+ opts::pdb2yaml::DbiModuleSourceFileInfo = true;
+
if (opts::pdb2yaml::DbiModuleSourceFileInfo)
opts::pdb2yaml::DbiModuleInfo = true;
+
if (opts::pdb2yaml::DbiModuleInfo)
opts::pdb2yaml::DbiStream = true;
@@ -66,6 +74,112 @@ Error YAMLOutputStyle::dump() {
return Error::success();
}
+namespace {
+class C13SubstreamVisitor : public codeview::IModuleSubstreamVisitor {
+public:
+ C13SubstreamVisitor(llvm::pdb::yaml::PdbSourceFileInfo &Info, PDBFile &F)
+ : Info(Info), F(F) {}
+
+ Error visitUnknown(codeview::ModuleSubstreamKind Kind,
+ BinaryStreamRef Stream) override {
+ return Error::success();
+ }
+
+ Error
+ visitFileChecksums(BinaryStreamRef Data,
+ const codeview::FileChecksumArray &Checksums) override {
+ for (const auto &C : Checksums) {
+ llvm::pdb::yaml::PdbSourceFileChecksumEntry Entry;
+ if (auto Result = getGlobalString(C.FileNameOffset))
+ Entry.FileName = *Result;
+ else
+ return Result.takeError();
+
+ Entry.Kind = C.Kind;
+ Entry.ChecksumBytes.Bytes = C.Checksum;
+ Info.FileChecksums.push_back(Entry);
+ }
+ return Error::success();
+ }
+
+ Error visitLines(BinaryStreamRef Data,
+ const codeview::LineSubstreamHeader *Header,
+ const codeview::LineInfoArray &Lines) override {
+
+ Info.Lines.CodeSize = Header->CodeSize;
+ Info.Lines.Flags =
+ static_cast<codeview::LineFlags>(uint16_t(Header->Flags));
+ Info.Lines.RelocOffset = Header->RelocOffset;
+ Info.Lines.RelocSegment = Header->RelocSegment;
+
+ for (const auto &L : Lines) {
+ llvm::pdb::yaml::PdbSourceLineBlock Block;
+
+ if (auto Result = getDbiFileName(L.NameIndex))
+ Block.FileName = *Result;
+ else
+ return Result.takeError();
+
+ for (const auto &N : L.LineNumbers) {
+ llvm::pdb::yaml::PdbSourceLineEntry Line;
+ Line.Offset = N.Offset;
+ codeview::LineInfo LI(N.Flags);
+ Line.LineStart = LI.getStartLine();
+ Line.EndDelta = LI.getEndLine();
+ Line.IsStatement = LI.isStatement();
+ Block.Lines.push_back(Line);
+ }
+
+ if (Info.Lines.Flags & codeview::LineFlags::HaveColumns) {
+ for (const auto &C : L.Columns) {
+ llvm::pdb::yaml::PdbSourceColumnEntry Column;
+ Column.StartColumn = C.StartColumn;
+ Column.EndColumn = C.EndColumn;
+ Block.Columns.push_back(Column);
+ }
+ }
+
+ Info.Lines.LineInfo.push_back(Block);
+ }
+ return Error::success();
+ }
+
+private:
+ Expected<StringRef> getGlobalString(uint32_t Offset) {
+ auto ST = F.getStringTable();
+ if (!ST)
+ return ST.takeError();
+
+ return ST->getStringForID(Offset);
+ }
+ Expected<StringRef> getDbiFileName(uint32_t Offset) {
+ auto DS = F.getPDBDbiStream();
+ if (!DS)
+ return DS.takeError();
+ return DS->getFileNameForIndex(Offset);
+ }
+
+ llvm::pdb::yaml::PdbSourceFileInfo &Info;
+ PDBFile &F;
+};
+}
+
+Expected<Optional<llvm::pdb::yaml::PdbSourceFileInfo>>
+YAMLOutputStyle::getFileLineInfo(const pdb::ModStream &ModS) {
+ if (!ModS.hasLineInfo())
+ return None;
+
+ yaml::PdbSourceFileInfo Info;
+ bool Error = false;
+ C13SubstreamVisitor Visitor(Info, File);
+ for (auto &Substream : ModS.lines(&Error)) {
+ if (auto E = codeview::visitModuleSubstream(Substream, Visitor))
+ return std::move(E);
+ }
+
+ return Info;
+}
+
Error YAMLOutputStyle::dumpFileHeaders() {
if (opts::pdb2yaml::NoFileHeaders)
return Error::success();
@@ -175,16 +289,24 @@ Error YAMLOutputStyle::dumpDbiStream() {
if (opts::pdb2yaml::DbiModuleSourceFileInfo)
DMI.SourceFiles = MI.SourceFiles;
+ auto ModStreamData = msf::MappedBlockStream::createIndexedStream(
+ File.getMsfLayout(), File.getMsfBuffer(),
+ MI.Info.getModuleStreamIndex());
+
+ pdb::ModStream ModS(MI.Info, std::move(ModStreamData));
+ if (auto EC = ModS.reload())
+ return EC;
+
+ if (opts::pdb2yaml::DbiModuleSourceLineInfo) {
+ auto ExpectedInfo = getFileLineInfo(ModS);
+ if (!ExpectedInfo)
+ return ExpectedInfo.takeError();
+ DMI.FileLineInfo = *ExpectedInfo;
+ }
+
if (opts::pdb2yaml::DbiModuleSyms &&
MI.Info.getModuleStreamIndex() != kInvalidStreamIndex) {
DMI.Modi.emplace();
- auto ModStreamData = msf::MappedBlockStream::createIndexedStream(
- File.getMsfLayout(), File.getMsfBuffer(),
- MI.Info.getModuleStreamIndex());
-
- pdb::ModStream ModS(MI.Info, std::move(ModStreamData));
- if (auto EC = ModS.reload())
- return EC;
DMI.Modi->Signature = ModS.signature();
bool HadError = false;
diff --git a/tools/llvm-pdbdump/YAMLOutputStyle.h b/tools/llvm-pdbdump/YAMLOutputStyle.h
index db9868db4a7e1..263af776fa03f 100644
--- a/tools/llvm-pdbdump/YAMLOutputStyle.h
+++ b/tools/llvm-pdbdump/YAMLOutputStyle.h
@@ -19,6 +19,8 @@
namespace llvm {
namespace pdb {
+class ModStream;
+
class YAMLOutputStyle : public OutputStyle {
public:
YAMLOutputStyle(PDBFile &File);
@@ -26,6 +28,9 @@ public:
Error dump() override;
private:
+ Expected<Optional<llvm::pdb::yaml::PdbSourceFileInfo>>
+ getFileLineInfo(const pdb::ModStream &ModS);
+
Error dumpStringTable();
Error dumpFileHeaders();
Error dumpStreamMetadata();
diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp
index 06c2afc0bc78a..7337b1d28747d 100644
--- a/tools/llvm-pdbdump/llvm-pdbdump.cpp
+++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp
@@ -124,28 +124,37 @@ cl::opt<bool> Typedefs("typedefs", cl::desc("Display typedef types"),
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<ClassSortMode> ClassOrder(
"class-order", cl::desc("Class sort order"), cl::init(ClassSortMode::None),
- cl::values(clEnumValN(ClassSortMode::None, "none",
- "Undefined / no particular sort order"),
- clEnumValN(ClassSortMode::Name, "name", "Sort classes by name"),
- clEnumValN(ClassSortMode::Size, "size", "Sort classes by size"),
- clEnumValN(ClassSortMode::Padding, "padding",
- "Sort classes by amount of padding")),
+ cl::values(
+ clEnumValN(ClassSortMode::None, "none",
+ "Undefined / no particular sort order"),
+ clEnumValN(ClassSortMode::Name, "name", "Sort classes by name"),
+ clEnumValN(ClassSortMode::Size, "size", "Sort classes by size"),
+ clEnumValN(ClassSortMode::Padding, "padding",
+ "Sort classes by amount of padding"),
+ clEnumValN(ClassSortMode::PaddingPct, "padding-pct",
+ "Sort classes by percentage of space consumed by padding"),
+ clEnumValN(ClassSortMode::PaddingImmediate, "padding-imm",
+ "Sort classes by amount of immediate padding"),
+ clEnumValN(ClassSortMode::PaddingPctImmediate, "padding-pct-imm",
+ "Sort classes by percentage of space consumed by immediate "
+ "padding")),
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<ClassDefinitionFormat> ClassFormat(
"class-definitions", cl::desc("Class definition format"),
- cl::init(ClassDefinitionFormat::Standard),
+ cl::init(ClassDefinitionFormat::All),
cl::values(
- clEnumValN(ClassDefinitionFormat::Standard, "all-members",
+ clEnumValN(ClassDefinitionFormat::All, "all",
"Display all class members including data, constants, "
"typedefs, functions, etc"),
- clEnumValN(ClassDefinitionFormat::Layout, "layout-members",
+ clEnumValN(ClassDefinitionFormat::Layout, "layout",
"Only display members that contribute to class size."),
- clEnumValN(ClassDefinitionFormat::Graphical, "graphical",
- "Display graphical representation of each class's layout."),
clEnumValN(ClassDefinitionFormat::None, "none",
"Don't display class definitions")),
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<uint32_t> ClassRecursionDepth(
+ "class-recurse-depth", cl::desc("Class recursion depth (0=no limit)"),
+ cl::init(0), cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<bool> Lines("lines", cl::desc("Line tables"), cl::cat(TypeCategory),
cl::sub(PrettySubcommand));
@@ -193,6 +202,12 @@ cl::opt<uint32_t> PaddingThreshold(
"min-class-padding", cl::desc("Displays only those classes which have at "
"least the specified amount of padding."),
cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::opt<uint32_t> ImmediatePaddingThreshold(
+ "min-class-padding-imm",
+ cl::desc("Displays only those classes which have at least the specified "
+ "amount of immediate padding, ignoring padding internal to bases "
+ "and aggregates."),
+ cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::opt<bool> ExcludeCompilerGenerated(
"no-compiler-generated",
@@ -371,9 +386,15 @@ cl::opt<bool> DbiModuleSyms(
cl::opt<bool> DbiModuleSourceFileInfo(
"dbi-module-source-info",
cl::desc(
- "Dump DBI Module Source File Information (implies -dbi-module-info"),
+ "Dump DBI Module Source File Information (implies -dbi-module-info)"),
cl::sub(PdbToYamlSubcommand), cl::init(false));
+cl::opt<bool>
+ DbiModuleSourceLineInfo("dbi-module-lines",
+ cl::desc("Dump DBI Module Source Line Information "
+ "(implies -dbi-module-source-info)"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+
cl::opt<bool> TpiStream("tpi-stream",
cl::desc("Dump the TPI Stream (Stream 3)"),
cl::sub(PdbToYamlSubcommand), cl::init(false));
diff --git a/tools/llvm-pdbdump/llvm-pdbdump.h b/tools/llvm-pdbdump/llvm-pdbdump.h
index a5429a253df40..f080d6d552501 100644
--- a/tools/llvm-pdbdump/llvm-pdbdump.h
+++ b/tools/llvm-pdbdump/llvm-pdbdump.h
@@ -18,8 +18,16 @@ namespace opts {
namespace pretty {
-enum class ClassDefinitionFormat { None, Layout, Graphical, Standard };
-enum class ClassSortMode { None, Name, Size, Padding };
+enum class ClassDefinitionFormat { None, Layout, All };
+enum class ClassSortMode {
+ None,
+ Name,
+ Size,
+ Padding,
+ PaddingPct,
+ PaddingImmediate,
+ PaddingPctImmediate
+};
extern llvm::cl::opt<bool> Compilands;
extern llvm::cl::opt<bool> Symbols;
@@ -40,7 +48,9 @@ extern llvm::cl::list<std::string> IncludeCompilands;
extern llvm::cl::opt<ClassSortMode> ClassOrder;
extern llvm::cl::opt<uint32_t> SizeThreshold;
extern llvm::cl::opt<uint32_t> PaddingThreshold;
+extern llvm::cl::opt<uint32_t> ImmediatePaddingThreshold;
extern llvm::cl::opt<ClassDefinitionFormat> ClassFormat;
+extern llvm::cl::opt<uint32_t> ClassRecursionDepth;
}
namespace raw {
@@ -65,6 +75,7 @@ extern llvm::cl::opt<bool> DumpIpiRecords;
extern llvm::cl::opt<bool> DumpIpiRecordBytes;
extern llvm::cl::opt<bool> DumpModules;
extern llvm::cl::opt<bool> DumpModuleFiles;
+extern llvm::cl::opt<bool> DumpModuleLines;
extern llvm::cl::opt<bool> DumpModuleSyms;
extern llvm::cl::opt<bool> DumpPublics;
extern llvm::cl::opt<bool> DumpSectionContribs;
@@ -91,6 +102,7 @@ extern llvm::cl::opt<bool> DbiStream;
extern llvm::cl::opt<bool> DbiModuleInfo;
extern llvm::cl::opt<bool> DbiModuleSyms;
extern llvm::cl::opt<bool> DbiModuleSourceFileInfo;
+extern llvm::cl::opt<bool> DbiModuleSourceLineInfo;
extern llvm::cl::opt<bool> TpiStream;
extern llvm::cl::opt<bool> IpiStream;
extern llvm::cl::list<std::string> InputFilename;
diff --git a/tools/llvm-xray/CMakeLists.txt b/tools/llvm-xray/CMakeLists.txt
index 3baf4e64e81cb..6312e7ac47f4a 100644
--- a/tools/llvm-xray/CMakeLists.txt
+++ b/tools/llvm-xray/CMakeLists.txt
@@ -14,6 +14,7 @@ set(LLVM_XRAY_TOOLS
xray-extract.cc
xray-extract.cc
xray-graph.cc
+ xray-graph-diff.cc
xray-registry.cc)
add_llvm_tool(llvm-xray llvm-xray.cc ${LLVM_XRAY_TOOLS})
diff --git a/tools/llvm-xray/xray-color-helper.cc b/tools/llvm-xray/xray-color-helper.cc
index 925bb7483d8f0..7b6a73a5552b9 100644
--- a/tools/llvm-xray/xray-color-helper.cc
+++ b/tools/llvm-xray/xray-color-helper.cc
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
#include <algorithm>
+#include <iostream>
#include "xray-color-helper.h"
#include "llvm/Support/FormatVariadic.h"
@@ -42,8 +43,18 @@ static const std::tuple<uint8_t, uint8_t, uint8_t> SequentialMaps[][9] = {
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)]) {}
+ : 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.
@@ -58,8 +69,16 @@ static const std::tuple<uint8_t, uint8_t, uint8_t> DivergingCoeffs[][11] = {
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)]) {}
+ : 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
@@ -78,12 +97,12 @@ convertToHSV(const std::tuple<uint8_t, uint8_t, uint8_t> &Color) {
double C = Scaled[Max] - Scaled[Min];
- double HPrime = (Scaled[(Max + 1) % 3] - Scaled[(Max + 2) % 3]) / C;
+ 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;
@@ -164,6 +183,13 @@ interpolateHSV(const std::tuple<double, double, double> &C0,
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;
diff --git a/tools/llvm-xray/xray-color-helper.h b/tools/llvm-xray/xray-color-helper.h
index d3c77de03cb29..b2dcf626a65f6 100644
--- a/tools/llvm-xray/xray-color-helper.h
+++ b/tools/llvm-xray/xray-color-helper.h
@@ -46,6 +46,7 @@ class ColorHelper {
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
@@ -73,9 +74,16 @@ public:
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/tools/llvm-xray/xray-graph-diff.cc b/tools/llvm-xray/xray-graph-diff.cc
new file mode 100644
index 0000000000000..3c69b3fb0751f
--- /dev/null
+++ b/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/tools/llvm-xray/xray-graph-diff.h b/tools/llvm-xray/xray-graph-diff.h
new file mode 100644
index 0000000000000..5abec91d85825
--- /dev/null
+++ b/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/tools/llvm-xray/xray-graph.cc b/tools/llvm-xray/xray-graph.cc
index 9be0b70c2cdd8..685c24cb91876 100644
--- a/tools/llvm-xray/xray-graph.cc
+++ b/tools/llvm-xray/xray-graph.cc
@@ -19,7 +19,6 @@
#include "xray-graph.h"
#include "xray-registry.h"
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/XRay/InstrumentationMap.h"
@@ -98,7 +97,7 @@ static cl::opt<GraphRenderer::StatType> GraphVertexLabel(
cl::value_desc("field"), cl::sub(GraphC),
cl::init(GraphRenderer::StatType::NONE),
cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
- "Do not label Edges"),
+ "Do not label Vertices"),
clEnumValN(GraphRenderer::StatType::COUNT, "count",
"function call counts"),
clEnumValN(GraphRenderer::StatType::MIN, "min",
@@ -123,7 +122,7 @@ static cl::opt<GraphRenderer::StatType> GraphEdgeColorType(
cl::value_desc("field"), cl::sub(GraphC),
cl::init(GraphRenderer::StatType::NONE),
cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
- "Do not label Edges"),
+ "Do not color Edges"),
clEnumValN(GraphRenderer::StatType::COUNT, "count",
"function call counts"),
clEnumValN(GraphRenderer::StatType::MIN, "min",
@@ -148,7 +147,7 @@ static cl::opt<GraphRenderer::StatType> GraphVertexColorType(
cl::value_desc("field"), cl::sub(GraphC),
cl::init(GraphRenderer::StatType::NONE),
cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
- "Do not label Edges"),
+ "Do not color vertices"),
clEnumValN(GraphRenderer::StatType::COUNT, "count",
"function call counts"),
clEnumValN(GraphRenderer::StatType::MIN, "min",
@@ -210,7 +209,7 @@ Error GraphRenderer::accountRecord(const XRayRecord &Record) {
auto &ThreadStack = PerThreadFunctionStack[Record.TId];
switch (Record.Type) {
case RecordTypes::ENTER: {
- if (G.count(Record.FuncId) == 0)
+ if (Record.FuncId != 0 && G.count(Record.FuncId) == 0)
G[Record.FuncId].SymbolName = FuncIdHelper.SymbolOrNumber(Record.FuncId);
ThreadStack.push_back({Record.FuncId, Record.TSC});
break;
@@ -312,12 +311,9 @@ void GraphRenderer::calculateVertexStatistics() {
// TimeStat element.
static void normalizeTimeStat(GraphRenderer::TimeStat &S,
double CycleFrequency) {
- S.Min /= CycleFrequency;
- S.Median /= CycleFrequency;
- S.Max /= CycleFrequency;
- S.Sum /= CycleFrequency;
- S.Pct90 /= CycleFrequency;
- S.Pct99 /= CycleFrequency;
+ int64_t OldCount = S.Count;
+ S = S / CycleFrequency;
+ S.Count = OldCount;
}
// Normalises the statistics in the graph for a given TSC frequency.
@@ -337,32 +333,22 @@ void GraphRenderer::normalizeStatistics(double CycleFrequency) {
// Returns a string containing the value of statistic field T
std::string
-GraphRenderer::TimeStat::getAsString(GraphRenderer::StatType T) const {
+GraphRenderer::TimeStat::getString(GraphRenderer::StatType T) const {
std::string St;
raw_string_ostream S{St};
+ 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;
- case GraphRenderer::StatType::MIN:
- S << Min;
- break;
- case GraphRenderer::StatType::MED:
- S << Median;
- break;
- case GraphRenderer::StatType::PCT90:
- S << Pct90;
- break;
- case GraphRenderer::StatType::PCT99:
- S << Pct99;
- break;
- case GraphRenderer::StatType::MAX:
- S << Max;
- break;
- case GraphRenderer::StatType::SUM:
- S << Sum;
- break;
- case GraphRenderer::StatType::NONE:
+ default:
+ S << (*this).*
+ DoubleStatPtrs[static_cast<int>(T) -
+ static_cast<int>(GraphRenderer::StatType::MIN)];
break;
}
return S.str();
@@ -370,38 +356,25 @@ GraphRenderer::TimeStat::getAsString(GraphRenderer::StatType T) const {
// Returns the quotient between the property T of this and another TimeStat as
// a double
-double GraphRenderer::TimeStat::compare(StatType T, const TimeStat &O) const {
+double GraphRenderer::TimeStat::getDouble(StatType T) const {
double retval = 0;
+ double TimeStat::*DoubleStatPtrs[] = {&TimeStat::Min, &TimeStat::Median,
+ &TimeStat::Pct90, &TimeStat::Pct99,
+ &TimeStat::Max, &TimeStat::Sum};
switch (T) {
- case GraphRenderer::StatType::COUNT:
- retval = static_cast<double>(Count) / static_cast<double>(O.Count);
- break;
- case GraphRenderer::StatType::MIN:
- retval = Min / O.Min;
- break;
- case GraphRenderer::StatType::MED:
- retval = Median / O.Median;
- break;
- case GraphRenderer::StatType::PCT90:
- retval = Pct90 / O.Pct90;
- break;
- case GraphRenderer::StatType::PCT99:
- retval = Pct99 / O.Pct99;
- break;
- case GraphRenderer::StatType::MAX:
- retval = Max / O.Max;
- break;
- case GraphRenderer::StatType::SUM:
- retval = Sum / O.Sum;
- break;
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 std::sqrt(
- retval); // the square root here provides more dynamic contrast for
- // low runtime edges, giving better separation and
- // coloring lower down the call stack.
+ return retval;
}
// Outputs a DOT format version of the Graph embedded in the GraphRenderer
@@ -410,17 +383,8 @@ double GraphRenderer::TimeStat::compare(StatType T, const TimeStat &O) const {
// annotations.
//
// FIXME: output more information, better presented.
-void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H,
- StatType ET, StatType EC, StatType VT,
- StatType VC) {
- G.GraphEdgeMax = {};
- G.GraphVertexMax = {};
- calculateEdgeStatistics();
- calculateVertexStatistics();
-
- if (H.CycleFrequency)
- normalizeStatistics(H.CycleFrequency);
-
+void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, StatType ET, StatType EC,
+ StatType VT, StatType VC) {
OS << "digraph xray {\n";
if (VT != StatType::NONE)
@@ -429,9 +393,11 @@ void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H,
for (const auto &E : G.edges()) {
const auto &S = E.second.S;
OS << "F" << E.first.first << " -> "
- << "F" << E.first.second << " [label=\"" << S.getAsString(ET) << "\"";
+ << "F" << E.first.second << " [label=\"" << S.getString(ET) << "\"";
if (EC != StatType::NONE)
- OS << " color=\"" << CHelper.getColorString(S.compare(EC, G.GraphEdgeMax))
+ OS << " color=\""
+ << CHelper.getColorString(
+ std::sqrt(S.getDouble(EC) / G.GraphEdgeMax.getDouble(EC)))
<< "\"";
OS << "];\n";
}
@@ -444,26 +410,20 @@ void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H,
<< (VA.SymbolName.size() > 40 ? VA.SymbolName.substr(0, 40) + "..."
: VA.SymbolName);
if (VT != StatType::NONE)
- OS << "|" << VA.S.getAsString(VT) << "}\"";
+ OS << "|" << VA.S.getString(VT) << "}\"";
else
OS << "\"";
if (VC != StatType::NONE)
- OS << " color=\"" << CHelper.getColorString(VA.S.compare(VC, G.GraphVertexMax))
+ OS << " color=\""
+ << CHelper.getColorString(
+ std::sqrt(VA.S.getDouble(VC) / G.GraphVertexMax.getDouble(VC)))
<< "\"";
OS << "];\n";
}
OS << "}\n";
}
-// Here we register and implement the llvm-xray graph subcommand.
-// The bulk of this code reads in the options, opens the required files, uses
-// those files to create a context for analysing the xray trace, then there is a
-// short loop which actually analyses the trace, generates the graph and then
-// outputs it as a DOT.
-//
-// FIXME: include additional filtering and annalysis passes to provide more
-// specific useful information.
-static CommandRegistration Unused(&GraphC, []() -> Error {
+Expected<GraphRenderer> GraphRenderer::Factory::getGraphRenderer() {
InstrumentationMap Map;
if (!GraphInstrMap.empty()) {
auto InstrumentationMapOrError = loadInstrumentationMap(GraphInstrMap);
@@ -477,30 +437,16 @@ static CommandRegistration Unused(&GraphC, []() -> Error {
}
const auto &FunctionAddresses = Map.getFunctionAddresses();
+
symbolize::LLVMSymbolizer::Options Opts(
symbolize::FunctionNameKind::LinkageName, true, true, false, "");
symbolize::LLVMSymbolizer Symbolizer(Opts);
- llvm::xray::FuncIdConversionHelper FuncIdHelper(GraphInstrMap, Symbolizer,
- FunctionAddresses);
- xray::GraphRenderer GR(FuncIdHelper, GraphDeduceSiblingCalls);
- std::error_code EC;
- raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::F_Text);
- if (EC)
- return make_error<StringError>(
- Twine("Cannot open file '") + GraphOutput + "' for writing.", EC);
-
- auto TraceOrErr = loadTraceFile(GraphInput, true);
- if (!TraceOrErr)
- return joinErrors(
- make_error<StringError>(Twine("Failed loading input file '") +
- GraphInput + "'",
- make_error_code(llvm::errc::invalid_argument)),
- TraceOrErr.takeError());
-
- auto &Trace = *TraceOrErr;
const auto &Header = Trace.getFileHeader();
- // Here we generate the call graph from entries we find in the trace.
+ llvm::xray::FuncIdConversionHelper FuncIdHelper(InstrMap, Symbolizer,
+ FunctionAddresses);
+
+ xray::GraphRenderer GR(FuncIdHelper, DeduceSiblingCalls);
for (const auto &Record : Trace) {
auto E = GR.accountRecord(Record);
if (!E)
@@ -523,7 +469,53 @@ static CommandRegistration Unused(&GraphC, []() -> Error {
handleAllErrors(std::move(E),
[&](const ErrorInfoBase &E) { E.log(errs()); });
}
- GR.exportGraphAsDOT(OS, Header, GraphEdgeLabel, GraphEdgeColorType,
- GraphVertexLabel, GraphVertexColorType);
+
+ 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/tools/llvm-xray/xray-graph.h b/tools/llvm-xray/xray-graph.h
index 1c7a3c0ef454b..a43df265d0e1f 100644
--- a/tools/llvm-xray/xray-graph.h
+++ b/tools/llvm-xray/xray-graph.h
@@ -41,17 +41,18 @@ public:
/// An inner struct for common timing statistics information
struct TimeStat {
- uint64_t Count = 0;
- double Min = 0;
- double Median = 0;
- double Pct90 = 0;
- double Pct99 = 0;
- double Max = 0;
- double Sum = 0;
- std::string getAsString(StatType T) const;
- double compare(StatType T, const TimeStat &Other) const;
+ int64_t Count;
+ double Min;
+ double Median;
+ double Pct90;
+ double Pct99;
+ double Max;
+ double Sum;
+
+ std::string getString(StatType T) const;
+ double getDouble(StatType T) const;
};
- typedef uint64_t TimestampT;
+ using TimestampT = uint64_t;
/// An inner struct for storing edge attributes for our graph. Here the
/// attributes are mainly function call statistics.
@@ -68,7 +69,7 @@ public:
/// FIXME: Store more attributes based on instrumentation map.
struct FunctionStats {
std::string SymbolName;
- TimeStat S;
+ TimeStat S = {};
};
struct FunctionAttr {
@@ -76,10 +77,10 @@ public:
uint64_t TSC;
};
- typedef SmallVector<FunctionAttr, 4> FunctionStack;
+ using FunctionStack = SmallVector<FunctionAttr, 4>;
- typedef DenseMap<llvm::sys::ProcessInfo::ProcessId, FunctionStack>
- PerThreadFunctionStackMap;
+ using PerThreadFunctionStackMap =
+ DenseMap<llvm::sys::ProcessInfo::ProcessId, FunctionStack>;
class GraphT : public Graph<FunctionStats, CallStats, int32_t> {
public:
@@ -88,8 +89,8 @@ public:
};
GraphT G;
- typedef typename decltype(G)::VertexIdentifier VertexIdentifier;
- typedef typename decltype(G)::EdgeIdentifier EdgeIdentifier;
+ using VertexIdentifier = typename decltype(G)::VertexIdentifier;
+ using EdgeIdentifier = decltype(G)::EdgeIdentifier;
/// Use a Map to store the Function stack for each thread whilst building the
/// graph.
@@ -98,7 +99,7 @@ public:
PerThreadFunctionStackMap PerThreadFunctionStack;
/// Usefull object for getting human readable Symbol Names.
- const FuncIdConversionHelper &FuncIdHelper;
+ FuncIdConversionHelper FuncIdHelper;
bool DeduceSiblingCalls = false;
TimestampT CurrentMaxTSC = 0;
@@ -143,22 +144,90 @@ public:
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, const XRayFileHeader &H,
- StatType EdgeLabel = StatType::NONE,
+ void exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel = StatType::NONE,
StatType EdgeColor = StatType::NONE,
StatType VertexLabel = StatType::NONE,
StatType VertexColor = StatType::NONE);
/// Get a reference to the internal graph.
- const GraphT &getGraph() {
- calculateEdgeStatistics();
- calculateVertexStatistics();
- return G;
- }
+ 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