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