--- /dev/null
+---\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
--- /dev/null
+---\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
--- /dev/null
+; 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
#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"
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");
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;
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]);
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();