diff options
author | Zachary Turner <zturner@google.com> | 2018-03-29 16:28:20 +0000 |
---|---|---|
committer | Zachary Turner <zturner@google.com> | 2018-03-29 16:28:20 +0000 |
commit | ea40f40e1b024598fb1dbd56211c2f24cb703df2 (patch) | |
tree | 9229b61a4aa5ff8bab244758d5845b0b05b11484 /llvm/tools/llvm-pdbutil | |
parent | c7cc87922e7e28487fd93b010d6de9ec7c0ef318 (diff) | |
download | bcm5719-llvm-ea40f40e1b024598fb1dbd56211c2f24cb703df2.tar.gz bcm5719-llvm-ea40f40e1b024598fb1dbd56211c2f24cb703df2.zip |
[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
llvm-svn: 328799
Diffstat (limited to 'llvm/tools/llvm-pdbutil')
-rw-r--r-- | llvm/tools/llvm-pdbutil/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp | 200 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/ExplainOutputStyle.h | 59 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp | 24 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/llvm-pdbutil.h | 4 |
5 files changed, 288 insertions, 0 deletions
diff --git a/llvm/tools/llvm-pdbutil/CMakeLists.txt b/llvm/tools/llvm-pdbutil/CMakeLists.txt index 82f0088e2a8..1ccbfdfc4df 100644 --- a/llvm/tools/llvm-pdbutil/CMakeLists.txt +++ b/llvm/tools/llvm-pdbutil/CMakeLists.txt @@ -12,6 +12,7 @@ add_llvm_tool(llvm-pdbutil Analyze.cpp BytesOutputStyle.cpp DumpOutputStyle.cpp + ExplainOutputStyle.cpp InputFile.cpp llvm-pdbutil.cpp FormatUtil.cpp diff --git a/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp b/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp new file mode 100644 index 00000000000..1947c93d981 --- /dev/null +++ b/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp @@ -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."); +} diff --git a/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h b/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h new file mode 100644 index 00000000000..386f6156a89 --- /dev/null +++ b/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h @@ -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 diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp index 89d03d5b924..f472bfa5fa3 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -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(); diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h index 93d34a124e5..6765b93146f 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h @@ -189,6 +189,10 @@ extern llvm::cl::list<ModuleSubsection> DumpModuleSubsections; 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 |