[llvm-pdbutil] Add an export subcommand.
authorZachary Turner <zturner@google.com>
Mon, 2 Apr 2018 18:35:21 +0000 (18:35 +0000)
committerZachary Turner <zturner@google.com>
Mon, 2 Apr 2018 18:35:21 +0000 (18:35 +0000)
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

llvm/include/llvm/DebugInfo/PDB/Native/InfoStream.h
llvm/lib/DebugInfo/PDB/Native/InfoStream.cpp
llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp
llvm/test/tools/llvm-pdbdump/Inputs/tpi.bin [new file with mode: 0644]
llvm/test/tools/llvm-pdbdump/export-stream.test [new file with mode: 0644]
llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
llvm/tools/llvm-pdbutil/llvm-pdbutil.h

index caeb423..f49e7af 100644 (file)
@@ -52,7 +52,7 @@ public:
 
   BinarySubstreamRef getNamedStreamsBuffer() const;
 
-  uint32_t getNamedStreamIndex(llvm::StringRef Name) const;
+  Expected<uint32_t> getNamedStreamIndex(llvm::StringRef Name) const;
   StringMap<uint32_t> named_streams() const;
 
 private:
index e9ae5a1..0bb543e 100644 (file)
@@ -86,10 +86,10 @@ Error InfoStream::reload() {
 
 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;
   if (!NamedStreams.get(Name, Result))
-    return 0;
+    return make_error<RawError>(raw_error_code::no_stream);
   return Result;
 }
 
index 15b31d8..6b1d28a 100644 (file)
@@ -370,7 +370,10 @@ Expected<PDBStringTable &> PDBFile::getStringTable() {
     if (!IS)
       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 =
         safelyCreateIndexedStream(ContainerLayout, *Buffer, NameStreamIndex);
@@ -445,7 +448,13 @@ bool PDBFile::hasPDBStringTable() {
   auto IS = getPDBInfoStream();
   if (!IS)
     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
diff --git a/llvm/test/tools/llvm-pdbdump/Inputs/tpi.bin b/llvm/test/tools/llvm-pdbdump/Inputs/tpi.bin
new file mode 100644 (file)
index 0000000..269a93d
Binary files /dev/null and b/llvm/test/tools/llvm-pdbdump/Inputs/tpi.bin differ
diff --git a/llvm/test/tools/llvm-pdbdump/export-stream.test b/llvm/test/tools/llvm-pdbdump/export-stream.test
new file mode 100644 (file)
index 0000000..f60606d
--- /dev/null
@@ -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
index 3cb0237..a694d47 100644 (file)
@@ -50,6 +50,7 @@
 #include "llvm/DebugInfo/PDB/IPDBSession.h"
 #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.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/NativeSession.h"
 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
@@ -115,6 +116,9 @@ cl::SubCommand MergeSubcommand("merge",
 cl::SubCommand ExplainSubcommand("explain",
                                  "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 FilterCategory("Filtering and Sorting 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::sub(ExplainSubcommand), cl::OneOrMore);
 } // 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;
@@ -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,
                        Optional<opts::bytes::NumberRange> &Parsed) {
   if (Str.empty())
@@ -1274,6 +1336,8 @@ int main(int argc_, const char *argv_[]) {
     mergePdbs();
   } else if (opts::ExplainSubcommand) {
     explain();
+  } else if (opts::ExportSubcommand) {
+    exportStream();
   }
 
   outs().flush();
index e7ad269..82f4e52 100644 (file)
@@ -193,6 +193,12 @@ namespace explain {
 extern llvm::cl::list<std::string> InputFilename;
 extern llvm::cl::list<uint64_t> Offsets;
 } // 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