-; 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: ============================================================
-; 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: ============================================================
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.
-; 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: ============================================================
--- /dev/null
+//===- 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);
+ }
+}
--- /dev/null
+//===- 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
add_llvm_tool(llvm-pdbutil
Analyze.cpp
+ BytesOutputStyle.cpp
Diff.cpp
DumpOutputStyle.cpp
llvm-pdbutil.cpp
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;
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");
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;
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; }
#include "llvm-pdbutil.h"
#include "Analyze.h"
+#include "BytesOutputStyle.h"
#include "Diff.h"
#include "DumpOutputStyle.h"
#include "LinePrinter.h"
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");
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");
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",
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);
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]);
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) {
} 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";
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;