Index: llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h =================================================================== --- llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h +++ llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h @@ -72,6 +72,12 @@ assert(Map->Present.test(Index)); return Map->Buckets[Index]; } + + // Implement postfix op++ in terms of prefix op++ by using the superclass + // implementation. + using iterator_facade_base, + std::forward_iterator_tag, + const std::pair>::operator++; HashTableIterator &operator++() { while (Index < Map->Buckets.size()) { ++Index; @@ -94,9 +100,6 @@ template class HashTable { - using const_iterator = HashTableIterator; - friend const_iterator; - struct Header { support::ulittle32_t Size; support::ulittle32_t Capacity; @@ -105,6 +108,9 @@ using BucketList = std::vector>; public: + using const_iterator = HashTableIterator; + friend const_iterator; + HashTable() { Buckets.resize(8); } explicit HashTable(uint32_t Capacity) { Buckets.resize(Capacity); Index: llvm/include/llvm/DebugInfo/PDB/Native/InjectedSourceStream.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/PDB/Native/InjectedSourceStream.h @@ -0,0 +1,44 @@ +//===- InjectedSourceStream.h - PDB Headerblock Stream Access ---*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_PDB_RAW_PDBINJECTEDSOURCESTREAM_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBINJECTEDSOURCESTREAM_H + +#include "llvm/DebugInfo/PDB/Native/HashTable.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace msf { +class MappedBlockStream; +} +namespace pdb { +class PDBFile; +class PDBStringTable; + +class InjectedSourceStream { +public: + InjectedSourceStream(std::unique_ptr Stream); + Error reload(const PDBStringTable &Strings); + + using const_iterator = HashTable::const_iterator; + const_iterator begin() const { return InjectedSourceTable.begin(); } + const_iterator end() const { return InjectedSourceTable.end(); } + + uint32_t size() const { return InjectedSourceTable.size(); } + +private: + std::unique_ptr Stream; + + const SrcHeaderBlockHeader* Header; + HashTable InjectedSourceTable; +}; +} +} + +#endif Index: llvm/include/llvm/DebugInfo/PDB/Native/NativeEnumInjectedSources.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/PDB/Native/NativeEnumInjectedSources.h @@ -0,0 +1,43 @@ +//==- NativeEnumInjectedSources.cpp - Native Injected Source Enumerator --*-==// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_PDB_NATIVE_NATIVEENUMINJECTEDSOURCES_H +#define LLVM_DEBUGINFO_PDB_NATIVE_NATIVEENUMINJECTEDSOURCES_H + +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h" +#include "llvm/DebugInfo/PDB/Native/InjectedSourceStream.h" + +namespace llvm { +namespace pdb { + +class InjectedSourceStream; +class PDBStringTable; + +class NativeEnumInjectedSources : public IPDBEnumChildren { +public: + NativeEnumInjectedSources(PDBFile &File, const InjectedSourceStream &IJS, + const PDBStringTable &Strings); + + uint32_t getChildCount() const override; + std::unique_ptr + getChildAtIndex(uint32_t Index) const override; + std::unique_ptr getNext() override; + void reset() override; + +private: + PDBFile &File; + const InjectedSourceStream &Stream; + const PDBStringTable &Strings; + InjectedSourceStream::const_iterator Cur; +}; + +} // namespace pdb +} // namespace llvm + +#endif Index: llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h =================================================================== --- llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h +++ llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h @@ -32,6 +32,7 @@ class DbiStream; class GlobalsStream; class InfoStream; +class InjectedSourceStream; class PDBStringTable; class PDBFileBuilder; class PublicsStream; @@ -87,6 +88,8 @@ createIndexedStream(uint16_t SN) const; Expected> safelyCreateIndexedStream(uint32_t StreamIndex) const; + Expected> + safelyCreateNamedStream(StringRef Name); msf::MSFStreamLayout getStreamLayout(uint32_t StreamIdx) const; msf::MSFStreamLayout getFpmStreamLayout() const; @@ -102,6 +105,7 @@ Expected getPDBPublicsStream(); Expected getPDBSymbolStream(); Expected getStringTable(); + Expected getInjectedSourceStream(); BumpPtrAllocator &getAllocator() { return Allocator; } @@ -113,6 +117,7 @@ bool hasPDBSymbolStream(); bool hasPDBTpiStream() const; bool hasPDBStringTable(); + bool hasPDBInjectedSourceStream(); uint32_t getPointerSize(); @@ -133,6 +138,7 @@ std::unique_ptr Symbols; std::unique_ptr DirectoryStream; std::unique_ptr StringTableStream; + std::unique_ptr InjectedSources; std::unique_ptr Strings; }; } Index: llvm/lib/DebugInfo/PDB/CMakeLists.txt =================================================================== --- llvm/lib/DebugInfo/PDB/CMakeLists.txt +++ llvm/lib/DebugInfo/PDB/CMakeLists.txt @@ -47,9 +47,11 @@ Native/HashTable.cpp Native/InfoStream.cpp Native/InfoStreamBuilder.cpp + Native/InjectedSourceStream.cpp Native/ModuleDebugStream.cpp Native/NativeCompilandSymbol.cpp Native/NativeEnumGlobals.cpp + Native/NativeEnumInjectedSources.cpp Native/NativeEnumModules.cpp Native/NativeEnumTypes.cpp Native/NativeExeSymbol.cpp Index: llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp @@ -0,0 +1,65 @@ +//===- InjectedSourceStream.cpp - PDB Headerblock Stream Access -----------===// +// +// 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 "llvm/DebugInfo/PDB/Native/InjectedSourceStream.h" + +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::support; +using namespace llvm::pdb; + +InjectedSourceStream::InjectedSourceStream( + std::unique_ptr Stream) + : Stream(std::move(Stream)) {} + +Error InjectedSourceStream::reload(const PDBStringTable &Strings) { + BinaryStreamReader Reader(*Stream); + + if (auto EC = Reader.readObject(Header)) + return EC; + + if (Header->Version != + static_cast(PdbRaw_SrcHeaderBlockVer::SrcVerOne)) + return make_error(raw_error_code::corrupt_file, + "Invalid headerblock header version"); + + if (auto EC = InjectedSourceTable.load(Reader)) + return EC; + + for (const auto& Entry : *this) { + if (Entry.second.Size != sizeof(SrcHeaderBlockEntry)) + return make_error(raw_error_code::corrupt_file, + "Invalid headerbock entry size"); + if (Entry.second.Version != + static_cast(PdbRaw_SrcHeaderBlockVer::SrcVerOne)) + return make_error(raw_error_code::corrupt_file, + "Invalid headerbock entry version"); + + // Check that all name references are valid. + auto Name = Strings.getStringForID(Entry.second.FileNI); + if (!Name) + return Name.takeError(); + auto ObjName = Strings.getStringForID(Entry.second.ObjNI); + if (!ObjName) + return ObjName.takeError(); + auto VName = Strings.getStringForID(Entry.second.VFileNI); + if (!VName) + return VName.takeError(); + } + + assert(Reader.bytesRemaining() == 0); + return Error::success(); +} Index: llvm/lib/DebugInfo/PDB/Native/NativeEnumInjectedSources.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/PDB/Native/NativeEnumInjectedSources.cpp @@ -0,0 +1,119 @@ +//==- NativeEnumInjectedSources.cpp - Native Injected Source Enumerator --*-==// +// +// 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 "llvm/DebugInfo/PDB/Native/NativeEnumInjectedSources.h" + +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" + +namespace llvm { +namespace pdb { + +namespace { + +Expected readStreamData(BinaryStream &Stream) { + uint32_t Offset = 0, DataLength = Stream.getLength(); + std::string Result; + Result.reserve(DataLength); + while (Offset < DataLength) { + ArrayRef Data; + if (auto E = Stream.readLongestContiguousChunk(Offset, Data)) + return std::move(E); + Offset += Data.size(); + Result += toStringRef(Data); + } + return Result; +} + +class NativeInjectedSource final : public IPDBInjectedSource { + const SrcHeaderBlockEntry &Entry; + const PDBStringTable &Strings; + PDBFile &File; + +public: + NativeInjectedSource(const SrcHeaderBlockEntry &Entry, + PDBFile &File, const PDBStringTable &Strings) + : Entry(Entry), Strings(Strings), File(File) {} + + uint32_t getCrc32() const override { return Entry.CRC; } + uint64_t getCodeByteSize() const override { return Entry.FileSize; } + + std::string getFileName() const override { + auto Name = Strings.getStringForID(Entry.FileNI); + assert(Name && "InjectedSourceStream should have rejected this"); + return *Name; + } + + std::string getObjectFileName() const override { + auto ObjName = Strings.getStringForID(Entry.ObjNI); + assert(ObjName && "InjectedSourceStream should have rejected this"); + return *ObjName; + } + + std::string getVirtualFileName() const override { + auto VName = Strings.getStringForID(Entry.VFileNI); + assert(VName && "InjectedSourceStream should have rejected this"); + return *VName; + } + + PDB_SourceCompression getCompression() const override { + return static_cast(Entry.Compression); + } + + std::string getCode() const override { + // Get name of stream storing the data. + auto VName = Strings.getStringForID(Entry.VFileNI); + assert(VName && "InjectedSourceStream should have rejected this"); + std::string StreamName = ("/src/files/" + *VName).str(); + + // Find stream with that name and read its data. + auto ExpectedFileStream = File.safelyCreateNamedStream(StreamName); + if (!ExpectedFileStream) { + consumeError(ExpectedFileStream.takeError()); + return "(failed to open data stream)"; + } + + auto Data = readStreamData(**ExpectedFileStream); + if (!Data) { + consumeError(Data.takeError()); + return "(failed to read data)"; + } + return *Data; + } +}; + +} // namespace + +NativeEnumInjectedSources::NativeEnumInjectedSources( + PDBFile &File, const InjectedSourceStream &IJS, + const PDBStringTable &Strings) + : File(File), Stream(IJS), Strings(Strings), Cur(Stream.begin()) {} + +uint32_t NativeEnumInjectedSources::getChildCount() const { + return static_cast(Stream.size()); +} + +std::unique_ptr +NativeEnumInjectedSources::getChildAtIndex(uint32_t N) const { + if (N >= getChildCount()) + return nullptr; + return make_unique(std::next(Stream.begin(), N)->second, + File, Strings); +} + +std::unique_ptr NativeEnumInjectedSources::getNext() { + if (Cur == Stream.end()) + return nullptr; + return make_unique((Cur++)->second, File, Strings); +} + +void NativeEnumInjectedSources::reset() { Cur = Stream.begin(); } + +} +} Index: llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp +++ llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" #include "llvm/DebugInfo/PDB/IPDBSourceFile.h" #include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" +#include "llvm/DebugInfo/PDB/Native/NativeEnumInjectedSources.h" #include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" #include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h" #include "llvm/DebugInfo/PDB/Native/NativeTypeBuiltin.h" @@ -191,7 +192,17 @@ std::unique_ptr NativeSession::getInjectedSources() const { - return nullptr; + auto ISS = Pdb->getInjectedSourceStream(); + if (!ISS) { + consumeError(ISS.takeError()); + return nullptr; + } + auto Strings = Pdb->getStringTable(); + if (!Strings) { + consumeError(Strings.takeError()); + return nullptr; + } + return make_unique(*Pdb, *ISS, *Strings); } std::unique_ptr Index: llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp +++ llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -14,6 +14,7 @@ #include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InjectedSourceStream.h" #include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" @@ -365,16 +366,7 @@ Expected PDBFile::getStringTable() { if (!Strings) { - auto IS = getPDBInfoStream(); - if (!IS) - return IS.takeError(); - - Expected ExpectedNSI = IS->getNamedStreamIndex("/names"); - if (!ExpectedNSI) - return ExpectedNSI.takeError(); - uint32_t NameStreamIndex = *ExpectedNSI; - - auto NS = safelyCreateIndexedStream(NameStreamIndex); + auto NS = safelyCreateNamedStream("/names"); if (!NS) return NS.takeError(); @@ -389,6 +381,24 @@ return *Strings; } +Expected PDBFile::getInjectedSourceStream() { + if (!InjectedSources) { + auto IJS = safelyCreateNamedStream("/src/headerblock"); + if (!IJS) + return IJS.takeError(); + + auto Strings = getStringTable(); + if (!Strings) + return Strings.takeError(); + + auto IJ = llvm::make_unique(std::move(*IJS)); + if (auto EC = IJ->reload(*Strings)) + return std::move(EC); + InjectedSources = std::move(IJ); + } + return *InjectedSources; +} + uint32_t PDBFile::getPointerSize() { auto DbiS = getPDBDbiStream(); if (!DbiS) @@ -457,6 +467,19 @@ return true; } +bool PDBFile::hasPDBInjectedSourceStream() { + auto IS = getPDBInfoStream(); + if (!IS) + return false; + Expected ExpectedNSI = IS->getNamedStreamIndex("/src/headerblock"); + if (!ExpectedNSI) { + consumeError(ExpectedNSI.takeError()); + return false; + } + assert(*ExpectedNSI < getNumStreams()); + return true; +} + /// Wrapper around MappedBlockStream::createIndexedStream() that checks if a /// stream with that index actually exists. If it does not, the return value /// will have an MSFError with code msf_error_code::no_stream. Else, the return @@ -468,3 +491,17 @@ return make_error(raw_error_code::no_stream); return createIndexedStream(StreamIndex); } + +Expected> +PDBFile::safelyCreateNamedStream(StringRef Name) { + auto IS = getPDBInfoStream(); + if (!IS) + return IS.takeError(); + + Expected ExpectedNSI = IS->getNamedStreamIndex(Name); + if (!ExpectedNSI) + return ExpectedNSI.takeError(); + uint32_t NameStreamIndex = *ExpectedNSI; + + return safelyCreateIndexedStream(NameStreamIndex); +} Index: llvm/test/tools/llvm-pdbutil/injected-sources-native.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-pdbutil/injected-sources-native.test @@ -0,0 +1,30 @@ +; This is identical to injected-sources.test, except that it uses the -native +; mode of pretty (and hence doesn't require diasdk and runs on all platforms). + +; RUN: llvm-pdbutil pretty -native -injected-sources -injected-source-content \ +; RUN: %p/Inputs/InjectedSource.pdb | FileCheck %s +; RUN: llvm-pdbutil pretty -native -injected-sources -injected-source-content \ +; RUN: %p/Inputs/ClassLayoutTest.pdb | FileCheck --check-prefix=NEGATIVE %s + +; CHECK: ---INJECTED SOURCES--- +; CHECK: c.natvis (140 bytes): obj=, vname=c.natvis, crc=334478030, compression=None +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK: a.natvis (140 bytes): obj=, vname=a.natvis, crc=334478030, compression=None +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK: b.natvis (294 bytes): obj=, vname=b.natvis, crc=2059731902, compression=None +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-NEXT: Third test +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-NEXT: Fourth test +; CHECK-NEXT: +; CHECK-NEXT: + +; NEGATIVE: ---INJECTED SOURCES--- +; NEGATIVE-NEXT: There are no injected sources. Index: llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp =================================================================== --- llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -934,7 +934,7 @@ static void dumpInjectedSources(LinePrinter &Printer, IPDBSession &Session) { auto Sources = Session.getInjectedSources(); - if (0 == Sources->getChildCount()) { + if (!Sources || !Sources->getChildCount()) { Printer.printLine("There are no injected sources."); return; } @@ -1279,12 +1279,7 @@ 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); + dumpInjectedSources(Printer, *Session); } Printer.NewLine(); Index: llvm/utils/gn/secondary/llvm/lib/DebugInfo/PDB/BUILD.gn =================================================================== --- llvm/utils/gn/secondary/llvm/lib/DebugInfo/PDB/BUILD.gn +++ llvm/utils/gn/secondary/llvm/lib/DebugInfo/PDB/BUILD.gn @@ -24,10 +24,12 @@ "Native/HashTable.cpp", "Native/InfoStream.cpp", "Native/InfoStreamBuilder.cpp", + "Native/InjectedSourceStream.cpp", "Native/ModuleDebugStream.cpp", "Native/NamedStreamMap.cpp", "Native/NativeCompilandSymbol.cpp", "Native/NativeEnumGlobals.cpp", + "Native/NativeEnumInjectedSources.cpp", "Native/NativeEnumModules.cpp", "Native/NativeEnumTypes.cpp", "Native/NativeExeSymbol.cpp",