From 7b596b1db9be48c176bc89f9b03a7696599b1382 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Thu, 16 Mar 2017 20:18:41 +0000 Subject: [PATCH] [llvm-pdbdump] Add support for diffing the PDB Stream. In doing so I discovered that we completely ignore some bytes of the PDB Stream after we "finish" loading it. These bytes seem to specify some additional information about what kind of data is present in the PDB. A subsequent patch will add code to read in those fields and store their values. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@297983 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/DebugInfo/CodeView/Formatters.h | 4 + .../llvm/DebugInfo/PDB/Native/Formatters.h | 52 +++++++ .../llvm/DebugInfo/PDB/Native/InfoStream.h | 5 + .../DebugInfo/PDB/Native/NamedStreamMap.h | 1 + include/llvm/DebugInfo/PDB/Native/RawTypes.h | 4 + lib/DebugInfo/PDB/Native/InfoStream.cpp | 13 +- lib/DebugInfo/PDB/Native/NamedStreamMap.cpp | 2 + tools/llvm-pdbdump/Diff.cpp | 133 +++++++++++++++--- 8 files changed, 190 insertions(+), 24 deletions(-) create mode 100644 include/llvm/DebugInfo/PDB/Native/Formatters.h diff --git a/include/llvm/DebugInfo/CodeView/Formatters.h b/include/llvm/DebugInfo/CodeView/Formatters.h index d64024c56be..37a91098a8b 100644 --- a/include/llvm/DebugInfo/CodeView/Formatters.h +++ b/include/llvm/DebugInfo/CodeView/Formatters.h @@ -30,6 +30,10 @@ public: inline detail::GuidAdapter fmt_guid(StringRef Item) { return detail::GuidAdapter(Item); } + +inline detail::GuidAdapter fmt_guid(ArrayRef Item) { + return detail::GuidAdapter(Item); +} } } diff --git a/include/llvm/DebugInfo/PDB/Native/Formatters.h b/include/llvm/DebugInfo/PDB/Native/Formatters.h new file mode 100644 index 00000000000..183f0ad8307 --- /dev/null +++ b/include/llvm/DebugInfo/PDB/Native/Formatters.h @@ -0,0 +1,52 @@ +//===- Formatters.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_DEBUGINFO_PDB_NATIVE_FORMATTERS_H +#define LLVM_DEBUGINFO_PDB_NATIVE_FORMATTERS_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/FormatProviders.h" + +#define FORMAT_CASE(Value, Name) \ + case Value: \ + Stream << Name; \ + break; + +namespace llvm { +template <> struct format_provider { + static void format(const pdb::PDB_UniqueId &V, llvm::raw_ostream &Stream, + StringRef Style) { + codeview::fmt_guid(V.Guid).format(Stream, Style); + } +}; + +template <> struct format_provider { + static void format(const pdb::PdbRaw_ImplVer &V, llvm::raw_ostream &Stream, + StringRef Style) { + switch (V) { + FORMAT_CASE(pdb::PdbRaw_ImplVer::PdbImplVC110, "VC110") + FORMAT_CASE(pdb::PdbRaw_ImplVer::PdbImplVC140, "VC140") + FORMAT_CASE(pdb::PdbRaw_ImplVer::PdbImplVC2, "VC2") + FORMAT_CASE(pdb::PdbRaw_ImplVer::PdbImplVC4, "VC4") + FORMAT_CASE(pdb::PdbRaw_ImplVer::PdbImplVC41, "VC41") + FORMAT_CASE(pdb::PdbRaw_ImplVer::PdbImplVC50, "VC50") + FORMAT_CASE(pdb::PdbRaw_ImplVer::PdbImplVC70, "VC70") + FORMAT_CASE(pdb::PdbRaw_ImplVer::PdbImplVC70Dep, "VC70Dep") + FORMAT_CASE(pdb::PdbRaw_ImplVer::PdbImplVC80, "VC80") + FORMAT_CASE(pdb::PdbRaw_ImplVer::PdbImplVC98, "VC98") + } + } +}; +} + +#endif diff --git a/include/llvm/DebugInfo/PDB/Native/InfoStream.h b/include/llvm/DebugInfo/PDB/Native/InfoStream.h index 0b59d9e789d..4a4222a680e 100644 --- a/include/llvm/DebugInfo/PDB/Native/InfoStream.h +++ b/include/llvm/DebugInfo/PDB/Native/InfoStream.h @@ -32,10 +32,13 @@ public: Error reload(); + uint32_t getStreamSize() const; + PdbRaw_ImplVer getVersion() const; uint32_t getSignature() const; uint32_t getAge() const; PDB_UniqueId getGuid() const; + uint32_t getNamedStreamMapByteSize() const; const NamedStreamMap &getNamedStreams() const; @@ -63,6 +66,8 @@ private: // universally unique. PDB_UniqueId Guid; + uint32_t NamedStreamMapByteSize = 0; + NamedStreamMap NamedStreams; }; } diff --git a/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h b/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h index 2327bfadb69..d4206503e7d 100644 --- a/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h +++ b/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h @@ -36,6 +36,7 @@ public: Error commit(BinaryStreamWriter &Writer) const; uint32_t finalize(); + uint32_t size() const; bool get(StringRef Stream, uint32_t &StreamNo) const; void set(StringRef Stream, uint32_t StreamNo); void remove(StringRef Stream); diff --git a/include/llvm/DebugInfo/PDB/Native/RawTypes.h b/include/llvm/DebugInfo/PDB/Native/RawTypes.h index 5f0a824b4dd..1b2631efce7 100644 --- a/include/llvm/DebugInfo/PDB/Native/RawTypes.h +++ b/include/llvm/DebugInfo/PDB/Native/RawTypes.h @@ -266,6 +266,10 @@ struct PDB_UniqueId { uint8_t Guid[16]; }; +inline bool operator==(const PDB_UniqueId &LHS, const PDB_UniqueId &RHS) { + return 0 == ::memcmp(LHS.Guid, RHS.Guid, sizeof(LHS.Guid)); +} + // The header preceeding the global TPI stream. // This corresponds to `HDR` in PDB/dbi/tpi.h. struct TpiStreamHeader { diff --git a/lib/DebugInfo/PDB/Native/InfoStream.cpp b/lib/DebugInfo/PDB/Native/InfoStream.cpp index f3f64847fcf..1ce0fe84206 100644 --- a/lib/DebugInfo/PDB/Native/InfoStream.cpp +++ b/lib/DebugInfo/PDB/Native/InfoStream.cpp @@ -51,9 +51,16 @@ Error InfoStream::reload() { Age = H->Age; Guid = H->Guid; - return NamedStreams.load(Reader); + uint32_t Offset = Reader.getOffset(); + if (auto EC = NamedStreams.load(Reader)) + return EC; + uint32_t NewOffset = Reader.getOffset(); + NamedStreamMapByteSize = NewOffset - Offset; + return Error::success(); } +uint32_t InfoStream::getStreamSize() const { return Stream->getLength(); } + uint32_t InfoStream::getNamedStreamIndex(llvm::StringRef Name) const { uint32_t Result; if (!NamedStreams.get(Name, Result)) @@ -76,6 +83,10 @@ uint32_t InfoStream::getAge() const { return Age; } PDB_UniqueId InfoStream::getGuid() const { return Guid; } +uint32_t InfoStream::getNamedStreamMapByteSize() const { + return NamedStreamMapByteSize; +} + const NamedStreamMap &InfoStream::getNamedStreams() const { return NamedStreams; } diff --git a/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp b/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp index ae453282b06..c7ba32b82bc 100644 --- a/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp +++ b/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp @@ -114,6 +114,8 @@ NamedStreamMap::entries() const { Mapping.end()); } +uint32_t NamedStreamMap::size() const { return Mapping.size(); } + bool NamedStreamMap::get(StringRef Stream, uint32_t &StreamNo) const { auto Iter = Mapping.find(Stream); if (Iter == Mapping.end()) diff --git a/tools/llvm-pdbdump/Diff.cpp b/tools/llvm-pdbdump/Diff.cpp index d78c07f1019..529e57da5c1 100644 --- a/tools/llvm-pdbdump/Diff.cpp +++ b/tools/llvm-pdbdump/Diff.cpp @@ -12,6 +12,8 @@ #include "StreamUtil.h" #include "llvm-pdbdump.h" +#include "llvm/DebugInfo/PDB/Native/Formatters.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawConstants.h" #include "llvm/DebugInfo/PDB/Native/StringTable.h" @@ -114,12 +116,37 @@ Error DiffStyle::dump() { template static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, T V1, T V2) { - if (V1 != V2) { - outs().indent(2) << Label << "\n"; - outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), V1); - outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), V2); + if (V1 == V2) { + outs() << formatv(" {0}: No differences detected!\n", Label); + return false; } - return (V1 != V2); + + outs().indent(2) << Label << "\n"; + outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), V1); + outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), V2); + return true; +} + +template +static bool printSymmetricDifferences(PDBFile &File1, PDBFile &File2, + T &&OnlyRange1, T &&OnlyRange2, + StringRef Label) { + bool HasDiff = false; + if (!OnlyRange1.empty()) { + HasDiff = true; + outs() << formatv(" {0} {1}(s) only in ({2})\n", OnlyRange1.size(), Label, + File1.getFilePath()); + for (const auto &Item : OnlyRange1) + outs() << formatv(" {0}\n", Label, Item); + } + if (!OnlyRange2.empty()) { + HasDiff = true; + outs() << formatv(" {0} {1}(s) only in ({2})\n", OnlyRange2.size(), + File2.getFilePath()); + for (const auto &Item : OnlyRange2) + outs() << formatv(" {0}\n", Item); + } + return HasDiff; } Error DiffStyle::diffSuperBlock() { @@ -299,8 +326,16 @@ Error DiffStyle::diffStringTable() { auto &ST1 = *ExpectedST1; auto &ST2 = *ExpectedST2; - HasDiff |= diffAndPrint("Stream Size", File1, File2, ST1.getByteSize(), - ST2.getByteSize()); + if (ST1.getByteSize() != ST2.getByteSize()) { + outs() << " Stream Size\n"; + outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(), + ST1.getByteSize()); + outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(), + ST2.getByteSize()); + outs() << formatv(" Difference: {0} bytes\n", + AbsoluteDifference(ST1.getByteSize(), ST2.getByteSize())); + HasDiff = true; + } HasDiff |= diffAndPrint("Hash Version", File1, File2, ST1.getHashVersion(), ST1.getHashVersion()); HasDiff |= diffAndPrint("Signature", File1, File2, ST1.getSignature(), @@ -351,22 +386,21 @@ Error DiffStyle::diffStringTable() { SmallVector OnlyP; SmallVector OnlyQ; - + auto End1 = std::remove(Strings1.begin(), Strings1.end(), ""); + auto End2 = std::remove(Strings2.begin(), Strings2.end(), ""); + uint32_t Empty1 = std::distance(End1, Strings1.end()); + uint32_t Empty2 = std::distance(End2, Strings2.end()); + Strings1.erase(End1, Strings1.end()); + Strings2.erase(End2, Strings2.end()); set_differences(Strings1, Strings2, &OnlyP, &OnlyQ); - - if (!OnlyP.empty()) { - HasDiff = true; - outs() << formatv(" {0} String(s) only in ({1})\n", OnlyP.size(), - File1.getFilePath()); - for (auto Item : OnlyP) - outs() << formatv(" {2}\n", Item); - } - if (!OnlyQ.empty()) { - HasDiff = true; - outs() << formatv(" {0} String(s) only in ({1})\n", OnlyQ.size(), - File2.getFilePath()); - for (auto Item : OnlyQ) - outs() << formatv(" {2}\n", Item); + printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "String"); + + if (Empty1 != Empty2) { + PDBFile &MoreF = (Empty1 > Empty2) ? File1 : File2; + PDBFile &LessF = (Empty1 < Empty2) ? File1 : File2; + uint32_t Difference = AbsoluteDifference(Empty1, Empty2); + outs() << formatv(" {0} had {1} more empty strings than {2}\n", + MoreF.getFilePath(), Difference, LessF.getFilePath()); } } if (!HasDiff) @@ -376,7 +410,60 @@ Error DiffStyle::diffStringTable() { Error DiffStyle::diffFreePageMap() { return Error::success(); } -Error DiffStyle::diffInfoStream() { return Error::success(); } +Error DiffStyle::diffInfoStream() { + auto ExpectedInfo1 = File1.getPDBInfoStream(); + auto ExpectedInfo2 = File2.getPDBInfoStream(); + + outs() << "PDB Stream: Searching for differences...\n"; + bool Has1 = !!ExpectedInfo1; + bool Has2 = !!ExpectedInfo2; + if (!(Has1 && Has2)) { + if (Has1 != Has2) + outs() << formatv("{0} does not have a PDB Stream!\n", + Has1 ? File1.getFilePath() : File2.getFilePath()); + consumeError(ExpectedInfo2.takeError()); + consumeError(ExpectedInfo2.takeError()); + return Error::success(); + } + + bool HasDiff = false; + auto &IS1 = *ExpectedInfo1; + auto &IS2 = *ExpectedInfo2; + if (IS1.getStreamSize() != IS2.getStreamSize()) { + outs() << " Stream Size\n"; + outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(), + IS1.getStreamSize()); + outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(), + IS2.getStreamSize()); + outs() << formatv( + " Difference: {0} bytes\n", + AbsoluteDifference(IS1.getStreamSize(), IS2.getStreamSize())); + HasDiff = true; + } + HasDiff |= diffAndPrint("Age", File1, File2, IS1.getAge(), IS2.getAge()); + HasDiff |= diffAndPrint("Guid", File1, File2, IS1.getGuid(), IS2.getGuid()); + HasDiff |= diffAndPrint("Signature", File1, File2, IS1.getSignature(), + IS2.getSignature()); + HasDiff |= + diffAndPrint("Version", File1, File2, IS1.getVersion(), IS2.getVersion()); + HasDiff |= diffAndPrint("Named Stream Byte Size", File1, File2, + IS1.getNamedStreamMapByteSize(), + IS2.getNamedStreamMapByteSize()); + SmallVector NS1; + SmallVector NS2; + for (const auto &X : IS1.getNamedStreams().entries()) + NS1.push_back(X.getKey()); + for (const auto &X : IS2.getNamedStreams().entries()) + NS2.push_back(X.getKey()); + SmallVector OnlyP; + SmallVector OnlyQ; + set_differences(NS1, NS2, &OnlyP, &OnlyQ); + printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "Named Streams"); + if (!HasDiff) + outs() << "PDB Stream: No differences detected!\n"; + + return Error::success(); +} Error DiffStyle::diffDbiStream() { return Error::success(); } -- 2.50.1