Resubmit "Add pdb-diff test."

This was originally reverted because of two issues.
  1) Printing ANSI color escape codes even when outputting to
     a file
  2) Module name comparisons were failing when comparing a PDB
     generated on one machine to a PDB generated on another
     machine.

I attempted to fix #2 by adding command line options which let
you specify prefixes to strip from the beginning of embedded
paths, which effectively lets us specify a path to "base" each
PDB from and only compare the parts under the base.  But this is
tricky because PDB paths always use Windows path syntax, even
when they are created on non-Windows hosts.  A problem still
existed when constructing the prefix to strip, where we were
accidentally using a host-specific path separator instead of
a Windows path separator.

This resubmission fixes the issue on Linux (and I have verified
that the test now passes on Linux).

llvm-svn: 307571
This commit is contained in:
Zachary Turner 2017-07-10 19:16:49 +00:00
parent fd7b1c0a56
commit a9d944fd6f
8 changed files with 501 additions and 147 deletions

212
lld/test/COFF/pdb-diff.test Normal file
View File

@ -0,0 +1,212 @@
This test verifies that we produce PDBs compatible with MSVC in various ways.
We check in a cl-generated object file, PDB, and original source which serve
as the "baseline" for us to measure against. Then we link the same object
file with LLD and compare the two PDBs. Since the baseline object file and
PDB are already checked in, we just run LLD on the object file.
RUN: lld-link /debug /pdb:%T/pdb-diff-lld.pdb /nodefaultlib /entry:main %S/Inputs/pdb-diff.obj
RUN: llvm-pdbutil diff -result -values=false -left-bin-root=%S -right-bin-root=D:/src/llvm-mono/lld/test/COFF/ %T/pdb-diff-lld.pdb %S/Inputs/pdb-diff-cl.pdb | FileCheck %s
CHECK: ----------------------
CHECK-NEXT: | MSF Super Block |
CHECK-NEXT: |----------------+---|
CHECK-NEXT: | File | |
CHECK-NEXT: |----------------+---|
CHECK-NEXT: | Block Size | I |
CHECK-NEXT: |----------------+---|
CHECK-NEXT: | Block Count |
CHECK-NEXT: |----------------+---|
CHECK-NEXT: | Unknown 1 | I |
CHECK-NEXT: |----------------+---|
CHECK-NEXT: | Directory Size |
CHECK-NEXT: |----------------+---|
CHECK-NEXT: ------------------------------------
CHECK-NEXT: | Stream Directory |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | File | |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Stream Count | D |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Old MSF Directory | I |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | PDB Stream | I |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | TPI Stream | I |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | DBI Stream | I |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | IPI Stream | I |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | New FPO Data | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Section Header Data | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Named Stream "/names" | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Named Stream "/LinkInfo" | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Module "Inputs\pdb-diff.obj" | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Module "* Linker *" | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | TPI Hash | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | IPI Hash | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Global Symbol Hash | D |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Public Symbol Hash | D |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Public Symbol Records | D |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: ------------------------------------
CHECK-NEXT: | String Table |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | File | |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Number of Strings | D |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Hash Version | I |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Byte Size |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Signature | I |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Empty Strings |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | {{.*}}pdb-diff.cpp | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | $T0 $ebp = $...p $T0 8 + = | D |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | d:\src\llvm-...er internal) | D |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: ----------------------------
CHECK-NEXT: | PDB Stream |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | File | |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | Stream Size |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | Age | I |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | Guid | D |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | Signature | D |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | Version | I |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | Features (set) | I |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | Feature | I |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | Named Stream Size |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | Named Streams (map) | {{[EI]}} |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | /names | {{[EI]}} |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: | /LinkInfo | {{[EI]}} |
CHECK-NEXT: |----------------------+---|
CHECK-NEXT: ----------------------------------------------
CHECK-NEXT: | DBI Stream |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | File | |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Dbi Version | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Age | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Machine | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Flags | D |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Build Major | D |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Build Minor | D |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Build Number | D |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | PDB DLL Version | D |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | PDB DLL RBLD | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (FPO) | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (Exception) | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (Fixup) | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (OmapToSrc) | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (OmapFromSrc) | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (SectionHdr) | {{[EI]}} |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (TokenRidMap) | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (Xdata) | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (Pdata) | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (NewFPO) | {{[EI]}} |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (SectionHdrOrig) | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Globals Stream | D |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Publics Stream | D |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Symbol Records | D |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Has CTypes | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Is Incrementally Linked | D |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Is Stripped | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Module Count | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Source File Count | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Module "Inputs\pdb-diff.obj" |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Modi | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Obj File Name | {{[EI]}} |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Debug Stream | {{[EI]}} |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - C11 Byte Size | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - C13 Byte Size | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - # of files | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Pdb File Path Index | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Source File Name Index | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Symbol Byte Size | D |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Module "* Linker *" |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Modi | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Obj File Name | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Debug Stream | {{[EI]}} |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - C11 Byte Size | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - C13 Byte Size | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - # of files | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Pdb File Path Index | {{[EI]}} |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Source File Name Index | {{[EI]}} |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Symbol Byte Size | D |
CHECK-NEXT: |----------------------------------------+---|

View File

@ -23,13 +23,148 @@
#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/FormatProviders.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Path.h"
using namespace llvm;
using namespace llvm::pdb;
namespace {
// Compare and format two stream numbers. Stream numbers are considered
// identical if they contain the same value, equivalent if they are both
// the invalid stream or neither is the invalid stream, and different if
// one is the invalid stream and another isn't.
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, bool Right) {
if (SN == kInvalidStreamIndex)
return "(not present)";
return formatv("{0}", SN).str();
}
};
// Compares and formats two module indices. Modis are considered identical
// if they are identical, equivalent if they either both contain a value or
// both don't contain a value, and different if one contains a value and the
// other doesn't.
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, bool Right) {
if (!Modi.hasValue())
return "(not present)";
return formatv("{0}", *Modi).str();
}
};
// Compares and formats two paths embedded in the PDB, ignoring the beginning
// of the path if the user specified it as a "root path" on the command line.
struct BinaryPathProvider {
explicit BinaryPathProvider(uint32_t MaxLen) : MaxLen(MaxLen) {}
DiffResult compare(StringRef L, StringRef R) {
if (L == R)
return DiffResult::IDENTICAL;
SmallString<64> LN = removeRoot(L, false);
SmallString<64> RN = removeRoot(R, true);
return (LN.equals_lower(RN)) ? DiffResult::EQUIVALENT
: DiffResult::DIFFERENT;
}
std::string format(StringRef S, bool Right) {
if (S.empty())
return "(empty)";
SmallString<64> Native = removeRoot(S, Right);
return truncateStringFront(Native.str(), MaxLen);
}
SmallString<64> removeRoot(StringRef Path, bool IsRight) const {
SmallString<64> Native(Path);
auto &RootOpt = IsRight ? opts::diff::RightRoot : opts::diff::LeftRoot;
SmallString<64> Root(static_cast<std::string>(RootOpt));
// pdb paths always use windows syntax, convert slashes to backslashes.
sys::path::native(Root, sys::path::Style::windows);
if (sys::path::has_stem(Root, sys::path::Style::windows))
sys::path::append(Root, sys::path::Style::windows,
sys::path::get_separator(sys::path::Style::windows));
sys::path::replace_path_prefix(Native, Root, "", sys::path::Style::windows);
return Native;
}
uint32_t MaxLen;
};
// Compare and format two stream purposes. For general streams, this just
// compares the description. For module streams it uses the path comparison
// algorithm taking into consideration the binary root, described above.
// Formatting stream purposes just prints the stream purpose, except for
// module streams and named streams, where it prefixes the name / module
// with an identifier. Example:
//
// Named Stream "\names"
// Module Stream "foo.obj"
//
// If a named stream is too long to fit in a column, it is truncated at the
// end, and if a module is too long to fit in a column, it is truncated at the
// beginning. Example:
//
// Named Stream "\Really Long Str..."
// Module Stream "...puts\foo.obj"
//
struct StreamPurposeProvider {
explicit StreamPurposeProvider(uint32_t MaxLen) : MaxLen(MaxLen) {}
DiffResult compare(const std::pair<StreamPurpose, std::string> &L,
const std::pair<StreamPurpose, std::string> &R) {
if (L.first != R.first)
return DiffResult::DIFFERENT;
if (L.first == StreamPurpose::ModuleStream) {
BinaryPathProvider PathProvider(MaxLen);
return PathProvider.compare(L.second, R.second);
}
return (L.second == R.second) ? DiffResult::IDENTICAL
: DiffResult::DIFFERENT;
}
std::string format(const std::pair<StreamPurpose, std::string> &P,
bool Right) {
if (P.first == StreamPurpose::Other)
return truncateStringBack(P.second, MaxLen);
if (P.first == StreamPurpose::NamedStream)
return truncateQuotedNameBack("Named Stream", P.second, MaxLen);
assert(P.first == StreamPurpose::ModuleStream);
uint32_t ExtraChars = strlen("Module \"\"");
BinaryPathProvider PathProvider(MaxLen - ExtraChars);
std::string Result = PathProvider.format(P.second, Right);
return formatv("Module \"{0}\"", Result);
}
uint32_t MaxLen;
};
} // namespace
namespace llvm {
template <> struct format_provider<PdbRaw_FeatureSig> {
static void format(const PdbRaw_FeatureSig &Sig, raw_ostream &Stream,
@ -100,19 +235,12 @@ Error DiffStyle::dump() {
return Error::success();
}
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() {
DiffPrinter D(2, "MSF Super Block", 16, 20, opts::diff::PrintResultColumn,
opts::diff::PrintValueColumns, outs());
D.printExplicit("File", DiffResult::UNSPECIFIED,
shortFilePath(File1.getFilePath(), 18),
shortFilePath(File2.getFilePath(), 18));
truncateStringFront(File1.getFilePath(), 18),
truncateStringFront(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());
@ -125,13 +253,13 @@ Error DiffStyle::diffStreamDirectory() {
DiffPrinter D(2, "Stream Directory", 30, 20, opts::diff::PrintResultColumn,
opts::diff::PrintValueColumns, outs());
D.printExplicit("File", DiffResult::UNSPECIFIED,
shortFilePath(File1.getFilePath(), 18),
shortFilePath(File2.getFilePath(), 18));
truncateStringFront(File1.getFilePath(), 18),
truncateStringFront(File2.getFilePath(), 18));
SmallVector<std::string, 32> P;
SmallVector<std::string, 32> Q;
discoverStreamPurposes(File1, P, 28);
discoverStreamPurposes(File2, Q, 28);
SmallVector<std::pair<StreamPurpose, std::string>, 32> P;
SmallVector<std::pair<StreamPurpose, std::string>, 32> Q;
discoverStreamPurposes(File1, P);
discoverStreamPurposes(File2, Q);
D.print("Stream Count", File1.getNumStreams(), File2.getNumStreams());
auto PI = to_vector<32>(enumerate(P));
auto QI = to_vector<32>(enumerate(Q));
@ -139,26 +267,31 @@ Error DiffStyle::diffStreamDirectory() {
// 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.
StreamPurposeProvider StreamProvider(28);
for (const auto &P : PI) {
typedef decltype(PI) ContainerType;
typedef typename ContainerType::value_type value_type;
auto Iter = llvm::find_if(
QI, [P](const value_type &V) { return V.value() == P.value(); });
auto Iter = llvm::find_if(QI, [P, &StreamProvider](const value_type &V) {
DiffResult Result = StreamProvider.compare(P.value(), V.value());
return Result == DiffResult::EQUIVALENT ||
Result == DiffResult::IDENTICAL;
});
if (Iter == QI.end()) {
D.printExplicit(P.value(), DiffResult::DIFFERENT, P.index(),
"(not present)");
D.printExplicit(StreamProvider.format(P.value(), false),
DiffResult::DIFFERENT, P.index(), "(not present)");
continue;
}
D.print<EquivalentDiffProvider>(P.value(), P.index(), Iter->index());
D.print<EquivalentDiffProvider>(StreamProvider.format(P.value(), false),
P.index(), Iter->index());
QI.erase(Iter);
}
for (const auto &Q : QI) {
D.printExplicit(Q.value(), DiffResult::DIFFERENT, "(not present)",
Q.index());
D.printExplicit(StreamProvider.format(Q.value(), true),
DiffResult::DIFFERENT, "(not present)", Q.index());
}
return Error::success();
@ -168,8 +301,8 @@ Error DiffStyle::diffStringTable() {
DiffPrinter D(2, "String Table", 30, 20, opts::diff::PrintResultColumn,
opts::diff::PrintValueColumns, outs());
D.printExplicit("File", DiffResult::UNSPECIFIED,
shortFilePath(File1.getFilePath(), 18),
shortFilePath(File2.getFilePath(), 18));
truncateStringFront(File1.getFilePath(), 18),
truncateStringFront(File2.getFilePath(), 18));
auto ExpectedST1 = File1.getStringTable();
auto ExpectedST2 = File2.getStringTable();
@ -257,8 +390,8 @@ Error DiffStyle::diffInfoStream() {
DiffPrinter D(2, "PDB Stream", 22, 40, opts::diff::PrintResultColumn,
opts::diff::PrintValueColumns, outs());
D.printExplicit("File", DiffResult::UNSPECIFIED,
shortFilePath(File1.getFilePath(), 38),
shortFilePath(File2.getFilePath(), 38));
truncateStringFront(File1.getFilePath(), 38),
truncateStringFront(File2.getFilePath(), 38));
auto ExpectedInfo1 = File1.getPDBInfoStream();
auto ExpectedInfo2 = File2.getPDBInfoStream();
@ -292,53 +425,6 @@ Error DiffStyle::diffInfoStream() {
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;
@ -348,16 +434,31 @@ getModuleDescriptors(const DbiModuleList &ML) {
return List;
}
static void diffOneModule(
DiffPrinter &D, const std::pair<uint32_t, DbiModuleDescriptor> Item,
std::vector<std::pair<uint32_t, DbiModuleDescriptor>> &Other, bool Invert) {
D.printFullRow(
truncateQuotedNameFront("Module", Item.second.getModuleName(), 70));
static void
diffOneModule(DiffPrinter &D,
const std::pair<uint32_t, DbiModuleDescriptor> Item,
std::vector<std::pair<uint32_t, DbiModuleDescriptor>> &Other,
bool ItemIsRight) {
StreamPurposeProvider HeaderProvider(70);
std::pair<StreamPurpose, std::string> Header;
Header.first = StreamPurpose::ModuleStream;
Header.second = Item.second.getModuleName();
D.printFullRow(HeaderProvider.format(Header, ItemIsRight));
const auto *L = &Item;
BinaryPathProvider PathProvider(28);
auto Iter = llvm::find_if(
Other, [&Item](const std::pair<uint32_t, DbiModuleDescriptor> &Other) {
return Other.second.getModuleName().equals_lower(
Item.second.getModuleName());
Other, [&PathProvider, ItemIsRight,
L](const std::pair<uint32_t, DbiModuleDescriptor> &Other) {
const auto *Left = L;
const auto *Right = &Other;
if (ItemIsRight)
std::swap(Left, Right);
DiffResult Result = PathProvider.compare(Left->second.getModuleName(),
Right->second.getModuleName());
return Result == DiffResult::EQUIVALENT ||
Result == DiffResult::IDENTICAL;
});
if (Iter == Other.end()) {
// We didn't find this module at all on the other side. Just print one row
@ -367,15 +468,13 @@ static void diffOneModule(
}
// We did find this module. Go through and compare each field.
const auto *L = &Item;
const auto *R = &*Iter;
if (Invert)
if (ItemIsRight)
std::swap(L, R);
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<BinaryPathProvider>("- Obj File Name", L->second.getObjFileName(),
R->second.getObjFileName(), PathProvider);
D.print<StreamNumberProvider>("- Debug Stream",
L->second.getModuleStreamIndex(),
R->second.getModuleStreamIndex());
@ -398,8 +497,8 @@ Error DiffStyle::diffDbiStream() {
DiffPrinter D(2, "DBI Stream", 40, 30, opts::diff::PrintResultColumn,
opts::diff::PrintValueColumns, outs());
D.printExplicit("File", DiffResult::UNSPECIFIED,
shortFilePath(File1.getFilePath(), 28),
shortFilePath(File2.getFilePath(), 28));
truncateStringFront(File1.getFilePath(), 28),
truncateStringFront(File2.getFilePath(), 28));
auto ExpectedDbi1 = File1.getPDBDbiStream();
auto ExpectedDbi2 = File2.getPDBDbiStream();

View File

@ -6,18 +6,31 @@
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;
namespace {
struct Colorize {
Colorize(raw_ostream &OS, DiffResult Result) : OS(OS) {
if (!OS.has_colors())
return;
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;
}
}
~Colorize() {
if (OS.has_colors())
OS.resetColor();
}
raw_ostream &OS;
};
}
DiffPrinter::DiffPrinter(uint32_t Indent, StringRef Header,
@ -124,9 +137,8 @@ void DiffPrinter::printValue(StringRef Value, DiffResult C, AlignStyle Style,
std::string FormattedItem =
formatv("{0}", fmt_align(Value, Style, Width)).str();
if (C != DiffResult::UNSPECIFIED) {
setColor(OS, C);
Colorize Color(OS, C);
OS << FormattedItem;
OS.resetColor();
} else
OS << FormattedItem;
if (Style == AlignStyle::Right)

View File

@ -43,7 +43,7 @@ struct IdenticalDiffProvider {
return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::DIFFERENT;
}
template <typename T> std::string format(const T &Item) {
template <typename T> std::string format(const T &Item, bool Right) {
return formatv("{0}", Item).str();
}
};
@ -54,7 +54,7 @@ struct EquivalentDiffProvider {
return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::EQUIVALENT;
}
template <typename T> std::string format(const T &Item) {
template <typename T> std::string format(const T &Item, bool Right) {
return formatv("{0}", Item).str();
}
};
@ -71,8 +71,8 @@ public:
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);
std::string L = P.format(Left, false);
std::string R = P.format(Right, true);
DiffResult Result = P.compare(Left, Right);
printExplicit(Property, Result, L, R);

View File

@ -22,10 +22,9 @@
using namespace llvm;
using namespace llvm::pdb;
void llvm::pdb::discoverStreamPurposes(PDBFile &File,
SmallVectorImpl<std::string> &Purposes,
uint32_t MaxLen) {
void llvm::pdb::discoverStreamPurposes(
PDBFile &File,
SmallVectorImpl<std::pair<StreamPurpose, std::string>> &Purposes) {
// It's OK if we fail to load some of these streams, we still attempt to print
// what we can.
auto Dbi = File.getPDBDbiStream();
@ -55,71 +54,72 @@ void llvm::pdb::discoverStreamPurposes(PDBFile &File,
Purposes.resize(StreamCount);
for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
std::string Value;
std::pair<StreamPurpose, std::string> Value;
if (StreamIdx == OldMSFDirectory)
Value = truncateStringBack("Old MSF Directory", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Old MSF Directory");
else if (StreamIdx == StreamPDB)
Value = truncateStringBack("PDB Stream", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "PDB Stream");
else if (StreamIdx == StreamDBI)
Value = truncateStringBack("DBI Stream", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "DBI Stream");
else if (StreamIdx == StreamTPI)
Value = truncateStringBack("TPI Stream", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "TPI Stream");
else if (StreamIdx == StreamIPI)
Value = truncateStringBack("IPI Stream", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "IPI Stream");
else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex())
Value = truncateStringBack("Global Symbol Hash", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Global Symbol Hash");
else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex())
Value = truncateStringBack("Public Symbol Hash", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Public Symbol Hash");
else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex())
Value = truncateStringBack("Public Symbol Records", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Public Symbol Records");
else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex())
Value = truncateStringBack("TPI Hash", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "TPI Hash");
else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex())
Value = truncateStringBack("TPI Aux Hash", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "TPI Aux Hash");
else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex())
Value = truncateStringBack("IPI Hash", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "IPI Hash");
else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex())
Value = truncateStringBack("IPI Aux Hash", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "IPI Aux Hash");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception))
Value = truncateStringBack("Exception Data", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Exception Data");
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup))
Value = truncateStringBack("Fixup Data", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Fixup Data");
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO))
Value = truncateStringBack("FPO Data", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "FPO Data");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO))
Value = truncateStringBack("New FPO Data", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "New FPO Data");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc))
Value = truncateStringBack("Omap From Source Data", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Omap From Source Data");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc))
Value = truncateStringBack("Omap To Source Data", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Omap To Source Data");
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata))
Value = truncateStringBack("Pdata", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Pdata");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr))
Value = truncateStringBack("Section Header Data", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Section Header Data");
else if (Dbi &&
StreamIdx ==
Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig))
Value = truncateStringBack("Section Header Original Data", MaxLen);
Value =
std::make_pair(StreamPurpose::Other, "Section Header Original Data");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap))
Value = truncateStringBack("Token Rid Data", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Token Rid Data");
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata))
Value = truncateStringBack("Xdata", MaxLen);
Value = std::make_pair(StreamPurpose::Other, "Xdata");
else {
auto ModIter = ModStreams.find(StreamIdx);
auto NSIter = NamedStreams.find(StreamIdx);
if (ModIter != ModStreams.end()) {
Value = truncateQuotedNameFront(
"Module", ModIter->second.getModuleName(), MaxLen);
Value = std::make_pair(StreamPurpose::ModuleStream,
ModIter->second.getModuleName());
} else if (NSIter != NamedStreams.end()) {
Value = truncateQuotedNameBack("Named Stream", NSIter->second, MaxLen);
Value = std::make_pair(StreamPurpose::NamedStream, NSIter->second);
} else {
Value = "???";
Value = std::make_pair(StreamPurpose::Other, "???");
}
}
Purposes[StreamIdx] = Value;
@ -135,3 +135,18 @@ void llvm::pdb::discoverStreamPurposes(PDBFile &File,
if (!Info)
consumeError(Info.takeError());
}
void llvm::pdb::discoverStreamPurposes(PDBFile &File,
SmallVectorImpl<std::string> &Purposes) {
SmallVector<std::pair<StreamPurpose, std::string>, 24> SP;
discoverStreamPurposes(File, SP);
Purposes.reserve(SP.size());
for (const auto &P : SP) {
if (P.first == StreamPurpose::NamedStream)
Purposes.push_back(formatv("Named Stream \"{0}\"", P.second));
else if (P.first == StreamPurpose::ModuleStream)
Purposes.push_back(formatv("Module \"{0}\"", P.second));
else
Purposes.push_back(P.second);
}
}

View File

@ -17,9 +17,13 @@
namespace llvm {
namespace pdb {
class PDBFile;
enum class StreamPurpose { NamedStream, ModuleStream, Other };
void discoverStreamPurposes(PDBFile &File,
SmallVectorImpl<std::string> &Purposes,
uint32_t MaxLen = 0);
SmallVectorImpl<std::string> &Purposes);
void discoverStreamPurposes(
PDBFile &File,
SmallVectorImpl<std::pair<StreamPurpose, std::string>> &Purposes);
}
}

View File

@ -293,9 +293,23 @@ cl::opt<bool>
cl::desc("Print a column with the result status"),
cl::Optional, cl::sub(DiffSubcommand));
cl::list<std::string> InputFilenames(cl::Positional,
cl::desc("<first> <second>"),
cl::OneOrMore, cl::sub(DiffSubcommand));
cl::opt<std::string> LeftRoot(
"left-bin-root", cl::Optional,
cl::desc("Treats the specified path as the root of the tree containing "
"binaries referenced by the left PDB. The root is stripped from "
"embedded paths when doing equality comparisons."),
cl::sub(DiffSubcommand));
cl::opt<std::string> RightRoot(
"right-bin-root", cl::Optional,
cl::desc("Treats the specified path as the root of the tree containing "
"binaries referenced by the right PDB. The root is stripped from "
"embedded paths when doing equality comparisons"),
cl::sub(DiffSubcommand));
cl::opt<std::string> Left(cl::Positional, cl::desc("<left>"),
cl::sub(DiffSubcommand));
cl::opt<std::string> Right(cl::Positional, cl::desc("<right>"),
cl::sub(DiffSubcommand));
}
cl::OptionCategory FileOptions("Module & File Options");
@ -1151,11 +1165,7 @@ int main(int argc_, const char *argv_[]) {
std::for_each(opts::bytes::InputFilenames.begin(),
opts::bytes::InputFilenames.end(), dumpBytes);
} else if (opts::DiffSubcommand) {
if (opts::diff::InputFilenames.size() != 2) {
errs() << "diff subcommand expects exactly 2 arguments.\n";
exit(1);
}
diff(opts::diff::InputFilenames[0], opts::diff::InputFilenames[1]);
diff(opts::diff::Left, opts::diff::Right);
} else if (opts::MergeSubcommand) {
if (opts::merge::InputFilenames.size() < 2) {
errs() << "merge subcommand requires at least 2 input files.\n";

View File

@ -172,6 +172,8 @@ extern llvm::cl::opt<bool> DumpModuleSyms;
namespace diff {
extern llvm::cl::opt<bool> PrintValueColumns;
extern llvm::cl::opt<bool> PrintResultColumn;
extern llvm::cl::opt<std::string> LeftRoot;
extern llvm::cl::opt<std::string> RightRoot;
} // namespace diff
}