From: Zachary Turner Date: Thu, 18 May 2017 23:03:41 +0000 (+0000) Subject: [llvm-pdbdump] Add the ability to merge PDBs. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=47b13b0b78008f8841062c12801c85c5d5e4ab7f;p=llvm [llvm-pdbdump] Add the ability to merge PDBs. 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 --- diff --git a/include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h b/include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h index 6c609c34665..21cfa83e6af 100644 --- a/include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h +++ b/include/llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h @@ -72,7 +72,7 @@ private: size_t TypeRecordBytes = 0; - Optional VerHeader; + PdbRaw_TpiVer VerHeader = PdbRaw_TpiVer::PdbTpiV80; std::vector> TypeRecords; std::vector TypeHashes; std::vector TypeIndexOffsets; diff --git a/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp b/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp index 701a318511b..20456cc9782 100644 --- a/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp +++ b/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp @@ -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 index 00000000000..89d471e3343 --- /dev/null +++ b/test/DebugInfo/PDB/Inputs/merge1.yaml @@ -0,0 +1,52 @@ +--- +TpiStream: + Records: + # uint32_t* [Index: 0x1000] + - Kind: LF_POINTER + Pointer: + ReferentType: 117 + Attrs: 32778 + # int64_t* [Index: 0x1001] + - Kind: LF_POINTER + Pointer: + ReferentType: 118 + Attrs: 32778 + # struct OnlyInMerge1 [Index: 0x1002] + - Kind: LF_STRUCTURE + Class: + MemberCount: 0 + Options: [ None, ForwardReference, HasUniqueName ] + FieldList: 0 + Name: 'OnlyInMerge1' + UniqueName: 'OnlyInMerge1' + DerivationList: 0 + VTableShape: 0 + Size: 0 + # uint32_t** [Index: 0x1003] + - Kind: LF_POINTER + Pointer: + ReferentType: 4096 + Attrs: 32778 + # uint32_t*** [Index: 0x1004] + - Kind: LF_POINTER + Pointer: + ReferentType: 4099 + Attrs: 32778 + # int64_t* [Index: 0x1005] + - Kind: LF_POINTER + Pointer: + ReferentType: 4097 + Attrs: 32778 + # [uint32_t, uint32_t*, uint32_t**] [Index: 0x1006] + - Kind: LF_ARGLIST + ArgList: + ArgIndices: [ 117, 4096, 4099 ] + # uint32_t (uint32_t, uint32_t*, uint32_t**) [Index: 0x1007] + - Kind: LF_PROCEDURE + Procedure: + ReturnType: 117 + CallConv: NearC + Options: [ None ] + ParameterCount: 0 + ArgumentList: 4102 +... diff --git a/test/DebugInfo/PDB/Inputs/merge2.yaml b/test/DebugInfo/PDB/Inputs/merge2.yaml new file mode 100644 index 00000000000..b6cbdb98f0c --- /dev/null +++ b/test/DebugInfo/PDB/Inputs/merge2.yaml @@ -0,0 +1,52 @@ +--- +TpiStream: + Records: + # uint32_t* [Index: 0x1000] + - Kind: LF_POINTER + Pointer: + ReferentType: 117 + Attrs: 32778 + # uint32_t** [Index: 0x1001] + - Kind: LF_POINTER + Pointer: + ReferentType: 4096 + Attrs: 32778 + # uint32_t*** [Index: 0x1002] + - Kind: LF_POINTER + Pointer: + ReferentType: 4097 + Attrs: 32778 + # [uint32_t, uint32_t*, uint32_t**] [Index: 0x1003] + - Kind: LF_ARGLIST + ArgList: + ArgIndices: [ 117, 4096, 4097 ] + # uint32_t (uint32_t, uint32_t*, uint32_t**) [Index: 0x1004] + - Kind: LF_PROCEDURE + Procedure: + ReturnType: 117 + CallConv: NearC + Options: [ None ] + ParameterCount: 0 + ArgumentList: 4099 + # int64_t* [Index: 0x1005] + - Kind: LF_POINTER + Pointer: + ReferentType: 118 + Attrs: 32778 + # int64_t** [Index: 0x1006] + - Kind: LF_POINTER + Pointer: + ReferentType: 4101 + Attrs: 32778 + # struct OnlyInMerge2 [Index: 0x1007] + - Kind: LF_STRUCTURE + Class: + MemberCount: 0 + Options: [ None, ForwardReference, HasUniqueName ] + FieldList: 0 + Name: 'OnlyInMerge2' + UniqueName: 'OnlyInMerge2' + DerivationList: 0 + VTableShape: 0 + Size: 0 +... diff --git a/test/DebugInfo/PDB/pdbdump-mergetypes.test b/test/DebugInfo/PDB/pdbdump-mergetypes.test new file mode 100644 index 00000000000..96f6316d476 --- /dev/null +++ b/test/DebugInfo/PDB/pdbdump-mergetypes.test @@ -0,0 +1,24 @@ +; RUN: llvm-pdbdump yaml2pdb -pdb=%t.1.pdb %p/Inputs/merge1.yaml +; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2.pdb %p/Inputs/merge2.yaml +; RUN: llvm-pdbdump merge -pdb=%t.3.pdb %t.1.pdb %t.2.pdb +; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=MERGED %s +; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=ARGLIST %s + + +MERGED: Type Info Stream (TPI) +MERGED: Record count: 9 +MERGED-DAG: PointeeType: unsigned +MERGED-DAG: PointeeType: unsigned* +MERGED-DAG: PointeeType: unsigned** +MERGED-DAG: PointeeType: __int64 +MERGED-DAG: PointeeType: __int64* +MERGED-DAG: Name: OnlyInMerge1 +MERGED-DAG: Name: OnlyInMerge2 +MERGED-DAG: TypeLeafKind: LF_ARGLIST + +ARGLIST: TypeLeafKind: LF_ARGLIST +ARGLIST-NEXT: NumArgs: 3 +ARGLIST-NEXT: Arguments [ +ARGLIST-NEXT: ArgType: unsigned +ARGLIST-NEXT: ArgType: unsigned* +ARGLIST-NEXT: ArgType: unsigned** diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp index dc3e15a89af..d3c6a799ac4 100644 --- a/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -31,9 +31,11 @@ #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 InputFilename(cl::Positional, cl::desc(""), cl::Required, cl::sub(AnalyzeSubcommand)); } + +namespace merge { +cl::list InputFilenames(cl::Positional, + cl::desc(""), + cl::OneOrMore, cl::sub(MergeSubcommand)); +cl::opt + 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 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 Data) { + DestTpi.addTypeRecord(Data, None); + }); + MergedIpi.ForEachRecord( + [&DestIpi](TypeIndex TI, MutableArrayRef 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();