diff options
Diffstat (limited to 'tools')
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 |
