mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-18 18:42:46 +02:00
[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 llvm-svn: 307421
This commit is contained in:
parent
53368afb5b
commit
f0a7a434de
@ -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<uint32_t> &getStringMap() const { return Mapping; }
|
||||
iterator_range<StringMapConstIterator<uint32_t>> entries() const;
|
||||
|
||||
private:
|
||||
|
@ -56,7 +56,6 @@ private:
|
||||
const PDBStringTableHeader *Header = nullptr;
|
||||
codeview::DebugStringTableSubsectionRef Strings;
|
||||
FixedStreamArray<support::ulittle32_t> IDs;
|
||||
uint32_t ByteSize = 0;
|
||||
uint32_t NameCount = 0;
|
||||
};
|
||||
|
||||
|
@ -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; }
|
||||
|
@ -11,6 +11,7 @@ add_llvm_tool(llvm-pdbutil
|
||||
Analyze.cpp
|
||||
BytesOutputStyle.cpp
|
||||
Diff.cpp
|
||||
DiffPrinter.cpp
|
||||
DumpOutputStyle.cpp
|
||||
llvm-pdbutil.cpp
|
||||
FormatUtil.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<PdbRaw_FeatureSig> {
|
||||
|
||||
template <typename R> using ValueOfRange = llvm::detail::ValueOfRange<R>;
|
||||
|
||||
template <typename Range, typename Comp>
|
||||
static void set_differences(Range &&R1, Range &&R2,
|
||||
SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft,
|
||||
SmallVectorImpl<ValueOfRange<Range>> *OnlyRight,
|
||||
SmallVectorImpl<ValueOfRange<Range>> *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 <typename Range>
|
||||
static void
|
||||
set_differences(Range &&R1, Range &&R2,
|
||||
SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft,
|
||||
SmallVectorImpl<ValueOfRange<Range>> *OnlyRight,
|
||||
SmallVectorImpl<ValueOfRange<Range>> *Intersection = nullptr) {
|
||||
std::less<ValueOfRange<Range>> Comp;
|
||||
set_differences(std::forward<Range>(R1), std::forward<Range>(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 <typename T>
|
||||
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 <typename T>
|
||||
static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2,
|
||||
ArrayRef<T> V1, ArrayRef<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(),
|
||||
make_range(V1.begin(), V1.end()));
|
||||
outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(),
|
||||
make_range(V2.begin(), V2.end()));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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<std::string, 32> P;
|
||||
SmallVector<std::string, 32> 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;
|
||||
// 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;
|
||||
|
||||
auto Comparator = [](const value_type &I1, const value_type &I2) {
|
||||
return I1.value() < I2.value();
|
||||
};
|
||||
auto Iter = llvm::find_if(
|
||||
QI, [P](const value_type &V) { return V.value() == P.value(); });
|
||||
|
||||
decltype(PI) OnlyP;
|
||||
decltype(QI) OnlyQ;
|
||||
decltype(PI) Common;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
D.print<EquivalentDiffProvider>(P.value(), P.index(), Iter->index());
|
||||
QI.erase(Iter);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
for (const auto &Q : QI) {
|
||||
D.printExplicit(Q.value(), DiffResult::DIFFERENT, "(not present)",
|
||||
Q.index());
|
||||
}
|
||||
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";
|
||||
}
|
||||
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<StringRef> 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;
|
||||
}
|
||||
|
||||
uint32_t J = cantFail(ST2.getIDForString(R->getKey()));
|
||||
D.print<EquivalentDiffProvider>(Truncated, I, J);
|
||||
RS.erase(R);
|
||||
}
|
||||
|
||||
SmallVector<StringRef, 64> OnlyP;
|
||||
SmallVector<StringRef, 64> 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");
|
||||
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;
|
||||
}
|
||||
|
||||
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 I = cantFail(ST1.getIDForString(L->getKey()));
|
||||
D.print<EquivalentDiffProvider>(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;
|
||||
}
|
||||
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<StringRef, 4> NS1;
|
||||
SmallVector<StringRef, 4> 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<StringRef, 4> OnlyP;
|
||||
SmallVector<StringRef, 4> OnlyQ;
|
||||
set_differences(NS1, NS2, &OnlyP, &OnlyQ);
|
||||
printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "Named Streams");
|
||||
if (!HasDiff)
|
||||
outs() << "PDB Stream: No differences detected!\n";
|
||||
|
||||
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<uint32_t> NSL = IS1.getNamedStreams().getStringMap();
|
||||
StringMap<uint32_t> NSR = IS2.getNamedStreams().getStringMap();
|
||||
D.diffUnorderedMap<EquivalentDiffProvider>("Named Stream", NSL, NSR);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error DiffStyle::diffDbiStream() { 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;
|
||||
}
|
||||
|
||||
static std::string format(uint16_t SN) {
|
||||
if (SN == kInvalidStreamIndex)
|
||||
return "(not present)";
|
||||
return formatv("{0}", SN).str();
|
||||
}
|
||||
};
|
||||
|
||||
struct ModiProvider {
|
||||
DiffResult compare(Optional<uint32_t> L, Optional<uint32_t> R) {
|
||||
if (L == R)
|
||||
return DiffResult::IDENTICAL;
|
||||
if (L.hasValue() != R.hasValue())
|
||||
return DiffResult::DIFFERENT;
|
||||
return DiffResult::EQUIVALENT;
|
||||
}
|
||||
|
||||
std::string format(Optional<uint32_t> 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<std::pair<uint32_t, DbiModuleDescriptor>>
|
||||
getModuleDescriptors(const DbiModuleList &ML) {
|
||||
std::vector<std::pair<uint32_t, DbiModuleDescriptor>> 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() {
|
||||
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<StreamNumberProvider>("DBG (FPO)",
|
||||
DL.getDebugStreamIndex(DbgHeaderType::FPO),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::FPO));
|
||||
D.print<StreamNumberProvider>(
|
||||
"DBG (Exception)", DL.getDebugStreamIndex(DbgHeaderType::Exception),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::Exception));
|
||||
D.print<StreamNumberProvider>("DBG (Fixup)",
|
||||
DL.getDebugStreamIndex(DbgHeaderType::Fixup),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::Fixup));
|
||||
D.print<StreamNumberProvider>(
|
||||
"DBG (OmapToSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapToSrc),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::OmapToSrc));
|
||||
D.print<StreamNumberProvider>(
|
||||
"DBG (OmapFromSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapFromSrc),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::OmapFromSrc));
|
||||
D.print<StreamNumberProvider>(
|
||||
"DBG (SectionHdr)", DL.getDebugStreamIndex(DbgHeaderType::SectionHdr),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::SectionHdr));
|
||||
D.print<StreamNumberProvider>(
|
||||
"DBG (TokenRidMap)", DL.getDebugStreamIndex(DbgHeaderType::TokenRidMap),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::TokenRidMap));
|
||||
D.print<StreamNumberProvider>("DBG (Xdata)",
|
||||
DL.getDebugStreamIndex(DbgHeaderType::Xdata),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::Xdata));
|
||||
D.print<StreamNumberProvider>("DBG (Pdata)",
|
||||
DL.getDebugStreamIndex(DbgHeaderType::Pdata),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::Pdata));
|
||||
D.print<StreamNumberProvider>("DBG (NewFPO)",
|
||||
DL.getDebugStreamIndex(DbgHeaderType::NewFPO),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::NewFPO));
|
||||
D.print<StreamNumberProvider>(
|
||||
"DBG (SectionHdrOrig)",
|
||||
DL.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig),
|
||||
DR.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig));
|
||||
D.print<StreamNumberProvider>("Globals Stream",
|
||||
DL.getGlobalSymbolStreamIndex(),
|
||||
DR.getGlobalSymbolStreamIndex());
|
||||
D.print<StreamNumberProvider>("Publics Stream",
|
||||
DL.getPublicSymbolStreamIndex(),
|
||||
DR.getPublicSymbolStreamIndex());
|
||||
D.print<StreamNumberProvider>("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<uint32_t, DbiModuleDescriptor> &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<ModiProvider>("- Modi", L.first, None);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We did find this module. Go through and compare each field.
|
||||
const auto &R = *Iter;
|
||||
D.print<ModiProvider>("- Modi", L.first, R.first);
|
||||
D.print<StringProvider>("- Obj File Name",
|
||||
shortFilePath(L.second.getObjFileName(), 28),
|
||||
shortFilePath(R.second.getObjFileName(), 28));
|
||||
D.print<StreamNumberProvider>("- 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(); }
|
||||
|
||||
|
106
tools/llvm-pdbutil/DiffPrinter.cpp
Normal file
106
tools/llvm-pdbutil/DiffPrinter.cpp
Normal file
@ -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 << '|';
|
||||
}
|
158
tools/llvm-pdbutil/DiffPrinter.h
Normal file
158
tools/llvm-pdbutil/DiffPrinter.h
Normal file
@ -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 <list>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace llvm {
|
||||
namespace pdb {
|
||||
|
||||
class PDBFile;
|
||||
|
||||
enum class DiffResult { UNSPECIFIED, IDENTICAL, EQUIVALENT, DIFFERENT };
|
||||
|
||||
struct IdenticalDiffProvider {
|
||||
template <typename T, typename U>
|
||||
DiffResult compare(const T &Left, const U &Right) {
|
||||
return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::DIFFERENT;
|
||||
}
|
||||
|
||||
template <typename T> std::string format(const T &Item) {
|
||||
return formatv("{0}", Item).str();
|
||||
}
|
||||
};
|
||||
|
||||
struct EquivalentDiffProvider {
|
||||
template <typename T, typename U>
|
||||
DiffResult compare(const T &Left, const U &Right) {
|
||||
return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::EQUIVALENT;
|
||||
}
|
||||
|
||||
template <typename T> 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 <typename T, typename U> struct Identical {};
|
||||
|
||||
template <typename Provider = IdenticalDiffProvider, typename T, typename U>
|
||||
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 <typename T, typename U>
|
||||
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 <typename T, typename U>
|
||||
void diffUnorderedArray(StringRef Property, ArrayRef<T> Left,
|
||||
ArrayRef<U> Right) {
|
||||
std::unordered_set<T> LS(Left.begin(), Left.end());
|
||||
std::unordered_set<U> 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 <typename ValueProvider = IdenticalDiffProvider, typename T,
|
||||
typename U>
|
||||
void diffUnorderedMap(StringRef Property, const StringMap<T> &Left,
|
||||
const StringMap<U> &Right,
|
||||
ValueProvider P = ValueProvider()) {
|
||||
StringMap<U> 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
|
@ -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<size_t>(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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user