Index: llvm/docs/CommandGuide/index.rst =================================================================== --- llvm/docs/CommandGuide/index.rst +++ llvm/docs/CommandGuide/index.rst @@ -23,6 +23,7 @@ llvm-cxxmap llvm-diff llvm-dis + llvm-dva llvm-dwarfdump llvm-lib llvm-libtool-darwin Index: llvm/docs/CommandGuide/llvm-dva.rst =================================================================== --- /dev/null +++ llvm/docs/CommandGuide/llvm-dva.rst @@ -0,0 +1,789 @@ +llvm-dva - Print a logical representation of low-level debug information. +========================================================================= + +.. program:: llvm-dva + +.. contents:: + :local: + +SYNOPSIS +-------- +:program:`llvm-dva` [*options*] [*filename ...*] + +DESCRIPTION +----------- +:program:`llvm-dva` 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, MacOS, 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-dva` 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-dva` 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-dva` 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-dva` 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-dva` defaults to read +`a.out` and return an error when no input file is found. + +If `-` is used as the input file, :program:`llvm-dva` 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. + =operation: Debug location operation. + =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. + =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 to be + used when a more low level detail is required. + + .. code-block:: text + + =argument + =discarded + =encoded + =gaps + =generated + =global + =inserted + =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 printed, allocated; warnings produced during the + view creation. + + .. code-block:: text + + =sizes: Debug Information scopes contributions. + =summary: Summary of elements allocated and 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-dva` 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. + =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 + + =details: The elements are displayed in a tabular format. + =summary: Summary of elements printed, missing, added. + =view: The elements are displayed in a tree format. + +The **details** 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 **summary** layout presents a synopsis containing the total number +of elements related to the task being executed: printed, missing, added, +found. + +The **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). + +.. _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, allow to print 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 the selection for printing of elements that +match the given , offset or an element . + +.. option:: --select= + + Select for printing all elements whose name or line number matches the + given . + +.. option:: --select-offsets= + + Select for printing all elements whose offset matches the given values. + See :option:`--attribute` option. + +.. option:: --select-elements= + + Selection for printing 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 the selection for 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 + + =BasicBlock: Marks a new basic block. + =Discriminator: Line that has a discriminator. + =EndSequence: Marks the end in the sequence of lines. + =EpilogueBegin: Marks the begin of a function epilogue. + =LineDebug: Lines that correspond to debug lines. + =LineSource: Lines that correspond to disassembly text. + =NewStatement: Marks a new statement. + =PrologueEnd: Marks the end of a function prologue. + +SCOPES +^^^^^^ +The following options allow the selection for 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). + =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: An union. + +SYMBOLS +^^^^^^^ +The following options allow the selection for printing of symbols that +match the given . + +.. option:: --select-symbols= + + With **kind** being one the options in the following list. + + .. code-block:: text + + =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 the selection for 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 were 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, is very +difficult to use a regular diff tool to find those elements; even +impossible when dealing with different debug formats. + +:program:`llvm-dva` 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 is representing 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 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-dva` 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-dva` 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-dva` 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. + + .. code-block:: text + + =id: Print unique element ID. + =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. + +EXAMPLE +------- + +The below example is used to show different output generated by +:program:`llvm-dva`. 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 } + +The following command prints basic details for all the logical elements +sorted by the debug information internal offset; it includes its lexical +level. 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 + + llvm-dva --output-sort=offset + --attribute=level + --print=scopes,symbols,types,lines,instructions + test.o + + Logical View: + + [000] {File} 'test.o' + [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' + [003] 4 {TypeAlias} 'INTEGER' -> 'int' + [003] 2 {Line} + [003] {Code} 'pushq %rbp' + [003] {Code} 'movq %rsp, %rbp' + [003] {Code} 'movq %rdi, -0x10(%rbp)' + [003] {Code} 'movl %esi, -0x14(%rbp)' + [003] {Code} 'andb $0x1, %dl' + [003] {Code} 'movb %dl, -0x15(%rbp)' + [003] 3 {Line} + [003] {Code} 'testb $0x1, -0x15(%rbp)' + [003] {Code} 'je 0x13' + [003] 8 {Line} + [003] {Code} 'movl -0x14(%rbp), %eax' + [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' + [002] 1 {TypeAlias} 'INTPTR' -> '* const int' + [002] 9 {Line} + +The following prints all 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-dva --select-nocase --select-regex --report=details,summary + --select=INTe --select=movl + --attribute=level --print=symbols,types,instructions + test.o + + Logical View: + + [000] {File} 'test.o' + [004] {Code} 'movl $0x7, -0x1c(%rbp)' + [004] {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 + +EXIT STATUS +----------- +:program:`llvm-dva` 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 @@ -2285,6 +2285,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,745 @@ +//===-- 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 +// +//===----------------------------------------------------------------------===// +// +// 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); +// 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. +// +// 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 (sorted in ascending by beginning point). +// IR: Intervals to the right (sorted in ascending 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] +// +// 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 creation of an internal 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 will all the intervals containing the target point. +// +// The returned intervals are in their natural tree location. They can +// be sorted: +// +// 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. +// +// In order to debug, define the symbol INTERVAL_TREE_DEBUG. + +#ifndef NDEBUG +//#define INTERVAL_TREE_DEBUG +#ifdef INTERVAL_TREE_DEBUG +#include "llvm/Support/Debug.h" +#endif +#endif + +#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 + +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 contain(const PointType &Point) const { + return left(Point) && right(Point); + } +}; + +//===----------------------------------------------------------------------===// +//--- IntervalTree ----// +//===----------------------------------------------------------------------===// +template > +class IntervalTree { +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() { return MiddlePoint; } + unsigned start() { return BucketIntervalsStart; } + unsigned size() { 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); + } + } + +#ifdef INTERVAL_TREE_DEBUG + /// Print the end points vector. + void printEndPoints(bool HexFormat = true) { + const char *Format = HexFormat ? "[0x%08x] " : "[%d] "; + dbgs() << format("\nEnd points: Total %d\n", EndPoints.size()); + for (auto Item : EndPoints) + dbgs() << format(Format, Item); + dbgs() << "\n"; + } +#endif + + /// Print the interval list (left and right) for a given \a Node. + void printList(raw_ostream &OS, IntervalReferences &IntervalSet, + unsigned Start, unsigned Size, bool HexFormat = true) { + 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) { +#ifdef INTERVAL_TREE_DEBUG + dbgs() << format("Intervals Size: %d\n", IntervalsSize) + << format("End Points: Begin %d, End %d\n", PointsBeginIndex, + PointsEndIndex) + << format("References: Begin %d, Size %d\n", ReferencesBeginIndex, + ReferencesSize); +#endif + // 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 left. + + if (PointsBeginIndex > PointsEndIndex || + ReferencesBeginIndex >= ReferencesSize) + return nullptr; + + int MiddleIndex = (PointsBeginIndex + PointsEndIndex) / 2; + PointType MiddlePoint = EndPoints[MiddleIndex]; + +#ifdef INTERVAL_TREE_DEBUG + dbgs() << format("\nReferences before scanning; MP=%2d: \n", MiddlePoint); + printList(dbgs(), References, 0, References.size(), /*HexFormat=*/false); + dbgs() << "\n"; + unsigned Count = 0; +#endif + + 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;) { + +#ifdef INTERVAL_TREE_DEBUG + dbgs() << format("%2d: %2d: ", Count++, Index); + printList(dbgs(), References, 0, References.size(), /*HexFormat=*/false); +#endif + + // Current interval contains the middle point. + if (References[Index]->contain(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; + } + +#ifdef INTERVAL_TREE_DEBUG + dbgs() << "\nReferences after scanning\n"; + printList(dbgs(), References, 0, References.size(), /*HexFormat=*/false); +#endif + +#ifdef INTERVAL_TREE_DEBUG + dbgs() << format("\nNode before sorting: Middle=%d, Start=%d, Size=%d\n", + Root->middle(), Root->start(), Root->size()); + dbgs() << "IL "; + printList(dbgs(), IntervalsLeft, Root->start(), Root->size(), + /*HexFormat=*/false); + dbgs() << "IR "; + printList(dbgs(), IntervalsRight, Root->start(), Root->size(), + /*HexFormat=*/false); +#endif + + // 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, + [](DataType *LHS, 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, + [](DataType *LHS, DataType *RHS) { + return LHS->right() > RHS->right(); + }); + } + +#ifdef INTERVAL_TREE_DEBUG + dbgs() << format("\nNode after sorting: Middle=%d, Start=%d, Size=%d\n", + Root->middle(), Root->start(), Root->size()); + dbgs() << "IL "; + printList(dbgs(), IntervalsLeft, Root->start(), Root->size(), + /*HexFormat=*/false); + dbgs() << "IR "; + printList(dbgs(), IntervalsRight, Root->start(), Root->size(), + /*HexFormat=*/false); +#endif + + if (PointsBeginIndex <= MiddleIndex - 1) { +#ifdef INTERVAL_TREE_DEBUG + dbgs() << "\nCreating Left Node: "; +#endif + Root->Left = createTree(IntervalsSize, PointsBeginIndex, MiddleIndex - 1, + ReferencesBeginIndex, ReferencesRightIndex); + } + + if (MiddleIndex + 1 <= PointsEndIndex) { +#ifdef INTERVAL_TREE_DEBUG + dbgs() << "\nCreating Right Node: "; +#endif + Root->Right = createTree(IntervalsSize, MiddleIndex + 1, PointsEndIndex, + ReferencesRightIndex, ReferencesSize); + } + + return Root; + } + +public: + class find_iterator + : public std::iterator { + + 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(); + } + + 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 (Point <= Node->middle()) + ? (*AscendingBuckets)[Node->start() + Index] + : (*DescendingBuckets)[Node->start() + Index]; + } + const DataType &operator*() const { + return *(((Point <= Node->middle()) + ? ((*AscendingBuckets)[Node->start() + Index]) + : ((*DescendingBuckets)[Node->start() + Index]))); + } + + /// 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 { + 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. + void sortIntervals(IntervalReferences &IntervalSet, Sorting Sort) const { + std::sort(IntervalSet.begin(), IntervalSet.end(), + [Sort](DataType *RHS, 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 (DataType &Data : Intervals) { + Points.push_back(Data.left()); + Points.push_back(Data.right()); + References.emplace_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()); + +#ifdef INTERVAL_TREE_DEBUG + printEndPoints(/*HexFormat=*/false); + dbgs() << format("\nOriginal references: Total %d\n", References.size()); + printList(dbgs(), References, 0, References.size(), /*HexFormat=*/false); + dbgs() << "\nCreating Main Node: "; +#endif + + // 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/CodeView/TypeRecordHelpers.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeRecordHelpers.h +++ llvm/include/llvm/DebugInfo/CodeView/TypeRecordHelpers.h @@ -41,6 +41,27 @@ } } +/// Given an arbitrary codeview type, determine if it is an LF_STRUCTURE, +/// LF_CLASS, LF_INTERFACE, LF_UNION. +inline bool isAggregate(CVType CVT) { + switch (CVT.kind()) { + case LF_STRUCTURE: + case LF_CLASS: + case LF_INTERFACE: + case LF_UNION: + return true; + default: + return false; + } +} + +/// Given an arbitrary codeview type index, determine its size. +uint64_t getSize(TypeIndex TI); + +/// Given an arbitrary codeview type, return the type's size in the case +/// of aggregate (LF_STRUCTURE, LF_CLASS, LF_INTERFACE, LF_UNION). +uint64_t getSize(CVType CVT); + } // namespace codeview } // namespace llvm Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVBasicDefinitions.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVBasicDefinitions.h @@ -0,0 +1,86 @@ +//===-- LVBasicDefinitions.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 basic definitions used across the logical elements. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVBASICDEFINITIONS_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVBASICDEFINITIONS_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include +#include +#include + +namespace llvm { +namespace logicalview { + +// Common definitions that represent a specific logical view type. +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 LVObject; +class LVOperation; +class LVScope; +class LVSymbol; +class LVLocationSymbol; +class LVType; + +class LVOptions; +class LVPatterns; + +LVElement *nullElement(); +LVLine *nullLine(); +LVLocation *nullLocation(); +LVLocationSymbol *nullLocationSymbol(); +LVObject *nullObject(); +LVType *nullType(); +LVScope *nullScope(); +LVSymbol *nullSymbol(); + +StringRef typeNone(); +StringRef typeVoid(); +StringRef typeInt(); +StringRef typeUnknown(); +StringRef emptyString(); + +using LVLines = SmallVector; +using LVLocations = SmallVector; +using LVElements = SmallVector; +using LVObjects = SmallVector; +using LVOffsets = SmallVector; +using LVOperations = SmallVector; +using LVScopes = SmallVector; +using LVSymbols = SmallVector; +using LVTypes = SmallVector; +using LVLineList = SmallVector; + +using LVNameInfo = std::pair; +using LVPublicNames = std::map; + +const LVAddress MaxAddress = std::numeric_limits::max(); + +enum class LVComparePass { Missing, Added }; + +// Validate functions. +using LVCheckLocation = bool (LVLocation::*)(); + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVBASICDEFINITIONS_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVCompare.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVCompare.h @@ -0,0 +1,75 @@ +//===-- 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" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { +namespace logicalview { + +class LVReader; + +class LVCompare { + raw_ostream &OS; + LVScopes ScopeStack; + + bool FirstMissing = true; + bool PrintAll = false; + bool PrintLines = false; + bool PrintScopes = false; + bool PrintSymbols = false; + bool PrintTypes = false; + + int printCurrentStack(); + void printDetails(LVElement *Element, StringRef Kind, StringRef Name); + void printSummary() const; + +public: + LVCompare() = delete; + LVCompare(raw_ostream &OS); + LVCompare(const LVCompare &) = delete; + LVCompare &operator=(LVCompare const &) = delete; + ~LVCompare(){}; + +public: + static LVCompare &getInstance(); + static void setInstance(LVCompare *Compare); + + // Scopes stack used during the missing/added reporting. + void push(LVScope *Scope) { ScopeStack.push_back(Scope); } + void pop() { ScopeStack.pop_back(); } + + // Do the compare between the 'Reference' and 'Target' scopes tree. + Error execute(LVReader *ReferenceReader, LVReader *TargetReader); + +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,441 @@ +//===-- 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/ADT/BitVector.h" +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" +#include "llvm/DebugInfo/LogicalView/Core/LVStringPool.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { +namespace logicalview { + +enum class LVElementKind { Discarded, Global, Optimized, LastEntry }; +using LVElementKindSet = std::set; +using LVElementGetFunction = bool (LVElement::*)() const; +using LVElementTarget = std::map; +using LVElementRequest = std::vector; + +class LVElement : public LVObject { + enum class Property { + Line, // A logical line. + Scope, // A logical scope. + Symbol, // A logical symbol. + Type, // A logical type. + EnumClass, + External, + HasType, + HasAugmentedName, + TypedefReduced, + ArrayResolved, + MemberPointerResolved, + TemplateResolved, + Inlined, + InlinedAbstract, + InvalidFilename, + HasReference, + HasReferenceAbstract, + HasReferenceExtension, + HasReferenceSpecification, + QualifiedResolved, + IncludeInPrint, + Static, + TransformName, + Scoped, // CodeView local type. + Nested, // CodeView nested type. + ScopedAlready, // CodeView nested type inserted in correct scope. + Artificial, + ReferencedType, + System, + OffsetFromTypeIndex, + LastEntry + }; + // Typed bitvector with properties for this element. + LVProperties Properties; + static LVElementTarget Targets; + + // 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() + : LVObject(), AccessibilityCode(0), InlineCode(0), VirtualityCode(0) {} + LVElement(const LVElement &) = delete; + LVElement &operator=(LVElement const &) = delete; + virtual ~LVElement() {} + + bool getIsLine() const { return Properties.get(Property::Line); } + void setIsLine() { Properties.set(Property::Line); } + + bool getIsScope() const { return Properties.get(Property::Scope); } + void setIsScope() { Properties.set(Property::Scope); } + + bool getIsSymbol() const { return Properties.get(Property::Symbol); } + void setIsSymbol() { Properties.set(Property::Symbol); } + + bool getIsType() const { return Properties.get(Property::Type); } + void setIsType() { Properties.set(Property::Type); } + + bool getIsEnumClass() const { return Properties.get(Property::EnumClass); } + void setIsEnumClass() { Properties.set(Property::EnumClass); } + + bool getIsExternal() const { return Properties.get(Property::External); } + void setIsExternal() { Properties.set(Property::External); } + void resetExternal() { Properties.reset(Property::External); } + + bool getHasType() const { return Properties.get(Property::HasType); } + void setHasType() { Properties.set(Property::HasType); } + + bool getHasAugmentedName() const { + return Properties.get(Property::HasAugmentedName); + } + void setHasAugmentedName() { Properties.set(Property::HasAugmentedName); } + + bool getIsTypedefReduced() const { + return Properties.get(Property::TypedefReduced); + } + void setIsTypedefReduced() { Properties.set(Property::TypedefReduced); } + + bool getIsArrayResolved() const { + return Properties.get(Property::ArrayResolved); + } + void setIsArrayResolved() { Properties.set(Property::ArrayResolved); } + + bool getIsMemberPointerResolved() const { + return Properties.get(Property::MemberPointerResolved); + } + void setIsMemberPointerResolved() { + Properties.set(Property::MemberPointerResolved); + } + + bool getIsTemplateResolved() const { + return Properties.get(Property::TemplateResolved); + } + void setIsTemplateResolved() { Properties.set(Property::TemplateResolved); } + + bool getIsInlined() const { return Properties.get(Property::Inlined); } + void setIsInlined() { Properties.set(Property::Inlined); } + + bool getIsInlinedAbstract() const { + return Properties.get(Property::InlinedAbstract); + } + void setIsInlinedAbstract() { Properties.set(Property::InlinedAbstract); } + + bool getInvalidFilename() const { + return Properties.get(Property::InvalidFilename); + } + void setInvalidFilename() { Properties.set(Property::InvalidFilename); } + + bool getHasReference() const { + return Properties.get(Property::HasReference); + } + void setHasReference() { Properties.set(Property::HasReference); } + + bool getHasReferenceAbstract() const { + return Properties.get(Property::HasReferenceAbstract); + } + void setHasReferenceAbstract() { + Properties.set(Property::HasReferenceAbstract); + } + + bool getHasReferenceExtension() const { + return Properties.get(Property::HasReferenceExtension); + } + void setHasReferenceExtension() { + Properties.set(Property::HasReferenceExtension); + } + + bool getHasReferenceSpecification() const { + return Properties.get(Property::HasReferenceSpecification); + } + void setHasReferenceSpecification() { + Properties.set(Property::HasReferenceSpecification); + } + + bool getQualifiedResolved() const { + return Properties.get(Property::QualifiedResolved); + } + void setQualifiedResolved() { Properties.set(Property::QualifiedResolved); } + + bool getIncludeInPrint() const { + return Properties.get(Property::IncludeInPrint); + } + void setIncludeInPrint() { Properties.set(Property::IncludeInPrint); } + void resetIncludeInPrint() { Properties.reset(Property::IncludeInPrint); } + + bool getIsStatic() const { return Properties.get(Property::Static); } + void setIsStatic() { Properties.set(Property::Static); } + + bool getTransformName() const { + return Properties.get(Property::TransformName); + } + void setTransformName() { Properties.set(Property::TransformName); } + + bool getIsScoped() const { return Properties.get(Property::Scoped); } + void setIsScoped() { Properties.set(Property::Scoped); } + + bool getIsNested() const { return Properties.get(Property::Nested); } + void setIsNested() { Properties.set(Property::Nested); } + + bool getIsScopedAlready() const { + return Properties.get(Property::ScopedAlready); + } + void setIsScopedAlready() { Properties.set(Property::ScopedAlready); } + + bool getIsArtificial() const { return Properties.get(Property::Artificial); } + void setIsArtificial() { Properties.set(Property::Artificial); } + + bool getIsReferencedType() const { + return Properties.get(Property::ReferencedType); + } + void setIsReferencedType() { Properties.set(Property::ReferencedType); } + + bool getIsSystem() const { return Properties.get(Property::System); } + void setIsSystem() { Properties.set(Property::System); } + + bool getOffsetFromTypeIndex() const { + return Properties.get(Property::OffsetFromTypeIndex); + } + void setOffsetFromTypeIndex() { + Properties.set(Property::OffsetFromTypeIndex); + } + +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 { + // In the case of Root or Compile Unit, flat out the name. + StringRef Name = ElementName; + std::string TheFilename; + if (getTransformName()) { + TheFilename = transformPath(ElementName); + Name = TheFilename.c_str(); + } + NameIndex = getStringPool().getIndex(Name); + } + + // Get filename associated with the Element. + StringRef getPathname() const; + + // Set filename associated with the Element. + void setFilename(StringRef Filename) { + std::string TheFilename(transformPath(Filename)); + FilenameIndex = getStringPool().getIndex(TheFilename.c_str()); + } + + // 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; } + + bool markSystemEntry(StringRef Name = {}); + void setInnerComponent() { setInnerComponent(getName()); } + void setInnerComponent(StringRef Name); + + // Element type name. + StringRef getTypeName() const; + + // Get all the locations associated with symbols. + virtual void getLocations(LVLocations *LocationList, + LVCheckLocation CheckLocation){}; + + 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 = nullElement()); + + virtual bool getIsBase() 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; + + // Encoded template arguments. + virtual StringRef getEncodedArgs() const { return StringRef(); } + virtual void setEncodedArgs(StringRef EncodedArgs) {} + + LVType *getType(LVType *Type) const; + LVScope *getType(LVScope *Scope) const; + LVSymbol *getType(LVSymbol *Symbol) const; + LVElement *getType(LVElement *Element = nullptr) 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 *getFunctionParent() const; + LVScope *getCompileUnitParent() const; + + // Print any referenced element. + void printReference(LVElement *Parent, raw_ostream &OS, + bool Full = true) const; + + // Generate the full name for the Element. + void resolveFullname(StringRef Name, LVElement *BaseType); + + // Generate a name for unnamed elements. + void generateName(std::string &Prefix); + 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 LVElementTarget &getTargets() { return Targets; } +}; + +} // 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,188 @@ +//===-- 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/ADT/BitVector.h" +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { +namespace logicalview { + +enum class LVLineKind { + BasicBlock, + Discriminator, + EndSequence, + EpilogueBegin, + LineDebug, + LineSource, + NewStatement, // Shared with CodeView 'IsStatement' flag. + PrologueEnd, + AlwaysStepInto, // CodeView + NeverStepInto, // CodeView + LastEntry +}; +using LVLineKindSet = std::set; +using LVLineGetFunction = bool (LVLine::*)() const; +using LVLineTarget = 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 LVLineTarget Targets; + + // Find the 'current' line in the 'target' set. + LVLine *findIn(const LVLines *Targets); + +public: + LVLine() : LVElement() { + setIsLine(); + setIncludeInPrint(); + } + LVLine(const LVLine &) = delete; + LVLine &operator=(LVLine const &) = delete; + virtual ~LVLine() {} + + bool getIsBasicBlock() const { return Kinds.get(LVLineKind::BasicBlock); } + void setIsBasicBlock() { Kinds.set(LVLineKind::BasicBlock); } + + bool getIsDiscriminator() const { + return Kinds.get(LVLineKind::Discriminator); + } + void setIsDiscriminator() { Kinds.set(LVLineKind::Discriminator); } + + bool getIsEndSequence() const { return Kinds.get(LVLineKind::EndSequence); } + void setIsEndSequence() { Kinds.set(LVLineKind::EndSequence); } + + bool getIsEpilogueBegin() const { + return Kinds.get(LVLineKind::EpilogueBegin); + } + void setIsEpilogueBegin() { Kinds.set(LVLineKind::EpilogueBegin); } + + bool getIsLineDebug() const { return Kinds.get(LVLineKind::LineDebug); } + void setIsLineDebug() { Kinds.set(LVLineKind::LineDebug); } + + bool getIsLineSource() const { return Kinds.get(LVLineKind::LineSource); } + void setIsLineSource() { Kinds.set(LVLineKind::LineSource); } + + bool getIsNewStatement() const { return Kinds.get(LVLineKind::NewStatement); } + void setIsNewStatement() { Kinds.set(LVLineKind::NewStatement); } + + bool getIsPrologueEnd() const { return Kinds.get(LVLineKind::PrologueEnd); } + void setIsPrologueEnd() { Kinds.set(LVLineKind::PrologueEnd); } + + bool getIsAlwaysStepInto() const { + return Kinds.get(LVLineKind::AlwaysStepInto); + } + void setIsAlwaysStepInto() { Kinds.set(LVLineKind::AlwaysStepInto); } + + bool getIsNeverStepInto() const { + return Kinds.get(LVLineKind::NeverStepInto); + } + void setIsNeverStepInto() { Kinds.set(LVLineKind::NeverStepInto); } + +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. + virtual std::string noLineAsString(bool ShowZero) 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 LVLineTarget &getTargets() { return Targets; } + +public: + // A function to iterate through the reference set and check that + // all it's members are present in the target set. + static void isContainedIn(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: + virtual void print(raw_ostream &OS, bool Full = true) const override; + virtual void printExtra(raw_ostream &OS, bool Full = true) const override{}; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + virtual 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=(LVLineDebug const &) = delete; + ~LVLineDebug() {} + + // Additional line information. + std::string sourceInfo(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 LVLineSource final : public LVLine { +public: + LVLineSource() : LVLine() { setIsLineSource(); } + LVLineSource(const LVLineSource &) = delete; + LVLineSource &operator=(LVLineSource const &) = delete; + ~LVLineSource() {} + + // 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,256 @@ +//===-- 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 debug information location. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H + +#include "llvm/ADT/BitVector.h" +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace logicalview { + +class LVOperation { + // 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. + // Offset: Offset in location expression for OP_BRA. + 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=(LVOperation const &) = delete; + + virtual ~LVOperation() {} + + LVSmall getOpcode() const { return Opcode; } + uint64_t getOperand1() const { return Operands[0]; } + uint64_t getOperand2() const { return Operands[1]; } + std::string getOperandsInfo(); + +public: + virtual void print(raw_ostream &OS, bool Full = true); + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + virtual void dump() { print(dbgs()); } +#endif +}; + +class LVLocation : public LVObject { + enum class Property { + AddressRange, + BaseClassOffset, + BaseClassStep, + ClassOffset, + FixedAddress, + LocationSimple, + GapEntry, + Operation, + OperationList, + Register, + StackOffset, + DiscardedRange, + InvalidRange, + InvalidLower, + InvalidUpper, + LastEntry + }; + // Typed bitvector with properties for this location. + LVProperties Properties; + + // Check if we need to print a range. + bool requiredRange() const { + return (getIsClassOffset() || getIsDiscardedRange()) ? false : true; + } + +protected: + // Line numbers associated with locations ranges. + LVLine *LowerLine = 0; + LVLine *UpperLine = 0; + + // Active range: + // Begin: an offset of an applicable base address, not a PC value. + // End: an offset of 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=(LVLocation const &) = delete; + virtual ~LVLocation() {} + + bool getIsAddressRange() const { + return Properties.get(Property::AddressRange); + } + void setIsAddressRange() { Properties.set(Property::AddressRange); } + + bool getIsBaseClassOffset() const { + return Properties.get(Property::BaseClassOffset); + } + void setIsBaseClassOffset() { Properties.set(Property::BaseClassOffset); } + + bool getIsBaseClassStep() const { + return Properties.get(Property::BaseClassStep); + } + void setIsBaseClassStep() { Properties.set(Property::BaseClassStep); } + + bool getIsClassOffset() const { + return Properties.get(Property::ClassOffset); + } + void setIsClassOffset() { + Properties.set(Property::ClassOffset); + setIsLocationSimple(); + } + + bool getIsFixedAddress() const { + return Properties.get(Property::FixedAddress); + } + void setIsFixedAddress() { + Properties.set(Property::FixedAddress); + setIsLocationSimple(); + } + + bool getIsLocationSimple() const { + return Properties.get(Property::LocationSimple); + } + void setIsLocationSimple() { Properties.set(Property::LocationSimple); } + + bool getIsGapEntry() const { return Properties.get(Property::GapEntry); } + void setIsGapEntry() { Properties.set(Property::GapEntry); } + + bool getIsOperationList() const { + return Properties.get(Property::OperationList); + } + void setIsOperationList() { Properties.set(Property::OperationList); } + + bool getIsOperation() const { return Properties.get(Property::Operation); } + void setIsOperation() { Properties.set(Property::Operation); } + + bool getIsRegister() const { return Properties.get(Property::Register); } + void setIsRegister() { Properties.set(Property::Register); } + + bool getIsStackOffset() const { + return Properties.get(Property::StackOffset); + } + void setIsStackOffset() { + Properties.set(Property::StackOffset); + setIsLocationSimple(); + } + + bool getIsDiscardedRange() const { + return Properties.get(Property::DiscardedRange); + } + void setIsDiscardedRange() { Properties.set(Property::DiscardedRange); } + + bool getIsInvalidRange() const { + return Properties.get(Property::InvalidRange); + } + void setIsInvalidRange() { Properties.set(Property::InvalidRange); } + + bool getIsInvalidLower() const { + return Properties.get(Property::InvalidLower); + } + void setIsInvalidLower() { Properties.set(Property::InvalidLower); } + + bool getIsInvalidUpper() const { + return Properties.get(Property::InvalidUpper); + } + void setIsInvalidUpper() { Properties.set(Property::InvalidUpper); } + + const char *kind() const override; + virtual void updateKind() {} + +public: + // Line numbers for locations. + LVLine *getLowerLine() const { return LowerLine; } + void setLowerLine(LVLine *Line) { LowerLine = Line; } + 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 validRanges(); + bool invalidRanges() { return !validRanges(); }; + + 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 {} + + virtual void print(raw_ostream &OS, bool Full = true) const override; + virtual void printExtra(raw_ostream &OS, bool Full = true) const override; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + virtual void dump() const override { print(dbgs()); } +#endif +}; + +class LVLocationSymbol final : public LVLocation { + // Location descriptors for the active range. + LVOperations Entries; + + void updateKind() override; + +public: + LVLocationSymbol() : LVLocation() {} + LVLocationSymbol(const LVLocationSymbol &) = delete; + LVLocationSymbol &operator=(LVLocationSymbol const &) = delete; + ~LVLocationSymbol(); + +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,307 @@ +//===-- 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/ADT/BitVector.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/LogicalView/Core/LVBasicDefinitions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace logicalview { + +using LVObjectSetFunction = void (LVObject::*)(); +using LVObjectGetFunction = bool (LVObject::*)() const; + +// 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 { + Location, // Location. + GlobalReference, // This object is being referenced from another CU. + GeneratedName, // The Object name was generated. + Resolved, // Object has been resolved. + ResolvedName, // Object name has been resolved. + Discarded, // Object has been stripped by the linker. + Optimized, // Object has been optimized by the compiler. + Added, // Object has been 'added'. + Missing, // Object is 'missing'. + MissingLink, // Object is indirectly 'missing'. + InCompare, // In 'compare' mode. + FileFromReference, // File ID from specification. + LineFromReference, // Line No from specification. + HasMoved, // The object was moved from 'target' to 'reference'. + HasPattern, // The object has a pattern. + Finalized, // CodeView object is finalized. + Referenced, // CodeView object being referenced. + 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. + // Empty implementation for those objects that do not have any user + // source file references, such as debug locations. + std::string referenceAsString(uint32_t LineNumber, bool Spaces) const; + + // Print the Filename or Pathname. + virtual void printFileIndex(raw_ostream &OS, bool Full = true) const {} + +public: + // 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() { +#ifndef NDEBUG + incID(); +#endif + }; + 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; + } + LVObject &operator=(LVObject const &) = delete; + virtual ~LVObject() {} + + bool getIsLocation() const { return Properties.get(Property::Location); } + void setIsLocation() { Properties.set(Property::Location); } + + bool getIsGlobalReference() const { + return Properties.get(Property::GlobalReference); + } + void setIsGlobalReference() { Properties.set(Property::GlobalReference); } + + bool getIsGeneratedName() const { + return Properties.get(Property::GeneratedName); + } + void setIsGeneratedName() { Properties.set(Property::GeneratedName); } + + bool getIsResolved() const { return Properties.get(Property::Resolved); } + void setIsResolved() { Properties.set(Property::Resolved); } + + bool getIsResolvedName() const { + return Properties.get(Property::ResolvedName); + } + void setIsResolvedName() { Properties.set(Property::ResolvedName); } + + bool getIsDiscarded() const { return Properties.get(Property::Discarded); } + void setIsDiscarded() { Properties.set(Property::Discarded); } + + bool getIsOptimized() const { return Properties.get(Property::Optimized); } + void setIsOptimized() { Properties.set(Property::Optimized); } + + bool getIsAdded() const { return Properties.get(Property::Added); } + void setIsAdded() { Properties.set(Property::Added); } + + bool getIsMissing() const { return Properties.get(Property::Missing); } + void setIsMissing() { Properties.set(Property::Missing); } + + bool getIsMissingLink() const { + return Properties.get(Property::MissingLink); + } + void setIsMissingLink() { Properties.set(Property::MissingLink); } + + bool getIsInCompare() const { return Properties.get(Property::InCompare); } + void setIsInCompare() { Properties.set(Property::InCompare); } + + bool getIsFileFromReference() const { + return Properties.get(Property::FileFromReference); + } + void setIsFileFromReference() { Properties.set(Property::FileFromReference); } + + bool getIsLineFromReference() const { + return Properties.get(Property::LineFromReference); + } + void setIsLineFromReference() { Properties.set(Property::LineFromReference); } + + bool getHasMoved() const { return Properties.get(Property::HasMoved); } + void setHasMoved() { Properties.set(Property::HasMoved); } + + bool getHasPattern() const { return Properties.get(Property::HasPattern); } + void setHasPattern() { Properties.set(Property::HasPattern); } + + bool getIsFinalized() const { return Properties.get(Property::Finalized); } + void setIsFinalized() { Properties.set(Property::Finalized); } + + bool getIsReferenced() const { return Properties.get(Property::Referenced); } + void setIsReferenced() { Properties.set(Property::Referenced); } + +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) {} + + virtual StringRef getProducer() const { return StringRef(); } + virtual void setProducer(StringRef ProducerName) {} + + LVElement *getParent() const { return Parent.Element; } + LVScope *getParentScope() const { return Parent.Scope; } + LVSymbol *getParentSymbol() const { return Parent.Symbol; } + void setParent(LVScope *Scope); + void setParent(LVSymbol *Symbol); + + virtual LVAddress getLowerAddress() const { return 0; } + virtual void setLowerAddress(LVAddress Address) {} + virtual LVAddress getUpperAddress() const { return 0; } + virtual void setUpperAddress(LVAddress Address) {} + virtual bool getIsCompileUnit() const { return false; } + virtual bool getIsRoot() const { return false; } + + 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); + } + virtual 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(LVObject *Parent, raw_ostream &OS, bool Full, + StringRef Name, 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,616 @@ +//===-- 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/BitVector.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/DebugInfo/LogicalView/Core/LVBasicDefinitions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSort.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.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 it = FAMILY.SET.find(TYPE::FIELD); \ + if (it != FAMILY.SET.end()) \ + FAMILY.SET.erase(it); \ + } + +#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 + Local, // --attribute=local + Location, // --attribute=location + Offset, // --attribute=offset + Operation, // --attribute=operation + 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 + Details, // --report=details + Summary, // --report=summary + 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 + ID, // --internal=id + Integrity, // --internal=integrity + None, // --internal=none + Tag // --internal=tag +}; +using LVInternalKindSet = std::set; + +class LVOptions { + struct LVAttribute { + 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. + }; + + struct LVCompare { + LVCompareKindSet Elements; // --compare= + bool Context = false; // --compare-context + bool Execute = false; // Compare requested. + }; + + struct LVPrint { + LVPrintKindSet Kinds; // --print= + bool AnyLine = false; // Print 'lines' or 'instructions'. + bool Execute = false; // Print requested. + bool Formatting = true; // Disable formatting during printing. + bool Generic = false; // Request to print an element. + bool Offset = false; // Print offsets while formatting is disable. + }; + + struct LVReport { + LVReportKindSet Kinds; // --report= + bool Execute = false; // Report requested. + }; + + struct LVSelect { + bool IgnoreCase = false; // --select-ignore-case + bool UseRegex = false; // --select-use-regex + bool Execute = false; // Select requested. + bool GenericKind = false; // We have collected generic kind. + 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= + }; + + struct LVOutput { + LVOutputKindSet Kinds; // --output= + LVSortMode SortMode = LVSortMode::None; // --output-sort= + std::string Folder; // --output-folder= + unsigned Level = -1U; // --output-level= + }; + + struct LVWarning { + LVWarningKindSet Kinds; // --warning= + }; + + struct LVInternal { + LVInternalKindSet Kinds; // --internal= + }; + + struct LVGeneral { + 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 set to zero, 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=(LVOptions const &) = default; + ~LVOptions() {} + + // 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(Location); + ATTRIBUTE_OPTION(Local); + ATTRIBUTE_OPTION(Offset); + ATTRIBUTE_OPTION(Operation); + 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); + + // --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, AnyLine); + BOOL_FUNCTION(Print, Execute); + BOOL_FUNCTION(Print, Formatting); + BOOL_FUNCTION(Print, Generic); + BOOL_FUNCTION(Print, Offset); + + // --report. + REPORT_OPTION(All); + REPORT_OPTION(Details); + REPORT_OPTION(Summary); + REPORT_OPTION(View); + 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(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 { + // Pattern Mode. + enum class LVMatchMode { + None = 0, // No given pattern. + Match, // Perfect match. + 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 LVMatchOffset = std::vector; + LVMatchOffset OffsetMatchInfo; + + // Element selection. + LVElementTarget ElementTarget; + LVLineTarget LineTarget; + LVScopeTarget ScopeTarget; + LVSymbolTarget SymbolTarget; + LVTypeTarget TypeTarget; + + // 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 { + for (const auto &Request : Requests) + if ((Element->*Request)()) + return true; + // Check generic element requests. + for (const auto &Request : ElementRequest) + if ((Element->*Request)()) + return true; + return false; + } + + // Add an element printing request based on its kind. + template + void addRequest(const T &Selection, U &Target, V &Request) const { + for (auto &Entry : Selection) { + // Find target function to fullfit request. + typename U::iterator I = Target.find(Entry); + if (I != Target.end()) + Request.push_back(I->second); + } + } + + void addElement(LVElement *Element); + + template + void resolveGenericPatternMatch(T *Element, const U &Requests) { + auto checkPattern = [&]() -> auto { + return Element->isNamed() && + (matchGenericPattern(Element->getName()) || + matchGenericPattern(Element->getTypeName())); + }; + auto checkOffset = [&]() -> auto { + 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) { + auto checkPattern = [&]() -> auto { + return matchGenericPattern(Line->lineNumberAsStringStripped()) || + matchGenericPattern(Line->getName()); + }; + if ((options().getSelectGenericPattern() && checkPattern()) || + (Requests.size() && checkElementRequest(Line, Requests))) + addElement(Line); + } + + Error createMatchEntry(LVMatchInfo &Filters, StringRef Pattern, + bool IgnoreCase, bool UseRegex); + +public: + static LVPatterns *getPatterns(); + +public: + LVPatterns() { + ElementTarget = LVElement::getTargets(); + LineTarget = LVLine::getTargets(); + ScopeTarget = LVScope::getTargets(); + SymbolTarget = LVSymbol::getTargets(); + TypeTarget = LVType::getTargets(); + } + LVPatterns(const LVPatterns &) = delete; + LVPatterns &operator=(LVPatterns const &) = default; + ~LVPatterns() {} + + void addRequest(LVElementKindSet &Set) { + addRequest(Set, ElementTarget, ElementRequest); + } + void addRequest(LVLineKindSet &Set) { + addRequest(Set, LineTarget, LineRequest); + } + void addRequest(LVScopeKindSet &Set) { + addRequest(Set, ScopeTarget, ScopeRequest); + } + void addRequest(LVSymbolKindSet &Set) { + addRequest(Set, SymbolTarget, SymbolRequest); + } + void addRequest(LVTypeKindSelection &Selection) { + addRequest(Selection, TypeTarget, TypeRequest); + } + + void updateReportOptions(); + + bool matchPattern(StringRef Input, LVMatchInfo &MatchInfo); + // Match a pattern (--select 'pattern'). + bool matchGenericPattern(StringRef Input) { + return matchPattern(Input, GenericMatchInfo); + } + bool matchOffsetPattern(uint64_t 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(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,100 @@ +//===-- 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" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace logicalview { + +using LVAddressRange = std::pair; + +class LVRangeEntry { + LVAddress Low = 0; + LVAddress High = 0; + LVScope *Scope = nullptr; + +public: + using RangeType = LVAddress; + +public: + LVRangeEntry() = delete; + LVRangeEntry(LVAddress Low, LVAddress High, LVScope *Scope) + : Low(Low), High(High), Scope(Scope) {} + +public: + RangeType low() const { return Low; } + RangeType high() const { return High; } + LVAddressRange addressRange() const { return LVAddressRange(low(), high()); } + LVScope *scope() const { return Scope; } +}; + +// Class to represent a list of range addresses associated with a +// scope; the addresses are stored in ascending order. +class LVRange final : public LVObject { + using LVRangeEntries = std::vector; + + /// 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 Higher = 0; + +public: + LVRange() : LVObject(), RangesTree(Allocator) {} + LVRange(const LVRange &) = delete; + LVRange &operator=(LVRange const &) = delete; + ~LVRange() {} + +public: + void addEntry(LVScope *Scope, LVAddress LowAddress, LVAddress HighAddress); + void addEntry(LVScope *Scope); + LVScope *getEntry(LVAddress Address); + LVScope *getEntry(LVAddress LowAddress, LVAddress HighAddress); + bool findEntry(LVAddress Low, LVAddress High); + LVAddress getLower() { return Lower; } + LVAddress getHigher() { return Higher; } + + void clear() { + RangeEntries.clear(); + Lower = MaxAddress; + Higher = 0; + } + bool empty() { 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,231 @@ +//===-- 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/LVObject.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVRange.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace llvm { +namespace logicalview { + +class LVScopeCompileUnit; +class LVObject; + +class LVSplitContext { + std::unique_ptr OutputFile = nullptr; + std::string Location; + +public: + LVSplitContext() = default; + LVSplitContext(const LVSplitContext &) = delete; + LVSplitContext &operator=(LVSplitContext const &) = delete; + ~LVSplitContext(){}; + +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(); + } + + std::string getLocation() const { return Location; } + raw_fd_ostream &os() { return OutputFile->os(); } +}; + +class LVReader { + // Context used by '--output=split' command line option. + LVSplitContext SplitContext; + + // Compile Units DIE Offset => Scope. + using LVCompileUnits = std::map; + LVCompileUnits CompileUnits; + + // Compile Unit Offsets. + LVOffsets CompileUnitOffsets; + + // 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 Filename; + std::string FileFormatName; + ScopedPrinter &W; + raw_ostream &OS; + LVScopeCompileUnit *CompileUnit = nullptr; + + // Mapping from virtual address to section. + // The virtual address refers to the address where the section is loaded. + using LVSectionAddresses = std::map; + LVSectionAddresses SectionAddresses; + + // Scopes with ranges for current compile unit. + LVRange ScopesWithRanges; + + // It contains the LVLineDebug objects representing the logical lines for + // the current compile unit, created by parsing the debug line section. + LVLineList CULines; + // It contains the LVLineSource objects representing the disassembled + // instructions, created by parsing the text section. + LVLineList CUInstructions; + + // Create a mapping from virtual address to section. + virtual void mapVirtualAddress(const object::ObjectFile &Obj); + virtual void mapRangeAddress(const object::ObjectFile &Obj, + const object::SectionRef &Sec) {} + + // Record Compilation Unit entry. + void addCompileUnitOffset(LVOffset Offset, LVScopeCompileUnit *CompileUnit) { + CompileUnitOffsets.push_back(Offset); + CompileUnits.insert(std::make_pair(Offset, CompileUnit)); + } + + void processLines(); + + // 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(); + virtual void sortScopes() {} + +public: + LVReader() = delete; + LVReader(StringRef Filename, StringRef FileFormatName, ScopedPrinter &W) + : OutputSplit(options().getOutputSplit()), Filename(Filename), + FileFormatName(FileFormatName), W(W), OS(W.getOStream()) {} + LVReader(const LVReader &) = delete; + LVReader &operator=(LVReader const &) = delete; + virtual ~LVReader() { + if (Root) + delete Root; + } + + StringRef getFilename(LVObject *Object, size_t Index) const; + StringRef getFilename() const { return Filename; } + void setFilename(std::string Name) { Filename = Name; } + StringRef getFileFormatName() const { return FileFormatName; } + + raw_ostream &outputStream() { return OS; } + +public: + LVScopeCompileUnit *getCompileUnit() { return CompileUnit; } + void setCompileUnit(LVScope *Scope) { + CompileUnit = static_cast(Scope); + } + +public: + // Access to the scopes root. + LVScopeRoot *getScopesRoot() const { return Root; } + + Error doPrint(); + Error doLoad(); + + virtual std::string getOpcodeName(LVSmall Opcode) { return {}; } + virtual std::string getRegisterName(LVSmall Opcode, uint64_t Operands[2]) { + return {}; + } + + // 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; + +#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,856 @@ +//===-- 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/ADT/BitVector.h" +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSort.h" +#include "llvm/DebugInfo/LogicalView/Core/LVStringPool.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace llvm { +namespace logicalview { + +class LVRange; + +enum class LVScopeKind { + Aggregate, + Array, + Block, + CatchBlock, + Class, + CompileUnit, + EntryPoint, + Enumeration, + Function, + FunctionType, + InlinedFunction, + Label, + LexicalBlock, + Member, + Namespace, + Root, + Structure, + Subprogram, + Template, + TemplateAlias, + TemplatePack, + TryBlock, + Union, + LastEntry +}; +using LVScopeKindSet = std::set; +using LVScopeSetFunction = void (LVScope::*)(); +using LVScopeGetFunction = bool (LVScope::*)() const; +using LVScopeTarget = std::map; +using LVScopeRequest = std::vector; + +using LVOffsetElementMap = std::map; + +// Class to represent a DWARF Scope. +class LVScope : public LVElement { + enum class Property { + HasDiscriminator, + CanHaveRanges, + CanHaveLines, + HasGlobals, + HasLocals, + HasLines, + HasScopes, + HasSymbols, + HasTypes, + LastEntry + }; + + // Typed bitvector with kinds and properties for this scope. + LVProperties Kinds; + LVProperties Properties; + static LVScopeTarget Targets; + + // 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); + + void traverse(LVScopeGetFunction GetFunction, LVScopeSetFunction SetFunction); + void traverse(LVObjectGetFunction GetFunction, + LVObjectSetFunction SetFunction); + +protected: + // Types, Symbols, Scopes, Lines, Locations in this scope. + LVTypes *Types = nullptr; + LVSymbols *Symbols = nullptr; + LVScopes *Scopes = nullptr; + LVLines *Lines = nullptr; + LVLocations *Ranges = nullptr; + + // Vector of objects (types, scopes, symbols, lines). + 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 {}; + +public: + LVScope() : LVElement() { + setIsScope(); + setIncludeInPrint(); + } + LVScope(const LVScope &) = delete; + LVScope &operator=(LVScope const &) = delete; + virtual ~LVScope(); + + bool getIsAggregate() const { return Kinds.get(LVScopeKind::Aggregate); } + void setIsAggregate() { Kinds.set(LVScopeKind::Aggregate); } + + bool getIsArray() const { return Kinds.get(LVScopeKind::Array); } + void setIsArray() { Kinds.set(LVScopeKind::Array); } + + bool getIsBlock() const { return Kinds.get(LVScopeKind::Block); } + void setIsBlock() { + Kinds.set(LVScopeKind::Block); + setCanHaveRanges(); + setCanHaveLines(); + } + + bool getIsCatchBlock() const { return Kinds.get(LVScopeKind::CatchBlock); } + void setIsCatchBlock() { + Kinds.set(LVScopeKind::CatchBlock); + setIsBlock(); + } + + bool getIsClass() const { return Kinds.get(LVScopeKind::Class); } + void setIsClass() { + Kinds.set(LVScopeKind::Class); + setIsAggregate(); + } + + bool getIsCompileUnit() const override { + return Kinds.get(LVScopeKind::CompileUnit); + } + void setIsCompileUnit() { + Kinds.set(LVScopeKind::CompileUnit); + setCanHaveRanges(); + setCanHaveLines(); + setTransformName(); + } + + bool getIsEntryPoint() const { return Kinds.get(LVScopeKind::EntryPoint); } + void setIsEntryPoint() { + Kinds.set(LVScopeKind::EntryPoint); + setIsFunction(); + } + + bool getIsEnumeration() const { return Kinds.get(LVScopeKind::Enumeration); } + void setIsEnumeration() { Kinds.set(LVScopeKind::Enumeration); } + + bool getIsFunction() const { return Kinds.get(LVScopeKind::Function); } + void setIsFunction() { + Kinds.set(LVScopeKind::Function); + setCanHaveRanges(); + setCanHaveLines(); + } + + bool getIsFunctionType() const { + return Kinds.get(LVScopeKind::FunctionType); + } + void setIsFunctionType() { + Kinds.set(LVScopeKind::FunctionType); + setIsFunction(); + } + + bool getIsInlinedFunction() const { + return Kinds.get(LVScopeKind::InlinedFunction); + } + void setIsInlinedFunction() { + Kinds.set(LVScopeKind::InlinedFunction); + setIsFunction(); + setIsInlined(); + } + + bool getIsLabel() const { return Kinds.get(LVScopeKind::Label); } + void setIsLabel() { + Kinds.set(LVScopeKind::Label); + setIsFunction(); + } + + bool getIsLexicalBlock() const { + return Kinds.get(LVScopeKind::LexicalBlock); + } + void setIsLexicalBlock() { + Kinds.set(LVScopeKind::LexicalBlock); + setIsBlock(); + } + + bool getIsMember() const { return Kinds.get(LVScopeKind::Member); } + void setIsMember() { Kinds.set(LVScopeKind::Member); } + + bool getIsNamespace() const { return Kinds.get(LVScopeKind::Namespace); } + void setIsNamespace() { Kinds.set(LVScopeKind::Namespace); } + + bool getIsRoot() const override { return Kinds.get(LVScopeKind::Root); } + void setIsRoot() { + Kinds.set(LVScopeKind::Root); + setTransformName(); + } + + bool getIsSubprogram() const { return Kinds.get(LVScopeKind::Subprogram); } + void setIsSubprogram() { + Kinds.set(LVScopeKind::Subprogram); + setIsFunction(); + } + + bool getIsStruct() const { return Kinds.get(LVScopeKind::Structure); } + void setIsStruct() { + Kinds.set(LVScopeKind::Structure); + setIsAggregate(); + } + + bool getIsTemplate() const { return Kinds.get(LVScopeKind::Template); } + void setIsTemplate() { Kinds.set(LVScopeKind::Template); } + + bool getIsTemplateAlias() const { + return Kinds.get(LVScopeKind::TemplateAlias); + } + void setIsTemplateAlias() { Kinds.set(LVScopeKind::TemplateAlias); } + + bool getIsTemplatePack() const { + return Kinds.get(LVScopeKind::TemplatePack); + } + void setIsTemplatePack() { Kinds.set(LVScopeKind::TemplatePack); } + + bool getIsTryBlock() const { return Kinds.get(LVScopeKind::TryBlock); } + void setIsTryBlock() { + Kinds.set(LVScopeKind::TryBlock); + setIsBlock(); + } + + bool getIsUnion() const { return Kinds.get(LVScopeKind::Union); } + void setIsUnion() { + Kinds.set(LVScopeKind::Union); + setIsAggregate(); + } + + bool getHasDiscriminator() const { + return Properties.get(Property::HasDiscriminator); + } + void setHasDiscriminator() { Properties.set(Property::HasDiscriminator); } + + bool getCanHaveRanges() const { + return Properties.get(Property::CanHaveRanges); + } + void setCanHaveRanges() { Properties.set(Property::CanHaveRanges); } + + bool getCanHaveLines() const { + return Properties.get(Property::CanHaveLines); + } + void setCanHaveLines() { Properties.set(Property::CanHaveLines); } + + bool getHasGlobals() const { return Properties.get(Property::HasGlobals); } + void setHasGlobals() { Properties.set(Property::HasGlobals); } + + bool getHasLocals() const { return Properties.get(Property::HasLocals); } + void setHasLocals() { Properties.set(Property::HasLocals); } + + bool getHasLines() const { return Properties.get(Property::HasLines); } + void setHasLines() { Properties.set(Property::HasLines); } + + bool getHasScopes() const { return Properties.get(Property::HasScopes); } + void setHasScopes() { Properties.set(Property::HasScopes); } + + bool getHasSymbols() const { return Properties.get(Property::HasSymbols); } + void setHasSymbols() { Properties.set(Property::HasSymbols); } + + bool getHasTypes() const { return Properties.get(Property::HasTypes); } + void setHasTypes() { Properties.set(Property::HasTypes); } + +public: + 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; } + + 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 LowAddress, LVAddress HighAddress); + 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); + + void traverse(LVObjectGetFunction GetFunction, + LVObjectSetFunction SetFunction, bool Down); + + // 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); + } + + // Get containing parent for the given address. + LVScope *getOutermostParent(LVAddress Address); + + // Get all the locations associated with symbols. + void getLocations(LVLocations *LocationList, + LVCheckLocation CheckLocation) override; + void getRanges(LVLocations *LocationList, LVCheckLocation CheckLocation); + 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; + 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 nullScope(); } + + StringRef resolveChainName(); + + bool removeElement(LVElement *Element) override; + void updateLevel(LVScope *Parent, bool Moved) override; + + virtual void resolve() override; + virtual void resolveName() override; + virtual void resolveReferences() override; + + // Return the chain of parents as a string. + void getQualifiedName(std::string &QualifiedName); + // Encode the template arguments. + void encodeTemplateArguments(std::string &Name, const LVTypes *Types, + bool QualifyBase = false); + + void resolveElements(); + +public: + // A function to iterate through the reference set and check that + // all it's members are present in the target set. + static void isContainedIn(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 isContainedIn(const LVScope *Target, bool TraverseChildren); + + // Returns true if 'current' scope is equal to the given 'scope'. + virtual bool sameChildren(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 LVScopeTarget &getTargets() { return Targets; } + +public: + virtual void print(raw_ostream &OS, bool Full = true) const override; + virtual 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){}; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + virtual 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=(LVScopeAggregate const &) = delete; + ~LVScopeAggregate() {} + + // DW_AT_specification, DW_AT_abstract_origin. + LVScope *getReference() const override { return Reference; } + void setReference(LVScope *Scope) override { + Reference = Scope; + setHasReference(); + } + virtual 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=(LVScopeAlias const &) = delete; + ~LVScopeAlias() {} + + // 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=(LVScopeArray const &) = delete; + ~LVScopeArray() {} + + 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. + LVPublicNames PublicNames; + + // Toolchain producer + size_t ProducerIndex = 0; + + // 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; + + // Address to line mapping. + using LVAddressToLine = std::map; + LVAddressToLine AddressToLine; + + using LVOffsetList = std::list; + using LVTagOffsetsMap = std::map; + using LVOffsetLocationsMap = std::map; + using LVOffsetLinesMap = std::map; + + // 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. + using LVOffsetSymbolMap = std::map; + 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; + + // 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; + + 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=(LVScopeCompileUnit const &) = delete; + ~LVScopeCompileUnit() {} + + // Add line to address mapping. + void addMapping(LVAddress Address, LVLine *Line); + // Get the line located at the given address. + LVLine *getLowerLine(LVAddress Address); + LVLine *getUpperLine(LVAddress Address); + + void addPublicName(LVAddress LowPC, LVAddress HighPC, LVScope *Scope) { + PublicNames.insert( + std::make_pair(LowPC, LVNameInfo(Scope, HighPC - LowPC))); + } + LVPublicNames const &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() { + return Ranges ? Ranges->front()->getLowerAddress() : 0; + } + + 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); + } + + // Record DWARF tags. + void addDebugTag(dwarf::Tag Target, LVOffset Offset); + // 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); + + // Process ranges, locations and calculate coverage. + void processRangeLocationCoverage(); + + // Add matched element. + void addMatched(LVElement *Element) { MatchedElements.push_back(Element); } + void addMatched(LVScope *Scope) { MatchedScopes.push_back(Scope); } + void propagatePatternMatch(); + + 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) 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=(LVScopeEnumeration const &) = delete; + ~LVScopeEnumeration() {} + + // 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=(LVScopeFormalPack const &) = delete; + ~LVScopeFormalPack() {} + + // 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=(LVScopeFunction const &) = delete; + virtual ~LVScopeFunction() {} + + // 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 { + // Check for system generated functions. + LVScope::setName(ObjectName); + markSystemEntry(ObjectName); + } + + virtual void resolveExtra() override; + virtual void resolveReferences() override; + + // Returns true if 'current' scope is equal to the given 'scope'. + virtual bool equals(const LVScope *Scope) const override; + virtual 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=(LVScopeFunctionInlined const &) = delete; + ~LVScopeFunctionInlined() {} + + 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=(LVScopeFunctionType const &) = delete; + ~LVScopeFunctionType() {} + + 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=(LVScopeNamespace const &) = delete; + ~LVScopeNamespace() {} + + // 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=(LVScopeRoot const &) = delete; + ~LVScopeRoot() {} + + 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) 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=(LVScopeTemplatePack const &) = delete; + ~LVScopeTemplatePack() {} + + // 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,100 @@ +//===-- 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 { + const size_t BadIndex = std::numeric_limits::max(); + using TableType = StringMap; + using ValueType = TableType::value_type; + TableType StringTable; + std::vector Entries; + BumpPtrAllocator Allocator; + + LVStringPool() { getIndex(""); } + LVStringPool(LVStringPool const &other) = delete; + LVStringPool(LVStringPool &&other) = delete; + ~LVStringPool() {} + +public: + bool isValidIndex(size_t Index) { 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() { return Entries.size() - 1; } + + // Return the index for the specified key, otherwise 'BadIndex'. + size_t findIndex(StringRef Key) { + TableType::const_iterator it = StringTable.find(Key); + if (it != StringTable.end()) + return it->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) { + 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 auto &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,132 @@ +//===-- 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/BitVector.h" +#include "llvm/ADT/Twine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVBasicDefinitions.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace llvm { +namespace logicalview { + +using LVStringRefs = std::vector; +using LVLexicalComponent = std::tuple; +using LVLexicalIndex = + std::tuple; + +// Used to record specific characteristics about the objects. +template class LVProperties { + BitVector Bits = BitVector(static_cast(T::LastEntry) + 1); + +public: + LVProperties() {} + + 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)]; + } +}; + +// Output the hexadecimal representation of 'Value' using '[0x%08x]' format. +inline std::string hexString(uint64_t Value, size_t Width = 10) { + std::string String; + raw_string_ostream Stream(String); + Stream << format_hex(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 auto &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->insert(std::make_pair(Key, List)); + } + List->push_back(Value); +} + +// 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(LVStringRefs Components, StringRef BaseName = {}); + +// Convert Segment::Offset pair to absolute address. +inline LVAddress segmentToLinear(uint16_t Segment, uint32_t Offset, + LVAddress addendum = 0) { + return addendum + ((Segment * 0x10) + Offset); +} + +} // 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,202 @@ +//===-- 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/ADT/BitVector.h" +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { +namespace logicalview { + +enum class LVSymbolKind { + Constant, + Inheritance, + Member, + Parameter, + Unspecified, + Variable, + LastEntry +}; +using LVSymbolKindSet = std::set; +using LVSymbolGetFunction = bool (LVSymbol::*)() const; +using LVSymbolTarget = 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 LVSymbolTarget Targets; + + // CodeView symbol Linkage name. + size_t LinkageNameIndex = 0; + + // Reference to DW_AT_specification, DW_AT_abstract_origin attribute. + LVSymbol *Reference = nullptr; + LVLocations *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. + LVLocations::iterator addLocationGap(LVLocations::iterator Pos, + LVAddress LowPC, LVAddress HighPC); + + // Find the 'current' symbol in the 'targets'. + LVSymbol *findIn(const LVSymbols *Targets); + +public: + LVSymbol() : LVElement() { + setIsSymbol(); + setIncludeInPrint(); + } + LVSymbol(const LVSymbol &) = delete; + LVSymbol &operator=(LVSymbol const &) = delete; + ~LVSymbol(); + + bool getIsConstant() const { return Kinds.get(LVSymbolKind::Constant); } + void setIsConstant() { Kinds.set(LVSymbolKind::Constant); } + + bool getIsInheritance() const { return Kinds.get(LVSymbolKind::Inheritance); } + void setIsInheritance() { Kinds.set(LVSymbolKind::Inheritance); } + + bool getIsMember() const { return Kinds.get(LVSymbolKind::Member); } + void setIsMember() { Kinds.set(LVSymbolKind::Member); } + + bool getIsParameter() const { return Kinds.get(LVSymbolKind::Parameter); } + void setIsParameter() { Kinds.set(LVSymbolKind::Parameter); } + + bool getIsUnspecified() const { return Kinds.get(LVSymbolKind::Unspecified); } + void setIsUnspecified() { Kinds.set(LVSymbolKind::Unspecified); } + + bool getIsVariable() const { return Kinds.get(LVSymbolKind::Variable); } + void setIsVariable() { Kinds.set(LVSymbolKind::Variable); } + + bool getHasLocation() const { return Properties.get(Property::HasLocation); } + void setHasLocation() { Properties.set(Property::HasLocation); } + + bool getFillGaps() const { return Properties.get(Property::FillGaps); } + void setFillGaps() { Properties.set(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 { + 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(LVSmall Opcode, LVUnsigned Constant, + uint64_t LocDescOffset); + void addLocationOperands(LVSmall Opcode, uint64_t Operand1, + uint64_t Operand2); + void addLocation(LVSmall Opcode, LVAddress LowPC, LVAddress HighPC, + LVUnsigned SectionOffset, uint64_t LocDescOffset); + + // Fill gaps in the location list. + void fillLocationGaps(); + + // Get all the locations associated with symbols. + void getLocations(LVLocations *LocationList, + LVCheckLocation CheckLocation) override; + + // 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; + + StringRef resolveChainName(); + + void resolveName() override; + void resolveReferences() override; + +public: + static LVSymbolTarget &getTargets() { return Targets; } + +public: + static bool parametersMatch(const LVSymbols *References, + const LVSymbols *Targets); + + static void getParameters(const LVSymbols *Symbols, LVSymbols *Parameters); + + // A function to iterate through the reference set and check that + // all it's members are present in the target set. + static void isContainedIn(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,355 @@ +//===-- 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/ADT/BitVector.h" +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { +namespace logicalview { + +enum class LVTypeKind { + Base, + Const, + Enumerator, + Import, + ImportDeclaration, + ImportModule, + Pointer, + PointerMember, + Reference, + Restrict, + RvalueReference, + Subrange, + TemplateParam, + TemplateTemplateParam, + TemplateTypeParam, + TemplateValueParam, + Typedef, + Unspecified, + Volatile, + Modifier, // CodeView - LF_MODIFIER + LastEntry +}; +using LVTypeKindSelection = std::set; +using LVTypeGetFunction = bool (LVType::*)() const; +using LVTypeTarget = std::map; +using LVTypeRequest = std::vector; + +// Class to represent a DWARF Type. +class LVType : public LVElement { + enum class Property { SubrangeCount, LastEntry }; + + // Typed bitvector with kinds and properties for this type. + LVProperties Kinds; + LVProperties Properties; + static LVTypeTarget Targets; + + // Find the 'current' type in the 'targets' set. + LVType *findIn(const LVTypes *Targets); + +public: + LVType() : LVElement() { setIsType(); } + LVType(const LVType &) = delete; + LVType &operator=(LVType const &) = delete; + virtual ~LVType() {} + + bool getIsBase() const override { return Kinds.get(LVTypeKind::Base); } + void setIsBase() { Kinds.set(LVTypeKind::Base); } + + bool getIsConst() const { return Kinds.get(LVTypeKind::Const); } + void setIsConst() { Kinds.set(LVTypeKind::Const); } + + bool getIsEnumerator() const { return Kinds.get(LVTypeKind::Enumerator); } + void setIsEnumerator() { Kinds.set(LVTypeKind::Enumerator); } + + bool getIsImport() const { return Kinds.get(LVTypeKind::Import); } + void setIsImport() { Kinds.set(LVTypeKind::Import); } + + bool getIsImportDeclaration() const { + return Kinds.get(LVTypeKind::ImportDeclaration); + } + void setIsImportDeclaration() { + Kinds.set(LVTypeKind::ImportDeclaration); + setIsImport(); + } + + bool getIsImportModule() const { return Kinds.get(LVTypeKind::ImportModule); } + void setIsImportModule() { + Kinds.set(LVTypeKind::ImportModule); + setIsImport(); + } + + bool getIsPointer() const { return Kinds.get(LVTypeKind::Pointer); } + void setIsPointer() { Kinds.set(LVTypeKind::Pointer); } + + bool getIsPointerMember() const { + return Kinds.get(LVTypeKind::PointerMember); + } + void setIsPointerMember() { Kinds.set(LVTypeKind::PointerMember); } + + bool getIsReference() const { return Kinds.get(LVTypeKind::Reference); } + void setIsReference() { Kinds.set(LVTypeKind::Reference); } + + bool getIsRestrict() const { return Kinds.get(LVTypeKind::Restrict); } + void setIsRestrict() { Kinds.set(LVTypeKind::Restrict); } + + bool getIsRvalueReference() const { + return Kinds.get(LVTypeKind::RvalueReference); + } + void setIsRvalueReference() { Kinds.set(LVTypeKind::RvalueReference); } + + bool getIsSubrange() const { return Kinds.get(LVTypeKind::Subrange); } + void setIsSubrange() { Kinds.set(LVTypeKind::Subrange); } + + bool getIsTemplateParam() const { + return Kinds.get(LVTypeKind::TemplateParam); + } + void setIsTemplateParam() { Kinds.set(LVTypeKind::TemplateParam); } + + bool getIsTemplateTemplate() const { + return Kinds.get(LVTypeKind::TemplateTemplateParam); + } + void setIsTemplateTemplate() { + Kinds.set(LVTypeKind::TemplateTemplateParam); + setIsTemplateParam(); + } + + bool getIsTemplateType() const { + return Kinds.get(LVTypeKind::TemplateTypeParam); + } + void setIsTemplateType() { + Kinds.set(LVTypeKind::TemplateTypeParam); + setIsTemplateParam(); + } + + bool getIsTemplateValue() const { + return Kinds.get(LVTypeKind::TemplateValueParam); + } + void setIsTemplateValue() { + Kinds.set(LVTypeKind::TemplateValueParam); + setIsTemplateParam(); + } + + bool getIsTypedef() const { return Kinds.get(LVTypeKind::Typedef); } + void setIsTypedef() { Kinds.set(LVTypeKind::Typedef); } + + bool getIsUnspecified() const { return Kinds.get(LVTypeKind::Unspecified); } + void setIsUnspecified() { Kinds.set(LVTypeKind::Unspecified); } + + bool getIsVolatile() const { return Kinds.get(LVTypeKind::Volatile); } + void setIsVolatile() { Kinds.set(LVTypeKind::Volatile); } + + bool getIsModifier() const { return Kinds.get(LVTypeKind::Modifier); } + void setIsModifier() { Kinds.set(LVTypeKind::Modifier); } + + bool getIsSubrangeCount() const { + return Properties.get(Property::SubrangeCount); + } + void setIsSubrangeCount() { Properties.set(Property::SubrangeCount); } + +public: + const char *kind() const override; + + StringRef resolveChainName(); + + bool isTemplateParam() const override { return getIsTemplateParam(); } + + // Encode the specific template argument. + virtual void encodeTemplateArgument(std::string &Name) {} + + // Return the underlying type for a type definition. + virtual LVElement *getUnderlyingType() { return nullElement(); } + virtual void setUnderlyingType(LVElement *Element) {} + + void resolveName() override; + void resolveReferences() override; + +public: + static LVTypeTarget &getTargets() { return Targets; } + +public: + static bool parametersMatch(const LVTypes *References, + const LVTypes *Targets); + + static void getParameters(const LVTypes *Types, LVTypes *TypesParam, + LVScopes *ScopesParam); + + // A function to iterate through the reference set and check that + // all it's members are present in the target set. + static void isContainedIn(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: + virtual void print(raw_ostream &OS, bool Full = true) const override; + virtual void printExtra(raw_ostream &OS, bool Full = true) const override; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + virtual 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=(LVTypeDefinition const &) = delete; + ~LVTypeDefinition() {} + + // 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=(LVTypeEnumerator const &) = delete; + ~LVTypeEnumerator() {} + + // 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=(LVTypeImport const &) = delete; + ~LVTypeImport() {} + + // 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=(LVTypeParam const &) = delete; + ~LVTypeParam() {} + + // 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) 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=(LVTypeSubrange const &) = delete; + ~LVTypeSubrange() {} + + 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,102 @@ +//===-- 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/LVOptions.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/Debug.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +namespace llvm { +namespace pdb { +class PDBFile; +} +namespace logicalview { + +class LVReader; +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); + Error handleFile(LVReaders &Readers, StringRef Filename); + 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=(LVReaderHandler const &) = delete; + ~LVReaderHandler() { destroyReaders(); } + + Error createReader(StringRef Filename, LVReaders &Readers) { + return handleFile(Readers, Filename); + } + Error process(); + +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/LVCodeViewReader.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h @@ -0,0 +1,217 @@ +//===-- 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/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.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 mapped them to the DWARF ones. + +class LVCodeViewReader final : public LVReader { + 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 = nullptr; + std::shared_ptr PrecompHeader = nullptr; + + // Persistance data when loading a type server. + ErrorOr> BuffOrErr = nullptr; + std::unique_ptr MemBuffer = nullptr; + std::unique_ptr Session = nullptr; + std::unique_ptr PdbSession = nullptr; + + // Persistance data when loading a precompiled header. + BumpPtrAllocator BuilderAllocator; + std::unique_ptr Builder = nullptr; + std::unique_ptr> ItemStream = nullptr; + std::unique_ptr ReaderPrecomp = nullptr; + std::vector TypeArray; + CVTypeArray TypeStream; + CVTypeArray CVTypesPrecomp; + + 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. + StringRef 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); + + Error loadPrecompiledObject(PrecompRecord &Precomp, CVTypeArray &CVTypesObj); + Error loadTypeServer(TypeServer2Record &TS); + Error traverseTypes(llvm::pdb::PDBFile &Pdb, LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids); + + 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 resolveSymbolName(const llvm::object::coff_section *CoffSection, + StringRef SectionContents, const void *RelocPtr, + 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); + void mapRangeAddress(const object::ObjectFile &Obj, + const object::SectionRef &Sec) override; + + 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); + void processModule(); + +protected: + Error createScopes() override; + void sortScopes() override; + +public: + LVCodeViewReader() = delete; + LVCodeViewReader(StringRef Filename, StringRef FileFormatName, + llvm::object::COFFObjectFile &Obj, ScopedPrinter &W, + StringRef ExePath) + : LVReader(Filename, FileFormatName, W), Input(&Obj), ExePath(ExePath), + LogicalVisitor(this, W, Input) {} + LVCodeViewReader(StringRef Filename, StringRef FileFormatName, + llvm::pdb::PDBFile &Pdb, ScopedPrinter &W, StringRef ExePath) + : LVReader(Filename, FileFormatName, W), Input(&Pdb), ExePath(ExePath), + LogicalVisitor(this, W, Input) {} + LVCodeViewReader(const LVCodeViewReader &) = delete; + LVCodeViewReader &operator=(LVCodeViewReader const &) = delete; + ~LVCodeViewReader() {} + + 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]; + } + +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_CODEVIEWREADER_H Index: llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h @@ -0,0 +1,616 @@ +//===-- 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/PointerUnion.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/iterator.h" +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.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/Core/LVBasicDefinitions.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Error.h" +#include +#include +#include +#include + +//===----------------------------------------------------------------------===// +// This code has been extracted from llvm-pdbutil module. For the time being +// it used just to get the PDB reading going. After that, see what kind of +// refactoring can be done and move it to the debug info libraries. +//===----------------------------------------------------------------------===// +// START +//===----------------------------------------------------------------------===// + +namespace llvm { +namespace codeview { +class LazyRandomTypeCollection; +} +namespace object { +class COFFObjectFile; +class SectionRef; +} // namespace object + +namespace pdb { +class PDBFile; +class SymbolGroupIterator; +class SymbolGroup; + +class InputFile { + InputFile() = default; + PointerUnion PdbOrObj; + + using TypeCollectionPtr = std::unique_ptr; + TypeCollectionPtr TpiTypes; + TypeCollectionPtr IpiTypes; + +public: + InputFile(PDBFile *Pdb); + InputFile(object::COFFObjectFile *Obj); + InputFile(MemoryBuffer *Buffer); + ~InputFile() = default; + InputFile(InputFile &&Other) = default; + + PDBFile &pdb(); + const PDBFile &pdb() const; + object::COFFObjectFile &obj(); + const object::COFFObjectFile &obj() const; + MemoryBuffer &unknown(); + const MemoryBuffer &unknown() const; + + StringRef getFilePath() const; + + enum TypeCollectionKind { kTypes, kIds }; + + bool hasTypes() const; + bool hasIds() const; + + codeview::LazyRandomTypeCollection &types(); + codeview::LazyRandomTypeCollection &ids(); + codeview::LazyRandomTypeCollection & + getOrCreateTypeCollection(TypeCollectionKind Kind); + + iterator_range symbol_groups(); + SymbolGroupIterator symbol_groups_begin(); + SymbolGroupIterator symbol_groups_end(); + + bool isPdb() const; + bool isObj() const; + bool isUnknown() const; +}; + +class SymbolGroup { + friend class SymbolGroupIterator; + +public: + explicit SymbolGroup(InputFile *File, uint32_t GroupIndex = 0); + + Expected getNameFromChecksums(uint32_t Offset) const; + Expected getNameFromStringTable(uint32_t Offset) const; + StringRef name() const; + + codeview::DebugSubsectionArray getDebugSubsections() const { + return Subsections; + } + const ModuleDebugStreamRef &getPdbModuleStream() const; + + const InputFile &getFile() const { return *File; } + InputFile &getFile() { return *File; } + + bool hasDebugStream() const { return DebugStream != nullptr; } + +private: + void initializeForPdb(uint32_t Modi); + void updatePdbModi(uint32_t Modi); + void updateDebugS(const codeview::DebugSubsectionArray &SS); + + void rebuildChecksumMap(); + InputFile *File = nullptr; + StringRef Name; + codeview::DebugSubsectionArray Subsections; + std::shared_ptr DebugStream; + codeview::StringsAndChecksumsRef SC; + StringMap ChecksumsByFile; +}; + +class SymbolGroupIterator + : public iterator_facade_base { +public: + SymbolGroupIterator(); + explicit SymbolGroupIterator(InputFile &File); + SymbolGroupIterator(const SymbolGroupIterator &Other) = default; + SymbolGroupIterator &operator=(const SymbolGroupIterator &R) = default; + + const SymbolGroup &operator*() const; + SymbolGroup &operator*(); + + bool operator==(const SymbolGroupIterator &R) const; + SymbolGroupIterator &operator++(); + +private: + void scanToNextDebugS(); + bool isEnd() const; + + uint32_t Index = 0; + Optional SectionIter; + SymbolGroup Value; +}; + +bool shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group); + +Expected getModuleDebugStream(PDBFile &File, + uint32_t Index); + +Expected +getModuleDebugStream(PDBFile &File, StringRef &ModuleName, uint32_t Index); + +template +Error iterateOneModule(InputFile &Input, const SymbolGroup &SG, uint32_t Modi, + CallbackT Callback) { + return Callback(Modi, SG); +} + +template +Error iterateSymbolGroups(InputFile &Input, CallbackT Callback) { + uint32_t I = 0; + for (const auto &SG : Input.symbol_groups()) { + if (shouldDumpSymbolGroup(I, SG)) + if (auto Err = iterateOneModule(Input, SG, I, Callback)) + return Err; + ++I; + } + return Error::success(); +} + +template +Error iterateModuleSubsections( + InputFile &File, + llvm::function_ref + Callback) { + + return iterateSymbolGroups( + File, [&](uint32_t Modi, const SymbolGroup &SG) -> Error { + for (const auto &SS : SG.getDebugSubsections()) { + SubsectionT Subsection; + + if (SS.kind() != Subsection.kind()) + continue; + + BinaryStreamReader Reader(SS.getRecordData()); + if (auto Err = Subsection.initialize(Reader)) + continue; + if (auto Err = Callback(Modi, SG, Subsection)) + return Err; + } + return Error::success(); + }); +} + +} // namespace pdb +} // namespace llvm + +//===----------------------------------------------------------------------===// +// END +//===----------------------------------------------------------------------===// + +namespace llvm { +namespace logicalview { + +using namespace llvm::codeview; + +class LVCodeViewReader; +class LVLogicalVisitor; + +class LVTypeVisitor final : public TypeVisitorCallbacks { + ScopedPrinter &W; + LVLogicalVisitor *LogicalVisitor; + LazyRandomTypeCollection &Types; + LazyRandomTypeCollection &Ids; + uint32_t StreamIdx; + + // 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) + : TypeVisitorCallbacks(), W(W), LogicalVisitor(LogicalVisitor), + Types(Types), Ids(Ids), StreamIdx(StreamIdx) { + 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 (auto EC = Reader.readLongestContiguousChunk(Data)) { + llvm::consumeError(std::move(EC)); + 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); + + void printBinaryBlockWithRelocs(StringRef Label, ArrayRef Block) { + StringRef SBlock(reinterpret_cast(Block.data()), + Block.size()); + } + + 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; + + // Symbol offset when processing PDB streams. + uint32_t CurrentOffset = 0; + // Current object name collected from S_OBJNAME. + StringRef CurrentObjectName; + + CPUType CompilationCPUType = CPUType::X64; + bool HasIds = false; + bool InFunctionScope = false; + bool IsCompileUnit = false; + + 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; + } + } + +public: + LVSymbolVisitor(LVCodeViewReader *Reader, ScopedPrinter &W, + LVLogicalVisitor *LogicalVisitor, + LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids, + LVSymbolVisitorDelegate *ObjDelegate) + : Reader(Reader), W(W), LogicalVisitor(LogicalVisitor), Types(Types), + Ids(Ids), ObjDelegate(ObjDelegate) { + 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, + DefRangeFramePointerRelSym &DefRangeFramePointerRel) 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 { + 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; + + // 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; + + 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); + + // Input source in the case of type server o precompiled header. + void setInput(std::shared_ptr TypeServer) { + this->TypeServer = TypeServer; + } + void setInput(std::shared_ptr PrecompHeader) { + this->PrecompHeader = PrecompHeader; + } + + 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); + + 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 = Name; } + +public: + LVElement *getElement(uint32_t StreamIdx, TypeIndex TI, + LVScope *Parent = nullptr); + + 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; } + + LVAddress getLinkageAddress(StringRef Name); + LVScope *getLinkageScope(StringRef Name); + void addLinkageScope(StringRef Name, LVAddress Address); + + void processFiles(); + void processElements(); + void processNamespaces(); + + void printRecordTag(); + +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 (auto EC = Callbacks.visitKnownMember(Record, KnownRecord)) + return EC; + if (auto EC = visitKnownMember(Record, KnownRecord, TI, Element)) + return EC; + return Error::success(); + } + + template + Error visitKnownRecord(CVType &Record, TypeIndex TI, LVElement *Element) { + TypeRecordKind RK = static_cast(Record.kind()); + T KnownRecord(RK); + if (auto EC = TypeDeserializer::deserializeAs(const_cast(Record), + KnownRecord)) + return EC; + if (auto EC = visitKnownRecord(Record, KnownRecord, TI, Element)) + return EC; + 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,181 @@ +//===-- 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/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include +#include +#include + +namespace llvm { + +class MCAsmInfo; +class MCContext; +class MCDisassembler; +class MCInstPrinter; +class MCInstrInfo; +class MCRegisterInfo; +class MCSubtargetInfo; + +namespace logicalview { + +class LVElement; +class LVLine; +class LVScopeCompileUnit; +class LVSymbol; +class LVType; + +using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; + +class LVElfReader final : public LVReader { + object::ObjectFile &Obj; + + // 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; + + // 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; + + std::unique_ptr MRI = nullptr; + std::unique_ptr MAI = nullptr; + std::unique_ptr MSI = nullptr; + std::unique_ptr MII = nullptr; + std::unique_ptr MD = nullptr; + MCObjectFileInfo MOFI; + std::unique_ptr MC = nullptr; + std::unique_ptr MIP = nullptr; + + // Loads all info for the architecture of the provided object file. + Error loadTargetInfo(const object::ObjectFile &Obj); + + 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); + Error createInstructions(); + 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.insert(std::make_pair(Offset, nullptr)); + } + + // Remove offset from global map. + void removeGlobalOffset(LVOffset Offset) { + const auto &Entry = GlobalOffsets.find(Offset); + if (Entry != GlobalOffsets.end()) + GlobalOffsets.erase(Entry); + } + + // 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); + void updateReference(dwarf::Attribute Attr, DWARFFormValue &FormValue); + + // Get an element given the DIE offset. + LVElement *getElementForOffset(LVOffset offset, LVElement *Element); + + // Customized LLVM version. It does not support DW_OP_regval_type. The LLVM + // implementation uses the DWARFUnit to access the offset and tag name. + // This function is called on a logical view item, with no access to the + // underline DWARF data used by LLVM. + bool prettyPrintRegisterOp(raw_ostream &OS, uint8_t Opcode, + uint64_t Operands[2], const MCRegisterInfo *MRI, + bool isEH); + +protected: + Error createScopes() override; + void sortScopes() override; + +public: + LVElfReader() = delete; + LVElfReader(StringRef Filename, StringRef FileFormatName, + object::ObjectFile &Obj, ScopedPrinter &W) + : LVReader(Filename, FileFormatName, W), Obj(Obj) {} + LVElfReader(const LVElfReader &) = delete; + LVElfReader &operator=(LVElfReader const &) = delete; + ~LVElfReader() {} + +public: + LVAddress getCUBaseAddress() { return CUBaseAddress; } + void setCUBaseAddress(LVAddress Address) { CUBaseAddress = Address; } + LVAddress getCUHighAddress() { return CUHighAddress; } + void setCUHighAddress(LVAddress Address) { CUHighAddress = Address; } + + LVSymbols const &GetSymbolsWithLocations() const { + return SymbolsWithLocations; + } + + std::string getOpcodeName(LVSmall Opcode) override { + return std::string(dwarf::OperationEncodingString(Opcode)); + } + 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/CodeView/TypeRecordHelpers.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp +++ llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp @@ -50,3 +50,111 @@ discoverTypeIndices(CVT, Refs); return Refs.front(); } + +uint64_t llvm::codeview::getSize(TypeIndex TI) { + if (!TI.isSimple()) + return 0; + if (TI.getSimpleMode() != SimpleTypeMode::Direct) { + // We have a native pointer. + switch (TI.getSimpleMode()) { + case SimpleTypeMode::NearPointer: + case SimpleTypeMode::FarPointer: + case SimpleTypeMode::HugePointer: + return 2; + case SimpleTypeMode::NearPointer32: + case SimpleTypeMode::FarPointer32: + return 4; + case SimpleTypeMode::NearPointer64: + return 8; + case SimpleTypeMode::NearPointer128: + return 16; + default: + assert(false && "invalid simple type mode!"); + } + } + switch (TI.getSimpleKind()) { + case SimpleTypeKind::None: + case SimpleTypeKind::Void: + return 0; + case SimpleTypeKind::HResult: + return 4; + case SimpleTypeKind::SByte: + case SimpleTypeKind::Byte: + return 1; + case SimpleTypeKind::Int16Short: + case SimpleTypeKind::UInt16Short: + case SimpleTypeKind::Int16: + case SimpleTypeKind::UInt16: + return 2; + case SimpleTypeKind::Int32Long: + case SimpleTypeKind::UInt32Long: + case SimpleTypeKind::Int32: + case SimpleTypeKind::UInt32: + return 4; + case SimpleTypeKind::Int64Quad: + case SimpleTypeKind::UInt64Quad: + case SimpleTypeKind::Int64: + case SimpleTypeKind::UInt64: + return 8; + case SimpleTypeKind::Int128Oct: + case SimpleTypeKind::UInt128Oct: + case SimpleTypeKind::Int128: + case SimpleTypeKind::UInt128: + return 16; + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::UnsignedCharacter: + case SimpleTypeKind::NarrowCharacter: + return 1; + case SimpleTypeKind::WideCharacter: + case SimpleTypeKind::Character16: + return 2; + case SimpleTypeKind::Character32: + return 4; + case SimpleTypeKind::Float16: + return 2; + case SimpleTypeKind::Float32: + return 4; + case SimpleTypeKind::Float64: + return 8; + case SimpleTypeKind::Float80: + return 10; + case SimpleTypeKind::Float128: + return 16; + case SimpleTypeKind::Boolean8: + return 1; + case SimpleTypeKind::Boolean16: + return 2; + case SimpleTypeKind::Boolean32: + return 4; + case SimpleTypeKind::Boolean64: + return 8; + case SimpleTypeKind::Boolean128: + return 16; + default: + return 0; + } +} + +template static uint64_t getUdtSize(CVType CVT) { + RecordT Record; + if (auto EC = TypeDeserializer::deserializeAs(CVT, Record)) { + consumeError(std::move(EC)); + return 0; + } + return Record.getSize(); +} + +uint64_t llvm::codeview::getSize(CVType CVT) { + switch (CVT.kind()) { + case LF_STRUCTURE: + case LF_CLASS: + case LF_INTERFACE: + return getUdtSize(std::move(CVT)); + break; + case LF_UNION: + return getUdtSize(std::move(CVT)); + break; + default: + return CVT.length(); + } +} Index: llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -224,7 +224,7 @@ } } -static bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS, +bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS, DIDumpOptions DumpOpts, uint8_t Opcode, uint64_t Operands[2], const MCRegisterInfo *MRI, bool isEH) { Index: llvm/lib/DebugInfo/LogicalView/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/CMakeLists.txt @@ -0,0 +1,51 @@ +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/LVCodeViewVisitor.cpp + Readers/LVCodeViewReader.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,415 @@ +//===-- 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/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.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 + +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) { + PrintAll = options().getPrintAll(); + PrintLines = PrintAll || options().getPrintLines(); + PrintScopes = PrintAll || options().getPrintScopes(); + PrintSymbols = PrintAll || options().getPrintSymbols(); + PrintTypes = PrintAll || options().getPrintTypes(); +} + +void zeroResults() { + // In case the same instance is used. + for (auto &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](auto &LHS, auto &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 apply on its children. + // If a difference is found, the current path is marked as missing. + auto compareViews = [this](auto &LHS, auto &RHS) -> Error { + LHS->isContainedIn(RHS, /*TraverseChildren=*/true); + if (LHS->getIsMissingLink() && options().getReportView()) { + OS << "\nMissing Tree:\n"; + if (auto Err = LHS->doPrint(/*Split=*/false, /*Match=*/false, + /*Print=*/true, OS)) + return Err; + OS << "\n"; + } + + return Error::success(); + }; + + printHeader(ReferenceRoot, TargetRoot); + if (auto Err = compareViews(ReferenceRoot, TargetRoot)) + return Err; + ReferenceRoot->report(LVComparePass::Missing); + + printHeader(TargetRoot, ReferenceRoot); + if (auto Err = compareViews(TargetRoot, ReferenceRoot)) + return Err; + TargetRoot->report(LVComparePass::Added); + + // 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 = [&](auto &LHS, auto &RHS, auto &Set, + auto Pass) -> Error { + auto findMatch = [&](auto &References, auto &Targets, + auto &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) -> auto { + 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.insert( + std::make_pair(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); + } + } + } + if (Pass == LVComparePass::Added) + // Record all the current missing elements for this category. + Set.insert(Set.end(), Elements.begin(), Elements.end()); + if (options().getReportDetails()) { + if (Elements.size()) { + OS << "\n(" << Elements.size() << ") " + << (Pass == LVComparePass::Missing ? "Missing" : "Added") << " " + << Category << ":\n"; + for (const auto &Element : Elements) { + if (auto 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 would be required. + if (options().getCompareScopes()) + if (auto Err = findMatch(LHS->getScopes(), RHS->getScopes(), "Scopes")) + return Err; + if (options().getCompareSymbols()) + if (auto Err = + findMatch(LHS->getSymbols(), RHS->getSymbols(), "Symbols")) + return Err; + if (options().getCompareTypes()) + if (auto Err = findMatch(LHS->getTypes(), RHS->getTypes(), "Types")) + return Err; + if (options().getCompareLines()) + if (auto Err = findMatch(LHS->getLines(), RHS->getLines(), "Lines")) + return Err; + + return Error::success(); + }; + + // If the user have 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; + if (auto Err = compareReaders(ReferenceReader, TargetReader, ElementsToAdd, + LVComparePass::Missing)) + return Err; + if (auto Err = compareReaders(TargetReader, ReferenceReader, ElementsToAdd, + LVComparePass::Added)) + return Err; + + LLVM_DEBUG({ + dbgs() << "\nReference/Target Scope links:\n"; + for (auto &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 (const auto &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().getReportView()) + if (auto Err = ReferenceReader->doPrint()) + return Err; + + LLVM_DEBUG({ + dbgs() << "\nModified Reference Reader"; + if (auto Err = ReferenceReader->doPrint()) + return Err; + dbgs() << "\nModified Target Reader"; + if (auto Err = TargetReader->doPrint()) + return Err; + }); + + // Display a summary with the elements missing and/or added. + printSummary(); + } + + return Error::success(); +} + +int LVCompare::printCurrentStack() { + int Indent = 0; + for (const auto &Scope : ScopeStack) { + Scope->printAttributes(OS); + OS << Scope->lineNumberAsString(/*ShowZero=*/true) << " " + << std::string(Indent * 2, ' ') << Scope->kind() << " " + << formattedName(Scope->getName()) << "\n"; + Indent++; + } + return Indent; +} + +void LVCompare::printItem(LVElement *Element, LVComparePass Pass) { + // Record expected, missing, added. + updateExpected(Element); + updateMissingOrAdded(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"; + + printDetails(Element, Kind, Name); + } +} + +void LVCompare::printDetails(LVElement *Element, StringRef Kind, + StringRef Name) { + if (options().getReportDetails()) { + auto Indent = printCurrentStack(); + Element->printAttributes(OS); + OS << Element->lineNumberAsString(/*ShowZero=*/true) << " " + << std::string(Indent * 2, ' ') << Kind << " " << Name << "\n\n"; + } +} + +void LVCompare::printSummary() const { + if (!options().getReportSummary()) + return; + auto 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 (auto &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,569 @@ +//===-- 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" + +LVElementTarget LVElement::Targets = { + {LVElementKind::Discarded, &LVElement::getIsDiscarded}, + {LVElementKind::Global, &LVElement::getIsGlobalReference}, + {LVElementKind::Optimized, &LVElement::getIsOptimized}}; + +LVElement *LVElement::getType(LVElement *Element) const { return ElementType; } + +LVType *LVElement::getType(LVType *Type) const { + return ElementType && ElementType->getIsType() + ? static_cast(ElementType) + : nullptr; +} + +LVScope *LVElement::getType(LVScope *Scope) const { + return ElementType && ElementType->getIsScope() + ? static_cast(ElementType) + : nullptr; +} + +LVSymbol *LVElement::getType(LVSymbol *Symbol) const { + return ElementType && ElementType->getIsSymbol() + ? 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->getType(nullType())); + else if (Element->getIsKindScope()) + setType(Element->getType(nullScope())); + } 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(); +} + +// Get the filename associated with the element. Unless asked to produce +// the full pathname, it returns the basename. +StringRef LVElement::getPathname() const { + // The string stored in the string pool, is the full pathname. + StringRef Filename = getStringPool().getString(getFilenameIndex()); + if (options().getAttributePathname()) + return Filename; + + // The path is in Unified format; all '\' have been converted into '/'. + // Ignore any prefix up to the last slash ('/') character and return + // the result which is the basename. + size_t Pos = Filename.rfind('/'); + if (Pos != std::string::npos) + Filename = Filename.substr(Pos + 1); + + return Filename; +} + +// 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 LVElement::markSystemEntry(StringRef Name) { + Name = Name.empty() ? getName() : Name; + auto find = [&](auto String) { return StringRef::npos != Name.find(String); }; + auto starts = [&](auto Pattern) { return Name.startswith(Pattern); }; + auto checkExclude = [&]() { + 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) + setIsSystem(); + + return Excluded; +} + +void LVElement::setInnerComponent(StringRef Name) { + if (Name.size()) { + StringRef OuterComponent; + StringRef InnerComponent; + std::tie(OuterComponent, 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 (getIsRoot() || getIsCompileUnit()) + return; + + LVScope *Parent = getParentScope(); + if (Parent && !Parent->getIsCompileUnit()) + Parent->resolve(); +} + +// Generate a name for unnamed elements. +void LVElement::generateName(std::string &Prefix) { + // CodeView elements that are not referenced and not finalized. + if (!getParentScope()) + return; + + // Use its parent name and any line information. + Prefix.append(std::string(getParentScope()->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() { + 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(StringRef Name, LVElement *BaseType) { + // 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(); + auto GetBaseTypename = false; + auto UseBaseTypename = true; + auto 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"; + 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_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_entry_point: + case dwarf::DW_TAG_enumeration_type: + 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: + return; + break; + } + + // Overwrite if no given value. + 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)); + } + + // Remove any double spaces; just for better layout. + size_t StartPos = 0; + std::string Spaces(" "); + std::string Single(" "); + while ((StartPos = Fullname.find(Spaces, StartPos)) != std::string::npos) { + Fullname.replace(StartPos, Spaces.length(), Single); + StartPos += Single.length(); + } + + 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::getFunctionParent() const { + LVScope *Parent = getParentScope(); + while (Parent && !Parent->getIsFunction()) + Parent = Parent->getParentScope(); + return Parent; +} + +LVScope *LVElement::getCompileUnitParent() const { + if (getIsCompileUnit()) + return static_cast(const_cast(this)); + LVScope *Parent = getParentScope(); + while (Parent && !Parent->getIsCompileUnit()) + Parent = Parent->getParentScope(); + return Parent; +} + +// Resolve the qualified name to include the parent hierarchy names. +void LVElement::resolveQualifiedName() { + if (!getIsReferencedType() || getIsBase() || 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, 0); + + OS << " {Source} "; + if (getInvalidFilename()) + OS << format("[0x%08x]\n", Index); + else + OS << formattedName(getPathname()) << "\n"; + } + } +} + +void LVElement::printReference(LVElement *Parent, raw_ostream &OS, + bool Full) const { + if (options().getPrintFormatting() && options().getAttributeReference()) + printAttributes(Parent, OS, Full, "{Reference} ", + referenceAsString(getLineNumber(), /*Spaces=*/false), + /*UseQuotes=*/false, /*PrintRef=*/true); +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp @@ -0,0 +1,185 @@ +//===-- 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 *KindBasicBlock = "BasicBlock"; +const char *KindDiscriminator = "Discriminator"; +const char *KindEndSequence = "EndSequence"; +const char *KindEpilogueBegin = "EpilogueBegin"; +const char *KindLineDebug = "Line"; +const char *KindLineSource = "Code"; +const char *KindNewStatement = "NewStatement"; +const char *KindPrologueEnd = "PrologueEnd"; +const char *KindUndefined = "Undefined"; +const char *KindAlwaysStepInto = "AlwaysStepInto"; // CodeView +const char *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 (getIsLineSource()) + Kind = KindLineSource; + return Kind; +} + +LVLineTarget LVLine::Targets = { + {LVLineKind::BasicBlock, &LVLine::getIsBasicBlock}, + {LVLineKind::Discriminator, &LVLine::getIsDiscriminator}, + {LVLineKind::EndSequence, &LVLine::getIsEndSequence}, + {LVLineKind::LineDebug, &LVLine::getIsLineDebug}, + {LVLineKind::LineSource, &LVLine::getIsLineSource}, + {LVLineKind::NewStatement, &LVLine::getIsNewStatement}, + {LVLineKind::EpilogueBegin, &LVLine::getIsEpilogueBegin}, + {LVLineKind::PrologueEnd, &LVLine::getIsPrologueEnd}, + {LVLineKind::AlwaysStepInto, &LVLine::getIsAlwaysStepInto}, + {LVLineKind::NeverStepInto, &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::isContainedIn(const LVLines *References, const LVLines *Targets) { + if (References && Targets) + for (const auto &Reference : *References) + if (!Reference->findIn(Targets)) + Reference->markBranchAsMissing(); +} + +LVLine *LVLine::findIn(const LVLines *Targets) { + if (Targets) + for (const auto &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 auto &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. +//===----------------------------------------------------------------------===// +// Additional line information. +std::string LVLineDebug::sourceInfo(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()) + OS << sourceInfo(/*Formatted=*/true); + if (options().getAttributeAnySource()) + OS << " " << formattedName(getPathname()); + OS << "\n"; +} + +//===----------------------------------------------------------------------===// +// Assembler line extracted from the ELF .text section. +//===----------------------------------------------------------------------===// +bool LVLineSource::equals(const LVLine *Line) const { + return LVLine::equals(Line); +} + +void LVLineSource::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,423 @@ +//===-- 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) {} + +std::string LVOperation::getOperandsInfo() { + std::string String; + raw_string_ostream Stream(String); + + bool PrintOperations = true; + // Identify the most common type of operations and print them using a high + // level format, trying to isolate the DWARF complexity. + switch (Opcode) { + // Member location. + case 0: + Stream << "Class Offset: " << (int)Operands[0] << " (" + << hexString(Operands[0]) << ")"; + break; + + // Missing location. + case dwarf::DW_OP_hi_user: + Stream << "Missing"; + PrintOperations = false; + break; + + //----------------------------------------------------------------------- + // Literal encodings. + //----------------------------------------------------------------------- + case dwarf::DW_OP_addr: + Stream << "Address: " << hexString(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 << "Signed: " << (int)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 << "Unsigned: " << (int)Operands[0]; + break; + + //----------------------------------------------------------------------- + // Stack operations. + //----------------------------------------------------------------------- + case dwarf::DW_OP_deref: + Stream << "Address TOS"; + break; + case dwarf::DW_OP_form_tls_address: + Stream << "Address: " << hexString(Operands[0]); + break; + + //----------------------------------------------------------------------- + // Register values. + //----------------------------------------------------------------------- + case dwarf::DW_OP_fbreg: + Stream << "Stack Offset: " << (int)Operands[0] << " (" + << hexString(Operands[0]) << ")"; + break; + + //----------------------------------------------------------------------- + // Composite location descriptions. + //----------------------------------------------------------------------- + case dwarf::DW_OP_piece: + Stream << "Piece: size " << (int)Operands[0]; + break; + case dwarf::DW_OP_bit_piece: + Stream << "Piece: size " << (int)Operands[0] << ", offset " + << (int)Operands[1]; + break; + + //----------------------------------------------------------------------- + // Register location descriptions. + //----------------------------------------------------------------------- + case dwarf::DW_OP_regx: + Stream << "Register:" << getReader().getRegisterName(Opcode, Operands); + break; + + //----------------------------------------------------------------------- + // Implicit location descriptions. + //----------------------------------------------------------------------- + case dwarf::DW_OP_stack_value: + Stream << "StackValue"; + break; + + //----------------------------------------------------------------------- + // Register values. + //----------------------------------------------------------------------- + default: + if (dwarf::DW_OP_breg0 <= Opcode && Opcode <= dwarf::DW_OP_breg31) { + std::string RegisterName(getReader().getRegisterName(Opcode, Operands)); + Stream << format("Register: breg%d%s", Opcode - dwarf::DW_OP_breg0, + RegisterName.c_str()); + break; + } + if (dwarf::DW_OP_reg0 <= Opcode && Opcode <= dwarf::DW_OP_reg31) { + std::string RegisterName(getReader().getRegisterName(Opcode, Operands)); + Stream << format("Register: reg%d%s", Opcode, RegisterName.c_str()); + break; + } + + // For now print all the fields. + Stream << format("#0x%02x: ", Opcode) << hexString(Operands[0]) << "," + << hexString(Operands[1]) << "#"; + break; + } + + // Print DWARF operations. + if (PrintOperations && options().getAttributeOperation()) + if (getOpcode()) + Stream << " [" << getReader().getOpcodeName(getOpcode()) << "]"; + + return String; +} + +namespace { +const char *KindBaseClassOffset = "BaseClassOffset"; +const char *KindBaseClassStep = "BaseClassStep"; +const char *KindClassOffset = "ClassOffset"; +const char *KindFixedAddress = "FixedAddress"; +const char *KindMissingInfo = "Missing"; +const char *KindOperation = "Operation"; +const char *KindOperationList = "OperationList"; +const char *KindRegister = "Register"; +const char *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 *Question = "?"; + std::string String; + raw_string_ostream Stream(String); + if (getIsAddressRange()) + Stream << "{Range}"; + + auto printLine = [&](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::validRanges() { + // Traverse the locations and validate them against the address to line + // mapping in the current compile unit. Record those invalid ranges, + // depending on the type of condition: + // a) line(lopc) <= line(hipc) + // b) line(lopc) and line(hipc) are valid. + + if (!requiredRange()) + return true; + + LVScopeCompileUnit *Scope = getReader().getCompileUnit(); + auto *LowLine = Scope->getLowerLine(getLowerAddress()); + auto *HighLine = Scope->getUpperLine(getUpperAddress()); + if (LowLine) + setLowerLine(LowLine); + else { + setIsInvalidLower(); + return false; + } + if (HighLine) + setUpperLine(HighLine); + else { + setIsInvalidUpper(); + return false; + } + // Check for a valid interval. + if (LowLine && HighLine && + LowLine->getLineNumber() > HighLine->getLineNumber()) { + setIsInvalidRange(); + return false; + } + + return true; +} + +// Calculate coverage factor. +bool LVLocation::calculateCoverage(LVLocations *Locations, unsigned &Factor, + float &Percentage) { + // 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. + 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 auto &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 (requiredRange()) + 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. +//===----------------------------------------------------------------------===// +LVLocationSymbol::~LVLocationSymbol() { + for (const auto &Operation : Entries) + delete Operation; +} + +// 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 hipc value -1, indicates no range. + if (!((HighPC != (LVAddress)-1) && LowPC && HighPC)) + setIsDiscardedRange(); + + // Update the location kind, using the DWARF attribute. + setKind(); +} + +// Add a Location Record. +void LVLocationSymbol::addObject(LVSmall Opcode, LVUnsigned Operand1, + LVUnsigned Operand2) { + Entries.push_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; + case dwarf::DW_AT_string_length: + case dwarf::DW_AT_use_location: + case dwarf::DW_AT_static_link: + case dwarf::DW_AT_vtable_elem_location: // Scope. + case dwarf::DW_AT_return_addr: // Scope. + case dwarf::DW_AT_frame_base: // Scope. + 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 (requiredRange()) + getParentSymbol()->setFillGaps(); +} + +void LVLocationSymbol::updateKind() { + // Update the location type for simple ones. + if (Entries.size() == 1) { + LVOperation *Operation = Entries[0]; + if (dwarf::DW_OP_fbreg == Operation->getOpcode()) + setIsStackOffset(); + } +} + +void LVLocationSymbol::printRawExtra(raw_ostream &OS, bool Full) const { + for (const auto &Operation : Entries) + Operation->print(OS, Full); +} + +// Print location (formatted version). +void LVLocation::print(LVLocations *Locations, raw_ostream &OS, bool Full) { + // Print the symbol coverage. + if (options().getAttributeCoverage()) { + // 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; + if (Location->getIsLocationSimple()) + raw_string_ostream(String) << format("%.2f%%", Percentage); + else + raw_string_ostream(String) + << format("%.2f%% (%d/%d)", Percentage, Symbol->getCoverageFactor(), + Symbol->getParentScope()->getCoverageFactor()); + Symbol->printAttributes(Symbol, OS, Full, "{Coverage} ", StringRef(String), + /*UseQuotes=*/false, + /*PrintRef=*/false); + } + + // Print the symbol location, including the missing entries. + if (getReader().doPrintLocation(nullLocation())) + for (const auto &Location : *Locations) + Location->print(OS, Full); +} + +void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const { + OS << "{Location}"; + printInterval(OS, Full); + OS << "\n"; + + // Print location entries. + if (Full) + for (const auto &Operation : Entries) + printAttributes(const_cast(this), OS, Full, + "{Entry} ", StringRef(Operation->getOperandsInfo()), + /*UseQuotes=*/false, + /*PrintRef=*/false); +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp @@ -0,0 +1,179 @@ +//===-- 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/LVSupport.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 + +LVElement *llvm::logicalview::nullElement() { return nullptr; } +LVLine *llvm::logicalview::nullLine() { return nullptr; } +LVLocation *llvm::logicalview::nullLocation() { return nullptr; } +LVLocationSymbol *llvm::logicalview::nullLocationSymbol() { return nullptr; } +LVObject *llvm::logicalview::nullObject() { return nullptr; } +LVType *llvm::logicalview::nullType() { return nullptr; } +LVScope *llvm::logicalview::nullScope() { return nullptr; } +LVSymbol *llvm::logicalview::nullSymbol() { return nullptr; } + +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(LVObject *Parent, raw_ostream &OS, bool Full, + StringRef Name, 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,522 @@ +//===-- 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" + +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 = [&]() { + setAttributeBase(); + setAttributeCoverage(); + setAttributeDirectories(); + setAttributeDiscriminator(); + setAttributeFilename(); + setAttributeFiles(); + setAttributeFormat(); + setAttributeLevel(); + setAttributeProducer(); + setAttributePublics(); + setAttributeRange(); + setAttributeReference(); + setAttributeZero(); + }; + + // Attributes that are classified as extended options. + auto extendedAttributes = [&]() { + setAttributeArgument(); + setAttributeDiscarded(); + setAttributeEncoded(); + setAttributeGaps(); + setAttributeGenerated(); + setAttributeGlobal(); + setAttributeInserted(); + setAttributeLocal(); + setAttributeLocation(); + setAttributeOffset(); + setAttributeOperation(); + 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(); + + // '--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()) { + 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(); + + // Generic element for printing. + if (getPrintInstructions() || getPrintLines() || getPrintScopes() || + getPrintSymbols() || getPrintTypes()) + setPrintGeneric(); + + // Generic request for printing. + if (getPrintAll() || getPrintGeneric() || getPrintSizes() || + getPrintSummary() || getPrintWarnings()) + setPrintExecute(); + + // '--reports=all' settings. + if (getReportAll()) { + setReportDetails(); + setReportSummary(); + setReportView(); + } + + // Generic report request. + if (getReportDetails() || getReportSummary() || getReportView()) + 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(); + 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() || getAttributeOperation() || + getAttributeRegister()) + setAttributeLocation(); + + // Location information is only relevant when printing symbols. + if (!getPrintSymbols()) { + resetAttributeCoverage(); + resetAttributeGaps(); + resetAttributeLocation(); + resetAttributeOperation(); + resetAttributeRegister(); + } + + // Quick check for printing any element source information. + if (getAttributeFilename() || getAttributePathname()) + setAttributeAnySource(); + + // Quick check for printing any location information. + if (getAttributeLocation() || getAttributeRange()) + setAttributeAnyLocation(); + + // Quick check for printing logical lines. + if (getPrintInstructions() || getPrintLines()) + setPrintAnyLine(); + + 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 (options):\n" + << "All: " << getAttributeAll() << "\n" + << "Argument: " << getAttributeArgument() << "\n" + << "Base: " << getAttributeBase() << "\n" + << "Coverage: " << getAttributeCoverage() << "\n" + << "Directories: " << getAttributeDirectories() << "\n" + << "Discarded: " << getAttributeDiscarded() << "\n" + << "Discriminator: " << getAttributeDiscriminator() << "\n" + << "Encoded: " << getAttributeEncoded() << "\n" + << "Extended: " << getAttributeExtended() << "\n" + << "Filename: " << getAttributeFilename() << "\n" + << "Files: " << getAttributeFiles() << "\n" + << "Format: " << getAttributeFormat() << "\n" + << "Gaps: " << getAttributeGaps() << "\n" + << "Generated: " << getAttributeGenerated() << "\n" + << "Global: " << getAttributeGlobal() << "\n" + << "Inserted: " << getAttributeInserted() << "\n" + << "Level: " << getAttributeLevel() << "\n" + << "Local: " << getAttributeLocal() << "\n" + << "Location: " << getAttributeLocation() << "\n" + << "Offset: " << getAttributeOffset() << "\n" + << "Operation: " << getAttributeOperation() << "\n" + << "Pathname: " << getAttributePathname() << "\n" + << "Producer: " << getAttributeProducer() << "\n" + << "Publics: " << getAttributePublics() << "\n" + << "Qualified: " << getAttributeQualified() << "\n" + << "Qualifier: " << getAttributeQualifier() << "\n" + << "Range: " << getAttributeRange() << "\n" + << "Reference: " << getAttributeReference() << "\n" + << "Register: " << getAttributeRegister() << "\n" + << "Standard: " << getAttributeStandard() << "\n" + << "Subrange: " << getAttributeSubrange() << "\n" + << "Typename: " << getAttributeTypename() << "\n" + << "Underlying: " << getAttributeUnderlying() << "\n" + << "Zero: " << getAttributeZero() << "\n"; + OS << "Attributes (flags):\n" + << "Added: " << getAttributeAdded() << "\n" + << "AnyLocation: " << getAttributeAnyLocation() << "\n" + << "AnySource: " << getAttributeAnySource() << "\n" + << "Missing: " << getAttributeMissing() << "\n\n"; + + // --compare + OS << "Compare: (options)\n" + << "All: " << getCompareAll() << "\n" + << "Lines: " << getCompareLines() << "\n" + << "Scopes: " << getCompareScopes() << "\n" + << "Symbols: " << getCompareSymbols() << "\n" + << "Types: " << getCompareTypes() << "\n"; + OS << "Compare: (flags)\n" + << "Context: " << getCompareContext() << "\n" + << "Execute: " << getCompareExecute() << "\n\n"; + + // --print + OS << "Print: (options)\n" + << "All: " << getPrintAll() << "\n" + << "Elements: " << getPrintElements() << "\n" + << "Instructions: " << getPrintInstructions() << "\n" + << "Lines: " << getPrintLines() << "\n" + << "Scopes: " << getPrintScopes() << "\n" + << "Sizes: " << getPrintSizes() << "\n" + << "Summary: " << getPrintSummary() << "\n" + << "Symbols: " << getPrintSymbols() << "\n" + << "Types: " << getPrintTypes() << "\n" + << "Warnings: " << getPrintWarnings() << "\n"; + OS << "Print: (flags)\n" + << "AnyLine: " << getPrintAnyLine() << "\n" + << "Execute: " << getPrintExecute() << "\n\n" + << "Formatting: " << getPrintFormatting() << "\n\n" + << "Generic: " << getPrintGeneric() << "\n\n" + << "Offset: " << getPrintOffset() << "\n\n"; + + // --report + OS << "Report: (options)\n" + << "All: " << getReportAll() << "\n" + << "Details: " << getReportDetails() << "\n" + << "Summary: " << getReportSummary() << "\n" + << "View: " << getReportView() << "\n"; + OS << "Report: (flags)\n" + << "Execute: " << getReportExecute() << "\n\n"; + + // --warning + OS << "Warning: (options)\n" + << "All: " << getWarningAll() << "\n" + << "Coverage: " << getWarningCoverages() << "\n" + << "Lines: " << getWarningLines() << "\n" + << "Locations: " << getWarningLocations() << "\n" + << "Ranges: " << getWarningRanges() << "\n"; + + // --internal + OS << "Internal: (options)\n" + << "All: " << Options.getInternalAll() << "\n" + << "ID: " << Options.getInternalID() << "\n" + << "Integrity: " << Options.getInternalIntegrity() << "\n" + << "None: " << Options.getInternalNone() << "\n" + << "Tag: " << Options.getInternalTag() << "\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(std::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. + Match.Pattern = std::string(Pattern); + if (Match.Pattern.size()) { + Match.Mode = 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(LVOffsetSet &Patterns) { + for (auto &Entry : Patterns) + OffsetMatchInfo.push_back(Entry); + if (OffsetMatchInfo.size()) { + options().setSelectOffsetPattern(); + options().setSelectExecute(); + } +} + +void LVPatterns::addPatterns(StringSet<> &Patterns, LVMatchInfo &Filters) { + auto IgnoreCase = options().getSelectIgnoreCase(); + auto UseRegex = options().getSelectUseRegex(); + for (auto &Entry : Patterns) { + StringRef Pattern = Entry.first(); + auto Err = createMatchEntry(Filters, Pattern, IgnoreCase, UseRegex); + if (Err) + consumeError(std::move(Err)); + } + + LLVM_DEBUG({ + dbgs() << "\nPattern Information:\n"; + for (auto &Match : Filters) + dbgs() << "Mode: " + << (Match.Mode == LVMatchMode::Match ? "Match" : "Regex") + << " Pattern: '" << Match.Pattern << "'\n"; + }); +} + +void LVPatterns::addElement(LVElement *Element) { + options().setSelectExecute(); + if (options().getReportDetails()) + getReaderCompileUnit()->addMatched(Element); + if (options().getReportView()) + getReaderCompileUnit()->addMatched(Element->getIsScope() + ? static_cast(Element) + : Element->getParentScope()); +} + +void LVPatterns::updateReportOptions() { + if (ElementRequest.size() || LineRequest.size() || ScopeRequest.size() || + SymbolRequest.size() || TypeRequest.size()) { + options().setSelectGenericKind(); + // If we have selected requests and there are no specified report options, + // assume the 'details' option. + if (!options().getReportExecute()) + options().setReportDetails(); + } +} + +// Match a general pattern. +bool LVPatterns::matchPattern(StringRef Input, LVMatchInfo &MatchInfo) { + std::string Name(Input); + auto Matched = false; + // Traverse all match specifications. + for (auto &Match : MatchInfo) { + switch (Match.Mode) { + case LVMatchMode::Match: + Matched = !Name.compare(Match.Pattern); + break; + case LVMatchMode::Regex: + Matched = Match.RE->match(Name); + 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->getIsLineSource()); +} + +bool LVPatterns::printObject(const LVLocation *Location) const { + if (options().getAttributeAll()) + return true; + auto 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: + // - Normal printing: + // a) Request to print all elements. + // b) Request to print warnings and the scope is Root or CompileUnit. + return options().getPrintScopes() || + (options().getPrintSymbols() && Scope->getHasSymbols()) || + ((options().getPrintLines() || options().getPrintInstructions()) && + Scope->getHasLines()) || + (options().getPrintTypes() && Scope->getHasTypes()) || + (options().getPrintSizes()) || + ((options().getPrintSummary() || 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,157 @@ +//===-- 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" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Range" + +using LVTuple = std::tuple; +LVTuple getEntry(LVAddress Low, LVAddress High, LVLevel Level) { + LVScope *Scope = new LVScope(); + Scope->setLevel(Level); + return std::make_tuple(Low, High, Scope); +} + +void LVRange::startSearch() { + RangesTree.clear(); + + LLVM_DEBUG({ dbgs() << "\nRanges Tree:\n"; }); + + // Traverse the ranges and store them into the interval tree. + if (RangeEntries.size()) + for (const auto &RangeEntry : RangeEntries) { + LLVM_DEBUG({ + LVScope *Scope = RangeEntry.scope(); + dbgs() << "Scope: " << format_decimal(Scope->getLevel(), 5) << " " + << "Range: [" << format_hex(RangeEntry.low(), 8) << ":" + << format_hex(RangeEntry.high(), 8) << "]\n"; + }); + + RangesTree.insert(RangeEntry.low(), RangeEntry.high(), + 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 the low <= high. +void LVRange::addEntry(LVScope *Scope, LVAddress LowAddress, + LVAddress HighAddress) { + // We assume the low <= high. + if (LowAddress > HighAddress) + std::swap(LowAddress, HighAddress); + + // Record the lowest and highest seen addresses. + if (LowAddress < Lower) + Lower = LowAddress; + if (HighAddress > Higher) + Higher = HighAddress; + + // Just add the scope and range pair, in no particular order. + RangeEntries.push_back(LVRangeEntry(LowAddress, HighAddress, Scope)); +} + +void LVRange::addEntry(LVScope *Scope) { + // 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 auto &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) { + LLVM_DEBUG({ dbgs() << format("Searching: 0x%08x\nFound: ", Address); }); + + LVScope *Target = nullptr; + LVLevel TargetLevel = 0; + LVLevel Level = 0; + LVScope *Scope = nullptr; + for (auto 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 LowAddress, LVAddress HighAddress) { + for (const auto &RangeEntry : RangeEntries) + if (LowAddress >= RangeEntry.low() && HighAddress < RangeEntry.high()) + return RangeEntry.scope(); + return nullptr; +} + +// Find the associated Scope for the given ranges values. +bool LVRange::findEntry(LVAddress LowAddress, LVAddress HighAddress) { + for (const auto &RangeEntry : RangeEntries) + if (LowAddress == RangeEntry.low() && HighAddress == RangeEntry.high()) + return true; + return false; +} + +// Callback sort routine for Range set compare. +static bool CompareRangeEntry(const LVRangeEntry &lhs, + const LVRangeEntry &rhs) { + return (lhs.low() < rhs.low()); +} + +// Sort the range elements for the whole Compile Unit. +void LVRange::sort() { + // 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 { + LVScope *Scope; + size_t Indentation = 0; + for (const auto &RangeEntry : RangeEntries) { + Scope = RangeEntry.scope(); + Scope->printAttributes(OS, Full); + Indentation = options().indentationSize(); + if (Indentation) + OS << " "; + OS << format("[0x%08x,0x%08x] ", RangeEntry.low(), RangeEntry.high()) + << 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,386 @@ +//===-- 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/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Path.h" +#include + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Reader" + +// Detect elements that are inserted more that 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 I = Integrity.find(Element); + if (I == Integrity.end()) + Integrity.insert(std::make_pair(Element, Scope)); + else + // We found a duplicated. + Duplicate.push_back(LVDuplicateEntry(Element, Scope, I->second)); + }; + + // Recursively add all the elements in the scope. + std::function traverseScope; + traverseScope = [&](LVScope *Parent) { + auto traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) + addElement(Entry, Parent); + }; + if (auto Scopes = Parent->getScopes()) + for (const auto &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); + LVElement *Element; + LVScope *First; + LVScope *Second; + 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 auto &Entry : Duplicate) { + 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 an 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() { + static ScopedPrinter W(outs()); + static LVReader DefaultReader("", "", W); + return CurrentReader ? *CurrentReader : DefaultReader; +} +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 enable to create a split context location. + if (auto 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 (CompileUnitOffsets.size()) { + // Get Compile Unit for the given object. + LVOffset Offset = *std::prev(std::lower_bound(CompileUnitOffsets.begin(), + CompileUnitOffsets.end(), + Object->getOffset())); + + LVCompileUnits::const_iterator cit = CompileUnits.find(Offset); + if (cit != CompileUnits.end()) + return cit->second->getFilename(Index); + } + + return CompileUnit ? CompileUnit->getFilename(Index) : StringRef(); +} + +void LVReader::mapVirtualAddress(const object::ObjectFile &Obj) { + if (!options().getPrintInstructions()) + return; + + for (const object::SectionRef &Section : Obj.sections()) { + if (!Section.isText() || Section.isVirtual() || !Section.getSize()) + continue; + uint64_t Address = Section.getAddress(); + if (SectionAddresses.find(Address) == SectionAddresses.end()) + SectionAddresses.insert(std::make_pair(Address, Section)); + + // Additional initialization on the specific object format. + mapRangeAddress(Obj, Section); + } + + LLVM_DEBUG({ + dbgs() << "\nObject Section Information:\n"; + for (auto &Entry : SectionAddresses) + dbgs() << "Range: [" << format_hex(Entry.first, 8) << ":" + << format_hex(Entry.first + Entry.second.getSize(), 10) + << "] Size: " << format_hex(Entry.second.getSize(), 10) << "\n"; + }); +} + +// 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 LVReader::processLines() { + if (CULines.empty() && CUInstructions.empty()) + return; + + // If we have both line records and instructions, they are merged 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.. + if (CULines.empty()) { + // There is no lines records, then transfer all instructions. + std::move(CUInstructions.begin(), CUInstructions.end(), + std::back_inserter(CULines)); + } else { + LVLineList::iterator LineIter = CULines.begin(); + for (auto &Entry : CUInstructions) { + // Get address of instruction. + auto InstAddr = Entry->getAddress(); + while (LineIter != CULines.end()) { + // Get address of line. + auto LineAddr = (*LineIter)->getAddress(); + // Instruction address before Line Debug. + if (InstAddr < LineAddr) { + LineIter = CULines.insert(LineIter, Entry); + break; + } + ++LineIter; + } + } + } + + CUInstructions.clear(); + + // This compilation unit does not have instructions or line records. + if (CULines.empty()) + return; + + ScopesWithRanges.startSearch(); + + // Process collected lines. + LVScope *Scope; + for (const auto &Line : CULines) { + // 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; + + // 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->getAddress(), Line); + + // Resolve any given pattern. + patterns().resolvePatternMatch(Line); + } + + ScopesWithRanges.endSearch(); + CULines.clear(); +} + +// Default handler for a generic reader. +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 (auto Err = createScopes()) + return Err; + + if (options().getInternalIntegrity()) + checkIntegrityScopesTree(Root); + + // Recursively process locations, ranges and calculate coverage. + // Resolve the elements names using its references. + Root->processRangeInformation(); + Root->resolveElements(); + + sortScopes(); + return Error::success(); +} + +// Default handler for a generic reader. +Error LVReader::doPrint() { + // Set current Reader instance. + setInstance(this); + + // Check for any select request. + if (options().getSelectExecute()) { + if (options().getReportDetails()) + if (auto Err = printMatchedElements()) + return Err; + if (options().getReportView()) + if (auto Err = printScopes()) + return Err; + + return Error::success(); + } + + return printScopes(); +} + +Error LVReader::printScopes() { + if (auto DoPrint = options().getPrintExecute()) { + if (auto Err = createSplitFolder()) + return Err; + + // Start printing from the root. + auto DoMatch = options().getSelectGenericPattern() || + options().getSelectOffsetPattern(); + return Root->doPrint(OutputSplit, DoMatch, DoPrint, OS); + } + + return Error::success(); +} + +Error LVReader::printMatchedElements() { + if (auto Err = createSplitFolder()) + return Err; + + return Root->doPrintMatches(OutputSplit, OS); +} + +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,2040 @@ +//===-- 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/LVStringPool.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Scope" + +namespace { +const char *KindArray = "Array"; +const char *KindBlock = "Block"; +const char *KindClass = "Class"; +const char *KindCompileUnit = "CompileUnit"; +const char *KindEnumeration = "Enumeration"; +const char *KindFile = "File"; +const char *KindFunction = "Function"; +const char *KindInlinedFunction = "InlinedFunction"; +const char *KindNamespace = "Namespace"; +const char *KindStruct = "Struct"; +const char *KindTemplateAlias = "TemplateAlias"; +const char *KindTemplatePack = "TemplatePack"; +const char *KindUndefined = "Undefined"; +const char *KindUnion = "Union"; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// DWARF lexical block, such as: namespace, function, compile unit, module, etc. +//===----------------------------------------------------------------------===// +LVScope::~LVScope() { + if (Types) { + for (const auto &Type : *Types) + delete Type; + delete Types; + } + if (Symbols) { + for (const auto &Symbol : *Symbols) + delete Symbol; + delete Symbols; + } + if (Scopes) { + for (const auto &Scope : *Scopes) + delete Scope; + delete Scopes; + } + if (Lines) { + for (const auto &Line : *Lines) + delete Line; + delete Lines; + } + if (Ranges) { + for (const auto &Location : *Ranges) + delete Location; + delete Ranges; + } + if (Children) + 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 (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 (getIsStruct()) + Kind = KindStruct; + else if (getIsUnion()) + Kind = KindUnion; + return Kind; +} + +LVScopeTarget LVScope::Targets = { + {LVScopeKind::Aggregate, &LVScope::getIsAggregate}, + {LVScopeKind::Array, &LVScope::getIsArray}, + {LVScopeKind::Block, &LVScope::getIsBlock}, + {LVScopeKind::CatchBlock, &LVScope::getIsCatchBlock}, + {LVScopeKind::Class, &LVScope::getIsClass}, + {LVScopeKind::CompileUnit, &LVScope::getIsCompileUnit}, + {LVScopeKind::EntryPoint, &LVScope::getIsEntryPoint}, + {LVScopeKind::Enumeration, &LVScope::getIsEnumeration}, + {LVScopeKind::Root, &LVScope::getIsRoot}, + {LVScopeKind::Function, &LVScope::getIsFunction}, + {LVScopeKind::FunctionType, &LVScope::getIsFunctionType}, + {LVScopeKind::InlinedFunction, &LVScope::getIsInlinedFunction}, + {LVScopeKind::LexicalBlock, &LVScope::getIsLexicalBlock}, + {LVScopeKind::Namespace, &LVScope::getIsNamespace}, + {LVScopeKind::Member, &LVScope::getIsMember}, + {LVScopeKind::Structure, &LVScope::getIsStruct}, + {LVScopeKind::Template, &LVScope::getIsTemplate}, + {LVScopeKind::TemplateAlias, &LVScope::getIsTemplateAlias}, + {LVScopeKind::TemplatePack, &LVScope::getIsTemplatePack}, + {LVScopeKind::TryBlock, &LVScope::getIsTryBlock}, + {LVScopeKind::Union, &LVScope::getIsUnion}}; + +void LVScope::addToChildren(LVElement *Element) { + if (!Children) + Children = new LVElements(); + Children->push_back(Element); +} + +void LVScope::addElement(LVElement *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 + addElement(static_cast(Element)); +} + +// Adds the line info item to the ones stored in the scope. +void LVScope::addElement(LVLine *Line) { + if (!Lines) + Lines = new LVLines(); + + // 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. + traverse(&LVScope::getHasLines, &LVScope::setHasLines); +} + +// Add a location. +void LVScope::addObject(LVLocation *Location) { + if (!Ranges) + Ranges = new LVLocations(); + + // Add it to parent. + Location->setParent(this); + Location->setOffset(getOffset()); + + Ranges->push_back(Location); +} + +// Adds the scope to the child scopes and sets the parent in the child. +void LVScope::addElement(LVScope *Scope) { + if (!Scopes) + Scopes = new LVScopes(); + + // 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()) + traverse(&LVScope::getHasGlobals, &LVScope::setHasGlobals); + else + traverse(&LVScope::getHasLocals, &LVScope::setHasLocals); + + // Indicate that this tree branch has scopes. + traverse(&LVScope::getHasScopes, &LVScope::setHasScopes); +} + +// Adds a symbol to the ones stored in the scope. +void LVScope::addElement(LVSymbol *Symbol) { + if (!Symbols) + Symbols = new LVSymbols(); + + // 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()) + traverse(&LVScope::getHasGlobals, &LVScope::setHasGlobals); + else + traverse(&LVScope::getHasLocals, &LVScope::setHasLocals); + + // Indicate that this tree branch has symbols. + traverse(&LVScope::getHasSymbols, &LVScope::setHasSymbols); +} + +// Adds a type to the ones stored in the scope. +void LVScope::addElement(LVType *Type) { + if (!Types) + Types = new LVTypes(); + + // 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()) + traverse(&LVScope::getHasGlobals, &LVScope::setHasGlobals); + else + traverse(&LVScope::getHasLocals, &LVScope::setHasLocals); + + // Indicate that this tree branch has types. + traverse(&LVScope::getHasTypes, &LVScope::setHasTypes); +} + +// Add a pair of ranges. +void LVScope::addObject(LVAddress LowAddress, LVAddress HighAddress) { + // Pack the ranges into a Location object. + LVLocation *Location = new LVLocation(); + Location->setLowerAddress(LowAddress); + Location->setUpperAddress(HighAddress); + Location->setIsAddressRange(); + + addObject(Location); +} + +bool LVScope::removeElement(LVElement *Element) { + auto predicate = [&](LVElement * Item) -> auto { return Item == Element; }; + auto removeElement = [&](auto &Container) -> auto { + auto it = std::remove_if(Container->begin(), Container->end(), predicate); + if (it != Container->end()) { + Container->erase(it, Container->end()); + 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); + } + + return false; +} + +void LVScope::addMissingElements(LVScope *Reference) { + if (!Reference) + return; + + // Get abstract symbols for the given scope reference. + const LVSymbols *Symbols = Reference->getSymbols(); + if (!Symbols) + return; + + LVSymbols References; + References.insert(References.end(), Symbols->begin(), Symbols->end()); + + auto removeElement = [&](auto &Container, auto *Element) { + auto it = std::remove_if( + Container.begin(), + Container.end(), [&](auto *Item) -> auto { return Item == Element; }); + if (it != Container.end()) + Container.erase(it, Container.end()); + }; + + // Erase abstract symbols already in this scope from the collection of + // symbols in the referenced scope. + if (getSymbols()) + for (const auto &Symbol : *getSymbols()) + if (Symbol->getHasReferenceAbstract()) + removeElement(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 (const auto &Reference : References) { + LLVM_DEBUG({ + dbgs() << "Missing Offset = " << hexSquareString(Reference->getOffset()) + << "\n"; + }); + // We can't clone the abstract origin refernce, as it contain extra + // information that is incorrect for the element to be inserted. The + // symbols can be a variable or parameter. + LVSymbol *Symbol = new LVSymbol(); + addElement(Symbol); + Symbol->setIsOptimized(); + Symbol->setReference(Reference); + Reference->getIsParameter() ? Symbol->setIsParameter() + : Symbol->setIsVariable(); + } + } +} + +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 (const auto &Element : *Children) + Element->updateLevel(this, Moved); + + // Update any lines. + if (Lines) + for (const auto &Line : *Lines) + Line->updateLevel(this, Moved); +} + +void LVScope::resolve() { + if (getIsResolved()) + return; + + // Resolve the element itself. + LVElement::resolve(); + + // Resolve the children. + if (Children) + for (const auto &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(/*BaseText=*/emptyString(), BaseType); + } + } + + // In the case of unnamed scopes, try to generate a name for it, using + // the parents name and the line information. + if (!isNamed()) + 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. + resolveChainName(); + } + + // Set the file/line information. + 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 (const auto &Scope : *Scopes) { + LVScopeCompileUnit *CompileUnit = static_cast(Scope); + getReader().setCompileUnit(CompileUnit); + CompileUnit->resolve(); + // Propagate any matching information into the scopes tree. + CompileUnit->propagatePatternMatch(); + } +} + +// Follow a chain of references given by DW_AT_abstract_origin and/or +// DW_AT_specification and update the scope name. This setting is required +// for the comparison as the scopes names are already resolved. +StringRef LVScope::resolveChainName() { + // 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()->resolveChainName()); + + return getName(); +} + +// Get template parameter types. +bool LVScope::getTemplateParameterTypes(LVTypes &Params) { + // Traverse the scope types, looking for those types that are template + // parameters and create the template information block, which will be + // resolved by the 'EncodeTemplateArguments' function. + if (const LVTypes *Types = getTypes()) + for (auto &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; + // When we generate qualified names, do not include the encoded + // arguments in the name. Just include the generic template name. + // Encode the arguments as part of the template name and update the + // template name, to reflect the encoded parameters. + encodeTemplateArguments(EncodedArgs, &Params, /*QualifyBase=*/false); + setEncodedArgs(EncodedArgs.c_str()); + } + } +} + +// Get the qualified name for the template. +void LVScope::getQualifiedName(std::string &QualifiedName) { + 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 LVTypes *Types, + bool QualifyBase) { + // The 'QualifyBase' is true only when we are resolving a template + // parameter which is another template. If that template is already + // resolved, just get the qualified name and return. + if (QualifyBase) { + // 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'. + std::string BaseName; + getQualifiedName(BaseName); + if (getIsTemplateResolved()) + Name.append(BaseName); + } else { + // The encoded string will start with the scope name. + Name.append("<"); + + // The list of types are the template parameters. + if (Types) { + auto AddComma = false; + for (const auto &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; + + auto DoPrint = true; + auto Globals = options().getAttributeGlobal(); + auto 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()))) + DoPrint = false; + } + + // For the case of functions, skip it if unnamed (name is nullptr) or + // unlined (line number is zero) or compiler generated. + if (DoPrint && getIsFunction()) + if (!options().getAttributeGenerated() && (!isNamed() || getIsArtificial())) + DoPrint = false; + + // Check if we are using any pattern. + if (DoPrint) + // Indicate that this tree branch has a matched pattern. + if (options().getSelectExecute()) + DoPrint = getHasPattern(); + + return DoPrint; +} + +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). + auto 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) { + if (getIsInCompare()) + DoPrint = options().getReportView() || options().getReportDetails(); + else + DoPrint = 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().getPrintGeneric()) && + options().getPrintFormatting() && + getLevel() < options().getOutputLevel()) { + // Print the children. + if (Children) + for (const auto &Element : *Children) { + if (Match && !Element->getHasPattern()) + continue; + if (auto Err = + Element->doPrint(Split, Match, Print, *StreamSplit, Full)) + return Err; + } + + // Print the line records. + if (Lines) + for (const auto &Line : *Lines) { + if (Match && !Line->getHasPattern()) + continue; + if (auto 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(); + } + } + + 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; + 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 (const auto &Scope : *Parent->Scopes) + sort(Scope, SortFunction); + }; + + // Start traversing the scopes root and transform the element name. + sort(this, SortFunction); + } +} + +// Traverse the scope parents and execute the given 'set/get' functions. +void LVScope::traverse(LVScopeGetFunction GetFunction, + LVScopeSetFunction SetFunction) { + // 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(); + } +} + +// Traverse the scope parents and execute the given 'set/get' functions. +void LVScope::traverse(LVObjectGetFunction GetFunction, + LVObjectSetFunction SetFunction, bool Down) { + // 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(); + } + // If requested, traverse the children tree. + if (Down) + traverse(GetFunction, SetFunction); +} + +// Traverse the scope tree and execute the given 'callback' function. +void LVScope::traverse(LVObjectGetFunction GetFunction, + LVObjectSetFunction SetFunction) { + (this->*SetFunction)(); + + if (Types) + for (const auto &Type : *Types) + (Type->*SetFunction)(); + + if (Symbols) + for (const auto &Symbol : *Symbols) + (Symbol->*SetFunction)(); + + if (Lines) + for (const auto &Line : *Lines) + (Line->*SetFunction)(); + + if (Scopes) + for (const auto &Scope : *Scopes) + Scope->traverse(GetFunction, SetFunction); +} + +// Get all the locations associated with symbols. +void LVScope::getLocations(LVLocations *LocationList, + LVCheckLocation CheckLocation) { + // Traverse the children. + if (Children) + for (const auto &Element : *Children) + Element->getLocations(LocationList, CheckLocation); +} + +// Get all the ranges associated with scopes. +void LVScope::getRanges(LVLocations *LocationList, + LVCheckLocation CheckLocation) { + // Ignore discarded or stripped scopes (functions). + if (getIsDiscarded()) + return; + // Process the ranges for current scope. + if (Ranges) { + for (const auto &Location : *Ranges) { + // Add the invalid location object. + if ((Location->*CheckLocation)() && LocationList) + LocationList->push_back(Location); + } + // Calculate coverage factor. + calculateCoverage(); + } + // Traverse the scopes. + if (Scopes) + for (const auto &Scope : *Scopes) + Scope->getRanges(LocationList, CheckLocation); +} + +// 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 (const auto &Scope : *Scopes) + Scope->getRanges(RangeList); +} + +LVScope *LVScope::getOutermostParent(LVAddress Address) { + LVScope *Parent = this; + while (Parent) { + if (Ranges) + for (const auto &Location : *Ranges) + if (Location->getLowerAddress() <= Address) + return Parent; + Parent = Parent->getParentScope(); + } + return Parent; +} + +// Find the 'current' scope in the 'target' scope set. +LVScope *LVScope::findIn(const LVScopes *Targets) { + 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 (const auto &Target : *Targets) + if (LVScope::equals(Target)) + Candidates.push_back(Target); + + LLVM_DEBUG({ + if (Candidates.size()) { + dbgs() << "\n[LVScope::findIn]\n" + << "Reference: " + << "Offset = " << hexSquareString(getOffset()) << ", " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << "\n"; + for (const auto &Candidate : Candidates) + dbgs() << "Candidate: " + << "Offset = " << hexSquareString(Candidate->getOffset()) + << ", " + << "Level = " << Candidate->getLevel() << ", " + << "Kind = " << formattedKind(Candidate->kind()) << ", " + << "Name = " << formattedName(Candidate->getName()) << "\n"; + } + }); + + size_t Size = Candidates.size(); + if (Size) + return (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::sameChildren(const LVScope *Scope) const { + // Same number of children. Take into account which elements are requested + // to be included in the comparison. + if ((options().getCompareScopes() && scopeCount() != Scope->scopeCount()) || + (options().getCompareSymbols() && + symbolCount() != Scope->symbolCount()) || + (options().getCompareTypes() && typeCount() != Scope->typeCount()) || + (options().getCompareLines() && lineCount() != Scope->lineCount())) + return false; + return true; +} + +void LVScope::isContainedIn(const LVScope *Target, bool TraverseChildren) { + if (Types) + for (const auto &Type : *Types) + Type->setIsInCompare(); + if (Symbols) + for (const auto &Symbol : *Symbols) + Symbol->setIsInCompare(); + if (Lines) + for (const auto &Line : *Lines) + Line->setIsInCompare(); + if (Scopes) + for (const auto &Scope : *Scopes) + Scope->setIsInCompare(); + + // 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::isContainedIn(getTypes(), Target->getTypes()); + if (options().getCompareSymbols() && getSymbols() && Target->getSymbols()) + LVSymbol::isContainedIn(getSymbols(), Target->getSymbols()); + if (options().getCompareLines() && getLines() && Target->getLines()) + LVLine::isContainedIn(getLines(), Target->getLines()); + if (getScopes() && Target->getScopes()) + LVScope::isContainedIn(getScopes(), Target->getScopes(), TraverseChildren); +} + +void LVScope::isContainedIn(const LVScopes *References, const LVScopes *Targets, + bool TraverseChildren) { + LLVM_DEBUG({ + if (References && Targets) { + dbgs() << "\n[LVScope::isContainedIn]\n"; + for (const auto &Reference : *References) + dbgs() << "References: " + << "Offset = " << hexSquareString(Reference->getOffset()) << ", " + << "Level = " << Reference->getLevel() << ", " + << "Kind = " << formattedKind(Reference->kind()) << ", " + << "Name = " << formattedName(Reference->getName()) << "\n"; + for (const auto &Target : *Targets) + dbgs() << "Targets : " + << "Offset = " << hexSquareString(Target->getOffset()) << ", " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + } + }); + if (References && Targets) + for (const auto &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->isContainedIn(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 { + for (const auto &Current : *Scopes) + if (equals(Current)) + return Current; + 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 auto &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 (const auto &Element : *Children) + Element->report(Pass); + + if (Lines) + for (const auto &Line : *Lines) + Line->report(Pass); + getComparator().pop(); +} + +void LVScope::printActiveRanges(raw_ostream &OS, bool Full) { + if (options().getPrintFormatting() && options().getAttributeRange() && + Ranges) { + // For a nice printing layout, print a modified version of the scope + // attributes; modify the line number. + uint32_t LineNumber = getLineNumber(); + setLineNumber(0); + for (const auto &Location : *Ranges) + Location->print(OS, Full); + setLineNumber(LineNumber); + } +} + +void LVScope::printEncodedArgs(raw_ostream &OS, bool Full) const { + if (options().getPrintFormatting() && options().getAttributeEncoded()) + printAttributes(const_cast(this), OS, Full, "{Encoded} ", + 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 (!sameChildren(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 { + for (const auto &Current : *Scopes) + if (equals(Current)) + return Current; + 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(const_cast(this), OS, Full); + } +} + +//===----------------------------------------------------------------------===// +// DWARF Template alias. +//===----------------------------------------------------------------------===// +bool LVScopeAlias::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + return sameChildren(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 (auto &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(/*BaseText=*/emptyString(), BaseType); + } + std::stringstream ArrayInfo; + ArrayInfo << getTypeName().str() << " "; + + for (const auto &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 (!sameChildren(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; +} + +void LVScopeCompileUnit::addMapping(LVAddress Address, LVLine *Line) { + // Check for an already recorded mapping. + if (AddressToLine.find(Line->getOffset()) == AddressToLine.end()) + AddressToLine.insert(std::make_pair(Address, Line)); +} + +// 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 children + // as having that pattern, before any printing is done. + for (const auto &Scope : MatchedScopes) + Scope->traverse(&LVScope::getHasPattern, &LVScope::setHasPattern, + /*Down=*/true); +} + +void LVScopeCompileUnit::processRangeLocationCoverage() { + LVLocations Locations; + LVLocations *List; + + if (options().getAttributeRange()) { + // Traverse the scopes to get scopes that have invalid ranges. + List = (options().getWarningRanges()) ? &Locations : nullptr; + getRanges(List, &LVLocation::invalidRanges); + + // Validate ranges associated with scopes. + if (List) + for (const auto &Location : *List) + addInvalidRange(Location); + } + + if (options().getAttributeLocation()) { + // Traverse the scopes to get locations that have invalid ranges. + List = (options().getWarningLocations()) ? &Locations : nullptr; + getLocations(List, &LVLocation::invalidRanges); + + // Validate ranges associated with locations. + if (List) + for (const auto &Location : *List) + addInvalidLocation(Location); + } +} + +LVLine *LVScopeCompileUnit::getLowerLine(LVAddress Address) { + LVAddressToLine::const_iterator I = AddressToLine.find(Address); + if (I != AddressToLine.end()) + // We found a perfect match. + return I->second; + + I = AddressToLine.lower_bound(Address); + return (I != AddressToLine.end()) ? I->second : nullptr; +} + +LVLine *LVScopeCompileUnit::getUpperLine(LVAddress Address) { + LVAddressToLine::const_iterator I = AddressToLine.find(Address); + if (I != AddressToLine.end()) + // We found a perfect match. + return I->second; + + I = AddressToLine.lower_bound(Address); + if (I != AddressToLine.begin()) + I = std::prev(I); + return (I != AddressToLine.end()) ? I->second : nullptr; +} + +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 symbols with invalid coverage values. +void LVScopeCompileUnit::addInvalidCoverage(LVSymbol *Symbol) { + LVOffset Offset = Symbol->getOffset(); + if (InvalidCoverages.find(Offset) == InvalidCoverages.end()) + InvalidCoverages.insert(std::make_pair(Offset, Symbol)); +} + +// Record symbols with invalid locations. +void LVScopeCompileUnit::addInvalidLocation(LVLocation *Location) { + LVSymbol *Symbol = Location->getParentSymbol(); + LVOffset Offset = Symbol->getOffset(); + if (WarningOffsets.find(Offset) == WarningOffsets.end()) + WarningOffsets.insert(std::make_pair(Offset, Symbol)); + addItem( + &InvalidLocations, Offset, Location); +} + +// Record scopes with invalid ranges. +void LVScopeCompileUnit::addInvalidRange(LVLocation *Location) { + LVScope *Scope = Location->getParentScope(); + LVOffset Offset = Scope->getOffset(); + if (WarningOffsets.find(Offset) == WarningOffsets.end()) + WarningOffsets.insert(std::make_pair(Offset, Scope)); + addItem( + &InvalidRanges, Offset, Location); +} + +// Record line zero. +void LVScopeCompileUnit::addLineZero(LVLine *Line) { + LVScope *Scope = Line->getParentScope(); + LVOffset Offset = Scope->getOffset(); + if (WarningOffsets.find(Offset) == WarningOffsets.end()) + WarningOffsets.insert(std::make_pair(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"; + for (const auto &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('/'); + Name = + (Action == Option::File) ? Name.substr(Pos + 1) : Name.substr(0, Pos); + 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"; + for (const auto &Entry : PublicNames) { + OS << std::string(Indentation, ' ') << formattedKind(Kind) << " " + << formattedName(Entry.second.first->getName()); + if (options().getAttributeOffset()) + OS << " [" << hexString(Entry.first) << ":" + << hexString(Entry.first + Entry.second.second) << "]"; + OS << "\n"; + } + } +} + +void LVScopeCompileUnit::printWarnings(raw_ostream &OS, bool Full) const { + auto printHeader = [&](auto &OS, auto &PrintHeader, auto &Header) { + if (PrintHeader) { + OS << "\n" + << "* " << Header << " *\n"; + PrintHeader = false; + } + }; + auto printOffset = [&](auto &OS, auto &Count, auto Offset) { + if (Count == 5) { + Count = 0; + OS << "\n"; + } + ++Count; + OS << hexSquareString(Offset) << " "; + }; + auto printElement = [&](auto &OS, auto &Map, auto 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, + StringRef Header) { + auto PrintHeader = true; + for (const auto &Entry : Map) { + printHeader(OS, PrintHeader, Header); + printElement(OS, WarningOffsets, Entry.first); + for (const auto &Location : *Entry.second) + OS << hexSquareString(Location->getOffset()) << " " + << Location->getIntervalInfo() << "\n"; + } + }; + + if (options().getInternalTag()) { + auto PrintHeader = true; + for (const auto &Entry : DebugTags) { + printHeader(OS, PrintHeader, "Unsupported DWARF Tags"); + OS << format("\n0x%02x", (unsigned)Entry.first) << ", " + << dwarf::TagString(Entry.first) << "\n"; + unsigned Count = 0; + for (const auto &Offset : *Entry.second) + printOffset(OS, Count, Offset); + OS << "\n"; + } + } + + if (options().getWarningCoverages()) { + auto PrintHeader = true; + for (const auto &Entry : InvalidCoverages) { + printHeader(OS, PrintHeader, "Symbols Invalid Coverages"); + + // Symbol basic information. + LVSymbol *Symbol = Entry.second; + OS << hexSquareString(Entry.first) << " {Coverage} " + << format("%.2f%%", Symbol->getCoveragePercentage()) << " " + << formattedKind(Symbol->kind()) << " " + << formattedName(Symbol->getName()) << "\n"; + } + } + + if (options().getWarningLines()) { + auto PrintHeader = true; + for (const auto &Entry : LinesZero) { + printHeader(OS, PrintHeader, "Lines Zero References"); + printElement(OS, WarningOffsets, Entry.first); + unsigned Count = 0; + for (const auto &Line : *Entry.second) + printOffset(OS, Count, Line->getOffset()); + OS << "\n"; + } + } + + 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) { + const auto &Entry = Sizes.find(Scope); + if (Entry != Sizes.end()) { + LVOffset Size = Entry->second; + assert(CUContributionSize && "Invalid CU contribution size."); + float Percentage = 100 * float(Size) / CUContributionSize; + 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; + printScope = [&](LVScope *Scope) { + // If we have selection criteria, then use only the selected scopes. + if (options().getSelectExecute() && options().getReportView()) { + for (const auto &Scope : MatchedScopes) + if (Scope->getLevel() < options().getOutputLevel()) + printScopeSize(Scope, OS); + return; + } + if (Scope->getLevel() < options().getOutputLevel()) { + if (auto Scopes = Scope->getScopes()) + for (const auto &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 { + auto 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) { + if (MatchedElements.empty()) + return; + 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().getPrintGeneric()) { + OS << "\n"; + print(OS); + for (const auto &Element : MatchedElements) + Element->print(OS); + + // Print any requested summary. + if (options().getReportSummary()) + printSummary(OS, Found, "Found"); + } + + // 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 (const auto &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 { + StringRef Name = getName(); + if (!options().getAttributePathname()) { + size_t Pos = Name.rfind('/'); + if (Pos != std::string::npos) + Name = Name.substr(Pos + 1); + } + OS << formattedKind(kind()) << " '" << Name << "'\n"; + + if (options().getPrintFormatting() && options().getAttributeProducer()) + printAttributes(const_cast(this), OS, Full, + "{Producer} ", 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 sameChildren(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 sameChildren(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()) { + // Add missing elements at the function scope. + addMissingElements(getReference()); + if (Scopes) + for (const auto &Scope : *Scopes) + if (Scope->getHasReferenceAbstract()) + // 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->getIsExternal()) { + Reference->resetExternal(); + setIsExternal(); + } + } + + // Resolve the function associated type. + if (!getType()) + if (LVScope *Reference = getReference()) + setType(Reference->getType()); +} + +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()) + if (!sameChildren(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()) + if (!LVLine::equals(getLines(), Scope->getLines())) + return false; + + // Check if any reference is the same. + if (!referenceMatch(Scope)) + return false; + + if (getReference()) + if (!getReference()->equals(Scope->getReference())) + return false; + + return true; +} + +LVScope *LVScopeFunction::equals(const LVScopes *Scopes) const { + // Go through candidates and try to find a best match. + for (const auto &Current : *Scopes) + // Match arguments, children, lines, references. + if (equals(Current)) + return Current; + 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 = + 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 (Reference) + Reference->printReference(const_cast(this), OS, Full); + } +} + +//===----------------------------------------------------------------------===// +// 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. + auto AddComma = false; + if (const LVSymbols *Symbols = getSymbols()) { + AddComma = false; + for (auto &Symbol : *Symbols) + if (Symbol->getIsParameter()) { + Symbol->resolve(); + if (LVElement *Type = Symbol->getType()) { + Type->resolveName(); + Symbol->resolveFullname(/*BaseText=*/emptyString(), Type); + } + 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 (!sameChildren(Scope)) + return false; + + // Check if any reference is the same. + if (!referenceMatch(Scope)) + return false; + + if (getReference()) + if (!getReference()->equals(Scope->getReference())) + return false; + + return true; +} + +LVScope *LVScopeNamespace::equals(const LVScopes *Scopes) const { + // Go through candidates and try to find a best match. + for (const auto &Current : *Scopes) + if (equals(Current)) + return Current; + 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); + + LVScope *Reference = getReference(); + if (Reference) + Reference->printReference(const_cast(this), OS, Full); + } +} + +//===----------------------------------------------------------------------===// +// An object file (single or multiple CUs). +//===----------------------------------------------------------------------===// +void LVScopeRoot::processRangeInformation() { + if (!options().getAttributeAnyLocation()) + return; + + if (Scopes) + for (const auto &Scope : *Scopes) { + LVScopeCompileUnit *CompileUnit = + static_cast(Scope); + getReader().setCompileUnit(CompileUnit); + CompileUnit->processRangeLocationCoverage(); + } +} + +void LVScopeRoot::transformScopedName() { + // Recursively transform all names. + std::function traverseScope; + traverseScope = [&](LVScope *Parent) { + auto traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) + Entry->setInnerComponent(); + }; + if (auto Scopes = Parent->getScopes()) + for (const auto &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) 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) { + options().resetPrintFormatting(); + print(OS); + for (const auto &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); + + // Done printing the compile unit. Restore the original output context. + if (Split) { + getReaderSplitContext().close(); + StreamSplit = &getReader().outputStream(); + } + } + 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 sameChildren(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,132 @@ +//===-- 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/LVObject.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 std::string(LHS->getName()) < std::string(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) { + // Use the kind as the first key. + if (std::string(LHS->kind()) != std::string(RHS->kind())) + return compareKind(LHS, RHS); + + // Kinds are the same; use the names as key. + if (std::string(LHS->getName()) != std::string(RHS->getName())) + return compareName(LHS, RHS); + + // Names are the same; use the lines as key. + if (LHS->getLineNumber() != RHS->getLineNumber()) + return compareLine(LHS, RHS); + else + return compareOffset(LHS, RHS); +} + +// Callback comparator based on multiple keys (First: Line). +LVSortValue llvm::logicalview::sortByLine(const LVObject *LHS, + const LVObject *RHS) { + // Use the line number as the first key. + if (LHS->getLineNumber() != RHS->getLineNumber()) + return compareLine(LHS, RHS); + + // Lines are the same; use the names. + if (std::string(LHS->getName()) != std::string(RHS->getName())) + return compareName(LHS, RHS); + + // Names are the same; use the kinds. + if (std::string(LHS->kind()) != std::string(RHS->kind())) + return compareKind(LHS, RHS); + else + return compareOffset(LHS, RHS); +} + +// Callback comparator based on multiple keys (First: Name). +LVSortValue llvm::logicalview::sortByName(const LVObject *LHS, + const LVObject *RHS) { + // Use the name as the first key. + if (std::string(LHS->getName()) != std::string(RHS->getName())) + return compareName(LHS, RHS); + + // Names are the same; use the lines. + if (LHS->getLineNumber() != RHS->getLineNumber()) + return compareLine(LHS, RHS); + + // Lines are the same; use the kinds. + if (std::string(LHS->kind()) != std::string(RHS->kind())) + return compareKind(LHS, RHS); + else + return compareOffset(LHS, RHS); +} + +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 I = SortInfo.find(options().getSortMode()); + if (I != SortInfo.end()) + SortFunction = I->second; + return SortFunction; +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp @@ -0,0 +1,159 @@ +//===-- 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 +#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; +} + +// Flat a path (transforms '/', '\', '.', ':' into '_'). +std::string llvm::logicalview::flattenedFilePath(StringRef Path) { + // Transform the given name into a single string; the input name, can be + // the full pathname and will include the ':', '/', '.' characters. The + // idea is to generate an unique single name. + 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(), ':', '_'); + return Name; +} + +using LexicalEntry = std::pair; +using LexicalIndexes = SmallVector; + +LexicalIndexes getAllLexicalIndices(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; + 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(std::make_pair(Current, Index - 2)); + Current = Index + 1; + CharsSeen = 0; + LLVM_DEBUG({ + LexicalEntry Entry = Indexes.back(); + llvm::dbgs() << formatv( + "'{0}:{1}', '{2}'\n", Entry.first, Entry.second, + Name.substr(Entry.first, Entry.second - Entry.first + 1)); + }); + } + ColonSeen = 0; + continue; + } + if (InTemplate && !AngleCount) + InTemplate = false; + } + + // Store last component. + Indexes.push_back(std::make_pair(Current, Length - 1)); + LLVM_DEBUG({ + LexicalEntry Entry = Indexes.back(); + llvm::dbgs() << formatv( + "'{0}:{1}', '{2}'\n", Entry.first, Entry.second, + Name.substr(Entry.first, Entry.second - Entry.first + 1)); + }); + return Indexes; +} + +LVLexicalComponent llvm::logicalview::getInnerComponent(StringRef Name) { + if (Name.empty()) + return {}; + + LexicalIndexes Indexes = getAllLexicalIndices(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 = getAllLexicalIndices(Name); + LVStringRefs Components; + for (const auto &Entry : Indexes) + Components.push_back( + Name.substr(Entry.first, Entry.second - Entry.first + 1)); + + return Components; +} + +std::string llvm::logicalview::getScopedName(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,436 @@ +//===-- 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 *KindConstant = "Constant"; +const char *KindInherits = "Inherits"; +const char *KindMember = "Member"; +const char *KindParameter = "Parameter"; +const char *KindUndefined = "Undefined"; +const char *KindUnspecified = "Unspecified"; +const char *KindVariable = "Variable"; +} // end anonymous namespace + +// Return a string representation for the symbol kind. +const char *LVSymbol::kind() const { + static const char *Kind = KindUndefined; + 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; +} + +LVSymbolTarget LVSymbol::Targets = { + {LVSymbolKind::Constant, &LVSymbol::getIsConstant}, + {LVSymbolKind::Inheritance, &LVSymbol::getIsInheritance}, + {LVSymbolKind::Member, &LVSymbol::getIsMember}, + {LVSymbolKind::Parameter, &LVSymbol::getIsParameter}, + {LVSymbolKind::Unspecified, &LVSymbol::getIsUnspecified}, + {LVSymbolKind::Variable, &LVSymbol::getIsVariable}}; + +LVSymbol::~LVSymbol() { + if (Locations) { + for (const auto &Location : *Locations) + delete Location; + delete Locations; + } +} + +// Add a Location Entry. +void LVSymbol::addLocation(LVSmall Opcode, LVAddress LowPC, LVAddress HighPC, + LVUnsigned SectionOffset, uint64_t LocDescOffset) { + if (!Locations) + Locations = new LVLocations(); + + // Create the location entry. + CurrentLocation = new LVLocationSymbol(); + CurrentLocation->setParent(this); + CurrentLocation->setOpcode(Opcode); + 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(LVSmall Opcode, LVUnsigned Constant, + uint64_t LocDescOffset) { + // Create a Location Entry, with the global information. + addLocation(Opcode, + /*LowPC=*/0, /*HighPC=*/-1, + /*SectionOffset=*/0, LocDescOffset); + + // Add records to Location Entry. + addLocationOperands(/*Opcode=*/(LVSmall)0, + /*Operand1=*/Constant, /*Operand2=*/(uint64_t)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 auto &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, + LVCheckLocation CheckLocation) { + if (!Locations) + return; + for (const auto &Location : *Locations) { + // Add the invalid location object. + if ((Location->*CheckLocation)() && LocationList) + LocationList->push_back(Location); + } + + // Calculate coverage factor. + calculateCoverage(); +} + +// 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 outher 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. + // Let the verification of locations, to pickup any invalid entry. + // If no scope contains the location, assume its enclosing parent. + LVScope *Scope = + Parent->getOutermostParent(Locations->front()->getLowerAddress()); + if (Scope) + Parent = Scope; + } + unsigned CoverageParent = Parent->getCoverageFactor(); + CoveragePercentage = + CoverageParent ? float(CoverageFactor * 100) / float(CoverageParent) + : 0; + // Record invalid coverage entry. + if (options().getWarningCoverages() && CoveragePercentage > 100) + getReader().getCompileUnit()->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. + resolveChainName(); + } + + // Set the file/line information. + 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()); +} + +// Follow a chain of references given by DW_AT_abstract_origin and/or +// DW_AT_specification and update the symbol name. This setting is required +// for the comparison as the symbol names are already resolved. +StringRef LVSymbol::resolveChainName() { + // 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()->resolveChainName()); + + return getName(); +} + +void LVSymbol::isContainedIn(const LVSymbols *References, + const LVSymbols *Targets) { + LLVM_DEBUG({ + if (References && Targets) { + dbgs() << "\n[LVSymbol::isContainedIn]\n"; + for (const auto &Reference : *References) + dbgs() << "References: " + << "Kind = " << formattedKind(Reference->kind()) << ", " + << "Name = " << formattedName(Reference->getName()) << "\n"; + for (const auto &Target : *Targets) + dbgs() << "Targets : " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + } + }); + if (References && Targets) + for (const auto &Reference : *References) { + LLVM_DEBUG({ + dbgs() << "Search Reference: Name = " + << formattedName(Reference->getName()) << "\n"; + }); + if (!Reference->findIn(Targets)) + Reference->markBranchAsMissing(); + } +} + +// Find the 'current' symbol in the 'target' symbol set. +LVSymbol *LVSymbol::findIn(const LVSymbols *Targets) { + if (Targets) { + LLVM_DEBUG({ + dbgs() << "\n[LVSymbol::findIn]\n" + << "Reference: " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << "\n"; + for (const auto &Target : *Targets) + dbgs() << "Target : " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + + for (const auto &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 (const auto &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()) + if (!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 auto &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 auto &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 = 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 (auto 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 (LVSymbol *Reference = getReference()) + Reference->printReference(const_cast(this), OS, Full); + + 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,524 @@ +//===-- 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" +#include + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Type" + +namespace { +const char *KindBaseType = "BaseType"; +const char *KindConst = "Const"; +const char *KindEnumerator = "Enumerator"; +const char *KindImport = "Import"; +const char *KindPointer = "Pointer"; +const char *KindPointerMember = "PointerMember"; +const char *KindReference = "Reference"; +const char *KindRestrict = "Restrict"; +const char *KindRvalueReference = "RvalueReference"; +const char *KindSubrange = "Subrange"; +const char *KindTemplateTemplate = "TemplateTemplate"; +const char *KindTemplateType = "TemplateType"; +const char *KindTemplateValue = "TemplateValue"; +const char *KindTypeAlias = "TypeAlias"; +const char *KindUndefined = "Undefined"; +const char *KindUnspecified = "Unspecified"; +const char *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 (getIsTemplateType()) + Kind = KindTemplateType; + else if (getIsTemplateValue()) + Kind = KindTemplateValue; + else if (getIsTemplateTemplate()) + Kind = KindTemplateTemplate; + else if (getIsTypedef()) + Kind = KindTypeAlias; + else if (getIsUnspecified()) + Kind = KindUnspecified; + else if (getIsVolatile()) + Kind = KindVolatile; + return Kind; +} + +LVTypeTarget LVType::Targets = { + {LVTypeKind::Base, &LVType::getIsBase}, + {LVTypeKind::Const, &LVType::getIsConst}, + {LVTypeKind::Enumerator, &LVType::getIsEnumerator}, + {LVTypeKind::Import, &LVType::getIsImport}, + {LVTypeKind::ImportDeclaration, &LVType::getIsImportDeclaration}, + {LVTypeKind::ImportModule, &LVType::getIsImportModule}, + {LVTypeKind::Pointer, &LVType::getIsPointer}, + {LVTypeKind::PointerMember, &LVType::getIsPointerMember}, + {LVTypeKind::Reference, &LVType::getIsReference}, + {LVTypeKind::Restrict, &LVType::getIsRestrict}, + {LVTypeKind::RvalueReference, &LVType::getIsRvalueReference}, + {LVTypeKind::Subrange, &LVType::getIsSubrange}, + {LVTypeKind::TemplateParam, &LVType::getIsTemplateParam}, + {LVTypeKind::TemplateTemplateParam, &LVType::getIsTemplateTemplate}, + {LVTypeKind::TemplateTypeParam, &LVType::getIsTemplateType}, + {LVTypeKind::TemplateValueParam, &LVType::getIsTemplateValue}, + {LVTypeKind::Typedef, &LVType::getIsTypedef}, + {LVTypeKind::Unspecified, &LVType::getIsUnspecified}, + {LVTypeKind::Volatile, &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. + 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(getName(), BaseType); + + // 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); +} + +// Follow a chain of references given by DW_AT_abstract_origin and/or +// DW_AT_specification and update the type name. This setting is required +// for the comparison as the type names are already resolved. +StringRef LVType::resolveChainName() { + // The types do not have a DW_AT_specification or DW_AT_abstract_origin + // reference. Just return the type name. + return getName(); +} + +void LVType::isContainedIn(const LVTypes *References, const LVTypes *Targets) { + LLVM_DEBUG({ + if (References && Targets) { + dbgs() << "\n[LVType::isContainedIn]\n"; + for (const auto &Reference : *References) + dbgs() << "References: " + << "Kind = " << formattedKind(Reference->kind()) << ", " + << "Name = " << formattedName(Reference->getName()) << "\n"; + for (const auto &Target : *Targets) + dbgs() << "Targets : " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + } + }); + if (References && Targets) + for (const auto &Reference : *References) { + LLVM_DEBUG({ + dbgs() << "Search Reference: Name = " + << formattedName(Reference->getName()) << "\n"; + }); + if (!Reference->findIn(Targets)) + Reference->markBranchAsMissing(); + } +} + +// Find the 'current' type in the 'target' type set. +LVType *LVType::findIn(const LVTypes *Targets) { + if (Targets) { + LLVM_DEBUG({ + dbgs() << "\n[LVType::findIn]\n" + << "Reference: " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << "\n"; + for (const auto &Target : *Targets) + dbgs() << "Target : " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + + for (const auto &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 (auto Type : *Types) { + if (!Type->getIsTemplateParam()) + continue; + if (options().getAttributeArgument()) { + LVScope *Scope = nullptr; + if (Type->getIsKindType()) + Type = Type->getType(nullType()); + else { + if (Type->getIsKindScope()) { + Scope = Type->getType(nullScope()); + 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 auto &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 = getType(nullScope()); + if (BaseType) + // Underlying type is a scope. + return BaseType; + + LVType *Type = getType(nullType()); + assert(Type && "Type definition does not have a type."); + + BaseType = Type; + while (Type->getIsTypedef()) { + BaseType = Type->getType(nullScope()); + if (BaseType) + // Underlying type is a scope. + return BaseType; + + Type = Type->getType(nullType()); + 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(/*BaseText=*/emptyString(), Type); + } + } +} + +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) { + // 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 the incomplete names: + // "type" + // "type" + // which causes the debugger's class de-duplication to remove the second + // definition and both variables end up using the first 'type'. + + if (getIsTemplateType()) { + // 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 = getType(nullType()); + // 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 = getType(nullScope()); + // If the scope is a template, we have to resolve that template, + // by recursively traversing its arguments. + if (ArgScope->getIsTemplate()) + ArgScope->encodeTemplateArguments(Name, /*Types=*/nullptr, true); + 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 (getIsTemplateType() && Type->getIsTemplateType()) + return getType()->equals(Type->getType()); + + if ((getIsTemplateValue() && Type->getIsTemplateValue()) || + (getIsTemplateTemplate() && Type->getIsTemplateTemplate())) + 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 (getIsTemplateType()) { + OS << formattedNames(getTypeQualifiedName(), getTypeName()) << "\n"; + return; + } + if (getIsTemplateValue()) { + OS << formattedName(getValue()) << " " << formattedName(getName()) << "\n"; + return; + } + if (getIsTemplateTemplate()) + 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; + + if ((getTypeName() != Type->getTypeName()) || (getName() != Type->getName())) + return false; + + return true; +} + +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 \ No newline at end of file Index: llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp @@ -0,0 +1,271 @@ +//===-- 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/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.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/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/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 (auto Err = createReaders()) + return Err; + if (auto Err = printReaders()) + return Err; + if (auto Err = compareReaders()) + return Err; + + return Error::success(); +} + +void LVReaderHandler::destroyReaders() { + LLVM_DEBUG(dbgs() << "destroyReaders\n"); + for (const auto &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()) { + auto *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 (auto &Child : Arch.children(Err)) { + auto BuffOrErr = Child.getMemoryBufferRef(); + if (auto Err = BuffOrErr.takeError()) + return createStringError(errorToErrorCode(std::move(Err)), "%s", + TheFilename.c_str()); + auto NameOrErr = Child.getName(); + if (auto Err = NameOrErr.takeError()) + return createStringError(errorToErrorCode(std::move(Err)), "%s", + TheFilename.c_str()); + std::string Name = (Filename + "(" + NameOrErr.get() + ")").str(); + if (auto Err = handleBuffer(Readers, Name, BuffOrErr.get())) + return createStringError(errorToErrorCode(std::move(Err)), "%s", + TheFilename.c_str()); + } + + return Error::success(); +} + +static std::string searchForExe(const StringRef Path) { + std::vector Extensions = {"o", "obj", "exe", "lib", "dll"}; + SmallString<128> ExePath(Path); + for (const auto Extension : Extensions) { + llvm::sys::path::replace_extension(ExePath, Extension); + // Check if pdb exists in the executable directory. + std::unique_ptr Session; + if (auto Err = loadDataForEXE(PDB_ReaderType::Native, ExePath, Session)) { + llvm::consumeError(std::move(Err)); + continue; + } + // We have a candidate for the executable image. + Expected PdbPath = NativeSession::searchForPdb({ExePath}); + if (PdbPath && PdbPath.get() == Path) + return std::string(ExePath); + } + return std::string(); +} + +Error LVReaderHandler::handleBuffer(LVReaders &Readers, StringRef Filename, + MemoryBufferRef Buffer) { + // As PDB does not support the Binary interface, at this point we can check + // if the buffer corresponds to a PDB or PE file. + if (identify_magic(Buffer.getBuffer()) == file_magic::pdb) { + return handleObject(Readers, Filename, Buffer.getBuffer(), + searchForExe(Filename)); + } + if (identify_magic(Buffer.getBuffer()) == 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()); + } + return handleObject(Readers, PdbPath.get(), Buffer.getBuffer(), Filename); + } + + Expected> BinOrErr = object::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) { + ErrorOr> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(Filename); + if (BuffOrErr.getError()) { + std::string TheFilename(Filename); + 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, Filename, *Buffer); +} + +Error LVReaderHandler::handleMach(LVReaders &Readers, StringRef Filename, + MachOUniversalBinary &Mach) { + for (auto &ObjForArch : Mach.objects()) { + std::string ObjName = (Twine(Filename) + Twine("(") + + Twine(ObjForArch.getArchFlagName()) + Twine(")")) + .str(); + if (auto MachOOrErr = ObjForArch.getAsObjectFile()) { + auto &Obj = **MachOOrErr; + PdbOrObj Input = &Obj; + if (auto Err = + createReader(Filename, Readers, Input, Obj.getFileFormatName())) + return Err; + continue; + } else + consumeError(MachOOrErr.takeError()); + if (auto ArchiveOrErr = ObjForArch.getAsArchive()) { + return createStringError(errorToErrorCode(ArchiveOrErr.takeError()), "%s", + ObjName.c_str()); + if (auto 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 (auto *Fat = dyn_cast(&Binary)) + return handleMach(Readers, Filename, *Fat); + + if (auto *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 (auto 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 (auto &Object : Objects) { + LVReaders Readers; + if (auto 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 (const auto &Reader : TheReaders) + if (options().getPrintExecute()) + if (auto Err = Reader->doPrint()) + return Err; + + return Error::success(); +} + +Error LVReaderHandler::compareReaders() { + LLVM_DEBUG(dbgs() << "compareReaders\n"); + auto 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 (auto 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/LVCodeViewReader.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp @@ -0,0 +1,1017 @@ +//===-- 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/DebugInlineeLinesSubsection.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/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::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) + +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()); + + auto 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) { + Expected NameOrErr = getFileNameForFileOffset(FileOffset); + 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 &Reloc : Section.relocations()) + RelocMap[CoffSection].push_back(Reloc); + + // 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]; + auto SymI = getObj().symbol_end(); + for (const auto &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(); +} + +// Helper for when you have a pointer to real data and you want to know about +// relocations against it. +Error LVCodeViewReader::resolveSymbolName(const coff_section *CoffSection, + StringRef SectionContents, + const void *RelocPtr, + StringRef &Name) { + assert(SectionContents.data() < RelocPtr && + RelocPtr < SectionContents.data() + SectionContents.size() && + "pointer to relocated object is not in section"); + uint64_t Offset = ptrdiff_t(reinterpret_cast(RelocPtr) - + SectionContents.data()); + return resolveSymbolName(CoffSection, Offset, Name); +} + +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()); + + LLVM_DEBUG({ + for (auto &Line : Lines) { + DictScope S(W, "InlineeSourceLine"); + LogicalVisitor.printTypeIndex("Inlinee", Line.Header->Inlinee, StreamTPI); + if (auto Err = printFileNameForOffset("FileID", Line.Header->FileID)) + return Err; + W.printNumber("SourceLineNum", Line.Header->SourceLineNum); + + if (Lines.hasExtraFiles()) { + W.printNumber("ExtraFileCount", Line.ExtraFiles.size()); + ListScope ExtraFiles(W, "ExtraFiles"); + for (const auto &FID : Line.ExtraFiles) + if (auto Err = printFileNameForOffset("FileID", FID)) + return Err; + } + } + }); + + return Error::success(); +} + +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 auto &Line : LineNumbers) { + if (Line.Offset >= Size) + return createStringError(object_error::parse_failed, getFileName()); + + codeview::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')); + }); + + // Create the logical line record. + LVLineDebug *LineDebug = new LVLineDebug(); + CULines.push_back(LineDebug); + LVAddress Address = SG ? segmentToLinear(Segment, Begin + Line.Offset) + : uint32_t(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()); + }); + + StringRef Filename = TS.getName(); + BuffOrErr = MemoryBuffer::getFile(Filename); + if (BuffOrErr.getError()) { + std::string TheFilename(Filename); + 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 (auto Err = loadDataForPDB(PDB_ReaderType::Native, Filename, Session)) { + std::string TheFilename(Filename); + 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. + auto 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); + + auto &Types = types(); + auto &Ids = ids(); + if (auto 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()); + }); + + StringRef Filename = Precomp.getPrecompFilePath(); + BuffOrErr = MemoryBuffer::getFile(Filename); + if (BuffOrErr.getError()) { + std::string TheFilename(Filename); + return createStringError(errc::bad_file_descriptor, + "File '%s' does not exist.", TheFilename.c_str()); + } + MemBuffer = std::move(BuffOrErr.get()); + + Expected> BinOrErr = object::createBinary(*MemBuffer); + if (errorToErrorCode(BinOrErr.takeError())) { + std::string TheFilename(Filename); + 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(Filename); + 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 = *dyn_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 (auto 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 (auto &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 (auto &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); + + auto &Types = types(); + auto &Ids = ids(); + LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamTPI); + return codeview::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 (auto 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); + } + + auto &Types = types(); + auto &Ids = ids(); + Types.reset(*DataOrErr, 100); + LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamTPI); + return codeview::visitTypeStream(Types, TDV); +} + +Error LVCodeViewReader::traverseTypes(PDBFile &Pdb, + LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids) { + // Traverse types (TPI and IPI). + auto visitTypes = [&](auto &Types, auto &Ids, auto StreamIdx) -> Error { + LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamIdx); + return codeview::visitTypeStream(Types, TDV); + }; + + auto StreamTpiOrErr = Pdb.getPDBTpiStream(); + if (!StreamTpiOrErr) + return StreamTpiOrErr.takeError(); + auto &StreamTpi = *StreamTpiOrErr; + StreamTpi.buildHashMap(); + LLVM_DEBUG({ + W.getOStream() << formatv("\nShowing {0:N} TPI records\n", + StreamTpi.getNumTypeRecords()); + }); + if (auto Err = visitTypes(Types, Ids, StreamTPI)) + return Err; + + auto StreamIpiOrErr = Pdb.getPDBIpiStream(); + if (!StreamIpiOrErr) + return StreamIpiOrErr.takeError(); + auto &StreamIpi = *StreamIpiOrErr; + StreamIpi.buildHashMap(); + LLVM_DEBUG({ + W.getOStream() << formatv("\nShowing {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()); + + auto &Types = types(); + auto &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); + + 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 FunctionNames; + 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 (auto 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 (auto Err = + traverseSymbolsSubsection(Contents, Section, SectionContents)) + return Err; + break; + + case DebugSubsectionKind::InlineeLines: + if (auto 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 LinkageName; + if (auto Err = resolveSymbolName(getObj().getCOFFSection(Section), + SectionOffset, LinkageName)) + return createStringError(errorToErrorCode(std::move(Err)), + getFileName()); + + LLVM_DEBUG({ W.printString("LinkageName", LinkageName); }); + if (FunctionLineTables.count(LinkageName) != 0) { + // Saw debug info for this function already? + return createStringError(object_error::parse_failed, getFileName()); + } + + FunctionLineTables[LinkageName] = Contents; + FunctionNames.push_back(LinkageName); + } + 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 (auto &LinkageName : FunctionNames) { + LLVM_DEBUG({ + ListScope S(W, "FunctionLineTable"); + W.printString("LinkageName", LinkageName); + }); + + BinaryStreamReader Reader(FunctionLineTables[LinkageName], support::little); + + DebugLinesSubsectionRef Lines; + if (Error E = Lines.initialize(Reader)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + // Find the associate logical function. + LVScope *Function = LogicalVisitor.getLinkageScope(LinkageName); + if (!Function) + continue; + + LVAddress addendum = LogicalVisitor.getLinkageAddress(LinkageName); + + // 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. + 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 auto &Block : Lines) + if (auto Err = createLines(Block.LineNumbers, addendum, Segment, Begin, + Size, Block.NameIndex)) + return Err; + + processLines(); + } + + return Error::success(); +} + +void LVCodeViewReader::sortScopes() { Root->sort(); } + +void LVCodeViewReader::print(raw_ostream &OS) const { + LLVM_DEBUG(dbgs() << "CreateReaders\n"); +} + +void LVCodeViewReader::mapRangeAddress(const object::ObjectFile &Obj, + const object::SectionRef &Sec) { + if (!Obj.isCOFF()) + return; + auto *Object = dyn_cast(&Obj); + + for (const SymbolRef &Sym : Object->symbols()) { + if (!Sec.containsSymbol(Sym)) + continue; + + COFFSymbolRef Symbol = Object->getCOFFSymbol(Sym); + if (Symbol.getComplexType() != llvm::COFF::IMAGE_SYM_DTYPE_FUNCTION) + continue; + + StringRef SymbolName; + if (Expected SymNameOrErr = Object->getSymbolName(Symbol)) + SymbolName = *SymNameOrErr; + + LLVM_DEBUG({ + Expected SecOrErr = + Object->getSection(Symbol.getSectionNumber()); + if (!SecOrErr) { + W.startLine() << "Invalid section number: " << Symbol.getSectionNumber() + << "\n"; + consumeError(SecOrErr.takeError()); + return; + } + W.printString("Name", SymbolName); + W.printHex("Value", Symbol.getValue()); + }); + + // Record the symbol name (linkage) and its loading address. + LogicalVisitor.addLinkageScope(SymbolName, Symbol.getValue()); + } +} + +Error LVCodeViewReader::createScopes(COFFObjectFile &Obj) { + // 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 (auto 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 (auto 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.processElements(); + + // Translate composite names into a single component. + Root->transformScopedName(); + return Error::success(); +} + +Error LVCodeViewReader::createScopes(PDBFile &Pdb) { + if (!Pdb.hasPDBTpiStream() || !Pdb.hasPDBDbiStream()) + return Error::success(); + + // 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 + + auto &Types = types(); + auto &Ids = ids(); + if (auto Err = traverseTypes(Pdb, Types, Ids)) + return Err; + + // Process collected namespaces. + LogicalVisitor.processNamespaces(); + + // Traverse global symbols. + LLVM_DEBUG({ W.getOStream() << "\nTraversing global symbols\n"; }); + if (Pdb.hasPDBGlobalsStream()) { + auto GlobalsOrErr = Pdb.getPDBGlobalsStream(); + if (!GlobalsOrErr) + return GlobalsOrErr.takeError(); + auto &Globals = *GlobalsOrErr; + const GSIHashTable &Table = Globals.getGlobalsTable(); + auto ExpectedSyms = Pdb.getPDBSymbolStream(); + if (ExpectedSyms) { + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, nullptr); + + // 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 (auto Err = Traverser.visitSymbolBegin(Symbol, Offset)) + consumeError(std::move(Err)); + else { + 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 (auto 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() << "\nTraversing symbol groups\n"; }); + + if (auto Err = iterateSymbolGroups( + Input, [&](uint32_t Modi, const SymbolGroup &SG) -> Error { + auto ExpectedModS = getModuleDebugStream(Pdb, Modi); + if (ExpectedModS) { + ModuleDebugStreamRef &ModS = *ExpectedModS; + + LLVM_DEBUG({ + W.getOStream() + << formatv("\nTraversing Group: Mod {0:4}\n", Modi); + }); + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, + nullptr); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Traverser); + CVSymbolVisitor Visitor(Pipeline); + auto SS = ModS.getSymbolsSubstream(); + if (auto 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(); + })) + 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() << "\nTraversing lines\n"; }); + + // Record all line records for a Compile Unit. + CULines.clear(); + + if (auto Err = iterateModuleSubsections( + Input, + [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) { + processModule(); + CurrentModule = Modi; + } + + for (const auto &Block : Lines) + if (auto Err = + createLines(Block.LineNumbers, /*addendum=*/0, Segment, + Begin, Size, Block.NameIndex, &SG)) + return Err; + + return Error::success(); + })) + return Err; + + LLVM_DEBUG({ W.getOStream() << "\nTraversing inlined lines\n"; }); + + if (auto Err = iterateModuleSubsections( + Input, + [](int32_t Modi, const SymbolGroup &SG, + DebugInlineeLinesSubsectionRef &Lines) -> Error { + return Error::success(); + })) + return Err; + + // Check if we have to close the Compile Unit scope. + LogicalVisitor.closeScope(); + + // Process collected element lines. + LogicalVisitor.processElements(); + + // Translate composite names into a single component. + Root->transformScopedName(); + return Error::success(); +} + +void LVCodeViewReader::processModule() { + if (LVScope *Scope = getScopeForModule(CurrentModule)) { + CompileUnit = static_cast(Scope); + + // For the given compile unit, collect all scopes ranges. + ScopesWithRanges.clear(); + + // 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. + CompileUnit->getRanges(&ScopesWithRanges); + if (!ScopesWithRanges.empty()) + CompileUnit->addObject(ScopesWithRanges.getLower(), + ScopesWithRanges.getHigher()); + + ScopesWithRanges.sort(); + + processLines(); + ScopesWithRanges.clear(); + } + + CULines.clear(); +} + +// 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("Format", FileFormatName); + }); + + if (auto Err = LVReader::createScopes()) + return Err; + + LogicalVisitor.setRoot(Root); + + if (isObj()) { + if (auto Err = createScopes(getObj())) + return Err; + } else { + if (auto Err = createScopes(getPdb())) + return Err; + } + + // Print the record tags. + if (options().getInternalTag()) + LogicalVisitor.printRecordTag(); + + return Error::success(); +} Index: llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp @@ -0,0 +1,3521 @@ +//===-- 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/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; + +//===----------------------------------------------------------------------===// +// This code has been extracted from llvm-pdbutil module. For the time being +// it used just to get the PDB reading going. After that, see what kind of +// refactoring can be done and move it to the debug info libraries. +//===----------------------------------------------------------------------===// +// START +//===----------------------------------------------------------------------===// + +static inline bool isCodeViewDebugSubsection(object::SectionRef Section, + StringRef Name, + BinaryStreamReader &Reader) { + if (Expected NameOrErr = Section.getName()) { + if (*NameOrErr != Name) + return false; + } else { + consumeError(NameOrErr.takeError()); + return false; + } + + Expected ContentsOrErr = Section.getContents(); + if (!ContentsOrErr) { + consumeError(ContentsOrErr.takeError()); + return false; + } + + Reader = BinaryStreamReader(*ContentsOrErr, support::little); + uint32_t Magic; + if (Reader.bytesRemaining() < sizeof(uint32_t)) + return false; + cantFail(Reader.readInteger(Magic)); + if (Magic != COFF::DEBUG_SECTION_MAGIC) + return false; + return true; +} + +static inline bool +isDebugSSection(object::SectionRef Section, + codeview::DebugSubsectionArray &Subsections) { + BinaryStreamReader Reader; + if (!isCodeViewDebugSubsection(Section, ".debug$S", Reader)) + return false; + + cantFail(Reader.readArray(Subsections, Reader.bytesRemaining())); + return true; +} + +static bool isDebugTSection(object::SectionRef Section, + codeview::CVTypeArray &Types) { + BinaryStreamReader Reader; + if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader) && + !isCodeViewDebugSubsection(Section, ".debug$P", Reader)) + return false; + cantFail(Reader.readArray(Types, Reader.bytesRemaining())); + return true; +} + +InputFile::InputFile(PDBFile *Pdb) { PdbOrObj = Pdb; } +InputFile::InputFile(object::COFFObjectFile *Obj) { PdbOrObj = Obj; } +InputFile::InputFile(MemoryBuffer *Buffer) { PdbOrObj = Buffer; } + +PDBFile &InputFile::pdb() { + assert(isPdb()); + return *PdbOrObj.get(); +} + +const PDBFile &InputFile::pdb() const { + assert(isPdb()); + return *PdbOrObj.get(); +} + +object::COFFObjectFile &InputFile::obj() { + assert(isObj()); + return *PdbOrObj.get(); +} + +const object::COFFObjectFile &InputFile::obj() const { + assert(isObj()); + return *PdbOrObj.get(); +} + +MemoryBuffer &InputFile::unknown() { + assert(isUnknown()); + return *PdbOrObj.get(); +} + +const MemoryBuffer &InputFile::unknown() const { + assert(isUnknown()); + return *PdbOrObj.get(); +} + +StringRef InputFile::getFilePath() const { + if (isPdb()) + return pdb().getFilePath(); + if (isObj()) + return obj().getFileName(); + assert(isUnknown()); + return unknown().getBufferIdentifier(); +} + +bool InputFile::hasTypes() const { + if (isPdb()) + return pdb().hasPDBTpiStream(); + + for (const auto &Section : obj().sections()) { + codeview::CVTypeArray Types; + if (isDebugTSection(Section, Types)) + return true; + } + return false; +} + +bool InputFile::hasIds() const { + if (isObj()) + return false; + return pdb().hasPDBIpiStream(); +} + +bool InputFile::isPdb() const { return PdbOrObj.is(); } + +bool InputFile::isObj() const { + return PdbOrObj.is(); +} + +bool InputFile::isUnknown() const { return PdbOrObj.is(); } + +codeview::LazyRandomTypeCollection &InputFile::types() { + return getOrCreateTypeCollection(kTypes); +} + +codeview::LazyRandomTypeCollection &InputFile::ids() { + // Object files have only one type stream that contains both types and ids. + // Similarly, some PDBs don't contain an IPI stream, and for those both + // types and IDs are in the same stream. + if (isObj() || !pdb().hasPDBIpiStream()) + return types(); + + return getOrCreateTypeCollection(kIds); +} + +codeview::LazyRandomTypeCollection & +InputFile::getOrCreateTypeCollection(TypeCollectionKind Kind) { + if (TpiTypes && Kind == kTypes) + return *TpiTypes; + if (IpiTypes && Kind == kIds) + return *IpiTypes; + + if (Kind == kIds) { + assert(isPdb() && pdb().hasPDBIpiStream()); + } + + // If the collection was already initialized, we should have just returned it + // in step 1. + if (isPdb()) { + TypeCollectionPtr &Collection = (Kind == kIds) ? IpiTypes : TpiTypes; + auto &Stream = cantFail((Kind == kIds) ? pdb().getPDBIpiStream() + : pdb().getPDBTpiStream()); + + auto &Array = Stream.typeArray(); + uint32_t Count = Stream.getNumTypeRecords(); + auto Offsets = Stream.getTypeIndexOffsets(); + Collection = std::make_unique( + Array, Count, Offsets); + return *Collection; + } + + assert(isObj()); + assert(Kind == kTypes); + assert(!TpiTypes); + + for (const object::SectionRef &Section : obj().sections()) { + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) { + consumeError(SectionNameOrErr.takeError()); + continue; + } + codeview::CVTypeArray Records; + if (*SectionNameOrErr != ".debug$T" && *SectionNameOrErr != ".debug$P") + continue; + TpiTypes = + std::make_unique(Records, 100); + return *TpiTypes; + } + + TpiTypes = std::make_unique(100); + return *TpiTypes; +} + +Expected +llvm::pdb::getModuleDebugStream(PDBFile &File, StringRef &ModuleName, + uint32_t Index) { + auto DbiOrErr = File.getPDBDbiStream(); + if (!DbiOrErr) + return DbiOrErr.takeError(); + auto &Dbi = *DbiOrErr; + const auto &Modules = Dbi.modules(); + if (Index >= Modules.getModuleCount()) + return make_error(raw_error_code::index_out_of_bounds, + "Invalid module index"); + + auto Modi = Modules.getModuleDescriptor(Index); + + ModuleName = Modi.getModuleName(); + + uint16_t ModiStream = Modi.getModuleStreamIndex(); + if (ModiStream == kInvalidStreamIndex) + return make_error(raw_error_code::no_stream, + "Module stream not present"); + + auto ModStreamData = File.createIndexedStream(ModiStream); + + ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); + if (auto Err = ModS.reload()) + return make_error(raw_error_code::corrupt_file, + "Invalid module stream"); + + return std::move(ModS); +} + +SymbolGroup::SymbolGroup(InputFile *File, uint32_t GroupIndex) : File(File) { + if (!File) + return; + + if (File->isPdb()) + initializeForPdb(GroupIndex); + else { + Name = ".debug$S"; + uint32_t I = 0; + for (const auto &S : File->obj().sections()) { + codeview::DebugSubsectionArray SS; + if (!isDebugSSection(S, SS)) + continue; + + if (!SC.hasChecksums() || !SC.hasStrings()) + SC.initialize(SS); + + if (I == GroupIndex) + Subsections = SS; + + if (SC.hasChecksums() && SC.hasStrings()) + break; + } + rebuildChecksumMap(); + } +} + +StringRef SymbolGroup::name() const { return Name; } + +void SymbolGroup::updateDebugS(const codeview::DebugSubsectionArray &SS) { + Subsections = SS; +} + +void SymbolGroup::updatePdbModi(uint32_t Modi) { initializeForPdb(Modi); } + +void SymbolGroup::initializeForPdb(uint32_t Modi) { + assert(File && File->isPdb()); + + // PDB always uses the same string table, but each module has its own + // checksums. So we only set the strings if they're not already set. + if (!SC.hasStrings()) { + auto StringTable = File->pdb().getStringTable(); + if (StringTable) + SC.setStrings(StringTable->getStringTable()); + else + consumeError(StringTable.takeError()); + } + + SC.resetChecksums(); + auto MDS = getModuleDebugStream(File->pdb(), Name, Modi); + if (!MDS) { + consumeError(MDS.takeError()); + return; + } + + DebugStream = std::make_shared(std::move(*MDS)); + Subsections = DebugStream->getSubsectionsArray(); + SC.initialize(Subsections); + rebuildChecksumMap(); +} + +void SymbolGroup::rebuildChecksumMap() { + if (!SC.hasChecksums()) + return; + + for (const auto &Entry : SC.checksums()) { + auto S = SC.strings().getString(Entry.FileNameOffset); + if (!S) + continue; + ChecksumsByFile[*S] = Entry; + } +} + +Expected SymbolGroup::getNameFromStringTable(uint32_t Offset) const { + return SC.strings().getString(Offset); +} + +Expected SymbolGroup::getNameFromChecksums(uint32_t Offset) const { + StringRef Name; + if (!SC.hasChecksums()) { + return std::move(Name); + } + + auto Iter = SC.checksums().getArray().at(Offset); + if (Iter == SC.checksums().getArray().end()) { + return std::move(Name); + } + + uint32_t FO = Iter->FileNameOffset; + auto ExpectedFile = getNameFromStringTable(FO); + if (!ExpectedFile) { + return std::move(Name); + } + + return *ExpectedFile; +} + +iterator_range InputFile::symbol_groups() { + return make_range(symbol_groups_begin(), + symbol_groups_end()); +} + +SymbolGroupIterator InputFile::symbol_groups_begin() { + return SymbolGroupIterator(*this); +} + +SymbolGroupIterator InputFile::symbol_groups_end() { + return SymbolGroupIterator(); +} + +SymbolGroupIterator::SymbolGroupIterator() : Value(nullptr) {} + +SymbolGroupIterator::SymbolGroupIterator(InputFile &File) : Value(&File) { + if (File.isObj()) { + SectionIter = File.obj().section_begin(); + scanToNextDebugS(); + } +} + +bool SymbolGroupIterator::operator==(const SymbolGroupIterator &R) const { + bool E = isEnd(); + bool RE = R.isEnd(); + if (E || RE) + return E == RE; + + if (Value.File != R.Value.File) + return false; + return Index == R.Index; +} + +const SymbolGroup &SymbolGroupIterator::operator*() const { + assert(!isEnd()); + return Value; +} +SymbolGroup &SymbolGroupIterator::operator*() { + assert(!isEnd()); + return Value; +} + +SymbolGroupIterator &SymbolGroupIterator::operator++() { + assert(Value.File && !isEnd()); + ++Index; + if (isEnd()) + return *this; + + if (Value.File->isPdb()) { + Value.updatePdbModi(Index); + return *this; + } + + scanToNextDebugS(); + return *this; +} + +void SymbolGroupIterator::scanToNextDebugS() { + assert(SectionIter.hasValue()); + auto End = Value.File->obj().section_end(); + auto &Iter = *SectionIter; + assert(!isEnd()); + + while (++Iter != End) { + codeview::DebugSubsectionArray SS; + object::SectionRef SR = *Iter; + if (!isDebugSSection(SR, SS)) + continue; + + Value.updateDebugS(SS); + return; + } +} + +bool SymbolGroupIterator::isEnd() const { + if (!Value.File) + return true; + if (Value.File->isPdb()) { + auto &Dbi = cantFail(Value.File->pdb().getPDBDbiStream()); + uint32_t Count = Dbi.modules().getModuleCount(); + assert(Index <= Count); + return Index == Count; + } + + assert(SectionIter.hasValue()); + return *SectionIter == Value.File->obj().section_end(); +} + +static bool isMyCode(const SymbolGroup &Group) { + if (Group.getFile().isObj()) + return true; + + StringRef Name = Group.name(); + if (Name.startswith("Import:")) + return false; + if (Name.endswith_lower(".dll")) + return false; + if (Name.equals_lower("* linker *")) + return false; + if (Name.startswith_lower("f:\\binaries\\Intermediate\\vctools")) + return false; + if (Name.startswith_lower("f:\\dd\\vctools\\crt")) + return false; + return true; +} + +// This should be a command line option. +bool JustMyCode = true; +bool llvm::pdb::shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group) { + if (JustMyCode && !isMyCode(Group)) + return false; + + // If the arg was not specified on the command line, always dump all modules. + if (/*opts::dump::DumpModi.getNumOccurrences() == 0*/ true) + return true; + + // Otherwise, only dump if this is the same module specified. + return (/*opts::dump::DumpModi == Idx*/ true); +} + +Expected llvm::pdb::getModuleDebugStream(PDBFile &File, + uint32_t Index) { + auto DbiOrErr = File.getPDBDbiStream(); + if (!DbiOrErr) + return DbiOrErr.takeError(); + auto &Dbi = *DbiOrErr; + const auto &Modules = Dbi.modules(); + auto Modi = Modules.getModuleDescriptor(Index); + + uint16_t ModiStream = Modi.getModuleStreamIndex(); + if (ModiStream == kInvalidStreamIndex) + return make_error(raw_error_code::no_stream, + "Module stream not present"); + + auto ModStreamData = File.createIndexedStream(ModiStream); + + ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); + if (auto Err = ModS.reload()) + return make_error(raw_error_code::corrupt_file, + "Invalid module stream"); + + return std::move(ModS); +} + +//===----------------------------------------------------------------------===// +// END +//===----------------------------------------------------------------------===// + +#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" +}; + +static StringRef getSymbolKindName(SymbolKind Kind) { + switch (Kind) { +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + case EnumName: \ + return #EnumName; +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" + default: + return "UnknownSym"; + } +} + +static StringRef getLeafTypeName(TypeLeafKind Kind) { + switch (Kind) { +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + case EnumName: \ + return #EnumName; +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + default: + return "UnknownLeaf"; + } +} + +// 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 (auto 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 { + +// Keeps the type indexes with line information. +using LVLineRecords = std::vector; + +class LVTypeRecords { + // 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() = default; + + 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.insert(std::make_pair(TIForward, TIReference)); + } + + void add(StringRef Name, TypeIndex TIForward) { + if (ForwardTypesNames.find(Name) == ForwardTypesNames.end()) { + ForwardTypesNames.insert( + std::make_pair(Name, ForwardEntry(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.insert( + std::make_pair(Name, ForwardEntry(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; + } +}; + +class LVLinkageNames { + // Logical scope and its linkage name (Used to access the line table). + using NameInfo = std::pair; + using Names = std::map; + Names FunctionNames; + +public: + LVLinkageNames() = default; + + // Function names and its linkage names. + void add(StringRef LinkageName, LVScope *Function) { + if (FunctionNames.find(LinkageName) == FunctionNames.end()) + FunctionNames.insert(std::make_pair(LinkageName, NameInfo(Function, 0))); + else + // Update a recorded linkage name with its logical scope. + FunctionNames[LinkageName].first = Function; + } + + void add(StringRef LinkageName, LVAddress Address) { + if (FunctionNames.find(LinkageName) == FunctionNames.end()) + FunctionNames.insert( + std::make_pair(LinkageName, NameInfo(nullptr, Address))); + else + // Update a recorded linkage name with its logical scope. + FunctionNames[LinkageName].second = Address; + } + + LVScope *getScope(StringRef LinkageName) { + return (FunctionNames.find(LinkageName) != FunctionNames.end()) + ? FunctionNames[LinkageName].first + : nullptr; + } + + LVAddress getAddress(StringRef LinkageName) { + return (FunctionNames.find(LinkageName) != FunctionNames.end()) + ? FunctionNames[LinkageName].second + : 0; + } +}; + +// Namespace deduction. +class LVNamespaceDeduction { + 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.insert(std::make_pair(Name, Namespace)); + } + +public: + LVNamespaceDeduction() = default; + + 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 I = IdentifiedNamespaces.find(Components[Index]); + if (I == 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.insert(std::make_pair( + TI, StringEntry(++Index, std::string(String), nullptr))); + } + + StringRef find(TypeIndex TI) { + StringRef String; + if (Strings.find(TI) != Strings.end()) + String = std::get<1>(Strings[TI]); + return String; + } + + uint32_t findIndex(TypeIndex TI) { + uint32_t Index = 0; + if (Strings.find(TI) != Strings.end()) + Index = std::get<0>(Strings[TI]); + return Index; + } + + // Move strings representing the filenames to the compile unit. + void addFilenames(); + void addFilenames(LVScopeCompileUnit *Scope); +}; + +// The following data keeps forward information, linkage names, 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; + LVLinkageNames LinkageNames; + LVNamespaceDeduction NamespaceDeduction; + LVStringRecords StringRecords; + LVTypeRecords TypeRecords; + + LVShared(LVCodeViewReader *Reader, LVLogicalVisitor *Visitor) + : Reader(Reader), Visitor(Visitor) {} +}; +std::unique_ptr Shared; + +// 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. +using LVTypeKinds = std::set; +using LVSymbolKinds = std::set; +LVTypeKinds TypeKinds; +LVSymbolKinds SymbolKinds; +} // namespace + +void LVTypeRecords::add(uint32_t StreamIdx, TypeIndex TI, TypeLeafKind Kind, + LVElement *Element) { + RecordTable &Target = + (StreamIdx == StreamTPI) ? RecordFromTypes : RecordFromIds; + Target.insert(std::make_pair(TI, RecordEntry(Kind, Element))); +} + +void LVTypeRecords::add(uint32_t StreamIdx, TypeIndex TI, StringRef Name) { + NameTable &Target = (StreamIdx == StreamTPI) ? NameFromTypes : NameFromIds; + Target.insert(std::make_pair(Name, TI)); +} + +LVElement *LVTypeRecords::find(uint32_t StreamIdx, TypeIndex TI) { + RecordTable &Target = + (StreamIdx == StreamTPI) ? RecordFromTypes : RecordFromIds; + + LVElement *Element = nullptr; + if (Target.find(TI) != Target.end()) { + Element = Target[TI].second; + if (Element) + return Element; + + // Create the logical element if not found. + Element = Shared->Visitor->createElement(Target[TI].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; + return Target.find(Name) != Target.end() ? Target[Name] : TypeIndex::None(); +} + +void LVStringRecords::addFilenames() { + for (const auto &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 (auto &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 auto &Unresolved : UnresolvedScopes) { + Components = getAllLexicalComponents(Unresolved); + for (const auto &Component : Components) { + LookupSet::iterator Iter = DeductedScopes.find(Component); + if (Iter == DeductedScopes.end()) + IdentifiedNamespaces.insert(Component); + } + } + + LLVM_DEBUG({ + auto print = [&](auto &Container, auto 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 auto &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 auto &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 auto &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); + Namespace->markSystemEntry(); + 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() << getLeafTypeName(Record.kind()); + W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")\n"; + }); + + if (options().getInternalTag()) + 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() << getLeafTypeName(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 (auto 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("LinkageName", 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 *Name; +} + +DebugStringTableSubsectionRef LVSymbolVisitorDelegate::getStringTable() { + return Reader->CVStringTable; +} + +void LVSymbolVisitor::printLocalVariableAddrRange( + const LocalVariableAddrRange &Range, uint32_t RelocationOffset) { + LLVM_DEBUG({ + 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) { + LLVM_DEBUG({ + for (auto &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()) + 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 Address = Shared->LinkageNames.getAddress(LinkageName); + LVAddress LowPC = + segmentToLinear(Block.Segment, Block.CodeOffset, Address); + LVAddress HighPC = LowPC + Block.CodeSize; + 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. + if (Local.Name.equals("this")) { + Symbol->setIsParameter(); + Symbol->setIsArtificial(); + } else + bool(Local.Offset > 0) ? Symbol->setIsParameter() + : Symbol->setIsVariable(); + + 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); + + // Check for the 'this' symbol. + if (Local.Name.equals("this")) { + Symbol->setIsArtificial(); + Symbol->setIsParameter(); + } else + bool(Local.Offset > 0) ? Symbol->setIsParameter() + : Symbol->setIsVariable(); + + 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 (auto 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); + }); + + if (LVScope *Scope = CurrentScope) { + // The name of the CU, was extracted from the 'BuildInfo' subsection. + CompilationCPUType = Compile2.Machine; + Scope->setName(LogicalVisitor->getCompileUnitName()); + if (options().getAttributeProducer()) + Scope->setProducer(Compile2.Version); + Scope->markSystemEntry(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); + }); + + if (LVScope *Scope = CurrentScope) { + // The name of the CU, was extracted from the 'BuildInfo' subsection. + CompilationCPUType = Compile3.Machine; + Scope->setName(LogicalVisitor->getCompileUnitName()); + if (options().getAttributeProducer()) + Scope->setProducer(Compile3.Version); + Scope->markSystemEntry(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_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 (Symbol->markSystemEntry() && !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_DEFRANGE_FRAMEPOINTER_REL +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, DefRangeFramePointerRelSym &DefRangeFramePointerRel) { + LLVM_DEBUG({ + W.printNumber("Offset", DefRangeFramePointerRel.Hdr.Offset); + printLocalVariableAddrRange(DefRangeFramePointerRel.Range, + DefRangeFramePointerRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeFramePointerRel.Gaps); + }); + 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 Symbol ID. + CVType CVFunctionType = Ids.getType(InlineSite.Inlinee); + if (auto Err = LogicalVisitor->finishVisitation( + CVFunctionType, InlineSite.Inlinee, AbstractFunction)) + return Err; + LogicalVisitor->stopProcessArgumentList(); + } + } + + 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); + + // PR46431 - 'this' is not market as 'compiler generated'. + if (bool(Local.Flags & LocalSymFlags::IsCompilerGenerated) || + Local.Name.equals("this")) { + Symbol->setIsArtificial(); + } else + bool(Local.Flags & LocalSymFlags::IsParameter) ? Symbol->setIsParameter() + : Symbol->setIsVariable(); + + 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); + } + + 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. + Shared->LinkageNames.add(LinkageName, Function); + Function->setName(Proc.Name); + Function->setLinkageName(LinkageName); + + if (options().getGeneralCollectRanges()) { + // Record converted segment::offset addressing for this scope. + LVAddress Address = Shared->LinkageNames.getAddress(LinkageName); + LVAddress LowPC = segmentToLinear(Proc.Segment, Proc.CodeOffset, Address); + LVAddress HighPC = LowPC + Proc.CodeSize; + 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(LowPC, HighPC, Function); + } + + 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 InnerComponent; + StringRef OuterComponent; + std::tie(OuterComponent, InnerComponent) = getInnerComponent(Proc.Name); + TypeIndex TI = Shared->ForwardReferences.find(OuterComponent); + + llvm::Optional CVFunctionType; + auto getRecordType = [&]() { + 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. + CVFunctionType = Ids.tryGetType(TIFunctionType); + if (!getRecordType()) { + CVFunctionType = Types.tryGetType(TIFunctionType); + if (!CVFunctionType) + return llvm::make_error("Invalid type index"); + } + + if (auto 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 (Type->markSystemEntry()) + 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, + llvm::pdb::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 = std::make_unique(Reader, this); +} + +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() << getLeafTypeName(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() << getLeafTypeName(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) { + auto 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); + Reader->getCompileUnit()->addElement(Array); + TypeIndex TIElementType = AT.getElementType(); + + LVType *PrevSubrange = nullptr; + auto &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->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 (auto EC = + finishVisitation(CVElementType, TIElementType, QualifiedType)) + return EC; + // 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 (auto EC = + TypeDeserializer::deserializeAs(const_cast(CVEntry), AR)) + return EC; + 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) + ? getSize(Types.getType(TIArrayType)) + : getSize(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) { + // All the args are references into the TPI/IPI stream. + TypeIndex TIName = BI.getArgs()[BuildInfoRecord::BuildInfoArg::SourceFile]; + Element->setName(std::string(ids().getTypeName(TIName))); + + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + W.printNumber("NumArgs", static_cast(BI.getArgs().size())); + ListScope Arguments(W, "Arguments"); + for (auto Arg : BI.getArgs()) + printTypeIndex("ArgType", Arg, StreamIPI); + printTypeEnd(Record); + }); + 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("LinkageName", Class.getUniqueName()); + printTypeEnd(Record); + }); + + if (Element->getIsFinalized()) + return Error::success(); + Element->setIsFinalized(); + + Element->setName(Class.getName()); + if (Class.hasUniqueName()) + Element->setLinkageName(Class.getUniqueName()); + + if (Class.isNested()) { + Element->setIsNested(); + createParents(Class.getName(), Element); + } + + if (Class.isScoped()) + Element->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(Element); + else + Reader->getCompileUnit()->addElement(Element); + } + + auto &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 (auto 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 (auto Err = finishVisitation(CVFieldList, TI, Element)) + 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); + }); + + if (Element->getIsFinalized()) + return Error::success(); + Element->setIsFinalized(); + + // Set the name, as in the case of nested, it would determine the relation + // to any potential parent, via the LF_NESTTYPE record. + Element->setName(Enum.getName()); + if (Enum.hasUniqueName()) + Element->setLinkageName(Enum.getUniqueName()); + + Element->setType(getElement(StreamTPI, Enum.getUnderlyingType())); + + if (Enum.isNested()) { + Element->setIsNested(); + createParents(Enum.getName(), Element); + } + + if (Enum.isScoped()) { + Element->setIsScoped(); + Element->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(Element); + else + Reader->getCompileUnit()->addElement(Element); + } + + TypeIndex TIFieldList = Enum.getFieldList(); + if (!TIFieldList.isNoneType()) { + auto &Types = types(); + CVType CVFieldList = Types.getType(TIFieldList); + if (auto Err = finishVisitation(CVFieldList, TIFieldList, Element)) + 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 (auto 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. + auto &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 (auto Err = finishVisitation(CVParentScope, TIParent, FunctionDcl)) + return Err; + } + + TypeIndex TIFunctionType = Func.getFunctionType(); + CVType CVFunctionType = Types.getType(TIFunctionType); + if (auto 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 (auto 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); + }); + + LVScope *MemberFunction = static_cast(Element); + if (MemberFunction) { + 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. + auto &Types = types(); + CVType CVArguments = Types.getType(MF.getArgumentList()); + if (auto 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 (auto &Method : Overloads.Methods) { + CVMemberRecord Record; + Record.Kind = LF_METHOD; + Method.Name = OverloadedMethodName; + if (auto 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()); + + // 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); + + 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; + } + LastLink->setTag(dwarf::DW_TAG_volatile_type); + LastLink->setIsVolatile(); + LastLink->setName("volatile"); + } + + 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()); + + // Orden for the different modifiers: + // + // Const and volatile already processed. + bool SeenModifier = false; + LVType *LastLink = Pointer; + + 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; + } + if (Mode == PointerMode::LValueReference) { + if (SeenModifier) { + LVType *LReference = new LVType(); + LReference->setIsModifier(); + LastLink->setType(LReference); + LastLink = LReference; + } + 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; + } + 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. + auto &Types = types(); + CVType CVArguments = Types.getType(Proc.getArgumentList()); + if (auto 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("LinkageName", Union.getUniqueName()); + printTypeEnd(Record); + }); + + if (Element->getIsFinalized()) + return Error::success(); + Element->setIsFinalized(); + + Element->setName(Union.getName()); + if (Union.hasUniqueName()) + Element->setLinkageName(Union.getUniqueName()); + + if (Union.isNested()) { + Element->setIsNested(); + createParents(Union.getName(), Element); + } else { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Union.getName())) + Namespace->addElement(Element); + else + Reader->getCompileUnit()->addElement(Element); + } + + if (!Union.getFieldList().isNoneType()) { + auto &Types = types(); + // Pass down the TypeIndex 'TI' for the aggregate containing the field list. + CVType CVFieldList = Types.getType(Union.getFieldList()); + if (auto Err = finishVisitation(CVFieldList, TI, Element)) + 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 (auto &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); + auto 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 InnerComponent; + StringRef OuterComponent; + std::tie(OuterComponent, InnerComponent) = + 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; + LVElement *MemberFunction = createElement(TypeLeafKind::LF_ONEMETHOD); + if (MemberFunction) { + 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(); + + auto &Types = types(); + CVType CVMethodType = Types.getType(Method.getType()); + if (auto EC = + finishVisitation(CVMethodType, Method.getType(), MemberFunction)) + return EC; + } + 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. + auto &Types = types(); + OverloadedMethodName = Method.getName(); + CVType CVMethods = Types.getType(Method.getMethodList()); + if (auto 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 (auto Err = Callbacks.visitMemberBegin(Record)) + return Err; + + switch (Record.Kind) { + default: + if (auto Err = Callbacks.visitUnknownMember(Record)) + return Err; + break; +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (auto 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 (auto Err = Callbacks.visitMemberEnd(Record)) + return Err; + + return Error::success(); +} + +Error LVLogicalVisitor::finishVisitation(CVType &Record, TypeIndex TI, + LVElement *Element) { + switch (Record.kind()) { + default: + if (auto Err = visitUnknownType(Record, TI)) + return Err; + break; +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (auto 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 (auto Err = Reader.readEnum(Leaf)) + return Err; + + CVMemberRecord Record; + Record.Kind = Leaf; + if (auto 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->setIsStruct(); + 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: + // Collect Records not implemented. + // if (options().getInternalTag() && Kind) + // CompileUnit->addUnsupportedTag(Tag, CurrentOffset); + 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: + 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: + // Collect Records not implemented. + // if (options().getInternalTag() && Kind) + // CompileUnit->addUnsupportedTag(Tag, CurrentOffset); + 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", getLeafTypeName(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 { + auto &Types = types(); + CVType CVMemberType = Types.getType(TI); + if (CVMemberType.kind() == LF_BITFIELD) { + if (auto 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 one. + 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 PR46394. Missing LF_NESTTYPE 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. + auto &Types = types(); + CVType CVRecord = Types.getType(TI); + if (auto Err = finishVisitation(CVRecord, TI, Element)) { + consumeError(std::move(Err)); + return nullptr; + } + Element->setIsFinalized(); + return Element; +} + +void LVLogicalVisitor::processElements() { + // Traverse the collected LF_UDT_SRC_LINE records and add the source line + // information to the logical elements. + for (const auto &Entry : Shared->LineRecords) { + CVType CVRecord = ids().getType(Entry); + UdtSourceLineRecord Line; + if (auto 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::addLinkageScope(StringRef Name, LVAddress Address) { + Shared->LinkageNames.add(Name, Address); +} + +LVScope *LVLogicalVisitor::getLinkageScope(StringRef Name) { + return Shared->LinkageNames.getScope(Name); +} + +LVAddress LVLogicalVisitor::getLinkageAddress(StringRef Name) { + return Shared->LinkageNames.getAddress(Name); +} + +void LVLogicalVisitor::printRecordTag() { + unsigned Count = 0; + auto printItem = [&](StringRef Name) { + auto newLine = [&]() { + if (++Count == 4) { + Count = 0; + W.getOStream() << "\n"; + } + }; + std::string TheName(Name); + W.getOStream() << format("%20s", TheName.c_str()); + newLine(); + }; + + W.getOStream() << "\n** Types **\n"; + for (const auto &Kind : TypeKinds) + printItem(getLeafTypeName(Kind)); + TypeKinds.clear(); + + Count = 0; + W.getOStream() << "\n** Symbols **\n"; + for (const auto &Kind : SymbolKinds) + printItem(getSymbolKindName(Kind)); + SymbolKinds.clear(); +} Index: llvm/lib/DebugInfo/LogicalView/Readers/LVElfReader.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Readers/LVElfReader.cpp @@ -0,0 +1,1125 @@ +//===-- 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/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/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/Support/TargetRegistry.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; + + 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: + 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->setIsTemplateValue(); + return CurrentType; + case dwarf::DW_TAG_template_type_parameter: + CurrentType = new LVTypeParam(); + CurrentType->setIsTemplateType(); + return CurrentType; + case dwarf::DW_TAG_GNU_template_template_param: + CurrentType = new LVTypeParam(); + CurrentType->setIsTemplateTemplate(); + 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; + + // 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: + 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_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->setIsStruct(); + 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) { + return FormValue.isFormClass(DWARFFormValue::FC_Flag) ? 1 : 0; + }; + + 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(*FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_const_value: + if (FormValue.isFormClass(DWARFFormValue::FC_Block)) { + ArrayRef Expr = *FormValue.getAsBlock(); + CurrentElement->setValue(llvm::toStringRef(Expr)); + } 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; + auto 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(*FormValue.getAsCString()); + 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(*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(*FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_name: + CurrentElement->setName(*FormValue.getAsCString()); + break; + case dwarf::DW_AT_linkage_name: + case dwarf::DW_AT_MIPS_linkage_name: + CurrentElement->setLinkageName(*FormValue.getAsCString()); + break; + case dwarf::DW_AT_producer: + if (options().getAttributeProducer()) + CurrentElement->setProducer(*FormValue.getAsCString()); + break; + case dwarf::DW_AT_upper_bound: + CurrentElement->setUpperBound(*FormValue.getAsUnsignedConstant()); + break; + case dwarf::DW_AT_virtuality: + CurrentElement->setVirtualityCode(*FormValue.getAsUnsignedConstant()); + break; + + case dwarf::DW_AT_abstract_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 toolchain 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 (auto Value = FormValue.getAsAddress()) { + CurrentLowPC = Value.getValue(); + } else { + uint64_t UValue = FormValue.getRawUValue(); + Optional A = + U->getAddrOffsetSectionItem(UValue); + if (A) { + 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->getIsCompileUnit()) + setCUBaseAddress(CurrentLowPC); + } + } + break; + + case dwarf::DW_AT_high_pc: + if (options().getGeneralCollectRanges()) { + FoundHighPC = true; + if (auto Address = FormValue.getAsAddress()) + // High PC is an address. + CurrentHighPC = *Address; + if (auto Offset = FormValue.getAsUnsignedConstant()) + // High PC is an offset from LowPC. + CurrentHighPC = CurrentLowPC + *Offset; + // Store the real upper limit for the address range. + if (CurrentHighPC > 0) + --CurrentHighPC; + if (CurrentElement->getIsCompileUnit()) + setCUHighAddress(CurrentHighPC); + } + break; + + case dwarf::DW_AT_ranges: + if (RangesDataAvailable && options().getGeneralCollectRanges()) { + auto getRanges = [&](DWARFFormValue &FormValue, DWARFUnit *U) { + if (FormValue.getForm() == dwarf::DW_FORM_rnglistx) + return U->findRnglistFromIndex(*FormValue.getAsSectionOffset()); + return U->findRnglistFromOffset(*FormValue.getAsSectionOffset()); + }; + auto 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 (auto &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 (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->getIsCompileUnit()) + ScopesWithRanges.addEntry(CurrentScope, 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; + + 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)) { + + // Create the logical view element for the current DIE. + auto Tag = DIE.getTag(); + CurrentElement = createElement(Tag); + if (!CurrentElement) + return CurrentScope; + + CurrentElement->setTag(Tag); + CurrentElement->setOffset(Offset); + + if (options().getAttributeAnySource() && CurrentElement->getIsCompileUnit()) + 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.insert(std::make_pair( + Offset, std::make_pair(CurrentElement, LVElementSet()))); + else { + // There are previous references to this element. We need to update the + // element and all the references pointing to this element. + auto &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 (const auto &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 (auto AbbrevDecl = TheDIE.getAbbreviationDeclarationPtr()) + for (const auto &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) { + // Add scope to its parent and process any ranges. + if (FoundLowPC && FoundHighPC && CurrentScope->getCanHaveRanges()) { + // Add the pair of addresses. + CurrentScope->addObject(CurrentLowPC, CurrentHighPC); + // If the scope is the CU, do not update the ranges set. + if (!CurrentScope->getIsCompileUnit()) { + ScopesWithRanges.addEntry(CurrentScope, CurrentLowPC, CurrentHighPC); + // If the scope is a function, add it to the public names. + if ((options().getAttributePublics() || options().getPrintAnyLine()) && + CurrentScope->getIsFunction() && + !CurrentScope->getIsInlinedFunction()) + CompileUnit->addPublicName(CurrentLowPC, CurrentHighPC, CurrentScope); + } + } + // 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 (const auto &Symbol : SymbolsWithLocations) + Symbol->fillLocationGaps(); +} + +Error LVElfReader::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. + // Clear instruction lines for current compile unit. + CUInstructions.clear(); + + LVLineSource *Line = nullptr; + LVPublicNames PublicNames = CompileUnit->getPublicNames(); + for (const auto &Name : PublicNames) { + // Skip stripped functions. + if (Name.second.first->getIsDiscarded()) + continue; + + // Find associated scope for the given function entry point. + LVAddress Address = Name.first; + size_t Size = Name.second.second; + + LLVM_DEBUG({ + dbgs() << "\nPublic Name Entry:\n" + << "DIE Offset: " << format_hex(Name.second.first->getOffset(), 10) + << " Range: [" << format_hex(Address, 10) << ":" + << format_hex(Address + Size, 10) + << "] Size: " << format_hex(Size, 10) << "\n"; + }); + + // We have to be sure, the public name starting address is contained in + // any of the section address. + auto Entry = SectionAddresses.upper_bound(Address); + if (Entry == SectionAddresses.begin()) + // Invalid address. Skip this public name. + continue; + + // Get section that contains the code for this function. + Entry = SectionAddresses.lower_bound(Address); + if (Entry != SectionAddresses.begin()) + --Entry; + object::SectionRef Section = Entry->second; + + Expected SectionOrErr = Section.getContents(); + if (!SectionOrErr) + return SectionOrErr.takeError(); + ArrayRef Bytes = arrayRefFromStringRef(*SectionOrErr); + uint64_t Offset = Address - Entry->first; + uint8_t const *Begin = Bytes.data() + Offset; + uint8_t const *End = Bytes.data() + Offset + Size + 1; + + LLVM_DEBUG({ + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + consumeError(SectionNameOrErr.takeError()); + else + dbgs() << "Section Index: " << format_hex(Section.getIndex(), 10) + << " [" << format_hex((uint64_t)Section.getAddress(), 10) << ":" + << format_hex((uint64_t)Section.getAddress() + Section.getSize(), + 10) + << " Name: '" << *SectionNameOrErr << "'\n"; + }); + + while (Begin < End) { + MCInst Instruction; + std::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, *MSI, + Stream); + LLVM_DEBUG({ + dbgs() << "[" << format_hex((uint64_t)Begin, 10) << "] " + << format_hex((uint64_t)Address, 10) << ": " << Stream.str() + << "\n"; + }); + Line = new LVLineSource(); + Line->setAddress(Address); + Line->setName(StringRef(Stream.str()).trim()); + CUInstructions.push_back(Line); + break; + } + } + Address += BytesConsumed; + Begin += BytesConsumed; + } + } + + return Error::success(); +} + +void LVElfReader::createLineAndFileRecords( + const DWARFDebugLine::LineTable *Lines) { + if (!Lines) + return; + + // This routine should be in DWARFDebugLine::LineTable. + // Keep it here just for convenience. + auto getIncludeDir = [&](DWARFDebugLine::FileNameEntry Entry) -> auto { + StringRef IncludeDir; + if (Lines->Prologue.getVersion() >= 5) { + if (Entry.DirIdx < Lines->Prologue.IncludeDirectories.size()) + IncludeDir = Lines->Prologue.IncludeDirectories[Entry.DirIdx] + .getAsCString() + .getValue(); + } else { + if (0 < Entry.DirIdx && + Entry.DirIdx <= Lines->Prologue.IncludeDirectories.size()) + IncludeDir = Lines->Prologue.IncludeDirectories[Entry.DirIdx - 1] + .getAsCString() + .getValue(); + } + return IncludeDir; + }; + + // Get the source filenames. + if (!Lines->Prologue.FileNames.empty()) + for (const auto &Entry : Lines->Prologue.FileNames) { + std::string String; + raw_string_ostream(String) + << transformPath(getIncludeDir(Entry)) << "/" + << transformPath(Entry.Name.getAsCString().getValue()); + CompileUnit->addFilename(String); + } + + // Get the source lines. + if ((options().getAttributeRange() || options().getPrintLines()) && + Lines->Rows.size()) + for (const auto &Row : Lines->Rows) { + LVLineDebug *Line = new LVLineDebug(); + CULines.push_back(Line); + Line->setAddress(Row.Address.Address); + Line->setFilename(CompileUnit->getFilename(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(); + } +} + +static void prettyPrintBaseTypeRef(DWARFUnit *U, raw_ostream &OS, + uint64_t Operands[2], unsigned Operand) {} + +bool LVElfReader::prettyPrintRegisterOp(raw_ostream &OS, uint8_t Opcode, + uint64_t Operands[2], + const MCRegisterInfo *MRI, bool isEH) { + if (!MRI) + return false; + + uint64_t DwarfRegNum; + unsigned OpNum = 0; + + if (Opcode == dwarf::DW_OP_bregx || Opcode == dwarf::DW_OP_regx || + Opcode == dwarf::DW_OP_regval_type) + DwarfRegNum = Operands[OpNum++]; + else if (Opcode >= dwarf::DW_OP_breg0 && Opcode < dwarf::DW_OP_bregx) + DwarfRegNum = Opcode - dwarf::DW_OP_breg0; + else + DwarfRegNum = Opcode - dwarf::DW_OP_reg0; + + if (Optional LLVMRegNum = MRI->getLLVMRegNum(DwarfRegNum, isEH)) { + if (const char *RegName = MRI->getName(*LLVMRegNum)) { + if ((Opcode >= dwarf::DW_OP_breg0 && Opcode <= dwarf::DW_OP_breg31) || + Opcode == dwarf::DW_OP_bregx) + OS << format(" %s%+" PRId64, RegName, Operands[OpNum]); + else + OS << ' ' << RegName; + + // LLVM version introduced this option, but it requires access to the + // DWARFUnit underline data. + if (Opcode == dwarf::DW_OP_regval_type) + prettyPrintBaseTypeRef(/*U=*/nullptr, OS, Operands, 1); + return true; + } + } + + return false; +} + +std::string LVElfReader::getRegisterName(LVSmall Opcode, uint64_t Operands[2]) { + 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(Stream, 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 (auto Err = LVReader::createScopes()) + return Err; + + 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()); + } + + // 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. + if (auto 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 auto &CU : CompileUnits) { + // 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. + auto CUDie = CU->getNonSkeletonUnitDIE(false); + 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 (auto 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. + ScopesWithRanges.addEntry(CompileUnit); + ScopesWithRanges.sort(); + + processLines(); + processLocationGaps(); + + // These are per compile unit. + ScopesWithRanges.clear(); + SymbolsWithLocations.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) { + + auto processLocationExpression = [&](auto &Expression) { + // DW_OP_const_type is variable-length and has 3 + // operands. DWARFExpression thus far only supports 2. + uint64_t Operands[2] = {0}; + for (auto &Op : Expression) { + auto Description = Op.getDescription(); + for (unsigned Operand = 0; Operand < 2; ++Operand) { + unsigned Size = Description.Op[Operand]; + if (Size == 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) || + 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()); + + // Add location and operation entries. + CurrentSymbol->addLocation(Attr, /*LowPC=*/0, /*HighPC=*/-1, + /*SectionOffset=*/0, OffsetOnEntry); + processLocationExpression(Expression); + return; + } + + if (FormValue.isFormClass(DWARFFormValue::FC_SectionOffset)) { + uint64_t Offset = *FormValue.getAsSectionOffset(); + if (FormValue.getForm() == dwarf::DW_FORM_loclistx) { + if (auto LoclistOffset = U->getLoclistOffset(Offset)) + Offset = *LoclistOffset; + else + return; + } + uint64_t BaseAddr = 0; + if (Optional BA = U->getBaseAddress()) + BaseAddr = BA->Address; + LVAddress LowPC = 0; + LVAddress HighPC = 0; + + auto processLocationEntry = [&](auto &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}; + DataExtractor Data(toStringRef(Loc.Expr), IsLittleEndian, + U->getAddressByteSize()); + DWARFExpression Expression(Data, U->getAddressByteSize()); + + // Store the real upper limit for the address range. + if (HighPC > 0) + --HighPC; + // Add location and operation entries. + CurrentSymbol->addLocation(Attr, LowPC, HighPC, Offset, OffsetOnEntry); + 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: + 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 I = ElementTable.find(offset); + if (I == ElementTable.end()) + // Reference to an unseen element. + ElementTable.emplace( + std::make_pair(offset, std::make_pair(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. + auto &Reference = I->second; + Target = Reference.first; + if (!Target) + // Add the element to the set. + Reference.second.insert(Element); + } + return Target; +} + +Error LVElfReader::loadTargetInfo(const object::ObjectFile &Obj) { + if (!options().getPrintInstructions()) + return Error::success(); + + // 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); + std::string TargetLookupError; + const Target *TheTarget = + TargetRegistry::lookupTarget(TT.str(), TargetLookupError); + if (!TheTarget) + return createStringError(errc::invalid_argument, TargetLookupError.c_str()); + StringRef TripleName = TT.str(); + + // Features to be passed to target/subtarget + SubtargetFeatures Features = Obj.getFeatures(); + + // Register information. + MCRegisterInfo *RegisterInfo = TheTarget->createMCRegInfo(TripleName); + if (!RegisterInfo) + return createStringError(errc::invalid_argument, + "no register info for target " + TripleName); + MRI.reset(RegisterInfo); + + // Assembler properties and features. + MCTargetOptions MCOptions; + MCAsmInfo *AsmInfo(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); + if (!AsmInfo) + return createStringError(errc::invalid_argument, + "no assembly info for target " + TripleName); + MAI.reset(AsmInfo); + + // Target subtargets. + StringRef CPU; + MCSubtargetInfo *SubtargetInfo( + TheTarget->createMCSubtargetInfo(TripleName, CPU, Features.getString())); + if (!SubtargetInfo) + return createStringError(errc::invalid_argument, + "no subtarget info for target " + TripleName); + MSI.reset(SubtargetInfo); + + // Instructions Info. + MCInstrInfo *InstructionInfo(TheTarget->createMCInstrInfo()); + if (!InstructionInfo) + return createStringError(errc::invalid_argument, + "no instruction info for target " + TripleName); + MII.reset(InstructionInfo); + + MC = std::make_unique(MAI.get(), MRI.get(), &MOFI); + MOFI.InitMCObjectFileInfo(Triple(TripleName), false, *MC); + + // Assembler. + MCDisassembler *DisAsm(TheTarget->createMCDisassembler(*MSI, *MC)); + if (!DisAsm) + return createStringError(errc::invalid_argument, + "no disassembler for target " + TripleName); + MD.reset(DisAsm); + + MCInstPrinter *InstructionPrinter(TheTarget->createMCInstPrinter( + Triple(TripleName), AsmInfo->getAssemblerDialect(), *MAI, *MII, *MRI)); + if (!InstructionPrinter) + return createStringError(errc::invalid_argument, + "no target assembly language printer for target " + + TripleName); + MIP.reset(InstructionPrinter); + InstructionPrinter->setPrintImmHex(true); + + return Error::success(); +} + +void LVElfReader::sortScopes() { Root->sort(); } + +void LVElfReader::print(raw_ostream &OS) const { + OS << "LVType\n"; + LLVM_DEBUG(dbgs() << "CreateReaders\n"); +} Index: llvm/tools/llvm-dva/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-dva/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-dva + llvm-dva.cpp + Options.cpp + ) Index: llvm/tools/llvm-dva/LLVMBuild.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-dva/LLVMBuild.txt @@ -0,0 +1,21 @@ +;===- ./tools/llvm-dva/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-dva +parent = Tools +required_libraries = DebugInfoLogicalView DebugInfoDWARF DebugInfoCodeView DebugInfoPDB Object Index: llvm/tools/llvm-dva/Options.h =================================================================== --- /dev/null +++ llvm/tools/llvm-dva/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-dva. +// +//===----------------------------------------------------------------------===// + +#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 : 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-dva/Options.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-dva/Options.cpp @@ -0,0 +1,480 @@ +//===-- options.cpp - Command line options for llvm-dva -------------------===// +// +// 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-dva. +// +//===----------------------------------------------------------------------===// + +#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", + "Objet 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::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::Operation, "operation", + "Debug location operation."), + 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 allocated and 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::Details, "details", + "The elements are displayed in a tabular format."), + clEnumValN(LVReportKind::Summary, "summary", + "Summary of elements printed, missing, added."), + clEnumValN(LVReportKind::View, "view", + "The elements are displayed in a tree View."))); + +//===----------------------------------------------------------------------===// +// '--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::BasicBlock, "BasicBlock", "Basic block."), + clEnumValN(LVLineKind::Discriminator, "Discriminator", + "Discriminator."), + clEnumValN(LVLineKind::EndSequence, "EndSequence", "End sequence."), + clEnumValN(LVLineKind::EpilogueBegin, "EpilogueBegin.", + "Epilogue begin."), + clEnumValN(LVLineKind::LineDebug, "LineDebug", "Debug line."), + clEnumValN(LVLineKind::LineSource, "LineSource", "Source line."), + clEnumValN(LVLineKind::NewStatement, "NewStatement", "New statement."), + clEnumValN(LVLineKind::PrologueEnd, "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::Aggregate, "Aggregate", + "Class, Structure or Union."), + clEnumValN(LVScopeKind::Array, "Array", "Array."), + clEnumValN(LVScopeKind::Block, "Block", "Lexical block."), + clEnumValN(LVScopeKind::CatchBlock, "CatchBlock", + "Exception catch block."), + clEnumValN(LVScopeKind::Class, "Class", "Class."), + clEnumValN(LVScopeKind::CompileUnit, "CompileUnit", "Compile unit."), + clEnumValN(LVScopeKind::EntryPoint, "EntryPoint", + "Function entry point."), + clEnumValN(LVScopeKind::Enumeration, "Enumeration", "Enumeration."), + clEnumValN(LVScopeKind::Function, "Function", "Function."), + clEnumValN(LVScopeKind::FunctionType, "FunctionType", "Function type."), + clEnumValN(LVScopeKind::InlinedFunction, "InlinedFunction", + "Inlined function."), + clEnumValN(LVScopeKind::Label, "Label", "Label."), + clEnumValN(LVScopeKind::LexicalBlock, "LexicalBlock", "Lexical block."), + clEnumValN(LVScopeKind::Namespace, "Namespace", "Namespace."), + clEnumValN(LVScopeKind::Root, "Root", "Root."), + clEnumValN(LVScopeKind::Structure, "Structure", "Structure."), + clEnumValN(LVScopeKind::Subprogram, "Subprogram", "Subprogram."), + clEnumValN(LVScopeKind::Template, "Template", "Template."), + clEnumValN(LVScopeKind::TemplateAlias, "TemplateAlias", + "Template alias."), + clEnumValN(LVScopeKind::TemplatePack, "TemplatePack", "Template pack."), + clEnumValN(LVScopeKind::TryBlock, "TryBlock", "Exception try block."), + clEnumValN(LVScopeKind::Union, "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::Inheritance, "Inheritance", "Inheritance."), + clEnumValN(LVSymbolKind::Member, "Member", "Member."), + clEnumValN(LVSymbolKind::Parameter, "Parameter", "Parameter."), + clEnumValN(LVSymbolKind::Unspecified, "Unspecified", + "Unspecified parameter."), + clEnumValN(LVSymbolKind::Variable, "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::Base, "Base", "Base Type (int, bool, etc.)."), + clEnumValN(LVTypeKind::Const, "Const", "Constant specifier."), + clEnumValN(LVTypeKind::Enumerator, "Enumerator", "Enumerator."), + clEnumValN(LVTypeKind::Import, "Import", "Import declaration."), + clEnumValN(LVTypeKind::ImportDeclaration, "ImportDeclaration", + "Import declaration."), + clEnumValN(LVTypeKind::ImportModule, "ImportModule", "Import module."), + clEnumValN(LVTypeKind::Pointer, "Pointer", "Pointer."), + clEnumValN(LVTypeKind::PointerMember, "PointerMember", + "Pointer to member."), + clEnumValN(LVTypeKind::Reference, "Reference", "Reference type."), + clEnumValN(LVTypeKind::Restrict, "Restrict", "Restrict specifier."), + clEnumValN(LVTypeKind::RvalueReference, "RvalueReference", + "Rvalue reference."), + clEnumValN(LVTypeKind::Subrange, "Subrange", "Array subrange."), + clEnumValN(LVTypeKind::TemplateParam, "TemplateParam", + "Template Parameter."), + clEnumValN(LVTypeKind::TemplateTemplateParam, "TemplateTemplateParam", + "Template template parameter."), + clEnumValN(LVTypeKind::TemplateTypeParam, "TemplateTypeParam", + "Template type parameter."), + clEnumValN(LVTypeKind::TemplateValueParam, "TemplateValueParam", + "Template value parameter."), + clEnumValN(LVTypeKind::Typedef, "Typedef", "Type definition."), + clEnumValN(LVTypeKind::Unspecified, "Unspecified", "Unspecified type."), + clEnumValN(LVTypeKind::Volatile, "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::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 Case, bool Regex) { + if (!List.empty()) + for (auto &Pattern : List) + Set.insert((Case && !Regex) ? 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) { + if (!List.empty()) + for (auto &Kind : List) + Set.insert(Kind); + }; + + // Handle options sets. + updateSet(AttributeOptions, ReaderOptions.Attribute.Kinds); + updateSet(PrintOptions, ReaderOptions.Print.Kinds); + updateSet(OutputOptions, ReaderOptions.Output.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); + updateSet(ReportOptions, ReaderOptions.Report.Kinds); + + // Resolve any options dependencies (ie. --print=all should set other + // print options, etc.). + ReaderOptions.resolveDependencies(); +} Index: llvm/tools/llvm-dva/llvm-dva.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-dva/llvm-dva.cpp @@ -0,0 +1,132 @@ +//===-- llvm-dva.cpp - Debug info analysis utility for llvm ---------------===// +// +// 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 display 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/Path.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-dva"; +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.size()) + 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 auto &F : InputFilenames) { + auto Objs = expandBundle(F); + Objects.insert(Objects.end(), Objs.begin(), Objs.end()); + } + + // Create readers and perform requested tasks on them. + propagateOptions(); + ScopedPrinter W(OutputFile.os()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + if (auto 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 @@ -40,6 +40,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,1594 @@ +//===---- 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; + +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; + +namespace { + +#ifdef INTERVAL_TREE_DEBUG +static void printData(UUValue Value, UUReferences &Intervals) { + int Index = 0; + outs() << format("\n** %d, %d **\n", Value, Intervals.size()); + for (const auto Data : Intervals) + outs() << format("%2d: (%2d) [%d...%d] <- %d\n", Index++, + Data->right() - Data->left(), Data->left(), Data->right(), + Data->value()); +} +#endif + +static void checkData(UUPoint Point, UUData *Data, UUPoint Left, UUPoint Right, + UUValue Value) { + EXPECT_TRUE(Data->contain(Point)); + EXPECT_EQ(Data->left(), Left); + EXPECT_EQ(Data->right(), Right); + EXPECT_EQ(Data->value(), Value); +} + +static void checkData(UUPoint Point, UUIter Iter, UUPoint Left, UUPoint Right, + UUValue Value) { + EXPECT_TRUE(Iter->contain(Point)); + EXPECT_EQ(Iter->left(), Left); + EXPECT_EQ(Iter->right(), Right); + EXPECT_EQ(Iter->value(), Value); +} + +#ifdef INTERVAL_TREE_DEBUG +// Debug data from documentation. +TEST(IntervalTreeTest, DocData) { + UUAlloc Allocator; + UUTree Tree(Allocator); + UUReferences Intervals; + + // [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(); + + dbgs() << "\nTree:\n"; + Tree.print(dbgs(), /*HexFormat=*/false); +} +#endif + +// 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 contain(const PointType &Point) const { + return UUData::contain(Point); + } + }; + + using UUTree = IntervalTree; + using UUReferences = UUTree::IntervalReferences; + using UUData = UUTree::DataType; + + auto checkData = [](UUPoint Point, UUData *Data, UUPoint Left, UUPoint Right, + UUValue Value) { + EXPECT_TRUE(Data->contain(Point)); + EXPECT_EQ(Data->left(), Left); + EXPECT_EQ(Data->right(), Right); + EXPECT_EQ(Data->value(), 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); +} + +// 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. +TEST(IntervalTreeTest, MappedValuesTree) { + 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,5 +1,6 @@ add_subdirectory(CodeView) add_subdirectory(DWARF) add_subdirectory(GSYM) +add_subdirectory(LogicalView) add_subdirectory(MSF) add_subdirectory(PDB) Index: llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_LINK_COMPONENTS + DebugInfoLogicalView + ) + +add_llvm_unittest(DebugInfoLogicalViewTests + StringPoolTest.cpp + ) + +target_link_libraries(DebugInfoLogicalViewTests PRIVATE LLVMTestingSupport) Index: llvm/unittests/DebugInfo/LogicalView/StringPoolTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/StringPoolTest.cpp @@ -0,0 +1,56 @@ +//===- 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; + +TEST(StringPoolTest, AddStrings) { + LVStringPool &PoolInstance = LVStringPool::getInstance(); + 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"))); +}