summaryrefslogtreecommitdiffstats
path: root/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp
diff options
context:
space:
mode:
authorZachary Turner <zturner@google.com>2018-03-29 16:28:20 +0000
committerZachary Turner <zturner@google.com>2018-03-29 16:28:20 +0000
commitea40f40e1b024598fb1dbd56211c2f24cb703df2 (patch)
tree9229b61a4aa5ff8bab244758d5845b0b05b11484 /llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp
parentc7cc87922e7e28487fd93b010d6de9ec7c0ef318 (diff)
downloadbcm5719-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/ExplainOutputStyle.cpp')
-rw-r--r--llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp200
1 files changed, 200 insertions, 0 deletions
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.");
+}
OpenPOWER on IntegriCloud