| //===- YAMLOutputStyle.cpp ------------------------------------ *- 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "YAMLOutputStyle.h" |
| |
| #include "PdbYaml.h" |
| #include "llvm-pdbutil.h" |
| |
| #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" |
| #include "llvm/DebugInfo/CodeView/DebugSubsection.h" |
| #include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h" |
| #include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" |
| #include "llvm/DebugInfo/MSF/MappedBlockStream.h" |
| #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/ModuleDebugStream.h" |
| #include "llvm/DebugInfo/PDB/Native/PDBFile.h" |
| #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" |
| #include "llvm/DebugInfo/PDB/Native/RawConstants.h" |
| #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" |
| #include "llvm/DebugInfo/PDB/Native/TpiStream.h" |
| |
| using namespace llvm; |
| using namespace llvm::codeview; |
| using namespace llvm::pdb; |
| |
| static bool checkModuleSubsection(opts::ModuleSubsection MS) { |
| return any_of(opts::pdb2yaml::DumpModuleSubsections, |
| [=](opts::ModuleSubsection M) { |
| return M == MS || M == opts::ModuleSubsection::All; |
| }); |
| } |
| |
| YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) |
| : File(File), Out(outs()), Obj(File.getAllocator()) { |
| Out.setWriteDefaultValues(!opts::pdb2yaml::Minimal); |
| } |
| |
| Error YAMLOutputStyle::dump() { |
| if (opts::pdb2yaml::StreamDirectory) |
| opts::pdb2yaml::StreamMetadata = true; |
| |
| if (auto EC = dumpFileHeaders()) |
| return EC; |
| |
| if (auto EC = dumpStreamMetadata()) |
| return EC; |
| |
| if (auto EC = dumpStreamDirectory()) |
| return EC; |
| |
| if (auto EC = dumpStringTable()) |
| return EC; |
| |
| if (auto EC = dumpPDBStream()) |
| return EC; |
| |
| if (auto EC = dumpDbiStream()) |
| return EC; |
| |
| if (auto EC = dumpTpiStream()) |
| return EC; |
| |
| if (auto EC = dumpIpiStream()) |
| return EC; |
| |
| if (auto EC = dumpPublics()) |
| return EC; |
| |
| flush(); |
| return Error::success(); |
| } |
| |
| |
| Error YAMLOutputStyle::dumpFileHeaders() { |
| if (opts::pdb2yaml::NoFileHeaders) |
| return Error::success(); |
| |
| yaml::MSFHeaders Headers; |
| Obj.Headers.emplace(); |
| Obj.Headers->SuperBlock.NumBlocks = File.getBlockCount(); |
| Obj.Headers->SuperBlock.BlockMapAddr = File.getBlockMapIndex(); |
| Obj.Headers->SuperBlock.BlockSize = File.getBlockSize(); |
| auto Blocks = File.getDirectoryBlockArray(); |
| Obj.Headers->DirectoryBlocks.assign(Blocks.begin(), Blocks.end()); |
| Obj.Headers->NumDirectoryBlocks = File.getNumDirectoryBlocks(); |
| Obj.Headers->SuperBlock.NumDirectoryBytes = File.getNumDirectoryBytes(); |
| Obj.Headers->NumStreams = |
| opts::pdb2yaml::StreamMetadata ? File.getNumStreams() : 0; |
| Obj.Headers->SuperBlock.FreeBlockMapBlock = File.getFreeBlockMapBlock(); |
| Obj.Headers->SuperBlock.Unknown1 = File.getUnknown1(); |
| Obj.Headers->FileSize = File.getFileSize(); |
| |
| return Error::success(); |
| } |
| |
| Error YAMLOutputStyle::dumpStringTable() { |
| bool RequiresStringTable = opts::pdb2yaml::DumpModuleFiles || |
| !opts::pdb2yaml::DumpModuleSubsections.empty(); |
| bool RequestedStringTable = opts::pdb2yaml::StringTable; |
| if (!RequiresStringTable && !RequestedStringTable) |
| return Error::success(); |
| |
| auto ExpectedST = File.getStringTable(); |
| if (!ExpectedST) |
| return ExpectedST.takeError(); |
| |
| Obj.StringTable.emplace(); |
| const auto &ST = ExpectedST.get(); |
| for (auto ID : ST.name_ids()) { |
| auto S = ST.getStringForID(ID); |
| if (!S) |
| return S.takeError(); |
| if (S->empty()) |
| continue; |
| Obj.StringTable->push_back(*S); |
| } |
| return Error::success(); |
| } |
| |
| Error YAMLOutputStyle::dumpStreamMetadata() { |
| if (!opts::pdb2yaml::StreamMetadata) |
| return Error::success(); |
| |
| Obj.StreamSizes.emplace(); |
| Obj.StreamSizes->assign(File.getStreamSizes().begin(), |
| File.getStreamSizes().end()); |
| return Error::success(); |
| } |
| |
| Error YAMLOutputStyle::dumpStreamDirectory() { |
| if (!opts::pdb2yaml::StreamDirectory) |
| return Error::success(); |
| |
| auto StreamMap = File.getStreamMap(); |
| Obj.StreamMap.emplace(); |
| for (auto &Stream : StreamMap) { |
| pdb::yaml::StreamBlockList BlockList; |
| BlockList.Blocks.assign(Stream.begin(), Stream.end()); |
| Obj.StreamMap->push_back(BlockList); |
| } |
| |
| 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(); |
| Obj.PdbStream->Features = InfoS.getFeatureSignatures(); |
| |
| return Error::success(); |
| } |
| |
| static opts::ModuleSubsection convertSubsectionKind(DebugSubsectionKind K) { |
| switch (K) { |
| case DebugSubsectionKind::CrossScopeExports: |
| return opts::ModuleSubsection::CrossScopeExports; |
| case DebugSubsectionKind::CrossScopeImports: |
| return opts::ModuleSubsection::CrossScopeImports; |
| case DebugSubsectionKind::FileChecksums: |
| return opts::ModuleSubsection::FileChecksums; |
| case DebugSubsectionKind::InlineeLines: |
| return opts::ModuleSubsection::InlineeLines; |
| case DebugSubsectionKind::Lines: |
| return opts::ModuleSubsection::Lines; |
| case DebugSubsectionKind::Symbols: |
| return opts::ModuleSubsection::Symbols; |
| case DebugSubsectionKind::StringTable: |
| return opts::ModuleSubsection::StringTable; |
| case DebugSubsectionKind::FrameData: |
| return opts::ModuleSubsection::FrameData; |
| default: |
| return opts::ModuleSubsection::Unknown; |
| } |
| llvm_unreachable("Unreachable!"); |
| } |
| |
| Error YAMLOutputStyle::dumpDbiStream() { |
| if (!opts::pdb2yaml::DbiStream) |
| return Error::success(); |
| |
| if (!File.hasPDBDbiStream()) |
| return Error::success(); |
| |
| auto DbiS = File.getPDBDbiStream(); |
| if (!DbiS) |
| return DbiS.takeError(); |
| |
| auto &DS = DbiS.get(); |
| Obj.DbiStream.emplace(); |
| Obj.DbiStream->Age = DS.getAge(); |
| Obj.DbiStream->BuildNumber = DS.getBuildNumber(); |
| Obj.DbiStream->Flags = DS.getFlags(); |
| Obj.DbiStream->MachineType = DS.getMachineType(); |
| Obj.DbiStream->PdbDllRbld = DS.getPdbDllRbld(); |
| Obj.DbiStream->PdbDllVersion = DS.getPdbDllVersion(); |
| Obj.DbiStream->VerHeader = DS.getDbiVersion(); |
| if (opts::pdb2yaml::DumpModules) { |
| const auto &Modules = DS.modules(); |
| for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) { |
| DbiModuleDescriptor MI = Modules.getModuleDescriptor(I); |
| |
| Obj.DbiStream->ModInfos.emplace_back(); |
| yaml::PdbDbiModuleInfo &DMI = Obj.DbiStream->ModInfos.back(); |
| |
| DMI.Mod = MI.getModuleName(); |
| DMI.Obj = MI.getObjFileName(); |
| if (opts::pdb2yaml::DumpModuleFiles) { |
| auto Files = Modules.source_files(I); |
| DMI.SourceFiles.assign(Files.begin(), Files.end()); |
| } |
| |
| uint16_t ModiStream = MI.getModuleStreamIndex(); |
| if (ModiStream == kInvalidStreamIndex) |
| continue; |
| |
| auto ModStreamData = File.createIndexedStream(ModiStream); |
| pdb::ModuleDebugStreamRef ModS(MI, std::move(ModStreamData)); |
| if (auto EC = ModS.reload()) |
| return EC; |
| |
| auto ExpectedST = File.getStringTable(); |
| if (!ExpectedST) |
| return ExpectedST.takeError(); |
| if (!opts::pdb2yaml::DumpModuleSubsections.empty() && |
| ModS.hasDebugSubsections()) { |
| auto ExpectedChecksums = ModS.findChecksumsSubsection(); |
| if (!ExpectedChecksums) |
| return ExpectedChecksums.takeError(); |
| |
| StringsAndChecksumsRef SC(ExpectedST->getStringTable(), |
| *ExpectedChecksums); |
| |
| for (const auto &SS : ModS.subsections()) { |
| opts::ModuleSubsection OptionKind = convertSubsectionKind(SS.kind()); |
| if (!checkModuleSubsection(OptionKind)) |
| continue; |
| |
| auto Converted = |
| CodeViewYAML::YAMLDebugSubsection::fromCodeViewSubection(SC, SS); |
| if (!Converted) |
| return Converted.takeError(); |
| DMI.Subsections.push_back(*Converted); |
| } |
| } |
| |
| if (opts::pdb2yaml::DumpModuleSyms) { |
| DMI.Modi.emplace(); |
| |
| DMI.Modi->Signature = ModS.signature(); |
| bool HadError = false; |
| for (auto &Sym : ModS.symbols(&HadError)) { |
| auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(Sym); |
| if (!ES) |
| return ES.takeError(); |
| |
| DMI.Modi->Symbols.push_back(*ES); |
| } |
| } |
| } |
| } |
| return Error::success(); |
| } |
| |
| Error YAMLOutputStyle::dumpTpiStream() { |
| if (!opts::pdb2yaml::TpiStream) |
| return Error::success(); |
| |
| auto TpiS = File.getPDBTpiStream(); |
| if (!TpiS) |
| return TpiS.takeError(); |
| |
| auto &TS = TpiS.get(); |
| Obj.TpiStream.emplace(); |
| Obj.TpiStream->Version = TS.getTpiVersion(); |
| for (auto &Record : TS.types(nullptr)) { |
| auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Record); |
| if (!ExpectedRecord) |
| return ExpectedRecord.takeError(); |
| Obj.TpiStream->Records.push_back(*ExpectedRecord); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error YAMLOutputStyle::dumpIpiStream() { |
| if (!opts::pdb2yaml::IpiStream) |
| return Error::success(); |
| |
| auto InfoS = File.getPDBInfoStream(); |
| if (!InfoS) |
| return InfoS.takeError(); |
| if (!InfoS->containsIdStream()) |
| return Error::success(); |
| |
| auto IpiS = File.getPDBIpiStream(); |
| if (!IpiS) |
| return IpiS.takeError(); |
| |
| auto &IS = IpiS.get(); |
| Obj.IpiStream.emplace(); |
| Obj.IpiStream->Version = IS.getTpiVersion(); |
| for (auto &Record : IS.types(nullptr)) { |
| auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Record); |
| if (!ExpectedRecord) |
| return ExpectedRecord.takeError(); |
| |
| Obj.IpiStream->Records.push_back(*ExpectedRecord); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error YAMLOutputStyle::dumpPublics() { |
| if (!opts::pdb2yaml::PublicsStream) |
| return Error::success(); |
| |
| Obj.PublicsStream.emplace(); |
| auto ExpectedPublics = File.getPDBPublicsStream(); |
| if (!ExpectedPublics) { |
| llvm::consumeError(ExpectedPublics.takeError()); |
| return Error::success(); |
| } |
| |
| PublicsStream &Publics = *ExpectedPublics; |
| const GSIHashTable &PublicsTable = Publics.getPublicsTable(); |
| |
| auto ExpectedSyms = File.getPDBSymbolStream(); |
| if (!ExpectedSyms) { |
| llvm::consumeError(ExpectedSyms.takeError()); |
| return Error::success(); |
| } |
| |
| BinaryStreamRef SymStream = |
| ExpectedSyms->getSymbolArray().getUnderlyingStream(); |
| for (uint32_t PubSymOff : PublicsTable) { |
| Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff); |
| if (!Sym) |
| return Sym.takeError(); |
| auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(*Sym); |
| if (!ES) |
| return ES.takeError(); |
| |
| Obj.PublicsStream->PubSyms.push_back(*ES); |
| } |
| |
| return Error::success(); |
| } |
| |
| void YAMLOutputStyle::flush() { |
| Out << Obj; |
| outs().flush(); |
| } |