[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
This commit is contained in:
Zachary Turner 2017-05-18 23:03:41 +00:00
parent 2a4f1171a7
commit 47b13b0b78
6 changed files with 198 additions and 2 deletions

View 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;

View 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;

View File

@ -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
...

View File

@ -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
...

View File

@ -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**

View File

@ -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<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();