diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/StringTable.h b/llvm/include/llvm/DebugInfo/PDB/Native/StringTable.h index 399b7a1e9dc8..dd5e30e61827 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/StringTable.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/StringTable.h @@ -30,6 +30,8 @@ public: Error load(BinaryStreamReader &Stream); + uint32_t getByteSize() const; + uint32_t getNameCount() const { return NameCount; } uint32_t getHashVersion() const { return HashVersion; } uint32_t getSignature() const { return Signature; } @@ -42,9 +44,10 @@ public: private: BinaryStreamRef NamesBuffer; FixedStreamArray IDs; - uint32_t Signature; - uint32_t HashVersion; - uint32_t NameCount; + uint32_t ByteSize = 0; + uint32_t Signature = 0; + uint32_t HashVersion = 0; + uint32_t NameCount = 0; }; } // end namespace pdb diff --git a/llvm/lib/DebugInfo/PDB/Native/StringTable.cpp b/llvm/lib/DebugInfo/PDB/Native/StringTable.cpp index 2b13d96f08ec..7e28389b8383 100644 --- a/llvm/lib/DebugInfo/PDB/Native/StringTable.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/StringTable.cpp @@ -20,9 +20,11 @@ using namespace llvm; using namespace llvm::support; using namespace llvm::pdb; -StringTable::StringTable() : Signature(0), HashVersion(0), NameCount(0) {} +StringTable::StringTable() {} Error StringTable::load(BinaryStreamReader &Stream) { + ByteSize = Stream.getLength(); + const StringTableHeader *H; if (auto EC = Stream.readObject(H)) return EC; @@ -56,9 +58,18 @@ Error StringTable::load(BinaryStreamReader &Stream) { if (auto EC = Stream.readInteger(NameCount)) return EC; + + if (Stream.bytesRemaining() > 0) + return make_error(raw_error_code::stream_too_long, + "Unexpected bytes found in string table"); + return Error::success(); } +uint32_t StringTable::getByteSize() const { + return ByteSize; +} + StringRef StringTable::getStringForID(uint32_t ID) const { if (ID == IDs[0]) return StringRef(); diff --git a/llvm/tools/llvm-pdbdump/Diff.cpp b/llvm/tools/llvm-pdbdump/Diff.cpp index 9e90c5af7e60..6ad235a88395 100644 --- a/llvm/tools/llvm-pdbdump/Diff.cpp +++ b/llvm/tools/llvm-pdbdump/Diff.cpp @@ -14,6 +14,7 @@ #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/StringTable.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" @@ -21,6 +22,48 @@ using namespace llvm; using namespace llvm::pdb; +template +using ValueOfRange = llvm::detail::ValueOfRange; + +template +static void set_differences(Range &&R1, Range &&R2, + SmallVectorImpl> *OnlyLeft, + SmallVectorImpl> *OnlyRight, + SmallVectorImpl> *Intersection, Comp Comparator) { + + std::sort(R1.begin(), R1.end(), Comparator); + std::sort(R2.begin(), R2.end(), Comparator); + + if (OnlyLeft) { + OnlyLeft->reserve(R1.size()); + auto End = std::set_difference(R1.begin(), R1.end(), R2.begin(), R2.end(), + OnlyLeft->begin(), Comparator); + OnlyLeft->set_size(std::distance(OnlyLeft->begin(), End)); + } + if (OnlyRight) { + OnlyLeft->reserve(R2.size()); + auto End = std::set_difference(R2.begin(), R2.end(), R1.begin(), R1.end(), + OnlyRight->begin(), Comparator); + OnlyRight->set_size(std::distance(OnlyRight->begin(), End)); + } + if (Intersection) { + Intersection->reserve(std::min(R1.size(), R2.size())); + auto End = std::set_intersection(R1.begin(), R1.end(), R2.begin(), + R2.end(), Intersection->begin(), Comparator); + Intersection->set_size(std::distance(Intersection->begin(), End)); + } +} + +template +static void set_differences(Range &&R1, Range &&R2, + SmallVectorImpl> *OnlyLeft, + SmallVectorImpl> *OnlyRight, + SmallVectorImpl> *Intersection) { + std::less> Comp; + set_differences(std::forward(R1), std::forward(R2), OnlyLeft, OnlyRight, Intersection, Comp); +} + + DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2) : File1(File1), File2(File2) {} @@ -162,26 +205,12 @@ Error DiffStyle::diffStreamDirectory() { auto Comparator = [](const value_type &I1, const value_type &I2) { return I1.value() < I2.value(); }; - std::sort(PI.begin(), PI.end(), Comparator); - std::sort(QI.begin(), QI.end(), Comparator); decltype(PI) OnlyP; decltype(QI) OnlyQ; decltype(PI) Common; - OnlyP.reserve(P.size()); - OnlyQ.reserve(Q.size()); - Common.reserve(Q.size()); - - auto PEnd = std::set_difference(PI.begin(), PI.end(), QI.begin(), QI.end(), - OnlyP.begin(), Comparator); - auto QEnd = std::set_difference(QI.begin(), QI.end(), PI.begin(), PI.end(), - OnlyQ.begin(), Comparator); - auto ComEnd = std::set_intersection(PI.begin(), PI.end(), QI.begin(), - QI.end(), Common.begin(), Comparator); - OnlyP.set_size(std::distance(OnlyP.begin(), PEnd)); - OnlyQ.set_size(std::distance(OnlyQ.begin(), QEnd)); - Common.set_size(std::distance(Common.begin(), ComEnd)); + set_differences(PI, QI, &OnlyP, &OnlyQ, &Common, Comparator); if (!OnlyP.empty()) { HasDifferences = true; @@ -238,7 +267,98 @@ Error DiffStyle::diffStreamDirectory() { return Error::success(); } -Error DiffStyle::diffStringTable() { return Error::success(); } +Error DiffStyle::diffStringTable() { + auto ExpectedST1 = File1.getStringTable(); + auto ExpectedST2 = File2.getStringTable(); + outs() << "String Table: Searching for differences...\n"; + bool Has1 = !!ExpectedST1; + bool Has2 = !!ExpectedST2; + if (!(Has1 && Has2)) { + // If one has a string table and the other doesn't, we can print less output. + if (Has1 != Has2) { + if (Has1) { + outs() << formatv(" {0}: ({1} strings)\n", File1.getFilePath(), ExpectedST1->getNameCount()); + outs() << formatv(" {0}: (string table not present)\n", File2.getFilePath()); + } else { + outs() << formatv(" {0}: (string table not present)\n", File1.getFilePath()); + outs() << formatv(" {0}: ({1})\n", File2.getFilePath(), ExpectedST2->getNameCount()); + } + } + consumeError(ExpectedST1.takeError()); + consumeError(ExpectedST2.takeError()); + return Error::success(); + } + + bool HasDiff = false; + auto &ST1 = *ExpectedST1; + auto &ST2 = *ExpectedST2; + + HasDiff |= diffAndPrint("Stream Size", File1, File2, ST1.getByteSize(), ST2.getByteSize()); + HasDiff |= diffAndPrint("Hash Version", File1, File2, ST1.getHashVersion(), ST1.getHashVersion()); + HasDiff |= diffAndPrint("Signature", File1, File2, ST1.getSignature(), ST1.getSignature()); + + // Both have a valid string table, dive in and compare individual strings. + + auto IdList1 = ST1.name_ids(); + auto IdList2 = ST2.name_ids(); + if (opts::diff::Pedantic) { + // In pedantic mode, we compare index by index (i.e. the strings are in the same order + // in both tables. + uint32_t Max = std::max(IdList1.size(), IdList2.size()); + for (uint32_t I = 0; I < Max; ++I) { + Optional Id1, Id2; + StringRef S1, S2; + if (I < IdList1.size()) { + Id1 = IdList1[I]; + S1 = ST1.getStringForID(*Id1); + } + if (I < IdList2.size()) { + Id2 = IdList2[I]; + S2 = ST2.getStringForID(*Id2); + } + if (Id1 == Id2 && S1 == S2) + continue; + + std::string OutId1 = Id1 ? formatv("{0}", *Id1).str() : "(index not present)"; + std::string OutId2 = Id2 ? formatv("{0}", *Id2).str() : "(index not present)"; + outs() << formatv(" String {0}\n", I); + outs() << formatv(" {0}: Hash - {1}, Value - {2}\n", File1.getFilePath(), OutId1, S1); + outs() << formatv(" {0}: Hash - {1}, Value - {2}\n", File2.getFilePath(), OutId2, S2); + HasDiff = true; + } + } else { + std::vector Strings1, Strings2; + Strings1.reserve(IdList1.size()); + Strings2.reserve(IdList2.size()); + for (auto ID : IdList1) + Strings1.push_back(ST1.getStringForID(ID)); + for (auto ID : IdList2) + Strings2.push_back(ST2.getStringForID(ID)); + + SmallVector OnlyP; + SmallVector OnlyQ; + + set_differences(Strings1, Strings2, &OnlyP, &OnlyQ, nullptr); + + 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); + } + } + if (!HasDiff) + outs() << "String Table: No differences detected!\n"; + return Error::success(); +} Error DiffStyle::diffFreePageMap() { return Error::success(); }