llvm-debuginfo-analyzer - 低レベルデバッグ情報の論理表現を出力する。

概要

llvm-debuginfo-analyzer [オプション] [ファイル名 ...]

説明

llvm-debuginfo-analyzer は、バイナリオブジェクトファイル内のデバッグセクションとテキストセクションを解析し、それらの内容を論理ビューで出力します。論理ビューとは、元のユーザーソースコードの構造に密接に対応する、人間が判読可能な表現です。サポートされるオブジェクトファイル形式には、ELF、Mach-O、WebAssembly、PDB、COFF などがあります。

論理ビューは、オブジェクトファイルに埋め込まれたデバッグ情報のさまざまな低レベル表現に関連する複雑さを抽象化します。llvm-debuginfo-analyzer は、その形式に関係なく、デバッグ情報の標準的なビューを生成します。デバッグ情報が同じ元のソースコードを正確に表していると仮定すると、オブジェクトファイルの形式に関係なく、同じ論理ビューが表示されます。

論理ビューには、スコープシンボルという、C/C++プログラミング言語で使用される基本的なソフトウェア要素である、以下の論理要素が含まれています。各論理要素には、クラス関数変数パラメーターなどの属性のセットがあります。 --attribute を使用して、論理要素を出力するときに含める属性を指定できます。論理要素には、要素の特定の型を記述する種類がある場合があります。たとえば、スコープには、関数クラス名前空間の種類値があります。

llvm-debuginfo-analyzer はデフォルトで、論理要素と属性の事前定義されたレイアウトを出力します。コマンドラインオプションを使用すると、(--print) で出力される要素を制御したり、(--report) で特定のレイアウトを使用したり、(--select, --select-offsets) で指定されたパターンに一致させることができます。また、(--select-lines, --select-scopes, --select-symbols, --select-types) を使用して、出力を指定された論理要素に制限できます。

llvm-debuginfo-analyzer は、一連の論理ビューを比較 (--compare) して、差異を見つけ、オブジェクトファイルで検出された可能性のあるデバッグ情報の構文問題を特定 (--warning) することもできます。

オプション

llvm-debuginfo-analyzer のオプションは、いくつかのカテゴリに分かれており、それぞれ異なる目的に合わせて調整されています。

  • 全般 - ヘルプ、バージョンなどを表示するための標準の LLVM オプション。

  • 属性 - 要素を出力するときに、さまざまな詳細を含める方法を記述します。

  • 出力 - ビューを出力するときに含める要素を指定します。

  • 出力 - ビューを出力するときにサポートされる形式を記述します。

  • レポート - ビュー出力用の形式レイアウトを記述します。

  • 選択 - 出力する要素を選択するために、特定の条件または条件を使用できます。

  • 比較 - 論理ビューを比較し、不足している要素または追加された要素を出力します。

  • 警告 - ビューの作成中に検出された警告を出力します。

  • 内部 - 論理ビューの内部解析。

全般

このセクションでは、使用法、バージョン、応答ファイルなどを表示するために使用される標準のヘルプオプションについて説明します。

-h, --help

このコマンドのヘルプと使用法を表示します(詳細については–help-hidden)。

--help-list

オプションをカテゴリにグループ化せずに、このコマンドのヘルプと使用法を表示します(詳細については–help-list-hidden)。

--help-hidden

使用可能なすべてのオプションを表示します。

--print-all-options

コマンドライン解析後にすべてのオプション値を表示します。

--print-options

コマンドライン解析後に、デフォルト以外のオプションを表示します

--version

ツールのバージョンを表示します。

@<FILE>

<FILE>からコマンドラインオプションを読み取ります。

入力ファイルが指定されていない場合、llvm-debuginfo-analyzer はデフォルトで a.out を読み取り、入力ファイルが見つからない場合はエラーを返します。

入力ファイルとして - が使用されている場合、llvm-debuginfo-analyzer は標準入力ストリームから入力を読み取ります。

属性

次のオプションでは、出力された要素に指定された属性を有効にします。属性は、バイナリファイル内の内部オフセット、ロケーション記述子、レジスタ名、ユーザーソースファイル名、追加の要素変換、ツールチェーン名、バイナリファイル形式など、追加されるデータのタイプに基づいてカテゴリに分けられます。

--attribute=<value[,value,...]>

value は、次のリストのいずれかのオプションです。

=all: Include all the below attributes.
=extended: Add low-level attributes.
=standard: Add standard high-level attributes.

次の属性は、論理要素の最も一般的な情報を記述します。これらは、レキシカルスコープレベル、モジュール全体での要素の可視性(グローバル、ローカル)、バイナリファイルを生成したツールチェーン名を特定するのに役立ちます。

=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.

次の属性は、要素が宣言または定義されているユーザーソースコードのファイル名とディレクトリ名を記述します。モジュール全体でパブリックな可視性を持つ関数。これらのオプションを使用すると、相互参照の目的で、要素をユーザーコードの場所にマッピングできます。

=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.

以下の属性は、組み込み型(int、boolなど)、テンプレートのインスタンス化中に使用されるパラメーターと引数、親の名前階層、配列の次元情報、コンパイラーによって生成された要素、および型のエイリアスに関連付けられた基になる型を表示するために、追加の論理要素のソース変換を記述します。

=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.

以下の属性は、シンボルまたはスコープのデバッグ位置情報を記述します。これには、シンボルのカバレッジ率、位置レイアウト内のギャップ、関数に付随するコードセクションを決定する範囲が含まれます。記述子が使用される場合、ターゲットプロセッサーレジスタが表示されます。

=coverage: Symbol location coverage.
=gaps: Missing debug location (gaps).
=location: Symbol debug location.
=range: Debug location ranges.
=register: Processor register names.

以下の属性は、バイナリファイル内のオフセット、特定のインスタンスを区別するためにインライン関数の行に追加された識別子、デバッグ行の状態マシンのレジスタ、コンパイラー(インライン化)またはリンカーの最適化(デッドストリッピング)によって破棄された要素、PDBでMSツールチェーンによって生成されたシステムコンパイルユニットなど、低レベルの詳細に関連付けられています。

=discarded: Discarded elements by the linker.
=discriminator: Discriminators for inlined function instances.
=inserted: Generated inlined abstract references.
=linkage: Object file linkage name.
=offset: Debug information offset.
=qualifier: Line qualifiers (Newstatement, BasicBlock, etc).
=zero: Zero line numbers.

以下の属性は、PE/COFFファイル形式に固有の情報を記述します。これには、MSランタイム型が含まれます。

=system: Display PDB's MS system elements.

上記の属性は、有効にできる標準および拡張カテゴリにグループ化されています。

標準グループには、論理要素を記述するのに十分な情報を追加し、デバッグ情報を扱う際の通常の状況をカバーできる属性が含まれています。

=base
=coverage
=directories
=discriminator
=filename
=files
=format
=level
=producer
=publics
=range
=reference
=zero

拡張グループには、デバッグ情報に関するより広範な知識を必要とする属性が含まれています。これらは、より低いレベルの詳細が必要な場合に意図されています。

=argument
=discarded
=encoded
=gaps
=generated
=global
=inserted
=linkage
=local
=location
=offset
=operation
=pathname
=qualified
=qualifier
=register
=subrange
=system
=typename

PRINT

以下のオプションは、印刷する要素を記述します。使用されるレイアウトは、--reportによって決定されます。ツリーレイアウトでは、明示的に指定されていない場合でも、すべての要素にそれらを囲むレキシカルスコープが印刷されます。

--print=<値[,値,...]>

value は、次のリストのいずれかのオプションです。

=all: Include all the below attributes.

以下のオプションは、要求された要素を印刷します。特定の選択条件(--select)がある場合は、それらと一致する要素のみが印刷されます。elementsの値は、命令、行、スコープ、シンボル、および型を一度に指定する便利な方法です。

=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).

以下のオプションは、要素の作成中に収集された情報(デバッグ情報へのスコープの貢献、作成、印刷、または一致した要素の概要(--select)、ビューの作成中に生成された警告など)を印刷します。

=sizes: Debug Information scopes contributions.
=summary: Summary of elements allocated, selected or printed.
=warnings: Warnings detected.

注:–print=sizesオプションは、ELF固有です。

OUTPUT

以下のオプションは、論理要素を印刷する際に生成される出力を制御する方法を記述します。

--output-file=<path>

出力を<path>で指定されたファイルにリダイレクトします。ここで、-は標準出力ストリームです。

llvm-debuginfo-analyzerには、分割ビューの概念があります。複雑なバイナリ形式からの出力をリダイレクトする場合、個々のファイルに分割され、各ファイルには単一のコンパイルユニットの論理ビュー出力が含まれます。

--output-folder=<名前>

–output=splitが指定された場合にコンパイルユニットごとにファイルを書き込むフォルダ。

--output-level=<レベル>

指定されたレキシカルレベルの値までの要素のみを印刷します。入力ファイルはレキシカルレベル0、コンパイルユニットはレキシカルレベル1です。

--output=<値[,値,...]>

value は、次のリストのいずれかのオプションです。

=all: Include all the below outputs.
=json: Use JSON as the output format (Not implemented).
=split: Split the output by Compile Units.
=text: Use a free form text output.
--output-sort=<キー>

出力内の要素を順序付ける際のプライマリキー(デフォルト:行)。論理要素の種類でソートするには、要素の種類選択オプション(--select-lines--select-scopes--select-symbols--select-types)を熟知している必要があります。これらのオプションは、異なる論理要素の種類を記述しているためです。

=kind: Sort by element kind.
=line: Sort by element line number.
=name: Sort by element name.
=offset: Sort by element offset.

REPORT

実行されているタスク(印刷、比較、選択)に応じて、要素をより適切に表示し、出力を理解しやすくするために、いくつかのレイアウトがサポートされています。

--report=<値[,値,...]>

valueは、次のリストのいずれかのオプションです。

=all: Include all the below reports.
=children: Elements and children are displayed in a tree format.
=list: Elements are displayed in a tabular format.
=parents: Elements and parents are displayed in a tree format.
=view: Elements, parents and children are displayed in a tree format.

listレイアウトは、親子関係なしに表形式で論理要素を表示します。これは、論理ビューを比較するときに特定の条件に一致する要素を表示する好ましい方法であり、違いを見つけやすくする場合があります。

childrenparents、およびviewレイアウトは、スコープをノードとして、型、シンボル、行、その他のスコープを子として、要素をツリー形式で表示します。このレイアウトは、要素間のレキシカルスコープの関係を示し、バイナリファイルをツリールート(レベル0)とし、各コンパイルユニットを子(レベル1)としています。

childrenレイアウトには、指定された条件(--select)または(--compare)に一致する要素とその子が含まれます。

parentsレイアウトには、指定された条件(--select)または(--compare)に一致する要素とその親が含まれます。

組み合わせたviewレイアウトには、指定された条件(--select)または(--compare)に一致する要素、その親と子が含まれます。

注記:

  1. レポートオプションなしで選択条件(--select)が指定されている場合、listレイアウトが選択されます。

  2. 比較モードでは常にviewレイアウトが使用されます。

SELECTION

要素を印刷する場合、異なるデータを含めることができ、バイナリファイルに直接関連付けられたデータ(オフセット)から、カバレッジ、レキシカルスコープレベル、場所などの高レベルの詳細まで、(--attribute)によって異なります。印刷される出力はかなりのサイズになる可能性があるため、いくつかの選択オプションを使用すると、特定の要素を印刷できます。

パターンマッチングでは、大文字と小文字を無視(--select-nocase)でき、正規表現を使用するように拡張(--select-regex)できます。

ELEMENTS

以下のオプションを使用すると、指定された<パターン>、オフセット<値>、または要素<条件>に一致する要素を印刷できます。

--select=<パターン>

名前または行番号が指定された<パターン>に一致するすべての要素を印刷します。

--select-offsets=<値[,値,...]>

オフセットが指定された値に一致するすべての要素を印刷します。--attributeオプションを参照してください。

--select-elements=<条件[,条件,...]>

指定された<条件>を満たすすべての要素を印刷します。ここで、conditionは次のリストのいずれかのオプションです。

=discarded: Discarded elements by the linker.
=global: Element referenced across Compile Units.
=optimized: Optimized inlined abstract references.
--select-nocase

--selectを使用する場合、パターンマッチングでは大文字と小文字が区別されません。

--select-regex

--selectオプションで選択する場合、任意の<パターン>文字列を正規表現として扱います。--select-nocaseが指定されている場合、正規表現では大文字と小文字が区別されなくなります。

<パターン>条件があまりにも一般的な場合、行(--select-lines)、スコープ(--select-scopes)、シンボル(--select-symbols)、型(--select-types)の特定のカテゴリの要素をターゲットにするために、より選択的なオプションを指定できます。

これらのオプションでは、デバッグ情報形式(DWARF、CodeView)の知識が必要です。指定されたkindは、非常に特定の種類の要素を記述しているためです。

LINES

以下のオプションでは、指定された <kind> に一致する行の出力が可能です。指定された条件は、デバッグ行の状態マシンのレジスタを記述します。

--select-lines=<kind[,kind,...]>

kind には、以下のリストのいずれかのオプションを指定します。

=AlwaysStepInto: marks an always step into.
=BasicBlock: Marks a new basic block.
=Discriminator: Line that has a discriminator.
=EndSequence: Marks the end in the sequence of lines.
=EpilogueBegin: Marks the start of a function epilogue.
=LineDebug: Lines that correspond to debug lines.
=LineAssembler: Lines that correspond to disassembly text.
=NeverStepInto: marks a never step into.
=NewStatement: Marks a new statement.
=PrologueEnd: Marks the end of a function prologue.

スコープ

以下のオプションでは、指定された <kind> に一致するスコープの出力が可能です。

--select-scopes=<kind[,kind,...]>

kind には、以下のリストのいずれかのオプションを指定します。

=Aggregate: A class, structure or union.
=Array: An array.
=Block: A generic block (lexical block or exception block).
=CallSite: A call site.
=CatchBlock: An exception block.
=Class: A class.
=CompileUnit: A compile unit.
=EntryPoint: A subroutine entry point.
=Enumeration: An enumeration.
=Function: A function.
=FunctionType: A function pointer.
=InlinedFunction: An inlined function.
=Label: A label.
=LexicalBlock: A lexical block.
=Namespace: A namespace.
=Root: The element representing the main scope.
=Structure: A structure.
=Subprogram: A subprogram.
=Template: A template definition.
=TemplateAlias: A template alias.
=TemplatePack: A template pack.
=TryBlock: An exception try block.
=Union: A union.

シンボル

以下のオプションでは、指定された <kind> に一致するシンボルの出力が可能です。

--select-symbols=<kind[,kind,...]>

kind には、以下のリストのいずれかのオプションを指定します。

=CallSiteParameter: A call site parameter.
=Constant: A constant symbol.
=Inheritance: A base class.
=Member: A member class.
=Parameter: A parameter to function.
=Unspecified: Unspecified parameters to function.
=Variable: A variable.

以下のオプションでは、指定された <kind> に一致する型の出力が可能です。

--select-types=<kind[,kind,...]>

kind には、以下のリストのいずれかのオプションを指定します。

=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.

比較

デバッグ情報を扱う場合、要素の出力が適切なアプローチではない場合があります。これは、同じツールチェーンの異なるバージョンによって引き起こされる影響や、特定のコンパイラの最適化の影響に関心がある場合に当てはまります。

このような場合、どの要素が追加または削除されたかを確認しようとします。複雑なデバッグ情報形式のため、通常の差分ツールを使用してそれらの要素を見つけることは非常に困難であり、異なるデバッグ形式を扱う場合は不可能でさえあります。

llvm-debuginfo-analyzer は、論理要素の比較をサポートしており、異なるツールチェーンバージョンや異なるデバッグ情報形式によって生成された論理ビュー間のセマンティックな違いを見つけることができます。

異なるデバッグ形式から作成された論理ビューを比較する場合、その精度は、デバッグ情報がユーザーコードをどれだけ正確に表現しているかに依存します。たとえば、DWARFデバッグ情報を持つバイナリファイルから作成された論理ビューには、CodeViewデバッグ情報を持つバイナリファイルから作成された論理ビューよりも詳細なデータが含まれる場合があります。

以下のオプションは、比較する要素を記述します。

--compare=<value[,value,...]>

valueは、次のリストのいずれかのオプションです。

=all: Include all the below elements.
=lines: Include lines.
=scopes: Include scopes.
=symbols: Include symbols.
=types: Include types.

llvm-debuginfo-analyzer は、コマンドライン上の最初のバイナリファイルを参照として、2番目のバイナリファイルをターゲットとして扱います。より詳細なレポートを得るために、比較は2回実行されます。参照ビューとターゲットビューを交換することで、ターゲットビューから欠落している要素と、参照ビューに追加された要素を生成します。

比較レポートの記述方法については、--report オプションを参照してください。

警告

入力オブジェクトファイルを読み込む際、llvm-debuginfo-analyzer は、生のデバッグ情報に問題があることを検出できます。これらは論理ビューを出力する目的には致命的とは見なされないかもしれませんが、品質に関する指標となり、生成されたデバッグ情報の問題を潜在的に明らかにすることができます。

以下のオプションでは、後で出力するために記録する警告を記述します。これらは、--print でリクエストされた場合に出力されます。

--warning=<value[,value,...]>

valueは、次のリストのいずれかのオプションです。

=all: Include all the below warnings.

以下のオプションは、論理ビューの作成中に、無効なカバレッジ値、シンボルの位置、無効なコード範囲、ゼロの行などの追加情報を収集します。

=coverages: Invalid symbol coverages values.
=lines: Debug lines that are zero.
=locations: Invalid symbol locations.
=ranges: Invalid code ranges.

内部

論理ビューをより深く理解するためには、より詳細な内部情報へのアクセスが必要となる場合があります。このようなデータは、処理されたデバッグ情報や、誤った論理要素の管理を特定するのに役立ちます。通常、このようなオプションは *デバッグ* ビルドでのみ利用可能です。

llvm-debuginfo-analyzer は、*リリース* ビルドと *デバッグ* ビルドの両方でこれらの高度なオプションをサポートしています。ただし、一意のIDは *デバッグ* ビルドでのみ生成されます。

--internal=<value[,value,...]>

valueは、次のリストのいずれかのオプションです。

=all: Include all the below options.

以下のオプションでは、論理ビューの整合性を確認し、処理されたデバッグタグまたは未実装のデバッグタグを収集し、外部比較ツールを使用する際の論理ビュー比較を容易にするために論理要素の行番号を無視し、llvm-debuginfo-analyzer を呼び出すために使用したコマンドラインオプションを出力できます。

=id: Print unique element ID.
=cmdline: Print command line.
=integrity: Check elements integrity.
=none: Ignore element line number.
=tag: Debug information tags.

注: ELF形式の場合、収集されたタグは、処理されていないデバッグタグを表します。PE/COFF形式の場合、処理されたタグを表します。

このセクションでは、llvm-debuginfo-analyzer を使用して論理ビューを出力し、デバッグ情報の潜在的な問題を診断する方法を示すために、いくつかの実際のバイナリファイルを使用します。

テストケース 1 - 一般的なオプション

以下の例は、llvm-debuginfo-analyzer によって生成されるさまざまな出力を示すために使用されます。この例は、Clang (-O0 -g) を使用して X86 ELFターゲット用にコンパイルしました。

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  }

出力モード

このモードでは、llvm-debuginfo-analyzer は、出力に含める *論理要素* の種類を選択するための条件パターン(正規表現を含む)に基づいて、*論理ビュー* またはその一部を出力します。

基本情報

次のコマンドは、デバッグ情報の内部オフセットでソートされたすべての論理要素の基本情報を出力します。これには、字句レベルとデバッグ情報形式が含まれます。

llvm-debuginfo-analyzer --attribute=level,format
                        --output-sort=offset
                        --print=scopes,symbols,types,lines,instructions
                        test-dwarf-clang.o

または

llvm-debuginfo-analyzer --attribute=level,format
                        --output-sort=offset
                        --print=elements
                        test-dwarf-clang.o

各行は、デバッグ情報内に存在する要素を表します。最初の列はスコープレベル、次に(存在する場合は)関連付けられた行番号、最後に要素の説明が続きます。

Logical View:
[000]           {File} 'test-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'test.cpp'
[002]     2         {Function} extern not_inlined 'foo' -> 'int'
[003]     2           {Parameter} 'ParamPtr' -> 'INTPTR'
[003]     2           {Parameter} 'ParamUnsigned' -> 'unsigned int'
[003]     2           {Parameter} 'ParamBool' -> 'bool'
[003]                 {Block}
[004]     5             {Variable} 'CONSTANT' -> 'const INTEGER'
[004]     5             {Line}
[004]                   {Code} 'movl  $0x7, -0x1c(%rbp)'
[004]     6             {Line}
[004]                   {Code} 'movl  $0x7, -0x4(%rbp)'
[004]                   {Code} 'jmp   0x6'
[004]     8             {Line}
[004]                   {Code} 'movl  -0x14(%rbp), %eax'
[003]     4           {TypeAlias} 'INTEGER' -> 'int'
[003]     2           {Line}
[003]                 {Code} 'pushq   %rbp'
[003]                 {Code} 'movq    %rsp, %rbp'
[003]                 {Code} 'movb    %dl, %al'
[003]                 {Code} 'movq    %rdi, -0x10(%rbp)'
[003]                 {Code} 'movl    %esi, -0x14(%rbp)'
[003]                 {Code} 'andb    $0x1, %al'
[003]                 {Code} 'movb    %al, -0x15(%rbp)'
[003]     3           {Line}
[003]                 {Code} 'testb   $0x1, -0x15(%rbp)'
[003]                 {Code} 'je      0x13'
[003]     8           {Line}
[003]                 {Code} 'movl    %eax, -0x4(%rbp)'
[003]     9           {Line}
[003]                 {Code} 'movl    -0x4(%rbp), %eax'
[003]                 {Code} 'popq    %rbp'
[003]                 {Code} 'retq'
[003]     9           {Line}
[002]     1         {TypeAlias} 'INTPTR' -> '* const int'

詳細に調べると、潜在的なデバッグ問題が見つかる可能性があります。

[003]                 {Block}
[003]     4           {TypeAlias} 'INTEGER' -> 'int'

‘INTEGER’ の定義はレベル [003] にあり、匿名の {Block} (「if」ステートメントの「true」ブランチ)と同じ字句スコープにありますが、元のソースコードでは typedef ステートメントがそのブロックの内側にあることは明らかであるため、‘INTEGER’ の定義もブロック内のレベル [004] にある必要があります。

論理要素の選択

次は、名前または型に ‘inte’ または ‘movl’ を含むすべての *命令*、*シンボル*、*型* を、タブレイアウトを使用して、一致数を指定して出力します。

llvm-debuginfo-analyzer --attribute=level
                        --select-nocase --select-regex
                        --select=INTe --select=movl
                        --report=list
                        --print=symbols,types,instructions,summary
                        test-dwarf-clang.o

Logical View:
[000]           {File} 'test-dwarf-clang.o'

[001]           {CompileUnit} 'test.cpp'
[003]           {Code} 'movl  $0x7, -0x1c(%rbp)'
[003]           {Code} 'movl  $0x7, -0x4(%rbp)'
[003]           {Code} 'movl  %eax, -0x4(%rbp)'
[003]           {Code} 'movl  %esi, -0x14(%rbp)'
[003]           {Code} 'movl  -0x14(%rbp), %eax'
[003]           {Code} 'movl  -0x4(%rbp), %eax'
[003]     4     {TypeAlias} 'INTEGER' -> 'int'
[004]     5     {Variable} 'CONSTANT' -> 'const INTEGER'

-----------------------------
Element      Total      Found
-----------------------------
Scopes           3          0
Symbols          4          1
Types            2          1
Lines           17          6
-----------------------------
Total           26          8

比較モード

このモードでは、llvm-debuginfo-analyzer は、論理ビューを比較して、欠落している論理要素または追加された論理要素を含むレポートを生成します。これは、異なるツールチェーンバージョン、またはまったく異なるツールチェーン(たとえば、DWARFを生成するコンパイラを、CodeViewを生成するまったく異なるコンパイラと直接比較することができます)によって生成されたデバッグ情報のセマンティックな違いを見つけるのに非常に役立ちます。

前の例から、別のコンパイラと比較することによって、上記のデバッグ情報の問題(‘typedef int INTEGER’ の前の無効なスコープの場所に関連する)を発見しました。

GCCを使用して test-dwarf-gcc.o を生成し、出力モードで選択パターンを適用して、次の論理ビュー出力を取得できます。

llvm-debuginfo-analyzer --attribute=level
                        --select-regex --select-nocase --select=INTe
                        --report=list
                        --print=symbols,types
                        test-dwarf-clang.o test-dwarf-gcc.o

Logical View:
[000]           {File} 'test-dwarf-clang.o'

[001]           {CompileUnit} 'test.cpp'
[003]     4     {TypeAlias} 'INTEGER' -> 'int'
[004]     5     {Variable} 'CONSTANT' -> 'const INTEGER'

Logical View:
[000]           {File} 'test-dwarf-gcc.o'

[001]           {CompileUnit} 'test.cpp'
[004]     4     {TypeAlias} 'INTEGER' -> 'int'
[004]     5     {Variable} 'CONSTANT' -> 'const INTEGER'

出力は、両方のオブジェクトに同じ要素が含まれていることを示しています。ただし、‘typedef INTEGER’ は異なるスコープレベルにあります。GCCで生成されたオブジェクトは、正しい値である ‘4’ を示しています。

比較を可能にするために、GCCがClangと同一または類似のDWARFを生成する必要がないことに注意してください。比較しているのはセマンティクスのみです。MSVCとClangによって生成されたCodeViewデバッグ情報を比較する場合も同様です。

比較方法は2つあります。論理ビューと論理要素です。

論理ビュー

論理ビューを全体として比較します。一致するには、比較された各論理要素が同じ親と子を持っている必要があります。

llvm-debuginfo-analyzer の比較機能を使用すると、論理ビューを含む、よりグローバルなコンテキストでその問題を確認できます。

出力は、参照オブジェクトファイルとターゲットオブジェクトファイルを交換することにより、欠落 (-) および 追加 (+) の要素をビュー形式で表示します。

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=view
                        --print=symbols,types
                        test-dwarf-clang.o test-dwarf-gcc.o

Reference: 'test-dwarf-clang.o'
Target:    'test-dwarf-gcc.o'

Logical View:
 [000]           {File} 'test-dwarf-clang.o'

 [001]             {CompileUnit} 'test.cpp'
 [002]     1         {TypeAlias} 'INTPTR' -> '* const int'
 [002]     2         {Function} extern not_inlined 'foo' -> 'int'
 [003]                 {Block}
 [004]     5             {Variable} 'CONSTANT' -> 'const INTEGER'
+[004]     4             {TypeAlias} 'INTEGER' -> 'int'
 [003]     2           {Parameter} 'ParamBool' -> 'bool'
 [003]     2           {Parameter} 'ParamPtr' -> 'INTPTR'
 [003]     2           {Parameter} 'ParamUnsigned' -> 'unsigned int'
-[003]     4           {TypeAlias} 'INTEGER' -> 'int'

出力は、欠落している要素と追加された要素を含む、マージされたビューパス(参照とターゲット)を示します。

論理要素

親が同じであるかどうかを考慮せずに、個々の論理要素を比較します。両方の比較方法で、等しい基準には、名前、ソースコードの場所、型、字句スコープレベルが含まれます。

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=list
                        --print=symbols,types,summary
                        test-dwarf-clang.o test-dwarf-gcc.o

Reference: 'test-dwarf-clang.o'
Target:    'test-dwarf-gcc.o'

(1) Missing Types:
-[003]     4     {TypeAlias} 'INTEGER' -> 'int'

(1) Added Types:
+[004]     4     {TypeAlias} 'INTEGER' -> 'int'

----------------------------------------
Element   Expected    Missing      Added
----------------------------------------
Scopes           4          0          0
Symbols          0          0          0
Types            2          1          1
Lines            0          0          0
----------------------------------------
Total            6          1          1

参照ターゲット の順序の変更

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=list
                        --print=symbols,types,summary
                        test-dwarf-gcc.o test-dwarf-clang.o

Reference: 'test-dwarf-gcc.o'
Target:    'test-dwarf-clang.o'

(1) Missing Types:
-[004]     4     {TypeAlias} 'INTEGER' -> 'int'

(1) Added Types:
+[003]     4     {TypeAlias} 'INTEGER' -> 'int'

----------------------------------------
Element   Expected    Missing      Added
----------------------------------------
Scopes           4          0          0
Symbols          0          0          0
Types            2          1          1
Lines            0          0          0
----------------------------------------
Total            6          1          1

参照ターゲット が切り替えられると、最初のケースの *追加された型* は *欠落した型* としてリストされます。

テストケース 2 - アセンブラ命令

以下の例は、llvm-debuginfo-analyzer によって生成されるさまざまな出力を示すために使用されます。この例は、WindowsおよびLinux用のClang、GCC、MSVCの最新バージョンを使用して、X86 CodeviewおよびELFターゲット用にコンパイルしました(-O0 -g)。

1  extern int printf(const char * format, ... );
2
3  int main()
4  {
5    printf("Hello, World\n");
6    return 0;
7  }

これらは、WindowsおよびLinuxで異なるデバッグ情報形式(CodeView、DWARF)を出力する3つの異なるコンパイラ(MSVC、Clang、GCC)に対して llvm-debuginfo-analyzer が生成する論理ビューです。

llvm-debuginfo-analyzer --attribute=level,format,producer
                        --print=lines,instructions
                        hello-world-codeview-clang.o
                        hello-world-codeview-msvc.o
                        hello-world-dwarf-clang.o
                        hello-world-dwarf-gcc.o

CodeView - Clang (Windows)

Logical View:
[000]           {File} 'hello-world-codeview-clang.o' -> COFF-x86-64

[001]             {CompileUnit} 'hello-world.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]               {Function} extern not_inlined 'main' -> 'int'
[003]     4           {Line}
[003]                 {Code} 'subq    $0x28, %rsp'
[003]                 {Code} 'movl    $0x0, 0x24(%rsp)'
[003]     5           {Line}
[003]                 {Code} 'leaq    (%rip), %rcx'
[003]                 {Code} 'callq   0x0'
[003]     6           {Line}
[003]                 {Code} 'xorl    %eax, %eax'
[003]                 {Code} 'addq    $0x28, %rsp'
[003]                 {Code} 'retq'

CodeView - MSVC (Windows)

Logical View:
[000]           {File} 'hello-world-codeview-msvc.o' -> COFF-i386

[001]             {CompileUnit} 'hello-world.cpp'
[002]               {Producer} 'Microsoft (R) Optimizing Compiler'
[002]               {Function} extern not_inlined 'main' -> 'int'
[003]     4           {Line}
[003]                 {Code} 'pushl   %ebp'
[003]                 {Code} 'movl    %esp, %ebp'
[003]     5           {Line}
[003]                 {Code} 'pushl   $0x0'
[003]                 {Code} 'calll   0x0'
[003]                 {Code} 'addl    $0x4, %esp'
[003]     6           {Line}
[003]                 {Code} 'xorl    %eax, %eax'
[003]     7           {Line}
[003]                 {Code} 'popl    %ebp'
[003]                 {Code} 'retl'

DWARF - Clang (Linux)

Logical View:
[000]           {File} 'hello-world-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'hello-world.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]     3         {Function} extern not_inlined 'main' -> 'int'
[003]     4           {Line}
[003]                 {Code} 'pushq   %rbp'
[003]                 {Code} 'movq    %rsp, %rbp'
[003]                 {Code} 'subq    $0x10, %rsp'
[003]                 {Code} 'movl    $0x0, -0x4(%rbp)'
[003]     5           {Line}
[003]                 {Code} 'movabsq $0x0, %rdi'
[003]                 {Code} 'movb    $0x0, %al'
[003]                 {Code} 'callq   0x0'
[003]     6           {Line}
[003]                 {Code} 'xorl    %eax, %eax'
[003]                 {Code} 'addq    $0x10, %rsp'
[003]                 {Code} 'popq    %rbp'
[003]                 {Code} 'retq'
[003]     6           {Line}

DWARF - GCC (Linux)

Logical View:
[000]           {File} 'hello-world-dwarf-gcc.o' -> elf64-x86-64

[001]             {CompileUnit} 'hello-world.cpp'
[002]               {Producer} 'GNU C++14 9.3.0'
[002]     3         {Function} extern not_inlined 'main' -> 'int'
[003]     4           {Line}
[003]                 {Code} 'endbr64'
[003]                 {Code} 'pushq   %rbp'
[003]                 {Code} 'movq    %rsp, %rbp'
[003]     5           {Line}
[003]                 {Code} 'leaq    (%rip), %rdi'
[003]                 {Code} 'movl    $0x0, %eax'
[003]                 {Code} 'callq   0x0'
[003]     6           {Line}
[003]                 {Code} 'movl    $0x0, %eax'
[003]     7           {Line}
[003]                 {Code} 'popq    %rbp'
[003]                 {Code} 'retq'
[003]     7           {Line}

論理ビューは、行とアセンブラ命令が混在して表示され、異なるツールチェーンによって生成されたコードを比較できます。

テストケース 3 - typedef の字句スコープが正しくない

以下の例は、llvm-debuginfo-analyzer が生成するさまざまな出力を示すために使用されます。この例は、Clang、GCC、MSVC の最新バージョン(-O0 -g)で X86 Codeview および ELF ターゲット向けにコンパイルしました。

 1  int bar(float Input) { return (int)Input; }
 2
 3  unsigned foo(char Param) {
 4    typedef int INT;                // ** Definition for INT **
 5    INT Value = Param;
 6    {
 7      typedef float FLOAT;          // ** Definition for FLOAT **
 8      {
 9        FLOAT Added = Value + Param;
10        Value = bar(Added);
11      }
12    }
13    return Value + Param;
14  }

上記のテストは、Clang コンパイラで見つかったスコープの問題を示すために使用されています: PR44884 (Bugs LLVM) / PR44229 (GitHub LLVM)

4行目と7行目には、異なるレキシカルスコープで定義された2つのtypedefが含まれています。

4    typedef int INT;
7      typedef float FLOAT;

これらは、異なるプラットフォームで異なるデバッグ情報フォーマット(CodeView、DWARF)を出力する3つの異なるコンパイラ(MSVC、Clang、GCC)に対して、llvm-debuginfo-analyzer が生成する論理ビューです。

llvm-debuginfo-analyzer --attribute=level,format,producer
                        --print=symbols,types,lines
                        --output-sort=kind
                        pr-44884-codeview-clang.o
                        pr-44884-codeview-msvc.o
                        pr-44884-dwarf-clang.o
                        pr-44884-dwarf-gcc.o

CodeView - Clang (Windows)

Logical View:
[000]           {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64

[001]             {CompileUnit} 'pr-44884.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]               {Function} extern not_inlined 'bar' -> 'int'
[003]                 {Parameter} 'Input' -> 'float'
[003]     1           {Line}
[002]               {Function} extern not_inlined 'foo' -> 'unsigned'
[003]                 {Block}
[004]                   {Variable} 'Added' -> 'float'
[004]     9             {Line}
[004]    10             {Line}
[003]                 {Parameter} 'Param' -> 'char'
[003]                 {TypeAlias} 'FLOAT' -> 'float'
[003]                 {TypeAlias} 'INT' -> 'int'
[003]                 {Variable} 'Value' -> 'int'
[003]     3           {Line}
[003]     5           {Line}
[003]    13           {Line}

CodeView - MSVC (Windows)

Logical View:
[000]           {File} 'pr-44884-codeview-msvc.o' -> COFF-i386

[001]             {CompileUnit} 'pr-44884.cpp'
[002]               {Producer} 'Microsoft (R) Optimizing Compiler'
[002]               {Function} extern not_inlined 'bar' -> 'int'
[003]                 {Variable} 'Input' -> 'float'
[003]     1           {Line}
[002]               {Function} extern not_inlined 'foo' -> 'unsigned'
[003]                 {Block}
[004]                   {Block}
[005]                     {Variable} 'Added' -> 'float'
[004]                   {TypeAlias} 'FLOAT' -> 'float'
[004]     9             {Line}
[004]    10             {Line}
[003]                 {TypeAlias} 'INT' -> 'int'
[003]                 {Variable} 'Param' -> 'char'
[003]                 {Variable} 'Value' -> 'int'
[003]     3           {Line}
[003]     5           {Line}
[003]    13           {Line}
[003]    14           {Line}

DWARF - Clang (Linux)

Logical View:
[000]           {File} 'pr-44884-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-44884.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]     1         {Function} extern not_inlined 'bar' -> 'int'
[003]     1           {Parameter} 'Input' -> 'float'
[003]     1           {Line}
[003]     1           {Line}
[003]     1           {Line}
[002]     3         {Function} extern not_inlined 'foo' -> 'unsigned int'
[003]                 {Block}
[004]     9             {Variable} 'Added' -> 'FLOAT'
[004]     9             {Line}
[004]     9             {Line}
[004]     9             {Line}
[004]     9             {Line}
[004]     9             {Line}
[004]    10             {Line}
[004]    10             {Line}
[004]    10             {Line}
[004]    13             {Line}
[003]     3           {Parameter} 'Param' -> 'char'
[003]     7           {TypeAlias} 'FLOAT' -> 'float'
[003]     4           {TypeAlias} 'INT' -> 'int'
[003]     5           {Variable} 'Value' -> 'INT'
[003]     3           {Line}
[003]     5           {Line}
[003]     5           {Line}
[003]    13           {Line}
[003]    13           {Line}
[003]    13           {Line}
[003]    13           {Line}

DWARF - GCC (Linux)

Logical View:
[000]           {File} 'pr-44884-dwarf-gcc.o' -> elf32-littlearm

[001]             {CompileUnit} 'pr-44884.cpp'
[002]               {Producer} 'GNU C++14 10.2.1 20201103'
[002]     1         {Function} extern not_inlined 'bar' -> 'int'
[003]     1           {Parameter} 'Input' -> 'float'
[003]     1           {Line}
[003]     1           {Line}
[003]     1           {Line}
[002]     3         {Function} extern not_inlined 'foo' -> 'unsigned int'
[003]                 {Block}
[004]                   {Block}
[005]     9               {Variable} 'Added' -> 'FLOAT'
[005]     9               {Line}
[005]     9               {Line}
[005]     9               {Line}
[005]    10               {Line}
[005]    13               {Line}
[004]     7             {TypeAlias} 'FLOAT' -> 'float'
[003]     3           {Parameter} 'Param' -> 'char'
[003]     4           {TypeAlias} 'INT' -> 'int'
[003]     5           {Variable} 'Value' -> 'INT'
[003]     3           {Line}
[003]     5           {Line}
[003]    13           {Line}
[003]    14           {Line}
[003]    14           {Line}

前の論理ビューから、Clang コンパイラは両方の typedef を同じレキシカルスコープ(3)で出力していることがわかります。これは誤りです。GCC と MSVC は、両方の typedef に対して正しいレキシカルスコープを出力しています。

llvm-debuginfo-analyzer の選択機能を使用すると、Typedef である論理型のみを表示するシンプルな表形式の出力を生成できます。

llvm-debuginfo-analyzer --attribute=level,format
                        --output-sort=name
                        --select-types=Typedef
                        --report=list
                        --print=types
                        pr-44884-*.o

Logical View:
[000]           {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64

[001]           {CompileUnit} 'pr_44884.cpp'
[003]           {TypeAlias} 'FLOAT' -> 'float'
[003]           {TypeAlias} 'INT' -> 'int'

Logical View:
[000]           {File} 'pr-44884-codeview-msvc.o' -> COFF-i386

[001]           {CompileUnit} 'pr_44884.cpp'
[004]           {TypeAlias} 'FLOAT' -> 'float'
[003]           {TypeAlias} 'INT' -> 'int'

Logical View:
[000]           {File} 'pr-44884-dwarf-clang.o' -> elf64-x86-64

[001]           {CompileUnit} 'pr_44884.cpp'
[003]     7     {TypeAlias} 'FLOAT' -> 'float'
[003]     4     {TypeAlias} 'INT' -> 'int'

Logical View:
[000]           {File} 'pr-44884-dwarf-gcc.o' -> elf32-littlearm

[001]           {CompileUnit} 'pr_44884.cpp'
[004]     7     {TypeAlias} 'FLOAT' -> 'float'
[003]     4     {TypeAlias} 'INT' -> 'int'

また、CodeView デバッグ情報では、これらの論理型に対してソースコードの行番号が生成されていないことも示しています。論理ビューは型名でソートされています。

テストケース 4 - ネストされた列挙型の欠落

以下の例は、llvm-debuginfo-analyzer が生成するさまざまな出力を示すために使用されます。この例は、Clang、GCC、MSVC の最新バージョン(-O0 -g)で X86 Codeview および ELF ターゲット向けにコンパイルしました。

 1  struct Struct {
 2    union Union {
 3      enum NestedEnum { RED, BLUE };
 4    };
 5    Union U;
 6  };
 7
 8  Struct S;
 9  int test() {
10    return S.U.BLUE;
11  }

上記のテストは、Clang コンパイラで見つかったスコープの問題を示すために使用されています: PR46466 (Bugs LLVM) / PR45811 (GitHub LLVM)

これらは、異なるプラットフォームで異なるデバッグ情報フォーマット(CodeView、DWARF)を出力する3つの異なるコンパイラ(MSVC、Clang、GCC)に対して、llvm-debuginfo-analyzer が生成する論理ビューです。

llvm-debuginfo-analyzer --attribute=level,format,producer
                        --output-sort=name
                        --print=symbols,types
                        pr-46466-codeview-clang.o
                        pr-46466-codeview-msvc.o
                        pr-46466-dwarf-clang.o
                        pr-46466-dwarf-gcc.o

CodeView - Clang (Windows)

Logical View:
[000]           {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]               {Variable} extern 'S' -> 'Struct'
[002]     1         {Struct} 'Struct'
[003]                 {Member} public 'U' -> 'Union'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

CodeView - MSVC (Windows)

Logical View:
[000]           {File} 'pr-46466-codeview-msvc.o' -> COFF-i386

[001]             {CompileUnit} 'pr-46466.cpp'
[002]               {Producer} 'Microsoft (R) Optimizing Compiler'
[002]               {Variable} extern 'S' -> 'Struct'
[002]     1         {Struct} 'Struct'
[003]                 {Member} public 'U' -> 'Union'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

DWARF - Clang (Linux)

Logical View:
[000]           {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]     8         {Variable} extern 'S' -> 'Struct'
[002]     1         {Struct} 'Struct'
[003]     5           {Member} public 'U' -> 'Union'

DWARF - GCC (Linux)

Logical View:
[000]           {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'
[002]               {Producer} 'GNU C++14 9.3.0'
[002]     8         {Variable} extern 'S' -> 'Struct'
[002]     1         {Struct} 'Struct'
[003]     5           {Member} public 'U' -> 'Union'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'unsigned int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

前の論理ビューから、Clang コンパイラによって生成された DWARF デバッグ情報には、列挙子 **RED** および **BLUE** への参照が含まれていないことがわかります。GCC によって生成された DWARF、Clang および MSVC によって生成された CodeView には、そのような参照が含まれています。

llvm-debuginfo-analyzer の選択機能を使用すると、**Enumerator** である論理型とその親のみを表示する論理ビューを生成できます。論理ビューは型名でソートされています。

llvm-debuginfo-analyzer --attribute=format,level
                        --output-sort=name
                        --select-types=Enumerator
                        --report=parents
                        --print=types
                        pr-46466-*.o
Logical View:
[000]           {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'
[002]     1         {Struct} 'Struct'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

Logical View:
[000]           {File} 'pr-46466-codeview-msvc.o' -> COFF-i386

[001]             {CompileUnit} 'pr-46466.cpp'
[002]     1         {Struct} 'Struct'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

Logical View:
[000]           {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'

Logical View:
[000]           {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-46466.cpp'
[002]     1         {Struct} 'Struct'
[003]     2           {Union} 'Union'
[004]     3             {Enumeration} 'NestedEnum' -> 'unsigned int'
[005]                     {Enumerator} 'BLUE' = '0x1'
[005]                     {Enumerator} 'RED' = '0x0'

llvm-debuginfo-analyzer の選択機能を使用すると、**Enumerator** である論理型のサマリーを含むシンプルな表形式の出力を生成できます。論理ビューは型名でソートされています。

llvm-debuginfo-analyzer --attribute=format,level
                        --output-sort=name
                        --select-types=Enumerator
                        --print=types,summary
                        pr-46466-*.o
Logical View:
[000]           {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64

[001]           {CompileUnit} 'pr-46466.cpp'
[005]           {Enumerator} 'BLUE' = '0x1'
[005]           {Enumerator} 'RED' = '0x0'

-----------------------------
Element      Total      Found
-----------------------------
Scopes           5          0
Symbols          2          0
Types            6          2
Lines            0          0
-----------------------------
Total           13          2

Logical View:
[000]           {File} 'pr-46466-codeview-msvc.o' -> COFF-i386

[001]           {CompileUnit} 'pr-46466.cpp'
[005]           {Enumerator} 'BLUE' = '0x1'
[005]           {Enumerator} 'RED' = '0x0'

-----------------------------
Element      Total      Found
-----------------------------
Scopes           5          0
Symbols          2          0
Types            7          2
Lines            0          0
-----------------------------
Total           14          2

Logical View:
[000]           {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64

[001]           {CompileUnit} 'pr-46466.cpp'

-----------------------------
Element      Total      Found
-----------------------------
Scopes           4          0
Symbols          0          0
Types            0          0
Lines            0          0
-----------------------------
Total            4          0

Logical View:
[000]           {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64

[001]           {CompileUnit} 'pr-46466.cpp'
[005]           {Enumerator} 'BLUE' = '0x1'
[005]           {Enumerator} 'RED' = '0x0'

-----------------------------
Element      Total      Found
-----------------------------
Scopes           5          0
Symbols          0          0
Types            2          2
Lines            0          0
-----------------------------
Total            7          2

**Found** 列の下に表示された値から、Clang によって生成された DWARF デバッグ情報には **Types** が見つからなかったことがわかります。

テストケース 5 - 変数の不正なレキシカルスコープ

以下の例は、llvm-debuginfo-analyzer が生成するさまざまな出力を示すために使用されます。この例は、Clang、GCC、MSVC の最新バージョン(-O0 -g)で X86 Codeview および ELF ターゲット向けにコンパイルしました。

// definitions.h
#ifdef _MSC_VER
  #define forceinline __forceinline
#elif defined(__clang__)
  #if __has_attribute(__always_inline__)
    #define forceinline inline __attribute__((__always_inline__))
  #else
    #define forceinline inline
  #endif
#elif defined(__GNUC__)
  #define forceinline inline __attribute__((__always_inline__))
#else
  #define forceinline inline
  #error
#endif

このテストはインラインコンパイラオプションに依存するため、上記のヘッダーファイルでは _forceinline_ が定義されています。

#include "definitions.h"
 1  #include "definitions.h"
 2  forceinline int InlineFunction(int Param) {
 3    int Var_1 = Param;
 4    {
 5      int Var_2 = Param + Var_1;
 6      Var_1 = Var_2;
 7    }
 8    return Var_1;
 9  }
10
11  int test(int Param_1, int Param_2) {
12    int A = Param_1;
13    A += InlineFunction(Param_2);
14    return A;
15  }

上記のテストは、Clang コンパイラで見つかった変数の問題を示すために使用されています: PR43860 (Bugs LLVM) / PR43205 (GitHub)

これらは、異なるプラットフォームで異なるデバッグ情報フォーマット(CodeView、DWARF)を出力する3つの異なるコンパイラ(MSVC、Clang、GCC)に対して、llvm-debuginfo-analyzer が生成する論理ビューです。

llvm-debuginfo-analyzer --attribute=level,format,producer
                        --output-sort=name
                        --print=symbols
                        pr-43860-codeview-clang.o
                        pr-43860-codeview-msvc.o
                        pr-43860-dwarf-clang.o
                        pr-43860-dwarf-gcc.o

CODEVIEW - Clang (Windows)

Logical View:
[000]           {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64

[001]             {CompileUnit} 'pr-43860.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]     2         {Function} inlined 'InlineFunction' -> 'int'
[003]                 {Parameter} '' -> 'int'
[002]               {Function} extern not_inlined 'test' -> 'int'
[003]                 {Variable} 'A' -> 'int'
[003]                 {InlinedFunction} inlined 'InlineFunction' -> 'int'
[004]                   {Parameter} 'Param' -> 'int'
[004]                   {Variable} 'Var_1' -> 'int'
[004]                   {Variable} 'Var_2' -> 'int'
[003]                 {Parameter} 'Param_1' -> 'int'
[003]                 {Parameter} 'Param_2' -> 'int'

CODEVIEW - MSVC (Windows)

Logical View:
[000]           {File} 'pr-43860-codeview-msvc.o' -> COFF-i386

[001]             {CompileUnit} 'pr-43860.cpp'
[002]               {Producer} 'Microsoft (R) Optimizing Compiler'
[002]               {Function} extern not_inlined 'InlineFunction' -> 'int'
[003]                 {Block}
[004]                   {Variable} 'Var_2' -> 'int'
[003]                 {Variable} 'Param' -> 'int'
[003]                 {Variable} 'Var_1' -> 'int'
[002]               {Function} extern not_inlined 'test' -> 'int'
[003]                 {Variable} 'A' -> 'int'
[003]                 {Variable} 'Param_1' -> 'int'
[003]                 {Variable} 'Param_2' -> 'int'

DWARF - Clang (Linux)

Logical View:
[000]           {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-43860.cpp'
[002]               {Producer} 'clang version 14.0.0'
[002]     2         {Function} extern inlined 'InlineFunction' -> 'int'
[003]                 {Block}
[004]     5             {Variable} 'Var_2' -> 'int'
[003]     2           {Parameter} 'Param' -> 'int'
[003]     3           {Variable} 'Var_1' -> 'int'
[002]    11         {Function} extern not_inlined 'test' -> 'int'
[003]    12           {Variable} 'A' -> 'int'
[003]    14           {InlinedFunction} inlined 'InlineFunction' -> 'int'
[004]                   {Block}
[005]                     {Variable} 'Var_2' -> 'int'
[004]                   {Parameter} 'Param' -> 'int'
[004]                   {Variable} 'Var_1' -> 'int'
[003]    11           {Parameter} 'Param_1' -> 'int'
[003]    11           {Parameter} 'Param_2' -> 'int'

DWARF - GCC (Linux)

Logical View:
[000]           {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64

[001]             {CompileUnit} 'pr-43860.cpp'
[002]               {Producer} 'GNU C++14 9.3.0'
[002]     2         {Function} extern declared_inlined 'InlineFunction' -> 'int'
[003]                 {Block}
[004]     5             {Variable} 'Var_2' -> 'int'
[003]     2           {Parameter} 'Param' -> 'int'
[003]     3           {Variable} 'Var_1' -> 'int'
[002]    11         {Function} extern not_inlined 'test' -> 'int'
[003]    12           {Variable} 'A' -> 'int'
[003]    13           {InlinedFunction} declared_inlined 'InlineFunction' -> 'int'
[004]                   {Block}
[005]                     {Variable} 'Var_2' -> 'int'
[004]                   {Parameter} 'Param' -> 'int'
[004]                   {Variable} 'Var_1' -> 'int'
[003]    11           {Parameter} 'Param_1' -> 'int'
[003]    11           {Parameter} 'Param_2' -> 'int'

前の論理ビューから、Clang コンパイラによって生成された CodeView デバッグ情報では、関数 **InlineFuction** の変数 **Var_1** と **Var_2** が同じレキシカルスコープ(**4**)にあることがわかります。GCC/Clang によって生成された DWARF、および MSVC によって生成された CodeView では、これらの変数が正しいレキシカルスコープ:それぞれ **3** と **4** に示されています。

llvm-debuginfo-analyzer の選択機能を使用すると、名前に _var_ パターンを持つ論理要素のみを表示するシンプルな表形式の出力を生成できます。論理ビューは変数名でソートされています。

llvm-debuginfo-analyzer --attribute=level,format
                        --output-sort=name
                        --select-regex --select-nocase --select=Var
                        --report=list
                        --print=symbols
                        pr-43860-*.o
Logical View:
[000]           {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64

[001]           {CompileUnit} 'pr-43860.cpp'
[004]           {Variable} 'Var_1' -> 'int'
[004]           {Variable} 'Var_2' -> 'int'

Logical View:
[000]           {File} 'pr-43860-codeview-msvc.o' -> COFF-i386

[001]           {CompileUnit} 'pr-43860.cpp'
[003]           {Variable} 'Var_1' -> 'int'
[004]           {Variable} 'Var_2' -> 'int'

Logical View:
[000]           {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64

[001]           {CompileUnit} 'pr-43860.cpp'
[004]           {Variable} 'Var_1' -> 'int'
[003]     3     {Variable} 'Var_1' -> 'int'
[005]           {Variable} 'Var_2' -> 'int'
[004]     5     {Variable} 'Var_2' -> 'int'

Logical View:
[000]           {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64

[001]           {CompileUnit} 'pr-43860.cpp'
[004]           {Variable} 'Var_1' -> 'int'
[003]     3     {Variable} 'Var_1' -> 'int'
[005]           {Variable} 'Var_2' -> 'int'
[004]     5     {Variable} 'Var_2' -> 'int'

また、CodeView デバッグ情報では、これらの論理シンボルに対してソースコードの行番号が生成されていないことも示しています。論理ビューは型名でソートされています。

テストケース 6 - 完全な論理ビュー

上級ユーザー向けに、llvm-debuginfo-analyzer は、デバッグ情報セクション内のオフセット、デバッグロケーションオペランド、リンケージ名などを含む低レベル情報を表示できます。

llvm-debuginfo-analyzer --attribute=all
                        --print=all
                        test-dwarf-clang.o

Logical View:
[0x0000000000][000]            {File} 'test-dwarf-clang.o' -> elf64-x86-64

[0x000000000b][001]              {CompileUnit} 'test.cpp'
[0x000000000b][002]                {Producer} 'clang version 12.0.0'
                                   {Directory} ''
                                   {File} 'test.cpp'
                                   {Public} 'foo' [0x0000000000:0x000000003a]
[0x000000000b][002]                {Range} Lines 2:9 [0x0000000000:0x000000003a]
[0x00000000bc][002]                {BaseType} 'bool'
[0x0000000099][002]                {BaseType} 'int'
[0x00000000b5][002]                {BaseType} 'unsigned int'

[0x00000000a0][002]   {Source} '/test.cpp'
[0x00000000a0][002]      1         {TypeAlias} 'INTPTR' -> [0x00000000ab]'* const int'
[0x000000002a][002]      2         {Function} extern not_inlined 'foo' -> [0x0000000099]'int'
[0x000000002a][003]                  {Range} Lines 2:9 [0x0000000000:0x000000003a]
[0x000000002a][003]                  {Linkage}  0x2 '_Z3fooPKijb'
[0x0000000071][003]                  {Block}
[0x0000000071][004]                    {Range} Lines 5:8 [0x000000001c:0x000000002f]
[0x000000007e][004]      5             {Variable} 'CONSTANT' -> [0x00000000c3]'const INTEGER'
[0x000000007e][005]                      {Coverage} 100.00%
[0x000000007f][005]                      {Location}
[0x000000007f][006]                        {Entry} Stack Offset: -28 (0xffffffffffffffe4) [DW_OP_fbreg]
[0x000000001c][004]      5             {Line} {NewStatement} '/test.cpp'
[0x000000001c][004]                    {Code} 'movl   $0x7, -0x1c(%rbp)'
[0x0000000023][004]      6             {Line} {NewStatement} '/test.cpp'
[0x0000000023][004]                    {Code} 'movl   $0x7, -0x4(%rbp)'
[0x000000002a][004]                    {Code} 'jmp    0x6'
[0x000000002f][004]      8             {Line} {NewStatement} '/test.cpp'
[0x000000002f][004]                    {Code} 'movl   -0x14(%rbp), %eax'
[0x0000000063][003]      2           {Parameter} 'ParamBool' -> [0x00000000bc]'bool'
[0x0000000063][004]                    {Coverage} 100.00%
[0x0000000064][004]                    {Location}
[0x0000000064][005]                      {Entry} Stack Offset: -21 (0xffffffffffffffeb) [DW_OP_fbreg]
[0x0000000047][003]      2           {Parameter} 'ParamPtr' -> [0x00000000a0]'INTPTR'
[0x0000000047][004]                    {Coverage} 100.00%
[0x0000000048][004]                    {Location}
[0x0000000048][005]                      {Entry} Stack Offset: -16 (0xfffffffffffffff0) [DW_OP_fbreg]
[0x0000000055][003]      2           {Parameter} 'ParamUnsigned' -> [0x00000000b5]'unsigned int'
[0x0000000055][004]                    {Coverage} 100.00%
[0x0000000056][004]                    {Location}
[0x0000000056][005]                      {Entry} Stack Offset: -20 (0xffffffffffffffec) [DW_OP_fbreg]
[0x000000008d][003]      4           {TypeAlias} 'INTEGER' -> [0x0000000099]'int'
[0x0000000000][003]      2           {Line} {NewStatement} '/test.cpp'
[0x0000000000][003]                  {Code} 'pushq    %rbp'
[0x0000000001][003]                  {Code} 'movq     %rsp, %rbp'
[0x0000000004][003]                  {Code} 'movb     %dl, %al'
[0x0000000006][003]                  {Code} 'movq     %rdi, -0x10(%rbp)'
[0x000000000a][003]                  {Code} 'movl     %esi, -0x14(%rbp)'
[0x000000000d][003]                  {Code} 'andb     $0x1, %al'
[0x000000000f][003]                  {Code} 'movb     %al, -0x15(%rbp)'
[0x0000000012][003]      3           {Line} {NewStatement} {PrologueEnd} '/test.cpp'
[0x0000000012][003]                  {Code} 'testb    $0x1, -0x15(%rbp)'
[0x0000000016][003]                  {Code} 'je       0x13'
[0x0000000032][003]      8           {Line} '/test.cpp'
[0x0000000032][003]                  {Code} 'movl     %eax, -0x4(%rbp)'
[0x0000000035][003]      9           {Line} {NewStatement} '/test.cpp'
[0x0000000035][003]                  {Code} 'movl     -0x4(%rbp), %eax'
[0x0000000038][003]                  {Code} 'popq     %rbp'
[0x0000000039][003]                  {Code} 'retq'
[0x000000003a][003]      9           {Line} {NewStatement} {EndSequence} '/test.cpp'

-----------------------------
Element      Total    Printed
-----------------------------
Scopes           3          3
Symbols          4          4
Types            5          5
Lines           25         25
-----------------------------
Total           37         37

Scope Sizes:
       189 (100.00%) : [0x000000000b][001]              {CompileUnit} 'test.cpp'
       110 ( 58.20%) : [0x000000002a][002]      2         {Function} extern not_inlined 'foo' -> [0x0000000099]'int'
        27 ( 14.29%) : [0x0000000071][003]                  {Block}

Totals by lexical level:
[001]:        189 (100.00%)
[002]:        110 ( 58.20%)
[003]:         27 ( 14.29%)

**スコープサイズ** テーブルには、各スコープがデバッグ情報に寄与するバイト数が表示され、同じツールチェーンの異なるバージョン間で DWARF セクションの予期しないサイズ変化を特定するために使用できます。

[0x000000002a][002]      2         {Function} extern not_inlined 'foo' -> [0x0000000099]'int'
[0x000000002a][003]                  {Range} Lines 2:9 [0x0000000000:0x000000003a]
[0x000000002a][003]                  {Linkage}  0x2 '_Z3fooPKijb'
[0x0000000071][003]                  {Block}
[0x0000000071][004]                    {Range} Lines 5:8 [0x000000001c:0x000000002f]
[0x000000007e][004]      5             {Variable} 'CONSTANT' -> [0x00000000c3]'const INTEGER'
[0x000000007e][005]                      {Coverage} 100.00%
[0x000000007f][005]                      {Location}
[0x000000007f][006]                        {Entry} Stack Offset: -28 (0xffffffffffffffe4) [DW_OP_fbreg]

**{Range}** 属性は、論理スコープの行範囲を記述します。この場合、関数 **foo** は **2** 行目から **9** 行目の範囲にあります。

**{Coverage}** および **{Location}** 属性は、論理シンボルのデバッグロケーションとカバレッジを記述します。最適化されたコードの場合、カバレッジの値が減少し、プログラムのデバッグ可能性に影響を与えます。

WEBASSEMBLY サポート

以下の例は、llvm-debuginfo-analyzer によって生成された WebAssembly の出力を示すために使用されます。この例は、Clang (-O0 -g –target=wasm32) を使用して WebAssembly 32 ビットターゲット向けにコンパイルしました。

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  }

論理要素の選択

以下は、名前または型に **'block'** または **'.store'** を含むすべての _instruction_、_symbol_、および _type_ を、タブレイアウトを使用して、一致数とともに表示します。

llvm-debuginfo-analyzer --attribute=level
                        --select-nocase --select-regex
                        --select=BLOCK --select=.store
                        --report=list
                        --print=symbols,types,instructions,summary
                        test-clang.o

Logical View:
[000]           {File} 'test-clang.o'

[001]           {CompileUnit} 'test.cpp'
[003]           {Code} 'block'
[003]           {Code} 'block'
[004]           {Code} 'i32.store     12'
[003]           {Code} 'i32.store     20'
[003]           {Code} 'i32.store     24'
[004]           {Code} 'i32.store     28'
[003]           {Code} 'i32.store     28'
[003]           {Code} 'i32.store8    19'

-----------------------------
Element      Total    Printed
-----------------------------
Scopes           3          0
Symbols          4          0
Types            2          0
Lines           62          8
-----------------------------
Total           71          8

比較モード

前の例から、別のコンパイラと比較することによって、上記のデバッグ情報の問題(‘typedef int INTEGER’ の前の無効なスコープの場所に関連する)を発見しました。

GCCを使用して test-dwarf-gcc.o を生成し、出力モードで選択パターンを適用して、次の論理ビュー出力を取得できます。

llvm-debuginfo-analyzer --attribute=level
                        --select-regex --select-nocase --select=INTe
                        --report=list
                        --print=symbols,types
                        test-clang.o test-dwarf-gcc.o

Logical View:
[000]           {File} 'test-clang.o'

[001]           {CompileUnit} 'test.cpp'
[003]     4     {TypeAlias} 'INTEGER' -> 'int'
[004]     5     {Variable} 'CONSTANT' -> 'const INTEGER'

Logical View:
[000]           {File} 'test-dwarf-gcc.o'

[001]           {CompileUnit} 'test.cpp'
[004]     4     {TypeAlias} 'INTEGER' -> 'int'
[004]     5     {Variable} 'CONSTANT' -> 'const INTEGER'

出力は、両方のオブジェクトに同じ要素が含まれていることを示しています。ただし、‘typedef INTEGER’ は異なるスコープレベルにあります。GCCで生成されたオブジェクトは、正しい値である ‘4’ を示しています。

比較方法は2つあります。論理ビューと論理要素です。

論理ビュー

論理ビューを全体として比較します。一致するには、比較された各論理要素が同じ親と子を持っている必要があります。

出力は、参照オブジェクトファイルとターゲットオブジェクトファイルを交換することにより、欠落 (-) および 追加 (+) の要素をビュー形式で表示します。

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=view
                        --print=symbols,types
                        test-clang.o test-dwarf-gcc.o

Reference: 'test-clang.o'
Target:    'test-dwarf-gcc.o'

Logical View:
 [000]           {File} 'test-clang.o'

 [001]             {CompileUnit} 'test.cpp'
 [002]     1         {TypeAlias} 'INTPTR' -> '* const int'
 [002]     2         {Function} extern not_inlined 'foo' -> 'int'
 [003]                 {Block}
 [004]     5             {Variable} 'CONSTANT' -> 'const INTEGER'
+[004]     4             {TypeAlias} 'INTEGER' -> 'int'
 [003]     2           {Parameter} 'ParamBool' -> 'bool'
 [003]     2           {Parameter} 'ParamPtr' -> 'INTPTR'
 [003]     2           {Parameter} 'ParamUnsigned' -> 'unsigned int'
-[003]     4           {TypeAlias} 'INTEGER' -> 'int'

出力は、欠落している要素と追加された要素を含む、マージされたビューパス(参照とターゲット)を示します。

論理要素

親が同じであるかどうかを考慮せずに、個々の論理要素を比較します。両方の比較方法で、等しい基準には、名前、ソースコードの場所、型、字句スコープレベルが含まれます。

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=list
                        --print=symbols,types,summary
                        test-clang.o test-dwarf-gcc.o

Reference: 'test-clang.o'
Target:    'test-dwarf-gcc.o'

(1) Missing Types:
-[003]     4     {TypeAlias} 'INTEGER' -> 'int'

(1) Added Types:
+[004]     4     {TypeAlias} 'INTEGER' -> 'int'

----------------------------------------
Element   Expected    Missing      Added
----------------------------------------
Scopes           4          0          0
Symbols          0          0          0
Types            2          1          1
Lines            0          0          0
----------------------------------------
Total            6          1          1

参照ターゲット の順序の変更

llvm-debuginfo-analyzer --attribute=level
                        --compare=types
                        --report=list
                        --print=symbols,types,summary
                        test-dwarf-gcc.o test-clang.o

Reference: 'test-dwarf-gcc.o'
Target:    'test-clang.o'

(1) Missing Types:
-[004]     4     {TypeAlias} 'INTEGER' -> 'int'

(1) Added Types:
+[003]     4     {TypeAlias} 'INTEGER' -> 'int'

----------------------------------------
Element   Expected    Missing      Added
----------------------------------------
Scopes           4          0          0
Symbols          0          0          0
Types            2          1          1
Lines            0          0          0
----------------------------------------
Total            6          1          1

参照ターゲット が切り替えられると、最初のケースの *追加された型* は *欠落した型* としてリストされます。

終了ステータス

llvm-debuginfo-analyzer は、入力ファイルが正常に解析および出力された場合は 0 を返します。それ以外の場合は 1 を返します。

制限事項と既知の問題

Limitations を参照してください。

参照

llvm-dwarfdump