From 3324d0bf04207405bda200718d3236e370ae79cb Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Fri, 7 Jul 2017 18:45:37 +0000 Subject: [PATCH] [llvm-pdbutil] Improve diff mode. We're getting to the point that some MS tools (e.g. DIA) can recognize our PDBs but others (e.g. link.exe) cannot. I think the way forward is to improve our tooling to help us find differences more easily. For example, if we can compile the same program with clang-cl and cl and have a tool tell us all the places where the PDBs differ, this could tell us what we're doing wrong. It's tricky though, because there are a lot of "benign" differences in a PDB. For example, if the string table in one PDB consists of "foo" followed by "bar" and in the other PDB it consists of "bar" followed by "foo", this is not necessarily a critical difference, as long as the uses of these strings also refer to the correct location. On the other hand, if the second PDB doesn't even contain the string "foo" at all, this is a critical difference. diff mode has been in llvm-pdbutil for quite a while, but because of the above challenge along with some others, it's been hard to make it useful. I think this patch addresses that. It looks for all the same things, but it now prints the output in tabular format (carefully formatted and aligned into tables and fields), and it highlights critical differences in red, non-critical differences in yellow, and identical fields in green. This makes it easy to spot the places we differ, and the general concept of outputting arbitrary fields in tabular format can be extended to provide analysis into many of the different types of information that show up in a PDB. Differential Revision: https://reviews.llvm.org/D35039 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@307421 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../DebugInfo/PDB/Native/NamedStreamMap.h | 2 +- .../DebugInfo/PDB/Native/PDBStringTable.h | 1 - lib/DebugInfo/PDB/Native/PDBStringTable.cpp | 2 +- tools/llvm-pdbutil/CMakeLists.txt | 1 + tools/llvm-pdbutil/Diff.cpp | 568 ++++++++++-------- tools/llvm-pdbutil/DiffPrinter.cpp | 106 ++++ tools/llvm-pdbutil/DiffPrinter.h | 158 +++++ tools/llvm-pdbutil/FormatUtil.cpp | 11 + tools/llvm-pdbutil/FormatUtil.h | 1 + 9 files changed, 586 insertions(+), 264 deletions(-) create mode 100644 tools/llvm-pdbutil/DiffPrinter.cpp create mode 100644 tools/llvm-pdbutil/DiffPrinter.h diff --git a/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h b/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h index 25f66240a6a..17a82b7ce12 100644 --- a/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h +++ b/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h @@ -44,7 +44,7 @@ public: bool get(StringRef Stream, uint32_t &StreamNo) const; void set(StringRef Stream, uint32_t StreamNo); void remove(StringRef Stream); - + const StringMap &getStringMap() const { return Mapping; } iterator_range> entries() const; private: diff --git a/include/llvm/DebugInfo/PDB/Native/PDBStringTable.h b/include/llvm/DebugInfo/PDB/Native/PDBStringTable.h index 86ef1136b41..29167c966d4 100644 --- a/include/llvm/DebugInfo/PDB/Native/PDBStringTable.h +++ b/include/llvm/DebugInfo/PDB/Native/PDBStringTable.h @@ -56,7 +56,6 @@ private: const PDBStringTableHeader *Header = nullptr; codeview::DebugStringTableSubsectionRef Strings; FixedStreamArray IDs; - uint32_t ByteSize = 0; uint32_t NameCount = 0; }; diff --git a/lib/DebugInfo/PDB/Native/PDBStringTable.cpp b/lib/DebugInfo/PDB/Native/PDBStringTable.cpp index f9f8ac219d3..acd45f7a621 100644 --- a/lib/DebugInfo/PDB/Native/PDBStringTable.cpp +++ b/lib/DebugInfo/PDB/Native/PDBStringTable.cpp @@ -21,7 +21,7 @@ using namespace llvm; using namespace llvm::support; using namespace llvm::pdb; -uint32_t PDBStringTable::getByteSize() const { return ByteSize; } +uint32_t PDBStringTable::getByteSize() const { return Header->ByteSize; } uint32_t PDBStringTable::getNameCount() const { return NameCount; } uint32_t PDBStringTable::getHashVersion() const { return Header->HashVersion; } uint32_t PDBStringTable::getSignature() const { return Header->Signature; } diff --git a/tools/llvm-pdbutil/CMakeLists.txt b/tools/llvm-pdbutil/CMakeLists.txt index 7a3245424ef..bc28e6bdd7e 100644 --- a/tools/llvm-pdbutil/CMakeLists.txt +++ b/tools/llvm-pdbutil/CMakeLists.txt @@ -11,6 +11,7 @@ add_llvm_tool(llvm-pdbutil Analyze.cpp BytesOutputStyle.cpp Diff.cpp + DiffPrinter.cpp DumpOutputStyle.cpp llvm-pdbutil.cpp FormatUtil.cpp diff --git a/tools/llvm-pdbutil/Diff.cpp b/tools/llvm-pdbutil/Diff.cpp index 9b38ae1d603..eda0ce275af 100644 --- a/tools/llvm-pdbutil/Diff.cpp +++ b/tools/llvm-pdbutil/Diff.cpp @@ -9,9 +9,14 @@ #include "Diff.h" +#include "DiffPrinter.h" +#include "FormatUtil.h" #include "StreamUtil.h" #include "llvm-pdbutil.h" +#include "llvm/ADT/StringSet.h" + +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/Formatters.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" @@ -49,47 +54,6 @@ template <> struct format_provider { 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 = nullptr) { - 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) {} @@ -136,300 +100,382 @@ Error DiffStyle::dump() { return Error::success(); } -template -static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, T V1, - T V2) { - if (V1 == V2) { - outs() << formatv(" {0}: No differences detected!\n", Label); - return false; - } - - 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 diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, - ArrayRef V1, ArrayRef V2) { - if (V1 == V2) { - outs() << formatv(" {0}: No differences detected!\n", Label); - return false; - } - - outs().indent(2) << Label << "\n"; - outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), - make_range(V1.begin(), V1.end())); - outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), - make_range(V2.begin(), V2.end())); - 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; +static std::string shortFilePath(StringRef Path, uint32_t Width) { + if (Path.size() <= Width) + return Path; + Path = Path.take_back(Width - 3); + return std::string("...") + Path.str(); } Error DiffStyle::diffSuperBlock() { - outs() << "MSF Super Block: Searching for differences...\n"; - bool Diffs = false; - - Diffs |= diffAndPrint("Block Size", File1, File2, File1.getBlockSize(), - File2.getBlockSize()); - Diffs |= diffAndPrint("Block Count", File1, File2, File1.getBlockCount(), - File2.getBlockCount()); - Diffs |= diffAndPrint("Unknown 1", File1, File2, File1.getUnknown1(), - File2.getUnknown1()); - if (!Diffs) - outs() << "MSF Super Block: No differences detected...\n"; + DiffPrinter D(2, "MSF Super Block", 16, 20, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + shortFilePath(File1.getFilePath(), 18), + shortFilePath(File2.getFilePath(), 18)); + D.print("Block Size", File1.getBlockSize(), File2.getBlockSize()); + D.print("Block Count", File1.getBlockCount(), File2.getBlockCount()); + D.print("Unknown 1", File1.getUnknown1(), File2.getUnknown1()); + D.print("Directory Size", File1.getNumDirectoryBytes(), + File2.getNumDirectoryBytes()); return Error::success(); } Error DiffStyle::diffStreamDirectory() { + DiffPrinter D(2, "Stream Directory", 30, 20, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + shortFilePath(File1.getFilePath(), 18), + shortFilePath(File2.getFilePath(), 18)); + SmallVector P; SmallVector Q; - discoverStreamPurposes(File1, P); - discoverStreamPurposes(File2, Q); - outs() << "Stream Directory: Searching for differences...\n"; - - bool HasDifferences = false; + discoverStreamPurposes(File1, P, 28); + discoverStreamPurposes(File2, Q, 28); + D.print("Stream Count", File1.getNumStreams(), File2.getNumStreams()); auto PI = to_vector<32>(enumerate(P)); auto QI = to_vector<32>(enumerate(Q)); - typedef decltype(PI) ContainerType; - typedef typename ContainerType::value_type value_type; - - auto Comparator = [](const value_type &I1, const value_type &I2) { - return I1.value() < I2.value(); - }; + // Scan all streams in the left hand side, looking for ones that are also + // in the right. Each time we find one, remove it. When we're done, Q + // should contain all the streams that are in the right but not in the left. + for (const auto &P : PI) { + typedef decltype(PI) ContainerType; + typedef typename ContainerType::value_type value_type; - decltype(PI) OnlyP; - decltype(QI) OnlyQ; - decltype(PI) Common; + auto Iter = llvm::find_if( + QI, [P](const value_type &V) { return V.value() == P.value(); }); - set_differences(PI, QI, &OnlyP, &OnlyQ, &Common, Comparator); - - if (!OnlyP.empty()) { - HasDifferences = true; - outs().indent(2) << formatv("{0} Stream(s) only in ({1})\n", OnlyP.size(), - File1.getFilePath()); - for (auto &Item : OnlyP) { - outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(), - Item.value()); + if (Iter == QI.end()) { + D.printExplicit(P.value(), DiffResult::DIFFERENT, P.index(), + "(not present)"); + continue; } - } - if (!OnlyQ.empty()) { - HasDifferences = true; - outs().indent(2) << formatv("{0} Streams(s) only in ({1})\n", OnlyQ.size(), - File2.getFilePath()); - for (auto &Item : OnlyQ) { - outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(), - Item.value()); - } + D.print(P.value(), P.index(), Iter->index()); + QI.erase(Iter); } - if (!Common.empty()) { - outs().indent(2) << formatv("Found {0} common streams. Searching for " - "intra-stream differences.\n", - Common.size()); - bool HasCommonDifferences = false; - for (const auto &Left : Common) { - // Left was copied from the first range so its index refers to a stream - // index in the first file. Find the corresponding stream index in the - // second file. - auto Range = - std::equal_range(QI.begin(), QI.end(), Left, - [](const value_type &L, const value_type &R) { - return L.value() < R.value(); - }); - const auto &Right = *Range.first; - assert(Left.value() == Right.value()); - uint32_t LeftSize = File1.getStreamByteSize(Left.index()); - uint32_t RightSize = File2.getStreamByteSize(Right.index()); - if (LeftSize != RightSize) { - HasDifferences = true; - HasCommonDifferences = true; - outs().indent(4) << formatv("{0} ({1}: {2} bytes, {3}: {4} bytes)\n", - Left.value(), File1.getFilePath(), LeftSize, - File2.getFilePath(), RightSize); - } - } - if (!HasCommonDifferences) - outs().indent(2) << "Common Streams: No differences detected!\n"; + + for (const auto &Q : QI) { + D.printExplicit(Q.value(), DiffResult::DIFFERENT, "(not present)", + Q.index()); } - if (!HasDifferences) - outs() << "Stream Directory: No differences detected!\n"; return Error::success(); } Error DiffStyle::diffStringTable() { + DiffPrinter D(2, "String Table", 30, 20, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + shortFilePath(File1.getFilePath(), 18), + shortFilePath(File2.getFilePath(), 18)); + 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()); - } - } + std::string Count1 = Has1 ? llvm::utostr(ExpectedST1->getNameCount()) + : "(string table not present)"; + std::string Count2 = Has2 ? llvm::utostr(ExpectedST2->getNameCount()) + : "(string table not present)"; + D.print("Number of Strings", Count1, Count2); + + if (!Has1 || !Has2) { consumeError(ExpectedST1.takeError()); consumeError(ExpectedST2.takeError()); return Error::success(); } - bool HasDiff = false; auto &ST1 = *ExpectedST1; auto &ST2 = *ExpectedST2; - 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(), - ST1.getSignature()); + D.print("Hash Version", ST1.getHashVersion(), ST2.getHashVersion()); + D.print("Byte Size", ST1.getByteSize(), ST2.getByteSize()); + D.print("Signature", ST1.getSignature(), ST2.getSignature()); // Both have a valid string table, dive in and compare individual strings. auto IdList1 = ST1.name_ids(); auto IdList2 = ST2.name_ids(); - std::vector Strings1, Strings2; - Strings1.reserve(IdList1.size()); - Strings2.reserve(IdList2.size()); + StringSet<> LS; + StringSet<> RS; + uint32_t Empty1 = 0; + uint32_t Empty2 = 0; for (auto ID : IdList1) { auto S = ST1.getStringForID(ID); if (!S) return S.takeError(); - Strings1.push_back(*S); + if (S->empty()) + ++Empty1; + else + LS.insert(*S); } for (auto ID : IdList2) { auto S = ST2.getStringForID(ID); if (!S) return S.takeError(); - Strings2.push_back(*S); + if (S->empty()) + ++Empty2; + else + RS.insert(*S); } + D.print("Empty Strings", Empty1, Empty2); + + for (const auto &S : LS) { + auto R = RS.find(S.getKey()); + std::string Truncated = truncateStringMiddle(S.getKey(), 28); + uint32_t I = cantFail(ST1.getIDForString(S.getKey())); + if (R == RS.end()) { + D.printExplicit(Truncated, DiffResult::DIFFERENT, I, "(not present)"); + continue; + } - 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); - 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()); + uint32_t J = cantFail(ST2.getIDForString(R->getKey())); + D.print(Truncated, I, J); + RS.erase(R); + } + + for (const auto &S : RS) { + auto L = LS.find(S.getKey()); + std::string Truncated = truncateStringMiddle(S.getKey(), 28); + uint32_t J = cantFail(ST2.getIDForString(S.getKey())); + if (L == LS.end()) { + D.printExplicit(Truncated, DiffResult::DIFFERENT, "(not present)", J); + continue; + } + + uint32_t I = cantFail(ST1.getIDForString(L->getKey())); + D.print(Truncated, I, J); } - if (!HasDiff) - outs() << "String Table: No differences detected!\n"; return Error::success(); } Error DiffStyle::diffFreePageMap() { return Error::success(); } Error DiffStyle::diffInfoStream() { + DiffPrinter D(2, "PDB Stream", 22, 40, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + shortFilePath(File1.getFilePath(), 38), + shortFilePath(File2.getFilePath(), 38)); + 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()); + std::string L = Has1 ? "(present)" : "(not present)"; + std::string R = Has2 ? "(present)" : "(not present)"; + D.print("Stream", L, R); + + consumeError(ExpectedInfo1.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; + D.print("Stream Size", IS1.getStreamSize(), IS2.getStreamSize()); + D.print("Age", IS1.getAge(), IS2.getAge()); + D.print("Guid", IS1.getGuid(), IS2.getGuid()); + D.print("Signature", IS1.getSignature(), IS2.getSignature()); + D.print("Version", IS1.getVersion(), IS2.getVersion()); + D.diffUnorderedArray("Feature", IS1.getFeatureSignatures(), + IS2.getFeatureSignatures()); + D.print("Named Stream Size", IS1.getNamedStreamMapByteSize(), + IS2.getNamedStreamMapByteSize()); + StringMap NSL = IS1.getNamedStreams().getStringMap(); + StringMap NSR = IS2.getNamedStreams().getStringMap(); + D.diffUnorderedMap("Named Stream", NSL, NSR); + return Error::success(); +} + +struct StreamNumberProvider { + static DiffResult compare(uint16_t L, uint16_t R) { + if (L == R) + return DiffResult::IDENTICAL; + bool LP = L != kInvalidStreamIndex; + bool RP = R != kInvalidStreamIndex; + if (LP != RP) + return DiffResult::DIFFERENT; + return DiffResult::EQUIVALENT; } - 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("Features", File1, File2, IS1.getFeatureSignatures(), - IS2.getFeatureSignatures()); - 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(); + static std::string format(uint16_t SN) { + if (SN == kInvalidStreamIndex) + return "(not present)"; + return formatv("{0}", SN).str(); + } +}; + +struct ModiProvider { + DiffResult compare(Optional L, Optional R) { + if (L == R) + return DiffResult::IDENTICAL; + if (L.hasValue() != R.hasValue()) + return DiffResult::DIFFERENT; + return DiffResult::EQUIVALENT; + } + + std::string format(Optional Modi) { + if (!Modi.hasValue()) + return "(not present)"; + return formatv("{0}", *Modi).str(); + } +}; + +struct StringProvider { + DiffResult compare(StringRef L, StringRef R) { + IdenticalDiffProvider I; + return I.compare(L, R); + } + + std::string format(StringRef S) { + if (S.empty()) + return "(empty)"; + return S; + } +}; + +static std::vector> +getModuleDescriptors(const DbiModuleList &ML) { + std::vector> List; + List.reserve(ML.getModuleCount()); + for (uint32_t I = 0; I < ML.getModuleCount(); ++I) + List.emplace_back(I, ML.getModuleDescriptor(I)); + return List; } -Error DiffStyle::diffDbiStream() { return Error::success(); } +Error DiffStyle::diffDbiStream() { + DiffPrinter D(2, "DBI Stream", 40, 30, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + shortFilePath(File1.getFilePath(), 38), + shortFilePath(File2.getFilePath(), 38)); + + auto ExpectedDbi1 = File1.getPDBDbiStream(); + auto ExpectedDbi2 = File2.getPDBDbiStream(); + + bool Has1 = !!ExpectedDbi1; + bool Has2 = !!ExpectedDbi2; + if (!(Has1 && Has2)) { + std::string L = Has1 ? "(present)" : "(not present)"; + std::string R = Has2 ? "(present)" : "(not present)"; + D.print("Stream", L, R); + + consumeError(ExpectedDbi1.takeError()); + consumeError(ExpectedDbi2.takeError()); + return Error::success(); + } + + auto &DL = *ExpectedDbi1; + auto &DR = *ExpectedDbi2; + + D.print("Dbi Version", (uint32_t)DL.getDbiVersion(), + (uint32_t)DR.getDbiVersion()); + D.print("Age", DL.getAge(), DR.getAge()); + D.print("Machine", (uint16_t)DL.getMachineType(), + (uint16_t)DR.getMachineType()); + D.print("Flags", DL.getFlags(), DR.getFlags()); + D.print("Build Major", DL.getBuildMajorVersion(), DR.getBuildMajorVersion()); + D.print("Build Minor", DL.getBuildMinorVersion(), DR.getBuildMinorVersion()); + D.print("Build Number", DL.getBuildNumber(), DR.getBuildNumber()); + D.print("PDB DLL Version", DL.getPdbDllVersion(), DR.getPdbDllVersion()); + D.print("PDB DLL RBLD", DL.getPdbDllRbld(), DR.getPdbDllRbld()); + D.print("DBG (FPO)", + DL.getDebugStreamIndex(DbgHeaderType::FPO), + DR.getDebugStreamIndex(DbgHeaderType::FPO)); + D.print( + "DBG (Exception)", DL.getDebugStreamIndex(DbgHeaderType::Exception), + DR.getDebugStreamIndex(DbgHeaderType::Exception)); + D.print("DBG (Fixup)", + DL.getDebugStreamIndex(DbgHeaderType::Fixup), + DR.getDebugStreamIndex(DbgHeaderType::Fixup)); + D.print( + "DBG (OmapToSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapToSrc), + DR.getDebugStreamIndex(DbgHeaderType::OmapToSrc)); + D.print( + "DBG (OmapFromSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapFromSrc), + DR.getDebugStreamIndex(DbgHeaderType::OmapFromSrc)); + D.print( + "DBG (SectionHdr)", DL.getDebugStreamIndex(DbgHeaderType::SectionHdr), + DR.getDebugStreamIndex(DbgHeaderType::SectionHdr)); + D.print( + "DBG (TokenRidMap)", DL.getDebugStreamIndex(DbgHeaderType::TokenRidMap), + DR.getDebugStreamIndex(DbgHeaderType::TokenRidMap)); + D.print("DBG (Xdata)", + DL.getDebugStreamIndex(DbgHeaderType::Xdata), + DR.getDebugStreamIndex(DbgHeaderType::Xdata)); + D.print("DBG (Pdata)", + DL.getDebugStreamIndex(DbgHeaderType::Pdata), + DR.getDebugStreamIndex(DbgHeaderType::Pdata)); + D.print("DBG (NewFPO)", + DL.getDebugStreamIndex(DbgHeaderType::NewFPO), + DR.getDebugStreamIndex(DbgHeaderType::NewFPO)); + D.print( + "DBG (SectionHdrOrig)", + DL.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig), + DR.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)); + D.print("Globals Stream", + DL.getGlobalSymbolStreamIndex(), + DR.getGlobalSymbolStreamIndex()); + D.print("Publics Stream", + DL.getPublicSymbolStreamIndex(), + DR.getPublicSymbolStreamIndex()); + D.print("Symbol Records", DL.getSymRecordStreamIndex(), + DR.getSymRecordStreamIndex()); + D.print("Has CTypes", DL.hasCTypes(), DR.hasCTypes()); + D.print("Is Incrementally Linked", DL.isIncrementallyLinked(), + DR.isIncrementallyLinked()); + D.print("Is Stripped", DL.isStripped(), DR.isStripped()); + const DbiModuleList &ML = DL.modules(); + const DbiModuleList &MR = DR.modules(); + D.print("Module Count", ML.getModuleCount(), MR.getModuleCount()); + D.print("Source File Count", ML.getSourceFileCount(), + MR.getSourceFileCount()); + auto MDL = getModuleDescriptors(ML); + auto MDR = getModuleDescriptors(MR); + // Scan all module descriptors from the left, and look for corresponding + // module descriptors on the right. + for (const auto &L : MDL) { + D.printFullRow( + truncateQuotedNameFront("Module", L.second.getModuleName(), 70)); + + auto Iter = llvm::find_if( + MDR, [&L](const std::pair &R) { + return R.second.getModuleName().equals_lower( + L.second.getModuleName()); + }); + if (Iter == MDR.end()) { + // We didn't find this module at all on the right. Just print one row + // and continue. + D.print("- Modi", L.first, None); + continue; + } + + // We did find this module. Go through and compare each field. + const auto &R = *Iter; + D.print("- Modi", L.first, R.first); + D.print("- Obj File Name", + shortFilePath(L.second.getObjFileName(), 28), + shortFilePath(R.second.getObjFileName(), 28)); + D.print("- Debug Stream", + L.second.getModuleStreamIndex(), + R.second.getModuleStreamIndex()); + D.print("- C11 Byte Size", L.second.getC11LineInfoByteSize(), + R.second.getC11LineInfoByteSize()); + D.print("- C13 Byte Size", L.second.getC13LineInfoByteSize(), + R.second.getC13LineInfoByteSize()); + D.print("- # of files", L.second.getNumberOfFiles(), + R.second.getNumberOfFiles()); + D.print("- Pdb File Path Index", L.second.getPdbFilePathNameIndex(), + R.second.getPdbFilePathNameIndex()); + D.print("- Source File Name Index", L.second.getSourceFileNameIndex(), + R.second.getSourceFileNameIndex()); + D.print("- Symbol Byte Size", L.second.getSymbolDebugInfoByteSize(), + R.second.getSymbolDebugInfoByteSize()); + MDR.erase(Iter); + } + + return Error::success(); +} Error DiffStyle::diffSectionContribs() { return Error::success(); } diff --git a/tools/llvm-pdbutil/DiffPrinter.cpp b/tools/llvm-pdbutil/DiffPrinter.cpp new file mode 100644 index 00000000000..b608d54d0e2 --- /dev/null +++ b/tools/llvm-pdbutil/DiffPrinter.cpp @@ -0,0 +1,106 @@ + +#include "DiffPrinter.h" + +#include "llvm/Support/FormatAdapters.h" + +using namespace llvm; +using namespace llvm::pdb; + +static void setColor(llvm::raw_ostream &OS, DiffResult Result) { + switch (Result) { + case DiffResult::IDENTICAL: + OS.changeColor(raw_ostream::Colors::GREEN, false); + break; + case DiffResult::EQUIVALENT: + OS.changeColor(raw_ostream::Colors::YELLOW, true); + break; + default: + OS.changeColor(raw_ostream::Colors::RED, false); + break; + } +} + +DiffPrinter::DiffPrinter(uint32_t Indent, StringRef Header, + uint32_t PropertyWidth, uint32_t FieldWidth, + raw_ostream &Stream) + : Indent(Indent), PropertyWidth(PropertyWidth), FieldWidth(FieldWidth), + OS(Stream) { + printHeaderRow(); + printFullRow(Header); +} + +DiffPrinter::~DiffPrinter() {} + +void DiffPrinter::printFullRow(StringRef Text) { + newLine(); + printField(Text, DiffResult::UNSPECIFIED, AlignStyle::Center, + PropertyWidth + 1 + FieldWidth + 1 + FieldWidth); + printSeparatorRow(); +} + +void DiffPrinter::printSeparatorRow() { + newLine(); + OS << formatv("{0}", fmt_repeat('-', PropertyWidth)); + OS << '+'; + OS << formatv("{0}", fmt_repeat('-', FieldWidth)); + OS << '+'; + OS << formatv("{0}", fmt_repeat('-', FieldWidth)); + OS << '|'; +} + +void DiffPrinter::printHeaderRow() { + newLine('-'); + OS << formatv("{0}", fmt_repeat('-', PropertyWidth + 2 * FieldWidth + 3)); +} + +void DiffPrinter::newLine(char InitialChar) { + OS << "\n"; + OS.indent(Indent) << InitialChar; +} + +void DiffPrinter::printExplicit(StringRef Property, DiffResult C, + StringRef Left, StringRef Right) { + newLine(); + printField(Property, DiffResult::UNSPECIFIED, AlignStyle::Right, + PropertyWidth); + printField(Left, C, AlignStyle::Center, FieldWidth); + printField(Right, C, AlignStyle::Center, FieldWidth); + printSeparatorRow(); +} + +void DiffPrinter::printSame(StringRef Property, StringRef Value) { + newLine(); + printField(Property, DiffResult::UNSPECIFIED, AlignStyle::Right, + PropertyWidth); + printField(Value, DiffResult::IDENTICAL, AlignStyle::Center, + FieldWidth + 1 + FieldWidth); + printSeparatorRow(); +} + +void DiffPrinter::printDifferent(StringRef Property, StringRef Left, + StringRef Right) { + newLine(); + printField(Property, DiffResult::UNSPECIFIED, AlignStyle::Right, + PropertyWidth); + printField(Left, DiffResult::DIFFERENT, AlignStyle::Center, FieldWidth); + printField(Right, DiffResult::DIFFERENT, AlignStyle::Center, FieldWidth); + printSeparatorRow(); +} + +void DiffPrinter::printField(StringRef Value, DiffResult C, AlignStyle Style, + uint32_t Width) { + if (Style == AlignStyle::Right) + --Width; + + std::string FormattedItem = + formatv("{0}", fmt_align(Value, Style, Width)).str(); + if (C != DiffResult::UNSPECIFIED) { + setColor(OS, C); + OS << FormattedItem; + OS.resetColor(); + } else + OS << FormattedItem; + if (Style == AlignStyle::Right) + OS << ' '; + OS << '|'; +} diff --git a/tools/llvm-pdbutil/DiffPrinter.h b/tools/llvm-pdbutil/DiffPrinter.h new file mode 100644 index 00000000000..8a4527c6274 --- /dev/null +++ b/tools/llvm-pdbutil/DiffPrinter.h @@ -0,0 +1,158 @@ +//===- DiffPrinter.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_DIFFPRINTER_H +#define LLVM_TOOLS_LLVMPDBDUMP_DIFFPRINTER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +namespace llvm { +namespace pdb { + +class PDBFile; + +enum class DiffResult { UNSPECIFIED, IDENTICAL, EQUIVALENT, DIFFERENT }; + +struct IdenticalDiffProvider { + template + DiffResult compare(const T &Left, const U &Right) { + return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::DIFFERENT; + } + + template std::string format(const T &Item) { + return formatv("{0}", Item).str(); + } +}; + +struct EquivalentDiffProvider { + template + DiffResult compare(const T &Left, const U &Right) { + return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::EQUIVALENT; + } + + template std::string format(const T &Item) { + return formatv("{0}", Item).str(); + } +}; + +class DiffPrinter { +public: + DiffPrinter(uint32_t Indent, StringRef Header, uint32_t PropertyWidth, + uint32_t FieldWidth, raw_ostream &Stream); + ~DiffPrinter(); + + template struct Identical {}; + + template + void print(StringRef Property, const T &Left, const U &Right, + Provider P = Provider()) { + std::string L = P.format(Left); + std::string R = P.format(Right); + + DiffResult Result = P.compare(Left, Right); + printExplicit(Property, Result, L, R); + } + + void printExplicit(StringRef Property, DiffResult C, StringRef Left, + StringRef Right); + + template + void printExplicit(StringRef Property, DiffResult C, const T &Left, + const U &Right) { + std::string L = formatv("{0}", Left).str(); + std::string R = formatv("{0}", Right).str(); + printExplicit(Property, C, StringRef(L), StringRef(R)); + } + + template + void diffUnorderedArray(StringRef Property, ArrayRef Left, + ArrayRef Right) { + std::unordered_set LS(Left.begin(), Left.end()); + std::unordered_set RS(Right.begin(), Right.end()); + std::string Count1 = formatv("{0} element(s)", Left.size()); + std::string Count2 = formatv("{0} element(s)", Right.size()); + print(std::string(Property) + "s (set)", Count1, Count2); + for (const auto &L : LS) { + auto Iter = RS.find(L); + std::string Text = formatv("{0}", L).str(); + if (Iter == RS.end()) { + print(Property, Text, "(not present)"); + continue; + } + print(Property, Text, Text); + RS.erase(Iter); + } + for (const auto &R : RS) { + auto Iter = LS.find(R); + std::string Text = formatv("{0}", R).str(); + if (Iter == LS.end()) { + print(Property, "(not present)", Text); + continue; + } + print(Property, Text, Text); + } + } + + template + void diffUnorderedMap(StringRef Property, const StringMap &Left, + const StringMap &Right, + ValueProvider P = ValueProvider()) { + StringMap RightCopy(Right); + + std::string Count1 = formatv("{0} element(s)", Left.size()); + std::string Count2 = formatv("{0} element(s)", Right.size()); + print(std::string(Property) + "s (map)", Count1, Count2); + + for (const auto &L : Left) { + auto Iter = RightCopy.find(L.getKey()); + if (Iter == RightCopy.end()) { + printExplicit(L.getKey(), DiffResult::DIFFERENT, L.getValue(), + "(not present)"); + continue; + } + + print(L.getKey(), L.getValue(), Iter->getValue(), P); + RightCopy.erase(Iter); + } + + for (const auto &R : RightCopy) { + printExplicit(R.getKey(), DiffResult::DIFFERENT, "(not present)", + R.getValue()); + } + } + + void printFullRow(StringRef Text); + +private: + void printSame(StringRef Property, StringRef Value); + void printDifferent(StringRef Property, StringRef Left, StringRef Right); + + void printHeaderRow(); + void printSeparatorRow(); + void newLine(char InitialChar = '|'); + void printField(StringRef Value, DiffResult C, AlignStyle Style, + uint32_t Width); + + uint32_t Indent; + uint32_t PropertyWidth; + uint32_t FieldWidth; + raw_ostream &OS; +}; +} // namespace pdb +} // namespace llvm + +#endif diff --git a/tools/llvm-pdbutil/FormatUtil.cpp b/tools/llvm-pdbutil/FormatUtil.cpp index 46a1774f166..02030272dd4 100644 --- a/tools/llvm-pdbutil/FormatUtil.cpp +++ b/tools/llvm-pdbutil/FormatUtil.cpp @@ -26,6 +26,17 @@ std::string llvm::pdb::truncateStringBack(StringRef S, uint32_t MaxLen) { return std::string(S) + std::string("..."); } +std::string llvm::pdb::truncateStringMiddle(StringRef S, uint32_t MaxLen) { + if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) + return S; + + assert(MaxLen >= 3); + uint32_t FinalLen = std::min(S.size(), MaxLen - 3); + StringRef Front = S.take_front(FinalLen / 2); + StringRef Back = S.take_back(Front.size()); + return std::string(Front) + std::string("...") + std::string(Back); +} + std::string llvm::pdb::truncateStringFront(StringRef S, uint32_t MaxLen) { if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) return S; diff --git a/tools/llvm-pdbutil/FormatUtil.h b/tools/llvm-pdbutil/FormatUtil.h index 31e4be34958..df32ed9360f 100644 --- a/tools/llvm-pdbutil/FormatUtil.h +++ b/tools/llvm-pdbutil/FormatUtil.h @@ -23,6 +23,7 @@ namespace llvm { namespace pdb { std::string truncateStringBack(StringRef S, uint32_t MaxLen); +std::string truncateStringMiddle(StringRef S, uint32_t MaxLen); std::string truncateStringFront(StringRef S, uint32_t MaxLen); std::string truncateQuotedNameFront(StringRef Label, StringRef Name, uint32_t MaxLen); -- 2.49.0