[PDB] Support dumping injected sources via the DIA reader.

Injected sources are basically a way to add actual source file content
to your PDB. Presumably you could use this for shipping your source code
with your debug information, but in practice I can only find this being
used for embedding natvis files inside of PDBs.

In order to effectively test LLVM's natvis file injection, we need a way
to dump the injected sources of a PDB in a way that is authoritative
(i.e. based on Microsoft's understanding of the PDB format, and not
LLVM's). To this end, I've added support for dumping injected sources
via DIA. I made a PDB file that used the /natvis option to generate a
test case.

Differential Revision: https://reviews.llvm.org/D44405

llvm-svn: 327428
This commit is contained in:
Zachary Turner 2018-03-13 17:46:06 +00:00
parent 30167650e0
commit 1882229246
19 changed files with 435 additions and 0 deletions

View File

@ -0,0 +1,40 @@
//==- DIAEnumInjectedSources.h - DIA Injected Sources Enumerator -*- 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_DIA_DIAENUMINJECTEDSOURCES_H
#define LLVM_DEBUGINFO_PDB_DIA_DIAENUMINJECTEDSOURCES_H
#include "DIASupport.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h"
namespace llvm {
namespace pdb {
class DIASession;
class DIAEnumInjectedSources : public IPDBEnumChildren<IPDBInjectedSource> {
public:
explicit DIAEnumInjectedSources(
const DIASession &PDBSession,
CComPtr<IDiaEnumInjectedSources> DiaEnumerator);
uint32_t getChildCount() const override;
ChildTypePtr getChildAtIndex(uint32_t Index) const override;
ChildTypePtr getNext() override;
void reset() override;
DIAEnumInjectedSources *clone() const override;
private:
const DIASession &Session;
CComPtr<IDiaEnumInjectedSources> Enumerator;
};
} // namespace pdb
} // namespace llvm
#endif // LLVM_DEBUGINFO_PDB_DIA_DIAENUMINJECTEDSOURCES_H

View File

@ -0,0 +1,40 @@
//===- DIAInjectedSource.h - DIA impl for IPDBInjectedSource ----*- 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_DIA_DIAINJECTEDSOURCE_H
#define LLVM_DEBUGINFO_PDB_DIA_DIAINJECTEDSOURCE_H
#include "DIASupport.h"
#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h"
namespace llvm {
namespace pdb {
class DIASession;
class DIAInjectedSource : public IPDBInjectedSource {
public:
explicit DIAInjectedSource(const DIASession &Session,
CComPtr<IDiaInjectedSource> DiaSourceFile);
uint32_t getCrc32() const override;
uint64_t getCodeByteSize() const override;
std::string getFileName() const override;
std::string getObjectFileName() const override;
std::string getVirtualFileName() const override;
PDB_SourceCompression getCompression() const override;
std::string getCode() const override;
private:
const DIASession &Session;
CComPtr<IDiaInjectedSource> SourceFile;
};
} // namespace pdb
} // namespace llvm
#endif // LLVM_DEBUGINFO_PDB_DIA_DIAINJECTEDSOURCE_H

View File

@ -65,6 +65,9 @@ public:
std::unique_ptr<IPDBEnumDataStreams> getDebugStreams() const override;
std::unique_ptr<IPDBEnumTables> getEnumTables() const override;
std::unique_ptr<IPDBEnumInjectedSources> getInjectedSources() const override;
private:
CComPtr<IDiaSession> Session;
};

View File

@ -0,0 +1,31 @@
//===- DIAUtils.h - Utility functions for working with DIA ------*- 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_DIA_DIAUTILS_H
#define LLVM_DEBUGINFO_PDB_DIA_DIAUTILS_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/ConvertUTF.h"
template <typename Obj>
std::string invokeBstrMethod(Obj &Object,
HRESULT (__stdcall Obj::*Func)(BSTR *)) {
CComBSTR Str16;
HRESULT Result = (Object.*Func)(&Str16);
if (S_OK != Result)
return std::string();
std::string Str8;
llvm::ArrayRef<char> StrBytes(reinterpret_cast<char *>(Str16.m_str),
Str16.ByteLength());
llvm::convertUTF16ToUTF8String(StrBytes, Str8);
return Str8;
}
#endif // LLVM_DEBUGINFO_PDB_DIA_DIAUTILS_H

View File

@ -0,0 +1,42 @@
//===- IPDBInjectedSource.h - base class for PDB injected file --*- 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_IPDBINJECTEDSOURCE_H
#define LLVM_DEBUGINFO_PDB_IPDBINJECTEDSOURCE_H
#include "PDBTypes.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <string>
namespace llvm {
class raw_ostream;
namespace pdb {
/// IPDBInjectedSource defines an interface used to represent source files
/// which were injected directly into the PDB file during the compilation
/// process. This is used, for example, to add natvis files to a PDB, but
/// in theory could be used to add arbitrary source code.
class IPDBInjectedSource {
public:
virtual ~IPDBInjectedSource();
virtual uint32_t getCrc32() const = 0;
virtual uint64_t getCodeByteSize() const = 0;
virtual std::string getFileName() const = 0;
virtual std::string getObjectFileName() const = 0;
virtual std::string getVirtualFileName() const = 0;
virtual PDB_SourceCompression getCompression() const = 0;
virtual std::string getCode() const = 0;
};
} // namespace pdb
} // namespace llvm
#endif // LLVM_DEBUGINFO_PDB_IPDBINJECTEDSOURCE_H

View File

@ -69,6 +69,9 @@ public:
virtual std::unique_ptr<IPDBEnumDataStreams> getDebugStreams() const = 0;
virtual std::unique_ptr<IPDBEnumTables> getEnumTables() const = 0;
virtual std::unique_ptr<IPDBEnumInjectedSources>
getInjectedSources() const = 0;
};
}
}

View File

@ -85,6 +85,8 @@ public:
std::unique_ptr<IPDBEnumTables> getEnumTables() const override;
std::unique_ptr<IPDBEnumInjectedSources> getInjectedSources() const override;
PDBFile &getPDBFile() { return *Pdb; }
const PDBFile &getPDBFile() const { return *Pdb; }

View File

@ -34,6 +34,8 @@ raw_ostream &operator<<(raw_ostream &OS, const PDB_SymType &Tag);
raw_ostream &operator<<(raw_ostream &OS, const PDB_MemberAccess &Access);
raw_ostream &operator<<(raw_ostream &OS, const PDB_UdtType &Type);
raw_ostream &operator<<(raw_ostream &OS, const PDB_Machine &Machine);
raw_ostream &operator<<(raw_ostream &OS,
const PDB_SourceCompression &Compression);
raw_ostream &operator<<(raw_ostream &OS, const Variant &Value);
raw_ostream &operator<<(raw_ostream &OS, const VersionInfo &Version);

View File

@ -23,6 +23,7 @@ namespace llvm {
namespace pdb {
class IPDBDataStream;
class IPDBInjectedSource;
class IPDBLineNumber;
class IPDBSourceFile;
class IPDBTable;
@ -65,6 +66,7 @@ using IPDBEnumSourceFiles = IPDBEnumChildren<IPDBSourceFile>;
using IPDBEnumDataStreams = IPDBEnumChildren<IPDBDataStream>;
using IPDBEnumLineNumbers = IPDBEnumChildren<IPDBLineNumber>;
using IPDBEnumTables = IPDBEnumChildren<IPDBTable>;
using IPDBEnumInjectedSources = IPDBEnumChildren<IPDBInjectedSource>;
/// Specifies which PDB reader implementation is to be used. Only a value
/// of PDB_ReaderType::DIA is currently supported, but Native is in the works.
@ -133,6 +135,13 @@ enum class PDB_Machine {
WceMipsV2 = 0x169
};
enum class PDB_SourceCompression {
None,
RunLengthEncoded,
Huffman,
LZ,
};
/// These values correspond to the CV_call_e enumeration, and are documented
/// at the following locations:
/// https://msdn.microsoft.com/en-us/library/b2fc64ek.aspx

View File

@ -14,11 +14,13 @@ if(LLVM_ENABLE_DIA_SDK)
add_pdb_impl_folder(DIA
DIA/DIADataStream.cpp
DIA/DIAEnumDebugStreams.cpp
DIA/DIAEnumInjectedSources.cpp
DIA/DIAEnumLineNumbers.cpp
DIA/DIAEnumSourceFiles.cpp
DIA/DIAEnumSymbols.cpp
DIA/DIAEnumTables.cpp
DIA/DIAError.cpp
DIA/DIAInjectedSource.cpp
DIA/DIALineNumber.cpp
DIA/DIARawSymbol.cpp
DIA/DIASession.cpp

View File

@ -0,0 +1,54 @@
//==- DIAEnumSourceFiles.cpp - DIA Source File Enumerator impl ---*- 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/DIA/DIAEnumInjectedSources.h"
#include "llvm/DebugInfo/PDB/DIA/DIAInjectedSource.h"
#include "llvm/DebugInfo/PDB/PDBSymbol.h"
using namespace llvm;
using namespace llvm::pdb;
DIAEnumInjectedSources::DIAEnumInjectedSources(
const DIASession &PDBSession,
CComPtr<IDiaEnumInjectedSources> DiaEnumerator)
: Session(PDBSession), Enumerator(DiaEnumerator) {}
uint32_t DIAEnumInjectedSources::getChildCount() const {
LONG Count = 0;
return (S_OK == Enumerator->get_Count(&Count)) ? Count : 0;
}
std::unique_ptr<IPDBInjectedSource>
DIAEnumInjectedSources::getChildAtIndex(uint32_t Index) const {
CComPtr<IDiaInjectedSource> Item;
if (S_OK != Enumerator->Item(Index, &Item))
return nullptr;
return std::unique_ptr<IPDBInjectedSource>(
new DIAInjectedSource(Session, Item));
}
std::unique_ptr<IPDBInjectedSource> DIAEnumInjectedSources::getNext() {
CComPtr<IDiaInjectedSource> Item;
ULONG NumFetched = 0;
if (S_OK != Enumerator->Next(1, &Item, &NumFetched))
return nullptr;
return std::unique_ptr<IPDBInjectedSource>(
new DIAInjectedSource(Session, Item));
}
void DIAEnumInjectedSources::reset() { Enumerator->Reset(); }
DIAEnumInjectedSources *DIAEnumInjectedSources::clone() const {
CComPtr<IDiaEnumInjectedSources> EnumeratorClone;
if (S_OK != Enumerator->Clone(&EnumeratorClone))
return nullptr;
return new DIAEnumInjectedSources(Session, EnumeratorClone);
}

View File

@ -0,0 +1,64 @@
//===- DIAInjectedSource.cpp - DIA impl for IPDBInjectedSource --*- 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/DIA/DIAInjectedSource.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h"
#include "llvm/DebugInfo/PDB/DIA/DIASession.h"
#include "llvm/DebugInfo/PDB/DIA/DIAUtils.h"
using namespace llvm;
using namespace llvm::pdb;
DIAInjectedSource::DIAInjectedSource(const DIASession &Session,
CComPtr<IDiaInjectedSource> DiaSourceFile)
: Session(Session), SourceFile(DiaSourceFile) {}
uint32_t DIAInjectedSource::getCrc32() const {
DWORD Crc;
return (S_OK == SourceFile->get_crc(&Crc)) ? Crc : 0;
}
uint64_t DIAInjectedSource::getCodeByteSize() const {
ULONGLONG Size;
return (S_OK == SourceFile->get_length(&Size)) ? Size : 0;
}
std::string DIAInjectedSource::getFileName() const {
return invokeBstrMethod(*SourceFile, &IDiaInjectedSource::get_filename);
}
std::string DIAInjectedSource::getObjectFileName() const {
return invokeBstrMethod(*SourceFile, &IDiaInjectedSource::get_objectFilename);
}
std::string DIAInjectedSource::getVirtualFileName() const {
return invokeBstrMethod(*SourceFile,
&IDiaInjectedSource::get_virtualFilename);
}
PDB_SourceCompression DIAInjectedSource::getCompression() const {
DWORD Compression = 0;
if (S_OK != SourceFile->get_sourceCompression(&Compression))
return PDB_SourceCompression::None;
return static_cast<PDB_SourceCompression>(Compression);
}
std::string DIAInjectedSource::getCode() const {
DWORD DataSize;
if (S_OK != SourceFile->get_source(0, &DataSize, nullptr))
return "";
std::vector<uint8_t> Buffer(DataSize);
if (S_OK != SourceFile->get_source(DataSize, &DataSize, Buffer.data()))
return "";
assert(Buffer.size() == DataSize);
return std::string(reinterpret_cast<const char *>(Buffer.data()),
Buffer.size());
}

View File

@ -9,6 +9,7 @@
#include "llvm/DebugInfo/PDB/DIA/DIASession.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/DebugInfo/PDB/DIA/DIAEnumDebugStreams.h"
#include "llvm/DebugInfo/PDB/DIA/DIAEnumInjectedSources.h"
#include "llvm/DebugInfo/PDB/DIA/DIAEnumLineNumbers.h"
#include "llvm/DebugInfo/PDB/DIA/DIAEnumSourceFiles.h"
#include "llvm/DebugInfo/PDB/DIA/DIAEnumTables.h"
@ -310,3 +311,31 @@ std::unique_ptr<IPDBEnumTables> DIASession::getEnumTables() const {
return llvm::make_unique<DIAEnumTables>(DiaEnumerator);
}
static CComPtr<IDiaEnumInjectedSources>
getEnumInjectedSources(IDiaSession &Session) {
CComPtr<IDiaEnumInjectedSources> EIS;
CComPtr<IDiaEnumTables> ET;
CComPtr<IDiaTable> Table;
ULONG Count = 0;
if (Session.getEnumTables(&ET) != S_OK)
return nullptr;
while (ET->Next(1, &Table, &Count) == S_OK && Count == 1) {
// There is only one table that matches the given iid
if (S_OK ==
Table->QueryInterface(__uuidof(IDiaEnumInjectedSources), (void **)&EIS))
break;
Table.Release();
}
return EIS;
}
std::unique_ptr<IPDBEnumInjectedSources>
DIASession::getInjectedSources() const {
CComPtr<IDiaEnumInjectedSources> Files = getEnumInjectedSources(*Session);
if (!Files)
return nullptr;
return llvm::make_unique<DIAEnumInjectedSources>(*this, Files);
}

View File

@ -249,3 +249,8 @@ std::unique_ptr<IPDBEnumDataStreams> NativeSession::getDebugStreams() const {
std::unique_ptr<IPDBEnumTables> NativeSession::getEnumTables() const {
return nullptr;
}
std::unique_ptr<IPDBEnumInjectedSources>
NativeSession::getInjectedSources() const {
return nullptr;
}

View File

@ -254,6 +254,18 @@ raw_ostream &llvm::pdb::operator<<(raw_ostream &OS,
return OS;
}
raw_ostream &llvm::pdb::operator<<(raw_ostream &OS,
const PDB_SourceCompression &Compression) {
switch (Compression) {
CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SourceCompression, None, OS)
CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SourceCompression, Huffman, OS)
CASE_OUTPUT_ENUM_CLASS_NAME(PDB_SourceCompression, LZ, OS)
CASE_OUTPUT_ENUM_CLASS_STR(PDB_SourceCompression, RunLengthEncoded, "RLE",
OS)
}
return OS;
}
raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, const Variant &Value) {
switch (Value.Type) {
case PDB_VariantType::Bool:

View File

@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/PDB/IPDBDataStream.h"
#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h"
#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"
#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"
#include "llvm/DebugInfo/PDB/IPDBSession.h"
@ -29,3 +30,5 @@ IPDBRawSymbol::~IPDBRawSymbol() = default;
IPDBLineNumber::~IPDBLineNumber() = default;
IPDBTable::~IPDBTable() = default;
IPDBInjectedSource::~IPDBInjectedSource() = default;

Binary file not shown.

View File

@ -0,0 +1,16 @@
; RUN: llvm-pdbutil pretty -injected-sources -injected-source-content \
; RUN: %p/Inputs/InjectedSource.pdb | FileCheck %s
; RUN: llvm-pdbutil pretty -injected-sources -injected-source-content \
; RUN: %p/Inputs/ClassLayoutTest.pdb | FileCheck --check-prefix=NEGATIVE %s
; CHECK: ---INJECTED SOURCES---
; CHECK-NEXT: d:\sandbox\natvistest\natvistest\test.natvis (220 bytes): obj=<null>, vname=d:\sandbox\natvistest\natvistest\test.natvis, crc=2374979362, compression=None
; CHECK-NEXT: <?xml version="1.0" encoding="utf-8"?>
; CHECK-NEXT: <AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
; CHECK-NEXT: <Type Name="Foo">
; CHECK-NEXT: <DisplayString>This is a test</DisplayString>
; CHECK-NEXT: </Type>
; CHECK-NEXT: </AutoVisualizer>
; NEGATIVE: ---INJECTED SOURCES---
; NEGATIVE-NEXT: There are no injected sources.

View File

@ -45,6 +45,7 @@
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/PDB/GenericError.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h"
#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"
#include "llvm/DebugInfo/PDB/IPDBSession.h"
#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
@ -147,6 +148,14 @@ cl::list<std::string> InputFilenames(cl::Positional,
cl::desc("<input PDB files>"),
cl::OneOrMore, cl::sub(PrettySubcommand));
cl::opt<bool> InjectedSources("injected-sources",
cl::desc("Display injected sources"),
cl::cat(OtherOptions), cl::sub(PrettySubcommand));
cl::opt<bool> ShowInjectedSourceContent(
"injected-source-content",
cl::desc("When displaying an injected source, display the file content"),
cl::cat(OtherOptions), cl::sub(PrettySubcommand));
cl::opt<bool> Compilands("compilands", cl::desc("Display compilands"),
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<bool> Symbols("module-syms",
@ -840,6 +849,62 @@ bool opts::pretty::compareDataSymbols(
return getTypeLength(*F1) > getTypeLength(*F2);
}
static std::string stringOr(std::string Str, std::string IfEmpty) {
return (Str.empty()) ? IfEmpty : Str;
}
static void dumpInjectedSources(LinePrinter &Printer, IPDBSession &Session) {
auto Sources = Session.getInjectedSources();
if (0 == Sources->getChildCount()) {
Printer.printLine("There are no injected sources.");
return;
}
while (auto IS = Sources->getNext()) {
Printer.NewLine();
std::string File = stringOr(IS->getFileName(), "<null>");
uint64_t Size = IS->getCodeByteSize();
std::string Obj = stringOr(IS->getObjectFileName(), "<null>");
std::string VFName = stringOr(IS->getVirtualFileName(), "<null>");
uint32_t CRC = IS->getCrc32();
std::string CompressionStr;
llvm::raw_string_ostream Stream(CompressionStr);
Stream << IS->getCompression();
WithColor(Printer, PDB_ColorItem::Path).get() << File;
Printer << " (";
WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Size;
Printer << " bytes): ";
WithColor(Printer, PDB_ColorItem::Keyword).get() << "obj";
Printer << "=";
WithColor(Printer, PDB_ColorItem::Path).get() << Obj;
Printer << ", ";
WithColor(Printer, PDB_ColorItem::Keyword).get() << "vname";
Printer << "=";
WithColor(Printer, PDB_ColorItem::Path).get() << VFName;
Printer << ", ";
WithColor(Printer, PDB_ColorItem::Keyword).get() << "crc";
Printer << "=";
WithColor(Printer, PDB_ColorItem::LiteralValue).get() << CRC;
Printer << ", ";
WithColor(Printer, PDB_ColorItem::Keyword).get() << "compression";
Printer << "=";
WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Stream.str();
if (!opts::pretty::ShowInjectedSourceContent)
continue;
// Set the indent level to 0 when printing file content.
int Indent = Printer.getIndentLevel();
Printer.Unindent(Indent);
Printer.printLine(IS->getCode());
// Re-indent back to the original level.
Printer.Indent(Indent);
}
}
static void dumpPretty(StringRef Path) {
std::unique_ptr<IPDBSession> Session;
@ -989,6 +1054,19 @@ static void dumpPretty(StringRef Path) {
if (opts::pretty::Lines) {
Printer.NewLine();
}
if (opts::pretty::InjectedSources) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get()
<< "---INJECTED SOURCES---";
AutoIndent Indent1(Printer);
if (ReaderType == PDB_ReaderType::Native)
Printer.printLine(
"Injected sources are not supported with the native reader.");
else
dumpInjectedSources(Printer, *Session);
}
outs().flush();
}