forked from OSchip/llvm-project
[pdb] Round trip the PDB stream between YAML and binary PDB.
This gets writing of the PDB stream working. llvm-svn: 274647
This commit is contained in:
parent
2ebe18b609
commit
8848a7a6b2
|
@ -75,6 +75,8 @@ public:
|
||||||
codeview::FixedStreamArray<SecMapEntry> getSectionMap() const;
|
codeview::FixedStreamArray<SecMapEntry> getSectionMap() const;
|
||||||
void visitSectionContributions(ISectionContribVisitor &Visitor) const;
|
void visitSectionContributions(ISectionContribVisitor &Visitor) const;
|
||||||
|
|
||||||
|
Error commit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Error initializeSectionContributionData();
|
Error initializeSectionContributionData();
|
||||||
Error initializeSectionHeadersData();
|
Error initializeSectionHeadersData();
|
||||||
|
|
|
@ -33,9 +33,16 @@ public:
|
||||||
uint32_t getAge() const;
|
uint32_t getAge() const;
|
||||||
PDB_UniqueId getGuid() const;
|
PDB_UniqueId getGuid() const;
|
||||||
|
|
||||||
|
void setVersion(PdbRaw_ImplVer Ver);
|
||||||
|
void setSignature(uint32_t Sig);
|
||||||
|
void setAge(uint32_t Age);
|
||||||
|
void setGuid(PDB_UniqueId Guid);
|
||||||
|
|
||||||
uint32_t getNamedStreamIndex(llvm::StringRef Name) const;
|
uint32_t getNamedStreamIndex(llvm::StringRef Name) const;
|
||||||
iterator_range<StringMapConstIterator<uint32_t>> named_streams() const;
|
iterator_range<StringMapConstIterator<uint32_t>> named_streams() const;
|
||||||
|
|
||||||
|
Error commit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<MappedBlockStream> Stream;
|
std::unique_ptr<MappedBlockStream> Stream;
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,8 @@ public:
|
||||||
iterator_range<codeview::ModuleSubstreamArray::Iterator>
|
iterator_range<codeview::ModuleSubstreamArray::Iterator>
|
||||||
lines(bool *HadError) const;
|
lines(bool *HadError) const;
|
||||||
|
|
||||||
|
Error commit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ModInfo &Mod;
|
const ModInfo &Mod;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
namespace codeview {
|
namespace codeview {
|
||||||
class StreamReader;
|
class StreamReader;
|
||||||
|
class StreamWriter;
|
||||||
}
|
}
|
||||||
namespace pdb {
|
namespace pdb {
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ public:
|
||||||
NameMap();
|
NameMap();
|
||||||
|
|
||||||
Error load(codeview::StreamReader &Stream);
|
Error load(codeview::StreamReader &Stream);
|
||||||
|
Error commit(codeview::StreamWriter &Writer);
|
||||||
|
|
||||||
bool tryGetValue(StringRef Name, uint32_t &Value) const;
|
bool tryGetValue(StringRef Name, uint32_t &Value) const;
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ public:
|
||||||
return BlockNumber * BlockSize;
|
return BlockNumber * BlockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expected<InfoStream &> emplacePDBInfoStream();
|
||||||
|
|
||||||
Expected<InfoStream &> getPDBInfoStream();
|
Expected<InfoStream &> getPDBInfoStream();
|
||||||
Expected<DbiStream &> getPDBDbiStream();
|
Expected<DbiStream &> getPDBDbiStream();
|
||||||
Expected<TpiStream &> getPDBTpiStream();
|
Expected<TpiStream &> getPDBTpiStream();
|
||||||
|
@ -117,8 +119,9 @@ public:
|
||||||
|
|
||||||
Error setSuperBlock(const SuperBlock *Block);
|
Error setSuperBlock(const SuperBlock *Block);
|
||||||
void setStreamSizes(ArrayRef<support::ulittle32_t> Sizes);
|
void setStreamSizes(ArrayRef<support::ulittle32_t> Sizes);
|
||||||
void setStreamMap(ArrayRef<support::ulittle32_t> Directory,
|
void setDirectoryBlocks(ArrayRef<support::ulittle32_t> Directory);
|
||||||
std::vector<ArrayRef<support::ulittle32_t>> &Streams);
|
void setStreamMap(std::vector<ArrayRef<support::ulittle32_t>> &Streams);
|
||||||
|
Error generateSimpleStreamMap();
|
||||||
Error commit();
|
Error commit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -51,6 +51,8 @@ public:
|
||||||
return SectionOffsets;
|
return SectionOffsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error commit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PDBFile &Pdb;
|
PDBFile &Pdb;
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ public:
|
||||||
iterator_range<codeview::CVSymbolArray::Iterator>
|
iterator_range<codeview::CVSymbolArray::Iterator>
|
||||||
getSymbols(bool *HadError) const;
|
getSymbols(bool *HadError) const;
|
||||||
|
|
||||||
|
Error commit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
codeview::CVSymbolArray SymbolRecords;
|
codeview::CVSymbolArray SymbolRecords;
|
||||||
std::unique_ptr<MappedBlockStream> Stream;
|
std::unique_ptr<MappedBlockStream> Stream;
|
||||||
|
|
|
@ -49,6 +49,8 @@ public:
|
||||||
|
|
||||||
iterator_range<codeview::CVTypeArray::Iterator> types(bool *HadError) const;
|
iterator_range<codeview::CVTypeArray::Iterator> types(bool *HadError) const;
|
||||||
|
|
||||||
|
Error commit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Error verifyHashValues();
|
Error verifyHashValues();
|
||||||
|
|
||||||
|
|
|
@ -452,3 +452,5 @@ Expected<StringRef> DbiStream::getFileNameForIndex(uint32_t Index) const {
|
||||||
return std::move(EC);
|
return std::move(EC);
|
||||||
return Name;
|
return Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error DbiStream::commit() { return Error::success(); }
|
||||||
|
|
|
@ -11,27 +11,31 @@
|
||||||
#include "llvm/ADT/BitVector.h"
|
#include "llvm/ADT/BitVector.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include "llvm/DebugInfo/CodeView/StreamReader.h"
|
#include "llvm/DebugInfo/CodeView/StreamReader.h"
|
||||||
|
#include "llvm/DebugInfo/CodeView/StreamWriter.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h"
|
#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
|
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
|
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
using namespace llvm::codeview;
|
||||||
using namespace llvm::pdb;
|
using namespace llvm::pdb;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct Header {
|
||||||
|
support::ulittle32_t Version;
|
||||||
|
support::ulittle32_t Signature;
|
||||||
|
support::ulittle32_t Age;
|
||||||
|
PDB_UniqueId Guid;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
InfoStream::InfoStream(std::unique_ptr<MappedBlockStream> Stream)
|
InfoStream::InfoStream(std::unique_ptr<MappedBlockStream> Stream)
|
||||||
: Stream(std::move(Stream)) {}
|
: Stream(std::move(Stream)) {}
|
||||||
|
|
||||||
Error InfoStream::reload() {
|
Error InfoStream::reload() {
|
||||||
codeview::StreamReader Reader(*Stream);
|
codeview::StreamReader Reader(*Stream);
|
||||||
|
|
||||||
struct Header {
|
|
||||||
support::ulittle32_t Version;
|
|
||||||
support::ulittle32_t Signature;
|
|
||||||
support::ulittle32_t Age;
|
|
||||||
PDB_UniqueId Guid;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Header *H;
|
const Header *H;
|
||||||
if (auto EC = Reader.readObject(H))
|
if (auto EC = Reader.readObject(H))
|
||||||
return joinErrors(
|
return joinErrors(
|
||||||
|
@ -39,9 +43,16 @@ Error InfoStream::reload() {
|
||||||
make_error<RawError>(raw_error_code::corrupt_file,
|
make_error<RawError>(raw_error_code::corrupt_file,
|
||||||
"PDB Stream does not contain a header."));
|
"PDB Stream does not contain a header."));
|
||||||
|
|
||||||
if (H->Version < PdbRaw_ImplVer::PdbImplVC70)
|
switch (H->Version) {
|
||||||
|
case PdbImplVC70:
|
||||||
|
case PdbImplVC80:
|
||||||
|
case PdbImplVC110:
|
||||||
|
case PdbImplVC140:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||||
"Unsupported PDB stream version.");
|
"Unsupported PDB stream version.");
|
||||||
|
}
|
||||||
|
|
||||||
Version = H->Version;
|
Version = H->Version;
|
||||||
Signature = H->Signature;
|
Signature = H->Signature;
|
||||||
|
@ -72,3 +83,27 @@ uint32_t InfoStream::getSignature() const { return Signature; }
|
||||||
uint32_t InfoStream::getAge() const { return Age; }
|
uint32_t InfoStream::getAge() const { return Age; }
|
||||||
|
|
||||||
PDB_UniqueId InfoStream::getGuid() const { return Guid; }
|
PDB_UniqueId InfoStream::getGuid() const { return Guid; }
|
||||||
|
|
||||||
|
void InfoStream::setVersion(PdbRaw_ImplVer Ver) {
|
||||||
|
Version = static_cast<uint32_t>(Ver);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoStream::setSignature(uint32_t Sig) { Signature = Sig; }
|
||||||
|
|
||||||
|
void InfoStream::setAge(uint32_t Age) { this->Age = Age; }
|
||||||
|
|
||||||
|
void InfoStream::setGuid(PDB_UniqueId Guid) { this->Guid = Guid; }
|
||||||
|
|
||||||
|
Error InfoStream::commit() {
|
||||||
|
StreamWriter Writer(*Stream);
|
||||||
|
|
||||||
|
Header H;
|
||||||
|
H.Age = Age;
|
||||||
|
H.Signature = Signature;
|
||||||
|
H.Version = Version;
|
||||||
|
H.Guid = Guid;
|
||||||
|
if (auto EC = Writer.writeObject(H))
|
||||||
|
return EC;
|
||||||
|
|
||||||
|
return NamedStreams.commit(Writer);
|
||||||
|
}
|
||||||
|
|
|
@ -78,3 +78,5 @@ iterator_range<codeview::ModuleSubstreamArray::Iterator>
|
||||||
ModStream::lines(bool *HadError) const {
|
ModStream::lines(bool *HadError) const {
|
||||||
return llvm::make_range(LineInfo.begin(HadError), LineInfo.end());
|
return llvm::make_range(LineInfo.begin(HadError), LineInfo.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error ModStream::commit() { return Error::success(); }
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
#include "llvm/DebugInfo/PDB/Raw/NameMap.h"
|
#include "llvm/DebugInfo/PDB/Raw/NameMap.h"
|
||||||
#include "llvm/ADT/SparseBitVector.h"
|
#include "llvm/ADT/SparseBitVector.h"
|
||||||
#include "llvm/DebugInfo/CodeView/StreamReader.h"
|
#include "llvm/DebugInfo/CodeView/StreamReader.h"
|
||||||
|
#include "llvm/DebugInfo/CodeView/StreamWriter.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
|
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
using namespace llvm::codeview;
|
||||||
using namespace llvm::pdb;
|
using namespace llvm::pdb;
|
||||||
|
|
||||||
NameMap::NameMap() {}
|
NameMap::NameMap() {}
|
||||||
|
@ -143,6 +145,24 @@ Error NameMap::load(codeview::StreamReader &Stream) {
|
||||||
return Error::success();
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error NameMap::commit(codeview::StreamWriter &Writer) {
|
||||||
|
if (auto EC = Writer.writeInteger(0U)) // Number of bytes in table
|
||||||
|
return EC;
|
||||||
|
|
||||||
|
if (auto EC = Writer.writeInteger(0U)) // Hash Size
|
||||||
|
return EC;
|
||||||
|
|
||||||
|
if (auto EC = Writer.writeInteger(0U)) // Max Number of Strings
|
||||||
|
return EC;
|
||||||
|
|
||||||
|
if (auto EC = Writer.writeInteger(0U)) // Num Present Words
|
||||||
|
return EC;
|
||||||
|
|
||||||
|
if (auto EC = Writer.writeInteger(0U)) // Num Deleted Words
|
||||||
|
return EC;
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
iterator_range<StringMapConstIterator<uint32_t>> NameMap::entries() const {
|
iterator_range<StringMapConstIterator<uint32_t>> NameMap::entries() const {
|
||||||
return llvm::make_range<StringMapConstIterator<uint32_t>>(Mapping.begin(),
|
return llvm::make_range<StringMapConstIterator<uint32_t>>(Mapping.begin(),
|
||||||
Mapping.end());
|
Mapping.end());
|
||||||
|
|
|
@ -89,7 +89,7 @@ Error PDBFile::setBlockData(uint32_t BlockIndex, uint32_t Offset,
|
||||||
return make_error<RawError>(
|
return make_error<RawError>(
|
||||||
raw_error_code::invalid_block_address,
|
raw_error_code::invalid_block_address,
|
||||||
"setBlockData attempted to write out of block bounds.");
|
"setBlockData attempted to write out of block bounds.");
|
||||||
if (Data.size() >= getBlockSize() - Offset)
|
if (Data.size() > getBlockSize() - Offset)
|
||||||
return make_error<RawError>(
|
return make_error<RawError>(
|
||||||
raw_error_code::invalid_block_address,
|
raw_error_code::invalid_block_address,
|
||||||
"setBlockData attempted to write out of block bounds.");
|
"setBlockData attempted to write out of block bounds.");
|
||||||
|
@ -167,6 +167,17 @@ llvm::ArrayRef<support::ulittle32_t> PDBFile::getDirectoryBlockArray() const {
|
||||||
return DirectoryBlocks;
|
return DirectoryBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expected<InfoStream &> PDBFile::emplacePDBInfoStream() {
|
||||||
|
if (Info)
|
||||||
|
Info.reset();
|
||||||
|
|
||||||
|
auto InfoS = MappedBlockStream::createIndexedStream(StreamPDB, *this);
|
||||||
|
if (!InfoS)
|
||||||
|
return InfoS.takeError();
|
||||||
|
Info = llvm::make_unique<InfoStream>(std::move(*InfoS));
|
||||||
|
return *Info;
|
||||||
|
}
|
||||||
|
|
||||||
Expected<InfoStream &> PDBFile::getPDBInfoStream() {
|
Expected<InfoStream &> PDBFile::getPDBInfoStream() {
|
||||||
if (!Info) {
|
if (!Info) {
|
||||||
auto InfoS = MappedBlockStream::createIndexedStream(StreamPDB, *this);
|
auto InfoS = MappedBlockStream::createIndexedStream(StreamPDB, *this);
|
||||||
|
@ -340,12 +351,59 @@ void PDBFile::setStreamSizes(ArrayRef<support::ulittle32_t> Sizes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDBFile::setStreamMap(
|
void PDBFile::setStreamMap(
|
||||||
ArrayRef<support::ulittle32_t> Directory,
|
|
||||||
std::vector<ArrayRef<support::ulittle32_t>> &Streams) {
|
std::vector<ArrayRef<support::ulittle32_t>> &Streams) {
|
||||||
DirectoryBlocks = Directory;
|
|
||||||
StreamMap = Streams;
|
StreamMap = Streams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDBFile::setDirectoryBlocks(ArrayRef<support::ulittle32_t> Directory) {
|
||||||
|
DirectoryBlocks = Directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error PDBFile::generateSimpleStreamMap() {
|
||||||
|
if (StreamSizes.empty())
|
||||||
|
return Error::success();
|
||||||
|
|
||||||
|
static std::vector<std::vector<support::ulittle32_t>> StaticMap;
|
||||||
|
StreamMap.clear();
|
||||||
|
StaticMap.clear();
|
||||||
|
|
||||||
|
// Figure out how many blocks are needed for all streams, and set the first
|
||||||
|
// used block to the highest block so that we can write the rest of the
|
||||||
|
// blocks contiguously.
|
||||||
|
uint32_t TotalFileBlocks = getBlockCount();
|
||||||
|
std::vector<support::ulittle32_t> ReservedBlocks;
|
||||||
|
ReservedBlocks.push_back(support::ulittle32_t(0));
|
||||||
|
ReservedBlocks.push_back(SB->BlockMapAddr);
|
||||||
|
ReservedBlocks.insert(ReservedBlocks.end(), DirectoryBlocks.begin(),
|
||||||
|
DirectoryBlocks.end());
|
||||||
|
|
||||||
|
uint32_t BlocksNeeded = 0;
|
||||||
|
for (auto Size : StreamSizes)
|
||||||
|
BlocksNeeded += bytesToBlocks(Size, getBlockSize());
|
||||||
|
|
||||||
|
support::ulittle32_t NextBlock(TotalFileBlocks - BlocksNeeded -
|
||||||
|
ReservedBlocks.size());
|
||||||
|
|
||||||
|
StaticMap.resize(StreamSizes.size());
|
||||||
|
for (uint32_t S = 0; S < StreamSizes.size(); ++S) {
|
||||||
|
uint32_t Size = StreamSizes[S];
|
||||||
|
uint32_t NumBlocks = bytesToBlocks(Size, getBlockSize());
|
||||||
|
auto &ThisStream = StaticMap[S];
|
||||||
|
for (uint32_t I = 0; I < NumBlocks;) {
|
||||||
|
NextBlock += 1;
|
||||||
|
if (std::find(ReservedBlocks.begin(), ReservedBlocks.end(), NextBlock) !=
|
||||||
|
ReservedBlocks.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
++I;
|
||||||
|
assert(NextBlock < getBlockCount());
|
||||||
|
ThisStream.push_back(NextBlock);
|
||||||
|
}
|
||||||
|
StreamMap.push_back(ThisStream);
|
||||||
|
}
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
Error PDBFile::commit() {
|
Error PDBFile::commit() {
|
||||||
StreamWriter Writer(*Buffer);
|
StreamWriter Writer(*Buffer);
|
||||||
|
|
||||||
|
@ -371,5 +429,35 @@ Error PDBFile::commit() {
|
||||||
return EC;
|
return EC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Info) {
|
||||||
|
if (auto EC = Info->commit())
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Dbi) {
|
||||||
|
if (auto EC = Dbi->commit())
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Symbols) {
|
||||||
|
if (auto EC = Symbols->commit())
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Publics) {
|
||||||
|
if (auto EC = Publics->commit())
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Tpi) {
|
||||||
|
if (auto EC = Tpi->commit())
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Ipi) {
|
||||||
|
if (auto EC = Ipi->commit())
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
|
||||||
return Buffer->commit();
|
return Buffer->commit();
|
||||||
}
|
}
|
|
@ -169,3 +169,5 @@ PublicsStream::getSymbols(bool *HadError) const {
|
||||||
|
|
||||||
return SS.getSymbols(HadError);
|
return SS.getSymbols(HadError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error PublicsStream::commit() { return Error::success(); }
|
||||||
|
|
|
@ -42,3 +42,5 @@ iterator_range<codeview::CVSymbolArray::Iterator>
|
||||||
SymbolStream::getSymbols(bool *HadError) const {
|
SymbolStream::getSymbols(bool *HadError) const {
|
||||||
return llvm::make_range(SymbolRecords.begin(HadError), SymbolRecords.end());
|
return llvm::make_range(SymbolRecords.begin(HadError), SymbolRecords.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error SymbolStream::commit() { return Error::success(); }
|
||||||
|
|
|
@ -269,3 +269,5 @@ iterator_range<CVTypeArray::Iterator>
|
||||||
TpiStream::types(bool *HadError) const {
|
TpiStream::types(bool *HadError) const {
|
||||||
return llvm::make_range(TypeRecords.begin(HadError), TypeRecords.end());
|
return llvm::make_range(TypeRecords.begin(HadError), TypeRecords.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error TpiStream::commit() { return Error::success(); }
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
; constructed PDB to YAML, and verifies that the YAML is the same as the
|
; constructed PDB to YAML, and verifies that the YAML is the same as the
|
||||||
; original YAML generated from the good PDB.
|
; original YAML generated from the good PDB.
|
||||||
;
|
;
|
||||||
; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory %p/Inputs/empty.pdb > %t.1
|
; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream %p/Inputs/empty.pdb > %t.1
|
||||||
; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1
|
; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1
|
||||||
; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory %t.2 > %t.3
|
; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream %t.2 > %t.3
|
||||||
; RUN: diff %t.1 %t.3
|
; RUN: diff %t.1 %t.3
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
; RUN: llvm-pdbdump pdb2yaml -stream-metadata %p/Inputs/empty.pdb | FileCheck -check-prefix=YAML %s
|
; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream %p/Inputs/empty.pdb \
|
||||||
|
; RUN: | FileCheck -check-prefix=YAML %s
|
||||||
|
|
||||||
; YAML: ---
|
; YAML: ---
|
||||||
; YAML-NEXT: MSF:
|
; YAML-NEXT: MSF:
|
||||||
; YAML-NEXT: SuperBlock:
|
; YAML-NEXT: SuperBlock:
|
||||||
; YAML-NEXT: BlockSize: 4096
|
; YAML-NEXT: BlockSize: 4096
|
||||||
; YAML-NEXT: Unknown0: 2
|
; YAML-NEXT: Unknown0: 2
|
||||||
|
@ -13,6 +14,63 @@
|
||||||
; YAML-NEXT: BlockMapOffset: 98304
|
; YAML-NEXT: BlockMapOffset: 98304
|
||||||
; YAML-NEXT: DirectoryBlocks:
|
; YAML-NEXT: DirectoryBlocks:
|
||||||
; YAML-NEXT: - 23
|
; YAML-NEXT: - 23
|
||||||
; YAML-NEXT: NumStreams: 17
|
; YAML-NEXT: NumStreams: 17
|
||||||
; YAML-NEXT: FileSize: 102400
|
; YAML-NEXT: FileSize: 102400
|
||||||
; YAML: ...
|
; YAML-NEXT: StreamSizes:
|
||||||
|
; YAML-NEXT: - 40
|
||||||
|
; YAML-NEXT: - 118
|
||||||
|
; YAML-NEXT: - 5392
|
||||||
|
; YAML-NEXT: - 739
|
||||||
|
; YAML-NEXT: - 784
|
||||||
|
; YAML-NEXT: - 0
|
||||||
|
; YAML-NEXT: - 556
|
||||||
|
; YAML-NEXT: - 604
|
||||||
|
; YAML-NEXT: - 104
|
||||||
|
; YAML-NEXT: - 0
|
||||||
|
; YAML-NEXT: - 160
|
||||||
|
; YAML-NEXT: - 32
|
||||||
|
; YAML-NEXT: - 308
|
||||||
|
; YAML-NEXT: - 239
|
||||||
|
; YAML-NEXT: - 520
|
||||||
|
; YAML-NEXT: - 308
|
||||||
|
; YAML-NEXT: - 68
|
||||||
|
; YAML-NEXT: StreamMap:
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 8
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 19
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 18
|
||||||
|
; YAML-NEXT: - 17
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 14
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 20
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 11
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 13
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 12
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 10
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 15
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 6
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 16
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 7
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 21
|
||||||
|
; YAML-NEXT: - Stream:
|
||||||
|
; YAML-NEXT: - 22
|
||||||
|
; YAML-NEXT: PdbStream:
|
||||||
|
; YAML-NEXT: Age: 1
|
||||||
|
; YAML-NEXT: Guid: '{0B355641-86A0-A249-896F-9988FAE52FF0}'
|
||||||
|
; YAML-NEXT: Signature: 1424295906
|
||||||
|
; YAML-NEXT: Version: 20000404
|
||||||
|
; YAML-NEXT: ...
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "PdbYaml.h"
|
#include "PdbYaml.h"
|
||||||
|
|
||||||
|
#include "llvm/DebugInfo/PDB/PDBExtras.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
@ -16,6 +17,45 @@ using namespace llvm::yaml;
|
||||||
using namespace llvm::pdb;
|
using namespace llvm::pdb;
|
||||||
using namespace llvm::pdb::yaml;
|
using namespace llvm::pdb::yaml;
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
namespace yaml {
|
||||||
|
template <> struct ScalarTraits<llvm::pdb::PDB_UniqueId> {
|
||||||
|
static void output(const llvm::pdb::PDB_UniqueId &S, void *,
|
||||||
|
llvm::raw_ostream &OS) {
|
||||||
|
OS << S;
|
||||||
|
}
|
||||||
|
|
||||||
|
static StringRef input(StringRef Scalar, void *Ctx,
|
||||||
|
llvm::pdb::PDB_UniqueId &S) {
|
||||||
|
if (Scalar.size() != 38)
|
||||||
|
return "GUID strings are 38 characters long";
|
||||||
|
if (Scalar[0] != '{' || Scalar[37] != '}')
|
||||||
|
return "GUID is not enclosed in {}";
|
||||||
|
if (Scalar[9] != '-' || Scalar[14] != '-' || Scalar[19] != '-' ||
|
||||||
|
Scalar[24] != '-')
|
||||||
|
return "GUID sections are not properly delineated with dashes";
|
||||||
|
|
||||||
|
char *OutBuffer = S.Guid;
|
||||||
|
for (auto Iter = Scalar.begin(); Iter != Scalar.end();) {
|
||||||
|
if (*Iter == '-' || *Iter == '{' || *Iter == '}') {
|
||||||
|
++Iter;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint8_t Value = (llvm::hexDigitValue(*Iter) << 4);
|
||||||
|
++Iter;
|
||||||
|
Value |= llvm::hexDigitValue(*Iter);
|
||||||
|
++Iter;
|
||||||
|
*OutBuffer++ = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mustQuote(StringRef Scalar) { return needsQuotes(Scalar); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MappingTraits<PDBFile::SuperBlock>::mapping(IO &IO,
|
void MappingTraits<PDBFile::SuperBlock>::mapping(IO &IO,
|
||||||
PDBFile::SuperBlock &SB) {
|
PDBFile::SuperBlock &SB) {
|
||||||
if (!IO.outputting()) {
|
if (!IO.outputting()) {
|
||||||
|
@ -47,4 +87,12 @@ void MappingTraits<PdbObject>::mapping(IO &IO, PdbObject &Obj) {
|
||||||
IO.mapOptional("MSF", Obj.Headers);
|
IO.mapOptional("MSF", Obj.Headers);
|
||||||
IO.mapOptional("StreamSizes", Obj.StreamSizes);
|
IO.mapOptional("StreamSizes", Obj.StreamSizes);
|
||||||
IO.mapOptional("StreamMap", Obj.StreamMap);
|
IO.mapOptional("StreamMap", Obj.StreamMap);
|
||||||
|
IO.mapOptional("PdbStream", Obj.PdbStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MappingTraits<PdbInfoStream>::mapping(IO &IO, PdbInfoStream &Obj) {
|
||||||
|
IO.mapRequired("Age", Obj.Age);
|
||||||
|
IO.mapRequired("Guid", Obj.Guid);
|
||||||
|
IO.mapRequired("Signature", Obj.Signature);
|
||||||
|
IO.mapRequired("Version", Obj.Version);
|
||||||
|
}
|
|
@ -13,7 +13,9 @@
|
||||||
#include "OutputStyle.h"
|
#include "OutputStyle.h"
|
||||||
|
|
||||||
#include "llvm/ADT/Optional.h"
|
#include "llvm/ADT/Optional.h"
|
||||||
|
#include "llvm/DebugInfo/PDB/PDBTypes.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
||||||
|
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
|
||||||
#include "llvm/Support/Endian.h"
|
#include "llvm/Support/Endian.h"
|
||||||
#include "llvm/Support/YAMLTraits.h"
|
#include "llvm/Support/YAMLTraits.h"
|
||||||
|
|
||||||
|
@ -36,10 +38,18 @@ struct StreamBlockList {
|
||||||
std::vector<support::ulittle32_t> Blocks;
|
std::vector<support::ulittle32_t> Blocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PdbInfoStream {
|
||||||
|
uint32_t Version;
|
||||||
|
uint32_t Signature;
|
||||||
|
uint32_t Age;
|
||||||
|
PDB_UniqueId Guid;
|
||||||
|
};
|
||||||
|
|
||||||
struct PdbObject {
|
struct PdbObject {
|
||||||
MsfHeaders Headers;
|
MsfHeaders Headers;
|
||||||
Optional<std::vector<support::ulittle32_t>> StreamSizes;
|
Optional<std::vector<support::ulittle32_t>> StreamSizes;
|
||||||
Optional<std::vector<StreamBlockList>> StreamMap;
|
Optional<std::vector<StreamBlockList>> StreamMap;
|
||||||
|
Optional<PdbInfoStream> PdbStream;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +73,10 @@ template <> struct MappingTraits<pdb::yaml::MsfHeaders> {
|
||||||
template <> struct MappingTraits<pdb::yaml::PdbObject> {
|
template <> struct MappingTraits<pdb::yaml::PdbObject> {
|
||||||
static void mapping(IO &IO, pdb::yaml::PdbObject &Obj);
|
static void mapping(IO &IO, pdb::yaml::PdbObject &Obj);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <> struct MappingTraits<pdb::yaml::PdbInfoStream> {
|
||||||
|
static void mapping(IO &IO, pdb::yaml::PdbInfoStream &Obj);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "PdbYaml.h"
|
#include "PdbYaml.h"
|
||||||
#include "llvm-pdbdump.h"
|
#include "llvm-pdbdump.h"
|
||||||
|
|
||||||
|
#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
|
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
|
||||||
|
|
||||||
|
@ -21,6 +22,9 @@ using namespace llvm::pdb;
|
||||||
YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) : File(File), Out(outs()) {}
|
YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) : File(File), Out(outs()) {}
|
||||||
|
|
||||||
Error YAMLOutputStyle::dump() {
|
Error YAMLOutputStyle::dump() {
|
||||||
|
if (opts::pdb2yaml::StreamDirectory || opts::pdb2yaml::PdbStream)
|
||||||
|
opts::pdb2yaml::StreamMetadata = true;
|
||||||
|
|
||||||
if (auto EC = dumpFileHeaders())
|
if (auto EC = dumpFileHeaders())
|
||||||
return EC;
|
return EC;
|
||||||
|
|
||||||
|
@ -30,6 +34,9 @@ Error YAMLOutputStyle::dump() {
|
||||||
if (auto EC = dumpStreamDirectory())
|
if (auto EC = dumpStreamDirectory())
|
||||||
return EC;
|
return EC;
|
||||||
|
|
||||||
|
if (auto EC = dumpPDBStream())
|
||||||
|
return EC;
|
||||||
|
|
||||||
flush();
|
flush();
|
||||||
return Error::success();
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
@ -76,6 +83,24 @@ Error YAMLOutputStyle::dumpStreamDirectory() {
|
||||||
return Error::success();
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error YAMLOutputStyle::dumpPDBStream() {
|
||||||
|
if (!opts::pdb2yaml::PdbStream)
|
||||||
|
return Error::success();
|
||||||
|
|
||||||
|
auto IS = File.getPDBInfoStream();
|
||||||
|
if (!IS)
|
||||||
|
return IS.takeError();
|
||||||
|
|
||||||
|
auto &InfoS = IS.get();
|
||||||
|
Obj.PdbStream.emplace();
|
||||||
|
Obj.PdbStream->Age = InfoS.getAge();
|
||||||
|
Obj.PdbStream->Guid = InfoS.getGuid();
|
||||||
|
Obj.PdbStream->Signature = InfoS.getSignature();
|
||||||
|
Obj.PdbStream->Version = InfoS.getVersion();
|
||||||
|
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
void YAMLOutputStyle::flush() {
|
void YAMLOutputStyle::flush() {
|
||||||
Out << Obj;
|
Out << Obj;
|
||||||
outs().flush();
|
outs().flush();
|
||||||
|
|
|
@ -29,6 +29,7 @@ private:
|
||||||
Error dumpFileHeaders();
|
Error dumpFileHeaders();
|
||||||
Error dumpStreamMetadata();
|
Error dumpStreamMetadata();
|
||||||
Error dumpStreamDirectory();
|
Error dumpStreamDirectory();
|
||||||
|
Error dumpPDBStream();
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
|
#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
|
||||||
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
|
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
|
||||||
#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h"
|
#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h"
|
||||||
|
#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
|
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
|
||||||
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
|
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
|
||||||
|
@ -268,6 +269,10 @@ cl::opt<bool> StreamDirectory(
|
||||||
"stream-directory",
|
"stream-directory",
|
||||||
cl::desc("Dump each stream's block map (implies -stream-metadata)"),
|
cl::desc("Dump each stream's block map (implies -stream-metadata)"),
|
||||||
cl::sub(PdbToYamlSubcommand));
|
cl::sub(PdbToYamlSubcommand));
|
||||||
|
cl::opt<bool> PdbStream(
|
||||||
|
"pdb-stream",
|
||||||
|
cl::desc("Dump the PDB Stream (Stream 1) (implies -stream-metadata)"),
|
||||||
|
cl::sub(PdbToYamlSubcommand));
|
||||||
|
|
||||||
cl::list<std::string> InputFilename(cl::Positional,
|
cl::list<std::string> InputFilename(cl::Positional,
|
||||||
cl::desc("<input PDB file>"), cl::Required,
|
cl::desc("<input PDB file>"), cl::Required,
|
||||||
|
@ -302,31 +307,53 @@ static void yamlToPdb(StringRef Path) {
|
||||||
llvm::make_unique<FileBufferByteStream>(std::move(*OutFileOrError));
|
llvm::make_unique<FileBufferByteStream>(std::move(*OutFileOrError));
|
||||||
PDBFile Pdb(std::move(FileByteStream));
|
PDBFile Pdb(std::move(FileByteStream));
|
||||||
ExitOnErr(Pdb.setSuperBlock(&YamlObj.Headers.SuperBlock));
|
ExitOnErr(Pdb.setSuperBlock(&YamlObj.Headers.SuperBlock));
|
||||||
|
if (YamlObj.StreamSizes.hasValue()) {
|
||||||
|
Pdb.setStreamSizes(YamlObj.StreamSizes.getValue());
|
||||||
|
}
|
||||||
|
Pdb.setDirectoryBlocks(YamlObj.Headers.DirectoryBlocks);
|
||||||
|
|
||||||
if (YamlObj.StreamMap.hasValue()) {
|
if (YamlObj.StreamMap.hasValue()) {
|
||||||
std::vector<ArrayRef<support::ulittle32_t>> StreamMap;
|
std::vector<ArrayRef<support::ulittle32_t>> StreamMap;
|
||||||
for (auto &E : YamlObj.StreamMap.getValue()) {
|
for (auto &E : YamlObj.StreamMap.getValue()) {
|
||||||
StreamMap.push_back(E.Blocks);
|
StreamMap.push_back(E.Blocks);
|
||||||
}
|
}
|
||||||
Pdb.setStreamMap(YamlObj.Headers.DirectoryBlocks, StreamMap);
|
Pdb.setStreamMap(StreamMap);
|
||||||
|
} else {
|
||||||
|
ExitOnErr(Pdb.generateSimpleStreamMap());
|
||||||
}
|
}
|
||||||
if (YamlObj.StreamSizes.hasValue()) {
|
|
||||||
Pdb.setStreamSizes(YamlObj.StreamSizes.getValue());
|
if (YamlObj.PdbStream.hasValue()) {
|
||||||
|
auto IS = Pdb.emplacePDBInfoStream();
|
||||||
|
ExitOnErr(IS.takeError());
|
||||||
|
auto &InfoS = IS.get();
|
||||||
|
InfoS.setAge(YamlObj.PdbStream->Age);
|
||||||
|
InfoS.setGuid(YamlObj.PdbStream->Guid);
|
||||||
|
InfoS.setSignature(YamlObj.PdbStream->Signature);
|
||||||
|
InfoS.setVersion(static_cast<PdbRaw_ImplVer>(YamlObj.PdbStream->Version));
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitOnErr(Pdb.commit());
|
ExitOnErr(Pdb.commit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pdb2Yaml(StringRef Path) {
|
||||||
|
std::unique_ptr<IPDBSession> Session;
|
||||||
|
ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session));
|
||||||
|
|
||||||
|
RawSession *RS = static_cast<RawSession *>(Session.get());
|
||||||
|
PDBFile &File = RS->getPDBFile();
|
||||||
|
auto O = llvm::make_unique<YAMLOutputStyle>(File);
|
||||||
|
O = llvm::make_unique<YAMLOutputStyle>(File);
|
||||||
|
|
||||||
|
ExitOnErr(O->dump());
|
||||||
|
}
|
||||||
|
|
||||||
static void dumpRaw(StringRef Path) {
|
static void dumpRaw(StringRef Path) {
|
||||||
std::unique_ptr<IPDBSession> Session;
|
std::unique_ptr<IPDBSession> Session;
|
||||||
ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session));
|
ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session));
|
||||||
|
|
||||||
RawSession *RS = static_cast<RawSession *>(Session.get());
|
RawSession *RS = static_cast<RawSession *>(Session.get());
|
||||||
PDBFile &File = RS->getPDBFile();
|
PDBFile &File = RS->getPDBFile();
|
||||||
std::unique_ptr<OutputStyle> O;
|
auto O = llvm::make_unique<LLVMOutputStyle>(File);
|
||||||
if (opts::PdbToYamlSubcommand)
|
|
||||||
O = llvm::make_unique<YAMLOutputStyle>(File);
|
|
||||||
else
|
|
||||||
O = llvm::make_unique<LLVMOutputStyle>(File);
|
|
||||||
|
|
||||||
ExitOnErr(O->dump());
|
ExitOnErr(O->dump());
|
||||||
}
|
}
|
||||||
|
@ -486,7 +513,7 @@ int main(int argc_, const char *argv_[]) {
|
||||||
llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
|
llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
|
||||||
|
|
||||||
if (opts::PdbToYamlSubcommand) {
|
if (opts::PdbToYamlSubcommand) {
|
||||||
dumpRaw(opts::pdb2yaml::InputFilename.front());
|
pdb2Yaml(opts::pdb2yaml::InputFilename.front());
|
||||||
} else if (opts::YamlToPdbSubcommand) {
|
} else if (opts::YamlToPdbSubcommand) {
|
||||||
yamlToPdb(opts::yaml2pdb::InputFilename.front());
|
yamlToPdb(opts::yaml2pdb::InputFilename.front());
|
||||||
} else if (opts::PrettySubcommand) {
|
} else if (opts::PrettySubcommand) {
|
||||||
|
|
|
@ -59,6 +59,7 @@ extern llvm::cl::opt<bool> DumpFpo;
|
||||||
namespace pdb2yaml {
|
namespace pdb2yaml {
|
||||||
extern llvm::cl::opt<bool> StreamMetadata;
|
extern llvm::cl::opt<bool> StreamMetadata;
|
||||||
extern llvm::cl::opt<bool> StreamDirectory;
|
extern llvm::cl::opt<bool> StreamDirectory;
|
||||||
|
extern llvm::cl::opt<bool> PdbStream;
|
||||||
extern llvm::cl::list<std::string> InputFilename;
|
extern llvm::cl::list<std::string> InputFilename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue