492 lines
14 KiB
C++
492 lines
14 KiB
C++
|
//===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===//
|
||
|
//
|
||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "BytesOutputStyle.h"
|
||
|
|
||
|
#include "FormatUtil.h"
|
||
|
#include "StreamUtil.h"
|
||
|
#include "llvm-pdbutil.h"
|
||
|
|
||
|
#include "llvm/DebugInfo/CodeView/Formatters.h"
|
||
|
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
||
|
#include "llvm/DebugInfo/MSF/MSFCommon.h"
|
||
|
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
|
||
|
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
|
||
|
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
||
|
#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
|
||
|
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||
|
#include "llvm/DebugInfo/PDB/Native/RawError.h"
|
||
|
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
||
|
#include "llvm/Support/BinaryStreamReader.h"
|
||
|
#include "llvm/Support/FormatAdapters.h"
|
||
|
#include "llvm/Support/FormatVariadic.h"
|
||
|
|
||
|
using namespace llvm;
|
||
|
using namespace llvm::codeview;
|
||
|
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::DumpByteRange.hasValue()) {
|
||
|
auto &R = *opts::bytes::DumpByteRange;
|
||
|
uint32_t Max = R.Max.getValueOr(File.getFileSize());
|
||
|
|
||
|
if (Max < R.Min)
|
||
|
return make_error<StringError>("Invalid byte range specified. Max < Min",
|
||
|
inconvertibleErrorCode());
|
||
|
if (Max >= File.getFileSize())
|
||
|
return make_error<StringError>(
|
||
|
"Invalid byte range specified. Requested byte larger than file size",
|
||
|
inconvertibleErrorCode());
|
||
|
|
||
|
dumpByteRanges(R.Min, Max);
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::Fpm) {
|
||
|
dumpFpm();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (!opts::bytes::DumpStreamData.empty()) {
|
||
|
dumpStreamBytes();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::NameMap) {
|
||
|
dumpNameMap();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::SectionContributions) {
|
||
|
dumpSectionContributions();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::SectionMap) {
|
||
|
dumpSectionMap();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::ModuleInfos) {
|
||
|
dumpModuleInfos();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::FileInfo) {
|
||
|
dumpFileInfo();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::TypeServerMap) {
|
||
|
dumpTypeServerMap();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::ECData) {
|
||
|
dumpECData();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (!opts::bytes::TypeIndex.empty()) {
|
||
|
dumpTypeIndex(StreamTPI, opts::bytes::TypeIndex);
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (!opts::bytes::IdIndex.empty()) {
|
||
|
dumpTypeIndex(StreamIPI, opts::bytes::IdIndex);
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::ModuleSyms) {
|
||
|
dumpModuleSyms();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::ModuleC11) {
|
||
|
dumpModuleC11();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
if (opts::bytes::ModuleC13) {
|
||
|
dumpModuleC13();
|
||
|
P.NewLine();
|
||
|
}
|
||
|
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpNameMap() {
|
||
|
printHeader(P, "Named Stream Map");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
auto &InfoS = Err(File.getPDBInfoStream());
|
||
|
BinarySubstreamRef NS = InfoS.getNamedStreamsBuffer();
|
||
|
auto Layout = File.getStreamLayout(StreamPDB);
|
||
|
P.formatMsfStreamData("Named Stream Map", File, Layout, NS);
|
||
|
}
|
||
|
|
||
|
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::dumpSectionContributions() {
|
||
|
printHeader(P, "Section Contributions");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
auto &DbiS = Err(File.getPDBDbiStream());
|
||
|
BinarySubstreamRef NS = DbiS.getSectionContributionData();
|
||
|
auto Layout = File.getStreamLayout(StreamDBI);
|
||
|
P.formatMsfStreamData("Section Contributions", File, Layout, NS);
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpSectionMap() {
|
||
|
printHeader(P, "Section Map");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
auto &DbiS = Err(File.getPDBDbiStream());
|
||
|
BinarySubstreamRef NS = DbiS.getSecMapSubstreamData();
|
||
|
auto Layout = File.getStreamLayout(StreamDBI);
|
||
|
P.formatMsfStreamData("Section Map", File, Layout, NS);
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpModuleInfos() {
|
||
|
printHeader(P, "Module Infos");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
auto &DbiS = Err(File.getPDBDbiStream());
|
||
|
BinarySubstreamRef NS = DbiS.getModiSubstreamData();
|
||
|
auto Layout = File.getStreamLayout(StreamDBI);
|
||
|
P.formatMsfStreamData("Module Infos", File, Layout, NS);
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpFileInfo() {
|
||
|
printHeader(P, "File Info");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
auto &DbiS = Err(File.getPDBDbiStream());
|
||
|
BinarySubstreamRef NS = DbiS.getFileInfoSubstreamData();
|
||
|
auto Layout = File.getStreamLayout(StreamDBI);
|
||
|
P.formatMsfStreamData("File Info", File, Layout, NS);
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpTypeServerMap() {
|
||
|
printHeader(P, "Type Server Map");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
auto &DbiS = Err(File.getPDBDbiStream());
|
||
|
BinarySubstreamRef NS = DbiS.getTypeServerMapSubstreamData();
|
||
|
auto Layout = File.getStreamLayout(StreamDBI);
|
||
|
P.formatMsfStreamData("Type Server Map", File, Layout, NS);
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpECData() {
|
||
|
printHeader(P, "Edit and Continue Data");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
auto &DbiS = Err(File.getPDBDbiStream());
|
||
|
BinarySubstreamRef NS = DbiS.getECSubstreamData();
|
||
|
auto Layout = File.getStreamLayout(StreamDBI);
|
||
|
P.formatMsfStreamData("Edit and Continue Data", File, Layout, NS);
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx,
|
||
|
ArrayRef<uint32_t> Indices) {
|
||
|
assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
|
||
|
assert(!Indices.empty());
|
||
|
|
||
|
bool IsTpi = (StreamIdx == StreamTPI);
|
||
|
|
||
|
StringRef Label = IsTpi ? "Type (TPI) Records" : "Index (IPI) Records";
|
||
|
printHeader(P, Label);
|
||
|
auto &Stream = Err(IsTpi ? File.getPDBTpiStream() : File.getPDBIpiStream());
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
auto Substream = Stream.getTypeRecordsSubstream();
|
||
|
auto &Types = Err(initializeTypes(StreamIdx));
|
||
|
auto Layout = File.getStreamLayout(StreamIdx);
|
||
|
for (const auto &Id : Indices) {
|
||
|
TypeIndex TI(Id);
|
||
|
if (TI.toArrayIndex() >= Types.capacity()) {
|
||
|
P.formatLine("Error: TypeIndex {0} does not exist", TI);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
auto Type = Types.getType(TI);
|
||
|
uint32_t Offset = Types.getOffsetOfType(TI);
|
||
|
auto OneType = Substream.slice(Offset, Type.length());
|
||
|
P.formatMsfStreamData(formatv("Type {0}", TI).str(), File, Layout, OneType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename CallbackT>
|
||
|
static void iterateOneModule(PDBFile &File, LinePrinter &P,
|
||
|
const DbiModuleList &Modules, uint32_t I,
|
||
|
uint32_t Digits, uint32_t IndentLevel,
|
||
|
CallbackT Callback) {
|
||
|
if (I >= Modules.getModuleCount()) {
|
||
|
P.formatLine("Mod {0:4} | Invalid module index ",
|
||
|
fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
auto Modi = Modules.getModuleDescriptor(I);
|
||
|
P.formatLine("Mod {0:4} | `{1}`: ",
|
||
|
fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)),
|
||
|
Modi.getModuleName());
|
||
|
|
||
|
uint16_t ModiStream = Modi.getModuleStreamIndex();
|
||
|
AutoIndent Indent2(P, IndentLevel);
|
||
|
if (ModiStream == kInvalidStreamIndex)
|
||
|
return;
|
||
|
|
||
|
auto ModStreamData = File.createIndexedStream(ModiStream);
|
||
|
ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData));
|
||
|
if (auto EC = ModStream.reload()) {
|
||
|
P.formatLine("Could not parse debug information.");
|
||
|
return;
|
||
|
}
|
||
|
auto Layout = File.getStreamLayout(ModiStream);
|
||
|
Callback(I, ModStream, Layout);
|
||
|
}
|
||
|
|
||
|
template <typename CallbackT>
|
||
|
static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
|
||
|
CallbackT Callback) {
|
||
|
AutoIndent Indent(P);
|
||
|
if (!File.hasPDBDbiStream()) {
|
||
|
P.formatLine("DBI Stream not present");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ExitOnError Err("Unexpected error processing modules");
|
||
|
|
||
|
auto &Stream = Err(File.getPDBDbiStream());
|
||
|
|
||
|
const DbiModuleList &Modules = Stream.modules();
|
||
|
|
||
|
if (opts::bytes::ModuleIndex.getNumOccurrences() > 0) {
|
||
|
iterateOneModule(File, P, Modules, opts::bytes::ModuleIndex, 1, IndentLevel,
|
||
|
Callback);
|
||
|
} else {
|
||
|
uint32_t Count = Modules.getModuleCount();
|
||
|
uint32_t Digits = NumDigits(Count);
|
||
|
for (uint32_t I = 0; I < Count; ++I) {
|
||
|
iterateOneModule(File, P, Modules, I, Digits, IndentLevel, Callback);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpModuleSyms() {
|
||
|
printHeader(P, "Module Symbols");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
iterateModules(File, P, 2,
|
||
|
[this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
|
||
|
const MSFStreamLayout &Layout) {
|
||
|
auto Symbols = Stream.getSymbolsSubstream();
|
||
|
P.formatMsfStreamData("Symbols", File, Layout, Symbols);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpModuleC11() {
|
||
|
printHeader(P, "C11 Debug Chunks");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
iterateModules(File, P, 2,
|
||
|
[this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
|
||
|
const MSFStreamLayout &Layout) {
|
||
|
auto Chunks = Stream.getC11LinesSubstream();
|
||
|
P.formatMsfStreamData("C11 Debug Chunks", File, Layout,
|
||
|
Chunks);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpModuleC13() {
|
||
|
printHeader(P, "Debug Chunks");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
iterateModules(
|
||
|
File, P, 2,
|
||
|
[this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
|
||
|
const MSFStreamLayout &Layout) {
|
||
|
auto Chunks = Stream.getC13LinesSubstream();
|
||
|
if (opts::bytes::SplitChunks) {
|
||
|
for (const auto &SS : Stream.subsections()) {
|
||
|
BinarySubstreamRef ThisChunk;
|
||
|
std::tie(ThisChunk, Chunks) = Chunks.split(SS.getRecordLength());
|
||
|
P.formatMsfStreamData(formatChunkKind(SS.kind()), File, Layout,
|
||
|
ThisChunk);
|
||
|
}
|
||
|
} else {
|
||
|
P.formatMsfStreamData("Debug Chunks", File, Layout, Chunks);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) {
|
||
|
printHeader(P, "MSF Bytes");
|
||
|
|
||
|
AutoIndent Indent(P);
|
||
|
|
||
|
BinaryStreamReader Reader(File.getMsfBuffer());
|
||
|
ArrayRef<uint8_t> Data;
|
||
|
consumeError(Reader.skip(Min));
|
||
|
uint32_t Size = Max - Min + 1;
|
||
|
auto EC = Reader.readBytes(Data, Size);
|
||
|
assert(!EC);
|
||
|
consumeError(std::move(EC));
|
||
|
P.formatBinary("Bytes", Data, Min);
|
||
|
}
|
||
|
|
||
|
Expected<codeview::LazyRandomTypeCollection &>
|
||
|
BytesOutputStyle::initializeTypes(uint32_t StreamIdx) {
|
||
|
auto &TypeCollection = (StreamIdx == StreamTPI) ? TpiTypes : IpiTypes;
|
||
|
if (TypeCollection)
|
||
|
return *TypeCollection;
|
||
|
|
||
|
auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream()
|
||
|
: File.getPDBIpiStream();
|
||
|
if (!Tpi)
|
||
|
return Tpi.takeError();
|
||
|
|
||
|
auto &Types = Tpi->typeArray();
|
||
|
uint32_t Count = Tpi->getNumTypeRecords();
|
||
|
auto Offsets = Tpi->getTypeIndexOffsets();
|
||
|
TypeCollection =
|
||
|
std::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets);
|
||
|
|
||
|
return *TypeCollection;
|
||
|
}
|
||
|
|
||
|
void BytesOutputStyle::dumpFpm() {
|
||
|
printHeader(P, "Free Page Map");
|
||
|
|
||
|
msf::MSFStreamLayout FpmLayout = File.getFpmStreamLayout();
|
||
|
P.formatMsfStreamBlocks(File, FpmLayout);
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
AutoIndent Indent(P);
|
||
|
if (Spec.SI >= StreamPurposes.size()) {
|
||
|
P.formatLine("Stream {0}: Not present", Spec.SI);
|
||
|
continue;
|
||
|
}
|
||
|
P.formatMsfStreamData("Data", File, Spec.SI,
|
||
|
StreamPurposes[Spec.SI].getShortName(), Spec.Begin,
|
||
|
Spec.Size);
|
||
|
}
|
||
|
}
|