566 lines · cpp
1//===-- LVElement.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 LVElement class.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/DebugInfo/LogicalView/Core/LVElement.h"14#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"15#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"16#include "llvm/DebugInfo/LogicalView/Core/LVType.h"17 18using namespace llvm;19using namespace llvm::codeview;20using namespace llvm::logicalview;21 22#define DEBUG_TYPE "Element"23 24LVElementDispatch LVElement::Dispatch = {25 {LVElementKind::Discarded, &LVElement::getIsDiscarded},26 {LVElementKind::Global, &LVElement::getIsGlobalReference},27 {LVElementKind::Optimized, &LVElement::getIsOptimized}};28 29LVType *LVElement::getTypeAsType() const {30 return ElementType && ElementType->getIsType()31 ? static_cast<LVType *>(ElementType)32 : nullptr;33}34 35LVScope *LVElement::getTypeAsScope() const {36 return ElementType && ElementType->getIsScope()37 ? static_cast<LVScope *>(ElementType)38 : nullptr;39}40 41// Set the element type.42void LVElement::setGenericType(LVElement *Element) {43 if (!Element->isTemplateParam()) {44 setType(Element);45 return;46 }47 // For template parameters, the instance type can be a type or a scope.48 if (options().getAttributeArgument()) {49 if (Element->getIsKindType())50 setType(Element->getTypeAsType());51 else if (Element->getIsKindScope())52 setType(Element->getTypeAsScope());53 } else54 setType(Element);55}56 57// Discriminator as string.58std::string LVElement::discriminatorAsString() const {59 uint32_t Discriminator = getDiscriminator();60 std::string String;61 raw_string_ostream Stream(String);62 if (Discriminator && options().getAttributeDiscriminator())63 Stream << "," << Discriminator;64 return String;65}66 67// Get the type as a string.68StringRef LVElement::typeAsString() const {69 return getHasType() ? getTypeName() : typeVoid();70}71 72// Get name for element type.73StringRef LVElement::getTypeName() const {74 return ElementType ? ElementType->getName() : StringRef();75}76 77static size_t getStringIndex(StringRef Name) {78 // Convert the name to Unified format ('\' have been converted into '/').79 std::string Pathname(transformPath(Name));80 81 // Depending on the --attribute=filename and --attribute=pathname command82 // line options, use the basename or the full pathname as the name.83 if (!options().getAttributePathname()) {84 // Get the basename by ignoring any prefix up to the last slash ('/').85 StringRef Basename = Pathname;86 size_t Pos = Basename.rfind('/');87 if (Pos != std::string::npos)88 Basename = Basename.substr(Pos + 1);89 return getStringPool().getIndex(Basename);90 }91 92 return getStringPool().getIndex(Pathname);93}94 95void LVElement::setName(StringRef ElementName) {96 // In the case of Root or Compile Unit, get index for the flatted out name.97 NameIndex = getTransformName() ? getStringIndex(ElementName)98 : getStringPool().getIndex(ElementName);99}100 101void LVElement::setFilename(StringRef Filename) {102 // Get index for the flattened out filename.103 FilenameIndex = getStringIndex(Filename);104}105 106void LVElement::setInnerComponent(StringRef Name) {107 if (Name.size()) {108 StringRef InnerComponent;109 std::tie(std::ignore, InnerComponent) = getInnerComponent(Name);110 setName(InnerComponent);111 }112}113 114// Return the string representation of a DIE offset.115std::string LVElement::typeOffsetAsString() const {116 if (options().getAttributeOffset()) {117 LVElement *Element = getType();118 return hexSquareString(Element ? Element->getOffset() : 0);119 }120 return {};121}122 123StringRef LVElement::accessibilityString(uint32_t Access) const {124 uint32_t Value = getAccessibilityCode();125 switch (Value ? Value : Access) {126 case dwarf::DW_ACCESS_public:127 return "public";128 case dwarf::DW_ACCESS_protected:129 return "protected";130 case dwarf::DW_ACCESS_private:131 return "private";132 default:133 return StringRef();134 }135}136 137std::optional<uint32_t> LVElement::getAccessibilityCode(MemberAccess Access) {138 switch (Access) {139 case MemberAccess::Private:140 return dwarf::DW_ACCESS_private;141 case MemberAccess::Protected:142 return dwarf::DW_ACCESS_protected;143 case MemberAccess::Public:144 return dwarf::DW_ACCESS_public;145 default:146 return std::nullopt;147 }148}149 150StringRef LVElement::externalString() const {151 return getIsExternal() ? "extern" : StringRef();152}153 154StringRef LVElement::inlineCodeString(uint32_t Code) const {155 uint32_t Value = getInlineCode();156 switch (Value ? Value : Code) {157 case dwarf::DW_INL_not_inlined:158 return "not_inlined";159 case dwarf::DW_INL_inlined:160 return "inlined";161 case dwarf::DW_INL_declared_not_inlined:162 return "declared_not_inlined";163 case dwarf::DW_INL_declared_inlined:164 return "declared_inlined";165 default:166 return StringRef();167 }168}169 170StringRef LVElement::virtualityString(uint32_t Virtuality) const {171 uint32_t Value = getVirtualityCode();172 switch (Value ? Value : Virtuality) {173 case dwarf::DW_VIRTUALITY_none:174 return StringRef();175 case dwarf::DW_VIRTUALITY_virtual:176 return "virtual";177 case dwarf::DW_VIRTUALITY_pure_virtual:178 return "pure virtual";179 default:180 return StringRef();181 }182}183 184std::optional<uint32_t> LVElement::getVirtualityCode(MethodKind Virtuality) {185 switch (Virtuality) {186 case MethodKind::Virtual:187 return dwarf::DW_VIRTUALITY_virtual;188 case MethodKind::PureVirtual:189 return dwarf::DW_VIRTUALITY_pure_virtual;190 case MethodKind::IntroducingVirtual:191 case MethodKind::PureIntroducingVirtual:192 // No direct equivalents in DWARF. Assume Virtual.193 return dwarf::DW_VIRTUALITY_virtual;194 default:195 return std::nullopt;196 }197}198 199void LVElement::resolve() {200 if (getIsResolved())201 return;202 setIsResolved();203 204 resolveReferences();205 resolveParents();206 resolveExtra();207 resolveName();208}209 210// Set File/Line using the specification element.211void LVElement::setFileLine(LVElement *Specification) {212 // In the case of inlined functions, the correct scope must be associated213 // with the file and line information of the outline version.214 if (!isLined()) {215 setLineNumber(Specification->getLineNumber());216 setIsLineFromReference();217 }218 if (!isFiled()) {219 setFilenameIndex(Specification->getFilenameIndex());220 setIsFileFromReference();221 }222}223 224void LVElement::resolveName() {225 // Set the qualified name if requested.226 if (options().getAttributeQualified())227 resolveQualifiedName();228 229 setIsResolvedName();230}231 232// Resolve any parents.233void LVElement::resolveParents() {234 if (isRoot() || isCompileUnit())235 return;236 237 LVScope *Parent = getParentScope();238 if (Parent && !Parent->getIsCompileUnit())239 Parent->resolve();240}241 242// Generate a name for unnamed elements.243void LVElement::generateName(std::string &Prefix) const {244 LVScope *Scope = getParentScope();245 if (!Scope)246 return;247 248 // Use its parent name and any line information.249 Prefix.append(std::string(Scope->getName()));250 Prefix.append("::");251 Prefix.append(isLined() ? lineNumberAsString(/*ShowZero=*/true) : "?");252 253 // Remove any whitespaces.254 llvm::erase_if(Prefix, ::isspace);255}256 257// Generate a name for unnamed elements.258void LVElement::generateName() {259 setIsAnonymous();260 std::string Name;261 generateName(Name);262 setName(Name);263 setIsGeneratedName();264}265 266void LVElement::updateLevel(LVScope *Parent, bool Moved) {267 setLevel(Parent->getLevel() + 1);268 if (Moved)269 setHasMoved();270}271 272// Generate the full name for the element, to include special qualifiers.273void LVElement::resolveFullname(LVElement *BaseType, StringRef Name) {274 // For the following sample code,275 // void *p;276 // some compilers do not generate an attribute for the associated type:277 // DW_TAG_variable278 // DW_AT_name 'p'279 // DW_AT_type $1280 // ...281 // $1: DW_TAG_pointer_type282 // ...283 // For those cases, generate the implicit 'void' type.284 StringRef BaseTypename = BaseType ? BaseType->getName() : emptyString();285 bool GetBaseTypename = false;286 bool UseBaseTypename = true;287 bool UseNameText = true;288 289 switch (getTag()) {290 case dwarf::DW_TAG_pointer_type: // "*";291 if (!BaseType)292 BaseTypename = typeVoid();293 break;294 case dwarf::DW_TAG_const_type: // "const"295 case dwarf::DW_TAG_ptr_to_member_type: // "*"296 case dwarf::DW_TAG_rvalue_reference_type: // "&&"297 case dwarf::DW_TAG_reference_type: // "&"298 case dwarf::DW_TAG_restrict_type: // "restrict"299 case dwarf::DW_TAG_volatile_type: // "volatile"300 case dwarf::DW_TAG_unaligned: // "unaligned"301 break;302 case dwarf::DW_TAG_base_type:303 case dwarf::DW_TAG_compile_unit:304 case dwarf::DW_TAG_class_type:305 case dwarf::DW_TAG_enumerator:306 case dwarf::DW_TAG_namespace:307 case dwarf::DW_TAG_skeleton_unit:308 case dwarf::DW_TAG_structure_type:309 case dwarf::DW_TAG_union_type:310 case dwarf::DW_TAG_unspecified_type:311 case dwarf::DW_TAG_GNU_template_parameter_pack:312 GetBaseTypename = true;313 break;314 case dwarf::DW_TAG_array_type:315 case dwarf::DW_TAG_call_site:316 case dwarf::DW_TAG_entry_point:317 case dwarf::DW_TAG_enumeration_type:318 case dwarf::DW_TAG_GNU_call_site:319 case dwarf::DW_TAG_imported_module:320 case dwarf::DW_TAG_imported_declaration:321 case dwarf::DW_TAG_inlined_subroutine:322 case dwarf::DW_TAG_label:323 case dwarf::DW_TAG_subprogram:324 case dwarf::DW_TAG_subrange_type:325 case dwarf::DW_TAG_subroutine_type:326 case dwarf::DW_TAG_typedef:327 GetBaseTypename = true;328 UseBaseTypename = false;329 break;330 case dwarf::DW_TAG_template_type_parameter:331 case dwarf::DW_TAG_template_value_parameter:332 UseBaseTypename = false;333 break;334 case dwarf::DW_TAG_GNU_template_template_param:335 break;336 case dwarf::DW_TAG_catch_block:337 case dwarf::DW_TAG_lexical_block:338 case dwarf::DW_TAG_try_block:339 UseNameText = false;340 break;341 default:342 llvm_unreachable("Invalid type.");343 return;344 break;345 }346 347 // Overwrite if no given value. 'Name' is empty when resolving for scopes348 // and symbols. In the case of types, it represents the type base name.349 if (Name.empty() && GetBaseTypename)350 Name = getName();351 352 // Concatenate the elements to get the full type name.353 // Type will be: base_parent + pre + base + parent + post.354 std::string Fullname;355 356 if (UseNameText && Name.size())357 Fullname.append(std::string(Name));358 if (UseBaseTypename && BaseTypename.size()) {359 if (UseNameText && Name.size())360 Fullname.append(" ");361 Fullname.append(std::string(BaseTypename));362 }363 364 // For a better and consistent layout, check if the generated name365 // contains double space sequences.366 assert((Fullname.find(" ", 0) == std::string::npos) &&367 "Extra double spaces in name.");368 369 LLVM_DEBUG({ dbgs() << "Fullname = '" << Fullname << "'\n"; });370 setName(Fullname);371}372 373void LVElement::setFile(LVElement *Reference) {374 if (!options().getAttributeAnySource())375 return;376 377 // At this point, any existing reference to another element, have been378 // resolved and the file ID extracted from the DI entry.379 if (Reference)380 setFileLine(Reference);381 382 // The file information is used to show the source file for any element383 // and display any new source file in relation to its parent element.384 // a) Elements that are not inlined.385 // - We record the DW_AT_decl_line and DW_AT_decl_file.386 // b) Elements that are inlined.387 // - We record the DW_AT_decl_line and DW_AT_decl_file.388 // - We record the DW_AT_call_line and DW_AT_call_file.389 // For both cases, we use the DW_AT_decl_file value to detect any changes390 // in the source filename containing the element. Changes on this value391 // indicates that the element being printed is not contained in the392 // previous printed filename.393 394 // The source files are indexed starting at 0, but DW_AT_decl_file defines395 // that 0 means no file; a value of 1 means the 0th entry.396 size_t Index = 0;397 398 // An element with no source file information will use the reference399 // attribute (DW_AT_specification, DW_AT_abstract_origin, DW_AT_extension)400 // to update its information.401 if (getIsFileFromReference() && Reference) {402 Index = Reference->getFilenameIndex();403 if (Reference->getInvalidFilename())404 setInvalidFilename();405 setFilenameIndex(Index);406 return;407 }408 409 // The source files are indexed starting at 0, but DW_AT_decl_file410 // defines that 0 means no file; a value of 1 means the 0th entry.411 Index = getFilenameIndex();412 if (Index) {413 StringRef Filename = getReader().getFilename(this, Index);414 Filename.size() ? setFilename(Filename) : setInvalidFilename();415 }416}417 418LVScope *LVElement::traverseParents(LVScopeGetFunction GetFunction) const {419 LVScope *Parent = getParentScope();420 while (Parent && !(Parent->*GetFunction)())421 Parent = Parent->getParentScope();422 return Parent;423}424 425LVScope *LVElement::getFunctionParent() const {426 return traverseParents(&LVScope::getIsFunction);427}428 429LVScope *LVElement::getCompileUnitParent() const {430 return traverseParents(&LVScope::getIsCompileUnit);431}432 433// Resolve the qualified name to include the parent hierarchy names.434void LVElement::resolveQualifiedName() {435 if (!getIsReferencedType() || isBase() || getQualifiedResolved() ||436 !getIncludeInPrint())437 return;438 439 std::string Name;440 441 // Get the qualified name, excluding the Compile Unit.442 LVScope *Parent = getParentScope();443 if (Parent && !Parent->getIsRoot()) {444 while (Parent && !Parent->getIsCompileUnit()) {445 Name.insert(0, "::");446 if (Parent->isNamed())447 Name.insert(0, std::string(Parent->getName()));448 else {449 std::string Temp;450 Parent->generateName(Temp);451 Name.insert(0, Temp);452 }453 Parent = Parent->getParentScope();454 }455 }456 457 if (Name.size()) {458 setQualifiedName(Name);459 setQualifiedResolved();460 }461 LLVM_DEBUG({462 dbgs() << "Offset: " << hexSquareString(getOffset())463 << ", Kind: " << formattedKind(kind())464 << ", Name: " << formattedName(getName())465 << ", QualifiedName: " << formattedName(Name) << "\n";466 });467}468 469bool LVElement::referenceMatch(const LVElement *Element) const {470 return (getHasReference() && Element->getHasReference()) ||471 (!getHasReference() && !Element->getHasReference());472}473 474bool LVElement::equals(const LVElement *Element) const {475 // The minimum factors that must be the same for an equality are:476 // line number, level, name, qualified name and filename.477 LLVM_DEBUG({478 dbgs() << "\n[Element::equals]\n";479 if (options().getAttributeOffset()) {480 dbgs() << "Reference: " << hexSquareString(getOffset()) << "\n";481 dbgs() << "Target : " << hexSquareString(Element->getOffset()) << "\n";482 }483 dbgs() << "Reference: "484 << "Kind = " << formattedKind(kind()) << ", "485 << "Name = " << formattedName(getName()) << ", "486 << "Qualified = " << formattedName(getQualifiedName()) << "\n"487 << "Target : "488 << "Kind = " << formattedKind(Element->kind()) << ", "489 << "Name = " << formattedName(Element->getName()) << ", "490 << "Qualified = " << formattedName(Element->getQualifiedName())491 << "\n"492 << "Reference: "493 << "NameIndex = " << getNameIndex() << ", "494 << "QualifiedNameIndex = " << getQualifiedNameIndex() << ", "495 << "FilenameIndex = " << getFilenameIndex() << "\n"496 << "Target : "497 << "NameIndex = " << Element->getNameIndex() << ", "498 << "QualifiedNameIndex = " << Element->getQualifiedNameIndex()499 << ", "500 << "FilenameIndex = " << Element->getFilenameIndex() << "\n";501 });502 if ((getLineNumber() != Element->getLineNumber()) ||503 (getLevel() != Element->getLevel()))504 return false;505 506 if ((getQualifiedNameIndex() != Element->getQualifiedNameIndex()) ||507 (getNameIndex() != Element->getNameIndex()) ||508 (getFilenameIndex() != Element->getFilenameIndex()))509 return false;510 511 if (!getType() && !Element->getType())512 return true;513 if (getType() && Element->getType())514 return getType()->equals(Element->getType());515 return false;516}517 518// Print the FileName Index.519void LVElement::printFileIndex(raw_ostream &OS, bool Full) const {520 if (options().getPrintFormatting() && options().getAttributeAnySource() &&521 getFilenameIndex()) {522 523 // Check if there is a change in the File ID sequence.524 size_t Index = getFilenameIndex();525 if (options().changeFilenameIndex(Index)) {526 // Just to keep a nice layout.527 OS << "\n";528 printAttributes(OS, /*Full=*/false);529 530 OS << " {Source} ";531 if (getInvalidFilename())532 OS << format("[0x%08x]\n", Index);533 else534 OS << formattedName(getPathname()) << "\n";535 }536 }537}538 539void LVElement::printReference(raw_ostream &OS, bool Full,540 LVElement *Parent) const {541 if (options().getPrintFormatting() && options().getAttributeReference())542 printAttributes(OS, Full, "{Reference} ", Parent,543 referenceAsString(getLineNumber(), /*Spaces=*/false),544 /*UseQuotes=*/false, /*PrintRef=*/true);545}546 547void LVElement::printLinkageName(raw_ostream &OS, bool Full,548 LVElement *Parent) const {549 if (options().getPrintFormatting() && options().getAttributeLinkage()) {550 printAttributes(OS, Full, "{Linkage} ", Parent, getLinkageName(),551 /*UseQuotes=*/true, /*PrintRef=*/false);552 }553}554 555void LVElement::printLinkageName(raw_ostream &OS, bool Full, LVElement *Parent,556 LVScope *Scope) const {557 if (options().getPrintFormatting() && options().getAttributeLinkage()) {558 LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope);559 std::string Text = (Twine(" 0x") + Twine::utohexstr(SectionIndex) +560 Twine(" '") + Twine(getLinkageName()) + Twine("'"))561 .str();562 printAttributes(OS, Full, "{Linkage} ", Parent, Text,563 /*UseQuotes=*/false, /*PrintRef=*/false);564 }565}566