[llvm] r328799 - [PDB] Add an explain subcommand.

Zachary Turner via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 29 09:28:20 PDT 2018


Author: zturner
Date: Thu Mar 29 09:28:20 2018
New Revision: 328799

URL: http://llvm.org/viewvc/llvm-project?rev=328799&view=rev
Log:
[PDB] Add an explain subcommand.

When investigating various things, we often have a file offset
and what to know what's in the PDB at that address.  For example
we may be doing a binary comparison of two LLD-generated PDBs
to look for sources of non-determinism, or we may wish to compare
an LLD-generated PDB with a Microsoft generated PDB for sources
of byte-for-byte incompatibility.  In these cases, we can do a
binary diff of the two files, and once we find a mismatched byte
we can use explain to figure out what that byte is, immediately
honining in on the problem.

This patch implements this by trying to narrow the meaning of
a particular file offset down as much as possible.

Differential Revision: https://reviews.llvm.org/D44959

Added:
    llvm/trunk/test/tools/llvm-pdbdump/explain.test
    llvm/trunk/tools/llvm-pdbutil/ExplainOutputStyle.cpp
    llvm/trunk/tools/llvm-pdbutil/ExplainOutputStyle.h
Modified:
    llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt
    llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp
    llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h

Added: llvm/trunk/test/tools/llvm-pdbdump/explain.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-pdbdump/explain.test?rev=328799&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-pdbdump/explain.test (added)
+++ llvm/trunk/test/tools/llvm-pdbdump/explain.test Thu Mar 29 09:28:20 2018
@@ -0,0 +1,83 @@
+; RUN: llvm-pdbutil explain -offset=0 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=ZERO %s
+; RUN: llvm-pdbutil explain -offset=40 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=FORTY %s
+; RUN: llvm-pdbutil explain -offset=60 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=SIXTY %s
+
+; RUN: llvm-pdbutil explain -offset=0x1000 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=FPM1 %s
+; RUN: llvm-pdbutil explain -offset=0x1100 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=EXTRANEOUSFPM %s
+; RUN: llvm-pdbutil explain -offset=0x2000 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=FPM2 %s
+
+; RUN: llvm-pdbutil explain -offset=0x3000 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=UNALLOCATED %s
+
+; RUN: llvm-pdbutil explain -offset=0x7000 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=STREAM %s
+
+; RUN: llvm-pdbutil explain -offset=0x1A000 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=STREAMDIR %s
+
+; RUN: llvm-pdbutil explain -offset=0x1B000 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=DIRBLOCKLIST %s
+
+; RUN: llvm-pdbutil explain -offset=0x1D000 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=INVALIDFILEOFFSET %s
+
+; RUN: llvm-pdbutil explain -offset=0xA100 %p/Inputs/InjectedSource.pdb \
+; RUN:  | FileCheck --check-prefix=UNUSED %s
+
+
+ZERO:      Block:Offset = 0:0000.
+ZERO-NEXT: Address is in block 0 (allocated).
+ZERO-NEXT:   This corresponds to offset 0 of MSF super block,
+ZERO-NEXT:   which is part of the MSF file magic.
+
+FORTY:      Block:Offset = 0:0028.
+FORTY-NEXT: Address is in block 0 (allocated).
+FORTY-NEXT:   This corresponds to offset 40 of MSF super block,
+FORTY-NEXT:   which contains the number of bytes in the stream directory.
+
+SIXTY:      Block:Offset = 0:003C.
+SIXTY-NEXT: Address is in block 0 (allocated).
+SIXTY-NEXT:   This corresponds to offset 60 of MSF super block,
+SIXTY-NEXT:   which is outside the range of valid data for the super block.
+
+FPM1:      Block:Offset = 1:0000.
+FPM1-NEXT: Address is in block 1 (allocated).
+FPM1-NEXT:   Address is in FPM1 (Alt FPM)
+FPM1-NEXT:   Address describes the allocation status of blocks [0,8)
+
+EXTRANEOUSFPM:      Block:Offset = 1:0100.
+EXTRANEOUSFPM-NEXT: Address is in block 1 (allocated).
+EXTRANEOUSFPM-NEXT:   Address is in FPM1 (Alt FPM)
+EXTRANEOUSFPM-NEXT:   Address is in extraneous FPM space.
+
+FPM2:      Block:Offset = 2:0000.
+FPM2-NEXT: Address is in block 2 (allocated).
+FPM2-NEXT:   Address is in FPM2 (Main FPM)
+FPM2-NEXT:   Address describes the allocation status of blocks [0,8)
+
+UNALLOCATED:      Block:Offset = 3:0000.
+UNALLOCATED-NEXT: Address is in block 3 (unallocated).
+
+STREAM:      Block:Offset = 7:0000.
+STREAM-NEXT: Address is in block 7 (allocated).
+STREAM-NEXT:   Address is at offset 0/684 of Stream 12 (Module "* Linker *").
+
+STREAMDIR:      Block:Offset = 1A:0000.
+STREAMDIR-NEXT: Address is in block 26 (allocated).
+STREAMDIR-NEXT:   Address is at offset 0/156 of Stream Directory.
+
+DIRBLOCKLIST:      Block:Offset = 1B:0000.
+DIRBLOCKLIST-NEXT: Address is in block 27 (allocated).
+DIRBLOCKLIST-NEXT:   Address is at offset 0 of the directory block list
+
+INVALIDFILEOFFSET: Address 118784 is not in the file (file size = 118784).
+
+UNUSED:      Block:Offset = A:0100.
+UNUSED-NEXT: Address is in block 10 (allocated).
+UNUSED-NEXT:   Address is at offset 256/120 of Stream 11 (Section Header Data) in unused space.

Modified: llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt?rev=328799&r1=328798&r2=328799&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt (original)
+++ llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt Thu Mar 29 09:28:20 2018
@@ -12,6 +12,7 @@ add_llvm_tool(llvm-pdbutil
   Analyze.cpp
   BytesOutputStyle.cpp
   DumpOutputStyle.cpp
+  ExplainOutputStyle.cpp
   InputFile.cpp
   llvm-pdbutil.cpp
   FormatUtil.cpp

Added: llvm/trunk/tools/llvm-pdbutil/ExplainOutputStyle.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/ExplainOutputStyle.cpp?rev=328799&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/ExplainOutputStyle.cpp (added)
+++ llvm/trunk/tools/llvm-pdbutil/ExplainOutputStyle.cpp Thu Mar 29 09:28:20 2018
@@ -0,0 +1,200 @@
+//===- ExplainOutputStyle.cpp --------------------------------- *- C++ --*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExplainOutputStyle.h"
+
+#include "FormatUtil.h"
+#include "StreamUtil.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::msf;
+using namespace llvm::pdb;
+
+ExplainOutputStyle::ExplainOutputStyle(PDBFile &File, uint64_t FileOffset)
+    : File(File), FileOffset(FileOffset),
+      BlockIndex(FileOffset / File.getBlockSize()),
+      OffsetInBlock(FileOffset - BlockIndex * File.getBlockSize()),
+      P(2, false, outs()) {}
+
+Error ExplainOutputStyle::dump() {
+  P.formatLine("Explaining file offset {0} of file '{1}'.", FileOffset,
+               File.getFilePath());
+
+  bool IsAllocated = explainBlockStatus();
+  if (!IsAllocated)
+    return Error::success();
+
+  AutoIndent Indent(P);
+  if (isSuperBlock())
+    explainSuperBlockOffset();
+  else if (isFpmBlock())
+    explainFpmBlockOffset();
+  else if (isBlockMapBlock())
+    explainBlockMapOffset();
+  else if (isStreamDirectoryBlock())
+    explainStreamDirectoryOffset();
+  else if (auto Index = getBlockStreamIndex())
+    explainStreamOffset(*Index);
+  else
+    explainUnknownBlock();
+  return Error::success();
+}
+
+bool ExplainOutputStyle::isSuperBlock() const { return BlockIndex == 0; }
+
+bool ExplainOutputStyle::isFpm1() const {
+  return ((BlockIndex - 1) % File.getBlockSize() == 0);
+}
+bool ExplainOutputStyle::isFpm2() const {
+  return ((BlockIndex - 2) % File.getBlockSize() == 0);
+}
+
+bool ExplainOutputStyle::isFpmBlock() const { return isFpm1() || isFpm2(); }
+
+bool ExplainOutputStyle::isBlockMapBlock() const {
+  return BlockIndex == File.getBlockMapIndex();
+}
+
+bool ExplainOutputStyle::isStreamDirectoryBlock() const {
+  const auto &Layout = File.getMsfLayout();
+  return llvm::is_contained(Layout.DirectoryBlocks, BlockIndex);
+}
+
+Optional<uint32_t> ExplainOutputStyle::getBlockStreamIndex() const {
+  const auto &Layout = File.getMsfLayout();
+  for (const auto &Entry : enumerate(Layout.StreamMap)) {
+    if (!llvm::is_contained(Entry.value(), BlockIndex))
+      continue;
+    return Entry.index();
+  }
+  return None;
+}
+
+bool ExplainOutputStyle::explainBlockStatus() {
+  if (FileOffset >= File.getFileSize()) {
+    P.formatLine("Address {0} is not in the file (file size = {1}).",
+                 FileOffset, File.getFileSize());
+    return false;
+  }
+  P.formatLine("Block:Offset = {2:X-}:{1:X-4}.", FileOffset, OffsetInBlock,
+               BlockIndex);
+
+  bool IsFree = File.getMsfLayout().FreePageMap[BlockIndex];
+  P.formatLine("Address is in block {0} ({1}allocated).", BlockIndex,
+               IsFree ? "un" : "");
+  return !IsFree;
+}
+
+void ExplainOutputStyle::explainSuperBlockOffset() {
+  P.formatLine("This corresponds to offset {0} of MSF super block, ",
+               OffsetInBlock);
+  if (OffsetInBlock < sizeof(msf::Magic))
+    P.printLine("which is part of the MSF file magic.");
+  else if (OffsetInBlock < offsetof(SuperBlock, BlockSize))
+    P.printLine("which contains the block size of the file.");
+  else if (OffsetInBlock < offsetof(SuperBlock, FreeBlockMapBlock))
+    P.printLine("which contains the index of the FPM block (e.g. 1 or 2).");
+  else if (OffsetInBlock < offsetof(SuperBlock, NumBlocks))
+    P.printLine("which contains the number of blocks in the file.");
+  else if (OffsetInBlock < offsetof(SuperBlock, NumDirectoryBytes))
+    P.printLine("which contains the number of bytes in the stream directory.");
+  else if (OffsetInBlock < offsetof(SuperBlock, Unknown1))
+    P.printLine("whose purpose is unknown.");
+  else if (OffsetInBlock < offsetof(SuperBlock, BlockMapAddr))
+    P.printLine("which contains the file offset of the block map.");
+  else {
+    assert(OffsetInBlock > sizeof(SuperBlock));
+    P.printLine(
+        "which is outside the range of valid data for the super block.");
+  }
+}
+
+void ExplainOutputStyle::explainFpmBlockOffset() {
+  const MSFLayout &Layout = File.getMsfLayout();
+  uint32_t MainFpm = Layout.mainFpmBlock();
+  uint32_t AltFpm = Layout.alternateFpmBlock();
+
+  assert(isFpmBlock());
+  uint32_t Fpm = isFpm1() ? 1 : 2;
+  uint32_t FpmChunk = BlockIndex / File.getBlockSize();
+  assert((Fpm == MainFpm) || (Fpm == AltFpm));
+  (void)AltFpm;
+  bool IsMain = (Fpm == MainFpm);
+  P.formatLine("Address is in FPM{0} ({1} FPM)", Fpm, IsMain ? "Main" : "Alt");
+  uint32_t DescribedBlockStart =
+      8 * (FpmChunk * File.getBlockSize() + OffsetInBlock);
+  if (DescribedBlockStart > File.getBlockCount()) {
+    P.printLine("Address is in extraneous FPM space.");
+    return;
+  }
+
+  P.formatLine("Address describes the allocation status of blocks [{0},{1})",
+               DescribedBlockStart, DescribedBlockStart + 8);
+}
+
+static bool offsetIsInBlock(const PDBFile &File, uint64_t Offset,
+                            uint32_t Block) {
+  uint64_t BlockOffset = uint64_t(Block) * File.getBlockSize();
+  uint64_t BlockOffset1 = BlockOffset + File.getBlockSize();
+  return (Offset >= BlockOffset && Offset < BlockOffset1);
+}
+
+void ExplainOutputStyle::explainBlockMapOffset() {
+  assert(offsetIsInBlock(File, FileOffset, File.getBlockMapIndex()));
+  uint64_t BlockMapOffset = File.getBlockMapOffset();
+  uint32_t OffsetInBlock = FileOffset - BlockMapOffset;
+  P.formatLine("Address is at offset {0} of the directory block list",
+               OffsetInBlock);
+}
+
+static uint32_t getOffsetInStream(ArrayRef<support::ulittle32_t> StreamBlocks,
+                                  uint64_t FileOffset, uint32_t BlockSize) {
+  uint32_t BlockIndex = FileOffset / BlockSize;
+  uint32_t OffsetInBlock = FileOffset - BlockIndex * BlockSize;
+
+  auto Iter = llvm::find(StreamBlocks, BlockIndex);
+  assert(Iter != StreamBlocks.end());
+  uint32_t StreamBlockIndex = std::distance(StreamBlocks.begin(), Iter);
+  return StreamBlockIndex * BlockSize + OffsetInBlock;
+}
+
+void ExplainOutputStyle::explainStreamOffset(uint32_t Stream) {
+  SmallVector<StreamInfo, 12> Streams;
+  discoverStreamPurposes(File, Streams);
+
+  assert(Stream <= Streams.size());
+  const StreamInfo &S = Streams[Stream];
+  const auto &Layout = File.getStreamLayout(Stream);
+  uint32_t StreamOff =
+      getOffsetInStream(Layout.Blocks, FileOffset, File.getBlockSize());
+  P.formatLine("Address is at offset {0}/{1} of Stream {2} ({3}){4}.",
+               StreamOff, Layout.Length, Stream, S.getLongName(),
+               (StreamOff > Layout.Length) ? " in unused space" : "");
+}
+
+void ExplainOutputStyle::explainStreamDirectoryOffset() {
+  auto DirectoryBlocks = File.getDirectoryBlockArray();
+  const auto &Layout = File.getMsfLayout();
+  uint32_t StreamOff =
+      getOffsetInStream(DirectoryBlocks, FileOffset, File.getBlockSize());
+  P.formatLine("Address is at offset {0}/{1} of Stream Directory{2}.",
+               StreamOff, uint32_t(Layout.SB->NumDirectoryBytes),
+               uint32_t(StreamOff > Layout.SB->NumDirectoryBytes)
+                   ? " in unused space"
+                   : "");
+}
+
+void ExplainOutputStyle::explainUnknownBlock() {
+  P.formatLine("Address has unknown purpose.");
+}

Added: llvm/trunk/tools/llvm-pdbutil/ExplainOutputStyle.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/ExplainOutputStyle.h?rev=328799&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/ExplainOutputStyle.h (added)
+++ llvm/trunk/tools/llvm-pdbutil/ExplainOutputStyle.h Thu Mar 29 09:28:20 2018
@@ -0,0 +1,59 @@
+//===- ExplainOutputStyle.h ----------------------------------- *- C++ --*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_EXPLAINOUTPUTSTYLE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_EXPLAINOUTPUTSTYLE_H
+
+#include "LinePrinter.h"
+#include "OutputStyle.h"
+
+#include <string>
+
+namespace llvm {
+
+namespace pdb {
+
+class PDBFile;
+
+class ExplainOutputStyle : public OutputStyle {
+
+public:
+  ExplainOutputStyle(PDBFile &File, uint64_t FileOffset);
+
+  Error dump() override;
+
+private:
+  bool explainBlockStatus();
+
+  bool isFpm1() const;
+  bool isFpm2() const;
+
+  bool isSuperBlock() const;
+  bool isFpmBlock() const;
+  bool isBlockMapBlock() const;
+  bool isStreamDirectoryBlock() const;
+  Optional<uint32_t> getBlockStreamIndex() const;
+
+  void explainSuperBlockOffset();
+  void explainFpmBlockOffset();
+  void explainBlockMapOffset();
+  void explainStreamDirectoryOffset();
+  void explainStreamOffset(uint32_t Stream);
+  void explainUnknownBlock();
+
+  PDBFile &File;
+  const uint64_t FileOffset;
+  const uint64_t BlockIndex;
+  const uint64_t OffsetInBlock;
+  LinePrinter P;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif

Modified: llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp?rev=328799&r1=328798&r2=328799&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp (original)
+++ llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp Thu Mar 29 09:28:20 2018
@@ -16,6 +16,7 @@
 #include "Analyze.h"
 #include "BytesOutputStyle.h"
 #include "DumpOutputStyle.h"
+#include "ExplainOutputStyle.h"
 #include "InputFile.h"
 #include "LinePrinter.h"
 #include "OutputStyle.h"
@@ -111,6 +112,9 @@ cl::SubCommand
 cl::SubCommand MergeSubcommand("merge",
                                "Merge multiple PDBs into a single PDB");
 
+cl::SubCommand ExplainSubcommand("explain",
+                                 "Explain the meaning of a file offset");
+
 cl::OptionCategory TypeCategory("Symbol Type Options");
 cl::OptionCategory FilterCategory("Filtering and Sorting Options");
 cl::OptionCategory OtherOptions("Other Options");
@@ -605,6 +609,16 @@ cl::opt<std::string>
     PdbOutputFile("pdb", cl::desc("the name of the PDB file to write"),
                   cl::sub(MergeSubcommand));
 }
+
+namespace explain {
+cl::list<std::string> InputFilename(cl::Positional,
+                                    cl::desc("<input PDB file>"), cl::Required,
+                                    cl::sub(ExplainSubcommand));
+
+cl::opt<uint64_t> Offset("offset", cl::desc("The file offset to explain"),
+                         cl::sub(ExplainSubcommand), cl::Required,
+                         cl::OneOrMore);
+} // namespace explain
 }
 
 static ExitOnError ExitOnErr;
@@ -1074,6 +1088,14 @@ static void mergePdbs() {
   ExitOnErr(Builder.commit(OutFile));
 }
 
+static void explain() {
+  std::unique_ptr<IPDBSession> Session;
+  PDBFile &File = loadPDB(opts::explain::InputFilename.front(), Session);
+  auto O = llvm::make_unique<ExplainOutputStyle>(File, opts::explain::Offset);
+
+  ExitOnErr(O->dump());
+}
+
 static bool parseRange(StringRef Str,
                        Optional<opts::bytes::NumberRange> &Parsed) {
   if (Str.empty())
@@ -1248,6 +1270,8 @@ int main(int argc_, const char *argv_[])
       exit(1);
     }
     mergePdbs();
+  } else if (opts::ExplainSubcommand) {
+    explain();
   }
 
   outs().flush();

Modified: llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h?rev=328799&r1=328798&r2=328799&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h (original)
+++ llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h Thu Mar 29 09:28:20 2018
@@ -189,6 +189,10 @@ extern llvm::cl::list<ModuleSubsection>
 extern llvm::cl::opt<bool> DumpModuleSyms;
 } // namespace pdb2yaml
 
+namespace explain {
+extern llvm::cl::list<std::string> InputFilename;
+extern llvm::cl::opt<uint64_t> Offset;
+} // namespace explain
 }
 
 #endif




More information about the llvm-commits mailing list