]> granicus.if.org Git - llvm/commitdiff
[llvm-pdbdump] Add the ability to merge PDBs.
authorZachary Turner <zturner@google.com>
Thu, 18 May 2017 23:03:41 +0000 (23:03 +0000)
committerZachary Turner <zturner@google.com>
Thu, 18 May 2017 23:03:41 +0000 (23:03 +0000)
Merging PDBs is a feature that will be used heavily by
the linker.  The functionality already exists but does not
have deep test coverage because it's not easily exposed through
any tools.  This patch aims to address that by adding the
ability to merge PDBs via llvm-pdbdump.  It takes arbitrarily
many PDBs and outputs a single PDB.

Using this new functionality, a test is added for merging
type records.  Future patches will add the ability to merge
symbol records, module information, etc.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@303389 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h
lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp
test/DebugInfo/PDB/Inputs/merge1.yaml [new file with mode: 0644]
test/DebugInfo/PDB/Inputs/merge2.yaml [new file with mode: 0644]
test/DebugInfo/PDB/pdbdump-mergetypes.test [new file with mode: 0644]
tools/llvm-pdbdump/llvm-pdbdump.cpp

index 6c609c34665cade9d102ed216f9a7b45e7d7a81d..21cfa83e6af4efa25b7253f08fe0413f045395fa 100644 (file)
@@ -72,7 +72,7 @@ private:
 
   size_t TypeRecordBytes = 0;
 
-  Optional<PdbRaw_TpiVer> VerHeader;
+  PdbRaw_TpiVer VerHeader = PdbRaw_TpiVer::PdbTpiV80;
   std::vector<ArrayRef<uint8_t>> TypeRecords;
   std::vector<uint32_t> TypeHashes;
   std::vector<codeview::TypeIndexOffset> TypeIndexOffsets;
index 701a318511b8fe1d13d5e79f66b9f5a7f2e56132..20456cc978234d41875ebafc05ba6f96bd3fbef4 100644 (file)
@@ -69,7 +69,7 @@ Error TpiStreamBuilder::finalize() {
 
   uint32_t Count = TypeRecords.size();
 
-  H->Version = *VerHeader;
+  H->Version = VerHeader;
   H->HeaderSize = sizeof(TpiStreamHeader);
   H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex;
   H->TypeIndexEnd = H->TypeIndexBegin + Count;
diff --git a/test/DebugInfo/PDB/Inputs/merge1.yaml b/test/DebugInfo/PDB/Inputs/merge1.yaml
new file mode 100644 (file)
index 0000000..89d471e
--- /dev/null
@@ -0,0 +1,52 @@
+---\r
+TpiStream:\r
+  Records:\r
+    # uint32_t* [Index: 0x1000]\r
+    - Kind:            LF_POINTER\r
+      Pointer:         \r
+        ReferentType:    117\r
+        Attrs:           32778\r
+    # int64_t* [Index: 0x1001]\r
+    - Kind:            LF_POINTER\r
+      Pointer:         \r
+        ReferentType:    118\r
+        Attrs:           32778\r
+    # struct OnlyInMerge1 [Index: 0x1002]\r
+    - Kind:            LF_STRUCTURE\r
+      Class:           \r
+        MemberCount:     0\r
+        Options:         [ None, ForwardReference, HasUniqueName ]\r
+        FieldList:       0\r
+        Name:            'OnlyInMerge1'\r
+        UniqueName:      'OnlyInMerge1'\r
+        DerivationList:  0\r
+        VTableShape:     0\r
+        Size:            0\r
+    # uint32_t** [Index: 0x1003]\r
+    - Kind:            LF_POINTER\r
+      Pointer:         \r
+        ReferentType:    4096\r
+        Attrs:           32778\r
+    # uint32_t*** [Index: 0x1004]\r
+    - Kind:            LF_POINTER\r
+      Pointer:         \r
+        ReferentType:    4099\r
+        Attrs:           32778\r
+    # int64_t* [Index: 0x1005]\r
+    - Kind:            LF_POINTER\r
+      Pointer:         \r
+        ReferentType:    4097\r
+        Attrs:           32778\r
+    # [uint32_t, uint32_t*, uint32_t**] [Index: 0x1006]\r
+    - Kind:            LF_ARGLIST\r
+      ArgList:         \r
+        ArgIndices:      [ 117, 4096, 4099 ]\r
+    # uint32_t (uint32_t, uint32_t*, uint32_t**) [Index: 0x1007]\r
+    - Kind:            LF_PROCEDURE\r
+      Procedure:       \r
+        ReturnType:      117\r
+        CallConv:        NearC\r
+        Options:         [ None ]\r
+        ParameterCount:  0\r
+        ArgumentList:    4102\r
+...\r
diff --git a/test/DebugInfo/PDB/Inputs/merge2.yaml b/test/DebugInfo/PDB/Inputs/merge2.yaml
new file mode 100644 (file)
index 0000000..b6cbdb9
--- /dev/null
@@ -0,0 +1,52 @@
+---\r
+TpiStream:\r
+  Records:         \r
+    # uint32_t* [Index: 0x1000]\r
+    - Kind:            LF_POINTER\r
+      Pointer:         \r
+        ReferentType:    117     \r
+        Attrs:           32778\r
+    # uint32_t** [Index: 0x1001]\r
+    - Kind:            LF_POINTER\r
+      Pointer:         \r
+        ReferentType:    4096    \r
+        Attrs:           32778\r
+    # uint32_t*** [Index: 0x1002]\r
+    - Kind:            LF_POINTER\r
+      Pointer:         \r
+        ReferentType:    4097    \r
+        Attrs:           32778\r
+    # [uint32_t, uint32_t*, uint32_t**] [Index: 0x1003]\r
+    - Kind:            LF_ARGLIST\r
+      ArgList:         \r
+        ArgIndices:      [ 117, 4096, 4097 ]\r
+    # uint32_t (uint32_t, uint32_t*, uint32_t**) [Index: 0x1004]\r
+    - Kind:            LF_PROCEDURE\r
+      Procedure:       \r
+        ReturnType:      117\r
+        CallConv:        NearC\r
+        Options:         [ None ]\r
+        ParameterCount:  0\r
+        ArgumentList:    4099\r
+    # int64_t* [Index: 0x1005]\r
+    - Kind:            LF_POINTER\r
+      Pointer:         \r
+        ReferentType:    118     \r
+        Attrs:           32778\r
+    # int64_t** [Index: 0x1006]\r
+    - Kind:            LF_POINTER\r
+      Pointer:         \r
+        ReferentType:    4101\r
+        Attrs:           32778\r
+    # struct OnlyInMerge2 [Index: 0x1007]\r
+    - Kind:            LF_STRUCTURE\r
+      Class:           \r
+        MemberCount:     0\r
+        Options:         [ None, ForwardReference, HasUniqueName ]\r
+        FieldList:       0\r
+        Name:            'OnlyInMerge2'\r
+        UniqueName:      'OnlyInMerge2'\r
+        DerivationList:  0\r
+        VTableShape:     0\r
+        Size:            0\r
+...\r
diff --git a/test/DebugInfo/PDB/pdbdump-mergetypes.test b/test/DebugInfo/PDB/pdbdump-mergetypes.test
new file mode 100644 (file)
index 0000000..96f6316
--- /dev/null
@@ -0,0 +1,24 @@
+; RUN: llvm-pdbdump yaml2pdb -pdb=%t.1.pdb %p/Inputs/merge1.yaml\r
+; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2.pdb %p/Inputs/merge2.yaml\r
+; RUN: llvm-pdbdump merge -pdb=%t.3.pdb %t.1.pdb %t.2.pdb\r
+; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=MERGED %s\r
+; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=ARGLIST %s\r
+\r
+\r
+MERGED: Type Info Stream (TPI)\r
+MERGED: Record count: 9\r
+MERGED-DAG: PointeeType: unsigned\r
+MERGED-DAG: PointeeType: unsigned*\r
+MERGED-DAG: PointeeType: unsigned**\r
+MERGED-DAG: PointeeType: __int64\r
+MERGED-DAG: PointeeType: __int64*\r
+MERGED-DAG: Name: OnlyInMerge1\r
+MERGED-DAG: Name: OnlyInMerge2\r
+MERGED-DAG: TypeLeafKind: LF_ARGLIST\r
+\r
+ARGLIST: TypeLeafKind: LF_ARGLIST\r
+ARGLIST-NEXT: NumArgs: 3\r
+ARGLIST-NEXT: Arguments [\r
+ARGLIST-NEXT: ArgType: unsigned\r
+ARGLIST-NEXT: ArgType: unsigned*\r
+ARGLIST-NEXT: ArgType: unsigned**\r
index dc3e15a89af504ab7fdc1f9ec6a28e6364fb1d50..d3c6a799ac4222eb601271cb0de943e78e7c766c 100644 (file)
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Config/config.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
 #include "llvm/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.h"
 #include "llvm/DebugInfo/CodeView/ModuleDebugInlineeLinesFragment.h"
 #include "llvm/DebugInfo/CodeView/ModuleDebugLineFragment.h"
+#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
 #include "llvm/DebugInfo/MSF/MSFBuilder.h"
 #include "llvm/DebugInfo/PDB/GenericError.h"
 #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
@@ -100,6 +102,9 @@ cl::SubCommand
     AnalyzeSubcommand("analyze",
                       "Analyze various aspects of a PDB's structure");
 
+cl::SubCommand MergeSubcommand("merge",
+                               "Merge multiple PDBs into a single PDB");
+
 cl::OptionCategory TypeCategory("Symbol Type Options");
 cl::OptionCategory FilterCategory("Filtering and Sorting Options");
 cl::OptionCategory OtherOptions("Other Options");
@@ -441,6 +446,15 @@ cl::list<std::string> InputFilename(cl::Positional,
                                     cl::desc("<input PDB file>"), cl::Required,
                                     cl::sub(AnalyzeSubcommand));
 }
+
+namespace merge {
+cl::list<std::string> InputFilenames(cl::Positional,
+                                     cl::desc("<input PDB files>"),
+                                     cl::OneOrMore, cl::sub(MergeSubcommand));
+cl::opt<std::string>
+    PdbOutputFile("pdb", cl::desc("the name of the PDB file to write"),
+                  cl::sub(MergeSubcommand));
+}
 }
 
 static ExitOnError ExitOnErr;
@@ -828,6 +842,54 @@ static void dumpPretty(StringRef Path) {
   outs().flush();
 }
 
+static void mergePdbs() {
+  BumpPtrAllocator Allocator;
+  TypeTableBuilder MergedTpi(Allocator);
+  TypeTableBuilder MergedIpi(Allocator);
+
+  // Create a Tpi and Ipi type table with all types from all input files.
+  for (const auto &Path : opts::merge::InputFilenames) {
+    std::unique_ptr<IPDBSession> Session;
+    auto &File = loadPDB(Path, Session);
+    if (File.hasPDBTpiStream()) {
+      auto &Tpi = ExitOnErr(File.getPDBTpiStream());
+      ExitOnErr(codeview::mergeTypeStreams(MergedIpi, MergedTpi, nullptr,
+                                           Tpi.typeArray()));
+    }
+    if (File.hasPDBIpiStream()) {
+      auto &Ipi = ExitOnErr(File.getPDBIpiStream());
+      ExitOnErr(codeview::mergeTypeStreams(MergedIpi, MergedTpi, nullptr,
+                                           Ipi.typeArray()));
+    }
+  }
+
+  // Then write the PDB.
+  PDBFileBuilder Builder(Allocator);
+  ExitOnErr(Builder.initialize(4096));
+  // Add each of the reserved streams.  We might not put any data in them,
+  // but at least they have to be present.
+  for (uint32_t I = 0; I < kSpecialStreamCount; ++I)
+    ExitOnErr(Builder.getMsfBuilder().addStream(0));
+
+  auto &DestTpi = Builder.getTpiBuilder();
+  auto &DestIpi = Builder.getIpiBuilder();
+  MergedTpi.ForEachRecord(
+      [&DestTpi](TypeIndex TI, MutableArrayRef<uint8_t> Data) {
+        DestTpi.addTypeRecord(Data, None);
+      });
+  MergedIpi.ForEachRecord(
+      [&DestIpi](TypeIndex TI, MutableArrayRef<uint8_t> Data) {
+        DestIpi.addTypeRecord(Data, None);
+      });
+
+  SmallString<64> OutFile = opts::merge::PdbOutputFile;
+  if (OutFile.empty()) {
+    OutFile = opts::merge::InputFilenames[0];
+    llvm::sys::path::replace_extension(OutFile, "merged.pdb");
+  }
+  ExitOnErr(Builder.commit(OutFile));
+}
+
 int main(int argc_, const char *argv_[]) {
   // Print a stack trace if we signal out.
   sys::PrintStackTraceOnErrorSignal(argv_[0]);
@@ -949,6 +1011,12 @@ int main(int argc_, const char *argv_[]) {
       exit(1);
     }
     diff(opts::diff::InputFilenames[0], opts::diff::InputFilenames[1]);
+  } else if (opts::MergeSubcommand) {
+    if (opts::merge::InputFilenames.size() < 2) {
+      errs() << "merge subcommand requires at least 2 input files.\n";
+      exit(1);
+    }
+    mergePdbs();
   }
 
   outs().flush();