[llvm-pdbdump] Dump CodeView line information.

This first pass only splits apart the records and dumps the line
info kinds and binary data.  Subsequent patches will parse out
the binary data into more useful information and dump it in
detail.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@271576 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Zachary Turner 2016-06-02 20:11:22 +00:00
parent 66d860c198
commit b36db5aff2
14 changed files with 245 additions and 21 deletions

View File

@ -287,6 +287,7 @@ enum class ModifierOptions : uint16_t {
CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(ModifierOptions)
enum class ModuleSubstreamKind : uint32_t {
None = 0,
Symbols = 0xf1,
Lines = 0xf2,
StringTable = 0xf3,

View File

@ -30,6 +30,7 @@ ArrayRef<EnumEntry<uint32_t>> getCompileSym3FlagNames();
ArrayRef<EnumEntry<unsigned>> getCPUTypeNames();
ArrayRef<EnumEntry<uint32_t>> getFrameProcSymFlagNames();
ArrayRef<EnumEntry<uint16_t>> getExportSymFlagNames();
ArrayRef<EnumEntry<uint32_t>> getModuleSubstreamKindNames();
ArrayRef<EnumEntry<uint8_t>> getThunkOrdinalNames();
ArrayRef<EnumEntry<uint16_t>> getTrampolineNames();
ArrayRef<EnumEntry<COFF::SectionCharacteristics>>

View File

@ -23,6 +23,8 @@ public:
: Stream(&Stream), ViewOffset(0), Length(Stream.getLength()) {}
StreamRef(const StreamInterface &Stream, uint32_t Offset, uint32_t Length)
: Stream(&Stream), ViewOffset(Offset), Length(Length) {}
StreamRef(const StreamRef &Stream, uint32_t Offset, uint32_t Length) = delete;
StreamRef(const StreamRef &Other)
: Stream(Other.Stream), ViewOffset(Other.ViewOffset),
Length(Other.Length) {}

View File

@ -15,6 +15,7 @@
#include "llvm/DebugInfo/PDB/PDBTypes.h"
#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Raw/ModInfo.h"
#include "llvm/DebugInfo/PDB/Raw/ModuleSubstreamRecord.h"
#include "llvm/DebugInfo/PDB/Raw/NameHashTable.h"
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
#include "llvm/DebugInfo/PDB/Raw/RawTypes.h"

View File

@ -12,9 +12,11 @@
#include "llvm/ADT/iterator_range.h"
#include "llvm/DebugInfo/CodeView/CVRecord.h"
#include "llvm/DebugInfo/CodeView/StreamArray.h"
#include "llvm/DebugInfo/CodeView/StreamRef.h"
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Raw/ModuleSubstreamRecord.h"
#include "llvm/Support/Error.h"
namespace llvm {
@ -24,6 +26,8 @@ class ModInfo;
class ModStream {
public:
typedef codeview::VarStreamArray<ModuleSubstreamRecord> LineInfoArray;
ModStream(PDBFile &File, const ModInfo &Module);
~ModStream();
@ -32,6 +36,8 @@ public:
iterator_range<codeview::CVSymbolArray::Iterator>
symbols(bool *HadError) const;
iterator_range<LineInfoArray::Iterator> lines(bool *HadError) const;
private:
const ModInfo &Mod;
@ -41,6 +47,8 @@ private:
codeview::StreamRef LinesSubstream;
codeview::StreamRef C13LinesSubstream;
codeview::StreamRef GlobalRefsSubstream;
LineInfoArray LineInfo;
};
}
}

View File

@ -0,0 +1,51 @@
//===- ModuleSubstreamRecord.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_DEBUGINFO_PDB_RAW_MODULESUBSTREAMRECORD_H
#define LLVM_DEBUGINFO_PDB_RAW_MODULESUBSTREAMRECORD_H
#include "llvm/DebugInfo/CodeView/CodeView.h"
#include "llvm/DebugInfo/CodeView/StreamArray.h"
#include "llvm/DebugInfo/CodeView/StreamRef.h"
#include "llvm/Support/Error.h"
namespace llvm {
namespace pdb {
class ModuleSubstreamRecord {
public:
ModuleSubstreamRecord();
ModuleSubstreamRecord(codeview::ModuleSubstreamKind Kind,
codeview::StreamRef Data);
static Error initialize(codeview::StreamRef Stream,
ModuleSubstreamRecord &Info);
uint32_t getRecordLength() const;
codeview::ModuleSubstreamKind getSubstreamKind() const;
codeview::StreamRef getRecordData() const;
private:
codeview::ModuleSubstreamKind Kind;
codeview::StreamRef Data;
};
}
namespace codeview {
template <> struct VarStreamArrayExtractor<pdb::ModuleSubstreamRecord> {
Error operator()(StreamRef Stream, uint32_t &Length,
pdb::ModuleSubstreamRecord &Info) const {
if (auto EC = pdb::ModuleSubstreamRecord::initialize(Stream, Info))
return EC;
Length = Info.getRecordLength();
return Error::success();
}
};
}
}
#endif // LLVM_DEBUGINFO_PDB_RAW_MODULESUBSTREAMRECORD_H

View File

@ -72,6 +72,45 @@ struct SecMapEntry {
support::ulittle32_t SecByteLength; // Byte count of the segment or group.
};
// Corresponds to the `CV_DebugSSubsectionHeader_t` structure.
struct ModuleSubsectionHeader {
support::ulittle32_t Kind; // codeview::ModuleSubstreamKind enum
support::ulittle32_t Length; // number of bytes occupied by this record.
};
// Corresponds to the `CV_DebugSLinesHeader_t` structure.
struct LineTableSubsectionHeader {
support::ulittle32_t OffCon;
support::ulittle16_t SegCon;
support::ulittle16_t Flags;
support::ulittle32_t CbCon;
};
// Corresponds to the `CV_DebugSLinesFileBlockHeader_t` structure.
struct SourceFileBlockHeader {
support::ulittle32_t offFile;
support::ulittle32_t nLines;
support::ulittle32_t cbBlock;
// LineInfo lines[nLines];
// ColumnInfo columns[nColumns];
};
// Corresponds to `CV_Line_t` structure
struct LineInfo {
unsigned long Offset; // Offset to start of code bytes for line number
unsigned long LinenumStart : 24; // line where statement/expression starts
unsigned long
DeltaLineEnd : 7; // delta to line where statement ends (optional)
unsigned long FStatement : 1; // true if a statement linenumber, else an
// expression line num
};
// Corresponds to `CV_Column_t` structure
struct ColumnInfo {
support::ulittle16_t OffColumnStart;
support::ulittle16_t OffColumnEnd;
};
} // namespace pdb
} // namespace llvm

View File

@ -231,6 +231,23 @@ static const EnumEntry<uint32_t> FrameProcSymFlagNames[] = {
CV_ENUM_CLASS_ENT(FrameProcedureOptions, GuardCfw),
};
static const EnumEntry<uint32_t> ModuleSubstreamKindNames[] = {
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, None),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, Symbols),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, Lines),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, StringTable),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, FileChecksums),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, FrameData),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, InlineeLines),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, CrossScopeImports),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, CrossScopeExports),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, ILLines),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, FuncMDTokenMap),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, TypeMDTokenMap),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, MergedAssemblyInput),
CV_ENUM_CLASS_ENT(ModuleSubstreamKind, CoffSymbolRVA),
};
static const EnumEntry<uint16_t> ExportSymFlagNames[] = {
CV_ENUM_CLASS_ENT(ExportFlags, IsConstant),
CV_ENUM_CLASS_ENT(ExportFlags, IsData),
@ -331,6 +348,9 @@ ArrayRef<EnumEntry<uint32_t>> getFrameProcSymFlagNames() {
ArrayRef<EnumEntry<uint16_t>> getExportSymFlagNames() {
return makeArrayRef(ExportSymFlagNames);
}
ArrayRef<EnumEntry<uint32_t>> getModuleSubstreamKindNames() {
return makeArrayRef(ModuleSubstreamKindNames);
}
ArrayRef<EnumEntry<uint8_t>> getThunkOrdinalNames() {
return makeArrayRef(ThunkOrdinalNames);
}

View File

@ -15,7 +15,7 @@
using namespace llvm;
using namespace llvm::codeview;
StreamReader::StreamReader(StreamRef Stream) : Stream(Stream), Offset(0) {}
StreamReader::StreamReader(StreamRef S) : Stream(S), Offset(0) {}
Error StreamReader::readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size) {
if (auto EC = Stream.readBytes(Offset, Size, Buffer))

View File

@ -33,6 +33,7 @@ add_pdb_impl_folder(Raw
Raw/InfoStream.cpp
Raw/MappedBlockStream.cpp
Raw/ModInfo.cpp
Raw/ModuleSubstreamRecord.cpp
Raw/ModStream.cpp
Raw/NameHashTable.cpp
Raw/NameMap.cpp

View File

@ -13,6 +13,7 @@
#include "llvm/DebugInfo/PDB/Raw/ModInfo.h"
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
#include "llvm/DebugInfo/PDB/Raw/RawTypes.h"
using namespace llvm;
using namespace llvm::pdb;
@ -45,9 +46,9 @@ Error ModStream::reload() {
return EC;
if (auto EC = Reader.readStreamRef(C13LinesSubstream, C13Size))
return EC;
ArrayRef<uint8_t> LineBytes;
codeview::StreamReader LinesReader(C13LinesSubstream);
if (auto EC = LinesReader.readBytes(LineBytes, C13LinesSubstream.getLength()))
codeview::StreamReader LineReader(C13LinesSubstream);
if (auto EC = LineReader.readArray(LineInfo, LineReader.bytesRemaining()))
return EC;
uint32_t GlobalRefsSize;
@ -67,3 +68,8 @@ ModStream::symbols(bool *HadError) const {
return llvm::make_range(SymbolsSubstream.begin(HadError),
SymbolsSubstream.end());
}
iterator_range<ModStream::LineInfoArray::Iterator>
ModStream::lines(bool *HadError) const {
return llvm::make_range(LineInfo.begin(HadError), LineInfo.end());
}

View File

@ -0,0 +1,49 @@
//===- ModuleSubstreamRecord.cpp --------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/PDB/Raw/ModuleSubstreamRecord.h"
#include "llvm/DebugInfo/CodeView/StreamReader.h"
#include "llvm/DebugInfo/PDB/Raw/RawTypes.h"
using namespace llvm;
using namespace llvm::codeview;
using namespace llvm::pdb;
ModuleSubstreamRecord::ModuleSubstreamRecord()
: Kind(ModuleSubstreamKind::None) {}
ModuleSubstreamRecord::ModuleSubstreamRecord(ModuleSubstreamKind Kind,
StreamRef Data)
: Kind(Kind), Data(Data) {}
Error ModuleSubstreamRecord::initialize(StreamRef Stream,
ModuleSubstreamRecord &Info) {
const ModuleSubsectionHeader *Header;
StreamReader Reader(Stream);
if (auto EC = Reader.readObject(Header))
return EC;
ModuleSubstreamKind Kind =
static_cast<ModuleSubstreamKind>(uint32_t(Header->Kind));
if (auto EC = Reader.readStreamRef(Info.Data, Header->Length))
return EC;
Info.Kind = Kind;
return Error::success();
}
uint32_t ModuleSubstreamRecord::getRecordLength() const {
return sizeof(ModuleSubsectionHeader) + Data.getLength();
}
ModuleSubstreamKind ModuleSubstreamRecord::getSubstreamKind() const {
return Kind;
}
StreamRef ModuleSubstreamRecord::getRecordData() const { return Data; }

View File

@ -1,8 +1,8 @@
; RUN: llvm-pdbdump -raw-headers -raw-tpi-records -raw-tpi-record-bytes -raw-module-syms \
; RUN: -raw-sym-record-bytes -raw-publics -raw-module-files -raw-stream-name=/names \
; RUN: -raw-stream-summary -raw-stream-blocks -raw-ipi-records -raw-ipi-record-bytes \
; RUN: -raw-section-contribs -raw-section-map -raw-section-headers %p/Inputs/empty.pdb \
; RUN: | FileCheck -check-prefix=EMPTY %s
; RUN: -raw-section-contribs -raw-section-map -raw-section-headers -raw-line-info \
; RUN: %p/Inputs/empty.pdb | FileCheck -check-prefix=EMPTY %s
; RUN: llvm-pdbdump -raw-all %p/Inputs/empty.pdb | FileCheck -check-prefix=ALL %s
; RUN: llvm-pdbdump -raw-headers -raw-stream-name=/names -raw-modules -raw-module-files \
; RUN: %p/Inputs/big-read.pdb | FileCheck -check-prefix=BIG %s
@ -330,6 +330,23 @@
; EMPTY-NEXT: )
; EMPTY-NEXT: }
; EMPTY-NEXT: ]
; EMPTY-NEXT: LineInfo [
; EMPTY-NEXT: {
; EMPTY-NEXT: Kind: Lines (0xF2)
; EMPTY-NEXT: Data (
; EMPTY-NEXT: 0000: 10000000 01000000 0A000000 00000000 |................|
; EMPTY-NEXT: 0010: 03000000 24000000 00000000 05000080 |....$...........|
; EMPTY-NEXT: 0020: 03000000 06000080 08000000 07000080 |................|
; EMPTY-NEXT: )
; EMPTY-NEXT: }
; EMPTY-NEXT: {
; EMPTY-NEXT: Kind: FileChecksums (0xF4)
; EMPTY-NEXT: Data (
; EMPTY-NEXT: 0000: 56000000 1001A0A5 BD0D3ECD 93FC29D1 |V.........>...).|
; EMPTY-NEXT: 0010: 9DE826FB F4BC0000 |..&.....|
; EMPTY-NEXT: )
; EMPTY-NEXT: }
; EMPTY-NEXT: ]
; EMPTY-NEXT: }
; EMPTY-NEXT: {
; EMPTY-NEXT: Name: * Linker *
@ -568,6 +585,8 @@
; EMPTY-NEXT: )
; EMPTY-NEXT: }
; EMPTY-NEXT: ]
; EMPTY-NEXT: LineInfo [
; EMPTY-NEXT: ]
; EMPTY-NEXT: }
; EMPTY-NEXT: ]
; EMPTY-NEXT: }

View File

@ -144,6 +144,9 @@ cl::opt<bool> DumpPublics("raw-publics", cl::desc("dump Publics stream data"),
cl::opt<bool> DumpSectionContribs("raw-section-contribs",
cl::desc("dump section contributions"),
cl::cat(NativeOptions));
cl::opt<bool> DumpLineInfo("raw-line-info",
cl::desc("dump file and line information"),
cl::cat(NativeOptions));
cl::opt<bool> DumpSectionMap("raw-section-map", cl::desc("dump section map"),
cl::cat(NativeOptions));
cl::opt<bool>
@ -429,8 +432,8 @@ static Error dumpNamedStream(ScopedPrinter &P, PDBFile &File) {
static Error dumpDbiStream(ScopedPrinter &P, PDBFile &File,
codeview::CVTypeDumper &TD) {
bool DumpModules =
opts::DumpModules || opts::DumpModuleSyms || opts::DumpModuleFiles;
bool DumpModules = opts::DumpModules || opts::DumpModuleSyms ||
opts::DumpModuleFiles || opts::DumpLineInfo;
if (!opts::DumpHeaders && !DumpModules)
return Error::success();
@ -487,25 +490,45 @@ static Error dumpDbiStream(ScopedPrinter &P, PDBFile &File,
(Modi.Info.getModuleStreamIndex() < File.getNumStreams());
bool ShouldDumpSymbols =
(opts::DumpModuleSyms || opts::DumpSymRecordBytes);
if (HasModuleDI && ShouldDumpSymbols) {
ListScope SS(P, "Symbols");
if (HasModuleDI && (ShouldDumpSymbols || opts::DumpLineInfo)) {
ModStream ModS(File, Modi.Info);
if (auto EC = ModS.reload())
return EC;
codeview::CVSymbolDumper SD(P, TD, nullptr, false);
bool HadError = false;
for (auto &S : ModS.symbols(&HadError)) {
DictScope DD(P, "");
if (ShouldDumpSymbols) {
ListScope SS(P, "Symbols");
codeview::CVSymbolDumper SD(P, TD, nullptr, false);
bool HadError = false;
for (auto &S : ModS.symbols(&HadError)) {
DictScope DD(P, "");
if (opts::DumpModuleSyms)
SD.dump(S);
if (opts::DumpSymRecordBytes)
P.printBinaryBlock("Bytes", S.Data);
if (opts::DumpModuleSyms)
SD.dump(S);
if (opts::DumpSymRecordBytes)
P.printBinaryBlock("Bytes", S.Data);
}
if (HadError)
return make_error<RawError>(
raw_error_code::corrupt_file,
"DBI stream contained corrupt symbol record");
}
if (opts::DumpLineInfo) {
ListScope SS(P, "LineInfo");
bool HadError = false;
for (auto &L : ModS.lines(&HadError)) {
DictScope DD(P, "");
P.printEnum("Kind", uint32_t(L.getSubstreamKind()),
codeview::getModuleSubstreamKindNames());
ArrayRef<uint8_t> Data;
codeview::StreamReader R(L.getRecordData());
if (auto EC = R.readBytes(Data, R.bytesRemaining())) {
return make_error<RawError>(
raw_error_code::corrupt_file,
"DBI stream contained corrupt line info record");
}
P.printBinaryBlock("Data", Data);
}
}
if (HadError)
return make_error<RawError>(raw_error_code::corrupt_file,
"DBI stream contained corrupt record");
}
}
}
@ -805,6 +828,8 @@ bool isRawDumpEnabled() {
return true;
if (opts::DumpSectionMap)
return true;
if (opts::DumpLineInfo)
return true;
return false;
}
@ -972,6 +997,7 @@ int main(int argc_, const char *argv_[]) {
opts::DumpIpiRecords = true;
opts::DumpSectionMap = true;
opts::DumpSectionContribs = true;
opts::DumpLineInfo = true;
}
// When adding filters for excluded compilands and types, we need to remember