forked from OSchip/llvm-project
[llvm-pdbutil] Add an export subcommand.
This command can dump the binary contents of a stream to a file. This is useful when you want to do side-by-side comparisons of a specific stream from two PDBs to examine the differences between them. You can export both of them to a file, then open them up side by side in a hex editor (for example), so as to eliminate any differences that might arise from the contents being on different blocks in the PDB. In subsequent patches I plan to improve the "explain" subcommand so that you can explain the contents of a binary file that isn't necessarily a full PDB, but one of these dumped streams, by telling the subcommand how to interpret the contents. llvm-svn: 329002
This commit is contained in:
parent
a26755cd55
commit
d11328a1bb
|
@ -52,7 +52,7 @@ public:
|
||||||
|
|
||||||
BinarySubstreamRef getNamedStreamsBuffer() const;
|
BinarySubstreamRef getNamedStreamsBuffer() const;
|
||||||
|
|
||||||
uint32_t getNamedStreamIndex(llvm::StringRef Name) const;
|
Expected<uint32_t> getNamedStreamIndex(llvm::StringRef Name) const;
|
||||||
StringMap<uint32_t> named_streams() const;
|
StringMap<uint32_t> named_streams() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -86,10 +86,10 @@ Error InfoStream::reload() {
|
||||||
|
|
||||||
uint32_t InfoStream::getStreamSize() const { return Stream->getLength(); }
|
uint32_t InfoStream::getStreamSize() const { return Stream->getLength(); }
|
||||||
|
|
||||||
uint32_t InfoStream::getNamedStreamIndex(llvm::StringRef Name) const {
|
Expected<uint32_t> InfoStream::getNamedStreamIndex(llvm::StringRef Name) const {
|
||||||
uint32_t Result;
|
uint32_t Result;
|
||||||
if (!NamedStreams.get(Name, Result))
|
if (!NamedStreams.get(Name, Result))
|
||||||
return 0;
|
return make_error<RawError>(raw_error_code::no_stream);
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -370,7 +370,10 @@ Expected<PDBStringTable &> PDBFile::getStringTable() {
|
||||||
if (!IS)
|
if (!IS)
|
||||||
return IS.takeError();
|
return IS.takeError();
|
||||||
|
|
||||||
uint32_t NameStreamIndex = IS->getNamedStreamIndex("/names");
|
Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex("/names");
|
||||||
|
if (!ExpectedNSI)
|
||||||
|
return ExpectedNSI.takeError();
|
||||||
|
uint32_t NameStreamIndex = *ExpectedNSI;
|
||||||
|
|
||||||
auto NS =
|
auto NS =
|
||||||
safelyCreateIndexedStream(ContainerLayout, *Buffer, NameStreamIndex);
|
safelyCreateIndexedStream(ContainerLayout, *Buffer, NameStreamIndex);
|
||||||
|
@ -445,7 +448,13 @@ bool PDBFile::hasPDBStringTable() {
|
||||||
auto IS = getPDBInfoStream();
|
auto IS = getPDBInfoStream();
|
||||||
if (!IS)
|
if (!IS)
|
||||||
return false;
|
return false;
|
||||||
return IS->getNamedStreamIndex("/names") < getNumStreams();
|
Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex("/names");
|
||||||
|
if (!ExpectedNSI) {
|
||||||
|
consumeError(ExpectedNSI.takeError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
assert(*ExpectedNSI < getNumStreams());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around MappedBlockStream::createIndexedStream() that checks if a
|
/// Wrapper around MappedBlockStream::createIndexedStream() that checks if a
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,2 @@
|
||||||
|
; RUN: llvm-pdbutil export -stream=2 -out=%t.tpi.bin %p/Inputs/InjectedSource.pdb
|
||||||
|
; RUN: diff %t.tpi.bin %p/Inputs/tpi.bin
|
|
@ -50,6 +50,7 @@
|
||||||
#include "llvm/DebugInfo/PDB/IPDBSession.h"
|
#include "llvm/DebugInfo/PDB/IPDBSession.h"
|
||||||
#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
|
#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
|
||||||
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
|
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
|
||||||
|
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
||||||
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
|
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
|
||||||
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
|
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
|
||||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||||
|
@ -115,6 +116,9 @@ cl::SubCommand MergeSubcommand("merge",
|
||||||
cl::SubCommand ExplainSubcommand("explain",
|
cl::SubCommand ExplainSubcommand("explain",
|
||||||
"Explain the meaning of a file offset");
|
"Explain the meaning of a file offset");
|
||||||
|
|
||||||
|
cl::SubCommand ExportSubcommand("export",
|
||||||
|
"Write binary data from a stream to a file");
|
||||||
|
|
||||||
cl::OptionCategory TypeCategory("Symbol Type Options");
|
cl::OptionCategory TypeCategory("Symbol Type Options");
|
||||||
cl::OptionCategory FilterCategory("Filtering and Sorting Options");
|
cl::OptionCategory FilterCategory("Filtering and Sorting Options");
|
||||||
cl::OptionCategory OtherOptions("Other Options");
|
cl::OptionCategory OtherOptions("Other Options");
|
||||||
|
@ -618,6 +622,24 @@ cl::list<std::string> InputFilename(cl::Positional,
|
||||||
cl::list<uint64_t> Offsets("offset", cl::desc("The file offset to explain"),
|
cl::list<uint64_t> Offsets("offset", cl::desc("The file offset to explain"),
|
||||||
cl::sub(ExplainSubcommand), cl::OneOrMore);
|
cl::sub(ExplainSubcommand), cl::OneOrMore);
|
||||||
} // namespace explain
|
} // namespace explain
|
||||||
|
|
||||||
|
namespace exportstream {
|
||||||
|
cl::list<std::string> InputFilename(cl::Positional,
|
||||||
|
cl::desc("<input PDB file>"), cl::Required,
|
||||||
|
cl::sub(ExportSubcommand));
|
||||||
|
cl::opt<std::string> OutputFile("out",
|
||||||
|
cl::desc("The file to write the stream to"),
|
||||||
|
cl::Required, cl::sub(ExportSubcommand));
|
||||||
|
cl::opt<std::string>
|
||||||
|
Stream("stream", cl::Required,
|
||||||
|
cl::desc("The index or name of the stream whose contents to export"),
|
||||||
|
cl::sub(ExportSubcommand));
|
||||||
|
cl::opt<bool> ForceName("name",
|
||||||
|
cl::desc("Force the interpretation of -stream as a "
|
||||||
|
"string, even if it is a valid integer"),
|
||||||
|
cl::sub(ExportSubcommand), cl::Optional,
|
||||||
|
cl::init(false));
|
||||||
|
} // namespace exportstream
|
||||||
}
|
}
|
||||||
|
|
||||||
static ExitOnError ExitOnErr;
|
static ExitOnError ExitOnErr;
|
||||||
|
@ -1098,6 +1120,46 @@ static void explain() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void exportStream() {
|
||||||
|
std::unique_ptr<IPDBSession> Session;
|
||||||
|
PDBFile &File = loadPDB(opts::exportstream::InputFilename.front(), Session);
|
||||||
|
|
||||||
|
std::unique_ptr<MappedBlockStream> SourceStream;
|
||||||
|
uint32_t Index = 0;
|
||||||
|
bool Success = false;
|
||||||
|
std::string OutFileName = opts::exportstream::OutputFile;
|
||||||
|
|
||||||
|
if (!opts::exportstream::ForceName) {
|
||||||
|
// First try to parse it as an integer, if it fails fall back to treating it
|
||||||
|
// as a named stream.
|
||||||
|
if (to_integer(opts::exportstream::Stream, Index)) {
|
||||||
|
if (Index >= File.getNumStreams()) {
|
||||||
|
errs() << "Error: " << Index << " is not a valid stream index.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
Success = true;
|
||||||
|
outs() << "Dumping contents of stream index " << Index << " to file "
|
||||||
|
<< OutFileName << ".\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Success) {
|
||||||
|
InfoStream &IS = cantFail(File.getPDBInfoStream());
|
||||||
|
Index = ExitOnErr(IS.getNamedStreamIndex(opts::exportstream::Stream));
|
||||||
|
outs() << "Dumping contents of stream '" << opts::exportstream::Stream
|
||||||
|
<< "' (index " << Index << ") to file " << OutFileName << ".\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceStream = MappedBlockStream::createIndexedStream(
|
||||||
|
File.getMsfLayout(), File.getMsfBuffer(), Index, File.getAllocator());
|
||||||
|
auto OutFile = ExitOnErr(
|
||||||
|
FileOutputBuffer::create(OutFileName, SourceStream->getLength()));
|
||||||
|
FileBufferByteStream DestStream(std::move(OutFile), llvm::support::little);
|
||||||
|
BinaryStreamWriter Writer(DestStream);
|
||||||
|
ExitOnErr(Writer.writeStreamRef(*SourceStream));
|
||||||
|
ExitOnErr(DestStream.commit());
|
||||||
|
}
|
||||||
|
|
||||||
static bool parseRange(StringRef Str,
|
static bool parseRange(StringRef Str,
|
||||||
Optional<opts::bytes::NumberRange> &Parsed) {
|
Optional<opts::bytes::NumberRange> &Parsed) {
|
||||||
if (Str.empty())
|
if (Str.empty())
|
||||||
|
@ -1274,6 +1336,8 @@ int main(int argc_, const char *argv_[]) {
|
||||||
mergePdbs();
|
mergePdbs();
|
||||||
} else if (opts::ExplainSubcommand) {
|
} else if (opts::ExplainSubcommand) {
|
||||||
explain();
|
explain();
|
||||||
|
} else if (opts::ExportSubcommand) {
|
||||||
|
exportStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
outs().flush();
|
outs().flush();
|
||||||
|
|
|
@ -193,6 +193,12 @@ namespace explain {
|
||||||
extern llvm::cl::list<std::string> InputFilename;
|
extern llvm::cl::list<std::string> InputFilename;
|
||||||
extern llvm::cl::list<uint64_t> Offsets;
|
extern llvm::cl::list<uint64_t> Offsets;
|
||||||
} // namespace explain
|
} // namespace explain
|
||||||
|
|
||||||
|
namespace exportstream {
|
||||||
|
extern llvm::cl::opt<std::string> OutputFile;
|
||||||
|
extern llvm::cl::opt<std::string> Stream;
|
||||||
|
extern llvm::cl::opt<bool> ForceName;
|
||||||
|
} // namespace exportstream
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue