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/LVType.h" |
17 | |
18 | using namespace llvm; |
19 | using namespace llvm::codeview; |
20 | using namespace llvm::logicalview; |
21 | |
22 | #define DEBUG_TYPE "Element" |
23 | |
24 | LVElementDispatch LVElement::Dispatch = { |
25 | {LVElementKind::Discarded, &LVElement::getIsDiscarded}, |
26 | {LVElementKind::Global, &LVElement::getIsGlobalReference}, |
27 | {LVElementKind::Optimized, &LVElement::getIsOptimized}}; |
28 | |
29 | LVType *LVElement::getTypeAsType() const { |
30 | return ElementType && ElementType->getIsType() |
31 | ? static_cast<LVType *>(ElementType) |
32 | : nullptr; |
33 | } |
34 | |
35 | LVScope *LVElement::getTypeAsScope() const { |
36 | return ElementType && ElementType->getIsScope() |
37 | ? static_cast<LVScope *>(ElementType) |
38 | : nullptr; |
39 | } |
40 | |
41 | // Set the element type. |
42 | void 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 | } else |
54 | setType(Element); |
55 | } |
56 | |
57 | // Discriminator as string. |
58 | std::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. |
68 | StringRef LVElement::typeAsString() const { |
69 | return getHasType() ? getTypeName() : typeVoid(); |
70 | } |
71 | |
72 | // Get name for element type. |
73 | StringRef LVElement::getTypeName() const { |
74 | return ElementType ? ElementType->getName() : StringRef(); |
75 | } |
76 | |
77 | static size_t getStringIndex(StringRef Name) { |
78 | // Convert the name to Unified format ('\' have been converted into '/'). |
79 | std::string Pathname(transformPath(Path: Name)); |
80 | |
81 | // Depending on the --attribute=filename and --attribute=pathname command |
82 | // 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(C: '/'); |
87 | if (Pos != std::string::npos) |
88 | Basename = Basename.substr(Start: Pos + 1); |
89 | return getStringPool().getIndex(Key: Basename); |
90 | } |
91 | |
92 | return getStringPool().getIndex(Key: Pathname); |
93 | } |
94 | |
95 | void LVElement::setName(StringRef ElementName) { |
96 | // In the case of Root or Compile Unit, get index for the flatted out name. |
97 | NameIndex = getTransformName() ? getStringIndex(Name: ElementName) |
98 | : getStringPool().getIndex(Key: ElementName); |
99 | } |
100 | |
101 | void LVElement::setFilename(StringRef Filename) { |
102 | // Get index for the flattened out filename. |
103 | FilenameIndex = getStringIndex(Name: Filename); |
104 | } |
105 | |
106 | void LVElement::setInnerComponent(StringRef Name) { |
107 | if (Name.size()) { |
108 | StringRef InnerComponent; |
109 | std::tie(args: std::ignore, args&: InnerComponent) = getInnerComponent(Name); |
110 | setName(InnerComponent); |
111 | } |
112 | } |
113 | |
114 | // Return the string representation of a DIE offset. |
115 | std::string LVElement::typeOffsetAsString() const { |
116 | if (options().getAttributeOffset()) { |
117 | LVElement *Element = getType(); |
118 | return hexSquareString(Value: Element ? Element->getOffset() : 0); |
119 | } |
120 | return {}; |
121 | } |
122 | |
123 | StringRef 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 | |
137 | std::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 | |
150 | StringRef LVElement::externalString() const { |
151 | return getIsExternal() ? "extern" : StringRef(); |
152 | } |
153 | |
154 | StringRef 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 | |
170 | StringRef 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 | |
184 | std::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 | |
199 | void 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. |
211 | void LVElement::setFileLine(LVElement *Specification) { |
212 | // In the case of inlined functions, the correct scope must be associated |
213 | // 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 | |
224 | void LVElement::resolveName() { |
225 | // Set the qualified name if requested. |
226 | if (options().getAttributeQualified()) |
227 | resolveQualifiedName(); |
228 | |
229 | setIsResolvedName(); |
230 | } |
231 | |
232 | // Resolve any parents. |
233 | void 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. |
243 | void 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(str: std::string(Scope->getName())); |
250 | Prefix.append(s: "::" ); |
251 | Prefix.append(str: isLined() ? lineNumberAsString(/*ShowZero=*/true) : "?" ); |
252 | |
253 | // Remove any whitespaces. |
254 | llvm::erase_if(C&: Prefix, P: ::isspace); |
255 | } |
256 | |
257 | // Generate a name for unnamed elements. |
258 | void LVElement::generateName() { |
259 | setIsAnonymous(); |
260 | std::string Name; |
261 | generateName(Prefix&: Name); |
262 | setName(Name); |
263 | setIsGeneratedName(); |
264 | } |
265 | |
266 | void 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. |
273 | void 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_variable |
278 | // DW_AT_name 'p' |
279 | // DW_AT_type $1 |
280 | // ... |
281 | // $1: DW_TAG_pointer_type |
282 | // ... |
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 scopes |
348 | // 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(str: std::string(Name)); |
358 | if (UseBaseTypename && BaseTypename.size()) { |
359 | if (UseNameText && Name.size()) |
360 | Fullname.append(s: " " ); |
361 | Fullname.append(str: std::string(BaseTypename)); |
362 | } |
363 | |
364 | // For a better and consistent layout, check if the generated name |
365 | // 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 | |
373 | void LVElement::setFile(LVElement *Reference) { |
374 | if (!options().getAttributeAnySource()) |
375 | return; |
376 | |
377 | // At this point, any existing reference to another element, have been |
378 | // 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 element |
383 | // 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 changes |
390 | // in the source filename containing the element. Changes on this value |
391 | // indicates that the element being printed is not contained in the |
392 | // previous printed filename. |
393 | |
394 | // The source files are indexed starting at 0, but DW_AT_decl_file defines |
395 | // 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 reference |
399 | // 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_file |
410 | // 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(Object: this, Index); |
414 | Filename.size() ? setFilename(Filename) : setInvalidFilename(); |
415 | } |
416 | } |
417 | |
418 | LVScope *LVElement::traverseParents(LVScopeGetFunction GetFunction) const { |
419 | LVScope *Parent = getParentScope(); |
420 | while (Parent && !(Parent->*GetFunction)()) |
421 | Parent = Parent->getParentScope(); |
422 | return Parent; |
423 | } |
424 | |
425 | LVScope *LVElement::getFunctionParent() const { |
426 | return traverseParents(GetFunction: &LVScope::getIsFunction); |
427 | } |
428 | |
429 | LVScope *LVElement::getCompileUnitParent() const { |
430 | return traverseParents(GetFunction: &LVScope::getIsCompileUnit); |
431 | } |
432 | |
433 | // Resolve the qualified name to include the parent hierarchy names. |
434 | void 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(pos: 0, s: "::" ); |
446 | if (Parent->isNamed()) |
447 | Name.insert(pos1: 0, str: std::string(Parent->getName())); |
448 | else { |
449 | std::string Temp; |
450 | Parent->generateName(Prefix&: Temp); |
451 | Name.insert(pos1: 0, str: 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 | |
469 | bool LVElement::referenceMatch(const LVElement *Element) const { |
470 | return (getHasReference() && Element->getHasReference()) || |
471 | (!getHasReference() && !Element->getHasReference()); |
472 | } |
473 | |
474 | bool 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: Element->getType()); |
515 | return false; |
516 | } |
517 | |
518 | // Print the FileName Index. |
519 | void 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(Fmt: "[0x%08x]\n" , Vals: Index); |
533 | else |
534 | OS << formattedName(Name: getPathname()) << "\n" ; |
535 | } |
536 | } |
537 | } |
538 | |
539 | void LVElement::printReference(raw_ostream &OS, bool Full, |
540 | LVElement *Parent) const { |
541 | if (options().getPrintFormatting() && options().getAttributeReference()) |
542 | printAttributes(OS, Full, Name: "{Reference} " , Parent, |
543 | Value: referenceAsString(LineNumber: getLineNumber(), /*Spaces=*/false), |
544 | /*UseQuotes=*/false, /*PrintRef=*/true); |
545 | } |
546 | |
547 | void LVElement::printLinkageName(raw_ostream &OS, bool Full, |
548 | LVElement *Parent) const { |
549 | if (options().getPrintFormatting() && options().getAttributeLinkage()) { |
550 | printAttributes(OS, Full, Name: "{Linkage} " , Parent, Value: getLinkageName(), |
551 | /*UseQuotes=*/true, /*PrintRef=*/false); |
552 | } |
553 | } |
554 | |
555 | void 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(Val: SectionIndex) + |
560 | Twine(" '" ) + Twine(getLinkageName()) + Twine("'" )) |
561 | .str(); |
562 | printAttributes(OS, Full, Name: "{Linkage} " , Parent, Value: Text, |
563 | /*UseQuotes=*/false, /*PrintRef=*/false); |
564 | } |
565 | } |
566 | |