Index: llvm/docs/CommandGuide/index.rst =================================================================== --- llvm/docs/CommandGuide/index.rst +++ llvm/docs/CommandGuide/index.rst @@ -21,6 +21,7 @@ llvm-config llvm-cov llvm-cxxmap + llvm-debuginfo-analyzer llvm-diff llvm-dis llvm-dwarfdump Index: llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst =================================================================== --- /dev/null +++ llvm/docs/CommandGuide/llvm-debuginfo-analyzer.rst @@ -0,0 +1,1962 @@ +llvm-debuginfo-analyzer - Print a logical representation of low-level debug information. +======================================================================================== + +.. program:: llvm-debuginfo-analyzer + +.. contents:: + :local: + +SYNOPSIS +-------- +:program:`llvm-debuginfo-analyzer` [*options*] [*filename ...*] + +DESCRIPTION +----------- +:program:`llvm-debuginfo-analyzer` parses debug and text sections in +binary object files and prints their contents in a logical view, which +is a human readable representation that closely matches the structure +of the original user source code. Supported object file formats include +ELF, Mach-O, PDB and COFF. + +The **logical view** abstracts the complexity associated with the +different low-level representations of the debugging information that +is embedded in the object file. :program:`llvm-debuginfo-analyzer` +produces a canonical view of the debug information regardless of how it +is formatted. The same logical view will be seen regardless of object +file format, assuming the debug information correctly represents the +same original source code. + +The logical view includes the following **logical elements**: *type*, +*scope*, *symbol* and *line*, which are the basic software elements used +in the C/C++ programming language. Each logical element has a set of +**attributes**, such as *types*, *classes*, *functions*, *variables*, +*parameters*, etc. The :option:`--attribute` can be used to specify which +attributes to include when printing a logical element. A logical element +may have a **kind** that describes specific types of elements. For +instance, a *scope* could have a kind value of *function*, *class*, +*namespace*. + +:program:`llvm-debuginfo-analyzer` defaults to print a pre-defined +layout of logical elements and attributes. The command line options can +be used to control the printed elements (:option:`--print`), using a +specific layout (:option:`--report`), matching a given pattern +(:option:`--select`, :option:`--select-offsets`). Also, the output can +be limited to specified logical elements using (:option:`--select-lines`, +:option:`--select-scopes`, :option:`--select-symbols`, +:option:`--select-types`). + +:program:`llvm-debuginfo-analyzer` can also compare a set of logical +views (:option:`--compare`), to find differences and identify possible +debug information syntax issues (:option:`--warning`) in any object file. + +OPTIONS +------- +:program:`llvm-debuginfo-analyzer` options are separated into several +categories, each tailored to a different purpose: + + * :ref:`general_` - Standard LLVM options to display help, version, etc. + * :ref:`attributes_` - Describe how to include different details when + printing an element. + * :ref:`print_` - Specify which elements will be included when printing + the view. + * :ref:`output_` - Describe the supported formats when printing the view. + * :ref:`report_` - Describe the format layouts for view printing. + * :ref:`select_` - Allows to use specific criteria or conditions to + select which elements to print. + * :ref:`compare_` - Compare logical views and print missing and/or + added elements. + * :ref:`warning_` - Print the warnings detected during the creation + of the view. + * :ref:`internal_` - Internal analysis of the logical view. + +.. _general_: + +GENERAL +~~~~~~~ +This section describes the standard help options, used to display the +usage, version, response files, etc. + +.. option:: -h, --help + + Show help and usage for this command. (--help-hidden for more). + +.. option:: --help-list + + Show help and usage for this command without grouping the options + into categories (--help-list-hidden for more). + +.. option:: --help-hidden + + Display all available options. + +.. option:: --print-all-options + + Print all option values after command line parsing. + +.. option:: --print-options + + Print non-default options after command line parsing + +.. option:: --version + + Display the version of the tool. + +.. option:: @ + + Read command-line options from ``. + +If no input file is specified, :program:`llvm-debuginfo-analyzer` +defaults to read `a.out` and return an error when no input file is found. + +If `-` is used as the input file, :program:`llvm-debuginfo-analyzer` +reads the input from its standard input stream. + +.. _attributes_: + +ATTRIBUTES +~~~~~~~~~~ +The following options enable attributes given for the printed elements. +The attributes are divided in categories based on the type of data being +added, such as: internal offsets in the binary file, location descriptors, +register names, user source filenames, additional element transformations, +toolchain name, binary file format, etc. + +.. option:: --attribute= + + With **value** being one the options in the following lists. + + .. code-block:: text + + =all: Include all the below attributes. + =extended: Add low-level attributes. + =standard: Add standard high-level attributes. + + The following attributes describe the most common information for a + logical element. They help to identify the lexical scope level; the + element visibility across modules (global, local); the toolchain name + that produced the binary file. + + .. code-block:: text + + =global: Element referenced across Compile Units. + =format: Object file format name. + =level: Lexical scope level (File=0, Compile Unit=1). + =local: Element referenced only in the Compile Unit. + =producer: Toolchain identification name. + + The following attributes describe files and directory names from the + user source code, where the elements are declared or defined; functions + with public visibility across modules. These options allow to map the + elements to their user code location, for cross references purposes. + + .. code-block:: text + + =directories: Directories referenced in the debug information. + =filename: Filename where the element is defined. + =files: Files referenced in the debug information. + =pathname: Pathname where the object is defined. + =publics: Function names that are public. + + The following attributes describe additional logical element source + transformations, in order to display built-in types (int, bool, etc.); + parameters and arguments used during template instantiation; parent + name hierarchy; array dimensions information; compiler generated + elements and the underlying types associated with the types aliases. + + .. code-block:: text + + =argument: Template parameters replaced by its arguments. + =base: Base types (int, bool, etc.). + =generated: Compiler generated elements. + =encoded: Template arguments encoded in the template name. + =qualified: The element type include parents in its name. + =reference: Element declaration and definition references. + =subrange: Subrange encoding information for arrays. + =typename: Template parameters. + =underlying: Underlying type for type definitions. + + The following attributes describe the debug location information for + a symbol or scope. It includes the symbol percentage coverage and any + gaps within the location layout; ranges determining the code sections + attached to a function. When descriptors are used, the target processor + registers are displayed. + + .. code-block:: text + + =coverage: Symbol location coverage. + =gaps: Missing debug location (gaps). + =location: Symbol debug location. + =range: Debug location ranges. + =register: Processor register names. + + The following attributes are associated with low level details, such + as: offsets in the binary file; discriminators added to the lines of + inlined functions in order to distinguish specific instances; debug + lines state machine registers; elements discarded by the compiler + (inlining) or by the linker optimizations (dead-stripping); system + compile units generated by the MS toolchain in PDBs. + + .. code-block:: text + + =discarded: Discarded elements by the linker. + =discriminator: Discriminators for inlined function instances. + =inserted: Generated inlined abstract references. + =linkage: Object file linkage name. + =offset: Debug information offset. + =qualifier: Line qualifiers (Newstatement, BasicBlock, etc). + =zero: Zero line numbers. + + The following attribute described specific information for the **PE/COFF** + file format. It includes MS runtime types. + + .. code-block:: text + + =system: Display PDB's MS system elements. + + The above attributes are grouped into *standard* and *extended* + categories that can be enabled. + + The *standard* group, contains those attributes that add sufficient + information to describe a logical element and that can cover the + normal situations while dealing with debug information. + + .. code-block:: text + + =base + =coverage + =directories + =discriminator + =filename + =files + =format + =level + =producer + =publics + =range + =reference + =zero + + The *extended* group, contains those attributes that require a more + extended knowledge about debug information. They are intended when a + lower level of detail is required. + + .. code-block:: text + + =argument + =discarded + =encoded + =gaps + =generated + =global + =inserted + =linkage + =local + =location + =offset + =operation + =pathname + =qualified + =qualifier + =register + =subrange + =system + =typename + +.. _print_: + +PRINT +~~~~~ +The following options describe the elements to print. The layout used +is determined by the :option:`--report`. In the tree layout, all the +elements have their enclosing lexical scopes printed, even when not +explicitly specified. + +.. option:: --print= + + With **value** being one the options in the following lists. + + .. code-block:: text + + =all: Include all the below attributes. + + The following options print the requested elements; in the case of any + given select conditions (:option:`--select`), only those elements that + match them, will be printed. The **elements** value is a convenient + way to specify instructions, lines, scopes, symbols and types all at + once. + + .. code-block:: text + + =elements: Instructions, lines, scopes, symbols and types. + =instructions: Assembler instructions for code sections. + =lines: Source lines referenced in the debug information. + =scopes: Lexical blocks (function, class, namespace, etc). + =symbols: Symbols (variable, member, parameter, etc). + =types: Types (pointer, reference, type alias, etc). + + The following options print information, collected during the creation + of the elements, such as: scope contributions to the debug information; + summary of elements created, printed or matched (:option:`--select`); + warnings produced during the view creation. + + .. code-block:: text + + =sizes: Debug Information scopes contributions. + =summary: Summary of elements allocated, selected or printed. + =warnings: Warnings detected. + + Note: The **--print=sizes** option is ELF specific. + +.. _output_: + +OUTPUT +~~~~~~ +The following options describe how to control the output generated when +printing the logical elements. + +.. option:: --output-file= + + Redirect the output to a file specified by , where - is the + standard output stream. + +:program:`llvm-debuginfo-analyzer` has the concept of **split view**. +When redirecting the output from a complex binary format, it is +**divided** into individual files, each one containing the logical view +output for a single compilation unit. + +.. option:: --output-folder= + + The folder to write a file per compilation unit when **--output=split** + is specified. + +.. option:: --output-level= + + Only print elements up to the given **lexical level** value. The input + file is at lexical level zero and a compilation unit is at lexical level + one. + +.. option:: --output= + + With **value** being one the options in the following lists. + + .. code-block:: text + + =all: Include all the below outputs. + + .. code-block:: text + + =json: Use JSON as the output format (Not implemented). + =split: Split the output by Compile Units. + =text: Use a free form text output. + +.. option:: --output-sort= + + Primary key when ordering the elements in the output (default: line). + Sorting by logical element kind, requires be familiarity with the + element kind selection options (:option:`--select-lines`, + :option:`--select-scopes`, :option:`--select-symbols`, + :option:`--select-types`), as those options describe the different + logical element kinds. + + .. code-block:: text + + =kind: Sort by element kind. + =line: Sort by element line number. + =name: Sort by element name. + =offset: Sort by element offset. + +.. _report_: + +REPORT +~~~~~~ +Depending on the task being executed (print, compare, select), several +layouts are supported to display the elements in a more suitable way, +to make the output easier to understand. + +.. option:: --report= + + With **value** being one the options in the following list. + + .. code-block:: text + + =all: Include all the below reports. + + .. code-block:: text + + =children: Elements and children are displayed in a tree format. + =list: Elements are displayed in a tabular format. + =parents: Elements and parents are displayed in a tree format. + =view: Elements, parents and children are displayed in a tree format. + +The **list** layout presents the logical elements in a tabular form +without any parent-child relationship. This may be the preferred way to +display elements that match specific conditions when comparing logical +views, making it easier to find differences. + +The **children**, **parents** and **view** layout displays the elements +in a tree format, with the scopes representing their nodes, and types, +symbols, lines and other scopes representing the children. The layout +shows the lexical scoping relationship between elements, with the binary +file being the tree root (level 0) and each compilation unit being a +child (level 1). + +The **children** layout includes the elements that match any given +criteria (:option:`--select`) or (:option:`--compare`) and its children. + +The **parents** layout includes the elements that match any given +criteria (:option:`--select`) or (:option:`--compare`) and its parents. + +The combined **view** layout includes the elements that match any given +criteria (:option:`--select`) or (:option:`--compare`), its parents +and children. + +**Notes**: + +1. When a selection criteria (:option:`--select`) is specified with no + report option, the **list** layout is selected. +2. The comparison mode always uses the **view** layout. + +.. _select_: + +SELECTION +~~~~~~~~~ +When printing an element, different data can be included and it varies +(:option:`--attribute`) from data directly associated with the binary +file (offset) to high level details such as coverage, lexical scope +level, location. As the printed output can reach a considerable size, +several selection options, enable printing of specific elements. + +The pattern matching can ignore the case (:option:`--select-nocase`) +and be extended to use regular expressions (:option:`--select-regex`). + +ELEMENTS +^^^^^^^^ +The following options allow printing of elements that match the given +, offset or an element . + +.. option:: --select= + + Print all elements whose name or line number matches the given . + +.. option:: --select-offsets= + + Print all elements whose offset matches the given values. See + :option:`--attribute` option. + +.. option:: --select-elements= + + Print all elements that satisfy the given . With **condition** + being one the options in the following list. + + .. code-block:: text + + =discarded: Discarded elements by the linker. + =global: Element referenced across Compile Units. + =optimized: Optimized inlined abstract references. + +.. option:: --select-nocase + + Pattern matching is case-insensitive when using :option:`--select`. + +.. option:: --select-regex + + Treat any strings as regular expressions when selecting with + :option:`--select` option. If :option:`--select-nocase` is specified, + the regular expression becomes case-insensitive. + +If the criteria is too general, a more selective option can +be specified to target a particular category of elements: +lines (:option:`--select-lines`), scopes (:option:`--select-scopes`), +symbols (:option:`--select-symbols`) and types (:option:`--select-types`). +These options, require knowledge of the debug information format (DWARF, +CodeView, COFF), as the given **kind** describes a very specific type +of element. + +LINES +^^^^^ +The following options allow printing of lines that match the given . +The given criteria describes the debug line state machine registers. + +.. option:: --select-lines= + + With **kind** being one the options in the following list. + + .. code-block:: text + + =AlwaysStepInto: marks an always step into. + =BasicBlock: Marks a new basic block. + =Discriminator: Line that has a discriminator. + =EndSequence: Marks the end in the sequence of lines. + =EpilogueBegin: Marks the start of a function epilogue. + =LineDebug: Lines that correspond to debug lines. + =LineAssembler: Lines that correspond to disassembly text. + =NeverStepInto: marks a never step into. + =NewStatement: Marks a new statement. + =PrologueEnd: Marks the end of a function prologue. + +SCOPES +^^^^^^ +The following options allow printing of scopes that match the given . + +.. option:: --select-scopes= + + With **kind** being one the options in the following list. + + .. code-block:: text + + =Aggregate: A class, structure or union. + =Array: An array. + =Block: A generic block (lexical block or exception block). + =CallSite: A call site. + =CatchBlock: An exception block. + =Class: A class. + =CompileUnit: A compile unit. + =EntryPoint: A subroutine entry point. + =Enumeration: An enumeration. + =Function: A function. + =FunctionType: A function pointer. + =InlinedFunction: An inlined function. + =Label: A label. + =LexicalBlock: A lexical block. + =Namespace: A namespace. + =Root: The element representing the main scope. + =Structure: A structure. + =Subprogram: A subprogram. + =Template: A template definition. + =TemplateAlias: A template alias. + =TemplatePack: A template pack. + =TryBlock: An exception try block. + =Union: A union. + +SYMBOLS +^^^^^^^ +The following options allow printing of symbols that match the given . + +.. option:: --select-symbols= + + With **kind** being one the options in the following list. + + .. code-block:: text + + =CallSiteParameter: A call site parameter. + =Constant: A constant symbol. + =Inheritance: A base class. + =Member: A member class. + =Parameter: A parameter to function. + =Unspecified: Unspecified parameters to function. + =Variable: A variable. + +TYPES +^^^^^ +The following options allow printing of types that match the given . + +.. option:: --select-types= + + With **kind** being one the options in the following list. + + .. code-block:: text + + =Base: Base type (integer, boolean, etc). + =Const: Constant specifier. + =Enumerator: Enumerator. + =Import: Import declaration. + =ImportDeclaration: Import declaration. + =ImportModule: Import module. + =Pointer: Pointer type. + =PointerMember: Pointer to member function. + =Reference: Reference type. + =Restrict: Restrict specifier. + =RvalueReference: R-value reference. + =Subrange: Array subrange. + =TemplateParam: Template parameter. + =TemplateTemplateParam: Template template parameter. + =TemplateTypeParam: Template type parameter. + =TemplateValueParam: Template value parameter. + =Typedef: Type definition. + =Unspecified: Unspecified type. + =Volatile: Volatile specifier. + +.. _compare_: + +COMPARE +~~~~~~~ +When dealing with debug information, there are situations when the +printing of the elements is not the correct approach. That is the case, +when we are interested in the effects caused by different versions of +the same toolchain, or the impact of specific compiler optimizations. + +For those cases, we are looking to see which elements have been added +or removed. Due to the complicated debug information format, it is very +difficult to use a regular diff tool to find those elements; even +impossible when dealing with different debug formats. + +:program:`llvm-debuginfo-analyzer` supports a logical element comparison, +allowing to find semantic differences between logical views, produced by +different toolchain versions or even debug information formats. + +When comparing logical views created from different debug formats, its +accuracy depends on how close the debug information represents the +user code. For instance, a logical view created from a binary file with +DWARF debug information may include more detailed data than a logical +view created from a binary file with CodeView/COFF debug information. + +The following options describe the elements to compare. + +.. option:: --compare= + + With **value** being one of the options in the following list. + + .. code-block:: text + + =all: Include all the below elements. + + .. code-block:: text + + =lines: Include lines. + =scopes: Include scopes. + =symbols: Include symbols. + =types: Include types. + +:program:`llvm-debuginfo-analyzer` takes the first binary file on the +command line as the **reference** and the second one as the **target**. +To get a more descriptive report, the comparison is done twice. The +reference and target views are swapped, in order to produce those +**missing** elements from the target view and those **added** elements +to the reference view. + +See :option:`--report` options on how to describe the comparison +reports. + +.. _warning_: + +WARNING +~~~~~~~ +When reading the input object files, :program:`llvm-debuginfo-analyzer` +can detected issues in the raw debug information. These may not be +considered fatal to the purpose of printing a logical view but they can +give an indication about the quality and potentially expose issues with +the generated debug information. + +The following options describe the warnings to be recorded for later +printing, if they are requested by :option:`--print`. + +.. option:: --warning= + + With **value** being one the options in the following list. + + .. code-block:: text + + =all: Include all the below warnings. + + The following options collect additional information during the creation + of the logical view, to include invalid coverage values and locations + for symbols; invalid code ranges; lines that are zero. + + .. code-block:: text + + =coverages: Invalid symbol coverages values. + =lines: Debug lines that are zero. + =locations: Invalid symbol locations. + =ranges: Invalid code ranges. + +.. _internal_: + +INTERNAL +~~~~~~~~ + For a better understanding of the logical view, access to more detailed + internal information is needed. Such data would help to identify debug + information processed or incorrect logical element management. Typically + these kind of options are available only in *debug* builds. + + :program:`llvm-debuginfo-analyzer` supports these advanced options in + both *release* and *debug* builds, with the exception of the unique ID + that is generated only in *debug* builds. + +.. option:: --internal= + + With **value** being one the options in the following list. + + .. code-block:: text + + =all: Include all the below options. + + The following options allow to check the integrity of the logical view; + collect the debug tags that are processed or not implemented; ignore the + logical element line number, to facilitate the logical view comparison + when using external comparison tools; print the command line options + used to invoke :program:`llvm-debuginfo-analyzer`. + + .. code-block:: text + + =id: Print unique element ID. + =cmdline: Print command line. + =integrity: Check elements integrity. + =none: Ignore element line number. + =tag: Debug information tags. + + **Note:** For ELF format, the collected tags represent the debug tags + that are not processed. For PE/COFF format, they represent the tags + that are processed. + +EXAMPLES +-------- +This section includes some real binary files to show how to use +:program:`llvm-debuginfo-analyzer` to print a logical view and to +diagnose possible debug information issues. + +TEST CASE 1 - GENERAL OPTIONS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The below example is used to show different output generated by +:program:`llvm-debuginfo-analyzer`. We compiled the example for an X86 +ELF target with a recent version of Clang (-O0 -g): + +.. code-block:: c++ + + 1 using INTPTR = const int *; + 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { + 3 if (ParamBool) { + 4 typedef int INTEGER; + 5 const INTEGER CONSTANT = 7; + 6 return CONSTANT; + 7 } + 8 return ParamUnsigned; + 9 } + +PRINTING MODE +^^^^^^^^^^^^^ +In this mode :program:`llvm-debuginfo-analyzer` prints the *logical view* +or portions of it, based on criteria patterns (including regular +expressions) to select the kind of *logical elements* to be included in +the output. + +BASIC DETAILS +""""""""""""" +The following command prints basic details for all the logical elements +sorted by the debug information internal offset; it includes its lexical +level and debug info format. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level,format + --output-sort=offset + --print=scopes,symbols,types,lines,instructions + test-dwarf-clang.o + +or + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level,format + --output-sort=offset + --print=elements + test-dwarf-clang.o + +Each row represents an element that is present within the debug +information. The first column represents the scope level, followed by +the associated line number (if any), and finally the description of +the element. + +.. code-block:: none + + Logical View: + [000] {File} 'test-dwarf-clang.o' -> elf64-x86-64 + + [001] {CompileUnit} 'test.cpp' + [002] 2 {Function} extern not_inlined 'foo' -> 'int' + [003] 2 {Parameter} 'ParamPtr' -> 'INTPTR' + [003] 2 {Parameter} 'ParamUnsigned' -> 'unsigned int' + [003] 2 {Parameter} 'ParamBool' -> 'bool' + [003] {Block} + [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER' + [004] 5 {Line} + [004] {Code} 'movl $0x7, -0x1c(%rbp)' + [004] 6 {Line} + [004] {Code} 'movl $0x7, -0x4(%rbp)' + [004] {Code} 'jmp 0x6' + [004] 8 {Line} + [004] {Code} 'movl -0x14(%rbp), %eax' + [003] 4 {TypeAlias} 'INTEGER' -> 'int' + [003] 2 {Line} + [003] {Code} 'pushq %rbp' + [003] {Code} 'movq %rsp, %rbp' + [003] {Code} 'movb %dl, %al' + [003] {Code} 'movq %rdi, -0x10(%rbp)' + [003] {Code} 'movl %esi, -0x14(%rbp)' + [003] {Code} 'andb $0x1, %al' + [003] {Code} 'movb %al, -0x15(%rbp)' + [003] 3 {Line} + [003] {Code} 'testb $0x1, -0x15(%rbp)' + [003] {Code} 'je 0x13' + [003] 8 {Line} + [003] {Code} 'movl %eax, -0x4(%rbp)' + [003] 9 {Line} + [003] {Code} 'movl -0x4(%rbp), %eax' + [003] {Code} 'popq %rbp' + [003] {Code} 'retq' + [003] 9 {Line} + [002] 1 {TypeAlias} 'INTPTR' -> '* const int' + +On closer inspection, we can see what could be a potential debug issue: + +.. code-block:: none + + [003] {Block} + [003] 4 {TypeAlias} 'INTEGER' -> 'int' + +The **'INTEGER'** definition is at level **[003]**, the same lexical +scope as the anonymous **{Block}** ('true' branch for the 'if' statement) +whereas in the original source code the typedef statement is clearly +inside that block, so the **'INTEGER'** definition should also be at +level **[004]** inside the block. + +SELECT LOGICAL ELEMENTS +""""""""""""""""""""""" +The following prints all *instructions*, *symbols* and *types* that +contain **'inte'** or **'movl'** in their names or types, using a tab +layout and given the number of matches. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level + --select-nocase --select-regex + --select=INTe --select=movl + --report=list + --print=symbols,types,instructions,summary + test-dwarf-clang.o + + Logical View: + [000] {File} 'test-dwarf-clang.o' + + [001] {CompileUnit} 'test.cpp' + [003] {Code} 'movl $0x7, -0x1c(%rbp)' + [003] {Code} 'movl $0x7, -0x4(%rbp)' + [003] {Code} 'movl %eax, -0x4(%rbp)' + [003] {Code} 'movl %esi, -0x14(%rbp)' + [003] {Code} 'movl -0x14(%rbp), %eax' + [003] {Code} 'movl -0x4(%rbp), %eax' + [003] 4 {TypeAlias} 'INTEGER' -> 'int' + [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER' + + ----------------------------- + Element Total Found + ----------------------------- + Scopes 3 0 + Symbols 4 1 + Types 2 1 + Lines 17 6 + ----------------------------- + Total 26 8 + +COMPARISON MODE +^^^^^^^^^^^^^^^ +In this mode :program:`llvm-debuginfo-analyzer` compares logical views +to produce a report with the logical elements that are missing or added. +This a very powerful aid in finding semantic differences in the debug +information produced by different toolchain versions or even completely +different toolchains altogether (For example a compiler producing DWARF +can be directly compared against a completely different compiler that +produces CodeView). + +Given the previous example we found the above debug information issue +(related to the previous invalid scope location for the **'typedef int +INTEGER'**) by comparing against another compiler. + +Using GCC to generate test-dwarf-gcc.o, we can apply a selection pattern +with the printing mode to obtain the following logical view output. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level + --select-regex --select-nocase --select=INTe + --report=list + --print=symbols,types + test-dwarf-clang.o test-dwarf-gcc.o + + Logical View: + [000] {File} 'test-dwarf-clang.o' + + [001] {CompileUnit} 'test.cpp' + [003] 4 {TypeAlias} 'INTEGER' -> 'int' + [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER' + + Logical View: + [000] {File} 'test-dwarf-gcc.o' + + [001] {CompileUnit} 'test.cpp' + [004] 4 {TypeAlias} 'INTEGER' -> 'int' + [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER' + +The output shows that both objects contain the same elements. But the +**'typedef INTEGER'** is located at different scope level. The GCC +generated object, shows **'4'**, which is the correct value. + +Note that there is no requirement that GCC must produce identical or +similar DWARF to Clang to allow the comparison. We're only comparing +the semantics. The same case when comparing CodeView debug information +generated by MSVC and Clang. + +There are 2 comparison methods: logical view and logical elements. + +LOGICAL VIEW +"""""""""""" +It compares the logical view as a whole unit; for a match, each compared +logical element must have the same parents and children. + +Using the :program:`llvm-debuginfo-analyzer` comparison functionality, +that issue can be seen in a more global context, that can include the +logical view. + +The output shows in view form the **missing (-), added (+)** elements, +giving more context by swapping the reference and target object files. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level + --compare=types + --report=view + --print=symbols,types + test-dwarf-clang.o test-dwarf-gcc.o + + Reference: 'test-dwarf-clang.o' + Target: 'test-dwarf-gcc.o' + + Logical View: + [000] {File} 'test-dwarf-clang.o' + + [001] {CompileUnit} 'test.cpp' + [002] 1 {TypeAlias} 'INTPTR' -> '* const int' + [002] 2 {Function} extern not_inlined 'foo' -> 'int' + [003] {Block} + [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER' + +[004] 4 {TypeAlias} 'INTEGER' -> 'int' + [003] 2 {Parameter} 'ParamBool' -> 'bool' + [003] 2 {Parameter} 'ParamPtr' -> 'INTPTR' + [003] 2 {Parameter} 'ParamUnsigned' -> 'unsigned int' + -[003] 4 {TypeAlias} 'INTEGER' -> 'int' + +The output shows the merging view path (reference and target) with the +missing and added elements. + +LOGICAL ELEMENTS +"""""""""""""""" +It compares individual logical elements without considering if their +parents are the same. For both comparison methods, the equal criteria +includes the name, source code location, type, lexical scope level. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level + --compare=types + --report=list + --print=symbols,types,summary + test-dwarf-clang.o test-dwarf-gcc.o + + Reference: 'test-dwarf-clang.o' + Target: 'test-dwarf-gcc.o' + + (1) Missing Types: + -[003] 4 {TypeAlias} 'INTEGER' -> 'int' + + (1) Added Types: + +[004] 4 {TypeAlias} 'INTEGER' -> 'int' + + ---------------------------------------- + Element Expected Missing Added + ---------------------------------------- + Scopes 4 0 0 + Symbols 0 0 0 + Types 2 1 1 + Lines 0 0 0 + ---------------------------------------- + Total 6 1 1 + +Changing the *Reference* and *Target* order: + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level + --compare=types + --report=list + --print=symbols,types,summary + test-dwarf-gcc.o test-dwarf-clang.o + + Reference: 'test-dwarf-gcc.o' + Target: 'test-dwarf-clang.o' + + (1) Missing Types: + -[004] 4 {TypeAlias} 'INTEGER' -> 'int' + + (1) Added Types: + +[003] 4 {TypeAlias} 'INTEGER' -> 'int' + + ---------------------------------------- + Element Expected Missing Added + ---------------------------------------- + Scopes 4 0 0 + Symbols 0 0 0 + Types 2 1 1 + Lines 0 0 0 + ---------------------------------------- + Total 6 1 1 + +As the *Reference* and *Target* are switched, the *Added Types* from +the first case now are listed as *Missing Types*. + +TEST CASE 2 - ASSEMBLER INSTRUCTIONS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The below example is used to show different output generated by +:program:`llvm-debuginfo-analyzer`. We compiled the example for an X86 +Codeview and ELF targets with recent versions of Clang, GCC and MSVC +(-O0 -g) for Windows and Linux. + +.. code-block:: c++ + + 1 extern int printf(const char * format, ... ); + 2 + 3 int main() + 4 { + 5 printf("Hello, World\n"); + 6 return 0; + 7 } + +These are the logical views that :program:`llvm-debuginfo-analyzer` +generates for 3 different compilers (MSVC, Clang and GCC), emitting +different debug information formats (CodeView, DWARF) on Windows and +Linux. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level,format,producer + --print=lines,instructions + hello-world-codeview-clang.o + hello-world-codeview-msvc.o + hello-world-dwarf-clang.o + hello-world-dwarf-gcc.o + +CodeView - Clang (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'hello-world-codeview-clang.o' -> COFF-x86-64 + + [001] {CompileUnit} 'hello-world.cpp' + [002] {Producer} 'clang version 14.0.0' + [002] {Function} extern not_inlined 'main' -> 'int' + [003] 4 {Line} + [003] {Code} 'subq $0x28, %rsp' + [003] {Code} 'movl $0x0, 0x24(%rsp)' + [003] 5 {Line} + [003] {Code} 'leaq (%rip), %rcx' + [003] {Code} 'callq 0x0' + [003] 6 {Line} + [003] {Code} 'xorl %eax, %eax' + [003] {Code} 'addq $0x28, %rsp' + [003] {Code} 'retq' + +CodeView - MSVC (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'hello-world-codeview-msvc.o' -> COFF-i386 + + [001] {CompileUnit} 'hello-world.cpp' + [002] {Producer} 'Microsoft (R) Optimizing Compiler' + [002] {Function} extern not_inlined 'main' -> 'int' + [003] 4 {Line} + [003] {Code} 'pushl %ebp' + [003] {Code} 'movl %esp, %ebp' + [003] 5 {Line} + [003] {Code} 'pushl $0x0' + [003] {Code} 'calll 0x0' + [003] {Code} 'addl $0x4, %esp' + [003] 6 {Line} + [003] {Code} 'xorl %eax, %eax' + [003] 7 {Line} + [003] {Code} 'popl %ebp' + [003] {Code} 'retl' + +DWARF - Clang (Linux) +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'hello-world-dwarf-clang.o' -> elf64-x86-64 + + [001] {CompileUnit} 'hello-world.cpp' + [002] {Producer} 'clang version 14.0.0' + [002] 3 {Function} extern not_inlined 'main' -> 'int' + [003] 4 {Line} + [003] {Code} 'pushq %rbp' + [003] {Code} 'movq %rsp, %rbp' + [003] {Code} 'subq $0x10, %rsp' + [003] {Code} 'movl $0x0, -0x4(%rbp)' + [003] 5 {Line} + [003] {Code} 'movabsq $0x0, %rdi' + [003] {Code} 'movb $0x0, %al' + [003] {Code} 'callq 0x0' + [003] 6 {Line} + [003] {Code} 'xorl %eax, %eax' + [003] {Code} 'addq $0x10, %rsp' + [003] {Code} 'popq %rbp' + [003] {Code} 'retq' + [003] 6 {Line} + +DWARF - GCC (Linux) +^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'hello-world-dwarf-gcc.o' -> elf64-x86-64 + + [001] {CompileUnit} 'hello-world.cpp' + [002] {Producer} 'GNU C++14 9.3.0' + [002] 3 {Function} extern not_inlined 'main' -> 'int' + [003] 4 {Line} + [003] {Code} 'endbr64' + [003] {Code} 'pushq %rbp' + [003] {Code} 'movq %rsp, %rbp' + [003] 5 {Line} + [003] {Code} 'leaq (%rip), %rdi' + [003] {Code} 'movl $0x0, %eax' + [003] {Code} 'callq 0x0' + [003] 6 {Line} + [003] {Code} 'movl $0x0, %eax' + [003] 7 {Line} + [003] {Code} 'popq %rbp' + [003] {Code} 'retq' + [003] 7 {Line} + +The logical views shows the intermixed lines and assembler instructions, +allowing to compare the code generated by the different toolchains. + +TEST CASE 3 - INCORRECT LEXICAL SCOPE FOR TYPEDEF +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The below example is used to show different output generated by +:program:`llvm-debuginfo-analyzer`. We compiled the example for an X86 +Codeview and ELF targets with recent versions of Clang, GCC and MSVC +(-O0 -g). + +.. code-block:: c++ + + 1 int bar(float Input) { return (int)Input; } + 2 + 3 unsigned foo(char Param) { + 4 typedef int INT; // ** Definition for INT ** + 5 INT Value = Param; + 6 { + 7 typedef float FLOAT; // ** Definition for FLOAT ** + 8 { + 9 FLOAT Added = Value + Param; + 10 Value = bar(Added); + 11 } + 12 } + 13 return Value + Param; + 14 } + +The above test is used to illustrate a scope issue found in the Clang +compiler: +`PR44884 (Bugs LLVM) `_ / +`PR44229 (GitHub LLVM) `_ + +The lines 4 and 7 contains 2 typedefs, defined at different lexical +scopes. + +.. code-block:: c++ + + 4 typedef int INT; + 7 typedef float FLOAT; + +These are the logical views that :program:`llvm-debuginfo-analyzer` +generates for 3 different compilers (MSVC, Clang and GCC), emitting +different debug information formats (CodeView, DWARF) on different +platforms. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level,format,producer + --print=symbols,types,lines + --output-sort=kind + pr-44884-codeview-clang.o + pr-44884-codeview-msvc.o + pr-44884-dwarf-clang.o + pr-44884-dwarf-gcc.o + +CodeView - Clang (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64 + + [001] {CompileUnit} 'pr-44884.cpp' + [002] {Producer} 'clang version 14.0.0' + [002] {Function} extern not_inlined 'bar' -> 'int' + [003] {Parameter} 'Input' -> 'float' + [003] 1 {Line} + [002] {Function} extern not_inlined 'foo' -> 'unsigned' + [003] {Block} + [004] {Variable} 'Added' -> 'float' + [004] 9 {Line} + [004] 10 {Line} + [003] {Parameter} 'Param' -> 'char' + [003] {TypeAlias} 'FLOAT' -> 'float' + [003] {TypeAlias} 'INT' -> 'int' + [003] {Variable} 'Value' -> 'int' + [003] 3 {Line} + [003] 5 {Line} + [003] 13 {Line} + +CodeView - MSVC (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-44884-codeview-msvc.o' -> COFF-i386 + + [001] {CompileUnit} 'pr-44884.cpp' + [002] {Producer} 'Microsoft (R) Optimizing Compiler' + [002] {Function} extern not_inlined 'bar' -> 'int' + [003] {Variable} 'Input' -> 'float' + [003] 1 {Line} + [002] {Function} extern not_inlined 'foo' -> 'unsigned' + [003] {Block} + [004] {Block} + [005] {Variable} 'Added' -> 'float' + [004] {TypeAlias} 'FLOAT' -> 'float' + [004] 9 {Line} + [004] 10 {Line} + [003] {TypeAlias} 'INT' -> 'int' + [003] {Variable} 'Param' -> 'char' + [003] {Variable} 'Value' -> 'int' + [003] 3 {Line} + [003] 5 {Line} + [003] 13 {Line} + [003] 14 {Line} + +DWARF - Clang (Linux) +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-44884-dwarf-clang.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-44884.cpp' + [002] {Producer} 'clang version 14.0.0' + [002] 1 {Function} extern not_inlined 'bar' -> 'int' + [003] 1 {Parameter} 'Input' -> 'float' + [003] 1 {Line} + [003] 1 {Line} + [003] 1 {Line} + [002] 3 {Function} extern not_inlined 'foo' -> 'unsigned int' + [003] {Block} + [004] 9 {Variable} 'Added' -> 'FLOAT' + [004] 9 {Line} + [004] 9 {Line} + [004] 9 {Line} + [004] 9 {Line} + [004] 9 {Line} + [004] 10 {Line} + [004] 10 {Line} + [004] 10 {Line} + [004] 13 {Line} + [003] 3 {Parameter} 'Param' -> 'char' + [003] 7 {TypeAlias} 'FLOAT' -> 'float' + [003] 4 {TypeAlias} 'INT' -> 'int' + [003] 5 {Variable} 'Value' -> 'INT' + [003] 3 {Line} + [003] 5 {Line} + [003] 5 {Line} + [003] 13 {Line} + [003] 13 {Line} + [003] 13 {Line} + [003] 13 {Line} + +DWARF - GCC (Linux) +^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-44884-dwarf-gcc.o' -> elf32-littlearm + + [001] {CompileUnit} 'pr-44884.cpp' + [002] {Producer} 'GNU C++14 10.2.1 20201103' + [002] 1 {Function} extern not_inlined 'bar' -> 'int' + [003] 1 {Parameter} 'Input' -> 'float' + [003] 1 {Line} + [003] 1 {Line} + [003] 1 {Line} + [002] 3 {Function} extern not_inlined 'foo' -> 'unsigned int' + [003] {Block} + [004] {Block} + [005] 9 {Variable} 'Added' -> 'FLOAT' + [005] 9 {Line} + [005] 9 {Line} + [005] 9 {Line} + [005] 10 {Line} + [005] 13 {Line} + [004] 7 {TypeAlias} 'FLOAT' -> 'float' + [003] 3 {Parameter} 'Param' -> 'char' + [003] 4 {TypeAlias} 'INT' -> 'int' + [003] 5 {Variable} 'Value' -> 'INT' + [003] 3 {Line} + [003] 5 {Line} + [003] 13 {Line} + [003] 14 {Line} + [003] 14 {Line} + +From the previous logical views, we can see that the Clang compiler +emits **both typedefs at the same lexical scope (3)**, which is wrong. +GCC and MSVC emit correct lexical scope for both typedefs. + +Using the :program:`llvm-debuginfo-analyzer` selection facilities, we +can produce a simple tabular output showing just the logical types that +are **Typedef**. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level,format + --output-sort=name + --select-types=Typedef + --report=list + --print=types + pr-44884-*.o + + Logical View: + [000] {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64 + + [001] {CompileUnit} 'pr_44884.cpp' + [003] {TypeAlias} 'FLOAT' -> 'float' + [003] {TypeAlias} 'INT' -> 'int' + + Logical View: + [000] {File} 'pr-44884-codeview-msvc.o' -> COFF-i386 + + [001] {CompileUnit} 'pr_44884.cpp' + [004] {TypeAlias} 'FLOAT' -> 'float' + [003] {TypeAlias} 'INT' -> 'int' + + Logical View: + [000] {File} 'pr-44884-dwarf-clang.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr_44884.cpp' + [003] 7 {TypeAlias} 'FLOAT' -> 'float' + [003] 4 {TypeAlias} 'INT' -> 'int' + + Logical View: + [000] {File} 'pr-44884-dwarf-gcc.o' -> elf32-littlearm + + [001] {CompileUnit} 'pr_44884.cpp' + [004] 7 {TypeAlias} 'FLOAT' -> 'float' + [003] 4 {TypeAlias} 'INT' -> 'int' + +It also shows, that the CodeView debug information does not generate +source code line numbers for the those logical types. The logical view +is sorted by the types name. + +TEST CASE 4 - MISSING NESTED ENUMERATIONS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The below example is used to show different output generated by +:program:`llvm-debuginfo-analyzer`. We compiled the example for an X86 +Codeview and ELF targets with recent versions of Clang, GCC and MSVC +(-O0 -g). + +.. code-block:: c++ + + 1 struct Struct { + 2 union Union { + 3 enum NestedEnum { RED, BLUE }; + 4 }; + 5 Union U; + 6 }; + 7 + 8 Struct S; + 9 int test() { + 10 return S.U.BLUE; + 11 } + +The above test is used to illustrate a scope issue found in the Clang +compiler: +`PR46466 (Bugs LLVM) `_ / +`PR45811 (GitHub LLVM) `_ + +These are the logical views that :program:`llvm-debuginfo-analyzer` +generates for 3 different compilers (MSVC, Clang and GCC), emitting +different debug information formats (CodeView, DWARF) on different +platforms. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level,format,producer + --output-sort=name + --print=symbols,types + pr-46466-codeview-clang.o + pr-46466-codeview-msvc.o + pr-46466-dwarf-clang.o + pr-46466-dwarf-gcc.o + +CodeView - Clang (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64 + + [001] {CompileUnit} 'pr-46466.cpp' + [002] {Producer} 'clang version 14.0.0' + [002] {Variable} extern 'S' -> 'Struct' + [002] 1 {Struct} 'Struct' + [003] {Member} public 'U' -> 'Union' + [003] 2 {Union} 'Union' + [004] 3 {Enumeration} 'NestedEnum' -> 'int' + [005] {Enumerator} 'BLUE' = '0x1' + [005] {Enumerator} 'RED' = '0x0' + +CodeView - MSVC (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-46466-codeview-msvc.o' -> COFF-i386 + + [001] {CompileUnit} 'pr-46466.cpp' + [002] {Producer} 'Microsoft (R) Optimizing Compiler' + [002] {Variable} extern 'S' -> 'Struct' + [002] 1 {Struct} 'Struct' + [003] {Member} public 'U' -> 'Union' + [003] 2 {Union} 'Union' + [004] 3 {Enumeration} 'NestedEnum' -> 'int' + [005] {Enumerator} 'BLUE' = '0x1' + [005] {Enumerator} 'RED' = '0x0' + +DWARF - Clang (Linux) +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-46466.cpp' + [002] {Producer} 'clang version 14.0.0' + [002] 8 {Variable} extern 'S' -> 'Struct' + [002] 1 {Struct} 'Struct' + [003] 5 {Member} public 'U' -> 'Union' + +DWARF - GCC (Linux) +^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-46466.cpp' + [002] {Producer} 'GNU C++14 9.3.0' + [002] 8 {Variable} extern 'S' -> 'Struct' + [002] 1 {Struct} 'Struct' + [003] 5 {Member} public 'U' -> 'Union' + [003] 2 {Union} 'Union' + [004] 3 {Enumeration} 'NestedEnum' -> 'unsigned int' + [005] {Enumerator} 'BLUE' = '0x1' + [005] {Enumerator} 'RED' = '0x0' + +From the previous logical views, we can see that the DWARF debug +information generated by the Clang compiler does not include any +references to the enumerators **RED** and **BLUE**. The DWARF +generated by GCC, CodeView generated by Clang and MSVC, they do +include such references. + +Using the :program:`llvm-debuginfo-analyzer` selection facilities, we +can produce a logical view showing just the logical types that are +**Enumerator** and its parents. The logical view is sorted by the types +name. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=format,level + --output-sort=name + --select-types=Enumerator + --report=parents + --print=types + pr-46466-*.o + +.. code-block:: none + + Logical View: + [000] {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64 + + [001] {CompileUnit} 'pr-46466.cpp' + [002] 1 {Struct} 'Struct' + [003] 2 {Union} 'Union' + [004] 3 {Enumeration} 'NestedEnum' -> 'int' + [005] {Enumerator} 'BLUE' = '0x1' + [005] {Enumerator} 'RED' = '0x0' + + Logical View: + [000] {File} 'pr-46466-codeview-msvc.o' -> COFF-i386 + + [001] {CompileUnit} 'pr-46466.cpp' + [002] 1 {Struct} 'Struct' + [003] 2 {Union} 'Union' + [004] 3 {Enumeration} 'NestedEnum' -> 'int' + [005] {Enumerator} 'BLUE' = '0x1' + [005] {Enumerator} 'RED' = '0x0' + + Logical View: + [000] {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-46466.cpp' + + Logical View: + [000] {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-46466.cpp' + [002] 1 {Struct} 'Struct' + [003] 2 {Union} 'Union' + [004] 3 {Enumeration} 'NestedEnum' -> 'unsigned int' + [005] {Enumerator} 'BLUE' = '0x1' + [005] {Enumerator} 'RED' = '0x0' + +Using the :program:`llvm-debuginfo-analyzer` selection facilities, we +can produce a simple tabular output including a summary for the logical +types that are **Enumerator**. The logical view is sorted by the types +name. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=format,level + --output-sort=name + --select-types=Enumerator + --print=types,summary + pr-46466-*.o + +.. code-block:: none + + Logical View: + [000] {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64 + + [001] {CompileUnit} 'pr-46466.cpp' + [005] {Enumerator} 'BLUE' = '0x1' + [005] {Enumerator} 'RED' = '0x0' + + ----------------------------- + Element Total Found + ----------------------------- + Scopes 5 0 + Symbols 2 0 + Types 6 2 + Lines 0 0 + ----------------------------- + Total 13 2 + + Logical View: + [000] {File} 'pr-46466-codeview-msvc.o' -> COFF-i386 + + [001] {CompileUnit} 'pr-46466.cpp' + [005] {Enumerator} 'BLUE' = '0x1' + [005] {Enumerator} 'RED' = '0x0' + + ----------------------------- + Element Total Found + ----------------------------- + Scopes 5 0 + Symbols 2 0 + Types 7 2 + Lines 0 0 + ----------------------------- + Total 14 2 + + Logical View: + [000] {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-46466.cpp' + + ----------------------------- + Element Total Found + ----------------------------- + Scopes 4 0 + Symbols 0 0 + Types 0 0 + Lines 0 0 + ----------------------------- + Total 4 0 + + Logical View: + [000] {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-46466.cpp' + [005] {Enumerator} 'BLUE' = '0x1' + [005] {Enumerator} 'RED' = '0x0' + + ----------------------------- + Element Total Found + ----------------------------- + Scopes 5 0 + Symbols 0 0 + Types 2 2 + Lines 0 0 + ----------------------------- + Total 7 2 + +From the values printed under the **Found** column, we can see that no +**Types** were found in the DWARF debug information generated by Clang. + +TEST CASE 5 - INCORRECT LEXICAL SCOPE FOR VARIABLE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The below example is used to show different output generated by +:program:`llvm-debuginfo-analyzer`. We compiled the example for an X86 +Codeview and ELF targets with recent versions of Clang, GCC and MSVC +(-O0 -g). + +.. code-block:: c++ + + // definitions.h + #ifdef _MSC_VER + #define forceinline __forceinline + #elif defined(__clang__) + #if __has_attribute(__always_inline__) + #define forceinline inline __attribute__((__always_inline__)) + #else + #define forceinline inline + #endif + #elif defined(__GNUC__) + #define forceinline inline __attribute__((__always_inline__)) + #else + #define forceinline inline + #error + #endif + +As the test is dependent on inline compiler options, the above header +file defines *forceinline*. + +.. code-block:: c++ + + #include "definitions.h" + +.. code-block:: c++ + + 1 #include "definitions.h" + 2 forceinline int InlineFunction(int Param) { + 3 int Var_1 = Param; + 4 { + 5 int Var_2 = Param + Var_1; + 6 Var_1 = Var_2; + 7 } + 8 return Var_1; + 9 } + 10 + 11 int test(int Param_1, int Param_2) { + 12 int A = Param_1; + 13 A += InlineFunction(Param_2); + 14 return A; + 15 } + +The above test is used to illustrate a variable issue found in the Clang +compiler: +`PR43860 (Bugs LLVM) `_ / +`PR43205 (GitHub) `_ + +These are the logical views that :program:`llvm-debuginfo-analyzer` +generates for 3 different compilers (MSVC, Clang and GCC), emitting +different debug information formats (CodeView, DWARF) on different +platforms. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level,format,producer + --output-sort=name + --print=symbols + pr-43860-codeview-clang.o + pr-43860-codeview-msvc.o + pr-43860-dwarf-clang.o + pr-43860-dwarf-gcc.o + +CODEVIEW - Clang (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64 + + [001] {CompileUnit} 'pr-43860.cpp' + [002] {Producer} 'clang version 14.0.0' + [002] 2 {Function} inlined 'InlineFunction' -> 'int' + [003] {Parameter} '' -> 'int' + [002] {Function} extern not_inlined 'test' -> 'int' + [003] {Variable} 'A' -> 'int' + [003] {InlinedFunction} inlined 'InlineFunction' -> 'int' + [004] {Parameter} 'Param' -> 'int' + [004] {Variable} 'Var_1' -> 'int' + [004] {Variable} 'Var_2' -> 'int' + [003] {Parameter} 'Param_1' -> 'int' + [003] {Parameter} 'Param_2' -> 'int' + +CODEVIEW - MSVC (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-43860-codeview-msvc.o' -> COFF-i386 + + [001] {CompileUnit} 'pr-43860.cpp' + [002] {Producer} 'Microsoft (R) Optimizing Compiler' + [002] {Function} extern not_inlined 'InlineFunction' -> 'int' + [003] {Block} + [004] {Variable} 'Var_2' -> 'int' + [003] {Variable} 'Param' -> 'int' + [003] {Variable} 'Var_1' -> 'int' + [002] {Function} extern not_inlined 'test' -> 'int' + [003] {Variable} 'A' -> 'int' + [003] {Variable} 'Param_1' -> 'int' + [003] {Variable} 'Param_2' -> 'int' + +DWARF - Clang (Linux) +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-43860.cpp' + [002] {Producer} 'clang version 14.0.0' + [002] 2 {Function} extern inlined 'InlineFunction' -> 'int' + [003] {Block} + [004] 5 {Variable} 'Var_2' -> 'int' + [003] 2 {Parameter} 'Param' -> 'int' + [003] 3 {Variable} 'Var_1' -> 'int' + [002] 11 {Function} extern not_inlined 'test' -> 'int' + [003] 12 {Variable} 'A' -> 'int' + [003] 14 {InlinedFunction} inlined 'InlineFunction' -> 'int' + [004] {Block} + [005] {Variable} 'Var_2' -> 'int' + [004] {Parameter} 'Param' -> 'int' + [004] {Variable} 'Var_1' -> 'int' + [003] 11 {Parameter} 'Param_1' -> 'int' + [003] 11 {Parameter} 'Param_2' -> 'int' + +DWARF - GCC (Linux) +^^^^^^^^^^^^^^^^^^^ + +.. code-block:: none + + Logical View: + [000] {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-43860.cpp' + [002] {Producer} 'GNU C++14 9.3.0' + [002] 2 {Function} extern declared_inlined 'InlineFunction' -> 'int' + [003] {Block} + [004] 5 {Variable} 'Var_2' -> 'int' + [003] 2 {Parameter} 'Param' -> 'int' + [003] 3 {Variable} 'Var_1' -> 'int' + [002] 11 {Function} extern not_inlined 'test' -> 'int' + [003] 12 {Variable} 'A' -> 'int' + [003] 13 {InlinedFunction} declared_inlined 'InlineFunction' -> 'int' + [004] {Block} + [005] {Variable} 'Var_2' -> 'int' + [004] {Parameter} 'Param' -> 'int' + [004] {Variable} 'Var_1' -> 'int' + [003] 11 {Parameter} 'Param_1' -> 'int' + [003] 11 {Parameter} 'Param_2' -> 'int' + +From the previous logical views, we can see that the CodeView debug +information generated by the Clang compiler shows the variables **Var_1** +and **Var_2** are at the same lexical scope (**4**) in the function +**InlineFuction**. The DWARF generated by GCC/Clang and CodeView +generated by MSVC, show those variables at the correct lexical scope: +**3** and **4** respectively. + +Using the :program:`llvm-debuginfo-analyzer` selection facilities, we +can produce a simple tabular output showing just the logical elements +that have in their name the *var* pattern. The logical view is sorted +by the variables name. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=level,format + --output-sort=name + --select-regex --select-nocase --select=Var + --report=list + --print=symbols + pr-43860-*.o + +.. code-block:: none + + Logical View: + [000] {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64 + + [001] {CompileUnit} 'pr-43860.cpp' + [004] {Variable} 'Var_1' -> 'int' + [004] {Variable} 'Var_2' -> 'int' + + Logical View: + [000] {File} 'pr-43860-codeview-msvc.o' -> COFF-i386 + + [001] {CompileUnit} 'pr-43860.cpp' + [003] {Variable} 'Var_1' -> 'int' + [004] {Variable} 'Var_2' -> 'int' + + Logical View: + [000] {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-43860.cpp' + [004] {Variable} 'Var_1' -> 'int' + [003] 3 {Variable} 'Var_1' -> 'int' + [005] {Variable} 'Var_2' -> 'int' + [004] 5 {Variable} 'Var_2' -> 'int' + + Logical View: + [000] {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64 + + [001] {CompileUnit} 'pr-43860.cpp' + [004] {Variable} 'Var_1' -> 'int' + [003] 3 {Variable} 'Var_1' -> 'int' + [005] {Variable} 'Var_2' -> 'int' + [004] 5 {Variable} 'Var_2' -> 'int' + +It also shows, that the CodeView debug information does not generate +source code line numbers for the those logical symbols. The logical +view is sorted by the types name. + +TEST CASE 6 - FULL LOGICAL VIEW +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For advanced users, :program:`llvm-debuginfo-analyzer` can display low +level information that includes offsets within the debug information +section, debug location operands, linkage names, etc. + +.. code-block:: none + + llvm-debuginfo-analyzer --attribute=all + --print=all + test-dwarf-clang.o + + Logical View: + [0x0000000000][000] {File} 'test-dwarf-clang.o' -> elf64-x86-64 + + [0x000000000b][001] {CompileUnit} 'test.cpp' + [0x000000000b][002] {Producer} 'clang version 12.0.0' + {Directory} '' + {File} 'test.cpp' + {Public} 'foo' [0x0000000000:0x000000003a] + [0x000000000b][002] {Range} Lines 2:9 [0x0000000000:0x000000003a] + [0x00000000bc][002] {BaseType} 'bool' + [0x0000000099][002] {BaseType} 'int' + [0x00000000b5][002] {BaseType} 'unsigned int' + + [0x00000000a0][002] {Source} '/test.cpp' + [0x00000000a0][002] 1 {TypeAlias} 'INTPTR' -> [0x00000000ab]'* const int' + [0x000000002a][002] 2 {Function} extern not_inlined 'foo' -> [0x0000000099]'int' + [0x000000002a][003] {Range} Lines 2:9 [0x0000000000:0x000000003a] + [0x000000002a][003] {Linkage} 0x2 '_Z3fooPKijb' + [0x0000000071][003] {Block} + [0x0000000071][004] {Range} Lines 5:8 [0x000000001c:0x000000002f] + [0x000000007e][004] 5 {Variable} 'CONSTANT' -> [0x00000000c3]'const INTEGER' + [0x000000007e][005] {Coverage} 100.00% + [0x000000007f][005] {Location} + [0x000000007f][006] {Entry} Stack Offset: -28 (0xffffffffffffffe4) [DW_OP_fbreg] + [0x000000001c][004] 5 {Line} {NewStatement} '/test.cpp' + [0x000000001c][004] {Code} 'movl $0x7, -0x1c(%rbp)' + [0x0000000023][004] 6 {Line} {NewStatement} '/test.cpp' + [0x0000000023][004] {Code} 'movl $0x7, -0x4(%rbp)' + [0x000000002a][004] {Code} 'jmp 0x6' + [0x000000002f][004] 8 {Line} {NewStatement} '/test.cpp' + [0x000000002f][004] {Code} 'movl -0x14(%rbp), %eax' + [0x0000000063][003] 2 {Parameter} 'ParamBool' -> [0x00000000bc]'bool' + [0x0000000063][004] {Coverage} 100.00% + [0x0000000064][004] {Location} + [0x0000000064][005] {Entry} Stack Offset: -21 (0xffffffffffffffeb) [DW_OP_fbreg] + [0x0000000047][003] 2 {Parameter} 'ParamPtr' -> [0x00000000a0]'INTPTR' + [0x0000000047][004] {Coverage} 100.00% + [0x0000000048][004] {Location} + [0x0000000048][005] {Entry} Stack Offset: -16 (0xfffffffffffffff0) [DW_OP_fbreg] + [0x0000000055][003] 2 {Parameter} 'ParamUnsigned' -> [0x00000000b5]'unsigned int' + [0x0000000055][004] {Coverage} 100.00% + [0x0000000056][004] {Location} + [0x0000000056][005] {Entry} Stack Offset: -20 (0xffffffffffffffec) [DW_OP_fbreg] + [0x000000008d][003] 4 {TypeAlias} 'INTEGER' -> [0x0000000099]'int' + [0x0000000000][003] 2 {Line} {NewStatement} '/test.cpp' + [0x0000000000][003] {Code} 'pushq %rbp' + [0x0000000001][003] {Code} 'movq %rsp, %rbp' + [0x0000000004][003] {Code} 'movb %dl, %al' + [0x0000000006][003] {Code} 'movq %rdi, -0x10(%rbp)' + [0x000000000a][003] {Code} 'movl %esi, -0x14(%rbp)' + [0x000000000d][003] {Code} 'andb $0x1, %al' + [0x000000000f][003] {Code} 'movb %al, -0x15(%rbp)' + [0x0000000012][003] 3 {Line} {NewStatement} {PrologueEnd} '/test.cpp' + [0x0000000012][003] {Code} 'testb $0x1, -0x15(%rbp)' + [0x0000000016][003] {Code} 'je 0x13' + [0x0000000032][003] 8 {Line} '/test.cpp' + [0x0000000032][003] {Code} 'movl %eax, -0x4(%rbp)' + [0x0000000035][003] 9 {Line} {NewStatement} '/test.cpp' + [0x0000000035][003] {Code} 'movl -0x4(%rbp), %eax' + [0x0000000038][003] {Code} 'popq %rbp' + [0x0000000039][003] {Code} 'retq' + [0x000000003a][003] 9 {Line} {NewStatement} {EndSequence} '/test.cpp' + + ----------------------------- + Element Total Printed + ----------------------------- + Scopes 3 3 + Symbols 4 4 + Types 5 5 + Lines 25 25 + ----------------------------- + Total 37 37 + + Scope Sizes: + 189 (100.00%) : [0x000000000b][001] {CompileUnit} 'test.cpp' + 110 ( 58.20%) : [0x000000002a][002] 2 {Function} extern not_inlined 'foo' -> [0x0000000099]'int' + 27 ( 14.29%) : [0x0000000071][003] {Block} + + Totals by lexical level: + [001]: 189 (100.00%) + [002]: 110 ( 58.20%) + [003]: 27 ( 14.29%) + +The **Scope Sizes** table shows the contribution in bytes to the debug +information by each scope, which can be used to determine unexpected +size changes in the DWARF sections between different versions of the +same toolchain. + +.. code-block:: none + + [0x000000002a][002] 2 {Function} extern not_inlined 'foo' -> [0x0000000099]'int' + [0x000000002a][003] {Range} Lines 2:9 [0x0000000000:0x000000003a] + [0x000000002a][003] {Linkage} 0x2 '_Z3fooPKijb' + [0x0000000071][003] {Block} + [0x0000000071][004] {Range} Lines 5:8 [0x000000001c:0x000000002f] + [0x000000007e][004] 5 {Variable} 'CONSTANT' -> [0x00000000c3]'const INTEGER' + [0x000000007e][005] {Coverage} 100.00% + [0x000000007f][005] {Location} + [0x000000007f][006] {Entry} Stack Offset: -28 (0xffffffffffffffe4) [DW_OP_fbreg] + +The **{Range}** attribute describe the line ranges for a logical scope. +For this case, the function **foo** is within the lines **2** and **9**. + +The **{Coverage}** and **{Location}** attributes describe the debug +location and coverage for logical symbols. For optimized code, the +coverage value decreases and it affects the program debuggability. + +EXIT STATUS +----------- +:program:`llvm-debuginfo-analyzer` returns 0 if the input files were +parsed and printed successfully. Otherwise, it returns 1. + +SEE ALSO +-------- +:manpage:`llvm-dwarfdump` Index: llvm/docs/ProgrammersManual.rst =================================================================== --- llvm/docs/ProgrammersManual.rst +++ llvm/docs/ProgrammersManual.rst @@ -2314,6 +2314,18 @@ The IntervalMap iterators are quite big, so they should not be passed around as STL iterators. The heavyweight iterators allow a smaller data structure. +.. _dss_intervaltree: + +llvm/ADT/IntervalTree.h +^^^^^^^^^^^^^^^^^^^^^^^ + +``llvm::IntervalTree`` is a light tree data structure to hold intervals. It +allows finding all intervals that overlap with any given point. At this time, +it does not support any deletion or rebalancing operations. + +The IntervalTree is designed to be set up once, and then queried without any +further additions. + .. _dss_map: Index: llvm/include/llvm/ADT/IntervalTree.h =================================================================== --- /dev/null +++ llvm/include/llvm/ADT/IntervalTree.h @@ -0,0 +1,685 @@ +//===-- IntervalTree.h ------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements an interval tree. +// +// Further information: +// https://en.wikipedia.org/wiki/Interval_tree +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_INTERVALTREE_H +#define LLVM_ADT_INTERVALTREE_H + +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +// IntervalTree is a light tree data structure to hold intervals. It allows +// finding all intervals that overlap with any given point. At this time, +// it does not support any deletion or rebalancing operations. +// +// The IntervalTree is designed to be set up once, and then queried without +// any further additions. +// +// Synopsis: +// PointT objects are mapped to ValueT objects. +// +// template +// class IntervalTree { +// public: +// +// IntervalTree(); +// ~IntervalTree(): +// +// using IntervalReferences = SmallVector; +// +// void create(); +// void insert(PointT Left, PointT Right, ValueT Value); +// +// IntervalReferences getContaining(PointT Point); +// static void sortIntervals(IntervalReferences &Intervals, Sorting Sort); +// +// find_iterator begin(PointType Point) const; +// find_iterator end() const; +// +// bool empty() const; +// void clear(); +// +// void print(raw_ostream &OS, bool HexFormat = true); +// }; +// +//===----------------------------------------------------------------------===// +// +// In the below given dataset +// +// [a, b] <- (x) +// +// 'a' and 'b' describe a range and 'x' the value for that interval. +// +// The following data are purely for illustrative purposes: +// +// [30, 35] <- (3035), [39, 50] <- (3950), [55, 61] <- (5561), +// [31, 56] <- (3156), [12, 21] <- (1221), [25, 41] <- (2541), +// [49, 65] <- (4965), [71, 79] <- (7179), [11, 16] <- (1116), +// [20, 30] <- (2030), [36, 54] <- (3654), [60, 70] <- (6070), +// [74, 80] <- (7480), [15, 40] <- (1540), [43, 45] <- (4345), +// [50, 75] <- (5075), [10, 85] <- (1085) +// +// The data represents a set of overlapping intervals: +// +// 30--35 39------------50 55----61 +// 31------------------------56 +// 12--------21 25------------41 49-------------65 71-----79 +// 11----16 20-----30 36----------------54 60------70 74---- 80 +// 15---------------------40 43--45 50--------------------75 +// 10----------------------------------------------------------------------85 +// +// The items are stored in a binary tree with each node storing: +// +// MP: A middle point. +// IL: All intervals whose left value are completely to the left of the middle +// point. They are sorted in ascending order by their beginning point. +// IR: All intervals whose right value are completely to the right of the +// middle point. They are sorted in descending order by their ending point. +// LS: Left subtree. +// RS: Right subtree. +// +// As IL and IR will contain the same intervals, in order to optimize space, +// instead of storing intervals on each node, we use two vectors that will +// contain the intervals described by IL and IR. Each node will contain an +// index into that vector (global bucket), to indicate the beginning of the +// intervals assigned to the node. +// +// The following is the output from print(): +// +// 0: MP:43 IR [10,85] [31,56] [36,54] [39,50] [43,45] +// 0: MP:43 IL [10,85] [31,56] [36,54] [39,50] [43,45] +// 1: MP:25 IR [25,41] [15,40] [20,30] +// 1: MP:25 IL [15,40] [20,30] [25,41] +// 2: MP:15 IR [12,21] [11,16] +// 2: MP:15 IL [11,16] [12,21] +// 2: MP:36 IR [] +// 2: MP:36 IL [] +// 3: MP:31 IR [30,35] +// 3: MP:31 IL [30,35] +// 1: MP:61 IR [50,75] [60,70] [49,65] [55,61] +// 1: MP:61 IL [49,65] [50,75] [55,61] [60,70] +// 2: MP:74 IR [74,80] [71,79] +// 2: MP:74 IL [71,79] [74,80] +// +// with: +// 0: Root Node. +// MP: Middle point. +// IL: Intervals to the left (in ascending order by beginning point). +// IR: Intervals to the right (in descending order by ending point). +// +// Root +// | +// V +// +------------MP:43------------+ +// | IL IR | +// | [10,85] [10,85] | +// LS | [31,56] [31,56] | RS +// | [36,54] [36,54] | +// | [39,50] [39,50] | +// | [43,45] [43,45] | +// V V +// +------------MP:25------------+ MP:61------------+ +// | IL IR | IL IR | +// | [15,40] [25,41] | [49,65] [50,75] | +// LS | [20,30] [15,40] | RS [50,75] [60,70] | RS +// | [25,41] [20,30] | [55,61] [49,65] | +// | | [60,70] [55,61] | +// V V V +// MP:15 +-------MP:36 MP:74 +// IL IR | IL IR IL IR +// [11,16] [12,21] LS | [] [] [71,79] [74,80] +// [12,21] [11,16] | [74,80] [71,79] +// V +// MP:31 +// IL IR +// [30,35] [30,35] +// +// The creation of an interval tree is done in 2 steps: +// 1) Insert the interval items by calling +// void insert(PointT Left, PointT Right, ValueT Value); +// Left, Right: the interval left and right limits. +// Value: the data associated with that specific interval. +// +// 2) Create the interval tree by calling +// void create(); +// +// Once the tree is created, it is switched to query mode. +// Query the tree by using iterators or container. +// +// a) Iterators over intervals overlapping the given point with very weak +// ordering guarantees. +// find_iterator begin(PointType Point) const; +// find_iterator end() const; +// Point: a target point to be tested for inclusion in any interval. +// +// b) Container: +// IntervalReferences getContaining(PointT Point); +// Point: a target point to be tested for inclusion in any interval. +// Returns vector with all the intervals containing the target point. +// +// The returned intervals are in their natural tree location. They can +// be sorted: +// +// static void sortIntervals(IntervalReferences &Intervals, Sorting Sort); +// +// Ability to print the constructed interval tree: +// void print(raw_ostream &OS, bool HexFormat = true); +// Display the associated data in hexadecimal format. + +namespace llvm { + +//===----------------------------------------------------------------------===// +//--- IntervalData ----// +//===----------------------------------------------------------------------===// +/// An interval data composed by a \a Left and \a Right points and an +/// associated \a Value. +/// \a PointT corresponds to the interval ranges type. +/// \a ValueT corresponds to the interval value type. +template class IntervalData { +protected: + using PointType = PointT; + using ValueType = ValueT; + +private: + PointType Left; + PointType Right; + ValueType Value; + +public: + IntervalData() = delete; + IntervalData(PointType Left, PointType Right, ValueType Value) + : Left(Left), Right(Right), Value(Value) {} + virtual ~IntervalData() = default; + PointType left() const { return Left; } + PointType right() const { return Right; } + ValueType value() const { return Value; } + + /// Return true if \a Point is inside the left bound of closed interval \a + /// [Left;Right]. This is Left <= Point for closed intervals. + bool left(const PointType &Point) const { return left() <= Point; } + + /// Return true if \a Point is inside the right bound of closed interval \a + /// [Left;Right]. This is Point <= Right for closed intervals. + bool right(const PointType &Point) const { return Point <= right(); } + + /// Return true when \a Point is contained in interval \a [Left;Right]. + /// This is Left <= Point <= Right for closed intervals. + bool contains(const PointType &Point) const { + return left(Point) && right(Point); + } +}; + +//===----------------------------------------------------------------------===// +//--- IntervalTree ----// +//===----------------------------------------------------------------------===// +// Helper class template that is used by the IntervalTree to ensure that one +// does instantiate using only fundamental and/or pointer types. +template +using TypeIsValid = + std::integral_constant::value || + std::is_pointer::value>; + +template > +class IntervalTree { + static_assert(TypeIsValid::value, + "PointT must be an fundamental or pointer type"); + static_assert(TypeIsValid::value, + "ValueT must be an fundamental or pointer type"); + +public: + using PointType = PointT; + using ValueType = ValueT; + using DataType = DataT; + using Allocator = BumpPtrAllocator; + + enum class Sorting { Ascending, Descending }; + using IntervalReferences = SmallVector; + +private: + using IntervalVector = SmallVector; + using PointsVector = SmallVector; + + class IntervalNode { + PointType MiddlePoint; // MP - Middle point. + IntervalNode *Left = nullptr; // LS - Left subtree. + IntervalNode *Right = nullptr; // RS - Right subtree. + unsigned BucketIntervalsStart = 0; // Starting index in global bucket. + unsigned BucketIntervalsSize = 0; // Size of bucket. + + public: + PointType middle() const { return MiddlePoint; } + unsigned start() const { return BucketIntervalsStart; } + unsigned size() const { return BucketIntervalsSize; } + + IntervalNode(PointType Point, unsigned Start) + : MiddlePoint(Point), BucketIntervalsStart(Start) {} + + friend IntervalTree; + }; + + Allocator &NodeAllocator; // Allocator used for creating interval nodes. + IntervalNode *Root = nullptr; // Interval tree root. + IntervalVector Intervals; // Storage for each interval and all of the fields + // point back into it. + PointsVector EndPoints; // Sorted left and right points of all the intervals. + + // These vectors provide storage that nodes carve buckets of overlapping + // intervals out of. All intervals are recorded on each vector. + // The bucket with the intervals associated to a node, is determined by + // the fields 'BucketIntervalStart' and 'BucketIntervalSize' in the node. + // The buckets in the first vector are sorted in ascending order using + // the left value and the buckets in the second vector are sorted in + // descending order using the right value. Every interval in a bucket + // contains the middle point for the node. + IntervalReferences IntervalsLeft; // Intervals to the left of middle point. + IntervalReferences IntervalsRight; // Intervals to the right of middle point. + + // Working vector used during the tree creation to sort the intervals. It is + // cleared once the tree is created. + IntervalReferences References; + + /// Recursively delete the constructed tree. + void deleteTree(IntervalNode *Node) { + if (Node) { + deleteTree(Node->Left); + deleteTree(Node->Right); + Node->~IntervalNode(); + NodeAllocator.Deallocate(Node); + } + } + + /// Print the interval list (left and right) for a given \a Node. + static void printList(raw_ostream &OS, IntervalReferences &IntervalSet, + unsigned Start, unsigned Size, bool HexFormat = true) { + assert(Start + Size < IntervalSet.size() && + "Start + Size must be in bounds of the IntervalSet"); + const char *Format = HexFormat ? "[0x%08x,0x%08x] " : "[%2d,%2d] "; + if (Size) { + for (unsigned Position = Start; Position < Start + Size; ++Position) + OS << format(Format, IntervalSet[Position]->left(), + IntervalSet[Position]->right()); + } else { + OS << "[]"; + } + OS << "\n"; + } + + /// Print an interval tree \a Node. + void printNode(raw_ostream &OS, unsigned Level, IntervalNode *Node, + bool HexFormat = true) { + const char *Format = HexFormat ? "MP:0x%08x " : "MP:%2d "; + auto PrintNodeData = [&](StringRef Text, IntervalReferences &IntervalSet) { + OS << format("%5d: ", Level); + OS.indent(Level * 2); + OS << format(Format, Node->middle()) << Text << " "; + printList(OS, IntervalSet, Node->start(), Node->size(), HexFormat); + }; + + PrintNodeData("IR", IntervalsRight); + PrintNodeData("IL", IntervalsLeft); + } + + /// Recursively print all the interval nodes. + void printTree(raw_ostream &OS, unsigned Level, IntervalNode *Node, + bool HexFormat = true) { + if (Node) { + printNode(OS, Level, Node, HexFormat); + ++Level; + printTree(OS, Level, Node->Left, HexFormat); + printTree(OS, Level, Node->Right, HexFormat); + } + } + + /// Recursively construct the interval tree. + /// IntervalsSize: Number of intervals that have been processed and it will + /// be used as the start for the intervals bucket for a node. + /// PointsBeginIndex, PointsEndIndex: Determine the range into the EndPoints + /// vector of end points to be processed. + /// ReferencesBeginIndex, ReferencesSize: Determine the range into the + /// intervals being processed. + IntervalNode *createTree(unsigned &IntervalsSize, int PointsBeginIndex, + int PointsEndIndex, int ReferencesBeginIndex, + int ReferencesSize) { + // We start by taking the entire range of all the intervals and dividing + // it in half at x_middle (in practice, x_middle should be picked to keep + // the tree relatively balanced). + // This gives three sets of intervals, those completely to the left of + // x_middle which we'll call S_left, those completely to the right of + // x_middle which we'll call S_right, and those overlapping x_middle + // which we'll call S_middle. + // The intervals in S_left and S_right are recursively divided in the + // same manner until there are no intervals remaining. + + if (PointsBeginIndex > PointsEndIndex || + ReferencesBeginIndex >= ReferencesSize) + return nullptr; + + int MiddleIndex = (PointsBeginIndex + PointsEndIndex) / 2; + PointType MiddlePoint = EndPoints[MiddleIndex]; + + unsigned NewBucketStart = IntervalsSize; + unsigned NewBucketSize = 0; + int ReferencesRightIndex = ReferencesSize; + + IntervalNode *Root = + new (NodeAllocator) IntervalNode(MiddlePoint, NewBucketStart); + + // A quicksort implementation where all the intervals that overlap + // with the pivot are put into the "bucket", and "References" is the + // partition space where we recursively sort the remaining intervals. + for (int Index = ReferencesBeginIndex; Index < ReferencesRightIndex;) { + + // Current interval contains the middle point. + if (References[Index]->contains(MiddlePoint)) { + IntervalsLeft[IntervalsSize] = References[Index]; + IntervalsRight[IntervalsSize] = References[Index]; + ++IntervalsSize; + Root->BucketIntervalsSize = ++NewBucketSize; + + if (Index < --ReferencesRightIndex) + std::swap(References[Index], References[ReferencesRightIndex]); + if (ReferencesRightIndex < --ReferencesSize) + std::swap(References[ReferencesRightIndex], + References[ReferencesSize]); + continue; + } + + if (References[Index]->left() > MiddlePoint) { + if (Index < --ReferencesRightIndex) + std::swap(References[Index], References[ReferencesRightIndex]); + continue; + } + ++Index; + } + + // Sort intervals on the left and right of the middle point. + if (NewBucketSize > 1) { + // Sort the intervals in ascending order by their beginning point. + std::sort(IntervalsLeft.begin() + NewBucketStart, + IntervalsLeft.begin() + NewBucketStart + NewBucketSize, + [](const DataType *LHS, const DataType *RHS) { + return LHS->left() < RHS->left(); + }); + // Sort the intervals in descending order by their ending point. + std::sort(IntervalsRight.begin() + NewBucketStart, + IntervalsRight.begin() + NewBucketStart + NewBucketSize, + [](const DataType *LHS, const DataType *RHS) { + return LHS->right() > RHS->right(); + }); + } + + if (PointsBeginIndex <= MiddleIndex - 1) { + Root->Left = createTree(IntervalsSize, PointsBeginIndex, MiddleIndex - 1, + ReferencesBeginIndex, ReferencesRightIndex); + } + + if (MiddleIndex + 1 <= PointsEndIndex) { + Root->Right = createTree(IntervalsSize, MiddleIndex + 1, PointsEndIndex, + ReferencesRightIndex, ReferencesSize); + } + + return Root; + } + +public: + class find_iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = DataType; + using difference_type = DataType; + using pointer = DataType *; + using reference = DataType &; + + private: + const IntervalReferences *AscendingBuckets = nullptr; + const IntervalReferences *DescendingBuckets = nullptr; + + // Current node and index while traversing the intervals that contain + // the reference point. + IntervalNode *Node = nullptr; + PointType Point; + unsigned Index = 0; + + // For the current node, check if we have intervals that contain the + // reference point. We return when the node does have intervals that + // contain such point. Otherwise we keep descending on that branch. + void initNode() { + Index = 0; + while (Node) { + // Return if the reference point is the same as the middle point or + // the current node doesn't have any intervals at all. + if (Point == Node->middle()) { + if (Node->size() == 0) { + // No intervals that contain the reference point. + Node = nullptr; + } + return; + } + + if (Point < Node->middle()) { + // The reference point can be at the left or right of the middle + // point. Return if the current node has intervals that contain the + // reference point; otherwise descend on the respective branch. + if (Node->size() && (*AscendingBuckets)[Node->start()]->left(Point)) { + return; + } + Node = Node->Left; + } else { + if (Node->size() && + (*DescendingBuckets)[Node->start()]->right(Point)) { + return; + } + Node = Node->Right; + } + } + } + + // Given the current node (which was initialized by initNode), move to + // the next interval in the list of intervals that contain the reference + // point. Otherwise move to the next node, as the intervals contained + // in that node, can contain the reference point. + void nextInterval() { + // If there are available intervals that contain the reference point, + // traverse them; otherwise move to the left or right node, depending + // on the middle point value. + if (++Index < Node->size()) { + if (Node->middle() == Point) + return; + if (Point < Node->middle()) { + // Reference point is on the left. + if (!(*AscendingBuckets)[Node->start() + Index]->left(Point)) { + // The intervals don't contain the reference point. Move to the + // next node, preserving the descending order. + Node = Node->Left; + initNode(); + } + } else { + // Reference point is on the right. + if (!(*DescendingBuckets)[Node->start() + Index]->right(Point)) { + // The intervals don't contain the reference point. Move to the + // next node, preserving the ascending order. + Node = Node->Right; + initNode(); + } + } + } else { + // We have traversed all the intervals in the current node. + if (Point == Node->middle()) { + Node = nullptr; + Index = 0; + return; + } + // Select a branch based on the middle point. + Node = Point < Node->middle() ? Node->Left : Node->Right; + initNode(); + } + } + + find_iterator() = default; + explicit find_iterator(const IntervalReferences *Left, + const IntervalReferences *Right, IntervalNode *Node, + PointType Point) + : AscendingBuckets(Left), DescendingBuckets(Right), Node(Node), + Point(Point), Index(0) { + initNode(); + } + + const DataType *current() const { + return (Point <= Node->middle()) + ? (*AscendingBuckets)[Node->start() + Index] + : (*DescendingBuckets)[Node->start() + Index]; + } + + public: + find_iterator &operator++() { + nextInterval(); + return *this; + } + + find_iterator operator++(int) { + find_iterator Iter(*this); + nextInterval(); + return Iter; + } + + /// Dereference operators. + const DataType *operator->() const { return current(); } + const DataType &operator*() const { return *(current()); } + + /// Comparison operators. + friend bool operator==(const find_iterator &LHS, const find_iterator &RHS) { + return (!LHS.Node && !RHS.Node && !LHS.Index && !RHS.Index) || + (LHS.Point == RHS.Point && LHS.Node == RHS.Node && + LHS.Index == RHS.Index); + } + friend bool operator!=(const find_iterator &LHS, const find_iterator &RHS) { + return !(LHS == RHS); + } + + friend IntervalTree; + }; + +private: + find_iterator End; + +public: + explicit IntervalTree(Allocator &NodeAllocator) + : NodeAllocator(NodeAllocator) {} + ~IntervalTree() { clear(); } + + /// Return true when no intervals are mapped. + bool empty() const { return Root == nullptr; } + + /// Remove all entries. + void clear() { + deleteTree(Root); + Root = nullptr; + Intervals.clear(); + IntervalsLeft.clear(); + IntervalsRight.clear(); + EndPoints.clear(); + } + + /// Add a mapping of [Left;Right] to \a Value. + void insert(PointType Left, PointType Right, ValueType Value) { + assert(empty() && "Invalid insertion. Interval tree already constructed."); + Intervals.emplace_back(Left, Right, Value); + } + + /// Return all the intervals in their natural tree location, that + /// contain the given point. + IntervalReferences getContaining(PointType Point) const { + assert(!empty() && "Interval tree it is not constructed."); + IntervalReferences IntervalSet; + for (find_iterator Iter = find(Point), E = find_end(); Iter != E; ++Iter) + IntervalSet.push_back(const_cast(&(*Iter))); + return IntervalSet; + } + + /// Sort the given intervals using the following sort options: + /// Ascending: return the intervals with the smallest at the front. + /// Descending: return the intervals with the biggest at the front. + static void sortIntervals(IntervalReferences &IntervalSet, Sorting Sort) { + std::sort(IntervalSet.begin(), IntervalSet.end(), + [Sort](const DataType *RHS, const DataType *LHS) { + return Sort == Sorting::Ascending + ? (LHS->right() - LHS->left()) > + (RHS->right() - RHS->left()) + : (LHS->right() - LHS->left()) < + (RHS->right() - RHS->left()); + }); + } + + /// Print the interval tree. + /// When \a HexFormat is true, the interval tree interval ranges and + /// associated values are printed in hexadecimal format. + void print(raw_ostream &OS, bool HexFormat = true) { + printTree(OS, 0, Root, HexFormat); + } + + /// Create the interval tree. + void create() { + assert(empty() && "Interval tree already constructed."); + // Sorted vector of unique end points values of all the intervals. + // Records references to the collected intervals. + SmallVector Points; + for (const DataType &Data : Intervals) { + Points.push_back(Data.left()); + Points.push_back(Data.right()); + References.push_back(std::addressof(Data)); + } + std::sort(Points.begin(), Points.end()); + auto Last = std::unique(Points.begin(), Points.end()); + Points.erase(Last, Points.end()); + + EndPoints.assign(Points.begin(), Points.end()); + + IntervalsLeft.resize(Intervals.size()); + IntervalsRight.resize(Intervals.size()); + + // Given a set of n intervals, construct a data structure so that + // we can efficiently retrieve all intervals overlapping another + // interval or point. + unsigned IntervalsSize = 0; + Root = + createTree(IntervalsSize, /*PointsBeginIndex=*/0, EndPoints.size() - 1, + /*ReferencesBeginIndex=*/0, References.size()); + + // Save to clear this storage, as it used only to sort the intervals. + References.clear(); + } + + /// Iterator to start a find operation; it returns find_end() if the + /// tree has not been built. + /// There is no support to iterate over all the elements of the tree. + find_iterator find(PointType Point) const { + return empty() + ? find_end() + : find_iterator(&IntervalsLeft, &IntervalsRight, Root, Point); + } + + /// Iterator to end find operation. + find_iterator find_end() const { return End; } +}; + +} // namespace llvm + +#endif // LLVM_ADT_INTERVALTREE_H Index: llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -268,6 +268,11 @@ DILineInfoSpecifier::FileLineInfoKind Kind, DILineInfo &Result) const; + /// Extracts directory name by its Entry in include directories table + /// in prologue. Returns true on success. + bool getDirectoryForEntry(const FileNameEntry &Entry, + std::string &Directory) const; + void dump(raw_ostream &OS, DIDumpOptions DumpOptions) const; void clear(); Index: llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h @@ -170,6 +170,11 @@ Optional Format; }; +bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS, + DIDumpOptions DumpOpts, uint8_t Opcode, + const uint64_t Operands[2], const MCRegisterInfo *MRI, + bool isEH); + inline bool operator==(const DWARFExpression::iterator &LHS, const DWARFExpression::iterator &RHS) { return LHS.Expr == RHS.Expr && LHS.Offset == RHS.Offset; Index: llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h @@ -429,11 +429,11 @@ return DWARFDie(this, &DieArray[0]); } - DWARFDie getNonSkeletonUnitDIE(bool ExtractUnitDIEOnly = true) { - parseDWO(); - if (DWO) - return DWO->getUnitDIE(ExtractUnitDIEOnly); - return getUnitDIE(ExtractUnitDIEOnly); + DWARFDie getNonSkeletonUnitDIE(bool ExtractUnitDIEOnly = true, + StringRef DWOAlternativeLocation = {}) { + parseDWO(DWOAlternativeLocation); + return DWO ? DWO->getUnitDIE(ExtractUnitDIEOnly) + : getUnitDIE(ExtractUnitDIEOnly); } const char *getCompilationDir(); @@ -568,7 +568,7 @@ /// parseDWO - Parses .dwo file for current compile unit. Returns true if /// it was actually constructed. - bool parseDWO(); + bool parseDWO(StringRef AlternativeLocation = {}); }; inline bool isCompileUnit(const std::unique_ptr &U) { Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVCompare.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVCompare.h @@ -0,0 +1,91 @@ +//===-- LVCompare.h ---------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVCompare class, which is used to describe a logical +// view comparison. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVCOMPARE_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVCOMPARE_H + +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" + +namespace llvm { +namespace logicalview { + +class LVReader; + +// Record the elements missing or added and their compare pass. +using LVPassEntry = std::tuple; +using LVPassTable = std::vector; + +class LVCompare final { + raw_ostream &OS; + LVScopes ScopeStack; + + // As the comparison is performed twice (by exchanging the reference + // and target readers) the element missing/added status does specify + // the comparison pass. + // By recording the missing/added elements along with its pass, it + // allow to check which elements were missing/added during each pass. + LVPassTable PassTable; + + // Reader used on the LHS of the comparison. + // In the 'Missing' pass, it points to the reference reader. + // In the 'Added' pass it points to the target reader. + LVReader *Reader = nullptr; + + bool FirstMissing = true; + bool PrintLines = false; + bool PrintScopes = false; + bool PrintSymbols = false; + bool PrintTypes = false; + + static void setInstance(LVCompare *Compare); + + void printCurrentStack(); + void printSummary() const; + +public: + LVCompare() = delete; + LVCompare(raw_ostream &OS); + LVCompare(const LVCompare &) = delete; + LVCompare &operator=(const LVCompare &) = delete; + ~LVCompare() = default; + +public: + static LVCompare &getInstance(); + + // Scopes stack used during the missing/added reporting. + void push(LVScope *Scope) { ScopeStack.push_back(Scope); } + void pop() { ScopeStack.pop_back(); } + + // Perform comparison between the 'Reference' and 'Target' scopes tree. + Error execute(LVReader *ReferenceReader, LVReader *TargetReader); + + void addPassEntry(LVReader *Reader, LVElement *Element, LVComparePass Pass) { + PassTable.emplace_back(Reader, Element, Pass); + } + LVPassTable getPassTable() const { return PassTable; } + +public: + void printItem(LVElement *Element, LVComparePass Pass); + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +inline LVCompare &getComparator() { return LVCompare::getInstance(); } + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVCOMPARE_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h @@ -0,0 +1,369 @@ +//===-- LVElement.h ---------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVElement class, which is used to describe a debug +// information element. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVELEMENT_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVELEMENT_H + +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" +#include "llvm/DebugInfo/LogicalView/Core/LVStringPool.h" +#include +#include +#include + +namespace llvm { +namespace logicalview { + +// RTTI Subclasses ID. +enum class LVSubclassID : unsigned char { + LV_ELEMENT, + LV_LINE_FIRST, + LV_LINE, + LV_LINE_DEBUG, + LV_LINE_ASSEMBLER, + LV_LINE_LAST, + lV_SCOPE_FIRST, + LV_SCOPE, + LV_SCOPE_AGGREGATE, + LV_SCOPE_ALIAS, + LV_SCOPE_ARRAY, + LV_SCOPE_COMPILE_UNIT, + LV_SCOPE_ENUMERATION, + LV_SCOPE_FORMAL_PACK, + LV_SCOPE_FUNCTION, + LV_SCOPE_FUNCTION_INLINED, + LV_SCOPE_FUNCTION_TYPE, + LV_SCOPE_NAMESPACE, + LV_SCOPE_ROOT, + LV_SCOPE_TEMPLATE_PACK, + LV_SCOPE_LAST, + LV_SYMBOL_FIRST, + LV_SYMBOL, + LV_SYMBOL_LAST, + LV_TYPE_FIRST, + LV_TYPE, + LV_TYPE_DEFINITION, + LV_TYPE_ENUMERATOR, + LV_TYPE_IMPORT, + LV_TYPE_PARAM, + LV_TYPE_SUBRANGE, + LV_TYPE_LAST +}; + +enum class LVElementKind { Discarded, Global, Optimized, LastEntry }; +using LVElementKindSet = std::set; +using LVElementDispatch = std::map; +using LVElementRequest = std::vector; + +class LVElement : public LVObject { + enum class Property { + IsLine, // A logical line. + IsScope, // A logical scope. + IsSymbol, // A logical symbol. + IsType, // A logical type. + IsEnumClass, + IsExternal, + HasType, + HasAugmentedName, + IsTypedefReduced, + IsArrayResolved, + IsMemberPointerResolved, + IsTemplateResolved, + IsInlined, + IsInlinedAbstract, + InvalidFilename, + HasReference, + HasReferenceAbstract, + HasReferenceExtension, + HasReferenceSpecification, + QualifiedResolved, + IncludeInPrint, + IsStatic, + TransformName, + IsScoped, // CodeView local type. + IsNested, // CodeView nested type. + IsScopedAlready, // CodeView nested type inserted in correct scope. + IsArtificial, + IsReferencedType, + IsSystem, + OffsetFromTypeIndex, + IsAnonymous, + LastEntry + }; + // Typed bitvector with properties for this element. + LVProperties Properties; + static LVElementDispatch Dispatch; + + /// RTTI. + const LVSubclassID SubclassID; + + // Indexes in the String Pool. + size_t NameIndex = 0; + size_t QualifiedNameIndex = 0; + size_t FilenameIndex = 0; + + uint16_t AccessibilityCode : 2; // DW_AT_accessibility. + uint16_t InlineCode : 2; // DW_AT_inline. + uint16_t VirtualityCode : 2; // DW_AT_virtuality. + + // The given Specification points to an element that is connected via the + // DW_AT_specification, DW_AT_abstract_origin or DW_AT_extension attribute. + void setFileLine(LVElement *Specification); + + // Get the qualified name that include its parents name. + void resolveQualifiedName(); + +protected: + // Type of this element. + LVElement *ElementType = nullptr; + + // Print the FileName Index. + void printFileIndex(raw_ostream &OS, bool Full = true) const override; + +public: + LVElement(LVSubclassID ID) + : LVObject(), SubclassID(ID), AccessibilityCode(0), InlineCode(0), + VirtualityCode(0) {} + LVElement(const LVElement &) = delete; + LVElement &operator=(const LVElement &) = delete; + virtual ~LVElement() = default; + + LVSubclassID getSubclassID() const { return SubclassID; } + + PROPERTY(Property, IsLine); + PROPERTY(Property, IsScope); + PROPERTY(Property, IsSymbol); + PROPERTY(Property, IsType); + PROPERTY(Property, IsEnumClass); + PROPERTY(Property, IsExternal); + PROPERTY(Property, HasType); + PROPERTY(Property, HasAugmentedName); + PROPERTY(Property, IsTypedefReduced); + PROPERTY(Property, IsArrayResolved); + PROPERTY(Property, IsMemberPointerResolved); + PROPERTY(Property, IsTemplateResolved); + PROPERTY(Property, IsInlined); + PROPERTY(Property, IsInlinedAbstract); + PROPERTY(Property, InvalidFilename); + PROPERTY(Property, HasReference); + PROPERTY(Property, HasReferenceAbstract); + PROPERTY(Property, HasReferenceExtension); + PROPERTY(Property, HasReferenceSpecification); + PROPERTY(Property, QualifiedResolved); + PROPERTY(Property, IncludeInPrint); + PROPERTY(Property, IsStatic); + PROPERTY(Property, TransformName); + PROPERTY(Property, IsScoped); + PROPERTY(Property, IsNested); + PROPERTY(Property, IsScopedAlready); + PROPERTY(Property, IsArtificial); + PROPERTY(Property, IsReferencedType); + PROPERTY(Property, IsSystem); + PROPERTY(Property, OffsetFromTypeIndex); + PROPERTY(Property, IsAnonymous); + +public: + bool isNamed() const override { return NameIndex != 0; } + bool isTyped() const override { return ElementType != nullptr; } + bool isFiled() const override { return FilenameIndex != 0; } + + // The Element class type can point to a Type or Scope. + bool getIsKindType() const { return ElementType && ElementType->getIsType(); } + bool getIsKindScope() const { + return ElementType && ElementType->getIsScope(); + } + + StringRef getName() const override { + return getStringPool().getString(NameIndex); + } + void setName(StringRef ElementName) override; + + // Get pathname associated with the Element. + StringRef getPathname() const { + return getStringPool().getString(getFilenameIndex()); + } + + // Set filename associated with the Element. + void setFilename(StringRef Filename); + + // Set the Element qualified name. + void setQualifiedName(StringRef Name) { + QualifiedNameIndex = getStringPool().getIndex(Name); + } + StringRef getQualifiedName() const { + return getStringPool().getString(QualifiedNameIndex); + } + + size_t getNameIndex() const { return NameIndex; } + size_t getQualifiedNameIndex() const { return QualifiedNameIndex; } + + void setInnerComponent() { setInnerComponent(getName()); } + void setInnerComponent(StringRef Name); + + // Element type name. + StringRef getTypeName() const; + + virtual StringRef getProducer() const { return StringRef(); } + virtual void setProducer(StringRef ProducerName) {} + + virtual bool isCompileUnit() const { return false; } + virtual bool isRoot() const { return false; } + + virtual void setReference(LVElement *Element) {} + virtual void setReference(LVScope *Scope) {} + virtual void setReference(LVSymbol *Symbol) {} + virtual void setReference(LVType *Type) {} + + virtual void setLinkageName(StringRef LinkageName) {} + virtual StringRef getLinkageName() const { return StringRef(); } + virtual size_t getLinkageNameIndex() const { return 0; } + + virtual uint32_t getCallLineNumber() const { return 0; } + virtual void setCallLineNumber(uint32_t Number) {} + virtual size_t getCallFilenameIndex() const { return 0; } + virtual void setCallFilenameIndex(size_t Index) {} + size_t getFilenameIndex() const { return FilenameIndex; } + void setFilenameIndex(size_t Index) { FilenameIndex = Index; } + + // Set the File location for the Element. + void setFile(LVElement *Reference = nullptr); + + virtual bool isBase() const { return false; } + virtual bool isTemplateParam() const { return false; } + + virtual uint32_t getBitSize() const { return 0; } + virtual void setBitSize(uint32_t Size) {} + + virtual int64_t getCount() const { return 0; } + virtual void setCount(int64_t Value) {} + virtual int64_t getLowerBound() const { return 0; } + virtual void setLowerBound(int64_t Value) {} + virtual int64_t getUpperBound() const { return 0; } + virtual void setUpperBound(int64_t Value) {} + virtual std::pair getBounds() const { return {}; } + virtual void setBounds(unsigned Lower, unsigned Upper) {} + + // Access DW_AT_GNU_discriminator attribute. + virtual uint32_t getDiscriminator() const { return 0; } + virtual void setDiscriminator(uint32_t Value) {} + + // Process the values for a DW_TAG_enumerator. + virtual std::string getValue() const { return {}; } + virtual void setValue(StringRef Value) {} + virtual size_t getValueIndex() const { return 0; } + + // DWARF Accessibility Codes. + uint32_t getAccessibilityCode() const { return AccessibilityCode; } + void setAccessibilityCode(uint32_t Access) { AccessibilityCode = Access; } + StringRef + accessibilityString(uint32_t Access = dwarf::DW_ACCESS_private) const; + + // CodeView Accessibility Codes. + uint32_t getAccessibilityCode(codeview::MemberAccess Access); + void setAccessibilityCode(codeview::MemberAccess Access) { + AccessibilityCode = getAccessibilityCode(Access); + } + + // DWARF Inline Codes. + uint32_t getInlineCode() const { return InlineCode; } + void setInlineCode(uint32_t Code) { InlineCode = Code; } + StringRef inlineCodeString(uint32_t Code) const; + + // DWARF Virtuality Codes. + uint32_t getVirtualityCode() const { return VirtualityCode; } + void setVirtualityCode(uint32_t Virtuality) { VirtualityCode = Virtuality; } + StringRef + virtualityString(uint32_t Virtuality = dwarf::DW_VIRTUALITY_none) const; + + // CodeView Virtuality Codes. + uint32_t getVirtualityCode(codeview::MethodKind Virtuality); + void setVirtualityCode(codeview::MethodKind Virtuality) { + VirtualityCode = getVirtualityCode(Virtuality); + } + + // DWARF Extern Codes. + StringRef externalString() const; + + LVElement *getType() const { return ElementType; } + LVType *getTypeAsType() const; + LVScope *getTypeAsScope() const; + + void setType(LVElement *Element = nullptr) { + ElementType = Element; + if (Element) { + setHasType(); + Element->setIsReferencedType(); + } + } + + // Set the type for the element, handling template parameters. + void setGenericType(LVElement *Element); + + StringRef getTypeQualifiedName() const { + return ElementType ? ElementType->getQualifiedName() : ""; + } + + StringRef typeAsString() const; + std::string typeOffsetAsString() const; + std::string discriminatorAsString() const; + + LVScope *traverseParents(LVScopeGetFunction GetFunction) const; + + LVScope *getFunctionParent() const; + virtual LVScope *getCompileUnitParent() const; + + // Print any referenced element. + void printReference(raw_ostream &OS, bool Full, LVElement *Parent) const; + + // Print the linkage name (Symbols and functions). + void printLinkageName(raw_ostream &OS, bool Full, LVElement *Parent, + LVScope *Scope) const; + void printLinkageName(raw_ostream &OS, bool Full, LVElement *Parent) const; + + // Generate the full name for the Element. + void resolveFullname(LVElement *BaseType, StringRef Name = emptyString()); + + // Generate a name for unnamed elements. + void generateName(std::string &Prefix) const; + void generateName(); + + virtual bool removeElement(LVElement *Element) { return false; } + virtual void updateLevel(LVScope *Parent, bool Moved = false); + + // During the parsing of the debug information, the logical elements are + // created with information extracted from its description entries (DIE). + // But they are not complete for the logical view concept. A second pass + // is executed in order to collect their additional information. + // The following functions 'resolve' some of their properties, such as + // name, references, parents, extra information based on the element kind. + virtual void resolve(); + virtual void resolveExtra() {} + virtual void resolveName(); + virtual void resolveReferences() {} + void resolveParents(); + + bool referenceMatch(const LVElement *Element) const; + + // Performs a set of tests to identify if the current Element is equal + // to the one supplied. Returns true if they are equal. False otherwise. + bool equals(const LVElement *Element) const; + // Report the element as missing or added during comparison. + virtual void report(LVComparePass Pass){}; + +public: + static LVElementDispatch &getDispatch() { return Dispatch; } +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVELEMENT_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h @@ -0,0 +1,163 @@ +//===-- LVLine.h ------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVLine class, which is used to describe a debug +// information line. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLINE_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLINE_H + +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" + +namespace llvm { +namespace logicalview { + +enum class LVLineKind { + IsBasicBlock, + IsDiscriminator, + IsEndSequence, + IsEpilogueBegin, + IsLineDebug, + IsLineAssembler, + IsNewStatement, // Shared with CodeView 'IsStatement' flag. + IsPrologueEnd, + IsAlwaysStepInto, // CodeView + IsNeverStepInto, // CodeView + LastEntry +}; +using LVLineKindSet = std::set; +using LVLineDispatch = std::map; +using LVLineRequest = std::vector; + +// Class to represent a logical line. +class LVLine : public LVElement { + // Typed bitvector with kinds for this line. + LVProperties Kinds; + static LVLineDispatch Dispatch; + + // Find the 'current' line in the 'Targets' set. + LVLine *findIn(const LVLines *Targets) const; + +public: + LVLine() : LVElement(LVSubclassID::LV_LINE) { + setIsLine(); + setIncludeInPrint(); + } + LVLine(const LVLine &) = delete; + LVLine &operator=(const LVLine &) = delete; + virtual ~LVLine() = default; + + static bool classof(const LVElement *Element) { + return Element->getSubclassID() == LVSubclassID::LV_LINE; + } + + KIND(LVLineKind, IsBasicBlock); + KIND(LVLineKind, IsDiscriminator); + KIND(LVLineKind, IsEndSequence); + KIND(LVLineKind, IsEpilogueBegin); + KIND(LVLineKind, IsLineDebug); + KIND(LVLineKind, IsLineAssembler); + KIND(LVLineKind, IsNewStatement); + KIND(LVLineKind, IsPrologueEnd); + KIND(LVLineKind, IsAlwaysStepInto); + KIND(LVLineKind, IsNeverStepInto); + +public: + const char *kind() const override; + + // Use the offset to store the line address. + uint64_t getAddress() const { return getOffset(); } + void setAddress(uint64_t address) { setOffset(address); } + + // String used for printing objects with no line number. + std::string noLineAsString(bool ShowZero = false) const override; + + // Line number for display; in the case of Inlined Functions, we use the + // DW_AT_call_line attribute; otherwise use DW_AT_decl_line attribute. + std::string lineNumberAsString(bool ShowZero = false) const override { + return lineAsString(getLineNumber(), getDiscriminator(), ShowZero); + } + +public: + static LVLineDispatch &getDispatch() { return Dispatch; } + +public: + // Iterate through the 'References' set and check that all its elements + // are present in the 'Targets' set. For a missing element, mark its + // parents as missing. + static void markMissingParents(const LVLines *References, + const LVLines *Targets); + + // Returns true if 'current' line is equal to the given 'Line'. + virtual bool equals(const LVLine *Line) const; + static bool equals(const LVLines *References, const LVLines *Targets); + void report(LVComparePass Pass) override; + +public: + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override{}; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +// Class to represent a DWARF line record object. +class LVLineDebug final : public LVLine { + // Discriminator value (DW_LNE_set_discriminator). The DWARF standard + // defines the discriminator as an unsigned LEB128 integer. + uint32_t Discriminator = 0; + +public: + LVLineDebug() : LVLine() { setIsLineDebug(); } + LVLineDebug(const LVLineDebug &) = delete; + LVLineDebug &operator=(const LVLineDebug &) = delete; + ~LVLineDebug() = default; + + // Additional line information. It includes attributes that describes + // states in the machine instructions (basic block, end prologue, etc). + std::string statesInfo(bool Formatted) const; + + // Access DW_LNE_set_discriminator attribute. + uint32_t getDiscriminator() const override { return Discriminator; } + void setDiscriminator(uint32_t Value) override { + Discriminator = Value; + setIsDiscriminator(); + } + + // Returns true if 'current' line is equal to the given 'line'. + bool equals(const LVLine *Line) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent an assembler line extracted from the text section. +class LVLineAssembler final : public LVLine { +public: + LVLineAssembler() : LVLine() { setIsLineAssembler(); } + LVLineAssembler(const LVLineAssembler &) = delete; + LVLineAssembler &operator=(const LVLineAssembler &) = delete; + ~LVLineAssembler() = default; + + // Print blanks as the line number. + std::string noLineAsString(bool ShowZero) const override { + return std::string(8, ' '); + }; + + // Returns true if 'current' line is equal to the given 'line'. + bool equals(const LVLine *Line) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLINE_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h @@ -0,0 +1,202 @@ +//===-- LVLocation.h --------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVOperation and LVLocation classes, which are used +// to describe variable locations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H + +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" + +namespace llvm { +namespace logicalview { + +using LVLineRange = std::pair; + +// The DW_AT_data_member_location attribute is a simple member offset. +const LVSmall LVLocationMemberOffset = 0; + +class LVOperation final { + // To describe an operation: + // OpCode + // Operands[0]: First operand. + // Operands[1]: Second operand. + // OP_bregx, OP_bit_piece, OP_[GNU_]const_type, + // OP_[GNU_]deref_type, OP_[GNU_]entry_value, OP_implicit_value, + // OP_[GNU_]implicit_pointer, OP_[GNU_]regval_type, OP_xderef_type. + LVSmall Opcode = 0; + uint64_t Operands[2]; + +public: + LVOperation() = delete; + LVOperation(LVSmall Opcode, LVUnsigned Operand1, LVUnsigned Operand2) + : Opcode(Opcode) { + Operands[0] = Operand1; + Operands[1] = Operand2; + } + LVOperation(const LVOperation &) = delete; + LVOperation &operator=(const LVOperation &) = delete; + ~LVOperation() = default; + + LVSmall getOpcode() const { return Opcode; } + uint64_t getOperand1() const { return Operands[0]; } + uint64_t getOperand2() const { return Operands[1]; } + std::string getOperandsDWARFInfo(); + std::string getOperandsCodeViewInfo(); + +public: + void print(raw_ostream &OS, bool Full = true) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() { print(dbgs()); } +#endif +}; + +class LVLocation : public LVObject { + enum class Property { + IsAddressRange, + IsBaseClassOffset, + IsBaseClassStep, + IsClassOffset, + IsFixedAddress, + IsLocationSimple, + IsGapEntry, + IsOperation, + IsOperationList, + IsRegister, + IsStackOffset, + IsDiscardedRange, + IsInvalidRange, + IsInvalidLower, + IsInvalidUpper, + IsCallSite, + LastEntry + }; + // Typed bitvector with properties for this location. + LVProperties Properties; + + // True if the location it is associated with a debug range. + bool hasAssociatedRange() const { + return !getIsClassOffset() && !getIsDiscardedRange(); + } + +protected: + // Line numbers associated with locations ranges. + LVLine *LowerLine = nullptr; + LVLine *UpperLine = nullptr; + + // Active range: + // LowPC: an offset from an applicable base address, not a PC value. + // HighPC: an offset from an applicable base address, or a length. + LVAddress LowPC = 0; + LVAddress HighPC = 0; + + void setKind(); + +public: + LVLocation() : LVObject() { setIsLocation(); } + LVLocation(const LVLocation &) = delete; + LVLocation &operator=(const LVLocation &) = delete; + virtual ~LVLocation() = default; + + PROPERTY(Property, IsAddressRange); + PROPERTY(Property, IsBaseClassOffset); + PROPERTY(Property, IsBaseClassStep); + PROPERTY_1(Property, IsClassOffset, IsLocationSimple); + PROPERTY_1(Property, IsFixedAddress, IsLocationSimple); + PROPERTY(Property, IsLocationSimple); + PROPERTY(Property, IsGapEntry); + PROPERTY(Property, IsOperationList); + PROPERTY(Property, IsOperation); + PROPERTY(Property, IsRegister); + PROPERTY_1(Property, IsStackOffset, IsLocationSimple); + PROPERTY(Property, IsDiscardedRange); + PROPERTY(Property, IsInvalidRange); + PROPERTY(Property, IsInvalidLower); + PROPERTY(Property, IsInvalidUpper); + PROPERTY(Property, IsCallSite); + +public: + const char *kind() const override; + // Mark the locations that have only DW_OP_fbreg as stack offset based. + virtual void updateKind() {} + + // Line numbers for locations. + const LVLine *getLowerLine() const { return LowerLine; } + void setLowerLine(LVLine *Line) { LowerLine = Line; } + const LVLine *getUpperLine() const { return UpperLine; } + void setUpperLine(LVLine *Line) { UpperLine = Line; } + + // Addresses for locations. + LVAddress getLowerAddress() const override { return LowPC; } + void setLowerAddress(LVAddress Address) override { LowPC = Address; } + LVAddress getUpperAddress() const override { return HighPC; } + void setUpperAddress(LVAddress Address) override { HighPC = Address; } + + std::string getIntervalInfo() const; + + bool validateRanges(); + + // In order to calculate a symbol coverage (percentage), take the ranges + // and obtain the number of units (bytes) covered by those ranges. We can't + // use the line numbers, because they can be zero or invalid. + // We return: + // false: No locations or multiple locations. + // true: a single location. + static bool calculateCoverage(LVLocations *Locations, unsigned &Factor, + float &Percentage); + + virtual void addObject(LVAddress LowPC, LVAddress HighPC, + LVUnsigned SectionOffset, uint64_t LocDescOffset) {} + virtual void addObject(LVSmall Opcode, LVUnsigned Operand1, + LVUnsigned Operand2) {} + +public: + static void print(LVLocations *Locations, raw_ostream &OS, bool Full = true); + void printInterval(raw_ostream &OS, bool Full = true) const; + void printRaw(raw_ostream &OS, bool Full = true) const; + virtual void printRawExtra(raw_ostream &OS, bool Full = true) const {} + + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +class LVLocationSymbol final : public LVLocation { + // Location descriptors for the active range. + LVAutoOperations *Entries = nullptr; + + void updateKind() override; + +public: + LVLocationSymbol() : LVLocation() {} + LVLocationSymbol(const LVLocationSymbol &) = delete; + LVLocationSymbol &operator=(const LVLocationSymbol &) = delete; + ~LVLocationSymbol() { delete Entries; }; + +public: + void addObject(LVAddress LowPC, LVAddress HighPC, LVUnsigned SectionOffset, + uint64_t LocDescOffset) override; + void addObject(LVSmall Opcode, LVUnsigned Operand1, + LVUnsigned Operand2) override; + +public: + void printRawExtra(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h @@ -0,0 +1,358 @@ +//===-- LVObject.h ----------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVObject class, which is used to describe a debug +// information object. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVOBJECT_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVOBJECT_H + +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include +#include +#include +#include + +namespace llvm { +namespace dwarf { +// Support for CodeView ModifierOptions::Unaligned. +constexpr Tag DW_TAG_unaligned = Tag(dwarf::DW_TAG_hi_user + 1); +} // namespace dwarf +} // namespace llvm + +namespace llvm { +namespace logicalview { + +using LVSectionIndex = uint64_t; +using LVAddress = uint64_t; +using LVHalf = uint16_t; +using LVLevel = uint32_t; +using LVOffset = uint64_t; +using LVSigned = int64_t; +using LVUnsigned = uint64_t; +using LVSmall = uint8_t; + +class LVElement; +class LVLine; +class LVLocation; +class LVLocationSymbol; +class LVObject; +class LVOperation; +class LVScope; +class LVSymbol; +class LVType; + +class LVOptions; +class LVPatterns; + +StringRef typeNone(); +StringRef typeVoid(); +StringRef typeInt(); +StringRef typeUnknown(); +StringRef emptyString(); + +using LVElementSetFunction = void (LVElement::*)(); +using LVElementGetFunction = bool (LVElement::*)() const; +using LVLineSetFunction = void (LVLine::*)(); +using LVLineGetFunction = bool (LVLine::*)() const; +using LVObjectSetFunction = void (LVObject::*)(); +using LVObjectGetFunction = bool (LVObject::*)() const; +using LVScopeSetFunction = void (LVScope::*)(); +using LVScopeGetFunction = bool (LVScope::*)() const; +using LVSymbolSetFunction = void (LVSymbol::*)(); +using LVSymbolGetFunction = bool (LVSymbol::*)() const; +using LVTypeSetFunction = void (LVType::*)(); +using LVTypeGetFunction = bool (LVType::*)() const; + +// The LVScope class represents a logical scope and uses vectors to store its +// children, which are pointers to other allocated logical elements (types, +// symbols, lines, scopes, ranges). On destruction, we have to traverse each +// vector and destroy its elements. The other case is LVSymbol. +// These definitions are intended to be used by the LVScope and LVSymbol +// to support automatic vector cleanup. +using LVAutoLines = LVAutoSmallVector; +using LVAutoLocations = LVAutoSmallVector; +using LVAutoOperations = LVAutoSmallVector; +using LVAutoScopes = LVAutoSmallVector; +using LVAutoSymbols = LVAutoSmallVector; +using LVAutoTypes = LVAutoSmallVector; + +// These definitions are intended to be used when the vector will be used +// just a container, with no automatic destruction. +using LVElements = SmallVector; +using LVLines = SmallVector; +using LVLocations = SmallVector; +using LVOperations = SmallVector; +using LVScopes = SmallVector; +using LVSymbols = SmallVector; +using LVTypes = SmallVector; + +using LVOffsets = SmallVector; + +const LVAddress MaxAddress = std::numeric_limits::max(); + +enum class LVBinaryType { NONE, ELF, COFF }; +enum class LVComparePass { Missing, Added }; + +// Validate functions. +using LVValidLocation = bool (LVLocation::*)(); + +// Keep counters of objects. +struct LVCounter { + unsigned Lines = 0; + unsigned Scopes = 0; + unsigned Symbols = 0; + unsigned Types = 0; + void reset() { + Lines = 0; + Scopes = 0; + Symbols = 0; + Types = 0; + } +}; + +class LVObject { + enum class Property { + IsLocation, // Location. + IsGlobalReference, // This object is being referenced from another CU. + IsGeneratedName, // The Object name was generated. + IsResolved, // Object has been resolved. + IsResolvedName, // Object name has been resolved. + IsDiscarded, // Object has been stripped by the linker. + IsOptimized, // Object has been optimized by the compiler. + IsAdded, // Object has been 'added'. + IsMatched, // Object has been matched to a given pattern. + IsMissing, // Object is 'missing'. + IsMissingLink, // Object is indirectly 'missing'. + IsInCompare, // In 'compare' mode. + IsFileFromReference, // File ID from specification. + IsLineFromReference, // Line No from specification. + HasMoved, // The object was moved from 'target' to 'reference'. + HasPattern, // The object has a pattern. + IsFinalized, // CodeView object is finalized. + IsReferenced, // CodeView object being referenced. + HasCodeViewLocation, // CodeView object with debug location. + LastEntry + }; + // Typed bitvector with properties for this object. + LVProperties Properties; + + LVOffset Offset = 0; + uint32_t LineNumber = 0; + LVLevel ScopeLevel = 0; + union { + dwarf::Tag Tag; + dwarf::Attribute Attr; + LVSmall Opcode; + } TagAttrOpcode = {dwarf::DW_TAG_null}; + + // The parent of this object (nullptr if the root scope). For locations, + // the parent it is a symbol object; otherwise is a scope object. + union { + LVElement *Element; + LVScope *Scope; + LVSymbol *Symbol; + } Parent = {nullptr}; + +protected: + // Get a string representation for the given number and discriminator. + std::string lineAsString(uint32_t LineNumber, LVHalf Discriminator, + bool ShowZero) const; + + // Get a string representation for the given number. + std::string referenceAsString(uint32_t LineNumber, bool Spaces) const; + + // Print the Filename or Pathname. + // Empty implementation for those objects that do not have any user + // source file references, such as debug locations. + virtual void printFileIndex(raw_ostream &OS, bool Full = true) const {} + +private: + // We do not support any object duplication, as they are created by parsing + // the debug information. There is only the case where we need a very basic + // object, to manipulate its offset, line number and scope level. Allow the + // copy constructor to create that object; it is used to print a reference + // to another object and in the case of templates, to print its encoded args. + LVObject(const LVObject &Object) { +#ifndef NDEBUG + incID(); +#endif + Properties = Object.Properties; + Offset = Object.Offset; + LineNumber = Object.LineNumber; + ScopeLevel = Object.ScopeLevel; + TagAttrOpcode = Object.TagAttrOpcode; + Parent = Object.Parent; + } + +public: + LVObject() { +#ifndef NDEBUG + incID(); +#endif + }; + LVObject &operator=(const LVObject &) = delete; + virtual ~LVObject() = default; + + PROPERTY(Property, IsLocation); + PROPERTY(Property, IsGlobalReference); + PROPERTY(Property, IsGeneratedName); + PROPERTY(Property, IsResolved); + PROPERTY(Property, IsResolvedName); + PROPERTY(Property, IsDiscarded); + PROPERTY(Property, IsOptimized); + PROPERTY(Property, IsAdded); + PROPERTY(Property, IsMatched); + PROPERTY(Property, IsMissing); + PROPERTY(Property, IsMissingLink); + PROPERTY(Property, IsInCompare); + PROPERTY(Property, IsFileFromReference); + PROPERTY(Property, IsLineFromReference); + PROPERTY(Property, HasMoved); + PROPERTY(Property, HasPattern); + PROPERTY(Property, IsFinalized); + PROPERTY(Property, IsReferenced); + PROPERTY(Property, HasCodeViewLocation); + +public: + // True if the scope has not been named or typed or no line number. + virtual bool isNamed() const { return false; } + virtual bool isTyped() const { return false; } + virtual bool isFiled() const { return false; } + bool isLined() const { return LineNumber != 0; } + +public: + // DWARF tag, attribute or expression opcode. + dwarf::Tag getTag() const { return TagAttrOpcode.Tag; } + void setTag(dwarf::Tag Tag) { TagAttrOpcode.Tag = Tag; } + dwarf::Attribute getAttr() const { return TagAttrOpcode.Attr; } + void setAttr(dwarf::Attribute Attr) { TagAttrOpcode.Attr = Attr; } + LVSmall getOpcode() const { return TagAttrOpcode.Opcode; } + void setOpcode(LVSmall Opcode) { TagAttrOpcode.Opcode = Opcode; } + + // DIE offset. + LVOffset getOffset() const { return Offset; } + void setOffset(LVOffset DieOffset) { Offset = DieOffset; } + + // Level where this object is located. + LVLevel getLevel() const { return ScopeLevel; } + void setLevel(LVLevel Level) { ScopeLevel = Level; } + + virtual StringRef getName() const { return StringRef(); } + virtual void setName(StringRef ObjectName) {} + + LVElement *getParent() const { + assert((!Parent.Element || + (Parent.Element && static_cast(Parent.Element))) && + "Invalid element"); + return Parent.Element; + } + LVScope *getParentScope() const { + assert((!Parent.Scope || + (Parent.Scope && static_cast(Parent.Scope))) && + "Invalid scope"); + return Parent.Scope; + } + LVSymbol *getParentSymbol() const { + assert((!Parent.Symbol || + (Parent.Symbol && static_cast(Parent.Symbol))) && + "Invalid symbol"); + return Parent.Symbol; + } + void setParent(LVScope *Scope); + void setParent(LVSymbol *Symbol); + void resetParent() { Parent = {nullptr}; } + + virtual LVAddress getLowerAddress() const { return 0; } + virtual void setLowerAddress(LVAddress Address) {} + virtual LVAddress getUpperAddress() const { return 0; } + virtual void setUpperAddress(LVAddress Address) {} + + uint32_t getLineNumber() const { return LineNumber; } + void setLineNumber(uint32_t Number) { LineNumber = Number; } + + virtual const char *kind() const { return nullptr; } + +public: + std::string indentAsString() const; + std::string indentAsString(LVLevel Level) const; + + // String used as padding for printing objects with no line number. + virtual std::string noLineAsString(bool ShowZero) const; + + // Line number for display; in the case of inlined functions, we use the + // DW_AT_call_line attribute; otherwise use DW_AT_decl_line attribute. + virtual std::string lineNumberAsString(bool ShowZero = false) const { + return lineAsString(getLineNumber(), 0, ShowZero); + } + std::string lineNumberAsStringStripped(bool ShowZero = false) const; + +public: + // This function prints the logical view to an output stream. + // Split: Prints the compilation unit view to a file. + // Match: Prints the object only if it satisfies the patterns collected + // from the command line. See the '--select' option. + // Print: Print the object only if satisfies the conditions specified by + // the different '--print' options. + // Full: Prints full information for objects representing debug locations, + // aggregated scopes, compile unit, functions and namespaces. + virtual Error doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, + bool Full = true) const; + void printAttributes(raw_ostream &OS, bool Full = true) const; + void printAttributes(raw_ostream &OS, bool Full, StringRef Name, + LVObject *Parent, StringRef Value, + bool UseQuotes = false, bool PrintRef = false) const; + + // Mark branch as missing (current element and parents). + void markBranchAsMissing(); + +public: + // Prints the common information for an object (name, type, etc). + virtual void print(raw_ostream &OS, bool Full = true) const; + // Prints additional information for an object, depending on its kind + // (class attributes, debug ranges, files, directories, etc). + virtual void printExtra(raw_ostream &OS, bool Full = true) const {} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + virtual void dump() const { print(dbgs()); } +#endif + +#ifndef NDEBUG +private: + // This is an internal ID used for debugging logical elements. It is used + // for cases where an unique offset within the binary input file is not + // available. + static uint64_t GID; + uint64_t ID = 0; + + void incID() { + ++GID; + ID = GID; + } +#endif + +public: + uint64_t getID() const { + return +#ifndef NDEBUG + ID; +#else + 0; +#endif + } +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVOBJECT_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVOptions.h @@ -0,0 +1,655 @@ +//===-- LVOptions.h ---------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVOptions class, which is used to record the command +// line options. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVOPTIONS_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVOPTIONS_H + +#include "llvm/ADT/StringSet.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/Support/Regex.h" +#include +#include + +namespace llvm { +namespace logicalview { + +// Generate get and set 'bool' functions. +#define BOOL_FUNCTION(FAMILY, FIELD) \ + bool get##FAMILY##FIELD() const { return FAMILY.FIELD; } \ + void set##FAMILY##FIELD() { FAMILY.FIELD = true; } \ + void reset##FAMILY##FIELD() { FAMILY.FIELD = false; } + +// Generate get and set 'unsigned' functions. +#define UNSIGNED_FUNCTION(FAMILY, FIELD) \ + unsigned get##FAMILY##FIELD() const { return FAMILY.FIELD; } \ + void set##FAMILY##FIELD(unsigned Value) { FAMILY.FIELD = Value; } \ + void reset##FAMILY##FIELD() { FAMILY.FIELD = -1U; } + +// Generate get and set 'std::string' functions. +#define STD_STRING_FUNCTION(FAMILY, FIELD) \ + std::string get##FAMILY##FIELD() const { return FAMILY.FIELD; } \ + void set##FAMILY##FIELD(std::string FIELD) { FAMILY.FIELD = FIELD; } \ + void reset##FAMILY##FIELD() { FAMILY.FIELD = ""; } + +// Generate get and set 'std::set' functions. +#define STDSET_FUNCTION_4(FAMILY, FIELD, TYPE, SET) \ + bool get##FAMILY##FIELD() const { \ + return FAMILY.SET.find(TYPE::FIELD) != FAMILY.SET.end(); \ + } \ + void set##FAMILY##FIELD() { FAMILY.SET.insert(TYPE::FIELD); } \ + void reset##FAMILY##FIELD() { \ + std::set::iterator Iter = FAMILY.SET.find(TYPE::FIELD); \ + if (Iter != FAMILY.SET.end()) \ + FAMILY.SET.erase(Iter); \ + } + +#define STDSET_FUNCTION_5(FAMILY, FIELD, ENTRY, TYPE, SET) \ + bool get##FAMILY##FIELD##ENTRY() const { \ + return FAMILY.SET.find(TYPE::ENTRY) != FAMILY.SET.end(); \ + } \ + void set##FAMILY##FIELD##ENTRY() { FAMILY.SET.insert(TYPE::ENTRY); } + +// Generate get and set functions for '--attribute' +#define ATTRIBUTE_OPTION(FIELD) \ + STDSET_FUNCTION_4(Attribute, FIELD, LVAttributeKind, Kinds) + +// Generate get and set functions for '--output' +#define OUTPUT_OPTION(FIELD) \ + STDSET_FUNCTION_4(Output, FIELD, LVOutputKind, Kinds) + +// Generate get and set functions for '--print' +#define PRINT_OPTION(FIELD) STDSET_FUNCTION_4(Print, FIELD, LVPrintKind, Kinds) + +// Generate get and set functions for '--warning' +#define WARNING_OPTION(FIELD) \ + STDSET_FUNCTION_4(Warning, FIELD, LVWarningKind, Kinds) + +// Generate get and set functions for '--compare' +#define COMPARE_OPTION(FIELD) \ + STDSET_FUNCTION_4(Compare, FIELD, LVCompareKind, Elements) + +// Generate get and set functions for '--report' +#define REPORT_OPTION(FIELD) \ + STDSET_FUNCTION_4(Report, FIELD, LVReportKind, Kinds) + +// Generate get and set functions for '--internal' +#define INTERNAL_OPTION(FIELD) \ + STDSET_FUNCTION_4(Internal, FIELD, LVInternalKind, Kinds) + +using LVOffsetSet = std::set; + +enum class LVAttributeKind { + All, // --attribute=all + Argument, // --attribute=argument + Base, // --attribute=base + Coverage, // --attribute=coverage + Directories, // --attribute=directories + Discarded, // --attribute=discarded + Discriminator, // --attribute=discriminator + Encoded, // --attribute=encoded + Extended, // --attribute=extended + Filename, // --attribute=filename + Files, // --attribute=files + Format, // --attribute=format + Gaps, // --attribute=gaps + Generated, // --attribute=generated + Global, // --attribute=global + Inserted, // --attribute=inserted + Level, // --attribute=level + Linkage, // --attribute=linkage + Local, // --attribute=local + Location, // --attribute=location + Offset, // --attribute=offset + Pathname, // --attribute=pathname + Producer, // --attribute=producer + Publics, // --attribute=publics + Qualified, // --attribute=qualified + Qualifier, // --attribute=qualifier + Range, // --attribute=range + Reference, // --attribute=reference + Register, // --attribute=register + Standard, // --attribute=standard + Subrange, // --attribute=subrange + System, // --attribute=system + Typename, // --attribute=typename + Underlying, // --attribute=underlying + Zero // --attribute=zero +}; +using LVAttributeKindSet = std::set; + +enum class LVCompareKind { + All, // --compare=all + Lines, // --compare=lines + Scopes, // --compare=scopes + Symbols, // --compare=symbols + Types // --compare=types +}; +using LVCompareKindSet = std::set; + +enum class LVOutputKind { + All, // --output=all + Split, // --output=split + Json, // --output=json + Text // --output=text +}; +using LVOutputKindSet = std::set; + +enum class LVPrintKind { + All, // --print=all + Elements, // --print=elements + Instructions, // --print=instructions + Lines, // --print=lines + Scopes, // --print=scopes + Sizes, // --print=sizes + Symbols, // --print=symbols + Summary, // --print=summary + Types, // --print=types + Warnings // --print=warnings +}; +using LVPrintKindSet = std::set; + +enum class LVReportKind { + All, // --report=all + Children, // --report=children + List, // --report=list + Parents, // --report=parents + View // --report=view +}; +using LVReportKindSet = std::set; + +enum class LVWarningKind { + All, // --warning=all + Coverages, // --warning=coverages + Lines, // --warning=lines + Locations, // --warning=locations + Ranges // --warning=ranges +}; +using LVWarningKindSet = std::set; + +enum class LVInternalKind { + All, // --internal=all + Cmdline, // --internal=cmdline + ID, // --internal=id + Integrity, // --internal=integrity + None, // --internal=none + Tag // --internal=tag +}; +using LVInternalKindSet = std::set; + +// The 'Kinds' members are a one-to-one mapping to the associated command +// options that supports comma separated values. There are other 'bool' +// members that in very few cases point to a command option (see associated +// comment). Other cases for 'bool' refers to internal values derivated from +// the command options. +class LVOptions { + class LVAttribute { + public: + LVAttributeKindSet Kinds; // --attribute= + bool Added = false; // Added elements found during comparison. + bool AnyLocation = false; // Any kind of location information. + bool AnySource = false; // Any kind of source information. + bool Missing = false; // Missing elements found during comparison. + }; + + class LVCompare { + public: + LVCompareKindSet Elements; // --compare= + bool Context = false; // --compare-context + bool Execute = false; // Compare requested. + bool Print = false; // Enable any printing. + }; + + class LVPrint { + public: + LVPrintKindSet Kinds; // --print= + bool AnyElement = false; // Request to print any element. + bool AnyLine = false; // Print 'lines' or 'instructions'. + bool Execute = false; // Print requested. + bool Formatting = true; // Disable formatting during printing. + bool Offset = false; // Print offsets while formatting is disabled. + bool SizesSummary = false; // Print 'sizes' or 'summary'. + }; + + class LVReport { + public: + LVReportKindSet Kinds; // --report= + bool AnyView = false; // View, Parents or Children. + bool Execute = false; // Report requested. + }; + + class LVSelect { + public: + bool IgnoreCase = false; // --select-ignore-case + bool UseRegex = false; // --select-use-regex + bool Execute = false; // Select requested. + bool GenericKind = false; // We have collected generic kinds. + bool GenericPattern = false; // We have collected generic patterns. + bool OffsetPattern = false; // We have collected offset patterns. + StringSet<> Generic; // --select= + LVOffsetSet Offsets; // --select-offset= + LVElementKindSet Elements; // --select-elements= + LVLineKindSet Lines; // --select-lines= + LVScopeKindSet Scopes; // --select-scopes= + LVSymbolKindSet Symbols; // --select-symbols= + LVTypeKindSelection Types; // --select-types= + }; + + class LVOutput { + public: + LVOutputKindSet Kinds; // --output= + LVSortMode SortMode = LVSortMode::None; // --output-sort= + std::string Folder; // --output-folder= + unsigned Level = -1U; // --output-level= + }; + + class LVWarning { + public: + LVWarningKindSet Kinds; // --warning= + }; + + class LVInternal { + public: + LVInternalKindSet Kinds; // --internal= + }; + + class LVGeneral { + public: + bool CollectRanges = false; // Collect ranges information. + }; + + // Filters the output of the filename associated with the element being + // printed in order to see clearly which logical elements belongs to + // a particular filename. It is value is reset after the element + // that represents the Compile Unit is printed. + size_t LastFilenameIndex = 0; + + // Controls the amount of additional spaces to insert when printing + // object attributes, in order to get a consistent printing layout. + size_t IndentationSize = 0; + + // Calculate the indentation size, so we can use that value when printing + // additional attributes to objects, such as location. + void calculateIndentationSize(); + +public: + void resetFilenameIndex() { LastFilenameIndex = 0; } + bool changeFilenameIndex(size_t Index) { + bool IndexChanged = (Index != LastFilenameIndex); + if (IndexChanged) + LastFilenameIndex = Index; + return IndexChanged; + } + +public: + // Access to command line options, pattern and printing information. + static LVOptions *getOptions(); + static void setOptions(LVOptions *Options); + +public: + LVOptions() = default; + LVOptions(const LVOptions &) = default; + LVOptions &operator=(const LVOptions &) = default; + ~LVOptions() = default; + + // Some command line options support shortcuts. For example: + // The command line option '--print=elements' is a shortcut for: + // '--print=instructions,lines,scopes,symbols,types'. + // In the case of logical view comparison, some options related to + // attributes must be set or reset for a proper comparison. + // Resolve any dependencies between command line options. + void resolveDependencies(); + size_t indentationSize() const { return IndentationSize; } + +public: + LVAttribute Attribute; + LVCompare Compare; + LVOutput Output; + LVPrint Print; + LVReport Report; + LVSelect Select; + LVWarning Warning; + LVInternal Internal; + LVGeneral General; + +public: + // --attribute. + ATTRIBUTE_OPTION(All); + ATTRIBUTE_OPTION(Argument); + ATTRIBUTE_OPTION(Base); + ATTRIBUTE_OPTION(Coverage); + ATTRIBUTE_OPTION(Directories); + ATTRIBUTE_OPTION(Discarded); + ATTRIBUTE_OPTION(Discriminator); + ATTRIBUTE_OPTION(Encoded); + ATTRIBUTE_OPTION(Extended); + ATTRIBUTE_OPTION(Filename); + ATTRIBUTE_OPTION(Files); + ATTRIBUTE_OPTION(Format); + ATTRIBUTE_OPTION(Gaps); + ATTRIBUTE_OPTION(Generated); + ATTRIBUTE_OPTION(Global); + ATTRIBUTE_OPTION(Inserted); + ATTRIBUTE_OPTION(Level); + ATTRIBUTE_OPTION(Linkage); + ATTRIBUTE_OPTION(Location); + ATTRIBUTE_OPTION(Local); + ATTRIBUTE_OPTION(Offset); + ATTRIBUTE_OPTION(Pathname); + ATTRIBUTE_OPTION(Producer); + ATTRIBUTE_OPTION(Publics); + ATTRIBUTE_OPTION(Qualified); + ATTRIBUTE_OPTION(Qualifier); + ATTRIBUTE_OPTION(Range); + ATTRIBUTE_OPTION(Reference); + ATTRIBUTE_OPTION(Register); + ATTRIBUTE_OPTION(Standard); + ATTRIBUTE_OPTION(Subrange); + ATTRIBUTE_OPTION(System); + ATTRIBUTE_OPTION(Typename); + ATTRIBUTE_OPTION(Underlying); + ATTRIBUTE_OPTION(Zero); + BOOL_FUNCTION(Attribute, Added); + BOOL_FUNCTION(Attribute, AnyLocation); + BOOL_FUNCTION(Attribute, AnySource); + BOOL_FUNCTION(Attribute, Missing); + + // --compare. + COMPARE_OPTION(All); + COMPARE_OPTION(Lines); + COMPARE_OPTION(Scopes); + COMPARE_OPTION(Symbols); + COMPARE_OPTION(Types); + BOOL_FUNCTION(Compare, Context); + BOOL_FUNCTION(Compare, Execute); + BOOL_FUNCTION(Compare, Print); + + // --output. + OUTPUT_OPTION(All); + OUTPUT_OPTION(Split); + OUTPUT_OPTION(Text); + OUTPUT_OPTION(Json); + STD_STRING_FUNCTION(Output, Folder); + UNSIGNED_FUNCTION(Output, Level); + LVSortMode getSortMode() const { return Output.SortMode; } + void setSortMode(LVSortMode SortMode) { Output.SortMode = SortMode; } + + // --print. + PRINT_OPTION(All); + PRINT_OPTION(Elements); + PRINT_OPTION(Instructions); + PRINT_OPTION(Lines); + PRINT_OPTION(Scopes); + PRINT_OPTION(Sizes); + PRINT_OPTION(Symbols); + PRINT_OPTION(Summary); + PRINT_OPTION(Types); + PRINT_OPTION(Warnings); + BOOL_FUNCTION(Print, AnyElement); + BOOL_FUNCTION(Print, AnyLine); + BOOL_FUNCTION(Print, Execute); + BOOL_FUNCTION(Print, Formatting); + BOOL_FUNCTION(Print, Offset); + BOOL_FUNCTION(Print, SizesSummary); + + // --report. + REPORT_OPTION(All); + REPORT_OPTION(Children); + REPORT_OPTION(List); + REPORT_OPTION(Parents); + REPORT_OPTION(View); + BOOL_FUNCTION(Report, AnyView); + BOOL_FUNCTION(Report, Execute); + + // --select. + BOOL_FUNCTION(Select, IgnoreCase); + BOOL_FUNCTION(Select, UseRegex); + BOOL_FUNCTION(Select, Execute); + BOOL_FUNCTION(Select, GenericKind); + BOOL_FUNCTION(Select, GenericPattern); + BOOL_FUNCTION(Select, OffsetPattern); + + // --warning. + WARNING_OPTION(All); + WARNING_OPTION(Coverages); + WARNING_OPTION(Lines); + WARNING_OPTION(Locations); + WARNING_OPTION(Ranges); + + // --internal. + INTERNAL_OPTION(All); + INTERNAL_OPTION(Cmdline); + INTERNAL_OPTION(ID); + INTERNAL_OPTION(Integrity); + INTERNAL_OPTION(None); + INTERNAL_OPTION(Tag); + + // General shortcuts to some combinations. + BOOL_FUNCTION(General, CollectRanges); + +public: + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +inline LVOptions &options() { return (*LVOptions::getOptions()); } +inline void setOptions(LVOptions *Options) { LVOptions::setOptions(Options); } + +class LVPatterns final { + // Pattern Mode. + enum class LVMatchMode { + None = 0, // No given pattern. + Match, // Perfect match. + NoCase, // Ignore case. + Regex // Regular expression. + }; + + // Keep the search pattern information. + struct LVMatch { + std::string Pattern; // Normal pattern. + std::shared_ptr RE; // Regular Expression Pattern. + LVMatchMode Mode = LVMatchMode::None; // Match mode. + }; + + using LVMatchInfo = std::vector; + LVMatchInfo GenericMatchInfo; + using LVMatchOffsets = std::vector; + LVMatchOffsets OffsetMatchInfo; + + // Element selection. + LVElementDispatch ElementDispatch; + LVLineDispatch LineDispatch; + LVScopeDispatch ScopeDispatch; + LVSymbolDispatch SymbolDispatch; + LVTypeDispatch TypeDispatch; + + // Element selection request. + LVElementRequest ElementRequest; + LVLineRequest LineRequest; + LVScopeRequest ScopeRequest; + LVSymbolRequest SymbolRequest; + LVTypeRequest TypeRequest; + + // Check an element printing Request. + template + bool checkElementRequest(const T *Element, const U &Requests) const { + assert(Element && "Element must not be nullptr"); + for (const auto &Request : Requests) + if ((Element->*Request)()) + return true; + // Check generic element requests. + for (const LVElementGetFunction &Request : ElementRequest) + if ((Element->*Request)()) + return true; + return false; + } + + // Add an element printing request based on its kind. + template + void addRequest(const T &Selection, const U &Dispatch, V &Request) const { + for (const auto &Entry : Selection) { + // Find target function to fullfit request. + typename U::const_iterator Iter = Dispatch.find(Entry); + if (Iter != Dispatch.end()) + Request.push_back(Iter->second); + } + } + + void addElement(LVElement *Element); + + template + void resolveGenericPatternMatch(T *Element, const U &Requests) { + assert(Element && "Element must not be nullptr"); + auto CheckPattern = [=]() -> bool { + return Element->isNamed() && + (matchGenericPattern(Element->getName()) || + matchGenericPattern(Element->getTypeName())); + }; + auto CheckOffset = [=]() -> bool { + return matchOffsetPattern(Element->getOffset()); + }; + if ((options().getSelectGenericPattern() && CheckPattern()) || + (options().getSelectOffsetPattern() && CheckOffset()) || + ((Requests.size() || ElementRequest.size()) && + checkElementRequest(Element, Requests))) + addElement(Element); + } + + template + void resolveGenericPatternMatch(LVLine *Line, const U &Requests) { + assert(Line && "Line must not be nullptr"); + auto CheckPattern = [=]() -> bool { + return matchGenericPattern(Line->lineNumberAsStringStripped()) || + matchGenericPattern(Line->getName()) || + matchGenericPattern(Line->getPathname()); + }; + auto CheckOffset = [=]() -> bool { + return matchOffsetPattern(Line->getAddress()); + }; + if ((options().getSelectGenericPattern() && CheckPattern()) || + (options().getSelectOffsetPattern() && CheckOffset()) || + (Requests.size() && checkElementRequest(Line, Requests))) + addElement(Line); + } + + Error createMatchEntry(LVMatchInfo &Filters, StringRef Pattern, + bool IgnoreCase, bool UseRegex); + +public: + static LVPatterns *getPatterns(); + +public: + LVPatterns() { + ElementDispatch = LVElement::getDispatch(); + LineDispatch = LVLine::getDispatch(); + ScopeDispatch = LVScope::getDispatch(); + SymbolDispatch = LVSymbol::getDispatch(); + TypeDispatch = LVType::getDispatch(); + } + LVPatterns(const LVPatterns &) = delete; + LVPatterns &operator=(const LVPatterns &) = delete; + ~LVPatterns() = default; + + // Clear any existing patterns. + void clear() { + GenericMatchInfo.clear(); + OffsetMatchInfo.clear(); + ElementRequest.clear(); + LineRequest.clear(); + ScopeRequest.clear(); + SymbolRequest.clear(); + TypeRequest.clear(); + + options().resetSelectGenericKind(); + options().resetSelectGenericPattern(); + options().resetSelectOffsetPattern(); + } + + void addRequest(LVElementKindSet &Selection) { + addRequest(Selection, ElementDispatch, ElementRequest); + } + void addRequest(LVLineKindSet &Selection) { + addRequest(Selection, LineDispatch, LineRequest); + } + void addRequest(LVScopeKindSet &Selection) { + addRequest(Selection, ScopeDispatch, ScopeRequest); + } + void addRequest(LVSymbolKindSet &Selection) { + addRequest(Selection, SymbolDispatch, SymbolRequest); + } + void addRequest(LVTypeKindSelection &Selection) { + addRequest(Selection, TypeDispatch, TypeRequest); + } + + void updateReportOptions(); + + bool matchPattern(StringRef Input, const LVMatchInfo &MatchInfo); + // Match a pattern (--select='pattern'). + bool matchGenericPattern(StringRef Input) { + return matchPattern(Input, GenericMatchInfo); + } + bool matchOffsetPattern(LVOffset Offset) { + return std::find(OffsetMatchInfo.begin(), OffsetMatchInfo.end(), Offset) != + OffsetMatchInfo.end(); + } + + void resolvePatternMatch(LVLine *Line) { + resolveGenericPatternMatch(Line, LineRequest); + } + + void resolvePatternMatch(LVScope *Scope) { + resolveGenericPatternMatch(Scope, ScopeRequest); + } + + void resolvePatternMatch(LVSymbol *Symbol) { + resolveGenericPatternMatch(Symbol, SymbolRequest); + } + + void resolvePatternMatch(LVType *Type) { + resolveGenericPatternMatch(Type, TypeRequest); + } + + void addPatterns(StringSet<> &Patterns, LVMatchInfo &Filters); + +public: + // Add generic and offset patterns info. + void addGenericPatterns(StringSet<> &Patterns); + void addOffsetPatterns(const LVOffsetSet &Patterns); + +public: + // Conditions to print an object. + bool printElement(const LVLine *Line) const; + bool printObject(const LVLocation *Location) const; + bool printElement(const LVScope *Scope) const; + bool printElement(const LVSymbol *Symbol) const; + bool printElement(const LVType *Type) const; + +public: + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +inline LVPatterns &patterns() { return *LVPatterns::getPatterns(); } + +} // namespace logicalview +} // namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVOPTIONS_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVRange.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVRange.h @@ -0,0 +1,102 @@ +//===-- LVRange.h -----------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVRange class, which is used to describe a debug +// information range. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVRANGE_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVRANGE_H + +#include "llvm/ADT/IntervalTree.h" +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" + +namespace llvm { +namespace logicalview { + +using LVAddressRange = std::pair; + +class LVRangeEntry final { + LVAddress Lower = 0; + LVAddress Upper = 0; + LVScope *Scope = nullptr; + +public: + using RangeType = LVAddress; + +public: + LVRangeEntry() = delete; + LVRangeEntry(LVAddress LowerAddress, LVAddress UpperAddress, LVScope *Scope) + : Lower(LowerAddress), Upper(UpperAddress), Scope(Scope) {} + +public: + RangeType lower() const { return Lower; } + RangeType upper() const { return Upper; } + LVAddressRange addressRange() const { + return LVAddressRange(lower(), upper()); + } + LVScope *scope() const { return Scope; } +}; + +// Class to represent a list of range addresses associated with a +// scope; the addresses are stored in ascending order. +using LVRangeEntries = std::vector; + +class LVRange final : public LVObject { + /// Map of where a user value is live, and its location. + using LVRangesTree = IntervalTree; + using LVAllocator = LVRangesTree::Allocator; + + LVAllocator Allocator; + LVRangesTree RangesTree; + LVRangeEntries RangeEntries; + LVAddress Lower = MaxAddress; + LVAddress Upper = 0; + +public: + LVRange() : LVObject(), RangesTree(Allocator) {} + LVRange(const LVRange &) = delete; + LVRange &operator=(const LVRange &) = delete; + ~LVRange() = default; + +public: + void addEntry(LVScope *Scope, LVAddress LowerAddress, LVAddress UpperAddress); + void addEntry(LVScope *Scope); + LVScope *getEntry(LVAddress Address) const; + LVScope *getEntry(LVAddress LowerAddress, LVAddress UpperAddress) const; + bool findEntry(LVAddress Low, LVAddress High) const; + LVAddress getLower() const { return Lower; } + LVAddress getUpper() const { return Upper; } + + const LVRangeEntries &getEntries() const { return RangeEntries; } + + void clear() { + RangeEntries.clear(); + Lower = MaxAddress; + Upper = 0; + } + bool empty() const { return RangeEntries.empty(); } + void sort(); + + void startSearch(); + void endSearch() {} + +public: + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override {} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVRANGE_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h @@ -0,0 +1,234 @@ +//===-- LVReader.h ----------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVReader class, which is used to describe a debug +// information reader. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVREADER_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVREADER_H + +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVRange.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/ToolOutputFile.h" +#include + +namespace llvm { +namespace logicalview { + +constexpr LVSectionIndex UndefinedSectionIndex = 0; + +class LVScopeCompileUnit; +class LVObject; + +class LVSplitContext final { + std::unique_ptr OutputFile; + std::string Location; + +public: + LVSplitContext() = default; + LVSplitContext(const LVSplitContext &) = delete; + LVSplitContext &operator=(const LVSplitContext &) = delete; + ~LVSplitContext() = default; + +public: + Error createSplitFolder(StringRef Where); + std::error_code open(std::string Name, std::string Extension, + raw_ostream &OS); + void close() { + if (OutputFile) { + OutputFile->os().close(); + OutputFile = nullptr; + } + } + + std::string getLocation() const { return Location; } + raw_fd_ostream &os() { return OutputFile->os(); } +}; + +class LVReader { + LVBinaryType BinaryType; + + // Context used by '--output=split' command line option. + LVSplitContext SplitContext; + + // Compile Units DIE Offset => Scope. + using LVCompileUnits = std::map; + LVCompileUnits CompileUnits; + + // Added elements to be used during elements comparison. + LVLines Lines; + LVScopes Scopes; + LVSymbols Symbols; + LVTypes Types; + + // Create split folder. + Error createSplitFolder(); + bool OutputSplit = false; + +protected: + LVScopeRoot *Root = nullptr; + std::string InputFilename; + std::string FileFormatName; + ScopedPrinter &W; + raw_ostream &OS; + LVScopeCompileUnit *CompileUnit = nullptr; + + // Only for ELF format. The CodeView is handled in a different way. + LVSectionIndex DotTextSectionIndex = UndefinedSectionIndex; + + // Record Compilation Unit entry. + void addCompileUnitOffset(LVOffset Offset, LVScopeCompileUnit *CompileUnit) { + CompileUnits.emplace(Offset, CompileUnit); + } + + // Create the Scope Root. + virtual Error createScopes() { + Root = new LVScopeRoot(); + Root->setName(getFilename()); + if (options().getAttributeFormat()) + Root->setFileFormatName(FileFormatName); + return Error::success(); + } + + virtual Error printScopes(); + virtual Error printMatchedElements(bool UseMatchedElements); + virtual void sortScopes() {} + +public: + LVReader() = delete; + LVReader(StringRef InputFilename, StringRef FileFormatName, ScopedPrinter &W, + LVBinaryType BinaryType = LVBinaryType::NONE) + : BinaryType(BinaryType), OutputSplit(options().getOutputSplit()), + InputFilename(InputFilename), FileFormatName(FileFormatName), W(W), + OS(W.getOStream()) {} + LVReader(const LVReader &) = delete; + LVReader &operator=(const LVReader &) = delete; + virtual ~LVReader() { + if (Root) + delete Root; + } + + StringRef getFilename(LVObject *Object, size_t Index) const; + StringRef getFilename() const { return InputFilename; } + void setFilename(std::string Name) { InputFilename = std::move(Name); } + StringRef getFileFormatName() const { return FileFormatName; } + + raw_ostream &outputStream() { return OS; } + + bool isBinaryTypeNone() const { return BinaryType == LVBinaryType::NONE; } + bool isBinaryTypeELF() const { return BinaryType == LVBinaryType::ELF; } + bool isBinaryTypeCOFF() const { return BinaryType == LVBinaryType::COFF; } + +public: + LVScopeCompileUnit *getCompileUnit() const { return CompileUnit; } + void setCompileUnit(LVScope *Scope) { + assert(Scope && Scope->isCompileUnit() && "Scope is not a compile unit"); + CompileUnit = static_cast(Scope); + } + void setCompileUnitCPUType(codeview::CPUType Type) { + CompileUnit->setCPUType(Type); + } + codeview::CPUType getCompileUnitCPUType() { + return CompileUnit->getCPUType(); + } + +public: + // Access to the scopes root. + LVScopeRoot *getScopesRoot() const { return Root; } + + Error doPrint(); + Error doLoad(); + + virtual std::string getRegisterName(LVSmall Opcode, uint64_t Operands[2]) { + llvm_unreachable("Invalid instance reader."); + return {}; + } + + LVSectionIndex getDotTextSectionIndex() const { return DotTextSectionIndex; } + virtual LVSectionIndex getSectionIndex(LVScope *Scope) { + return getDotTextSectionIndex(); + } + + virtual bool isSystemEntry(LVElement *Element, StringRef Name = {}) const { + return false; + }; + + // Access to split context. + LVSplitContext &getSplitContext() { return SplitContext; } + + // In the case of element comparison, register that added element. + void notifyAddedElement(LVLine *Line) { + if (!options().getCompareContext() && options().getCompareLines()) + Lines.push_back(Line); + } + void notifyAddedElement(LVScope *Scope) { + if (!options().getCompareContext() && options().getCompareScopes()) + Scopes.push_back(Scope); + } + void notifyAddedElement(LVSymbol *Symbol) { + if (!options().getCompareContext() && options().getCompareSymbols()) + Symbols.push_back(Symbol); + } + void notifyAddedElement(LVType *Type) { + if (!options().getCompareContext() && options().getCompareTypes()) + Types.push_back(Type); + } + + const LVLines &getLines() const { return Lines; } + const LVScopes &getScopes() const { return Scopes; } + const LVSymbols &getSymbols() const { return Symbols; } + const LVTypes &getTypes() const { return Types; } + +public: + // Conditions to print an object. + bool doPrintLine(const LVLine *Line) const { + return patterns().printElement(Line); + } + bool doPrintLocation(const LVLocation *Location) const { + return patterns().printObject(Location); + } + bool doPrintScope(const LVScope *Scope) const { + return patterns().printElement(Scope); + } + bool doPrintSymbol(const LVSymbol *Symbol) const { + return patterns().printElement(Symbol); + } + bool doPrintType(const LVType *Type) const { + return patterns().printElement(Type); + } + +public: + static LVReader &getInstance(); + static void setInstance(LVReader *Reader); + +public: + void print(raw_ostream &OS) const; + virtual void printRecords(raw_ostream &OS) const {}; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +inline LVReader &getReader() { return LVReader::getInstance(); } +inline LVSplitContext &getReaderSplitContext() { + return getReader().getSplitContext(); +} +inline LVScopeCompileUnit *getReaderCompileUnit() { + return getReader().getCompileUnit(); +} + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVREADER_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h @@ -0,0 +1,827 @@ +//===-- LVScope.h -----------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVScope class, which is used to describe a debug +// information scope. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSCOPE_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSCOPE_H + +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSort.h" +#include "llvm/Object/ObjectFile.h" +#include +#include +#include + +namespace llvm { +namespace logicalview { + +// Name address, Code size. +using LVNameInfo = std::pair; +using LVPublicNames = std::map; +using LVPublicAddresses = std::map; + +class LVRange; + +enum class LVScopeKind { + IsAggregate, + IsArray, + IsBlock, + IsCallSite, + IsCatchBlock, + IsClass, + IsCompileUnit, + IsEntryPoint, + IsEnumeration, + IsFunction, + IsFunctionType, + IsInlinedFunction, + IsLabel, + IsLexicalBlock, + IsMember, + IsNamespace, + IsRoot, + IsStructure, + IsSubprogram, + IsTemplate, + IsTemplateAlias, + IsTemplatePack, + IsTryBlock, + IsUnion, + LastEntry +}; +using LVScopeKindSet = std::set; +using LVScopeDispatch = std::map; +using LVScopeRequest = std::vector; + +using LVOffsetList = std::list; +using LVOffsetElementMap = std::map; +using LVOffsetLinesMap = std::map; +using LVOffsetLocationsMap = std::map; +using LVOffsetSymbolMap = std::map; +using LVTagOffsetsMap = std::map; + +// Class to represent a DWARF Scope. +class LVScope : public LVElement { + enum class Property { + HasDiscriminator, + CanHaveRanges, + CanHaveLines, + HasGlobals, + HasLocals, + HasLines, + HasScopes, + HasSymbols, + HasTypes, + IsComdat, + HasComdatScopes, // Compile Unit has comdat functions. + HasRanges, + AddedMissing, // Added missing referenced symbols. + LastEntry + }; + + // Typed bitvector with kinds and properties for this scope. + LVProperties Kinds; + LVProperties Properties; + static LVScopeDispatch Dispatch; + + // Coverage factor in units (bytes). + unsigned CoverageFactor = 0; + + // Calculate coverage factor. + void calculateCoverage() { + float CoveragePercentage = 0; + LVLocation::calculateCoverage(Ranges, CoverageFactor, CoveragePercentage); + } + + // Decide if the scope will be printed, using some conditions given by: + // only-globals, only-locals, a-pattern. + bool resolvePrinting() const; + + // Find the 'current' scope in the 'Targets'. + LVScope *findIn(const LVScopes *Targets) const; + + // Traverse the scope parent tree, executing the given callback function + // on each scope. + void traverseParents(LVScopeGetFunction GetFunction, + LVScopeSetFunction SetFunction); + +protected: + // Types, Symbols, Scopes, Lines, Locations in this scope. + LVAutoTypes *Types = nullptr; + LVAutoSymbols *Symbols = nullptr; + LVAutoScopes *Scopes = nullptr; + LVAutoLines *Lines = nullptr; + LVAutoLocations *Ranges = nullptr; + + // Vector of elements (types, scopes and symbols). + // It is the union of (*Types, *Symbols and *Scopes) to be used for + // the following reasons: + // - Preserve the order the logical elements are read in. + // - To have a single container with all the logical elements, when + // the traversal does not require any specific element kind. + LVElements *Children = nullptr; + + // Resolve the template parameters/arguments relationship. + void resolveTemplate(); + void printEncodedArgs(raw_ostream &OS, bool Full) const; + + void printActiveRanges(raw_ostream &OS, bool Full = true); + void printActiveRanges(raw_ostream &OS, bool Full = true) const { + (const_cast(this))->printActiveRanges(OS, Full); + } + virtual void printSizes(raw_ostream &OS) const {}; + virtual void printSummary(raw_ostream &OS) const {}; + + // Encoded template arguments. + virtual StringRef getEncodedArgs() const { return StringRef(); } + virtual void setEncodedArgs(StringRef EncodedArgs) {} + +public: + LVScope() : LVElement(LVSubclassID::LV_SCOPE) { + setIsScope(); + setIncludeInPrint(); + } + LVScope(const LVScope &) = delete; + LVScope &operator=(const LVScope &) = delete; + virtual ~LVScope(); + + static bool classof(const LVElement *Element) { + return Element->getSubclassID() == LVSubclassID::LV_SCOPE; + } + + KIND(LVScopeKind, IsAggregate); + KIND(LVScopeKind, IsArray); + KIND_2(LVScopeKind, IsBlock, CanHaveRanges, CanHaveLines); + KIND_1(LVScopeKind, IsCallSite, IsFunction); + KIND_1(LVScopeKind, IsCatchBlock, IsBlock); + KIND_1(LVScopeKind, IsClass, IsAggregate); + KIND_3(LVScopeKind, IsCompileUnit, CanHaveRanges, CanHaveLines, + TransformName); + KIND_1(LVScopeKind, IsEntryPoint, IsFunction); + KIND(LVScopeKind, IsEnumeration); + KIND_2(LVScopeKind, IsFunction, CanHaveRanges, CanHaveLines); + KIND_1(LVScopeKind, IsFunctionType, IsFunction); + KIND_2(LVScopeKind, IsInlinedFunction, IsFunction, IsInlined); + KIND_1(LVScopeKind, IsLabel, IsFunction); + KIND_1(LVScopeKind, IsLexicalBlock, IsBlock); + KIND(LVScopeKind, IsMember); + KIND(LVScopeKind, IsNamespace); + KIND_1(LVScopeKind, IsRoot, TransformName); + KIND_1(LVScopeKind, IsStructure, IsAggregate); + KIND_1(LVScopeKind, IsSubprogram, IsFunction); + KIND(LVScopeKind, IsTemplate); + KIND(LVScopeKind, IsTemplateAlias); + KIND(LVScopeKind, IsTemplatePack); + KIND_1(LVScopeKind, IsTryBlock, IsBlock); + KIND_1(LVScopeKind, IsUnion, IsAggregate); + + PROPERTY(Property, HasDiscriminator); + PROPERTY(Property, CanHaveRanges); + PROPERTY(Property, CanHaveLines); + PROPERTY(Property, HasGlobals); + PROPERTY(Property, HasLocals); + PROPERTY(Property, HasLines); + PROPERTY(Property, HasScopes); + PROPERTY(Property, HasSymbols); + PROPERTY(Property, HasTypes); + PROPERTY(Property, IsComdat); + PROPERTY(Property, HasComdatScopes); + PROPERTY(Property, HasRanges); + PROPERTY(Property, AddedMissing); + +public: + bool isCompileUnit() const override { return getIsCompileUnit(); } + bool isRoot() const override { return getIsRoot(); } + + const char *kind() const override; + + // Get the specific children. + const LVLines *getLines() const { return Lines; } + const LVLocations *getRanges() const { return Ranges; } + const LVScopes *getScopes() const { return Scopes; } + const LVSymbols *getSymbols() const { return Symbols; } + const LVTypes *getTypes() const { return Types; } + const LVElements *getChildren() const { return Children; } + + void addElement(LVElement *Element); + void addElement(LVLine *Line); + void addElement(LVScope *Scope); + void addElement(LVSymbol *Symbol); + void addElement(LVType *Type); + void addObject(LVLocation *Location); + void addObject(LVAddress LowerAddress, LVAddress UpperAddress); + void addToChildren(LVElement *Element); + + // Add the missing elements from the given 'Reference', which is the + // scope associated with any DW_AT_specification, DW_AT_abstract_origin. + void addMissingElements(LVScope *Reference); + + // Traverse the scope parent tree and the children, executing the given + // callback function on each element. + void traverseParentsAndChildren(LVObjectGetFunction GetFunction, + LVObjectSetFunction SetFunction); + + // Get the size of specific children. + size_t lineCount() const { return Lines ? Lines->size() : 0; } + size_t rangeCount() const { return Ranges ? Ranges->size() : 0; } + size_t scopeCount() const { return Scopes ? Scopes->size() : 0; } + size_t symbolCount() const { return Symbols ? Symbols->size() : 0; } + size_t typeCount() const { return Types ? Types->size() : 0; } + + // Calculate coverage factor. + void CalculateCoverageFactor() { + float CoveragePercentage = 0; + LVLocation::calculateCoverage(Ranges, CoverageFactor, CoveragePercentage); + } + + // Find containing parent for the given address. + LVScope *outermostParent(LVAddress Address); + + // Get all the locations associated with symbols. + void getLocations(LVLocations &LocationList, LVValidLocation ValidLocation, + bool RecordInvalid = false); + void getRanges(LVLocations &LocationList, LVValidLocation ValidLocation, + bool RecordInvalid = false); + void getRanges(LVRange &RangeList); + + unsigned getCoverageFactor() const { return CoverageFactor; } + + Error doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, + bool Full = true) const override; + // Sort the logical elements using the criteria specified by the + // command line option '--output-sort'. + void sort(); + + // Get template parameter types. + bool getTemplateParameterTypes(LVTypes &Params); + + // DW_AT_specification, DW_AT_abstract_origin, DW_AT_extension. + virtual LVScope *getReference() const { return nullptr; } + + LVScope *getCompileUnitParent() const override { + return LVElement::getCompileUnitParent(); + } + + // Follow a chain of references given by DW_AT_abstract_origin and/or + // DW_AT_specification and update the scope name. + StringRef resolveReferencesChain(); + + bool removeElement(LVElement *Element) override; + void updateLevel(LVScope *Parent, bool Moved) override; + + void resolve() override; + void resolveName() override; + void resolveReferences() override; + + // Return the chain of parents as a string. + void getQualifiedName(std::string &QualifiedName) const; + // Encode the template arguments. + void encodeTemplateArguments(std::string &Name) const; + void encodeTemplateArguments(std::string &Name, const LVTypes *Types) const; + + void resolveElements(); + +public: + // Iterate through the 'References' set and check that all its elements + // are present in the 'Targets' set. For a missing element, mark its + // parents as missing. + static void markMissingParents(const LVScopes *References, + const LVScopes *Targets, + bool TraverseChildren); + + // Checks if the current scope is contained within the target scope. + // Depending on the result, the callback may be performed. + virtual void markMissingParents(const LVScope *Target, bool TraverseChildren); + + // Returns true if 'current' scope is equal to the given 'scope'. + virtual bool equalNumberOfChildren(const LVScope *Scope) const; + virtual bool equals(const LVScope *Scope) const; + virtual LVScope *equals(const LVScopes *Scopes) const; + static bool equals(const LVScopes *References, const LVScopes *Targets); + void report(LVComparePass Pass) override; + +public: + static LVScopeDispatch &getDispatch() { return Dispatch; } + +public: + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + virtual void printWarnings(raw_ostream &OS, bool Full = true) const {}; + virtual void printMatchedElements(raw_ostream &OS, bool UseMatchedElements){}; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +// Class to represent a DWARF Union/Structure/Class. +class LVScopeAggregate final : public LVScope { + LVScope *Reference = nullptr; // DW_AT_specification, DW_AT_abstract_origin. + size_t EncodedArgsIndex = 0; // Template encoded arguments. + +public: + LVScopeAggregate() : LVScope() {} + LVScopeAggregate(const LVScopeAggregate &) = delete; + LVScopeAggregate &operator=(const LVScopeAggregate &) = delete; + ~LVScopeAggregate() = default; + + // DW_AT_specification, DW_AT_abstract_origin. + LVScope *getReference() const override { return Reference; } + void setReference(LVScope *Scope) override { + Reference = Scope; + setHasReference(); + } + void setReference(LVElement *Element) override { + setReference(static_cast(Element)); + } + + StringRef getEncodedArgs() const override { + return getStringPool().getString(EncodedArgsIndex); + } + void setEncodedArgs(StringRef EncodedArgs) override { + EncodedArgsIndex = getStringPool().getIndex(EncodedArgs); + } + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + LVScope *equals(const LVScopes *Scopes) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF Template alias. +class LVScopeAlias final : public LVScope { +public: + LVScopeAlias() : LVScope() { + setIsTemplateAlias(); + setIsTemplate(); + } + LVScopeAlias(const LVScopeAlias &) = delete; + LVScopeAlias &operator=(const LVScopeAlias &) = delete; + ~LVScopeAlias() = default; + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF array (DW_TAG_array_type). +class LVScopeArray final : public LVScope { +public: + LVScopeArray() : LVScope() { setIsArray(); } + LVScopeArray(const LVScopeArray &) = delete; + LVScopeArray &operator=(const LVScopeArray &) = delete; + ~LVScopeArray() = default; + + void resolveExtra() override; + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF Compilation Unit (CU). +class LVScopeCompileUnit final : public LVScope { + // Names (files and directories) used by the Compile Unit. + std::vector Filenames; + + // As the .debug_pubnames section has been removed in DWARF5, we have a + // similar functionality, which is used by the decoded functions. We use + // the low-pc and high-pc for those scopes that are marked as public, in + // order to support DWARF and CodeView. + LVPublicNames PublicNames; + + // Toolchain producer. + size_t ProducerIndex = 0; + + // Compilation directory name. + size_t CompilationDirectoryIndex = 0; + + // Used by the CodeView Reader. + codeview::CPUType CompilationCPUType = codeview::CPUType::X64; + + // Keep record of elements. They are needed at the compilation unit level + // to print the summary at the end of the printing. + LVCounter Allocated; + LVCounter Found; + LVCounter Printed; + + // Elements that match a given command line pattern. + LVElements MatchedElements; + LVScopes MatchedScopes; + + // It records the mapping between logical lines representing a debug line + // entry and its address in the text section. It is used to find a line + // giving its exact or closest address. To support comdat functions, all + // addresses for the same section are recorded in the same map. + using LVAddressToLine = std::map; + LVDoubleMap SectionMappings; + + // DWARF Tags (Tag, Element list). + LVTagOffsetsMap DebugTags; + + // Offsets associated with objects being flagged as having invalid data + // (ranges, locations, lines zero or coverages). + LVOffsetElementMap WarningOffsets; + + // Symbols with invalid locations. (Symbol, Location List). + LVOffsetLocationsMap InvalidLocations; + + // Symbols with invalid coverage values. + LVOffsetSymbolMap InvalidCoverages; + + // Scopes with invalid ranges (Scope, Range list). + LVOffsetLocationsMap InvalidRanges; + + // Scopes with lines zero (Scope, Line list). + LVOffsetLinesMap LinesZero; + + // Record scopes contribution in bytes to the debug information. + using LVSizesMap = std::map; + LVSizesMap Sizes; + LVOffset CUContributionSize = 0; + + // Helper function to add an invalid location/range. + void addInvalidLocationOrRange(LVLocation *Location, LVElement *Element, + LVOffsetLocationsMap *Map) { + LVOffset Offset = Element->getOffset(); + addInvalidOffset(Offset, Element); + addItem( + Map, Offset, Location); + } + + // Record scope sizes indexed by lexical level. + // Setting an initial size that will cover a very deep nested scopes. + const size_t TotalInitialSize = 8; + using LVTotalsEntry = std::pair; + SmallVector Totals; + // Maximum seen lexical level. It is used to control how many entries + // in the 'Totals' vector are valid values. + LVLevel MaxSeenLevel = 0; + + // Get the line located at the given address. + LVLine *lineLowerBound(LVAddress Address, LVScope *Scope) const; + LVLine *lineUpperBound(LVAddress Address, LVScope *Scope) const; + + void printScopeSize(LVScope *Scope, raw_ostream &OS); + void printScopeSize(LVScope *Scope, raw_ostream &OS) const { + (const_cast(this))->printScopeSize(Scope, OS); + } + void printTotals(raw_ostream &OS) const; + +protected: + void printSizes(raw_ostream &OS) const override; + void printSummary(raw_ostream &OS) const override; + +public: + LVScopeCompileUnit() : LVScope(), Totals(TotalInitialSize, {0, 0.0}) { + setIsCompileUnit(); + } + LVScopeCompileUnit(const LVScopeCompileUnit &) = delete; + LVScopeCompileUnit &operator=(const LVScopeCompileUnit &) = delete; + ~LVScopeCompileUnit() { + deleteList(DebugTags); + deleteList(InvalidLocations); + deleteList(InvalidRanges); + deleteList(LinesZero); + } + + LVScope *getCompileUnitParent() const override { + return static_cast(const_cast(this)); + } + + // Add line to address mapping. + void addMapping(LVLine *Line, LVSectionIndex SectionIndex); + LVLineRange lineRange(LVLocation *Location) const; + + LVNameInfo NameNone = {UINT64_MAX, 0}; + void addPublicName(LVScope *Scope, LVAddress LowPC, LVAddress HighPC) { + PublicNames.emplace(std::piecewise_construct, std::forward_as_tuple(Scope), + std::forward_as_tuple(LowPC, HighPC - LowPC)); + } + const LVNameInfo &findPublicName(LVScope *Scope) { + LVPublicNames::iterator Iter = PublicNames.find(Scope); + return (Iter != PublicNames.end()) ? Iter->second : NameNone; + } + const LVPublicNames &getPublicNames() const { return PublicNames; } + + // The base address of the scope for any of the debugging information + // entries listed, is given by either the DW_AT_low_pc attribute or the + // first address in the first range entry in the list of ranges given by + // the DW_AT_ranges attribute. + LVAddress getBaseAddress() const { + return Ranges ? Ranges->front()->getLowerAddress() : 0; + } + + StringRef getCompilationDirectory() const { + return getStringPool().getString(CompilationDirectoryIndex); + } + void setCompilationDirectory(StringRef CompilationDirectory) { + CompilationDirectoryIndex = getStringPool().getIndex(CompilationDirectory); + } + + StringRef getFilename(size_t Index) const; + void addFilename(StringRef Name) { + Filenames.push_back(getStringPool().getIndex(Name)); + } + + StringRef getProducer() const override { + return getStringPool().getString(ProducerIndex); + } + void setProducer(StringRef ProducerName) override { + ProducerIndex = getStringPool().getIndex(ProducerName); + } + + void setCPUType(codeview::CPUType Type) { CompilationCPUType = Type; } + codeview::CPUType getCPUType() { return CompilationCPUType; } + + // Record DWARF tags. + void addDebugTag(dwarf::Tag Target, LVOffset Offset); + // Record elements with invalid offsets. + void addInvalidOffset(LVOffset Offset, LVElement *Element); + // Record symbols with invalid coverage values. + void addInvalidCoverage(LVSymbol *Symbol); + // Record symbols with invalid locations. + void addInvalidLocation(LVLocation *Location); + // Record scopes with invalid ranges. + void addInvalidRange(LVLocation *Location); + // Record line zero. + void addLineZero(LVLine *Line); + + const LVTagOffsetsMap getDebugTags() const { return DebugTags; } + const LVOffsetElementMap getWarningOffsets() const { return WarningOffsets; } + const LVOffsetLocationsMap getInvalidLocations() const { + return InvalidLocations; + } + const LVOffsetSymbolMap getInvalidCoverages() const { + return InvalidCoverages; + } + const LVOffsetLocationsMap getInvalidRanges() const { return InvalidRanges; } + const LVOffsetLinesMap getLinesZero() const { return LinesZero; } + + // Process ranges, locations and calculate coverage. + void processRangeLocationCoverage( + LVValidLocation ValidLocation = &LVLocation::validateRanges); + + // Add matched element. + void addMatched(LVElement *Element) { MatchedElements.push_back(Element); } + void addMatched(LVScope *Scope) { MatchedScopes.push_back(Scope); } + void propagatePatternMatch(); + + const LVElements &getMatchedElements() const { return MatchedElements; } + const LVScopes &getMatchedScopes() const { return MatchedScopes; } + + void printLocalNames(raw_ostream &OS, bool Full = true) const; + void printSummary(raw_ostream &OS, const LVCounter &Counter, + const char *Header) const; + + void incrementPrintedLines(); + void incrementPrintedScopes(); + void incrementPrintedSymbols(); + void incrementPrintedTypes(); + + // Values are used by '--summary' option (allocated). + void increment(LVLine *Line); + void increment(LVScope *Scope); + void increment(LVSymbol *Symbol); + void increment(LVType *Type); + + // A new element has been added to the scopes tree. Take the following steps: + // Increase the added element counters, for printing summary. + // Notify the Reader if element comparison. + void addedElement(LVLine *Line); + void addedElement(LVScope *Scope); + void addedElement(LVSymbol *Symbol); + void addedElement(LVType *Type); + + void addSize(LVScope *Scope, LVOffset Lower, LVOffset Upper); + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + +public: + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + void printWarnings(raw_ostream &OS, bool Full = true) const override; + void printMatchedElements(raw_ostream &OS, bool UseMatchedElements) override; +}; + +// Class to represent a DWARF enumerator (DW_TAG_enumeration_type). +class LVScopeEnumeration final : public LVScope { +public: + LVScopeEnumeration() : LVScope() { setIsEnumeration(); } + LVScopeEnumeration(const LVScopeEnumeration &) = delete; + LVScopeEnumeration &operator=(const LVScopeEnumeration &) = delete; + ~LVScopeEnumeration() = default; + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF formal parameter pack +// (DW_TAG_GNU_formal_parameter_pack). +class LVScopeFormalPack final : public LVScope { +public: + LVScopeFormalPack() : LVScope() { setIsTemplatePack(); } + LVScopeFormalPack(const LVScopeFormalPack &) = delete; + LVScopeFormalPack &operator=(const LVScopeFormalPack &) = delete; + ~LVScopeFormalPack() = default; + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF Function. +class LVScopeFunction : public LVScope { + LVScope *Reference = nullptr; // DW_AT_specification, DW_AT_abstract_origin. + size_t LinkageNameIndex = 0; // Function DW_AT_linkage_name attribute. + size_t EncodedArgsIndex = 0; // Template encoded arguments. + +public: + LVScopeFunction() : LVScope() {} + LVScopeFunction(const LVScopeFunction &) = delete; + LVScopeFunction &operator=(const LVScopeFunction &) = delete; + virtual ~LVScopeFunction() = default; + + // DW_AT_specification, DW_AT_abstract_origin. + LVScope *getReference() const override { return Reference; } + void setReference(LVScope *Scope) override { + Reference = Scope; + setHasReference(); + } + void setReference(LVElement *Element) override { + setReference(static_cast(Element)); + } + + StringRef getEncodedArgs() const override { + return getStringPool().getString(EncodedArgsIndex); + } + void setEncodedArgs(StringRef EncodedArgs) override { + EncodedArgsIndex = getStringPool().getIndex(EncodedArgs); + } + + void setLinkageName(StringRef LinkageName) override { + LinkageNameIndex = getStringPool().getIndex(LinkageName); + } + StringRef getLinkageName() const override { + return getStringPool().getString(LinkageNameIndex); + } + size_t getLinkageNameIndex() const override { return LinkageNameIndex; } + + void setName(StringRef ObjectName) override; + + void resolveExtra() override; + void resolveReferences() override; + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + LVScope *equals(const LVScopes *Scopes) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF inlined function. +class LVScopeFunctionInlined final : public LVScopeFunction { + size_t CallFilenameIndex = 0; + uint32_t CallLineNumber = 0; + uint32_t Discriminator = 0; + +public: + LVScopeFunctionInlined() : LVScopeFunction() { setIsInlinedFunction(); } + LVScopeFunctionInlined(const LVScopeFunctionInlined &) = delete; + LVScopeFunctionInlined &operator=(const LVScopeFunctionInlined &) = delete; + ~LVScopeFunctionInlined() = default; + + uint32_t getDiscriminator() const override { return Discriminator; } + void setDiscriminator(uint32_t Value) override { + Discriminator = Value; + setHasDiscriminator(); + } + + uint32_t getCallLineNumber() const override { return CallLineNumber; } + void setCallLineNumber(uint32_t Number) override { CallLineNumber = Number; } + size_t getCallFilenameIndex() const override { return CallFilenameIndex; } + void setCallFilenameIndex(size_t Index) override { + CallFilenameIndex = Index; + } + + // Line number for display; in the case of Inlined Functions, we use the + // DW_AT_call_line attribute; otherwise use DW_AT_decl_line attribute. + std::string lineNumberAsString(bool ShowZero = false) const override { + return lineAsString(getCallLineNumber(), getDiscriminator(), ShowZero); + } + + void resolveExtra() override; + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + LVScope *equals(const LVScopes *Scopes) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF subroutine function. +class LVScopeFunctionType final : public LVScopeFunction { +public: + LVScopeFunctionType() : LVScopeFunction() { setIsFunctionType(); } + LVScopeFunctionType(const LVScopeFunctionType &) = delete; + LVScopeFunctionType &operator=(const LVScopeFunctionType &) = delete; + ~LVScopeFunctionType() = default; + + void resolveExtra() override; +}; + +// Class to represent a DWARF Namespace. +class LVScopeNamespace final : public LVScope { + LVScope *Reference = nullptr; // Reference to DW_AT_extension attribute. + +public: + LVScopeNamespace() : LVScope() { setIsNamespace(); } + LVScopeNamespace(const LVScopeNamespace &) = delete; + LVScopeNamespace &operator=(const LVScopeNamespace &) = delete; + ~LVScopeNamespace() = default; + + // Access DW_AT_extension reference. + LVScope *getReference() const override { return Reference; } + void setReference(LVScope *Scope) override { + Reference = Scope; + setHasReference(); + } + void setReference(LVElement *Element) override { + setReference(static_cast(Element)); + } + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + LVScope *equals(const LVScopes *Scopes) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +class LVScopeRoot final : public LVScope { + size_t FileFormatNameIndex = 0; + +public: + LVScopeRoot() : LVScope() { setIsRoot(); } + LVScopeRoot(const LVScopeRoot &) = delete; + LVScopeRoot &operator=(const LVScopeRoot &) = delete; + ~LVScopeRoot() = default; + + StringRef getFileFormatName() const { + return getStringPool().getString(FileFormatNameIndex); + } + void setFileFormatName(StringRef FileFormatName) { + FileFormatNameIndex = getStringPool().getIndex(FileFormatName); + } + + // The CodeView Reader uses scoped names. Recursively transform the + // element name to use just the most inner component. + void transformScopedName(); + + // Process the collected location, ranges and calculate coverage. + void processRangeInformation(); + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + Error doPrintMatches(bool Split, raw_ostream &OS, + bool UseMatchedElements) const; +}; + +// Class to represent a DWARF template parameter pack +// (DW_TAG_GNU_template_parameter_pack). +class LVScopeTemplatePack final : public LVScope { +public: + LVScopeTemplatePack() : LVScope() { setIsTemplatePack(); } + LVScopeTemplatePack(const LVScopeTemplatePack &) = delete; + LVScopeTemplatePack &operator=(const LVScopeTemplatePack &) = delete; + ~LVScopeTemplatePack() = default; + + // Returns true if 'current' scope is equal to the given 'scope'. + bool equals(const LVScope *Scope) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSCOPE_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVSort.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVSort.h @@ -0,0 +1,51 @@ +//===-- LVSort.h ------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the sort algorithms. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSORT_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSORT_H + +namespace llvm { +namespace logicalview { + +class LVObject; + +// Object Sorting Mode. +enum class LVSortMode { + None = 0, // No given sort. + Kind, // Sort by kind. + Line, // Sort by line. + Name, // Sort by name. + Offset // Sort by offset. +}; + +// Type of function to be called when sorting an object. +using LVSortValue = int; +using LVSortFunction = LVSortValue (*)(const LVObject *LHS, + const LVObject *RHS); + +// Get the comparator function, based on the command line options. +LVSortFunction getSortFunction(); + +// Comparator functions that can be used for sorting. +LVSortValue compareKind(const LVObject *LHS, const LVObject *RHS); +LVSortValue compareLine(const LVObject *LHS, const LVObject *RHS); +LVSortValue compareName(const LVObject *LHS, const LVObject *RHS); +LVSortValue compareOffset(const LVObject *LHS, const LVObject *RHS); +LVSortValue compareRange(const LVObject *LHS, const LVObject *RHS); +LVSortValue sortByKind(const LVObject *LHS, const LVObject *RHS); +LVSortValue sortByLine(const LVObject *LHS, const LVObject *RHS); +LVSortValue sortByName(const LVObject *LHS, const LVObject *RHS); + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSORT_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVStringPool.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVStringPool.h @@ -0,0 +1,101 @@ +//===-- LVStringPool.h ------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVStringPool class, which is used to implement a +// basic string pool table. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSTRINGPOOL_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSTRINGPOOL_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace llvm { +namespace logicalview { + +class LVStringPool { + static constexpr size_t BadIndex = std::numeric_limits::max(); + using TableType = StringMap; + using ValueType = TableType::value_type; + BumpPtrAllocator Allocator; + TableType StringTable; + std::vector Entries; + +public: + LVStringPool() { getIndex(""); } + LVStringPool(LVStringPool const &other) = delete; + LVStringPool(LVStringPool &&other) = delete; + ~LVStringPool() = default; + +public: + bool isValidIndex(size_t Index) const { return Index != BadIndex; } + + // Return number of strings in the pool. The empty string is allocated + // at the slot zero. We substract 1 to indicate the number of non empty + // strings. + size_t getSize() const { return Entries.size() - 1; } + + // Return the index for the specified key, otherwise 'BadIndex'. + size_t findIndex(StringRef Key) const { + TableType::const_iterator Iter = StringTable.find(Key); + if (Iter != StringTable.end()) + return Iter->second; + return BadIndex; + } + + // Return an index for the specified key. + size_t getIndex(StringRef Key) { + size_t Index = findIndex(Key); + if (isValidIndex(Index)) + return Index; + size_t Value = Entries.size(); + ValueType *Entry = ValueType::Create(Key, Allocator, std::move(Value)); + StringTable.insert(Entry); + Entries.push_back(Entry); + return Value; + } + + // Given the index, return its corresponding string. + StringRef getString(size_t Index) const { + assert(Index < Entries.size() && "Invalid string pool index."); + return (Index >= Entries.size()) ? StringRef() : Entries[Index]->getKey(); + } + + static LVStringPool &getInstance() { + static LVStringPool Instance; + return Instance; + } + +public: + void print(raw_ostream &OS) const { + if (!Entries.empty()) { + OS << "\nString Pool:\n"; + for (const ValueType *Entry : Entries) + OS << "Index: " << Entry->getValue() << ", " + << "Key: '" << Entry->getKey() << "'\n"; + } + } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + virtual void dump() const { print(dbgs()); } +#endif +}; + +inline LVStringPool &getStringPool() { return LVStringPool::getInstance(); } + +} // namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSTRINGPOOL_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h @@ -0,0 +1,289 @@ +//===-- LVSupport.h ---------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines support functions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSUPPORT_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSUPPORT_H + +#include "llvm/ADT/SmallBitVector.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +namespace llvm { +namespace logicalview { + +// Utility class to help memory management and perform an automatic cleaning. +template +class LVAutoSmallVector : public SmallVector { +public: + using iterator = typename SmallVector::iterator; + LVAutoSmallVector() : SmallVector::SmallVector() {} + + ~LVAutoSmallVector() { + // Destroy the constructed elements in the vector. + for (auto *Item : *this) + delete Item; + } +}; + +using LVStringRefs = std::vector; +using LVLexicalComponent = std::tuple; +using LVLexicalIndex = + std::tuple; + +// Used to record specific characteristics about the objects. +template class LVProperties { + SmallBitVector Bits = SmallBitVector(static_cast(T::LastEntry) + 1); + +public: + LVProperties() = default; + + void set(T Idx) { + assert(static_cast(Idx) < static_cast(T::LastEntry) && + "Invalid property"); + Bits.set(static_cast(Idx)); + } + + void reset(T Idx) { + assert(static_cast(Idx) < static_cast(T::LastEntry) && + "Invalid property"); + Bits.reset(static_cast(Idx)); + } + + bool get(T Idx) const { + assert(static_cast(Idx) < static_cast(T::LastEntry) && + "Invalid property"); + return Bits[static_cast(Idx)]; + } +}; + +// Generate get, set and reset 'bool' functions for LVProperties instances. +// FAMILY: instance name. +// ENUM: enumeration instance. +// FIELD: enumerator instance. +// F1, F2, F3: optional 'set' functions to be called. +#define BOOL_BIT(FAMILY, ENUM, FIELD) \ + bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); } \ + void set##FIELD() { FAMILY.set(ENUM::FIELD); } \ + void reset##FIELD() { FAMILY.reset(ENUM::FIELD); } + +#define BOOL_BIT_1(FAMILY, ENUM, FIELD, F1) \ + bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); } \ + void set##FIELD() { \ + FAMILY.set(ENUM::FIELD); \ + set##F1(); \ + } \ + void reset##FIELD() { FAMILY.reset(ENUM::FIELD); } + +#define BOOL_BIT_2(FAMILY, ENUM, FIELD, F1, F2) \ + bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); } \ + void set##FIELD() { \ + FAMILY.set(ENUM::FIELD); \ + set##F1(); \ + set##F2(); \ + } \ + void reset##FIELD() { FAMILY.reset(ENUM::FIELD); } + +#define BOOL_BIT_3(FAMILY, ENUM, FIELD, F1, F2, F3) \ + bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); } \ + void set##FIELD() { \ + FAMILY.set(ENUM::FIELD); \ + set##F1(); \ + set##F2(); \ + set##F3(); \ + } \ + void reset##FIELD() { FAMILY.reset(ENUM::FIELD); } + +// Generate get, set and reset functions for 'properties'. +#define PROPERTY(ENUM, FIELD) BOOL_BIT(Properties, ENUM, FIELD) +#define PROPERTY_1(ENUM, FIELD, F1) BOOL_BIT_1(Properties, ENUM, FIELD, F1) +#define PROPERTY_2(ENUM, FIELD, F1, F2) \ + BOOL_BIT_2(Properties, ENUM, FIELD, F1, F2) +#define PROPERTY_3(ENUM, FIELD, F1, F2, F3) \ + BOOL_BIT_3(Properties, ENUM, FIELD, F1, F2, F3) + +// Generate get, set and reset functions for 'kinds'. +#define KIND(ENUM, FIELD) BOOL_BIT(Kinds, ENUM, FIELD) +#define KIND_1(ENUM, FIELD, F1) BOOL_BIT_1(Kinds, ENUM, FIELD, F1) +#define KIND_2(ENUM, FIELD, F1, F2) BOOL_BIT_2(Kinds, ENUM, FIELD, F1, F2) +#define KIND_3(ENUM, FIELD, F1, F2, F3) \ + BOOL_BIT_3(Kinds, ENUM, FIELD, F1, F2, F3) + +const int HEX_WIDTH = 12; +inline FormattedNumber hexValue(uint64_t N, unsigned Width = HEX_WIDTH, + bool Upper = false) { + return format_hex(N, Width, Upper); +} + +// Output the hexadecimal representation of 'Value' using '[0x%08x]' format. +inline std::string hexString(uint64_t Value, size_t Width = HEX_WIDTH) { + std::string String; + raw_string_ostream Stream(String); + Stream << hexValue(Value, Width, false); + return Stream.str(); +} + +// Get a hexadecimal string representation for the given value. +inline std::string hexSquareString(uint64_t Value) { + return (Twine("[") + Twine(hexString(Value)) + Twine("]")).str(); +} + +// Return a string with the First and Others separated by spaces. +template +std::string formatAttributes(const StringRef First, Args... Others) { + const auto List = {First, Others...}; + std::stringstream Stream; + size_t Size = 0; + for (const StringRef &Item : List) { + Stream << (Size ? " " : "") << Item.str(); + Size = Item.size(); + } + Stream << (Size ? " " : ""); + return Stream.str(); +} + +// Add an item to a map with second being a list. +template +void addItem(MapType *Map, KeyType Key, ValueType Value) { + ListType *List = nullptr; + typename MapType::const_iterator Iter = Map->find(Key); + if (Iter != Map->end()) + List = Iter->second; + else { + List = new ListType(); + Map->emplace(Key, List); + } + List->push_back(Value); +} + +// Delete the map contained list. +template void deleteList(MapType &Map) { + for (typename MapType::const_reference Entry : Map) + delete Entry.second; +} + +// Double map data structure. +template +class LVDoubleMap { +private: + using LVSecondMapType = std::map; + using LVFirstMapType = std::map; + LVFirstMapType FirstMap; + +public: + LVDoubleMap() = default; + ~LVDoubleMap() { + for (auto &Entry : FirstMap) + delete Entry.second; + } + +public: + void add(FirstKeyType FirstKey, SecondKeyType SecondKey, ValueType Value) { + LVSecondMapType *SecondMap = nullptr; + typename LVFirstMapType::iterator FirstIter = FirstMap.find(FirstKey); + if (FirstIter == FirstMap.end()) { + SecondMap = new LVSecondMapType(); + FirstMap.emplace(FirstKey, SecondMap); + } else { + SecondMap = FirstIter->second; + } + + assert(SecondMap && "SecondMap is null."); + if (SecondMap && SecondMap->find(SecondKey) == SecondMap->end()) + SecondMap->emplace(SecondKey, Value); + } + + LVSecondMapType *findMap(FirstKeyType FirstKey) const { + typename LVFirstMapType::const_iterator FirstIter = FirstMap.find(FirstKey); + if (FirstIter == FirstMap.end()) + return nullptr; + + LVSecondMapType *SecondMap = FirstIter->second; + return SecondMap; + } + + ValueType find(FirstKeyType FirstKey, SecondKeyType SecondKey) const { + LVSecondMapType *SecondMap = findMap(FirstKey); + if (!SecondMap) + return nullptr; + + typename LVSecondMapType::const_iterator SecondIter = + SecondMap->find(SecondKey); + return (SecondIter != SecondMap->end()) ? SecondIter->second : nullptr; + } +}; + +// Unified and flattened pathnames. +std::string transformPath(StringRef Path); +std::string flattenedFilePath(StringRef Path); + +inline std::string formattedKind(StringRef Kind) { + return (Twine("{") + Twine(Kind) + Twine("}")).str(); +} + +inline std::string formattedName(StringRef Name) { + return (Twine("'") + Twine(Name) + Twine("'")).str(); +} + +inline std::string formattedNames(StringRef Name1, StringRef Name2) { + return (Twine("'") + Twine(Name1) + Twine(Name2) + Twine("'")).str(); +} + +// The given string represents a symbol or type name with optional enclosing +// scopes, such as: name, name<..>, scope::name, scope::..::name, etc. +// The string can have multiple references to template instantiations. +// It returns the inner most component. +LVLexicalComponent getInnerComponent(StringRef Name); +LVStringRefs getAllLexicalComponents(StringRef Name); +std::string getScopedName(const LVStringRefs &Components, + StringRef BaseName = {}); + +// Return a pathname composed by: parent_path(To) / filename(From). +// This is useful when a type server (PDB file associated with an object +// file or a precompiled header file) has been moved from its original +// location. That is the case when running regression tests. +inline std::string createAlternativePath(StringRef To, StringRef From) { + // Convert any Windows backslashes into forward slashes to get the path. + std::string ConvertedPath = + sys::path::convert_to_slash(From, sys::path::Style::windows); + SmallString<128> Path; + sys::path::append(Path, sys::path::parent_path(To), + sys::path::filename(ConvertedPath)); + return std::string(Path); +} + +// These are the values assigned to the debug location record IDs. +// See DebugInfo/CodeView/CodeViewSymbols.def. +// S_DEFRANGE 0x113f +// S_DEFRANGE_SUBFIELD 0x1140 +// S_DEFRANGE_REGISTER 0x1141 +// S_DEFRANGE_FRAMEPOINTER_REL 0x1142 +// S_DEFRANGE_SUBFIELD_REGISTER 0x1143 +// S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE 0x1144 +// S_DEFRANGE_REGISTER_REL 0x1145 +// When recording CodeView debug location, the above values are truncated +// to a uint8_t value in order to fit the 'OpCode' used for the logical +// debug location operations. +// Return the original CodeView enum value. +inline uint16_t updateOperationCode(uint8_t Code) { return 0x1100 | Code; } + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSUPPORT_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h @@ -0,0 +1,195 @@ +//===-- LVSymbol.h ----------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVSymbol class, which is used to describe a debug +// information symbol. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSYMBOL_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSYMBOL_H + +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" + +namespace llvm { +namespace logicalview { + +enum class LVSymbolKind { + IsCallSiteParameter, + IsConstant, + IsInheritance, + IsMember, + IsParameter, + IsUnspecified, + IsVariable, + LastEntry +}; +using LVSymbolKindSet = std::set; +using LVSymbolDispatch = std::map; +using LVSymbolRequest = std::vector; + +class LVSymbol final : public LVElement { + enum class Property { HasLocation, FillGaps, LastEntry }; + + // Typed bitvector with kinds and properties for this symbol. + LVProperties Kinds; + LVProperties Properties; + static LVSymbolDispatch Dispatch; + + // CodeView symbol Linkage name. + size_t LinkageNameIndex = 0; + + // Reference to DW_AT_specification, DW_AT_abstract_origin attribute. + LVSymbol *Reference = nullptr; + LVAutoLocations *Locations = nullptr; + LVLocation *CurrentLocation = nullptr; + + // Bitfields length. + uint32_t BitSize = 0; + + // Index in the String pool representing any initial value. + size_t ValueIndex = 0; + + // Coverage factor in units (bytes). + unsigned CoverageFactor = 0; + float CoveragePercentage = 0; + + // Add a location gap into the location list. + LVAutoLocations::iterator addLocationGap(LVAutoLocations::iterator Pos, + LVAddress LowPC, LVAddress HighPC); + + // Find the 'current' symbol in the 'Targets'. + LVSymbol *findIn(const LVSymbols *Targets) const; + +public: + LVSymbol() : LVElement(LVSubclassID::LV_SYMBOL) { + setIsSymbol(); + setIncludeInPrint(); + } + LVSymbol(const LVSymbol &) = delete; + LVSymbol &operator=(const LVSymbol &) = delete; + ~LVSymbol() { delete Locations; } + + static bool classof(const LVElement *Element) { + return Element->getSubclassID() == LVSubclassID::LV_SYMBOL; + } + + KIND(LVSymbolKind, IsCallSiteParameter); + KIND(LVSymbolKind, IsConstant); + KIND(LVSymbolKind, IsInheritance); + KIND(LVSymbolKind, IsMember); + KIND(LVSymbolKind, IsParameter); + KIND(LVSymbolKind, IsUnspecified); + KIND(LVSymbolKind, IsVariable); + + PROPERTY(Property, HasLocation); + PROPERTY(Property, FillGaps); + +public: + const char *kind() const override; + + // Access DW_AT_specification, DW_AT_abstract_origin reference. + LVSymbol *getReference() const { return Reference; } + void setReference(LVSymbol *Symbol) override { + Reference = Symbol; + setHasReference(); + } + void setReference(LVElement *Element) override { + assert((!Element || (Element && static_cast(Element))) && + "Invalid element"); + setReference(static_cast(Element)); + } + + void setLinkageName(StringRef LinkageName) override { + LinkageNameIndex = getStringPool().getIndex(LinkageName); + } + StringRef getLinkageName() const override { + return getStringPool().getString(LinkageNameIndex); + } + size_t getLinkageNameIndex() const override { return LinkageNameIndex; } + + uint32_t getBitSize() const override { return BitSize; } + void setBitSize(uint32_t Size) override { BitSize = Size; } + + // Process the values for a DW_AT_const_value. + std::string getValue() const override { + return std::string(getStringPool().getString(ValueIndex)); + } + void setValue(StringRef Value) override { + ValueIndex = getStringPool().getIndex(Value); + } + size_t getValueIndex() const override { return ValueIndex; } + + // Add a Location Entry. + void addLocationConstant(dwarf::Attribute Attr, LVUnsigned Constant, + uint64_t LocDescOffset); + void addLocationOperands(LVSmall Opcode, uint64_t Operand1, + uint64_t Operand2); + void addLocation(dwarf::Attribute Attr, LVAddress LowPC, LVAddress HighPC, + LVUnsigned SectionOffset, uint64_t LocDescOffset, + bool CallSiteLocation = false); + + // Fill gaps in the location list. + void fillLocationGaps(); + + // Get all the locations associated with symbols. + void getLocations(LVLocations &LocationList, LVValidLocation ValidLocation, + bool RecordInvalid = false); + void getLocations(LVLocations &LocationList) const; + + // Calculate coverage factor. + void calculateCoverage(); + + unsigned getCoverageFactor() const { return CoverageFactor; } + void setCoverageFactor(unsigned Value) { CoverageFactor = Value; } + float getCoveragePercentage() const { return CoveragePercentage; } + void setCoveragePercentage(float Value) { CoveragePercentage = Value; } + + // Print location in raw format. + void printLocations(raw_ostream &OS, bool Full = true) const; + + // Follow a chain of references given by DW_AT_abstract_origin and/or + // DW_AT_specification and update the symbol name. + StringRef resolveReferencesChain(); + + void resolveName() override; + void resolveReferences() override; + +public: + static LVSymbolDispatch &getDispatch() { return Dispatch; } + +public: + static bool parametersMatch(const LVSymbols *References, + const LVSymbols *Targets); + + static void getParameters(const LVSymbols *Symbols, LVSymbols *Parameters); + + // Iterate through the 'References' set and check that all its elements + // are present in the 'Targets' set. For a missing element, mark its + // parents as missing. + static void markMissingParents(const LVSymbols *References, + const LVSymbols *Targets); + + // Returns true if 'current' type is equal to the given 'type'. + bool equals(const LVSymbol *Symbol) const; + static bool equals(const LVSymbols *References, const LVSymbols *Targets); + void report(LVComparePass Pass) override; + +public: + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSYMBOL_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h @@ -0,0 +1,289 @@ +//===-- LVType.h ------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVType class, which is used to describe a debug +// information type. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVTYPE_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVTYPE_H + +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" + +namespace llvm { +namespace logicalview { + +enum class LVTypeKind { + IsBase, + IsConst, + IsEnumerator, + IsImport, + IsImportDeclaration, + IsImportModule, + IsPointer, + IsPointerMember, + IsReference, + IsRestrict, + IsRvalueReference, + IsSubrange, + IsTemplateParam, + IsTemplateTemplateParam, + IsTemplateTypeParam, + IsTemplateValueParam, + IsTypedef, + IsUnaligned, + IsUnspecified, + IsVolatile, + IsModifier, // CodeView - LF_MODIFIER + LastEntry +}; +using LVTypeKindSelection = std::set; +using LVTypeDispatch = std::map; +using LVTypeRequest = std::vector; + +// Class to represent a DWARF Type. +class LVType : public LVElement { + enum class Property { IsSubrangeCount, LastEntry }; + + // Typed bitvector with kinds and properties for this type. + LVProperties Kinds; + LVProperties Properties; + static LVTypeDispatch Dispatch; + + // Find the 'current' type in the 'Targets' set. + LVType *findIn(const LVTypes *Targets) const; + +public: + LVType() : LVElement(LVSubclassID::LV_TYPE) { setIsType(); } + LVType(const LVType &) = delete; + LVType &operator=(const LVType &) = delete; + virtual ~LVType() = default; + + static bool classof(const LVElement *Element) { + return Element->getSubclassID() == LVSubclassID::LV_TYPE; + } + + KIND(LVTypeKind, IsBase); + KIND(LVTypeKind, IsConst); + KIND(LVTypeKind, IsEnumerator); + KIND(LVTypeKind, IsImport); + KIND_1(LVTypeKind, IsImportDeclaration, IsImport); + KIND_1(LVTypeKind, IsImportModule, IsImport); + KIND(LVTypeKind, IsPointer); + KIND(LVTypeKind, IsPointerMember); + KIND(LVTypeKind, IsReference); + KIND(LVTypeKind, IsRestrict); + KIND(LVTypeKind, IsRvalueReference); + KIND(LVTypeKind, IsSubrange); + KIND(LVTypeKind, IsTemplateParam); + KIND_1(LVTypeKind, IsTemplateTemplateParam, IsTemplateParam); + KIND_1(LVTypeKind, IsTemplateTypeParam, IsTemplateParam); + KIND_1(LVTypeKind, IsTemplateValueParam, IsTemplateParam); + KIND(LVTypeKind, IsTypedef); + KIND(LVTypeKind, IsUnaligned); + KIND(LVTypeKind, IsUnspecified); + KIND(LVTypeKind, IsVolatile); + KIND(LVTypeKind, IsModifier); + + PROPERTY(Property, IsSubrangeCount); + +public: + const char *kind() const override; + + // Follow a chain of references given by DW_AT_abstract_origin and/or + // DW_AT_specification and update the type name. + StringRef resolveReferencesChain(); + + bool isBase() const override { return getIsBase(); } + bool isTemplateParam() const override { return getIsTemplateParam(); } + + // Encode the specific template argument. + virtual void encodeTemplateArgument(std::string &Name) const {} + + // Return the underlying type for a type definition. + virtual LVElement *getUnderlyingType() { return nullptr; } + virtual void setUnderlyingType(LVElement *Element) {} + + void resolveName() override; + void resolveReferences() override; + +public: + static LVTypeDispatch &getDispatch() { return Dispatch; } + +public: + static bool parametersMatch(const LVTypes *References, + const LVTypes *Targets); + + static void getParameters(const LVTypes *Types, LVTypes *TypesParam, + LVScopes *ScopesParam); + + // Iterate through the 'References' set and check that all its elements + // are present in the 'Targets' set. For a missing element, mark its + // parents as missing. + static void markMissingParents(const LVTypes *References, + const LVTypes *Targets); + + // Returns true if 'current' type is equal to the given 'type'. + virtual bool equals(const LVType *Type) const; + static bool equals(const LVTypes *References, const LVTypes *Targets); + void report(LVComparePass Pass) override; + +public: + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +// Class to represent DW_TAG_typedef_type. +class LVTypeDefinition final : public LVType { +public: + LVTypeDefinition() : LVType() { + setIsTypedef(); + setIncludeInPrint(); + } + LVTypeDefinition(const LVTypeDefinition &) = delete; + LVTypeDefinition &operator=(const LVTypeDefinition &) = delete; + ~LVTypeDefinition() = default; + + // Return the underlying type for a type definition. + LVElement *getUnderlyingType() override; + void setUnderlyingType(LVElement *Element) override { setType(Element); } + + void resolveExtra() override; + + // Returns true if 'current' type is equal to the given 'type'. + bool equals(const LVType *Type) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DW_TAG_enumerator. +class LVTypeEnumerator final : public LVType { + // Index in the String pool representing any initial value. + size_t ValueIndex = 0; + +public: + LVTypeEnumerator() : LVType() { + setIsEnumerator(); + setIncludeInPrint(); + } + LVTypeEnumerator(const LVTypeEnumerator &) = delete; + LVTypeEnumerator &operator=(const LVTypeEnumerator &) = delete; + ~LVTypeEnumerator() = default; + + // Process the values for a DW_TAG_enumerator. + std::string getValue() const override { + return std::string(getStringPool().getString(ValueIndex)); + } + void setValue(StringRef Value) override { + ValueIndex = getStringPool().getIndex(Value); + } + size_t getValueIndex() const override { return ValueIndex; } + + // Returns true if 'current' type is equal to the given 'type'. + bool equals(const LVType *Type) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent DW_TAG_imported_module / DW_TAG_imported_declaration. +class LVTypeImport final : public LVType { +public: + LVTypeImport() : LVType() { setIncludeInPrint(); } + LVTypeImport(const LVTypeImport &) = delete; + LVTypeImport &operator=(const LVTypeImport &) = delete; + ~LVTypeImport() = default; + + // Returns true if 'current' type is equal to the given 'type'. + bool equals(const LVType *Type) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF Template parameter holder (type or param). +class LVTypeParam final : public LVType { + // Index in the String pool representing any initial value. + size_t ValueIndex = 0; + +public: + LVTypeParam(); + LVTypeParam(const LVTypeParam &) = delete; + LVTypeParam &operator=(const LVTypeParam &) = delete; + ~LVTypeParam() = default; + + // Template parameter value. + std::string getValue() const override { + return std::string(getStringPool().getString(ValueIndex)); + } + void setValue(StringRef Value) override { + ValueIndex = getStringPool().getIndex(Value); + } + size_t getValueIndex() const override { return ValueIndex; } + + // Encode the specific template argument. + void encodeTemplateArgument(std::string &Name) const override; + + // Returns true if 'current' type is equal to the given 'type'. + bool equals(const LVType *Type) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DW_TAG_subrange_type. +class LVTypeSubrange final : public LVType { + // Values describing the subrange bounds. + int64_t LowerBound = 0; // DW_AT_lower_bound or DW_AT_count value. + int64_t UpperBound = 0; // DW_AT_upper_bound value. + +public: + LVTypeSubrange() : LVType() { + setIsSubrange(); + setIncludeInPrint(); + } + LVTypeSubrange(const LVTypeSubrange &) = delete; + LVTypeSubrange &operator=(const LVTypeSubrange &) = delete; + ~LVTypeSubrange() = default; + + int64_t getCount() const override { + return getIsSubrangeCount() ? LowerBound : 0; + } + void setCount(int64_t Value) override { + LowerBound = Value; + setIsSubrangeCount(); + } + + int64_t getLowerBound() const override { return LowerBound; } + void setLowerBound(int64_t Value) override { LowerBound = Value; } + + int64_t getUpperBound() const override { return UpperBound; } + void setUpperBound(int64_t Value) override { UpperBound = Value; } + + std::pair getBounds() const override { + return {LowerBound, UpperBound}; + } + void setBounds(unsigned Lower, unsigned Upper) override { + LowerBound = Lower; + UpperBound = Upper; + } + + void resolveExtra() override; + + // Returns true if 'current' type is equal to the given 'type'. + bool equals(const LVType *Type) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVTYPE_H Index: llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h @@ -0,0 +1,105 @@ +//===-- LVReaderHandler.h ---------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This class implements the Reader handler. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVREADERHANDLER_H +#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVREADERHANDLER_H + +#include "llvm/ADT/PointerUnion.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ScopedPrinter.h" +#include +#include + +namespace llvm { +namespace logicalview { + +using LVReaders = std::vector; +using ArgVector = std::vector; +using PdbOrObj = PointerUnion; + +// This class performs the following tasks: +// - Creates a logical reader for every binary file in the command line, +// that parses the debug information and creates a high level logical +// view representation containing scopes, symbols, types and lines. +// - Prints and compares the logical views. +// +// The supported binary formats are: ELF, MacOS and CodeView. +class LVReaderHandler { + ArgVector &Objects; + ScopedPrinter &W; + raw_ostream &OS; + LVReaders TheReaders; + + Error createReaders(); + void destroyReaders(); + Error printReaders(); + Error compareReaders(); + + Error handleArchive(LVReaders &Readers, StringRef Filename, + object::Archive &Arch); + Error handleBuffer(LVReaders &Readers, StringRef Filename, + MemoryBufferRef Buffer, StringRef ExePath = {}); + Error handleFile(LVReaders &Readers, StringRef Filename, + StringRef ExePath = {}); + Error handleMach(LVReaders &Readers, StringRef Filename, + object::MachOUniversalBinary &Mach); + Error handleObject(LVReaders &Readers, StringRef Filename, + object::Binary &Binary); + Error handleObject(LVReaders &Readers, StringRef Filename, StringRef Buffer, + StringRef ExePath); + + Error createReader(StringRef Filename, LVReaders &Readers, PdbOrObj &Input, + StringRef FileFormatName, StringRef ExePath = {}); + +public: + LVReaderHandler() = delete; + LVReaderHandler(ArgVector &Objects, ScopedPrinter &W, + LVOptions &ReaderOptions) + : Objects(Objects), W(W), OS(W.getOStream()) { + setOptions(&ReaderOptions); + } + LVReaderHandler(const LVReaderHandler &) = delete; + LVReaderHandler &operator=(const LVReaderHandler &) = delete; + ~LVReaderHandler() { destroyReaders(); } + + Error createReader(StringRef Filename, LVReaders &Readers) { + return handleFile(Readers, Filename); + } + Error process(); + + Expected createReader(StringRef Pathname) { + LVReaders Readers; + if (Error Err = createReader(Pathname, Readers)) + return std::move(Err); + return Readers[0]; + } + void deleteReader(LVReader *Reader) { + delete Reader; + } + +public: + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +} // end namespace logicalview +} // namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVREADERHANDLER_H Index: llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h @@ -0,0 +1,208 @@ +//===-- LVBinaryReader.h ----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVBinaryReader class, which is used to describe a +// binary reader. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVBINARYREADER_H +#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVBINARYREADER_H + +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/ObjectFile.h" + +namespace llvm { +namespace logicalview { + +constexpr bool UpdateHighAddress = false; + +// Logical scope, Section address, Section index, IsComdat. +struct LVSymbolTableEntry final { + LVScope *Scope = nullptr; + LVAddress Address = 0; + LVSectionIndex SectionIndex = 0; + bool IsComdat = false; + LVSymbolTableEntry() = default; + LVSymbolTableEntry(LVScope *Scope, LVAddress Address, + LVSectionIndex SectionIndex, bool IsComdat) + : Scope(Scope), Address(Address), SectionIndex(SectionIndex), + IsComdat(IsComdat) {} +}; + +// Function names extracted from the object symbol table. +class LVSymbolTable final { + using LVSymbolNames = std::map; + LVSymbolNames SymbolNames; + +public: + LVSymbolTable() = default; + + void add(StringRef Name, LVScope *Function, LVSectionIndex SectionIndex = 0); + void add(StringRef Name, LVAddress Address, LVSectionIndex SectionIndex, + bool IsComdat); + LVSectionIndex update(LVScope *Function); + + const LVSymbolTableEntry &getEntry(StringRef Name); + LVAddress getAddress(StringRef Name); + LVSectionIndex getIndex(StringRef Name); + bool getIsComdat(StringRef Name); + + void print(raw_ostream &OS); +}; + +class LVBinaryReader : public LVReader { + // Function names extracted from the object symbol table. + LVSymbolTable SymbolTable; + + // It contains the LVLineDebug elements representing the inlined logical + // lines for the current compile unit, created by parsing the CodeView + // S_INLINESITE symbol annotation data. + using LVInlineeLine = std::map; + LVInlineeLine CUInlineeLines; + + // Instruction lines for a logical scope. These instructions are fetched + // during its merge with the debug lines. + using LVInstructions = std::map; + LVInstructions ScopeInstructions; + + // Links the scope with its first assembler address line. + LVDoubleMap AssemblerMappings; + + // Mapping from virtual address to section. + // The virtual address refers to the address where the section is loaded. + using LVSectionAddresses = std::map; + LVSectionAddresses SectionAddresses; + + void addSectionAddress(const object::SectionRef &Section) { + if (SectionAddresses.find(Section.getAddress()) == SectionAddresses.end()) + SectionAddresses.emplace(Section.getAddress(), Section); + } + + // Scopes with ranges for current compile unit. It is used to find a line + // giving its exact or closest address. To support comdat functions, all + // addresses for the same section are recorded in the same map. + using LVSectionRanges = std::map; + LVSectionRanges SectionRanges; + + // Image base and virtual address for Executable file. + uint64_t ImageBaseAddress = 0; + uint64_t VirtualAddress = 0; + + // Object sections with machine code. + using LVSections = std::map; + LVSections Sections; + +protected: + // It contains the LVLineDebug elements representing the logical lines for + // the current compile unit, created by parsing the debug line section. + LVLines CULines; + + std::unique_ptr MRI; + std::unique_ptr MAI; + std::unique_ptr STI; + std::unique_ptr MII; + std::unique_ptr MD; + std::unique_ptr MC; + std::unique_ptr MIP; + + // Loads all info for the architecture of the provided object file. + Error loadGenericTargetInfo(StringRef TheTriple, StringRef TheFeatures); + + virtual void mapRangeAddress(const object::ObjectFile &Obj) {} + virtual void mapRangeAddress(const object::ObjectFile &Obj, + const object::SectionRef &Section, + bool IsComdat) {} + + // Create a mapping from virtual address to section. + void mapVirtualAddress(const object::ObjectFile &Obj); + void mapVirtualAddress(const object::COFFObjectFile &COFFObj); + + Expected> + getSection(LVScope *Scope, LVAddress Address, LVSectionIndex SectionIndex); + + void addSectionRange(LVSectionIndex SectionIndex, LVScope *Scope); + void addSectionRange(LVSectionIndex SectionIndex, LVScope *Scope, + LVAddress LowerAddress, LVAddress UpperAddress); + LVRange *getSectionRanges(LVSectionIndex SectionIndex); + + void includeInlineeLines(LVSectionIndex SectionIndex, LVScope *Function); + + Error createInstructions(); + Error createInstructions(LVScope *Function, LVSectionIndex SectionIndex); + Error createInstructions(LVScope *Function, LVSectionIndex SectionIndex, + const LVNameInfo &Name); + + void processLines(LVLines *DebugLines, LVSectionIndex SectionIndex); + void processLines(LVLines *DebugLines, LVSectionIndex SectionIndex, + LVScope *Function); + +public: + LVBinaryReader() = delete; + LVBinaryReader(StringRef Filename, StringRef FileFormatName, ScopedPrinter &W, + LVBinaryType BinaryType) + : LVReader(Filename, FileFormatName, W, BinaryType) {} + LVBinaryReader(const LVBinaryReader &) = delete; + LVBinaryReader &operator=(const LVBinaryReader &) = delete; + ~LVBinaryReader() { + for (LVSectionRanges::reference Entry : SectionRanges) + delete Entry.second; + } + +public: + void addInlineeLines(LVScope *Scope, LVLines &Lines) { + LVLines *InlineeLines = new LVLines(); + *InlineeLines = std::move(Lines); + CUInlineeLines.emplace(Scope, InlineeLines); + } + + // Convert Segment::Offset pair to absolute address. + LVAddress linearAddress(uint16_t Segment, uint32_t Offset, + LVAddress Addendum = 0) { + return ImageBaseAddress + (Segment * VirtualAddress) + Offset + Addendum; + } + +public: + void addToSymbolTable(StringRef Name, LVScope *Function, + LVSectionIndex SectionIndex = 0); + void addToSymbolTable(StringRef Name, LVAddress Address, + LVSectionIndex SectionIndex, bool IsComdat); + LVSectionIndex updateSymbolTable(LVScope *Function); + + const LVSymbolTableEntry &getSymbolTableEntry(StringRef Name); + LVAddress getSymbolTableAddress(StringRef Name); + LVSectionIndex getSymbolTableIndex(StringRef Name); + bool getSymbolTableIsComdat(StringRef Name); + + LVSectionIndex getSectionIndex(LVScope *Scope) override { + return Scope ? getSymbolTableIndex(Scope->getLinkageName()) + : DotTextSectionIndex; + } + +public: + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVBINARYREADER_H Index: llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h @@ -0,0 +1,238 @@ +//===-- LVCodeViewReader.h --------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVCodeViewReader class, which is used to describe a +// debug information (COFF) reader. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWREADER_H +#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWREADER_H + +#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryItemStream.h" +#include "llvm/Support/BinaryStreamArray.h" + +namespace llvm { +template <> struct BinaryItemTraits { + static size_t length(const codeview::CVType &Item) { return Item.length(); } + static ArrayRef bytes(const codeview::CVType &Item) { + return Item.data(); + } +}; +} // namespace llvm + +namespace llvm { +namespace codeview { +class LazyRandomTypeCollection; +} +namespace object { +struct coff_section; +} +namespace pdb { +class SymbolGroup; +} +namespace logicalview { + +class LVElement; +class LVLine; +class LVScope; +class LVScopeCompileUnit; +class LVSymbol; +class LVType; +class LVTypeVisitor; +class LVSymbolVisitor; +class LVSymbolVisitorDelegate; + +using LVNames = SmallVector; + +// The ELF reader use the DWARF constants to create the logical elements. +// The DW_TAG_* and DW_AT_* are used to select the logical object and to +// set specific attributes, such as name, type, etc. +// As the CodeView constants are different to the DWARF constants, the +// CodeView reader will map them to the DWARF ones. + +class LVCodeViewReader final : public LVBinaryReader { + friend class LVTypeVisitor; + friend class LVSymbolVisitor; + friend class LVSymbolVisitorDelegate; + + using LVModules = std::vector; + LVModules Modules; + + // Encapsulates access to the input file and any dependent type server, + // including any precompiled header object. + llvm::pdb::InputFile Input; + std::shared_ptr TypeServer; + std::shared_ptr PrecompHeader; + + // Persistance data when loading a type server. + ErrorOr> BuffOrErr = nullptr; + std::unique_ptr MemBuffer; + std::unique_ptr Session; + std::unique_ptr PdbSession; + + // Persistance data when loading a precompiled header. + BumpPtrAllocator BuilderAllocator; + std::unique_ptr Builder; + std::unique_ptr> ItemStream; + std::unique_ptr ReaderPrecomp; + std::vector TypeArray; + CVTypeArray TypeStream; + CVTypeArray CVTypesPrecomp; + + // Persistance data when loading an executable file. + std::unique_ptr BinaryBuffer; + std::unique_ptr BinaryExecutable; + + Error loadTargetInfo(const object::ObjectFile &Obj); + Error loadTargetInfo(const llvm::pdb::PDBFile &Pdb); + + void mapRangeAddress(const object::ObjectFile &Obj, + const object::SectionRef &Section, + bool IsComdat) override; + + llvm::object::COFFObjectFile &getObj() { return Input.obj(); } + llvm::pdb::PDBFile &getPdb() { return Input.pdb(); } + bool isObj() const { return Input.isObj(); } + bool isPdb() const { return Input.isPdb(); } + StringRef getFileName() { return Input.getFilePath(); } + + // Pathname to executable image. + std::string ExePath; + + LVOffset CurrentOffset = 0; + int32_t CurrentModule = -1; + + using RelocMapTy = DenseMap>; + RelocMapTy RelocMap; + + // Object files have only one type stream that contains both types and ids. + // Precompiled header objects don't contain an IPI stream. Use the TPI. + LazyRandomTypeCollection &types() { + return TypeServer ? TypeServer->types() + : (PrecompHeader ? *PrecompHeader : Input.types()); + } + LazyRandomTypeCollection &ids() { + return TypeServer ? TypeServer->ids() + : (PrecompHeader ? *PrecompHeader : Input.ids()); + } + + LVLogicalVisitor LogicalVisitor; + + Expected + getFileNameForFileOffset(uint32_t FileOffset, + const llvm::pdb::SymbolGroup *SG = nullptr); + void printRelocatedField(StringRef Label, + const llvm::object::coff_section *CoffSection, + uint32_t RelocOffset, uint32_t Offset, + StringRef *RelocSym); + + Error printFileNameForOffset(StringRef Label, uint32_t FileOffset, + const llvm::pdb::SymbolGroup *SG = nullptr); + + Error loadPrecompiledObject(PrecompRecord &Precomp, CVTypeArray &CVTypesObj); + Error loadTypeServer(TypeServer2Record &TS); + Error traverseTypes(llvm::pdb::PDBFile &Pdb, LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids); + + Error collectInlineeInfo(DebugInlineeLinesSubsectionRef &Lines, + const llvm::pdb::SymbolGroup *SG = nullptr); + + void cacheRelocations(); + Error resolveSymbol(const llvm::object::coff_section *CoffSection, + uint64_t Offset, llvm::object::SymbolRef &Sym); + Error resolveSymbolName(const llvm::object::coff_section *CoffSection, + uint64_t Offset, StringRef &Name); + Error traverseTypeSection(StringRef SectionName, + const llvm::object::SectionRef &Section); + Error traverseSymbolSection(StringRef SectionName, + const llvm::object::SectionRef &Section); + Error traverseInlineeLines(StringRef Subsection); + + DebugChecksumsSubsectionRef CVFileChecksumTable; + DebugStringTableSubsectionRef CVStringTable; + + Error traverseSymbolsSubsection(StringRef Subsection, + const llvm::object::SectionRef &Section, + StringRef SectionContents); + + /// Given a .debug$S section, find the string table and file checksum table. + /// This function taken from (COFFDumper.cpp). + /// It can be moved to the COFF library. + Error initializeFileAndStringTables(BinaryStreamReader &Reader); + + Error createLines(const FixedStreamArray &LineNumbers, + LVAddress Addendum, uint32_t Segment, uint32_t Begin, + uint32_t Size, uint32_t NameIndex, + const llvm::pdb::SymbolGroup *SG = nullptr); + Error createScopes(llvm::object::COFFObjectFile &Obj); + Error createScopes(llvm::pdb::PDBFile &Pdb); + Error processModule(); + +protected: + Error createScopes() override; + void sortScopes() override; + +public: + LVCodeViewReader() = delete; + LVCodeViewReader(StringRef Filename, StringRef FileFormatName, + llvm::object::COFFObjectFile &Obj, ScopedPrinter &W, + StringRef ExePath) + : LVBinaryReader(Filename, FileFormatName, W, LVBinaryType::COFF), + Input(&Obj), ExePath(ExePath), LogicalVisitor(this, W, Input) {} + LVCodeViewReader(StringRef Filename, StringRef FileFormatName, + llvm::pdb::PDBFile &Pdb, ScopedPrinter &W, StringRef ExePath) + : LVBinaryReader(Filename, FileFormatName, W, LVBinaryType::COFF), + Input(&Pdb), ExePath(ExePath), LogicalVisitor(this, W, Input) {} + LVCodeViewReader(const LVCodeViewReader &) = delete; + LVCodeViewReader &operator=(const LVCodeViewReader &) = delete; + ~LVCodeViewReader() = default; + + void getLinkageName(const llvm::object::coff_section *CoffSection, + uint32_t RelocOffset, uint32_t Offset, + StringRef *RelocSym); + + void addModule(LVScope *Scope) { Modules.push_back(Scope); } + LVScope *getScopeForModule(uint32_t Modi) { + return Modi >= Modules.size() ? nullptr : Modules[Modi]; + } + + // Get the string representation for the CodeView symbols. + static std::string getSymbolKindName(SymbolKind Kind); + static std::string formatRegisterId(RegisterId Register, CPUType CPU); + + std::string getRegisterName(LVSmall Opcode, uint64_t Operands[2]) override; + + bool isSystemEntry(LVElement *Element, StringRef Name) const override; + +public: + void print(raw_ostream &OS) const; + void printRecords(raw_ostream &OS) const override { + LogicalVisitor.printRecords(OS); + }; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWREADER_H Index: llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h @@ -0,0 +1,481 @@ +//===-- LVCodeViewVisitor.h -------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVCodeViewVisitor class, which is used to describe a +// debug information (CodeView) visitor. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWVISITOR_H +#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWVISITOR_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/iterator.h" +#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h" +#include "llvm/DebugInfo/PDB/Native/InputFile.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { +namespace logicalview { + +using namespace llvm::codeview; + +class LVCodeViewReader; +class LVLogicalVisitor; +struct LVShared; + +class LVTypeVisitor final : public TypeVisitorCallbacks { + ScopedPrinter &W; + LVLogicalVisitor *LogicalVisitor; + LazyRandomTypeCollection &Types; + LazyRandomTypeCollection &Ids; + uint32_t StreamIdx; + LVShared *Shared = nullptr; + + // In a PDB, a type index may refer to a type (TPI) or an item ID (IPI). + // In a COFF or PDB (/Z7), the type index always refer to a type (TPI). + // When creating logical elements, we must access the correct element + // table, while searching for a type index. + bool HasIds = false; + + // Current type index during the types traversal. + TypeIndex CurrentTypeIndex = TypeIndex::None(); + + void printTypeIndex(StringRef FieldName, TypeIndex TI, + uint32_t StreamIdx) const; + +public: + LVTypeVisitor(ScopedPrinter &W, LVLogicalVisitor *LogicalVisitor, + LazyRandomTypeCollection &Types, LazyRandomTypeCollection &Ids, + uint32_t StreamIdx, LVShared *Shared) + : TypeVisitorCallbacks(), W(W), LogicalVisitor(LogicalVisitor), + Types(Types), Ids(Ids), StreamIdx(StreamIdx), Shared(Shared) { + HasIds = &Types != &Ids; + } + + Error visitTypeBegin(CVType &Record) override; + Error visitTypeBegin(CVType &Record, TypeIndex TI) override; + Error visitMemberBegin(CVMemberRecord &Record) override; + Error visitMemberEnd(CVMemberRecord &Record) override; + Error visitUnknownMember(CVMemberRecord &Record) override; + + Error visitKnownRecord(CVType &Record, BuildInfoRecord &Args) override; + Error visitKnownRecord(CVType &Record, ClassRecord &Class) override; + Error visitKnownRecord(CVType &Record, EnumRecord &Enum) override; + Error visitKnownRecord(CVType &Record, FuncIdRecord &Func) override; + Error visitKnownRecord(CVType &Record, ProcedureRecord &Proc) override; + Error visitKnownRecord(CVType &Record, StringIdRecord &String) override; + Error visitKnownRecord(CVType &Record, UdtSourceLineRecord &Line) override; + Error visitKnownRecord(CVType &Record, UnionRecord &Union) override; + Error visitUnknownType(CVType &Record) override; +}; + +class LVSymbolVisitorDelegate final : public SymbolVisitorDelegate { +public: + LVSymbolVisitorDelegate(LVCodeViewReader *Reader, + const llvm::object::SectionRef &Section, + const llvm::object::COFFObjectFile *Obj, + StringRef SectionContents) + : Reader(Reader), SectionContents(SectionContents) { + CoffSection = Obj->getCOFFSection(Section); + } + + uint32_t getRecordOffset(BinaryStreamReader Reader) override { + ArrayRef Data; + if (Error Err = Reader.readLongestContiguousChunk(Data)) { + llvm::consumeError(std::move(Err)); + return 0; + } + return Data.data() - SectionContents.bytes_begin(); + } + + void printRelocatedField(StringRef Label, uint32_t RelocOffset, + uint32_t Offset, StringRef *RelocSym = nullptr); + + void getLinkageName(uint32_t RelocOffset, uint32_t Offset, + StringRef *RelocSym = nullptr); + + StringRef getFileNameForFileOffset(uint32_t FileOffset) override; + DebugStringTableSubsectionRef getStringTable() override; + +private: + LVCodeViewReader *Reader = nullptr; + const llvm::object::coff_section *CoffSection; + StringRef SectionContents; +}; + +class LVElement; +class LVScope; +class LVSymbol; +class LVType; + +// Current elements during the processing of a RecordType or RecordSymbol. +extern LVElement *CurrentElement; +extern LVScope *CurrentScope; +extern LVSymbol *CurrentSymbol; +extern LVType *CurrentType; + +// Visitor for CodeView symbol streams found in COFF object files and PDB files. +class LVSymbolVisitor final : public SymbolVisitorCallbacks { + LVCodeViewReader *Reader = nullptr; + ScopedPrinter &W; + LVLogicalVisitor *LogicalVisitor; + LazyRandomTypeCollection &Types; + LazyRandomTypeCollection &Ids; + LVSymbolVisitorDelegate *ObjDelegate; + LVShared *Shared = nullptr; + + // Symbol offset when processing PDB streams. + uint32_t CurrentOffset = 0; + // Current object name collected from S_OBJNAME. + StringRef CurrentObjectName; + // Last symbol processed by S_LOCAL. + LVSymbol *LocalSymbol = nullptr; + + bool HasIds = false; + bool InFunctionScope = false; + bool IsCompileUnit = false; + + // Register for the locals and parameters symbols in the current frame. + RegisterId LocalFrameRegister; + RegisterId ParamFrameRegister; + + void printLocalVariableAddrRange(const LocalVariableAddrRange &Range, + uint32_t RelocationOffset); + void printLocalVariableAddrGap(ArrayRef Gaps); + void printTypeIndex(StringRef FieldName, TypeIndex TI) const; + + // Return true if this symbol is a Compile Unit. + bool symbolIsCompileUnit(SymbolKind Kind) { + switch (Kind) { + case SymbolKind::S_COMPILE2: + case SymbolKind::S_COMPILE3: + return true; + default: + return false; + } + } + + // Determine symbol kind (local or parameter). + void determineSymbolKind(LVSymbol *Symbol, RegisterId Register) { + if (Register == LocalFrameRegister) { + Symbol->setIsVariable(); + return; + } + if (Register == ParamFrameRegister) { + Symbol->setIsParameter(); + return; + } + // Assume is a variable. + Symbol->setIsVariable(); + } + +public: + LVSymbolVisitor(LVCodeViewReader *Reader, ScopedPrinter &W, + LVLogicalVisitor *LogicalVisitor, + LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids, + LVSymbolVisitorDelegate *ObjDelegate, LVShared *Shared) + : Reader(Reader), W(W), LogicalVisitor(LogicalVisitor), Types(Types), + Ids(Ids), ObjDelegate(ObjDelegate), Shared(Shared) { + HasIds = &Types != &Ids; + } + + Error visitSymbolBegin(CVSymbol &Record) override; + Error visitSymbolBegin(CVSymbol &Record, uint32_t Offset) override; + Error visitSymbolEnd(CVSymbol &Record) override; + Error visitUnknownSymbol(CVSymbol &Record) override; + + Error visitKnownRecord(CVSymbol &Record, BlockSym &Block) override; + Error visitKnownRecord(CVSymbol &Record, BPRelativeSym &Local) override; + Error visitKnownRecord(CVSymbol &Record, BuildInfoSym &BuildInfo) override; + Error visitKnownRecord(CVSymbol &Record, Compile2Sym &Compile2) override; + Error visitKnownRecord(CVSymbol &Record, Compile3Sym &Compile3) override; + Error visitKnownRecord(CVSymbol &Record, ConstantSym &Constant) override; + Error visitKnownRecord(CVSymbol &Record, DataSym &Data) override; + Error visitKnownRecord(CVSymbol &Record, + DefRangeFramePointerRelFullScopeSym + &DefRangeFramePointerRelFullScope) override; + Error visitKnownRecord( + CVSymbol &Record, + DefRangeFramePointerRelSym &DefRangeFramePointerRel) override; + Error visitKnownRecord(CVSymbol &Record, + DefRangeRegisterRelSym &DefRangeRegisterRel) override; + Error visitKnownRecord(CVSymbol &Record, + DefRangeRegisterSym &DefRangeRegister) override; + Error visitKnownRecord( + CVSymbol &Record, + DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) override; + Error visitKnownRecord(CVSymbol &Record, + DefRangeSubfieldSym &DefRangeSubfield) override; + Error visitKnownRecord(CVSymbol &Record, DefRangeSym &DefRange) override; + Error visitKnownRecord(CVSymbol &Record, FrameProcSym &FrameProc) override; + Error visitKnownRecord(CVSymbol &Record, InlineSiteSym &InlineSite) override; + Error visitKnownRecord(CVSymbol &Record, LocalSym &Local) override; + Error visitKnownRecord(CVSymbol &Record, ObjNameSym &ObjName) override; + Error visitKnownRecord(CVSymbol &Record, ProcSym &Proc) override; + Error visitKnownRecord(CVSymbol &Record, RegRelativeSym &Local) override; + Error visitKnownRecord(CVSymbol &Record, ScopeEndSym &ScopeEnd) override; + Error visitKnownRecord(CVSymbol &Record, Thunk32Sym &Thunk) override; + Error visitKnownRecord(CVSymbol &Record, UDTSym &UDT) override; + Error visitKnownRecord(CVSymbol &Record, UsingNamespaceSym &UN) override; +}; + +// Visitor for CodeView types and symbols to populate elements. +class LVLogicalVisitor final { + LVCodeViewReader *Reader = nullptr; + ScopedPrinter &W; + + // Encapsulates access to the input file and any dependent type server, + // including any precompiled header object. + llvm::pdb::InputFile &Input; + std::shared_ptr TypeServer = nullptr; + std::shared_ptr PrecompHeader = nullptr; + + LVShared *Shared = nullptr; + + // Object files have only one type stream that contains both types and ids. + // Precompiled header objects don't contain an IPI stream. Use the TPI. + LazyRandomTypeCollection &types() { + return TypeServer ? TypeServer->types() + : (PrecompHeader ? *PrecompHeader : Input.types()); + } + LazyRandomTypeCollection &ids() { + return TypeServer ? TypeServer->ids() + : (PrecompHeader ? *PrecompHeader : Input.ids()); + } + + using LVScopeStack = std::stack; + LVScopeStack ScopeStack; + LVScope *ReaderParent = nullptr; + LVScope *ReaderScope = nullptr; + bool InCompileUnitScope = false; + + // Allow processing of argument list. + bool ProcessArgumentList = false; + StringRef OverloadedMethodName; + std::string CompileUnitName; + + // Inlined functions source information. + using LVInlineeEntry = std::pair; + using LVInlineeInfo = std::map; + LVInlineeInfo InlineeInfo; + + Error visitFieldListMemberStream(TypeIndex TI, LVElement *Element, + ArrayRef FieldList); + + LVType *createBaseType(TypeIndex TI, StringRef TypeName); + LVType *createPointerType(TypeIndex TI, StringRef TypeName); + LVSymbol *createParameter(TypeIndex TI, StringRef Name, LVScope *Parent); + LVSymbol *createParameter(LVElement *Element, StringRef Name, + LVScope *Parent); + void createDataMember(CVMemberRecord &Record, LVScope *Parent, StringRef Name, + TypeIndex Type, MemberAccess Access); + void createParents(StringRef ScopedName, LVElement *Element); + +public: + LVLogicalVisitor(LVCodeViewReader *Reader, ScopedPrinter &W, + llvm::pdb::InputFile &Input); + ~LVLogicalVisitor(); + + // Input source in the case of type server or precompiled header. + void setInput(std::shared_ptr TypeServer) { + this->TypeServer = TypeServer; + } + void setInput(std::shared_ptr PrecompHeader) { + this->PrecompHeader = PrecompHeader; + } + + void addInlineeInfo(TypeIndex TI, uint32_t LineNumber, StringRef Filename) { + InlineeInfo.emplace(std::piecewise_construct, std::forward_as_tuple(TI), + std::forward_as_tuple(LineNumber, Filename)); + } + + void printTypeIndex(StringRef FieldName, TypeIndex TI, uint32_t StreamIdx); + void printMemberAttributes(MemberAttributes Attrs); + void printMemberAttributes(MemberAccess Access, MethodKind Kind, + MethodOptions Options); + + LVElement *createElement(TypeLeafKind Kind); + LVElement *createElement(SymbolKind Kind); + LVElement *createElement(TypeIndex TI, TypeLeafKind Kind); + + // Break down the annotation byte code and calculate code and line offsets. + Error inlineSiteAnnotation(LVScope *AbstractFunction, + LVScope *InlinedFunction, + InlineSiteSym &InlineSite); + + void pushScope(LVScope *Scope) { + ScopeStack.push(ReaderParent); + ReaderParent = ReaderScope; + ReaderScope = Scope; + } + void popScope() { + ReaderScope = ReaderParent; + ReaderParent = ScopeStack.top(); + ScopeStack.pop(); + } + void closeScope() { + if (InCompileUnitScope) { + InCompileUnitScope = false; + popScope(); + } + } + void setRoot(LVScope *Root) { ReaderScope = Root; } + + void addElement(LVScope *Scope, bool IsCompileUnit); + void addElement(LVSymbol *Symbol); + void addElement(LVType *Type); + + std::string getCompileUnitName() { return CompileUnitName; } + void setCompileUnitName(std::string Name) { + CompileUnitName = std::move(Name); + } + +public: + LVElement *getElement(uint32_t StreamIdx, TypeIndex TI, + LVScope *Parent = nullptr); + LVShared *getShared() { return Shared; } + + LVScope *getReaderScope() const { return ReaderScope; } + + void printTypeBegin(CVType &Record, TypeIndex TI, LVElement *Element, + uint32_t StreamIdx); + void printTypeEnd(CVType &Record); + void printMemberBegin(CVMemberRecord &Record, TypeIndex TI, + LVElement *Element, uint32_t StreamIdx); + void printMemberEnd(CVMemberRecord &Record); + + void startProcessArgumentList() { ProcessArgumentList = true; } + void stopProcessArgumentList() { ProcessArgumentList = false; } + + void processFiles(); + void processLines(); + void processNamespaces(); + + void printRecords(raw_ostream &OS) const; + +public: + Error visitUnknownType(CVType &Record, TypeIndex TI); + Error visitKnownRecord(CVType &Record, ArgListRecord &Args, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, ArrayRecord &AT, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, BitFieldRecord &BF, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, BuildInfoRecord &BI, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, ClassRecord &Class, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, EnumRecord &Enum, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, FieldListRecord &FieldList, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, FuncIdRecord &Func, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, LabelRecord &LR, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, ModifierRecord &Mod, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, MemberFuncIdRecord &Id, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, MemberFunctionRecord &MF, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, MethodOverloadListRecord &Overloads, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, PointerRecord &Ptr, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, ProcedureRecord &Proc, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, UnionRecord &Union, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, TypeServer2Record &TS, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, VFTableRecord &VFT, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, VFTableShapeRecord &Shape, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, StringListRecord &Strings, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, StringIdRecord &String, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, UdtSourceLineRecord &SourceLine, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, UdtModSourceLineRecord &ModSourceLine, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, PrecompRecord &Precomp, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, EndPrecompRecord &EndPrecomp, + TypeIndex TI, LVElement *Element); + + Error visitUnknownMember(CVMemberRecord &Record, TypeIndex TI); + Error visitKnownMember(CVMemberRecord &Record, BaseClassRecord &Base, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, DataMemberRecord &Field, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, EnumeratorRecord &Enum, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, ListContinuationRecord &Cont, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, NestedTypeRecord &Nested, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, OneMethodRecord &Method, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, OverloadedMethodRecord &Method, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, StaticDataMemberRecord &Field, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, VFPtrRecord &VFTable, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, VirtualBaseClassRecord &Base, + TypeIndex TI, LVElement *Element); + + template + Error visitKnownMember(CVMemberRecord &Record, + TypeVisitorCallbacks &Callbacks, TypeIndex TI, + LVElement *Element) { + TypeRecordKind RK = static_cast(Record.Kind); + T KnownRecord(RK); + if (Error Err = Callbacks.visitKnownMember(Record, KnownRecord)) + return Err; + if (Error Err = visitKnownMember(Record, KnownRecord, TI, Element)) + return Err; + return Error::success(); + } + + template + Error visitKnownRecord(CVType &Record, TypeIndex TI, LVElement *Element) { + TypeRecordKind RK = static_cast(Record.kind()); + T KnownRecord(RK); + if (Error Err = TypeDeserializer::deserializeAs( + const_cast(Record), KnownRecord)) + return Err; + if (Error Err = visitKnownRecord(Record, KnownRecord, TI, Element)) + return Err; + return Error::success(); + } + + Error visitMemberRecord(CVMemberRecord &Record, + TypeVisitorCallbacks &Callbacks, TypeIndex TI, + LVElement *Element); + Error finishVisitation(CVType &Record, TypeIndex TI, LVElement *Element); +}; + +} // namespace logicalview +} // namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWVISITOR_H Index: llvm/include/llvm/DebugInfo/LogicalView/Readers/LVELFReader.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Readers/LVELFReader.h @@ -0,0 +1,159 @@ +//===-- LVELFReader.h -------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVELFReader class, which is used to describe a +// debug information (DWARF) reader. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVELFREADER_H +#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVELFREADER_H + +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h" +#include + +namespace llvm { +namespace logicalview { + +class LVElement; +class LVLine; +class LVScopeCompileUnit; +class LVSymbol; +class LVType; + +using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; + +class LVELFReader final : public LVBinaryReader { + object::ObjectFile &Obj; + + // The ELF reader does not use this container; it is defined only to + // call 'prettyPrintRegisterOp'. + DIDumpOptions DumpOpts; + + // Indicates if ranges data are available; in the case of split DWARF any + // reference to ranges is valid only if the skeleton DIE has been loaded. + bool RangesDataAvailable = false; + DWARFDie DummyDie; + LVAddress CUBaseAddress = 0; + LVAddress CUHighAddress = 0; + + // Current elements during the processing of a DIE. + LVElement *CurrentElement = nullptr; + LVScope *CurrentScope = nullptr; + LVSymbol *CurrentSymbol = nullptr; + LVType *CurrentType = nullptr; + LVOffset CurrentOffset = 0; + LVOffset CurrentEndOffset = 0; + + // In Dwarf v4, the files are 1-indexed. + // In DWARF v5, the files are 0-indexed. + // The ELF reader expects the indexes as 1-indexed. + bool IncrementFileIndex = false; + + // Address ranges collected for current DIE. + std::vector CurrentRanges; + + // Symbols with locations for current compile unit. + LVSymbols SymbolsWithLocations; + + // Global Offsets (Offset, Element). + LVOffsetElementMap GlobalOffsets; + + // Low PC and High PC values for DIE being processed. + LVAddress CurrentLowPC = 0; + LVAddress CurrentHighPC = 0; + bool FoundLowPC = false; + bool FoundHighPC = false; + + // Cross references (Elements). + using LVElementSet = std::unordered_set; + using LVElementEntry = std::pair; + using LVElementReference = std::unordered_map; + LVElementReference ElementTable; + + Error loadTargetInfo(const object::ObjectFile &Obj); + + void mapRangeAddress(const object::ObjectFile &Obj) override; + + LVElement *createElement(dwarf::Tag Tag); + void traverseDieAndChildren(DWARFDie &DIE, LVScope *Parent, + DWARFDie &SkeletonDie); + // Process the attributes for the given DIE. + LVScope *processOneDie(const DWARFDie &InputDIE, LVScope *Parent, + DWARFDie &SkeletonDie); + void processOneAttribute(const DWARFDie &Die, LVOffset *OffsetPtr, + AttributeSpec AttrSpec); + void createLineAndFileRecords(const DWARFDebugLine::LineTable *Lines); + void processLocationGaps(); + + // Add offset to global map. + void addGlobalOffset(LVOffset Offset) { + if (GlobalOffsets.find(Offset) == GlobalOffsets.end()) + // Just associate the DIE offset with a null element, as we do not + // know if the referenced element has been created. + GlobalOffsets.emplace(Offset, nullptr); + } + + // Remove offset from global map. + void removeGlobalOffset(LVOffset Offset) { + LVOffsetElementMap::iterator Iter = GlobalOffsets.find(Offset); + if (Iter != GlobalOffsets.end()) + GlobalOffsets.erase(Iter); + } + + // Get the location information for DW_AT_data_member_location. + void getLocationMember(dwarf::Attribute Attr, DWARFFormValue &FormValue, + const DWARFDie &Die, uint64_t OffsetOnEntry); + void getLocationList(dwarf::Attribute Attr, DWARFFormValue &FormValue, + const DWARFDie &Die, uint64_t OffsetOnEntry, + bool CallSiteLocation = false); + void updateReference(dwarf::Attribute Attr, DWARFFormValue &FormValue); + + // Get an element given the DIE offset. + LVElement *getElementForOffset(LVOffset offset, LVElement *Element); + +protected: + Error createScopes() override; + void sortScopes() override; + +public: + LVELFReader() = delete; + LVELFReader(StringRef Filename, StringRef FileFormatName, + object::ObjectFile &Obj, ScopedPrinter &W) + : LVBinaryReader(Filename, FileFormatName, W, LVBinaryType::ELF), + Obj(Obj) {} + LVELFReader(const LVELFReader &) = delete; + LVELFReader &operator=(const LVELFReader &) = delete; + ~LVELFReader() = default; + +public: + LVAddress getCUBaseAddress() const { return CUBaseAddress; } + void setCUBaseAddress(LVAddress Address) { CUBaseAddress = Address; } + LVAddress getCUHighAddress() const { return CUHighAddress; } + void setCUHighAddress(LVAddress Address) { CUHighAddress = Address; } + + const LVSymbols &GetSymbolsWithLocations() const { + return SymbolsWithLocations; + } + + std::string getRegisterName(LVSmall Opcode, uint64_t Operands[2]) override; + +public: + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVELFREADER_H Index: llvm/lib/DebugInfo/CMakeLists.txt =================================================================== --- llvm/lib/DebugInfo/CMakeLists.txt +++ llvm/lib/DebugInfo/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(DWARF) add_subdirectory(GSYM) +add_subdirectory(LogicalView) add_subdirectory(MSF) add_subdirectory(CodeView) add_subdirectory(PDB) Index: llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -1417,6 +1417,22 @@ return true; } +bool DWARFDebugLine::LineTable::getDirectoryForEntry( + const FileNameEntry &Entry, std::string &Directory) const { + if (Prologue.getVersion() >= 5) { + if (Entry.DirIdx < Prologue.IncludeDirectories.size()) + Directory = + dwarf::toString(Prologue.IncludeDirectories[Entry.DirIdx], ""); + return true; + } else { + if (0 < Entry.DirIdx && Entry.DirIdx <= Prologue.IncludeDirectories.size()) + Directory = + dwarf::toString(Prologue.IncludeDirectories[Entry.DirIdx - 1], ""); + return true; + } + return false; +} + // We want to supply the Unit associated with a .debug_line[.dwo] table when // we dump it, if possible, but still dump the table even if there isn't a Unit. // Therefore, collect up handles on all the Units that point into the Index: llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -225,7 +225,7 @@ } } -static bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS, +bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS, DIDumpOptions DumpOpts, uint8_t Opcode, const uint64_t Operands[2], const MCRegisterInfo *MRI, bool isEH) { Index: llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -587,7 +587,7 @@ return Error::success(); } -bool DWARFUnit::parseDWO() { +bool DWARFUnit::parseDWO(StringRef DWOAlternativeLocation) { if (IsDWO) return false; if (DWO.get()) @@ -611,8 +611,17 @@ if (!DWOId) return false; auto DWOContext = Context.getDWOContext(AbsolutePath); - if (!DWOContext) - return false; + if (!DWOContext) { + // Use the alternative location to get the DWARF context for the DWO object. + if (DWOAlternativeLocation.empty()) + return false; + // If the alternative context does not correspond to the original DWO object + // (different hashes), the below 'getDWOCompileUnitForHash' call will catch + // the issue, with a returned null context. + DWOContext = Context.getDWOContext(DWOAlternativeLocation); + if (!DWOContext) + return false; + } DWARFCompileUnit *DWOCU = DWOContext->getDWOCompileUnitForHash(*DWOId); if (!DWOCU) Index: llvm/lib/DebugInfo/LogicalView/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/CMakeLists.txt @@ -0,0 +1,52 @@ +set(LLVM_LINK_COMPONENTS + BinaryFormat + DebugInfoDWARF + DebugInfoCodeView + DebugInfoPDB + Demangle + MC + Object + Support + ) + +macro(add_lv_impl_folder group) + list(APPEND LV_IMPL_SOURCES ${ARGN}) + source_group(${group} FILES ${ARGN}) +endmacro() + +add_lv_impl_folder(Core + Core/LVCompare.cpp + Core/LVElement.cpp + Core/LVLine.cpp + Core/LVLocation.cpp + Core/LVObject.cpp + Core/LVOptions.cpp + Core/LVRange.cpp + Core/LVReader.cpp + Core/LVScope.cpp + Core/LVSort.cpp + Core/LVSupport.cpp + Core/LVSymbol.cpp + Core/LVType.cpp + ) + +add_lv_impl_folder(Readers + LVReaderHandler.cpp + Readers/LVBinaryReader.cpp + Readers/LVCodeViewReader.cpp + Readers/LVCodeViewVisitor.cpp + Readers/LVELFReader.cpp + ) + +list(APPEND LIBLV_ADDITIONAL_HEADER_DIRS + "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/LogicalView" + "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/LogicalView/Core" + "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/LogicalView/Readers" + ) + +add_llvm_library(LLVMDebugInfoLogicalView + ${LV_IMPL_SOURCES} + + ADDITIONAL_HEADER_DIRS + ${LIBLV_ADDITIONAL_HEADER_DIRS} + ) Index: llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp @@ -0,0 +1,422 @@ +//===-- LVCompare.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVCompare class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Compare" + +enum class LVCompareItem { Scope, Symbol, Type, Line, Total }; +enum class LVCompareIndex { Header, Expected, Missing, Added }; +using LVCompareEntry = std::tuple; +using LVCompareInfo = std::map; +LVCompareInfo Results = { + {LVCompareItem::Line, LVCompareEntry("Lines", 0, 0, 0)}, + {LVCompareItem::Scope, LVCompareEntry("Scopes", 0, 0, 0)}, + {LVCompareItem::Symbol, LVCompareEntry("Symbols", 0, 0, 0)}, + {LVCompareItem::Type, LVCompareEntry("Types", 0, 0, 0)}, + {LVCompareItem::Total, LVCompareEntry("Total", 0, 0, 0)}}; +static LVCompareInfo::iterator IterTotal = Results.end(); + +constexpr unsigned getHeader() { + return static_cast(LVCompareIndex::Header); +} +constexpr unsigned getExpected() { + return static_cast(LVCompareIndex::Expected); +} +constexpr unsigned getMissing() { + return static_cast(LVCompareIndex::Missing); +} +constexpr unsigned getAdded() { + return static_cast(LVCompareIndex::Added); +} + +LVCompare *CurrentComparator = nullptr; +LVCompare &LVCompare::getInstance() { + static LVCompare DefaultComparator(outs()); + return CurrentComparator ? *CurrentComparator : DefaultComparator; +} +void LVCompare::setInstance(LVCompare *Comparator) { + CurrentComparator = Comparator; +} + +LVCompare::LVCompare(raw_ostream &OS) : OS(OS) { + PrintLines = options().getPrintLines(); + PrintSymbols = options().getPrintSymbols(); + PrintTypes = options().getPrintTypes(); + PrintScopes = + options().getPrintScopes() || PrintLines || PrintSymbols || PrintTypes; +} + +void zeroResults() { + // In case the same reader instance is used. + for (LVCompareInfo::reference Entry : Results) { + std::get(Entry.second) = 0; + std::get(Entry.second) = 0; + std::get(Entry.second) = 0; + } + IterTotal = Results.find(LVCompareItem::Total); + assert(IterTotal != Results.end()); +} + +LVCompareInfo::iterator getResultsEntry(LVElement *Element) { + LVCompareItem Kind; + if (Element->getIsLine()) + Kind = LVCompareItem::Line; + else if (Element->getIsScope()) + Kind = LVCompareItem::Scope; + else if (Element->getIsSymbol()) + Kind = LVCompareItem::Symbol; + else + Kind = LVCompareItem::Type; + + // Used to update the expected, missing or added entry for the given kind. + LVCompareInfo::iterator Iter = Results.find(Kind); + assert(Iter != Results.end()); + return Iter; +} + +void updateExpected(LVElement *Element) { + LVCompareInfo::iterator Iter = getResultsEntry(Element); + // Update total for expected. + ++std::get(IterTotal->second); + // Update total for specific element kind. + ++std::get(Iter->second); +} + +void updateMissingOrAdded(LVElement *Element, LVComparePass Pass) { + LVCompareInfo::iterator Iter = getResultsEntry(Element); + if (Pass == LVComparePass::Missing) { + ++std::get(IterTotal->second); + ++std::get(Iter->second); + } else { + ++std::get(IterTotal->second); + ++std::get(Iter->second); + } +} + +Error LVCompare::execute(LVReader *ReferenceReader, LVReader *TargetReader) { + setInstance(this); + // In the case of added elements, the 'Reference' reader will be modified; + // those elements will be added to it. Update the current reader instance. + LVReader::setInstance(ReferenceReader); + + auto PrintHeader = [this](LVScopeRoot *LHS, LVScopeRoot *RHS) { + LLVM_DEBUG({ + dbgs() << "[Reference] " << LHS->getName() << "\n" + << "[Target] " << RHS->getName() << "\n"; + }); + OS << "\nReference: " << formattedName(LHS->getName()) << "\n" + << "Target: " << formattedName(RHS->getName()) << "\n"; + }; + + // We traverse the given scopes tree ('Reference' and 'Target') twice. + // The first time we look for missing items from the 'Reference' and the + // second time we look for items added to the 'Target'. + // The comparison test includes the name, lexical level, type, source + // location, etc. + LVScopeRoot *ReferenceRoot = ReferenceReader->getScopesRoot(); + LVScopeRoot *TargetRoot = TargetReader->getScopesRoot(); + ReferenceRoot->setIsInCompare(); + TargetRoot->setIsInCompare(); + + // Reset possible previous results. + zeroResults(); + + if (options().getCompareContext()) { + // Perform a logical view comparison as a whole unit. We start at the + // root reference; at each scope an equal test is applied to its children. + // If a difference is found, the current path is marked as missing. + auto CompareViews = [this](LVScopeRoot *LHS, LVScopeRoot *RHS) -> Error { + LHS->markMissingParents(RHS, /*TraverseChildren=*/true); + if (LHS->getIsMissingLink() && options().getReportAnyView()) { + // As we are printing a missing tree, enable formatting. + options().setPrintFormatting(); + OS << "\nMissing Tree:\n"; + if (Error Err = LHS->doPrint(/*Split=*/false, /*Match=*/false, + /*Print=*/true, OS)) + return Err; + options().resetPrintFormatting(); + } + + return Error::success(); + }; + + // If the user has requested printing details for the comparison, we + // disable the indentation and the added/missing tags ('+'/'-'), as the + // details are just a list of elements. + options().resetPrintFormatting(); + + PrintHeader(ReferenceRoot, TargetRoot); + Reader = ReferenceReader; + if (Error Err = CompareViews(ReferenceRoot, TargetRoot)) + return Err; + FirstMissing = true; + ReferenceRoot->report(LVComparePass::Missing); + + PrintHeader(TargetRoot, ReferenceRoot); + Reader = TargetReader; + if (Error Err = CompareViews(TargetRoot, ReferenceRoot)) + return Err; + FirstMissing = true; + TargetRoot->report(LVComparePass::Added); + + options().setPrintFormatting(); + + // Display a summary with the elements missing and/or added. + printSummary(); + } else { + // Perform logical elements comparison. An equal test is apply to each + // element. If a difference is found, the reference element is marked as + // 'missing'. + // The final comparison result will show the 'Reference' scopes tree, + // having both missing and added elements. + using LVScopeLink = std::map; + LVScopeLink ScopeLinks; + auto CompareReaders = [&](LVReader *LHS, LVReader *RHS, LVElements &Set, + LVComparePass Pass) -> Error { + auto FindMatch = [&](auto &References, auto &Targets, + const char *Category) -> Error { + LVElements Elements; + for (LVElement *Reference : References) { + // Report elements that can be printed; ignore logical elements that + // have qualifiers. + if (Reference->getIncludeInPrint()) { + if (Pass == LVComparePass::Missing) + updateExpected(Reference); + Reference->setIsInCompare(); + LVElement *CurrentTarget = nullptr; + if (std::any_of(Targets.begin(), Targets.end(), + [&](auto Target) -> bool { + CurrentTarget = Target; + return Reference->equals(Target); + })) { + if (Pass == LVComparePass::Missing && Reference->getIsScope()) { + // If the elements being compared are scopes and are a match, + // they are recorded, to be used when creating the augmented + // tree, as insertion points for the "added" items. + ScopeLinks.emplace(static_cast(CurrentTarget), + static_cast(Reference)); + } + } else { + // Element is missing or added. + Pass == LVComparePass::Missing ? Reference->setIsMissing() + : Reference->setIsAdded(); + Elements.push_back(Reference); + updateMissingOrAdded(Reference, Pass); + // Record missing/added element. + addPassEntry(Reader, Reference, Pass); + } + } + } + if (Pass == LVComparePass::Added) + // Record all the current missing elements for this category. + Set.insert(Set.end(), Elements.begin(), Elements.end()); + if (options().getReportList()) { + if (Elements.size()) { + OS << "\n(" << Elements.size() << ") " + << (Pass == LVComparePass::Missing ? "Missing" : "Added") << " " + << Category << ":\n"; + for (const LVElement *Element : Elements) { + if (Error Err = Element->doPrint(/*Split=*/false, /*Match=*/false, + /*Print=*/true, OS)) + return Err; + } + } + } + + return Error::success(); + }; + + // First compare the scopes, so they will be inserted at the front of + // the missing elements list. When they are moved, their children are + // moved as well and no additional work is required. + if (options().getCompareScopes()) + if (Error Err = FindMatch(LHS->getScopes(), RHS->getScopes(), "Scopes")) + return Err; + if (options().getCompareSymbols()) + if (Error Err = + FindMatch(LHS->getSymbols(), RHS->getSymbols(), "Symbols")) + return Err; + if (options().getCompareTypes()) + if (Error Err = FindMatch(LHS->getTypes(), RHS->getTypes(), "Types")) + return Err; + if (options().getCompareLines()) + if (Error Err = FindMatch(LHS->getLines(), RHS->getLines(), "Lines")) + return Err; + + return Error::success(); + }; + + // If the user has requested printing details for the comparison, we + // disable the indentation and the added/missing tags ('+'/'-'), as the + // details are just a list of elements. + options().resetPrintFormatting(); + + PrintHeader(ReferenceRoot, TargetRoot); + // Include the root in the expected count. + updateExpected(ReferenceRoot); + + LVElements ElementsToAdd; + Reader = ReferenceReader; + if (Error Err = CompareReaders(ReferenceReader, TargetReader, ElementsToAdd, + LVComparePass::Missing)) + return Err; + Reader = TargetReader; + if (Error Err = CompareReaders(TargetReader, ReferenceReader, ElementsToAdd, + LVComparePass::Added)) + return Err; + + LLVM_DEBUG({ + dbgs() << "\nReference/Target Scope links:\n"; + for (LVScopeLink::const_reference Entry : ScopeLinks) + dbgs() << "Source: " << hexSquareString(Entry.first->getOffset()) << " " + << "Destination: " << hexSquareString(Entry.second->getOffset()) + << "\n"; + dbgs() << "\n"; + }); + + // Add the 'missing' elements from the 'Target' into the 'Reference'. + // First insert the missing scopes, as they include any missing children. + LVScope *Parent = nullptr; + for (LVElement *Element : ElementsToAdd) { + LLVM_DEBUG({ + dbgs() << "Element to Insert: " << hexSquareString(Element->getOffset()) + << ", Parent: " + << hexSquareString(Element->getParentScope()->getOffset()) + << "\n"; + }); + // Skip already inserted elements. They were inserted, if their parents + // were missing. When inserting them, all the children are moved. + if (Element->getHasMoved()) + continue; + + // We need to find an insertion point in the reference scopes tree. + Parent = Element->getParentScope(); + if (ScopeLinks.find(Parent) != ScopeLinks.end()) { + LVScope *InsertionPoint = ScopeLinks[Parent]; + LLVM_DEBUG({ + dbgs() << "Inserted at: " + << hexSquareString(InsertionPoint->getOffset()) << "\n"; + }); + if (Parent->removeElement(Element)) { + // Be sure we have a current compile unit. + getReader().setCompileUnit(InsertionPoint->getCompileUnitParent()); + InsertionPoint->addElement(Element); + Element->updateLevel(InsertionPoint, /*Moved=*/true); + } + } + } + + options().setPrintFormatting(); + + // Display the augmented reference scopes tree. + if (options().getReportAnyView()) + if (Error Err = ReferenceReader->doPrint()) + return Err; + + LLVM_DEBUG({ + dbgs() << "\nModified Reference Reader"; + if (Error Err = ReferenceReader->doPrint()) + return Err; + dbgs() << "\nModified Target Reader"; + if (Error Err = TargetReader->doPrint()) + return Err; + }); + + // Display a summary with the elements missing and/or added. + printSummary(); + } + + return Error::success(); +} + +void LVCompare::printCurrentStack() { + for (const LVScope *Scope : ScopeStack) { + Scope->printAttributes(OS); + OS << Scope->lineNumberAsString(/*ShowZero=*/true) << " " << Scope->kind() + << " " << formattedName(Scope->getName()) << "\n"; + } +} + +void LVCompare::printItem(LVElement *Element, LVComparePass Pass) { + // Record expected, missing, added. + updateExpected(Element); + updateMissingOrAdded(Element, Pass); + + // Record missing/added element. + if (Element->getIsMissing()) + addPassEntry(Reader, Element, Pass); + + if ((!PrintLines && Element->getIsLine()) || + (!PrintScopes && Element->getIsScope()) || + (!PrintSymbols && Element->getIsSymbol()) || + (!PrintTypes && Element->getIsType())) + return; + + if (Element->getIsMissing()) { + if (FirstMissing) { + OS << "\n"; + FirstMissing = false; + } + + StringRef Kind = Element->kind(); + StringRef Name = + Element->getIsLine() ? Element->getPathname() : Element->getName(); + StringRef Status = (Pass == LVComparePass::Missing) ? "Missing" : "Added"; + OS << Status << " " << Kind << " '" << Name << "'"; + if (Element->getLineNumber() > 0) + OS << " at line " << Element->getLineNumber(); + OS << "\n"; + + if (options().getReportList()) { + printCurrentStack(); + Element->printAttributes(OS); + OS << Element->lineNumberAsString(/*ShowZero=*/true) << " " << Kind << " " + << Name << "\n"; + } + } +} + +void LVCompare::printSummary() const { + if (!options().getPrintSummary()) + return; + std::string Separator = std::string(40, '-'); + auto PrintSeparator = [&]() { OS << Separator << "\n"; }; + auto PrintHeadingRow = [&](const char *T, const char *U, const char *V, + const char *W) { + OS << format("%-9s%9s %9s %9s\n", T, U, V, W); + }; + auto PrintDataRow = [&](const char *T, unsigned U, unsigned V, unsigned W) { + OS << format("%-9s%9d %9d %9d\n", T, U, V, W); + }; + + OS << "\n"; + PrintSeparator(); + PrintHeadingRow("Element", "Expected", "Missing", "Added"); + PrintSeparator(); + for (LVCompareInfo::reference Entry : Results) { + if (Entry.first == LVCompareItem::Total) + PrintSeparator(); + PrintDataRow(std::get(Entry.second), + std::get(Entry.second), + std::get(Entry.second), + std::get(Entry.second)); + } +} + +void LVCompare::print(raw_ostream &OS) const { OS << "LVCompare\n"; } Index: llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp @@ -0,0 +1,564 @@ +//===-- LVElement.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVElement class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Element" + +LVElementDispatch LVElement::Dispatch = { + {LVElementKind::Discarded, &LVElement::getIsDiscarded}, + {LVElementKind::Global, &LVElement::getIsGlobalReference}, + {LVElementKind::Optimized, &LVElement::getIsOptimized}}; + +LVType *LVElement::getTypeAsType() const { + return ElementType && ElementType->getIsType() + ? static_cast(ElementType) + : nullptr; +} + +LVScope *LVElement::getTypeAsScope() const { + return ElementType && ElementType->getIsScope() + ? static_cast(ElementType) + : nullptr; +} + +// Set the element type. +void LVElement::setGenericType(LVElement *Element) { + if (!Element->isTemplateParam()) { + setType(Element); + return; + } + // For template parameters, the instance type can be a type or a scope. + if (options().getAttributeArgument()) { + if (Element->getIsKindType()) + setType(Element->getTypeAsType()); + else if (Element->getIsKindScope()) + setType(Element->getTypeAsScope()); + } else + setType(Element); +} + +// Discriminator as string. +std::string LVElement::discriminatorAsString() const { + uint32_t Discriminator = getDiscriminator(); + std::string String; + raw_string_ostream Stream(String); + if (Discriminator && options().getAttributeDiscriminator()) + Stream << "," << Discriminator; + return String; +} + +// Get the type as a string. +StringRef LVElement::typeAsString() const { + return getHasType() ? getTypeName() : typeVoid(); +} + +// Get name for element type. +StringRef LVElement::getTypeName() const { + return ElementType ? ElementType->getName() : StringRef(); +} + +static size_t getStringIndex(StringRef Name) { + // Convert the name to Unified format ('\' have been converted into '/'). + std::string Pathname(transformPath(Name)); + + // Depending on the --attribute=filename and --attribute=pathname command + // line options, use the basename or the full pathname as the name. + if (!options().getAttributePathname()) { + // Get the basename by ignoring any prefix up to the last slash ('/'). + StringRef Basename = Pathname; + size_t Pos = Basename.rfind('/'); + if (Pos != std::string::npos) + Basename = Basename.substr(Pos + 1); + return getStringPool().getIndex(Basename); + } + + return getStringPool().getIndex(Pathname); +} + +void LVElement::setName(StringRef ElementName) { + // In the case of Root or Compile Unit, get index for the flatted out name. + NameIndex = getTransformName() ? getStringIndex(ElementName) + : getStringPool().getIndex(ElementName); +} + +void LVElement::setFilename(StringRef Filename) { + // Get index for the flatted out filename. + FilenameIndex = getStringIndex(Filename); +} + +void LVElement::setInnerComponent(StringRef Name) { + if (Name.size()) { + StringRef InnerComponent; + std::tie(std::ignore, InnerComponent) = getInnerComponent(Name); + setName(InnerComponent); + } +} + +// Return the string representation of a DIE offset. +std::string LVElement::typeOffsetAsString() const { + if (options().getAttributeOffset()) { + LVElement *Element = getType(); + return hexSquareString(Element ? Element->getOffset() : 0); + } + return {}; +} + +StringRef LVElement::accessibilityString(uint32_t Access) const { + uint32_t Value = getAccessibilityCode(); + switch (Value ? Value : Access) { + case dwarf::DW_ACCESS_public: + return "public"; + case dwarf::DW_ACCESS_protected: + return "protected"; + case dwarf::DW_ACCESS_private: + return "private"; + default: + return StringRef(); + } +} + +uint32_t LVElement::getAccessibilityCode(MemberAccess Access) { + switch (Access) { + case MemberAccess::Private: + return dwarf::DW_ACCESS_private; + case MemberAccess::Protected: + return dwarf::DW_ACCESS_protected; + case MemberAccess::Public: + return dwarf::DW_ACCESS_public; + default: + return 0; + } +} + +StringRef LVElement::externalString() const { + return getIsExternal() ? "extern" : StringRef(); +} + +StringRef LVElement::inlineCodeString(uint32_t Code) const { + uint32_t Value = getInlineCode(); + switch (Value ? Value : Code) { + case dwarf::DW_INL_not_inlined: + return "not_inlined"; + case dwarf::DW_INL_inlined: + return "inlined"; + case dwarf::DW_INL_declared_not_inlined: + return "declared_not_inlined"; + case dwarf::DW_INL_declared_inlined: + return "declared_inlined"; + default: + return StringRef(); + } +} + +StringRef LVElement::virtualityString(uint32_t Virtuality) const { + uint32_t Value = getVirtualityCode(); + switch (Value ? Value : Virtuality) { + case dwarf::DW_VIRTUALITY_none: + return StringRef(); + case dwarf::DW_VIRTUALITY_virtual: + return "virtual"; + case dwarf::DW_VIRTUALITY_pure_virtual: + return "pure virtual"; + default: + return StringRef(); + } +} + +uint32_t LVElement::getVirtualityCode(MethodKind Virtuality) { + switch (Virtuality) { + case MethodKind::Virtual: + return dwarf::DW_VIRTUALITY_virtual; + case MethodKind::PureVirtual: + return dwarf::DW_VIRTUALITY_pure_virtual; + case MethodKind::IntroducingVirtual: + case MethodKind::PureIntroducingVirtual: + // No direct equivalents in DWARF. Assume Virtual. + return dwarf::DW_VIRTUALITY_virtual; + default: + return 0; + } +} + +void LVElement::resolve() { + if (getIsResolved()) + return; + setIsResolved(); + + resolveReferences(); + resolveParents(); + resolveExtra(); + resolveName(); +} + +// Set File/Line using the specification element. +void LVElement::setFileLine(LVElement *Specification) { + // In the case of inlined functions, the correct scope must be associated + // with the file and line information of the outline version. + if (!isLined()) { + setLineNumber(Specification->getLineNumber()); + setIsLineFromReference(); + } + if (!isFiled()) { + setFilenameIndex(Specification->getFilenameIndex()); + setIsFileFromReference(); + } +} + +void LVElement::resolveName() { + // Set the qualified name if requested. + if (options().getAttributeQualified()) + resolveQualifiedName(); + + setIsResolvedName(); +} + +// Resolve any parents. +void LVElement::resolveParents() { + if (isRoot() || isCompileUnit()) + return; + + LVScope *Parent = getParentScope(); + if (Parent && !Parent->getIsCompileUnit()) + Parent->resolve(); +} + +// Generate a name for unnamed elements. +void LVElement::generateName(std::string &Prefix) const { + // CodeView elements that are not referenced and not finalized. + LVScope *Scope = getParentScope(); + if (!Scope) + return; + + // Use its parent name and any line information. + Prefix.append(std::string(Scope->getName())); + Prefix.append("::"); + Prefix.append(isLined() ? lineNumberAsString(/*ShowZero=*/true) : "?"); + + // Remove any whitespaces. + Prefix.erase(std::remove_if(Prefix.begin(), Prefix.end(), ::isspace), + Prefix.end()); +} + +// Generate a name for unnamed elements. +void LVElement::generateName() { + setIsAnonymous(); + std::string Name; + generateName(Name); + setName(Name); + setIsGeneratedName(); +} + +void LVElement::updateLevel(LVScope *Parent, bool Moved) { + setLevel(Parent->getLevel() + 1); + if (Moved) + setHasMoved(); +} + +// Generate the full name for the element, to include special qualifiers. +void LVElement::resolveFullname(LVElement *BaseType, StringRef Name) { + // For the following sample code, + // void *p; + // some compilers do not generate an attribute for the associated type: + // DW_TAG_variable + // DW_AT_name 'p' + // DW_AT_type $1 + // ... + // $1: DW_TAG_pointer_type + // ... + // For those cases, generate the implicit 'void' type. + StringRef BaseTypename = BaseType ? BaseType->getName() : emptyString(); + bool GetBaseTypename = false; + bool UseBaseTypename = true; + bool UseNameText = true; + + switch (getTag()) { + case dwarf::DW_TAG_pointer_type: // "*"; + if (!BaseType) + BaseTypename = typeVoid(); + break; + case dwarf::DW_TAG_const_type: // "const" + case dwarf::DW_TAG_ptr_to_member_type: // "*" + case dwarf::DW_TAG_rvalue_reference_type: // "&&" + case dwarf::DW_TAG_reference_type: // "&" + case dwarf::DW_TAG_restrict_type: // "restrict" + case dwarf::DW_TAG_volatile_type: // "volatile" + case dwarf::DW_TAG_unaligned: // "unaligned" + break; + case dwarf::DW_TAG_base_type: + case dwarf::DW_TAG_compile_unit: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_enumerator: + case dwarf::DW_TAG_namespace: + case dwarf::DW_TAG_skeleton_unit: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_unspecified_type: + case dwarf::DW_TAG_GNU_template_parameter_pack: + GetBaseTypename = true; + break; + case dwarf::DW_TAG_array_type: + case dwarf::DW_TAG_call_site: + case dwarf::DW_TAG_entry_point: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_GNU_call_site: + case dwarf::DW_TAG_imported_module: + case dwarf::DW_TAG_imported_declaration: + case dwarf::DW_TAG_inlined_subroutine: + case dwarf::DW_TAG_label: + case dwarf::DW_TAG_subprogram: + case dwarf::DW_TAG_subrange_type: + case dwarf::DW_TAG_subroutine_type: + case dwarf::DW_TAG_typedef: + GetBaseTypename = true; + UseBaseTypename = false; + break; + case dwarf::DW_TAG_template_type_parameter: + case dwarf::DW_TAG_template_value_parameter: + UseBaseTypename = false; + break; + case dwarf::DW_TAG_GNU_template_template_param: + break; + case dwarf::DW_TAG_catch_block: + case dwarf::DW_TAG_lexical_block: + case dwarf::DW_TAG_try_block: + UseNameText = false; + break; + default: + llvm_unreachable("Invalid type."); + return; + break; + } + + // Overwrite if no given value. 'Name' is empty when resolving for scopes + // and symbols. In the case of types, it represents the type base name. + if (Name.empty() && GetBaseTypename) + Name = getName(); + + // Concatenate the elements to get the full type name. + // Type will be: base_parent + pre + base + parent + post. + std::string Fullname; + + if (UseNameText && Name.size()) + Fullname.append(std::string(Name)); + if (UseBaseTypename && BaseTypename.size()) { + if (UseNameText && Name.size()) + Fullname.append(" "); + Fullname.append(std::string(BaseTypename)); + } + + // For a better and consistent layout, check if the generated name + // contains double space sequences. + assert((Fullname.find(" ", 0) == std::string::npos) && + "Extra double spaces in name."); + + LLVM_DEBUG({ dbgs() << "Fullname = '" << Fullname << "'\n"; }); + setName(Fullname.c_str()); +} + +void LVElement::setFile(LVElement *Reference) { + if (!options().getAttributeAnySource()) + return; + + // At this point, any existing reference to another element, have been + // resolved and the file ID extracted from the DI entry. + if (Reference) + setFileLine(Reference); + + // The file information is used to show the source file for any element + // and display any new source file in relation to its parent element. + // a) Elements that are not inlined. + // - We record the DW_AT_decl_line and DW_AT_decl_file. + // b) Elements that are inlined. + // - We record the DW_AT_decl_line and DW_AT_decl_file. + // - We record the DW_AT_call_line and DW_AT_call_file. + // For both cases, we use the DW_AT_decl_file value to detect any changes + // in the source filename containing the element. Changes on this value + // indicates that the element being printed is not contained in the + // previous printed filename. + + // The source files are indexed starting at 0, but DW_AT_decl_file defines + // that 0 means no file; a value of 1 means the 0th entry. + size_t Index = 0; + + // An element with no source file information will use the reference + // attribute (DW_AT_specification, DW_AT_abstract_origin, DW_AT_extension) + // to update its information. + if (getIsFileFromReference() && Reference) { + Index = Reference->getFilenameIndex(); + if (Reference->getInvalidFilename()) + setInvalidFilename(); + setFilenameIndex(Index); + return; + } + + // The source files are indexed starting at 0, but DW_AT_decl_file + // defines that 0 means no file; a value of 1 means the 0th entry. + Index = getFilenameIndex(); + if (Index) { + StringRef Filename = getReader().getFilename(this, Index); + Filename.size() ? setFilename(Filename) : setInvalidFilename(); + } +} + +LVScope *LVElement::traverseParents(LVScopeGetFunction GetFunction) const { + LVScope *Parent = getParentScope(); + while (Parent && !(Parent->*GetFunction)()) + Parent = Parent->getParentScope(); + return Parent; +} + +LVScope *LVElement::getFunctionParent() const { + return traverseParents(&LVScope::getIsFunction); +} + +LVScope *LVElement::getCompileUnitParent() const { + return traverseParents(&LVScope::getIsCompileUnit); +} + +// Resolve the qualified name to include the parent hierarchy names. +void LVElement::resolveQualifiedName() { + if (!getIsReferencedType() || isBase() || getQualifiedResolved() || + !getIncludeInPrint()) + return; + + std::string Name; + + // Get the qualified name, excluding the Compile Unit. + LVScope *Parent = getParentScope(); + if (Parent && !Parent->getIsRoot()) { + while (Parent && !Parent->getIsCompileUnit()) { + Name.insert(0, "::"); + if (Parent->isNamed()) + Name.insert(0, std::string(Parent->getName())); + else { + std::string Temp; + Parent->generateName(Temp); + Name.insert(0, Temp); + } + Parent = Parent->getParentScope(); + } + } + + if (Name.size()) { + setQualifiedName(Name); + setQualifiedResolved(); + } + LLVM_DEBUG({ + dbgs() << "Offset: " << hexSquareString(getOffset()) + << ", Kind: " << formattedKind(kind()) + << ", Name: " << formattedName(getName()) + << ", QualifiedName: " << formattedName(Name) << "\n"; + }); +} + +bool LVElement::referenceMatch(const LVElement *Element) const { + return (getHasReference() && Element->getHasReference()) || + (!getHasReference() && !Element->getHasReference()); +} + +bool LVElement::equals(const LVElement *Element) const { + // The minimum factors that must be the same for an equality are: + // line number, level, name, qualified name and filename. + LLVM_DEBUG({ + dbgs() << "\n[Element::equals]\n" + << "Reference: " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << ", " + << "Qualified = " << formattedName(getQualifiedName()) << "\n" + << "Target : " + << "Kind = " << formattedKind(Element->kind()) << ", " + << "Name = " << formattedName(Element->getName()) << ", " + << "Qualified = " << formattedName(Element->getQualifiedName()) + << "\n" + << "Reference: " + << "NameIndex = " << getNameIndex() << ", " + << "QualifiedNameIndex = " << getQualifiedNameIndex() << ", " + << "FilenameIndex = " << getFilenameIndex() << "\n" + << "Target : " + << "NameIndex = " << Element->getNameIndex() << ", " + << "QualifiedNameIndex = " << Element->getQualifiedNameIndex() + << ", " + << "FilenameIndex = " << Element->getFilenameIndex() << "\n"; + }); + if ((getLineNumber() != Element->getLineNumber()) || + (getLevel() != Element->getLevel())) + return false; + + if ((getQualifiedNameIndex() != Element->getQualifiedNameIndex()) || + (getNameIndex() != Element->getNameIndex()) || + (getFilenameIndex() != Element->getFilenameIndex())) + return false; + + if (!getType() && !Element->getType()) + return true; + if (getType() && Element->getType()) + return getType()->equals(Element->getType()); + return false; +} + +// Print the FileName Index. +void LVElement::printFileIndex(raw_ostream &OS, bool Full) const { + if (options().getPrintFormatting() && options().getAttributeAnySource() && + getFilenameIndex()) { + + // Check if there is a change in the File ID sequence. + size_t Index = getFilenameIndex(); + if (options().changeFilenameIndex(Index)) { + // Just to keep a nice layout. + OS << "\n"; + printAttributes(OS, /*Full=*/false); + + OS << " {Source} "; + if (getInvalidFilename()) + OS << format("[0x%08x]\n", Index); + else + OS << formattedName(getPathname()) << "\n"; + } + } +} + +void LVElement::printReference(raw_ostream &OS, bool Full, + LVElement *Parent) const { + if (options().getPrintFormatting() && options().getAttributeReference()) + printAttributes(OS, Full, "{Reference} ", Parent, + referenceAsString(getLineNumber(), /*Spaces=*/false), + /*UseQuotes=*/false, /*PrintRef=*/true); +} + +void LVElement::printLinkageName(raw_ostream &OS, bool Full, + LVElement *Parent) const { + if (options().getPrintFormatting() && options().getAttributeLinkage()) { + printAttributes(OS, Full, "{Linkage} ", Parent, getLinkageName(), + /*UseQuotes=*/true, /*PrintRef=*/false); + } +} + +void LVElement::printLinkageName(raw_ostream &OS, bool Full, LVElement *Parent, + LVScope *Scope) const { + if (options().getPrintFormatting() && options().getAttributeLinkage()) { + LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope); + std::string Text = (Twine(" 0x") + Twine::utohexstr(SectionIndex) + + Twine(" '") + Twine(getLinkageName()) + Twine("'")) + .str(); + printAttributes(OS, Full, "{Linkage} ", Parent, Text, + /*UseQuotes=*/false, /*PrintRef=*/false); + } +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp @@ -0,0 +1,201 @@ +//===-- LVLine.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVLine class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Line" + +namespace { +const char *const KindBasicBlock = "BasicBlock"; +const char *const KindDiscriminator = "Discriminator"; +const char *const KindEndSequence = "EndSequence"; +const char *const KindEpilogueBegin = "EpilogueBegin"; +const char *const KindLineDebug = "Line"; +const char *const KindLineSource = "Code"; +const char *const KindNewStatement = "NewStatement"; +const char *const KindPrologueEnd = "PrologueEnd"; +const char *const KindUndefined = "Undefined"; +const char *const KindAlwaysStepInto = "AlwaysStepInto"; // CodeView +const char *const KindNeverStepInto = "NeverStepInto"; // CodeView +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Logical line. +//===----------------------------------------------------------------------===// +// Return a string representation for the line kind. +const char *LVLine::kind() const { + static const char *Kind = KindUndefined; + if (getIsLineDebug()) + Kind = KindLineDebug; + else if (getIsLineAssembler()) + Kind = KindLineSource; + return Kind; +} + +LVLineDispatch LVLine::Dispatch = { + {LVLineKind::IsBasicBlock, &LVLine::getIsBasicBlock}, + {LVLineKind::IsDiscriminator, &LVLine::getIsDiscriminator}, + {LVLineKind::IsEndSequence, &LVLine::getIsEndSequence}, + {LVLineKind::IsLineDebug, &LVLine::getIsLineDebug}, + {LVLineKind::IsLineAssembler, &LVLine::getIsLineAssembler}, + {LVLineKind::IsNewStatement, &LVLine::getIsNewStatement}, + {LVLineKind::IsEpilogueBegin, &LVLine::getIsEpilogueBegin}, + {LVLineKind::IsPrologueEnd, &LVLine::getIsPrologueEnd}, + {LVLineKind::IsAlwaysStepInto, &LVLine::getIsAlwaysStepInto}, + {LVLineKind::IsNeverStepInto, &LVLine::getIsNeverStepInto}}; + +// String used as padding for printing elements with no line number. +std::string LVLine::noLineAsString(bool ShowZero) const { + if (options().getInternalNone()) + return LVObject::noLineAsString(ShowZero); + return (ShowZero || options().getAttributeZero()) ? (" 0 ") + : (" - "); +} + +void LVLine::markMissingParents(const LVLines *References, + const LVLines *Targets) { + if (References && Targets) + for (LVLine *Reference : *References) + if (!Reference->findIn(Targets)) + Reference->markBranchAsMissing(); +} + +LVLine *LVLine::findIn(const LVLines *Targets) const { + if (Targets) { + LLVM_DEBUG({ + dbgs() << "\n[LVLine::findIn]\n" + << "Reference: " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Line = " << getLineNumber() << "\n"; + for (const LVLine *Target : *Targets) + dbgs() << "Target : " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Line = " << Target->getLineNumber() << "\n"; + }); + + for (LVLine *Line : *Targets) + if (equals(Line)) + return Line; + } + return nullptr; +} + +bool LVLine::equals(const LVLine *Line) const { + return LVElement::equals(Line); +} + +bool LVLine::equals(const LVLines *References, const LVLines *Targets) { + if (!References && !Targets) + return true; + if (References && Targets && References->size() == Targets->size()) { + for (const LVLine *Reference : *References) + if (!Reference->findIn(Targets)) + return false; + return true; + } + return false; +} + +void LVLine::report(LVComparePass Pass) { + getComparator().printItem(this, Pass); +} + +void LVLine::print(raw_ostream &OS, bool Full) const { + if (getReader().doPrintLine(this)) { + getReaderCompileUnit()->incrementPrintedLines(); + LVElement::print(OS, Full); + printExtra(OS, Full); + } +} + +//===----------------------------------------------------------------------===// +// DWARF line record. +//===----------------------------------------------------------------------===// +std::string LVLineDebug::statesInfo(bool Formatted) const { + // Returns the DWARF extra qualifiers. + std::string String; + raw_string_ostream Stream(String); + + std::string Separator = Formatted ? " " : ""; + if (getIsNewStatement()) { + Stream << Separator << "{" << KindNewStatement << "}"; + Separator = " "; + } + if (getIsDiscriminator()) { + Stream << Separator << "{" << KindDiscriminator << "}"; + Separator = " "; + } + if (getIsBasicBlock()) { + Stream << Separator << "{" << KindBasicBlock << "}"; + Separator = " "; + } + if (getIsEndSequence()) { + Stream << Separator << "{" << KindEndSequence << "}"; + Separator = " "; + } + if (getIsEpilogueBegin()) { + Stream << Separator << "{" << KindEpilogueBegin << "}"; + Separator = " "; + } + if (getIsPrologueEnd()) { + Stream << Separator << "{" << KindPrologueEnd << "}"; + Separator = " "; + } + if (getIsAlwaysStepInto()) { + Stream << Separator << "{" << KindAlwaysStepInto << "}"; + Separator = " "; + } + if (getIsNeverStepInto()) { + Stream << Separator << "{" << KindNeverStepInto << "}"; + Separator = " "; + } + + return String; +} + +bool LVLineDebug::equals(const LVLine *Line) const { + if (!LVLine::equals(Line)) + return false; + return getFilenameIndex() == Line->getFilenameIndex(); +} + +void LVLineDebug::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()); + + if (options().getAttributeQualifier()) { + // The qualifier includes the states information and the source filename + // that contains the line element. + OS << statesInfo(/*Formatted=*/true); + OS << " " << formattedName(getPathname()); + } + OS << "\n"; +} + +//===----------------------------------------------------------------------===// +// Assembler line extracted from the ELF .text section. +//===----------------------------------------------------------------------===// +bool LVLineAssembler::equals(const LVLine *Line) const { + return LVLine::equals(Line); +} + +void LVLineAssembler::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()); + OS << " " << formattedName(getName()); + OS << "\n"; +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp @@ -0,0 +1,674 @@ +//===-- LVLocation.cpp ----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVOperation and LVLocation classes. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Location" + +void LVOperation::print(raw_ostream &OS, bool Full) const {} + +// Identify the most common type of operations and print them using a high +// level format, trying to isolate the DWARF complexity. +std::string LVOperation::getOperandsDWARFInfo() { + std::string String; + raw_string_ostream Stream(String); + + auto PrintRegisterInfo = [&](LVSmall Code) { + //----------------------------------------------------------------------- + // 2.5.1.1 Literal encodings. + //----------------------------------------------------------------------- + if (dwarf::DW_OP_lit0 <= Code && Code <= dwarf::DW_OP_lit31) { + Stream << format("lit%d", Code - dwarf::DW_OP_lit0); + return; + } + + //----------------------------------------------------------------------- + // 2.5.1.2 Register values. + //----------------------------------------------------------------------- + if (dwarf::DW_OP_breg0 <= Code && Code <= dwarf::DW_OP_breg31) { + std::string RegisterName(getReader().getRegisterName(Code, Operands)); + Stream << format("breg%d+%d%s", Code - dwarf::DW_OP_breg0, Operands[0], + RegisterName.c_str()); + return; + } + + //----------------------------------------------------------------------- + // 2.6.1.1.3 Register location descriptions. + //----------------------------------------------------------------------- + if (dwarf::DW_OP_reg0 <= Code && Code <= dwarf::DW_OP_reg31) { + std::string RegisterName(getReader().getRegisterName(Code, Operands)); + Stream << format("reg%d%s", Code - dwarf::DW_OP_reg0, + RegisterName.c_str()); + return; + } + + Stream << format("#0x%02x ", Code) << hexString(Operands[0]) << " " + << hexString(Operands[1]) << "#"; + }; + + switch (Opcode) { + //------------------------------------------------------------------------- + // 2.5.1.1 Literal encodings. + //------------------------------------------------------------------------- + case dwarf::DW_OP_addr: + Stream << "addr " << hexString(Operands[0]); + break; + case dwarf::DW_OP_constu: + case dwarf::DW_OP_const1u: + case dwarf::DW_OP_const2u: + case dwarf::DW_OP_const4u: + case dwarf::DW_OP_const8u: + Stream << "const_u " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_consts: + case dwarf::DW_OP_const1s: + case dwarf::DW_OP_const2s: + case dwarf::DW_OP_const4s: + case dwarf::DW_OP_const8s: + Stream << "const_s " << int(Operands[0]); + break; + case dwarf::DW_OP_addrx: + Stream << "addrx " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_constx: + Stream << "constx " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_const_type: + Stream << "TODO: DW_OP_const_type"; + break; + + //------------------------------------------------------------------------- + // 2.5.1.2 Register values. + //------------------------------------------------------------------------- + case dwarf::DW_OP_fbreg: + Stream << "fbreg " << int(Operands[0]); + break; + case dwarf::DW_OP_bregx: { + std::string RegisterName(getReader().getRegisterName(Opcode, Operands)); + Stream << format("bregx %d%s+%d", Operands[0], RegisterName.c_str(), + unsigned(Operands[1])); + break; + } + case dwarf::DW_OP_regval_type: { + std::string RegisterName(getReader().getRegisterName(Opcode, Operands)); + Stream << format("regval_type %d%s+%d", Operands[0], RegisterName.c_str(), + unsigned(Operands[1])); + break; + } + + //------------------------------------------------------------------------- + // 2.5.1.3 Stack operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_dup: + Stream << "dup"; + break; + case dwarf::DW_OP_drop: + Stream << "drop"; + break; + case dwarf::DW_OP_pick: + Stream << "pick " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_over: + Stream << "over"; + break; + case dwarf::DW_OP_swap: + Stream << "swap"; + break; + case dwarf::DW_OP_rot: + Stream << "rot"; + break; + case dwarf::DW_OP_deref: + Stream << "deref"; + break; + case dwarf::DW_OP_deref_size: + Stream << "deref_size " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_deref_type: + Stream << "deref_type " << unsigned(Operands[0]) << " DIE offset " + << hexString(Operands[1]); + break; + case dwarf::DW_OP_xderef: + Stream << "xderef"; + break; + case dwarf::DW_OP_xderef_size: + Stream << "xderef_size " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_xderef_type: + Stream << "xderef_type " << unsigned(Operands[0]) << " DIE offset " + << hexString(Operands[1]); + break; + case dwarf::DW_OP_push_object_address: + Stream << "push_object_address"; + break; + case dwarf::DW_OP_form_tls_address: + Stream << "form_tls_address " << hexString(Operands[0]); + break; + case dwarf::DW_OP_call_frame_cfa: + Stream << "call_frame_cfa"; + break; + + //------------------------------------------------------------------------- + // 2.5.1.4 Arithmetic and Logical Operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_abs: + Stream << "abs"; + break; + case dwarf::DW_OP_and: + Stream << "and"; + break; + case dwarf::DW_OP_div: + Stream << "div"; + break; + case dwarf::DW_OP_minus: + Stream << "minus"; + break; + case dwarf::DW_OP_mod: + Stream << "mod"; + break; + case dwarf::DW_OP_mul: + Stream << "mul"; + break; + case dwarf::DW_OP_neg: + Stream << "neg"; + break; + case dwarf::DW_OP_not: + Stream << "not"; + break; + case dwarf::DW_OP_or: + Stream << "or"; + break; + case dwarf::DW_OP_plus: + Stream << "plus"; + break; + case dwarf::DW_OP_plus_uconst: + Stream << "plus_uconst " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_shl: + Stream << "shl"; + break; + case dwarf::DW_OP_shr: + Stream << "shr"; + break; + case dwarf::DW_OP_shra: + Stream << "shra"; + break; + case dwarf::DW_OP_xor: + Stream << "xor"; + break; + + //------------------------------------------------------------------------- + // 2.5.1.5 Control Flow Operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_le: + Stream << "le"; + break; + case dwarf::DW_OP_ge: + Stream << "ge"; + break; + case dwarf::DW_OP_eq: + Stream << "eq"; + break; + case dwarf::DW_OP_lt: + Stream << "lt"; + break; + case dwarf::DW_OP_gt: + Stream << "gt"; + break; + case dwarf::DW_OP_ne: + Stream << "ne"; + break; + case dwarf::DW_OP_skip: + Stream << "skip " << signed(Operands[0]); + break; + case dwarf::DW_OP_bra: + Stream << "bra " << signed(Operands[0]); + break; + case dwarf::DW_OP_call2: + Stream << "call2 DIE offset " << hexString(Operands[0]); + break; + case dwarf::DW_OP_call4: + Stream << "call4 DIE offset " << hexString(Operands[0]); + break; + case dwarf::DW_OP_call_ref: + Stream << "call_ref DIE offset " << hexString(Operands[0]); + break; + + //------------------------------------------------------------------------- + // 2.5.1.6 Type Conversions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_convert: + Stream << "convert DIE offset " << hexString(Operands[0]); + break; + case dwarf::DW_OP_reinterpret: + Stream << "reinterpret DIE offset " << hexString(Operands[0]); + break; + + //------------------------------------------------------------------------- + // 2.5.1.7 Special Operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_nop: + Stream << "nop"; + break; + case dwarf::DW_OP_entry_value: + Stream << "TODO: DW_OP_entry_value"; + break; + + //------------------------------------------------------------------------- + // 2.6.1.1.3 Register location descriptions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_regx: + Stream << "regx" << getReader().getRegisterName(Opcode, Operands); + break; + + //------------------------------------------------------------------------- + // 2.6.1.1.4 Implicit location descriptions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_stack_value: + Stream << "stack_value"; + break; + case dwarf::DW_OP_implicit_value: + Stream << "TODO: DW_OP_implicit_value"; + break; + case dwarf::DW_OP_implicit_pointer: + Stream << "implicit_pointer DIE offset " << hexString(Operands[0]) << " " + << int(Operands[1]); + break; + + //------------------------------------------------------------------------- + // 2.6.1.2 Composite location descriptions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_piece: + Stream << "piece " << int(Operands[0]); + break; + case dwarf::DW_OP_bit_piece: + Stream << "bit_piece " << int(Operands[0]) << " offset " + << int(Operands[1]); + break; + + //------------------------------------------------------------------------- + // GNU extensions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_GNU_entry_value: + Stream << "gnu_entry_value "; + PrintRegisterInfo(dwarf::DW_OP_reg0); + break; + case dwarf::DW_OP_GNU_push_tls_address: + Stream << "gnu_push_tls_address " << hexString(Operands[0]); + break; + case dwarf::DW_OP_GNU_addr_index: + Stream << "gnu_addr_index " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_GNU_const_index: + Stream << "gnu_const_index " << unsigned(Operands[0]); + break; + + //------------------------------------------------------------------------- + // Member location. + //------------------------------------------------------------------------- + case LVLocationMemberOffset: + Stream << "offset " << int(Operands[0]); + break; + + //------------------------------------------------------------------------- + // Missing location. + //------------------------------------------------------------------------- + case dwarf::DW_OP_hi_user: + Stream << "missing"; + break; + + //------------------------------------------------------------------------- + // Register values. + //------------------------------------------------------------------------- + default: + PrintRegisterInfo(Opcode); + break; + } + + return String; +} + +// Identify the most common type of operations and print them using a high +// level format, trying to isolate the CodeView complexity. +std::string LVOperation::getOperandsCodeViewInfo() { + std::string String; + raw_string_ostream Stream(String); + + // Get original CodeView operation code. + uint16_t OperationCode = updateOperationCode(Opcode); + + switch (OperationCode) { + // Operands: [Offset, 0]. + case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL: + Stream << "frame_pointer_rel " << int(Operands[0]); + break; + case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + Stream << "frame_pointer_rel_full_scope " << int(Operands[0]); + break; + + // Operands: [Register, 0]. + case codeview::SymbolKind::S_DEFRANGE_REGISTER: + Stream << "register " << getReader().getRegisterName(Opcode, Operands); + break; + case codeview::SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER: + Stream << "subfield_register " + << getReader().getRegisterName(Opcode, Operands); + break; + + // Operands: [Register, Offset]. + case codeview::SymbolKind::S_DEFRANGE_REGISTER_REL: + Stream << "register_rel " << getReader().getRegisterName(Opcode, Operands) + << " offset " << int(Operands[1]); + break; + + // Operands: [Program, 0]. + case codeview::SymbolKind::S_DEFRANGE: + Stream << "frame " << int(Operands[0]); + break; + case codeview::SymbolKind::S_DEFRANGE_SUBFIELD: + Stream << "subfield " << int(Operands[0]); + break; + + default: + Stream << format("#0x%02x: ", Opcode) << hexString(Operands[0]) << " " + << hexString(Operands[1]) << "#"; + break; + } + + return String; +} + +namespace { +const char *const KindBaseClassOffset = "BaseClassOffset"; +const char *const KindBaseClassStep = "BaseClassStep"; +const char *const KindClassOffset = "ClassOffset"; +const char *const KindFixedAddress = "FixedAddress"; +const char *const KindMissingInfo = "Missing"; +const char *const KindOperation = "Operation"; +const char *const KindOperationList = "OperationList"; +const char *const KindRegister = "Register"; +const char *const KindUndefined = "Undefined"; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// DWARF location information. +//===----------------------------------------------------------------------===// +const char *LVLocation::kind() const { + const char *Kind = KindUndefined; + if (getIsBaseClassOffset()) + Kind = KindBaseClassOffset; + else if (getIsBaseClassStep()) + Kind = KindBaseClassStep; + else if (getIsClassOffset()) + Kind = KindClassOffset; + else if (getIsFixedAddress()) + Kind = KindFixedAddress; + else if (getIsGapEntry()) + Kind = KindMissingInfo; + else if (getIsOperation()) + Kind = KindOperation; + else if (getIsOperationList()) + Kind = KindOperationList; + else if (getIsRegister()) + Kind = KindRegister; + return Kind; +} + +std::string LVLocation::getIntervalInfo() const { + static const char *const Question = "?"; + std::string String; + raw_string_ostream Stream(String); + if (getIsAddressRange()) + Stream << "{Range}"; + + auto PrintLine = [&](const LVLine *Line) { + if (Line) { + std::string TheLine; + TheLine = Line->lineNumberAsStringStripped(); + Stream << TheLine.c_str(); + } else { + Stream << Question; + } + }; + + Stream << " Lines "; + PrintLine(getLowerLine()); + Stream << ":"; + PrintLine(getUpperLine()); + + if (options().getAttributeOffset()) + // Print the active range (low pc and high pc). + Stream << " [" << hexString(getLowerAddress()) << ":" + << hexString(getUpperAddress()) << "]"; + + return String; +} + +// Validate the ranges associated with the location. +bool LVLocation::validateRanges() { + // Traverse the locations and validate them against the address to line + // mapping in the current compile unit. Record those invalid ranges. + // A valid range must meet the following conditions: + // a) line(lopc) <= line(hipc) + // b) line(lopc) and line(hipc) are valid. + + if (!hasAssociatedRange()) + return true; + + LVLineRange Range = getReaderCompileUnit()->lineRange(this); + LVLine *LowLine = Range.first; + LVLine *HighLine = Range.second; + if (LowLine) + setLowerLine(LowLine); + else { + setIsInvalidLower(); + return false; + } + if (HighLine) + setUpperLine(HighLine); + else { + setIsInvalidUpper(); + return false; + } + // Check for a valid interval. + if (LowLine->getLineNumber() > HighLine->getLineNumber()) { + setIsInvalidRange(); + return false; + } + + return true; +} + +bool LVLocation::calculateCoverage(LVLocations *Locations, unsigned &Factor, + float &Percentage) { + if (!options().getAttributeCoverage() && !Locations) + return false; + + // Calculate the coverage depending on the kind of location. We have + // the simple and composed locations. + if (Locations->size() == 1) { + // Simple: fixed address, class offset, stack offset. + LVLocation *Location = Locations->front(); + // Some types of locations do not have specific kind. Now is the time + // to set those types, depending on the operation type. + Location->updateKind(); + if (Location->getIsLocationSimple()) { + Factor = 100; + Percentage = 100; + return true; + } + } + + // Composed locations. + LVAddress LowerAddress = 0; + LVAddress UpperAddress = 0; + for (const LVLocation *Location : *Locations) + // Do not include locations representing a gap. + if (!Location->getIsGapEntry()) { + LowerAddress = Location->getLowerAddress(); + UpperAddress = Location->getUpperAddress(); + Factor += (UpperAddress > LowerAddress) ? UpperAddress - LowerAddress + : LowerAddress - UpperAddress; + } + + Percentage = 0; + return false; +} + +void LVLocation::printRaw(raw_ostream &OS, bool Full) const { + // Print the active range (low pc and high pc). + OS << " [" << hexString(getLowerAddress()) << ":" + << hexString(getUpperAddress()) << "]\n"; + // Print any DWARF operations. + printRawExtra(OS, Full); +} + +void LVLocation::printInterval(raw_ostream &OS, bool Full) const { + if (hasAssociatedRange()) + OS << getIntervalInfo(); +} + +void LVLocation::print(raw_ostream &OS, bool Full) const { + if (getReader().doPrintLocation(this)) { + LVObject::print(OS, Full); + printExtra(OS, Full); + } +} + +void LVLocation::printExtra(raw_ostream &OS, bool Full) const { + printInterval(OS, Full); + OS << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF location for a symbol. +//===----------------------------------------------------------------------===// +// Add a Location Entry. +void LVLocationSymbol::addObject(LVAddress LowPC, LVAddress HighPC, + LVUnsigned SectionOffset, + uint64_t LocDescOffset) { + setLowerAddress(LowPC); + setUpperAddress(HighPC); + + // Record the offset where the location information begins. + setOffset(LocDescOffset ? LocDescOffset : SectionOffset); + + // A -1 HighPC value, indicates no range. + if (HighPC == LVAddress(UINT64_MAX)) + setIsDiscardedRange(); + + // Update the location kind, using the DWARF attribute. + setKind(); +} + +// Add a Location Record. +void LVLocationSymbol::addObject(LVSmall Opcode, LVUnsigned Operand1, + LVUnsigned Operand2) { + if (!Entries) + Entries = new LVAutoOperations(); + Entries->emplace_back(new LVOperation(Opcode, Operand1, Operand2)); +} + +// Based on the DWARF attribute, define the location kind. +void LVLocation::setKind() { + switch (getAttr()) { + case dwarf::DW_AT_data_member_location: + setIsClassOffset(); + break; + case dwarf::DW_AT_location: + // Depending on the operand, we have a fixed address. + setIsFixedAddress(); + break; + default: + break; + } + // For those symbols with absolute location information, ignore any + // gaps in their location description; that is the case with absolute + // memory addresses and members located at specific offsets. + if (hasAssociatedRange()) + getParentSymbol()->setFillGaps(); +} + +void LVLocationSymbol::updateKind() { + // Update the location type for simple ones. + if (Entries && Entries->size() == 1) { + LVOperation *Operation = Entries->front(); + if (dwarf::DW_OP_fbreg == Operation->getOpcode()) + setIsStackOffset(); + } +} + +void LVLocationSymbol::printRawExtra(raw_ostream &OS, bool Full) const { + if (Entries) + for (const LVOperation *Operation : *Entries) + Operation->print(OS, Full); +} + +// Print location (formatted version). +void LVLocation::print(LVLocations *Locations, raw_ostream &OS, bool Full) { + assert(Locations && "Locations must not be nullptr"); + // Print the symbol coverage. + if (Locations && !Locations->empty() && options().getAttributeCoverage()) { + assert(!Locations->empty() && "Locations must have at least one element"); + // The location entries are contained within a symbol. Get a location, + // to access basic information about indentation, parent, etc. + LVLocation *Location = Locations->front(); + LVSymbol *Symbol = Location->getParentSymbol(); + float Percentage = Symbol->getCoveragePercentage(); + + // The coverage is dependent on the kind of location. + std::string String; + raw_string_ostream Stream(String); + Stream << format("%.2f%%", Percentage); + if (!Location->getIsLocationSimple()) + Stream << format(" (%d/%d)", Symbol->getCoverageFactor(), + Symbol->getParentScope()->getCoverageFactor()); + Symbol->printAttributes(OS, Full, "{Coverage} ", Symbol, StringRef(String), + /*UseQuotes=*/false, + /*PrintRef=*/false); + } + + // Print the symbol location, including the missing entries. + if (Locations && getReader().doPrintLocation(/*Location=*/nullptr)) + for (const LVLocation *Location : *Locations) + Location->print(OS, Full); +} + +void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const { + OS << "{Location}"; + if (getIsCallSite()) + OS << " -> CallSite"; + printInterval(OS, Full); + OS << "\n"; + + // Print location entries. + if (Full && Entries) { + bool CodeViewLocation = getParentSymbol()->getHasCodeViewLocation(); + std::stringstream Stream; + std::string Leading = ""; + for (LVOperation *Operation : *Entries) { + Stream << Leading + << (CodeViewLocation ? Operation->getOperandsCodeViewInfo() + : Operation->getOperandsDWARFInfo()); + Leading = ", "; + } + printAttributes(OS, Full, "{Entry} ", const_cast(this), + StringRef(Stream.str()), + /*UseQuotes=*/false, + /*PrintRef=*/false); + } +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp @@ -0,0 +1,169 @@ +//===-- LVObject.cpp ------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVObject class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Object" + +#ifndef NDEBUG +uint64_t LVObject::GID = 0; +#endif + +StringRef llvm::logicalview::typeNone() { return StringRef(); } +StringRef llvm::logicalview::typeVoid() { return "void"; } +StringRef llvm::logicalview::typeInt() { return "int"; } +StringRef llvm::logicalview::typeUnknown() { return "?"; } +StringRef llvm::logicalview::emptyString() { return StringRef(); } + +// Get a string representing the indentation level. +std::string LVObject::indentAsString(LVLevel Level) const { + return std::string(Level * 2, ' '); +} + +// Get a string representing the indentation level. +std::string LVObject::indentAsString() const { + return (options().getPrintFormatting() || options().getPrintOffset()) + ? indentAsString(ScopeLevel) + : ""; +} + +// String used as padding for printing objects with no line number. +std::string LVObject::noLineAsString(bool ShowZero) const { + return std::string(8, ' '); +} + +// Get a string representation for the given number and discriminator. +std::string LVObject::lineAsString(uint32_t LineNumber, LVHalf Discriminator, + bool ShowZero) const { + // The representation is formatted as: + // a) line number (xxxxx) and discriminator (yy): 'xxxxx,yy' + // b) Only line number (xxxxx): 'xxxxx ' + // c) No line number: ' ' + std::stringstream Stream; + if (LineNumber) { + if (Discriminator && options().getAttributeDiscriminator()) + Stream << std::setw(5) << LineNumber << "," << std::left << std::setw(2) + << Discriminator; + else + Stream << std::setw(5) << LineNumber << " "; + } else + Stream << noLineAsString(ShowZero); + + if (options().getInternalNone()) + Stream.str(noLineAsString(ShowZero)); + + return Stream.str(); +} + +// Same as 'LineString' but with stripped whitespaces. +std::string LVObject::lineNumberAsStringStripped(bool ShowZero) const { + return std::string(StringRef(lineNumberAsString(ShowZero)).trim()); +} + +std::string LVObject::referenceAsString(uint32_t LineNumber, + bool Spaces) const { + std::string String; + raw_string_ostream Stream(String); + if (LineNumber) + Stream << "@" << LineNumber << (Spaces ? " " : ""); + + return String; +} + +void LVObject::setParent(LVScope *Scope) { + Parent.Scope = Scope; + setLevel(Scope->getLevel() + 1); +} +void LVObject::setParent(LVSymbol *Symbol) { + Parent.Symbol = Symbol; + setLevel(Symbol->getLevel() + 1); +} + +void LVObject::markBranchAsMissing() { + // Mark the current object as 'missing'; then traverse the parents chain + // marking them as 'special missing' to indicate a missing branch. They + // can not be marked as missing, because will generate incorrect reports. + LVObject *Parent = this; + Parent->setIsMissing(); + while (Parent) { + Parent->setIsMissingLink(); + Parent = Parent->getParent(); + } +} + +Error LVObject::doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, + bool Full) const { + print(OS, Full); + return Error::success(); +} + +void LVObject::printAttributes(raw_ostream &OS, bool Full, StringRef Name, + LVObject *Parent, StringRef Value, + bool UseQuotes, bool PrintRef) const { + // The current object will be the enclosing scope, use its offset and level. + LVObject Object(*Parent); + Object.setLevel(Parent->getLevel() + 1); + Object.setLineNumber(0); + Object.printAttributes(OS, Full); + + // Print the line. + std::string TheLineNumber(Object.lineNumberAsString()); + std::string TheIndentation(Object.indentAsString()); + OS << format(" %5s %s ", TheLineNumber.c_str(), TheIndentation.c_str()); + + OS << Name; + if (PrintRef && options().getAttributeOffset()) + OS << hexSquareString(getOffset()); + if (UseQuotes) + OS << formattedName(Value) << "\n"; + else + OS << Value << "\n"; +} + +void LVObject::printAttributes(raw_ostream &OS, bool Full) const { +#ifndef NDEBUG + if (options().getInternalID()) + OS << hexSquareString(getID()); +#endif + if (options().getCompareExecute() && + (options().getAttributeAdded() || options().getAttributeMissing())) + OS << (getIsAdded() ? '+' : getIsMissing() ? '-' : ' '); + if (options().getAttributeOffset()) + OS << hexSquareString(getOffset()); + if (options().getAttributeLevel()) { + std::stringstream Stream; + Stream.str(std::string()); + Stream << "[" << std::setfill('0') << std::setw(3) << getLevel() << "]"; + std::string TheLevel(Stream.str()); + OS << TheLevel; + } + if (options().getAttributeGlobal()) + OS << (getIsGlobalReference() ? 'X' : ' '); +} + +void LVObject::print(raw_ostream &OS, bool Full) const { + printFileIndex(OS, Full); + printAttributes(OS, Full); + + // Print the line and any discriminator. + std::stringstream Stream; + Stream << " " << std::setw(5) << lineNumberAsString() << " " + << indentAsString() << " "; + OS << Stream.str(); +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp @@ -0,0 +1,576 @@ +//===-- LVOptions.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVOptions class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/Support/Errc.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Options" + +//===----------------------------------------------------------------------===// +// Options extracted from the command line. +//===----------------------------------------------------------------------===// +static LVOptions Options; +LVOptions *LVOptions::getOptions() { return &Options; } +void LVOptions::setOptions(LVOptions *CmdOptions) { Options = *CmdOptions; } + +void LVOptions::resolveDependencies() { + // Attributes that are classified as standard options. + auto StandardAttributes = [&]() { + // Set the 'standard' attribute to indicate its associated attributes. + setAttributeStandard(); + + setAttributeBase(); + setAttributeCoverage(); + setAttributeDirectories(); + setAttributeDiscriminator(); + setAttributeFilename(); + setAttributeFiles(); + setAttributeFormat(); + setAttributeLevel(); + setAttributeProducer(); + setAttributePublics(); + setAttributeRange(); + setAttributeReference(); + setAttributeZero(); + }; + + // Attributes that are classified as extended options. + auto ExtendedAttributes = [&]() { + // Set the 'extended' attribute to indicate its associated attributes. + setAttributeExtended(); + + setAttributeArgument(); + setAttributeDiscarded(); + setAttributeEncoded(); + setAttributeGaps(); + setAttributeGenerated(); + setAttributeGlobal(); + setAttributeInserted(); + setAttributeLinkage(); + setAttributeLocal(); + setAttributeLocation(); + setAttributeOffset(); + setAttributePathname(); + setAttributeQualified(); + setAttributeQualifier(); + setAttributeRegister(); + setAttributeSubrange(); + setAttributeSystem(); + setAttributeTypename(); + }; + + // '--Attribute=standard' settings. + if (getAttributeStandard()) + StandardAttributes(); + + // '--Attribute=extended' settings. + if (getAttributeExtended()) + ExtendedAttributes(); + + // '--Attribute=all' settings. + if (getAttributeAll()) { + StandardAttributes(); + ExtendedAttributes(); + } + + // '--attribute=pathname' supersedes '--attribute=filename'. + if (getAttributePathname()) + resetAttributeFilename(); + + // Assume '--output=text' as default + if (!getOutputText() && !getOutputJson()) + setOutputText(); + + // '--output=all' settings. + if (getOutputAll()) { + setOutputJson(); + setOutputSplit(); + setOutputText(); + } + + // A view split folder was specified. + if (getOutputFolder().length()) + setOutputSplit(); + + // Always use the full pathname with splitted output. + if (getOutputSplit()) + setAttributePathname(); + + // '--print=elements' settings. + if (getPrintElements()) { + setPrintInstructions(); + setPrintLines(); + setPrintScopes(); + setPrintSymbols(); + setPrintTypes(); + } + + // '--print=all' settings. + if (getPrintAll()) { + setPrintInstructions(); + setPrintLines(); + setPrintScopes(); + setPrintSizes(); + setPrintSymbols(); + setPrintSummary(); + setPrintTypes(); + setPrintWarnings(); + } + + // '--warning=all' settings. + if (getWarningAll()) { + setWarningCoverages(); + setWarningLines(); + setWarningLocations(); + setWarningRanges(); + } + + // '--internal=all' settings. + if (getInternalAll()) { + setInternalCmdline(); + setInternalID(); + setInternalIntegrity(); + setInternalNone(); + setInternalTag(); + } + + // '--compare=all' settings. + if (getCompareAll()) { + setCompareLines(); + setCompareScopes(); + setCompareSymbols(); + setCompareTypes(); + } + + // Compare the scopes if a request for compare symbols, types, lines. + if (getCompareLines() || getCompareSymbols() || getCompareTypes()) + setCompareScopes(); + + // Generic request for comparison. + if (getCompareScopes()) + setCompareExecute(); + + // Print any logical line (debug or instruction). + if (getPrintInstructions() || getPrintLines()) + setPrintAnyLine(); + + // Print any logical element (line, scope, symbol or type). + if (getPrintAnyLine() || getPrintScopes() || getPrintSymbols() || + getPrintTypes()) + setPrintAnyElement(); + + // Print 'sizes' or 'summary'. + if (getPrintSizes() && getPrintSummary()) + setPrintSizesSummary(); + + // Generic request for printing. + if (getPrintAll() || getPrintAnyElement() || getPrintSizesSummary() || + getPrintWarnings()) + setPrintExecute(); + + // '--reports=all' settings. + if (getReportAll()) { + setReportChildren(); + setReportList(); + setReportParents(); + setReportView(); + } + + // '--report=view' is a shortcut for '--report=parents,children'. + if (getReportView()) { + setReportChildren(); + setReportParents(); + } + + // The report will include: Parents or Children. + if (getReportParents() || getReportChildren() || getReportView()) + setReportAnyView(); + + // The report will include: List or Parents or Children. + if (getReportList() || getReportAnyView()) + setReportExecute(); + + // If a view or element comparison has been requested, the following options + // must be set, in order to get a correct compare: + // 1) Sort the CUs, to get a fast compare. + // 2) Encode template instantiations, so the names include template + // parameter information. + // 3) Include qualified types. + // 4) Include any inserted abstract references. + // 5) For added/missing elements add the '+' or '-' tags. + if (getCompareExecute()) { + resetPrintExecute(); + setComparePrint(); + setSortMode(LVSortMode::Line); + setAttributeAdded(); + setAttributeArgument(); + setAttributeEncoded(); + setAttributeInserted(); + setAttributeMissing(); + setAttributeQualified(); + } + + // Enable formatting for printing (indentation, print children). + setPrintFormatting(); + + // These attributes are dependent on the capture of location information. + if (getAttributeCoverage() || getAttributeGaps() || getAttributeRegister()) + setAttributeLocation(); + + // Location information is only relevant when printing symbols. + if (!getPrintSymbols()) { + resetAttributeCoverage(); + resetAttributeGaps(); + resetAttributeLocation(); + resetAttributeRegister(); + } + + // Quick check for printing any element source information. + if (getAttributeFilename() || getAttributePathname()) + setAttributeAnySource(); + + // Quick check for printing any location information. + if (getAttributeLocation() || getAttributeRange()) + setAttributeAnyLocation(); + + if (getAttributeRange() || getPrintAnyLine()) + setGeneralCollectRanges(); + + calculateIndentationSize(); + + // Print collected command line options. + LLVM_DEBUG({ dump(); }); +} + +void LVOptions::calculateIndentationSize() { +#ifndef NDEBUG + if (getInternalID()) { + std::string String = hexSquareString(0); + IndentationSize += String.length(); + } +#endif + if (getCompareExecute() && (getAttributeAdded() || getAttributeMissing())) + ++IndentationSize; + if (getAttributeOffset()) { + std::string String = hexSquareString(0); + IndentationSize += String.length(); + } + if (getAttributeLevel()) { + std::stringstream Stream; + Stream.str(std::string()); + Stream << "[" << std::setfill('0') << std::setw(3) << 0 << "]"; + IndentationSize += Stream.tellp(); + } + if (getAttributeGlobal()) + ++IndentationSize; +} + +// Print the current values for all the options, after the dependencies +// has been resolved. +void LVOptions::print(raw_ostream &OS) const { + // --attribute + OS << "** Attributes **\n" + << "All: " << getAttributeAll() << ", " + << "Argument: " << getAttributeArgument() << ", " + << "Base: " << getAttributeBase() << ", " + << "Coverage: " << getAttributeCoverage() << "\n" + << "Directories: " << getAttributeDirectories() << ", " + << "Discarded: " << getAttributeDiscarded() << ", " + << "Discriminator: " << getAttributeDiscriminator() << ", " + << "Encoded: " << getAttributeEncoded() << "\n" + << "Extended: " << getAttributeExtended() << ", " + << "Filename: " << getAttributeFilename() << ", " + << "Files: " << getAttributeFiles() << ", " + << "Format: " << getAttributeFormat() << "\n" + << "Gaps: " << getAttributeGaps() << ", " + << "Generated: " << getAttributeGenerated() << ", " + << "Global: " << getAttributeGlobal() << ", " + << "Inserted: " << getAttributeInserted() << "\n" + << "Level: " << getAttributeLevel() << ", " + << "Linkage: " << getAttributeLinkage() << ", " + << "Local: " << getAttributeLocal() << ", " + << "Location: " << getAttributeLocation() << "\n" + << "Offset: " << getAttributeOffset() << ", " + << "Pathname: " << getAttributePathname() << ", " + << "Producer: " << getAttributeProducer() << ", " + << "Publics: " << getAttributePublics() << "\n" + << "Qualified: " << getAttributeQualified() << ", " + << "Qualifier: " << getAttributeQualifier() << ", " + << "Range: " << getAttributeRange() << ", " + << "Reference: " << getAttributeReference() << "\n" + << "Register: " << getAttributeRegister() << ", " + << "Standard: " << getAttributeStandard() << ", " + << "Subrange: " << getAttributeSubrange() << ", " + << "System: " << getAttributeSystem() << "\n" + << "Typename: " << getAttributeTypename() << ", " + << "Underlying: " << getAttributeUnderlying() << ", " + << "Zero: " << getAttributeZero() << "\n"; + OS << "Added: " << getAttributeAdded() << ", " + << "AnyLocation: " << getAttributeAnyLocation() << ", " + << "AnySource: " << getAttributeAnySource() << ", " + << "Missing: " << getAttributeMissing() << "\n" + << "\n"; + + // --compare + OS << "** Compare **\n" + << "All: " << getCompareAll() << ", " + << "Lines: " << getCompareLines() << ", " + << "Scopes: " << getCompareScopes() << ", " + << "Symbols: " << getCompareSymbols() << ", " + << "Types: " << getCompareTypes() << "\n"; + OS << "Context: " << getCompareContext() << ", " + << "Execute: " << getCompareExecute() << ", " + << "Print: " << getComparePrint() << "\n" + << "\n"; + + // --print + OS << "** Print **\n" + << "All: " << getPrintAll() << ", " + << "Elements: " << getPrintElements() << ", " + << "Instructions: " << getPrintInstructions() << ", " + << "Lines: " << getPrintLines() << "\n" + << "Scopes: " << getPrintScopes() << ", " + << "Sizes: " << getPrintSizes() << ", " + << "Summary: " << getPrintSummary() << ", " + << "Symbols: " << getPrintSymbols() << "\n" + << "Types: " << getPrintTypes() << ", " + << "Warnings: " << getPrintWarnings() << "\n"; + OS << "AnyElemeny: " << getPrintAnyElement() << ", " + << "AnyLine: " << getPrintAnyLine() << ", " + << "Execute: " << getPrintExecute() << ", " + << "Formatting: " << getPrintFormatting() << "\n" + << "Offset: " << getPrintOffset() << ", " + << "SizesSummary: " << getPrintSizesSummary() << "\n" + << "\n"; + + // --report + OS << "** Report **\n" + << "All: " << getReportAll() << ", " + << "Children: " << getReportChildren() << ", " + << "List: " << getReportList() << ", " + << "Parents: " << getReportParents() << ", " + << "View: " << getReportView() << "\n"; + OS << "AnyView: " << getReportAnyView() << ", " + << "Execute: " << getReportExecute() << "\n" + << "\n"; + + // --select + OS << "** Select **\n" + << "IgnoreCase: " << getSelectIgnoreCase() << ", " + << "UseRegex: " << getSelectUseRegex() << ", " + << "Execute: " << getSelectExecute() << ", " + << "GenericKind: " << getSelectGenericKind() << "\n" + << "GenericPattern: " << getSelectGenericPattern() << ", " + << "OffsetPattern: " << getSelectOffsetPattern() << "\n" + << "\n"; + + // --warning + OS << "** Warning **\n" + << "All: " << getWarningAll() << ", " + << "Coverage: " << getWarningCoverages() << ", " + << "Lines: " << getWarningLines() << ", " + << "Locations: " << getWarningLocations() << ", " + << "Ranges: " << getWarningRanges() << "\n" + << "\n"; + + // --internal + OS << "** Internal **\n" + << "All: " << Options.getInternalAll() << ", " + << "Cmdline: " << Options.getInternalCmdline() << ", " + << "ID: " << Options.getInternalID() << ", " + << "Integrity: " << Options.getInternalIntegrity() << ", " + << "None: " << Options.getInternalNone() << "\n" + << "Tag: " << Options.getInternalTag() << "\n" + << "\n"; +} + +//===----------------------------------------------------------------------===// +// Logical element selection using patterns. +//===----------------------------------------------------------------------===// +LVPatterns *LVPatterns::getPatterns() { + static LVPatterns Patterns; + return &Patterns; +} + +Error LVPatterns::createMatchEntry(LVMatchInfo &Filters, StringRef Pattern, + bool IgnoreCase, bool UseRegex) { + LVMatch Match; + // Process pattern as regular expression. + if (UseRegex) { + Match.Pattern = std::string(Pattern); + if (Pattern.size()) { + Match.RE = std::make_shared(Pattern, IgnoreCase ? Regex::IgnoreCase + : Regex::NoFlags); + std::string Error; + if (!Match.RE->isValid(Error)) + return createStringError(errc::invalid_argument, + "Error in regular expression: %s", + Error.c_str()); + + Match.Mode = LVMatchMode::Regex; + Filters.push_back(Match); + return Error::success(); + } + } + + // Process pattern as an exact string match, depending on the case. + Match.Pattern = std::string(Pattern); + if (Match.Pattern.size()) { + Match.Mode = IgnoreCase ? LVMatchMode::NoCase : LVMatchMode::Match; + Filters.push_back(Match); + } + + return Error::success(); +} + +void LVPatterns::addGenericPatterns(StringSet<> &Patterns) { + addPatterns(Patterns, GenericMatchInfo); + if (GenericMatchInfo.size()) { + options().setSelectGenericPattern(); + options().setSelectExecute(); + } +} + +void LVPatterns::addOffsetPatterns(const LVOffsetSet &Patterns) { + for (const LVOffset &Entry : Patterns) + OffsetMatchInfo.push_back(Entry); + if (OffsetMatchInfo.size()) { + options().setSelectOffsetPattern(); + options().setSelectExecute(); + } +} + +void LVPatterns::addPatterns(StringSet<> &Patterns, LVMatchInfo &Filters) { + bool IgnoreCase = options().getSelectIgnoreCase(); + bool UseRegex = options().getSelectUseRegex(); + for (const StringSet<>::value_type &Entry : Patterns) { + StringRef Pattern = Entry.first(); + if (Error Err = createMatchEntry(Filters, Pattern, IgnoreCase, UseRegex)) + consumeError(std::move(Err)); + } + + LLVM_DEBUG({ + dbgs() << "\nPattern Information:\n"; + for (LVMatch &Match : Filters) + dbgs() << "Mode: " + << (Match.Mode == LVMatchMode::Match ? "Match" : "Regex") + << " Pattern: '" << Match.Pattern << "'\n"; + }); +} + +void LVPatterns::addElement(LVElement *Element) { + // Mark any element that matches a given pattern. + Element->setIsMatched(); + options().setSelectExecute(); + if (options().getReportList()) + getReaderCompileUnit()->addMatched(Element); + if (options().getReportAnyView()) { + getReaderCompileUnit()->addMatched(Element->getIsScope() + ? static_cast(Element) + : Element->getParentScope()); + // Mark element as matched. + if (!Element->getIsScope()) + Element->setHasPattern(); + } +} + +void LVPatterns::updateReportOptions() { + if (ElementRequest.size() || LineRequest.size() || ScopeRequest.size() || + SymbolRequest.size() || TypeRequest.size()) { + options().setSelectGenericKind(); + options().setSelectExecute(); + } + + // If we have selected requests and there are no specified report options, + // assume the 'details' option. + if (options().getSelectExecute() && !options().getReportExecute()) { + options().setReportExecute(); + options().setReportList(); + } +} + +// Match a general pattern. +bool LVPatterns::matchPattern(StringRef Input, const LVMatchInfo &MatchInfo) { + bool Matched = false; + // Traverse all match specifications. + for (const LVMatch &Match : MatchInfo) { + switch (Match.Mode) { + case LVMatchMode::Match: + Matched = Input.equals(Match.Pattern); + break; + case LVMatchMode::NoCase: + Matched = Input.equals_insensitive(Match.Pattern); + break; + case LVMatchMode::Regex: + Matched = Match.RE->match(Input); + break; + default: + break; + } + // Return if we have a match. + if (Matched) + return true; + } + return Matched; +} + +bool LVPatterns::printElement(const LVLine *Line) const { + return (options().getPrintLines() && Line->getIsLineDebug()) || + (options().getPrintInstructions() && Line->getIsLineAssembler()); +} + +bool LVPatterns::printObject(const LVLocation *Location) const { + if (options().getAttributeAll()) + return true; + bool DoPrint = options().getAttributeAnyLocation(); + // Consider the case of filler locations. + if (DoPrint && Location && Location->getIsGapEntry()) + DoPrint = options().getAttributeGaps(); + return DoPrint; +} + +bool LVPatterns::printElement(const LVScope *Scope) const { + // A scope will be printed depending on the following rules: + // - Request to print scopes. + // - Request to print any of its children. + // - If the scope is Root or CompileUnit: + // Request to print summary, sizes or warnings. + return options().getPrintScopes() || + (options().getPrintSymbols() && Scope->getHasSymbols()) || + (options().getPrintAnyLine() && Scope->getHasLines()) || + (options().getPrintTypes() && Scope->getHasTypes()) || + ((options().getPrintSizesSummary() || options().getPrintWarnings()) && + (Scope->getIsRoot() || Scope->getIsCompileUnit())); +} + +bool LVPatterns::printElement(const LVSymbol *Symbol) const { + // Print compiler generated symbols only if command line option. + if (Symbol->getIsArtificial()) + return options().getAttributeGenerated() && options().getPrintSymbols(); + return options().getPrintSymbols(); +} + +bool LVPatterns::printElement(const LVType *Type) const { + // Print array subranges only if print types is requested. + if (Type->getIsSubrange()) + return options().getAttributeSubrange() && options().getPrintTypes(); + return options().getPrintTypes(); +} + +void LVPatterns::print(raw_ostream &OS) const { + OS << "LVPatterns\n"; + LLVM_DEBUG(dbgs() << "Print Patterns\n"); +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp @@ -0,0 +1,150 @@ +//===-- LVRange.cpp -------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVRange class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVRange.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Range" + +void LVRange::startSearch() { + RangesTree.clear(); + + LLVM_DEBUG({ dbgs() << "\nRanges Tree:\n"; }); + + // Traverse the ranges and store them into the interval tree. + for (LVRangeEntry &RangeEntry : RangeEntries) { + LLVM_DEBUG({ + LVScope *Scope = RangeEntry.scope(); + dbgs() << "Scope: " << format_decimal(Scope->getLevel(), 5) << " " + << "Range: [" << hexValue(RangeEntry.lower()) << ":" + << hexValue(RangeEntry.upper()) << "]\n"; + }); + + RangesTree.insert(RangeEntry.lower(), RangeEntry.upper(), + RangeEntry.scope()); + } + + // Create the interval tree. + RangesTree.create(); + + LLVM_DEBUG({ + dbgs() << "\nRanges Tree:\n"; + RangesTree.print(dbgs()); + }); +} + +// Add the pair in an ascending order, with the smallest ranges at the +// start; in that way, enclosing scopes ranges are at the end of the +// list; we assume that low <= high. +void LVRange::addEntry(LVScope *Scope, LVAddress LowerAddress, + LVAddress UpperAddress) { + // We assume the low <= high. + if (LowerAddress > UpperAddress) + std::swap(LowerAddress, UpperAddress); + + // Record the lowest and highest seen addresses. + if (LowerAddress < Lower) + Lower = LowerAddress; + if (UpperAddress > Upper) + Upper = UpperAddress; + + // Just add the scope and range pair, in no particular order. + RangeEntries.emplace_back(LowerAddress, UpperAddress, Scope); +} + +void LVRange::addEntry(LVScope *Scope) { + assert(Scope && "Scope must not be nullptr"); + // Traverse the ranges and update the ranges set only if the ranges + // values are not already recorded. + if (const LVLocations *Locations = Scope->getRanges()) + for (const LVLocation *Location : *Locations) { + LVAddress LowPC = Location->getLowerAddress(); + LVAddress HighPC = Location->getUpperAddress(); + if (!findEntry(LowPC, HighPC)) + // Add the pair of addresses. + addEntry(Scope, LowPC, HighPC); + } +} + +// Get the scope associated with the input address. +LVScope *LVRange::getEntry(LVAddress Address) const { + LLVM_DEBUG({ dbgs() << format("Searching: 0x%08x\nFound: ", Address); }); + + LVScope *Target = nullptr; + LVLevel TargetLevel = 0; + LVLevel Level = 0; + LVScope *Scope = nullptr; + for (LVRangesTree::find_iterator Iter = RangesTree.find(Address), + End = RangesTree.find_end(); + Iter != End; ++Iter) { + LLVM_DEBUG( + { dbgs() << format("[0x%08x,0x%08x] ", Iter->left(), Iter->right()); }); + Scope = Iter->value(); + Level = Scope->getLevel(); + if (Level > TargetLevel) { + TargetLevel = Level; + Target = Scope; + } + } + + LLVM_DEBUG({ dbgs() << (Scope ? "\n" : "None\n"); }); + + return Target; +} + +// Find the associated Scope for the given ranges values. +LVScope *LVRange::getEntry(LVAddress LowerAddress, + LVAddress UpperAddress) const { + for (const LVRangeEntry &RangeEntry : RangeEntries) + if (LowerAddress >= RangeEntry.lower() && UpperAddress < RangeEntry.upper()) + return RangeEntry.scope(); + return nullptr; +} + +// Find the associated Scope for the given ranges values. +bool LVRange::findEntry(LVAddress LowerAddress, LVAddress UpperAddress) const { + for (const LVRangeEntry &RangeEntry : RangeEntries) + if (LowerAddress == RangeEntry.lower() && + UpperAddress == RangeEntry.upper()) + return true; + return false; +} + +// Sort the range elements for the whole Compile Unit. +void LVRange::sort() { + auto CompareRangeEntry = [](const LVRangeEntry &lhs, + const LVRangeEntry &rhs) -> bool { + return (lhs.lower() < rhs.lower()); + }; + + // Sort the ranges using low address and range size. + std::stable_sort(RangeEntries.begin(), RangeEntries.end(), CompareRangeEntry); +} + +void LVRange::print(raw_ostream &OS, bool Full) const { + size_t Indentation = 0; + for (const LVRangeEntry &RangeEntry : RangeEntries) { + LVScope *Scope = RangeEntry.scope(); + Scope->printAttributes(OS, Full); + Indentation = options().indentationSize(); + if (Indentation) + OS << " "; + OS << format("[0x%08x,0x%08x] ", RangeEntry.lower(), RangeEntry.upper()) + << formattedKind(Scope->kind()) << " " << formattedName(Scope->getName()) + << "\n"; + } + printExtra(OS, Full); +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp @@ -0,0 +1,303 @@ +//===-- LVReader.cpp ------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVReader class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Reader" + +// Detect elements that are inserted more than once at different scopes, +// causing a crash on the reader destruction, as the element is already +// deleted from other scope. Helper for CodeView reader. +void checkIntegrityScopesTree(LVScope *Root) { + using LVDuplicateEntry = std::tuple; + using LVDuplicate = std::vector; + LVDuplicate Duplicate; + + using LVIntegrity = std::map; + LVIntegrity Integrity; + + // Add the given element to the integrity map. + auto AddElement = [&](LVElement *Element, LVScope *Scope) { + LVIntegrity::iterator Iter = Integrity.find(Element); + if (Iter == Integrity.end()) + Integrity.emplace(Element, Scope); + else + // We found a duplicated. + Duplicate.emplace_back(Element, Scope, Iter->second); + }; + + // Recursively add all the elements in the scope. + std::function TraverseScope = [&](LVScope *Parent) { + auto Traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) + AddElement(Entry, Parent); + }; + if (const LVScopes *Scopes = Parent->getScopes()) + for (LVScope *Scope : *Scopes) { + AddElement(Scope, Parent); + TraverseScope(Scope); + } + Traverse(Parent->getSymbols()); + Traverse(Parent->getTypes()); + Traverse(Parent->getLines()); + }; + + // Start traversing the scopes root and print any duplicates. + TraverseScope(Root); + if (Duplicate.size()) { + std::stable_sort(begin(Duplicate), end(Duplicate), + [](const auto &l, const auto &r) { + return std::get<0>(l)->getID() < std::get<0>(r)->getID(); + }); + + auto PrintIndex = [](unsigned Index) { + if (Index) + dbgs() << format("%8d: ", Index); + else + dbgs() << format("%8c: ", ' '); + }; + auto PrintElement = [&](LVElement *Element, unsigned Index = 0) { + PrintIndex(Index); + std::string ElementName(Element->getName()); + dbgs() << format("%15s ID=0x%08x '%s'\n", Element->kind(), + Element->getID(), ElementName.c_str()); + }; + + std::string RootName(Root->getName()); + dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); + dbgs() << format("Root: '%s'\nDuplicated elements: %d\n", RootName.c_str(), + Duplicate.size()); + dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); + + unsigned Index = 0; + for (const LVDuplicateEntry &Entry : Duplicate) { + LVElement *Element; + LVScope *First; + LVScope *Second; + std::tie(Element, First, Second) = Entry; + dbgs() << formatv("\n{0}\n", fmt_repeat('-', 72)); + PrintElement(Element, ++Index); + PrintElement(First); + PrintElement(Second); + dbgs() << formatv("{0}\n", fmt_repeat('-', 72)); + } + exit(0); + } +} + +//===----------------------------------------------------------------------===// +// Class to represent a split context. +//===----------------------------------------------------------------------===// +Error LVSplitContext::createSplitFolder(StringRef Where) { + // The 'location' will represent the root directory for the output created + // by the context. It will contain the different CUs files, that will be + // extracted from a single ELF. + Location = std::string(Where); + + // Add a trailing slash, if there is none. + size_t Pos = Location.find_last_of('/'); + if (Location.length() != Pos + 1) + Location.append("/"); + + // Make sure the new directory exists, creating it if necessary. + if (std::error_code EC = llvm::sys::fs::create_directories(Location)) + return createStringError(EC, "Error: could not create directory %s", + Location.c_str()); + + return Error::success(); +} + +std::error_code LVSplitContext::open(std::string ContextName, + std::string Extension, raw_ostream &OS) { + assert(OutputFile == nullptr && "OutputFile already set."); + + // Transforms '/', '\', '.', ':' into '_'. + std::string Name(flattenedFilePath(ContextName)); + Name.append(Extension); + // Add the split context location folder name. + if (!Location.empty()) + Name.insert(0, Location); + + std::error_code EC; + OutputFile = std::make_unique(Name, EC, sys::fs::OF_None); + if (EC) + return EC; + + // Don't remove output file. + OutputFile->keep(); + return std::error_code(); +} + +LVReader *CurrentReader = nullptr; +LVReader &LVReader::getInstance() { + if (CurrentReader) + return *CurrentReader; + outs() << "Invalid instance reader.\n"; + llvm_unreachable("Invalid instance reader."); +} +void LVReader::setInstance(LVReader *Reader) { CurrentReader = Reader; } + +Error LVReader::createSplitFolder() { + if (OutputSplit) { + // If the '--output=split' was specified, but no '--split-folder' + // option, use the input file as base for the split location. + if (options().getOutputFolder().empty()) + options().setOutputFolder(getFilename().str() + "_cus"); + + SmallString<128> SplitFolder; + SplitFolder = options().getOutputFolder(); + sys::fs::make_absolute(SplitFolder); + + // Return error if unable to create a split context location. + if (Error Err = SplitContext.createSplitFolder(SplitFolder)) + return Err; + + OS << "\nSplit View Location: '" << SplitContext.getLocation() << "'\n"; + } + + return Error::success(); +} + +// Get the filename for given object. +StringRef LVReader::getFilename(LVObject *Object, size_t Index) const { + // The current CodeView Reader implementation does not have support for + // multiple compile units. Until a proper offset calculation, check only + // in the current compile unit. + if (CompileUnits.size()) { + // Get Compile Unit for the given object. + LVCompileUnits::const_iterator Iter = + std::prev(CompileUnits.lower_bound(Object->getOffset())); + if (Iter != CompileUnits.end()) + return Iter->second->getFilename(Index); + } + + return CompileUnit ? CompileUnit->getFilename(Index) : StringRef(); +} + +// The Reader is the module that creates the logical view using the debug +// information contained in the binary file specified in the command line. +// This is the main entry point for the Reader and performs the following +// steps: +// - Process any patterns collected from the '--select' options. +// - For each compile unit in the debug information: +// * Create the logical elements (scopes, symbols, types, lines). +// * Collect debug ranges and debug locations. +// * Move the collected logical lines to their associated scopes. +// - Once all the compile units have been processed, traverse the scopes +// tree in order to: +// * Calculate symbol coverage. +// * Detect invalid ranges and locations. +// * "resolve" the logical elements. During this pass, the names and +// file information are updated, to reflect any dependency with other +// logical elements. +Error LVReader::doLoad() { + // Set current Reader instance. + setInstance(this); + + // Before any scopes creation, process any pattern specified by the + // --select and --select-offsets options. + patterns().addGenericPatterns(options().Select.Generic); + patterns().addOffsetPatterns(options().Select.Offsets); + + // Add any specific element printing requests based on the element kind. + patterns().addRequest(options().Select.Elements); + patterns().addRequest(options().Select.Lines); + patterns().addRequest(options().Select.Scopes); + patterns().addRequest(options().Select.Symbols); + patterns().addRequest(options().Select.Types); + + // Once we have processed the requests for any particular kind of elements, + // we need to update the report options, in order to have a default value. + patterns().updateReportOptions(); + + // Delegate the scope tree creation to the specific reader. + if (Error Err = createScopes()) + return Err; + + if (options().getInternalIntegrity()) + checkIntegrityScopesTree(Root); + + // Calculate symbol coverage and detect invalid debug locations and ranges. + Root->processRangeInformation(); + + // As the elements can depend on elements from a different compile unit, + // information such as name and file/line source information needs to be + // updated. + Root->resolveElements(); + + sortScopes(); + return Error::success(); +} + +// Default handler for a generic reader. +Error LVReader::doPrint() { + // Set current Reader instance. + setInstance(this); + + // Check for any '--report' request. + if (options().getReportExecute()) { + // Requested details. + if (options().getReportList()) + if (Error Err = printMatchedElements(/*UseMatchedElements=*/true)) + return Err; + // Requested only children. + if (options().getReportChildren() && !options().getReportParents()) + if (Error Err = printMatchedElements(/*UseMatchedElements=*/false)) + return Err; + // Requested (parents) or (parents and children). + if (options().getReportParents() || options().getReportView()) + if (Error Err = printScopes()) + return Err; + + return Error::success(); + } + + return printScopes(); +} + +Error LVReader::printScopes() { + if (bool DoPrint = + (options().getPrintExecute() || options().getComparePrint())) { + if (Error Err = createSplitFolder()) + return Err; + + // Start printing from the root. + bool DoMatch = options().getSelectGenericPattern() || + options().getSelectGenericKind() || + options().getSelectOffsetPattern(); + return Root->doPrint(OutputSplit, DoMatch, DoPrint, OS); + } + + return Error::success(); +} + +Error LVReader::printMatchedElements(bool UseMatchedElements) { + if (Error Err = createSplitFolder()) + return Err; + + return Root->doPrintMatches(OutputSplit, OS, UseMatchedElements); +} + +void LVReader::print(raw_ostream &OS) const { + OS << "LVReader\n"; + LLVM_DEBUG(dbgs() << "PrintReader\n"); +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp @@ -0,0 +1,2121 @@ +//===-- LVScope.cpp -------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVScope class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVRange.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Scope" + +namespace { +const char *const KindArray = "Array"; +const char *const KindBlock = "Block"; +const char *const KindCallSite = "CallSite"; +const char *const KindClass = "Class"; +const char *const KindCompileUnit = "CompileUnit"; +const char *const KindEnumeration = "Enumeration"; +const char *const KindFile = "File"; +const char *const KindFunction = "Function"; +const char *const KindInlinedFunction = "InlinedFunction"; +const char *const KindNamespace = "Namespace"; +const char *const KindStruct = "Struct"; +const char *const KindTemplateAlias = "TemplateAlias"; +const char *const KindTemplatePack = "TemplatePack"; +const char *const KindUndefined = "Undefined"; +const char *const KindUnion = "Union"; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// DWARF lexical block, such as: namespace, function, compile unit, module, etc. +//===----------------------------------------------------------------------===// +LVScope::~LVScope() { + delete Types; + delete Symbols; + delete Scopes; + delete Lines; + delete Ranges; + delete Children; +} + +// Return a string representation for the scope kind. +const char *LVScope::kind() const { + static const char *Kind = KindUndefined; + if (getIsArray()) + Kind = KindArray; + else if (getIsBlock()) + Kind = KindBlock; + else if (getIsCallSite()) + Kind = KindCallSite; + else if (getIsCompileUnit()) + Kind = KindCompileUnit; + else if (getIsEnumeration()) + Kind = KindEnumeration; + else if (getIsInlinedFunction()) + Kind = KindInlinedFunction; + else if (getIsNamespace()) + Kind = KindNamespace; + else if (getIsTemplatePack()) + Kind = KindTemplatePack; + else if (getIsRoot()) + Kind = KindFile; + else if (getIsTemplateAlias()) + Kind = KindTemplateAlias; + else if (getIsClass()) + Kind = KindClass; + else if (getIsFunction()) + Kind = KindFunction; + else if (getIsStructure()) + Kind = KindStruct; + else if (getIsUnion()) + Kind = KindUnion; + return Kind; +} + +LVScopeDispatch LVScope::Dispatch = { + {LVScopeKind::IsAggregate, &LVScope::getIsAggregate}, + {LVScopeKind::IsArray, &LVScope::getIsArray}, + {LVScopeKind::IsBlock, &LVScope::getIsBlock}, + {LVScopeKind::IsCallSite, &LVScope::getIsCallSite}, + {LVScopeKind::IsCatchBlock, &LVScope::getIsCatchBlock}, + {LVScopeKind::IsClass, &LVScope::getIsClass}, + {LVScopeKind::IsCompileUnit, &LVScope::getIsCompileUnit}, + {LVScopeKind::IsEntryPoint, &LVScope::getIsEntryPoint}, + {LVScopeKind::IsEnumeration, &LVScope::getIsEnumeration}, + {LVScopeKind::IsFunction, &LVScope::getIsFunction}, + {LVScopeKind::IsFunctionType, &LVScope::getIsFunctionType}, + {LVScopeKind::IsInlinedFunction, &LVScope::getIsInlinedFunction}, + {LVScopeKind::IsLabel, &LVScope::getIsLabel}, + {LVScopeKind::IsLexicalBlock, &LVScope::getIsLexicalBlock}, + {LVScopeKind::IsNamespace, &LVScope::getIsNamespace}, + {LVScopeKind::IsRoot, &LVScope::getIsRoot}, + {LVScopeKind::IsStructure, &LVScope::getIsStructure}, + {LVScopeKind::IsTemplate, &LVScope::getIsTemplate}, + {LVScopeKind::IsTemplateAlias, &LVScope::getIsTemplateAlias}, + {LVScopeKind::IsTemplatePack, &LVScope::getIsTemplatePack}, + {LVScopeKind::IsTryBlock, &LVScope::getIsTryBlock}, + {LVScopeKind::IsUnion, &LVScope::getIsUnion}}; + +void LVScope::addToChildren(LVElement *Element) { + if (!Children) + Children = new LVElements(); + Children->push_back(Element); +} + +void LVScope::addElement(LVElement *Element) { + assert(Element && "Invalid element."); + if (Element->getIsType()) + addElement(static_cast(Element)); + else if (Element->getIsScope()) + addElement(static_cast(Element)); + else if (Element->getIsSymbol()) + addElement(static_cast(Element)); + else if (Element->getIsLine()) + addElement(static_cast(Element)); + else + llvm_unreachable("Invalid Element."); +} + +// Adds the line info item to the ones stored in the scope. +void LVScope::addElement(LVLine *Line) { + assert(Line && "Invalid line."); + assert(!Line->getParent() && "Line already inserted"); + if (!Lines) + Lines = new LVAutoLines(); + + // Add it to parent. + Lines->push_back(Line); + Line->setParent(this); + + // Notify the reader about the new element being added. + getReaderCompileUnit()->addedElement(Line); + + // All logical elements added to the children, are sorted by any of the + // following criterias: offset, name, line number, kind. + // Do not add the line records to the children, as they represent the + // logical view for the text section and any sorting will not preserve + // the original sequence. + + // Indicate that this tree branch has lines. + traverseParents(&LVScope::getHasLines, &LVScope::setHasLines); +} + +// Add a location. +void LVScope::addObject(LVLocation *Location) { + assert(Location && "Invalid location."); + assert(!Location->getParent() && "Location already inserted"); + if (!Ranges) + Ranges = new LVAutoLocations(); + + // Add it to parent. + Location->setParent(this); + Location->setOffset(getOffset()); + + Ranges->push_back(Location); + setHasRanges(); +} + +// Adds the scope to the child scopes and sets the parent in the child. +void LVScope::addElement(LVScope *Scope) { + assert(Scope && "Invalid scope."); + assert(!Scope->getParent() && "Scope already inserted"); + if (!Scopes) + Scopes = new LVAutoScopes(); + + // Add it to parent. + Scopes->push_back(Scope); + addToChildren(Scope); + Scope->setParent(this); + + // Notify the reader about the new element being added. + getReaderCompileUnit()->addedElement(Scope); + + // If the element is a global reference, mark its parent as having global + // references; that information is used, to print only those branches + // with global references. + if (Scope->getIsGlobalReference()) + traverseParents(&LVScope::getHasGlobals, &LVScope::setHasGlobals); + else + traverseParents(&LVScope::getHasLocals, &LVScope::setHasLocals); + + // Indicate that this tree branch has scopes. + traverseParents(&LVScope::getHasScopes, &LVScope::setHasScopes); +} + +// Adds a symbol to the ones stored in the scope. +void LVScope::addElement(LVSymbol *Symbol) { + assert(Symbol && "Invalid symbol."); + assert(!Symbol->getParent() && "Symbol already inserted"); + if (!Symbols) + Symbols = new LVAutoSymbols(); + + // Add it to parent. + Symbols->push_back(Symbol); + addToChildren(Symbol); + Symbol->setParent(this); + + // Notify the reader about the new element being added. + getReaderCompileUnit()->addedElement(Symbol); + + // If the element is a global reference, mark its parent as having global + // references; that information is used, to print only those branches + // with global references. + if (Symbol->getIsGlobalReference()) + traverseParents(&LVScope::getHasGlobals, &LVScope::setHasGlobals); + else + traverseParents(&LVScope::getHasLocals, &LVScope::setHasLocals); + + // Indicate that this tree branch has symbols. + traverseParents(&LVScope::getHasSymbols, &LVScope::setHasSymbols); +} + +// Adds a type to the ones stored in the scope. +void LVScope::addElement(LVType *Type) { + assert(Type && "Invalid type."); + assert(!Type->getParent() && "Type already inserted"); + if (!Types) + Types = new LVAutoTypes(); + + // Add it to parent. + Types->push_back(Type); + addToChildren(Type); + Type->setParent(this); + + // Notify the reader about the new element being added. + getReaderCompileUnit()->addedElement(Type); + + // If the element is a global reference, mark its parent as having global + // references; that information is used, to print only those branches + // with global references. + if (Type->getIsGlobalReference()) + traverseParents(&LVScope::getHasGlobals, &LVScope::setHasGlobals); + else + traverseParents(&LVScope::getHasLocals, &LVScope::setHasLocals); + + // Indicate that this tree branch has types. + traverseParents(&LVScope::getHasTypes, &LVScope::setHasTypes); +} + +// Add a pair of ranges. +void LVScope::addObject(LVAddress LowerAddress, LVAddress UpperAddress) { + // Pack the ranges into a Location object. + LVLocation *Location = new LVLocation(); + Location->setLowerAddress(LowerAddress); + Location->setUpperAddress(UpperAddress); + Location->setIsAddressRange(); + + addObject(Location); +} + +bool LVScope::removeElement(LVElement *Element) { + auto Predicate = [Element](LVElement *Item) -> bool { + return Item == Element; + }; + auto RemoveElement = [Element, Predicate](auto &Container) -> bool { + auto Iter = std::remove_if(Container->begin(), Container->end(), Predicate); + if (Iter != Container->end()) { + Container->erase(Iter, Container->end()); + Element->resetParent(); + return true; + } + return false; + }; + + // As 'children' contains only (scopes, symbols and types), check if the + // element we are deleting is a line. + if (Element->getIsLine()) + return RemoveElement(Lines); + + if (RemoveElement(Children)) { + if (Element->getIsSymbol()) + return RemoveElement(Symbols); + if (Element->getIsType()) + return RemoveElement(Types); + if (Element->getIsScope()) + return RemoveElement(Scopes); + llvm_unreachable("Invalid element."); + } + + return false; +} + +void LVScope::addMissingElements(LVScope *Reference) { + setAddedMissing(); + if (!Reference) + return; + + // Get abstract symbols for the given scope reference. + const LVSymbols *ReferenceSymbols = Reference->getSymbols(); + if (!ReferenceSymbols) + return; + + LVSymbols References; + References.append(ReferenceSymbols->begin(), ReferenceSymbols->end()); + + auto RemoveSymbol = [&](LVSymbols &Symbols, LVSymbol *Symbol) { + LVSymbols::iterator Iter = std::remove_if( + Symbols.begin(), Symbols.end(), + [Symbol](LVSymbol *Item) -> bool { return Item == Symbol; }); + if (Iter != Symbols.end()) + Symbols.erase(Iter, Symbols.end()); + }; + + // Erase abstract symbols already in this scope from the collection of + // symbols in the referenced scope. + if (getSymbols()) + for (const LVSymbol *Symbol : *getSymbols()) + if (Symbol->getHasReferenceAbstract()) + RemoveSymbol(References, Symbol->getReference()); + + // If we have elements left in 'References', those are the elements that + // need to be inserted in the current scope. + if (References.size()) { + LLVM_DEBUG({ + dbgs() << "Insert Missing Inlined Elements\n" + << "Offset = " << hexSquareString(getOffset()) << " " + << "Abstract = " << hexSquareString(Reference->getOffset()) + << "\n"; + }); + for (LVSymbol *Reference : References) { + LLVM_DEBUG({ + dbgs() << "Missing Offset = " << hexSquareString(Reference->getOffset()) + << "\n"; + }); + // We can't clone the abstract origin reference, as it contain extra + // information that is incorrect for the element to be inserted. + // As the symbol being added does not exist in the debug section, + // use its parent scope offset, to indicate its DIE location. + LVSymbol *Symbol = new LVSymbol(); + addElement(Symbol); + Symbol->setOffset(getOffset()); + Symbol->setIsOptimized(); + Symbol->setReference(Reference); + + // The symbol can be a constant, parameter or variable. + if (Reference->getIsConstant()) + Symbol->setIsConstant(); + else if (Reference->getIsParameter()) + Symbol->setIsParameter(); + else if (Reference->getIsVariable()) + Symbol->setIsVariable(); + else + llvm_unreachable("Invalid symbol kind."); + } + } +} + +void LVScope::updateLevel(LVScope *Parent, bool Moved) { + // Update the level for the element itself and all its children, using the + // given scope parent as reference. + setLevel(Parent->getLevel() + 1); + + // Update the children. + if (Children) + for (LVElement *Element : *Children) + Element->updateLevel(this, Moved); + + // Update any lines. + if (Lines) + for (LVLine *Line : *Lines) + Line->updateLevel(this, Moved); +} + +void LVScope::resolve() { + if (getIsResolved()) + return; + + // Resolve the element itself. + LVElement::resolve(); + + // Resolve the children. + if (Children) + for (LVElement *Element : *Children) { + if (getIsGlobalReference()) + // If the scope is a global reference, mark all its children as well. + Element->setIsGlobalReference(); + Element->resolve(); + } +} + +void LVScope::resolveName() { + if (getIsResolvedName()) + return; + setIsResolvedName(); + + // If the scope is a template, resolve the template parameters and get + // the name for the template with the encoded arguments. + if (getIsTemplate()) + resolveTemplate(); + else { + if (LVElement *BaseType = getType()) { + BaseType->resolveName(); + resolveFullname(BaseType); + } + } + + // In the case of unnamed scopes, try to generate a name for it, using + // the parents name and the line information. In the case of compiler + // generated functions, use its linkage name if is available. + if (!isNamed()) { + if (getIsArtificial()) + setName(getLinkageName()); + else + generateName(); + } + + LVElement::resolveName(); + + // Resolve any given pattern. + patterns().resolvePatternMatch(this); +} + +void LVScope::resolveReferences() { + // The scopes can have the following references to other elements: + // A type: + // DW_AT_type -> Type or Scope + // DW_AT_import -> Type + // A Reference: + // DW_AT_specification -> Scope + // DW_AT_abstract_origin -> Scope + // DW_AT_extension -> Scope + + // Resolve any referenced scope. + LVScope *Reference = getReference(); + if (Reference) { + Reference->resolve(); + // Recursively resolve the scope names. + resolveReferencesChain(); + } + + // Set the file/line information using the Debug Information entry. + setFile(Reference); + + // Resolve any referenced type or scope. + if (LVElement *Element = getType()) + Element->resolve(); +} + +void LVScope::resolveElements() { + // The current element represents the Root. Traverse each Compile Unit. + if (!Scopes) + return; + + for (LVScope *Scope : *Scopes) { + LVScopeCompileUnit *CompileUnit = static_cast(Scope); + getReader().setCompileUnit(CompileUnit); + CompileUnit->resolve(); + // Propagate any matching information into the scopes tree. + CompileUnit->propagatePatternMatch(); + } +} + +StringRef LVScope::resolveReferencesChain() { + // If the scope have a DW_AT_specification or DW_AT_abstract_origin, + // follow the chain to resolve the name from those references. + if (getHasReference() && !isNamed()) + setName(getReference()->resolveReferencesChain()); + + return getName(); +} + +// Get template parameter types. +bool LVScope::getTemplateParameterTypes(LVTypes &Params) { + // Traverse the scope types and populate the given container with those + // types that are template parameters; that container will be used by + // 'encodeTemplateArguments' to resolve them. + if (const LVTypes *Types = getTypes()) + for (LVType *Type : *Types) + if (Type->getIsTemplateParam()) { + Type->resolve(); + Params.push_back(Type); + } + + return !Params.empty(); +} + +// Resolve the template parameters/arguments relationship. +void LVScope::resolveTemplate() { + if (getIsTemplateResolved()) + return; + setIsTemplateResolved(); + + // Check if we need to encode the template arguments. + if (options().getAttributeEncoded()) { + LVTypes Params; + if (getTemplateParameterTypes(Params)) { + std::string EncodedArgs; + // Encode the arguments as part of the template name and update the + // template name, to reflect the encoded parameters. + encodeTemplateArguments(EncodedArgs, &Params); + setEncodedArgs(EncodedArgs.c_str()); + } + } +} + +// Get the qualified name for the template. +void LVScope::getQualifiedName(std::string &QualifiedName) const { + if (getIsRoot() || getIsCompileUnit()) + return; + + if (LVScope *Parent = getParentScope()) + Parent->getQualifiedName(QualifiedName); + if (!QualifiedName.empty()) + QualifiedName.append("::"); + QualifiedName.append(std::string(getName())); +} + +// Encode the template arguments as part of the template name. +void LVScope::encodeTemplateArguments(std::string &Name) const { + // Qualify only when we are expanding parameters that are template + // instances; the debugger will assume the current scope symbol as + // the qualifying tag for the symbol being generated, which gives: + // namespace std { + // ... + // set,std::allocator> + // ... + // } + // The 'set' symbol is assumed to have the qualified tag 'std'. + + // We are resolving a template parameter which is another template. If + // it is already resolved, just get the qualified name and return. + std::string BaseName; + getQualifiedName(BaseName); + if (getIsTemplateResolved()) + Name.append(BaseName); +} + +void LVScope::encodeTemplateArguments(std::string &Name, + const LVTypes *Types) const { + // The encoded string will start with the scope name. + Name.append("<"); + + // The list of types are the template parameters. + if (Types) { + bool AddComma = false; + for (const LVType *Type : *Types) { + if (AddComma) + Name.append(", "); + Type->encodeTemplateArgument(Name); + AddComma = true; + } + } + + Name.append(">"); +} + +bool LVScope::resolvePrinting() const { + // The warnings collected during the scope creation as per compile unit. + // If there is a request for printing warnings, always print its associate + // Compile Unit. + if (options().getPrintWarnings() && (getIsRoot() || getIsCompileUnit())) + return true; + + // In selection mode, always print the root scope regardless of the + // number of matched elements. If no matches, the root by itself will + // indicate no matches. + if (options().getSelectExecute()) { + return getIsRoot() || getIsCompileUnit() || getHasPattern(); + } + + bool Globals = options().getAttributeGlobal(); + bool Locals = options().getAttributeLocal(); + if ((Globals && Locals) || (!Globals && !Locals)) { + // Print both Global and Local. + } else { + // Check for Global or Local Objects. + if ((Globals && !(getHasGlobals() || getIsGlobalReference())) || + (Locals && !(getHasLocals() || !getIsGlobalReference()))) + return false; + } + + // For the case of functions, skip it if is compiler generated. + if (getIsFunction() && getIsArtificial() && + !options().getAttributeGenerated()) + return false; + + return true; +} + +Error LVScope::doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, + bool Full) const { + // During a view output splitting, use the output stream created by the + // split context, then switch to the reader output stream. + raw_ostream *StreamSplit = &OS; + + // Ignore the CU generated by the VS toolchain, when compiling to PDB. + if (getIsSystem() && !options().getAttributeSystem()) + return Error::success(); + + // If 'Split', we use the scope name (CU name) as the ouput file; the + // delimiters in the pathname, must be replaced by a normal character. + if (getIsCompileUnit()) { + getReader().setCompileUnit(const_cast(this)); + if (Split) { + std::string ScopeName(getName()); + if (std::error_code EC = + getReaderSplitContext().open(ScopeName, ".txt", OS)) + return createStringError(EC, "Unable to create split output file %s", + ScopeName.c_str()); + StreamSplit = static_cast(&getReaderSplitContext().os()); + } + } + + // Ignore discarded or stripped scopes (functions). + bool DoPrint = (options().getAttributeDiscarded()) ? true : !getIsDiscarded(); + + // If we are in compare mode, the only conditions are related to the + // element being missing. In the case of elements comparison, we print the + // augmented view, that includes added elements. + // In print mode, we check other conditions, such as local, global, etc. + if (DoPrint) { + DoPrint = + getIsInCompare() ? options().getReportExecute() : resolvePrinting(); + } + + // At this point we have checked for very specific options, to decide if the + // element will be printed. Include the caller's test for element general + // print. + DoPrint = DoPrint && (Print || options().getOutputSplit()); + + if (DoPrint) { + // Print the element itself. + print(*StreamSplit, Full); + + // Check if we have reached the requested lexical level specified in the + // command line options. Input file is level zero and the CU is level 1. + if ((getIsRoot() || options().getPrintAnyElement()) && + options().getPrintFormatting() && + getLevel() < options().getOutputLevel()) { + // Print the children. + if (Children) + for (const LVElement *Element : *Children) { + if (Match && !Element->getHasPattern()) + continue; + if (Error Err = + Element->doPrint(Split, Match, Print, *StreamSplit, Full)) + return Err; + } + + // Print the line records. + if (Lines) + for (const LVLine *Line : *Lines) { + if (Match && !Line->getHasPattern()) + continue; + if (Error Err = + Line->doPrint(Split, Match, Print, *StreamSplit, Full)) + return Err; + } + + // Print the warnings. + if (options().getPrintWarnings()) + printWarnings(*StreamSplit, Full); + } + } + + // Done printing the compile unit. Print any requested summary and + // restore the original output context. + if (getIsCompileUnit()) { + if (options().getPrintSummary()) + printSummary(*StreamSplit); + if (options().getPrintSizes()) + printSizes(*StreamSplit); + if (Split) { + getReaderSplitContext().close(); + StreamSplit = &getReader().outputStream(); + } + } + + if (getIsRoot() && options().getPrintWarnings()) { + getReader().printRecords(*StreamSplit); + } + + return Error::success(); +} + +void LVScope::sort() { + // Preserve the lines order as they are associated with user code. + LVSortFunction SortFunction = getSortFunction(); + if (SortFunction) { + std::function Sort = + [&](LVScope *Parent, LVSortFunction SortFunction) { + auto Traverse = [&](auto *Set, LVSortFunction SortFunction) { + if (Set) + std::stable_sort(Set->begin(), Set->end(), SortFunction); + }; + Traverse(Parent->Types, SortFunction); + Traverse(Parent->Symbols, SortFunction); + Traverse(Parent->Scopes, SortFunction); + Traverse(Parent->Ranges, compareRange); + Traverse(Parent->Children, SortFunction); + + if (Parent->Scopes) + for (LVScope *Scope : *Parent->Scopes) + Sort(Scope, SortFunction); + }; + + // Start traversing the scopes root and transform the element name. + Sort(this, SortFunction); + } +} + +void LVScope::traverseParents(LVScopeGetFunction GetFunction, + LVScopeSetFunction SetFunction) { + // Traverse the parent tree. + LVScope *Parent = this; + while (Parent) { + // Terminates if the 'SetFunction' have been already executed. + if ((Parent->*GetFunction)()) + break; + (Parent->*SetFunction)(); + Parent = Parent->getParentScope(); + } +} + +void LVScope::traverseParentsAndChildren(LVObjectGetFunction GetFunction, + LVObjectSetFunction SetFunction) { + if (options().getReportParents()) { + // First traverse the parent tree. + LVScope *Parent = this; + while (Parent) { + // Terminates if the 'SetFunction' have been already executed. + if ((Parent->*GetFunction)()) + break; + (Parent->*SetFunction)(); + Parent = Parent->getParentScope(); + } + } + + std::function TraverseChildren = [&](LVScope *Scope) { + auto Traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) + (Entry->*SetFunction)(); + }; + + (Scope->*SetFunction)(); + + Traverse(Scope->getTypes()); + Traverse(Scope->getSymbols()); + Traverse(Scope->getLines()); + + if (const LVScopes *Scopes = Scope->getScopes()) + for (LVScope *Scope : *Scopes) + TraverseChildren(Scope); + }; + + if (options().getReportChildren()) + TraverseChildren(this); +} + +// Get all the locations associated with symbols. +void LVScope::getLocations(LVLocations &LocationList, + LVValidLocation ValidLocation, bool RecordInvalid) { + // Traverse scopes and symbols. + if (Symbols) + for (LVSymbol *Symbol : *Symbols) + Symbol->getLocations(LocationList, ValidLocation, RecordInvalid); + if (Scopes) + for (LVScope *Scope : *Scopes) + Scope->getLocations(LocationList, ValidLocation, RecordInvalid); +} + +// Get all the ranges associated with scopes. +void LVScope::getRanges(LVLocations &LocationList, + LVValidLocation ValidLocation, bool RecordInvalid) { + // Ignore discarded or stripped scopes (functions). + if (getIsDiscarded()) + return; + + // Process the ranges for current scope. + if (Ranges) { + for (LVLocation *Location : *Ranges) { + // Add the invalid location object. + if (!(Location->*ValidLocation)() && RecordInvalid) + LocationList.push_back(Location); + } + + // Calculate coverage factor. + calculateCoverage(); + } + + // Traverse the scopes. + if (Scopes) + for (LVScope *Scope : *Scopes) + Scope->getRanges(LocationList, ValidLocation, RecordInvalid); +} + +// Get all the ranges associated with scopes. +void LVScope::getRanges(LVRange &RangeList) { + // Ignore discarded or stripped scopes (functions). + if (getIsDiscarded()) + return; + + if (Ranges) + RangeList.addEntry(this); + if (Scopes) + for (LVScope *Scope : *Scopes) + Scope->getRanges(RangeList); +} + +LVScope *LVScope::outermostParent(LVAddress Address) { + LVScope *Parent = this; + while (Parent) { + if (Ranges) + for (const LVLocation *Location : *Ranges) + if (Location->getLowerAddress() <= Address) + return Parent; + Parent = Parent->getParentScope(); + } + return Parent; +} + +// Find the 'current' scope in the 'Targets' scope set. +LVScope *LVScope::findIn(const LVScopes *Targets) const { + if (Targets) { + // In the case of overloaded functions, sometimes the DWARF used to + // describe them, does not give suficient information. Try to find a + // perfect match or mark them as possible conflicts. + LVScopes Candidates; + for (LVScope *Target : *Targets) + if (LVScope::equals(Target)) + Candidates.push_back(Target); + + LLVM_DEBUG({ + if (!Candidates.empty()) { + dbgs() << "\n[LVScope::findIn]\n" + << "Reference: " + << "Offset = " << hexSquareString(getOffset()) << ", " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << "\n"; + for (const LVScope *Candidate : Candidates) + dbgs() << "Candidate: " + << "Offset = " << hexSquareString(Candidate->getOffset()) + << ", " + << "Level = " << Candidate->getLevel() << ", " + << "Kind = " << formattedKind(Candidate->kind()) << ", " + << "Name = " << formattedName(Candidate->getName()) << "\n"; + } + }); + + if (!Candidates.empty()) + return (Candidates.size() == 1) + ? (equals(Candidates[0]) ? Candidates[0] : nullptr) + : equals(&Candidates); + } + return nullptr; +} + +// Returns true if 'current' scope is equal to the given 'scope'. +bool LVScope::equalNumberOfChildren(const LVScope *Scope) const { + // Same number of children. Take into account which elements are requested + // to be included in the comparison. + return !( + (options().getCompareScopes() && scopeCount() != Scope->scopeCount()) || + (options().getCompareSymbols() && + symbolCount() != Scope->symbolCount()) || + (options().getCompareTypes() && typeCount() != Scope->typeCount()) || + (options().getCompareLines() && lineCount() != Scope->lineCount())); +} + +void LVScope::markMissingParents(const LVScope *Target, bool TraverseChildren) { + auto SetCompareState = [&](auto *Container) { + if (Container) + for (auto *Entry : *Container) + Entry->setIsInCompare(); + }; + SetCompareState(Types); + SetCompareState(Symbols); + SetCompareState(Lines); + SetCompareState(Scopes); + + // At this point, we are ready to start comparing the current scope, once + // the compare bits have been set. + if (options().getCompareTypes() && getTypes() && Target->getTypes()) + LVType::markMissingParents(getTypes(), Target->getTypes()); + if (options().getCompareSymbols() && getSymbols() && Target->getSymbols()) + LVSymbol::markMissingParents(getSymbols(), Target->getSymbols()); + if (options().getCompareLines() && getLines() && Target->getLines()) + LVLine::markMissingParents(getLines(), Target->getLines()); + if (getScopes() && Target->getScopes()) + LVScope::markMissingParents(getScopes(), Target->getScopes(), + TraverseChildren); +} + +void LVScope::markMissingParents(const LVScopes *References, + const LVScopes *Targets, + bool TraverseChildren) { + LLVM_DEBUG({ + if (References && Targets) { + dbgs() << "\n[LVScope::markMissingParents]\n"; + for (const LVScope *Reference : *References) + dbgs() << "References: " + << "Offset = " << hexSquareString(Reference->getOffset()) << ", " + << "Level = " << Reference->getLevel() << ", " + << "Kind = " << formattedKind(Reference->kind()) << ", " + << "Name = " << formattedName(Reference->getName()) << "\n"; + for (const LVScope *Target : *Targets) + dbgs() << "Targets : " + << "Offset = " << hexSquareString(Target->getOffset()) << ", " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + } + }); + if (References && Targets) + for (LVScope *Reference : *References) { + // Don't process 'Block' scopes, as we can't identify them. + if (Reference->getIsBlock() || Reference->getIsGeneratedName()) + continue; + + LLVM_DEBUG({ + dbgs() << "\nSearch Reference: " + << "Offset = " << hexSquareString(Reference->getOffset()) << " " + << "Name = " << formattedName(Reference->getName()) << "\n"; + }); + LVScope *Target = Reference->findIn(Targets); + if (Target) { + LLVM_DEBUG({ + dbgs() << "\nFound Target: " + << "Offset = " << hexSquareString(Target->getOffset()) << " " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + if (TraverseChildren) + Reference->markMissingParents(Target, TraverseChildren); + } else { + LLVM_DEBUG({ + dbgs() << "Missing Reference: " + << "Offset = " << hexSquareString(Reference->getOffset()) + << " " + << "Name = " << formattedName(Reference->getName()) << "\n"; + }); + Reference->markBranchAsMissing(); + } + } +} + +bool LVScope::equals(const LVScope *Scope) const { + if (!LVElement::equals(Scope)) + return false; + // For lexical scopes, check if their parents are the same. + if (getIsLexicalBlock() && Scope->getIsLexicalBlock()) + return getParentScope()->equals(Scope->getParentScope()); + return true; +} + +LVScope *LVScope::equals(const LVScopes *Scopes) const { + assert(Scopes && "Scopes must not be nullptr"); + for (LVScope *Scope : *Scopes) + if (equals(Scope)) + return Scope; + return nullptr; +} + +bool LVScope::equals(const LVScopes *References, const LVScopes *Targets) { + if (!References && !Targets) + return true; + if (References && Targets && References->size() == Targets->size()) { + for (const LVScope *Reference : *References) + if (!Reference->findIn(Targets)) + return false; + return true; + } + return false; +} + +void LVScope::report(LVComparePass Pass) { + getComparator().printItem(this, Pass); + getComparator().push(this); + if (Children) + for (LVElement *Element : *Children) + Element->report(Pass); + + if (Lines) + for (LVLine *Line : *Lines) + Line->report(Pass); + getComparator().pop(); +} + +void LVScope::printActiveRanges(raw_ostream &OS, bool Full) { + if (options().getPrintFormatting() && options().getAttributeRange() && + Ranges) { + for (const LVLocation *Location : *Ranges) + Location->print(OS, Full); + } +} + +void LVScope::printEncodedArgs(raw_ostream &OS, bool Full) const { + if (options().getPrintFormatting() && options().getAttributeEncoded()) + printAttributes(OS, Full, "{Encoded} ", const_cast(this), + getEncodedArgs(), /*UseQuotes=*/false, /*PrintRef=*/false); +} + +void LVScope::print(raw_ostream &OS, bool Full) const { + if (getIncludeInPrint() && getReader().doPrintScope(this)) { + // For a summary (printed elements), do not count the scope root. + // For a summary (selected elements) do not count a compile unit. + if (!(getIsRoot() || (getIsCompileUnit() && options().getSelectExecute()))) + getReaderCompileUnit()->incrementPrintedScopes(); + LVElement::print(OS, Full); + printExtra(OS, Full); + } +} + +void LVScope::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()); + // Do not print any type or name for a lexical block. + if (!getIsBlock()) { + OS << " " << formattedName(getName()); + if (!getIsAggregate()) + OS << " -> " << typeOffsetAsString() + << formattedNames(getTypeQualifiedName(), typeAsString()); + } + OS << "\n"; + + // Print any active ranges. + if (Full && getIsBlock()) + printActiveRanges(OS, Full); +} + +//===----------------------------------------------------------------------===// +// DWARF Union/Structure/Class. +//===----------------------------------------------------------------------===// +bool LVScopeAggregate::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + + if (!equalNumberOfChildren(Scope)) + return false; + + // Check if the parameters match in the case of templates. + if (!LVType::parametersMatch(getTypes(), Scope->getTypes())) + return false; + + if (!isNamed() && !Scope->isNamed()) + // Compare full name. + if (getFilenameIndex() != Scope->getFilenameIndex()) + return false; + + return true; +} + +LVScope *LVScopeAggregate::equals(const LVScopes *Scopes) const { + assert(Scopes && "Scopes must not be nullptr"); + for (LVScope *Scope : *Scopes) + if (equals(Scope)) + return Scope; + return nullptr; +} + +void LVScopeAggregate::printExtra(raw_ostream &OS, bool Full) const { + LVScope::printExtra(OS, Full); + if (Full) { + if (getIsTemplateResolved()) + printEncodedArgs(OS, Full); + LVScope *Reference = getReference(); + if (Reference) + Reference->printReference(OS, Full, const_cast(this)); + } +} + +//===----------------------------------------------------------------------===// +// DWARF Template alias. +//===----------------------------------------------------------------------===// +bool LVScopeAlias::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + return equalNumberOfChildren(Scope); +} + +void LVScopeAlias::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << " -> " + << typeOffsetAsString() + << formattedNames(getTypeQualifiedName(), typeAsString()) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF array (DW_TAG_array_type). +//===----------------------------------------------------------------------===// +void LVScopeArray::resolveExtra() { + // If the scope is an array, resolve the subrange entries and get those + // values encoded and assigned to the scope type. + // Encode the array subrange entries as part of the name. + if (getIsArrayResolved()) + return; + setIsArrayResolved(); + + // There are 2 cases to represent the bounds information for an array: + // 1) DW_TAG_array_type + // DW_AT_type --> ref_type + // DW_TAG_subrange_type + // DW_AT_type --> ref_type (type of object) + // DW_AT_count --> value (number of elements in subrange) + + // 2) DW_TAG_array_type + // DW_AT_type --> ref_type + // DW_TAG_subrange_type + // DW_AT_lower_bound --> value + // DW_AT_upper_bound --> value + + // The idea is to represent the bounds as a string, depending on the format: + // 1) [count] + // 2) [lower][upper] + + // Traverse scope types, looking for those types that are subranges. + LVTypes Subranges; + if (const LVTypes *Types = getTypes()) + for (LVType *Type : *Types) + if (Type->getIsSubrange()) { + Type->resolve(); + Subranges.push_back(Type); + } + + // Use the subrange types to generate the high level name for the array. + // Check the type has been fully resolved. + if (LVElement *BaseType = getType()) { + BaseType->resolveName(); + resolveFullname(BaseType); + } + + // In 'resolveFullname' a check is done for double spaces in the type name. + std::stringstream ArrayInfo; + if (ElementType) + ArrayInfo << getTypeName().str() << " "; + + for (const LVType *Type : Subranges) { + if (Type->getIsSubrangeCount()) + // Check if we have DW_AT_count subrange style. + ArrayInfo << "[" << Type->getCount() << "]"; + else { + // Get lower and upper subrange values. + unsigned LowerBound; + unsigned UpperBound; + std::tie(LowerBound, UpperBound) = Type->getBounds(); + + // The representation depends on the bound values. If the lower value + // is zero, treat the pair as the elements count. Otherwise, just use + // the pair, as they are representing arrays in languages other than + // C/C++ and the lower limit is not zero. + if (LowerBound) + ArrayInfo << "[" << LowerBound << ".." << UpperBound << "]"; + else + ArrayInfo << "[" << UpperBound + 1 << "]"; + } + } + + // Update the scope name, to reflect the encoded subranges. + setName(ArrayInfo.str()); +} + +bool LVScopeArray::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + + if (!equalNumberOfChildren(Scope)) + return false; + + // Despite the arrays are encoded, to reflect the dimensions, we have to + // check the subranges, in order to determine if they are the same. + if (!LVType::equals(getTypes(), Scope->getTypes())) + return false; + + return true; +} + +void LVScopeArray::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << typeOffsetAsString() + << formattedName(getName()) << "\n"; +} + +//===----------------------------------------------------------------------===// +// An object file (single or multiple CUs). +//===----------------------------------------------------------------------===// +void LVScopeCompileUnit::addSize(LVScope *Scope, LVOffset Lower, + LVOffset Upper) { + LLVM_DEBUG({ + dbgs() << format( + "CU [0x%08x], Scope [0x%08x], Range [0x%08x:0x%08x], Size = %d\n", + getOffset(), Scope->getOffset(), Lower, Upper, Upper - Lower); + }); + + // There is no need to check for a previous entry, as we are traversing the + // debug information in sequential order. + LVOffset Size = Upper - Lower; + Sizes[Scope] = Size; + if (this == Scope) + // Record contribution size for the compilation unit. + CUContributionSize = Size; +} + +// Update parents and children with pattern information. +void LVScopeCompileUnit::propagatePatternMatch() { + // At this stage, we have finished creating the Scopes tree and we have + // a list of elements that match the pattern specified in the command line. + // The pattern corresponds to a scope or element; mark parents and/or + // children as having that pattern, before any printing is done. + if (!options().getSelectExecute()) + return; + + if (MatchedScopes.size()) { + for (LVScope *Scope : MatchedScopes) + Scope->traverseParentsAndChildren(&LVScope::getHasPattern, + &LVScope::setHasPattern); + } else { + // Mark the compile unit as having a pattern to enable any requests to + // print sizes and summary as that information is recorded at that level. + setHasPattern(); + } +} + +void LVScopeCompileUnit::processRangeLocationCoverage( + LVValidLocation ValidLocation) { + if (options().getAttributeRange()) { + // Traverse the scopes to get scopes that have invalid ranges. + LVLocations Locations; + bool RecordInvalid = options().getWarningRanges(); + getRanges(Locations, ValidLocation, RecordInvalid); + + // Validate ranges associated with scopes. + if (RecordInvalid) + for (LVLocation *Location : Locations) + addInvalidRange(Location); + } + + if (options().getAttributeLocation()) { + // Traverse the scopes to get locations that have invalid ranges. + LVLocations Locations; + bool RecordInvalid = options().getWarningLocations(); + getLocations(Locations, ValidLocation, RecordInvalid); + + // Validate ranges associated with locations. + if (RecordInvalid) + for (LVLocation *Location : Locations) + addInvalidLocation(Location); + } +} + +void LVScopeCompileUnit::addMapping(LVLine *Line, LVSectionIndex SectionIndex) { + LVAddress Address = Line->getOffset(); + SectionMappings.add(SectionIndex, Address, Line); +} + +LVLine *LVScopeCompileUnit::lineLowerBound(LVAddress Address, + LVScope *Scope) const { + LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope); + LVAddressToLine *Map = SectionMappings.findMap(SectionIndex); + if (!Map || Map->empty()) + return nullptr; + LVAddressToLine::const_iterator Iter = Map->lower_bound(Address); + return (Iter != Map->end()) ? Iter->second : nullptr; +} + +LVLine *LVScopeCompileUnit::lineUpperBound(LVAddress Address, + LVScope *Scope) const { + LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope); + LVAddressToLine *Map = SectionMappings.findMap(SectionIndex); + if (!Map || Map->empty()) + return nullptr; + LVAddressToLine::const_iterator Iter = Map->upper_bound(Address); + if (Iter != Map->begin()) + Iter = std::prev(Iter); + return Iter->second; +} + +LVLineRange LVScopeCompileUnit::lineRange(LVLocation *Location) const { + // The parent of a location can be a symbol or a scope. + LVElement *Element = Location->getParent(); + LVScope *Parent = Element->getIsScope() ? static_cast(Element) + : Element->getParentScope(); + LVLine *LowLine = lineLowerBound(Location->getLowerAddress(), Parent); + LVLine *HighLine = lineUpperBound(Location->getUpperAddress(), Parent); + return LVLineRange(LowLine, HighLine); +} + +StringRef LVScopeCompileUnit::getFilename(size_t Index) const { + if (Index <= 0 || Index > Filenames.size()) + return StringRef(); + return getStringPool().getString(Filenames[Index - 1]); +} + +bool LVScopeCompileUnit::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + + return getNameIndex() == Scope->getNameIndex(); +} + +void LVScopeCompileUnit::incrementPrintedLines() { + options().getSelectExecute() ? ++Found.Lines : ++Printed.Lines; +} +void LVScopeCompileUnit::incrementPrintedScopes() { + options().getSelectExecute() ? ++Found.Scopes : ++Printed.Scopes; +} +void LVScopeCompileUnit::incrementPrintedSymbols() { + options().getSelectExecute() ? ++Found.Symbols : ++Printed.Symbols; +} +void LVScopeCompileUnit::incrementPrintedTypes() { + options().getSelectExecute() ? ++Found.Types : ++Printed.Types; +} + +// Values are used by '--summary' option (allocated). +void LVScopeCompileUnit::increment(LVLine *Line) { + if (Line->getIncludeInPrint()) + ++Allocated.Lines; +} +void LVScopeCompileUnit::increment(LVScope *Scope) { + if (Scope->getIncludeInPrint()) + ++Allocated.Scopes; +} +void LVScopeCompileUnit::increment(LVSymbol *Symbol) { + if (Symbol->getIncludeInPrint()) + ++Allocated.Symbols; +} +void LVScopeCompileUnit::increment(LVType *Type) { + if (Type->getIncludeInPrint()) + ++Allocated.Types; +} + +// A new element has been added to the scopes tree. Take the following steps: +// Increase the added element counters, for printing summary. +// Notify the Reader if element comparison. +void LVScopeCompileUnit::addedElement(LVLine *Line) { + increment(Line); + getReader().notifyAddedElement(Line); +} +void LVScopeCompileUnit::addedElement(LVScope *Scope) { + increment(Scope); + getReader().notifyAddedElement(Scope); +} +void LVScopeCompileUnit::addedElement(LVSymbol *Symbol) { + increment(Symbol); + getReader().notifyAddedElement(Symbol); +} +void LVScopeCompileUnit::addedElement(LVType *Type) { + increment(Type); + getReader().notifyAddedElement(Type); +} + +// Record unsuported DWARF tags. +void LVScopeCompileUnit::addDebugTag(dwarf::Tag Target, LVOffset Offset) { + addItem(&DebugTags, + Target, Offset); +} + +// Record elements with invalid offsets. +void LVScopeCompileUnit::addInvalidOffset(LVOffset Offset, LVElement *Element) { + if (WarningOffsets.find(Offset) == WarningOffsets.end()) + WarningOffsets.emplace(Offset, Element); +} + +// Record symbols with invalid coverage values. +void LVScopeCompileUnit::addInvalidCoverage(LVSymbol *Symbol) { + LVOffset Offset = Symbol->getOffset(); + if (InvalidCoverages.find(Offset) == InvalidCoverages.end()) + InvalidCoverages.emplace(Offset, Symbol); +} + +// Record symbols with invalid locations. +void LVScopeCompileUnit::addInvalidLocation(LVLocation *Location) { + addInvalidLocationOrRange(Location, Location->getParentSymbol(), + &InvalidLocations); +} + +// Record scopes with invalid ranges. +void LVScopeCompileUnit::addInvalidRange(LVLocation *Location) { + addInvalidLocationOrRange(Location, Location->getParentScope(), + &InvalidRanges); +} + +// Record line zero. +void LVScopeCompileUnit::addLineZero(LVLine *Line) { + LVScope *Scope = Line->getParentScope(); + LVOffset Offset = Scope->getOffset(); + addInvalidOffset(Offset, Scope); + addItem(&LinesZero, Offset, + Line); +} + +void LVScopeCompileUnit::printLocalNames(raw_ostream &OS, bool Full) const { + if (!options().getPrintFormatting()) + return; + + // Calculate an indentation value, to preserve a nice layout. + size_t Indentation = options().indentationSize() + + lineNumberAsString().length() + + indentAsString(getLevel() + 1).length() + 3; + + enum class Option { Directory, File }; + auto PrintNames = [&](Option Action) { + StringRef Kind = Action == Option::Directory ? "Directory" : "File"; + std::set UniqueNames; + for (size_t Index : Filenames) { + // In the case of missing directory name in the .debug_line table, + // the returned string has a leading '/'. + StringRef Name = getStringPool().getString(Index); + size_t Pos = Name.rfind('/'); + if (Pos != std::string::npos) + Name = (Action == Option::File) ? Name.substr(Pos + 1) + : Name.substr(0, Pos); + // Collect only unique names. + UniqueNames.insert(std::string(Name)); + } + for (const std::string &Name : UniqueNames) + OS << std::string(Indentation, ' ') << formattedKind(Kind) << " " + << formattedName(Name) << "\n"; + }; + + if (options().getAttributeDirectories()) + PrintNames(Option::Directory); + if (options().getAttributeFiles()) + PrintNames(Option::File); + if (options().getAttributePublics()) { + StringRef Kind = "Public"; + // The public names are indexed by 'LVScope *'. We want to print + // them by logical element address, to show the scopes layout. + using OffsetSorted = std::map; + OffsetSorted SortedNames; + for (LVPublicNames::const_iterator Iter = PublicNames.begin(); + Iter != PublicNames.end(); ++Iter) + SortedNames.emplace(Iter->first->getOffset(), Iter); + + LVPublicNames::const_iterator Iter; + for (OffsetSorted::reference Entry : SortedNames) { + Iter = Entry.second; + OS << std::string(Indentation, ' ') << formattedKind(Kind) << " " + << formattedName((*Iter).first->getName()); + if (options().getAttributeOffset()) { + LVAddress Address = (*Iter).second.first; + size_t Size = (*Iter).second.second; + OS << " [" << hexString(Address) << ":" << hexString(Address + Size) + << "]"; + } + OS << "\n"; + } + } +} + +void LVScopeCompileUnit::printWarnings(raw_ostream &OS, bool Full) const { + auto PrintHeader = [&](const char *Header) { OS << "\n" << Header << ":\n"; }; + auto PrintFooter = [&](auto &Set) { + if (Set.empty()) + OS << "None\n"; + }; + auto PrintOffset = [&](unsigned &Count, LVOffset Offset) { + if (Count == 5) { + Count = 0; + OS << "\n"; + } + ++Count; + OS << hexSquareString(Offset) << " "; + }; + auto PrintElement = [&](const LVOffsetElementMap &Map, LVOffset Offset) { + LVOffsetElementMap::const_iterator Iter = Map.find(Offset); + LVElement *Element = Iter != Map.end() ? Iter->second : nullptr; + OS << "[" << hexString(Offset) << "]"; + if (Element) + OS << " " << formattedKind(Element->kind()) << " " + << formattedName(Element->getName()); + OS << "\n"; + }; + auto PrintInvalidLocations = [&](const LVOffsetLocationsMap &Map, + const char *Header) { + PrintHeader(Header); + for (LVOffsetLocationsMap::const_reference Entry : Map) { + PrintElement(WarningOffsets, Entry.first); + for (const LVLocation *Location : *Entry.second) + OS << hexSquareString(Location->getOffset()) << " " + << Location->getIntervalInfo() << "\n"; + } + PrintFooter(Map); + }; + + if (options().getInternalTag() && getReader().isBinaryTypeELF()) { + PrintHeader("Unsupported DWARF Tags"); + for (LVTagOffsetsMap::const_reference Entry : DebugTags) { + OS << format("\n0x%02x", (unsigned)Entry.first) << ", " + << dwarf::TagString(Entry.first) << "\n"; + unsigned Count = 0; + for (const LVOffset &Offset : *Entry.second) + PrintOffset(Count, Offset); + OS << "\n"; + } + PrintFooter(DebugTags); + } + + if (options().getWarningCoverages()) { + PrintHeader("Symbols Invalid Coverages"); + for (LVOffsetSymbolMap::const_reference Entry : InvalidCoverages) { + // Symbol basic information. + LVSymbol *Symbol = Entry.second; + OS << hexSquareString(Entry.first) << " {Coverage} " + << format("%.2f%%", Symbol->getCoveragePercentage()) << " " + << formattedKind(Symbol->kind()) << " " + << formattedName(Symbol->getName()) << "\n"; + } + PrintFooter(InvalidCoverages); + } + + if (options().getWarningLines()) { + PrintHeader("Lines Zero References"); + for (LVOffsetLinesMap::const_reference Entry : LinesZero) { + PrintElement(WarningOffsets, Entry.first); + unsigned Count = 0; + for (const LVLine *Line : *Entry.second) + PrintOffset(Count, Line->getOffset()); + OS << "\n"; + } + PrintFooter(LinesZero); + } + + if (options().getWarningLocations()) + PrintInvalidLocations(InvalidLocations, "Invalid Location Ranges"); + + if (options().getWarningRanges()) + PrintInvalidLocations(InvalidRanges, "Invalid Code Ranges"); +} + +void LVScopeCompileUnit::printTotals(raw_ostream &OS) const { + OS << "\nTotals by lexical level:\n"; + for (size_t Index = 1; Index <= MaxSeenLevel; ++Index) + OS << format("[%03d]: %10d (%6.2f%%)\n", Index, Totals[Index].first, + Totals[Index].second); +} + +void LVScopeCompileUnit::printScopeSize(LVScope *Scope, raw_ostream &OS) { + LVSizesMap::iterator Iter = Sizes.find(Scope); + if (Iter != Sizes.end()) { + LVOffset Size = Iter->second; + assert(CUContributionSize && "Invalid CU contribution size."); + // Get a percentage rounded to two decimal digits. This avoids + // implementation-defined rounding inside printing functions. + float Percentage = + rint((float(Size) / CUContributionSize) * 100.0 * 100.0) / 100.0; + OS << format("%10d (%6.2f%%) : ", Size, Percentage); + Scope->print(OS); + + // Keep record of the total sizes at each lexical level. + LVLevel Level = Scope->getLevel(); + if (Level > MaxSeenLevel) + MaxSeenLevel = Level; + if (Level >= Totals.size()) + Totals.resize(2 * Level); + Totals[Level].first += Size; + Totals[Level].second += Percentage; + } +} + +void LVScopeCompileUnit::printSizes(raw_ostream &OS) const { + // Recursively print the contributions for each scope. + std::function PrintScope = [&](LVScope *Scope) { + // If we have selection criteria, then use only the selected scopes. + if (options().getSelectExecute() && options().getReportAnyView()) { + for (LVScope *Scope : MatchedScopes) + if (Scope->getLevel() < options().getOutputLevel()) + printScopeSize(Scope, OS); + return; + } + if (Scope->getLevel() < options().getOutputLevel()) { + if (const LVScopes *Scopes = Scope->getScopes()) + for (LVScope *Scope : *Scopes) { + printScopeSize(Scope, OS); + PrintScope(Scope); + } + } + }; + + bool PrintScopes = options().getPrintScopes(); + if (!PrintScopes) + options().setPrintScopes(); + getReader().setCompileUnit(const_cast(this)); + + OS << "\nScope Sizes:\n"; + options().resetPrintFormatting(); + options().setPrintOffset(); + + // Print the scopes regardless if the user has requested any scopes + // printing. Set the option just to allow printing the contributions. + printScopeSize(const_cast(this), OS); + PrintScope(const_cast(this)); + + // Print total scope sizes by level. + printTotals(OS); + + options().resetPrintOffset(); + options().setPrintFormatting(); + + if (!PrintScopes) + options().resetPrintScopes(); +} + +void LVScopeCompileUnit::printSummary(raw_ostream &OS) const { + printSummary(OS, options().getSelectExecute() ? Found : Printed, "Printed"); +} + +// Print summary details for the scopes tree. +void LVScopeCompileUnit::printSummary(raw_ostream &OS, const LVCounter &Counter, + const char *Header) const { + std::string Separator = std::string(29, '-'); + auto PrintSeparator = [&]() { OS << Separator << "\n"; }; + auto PrintHeadingRow = [&](const char *T, const char *U, const char *V) { + OS << format("%-9s%9s %9s\n", T, U, V); + }; + auto PrintDataRow = [&](const char *T, unsigned U, unsigned V) { + OS << format("%-9s%9d %9d\n", T, U, V); + }; + + OS << "\n"; + PrintSeparator(); + PrintHeadingRow("Element", "Total", Header); + PrintSeparator(); + PrintDataRow("Scopes", Allocated.Scopes, Counter.Scopes); + PrintDataRow("Symbols", Allocated.Symbols, Counter.Symbols); + PrintDataRow("Types", Allocated.Types, Counter.Types); + PrintDataRow("Lines", Allocated.Lines, Counter.Lines); + PrintSeparator(); + PrintDataRow( + "Total", + Allocated.Scopes + Allocated.Symbols + Allocated.Lines + Allocated.Types, + Counter.Scopes + Counter.Symbols + Counter.Lines + Counter.Types); +} + +void LVScopeCompileUnit::printMatchedElements(raw_ostream &OS, + bool UseMatchedElements) { + LVSortFunction SortFunction = getSortFunction(); + if (SortFunction) + std::stable_sort(MatchedElements.begin(), MatchedElements.end(), + SortFunction); + + // Check the type of elements required to be printed. 'MatchedElements' + // contains generic elements (lines, scopes, symbols, types). If we have a + // request to print any generic element, then allow the normal printing. + if (options().getPrintAnyElement()) { + if (UseMatchedElements) + OS << "\n"; + print(OS); + + if (UseMatchedElements) { + // Print the details for the matched elements. + for (const LVElement *Element : MatchedElements) + Element->print(OS); + } else { + // Print the view for the matched scopes. + for (const LVScope *Scope : MatchedScopes) { + Scope->print(OS); + if (const LVElements *Elements = Scope->getChildren()) + for (LVElement *Element : *Elements) + Element->print(OS); + } + } + + // Print any requested summary. + if (options().getPrintSummary()) { + // In the case of '--report=details' the matched elements are + // already counted; just proceed to print any requested summary. + // Otherwise, count them and print the summary. + if (!options().getReportList()) { + for (LVElement *Element : MatchedElements) { + if (!Element->getIncludeInPrint()) + continue; + if (Element->getIsType()) + ++Found.Types; + else if (Element->getIsSymbol()) + ++Found.Symbols; + else if (Element->getIsScope()) + ++Found.Scopes; + else if (Element->getIsLine()) + ++Found.Lines; + else + assert(Element && "Invalid element."); + } + } + printSummary(OS, Found, "Printed"); + } + } + + // Check if we have a request to print sizes for the matched elements + // that are scopes. + if (options().getPrintSizes()) { + OS << "\n"; + print(OS); + + OS << "\nScope Sizes:\n"; + printScopeSize(const_cast(this), OS); + for (LVElement *Element : MatchedElements) + if (Element->getIsScope()) + // Print sizes only for scopes. + printScopeSize(static_cast(Element), OS); + + printTotals(OS); + } +} + +void LVScopeCompileUnit::print(raw_ostream &OS, bool Full) const { + // Reset counters for printed and found elements. + const_cast(this)->Found.reset(); + const_cast(this)->Printed.reset(); + + if (getReader().doPrintScope(this) && options().getPrintFormatting()) + OS << "\n"; + + LVScope::print(OS, Full); +} + +void LVScopeCompileUnit::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " '" << getName() << "'\n"; + if (options().getPrintFormatting() && options().getAttributeProducer()) + printAttributes(OS, Full, "{Producer} ", + const_cast(this), getProducer(), + /*UseQuotes=*/true, + /*PrintRef=*/false); + + // Reset file index, to allow its children to print the correct filename. + options().resetFilenameIndex(); + + // Print any files, directories, public names and active ranges. + if (Full) { + printLocalNames(OS, Full); + printActiveRanges(OS, Full); + } +} + +//===----------------------------------------------------------------------===// +// DWARF enumeration (DW_TAG_enumeration_type). +//===----------------------------------------------------------------------===// +bool LVScopeEnumeration::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + return equalNumberOfChildren(Scope); +} + +void LVScopeEnumeration::printExtra(raw_ostream &OS, bool Full) const { + // Print the full type name. + OS << formattedKind(kind()) << " " << (getIsEnumClass() ? "class " : "") + << formattedName(getName()); + if (getHasType()) + OS << " -> " << typeOffsetAsString() + << formattedNames(getTypeQualifiedName(), typeAsString()); + OS << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF formal parameter pack (DW_TAG_GNU_formal_parameter_pack). +//===----------------------------------------------------------------------===// +bool LVScopeFormalPack::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + return equalNumberOfChildren(Scope); +} + +void LVScopeFormalPack::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF function. +//===----------------------------------------------------------------------===// +void LVScopeFunction::resolveReferences() { + // Before we resolve any references to other elements, check if we have + // to insert missing elements, that have been stripped, which will help + // the logical view comparison. + if (options().getAttributeInserted() && getHasReferenceAbstract() && + !getAddedMissing()) { + // Add missing elements at the function scope. + addMissingElements(getReference()); + if (Scopes) + for (LVScope *Scope : *Scopes) + if (Scope->getHasReferenceAbstract() && !Scope->getAddedMissing()) + // Add missing elements at the function nested scopes. + Scope->addMissingElements(Scope->getReference()); + } + + LVScope::resolveReferences(); + + // The DWARF 'extern' attribute is generated at the class level. + // 0000003f DW_TAG_class_type "CLASS" + // 00000048 DW_TAG_subprogram "bar" + // DW_AT_external DW_FORM_flag_present + // 00000070 DW_TAG_subprogram "bar" + // DW_AT_specification DW_FORM_ref4 0x00000048 + // The CodeView does not include any information at the class level to + // mark the member function as external. + // If there is a reference linking the declaration and definition, mark + // the definition as extern, to facilitate the logical view comparison. + if (getHasReferenceSpecification()) { + LVScope *Reference = getReference(); + if (Reference && Reference->getIsExternal()) { + Reference->resetIsExternal(); + setIsExternal(); + } + } + + // Resolve the function associated type. + if (!getType()) + if (LVScope *Reference = getReference()) + setType(Reference->getType()); +} + +void LVScopeFunction::setName(StringRef ObjectName) { + LVScope::setName(ObjectName); + // Check for system generated functions. + getReader().isSystemEntry(this, ObjectName); +} + +void LVScopeFunction::resolveExtra() { + // Check if we need to encode the template arguments. + if (getIsTemplate()) + resolveTemplate(); +} + +bool LVScopeFunction::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + + // When comparing logical elements, ignore any difference in the children. + if (options().getCompareContext() && !equalNumberOfChildren(Scope)) + return false; + + // Check if the linkage name matches. + if (getLinkageNameIndex() != Scope->getLinkageNameIndex()) + return false; + + // Check if the parameters match in the case of templates. + if (!LVType::parametersMatch(getTypes(), Scope->getTypes())) + return false; + + // Check if the arguments match. + if (!LVSymbol::parametersMatch(getSymbols(), Scope->getSymbols())) + return false; + + // Check if the lines match. + if (options().getCompareLines() && + !LVLine::equals(getLines(), Scope->getLines())) + return false; + + // Check if any reference is the same. + if (!referenceMatch(Scope)) + return false; + + if (getReference() && !getReference()->equals(Scope->getReference())) + return false; + + return true; +} + +LVScope *LVScopeFunction::equals(const LVScopes *Scopes) const { + assert(Scopes && "Scopes must not be nullptr"); + // Go through candidates and try to find a best match. + for (LVScope *Scope : *Scopes) + // Match arguments, children, lines, references. + if (equals(Scope)) + return Scope; + return nullptr; +} + +void LVScopeFunction::printExtra(raw_ostream &OS, bool Full) const { + LVScope *Reference = getReference(); + + // Inline attributes based on the reference element. + uint32_t InlineCode = + Reference ? Reference->getInlineCode() : getInlineCode(); + + // Accessibility depends on the parent (class, structure). + uint32_t AccessCode = 0; + if (getIsMember()) + AccessCode = getParentScope()->getIsClass() ? dwarf::DW_ACCESS_private + : dwarf::DW_ACCESS_public; + + std::string Attributes = + getIsCallSite() + ? "" + : formatAttributes(externalString(), accessibilityString(AccessCode), + inlineCodeString(InlineCode), virtualityString()); + + OS << formattedKind(kind()) << " " << Attributes << formattedName(getName()) + << discriminatorAsString() << " -> " << typeOffsetAsString() + << formattedNames(getTypeQualifiedName(), typeAsString()) << "\n"; + + // Print any active ranges. + if (Full) { + if (getIsTemplateResolved()) + printEncodedArgs(OS, Full); + printActiveRanges(OS, Full); + if (getLinkageNameIndex()) + printLinkageName(OS, Full, const_cast(this), + const_cast(this)); + if (Reference) + Reference->printReference(OS, Full, const_cast(this)); + } +} + +//===----------------------------------------------------------------------===// +// DWARF inlined function (DW_TAG_inlined_function). +//===----------------------------------------------------------------------===// +void LVScopeFunctionInlined::resolveExtra() { + // Check if we need to encode the template arguments. + if (getIsTemplate()) + resolveTemplate(); +} + +bool LVScopeFunctionInlined::equals(const LVScope *Scope) const { + if (!LVScopeFunction::equals(Scope)) + return false; + + // Check if any reference is the same. + if (getHasDiscriminator() && Scope->getHasDiscriminator()) + if (getDiscriminator() != Scope->getDiscriminator()) + return false; + + // Check the call site information. + if (getCallFilenameIndex() != Scope->getCallFilenameIndex() || + getCallLineNumber() != Scope->getCallLineNumber()) + return false; + + return true; +} + +LVScope *LVScopeFunctionInlined::equals(const LVScopes *Scopes) const { + return LVScopeFunction::equals(Scopes); +} + +void LVScopeFunctionInlined::printExtra(raw_ostream &OS, bool Full) const { + LVScopeFunction::printExtra(OS, Full); +} + +//===----------------------------------------------------------------------===// +// DWARF subroutine type. +//===----------------------------------------------------------------------===// +// Resolve a Subroutine Type (Callback). +void LVScopeFunctionType::resolveExtra() { + if (getIsMemberPointerResolved()) + return; + setIsMemberPointerResolved(); + + // The encoded string has the return type and the formal parameters type. + std::string Name(typeAsString()); + Name.append(" (*)"); + Name.append("("); + + // Traverse the scope symbols, looking for those which are parameters. + if (const LVSymbols *Symbols = getSymbols()) { + bool AddComma = false; + for (LVSymbol *Symbol : *Symbols) + if (Symbol->getIsParameter()) { + Symbol->resolve(); + if (LVElement *Type = Symbol->getType()) + Type->resolveName(); + if (AddComma) + Name.append(", "); + Name.append(std::string(Symbol->getTypeName())); + AddComma = true; + } + } + + Name.append(")"); + + // Update the scope name, to reflect the encoded parameters. + setName(Name.c_str()); +} + +//===----------------------------------------------------------------------===// +// DWARF namespace (DW_TAG_namespace). +//===----------------------------------------------------------------------===// +bool LVScopeNamespace::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + + if (!equalNumberOfChildren(Scope)) + return false; + + // Check if any reference is the same. + if (!referenceMatch(Scope)) + return false; + + if (getReference() && !getReference()->equals(Scope->getReference())) + return false; + + return true; +} + +LVScope *LVScopeNamespace::equals(const LVScopes *Scopes) const { + assert(Scopes && "Scopes must not be nullptr"); + // Go through candidates and try to find a best match. + for (LVScope *Scope : *Scopes) + if (equals(Scope)) + return Scope; + return nullptr; +} + +void LVScopeNamespace::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; + + // Print any active ranges. + if (Full) { + printActiveRanges(OS, Full); + + if (LVScope *Reference = getReference()) + Reference->printReference(OS, Full, const_cast(this)); + } +} + +//===----------------------------------------------------------------------===// +// An object file (single or multiple CUs). +//===----------------------------------------------------------------------===// +void LVScopeRoot::processRangeInformation() { + if (!options().getAttributeAnyLocation()) + return; + + if (Scopes) + for (LVScope *Scope : *Scopes) { + LVScopeCompileUnit *CompileUnit = + static_cast(Scope); + getReader().setCompileUnit(CompileUnit); + CompileUnit->processRangeLocationCoverage(); + } +} + +void LVScopeRoot::transformScopedName() { + // Recursively transform all names. + std::function TraverseScope = [&](LVScope *Parent) { + auto Traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) + Entry->setInnerComponent(); + }; + if (const LVScopes *Scopes = Parent->getScopes()) + for (LVScope *Scope : *Scopes) { + Scope->setInnerComponent(); + TraverseScope(Scope); + } + Traverse(Parent->getSymbols()); + Traverse(Parent->getTypes()); + Traverse(Parent->getLines()); + }; + + // Start traversing the scopes root and transform the element name. + TraverseScope(this); +} + +bool LVScopeRoot::equals(const LVScope *Scope) const { + return LVScope::equals(Scope); +} + +void LVScopeRoot::print(raw_ostream &OS, bool Full) const { + OS << "\nLogical View:\n"; + LVScope::print(OS, Full); +} + +void LVScopeRoot::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << ""; + if (options().getAttributeFormat()) + OS << " -> " << getFileFormatName(); + OS << "\n"; +} + +Error LVScopeRoot::doPrintMatches(bool Split, raw_ostream &OS, + bool UseMatchedElements) const { + // During a view output splitting, use the output stream created by the + // split context, then switch to the reader output stream. + static raw_ostream *StreamSplit = &OS; + + if (Scopes) { + if (UseMatchedElements) + options().resetPrintFormatting(); + print(OS); + + for (LVScope *Scope : *Scopes) { + getReader().setCompileUnit(const_cast(Scope)); + + // If 'Split', we use the scope name (CU name) as the ouput file; the + // delimiters in the pathname, must be replaced by a normal character. + if (Split) { + std::string ScopeName(Scope->getName()); + if (std::error_code EC = + getReaderSplitContext().open(ScopeName, ".txt", OS)) + return createStringError(EC, "Unable to create split output file %s", + ScopeName.c_str()); + StreamSplit = static_cast(&getReaderSplitContext().os()); + } + + Scope->printMatchedElements(*StreamSplit, UseMatchedElements); + + // Done printing the compile unit. Restore the original output context. + if (Split) { + getReaderSplitContext().close(); + StreamSplit = &getReader().outputStream(); + } + } + if (UseMatchedElements) + options().setPrintFormatting(); + } + + return Error::success(); +} + +//===----------------------------------------------------------------------===// +// DWARF template parameter pack (DW_TAG_GNU_template_parameter_pack). +//===----------------------------------------------------------------------===// +bool LVScopeTemplatePack::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + return equalNumberOfChildren(Scope); +} + +void LVScopeTemplatePack::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp @@ -0,0 +1,113 @@ +//===-- LVSort.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Support for LVObject sorting. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVSort.h" +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Sort" + +//===----------------------------------------------------------------------===// +// Callback functions to sort objects. +//===----------------------------------------------------------------------===// +// Callback comparator based on kind. +LVSortValue llvm::logicalview::compareKind(const LVObject *LHS, + const LVObject *RHS) { + return std::string(LHS->kind()) < std::string(RHS->kind()); +} + +// Callback comparator based on line. +LVSortValue llvm::logicalview::compareLine(const LVObject *LHS, + const LVObject *RHS) { + return LHS->getLineNumber() < RHS->getLineNumber(); +} + +// Callback comparator based on name. +LVSortValue llvm::logicalview::compareName(const LVObject *LHS, + const LVObject *RHS) { + return LHS->getName() < RHS->getName(); +} + +// Callback comparator based on DIE offset. +LVSortValue llvm::logicalview::compareOffset(const LVObject *LHS, + const LVObject *RHS) { + return LHS->getOffset() < RHS->getOffset(); +} + +// Callback comparator for Range compare. +LVSortValue llvm::logicalview::compareRange(const LVObject *LHS, + const LVObject *RHS) { + if (LHS->getLowerAddress() < RHS->getLowerAddress()) + return true; + + // If the lower address is the same, use the upper address value in + // order to put first the smallest interval. + if (LHS->getLowerAddress() == RHS->getLowerAddress()) + return LHS->getUpperAddress() < RHS->getUpperAddress(); + + return false; +} + +// Callback comparator based on multiple keys (First: Kind). +LVSortValue llvm::logicalview::sortByKind(const LVObject *LHS, + const LVObject *RHS) { + // Order in which the object attributes are used for comparison: + // kind, name, line number, offset. + std::tuple Left( + LHS->kind(), LHS->getName(), LHS->getLineNumber(), LHS->getOffset()); + std::tuple Right( + RHS->kind(), RHS->getName(), RHS->getLineNumber(), RHS->getOffset()); + return Left < Right; +} + +// Callback comparator based on multiple keys (First: Line). +LVSortValue llvm::logicalview::sortByLine(const LVObject *LHS, + const LVObject *RHS) { + // Order in which the object attributes are used for comparison: + // line number, name, kind, offset. + std::tuple Left( + LHS->getLineNumber(), LHS->getName(), LHS->kind(), LHS->getOffset()); + std::tuple Right( + RHS->getLineNumber(), RHS->getName(), RHS->kind(), RHS->getOffset()); + return Left < Right; +} + +// Callback comparator based on multiple keys (First: Name). +LVSortValue llvm::logicalview::sortByName(const LVObject *LHS, + const LVObject *RHS) { + // Order in which the object attributes are used for comparison: + // name, line number, kind, offset. + std::tuple Left( + LHS->getName(), LHS->getLineNumber(), LHS->kind(), LHS->getOffset()); + std::tuple Right( + RHS->getName(), RHS->getLineNumber(), RHS->kind(), RHS->getOffset()); + return Left < Right; +} + +LVSortFunction llvm::logicalview::getSortFunction() { + using LVSortInfo = std::map; + static LVSortInfo SortInfo = { + {LVSortMode::None, nullptr}, {LVSortMode::Kind, sortByKind}, + {LVSortMode::Line, sortByLine}, {LVSortMode::Name, sortByName}, + {LVSortMode::Offset, compareOffset}, + }; + + LVSortFunction SortFunction = nullptr; + LVSortInfo::iterator Iter = SortInfo.find(options().getSortMode()); + if (Iter != SortInfo.end()) + SortFunction = Iter->second; + return SortFunction; +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp @@ -0,0 +1,166 @@ +//===-- LVSupport.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the supporting functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Support" + +// Transforms '\\' into '/' (Platform independent). +std::string llvm::logicalview::transformPath(StringRef Path) { + std::string Name(Path); + std::transform(Name.begin(), Name.end(), Name.begin(), tolower); + std::replace(Name.begin(), Name.end(), '\\', '/'); + + // Remove all duplicate slashes. + size_t Pos = 0; + while ((Pos = Name.find("//", Pos)) != std::string::npos) + Name.erase(Pos, 1); + + return Name; +} + +// Transforms specific characters in the given pathname into '_'. +// The characters are: +// '/', '\', '<', '>', '.', ':', '%', '*', '?', '|', '"', ' '. +std::string llvm::logicalview::flattenedFilePath(StringRef Path) { + std::string Name(Path); + std::transform(Name.begin(), Name.end(), Name.begin(), tolower); + std::replace(Name.begin(), Name.end(), '.', '_'); + std::replace(Name.begin(), Name.end(), '/', '_'); + std::replace(Name.begin(), Name.end(), ':', '_'); + std::replace(Name.begin(), Name.end(), '<', '_'); + std::replace(Name.begin(), Name.end(), '>', '_'); + std::replace(Name.begin(), Name.end(), '%', '_'); + std::replace(Name.begin(), Name.end(), '*', '_'); + std::replace(Name.begin(), Name.end(), '?', '_'); + std::replace(Name.begin(), Name.end(), '|', '_'); + std::replace(Name.begin(), Name.end(), '\\', '_'); + std::replace(Name.begin(), Name.end(), '"', '_'); + std::replace(Name.begin(), Name.end(), ' ', '_'); + return Name; +} + +using LexicalEntry = std::pair; +using LexicalIndexes = SmallVector; + +static LexicalIndexes getAllLexicalIndexes(StringRef Name) { + if (Name.empty()) + return {}; + + bool InTemplate = false; + size_t AngleCount = 0; + size_t CharsSeen = 0; + size_t ColonSeen = 0; + size_t Current = 0; + + LexicalIndexes Indexes; + +#ifndef NDEBUG + auto PrintLexicalEntry = [&]() { + LexicalEntry Entry = Indexes.back(); + llvm::dbgs() << formatv( + "'{0}:{1}', '{2}'\n", Entry.first, Entry.second, + Name.substr(Entry.first, Entry.second - Entry.first + 1)); + }; +#endif + + size_t Length = Name.size(); + for (size_t Index = 0; Index < Length; ++Index) { + LLVM_DEBUG({ + llvm::dbgs() << formatv("Index: '{0}', Char: '{1}'\n", Index, + Name[Index]); + }); + switch (Name[Index]) { + case '<': + ++AngleCount; + InTemplate = true; + break; + case '>': + --AngleCount; + break; + case ':': + ++ColonSeen; + break; + } + ++CharsSeen; + if (ColonSeen == 2) { + if (!InTemplate) { + Indexes.push_back(LexicalEntry(Current, Index - 2)); + Current = Index + 1; + CharsSeen = 0; + LLVM_DEBUG({ PrintLexicalEntry(); }); + } + ColonSeen = 0; + continue; + } + if (InTemplate && !AngleCount) + InTemplate = false; + } + + // Store last component. + Indexes.push_back(LexicalEntry(Current, Length - 1)); + LLVM_DEBUG({ PrintLexicalEntry(); }); + return Indexes; +} + +LVLexicalComponent llvm::logicalview::getInnerComponent(StringRef Name) { + if (Name.empty()) + return {}; + + LexicalIndexes Indexes = getAllLexicalIndexes(Name); + if (Indexes.size() == 1) + return std::make_tuple(StringRef(), Name); + + LexicalEntry BeginEntry = Indexes.front(); + LexicalEntry EndEntry = Indexes[Indexes.size() - 2]; + StringRef Outer = + Name.substr(BeginEntry.first, EndEntry.second - BeginEntry.first + 1); + + LexicalEntry LastEntry = Indexes.back(); + StringRef Inner = + Name.substr(LastEntry.first, LastEntry.second - LastEntry.first + 1); + + return std::make_tuple(Outer, Inner); +} + +LVStringRefs llvm::logicalview::getAllLexicalComponents(StringRef Name) { + if (Name.empty()) + return {}; + + LexicalIndexes Indexes = getAllLexicalIndexes(Name); + LVStringRefs Components; + for (const LexicalEntry &Entry : Indexes) + Components.push_back( + Name.substr(Entry.first, Entry.second - Entry.first + 1)); + + return Components; +} + +std::string llvm::logicalview::getScopedName(const LVStringRefs &Components, + StringRef BaseName) { + if (Components.empty()) + return {}; + std::string Name(BaseName); + raw_string_ostream Stream(Name); + if (BaseName.size()) + Stream << "::"; + Stream << Components[0]; + for (LVStringRefs::size_type Index = 1; Index < Components.size(); ++Index) + Stream << "::" << Components[Index]; + return Name; +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp @@ -0,0 +1,448 @@ +//===-- LVSymbol.cpp ------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVSymbol class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Symbol" + +namespace { +const char *const KindCallSiteParameter = "CallSiteParameter"; +const char *const KindConstant = "Constant"; +const char *const KindInherits = "Inherits"; +const char *const KindMember = "Member"; +const char *const KindParameter = "Parameter"; +const char *const KindUndefined = "Undefined"; +const char *const KindUnspecified = "Unspecified"; +const char *const KindVariable = "Variable"; +} // end anonymous namespace + +// Return a string representation for the symbol kind. +const char *LVSymbol::kind() const { + static const char *Kind = KindUndefined; + if (getIsCallSiteParameter()) + Kind = KindCallSiteParameter; + else if (getIsConstant()) + Kind = KindConstant; + else if (getIsInheritance()) + Kind = KindInherits; + else if (getIsMember()) + Kind = KindMember; + else if (getIsParameter()) + Kind = KindParameter; + else if (getIsUnspecified()) + Kind = KindUnspecified; + else if (getIsVariable()) + Kind = KindVariable; + return Kind; +} + +LVSymbolDispatch LVSymbol::Dispatch = { + {LVSymbolKind::IsCallSiteParameter, &LVSymbol::getIsCallSiteParameter}, + {LVSymbolKind::IsConstant, &LVSymbol::getIsConstant}, + {LVSymbolKind::IsInheritance, &LVSymbol::getIsInheritance}, + {LVSymbolKind::IsMember, &LVSymbol::getIsMember}, + {LVSymbolKind::IsParameter, &LVSymbol::getIsParameter}, + {LVSymbolKind::IsUnspecified, &LVSymbol::getIsUnspecified}, + {LVSymbolKind::IsVariable, &LVSymbol::getIsVariable}}; + +// Add a Location Entry. +void LVSymbol::addLocation(dwarf::Attribute Attr, LVAddress LowPC, + LVAddress HighPC, LVUnsigned SectionOffset, + uint64_t LocDescOffset, bool CallSiteLocation) { + if (!Locations) + Locations = new LVAutoLocations(); + + // Create the location entry. + CurrentLocation = new LVLocationSymbol(); + CurrentLocation->setParent(this); + CurrentLocation->setAttr(Attr); + if (CallSiteLocation) + CurrentLocation->setIsCallSite(); + CurrentLocation->addObject(LowPC, HighPC, SectionOffset, LocDescOffset); + Locations->push_back(CurrentLocation); + + // Mark the symbol as having location information. + setHasLocation(); +} + +// Add a Location Record. +void LVSymbol::addLocationOperands(LVSmall Opcode, uint64_t Operand1, + uint64_t Operand2) { + if (CurrentLocation) + CurrentLocation->addObject(Opcode, Operand1, Operand2); +} + +// Add a Location Entry. +void LVSymbol::addLocationConstant(dwarf::Attribute Attr, LVUnsigned Constant, + uint64_t LocDescOffset) { + // Create a Location Entry, with the global information. + addLocation(Attr, + /*LowPC=*/0, /*HighPC=*/-1, + /*SectionOffset=*/0, LocDescOffset); + + // Add records to Location Entry. + addLocationOperands(/*Opcode=*/LVLocationMemberOffset, + /*Operand1=*/Constant, /*Operand2=*/0); +} + +LVLocations::iterator LVSymbol::addLocationGap(LVLocations::iterator Pos, + LVAddress LowPC, + LVAddress HighPC) { + // Create a location entry for the gap. + LVLocation *Gap = new LVLocationSymbol(); + Gap->setParent(this); + Gap->setAttr(dwarf::DW_AT_location); + Gap->addObject(LowPC, HighPC, + /*section_offset=*/0, + /*locdesc_offset=*/0); + + LVLocations::iterator Iter = Locations->insert(Pos, Gap); + + // Add gap to Location Entry. + Gap->addObject(/*op=*/dwarf::DW_OP_hi_user, + /*opd1=*/0, /*opd2=*/0); + + // Mark the entry as a gap. + Gap->setIsGapEntry(); + + return Iter; +} + +void LVSymbol::fillLocationGaps() { + // The symbol has locations records. Fill gaps in the location list. + if (!getHasLocation() || !getFillGaps()) + return; + + // Get the parent range information and add dummy location entries. + const LVLocations *Ranges = getParentScope()->getRanges(); + if (!Ranges) + return; + + for (const LVLocation *Entry : *Ranges) { + LVAddress ParentLowPC = Entry->getLowerAddress(); + LVAddress ParentHighPC = Entry->getUpperAddress(); + + // Traverse the symbol locations and for each location contained in + // the current parent range, insert locations for any existing gap. + LVLocation *Location; + LVAddress LowPC = 0; + LVAddress Marker = ParentLowPC; + for (LVLocations::iterator Iter = Locations->begin(); + Iter != Locations->end(); ++Iter) { + Location = *Iter; + LowPC = Location->getLowerAddress(); + if (LowPC != Marker) { + // We have a gap at [Marker,LowPC - 1]. + Iter = addLocationGap(Iter, Marker, LowPC - 1); + ++Iter; + } + + // Move to the next item in the location list. + Marker = Location->getUpperAddress() + 1; + } + + // Check any gap at the end. + if (Marker < ParentHighPC) + // We have a gap at [Marker,ParentHighPC]. + addLocationGap(Locations->end(), Marker, ParentHighPC); + } +} + +// Get all the locations based on the valid function. +void LVSymbol::getLocations(LVLocations &LocationList, + LVValidLocation ValidLocation, bool RecordInvalid) { + if (!Locations) + return; + + for (LVLocation *Location : *Locations) { + // Add the invalid location object. + if (!(Location->*ValidLocation)() && RecordInvalid) + LocationList.push_back(Location); + } + + // Calculate coverage factor. + calculateCoverage(); +} + +void LVSymbol::getLocations(LVLocations &LocationList) const { + if (!Locations) + return; + + for (LVLocation *Location : *Locations) + LocationList.push_back(Location); +} + +// Calculate coverage factor. +void LVSymbol::calculateCoverage() { + if (!LVLocation::calculateCoverage(Locations, CoverageFactor, + CoveragePercentage)) { + LVScope *Parent = getParentScope(); + if (Parent->getIsInlinedFunction()) { + // For symbols representing the inlined function parameters and its + // variables, get the outer most parent that contains their location + // lower address. + // The symbol can have a set of non-continuos locations. We are using + // only the first location entry to get the outermost parent. + // If no scope contains the location, assume its enclosing parent. + LVScope *Scope = + Parent->outermostParent(Locations->front()->getLowerAddress()); + if (Scope) + Parent = Scope; + } + unsigned CoverageParent = Parent->getCoverageFactor(); + // Get a percentage rounded to two decimal digits. This avoids + // implementation-defined rounding inside printing functions. + CoveragePercentage = + CoverageParent + ? rint((double(CoverageFactor) / CoverageParent) * 100.0 * 100.0) / + 100.0 + : 0; + // Record invalid coverage entry. + if (options().getWarningCoverages() && CoveragePercentage > 100) + getReaderCompileUnit()->addInvalidCoverage(this); + } +} + +void LVSymbol::resolveName() { + if (getIsResolvedName()) + return; + setIsResolvedName(); + + LVElement::resolveName(); + + // Resolve any given pattern. + patterns().resolvePatternMatch(this); +} + +void LVSymbol::resolveReferences() { + // The symbols can have the following references to other elements: + // A Type: + // DW_AT_type -> Type or Scope + // DW_AT_import -> Type + // A Reference: + // DW_AT_specification -> Symbol + // DW_AT_abstract_origin -> Symbol + // DW_AT_extension -> Symbol + + // Resolve any referenced symbol. + LVSymbol *Reference = getReference(); + if (Reference) { + Reference->resolve(); + // Recursively resolve the symbol names. + resolveReferencesChain(); + } + + // Set the file/line information using the Debug Information entry. + setFile(Reference); + + // Resolve symbol type. + if (LVElement *Element = getType()) { + Element->resolve(); + + // In the case of demoted typedefs, use the underlying type. + if (Element->getIsTypedefReduced()) { + Element = Element->getType(); + Element->resolve(); + } + + // If the type is a template parameter, get its type, which can + // point to a type or scope, depending on the argument instance. + setGenericType(Element); + } + + // Resolve the variable associated type. + if (!getType() && Reference) + setType(Reference->getType()); +} + +StringRef LVSymbol::resolveReferencesChain() { + // If the symbol have a DW_AT_specification or DW_AT_abstract_origin, + // follow the chain to resolve the name from those references. + if (getHasReference() && !isNamed()) + setName(getReference()->resolveReferencesChain()); + + return getName(); +} + +void LVSymbol::markMissingParents(const LVSymbols *References, + const LVSymbols *Targets) { + LLVM_DEBUG({ + if (References && Targets) { + dbgs() << "\n[LVSymbol::markMissingParents]\n"; + for (const LVSymbol *Reference : *References) + dbgs() << "References: " + << "Kind = " << formattedKind(Reference->kind()) << ", " + << "Name = " << formattedName(Reference->getName()) << "\n"; + for (const LVSymbol *Target : *Targets) + dbgs() << "Targets : " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + } + }); + if (References && Targets) + for (LVSymbol *Reference : *References) { + LLVM_DEBUG({ + dbgs() << "Search Reference: Name = " + << formattedName(Reference->getName()) << "\n"; + }); + if (!Reference->findIn(Targets)) + Reference->markBranchAsMissing(); + } +} + +// Find the 'current' symbol in the 'Targets' symbol set. +LVSymbol *LVSymbol::findIn(const LVSymbols *Targets) const { + if (Targets) { + LLVM_DEBUG({ + dbgs() << "\n[LVSymbol::findIn]\n" + << "Reference: " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << "\n"; + for (const LVSymbol *Target : *Targets) + dbgs() << "Target : " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + + for (LVSymbol *Target : *Targets) + if (equals(Target)) + return Target; + } + return nullptr; +} + +// Check for a match on the arguments of a function. +bool LVSymbol::parametersMatch(const LVSymbols *References, + const LVSymbols *Targets) { + if (!References && !Targets) + return true; + if (References && Targets) { + LVSymbols ReferenceParams; + getParameters(References, &ReferenceParams); + LVSymbols TargetParams; + getParameters(Targets, &TargetParams); + return LVSymbol::equals(&ReferenceParams, &TargetParams); + } + return false; +} + +// Return the symbols which are parameters. +void LVSymbol::getParameters(const LVSymbols *Symbols, LVSymbols *Parameters) { + if (Symbols) + for (LVSymbol *Symbol : *Symbols) + if (Symbol->getIsParameter()) + Parameters->push_back(Symbol); +} + +bool LVSymbol::equals(const LVSymbol *Symbol) const { + if (!LVElement::equals(Symbol)) + return false; + + // Check if any reference is the same. + if (!referenceMatch(Symbol)) + return false; + + if (getReference() && !getReference()->equals(Symbol->getReference())) + return false; + + return true; +} + +bool LVSymbol::equals(const LVSymbols *References, const LVSymbols *Targets) { + if (!References && !Targets) + return true; + if (References && Targets && References->size() == Targets->size()) { + for (const LVSymbol *Reference : *References) + if (!Reference->findIn(Targets)) + return false; + return true; + } + return false; +} + +void LVSymbol::report(LVComparePass Pass) { + getComparator().printItem(this, Pass); +} + +void LVSymbol::printLocations(raw_ostream &OS, bool Full) const { + if (Locations) + for (const LVLocation *Location : *Locations) + Location->printRaw(OS, Full); +} + +void LVSymbol::print(raw_ostream &OS, bool Full) const { + if (getIncludeInPrint() && getReader().doPrintSymbol(this)) { + getReaderCompileUnit()->incrementPrintedSymbols(); + LVElement::print(OS, Full); + printExtra(OS, Full); + } +} + +void LVSymbol::printExtra(raw_ostream &OS, bool Full) const { + // Accessibility depends on the parent (class, structure). + uint32_t AccessCode = 0; + if (getIsMember() || getIsInheritance()) + AccessCode = getParentScope()->getIsClass() ? dwarf::DW_ACCESS_private + : dwarf::DW_ACCESS_public; + + const LVSymbol *Symbol = getIsInlined() ? Reference : this; + std::string Attributes = + Symbol->getIsCallSiteParameter() + ? "" + : formatAttributes(Symbol->externalString(), + Symbol->accessibilityString(AccessCode), + virtualityString()); + + OS << formattedKind(Symbol->kind()) << " " << Attributes; + if (Symbol->getIsUnspecified()) + OS << formattedName(Symbol->getName()); + else { + if (Symbol->getIsInheritance()) + OS << Symbol->typeOffsetAsString() + << formattedNames(Symbol->getTypeQualifiedName(), + Symbol->typeAsString()); + else { + OS << formattedName(Symbol->getName()); + // Print any bitfield information. + if (uint32_t Size = getBitSize()) + OS << ":" << Size; + OS << " -> " << Symbol->typeOffsetAsString() + << formattedNames(Symbol->getTypeQualifiedName(), + Symbol->typeAsString()); + } + } + + // Print any initial value if any. + if (ValueIndex) + OS << " = " << formattedName(getValue()); + OS << "\n"; + + if (Full && options().getPrintFormatting()) { + if (getLinkageNameIndex()) + printLinkageName(OS, Full, const_cast(this)); + if (LVSymbol *Reference = getReference()) + Reference->printReference(OS, Full, const_cast(this)); + + if (Locations) + // Print location information. + LVLocation::print(Locations, OS, Full); + } +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp @@ -0,0 +1,528 @@ +//===-- LVType.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVType class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Type" + +namespace { +const char *const KindBaseType = "BaseType"; +const char *const KindConst = "Const"; +const char *const KindEnumerator = "Enumerator"; +const char *const KindImport = "Import"; +const char *const KindPointer = "Pointer"; +const char *const KindPointerMember = "PointerMember"; +const char *const KindReference = "Reference"; +const char *const KindRestrict = "Restrict"; +const char *const KindRvalueReference = "RvalueReference"; +const char *const KindSubrange = "Subrange"; +const char *const KindTemplateTemplate = "TemplateTemplate"; +const char *const KindTemplateType = "TemplateType"; +const char *const KindTemplateValue = "TemplateValue"; +const char *const KindTypeAlias = "TypeAlias"; +const char *const KindUndefined = "Undefined"; +const char *const KindUnaligned = "Unaligned"; +const char *const KindUnspecified = "Unspecified"; +const char *const KindVolatile = "Volatile"; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// DWARF Type. +//===----------------------------------------------------------------------===// +// Return a string representation for the type kind. +const char *LVType::kind() const { + static const char *Kind = KindUndefined; + if (getIsBase()) + Kind = KindBaseType; + else if (getIsConst()) + Kind = KindConst; + else if (getIsEnumerator()) + Kind = KindEnumerator; + else if (getIsImport()) + Kind = KindImport; + else if (getIsPointerMember()) + Kind = KindPointerMember; + else if (getIsPointer()) + Kind = KindPointer; + else if (getIsReference()) + Kind = KindReference; + else if (getIsRestrict()) + Kind = KindRestrict; + else if (getIsRvalueReference()) + Kind = KindRvalueReference; + else if (getIsSubrange()) + Kind = KindSubrange; + else if (getIsTemplateTypeParam()) + Kind = KindTemplateType; + else if (getIsTemplateValueParam()) + Kind = KindTemplateValue; + else if (getIsTemplateTemplateParam()) + Kind = KindTemplateTemplate; + else if (getIsTypedef()) + Kind = KindTypeAlias; + else if (getIsUnaligned()) + Kind = KindUnaligned; + else if (getIsUnspecified()) + Kind = KindUnspecified; + else if (getIsVolatile()) + Kind = KindVolatile; + return Kind; +} + +LVTypeDispatch LVType::Dispatch = { + {LVTypeKind::IsBase, &LVType::getIsBase}, + {LVTypeKind::IsConst, &LVType::getIsConst}, + {LVTypeKind::IsEnumerator, &LVType::getIsEnumerator}, + {LVTypeKind::IsImport, &LVType::getIsImport}, + {LVTypeKind::IsImportDeclaration, &LVType::getIsImportDeclaration}, + {LVTypeKind::IsImportModule, &LVType::getIsImportModule}, + {LVTypeKind::IsPointer, &LVType::getIsPointer}, + {LVTypeKind::IsPointerMember, &LVType::getIsPointerMember}, + {LVTypeKind::IsReference, &LVType::getIsReference}, + {LVTypeKind::IsRestrict, &LVType::getIsRestrict}, + {LVTypeKind::IsRvalueReference, &LVType::getIsRvalueReference}, + {LVTypeKind::IsSubrange, &LVType::getIsSubrange}, + {LVTypeKind::IsTemplateParam, &LVType::getIsTemplateParam}, + {LVTypeKind::IsTemplateTemplateParam, &LVType::getIsTemplateTemplateParam}, + {LVTypeKind::IsTemplateTypeParam, &LVType::getIsTemplateTypeParam}, + {LVTypeKind::IsTemplateValueParam, &LVType::getIsTemplateValueParam}, + {LVTypeKind::IsTypedef, &LVType::getIsTypedef}, + {LVTypeKind::IsUnaligned, &LVType::getIsUnaligned}, + {LVTypeKind::IsUnspecified, &LVType::getIsUnspecified}, + {LVTypeKind::IsVolatile, &LVType::getIsVolatile}}; + +void LVType::resolveReferences() { + // Some DWARF tags are the representation of types. However, we associate + // some of them to scopes. The ones associated with types, do not have + // any reference tags, such as DW_AT_specification, DW_AT_abstract_origin, + // DW_AT_extension. + + // Set the file/line information using the Debug Information entry. + setFile(/*Reference=*/nullptr); + + if (LVElement *Element = getType()) + Element->resolve(); +} + +void LVType::resolveName() { + if (getIsResolvedName()) + return; + setIsResolvedName(); + + // The templates are recorded as normal DWARF objects relationships; + // the template parameters are preserved to show the types used during + // the instantiation; however if a compare have been requested, those + // parameters needs to be resolved, so no conflicts are generated. + // The following DWARF illustrates this issue: + // + // a) Template Parameters are preserved: + // {Class} 'ConstArray' + // {Inherits} -> 'ArrayBase' + // {TemplateType} 'taTYPE' -> 'AtomTable' + // {Member} 'mData' -> '* taTYPE' + // + // b) Template Parameters are resolved: + // {Class} 'ConstArray' + // {Inherits} -> 'ArrayBase' + // {TemplateType} 'taTYPE' -> 'AtomTable' + // {Member} 'mData' -> '* AtomTable' + // + // In (b), the {Member} type have been resolved to use the real type. + + LVElement *BaseType = getType(); + if (BaseType && options().getAttributeArgument()) + if (BaseType->isTemplateParam()) + BaseType = BaseType->getType(); + + if (BaseType && !BaseType->getIsResolvedName()) + BaseType->resolveName(); + resolveFullname(BaseType, getName()); + + // In the case of unnamed types, try to generate a name for it, using + // the parents name and the line information. Ignore the template + if (!isNamed() && !getIsTemplateParam()) + generateName(); + + LVElement::resolveName(); + + // Resolve any given pattern. + patterns().resolvePatternMatch(this); +} + +StringRef LVType::resolveReferencesChain() { + // The types do not have a DW_AT_specification or DW_AT_abstract_origin + // reference. Just return the type name. + return getName(); +} + +void LVType::markMissingParents(const LVTypes *References, + const LVTypes *Targets) { + LLVM_DEBUG({ + if (References && Targets) { + dbgs() << "\n[LVType::markMissingParents]\n"; + for (const LVType *Reference : *References) + dbgs() << "References: " + << "Kind = " << formattedKind(Reference->kind()) << ", " + << "Name = " << formattedName(Reference->getName()) << "\n"; + for (const LVType *Target : *Targets) + dbgs() << "Targets : " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + } + }); + if (References && Targets) + for (LVType *Reference : *References) { + LLVM_DEBUG({ + dbgs() << "Search Reference: Name = " + << formattedName(Reference->getName()) << "\n"; + }); + if (!Reference->findIn(Targets)) + Reference->markBranchAsMissing(); + } +} + +// Find the 'current' type in the 'Targets' type set. +LVType *LVType::findIn(const LVTypes *Targets) const { + if (Targets) { + LLVM_DEBUG({ + dbgs() << "\n[LVType::findIn]\n" + << "Reference: " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << "\n"; + for (const LVType *Target : *Targets) + dbgs() << "Target : " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + + for (LVType *Target : *Targets) + if (equals(Target)) + return Target; + } + return nullptr; +} + +// Check for a match on the arguments of a function. +bool LVType::parametersMatch(const LVTypes *References, + const LVTypes *Targets) { + if (!References && !Targets) + return true; + if (References && Targets) { + LVTypes ReferenceTypes; + LVScopes ReferenceScopes; + getParameters(References, &ReferenceTypes, &ReferenceScopes); + LVTypes TargetTypes; + LVScopes TargetScopes; + getParameters(Targets, &TargetTypes, &TargetScopes); + if (!LVType::equals(&ReferenceTypes, &TargetTypes) || + !LVScope::equals(&ReferenceScopes, &TargetScopes)) + return false; + return true; + } + return false; +} + +// Return the types which are parameters. +void LVType::getParameters(const LVTypes *Types, LVTypes *TypesParam, + LVScopes *ScopesParam) { + if (!Types) + return; + + // During a compare task, the template parameters are expanded to + // point to their real types, to avoid compare conflicts. + for (LVType *Type : *Types) { + if (!Type->getIsTemplateParam()) + continue; + if (options().getAttributeArgument()) { + LVScope *Scope = nullptr; + if (Type->getIsKindType()) + Type = Type->getTypeAsType(); + else { + if (Type->getIsKindScope()) { + Scope = Type->getTypeAsScope(); + Type = nullptr; + } + } + Type ? TypesParam->push_back(Type) : ScopesParam->push_back(Scope); + } else + TypesParam->push_back(Type); + } +} + +bool LVType::equals(const LVType *Type) const { + return LVElement::equals(Type); +} + +bool LVType::equals(const LVTypes *References, const LVTypes *Targets) { + if (!References && !Targets) + return true; + if (References && Targets && References->size() == Targets->size()) { + for (const LVType *Reference : *References) + if (!Reference->findIn(Targets)) + return false; + return true; + } + return false; +} + +void LVType::report(LVComparePass Pass) { + getComparator().printItem(this, Pass); +} + +void LVType::print(raw_ostream &OS, bool Full) const { + if (getIncludeInPrint() && + (getIsReference() || getReader().doPrintType(this))) { + getReaderCompileUnit()->incrementPrintedTypes(); + LVElement::print(OS, Full); + printExtra(OS, Full); + } +} + +void LVType::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF typedef. +//===----------------------------------------------------------------------===// +// Return the underlying type for a typedef, which can be a type or scope. +LVElement *LVTypeDefinition::getUnderlyingType() { + LVElement *BaseType = getTypeAsScope(); + if (BaseType) + // Underlying type is a scope. + return BaseType; + + LVType *Type = getTypeAsType(); + assert(Type && "Type definition does not have a type."); + + BaseType = Type; + while (Type->getIsTypedef()) { + BaseType = Type->getTypeAsScope(); + if (BaseType) + // Underlying type is a scope. + return BaseType; + + Type = Type->getTypeAsType(); + if (Type) + BaseType = Type; + } + + return BaseType; +} + +void LVTypeDefinition::resolveExtra() { + // In the case of CodeView, the MSVC toolset generates a series of typedefs + // that refer to internal runtime structures, that we do not process. Those + // typedes are marked as 'system'. They have an associated logical type, + // but the underlying type always is null. + if (getIsSystem()) + return; + + // Set the reference to the typedef type. + if (options().getAttributeUnderlying()) { + setUnderlyingType(getUnderlyingType()); + setIsTypedefReduced(); + if (LVElement *Type = getType()) { + Type->resolveName(); + resolveFullname(Type); + } + } + + // For the case of typedef'd anonymous structures: + // typedef struct { ... } Name; + // Propagate the typedef name to the anonymous structure. + LVScope *Aggregate = getTypeAsScope(); + if (Aggregate && Aggregate->getIsAnonymous()) + Aggregate->setName(getName()); +} + +bool LVTypeDefinition::equals(const LVType *Type) const { + return LVType::equals(Type); +} + +void LVTypeDefinition::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << " -> " + << typeOffsetAsString() + << formattedName((getType() ? getType()->getName() : "")) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF enumerator (DW_TAG_enumerator). +//===----------------------------------------------------------------------===// +bool LVTypeEnumerator::equals(const LVType *Type) const { + return LVType::equals(Type); +} + +void LVTypeEnumerator::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " '" << getName() + << "' = " << formattedName(getValue()) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF import (DW_TAG_imported_module / DW_TAG_imported_declaration). +//===----------------------------------------------------------------------===// +bool LVTypeImport::equals(const LVType *Type) const { + return LVType::equals(Type); +} + +void LVTypeImport::printExtra(raw_ostream &OS, bool Full) const { + std::string Attributes = + formatAttributes(virtualityString(), accessibilityString()); + + OS << formattedKind(kind()) << " " << typeOffsetAsString() << Attributes + << formattedName((getType() ? getType()->getName() : "")) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF Template parameter holder (type or param). +//===----------------------------------------------------------------------===// +LVTypeParam::LVTypeParam() : LVType() { + options().getAttributeTypename() ? setIncludeInPrint() + : resetIncludeInPrint(); +} + +// Encode the specific template argument. +void LVTypeParam::encodeTemplateArgument(std::string &Name) const { + // The incoming type is a template parameter; we have 3 kinds of parameters: + // - type parameter: resolve the instance (type); + // - value parameter: resolve the constant value + // - template parameter: resolve the name of the template. + // If the parameter type is a template instance (STL sample), we need to + // expand the type (template template case). For the following variable + // declarations: + // std::type a_float; + // std::type a_int; + // We must generate names like: + // "std::type,std::allocator,false>" + // "std::type,std::allocator,false>" + // Instead of the incomplete names: + // "type" + // "type" + + if (getIsTemplateTypeParam()) { + // Get the type instance recorded in the template type; it can be a + // reference to a type or to a scope. + + if (getIsKindType()) { + // The argument types always are qualified. + Name.append(std::string(getTypeQualifiedName())); + + LVType *ArgType = getTypeAsType(); + // For template arguments that are typedefs, use the underlying type, + // which can be a type or scope. + if (ArgType->getIsTypedef()) { + LVObject *BaseType = ArgType->getUnderlyingType(); + Name.append(std::string(BaseType->getName())); + } else + Name.append(std::string(ArgType->getName())); + } else { + if (getIsKindScope()) { + LVScope *ArgScope = getTypeAsScope(); + // If the scope is a template, we have to resolve that template, + // by recursively traversing its arguments. + if (ArgScope->getIsTemplate()) + ArgScope->encodeTemplateArguments(Name); + else { + // The argument types always are qualified. + Name.append(std::string(getTypeQualifiedName())); + Name.append(std::string(ArgScope->getName())); + } + } + } + } else + // Template value parameter or template template parameter. + Name.append(getValue()); +} + +bool LVTypeParam::equals(const LVType *Type) const { + if (!LVType::equals(Type)) + return false; + + // Checks the kind of template argument. + if (getIsTemplateTypeParam() && Type->getIsTemplateTypeParam()) + return getType()->equals(Type->getType()); + + if ((getIsTemplateValueParam() && Type->getIsTemplateValueParam()) || + (getIsTemplateTemplateParam() && Type->getIsTemplateTemplateParam())) + return getValueIndex() == Type->getValueIndex(); + + return false; +} + +void LVTypeParam::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << " -> " + << typeOffsetAsString(); + + // Depending on the type of parameter, the print includes different + // information: type, value or reference to a template. + if (getIsTemplateTypeParam()) { + OS << formattedNames(getTypeQualifiedName(), getTypeName()) << "\n"; + return; + } + if (getIsTemplateValueParam()) { + OS << formattedName(getValue()) << " " << formattedName(getName()) << "\n"; + return; + } + if (getIsTemplateTemplateParam()) + OS << formattedName(getValue()) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DW_TAG_subrange_type +//===----------------------------------------------------------------------===// +void LVTypeSubrange::resolveExtra() { + // There are 2 cases to represent the bounds information for an array: + // 1) DW_TAG_subrange_type + // DW_AT_type --> ref_type (type of count) + // DW_AT_count --> value (number of elements in subrange) + + // 2) DW_TAG_subrange_type + // DW_AT_lower_bound --> value + // DW_AT_upper_bound --> value + + // The idea is to represent the bounds as a string, depending on the format: + // 1) [count] + // 2) [lower..upper] + + // Subrange information. + std::string String; + + // Check if we have DW_AT_count subrange style. + if (getIsSubrangeCount()) + // Get count subrange value. Assume 0 if missing. + raw_string_ostream(String) << "[" << getCount() << "]"; + else + raw_string_ostream(String) + << "[" << getLowerBound() << ".." << getUpperBound() << "]"; + + setName(String); +} + +bool LVTypeSubrange::equals(const LVType *Type) const { + if (!LVType::equals(Type)) + return false; + + return getTypeName() == Type->getTypeName() && getName() == Type->getName(); +} + +void LVTypeSubrange::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " -> " << typeOffsetAsString() + << formattedName(getTypeName()) << " " << formattedName(getName()) << "\n"; +} Index: llvm/lib/DebugInfo/LogicalView/LLVMBuild.txt =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/LLVMBuild.txt @@ -0,0 +1,21 @@ +;===- ./lib/DebugInfo/LogicalView/LLVMBuild.txt ----------------*- Conf -*--===; +; +; 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 +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = DebugInfoLogicalView +parent = DebugInfo +required_libraries = Object Support DebugInfoDWARF DebugInfoCodeView DebugInfoPDB Index: llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp @@ -0,0 +1,324 @@ +//===-- LVReaderHandler.cpp -----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This class implements the Reader Handler. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVELFReader.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Object/COFF.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::pdb; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "ReaderHandler" + +Error LVReaderHandler::process() { + if (Error Err = createReaders()) + return Err; + if (Error Err = printReaders()) + return Err; + if (Error Err = compareReaders()) + return Err; + + return Error::success(); +} + +void LVReaderHandler::destroyReaders() { + LLVM_DEBUG(dbgs() << "destroyReaders\n"); + for (const LVReader *Reader : TheReaders) + delete Reader; +} + +Error LVReaderHandler::createReader(StringRef Filename, LVReaders &Readers, + PdbOrObj &Input, StringRef FileFormatName, + StringRef ExePath) { + auto CreateOneReader = [&]() -> LVReader * { + if (Input.is()) { + ObjectFile &Obj = *Input.get(); + if (Obj.isCOFF()) { + COFFObjectFile *COFF = dyn_cast(&Obj); + return new LVCodeViewReader(Filename, FileFormatName, *COFF, W, + ExePath); + } + if (Obj.isELF() || Obj.isMachO()) + return new LVELFReader(Filename, FileFormatName, Obj, W); + } + if (Input.is()) { + PDBFile &Pdb = *Input.get(); + return new LVCodeViewReader(Filename, FileFormatName, Pdb, W, ExePath); + } + return nullptr; + }; + + LVReader *Reader = CreateOneReader(); + if (!Reader) { + std::string TheFilename(Filename); + return createStringError(errc::invalid_argument, + "unable to create reader for: '%s'", + TheFilename.c_str()); + } + + Readers.push_back(Reader); + return Reader->doLoad(); +} + +Error LVReaderHandler::handleArchive(LVReaders &Readers, StringRef Filename, + Archive &Arch) { + Error Err = Error::success(); + std::string TheFilename(Filename); + for (const Archive::Child &Child : Arch.children(Err)) { + Expected BuffOrErr = Child.getMemoryBufferRef(); + if (Error Err = BuffOrErr.takeError()) + return createStringError(errorToErrorCode(std::move(Err)), "%s", + TheFilename.c_str()); + Expected NameOrErr = Child.getName(); + if (Error Err = NameOrErr.takeError()) + return createStringError(errorToErrorCode(std::move(Err)), "%s", + TheFilename.c_str()); + std::string Name = (Filename + "(" + NameOrErr.get() + ")").str(); + if (Error Err = handleBuffer(Readers, Name, BuffOrErr.get())) + return createStringError(errorToErrorCode(std::move(Err)), "%s", + TheFilename.c_str()); + } + + return Error::success(); +} + +// Search for a matching executable image for the given PDB path. +static std::string searchForExe(const StringRef Path, + const StringRef Extension) { + SmallString<128> ExePath(Path); + llvm::sys::path::replace_extension(ExePath, Extension); + + std::unique_ptr Session; + if (Error Err = loadDataForEXE(PDB_ReaderType::Native, ExePath, Session)) { + consumeError(std::move(Err)); + return {}; + } + // We have a candidate for the executable image. + Expected PdbPathOrErr = NativeSession::searchForPdb({ExePath}); + if (!PdbPathOrErr) { + consumeError(PdbPathOrErr.takeError()); + return {}; + } + // Convert any Windows backslashes into forward slashes to get the path. + std::string ConvertedPath = sys::path::convert_to_slash( + PdbPathOrErr.get(), sys::path::Style::windows); + if (ConvertedPath == Path) + return std::string(ExePath); + + return {}; +} + +// Search for a matching object image for the given PDB path. +static std::string searchForObj(const StringRef Path, + const StringRef Extension) { + SmallString<128> ObjPath(Path); + llvm::sys::path::replace_extension(ObjPath, Extension); + if (llvm::sys::fs::exists(Twine(ObjPath))) { + ErrorOr> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(ObjPath); + if (!BuffOrErr) + return {}; + return std::string(ObjPath); + } + + return {}; +} + +Error LVReaderHandler::handleBuffer(LVReaders &Readers, StringRef Filename, + MemoryBufferRef Buffer, StringRef ExePath) { + // As PDB does not support the Binary interface, at this point we can check + // if the buffer corresponds to a PDB or PE file. + file_magic FileMagic = identify_magic(Buffer.getBuffer()); + if (FileMagic == file_magic::pdb) { + if (!ExePath.empty()) + return handleObject(Readers, Filename, Buffer.getBuffer(), ExePath); + + // Search in the directory derivated from the given 'Filename' for a + // matching object file (.o, .obj, .lib) or a matching executable file + // (.exe/.dll) and try to create the reader based on the matched file. + // If no matching file is found then we load the original PDB file. + std::vector ExecutableExtensions = {"exe", "dll"}; + for (StringRef Extension : ExecutableExtensions) { + std::string ExecutableImage = searchForExe(Filename, Extension); + if (ExecutableImage.empty()) + continue; + if (Error Err = handleObject(Readers, Filename, Buffer.getBuffer(), + ExecutableImage)) { + consumeError(std::move(Err)); + continue; + } + return Error::success(); + } + + std::vector ObjectExtensions = {"o", "obj", "lib"}; + for (StringRef Extension : ObjectExtensions) { + std::string ObjectImage = searchForObj(Filename, Extension); + if (ObjectImage.empty()) + continue; + if (Error Err = handleFile(Readers, ObjectImage)) { + consumeError(std::move(Err)); + continue; + } + return Error::success(); + } + + // No matching executable/object image was found. Load the given PDB. + return handleObject(Readers, Filename, Buffer.getBuffer(), ExePath); + } + if (FileMagic == file_magic::pecoff_executable) { + // If we have a valid executable, try to find a matching PDB file. + Expected PdbPath = NativeSession::searchForPdb({Filename}); + if (errorToErrorCode(PdbPath.takeError())) { + std::string TheFilename(Filename); + return createStringError( + errc::not_supported, + "Binary object format in '%s' does not have debug info.", + TheFilename.c_str()); + } + // Process the matching PDB file and pass the executable filename. + return handleFile(Readers, PdbPath.get(), Filename); + } + + Expected> BinOrErr = createBinary(Buffer); + if (errorToErrorCode(BinOrErr.takeError())) { + std::string TheFilename(Filename); + return createStringError(errc::not_supported, + "Binary object format in '%s' is not supported.", + TheFilename.c_str()); + } + return handleObject(Readers, Filename, *BinOrErr.get()); +} + +Error LVReaderHandler::handleFile(LVReaders &Readers, StringRef Filename, + StringRef ExePath) { + // Convert any Windows backslashes into forward slashes to get the path. + std::string ConvertedPath = + sys::path::convert_to_slash(Filename, sys::path::Style::windows); + ErrorOr> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(ConvertedPath); + if (BuffOrErr.getError()) { + std::string TheFilename(ConvertedPath); + return createStringError(errc::bad_file_descriptor, + "File '%s' does not exist.", TheFilename.c_str()); + } + std::unique_ptr Buffer = std::move(BuffOrErr.get()); + return handleBuffer(Readers, ConvertedPath, *Buffer, ExePath); +} + +Error LVReaderHandler::handleMach(LVReaders &Readers, StringRef Filename, + MachOUniversalBinary &Mach) { + for (const MachOUniversalBinary::ObjectForArch &ObjForArch : Mach.objects()) { + std::string ObjName = (Twine(Filename) + Twine("(") + + Twine(ObjForArch.getArchFlagName()) + Twine(")")) + .str(); + if (Expected> MachOOrErr = + ObjForArch.getAsObjectFile()) { + MachOObjectFile &Obj = **MachOOrErr; + PdbOrObj Input = &Obj; + if (Error Err = + createReader(Filename, Readers, Input, Obj.getFileFormatName())) + return Err; + continue; + } else + consumeError(MachOOrErr.takeError()); + if (Expected> ArchiveOrErr = + ObjForArch.getAsArchive()) { + if (Error Err = handleArchive(Readers, ObjName, *ArchiveOrErr.get())) + return Err; + continue; + } else + consumeError(ArchiveOrErr.takeError()); + } + return Error::success(); +} + +Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename, + Binary &Binary) { + if (PdbOrObj Input = dyn_cast(&Binary)) + return createReader(Filename, Readers, Input, + Input.get()->getFileFormatName()); + + if (MachOUniversalBinary *Fat = dyn_cast(&Binary)) + return handleMach(Readers, Filename, *Fat); + + if (Archive *Arch = dyn_cast(&Binary)) + return handleArchive(Readers, Filename, *Arch); + + return Error::success(); +} + +Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename, + StringRef Buffer, StringRef ExePath) { + std::unique_ptr Session; + if (Error Err = loadDataForPDB(PDB_ReaderType::Native, Filename, Session)) { + std::string TheFilename(Filename); + return createStringError(errorToErrorCode(std::move(Err)), "%s", + TheFilename.c_str()); + } + + std::unique_ptr PdbSession; + PdbSession.reset(static_cast(Session.release())); + PdbOrObj Input = &PdbSession->getPDBFile(); + StringRef FileFormatName; + size_t Pos = Buffer.find_first_of("\r\n"); + if (Pos) + FileFormatName = Buffer.substr(0, Pos - 1); + return createReader(Filename, Readers, Input, FileFormatName, ExePath); +} + +Error LVReaderHandler::createReaders() { + LLVM_DEBUG(dbgs() << "createReaders\n"); + for (std::string &Object : Objects) { + LVReaders Readers; + if (Error Err = createReader(Object, Readers)) + return Err; + TheReaders.insert(TheReaders.end(), Readers.begin(), Readers.end()); + } + + return Error::success(); +} + +Error LVReaderHandler::printReaders() { + LLVM_DEBUG(dbgs() << "printReaders\n"); + for (LVReader *Reader : TheReaders) + if (options().getPrintExecute()) + if (Error Err = Reader->doPrint()) + return Err; + + return Error::success(); +} + +Error LVReaderHandler::compareReaders() { + LLVM_DEBUG(dbgs() << "compareReaders\n"); + size_t ReadersCount = TheReaders.size(); + if (options().getCompareExecute() && ReadersCount >= 2) { + // If we have more than 2 readers, compare them by pairs. + size_t ViewPairs = ReadersCount / 2; + LVCompare Compare(OS); + for (size_t Pair = 0, Index = 0; Pair < ViewPairs; ++Pair) { + if (Error Err = Compare.execute(TheReaders[Index], TheReaders[Index + 1])) + return Err; + Index += 2; + } + } + + return Error::success(); +} + +void LVReaderHandler::print(raw_ostream &OS) const { OS << "ReaderHandler\n"; } Index: llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp @@ -0,0 +1,895 @@ +//===-- LVBinaryReader.cpp ------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVBinaryReader class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "BinaryReader" + +// Function names extracted from the object symbol table. +void LVSymbolTable::add(StringRef Name, LVScope *Function, + LVSectionIndex SectionIndex) { + std::string SymbolName(Name); + if (SymbolNames.find(SymbolName) == SymbolNames.end()) { + SymbolNames.emplace( + std::piecewise_construct, std::forward_as_tuple(SymbolName), + std::forward_as_tuple(Function, 0, SectionIndex, false)); + } else { + // Update a recorded entry with its logical scope and section index. + SymbolNames[SymbolName].Scope = Function; + if (SectionIndex) + SymbolNames[SymbolName].SectionIndex = SectionIndex; + } + + if (Function && SymbolNames[SymbolName].IsComdat) + Function->setIsComdat(); + + LLVM_DEBUG({ print(dbgs()); }); +} + +void LVSymbolTable::add(StringRef Name, LVAddress Address, + LVSectionIndex SectionIndex, bool IsComdat) { + std::string SymbolName(Name); + if (SymbolNames.find(SymbolName) == SymbolNames.end()) + SymbolNames.emplace( + std::piecewise_construct, std::forward_as_tuple(SymbolName), + std::forward_as_tuple(nullptr, Address, SectionIndex, IsComdat)); + else + // Update a recorded symbol name with its logical scope. + SymbolNames[SymbolName].Address = Address; + + LVScope *Function = SymbolNames[SymbolName].Scope; + if (Function && IsComdat) + Function->setIsComdat(); + LLVM_DEBUG({ print(dbgs()); }); +} + +LVSectionIndex LVSymbolTable::update(LVScope *Function) { + LVSectionIndex SectionIndex = getReader().getDotTextSectionIndex(); + StringRef Name = Function->getLinkageName(); + if (Name.empty()) + Name = Function->getName(); + std::string SymbolName(Name); + + if (SymbolName.empty() || (SymbolNames.find(SymbolName) == SymbolNames.end())) + return SectionIndex; + + // Update a recorded entry with its logical scope, only if the scope has + // ranges. That is the case when in DWARF there are 2 DIEs connected via + // the DW_AT_specification. + if (Function->getHasRanges()) { + SymbolNames[SymbolName].Scope = Function; + SectionIndex = SymbolNames[SymbolName].SectionIndex; + } else { + SectionIndex = UndefinedSectionIndex; + } + + if (SymbolNames[SymbolName].IsComdat) + Function->setIsComdat(); + + LLVM_DEBUG({ print(dbgs()); }); + return SectionIndex; +} + +const LVSymbolTableEntry &LVSymbolTable::getEntry(StringRef Name) { + static LVSymbolTableEntry Empty = LVSymbolTableEntry(); + LVSymbolNames::iterator Iter = SymbolNames.find(std::string(Name)); + return Iter != SymbolNames.end() ? Iter->second : Empty; +} +LVAddress LVSymbolTable::getAddress(StringRef Name) { + LVSymbolNames::iterator Iter = SymbolNames.find(std::string(Name)); + return Iter != SymbolNames.end() ? Iter->second.Address : 0; +} +LVSectionIndex LVSymbolTable::getIndex(StringRef Name) { + LVSymbolNames::iterator Iter = SymbolNames.find(std::string(Name)); + return Iter != SymbolNames.end() ? Iter->second.SectionIndex + : getReader().getDotTextSectionIndex(); +} +bool LVSymbolTable::getIsComdat(StringRef Name) { + LVSymbolNames::iterator Iter = SymbolNames.find(std::string(Name)); + return Iter != SymbolNames.end() ? Iter->second.IsComdat : false; +} + +void LVSymbolTable::print(raw_ostream &OS) { + OS << "Symbol Table\n"; + for (LVSymbolNames::reference Entry : SymbolNames) { + LVSymbolTableEntry &SymbolName = Entry.second; + LVScope *Scope = SymbolName.Scope; + LVOffset Offset = Scope ? Scope->getOffset() : 0; + OS << "Index: " << hexValue(SymbolName.SectionIndex, 5) + << " Comdat: " << (SymbolName.IsComdat ? "Y" : "N") + << " Scope: " << hexValue(Offset) + << " Address: " << hexValue(SymbolName.Address) + << " Name: " << Entry.first << "\n"; + } +} + +void LVBinaryReader::addToSymbolTable(StringRef Name, LVScope *Function, + LVSectionIndex SectionIndex) { + SymbolTable.add(Name, Function, SectionIndex); +} +void LVBinaryReader::addToSymbolTable(StringRef Name, LVAddress Address, + LVSectionIndex SectionIndex, + bool IsComdat) { + SymbolTable.add(Name, Address, SectionIndex, IsComdat); +} +LVSectionIndex LVBinaryReader::updateSymbolTable(LVScope *Function) { + return SymbolTable.update(Function); +} + +const LVSymbolTableEntry &LVBinaryReader::getSymbolTableEntry(StringRef Name) { + return SymbolTable.getEntry(Name); +} +LVAddress LVBinaryReader::getSymbolTableAddress(StringRef Name) { + return SymbolTable.getAddress(Name); +} +LVSectionIndex LVBinaryReader::getSymbolTableIndex(StringRef Name) { + return SymbolTable.getIndex(Name); +} +bool LVBinaryReader::getSymbolTableIsComdat(StringRef Name) { + return SymbolTable.getIsComdat(Name); +} + +void LVBinaryReader::mapVirtualAddress(const object::ObjectFile &Obj) { + for (const object::SectionRef &Section : Obj.sections()) { + if (!Section.isText() || Section.isVirtual() || !Section.getSize()) + continue; + + // Record section information required for symbol resolution. + // Note: The section index returned by 'getIndex()' is one based. + Sections.emplace(Section.getIndex(), Section); + addSectionAddress(Section); + + // Identify the ".text" section. + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) { + consumeError(SectionNameOrErr.takeError()); + continue; + } + if ((*SectionNameOrErr).equals(".text") || + (*SectionNameOrErr).equals(".code")) + DotTextSectionIndex = Section.getIndex(); + } + + // Process the symbol table. + mapRangeAddress(Obj); + + LLVM_DEBUG({ + dbgs() << "\nSections Information:\n"; + for (LVSections::reference Entry : Sections) { + LVSectionIndex SectionIndex = Entry.first; + const object::SectionRef Section = Entry.second; + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + consumeError(SectionNameOrErr.takeError()); + dbgs() << "\nIndex: " << format_decimal(SectionIndex, 3) + << " Name: " << *SectionNameOrErr << "\n" + << "Size: " << hexValue(Section.getSize()) << "\n" + << "VirtualAddress: " << hexValue(VirtualAddress) << "\n" + << "SectionAddress: " << hexValue(Section.getAddress()) << "\n"; + } + dbgs() << "\nObject Section Information:\n"; + for (LVSectionAddresses::const_reference Entry : SectionAddresses) + dbgs() << "[" << hexValue(Entry.first) << ":" + << hexValue(Entry.first + Entry.second.getSize()) + << "] Size: " << hexValue(Entry.second.getSize()) << "\n"; + }); +} + +void LVBinaryReader::mapVirtualAddress(const object::COFFObjectFile &COFFObj) { + ErrorOr ImageBase = COFFObj.getImageBase(); + if (ImageBase) + ImageBaseAddress = ImageBase.get(); + + LLVM_DEBUG({ + dbgs() << "ImageBaseAddress: " << hexValue(ImageBaseAddress) << "\n"; + }); + + uint32_t Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_LNK_COMDAT; + + for (const object::SectionRef &Section : COFFObj.sections()) { + if (!Section.isText() || Section.isVirtual() || !Section.getSize()) + continue; + + const object::coff_section *COFFSection = COFFObj.getCOFFSection(Section); + VirtualAddress = COFFSection->VirtualAddress; + bool IsComdat = (COFFSection->Characteristics & Flags) == Flags; + + // Record section information required for symbol resolution. + // Note: The section index returned by 'getIndex()' is zero based. + Sections.emplace(Section.getIndex() + 1, Section); + addSectionAddress(Section); + + // Additional initialization on the specific object format. + mapRangeAddress(COFFObj, Section, IsComdat); + } + + LLVM_DEBUG({ + dbgs() << "\nSections Information:\n"; + for (LVSections::reference Entry : Sections) { + LVSectionIndex SectionIndex = Entry.first; + const object::SectionRef Section = Entry.second; + const object::coff_section *COFFSection = COFFObj.getCOFFSection(Section); + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + consumeError(SectionNameOrErr.takeError()); + dbgs() << "\nIndex: " << format_decimal(SectionIndex, 3) + << " Name: " << *SectionNameOrErr << "\n" + << "Size: " << hexValue(Section.getSize()) << "\n" + << "VirtualAddress: " << hexValue(VirtualAddress) << "\n" + << "SectionAddress: " << hexValue(Section.getAddress()) << "\n" + << "PointerToRawData: " << hexValue(COFFSection->PointerToRawData) + << "\n" + << "SizeOfRawData: " << hexValue(COFFSection->SizeOfRawData) + << "\n"; + } + dbgs() << "\nObject Section Information:\n"; + for (LVSectionAddresses::const_reference Entry : SectionAddresses) + dbgs() << "[" << hexValue(Entry.first) << ":" + << hexValue(Entry.first + Entry.second.getSize()) + << "] Size: " << hexValue(Entry.second.getSize()) << "\n"; + }); +} + +Error LVBinaryReader::loadGenericTargetInfo(StringRef TheTriple, + StringRef TheFeatures) { + std::string TargetLookupError; + const Target *TheTarget = + TargetRegistry::lookupTarget(std::string(TheTriple), TargetLookupError); + if (!TheTarget) + return createStringError(errc::invalid_argument, TargetLookupError.c_str()); + + // Register information. + MCRegisterInfo *RegisterInfo = TheTarget->createMCRegInfo(TheTriple); + if (!RegisterInfo) + return createStringError(errc::invalid_argument, + "no register info for target " + TheTriple); + MRI.reset(RegisterInfo); + + // Assembler properties and features. + MCTargetOptions MCOptions; + MCAsmInfo *AsmInfo(TheTarget->createMCAsmInfo(*MRI, TheTriple, MCOptions)); + if (!AsmInfo) + return createStringError(errc::invalid_argument, + "no assembly info for target " + TheTriple); + MAI.reset(AsmInfo); + + // Target subtargets. + StringRef CPU; + MCSubtargetInfo *SubtargetInfo( + TheTarget->createMCSubtargetInfo(TheTriple, CPU, TheFeatures)); + if (!SubtargetInfo) + return createStringError(errc::invalid_argument, + "no subtarget info for target " + TheTriple); + STI.reset(SubtargetInfo); + + // Instructions Info. + MCInstrInfo *InstructionInfo(TheTarget->createMCInstrInfo()); + if (!InstructionInfo) + return createStringError(errc::invalid_argument, + "no instruction info for target " + TheTriple); + MII.reset(InstructionInfo); + + MC = std::make_unique(Triple(TheTriple), MAI.get(), MRI.get(), + STI.get()); + + // Assembler. + MCDisassembler *DisAsm(TheTarget->createMCDisassembler(*STI, *MC)); + if (!DisAsm) + return createStringError(errc::invalid_argument, + "no disassembler for target " + TheTriple); + MD.reset(DisAsm); + + MCInstPrinter *InstructionPrinter(TheTarget->createMCInstPrinter( + Triple(TheTriple), AsmInfo->getAssemblerDialect(), *MAI, *MII, *MRI)); + if (!InstructionPrinter) + return createStringError(errc::invalid_argument, + "no target assembly language printer for target " + + TheTriple); + MIP.reset(InstructionPrinter); + InstructionPrinter->setPrintImmHex(true); + + return Error::success(); +} + +Expected> +LVBinaryReader::getSection(LVScope *Scope, LVAddress Address, + LVSectionIndex SectionIndex) { + // Return the 'text' section with the code for this logical scope. + // COFF: SectionIndex is zero. Use 'SectionAddresses' data. + // ELF: SectionIndex is the section index in the file. + if (SectionIndex) { + LVSections::iterator Iter = Sections.find(SectionIndex); + if (Iter == Sections.end()) { + std::string TheFilename(Scope->getName()); + return createStringError(errc::invalid_argument, + "invalid section index for: '%s'", + TheFilename.c_str()); + } + const object::SectionRef Section = Iter->second; + return std::make_pair(Section.getAddress(), Section); + } + + // Ensure a valid starting address for the public name's. + LVSectionAddresses::const_iterator Iter = + SectionAddresses.upper_bound(Address); + if (Iter == SectionAddresses.begin()) { + std::string TheFilename(Scope->getName()); + return createStringError(errc::invalid_argument, + "invalid section address for: '%s'", + TheFilename.c_str()); + } + + // Get section that contains the code for this function. + Iter = SectionAddresses.lower_bound(Address); + if (Iter != SectionAddresses.begin()) + --Iter; + return std::make_pair(Iter->first, Iter->second); +} + +void LVBinaryReader::addSectionRange(LVSectionIndex SectionIndex, + LVScope *Scope) { + LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); + ScopesWithRanges->addEntry(Scope); +} + +void LVBinaryReader::addSectionRange(LVSectionIndex SectionIndex, + LVScope *Scope, LVAddress LowerAddress, + LVAddress UpperAddress) { + LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); + ScopesWithRanges->addEntry(Scope, LowerAddress, UpperAddress); +} + +LVRange *LVBinaryReader::getSectionRanges(LVSectionIndex SectionIndex) { + LVRange *Range = nullptr; + // Check if we already have a mapping for this section index. + LVSectionRanges::iterator IterSection = SectionRanges.find(SectionIndex); + if (IterSection == SectionRanges.end()) { + Range = new LVRange(); + SectionRanges.emplace(SectionIndex, Range); + } else { + Range = IterSection->second; + } + assert(Range && "Range is null."); + return Range; +} + +Error LVBinaryReader::createInstructions(LVScope *Scope, + LVSectionIndex SectionIndex, + const LVNameInfo &Name) { + assert(Scope && "Scope is null."); + + // Skip stripped functions. + if (Scope->getIsDiscarded()) + return Error::success(); + + // Find associated scope for the given function entry point. + LVAddress Address = Name.first; + uint64_t Size = Name.second; + + LLVM_DEBUG({ + dbgs() << "\nPublic Name instructions: '" << Scope->getName() << "' / '" + << Scope->getLinkageName() << "'\n"; + dbgs() << "DIE Offset: " << hexValue(Scope->getOffset()) << " Range: [" + << hexValue(Address) << ":" << hexValue(Address + Size) << "]\n"; + }); + + Expected> SectionOrErr = + getSection(Scope, Address, SectionIndex); + if (!SectionOrErr) + return SectionOrErr.takeError(); + const object::SectionRef Section = (*SectionOrErr).second; + uint64_t SectionAddress = (*SectionOrErr).first; + + Expected SectionContentsOrErr = Section.getContents(); + if (!SectionContentsOrErr) + return SectionOrErr.takeError(); + + // There are cases where the section size is smaller that the [LowPC,HighPC] + // range; it causes to decode invalid addresses. The recorded size in the + // logical scope is one less that the real size. + LLVM_DEBUG({ + dbgs() << " Size: " << hexValue(Size) + << ", Section Size: " << hexValue(Section.getSize()) << "\n"; + }); + Size = std::min(Size + 1, Section.getSize()); + + ArrayRef Bytes = arrayRefFromStringRef(*SectionContentsOrErr); + uint64_t Offset = Address - SectionAddress; + uint8_t const *Begin = Bytes.data() + Offset; + uint8_t const *End = Bytes.data() + Offset + Size; + + LLVM_DEBUG({ + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + consumeError(SectionNameOrErr.takeError()); + else + dbgs() << "Section Index: " << hexValue(Section.getIndex()) << " [" + << hexValue((uint64_t)Section.getAddress()) << ":" + << hexValue((uint64_t)Section.getAddress() + Section.getSize(), 10) + << "] Name: '" << *SectionNameOrErr << "'\n" + << "Begin: " << hexValue((uint64_t)Begin) + << ", End: " << hexValue((uint64_t)End) << "\n"; + }); + + // Address for first instruction line. + LVAddress FirstAddress = Address; + LVLines Instructions; + + while (Begin < End) { + MCInst Instruction; + uint64_t BytesConsumed = 0; + SmallVector InsnStr; + raw_svector_ostream Annotations(InsnStr); + MCDisassembler::DecodeStatus const S = + MD->getInstruction(Instruction, BytesConsumed, + ArrayRef(Begin, End), Address, outs()); + switch (S) { + case MCDisassembler::Fail: + LLVM_DEBUG({ dbgs() << "Invalid instruction\n"; }); + if (BytesConsumed == 0) + // Skip invalid bytes + BytesConsumed = 1; + break; + case MCDisassembler::SoftFail: + LLVM_DEBUG({ dbgs() << "Potentially undefined instruction:"; }); + LLVM_FALLTHROUGH; + case MCDisassembler::Success: { + std::string Buffer; + raw_string_ostream Stream(Buffer); + StringRef AnnotationsStr = Annotations.str(); + MIP.get()->printInst(&Instruction, Address, AnnotationsStr, *STI, Stream); + LLVM_DEBUG({ + std::string BufferCodes; + raw_string_ostream StreamCodes(BufferCodes); + StreamCodes << format_bytes( + ArrayRef(Begin, Begin + BytesConsumed), None, 16, 16); + dbgs() << "[" << hexValue((uint64_t)Begin) << "] " + << "Size: " << format_decimal(BytesConsumed, 2) << " (" + << formatv("{0}", + fmt_align(StreamCodes.str(), AlignStyle::Left, 32)) + << ") " << hexValue((uint64_t)Address) << ": " << Stream.str() + << "\n"; + }); + // The 'processLines()' function will move each created logical line + // to its enclosing logical scope, using the debug ranges information + // and they will be released when its scope parent is deleted. + LVLineAssembler *Line = new LVLineAssembler(); + Line->setAddress(Address); + Line->setName(StringRef(Stream.str()).trim()); + Instructions.push_back(Line); + break; + } + } + Address += BytesConsumed; + Begin += BytesConsumed; + } + + LLVM_DEBUG({ + size_t Index = 0; + dbgs() << "\nAddress: " << hexValue(FirstAddress) + << format(" - Collected instructions lines: %d\n", + Instructions.size()); + for (const LVLine *Line : Instructions) + dbgs() << format_decimal(++Index, 5) << ": " + << hexValue(Line->getOffset()) << ", (" << Line->getName() + << ")\n"; + }); + + // The scope in the assembler names is linked to its own instructions. + ScopeInstructions.emplace(Scope, std::move(Instructions)); + AssemblerMappings.add(SectionIndex, FirstAddress, Scope); + + return Error::success(); +} + +Error LVBinaryReader::createInstructions(LVScope *Function, + LVSectionIndex SectionIndex) { + if (!options().getPrintInstructions()) + return Error::success(); + + LVNameInfo Name = CompileUnit->findPublicName(Function); + if (Name.first != LVAddress(UINT64_MAX)) + return createInstructions(Function, SectionIndex, Name); + + return Error::success(); +} + +Error LVBinaryReader::createInstructions() { + if (!options().getPrintInstructions()) + return Error::success(); + + // For each public name in the current compile unit, create the line + // records that represent the executable instructions. + for (LVPublicNames::const_reference Name : CompileUnit->getPublicNames()) { + LVScope *Scope = Name.first; + LVSectionIndex SectionIndex = getSymbolTableIndex(Scope->getLinkageName()); + if (Error Err = createInstructions(Scope, SectionIndex, Name.second)) + return Err; + } + + return Error::success(); +} + +// During the traversal of the debug information sections, we created the +// logical lines representing the disassembled instructions from the text +// section and the logical lines representing the line records from the +// debug line section. Using the ranges associated with the logical scopes, +// we will allocate those logical lines to their logical scopes. +void LVBinaryReader::processLines(LVLines *DebugLines, + LVSectionIndex SectionIndex, + LVScope *Function) { + assert(DebugLines && "DebugLines is null."); + + // Just return if this compilation unit does not have any line records + // and no instruction lines were created. + if (DebugLines->empty() && !options().getPrintInstructions()) + return; + + // Merge the debug lines and instruction lines using their text address; + // the logical line representing the debug line record is followed by the + // line(s) representing the disassembled instructions, whose addresses are + // equal or greater that the line address and less than the address of the + // next line record. + LLVM_DEBUG({ + size_t Index = 1; + size_t PerLine = 4; + dbgs() << format("\nProcess debug lines: %d\n", DebugLines->size()); + for (const LVLine *Line : *DebugLines) { + dbgs() << format_decimal(Index, 5) << ": " << hexValue(Line->getOffset()) + << ", (" << Line->getLineNumber() << ")"; + dbgs() << ((Index % PerLine) ? " " : "\n"); + ++Index; + } + dbgs() << ((Index % PerLine) ? "\n" : ""); + }); + + bool TraverseLines = true; + LVLines::iterator Iter = DebugLines->begin(); + while (TraverseLines && Iter != DebugLines->end()) { + uint64_t DebugAddress = (*Iter)->getAddress(); + + // Get the function with an entry point that matches this line and + // its associated assembler entries. In the case of COMDAT, the input + // 'Function' is not null. Use it to find its address ranges. + LVScope *Scope = Function; + if (!Function) { + Scope = AssemblerMappings.find(SectionIndex, DebugAddress); + if (!Scope) { + ++Iter; + continue; + } + } + + // Get the associated instructions for the found 'Scope'. + LVLines InstructionLines; + LVInstructions::iterator IterLines = ScopeInstructions.find(Scope); + if (IterLines != ScopeInstructions.end()) + InstructionLines = std::move(IterLines->second); + + LLVM_DEBUG({ + size_t Index = 0; + dbgs() << format("\nProcess instructions lines: %d\n", + InstructionLines.size()); + for (const LVLine *Line : InstructionLines) + dbgs() << format_decimal(++Index, 5) << ": " + << hexValue(Line->getOffset()) << ", (" << Line->getName() + << ")\n"; + }); + + // Continue with next debug line if there are not instructions lines. + if (InstructionLines.empty()) { + ++Iter; + continue; + } + + for (LVLine *InstructionLine : InstructionLines) { + uint64_t InstructionAddress = InstructionLine->getAddress(); + LLVM_DEBUG({ + dbgs() << "Instruction address: " << hexValue(InstructionAddress) + << "\n"; + }); + if (TraverseLines) { + while (Iter != DebugLines->end()) { + DebugAddress = (*Iter)->getAddress(); + LLVM_DEBUG({ + bool IsDebug = (*Iter)->getIsLineDebug(); + dbgs() << "Line " << (IsDebug ? "dbg:" : "ins:") << " [" + << hexValue(DebugAddress) << "]"; + if (IsDebug) + dbgs() << format(" %d", (*Iter)->getLineNumber()); + dbgs() << "\n"; + }); + // Instruction address before debug line. + if (InstructionAddress < DebugAddress) { + LLVM_DEBUG({ + dbgs() << "Inserted instruction address: " + << hexValue(InstructionAddress) << " before line: " + << format("%d", (*Iter)->getLineNumber()) << " [" + << hexValue(DebugAddress) << "]\n"; + }); + Iter = DebugLines->insert(Iter, InstructionLine); + // The returned iterator points to the inserted instruction. + // Skip it and point to the line acting as reference. + ++Iter; + break; + } + ++Iter; + } + if (Iter == DebugLines->end()) { + // We have reached the end of the source lines and the current + // instruction line address is greater than the last source line. + TraverseLines = false; + DebugLines->push_back(InstructionLine); + } + } else { + DebugLines->push_back(InstructionLine); + } + } + } + + LLVM_DEBUG({ + dbgs() << format("Lines after merge: %d\n", DebugLines->size()); + size_t Index = 0; + for (const LVLine *Line : *DebugLines) { + dbgs() << format_decimal(++Index, 5) << ": " + << hexValue(Line->getOffset()) << ", (" + << ((Line->getIsLineDebug()) + ? Line->lineNumberAsStringStripped(/*ShowZero=*/true) + : Line->getName()) + << ")\n"; + } + }); + + // This compilation unit does not have line records. Traverse its scopes + // and take any collected instruction lines as the working set in order + // to move them to their associated scope. + if (DebugLines->empty()) { + if (const LVScopes *Scopes = CompileUnit->getScopes()) + for (LVScope *Scope : *Scopes) { + LVInstructions::iterator IterLines = ScopeInstructions.find(Scope); + if (IterLines != ScopeInstructions.end()) + *DebugLines = std::move(IterLines->second); + } + } + + LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); + ScopesWithRanges->startSearch(); + + // Process collected lines. + LVScope *Scope; + for (LVLine *Line : *DebugLines) { + // Using the current line address, get its associated lexical scope and + // add the line information to it. + Scope = ScopesWithRanges->getEntry(Line->getAddress()); + if (!Scope) { + // If missing scope, use the compile unit. + Scope = CompileUnit; + LLVM_DEBUG({ + dbgs() << "Adding line to CU: " << hexValue(Line->getOffset()) << ", (" + << ((Line->getIsLineDebug()) + ? Line->lineNumberAsStringStripped(/*ShowZero=*/true) + : Line->getName()) + << ")\n"; + }); + } + + // Add line object to scope. + Scope->addElement(Line); + + // Report any line zero. + if (options().getWarningLines() && Line->getIsLineDebug() && + !Line->getLineNumber()) + CompileUnit->addLineZero(Line); + + // Some compilers generate ranges in the compile unit; other compilers + // only DW_AT_low_pc/DW_AT_high_pc. In order to correctly map global + // variables, we need to generate the map ranges for the compile unit. + // If we use the ranges stored at the scope level, there are cases where + // the address referenced by a symbol location, is not in the enclosing + // scope, but in an outer one. By using the ranges stored in the compile + // unit, we can catch all those addresses. + if (Line->getIsLineDebug()) + CompileUnit->addMapping(Line, SectionIndex); + + // Resolve any given pattern. + patterns().resolvePatternMatch(Line); + } + + ScopesWithRanges->endSearch(); +} + +void LVBinaryReader::processLines(LVLines *DebugLines, + LVSectionIndex SectionIndex) { + assert(DebugLines && "DebugLines is null."); + if (DebugLines->empty() && ScopeInstructions.empty()) + return; + + // If the Compile Unit does not contain comdat functions, use the whole + // set of debug lines, as the addresses don't have conflicts. + if (!CompileUnit->getHasComdatScopes()) { + processLines(DebugLines, SectionIndex, nullptr); + return; + } + + // Find the indexes for the lines whose address is zero. + std::vector AddressZero; + LVLines::iterator It = + std::find_if(std::begin(*DebugLines), std::end(*DebugLines), + [](LVLine *Line) { return !Line->getAddress(); }); + while (It != std::end(*DebugLines)) { + AddressZero.emplace_back(std::distance(std::begin(*DebugLines), It)); + It = std::find_if(std::next(It), std::end(*DebugLines), + [](LVLine *Line) { return !Line->getAddress(); }); + } + + // If the set of debug lines does not contain any line with address zero, + // use the whole set. It means we are dealing with an initialization + // section from a fully linked binary. + if (AddressZero.empty()) { + processLines(DebugLines, SectionIndex, nullptr); + return; + } + + // The Compile unit contains comdat functions. Traverse the collected + // debug lines and identify logical groups based on their start and + // address. Each group starts with a zero address. + // Begin, End, Address, IsDone. + using LVBucket = std::tuple; + std::vector Buckets; + + LVAddress Address; + size_t Begin = 0; + size_t End = 0; + size_t Index = 0; + for (Index = 0; Index < AddressZero.size() - 1; ++Index) { + Begin = AddressZero[Index]; + End = AddressZero[Index + 1] - 1; + Address = (*DebugLines)[End]->getAddress(); + Buckets.emplace_back(Begin, End, Address, false); + } + + // Add the last bucket. + if (Index) { + Begin = AddressZero[Index]; + End = DebugLines->size() - 1; + Address = (*DebugLines)[End]->getAddress(); + Buckets.emplace_back(Begin, End, Address, false); + } + + LLVM_DEBUG({ + dbgs() << "Debug Lines buckets: " << Buckets.size() << "\n"; + for (LVBucket &Bucket : Buckets) { + dbgs() << "Begin: " << format_decimal(std::get<0>(Bucket), 5) << ", " + << "End: " << format_decimal(std::get<1>(Bucket), 5) << ", " + << "Address: " << hexValue(std::get<2>(Bucket)) << "\n"; + } + }); + + // Traverse the sections and buckets looking for matches on the section + // sizes. In the unlikely event of different buckets with the same size + // process them in order and mark them as done. + LVLines Group; + for (LVSections::reference Entry : Sections) { + LVSectionIndex SectionIndex = Entry.first; + const object::SectionRef Section = Entry.second; + uint64_t Size = Section.getSize(); + LLVM_DEBUG({ + dbgs() << "\nSection Index: " << format_decimal(SectionIndex, 3) + << " , Section Size: " << hexValue(Section.getSize()) + << " , Section Address: " << hexValue(Section.getAddress()) + << "\n"; + }); + + for (LVBucket &Bucket : Buckets) { + if (std::get<3>(Bucket)) + // Already done for previous section. + continue; + if (Size == std::get<2>(Bucket)) { + // We have a match on the section size. + Group.clear(); + LVLines::iterator IterStart = DebugLines->begin() + std::get<0>(Bucket); + LVLines::iterator IterEnd = + DebugLines->begin() + std::get<1>(Bucket) + 1; + for (LVLines::iterator Iter = IterStart; Iter < IterEnd; ++Iter) + Group.push_back(*Iter); + processLines(&Group, SectionIndex, /*Function=*/nullptr); + std::get<3>(Bucket) = true; + break; + } + } + } +} + +// Traverse the scopes for the given 'Function' looking for any inlined +// scopes with inlined lines, which are recorded in 'CUInlineeLines'. +void LVBinaryReader::includeInlineeLines(LVSectionIndex SectionIndex, + LVScope *Function) { + SmallVector InlineeIters; + std::function FindInlinedScopes = + [&](LVScope *Parent) { + if (const LVScopes *Scopes = Parent->getScopes()) + for (LVScope *Scope : *Scopes) { + LVInlineeLine::iterator Iter = CUInlineeLines.find(Scope); + if (Iter != CUInlineeLines.end()) + InlineeIters.push_back(Iter); + FindInlinedScopes(Scope); + } + }; + + // Find all inlined scopes for the given 'Function'. + FindInlinedScopes(Function); + for (LVInlineeLine::iterator InlineeIter : InlineeIters) { + LVScope *Scope = InlineeIter->first; + addToSymbolTable(Scope->getLinkageName(), Scope, SectionIndex); + + LVLines *InlineeLines = InlineeIter->second; + LLVM_DEBUG({ + dbgs() << "Inlined lines for: " << Scope->getName() << "\n"; + for (const LVLine *Line : *InlineeLines) + dbgs() << "[" << hexValue(Line->getAddress()) << "] " + << Line->getLineNumber() << "\n"; + dbgs() << format("Debug lines: %d\n", CULines.size()); + for (const LVLine *Line : CULines) + dbgs() << "Line address: " << hexValue(Line->getOffset()) << ", (" + << Line->getLineNumber() << ")\n"; + ; + }); + + // The inlined lines must be merged using its address, in order to keep + // the real order of the instructions. The inlined lines are mixed with + // the other non-inlined lines. + if (InlineeLines->size()) { + // First address of inlinee code. + uint64_t InlineeStart = (InlineeLines->front())->getAddress(); + LVLines::iterator Iter = std::find_if( + CULines.begin(), CULines.end(), [&](LVLine *Item) -> bool { + return Item->getAddress() == InlineeStart; + }); + if (Iter != CULines.end()) { + // 'Iter' points to the line where the inlined function is called. + // Emulate the DW_AT_call_line attribute. + Scope->setCallLineNumber((*Iter)->getLineNumber()); + // Mark the referenced line as the start of the inlined function. + // Skip the first line during the insertion, as the address and + // line number as the same. Otherwise we have to erase and insert. + (*Iter)->setLineNumber((*InlineeLines->begin())->getLineNumber()); + ++Iter; + CULines.insert(Iter, InlineeLines->begin() + 1, InlineeLines->end()); + } + } + + // Remove this set of lines from the container; each inlined function + // creates an unique set of lines. Remove only the created container. + CUInlineeLines.erase(InlineeIter); + InlineeLines->clear(); + delete InlineeLines; + } + LLVM_DEBUG({ + dbgs() << "Merged Inlined lines for: " << Function->getName() << "\n"; + dbgs() << format("Debug lines: %d\n", CULines.size()); + for (const LVLine *Line : CULines) + dbgs() << "Line address: " << hexValue(Line->getOffset()) << ", (" + << Line->getLineNumber() << ")\n"; + ; + }); +} + +void LVBinaryReader::print(raw_ostream &OS) const { + OS << "LVBinaryReader\n"; + LLVM_DEBUG(dbgs() << "PrintReader\n"); +} Index: llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp @@ -0,0 +1,1233 @@ +//===-- LVCodeViewReader.cpp ----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVCodeViewReader class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h" +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/DebugInfo/PDB/GenericError.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/LinePrinter.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::logicalview; +using namespace llvm::msf; +using namespace llvm::object; +using namespace llvm::pdb; + +#define DEBUG_TYPE "CodeViewReader" + +//===----------------------------------------------------------------------===// +// CodeView Reader. +//===----------------------------------------------------------------------===// +// Use the 'PointerToRawData' as base for the unique offset for the +// Symbol records. Using 'RecordOffset' does not give unique values +// as that offset is relative to each subsection. +uint32_t PointerToRawData = 0; +#define ABSOLUTE_OFFSET(offset) (PointerToRawData + offset) + +std::string LVCodeViewReader::getSymbolKindName(SymbolKind Kind) { + switch (Kind) { +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + case EnumName: \ + return #EnumName; +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" + default: + return "UnknownSym"; + } +} + +std::string LVCodeViewReader::formatRegisterId(RegisterId Register, + CPUType CPU) { +#define RETURN_CASE(Enum, X, Ret) \ + case Enum::X: \ + return Ret; + + if (CPU == CPUType::ARMNT) { + switch (Register) { +#define CV_REGISTERS_ARM +#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name) +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_ARM + + default: + break; + } + } else if (CPU == CPUType::ARM64) { + switch (Register) { +#define CV_REGISTERS_ARM64 +#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name) +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_ARM64 + + default: + break; + } + } else { + switch (Register) { +#define CV_REGISTERS_X86 +#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name) +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_X86 + + default: + break; + } + } + return "formatUnknownEnum(Id)"; +} + +void LVCodeViewReader::printRelocatedField(StringRef Label, + const coff_section *CoffSection, + uint32_t RelocOffset, + uint32_t Offset, + StringRef *RelocSym) { + StringRef SymStorage; + StringRef &Symbol = RelocSym ? *RelocSym : SymStorage; + if (!resolveSymbolName(CoffSection, RelocOffset, Symbol)) + W.printSymbolOffset(Label, Symbol, Offset); + else + W.printHex(Label, RelocOffset); +} + +void LVCodeViewReader::getLinkageName(const coff_section *CoffSection, + uint32_t RelocOffset, uint32_t Offset, + StringRef *RelocSym) { + StringRef SymStorage; + StringRef &Symbol = RelocSym ? *RelocSym : SymStorage; + if (resolveSymbolName(CoffSection, RelocOffset, Symbol)) + *RelocSym = ""; +} + +Expected +LVCodeViewReader::getFileNameForFileOffset(uint32_t FileOffset, + const SymbolGroup *SG) { + if (SG) { + Expected Filename = SG->getNameFromChecksums(FileOffset); + if (!Filename) { + consumeError(Filename.takeError()); + return StringRef(""); + } + return *Filename; + } + + // The file checksum subsection should precede all references to it. + if (!CVFileChecksumTable.valid() || !CVStringTable.valid()) + return createStringError(object_error::parse_failed, getFileName()); + + VarStreamArray::Iterator Iter = + CVFileChecksumTable.getArray().at(FileOffset); + + // Check if the file checksum table offset is valid. + if (Iter == CVFileChecksumTable.end()) + return createStringError(object_error::parse_failed, getFileName()); + + Expected NameOrErr = CVStringTable.getString(Iter->FileNameOffset); + if (!NameOrErr) + return createStringError(object_error::parse_failed, getFileName()); + return *NameOrErr; +} + +Error LVCodeViewReader::printFileNameForOffset(StringRef Label, + uint32_t FileOffset, + const SymbolGroup *SG) { + Expected NameOrErr = getFileNameForFileOffset(FileOffset, SG); + if (!NameOrErr) + return NameOrErr.takeError(); + W.printHex(Label, *NameOrErr, FileOffset); + return Error::success(); +} + +void LVCodeViewReader::cacheRelocations() { + for (const SectionRef &Section : getObj().sections()) { + const coff_section *CoffSection = getObj().getCOFFSection(Section); + + for (const RelocationRef &Relocacion : Section.relocations()) + RelocMap[CoffSection].push_back(Relocacion); + + // Sort relocations by address. + llvm::sort(RelocMap[CoffSection], [](RelocationRef L, RelocationRef R) { + return L.getOffset() < R.getOffset(); + }); + } +} + +// Given a section and an offset into this section the function returns the +// symbol used for the relocation at the offset. +Error LVCodeViewReader::resolveSymbol(const coff_section *CoffSection, + uint64_t Offset, SymbolRef &Sym) { + const auto &Relocations = RelocMap[CoffSection]; + basic_symbol_iterator SymI = getObj().symbol_end(); + for (const RelocationRef &Relocation : Relocations) { + uint64_t RelocationOffset = Relocation.getOffset(); + + if (RelocationOffset == Offset) { + SymI = Relocation.getSymbol(); + break; + } + } + if (SymI == getObj().symbol_end()) + return make_error("Unknown Symbol", inconvertibleErrorCode()); + Sym = *SymI; + return ErrorSuccess(); +} + +// Given a section and an offset into this section the function returns the +// name of the symbol used for the relocation at the offset. +Error LVCodeViewReader::resolveSymbolName(const coff_section *CoffSection, + uint64_t Offset, StringRef &Name) { + SymbolRef Symbol; + if (Error E = resolveSymbol(CoffSection, Offset, Symbol)) + return E; + Expected NameOrErr = Symbol.getName(); + if (!NameOrErr) + return NameOrErr.takeError(); + Name = *NameOrErr; + return ErrorSuccess(); +} + +// CodeView and DWARF can have references to compiler generated elements, +// used for initialization. The MSVC includes in the PDBs, internal compile +// units, associated with the MS runtime support. We mark them as 'system' +// and they are printed only if the command line option 'internal=system'. +bool LVCodeViewReader::isSystemEntry(LVElement *Element, StringRef Name) const { + Name = Name.empty() ? Element->getName() : Name; + auto Find = [=](const char *String) -> bool { + return StringRef::npos != Name.find(String); + }; + auto Starts = [=](const char *Pattern) -> bool { + return Name.startswith(Pattern); + }; + auto CheckExclude = [&]() -> bool { + if (Starts("__") || Starts("_PMD") || Starts("_PMFN")) + return true; + if (Find("_s__")) + return true; + if (Find("_CatchableType") || Find("_TypeDescriptor")) + return true; + if (Find("Intermediate\\vctools")) + return true; + if (Find("$initializer$") || Find("dynamic initializer")) + return true; + if (Find("`vftable'") || Find("_GLOBAL__sub")) + return true; + return false; + }; + bool Excluded = CheckExclude(); + if (Excluded) + Element->setIsSystem(); + + return Excluded; +} + +Error LVCodeViewReader::collectInlineeInfo( + DebugInlineeLinesSubsectionRef &Lines, const llvm::pdb::SymbolGroup *SG) { + for (const InlineeSourceLine &Line : Lines) { + TypeIndex TIInlinee = Line.Header->Inlinee; + uint32_t LineNumber = Line.Header->SourceLineNum; + uint32_t FileOffset = Line.Header->FileID; + LLVM_DEBUG({ + DictScope S(W, "InlineeSourceLine"); + LogicalVisitor.printTypeIndex("Inlinee", TIInlinee, StreamTPI); + if (Error Err = printFileNameForOffset("FileID", FileOffset, SG)) + return Err; + W.printNumber("SourceLineNum", LineNumber); + + if (Lines.hasExtraFiles()) { + W.printNumber("ExtraFileCount", Line.ExtraFiles.size()); + ListScope ExtraFiles(W, "ExtraFiles"); + for (const ulittle32_t &FID : Line.ExtraFiles) + if (Error Err = printFileNameForOffset("FileID", FID, SG)) + return Err; + } + }); + Expected NameOrErr = getFileNameForFileOffset(FileOffset, SG); + if (!NameOrErr) + return NameOrErr.takeError(); + LogicalVisitor.addInlineeInfo(TIInlinee, LineNumber, *NameOrErr); + } + + return Error::success(); +} + +Error LVCodeViewReader::traverseInlineeLines(StringRef Subsection) { + BinaryStreamReader SR(Subsection, llvm::support::little); + DebugInlineeLinesSubsectionRef Lines; + if (Error E = Lines.initialize(SR)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + return collectInlineeInfo(Lines); +} + +Error LVCodeViewReader::createLines( + const FixedStreamArray &LineNumbers, LVAddress Addendum, + uint32_t Segment, uint32_t Begin, uint32_t Size, uint32_t NameIndex, + const SymbolGroup *SG) { + LLVM_DEBUG({ + uint32_t End = Begin + Size; + W.getOStream() << formatv("{0:x-4}:{1:x-8}-{2:x-8}\n", Segment, Begin, End); + }); + + for (const LineNumberEntry &Line : LineNumbers) { + if (Line.Offset >= Size) + return createStringError(object_error::parse_failed, getFileName()); + + LineInfo LI(Line.Flags); + + LLVM_DEBUG({ + W.getOStream() << formatv( + "{0} {1:x-8}\n", utostr(LI.getStartLine()), + fmt_align(Begin + Line.Offset, AlignStyle::Right, 8, '0')); + }); + + // The 'processLines()' function will move each created logical line + // to its enclosing logical scope, using the debug ranges information + // and they will be released when its scope parent is deleted. + LVLineDebug *LineDebug = new LVLineDebug(); + CULines.push_back(LineDebug); + LVAddress Address = linearAddress(Segment, Begin + Line.Offset); + LineDebug->setAddress(Address + Addendum); + + if (LI.isAlwaysStepInto()) + LineDebug->setIsAlwaysStepInto(); + else if (LI.isNeverStepInto()) + LineDebug->setIsNeverStepInto(); + else + LineDebug->setLineNumber(LI.getStartLine()); + + if (LI.isStatement()) + LineDebug->setIsNewStatement(); + + Expected NameOrErr = getFileNameForFileOffset(NameIndex, SG); + if (!NameOrErr) + return NameOrErr.takeError(); + LineDebug->setFilename(*NameOrErr); + } + + return Error::success(); +} + +Error LVCodeViewReader::initializeFileAndStringTables( + BinaryStreamReader &Reader) { + while (Reader.bytesRemaining() > 0 && + (!CVFileChecksumTable.valid() || !CVStringTable.valid())) { + // The section consists of a number of subsection in the following format: + // |SubSectionType|SubSectionSize|Contents...| + uint32_t SubType, SubSectionSize; + + if (Error E = Reader.readInteger(SubType)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + if (Error E = Reader.readInteger(SubSectionSize)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + StringRef Contents; + if (Error E = Reader.readFixedString(Contents, SubSectionSize)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + BinaryStreamRef ST(Contents, support::little); + switch (DebugSubsectionKind(SubType)) { + case DebugSubsectionKind::FileChecksums: + if (Error E = CVFileChecksumTable.initialize(ST)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + break; + case DebugSubsectionKind::StringTable: + if (Error E = CVStringTable.initialize(ST)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + break; + default: + break; + } + + uint32_t PaddedSize = alignTo(SubSectionSize, 4); + if (Error E = Reader.skip(PaddedSize - SubSectionSize)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + } + + return Error::success(); +} + +Error LVCodeViewReader::loadTypeServer(TypeServer2Record &TS) { + LLVM_DEBUG({ + W.printString("Guid", formatv("{0}", TS.getGuid()).str()); + W.printNumber("Age", TS.getAge()); + W.printString("Name", TS.getName()); + }); + + SmallString<128> ServerName(TS.getName()); + BuffOrErr = MemoryBuffer::getFile(ServerName); + if (BuffOrErr.getError()) { + // The server name does not exist. Try in the directory as the input file. + ServerName = createAlternativePath(InputFilename, ServerName); + BuffOrErr = MemoryBuffer::getFile(ServerName); + if (BuffOrErr.getError()) { + // For the error message, use the original type server name. + std::string TheFilename(TS.getName()); + return createStringError(errc::bad_file_descriptor, + "File '%s' does not exist.", + TheFilename.c_str()); + } + } + MemBuffer = std::move(BuffOrErr.get()); + + // Check if the buffer corresponds to a PDB file. + assert(identify_magic((*MemBuffer).getBuffer()) == file_magic::pdb && + "Invalid PDB file."); + + if (Error Err = loadDataForPDB(PDB_ReaderType::Native, ServerName, Session)) { + std::string TheFilename(ServerName); + return createStringError(errorToErrorCode(std::move(Err)), "%s", + TheFilename.c_str()); + } + + PdbSession.reset(static_cast(Session.release())); + PDBFile &Pdb = PdbSession->getPDBFile(); + + // Just because a file with a matching name was found and it was an actual + // PDB file doesn't mean it matches. For it to match the InfoStream's GUID + // must match the GUID specified in the TypeServer2 record. + Expected expectedInfo = Pdb.getPDBInfoStream(); + if (expectedInfo && expectedInfo->getGuid() != TS.getGuid()) + return createStringError(errc::invalid_argument, "signature_out_of_date"); + + // The reader needs to switch to a type server, to process the types from + // the server. We need to keep the original input source, as reading other + // sections wil require the input associated with the loaded object file. + TypeServer = std::make_shared(&Pdb); + LogicalVisitor.setInput(TypeServer); + + LazyRandomTypeCollection &Types = types(); + LazyRandomTypeCollection &Ids = ids(); + if (Error Err = traverseTypes(Pdb, Types, Ids)) + return Err; + + return Error::success(); +} + +Error LVCodeViewReader::loadPrecompiledObject(PrecompRecord &Precomp, + CVTypeArray &CVTypesObj) { + LLVM_DEBUG({ + W.printHex("Count", Precomp.getTypesCount()); + W.printHex("Signature", Precomp.getSignature()); + W.printString("PrecompFile", Precomp.getPrecompFilePath()); + }); + + SmallString<128> ServerName(Precomp.getPrecompFilePath()); + BuffOrErr = MemoryBuffer::getFile(ServerName); + if (BuffOrErr.getError()) { + // The server name does not exist. Try in the directory as the input file. + ServerName = createAlternativePath(InputFilename, ServerName); + if (BuffOrErr.getError()) { + // For the error message, use the original type server name. + std::string TheFilename(Precomp.getPrecompFilePath()); + return createStringError(errc::bad_file_descriptor, + "File '%s' does not exist.", + TheFilename.c_str()); + } + } + MemBuffer = std::move(BuffOrErr.get()); + + Expected> BinOrErr = createBinary(*MemBuffer); + if (errorToErrorCode(BinOrErr.takeError())) { + std::string TheFilename(ServerName); + return createStringError(errc::not_supported, + "Binary object format in '%s' is not supported.", + TheFilename.c_str()); + } + + Binary &BinaryObj = *BinOrErr.get(); + if (!BinaryObj.isCOFF()) { + std::string TheFilename(ServerName); + return createStringError(errc::not_supported, "'%s' is not a COFF object.", + TheFilename.c_str()); + } + + Builder = std::make_unique(BuilderAllocator); + + // The MSVC precompiled header object file, should contain just a single + // ".debug$P" section. + COFFObjectFile &Obj = *cast(&BinaryObj); + for (const SectionRef &Section : Obj.sections()) { + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + return SectionNameOrErr.takeError(); + if (*SectionNameOrErr == ".debug$P") { + Expected DataOrErr = Section.getContents(); + if (!DataOrErr) + return DataOrErr.takeError(); + uint32_t Magic; + if (Error Err = consume(*DataOrErr, Magic)) + return Err; + if (Magic != COFF::DEBUG_SECTION_MAGIC) + return errorCodeToError(object_error::parse_failed); + + ReaderPrecomp = + std::make_unique(*DataOrErr, support::little); + cantFail( + ReaderPrecomp->readArray(CVTypesPrecomp, ReaderPrecomp->getLength())); + + // Append all the type records up to the LF_ENDPRECOMP marker and + // check if the signatures match. + for (const CVType &Type : CVTypesPrecomp) { + ArrayRef TypeData = Type.data(); + if (Type.kind() == LF_ENDPRECOMP) { + EndPrecompRecord EndPrecomp = cantFail( + TypeDeserializer::deserializeAs(TypeData)); + if (Precomp.getSignature() != EndPrecomp.getSignature()) + return createStringError(errc::invalid_argument, "no matching pch"); + break; + } + Builder->insertRecordBytes(TypeData); + } + break; + } + } + + // Append all the type records, skipping the first record which is the + // reference to the precompiled header object information. + for (const CVType &Type : CVTypesObj) { + ArrayRef TypeData = Type.data(); + if (Type.kind() != LF_PRECOMP) + Builder->insertRecordBytes(TypeData); + } + + // Set up a type stream that refers to the added type records. + Builder->ForEachRecord( + [&](TypeIndex TI, const CVType &Type) { TypeArray.push_back(Type); }); + + ItemStream = + std::make_unique>(llvm::support::little); + ItemStream->setItems(TypeArray); + TypeStream.setUnderlyingStream(*ItemStream); + + PrecompHeader = + std::make_shared(TypeStream, TypeArray.size()); + + // Change the original input source to use the collected type records. + LogicalVisitor.setInput(PrecompHeader); + + LazyRandomTypeCollection &Types = types(); + LazyRandomTypeCollection &Ids = ids(); + LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamTPI, + LogicalVisitor.getShared()); + return visitTypeStream(Types, TDV); +} + +Error LVCodeViewReader::traverseTypeSection(StringRef SectionName, + const SectionRef &Section) { + LLVM_DEBUG({ + ListScope D(W, "CodeViewTypes"); + W.printNumber("Section", SectionName, getObj().getSectionID(Section)); + }); + + Expected DataOrErr = Section.getContents(); + if (!DataOrErr) + return DataOrErr.takeError(); + uint32_t Magic; + if (Error Err = consume(*DataOrErr, Magic)) + return Err; + if (Magic != COFF::DEBUG_SECTION_MAGIC) + return errorCodeToError(object_error::parse_failed); + + // Get the first type record. It will indicate if this object uses a type + // server (/Zi) or a PCH file (/Yu). + CVTypeArray CVTypes; + BinaryStreamReader Reader(*DataOrErr, support::little); + cantFail(Reader.readArray(CVTypes, Reader.getLength())); + CVTypeArray::Iterator FirstType = CVTypes.begin(); + + // The object was compiled with /Zi. It uses types from a type server PDB. + if (FirstType->kind() == LF_TYPESERVER2) { + TypeServer2Record TS = cantFail( + TypeDeserializer::deserializeAs(FirstType->data())); + return loadTypeServer(TS); + } + + // The object was compiled with /Yc or /Yu. It uses types from another + // object file with a matching signature. + if (FirstType->kind() == LF_PRECOMP) { + PrecompRecord Precomp = cantFail( + TypeDeserializer::deserializeAs(FirstType->data())); + return loadPrecompiledObject(Precomp, CVTypes); + } + + LazyRandomTypeCollection &Types = types(); + LazyRandomTypeCollection &Ids = ids(); + Types.reset(*DataOrErr, 100); + LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamTPI, + LogicalVisitor.getShared()); + return visitTypeStream(Types, TDV); +} + +Error LVCodeViewReader::traverseTypes(PDBFile &Pdb, + LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids) { + // Traverse types (TPI and IPI). + auto VisitTypes = [&](LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids, + SpecialStream StreamIdx) -> Error { + LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamIdx, + LogicalVisitor.getShared()); + return visitTypeStream(Types, TDV); + }; + + Expected StreamTpiOrErr = Pdb.getPDBTpiStream(); + if (!StreamTpiOrErr) + return StreamTpiOrErr.takeError(); + TpiStream &StreamTpi = *StreamTpiOrErr; + StreamTpi.buildHashMap(); + LLVM_DEBUG({ + W.getOStream() << formatv("Showing {0:N} TPI records\n", + StreamTpi.getNumTypeRecords()); + }); + if (Error Err = VisitTypes(Types, Ids, StreamTPI)) + return Err; + + Expected StreamIpiOrErr = Pdb.getPDBIpiStream(); + if (!StreamIpiOrErr) + return StreamIpiOrErr.takeError(); + TpiStream &StreamIpi = *StreamIpiOrErr; + StreamIpi.buildHashMap(); + LLVM_DEBUG({ + W.getOStream() << formatv("Showing {0:N} IPI records\n", + StreamIpi.getNumTypeRecords()); + }); + return VisitTypes(Ids, Ids, StreamIPI); +} + +Error LVCodeViewReader::traverseSymbolsSubsection(StringRef Subsection, + const SectionRef &Section, + StringRef SectionContents) { + ArrayRef BinaryData(Subsection.bytes_begin(), + Subsection.bytes_end()); + LVSymbolVisitorDelegate VisitorDelegate(this, Section, &getObj(), + SectionContents); + CVSymbolArray Symbols; + BinaryStreamReader Reader(BinaryData, llvm::support::little); + if (Error E = Reader.readArray(Symbols, Reader.getLength())) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + LazyRandomTypeCollection &Types = types(); + LazyRandomTypeCollection &Ids = ids(); + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(&VisitorDelegate, + CodeViewContainer::ObjectFile); + // As we are processing a COFF format, use TPI as IPI, so the generic code + // to process the CodeView format does not contain any additional checks. + LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, + &VisitorDelegate, LogicalVisitor.getShared()); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Traverser); + CVSymbolVisitor Visitor(Pipeline); + return Visitor.visitSymbolStream(Symbols); +} + +Error LVCodeViewReader::traverseSymbolSection(StringRef SectionName, + const SectionRef &Section) { + LLVM_DEBUG({ + ListScope D(W, "CodeViewDebugInfo"); + W.printNumber("Section", SectionName, getObj().getSectionID(Section)); + }); + + PointerToRawData = getObj().getCOFFSection(Section)->PointerToRawData; + + Expected SectionOrErr = Section.getContents(); + if (!SectionOrErr) + return SectionOrErr.takeError(); + StringRef SectionContents = *SectionOrErr; + StringRef Data = SectionContents; + + SmallVector SymbolNames; + StringMap FunctionLineTables; + + uint32_t Magic; + if (Error E = consume(Data, Magic)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + if (Magic != COFF::DEBUG_SECTION_MAGIC) + return createStringError(object_error::parse_failed, getFileName()); + + BinaryStreamReader FSReader(Data, support::little); + if (Error Err = initializeFileAndStringTables(FSReader)) + return Err; + + while (!Data.empty()) { + // The section consists of a number of subsection in the following format: + // |SubSectionType|SubSectionSize|Contents...| + uint32_t SubType, SubSectionSize; + if (Error E = consume(Data, SubType)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + if (Error E = consume(Data, SubSectionSize)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + if (SubType & SubsectionIgnoreFlag) + SubType &= ~SubsectionIgnoreFlag; + + // Get the contents of the subsection. + if (SubSectionSize > Data.size()) + return createStringError(object_error::parse_failed, getFileName()); + StringRef Contents = Data.substr(0, SubSectionSize); + + // Add SubSectionSize to the current offset and align that offset + // to find the next subsection. + size_t SectionOffset = Data.data() - SectionContents.data(); + size_t NextOffset = SectionOffset + SubSectionSize; + NextOffset = alignTo(NextOffset, 4); + if (NextOffset > SectionContents.size()) + return createStringError(object_error::parse_failed, getFileName()); + Data = SectionContents.drop_front(NextOffset); + + switch (DebugSubsectionKind(SubType)) { + case DebugSubsectionKind::Symbols: + if (Error Err = + traverseSymbolsSubsection(Contents, Section, SectionContents)) + return Err; + break; + + case DebugSubsectionKind::InlineeLines: + if (Error Err = traverseInlineeLines(Contents)) + return Err; + break; + + case DebugSubsectionKind::Lines: + // Holds a PC to file:line table. Some data to parse this subsection + // is stored in the other subsections, so just check sanity and store + // the pointers for deferred processing. + + // Collect function and ranges only if we need to print logical lines. + if (options().getGeneralCollectRanges()) { + + if (SubSectionSize < 12) { + // There should be at least three words to store two function + // relocations and size of the code. + return createStringError(object_error::parse_failed, getFileName()); + } + + StringRef SymbolName; + if (Error Err = resolveSymbolName(getObj().getCOFFSection(Section), + SectionOffset, SymbolName)) + return createStringError(errorToErrorCode(std::move(Err)), + getFileName()); + + LLVM_DEBUG({ W.printString("Symbol Name", SymbolName); }); + if (FunctionLineTables.count(SymbolName) != 0) { + // Saw debug info for this function already? + return createStringError(object_error::parse_failed, getFileName()); + } + + FunctionLineTables[SymbolName] = Contents; + SymbolNames.push_back(SymbolName); + } + break; + + // Do nothing for unrecognized subsections. + default: + break; + } + W.flush(); + } + + // Traverse the line tables now that we've read all the subsections and + // know all the required information. + for (StringRef SymbolName : SymbolNames) { + LLVM_DEBUG({ + ListScope S(W, "FunctionLineTable"); + W.printString("Symbol Name", SymbolName); + }); + + BinaryStreamReader Reader(FunctionLineTables[SymbolName], support::little); + + DebugLinesSubsectionRef Lines; + if (Error E = Lines.initialize(Reader)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + // Find the associate symbol table information. + LVSymbolTableEntry SymbolTableEntry = getSymbolTableEntry(SymbolName); + LVScope *Function = SymbolTableEntry.Scope; + if (!Function) + continue; + + LVAddress Addendum = SymbolTableEntry.Address; + LVSectionIndex SectionIndex = SymbolTableEntry.SectionIndex; + + // The given scope represents the function that contains the line numbers. + // Collect all generated debug lines associated with the function. + CULines.clear(); + + // For the given scope, collect all scopes ranges. + LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); + ScopesWithRanges->clear(); + Function->getRanges(*ScopesWithRanges); + ScopesWithRanges->sort(); + + uint16_t Segment = Lines.header()->RelocSegment; + uint32_t Begin = Lines.header()->RelocOffset; + uint32_t Size = Lines.header()->CodeSize; + for (const LineColumnEntry &Block : Lines) + if (Error Err = createLines(Block.LineNumbers, Addendum, Segment, Begin, + Size, Block.NameIndex)) + return Err; + + // Include lines from any inlined functions within the current function. + includeInlineeLines(SectionIndex, Function); + + if (Error Err = createInstructions(Function, SectionIndex)) + return Err; + + processLines(&CULines, SectionIndex, Function); + } + + return Error::success(); +} + +void LVCodeViewReader::sortScopes() { Root->sort(); } + +void LVCodeViewReader::print(raw_ostream &OS) const { + LLVM_DEBUG(dbgs() << "CreateReaders\n"); +} + +void LVCodeViewReader::mapRangeAddress(const ObjectFile &Obj, + const SectionRef &Section, + bool IsComdat) { + if (!Obj.isCOFF()) + return; + + const COFFObjectFile *Object = cast(&Obj); + + for (const SymbolRef &Sym : Object->symbols()) { + if (!Section.containsSymbol(Sym)) + continue; + + COFFSymbolRef Symbol = Object->getCOFFSymbol(Sym); + if (Symbol.getComplexType() != llvm::COFF::IMAGE_SYM_DTYPE_FUNCTION) + continue; + + StringRef SymbolName; + Expected SymNameOrErr = Object->getSymbolName(Symbol); + if (!SymNameOrErr) { + W.startLine() << "Invalid symbol name: " << Symbol.getSectionNumber() + << "\n"; + consumeError(SymNameOrErr.takeError()); + continue; + } + SymbolName = *SymNameOrErr; + + LLVM_DEBUG({ + Expected SectionOrErr = + Object->getSection(Symbol.getSectionNumber()); + if (!SectionOrErr) { + W.startLine() << "Invalid section number: " << Symbol.getSectionNumber() + << "\n"; + consumeError(SectionOrErr.takeError()); + return; + } + W.printNumber("Section #", Symbol.getSectionNumber()); + W.printString("Name", SymbolName); + W.printHex("Value", Symbol.getValue()); + }); + + // Record the symbol name (linkage) and its loading address. + addToSymbolTable(SymbolName, Symbol.getValue(), Symbol.getSectionNumber(), + IsComdat); + } +} + +Error LVCodeViewReader::createScopes(COFFObjectFile &Obj) { + if (Error Err = loadTargetInfo(Obj)) + return Err; + + // Initialization required when processing a COFF file: + // Cache the symbols relocations. + // Create a mapping for virtual addresses. + // Get the functions entry points. + cacheRelocations(); + mapVirtualAddress(Obj); + + for (const SectionRef &Section : Obj.sections()) { + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + return SectionNameOrErr.takeError(); + // .debug$T is a standard CodeView type section, while .debug$P is the + // same format but used for MSVC precompiled header object files. + if (*SectionNameOrErr == ".debug$T" || *SectionNameOrErr == ".debug$P") + if (Error Err = traverseTypeSection(*SectionNameOrErr, Section)) + return Err; + } + + // Process collected namespaces. + LogicalVisitor.processNamespaces(); + + for (const SectionRef &Section : Obj.sections()) { + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + return SectionNameOrErr.takeError(); + if (*SectionNameOrErr == ".debug$S") + if (Error Err = traverseSymbolSection(*SectionNameOrErr, Section)) + return Err; + } + + // Check if we have to close the Compile Unit scope. + LogicalVisitor.closeScope(); + + // Traverse the strings recorded and transform them into filenames. + LogicalVisitor.processFiles(); + + // Process collected element lines. + LogicalVisitor.processLines(); + + // Translate composite names into a single component. + Root->transformScopedName(); + return Error::success(); +} + +Error LVCodeViewReader::createScopes(PDBFile &Pdb) { + if (Error Err = loadTargetInfo(Pdb)) + return Err; + + if (!Pdb.hasPDBTpiStream() || !Pdb.hasPDBDbiStream()) + return Error::success(); + + // Open the executable associated with the PDB file and get the section + // addresses used to calculate linear addresses for CodeView Symbols. + if (!ExePath.empty()) { + ErrorOr> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(ExePath); + if (BuffOrErr.getError()) { + std::string TheFilename(ExePath); + return createStringError(errc::bad_file_descriptor, + "File '%s' does not exist.", + TheFilename.c_str()); + } + BinaryBuffer = std::move(BuffOrErr.get()); + + // Check if the buffer corresponds to a PECOFF executable. + assert(identify_magic(BinaryBuffer->getBuffer()) == + file_magic::pecoff_executable && + "Invalid PECOFF executable file."); + + Expected> BinOrErr = + createBinary(BinaryBuffer->getMemBufferRef()); + if (errorToErrorCode(BinOrErr.takeError())) { + std::string TheFilename(ExePath); + return createStringError(errc::not_supported, + "Binary object format in '%s' is not supported.", + TheFilename.c_str()); + } + BinaryExecutable = std::move(*BinOrErr); + if (COFFObjectFile *COFFObject = + dyn_cast(BinaryExecutable.get())) + mapVirtualAddress(*COFFObject); + } + + // In order to generate a full logical view, we have to traverse both + // streams TPI and IPI if they are present. The following table gives + // the stream where a specified type is located. If the IPI stream is + // not present, all the types are located in the TPI stream. + // + // TPI Stream: + // LF_POINTER LF_MODIFIER LF_PROCEDURE LF_MFUNCTION + // LF_LABEL LF_ARGLIST LF_FIELDLIST LF_ARRAY + // LF_CLASS LF_STRUCTURE LF_INTERFACE LF_UNION + // LF_ENUM LF_TYPESERVER2 LF_VFTABLE LF_VTSHAPE + // LF_BITFIELD LF_METHODLIST LF_PRECOMP LF_ENDPRECOMP + // + // IPI stream: + // LF_FUNC_ID LF_MFUNC_ID LF_BUILDINFO + // LF_SUBSTR_LIST LF_STRING_ID LF_UDT_SRC_LINE + // LF_UDT_MOD_SRC_LINE + + LazyRandomTypeCollection &Types = types(); + LazyRandomTypeCollection &Ids = ids(); + if (Error Err = traverseTypes(Pdb, Types, Ids)) + return Err; + + // Process collected namespaces. + LogicalVisitor.processNamespaces(); + + LLVM_DEBUG({ W.getOStream() << "Traversing inlined lines\n"; }); + + auto VisitInlineeLines = [&](int32_t Modi, const SymbolGroup &SG, + DebugInlineeLinesSubsectionRef &Lines) -> Error { + return collectInlineeInfo(Lines, &SG); + }; + + FilterOptions Filters = {}; + LinePrinter Printer(/*Indent=*/2, false, nulls(), Filters); + const PrintScope HeaderScope(Printer, /*IndentLevel=*/2); + if (Error Err = iterateModuleSubsections( + Input, HeaderScope, VisitInlineeLines)) + return Err; + + // Traverse global symbols. + LLVM_DEBUG({ W.getOStream() << "Traversing global symbols\n"; }); + if (Pdb.hasPDBGlobalsStream()) { + Expected GlobalsOrErr = Pdb.getPDBGlobalsStream(); + if (!GlobalsOrErr) + return GlobalsOrErr.takeError(); + GlobalsStream &Globals = *GlobalsOrErr; + const GSIHashTable &Table = Globals.getGlobalsTable(); + Expected ExpectedSyms = Pdb.getPDBSymbolStream(); + if (ExpectedSyms) { + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, nullptr, + LogicalVisitor.getShared()); + + // As the global symbols do not have an associated Compile Unit, create + // one, as the container for all global symbols. + RecordPrefix Prefix(SymbolKind::S_COMPILE3); + CVSymbol Symbol(&Prefix, sizeof(Prefix)); + uint32_t Offset = 0; + if (Error Err = Traverser.visitSymbolBegin(Symbol, Offset)) + consumeError(std::move(Err)); + else { + // The CodeView compile unit containing the global symbols does not + // have a name; use the and it is marked as such. Just append the + // '_global' string. + std::string Name(CompileUnit->getParentScope()->getName()); + CompileUnit->setName(Name.append("_global")); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Traverser); + CVSymbolVisitor Visitor(Pipeline); + + BinaryStreamRef SymStream = + ExpectedSyms->getSymbolArray().getUnderlyingStream(); + for (uint32_t PubSymOff : Table) { + Expected Sym = readSymbolFromStream(SymStream, PubSymOff); + if (Sym) { + if (Error Err = Visitor.visitSymbolRecord(*Sym, PubSymOff)) + return createStringError(errorToErrorCode(std::move(Err)), + getFileName()); + } else + consumeError(Sym.takeError()); + } + } + + LogicalVisitor.closeScope(); + } else + consumeError(ExpectedSyms.takeError()); + } + + // Traverse symbols (DBI). + LLVM_DEBUG({ W.getOStream() << "Traversing symbol groups\n"; }); + + auto VisitSymbolGroup = [&](uint32_t Modi, const SymbolGroup &SG) -> Error { + Expected ExpectedModS = + getModuleDebugStream(Pdb, Modi); + if (ExpectedModS) { + ModuleDebugStreamRef &ModS = *ExpectedModS; + + LLVM_DEBUG({ + W.getOStream() << formatv("Traversing Group: Mod {0:4}\n", Modi); + }); + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, nullptr, + LogicalVisitor.getShared()); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Traverser); + CVSymbolVisitor Visitor(Pipeline); + BinarySubstreamRef SS = ModS.getSymbolsSubstream(); + if (Error Err = + Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) + return createStringError(errorToErrorCode(std::move(Err)), + getFileName()); + } else { + // If the module stream does not exist, it is not an error condition. + consumeError(ExpectedModS.takeError()); + } + + return Error::success(); + }; + + if (Error Err = iterateSymbolGroups(Input, HeaderScope, VisitSymbolGroup)) + return Err; + + // At this stage, the logical view contains all scopes, symbols and types. + // For PDBs we can use the module id, to access its specific compile unit. + // The line record addresses has been already resolved, so we can apply the + // flow as when processing DWARF. + + LLVM_DEBUG({ W.getOStream() << "Traversing lines\n"; }); + + // Record all line records for a Compile Unit. + CULines.clear(); + + auto VisitDebugLines = [this](int32_t Modi, const SymbolGroup &SG, + DebugLinesSubsectionRef &Lines) -> Error { + if (!options().getPrintLines()) + return Error::success(); + + uint16_t Segment = Lines.header()->RelocSegment; + uint32_t Begin = Lines.header()->RelocOffset; + uint32_t Size = Lines.header()->CodeSize; + + LLVM_DEBUG({ W.getOStream() << formatv("Modi = {0}\n", Modi); }); + + // We have line information for a new module; finish processing the + // collected information for the current module. Once it is done, start + // recording the line information for the new module. + if (CurrentModule != Modi) { + if (Error Err = processModule()) + return Err; + CULines.clear(); + CurrentModule = Modi; + } + + for (const LineColumnEntry &Block : Lines) + if (Error Err = createLines(Block.LineNumbers, /*Addendum=*/0, Segment, + Begin, Size, Block.NameIndex, &SG)) + return Err; + + return Error::success(); + }; + + if (Error Err = iterateModuleSubsections( + Input, HeaderScope, VisitDebugLines)) + return Err; + + // Check if we have to close the Compile Unit scope. + LogicalVisitor.closeScope(); + + // Process collected element lines. + LogicalVisitor.processLines(); + + // Translate composite names into a single component. + Root->transformScopedName(); + return Error::success(); +} + +Error LVCodeViewReader::processModule() { + if (LVScope *Scope = getScopeForModule(CurrentModule)) { + CompileUnit = static_cast(Scope); + + LLVM_DEBUG({ dbgs() << "Processing Scope: " << Scope->getName() << "\n"; }); + + // For the given compile unit, collect all scopes ranges. + // For a complete ranges and lines mapping, the logical view support + // needs for the compile unit to have a low and high pc values. We + // can traverse the 'Modules' section and get the information for the + // specific module. Another option, is from all the ranges collected + // to take the first and last values. + LVSectionIndex SectionIndex = DotTextSectionIndex; + LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); + ScopesWithRanges->clear(); + CompileUnit->getRanges(*ScopesWithRanges); + if (!ScopesWithRanges->empty()) + CompileUnit->addObject(ScopesWithRanges->getLower(), + ScopesWithRanges->getUpper()); + ScopesWithRanges->sort(); + + if (Error Err = createInstructions()) + return Err; + + // Include lines from any inlined functions within the current function. + includeInlineeLines(SectionIndex, Scope); + + processLines(&CULines, SectionIndex, nullptr); + } + + return Error::success(); +} + +// In order to create the scopes, the CodeView Reader: +// = Traverse the TPI/IPI stream (Type visitor): +// Collect forward references, scoped names, type indexes that will represent +// a logical element, strings, line records, linkage names. +// = Traverse the symbols section (Symbol visitor): +// It creates the scopes tree and creates the required logical elements, by +// using the collected indexes from the type visitor. +Error LVCodeViewReader::createScopes() { + LLVM_DEBUG({ + W.startLine() << "\n"; + W.printString("File", getFileName().str()); + W.printString("Exe", ExePath); + W.printString("Format", FileFormatName); + }); + + if (Error Err = LVReader::createScopes()) + return Err; + + LogicalVisitor.setRoot(Root); + + if (isObj()) { + if (Error Err = createScopes(getObj())) + return Err; + } else { + if (Error Err = createScopes(getPdb())) + return Err; + } + + return Error::success(); +} + +Error LVCodeViewReader::loadTargetInfo(const ObjectFile &Obj) { + // Detect the architecture from the object file. We usually don't need OS + // info to lookup a target and create register info. + Triple TT; + TT.setArch(Triple::ArchType(Obj.getArch())); + TT.setVendor(Triple::UnknownVendor); + TT.setOS(Triple::UnknownOS); + + // Features to be passed to target/subtarget + SubtargetFeatures Features = Obj.getFeatures(); + + return loadGenericTargetInfo(TT.str(), Features.getString()); +} + +Error LVCodeViewReader::loadTargetInfo(const PDBFile &Pdb) { + Triple TT; + TT.setArch(Triple::ArchType::x86_64); + TT.setVendor(Triple::UnknownVendor); + TT.setOS(Triple::Win32); + + StringRef TheFeature = ""; + + return loadGenericTargetInfo(TT.str(), TheFeature); +} + +std::string LVCodeViewReader::getRegisterName(LVSmall Opcode, + uint64_t Operands[2]) { + // Get Compilation Unit CPU Type. + CPUType CPU = getCompileUnitCPUType(); + // For CodeView the register always is in Operands[0]; + RegisterId Register = (RegisterId(Operands[0])); + return formatRegisterId(Register, CPU); +} Index: llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp @@ -0,0 +1,3530 @@ +//===-- LVCodeViewVisitor.cpp ---------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVCodeViewVisitor class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InputFile.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::object; +using namespace llvm::pdb; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "CodeViewUtilities" + +namespace llvm { +namespace logicalview { + +static TypeIndex getTrueType(TypeIndex &TI) { + // Dealing with a MSVC generated PDB, we encountered a type index with the + // value of: 0x0280xxxx where xxxx=0000. + // + // There is some documentation about type indices: + // https://llvm.org/docs/PDB/TpiStream.html + // + // A type index is a 32-bit integer that uniquely identifies a type inside + // of an object file’s .debug$T section or a PDB file’s TPI or IPI stream. + // The value of the type index for the first type record from the TPI stream + // is given by the TypeIndexBegin member of the TPI Stream Header although + // in practice this value is always equal to 0x1000 (4096). + // + // Any type index with a high bit set is considered to come from the IPI + // stream, although this appears to be more of a hack, and LLVM does not + // generate type indices of this nature. They can, however, be observed in + // Microsoft PDBs occasionally, so one should be prepared to handle them. + // Note that having the high bit set is not a necessary condition to + // determine whether a type index comes from the IPI stream, it is only + // sufficient. + LLVM_DEBUG( + { dbgs() << "Index before: " << HexNumber(TI.getIndex()) << "\n"; }); + TI.setIndex(TI.getIndex() & 0x0000ffff); + LLVM_DEBUG( + { dbgs() << "Index after: " << HexNumber(TI.getIndex()) << "\n"; }); + return TI; +} + +static const EnumEntry LeafTypeNames[] = { +#define CV_TYPE(enum, val) {#enum, enum}, +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" +}; + +// Return the type name pointed by the type index. It uses the kind to query +// the associated name for the record type. +static StringRef getRecordName(LazyRandomTypeCollection &Types, TypeIndex TI) { + if (TI.isSimple()) + return {}; + + StringRef RecordName; + CVType CVReference = Types.getType(TI); + auto GetName = [&](auto Record) { + if (Error Err = TypeDeserializer::deserializeAs( + const_cast(CVReference), Record)) + consumeError(std::move(Err)); + else + RecordName = Record.getName(); + }; + + TypeRecordKind RK = static_cast(CVReference.kind()); + if (RK == TypeRecordKind::Class || RK == TypeRecordKind::Struct) + GetName(ClassRecord(RK)); + else if (RK == TypeRecordKind::Union) + GetName(UnionRecord(RK)); + else if (RK == TypeRecordKind::Enum) + GetName(EnumRecord(RK)); + + return RecordName; +} + +} // namespace logicalview +} // namespace llvm + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewDataVisitor" + +namespace llvm { +namespace logicalview { + +// Keeps the type indexes with line information. +using LVLineRecords = std::vector; + +namespace { + +class LVTypeRecords { + LVShared *Shared = nullptr; + + // Logical elements associated to their CodeView Type Index. + using RecordEntry = std::pair; + using RecordTable = std::map; + RecordTable RecordFromTypes; + RecordTable RecordFromIds; + + using NameTable = std::map; + NameTable NameFromTypes; + NameTable NameFromIds; + +public: + LVTypeRecords(LVShared *Shared) : Shared(Shared){}; + + void add(uint32_t StreamIdx, TypeIndex TI, TypeLeafKind Kind, + LVElement *Element = nullptr); + void add(uint32_t StreamIdx, TypeIndex TI, StringRef Name); + LVElement *find(uint32_t StreamIdx, TypeIndex TI); + TypeIndex find(uint32_t StreamIdx, StringRef Name); +}; + +class LVForwardReferences { + // Forward reference and its definitions (Name as key). + using ForwardEntry = std::pair; + using ForwardTypeNames = std::map; + ForwardTypeNames ForwardTypesNames; + + // Forward reference and its definition (TypeIndex as key). + using ForwardType = std::map; + ForwardType ForwardTypes; + + // Forward types and its references. + void add(TypeIndex TIForward, TypeIndex TIReference) { + ForwardTypes.emplace(TIForward, TIReference); + } + + void add(StringRef Name, TypeIndex TIForward) { + if (ForwardTypesNames.find(Name) == ForwardTypesNames.end()) { + ForwardTypesNames.emplace( + std::piecewise_construct, std::forward_as_tuple(Name), + std::forward_as_tuple(TIForward, TypeIndex::None())); + } else { + // Update a recorded definition with its reference. + ForwardTypesNames[Name].first = TIForward; + add(TIForward, ForwardTypesNames[Name].second); + } + } + + // Update a previously recorded forward reference with its definition. + void update(StringRef Name, TypeIndex TIReference) { + if (ForwardTypesNames.find(Name) != ForwardTypesNames.end()) { + // Update the recorded forward reference with its definition. + ForwardTypesNames[Name].second = TIReference; + add(ForwardTypesNames[Name].first, TIReference); + } else { + // We have not seen the forward reference. Insert the definition. + ForwardTypesNames.emplace( + std::piecewise_construct, std::forward_as_tuple(Name), + std::forward_as_tuple(TypeIndex::None(), TIReference)); + } + } + +public: + LVForwardReferences() = default; + + void record(bool IsForwardRef, StringRef Name, TypeIndex TI) { + // We are expecting for the forward references to be first. But that + // is not always the case. A name must be recorded regardless of the + // order in which the forward reference appears. + (IsForwardRef) ? add(Name, TI) : update(Name, TI); + } + + TypeIndex find(TypeIndex TIForward) { + return (ForwardTypes.find(TIForward) != ForwardTypes.end()) + ? ForwardTypes[TIForward] + : TypeIndex::None(); + } + + TypeIndex find(StringRef Name) { + return (ForwardTypesNames.find(Name) != ForwardTypesNames.end()) + ? ForwardTypesNames[Name].second + : TypeIndex::None(); + } + + // If the given TI corresponds to a reference, return the reference. + // Otherwise return the given TI. + TypeIndex remap(TypeIndex TI) { + TypeIndex Forward = find(TI); + return Forward.isNoneType() ? TI : Forward; + } +}; + +// Namespace deduction. +class LVNamespaceDeduction { + LVShared *Shared = nullptr; + + using Names = std::map; + Names NamespaceNames; + + using LookupSet = std::set; + LookupSet DeductedScopes; + LookupSet UnresolvedScopes; + LookupSet IdentifiedNamespaces; + + void add(StringRef Name, LVScope *Namespace) { + if (NamespaceNames.find(Name) == NamespaceNames.end()) + NamespaceNames.emplace(Name, Namespace); + } + +public: + LVNamespaceDeduction(LVShared *Shared) : Shared(Shared){}; + + void init(); + void add(StringRef String); + LVScope *get(LVStringRefs Components); + LVScope *get(StringRef Name, bool CheckScope = true); + + // Find the logical namespace for the 'Name' component. + LVScope *find(StringRef Name) { + LVScope *Namespace = (NamespaceNames.find(Name) != NamespaceNames.end()) + ? NamespaceNames[Name] + : nullptr; + return Namespace; + } + + // For the given lexical components, return a tuple with the first entry + // being the outermost namespace and the second entry being the first + // non-namespace. + LVLexicalIndex find(LVStringRefs Components) { + if (Components.empty()) + return {}; + + LVStringRefs::size_type FirstNamespace = 0; + LVStringRefs::size_type FirstNonNamespace = Components.size() - 1; + for (LVStringRefs::size_type Index = 0; Index < Components.size(); + ++Index) { + FirstNonNamespace = Index; + LookupSet::iterator Iter = IdentifiedNamespaces.find(Components[Index]); + if (Iter == IdentifiedNamespaces.end()) + // The component is not a namespace name. + break; + } + return std::make_tuple(FirstNamespace, FirstNonNamespace); + } +}; + +// Strings. +class LVStringRecords { + using StringEntry = std::tuple; + using StringIds = std::map; + StringIds Strings; + +public: + LVStringRecords() = default; + + void add(TypeIndex TI, StringRef String) { + static uint32_t Index = 0; + if (Strings.find(TI) == Strings.end()) + Strings.emplace( + std::piecewise_construct, std::forward_as_tuple(TI), + std::forward_as_tuple(++Index, std::string(String), nullptr)); + } + + StringRef find(TypeIndex TI) { + StringIds::iterator Iter = Strings.find(TI); + return Iter != Strings.end() ? std::get<1>(Iter->second) : StringRef{}; + } + + uint32_t findIndex(TypeIndex TI) { + StringIds::iterator Iter = Strings.find(TI); + return Iter != Strings.end() ? std::get<0>(Iter->second) : 0; + } + + // Move strings representing the filenames to the compile unit. + void addFilenames(); + void addFilenames(LVScopeCompileUnit *Scope); +}; +} // namespace + +using LVTypeKinds = std::set; +using LVSymbolKinds = std::set; + +// The following data keeps forward information, type records, names for +// namespace deduction, strings records, line records. +// It is shared by the type visitor, symbol visitor and logical visitor and +// it is independent from the CodeViewReader. +struct LVShared { + LVCodeViewReader *Reader = nullptr; + LVLogicalVisitor *Visitor = nullptr; + LVForwardReferences ForwardReferences; + LVLineRecords LineRecords; + LVNamespaceDeduction NamespaceDeduction; + LVStringRecords StringRecords; + LVTypeRecords TypeRecords; + + // In order to determine which types and/or symbols records should be handled + // by the reader, we record record kinds seen by the type and symbol visitors. + // At the end of the scopes creation, the '--internal=tag' option will allow + // to print the unique record ids collected. + LVTypeKinds TypeKinds; + LVSymbolKinds SymbolKinds; + + LVShared(LVCodeViewReader *Reader, LVLogicalVisitor *Visitor) + : Reader(Reader), Visitor(Visitor), NamespaceDeduction(this), + TypeRecords(this) {} + ~LVShared() = default; +}; +} // namespace logicalview +} // namespace llvm + +void LVTypeRecords::add(uint32_t StreamIdx, TypeIndex TI, TypeLeafKind Kind, + LVElement *Element) { + RecordTable &Target = + (StreamIdx == StreamTPI) ? RecordFromTypes : RecordFromIds; + Target.emplace(std::piecewise_construct, std::forward_as_tuple(TI), + std::forward_as_tuple(Kind, Element)); +} + +void LVTypeRecords::add(uint32_t StreamIdx, TypeIndex TI, StringRef Name) { + NameTable &Target = (StreamIdx == StreamTPI) ? NameFromTypes : NameFromIds; + Target.emplace(Name, TI); +} + +LVElement *LVTypeRecords::find(uint32_t StreamIdx, TypeIndex TI) { + RecordTable &Target = + (StreamIdx == StreamTPI) ? RecordFromTypes : RecordFromIds; + + LVElement *Element = nullptr; + RecordTable::iterator Iter = Target.find(TI); + if (Iter != Target.end()) { + Element = Iter->second.second; + if (Element) + return Element; + + // Create the logical element if not found. + Element = Shared->Visitor->createElement(Iter->second.first); + if (Element) { + Element->setOffset(TI.getIndex()); + Element->setOffsetFromTypeIndex(); + Target[TI].second = Element; + } + } + return Element; +} + +TypeIndex LVTypeRecords::find(uint32_t StreamIdx, StringRef Name) { + NameTable &Target = (StreamIdx == StreamTPI) ? NameFromTypes : NameFromIds; + NameTable::iterator Iter = Target.find(Name); + return Iter != Target.end() ? Iter->second : TypeIndex::None(); +} + +void LVStringRecords::addFilenames() { + for (StringIds::const_reference Entry : Strings) { + StringRef Name = std::get<1>(Entry.second); + LVScopeCompileUnit *Scope = std::get<2>(Entry.second); + Scope->addFilename(transformPath(Name)); + } + Strings.clear(); +} + +void LVStringRecords::addFilenames(LVScopeCompileUnit *Scope) { + for (StringIds::reference Entry : Strings) + if (!std::get<2>(Entry.second)) + std::get<2>(Entry.second) = Scope; +} + +void LVNamespaceDeduction::add(StringRef String) { + StringRef InnerComponent; + StringRef OuterComponent; + std::tie(OuterComponent, InnerComponent) = getInnerComponent(String); + DeductedScopes.insert(InnerComponent); + if (OuterComponent.size()) + UnresolvedScopes.insert(OuterComponent); +} + +void LVNamespaceDeduction::init() { + // We have 2 sets of names: + // - deducted scopes (class, structure, union and enum) and + // - unresolved scopes, that can represent namespaces or any deducted. + // Before creating the namespaces, we have to traverse the unresolved + // and remove any references to already deducted scopes. + LVStringRefs Components; + for (const StringRef &Unresolved : UnresolvedScopes) { + Components = getAllLexicalComponents(Unresolved); + for (const StringRef &Component : Components) { + LookupSet::iterator Iter = DeductedScopes.find(Component); + if (Iter == DeductedScopes.end()) + IdentifiedNamespaces.insert(Component); + } + } + + LLVM_DEBUG({ + auto Print = [&](LookupSet &Container, const char *Title) { + auto Header = [&]() { + dbgs() << formatv("\n{0}\n", fmt_repeat('=', 72)); + dbgs() << formatv("{0}\n", Title); + dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); + }; + Header(); + for (const StringRef &Item : Container) { + std::string TheItem(Item); + dbgs() << formatv("'{0}'\n", TheItem.c_str()); + } + }; + + Print(DeductedScopes, "Deducted Scopes"); + Print(UnresolvedScopes, "Unresolved Scopes"); + Print(IdentifiedNamespaces, "Namespaces"); + }); +} + +LVScope *LVNamespaceDeduction::get(LVStringRefs Components) { + LLVM_DEBUG({ + for (const StringRef &Component : Components) { + std::string TheComponent(Component); + dbgs() << formatv("'{0}'\n", TheComponent.c_str()); + } + }); + + if (Components.empty()) + return nullptr; + + // Update the namespaces relationship. + LVScope *Namespace = nullptr; + LVScope *Parent = Shared->Reader->getCompileUnit(); + for (const StringRef &Component : Components) { + // Check if we have seen the namespace. + Namespace = find(Component); + if (!Namespace) { + // We have identified namespaces that are generated by MSVC. Mark them + // as 'system' so they will be excluded from the logical view. + Namespace = new LVScopeNamespace(); + Namespace->setTag(dwarf::DW_TAG_namespace); + Namespace->setName(Component); + Parent->addElement(Namespace); + getReader().isSystemEntry(Namespace); + add(Component, Namespace); + } + Parent = Namespace; + } + return Parent; +} + +LVScope *LVNamespaceDeduction::get(StringRef ScopedName, bool CheckScope) { + LVStringRefs Components = getAllLexicalComponents(ScopedName); + if (CheckScope) + Components.erase(std::remove_if(Components.begin(), Components.end(), + [&](StringRef Component) { + LookupSet::iterator Iter = + IdentifiedNamespaces.find(Component); + return Iter == IdentifiedNamespaces.end(); + }), + Components.end()); + + LLVM_DEBUG({ + std::string TheScopedName(ScopedName); + dbgs() << formatv("ScopedName: '{0}'\n", TheScopedName.c_str()); + }); + + return get(Components); +} + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewTypeVisitor" + +//===----------------------------------------------------------------------===// +// TypeRecord traversal. +//===----------------------------------------------------------------------===// +void LVTypeVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI, + uint32_t StreamIdx) const { + codeview::printTypeIndex(W, FieldName, TI, + StreamIdx == StreamTPI ? Types : Ids); +} + +Error LVTypeVisitor::visitTypeBegin(CVType &Record) { + return visitTypeBegin(Record, TypeIndex::fromArrayIndex(Types.size())); +} + +Error LVTypeVisitor::visitTypeBegin(CVType &Record, TypeIndex TI) { + LLVM_DEBUG({ + W.getOStream() << formatTypeLeafKind(Record.kind()); + W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")\n"; + }); + + if (options().getInternalTag()) + Shared->TypeKinds.insert(Record.kind()); + + // The collected type records, will be use to create the logical elements + // during the symbols traversal when a type is referenced. + CurrentTypeIndex = TI; + Shared->TypeRecords.add(StreamIdx, TI, Record.kind()); + return Error::success(); +} + +Error LVTypeVisitor::visitUnknownType(CVType &Record) { + LLVM_DEBUG({ W.printNumber("Length", uint32_t(Record.content().size())); }); + return Error::success(); +} + +Error LVTypeVisitor::visitMemberBegin(CVMemberRecord &Record) { + LLVM_DEBUG({ + W.startLine() << formatTypeLeafKind(Record.Kind); + W.getOStream() << " {\n"; + W.indent(); + }); + return Error::success(); +} + +Error LVTypeVisitor::visitMemberEnd(CVMemberRecord &Record) { + LLVM_DEBUG({ + W.unindent(); + W.startLine() << "}\n"; + }); + return Error::success(); +} + +Error LVTypeVisitor::visitUnknownMember(CVMemberRecord &Record) { + LLVM_DEBUG({ W.printHex("UnknownMember", unsigned(Record.Kind)); }); + return Error::success(); +} + +// LF_BUILDINFO (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, BuildInfoRecord &Args) { + // All the args are references into the TPI/IPI stream. + LLVM_DEBUG({ + W.printNumber("NumArgs", static_cast(Args.getArgs().size())); + ListScope Arguments(W, "Arguments"); + for (TypeIndex Arg : Args.getArgs()) + printTypeIndex("ArgType", Arg, StreamIPI); + }); + + // Only add the strings that hold information about filenames. They will be + // used to complete the line/file information for the logical elements. + // There are other strings holding information about namespaces. + TypeIndex TI; + StringRef String; + + // Absolute CWD path + TI = Args.getArgs()[BuildInfoRecord::BuildInfoArg::CurrentDirectory]; + String = Ids.getTypeName(TI); + if (!String.empty()) + Shared->StringRecords.add(TI, String); + + // Get the compile unit name. + TI = Args.getArgs()[BuildInfoRecord::BuildInfoArg::SourceFile]; + String = Ids.getTypeName(TI); + if (!String.empty()) + Shared->StringRecords.add(TI, String); + LogicalVisitor->setCompileUnitName(std::string(String)); + + return Error::success(); +} + +// LF_CLASS, LF_STRUCTURE, LF_INTERFACE (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, ClassRecord &Class) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("FieldListType", Class.getFieldList(), StreamTPI); + W.printString("Name", Class.getName()); + }); + + // Collect class name for scope deduction. + Shared->NamespaceDeduction.add(Class.getName()); + Shared->ForwardReferences.record(Class.isForwardRef(), Class.getName(), + CurrentTypeIndex); + + // Collect class name for contained scopes deduction. + Shared->TypeRecords.add(StreamIdx, CurrentTypeIndex, Class.getName()); + return Error::success(); +} + +// LF_ENUM (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, EnumRecord &Enum) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("FieldListType", Enum.getFieldList(), StreamTPI); + W.printString("Name", Enum.getName()); + }); + + // Collect enum name for scope deduction. + Shared->NamespaceDeduction.add(Enum.getName()); + return Error::success(); +} + +// LF_FUNC_ID (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, FuncIdRecord &Func) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("Type", Func.getFunctionType(), StreamTPI); + printTypeIndex("Parent", Func.getParentScope(), StreamTPI); + W.printString("Name", Func.getName()); + }); + + // Collect function name for scope deduction. + Shared->NamespaceDeduction.add(Func.getName()); + return Error::success(); +} + +// LF_PROCEDURE (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, ProcedureRecord &Proc) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("ReturnType", Proc.getReturnType(), StreamTPI); + W.printNumber("NumParameters", Proc.getParameterCount()); + printTypeIndex("ArgListType", Proc.getArgumentList(), StreamTPI); + }); + + // Collect procedure information as they can be referenced by typedefs. + Shared->TypeRecords.add(StreamTPI, CurrentTypeIndex, {}); + return Error::success(); +} + +// LF_STRING_ID (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, StringIdRecord &String) { + // No additional references are needed. + LLVM_DEBUG({ + printTypeIndex("Id", String.getId(), StreamIPI); + W.printString("StringData", String.getString()); + }); + return Error::success(); +} + +// LF_UDT_SRC_LINE (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, + UdtSourceLineRecord &Line) { + // UDT and SourceFile are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeIndex("UDT", Line.getUDT(), StreamIPI); + printTypeIndex("SourceFile", Line.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", Line.getLineNumber()); + }); + + Shared->LineRecords.push_back(CurrentTypeIndex); + return Error::success(); +} + +// LF_UNION (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, UnionRecord &Union) { + LLVM_DEBUG({ + W.printNumber("MemberCount", Union.getMemberCount()); + printTypeIndex("FieldList", Union.getFieldList(), StreamTPI); + W.printNumber("SizeOf", Union.getSize()); + W.printString("Name", Union.getName()); + if (Union.hasUniqueName()) + W.printString("UniqueName", Union.getUniqueName()); + }); + + // Collect union name for scope deduction. + Shared->NamespaceDeduction.add(Union.getName()); + Shared->ForwardReferences.record(Union.isForwardRef(), Union.getName(), + CurrentTypeIndex); + + // Collect class name for contained scopes deduction. + Shared->TypeRecords.add(StreamIdx, CurrentTypeIndex, Union.getName()); + return Error::success(); +} + +namespace llvm { +namespace logicalview { + +// Current elements during the processing of a RecordType or RecordSymbol. +LVElement *CurrentElement = nullptr; +LVScope *CurrentScope = nullptr; +LVSymbol *CurrentSymbol = nullptr; +LVType *CurrentType = nullptr; + +} // namespace logicalview +} // namespace llvm + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewSymbolVisitor" + +//===----------------------------------------------------------------------===// +// SymbolRecord traversal. +//===----------------------------------------------------------------------===// +void LVSymbolVisitorDelegate::printRelocatedField(StringRef Label, + uint32_t RelocOffset, + uint32_t Offset, + StringRef *RelocSym) { + Reader->printRelocatedField(Label, CoffSection, RelocOffset, Offset, + RelocSym); +} + +void LVSymbolVisitorDelegate::getLinkageName(uint32_t RelocOffset, + uint32_t Offset, + StringRef *RelocSym) { + Reader->getLinkageName(CoffSection, RelocOffset, Offset, RelocSym); +} + +StringRef +LVSymbolVisitorDelegate::getFileNameForFileOffset(uint32_t FileOffset) { + Expected Name = Reader->getFileNameForFileOffset(FileOffset); + if (!Name) { + consumeError(Name.takeError()); + return {}; + } + return *Name; +} + +DebugStringTableSubsectionRef LVSymbolVisitorDelegate::getStringTable() { + return Reader->CVStringTable; +} + +void LVSymbolVisitor::printLocalVariableAddrRange( + const LocalVariableAddrRange &Range, uint32_t RelocationOffset) { + DictScope S(W, "LocalVariableAddrRange"); + if (ObjDelegate) + ObjDelegate->printRelocatedField("OffsetStart", RelocationOffset, + Range.OffsetStart); + W.printHex("ISectStart", Range.ISectStart); + W.printHex("Range", Range.Range); +} + +void LVSymbolVisitor::printLocalVariableAddrGap( + ArrayRef Gaps) { + for (const LocalVariableAddrGap &Gap : Gaps) { + ListScope S(W, "LocalVariableAddrGap"); + W.printHex("GapStartOffset", Gap.GapStartOffset); + W.printHex("Range", Gap.Range); + } +} + +void LVSymbolVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI) const { + codeview::printTypeIndex(W, FieldName, TI, Types); +} + +Error LVSymbolVisitor::visitSymbolBegin(CVSymbol &Record) { + return visitSymbolBegin(Record, 0); +} + +Error LVSymbolVisitor::visitSymbolBegin(CVSymbol &Record, uint32_t Offset) { + SymbolKind Kind = Record.kind(); + LLVM_DEBUG({ + W.printNumber("Offset", Offset); + W.printEnum("Begin Kind", unsigned(Kind), getSymbolTypeNames()); + }); + + if (options().getInternalTag()) + Shared->SymbolKinds.insert(Kind); + + CurrentElement = LogicalVisitor->createElement(Kind); + if (!CurrentElement) { + LLVM_DEBUG({ + // We have an unsupported Symbol or Type Record. + // W.printEnum("Kind ignored", unsigned(Kind), getSymbolTypeNames()); + }); + return Error::success(); + } + + // Offset carried by the traversal routines when dealing with streams. + CurrentOffset = Offset; + IsCompileUnit = false; + if (!CurrentElement->getOffsetFromTypeIndex()) + CurrentElement->setOffset(Offset); + if (symbolOpensScope(Kind) || (IsCompileUnit = symbolIsCompileUnit(Kind))) { + assert(CurrentScope && "Invalid scope!"); + LogicalVisitor->addElement(CurrentScope, IsCompileUnit); + } else { + if (CurrentSymbol) + LogicalVisitor->addElement(CurrentSymbol); + if (CurrentType) + LogicalVisitor->addElement(CurrentType); + } + + return Error::success(); +} + +Error LVSymbolVisitor::visitSymbolEnd(CVSymbol &Record) { + SymbolKind Kind = Record.kind(); + LLVM_DEBUG( + { W.printEnum("End Kind", unsigned(Kind), getSymbolTypeNames()); }); + + if (symbolEndsScope(Kind)) { + LogicalVisitor->popScope(); + } + + return Error::success(); +} + +Error LVSymbolVisitor::visitUnknownSymbol(CVSymbol &Record) { + LLVM_DEBUG({ W.printNumber("Length", Record.length()); }); + return Error::success(); +} + +// S_BLOCK32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, BlockSym &Block) { + LLVM_DEBUG({ + W.printHex("CodeSize", Block.CodeSize); + W.printHex("Segment", Block.Segment); + W.printString("BlockName", Block.Name); + }); + + if (LVScope *Scope = CurrentScope) { + StringRef LinkageName; + if (ObjDelegate) + ObjDelegate->getLinkageName(Block.getRelocationOffset(), Block.CodeOffset, + &LinkageName); + Scope->setLinkageName(LinkageName); + + if (options().getGeneralCollectRanges()) { + // Record converted segment::offset addressing for this scope. + LVAddress Addendum = Reader->getSymbolTableAddress(LinkageName); + LVAddress LowPC = + Reader->linearAddress(Block.Segment, Block.CodeOffset, Addendum); + LVAddress HighPC = LowPC + Block.CodeSize - 1; + Scope->addObject(LowPC, HighPC); + } + } + + return Error::success(); +} + +// S_BPREL32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + BPRelativeSym &Local) { + LLVM_DEBUG({ + printTypeIndex("Type", Local.Type); + W.printNumber("Offset", Local.Offset); + W.printString("VarName", Local.Name); + }); + + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Local.Name); + // From the MS_Symbol_Type.pdf documentation (S_BPREL32): + // This symbol specifies symbols that are allocated on the stack for a + // procedure. For C and C++, these include the actual function parameters + // and the local non-static variables of functions. + // However, the offset for 'this' comes as a negative value. + + // Symbol was created as 'variable'; determine its real kind. + Symbol->resetIsVariable(); + + if (Local.Name.equals("this")) { + Symbol->setIsParameter(); + Symbol->setIsArtificial(); + } else { + // Determine symbol kind. + bool(Local.Offset > 0) ? Symbol->setIsParameter() + : Symbol->setIsVariable(); + } + + // Update correct debug information tag. + if (Symbol->getIsParameter()) + Symbol->setTag(dwarf::DW_TAG_formal_parameter); + + LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type); + if (Element && Element->getIsScoped()) { + // We have a local type. Find its parent function. + LVScope *Parent = Symbol->getFunctionParent(); + // The element representing the type has been already finalized. If + // the type is an aggregate type, its members have been already added. + // As the type is local, its level will be changed. + + // FIXME: Currently the algorithm used to scope lambda functions is + // incorrect. Before we allocate the type at this scope, check if is + // already allocated in other scope. + if (!Element->getParentScope()) { + Parent->addElement(Element); + Element->updateLevel(Parent); + } + } + Symbol->setType(Element); + } + + return Error::success(); +} + +// S_REGREL32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + RegRelativeSym &Local) { + LLVM_DEBUG({ + printTypeIndex("Type", Local.Type); + W.printNumber("Offset", Local.Offset); + W.printString("VarName", Local.Name); + }); + + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Local.Name); + + // Symbol was created as 'variable'; determine its real kind. + Symbol->resetIsVariable(); + + // Check for the 'this' symbol. + if (Local.Name.equals("this")) { + Symbol->setIsArtificial(); + Symbol->setIsParameter(); + } else { + // Determine symbol kind. + determineSymbolKind(Symbol, Local.Register); + } + + // Update correct debug information tag. + if (Symbol->getIsParameter()) + Symbol->setTag(dwarf::DW_TAG_formal_parameter); + + LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type); + if (Element && Element->getIsScoped()) { + // We have a local type. Find its parent function. + LVScope *Parent = Symbol->getFunctionParent(); + // The element representing the type has been already finalized. If + // the type is an aggregate type, its members have been already added. + // As the type is local, its level will be changed. + + // FIXME: Currently the algorithm used to scope lambda functions is + // incorrect. Before we allocate the type at this scope, check if is + // already allocated in other scope. + if (!Element->getParentScope()) { + Parent->addElement(Element); + Element->updateLevel(Parent); + } + } + Symbol->setType(Element); + } + + return Error::success(); +} + +// S_BUILDINFO +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &CVR, + BuildInfoSym &BuildInfo) { + LLVM_DEBUG({ printTypeIndex("BuildId", BuildInfo.BuildId); }); + + CVType CVBuildType = Ids.getType(BuildInfo.BuildId); + if (Error Err = LogicalVisitor->finishVisitation( + CVBuildType, BuildInfo.BuildId, Reader->getCompileUnit())) + return Err; + + return Error::success(); +} + +// S_COMPILE2 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + Compile2Sym &Compile2) { + LLVM_DEBUG({ + W.printEnum("Language", uint8_t(Compile2.getLanguage()), + getSourceLanguageNames()); + W.printFlags("Flags", uint32_t(Compile2.getFlags()), + getCompileSym3FlagNames()); + W.printEnum("Machine", unsigned(Compile2.Machine), getCPUTypeNames()); + W.printString("VersionName", Compile2.Version); + }); + + // MSVC generates the following sequence for a CodeView module: + // S_OBJNAME --> Set 'CurrentObjectName'. + // S_COMPILE2 --> Set the compile unit name using 'CurrentObjectName'. + // ... + // S_BUILDINFO --> Extract the source name. + // + // Clang generates the following sequence for a CodeView module: + // S_COMPILE2 --> Set the compile unit name to empty string. + // ... + // S_BUILDINFO --> Extract the source name. + // + // For both toolchains, update the compile unit name from S_BUILDINFO. + if (LVScope *Scope = CurrentScope) { + // The name of the CU, was extracted from the 'BuildInfo' subsection. + Reader->setCompileUnitCPUType(Compile2.Machine); + Scope->setName(CurrentObjectName); + if (options().getAttributeProducer()) + Scope->setProducer(Compile2.Version); + getReader().isSystemEntry(Scope, CurrentObjectName); + + // The line records in CodeView are recorded per Module ID. Update + // the relationship between the current CU and the Module ID. + Reader->addModule(Scope); + + // Updated the collected strings with their associated compile unit. + Shared->StringRecords.addFilenames(Reader->getCompileUnit()); + } + + // Clear any previous ObjectName. + CurrentObjectName = ""; + return Error::success(); +} + +// S_COMPILE3 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + Compile3Sym &Compile3) { + LLVM_DEBUG({ + W.printEnum("Language", uint8_t(Compile3.getLanguage()), + getSourceLanguageNames()); + W.printFlags("Flags", uint32_t(Compile3.getFlags()), + getCompileSym3FlagNames()); + W.printEnum("Machine", unsigned(Compile3.Machine), getCPUTypeNames()); + W.printString("VersionName", Compile3.Version); + }); + + // MSVC generates the following sequence for a CodeView module: + // S_OBJNAME --> Set 'CurrentObjectName'. + // S_COMPILE3 --> Set the compile unit name using 'CurrentObjectName'. + // ... + // S_BUILDINFO --> Extract the source name. + // + // Clang generates the following sequence for a CodeView module: + // S_COMPILE3 --> Set the compile unit name to empty string. + // ... + // S_BUILDINFO --> Extract the source name. + // + // For both toolchains, update the compile unit name from S_BUILDINFO. + if (LVScope *Scope = CurrentScope) { + // The name of the CU, was extracted from the 'BuildInfo' subsection. + Reader->setCompileUnitCPUType(Compile3.Machine); + Scope->setName(CurrentObjectName); + if (options().getAttributeProducer()) + Scope->setProducer(Compile3.Version); + getReader().isSystemEntry(Scope, CurrentObjectName); + + // The line records in CodeView are recorded per Module ID. Update + // the relationship between the current CU and the Module ID. + Reader->addModule(Scope); + + // Updated the collected strings with their associated compile unit. + Shared->StringRecords.addFilenames(Reader->getCompileUnit()); + } + + // Clear any previous ObjectName. + CurrentObjectName = ""; + return Error::success(); +} + +// S_CONSTANT, S_MANCONSTANT +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + ConstantSym &Constant) { + LLVM_DEBUG({ + printTypeIndex("Type", Constant.Type); + W.printNumber("Value", Constant.Value); + W.printString("Name", Constant.Name); + }); + + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Constant.Name); + Symbol->setType(LogicalVisitor->getElement(StreamTPI, Constant.Type)); + Symbol->resetIncludeInPrint(); + } + + return Error::success(); +} + +// S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, + DefRangeFramePointerRelFullScopeSym &DefRangeFramePointerRelFullScope) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printNumber("Offset", DefRangeFramePointerRelFullScope.Offset); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Offset, 0]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE); + + uint64_t Operand1 = DefRangeFramePointerRelFullScope.Offset; + Symbol->addLocation(Attr, 0, 0, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_FRAMEPOINTER_REL +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, DefRangeFramePointerRelSym &DefRangeFramePointerRel) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printNumber("Offset", DefRangeFramePointerRel.Hdr.Offset); + printLocalVariableAddrRange(DefRangeFramePointerRel.Range, + DefRangeFramePointerRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeFramePointerRel.Gaps); + }); + + // We are expecting the following sequence: + // 128 | S_LOCAL [size = 20] `ParamBar` + // ... + // 148 | S_DEFRANGE_FRAMEPOINTER_REL [size = 16] + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Offset, 0]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL); + uint64_t Operand1 = DefRangeFramePointerRel.Hdr.Offset; + + LocalVariableAddrRange Range = DefRangeFramePointerRel.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_REGISTER_REL +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, DefRangeRegisterRelSym &DefRangeRegisterRel) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printBoolean("HasSpilledUDTMember", + DefRangeRegisterRel.hasSpilledUDTMember()); + W.printNumber("OffsetInParent", DefRangeRegisterRel.offsetInParent()); + W.printNumber("BasePointerOffset", + DefRangeRegisterRel.Hdr.BasePointerOffset); + printLocalVariableAddrRange(DefRangeRegisterRel.Range, + DefRangeRegisterRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegisterRel.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Register, Offset]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_REGISTER_REL); + uint64_t Operand1 = DefRangeRegisterRel.Hdr.Register; + uint64_t Operand2 = DefRangeRegisterRel.Hdr.BasePointerOffset; + + LocalVariableAddrRange Range = DefRangeRegisterRel.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, Operand2); + } + + return Error::success(); +} + +// S_DEFRANGE_REGISTER +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + DefRangeRegisterSym &DefRangeRegister) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printEnum("Register", uint16_t(DefRangeRegister.Hdr.Register), + getRegisterNames(Reader->getCompileUnitCPUType())); + W.printNumber("MayHaveNoName", DefRangeRegister.Hdr.MayHaveNoName); + printLocalVariableAddrRange(DefRangeRegister.Range, + DefRangeRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegister.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Register, 0]. + dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE_REGISTER); + uint64_t Operand1 = DefRangeRegister.Hdr.Register; + + Symbol->addLocation(Attr, 0, 0, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_SUBFIELD_REGISTER +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printEnum("Register", uint16_t(DefRangeSubfieldRegister.Hdr.Register), + getRegisterNames(Reader->getCompileUnitCPUType())); + W.printNumber("MayHaveNoName", DefRangeSubfieldRegister.Hdr.MayHaveNoName); + W.printNumber("OffsetInParent", + DefRangeSubfieldRegister.Hdr.OffsetInParent); + printLocalVariableAddrRange(DefRangeSubfieldRegister.Range, + DefRangeSubfieldRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeSubfieldRegister.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Register, 0]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER); + uint64_t Operand1 = DefRangeSubfieldRegister.Hdr.Register; + + Symbol->addLocation(Attr, 0, 0, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_SUBFIELD +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + DefRangeSubfieldSym &DefRangeSubfield) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + if (ObjDelegate) { + DebugStringTableSubsectionRef Strings = ObjDelegate->getStringTable(); + auto ExpectedProgram = Strings.getString(DefRangeSubfield.Program); + if (!ExpectedProgram) { + consumeError(ExpectedProgram.takeError()); + return llvm::make_error( + "String table offset outside of bounds of String Table!"); + } + W.printString("Program", *ExpectedProgram); + } + W.printNumber("OffsetInParent", DefRangeSubfield.OffsetInParent); + printLocalVariableAddrRange(DefRangeSubfield.Range, + DefRangeSubfield.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeSubfield.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Program, 0]. + dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE_SUBFIELD); + uint64_t Operand1 = DefRangeSubfield.Program; + + LocalVariableAddrRange Range = DefRangeSubfield.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + DefRangeSym &DefRange) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + if (ObjDelegate) { + DebugStringTableSubsectionRef Strings = ObjDelegate->getStringTable(); + auto ExpectedProgram = Strings.getString(DefRange.Program); + if (!ExpectedProgram) { + consumeError(ExpectedProgram.takeError()); + return llvm::make_error( + "String table offset outside of bounds of String Table!"); + } + W.printString("Program", *ExpectedProgram); + } + printLocalVariableAddrRange(DefRange.Range, DefRange.getRelocationOffset()); + printLocalVariableAddrGap(DefRange.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Program, 0]. + dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE); + uint64_t Operand1 = DefRange.Program; + + LocalVariableAddrRange Range = DefRange.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_FRAMEPROC +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + FrameProcSym &FrameProc) { + if (LVScope *Function = LogicalVisitor->getReaderScope()) { + // S_FRAMEPROC contains extra information for the function described + // by any of the previous generated records: + // S_GPROC32, S_LPROC32, S_LPROC32_ID, S_GPROC32_ID. + + // The generated sequence is: + // S_GPROC32_ID ... + // S_FRAMEPROC ... + + // Collect additional inline flags for the current scope function. + FrameProcedureOptions Flags = FrameProc.Flags; + if (FrameProcedureOptions::MarkedInline == + (Flags & FrameProcedureOptions::MarkedInline)) + Function->setInlineCode(dwarf::DW_INL_declared_inlined); + if (FrameProcedureOptions::Inlined == + (Flags & FrameProcedureOptions::Inlined)) + Function->setInlineCode(dwarf::DW_INL_inlined); + + // To determine the symbol kind for any symbol declared in that function, + // we can access the S_FRAMEPROC for the parent scope function. It contains + // information about the local fp and param fp registers and compare with + // the register in the S_REGREL32 to get a match. + codeview::CPUType CPU = Reader->getCompileUnitCPUType(); + LocalFrameRegister = FrameProc.getLocalFramePtrReg(CPU); + ParamFrameRegister = FrameProc.getParamFramePtrReg(CPU); + } + + return Error::success(); +} + +// S_GDATA32, S_LDATA32, S_LMANDATA, S_GMANDATA +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, DataSym &Data) { + LLVM_DEBUG({ + printTypeIndex("Type", Data.Type); + W.printString("DisplayName", Data.Name); + }); + + if (LVSymbol *Symbol = CurrentSymbol) { + StringRef LinkageName; + if (ObjDelegate) + ObjDelegate->getLinkageName(Data.getRelocationOffset(), Data.DataOffset, + &LinkageName); + + Symbol->setName(Data.Name); + Symbol->setLinkageName(LinkageName); + + // The MSVC generates local data as initialization for aggregates. It + // contains the address for an initialization function. + // The symbols contains the '$initializer$' pattern. Allow them only if + // the '--internal=system' option is given. + // 0 | S_LDATA32 `Struct$initializer$` + // type = 0x1040 (void ()*) + if (getReader().isSystemEntry(Symbol) && !options().getAttributeSystem()) { + Symbol->resetIncludeInPrint(); + return Error::success(); + } + + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Data.Name)) { + // The variable is already at different scope. In order to reflect + // the correct parent, move it to the namespace. + if (Symbol->getParentScope()->removeElement(Symbol)) + Namespace->addElement(Symbol); + } + + Symbol->setType(LogicalVisitor->getElement(StreamTPI, Data.Type)); + if (Record.kind() == SymbolKind::S_GDATA32) + Symbol->setIsExternal(); + } + + return Error::success(); +} + +// S_INLINESITE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + InlineSiteSym &InlineSite) { + LLVM_DEBUG({ printTypeIndex("Inlinee", InlineSite.Inlinee); }); + + if (LVScope *InlinedFunction = CurrentScope) { + LVScope *AbstractFunction = new LVScopeFunction(); + if (AbstractFunction) { + AbstractFunction->setIsSubprogram(); + AbstractFunction->setTag(dwarf::DW_TAG_subprogram); + AbstractFunction->setInlineCode(dwarf::DW_INL_inlined); + AbstractFunction->setIsInlinedAbstract(); + InlinedFunction->setReference(AbstractFunction); + + LogicalVisitor->startProcessArgumentList(); + // 'Inlinee' is a Type ID. + CVType CVFunctionType = Ids.getType(InlineSite.Inlinee); + if (Error Err = LogicalVisitor->finishVisitation( + CVFunctionType, InlineSite.Inlinee, AbstractFunction)) + return Err; + LogicalVisitor->stopProcessArgumentList(); + + // For inlined functions set the linkage name to be the same as + // the name. It used to find their lines and ranges. + StringRef Name = AbstractFunction->getName(); + InlinedFunction->setName(Name); + InlinedFunction->setLinkageName(Name); + + // Process annotation bytes to calculate code and line offsets. + if (Error Err = LogicalVisitor->inlineSiteAnnotation( + AbstractFunction, InlinedFunction, InlineSite)) + return Err; + } + } + + return Error::success(); +} + +// S_LOCAL +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, LocalSym &Local) { + LLVM_DEBUG({ + printTypeIndex("Type", Local.Type); + W.printFlags("Flags", uint16_t(Local.Flags), getLocalFlagNames()); + W.printString("VarName", Local.Name); + }); + + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Local.Name); + + // Symbol was created as 'variable'; determine its real kind. + Symbol->resetIsVariable(); + + // Be sure the 'this' symbol is marked as 'compiler generated'. + if (bool(Local.Flags & LocalSymFlags::IsCompilerGenerated) || + Local.Name.equals("this")) { + Symbol->setIsArtificial(); + Symbol->setIsParameter(); + } else { + bool(Local.Flags & LocalSymFlags::IsParameter) ? Symbol->setIsParameter() + : Symbol->setIsVariable(); + } + + // Update correct debug information tag. + if (Symbol->getIsParameter()) + Symbol->setTag(dwarf::DW_TAG_formal_parameter); + + LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type); + if (Element && Element->getIsScoped()) { + // We have a local type. Find its parent function. + LVScope *Parent = Symbol->getFunctionParent(); + // The element representing the type has been already finalized. If + // the type is an aggregate type, its members have been already added. + // As the type is local, its level will be changed. + Parent->addElement(Element); + Element->updateLevel(Parent); + } + Symbol->setType(Element); + + // The CodeView records (S_DEFFRAME_*) describing debug location for + // this symbol, do not have any direct reference to it. Those records + // are emitted after this symbol. Record the current symbol. + LocalSymbol = Symbol; + } + + return Error::success(); +} + +// S_OBJNAME +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, ObjNameSym &ObjName) { + LLVM_DEBUG({ + W.printHex("Signature", ObjName.Signature); + W.printString("ObjectName", ObjName.Name); + }); + + CurrentObjectName = ObjName.Name; + return Error::success(); +} + +// S_GPROC32, S_LPROC32, S_LPROC32_ID, S_GPROC32_ID +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, ProcSym &Proc) { + if (InFunctionScope) + return llvm::make_error("Visiting a ProcSym while inside " + "function scope!"); + + InFunctionScope = true; + + LLVM_DEBUG({ + printTypeIndex("FunctionType", Proc.FunctionType); + W.printHex("Segment", Proc.Segment); + W.printFlags("Flags", static_cast(Proc.Flags), + getProcSymFlagNames()); + W.printString("DisplayName", Proc.Name); + }); + + // Clang and Microsoft generated different debug information records: + // For functions definitions: + // Clang: S_GPROC32 -> LF_FUNC_ID -> LF_PROCEDURE + // Microsoft: S_GPROC32 -> LF_PROCEDURE + + // For member function definition: + // Clang: S_GPROC32 -> LF_MFUNC_ID -> LF_MFUNCTION + // Microsoft: S_GPROC32 -> LF_MFUNCTION + // In order to support both sequences, if we found LF_FUNCTION_ID, just + // get the TypeIndex for LF_PROCEDURE. + + // For the given test case, we have the sequence: + // namespace NSP_local { + // void foo_local() { + // } + // } + // + // 0x1000 | LF_STRING_ID String: NSP_local + // 0x1002 | LF_PROCEDURE + // return type = 0x0003 (void), # args = 0, param list = 0x1001 + // calling conv = cdecl, options = None + // 0x1003 | LF_FUNC_ID + // name = foo_local, type = 0x1002, parent scope = 0x1000 + // 0 | S_GPROC32_ID `NSP_local::foo_local` + // type = `0x1003 (foo_local)` + // 0x1004 | LF_STRING_ID String: suite + // 0x1005 | LF_STRING_ID String: suite_local.cpp + // + // The LF_STRING_ID can hold different information: + // 0x1000 - The enclosing namespace. + // 0x1004 - The compile unit directory name. + // 0x1005 - The compile unit name. + // + // Before deducting its scope, we need to evaluate its type and create any + // associated namespaces. + if (LVScope *Function = CurrentScope) { + StringRef LinkageName; + if (ObjDelegate) + ObjDelegate->getLinkageName(Proc.getRelocationOffset(), Proc.CodeOffset, + &LinkageName); + + // The line table can be accessed using the linkage name. + Reader->addToSymbolTable(LinkageName, Function); + Function->setName(Proc.Name); + Function->setLinkageName(LinkageName); + + if (options().getGeneralCollectRanges()) { + // Record converted segment::offset addressing for this scope. + LVAddress Addendum = Reader->getSymbolTableAddress(LinkageName); + LVAddress LowPC = + Reader->linearAddress(Proc.Segment, Proc.CodeOffset, Addendum); + LVAddress HighPC = LowPC + Proc.CodeSize - 1; + Function->addObject(LowPC, HighPC); + + // If the scope is a function, add it to the public names. + if ((options().getAttributePublics() || options().getPrintAnyLine()) && + !Function->getIsInlinedFunction()) + Reader->getCompileUnit()->addPublicName(Function, LowPC, HighPC); + } + + if (Function->getIsSystem() && !options().getAttributeSystem()) { + Function->resetIncludeInPrint(); + return Error::success(); + } + + TypeIndex TIFunctionType = Proc.FunctionType; + if (TIFunctionType.isSimple()) + Function->setType(LogicalVisitor->getElement(StreamTPI, TIFunctionType)); + else { + // We have to detect the correct stream, using the lexical parent + // name, as there is not other obvious way to get the stream. + // Normal function: LF_FUNC_ID (TPI)/(IPI) + // LF_PROCEDURE (TPI) + // Lambda function: LF_MFUNCTION (TPI) + // Member function: LF_MFUNC_ID (TPI)/(IPI) + + StringRef OuterComponent; + std::tie(OuterComponent, std::ignore) = getInnerComponent(Proc.Name); + TypeIndex TI = Shared->ForwardReferences.find(OuterComponent); + + llvm::Optional CVFunctionType; + auto GetRecordType = [&]() -> bool { + CVFunctionType = Ids.tryGetType(TIFunctionType); + if (!CVFunctionType) + return false; + + if (TI.isNoneType()) + // Normal function. + if (CVFunctionType->kind() == LF_FUNC_ID) + return true; + + // Member function. + return (CVFunctionType->kind() == LF_MFUNC_ID); + }; + + // We can have a LF_FUNC_ID, LF_PROCEDURE or LF_MFUNCTION. + if (!GetRecordType()) { + CVFunctionType = Types.tryGetType(TIFunctionType); + if (!CVFunctionType) + return llvm::make_error("Invalid type index"); + } + + if (Error Err = LogicalVisitor->finishVisitation( + *CVFunctionType, TIFunctionType, Function)) + return Err; + } + + if (Record.kind() == SymbolKind::S_GPROC32 || + Record.kind() == SymbolKind::S_GPROC32_ID) + Function->setIsExternal(); + + // We don't have a way to see if the symbol is compiler generated. Use + // the linkage name, to detect `scalar deleting destructor' functions. + std::string DemangledSymbol = demangle(std::string(LinkageName)); + if (DemangledSymbol.find("scalar deleting dtor") != std::string::npos) + Function->setIsArtificial(); + } + + return Error::success(); +} + +// S_END +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + ScopeEndSym &ScopeEnd) { + InFunctionScope = false; + return Error::success(); +} + +// S_THUNK32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, Thunk32Sym &Thunk) { + if (InFunctionScope) + return llvm::make_error("Visiting a Thunk32Sym while inside " + "function scope!"); + + InFunctionScope = true; + + LLVM_DEBUG({ + W.printHex("Segment", Thunk.Segment); + W.printString("Name", Thunk.Name); + }); + + if (LVScope *Function = CurrentScope) + Function->setName(Thunk.Name); + + return Error::success(); +} + +// S_UDT, S_COBOLUDT +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, UDTSym &UDT) { + LLVM_DEBUG({ + printTypeIndex("Type", UDT.Type); + W.printString("UDTName", UDT.Name); + }); + + if (LVType *Type = CurrentType) { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(UDT.Name)) { + if (Type->getParentScope()->removeElement(Type)) + Namespace->addElement(Type); + } + + Type->setName(UDT.Name); + + // We have to determine if the typedef is a real C/C++ definition or is + // the S_UDT record that describe all the user defined types. + // 0 | S_UDT `Name` original type = 0x1009 + // 0x1009 | LF_STRUCTURE `Name` + // Ignore type definitions for RTTI types: + // _s__RTTIBaseClassArray, _s__RTTIBaseClassDescriptor, + // _s__RTTICompleteObjectLocator, _s__RTTIClassHierarchyDescriptor. + if (getReader().isSystemEntry(Type)) + Type->resetIncludeInPrint(); + else { + StringRef RecordName = getRecordName(Types, UDT.Type); + if (UDT.Name.equals(RecordName)) + Type->resetIncludeInPrint(); + Type->setType(LogicalVisitor->getElement(StreamTPI, UDT.Type)); + } + } + + return Error::success(); +} + +// S_UNAMESPACE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + UsingNamespaceSym &UN) { + LLVM_DEBUG({ W.printString("Namespace", UN.Name); }); + return Error::success(); +} + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewLogicalVisitor" + +//===----------------------------------------------------------------------===// +// Logical visitor. +//===----------------------------------------------------------------------===// +LVLogicalVisitor::LVLogicalVisitor(LVCodeViewReader *Reader, ScopedPrinter &W, + InputFile &Input) + : Reader(Reader), W(W), Input(Input) { + // The LogicalVisitor connects the CodeViewReader with the visitors that + // traverse the types, symbols, etc. Do any initialization that is needed. + Shared = new LVShared(Reader, this); +} +LVLogicalVisitor::~LVLogicalVisitor() { delete Shared; } + +void LVLogicalVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI, + uint32_t StreamIdx) { + codeview::printTypeIndex(W, FieldName, TI, + StreamIdx == StreamTPI ? types() : ids()); +} + +void LVLogicalVisitor::printTypeBegin(CVType &Record, TypeIndex TI, + LVElement *Element, uint32_t StreamIdx) { + W.getOStream() << "\n"; + W.startLine() << formatTypeLeafKind(Record.kind()); + W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")"; + W.getOStream() << " {\n"; + W.indent(); + W.printEnum("TypeLeafKind", unsigned(Record.kind()), + makeArrayRef(LeafTypeNames)); + printTypeIndex("TI", TI, StreamIdx); + W.startLine() << "Element: " << HexNumber(Element->getOffset()) << " " + << Element->getName() << "\n"; +} + +void LVLogicalVisitor::printTypeEnd(CVType &Record) { + W.unindent(); + W.startLine() << "}\n"; +} + +void LVLogicalVisitor::printMemberBegin(CVMemberRecord &Record, TypeIndex TI, + LVElement *Element, + uint32_t StreamIdx) { + W.getOStream() << "\n"; + W.startLine() << formatTypeLeafKind(Record.Kind); + W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")"; + W.getOStream() << " {\n"; + W.indent(); + W.printEnum("TypeLeafKind", unsigned(Record.Kind), + makeArrayRef(LeafTypeNames)); + printTypeIndex("TI", TI, StreamIdx); + W.startLine() << "Element: " << HexNumber(Element->getOffset()) << " " + << Element->getName() << "\n"; +} + +void LVLogicalVisitor::printMemberEnd(CVMemberRecord &Record) { + W.unindent(); + W.startLine() << "}\n"; +} + +Error LVLogicalVisitor::visitUnknownType(CVType &Record, TypeIndex TI) { + LLVM_DEBUG({ + printTypeIndex("\nTI", TI, StreamTPI); + W.printNumber("Length", uint32_t(Record.content().size())); + }); + return Error::success(); +} + +// LF_ARGLIST (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ArgListRecord &Args, + TypeIndex TI, LVElement *Element) { + ArrayRef Indices = Args.getIndices(); + uint32_t Size = Indices.size(); + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("NumArgs", Size); + ListScope Arguments(W, "Arguments"); + for (uint32_t I = 0; I < Size; ++I) + printTypeIndex("ArgType", Indices[I], StreamTPI); + printTypeEnd(Record); + }); + + LVScope *Function = static_cast(Element); + for (uint32_t Index = 0; Index < Size; ++Index) { + TypeIndex ParameterType = Indices[Index]; + createParameter(ParameterType, StringRef(), Function); + } + + return Error::success(); +} + +// LF_ARRAY (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ArrayRecord &AT, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ElementType", AT.getElementType(), StreamTPI); + printTypeIndex("IndexType", AT.getIndexType(), StreamTPI); + W.printNumber("SizeOf", AT.getSize()); + W.printString("Name", AT.getName()); + printTypeEnd(Record); + }); + + if (Element->getIsFinalized()) + return Error::success(); + Element->setIsFinalized(); + + LVScopeArray *Array = static_cast(Element); + if (!Array) + return Error::success(); + + Reader->getCompileUnit()->addElement(Array); + TypeIndex TIElementType = AT.getElementType(); + + LVType *PrevSubrange = nullptr; + LazyRandomTypeCollection &Types = types(); + + // As the logical view is modeled on DWARF, for each dimension we have to + // create a DW_TAG_subrange_type, with dimension size. + // The subrange type can be: unsigned __int32 or unsigned __int64. + auto AddSubrangeType = [&](ArrayRecord &AR) { + LVType *Subrange = new LVTypeSubrange(); + Subrange->setTag(dwarf::DW_TAG_subrange_type); + Subrange->setType(getElement(StreamTPI, AR.getIndexType())); + Subrange->setCount(AR.getSize()); + Subrange->setOffset( + TIElementType.isSimple() + ? (uint32_t)(TypeLeafKind)TIElementType.getSimpleKind() + : TIElementType.getIndex()); + Array->addElement(Subrange); + + if (PrevSubrange) + if (int64_t Count = Subrange->getCount()) + PrevSubrange->setCount(PrevSubrange->getCount() / Count); + PrevSubrange = Subrange; + }; + + // Preserve the original TypeIndex; it would be updated in the case of: + // - The array type contains qualifiers. + // - In multidimensional arrays, the last LF_ARRAY entry contains the type. + TypeIndex TIArrayType; + + // For each dimension in the array, there is a LF_ARRAY entry. The last + // entry contains the array type, which can be a LF_MODIFIER in the case + // of the type being modified by a qualifier (const, etc). + ArrayRecord AR(AT); + CVType CVEntry = Record; + while (CVEntry.kind() == LF_ARRAY) { + // Create the subrange information, required by the logical view. Once + // the array has been processed, the dimension sizes will updated, as + // the sizes are a progression. For instance: + // sizeof(int) = 4 + // int Array[2]; Sizes: 8 Dim: 8 / 4 -> [2] + // int Array[2][3]; Sizes: 24, 12 Dim: 24 / 12 -> [2] + // Dim: 12 / 4 -> [3] + // int Array[2][3][4]; sizes: 96, 48, 16 Dim: 96 / 48 -> [2] + // Dim: 48 / 16 -> [3] + // Dim: 16 / 4 -> [4] + AddSubrangeType(AR); + TIArrayType = TIElementType; + + // The current ElementType can be a modifier, in which case we need to + // get the type being modified. + // If TypeIndex is not a simple type, check if we have a qualified type. + if (!TIElementType.isSimple()) { + CVType CVElementType = Types.getType(TIElementType); + if (CVElementType.kind() == LF_MODIFIER) { + LVElement *QualifiedType = + Shared->TypeRecords.find(StreamTPI, TIElementType); + if (Error Err = + finishVisitation(CVElementType, TIElementType, QualifiedType)) + return Err; + // Get the TypeIndex of the type that the LF_MODIFIER modifies. + TIElementType = getModifiedType(CVElementType); + } + } + // Ends the traversal, as we have reached a simple type (int, char, etc). + if (TIElementType.isSimple()) + break; + + // Read next dimension linked entry, if any. + CVEntry = Types.getType(TIElementType); + if (Error Err = TypeDeserializer::deserializeAs( + const_cast(CVEntry), AR)) { + consumeError(std::move(Err)); + break; + } + TIElementType = AR.getElementType(); + // NOTE: The typeindex has a value of: 0x0280.0000 + getTrueType(TIElementType); + } + + Array->setName(AT.getName()); + TIArrayType = Shared->ForwardReferences.remap(TIArrayType); + Array->setType(getElement(StreamTPI, TIArrayType)); + + if (PrevSubrange) + // In the case of an aggregate type (class, struct, union, interface), + // get the aggregate size. As the original record is pointing to its + // reference, we have to update it. + if (uint64_t Size = + isAggregate(CVEntry) + ? getSizeInBytesForTypeRecord(Types.getType(TIArrayType)) + : getSizeInBytesForTypeIndex(TIElementType)) + PrevSubrange->setCount(PrevSubrange->getCount() / Size); + + return Error::success(); +} + +// LF_BITFIELD (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, BitFieldRecord &BF, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", TI, StreamTPI); + W.printNumber("BitSize", BF.getBitSize()); + W.printNumber("BitOffset", BF.getBitOffset()); + printTypeEnd(Record); + }); + + Element->setType(getElement(StreamTPI, BF.getType())); + Element->setBitSize(BF.getBitSize()); + return Error::success(); +} + +// LF_BUILDINFO (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, BuildInfoRecord &BI, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + W.printNumber("NumArgs", static_cast(BI.getArgs().size())); + ListScope Arguments(W, "Arguments"); + for (TypeIndex Arg : BI.getArgs()) + printTypeIndex("ArgType", Arg, StreamIPI); + printTypeEnd(Record); + }); + + // The given 'Element' refers to the current compilation unit. + // All the args are references into the TPI/IPI stream. + TypeIndex TIName = BI.getArgs()[BuildInfoRecord::BuildInfoArg::SourceFile]; + std::string Name = std::string(ids().getTypeName(TIName)); + + // There are cases where LF_BUILDINFO fields are empty. + if (!Name.empty()) + Element->setName(Name); + + return Error::success(); +} + +// LF_CLASS, LF_STRUCTURE, LF_INTERFACE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ClassRecord &Class, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("MemberCount", Class.getMemberCount()); + printTypeIndex("FieldList", Class.getFieldList(), StreamTPI); + printTypeIndex("DerivedFrom", Class.getDerivationList(), StreamTPI); + printTypeIndex("VShape", Class.getVTableShape(), StreamTPI); + W.printNumber("SizeOf", Class.getSize()); + W.printString("Name", Class.getName()); + if (Class.hasUniqueName()) + W.printString("UniqueName", Class.getUniqueName()); + printTypeEnd(Record); + }); + + if (Element->getIsFinalized()) + return Error::success(); + Element->setIsFinalized(); + + LVScopeAggregate *Scope = static_cast(Element); + if (!Scope) + return Error::success(); + + Scope->setName(Class.getName()); + if (Class.hasUniqueName()) + Scope->setLinkageName(Class.getUniqueName()); + + if (Class.isNested()) { + Scope->setIsNested(); + createParents(Class.getName(), Scope); + } + + if (Class.isScoped()) + Scope->setIsScoped(); + + // Nested types will be added to their parents at creation. The forward + // references are only processed to finish the referenced element creation. + if (!(Class.isNested() || Class.isScoped())) { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Class.getName())) + Namespace->addElement(Scope); + else + Reader->getCompileUnit()->addElement(Scope); + } + + LazyRandomTypeCollection &Types = types(); + TypeIndex TIFieldList = Class.getFieldList(); + if (TIFieldList.isNoneType()) { + TypeIndex ForwardType = Shared->ForwardReferences.find(Class.getName()); + if (!ForwardType.isNoneType()) { + CVType CVReference = Types.getType(ForwardType); + TypeRecordKind RK = static_cast(CVReference.kind()); + ClassRecord ReferenceRecord(RK); + if (Error Err = TypeDeserializer::deserializeAs( + const_cast(CVReference), ReferenceRecord)) + return Err; + TIFieldList = ReferenceRecord.getFieldList(); + } + } + + if (!TIFieldList.isNoneType()) { + // Pass down the TypeIndex 'TI' for the aggregate containing the field list. + CVType CVFieldList = Types.getType(TIFieldList); + if (Error Err = finishVisitation(CVFieldList, TI, Scope)) + return Err; + } + + return Error::success(); +} + +// LF_ENUM (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, EnumRecord &Enum, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("NumEnumerators", Enum.getMemberCount()); + printTypeIndex("UnderlyingType", Enum.getUnderlyingType(), StreamTPI); + printTypeIndex("FieldListType", Enum.getFieldList(), StreamTPI); + W.printString("Name", Enum.getName()); + printTypeEnd(Record); + }); + + LVScopeEnumeration *Scope = static_cast(Element); + if (!Scope) + return Error::success(); + + if (Scope->getIsFinalized()) + return Error::success(); + Scope->setIsFinalized(); + + // Set the name, as in the case of nested, it would determine the relation + // to any potential parent, via the LF_NESTTYPE record. + Scope->setName(Enum.getName()); + if (Enum.hasUniqueName()) + Scope->setLinkageName(Enum.getUniqueName()); + + Scope->setType(getElement(StreamTPI, Enum.getUnderlyingType())); + + if (Enum.isNested()) { + Scope->setIsNested(); + createParents(Enum.getName(), Scope); + } + + if (Enum.isScoped()) { + Scope->setIsScoped(); + Scope->setIsEnumClass(); + } + + // Nested types will be added to their parents at creation. + if (!(Enum.isNested() || Enum.isScoped())) { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Enum.getName())) + Namespace->addElement(Scope); + else + Reader->getCompileUnit()->addElement(Scope); + } + + TypeIndex TIFieldList = Enum.getFieldList(); + if (!TIFieldList.isNoneType()) { + LazyRandomTypeCollection &Types = types(); + CVType CVFieldList = Types.getType(TIFieldList); + if (Error Err = finishVisitation(CVFieldList, TIFieldList, Scope)) + return Err; + } + + return Error::success(); +} + +// LF_FIELDLIST (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + FieldListRecord &FieldList, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeEnd(Record); + }); + + if (Error Err = visitFieldListMemberStream(TI, Element, FieldList.Data)) + return Err; + + return Error::success(); +} + +// LF_FUNC_ID (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, FuncIdRecord &Func, + TypeIndex TI, LVElement *Element) { + // ParentScope and FunctionType are references into the TPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + printTypeIndex("ParentScope", Func.getParentScope(), StreamTPI); + printTypeIndex("FunctionType", Func.getFunctionType(), StreamTPI); + W.printString("Name", Func.getName()); + printTypeEnd(Record); + }); + + // The TypeIndex (LF_PROCEDURE) returned by 'getFunctionType' is the + // function propotype, we need to use the function definition. + if (LVScope *FunctionDcl = static_cast(Element)) { + // For inlined functions, the inlined instance has been already processed + // (all its information is contained in the Symbols section). + // 'Element' points to the created 'abstract' (out-of-line) function. + // Use the parent scope information to allocate it to the correct scope. + LazyRandomTypeCollection &Types = types(); + TypeIndex TIParent = Func.getParentScope(); + if (FunctionDcl->getIsInlinedAbstract()) { + FunctionDcl->setName(Func.getName()); + if (TIParent.isNoneType()) + Reader->getCompileUnit()->addElement(FunctionDcl); + } + + if (!TIParent.isNoneType()) { + CVType CVParentScope = ids().getType(TIParent); + if (Error Err = finishVisitation(CVParentScope, TIParent, FunctionDcl)) + return Err; + } + + TypeIndex TIFunctionType = Func.getFunctionType(); + CVType CVFunctionType = Types.getType(TIFunctionType); + if (Error Err = + finishVisitation(CVFunctionType, TIFunctionType, FunctionDcl)) + return Err; + + FunctionDcl->setIsFinalized(); + } + + return Error::success(); +} + +// LF_LABEL (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, LabelRecord &LR, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_MFUNC_ID (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, MemberFuncIdRecord &Id, + TypeIndex TI, LVElement *Element) { + // ClassType and FunctionType are references into the TPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + printTypeIndex("ClassType", Id.getClassType(), StreamTPI); + printTypeIndex("FunctionType", Id.getFunctionType(), StreamTPI); + W.printString("Name", Id.getName()); + printTypeEnd(Record); + }); + + LVScope *FunctionDcl = static_cast(Element); + if (FunctionDcl->getIsInlinedAbstract()) { + // For inlined functions, the inlined instance has been already processed + // (all its information is contained in the Symbols section). + // 'Element' points to the created 'abstract' (out-of-line) function. + // Use the parent scope information to allocate it to the correct scope. + if (LVScope *Class = static_cast( + Shared->TypeRecords.find(StreamTPI, Id.getClassType()))) + Class->addElement(FunctionDcl); + } + + TypeIndex TIFunctionType = Id.getFunctionType(); + CVType CVFunction = types().getType(TIFunctionType); + if (Error Err = finishVisitation(CVFunction, TIFunctionType, Element)) + return Err; + + return Error::success(); +} + +// LF_MFUNCTION (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + MemberFunctionRecord &MF, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ReturnType", MF.getReturnType(), StreamTPI); + printTypeIndex("ClassType", MF.getClassType(), StreamTPI); + printTypeIndex("ThisType", MF.getThisType(), StreamTPI); + W.printNumber("NumParameters", MF.getParameterCount()); + printTypeIndex("ArgListType", MF.getArgumentList(), StreamTPI); + W.printNumber("ThisAdjustment", MF.getThisPointerAdjustment()); + printTypeEnd(Record); + }); + + if (LVScope *MemberFunction = static_cast(Element)) { + LVElement *Class = getElement(StreamTPI, MF.getClassType()); + + MemberFunction->setIsFinalized(); + MemberFunction->setType(getElement(StreamTPI, MF.getReturnType())); + MemberFunction->setOffset(TI.getIndex()); + MemberFunction->setOffsetFromTypeIndex(); + + if (ProcessArgumentList) { + ProcessArgumentList = false; + + if (!MemberFunction->getIsStatic()) { + LVElement *ThisPointer = getElement(StreamTPI, MF.getThisType()); + // When creating the 'this' pointer, check if it points to a reference. + ThisPointer->setType(Class); + LVSymbol *This = + createParameter(ThisPointer, StringRef(), MemberFunction); + This->setIsArtificial(); + } + + // Create formal parameters. + LazyRandomTypeCollection &Types = types(); + CVType CVArguments = Types.getType(MF.getArgumentList()); + if (Error Err = finishVisitation(CVArguments, MF.getArgumentList(), + MemberFunction)) + return Err; + } + } + + return Error::success(); +} + +// LF_METHODLIST (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + MethodOverloadListRecord &Overloads, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeEnd(Record); + }); + + for (OneMethodRecord &Method : Overloads.Methods) { + CVMemberRecord Record; + Record.Kind = LF_METHOD; + Method.Name = OverloadedMethodName; + if (Error Err = visitKnownMember(Record, Method, TI, Element)) + return Err; + } + + return Error::success(); +} + +// LF_MODIFIER (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ModifierRecord &Mod, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ModifiedType", Mod.getModifiedType(), StreamTPI); + printTypeEnd(Record); + }); + + // Create the modified type, which will be attached to the type(s) that + // contains the modifiers. + LVElement *ModifiedType = getElement(StreamTPI, Mod.getModifiedType()); + + // At this point the types recording the qualifiers do not have a + // scope parent. They must be assigned to the current compile unit. + LVScopeCompileUnit *CompileUnit = Reader->getCompileUnit(); + + // The incomming element, does not have a defined kind. Use the given + // modifiers to complete its type. A type can have more than one modifier; + // in that case, we have to create an extra type to have the other modifier. + LVType *LastLink = static_cast(Element); + if (!LastLink->getParentScope()) + CompileUnit->addElement(LastLink); + + bool SeenModifier = false; + uint16_t Mods = static_cast(Mod.getModifiers()); + if (Mods & uint16_t(ModifierOptions::Const)) { + SeenModifier = true; + LastLink->setTag(dwarf::DW_TAG_const_type); + LastLink->setIsConst(); + LastLink->setName("const"); + } + if (Mods & uint16_t(ModifierOptions::Volatile)) { + if (SeenModifier) { + LVType *Volatile = new LVType(); + Volatile->setIsModifier(); + LastLink->setType(Volatile); + LastLink = Volatile; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_volatile_type); + LastLink->setIsVolatile(); + LastLink->setName("volatile"); + } + if (Mods & uint16_t(ModifierOptions::Unaligned)) { + if (SeenModifier) { + LVType *Unaligned = new LVType(); + Unaligned->setIsModifier(); + LastLink->setType(Unaligned); + LastLink = Unaligned; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_unaligned); + LastLink->setIsUnaligned(); + LastLink->setName("unaligned"); + } + + LastLink->setType(ModifiedType); + return Error::success(); +} + +// LF_POINTER (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, PointerRecord &Ptr, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("PointeeType", Ptr.getReferentType(), StreamTPI); + W.printNumber("IsFlat", Ptr.isFlat()); + W.printNumber("IsConst", Ptr.isConst()); + W.printNumber("IsVolatile", Ptr.isVolatile()); + W.printNumber("IsUnaligned", Ptr.isUnaligned()); + W.printNumber("IsRestrict", Ptr.isRestrict()); + W.printNumber("IsThisPtr&", Ptr.isLValueReferenceThisPtr()); + W.printNumber("IsThisPtr&&", Ptr.isRValueReferenceThisPtr()); + W.printNumber("SizeOf", Ptr.getSize()); + + if (Ptr.isPointerToMember()) { + const MemberPointerInfo &MI = Ptr.getMemberInfo(); + printTypeIndex("ClassType", MI.getContainingType(), StreamTPI); + } + printTypeEnd(Record); + }); + + // Type pointer is point to. + LVType *Pointer = static_cast(Element); + LVElement *Pointee = nullptr; + + PointerMode Mode = Ptr.getMode(); + Pointee = Ptr.isPointerToMember() + ? Shared->TypeRecords.find(StreamTPI, Ptr.getReferentType()) + : getElement(StreamTPI, Ptr.getReferentType()); + + // At this point the types recording the qualifiers do not have a + // scope parent. They must be assigned to the current compile unit. + LVScopeCompileUnit *CompileUnit = Reader->getCompileUnit(); + + // Order for the different modifiers: + // + // Const and volatile already processed. + bool SeenModifier = false; + LVType *LastLink = Pointer; + if (!LastLink->getParentScope()) + CompileUnit->addElement(LastLink); + + if (Ptr.isRestrict()) { + SeenModifier = true; + LVType *Restrict = new LVType(); + Restrict->setTag(dwarf::DW_TAG_restrict_type); + Restrict->setIsRestrict(); + Restrict->setName("restrict"); + LastLink->setType(Restrict); + LastLink = Restrict; + CompileUnit->addElement(LastLink); + } + if (Mode == PointerMode::LValueReference) { + if (SeenModifier) { + LVType *LReference = new LVType(); + LReference->setIsModifier(); + LastLink->setType(LReference); + LastLink = LReference; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_reference_type); + LastLink->setIsReference(); + LastLink->setName("&"); + } + if (Mode == PointerMode::RValueReference) { + if (SeenModifier) { + LVType *RReference = new LVType(); + RReference->setIsModifier(); + LastLink->setType(RReference); + LastLink = RReference; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_rvalue_reference_type); + LastLink->setIsRvalueReference(); + LastLink->setName("&&"); + } + + // When creating the pointer, check if it points to a reference. + LastLink->setType(Pointee); + return Error::success(); +} + +// LF_PROCEDURE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ProcedureRecord &Proc, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ReturnType", Proc.getReturnType(), StreamTPI); + W.printNumber("NumParameters", Proc.getParameterCount()); + printTypeIndex("ArgListType", Proc.getArgumentList(), StreamTPI); + printTypeEnd(Record); + }); + + // There is no need to traverse the argument list, as the CodeView format + // declares the parameters as a 'S_LOCAL' symbol tagged as parameter. + // Only process parameters when dealing with inline functions. + if (LVScope *FunctionDcl = static_cast(Element)) { + FunctionDcl->setType(getElement(StreamTPI, Proc.getReturnType())); + + if (ProcessArgumentList) { + ProcessArgumentList = false; + // Create formal parameters. + LazyRandomTypeCollection &Types = types(); + CVType CVArguments = Types.getType(Proc.getArgumentList()); + if (Error Err = finishVisitation(CVArguments, Proc.getArgumentList(), + FunctionDcl)) + return Err; + } + } + + return Error::success(); +} + +// LF_UNION (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, UnionRecord &Union, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("MemberCount", Union.getMemberCount()); + printTypeIndex("FieldList", Union.getFieldList(), StreamTPI); + W.printNumber("SizeOf", Union.getSize()); + W.printString("Name", Union.getName()); + if (Union.hasUniqueName()) + W.printString("UniqueName", Union.getUniqueName()); + printTypeEnd(Record); + }); + + LVScopeAggregate *Scope = static_cast(Element); + if (!Scope) + return Error::success(); + + if (Scope->getIsFinalized()) + return Error::success(); + Scope->setIsFinalized(); + + Scope->setName(Union.getName()); + if (Union.hasUniqueName()) + Scope->setLinkageName(Union.getUniqueName()); + + if (Union.isNested()) { + Scope->setIsNested(); + createParents(Union.getName(), Scope); + } else { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Union.getName())) + Namespace->addElement(Scope); + else + Reader->getCompileUnit()->addElement(Scope); + } + + if (!Union.getFieldList().isNoneType()) { + LazyRandomTypeCollection &Types = types(); + // Pass down the TypeIndex 'TI' for the aggregate containing the field list. + CVType CVFieldList = Types.getType(Union.getFieldList()); + if (Error Err = finishVisitation(CVFieldList, TI, Scope)) + return Err; + } + + return Error::success(); +} + +// LF_TYPESERVER2 (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, TypeServer2Record &TS, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printString("Guid", formatv("{0}", TS.getGuid()).str()); + W.printNumber("Age", TS.getAge()); + W.printString("Name", TS.getName()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_VFTABLE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, VFTableRecord &VFT, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("CompleteClass", VFT.getCompleteClass(), StreamTPI); + printTypeIndex("OverriddenVFTable", VFT.getOverriddenVTable(), StreamTPI); + W.printHex("VFPtrOffset", VFT.getVFPtrOffset()); + W.printString("VFTableName", VFT.getName()); + for (const StringRef &N : VFT.getMethodNames()) + W.printString("MethodName", N); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_VTSHAPE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + VFTableShapeRecord &Shape, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("VFEntryCount", Shape.getEntryCount()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_SUBSTR_LIST (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + StringListRecord &Strings, + TypeIndex TI, LVElement *Element) { + // All the indices are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + ArrayRef Indices = Strings.getIndices(); + uint32_t Size = Indices.size(); + W.printNumber("NumStrings", Size); + ListScope Arguments(W, "Strings"); + for (uint32_t I = 0; I < Size; ++I) + printTypeIndex("String", Indices[I], StreamIPI); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_STRING_ID (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, StringIdRecord &String, + TypeIndex TI, LVElement *Element) { + // All args are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeIndex("\nTI", TI, StreamIPI); + printTypeIndex("Id", String.getId(), StreamIPI); + W.printString("StringData", String.getString()); + }); + + if (LVScope *Namespace = Shared->NamespaceDeduction.get( + String.getString(), /*CheckScope=*/false)) { + // The function is already at different scope. In order to reflect + // the correct parent, move it to the namespace. + if (LVScope *Scope = Element->getParentScope()) + Scope->removeElement(Element); + Namespace->addElement(Element); + } + + return Error::success(); +} + +// LF_UDT_SRC_LINE (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + UdtSourceLineRecord &SourceLine, + TypeIndex TI, LVElement *Element) { + // All args are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeIndex("\nTI", TI, StreamIPI); + printTypeIndex("UDT", SourceLine.getUDT(), StreamIPI); + printTypeIndex("SourceFile", SourceLine.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", SourceLine.getLineNumber()); + }); + return Error::success(); +} + +// LF_UDT_MOD_SRC_LINE (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + UdtModSourceLineRecord &ModSourceLine, + TypeIndex TI, LVElement *Element) { + // All args are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + printTypeIndex("\nTI", TI, StreamIPI); + printTypeIndex("UDT", ModSourceLine.getUDT(), StreamIPI); + printTypeIndex("SourceFile", ModSourceLine.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", ModSourceLine.getLineNumber()); + W.printNumber("Module", ModSourceLine.getModule()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_PRECOMP (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, PrecompRecord &Precomp, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printHex("StartIndex", Precomp.getStartTypeIndex()); + W.printHex("Count", Precomp.getTypesCount()); + W.printHex("Signature", Precomp.getSignature()); + W.printString("PrecompFile", Precomp.getPrecompFilePath()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_ENDPRECOMP (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + EndPrecompRecord &EndPrecomp, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printHex("Signature", EndPrecomp.getSignature()); + printTypeEnd(Record); + }); + return Error::success(); +} + +Error LVLogicalVisitor::visitUnknownMember(CVMemberRecord &Record, + TypeIndex TI) { + LLVM_DEBUG({ W.printHex("UnknownMember", unsigned(Record.Kind)); }); + return Error::success(); +} + +// LF_BCLASS, LF_BINTERFACE +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + BaseClassRecord &Base, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("BaseType", Base.getBaseType(), StreamTPI); + W.printHex("BaseOffset", Base.getBaseOffset()); + printMemberEnd(Record); + }); + + createElement(Record.Kind); + if (LVSymbol *Symbol = CurrentSymbol) { + LVElement *BaseClass = getElement(StreamTPI, Base.getBaseType()); + Symbol->setName(BaseClass->getName()); + Symbol->setType(BaseClass); + Symbol->setAccessibilityCode(Base.getAccess()); + static_cast(Element)->addElement(Symbol); + } + + return Error::success(); +} + +// LF_MEMBER +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + DataMemberRecord &Field, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Field.getType(), StreamTPI); + W.printHex("FieldOffset", Field.getFieldOffset()); + W.printString("Name", Field.getName()); + printMemberEnd(Record); + }); + + // Create the data member. + createDataMember(Record, static_cast(Element), Field.getName(), + Field.getType(), Field.getAccess()); + return Error::success(); +} + +// LF_ENUMERATE +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + EnumeratorRecord &Enum, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + W.printNumber("EnumValue", Enum.getValue()); + W.printString("Name", Enum.getName()); + printMemberEnd(Record); + }); + + createElement(Record.Kind); + if (LVType *Type = CurrentType) { + Type->setName(Enum.getName()); + SmallString<16> Value; + Enum.getValue().toString(Value, 16, true, true); + Type->setValue(Value); + static_cast(Element)->addElement(CurrentType); + } + + return Error::success(); +} + +// LF_INDEX +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + ListContinuationRecord &Cont, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ContinuationIndex", Cont.getContinuationIndex(), StreamTPI); + printMemberEnd(Record); + }); + return Error::success(); +} + +// LF_NESTTYPE +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + NestedTypeRecord &Nested, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Nested.getNestedType(), StreamTPI); + W.printString("Name", Nested.getName()); + printMemberEnd(Record); + }); + + if (LVElement *Typedef = createElement(SymbolKind::S_UDT)) { + Typedef->setName(Nested.getName()); + LVElement *NestedType = getElement(StreamTPI, Nested.getNestedType()); + Typedef->setType(NestedType); + LVScope *Scope = static_cast(Element); + Scope->addElement(Typedef); + + if (NestedType && NestedType->getIsNested()) { + // 'Element' is an aggregate type that may contains this nested type + // definition. Used their scoped names, to decide on their relationship. + StringRef RecordName = getRecordName(types(), TI); + + StringRef NestedTypeName = NestedType->getName(); + if (NestedTypeName.size() && RecordName.size()) { + StringRef OuterComponent; + std::tie(OuterComponent, std::ignore) = + getInnerComponent(NestedTypeName); + // We have an already created nested type. Add it to the current scope + // and update all its children if any. + if (OuterComponent.size() && OuterComponent.equals(RecordName)) { + if (!NestedType->getIsScopedAlready()) { + Scope->addElement(NestedType); + NestedType->setIsScopedAlready(); + NestedType->updateLevel(Scope); + } + Typedef->resetIncludeInPrint(); + } + } + } + } + + return Error::success(); +} + +// LF_ONEMETHOD +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + OneMethodRecord &Method, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Method.getType(), StreamTPI); + // If virtual, then read the vftable offset. + if (Method.isIntroducingVirtual()) + W.printHex("VFTableOffset", Method.getVFTableOffset()); + W.printString("Name", Method.getName()); + printMemberEnd(Record); + }); + + // All the LF_ONEMETHOD objects share the same type description. + // We have to create a scope object for each one and get the required + // information from the LF_MFUNCTION object. + ProcessArgumentList = true; + if (LVElement *MemberFunction = createElement(TypeLeafKind::LF_ONEMETHOD)) { + MemberFunction->setIsFinalized(); + static_cast(Element)->addElement(MemberFunction); + + MemberFunction->setName(Method.getName()); + MemberFunction->setAccessibilityCode(Method.getAccess()); + + MethodKind Kind = Method.getMethodKind(); + if (Kind == MethodKind::Static) + MemberFunction->setIsStatic(); + MemberFunction->setVirtualityCode(Kind); + + MethodOptions Flags = Method.Attrs.getFlags(); + if (MethodOptions::CompilerGenerated == + (Flags & MethodOptions::CompilerGenerated)) + MemberFunction->setIsArtificial(); + + LazyRandomTypeCollection &Types = types(); + CVType CVMethodType = Types.getType(Method.getType()); + if (Error Err = + finishVisitation(CVMethodType, Method.getType(), MemberFunction)) + return Err; + } + ProcessArgumentList = false; + + return Error::success(); +} + +// LF_METHOD +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + OverloadedMethodRecord &Method, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + W.printHex("MethodCount", Method.getNumOverloads()); + printTypeIndex("MethodListIndex", Method.getMethodList(), StreamTPI); + W.printString("Name", Method.getName()); + printMemberEnd(Record); + }); + + // Record the overloaded method name, which will be used during the + // traversal of the method list. + LazyRandomTypeCollection &Types = types(); + OverloadedMethodName = Method.getName(); + CVType CVMethods = Types.getType(Method.getMethodList()); + if (Error Err = finishVisitation(CVMethods, Method.getMethodList(), Element)) + return Err; + + return Error::success(); +} + +// LF_STMEMBER +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + StaticDataMemberRecord &Field, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Field.getType(), StreamTPI); + W.printString("Name", Field.getName()); + printMemberEnd(Record); + }); + + // Create the data member. + createDataMember(Record, static_cast(Element), Field.getName(), + Field.getType(), Field.getAccess()); + return Error::success(); +} + +// LF_VFUNCTAB +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + VFPtrRecord &VFTable, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", VFTable.getType(), StreamTPI); + printMemberEnd(Record); + }); + return Error::success(); +} + +// LF_VBCLASS, LF_IVBCLASS +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + VirtualBaseClassRecord &Base, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("BaseType", Base.getBaseType(), StreamTPI); + printTypeIndex("VBPtrType", Base.getVBPtrType(), StreamTPI); + W.printHex("VBPtrOffset", Base.getVBPtrOffset()); + W.printHex("VBTableIndex", Base.getVTableIndex()); + printMemberEnd(Record); + }); + + createElement(Record.Kind); + if (LVSymbol *Symbol = CurrentSymbol) { + LVElement *BaseClass = getElement(StreamTPI, Base.getBaseType()); + Symbol->setName(BaseClass->getName()); + Symbol->setType(BaseClass); + Symbol->setAccessibilityCode(Base.getAccess()); + Symbol->setVirtualityCode(MethodKind::Virtual); + static_cast(Element)->addElement(Symbol); + } + + return Error::success(); +} + +Error LVLogicalVisitor::visitMemberRecord(CVMemberRecord &Record, + TypeVisitorCallbacks &Callbacks, + TypeIndex TI, LVElement *Element) { + if (Error Err = Callbacks.visitMemberBegin(Record)) + return Err; + + switch (Record.Kind) { + default: + if (Error Err = Callbacks.visitUnknownMember(Record)) + return Err; + break; +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (Error Err = \ + visitKnownMember(Record, Callbacks, TI, Element)) \ + return Err; \ + break; \ + } +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + MEMBER_RECORD(EnumVal, EnumVal, AliasName) +#define TYPE_RECORD(EnumName, EnumVal, Name) +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + } + + if (Error Err = Callbacks.visitMemberEnd(Record)) + return Err; + + return Error::success(); +} + +Error LVLogicalVisitor::finishVisitation(CVType &Record, TypeIndex TI, + LVElement *Element) { + switch (Record.kind()) { + default: + if (Error Err = visitUnknownType(Record, TI)) + return Err; + break; +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (Error Err = visitKnownRecord(Record, TI, Element)) \ + return Err; \ + break; \ + } +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + TYPE_RECORD(EnumVal, EnumVal, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + } + + return Error::success(); +} + +// Customized version of 'FieldListVisitHelper'. +Error LVLogicalVisitor::visitFieldListMemberStream( + TypeIndex TI, LVElement *Element, ArrayRef FieldList) { + BinaryByteStream Stream(FieldList, llvm::support::little); + BinaryStreamReader Reader(Stream); + FieldListDeserializer Deserializer(Reader); + TypeVisitorCallbackPipeline Pipeline; + Pipeline.addCallbackToPipeline(Deserializer); + + TypeLeafKind Leaf; + while (!Reader.empty()) { + if (Error Err = Reader.readEnum(Leaf)) + return Err; + + CVMemberRecord Record; + Record.Kind = Leaf; + if (Error Err = visitMemberRecord(Record, Pipeline, TI, Element)) + return Err; + } + + return Error::success(); +} + +void LVLogicalVisitor::addElement(LVScope *Scope, bool IsCompileUnit) { + // The CodeView specifications does not treat S_COMPILE2 and S_COMPILE3 + // as symbols that open a scope. The CodeView reader, treat them in a + // similar way as DWARF. As there is no a symbole S_END to close the + // compile unit, we need to check for the next compile unit. + if (IsCompileUnit) { + if (!ScopeStack.empty()) + popScope(); + InCompileUnitScope = true; + } + + pushScope(Scope); + ReaderParent->addElement(Scope); +} + +void LVLogicalVisitor::addElement(LVSymbol *Symbol) { + ReaderScope->addElement(Symbol); +} + +void LVLogicalVisitor::addElement(LVType *Type) { + ReaderScope->addElement(Type); +} + +LVElement *LVLogicalVisitor::createElement(TypeLeafKind Kind) { + CurrentScope = nullptr; + CurrentSymbol = nullptr; + CurrentType = nullptr; + + if (Kind < TypeIndex::FirstNonSimpleIndex) { + CurrentType = new LVType(); + CurrentType->setIsBase(); + CurrentType->setTag(dwarf::DW_TAG_base_type); + if (options().getAttributeBase()) + CurrentType->setIncludeInPrint(); + return CurrentType; + } + + switch (Kind) { + // Types. + case TypeLeafKind::LF_ENUMERATE: + CurrentType = new LVTypeEnumerator(); + CurrentType->setTag(dwarf::DW_TAG_enumerator); + return CurrentType; + case TypeLeafKind::LF_MODIFIER: + CurrentType = new LVType(); + CurrentType->setIsModifier(); + return CurrentType; + case TypeLeafKind::LF_POINTER: + CurrentType = new LVType(); + CurrentType->setIsPointer(); + CurrentType->setName("*"); + CurrentType->setTag(dwarf::DW_TAG_pointer_type); + return CurrentType; + + // Symbols. + case TypeLeafKind::LF_BCLASS: + case TypeLeafKind::LF_IVBCLASS: + case TypeLeafKind::LF_VBCLASS: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setTag(dwarf::DW_TAG_inheritance); + CurrentSymbol->setIsInheritance(); + return CurrentSymbol; + case TypeLeafKind::LF_MEMBER: + case TypeLeafKind::LF_STMEMBER: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsMember(); + CurrentSymbol->setTag(dwarf::DW_TAG_member); + return CurrentSymbol; + + // Scopes. + case TypeLeafKind::LF_ARRAY: + CurrentScope = new LVScopeArray(); + CurrentScope->setTag(dwarf::DW_TAG_array_type); + return CurrentScope; + case TypeLeafKind::LF_CLASS: + CurrentScope = new LVScopeAggregate(); + CurrentScope->setTag(dwarf::DW_TAG_class_type); + CurrentScope->setIsClass(); + return CurrentScope; + case TypeLeafKind::LF_ENUM: + CurrentScope = new LVScopeEnumeration(); + CurrentScope->setTag(dwarf::DW_TAG_enumeration_type); + return CurrentScope; + case TypeLeafKind::LF_METHOD: + case TypeLeafKind::LF_ONEMETHOD: + case TypeLeafKind::LF_PROCEDURE: + CurrentScope = new LVScopeFunction(); + CurrentScope->setIsSubprogram(); + CurrentScope->setTag(dwarf::DW_TAG_subprogram); + return CurrentScope; + case TypeLeafKind::LF_STRUCTURE: + CurrentScope = new LVScopeAggregate(); + CurrentScope->setIsStructure(); + CurrentScope->setTag(dwarf::DW_TAG_structure_type); + return CurrentScope; + case TypeLeafKind::LF_UNION: + CurrentScope = new LVScopeAggregate(); + CurrentScope->setIsUnion(); + CurrentScope->setTag(dwarf::DW_TAG_union_type); + return CurrentScope; + default: + // If '--internal=tag' and '--print=warning' are specified in the command + // line, we record and print each seen 'TypeLeafKind'. + break; + } + return nullptr; +} + +LVElement *LVLogicalVisitor::createElement(SymbolKind Kind) { + CurrentScope = nullptr; + CurrentSymbol = nullptr; + CurrentType = nullptr; + switch (Kind) { + // Types. + case SymbolKind::S_UDT: + CurrentType = new LVTypeDefinition(); + CurrentType->setTag(dwarf::DW_TAG_typedef); + return CurrentType; + + // Symbols. + case SymbolKind::S_CONSTANT: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsConstant(); + CurrentSymbol->setTag(dwarf::DW_TAG_constant); + return CurrentSymbol; + + case SymbolKind::S_BPREL32: + case SymbolKind::S_REGREL32: + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + case SymbolKind::S_LOCAL: + // During the symbol traversal more information is available to + // determine if the symbol is a parameter or a variable. At this + // stage mark it as variable. + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsVariable(); + CurrentSymbol->setTag(dwarf::DW_TAG_variable); + return CurrentSymbol; + + // Scopes. + case SymbolKind::S_BLOCK32: + CurrentScope = new LVScope(); + CurrentScope->setIsLexicalBlock(); + CurrentScope->setTag(dwarf::DW_TAG_lexical_block); + return CurrentScope; + case SymbolKind::S_COMPILE2: + case SymbolKind::S_COMPILE3: + CurrentScope = new LVScopeCompileUnit(); + CurrentScope->setTag(dwarf::DW_TAG_compile_unit); + Reader->setCompileUnit(static_cast(CurrentScope)); + return CurrentScope; + case SymbolKind::S_INLINESITE: + case SymbolKind::S_INLINESITE2: + CurrentScope = new LVScopeFunctionInlined(); + CurrentScope->setIsInlinedFunction(); + CurrentScope->setTag(dwarf::DW_TAG_inlined_subroutine); + return CurrentScope; + case SymbolKind::S_LPROC32: + case SymbolKind::S_GPROC32: + case SymbolKind::S_LPROC32_ID: + case SymbolKind::S_GPROC32_ID: + case SymbolKind::S_SEPCODE: + case SymbolKind::S_THUNK32: + CurrentScope = new LVScopeFunction(); + CurrentScope->setIsSubprogram(); + CurrentScope->setTag(dwarf::DW_TAG_subprogram); + return CurrentScope; + default: + // If '--internal=tag' and '--print=warning' are specified in the command + // line, we record and print each seen 'SymbolKind'. + break; + } + return nullptr; +} + +LVElement *LVLogicalVisitor::createElement(TypeIndex TI, TypeLeafKind Kind) { + LVElement *Element = Shared->TypeRecords.find(StreamTPI, TI); + if (!Element) { + // We are dealing with a base type or pointer to a base type, which are + // not included explicitly in the CodeView format. + if (Kind < TypeIndex::FirstNonSimpleIndex) { + Element = createElement(Kind); + Element->setIsFinalized(); + Shared->TypeRecords.add(StreamTPI, (TypeIndex)Kind, Kind, Element); + Element->setOffset(Kind); + return Element; + } + // We are dealing with a pointer to a base type. + if (TI.getIndex() < TypeIndex::FirstNonSimpleIndex) { + Element = createElement(Kind); + Shared->TypeRecords.add(StreamTPI, TI, Kind, Element); + Element->setOffset(TI.getIndex()); + Element->setOffsetFromTypeIndex(); + return Element; + } + + W.printString("** Not implemented. **"); + printTypeIndex("TypeIndex", TI, StreamTPI); + W.printString("TypeLeafKind", formatTypeLeafKind(Kind)); + return nullptr; + } + + Element->setOffset(TI.getIndex()); + Element->setOffsetFromTypeIndex(); + return Element; +} + +void LVLogicalVisitor::createDataMember(CVMemberRecord &Record, LVScope *Parent, + StringRef Name, TypeIndex TI, + MemberAccess Access) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", TI, StreamTPI); + W.printString("TypeName", Name); + }); + + createElement(Record.Kind); + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Name); + if (TI.isNoneType() || TI.isSimple()) + Symbol->setType(getElement(StreamTPI, TI)); + else { + LazyRandomTypeCollection &Types = types(); + CVType CVMemberType = Types.getType(TI); + if (CVMemberType.kind() == LF_BITFIELD) { + if (Error Err = finishVisitation(CVMemberType, TI, Symbol)) { + consumeError(std::move(Err)); + return; + } + } else + Symbol->setType(getElement(StreamTPI, TI)); + } + Symbol->setAccessibilityCode(Access); + Parent->addElement(Symbol); + } +} + +LVSymbol *LVLogicalVisitor::createParameter(LVElement *Element, StringRef Name, + LVScope *Parent) { + LVSymbol *Parameter = new LVSymbol(); + Parent->addElement(Parameter); + Parameter->setIsParameter(); + Parameter->setTag(dwarf::DW_TAG_formal_parameter); + Parameter->setName(Name); + Parameter->setType(Element); + return Parameter; +} + +LVSymbol *LVLogicalVisitor::createParameter(TypeIndex TI, StringRef Name, + LVScope *Parent) { + return createParameter(getElement(StreamTPI, TI), Name, Parent); +} + +LVType *LVLogicalVisitor::createBaseType(TypeIndex TI, StringRef TypeName) { + TypeLeafKind SimpleKind = (TypeLeafKind)TI.getSimpleKind(); + TypeIndex TIR = (TypeIndex)SimpleKind; + LLVM_DEBUG({ + printTypeIndex("TypeIndex", TIR, StreamTPI); + W.printString("TypeName", TypeName); + }); + + if (LVElement *Element = Shared->TypeRecords.find(StreamTPI, TIR)) + return static_cast(Element); + + if (createElement(TIR, SimpleKind)) { + CurrentType->setName(TypeName); + Reader->getCompileUnit()->addElement(CurrentType); + } + return CurrentType; +} + +LVType *LVLogicalVisitor::createPointerType(TypeIndex TI, StringRef TypeName) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", TI, StreamTPI); + W.printString("TypeName", TypeName); + }); + + if (LVElement *Element = Shared->TypeRecords.find(StreamTPI, TI)) + return static_cast(Element); + + LVType *Pointee = createBaseType(TI, TypeName.drop_back(1)); + if (createElement(TI, TypeLeafKind::LF_POINTER)) { + CurrentType->setIsFinalized(); + CurrentType->setType(Pointee); + Reader->getCompileUnit()->addElement(CurrentType); + } + return CurrentType; +} + +void LVLogicalVisitor::createParents(StringRef ScopedName, LVElement *Element) { + // For the given test case: + // + // struct S { enum E { ... }; }; + // S::E V; + // + // 0 | S_LOCAL `V` + // type=0x1004 (S::E), flags = none + // 0x1004 | LF_ENUM `S::E` + // options: has unique name | is nested + // 0x1009 | LF_STRUCTURE `S` + // options: contains nested class + // + // When the local 'V' is processed, its type 'E' is created. But There is + // no direct reference to its parent 'S'. We use the scoped name for 'E', + // to create its parents. + + // The input scoped name must have at least parent and nested names. + // Drop the last element name, as it corresponds to the nested type. + LVStringRefs Components = getAllLexicalComponents(ScopedName); + if (Components.size() < 2) + return; + Components.pop_back(); + + LVStringRefs::size_type FirstNamespace; + LVStringRefs::size_type FirstAggregate; + std::tie(FirstNamespace, FirstAggregate) = + Shared->NamespaceDeduction.find(Components); + + LLVM_DEBUG({ + W.printString("First Namespace", Components[FirstNamespace]); + W.printString("First NonNamespace", Components[FirstAggregate]); + }); + + // Create any referenced namespaces. + if (FirstNamespace < FirstAggregate) { + Shared->NamespaceDeduction.get( + LVStringRefs(Components.begin() + FirstNamespace, + Components.begin() + FirstAggregate)); + } + + // Traverse the enclosing scopes (aggregates) and create them. In the + // case of nested empty aggregates, MSVC does not emit a full record + // description. It emits only the reference record. + LVScope *Aggregate = nullptr; + TypeIndex TIAggregate; + std::string AggregateName = getScopedName( + LVStringRefs(Components.begin(), Components.begin() + FirstAggregate)); + + // This traversal is executed at least once. + for (LVStringRefs::size_type Index = FirstAggregate; + Index < Components.size(); ++Index) { + AggregateName = getScopedName(LVStringRefs(Components.begin() + Index, + Components.begin() + Index + 1), + AggregateName); + TIAggregate = Shared->ForwardReferences.remap( + Shared->TypeRecords.find(StreamTPI, AggregateName)); + Aggregate = + TIAggregate.isNoneType() + ? nullptr + : static_cast(getElement(StreamTPI, TIAggregate)); + } + + // Workaround for cases where LF_NESTTYPE is missing for nested templates. + // If we manage to get parent information from the scoped name, we can add + // the nested type without relying on the LF_NESTTYPE. + if (Aggregate && !Element->getIsScopedAlready()) { + Aggregate->addElement(Element); + Element->setIsScopedAlready(); + } +} + +LVElement *LVLogicalVisitor::getElement(uint32_t StreamIdx, TypeIndex TI, + LVScope *Parent) { + LLVM_DEBUG({ printTypeIndex("TypeIndex", TI, StreamTPI); }); + TI = Shared->ForwardReferences.remap(TI); + LLVM_DEBUG({ printTypeIndex("TypeIndex Remap", TI, StreamTPI); }); + + LVElement *Element = Shared->TypeRecords.find(StreamIdx, TI); + if (!Element) { + if (TI.isNoneType() || TI.isSimple()) { + StringRef TypeName = TypeIndex::simpleTypeName(TI); + // If the name ends with "*", create 2 logical types: a pointer and a + // pointee type. TypeIndex is composed of a SympleTypeMode byte followed + // by a SimpleTypeKind byte. The logical pointer will be identified by + // the full TypeIndex value and the pointee by the SimpleTypeKind. + return (TypeName.back() == '*') ? createPointerType(TI, TypeName) + : createBaseType(TI, TypeName); + } + + LLVM_DEBUG({ W.printHex("TypeIndex not implemented: ", TI.getIndex()); }); + return nullptr; + } + + // The element has been finalized. + if (Element->getIsFinalized()) + return Element; + + // Add the element in case of a given parent. + if (Parent) + Parent->addElement(Element); + + // Check for a composite type. + LazyRandomTypeCollection &Types = types(); + CVType CVRecord = Types.getType(TI); + if (Error Err = finishVisitation(CVRecord, TI, Element)) { + consumeError(std::move(Err)); + return nullptr; + } + Element->setIsFinalized(); + return Element; +} + +void LVLogicalVisitor::processLines() { + // Traverse the collected LF_UDT_SRC_LINE records and add the source line + // information to the logical elements. + for (const TypeIndex &Entry : Shared->LineRecords) { + CVType CVRecord = ids().getType(Entry); + UdtSourceLineRecord Line; + if (Error Err = TypeDeserializer::deserializeAs( + const_cast(CVRecord), Line)) + consumeError(std::move(Err)); + else { + LLVM_DEBUG({ + printTypeIndex("UDT", Line.getUDT(), StreamIPI); + printTypeIndex("SourceFile", Line.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", Line.getLineNumber()); + }); + + if (LVElement *Element = + Shared->TypeRecords.find(StreamTPI, Line.getUDT())) { + Element->setLineNumber(Line.getLineNumber()); + Element->setFilenameIndex( + Shared->StringRecords.findIndex(Line.getSourceFile())); + } + } + } +} + +void LVLogicalVisitor::processNamespaces() { + // Create namespaces. + Shared->NamespaceDeduction.init(); +} + +void LVLogicalVisitor::processFiles() { Shared->StringRecords.addFilenames(); } + +void LVLogicalVisitor::printRecords(raw_ostream &OS) const { + if (!options().getInternalTag()) + return; + + unsigned Count = 0; + auto PrintItem = [&](StringRef Name) { + auto NewLine = [&]() { + if (++Count == 4) { + Count = 0; + OS << "\n"; + } + }; + std::string TheName(Name); + OS << format("%20s", TheName.c_str()); + NewLine(); + }; + + OS << "\nTypes:\n"; + for (const TypeLeafKind &Kind : Shared->TypeKinds) + PrintItem(formatTypeLeafKind(Kind)); + Shared->TypeKinds.clear(); + + Count = 0; + OS << "\nSymbols:\n"; + for (const SymbolKind &Kind : Shared->SymbolKinds) + PrintItem(LVCodeViewReader::getSymbolKindName(Kind)); + Shared->SymbolKinds.clear(); + + OS << "\n"; +} + +Error LVLogicalVisitor::inlineSiteAnnotation(LVScope *AbstractFunction, + LVScope *InlinedFunction, + InlineSiteSym &InlineSite) { + // Get the parent scope to update the address ranges of the nested + // scope representing the inlined function. + LVAddress ParentLowPC = 0; + LVScope *Parent = InlinedFunction->getParentScope(); + if (const LVLocations *Locations = Parent->getRanges()) { + for (const LVLocation *Location : *Locations) { + ParentLowPC = Location->getLowerAddress(); + break; + } + } + + // For the given inlinesite, get the initial line number and it's + // source filename. Update the logical scope representing it. + uint32_t LineNumber = 0; + StringRef Filename; + LVInlineeInfo::iterator Iter = InlineeInfo.find(InlineSite.Inlinee); + if (Iter != InlineeInfo.end()) { + LineNumber = Iter->second.first; + Filename = Iter->second.second; + AbstractFunction->setLineNumber(LineNumber); + // TODO: This part needs additional work in order to set properly the + // correct filename in order to detect changes between filenames. + // AbstractFunction->setFilename(Filename); + } + + LLVM_DEBUG({ + dbgs() << "inlineSiteAnnotation\n" + << "Abstract: " << AbstractFunction->getName() << "\n" + << "Inlined: " << InlinedFunction->getName() << "\n" + << "Parent: " << Parent->getName() << "\n" + << "Low PC: " << hexValue(ParentLowPC) << "\n"; + }); + + // Limitation: Currently we don't track changes in the FileOffset. The + // side effects are the caller that it is unable to differentiate the + // source filename for the inlined code. + uint64_t CodeOffset = ParentLowPC; + int32_t LineOffset = LineNumber; + uint32_t FileOffset = 0; + + auto UpdateClose = [&]() { LLVM_DEBUG({ dbgs() << ("\n"); }); }; + auto UpdateCodeOffset = [&](uint32_t Delta) { + CodeOffset += Delta; + LLVM_DEBUG({ + dbgs() << formatv(" code 0x{0} (+0x{1})", utohexstr(CodeOffset), + utohexstr(Delta)); + }); + }; + auto UpdateLineOffset = [&](int32_t Delta) { + LineOffset += Delta; + LLVM_DEBUG({ + char Sign = Delta > 0 ? '+' : '-'; + dbgs() << formatv(" line {0} ({1}{2})", LineOffset, Sign, + std::abs(Delta)); + }); + }; + auto UpdateFileOffset = [&](int32_t Offset) { + FileOffset = Offset; + LLVM_DEBUG({ dbgs() << formatv(" file {0}", FileOffset); }); + }; + + LVLines InlineeLines; + auto CreateLine = [&]() { + // Create the logical line record. + LVLineDebug *Line = new LVLineDebug(); + Line->setAddress(CodeOffset); + Line->setLineNumber(LineOffset); + // TODO: This part needs additional work in order to set properly the + // correct filename in order to detect changes between filenames. + // Line->setFilename(Filename); + InlineeLines.push_back(Line); + }; + + bool SeenLowAddress = false; + bool SeenHighAddress = false; + uint64_t LowPC = 0; + uint64_t HighPC = 0; + + for (auto &Annot : InlineSite.annotations()) { + LLVM_DEBUG({ + dbgs() << formatv(" {0}", + fmt_align(toHex(Annot.Bytes), AlignStyle::Left, 9)); + }); + + // Use the opcode to interpret the integer values. + switch (Annot.OpCode) { + case BinaryAnnotationsOpCode::ChangeCodeOffset: + case BinaryAnnotationsOpCode::CodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeLength: + UpdateCodeOffset(Annot.U1); + UpdateClose(); + if (Annot.OpCode == BinaryAnnotationsOpCode::ChangeCodeOffset) { + CreateLine(); + LowPC = CodeOffset; + SeenLowAddress = true; + break; + } + if (Annot.OpCode == BinaryAnnotationsOpCode::ChangeCodeLength) { + HighPC = CodeOffset - 1; + SeenHighAddress = true; + break; + } + break; + case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset: + UpdateCodeOffset(Annot.U2); + UpdateClose(); + break; + case BinaryAnnotationsOpCode::ChangeLineOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset: + UpdateCodeOffset(Annot.U1); + UpdateLineOffset(Annot.S1); + UpdateClose(); + if (Annot.OpCode == + BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset) + CreateLine(); + break; + case BinaryAnnotationsOpCode::ChangeFile: + UpdateFileOffset(Annot.U1); + UpdateClose(); + break; + default: + break; + } + if (SeenLowAddress && SeenHighAddress) { + SeenLowAddress = false; + SeenHighAddress = false; + InlinedFunction->addObject(LowPC, HighPC); + } + } + + Reader->addInlineeLines(InlinedFunction, InlineeLines); + UpdateClose(); + + return Error::success(); +} Index: llvm/lib/DebugInfo/LogicalView/Readers/LVELFReader.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Readers/LVELFReader.cpp @@ -0,0 +1,1140 @@ +//===-- LVELFReader.cpp ---------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVELFReader class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Readers/LVELFReader.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/Object/Error.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "ElfReader" + +LVElement *LVELFReader::createElement(dwarf::Tag Tag) { + CurrentScope = nullptr; + CurrentSymbol = nullptr; + CurrentType = nullptr; + CurrentRanges.clear(); + + if (!options().getPrintSymbols()) { + switch (Tag) { + // Symbols. + case dwarf::DW_TAG_formal_parameter: + case dwarf::DW_TAG_unspecified_parameters: + case dwarf::DW_TAG_member: + case dwarf::DW_TAG_variable: + case dwarf::DW_TAG_inheritance: + case dwarf::DW_TAG_constant: + case dwarf::DW_TAG_call_site_parameter: + case dwarf::DW_TAG_GNU_call_site_parameter: + return CurrentSymbol; + default: + break; + } + } + + switch (Tag) { + // Types. + case dwarf::DW_TAG_base_type: + CurrentType = new LVType(); + CurrentType->setIsBase(); + if (options().getAttributeBase()) + CurrentType->setIncludeInPrint(); + return CurrentType; + case dwarf::DW_TAG_const_type: + CurrentType = new LVType(); + CurrentType->setIsConst(); + CurrentType->setName("const"); + return CurrentType; + case dwarf::DW_TAG_enumerator: + CurrentType = new LVTypeEnumerator(); + return CurrentType; + case dwarf::DW_TAG_imported_declaration: + CurrentType = new LVTypeImport(); + CurrentType->setIsImportDeclaration(); + return CurrentType; + case dwarf::DW_TAG_imported_module: + CurrentType = new LVTypeImport(); + CurrentType->setIsImportModule(); + return CurrentType; + case dwarf::DW_TAG_pointer_type: + CurrentType = new LVType(); + CurrentType->setIsPointer(); + CurrentType->setName("*"); + return CurrentType; + case dwarf::DW_TAG_ptr_to_member_type: + CurrentType = new LVType(); + CurrentType->setIsPointerMember(); + CurrentType->setName("*"); + return CurrentType; + case dwarf::DW_TAG_reference_type: + CurrentType = new LVType(); + CurrentType->setIsReference(); + CurrentType->setName("&"); + return CurrentType; + case dwarf::DW_TAG_restrict_type: + CurrentType = new LVType(); + CurrentType->setIsRestrict(); + CurrentType->setName("restrict"); + return CurrentType; + case dwarf::DW_TAG_rvalue_reference_type: + CurrentType = new LVType(); + CurrentType->setIsRvalueReference(); + CurrentType->setName("&&"); + return CurrentType; + case dwarf::DW_TAG_subrange_type: + CurrentType = new LVTypeSubrange(); + return CurrentType; + case dwarf::DW_TAG_template_value_parameter: + CurrentType = new LVTypeParam(); + CurrentType->setIsTemplateValueParam(); + return CurrentType; + case dwarf::DW_TAG_template_type_parameter: + CurrentType = new LVTypeParam(); + CurrentType->setIsTemplateTypeParam(); + return CurrentType; + case dwarf::DW_TAG_GNU_template_template_param: + CurrentType = new LVTypeParam(); + CurrentType->setIsTemplateTemplateParam(); + return CurrentType; + case dwarf::DW_TAG_typedef: + CurrentType = new LVTypeDefinition(); + return CurrentType; + case dwarf::DW_TAG_unspecified_type: + CurrentType = new LVType(); + CurrentType->setIsUnspecified(); + return CurrentType; + case dwarf::DW_TAG_volatile_type: + CurrentType = new LVType(); + CurrentType->setIsVolatile(); + CurrentType->setName("volatile"); + return CurrentType; + + // Symbols. + case dwarf::DW_TAG_formal_parameter: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsParameter(); + return CurrentSymbol; + case dwarf::DW_TAG_unspecified_parameters: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsUnspecified(); + CurrentSymbol->setName("..."); + return CurrentSymbol; + case dwarf::DW_TAG_member: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsMember(); + return CurrentSymbol; + case dwarf::DW_TAG_variable: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsVariable(); + return CurrentSymbol; + case dwarf::DW_TAG_inheritance: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsInheritance(); + return CurrentSymbol; + case dwarf::DW_TAG_call_site_parameter: + case dwarf::DW_TAG_GNU_call_site_parameter: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsCallSiteParameter(); + return CurrentSymbol; + case dwarf::DW_TAG_constant: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsConstant(); + return CurrentSymbol; + + // Scopes. + case dwarf::DW_TAG_catch_block: + CurrentScope = new LVScope(); + CurrentScope->setIsCatchBlock(); + return CurrentScope; + case dwarf::DW_TAG_lexical_block: + CurrentScope = new LVScope(); + CurrentScope->setIsLexicalBlock(); + return CurrentScope; + case dwarf::DW_TAG_try_block: + CurrentScope = new LVScope(); + CurrentScope->setIsTryBlock(); + return CurrentScope; + case dwarf::DW_TAG_compile_unit: + case dwarf::DW_TAG_skeleton_unit: + CurrentScope = new LVScopeCompileUnit(); + CompileUnit = static_cast(CurrentScope); + return CurrentScope; + case dwarf::DW_TAG_inlined_subroutine: + CurrentScope = new LVScopeFunctionInlined(); + return CurrentScope; + case dwarf::DW_TAG_namespace: + CurrentScope = new LVScopeNamespace(); + return CurrentScope; + case dwarf::DW_TAG_template_alias: + CurrentScope = new LVScopeAlias(); + return CurrentScope; + case dwarf::DW_TAG_array_type: + CurrentScope = new LVScopeArray(); + return CurrentScope; + case dwarf::DW_TAG_call_site: + case dwarf::DW_TAG_GNU_call_site: + CurrentScope = new LVScopeFunction(); + CurrentScope->setIsCallSite(); + return CurrentScope; + case dwarf::DW_TAG_entry_point: + CurrentScope = new LVScopeFunction(); + CurrentScope->setIsEntryPoint(); + return CurrentScope; + case dwarf::DW_TAG_subprogram: + CurrentScope = new LVScopeFunction(); + CurrentScope->setIsSubprogram(); + return CurrentScope; + case dwarf::DW_TAG_subroutine_type: + CurrentScope = new LVScopeFunctionType(); + return CurrentScope; + case dwarf::DW_TAG_label: + CurrentScope = new LVScopeFunction(); + CurrentScope->setIsLabel(); + return CurrentScope; + case dwarf::DW_TAG_class_type: + CurrentScope = new LVScopeAggregate(); + CurrentScope->setIsClass(); + return CurrentScope; + case dwarf::DW_TAG_structure_type: + CurrentScope = new LVScopeAggregate(); + CurrentScope->setIsStructure(); + return CurrentScope; + case dwarf::DW_TAG_union_type: + CurrentScope = new LVScopeAggregate(); + CurrentScope->setIsUnion(); + return CurrentScope; + case dwarf::DW_TAG_enumeration_type: + CurrentScope = new LVScopeEnumeration(); + return CurrentScope; + case dwarf::DW_TAG_GNU_formal_parameter_pack: + CurrentScope = new LVScopeFormalPack(); + return CurrentScope; + case dwarf::DW_TAG_GNU_template_parameter_pack: + CurrentScope = new LVScopeTemplatePack(); + return CurrentScope; + default: + // Collect TAGs not implemented. + if (options().getInternalTag() && Tag) + CompileUnit->addDebugTag(Tag, CurrentOffset); + break; + } + return nullptr; +} + +void LVELFReader::processOneAttribute(const DWARFDie &Die, LVOffset *OffsetPtr, + AttributeSpec AttrSpec) { + uint64_t OffsetOnEntry = *OffsetPtr; + DWARFUnit *U = Die.getDwarfUnit(); + DWARFFormValue FormValue = + DWARFFormValue::createFromUnit(AttrSpec.Form, U, OffsetPtr); + + auto GetFlag = [](DWARFFormValue &FormValue) -> bool { + return FormValue.isFormClass(DWARFFormValue::FC_Flag); + }; + + auto GetBoundValue = [](DWARFFormValue &FormValue) -> int64_t { + switch (FormValue.getForm()) { + case dwarf::DW_FORM_ref_addr: + case dwarf::DW_FORM_ref1: + case dwarf::DW_FORM_ref2: + case dwarf::DW_FORM_ref4: + case dwarf::DW_FORM_ref8: + case dwarf::DW_FORM_ref_udata: + case dwarf::DW_FORM_ref_sig8: + return *FormValue.getAsReferenceUVal(); + case dwarf::DW_FORM_data1: + case dwarf::DW_FORM_flag: + case dwarf::DW_FORM_data2: + case dwarf::DW_FORM_data4: + case dwarf::DW_FORM_data8: + case dwarf::DW_FORM_udata: + case dwarf::DW_FORM_ref_sup4: + case dwarf::DW_FORM_ref_sup8: + return *FormValue.getAsUnsignedConstant(); + case dwarf::DW_FORM_sdata: + return *FormValue.getAsSignedConstant(); + default: + return 0; + } + }; + + LLVM_DEBUG({ + dbgs() << " " << hexValue(OffsetOnEntry) + << formatv(" {0}", AttrSpec.Attr) << "\n"; + }); + + switch (AttrSpec.Attr) { + case dwarf::DW_AT_accessibility: + CurrentElement->setAccessibilityCode(*FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_artificial: + CurrentElement->setIsArtificial(); + break; + case dwarf::DW_AT_bit_size: + CurrentElement->setBitSize(*FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_call_file: + CurrentElement->setCallFilenameIndex(*FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_call_line: + CurrentElement->setCallLineNumber( + IncrementFileIndex ? *FormValue.getAsUnsignedConstant() + 1 + : *FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_comp_dir: + CompileUnit->setCompilationDirectory(dwarf::toStringRef(FormValue)); + break; + case dwarf::DW_AT_const_value: + if (FormValue.isFormClass(DWARFFormValue::FC_Block)) { + ArrayRef Expr = *FormValue.getAsBlock(); + // Store the expression as a hexadecimal string. + CurrentElement->setValue( + llvm::toHex(llvm::toStringRef(Expr), /*LowerCase=*/true)); + } else if (FormValue.isFormClass(DWARFFormValue::FC_Constant)) { + // In the case of negative values, generate the string representation + // for a positive value prefixed with the negative sign. + if (FormValue.getForm() == dwarf::DW_FORM_sdata) { + std::stringstream Stream; + int64_t Value = *FormValue.getAsSignedConstant(); + if (Value < 0) { + Stream << "-"; + Value = std::abs(Value); + } + Stream << hexString(Value, 2); + CurrentElement->setValue(Stream.str()); + } else + CurrentElement->setValue( + hexString(*FormValue.getAsUnsignedConstant(), 2)); + } else + CurrentElement->setValue(dwarf::toStringRef(FormValue)); + break; + case dwarf::DW_AT_count: + CurrentElement->setCount(*FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_decl_line: + CurrentElement->setLineNumber(*FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_decl_file: + CurrentElement->setFilenameIndex( + IncrementFileIndex ? *FormValue.getAsUnsignedConstant() + 1 + : *FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_enum_class: + if (GetFlag(FormValue)) + CurrentElement->setIsEnumClass(); + break; + case dwarf::DW_AT_external: + if (GetFlag(FormValue)) + CurrentElement->setIsExternal(); + break; + case dwarf::DW_AT_GNU_discriminator: + CurrentElement->setDiscriminator(*FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_inline: + CurrentElement->setInlineCode(*FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_lower_bound: + CurrentElement->setLowerBound(GetBoundValue(FormValue)); + break; + case dwarf::DW_AT_name: + CurrentElement->setName(dwarf::toStringRef(FormValue)); + break; + case dwarf::DW_AT_linkage_name: + case dwarf::DW_AT_MIPS_linkage_name: + CurrentElement->setLinkageName(dwarf::toStringRef(FormValue)); + break; + case dwarf::DW_AT_producer: + if (options().getAttributeProducer()) + CurrentElement->setProducer(dwarf::toStringRef(FormValue)); + break; + case dwarf::DW_AT_upper_bound: + CurrentElement->setUpperBound(GetBoundValue(FormValue)); + break; + case dwarf::DW_AT_virtuality: + CurrentElement->setVirtualityCode(*FormValue.getAsUnsignedConstant()); + break; + + case dwarf::DW_AT_abstract_origin: + case dwarf::DW_AT_call_origin: + case dwarf::DW_AT_extension: + case dwarf::DW_AT_import: + case dwarf::DW_AT_specification: + case dwarf::DW_AT_type: + updateReference(AttrSpec.Attr, FormValue); + break; + + case dwarf::DW_AT_low_pc: + if (options().getGeneralCollectRanges()) { + FoundLowPC = true; + // For toolchains that support the removal of unused code, the linker + // marks functions that have been removed, by setting the value for the + // low_pc to the max address. + if (Optional Value = FormValue.getAsAddress()) { + CurrentLowPC = Value.getValue(); + } else { + uint64_t UValue = FormValue.getRawUValue(); + if (U->getAddrOffsetSectionItem(UValue)) { + CurrentLowPC = *FormValue.getAsAddress(); + } else { + FoundLowPC = false; + // We are dealing with an index into the .debug_addr section. + LLVM_DEBUG({ + dbgs() << format("indexed (%8.8x) address = ", (uint32_t)UValue); + }); + } + } + if (FoundLowPC) { + if (CurrentLowPC == MaxAddress) + CurrentElement->setIsDiscarded(); + if (CurrentElement->isCompileUnit()) + setCUBaseAddress(CurrentLowPC); + } + } + break; + + case dwarf::DW_AT_high_pc: + if (options().getGeneralCollectRanges()) { + FoundHighPC = true; + if (Optional Address = FormValue.getAsAddress()) + // High PC is an address. + CurrentHighPC = *Address; + if (Optional Offset = FormValue.getAsUnsignedConstant()) + // High PC is an offset from LowPC. + CurrentHighPC = CurrentLowPC + *Offset; + // Store the real upper limit for the address range. + if (UpdateHighAddress && CurrentHighPC > 0) + --CurrentHighPC; + if (CurrentElement->isCompileUnit()) + setCUHighAddress(CurrentHighPC); + } + break; + + case dwarf::DW_AT_ranges: + if (RangesDataAvailable && options().getGeneralCollectRanges()) { + auto GetRanges = [&](DWARFFormValue &FormValue, + DWARFUnit *U) -> Expected { + if (FormValue.getForm() == dwarf::DW_FORM_rnglistx) + return U->findRnglistFromIndex(*FormValue.getAsSectionOffset()); + return U->findRnglistFromOffset(*FormValue.getAsSectionOffset()); + }; + Expected RangesOrError = + GetRanges(FormValue, U); + if (!RangesOrError) { + LLVM_DEBUG({ + std::string TheError(toString(RangesOrError.takeError())); + dbgs() << format("error decoding address ranges = ", + TheError.c_str()); + }); + consumeError(RangesOrError.takeError()); + break; + } + // The address ranges are absolute. There is no need to add any addend. + DWARFAddressRangesVector Ranges = RangesOrError.get(); + for (DWARFAddressRange &Range : Ranges) { + // This seems to be a tombstone for empty ranges. + if (Range.LowPC == 1 && Range.HighPC == 1) + continue; + // Store the real upper limit for the address range. + if (UpdateHighAddress && Range.HighPC > 0) + --Range.HighPC; + // Add the pair of addresses. + CurrentScope->addObject(Range.LowPC, Range.HighPC); + // If the scope is the CU, do not update the ranges set. + if (!CurrentElement->isCompileUnit()) + CurrentRanges.emplace_back(Range.LowPC, Range.HighPC); + } + } + break; + + // Get the location list for the symbol. + case dwarf::DW_AT_data_member_location: + if (options().getAttributeAnyLocation()) + getLocationMember(AttrSpec.Attr, FormValue, Die, OffsetOnEntry); + break; + + // Get the location list for the symbol. + case dwarf::DW_AT_location: + case dwarf::DW_AT_string_length: + case dwarf::DW_AT_use_location: + if (options().getAttributeAnyLocation() && CurrentSymbol) + getLocationList(AttrSpec.Attr, FormValue, Die, OffsetOnEntry); + break; + + case dwarf::DW_AT_call_data_value: + case dwarf::DW_AT_call_value: + case dwarf::DW_AT_GNU_call_site_data_value: + case dwarf::DW_AT_GNU_call_site_value: + if (options().getAttributeAnyLocation() && CurrentSymbol) + getLocationList(AttrSpec.Attr, FormValue, Die, OffsetOnEntry, + /*CallSiteLocation=*/true); + break; + + default: + break; + } +} + +LVScope *LVELFReader::processOneDie(const DWARFDie &InputDIE, LVScope *Parent, + DWARFDie &SkeletonDie) { + // If the input DIE corresponds to the compile unit, it can be: + // a) Simple DWARF: a standard DIE. Ignore the skeleton DIE (is empty). + // b) Split DWARF: the DIE for the split DWARF. The skeleton is the DIE + // for the skeleton DWARF. Process both DIEs. + const DWARFDie &DIE = SkeletonDie.isValid() ? SkeletonDie : InputDIE; + DWARFDataExtractor DebugInfoData = + DIE.getDwarfUnit()->getDebugInfoExtractor(); + LVOffset Offset = DIE.getOffset(); + + // Reset values for the current DIE. + CurrentLowPC = 0; + CurrentHighPC = 0; + CurrentOffset = Offset; + CurrentEndOffset = 0; + FoundLowPC = false; + FoundHighPC = false; + + // Process supported attributes. + if (DebugInfoData.isValidOffset(Offset)) { + + LLVM_DEBUG({ + dbgs() << "DIE: " << hexValue(Offset) << formatv(" {0}", DIE.getTag()) + << "\n"; + }); + + // Create the logical view element for the current DIE. + dwarf::Tag Tag = DIE.getTag(); + CurrentElement = createElement(Tag); + if (!CurrentElement) + return CurrentScope; + + CurrentElement->setTag(Tag); + CurrentElement->setOffset(Offset); + + if (options().getAttributeAnySource() && CurrentElement->isCompileUnit()) + addCompileUnitOffset(Offset, + static_cast(CurrentElement)); + + // Insert the newly created element into the element symbol table. If the + // element is in the list, it means there are previously created elements + // referencing this element. + if (ElementTable.find(Offset) == ElementTable.end()) { + // No previous references to this offset. + ElementTable.emplace( + std::piecewise_construct, std::forward_as_tuple(Offset), + std::forward_as_tuple(CurrentElement, LVElementSet())); + } else { + // There are previous references to this element. We need to update the + // element and all the references pointing to this element. + LVElementEntry &Reference = ElementTable[Offset]; + Reference.first = CurrentElement; + // Traverse the element set and update the elements (backtracking). + // Using the bit associated with 'type' or 'reference' allows us to set + // the correct target. + for (LVElement *Target : Reference.second) + Target->getHasReference() ? Target->setReference(CurrentElement) + : Target->setType(CurrentElement); + // Clear the pending elements. + Reference.second.clear(); + } + + // Add the current element to its parent as there are attributes + // (locations) that require the scope level. + if (CurrentScope) + Parent->addElement(CurrentScope); + else if (CurrentSymbol) + Parent->addElement(CurrentSymbol); + else if (CurrentType) + Parent->addElement(CurrentType); + + // Process the attributes for the given DIE. + auto ProcessAttributes = [&](const DWARFDie &TheDIE, + DWARFDataExtractor &DebugData) { + CurrentEndOffset = Offset; + uint32_t abbrCode = DebugData.getULEB128(&CurrentEndOffset); + if (abbrCode) { + if (const DWARFAbbreviationDeclaration *AbbrevDecl = + TheDIE.getAbbreviationDeclarationPtr()) + for (const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec : + AbbrevDecl->attributes()) + if (AttrSpec.Form != dwarf::DW_FORM_implicit_const) + // We are processing .debug_info section, implicit_const + // attribute values are not really stored here, but in + // .debug_abbrev section. So we just skip such attrs. + processOneAttribute(TheDIE, &CurrentEndOffset, AttrSpec); + } + }; + + ProcessAttributes(DIE, DebugInfoData); + + // If the input DIE is for a compile unit, process its attributes in + // the case of split DWARF, to overrite any common attribute values. + if (SkeletonDie.isValid()) { + DWARFDataExtractor DebugInfoData = + InputDIE.getDwarfUnit()->getDebugInfoExtractor(); + LVOffset Offset = InputDIE.getOffset(); + if (DebugInfoData.isValidOffset(Offset)) + ProcessAttributes(InputDIE, DebugInfoData); + } + } + + if (CurrentScope) { + if (CurrentScope->getCanHaveRanges()) { + // If the scope has ranges, they are already added to the scope. + // Add any collected LowPC/HighPC values. + bool IsCompileUnit = CurrentScope->getIsCompileUnit(); + if (FoundLowPC && FoundHighPC) { + CurrentScope->addObject(CurrentLowPC, CurrentHighPC); + if (!IsCompileUnit) { + // If the scope is a function, add it to the public names. + if ((options().getAttributePublics() || + options().getPrintAnyLine()) && + CurrentScope->getIsFunction() && + !CurrentScope->getIsInlinedFunction()) + CompileUnit->addPublicName(CurrentScope, CurrentLowPC, + CurrentHighPC); + } + } + + // Look for scopes with ranges and no linkage name information that + // are referencing another scopes via DW_AT_specification. They are + // possible candidates for a comdat scope. + if (CurrentScope->getHasRanges() && + !CurrentScope->getLinkageNameIndex() && + CurrentScope->getHasReferenceSpecification()) { + // Get the linkage name in order to search for a possible comdat. + Optional LinkageDIE = + DIE.findRecursively(dwarf::DW_AT_linkage_name); + if (LinkageDIE.hasValue()) { + StringRef Name(dwarf::toStringRef(LinkageDIE)); + if (!Name.empty()) + CurrentScope->setLinkageName(Name); + } + } + + // If the current scope is in the 'LinkageNames' table, update its + // logical scope. For other scopes, always we will assume the default + // ".text" section index. + LVSectionIndex SectionIndex = updateSymbolTable(CurrentScope); + if (CurrentScope->getIsComdat()) + CompileUnit->setHasComdatScopes(); + + // Update section index contained ranges. + if (SectionIndex) { + if (!CurrentRanges.empty()) { + for (LVAddressRange &Range : CurrentRanges) + addSectionRange(SectionIndex, CurrentScope, Range.first, + Range.second); + CurrentRanges.clear(); + } + // If the scope is the CU, do not update the ranges set. + if (FoundLowPC && FoundHighPC && !IsCompileUnit) { + addSectionRange(SectionIndex, CurrentScope, CurrentLowPC, + CurrentHighPC); + } + } + } + // Mark member functions. + if (Parent->getIsAggregate()) + CurrentScope->setIsMember(); + } + + // Keep track of symbols with locations. + if (options().getAttributeAnyLocation() && CurrentSymbol && + CurrentSymbol->getHasLocation()) + SymbolsWithLocations.push_back(CurrentSymbol); + + // If we have template parameters, mark the parent as template. + if (CurrentType && CurrentType->getIsTemplateParam()) + Parent->setIsTemplate(); + + return CurrentScope; +} + +void LVELFReader::traverseDieAndChildren(DWARFDie &DIE, LVScope *Parent, + DWARFDie &SkeletonDie) { + // Process the current DIE. + LVScope *Scope = processOneDie(DIE, Parent, SkeletonDie); + if (Scope) { + LVOffset Lower = DIE.getOffset(); + LVOffset Upper = CurrentEndOffset; + // Traverse the children chain. + DWARFDie Child = DIE.getFirstChild(); + while (Child) { + traverseDieAndChildren(Child, Scope, DummyDie); + Upper = Child.getOffset(); + Child = Child.getSibling(); + } + // Calculate contributions to the debug info section. + if (options().getPrintSizes() && Upper) + CompileUnit->addSize(Scope, Lower, Upper); + } +} + +void LVELFReader::processLocationGaps() { + if (options().getAttributeAnyLocation()) + for (LVSymbol *Symbol : SymbolsWithLocations) + Symbol->fillLocationGaps(); +} + +void LVELFReader::createLineAndFileRecords( + const DWARFDebugLine::LineTable *Lines) { + if (!Lines) + return; + + // Get the source filenames. + if (!Lines->Prologue.FileNames.empty()) + for (const DWARFDebugLine::FileNameEntry &Entry : + Lines->Prologue.FileNames) { + std::string Directory; + if (Lines->getDirectoryForEntry(Entry, Directory)) + Directory = transformPath(Directory); + if (Directory.empty()) + Directory = std::string(CompileUnit->getCompilationDirectory()); + std::string File = transformPath(dwarf::toStringRef(Entry.Name)); + std::string String; + raw_string_ostream(String) << Directory << "/" << File; + CompileUnit->addFilename(String); + } + + // In DWARF5 the file indexes start at 0; + bool IncrementIndex = Lines->Prologue.getVersion() >= 5; + + // Get the source lines. + if ((options().getAttributeRange() || options().getPrintLines()) && + Lines->Rows.size()) + for (const DWARFDebugLine::Row &Row : Lines->Rows) { + // The 'processLines()' function will move each created logical line + // to its enclosing logical scope, using the debug ranges information + // and they will be released when its scope parent is deleted. + LVLineDebug *Line = new LVLineDebug(); + CULines.push_back(Line); + Line->setAddress(Row.Address.Address); + Line->setFilename( + CompileUnit->getFilename(IncrementIndex ? Row.File + 1 : Row.File)); + Line->setLineNumber(Row.Line); + if (Row.Discriminator) + Line->setDiscriminator(Row.Discriminator); + if (Row.IsStmt) + Line->setIsNewStatement(); + if (Row.BasicBlock) + Line->setIsBasicBlock(); + if (Row.EndSequence) + Line->setIsEndSequence(); + if (Row.EpilogueBegin) + Line->setIsEpilogueBegin(); + if (Row.PrologueEnd) + Line->setIsPrologueEnd(); + LLVM_DEBUG({ + dbgs() << "Address: " << hexValue(Line->getAddress()) + << " Line: " << Line->lineNumberAsString(/*ShowZero=*/true) + << "\n"; + }); + } +} + +std::string LVELFReader::getRegisterName(LVSmall Opcode, uint64_t Operands[2]) { + // The 'prettyPrintRegisterOp' function uses the DWARFUnit to access the + // offset and tag name. At this point we are operating on a logical view + // item, with no access to the underlying DWARF data used by LLVM. + // It does not support DW_OP_regval_type. + if (Opcode == dwarf::DW_OP_regval_type) + return {}; + if ((Opcode >= dwarf::DW_OP_breg0 && Opcode <= dwarf::DW_OP_breg31) || + (Opcode >= dwarf::DW_OP_reg0 && Opcode <= dwarf::DW_OP_reg31) || + Opcode == dwarf::DW_OP_bregx || Opcode == dwarf::DW_OP_regx) { + std::string string; + raw_string_ostream Stream(string); + prettyPrintRegisterOp(/*U=*/nullptr, Stream, DumpOpts, Opcode, Operands, + MRI.get(), /*isEH=*/false); + return Stream.str(); + } + return {}; +} + +Error LVELFReader::createScopes() { + LLVM_DEBUG({ + W.startLine() << "\n"; + W.printString("File", Obj.getFileName().str()); + W.printString("Format", FileFormatName); + }); + + if (Error Err = LVReader::createScopes()) + return Err; + + // As the DwarfContext object is valid only during the scopes creation, + // we need to create our own Target information, to be used during the + // logical view printing, in the case of instructions being requested. + std::unique_ptr DwarfContext = DWARFContext::create(Obj); + if (!DwarfContext) { + std::string TheFilename(getFilename()); + return createStringError(errc::invalid_argument, + "Could not create DWARF information: %s", + TheFilename.c_str()); + } + + if (Error Err = loadTargetInfo(Obj)) + return Err; + + // Create a mapping for virtual addresses. + mapVirtualAddress(Obj); + + // Select the correct compile unit range, depending if we are dealing with + // a standard or split DWARF object. + DWARFContext::compile_unit_range CompileUnits = + DwarfContext->getNumCompileUnits() ? DwarfContext->compile_units() + : DwarfContext->dwo_compile_units(); + for (const std::unique_ptr &CU : CompileUnits) { + // Record DWARF version for current Compile Unit. + IncrementFileIndex = CU->getVersion() >= 5; + DWARFDie UnitDie = CU->getUnitDIE(); + SmallString<16> DWOAlternativeLocation; + if (UnitDie) { + Optional DWOFileName = + CU->getVersion() >= 5 + ? dwarf::toString(UnitDie.find(dwarf::DW_AT_dwo_name)) + : dwarf::toString(UnitDie.find(dwarf::DW_AT_GNU_dwo_name)); + StringRef From(DWOFileName.hasValue() ? DWOFileName.getValue() : ""); + DWOAlternativeLocation = createAlternativePath(InputFilename, From); + } + + // The current CU can be a normal compile unit (standard) or a skeleton + // compile unit (split). For both cases, the returned die, will be used + // to create the logical scopes. + DWARFDie CUDie = CU->getNonSkeletonUnitDIE( + /*ExtractUnitDIEOnly=*/false, + /*DWOAlternativeLocation=*/DWOAlternativeLocation); + if (!CUDie.isValid()) + continue; + + // The current unit corresponds to the .dwo file. We need to get the + // skeleton unit and query for any ranges that will enclose any ranges + // in the non-skeleton unit. + DWARFDie SkeletonDie = + CUDie.getDwarfUnit()->isDWOUnit() ? CU->getUnitDIE(false) : DummyDie; + // Disable the ranges processing if we have just a single .dwo object, + // as any DW_AT_ranges will access not available range information. + RangesDataAvailable = + (!CUDie.getDwarfUnit()->isDWOUnit() || + (SkeletonDie.isValid() ? !SkeletonDie.getDwarfUnit()->isDWOUnit() + : true)); + + traverseDieAndChildren(CUDie, Root, SkeletonDie); + + createLineAndFileRecords(DwarfContext->getLineTableForUnit(CU.get())); + if (Error Err = createInstructions()) + return Err; + + // Process the compilation unit, as there are cases where enclosed + // functions have the same ranges values. Insert the compilation unit + // ranges at the end, to allow enclosing ranges to be first in the list. + LVSectionIndex SectionIndex = getSectionIndex(CompileUnit); + addSectionRange(SectionIndex, CompileUnit); + LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); + ScopesWithRanges->sort(); + + processLines(&CULines, SectionIndex); + processLocationGaps(); + + // These are per compile unit. + ScopesWithRanges->clear(); + SymbolsWithLocations.clear(); + CULines.clear(); + } + + return Error::success(); +} + +// Get the location information for the associated attribute. +void LVELFReader::getLocationList(dwarf::Attribute Attr, + DWARFFormValue &FormValue, + const DWARFDie &Die, uint64_t OffsetOnEntry, + bool CallSiteLocation) { + + auto ProcessLocationExpression = [&](const DWARFExpression &Expression) { + // DW_OP_const_type is variable-length and has 3 + // operands. DWARFExpression thus far only supports 2. + uint64_t Operands[2] = {0}; + for (const DWARFExpression::Operation &Op : Expression) { + DWARFExpression::Operation::Description Description = Op.getDescription(); + for (unsigned Operand = 0; Operand < 2; ++Operand) { + if (Description.Op[Operand] == DWARFExpression::Operation::SizeNA) + break; + Operands[Operand] = Op.getRawOperand(Operand); + } + CurrentSymbol->addLocationOperands(Op.getCode(), Operands[0], + Operands[1]); + } + }; + + DWARFUnit *U = Die.getDwarfUnit(); + DWARFContext &DwarfContext = U->getContext(); + bool IsLittleEndian = DwarfContext.isLittleEndian(); + if (FormValue.isFormClass(DWARFFormValue::FC_Block) || + (DWARFAttribute::mayHaveLocationExpr(Attr) && + FormValue.isFormClass(DWARFFormValue::FC_Exprloc))) { + ArrayRef Expr = *FormValue.getAsBlock(); + DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()), + IsLittleEndian, 0); + DWARFExpression Expression(Data, U->getAddressByteSize(), + U->getFormParams().Format); + + // Add location and operation entries. + CurrentSymbol->addLocation(Attr, /*LowPC=*/0, /*HighPC=*/-1, + /*SectionOffset=*/0, OffsetOnEntry, + CallSiteLocation); + ProcessLocationExpression(Expression); + return; + } + + if (DWARFAttribute::mayHaveLocationList(Attr) && + FormValue.isFormClass(DWARFFormValue::FC_SectionOffset)) { + uint64_t Offset = *FormValue.getAsSectionOffset(); + if (FormValue.getForm() == dwarf::DW_FORM_loclistx) { + Optional LoclistOffset = U->getLoclistOffset(Offset); + if (!LoclistOffset) + return; + Offset = *LoclistOffset; + } + uint64_t BaseAddr = 0; + if (Optional BA = U->getBaseAddress()) + BaseAddr = BA->Address; + LVAddress LowPC = 0; + LVAddress HighPC = 0; + + auto ProcessLocationEntry = [&](const DWARFLocationEntry &Entry) { + if (Entry.Kind == dwarf::DW_LLE_base_address) { + BaseAddr = Entry.Value0; + return; + } + if (Entry.Kind == dwarf::DW_LLE_offset_pair) { + LowPC = BaseAddr + Entry.Value0; + HighPC = BaseAddr + Entry.Value1; + DWARFAddressRange Range{LowPC, HighPC, Entry.SectionIndex}; + if (Range.SectionIndex == SectionedAddress::UndefSection) + Range.SectionIndex = Entry.SectionIndex; + DWARFLocationExpression Loc{Range, Entry.Loc}; + DWARFDataExtractor Data(Loc.Expr, IsLittleEndian, + U->getAddressByteSize()); + DWARFExpression Expression(Data, U->getAddressByteSize()); + + // Store the real upper limit for the address range. + if (UpdateHighAddress && HighPC > 0) + --HighPC; + // Add location and operation entries. + CurrentSymbol->addLocation(Attr, LowPC, HighPC, Offset, OffsetOnEntry, + CallSiteLocation); + ProcessLocationExpression(Expression); + } + }; + Error E = U->getLocationTable().visitLocationList( + &Offset, [&](const DWARFLocationEntry &E) { + ProcessLocationEntry(E); + return true; + }); + if (E) + consumeError(std::move(E)); + } +} + +void LVELFReader::getLocationMember(dwarf::Attribute Attr, + DWARFFormValue &FormValue, + const DWARFDie &Die, + uint64_t OffsetOnEntry) { + // Check if the value is an integer constant. + if (FormValue.isFormClass(DWARFFormValue::FC_Constant)) + // Add a record to hold a constant as location. + CurrentSymbol->addLocationConstant(Attr, *FormValue.getAsUnsignedConstant(), + OffsetOnEntry); + else + // This is a a location description, or a reference to one. + getLocationList(Attr, FormValue, Die, OffsetOnEntry); +} + +// Update the current element with the reference. +void LVELFReader::updateReference(dwarf::Attribute Attr, + DWARFFormValue &FormValue) { + // We are assuming that DW_AT_specification, DW_AT_abstract_origin, + // DW_AT_type and DW_AT_extension do not appear at the same time + // in the same DIE. + uint64_t Reference = *FormValue.getAsReference(); + // Get target for the given reference, if already created. + LVElement *Target = getElementForOffset(Reference, CurrentElement); + // Check if we are dealing with cross CU references. + if (FormValue.getForm() == dwarf::DW_FORM_ref_addr) { + if (Target) { + // The global reference is ready. Mark it as global. + Target->setIsGlobalReference(); + // Remove global reference from the unseen list. + removeGlobalOffset(Reference); + } else + // Record the unseen cross CU reference. + addGlobalOffset(Reference); + } + + // At this point, 'Target' can be null, in the case of the target element + // not being seen. But the correct bit is set, to indicate that the target + // is being referenced by (abstract_origin, extension, specification) or + // (import, type). + // We must differentiate between the kind of reference. This is needed to + // complete inlined function instances with dropped abstract references, + // in order to facilitate a logical comparison. + switch (Attr) { + case dwarf::DW_AT_abstract_origin: + case dwarf::DW_AT_call_origin: + CurrentElement->setReference(Target); + CurrentElement->setHasReferenceAbstract(); + break; + case dwarf::DW_AT_extension: + CurrentElement->setReference(Target); + CurrentElement->setHasReferenceExtension(); + break; + case dwarf::DW_AT_specification: + CurrentElement->setReference(Target); + CurrentElement->setHasReferenceSpecification(); + break; + case dwarf::DW_AT_import: + case dwarf::DW_AT_type: + CurrentElement->setType(Target); + break; + default: + break; + } +} + +// Get an element given the DIE offset. +LVElement *LVELFReader::getElementForOffset(LVOffset Offset, + LVElement *Element) { + LVElement *Target = nullptr; + // Search offset in the cross references. + LVElementReference::iterator Iter = ElementTable.find(Offset); + if (Iter == ElementTable.end()) + // Reference to an unseen element. + ElementTable.emplace(std::piecewise_construct, + std::forward_as_tuple(Offset), + std::forward_as_tuple(nullptr, LVElementSet{Element})); + else { + // There are previous references to this element. We need to update the + // element and all the references pointing to this element. + LVElementEntry &Reference = Iter->second; + Target = Reference.first; + if (!Target) + // Add the element to the set. + Reference.second.insert(Element); + } + return Target; +} + +Error LVELFReader::loadTargetInfo(const ObjectFile &Obj) { + // Detect the architecture from the object file. We usually don't need OS + // info to lookup a target and create register info. + Triple TT; + TT.setArch(Triple::ArchType(Obj.getArch())); + TT.setVendor(Triple::UnknownVendor); + TT.setOS(Triple::UnknownOS); + + // Features to be passed to target/subtarget + SubtargetFeatures Features = Obj.getFeatures(); + + return loadGenericTargetInfo(TT.str(), Features.getString()); +} + +void LVELFReader::mapRangeAddress(const ObjectFile &Obj) { + for (auto Iter = Obj.symbol_begin(); Iter != Obj.symbol_end(); ++Iter) { + const SymbolRef &Symbol = *Iter; + + Expected TypeOrErr = Symbol.getType(); + if (!TypeOrErr) { + consumeError(TypeOrErr.takeError()); + continue; + } + + // Process only symbols that represent a function. + SymbolRef::Type Type = *TypeOrErr; + if (Type != SymbolRef::ST_Function) + continue; + + // Don't ask a Mach-O STAB symbol for its section unless you know that + // STAB symbol's section field refers to a valid section index. Otherwise + // the symbol may error trying to load a section that does not exist. + const MachOObjectFile *MachO = dyn_cast(&Obj); + bool IsSTAB = false; + if (MachO) { + DataRefImpl SymDRI = Symbol.getRawDataRefImpl(); + uint8_t NType = + (MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type + : MachO->getSymbolTableEntry(SymDRI).n_type); + if (NType & MachO::N_STAB) + IsSTAB = true; + } + + Expected IterOrErr = Symbol.getSection(); + if (!IterOrErr) { + consumeError(IterOrErr.takeError()); + continue; + } + section_iterator Section = IsSTAB ? Obj.section_end() : *IterOrErr; + if (Section == Obj.section_end()) + continue; + + // Get the symbol value. + Expected AddressOrErr = Symbol.getAddress(); + if (!AddressOrErr) { + consumeError(AddressOrErr.takeError()); + continue; + } + uint64_t Address = *AddressOrErr; + + // Get symbol name. + StringRef Name; + Expected NameOrErr = Symbol.getName(); + if (!NameOrErr) { + consumeError(NameOrErr.takeError()); + continue; + } + Name = *NameOrErr; + + // Check if the symbol is Comdat. + Expected FlagsOrErr = Symbol.getFlags(); + if (!FlagsOrErr) { + consumeError(FlagsOrErr.takeError()); + continue; + } + uint32_t Flags = *FlagsOrErr; + + // Mark the symbol as 'comdat' in any of the following cases: + // - Symbol has the SF_Weak or + // - Symbol section index different of the DotTextSectionIndex. + LVSectionIndex SectionIndex = Section->getIndex(); + bool IsComdat = + (Flags & SymbolRef::SF_Weak) || (SectionIndex != DotTextSectionIndex); + + // Record the symbol name (linkage) and its loading address. + addToSymbolTable(Name, Address, SectionIndex, IsComdat); + } +} + +void LVELFReader::sortScopes() { Root->sort(); } + +void LVELFReader::print(raw_ostream &OS) const { + OS << "LVType\n"; + LLVM_DEBUG(dbgs() << "CreateReaders\n"); +} Index: llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp +++ llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp @@ -240,7 +240,9 @@ } uint32_t DbiModuleList::getModuleCount() const { - return FileInfoHeader->NumModules; + // Workaround to avoid the crash until upstream issue is fixed: + // https://github.com/llvm/llvm-project/issues/55214 + return FileInfoHeader ? FileInfoHeader->NumModules : 0; } uint32_t DbiModuleList::getSourceFileCount() const { Index: llvm/test/CMakeLists.txt =================================================================== --- llvm/test/CMakeLists.txt +++ llvm/test/CMakeLists.txt @@ -77,6 +77,7 @@ llvm-cxxdump llvm-cxxfilt llvm-cxxmap + llvm-debuginfo-analyzer llvm-debuginfod-find llvm-diff llvm-dis Index: llvm/test/lit.cfg.py =================================================================== --- llvm/test/lit.cfg.py +++ llvm/test/lit.cfg.py @@ -160,6 +160,7 @@ 'dsymutil', 'lli', 'lli-child-target', 'llvm-ar', 'llvm-as', 'llvm-addr2line', 'llvm-bcanalyzer', 'llvm-bitcode-strip', 'llvm-config', 'llvm-cov', 'llvm-cxxdump', 'llvm-cvtres', 'llvm-debuginfod-find', 'llvm-debuginfod', + 'llvm-debuginfo-analyzer', 'llvm-diff', 'llvm-dis', 'llvm-dwarfdump', 'llvm-dwarfutil', 'llvm-dlltool', 'llvm-exegesis', 'llvm-extract', 'llvm-isel-fuzzer', 'llvm-ifs', 'llvm-install-name-tool', 'llvm-jitlink', 'llvm-opt-fuzzer', 'llvm-lib', Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-compare-logical-elements.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-compare-logical-elements.test @@ -0,0 +1,101 @@ +; Test case 1 - General options + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Compare mode - Logical view. +; The output shows in view form the 'missing (-), added (+)' elements, +; giving more context by swapping the reference and target object files. + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --compare=types \ +; RUN: --report=view \ +; RUN: --print=symbols,types \ +; RUN: %p/Inputs/test-codeview-clang.o \ +; RUN: %p/Inputs/test-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Reference: 'test-codeview-clang.o' +; ONE-NEXT: Target: 'test-codeview-msvc.o' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'test-codeview-clang.o' +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [002] {TypeAlias} 'INTPTR' -> '* const int' +; ONE-NEXT: [002] {Function} extern not_inlined 'foo' -> 'int' +; ONE-NEXT: -[003] {TypeAlias} 'INTEGER' -> 'int' +; ONE-NEXT: [003] {Parameter} 'ParamBool' -> 'bool' +; ONE-NEXT: [003] {Parameter} 'ParamPtr' -> '* const int' +; ONE-NEXT: [003] {Parameter} 'ParamUnsigned' -> 'unsigned' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Variable} 'CONSTANT' -> 'const int' +; ONE-NEXT: +[004] {TypeAlias} 'INTEGER' -> 'int' + +; Compare mode - Logical elements. +; The output shows in tabular form the 'missing (-), added (+)' elements, +; giving more context by swapping the reference and target object files. + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --compare=types \ +; RUN: --report=list \ +; RUN: --print=symbols,types,summary \ +; RUN: %p/Inputs/test-codeview-clang.o \ +; RUN: %p/Inputs/test-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Reference: 'test-codeview-clang.o' +; TWO-NEXT: Target: 'test-codeview-msvc.o' +; TWO-EMPTY: +; TWO-NEXT: (1) Missing Types: +; TWO-NEXT: -[003] {TypeAlias} 'INTEGER' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: (1) Added Types: +; TWO-NEXT: +[004] {TypeAlias} 'INTEGER' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: ---------------------------------------- +; TWO-NEXT: Element Expected Missing Added +; TWO-NEXT: ---------------------------------------- +; TWO-NEXT: Scopes 4 0 0 +; TWO-NEXT: Symbols 0 0 0 +; TWO-NEXT: Types 2 1 1 +; TWO-NEXT: Lines 0 0 0 +; TWO-NEXT: ---------------------------------------- +; TWO-NEXT: Total 6 1 1 + +; Changing the 'Reference' and 'Target' order: + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --compare=types \ +; RUN: --report=list \ +; RUN: --print=symbols,types,summary \ +; RUN: %p/Inputs/test-codeview-msvc.o \ +; RUN: %p/Inputs/test-codeview-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=THR %s + +; THR: Reference: 'test-codeview-msvc.o' +; THR-NEXT: Target: 'test-codeview-clang.o' +; THR-EMPTY: +; THR-NEXT: (1) Missing Types: +; THR-NEXT: -[004] {TypeAlias} 'INTEGER' -> 'int' +; THR-EMPTY: +; THR-NEXT: (1) Added Types: +; THR-NEXT: +[003] {TypeAlias} 'INTEGER' -> 'int' +; THR-EMPTY: +; THR-NEXT: ---------------------------------------- +; THR-NEXT: Element Expected Missing Added +; THR-NEXT: ---------------------------------------- +; THR-NEXT: Scopes 4 0 0 +; THR-NEXT: Symbols 0 0 0 +; THR-NEXT: Types 2 1 1 +; THR-NEXT: Lines 0 0 0 +; THR-NEXT: ---------------------------------------- +; THR-NEXT: Total 6 1 1 Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test @@ -0,0 +1,63 @@ +; Test case 1 - General options. + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Print basic details. +; The following command prints basic details for all the logical elements +; sorted by the debug information internal offset; it includes its lexical +; level and debug info format. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=offset \ +; RUN: --print=scopes,symbols,types,lines,instructions \ +; RUN: %p/Inputs/test-codeview-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=offset \ +; RUN: --print=elements \ +; RUN: %p/Inputs/test-codeview-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'test-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [002] {Function} extern not_inlined 'foo' -> 'int' +; ONE-NEXT: [003] {Parameter} 'ParamPtr' -> '* const int' +; ONE-NEXT: [003] {Parameter} 'ParamUnsigned' -> 'unsigned' +; ONE-NEXT: [003] {Parameter} 'ParamBool' -> 'bool' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Variable} 'CONSTANT' -> 'const int' +; ONE-NEXT: [004] 5 {Line} +; ONE-NEXT: [004] {Code} 'movl $0x7, 0x4(%rsp)' +; ONE-NEXT: [004] 6 {Line} +; ONE-NEXT: [004] {Code} 'movl $0x7, 0x1c(%rsp)' +; ONE-NEXT: [004] {Code} 'jmp 0x8' +; ONE-NEXT: [003] {TypeAlias} 'INTEGER' -> 'int' +; ONE-NEXT: [003] 2 {Line} +; ONE-NEXT: [003] {Code} 'subq $0x20, %rsp' +; ONE-NEXT: [003] {Code} 'andb $0x1, %r8b' +; ONE-NEXT: [003] {Code} 'movb %r8b, 0x1b(%rsp)' +; ONE-NEXT: [003] {Code} 'movl %edx, 0x14(%rsp)' +; ONE-NEXT: [003] {Code} 'movq %rcx, 0x8(%rsp)' +; ONE-NEXT: [003] 3 {Line} +; ONE-NEXT: [003] {Code} 'testb $0x1, 0x1b(%rsp)' +; ONE-NEXT: [003] {Code} 'je 0x15' +; ONE-NEXT: [003] 8 {Line} +; ONE-NEXT: [003] {Code} 'movl 0x14(%rsp), %eax' +; ONE-NEXT: [003] {Code} 'movl %eax, 0x1c(%rsp)' +; ONE-NEXT: [003] 9 {Line} +; ONE-NEXT: [003] {Code} 'movl 0x1c(%rsp), %eax' +; ONE-NEXT: [003] {Code} 'addq $0x20, %rsp' +; ONE-NEXT: [003] {Code} 'retq' +; ONE-NEXT: [002] {TypeAlias} 'INTPTR' -> '* const int' Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-select-logical-elements.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-select-logical-elements.test @@ -0,0 +1,68 @@ +; Test case 1 - General options + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Select logical elements. +; The following prints all 'instructions', 'symbols' and 'types' that +; contain 'inte' or 'movl' in their names or types, using a tab layout +; and given the number of matches. + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --select-nocase --select-regex \ +; RUN: --select=INTe --select=movl \ +; RUN: --report=list \ +; RUN: --print=symbols,types,instructions,summary \ +; RUN: %p/Inputs/test-codeview-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'test-codeview-clang.o' +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [003] {TypeAlias} 'INTEGER' -> 'int' +; ONE-NEXT: [004] {Code} 'movl $0x7, 0x1c(%rsp)' +; ONE-NEXT: [004] {Code} 'movl $0x7, 0x4(%rsp)' +; ONE-NEXT: [003] {Code} 'movl %eax, 0x1c(%rsp)' +; ONE-NEXT: [003] {Code} 'movl %edx, 0x14(%rsp)' +; ONE-NEXT: [003] {Code} 'movl 0x14(%rsp), %eax' +; ONE-NEXT: [003] {Code} 'movl 0x1c(%rsp), %eax' +; ONE-EMPTY: +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Element Total Printed +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Scopes 3 0 +; ONE-NEXT: Symbols 4 0 +; ONE-NEXT: Types 2 1 +; ONE-NEXT: Lines 21 6 +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Total 30 7 + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --select-regex --select-nocase \ +; RUN: --select=INTe \ +; RUN: --report=list \ +; RUN: --print=symbols,types \ +; RUN: %p/Inputs/test-codeview-clang.o \ +; RUN: %p/Inputs/test-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'test-codeview-clang.o' +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'test.cpp' +; TWO-NEXT: [003] {TypeAlias} 'INTEGER' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'test-codeview-msvc.o' +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'test.cpp' +; TWO-NEXT: [004] {TypeAlias} 'INTEGER' -> 'int' Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/02-coff-logical-lines.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/02-coff-logical-lines.test @@ -0,0 +1,54 @@ +; Test case 2 - Assembler instructions. + +; hello-world.cpp +; 1 extern int printf(const char * format, ... ); +; 2 +; 3 int main() +; 4 { +; 5 printf("Hello, World\n"); +; 6 return 0; +; 7 } + +; Logical lines. +; The logical views shows the intermixed lines and assembler instructions, +; allowing to compare the code generated by the different toolchains. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --print=lines,instructions \ +; RUN: %p/Inputs/hello-world-codeview-clang.o \ +; RUN: %p/Inputs/hello-world-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'hello-world-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'hello-world.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] {Function} extern not_inlined 'main' -> 'int' +; ONE-NEXT: [003] 4 {Line} +; ONE-NEXT: [003] {Code} 'subq $0x28, %rsp' +; ONE-NEXT: [003] {Code} 'movl $0x0, 0x24(%rsp)' +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] {Code} 'leaq (%rip), %rcx' +; ONE-NEXT: [003] {Code} 'callq 0x0' +; ONE-NEXT: [003] 6 {Line} +; ONE-NEXT: [003] {Code} 'xorl %eax, %eax' +; ONE-NEXT: [003] {Code} 'addq $0x28, %rsp' +; ONE-NEXT: [003] {Code} 'retq' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'hello-world-codeview-msvc.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'hello-world.cpp' +; ONE-NEXT: [002] {Producer} 'Microsoft (R) Optimizing Compiler' +; ONE-NEXT: [002] {Function} extern not_inlined 'main' -> 'int' +; ONE-NEXT: [003] 4 {Line} +; ONE-NEXT: [003] {Code} 'subq $0x28, %rsp' +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] {Code} 'leaq (%rip), %rcx' +; ONE-NEXT: [003] {Code} 'callq 0x0' +; ONE-NEXT: [003] 6 {Line} +; ONE-NEXT: [003] {Code} 'xorl %eax, %eax' +; ONE-NEXT: [003] 7 {Line} +; ONE-NEXT: [003] {Code} 'addq $0x28, %rsp' +; ONE-NEXT: [003] {Code} 'retq' Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/03-coff-incorrect-lexical-scope-typedef.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/03-coff-incorrect-lexical-scope-typedef.test @@ -0,0 +1,105 @@ +; Test case 3 - Incorrect lexical scope for typedef. + +; pr-44884.cpp +; 1 int bar(float Input) { return (int)Input; } +; 2 +; 3 unsigned foo(char Param) { +; 4 typedef int INT; // ** Definition for INT ** +; 5 INT Value = Param; +; 6 { +; 7 typedef float FLOAT; // ** Definition for FLOAT ** +; 8 { +; 9 FLOAT Added = Value + Param; +; 10 Value = bar(Added); +; 11 } +; 12 } +; 13 return Value + Param; +; 14 } + +; The lines 4 and 7 contains 2 typedefs, defined at different lexical +; scopes. + +; The above test is used to illustrates a scope issue found in the +; Clang compiler. +; PR44884: https://bugs.llvm.org/show_bug.cgi?id=44884 +; PR44229: https://github.com/llvm/llvm-project/issues/44229 + +; In the following logical views, we can see that the Clang compiler +; emits both typedefs at the same lexical scope (3), which is wrong. +; GCC and MSVC emit correct lexical scope for both typedefs. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --output-sort=kind \ +; RUN: --print=symbols,types,lines \ +; RUN: %p/Inputs/pr-44884-codeview-clang.o \ +; RUN: %p/Inputs/pr-44884-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] {Function} extern not_inlined 'bar' -> 'int' +; ONE-NEXT: [003] {Parameter} 'Input' -> 'float' +; ONE-NEXT: [003] 1 {Line} +; ONE-NEXT: [002] {Function} extern not_inlined 'foo' -> 'unsigned' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Variable} 'Added' -> 'float' +; ONE-NEXT: [004] 9 {Line} +; ONE-NEXT: [004] 10 {Line} +; ONE-NEXT: [003] {Parameter} 'Param' -> 'char' +; ONE-NEXT: [003] {TypeAlias} 'FLOAT' -> 'float' +; ONE-NEXT: [003] {TypeAlias} 'INT' -> 'int' +; ONE-NEXT: [003] {Variable} 'Value' -> 'int' +; ONE-NEXT: [003] 3 {Line} +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] 13 {Line} +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'pr-44884-codeview-msvc.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; ONE-NEXT: [002] {Producer} 'Microsoft (R) Optimizing Compiler' +; ONE-NEXT: [002] {Function} extern not_inlined 'bar' -> 'int' +; ONE-NEXT: [003] {Variable} 'Input' -> 'float' +; ONE-NEXT: [003] 1 {Line} +; ONE-NEXT: [002] {Function} extern not_inlined 'foo' -> 'unsigned' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Block} +; ONE-NEXT: [005] {Variable} 'Added' -> 'float' +; ONE-NEXT: [004] {TypeAlias} 'FLOAT' -> 'float' +; ONE-NEXT: [004] 9 {Line} +; ONE-NEXT: [004] 10 {Line} +; ONE-NEXT: [003] {TypeAlias} 'INT' -> 'int' +; ONE-NEXT: [003] {Variable} 'Param' -> 'char' +; ONE-NEXT: [003] {Variable} 'Value' -> 'int' +; ONE-NEXT: [003] 3 {Line} +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] 13 {Line} +; ONE-NEXT: [003] 14 {Line} + +; Using the selection facilities, we can produce a simple tabular +; output showing just the logical types that are 'Typedef'. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-types=Typedef \ +; RUN: --report=list \ +; RUN: --print=types \ +; RUN: %p/Inputs/pr-44884-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; TWO-NEXT: [003] {TypeAlias} 'FLOAT' -> 'float' +; TWO-NEXT: [003] {TypeAlias} 'INT' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'pr-44884-codeview-msvc.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; TWO-NEXT: [004] {TypeAlias} 'FLOAT' -> 'float' +; TWO-NEXT: [003] {TypeAlias} 'INT' -> 'int' Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/04-coff-missing-nested-enumerators.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/04-coff-missing-nested-enumerators.test @@ -0,0 +1,135 @@ +; Test case 4 - Missing nested enumerations. + +; pr-46466.cpp +; 1 struct Struct { +; 2 union Union { +; 3 enum NestedEnum { RED, BLUE }; +; 4 }; +; 5 Union U; +; 6 }; +; 7 +; 8 Struct S; +; 9 int test() { +; 10 return S.U.BLUE; +; 11 } + +; The above test is used to illustrate a scope issue found in the Clang +; compiler. +; PR46466: https://bugs.llvm.org/show_bug.cgi?id=46466 +; PR45811: https://github.com/llvm/llvm-project/issues/45811 + +; In the following logical views, we can see that the DWARF debug +; information generated by the Clang compiler does not include any +; references to the enumerators 'RED' and 'BLUE'. The DWARF generated +; by GCC, CodeView generated by Clang and MSVC, it does include such +; references. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --output-sort=name \ +; RUN: --print=symbols,types \ +; RUN: %p/Inputs/pr-46466-codeview-clang.o \ +; RUN: %p/Inputs/pr-46466-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] {Variable} extern 'S' -> 'Struct' +; ONE-NEXT: [002] 1 {Struct} 'Struct' +; ONE-NEXT: [003] {Member} public 'U' -> 'Union' +; ONE-NEXT: [003] 2 {Union} 'Union' +; ONE-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'int' +; ONE-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; ONE-NEXT: [005] {Enumerator} 'RED' = '0x0' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'pr-46466-codeview-msvc.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; ONE-NEXT: [002] {Producer} 'Microsoft (R) Optimizing Compiler' +; ONE-NEXT: [002] {Variable} extern 'S' -> 'Struct' +; ONE-NEXT: [002] 1 {Struct} 'Struct' +; ONE-NEXT: [003] {Member} public 'U' -> 'Union' +; ONE-NEXT: [003] 2 {Union} 'Union' +; ONE-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'int' +; ONE-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; ONE-NEXT: [005] {Enumerator} 'RED' = '0x0' + +; Using the selection facilities, we can produce a logical view +; showing just the logical types that are 'Enumerator' and its +; parents. The logical view is sorted by the types name. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-types=Enumerator \ +; RUN: --report=parents \ +; RUN: --print=types \ +; RUN: %p/Inputs/pr-46466-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; TWO-NEXT: [002] 1 {Struct} 'Struct' +; TWO-NEXT: [003] 2 {Union} 'Union' +; TWO-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'int' +; TWO-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; TWO-NEXT: [005] {Enumerator} 'RED' = '0x0' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'pr-46466-codeview-msvc.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; TWO-NEXT: [002] 1 {Struct} 'Struct' +; TWO-NEXT: [003] 2 {Union} 'Union' +; TWO-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'int' +; TWO-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; TWO-NEXT: [005] {Enumerator} 'RED' = '0x0' + +; Using the selection facilities, we can produce a simple tabular output +; including a summary for the logical types that are 'Enumerator'. The +; logical view is sorted by the types name. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-types=Enumerator \ +; RUN: --print=types,summary \ +; RUN: %p/Inputs/pr-46466-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=THR %s + +; THR: Logical View: +; THR-NEXT: [000] {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64 +; THR-EMPTY: +; THR-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; THR-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; THR-NEXT: [005] {Enumerator} 'RED' = '0x0' +; THR-EMPTY: +; THR-NEXT: ----------------------------- +; THR-NEXT: Element Total Printed +; THR-NEXT: ----------------------------- +; THR-NEXT: Scopes 5 0 +; THR-NEXT: Symbols 2 0 +; THR-NEXT: Types 6 2 +; THR-NEXT: Lines 0 0 +; THR-NEXT: ----------------------------- +; THR-NEXT: Total 13 2 +; THR-EMPTY: +; THR-NEXT: Logical View: +; THR-NEXT: [000] {File} 'pr-46466-codeview-msvc.o' -> COFF-x86-64 +; THR-EMPTY: +; THR-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; THR-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; THR-NEXT: [005] {Enumerator} 'RED' = '0x0' +; THR-EMPTY: +; THR-NEXT: ----------------------------- +; THR-NEXT: Element Total Printed +; THR-NEXT: ----------------------------- +; THR-NEXT: Scopes 5 0 +; THR-NEXT: Symbols 2 0 +; THR-NEXT: Types 7 2 +; THR-NEXT: Lines 0 0 +; THR-NEXT: ----------------------------- +; THR-NEXT: Total 14 2 Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/05-coff-incorrect-lexical-scope-variable.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/05-coff-incorrect-lexical-scope-variable.test @@ -0,0 +1,95 @@ +; Test case 5 - Incorrect lexical scope variable. + +; pr-43860.cpp +; 1 #include "definitions.h" +; 2 forceinline int InlineFunction(int Param) { +; 3 int Var_1 = Param; +; 4 { +; 5 int Var_2 = Param + Var_1; +; 6 Var_1 = Var_2; +; 7 } +; 8 return Var_1; +; 9 } +; 10 +; 11 int test(int Param_1, int Param_2) { +; 12 int A = Param_1; +; 13 A += InlineFunction(Param_2); +; 14 return A; +; 15 } + +; The above test is used to illustrate a variable issue found in the +; Clang compiler. +; PR43860: https://bugs.llvm.org/show_bug.cgi?id=43860 +; PR43205: https://github.com/llvm/llvm-project/issues/43205 + +; In the following logical views, we can see that the CodeView debug +; information generated by the Clang compiler shows the variables +; 'Var_1' and 'Var_2' are at the same lexical scope (4) in the function +; 'InlineFuction'. +; The DWARF generated by GCC/Clang and CodeView generated by MSVC, show +; those variables at the correct lexical scope: '3' and '4' respectively. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --output-sort=name \ +; RUN: --print=symbols \ +; RUN: %p/Inputs/pr-43860-codeview-clang.o \ +; RUN: %p/Inputs/pr-43860-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] 2 {Function} inlined 'InlineFunction' -> 'int' +; ONE-NEXT: [003] {Parameter} '' -> 'int' +; ONE-NEXT: [002] {Function} extern not_inlined 'test' -> 'int' +; ONE-NEXT: [003] {Variable} 'A' -> 'int' +; ONE-NEXT: [003] {InlinedFunction} inlined 'InlineFunction' -> 'int' +; ONE-NEXT: [004] {Parameter} 'Param' -> 'int' +; ONE-NEXT: [004] {Variable} 'Var_1' -> 'int' +; ONE-NEXT: [004] {Variable} 'Var_2' -> 'int' +; ONE-NEXT: [003] {Parameter} 'Param_1' -> 'int' +; ONE-NEXT: [003] {Parameter} 'Param_2' -> 'int' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'pr-43860-codeview-msvc.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; ONE-NEXT: [002] {Producer} 'Microsoft (R) Optimizing Compiler' +; ONE-NEXT: [002] {Function} extern declared_inlined 'InlineFunction' -> 'int' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Variable} 'Var_2' -> 'int' +; ONE-NEXT: [003] {Variable} 'Param' -> 'int' +; ONE-NEXT: [003] {Variable} 'Var_1' -> 'int' +; ONE-NEXT: [002] {Function} extern not_inlined 'test' -> 'int' +; ONE-NEXT: [003] {Variable} 'A' -> 'int' +; ONE-NEXT: [003] {Variable} 'Param_1' -> 'int' +; ONE-NEXT: [003] {Variable} 'Param_2' -> 'int' + +; Using the selection facilities, we can produce a simple tabular output +; showing just the logical elements that have in their name the 'var' +; pattern. The logical view is sorted by the variables name. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-regex --select-nocase \ +; RUN: --select=Var \ +; RUN: --report=list \ +; RUN: --print=symbols \ +; RUN: %p/Inputs/pr-43860-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; TWO-NEXT: [004] {Variable} 'Var_1' -> 'int' +; TWO-NEXT: [004] {Variable} 'Var_2' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'pr-43860-codeview-msvc.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; TWO-NEXT: [003] {Variable} 'Var_1' -> 'int' +; TWO-NEXT: [004] {Variable} 'Var_2' -> 'int' Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/06-coff-full-logical-view.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/06-coff-full-logical-view.test @@ -0,0 +1,97 @@ +; Test case 6 - Full logical view + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Print low level details. +; The following command prints low level information that includes +; offsets within the debug information section, debug location +; operands, linkage names, etc. + +; RUN: llvm-debuginfo-analyzer --attribute=all \ +; RUN: --print=all \ +; RUN: %p/Inputs/test-codeview-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [0x0000000000][000] {File} '{{.*}}test-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [0x0000000000][001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [0x0000000000][002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: {Directory} 'test.cpp' +; ONE-NEXT: {Directory} 'x:/tests/input' +; ONE-NEXT: {File} 'general' +; ONE-NEXT: {File} 'test.cpp' +; ONE-NEXT: {Public} 'foo' [0x0000000000:0x0000000046] +; ONE-NEXT: [0x0000000000][002] {TypeAlias} 'INTPTR' -> [0x0000001001]'* const int' +; ONE-NEXT: [0x0000000030][002] {BaseType} 'bool' +; ONE-NEXT: [0x0000000000][002] {Function} extern not_inlined 'foo' -> [0x0000000074]'int' +; ONE-NEXT: [0x0000000000][003] {Range} Lines 2:9 [0x0000000000:0x0000000046] +; ONE-NEXT: [0x0000000000][003] {Linkage} 0x1 '?foo@@YAHPEBHI_N@Z' +; ONE-NEXT: [0x0000000000][003] {TypeAlias} 'INTEGER' -> [0x0000000074]'int' +; ONE-NEXT: [0x0000000000][003] {Parameter} 'ParamBool' -> [0x0000000030]'bool' +; ONE-NEXT: [0x0000000000][004] {Coverage} 70.00% (49/70) +; ONE-NEXT: [0x0000000000][004] {Location} Lines 3:9 [0x0000000016:0x0000000047] +; ONE-NEXT: [0x0000000000][005] {Entry} frame_pointer_rel 27 +; ONE-NEXT: [0x0000000000][003] {Parameter} 'ParamPtr' -> [0x0000001001]'* const int' +; ONE-NEXT: [0x0000000000][004] {Coverage} 70.00% (49/70) +; ONE-NEXT: [0x0000000000][004] {Location} Lines 3:9 [0x0000000016:0x0000000047] +; ONE-NEXT: [0x0000000000][005] {Entry} frame_pointer_rel 8 +; ONE-NEXT: [0x0000000000][003] {Parameter} 'ParamUnsigned' -> [0x0000000075]'unsigned' +; ONE-NEXT: [0x0000000000][004] {Coverage} 70.00% (49/70) +; ONE-NEXT: [0x0000000000][004] {Location} Lines 3:9 [0x0000000016:0x0000000047] +; ONE-NEXT: [0x0000000000][005] {Entry} frame_pointer_rel 20 +; ONE-NEXT: [0x0000000000][003] {Block} +; ONE-NEXT: [0x0000000000][004] {Range} Lines ?:? [0x0000000021:0x0000000035] +; ONE-NEXT: [0x0000000000][004] {Variable} 'CONSTANT' -> [0x0000001000]'const int' +; ONE-NEXT: [0x0000000000][005] {Coverage} 105.00% (21/20) +; ONE-NEXT: [0x0000000000][005] {Location} Lines ?:? [0x0000000021:0x0000000036] +; ONE-NEXT: [0x0000000000][006] {Entry} frame_pointer_rel 4 +; ONE-EMPTY: +; ONE-NEXT: [0x0000000021][004] {Source} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000021][004] 5 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000021][004] {Code} 'movl $0x7, 0x4(%rsp)' +; ONE-NEXT: [0x0000000029][004] 6 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000029][004] {Code} 'movl $0x7, 0x1c(%rsp)' +; ONE-NEXT: [0x0000000031][004] {Code} 'jmp 0x8' +; ONE-NEXT: [0x0000000000][003] 2 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000000][003] {Code} 'subq $0x20, %rsp' +; ONE-NEXT: [0x0000000004][003] {Code} 'andb $0x1, %r8b' +; ONE-NEXT: [0x0000000008][003] {Code} 'movb %r8b, 0x1b(%rsp)' +; ONE-NEXT: [0x000000000d][003] {Code} 'movl %edx, 0x14(%rsp)' +; ONE-NEXT: [0x0000000011][003] {Code} 'movq %rcx, 0x8(%rsp)' +; ONE-NEXT: [0x0000000016][003] 3 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000016][003] {Code} 'testb $0x1, 0x1b(%rsp)' +; ONE-NEXT: [0x000000001b][003] {Code} 'je 0x15' +; ONE-NEXT: [0x0000000036][003] 8 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000036][003] {Code} 'movl 0x14(%rsp), %eax' +; ONE-NEXT: [0x000000003a][003] {Code} 'movl %eax, 0x1c(%rsp)' +; ONE-NEXT: [0x000000003e][003] 9 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x000000003e][003] {Code} 'movl 0x1c(%rsp), %eax' +; ONE-NEXT: [0x0000000042][003] {Code} 'addq $0x20, %rsp' +; ONE-NEXT: [0x0000000046][003] {Code} 'retq' +; ONE-NEXT: [0x0000000074][002] {BaseType} 'int' +; ONE-NEXT: [0x0000000075][002] {BaseType} 'unsigned' +; ONE-EMPTY: +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Element Total Printed +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Scopes 3 3 +; ONE-NEXT: Symbols 4 4 +; ONE-NEXT: Types 5 5 +; ONE-NEXT: Lines 21 21 +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Total 33 33 +; ONE-EMPTY: +; ONE-NEXT: Scope Sizes: +; ONE-EMPTY: +; ONE-NEXT: Totals by lexical level: +; ONE-EMPTY: Index: llvm/test/tools/llvm-debuginfo-analyzer/DWARF/01-dwarf-compare-logical-elements.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/DWARF/01-dwarf-compare-logical-elements.test @@ -0,0 +1,101 @@ +; Test case 1 - General options + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Compare mode - Logical view. +; The output shows in view form the 'missing (-), added (+)' elements, +; giving more context by swapping the reference and target object files. + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --compare=types \ +; RUN: --report=view \ +; RUN: --print=symbols,types \ +; RUN: %p/Inputs/test-dwarf-clang.o \ +; RUN: %p/Inputs/test-dwarf-gcc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Reference: 'test-dwarf-clang.o' +; ONE-NEXT: Target: 'test-dwarf-gcc.o' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'test-dwarf-clang.o' +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [002] 1 {TypeAlias} 'INTPTR' -> '* const int' +; ONE-NEXT: [002] 2 {Function} extern not_inlined 'foo' -> 'int' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER' +; ONE-NEXT: +[004] 4 {TypeAlias} 'INTEGER' -> 'int' +; ONE-NEXT: [003] 2 {Parameter} 'ParamBool' -> 'bool' +; ONE-NEXT: [003] 2 {Parameter} 'ParamPtr' -> 'INTPTR' +; ONE-NEXT: [003] 2 {Parameter} 'ParamUnsigned' -> 'unsigned int' +; ONE-NEXT: -[003] 4 {TypeAlias} 'INTEGER' -> 'int' + +; Compare mode - Logical elements. +; The output shows in tabular form the 'missing (-), added (+)' elements, +; giving more context by swapping the reference and target object files. + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --compare=types \ +; RUN: --report=list \ +; RUN: --print=symbols,types,summary \ +; RUN: %p/Inputs/test-dwarf-clang.o \ +; RUN: %p/Inputs/test-dwarf-gcc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Reference: 'test-dwarf-clang.o' +; TWO-NEXT: Target: 'test-dwarf-gcc.o' +; TWO-EMPTY: +; TWO-NEXT: (1) Missing Types: +; TWO-NEXT: -[003] 4 {TypeAlias} 'INTEGER' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: (1) Added Types: +; TWO-NEXT: +[004] 4 {TypeAlias} 'INTEGER' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: ---------------------------------------- +; TWO-NEXT: Element Expected Missing Added +; TWO-NEXT: ---------------------------------------- +; TWO-NEXT: Scopes 4 0 0 +; TWO-NEXT: Symbols 0 0 0 +; TWO-NEXT: Types 2 1 1 +; TWO-NEXT: Lines 0 0 0 +; TWO-NEXT: ---------------------------------------- +; TWO-NEXT: Total 6 1 1 + +; Changing the 'Reference' and 'Target' order: + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --compare=types \ +; RUN: --report=list \ +; RUN: --print=symbols,types,summary \ +; RUN: %p/Inputs/test-dwarf-gcc.o \ +; RUN: %p/Inputs/test-dwarf-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=THR %s + +; THR: Reference: 'test-dwarf-gcc.o' +; THR-NEXT: Target: 'test-dwarf-clang.o' +; THR-EMPTY: +; THR-NEXT: (1) Missing Types: +; THR-NEXT: -[004] 4 {TypeAlias} 'INTEGER' -> 'int' +; THR-EMPTY: +; THR-NEXT: (1) Added Types: +; THR-NEXT: +[003] 4 {TypeAlias} 'INTEGER' -> 'int' +; THR-EMPTY: +; THR-NEXT: ---------------------------------------- +; THR-NEXT: Element Expected Missing Added +; THR-NEXT: ---------------------------------------- +; THR-NEXT: Scopes 4 0 0 +; THR-NEXT: Symbols 0 0 0 +; THR-NEXT: Types 2 1 1 +; THR-NEXT: Lines 0 0 0 +; THR-NEXT: ---------------------------------------- +; THR-NEXT: Total 6 1 1 Index: llvm/test/tools/llvm-debuginfo-analyzer/DWARF/01-dwarf-print-basic-details.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/DWARF/01-dwarf-print-basic-details.test @@ -0,0 +1,67 @@ +; Test case 1 - General options. + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Print basic details. +; The following command prints basic details for all the logical elements +; sorted by the debug information internal offset; it includes its lexical +; level and debug info format. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=offset \ +; RUN: --print=scopes,symbols,types,lines,instructions \ +; RUN: %p/Inputs/test-dwarf-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=offset \ +; RUN: --print=elements \ +; RUN: %p/Inputs/test-dwarf-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'test-dwarf-clang.o' -> elf64-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [002] 2 {Function} extern not_inlined 'foo' -> 'int' +; ONE-NEXT: [003] 2 {Parameter} 'ParamPtr' -> 'INTPTR' +; ONE-NEXT: [003] 2 {Parameter} 'ParamUnsigned' -> 'unsigned int' +; ONE-NEXT: [003] 2 {Parameter} 'ParamBool' -> 'bool' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER' +; ONE-NEXT: [004] 5 {Line} +; ONE-NEXT: [004] {Code} 'movl $0x7, -0x1c(%rbp)' +; ONE-NEXT: [004] 6 {Line} +; ONE-NEXT: [004] {Code} 'movl $0x7, -0x4(%rbp)' +; ONE-NEXT: [004] {Code} 'jmp 0x6' +; ONE-NEXT: [004] 8 {Line} +; ONE-NEXT: [004] {Code} 'movl -0x14(%rbp), %eax' +; ONE-NEXT: [003] 4 {TypeAlias} 'INTEGER' -> 'int' +; ONE-NEXT: [003] 2 {Line} +; ONE-NEXT: [003] {Code} 'pushq %rbp' +; ONE-NEXT: [003] {Code} 'movq %rsp, %rbp' +; ONE-NEXT: [003] {Code} 'movb %dl, %al' +; ONE-NEXT: [003] {Code} 'movq %rdi, -0x10(%rbp)' +; ONE-NEXT: [003] {Code} 'movl %esi, -0x14(%rbp)' +; ONE-NEXT: [003] {Code} 'andb $0x1, %al' +; ONE-NEXT: [003] {Code} 'movb %al, -0x15(%rbp)' +; ONE-NEXT: [003] 3 {Line} +; ONE-NEXT: [003] {Code} 'testb $0x1, -0x15(%rbp)' +; ONE-NEXT: [003] {Code} 'je 0x13' +; ONE-NEXT: [003] 8 {Line} +; ONE-NEXT: [003] {Code} 'movl %eax, -0x4(%rbp)' +; ONE-NEXT: [003] 9 {Line} +; ONE-NEXT: [003] {Code} 'movl -0x4(%rbp), %eax' +; ONE-NEXT: [003] {Code} 'popq %rbp' +; ONE-NEXT: [003] {Code} 'retq' +; ONE-NEXT: [003] 9 {Line} +; ONE-NEXT: [002] 1 {TypeAlias} 'INTPTR' -> '* const int' Index: llvm/test/tools/llvm-debuginfo-analyzer/DWARF/01-dwarf-select-logical-elements.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/DWARF/01-dwarf-select-logical-elements.test @@ -0,0 +1,71 @@ +; Test case 1 - General options + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Select logical elements. +; The following prints all 'instructions', 'symbols' and 'types' that +; contain 'inte' or 'movl' in their names or types, using a tab layout +; and given the number of matches. + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --select-nocase --select-regex \ +; RUN: --select=INTe --select=movl \ +; RUN: --report=list \ +; RUN: --print=symbols,types,instructions,summary \ +; RUN: %p/Inputs/test-dwarf-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'test-dwarf-clang.o' +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [004] {Code} 'movl $0x7, -0x1c(%rbp)' +; ONE-NEXT: [004] {Code} 'movl $0x7, -0x4(%rbp)' +; ONE-NEXT: [003] {Code} 'movl %eax, -0x4(%rbp)' +; ONE-NEXT: [003] {Code} 'movl %esi, -0x14(%rbp)' +; ONE-NEXT: [004] {Code} 'movl -0x14(%rbp), %eax' +; ONE-NEXT: [003] {Code} 'movl -0x4(%rbp), %eax' +; ONE-NEXT: [003] 4 {TypeAlias} 'INTEGER' -> 'int' +; ONE-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER' +; ONE-EMPTY: +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Element Total Printed +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Scopes 3 0 +; ONE-NEXT: Symbols 4 1 +; ONE-NEXT: Types 2 1 +; ONE-NEXT: Lines 17 6 +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Total 26 8 + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --select-regex --select-nocase \ +; RUN: --select=INTe \ +; RUN: --report=list \ +; RUN: --print=symbols,types \ +; RUN: %p/Inputs/test-dwarf-clang.o \ +; RUN: %p/Inputs/test-dwarf-gcc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'test-dwarf-clang.o' +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'test.cpp' +; TWO-NEXT: [003] 4 {TypeAlias} 'INTEGER' -> 'int' +; TWO-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'test-dwarf-gcc.o' +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'test.cpp' +; TWO-NEXT: [004] 4 {TypeAlias} 'INTEGER' -> 'int' +; TWO-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER' Index: llvm/test/tools/llvm-debuginfo-analyzer/DWARF/02-dwarf-logical-lines.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/DWARF/02-dwarf-logical-lines.test @@ -0,0 +1,63 @@ +; Test case 2 - Assembler instructions. + +; hello-world.cpp +; 1 extern int printf(const char * format, ... ); +; 2 +; 3 int main() +; 4 { +; 5 printf("Hello, World\n"); +; 6 return 0; +; 7 } + +; Logical lines. +; The logical views shows the intermixed lines and assembler instructions, +; allowing to compare the code generated by the different toolchains. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --print=lines,instructions \ +; RUN: %p/Inputs/hello-world-dwarf-clang.o \ +; RUN: %p/Inputs/hello-world-dwarf-gcc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'hello-world-dwarf-clang.o' -> elf64-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'hello-world.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] 3 {Function} extern not_inlined 'main' -> 'int' +; ONE-NEXT: [003] 4 {Line} +; ONE-NEXT: [003] {Code} 'pushq %rbp' +; ONE-NEXT: [003] {Code} 'movq %rsp, %rbp' +; ONE-NEXT: [003] {Code} 'subq $0x10, %rsp' +; ONE-NEXT: [003] {Code} 'movl $0x0, -0x4(%rbp)' +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] {Code} 'leaq (%rip), %rdi' +; ONE-NEXT: [003] {Code} 'movb $0x0, %al' +; ONE-NEXT: [003] {Code} 'callq 0x0' +; ONE-NEXT: [003] 6 {Line} +; ONE-NEXT: [003] {Code} 'xorl %eax, %eax' +; ONE-NEXT: [003] {Code} 'addq $0x10, %rsp' +; ONE-NEXT: [003] {Code} 'popq %rbp' +; ONE-NEXT: [003] {Code} 'retq' +; ONE-NEXT: [003] 6 {Line} +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'hello-world-dwarf-gcc.o' -> elf64-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'hello-world.cpp' +; ONE-NEXT: [002] {Producer} 'GNU C++14 10.3.0 {{.*}}' +; ONE-NEXT: [002] 3 {Function} extern not_inlined 'main' -> 'int' +; ONE-NEXT: [003] 4 {Line} +; ONE-NEXT: [003] {Code} 'endbr64' +; ONE-NEXT: [003] {Code} 'pushq %rbp' +; ONE-NEXT: [003] {Code} 'movq %rsp, %rbp' +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] {Code} 'leaq (%rip), %rdi' +; ONE-NEXT: [003] {Code} 'movl $0x0, %eax' +; ONE-NEXT: [003] {Code} 'callq 0x0' +; ONE-NEXT: [003] 6 {Line} +; ONE-NEXT: [003] {Code} 'movl $0x0, %eax' +; ONE-NEXT: [003] 7 {Line} +; ONE-NEXT: [003] {Code} 'popq %rbp' +; ONE-NEXT: [003] {Code} 'retq' +; ONE-NEXT: [003] 7 {Line} Index: llvm/test/tools/llvm-debuginfo-analyzer/DWARF/03-dwarf-incorrect-lexical-scope-typedef.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/DWARF/03-dwarf-incorrect-lexical-scope-typedef.test @@ -0,0 +1,124 @@ +; Test case 3 - Incorrect lexical scope for typedef. + +; pr-44884.cpp +; 1 int bar(float Input) { return (int)Input; } +; 2 +; 3 unsigned foo(char Param) { +; 4 typedef int INT; // ** Definition for INT ** +; 5 INT Value = Param; +; 6 { +; 7 typedef float FLOAT; // ** Definition for FLOAT ** +; 8 { +; 9 FLOAT Added = Value + Param; +; 10 Value = bar(Added); +; 11 } +; 12 } +; 13 return Value + Param; +; 14 } + +; The lines 4 and 7 contains 2 typedefs, defined at different lexical +; scopes. + +; The above test is used to illustrates a scope issue found in the +; Clang compiler. +; PR44884: https://bugs.llvm.org/show_bug.cgi?id=44884 +; PR44229: https://github.com/llvm/llvm-project/issues/44229 + +; In the following logical views, we can see that the Clang compiler +; emits both typedefs at the same lexical scope (3), which is wrong. +; GCC and MSVC emit correct lexical scope for both typedefs. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --output-sort=kind \ +; RUN: --print=symbols,types,lines \ +; RUN: %p/Inputs/pr-44884-dwarf-clang.o \ +; RUN: %p/Inputs/pr-44884-dwarf-gcc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'pr-44884-dwarf-clang.o' -> elf64-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] 1 {Function} extern not_inlined 'bar' -> 'int' +; ONE-NEXT: [003] 1 {Parameter} 'Input' -> 'float' +; ONE-NEXT: [003] 1 {Line} +; ONE-NEXT: [003] 1 {Line} +; ONE-NEXT: [003] 1 {Line} +; ONE-NEXT: [002] 3 {Function} extern not_inlined 'foo' -> 'unsigned int' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] 9 {Variable} 'Added' -> 'FLOAT' +; ONE-NEXT: [004] 9 {Line} +; ONE-NEXT: [004] 9 {Line} +; ONE-NEXT: [004] 9 {Line} +; ONE-NEXT: [004] 9 {Line} +; ONE-NEXT: [004] 9 {Line} +; ONE-NEXT: [004] 10 {Line} +; ONE-NEXT: [004] 10 {Line} +; ONE-NEXT: [004] 10 {Line} +; ONE-NEXT: [004] 13 {Line} +; ONE-NEXT: [003] 3 {Parameter} 'Param' -> 'char' +; ONE-NEXT: [003] 7 {TypeAlias} 'FLOAT' -> 'float' +; ONE-NEXT: [003] 4 {TypeAlias} 'INT' -> 'int' +; ONE-NEXT: [003] 5 {Variable} 'Value' -> 'INT' +; ONE-NEXT: [003] 3 {Line} +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] 13 {Line} +; ONE-NEXT: [003] 13 {Line} +; ONE-NEXT: [003] 13 {Line} +; ONE-NEXT: [003] 13 {Line} +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'pr-44884-dwarf-gcc.o' -> elf64-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; ONE-NEXT: [002] {Producer} 'GNU C++14 10.3.0 {{.*}}' +; ONE-NEXT: [002] 1 {Function} extern not_inlined 'bar' -> 'int' +; ONE-NEXT: [003] 1 {Parameter} 'Input' -> 'float' +; ONE-NEXT: [003] 1 {Line} +; ONE-NEXT: [003] 1 {Line} +; ONE-NEXT: [003] 1 {Line} +; ONE-NEXT: [002] 3 {Function} extern not_inlined 'foo' -> 'unsigned int' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Block} +; ONE-NEXT: [005] 9 {Variable} 'Added' -> 'FLOAT' +; ONE-NEXT: [005] 9 {Line} +; ONE-NEXT: [005] 9 {Line} +; ONE-NEXT: [005] 9 {Line} +; ONE-NEXT: [005] 10 {Line} +; ONE-NEXT: [005] 13 {Line} +; ONE-NEXT: [004] 7 {TypeAlias} 'FLOAT' -> 'float' +; ONE-NEXT: [003] 3 {Parameter} 'Param' -> 'char' +; ONE-NEXT: [003] 4 {TypeAlias} 'INT' -> 'int' +; ONE-NEXT: [003] 5 {Variable} 'Value' -> 'INT' +; ONE-NEXT: [003] 3 {Line} +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] 13 {Line} +; ONE-NEXT: [003] 14 {Line} +; ONE-NEXT: [003] 14 {Line} + +; Using the selection facilities, we can produce a simple tabular +; output showing just the logical types that are 'Typedef'. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-types=Typedef \ +; RUN: --report=list \ +; RUN: --print=types \ +; RUN: %p/Inputs/pr-44884-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'pr-44884-dwarf-clang.o' -> elf64-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; TWO-NEXT: [003] 7 {TypeAlias} 'FLOAT' -> 'float' +; TWO-NEXT: [003] 4 {TypeAlias} 'INT' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'pr-44884-dwarf-gcc.o' -> elf64-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; TWO-NEXT: [004] 7 {TypeAlias} 'FLOAT' -> 'float' +; TWO-NEXT: [003] 4 {TypeAlias} 'INT' -> 'int' Index: llvm/test/tools/llvm-debuginfo-analyzer/DWARF/04-dwarf-missing-nested-enumerators.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/DWARF/04-dwarf-missing-nested-enumerators.test @@ -0,0 +1,124 @@ +; Test case 4 - Missing nested enumerations. + +; pr-46466.cpp +; 1 struct Struct { +; 2 union Union { +; 3 enum NestedEnum { RED, BLUE }; +; 4 }; +; 5 Union U; +; 6 }; +; 7 +; 8 Struct S; +; 9 int test() { +; 10 return S.U.BLUE; +; 11 } + +; The above test is used to illustrate a scope issue found in the Clang +; compiler. +; PR46466: https://bugs.llvm.org/show_bug.cgi?id=46466 +; PR45811: https://github.com/llvm/llvm-project/issues/45811 + +; In the following logical views, we can see that the DWARF debug +; information generated by the Clang compiler does not include any +; references to the enumerators 'RED' and 'BLUE'. The DWARF generated +; by GCC, CodeView generated by Clang and MSVC, it does include such +; references. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --output-sort=name \ +; RUN: --print=symbols,types \ +; RUN: %p/Inputs/pr-46466-dwarf-clang.o \ +; RUN: %p/Inputs/pr-46466-dwarf-gcc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] 8 {Variable} extern 'S' -> 'Struct' +; ONE-NEXT: [002] 1 {Struct} 'Struct' +; ONE-NEXT: [003] 5 {Member} public 'U' -> 'Union' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; ONE-NEXT: [002] {Producer} 'GNU C++14 10.3.0 {{.*}}' +; ONE-NEXT: [002] 8 {Variable} extern 'S' -> 'Struct' +; ONE-NEXT: [002] 1 {Struct} 'Struct' +; ONE-NEXT: [003] 5 {Member} public 'U' -> 'Union' +; ONE-NEXT: [003] 2 {Union} 'Union' +; ONE-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'unsigned int' +; ONE-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; ONE-NEXT: [005] {Enumerator} 'RED' = '0x0' + +; Using the selection facilities, we can produce a logical view +; showing just the logical types that are 'Enumerator' and its +; parents. The logical view is sorted by the types name. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-types=Enumerator \ +; RUN: --report=parents \ +; RUN: --print=types \ +; RUN: %p/Inputs/pr-46466-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; TWO-NEXT: [002] 1 {Struct} 'Struct' +; TWO-NEXT: [003] 2 {Union} 'Union' +; TWO-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'unsigned int' +; TWO-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; TWO-NEXT: [005] {Enumerator} 'RED' = '0x0' + +; Using the selection facilities, we can produce a simple tabular output +; including a summary for the logical types that are 'Enumerator'. The +; logical view is sorted by the types name. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-types=Enumerator \ +; RUN: --print=types,summary \ +; RUN: %p/Inputs/pr-46466-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=THR %s + +; THR: Logical View: +; THR-NEXT: [000] {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64 +; THR-EMPTY: +; THR-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; THR-EMPTY: +; THR-NEXT: ----------------------------- +; THR-NEXT: Element Total Printed +; THR-NEXT: ----------------------------- +; THR-NEXT: Scopes 4 0 +; THR-NEXT: Symbols 0 0 +; THR-NEXT: Types 0 0 +; THR-NEXT: Lines 0 0 +; THR-NEXT: ----------------------------- +; THR-NEXT: Total 4 0 +; THR-EMPTY: +; THR-NEXT: Logical View: +; THR-NEXT: [000] {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64 +; THR-EMPTY: +; THR-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; THR-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; THR-NEXT: [005] {Enumerator} 'RED' = '0x0' +; THR-EMPTY: +; THR-NEXT: ----------------------------- +; THR-NEXT: Element Total Printed +; THR-NEXT: ----------------------------- +; THR-NEXT: Scopes 5 0 +; THR-NEXT: Symbols 0 0 +; THR-NEXT: Types 2 2 +; THR-NEXT: Lines 0 0 +; THR-NEXT: ----------------------------- +; THR-NEXT: Total 7 2 Index: llvm/test/tools/llvm-debuginfo-analyzer/DWARF/05-dwarf-incorrect-lexical-scope-variable.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/DWARF/05-dwarf-incorrect-lexical-scope-variable.test @@ -0,0 +1,108 @@ +; Test case 5 - Incorrect lexical scope variable. + +; pr-43860.cpp +; 1 #include "definitions.h" +; 2 forceinline int InlineFunction(int Param) { +; 3 int Var_1 = Param; +; 4 { +; 5 int Var_2 = Param + Var_1; +; 6 Var_1 = Var_2; +; 7 } +; 8 return Var_1; +; 9 } +; 10 +; 11 int test(int Param_1, int Param_2) { +; 12 int A = Param_1; +; 13 A += InlineFunction(Param_2); +; 14 return A; +; 15 } + +; The above test is used to illustrate a variable issue found in the +; Clang compiler. +; PR43860: https://bugs.llvm.org/show_bug.cgi?id=43860 +; PR43205: https://github.com/llvm/llvm-project/issues/43205 + +; In the following logical views, we can see that the CodeView debug +; information generated by the Clang compiler shows the variables +; 'Var_1' and 'Var_2' are at the same lexical scope (4) in the function +; 'InlineFuction'. +; The DWARF generated by GCC/Clang and CodeView generated by MSVC, show +; those variables at the correct lexical scope: '3' and '4' respectively. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --output-sort=name \ +; RUN: --print=symbols \ +; RUN: %p/Inputs/pr-43860-dwarf-clang.o \ +; RUN: %p/Inputs/pr-43860-dwarf-gcc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] 2 {Function} extern not_inlined 'InlineFunction' -> 'int' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] 5 {Variable} 'Var_2' -> 'int' +; ONE-NEXT: [003] 2 {Parameter} 'Param' -> 'int' +; ONE-NEXT: [003] 3 {Variable} 'Var_1' -> 'int' +; ONE-NEXT: [002] 11 {Function} extern not_inlined 'test' -> 'int' +; ONE-NEXT: [003] 12 {Variable} 'A' -> 'int' +; ONE-NEXT: [003] 14 {InlinedFunction} not_inlined 'InlineFunction' -> 'int' +; ONE-NEXT: [004] {Block} +; ONE-NEXT: [005] {Variable} 'Var_2' -> 'int' +; ONE-NEXT: [004] {Parameter} 'Param' -> 'int' +; ONE-NEXT: [004] {Variable} 'Var_1' -> 'int' +; ONE-NEXT: [003] 11 {Parameter} 'Param_1' -> 'int' +; ONE-NEXT: [003] 11 {Parameter} 'Param_2' -> 'int' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; ONE-NEXT: [002] {Producer} 'GNU C++14 10.3.0 {{.*}}' +; ONE-NEXT: [002] 2 {Function} extern declared_inlined 'InlineFunction' -> 'int' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] 5 {Variable} 'Var_2' -> 'int' +; ONE-NEXT: [003] 2 {Parameter} 'Param' -> 'int' +; ONE-NEXT: [003] 3 {Variable} 'Var_1' -> 'int' +; ONE-NEXT: [002] 11 {Function} extern not_inlined 'test' -> 'int' +; ONE-NEXT: [003] 12 {Variable} 'A' -> 'int' +; ONE-NEXT: [003] 13 {InlinedFunction} declared_inlined 'InlineFunction' -> 'int' +; ONE-NEXT: [004] {Block} +; ONE-NEXT: [005] {Variable} 'Var_2' -> 'int' +; ONE-NEXT: [004] {Parameter} 'Param' -> 'int' +; ONE-NEXT: [004] {Variable} 'Var_1' -> 'int' +; ONE-NEXT: [003] 11 {Parameter} 'Param_1' -> 'int' +; ONE-NEXT: [003] 11 {Parameter} 'Param_2' -> 'int' + +; Using the selection facilities, we can produce a simple tabular output +; showing just the logical elements that have in their name the 'var' +; pattern. The logical view is sorted by the variables name. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-regex --select-nocase \ +; RUN: --select=Var \ +; RUN: --report=list \ +; RUN: --print=symbols \ +; RUN: %p/Inputs/pr-43860-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; TWO-NEXT: [004] {Variable} 'Var_1' -> 'int' +; TWO-NEXT: [003] 3 {Variable} 'Var_1' -> 'int' +; TWO-NEXT: [005] {Variable} 'Var_2' -> 'int' +; TWO-NEXT: [004] 5 {Variable} 'Var_2' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; TWO-NEXT: [004] {Variable} 'Var_1' -> 'int' +; TWO-NEXT: [003] 3 {Variable} 'Var_1' -> 'int' +; TWO-NEXT: [005] {Variable} 'Var_2' -> 'int' +; TWO-NEXT: [004] 5 {Variable} 'Var_2' -> 'int' Index: llvm/test/tools/llvm-debuginfo-analyzer/DWARF/06-dwarf-full-logical-view.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/DWARF/06-dwarf-full-logical-view.test @@ -0,0 +1,105 @@ +; Test case 6 - Full logical view + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Print low level details. +; The following command prints low level information that includes +; offsets within the debug information section, debug location +; operands, linkage names, etc. + +; RUN: llvm-debuginfo-analyzer --attribute=all \ +; RUN: --print=all \ +; RUN: %p/Inputs/test-dwarf-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [0x0000000000][000] {File} '{{.*}}test-dwarf-clang.o' -> elf64-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [0x000000000b][001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [0x000000000b][002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: {Directory} '/data/projects/tests/input/general' +; ONE-NEXT: {File} 'test.cpp' +; ONE-NEXT: {Public} 'foo' [0x0000000000:0x000000003a] +; ONE-NEXT: [0x000000000b][002] {Range} Lines 2:9 [0x0000000000:0x000000003a] +; ONE-NEXT: [0x00000000bc][002] {BaseType} 'bool' +; ONE-NEXT: [0x0000000099][002] {BaseType} 'int' +; ONE-NEXT: [0x00000000b5][002] {BaseType} 'unsigned int' +; ONE-EMPTY: +; ONE-NEXT: [0x00000000a0][002] {Source} '/data/projects/tests/input/general/test.cpp' +; ONE-NEXT: [0x00000000a0][002] 1 {TypeAlias} 'INTPTR' -> [0x00000000ab]'* const int' +; ONE-NEXT: [0x000000002a][002] 2 {Function} extern not_inlined 'foo' -> [0x0000000099]'int' +; ONE-NEXT: [0x000000002a][003] {Range} Lines 2:9 [0x0000000000:0x000000003a] +; ONE-NEXT: [0x000000002a][003] {Linkage} 0x2 '_Z3fooPKijb' +; ONE-NEXT: [0x0000000071][003] {Block} +; ONE-NEXT: [0x0000000071][004] {Range} Lines 5:8 [0x000000001c:0x000000002f] +; ONE-NEXT: [0x000000007e][004] 5 {Variable} 'CONSTANT' -> [0x00000000c3]'const INTEGER' +; ONE-NEXT: [0x000000007e][005] {Coverage} 100.00% +; ONE-NEXT: [0x000000007f][005] {Location} +; ONE-NEXT: [0x000000007f][006] {Entry} fbreg -28 +; ONE-NEXT: [0x000000001c][004] 5 {Line} {NewStatement} '/data/projects/tests/input/general/test.cpp' +; ONE-NEXT: [0x000000001c][004] {Code} 'movl $0x7, -0x1c(%rbp)' +; ONE-NEXT: [0x0000000023][004] 6 {Line} {NewStatement} '/data/projects/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000023][004] {Code} 'movl $0x7, -0x4(%rbp)' +; ONE-NEXT: [0x000000002a][004] {Code} 'jmp 0x6' +; ONE-NEXT: [0x000000002f][004] 8 {Line} {NewStatement} '/data/projects/tests/input/general/test.cpp' +; ONE-NEXT: [0x000000002f][004] {Code} 'movl -0x14(%rbp), %eax' +; ONE-NEXT: [0x0000000063][003] 2 {Parameter} 'ParamBool' -> [0x00000000bc]'bool' +; ONE-NEXT: [0x0000000063][004] {Coverage} 100.00% +; ONE-NEXT: [0x0000000064][004] {Location} +; ONE-NEXT: [0x0000000064][005] {Entry} fbreg -21 +; ONE-NEXT: [0x0000000047][003] 2 {Parameter} 'ParamPtr' -> [0x00000000a0]'INTPTR' +; ONE-NEXT: [0x0000000047][004] {Coverage} 100.00% +; ONE-NEXT: [0x0000000048][004] {Location} +; ONE-NEXT: [0x0000000048][005] {Entry} fbreg -16 +; ONE-NEXT: [0x0000000055][003] 2 {Parameter} 'ParamUnsigned' -> [0x00000000b5]'unsigned int' +; ONE-NEXT: [0x0000000055][004] {Coverage} 100.00% +; ONE-NEXT: [0x0000000056][004] {Location} +; ONE-NEXT: [0x0000000056][005] {Entry} fbreg -20 +; ONE-NEXT: [0x000000008d][003] 4 {TypeAlias} 'INTEGER' -> [0x0000000099]'int' +; ONE-NEXT: [0x0000000000][003] 2 {Line} {NewStatement} '/data/projects/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000000][003] {Code} 'pushq %rbp' +; ONE-NEXT: [0x0000000001][003] {Code} 'movq %rsp, %rbp' +; ONE-NEXT: [0x0000000004][003] {Code} 'movb %dl, %al' +; ONE-NEXT: [0x0000000006][003] {Code} 'movq %rdi, -0x10(%rbp)' +; ONE-NEXT: [0x000000000a][003] {Code} 'movl %esi, -0x14(%rbp)' +; ONE-NEXT: [0x000000000d][003] {Code} 'andb $0x1, %al' +; ONE-NEXT: [0x000000000f][003] {Code} 'movb %al, -0x15(%rbp)' +; ONE-NEXT: [0x0000000012][003] 3 {Line} {NewStatement} {PrologueEnd} '/data/projects/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000012][003] {Code} 'testb $0x1, -0x15(%rbp)' +; ONE-NEXT: [0x0000000016][003] {Code} 'je 0x13' +; ONE-NEXT: [0x0000000032][003] 8 {Line} '/data/projects/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000032][003] {Code} 'movl %eax, -0x4(%rbp)' +; ONE-NEXT: [0x0000000035][003] 9 {Line} {NewStatement} '/data/projects/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000035][003] {Code} 'movl -0x4(%rbp), %eax' +; ONE-NEXT: [0x0000000038][003] {Code} 'popq %rbp' +; ONE-NEXT: [0x0000000039][003] {Code} 'retq' +; ONE-NEXT: [0x000000003a][003] 9 {Line} {NewStatement} {EndSequence} '/data/projects/tests/input/general/test.cpp' +; ONE-EMPTY: +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Element Total Printed +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Scopes 3 3 +; ONE-NEXT: Symbols 4 4 +; ONE-NEXT: Types 5 5 +; ONE-NEXT: Lines 25 25 +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Total 37 37 +; ONE-EMPTY: +; ONE-NEXT: Scope Sizes: +; ONE-NEXT: 189 (100.00%) : [0x000000000b][001] {CompileUnit} 'test.cpp' +; ONE-NEXT: 110 ( 58.20%) : [0x000000002a][002] 2 {Function} extern not_inlined 'foo' -> [0x0000000099]'int' +; ONE-NEXT: 27 ( 14.29%) : [0x0000000071][003] {Block} +; ONE-EMPTY: +; ONE-NEXT: Totals by lexical level: +; ONE-NEXT: [001]: 189 (100.00%) +; ONE-NEXT: [002]: 110 ( 58.20%) +; ONE-NEXT: [003]: 27 ( 14.29%) Index: llvm/test/tools/llvm-debuginfo-analyzer/cmdline.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/cmdline.test @@ -0,0 +1,221 @@ +RUN: llvm-debuginfo-analyzer --version 2>&1 | FileCheck --check-prefix=VERSION %s +VERSION: {{ version }} + +RUN: llvm-debuginfo-analyzer -h > %t 2>&1 +RUN: FileCheck -input-file=%t %s --check-prefix=HELP --implicit-check-not=out-file +RUN: llvm-debuginfo-analyzer --help > %t 2>&1 +RUN: FileCheck -input-file=%t %s --check-prefix=HELP --implicit-check-not=out-file +HELP: OVERVIEW: Printing a logical representation of low-level debug information. +HELP: USAGE: llvm-debuginfo-analyzer{{[^ ]*}} [options] +HELP: OPTIONS: +HELP: Generic Options: +HELP: --help - Display available options (--help-hidden for more) +HELP: --help-list - Display list of available options (--help-list-hidden for more) +HELP: --version - Display the version of this program +HELP: Print Options: +HELP: These control which elements are printed. +HELP: --print= - Element to print. +HELP: =all - All elements. +HELP: =elements - Instructions, lines, scopes, symbols and types. +HELP: =instructions - Assembler instructions. +HELP: =lines - Lines referenced in the debug information. +HELP: =scopes - A lexical block (Function, Class, etc.). +HELP: =sizes - Scope contributions to the debug information. +HELP: =summary - Summary of elements missing/added/matched/printed. +HELP: =symbols - Symbols (Variable, Members, etc.). +HELP: =types - Types (Pointer, Reference, etc.). +HELP: =warnings - Warnings detected. +HELP: Pass @FILE as argument to read options from FILE. + +RUN: llvm-debuginfo-analyzer --help-hidden > %t 2>&1 +RUN: FileCheck -input-file=%t %s --check-prefix=HELP-ALL --implicit-check-not=out-file +HELP-ALL: OVERVIEW: Printing a logical representation of low-level debug information. +HELP-ALL: USAGE: llvm-debuginfo-analyzer{{[^ ]*}} [options] +HELP-ALL: OPTIONS: +HELP-ALL: Attribute Options: +HELP-ALL: These control extra attributes that are added when the element is printed. +HELP-ALL: --attribute= - Element attributes. +HELP-ALL: =all - Include all attributes. +HELP-ALL: =argument - Template parameters replaced by its arguments. +HELP-ALL: =base - Base types (int, bool, etc.). +HELP-ALL: =coverage - Symbol location coverage. +HELP-ALL: =directories - Directories referenced in the debug information. +HELP-ALL: =discarded - Discarded elements by the linker. +HELP-ALL: =discriminator - Discriminators for inlined function instances. +HELP-ALL: =encoded - Template arguments encoded in the template name. +HELP-ALL: =extended - Advanced attributes alias. +HELP-ALL: =filename - Filename where the element is defined. +HELP-ALL: =files - Files referenced in the debug information. +HELP-ALL: =format - Object file format name. +HELP-ALL: =gaps - Missing debug location (gaps). +HELP-ALL: =generated - Compiler generated elements. +HELP-ALL: =global - Element referenced across Compile Units. +HELP-ALL: =inserted - Generated inlined abstract references. +HELP-ALL: =level - Lexical scope level (File=0, Compile Unit=1). +HELP-ALL: =linkage - Linkage name. +HELP-ALL: =local - Element referenced only in the Compile Unit. +HELP-ALL: =location - Element debug location. +HELP-ALL: =offset - Debug information offset. +HELP-ALL: =pathname - Pathname where the element is defined. +HELP-ALL: =producer - Toolchain identification name. +HELP-ALL: =publics - Function names that are public. +HELP-ALL: =qualified - The element type include parents in its name. +HELP-ALL: =qualifier - Line qualifiers (Newstatement, BasicBlock, etc.). +HELP-ALL: =range - Debug location ranges. +HELP-ALL: =reference - Element declaration and definition references. +HELP-ALL: =register - Processor register names. +HELP-ALL: =standard - Basic attributes alias. +HELP-ALL: =subrange - Subrange encoding information for arrays. +HELP-ALL: =system - Display PDB's MS system elements. +HELP-ALL: =typename - Include Parameters in templates. +HELP-ALL: =underlying - Underlying type for type definitions. +HELP-ALL: =zero - Zero line numbers. +HELP-ALL: Color Options: +HELP-ALL: This option category has no options. +HELP-ALL: Compare Options: +HELP-ALL: These control the view comparison. +HELP-ALL: --compare= - Elements to compare. +HELP-ALL: =all - Compare all elements. +HELP-ALL: =lines - Lines. +HELP-ALL: =scopes - Scopes. +HELP-ALL: =symbols - Symbols. +HELP-ALL: =types - Types. +HELP-ALL: --compare-context - Add the view as compare context. +HELP-ALL: General options: +HELP-ALL: This option category has no options. +HELP-ALL: Generic Options: +HELP-ALL: -h - Alias for --help +HELP-ALL: --help - Display available options (--help-hidden for more) +HELP-ALL: --help-hidden - Display all available options +HELP-ALL: --help-list - Display list of available options (--help-list-hidden for more) +HELP-ALL: --help-list-hidden - Display list of all available options +HELP-ALL: --print-all-options - Print all option values after command line parsing +HELP-ALL: --print-options - Print non-default options after command line parsing +HELP-ALL: --version - Display the version of this program +HELP-ALL: Internal Options: +HELP-ALL: Internal traces and extra debugging code. +HELP-ALL: --internal= - Traces to enable. +HELP-ALL: =all - Enable all traces. +HELP-ALL: =cmdline - Print command line. +HELP-ALL: =id - Print unique element ID +HELP-ALL: =integrity - Check elements integrity. +HELP-ALL: =none - Ignore element line number. +HELP-ALL: =tag - Debug information tags. +HELP-ALL: Output Options: +HELP-ALL: These control the output generated. +HELP-ALL: --output= - Outputs for view. +HELP-ALL: =all - All outputs. +HELP-ALL: =split - Split the output by Compile Units. +HELP-ALL: =text - Use a free form text output. +HELP-ALL: =json - Use JSON as the output format. +HELP-ALL: --output-file= - Redirect output to the specified file. +HELP-ALL: --output-folder= - Folder name for view splitting. +HELP-ALL: --output-level= - Only print to a depth of N elements. +HELP-ALL: --output-sort= - Primary key when ordering logical view (default: line). +HELP-ALL: =kind - Sort by element kind. +HELP-ALL: =line - Sort by element line number. +HELP-ALL: =name - Sort by element name. +HELP-ALL: =offset - Sort by element offset. +HELP-ALL: Print Options: +HELP-ALL: These control which elements are printed. +HELP-ALL: --print= - Element to print. +HELP-ALL: =all - All elements. +HELP-ALL: =elements - Instructions, lines, scopes, symbols and types. +HELP-ALL: =instructions - Assembler instructions. +HELP-ALL: =lines - Lines referenced in the debug information. +HELP-ALL: =scopes - A lexical block (Function, Class, etc.). +HELP-ALL: =sizes - Scope contributions to the debug information. +HELP-ALL: =summary - Summary of elements missing/added/matched/printed. +HELP-ALL: =symbols - Symbols (Variable, Members, etc.). +HELP-ALL: =types - Types (Pointer, Reference, etc.). +HELP-ALL: =warnings - Warnings detected. +HELP-ALL: Report Options: +HELP-ALL: These control how the elements are printed. +HELP-ALL: --report= - Reports layout used for print, compare and select. +HELP-ALL: =all - Generate all reports. +HELP-ALL: =children - Selected elements are displayed in a tree view (Include children) +HELP-ALL: =list - Selected elements are displayed in a tabular format. +HELP-ALL: =parents - Selected elements are displayed in a tree view. (Include parents) +HELP-ALL: =view - Selected elements are displayed in a tree view (Include parents and children. +HELP-ALL: Select Options: +HELP-ALL: These control which elements are selected. +HELP-ALL: --select= - Search elements matching the given pattern. +HELP-ALL: --select-elements= - Conditions to use when printing elements. +HELP-ALL: =Discarded - Discarded elements by the linker. +HELP-ALL: =Global - Element referenced across Compile Units. +HELP-ALL: =Optimized - Generated inlined abstract references. +HELP-ALL: --select-lines= - Line kind to use when printing lines. +HELP-ALL: =AlwaysStepInto - Always Step Into. +HELP-ALL: =BasicBlock - Basic block. +HELP-ALL: =Discriminator - Discriminator. +HELP-ALL: =EndSequence - End sequence. +HELP-ALL: =EpilogueBegin. - Epilogue begin. +HELP-ALL: =LineDebug - Debug line. +HELP-ALL: =LineAssembler - Assembler line. +HELP-ALL: =NeverStepInto - Never Step Into. +HELP-ALL: =NewStatement - New statement. +HELP-ALL: =PrologueEnd - Prologue end. +HELP-ALL: --select-nocase - Ignore case distinctions when searching. +HELP-ALL: --select-offsets= - Offset element to print. +HELP-ALL: --select-regex - Treat any strings as regular expressions when selecting instead of just as an exact string match. +HELP-ALL: --select-scopes= - Scope kind to use when printing scopes. +HELP-ALL: =Aggregate - Class, Structure or Union. +HELP-ALL: =Array - Array. +HELP-ALL: =Block - Lexical block. +HELP-ALL: =CallSite - Call site block. +HELP-ALL: =CatchBlock - Exception catch block. +HELP-ALL: =Class - Class. +HELP-ALL: =CompileUnit - Compile unit. +HELP-ALL: =EntryPoint - Function entry point. +HELP-ALL: =Enumeration - Enumeration. +HELP-ALL: =Function - Function. +HELP-ALL: =FunctionType - Function type. +HELP-ALL: =InlinedFunction - Inlined function. +HELP-ALL: =Label - Label. +HELP-ALL: =LexicalBlock - Lexical block. +HELP-ALL: =Namespace - Namespace. +HELP-ALL: =Root - Root. +HELP-ALL: =Structure - Structure. +HELP-ALL: =Subprogram - Subprogram. +HELP-ALL: =Template - Template. +HELP-ALL: =TemplateAlias - Template alias. +HELP-ALL: =TemplatePack - Template pack. +HELP-ALL: =TryBlock - Exception try block. +HELP-ALL: =Union - Union. +HELP-ALL: --select-symbols= - Symbol kind to use when printing symbols. +HELP-ALL: =CallSiteParameter - Call site parameter. +HELP-ALL: =Constant - Constant. +HELP-ALL: =Inheritance - Inheritance. +HELP-ALL: =Member - Member. +HELP-ALL: =Parameter - Parameter. +HELP-ALL: =Unspecified - Unspecified parameter. +HELP-ALL: =Variable - Variable. +HELP-ALL: --select-types= - Type kind to use when printing types. +HELP-ALL: =Base - Base Type (int, bool, etc.). +HELP-ALL: =Const - Constant specifier. +HELP-ALL: =Enumerator - Enumerator. +HELP-ALL: =Import - Import. +HELP-ALL: =ImportDeclaration - Import declaration. +HELP-ALL: =ImportModule - Import module. +HELP-ALL: =Pointer - Pointer. +HELP-ALL: =PointerMember - Pointer to member. +HELP-ALL: =Reference - Reference type. +HELP-ALL: =Restrict - Restrict specifier. +HELP-ALL: =RvalueReference - Rvalue reference. +HELP-ALL: =Subrange - Array subrange. +HELP-ALL: =TemplateParam - Template Parameter. +HELP-ALL: =TemplateTemplateParam - Template template parameter. +HELP-ALL: =TemplateTypeParam - Template type parameter. +HELP-ALL: =TemplateValueParam - Template value parameter. +HELP-ALL: =Typedef - Type definition. +HELP-ALL: =Unspecified - Unspecified type. +HELP-ALL: =Volatile - Volatile specifier. +HELP-ALL: Warning Options: +HELP-ALL: These control the generated warnings. +HELP-ALL: --warning= - Warnings to generate. +HELP-ALL: =all - All warnings. +HELP-ALL: =coverages - Invalid symbol coverages values. +HELP-ALL: =lines - Debug lines that are zero. +HELP-ALL: =locations - Invalid symbol locations. +HELP-ALL: =ranges - Invalid code ranges. +HELP-ALL: Pass @FILE as argument to read options from FILE. Index: llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-debuginfo-analyzer/CMakeLists.txt @@ -0,0 +1,19 @@ +set(LLVM_LINK_COMPONENTS + AllTargetsDescs + AllTargetsInfos + AllTargetsDisassemblers + BinaryFormat + DebugInfoLogicalView + DebugInfoCodeView + DebugInfoDWARF + DebugInfoPDB + MC + MCDisassembler + Object + Support + ) + +add_llvm_tool(llvm-debuginfo-analyzer + llvm-debuginfo-analyzer.cpp + Options.cpp + ) Index: llvm/tools/llvm-debuginfo-analyzer/LLVMBuild.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-debuginfo-analyzer/LLVMBuild.txt @@ -0,0 +1,21 @@ +;===- ./tools/llvm-debuginfo-analyzer/LLVMBuild.txt ------------*- Conf -*--===; +; +; 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 +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-debuginfo-analyzer +parent = Tools +required_libraries = DebugInfoLogicalView DebugInfoDWARF DebugInfoCodeView DebugInfoPDB Object Index: llvm/tools/llvm-debuginfo-analyzer/Options.h =================================================================== --- /dev/null +++ llvm/tools/llvm-debuginfo-analyzer/Options.h @@ -0,0 +1,81 @@ +//===-- Options.h -----------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines command line options used by llvm-debuginfo-analyzer. +// +//===----------------------------------------------------------------------===// + +#ifndef OPTIONS_H +#define OPTIONS_H + +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/Support/CommandLine.h" + +namespace llvm { +namespace logicalview { +namespace cmdline { + +class OffsetParser final : public llvm::cl::parser { +public: + OffsetParser(llvm::cl::Option &O); + ~OffsetParser() override; + + // Parse an argument representing an offset. Return true on error. + // If the prefix is 0, the base is octal, if the prefix is 0x or 0X, the + // base is hexadecimal, otherwise the base is decimal. + bool parse(llvm::cl::Option &O, StringRef ArgName, StringRef ArgValue, + unsigned long long &Val); +}; + +typedef llvm::cl::list OffsetOptionList; + +extern llvm::cl::OptionCategory AttributeCategory; +extern llvm::cl::OptionCategory CompareCategory; +extern llvm::cl::OptionCategory OutputCategory; +extern llvm::cl::OptionCategory PrintCategory; +extern llvm::cl::OptionCategory ReportCategory; +extern llvm::cl::OptionCategory SelectCategory; +extern llvm::cl::OptionCategory WarningCategory; +extern llvm::cl::OptionCategory InternalCategory; + +extern llvm::cl::list InputFilenames; +extern llvm::cl::opt OutputFilename; + +extern llvm::cl::list SelectPatterns; + +extern llvm::cl::list SelectElements; +extern llvm::cl::list SelectLines; +extern llvm::cl::list SelectScopes; +extern llvm::cl::list SelectSymbols; +extern llvm::cl::list SelectTypes; +extern OffsetOptionList SelectOffsets; + +extern llvm::cl::list AttributeOptions; +extern llvm::cl::list OutputOptions; +extern llvm::cl::list PrintOptions; +extern llvm::cl::list WarningOptions; +extern llvm::cl::list InternalOptions; + +extern llvm::cl::list CompareElements; +extern llvm::cl::list ReportOptions; + +extern LVOptions ReaderOptions; + +// Perform any additional post parse command line actions. Propagate the +// values captured by the command line parser, into the generic reader. +void propagateOptions(); + +} // namespace cmdline +} // namespace logicalview +} // namespace llvm + +#endif // OPTIONS_H Index: llvm/tools/llvm-debuginfo-analyzer/Options.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-debuginfo-analyzer/Options.cpp @@ -0,0 +1,501 @@ +//===-- options.cpp - Command line options for llvm-debuginfo-analyzer----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This handles the command line options for llvm-debuginfo-analyzer. +// +//===----------------------------------------------------------------------===// + +#include "Options.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSort.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; +using namespace llvm::logicalview; +using namespace llvm::logicalview::cmdline; + +/// @} +/// Command line options. +/// @{ + +OffsetParser::OffsetParser(cl::Option &O) : parser(O) {} +OffsetParser::~OffsetParser() = default; + +bool OffsetParser::parse(cl::Option &O, StringRef ArgName, StringRef Arg, + unsigned long long &Val) { + char *End; + std::string Argument(Arg); + Val = strtoull(Argument.c_str(), &End, 0); + if (*End) + // Print an error message if unrecognized character. + return O.error("'" + Arg + "' unrecognized character."); + return false; +} + +LVOptions cmdline::ReaderOptions; + +//===----------------------------------------------------------------------===// +// Specific options +//===----------------------------------------------------------------------===// +cl::list + cmdline::InputFilenames(cl::desc(""), + cl::Positional, cl::ZeroOrMore); + +//===----------------------------------------------------------------------===// +// '--attribute' options +//===----------------------------------------------------------------------===// +cl::OptionCategory + cmdline::AttributeCategory("Attribute Options", + "These control extra attributes that are " + "added when the element is printed."); + +// --attribute=[,,...] +cl::list cmdline::AttributeOptions( + "attribute", cl::cat(AttributeCategory), cl::desc("Element attributes."), + cl::Hidden, cl::CommaSeparated, + values(clEnumValN(LVAttributeKind::All, "all", "Include all attributes."), + clEnumValN(LVAttributeKind::Argument, "argument", + "Template parameters replaced by its arguments."), + clEnumValN(LVAttributeKind::Base, "base", + "Base types (int, bool, etc.)."), + clEnumValN(LVAttributeKind::Coverage, "coverage", + "Symbol location coverage."), + clEnumValN(LVAttributeKind::Directories, "directories", + "Directories referenced in the debug information."), + clEnumValN(LVAttributeKind::Discarded, "discarded", + "Discarded elements by the linker."), + clEnumValN(LVAttributeKind::Discriminator, "discriminator", + "Discriminators for inlined function instances."), + clEnumValN(LVAttributeKind::Encoded, "encoded", + "Template arguments encoded in the template name."), + clEnumValN(LVAttributeKind::Extended, "extended", + "Advanced attributes alias."), + clEnumValN(LVAttributeKind::Filename, "filename", + "Filename where the element is defined."), + clEnumValN(LVAttributeKind::Files, "files", + "Files referenced in the debug information."), + clEnumValN(LVAttributeKind::Format, "format", + "Object file format name."), + clEnumValN(LVAttributeKind::Gaps, "gaps", + "Missing debug location (gaps)."), + clEnumValN(LVAttributeKind::Generated, "generated", + "Compiler generated elements."), + clEnumValN(LVAttributeKind::Global, "global", + "Element referenced across Compile Units."), + clEnumValN(LVAttributeKind::Inserted, "inserted", + "Generated inlined abstract references."), + clEnumValN(LVAttributeKind::Level, "level", + "Lexical scope level (File=0, Compile Unit=1)."), + clEnumValN(LVAttributeKind::Linkage, "linkage", "Linkage name."), + clEnumValN(LVAttributeKind::Local, "local", + "Element referenced only in the Compile Unit."), + clEnumValN(LVAttributeKind::Location, "location", + "Element debug location."), + clEnumValN(LVAttributeKind::Offset, "offset", + "Debug information offset."), + clEnumValN(LVAttributeKind::Pathname, "pathname", + "Pathname where the element is defined."), + clEnumValN(LVAttributeKind::Producer, "producer", + "Toolchain identification name."), + clEnumValN(LVAttributeKind::Publics, "publics", + "Function names that are public."), + clEnumValN(LVAttributeKind::Qualified, "qualified", + "The element type include parents in its name."), + clEnumValN(LVAttributeKind::Qualifier, "qualifier", + "Line qualifiers (Newstatement, BasicBlock, etc.)."), + clEnumValN(LVAttributeKind::Range, "range", + "Debug location ranges."), + clEnumValN(LVAttributeKind::Reference, "reference", + "Element declaration and definition references."), + clEnumValN(LVAttributeKind::Register, "register", + "Processor register names."), + clEnumValN(LVAttributeKind::Standard, "standard", + "Basic attributes alias."), + clEnumValN(LVAttributeKind::Subrange, "subrange", + "Subrange encoding information for arrays."), + clEnumValN(LVAttributeKind::System, "system", + "Display PDB's MS system elements."), + clEnumValN(LVAttributeKind::Typename, "typename", + "Include Parameters in templates."), + clEnumValN(LVAttributeKind::Underlying, "underlying", + "Underlying type for type definitions."), + clEnumValN(LVAttributeKind::Zero, "zero", "Zero line numbers."))); + +//===----------------------------------------------------------------------===// +// '--compare' options +//===----------------------------------------------------------------------===// +cl::OptionCategory + cmdline::CompareCategory("Compare Options", + "These control the view comparison."); + +// --compare-context +static cl::opt + CompareContext("compare-context", cl::cat(CompareCategory), + cl::desc("Add the view as compare context."), cl::Hidden, + cl::ZeroOrMore, cl::location(ReaderOptions.Compare.Context), + cl::init(false)); + +// --compare=[,,...] +cl::list cmdline::CompareElements( + "compare", cl::cat(CompareCategory), cl::desc("Elements to compare."), + cl::Hidden, cl::CommaSeparated, + values(clEnumValN(LVCompareKind::All, "all", "Compare all elements."), + clEnumValN(LVCompareKind::Lines, "lines", "Lines."), + clEnumValN(LVCompareKind::Scopes, "scopes", "Scopes."), + clEnumValN(LVCompareKind::Symbols, "symbols", "Symbols."), + clEnumValN(LVCompareKind::Types, "types", "Types."))); + +//===----------------------------------------------------------------------===// +// '--output' options +//===----------------------------------------------------------------------===// +cl::OptionCategory + cmdline::OutputCategory("Output Options", + "These control the output generated."); + +// --output-file= +cl::opt + cmdline::OutputFilename("output-file", cl::cat(OutputCategory), + cl::desc("Redirect output to the specified file."), + cl::Hidden, cl::value_desc("filename"), + cl::init("-")); + +// --output-folder= +static cl::opt + OutputFolder("output-folder", cl::cat(OutputCategory), + cl::desc("Folder name for view splitting."), + cl::value_desc("pathname"), cl::Hidden, cl::ZeroOrMore, + cl::location(ReaderOptions.Output.Folder)); + +// --output-level= +static cl::opt + OutputLevel("output-level", cl::cat(OutputCategory), + cl::desc("Only print to a depth of N elements."), + cl::value_desc("N"), cl::Hidden, cl::ZeroOrMore, + cl::location(ReaderOptions.Output.Level), cl::init(-1U)); + +// --ouput=[,,...] +cl::list cmdline::OutputOptions( + "output", cl::cat(OutputCategory), cl::desc("Outputs for view."), + cl::Hidden, cl::CommaSeparated, + values(clEnumValN(LVOutputKind::All, "all", "All outputs."), + clEnumValN(LVOutputKind::Split, "split", + "Split the output by Compile Units."), + clEnumValN(LVOutputKind::Text, "text", + "Use a free form text output."), + clEnumValN(LVOutputKind::Json, "json", + "Use JSON as the output format."))); + +// --output-sort +static cl::opt OutputSort( + "output-sort", cl::cat(OutputCategory), + cl::desc("Primary key when ordering logical view (default: line)."), + cl::Hidden, cl::ZeroOrMore, + values(clEnumValN(LVSortMode::Kind, "kind", "Sort by element kind."), + clEnumValN(LVSortMode::Line, "line", "Sort by element line number."), + clEnumValN(LVSortMode::Name, "name", "Sort by element name."), + clEnumValN(LVSortMode::Offset, "offset", "Sort by element offset.")), + cl::location(ReaderOptions.Output.SortMode), cl::init(LVSortMode::Line)); + +//===----------------------------------------------------------------------===// +// '--print' options +//===----------------------------------------------------------------------===// +cl::OptionCategory + cmdline::PrintCategory("Print Options", + "These control which elements are printed."); + +// --print=[,,...] +cl::list cmdline::PrintOptions( + "print", cl::cat(PrintCategory), cl::desc("Element to print."), + cl::CommaSeparated, + values(clEnumValN(LVPrintKind::All, "all", "All elements."), + clEnumValN(LVPrintKind::Elements, "elements", + "Instructions, lines, scopes, symbols and types."), + clEnumValN(LVPrintKind::Instructions, "instructions", + "Assembler instructions."), + clEnumValN(LVPrintKind::Lines, "lines", + "Lines referenced in the debug information."), + clEnumValN(LVPrintKind::Scopes, "scopes", + "A lexical block (Function, Class, etc.)."), + clEnumValN(LVPrintKind::Sizes, "sizes", + "Scope contributions to the debug information."), + clEnumValN(LVPrintKind::Summary, "summary", + "Summary of elements missing/added/matched/printed."), + clEnumValN(LVPrintKind::Symbols, "symbols", + "Symbols (Variable, Members, etc.)."), + clEnumValN(LVPrintKind::Types, "types", + "Types (Pointer, Reference, etc.)."), + clEnumValN(LVPrintKind::Warnings, "warnings", + "Warnings detected."))); + +//===----------------------------------------------------------------------===// +// '--report' options +//===----------------------------------------------------------------------===// +cl::OptionCategory + cmdline::ReportCategory("Report Options", + "These control how the elements are printed."); + +// --report=[,,...] +cl::list cmdline::ReportOptions( + "report", cl::cat(ReportCategory), + cl::desc("Reports layout used for print, compare and select."), cl::Hidden, + cl::CommaSeparated, + values(clEnumValN(LVReportKind::All, "all", "Generate all reports."), + clEnumValN(LVReportKind::Children, "children", + "Selected elements are displayed in a tree view " + "(Include children)"), + clEnumValN(LVReportKind::List, "list", + "Selected elements are displayed in a tabular format."), + clEnumValN(LVReportKind::Parents, "parents", + "Selected elements are displayed in a tree view. " + "(Include parents)"), + clEnumValN(LVReportKind::View, "view", + "Selected elements are displayed in a tree view " + "(Include parents and children."))); + +//===----------------------------------------------------------------------===// +// '--select' options +//===----------------------------------------------------------------------===// +cl::OptionCategory + cmdline::SelectCategory("Select Options", + "These control which elements are selected."); + +// --select-nocase +static cl::opt + SelectIgnoreCase("select-nocase", cl::cat(SelectCategory), + cl::desc("Ignore case distinctions when searching."), + cl::Hidden, cl::ZeroOrMore, + cl::location(ReaderOptions.Select.IgnoreCase), + cl::init(false)); + +// --select-regex +static cl::opt SelectUseRegex( + "select-regex", cl::cat(SelectCategory), + cl::desc("Treat any strings as regular expressions when " + "selecting instead of just as an exact string match."), + cl::Hidden, cl::ZeroOrMore, cl::location(ReaderOptions.Select.UseRegex), + cl::init(false)); + +// --select= +cl::list cmdline::SelectPatterns( + "select", cl::cat(SelectCategory), + cl::desc("Search elements matching the given pattern."), cl::Hidden, + cl::value_desc("pattern"), cl::CommaSeparated); + +// --select-offsets=[,,...] +OffsetOptionList cmdline::SelectOffsets("select-offsets", + cl::cat(SelectCategory), + cl::desc("Offset element to print."), + cl::Hidden, cl::value_desc("offset"), + cl::CommaSeparated, cl::ZeroOrMore); + +// --select-elements=[,,...] +cl::list cmdline::SelectElements( + "select-elements", cl::cat(SelectCategory), + cl::desc("Conditions to use when printing elements."), cl::Hidden, + cl::CommaSeparated, + values(clEnumValN(LVElementKind::Discarded, "Discarded", + "Discarded elements by the linker."), + clEnumValN(LVElementKind::Global, "Global", + "Element referenced across Compile Units."), + clEnumValN(LVElementKind::Optimized, "Optimized", + "Generated inlined abstract references."))); + +// --select-lines=[,,...] +cl::list cmdline::SelectLines( + "select-lines", cl::cat(SelectCategory), + cl::desc("Line kind to use when printing lines."), cl::Hidden, + cl::CommaSeparated, + values( + clEnumValN(LVLineKind::IsAlwaysStepInto, "AlwaysStepInto", + "Always Step Into."), + clEnumValN(LVLineKind::IsBasicBlock, "BasicBlock", "Basic block."), + clEnumValN(LVLineKind::IsDiscriminator, "Discriminator", + "Discriminator."), + clEnumValN(LVLineKind::IsEndSequence, "EndSequence", "End sequence."), + clEnumValN(LVLineKind::IsEpilogueBegin, "EpilogueBegin.", + "Epilogue begin."), + clEnumValN(LVLineKind::IsLineDebug, "LineDebug", "Debug line."), + clEnumValN(LVLineKind::IsLineAssembler, "LineAssembler", + "Assembler line."), + clEnumValN(LVLineKind::IsNeverStepInto, "NeverStepInto", + "Never Step Into."), + clEnumValN(LVLineKind::IsNewStatement, "NewStatement", + "New statement."), + clEnumValN(LVLineKind::IsPrologueEnd, "PrologueEnd", "Prologue end."))); + +// --select-scopes=[,,...] +cl::list cmdline::SelectScopes( + "select-scopes", cl::cat(SelectCategory), + cl::desc("Scope kind to use when printing scopes."), cl::Hidden, + cl::CommaSeparated, + values( + clEnumValN(LVScopeKind::IsAggregate, "Aggregate", + "Class, Structure or Union."), + clEnumValN(LVScopeKind::IsArray, "Array", "Array."), + clEnumValN(LVScopeKind::IsBlock, "Block", "Lexical block."), + clEnumValN(LVScopeKind::IsCallSite, "CallSite", "Call site block."), + clEnumValN(LVScopeKind::IsCatchBlock, "CatchBlock", + "Exception catch block."), + clEnumValN(LVScopeKind::IsClass, "Class", "Class."), + clEnumValN(LVScopeKind::IsCompileUnit, "CompileUnit", "Compile unit."), + clEnumValN(LVScopeKind::IsEntryPoint, "EntryPoint", + "Function entry point."), + clEnumValN(LVScopeKind::IsEnumeration, "Enumeration", "Enumeration."), + clEnumValN(LVScopeKind::IsFunction, "Function", "Function."), + clEnumValN(LVScopeKind::IsFunctionType, "FunctionType", + "Function type."), + clEnumValN(LVScopeKind::IsInlinedFunction, "InlinedFunction", + "Inlined function."), + clEnumValN(LVScopeKind::IsLabel, "Label", "Label."), + clEnumValN(LVScopeKind::IsLexicalBlock, "LexicalBlock", + "Lexical block."), + clEnumValN(LVScopeKind::IsNamespace, "Namespace", "Namespace."), + clEnumValN(LVScopeKind::IsRoot, "Root", "Root."), + clEnumValN(LVScopeKind::IsStructure, "Structure", "Structure."), + clEnumValN(LVScopeKind::IsSubprogram, "Subprogram", "Subprogram."), + clEnumValN(LVScopeKind::IsTemplate, "Template", "Template."), + clEnumValN(LVScopeKind::IsTemplateAlias, "TemplateAlias", + "Template alias."), + clEnumValN(LVScopeKind::IsTemplatePack, "TemplatePack", + "Template pack."), + clEnumValN(LVScopeKind::IsTryBlock, "TryBlock", "Exception try block."), + clEnumValN(LVScopeKind::IsUnion, "Union", "Union."))); + +// --select-symbols=[,,...] +cl::list cmdline::SelectSymbols( + "select-symbols", cl::cat(SelectCategory), + cl::desc("Symbol kind to use when printing symbols."), cl::Hidden, + cl::CommaSeparated, + values(clEnumValN(LVSymbolKind::IsCallSiteParameter, "CallSiteParameter", + "Call site parameter."), + clEnumValN(LVSymbolKind::IsConstant, "Constant", "Constant."), + clEnumValN(LVSymbolKind::IsInheritance, "Inheritance", + "Inheritance."), + clEnumValN(LVSymbolKind::IsMember, "Member", "Member."), + clEnumValN(LVSymbolKind::IsParameter, "Parameter", "Parameter."), + clEnumValN(LVSymbolKind::IsUnspecified, "Unspecified", + "Unspecified parameter."), + clEnumValN(LVSymbolKind::IsVariable, "Variable", "Variable."))); + +// --select-types=[,,...] +cl::list cmdline::SelectTypes( + "select-types", cl::cat(SelectCategory), + cl::desc("Type kind to use when printing types."), cl::Hidden, + cl::CommaSeparated, + values( + clEnumValN(LVTypeKind::IsBase, "Base", "Base Type (int, bool, etc.)."), + clEnumValN(LVTypeKind::IsConst, "Const", "Constant specifier."), + clEnumValN(LVTypeKind::IsEnumerator, "Enumerator", "Enumerator."), + clEnumValN(LVTypeKind::IsImport, "Import", "Import."), + clEnumValN(LVTypeKind::IsImportDeclaration, "ImportDeclaration", + "Import declaration."), + clEnumValN(LVTypeKind::IsImportModule, "ImportModule", + "Import module."), + clEnumValN(LVTypeKind::IsPointer, "Pointer", "Pointer."), + clEnumValN(LVTypeKind::IsPointerMember, "PointerMember", + "Pointer to member."), + clEnumValN(LVTypeKind::IsReference, "Reference", "Reference type."), + clEnumValN(LVTypeKind::IsRestrict, "Restrict", "Restrict specifier."), + clEnumValN(LVTypeKind::IsRvalueReference, "RvalueReference", + "Rvalue reference."), + clEnumValN(LVTypeKind::IsSubrange, "Subrange", "Array subrange."), + clEnumValN(LVTypeKind::IsTemplateParam, "TemplateParam", + "Template Parameter."), + clEnumValN(LVTypeKind::IsTemplateTemplateParam, "TemplateTemplateParam", + "Template template parameter."), + clEnumValN(LVTypeKind::IsTemplateTypeParam, "TemplateTypeParam", + "Template type parameter."), + clEnumValN(LVTypeKind::IsTemplateValueParam, "TemplateValueParam", + "Template value parameter."), + clEnumValN(LVTypeKind::IsTypedef, "Typedef", "Type definition."), + clEnumValN(LVTypeKind::IsUnspecified, "Unspecified", + "Unspecified type."), + clEnumValN(LVTypeKind::IsVolatile, "Volatile", "Volatile specifier."))); + +//===----------------------------------------------------------------------===// +// '--warning' options +//===----------------------------------------------------------------------===// +cl::OptionCategory + cmdline::WarningCategory("Warning Options", + "These control the generated warnings."); + +// --warning=[,,...] +cl::list cmdline::WarningOptions( + "warning", cl::cat(WarningCategory), cl::desc("Warnings to generate."), + cl::Hidden, cl::CommaSeparated, + values( + clEnumValN(LVWarningKind::All, "all", "All warnings."), + clEnumValN(LVWarningKind::Coverages, "coverages", + "Invalid symbol coverages values."), + clEnumValN(LVWarningKind::Lines, "lines", "Debug lines that are zero."), + clEnumValN(LVWarningKind::Locations, "locations", + "Invalid symbol locations."), + clEnumValN(LVWarningKind::Ranges, "ranges", "Invalid code ranges."))); + +//===----------------------------------------------------------------------===// +// '--internal' options +//===----------------------------------------------------------------------===// +cl::OptionCategory + cmdline::InternalCategory("Internal Options", + "Internal traces and extra debugging code."); + +// --internal=[,,...] +cl::list cmdline::InternalOptions( + "internal", cl::cat(InternalCategory), cl::desc("Traces to enable."), + cl::Hidden, cl::CommaSeparated, + values( + clEnumValN(LVInternalKind::All, "all", "Enable all traces."), + clEnumValN(LVInternalKind::Cmdline, "cmdline", "Print command line."), + clEnumValN(LVInternalKind::ID, "id", "Print unique element ID"), + clEnumValN(LVInternalKind::Integrity, "integrity", + "Check elements integrity."), + clEnumValN(LVInternalKind::None, "none", "Ignore element line number."), + clEnumValN(LVInternalKind::Tag, "tag", "Debug information tags."))); + +/// @} + +// Copy local options into a globally accessible data structure. +void llvm::logicalview::cmdline::propagateOptions() { + // Traverse list of options and update the given set (Using case and Regex). + auto UpdatePattern = [&](auto &List, auto &Set, bool IgnoreCase, + bool UseRegex) { + if (!List.empty()) + for (std::string &Pattern : List) + Set.insert((IgnoreCase && !UseRegex) ? StringRef(Pattern).lower() + : Pattern); + }; + + // Handle --select. + UpdatePattern(SelectPatterns, ReaderOptions.Select.Generic, + ReaderOptions.Select.IgnoreCase, ReaderOptions.Select.UseRegex); + + // Traverse list of options and update the given set. + auto UpdateSet = [&](auto &List, auto &Set) { + std::copy(List.begin(), List.end(), std::inserter(Set, Set.begin())); + }; + + // Handle options sets. + UpdateSet(AttributeOptions, ReaderOptions.Attribute.Kinds); + UpdateSet(PrintOptions, ReaderOptions.Print.Kinds); + UpdateSet(OutputOptions, ReaderOptions.Output.Kinds); + UpdateSet(ReportOptions, ReaderOptions.Report.Kinds); + UpdateSet(WarningOptions, ReaderOptions.Warning.Kinds); + UpdateSet(InternalOptions, ReaderOptions.Internal.Kinds); + + UpdateSet(SelectElements, ReaderOptions.Select.Elements); + UpdateSet(SelectLines, ReaderOptions.Select.Lines); + UpdateSet(SelectScopes, ReaderOptions.Select.Scopes); + UpdateSet(SelectSymbols, ReaderOptions.Select.Symbols); + UpdateSet(SelectTypes, ReaderOptions.Select.Types); + UpdateSet(SelectOffsets, ReaderOptions.Select.Offsets); + UpdateSet(CompareElements, ReaderOptions.Compare.Elements); + + // Resolve any options dependencies (ie. --print=all should set other + // print options, etc.). + ReaderOptions.resolveDependencies(); +} Index: llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp @@ -0,0 +1,141 @@ +//===-- llvm-debuginfo-analyzer.cpp - LLVM Debug info analysis utility ---===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This program is a utility that displays the logical view for the debug +// information. +// +//===----------------------------------------------------------------------===// + +#include "Options.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h" +#include "llvm/Support/COM.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; +using namespace logicalview; +using namespace cmdline; + +/// Create formatted StringError object. +static StringRef ToolName = "llvm-debuginfo-analyzer"; +template +static void error(std::error_code EC, char const *Fmt, const Ts &...Vals) { + if (!EC) + return; + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format(Fmt, Vals...); + WithColor::error(errs(), ToolName) << Stream.str() << "\n"; + exit(1); +} + +static void error(Error EC) { + if (!EC) + return; + handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) { + errs() << "\n"; + WithColor::error(errs(), ToolName) << EI.message() << ".\n"; + exit(1); + }); +} + +/// If the input path is a .dSYM bundle (as created by the dsymutil tool), +/// replace it with individual entries for each of the object files inside the +/// bundle otherwise return the input path. +static std::vector expandBundle(const std::string &InputPath) { + std::vector BundlePaths; + SmallString<256> BundlePath(InputPath); + // Normalize input path. This is necessary to accept `bundle.dSYM/`. + sys::path::remove_dots(BundlePath); + // Manually open up the bundle to avoid introducing additional dependencies. + if (sys::fs::is_directory(BundlePath) && + sys::path::extension(BundlePath) == ".dSYM") { + std::error_code EC; + sys::path::append(BundlePath, "Contents", "Resources", "DWARF"); + for (sys::fs::directory_iterator Dir(BundlePath, EC), DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + const std::string &Path = Dir->path(); + sys::fs::file_status Status; + EC = sys::fs::status(Path, Status); + error(EC, "%s", Path.c_str()); + switch (Status.type()) { + case sys::fs::file_type::regular_file: + case sys::fs::file_type::symlink_file: + case sys::fs::file_type::type_unknown: + BundlePaths.push_back(Path); + break; + default: /*ignore*/; + } + } + } + if (BundlePaths.empty()) + BundlePaths.push_back(InputPath); + return BundlePaths; +} + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + + // Initialize targets and assembly printers/parsers. + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + InitializeAllDisassemblers(); + + llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); + + cl::extrahelp HelpResponse( + "\nPass @FILE as argument to read options from FILE.\n"); + + cl::HideUnrelatedOptions( + {&AttributeCategory, &CompareCategory, &InternalCategory, &OutputCategory, + &PrintCategory, &ReportCategory, &SelectCategory, &WarningCategory}); + cl::ParseCommandLineOptions(argc, argv, + "Printing a logical representation of low-level " + "debug information.\n"); + cl::PrintOptionValues(); + + std::error_code EC; + ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_None); + error(EC, "Unable to open output file %s", OutputFilename.c_str()); + // Don't remove output file if we exit with an error. + OutputFile.keep(); + + // Defaults to a.out if no filenames specified. + if (InputFilenames.empty()) + InputFilenames.push_back("a.out"); + + // Expand any .dSYM bundles to the individual object files contained therein. + std::vector Objects; + for (const std::string &Filename : InputFilenames) { + std::vector Objs = expandBundle(Filename); + Objects.insert(Objects.end(), Objs.begin(), Objs.end()); + } + + propagateOptions(); + ScopedPrinter W(OutputFile.os()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + + // Print the command line. + if (options().getInternalCmdline()) { + raw_ostream &Stream = W.getOStream(); + Stream << "\nCommand line:\n"; + for (int Index = 0; Index < argc; ++Index) + Stream << " " << argv[Index] << "\n"; + Stream << "\n"; + } + + // Create readers and perform requested tasks on them. + if (Error Err = ReaderHandler.process()) + error(std::move(Err)); + + return EXIT_SUCCESS; +} Index: llvm/unittests/ADT/CMakeLists.txt =================================================================== --- llvm/unittests/ADT/CMakeLists.txt +++ llvm/unittests/ADT/CMakeLists.txt @@ -42,6 +42,7 @@ ImmutableSetTest.cpp IntEqClassesTest.cpp IntervalMapTest.cpp + IntervalTreeTest.cpp IntrusiveRefCntPtrTest.cpp IteratorTest.cpp MappedIteratorTest.cpp Index: llvm/unittests/ADT/IntervalTreeTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/ADT/IntervalTreeTest.cpp @@ -0,0 +1,1530 @@ +//===---- ADT/IntervalTreeTest.cpp - IntervalTree unit tests --------------===// +// +// 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 "llvm/ADT/IntervalTree.h" +#include "gtest/gtest.h" + +// The test cases for the IntervalTree implementation, follow the below steps: +// a) Insert a series of intervals with their associated mapped value. +// b) Create the interval tree. +// c) Query for specific interval point, covering points inside and outside +// of any given intervals. +// d) Traversal for specific interval point, using the iterators. +// +// When querying for a set of intervals containing a given value, the query is +// done three times, by calling: +// 1) Intervals = getContaining(...). +// 2) Intervals = getContaining(...). +// sortIntervals(Intervals, Sorting=Ascending). +// 3) Intervals = getContaining(...). +// sortIntervals(Intervals, Sorting=Ascending). +// +// The returned intervals are: +// 1) In their location order within the tree. +// 2) Smaller intervals first. +// 3) Bigger intervals first. + +using namespace llvm; + +namespace { + +// Helper function to test a specific item or iterator. +template +void checkItem(TPoint Point, TItem Item, TPoint Left, TPoint Right, + TValue Value) { + EXPECT_TRUE(Item->contains(Point)); + EXPECT_EQ(Item->left(), Left); + EXPECT_EQ(Item->right(), Right); + EXPECT_EQ(Item->value(), Value); +} + +// User class tree tests. +TEST(IntervalTreeTest, UserClass) { + using UUPoint = unsigned; + using UUValue = double; + class MyData : public IntervalData { + using UUData = IntervalData; + + public: + // Inherit Base's constructors. + using UUData::UUData; + PointType left() const { return UUData::left(); } + PointType right() const { return UUData::right(); } + ValueType value() const { return UUData::value(); } + + bool left(const PointType &Point) const { return UUData::left(Point); } + bool right(const PointType &Point) const { return UUData::right(Point); } + bool contains(const PointType &Point) const { + return UUData::contains(Point); + } + }; + + using UUTree = IntervalTree; + using UUReferences = UUTree::IntervalReferences; + using UUData = UUTree::DataType; + using UUAlloc = UUTree::Allocator; + + auto CheckData = [](UUPoint Point, const UUData *Data, UUPoint Left, + UUPoint Right, UUValue Value) { + checkItem(Point, Data, Left, Right, + Value); + }; + + UUAlloc Allocator; + UUTree Tree(Allocator); + UUReferences Intervals; + UUPoint Point; + + EXPECT_EQ(Tree.empty(), true); + Tree.clear(); + EXPECT_EQ(Tree.empty(), true); + + // [10, 20] <- (10.20) + // [30, 40] <- (30.40) + // + // [10...20] [30...40] + Tree.insert(10, 20, 10.20); + Tree.insert(30, 40, 30.40); + Tree.create(); + + // Invalid interval values: x < [10 + Point = 5; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); + + // Valid interval values: [10...20] + Point = 10; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + CheckData(Point, Intervals[0], 10, 20, 10.20); + + Point = 15; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + CheckData(Point, Intervals[0], 10, 20, 10.20); + + Point = 20; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + CheckData(Point, Intervals[0], 10, 20, 10.20); + + // Invalid interval values: 20] < x < [30 + Point = 25; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); + + // Valid interval values: [30...40] + Point = 30; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + CheckData(Point, Intervals[0], 30, 40, 30.40); + + Point = 35; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + CheckData(Point, Intervals[0], 30, 40, 30.40); + + Point = 40; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + CheckData(Point, Intervals[0], 30, 40, 30.40); + + // Invalid interval values: 40] < x + Point = 45; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); +} + +using UUPoint = unsigned; // Interval range type. +using UUValue = unsigned; // Mapped value type. + +using UUTree = IntervalTree; +using UUReferences = UUTree::IntervalReferences; +using UUData = UUTree::DataType; +using UUSorting = UUTree::Sorting; +using UUPoint = UUTree::PointType; +using UUValue = UUTree::ValueType; +using UUIter = UUTree::find_iterator; +using UUAlloc = UUTree::Allocator; + +void checkData(UUPoint Point, const UUData *Data, UUPoint Left, UUPoint Right, + UUValue Value) { + checkItem(Point, Data, Left, Right, Value); +} + +void checkData(UUPoint Point, UUIter Iter, UUPoint Left, UUPoint Right, + UUValue Value) { + checkItem(Point, Iter, Left, Right, Value); +} + +// Empty tree tests. +TEST(IntervalTreeTest, NoIntervals) { + UUAlloc Allocator; + UUTree Tree(Allocator); + EXPECT_EQ(Tree.empty(), true); + Tree.clear(); + EXPECT_EQ(Tree.empty(), true); + + // Create the tree and switch to query mode. + Tree.create(); + EXPECT_EQ(Tree.empty(), true); + EXPECT_EQ(Tree.find(1), Tree.find_end()); +} + +// One item tree tests. +TEST(IntervalTreeTest, OneInterval) { + UUAlloc Allocator; + UUTree Tree(Allocator); + UUReferences Intervals; + UUPoint Point; + + // [10, 20] <- (1020) + // + // [10...20] + Tree.insert(10, 20, 1020); + + EXPECT_EQ(Tree.empty(), true); + Tree.create(); + EXPECT_FALSE(Tree.empty()); + + // Invalid interval values: x < [10. + Point = 5; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); + + // Valid interval values: [10...20]. + Point = 10; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 20, 1020); + + Point = 15; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 20, 1020); + + Point = 20; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 20, 1020); + + // Invalid interval values: 20] < x + Point = 25; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); +} + +// Two items tree tests. No overlapping. +TEST(IntervalTreeTest, TwoIntervals) { + UUAlloc Allocator; + UUTree Tree(Allocator); + UUReferences Intervals; + UUPoint Point; + + // [10, 20] <- (1020) + // [30, 40] <- (3040) + // + // [10...20] [30...40] + Tree.insert(10, 20, 1020); + Tree.insert(30, 40, 3040); + Tree.create(); + + // Invalid interval values: x < [10 + Point = 5; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); + + // Valid interval values: [10...20] + Point = 10; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 20, 1020); + + Point = 15; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 20, 1020); + + Point = 20; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 20, 1020); + + // Invalid interval values: 20] < x < [30 + Point = 25; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); + + // Valid interval values: [30...40] + Point = 30; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 30, 40, 3040); + + Point = 35; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 30, 40, 3040); + + Point = 40; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 30, 40, 3040); + + // Invalid interval values: 40] < x + Point = 45; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); +} + +// Three items tree tests. No overlapping. +TEST(IntervalTreeTest, ThreeIntervals) { + UUAlloc Allocator; + UUTree Tree(Allocator); + UUReferences Intervals; + UUPoint Point; + + // [10, 20] <- (1020) + // [30, 40] <- (3040) + // [50, 60] <- (5060) + // + // [10...20] [30...40] [50...60] + Tree.insert(10, 20, 1020); + Tree.insert(30, 40, 3040); + Tree.insert(50, 60, 5060); + Tree.create(); + + // Invalid interval values: x < [10 + Point = 5; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); + + // Valid interval values: [10...20] + Point = 10; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 20, 1020); + + Point = 15; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 20, 1020); + + Point = 20; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 20, 1020); + + // Invalid interval values: 20] < x < [30 + Point = 25; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); + + // Valid interval values: [30...40] + Point = 30; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 30, 40, 3040); + + Point = 35; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 30, 40, 3040); + + Point = 40; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 30, 40, 3040); + + // Invalid interval values: 40] < x < [50 + Point = 45; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); + + // Valid interval values: [50...60] + Point = 50; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 50, 60, 5060); + + Point = 55; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 50, 60, 5060); + + Point = 60; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 50, 60, 5060); + + // Invalid interval values: 60] < x + Point = 65; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); +} + +// Simple overlapping tests. +TEST(IntervalTreeTest, SimpleIntervalsOverlapping) { + UUAlloc Allocator; + UUTree Tree(Allocator); + UUReferences Intervals; + UUPoint Point; + + // [40, 60] <- (4060) + // [30, 70] <- (3070) + // [20, 80] <- (2080) + // [10, 90] <- (1090) + // + // [40...60] + // [30...............70] + // [20...........................80] + // [10.......................................90] + Tree.insert(40, 60, 4060); + Tree.insert(30, 70, 3070); + Tree.insert(20, 80, 2080); + Tree.insert(10, 90, 1090); + Tree.create(); + + // Invalid interval values: x < [10 + Point = 5; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); + + // Valid interval values: + Point = 10; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 90, 1090); + + Point = 15; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 90, 1090); + + Point = 20; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 20, 80, 2080); + checkData(Point, Intervals[1], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + + Point = 25; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 20, 80, 2080); + checkData(Point, Intervals[1], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + + Point = 30; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 30, 70, 3070); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + + Point = 35; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 30, 70, 3070); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + + Point = 40; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + checkData(Point, Intervals[3], 40, 60, 4060); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 40, 60, 4060); + checkData(Point, Intervals[1], 30, 70, 3070); + checkData(Point, Intervals[2], 20, 80, 2080); + checkData(Point, Intervals[3], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + checkData(Point, Intervals[3], 40, 60, 4060); + + Point = 50; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + checkData(Point, Intervals[3], 40, 60, 4060); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 40, 60, 4060); + checkData(Point, Intervals[1], 30, 70, 3070); + checkData(Point, Intervals[2], 20, 80, 2080); + checkData(Point, Intervals[3], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + checkData(Point, Intervals[3], 40, 60, 4060); + + Point = 60; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + checkData(Point, Intervals[3], 40, 60, 4060); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 40, 60, 4060); + checkData(Point, Intervals[1], 30, 70, 3070); + checkData(Point, Intervals[2], 20, 80, 2080); + checkData(Point, Intervals[3], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + checkData(Point, Intervals[3], 40, 60, 4060); + + Point = 65; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 30, 70, 3070); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + + Point = 70; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 30, 70, 3070); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + checkData(Point, Intervals[2], 30, 70, 3070); + + Point = 75; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 20, 80, 2080); + checkData(Point, Intervals[1], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + + Point = 80; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 20, 80, 2080); + checkData(Point, Intervals[1], 10, 90, 1090); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 90, 1090); + checkData(Point, Intervals[1], 20, 80, 2080); + + Point = 85; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 90, 1090); + + Point = 90; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 90, 1090); + + // Invalid interval values: 90] < x + Point = 95; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); +} + +// Complex Overlapping. +TEST(IntervalTreeTest, ComplexIntervalsOverlapping) { + UUAlloc Allocator; + UUTree Tree(Allocator); + UUReferences Intervals; + UUPoint Point; + + // [30, 35] <- (3035) + // [39, 50] <- (3950) + // [55, 61] <- (5561) + // [31, 56] <- (3156) + // [12, 21] <- (1221) + // [25, 41] <- (2541) + // [49, 65] <- (4965) + // [71, 79] <- (7179) + // [11, 16] <- (1116) + // [20, 30] <- (2030) + // [36, 54] <- (3654) + // [60, 70] <- (6070) + // [74, 80] <- (7480) + // [15, 40] <- (1540) + // [43, 45] <- (4345) + // [50, 75] <- (5075) + // [10, 85] <- (1085) + + // 30--35 39------------50 55----61 + // 31------------------------56 + // 12--------21 25------------41 49-------------65 71-----79 + // 11----16 20-----30 36----------------54 60------70 74---- 80 + // 15---------------------40 43--45 50--------------------75 + // 10----------------------------------------------------------------------85 + + Tree.insert(30, 35, 3035); + Tree.insert(39, 50, 3950); + Tree.insert(55, 61, 5561); + Tree.insert(31, 56, 3156); + Tree.insert(12, 21, 1221); + Tree.insert(25, 41, 2541); + Tree.insert(49, 65, 4965); + Tree.insert(71, 79, 7179); + Tree.insert(11, 16, 1116); + Tree.insert(20, 30, 2030); + Tree.insert(36, 54, 3654); + Tree.insert(60, 70, 6070); + Tree.insert(74, 80, 7480); + Tree.insert(15, 40, 1540); + Tree.insert(43, 45, 4345); + Tree.insert(50, 75, 5075); + Tree.insert(10, 85, 1085); + Tree.create(); + + // Find valid interval values. + Point = 30; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 25, 41, 2541); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 20, 30, 2030); + checkData(Point, Intervals[4], 30, 35, 3035); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 30, 35, 3035); + checkData(Point, Intervals[1], 20, 30, 2030); + checkData(Point, Intervals[2], 25, 41, 2541); + checkData(Point, Intervals[3], 15, 40, 1540); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 25, 41, 2541); + checkData(Point, Intervals[3], 20, 30, 2030); + checkData(Point, Intervals[4], 30, 35, 3035); + + Point = 35; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 25, 41, 2541); + checkData(Point, Intervals[3], 15, 40, 1540); + checkData(Point, Intervals[4], 30, 35, 3035); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 30, 35, 3035); + checkData(Point, Intervals[1], 25, 41, 2541); + checkData(Point, Intervals[2], 31, 56, 3156); + checkData(Point, Intervals[3], 15, 40, 1540); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 25, 41, 2541); + checkData(Point, Intervals[4], 30, 35, 3035); + + Point = 39; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 39, 50, 3950); + checkData(Point, Intervals[4], 25, 41, 2541); + checkData(Point, Intervals[5], 15, 40, 1540); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 39, 50, 3950); + checkData(Point, Intervals[1], 25, 41, 2541); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 31, 56, 3156); + checkData(Point, Intervals[4], 15, 40, 1540); + checkData(Point, Intervals[5], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 36, 54, 3654); + checkData(Point, Intervals[4], 25, 41, 2541); + checkData(Point, Intervals[5], 39, 50, 3950); + + Point = 50; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 39, 50, 3950); + checkData(Point, Intervals[4], 49, 65, 4965); + checkData(Point, Intervals[5], 50, 75, 5075); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 39, 50, 3950); + checkData(Point, Intervals[1], 49, 65, 4965); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 31, 56, 3156); + checkData(Point, Intervals[4], 50, 75, 5075); + checkData(Point, Intervals[5], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 50, 75, 5075); + checkData(Point, Intervals[3], 36, 54, 3654); + checkData(Point, Intervals[4], 49, 65, 4965); + checkData(Point, Intervals[5], 39, 50, 3950); + + Point = 55; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 49, 65, 4965); + checkData(Point, Intervals[3], 50, 75, 5075); + checkData(Point, Intervals[4], 55, 61, 5561); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 55, 61, 5561); + checkData(Point, Intervals[1], 49, 65, 4965); + checkData(Point, Intervals[2], 31, 56, 3156); + checkData(Point, Intervals[3], 50, 75, 5075); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 50, 75, 5075); + checkData(Point, Intervals[3], 49, 65, 4965); + checkData(Point, Intervals[4], 55, 61, 5561); + + Point = 61; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 49, 65, 4965); + checkData(Point, Intervals[2], 50, 75, 5075); + checkData(Point, Intervals[3], 55, 61, 5561); + checkData(Point, Intervals[4], 60, 70, 6070); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 55, 61, 5561); + checkData(Point, Intervals[1], 60, 70, 6070); + checkData(Point, Intervals[2], 49, 65, 4965); + checkData(Point, Intervals[3], 50, 75, 5075); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 49, 65, 4965); + checkData(Point, Intervals[3], 60, 70, 6070); + checkData(Point, Intervals[4], 55, 61, 5561); + + Point = 31; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 25, 41, 2541); + checkData(Point, Intervals[3], 15, 40, 1540); + checkData(Point, Intervals[4], 30, 35, 3035); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 30, 35, 3035); + checkData(Point, Intervals[1], 25, 41, 2541); + checkData(Point, Intervals[2], 31, 56, 3156); + checkData(Point, Intervals[3], 15, 40, 1540); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 25, 41, 2541); + checkData(Point, Intervals[4], 30, 35, 3035); + + Point = 56; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 49, 65, 4965); + checkData(Point, Intervals[3], 50, 75, 5075); + checkData(Point, Intervals[4], 55, 61, 5561); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 55, 61, 5561); + checkData(Point, Intervals[1], 49, 65, 4965); + checkData(Point, Intervals[2], 31, 56, 3156); + checkData(Point, Intervals[3], 50, 75, 5075); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 50, 75, 5075); + checkData(Point, Intervals[3], 49, 65, 4965); + checkData(Point, Intervals[4], 55, 61, 5561); + + Point = 12; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 11, 16, 1116); + checkData(Point, Intervals[2], 12, 21, 1221); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 11, 16, 1116); + checkData(Point, Intervals[1], 12, 21, 1221); + checkData(Point, Intervals[2], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 12, 21, 1221); + checkData(Point, Intervals[2], 11, 16, 1116); + + Point = 21; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 20, 30, 2030); + checkData(Point, Intervals[3], 12, 21, 1221); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 12, 21, 1221); + checkData(Point, Intervals[1], 20, 30, 2030); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 20, 30, 2030); + checkData(Point, Intervals[3], 12, 21, 1221); + + Point = 25; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 20, 30, 2030); + checkData(Point, Intervals[3], 25, 41, 2541); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 20, 30, 2030); + checkData(Point, Intervals[1], 25, 41, 2541); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 25, 41, 2541); + checkData(Point, Intervals[3], 20, 30, 2030); + + Point = 41; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 39, 50, 3950); + checkData(Point, Intervals[4], 25, 41, 2541); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 39, 50, 3950); + checkData(Point, Intervals[1], 25, 41, 2541); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 31, 56, 3156); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 25, 41, 2541); + checkData(Point, Intervals[4], 39, 50, 3950); + + Point = 49; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 39, 50, 3950); + checkData(Point, Intervals[4], 49, 65, 4965); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 39, 50, 3950); + checkData(Point, Intervals[1], 49, 65, 4965); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 31, 56, 3156); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 49, 65, 4965); + checkData(Point, Intervals[4], 39, 50, 3950); + + Point = 65; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 60, 70, 6070); + checkData(Point, Intervals[3], 49, 65, 4965); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 60, 70, 6070); + checkData(Point, Intervals[1], 49, 65, 4965); + checkData(Point, Intervals[2], 50, 75, 5075); + checkData(Point, Intervals[3], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 49, 65, 4965); + checkData(Point, Intervals[3], 60, 70, 6070); + + Point = 71; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 71, 79, 7179); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 71, 79, 7179); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 71, 79, 7179); + + Point = 79; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 74, 80, 7480); + checkData(Point, Intervals[2], 71, 79, 7179); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 74, 80, 7480); + checkData(Point, Intervals[1], 71, 79, 7179); + checkData(Point, Intervals[2], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 71, 79, 7179); + checkData(Point, Intervals[2], 74, 80, 7480); + + Point = 11; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 11, 16, 1116); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 11, 16, 1116); + checkData(Point, Intervals[1], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 11, 16, 1116); + + Point = 16; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 12, 21, 1221); + checkData(Point, Intervals[3], 11, 16, 1116); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 11, 16, 1116); + checkData(Point, Intervals[1], 12, 21, 1221); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 12, 21, 1221); + checkData(Point, Intervals[3], 11, 16, 1116); + + Point = 20; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 20, 30, 2030); + checkData(Point, Intervals[3], 12, 21, 1221); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 12, 21, 1221); + checkData(Point, Intervals[1], 20, 30, 2030); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 20, 30, 2030); + checkData(Point, Intervals[3], 12, 21, 1221); + + Point = 30; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 25, 41, 2541); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 20, 30, 2030); + checkData(Point, Intervals[4], 30, 35, 3035); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 30, 35, 3035); + checkData(Point, Intervals[1], 20, 30, 2030); + checkData(Point, Intervals[2], 25, 41, 2541); + checkData(Point, Intervals[3], 15, 40, 1540); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 25, 41, 2541); + checkData(Point, Intervals[3], 20, 30, 2030); + checkData(Point, Intervals[4], 30, 35, 3035); + + Point = 36; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 25, 41, 2541); + checkData(Point, Intervals[4], 15, 40, 1540); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 25, 41, 2541); + checkData(Point, Intervals[1], 36, 54, 3654); + checkData(Point, Intervals[2], 31, 56, 3156); + checkData(Point, Intervals[3], 15, 40, 1540); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 36, 54, 3654); + checkData(Point, Intervals[4], 25, 41, 2541); + + Point = 54; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 49, 65, 4965); + checkData(Point, Intervals[4], 50, 75, 5075); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 49, 65, 4965); + checkData(Point, Intervals[1], 36, 54, 3654); + checkData(Point, Intervals[2], 31, 56, 3156); + checkData(Point, Intervals[3], 50, 75, 5075); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 50, 75, 5075); + checkData(Point, Intervals[3], 36, 54, 3654); + checkData(Point, Intervals[4], 49, 65, 4965); + + Point = 60; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 49, 65, 4965); + checkData(Point, Intervals[2], 50, 75, 5075); + checkData(Point, Intervals[3], 55, 61, 5561); + checkData(Point, Intervals[4], 60, 70, 6070); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 55, 61, 5561); + checkData(Point, Intervals[1], 60, 70, 6070); + checkData(Point, Intervals[2], 49, 65, 4965); + checkData(Point, Intervals[3], 50, 75, 5075); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 49, 65, 4965); + checkData(Point, Intervals[3], 60, 70, 6070); + checkData(Point, Intervals[4], 55, 61, 5561); + + Point = 70; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 60, 70, 6070); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 60, 70, 6070); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 3); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 60, 70, 6070); + + Point = 74; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 71, 79, 7179); + checkData(Point, Intervals[3], 74, 80, 7480); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 74, 80, 7480); + checkData(Point, Intervals[1], 71, 79, 7179); + checkData(Point, Intervals[2], 50, 75, 5075); + checkData(Point, Intervals[3], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 71, 79, 7179); + checkData(Point, Intervals[3], 74, 80, 7480); + + Point = 80; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 74, 80, 7480); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 74, 80, 7480); + checkData(Point, Intervals[1], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 2); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 74, 80, 7480); + + Point = 15; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 11, 16, 1116); + checkData(Point, Intervals[3], 12, 21, 1221); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 11, 16, 1116); + checkData(Point, Intervals[1], 12, 21, 1221); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 15, 40, 1540); + checkData(Point, Intervals[2], 12, 21, 1221); + checkData(Point, Intervals[3], 11, 16, 1116); + + Point = 40; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 39, 50, 3950); + checkData(Point, Intervals[4], 25, 41, 2541); + checkData(Point, Intervals[5], 15, 40, 1540); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 39, 50, 3950); + checkData(Point, Intervals[1], 25, 41, 2541); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 31, 56, 3156); + checkData(Point, Intervals[4], 15, 40, 1540); + checkData(Point, Intervals[5], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 15, 40, 1540); + checkData(Point, Intervals[3], 36, 54, 3654); + checkData(Point, Intervals[4], 25, 41, 2541); + checkData(Point, Intervals[5], 39, 50, 3950); + + Point = 43; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 39, 50, 3950); + checkData(Point, Intervals[4], 43, 45, 4345); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 43, 45, 4345); + checkData(Point, Intervals[1], 39, 50, 3950); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 31, 56, 3156); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 39, 50, 3950); + checkData(Point, Intervals[4], 43, 45, 4345); + + Point = 45; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 39, 50, 3950); + checkData(Point, Intervals[4], 43, 45, 4345); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 43, 45, 4345); + checkData(Point, Intervals[1], 39, 50, 3950); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 31, 56, 3156); + checkData(Point, Intervals[4], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 5); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 39, 50, 3950); + checkData(Point, Intervals[4], 43, 45, 4345); + + Point = 50; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 39, 50, 3950); + checkData(Point, Intervals[4], 49, 65, 4965); + checkData(Point, Intervals[5], 50, 75, 5075); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 39, 50, 3950); + checkData(Point, Intervals[1], 49, 65, 4965); + checkData(Point, Intervals[2], 36, 54, 3654); + checkData(Point, Intervals[3], 31, 56, 3156); + checkData(Point, Intervals[4], 50, 75, 5075); + checkData(Point, Intervals[5], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 6); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 31, 56, 3156); + checkData(Point, Intervals[2], 50, 75, 5075); + checkData(Point, Intervals[3], 36, 54, 3654); + checkData(Point, Intervals[4], 49, 65, 4965); + checkData(Point, Intervals[5], 39, 50, 3950); + + Point = 75; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 74, 80, 7480); + checkData(Point, Intervals[3], 71, 79, 7179); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Ascending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 74, 80, 7480); + checkData(Point, Intervals[1], 71, 79, 7179); + checkData(Point, Intervals[2], 50, 75, 5075); + checkData(Point, Intervals[3], 10, 85, 1085); + Intervals = Tree.getContaining(Point); + Tree.sortIntervals(Intervals, UUSorting::Descending); + ASSERT_TRUE(Intervals.size() == 4); + checkData(Point, Intervals[0], 10, 85, 1085); + checkData(Point, Intervals[1], 50, 75, 5075); + checkData(Point, Intervals[2], 71, 79, 7179); + checkData(Point, Intervals[3], 74, 80, 7480); + + Point = 10; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 85, 1085); + + Point = 85; + Intervals = Tree.getContaining(Point); + ASSERT_TRUE(Intervals.size() == 1); + checkData(Point, Intervals[0], 10, 85, 1085); + + // Invalid interval values. + Point = 5; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); + Point = 90; + Intervals = Tree.getContaining(Point); + EXPECT_EQ(Intervals.empty(), true); +} + +// Four items tree tests. Overlapping. Check mapped values and iterators. +TEST(IntervalTreeTest, MappedValuesIteratorsTree) { + UUAlloc Allocator; + UUTree Tree(Allocator); + UUPoint Point; + + // [10, 20] <- (1020) + // [15, 25] <- (1525) + // [50, 60] <- (5060) + // [55, 65] <- (5565) + // + // [10.........20] + // [15.........25] + // [50.........60] + // [55.........65] + Tree.insert(10, 20, 1020); + Tree.insert(15, 25, 1525); + Tree.insert(50, 60, 5060); + Tree.insert(55, 65, 5565); + Tree.create(); + + // Iterators. + { + // Start searching for '10'. + Point = 10; + UUIter Iter = Tree.find(Point); + EXPECT_NE(Iter, Tree.find_end()); + checkData(Point, Iter, 10, 20, 1020); + ++Iter; + EXPECT_EQ(Iter, Tree.find_end()); + } + { + // Start searching for '15'. + Point = 15; + UUIter Iter = Tree.find(Point); + ASSERT_TRUE(Iter != Tree.find_end()); + checkData(Point, Iter, 15, 25, 1525); + ++Iter; + ASSERT_TRUE(Iter != Tree.find_end()); + checkData(Point, Iter, 10, 20, 1020); + ++Iter; + EXPECT_EQ(Iter, Tree.find_end()); + } + { + // Start searching for '20'. + Point = 20; + UUIter Iter = Tree.find(Point); + ASSERT_TRUE(Iter != Tree.find_end()); + checkData(Point, Iter, 15, 25, 1525); + ++Iter; + ASSERT_TRUE(Iter != Tree.find_end()); + checkData(Point, Iter, 10, 20, 1020); + ++Iter; + EXPECT_EQ(Iter, Tree.find_end()); + } + { + // Start searching for '25'. + Point = 25; + UUIter Iter = Tree.find(Point); + ASSERT_TRUE(Iter != Tree.find_end()); + checkData(Point, Iter, 15, 25, 1525); + ++Iter; + EXPECT_EQ(Iter, Tree.find_end()); + } + // Invalid interval values. + { + Point = 5; + UUIter Iter = Tree.find(Point); + EXPECT_EQ(Iter, Tree.find_end()); + } + { + Point = 45; + UUIter Iter = Tree.find(Point); + EXPECT_EQ(Iter, Tree.find_end()); + } + { + Point = 70; + UUIter Iter = Tree.find(Point); + EXPECT_EQ(Iter, Tree.find_end()); + } +} + +} // namespace Index: llvm/unittests/DebugInfo/CMakeLists.txt =================================================================== --- llvm/unittests/DebugInfo/CMakeLists.txt +++ llvm/unittests/DebugInfo/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(CodeView) add_subdirectory(DWARF) add_subdirectory(GSYM) +add_subdirectory(LogicalView) add_subdirectory(MSF) add_subdirectory(PDB) add_subdirectory(Symbolizer) Index: llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt @@ -0,0 +1,21 @@ +set(LLVM_LINK_COMPONENTS + AllTargetsDescs + AllTargetsInfos + AllTargetsDisassemblers + DebugInfoLogicalView + MCDisassembler + ) + +add_llvm_unittest_with_input_files(DebugInfoLogicalViewTests + CodeViewReaderTest.cpp + CommandLineOptionsTest.cpp + CompareElementsTest.cpp + ELFReaderTest.cpp + SelectElementsTest.cpp + LocationRangesTest.cpp + LogicalElementsTest.cpp + StringPoolTest.cpp + WarningInternalTest.cpp + ) + +target_link_libraries(DebugInfoLogicalViewTests PRIVATE LLVMTestingSupport) Index: llvm/unittests/DebugInfo/LogicalView/CodeViewReaderTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/CodeViewReaderTest.cpp @@ -0,0 +1,475 @@ +//===- llvm/unittest/DebugInfo/LogicalView/CodeViewReaderTest.cpp ---------===// +// +// 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 "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h" +#include "llvm/Support/COM.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +extern const char *TestMainArgv0; + +namespace { + +const char *CodeViewClang = "test-codeview-clang.o"; +const char *CodeViewMsvc = "test-codeview-msvc.o"; +const char *CodeViewPdbMsvc = "test-codeview-pdb-msvc.o"; + +// Helper function to get the first scope child from the given parent. +LVScope *getFirstScopeChild(LVScope *Parent) { + EXPECT_NE(Parent, nullptr); + const LVScopes *Scopes = Parent->getScopes(); + EXPECT_NE(Scopes, nullptr); + EXPECT_EQ(Scopes->size(), 1); + + LVScopes::const_iterator Iter = Scopes->begin(); + LVScope *Child = *Iter; + EXPECT_NE(Child, nullptr); + return Child; +} + +// Helper function to create a reader. +LVReader *createReader(LVReaderHandler &ReaderHandler, + SmallString<128> &InputsDir, StringRef Filename) { + SmallString<128> ObjectName(InputsDir); + llvm::sys::path::append(ObjectName, Filename); + + Expected ReaderOrErr = + ReaderHandler.createReader(std::string(ObjectName)); + EXPECT_THAT_EXPECTED(ReaderOrErr, Succeeded()); + LVReader *Reader = *ReaderOrErr; + EXPECT_NE(Reader, nullptr); + return Reader; +} + +// Check the logical elements basic properties (Clang - Codeview). +void checkElementPropertiesClangCodeview(LVReader *Reader) { + LVScopeRoot *Root = Reader->getScopesRoot(); + LVScopeCompileUnit *CompileUnit = + static_cast(getFirstScopeChild(Root)); + LVScopeFunction *Function = + static_cast(getFirstScopeChild(CompileUnit)); + + EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64"); + EXPECT_EQ(Root->getName(), CodeViewClang); + + EXPECT_EQ(CompileUnit->getBaseAddress(), 0); + EXPECT_TRUE(CompileUnit->getProducer().startswith("clang")); + EXPECT_EQ(CompileUnit->getName(), "test.cpp"); + + EXPECT_EQ(Function->lineCount(), 16); + EXPECT_EQ(Function->scopeCount(), 1); + EXPECT_EQ(Function->symbolCount(), 3); + EXPECT_EQ(Function->typeCount(), 1); + EXPECT_EQ(Function->rangeCount(), 1); + + const LVLocations *Ranges = Function->getRanges(); + EXPECT_NE(Ranges, nullptr); + ASSERT_EQ(Ranges->size(), 1); + LVLocations::const_iterator IterLocation = Ranges->begin(); + LVLocation *Location = (*IterLocation); + EXPECT_STREQ(Location->getIntervalInfo().c_str(), + "{Range} Lines 2:9 [0x0000000000:0x0000000046]"); + + LVRange RangeList; + Function->getRanges(RangeList); + + const LVRangeEntries &RangeEntries = RangeList.getEntries(); + ASSERT_EQ(RangeEntries.size(), 2); + LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin(); + LVRangeEntry RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0); + EXPECT_EQ(RangeEntry.upper(), 0x46); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + ++IterRanges; + RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0x21); + EXPECT_EQ(RangeEntry.upper(), 0x35); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); + ASSERT_EQ(PublicNames.size(), 1); + LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); + LVScope *Foo = (*IterNames).first; + EXPECT_EQ(Foo->getName(), "foo"); + EXPECT_EQ(Foo->getLineNumber(), 0); + LVNameInfo NameInfo = (*IterNames).second; + EXPECT_EQ(NameInfo.first, 0); + EXPECT_EQ(NameInfo.second, 0x46); + + // Lines (debug and assembler) for 'foo'. + const LVLines *Lines = Foo->getLines(); + EXPECT_NE(Lines, nullptr); + ASSERT_EQ(Lines->size(), 0x10); +} + +// Check the logical elements basic properties (MSVC - Codeview). +void checkElementPropertiesMsvcCodeview(LVReader *Reader) { + LVScopeRoot *Root = Reader->getScopesRoot(); + LVScopeCompileUnit *CompileUnit = + static_cast(getFirstScopeChild(Root)); + LVScopeFunction *Function = + static_cast(getFirstScopeChild(CompileUnit)); + + EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64"); + EXPECT_EQ(Root->getName(), CodeViewMsvc); + + EXPECT_EQ(CompileUnit->getBaseAddress(), 0); + EXPECT_TRUE(CompileUnit->getProducer().startswith("Microsoft")); + EXPECT_EQ(CompileUnit->getName(), "test.cpp"); + + EXPECT_EQ(Function->lineCount(), 14); + EXPECT_EQ(Function->scopeCount(), 1); + EXPECT_EQ(Function->symbolCount(), 3); + EXPECT_EQ(Function->typeCount(), 0); + EXPECT_EQ(Function->rangeCount(), 1); + + const LVLocations *Ranges = Function->getRanges(); + EXPECT_NE(Ranges, nullptr); + ASSERT_EQ(Ranges->size(), 1); + LVLocations::const_iterator IterLocation = Ranges->begin(); + LVLocation *Location = (*IterLocation); + EXPECT_STREQ(Location->getIntervalInfo().c_str(), + "{Range} Lines 2:9 [0x0000000000:0x0000000031]"); + + LVRange RangeList; + Function->getRanges(RangeList); + + const LVRangeEntries &RangeEntries = RangeList.getEntries(); + ASSERT_EQ(RangeEntries.size(), 2); + LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin(); + LVRangeEntry RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0); + EXPECT_EQ(RangeEntry.upper(), 0x31); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + ++IterRanges; + RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0x1b); + EXPECT_EQ(RangeEntry.upper(), 0x28); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); + ASSERT_EQ(PublicNames.size(), 1); + LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); + LVScope *Foo = (*IterNames).first; + EXPECT_EQ(Foo->getName(), "foo"); + EXPECT_EQ(Foo->getLineNumber(), 0); + LVNameInfo NameInfo = (*IterNames).second; + EXPECT_EQ(NameInfo.first, 0); + EXPECT_EQ(NameInfo.second, 0x31); + + // Lines (debug and assembler) for 'foo'. + const LVLines *Lines = Foo->getLines(); + EXPECT_NE(Lines, nullptr); + ASSERT_EQ(Lines->size(), 0x0e); +} + +// Check the logical elements basic properties (MSVC - PDB). +void checkElementPropertiesMsvcCodeviewPdb(LVReader *Reader) { + LVScopeRoot *Root = Reader->getScopesRoot(); + LVScopeCompileUnit *CompileUnit = + static_cast(getFirstScopeChild(Root)); + LVScopeFunction *Function = + static_cast(getFirstScopeChild(CompileUnit)); + + EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64"); + EXPECT_EQ(Root->getName(), CodeViewPdbMsvc); + + EXPECT_EQ(CompileUnit->getBaseAddress(), 0); + EXPECT_TRUE(CompileUnit->getProducer().startswith("Microsoft")); + EXPECT_EQ(CompileUnit->getName(), "test.cpp"); + + EXPECT_EQ(Function->lineCount(), 14); + EXPECT_EQ(Function->scopeCount(), 1); + EXPECT_EQ(Function->symbolCount(), 3); + EXPECT_EQ(Function->typeCount(), 0); + EXPECT_EQ(Function->rangeCount(), 1); + + const LVLocations *Ranges = Function->getRanges(); + EXPECT_NE(Ranges, nullptr); + ASSERT_EQ(Ranges->size(), 1); + LVLocations::const_iterator IterLocation = Ranges->begin(); + LVLocation *Location = (*IterLocation); + EXPECT_STREQ(Location->getIntervalInfo().c_str(), + "{Range} Lines 2:9 [0x0000000000:0x0000000031]"); + + LVRange RangeList; + Function->getRanges(RangeList); + + const LVRangeEntries &RangeEntries = RangeList.getEntries(); + ASSERT_EQ(RangeEntries.size(), 2); + LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin(); + LVRangeEntry RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0); + EXPECT_EQ(RangeEntry.upper(), 0x31); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + ++IterRanges; + RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0x1b); + EXPECT_EQ(RangeEntry.upper(), 0x28); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); + ASSERT_EQ(PublicNames.size(), 1); + LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); + LVScope *Foo = (*IterNames).first; + EXPECT_EQ(Foo->getName(), "foo"); + EXPECT_EQ(Foo->getLineNumber(), 0); + LVNameInfo NameInfo = (*IterNames).second; + EXPECT_EQ(NameInfo.first, 0); + EXPECT_EQ(NameInfo.second, 0x31); + + // Lines (debug and assembler) for 'foo'. + const LVLines *Lines = Foo->getLines(); + EXPECT_NE(Lines, nullptr); + ASSERT_EQ(Lines->size(), 0x0e); +} + +struct SelectionInfo { + const char *Name; + LVElementGetFunction Function; +}; + +// Check the logical elements selection. +void checkElementSelection(LVReader *Reader, std::vector &Data, + size_t Size) { + LVScopeRoot *Root = Reader->getScopesRoot(); + LVScopeCompileUnit *CompileUnit = + static_cast(getFirstScopeChild(Root)); + + // Get the matched elements. + LVElements MatchedElements = CompileUnit->getMatchedElements(); + std::map MapElements; + for (LVElement *Element : MatchedElements) + MapElements[Element->getName()] = Element; + ASSERT_EQ(MapElements.size(), Size); + + std::map::iterator Iter = MapElements.begin(); + for (const SelectionInfo &Entry : Data) { + // Get matched element. + EXPECT_NE(Iter, MapElements.end()); + LVElement *Element = Iter->second; + EXPECT_NE(Element, nullptr); + EXPECT_NE(Element->getName().find(Entry.Name), StringRef::npos); + EXPECT_EQ((Element->*Entry.Function)(), 1); + ++Iter; + } + + // Get the parents for the matched elements. + LVScopes MatchedScopes = CompileUnit->getMatchedScopes(); + std::set SetScopes; + for (LVScope *Scope : MatchedScopes) + SetScopes.insert(Scope->getName()); + ASSERT_EQ(SetScopes.size(), 3); + + // Parents of selected elements. + std::set::iterator IterScope; + IterScope = SetScopes.find("foo"); + EXPECT_NE(IterScope, SetScopes.end()); + IterScope = SetScopes.find("foo::?"); + EXPECT_NE(IterScope, SetScopes.end()); + IterScope = SetScopes.find("test.cpp"); + EXPECT_NE(IterScope, SetScopes.end()); +} + +// Check the logical elements comparison. +void checkElementComparison(LVReader *Reference, LVReader *Target) { + LVCompare Compare(nulls()); + Error Err = Compare.execute(Reference, Target); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + + // Get comparison table. + LVPassTable PassTable = Compare.getPassTable(); + ASSERT_EQ(PassTable.size(), 2); + + LVReader *Reader; + LVElement *Element; + LVComparePass Pass; + + // Reference: Missing TypeDefinition 'INTEGER' + std::tie(Reader, Element, Pass) = PassTable[0]; + EXPECT_NE(Reader, nullptr); + EXPECT_NE(Element, nullptr); + EXPECT_EQ(Reader, Reference); + EXPECT_EQ(Element->getLevel(), 3); + EXPECT_EQ(Element->getLineNumber(), 0); + EXPECT_EQ(Element->getName(), "INTEGER"); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Target: Added TypeDefinition 'INTEGER' + std::tie(Reader, Element, Pass) = PassTable[1]; + EXPECT_NE(Reader, nullptr); + EXPECT_NE(Element, nullptr); + EXPECT_EQ(Reader, Target); + EXPECT_EQ(Element->getLevel(), 4); + EXPECT_EQ(Element->getLineNumber(), 0); + EXPECT_EQ(Element->getName(), "INTEGER"); + EXPECT_EQ(Pass, LVComparePass::Added); +} + +// Logical elements properties. +void elementProperties(SmallString<128> &InputsDir) { + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setAttributeFormat(); + ReaderOptions.setAttributeFilename(); + ReaderOptions.setAttributeProducer(); + ReaderOptions.setAttributePublics(); + ReaderOptions.setAttributeRange(); + ReaderOptions.setAttributeLocation(); + ReaderOptions.setPrintAll(); + ReaderOptions.resolveDependencies(); + + std::vector Objects; + ScopedPrinter W(outs()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + + // Check logical elements properties. + LVReader *Reader = createReader(ReaderHandler, InputsDir, CodeViewClang); + checkElementPropertiesClangCodeview(Reader); + ReaderHandler.deleteReader(Reader); + + Reader = createReader(ReaderHandler, InputsDir, CodeViewMsvc); + checkElementPropertiesMsvcCodeview(Reader); + ReaderHandler.deleteReader(Reader); + + Reader = createReader(ReaderHandler, InputsDir, CodeViewPdbMsvc); + checkElementPropertiesMsvcCodeviewPdb(Reader); + ReaderHandler.deleteReader(Reader); +} + +// Logical elements selection. +void elementSelection(SmallString<128> &InputsDir) { + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setPrintAll(); + + ReaderOptions.setSelectIgnoreCase(); + ReaderOptions.setSelectUseRegex(); + + ReaderOptions.setReportList(); // Matched elements. + ReaderOptions.setReportView(); // Parents for matched elements. + + // Add patterns. + ReaderOptions.Select.Generic.insert("foo"); + ReaderOptions.Select.Generic.insert("movl[ \t]?%"); + ReaderOptions.Select.Generic.insert("INT[a-z]*"); + ReaderOptions.Select.Generic.insert("CONSTANT"); + + ReaderOptions.resolveDependencies(); + + std::vector Objects; + ScopedPrinter W(outs()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + + // Check logical elements selection. + std::vector DataClang = { + {"* const int", &LVElement::getIsType}, + {"CONSTANT", &LVElement::getIsSymbol}, + {"INTEGER", &LVElement::getIsType}, + {"INTPTR", &LVElement::getIsType}, + {"ParamPtr", &LVElement::getIsSymbol}, + {"const int", &LVElement::getIsType}, + {"foo", &LVElement::getIsScope}, + {"foo::?", &LVElement::getIsScope}, + {"int", &LVElement::getIsType}, + {"movl", &LVElement::getIsLine}, + {"movl", &LVElement::getIsLine}}; + LVReader *Reader = createReader(ReaderHandler, InputsDir, CodeViewClang); + checkElementSelection(Reader, DataClang, DataClang.size()); + ReaderHandler.deleteReader(Reader); + + std::vector DataMsvc = {{"* const int", &LVElement::getIsType}, + {"CONSTANT", &LVElement::getIsSymbol}, + {"INTEGER", &LVElement::getIsType}, + {"INTPTR", &LVElement::getIsType}, + {"ParamPtr", &LVElement::getIsSymbol}, + {"const int", &LVElement::getIsType}, + {"foo", &LVElement::getIsScope}, + {"foo::?", &LVElement::getIsScope}, + {"int", &LVElement::getIsType}, + {"movl", &LVElement::getIsLine}}; + Reader = createReader(ReaderHandler, InputsDir, CodeViewMsvc); + checkElementSelection(Reader, DataMsvc, DataMsvc.size()); + ReaderHandler.deleteReader(Reader); +} + +// Compare logical elements. +void compareElements(SmallString<128> &InputsDir) { + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setPrintLines(); + ReaderOptions.setPrintSymbols(); + ReaderOptions.setPrintTypes(); + ReaderOptions.setCompareLines(); + ReaderOptions.setCompareSymbols(); + ReaderOptions.setCompareTypes(); + + ReaderOptions.resolveDependencies(); + + std::vector Objects; + ScopedPrinter W(outs()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + + // Check logical comparison. + LVReader *Reference = createReader(ReaderHandler, InputsDir, CodeViewClang); + LVReader *Target = createReader(ReaderHandler, InputsDir, CodeViewMsvc); + checkElementComparison(Reference, Target); + ReaderHandler.deleteReader(Reference); + ReaderHandler.deleteReader(Target); +} + +TEST(LogicalViewTest, CodeViewReader) { + // Initialize targets and assembly printers/parsers. + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + InitializeAllDisassemblers(); + + llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); + + SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0); + + // Logical elements general properties and selection. + elementProperties(InputsDir); + elementSelection(InputsDir); + + // Compare logical elements. + compareElements(InputsDir); +} + +} // namespace Index: llvm/unittests/DebugInfo/LogicalView/CommandLineOptionsTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/CommandLineOptionsTest.cpp @@ -0,0 +1,276 @@ +//===- llvm/unittest/DebugInfo/LogicalView/CommandLineOptionsTest.cpp -----===// +// +// 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 "llvm/DebugInfo/LogicalView/Core/LVOptions.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +namespace { + +// '--attribute' options. +TEST(CommandLineOptionsTest, attributeOptions) { + auto CheckStandardAttributes = [&](LVOptions &Options, bool Value) { + EXPECT_EQ(Options.getAttributeBase(), 1); + EXPECT_EQ(Options.getAttributeCoverage(), Value); + EXPECT_EQ(Options.getAttributeDirectories(), 1); + EXPECT_EQ(Options.getAttributeDiscriminator(), 1); + EXPECT_EQ(Options.getAttributeFilename(), 0); + EXPECT_EQ(Options.getAttributeFiles(), 1); + EXPECT_EQ(Options.getAttributeFormat(), 1); + EXPECT_EQ(Options.getAttributeLevel(), 1); + EXPECT_EQ(Options.getAttributeProducer(), 1); + EXPECT_EQ(Options.getAttributePublics(), 1); + EXPECT_EQ(Options.getAttributeRange(), 1); + EXPECT_EQ(Options.getAttributeReference(), 1); + EXPECT_EQ(Options.getAttributeZero(), 1); + }; + + auto CheckExtendedAttributes = [&](LVOptions &Options, bool Value) { + EXPECT_EQ(Options.getAttributeArgument(), 1); + EXPECT_EQ(Options.getAttributeDiscarded(), 1); + EXPECT_EQ(Options.getAttributeEncoded(), 1); + EXPECT_EQ(Options.getAttributeGaps(), Value); + EXPECT_EQ(Options.getAttributeGenerated(), 1); + EXPECT_EQ(Options.getAttributeGlobal(), 1); + EXPECT_EQ(Options.getAttributeInserted(), 1); + EXPECT_EQ(Options.getAttributeLinkage(), 1); + EXPECT_EQ(Options.getAttributeLocal(), 1); + EXPECT_EQ(Options.getAttributeLocation(), Value); + EXPECT_EQ(Options.getAttributeOffset(), 1); + EXPECT_EQ(Options.getAttributePathname(), 1); + EXPECT_EQ(Options.getAttributeQualified(), 1); + EXPECT_EQ(Options.getAttributeQualifier(), 1); + EXPECT_EQ(Options.getAttributeRegister(), Value); + EXPECT_EQ(Options.getAttributeSubrange(), 1); + EXPECT_EQ(Options.getAttributeSystem(), 1); + EXPECT_EQ(Options.getAttributeTypename(), 1); + }; + + // Location information is only relevant when printing symbols. + // It means the following attributes are dependent on --print=symbols: + // Coverage, gaps, location and register attributes. + // '--attribute=pathname' supersedes '--attribute=filename'. + + // Set standard and extended attributes. + LVOptions OptionsOne; + OptionsOne.setAttributeStandard(); + OptionsOne.setAttributeExtended(); + OptionsOne.resolveDependencies(); + CheckStandardAttributes(OptionsOne, false); + CheckExtendedAttributes(OptionsOne, false); + + // Set standard and extended attributes; enable location attributes. + LVOptions OptionsTwo; + OptionsTwo.setAttributeStandard(); + OptionsTwo.setAttributeExtended(); + OptionsTwo.setPrintSymbols(); + OptionsTwo.resolveDependencies(); + CheckStandardAttributes(OptionsTwo, true); + CheckExtendedAttributes(OptionsTwo, true); + + // Set all attributes. + LVOptions OptionsThree; + OptionsThree.setAttributeAll(); + OptionsThree.resolveDependencies(); + EXPECT_EQ(OptionsThree.getAttributeExtended(), 1); + EXPECT_EQ(OptionsThree.getAttributeStandard(), 1); + + // Set filename attribute. + LVOptions OptionsFour; + OptionsFour.setAttributeFilename(); + OptionsFour.resolveDependencies(); + EXPECT_EQ(OptionsFour.getAttributeFilename(), 1); + EXPECT_EQ(OptionsFour.getAttributePathname(), 0); + + // Set pathname attribute. + OptionsFour.setAttributePathname(); + OptionsFour.resolveDependencies(); + EXPECT_EQ(OptionsFour.getAttributeFilename(), 0); + EXPECT_EQ(OptionsFour.getAttributePathname(), 1); + + // The location attribute depends on: coverage, gaps or register. + LVOptions OptionsFive; + OptionsFive.setPrintSymbols(); + OptionsFive.resetAttributeLocation(); + OptionsFive.resolveDependencies(); + EXPECT_EQ(OptionsFive.getAttributeLocation(), 0); + + OptionsFive.resetAttributeLocation(); + OptionsFive.setAttributeCoverage(); + OptionsFive.resolveDependencies(); + EXPECT_EQ(OptionsFive.getAttributeLocation(), 1); + + OptionsFive.resetAttributeLocation(); + OptionsFive.setAttributeGaps(); + OptionsFive.resolveDependencies(); + EXPECT_EQ(OptionsFive.getAttributeLocation(), 1); + + OptionsFive.resetAttributeLocation(); + OptionsFive.setAttributeRegister(); + OptionsFive.resolveDependencies(); + EXPECT_EQ(OptionsFive.getAttributeLocation(), 1); +} + +// '--compare' options. +TEST(CommandLineOptionsTest, compareOptions) { + LVOptions OptionsOne; + OptionsOne.setCompareAll(); + OptionsOne.resolveDependencies(); + EXPECT_EQ(OptionsOne.getCompareLines(), 1); + EXPECT_EQ(OptionsOne.getCompareScopes(), 1); + EXPECT_EQ(OptionsOne.getCompareSymbols(), 1); + EXPECT_EQ(OptionsOne.getCompareTypes(), 1); + + // The compare scopes attribute depends on: symbols, types or lines. + LVOptions OptionsTwo; + OptionsTwo.resetCompareScopes(); + OptionsTwo.resolveDependencies(); + EXPECT_EQ(OptionsTwo.getCompareScopes(), 0); + + OptionsTwo.resetCompareScopes(); + OptionsTwo.setCompareLines(); + OptionsTwo.resolveDependencies(); + EXPECT_EQ(OptionsTwo.getCompareScopes(), 1); + + OptionsTwo.resetCompareScopes(); + OptionsTwo.setCompareSymbols(); + OptionsTwo.resolveDependencies(); + EXPECT_EQ(OptionsTwo.getCompareScopes(), 1); + + OptionsTwo.resetCompareScopes(); + OptionsTwo.setCompareTypes(); + OptionsTwo.resolveDependencies(); + EXPECT_EQ(OptionsTwo.getCompareScopes(), 1); + + // The compare option, set/reset other attributes. + LVOptions OptionsThree; + OptionsThree.setCompareAll(); + OptionsThree.resolveDependencies(); + EXPECT_EQ(OptionsThree.getAttributeArgument(), 1); + EXPECT_EQ(OptionsThree.getAttributeEncoded(), 1); + EXPECT_EQ(OptionsThree.getAttributeInserted(), 1); + EXPECT_EQ(OptionsThree.getAttributeMissing(), 1); + EXPECT_EQ(OptionsThree.getAttributeQualified(), 1); +} + +// '--internal' options. +TEST(CommandLineOptionsTest, internalOptions) { + LVOptions OptionsOne; + OptionsOne.setInternalAll(); + OptionsOne.resolveDependencies(); + EXPECT_EQ(OptionsOne.getInternalCmdline(), 1); + EXPECT_EQ(OptionsOne.getInternalID(), 1); + EXPECT_EQ(OptionsOne.getInternalIntegrity(), 1); + EXPECT_EQ(OptionsOne.getInternalNone(), 1); + EXPECT_EQ(OptionsOne.getInternalTag(), 1); +} + +// '--output' options. +TEST(CommandLineOptionsTest, outputOptions) { + LVOptions OptionsOne; + OptionsOne.setOutputAll(); + OptionsOne.resolveDependencies(); + EXPECT_EQ(OptionsOne.getOutputJson(), 1); + EXPECT_EQ(OptionsOne.getOutputSplit(), 1); + EXPECT_EQ(OptionsOne.getOutputText(), 1); + + // The pathname attribute is set with split output. + LVOptions OptionsTwo; + OptionsTwo.resetAttributePathname(); + OptionsTwo.setOutputSplit(); + OptionsTwo.resolveDependencies(); + EXPECT_EQ(OptionsTwo.getAttributePathname(), 1); + + // Setting an output folder, it sets split option. + LVOptions OptionsThree; + OptionsThree.resolveDependencies(); + EXPECT_EQ(OptionsThree.getOutputSplit(), 0); + + OptionsThree.setOutputFolder("folder-name"); + OptionsThree.resolveDependencies(); + EXPECT_EQ(OptionsThree.getOutputSplit(), 1); + EXPECT_STREQ(OptionsThree.getOutputFolder().c_str(), "folder-name"); + + // Assume '--output=text' as default. + LVOptions OptionsFour; + OptionsFour.resolveDependencies(); + EXPECT_EQ(OptionsFour.getOutputText(), 1); +} + +// '--print' options. +TEST(CommandLineOptionsTest, printOptions) { + LVOptions OptionsOne; + OptionsOne.setPrintAll(); + OptionsOne.resolveDependencies(); + EXPECT_EQ(OptionsOne.getPrintInstructions(), 1); + EXPECT_EQ(OptionsOne.getPrintLines(), 1); + EXPECT_EQ(OptionsOne.getPrintScopes(), 1); + EXPECT_EQ(OptionsOne.getPrintSizes(), 1); + EXPECT_EQ(OptionsOne.getPrintSymbols(), 1); + EXPECT_EQ(OptionsOne.getPrintSummary(), 1); + EXPECT_EQ(OptionsOne.getPrintTypes(), 1); + EXPECT_EQ(OptionsOne.getPrintWarnings(), 1); + + // '--print=elements' is a shortcut for: + // '--print=instructions,lines,scopes,symbols,types'. + LVOptions OptionsTwo; + OptionsTwo.setPrintElements(); + OptionsTwo.resolveDependencies(); + EXPECT_EQ(OptionsTwo.getPrintInstructions(), 1); + EXPECT_EQ(OptionsTwo.getPrintLines(), 1); + EXPECT_EQ(OptionsTwo.getPrintScopes(), 1); + EXPECT_EQ(OptionsTwo.getPrintSizes(), 0); + EXPECT_EQ(OptionsTwo.getPrintSymbols(), 1); + EXPECT_EQ(OptionsTwo.getPrintSummary(), 0); + EXPECT_EQ(OptionsTwo.getPrintTypes(), 1); + EXPECT_EQ(OptionsTwo.getPrintWarnings(), 0); +} + +// '--report' options. +TEST(CommandLineOptionsTest, reportOptions) { + LVOptions OptionsOne; + OptionsOne.setReportAll(); + OptionsOne.resolveDependencies(); + EXPECT_EQ(OptionsOne.getReportChildren(), 1); + EXPECT_EQ(OptionsOne.getReportList(), 1); + EXPECT_EQ(OptionsOne.getReportParents(), 1); + EXPECT_EQ(OptionsOne.getReportView(), 1); + + // '--report=view' is a shortcut for '--report=parents,children'. + LVOptions OptionsTwo; + OptionsTwo.setReportView(); + OptionsTwo.resolveDependencies(); + EXPECT_EQ(OptionsTwo.getReportChildren(), 1); + EXPECT_EQ(OptionsTwo.getReportParents(), 1); +} + +// '--select' options. +TEST(CommandLineOptionsTest, selectOptions) { + LVOptions OptionsOne; + OptionsOne.setSelectIgnoreCase(); + OptionsOne.setSelectUseRegex(); + OptionsOne.resolveDependencies(); + EXPECT_EQ(OptionsOne.getSelectIgnoreCase(), 1); + EXPECT_EQ(OptionsOne.getSelectUseRegex(), 1); +} + +// '--warning' options. +TEST(CommandLineOptionsTest, warningOptions) { + LVOptions OptionsOne; + OptionsOne.setWarningAll(); + OptionsOne.resolveDependencies(); + EXPECT_EQ(OptionsOne.getWarningCoverages(), 1); + EXPECT_EQ(OptionsOne.getWarningLines(), 1); + EXPECT_EQ(OptionsOne.getWarningLocations(), 1); + EXPECT_EQ(OptionsOne.getWarningRanges(), 1); +} + +} // namespace Index: llvm/unittests/DebugInfo/LogicalView/CompareElementsTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/CompareElementsTest.cpp @@ -0,0 +1,450 @@ +//===- llvm/unittest/DebugInfo/LogicalView/CompareElementsTest.cpp --------===// +// +// 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 "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +namespace { + +//===----------------------------------------------------------------------===// +// Basic Reader functionality. +class ReaderTestCompare : public LVReader { +public: + // Types. + LVType *IntegerType = nullptr; + LVType *UnsignedType = nullptr; + LVType *GlobalType = nullptr; + LVType *LocalType = nullptr; + LVType *NestedType = nullptr; + LVTypeDefinition *TypeDefinitionOne = nullptr; + LVTypeDefinition *TypeDefinitionTwo = nullptr; + LVTypeEnumerator *EnumeratorOne = nullptr; + LVTypeEnumerator *EnumeratorTwo = nullptr; + + // Scopes. + LVScope *NestedScope = nullptr; + LVScope *InnerScope = nullptr; + LVScopeAggregate *Aggregate = nullptr; + LVScopeEnumeration *Enumeration = nullptr; + LVScopeFunction *FunctionOne = nullptr; + LVScopeFunction *FunctionTwo = nullptr; + LVScopeNamespace *Namespace = nullptr; + + // Symbols. + LVSymbol *GlobalVariable = nullptr; + LVSymbol *LocalVariable = nullptr; + LVSymbol *ClassMember = nullptr; + LVSymbol *NestedVariable = nullptr; + LVSymbol *ParameterOne = nullptr; + LVSymbol *ParameterTwo = nullptr; + + // Lines. + LVLine *LineOne = nullptr; + LVLine *LineTwo = nullptr; + LVLine *LineThree = nullptr; + +protected: + void add(LVScope *Parent, LVElement *Element); + template T *create(F Function) { + // 'Function' will update a specific kind of the logical element to + // have the ability of kind selection. + T *Element = new (std::nothrow) T(); + EXPECT_NE(Element, nullptr); + (Element->*Function)(); + return Element; + } + void set(LVElement *Element, StringRef Name, LVOffset Offset, + uint32_t LineNumber = 0, LVElement *Type = nullptr); + +public: + ReaderTestCompare(ScopedPrinter &W) : LVReader("", "", W) { + setInstance(this); + } + + Error createScopes() { return LVReader::createScopes(); } + Error printScopes() { return LVReader::printScopes(); } + + void createElements(); + void addElements(bool IsReference, bool IsTarget); + void initElements(); +}; + +// Helper function to add a logical element to a given scope. +void ReaderTestCompare::add(LVScope *Parent, LVElement *Child) { + Parent->addElement(Child); + EXPECT_EQ(Child->getParent(), Parent); + EXPECT_EQ(Child->getLevel(), Parent->getLevel() + 1); +} + +// Helper function to set the initial values for a given logical element. +void ReaderTestCompare::set(LVElement *Element, StringRef Name, LVOffset Offset, + uint32_t LineNumber, LVElement *Type) { + Element->setName(Name); + Element->setOffset(Offset); + Element->setLineNumber(LineNumber); + Element->setType(Type); +} + +//===----------------------------------------------------------------------===// +// Create the logical elements. +void ReaderTestCompare::createElements() { + // Create scope root. + Error Err = createScopes(); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + Root = getScopesRoot(); + EXPECT_NE(Root, nullptr); + + // Create the logical types. + IntegerType = create(&LVType::setIsBase); + UnsignedType = create(&LVType::setIsBase); + GlobalType = create(&LVType::setIsBase); + LocalType = create(&LVType::setIsBase); + NestedType = create(&LVType::setIsBase); + EnumeratorOne = + create(&LVType::setIsEnumerator); + EnumeratorTwo = + create(&LVType::setIsEnumerator); + TypeDefinitionOne = + create(&LVType::setIsTypedef); + TypeDefinitionTwo = + create(&LVType::setIsTypedef); + + // Create the logical scopes. + NestedScope = + create(&LVScope::setIsLexicalBlock); + InnerScope = create(&LVScope::setIsLexicalBlock); + Aggregate = + create(&LVScope::setIsAggregate); + CompileUnit = create( + &LVScope::setIsCompileUnit); + Enumeration = create( + &LVScope::setIsEnumeration); + FunctionOne = + create(&LVScope::setIsFunction); + FunctionTwo = + create(&LVScope::setIsFunction); + Namespace = + create(&LVScope::setIsNamespace); + + // Create the logical symbols. + GlobalVariable = + create(&LVSymbol::setIsVariable); + LocalVariable = + create(&LVSymbol::setIsVariable); + ClassMember = create(&LVSymbol::setIsMember); + NestedVariable = + create(&LVSymbol::setIsVariable); + ParameterOne = + create(&LVSymbol::setIsParameter); + ParameterTwo = + create(&LVSymbol::setIsParameter); + + // Create the logical lines. + LineOne = create(&LVLine::setIsLineDebug); + LineTwo = create(&LVLine::setIsLineDebug); + LineThree = create(&LVLine::setIsLineDebug); +} + +// Reference Reader: Target Reader: +// ---------------------- ---------------------- +// Root Root +// CompileUnit CompileUnit +// IntegerType IntegerType +// UnsignedType UnsignedType +// FunctionOne FunctionOne +// ParameterOne ParameterOne +// LocalVariable --- +// LocalType LocalType +// LineOne LineOne +// NestedScope NestedScope +// NestedVariable NestedVariable +// NestedType NestedType +// LineTwo --- +// InnerScope InnerScope +// --- LineThree +// --- FunctionTwo +// --- ParameterTwo +// GlobalVariable GlobalVariable +// GlobalType GlobalType +// Namespace Namespace +// Aggregate Aggregate +// ClassMember ClassMember +// Enumeration Enumeration +// EnumeratorOne EnumeratorOne +// EnumeratorTwo EnumeratorTwo +// TypeDefinitionOne --- +// --- TypeDefinitionTwo + +// Create the logical view adding the created logical elements. +void ReaderTestCompare::addElements(bool IsReference, bool IsTarget) { + Root->setName(IsReference ? "Reference-Reader" : "Target-Reader"); + + auto Insert = [&](bool Insert, auto *Parent, auto *Child) { + if (Insert) + add(Parent, Child); + }; + + setCompileUnit(CompileUnit); + add(Root, CompileUnit); + + // Add elements to CompileUnit. + Insert(true, CompileUnit, IntegerType); + Insert(true, CompileUnit, UnsignedType); + Insert(true, CompileUnit, FunctionOne); + Insert(IsTarget, CompileUnit, FunctionTwo); + Insert(true, CompileUnit, GlobalVariable); + Insert(true, CompileUnit, GlobalType); + Insert(true, CompileUnit, Namespace); + + // Add elements to Namespace. + Insert(true, Namespace, Aggregate); + Insert(true, Namespace, Enumeration); + Insert(IsReference, Namespace, TypeDefinitionOne); + Insert(IsTarget, Namespace, TypeDefinitionTwo); + + // Add elements to FunctionOne. + Insert(true, FunctionOne, ParameterOne); + Insert(IsReference, FunctionOne, LocalVariable); + Insert(true, FunctionOne, LocalType); + Insert(true, FunctionOne, LineOne); + Insert(true, FunctionOne, NestedScope); + + // Add elements to FunctionTwo. + Insert(IsTarget, FunctionTwo, ParameterTwo); + + // Add elements to NestedScope. + Insert(true, NestedScope, NestedVariable); + Insert(true, NestedScope, NestedType); + Insert(IsReference, NestedScope, LineTwo); + Insert(true, NestedScope, InnerScope); + + // Add elements to Enumeration. + Insert(true, Enumeration, EnumeratorOne); + Insert(true, Enumeration, EnumeratorTwo); + + // Add elements to Aggregate. + Insert(true, Aggregate, ClassMember); + + Insert(IsTarget, InnerScope, LineThree); +} + +// Set initial values to logical elements. +void ReaderTestCompare::initElements() { + setFilename("LogicalElements.obj"); + + Root->setFileFormatName("FileFormat"); + + // Types. + set(IntegerType, "int", 0x1000); + set(UnsignedType, "unsigned", 0x1010); + set(GlobalType, "GlobalType", 0x1020, 1020); + set(LocalType, "LocalType", 0x1030, 1030); + set(NestedType, "NestedType", 0x1040, 1040); + + set(TypeDefinitionOne, "INTEGER", 0x1050, 1050, IntegerType); + set(TypeDefinitionTwo, "INT", 0x1060, 1060, TypeDefinitionOne); + + set(EnumeratorOne, "One", 0x1070, 1070); + EnumeratorOne->setValue("Blue"); + + set(EnumeratorTwo, "Two", 0x1080, 1080); + EnumeratorTwo->setValue("Red"); + + // Scopes. + set(Aggregate, "Class", 0x2000, 2000); + set(Enumeration, "Colors", 0x2010, 2010); + set(FunctionOne, "FunctionOne", 0x2020, 2020, GlobalType); + set(FunctionTwo, "FunctionTwo", 0x2030, 2030, GlobalType); + set(Namespace, "Namespace", 0x2040, 2040); + set(NestedScope, "", 0x2050, 2050); + set(InnerScope, "", 0x2060, 2060); + set(CompileUnit, "test.cpp", 0x2070, 2070); + + // Symbols. + set(GlobalVariable, "GlobalVariable", 0x3000, 3000); + set(LocalVariable, "LocalVariable", 0x3010, 3010, UnsignedType); + set(ClassMember, "ClassMember", 0x3020, 3020, IntegerType); + set(ParameterOne, "ParameterOne", 0x3030, 3030, UnsignedType); + set(ParameterTwo, "ParameterTwo", 0x3040, 3040, UnsignedType); + set(NestedVariable, "NestedVariable", 0x3050, 3050); + + // Lines. + set(LineOne, "", 0x4000, 4000); + set(LineTwo, "", 0x4010, 4010); + set(LineThree, "", 0x4020, 4020); +} + +// Compare the logical views. +void compareReadersViews(ReaderTestCompare *ReferenceReader, + ReaderTestCompare *TargetReader) { + LVCompare Compare(nulls()); + Error Err = Compare.execute(ReferenceReader, TargetReader); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + + // Get comparison table. + LVPassTable PassTable = Compare.getPassTable(); + ASSERT_EQ(PassTable.size(), 5); + + LVReader *Reader; + LVElement *Element; + LVComparePass Pass; + + // Reference: Missing 'FunctionOne' + std::tie(Reader, Element, Pass) = PassTable[0]; + EXPECT_EQ(Reader, ReferenceReader); + EXPECT_EQ(Element, ReferenceReader->FunctionOne); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Reference: Missing 'TypeDefinitionOne' + std::tie(Reader, Element, Pass) = PassTable[1]; + EXPECT_EQ(Reader, ReferenceReader); + EXPECT_EQ(Element, ReferenceReader->TypeDefinitionOne); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Target: Added 'FunctionOne' + std::tie(Reader, Element, Pass) = PassTable[2]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->FunctionOne); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added 'FunctionTwo' + std::tie(Reader, Element, Pass) = PassTable[3]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->FunctionTwo); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added 'TypeDefinitionTwo' + std::tie(Reader, Element, Pass) = PassTable[4]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->TypeDefinitionTwo); + EXPECT_EQ(Pass, LVComparePass::Added); +} + +// Compare the logical elements. +void compareReadersElements(ReaderTestCompare *ReferenceReader, + ReaderTestCompare *TargetReader) { + LVCompare Compare(nulls()); + Error Err = Compare.execute(ReferenceReader, TargetReader); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + + // Get comparison table. + LVPassTable PassTable = Compare.getPassTable(); + ASSERT_EQ(PassTable.size(), 7); + + LVReader *Reader; + LVElement *Element; + LVComparePass Pass; + + // Reference: Missing 'LocalVariable' + std::tie(Reader, Element, Pass) = PassTable[0]; + EXPECT_EQ(Reader, ReferenceReader); + EXPECT_EQ(Element, ReferenceReader->LocalVariable); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Reference: Missing 'TypeDefinitionOne' + std::tie(Reader, Element, Pass) = PassTable[1]; + EXPECT_EQ(Reader, ReferenceReader); + EXPECT_EQ(Element, ReferenceReader->TypeDefinitionOne); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Reference: Missing 'LineTwo' + std::tie(Reader, Element, Pass) = PassTable[2]; + EXPECT_EQ(Reader, ReferenceReader); + EXPECT_EQ(Element, ReferenceReader->LineTwo); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Target: Added 'FunctionTwo' + std::tie(Reader, Element, Pass) = PassTable[3]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->FunctionTwo); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added 'ParameterTwo' + std::tie(Reader, Element, Pass) = PassTable[4]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->ParameterTwo); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added 'TypeDefinitionTwo' + std::tie(Reader, Element, Pass) = PassTable[5]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->TypeDefinitionTwo); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added 'LineThree' + std::tie(Reader, Element, Pass) = PassTable[6]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->LineThree); + EXPECT_EQ(Pass, LVComparePass::Added); +} + +TEST(LogicalViewTest, CompareElements) { + ScopedPrinter W(outs()); + + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setCompareLines(); + ReaderOptions.setCompareScopes(); + ReaderOptions.setCompareSymbols(); + ReaderOptions.setCompareTypes(); + + // The next set-ups are very similar. The only difference is the + // comparison type, which must be set before the readers are created. + // Views: setCompareContext() + // Elements: resetCompareContext() + { + // Compare the logical views as whole unit (--compare-context). + ReaderOptions.setCompareContext(); + ReaderOptions.resolveDependencies(); + options().setOptions(&ReaderOptions); + + ReaderTestCompare ReferenceReader(W); + ReferenceReader.createElements(); + ReferenceReader.addElements(/*IsReference=*/true, /*IsTarget=*/false); + ReferenceReader.initElements(); + + ReaderTestCompare TargetReader(W); + TargetReader.createElements(); + TargetReader.addElements(/*IsReference=*/false, /*IsTarget=*/true); + TargetReader.initElements(); + + compareReadersViews(&ReferenceReader, &TargetReader); + } + { + // Compare the logical elements. + ReaderOptions.resetCompareContext(); + ReaderOptions.resolveDependencies(); + options().setOptions(&ReaderOptions); + + ReaderTestCompare ReferenceReader(W); + ReferenceReader.createElements(); + ReferenceReader.addElements(/*IsReference=*/true, /*IsTarget=*/false); + ReferenceReader.initElements(); + + ReaderTestCompare TargetReader(W); + TargetReader.createElements(); + TargetReader.addElements(/*IsReference=*/false, /*IsTarget=*/true); + TargetReader.initElements(); + + compareReadersElements(&ReferenceReader, &TargetReader); + } +} + +} // namespace Index: llvm/unittests/DebugInfo/LogicalView/ELFReaderTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/ELFReaderTest.cpp @@ -0,0 +1,342 @@ +//===- llvm/unittest/DebugInfo/LogicalView/ELFReaderTest.cpp --------------===// +// +// 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 "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h" +#include "llvm/Support/COM.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +extern const char *TestMainArgv0; + +namespace { + +const char *DwarfClang = "test-dwarf-clang.o"; +const char *DwarfGcc = "test-dwarf-gcc.o"; + +// Helper function to get the first compile unit. +LVScopeCompileUnit *getFirstCompileUnit(LVScopeRoot *Root) { + EXPECT_NE(Root, nullptr); + const LVScopes *CompileUnits = Root->getScopes(); + EXPECT_NE(CompileUnits, nullptr); + EXPECT_EQ(CompileUnits->size(), 1); + + LVScopes::const_iterator Iter = CompileUnits->begin(); + EXPECT_NE(Iter, nullptr); + LVScopeCompileUnit *CompileUnit = static_cast(*Iter); + EXPECT_NE(CompileUnit, nullptr); + return CompileUnit; +} + +// Helper function to create a reader. +LVReader *createReader(LVReaderHandler &ReaderHandler, + SmallString<128> &InputsDir, StringRef Filename) { + SmallString<128> ObjectName(InputsDir); + llvm::sys::path::append(ObjectName, Filename); + + Expected ReaderOrErr = + ReaderHandler.createReader(std::string(ObjectName)); + EXPECT_THAT_EXPECTED(ReaderOrErr, Succeeded()); + LVReader *Reader = *ReaderOrErr; + EXPECT_NE(Reader, nullptr); + return Reader; +} + +// Check the logical elements basic properties. +void checkElementProperties(LVReader *Reader) { + LVScopeRoot *Root = Reader->getScopesRoot(); + LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root); + + EXPECT_EQ(Root->getFileFormatName(), "elf64-x86-64"); + EXPECT_EQ(Root->getName(), DwarfClang); + + EXPECT_EQ(CompileUnit->getBaseAddress(), 0); + EXPECT_TRUE(CompileUnit->getProducer().startswith("clang")); + EXPECT_EQ(CompileUnit->getName(), "test.cpp"); + + EXPECT_EQ(CompileUnit->lineCount(), 0); + EXPECT_EQ(CompileUnit->scopeCount(), 1); + EXPECT_EQ(CompileUnit->symbolCount(), 0); + EXPECT_EQ(CompileUnit->typeCount(), 7); + EXPECT_EQ(CompileUnit->rangeCount(), 1); + + const LVLocations *Ranges = CompileUnit->getRanges(); + EXPECT_NE(Ranges, nullptr); + ASSERT_EQ(Ranges->size(), 1); + LVLocations::const_iterator IterLocation = Ranges->begin(); + LVLocation *Location = (*IterLocation); + EXPECT_STREQ(Location->getIntervalInfo().c_str(), + "{Range} Lines 2:9 [0x0000000000:0x000000003a]"); + + LVRange RangeList; + CompileUnit->getRanges(RangeList); + + const LVRangeEntries &RangeEntries = RangeList.getEntries(); + ASSERT_EQ(RangeEntries.size(), 2); + LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin(); + LVRangeEntry RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0); + EXPECT_EQ(RangeEntry.upper(), 0x3a); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "test.cpp"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0x0b); + + ++IterRanges; + RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0x1c); + EXPECT_EQ(RangeEntry.upper(), 0x2f); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0x71); + + const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); + ASSERT_EQ(PublicNames.size(), 1); + LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); + LVScope *Function = (*IterNames).first; + EXPECT_EQ(Function->getName(), "foo"); + EXPECT_EQ(Function->getLineNumber(), 2); + LVNameInfo NameInfo = (*IterNames).second; + EXPECT_EQ(NameInfo.first, 0); + EXPECT_EQ(NameInfo.second, 0x3a); + + // Lines (debug and assembler) for 'foo'. + const LVLines *Lines = Function->getLines(); + EXPECT_NE(Lines, nullptr); + ASSERT_EQ(Lines->size(), 0x12); +} + +// Check the logical elements selection. +void checkElementSelection(LVReader *Reader) { + LVScopeRoot *Root = Reader->getScopesRoot(); + LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root); + + // Get the matched elements. + LVElements MatchedElements = CompileUnit->getMatchedElements(); + std::map MapElements; + for (LVElement *Element : MatchedElements) + MapElements[Element->getOffset()] = Element; + ASSERT_EQ(MapElements.size(), 0xe); + + LVElement *Element = MapElements[0x000000004b]; // 'foo' + EXPECT_NE(Element, nullptr); + EXPECT_NE(Element->getName().find("foo"), StringRef::npos); + EXPECT_EQ(Element->getIsScope(), 1); + + Element = MapElements[0x00000000c0]; // 'CONSTANT' + EXPECT_NE(Element, nullptr); + EXPECT_NE(Element->getName().find("CONSTANT"), StringRef::npos); + EXPECT_EQ(Element->getIsSymbol(), 1); + + Element = MapElements[0x000000002d]; // 'INTPTR' + EXPECT_NE(Element, nullptr); + EXPECT_NE(Element->getName().find("INTPTR"), StringRef::npos); + EXPECT_EQ(Element->getIsType(), 1); + + Element = MapElements[0x00000000af]; // 'INTEGER' + EXPECT_NE(Element, nullptr); + EXPECT_NE(Element->getName().find("INTEGER"), StringRef::npos); + EXPECT_EQ(Element->getIsType(), 1); + + Element = MapElements[0x000000000f]; // 'movl %edx, %eax' + EXPECT_NE(Element, nullptr); + EXPECT_NE(Element->getName().find("movl"), StringRef::npos); + EXPECT_EQ(Element->getIsLine(), 1); + + // Get the parents for the matched elements. + LVScopes MatchedScopes = CompileUnit->getMatchedScopes(); + std::set SetScopes; + for (LVScope *Scope : MatchedScopes) + SetScopes.insert(Scope->getOffset()); + std::set::iterator Iter; + ASSERT_EQ(SetScopes.size(), 3); + + Iter = SetScopes.find(0x000000000b); // CompileUnit <- 'foo' + EXPECT_NE(Iter, SetScopes.end()); + Iter = SetScopes.find(0x000000009e); // Function <- 'movl %edx, %eax' + EXPECT_NE(Iter, SetScopes.end()); + Iter = SetScopes.find(0x000000009e); // LexicalScope <- 'INTEGER' + EXPECT_NE(Iter, SetScopes.end()); +} + +// Check the logical elements comparison. +void checkElementComparison(LVReader *Reference, LVReader *Target) { + LVCompare Compare(nulls()); + Error Err = Compare.execute(Reference, Target); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + + // Get comparison table. + LVPassTable PassTable = Compare.getPassTable(); + ASSERT_EQ(PassTable.size(), 5); + + LVReader *Reader; + LVElement *Element; + LVComparePass Pass; + + // Reference: Missing Variable 'CONSTANT' + std::tie(Reader, Element, Pass) = PassTable[0]; + EXPECT_NE(Reader, nullptr); + EXPECT_NE(Element, nullptr); + EXPECT_EQ(Reader, Reference); + EXPECT_EQ(Element->getLevel(), 4); + EXPECT_EQ(Element->getLineNumber(), 5); + EXPECT_EQ(Element->getName(), "CONSTANT"); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Reference: Missing TypeDefinition 'INTEGER' + std::tie(Reader, Element, Pass) = PassTable[1]; + EXPECT_NE(Reader, nullptr); + EXPECT_NE(Element, nullptr); + EXPECT_EQ(Reader, Reference); + EXPECT_EQ(Element->getLevel(), 3); + EXPECT_EQ(Element->getLineNumber(), 4); + EXPECT_EQ(Element->getName(), "INTEGER"); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Reference: Missing DebugLine + std::tie(Reader, Element, Pass) = PassTable[2]; + EXPECT_NE(Reader, nullptr); + EXPECT_NE(Element, nullptr); + EXPECT_EQ(Reader, Reference); + EXPECT_EQ(Element->getLevel(), 3); + EXPECT_EQ(Element->getLineNumber(), 8); + EXPECT_EQ(Element->getName(), ""); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Target: Added Variable 'CONSTANT' + std::tie(Reader, Element, Pass) = PassTable[3]; + EXPECT_NE(Reader, nullptr); + EXPECT_NE(Element, nullptr); + EXPECT_EQ(Reader, Target); + EXPECT_EQ(Element->getLevel(), 4); + EXPECT_EQ(Element->getLineNumber(), 5); + EXPECT_EQ(Element->getName(), "CONSTANT"); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added TypeDefinition 'INTEGER' + std::tie(Reader, Element, Pass) = PassTable[4]; + EXPECT_NE(Reader, nullptr); + EXPECT_NE(Element, nullptr); + EXPECT_EQ(Reader, Target); + EXPECT_EQ(Element->getLevel(), 4); + EXPECT_EQ(Element->getLineNumber(), 4); + EXPECT_EQ(Element->getName(), "INTEGER"); + EXPECT_EQ(Pass, LVComparePass::Added); +} + +// Logical elements properties. +void elementProperties(SmallString<128> &InputsDir) { + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setAttributeFormat(); + ReaderOptions.setAttributeFilename(); + ReaderOptions.setAttributeProducer(); + ReaderOptions.setAttributePublics(); + ReaderOptions.setAttributeRange(); + ReaderOptions.setAttributeLocation(); + ReaderOptions.setPrintAll(); + ReaderOptions.resolveDependencies(); + + std::vector Objects; + ScopedPrinter W(outs()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + + // Check logical elements properties. + LVReader *Reader = createReader(ReaderHandler, InputsDir, DwarfClang); + checkElementProperties(Reader); + ReaderHandler.deleteReader(Reader); +} + +// Logical elements selection. +void elementSelection(SmallString<128> &InputsDir) { + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setPrintAll(); + + ReaderOptions.setSelectIgnoreCase(); + ReaderOptions.setSelectUseRegex(); + + ReaderOptions.setReportList(); // Matched elements. + ReaderOptions.setReportView(); // Parents for matched elements. + + // Add patterns. + ReaderOptions.Select.Generic.insert("foo"); + ReaderOptions.Select.Generic.insert("movl[ \t]?%"); + ReaderOptions.Select.Generic.insert("INT[a-z]*"); + ReaderOptions.Select.Generic.insert("CONSTANT"); + + ReaderOptions.resolveDependencies(); + + std::vector Objects; + ScopedPrinter W(outs()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + + // Check logical elements selection. + LVReader *Reader = createReader(ReaderHandler, InputsDir, DwarfGcc); + checkElementSelection(Reader); + ReaderHandler.deleteReader(Reader); +} + +// Compare logical elements. +void compareElements(SmallString<128> &InputsDir) { + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setPrintLines(); + ReaderOptions.setPrintSymbols(); + ReaderOptions.setPrintTypes(); + ReaderOptions.setCompareLines(); + ReaderOptions.setCompareSymbols(); + ReaderOptions.setCompareTypes(); + + ReaderOptions.resolveDependencies(); + + std::vector Objects; + ScopedPrinter W(outs()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + + // Check logical comparison. + LVReader *Reference = createReader(ReaderHandler, InputsDir, DwarfClang); + LVReader *Target = createReader(ReaderHandler, InputsDir, DwarfGcc); + checkElementComparison(Reference, Target); + ReaderHandler.deleteReader(Reference); + ReaderHandler.deleteReader(Target); +} + +TEST(LogicalViewTest, ELFReader) { + // Initialize targets and assembly printers/parsers. + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + InitializeAllDisassemblers(); + + llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); + + SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0); + + // Logical elements general properties and selection. + elementProperties(InputsDir); + elementSelection(InputsDir); + + // Compare logical elements. + compareElements(InputsDir); +} + +} // namespace Index: llvm/unittests/DebugInfo/LogicalView/LocationRangesTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/LocationRangesTest.cpp @@ -0,0 +1,345 @@ +//===- llvm/unittest/DebugInfo/LogicalView/LocationRangesTest.cpp ---------===// +// +// 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 "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +namespace { + +class ReaderTestLocations : public LVReader { + // Types. + LVType *IntegerType = nullptr; + + // Scopes. + LVScope *NestedScope = nullptr; + LVScopeFunction *Function = nullptr; + + // Symbols. + LVSymbol *LocalVariable = nullptr; + LVSymbol *NestedVariable = nullptr; + LVSymbol *Parameter = nullptr; + + // Lines. + LVLine *LineOne = nullptr; + LVLine *LineTwo = nullptr; + LVLine *LineThree = nullptr; + LVLine *LineFour = nullptr; + LVLine *LineFive = nullptr; + LVLine *LineSix = nullptr; + + // Locations. + LVLocation *LocationOne = nullptr; + LVLocation *LocationTwo = nullptr; + LVLocation *LocationThree = nullptr; + LVLocation *LocationFour = nullptr; + LVLocation *LocationFive = nullptr; + LVLocation *LocationSix = nullptr; + +protected: + void add(LVScope *Parent, LVElement *Element); + template T *create() { + T *Element = new (std::nothrow) T(); + EXPECT_NE(Element, nullptr); + return Element; + } + void set(LVElement *Element, StringRef Name, LVOffset Offset, + uint32_t LineNumber = 0, LVElement *Type = nullptr); + void set(LVLocation *Location, LVLine *LowerLine, LVLine *UpperLine, + LVAddress LowerAddress, LVAddress UpperAddress); + void add(LVSymbol *Symbol, LVLine *LowerLine, LVLine *UpperLine); + +public: + ReaderTestLocations(ScopedPrinter &W) : LVReader("", "", W) { + setInstance(this); + } + + Error createScopes() { return LVReader::createScopes(); } + + void createElements(); + void addElements(); + void initElements(); +}; + +// Helper function to add a logical element to a given scope. +void ReaderTestLocations::add(LVScope *Parent, LVElement *Child) { + Parent->addElement(Child); + EXPECT_EQ(Child->getParent(), Parent); + EXPECT_EQ(Child->getLevel(), Parent->getLevel() + 1); +} + +// Helper function to set the initial values for a given logical element. +void ReaderTestLocations::set(LVElement *Element, StringRef Name, + LVOffset Offset, uint32_t LineNumber, + LVElement *Type) { + Element->setName(Name); + Element->setOffset(Offset); + Element->setLineNumber(LineNumber); + Element->setType(Type); + EXPECT_EQ(Element->getName(), Name); + EXPECT_EQ(Element->getOffset(), Offset); + EXPECT_EQ(Element->getLineNumber(), LineNumber); + EXPECT_EQ(Element->getType(), Type); +} + +// Helper function to set the initial values for a given logical location. +void ReaderTestLocations::set(LVLocation *Location, LVLine *LowerLine, + LVLine *UpperLine, LVAddress LowerAddress, + LVAddress UpperAddress) { + Location->setLowerLine(LowerLine); + Location->setUpperLine(UpperLine); + Location->setLowerAddress(LowerAddress); + Location->setUpperAddress(UpperAddress); + EXPECT_EQ(Location->getLowerLine(), LowerLine); + EXPECT_EQ(Location->getUpperLine(), UpperLine); + EXPECT_EQ(Location->getLowerAddress(), LowerAddress); + EXPECT_EQ(Location->getUpperAddress(), UpperAddress); +} + +// Helper function to add a logical location to a logical symbol. +void ReaderTestLocations::add(LVSymbol *Symbol, LVLine *LowerLine, + LVLine *UpperLine) { + dwarf::Attribute Attr = dwarf::DW_AT_location; + + Symbol->addLocation(Attr, LowerLine->getAddress(), UpperLine->getAddress(), + /*SectionOffset=*/0, /*LocDesOffset=*/0); +} + +// Create the logical elements. +void ReaderTestLocations::createElements() { + // Create scope root. + Error Err = createScopes(); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + Root = getScopesRoot(); + EXPECT_NE(Root, nullptr); + + // Create the logical types. + IntegerType = create(); + + // Create the logical scopes. + NestedScope = create(); + CompileUnit = create(); + Function = create(); + + // Create the logical symbols. + LocalVariable = create(); + NestedVariable = create(); + Parameter = create(); + + // Create the logical lines. + LineOne = create(); + LineTwo = create(); + LineThree = create(); + LineFour = create(); + LineFive = create(); + LineSix = create(); + + // Create the logical locations. + LocationOne = create(); + LocationTwo = create(); + LocationThree = create(); + LocationFour = create(); + LocationFive = create(); + LocationSix = create(); +} + +// Create the logical view adding the created logical elements. +void ReaderTestLocations::addElements() { + setCompileUnit(CompileUnit); + + // Root + // CompileUnit + // IntegerType + // Function + // LocationOne + // LocationTwo + // LocationFive + // LocationSix + // Parameter + // LocalVariable + // LineOne + // LineTwo + // NestedScope + // LocationThree + // LocationFour + // NestedVariable + // LineThree + // LineFour + // LineFive + // LineSix + + // Add elements to Root. + add(Root, CompileUnit); + + // Add elements to CompileUnit. + add(CompileUnit, IntegerType); + add(CompileUnit, Function); + + // Add elements to Function. + add(Function, Parameter); + add(Function, LocalVariable); + add(Function, LineOne); + add(Function, LineTwo); + add(Function, LineFive); + add(Function, LineSix); + add(Function, NestedScope); + + // Add elements to NestedScope. + add(NestedScope, NestedVariable); + add(NestedScope, LineThree); + add(NestedScope, LineFour); +} + +// Set initial values to logical elements. +void ReaderTestLocations::initElements() { + // Types. + set(IntegerType, "int", 0x1000); + + // Scopes. + set(CompileUnit, "foo.cpp", 0x2000); + set(Function, "foo", 0x2010, 100, IntegerType); + set(NestedScope, "", 0x2020, 300); + + // Symbols. + set(Parameter, "Param", 0x3000, 110, IntegerType); + set(LocalVariable, "LocalVariable", 0x3000, 120, IntegerType); + set(NestedVariable, "NestedVariable", 0x3010, 310, IntegerType); + + // Lines. + set(LineOne, "", 0x5000, 100); + set(LineTwo, "", 0x5200, 200); + set(LineThree, "", 0x5400, 300); + set(LineFour, "", 0x5600, 400); + set(LineFive, "", 0x5800, 500); + set(LineSix, "", 0x6000, 600); + + // Locations. + set(LocationOne, LineOne, LineOne, 0x5000, 0x5100); + EXPECT_STREQ(LocationOne->getIntervalInfo().c_str(), + " Lines 100:100 [0x0000005000:0x0000005100]"); + + set(LocationTwo, LineTwo, LineTwo, 0x5200, 0x5300); + EXPECT_STREQ(LocationTwo->getIntervalInfo().c_str(), + " Lines 200:200 [0x0000005200:0x0000005300]"); + + set(LocationThree, LineThree, LineThree, 0x5400, 0x5500); + EXPECT_STREQ(LocationThree->getIntervalInfo().c_str(), + " Lines 300:300 [0x0000005400:0x0000005500]"); + + set(LocationFour, LineFour, LineFour, 0x5600, 0x5700); + LocationFour->setIsAddressRange(); + EXPECT_STREQ(LocationFour->getIntervalInfo().c_str(), + "{Range} Lines 400:400 [0x0000005600:0x0000005700]"); + + set(LocationFive, LineFive, LineFive, 0x5800, 0x5900); + LocationFive->setIsAddressRange(); + EXPECT_STREQ(LocationFive->getIntervalInfo().c_str(), + "{Range} Lines 500:500 [0x0000005800:0x0000005900]"); + + set(LocationSix, LineSix, LineSix, 0x6000, 0x6100); + LocationSix->setIsAddressRange(); + EXPECT_STREQ(LocationSix->getIntervalInfo().c_str(), + "{Range} Lines 600:600 [0x0000006000:0x0000006100]"); + + // Add ranges to Function. + // Function: LocationOne, LocationTwo, LocationFive, LocationSix + Function->addObject(LocationOne); + Function->addObject(LocationTwo); + Function->addObject(LocationFive); + Function->addObject(LocationSix); + EXPECT_EQ(Function->rangeCount(), 4); + + // Add ranges to NestedScope. + // NestedScope: LocationThree, LocationFour + NestedScope->addObject(LocationThree); + NestedScope->addObject(LocationFour); + EXPECT_EQ(NestedScope->rangeCount(), 2); + + // Get all ranges. + LVRange Ranges; + CompileUnit->getRanges(Ranges); + Ranges.startSearch(); + EXPECT_EQ(Ranges.getEntry(0x4000), nullptr); + + EXPECT_EQ(Ranges.getEntry(0x5060), Function); + EXPECT_EQ(Ranges.getEntry(0x5850), Function); + EXPECT_EQ(Ranges.getEntry(0x5010, 0x5090), Function); + EXPECT_EQ(Ranges.getEntry(0x5210, 0x5290), Function); + EXPECT_EQ(Ranges.getEntry(0x5810, 0x5890), Function); + EXPECT_EQ(Ranges.getEntry(0x6010, 0x6090), Function); + + EXPECT_EQ(Ranges.getEntry(0x5400), NestedScope); + EXPECT_EQ(Ranges.getEntry(0x5650), NestedScope); + EXPECT_EQ(Ranges.getEntry(0x5410, 0x5490), NestedScope); + EXPECT_EQ(Ranges.getEntry(0x5610, 0x5690), NestedScope); + + EXPECT_EQ(Ranges.getEntry(0x8000), nullptr); + Ranges.endSearch(); + + // Add locations to symbols. + // Parameter: [LineOne, LineSix] + // LocalVariable: [LineTwo, LineSix], [LineFour, LineFive] + // NestedVariable: [LineThree, LineFour] + add(Parameter, LineOne, LineSix); + add(LocalVariable, LineTwo, LineSix); + add(LocalVariable, LineFour, LineFive); + add(NestedVariable, LineThree, LineFour); + + LVLocation *Location; + LVLocations Locations; + Parameter->getLocations(Locations); + ASSERT_EQ(Locations.size(), 1); + Location = Locations[0]; + EXPECT_EQ(Location->getLowerAddress(), LineOne->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineSix->getAddress()); + + Locations.clear(); + LocalVariable->getLocations(Locations); + ASSERT_EQ(Locations.size(), 2); + Location = Locations[0]; + EXPECT_EQ(Location->getLowerAddress(), LineTwo->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineSix->getAddress()); + Location = Locations[1]; + EXPECT_EQ(Location->getLowerAddress(), LineFour->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineFive->getAddress()); + + Locations.clear(); + NestedVariable->getLocations(Locations); + ASSERT_EQ(Locations.size(), 1); + Location = Locations[0]; + EXPECT_EQ(Location->getLowerAddress(), LineThree->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineFour->getAddress()); +} + +TEST(LogicalViewTest, LocationRanges) { + ScopedPrinter W(outs()); + ReaderTestLocations Reader(W); + + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setPrintAll(); + ReaderOptions.resolveDependencies(); + options().setOptions(&ReaderOptions); + + Reader.createElements(); + Reader.addElements(); + Reader.initElements(); +} + +} // namespace Index: llvm/unittests/DebugInfo/LogicalView/LogicalElementsTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/LogicalElementsTest.cpp @@ -0,0 +1,342 @@ +//===- llvm/unittest/DebugInfo/LogicalView/LogicalElementsTest.cpp --------===// +// +// 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 "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +namespace { + +class ReaderTestElements : public LVReader { + // Types. + LVType *IntegerType = nullptr; + LVType *UnsignedType = nullptr; + LVType *GlobalType = nullptr; + LVType *LocalType = nullptr; + LVType *NestedType = nullptr; + LVTypeDefinition *TypeDefinitionOne = nullptr; + LVTypeDefinition *TypeDefinitionTwo = nullptr; + LVTypeEnumerator *EnumeratorOne = nullptr; + LVTypeEnumerator *EnumeratorTwo = nullptr; + LVTypeImport *TypeImport = nullptr; + LVTypeParam *TypeParam = nullptr; + LVTypeSubrange *TypeSubrange = nullptr; + + // Scopes. + LVScope *NestedScope = nullptr; + LVScopeAggregate *Aggregate = nullptr; + LVScopeArray *Array = nullptr; + LVScopeEnumeration *Enumeration = nullptr; + LVScopeFunction *Function = nullptr; + LVScopeFunction *ClassFunction = nullptr; + LVScopeFunctionInlined *InlinedFunction = nullptr; + LVScopeNamespace *Namespace = nullptr; + + // Symbols. + LVSymbol *GlobalVariable = nullptr; + LVSymbol *LocalVariable = nullptr; + LVSymbol *ClassMember = nullptr; + LVSymbol *NestedVariable = nullptr; + LVSymbol *Parameter = nullptr; + + // Lines. + LVLine *LocalLine = nullptr; + LVLine *NestedLine = nullptr; + +protected: + void add(LVScope *Parent, LVElement *Element); + template T *create() { + T *Element = new (std::nothrow) T(); + EXPECT_NE(Element, nullptr); + return Element; + } + void set(LVElement *Element, StringRef Name, LVOffset Offset, + uint32_t LineNumber = 0, LVElement *Type = nullptr); + +public: + ReaderTestElements(ScopedPrinter &W) : LVReader("", "", W) { + setInstance(this); + } + + Error createScopes() { return LVReader::createScopes(); } + Error printScopes() { return LVReader::printScopes(); } + + void createElements(); + void addElements(); + void initElements(); +}; + +// Helper function to add a logical element to a given scope. +void ReaderTestElements::add(LVScope *Parent, LVElement *Child) { + Parent->addElement(Child); + EXPECT_EQ(Child->getParent(), Parent); + EXPECT_EQ(Child->getLevel(), Parent->getLevel() + 1); +} + +// Helper function to set the initial values for a given logical element. +void ReaderTestElements::set(LVElement *Element, StringRef Name, + LVOffset Offset, uint32_t LineNumber, + LVElement *Type) { + Element->setName(Name); + Element->setOffset(Offset); + Element->setLineNumber(LineNumber); + Element->setType(Type); + EXPECT_EQ(Element->getName(), Name); + EXPECT_EQ(Element->getOffset(), Offset); + EXPECT_EQ(Element->getLineNumber(), LineNumber); + EXPECT_EQ(Element->getType(), Type); +} + +// Create the logical elements. +void ReaderTestElements::createElements() { + // Create scope root. + Error Err = createScopes(); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + Root = getScopesRoot(); + EXPECT_NE(Root, nullptr); + + // Create the logical types. + IntegerType = create(); + UnsignedType = create(); + GlobalType = create(); + LocalType = create(); + NestedType = create(); + EnumeratorOne = create(); + EnumeratorTwo = create(); + TypeDefinitionOne = create(); + TypeDefinitionTwo = create(); + TypeSubrange = create(); + TypeParam = create(); + TypeImport = create(); + + // Create the logical scopes. + NestedScope = create(); + Aggregate = create(); + Array = create(); + CompileUnit = create(); + Enumeration = create(); + Function = create(); + ClassFunction = create(); + InlinedFunction = create(); + Namespace = create(); + + // Create the logical symbols. + GlobalVariable = create(); + LocalVariable = create(); + ClassMember = create(); + NestedVariable = create(); + Parameter = create(); + + // Create the logical lines. + LocalLine = create(); + NestedLine = create(); +} + +// Create the logical view adding the created logical elements. +void ReaderTestElements::addElements() { + setCompileUnit(CompileUnit); + + // Root + // CompileUnit + // IntegerType + // UnsignedType + // Array + // TypeSubrange + // Function + // Parameter + // LocalVariable + // LocalType + // LocalLine + // InlinedFunction + // TypeImport + // TypeParam + // NestedScope + // NestedVariable + // NestedType + // NestedLine + // GlobalVariable + // GlobalType + // Namespace + // Aggregate + // ClassMember + // ClassFunction + // Enumeration + // EnumeratorOne + // EnumeratorTwo + // TypeDefinitionOne + // TypeDefinitionTwo + + add(Root, CompileUnit); + EXPECT_EQ(Root->lineCount(), 0); + EXPECT_EQ(Root->scopeCount(), 1); + EXPECT_EQ(Root->symbolCount(), 0); + EXPECT_EQ(Root->typeCount(), 0); + + // Add elements to CompileUnit. + add(CompileUnit, IntegerType); + add(CompileUnit, UnsignedType); + add(CompileUnit, Array); + add(CompileUnit, Function); + add(CompileUnit, GlobalVariable); + add(CompileUnit, GlobalType); + add(CompileUnit, Namespace); + EXPECT_EQ(CompileUnit->lineCount(), 0); + EXPECT_EQ(CompileUnit->scopeCount(), 3); + EXPECT_EQ(CompileUnit->symbolCount(), 1); + EXPECT_EQ(CompileUnit->typeCount(), 3); + + // Add elements to Namespace. + add(Namespace, Aggregate); + add(Namespace, Enumeration); + add(Namespace, TypeDefinitionOne); + add(Namespace, TypeDefinitionTwo); + EXPECT_EQ(Namespace->lineCount(), 0); + EXPECT_EQ(Namespace->scopeCount(), 2); + EXPECT_EQ(Namespace->symbolCount(), 0); + EXPECT_EQ(Namespace->typeCount(), 2); + + // Add elements to Function. + add(Function, Parameter); + add(Function, LocalVariable); + add(Function, LocalType); + add(Function, LocalLine); + add(Function, InlinedFunction); + add(Function, TypeImport); + add(Function, TypeParam); + add(Function, NestedScope); + EXPECT_EQ(Function->lineCount(), 1); + EXPECT_EQ(Function->scopeCount(), 2); + EXPECT_EQ(Function->symbolCount(), 2); + EXPECT_EQ(Function->typeCount(), 3); + + // Add elements to NestedScope. + add(NestedScope, NestedVariable); + add(NestedScope, NestedType); + add(NestedScope, NestedLine); + EXPECT_EQ(NestedScope->lineCount(), 1); + EXPECT_EQ(NestedScope->scopeCount(), 0); + EXPECT_EQ(NestedScope->symbolCount(), 1); + EXPECT_EQ(NestedScope->typeCount(), 1); + + // Add elements to Enumeration. + add(Enumeration, EnumeratorOne); + add(Enumeration, EnumeratorTwo); + EXPECT_EQ(Enumeration->lineCount(), 0); + EXPECT_EQ(Enumeration->scopeCount(), 0); + EXPECT_EQ(Enumeration->symbolCount(), 0); + EXPECT_EQ(Enumeration->typeCount(), 2); + + // Add elements to Aggregate. + add(Aggregate, ClassMember); + add(Aggregate, ClassFunction); + EXPECT_EQ(Aggregate->lineCount(), 0); + EXPECT_EQ(Aggregate->scopeCount(), 1); + EXPECT_EQ(Aggregate->symbolCount(), 1); + EXPECT_EQ(Aggregate->typeCount(), 0); + + // Add elements to Array. + add(Array, TypeSubrange); + EXPECT_EQ(Array->lineCount(), 0); + EXPECT_EQ(Array->scopeCount(), 0); + EXPECT_EQ(Array->symbolCount(), 0); + EXPECT_EQ(Array->typeCount(), 1); +} + +// Set initial values to logical elements. +void ReaderTestElements::initElements() { + setFilename("LogicalElements.obj"); + EXPECT_EQ(getFilename(), "LogicalElements.obj"); + + Root->setFileFormatName("FileFormat"); + EXPECT_EQ(Root->getFileFormatName(), "FileFormat"); + + // Types. + set(IntegerType, "int", 0x1000); + set(UnsignedType, "unsigned", 0x1010); + set(GlobalType, "GlobalType", 0x1020, 1020); + set(LocalType, "LocalType", 0x1030, 1030); + set(NestedType, "NestedType", 0x1040, 1040); + + set(TypeDefinitionOne, "INTEGER", 0x1040, 1040, IntegerType); + set(TypeDefinitionTwo, "INT", 0x1050, 1050, TypeDefinitionOne); + EXPECT_EQ(TypeDefinitionOne->getUnderlyingType(), IntegerType); + EXPECT_EQ(TypeDefinitionTwo->getUnderlyingType(), IntegerType); + + set(EnumeratorOne, "one", 0x1060, 1060); + EnumeratorOne->setValue("blue"); + EXPECT_EQ(EnumeratorOne->getValue(), "blue"); + + set(EnumeratorTwo, "two", 0x1070, 1070); + EnumeratorTwo->setValue("red"); + EXPECT_EQ(EnumeratorTwo->getValue(), "red"); + + set(TypeSubrange, "", 0x1080, 1080, IntegerType); + TypeSubrange->setCount(5); + EXPECT_EQ(TypeSubrange->getCount(), 5); + + TypeSubrange->setLowerBound(10); + TypeSubrange->setUpperBound(15); + EXPECT_EQ(TypeSubrange->getLowerBound(), 10); + EXPECT_EQ(TypeSubrange->getUpperBound(), 15); + + TypeSubrange->setBounds(20, 25); + std::pair Pair; + Pair = TypeSubrange->getBounds(); + EXPECT_EQ(Pair.first, 20); + EXPECT_EQ(Pair.second, 25); + + set(TypeParam, "INTEGER", 0x1090, 1090, UnsignedType); + TypeParam->setValue("10"); + EXPECT_EQ(TypeParam->getValue(), "10"); + + set(TypeImport, "", 0x1090, 1090, Aggregate); + EXPECT_EQ(TypeImport->getType(), Aggregate); + + // Scopes. + set(Aggregate, "Class", 0x2000, 2000); + set(Enumeration, "Colors", 0x2010, 2010); + set(Function, "function", 0x2020, 2020, GlobalType); + set(ClassFunction, "foo", 0x2030, 2030, TypeDefinitionTwo); + set(Namespace, "nsp", 0x2040, 2040); + set(NestedScope, "", 0x2050, 2050); + set(Array, "", 0x2060, 2060, UnsignedType); + set(InlinedFunction, "bar", 0x2070, 2070, TypeDefinitionOne); + set(CompileUnit, "test.cpp", 0x2080, 2080); + + // Symbols. + set(GlobalVariable, "GlobalVariable", 0x3000, 3000); + set(LocalVariable, "LocalVariable", 0x3010, 3010, TypeDefinitionOne); + set(ClassMember, "Member", 0x3020, 3020, IntegerType); + set(Parameter, "Param", 0x3030, 3030, UnsignedType); + set(NestedVariable, "NestedVariable", 0x3040, 3040); + + // Lines. + set(LocalLine, "", 0x4000, 4000); + set(NestedLine, "", 0x4010, 4010); +} + +TEST(LogicalViewTest, LogicalElements) { + ScopedPrinter W(outs()); + ReaderTestElements Reader(W); + + Reader.createElements(); + Reader.addElements(); + Reader.initElements(); +} + +} // namespace Index: llvm/unittests/DebugInfo/LogicalView/SelectElementsTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/SelectElementsTest.cpp @@ -0,0 +1,399 @@ +//===- llvm/unittest/DebugInfo/LogicalView/SelectElementsTest.cpp ---------===// +// +// 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 "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +namespace { + +class ReaderTestSelection : public LVReader { + // Types. + LVType *IntegerType = nullptr; + + // Scopes. + LVScope *NestedScope = nullptr; + LVScopeAggregate *Aggregate = nullptr; + LVScopeFunction *Function = nullptr; + LVScopeNamespace *Namespace = nullptr; + + // Symbols. + LVSymbol *ClassMember = nullptr; + LVSymbol *LocalVariable = nullptr; + LVSymbol *NestedVariable = nullptr; + LVSymbol *Parameter = nullptr; + + // Lines. + LVLine *LineOne = nullptr; + LVLine *LineTwo = nullptr; + LVLine *LineThree = nullptr; + LVLine *LineFour = nullptr; + LVLine *LineFive = nullptr; + +protected: + void add(LVScope *Parent, LVElement *Element); + template T *create(F Function) { + // 'Function' will update a specific kind of the logical element to + // have the ability of kind selection. + T *Element = new (std::nothrow) T(); + EXPECT_NE(Element, nullptr); + (Element->*Function)(); + return Element; + } + void set(LVElement *Element, StringRef Name, LVOffset Offset, + uint32_t LineNumber = 0, LVElement *Type = nullptr); + +public: + ReaderTestSelection(ScopedPrinter &W) : LVReader("", "", W) { + setInstance(this); + } + + Error createScopes() { return LVReader::createScopes(); } + + void createElements(); + void addElements(); + void initElements(); + void resolvePatterns(LVPatterns &Patterns); + void checkFlexiblePatterns(); + void checkGenericPatterns(); + void checkKindPatterns(); +}; + +// Helper function to add a logical element to a given scope. +void ReaderTestSelection::add(LVScope *Parent, LVElement *Child) { + Parent->addElement(Child); + EXPECT_EQ(Child->getParent(), Parent); + EXPECT_EQ(Child->getLevel(), Parent->getLevel() + 1); +} + +// Helper function to set the initial values for a given logical element. +void ReaderTestSelection::set(LVElement *Element, StringRef Name, + LVOffset Offset, uint32_t LineNumber, + LVElement *Type) { + Element->setName(Name); + Element->setOffset(Offset); + Element->setLineNumber(LineNumber); + Element->setType(Type); + EXPECT_EQ(Element->getName(), Name); + EXPECT_EQ(Element->getOffset(), Offset); + EXPECT_EQ(Element->getLineNumber(), LineNumber); + EXPECT_EQ(Element->getType(), Type); +} + +// Create the logical elements. +void ReaderTestSelection::createElements() { + // Create scope root. + Error Err = createScopes(); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + Root = getScopesRoot(); + EXPECT_NE(Root, nullptr); + + // Create the logical types. + IntegerType = create(&LVType::setIsBase); + + // Create the logical scopes. + CompileUnit = create( + &LVScope::setIsCompileUnit); + Function = + create(&LVScope::setIsFunction); + NestedScope = + create(&LVScope::setIsLexicalBlock); + Namespace = + create(&LVScope::setIsNamespace); + Aggregate = + create(&LVScope::setIsAggregate); + + // Create the logical symbols. + ClassMember = create(&LVSymbol::setIsMember); + LocalVariable = + create(&LVSymbol::setIsVariable); + NestedVariable = + create(&LVSymbol::setIsVariable); + Parameter = create(&LVSymbol::setIsParameter); + + // Create the logical lines. + LineOne = create(&LVLine::setIsLineDebug); + LineTwo = create(&LVLine::setIsBasicBlock); + LineThree = create(&LVLine::setIsNewStatement); + LineFour = create(&LVLine::setIsPrologueEnd); + LineFive = create(&LVLine::setIsLineAssembler); +} + +// Create the logical view adding the created logical elements. +void ReaderTestSelection::addElements() { + setCompileUnit(CompileUnit); + + // Root + // CompileUnit + // IntegerType + // Namespace + // Aggregate + // ClassMember + // Function + // Parameter + // LocalVariable + // LineOne + // LineTwo + // NestedScope + // NestedVariable + // LineThree + // LineFour + // LineFive + + // Add elements to Root. + add(Root, CompileUnit); + + // Add elements to CompileUnit. + add(CompileUnit, IntegerType); + add(CompileUnit, Namespace); + add(CompileUnit, Function); + + // Add elements to Namespace. + add(Namespace, Aggregate); + + // Add elements to Function. + add(Function, Parameter); + add(Function, LocalVariable); + add(Function, LineOne); + add(Function, LineTwo); + add(Function, LineFive); + add(Function, NestedScope); + + // Add elements to Aggregate. + add(Aggregate, ClassMember); + + // Add elements to NestedScope. + add(NestedScope, NestedVariable); + add(NestedScope, LineThree); + add(NestedScope, LineFour); +} + +void ReaderTestSelection::resolvePatterns(LVPatterns &Patterns) { + // Traverse the given scope and its children applying the pattern match. + // Before applying the pattern, reset previous matched state. + std::function TraverseScope = [&](LVScope *Parent) { + auto Traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) { + Entry->resetIsMatched(); + Patterns.resolvePatternMatch(Entry); + } + }; + + Parent->resetIsMatched(); + Patterns.resolvePatternMatch(Parent); + + Traverse(Parent->getSymbols()); + Traverse(Parent->getTypes()); + Traverse(Parent->getLines()); + + if (const LVScopes *Scopes = Parent->getScopes()) + for (LVScope *Scope : *Scopes) { + Scope->resetIsMatched(); + Patterns.resolvePatternMatch(Scope); + TraverseScope(Scope); + } + }; + + // Start traversing the scopes root and apply any matching pattern. + TraverseScope(Root); +} + +// Set initial values to logical elements. +void ReaderTestSelection::initElements() { + // Types. + set(IntegerType, "int", 0x1000); + + // Scopes. + set(CompileUnit, "test.cpp", 0x2000); + set(Namespace, "anyNamespace", 0x3000, 300); + set(Aggregate, "anyClass", 0x4000, 400); + set(Function, "anyFunction", 0x5000, 500, IntegerType); + set(NestedScope, "", 0x6000, 600); + + // Symbols. + set(Parameter, "Param", 0x5100, 510, IntegerType); + set(LocalVariable, "LocalVariable", 0x5200, 520, IntegerType); + set(NestedVariable, "NestedVariable", 0x6200, 620, IntegerType); + + // Lines. + set(LineOne, "", 0x5110, 511); + set(LineTwo, "", 0x5210, 521); + set(LineThree, "", 0x6110, 611); + set(LineFour, "", 0x6210, 621); + set(LineFive, "", 0x7110, 711); +} + +// Check logical elements kind patterns. +void ReaderTestSelection::checkKindPatterns() { + // Add patterns. + LVPatterns &Patterns = patterns(); + Patterns.clear(); + + LVElementKindSet KindElements; // --select-elements= + LVLineKindSet KindLines; // --select-lines= + LVScopeKindSet KindScopes; // --select-scopes= + LVSymbolKindSet KindSymbols; // --select-symbols= + LVTypeKindSelection KindTypes; // --select-types= + + KindElements.insert(LVElementKind::Global); + KindLines.insert(LVLineKind::IsLineDebug); + KindLines.insert(LVLineKind::IsNewStatement); + KindLines.insert(LVLineKind::IsLineAssembler); + KindScopes.insert(LVScopeKind::IsLexicalBlock); + KindSymbols.insert(LVSymbolKind::IsMember); + KindSymbols.insert(LVSymbolKind::IsParameter); + KindTypes.insert(LVTypeKind::IsBase); + + // Add requests based on the element kind. + Patterns.addRequest(KindElements); + Patterns.addRequest(KindLines); + Patterns.addRequest(KindScopes); + Patterns.addRequest(KindSymbols); + Patterns.addRequest(KindTypes); + + // Apply the collected patterns. + resolvePatterns(Patterns); + + EXPECT_FALSE(CompileUnit->getIsMatched()); + EXPECT_FALSE(Namespace->getIsMatched()); + EXPECT_FALSE(Aggregate->getIsMatched()); + EXPECT_FALSE(Function->getIsMatched()); + EXPECT_TRUE(NestedScope->getIsMatched()); + + EXPECT_TRUE(IntegerType->getIsMatched()); + + EXPECT_TRUE(ClassMember->getIsMatched()); + EXPECT_TRUE(Parameter->getIsMatched()); + EXPECT_FALSE(LocalVariable->getIsMatched()); + EXPECT_FALSE(NestedVariable->getIsMatched()); + + EXPECT_TRUE(LineOne->getIsMatched()); + EXPECT_FALSE(LineTwo->getIsMatched()); + EXPECT_TRUE(LineThree->getIsMatched()); + EXPECT_FALSE(LineFour->getIsMatched()); + EXPECT_TRUE(LineFive->getIsMatched()); +} + +// Check logical elements generic patterns (Case sensitive). +void ReaderTestSelection::checkGenericPatterns() { + // Add patterns. + LVPatterns &Patterns = patterns(); + Patterns.clear(); + + StringSet<> Generic; // --select= + Generic.insert(Function->getName()); // anyFunction + Generic.insert(Namespace->getName()); // anyNamespace + Generic.insert(LocalVariable->getName()); // LocalVariable + + LVOffsetSet Offsets; // --select-offset= + Offsets.insert(IntegerType->getOffset()); + Offsets.insert(LineOne->getOffset()); + Offsets.insert(LineTwo->getOffset()); + + // Add requests based on the generic string and offset. + Patterns.addGenericPatterns(Generic); + Patterns.addOffsetPatterns(Offsets); + + // Apply the collected patterns. + resolvePatterns(Patterns); + + EXPECT_FALSE(CompileUnit->getIsMatched()); + EXPECT_TRUE(Namespace->getIsMatched()); + EXPECT_FALSE(Aggregate->getIsMatched()); + EXPECT_TRUE(Function->getIsMatched()); + EXPECT_FALSE(NestedScope->getIsMatched()); + + EXPECT_TRUE(IntegerType->getIsMatched()); + + EXPECT_FALSE(ClassMember->getIsMatched()); + EXPECT_FALSE(Parameter->getIsMatched()); + EXPECT_TRUE(LocalVariable->getIsMatched()); + EXPECT_FALSE(NestedVariable->getIsMatched()); + + EXPECT_TRUE(LineOne->getIsMatched()); + EXPECT_TRUE(LineTwo->getIsMatched()); + EXPECT_FALSE(LineThree->getIsMatched()); + EXPECT_FALSE(LineFour->getIsMatched()); + EXPECT_FALSE(LineFive->getIsMatched()); +} + +// Check logical elements flexible patterns (case insensitive, RegEx). +void ReaderTestSelection::checkFlexiblePatterns() { + options().setSelectIgnoreCase(); + options().setSelectUseRegex(); + + // Add patterns. + LVPatterns &Patterns = patterns(); + Patterns.clear(); + + StringSet<> Generic; // --select= + Generic.insert("function"); + Generic.insert("NaMeSpAcE"); + Generic.insert("[a-z]*Variable"); + Generic.insert("[0-9]21"); + + // Add requests based on the flexible string. + Patterns.addGenericPatterns(Generic); + + // Apply the collected patterns. + resolvePatterns(Patterns); + + EXPECT_FALSE(CompileUnit->getIsMatched()); + EXPECT_TRUE(Namespace->getIsMatched()); // anyNamespace + EXPECT_FALSE(Aggregate->getIsMatched()); + EXPECT_TRUE(Function->getIsMatched()); // anyFunction + EXPECT_FALSE(NestedScope->getIsMatched()); + + EXPECT_FALSE(IntegerType->getIsMatched()); + + EXPECT_FALSE(ClassMember->getIsMatched()); + EXPECT_FALSE(Parameter->getIsMatched()); + EXPECT_TRUE(LocalVariable->getIsMatched()); // LocalVariable + EXPECT_TRUE(NestedVariable->getIsMatched()); // NestedVariable + + EXPECT_FALSE(LineOne->getIsMatched()); + EXPECT_TRUE(LineTwo->getIsMatched()); // 521 + EXPECT_FALSE(LineThree->getIsMatched()); + EXPECT_TRUE(LineFour->getIsMatched()); // 621 + EXPECT_FALSE(LineFive->getIsMatched()); +} + +TEST(LogicalViewTest, SelectElements) { + ScopedPrinter W(outs()); + ReaderTestSelection Reader(W); + + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setPrintAll(); + ReaderOptions.setReportList(); + ReaderOptions.setReportAnyView(); + + ReaderOptions.resolveDependencies(); + options().setOptions(&ReaderOptions); + + Reader.createElements(); + Reader.addElements(); + Reader.initElements(); + Reader.checkKindPatterns(); + Reader.checkGenericPatterns(); + Reader.checkFlexiblePatterns(); +} + +} // namespace Index: llvm/unittests/DebugInfo/LogicalView/StringPoolTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/StringPoolTest.cpp @@ -0,0 +1,60 @@ +//===- llvm/unittest/DebugInfo/LogicalView/StringPoolTest.cpp -------------===// +// +// 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 "llvm/DebugInfo/LogicalView/Core/LVStringPool.h" +#include + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +namespace { + +TEST(StringPoolTest, AddStrings) { + LVStringPool PoolInstance; + EXPECT_EQ(0u, PoolInstance.getSize()); + + // Get indexes for the initial strings. + EXPECT_EQ(1u, PoolInstance.getIndex("one")); + EXPECT_EQ(2u, PoolInstance.getIndex("two")); + EXPECT_EQ(3u, PoolInstance.getIndex("three")); + EXPECT_EQ(4u, PoolInstance.getIndex("four")); + EXPECT_EQ(5u, PoolInstance.getIndex("five")); + EXPECT_EQ(5u, PoolInstance.getSize()); + + // Verify the string returned by the given index. + EXPECT_EQ("one", PoolInstance.getString(1)); + EXPECT_EQ("two", PoolInstance.getString(2)); + EXPECT_EQ("three", PoolInstance.getString(3)); + EXPECT_EQ("four", PoolInstance.getString(4)); + EXPECT_EQ("five", PoolInstance.getString(5)); + EXPECT_EQ(5u, PoolInstance.getSize()); + + // Get indexes for the same initial strings. + EXPECT_EQ(5u, PoolInstance.getIndex("five")); + EXPECT_EQ(4u, PoolInstance.getIndex("four")); + EXPECT_EQ(3u, PoolInstance.getIndex("three")); + EXPECT_EQ(2u, PoolInstance.getIndex("two")); + EXPECT_EQ(1u, PoolInstance.getIndex("one")); + EXPECT_EQ(5u, PoolInstance.getSize()); + + // Empty string gets the index zero. + EXPECT_EQ(0u, PoolInstance.getIndex("")); + EXPECT_EQ(5u, PoolInstance.getSize()); + + // Empty string for invalid index. + EXPECT_EQ("", PoolInstance.getString(620)); + + // Lookup for strings + EXPECT_EQ(5u, PoolInstance.findIndex("five")); + EXPECT_TRUE(PoolInstance.isValidIndex(PoolInstance.findIndex("five"))); + EXPECT_FALSE(PoolInstance.isValidIndex(PoolInstance.findIndex("FIVE"))); +} + +} // namespace Index: llvm/unittests/DebugInfo/LogicalView/WarningInternalTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/WarningInternalTest.cpp @@ -0,0 +1,558 @@ +//===- llvm/unittest/DebugInfo/LogicalView/WarningInternalTest.cpp --------===// +// +// 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 "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +namespace { + +class MyLocation; + +// This code emulates the work done by the Readers when processing the +// binary files and the creation of the AddressToLine mapping is done +// automatically, using the text sections. +class MyAddressToLine { + using LVAddressToLine = std::map; + LVAddressToLine AddressToLineData; + +public: + MyAddressToLine() = default; + + void insert(LVLine *Line) { + AddressToLineData.emplace(Line->getOffset(), Line); + } + + LVLine *lineLowerBound(LVAddress Address); + LVLine *lineUpperBound(LVAddress Address); +}; + +LVLine *MyAddressToLine::lineLowerBound(LVAddress Address) { + LVAddressToLine::const_iterator Iter = AddressToLineData.lower_bound(Address); + return (Iter != AddressToLineData.end()) ? Iter->second : nullptr; +} + +LVLine *MyAddressToLine::lineUpperBound(LVAddress Address) { + if (AddressToLineData.empty()) + return nullptr; + LVAddressToLine::const_iterator Iter = AddressToLineData.upper_bound(Address); + if (Iter != AddressToLineData.begin()) + Iter = std::prev(Iter); + return Iter->second; +} + +MyAddressToLine AddressToLine; + +class ReaderTestWarningInternal : public LVReader { + // Types. + LVType *IntegerType = nullptr; + + // Scopes. + LVScope *NestedScope = nullptr; + LVScopeFunction *Function = nullptr; + + // Symbols. + LVSymbol *LocalVariable = nullptr; + LVSymbol *NestedVariable = nullptr; + LVSymbol *Parameter = nullptr; + + // Lines. + LVLine *LineOne = nullptr; + LVLine *LineTwo = nullptr; + LVLine *LineThree = nullptr; + LVLine *LineFour = nullptr; + LVLine *LineFive = nullptr; + LVLine *LineSix = nullptr; + + // Locations. + MyLocation *LocationOne = nullptr; + MyLocation *LocationTwo = nullptr; + MyLocation *LocationThree = nullptr; + MyLocation *LocationFour = nullptr; + MyLocation *LocationFive = nullptr; + MyLocation *LocationSix = nullptr; + +protected: + void add(LVScope *Parent, LVElement *Element); + template T *create() { + T *Element = new (std::nothrow) T(); + EXPECT_NE(Element, nullptr); + return Element; + } + void set(LVElement *Element, StringRef Name, LVOffset Offset, + uint32_t LineNumber = 0, LVElement *Type = nullptr); + void set(MyLocation *Location, LVLine *LowerLine, LVLine *UpperLine, + LVAddress LowerAddress, LVAddress UpperAddress); + void add(LVSymbol *Symbol, LVLine *LowerLine, LVLine *UpperLine); + +public: + ReaderTestWarningInternal(ScopedPrinter &W) : LVReader("", "", W) { + setInstance(this); + } + + Error createScopes() { return LVReader::createScopes(); } + + void setMapping(); + void createElements(); + void addElements(); + void initElements(); + void resolveElements(); + void checkWarnings(); +}; + +class MyLocation : public LVLocation { +public: + bool validateRanges(); +}; + +bool MyLocation::validateRanges() { + // Traverse the locations and validate them against the address to line + // mapping in the current compile unit. Record those invalid ranges. + // A valid range must meet the following conditions: + // a) line(lopc) <= line(hipc) + // b) line(lopc) and line(hipc) are valid. + + LVLine *LowLine = AddressToLine.lineLowerBound(getLowerAddress()); + LVLine *HighLine = AddressToLine.lineUpperBound(getUpperAddress()); + if (LowLine) + setLowerLine(LowLine); + else { + setIsInvalidLower(); + return false; + } + if (HighLine) + setUpperLine(HighLine); + else { + setIsInvalidUpper(); + return false; + } + // Check for a valid interval. + if (LowLine->getLineNumber() > HighLine->getLineNumber()) { + setIsInvalidRange(); + return false; + } + + return true; +} + +// Map all logical lines with their addresses. +void ReaderTestWarningInternal::setMapping() { + AddressToLine.insert(LineOne); + AddressToLine.insert(LineTwo); + AddressToLine.insert(LineThree); + AddressToLine.insert(LineFour); + AddressToLine.insert(LineFive); + AddressToLine.insert(LineSix); +} + +// Helper function to add a logical element to a given scope. +void ReaderTestWarningInternal::add(LVScope *Parent, LVElement *Child) { + Parent->addElement(Child); + EXPECT_EQ(Child->getParent(), Parent); + EXPECT_EQ(Child->getLevel(), Parent->getLevel() + 1); +} + +// Helper function to set the initial values for a given logical element. +void ReaderTestWarningInternal::set(LVElement *Element, StringRef Name, + LVOffset Offset, uint32_t LineNumber, + LVElement *Type) { + Element->setName(Name); + Element->setOffset(Offset); + Element->setLineNumber(LineNumber); + Element->setType(Type); + EXPECT_EQ(Element->getName(), Name); + EXPECT_EQ(Element->getOffset(), Offset); + EXPECT_EQ(Element->getLineNumber(), LineNumber); + EXPECT_EQ(Element->getType(), Type); +} + +// Helper function to set the initial values for a given logical location. +void ReaderTestWarningInternal::set(MyLocation *Location, LVLine *LowerLine, + LVLine *UpperLine, LVAddress LowerAddress, + LVAddress UpperAddress) { + Location->setLowerLine(LowerLine); + Location->setUpperLine(UpperLine); + Location->setLowerAddress(LowerAddress); + Location->setUpperAddress(UpperAddress); + EXPECT_EQ(Location->getLowerLine(), LowerLine); + EXPECT_EQ(Location->getUpperLine(), UpperLine); + EXPECT_EQ(Location->getLowerAddress(), LowerAddress); + EXPECT_EQ(Location->getUpperAddress(), UpperAddress); +} + +// Helper function to add a logical location to a logical symbol. +void ReaderTestWarningInternal::add(LVSymbol *Symbol, LVLine *LowerLine, + LVLine *UpperLine) { + dwarf::Attribute Attr = dwarf::DW_AT_location; + + Symbol->addLocation(Attr, LowerLine->getAddress(), UpperLine->getAddress(), + /*SectionOffset=*/0, /*LocDesOffset=*/0); +} + +// Create the logical elements. +void ReaderTestWarningInternal::createElements() { + // Create scope root. + Error Err = createScopes(); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + Root = getScopesRoot(); + EXPECT_NE(Root, nullptr); + + // Create the logical types. + IntegerType = create(); + + // Create the logical scopes. + NestedScope = create(); + CompileUnit = create(); + Function = create(); + + // Create the logical symbols. + LocalVariable = create(); + NestedVariable = create(); + Parameter = create(); + + // Create the logical lines. + LineOne = create(); + LineTwo = create(); + LineThree = create(); + LineFour = create(); + LineFive = create(); + LineSix = create(); + + // Create the logical locations. + LocationOne = create(); + LocationTwo = create(); + LocationThree = create(); + LocationFour = create(); + LocationFive = create(); + LocationSix = create(); +} + +// Create the logical view adding the created logical elements. +void ReaderTestWarningInternal::addElements() { + setCompileUnit(CompileUnit); + + // Root + // CompileUnit + // IntegerType + // Function + // LocationOne + // LocationTwo + // LocationFive + // LocationSix + // Parameter + // LocalVariable + // LineOne + // LineTwo + // NestedScope + // LocationThree + // LocationFour + // NestedVariable + // LineThree + // LineFour + // LineFive + // LineSix + + // Add elements to Root. + add(Root, CompileUnit); + + // Add elements to CompileUnit. + add(CompileUnit, IntegerType); + add(CompileUnit, Function); + + // Add elements to Function. + add(Function, Parameter); + add(Function, LocalVariable); + add(Function, LineOne); + add(Function, LineTwo); + add(Function, LineFive); + add(Function, LineSix); + add(Function, NestedScope); + + // Add elements to NestedScope. + add(NestedScope, NestedVariable); + add(NestedScope, LineThree); + add(NestedScope, LineFour); +} + +void ReaderTestWarningInternal::resolveElements() { + // Traverse the given scope and its children checking for any warnings. + std::function TraverseScope = [&](LVScope *Parent) { + auto Warnings = [&](const auto *Entry) { + if (Entry->getIsLine()) { + LVLine *Line = (LVLine *)Entry; + if (options().getWarningLines() && Line->getIsLineDebug() && + !Line->getLineNumber()) + CompileUnit->addLineZero(Line); + } + }; + auto Traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) { + Warnings(Entry); + } + }; + + Warnings(Parent); + + Traverse(Parent->getSymbols()); + Traverse(Parent->getTypes()); + Traverse(Parent->getLines()); + + if (const LVScopes *Scopes = Parent->getScopes()) + for (LVScope *Scope : *Scopes) { + Warnings(Scope); + TraverseScope(Scope); + } + }; + + // Start traversing the scopes root and resolve the elements. + TraverseScope(Root); +} + +// Set initial values to logical elements. +void ReaderTestWarningInternal::initElements() { + // Types. + set(IntegerType, "int", 0x1000); + + // Scopes. + set(CompileUnit, "foo.cpp", 0x2000); + set(Function, "foo", 0x2010, 100, IntegerType); + set(NestedScope, "", 0x2020, 300); + + // Symbols. + set(Parameter, "Param", 0x3000, 110, IntegerType); + set(LocalVariable, "LocalVariable", 0x3020, 120, IntegerType); + set(NestedVariable, "NestedVariable", 0x3010, 310, IntegerType); + + // Lines. + set(LineOne, "", 0x5000, 100); + LineOne->setIsLineDebug(); + set(LineTwo, "", 0x5200, 000); + LineTwo->setIsLineDebug(); + set(LineThree, "", 0x5400, 300); + LineThree->setIsLineDebug(); + set(LineFour, "", 0x5600, 000); + LineFour->setIsLineDebug(); + set(LineFive, "", 0x5800, 500); + LineOne->setIsLineDebug(); + set(LineSix, "", 0x6000, 600); + LineSix->setIsLineDebug(); + + // Locations. + set(LocationOne, LineOne, LineOne, 0x5000, 0x5100); + EXPECT_STREQ(LocationOne->getIntervalInfo().c_str(), + " Lines 100:100 [0x0000005000:0x0000005100]"); + + // Uses a Line zero. + set(LocationTwo, LineTwo, LineTwo, 0x5200, 0x5300); + EXPECT_STREQ(LocationTwo->getIntervalInfo().c_str(), + " Lines -:- [0x0000005200:0x0000005300]"); + + set(LocationThree, LineThree, LineThree, 0x5400, 0x5500); + EXPECT_STREQ(LocationThree->getIntervalInfo().c_str(), + " Lines 300:300 [0x0000005400:0x0000005500]"); + + // Uses a Line zero. + set(LocationFour, LineFour, LineFour, 0x5600, 0x5700); + LocationFour->setIsAddressRange(); + EXPECT_STREQ(LocationFour->getIntervalInfo().c_str(), + "{Range} Lines -:- [0x0000005600:0x0000005700]"); + + // Invalid range. + set(LocationFive, LineFive, LineFive, 0x7800, 0x5900); + LocationFive->setIsAddressRange(); + EXPECT_STREQ(LocationFive->getIntervalInfo().c_str(), + "{Range} Lines 500:500 [0x0000007800:0x0000005900]"); + + set(LocationSix, LineSix, LineSix, 0x6000, 0x6100); + LocationSix->setIsAddressRange(); + EXPECT_STREQ(LocationSix->getIntervalInfo().c_str(), + "{Range} Lines 600:600 [0x0000006000:0x0000006100]"); + + // Add ranges to Function. + // Function: LocationOne, LocationTwo, LocationFive, LocationSix + Function->addObject(LocationOne); + Function->addObject(LocationTwo); + Function->addObject(LocationFive); + Function->addObject(LocationSix); + EXPECT_EQ(Function->rangeCount(), 4); + + // Add ranges to NestedScope. + // NestedScope: LocationThree, LocationFour + NestedScope->addObject(LocationThree); + NestedScope->addObject(LocationFour); + EXPECT_EQ(NestedScope->rangeCount(), 2); + + // Get all ranges. + LVRange Ranges; + CompileUnit->getRanges(Ranges); + Ranges.startSearch(); + EXPECT_EQ(Ranges.getEntry(0x4000), nullptr); + + EXPECT_EQ(Ranges.getEntry(0x5060), Function); + EXPECT_EQ(Ranges.getEntry(0x5850), nullptr); + EXPECT_EQ(Ranges.getEntry(0x5010, 0x5090), Function); + EXPECT_EQ(Ranges.getEntry(0x5210, 0x5290), Function); + EXPECT_EQ(Ranges.getEntry(0x5810, 0x5890), nullptr); + EXPECT_EQ(Ranges.getEntry(0x6010, 0x6090), Function); + + EXPECT_EQ(Ranges.getEntry(0x5400), NestedScope); + EXPECT_EQ(Ranges.getEntry(0x5650), NestedScope); + EXPECT_EQ(Ranges.getEntry(0x5410, 0x5490), NestedScope); + EXPECT_EQ(Ranges.getEntry(0x5610, 0x5690), NestedScope); + + EXPECT_EQ(Ranges.getEntry(0x8000), nullptr); + Ranges.endSearch(); + + // Add locations to symbols. + // Parameter: [LineOne, LineSix] + // LocalVariable: [LineTwo, LineSix], [LineFour, LineFive] + // NestedVariable: [LineThree, LineFour] + add(Parameter, LineOne, LineSix); + add(LocalVariable, LineTwo, LineSix); + add(LocalVariable, LineFour, LineFive); + add(NestedVariable, LineThree, LineFour); + add(NestedVariable, LineOne, LineSix); +} + +// Check logical elements warnigs. +void ReaderTestWarningInternal::checkWarnings() { + // Map all lines with their addresses. + setMapping(); + + // Check for lines with line zero. + resolveElements(); + + // Check invalid locations and ranges using a customized validation. + CompileUnit->processRangeLocationCoverage( + (LVValidLocation)(&MyLocation::validateRanges)); + + // Get lines with line zero. [Parent, Line] + // Function, LineTwo + // NestedScope, LineFour + LVOffsetLinesMap LinesZero = CompileUnit->getLinesZero(); + ASSERT_EQ(LinesZero.size(), 2); + + LVOffsetLinesMap::iterator IterZero = LinesZero.begin(); + EXPECT_EQ(IterZero->first, Function->getOffset()); + LVLines *Lines = IterZero->second; + EXPECT_NE(Lines, nullptr); + ASSERT_EQ(Lines->size(), 1); + LVLine *Line = *(Lines->begin()); + EXPECT_NE(Line, nullptr); + EXPECT_EQ(Line, LineTwo); + + ++IterZero; + EXPECT_EQ(IterZero->first, NestedScope->getOffset()); + Lines = IterZero->second; + EXPECT_NE(Lines, nullptr); + ASSERT_EQ(Lines->size(), 1); + Line = *(Lines->begin()); + EXPECT_NE(Line, nullptr); + EXPECT_EQ(Line, LineFour); + + // Elements with invalid offsets. + // Function (line zero) + // NestedScope (line zero) + // NestedVariable (invalid location) + LVOffsetElementMap InvalidOffsets = CompileUnit->getWarningOffsets(); + ASSERT_EQ(InvalidOffsets.size(), 3); + + LVOffsetElementMap::iterator IterOffset = InvalidOffsets.begin(); + EXPECT_EQ(IterOffset->second, Function); + ++IterOffset; + EXPECT_EQ(IterOffset->second, NestedScope); + ++IterOffset; + EXPECT_EQ(IterOffset->second, NestedVariable); + + // Invalid ranges. + // Function + LVOffsetLocationsMap InvalidRanges = CompileUnit->getInvalidRanges(); + ASSERT_EQ(InvalidRanges.size(), 1); + + LVOffsetLocationsMap::iterator IterRange = InvalidRanges.begin(); + EXPECT_EQ(IterRange->first, Function->getOffset()); + LVLocations *Locations = IterRange->second; + EXPECT_NE(Locations, nullptr); + ASSERT_EQ(Locations->size(), 1); + LVLocation *Location = *(Locations->begin()); + EXPECT_NE(Location, nullptr); + EXPECT_EQ(Location, LocationFive); + + // Invalid location. + // NestedVariable + LVOffsetLocationsMap InvalidLocations = CompileUnit->getInvalidLocations(); + ASSERT_EQ(InvalidLocations.size(), 1); + + LVOffsetLocationsMap::iterator IterLocations = InvalidLocations.begin(); + EXPECT_EQ(IterLocations->first, NestedVariable->getOffset()); + Locations = IterLocations->second; + EXPECT_NE(Locations, nullptr); + ASSERT_EQ(Locations->size(), 1); + Location = *(Locations->begin()); + EXPECT_NE(Location, nullptr); + EXPECT_EQ(Location->getLowerAddress(), LocationThree->getLowerAddress()); + EXPECT_EQ(Location->getUpperAddress(), LocationFour->getLowerAddress()); + EXPECT_EQ(Location->getLowerLine()->getLineNumber(), + LineThree->getLineNumber()); + EXPECT_EQ(Location->getUpperLine()->getLineNumber(), 0); + + // Invalid coverages. + // NestedVariable + LVOffsetSymbolMap InvalidCoverages = CompileUnit->getInvalidCoverages(); + ASSERT_EQ(InvalidCoverages.size(), 1); + + LVOffsetSymbolMap::iterator IterCoverages = InvalidCoverages.begin(); + EXPECT_EQ(IterCoverages->first, NestedVariable->getOffset()); + EXPECT_EQ(IterCoverages->second, NestedVariable); + EXPECT_GE((int)NestedVariable->getCoveragePercentage(), 100); + EXPECT_EQ((int)NestedVariable->getCoveragePercentage(), 900); + EXPECT_EQ(NestedVariable->getCoverageFactor(), 0x1200); + + EXPECT_EQ((unsigned)Parameter->getCoveragePercentage(), 100); + EXPECT_EQ(Parameter->getCoverageFactor(), 100); + + EXPECT_EQ((unsigned)LocalVariable->getCoveragePercentage(), 47); + EXPECT_EQ(LocalVariable->getCoverageFactor(), + LineSix->getAddress() - LineOne->getAddress()); +} + +TEST(LogicalViewTest, WarningInternal) { + ScopedPrinter W(outs()); + ReaderTestWarningInternal Reader(W); + + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setAttributeRange(); + ReaderOptions.setAttributeLocation(); + ReaderOptions.setPrintAll(); + ReaderOptions.setWarningCoverages(); + ReaderOptions.setWarningLines(); + ReaderOptions.setWarningLocations(); + ReaderOptions.setWarningRanges(); + ReaderOptions.resolveDependencies(); + options().setOptions(&ReaderOptions); + + Reader.createElements(); + Reader.addElements(); + Reader.initElements(); + Reader.checkWarnings(); +} + +} // namespace