[llvm] r312358 - [llvm-pdbutil] Support dumping CodeView from object files.

Zachary Turner via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 1 13:06:56 PDT 2017


Author: zturner
Date: Fri Sep  1 13:06:56 2017
New Revision: 312358

URL: http://llvm.org/viewvc/llvm-project?rev=312358&view=rev
Log:
[llvm-pdbutil] Support dumping CodeView from object files.

We have llvm-readobj for dumping CodeView from object files, and
llvm-pdbutil has always been more focused on PDB.  However,
llvm-pdbutil has a lot of useful options for summarizing debug
information in aggregate and presenting high level statistical
views.  Furthermore, it's arguably better as a testing tool since
we don't have to write tests to conform to a state-machine like
structure where you match multiple lines in succession, each
depending on a previous match.  llvm-pdbutil dumps much more
concisely, so it's possible to use single-line matches in many
cases where as with readobj tests you have to use multi-line
matches with an implicit state machine.

Because of this, I'm adding object file support to llvm-pdbutil.
In fact, this mirrors the cvdump tool from Microsoft, which also
supports both object files and pdb files.  In the future we could
perhaps rename this tool llvm-cvutil.

In the meantime, this allows us to deep dive into object files
the same way we already can with PDB files.

Added:
    llvm/trunk/tools/llvm-pdbutil/InputFile.cpp
    llvm/trunk/tools/llvm-pdbutil/InputFile.h
Modified:
    llvm/trunk/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
    llvm/trunk/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h
    llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h
    llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFile.h
    llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
    llvm/trunk/lib/DebugInfo/CodeView/StringsAndChecksums.cpp
    llvm/trunk/lib/DebugInfo/PDB/Native/PDBFile.cpp
    llvm/trunk/test/DebugInfo/COFF/udts.ll
    llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt
    llvm/trunk/tools/llvm-pdbutil/DumpOutputStyle.cpp
    llvm/trunk/tools/llvm-pdbutil/DumpOutputStyle.h
    llvm/trunk/tools/llvm-pdbutil/LinePrinter.h
    llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp
    llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h

Modified: llvm/trunk/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h (original)
+++ llvm/trunk/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h Fri Sep  1 13:06:56 2017
@@ -70,6 +70,8 @@ public:
 
   uint32_t getOffsetOfType(TypeIndex Index);
 
+  Optional<CVType> tryGetType(TypeIndex Index);
+
   CVType getType(TypeIndex Index) override;
   StringRef getTypeName(TypeIndex Index) override;
   bool contains(TypeIndex Index) override;

Modified: llvm/trunk/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h (original)
+++ llvm/trunk/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h Fri Sep  1 13:06:56 2017
@@ -31,8 +31,13 @@ public:
   StringsAndChecksumsRef(const DebugStringTableSubsectionRef &Strings,
                          const DebugChecksumsSubsectionRef &Checksums);
 
+  void setStrings(const DebugStringTableSubsectionRef &Strings);
   void setChecksums(const DebugChecksumsSubsectionRef &CS);
 
+  void reset();
+  void resetStrings();
+  void resetChecksums();
+
   template <typename T> void initialize(T &&FragmentRange) {
     for (const DebugSubsectionRecord &R : FragmentRange) {
       if (Strings && Checksums)
@@ -67,8 +72,8 @@ private:
   void initializeStrings(const DebugSubsectionRecord &SR);
   void initializeChecksums(const DebugSubsectionRecord &FCR);
 
-  std::unique_ptr<DebugStringTableSubsectionRef> OwnedStrings;
-  std::unique_ptr<DebugChecksumsSubsectionRef> OwnedChecksums;
+  std::shared_ptr<DebugStringTableSubsectionRef> OwnedStrings;
+  std::shared_ptr<DebugChecksumsSubsectionRef> OwnedChecksums;
 
   const DebugStringTableSubsectionRef *Strings = nullptr;
   const DebugChecksumsSubsectionRef *Checksums = nullptr;

Modified: llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h (original)
+++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h Fri Sep  1 13:06:56 2017
@@ -52,6 +52,9 @@ public:
   ModuleDebugStreamRef &operator=(ModuleDebugStreamRef &&Other) = default;
 
   iterator_range<DebugSubsectionIterator> subsections() const;
+  codeview::DebugSubsectionArray getSubsectionsArray() const {
+    return Subsections;
+  }
 
   bool hasDebugSubsections() const;
 

Modified: llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFile.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFile.h?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFile.h (original)
+++ llvm/trunk/include/llvm/DebugInfo/PDB/Native/PDBFile.h Fri Sep  1 13:06:56 2017
@@ -105,7 +105,7 @@ public:
 
   bool hasPDBDbiStream() const;
   bool hasPDBGlobalsStream();
-  bool hasPDBInfoStream();
+  bool hasPDBInfoStream() const;
   bool hasPDBIpiStream() const;
   bool hasPDBPublicsStream();
   bool hasPDBSymbolStream();

Modified: llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp (original)
+++ llvm/trunk/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp Fri Sep  1 13:06:56 2017
@@ -90,6 +90,16 @@ CVType LazyRandomTypeCollection::getType
   return Records[Index.toArrayIndex()].Type;
 }
 
+Optional<CVType> LazyRandomTypeCollection::tryGetType(TypeIndex Index) {
+  if (auto EC = ensureTypeExists(Index)) {
+    consumeError(std::move(EC));
+    return None;
+  }
+
+  assert(contains(Index));
+  return Records[Index.toArrayIndex()].Type;
+}
+
 StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) {
   if (Index.isNoneType() || Index.isSimple())
     return TypeIndex::simpleTypeName(Index);
@@ -113,6 +123,9 @@ StringRef LazyRandomTypeCollection::getT
 }
 
 bool LazyRandomTypeCollection::contains(TypeIndex Index) {
+  if (Index.isSimple() || Index.isNoneType())
+    return false;
+
   if (Records.size() <= Index.toArrayIndex())
     return false;
   if (!Records[Index.toArrayIndex()].Type.valid())

Modified: llvm/trunk/lib/DebugInfo/CodeView/StringsAndChecksums.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/DebugInfo/CodeView/StringsAndChecksums.cpp?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/lib/DebugInfo/CodeView/StringsAndChecksums.cpp (original)
+++ llvm/trunk/lib/DebugInfo/CodeView/StringsAndChecksums.cpp Fri Sep  1 13:06:56 2017
@@ -35,14 +35,36 @@ void StringsAndChecksumsRef::initializeS
   assert(SR.kind() == DebugSubsectionKind::StringTable);
   assert(!Strings && "Found a string table even though we already have one!");
 
-  OwnedStrings = llvm::make_unique<DebugStringTableSubsectionRef>();
+  OwnedStrings = std::make_shared<DebugStringTableSubsectionRef>();
   consumeError(OwnedStrings->initialize(SR.getRecordData()));
   Strings = OwnedStrings.get();
 }
 
+void StringsAndChecksumsRef::reset() {
+  resetStrings();
+  resetChecksums();
+}
+
+void StringsAndChecksumsRef::resetStrings() {
+  OwnedStrings.reset();
+  Strings = nullptr;
+}
+
+void StringsAndChecksumsRef::resetChecksums() {
+  OwnedChecksums.reset();
+  Checksums = nullptr;
+}
+
+void StringsAndChecksumsRef::setStrings(
+    const DebugStringTableSubsectionRef &StringsRef) {
+  OwnedStrings = std::make_shared<DebugStringTableSubsectionRef>();
+  *OwnedStrings = StringsRef;
+  Strings = OwnedStrings.get();
+}
+
 void StringsAndChecksumsRef::setChecksums(
     const DebugChecksumsSubsectionRef &CS) {
-  OwnedChecksums = llvm::make_unique<DebugChecksumsSubsectionRef>();
+  OwnedChecksums = std::make_shared<DebugChecksumsSubsectionRef>();
   *OwnedChecksums = CS;
   Checksums = OwnedChecksums.get();
 }
@@ -53,7 +75,7 @@ void StringsAndChecksumsRef::initializeC
   if (Checksums)
     return;
 
-  OwnedChecksums = llvm::make_unique<DebugChecksumsSubsectionRef>();
+  OwnedChecksums = std::make_shared<DebugChecksumsSubsectionRef>();
   consumeError(OwnedChecksums->initialize(FCR.getRecordData()));
   Checksums = OwnedChecksums.get();
 }

Modified: llvm/trunk/lib/DebugInfo/PDB/Native/PDBFile.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/DebugInfo/PDB/Native/PDBFile.cpp?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/lib/DebugInfo/PDB/Native/PDBFile.cpp (original)
+++ llvm/trunk/lib/DebugInfo/PDB/Native/PDBFile.cpp Fri Sep  1 13:06:56 2017
@@ -312,6 +312,9 @@ Expected<TpiStream &> PDBFile::getPDBTpi
 
 Expected<TpiStream &> PDBFile::getPDBIpiStream() {
   if (!Ipi) {
+    if (!hasPDBIpiStream())
+      return make_error<RawError>(raw_error_code::no_stream);
+
     auto IpiS = safelyCreateIndexedStream(ContainerLayout, *Buffer, StreamIPI);
     if (!IpiS)
       return IpiS.takeError();
@@ -407,9 +410,18 @@ bool PDBFile::hasPDBGlobalsStream() {
   return DbiS->getGlobalSymbolStreamIndex() < getNumStreams();
 }
 
-bool PDBFile::hasPDBInfoStream() { return StreamPDB < getNumStreams(); }
+bool PDBFile::hasPDBInfoStream() const { return StreamPDB < getNumStreams(); }
+
+bool PDBFile::hasPDBIpiStream() const {
+  if (!hasPDBInfoStream())
+    return false;
+
+  if (StreamIPI >= getNumStreams())
+    return false;
 
-bool PDBFile::hasPDBIpiStream() const { return StreamIPI < getNumStreams(); }
+  auto &InfoStream = cantFail(const_cast<PDBFile *>(this)->getPDBInfoStream());
+  return InfoStream.containsIdStream();
+}
 
 bool PDBFile::hasPDBPublicsStream() {
   auto DbiS = getPDBDbiStream();

Modified: llvm/trunk/test/DebugInfo/COFF/udts.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/DebugInfo/COFF/udts.ll?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/test/DebugInfo/COFF/udts.ll (original)
+++ llvm/trunk/test/DebugInfo/COFF/udts.ll Fri Sep  1 13:06:56 2017
@@ -1,4 +1,7 @@
-; RUN: llc < %s -filetype=obj | llvm-readobj - -codeview | FileCheck %s
+; RUN: llc < %s -filetype=obj > %t.obj
+; RUN: llvm-readobj -codeview %t.obj | FileCheck --check-prefix=READOBJ %s
+; RUN: llvm-pdbutil dump -udt-stats %t.obj | FileCheck --check-prefix=PDBUTIL %s
+
 source_filename = "test/DebugInfo/COFF/udts.ll"
 target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
 target triple = "i686-pc-windows-msvc18.0.0"
@@ -18,39 +21,52 @@ target triple = "i686-pc-windows-msvc18.
 ; typedef struct { int x; } U;
 ; U u;
 
-; CHECK:      {{.*}}Proc{{.*}}Sym {
-; CHECK:        DisplayName: f
-; CHECK:        LinkageName: ?f@@YAXXZ
-; CHECK:      }
-; CHECK:      UDTSym {
-; CHECK-NEXT:   Kind: S_UDT (0x1108)
-; CHECK-NEXT:   Type: int (0x74)
-; CHECK-NEXT:   UDTName: f::FOO
-; CHECK-NEXT: }
-; CHECK-NEXT: ProcEnd {
-
-; CHECK:      {{.*}}Proc{{.*}}Sym {
-; CHECK:        DisplayName: g
-; CHECK:        LinkageName: ?g@@YAMPEAUS@@@Z
-; CHECK:      }
-; CHECK:      UDTSym {
-; CHECK-NEXT:   Kind: S_UDT (0x1108)
-; CHECK-NEXT:   Type: g::pun (0x{{[0-9A-F]+}})
-; CHECK-NEXT:   UDTName: g::pun
-; CHECK-NEXT: }
-; CHECK-NEXT: ProcEnd {
-
-; CHECK:      Subsection
-; CHECK-NOT:  {{.*}}Proc{{.*}}Sym
-; CHECK:      UDTSym {
-; CHECK-NEXT:   Kind: S_UDT (0x1108)
-; CHECK-NEXT: Type: S (0x{{[0-9A-F]+}})
-; CHECK-NEXT: UDTName: S
-; CHECK:      UDTSym {
-; CHECK-NEXT:   Kind: S_UDT (0x1108)
-; CHECK-NEXT: Type: <unnamed-tag> (0x{{[0-9A-F]+}})
-; CHECK-NEXT: UDTName: U
-; CHECK-NOT: UDTSym {
+; READOBJ:      {{.*}}Proc{{.*}}Sym {
+; READOBJ:        DisplayName: f
+; READOBJ:        LinkageName: ?f@@YAXXZ
+; READOBJ:      }
+; READOBJ:      UDTSym {
+; READOBJ-NEXT:   Kind: S_UDT (0x1108)
+; READOBJ-NEXT:   Type: int (0x74)
+; READOBJ-NEXT:   UDTName: f::FOO
+; READOBJ-NEXT: }
+; READOBJ-NEXT: ProcEnd {
+
+; READOBJ:      {{.*}}Proc{{.*}}Sym {
+; READOBJ:        DisplayName: g
+; READOBJ:        LinkageName: ?g@@YAMPEAUS@@@Z
+; READOBJ:      }
+; READOBJ:      UDTSym {
+; READOBJ-NEXT:   Kind: S_UDT (0x1108)
+; READOBJ-NEXT:   Type: g::pun (0x{{[0-9A-F]+}})
+; READOBJ-NEXT:   UDTName: g::pun
+; READOBJ-NEXT: }
+; READOBJ-NEXT: ProcEnd {
+
+; READOBJ:      Subsection
+; READOBJ-NOT:  {{.*}}Proc{{.*}}Sym
+; READOBJ:      UDTSym {
+; READOBJ-NEXT:   Kind: S_UDT (0x1108)
+; READOBJ-NEXT: Type: S (0x{{[0-9A-F]+}})
+; READOBJ-NEXT: UDTName: S
+; READOBJ:      UDTSym {
+; READOBJ-NEXT:   Kind: S_UDT (0x1108)
+; READOBJ-NEXT: Type: <unnamed-tag> (0x{{[0-9A-F]+}})
+; READOBJ-NEXT: UDTName: U
+; READOBJ-NOT:  UDTSym {
+
+; PDBUTIL:                           S_UDT Record Stats
+; PDBUTIL-NEXT: ============================================================
+; PDBUTIL:             Record Kind | Count  Size
+; PDBUTIL-NEXT:     -----------------------------
+; PDBUTIL-NEXT:           LF_UNION |     1    24
+; PDBUTIL-NEXT:      <simple type> |     1     0
+; PDBUTIL-NEXT:       LF_STRUCTURE |     2    76
+; PDBUTIL-NEXT:     -----------------------------
+; PDBUTIL-NEXT:      Total (S_UDT) |     4    50
+; PDBUTIL-NEXT:     -----------------------------
+; PDBUTIL-NEXT:      namespace 'f' |     1     0
+; PDBUTIL-NEXT:      namespace 'g' |     1    24
 
 %struct.U = type { i32 }
 %struct.S = type { i32 }

Modified: llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt (original)
+++ llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt Fri Sep  1 13:06:56 2017
@@ -1,4 +1,5 @@
 set(LLVM_LINK_COMPONENTS
+  BinaryFormat
   DebugInfoCodeView
   DebugInfoMSF
   DebugInfoPDB
@@ -13,6 +14,7 @@ add_llvm_tool(llvm-pdbutil
   Diff.cpp
   DiffPrinter.cpp
   DumpOutputStyle.cpp
+  InputFile.cpp
   llvm-pdbutil.cpp
   FormatUtil.cpp
   LinePrinter.cpp

Modified: llvm/trunk/tools/llvm-pdbutil/DumpOutputStyle.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/DumpOutputStyle.cpp?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/DumpOutputStyle.cpp (original)
+++ llvm/trunk/tools/llvm-pdbutil/DumpOutputStyle.cpp Fri Sep  1 13:06:56 2017
@@ -10,6 +10,7 @@
 #include "DumpOutputStyle.h"
 
 #include "FormatUtil.h"
+#include "InputFile.h"
 #include "MinimalSymbolDumper.h"
 #include "MinimalTypeDumper.h"
 #include "StreamUtil.h"
@@ -67,9 +68,12 @@ using namespace llvm::codeview;
 using namespace llvm::msf;
 using namespace llvm::pdb;
 
-DumpOutputStyle::DumpOutputStyle(PDBFile &File)
+DumpOutputStyle::DumpOutputStyle(InputFile &File)
     : File(File), P(2, false, outs()) {}
 
+PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); }
+object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); }
+
 Error DumpOutputStyle::dump() {
   if (opts::dump::DumpSummary) {
     if (auto EC = dumpFileSummary())
@@ -83,13 +87,13 @@ Error DumpOutputStyle::dump() {
     P.NewLine();
   }
 
-  if (opts::dump::DumpSymbolStats.getNumOccurrences() > 0) {
+  if (opts::dump::DumpSymbolStats) {
     if (auto EC = dumpSymbolStats())
       return EC;
     P.NewLine();
   }
 
-  if (opts::dump::DumpUdtStats.getNumOccurrences() > 0) {
+  if (opts::dump::DumpUdtStats) {
     if (auto EC = dumpUdtStats())
       return EC;
     P.NewLine();
@@ -154,7 +158,8 @@ Error DumpOutputStyle::dump() {
   }
 
   if (opts::dump::DumpSymbols) {
-    if (auto EC = dumpModuleSyms())
+    auto EC = File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj();
+    if (EC)
       return EC;
   }
 
@@ -188,22 +193,27 @@ Error DumpOutputStyle::dumpFileSummary()
   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());
+  if (File.isObj()) {
+    P.formatLine("Dumping File summary is not valid for object files");
+    return Error::success();
+  }
 
-  auto &PS = Err(File.getPDBInfoStream());
+  P.formatLine("Block Size: {0}", getPdb().getBlockSize());
+  P.formatLine("Number of blocks: {0}", getPdb().getBlockCount());
+  P.formatLine("Number of streams: {0}", getPdb().getNumStreams());
+
+  auto &PS = Err(getPdb().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("Has Debug Info: {0}", getPdb().hasPDBDbiStream());
+  P.formatLine("Has Types: {0}", getPdb().hasPDBTpiStream());
+  P.formatLine("Has IDs: {0}", getPdb().hasPDBIpiStream());
+  P.formatLine("Has Globals: {0}", getPdb().hasPDBGlobalsStream());
+  P.formatLine("Has Publics: {0}", getPdb().hasPDBPublicsStream());
+  if (getPdb().hasPDBDbiStream()) {
+    auto &DBI = Err(getPdb().getPDBDbiStream());
     P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked());
     P.formatLine("Has conflicting types: {0}", DBI.hasCTypes());
     P.formatLine("Is stripped: {0}", DBI.isStripped());
@@ -212,20 +222,38 @@ Error DumpOutputStyle::dumpFileSummary()
   return Error::success();
 }
 
-static StatCollection getSymbolStats(ModuleDebugStreamRef MDS,
+static StatCollection getSymbolStats(const SymbolGroup &SG,
                                      StatCollection &CumulativeStats) {
   StatCollection Stats;
-  for (const auto &S : MDS.symbols(nullptr)) {
-    Stats.update(S.kind(), S.length());
-    CumulativeStats.update(S.kind(), S.length());
+  if (SG.getFile().isPdb()) {
+    // For PDB files, all symbols are packed into one stream.
+    for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) {
+      Stats.update(S.kind(), S.length());
+      CumulativeStats.update(S.kind(), S.length());
+    }
+    return Stats;
+  }
+
+  for (const auto &SS : SG.getDebugSubsections()) {
+    // For object files, all symbols are spread across multiple Symbol
+    // subsections of a given .debug$S section.
+    if (SS.kind() != DebugSubsectionKind::Symbols)
+      continue;
+    DebugSymbolsSubsectionRef Symbols;
+    BinaryStreamReader Reader(SS.getRecordData());
+    cantFail(Symbols.initialize(Reader));
+    for (const auto &S : Symbols) {
+      Stats.update(S.kind(), S.length());
+      CumulativeStats.update(S.kind(), S.length());
+    }
   }
   return Stats;
 }
 
-static StatCollection getChunkStats(ModuleDebugStreamRef MDS,
+static StatCollection getChunkStats(const SymbolGroup &SG,
                                     StatCollection &CumulativeStats) {
   StatCollection Stats;
-  for (const auto &Chunk : MDS.subsections()) {
+  for (const auto &Chunk : SG.getDebugSubsections()) {
     Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());
     CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());
   }
@@ -256,8 +284,11 @@ static void printModuleDetailStats(LineP
   }
 }
 
-static bool isMyCode(const DbiModuleDescriptor &Desc) {
-  StringRef Name = Desc.getModuleName();
+static bool isMyCode(const SymbolGroup &Group) {
+  if (Group.getFile().isObj())
+    return true;
+
+  StringRef Name = Group.name();
   if (Name.startswith("Import:"))
     return false;
   if (Name.endswith_lower(".dll"))
@@ -271,8 +302,8 @@ static bool isMyCode(const DbiModuleDesc
   return true;
 }
 
-static bool shouldDumpModule(uint32_t Modi, const DbiModuleDescriptor &Desc) {
-  if (opts::dump::JustMyCode && !isMyCode(Desc))
+static bool shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group) {
+  if (opts::dump::JustMyCode && !isMyCode(Group))
     return false;
 
   // If the arg was not specified on the command line, always dump all modules.
@@ -280,29 +311,34 @@ static bool shouldDumpModule(uint32_t Mo
     return true;
 
   // Otherwise, only dump if this is the same module specified.
-  return (opts::dump::DumpModi == Modi);
+  return (opts::dump::DumpModi == Idx);
 }
 
 Error DumpOutputStyle::dumpStreamSummary() {
   printHeader(P, "Streams");
 
+  AutoIndent Indent(P);
+  if (File.isObj()) {
+    P.formatLine("Dumping streams is not valid for object files");
+    return Error::success();
+  }
+
   if (StreamPurposes.empty())
-    discoverStreamPurposes(File, StreamPurposes);
+    discoverStreamPurposes(getPdb(), StreamPurposes);
 
-  AutoIndent Indent(P);
-  uint32_t StreamCount = File.getNumStreams();
-  uint32_t MaxStreamSize = File.getMaxStreamSize();
+  uint32_t StreamCount = getPdb().getNumStreams();
+  uint32_t MaxStreamSize = getPdb().getMaxStreamSize();
 
   for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
     P.formatLine(
         "Stream {0} ({1} bytes): [{2}]",
         fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)),
-        fmt_align(File.getStreamByteSize(StreamIdx), AlignStyle::Right,
+        fmt_align(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right,
                   NumDigits(MaxStreamSize)),
         StreamPurposes[StreamIdx].getLongName());
 
     if (opts::dump::DumpStreamBlocks) {
-      auto Blocks = File.getStreamBlockList(StreamIdx);
+      auto Blocks = getPdb().getStreamBlockList(StreamIdx);
       std::vector<uint32_t> BV(Blocks.begin(), Blocks.end());
       P.formatLine("       {0}  Blocks: [{1}]",
                    fmt_repeat(' ', NumDigits(StreamCount)),
@@ -326,9 +362,7 @@ static Expected<ModuleDebugStreamRef> ge
     return make_error<RawError>(raw_error_code::no_stream,
                                 "Module stream not present");
 
-  auto ModStreamData = MappedBlockStream::createIndexedStream(
-      File.getMsfLayout(), File.getMsfBuffer(), ModiStream,
-      File.getAllocator());
+  auto ModStreamData = File.createIndexedStream(ModiStream);
 
   ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));
   if (auto EC = ModS.reload())
@@ -338,198 +372,91 @@ static Expected<ModuleDebugStreamRef> ge
   return std::move(ModS);
 }
 
-static std::string formatChecksumKind(FileChecksumKind Kind) {
-  switch (Kind) {
-    RETURN_CASE(FileChecksumKind, None, "None");
-    RETURN_CASE(FileChecksumKind, MD5, "MD5");
-    RETURN_CASE(FileChecksumKind, SHA1, "SHA-1");
-    RETURN_CASE(FileChecksumKind, SHA256, "SHA-256");
-  }
-  return formatUnknownEnum(Kind);
-}
-
-namespace {
-class StringsAndChecksumsPrinter {
-  const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) {
-    ExitOnError Err("Unexpected error processing modules: ");
-    return Err(File.getStringTable()).getStringTable();
-  }
-
-  template <typename... Args>
-  void formatInternal(LinePrinter &Printer, bool Append,
-                      Args &&... args) const {
-    if (Append)
-      Printer.format(std::forward<Args>(args)...);
-    else
-      Printer.formatLine(std::forward<Args>(args)...);
-  }
-
-public:
-  StringsAndChecksumsPrinter(PDBFile &File, uint32_t Modi)
-      : Records(extractStringTable(File)) {
-    auto MDS = getModuleDebugStream(File, Modi);
-    if (!MDS) {
-      consumeError(MDS.takeError());
-      return;
-    }
-
-    DebugStream = llvm::make_unique<ModuleDebugStreamRef>(std::move(*MDS));
-    Records.initialize(MDS->subsections());
-    if (Records.hasChecksums()) {
-      for (const auto &Entry : Records.checksums()) {
-        auto S = Records.strings().getString(Entry.FileNameOffset);
-        if (!S)
-          continue;
-        ChecksumsByFile[*S] = Entry;
-      }
-    }
-  }
-
-  Expected<StringRef> getNameFromStringTable(uint32_t Offset) const {
-    return Records.strings().getString(Offset);
-  }
-
-  void formatFromFileName(LinePrinter &Printer, StringRef File,
-                          bool Append = false) const {
-    auto FC = ChecksumsByFile.find(File);
-    if (FC == ChecksumsByFile.end()) {
-      formatInternal(Printer, Append, "- (no checksum) {0}", File);
-      return;
-    }
-
-    formatInternal(Printer, Append, "- ({0}: {1}) {2}",
-                   formatChecksumKind(FC->getValue().Kind),
-                   toHex(FC->getValue().Checksum), File);
-  }
-
-  void formatFromChecksumsOffset(LinePrinter &Printer, uint32_t Offset,
-                                 bool Append = false) const {
-    if (!Records.hasChecksums()) {
-      formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
-      return;
-    }
-
-    auto Iter = Records.checksums().getArray().at(Offset);
-    if (Iter == Records.checksums().getArray().end()) {
-      formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
-      return;
-    }
-
-    uint32_t FO = Iter->FileNameOffset;
-    auto ExpectedFile = getNameFromStringTable(FO);
-    if (!ExpectedFile) {
-      formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
-      consumeError(ExpectedFile.takeError());
-      return;
-    }
-    if (Iter->Kind == FileChecksumKind::None) {
-      formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile);
-    } else {
-      formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile,
-                     formatChecksumKind(Iter->Kind), toHex(Iter->Checksum));
-    }
+template <typename CallbackT>
+static void
+iterateOneModule(InputFile &File, const Optional<PrintScope> &HeaderScope,
+                 const SymbolGroup &SG, uint32_t Modi, CallbackT Callback) {
+  if (HeaderScope) {
+    HeaderScope->P.formatLine(
+        "Mod {0:4} | `{1}`: ",
+        fmt_align(Modi, AlignStyle::Right, HeaderScope->LabelWidth), SG.name());
   }
 
-  std::unique_ptr<ModuleDebugStreamRef> DebugStream;
-  StringsAndChecksumsRef Records;
-  StringMap<FileChecksumEntry> ChecksumsByFile;
-};
-} // namespace
-
-template <typename CallbackT>
-static void iterateOneModule(PDBFile &File, LinePrinter &P,
-                             const DbiModuleDescriptor &Descriptor,
-                             uint32_t Modi, uint32_t IndentLevel,
-                             uint32_t Digits, CallbackT Callback) {
-  P.formatLine(
-      "Mod {0:4} | `{1}`: ", fmt_align(Modi, AlignStyle::Right, Digits),
-      Descriptor.getModuleName());
-
-  StringsAndChecksumsPrinter Strings(File, Modi);
-  AutoIndent Indent2(P, IndentLevel);
-  Callback(Modi, Strings);
+  AutoIndent Indent(HeaderScope);
+  Callback(Modi, SG);
 }
 
 template <typename CallbackT>
-static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
-                           CallbackT Callback) {
-  AutoIndent Indent(P);
-  if (!File.hasPDBDbiStream()) {
-    P.formatLine("DBI Stream not present");
-    return;
-  }
+static void iterateSymbolGroups(InputFile &Input,
+                                const Optional<PrintScope> &HeaderScope,
+                                CallbackT Callback) {
+  AutoIndent Indent(HeaderScope);
 
   ExitOnError Err("Unexpected error processing modules: ");
 
-  auto &Stream = Err(File.getPDBDbiStream());
-
-  const DbiModuleList &Modules = Stream.modules();
-
   if (opts::dump::DumpModi.getNumOccurrences() > 0) {
     assert(opts::dump::DumpModi.getNumOccurrences() == 1);
     uint32_t Modi = opts::dump::DumpModi;
-    auto Descriptor = Modules.getModuleDescriptor(Modi);
-    iterateOneModule(File, P, Descriptor, Modi, IndentLevel, NumDigits(Modi),
-                     Callback);
+    SymbolGroup SG(&Input, Modi);
+    iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(Modi)), SG,
+                     Modi, Callback);
     return;
   }
 
-  uint32_t Count = Modules.getModuleCount();
-  uint32_t Digits = NumDigits(Count);
-  for (uint32_t I = 0; I < Count; ++I) {
-    auto Desc = Modules.getModuleDescriptor(I);
-    if (!shouldDumpModule(I, Desc))
-      continue;
-    iterateOneModule(File, P, Desc, I, IndentLevel, Digits, Callback);
+  uint32_t I = 0;
+
+  for (const auto &SG : Input.symbol_groups()) {
+    if (shouldDumpSymbolGroup(I, SG))
+      iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(I)), SG, I,
+                       Callback);
+
+    ++I;
   }
 }
 
 template <typename SubsectionT>
 static void iterateModuleSubsections(
-    PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
-    llvm::function_ref<void(uint32_t, StringsAndChecksumsPrinter &,
-                            SubsectionT &)>
+    InputFile &File, const Optional<PrintScope> &HeaderScope,
+    llvm::function_ref<void(uint32_t, const SymbolGroup &, SubsectionT &)>
         Callback) {
 
-  iterateModules(
-      File, P, IndentLevel,
-      [&File, &Callback](uint32_t Modi, StringsAndChecksumsPrinter &Strings) {
-        auto MDS = getModuleDebugStream(File, Modi);
-        if (!MDS) {
-          consumeError(MDS.takeError());
-          return;
-        }
-
-        for (const auto &SS : MDS->subsections()) {
-          SubsectionT Subsection;
-
-          if (SS.kind() != Subsection.kind())
-            continue;
-
-          BinaryStreamReader Reader(SS.getRecordData());
-          if (auto EC = Subsection.initialize(Reader))
-            continue;
-          Callback(Modi, Strings, Subsection);
-        }
-      });
+  iterateSymbolGroups(File, HeaderScope,
+                      [&](uint32_t Modi, const SymbolGroup &SG) {
+                        for (const auto &SS : SG.getDebugSubsections()) {
+                          SubsectionT Subsection;
+
+                          if (SS.kind() != Subsection.kind())
+                            continue;
+
+                          BinaryStreamReader Reader(SS.getRecordData());
+                          if (auto EC = Subsection.initialize(Reader))
+                            continue;
+                          Callback(Modi, SG, Subsection);
+                        }
+                      });
 }
 
 Error DumpOutputStyle::dumpModules() {
   printHeader(P, "Modules");
-
   AutoIndent Indent(P);
-  if (!File.hasPDBDbiStream()) {
+
+  if (File.isObj()) {
+    P.formatLine("Dumping modules is not supported for object files");
+    return Error::success();
+  }
+
+  if (!getPdb().hasPDBDbiStream()) {
     P.formatLine("DBI Stream not present");
     return Error::success();
   }
 
   ExitOnError Err("Unexpected error processing modules: ");
 
-  auto &Stream = Err(File.getPDBDbiStream());
+  auto &Stream = Err(getPdb().getPDBDbiStream());
 
   const DbiModuleList &Modules = Stream.modules();
-  iterateModules(
-      File, P, 11, [&](uint32_t Modi, StringsAndChecksumsPrinter &Strings) {
+  iterateSymbolGroups(
+      File, PrintScope{P, 11}, [&](uint32_t Modi, const SymbolGroup &Strings) {
         auto Desc = Modules.getModuleDescriptor(Modi);
         P.formatLine("Obj: `{0}`: ", Desc.getObjFileName());
         P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}",
@@ -549,18 +476,22 @@ Error DumpOutputStyle::dumpModules() {
 Error DumpOutputStyle::dumpModuleFiles() {
   printHeader(P, "Files");
 
+  if (File.isObj()) {
+    P.formatLine("Dumping files is not valid for object files");
+    return Error::success();
+  }
+
   ExitOnError Err("Unexpected error processing modules: ");
 
-  iterateModules(
-      File, P, 11,
-      [this, &Err](uint32_t Modi, StringsAndChecksumsPrinter &Strings) {
-        auto &Stream = Err(File.getPDBDbiStream());
-
-        const DbiModuleList &Modules = Stream.modules();
-        for (const auto &F : Modules.source_files(Modi)) {
-          Strings.formatFromFileName(P, F);
-        }
-      });
+  iterateSymbolGroups(File, PrintScope{P, 11},
+                      [this, &Err](uint32_t Modi, const SymbolGroup &Strings) {
+                        auto &Stream = Err(getPdb().getPDBDbiStream());
+
+                        const DbiModuleList &Modules = Stream.modules();
+                        for (const auto &F : Modules.source_files(Modi)) {
+                          Strings.formatFromFileName(P, F);
+                        }
+                      });
   return Error::success();
 }
 
@@ -571,37 +502,30 @@ Error DumpOutputStyle::dumpSymbolStats()
 
   StatCollection SymStats;
   StatCollection ChunkStats;
-  auto &Stream = Err(File.getPDBDbiStream());
-
-  const DbiModuleList &Modules = Stream.modules();
-  uint32_t ModCount = Modules.getModuleCount();
 
-  iterateModules(File, P, 0, [&](uint32_t Modi,
-                                 StringsAndChecksumsPrinter &Strings) {
-    DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi);
-    uint32_t StreamIdx = Desc.getModuleStreamIndex();
-
-    if (StreamIdx == kInvalidStreamIndex) {
-      P.formatLine("Mod {0} (debug info not present): [{1}]",
-                   fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)),
-                   Desc.getModuleName());
-      return;
-    }
+  iterateSymbolGroups(File, None, [&](uint32_t Modi, const SymbolGroup &SG) {
+    StatCollection SS = getSymbolStats(SG, SymStats);
+    StatCollection CS = getChunkStats(SG, ChunkStats);
+
+    if (SG.getFile().isPdb()) {
+      auto Modules = cantFail(File.pdb().getPDBDbiStream()).modules();
+      uint32_t ModCount = Modules.getModuleCount();
+      DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi);
+      uint32_t StreamIdx = Desc.getModuleStreamIndex();
+
+      if (StreamIdx == kInvalidStreamIndex) {
+        P.formatLine("Mod {0} (debug info not present): [{1}]",
+                     fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)),
+                     Desc.getModuleName());
+        return;
+      }
 
-    P.formatLine("Stream {0}, {1} bytes", StreamIdx,
-                 File.getStreamByteSize(StreamIdx));
+      P.formatLine("Stream {0}, {1} bytes", StreamIdx,
+                   getPdb().getStreamByteSize(StreamIdx));
 
-    ModuleDebugStreamRef MDS(Desc, File.createIndexedStream(StreamIdx));
-    if (auto EC = MDS.reload()) {
-      P.printLine("- Error parsing debug info stream");
-      consumeError(std::move(EC));
-      return;
+      printModuleDetailStats<SymbolKind>(P, "Symbols", SS);
+      printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS);
     }
-
-    printModuleDetailStats<SymbolKind>(P, "Symbols",
-                                       getSymbolStats(MDS, SymStats));
-    printModuleDetailStats<DebugSubsectionKind>(P, "Chunks",
-                                                getChunkStats(MDS, ChunkStats));
   });
 
   P.printLine("  Summary |");
@@ -661,58 +585,75 @@ Error DumpOutputStyle::dumpUdtStats() {
 
   StatCollection UdtStats;
   StatCollection UdtTargetStats;
-  if (!File.hasPDBGlobalsStream()) {
-    P.printLine("- Error: globals stream not present");
-    return Error::success();
-  }
-
   AutoIndent Indent(P, 4);
 
-  auto &SymbolRecords = cantFail(File.getPDBSymbolStream());
-  auto &Globals = cantFail(File.getPDBGlobalsStream());
-  auto &TpiTypes = cantFail(initializeTypes(StreamTPI));
+  auto &TpiTypes = File.types();
 
   StringMap<StatCollection::Stat> NamespacedStats;
 
-  P.NewLine();
-
   size_t LongestNamespace = 0;
-  for (uint32_t PubSymOff : Globals.getGlobalsTable()) {
-    CVSymbol Sym = SymbolRecords.readRecord(PubSymOff);
+  auto HandleOneSymbol = [&](const CVSymbol &Sym) {
     if (Sym.kind() != SymbolKind::S_UDT)
-      continue;
+      return;
     UdtStats.update(SymbolKind::S_UDT, Sym.length());
 
     UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym));
 
     uint32_t Kind = 0;
     uint32_t RecordSize = 0;
-    if (UDT.Type.isSimple() ||
-        (UDT.Type.toArrayIndex() >= TpiTypes.capacity())) {
-      if (UDT.Type.isNoneType())
-        Kind = kNoneUdtKind;
-      else if (UDT.Type.isSimple())
-        Kind = kSimpleUdtKind;
-      else
-        Kind = kUnknownUdtKind;
-    } else {
-      CVType T = TpiTypes.getType(UDT.Type);
-      Kind = T.kind();
-      RecordSize = T.length();
-    }
+
+    if (UDT.Type.isNoneType())
+      Kind = kNoneUdtKind;
+    else if (UDT.Type.isSimple())
+      Kind = kSimpleUdtKind;
+    else if (Optional<CVType> T = TpiTypes.tryGetType(UDT.Type)) {
+      Kind = T->kind();
+      RecordSize = T->length();
+    } else
+      Kind = kUnknownUdtKind;
 
     UdtTargetStats.update(Kind, RecordSize);
 
     size_t Pos = UDT.Name.find("::");
     if (Pos == StringRef::npos)
-      continue;
+      return;
 
     StringRef Scope = UDT.Name.take_front(Pos);
     if (Scope.empty() || !isValidNamespaceIdentifier(Scope))
-      continue;
+      return;
 
     LongestNamespace = std::max(LongestNamespace, Scope.size());
     NamespacedStats[Scope].update(RecordSize);
+  };
+
+  P.NewLine();
+
+  if (File.isPdb()) {
+    if (!getPdb().hasPDBGlobalsStream()) {
+      P.printLine("- Error: globals stream not present");
+      return Error::success();
+    }
+
+    auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream());
+    auto &Globals = cantFail(getPdb().getPDBGlobalsStream());
+
+    for (uint32_t PubSymOff : Globals.getGlobalsTable()) {
+      CVSymbol Sym = SymbolRecords.readRecord(PubSymOff);
+      HandleOneSymbol(Sym);
+    }
+  } else {
+    for (const auto &Sec : File.symbol_groups()) {
+      for (const auto &SS : Sec.getDebugSubsections()) {
+        if (SS.kind() != DebugSubsectionKind::Symbols)
+          continue;
+
+        DebugSymbolsSubsectionRef Symbols;
+        BinaryStreamReader Reader(SS.getRecordData());
+        cantFail(Symbols.initialize(Reader));
+        for (const auto &S : Symbols)
+          HandleOneSymbol(S);
+      }
+    }
   }
 
   LongestNamespace += StringRef(" namespace ''").size();
@@ -762,8 +703,8 @@ Error DumpOutputStyle::dumpUdtStats() {
   return Error::success();
 }
 
-static void typesetLinesAndColumns(PDBFile &File, LinePrinter &P,
-                                   uint32_t Start, const LineColumnEntry &E) {
+static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start,
+                                   const LineColumnEntry &E) {
   const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number
   uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5;
 
@@ -802,9 +743,9 @@ Error DumpOutputStyle::dumpLines() {
   uint32_t LastModi = UINT32_MAX;
   uint32_t LastNameIndex = UINT32_MAX;
   iterateModuleSubsections<DebugLinesSubsectionRef>(
-      File, P, 4,
+      File, PrintScope{P, 4},
       [this, &LastModi, &LastNameIndex](uint32_t Modi,
-                                        StringsAndChecksumsPrinter &Strings,
+                                        const SymbolGroup &Strings,
                                         DebugLinesSubsectionRef &Lines) {
         uint16_t Segment = Lines.header()->RelocSegment;
         uint32_t Begin = Lines.header()->RelocOffset;
@@ -825,7 +766,7 @@ Error DumpOutputStyle::dumpLines() {
             P.format("line/addr entries = {0}", Count);
 
           P.NewLine();
-          typesetLinesAndColumns(File, P, Begin, Block);
+          typesetLinesAndColumns(P, Begin, Block);
         }
       });
 
@@ -836,8 +777,8 @@ Error DumpOutputStyle::dumpInlineeLines(
   printHeader(P, "Inlinee Lines");
 
   iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(
-      File, P, 2,
-      [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings,
+      File, PrintScope{P, 2},
+      [this](uint32_t Modi, const SymbolGroup &Strings,
              DebugInlineeLinesSubsectionRef &Lines) {
         P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File");
         for (const auto &Entry : Lines) {
@@ -854,8 +795,8 @@ Error DumpOutputStyle::dumpInlineeLines(
 Error DumpOutputStyle::dumpXmi() {
   printHeader(P, "Cross Module Imports");
   iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>(
-      File, P, 2,
-      [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings,
+      File, PrintScope{P, 2},
+      [this](uint32_t Modi, const SymbolGroup &Strings,
              DebugCrossModuleImportsSubsectionRef &Imports) {
         P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs");
 
@@ -890,8 +831,8 @@ Error DumpOutputStyle::dumpXme() {
   printHeader(P, "Cross Module Exports");
 
   iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>(
-      File, P, 2,
-      [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings,
+      File, PrintScope{P, 2},
+      [this](uint32_t Modi, const SymbolGroup &Strings,
              DebugCrossModuleExportsSubsectionRef &Exports) {
         P.formatLine("{0,-10} | {1}", "Local ID", "Global ID");
         for (const auto &Export : Exports) {
@@ -906,8 +847,13 @@ Error DumpOutputStyle::dumpXme() {
 Error DumpOutputStyle::dumpStringTable() {
   printHeader(P, "String Table");
 
+  if (File.isObj()) {
+    P.formatLine("Dumping string table is not supported for object files");
+    return Error::success();
+  }
+
   AutoIndent Indent(P);
-  auto IS = File.getStringTable();
+  auto IS = getPdb().getStringTable();
   if (!IS) {
     P.formatLine("Not present in file");
     consumeError(IS.takeError());
@@ -1018,22 +964,32 @@ static void dumpPartialTypeStream(LinePr
 Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
   assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
 
+  if (StreamIdx == StreamTPI) {
+    printHeader(P, "Types (TPI Stream)");
+  } else if (StreamIdx == StreamIPI) {
+    printHeader(P, "Types (IPI Stream)");
+  }
+
+  AutoIndent Indent(P);
+  if (File.isObj()) {
+    P.formatLine("Dumping types is not supported for object files");
+    return Error::success();
+  }
+
   bool Present = false;
   bool DumpTypes = false;
   bool DumpBytes = false;
   bool DumpExtras = false;
   std::vector<uint32_t> Indices;
   if (StreamIdx == StreamTPI) {
-    printHeader(P, "Types (TPI Stream)");
-    Present = File.hasPDBTpiStream();
+    Present = getPdb().hasPDBTpiStream();
     DumpTypes = opts::dump::DumpTypes;
     DumpBytes = opts::dump::DumpTypeData;
     DumpExtras = opts::dump::DumpTypeExtras;
     Indices.assign(opts::dump::DumpTypeIndex.begin(),
                    opts::dump::DumpTypeIndex.end());
   } else if (StreamIdx == StreamIPI) {
-    printHeader(P, "Types (IPI Stream)");
-    Present = File.hasPDBIpiStream();
+    Present = getPdb().hasPDBIpiStream();
     DumpTypes = opts::dump::DumpIds;
     DumpBytes = opts::dump::DumpIdData;
     DumpExtras = opts::dump::DumpIdExtras;
@@ -1041,7 +997,6 @@ Error DumpOutputStyle::dumpTpiStream(uin
                    opts::dump::DumpIdIndex.end());
   }
 
-  AutoIndent Indent(P);
   if (!Present) {
     P.formatLine("Stream not present");
     return Error::success();
@@ -1049,10 +1004,10 @@ Error DumpOutputStyle::dumpTpiStream(uin
 
   ExitOnError Err("Unexpected error processing types: ");
 
-  auto &Stream = Err((StreamIdx == StreamTPI) ? File.getPDBTpiStream()
-                                              : File.getPDBIpiStream());
+  auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream()
+                                              : getPdb().getPDBIpiStream());
 
-  auto &Types = Err(initializeTypes(StreamIdx));
+  auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids();
 
   if (DumpTypes || !Indices.empty()) {
     if (Indices.empty())
@@ -1076,7 +1031,7 @@ Error DumpOutputStyle::dumpTpiStream(uin
     P.NewLine();
     P.formatLine("Hash Adjusters:");
     auto &Adjusters = Stream.getHashAdjusters();
-    auto &Strings = Err(File.getStringTable());
+    auto &Strings = Err(getPdb().getStringTable());
     for (const auto &A : Adjusters) {
       AutoIndent Indent2(P);
       auto ExpectedStr = Strings.getStringForID(A.first);
@@ -1092,42 +1047,60 @@ Error DumpOutputStyle::dumpTpiStream(uin
   return Error::success();
 }
 
-Expected<codeview::LazyRandomTypeCollection &>
-DumpOutputStyle::initializeTypes(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);
-  }
+Error DumpOutputStyle::dumpModuleSymsForObj() {
+  printHeader(P, "Symbols");
+
+  AutoIndent Indent(P);
+
+  ExitOnError Err("Unexpected error processing symbols: ");
+
+  auto &Types = File.types();
+
+  SymbolVisitorCallbackPipeline Pipeline;
+  SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile);
+  MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types);
+
+  Pipeline.addCallbackToPipeline(Deserializer);
+  Pipeline.addCallbackToPipeline(Dumper);
+  CVSymbolVisitor Visitor(Pipeline);
+
+  std::unique_ptr<llvm::Error> SymbolError;
+
+  iterateModuleSubsections<DebugSymbolsSubsectionRef>(
+      File, PrintScope{P, 2},
+      [&](uint32_t Modi, const SymbolGroup &Strings,
+          DebugSymbolsSubsectionRef &Symbols) {
+        for (auto Symbol : Symbols) {
+          if (auto EC = Visitor.visitSymbolRecord(Symbol)) {
+            SymbolError = llvm::make_unique<Error>(std::move(EC));
+            return;
+          }
+        }
+      });
 
-  return *TypeCollection;
+  if (SymbolError)
+    return std::move(*SymbolError);
+
+  return Error::success();
 }
 
-Error DumpOutputStyle::dumpModuleSyms() {
+Error DumpOutputStyle::dumpModuleSymsForPdb() {
   printHeader(P, "Symbols");
 
   AutoIndent Indent(P);
-  if (!File.hasPDBDbiStream()) {
+  if (!getPdb().hasPDBDbiStream()) {
     P.formatLine("DBI Stream not present");
     return Error::success();
   }
 
   ExitOnError Err("Unexpected error processing symbols: ");
 
-  auto &Ids = Err(initializeTypes(StreamIPI));
-  auto &Types = Err(initializeTypes(StreamTPI));
+  auto &Ids = File.ids();
+  auto &Types = File.types();
 
-  iterateModules(
-      File, P, 2, [&](uint32_t I, StringsAndChecksumsPrinter &Strings) {
-        auto ExpectedModS = getModuleDebugStream(File, I);
+  iterateSymbolGroups(
+      File, PrintScope{P, 2}, [&](uint32_t I, const SymbolGroup &Strings) {
+        auto ExpectedModS = getModuleDebugStream(File.pdb(), I);
         if (!ExpectedModS) {
           P.formatLine("Error loading module stream {0}.  {1}", I,
                        toString(ExpectedModS.takeError()));
@@ -1158,12 +1131,18 @@ Error DumpOutputStyle::dumpModuleSyms()
 Error DumpOutputStyle::dumpGlobals() {
   printHeader(P, "Global Symbols");
   AutoIndent Indent(P);
-  if (!File.hasPDBGlobalsStream()) {
+
+  if (File.isObj()) {
+    P.formatLine("Dumping Globals is not supported for object files");
+    return Error::success();
+  }
+
+  if (!getPdb().hasPDBGlobalsStream()) {
     P.formatLine("Globals stream not present");
     return Error::success();
   }
   ExitOnError Err("Error dumping globals stream: ");
-  auto &Globals = Err(File.getPDBGlobalsStream());
+  auto &Globals = Err(getPdb().getPDBGlobalsStream());
 
   const GSIHashTable &Table = Globals.getGlobalsTable();
   Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras));
@@ -1173,12 +1152,18 @@ Error DumpOutputStyle::dumpGlobals() {
 Error DumpOutputStyle::dumpPublics() {
   printHeader(P, "Public Symbols");
   AutoIndent Indent(P);
-  if (!File.hasPDBPublicsStream()) {
+
+  if (File.isObj()) {
+    P.formatLine("Dumping Globals is not supported for object files");
+    return Error::success();
+  }
+
+  if (!getPdb().hasPDBPublicsStream()) {
     P.formatLine("Publics stream not present");
     return Error::success();
   }
   ExitOnError Err("Error dumping publics stream: ");
-  auto &Publics = Err(File.getPDBPublicsStream());
+  auto &Publics = Err(getPdb().getPDBPublicsStream());
 
   const GSIHashTable &PublicsTable = Publics.getPublicsTable();
   if (opts::dump::DumpPublicExtras) {
@@ -1224,15 +1209,11 @@ Error DumpOutputStyle::dumpPublics() {
 
 Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table,
                                           bool HashExtras) {
-  auto ExpectedSyms = File.getPDBSymbolStream();
+  auto ExpectedSyms = getPdb().getPDBSymbolStream();
   if (!ExpectedSyms)
     return ExpectedSyms.takeError();
-  auto ExpectedTypes = initializeTypes(StreamTPI);
-  if (!ExpectedTypes)
-    return ExpectedTypes.takeError();
-  auto ExpectedIds = initializeTypes(StreamIPI);
-  if (!ExpectedIds)
-    return ExpectedIds.takeError();
+  auto &Types = File.types();
+  auto &Ids = File.ids();
 
   if (HashExtras) {
     P.printLine("GSI Header");
@@ -1246,8 +1227,7 @@ Error DumpOutputStyle::dumpSymbolsFromGS
     P.printLine("Records");
     SymbolVisitorCallbackPipeline Pipeline;
     SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
-    MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, *ExpectedIds,
-                               *ExpectedTypes);
+    MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
 
     Pipeline.addCallbackToPipeline(Deserializer);
     Pipeline.addCallbackToPipeline(Dumper);
@@ -1326,8 +1306,7 @@ loadSectionHeaders(PDBFile &File, DbgHea
         "PDB does not contain the requested image section header type",
         inconvertibleErrorCode());
 
-  auto Stream = MappedBlockStream::createIndexedStream(
-      File.getMsfLayout(), File.getMsfBuffer(), SI, File.getAllocator());
+  auto Stream = File.createIndexedStream(SI);
   if (!Stream)
     return make_error<StringError>("Could not load the required stream data",
                                    inconvertibleErrorCode());
@@ -1346,12 +1325,17 @@ loadSectionHeaders(PDBFile &File, DbgHea
 
 void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {
   printHeader(P, Label);
-  ExitOnError Err("Error dumping publics stream: ");
 
   AutoIndent Indent(P);
+  if (File.isObj()) {
+    P.formatLine("Dumping Section Headers is not supported for object files");
+    return;
+  }
+
+  ExitOnError Err("Error dumping section headers: ");
   std::unique_ptr<MappedBlockStream> Stream;
   ArrayRef<object::coff_section> Headers;
-  auto ExpectedHeaders = loadSectionHeaders(File, Type);
+  auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type);
   if (!ExpectedHeaders) {
     P.printLine(toString(ExpectedHeaders.takeError()));
     return;
@@ -1401,16 +1385,22 @@ std::vector<std::string> getSectionNames
 
 Error DumpOutputStyle::dumpSectionContribs() {
   printHeader(P, "Section Contributions");
-  ExitOnError Err("Error dumping publics stream: ");
 
   AutoIndent Indent(P);
-  if (!File.hasPDBDbiStream()) {
+  if (File.isObj()) {
+    P.formatLine(
+        "Dumping section contributions is not supported for object files");
+    return Error::success();
+  }
+
+  ExitOnError Err("Error dumping section contributions: ");
+  if (!getPdb().hasPDBDbiStream()) {
     P.formatLine(
         "Section contribs require a DBI Stream, which could not be loaded");
     return Error::success();
   }
 
-  auto &Dbi = Err(File.getPDBDbiStream());
+  auto &Dbi = Err(getPdb().getPDBDbiStream());
 
   class Visitor : public ISectionContribVisitor {
   public:
@@ -1456,7 +1446,7 @@ Error DumpOutputStyle::dumpSectionContri
     ArrayRef<std::string> Names;
   };
 
-  std::vector<std::string> Names = getSectionNames(File);
+  std::vector<std::string> Names = getSectionNames(getPdb());
   Visitor V(P, makeArrayRef(Names));
   Dbi.visitSectionContributions(V);
   return Error::success();
@@ -1464,16 +1454,22 @@ Error DumpOutputStyle::dumpSectionContri
 
 Error DumpOutputStyle::dumpSectionMap() {
   printHeader(P, "Section Map");
+  AutoIndent Indent(P);
+
+  if (File.isObj()) {
+    P.formatLine("Dumping section map is not supported for object files");
+    return Error::success();
+  }
+
   ExitOnError Err("Error dumping section map: ");
 
-  AutoIndent Indent(P);
-  if (!File.hasPDBDbiStream()) {
+  if (!getPdb().hasPDBDbiStream()) {
     P.formatLine("Dumping the section map requires a DBI Stream, which could "
                  "not be loaded");
     return Error::success();
   }
 
-  auto &Dbi = Err(File.getPDBDbiStream());
+  auto &Dbi = Err(getPdb().getPDBDbiStream());
 
   uint32_t I = 0;
   for (auto &M : Dbi.getSectionMap()) {

Modified: llvm/trunk/tools/llvm-pdbutil/DumpOutputStyle.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/DumpOutputStyle.h?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/DumpOutputStyle.h (original)
+++ llvm/trunk/tools/llvm-pdbutil/DumpOutputStyle.h Fri Sep  1 13:06:56 2017
@@ -28,8 +28,13 @@ namespace codeview {
 class LazyRandomTypeCollection;
 }
 
+namespace object {
+class COFFObjectFile;
+}
+
 namespace pdb {
 class GSIHashTable;
+class InputFile;
 
 struct StatCollection {
   struct Stat {
@@ -57,12 +62,13 @@ struct StatCollection {
 class DumpOutputStyle : public OutputStyle {
 
 public:
-  DumpOutputStyle(PDBFile &File);
+  DumpOutputStyle(InputFile &File);
 
   Error dump() override;
 
 private:
-  Expected<codeview::LazyRandomTypeCollection &> initializeTypes(uint32_t SN);
+  PDBFile &getPdb();
+  object::COFFObjectFile &getObj();
 
   Error dumpFileSummary();
   Error dumpStreamSummary();
@@ -76,7 +82,8 @@ private:
   Error dumpTpiStream(uint32_t StreamIdx);
   Error dumpModules();
   Error dumpModuleFiles();
-  Error dumpModuleSyms();
+  Error dumpModuleSymsForPdb();
+  Error dumpModuleSymsForObj();
   Error dumpGlobals();
   Error dumpPublics();
   Error dumpSymbolsFromGSI(const GSIHashTable &Table, bool HashExtras);
@@ -86,10 +93,8 @@ private:
 
   void dumpSectionHeaders(StringRef Label, DbgHeaderType Type);
 
-  PDBFile &File;
+  InputFile &File;
   LinePrinter P;
-  std::unique_ptr<codeview::LazyRandomTypeCollection> TpiTypes;
-  std::unique_ptr<codeview::LazyRandomTypeCollection> IpiTypes;
   SmallVector<StreamInfo, 32> StreamPurposes;
 };
 } // namespace pdb

Added: llvm/trunk/tools/llvm-pdbutil/InputFile.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/InputFile.cpp?rev=312358&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/InputFile.cpp (added)
+++ llvm/trunk/tools/llvm-pdbutil/InputFile.cpp Fri Sep  1 13:06:56 2017
@@ -0,0 +1,469 @@
+//===- InputFile.cpp ------------------------------------------ *- C++ --*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFile.h"
+
+#include "FormatUtil.h"
+#include "LinePrinter.h"
+
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
+#include "llvm/DebugInfo/PDB/Native/RawError.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::object;
+using namespace llvm::pdb;
+
+InputFile::InputFile() {}
+InputFile::~InputFile() {}
+
+static Expected<ModuleDebugStreamRef>
+getModuleDebugStream(PDBFile &File, StringRef &ModuleName, uint32_t Index) {
+  ExitOnError Err("Unexpected error: ");
+
+  auto &Dbi = Err(File.getPDBDbiStream());
+  const auto &Modules = Dbi.modules();
+  auto Modi = Modules.getModuleDescriptor(Index);
+
+  ModuleName = Modi.getModuleName();
+
+  uint16_t ModiStream = Modi.getModuleStreamIndex();
+  if (ModiStream == kInvalidStreamIndex)
+    return make_error<RawError>(raw_error_code::no_stream,
+                                "Module stream not present");
+
+  auto ModStreamData = File.createIndexedStream(ModiStream);
+
+  ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));
+  if (auto EC = ModS.reload())
+    return make_error<RawError>(raw_error_code::corrupt_file,
+                                "Invalid module stream");
+
+  return std::move(ModS);
+}
+
+static inline bool isCodeViewDebugSubsection(object::SectionRef Section,
+                                             StringRef Name,
+                                             BinaryStreamReader &Reader) {
+  StringRef SectionName, Contents;
+  if (auto EC = Section.getName(SectionName))
+    return false;
+
+  if (SectionName != Name)
+    return false;
+
+  if (auto EC = Section.getContents(Contents))
+    return false;
+
+  Reader = BinaryStreamReader(Contents, support::little);
+  uint32_t Magic;
+  if (Reader.bytesRemaining() < sizeof(uint32_t))
+    return false;
+  cantFail(Reader.readInteger(Magic));
+  if (Magic != COFF::DEBUG_SECTION_MAGIC)
+    return false;
+  return true;
+}
+
+static inline bool isDebugSSection(object::SectionRef Section,
+                                   DebugSubsectionArray &Subsections) {
+  BinaryStreamReader Reader;
+  if (!isCodeViewDebugSubsection(Section, ".debug$S", Reader))
+    return false;
+
+  cantFail(Reader.readArray(Subsections, Reader.bytesRemaining()));
+  return true;
+}
+
+static bool isDebugTSection(SectionRef Section, CVTypeArray &Types) {
+  BinaryStreamReader Reader;
+  if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader))
+    return false;
+  cantFail(Reader.readArray(Types, Reader.bytesRemaining()));
+  return true;
+}
+
+static std::string formatChecksumKind(FileChecksumKind Kind) {
+  switch (Kind) {
+    RETURN_CASE(FileChecksumKind, None, "None");
+    RETURN_CASE(FileChecksumKind, MD5, "MD5");
+    RETURN_CASE(FileChecksumKind, SHA1, "SHA-1");
+    RETURN_CASE(FileChecksumKind, SHA256, "SHA-256");
+  }
+  return formatUnknownEnum(Kind);
+}
+
+static const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) {
+  return cantFail(File.getStringTable()).getStringTable();
+}
+
+template <typename... Args>
+static void formatInternal(LinePrinter &Printer, bool Append, Args &&... args) {
+  if (Append)
+    Printer.format(std::forward<Args>(args)...);
+  else
+    Printer.formatLine(std::forward<Args>(args)...);
+}
+
+SymbolGroup::SymbolGroup(InputFile *File, uint32_t GroupIndex) : File(File) {
+  if (!File)
+    return;
+
+  if (File->isPdb())
+    initializeForPdb(GroupIndex);
+  else {
+    Name = ".debug$S";
+    uint32_t I = 0;
+    for (const auto &S : File->obj().sections()) {
+      DebugSubsectionArray SS;
+      if (!isDebugSSection(S, SS))
+        continue;
+
+      if (!SC.hasChecksums() || !SC.hasStrings())
+        SC.initialize(SS);
+
+      if (I == GroupIndex)
+        Subsections = SS;
+
+      if (SC.hasChecksums() && SC.hasStrings())
+        break;
+    }
+    rebuildChecksumMap();
+  }
+}
+
+StringRef SymbolGroup::name() const { return Name; }
+
+void SymbolGroup::updateDebugS(const codeview::DebugSubsectionArray &SS) {
+  Subsections = SS;
+}
+
+void SymbolGroup::updatePdbModi(uint32_t Modi) { initializeForPdb(Modi); }
+
+void SymbolGroup::initializeForPdb(uint32_t Modi) {
+  assert(File && File->isPdb());
+
+  // PDB always uses the same string table, but each module has its own
+  // checksums.  So we only set the strings if they're not already set.
+  if (!SC.hasStrings())
+    SC.setStrings(extractStringTable(File->pdb()));
+
+  SC.resetChecksums();
+  auto MDS = getModuleDebugStream(File->pdb(), Name, Modi);
+  if (!MDS) {
+    consumeError(MDS.takeError());
+    return;
+  }
+
+  DebugStream = std::make_shared<ModuleDebugStreamRef>(std::move(*MDS));
+  Subsections = DebugStream->getSubsectionsArray();
+  SC.initialize(Subsections);
+  rebuildChecksumMap();
+}
+
+void SymbolGroup::rebuildChecksumMap() {
+  if (!SC.hasChecksums())
+    return;
+
+  for (const auto &Entry : SC.checksums()) {
+    auto S = SC.strings().getString(Entry.FileNameOffset);
+    if (!S)
+      continue;
+    ChecksumsByFile[*S] = Entry;
+  }
+}
+
+const ModuleDebugStreamRef &SymbolGroup::getPdbModuleStream() const {
+  assert(File && File->isPdb() && DebugStream);
+  return *DebugStream;
+}
+
+Expected<StringRef> SymbolGroup::getNameFromStringTable(uint32_t Offset) const {
+  return SC.strings().getString(Offset);
+}
+
+void SymbolGroup::formatFromFileName(LinePrinter &Printer, StringRef File,
+                                     bool Append) const {
+  auto FC = ChecksumsByFile.find(File);
+  if (FC == ChecksumsByFile.end()) {
+    formatInternal(Printer, Append, "- (no checksum) {0}", File);
+    return;
+  }
+
+  formatInternal(Printer, Append, "- ({0}: {1}) {2}",
+                 formatChecksumKind(FC->getValue().Kind),
+                 toHex(FC->getValue().Checksum), File);
+}
+
+void SymbolGroup::formatFromChecksumsOffset(LinePrinter &Printer,
+                                            uint32_t Offset,
+                                            bool Append) const {
+  if (!SC.hasChecksums()) {
+    formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
+    return;
+  }
+
+  auto Iter = SC.checksums().getArray().at(Offset);
+  if (Iter == SC.checksums().getArray().end()) {
+    formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
+    return;
+  }
+
+  uint32_t FO = Iter->FileNameOffset;
+  auto ExpectedFile = getNameFromStringTable(FO);
+  if (!ExpectedFile) {
+    formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
+    consumeError(ExpectedFile.takeError());
+    return;
+  }
+  if (Iter->Kind == FileChecksumKind::None) {
+    formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile);
+  } else {
+    formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile,
+                   formatChecksumKind(Iter->Kind), toHex(Iter->Checksum));
+  }
+}
+
+Expected<InputFile> InputFile::open(StringRef Path) {
+  InputFile IF;
+  if (!llvm::sys::fs::exists(Path))
+    return make_error<StringError>(formatv("File {0} not found", Path),
+                                   inconvertibleErrorCode());
+
+  file_magic Magic;
+  if (auto EC = identify_magic(Path, Magic))
+    return make_error<StringError>(
+        formatv("Unable to identify file type for file {0}", Path), EC);
+
+  if (Magic == file_magic::coff_object) {
+    Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Path);
+    if (!BinaryOrErr)
+      return BinaryOrErr.takeError();
+
+    IF.CoffObject = std::move(*BinaryOrErr);
+    IF.PdbOrObj = llvm::cast<COFFObjectFile>(IF.CoffObject.getBinary());
+    return std::move(IF);
+  }
+
+  if (Magic == file_magic::unknown) {
+    std::unique_ptr<IPDBSession> Session;
+    if (auto Err = loadDataForPDB(PDB_ReaderType::Native, Path, Session))
+      return std::move(Err);
+
+    IF.PdbSession.reset(static_cast<NativeSession *>(Session.release()));
+    IF.PdbOrObj = &IF.PdbSession->getPDBFile();
+
+    return std::move(IF);
+  }
+
+  return make_error<StringError>(
+      formatv("File {0} is not a supported file type", Path),
+      inconvertibleErrorCode());
+}
+
+PDBFile &InputFile::pdb() {
+  assert(isPdb());
+  return *PdbOrObj.get<PDBFile *>();
+}
+
+const PDBFile &InputFile::pdb() const {
+  assert(isPdb());
+  return *PdbOrObj.get<PDBFile *>();
+}
+
+object::COFFObjectFile &InputFile::obj() {
+  assert(isObj());
+  return *PdbOrObj.get<object::COFFObjectFile *>();
+}
+
+const object::COFFObjectFile &InputFile::obj() const {
+  assert(isObj());
+  return *PdbOrObj.get<object::COFFObjectFile *>();
+}
+
+bool InputFile::hasTypes() const {
+  if (isPdb())
+    return pdb().hasPDBTpiStream();
+
+  for (const auto &Section : obj().sections()) {
+    CVTypeArray Types;
+    if (isDebugTSection(Section, Types))
+      return true;
+  }
+  return false;
+}
+
+bool InputFile::hasIds() const {
+  if (isObj())
+    return false;
+  return pdb().hasPDBIpiStream();
+}
+
+bool InputFile::isPdb() const { return PdbOrObj.is<PDBFile *>(); }
+
+bool InputFile::isObj() const {
+  return PdbOrObj.is<object::COFFObjectFile *>();
+}
+
+codeview::LazyRandomTypeCollection &
+InputFile::getOrCreateTypeCollection(TypeCollectionKind Kind) {
+  if (Types && Kind == kTypes)
+    return *Types;
+  if (Ids && Kind == kIds)
+    return *Ids;
+
+  if (Kind == kIds) {
+    assert(isPdb() && pdb().hasPDBIpiStream());
+  }
+
+  // If the collection was already initialized, we should have just returned it
+  // in step 1.
+  if (isPdb()) {
+    TypeCollectionPtr &Collection = (Kind == kIds) ? Ids : Types;
+    auto &Stream = cantFail((Kind == kIds) ? pdb().getPDBIpiStream()
+                                           : pdb().getPDBTpiStream());
+
+    auto &Array = Stream.typeArray();
+    uint32_t Count = Stream.getNumTypeRecords();
+    auto Offsets = Stream.getTypeIndexOffsets();
+    Collection =
+        llvm::make_unique<LazyRandomTypeCollection>(Array, Count, Offsets);
+    return *Collection;
+  }
+
+  assert(isObj());
+  assert(Kind == kTypes);
+  assert(!Types);
+
+  for (const auto &Section : obj().sections()) {
+    CVTypeArray Records;
+    if (!isDebugTSection(Section, Records))
+      continue;
+
+    Types = llvm::make_unique<LazyRandomTypeCollection>(Records, 100);
+    return *Types;
+  }
+
+  Types = llvm::make_unique<LazyRandomTypeCollection>(100);
+  return *Types;
+}
+
+codeview::LazyRandomTypeCollection &InputFile::types() {
+  return getOrCreateTypeCollection(kTypes);
+}
+
+codeview::LazyRandomTypeCollection &InputFile::ids() {
+  // Object files have only one type stream that contains both types and ids.
+  // Similarly, some PDBs don't contain an IPI stream, and for those both types
+  // and IDs are in the same stream.
+  if (isObj() || !pdb().hasPDBIpiStream())
+    return types();
+
+  return getOrCreateTypeCollection(kIds);
+}
+
+iterator_range<SymbolGroupIterator> InputFile::symbol_groups() {
+  return make_range<SymbolGroupIterator>(symbol_groups_begin(),
+                                         symbol_groups_end());
+}
+
+SymbolGroupIterator InputFile::symbol_groups_begin() {
+  return SymbolGroupIterator(*this);
+}
+
+SymbolGroupIterator InputFile::symbol_groups_end() {
+  return SymbolGroupIterator();
+}
+
+SymbolGroupIterator::SymbolGroupIterator() : Value(nullptr) {}
+
+SymbolGroupIterator::SymbolGroupIterator(InputFile &File) : Value(&File) {
+  if (File.isObj()) {
+    SectionIter = File.obj().section_begin();
+    scanToNextDebugS();
+  }
+}
+
+bool SymbolGroupIterator::operator==(const SymbolGroupIterator &R) const {
+  bool E = isEnd();
+  bool RE = R.isEnd();
+  if (E || RE)
+    return E == RE;
+
+  if (Value.File != R.Value.File)
+    return false;
+  return Index == R.Index;
+}
+
+const SymbolGroup &SymbolGroupIterator::operator*() const {
+  assert(!isEnd());
+  return Value;
+}
+SymbolGroup &SymbolGroupIterator::operator*() {
+  assert(!isEnd());
+  return Value;
+}
+
+SymbolGroupIterator &SymbolGroupIterator::operator++() {
+  assert(Value.File && !isEnd());
+  ++Index;
+  if (isEnd())
+    return *this;
+
+  if (Value.File->isPdb()) {
+    Value.updatePdbModi(Index);
+    return *this;
+  }
+
+  scanToNextDebugS();
+  return *this;
+}
+
+void SymbolGroupIterator::scanToNextDebugS() {
+  assert(SectionIter.hasValue());
+  auto End = Value.File->obj().section_end();
+  auto &Iter = *SectionIter;
+  assert(!isEnd());
+
+  while (++Iter != End) {
+    DebugSubsectionArray SS;
+    SectionRef SR = *Iter;
+    if (!isDebugSSection(SR, SS))
+      continue;
+
+    Value.updateDebugS(SS);
+    return;
+  }
+}
+
+bool SymbolGroupIterator::isEnd() const {
+  if (!Value.File)
+    return true;
+  if (Value.File->isPdb()) {
+    auto &Dbi = cantFail(Value.File->pdb().getPDBDbiStream());
+    uint32_t Count = Dbi.modules().getModuleCount();
+    assert(Index <= Count);
+    return Index == Count;
+  }
+
+  assert(SectionIter.hasValue());
+  return *SectionIter == Value.File->obj().section_end();
+}

Added: llvm/trunk/tools/llvm-pdbutil/InputFile.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/InputFile.h?rev=312358&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/InputFile.h (added)
+++ llvm/trunk/tools/llvm-pdbutil/InputFile.h Fri Sep  1 13:06:56 2017
@@ -0,0 +1,147 @@
+//===- InputFile.h -------------------------------------------- *- C++ --*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_INPUTFILE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_INPUTFILE_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/iterator.h"
+#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
+#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
+#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace codeview {
+class LazyRandomTypeCollection;
+}
+namespace object {
+class COFFObjectFile;
+class SectionRef;
+} // namespace object
+
+namespace pdb {
+class InputFile;
+class LinePrinter;
+class PDBFile;
+class NativeSession;
+class SymbolGroupIterator;
+class SymbolGroup;
+
+class InputFile {
+  InputFile();
+
+  std::unique_ptr<NativeSession> PdbSession;
+  object::OwningBinary<object::Binary> CoffObject;
+  PointerUnion<PDBFile *, object::COFFObjectFile *> PdbOrObj;
+
+  using TypeCollectionPtr = std::unique_ptr<codeview::LazyRandomTypeCollection>;
+
+  TypeCollectionPtr Types;
+  TypeCollectionPtr Ids;
+
+  enum TypeCollectionKind { kTypes, kIds };
+  codeview::LazyRandomTypeCollection &
+  getOrCreateTypeCollection(TypeCollectionKind Kind);
+
+public:
+  ~InputFile();
+  InputFile(InputFile &&Other) = default;
+
+  static Expected<InputFile> open(StringRef Path);
+
+  PDBFile &pdb();
+  const PDBFile &pdb() const;
+  object::COFFObjectFile &obj();
+  const object::COFFObjectFile &obj() const;
+
+  bool hasTypes() const;
+  bool hasIds() const;
+
+  codeview::LazyRandomTypeCollection &types();
+  codeview::LazyRandomTypeCollection &ids();
+
+  iterator_range<SymbolGroupIterator> symbol_groups();
+  SymbolGroupIterator symbol_groups_begin();
+  SymbolGroupIterator symbol_groups_end();
+
+  bool isPdb() const;
+  bool isObj() const;
+};
+
+class SymbolGroup {
+  friend class SymbolGroupIterator;
+
+public:
+  explicit SymbolGroup(InputFile *File, uint32_t GroupIndex = 0);
+
+  Expected<StringRef> getNameFromStringTable(uint32_t Offset) const;
+
+  void formatFromFileName(LinePrinter &Printer, StringRef File,
+                          bool Append = false) const;
+
+  void formatFromChecksumsOffset(LinePrinter &Printer, uint32_t Offset,
+                                 bool Append = false) const;
+
+  StringRef name() const;
+
+  codeview::DebugSubsectionArray getDebugSubsections() const {
+    return Subsections;
+  }
+  const ModuleDebugStreamRef &getPdbModuleStream() const;
+
+  const InputFile &getFile() const { return *File; }
+  InputFile &getFile() { return *File; }
+
+private:
+  void initializeForPdb(uint32_t Modi);
+  void updatePdbModi(uint32_t Modi);
+  void updateDebugS(const codeview::DebugSubsectionArray &SS);
+
+  void rebuildChecksumMap();
+  InputFile *File = nullptr;
+  StringRef Name;
+  codeview::DebugSubsectionArray Subsections;
+  std::shared_ptr<ModuleDebugStreamRef> DebugStream;
+  codeview::StringsAndChecksumsRef SC;
+  StringMap<codeview::FileChecksumEntry> ChecksumsByFile;
+};
+
+class SymbolGroupIterator
+    : public iterator_facade_base<SymbolGroupIterator,
+                                  std::forward_iterator_tag, SymbolGroup> {
+public:
+  SymbolGroupIterator();
+  explicit SymbolGroupIterator(InputFile &File);
+  SymbolGroupIterator(const SymbolGroupIterator &Other) = default;
+  SymbolGroupIterator &operator=(const SymbolGroupIterator &R) = default;
+
+  const SymbolGroup &operator*() const;
+  SymbolGroup &operator*();
+
+  bool operator==(const SymbolGroupIterator &R) const;
+  SymbolGroupIterator &operator++();
+
+private:
+  void scanToNextDebugS();
+  bool isEnd() const;
+
+  uint32_t Index = 0;
+  Optional<object::section_iterator> SectionIter;
+  SymbolGroup Value;
+};
+
+} // namespace pdb
+} // namespace llvm
+
+#endif

Modified: llvm/trunk/tools/llvm-pdbutil/LinePrinter.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/LinePrinter.h?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/LinePrinter.h (original)
+++ llvm/trunk/tools/llvm-pdbutil/LinePrinter.h Fri Sep  1 13:06:56 2017
@@ -93,14 +93,41 @@ private:
   std::list<Regex> IncludeSymbolFilters;
 };
 
+struct PrintScope {
+  explicit PrintScope(LinePrinter &P, uint32_t IndentLevel)
+      : P(P), IndentLevel(IndentLevel) {}
+  explicit PrintScope(const PrintScope &Other, uint32_t LabelWidth)
+      : P(Other.P), IndentLevel(Other.IndentLevel), LabelWidth(LabelWidth) {}
+
+  LinePrinter &P;
+  uint32_t IndentLevel;
+  uint32_t LabelWidth = 0;
+};
+
+inline Optional<PrintScope> withLabelWidth(const Optional<PrintScope> &Scope,
+                                           uint32_t W) {
+  if (!Scope)
+    return None;
+  return PrintScope{*Scope, W};
+}
+
 struct AutoIndent {
   explicit AutoIndent(LinePrinter &L, uint32_t Amount = 0)
-      : L(L), Amount(Amount) {
+      : L(&L), Amount(Amount) {
     L.Indent(Amount);
   }
-  ~AutoIndent() { L.Unindent(Amount); }
+  explicit AutoIndent(const Optional<PrintScope> &Scope) {
+    if (Scope.hasValue()) {
+      L = &Scope->P;
+      Amount = Scope->IndentLevel;
+    }
+  }
+  ~AutoIndent() {
+    if (L)
+      L->Unindent(Amount);
+  }
 
-  LinePrinter &L;
+  LinePrinter *L = nullptr;
   uint32_t Amount = 0;
 };
 

Modified: llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp (original)
+++ llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp Fri Sep  1 13:06:56 2017
@@ -17,6 +17,7 @@
 #include "BytesOutputStyle.h"
 #include "Diff.h"
 #include "DumpOutputStyle.h"
+#include "InputFile.h"
 #include "LinePrinter.h"
 #include "OutputStyle.h"
 #include "PrettyCompilandDumper.h"
@@ -32,6 +33,7 @@
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/BinaryFormat/Magic.h"
 #include "llvm/Config/config.h"
 #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
 #include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h"
@@ -761,11 +763,10 @@ static void pdb2Yaml(StringRef Path) {
 }
 
 static void dumpRaw(StringRef Path) {
-  std::unique_ptr<IPDBSession> Session;
-  auto &File = loadPDB(Path, Session);
 
-  auto O = llvm::make_unique<DumpOutputStyle>(File);
+  InputFile IF = ExitOnErr(InputFile::open(Path));
 
+  auto O = llvm::make_unique<DumpOutputStyle>(IF);
   ExitOnErr(O->dump());
 }
 

Modified: llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h?rev=312358&r1=312357&r2=312358&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h (original)
+++ llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h Fri Sep  1 13:06:56 2017
@@ -12,6 +12,7 @@
 
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerUnion.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -19,11 +20,17 @@
 #include <stdint.h>
 
 namespace llvm {
+namespace object {
+class COFFObjectFile;
+}
 namespace pdb {
 class PDBSymbolData;
 class PDBSymbolFunc;
+class PDBFile;
 uint32_t getTypeLength(const PDBSymbolData &Symbol);
 }
+typedef llvm::PointerUnion<object::COFFObjectFile *, pdb::PDBFile *>
+    PdbOrCoffObj;
 }
 
 namespace opts {




More information about the llvm-commits mailing list