diff options
author | Zachary Turner <zturner@google.com> | 2017-06-15 22:24:24 +0000 |
---|---|---|
committer | Zachary Turner <zturner@google.com> | 2017-06-15 22:24:24 +0000 |
commit | 7e5d31edd7148213417aa3a99615eea97f8b279e (patch) | |
tree | 488fa65c324e5dfe14bce399902da1675373cabd /tools/llvm-pdbutil | |
parent | 02688b00ef03018033c601d8d83c6baf7a596c7f (diff) |
Resubmit "[llvm-pdbutil] rewrite the "raw" output style."
This resubmits commit c0c249e9f2ef83e1d1e5f166b50673d92f3579d7.
It was broken due to some weird template issues, which have
since been fixed.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@305517 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/llvm-pdbutil')
-rw-r--r-- | tools/llvm-pdbutil/CMakeLists.txt | 5 | ||||
-rw-r--r-- | tools/llvm-pdbutil/FormatUtil.cpp | 49 | ||||
-rw-r--r-- | tools/llvm-pdbutil/FormatUtil.h | 120 | ||||
-rw-r--r-- | tools/llvm-pdbutil/LLVMOutputStyle.cpp | 1198 | ||||
-rw-r--r-- | tools/llvm-pdbutil/LinePrinter.cpp | 33 | ||||
-rw-r--r-- | tools/llvm-pdbutil/LinePrinter.h | 31 | ||||
-rw-r--r-- | tools/llvm-pdbutil/MinimalSymbolDumper.cpp | 749 | ||||
-rw-r--r-- | tools/llvm-pdbutil/MinimalSymbolDumper.h | 47 | ||||
-rw-r--r-- | tools/llvm-pdbutil/MinimalTypeDumper.cpp | 532 | ||||
-rw-r--r-- | tools/llvm-pdbutil/MinimalTypeDumper.h | 56 | ||||
-rw-r--r-- | tools/llvm-pdbutil/RawOutputStyle.cpp | 669 | ||||
-rw-r--r-- | tools/llvm-pdbutil/RawOutputStyle.h (renamed from tools/llvm-pdbutil/LLVMOutputStyle.h) | 35 | ||||
-rw-r--r-- | tools/llvm-pdbutil/YAMLOutputStyle.cpp | 21 | ||||
-rw-r--r-- | tools/llvm-pdbutil/llvm-pdbutil.cpp | 172 | ||||
-rw-r--r-- | tools/llvm-pdbutil/llvm-pdbutil.h | 45 |
15 files changed, 2399 insertions, 1363 deletions
diff --git a/tools/llvm-pdbutil/CMakeLists.txt b/tools/llvm-pdbutil/CMakeLists.txt index 9875dfb5a25..d09fa31d8b6 100644 --- a/tools/llvm-pdbutil/CMakeLists.txt +++ b/tools/llvm-pdbutil/CMakeLists.txt @@ -12,8 +12,10 @@ add_llvm_tool(llvm-pdbutil CompactTypeDumpVisitor.cpp Diff.cpp llvm-pdbutil.cpp + FormatUtil.cpp LinePrinter.cpp - LLVMOutputStyle.cpp + MinimalSymbolDumper.cpp + MinimalTypeDumper.cpp PdbYaml.cpp PrettyBuiltinDumper.cpp PrettyClassDefinitionDumper.cpp @@ -25,6 +27,7 @@ add_llvm_tool(llvm-pdbutil PrettyTypeDumper.cpp PrettyTypedefDumper.cpp PrettyVariableDumper.cpp + RawOutputStyle.cpp StreamUtil.cpp YAMLOutputStyle.cpp ) diff --git a/tools/llvm-pdbutil/FormatUtil.cpp b/tools/llvm-pdbutil/FormatUtil.cpp new file mode 100644 index 00000000000..007db9ee9f9 --- /dev/null +++ b/tools/llvm-pdbutil/FormatUtil.cpp @@ -0,0 +1,49 @@ +//===- FormatUtil.cpp ----------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FormatUtil.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::pdb; + +std::string llvm::pdb::typesetItemList(ArrayRef<std::string> Opts, + uint32_t GroupSize, uint32_t IndentLevel, + StringRef Sep) { + std::string Result; + while (!Opts.empty()) { + ArrayRef<std::string> ThisGroup; + ThisGroup = Opts.take_front(GroupSize); + Opts = Opts.drop_front(ThisGroup.size()); + Result += join(ThisGroup, Sep); + if (!Opts.empty()) { + Result += Sep; + Result += "\n"; + Result += formatv("{0}", fmt_repeat(' ', IndentLevel)); + } + } + return Result; +} + +std::string llvm::pdb::typesetStringList(uint32_t IndentLevel, + ArrayRef<StringRef> Strings) { + std::string Result = "["; + for (const auto &S : Strings) { + Result += formatv("\n{0}{1}", fmt_repeat(' ', IndentLevel), S); + } + Result += "]"; + return Result; +} + +std::string llvm::pdb::formatSegmentOffset(uint16_t Segment, uint32_t Offset) { + return formatv("{0:4}:{1:4}", Segment, Offset); +} diff --git a/tools/llvm-pdbutil/FormatUtil.h b/tools/llvm-pdbutil/FormatUtil.h new file mode 100644 index 00000000000..3db2dbacc57 --- /dev/null +++ b/tools/llvm-pdbutil/FormatUtil.h @@ -0,0 +1,120 @@ +//===- FormatUtil.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_LLVMPDBUTIL_FORMAT_UTIL_H +#define LLVM_TOOLS_LLVMPDBUTIL_FORMAT_UTIL_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +#include <string> +#include <type_traits> + +namespace llvm { +namespace pdb { + +#define PUSH_MASKED_FLAG(Enum, Mask, TheOpt, Value, Text) \ + if (Enum::TheOpt == (Value & Mask)) \ + Opts.push_back(Text); + +#define PUSH_FLAG(Enum, TheOpt, Value, Text) \ + PUSH_MASKED_FLAG(Enum, Enum::TheOpt, TheOpt, Value, Text) + +#define RETURN_CASE(Enum, X, Ret) \ + case Enum::X: \ + return Ret; + +template <typename T> static std::string formatUnknownEnum(T Value) { + return formatv("unknown ({0})", + static_cast<typename std::underlying_type<T>::type>(Value)) + .str(); +} + +std::string formatSegmentOffset(uint16_t Segment, uint32_t Offset); + +std::string typesetItemList(ArrayRef<std::string> Opts, uint32_t IndentLevel, + uint32_t GroupSize, StringRef Sep); + +std::string typesetStringList(uint32_t IndentLevel, + ArrayRef<StringRef> Strings); + +/// Returns the number of digits in the given integer. +inline int NumDigits(uint64_t N) { + if (N < 10ULL) + return 1; + if (N < 100ULL) + return 2; + if (N < 1000ULL) + return 3; + if (N < 10000ULL) + return 4; + if (N < 100000ULL) + return 5; + if (N < 1000000ULL) + return 6; + if (N < 10000000ULL) + return 7; + if (N < 100000000ULL) + return 8; + if (N < 1000000000ULL) + return 9; + if (N < 10000000000ULL) + return 10; + if (N < 100000000000ULL) + return 11; + if (N < 1000000000000ULL) + return 12; + if (N < 10000000000000ULL) + return 13; + if (N < 100000000000000ULL) + return 14; + if (N < 1000000000000000ULL) + return 15; + if (N < 10000000000000000ULL) + return 16; + if (N < 100000000000000000ULL) + return 17; + if (N < 1000000000000000000ULL) + return 18; + if (N < 10000000000000000000ULL) + return 19; + return 20; +} + +namespace detail { +template <typename T> +struct EndianAdapter final + : public FormatAdapter<support::detail::packed_endian_specific_integral< + T, support::little, support::unaligned>> { + using EndianType = + support::detail::packed_endian_specific_integral<T, support::little, + support::unaligned>; + + explicit EndianAdapter(EndianType &&Item) + : FormatAdapter<EndianType>(std::move(Item)) {} + + void format(llvm::raw_ostream &Stream, StringRef Style) { + format_provider<T>::format(static_cast<T>(this->Item), Stream, Style); + } +}; +} // namespace detail + +template <typename T> +detail::EndianAdapter<T> +fmtle(support::detail::packed_endian_specific_integral<T, support::little, + support::unaligned> + Value) { + return detail::EndianAdapter<T>(std::move(Value)); +} +} +} // namespace llvm +#endif diff --git a/tools/llvm-pdbutil/LLVMOutputStyle.cpp b/tools/llvm-pdbutil/LLVMOutputStyle.cpp deleted file mode 100644 index c4af4583a10..00000000000 --- a/tools/llvm-pdbutil/LLVMOutputStyle.cpp +++ /dev/null @@ -1,1198 +0,0 @@ -//===- LLVMOutputStyle.cpp ------------------------------------ *- C++ --*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "LLVMOutputStyle.h" - -#include "CompactTypeDumpVisitor.h" -#include "StreamUtil.h" -#include "llvm-pdbutil.h" - -#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" -#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" -#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" -#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" -#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" -#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" -#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" -#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" -#include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h" -#include "llvm/DebugInfo/CodeView/DebugSymbolRVASubsection.h" -#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" -#include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h" -#include "llvm/DebugInfo/CodeView/EnumTables.h" -#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" -#include "llvm/DebugInfo/CodeView/Line.h" -#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" -#include "llvm/DebugInfo/CodeView/SymbolDumper.h" -#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h" -#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" -#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" -#include "llvm/DebugInfo/MSF/MappedBlockStream.h" -#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" -#include "llvm/DebugInfo/PDB/Native/DbiStream.h" -#include "llvm/DebugInfo/PDB/Native/EnumTables.h" -#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" -#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" -#include "llvm/DebugInfo/PDB/Native/InfoStream.h" -#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" -#include "llvm/DebugInfo/PDB/Native/PDBFile.h" -#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" -#include "llvm/DebugInfo/PDB/Native/RawError.h" -#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" -#include "llvm/DebugInfo/PDB/Native/TpiStream.h" -#include "llvm/DebugInfo/PDB/PDBExtras.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/BinaryStreamReader.h" -#include "llvm/Support/FormatVariadic.h" - -#include <unordered_map> - -using namespace llvm; -using namespace llvm::codeview; -using namespace llvm::msf; -using namespace llvm::pdb; - -namespace { -struct PageStats { - explicit PageStats(const BitVector &FreePages) - : Upm(FreePages), ActualUsedPages(FreePages.size()), - MultiUsePages(FreePages.size()), UseAfterFreePages(FreePages.size()) { - const_cast<BitVector &>(Upm).flip(); - // To calculate orphaned pages, we start with the set of pages that the - // MSF thinks are used. Each time we find one that actually *is* used, - // we unset it. Whichever bits remain set at the end are orphaned. - OrphanedPages = Upm; - } - - // The inverse of the MSF File's copy of the Fpm. The basis for which we - // determine the allocation status of each page. - const BitVector Upm; - - // Pages which are marked as used in the FPM and are used at least once. - BitVector ActualUsedPages; - - // Pages which are marked as used in the FPM but are used more than once. - BitVector MultiUsePages; - - // Pages which are marked as used in the FPM but are not used at all. - BitVector OrphanedPages; - - // Pages which are marked free in the FPM but are used. - BitVector UseAfterFreePages; -}; - -class C13RawVisitor : public DebugSubsectionVisitor { -public: - C13RawVisitor(ScopedPrinter &P, LazyRandomTypeCollection &TPI, - LazyRandomTypeCollection *IPI) - : P(P), TPI(TPI), IPI(IPI) {} - - Error visitUnknown(DebugUnknownSubsectionRef &Unknown) override { - if (!opts::checkModuleSubsection(opts::ModuleSubsection::Unknown)) - return Error::success(); - DictScope DD(P, "Unknown"); - P.printHex("Kind", static_cast<uint32_t>(Unknown.kind())); - ArrayRef<uint8_t> Data; - BinaryStreamReader Reader(Unknown.getData()); - consumeError(Reader.readBytes(Data, Reader.bytesRemaining())); - P.printBinaryBlock("Data", Data); - return Error::success(); - } - - Error visitLines(DebugLinesSubsectionRef &Lines, - const StringsAndChecksumsRef &State) override { - if (!opts::checkModuleSubsection(opts::ModuleSubsection::Lines)) - return Error::success(); - - DictScope DD(P, "Lines"); - - P.printNumber("RelocSegment", Lines.header()->RelocSegment); - P.printNumber("RelocOffset", Lines.header()->RelocOffset); - P.printNumber("CodeSize", Lines.header()->CodeSize); - P.printBoolean("HasColumns", Lines.hasColumnInfo()); - - for (const auto &L : Lines) { - DictScope DDDD(P, "FileEntry"); - - if (auto EC = printFileName("FileName", L.NameIndex, State)) - return EC; - - for (const auto &N : L.LineNumbers) { - DictScope DDD(P, "Line"); - LineInfo LI(N.Flags); - P.printNumber("Offset", N.Offset); - if (LI.isAlwaysStepInto()) - P.printString("StepInto", StringRef("Always")); - else if (LI.isNeverStepInto()) - P.printString("StepInto", StringRef("Never")); - else - P.printNumber("LineNumberStart", LI.getStartLine()); - P.printNumber("EndDelta", LI.getLineDelta()); - P.printBoolean("IsStatement", LI.isStatement()); - } - for (const auto &C : L.Columns) { - DictScope DDD(P, "Column"); - P.printNumber("Start", C.StartColumn); - P.printNumber("End", C.EndColumn); - } - } - - return Error::success(); - } - - Error visitFileChecksums(DebugChecksumsSubsectionRef &Checksums, - const StringsAndChecksumsRef &State) override { - if (!opts::checkModuleSubsection(opts::ModuleSubsection::FileChecksums)) - return Error::success(); - - DictScope DD(P, "FileChecksums"); - for (const auto &CS : Checksums) { - DictScope DDD(P, "Checksum"); - if (auto Result = getNameFromStringTable(CS.FileNameOffset, State)) - P.printString("FileName", *Result); - else - return Result.takeError(); - P.printEnum("Kind", uint8_t(CS.Kind), getFileChecksumNames()); - P.printBinaryBlock("Checksum", CS.Checksum); - } - return Error::success(); - } - - Error visitInlineeLines(DebugInlineeLinesSubsectionRef &Inlinees, - const StringsAndChecksumsRef &State) override { - if (!opts::checkModuleSubsection(opts::ModuleSubsection::InlineeLines)) - return Error::success(); - - DictScope D(P, "InlineeLines"); - P.printBoolean("HasExtraFiles", Inlinees.hasExtraFiles()); - ListScope LS(P, "Lines"); - for (const auto &L : Inlinees) { - DictScope DDD(P, "Inlinee"); - if (auto EC = printFileName("FileName", L.Header->FileID, State)) - return EC; - - if (auto EC = dumpTypeRecord("Function", L.Header->Inlinee)) - return EC; - P.printNumber("SourceLine", L.Header->SourceLineNum); - if (Inlinees.hasExtraFiles()) { - ListScope DDDD(P, "ExtraFiles"); - for (const auto &EF : L.ExtraFiles) { - if (auto EC = printFileName("File", EF, State)) - return EC; - } - } - } - return Error::success(); - } - - Error visitCrossModuleExports(DebugCrossModuleExportsSubsectionRef &CSE, - const StringsAndChecksumsRef &State) override { - if (!opts::checkModuleSubsection(opts::ModuleSubsection::CrossScopeExports)) - return Error::success(); - - ListScope D(P, "CrossModuleExports"); - for (const auto &M : CSE) { - DictScope D(P, "Export"); - P.printHex("Local", M.Local); - P.printHex("Global", M.Global); - } - return Error::success(); - } - - Error visitCrossModuleImports(DebugCrossModuleImportsSubsectionRef &CSI, - const StringsAndChecksumsRef &State) override { - if (!opts::checkModuleSubsection(opts::ModuleSubsection::CrossScopeImports)) - return Error::success(); - - ListScope L(P, "CrossModuleImports"); - for (const auto &M : CSI) { - DictScope D(P, "ModuleImport"); - auto Name = getNameFromStringTable(M.Header->ModuleNameOffset, State); - if (!Name) - return Name.takeError(); - P.printString("Module", *Name); - P.printHexList("Imports", M.Imports); - } - return Error::success(); - } - - Error visitFrameData(DebugFrameDataSubsectionRef &FD, - const StringsAndChecksumsRef &State) override { - if (!opts::checkModuleSubsection(opts::ModuleSubsection::FrameData)) - return Error::success(); - - ListScope L(P, "FrameData"); - for (const auto &Frame : FD) { - DictScope D(P, "Frame"); - auto Name = getNameFromStringTable(Frame.FrameFunc, State); - if (!Name) - return joinErrors(make_error<RawError>(raw_error_code::invalid_format, - "Invalid Frame.FrameFunc index"), - Name.takeError()); - P.printNumber("Rva", Frame.RvaStart); - P.printNumber("CodeSize", Frame.CodeSize); - P.printNumber("LocalSize", Frame.LocalSize); - P.printNumber("ParamsSize", Frame.ParamsSize); - P.printNumber("MaxStackSize", Frame.MaxStackSize); - P.printString("FrameFunc", *Name); - P.printNumber("PrologSize", Frame.PrologSize); - P.printNumber("SavedRegsSize", Frame.SavedRegsSize); - P.printNumber("Flags", Frame.Flags); - } - return Error::success(); - } - - Error visitSymbols(DebugSymbolsSubsectionRef &Symbols, - const StringsAndChecksumsRef &State) override { - if (!opts::checkModuleSubsection(opts::ModuleSubsection::Symbols)) - return Error::success(); - ListScope L(P, "Symbols"); - - // This section should not actually appear in a PDB file, it really only - // appears in object files. But we support it here for testing. So we - // specify the Object File container type. - codeview::CVSymbolDumper SD(P, TPI, CodeViewContainer::ObjectFile, nullptr, - false); - for (auto S : Symbols) { - DictScope LL(P, ""); - if (auto EC = SD.dump(S)) { - return make_error<RawError>( - raw_error_code::corrupt_file, - "DEBUG_S_SYMBOLS subsection contained corrupt symbol record"); - } - } - return Error::success(); - } - - Error visitStringTable(DebugStringTableSubsectionRef &Strings, - const StringsAndChecksumsRef &State) override { - if (!opts::checkModuleSubsection(opts::ModuleSubsection::StringTable)) - return Error::success(); - - ListScope D(P, "String Table"); - BinaryStreamReader Reader(Strings.getBuffer()); - StringRef S; - consumeError(Reader.readCString(S)); - while (Reader.bytesRemaining() > 0) { - consumeError(Reader.readCString(S)); - if (S.empty() && Reader.bytesRemaining() < 4) - break; - P.printString(S); - } - return Error::success(); - } - - Error visitCOFFSymbolRVAs(DebugSymbolRVASubsectionRef &RVAs, - const StringsAndChecksumsRef &State) override { - if (!opts::checkModuleSubsection(opts::ModuleSubsection::CoffSymbolRVAs)) - return Error::success(); - - ListScope D(P, "COFF Symbol RVAs"); - P.printHexList("RVAs", RVAs); - return Error::success(); - } - -private: - Error dumpTypeRecord(StringRef Label, TypeIndex Index) { - bool Success = false; - DictScope D(P, Label); - if (IPI) { - CompactTypeDumpVisitor CTDV(*IPI, Index, &P); - if (IPI->contains(Index)) { - CVType Type = IPI->getType(Index); - if (auto EC = codeview::visitTypeRecord(Type, CTDV)) - return EC; - } - } - - if (!Success) { - P.printString( - llvm::formatv("Index: {0:x} (unknown function)", Index.getIndex()) - .str()); - } - return Error::success(); - } - Error printFileName(StringRef Label, uint32_t Offset, - const StringsAndChecksumsRef &State) { - if (auto Result = getNameFromChecksumsBuffer(Offset, State)) { - P.printString(Label, *Result); - return Error::success(); - } else - return Result.takeError(); - } - - Expected<StringRef> - getNameFromStringTable(uint32_t Offset, const StringsAndChecksumsRef &State) { - return State.strings().getString(Offset); - } - - Expected<StringRef> - getNameFromChecksumsBuffer(uint32_t Offset, - const StringsAndChecksumsRef &State) { - auto Array = State.checksums().getArray(); - auto ChecksumIter = Array.at(Offset); - if (ChecksumIter == Array.end()) - return make_error<RawError>(raw_error_code::invalid_format); - const auto &Entry = *ChecksumIter; - return getNameFromStringTable(Entry.FileNameOffset, State); - } - - ScopedPrinter &P; - LazyRandomTypeCollection &TPI; - LazyRandomTypeCollection *IPI; -}; -} - -static void recordKnownUsedPage(PageStats &Stats, uint32_t UsedIndex) { - if (Stats.Upm.test(UsedIndex)) { - if (Stats.ActualUsedPages.test(UsedIndex)) - Stats.MultiUsePages.set(UsedIndex); - Stats.ActualUsedPages.set(UsedIndex); - Stats.OrphanedPages.reset(UsedIndex); - } else { - // The MSF doesn't think this page is used, but it is. - Stats.UseAfterFreePages.set(UsedIndex); - } -} - -static void printSectionOffset(llvm::raw_ostream &OS, - const SectionOffset &Off) { - OS << Off.Off << ", " << Off.Isect; -} - -LLVMOutputStyle::LLVMOutputStyle(PDBFile &File) : File(File), P(outs()) {} - -Error LLVMOutputStyle::dump() { - if (auto EC = dumpFileHeaders()) - return EC; - - if (auto EC = dumpStreamSummary()) - return EC; - - if (auto EC = dumpFreePageMap()) - return EC; - - if (auto EC = dumpStreamBlocks()) - return EC; - - if (auto EC = dumpBlockRanges()) - return EC; - - if (auto EC = dumpStreamBytes()) - return EC; - - if (auto EC = dumpStringTable()) - return EC; - - if (auto EC = dumpInfoStream()) - return EC; - - if (auto EC = dumpTpiStream(StreamTPI)) - return EC; - - if (auto EC = dumpTpiStream(StreamIPI)) - return EC; - - if (auto EC = dumpDbiStream()) - return EC; - - if (auto EC = dumpSectionContribs()) - return EC; - - if (auto EC = dumpSectionMap()) - return EC; - - if (auto EC = dumpGlobalsStream()) - return EC; - - if (auto EC = dumpPublicsStream()) - return EC; - - if (auto EC = dumpSectionHeaders()) - return EC; - - if (auto EC = dumpFpoStream()) - return EC; - - flush(); - - return Error::success(); -} - -Error LLVMOutputStyle::dumpFileHeaders() { - if (!opts::raw::DumpHeaders) - return Error::success(); - - DictScope D(P, "FileHeaders"); - P.printNumber("BlockSize", File.getBlockSize()); - P.printNumber("FreeBlockMap", File.getFreeBlockMapBlock()); - P.printNumber("NumBlocks", File.getBlockCount()); - P.printNumber("NumDirectoryBytes", File.getNumDirectoryBytes()); - P.printNumber("Unknown1", File.getUnknown1()); - P.printNumber("BlockMapAddr", File.getBlockMapIndex()); - P.printNumber("NumDirectoryBlocks", File.getNumDirectoryBlocks()); - - // The directory is not contiguous. Instead, the block map contains a - // contiguous list of block numbers whose contents, when concatenated in - // order, make up the directory. - P.printList("DirectoryBlocks", File.getDirectoryBlockArray()); - P.printNumber("NumStreams", File.getNumStreams()); - return Error::success(); -} - -Error LLVMOutputStyle::dumpStreamSummary() { - if (!opts::raw::DumpStreamSummary) - return Error::success(); - - if (StreamPurposes.empty()) - discoverStreamPurposes(File, StreamPurposes); - - uint32_t StreamCount = File.getNumStreams(); - - ListScope L(P, "Streams"); - for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { - std::string Label("Stream "); - Label += to_string(StreamIdx); - - std::string Value = "[" + StreamPurposes[StreamIdx] + "] ("; - Value += to_string(File.getStreamByteSize(StreamIdx)); - Value += " bytes)"; - - P.printString(Label, Value); - } - - P.flush(); - return Error::success(); -} - -Error LLVMOutputStyle::dumpFreePageMap() { - if (!opts::raw::DumpPageStats) - return Error::success(); - - // Start with used pages instead of free pages because - // the number of free pages is far larger than used pages. - BitVector FPM = File.getMsfLayout().FreePageMap; - - PageStats PS(FPM); - - recordKnownUsedPage(PS, 0); // MSF Super Block - - uint32_t BlocksPerSection = msf::getFpmIntervalLength(File.getMsfLayout()); - uint32_t NumSections = msf::getNumFpmIntervals(File.getMsfLayout()); - for (uint32_t I = 0; I < NumSections; ++I) { - uint32_t Fpm0 = 1 + BlocksPerSection * I; - // 2 Fpm blocks spaced at `getBlockSize()` block intervals - recordKnownUsedPage(PS, Fpm0); - recordKnownUsedPage(PS, Fpm0 + 1); - } - - recordKnownUsedPage(PS, File.getBlockMapIndex()); // Stream Table - - for (auto DB : File.getDirectoryBlockArray()) - recordKnownUsedPage(PS, DB); - - // Record pages used by streams. Note that pages for stream 0 - // are considered being unused because that's what MSVC tools do. - // Stream 0 doesn't contain actual data, so it makes some sense, - // though it's a bit confusing to us. - for (auto &SE : File.getStreamMap().drop_front(1)) - for (auto &S : SE) - recordKnownUsedPage(PS, S); - - dumpBitVector("Msf Free Pages", FPM); - dumpBitVector("Orphaned Pages", PS.OrphanedPages); - dumpBitVector("Multiply Used Pages", PS.MultiUsePages); - dumpBitVector("Use After Free Pages", PS.UseAfterFreePages); - return Error::success(); -} - -void LLVMOutputStyle::dumpBitVector(StringRef Name, const BitVector &V) { - std::vector<uint32_t> Vec; - for (uint32_t I = 0, E = V.size(); I != E; ++I) - if (V[I]) - Vec.push_back(I); - P.printList(Name, Vec); -} - -Error LLVMOutputStyle::dumpGlobalsStream() { - if (!opts::raw::DumpGlobals) - return Error::success(); - if (!File.hasPDBGlobalsStream()) { - P.printString("Globals Stream not present"); - return Error::success(); - } - - auto Globals = File.getPDBGlobalsStream(); - if (!Globals) - return Globals.takeError(); - DictScope D(P, "Globals Stream"); - - auto Dbi = File.getPDBDbiStream(); - if (!Dbi) - return Dbi.takeError(); - - P.printNumber("Stream number", Dbi->getGlobalSymbolStreamIndex()); - P.printNumber("Number of buckets", Globals->getNumBuckets()); - P.printList("Hash Buckets", Globals->getHashBuckets()); - - return Error::success(); -} - -Error LLVMOutputStyle::dumpStreamBlocks() { - if (!opts::raw::DumpStreamBlocks) - return Error::success(); - - ListScope L(P, "StreamBlocks"); - uint32_t StreamCount = File.getNumStreams(); - for (uint32_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { - std::string Name("Stream "); - Name += to_string(StreamIdx); - auto StreamBlocks = File.getStreamBlockList(StreamIdx); - P.printList(Name, StreamBlocks); - } - return Error::success(); -} - -Error LLVMOutputStyle::dumpBlockRanges() { - if (!opts::raw::DumpBlockRange.hasValue()) - return Error::success(); - auto &R = *opts::raw::DumpBlockRange; - uint32_t Max = R.Max.getValueOr(R.Min); - - if (Max < R.Min) - return make_error<StringError>( - "Invalid block range specified. Max < Min", - std::make_error_code(std::errc::bad_address)); - if (Max >= File.getBlockCount()) - return make_error<StringError>( - "Invalid block range specified. Requested block out of bounds", - std::make_error_code(std::errc::bad_address)); - - DictScope D(P, "Block Data"); - for (uint32_t I = R.Min; I <= Max; ++I) { - auto ExpectedData = File.getBlockData(I, File.getBlockSize()); - if (!ExpectedData) - return ExpectedData.takeError(); - std::string Label; - llvm::raw_string_ostream S(Label); - S << "Block " << I; - S.flush(); - P.printBinaryBlock(Label, *ExpectedData); - } - - return Error::success(); -} - -static Error parseStreamSpec(StringRef Str, uint32_t &SI, uint32_t &Offset, - uint32_t &Size) { - if (Str.consumeInteger(0, SI)) - return make_error<RawError>(raw_error_code::invalid_format, - "Invalid Stream Specification"); - if (Str.consume_front(":")) { - if (Str.consumeInteger(0, Offset)) - return make_error<RawError>(raw_error_code::invalid_format, - "Invalid Stream Specification"); - } - if (Str.consume_front("@")) { - if (Str.consumeInteger(0, Size)) - return make_error<RawError>(raw_error_code::invalid_format, - "Invalid Stream Specification"); - } - if (!Str.empty()) - return make_error<RawError>(raw_error_code::invalid_format, - "Invalid Stream Specification"); - return Error::success(); -} - -Error LLVMOutputStyle::dumpStreamBytes() { - if (opts::raw::DumpStreamData.empty()) - return Error::success(); - - if (StreamPurposes.empty()) - discoverStreamPurposes(File, StreamPurposes); - - DictScope D(P, "Stream Data"); - for (auto &Str : opts::raw::DumpStreamData) { - uint32_t SI = 0; - uint32_t Begin = 0; - uint32_t Size = 0; - uint32_t End = 0; - - if (auto EC = parseStreamSpec(Str, SI, Begin, Size)) - return EC; - - if (SI >= File.getNumStreams()) - return make_error<RawError>(raw_error_code::no_stream); - - auto S = MappedBlockStream::createIndexedStream( - File.getMsfLayout(), File.getMsfBuffer(), SI, File.getAllocator()); - if (!S) - continue; - DictScope DD(P, "Stream"); - if (Size == 0) - End = S->getLength(); - else { - End = Begin + Size; - if (End >= S->getLength()) - return make_error<RawError>(raw_error_code::index_out_of_bounds, - "Stream is not long enough!"); - } - - P.printNumber("Index", SI); - P.printString("Type", StreamPurposes[SI]); - P.printNumber("Size", S->getLength()); - auto Blocks = File.getMsfLayout().StreamMap[SI]; - P.printList("Blocks", Blocks); - - BinaryStreamReader R(*S); - ArrayRef<uint8_t> StreamData; - if (auto EC = R.readBytes(StreamData, S->getLength())) - return EC; - Size = End - Begin; - StreamData = StreamData.slice(Begin, Size); - P.printBinaryBlock("Data", StreamData, Begin); - } - return Error::success(); -} - -Error LLVMOutputStyle::dumpStringTable() { - if (!opts::raw::DumpStringTable) - return Error::success(); - - auto IS = File.getStringTable(); - if (!IS) - return IS.takeError(); - - DictScope D(P, "String Table"); - for (uint32_t I : IS->name_ids()) { - auto ES = IS->getStringForID(I); - if (!ES) - return ES.takeError(); - - if (ES->empty()) - continue; - llvm::SmallString<32> Str; - Str.append("'"); - Str.append(*ES); - Str.append("'"); - P.printString(Str); - } - return Error::success(); -} - -Error LLVMOutputStyle::dumpInfoStream() { - if (!opts::raw::DumpHeaders) - return Error::success(); - if (!File.hasPDBInfoStream()) { - P.printString("PDB Stream not present"); - return Error::success(); - } - auto IS = File.getPDBInfoStream(); - if (!IS) - return IS.takeError(); - - DictScope D(P, "PDB Stream"); - P.printNumber("Version", IS->getVersion()); - P.printHex("Signature", IS->getSignature()); - P.printNumber("Age", IS->getAge()); - P.printObject("Guid", IS->getGuid()); - P.printHex("Features", IS->getFeatures()); - { - DictScope DD(P, "Named Streams"); - for (const auto &S : IS->getNamedStreams().entries()) - P.printObject(S.getKey(), S.getValue()); - } - return Error::success(); -} - -namespace { -class RecordBytesVisitor : public TypeVisitorCallbacks { -public: - explicit RecordBytesVisitor(ScopedPrinter &P) : P(P) {} - - Error visitTypeEnd(CVType &Record) override { - P.printBinaryBlock("Bytes", Record.content()); - return Error::success(); - } - -private: - ScopedPrinter &P; -}; -} - -Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) { - assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); - - bool DumpRecordBytes = false; - bool DumpRecords = false; - bool DumpTpiHash = false; - StringRef Label; - StringRef VerLabel; - if (StreamIdx == StreamTPI) { - if (!File.hasPDBTpiStream()) { - P.printString("Type Info Stream (TPI) not present"); - return Error::success(); - } - DumpRecordBytes = opts::raw::DumpTpiRecordBytes; - DumpRecords = opts::raw::DumpTpiRecords; - DumpTpiHash = opts::raw::DumpTpiHash; - Label = "Type Info Stream (TPI)"; - VerLabel = "TPI Version"; - } else if (StreamIdx == StreamIPI) { - auto InfoS = File.getPDBInfoStream(); - if (!InfoS) - return InfoS.takeError(); - - if (!File.hasPDBIpiStream() || !InfoS->containsIdStream()) - return Error::success(); - DumpRecordBytes = opts::raw::DumpIpiRecordBytes; - DumpRecords = opts::raw::DumpIpiRecords; - Label = "Type Info Stream (IPI)"; - VerLabel = "IPI Version"; - } - - auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream() - : File.getPDBIpiStream(); - if (!Tpi) - return Tpi.takeError(); - - auto ExpectedTypes = initializeTypeDatabase(StreamIdx); - if (!ExpectedTypes) - return ExpectedTypes.takeError(); - auto &Types = *ExpectedTypes; - - if (!DumpRecordBytes && !DumpRecords && !DumpTpiHash) - return Error::success(); - - std::unique_ptr<DictScope> StreamScope; - std::unique_ptr<ListScope> RecordScope; - - StreamScope = llvm::make_unique<DictScope>(P, Label); - P.printNumber(VerLabel, Tpi->getTpiVersion()); - P.printNumber("Record count", Tpi->getNumTypeRecords()); - - std::vector<std::unique_ptr<TypeVisitorCallbacks>> Visitors; - - // If we're in dump mode, add a dumper with the appropriate detail level. - if (DumpRecords) { - std::unique_ptr<TypeVisitorCallbacks> Dumper; - if (opts::raw::CompactRecords) - Dumper = make_unique<CompactTypeDumpVisitor>(Types, &P); - else { - assert(TpiTypes); - - auto X = make_unique<TypeDumpVisitor>(*TpiTypes, &P, false); - if (StreamIdx == StreamIPI) - X->setIpiTypes(*IpiTypes); - Dumper = std::move(X); - } - Visitors.push_back(std::move(Dumper)); - } - if (DumpRecordBytes) - Visitors.push_back(make_unique<RecordBytesVisitor>(P)); - - // We always need to deserialize and add it to the type database. This is - // true if even if we're not dumping anything, because we could need the - // type database for the purposes of dumping symbols. - TypeVisitorCallbackPipeline Pipeline; - for (const auto &V : Visitors) - Pipeline.addCallbackToPipeline(*V); - - if (DumpRecords || DumpRecordBytes) - RecordScope = llvm::make_unique<ListScope>(P, "Records"); - - Optional<TypeIndex> I = Types.getFirst(); - while (I) { - std::unique_ptr<DictScope> OneRecordScope; - - if ((DumpRecords || DumpRecordBytes) && !opts::raw::CompactRecords) - OneRecordScope = llvm::make_unique<DictScope>(P, ""); - - auto T = Types.getType(*I); - if (auto EC = codeview::visitTypeRecord(T, *I, Pipeline)) - return EC; - I = Types.getNext(*I); - } - - if (DumpTpiHash) { - DictScope DD(P, "Hash"); - P.printNumber("Number of Hash Buckets", Tpi->getNumHashBuckets()); - P.printNumber("Hash Key Size", Tpi->getHashKeySize()); - P.printList("Values", Tpi->getHashValues()); - - ListScope LHA(P, "Adjusters"); - auto ExpectedST = File.getStringTable(); - if (!ExpectedST) - return ExpectedST.takeError(); - const auto &ST = *ExpectedST; - for (const auto &E : Tpi->getHashAdjusters()) { - DictScope DHA(P); - auto Name = ST.getStringForID(E.first); - if (!Name) - return Name.takeError(); - - P.printString("Type", *Name); - P.printHex("TI", E.second); - } - } - - ListScope L(P, "TypeIndexOffsets"); - for (const auto &IO : Tpi->getTypeIndexOffsets()) { - P.printString(formatv("Index: {0:x}, Offset: {1:N}", IO.Type.getIndex(), - (uint32_t)IO.Offset) - .str()); - } - - P.flush(); - return Error::success(); -} - -Expected<codeview::LazyRandomTypeCollection &> -LLVMOutputStyle::initializeTypeDatabase(uint32_t SN) { - auto &TypeCollection = (SN == StreamTPI) ? TpiTypes : IpiTypes; - auto Tpi = - (SN == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream(); - if (!Tpi) - return Tpi.takeError(); - - if (!TypeCollection) { - // Initialize the type collection, even if we're not going to dump it. This - // way if some other part of the dumper decides it wants to use some or all - // of the records for whatever purposes, it can still access them lazily. - auto &Types = Tpi->typeArray(); - uint32_t Count = Tpi->getNumTypeRecords(); - auto Offsets = Tpi->getTypeIndexOffsets(); - TypeCollection = - llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets); - } - - return *TypeCollection; -} - -Error LLVMOutputStyle::dumpDbiStream() { - bool DumpModules = opts::shared::DumpModules || - opts::shared::DumpModuleSyms || - opts::shared::DumpModuleFiles || - !opts::shared::DumpModuleSubsections.empty(); - if (!opts::raw::DumpHeaders && !DumpModules) - return Error::success(); - if (!File.hasPDBDbiStream()) { - P.printString("DBI Stream not present"); - return Error::success(); - } - - ExitOnError Err("Error while processing DBI Stream"); - - auto DS = File.getPDBDbiStream(); - if (!DS) - return DS.takeError(); - - DictScope D(P, "DBI Stream"); - P.printNumber("Dbi Version", DS->getDbiVersion()); - P.printNumber("Age", DS->getAge()); - P.printBoolean("Incremental Linking", DS->isIncrementallyLinked()); - P.printBoolean("Has CTypes", DS->hasCTypes()); - P.printBoolean("Is Stripped", DS->isStripped()); - P.printObject("Machine Type", DS->getMachineType()); - P.printNumber("Symbol Record Stream Index", DS->getSymRecordStreamIndex()); - P.printNumber("Public Symbol Stream Index", DS->getPublicSymbolStreamIndex()); - P.printNumber("Global Symbol Stream Index", DS->getGlobalSymbolStreamIndex()); - - uint16_t Major = DS->getBuildMajorVersion(); - uint16_t Minor = DS->getBuildMinorVersion(); - P.printVersion("Toolchain Version", Major, Minor); - - std::string DllName; - raw_string_ostream DllStream(DllName); - DllStream << "mspdb" << Major << Minor << ".dll version"; - DllStream.flush(); - P.printVersion(DllName, Major, Minor, DS->getPdbDllVersion()); - - if (DumpModules) { - ListScope L(P, "Modules"); - const DbiModuleList &Modules = DS->modules(); - for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) { - const DbiModuleDescriptor &Modi = Modules.getModuleDescriptor(I); - DictScope DD(P); - P.printString("Name", Modi.getModuleName().str()); - P.printNumber("Debug Stream Index", Modi.getModuleStreamIndex()); - P.printString("Object File Name", Modi.getObjFileName().str()); - P.printNumber("Num Files", Modi.getNumberOfFiles()); - P.printNumber("Source File Name Idx", Modi.getSourceFileNameIndex()); - P.printNumber("Pdb File Name Idx", Modi.getPdbFilePathNameIndex()); - P.printNumber("Line Info Byte Size", Modi.getC11LineInfoByteSize()); - P.printNumber("C13 Line Info Byte Size", Modi.getC13LineInfoByteSize()); - P.printNumber("Symbol Byte Size", Modi.getSymbolDebugInfoByteSize()); - P.printNumber("Type Server Index", Modi.getTypeServerIndex()); - P.printBoolean("Has EC Info", Modi.hasECInfo()); - if (opts::shared::DumpModuleFiles) { - std::string FileListName = to_string(Modules.getSourceFileCount(I)) + - " Contributing Source Files"; - ListScope LL(P, FileListName); - for (auto File : Modules.source_files(I)) - P.printString(File); - } - bool HasModuleDI = (Modi.getModuleStreamIndex() < File.getNumStreams()); - bool ShouldDumpSymbols = - (opts::shared::DumpModuleSyms || opts::raw::DumpSymRecordBytes); - if (HasModuleDI && - (ShouldDumpSymbols || !opts::shared::DumpModuleSubsections.empty())) { - auto ModStreamData = MappedBlockStream::createIndexedStream( - File.getMsfLayout(), File.getMsfBuffer(), - Modi.getModuleStreamIndex(), File.getAllocator()); - - ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); - if (auto EC = ModS.reload()) - return EC; - - auto ExpectedTpi = initializeTypeDatabase(StreamTPI); - if (!ExpectedTpi) - return ExpectedTpi.takeError(); - auto &Tpi = *ExpectedTpi; - if (ShouldDumpSymbols) { - - ListScope SS(P, "Symbols"); - codeview::CVSymbolDumper SD(P, Tpi, CodeViewContainer::Pdb, nullptr, - false); - bool HadError = false; - for (auto S : ModS.symbols(&HadError)) { - DictScope LL(P, ""); - if (opts::shared::DumpModuleSyms) { - if (auto EC = SD.dump(S)) { - llvm::consumeError(std::move(EC)); - HadError = true; - break; - } - } - if (opts::raw::DumpSymRecordBytes) - P.printBinaryBlock("Bytes", S.content()); - } - if (HadError) - return make_error<RawError>( - raw_error_code::corrupt_file, - "DBI stream contained corrupt symbol record"); - } - if (!opts::shared::DumpModuleSubsections.empty()) { - ListScope SS(P, "Subsections"); - auto &InfoS = Err(File.getPDBInfoStream()); - LazyRandomTypeCollection *Ipi = nullptr; - if (InfoS.containsIdStream()) - Ipi = &Err(initializeTypeDatabase(StreamIPI)); - auto ExpectedStrings = File.getStringTable(); - if (!ExpectedStrings) - return joinErrors( - make_error<RawError>(raw_error_code::no_stream, - "Could not get string table!"), - ExpectedStrings.takeError()); - - C13RawVisitor V(P, Tpi, Ipi); - if (auto EC = codeview::visitDebugSubsections( - ModS.subsections(), V, ExpectedStrings->getStringTable())) - return EC; - } - } - } - } - return Error::success(); -} - -Error LLVMOutputStyle::dumpSectionContribs() { - if (!opts::raw::DumpSectionContribs) - return Error::success(); - if (!File.hasPDBDbiStream()) { - P.printString("DBI Stream not present"); - return Error::success(); - } - - auto Dbi = File.getPDBDbiStream(); - if (!Dbi) - return Dbi.takeError(); - - ListScope L(P, "Section Contributions"); - class Visitor : public ISectionContribVisitor { - public: - Visitor(ScopedPrinter &P, DbiStream &DS) : P(P), DS(DS) {} - void visit(const SectionContrib &SC) override { - DictScope D(P, "Contribution"); - P.printNumber("ISect", SC.ISect); - P.printNumber("Off", SC.Off); - P.printNumber("Size", SC.Size); - P.printFlags("Characteristics", SC.Characteristics, - codeview::getImageSectionCharacteristicNames(), - COFF::SectionCharacteristics(0x00F00000)); - { - DictScope DD(P, "Module"); - P.printNumber("Index", SC.Imod); - const DbiModuleList &Modules = DS.modules(); - if (Modules.getModuleCount() > SC.Imod) { - P.printString("Name", - Modules.getModuleDescriptor(SC.Imod).getModuleName()); - } - } - P.printNumber("Data CRC", SC.DataCrc); - P.printNumber("Reloc CRC", SC.RelocCrc); - P.flush(); - } - void visit(const SectionContrib2 &SC) override { - visit(SC.Base); - P.printNumber("ISect Coff", SC.ISectCoff); - P.flush(); - } - - private: - ScopedPrinter &P; - DbiStream &DS; - }; - Visitor V(P, *Dbi); - Dbi->visitSectionContributions(V); - return Error::success(); -} - -Error LLVMOutputStyle::dumpSectionMap() { - if (!opts::raw::DumpSectionMap) - return Error::success(); - if (!File.hasPDBDbiStream()) { - P.printString("DBI Stream not present"); - return Error::success(); - } - - auto Dbi = File.getPDBDbiStream(); - if (!Dbi) - return Dbi.takeError(); - - ListScope L(P, "Section Map"); - for (auto &M : Dbi->getSectionMap()) { - DictScope D(P, "Entry"); - P.printFlags("Flags", M.Flags, getOMFSegMapDescFlagNames()); - P.printNumber("Ovl", M.Ovl); - P.printNumber("Group", M.Group); - P.printNumber("Frame", M.Frame); - P.printNumber("SecName", M.SecName); - P.printNumber("ClassName", M.ClassName); - P.printNumber("Offset", M.Offset); - P.printNumber("SecByteLength", M.SecByteLength); - P.flush(); - } - return Error::success(); -} - -Error LLVMOutputStyle::dumpPublicsStream() { - if (!opts::raw::DumpPublics) - return Error::success(); - if (!File.hasPDBPublicsStream()) { - P.printString("Publics Stream not present"); - return Error::success(); - } - - auto Publics = File.getPDBPublicsStream(); - if (!Publics) - return Publics.takeError(); - DictScope D(P, "Publics Stream"); - - auto Dbi = File.getPDBDbiStream(); - if (!Dbi) - return Dbi.takeError(); - - P.printNumber("Stream number", Dbi->getPublicSymbolStreamIndex()); - P.printNumber("SymHash", Publics->getSymHash()); - P.printNumber("AddrMap", Publics->getAddrMap()); - P.printNumber("Number of buckets", Publics->getNumBuckets()); - P.printList("Hash Buckets", Publics->getHashBuckets()); - P.printList("Address Map", Publics->getAddressMap()); - P.printList("Thunk Map", Publics->getThunkMap()); - P.printList("Section Offsets", Publics->getSectionOffsets(), - printSectionOffset); - ListScope L(P, "Symbols"); - auto ExpectedTypes = initializeTypeDatabase(StreamTPI); - if (!ExpectedTypes) - return ExpectedTypes.takeError(); - auto &Tpi = *ExpectedTypes; - - codeview::CVSymbolDumper SD(P, Tpi, CodeViewContainer::Pdb, nullptr, false); - bool HadError = false; - for (auto S : Publics->getSymbols(&HadError)) { - DictScope DD(P, ""); - - if (auto EC = SD.dump(S)) { - HadError = true; - break; - } - if (opts::raw::DumpSymRecordBytes) - P.printBinaryBlock("Bytes", S.content()); - } - if (HadError) - return make_error<RawError>( - raw_error_code::corrupt_file, - "Public symbol stream contained corrupt record"); - - return Error::success(); -} - -Error LLVMOutputStyle::dumpSectionHeaders() { - if (!opts::raw::DumpSectionHeaders) - return Error::success(); - if (!File.hasPDBDbiStream()) { - P.printString("DBI Stream not present"); - return Error::success(); - } - - auto Dbi = File.getPDBDbiStream(); - if (!Dbi) - return Dbi.takeError(); - - ListScope D(P, "Section Headers"); - for (const object::coff_section &Section : Dbi->getSectionHeaders()) { - DictScope DD(P, ""); - - // If a name is 8 characters long, there is no NUL character at end. - StringRef Name(Section.Name, strnlen(Section.Name, sizeof(Section.Name))); - P.printString("Name", Name); - P.printNumber("Virtual Size", Section.VirtualSize); - P.printNumber("Virtual Address", Section.VirtualAddress); - P.printNumber("Size of Raw Data", Section.SizeOfRawData); - P.printNumber("File Pointer to Raw Data", Section.PointerToRawData); - P.printNumber("File Pointer to Relocations", Section.PointerToRelocations); - P.printNumber("File Pointer to Linenumbers", Section.PointerToLinenumbers); - P.printNumber("Number of Relocations", Section.NumberOfRelocations); - P.printNumber("Number of Linenumbers", Section.NumberOfLinenumbers); - P.printFlags("Characteristics", Section.Characteristics, - getImageSectionCharacteristicNames()); - } - return Error::success(); -} - -Error LLVMOutputStyle::dumpFpoStream() { - if (!opts::raw::DumpFpo) - return Error::success(); - if (!File.hasPDBDbiStream()) { - P.printString("DBI Stream not present"); - return Error::success(); - } - - auto Dbi = File.getPDBDbiStream(); - if (!Dbi) - return Dbi.takeError(); - - ListScope D(P, "New FPO"); - for (const object::FpoData &Fpo : Dbi->getFpoRecords()) { - DictScope DD(P, ""); - P.printNumber("Offset", Fpo.Offset); - P.printNumber("Size", Fpo.Size); - P.printNumber("Number of locals", Fpo.NumLocals); - P.printNumber("Number of params", Fpo.NumParams); - P.printNumber("Size of Prolog", Fpo.getPrologSize()); - P.printNumber("Number of Saved Registers", Fpo.getNumSavedRegs()); - P.printBoolean("Has SEH", Fpo.hasSEH()); - P.printBoolean("Use BP", Fpo.useBP()); - P.printNumber("Frame Pointer", Fpo.getFP()); - } - return Error::success(); -} - -void LLVMOutputStyle::flush() { P.flush(); } diff --git a/tools/llvm-pdbutil/LinePrinter.cpp b/tools/llvm-pdbutil/LinePrinter.cpp index ef56b5fe8e6..718d3394e21 100644 --- a/tools/llvm-pdbutil/LinePrinter.cpp +++ b/tools/llvm-pdbutil/LinePrinter.cpp @@ -13,6 +13,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/Support/Format.h" #include "llvm/Support/Regex.h" #include <algorithm> @@ -60,10 +61,16 @@ LinePrinter::LinePrinter(int Indent, bool UseColor, llvm::raw_ostream &Stream) opts::pretty::IncludeCompilands.end()); } -void LinePrinter::Indent() { CurrentIndent += IndentSpaces; } +void LinePrinter::Indent(uint32_t Amount) { + if (Amount == 0) + Amount = IndentSpaces; + CurrentIndent += Amount; +} -void LinePrinter::Unindent() { - CurrentIndent = std::max(0, CurrentIndent - IndentSpaces); +void LinePrinter::Unindent(uint32_t Amount) { + if (Amount == 0) + Amount = IndentSpaces; + CurrentIndent = std::max<int>(0, CurrentIndent - Amount); } void LinePrinter::NewLine() { @@ -71,6 +78,13 @@ void LinePrinter::NewLine() { OS.indent(CurrentIndent); } +void LinePrinter::print(const Twine &T) { OS << T; } + +void LinePrinter::printLine(const Twine &T) { + NewLine(); + OS << T; +} + bool LinePrinter::IsClassExcluded(const ClassLayout &Class) { if (IsTypeExcluded(Class.getName(), Class.getSize())) return true; @@ -79,6 +93,19 @@ bool LinePrinter::IsClassExcluded(const ClassLayout &Class) { return false; } +void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, + uint32_t StartOffset) { + NewLine(); + OS << Label << " ("; + if (!Data.empty()) { + OS << "\n"; + OS << format_bytes_with_ascii(Data, StartOffset, 32, 4, + CurrentIndent + IndentSpaces, true); + NewLine(); + } + OS << ")"; +} + bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size) { if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters)) return true; diff --git a/tools/llvm-pdbutil/LinePrinter.h b/tools/llvm-pdbutil/LinePrinter.h index 1a922feb1e6..f4fd22bcb6f 100644 --- a/tools/llvm-pdbutil/LinePrinter.h +++ b/tools/llvm-pdbutil/LinePrinter.h @@ -10,10 +10,12 @@ #ifndef LLVM_TOOLS_LLVMPDBDUMP_LINEPRINTER_H #define LLVM_TOOLS_LLVMPDBDUMP_LINEPRINTER_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" -#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.h" #include <list> @@ -28,10 +30,22 @@ class LinePrinter { public: LinePrinter(int Indent, bool UseColor, raw_ostream &Stream); - void Indent(); - void Unindent(); + void Indent(uint32_t Amount = 0); + void Unindent(uint32_t Amount = 0); void NewLine(); + void printLine(const Twine &T); + void print(const Twine &T); + template <typename... Ts> void formatLine(const char *Fmt, Ts &&... Items) { + printLine(formatv(Fmt, std::forward<Ts>(Items)...)); + } + template <typename... Ts> void format(const char *Fmt, Ts &&... Items) { + print(formatv(Fmt, std::forward<Ts>(Items)...)); + } + + void formatBinary(StringRef Label, ArrayRef<uint8_t> Data, + uint32_t StartOffset); + bool hasColor() const { return UseColor; } raw_ostream &getStream() { return OS; } int getIndentLevel() const { return CurrentIndent; } @@ -63,6 +77,17 @@ private: std::list<Regex> IncludeSymbolFilters; }; +struct AutoIndent { + explicit AutoIndent(LinePrinter &L, uint32_t Amount = 0) + : L(L), Amount(Amount) { + L.Indent(Amount); + } + ~AutoIndent() { L.Unindent(Amount); } + + LinePrinter &L; + uint32_t Amount = 0; +}; + template <class T> inline raw_ostream &operator<<(LinePrinter &Printer, const T &Item) { Printer.getStream() << Item; diff --git a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp new file mode 100644 index 00000000000..8b36de0b715 --- /dev/null +++ b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -0,0 +1,749 @@ +//===- MinimalSymbolDumper.cpp -------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MinimalSymbolDumper.h" + +#include "FormatUtil.h" +#include "LinePrinter.h" + +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static StringRef getSymbolKindName(SymbolKind K) { + switch (K) { +#define SYMBOL_RECORD(EnumName, value, name) \ + case EnumName: \ + return #EnumName; +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" + default: + llvm_unreachable("Unknown symbol kind!"); + } + return ""; +} + +static std::string formatLocalSymFlags(uint32_t IndentLevel, + LocalSymFlags Flags) { + std::vector<std::string> Opts; + if (Flags == LocalSymFlags::None) + return "none"; + + PUSH_FLAG(LocalSymFlags, IsParameter, Flags, "param"); + PUSH_FLAG(LocalSymFlags, IsAddressTaken, Flags, "address is taken"); + PUSH_FLAG(LocalSymFlags, IsCompilerGenerated, Flags, "compiler generated"); + PUSH_FLAG(LocalSymFlags, IsAggregate, Flags, "aggregate"); + PUSH_FLAG(LocalSymFlags, IsAggregated, Flags, "aggregated"); + PUSH_FLAG(LocalSymFlags, IsAliased, Flags, "aliased"); + PUSH_FLAG(LocalSymFlags, IsAlias, Flags, "alias"); + PUSH_FLAG(LocalSymFlags, IsReturnValue, Flags, "return val"); + PUSH_FLAG(LocalSymFlags, IsOptimizedOut, Flags, "optimized away"); + PUSH_FLAG(LocalSymFlags, IsEnregisteredGlobal, Flags, "enreg global"); + PUSH_FLAG(LocalSymFlags, IsEnregisteredStatic, Flags, "enreg static"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatExportFlags(uint32_t IndentLevel, ExportFlags Flags) { + std::vector<std::string> Opts; + if (Flags == ExportFlags::None) + return "none"; + + PUSH_FLAG(ExportFlags, IsConstant, Flags, "constant"); + PUSH_FLAG(ExportFlags, IsData, Flags, "data"); + PUSH_FLAG(ExportFlags, IsPrivate, Flags, "private"); + PUSH_FLAG(ExportFlags, HasNoName, Flags, "no name"); + PUSH_FLAG(ExportFlags, HasExplicitOrdinal, Flags, "explicit ord"); + PUSH_FLAG(ExportFlags, IsForwarder, Flags, "forwarder"); + + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatCompileSym2Flags(uint32_t IndentLevel, + CompileSym2Flags Flags) { + std::vector<std::string> Opts; + Flags &= ~CompileSym2Flags::SourceLanguageMask; + if (Flags == CompileSym2Flags::None) + return "none"; + + PUSH_FLAG(CompileSym2Flags, EC, Flags, "edit and continue"); + PUSH_FLAG(CompileSym2Flags, NoDbgInfo, Flags, "no dbg info"); + PUSH_FLAG(CompileSym2Flags, LTCG, Flags, "ltcg"); + PUSH_FLAG(CompileSym2Flags, NoDataAlign, Flags, "no data align"); + PUSH_FLAG(CompileSym2Flags, ManagedPresent, Flags, "has managed code"); + PUSH_FLAG(CompileSym2Flags, SecurityChecks, Flags, "security checks"); + PUSH_FLAG(CompileSym2Flags, HotPatch, Flags, "hot patchable"); + PUSH_FLAG(CompileSym2Flags, CVTCIL, Flags, "cvtcil"); + PUSH_FLAG(CompileSym2Flags, MSILModule, Flags, "msil module"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatCompileSym3Flags(uint32_t IndentLevel, + CompileSym3Flags Flags) { + std::vector<std::string> Opts; + Flags &= ~CompileSym3Flags::SourceLanguageMask; + + if (Flags == CompileSym3Flags::None) + return "none"; + + PUSH_FLAG(CompileSym3Flags, EC, Flags, "edit and continue"); + PUSH_FLAG(CompileSym3Flags, NoDbgInfo, Flags, "no dbg info"); + PUSH_FLAG(CompileSym3Flags, LTCG, Flags, "ltcg"); + PUSH_FLAG(CompileSym3Flags, NoDataAlign, Flags, "no data align"); + PUSH_FLAG(CompileSym3Flags, ManagedPresent, Flags, "has managed code"); + PUSH_FLAG(CompileSym3Flags, SecurityChecks, Flags, "security checks"); + PUSH_FLAG(CompileSym3Flags, HotPatch, Flags, "hot patchable"); + PUSH_FLAG(CompileSym3Flags, CVTCIL, Flags, "cvtcil"); + PUSH_FLAG(CompileSym3Flags, MSILModule, Flags, "msil module"); + PUSH_FLAG(CompileSym3Flags, Sdl, Flags, "sdl"); + PUSH_FLAG(CompileSym3Flags, PGO, Flags, "pgo"); + PUSH_FLAG(CompileSym3Flags, Exp, Flags, "exp"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatFrameProcedureOptions(uint32_t IndentLevel, + FrameProcedureOptions FPO) { + std::vector<std::string> Opts; + if (FPO == FrameProcedureOptions::None) + return "none"; + + PUSH_FLAG(FrameProcedureOptions, HasAlloca, FPO, "has alloca"); + PUSH_FLAG(FrameProcedureOptions, HasSetJmp, FPO, "has setjmp"); + PUSH_FLAG(FrameProcedureOptions, HasLongJmp, FPO, "has longjmp"); + PUSH_FLAG(FrameProcedureOptions, HasInlineAssembly, FPO, "has inline asm"); + PUSH_FLAG(FrameProcedureOptions, HasExceptionHandling, FPO, "has eh"); + PUSH_FLAG(FrameProcedureOptions, MarkedInline, FPO, "marked inline"); + PUSH_FLAG(FrameProcedureOptions, HasStructuredExceptionHandling, FPO, + "has seh"); + PUSH_FLAG(FrameProcedureOptions, Naked, FPO, "naked"); + PUSH_FLAG(FrameProcedureOptions, SecurityChecks, FPO, "secure checks"); + PUSH_FLAG(FrameProcedureOptions, AsynchronousExceptionHandling, FPO, + "has async eh"); + PUSH_FLAG(FrameProcedureOptions, NoStackOrderingForSecurityChecks, FPO, + "no stack order"); + PUSH_FLAG(FrameProcedureOptions, Inlined, FPO, "inlined"); + PUSH_FLAG(FrameProcedureOptions, StrictSecurityChecks, FPO, + "strict secure checks"); + PUSH_FLAG(FrameProcedureOptions, SafeBuffers, FPO, "safe buffers"); + PUSH_FLAG(FrameProcedureOptions, ProfileGuidedOptimization, FPO, "pgo"); + PUSH_FLAG(FrameProcedureOptions, ValidProfileCounts, FPO, + "has profile counts"); + PUSH_FLAG(FrameProcedureOptions, OptimizedForSpeed, FPO, "opt speed"); + PUSH_FLAG(FrameProcedureOptions, GuardCfg, FPO, "guard cfg"); + PUSH_FLAG(FrameProcedureOptions, GuardCfw, FPO, "guard cfw"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatProcSymFlags(uint32_t IndentLevel, + ProcSymFlags Flags) { + std::vector<std::string> Opts; + if (Flags == ProcSymFlags::None) + return "none"; + + PUSH_FLAG(ProcSymFlags, HasFP, Flags, "has fp"); + PUSH_FLAG(ProcSymFlags, HasIRET, Flags, "has iret"); + PUSH_FLAG(ProcSymFlags, HasFRET, Flags, "has fret"); + PUSH_FLAG(ProcSymFlags, IsNoReturn, Flags, "noreturn"); + PUSH_FLAG(ProcSymFlags, IsUnreachable, Flags, "unreachable"); + PUSH_FLAG(ProcSymFlags, HasCustomCallingConv, Flags, "custom calling conv"); + PUSH_FLAG(ProcSymFlags, IsNoInline, Flags, "noinline"); + PUSH_FLAG(ProcSymFlags, HasOptimizedDebugInfo, Flags, "opt debuginfo"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatThunkOrdinal(ThunkOrdinal Ordinal) { + switch (Ordinal) { + RETURN_CASE(ThunkOrdinal, Standard, "thunk"); + RETURN_CASE(ThunkOrdinal, ThisAdjustor, "this adjustor"); + RETURN_CASE(ThunkOrdinal, Vcall, "vcall"); + RETURN_CASE(ThunkOrdinal, Pcode, "pcode"); + RETURN_CASE(ThunkOrdinal, UnknownLoad, "unknown load"); + RETURN_CASE(ThunkOrdinal, TrampIncremental, "tramp incremental"); + RETURN_CASE(ThunkOrdinal, BranchIsland, "branch island"); + } + return formatUnknownEnum(Ordinal); +} + +static std::string formatTrampolineType(TrampolineType Tramp) { + switch (Tramp) { + RETURN_CASE(TrampolineType, TrampIncremental, "tramp incremental"); + RETURN_CASE(TrampolineType, BranchIsland, "branch island"); + } + return formatUnknownEnum(Tramp); +} + +static std::string formatSourceLanguage(SourceLanguage Lang) { + switch (Lang) { + RETURN_CASE(SourceLanguage, C, "c"); + RETURN_CASE(SourceLanguage, Cpp, "c++"); + RETURN_CASE(SourceLanguage, Fortran, "fortran"); + RETURN_CASE(SourceLanguage, Masm, "masm"); + RETURN_CASE(SourceLanguage, Pascal, "pascal"); + RETURN_CASE(SourceLanguage, Basic, "basic"); + RETURN_CASE(SourceLanguage, Cobol, "cobol"); + RETURN_CASE(SourceLanguage, Link, "link"); + RETURN_CASE(SourceLanguage, VB, "vb"); + RETURN_CASE(SourceLanguage, Cvtres, "cvtres"); + RETURN_CASE(SourceLanguage, Cvtpgd, "cvtpgd"); + RETURN_CASE(SourceLanguage, CSharp, "c#"); + RETURN_CASE(SourceLanguage, ILAsm, "il asm"); + RETURN_CASE(SourceLanguage, Java, "java"); + RETURN_CASE(SourceLanguage, JScript, "javascript"); + RETURN_CASE(SourceLanguage, MSIL, "msil"); + RETURN_CASE(SourceLanguage, HLSL, "hlsl"); + } + return formatUnknownEnum(Lang); +} + +static std::string formatMachineType(CPUType Cpu) { + switch (Cpu) { + RETURN_CASE(CPUType, Intel8080, "intel 8080"); + RETURN_CASE(CPUType, Intel8086, "intel 8086"); + RETURN_CASE(CPUType, Intel80286, "intel 80286"); + RETURN_CASE(CPUType, Intel80386, "intel 80386"); + RETURN_CASE(CPUType, Intel80486, "intel 80486"); + RETURN_CASE(CPUType, Pentium, "intel pentium"); + RETURN_CASE(CPUType, PentiumPro, "intel pentium pro"); + RETURN_CASE(CPUType, Pentium3, "intel pentium 3"); + RETURN_CASE(CPUType, MIPS, "mips"); + RETURN_CASE(CPUType, MIPS16, "mips-16"); + RETURN_CASE(CPUType, MIPS32, "mips-32"); + RETURN_CASE(CPUType, MIPS64, "mips-64"); + RETURN_CASE(CPUType, MIPSI, "mips i"); + RETURN_CASE(CPUType, MIPSII, "mips ii"); + RETURN_CASE(CPUType, MIPSIII, "mips iii"); + RETURN_CASE(CPUType, MIPSIV, "mips iv"); + RETURN_CASE(CPUType, MIPSV, "mips v"); + RETURN_CASE(CPUType, M68000, "motorola 68000"); + RETURN_CASE(CPUType, M68010, "motorola 68010"); + RETURN_CASE(CPUType, M68020, "motorola 68020"); + RETURN_CASE(CPUType, M68030, "motorola 68030"); + RETURN_CASE(CPUType, M68040, "motorola 68040"); + RETURN_CASE(CPUType, Alpha, "alpha"); + RETURN_CASE(CPUType, Alpha21164, "alpha 21164"); + RETURN_CASE(CPUType, Alpha21164A, "alpha 21164a"); + RETURN_CASE(CPUType, Alpha21264, "alpha 21264"); + RETURN_CASE(CPUType, Alpha21364, "alpha 21364"); + RETURN_CASE(CPUType, PPC601, "powerpc 601"); + RETURN_CASE(CPUType, PPC603, "powerpc 603"); + RETURN_CASE(CPUType, PPC604, "powerpc 604"); + RETURN_CASE(CPUType, PPC620, "powerpc 620"); + RETURN_CASE(CPUType, PPCFP, "powerpc fp"); + RETURN_CASE(CPUType, PPCBE, "powerpc be"); + RETURN_CASE(CPUType, SH3, "sh3"); + RETURN_CASE(CPUType, SH3E, "sh3e"); + RETURN_CASE(CPUType, SH3DSP, "sh3 dsp"); + RETURN_CASE(CPUType, SH4, "sh4"); + RETURN_CASE(CPUType, SHMedia, "shmedia"); + RETURN_CASE(CPUType, ARM3, "arm 3"); + RETURN_CASE(CPUType, ARM4, "arm 4"); + RETURN_CASE(CPUType, ARM4T, "arm 4t"); + RETURN_CASE(CPUType, ARM5, "arm 5"); + RETURN_CASE(CPUType, ARM5T, "arm 5t"); + RETURN_CASE(CPUType, ARM6, "arm 6"); + RETURN_CASE(CPUType, ARM_XMAC, "arm xmac"); + RETURN_CASE(CPUType, ARM_WMMX, "arm wmmx"); + RETURN_CASE(CPUType, ARM7, "arm 7"); + RETURN_CASE(CPUType, Omni, "omni"); + RETURN_CASE(CPUType, Ia64, "intel itanium ia64"); + RETURN_CASE(CPUType, Ia64_2, "intel itanium ia64 2"); + RETURN_CASE(CPUType, CEE, "cee"); + RETURN_CASE(CPUType, AM33, "am33"); + RETURN_CASE(CPUType, M32R, "m32r"); + RETURN_CASE(CPUType, TriCore, "tri-core"); + RETURN_CASE(CPUType, X64, "intel x86-x64"); + RETURN_CASE(CPUType, EBC, "ebc"); + RETURN_CASE(CPUType, Thumb, "thumb"); + RETURN_CASE(CPUType, ARMNT, "arm nt"); + RETURN_CASE(CPUType, D3D11_Shader, "d3d11 shader"); + } + return formatUnknownEnum(Cpu); +} + +static std::string formatCookieKind(FrameCookieKind Kind) { + switch (Kind) { + RETURN_CASE(FrameCookieKind, Copy, "copy"); + RETURN_CASE(FrameCookieKind, XorStackPointer, "xor stack ptr"); + RETURN_CASE(FrameCookieKind, XorFramePointer, "xor frame ptr"); + RETURN_CASE(FrameCookieKind, XorR13, "xor rot13"); + } + return formatUnknownEnum(Kind); +} + +static std::string formatRegisterId(RegisterId Id) { + switch (Id) { + RETURN_CASE(RegisterId, VFrame, "vframe"); + RETURN_CASE(RegisterId, AL, "al"); + RETURN_CASE(RegisterId, CL, "cl"); + RETURN_CASE(RegisterId, DL, "dl"); + RETURN_CASE(RegisterId, BL, "bl"); + RETURN_CASE(RegisterId, AH, "ah"); + RETURN_CASE(RegisterId, CH, "ch"); + RETURN_CASE(RegisterId, DH, "dh"); + RETURN_CASE(RegisterId, BH, "bh"); + RETURN_CASE(RegisterId, AX, "ax"); + RETURN_CASE(RegisterId, CX, "cx"); + RETURN_CASE(RegisterId, DX, "dx"); + RETURN_CASE(RegisterId, BX, "bx"); + RETURN_CASE(RegisterId, SP, "sp"); + RETURN_CASE(RegisterId, BP, "bp"); + RETURN_CASE(RegisterId, SI, "si"); + RETURN_CASE(RegisterId, DI, "di"); + RETURN_CASE(RegisterId, EAX, "eax"); + RETURN_CASE(RegisterId, ECX, "ecx"); + RETURN_CASE(RegisterId, EDX, "edx"); + RETURN_CASE(RegisterId, EBX, "ebx"); + RETURN_CASE(RegisterId, ESP, "esp"); + RETURN_CASE(RegisterId, EBP, "ebp"); + RETURN_CASE(RegisterId, ESI, "esi"); + RETURN_CASE(RegisterId, EDI, "edi"); + RETURN_CASE(RegisterId, ES, "es"); + RETURN_CASE(RegisterId, CS, "cs"); + RETURN_CASE(RegisterId, SS, "ss"); + RETURN_CASE(RegisterId, DS, "ds"); + RETURN_CASE(RegisterId, FS, "fs"); + RETURN_CASE(RegisterId, GS, "gs"); + RETURN_CASE(RegisterId, IP, "ip"); + RETURN_CASE(RegisterId, RAX, "rax"); + RETURN_CASE(RegisterId, RBX, "rbx"); + RETURN_CASE(RegisterId, RCX, "rcx"); + RETURN_CASE(RegisterId, RDX, "rdx"); + RETURN_CASE(RegisterId, RSI, "rsi"); + RETURN_CASE(RegisterId, RDI, "rdi"); + RETURN_CASE(RegisterId, RBP, "rbp"); + RETURN_CASE(RegisterId, RSP, "rsp"); + RETURN_CASE(RegisterId, R8, "r8"); + RETURN_CASE(RegisterId, R9, "r9"); + RETURN_CASE(RegisterId, R10, "r10"); + RETURN_CASE(RegisterId, R11, "r11"); + RETURN_CASE(RegisterId, R12, "r12"); + RETURN_CASE(RegisterId, R13, "r13"); + RETURN_CASE(RegisterId, R14, "r14"); + RETURN_CASE(RegisterId, R15, "r15"); + default: + return formatUnknownEnum(Id); + } +} + +static std::string formatRange(LocalVariableAddrRange Range) { + return formatv("[{0},+{1})", + formatSegmentOffset(Range.ISectStart, Range.OffsetStart), + Range.Range) + .str(); +} + +static std::string formatGaps(uint32_t IndentLevel, + ArrayRef<LocalVariableAddrGap> Gaps) { + std::vector<std::string> GapStrs; + for (const auto &G : Gaps) { + GapStrs.push_back(formatv("({0},{1})", G.GapStartOffset, G.Range).str()); + } + return typesetItemList(GapStrs, 7, IndentLevel, ", "); +} + +Error MinimalSymbolDumper::visitSymbolBegin(codeview::CVSymbol &Record) { + // formatLine puts the newline at the beginning, so we use formatLine here + // to start a new line, and then individual visit methods use format to + // append to the existing line. + P.formatLine("- {0} [size = {1}]", getSymbolKindName(Record.Type), + Record.length()); + P.Indent(); + return Error::success(); +} + +Error MinimalSymbolDumper::visitSymbolEnd(CVSymbol &Record) { + P.Unindent(); + return Error::success(); +} + +std::string MinimalSymbolDumper::typeIndex(TypeIndex TI) const { + if (TI.isSimple()) + return formatv("{0}", TI).str(); + StringRef Name = Types.getTypeName(TI); + if (Name.size() > 32) { + Name = Name.take_front(32); + return formatv("{0} ({1}...)", TI, Name); + } else + return formatv("{0} ({1})", TI, Name); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, BlockSym &Block) { + P.format(" `{0}`", Block.Name); + AutoIndent Indent(P); + P.formatLine("parent = {0}, addr = {1}", Block.Parent, + formatSegmentOffset(Block.Segment, Block.CodeOffset)); + P.formatLine("code size = {0}, end = {1}", Block.CodeSize, Block.End); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, Thunk32Sym &Thunk) { + P.format(" `{0}`", Thunk.Name); + AutoIndent Indent(P); + P.formatLine("parent = {0}, addr = {1}", Thunk.Parent, + formatSegmentOffset(Thunk.Segment, Thunk.Offset)); + P.formatLine("kind = {0}, size = {1}, end = {2}, next = {3}", + formatThunkOrdinal(Thunk.Thunk), Thunk.Length, Thunk.End, + Thunk.Next); + + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + TrampolineSym &Tramp) { + AutoIndent Indent(P); + P.formatLine("type = {0}, size = {1}, source = {2}, target = {3}", + formatTrampolineType(Tramp.Type), Tramp.Size, + formatSegmentOffset(Tramp.ThunkSection, Tramp.ThunkOffset), + formatSegmentOffset(Tramp.TargetSection, Tramp.ThunkOffset)); + + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + SectionSym &Section) { + P.format(" `{0}`", Section.Name); + AutoIndent Indent(P); + P.formatLine("length = {0}, alignment = {1}, rva = {2}, section # = {3}, " + "characteristics = {4}", + Section.Length, Section.Alignment, Section.Rva, + Section.SectionNumber, Section.Characteristics); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, CoffGroupSym &CG) { + P.format(" `{0}`", CG.Name); + AutoIndent Indent(P); + P.formatLine("length = {0}, addr = {1}, characteristics = {2}", CG.Size, + formatSegmentOffset(CG.Segment, CG.Offset), CG.Characteristics); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + BPRelativeSym &BPRel) { + P.format(" `{0}`", BPRel.Name); + AutoIndent Indent(P); + P.formatLine("type = {0}, offset = {1}", typeIndex(BPRel.Type), BPRel.Offset); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + BuildInfoSym &BuildInfo) { + P.format(" BuildId = `{0}`", BuildInfo.BuildId); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + CallSiteInfoSym &CSI) { + AutoIndent Indent(P); + P.formatLine("type = {0}, addr = {1}", typeIndex(CSI.Type), + formatSegmentOffset(CSI.Segment, CSI.CodeOffset)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + EnvBlockSym &EnvBlock) { + for (const auto &Entry : EnvBlock.Fields) { + P.formatLine("- {0}", Entry); + } + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FileStaticSym &FS) { + P.format(" `{0}`", FS.Name); + AutoIndent Indent(P); + P.formatLine("type = {0}, file name offset = {1}, flags = {2}", + typeIndex(FS.Index), FS.ModFilenameOffset, + formatLocalSymFlags(P.getIndentLevel() + 9, FS.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, ExportSym &Export) { + P.format(" `{0}`", Export.Name); + AutoIndent Indent(P); + P.formatLine("ordinal = {0}, flags = {1}", Export.Ordinal, + formatExportFlags(P.getIndentLevel() + 9, Export.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + Compile2Sym &Compile2) { + AutoIndent Indent(P); + SourceLanguage Lang = static_cast<SourceLanguage>( + Compile2.Flags & CompileSym2Flags::SourceLanguageMask); + P.formatLine("machine = {0}, ver = {1}, language = {2}", + formatMachineType(Compile2.Machine), Compile2.Version, + formatSourceLanguage(Lang)); + P.formatLine("frontend = {0}.{1}.{2}, backend = {3}.{4}.{5}", + Compile2.VersionFrontendMajor, Compile2.VersionFrontendMinor, + Compile2.VersionFrontendBuild, Compile2.VersionBackendMajor, + Compile2.VersionBackendMinor, Compile2.VersionBackendBuild); + P.formatLine("flags = {0}", + formatCompileSym2Flags(P.getIndentLevel() + 9, Compile2.Flags)); + P.formatLine( + "extra strings = {0}", + typesetStringList(P.getIndentLevel() + 9 + 2, Compile2.ExtraStrings)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + Compile3Sym &Compile3) { + AutoIndent Indent(P); + SourceLanguage Lang = static_cast<SourceLanguage>( + Compile3.Flags & CompileSym3Flags::SourceLanguageMask); + P.formatLine("machine = {0}, Ver = {1}, language = {2}", + formatMachineType(Compile3.Machine), Compile3.Version, + formatSourceLanguage(Lang)); + P.formatLine("frontend = {0}.{1}.{2}.{3}, backend = {4}.{5}.{6}.{7}", + Compile3.VersionFrontendMajor, Compile3.VersionFrontendMinor, + Compile3.VersionFrontendBuild, Compile3.VersionFrontendQFE, + Compile3.VersionBackendMajor, Compile3.VersionBackendMinor, + Compile3.VersionBackendBuild, Compile3.VersionBackendQFE); + P.formatLine("flags = {0}", + formatCompileSym3Flags(P.getIndentLevel() + 9, Compile3.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + ConstantSym &Constant) { + P.format(" `{0}`", Constant.Name); + AutoIndent Indent(P); + P.formatLine("type = {0}, value = {1}", typeIndex(Constant.Type), + Constant.Value.toString(10)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DataSym &Data) { + P.format(" `{0}`", Data.Name); + AutoIndent Indent(P); + P.formatLine("type = {0}, addr = {1}", typeIndex(Data.Type), + formatSegmentOffset(Data.Segment, Data.DataOffset)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord( + CVSymbol &CVR, DefRangeFramePointerRelFullScopeSym &Def) { + P.format(" offset = {0}", Def.Offset); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + DefRangeFramePointerRelSym &Def) { + AutoIndent Indent(P); + P.formatLine("offset = {0}, range = {1}", Def.Offset, formatRange(Def.Range)); + P.formatLine("gaps = {2}", Def.Offset, + formatGaps(P.getIndentLevel() + 9, Def.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + DefRangeRegisterRelSym &Def) { + AutoIndent Indent(P); + P.formatLine("register = {0}, base ptr = {1}, offset in parent = {2}, has " + "spilled udt = {3}", + uint16_t(Def.Hdr.Register), int32_t(Def.Hdr.BasePointerOffset), + Def.offsetInParent(), Def.hasSpilledUDTMember()); + P.formatLine("range = {0}, gaps = {1}", formatRange(Def.Range), + formatGaps(P.getIndentLevel() + 9, Def.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord( + CVSymbol &CVR, DefRangeRegisterSym &DefRangeRegister) { + AutoIndent Indent(P); + P.formatLine("register = {0}, may have no name = {1}, range start = " + "{2}, length = {3}", + uint16_t(DefRangeRegister.Hdr.Register), + uint16_t(DefRangeRegister.Hdr.MayHaveNoName), + formatSegmentOffset(DefRangeRegister.Range.ISectStart, + DefRangeRegister.Range.OffsetStart), + DefRangeRegister.Range.Range); + P.formatLine("gaps = [{0}]", + formatGaps(P.getIndentLevel() + 9, DefRangeRegister.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + DefRangeSubfieldRegisterSym &Def) { + AutoIndent Indent(P); + bool NoName = !!(Def.Hdr.MayHaveNoName == 0); + P.formatLine("register = {0}, may have no name = {1}, offset in parent = {2}", + uint16_t(Def.Hdr.Register), NoName, + uint32_t(Def.Hdr.OffsetInParent)); + P.formatLine("range = {0}, gaps = {1}", formatRange(Def.Range), + formatGaps(P.getIndentLevel() + 9, Def.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + DefRangeSubfieldSym &Def) { + AutoIndent Indent(P); + P.formatLine("program = {0}, offset in parent = {1}, range = {2}", + Def.Program, Def.OffsetInParent, formatRange(Def.Range)); + P.formatLine("gaps = {0}", formatGaps(P.getIndentLevel() + 9, Def.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DefRangeSym &Def) { + AutoIndent Indent(P); + P.formatLine("program = {0}, range = {1}", Def.Program, + formatRange(Def.Range)); + P.formatLine("gaps = {0}", formatGaps(P.getIndentLevel() + 9, Def.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameCookieSym &FC) { + AutoIndent Indent(P); + P.formatLine("code offset = {0}, Register = {1}, kind = {2}, flags = {3}", + FC.CodeOffset, FC.Register, formatCookieKind(FC.CookieKind), + FC.Flags); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameProcSym &FP) { + AutoIndent Indent(P); + P.formatLine("size = {0}, padding size = {1}, offset to padding = {2}", + FP.TotalFrameBytes, FP.PaddingFrameBytes, FP.OffsetToPadding); + P.formatLine("bytes of callee saved registers = {0}, exception handler addr " + "= {1}", + FP.BytesOfCalleeSavedRegisters, + formatSegmentOffset(FP.SectionIdOfExceptionHandler, + FP.OffsetOfExceptionHandler)); + P.formatLine("flags = {0}", + formatFrameProcedureOptions(P.getIndentLevel() + 9, FP.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + HeapAllocationSiteSym &HAS) { + AutoIndent Indent(P); + P.formatLine("type = {0}, addr = {1} call size = {2}", typeIndex(HAS.Type), + formatSegmentOffset(HAS.Segment, HAS.CodeOffset), + HAS.CallInstructionSize); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, InlineSiteSym &IS) { + AutoIndent Indent(P); + auto Bytes = makeArrayRef(IS.AnnotationData); + StringRef Annotations(reinterpret_cast<const char *>(Bytes.begin()), + Bytes.size()); + + P.formatLine("inlinee = {0}, parent = {1}, end = {2}", typeIndex(IS.Inlinee), + IS.Parent, IS.End); + P.formatLine("annotations = {0}", toHex(Annotations)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + RegisterSym &Register) { + P.format(" `{0}`", Register.Name); + AutoIndent Indent(P); + P.formatLine("register = {0}, type = {1}", + formatRegisterId(Register.Register), typeIndex(Register.Index)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + PublicSym32 &Public) { + P.format(" `{0}`", Public.Name); + AutoIndent Indent(P); + P.formatLine("type = {0}, addr = {1}", typeIndex(Public.Index), + formatSegmentOffset(Public.Segment, Public.Offset)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, ProcRefSym &PR) { + P.format(" `{0}`", PR.Name); + AutoIndent Indent(P); + P.formatLine("module = {0}, sum name = {1}, offset = {2}", PR.Module, + PR.SumName, PR.SymOffset); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, LabelSym &Label) { + P.format(" `{0}` (addr = {1})", Label.Name, + formatSegmentOffset(Label.Segment, Label.CodeOffset)); + AutoIndent Indent(P); + P.formatLine("flags = {0}", + formatProcSymFlags(P.getIndentLevel() + 9, Label.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, LocalSym &Local) { + P.format(" `{0}`", Local.Name); + AutoIndent Indent(P); + + std::string FlagStr = + formatLocalSymFlags(P.getIndentLevel() + 9, Local.Flags); + P.formatLine("type={0}, flags = {1}", typeIndex(Local.Type), FlagStr); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + ObjNameSym &ObjName) { + P.format(" sig={0}, `{1}`", ObjName.Signature, ObjName.Name); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, ProcSym &Proc) { + P.format(" `{0}`", Proc.Name); + AutoIndent Indent(P); + P.formatLine("parent = {0}, addr = {1}, code size = {2}, end = {3}", + Proc.Parent, formatSegmentOffset(Proc.Segment, Proc.CodeOffset), + Proc.CodeSize, Proc.End); + P.formatLine("debug start = {0}, debug end = {1}, flags = {2}", Proc.DbgStart, + Proc.DbgEnd, + formatProcSymFlags(P.getIndentLevel() + 9, Proc.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + ScopeEndSym &ScopeEnd) { + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, CallerSym &Caller) { + AutoIndent Indent(P); + for (const auto &I : Caller.Indices) { + P.formatLine("callee: {0}", typeIndex(I)); + } + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + RegRelativeSym &RegRel) { + P.format(" `{0}`", RegRel.Name); + AutoIndent Indent(P); + P.formatLine("type = {0}, register = {1}, offset = {2}", + typeIndex(RegRel.Type), formatRegisterId(RegRel.Register), + RegRel.Offset); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + ThreadLocalDataSym &Data) { + P.format(" `{0}`", Data.Name); + AutoIndent Indent(P); + P.formatLine("type = {0}, addr = {1}", typeIndex(Data.Type), + formatSegmentOffset(Data.Segment, Data.DataOffset)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, UDTSym &UDT) { + P.format(" `{0}`", UDT.Name); + AutoIndent Indent(P); + P.formatLine("original type = {0}", UDT.Type); + return Error::success(); +} diff --git a/tools/llvm-pdbutil/MinimalSymbolDumper.h b/tools/llvm-pdbutil/MinimalSymbolDumper.h new file mode 100644 index 00000000000..451f2da6fd1 --- /dev/null +++ b/tools/llvm-pdbutil/MinimalSymbolDumper.h @@ -0,0 +1,47 @@ +//===- MinimalSymbolDumper.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_LLVMPDBUTIL_MINIMAL_SYMBOL_DUMPER_H +#define LLVM_TOOLS_LLVMPDBUTIL_MINIMAL_SYMBOL_DUMPER_H + +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" + +namespace llvm { +namespace codeview { +class LazyRandomTypeCollection; +} + +namespace pdb { +class LinePrinter; + +class MinimalSymbolDumper : public codeview::SymbolVisitorCallbacks { +public: + MinimalSymbolDumper(LinePrinter &P, bool RecordBytes, + codeview::LazyRandomTypeCollection &Types) + : P(P), Types(Types) {} + + Error visitSymbolBegin(codeview::CVSymbol &Record) override; + Error visitSymbolEnd(codeview::CVSymbol &Record) override; + +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + virtual Error visitKnownRecord(codeview::CVSymbol &CVR, \ + codeview::Name &Record) override; +#define SYMBOL_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" + +private: + std::string typeIndex(codeview::TypeIndex TI) const; + + LinePrinter &P; + codeview::LazyRandomTypeCollection &Types; +}; +} // namespace pdb +} // namespace llvm + +#endif
\ No newline at end of file diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/tools/llvm-pdbutil/MinimalTypeDumper.cpp new file mode 100644 index 00000000000..98813e03c38 --- /dev/null +++ b/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -0,0 +1,532 @@ +//===- MinimalTypeDumper.cpp ---------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MinimalTypeDumper.h" + +#include "FormatUtil.h" +#include "LinePrinter.h" + +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MathExtras.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static StringRef getLeafTypeName(TypeLeafKind K) { + switch (K) { +#define TYPE_RECORD(EnumName, value, name) \ + case EnumName: \ + return #EnumName; +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + default: + llvm_unreachable("Unknown type leaf kind!"); + } + return ""; +} + +static std::string formatClassOptions(uint32_t IndentLevel, + ClassOptions Options) { + std::vector<std::string> Opts; + PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options, + "has ctor / dtor"); + PUSH_FLAG(ClassOptions, ContainsNestedClass, Options, + "contains nested class"); + PUSH_FLAG(ClassOptions, HasConversionOperator, Options, + "conversion operator"); + PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref"); + PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name"); + PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin"); + PUSH_FLAG(ClassOptions, Nested, Options, "is nested"); + PUSH_FLAG(ClassOptions, HasOverloadedOperator, Options, + "overloaded operator"); + PUSH_FLAG(ClassOptions, HasOverloadedAssignmentOperator, Options, + "overloaded operator="); + PUSH_FLAG(ClassOptions, Packed, Options, "packed"); + PUSH_FLAG(ClassOptions, Scoped, Options, "scoped"); + PUSH_FLAG(ClassOptions, Sealed, Options, "sealed"); + + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string pointerOptions(PointerOptions Options) { + std::vector<std::string> Opts; + PUSH_FLAG(PointerOptions, Flat32, Options, "flat32"); + PUSH_FLAG(PointerOptions, Volatile, Options, "volatile"); + PUSH_FLAG(PointerOptions, Const, Options, "const"); + PUSH_FLAG(PointerOptions, Unaligned, Options, "unaligned"); + PUSH_FLAG(PointerOptions, Restrict, Options, "restrict"); + PUSH_FLAG(PointerOptions, WinRTSmartPointer, Options, "winrt"); + if (Opts.empty()) + return "None"; + return join(Opts, " | "); +} + +static std::string modifierOptions(ModifierOptions Options) { + std::vector<std::string> Opts; + PUSH_FLAG(ModifierOptions, Const, Options, "const"); + PUSH_FLAG(ModifierOptions, Volatile, Options, "volatile"); + PUSH_FLAG(ModifierOptions, Unaligned, Options, "unaligned"); + if (Opts.empty()) + return "None"; + return join(Opts, " | "); +} + +static std::string formatCallingConvention(CallingConvention Convention) { + switch (Convention) { + RETURN_CASE(CallingConvention, AlphaCall, "alphacall"); + RETURN_CASE(CallingConvention, AM33Call, "am33call"); + RETURN_CASE(CallingConvention, ArmCall, "armcall"); + RETURN_CASE(CallingConvention, ClrCall, "clrcall"); + RETURN_CASE(CallingConvention, FarC, "far cdecl"); + RETURN_CASE(CallingConvention, FarFast, "far fastcall"); + RETURN_CASE(CallingConvention, FarPascal, "far pascal"); + RETURN_CASE(CallingConvention, FarStdCall, "far stdcall"); + RETURN_CASE(CallingConvention, FarSysCall, "far syscall"); + RETURN_CASE(CallingConvention, Generic, "generic"); + RETURN_CASE(CallingConvention, Inline, "inline"); + RETURN_CASE(CallingConvention, M32RCall, "m32rcall"); + RETURN_CASE(CallingConvention, MipsCall, "mipscall"); + RETURN_CASE(CallingConvention, NearC, "cdecl"); + RETURN_CASE(CallingConvention, NearFast, "fastcall"); + RETURN_CASE(CallingConvention, NearPascal, "pascal"); + RETURN_CASE(CallingConvention, NearStdCall, "stdcall"); + RETURN_CASE(CallingConvention, NearSysCall, "near syscall"); + RETURN_CASE(CallingConvention, NearVector, "vectorcall"); + RETURN_CASE(CallingConvention, PpcCall, "ppccall"); + RETURN_CASE(CallingConvention, SHCall, "shcall"); + RETURN_CASE(CallingConvention, SH5Call, "sh5call"); + RETURN_CASE(CallingConvention, ThisCall, "thiscall"); + RETURN_CASE(CallingConvention, TriCall, "tricall"); + } + return formatUnknownEnum(Convention); +} + +static std::string formatPointerMode(PointerMode Mode) { + switch (Mode) { + RETURN_CASE(PointerMode, LValueReference, "ref"); + RETURN_CASE(PointerMode, Pointer, "pointer"); + RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer"); + RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer"); + RETURN_CASE(PointerMode, RValueReference, "rvalue ref"); + } + return formatUnknownEnum(Mode); +} + +static std::string memberAccess(MemberAccess Access) { + switch (Access) { + RETURN_CASE(MemberAccess, None, ""); + RETURN_CASE(MemberAccess, Private, "private"); + RETURN_CASE(MemberAccess, Protected, "protected"); + RETURN_CASE(MemberAccess, Public, "public"); + } + return formatUnknownEnum(Access); +} + +static std::string methodKind(MethodKind Kind) { + switch (Kind) { + RETURN_CASE(MethodKind, Vanilla, ""); + RETURN_CASE(MethodKind, Virtual, "virtual"); + RETURN_CASE(MethodKind, Static, "static"); + RETURN_CASE(MethodKind, Friend, "friend"); + RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual"); + RETURN_CASE(MethodKind, PureVirtual, "pure virtual"); + RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual"); + } + return formatUnknownEnum(Kind); +} + +static std::string pointerKind(PointerKind Kind) { + switch (Kind) { + RETURN_CASE(PointerKind, Near16, "ptr16"); + RETURN_CASE(PointerKind, Far16, "far ptr16"); + RETURN_CASE(PointerKind, Huge16, "huge ptr16"); + RETURN_CASE(PointerKind, BasedOnSegment, "segment based"); + RETURN_CASE(PointerKind, BasedOnValue, "value based"); + RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based"); + RETURN_CASE(PointerKind, BasedOnAddress, "address based"); + RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based"); + RETURN_CASE(PointerKind, BasedOnType, "type based"); + RETURN_CASE(PointerKind, BasedOnSelf, "self based"); + RETURN_CASE(PointerKind, Near32, "ptr32"); + RETURN_CASE(PointerKind, Far32, "far ptr32"); + RETURN_CASE(PointerKind, Near64, "ptr64"); + } + return formatUnknownEnum(Kind); +} + +static std::string memberAttributes(const MemberAttributes &Attrs) { + std::vector<std::string> Opts; + std::string Access = memberAccess(Attrs.getAccess()); + std::string Kind = methodKind(Attrs.getMethodKind()); + if (!Access.empty()) + Opts.push_back(Access); + if (!Kind.empty()) + Opts.push_back(Kind); + MethodOptions Flags = Attrs.getFlags(); + PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo"); + PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit"); + PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct"); + PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated"); + PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed"); + return join(Opts, " "); +} + +static std::string formatPointerAttrs(const PointerRecord &Record) { + PointerMode Mode = Record.getMode(); + PointerOptions Opts = Record.getOptions(); + PointerKind Kind = Record.getPointerKind(); + return formatv("mode = {0}, opts = {1}, kind = {2}", formatPointerMode(Mode), + pointerOptions(Opts), pointerKind(Kind)); +} + +static std::string formatFunctionOptions(FunctionOptions Options) { + std::vector<std::string> Opts; + + PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt"); + PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options, + "constructor with virtual bases"); + PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor"); + if (Opts.empty()) + return "None"; + return join(Opts, " | "); +} + +Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { + // formatLine puts the newline at the beginning, so we use formatLine here + // to start a new line, and then individual visit methods use format to + // append to the existing line. + P.formatLine("{0} | {1} [size = {2}]", + fmt_align(Index, AlignStyle::Right, Width), + getLeafTypeName(Record.Type), Record.length()); + P.Indent(Width + 3); + return Error::success(); +} +Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) { + P.Unindent(Width + 3); + if (RecordBytes) { + AutoIndent Indent(P, 9); + P.formatBinary("Bytes", Record.RecordData, 0); + } + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) { + P.formatLine("- {0}", getLeafTypeName(Record.Kind)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) { + if (RecordBytes) { + AutoIndent Indent(P, 2); + P.formatBinary("Bytes", Record.Data, 0); + } + return Error::success(); +} + +StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const { + if (TI.isNoneType()) + return ""; + return Types.getTypeName(TI); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + FieldListRecord &FieldList) { + if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this)) + return EC; + + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + StringIdRecord &String) { + P.format(" ID: {0}, String: {1}", String.getId(), String.getString()); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + ArgListRecord &Args) { + auto Indices = Args.getIndices(); + if (Indices.empty()) + return Error::success(); + + auto Max = std::max_element(Indices.begin(), Indices.end()); + uint32_t W = NumDigits(Max->getIndex()) + 2; + + for (auto I : Indices) + P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), + getTypeName(I)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + StringListRecord &Strings) { + auto Indices = Strings.getIndices(); + if (Indices.empty()) + return Error::success(); + + auto Max = std::max_element(Indices.begin(), Indices.end()); + uint32_t W = NumDigits(Max->getIndex()) + 2; + + for (auto I : Indices) + P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), + getTypeName(I)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + ClassRecord &Class) { + P.formatLine("class name: `{0}`", Class.Name); + if (Class.hasUniqueName()) + P.formatLine("unique name: `{0}`", Class.UniqueName); + P.formatLine("vtable: {0}, base list: {1}, field list: {2}", + Class.VTableShape, Class.DerivationList, Class.FieldList); + P.formatLine("options: {0}", + formatClassOptions(P.getIndentLevel(), Class.Options)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + UnionRecord &Union) { + P.formatLine("class name: `{0}`", Union.Name); + if (Union.hasUniqueName()) + P.formatLine("unique name: `{0}`", Union.UniqueName); + P.formatLine("field list: {0}", Union.FieldList); + P.formatLine("options: {0}", + formatClassOptions(P.getIndentLevel(), Union.Options)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { + P.formatLine("name: `{0}`", Enum.Name); + if (Enum.hasUniqueName()) + P.formatLine("unique name: `{0}`", Enum.UniqueName); + P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList, + Enum.UnderlyingType); + P.formatLine("options: {0}", + formatClassOptions(P.getIndentLevel(), Enum.Options)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) { + if (AT.Name.empty()) { + P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size, + AT.IndexType, AT.ElementType); + } else { + P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}", + AT.Name, AT.Size, AT.IndexType, AT.ElementType); + } + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + VFTableRecord &VFT) { + P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}", + VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable); + P.formatLine("method names: "); + if (!VFT.MethodNames.empty()) { + std::string Sep = + formatv("\n{0}", + fmt_repeat(' ', P.getIndentLevel() + strlen("method names: "))) + .str(); + P.print(join(VFT.MethodNames, Sep)); + } + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + MemberFuncIdRecord &Id) { + P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name, + Id.FunctionType, Id.ClassType); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + ProcedureRecord &Proc) { + P.formatLine("return type = {0}, # args = {1}, param list = {2}", + Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList); + P.formatLine("calling conv = {0}, options = {1}", + formatCallingConvention(Proc.CallConv), + formatFunctionOptions(Proc.Options)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + MemberFunctionRecord &MF) { + P.formatLine("return type = {0}, # args = {1}, param list = {2}", + MF.ParameterCount, MF.ArgumentList, MF.ReturnType); + P.formatLine("class type = {0}, this type = {1}, this adjust = {2}", + MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment); + P.formatLine("calling conv = {0}, options = {1}", + formatCallingConvention(MF.CallConv), + formatFunctionOptions(MF.Options)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + FuncIdRecord &Func) { + P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name, + Func.FunctionType, Func.ParentScope); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + TypeServer2Record &TS) { + P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, + fmt_guid(TS.Guid)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + PointerRecord &Ptr) { + P.formatLine("referent = {0}, {1}", Ptr.ReferentType, + formatPointerAttrs(Ptr)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + ModifierRecord &Mod) { + P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType, + modifierOptions(Mod.Modifiers)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + VFTableShapeRecord &Shape) { + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + UdtModSourceLineRecord &U) { + P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module, + U.SourceFile.getIndex(), U.LineNumber); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + UdtSourceLineRecord &U) { + P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT, + U.SourceFile.getIndex(), U.LineNumber); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + BitFieldRecord &BF) { + P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type, + BF.BitOffset, BF.BitSize); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord( + CVType &CVR, MethodOverloadListRecord &Overloads) { + for (auto &M : Overloads.Methods) + P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]", + M.Type, M.VFTableOffset, memberAttributes(M.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + BuildInfoRecord &BI) { + auto Indices = BI.ArgIndices; + if (Indices.empty()) + return Error::success(); + + auto Max = std::max_element(Indices.begin(), Indices.end()); + uint32_t W = NumDigits(Max->getIndex()) + 2; + + for (auto I : Indices) + P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), + getTypeName(I)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) { + std::string Type = (R.Mode == LabelType::Far) ? "far" : "near"; + P.format(" type = {0}", Type); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + NestedTypeRecord &Nested) { + P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + OneMethodRecord &Method) { + P.format(" [name = `{0}`]", Method.Name); + AutoIndent Indent(P); + P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type, + Method.VFTableOffset, memberAttributes(Method.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + OverloadedMethodRecord &Method) { + P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]", + Method.Name, Method.NumOverloads, Method.MethodList); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + DataMemberRecord &Field) { + P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name, + Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + StaticDataMemberRecord &Field) { + P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type, + memberAttributes(Field.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + EnumeratorRecord &Enum) { + P.format(" [{0} = {1}]", Enum.Name, + Enum.Value.toString(10, Enum.Value.isSigned())); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + BaseClassRecord &Base) { + AutoIndent Indent(P); + P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset, + memberAttributes(Base.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + VirtualBaseClassRecord &Base) { + AutoIndent Indent(P); + P.formatLine( + "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}", + Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex); + P.formatLine("attrs = {0}", memberAttributes(Base.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + ListContinuationRecord &Cont) { + P.format(" continuation = {0}", Cont.ContinuationIndex); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + VFPtrRecord &VFP) { + P.format(" type = {0}", VFP.Type); + return Error::success(); +} diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.h b/tools/llvm-pdbutil/MinimalTypeDumper.h new file mode 100644 index 00000000000..47f6781e3ca --- /dev/null +++ b/tools/llvm-pdbutil/MinimalTypeDumper.h @@ -0,0 +1,56 @@ +//===- MinimalTypeDumper.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_LLVMPDBUTIL_MINIMAL_TYPE_DUMPER_H +#define LLVM_TOOLS_LLVMPDBUTIL_MINIMAL_TYPE_DUMPER_H + +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" + +namespace llvm { +namespace codeview { +class LazyRandomTypeCollection; +} + +namespace pdb { +class LinePrinter; + +class MinimalTypeDumpVisitor : public codeview::TypeVisitorCallbacks { +public: + MinimalTypeDumpVisitor(LinePrinter &P, uint32_t Width, bool RecordBytes, + codeview::LazyRandomTypeCollection &Types) + : P(P), Width(Width), RecordBytes(RecordBytes), Types(Types) {} + + Error visitTypeBegin(codeview::CVType &Record, + codeview::TypeIndex Index) override; + Error visitTypeEnd(codeview::CVType &Record) override; + Error visitMemberBegin(codeview::CVMemberRecord &Record) override; + Error visitMemberEnd(codeview::CVMemberRecord &Record) override; + +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + Error visitKnownRecord(codeview::CVType &CVR, \ + codeview::Name##Record &Record) override; +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + Error visitKnownMember(codeview::CVMemberRecord &CVR, \ + codeview::Name##Record &Record) override; +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + +private: + StringRef getTypeName(codeview::TypeIndex TI) const; + + LinePrinter &P; + uint32_t Width; + bool RecordBytes = false; + codeview::LazyRandomTypeCollection &Types; +}; +} // namespace pdb +} // namespace llvm + +#endif diff --git a/tools/llvm-pdbutil/RawOutputStyle.cpp b/tools/llvm-pdbutil/RawOutputStyle.cpp new file mode 100644 index 00000000000..e37b0b6763a --- /dev/null +++ b/tools/llvm-pdbutil/RawOutputStyle.cpp @@ -0,0 +1,669 @@ +//===- RawOutputStyle.cpp ------------------------------------ *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RawOutputStyle.h" + +#include "CompactTypeDumpVisitor.h" +#include "FormatUtil.h" +#include "MinimalSymbolDumper.h" +#include "MinimalTypeDumper.h" +#include "StreamUtil.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolDumper.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" +#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/EnumTables.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +#include <unordered_map> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +RawOutputStyle::RawOutputStyle(PDBFile &File) + : File(File), P(2, false, outs()) {} + +Error RawOutputStyle::dump() { + if (opts::raw::DumpSummary) { + if (auto EC = dumpFileSummary()) + return EC; + P.NewLine(); + } + + if (opts::raw::DumpStreams) { + if (auto EC = dumpStreamSummary()) + return EC; + P.NewLine(); + } + + if (opts::raw::DumpBlockRange.hasValue()) { + if (auto EC = dumpBlockRanges()) + return EC; + P.NewLine(); + } + + if (!opts::raw::DumpStreamData.empty()) { + if (auto EC = dumpStreamBytes()) + return EC; + P.NewLine(); + } + + if (opts::raw::DumpStringTable) { + if (auto EC = dumpStringTable()) + return EC; + P.NewLine(); + } + + if (opts::raw::DumpModules) { + if (auto EC = dumpModules()) + return EC; + } + + if (opts::raw::DumpTypes) { + if (auto EC = dumpTpiStream(StreamTPI)) + return EC; + } + + if (opts::raw::DumpIds) { + if (auto EC = dumpTpiStream(StreamIPI)) + return EC; + } + + if (opts::raw::DumpPublics) { + if (auto EC = dumpPublics()) + return EC; + } + + if (opts::raw::DumpSymbols) { + if (auto EC = dumpModuleSyms()) + return EC; + } + + if (opts::raw::DumpSectionContribs) { + if (auto EC = dumpSectionContribs()) + return EC; + } + + if (opts::raw::DumpSectionMap) { + if (auto EC = dumpSectionMap()) + return EC; + } + + return Error::success(); +} + +static void printHeader(LinePrinter &P, const Twine &S) { + P.NewLine(); + P.formatLine("{0,=60}", S); + P.formatLine("{0}", fmt_repeat('=', 60)); +} + +Error RawOutputStyle::dumpFileSummary() { + printHeader(P, "Summary"); + + ExitOnError Err("Invalid PDB Format"); + + AutoIndent Indent(P); + P.formatLine("Block Size: {0}", File.getBlockSize()); + P.formatLine("Number of blocks: {0}", File.getBlockCount()); + P.formatLine("Number of streams: {0}", File.getNumStreams()); + + auto &PS = Err(File.getPDBInfoStream()); + P.formatLine("Signature: {0}", PS.getSignature()); + P.formatLine("Age: {0}", PS.getAge()); + P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid)); + P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures())); + P.formatLine("Has Debug Info: {0}", File.hasPDBDbiStream()); + P.formatLine("Has Types: {0}", File.hasPDBTpiStream()); + P.formatLine("Has IDs: {0}", File.hasPDBIpiStream()); + P.formatLine("Has Globals: {0}", File.hasPDBGlobalsStream()); + P.formatLine("Has Publics: {0}", File.hasPDBPublicsStream()); + if (File.hasPDBDbiStream()) { + auto &DBI = Err(File.getPDBDbiStream()); + P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked()); + P.formatLine("Has conflicting types: {0}", DBI.hasCTypes()); + P.formatLine("Is stripped: {0}", DBI.isStripped()); + } + + return Error::success(); +} + +Error RawOutputStyle::dumpStreamSummary() { + printHeader(P, "Streams"); + + if (StreamPurposes.empty()) + discoverStreamPurposes(File, StreamPurposes); + + AutoIndent Indent(P); + uint32_t StreamCount = File.getNumStreams(); + + for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { + P.formatLine( + "Stream {0}: [{1}] ({2} bytes)", + fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)), + StreamPurposes[StreamIdx], File.getStreamByteSize(StreamIdx)); + } + + return Error::success(); +} + +Error RawOutputStyle::dumpBlockRanges() { + printHeader(P, "MSF Blocks"); + + auto &R = *opts::raw::DumpBlockRange; + uint32_t Max = R.Max.getValueOr(R.Min); + + AutoIndent Indent(P); + if (Max < R.Min) + return make_error<StringError>( + "Invalid block range specified. Max < Min", + std::make_error_code(std::errc::bad_address)); + if (Max >= File.getBlockCount()) + return make_error<StringError>( + "Invalid block range specified. Requested block out of bounds", + std::make_error_code(std::errc::bad_address)); + + for (uint32_t I = R.Min; I <= Max; ++I) { + auto ExpectedData = File.getBlockData(I, File.getBlockSize()); + if (!ExpectedData) + return ExpectedData.takeError(); + std::string Label = formatv("Block {0}", I).str(); + P.formatBinary(Label, *ExpectedData, 0); + } + + return Error::success(); +} + +static Error parseStreamSpec(StringRef Str, uint32_t &SI, uint32_t &Offset, + uint32_t &Size) { + if (Str.consumeInteger(0, SI)) + return make_error<RawError>(raw_error_code::invalid_format, + "Invalid Stream Specification"); + if (Str.consume_front(":")) { + if (Str.consumeInteger(0, Offset)) + return make_error<RawError>(raw_error_code::invalid_format, + "Invalid Stream Specification"); + } + if (Str.consume_front("@")) { + if (Str.consumeInteger(0, Size)) + return make_error<RawError>(raw_error_code::invalid_format, + "Invalid Stream Specification"); + } + if (!Str.empty()) + return make_error<RawError>(raw_error_code::invalid_format, + "Invalid Stream Specification"); + return Error::success(); +} + +Error RawOutputStyle::dumpStreamBytes() { + if (StreamPurposes.empty()) + discoverStreamPurposes(File, StreamPurposes); + + printHeader(P, "Stream Data"); + ExitOnError Err("Unexpected error reading stream data"); + + for (auto &Str : opts::raw::DumpStreamData) { + uint32_t SI = 0; + uint32_t Begin = 0; + uint32_t Size = 0; + uint32_t End = 0; + + if (auto EC = parseStreamSpec(Str, SI, Begin, Size)) + return EC; + + AutoIndent Indent(P); + if (SI >= File.getNumStreams()) { + P.formatLine("Stream {0}: Not present", SI); + continue; + } + + auto S = MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), SI, File.getAllocator()); + if (!S) { + P.NewLine(); + P.formatLine("Stream {0}: Not present", SI); + continue; + } + + if (Size == 0) + End = S->getLength(); + else + End = std::min(Begin + Size, S->getLength()); + + P.formatLine("Stream {0} ({1:N} bytes): {2}", SI, S->getLength(), + StreamPurposes[SI]); + AutoIndent Indent2(P); + + BinaryStreamReader R(*S); + ArrayRef<uint8_t> StreamData; + Err(R.readBytes(StreamData, S->getLength())); + Size = End - Begin; + StreamData = StreamData.slice(Begin, Size); + P.formatBinary("Data", StreamData, Begin); + } + return Error::success(); +} + +Error RawOutputStyle::dumpModules() { + printHeader(P, "Modules"); + + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine("DBI Stream not present"); + return Error::success(); + } + + ExitOnError Err("Unexpected error processing symbols"); + + auto &Stream = Err(File.getPDBDbiStream()); + + const DbiModuleList &Modules = Stream.modules(); + uint32_t Count = Modules.getModuleCount(); + uint32_t Digits = NumDigits(Count); + for (uint32_t I = 0; I < Count; ++I) { + auto Modi = Modules.getModuleDescriptor(I); + P.formatLine("Mod {0:4} | Name: `{1}`: ", + fmt_align(I, AlignStyle::Right, Digits), Modi.getModuleName()); + P.formatLine(" Obj: `{0}`: ", Modi.getObjFileName()); + P.formatLine(" debug stream: {0}, # files: {1}, has ec info: {2}", + Modi.getModuleStreamIndex(), Modi.getNumberOfFiles(), + Modi.hasECInfo()); + if (opts::raw::DumpModuleFiles) { + P.formatLine(" contributing source files:"); + for (const auto &F : Modules.source_files(I)) { + P.formatLine(" - {0}", F); + } + } + } + return Error::success(); +} +Error RawOutputStyle::dumpStringTable() { + printHeader(P, "String Table"); + + AutoIndent Indent(P); + auto IS = File.getStringTable(); + if (!IS) { + P.formatLine("Not present in file"); + consumeError(IS.takeError()); + return Error::success(); + } + + if (IS->name_ids().empty()) { + P.formatLine("Empty"); + return Error::success(); + } + + auto MaxID = std::max_element(IS->name_ids().begin(), IS->name_ids().end()); + uint32_t Digits = NumDigits(*MaxID); + + P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits), + "String"); + + std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), IS->name_ids().end()); + std::sort(SortedIDs.begin(), SortedIDs.end()); + for (uint32_t I : SortedIDs) { + auto ES = IS->getStringForID(I); + llvm::SmallString<32> Str; + if (!ES) { + consumeError(ES.takeError()); + Str = "Error reading string"; + } else if (!ES->empty()) { + Str.append("'"); + Str.append(*ES); + Str.append("'"); + } + + if (!Str.empty()) + P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), Str); + } + return Error::success(); +} + +Error RawOutputStyle::dumpTpiStream(uint32_t StreamIdx) { + assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); + + bool Present = false; + bool DumpBytes = false; + if (StreamIdx == StreamTPI) { + printHeader(P, "Types (TPI Stream)"); + Present = File.hasPDBTpiStream(); + DumpBytes = opts::raw::DumpTypeData; + } else if (StreamIdx == StreamIPI) { + printHeader(P, "Types (IPI Stream)"); + Present = File.hasPDBIpiStream(); + DumpBytes = opts::raw::DumpIdData; + } + + AutoIndent Indent(P); + if (!Present) { + P.formatLine("Stream not present"); + return Error::success(); + } + + ExitOnError Err("Unexpected error processing types"); + + auto &Stream = Err((StreamIdx == StreamTPI) ? File.getPDBTpiStream() + : File.getPDBIpiStream()); + + auto &Types = Err(initializeTypeDatabase(StreamIdx)); + + P.formatLine("Showing {0:N} records", Stream.getNumTypeRecords()); + uint32_t Width = + NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); + + MinimalTypeDumpVisitor V(P, Width + 2, DumpBytes, Types); + + Optional<TypeIndex> I = Types.getFirst(); + if (auto EC = codeview::visitTypeStream(Types, V)) { + P.formatLine("An error occurred dumping type records: {0}", + toString(std::move(EC))); + } + return Error::success(); +} + +Expected<codeview::LazyRandomTypeCollection &> +RawOutputStyle::initializeTypeDatabase(uint32_t SN) { + auto &TypeCollection = (SN == StreamTPI) ? TpiTypes : IpiTypes; + auto Tpi = + (SN == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream(); + if (!Tpi) + return Tpi.takeError(); + + if (!TypeCollection) { + auto &Types = Tpi->typeArray(); + uint32_t Count = Tpi->getNumTypeRecords(); + auto Offsets = Tpi->getTypeIndexOffsets(); + TypeCollection = + llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets); + } + + return *TypeCollection; +} + +Error RawOutputStyle::dumpModuleSyms() { + printHeader(P, "Symbols"); + + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine("DBI Stream not present"); + return Error::success(); + } + + ExitOnError Err("Unexpected error processing symbols"); + + auto &Stream = Err(File.getPDBDbiStream()); + + auto &Types = Err(initializeTypeDatabase(StreamTPI)); + + const DbiModuleList &Modules = Stream.modules(); + uint32_t Count = Modules.getModuleCount(); + uint32_t Digits = NumDigits(Count); + for (uint32_t I = 0; I < Count; ++I) { + auto Modi = Modules.getModuleDescriptor(I); + P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits), + Modi.getModuleName()); + uint16_t ModiStream = Modi.getModuleStreamIndex(); + if (ModiStream == kInvalidStreamIndex) { + P.formatLine(" <symbols not present>"); + continue; + } + auto ModStreamData = MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), ModiStream, + File.getAllocator()); + + ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); + if (auto EC = ModS.reload()) { + P.formatLine("Error loading module stream {0}. {1}", I, + toString(std::move(EC))); + continue; + } + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + MinimalSymbolDumper Dumper(P, opts::raw::DumpSymRecordBytes, Types); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + if (auto EC = Visitor.visitSymbolStream(ModS.getSymbolArray())) { + P.formatLine("Error while processing symbol records. {0}", + toString(std::move(EC))); + continue; + } + } + return Error::success(); +} + +Error RawOutputStyle::dumpPublics() { + printHeader(P, "Public Symbols"); + + AutoIndent Indent(P); + if (!File.hasPDBPublicsStream()) { + P.formatLine("Publics stream not present"); + return Error::success(); + } + + ExitOnError Err("Error dumping publics stream"); + + auto &Types = Err(initializeTypeDatabase(StreamTPI)); + auto &Publics = Err(File.getPDBPublicsStream()); + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + MinimalSymbolDumper Dumper(P, opts::raw::DumpSymRecordBytes, Types); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + auto ExpectedSymbols = Publics.getSymbolArray(); + if (!ExpectedSymbols) { + P.formatLine("Could not read public symbol record stream"); + return Error::success(); + } + + if (auto EC = Visitor.visitSymbolStream(*ExpectedSymbols)) + P.formatLine("Error while processing public symbol records. {0}", + toString(std::move(EC))); + + return Error::success(); +} + +static std::string formatSectionCharacteristics(uint32_t IndentLevel, + uint32_t C) { + using SC = COFF::SectionCharacteristics; + std::vector<std::string> Opts; + if (C == COFF::SC_Invalid) + return "invalid"; + if (C == 0) + return "none"; + + PUSH_FLAG(SC, IMAGE_SCN_TYPE_NOLOAD, C, "IMAGE_SCN_TYPE_NOLOAD"); + PUSH_FLAG(SC, IMAGE_SCN_TYPE_NO_PAD, C, "IMAGE_SCN_TYPE_NO_PAD"); + PUSH_FLAG(SC, IMAGE_SCN_CNT_CODE, C, "IMAGE_SCN_CNT_CODE"); + PUSH_FLAG(SC, IMAGE_SCN_CNT_INITIALIZED_DATA, C, + "IMAGE_SCN_CNT_INITIALIZED_DATA"); + PUSH_FLAG(SC, IMAGE_SCN_CNT_UNINITIALIZED_DATA, C, + "IMAGE_SCN_CNT_UNINITIALIZED_DATA"); + PUSH_FLAG(SC, IMAGE_SCN_LNK_OTHER, C, "IMAGE_SCN_LNK_OTHER"); + PUSH_FLAG(SC, IMAGE_SCN_LNK_INFO, C, "IMAGE_SCN_LNK_INFO"); + PUSH_FLAG(SC, IMAGE_SCN_LNK_REMOVE, C, "IMAGE_SCN_LNK_REMOVE"); + PUSH_FLAG(SC, IMAGE_SCN_LNK_COMDAT, C, "IMAGE_SCN_LNK_COMDAT"); + PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_PURGEABLE, C, "IMAGE_SCN_MEM_PURGEABLE"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_16BIT, C, "IMAGE_SCN_MEM_16BIT"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_LOCKED, C, "IMAGE_SCN_MEM_LOCKED"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_PRELOAD, C, "IMAGE_SCN_MEM_PRELOAD"); + PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); + PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1BYTES, C, + "IMAGE_SCN_ALIGN_1BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2BYTES, C, + "IMAGE_SCN_ALIGN_2BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4BYTES, C, + "IMAGE_SCN_ALIGN_4BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8BYTES, C, + "IMAGE_SCN_ALIGN_8BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_16BYTES, C, + "IMAGE_SCN_ALIGN_16BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_32BYTES, C, + "IMAGE_SCN_ALIGN_32BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_64BYTES, C, + "IMAGE_SCN_ALIGN_64BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_128BYTES, C, + "IMAGE_SCN_ALIGN_128BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_256BYTES, C, + "IMAGE_SCN_ALIGN_256BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_512BYTES, C, + "IMAGE_SCN_ALIGN_512BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1024BYTES, C, + "IMAGE_SCN_ALIGN_1024BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2048BYTES, C, + "IMAGE_SCN_ALIGN_2048BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4096BYTES, C, + "IMAGE_SCN_ALIGN_4096BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8192BYTES, C, + "IMAGE_SCN_ALIGN_8192BYTES"); + PUSH_FLAG(SC, IMAGE_SCN_LNK_NRELOC_OVFL, C, "IMAGE_SCN_LNK_NRELOC_OVFL"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_DISCARDABLE, C, "IMAGE_SCN_MEM_DISCARDABLE"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_NOT_CACHED, C, "IMAGE_SCN_MEM_NOT_CACHED"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_NOT_PAGED, C, "IMAGE_SCN_MEM_NOT_PAGED"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_SHARED, C, "IMAGE_SCN_MEM_SHARED"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_EXECUTE, C, "IMAGE_SCN_MEM_EXECUTE"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_READ, C, "IMAGE_SCN_MEM_READ"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_WRITE, C, "IMAGE_SCN_MEM_WRITE"); + return typesetItemList(Opts, 3, IndentLevel, " | "); +} + +static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel, + OMFSegDescFlags Flags) { + std::vector<std::string> Opts; + if (Flags == OMFSegDescFlags::None) + return "none"; + + PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read"); + PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write"); + PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute"); + PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr"); + PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector"); + PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr"); + PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +Error RawOutputStyle::dumpSectionContribs() { + printHeader(P, "Section Contributions"); + ExitOnError Err("Error dumping publics stream"); + + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine( + "Section contribs require a DBI Stream, which could not be loaded"); + return Error::success(); + } + + auto &Dbi = Err(File.getPDBDbiStream()); + + class Visitor : public ISectionContribVisitor { + public: + Visitor(LinePrinter &P) : P(P) {} + void visit(const SectionContrib &SC) override { + P.formatLine( + "SC | mod = {2}, {0}, size = {1}, data crc = {3}, reloc crc = {4}", + formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size), fmtle(SC.Imod), + fmtle(SC.DataCrc), fmtle(SC.RelocCrc)); + P.formatLine(" {0}", + formatSectionCharacteristics(P.getIndentLevel() + 6, + SC.Characteristics)); + } + void visit(const SectionContrib2 &SC) override { + P.formatLine("SC2 | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " + "crc = {4}, coff section = {5}", + formatSegmentOffset(SC.Base.ISect, SC.Base.Off), + fmtle(SC.Base.Size), fmtle(SC.Base.Imod), + fmtle(SC.Base.DataCrc), fmtle(SC.Base.RelocCrc), + fmtle(SC.ISectCoff)); + P.formatLine(" {0}", + formatSectionCharacteristics(P.getIndentLevel() + 6, + SC.Base.Characteristics)); + } + + private: + LinePrinter &P; + }; + + Visitor V(P); + Dbi.visitSectionContributions(V); + return Error::success(); +} + +Error RawOutputStyle::dumpSectionMap() { + printHeader(P, "Section Map"); + ExitOnError Err("Error dumping section map"); + + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine("Dumping the section map requires a DBI Stream, which could " + "not be loaded"); + return Error::success(); + } + + auto &Dbi = Err(File.getPDBDbiStream()); + + uint32_t I = 0; + for (auto &M : Dbi.getSectionMap()) { + P.formatLine( + "Section {0:4} | ovl = {0}, group = {1}, frame = {2}, name = {3}", I, + fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName)); + P.formatLine(" class = {0}, offset = {1}, size = {2}", + fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength)); + P.formatLine(" flags = {0}", + formatSegMapDescriptorFlag( + P.getIndentLevel() + 13, + static_cast<OMFSegDescFlags>(uint16_t(M.Flags)))); + ++I; + } + return Error::success(); +} diff --git a/tools/llvm-pdbutil/LLVMOutputStyle.h b/tools/llvm-pdbutil/RawOutputStyle.h index 184dc4e1f44..4c381c2a80b 100644 --- a/tools/llvm-pdbutil/LLVMOutputStyle.h +++ b/tools/llvm-pdbutil/RawOutputStyle.h @@ -1,4 +1,4 @@ -//===- LLVMOutputStyle.h -------------------------------------- *- C++ --*-===// +//===- RawOutputStyle.h -------------------------------------- *- C++ --*-===// // // The LLVM Compiler Infrastructure // @@ -7,15 +7,15 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_LLVMPDBDUMP_LLVMOUTPUTSTYLE_H -#define LLVM_TOOLS_LLVMPDBDUMP_LLVMOUTPUTSTYLE_H +#ifndef LLVM_TOOLS_LLVMPDBDUMP_RAWOUTPUTSTYLE_H +#define LLVM_TOOLS_LLVMPDBDUMP_RAWOUTPUTSTYLE_H +#include "LinePrinter.h" #include "OutputStyle.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/CodeView/TypeDatabase.h" -#include "llvm/Support/ScopedPrinter.h" #include <string> @@ -27,9 +27,9 @@ class LazyRandomTypeCollection; } namespace pdb { -class LLVMOutputStyle : public OutputStyle { +class RawOutputStyle : public OutputStyle { public: - LLVMOutputStyle(PDBFile &File); + RawOutputStyle(PDBFile &File); Error dump() override; @@ -37,34 +37,25 @@ private: Expected<codeview::LazyRandomTypeCollection &> initializeTypeDatabase(uint32_t SN); - Error dumpFileHeaders(); + Error dumpFileSummary(); Error dumpStreamSummary(); - Error dumpFreePageMap(); Error dumpBlockRanges(); - Error dumpGlobalsStream(); Error dumpStreamBytes(); - Error dumpStreamBlocks(); Error dumpStringTable(); - Error dumpInfoStream(); Error dumpTpiStream(uint32_t StreamIdx); - Error dumpDbiStream(); + Error dumpModules(); + Error dumpModuleSyms(); + Error dumpPublics(); Error dumpSectionContribs(); Error dumpSectionMap(); - Error dumpPublicsStream(); - Error dumpSectionHeaders(); - Error dumpFpoStream(); - - void dumpBitVector(StringRef Name, const BitVector &V); - - void flush(); PDBFile &File; - ScopedPrinter P; + LinePrinter P; std::unique_ptr<codeview::LazyRandomTypeCollection> TpiTypes; std::unique_ptr<codeview::LazyRandomTypeCollection> IpiTypes; SmallVector<std::string, 32> StreamPurposes; }; -} -} +} // namespace pdb +} // namespace llvm #endif diff --git a/tools/llvm-pdbutil/YAMLOutputStyle.cpp b/tools/llvm-pdbutil/YAMLOutputStyle.cpp index 8f7aba6d30c..ae3138efb13 100644 --- a/tools/llvm-pdbutil/YAMLOutputStyle.cpp +++ b/tools/llvm-pdbutil/YAMLOutputStyle.cpp @@ -32,6 +32,13 @@ using namespace llvm; using namespace llvm::codeview; using namespace llvm::pdb; +static bool checkModuleSubsection(opts::ModuleSubsection MS) { + return any_of(opts::pdb2yaml::DumpModuleSubsections, + [=](opts::ModuleSubsection M) { + return M == MS || M == opts::ModuleSubsection::All; + }); +} + YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) : File(File), Out(outs()), Obj(File.getAllocator()) { Out.setWriteDefaultValues(!opts::pdb2yaml::Minimal); @@ -93,8 +100,8 @@ Error YAMLOutputStyle::dumpFileHeaders() { } Error YAMLOutputStyle::dumpStringTable() { - bool RequiresStringTable = opts::shared::DumpModuleFiles || - !opts::shared::DumpModuleSubsections.empty(); + bool RequiresStringTable = opts::pdb2yaml::DumpModuleFiles || + !opts::pdb2yaml::DumpModuleSubsections.empty(); bool RequestedStringTable = opts::pdb2yaml::StringTable; if (!RequiresStringTable && !RequestedStringTable) return Error::success(); @@ -201,7 +208,7 @@ Error YAMLOutputStyle::dumpDbiStream() { Obj.DbiStream->PdbDllRbld = DS.getPdbDllRbld(); Obj.DbiStream->PdbDllVersion = DS.getPdbDllVersion(); Obj.DbiStream->VerHeader = DS.getDbiVersion(); - if (opts::shared::DumpModules) { + if (opts::pdb2yaml::DumpModules) { const auto &Modules = DS.modules(); for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) { DbiModuleDescriptor MI = Modules.getModuleDescriptor(I); @@ -211,7 +218,7 @@ Error YAMLOutputStyle::dumpDbiStream() { DMI.Mod = MI.getModuleName(); DMI.Obj = MI.getObjFileName(); - if (opts::shared::DumpModuleFiles) { + if (opts::pdb2yaml::DumpModuleFiles) { auto Files = Modules.source_files(I); DMI.SourceFiles.assign(Files.begin(), Files.end()); } @@ -231,7 +238,7 @@ Error YAMLOutputStyle::dumpDbiStream() { auto ExpectedST = File.getStringTable(); if (!ExpectedST) return ExpectedST.takeError(); - if (!opts::shared::DumpModuleSubsections.empty() && + if (!opts::pdb2yaml::DumpModuleSubsections.empty() && ModS.hasDebugSubsections()) { auto ExpectedChecksums = ModS.findChecksumsSubsection(); if (!ExpectedChecksums) @@ -242,7 +249,7 @@ Error YAMLOutputStyle::dumpDbiStream() { for (const auto &SS : ModS.subsections()) { opts::ModuleSubsection OptionKind = convertSubsectionKind(SS.kind()); - if (!opts::checkModuleSubsection(OptionKind)) + if (!checkModuleSubsection(OptionKind)) continue; auto Converted = @@ -253,7 +260,7 @@ Error YAMLOutputStyle::dumpDbiStream() { } } - if (opts::shared::DumpModuleSyms) { + if (opts::pdb2yaml::DumpModuleSyms) { DMI.Modi.emplace(); DMI.Modi->Signature = ModS.signature(); diff --git a/tools/llvm-pdbutil/llvm-pdbutil.cpp b/tools/llvm-pdbutil/llvm-pdbutil.cpp index 3826ba79a20..18fda036ee7 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -15,7 +15,6 @@ #include "Analyze.h" #include "Diff.h" -#include "LLVMOutputStyle.h" #include "LinePrinter.h" #include "OutputStyle.h" #include "PrettyCompilandDumper.h" @@ -23,6 +22,7 @@ #include "PrettyFunctionDumper.h" #include "PrettyTypeDumper.h" #include "PrettyVariableDumper.h" +#include "RawOutputStyle.h" #include "YAMLOutputStyle.h" #include "llvm/ADT/ArrayRef.h" @@ -266,6 +266,8 @@ cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, cl::sub(DiffSubcommand)); } +cl::OptionCategory FileOptions("Module & File Options"); + namespace raw { cl::OptionCategory MsfOptions("MSF Container Options"); @@ -274,18 +276,11 @@ cl::OptionCategory SymbolOptions("Symbol Options"); cl::OptionCategory MiscOptions("Miscellaneous Options"); // MSF OPTIONS -cl::opt<bool> DumpHeaders("headers", cl::desc("dump PDB headers"), +cl::opt<bool> DumpSummary("summary", cl::desc("dump file summary"), + cl::cat(MsfOptions), cl::sub(RawSubcommand)); +cl::opt<bool> DumpStreams("streams", + cl::desc("dump summary of the PDB streams"), cl::cat(MsfOptions), cl::sub(RawSubcommand)); -cl::opt<bool> DumpStreamBlocks("stream-blocks", - cl::desc("dump PDB stream blocks"), - cl::cat(MsfOptions), cl::sub(RawSubcommand)); -cl::opt<bool> DumpStreamSummary("stream-summary", - cl::desc("dump summary of the PDB streams"), - cl::cat(MsfOptions), cl::sub(RawSubcommand)); -cl::opt<bool> DumpPageStats( - "page-stats", - cl::desc("dump allocation stats of the pages in the MSF file"), - cl::cat(MsfOptions), cl::sub(RawSubcommand)); cl::opt<std::string> DumpBlockRangeOpt("block-data", cl::value_desc("start[-end]"), cl::desc("Dump binary data from specified range."), @@ -299,40 +294,45 @@ cl::list<std::string> cl::cat(MsfOptions), cl::sub(RawSubcommand)); // TYPE OPTIONS -cl::opt<bool> - CompactRecords("compact-records", - cl::desc("Dump type and symbol records with less detail"), - cl::cat(TypeOptions), cl::sub(RawSubcommand)); - -cl::opt<bool> - DumpTpiRecords("tpi-records", - cl::desc("dump CodeView type records from TPI stream"), - cl::cat(TypeOptions), cl::sub(RawSubcommand)); -cl::opt<bool> DumpTpiRecordBytes( - "tpi-record-bytes", +cl::opt<bool> DumpTypes("types", + cl::desc("dump CodeView type records from TPI stream"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); +cl::opt<bool> DumpTypeData( + "type-data", cl::desc("dump CodeView type record raw bytes from TPI stream"), cl::cat(TypeOptions), cl::sub(RawSubcommand)); -cl::opt<bool> DumpTpiHash("tpi-hash", cl::desc("dump CodeView TPI hash stream"), - cl::cat(TypeOptions), cl::sub(RawSubcommand)); + +cl::opt<bool> DumpTypeHashes("type-hash", + cl::desc("dump CodeView TPI hash stream"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); + +cl::opt<bool> DumpIds("ids", + cl::desc("dump CodeView type records from IPI stream"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); cl::opt<bool> - DumpIpiRecords("ipi-records", - cl::desc("dump CodeView type records from IPI stream"), - cl::cat(TypeOptions), cl::sub(RawSubcommand)); -cl::opt<bool> DumpIpiRecordBytes( - "ipi-record-bytes", - cl::desc("dump CodeView type record raw bytes from IPI stream"), - cl::cat(TypeOptions), cl::sub(RawSubcommand)); + DumpIdData("id-data", + cl::desc("dump CodeView type record raw bytes from IPI stream"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); // SYMBOL OPTIONS -cl::opt<bool> DumpGlobals("globals", cl::desc("dump globals stream data"), - cl::cat(SymbolOptions), cl::sub(RawSubcommand)); cl::opt<bool> DumpPublics("publics", cl::desc("dump Publics stream data"), cl::cat(SymbolOptions), cl::sub(RawSubcommand)); +cl::opt<bool> DumpSymbols("symbols", cl::desc("dump module symbols"), + cl::cat(SymbolOptions), cl::sub(RawSubcommand)); + cl::opt<bool> - DumpSymRecordBytes("sym-record-bytes", + DumpSymRecordBytes("sym-data", cl::desc("dump CodeView symbol record raw bytes"), cl::cat(SymbolOptions), cl::sub(RawSubcommand)); +// MODULE & FILE OPTIONS +cl::opt<bool> DumpModules("modules", cl::desc("dump compiland information"), + cl::cat(FileOptions), cl::sub(RawSubcommand)); +cl::opt<bool> DumpModuleFiles( + "files", + cl::desc("for each module dumped, dump the contributing source files"), + cl::cat(FileOptions), cl::sub(RawSubcommand)); + // MISCELLANEOUS OPTIONS cl::opt<bool> DumpStringTable("string-table", cl::desc("dump PDB String Table"), cl::cat(MiscOptions), cl::sub(RawSubcommand)); @@ -342,11 +342,6 @@ cl::opt<bool> DumpSectionContribs("section-contribs", cl::cat(MiscOptions), cl::sub(RawSubcommand)); cl::opt<bool> DumpSectionMap("section-map", cl::desc("dump section map"), cl::cat(MiscOptions), cl::sub(RawSubcommand)); -cl::opt<bool> DumpSectionHeaders("section-headers", - cl::desc("dump section headers"), - cl::cat(MiscOptions), cl::sub(RawSubcommand)); -cl::opt<bool> DumpFpo("fpo", cl::desc("dump FPO records"), cl::cat(MiscOptions), - cl::sub(RawSubcommand)); cl::opt<bool> RawAll("all", cl::desc("Implies most other options."), cl::cat(MiscOptions), cl::sub(RawSubcommand)); @@ -404,20 +399,11 @@ cl::opt<bool> IpiStream("ipi-stream", cl::desc("Dump the IPI Stream (Stream 5)"), cl::sub(PdbToYamlSubcommand), cl::init(false)); -cl::list<std::string> InputFilename(cl::Positional, - cl::desc("<input PDB file>"), cl::Required, - cl::sub(PdbToYamlSubcommand)); -} - -namespace shared { -cl::OptionCategory FileOptions("Module & File Options"); - // MODULE & FILE OPTIONS cl::opt<bool> DumpModules("modules", cl::desc("dump compiland information"), - cl::cat(FileOptions), cl::sub(RawSubcommand), - cl::sub(PdbToYamlSubcommand)); + cl::cat(FileOptions), cl::sub(PdbToYamlSubcommand)); cl::opt<bool> DumpModuleFiles("module-files", cl::desc("dump file information"), - cl::cat(FileOptions), cl::sub(RawSubcommand), + cl::cat(FileOptions), cl::sub(PdbToYamlSubcommand)); cl::list<ModuleSubsection> DumpModuleSubsections( "subsections", cl::ZeroOrMore, cl::CommaSeparated, @@ -448,11 +434,15 @@ cl::list<ModuleSubsection> DumpModuleSubsections( clEnumValN(ModuleSubsection::Unknown, "unknown", "Any subsection not covered by another option"), clEnumValN(ModuleSubsection::All, "all", "All known subsections")), - cl::cat(FileOptions), cl::sub(RawSubcommand), cl::sub(PdbToYamlSubcommand)); + cl::cat(FileOptions), cl::sub(PdbToYamlSubcommand)); cl::opt<bool> DumpModuleSyms("module-syms", cl::desc("dump module symbols"), - cl::cat(FileOptions), cl::sub(RawSubcommand), + cl::cat(FileOptions), cl::sub(PdbToYamlSubcommand)); -} // namespace shared + +cl::list<std::string> InputFilename(cl::Positional, + cl::desc("<input PDB file>"), cl::Required, + cl::sub(PdbToYamlSubcommand)); +} // namespace pdb2yaml namespace analyze { cl::opt<bool> StringTable("hash-collisions", cl::desc("Find hash collisions"), @@ -474,13 +464,6 @@ cl::opt<std::string> static ExitOnError ExitOnErr; -bool opts::checkModuleSubsection(opts::ModuleSubsection MS) { - return any_of(opts::shared::DumpModuleSubsections, - [=](opts::ModuleSubsection M) { - return M == MS || M == opts::ModuleSubsection::All; - }); -} - static void yamlToPdb(StringRef Path) { BumpPtrAllocator Allocator; ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = @@ -611,7 +594,7 @@ static void dumpRaw(StringRef Path) { std::unique_ptr<IPDBSession> Session; auto &File = loadPDB(Path, Session); - auto O = llvm::make_unique<LLVMOutputStyle>(File); + auto O = llvm::make_unique<RawOutputStyle>(File); ExitOnErr(O->dump()); } @@ -904,49 +887,21 @@ int main(int argc_, const char *argv_[]) { } } - if ((opts::RawSubcommand && opts::raw::RawAll) || - (opts::PdbToYamlSubcommand && opts::pdb2yaml::All)) { - opts::shared::DumpModules = true; - opts::shared::DumpModuleFiles = true; - opts::shared::DumpModuleSyms = true; - opts::shared::DumpModuleSubsections.push_back(opts::ModuleSubsection::All); - if (llvm::is_contained(opts::shared::DumpModuleSubsections, - opts::ModuleSubsection::All)) { - opts::shared::DumpModuleSubsections.reset(); - opts::shared::DumpModuleSubsections.push_back( - opts::ModuleSubsection::All); - } - } - - if (opts::shared::DumpModuleSyms || opts::shared::DumpModuleFiles) - opts::shared::DumpModules = true; - - if (opts::shared::DumpModules) - opts::pdb2yaml::DbiStream = true; - if (opts::RawSubcommand) { if (opts::raw::RawAll) { - opts::raw::DumpHeaders = true; - opts::raw::DumpGlobals = true; + opts::raw::DumpIds = true; opts::raw::DumpPublics = true; - opts::raw::DumpSectionHeaders = true; - opts::raw::DumpStreamSummary = true; - opts::raw::DumpPageStats = true; - opts::raw::DumpStreamBlocks = true; - opts::raw::DumpTpiRecords = true; - opts::raw::DumpTpiHash = true; - opts::raw::DumpIpiRecords = true; - opts::raw::DumpSectionMap = true; opts::raw::DumpSectionContribs = true; - opts::raw::DumpFpo = true; + opts::raw::DumpSectionMap = true; + opts::raw::DumpStreams = true; opts::raw::DumpStringTable = true; - } - - if (opts::raw::CompactRecords && - (opts::raw::DumpTpiRecordBytes || opts::raw::DumpIpiRecordBytes)) { - errs() << "-compact-records is incompatible with -tpi-record-bytes and " - "-ipi-record-bytes.\n"; - exit(1); + opts::raw::DumpSummary = true; + opts::raw::DumpSymbols = true; + opts::raw::DumpIds = true; + opts::raw::DumpTypes = true; + opts::raw::DumpTypeHashes = true; + opts::raw::DumpModules = true; + opts::raw::DumpModuleFiles = true; } } if (opts::PdbToYamlSubcommand) { @@ -958,7 +913,24 @@ int main(int argc_, const char *argv_[]) { opts::pdb2yaml::DbiStream = true; opts::pdb2yaml::TpiStream = true; opts::pdb2yaml::IpiStream = true; + opts::pdb2yaml::DumpModules = true; + opts::pdb2yaml::DumpModuleFiles = true; + opts::pdb2yaml::DumpModuleSyms = true; + opts::pdb2yaml::DumpModuleSubsections.push_back( + opts::ModuleSubsection::All); + if (llvm::is_contained(opts::pdb2yaml::DumpModuleSubsections, + opts::ModuleSubsection::All)) { + opts::pdb2yaml::DumpModuleSubsections.reset(); + opts::pdb2yaml::DumpModuleSubsections.push_back( + opts::ModuleSubsection::All); + } } + + if (opts::pdb2yaml::DumpModuleSyms || opts::pdb2yaml::DumpModuleFiles) + opts::pdb2yaml::DumpModules = true; + + if (opts::pdb2yaml::DumpModules) + opts::pdb2yaml::DbiStream = true; } llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); diff --git a/tools/llvm-pdbutil/llvm-pdbutil.h b/tools/llvm-pdbutil/llvm-pdbutil.h index f1699d0bb55..37c4ca3ee5d 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/tools/llvm-pdbutil/llvm-pdbutil.h @@ -27,6 +27,8 @@ uint32_t getTypeLength(const PDBSymbolData &Symbol); namespace opts { +enum class DumpLevel { None, Basic, Verbose }; + enum class ModuleSubsection { Unknown, Lines, @@ -41,15 +43,6 @@ enum class ModuleSubsection { All }; -bool checkModuleSubsection(ModuleSubsection Kind); - -template <typename... Ts> -bool checkModuleSubsection(ModuleSubsection K1, ModuleSubsection K2, - Ts &&... Rest) { - return checkModuleSubsection(K1) || - checkModuleSubsection(K2, std::forward<Ts>(Rest)...); -} - namespace pretty { enum class ClassDefinitionFormat { None, Layout, All }; @@ -105,27 +98,24 @@ struct BlockRange { llvm::Optional<uint32_t> Max; }; +extern llvm::cl::opt<bool> DumpSummary; +extern llvm::cl::opt<bool> DumpStreams; extern llvm::Optional<BlockRange> DumpBlockRange; extern llvm::cl::list<std::string> DumpStreamData; - -extern llvm::cl::opt<bool> CompactRecords; -extern llvm::cl::opt<bool> DumpGlobals; -extern llvm::cl::opt<bool> DumpHeaders; -extern llvm::cl::opt<bool> DumpStreamBlocks; -extern llvm::cl::opt<bool> DumpStreamSummary; -extern llvm::cl::opt<bool> DumpPageStats; -extern llvm::cl::opt<bool> DumpTpiHash; -extern llvm::cl::opt<bool> DumpTpiRecordBytes; -extern llvm::cl::opt<bool> DumpTpiRecords; -extern llvm::cl::opt<bool> DumpIpiRecords; -extern llvm::cl::opt<bool> DumpIpiRecordBytes; +extern llvm::cl::opt<bool> DumpStringTable; +extern llvm::cl::opt<bool> DumpTypes; +extern llvm::cl::opt<bool> DumpTypeData; +extern llvm::cl::opt<bool> DumpTypeHashes; +extern llvm::cl::opt<bool> DumpIds; +extern llvm::cl::opt<bool> DumpIdData; +extern llvm::cl::opt<bool> DumpSymbols; +extern llvm::cl::opt<bool> DumpSymRecordBytes; extern llvm::cl::opt<bool> DumpPublics; extern llvm::cl::opt<bool> DumpSectionContribs; extern llvm::cl::opt<bool> DumpSectionMap; -extern llvm::cl::opt<bool> DumpSymRecordBytes; -extern llvm::cl::opt<bool> DumpSectionHeaders; -extern llvm::cl::opt<bool> DumpFpo; -extern llvm::cl::opt<bool> DumpStringTable; +extern llvm::cl::opt<bool> DumpModules; +extern llvm::cl::opt<bool> DumpModuleFiles; +extern llvm::cl::opt<bool> RawAll; } namespace diff { @@ -144,14 +134,11 @@ extern llvm::cl::opt<bool> DbiStream; extern llvm::cl::opt<bool> TpiStream; extern llvm::cl::opt<bool> IpiStream; extern llvm::cl::list<std::string> InputFilename; -} - -namespace shared { extern llvm::cl::opt<bool> DumpModules; extern llvm::cl::opt<bool> DumpModuleFiles; extern llvm::cl::list<ModuleSubsection> DumpModuleSubsections; extern llvm::cl::opt<bool> DumpModuleSyms; -} // namespace shared +} // namespace pdb2yaml } #endif |