553 lines · cpp
1//===-- LVReader.cpp ------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This implements the LVReader class.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"14#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"15#include "llvm/Support/FileSystem.h"16#include "llvm/Support/FormatAdapters.h"17#include "llvm/Support/FormatVariadic.h"18#include <tuple>19 20using namespace llvm;21using namespace llvm::logicalview;22 23#define DEBUG_TYPE "Reader"24 25// Detect elements that are inserted more than once at different scopes,26// causing a crash on the reader destruction, as the element is already27// deleted from other scope. Helper for CodeView reader.28bool checkIntegrityScopesTree(LVScope *Root) {29 using LVDuplicateEntry = std::tuple<LVElement *, LVScope *, LVScope *>;30 using LVDuplicate = std::vector<LVDuplicateEntry>;31 LVDuplicate Duplicate;32 33 using LVIntegrity = std::map<LVElement *, LVScope *>;34 LVIntegrity Integrity;35 36 // Add the given element to the integrity map.37 auto AddElement = [&](LVElement *Element, LVScope *Scope) {38 LVIntegrity::iterator Iter = Integrity.find(Element);39 if (Iter == Integrity.end())40 Integrity.emplace(Element, Scope);41 else42 // We found a duplicate.43 Duplicate.emplace_back(Element, Scope, Iter->second);44 };45 46 // Recursively add all the elements in the scope.47 std::function<void(LVScope * Parent)> TraverseScope = [&](LVScope *Parent) {48 auto Traverse = [&](const auto *Set) {49 if (Set)50 for (const auto &Entry : *Set)51 AddElement(Entry, Parent);52 };53 if (const LVScopes *Scopes = Parent->getScopes()) {54 for (LVScope *Scope : *Scopes) {55 AddElement(Scope, Parent);56 TraverseScope(Scope);57 }58 }59 Traverse(Parent->getSymbols());60 Traverse(Parent->getTypes());61 Traverse(Parent->getLines());62 };63 64 // Start traversing the scopes root and print any duplicates.65 TraverseScope(Root);66 bool PassIntegrity = true;67 if (Duplicate.size()) {68 llvm::stable_sort(Duplicate, [](const auto &l, const auto &r) {69 return std::get<0>(l)->getID() < std::get<0>(r)->getID();70 });71 72 auto PrintIndex = [](unsigned Index) {73 if (Index)74 dbgs() << format("%8d: ", Index);75 else76 dbgs() << format("%8c: ", ' ');77 };78 auto PrintElement = [&](LVElement *Element, unsigned Index = 0) {79 PrintIndex(Index);80 std::string ElementName(Element->getName());81 dbgs() << format("%15s ID=0x%08x '%s'\n", Element->kind(),82 Element->getID(), ElementName.c_str());83 };84 85 std::string RootName(Root->getName());86 dbgs() << formatv("{0}\n", fmt_repeat('=', 72));87 dbgs() << format("Root: '%s'\nDuplicated elements: %d\n", RootName.c_str(),88 Duplicate.size());89 dbgs() << formatv("{0}\n", fmt_repeat('=', 72));90 91 unsigned Index = 0;92 for (const LVDuplicateEntry &Entry : Duplicate) {93 LVElement *Element;94 LVScope *First;95 LVScope *Second;96 std::tie(Element, First, Second) = Entry;97 dbgs() << formatv("\n{0}\n", fmt_repeat('-', 72));98 PrintElement(Element, ++Index);99 PrintElement(First);100 PrintElement(Second);101 dbgs() << formatv("{0}\n", fmt_repeat('-', 72));102 }103 PassIntegrity = false;104 }105 return PassIntegrity;106}107 108//===----------------------------------------------------------------------===//109// Class to represent a split context.110//===----------------------------------------------------------------------===//111Error LVSplitContext::createSplitFolder(StringRef Where) {112 // The 'location' will represent the root directory for the output created113 // by the context. It will contain the different CUs files, that will be114 // extracted from a single ELF.115 Location = std::string(Where);116 117 // Add a trailing slash, if there is none.118 size_t Pos = Location.find_last_of('/');119 if (Location.length() != Pos + 1)120 Location.append("/");121 122 // Make sure the new directory exists, creating it if necessary.123 if (std::error_code EC = llvm::sys::fs::create_directories(Location))124 return createStringError(EC, "Error: could not create directory %s",125 Location.c_str());126 127 return Error::success();128}129 130std::error_code LVSplitContext::open(std::string ContextName,131 std::string Extension, raw_ostream &OS) {132 assert(OutputFile == nullptr && "OutputFile already set.");133 134 // Transforms '/', '\', '.', ':' into '_'.135 std::string Name(flattenedFilePath(ContextName));136 Name.append(Extension);137 // Add the split context location folder name.138 if (!Location.empty())139 Name.insert(0, Location);140 141 std::error_code EC;142 OutputFile = std::make_unique<ToolOutputFile>(Name, EC, sys::fs::OF_None);143 if (EC)144 return EC;145 146 // Don't remove output file.147 OutputFile->keep();148 return std::error_code();149}150 151static LVReader *CurrentReader = nullptr;152LVReader &LVReader::getInstance() {153 if (CurrentReader)154 return *CurrentReader;155 outs() << "Invalid instance reader.\n";156 llvm_unreachable("Invalid instance reader.");157}158void LVReader::setInstance(LVReader *Reader) { CurrentReader = Reader; }159 160Error LVReader::createSplitFolder() {161 if (OutputSplit) {162 // If the '--output=split' was specified, but no '--split-folder'163 // option, use the input file as base for the split location.164 if (options().getOutputFolder().empty())165 options().setOutputFolder(getFilename().str() + "_cus");166 167 SmallString<128> SplitFolder;168 SplitFolder = options().getOutputFolder();169 sys::fs::make_absolute(SplitFolder);170 171 // Return error if unable to create a split context location.172 if (Error Err = SplitContext.createSplitFolder(SplitFolder))173 return Err;174 175 OS << "\nSplit View Location: '" << SplitContext.getLocation() << "'\n";176 }177 178 return Error::success();179}180 181// Get the filename for given object.182StringRef LVReader::getFilename(LVObject *Object, size_t Index) const {183 // TODO: The current CodeView Reader implementation does not have support184 // for multiple compile units. Until we have a proper offset calculation,185 // check only in the current compile unit.186 if (CompileUnits.size()) {187 // Get Compile Unit for the given object.188 LVCompileUnits::const_iterator Iter =189 std::prev(CompileUnits.lower_bound(Object->getOffset()));190 if (Iter != CompileUnits.end())191 return Iter->second->getFilename(Index);192 }193 194 return CompileUnit ? CompileUnit->getFilename(Index) : StringRef();195}196 197void LVReader::addSectionRange(LVSectionIndex SectionIndex, LVScope *Scope) {198 LVRange *ScopesWithRanges = getSectionRanges(SectionIndex);199 ScopesWithRanges->addEntry(Scope);200}201 202void LVReader::addSectionRange(LVSectionIndex SectionIndex, LVScope *Scope,203 LVAddress LowerAddress, LVAddress UpperAddress) {204 LVRange *ScopesWithRanges = getSectionRanges(SectionIndex);205 ScopesWithRanges->addEntry(Scope, LowerAddress, UpperAddress);206}207 208LVRange *LVReader::getSectionRanges(LVSectionIndex SectionIndex) {209 // Check if we already have a mapping for this section index.210 LVSectionRanges::iterator IterSection = SectionRanges.find(SectionIndex);211 if (IterSection == SectionRanges.end())212 IterSection =213 SectionRanges.emplace(SectionIndex, std::make_unique<LVRange>()).first;214 LVRange *Range = IterSection->second.get();215 assert(Range && "Range is null.");216 return Range;217}218 219LVElement *LVReader::createElement(dwarf::Tag Tag) {220 CurrentScope = nullptr;221 CurrentSymbol = nullptr;222 CurrentType = nullptr;223 CurrentRanges.clear();224 225 LLVM_DEBUG(226 { dbgs() << "\n[createElement] " << dwarf::TagString(Tag) << "\n"; });227 228 if (!options().getPrintSymbols()) {229 switch (Tag) {230 // As the command line options did not specify a request to print231 // logical symbols (--print=symbols or --print=all or --print=elements),232 // skip its creation.233 case dwarf::DW_TAG_formal_parameter:234 case dwarf::DW_TAG_unspecified_parameters:235 case dwarf::DW_TAG_member:236 case dwarf::DW_TAG_variable:237 case dwarf::DW_TAG_inheritance:238 case dwarf::DW_TAG_constant:239 case dwarf::DW_TAG_call_site_parameter:240 case dwarf::DW_TAG_GNU_call_site_parameter:241 return nullptr;242 default:243 break;244 }245 }246 247 switch (Tag) {248 // Types.249 case dwarf::DW_TAG_base_type:250 CurrentType = createType();251 CurrentType->setIsBase();252 if (options().getAttributeBase())253 CurrentType->setIncludeInPrint();254 return CurrentType;255 case dwarf::DW_TAG_const_type:256 CurrentType = createType();257 CurrentType->setIsConst();258 CurrentType->setName("const");259 return CurrentType;260 case dwarf::DW_TAG_enumerator:261 CurrentType = createTypeEnumerator();262 return CurrentType;263 case dwarf::DW_TAG_imported_declaration:264 CurrentType = createTypeImport();265 CurrentType->setIsImportDeclaration();266 return CurrentType;267 case dwarf::DW_TAG_imported_module:268 CurrentType = createTypeImport();269 CurrentType->setIsImportModule();270 return CurrentType;271 case dwarf::DW_TAG_pointer_type:272 CurrentType = createType();273 CurrentType->setIsPointer();274 CurrentType->setName("*");275 return CurrentType;276 case dwarf::DW_TAG_ptr_to_member_type:277 CurrentType = createType();278 CurrentType->setIsPointerMember();279 CurrentType->setName("*");280 return CurrentType;281 case dwarf::DW_TAG_reference_type:282 CurrentType = createType();283 CurrentType->setIsReference();284 CurrentType->setName("&");285 return CurrentType;286 case dwarf::DW_TAG_restrict_type:287 CurrentType = createType();288 CurrentType->setIsRestrict();289 CurrentType->setName("restrict");290 return CurrentType;291 case dwarf::DW_TAG_rvalue_reference_type:292 CurrentType = createType();293 CurrentType->setIsRvalueReference();294 CurrentType->setName("&&");295 return CurrentType;296 case dwarf::DW_TAG_subrange_type:297 CurrentType = createTypeSubrange();298 return CurrentType;299 case dwarf::DW_TAG_template_value_parameter:300 CurrentType = createTypeParam();301 CurrentType->setIsTemplateValueParam();302 return CurrentType;303 case dwarf::DW_TAG_template_type_parameter:304 CurrentType = createTypeParam();305 CurrentType->setIsTemplateTypeParam();306 return CurrentType;307 case dwarf::DW_TAG_GNU_template_template_param:308 CurrentType = createTypeParam();309 CurrentType->setIsTemplateTemplateParam();310 return CurrentType;311 case dwarf::DW_TAG_typedef:312 CurrentType = createTypeDefinition();313 return CurrentType;314 case dwarf::DW_TAG_unspecified_type:315 CurrentType = createType();316 CurrentType->setIsUnspecified();317 return CurrentType;318 case dwarf::DW_TAG_volatile_type:319 CurrentType = createType();320 CurrentType->setIsVolatile();321 CurrentType->setName("volatile");322 return CurrentType;323 324 // Symbols.325 case dwarf::DW_TAG_formal_parameter:326 CurrentSymbol = createSymbol();327 CurrentSymbol->setIsParameter();328 return CurrentSymbol;329 case dwarf::DW_TAG_unspecified_parameters:330 CurrentSymbol = createSymbol();331 CurrentSymbol->setIsUnspecified();332 CurrentSymbol->setName("...");333 return CurrentSymbol;334 case dwarf::DW_TAG_member:335 CurrentSymbol = createSymbol();336 CurrentSymbol->setIsMember();337 return CurrentSymbol;338 case dwarf::DW_TAG_variable:339 CurrentSymbol = createSymbol();340 CurrentSymbol->setIsVariable();341 return CurrentSymbol;342 case dwarf::DW_TAG_inheritance:343 CurrentSymbol = createSymbol();344 CurrentSymbol->setIsInheritance();345 return CurrentSymbol;346 case dwarf::DW_TAG_call_site_parameter:347 case dwarf::DW_TAG_GNU_call_site_parameter:348 CurrentSymbol = createSymbol();349 CurrentSymbol->setIsCallSiteParameter();350 return CurrentSymbol;351 case dwarf::DW_TAG_constant:352 CurrentSymbol = createSymbol();353 CurrentSymbol->setIsConstant();354 return CurrentSymbol;355 356 // Scopes.357 case dwarf::DW_TAG_catch_block:358 CurrentScope = createScope();359 CurrentScope->setIsCatchBlock();360 return CurrentScope;361 case dwarf::DW_TAG_lexical_block:362 CurrentScope = createScope();363 CurrentScope->setIsLexicalBlock();364 return CurrentScope;365 case dwarf::DW_TAG_try_block:366 CurrentScope = createScope();367 CurrentScope->setIsTryBlock();368 return CurrentScope;369 case dwarf::DW_TAG_compile_unit:370 case dwarf::DW_TAG_skeleton_unit:371 CurrentScope = createScopeCompileUnit();372 CompileUnit = static_cast<LVScopeCompileUnit *>(CurrentScope);373 return CurrentScope;374 case dwarf::DW_TAG_inlined_subroutine:375 CurrentScope = createScopeFunctionInlined();376 return CurrentScope;377 case dwarf::DW_TAG_namespace:378 CurrentScope = createScopeNamespace();379 return CurrentScope;380 case dwarf::DW_TAG_template_alias:381 CurrentScope = createScopeAlias();382 return CurrentScope;383 case dwarf::DW_TAG_array_type:384 CurrentScope = createScopeArray();385 return CurrentScope;386 case dwarf::DW_TAG_call_site:387 case dwarf::DW_TAG_GNU_call_site:388 CurrentScope = createScopeFunction();389 CurrentScope->setIsCallSite();390 return CurrentScope;391 case dwarf::DW_TAG_entry_point:392 CurrentScope = createScopeFunction();393 CurrentScope->setIsEntryPoint();394 return CurrentScope;395 case dwarf::DW_TAG_subprogram:396 CurrentScope = createScopeFunction();397 CurrentScope->setIsSubprogram();398 return CurrentScope;399 case dwarf::DW_TAG_subroutine_type:400 CurrentScope = createScopeFunctionType();401 return CurrentScope;402 case dwarf::DW_TAG_label:403 CurrentScope = createScopeFunction();404 CurrentScope->setIsLabel();405 return CurrentScope;406 case dwarf::DW_TAG_class_type:407 CurrentScope = createScopeAggregate();408 CurrentScope->setIsClass();409 return CurrentScope;410 case dwarf::DW_TAG_structure_type:411 CurrentScope = createScopeAggregate();412 CurrentScope->setIsStructure();413 return CurrentScope;414 case dwarf::DW_TAG_union_type:415 CurrentScope = createScopeAggregate();416 CurrentScope->setIsUnion();417 return CurrentScope;418 case dwarf::DW_TAG_enumeration_type:419 CurrentScope = createScopeEnumeration();420 return CurrentScope;421 case dwarf::DW_TAG_GNU_formal_parameter_pack:422 CurrentScope = createScopeFormalPack();423 return CurrentScope;424 case dwarf::DW_TAG_GNU_template_parameter_pack:425 CurrentScope = createScopeTemplatePack();426 return CurrentScope;427 case dwarf::DW_TAG_module:428 CurrentScope = createScopeModule();429 return CurrentScope;430 default:431 // Collect TAGs not implemented.432 if (options().getInternalTag() && Tag)433 CompileUnit->addDebugTag(Tag, CurrentOffset);434 break;435 }436 437 LLVM_DEBUG({438 dbgs() << "DWARF Tag not implemented: " << dwarf::TagString(Tag) << "\n";439 });440 441 return nullptr;442}443 444// The Reader is the module that creates the logical view using the debug445// information contained in the binary file specified in the command line.446// This is the main entry point for the Reader and performs the following447// steps:448// - Process any patterns collected from the '--select' options.449// - For each compile unit in the debug information:450// * Create the logical elements (scopes, symbols, types, lines).451// * Collect debug ranges and debug locations.452// * Move the collected logical lines to their associated scopes.453// - Once all the compile units have been processed, traverse the scopes454// tree in order to:455// * Calculate symbol coverage.456// * Detect invalid ranges and locations.457// * "resolve" the logical elements. During this pass, the names and458// file information are updated, to reflect any dependency with other459// logical elements.460Error LVReader::doLoad() {461 // Set current Reader instance.462 setInstance(this);463 464 // Before any scopes creation, process any pattern specified by the465 // --select and --select-offsets options.466 patterns().addGenericPatterns(options().Select.Generic);467 patterns().addOffsetPatterns(options().Select.Offsets);468 469 // Add any specific element printing requests based on the element kind.470 patterns().addRequest(options().Select.Elements);471 patterns().addRequest(options().Select.Lines);472 patterns().addRequest(options().Select.Scopes);473 patterns().addRequest(options().Select.Symbols);474 patterns().addRequest(options().Select.Types);475 476 // Once we have processed the requests for any particular kind of elements,477 // we need to update the report options, in order to have a default value.478 patterns().updateReportOptions();479 480 // Delegate the scope tree creation to the specific reader.481 if (Error Err = createScopes())482 return Err;483 484 if (options().getInternalIntegrity() && !checkIntegrityScopesTree(Root))485 return llvm::make_error<StringError>("Duplicated elements in Scopes Tree",486 inconvertibleErrorCode());487 488 // Calculate symbol coverage and detect invalid debug locations and ranges.489 Root->processRangeInformation();490 491 // As the elements can depend on elements from a different compile unit,492 // information such as name and file/line source information needs to be493 // updated.494 Root->resolveElements();495 496 sortScopes();497 return Error::success();498}499 500// Default handler for a generic reader.501Error LVReader::doPrint() {502 // Set current Reader instance.503 setInstance(this);504 505 // Check for any '--report' request.506 if (options().getReportExecute()) {507 // Requested details.508 if (options().getReportList())509 if (Error Err = printMatchedElements(/*UseMatchedElements=*/true))510 return Err;511 // Requested only children.512 if (options().getReportChildren() && !options().getReportParents())513 if (Error Err = printMatchedElements(/*UseMatchedElements=*/false))514 return Err;515 // Requested (parents) or (parents and children).516 if (options().getReportParents() || options().getReportView())517 if (Error Err = printScopes())518 return Err;519 520 return Error::success();521 }522 523 return printScopes();524}525 526Error LVReader::printScopes() {527 if (bool DoPrint =528 (options().getPrintExecute() || options().getComparePrint())) {529 if (Error Err = createSplitFolder())530 return Err;531 532 // Start printing from the root.533 bool DoMatch = options().getSelectGenericPattern() ||534 options().getSelectGenericKind() ||535 options().getSelectOffsetPattern();536 return Root->doPrint(OutputSplit, DoMatch, DoPrint, OS);537 }538 539 return Error::success();540}541 542Error LVReader::printMatchedElements(bool UseMatchedElements) {543 if (Error Err = createSplitFolder())544 return Err;545 546 return Root->doPrintMatches(OutputSplit, OS, UseMatchedElements);547}548 549void LVReader::print(raw_ostream &OS) const {550 OS << "LVReader\n";551 LLVM_DEBUG(dbgs() << "PrintReader\n");552}553