Index: lld/COFF/PDB.cpp =================================================================== --- lld/COFF/PDB.cpp +++ lld/COFF/PDB.cpp @@ -29,12 +29,14 @@ #include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h" #include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include @@ -94,9 +96,15 @@ msf::ByteStream Stream(Data); codeview::CVTypeArray Types; msf::StreamReader Reader(Stream); + // Follow type servers. If the same type server is encountered more than + // once for this instance of `PDBTypeServerHandler` (for example if many + // object files reference the same TypeServer), the types from the + // TypeServer will only be visited once. + pdb::PDBTypeServerHandler Handler; + Handler.addSearchPath(llvm::sys::path::parent_path(File->getName())); if (auto EC = Reader.readArray(Types, Reader.getLength())) fatal(EC, "Reader::readArray failed"); - if (auto Err = codeview::mergeTypeStreams(Builder, Types)) + if (auto Err = codeview::mergeTypeStreams(Builder, &Handler, Types)) fatal(Err, "codeview::mergeTypeStreams failed"); } @@ -116,6 +124,8 @@ TypeDatabase TDB; TypeDumpVisitor TDV(TDB, &W, false); + // Use a default implementation that does not follow type servers and instead + // just dumps the contents of the TypeServer2 record. CVTypeDumper TypeDumper(TDB); if (auto EC = TypeDumper.dump(Data, TDV)) fatal(EC, "CVTypeDumper::dump failed"); Index: llvm/include/llvm/DebugInfo/CodeView/CVTypeDumper.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/CVTypeDumper.h +++ llvm/include/llvm/DebugInfo/CodeView/CVTypeDumper.h @@ -22,10 +22,14 @@ namespace codeview { +class TypeServerHandler; + /// Dumper for CodeView type streams found in COFF object files and PDB files. class CVTypeDumper { public: - explicit CVTypeDumper(TypeDatabase &TypeDB) : TypeDB(TypeDB) {} + explicit CVTypeDumper(TypeDatabase &TypeDB, + TypeServerHandler *Handler = nullptr) + : TypeDB(TypeDB), Handler(Handler) {} /// Dumps one type record. Returns false if there was a type parsing error, /// and true otherwise. This should be called in order, since the dumper @@ -48,6 +52,7 @@ private: TypeDatabase &TypeDB; + TypeServerHandler *Handler; }; } // end namespace codeview Index: llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h +++ llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h @@ -10,9 +10,10 @@ #ifndef LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H #define LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H -#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/TinyPtrVector.h" #include "llvm/DebugInfo/CodeView/CVRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeServerHandler.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/Support/Error.h" @@ -23,11 +24,14 @@ public: explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks); + void addTypeServerHandler(TypeServerHandler &Handler); + Error visitTypeRecord(CVType &Record); Error visitMemberRecord(CVMemberRecord &Record); /// Visits the type records in Data. Sets the error flag on parse failures. Error visitTypeStream(const CVTypeArray &Types); + Error visitTypeStream(CVTypeRange Types); Error visitFieldListMemberStream(ArrayRef FieldList); Error visitFieldListMemberStream(msf::StreamReader Reader); @@ -35,6 +39,8 @@ private: /// The interface to the class that gets notified of each visitation. TypeVisitorCallbacks &Callbacks; + + TinyPtrVector Handlers; }; } // end namespace codeview Index: llvm/include/llvm/DebugInfo/CodeView/CodeViewError.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/CodeViewError.h +++ llvm/include/llvm/DebugInfo/CodeView/CodeViewError.h @@ -21,6 +21,7 @@ insufficient_buffer, operation_unsupported, corrupt_record, + no_records, unknown_member_record, }; Index: llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h +++ llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h @@ -43,6 +43,7 @@ ArrayRef Data; }; typedef msf::VarStreamArray CVTypeArray; +typedef iterator_range CVTypeRange; /// Equvalent to CV_fldattr_t in cvinfo.h. struct MemberAttributes { Index: llvm/include/llvm/DebugInfo/CodeView/TypeServerHandler.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/CodeView/TypeServerHandler.h @@ -0,0 +1,34 @@ +//===- TypeServerHandler.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_CODEVIEW_TYPESERVERHANDLER_H +#define LLVM_DEBUGINFO_CODEVIEW_TYPESERVERHANDLER_H + +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace codeview { +class TypeVisitorCallbacks; + +class TypeServerHandler { +public: + /// Handle a TypeServer record. If the implementation returns true + /// the record will not be processed by the top-level visitor. If + /// it returns false, it will be processed. If it returns an Error, + /// then the top-level visitor will fail. + virtual Expected handle(TypeServer2Record &TS, + TypeVisitorCallbacks &Callbacks) { + return false; + } +}; +} +} + +#endif Index: llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h +++ llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h @@ -18,8 +18,11 @@ namespace llvm { namespace codeview { +class TypeServerHandler; + /// Merges one type stream into another. Returns true on success. -Error mergeTypeStreams(TypeTableBuilder &DestStream, const CVTypeArray &Types); +Error mergeTypeStreams(TypeTableBuilder &DestStream, TypeServerHandler *Handler, + const CVTypeArray &Types); } // end namespace codeview } // end namespace llvm 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 @@ -42,10 +42,13 @@ friend PDBFileBuilder; public: - PDBFile(std::unique_ptr PdbFileBuffer, + PDBFile(StringRef Path, std::unique_ptr PdbFileBuffer, BumpPtrAllocator &Allocator); ~PDBFile() override; + StringRef getFileDirectory() const; + StringRef getFilePath() const; + uint32_t getFreeBlockMapBlock() const; uint32_t getUnknown1() const; @@ -110,6 +113,7 @@ const msf::ReadableStream &MsfData, uint32_t StreamIndex) const; + std::string FilePath; BumpPtrAllocator &Allocator; std::unique_ptr Buffer; Index: llvm/include/llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h @@ -0,0 +1,48 @@ +//===- PDBTypeServerHandler.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_PDBTYPESERVERHANDLER_H +#define LLVM_DEBUGINFO_PDB_PDBTYPESERVERHANDLER_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeServerHandler.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include +#include + +namespace llvm { +namespace pdb { +class NativeSession; + +class PDBTypeServerHandler : public codeview::TypeServerHandler { +public: + PDBTypeServerHandler(bool RevisitAlways = false); + + void addSearchPath(StringRef Path); + Expected handle(codeview::TypeServer2Record &TS, + codeview::TypeVisitorCallbacks &Callbacks) override; + +private: + Expected handleInternal(PDBFile &File, + codeview::TypeVisitorCallbacks &Callbacks); + + bool RevisitAlways; + std::unique_ptr Session; + SmallVector, 4> SearchPaths; +}; +} +} + +#endif Index: llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h =================================================================== --- llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h +++ llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h @@ -50,7 +50,7 @@ msf::FixedStreamArray getTypeIndexOffsets() const; HashTable &getHashAdjusters(); - iterator_range types(bool *HadError) const; + codeview::CVTypeRange types(bool *HadError) const; Error commit(); Index: llvm/lib/DebugInfo/CodeView/CVTypeDumper.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/CVTypeDumper.cpp +++ llvm/lib/DebugInfo/CodeView/CVTypeDumper.cpp @@ -28,6 +28,8 @@ Pipeline.addCallbackToPipeline(Dumper); CVTypeVisitor Visitor(Pipeline); + if (Handler) + Visitor.addTypeServerHandler(*Handler); CVType RecordCopy = Record; if (auto EC = Visitor.visitTypeRecord(RecordCopy)) @@ -45,6 +47,8 @@ Pipeline.addCallbackToPipeline(Dumper); CVTypeVisitor Visitor(Pipeline); + if (Handler) + Visitor.addTypeServerHandler(*Handler); if (auto EC = Visitor.visitTypeStream(Types)) return EC; Index: llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp +++ llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp @@ -10,9 +10,14 @@ #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/CodeView/TypeDatabase.h" +#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" +#include "llvm/DebugInfo/CodeView/TypeServerHandler.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/MSF/ByteStream.h" +#include "llvm/DebugInfo/MSF/StreamReader.h" using namespace llvm; using namespace llvm::codeview; @@ -21,7 +26,8 @@ : Callbacks(Callbacks) {} template -static Error visitKnownRecord(CVType &Record, TypeVisitorCallbacks &Callbacks) { +static Error visitKnownRecord(CVTypeVisitor &Visitor, CVType &Record, + TypeVisitorCallbacks &Callbacks) { TypeRecordKind RK = static_cast(Record.Type); T KnownRecord(RK); if (auto EC = Callbacks.visitKnownRecord(Record, KnownRecord)) @@ -39,7 +45,58 @@ return Error::success(); } +static Expected deserializeTypeServerRecord(CVType &Record) { + class StealTypeServerVisitor : public TypeVisitorCallbacks { + public: + explicit StealTypeServerVisitor(TypeServer2Record &TR) : TR(TR) {} + + Error visitKnownRecord(CVType &CVR, TypeServer2Record &Record) override { + TR = Record; + return Error::success(); + } + + private: + TypeServer2Record &TR; + }; + + TypeServer2Record R(TypeRecordKind::TypeServer2); + TypeDeserializer Deserializer; + StealTypeServerVisitor Thief(R); + TypeVisitorCallbackPipeline Pipeline; + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Thief); + CVTypeVisitor Visitor(Pipeline); + if (auto EC = Visitor.visitTypeRecord(Record)) + return std::move(EC); + + return R; +} + +void CVTypeVisitor::addTypeServerHandler(TypeServerHandler &Handler) { + Handlers.push_back(&Handler); +} + Error CVTypeVisitor::visitTypeRecord(CVType &Record) { + if (Record.Type == TypeLeafKind::LF_TYPESERVER2 && !Handlers.empty()) { + auto TS = deserializeTypeServerRecord(Record); + if (!TS) + return TS.takeError(); + + for (auto Handler : Handlers) { + auto ExpectedResult = Handler->handle(*TS, Callbacks); + // If there was an error, return the error. + if (!ExpectedResult) + return ExpectedResult.takeError(); + + // If the handler processed the record, return success. + if (*ExpectedResult) + return Error::success(); + + // Otherwise keep searching for a handler, eventually falling out and + // using the default record handler. + } + } + if (auto EC = Callbacks.visitTypeBegin(Record)) return EC; @@ -50,7 +107,7 @@ break; #define TYPE_RECORD(EnumName, EnumVal, Name) \ case EnumName: { \ - if (auto EC = visitKnownRecord(Record, Callbacks)) \ + if (auto EC = visitKnownRecord(*this, Record, Callbacks)) \ return EC; \ break; \ } @@ -109,6 +166,14 @@ return Error::success(); } +Error CVTypeVisitor::visitTypeStream(CVTypeRange Types) { + for (auto I : Types) { + if (auto EC = visitTypeRecord(I)) + return EC; + } + return Error::success(); +} + Error CVTypeVisitor::visitFieldListMemberStream(msf::StreamReader Reader) { FieldListDeserializer Deserializer(Reader); TypeVisitorCallbackPipeline Pipeline; Index: llvm/lib/DebugInfo/CodeView/CodeViewError.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/CodeViewError.cpp +++ llvm/lib/DebugInfo/CodeView/CodeViewError.cpp @@ -31,6 +31,8 @@ "bytes."; case cv_error_code::corrupt_record: return "The CodeView record is corrupted."; + case cv_error_code::no_records: + return "There are no records"; case cv_error_code::operation_unsupported: return "The requested operation is not supported."; case cv_error_code::unknown_member_record: Index: llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -54,8 +54,9 @@ /// existing destination type index. class TypeStreamMerger : public TypeVisitorCallbacks { public: - TypeStreamMerger(TypeTableBuilder &DestStream) - : DestStream(DestStream), FieldListBuilder(DestStream) {} + TypeStreamMerger(TypeTableBuilder &DestStream, TypeServerHandler *Handler) + : DestStream(DestStream), FieldListBuilder(DestStream), Handler(Handler) { + } /// TypeVisitorCallbacks overrides. #define TYPE_RECORD(EnumName, EnumVal, Name) \ @@ -109,6 +110,7 @@ TypeTableBuilder &DestStream; FieldListRecordBuilder FieldListBuilder; + TypeServerHandler *Handler; bool IsInFieldList{false}; size_t BeginIndexMapSize = 0; @@ -175,6 +177,8 @@ Pipeline.addCallbackToPipeline(*this); CVTypeVisitor Visitor(Pipeline); + if (Handler) + Visitor.addTypeServerHandler(*Handler); if (auto EC = Visitor.visitTypeStream(Types)) return EC; @@ -186,6 +190,7 @@ } Error llvm::codeview::mergeTypeStreams(TypeTableBuilder &DestStream, + TypeServerHandler *Handler, const CVTypeArray &Types) { - return TypeStreamMerger(DestStream).mergeStream(Types); + return TypeStreamMerger(DestStream, Handler).mergeStream(Types); } Index: llvm/lib/DebugInfo/PDB/CMakeLists.txt =================================================================== --- llvm/lib/DebugInfo/PDB/CMakeLists.txt +++ llvm/lib/DebugInfo/PDB/CMakeLists.txt @@ -44,6 +44,7 @@ Native/NativeSession.cpp Native/PDBFile.cpp Native/PDBFileBuilder.cpp + Native/PDBTypeServerHandler.cpp Native/PublicsStream.cpp Native/RawError.cpp Native/StringTable.cpp Index: llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp +++ llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp @@ -47,7 +47,7 @@ auto Stream = llvm::make_unique(std::move(Buffer)); auto Allocator = llvm::make_unique(); - auto File = llvm::make_unique(std::move(Stream), *Allocator); + auto File = llvm::make_unique(Path, std::move(Stream), *Allocator); if (auto EC = File->parseFileHeaders()) return EC; if (auto EC = File->parseStreamData()) Index: llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp +++ llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -25,6 +25,7 @@ #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" +#include "llvm/Support/Path.h" #include #include #include @@ -38,12 +39,18 @@ typedef FixedStreamArray ulittle_array; } // end anonymous namespace -PDBFile::PDBFile(std::unique_ptr PdbFileBuffer, +PDBFile::PDBFile(StringRef Path, std::unique_ptr PdbFileBuffer, BumpPtrAllocator &Allocator) - : Allocator(Allocator), Buffer(std::move(PdbFileBuffer)) {} + : FilePath(Path), Allocator(Allocator), Buffer(std::move(PdbFileBuffer)) {} PDBFile::~PDBFile() = default; +StringRef PDBFile::getFilePath() const { return FilePath; } + +StringRef PDBFile::getFileDirectory() const { + return sys::path::parent_path(FilePath); +} + uint32_t PDBFile::getBlockSize() const { return ContainerLayout.SB->BlockSize; } uint32_t PDBFile::getFreeBlockMapBlock() const { Index: llvm/lib/DebugInfo/PDB/Native/PDBTypeServerHandler.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/PDB/Native/PDBTypeServerHandler.cpp @@ -0,0 +1,104 @@ +//===- PDBTypeServerHandler.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/Native/PDBTypeServerHandler.h" + +#include "llvm/DebugInfo/CodeView/CodeViewError.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static void ignoreErrors(Error EC) { + llvm::handleAllErrors(std::move(EC), [&](ErrorInfoBase &EIB) {}); +} + +PDBTypeServerHandler::PDBTypeServerHandler(bool RevisitAlways) + : RevisitAlways(RevisitAlways) {} + +void PDBTypeServerHandler::addSearchPath(StringRef Path) { + if (Path.empty() || !sys::fs::is_directory(Path)) + return; + + SearchPaths.push_back(Path); +} + +Expected +PDBTypeServerHandler::handleInternal(PDBFile &File, + TypeVisitorCallbacks &Callbacks) { + auto ExpectedTpi = File.getPDBTpiStream(); + if (!ExpectedTpi) + return ExpectedTpi.takeError(); + CVTypeVisitor Visitor(Callbacks); + + if (auto EC = Visitor.visitTypeStream(ExpectedTpi->types(nullptr))) + return std::move(EC); + + return true; +} + +Expected PDBTypeServerHandler::handle(TypeServer2Record &TS, + TypeVisitorCallbacks &Callbacks) { + if (Session) { + // If we've already handled this TypeServer and we only want to handle each + // TypeServer once, consume the record without doing anything. + if (!RevisitAlways) + return true; + + return handleInternal(Session->getPDBFile(), Callbacks); + } + + StringRef File = sys::path::filename(TS.Name); + if (File.empty()) + return make_error( + cv_error_code::corrupt_record, + "TypeServer2Record does not contain filename!"); + + for (auto Path : SearchPaths) { + sys::path::append(Path, File); + if (!sys::fs::exists(Path)) + continue; + + std::unique_ptr ThisSession; + if (auto EC = loadDataForPDB(PDB_ReaderType::Native, Path, ThisSession)) { + // It is not an error if this PDB fails to load, it just means that it + // doesn't match and we should continue searching. + ignoreErrors(std::move(EC)); + continue; + } + + std::unique_ptr NS( + static_cast(ThisSession.release())); + PDBFile &File = NS->getPDBFile(); + auto ExpectedInfo = File.getPDBInfoStream(); + // All PDB Files should have an Info stream. + if (!ExpectedInfo) + return ExpectedInfo.takeError(); + + ArrayRef GuidBytes(ExpectedInfo->getGuid().Guid); + StringRef GuidStr(reinterpret_cast(GuidBytes.begin()), + GuidBytes.size()); + if (GuidStr != TS.Guid) + continue; + + Session = std::move(NS); + return handleInternal(File, Callbacks); + } + + // We couldn't find a matching PDB, so let it be handled by someone else. + return false; +} Index: llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp +++ llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp @@ -16,6 +16,7 @@ #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/MSF/StreamReader.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h" #include "llvm/DebugInfo/PDB/Native/RawConstants.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" @@ -164,7 +165,7 @@ HashTable &TpiStream::getHashAdjusters() { return HashAdjusters; } -iterator_range TpiStream::types(bool *HadError) const { +CVTypeRange TpiStream::types(bool *HadError) const { return make_range(TypeRecords.begin(HadError), TypeRecords.end()); } Index: llvm/tools/llvm-readobj/CMakeLists.txt =================================================================== --- llvm/tools/llvm-readobj/CMakeLists.txt +++ llvm/tools/llvm-readobj/CMakeLists.txt @@ -4,6 +4,7 @@ Support DebugInfoCodeView DebugInfoMSF + DebugInfoPDB ) add_llvm_tool(llvm-readobj Index: llvm/tools/llvm-readobj/COFFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/COFFDumper.cpp +++ llvm/tools/llvm-readobj/COFFDumper.cpp @@ -43,6 +43,7 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/Win64EH.h" @@ -1086,7 +1087,7 @@ error(object_error::parse_failed); } - if (auto EC = mergeTypeStreams(CVTypes, Types)) { + if (auto EC = mergeTypeStreams(CVTypes, nullptr, Types)) { consumeError(std::move(EC)); return error(object_error::parse_failed); } Index: llvm/tools/llvm-readobj/LLVMBuild.txt =================================================================== --- llvm/tools/llvm-readobj/LLVMBuild.txt +++ llvm/tools/llvm-readobj/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-readobj parent = Tools -required_libraries = all-targets BitReader Object DebugInfoCodeView DebugInfoMSF +required_libraries = all-targets BitReader Object DebugInfoCodeView DebugInfoPDB DebugInfoMSF Index: llvm/unittests/DebugInfo/PDB/CMakeLists.txt =================================================================== --- llvm/unittests/DebugInfo/PDB/CMakeLists.txt +++ llvm/unittests/DebugInfo/PDB/CMakeLists.txt @@ -10,6 +10,7 @@ StringTableBuilderTest.cpp MSFBuilderTest.cpp PDBApiTest.cpp + TypeServerHandlerTest.cpp ) add_llvm_unittest(DebugInfoPDBTests Index: llvm/unittests/DebugInfo/PDB/TypeServerHandlerTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/PDB/TypeServerHandlerTest.cpp @@ -0,0 +1,175 @@ +//===- llvm/unittest/DebugInfo/PDB/TypeServerHandlerTest.cpp --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ErrorChecking.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" +#include "llvm/DebugInfo/CodeView/TypeSerializer.h" +#include "llvm/DebugInfo/CodeView/TypeServerHandler.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { + +constexpr uint8_t Guid[] = {0x2a, 0x2c, 0x1c, 0x2a, 0xcb, 0x9e, 0x48, 0x18, + 0x82, 0x82, 0x7a, 0x87, 0xc3, 0xfe, 0x16, 0xe8}; +StringRef GuidStr(reinterpret_cast(Guid), + llvm::array_lengthof(Guid)); + +constexpr const char *Name = "Test Name"; +constexpr int Age = 1; + +class MockTypeServerHandler : public TypeServerHandler { +public: + explicit MockTypeServerHandler(bool HandleAlways) + : HandleAlways(HandleAlways) {} + + Expected handle(TypeServer2Record &TS, + TypeVisitorCallbacks &Callbacks) override { + if (TS.Age != Age || TS.Guid != GuidStr || TS.Name != Name) + return make_error(cv_error_code::corrupt_record, + "Invalid TypeServer record!"); + + if (Handled && !HandleAlways) + return false; + + Handled = true; + return true; + } + + bool Handled = false; + bool HandleAlways; +}; + +class MockTypeVisitorCallbacks : public TypeVisitorCallbacks { +public: + enum class State { + Ready, + VisitTypeBegin, + VisitKnownRecord, + VisitTypeEnd, + }; + Error visitTypeBegin(CVType &CVT) override { + if (S != State::Ready) + return make_error(cv_error_code::unspecified, + "Invalid visitor state!"); + + S = State::VisitTypeBegin; + return Error::success(); + } + + Error visitKnownRecord(CVType &CVT, TypeServer2Record &TS) override { + if (S != State::VisitTypeBegin) + return make_error(cv_error_code::unspecified, + "Invalid visitor state!"); + + S = State::VisitKnownRecord; + return Error::success(); + } + + Error visitTypeEnd(CVType &CVT) override { + if (S != State::VisitKnownRecord) + return make_error(cv_error_code::unspecified, + "Invalid visitor state!"); + + S = State::VisitTypeEnd; + return Error::success(); + } + + State S = State::Ready; +}; + +class TypeServerHandlerTest : public testing::Test { +public: + void SetUp() override { + TypeServer2Record R(TypeRecordKind::TypeServer2); + R.Age = Age; + R.Guid = GuidStr; + R.Name = Name; + + TypeTableBuilder Builder(Allocator); + Builder.writeKnownType(R); + TypeServerRecord.RecordData = Builder.records().front(); + TypeServerRecord.Type = TypeLeafKind::LF_TYPESERVER2; + } + +protected: + BumpPtrAllocator Allocator; + CVType TypeServerRecord; +}; + +// Test that when no type server handler is registered, it gets handled by the +// normal +// visitor callbacks. +TEST_F(TypeServerHandlerTest, VisitRecordNoTypeServer) { + MockTypeVisitorCallbacks C2; + MockTypeVisitorCallbacks C1; + TypeVisitorCallbackPipeline Pipeline; + + Pipeline.addCallbackToPipeline(C1); + Pipeline.addCallbackToPipeline(C2); + CVTypeVisitor Visitor(Pipeline); + EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord)); + + EXPECT_EQ(MockTypeVisitorCallbacks::State::VisitTypeEnd, C1.S); + EXPECT_EQ(MockTypeVisitorCallbacks::State::VisitTypeEnd, C2.S); +} + +// Test that when a TypeServerHandler is registered, it gets consumed by the +// handler if and only if the handler returns true. +TEST_F(TypeServerHandlerTest, VisitRecordWithTypeServerOnce) { + MockTypeServerHandler Handler(false); + + MockTypeVisitorCallbacks C1; + CVTypeVisitor Visitor(C1); + Visitor.addTypeServerHandler(Handler); + + // Our mock server returns true the first time. + EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord)); + EXPECT_TRUE(Handler.Handled); + EXPECT_EQ(MockTypeVisitorCallbacks::State::Ready, C1.S); + + // And false the second time. + EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord)); + EXPECT_TRUE(Handler.Handled); + EXPECT_EQ(MockTypeVisitorCallbacks::State::VisitTypeEnd, C1.S); +} + +// Test that when a type server handler is registered, if the handler keeps +// returning true, it will keep getting consumed by the handler and not go +// to the default processor. +TEST_F(TypeServerHandlerTest, VisitRecordWithTypeServerAlways) { + MockTypeServerHandler Handler(true); + + MockTypeVisitorCallbacks C1; + CVTypeVisitor Visitor(C1); + Visitor.addTypeServerHandler(Handler); + + EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord)); + EXPECT_TRUE(Handler.Handled); + EXPECT_EQ(MockTypeVisitorCallbacks::State::Ready, C1.S); + + EXPECT_NO_ERROR(Visitor.visitTypeRecord(TypeServerRecord)); + EXPECT_TRUE(Handler.Handled); + EXPECT_EQ(MockTypeVisitorCallbacks::State::Ready, C1.S); +} + +} // end anonymous namespace