[llvm-pdbutil] Create a "bytes" subcommand.

This idea originally came about when I was doing some deep
investigation of why certain bytes in a PDB that we round-tripped
differed from their original bytes in the source PDB.  I found
myself having to hack up the code in many places to dump the
bytes of this substream, or that record.  It would be nice if
we could just do this for every possible stream, substream,
debug chunk type, etc.

It doesn't make sense to put this under dump because there's just
so many options that would detract from the more common use case
of just dumping deserialized records.  So making a new subcommand
seems like the most logical course of action.  In doing so, we
already have two command line options that are suitable for this
new subcommand, so start out by moving them there.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@306056 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Zachary Turner 2017-06-22 20:58:11 +00:00
parent 0895032e6e
commit 08bb54f87b
11 changed files with 307 additions and 152 deletions

View File

@ -1,7 +1,7 @@
; RUN: llvm-pdbutil raw -all %p/Inputs/empty.pdb | FileCheck -check-prefix=ALL %s
; RUN: llvm-pdbutil raw -summary -modules -files \
; RUN: llvm-pdbutil dump -all %p/Inputs/empty.pdb | FileCheck -check-prefix=ALL %s
; RUN: llvm-pdbutil dump -summary -modules -files \
; RUN: %p/Inputs/big-read.pdb | FileCheck -check-prefix=BIG %s
; RUN: not llvm-pdbutil raw -summary %p/Inputs/bad-block-size.pdb 2>&1 | FileCheck -check-prefix=BAD-BLOCK-SIZE %s
; RUN: not llvm-pdbutil dump -summary %p/Inputs/bad-block-size.pdb 2>&1 | FileCheck -check-prefix=BAD-BLOCK-SIZE %s
ALL: Summary
ALL-NEXT: ============================================================

View File

@ -1,8 +1,9 @@
; RUN: llvm-pdbutil dump -block-data=0 %p/Inputs/empty.pdb | FileCheck --check-prefix=BLOCK0 %s
; RUN: llvm-pdbutil dump -block-data=0-1 %p/Inputs/empty.pdb | FileCheck --check-prefix=BLOCK01 %s
; RUN: not llvm-pdbutil dump -block-data=0,1 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s
; RUN: not llvm-pdbutil dump -block-data=0a1 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s
; RUN: not llvm-pdbutil dump -block-data=0- %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s
; RUN: llvm-pdbutil bytes -block-data=0 %p/Inputs/empty.pdb | FileCheck --check-prefix=BLOCK0 %s
; RUN: llvm-pdbutil bytes -block-data=0-1 %p/Inputs/empty.pdb | FileCheck --check-prefix=BLOCK01 %s
; RUN: llvm-pdbutil bytes -block-data=0-0x1 %p/Inputs/empty.pdb | FileCheck --check-prefix=BLOCK01 %s
; RUN: not llvm-pdbutil bytes -block-data=0,1 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s
; RUN: not llvm-pdbutil bytes -block-data=0a1 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s
; RUN: not llvm-pdbutil bytes -block-data=0- %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s
BLOCK0: MSF Blocks
BLOCK0-NEXT: ============================================================
@ -21,9 +22,9 @@ BLOCK01-NEXT: 0020: 00100000 02000000 19000000 88000000 00000000 18000000 00
BLOCK01-NEXT: 0040: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 |................................|
BLOCK01-NEXT: 0060: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 |................................|
BLOCK01: Block 1 (
BLOCK01-NEXT: 0000: C0FCFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................................|
BLOCK01-NEXT: 0020: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................................|
BLOCK01-NEXT: 0040: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................................|
BLOCK01-NEXT: 1000: C0FCFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................................|
BLOCK01-NEXT: 1020: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................................|
BLOCK01-NEXT: 1040: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................................|
BLOCK01-NOT: Block 2 (
BADSYNTAX: Argument '{{.*}}' invalid format.

View File

@ -1,6 +1,6 @@
; RUN: llvm-pdbutil dump -stream-data=1 %p/Inputs/empty.pdb | FileCheck --check-prefix=STREAM %s
; RUN: llvm-pdbutil dump -stream-data=100 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=INVALIDSTREAM %s
; RUN: llvm-pdbutil dump -stream-data=1,100 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BOTH %s
; RUN: llvm-pdbutil bytes -stream-data=1 %p/Inputs/empty.pdb | FileCheck --check-prefix=STREAM %s
; RUN: llvm-pdbutil bytes -stream-data=100 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=INVALIDSTREAM %s
; RUN: llvm-pdbutil bytes -stream-data=1,100 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BOTH %s
STREAM: Stream Data
STREAM-NEXT: ============================================================

View File

@ -0,0 +1,167 @@
//===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "BytesOutputStyle.h"
#include "StreamUtil.h"
#include "llvm-pdbutil.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/RawError.h"
#include "llvm/Support/BinaryStreamReader.h"
#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/FormatVariadic.h"
using namespace llvm;
using namespace llvm::msf;
using namespace llvm::pdb;
namespace {
struct StreamSpec {
uint32_t SI = 0;
uint32_t Begin = 0;
uint32_t Size = 0;
};
} // namespace
static Expected<StreamSpec> parseStreamSpec(StringRef Str) {
StreamSpec Result;
if (Str.consumeInteger(0, Result.SI))
return make_error<RawError>(raw_error_code::invalid_format,
"Invalid Stream Specification");
if (Str.consume_front(":")) {
if (Str.consumeInteger(0, Result.Begin))
return make_error<RawError>(raw_error_code::invalid_format,
"Invalid Stream Specification");
}
if (Str.consume_front("@")) {
if (Str.consumeInteger(0, Result.Size))
return make_error<RawError>(raw_error_code::invalid_format,
"Invalid Stream Specification");
}
if (!Str.empty())
return make_error<RawError>(raw_error_code::invalid_format,
"Invalid Stream Specification");
return Result;
}
static SmallVector<StreamSpec, 2> parseStreamSpecs(LinePrinter &P) {
SmallVector<StreamSpec, 2> Result;
for (auto &Str : opts::bytes::DumpStreamData) {
auto ESS = parseStreamSpec(Str);
if (!ESS) {
P.formatLine("Error parsing stream spec {0}: {1}", Str,
toString(ESS.takeError()));
continue;
}
Result.push_back(*ESS);
}
return Result;
}
static void printHeader(LinePrinter &P, const Twine &S) {
P.NewLine();
P.formatLine("{0,=60}", S);
P.formatLine("{0}", fmt_repeat('=', 60));
}
BytesOutputStyle::BytesOutputStyle(PDBFile &File)
: File(File), P(2, false, outs()) {}
Error BytesOutputStyle::dump() {
if (opts::bytes::DumpBlockRange.hasValue()) {
auto &R = *opts::bytes::DumpBlockRange;
uint32_t Max = R.Max.getValueOr(R.Min);
if (Max < R.Min)
return make_error<StringError>(
"Invalid block range specified. Max < Min",
inconvertibleErrorCode());
if (Max >= File.getBlockCount())
return make_error<StringError>(
"Invalid block range specified. Requested block out of bounds",
inconvertibleErrorCode());
dumpBlockRanges(R.Min, Max);
P.NewLine();
}
if (!opts::bytes::DumpStreamData.empty()) {
dumpStreamBytes();
P.NewLine();
}
return Error::success();
}
void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) {
printHeader(P, "MSF Blocks");
AutoIndent Indent(P);
for (uint32_t I = Min; I <= Max; ++I) {
uint64_t Base = I;
Base *= File.getBlockSize();
auto ExpectedData = File.getBlockData(I, File.getBlockSize());
if (!ExpectedData) {
P.formatLine("Could not get block {0}. Reason = {1}", I,
toString(ExpectedData.takeError()));
continue;
}
std::string Label = formatv("Block {0}", I).str();
P.formatBinary(Label, *ExpectedData, Base, 0);
}
}
void BytesOutputStyle::dumpStreamBytes() {
if (StreamPurposes.empty())
discoverStreamPurposes(File, StreamPurposes);
printHeader(P, "Stream Data");
ExitOnError Err("Unexpected error reading stream data");
auto Specs = parseStreamSpecs(P);
for (const auto &Spec : Specs) {
uint32_t End = 0;
AutoIndent Indent(P);
if (Spec.SI >= File.getNumStreams()) {
P.formatLine("Stream {0}: Not present", Spec.SI);
continue;
}
auto S = MappedBlockStream::createIndexedStream(
File.getMsfLayout(), File.getMsfBuffer(), Spec.SI, File.getAllocator());
if (!S) {
P.NewLine();
P.formatLine("Stream {0}: Not present", Spec.SI);
continue;
}
if (Spec.Size == 0)
End = S->getLength();
else
End = std::min(Spec.Begin + Spec.Size, S->getLength());
uint32_t Size = End - Spec.Begin;
P.formatLine("Stream {0} ({1:N} bytes): {2}", Spec.SI, S->getLength(),
StreamPurposes[Spec.SI]);
AutoIndent Indent2(P);
BinaryStreamReader R(*S);
ArrayRef<uint8_t> StreamData;
Err(R.readBytes(StreamData, S->getLength()));
StreamData = StreamData.slice(Spec.Begin, Size);
P.formatBinary("Data", StreamData, Spec.Begin);
}
}

View File

@ -0,0 +1,41 @@
//===- BytesOutputStyle.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_BYTESOUTPUTSTYLE_H
#define LLVM_TOOLS_LLVMPDBDUMP_BYTESOUTPUTSTYLE_H
#include "LinePrinter.h"
#include "OutputStyle.h"
#include "llvm/Support/Error.h"
namespace llvm {
namespace pdb {
class PDBFile;
class BytesOutputStyle : public OutputStyle {
public:
BytesOutputStyle(PDBFile &File);
Error dump() override;
private:
void dumpBlockRanges(uint32_t Min, uint32_t Max);
void dumpStreamBytes();
PDBFile &File;
LinePrinter P;
SmallVector<std::string, 8> StreamPurposes;
};
} // namespace pdb
} // namespace llvm
#endif

View File

@ -9,6 +9,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_tool(llvm-pdbutil
Analyze.cpp
BytesOutputStyle.cpp
Diff.cpp
DumpOutputStyle.cpp
llvm-pdbutil.cpp

View File

@ -80,18 +80,6 @@ Error DumpOutputStyle::dump() {
P.NewLine();
}
if (opts::dump::DumpBlockRange.hasValue()) {
if (auto EC = dumpBlockRanges())
return EC;
P.NewLine();
}
if (!opts::dump::DumpStreamData.empty()) {
if (auto EC = dumpStreamBytes())
return EC;
P.NewLine();
}
if (opts::dump::DumpStringTable) {
if (auto EC = dumpStringTable())
return EC;
@ -216,103 +204,6 @@ Error DumpOutputStyle::dumpStreamSummary() {
return Error::success();
}
Error DumpOutputStyle::dumpBlockRanges() {
printHeader(P, "MSF Blocks");
auto &R = *opts::dump::DumpBlockRange;
uint32_t Max = R.Max.getValueOr(R.Min);
AutoIndent Indent(P);
if (Max < R.Min)
return make_error<StringError>(
"Invalid block range specified. Max < Min",
std::make_error_code(std::errc::bad_address));
if (Max >= File.getBlockCount())
return make_error<StringError>(
"Invalid block range specified. Requested block out of bounds",
std::make_error_code(std::errc::bad_address));
for (uint32_t I = R.Min; I <= Max; ++I) {
auto ExpectedData = File.getBlockData(I, File.getBlockSize());
if (!ExpectedData)
return ExpectedData.takeError();
std::string Label = formatv("Block {0}", I).str();
P.formatBinary(Label, *ExpectedData, 0);
}
return Error::success();
}
static Error parseStreamSpec(StringRef Str, uint32_t &SI, uint32_t &Offset,
uint32_t &Size) {
if (Str.consumeInteger(0, SI))
return make_error<RawError>(raw_error_code::invalid_format,
"Invalid Stream Specification");
if (Str.consume_front(":")) {
if (Str.consumeInteger(0, Offset))
return make_error<RawError>(raw_error_code::invalid_format,
"Invalid Stream Specification");
}
if (Str.consume_front("@")) {
if (Str.consumeInteger(0, Size))
return make_error<RawError>(raw_error_code::invalid_format,
"Invalid Stream Specification");
}
if (!Str.empty())
return make_error<RawError>(raw_error_code::invalid_format,
"Invalid Stream Specification");
return Error::success();
}
Error DumpOutputStyle::dumpStreamBytes() {
if (StreamPurposes.empty())
discoverStreamPurposes(File, StreamPurposes);
printHeader(P, "Stream Data");
ExitOnError Err("Unexpected error reading stream data");
for (auto &Str : opts::dump::DumpStreamData) {
uint32_t SI = 0;
uint32_t Begin = 0;
uint32_t Size = 0;
uint32_t End = 0;
if (auto EC = parseStreamSpec(Str, SI, Begin, Size))
return EC;
AutoIndent Indent(P);
if (SI >= File.getNumStreams()) {
P.formatLine("Stream {0}: Not present", SI);
continue;
}
auto S = MappedBlockStream::createIndexedStream(
File.getMsfLayout(), File.getMsfBuffer(), SI, File.getAllocator());
if (!S) {
P.NewLine();
P.formatLine("Stream {0}: Not present", SI);
continue;
}
if (Size == 0)
End = S->getLength();
else
End = std::min(Begin + Size, S->getLength());
P.formatLine("Stream {0} ({1:N} bytes): {2}", SI, S->getLength(),
StreamPurposes[SI]);
AutoIndent Indent2(P);
BinaryStreamReader R(*S);
ArrayRef<uint8_t> StreamData;
Err(R.readBytes(StreamData, S->getLength()));
Size = End - Begin;
StreamData = StreamData.slice(Begin, Size);
P.formatBinary("Data", StreamData, Begin);
}
return Error::success();
}
static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File,
uint32_t Index) {
ExitOnError Err("Unexpected error");

View File

@ -106,6 +106,20 @@ void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
OS << ")";
}
void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
uint64_t Base, uint32_t StartOffset) {
NewLine();
OS << Label << " (";
if (!Data.empty()) {
OS << "\n";
Base += StartOffset;
OS << format_bytes_with_ascii(Data, Base, 32, 4,
CurrentIndent + IndentSpaces, true);
NewLine();
}
OS << ")";
}
bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size) {
if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters))
return true;

View File

@ -45,6 +45,8 @@ public:
void formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
uint32_t StartOffset);
void formatBinary(StringRef Label, ArrayRef<uint8_t> Data, uint64_t BaseAddr,
uint32_t StartOffset);
bool hasColor() const { return UseColor; }
raw_ostream &getStream() { return OS; }

View File

@ -14,6 +14,7 @@
#include "llvm-pdbutil.h"
#include "Analyze.h"
#include "BytesOutputStyle.h"
#include "Diff.h"
#include "DumpOutputStyle.h"
#include "LinePrinter.h"
@ -87,6 +88,8 @@ using namespace llvm::pdb;
namespace opts {
cl::SubCommand DumpSubcommand("dump", "Dump MSF and CodeView debug info");
cl::SubCommand BytesSubcommand("bytes", "Dump raw bytes from the PDB file");
cl::SubCommand
PrettySubcommand("pretty",
"Dump semantic information about types and symbols");
@ -263,6 +266,26 @@ cl::list<std::string> InputFilenames(cl::Positional,
cl::OptionCategory FileOptions("Module & File Options");
namespace bytes {
llvm::Optional<BlockRange> DumpBlockRange;
cl::opt<std::string>
DumpBlockRangeOpt("block-data", cl::value_desc("start[-end]"),
cl::desc("Dump binary data from specified range."),
cl::sub(BytesSubcommand));
cl::list<std::string>
DumpStreamData("stream-data", cl::CommaSeparated, cl::ZeroOrMore,
cl::desc("Dump binary data from specified streams. Format "
"is SN[:Start][@Size]"),
cl::sub(BytesSubcommand));
cl::list<std::string> InputFilenames(cl::Positional,
cl::desc("<input PDB files>"),
cl::OneOrMore, cl::sub(BytesSubcommand));
} // namespace bytes
namespace dump {
cl::OptionCategory MsfOptions("MSF Container Options");
@ -276,17 +299,6 @@ cl::opt<bool> DumpSummary("summary", cl::desc("dump file summary"),
cl::opt<bool> DumpStreams("streams",
cl::desc("dump summary of the PDB streams"),
cl::cat(MsfOptions), cl::sub(DumpSubcommand));
cl::opt<std::string>
DumpBlockRangeOpt("block-data", cl::value_desc("start[-end]"),
cl::desc("Dump binary data from specified range."),
cl::cat(MsfOptions), cl::sub(DumpSubcommand));
llvm::Optional<BlockRange> DumpBlockRange;
cl::list<std::string>
DumpStreamData("stream-data", cl::CommaSeparated, cl::ZeroOrMore,
cl::desc("Dump binary data from specified streams. Format "
"is SN[:Start][@Size]"),
cl::cat(MsfOptions), cl::sub(DumpSubcommand));
// TYPE OPTIONS
cl::opt<bool> DumpTypes("types",
@ -626,6 +638,15 @@ static void dumpRaw(StringRef Path) {
ExitOnErr(O->dump());
}
static void dumpBytes(StringRef Path) {
std::unique_ptr<IPDBSession> Session;
auto &File = loadPDB(Path, Session);
auto O = llvm::make_unique<BytesOutputStyle>(File);
ExitOnErr(O->dump());
}
static void dumpAnalysis(StringRef Path) {
std::unique_ptr<IPDBSession> Session;
auto &File = loadPDB(Path, Session);
@ -882,6 +903,27 @@ static void mergePdbs() {
ExitOnErr(Builder.commit(OutFile));
}
static bool validateBlockRangeArgument() {
if (opts::bytes::DumpBlockRangeOpt.empty())
return true;
llvm::Regex R("^([^-]+)(-([^-]+))?$");
llvm::SmallVector<llvm::StringRef, 2> Matches;
if (!R.match(opts::bytes::DumpBlockRangeOpt, &Matches))
return false;
opts::bytes::DumpBlockRange.emplace();
if (!to_integer(Matches[1], opts::bytes::DumpBlockRange->Min))
return false;
if (!Matches[3].empty()) {
opts::bytes::DumpBlockRange->Max.emplace();
if (!to_integer(Matches[3], *opts::bytes::DumpBlockRange->Max))
return false;
}
return true;
}
int main(int argc_, const char *argv_[]) {
// Print a stack trace if we signal out.
sys::PrintStackTraceOnErrorSignal(argv_[0]);
@ -897,21 +939,11 @@ int main(int argc_, const char *argv_[]) {
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argv.size(), argv.data(), "LLVM PDB Dumper\n");
if (!opts::dump::DumpBlockRangeOpt.empty()) {
llvm::Regex R("^([0-9]+)(-([0-9]+))?$");
llvm::SmallVector<llvm::StringRef, 2> Matches;
if (!R.match(opts::dump::DumpBlockRangeOpt, &Matches)) {
errs() << "Argument '" << opts::dump::DumpBlockRangeOpt
<< "' invalid format.\n";
errs().flush();
exit(1);
}
opts::dump::DumpBlockRange.emplace();
Matches[1].getAsInteger(10, opts::dump::DumpBlockRange->Min);
if (!Matches[3].empty()) {
opts::dump::DumpBlockRange->Max.emplace();
Matches[3].getAsInteger(10, *opts::dump::DumpBlockRange->Max);
}
if (!validateBlockRangeArgument()) {
errs() << "Argument '" << opts::bytes::DumpBlockRangeOpt
<< "' invalid format.\n";
errs().flush();
exit(1);
}
if (opts::DumpSubcommand) {
@ -1018,6 +1050,9 @@ int main(int argc_, const char *argv_[]) {
} else if (opts::DumpSubcommand) {
std::for_each(opts::dump::InputFilenames.begin(),
opts::dump::InputFilenames.end(), dumpRaw);
} else if (opts::BytesSubcommand) {
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";

View File

@ -92,16 +92,19 @@ extern llvm::cl::opt<ClassDefinitionFormat> ClassFormat;
extern llvm::cl::opt<uint32_t> ClassRecursionDepth;
}
namespace dump {
namespace bytes {
struct BlockRange {
uint32_t Min;
llvm::Optional<uint32_t> Max;
};
extern llvm::Optional<BlockRange> DumpBlockRange;
extern llvm::cl::list<std::string> DumpStreamData;
} // namespace bytes
namespace dump {
extern llvm::cl::opt<bool> DumpSummary;
extern llvm::cl::opt<bool> DumpStreams;
extern llvm::Optional<BlockRange> DumpBlockRange;
extern llvm::cl::list<std::string> DumpStreamData;
extern llvm::cl::opt<bool> DumpLines;
extern llvm::cl::opt<bool> DumpInlineeLines;