WWDC Quick Look 💓 By SwiftGGTeam
Symbolication: Beyond the basics

Symbolication: Beyond the basics

元の動画を見る

ハイライト

Apple は、シンボル化によってランタイム アドレスが関数名、ファイル名、行番号に復元される方法を説明し、クラッシュ ログやインストゥルメントのサンプリング情報を診断するための atos、otool、vmmap、nm、symbols、dwarfdump、codesign などのツールを提供します。

主要内容

クラッシュが発生すると、デバイスが認識するのはメモリ アドレスとマシン命令だけです。開発者が目にするコードは、関数、ファイル、行番号です。 2 つの間のマッピングがないと、クラッシュ ログからは特定のアドレスが間違っていることがわかるだけになります。

Session の MagicNumbers プログラムはこのシナリオです。コードは 10 個の乱数から数値を選択します。プログラムがクラッシュした後、元のクラッシュ ログには、MagicNumbers がどこかでクラッシュしたことが示されるだけです。レジスタ、アドレス、および逆アセンブリはすべて、問題を直接指摘することを困難にします。

Xcode Organizer が dSYM をダウンロードした後、クラッシュ ログが再処理されます。ログには、関数呼び出し、ファイル名、行番号、および範囲外の配列アクセスが表示され始めます。その理由は、MAGIC_CHOICE10 要素の配列範囲を超えました。

パフォーマンス分析にも同じ問題があります。機器は完全なシンボル情報を持たない部分的なバックトレースのみを表示できます。高 CPU 間隔と低 CPU 間隔は、同じコードのように見えます。 dSYM を追加すると、Instruments は CPU 使用率が従来のデバッグ コード パスから来ていることを指摘できます。

Apple はこのセッションでシンボル化を 2 つのステップに分割します。最初のステップは、実行時アドレスをバイナリ ファイル内のアドレスに変換し直すことです。 2 番目のステップは、デバッグ情報を使用してファイル アドレスをソース コードにマップすることです。前のステップは、Mach-O、load コマンド、および ASLR スライドに依存しています。後のステップは、関数の開始、nlist シンボル テーブル、および DWARF に依存します。

詳細

atos を使用してアドレスを手動でシンボル化する

02:51atosクラッシュ アドレスはソース コードの場所に復元できます。前提として、対応するビルドの dSYM、アーキテクチャ、ロード アドレス、およびクラッシュ アドレスがわかっていることが前提となります。

atos -o MagicNumbers.dSYM/Contents/Resources/DWARF/MagicNumbers -arch arm64 -l 0x10045c000 -i 0x10045fb70

キーポイント:

  • atosこれは、住所に対応するシンボル情報をクエリするために使用される住所記号化ツールです。
  • -o MagicNumbers.dSYM/Contents/Resources/DWARF/MagicNumbersdSYM パッケージ内の DWARF バイナリを指します。
  • -arch arm64クエリするスキーマ スライスを指定します。 Universal 2 App はスキーマ固有である必要があります。
  • -l 0x10045c000供給__TEXTツールが ASLR スライドを計算するために使用するセグメントのロード アドレス。
  • -i 0x10045fb70必要とするatosまた、インライン関数も考慮してください。これにより、バックトレースがソース コードの呼び出し関係に近づくようになります。

このコマンドは、セッション開始時のクラッシュの例に対応します。 Xcode Organizer を使用して自動的に作業を行うこともできますatos手動で行われます。

Mach-O からファイルアドレスを取得

(07:34) シンボル化の最初のステップは、「ファイルに戻る」ことです。ランタイム アドレスは ASLR (アドレス空間レイアウトのランダム化) の影響を受けます。ファイル内のアドレスは、リンカによって Mach-O (Apple 実行可能ファイル形式) ヘッダーのロード コマンドに書き込まれます。

otool -l MagicNumbers | grep LC_SEGMENT -A8

キーポイント:

  • otool -lMach-O ファイルのロード コマンドを出力します。
  • LC_SEGMENT_64セグメントの属性を説明します。
  • vmaddrリンカによってセグメントに割り当てられたファイル アドレスです。
  • __TEXTセグメントには関数とメソッドのコードが格納され、通常はクラッシュ命令が存在します。
  • -A8させてgrepセグメント名、アドレス、サイズを見やすくするために、一致する行の後の 8 行を出力します。

(11:32) 実行中の読み込みアドレスを使用できますvmmapチェック。クラッシュ ログのバイナリ イメージ領域にも、これらの読み込みアドレスが記録されます。

vmmap MagicNumbers | grep __TEXT

キーポイント:

  • vmmap MagicNumbersプロセスの MagicNumbers メモリ領域を列挙します。
  • grep __TEXTだけ保管してください__TEXT segment。
  • __TEXTランタイムロードアドレスは次のように記録されます。L
  • otool見つかったリンカアドレスは次のように記録されます。A
  • ASLRスライドの計算式はS = L - A

スライドを取得したら、使用します运行时地址 - Sファイルアドレスを取得します。カーネルは起動するたびに異なる ASLR スライドを選択するため、ファイル アドレスは安定しており、変更されません。

クラッシュする命令を逆アセンブリで確認する

(10:31) クラッシュ アドレスをファイル アドレスに変換すると、その場所でマシン命令を表示できます。セッションの例では、otool見つけましたbrk命令。プログラムが例外をトリガーしたことを示します。

otool -tV MagicNumbers -arch arm64

キーポイント:

  • otoolMach-O ファイルを読み取ります。
  • -t印刷する__TEXTセグメント内のテキスト セグメントの内容は、実行可能な命令です。
  • -V出力にシンボリック逆アセンブリ形式を使用させます。
  • MagicNumbersはチェック対象の実行可能ファイルです。
  • -arch arm64Universal 2 ファイルでの誤ったスキーマ チェックを回避するには、スキーマ スライスを指定します。

このステップは、「このアドレスが具体的に何を実行するのか」を検証するのに適しています。ソース コードの行番号を直接知ることはできませんが、クラッシュ ログ内のアドレスが異常な命令に該当するかどうかを確認できます。

関数の開始と nlist シンボル テーブルを区別する

(15:09) 関数の開始は、最も基本的なデバッグ情報です。関数の開始アドレスのみが記録され、関数名は記録されません。に保存されています__LINKEDITセグメントで構成されており、LC_FUNCTION_STARTSロードコマンドが指すもの。

symbols -onlyFuncStartsData -arch arm64 MagicNumbers

キーポイント:

  • symbolsバイナリ ファイル内のシンボルとデバッグ情報をチェックするために使用されます。
  • -onlyFuncStartsData関数開始データのみが出力されます。
  • 出力には、一連のアドレスと空のビットが含まれます。
  • これらのアドレスは、「関数の先頭から何バイト離れたアドレスがアドレスであるか」をデバッガーに伝えます。
  • 関数名、ファイル名、行番号はわかりません。

(17:06) nlist シンボル テーブルには、関数の開始時よりも 1 つ多くの情報レイヤーがあります。それは使用しますnlist_64構造には、名前、タイプ、セクション、説明、および値が記録されます。

struct nlist_64 {
    union {
        uint32_t  n_strx;
    } n_un;
    uint8_t n_type;
    uint8_t n_sect;
    uint16_t n_desc;
    uint64_t n_value;
};

キーポイント:

  • n_un.n_strx文字列テーブル内の名前へのポインタ。
  • n_typeシンボルの種類について説明するこのセッションでは、直接シンボルと間接シンボルに焦点を当てます。
  • n_sectシンボルが配置されているセクションを示します。
  • n_desc追加の説明情報を保存します。
  • n_valueアドレスまたは関連する値を保存します。

(17:59) 直接シンボルは、アプリまたはフレームワークがそれ自体を定義し、リンクに参加する関数またはメソッドです。 Swift シンボルは通常、名前がマングル化されており、読み取り可能にするためにデマングルが必要です。

nm -arch arm64 --defined-only --numeric-sort MagicNumbers | xcrun swift-demangle

キーポイント:

  • nmMach-O のシンボル テーブルを確認してください。
  • -arch arm64arm64 アーキテクチャを選択します。
  • --defined-only現在のバイナリで定義された直接シンボルのみを表示します。
  • --numeric-sortクラッシュアドレスと簡単に比較できるよう、アドレスで並べ替えます。
  • xcrun swift-demangleSwift の壊れた名前を読みやすい名前に復元します。

(23:06) 間接シンボルは、外部フレームワークまたはライブラリの関数を表します。たとえば、MagicNumbers は libswiftCore の Swift 関数に依存しています。

nm -m -arch arm64 --undefined-only --numeric-sort MagicNumbers

キーポイント:

  • -mシンボルがどのフレームワークまたはライブラリから来たのかを示します。
  • --undefined-only現在のバイナリで使用されているが、外部の依存関係によって提供されているシンボルのみを表示します。
  • --numeric-sort住所順に並べ替えます。
  • この情報は、アプリとシステム ライブラリまたは Swift ランタイム間のリンク関係を説明できます。
  • ローカル静的関数のソース コード行番号は提供されません。

リリース ビルドではシンボル テーブルが削除されることがよくあります。Strip Linked ProductStrip StyleStrip Swift Symbolsどのシンボルが保持されるかに影響します。 「関数名はあるがファイル名と行番号が含まれていない」バックトレースが表示される場合、通常は DWARF が欠落しています。

DWARF を使用してファイル名、行番号、インライン呼び出しを取得する

(27:16) DWARF (デバッグ情報形式) は、最も完全なソース コード マッピングを提供します。 dSYM バンドルには、__DWARFセグメントバイナリ。セッションで言及される主要なデータ フローは次のとおりです。debug_infodebug_abbrevそしてdebug_line

dwarfdump -v -debug-info -arch arm64 MagicNumbers.dSYM

キーポイント:

  • dwarfdumpDWARF デバッグ情報を読み取ります。
  • -v詳細を出力します。
  • -debug-infoチェックdebug_infoデータの流れ。
  • -arch arm64スキーマを指定します。
  • MagicNumbers.dSYM確認する dSYM バンドルです。

DWARF はコンパイル ユニットを使用してソース ファイルを表し、サブプログラムを使用して関数を表します。コンパイル ユニットはツリーのルートであり、サブプログラムは子ノードです。debug_lineファイル アドレスとファイル名および行番号のマッピングを保存します。

(29:25)atos使用されていません-iを実行すると、インライン関数の呼び出しの詳細が失われます。セッションの出力には関数名と行番号が含まれていますが、多くの重要な関数関係が欠落しています。

atos -o MagicNumbers.dSYM/Contents/Resources/DWARF/MagicNumbers -arch arm64 -l 0x10045c000 0x10045fb70

キーポイント:

  • このコマンドは、同じ dSYM および同じアドレスをクエリします。
  • それは保持します-o-arch-lそして宛先アドレス。 -そうではありません-iパラメータ。
  • コンパイラの最適化により、関数呼び出しが関数本体に置き換えられます。これはインライン化と呼ばれます。
  • DWARF は、インライン サブルーチンを使用して、そのような関係を記録します。

インライン情報では、インストゥルメントとクラッシュ ログにとって dSYM が重要である理由が説明されています。関数がインライン化される場所、呼び出しポイントがソース コードのどの行にあるのか、インライン呼び出し間の親子関係を表現できるのは DWARF だけです。

dSYM、UUID、DWARF、署名を確認する

(32:29) 静的ライブラリとオブジェクト ファイルには DWARF が含まれる場合もあります。dsymutilデバッグ マップは、関数がどのファイルに由来するかを示すことができ、ツールはこれらの場所をスキャンして DWARF を処理できます。

dsymutil --dump-debug-map -arch arm64 MagicNumbers

キーポイント:

  • dsymutildSYM を生成および確認するためのツールです。
  • --dump-debug-mapデバッグマップを出力します。
  • -arch arm64スキーマを指定します。
  • MagicNumbersチェックされているバイナリです。
  • 出力は、デバッグ情報がどのオブジェクト ファイルまたは静的ライブラリから取得されたものであるかを特定するのに役立ちます。

(33:59) dSYM はアプリのビルドと一致する必要があります。照合は UUID に基づいて行われます。クラッシュ レポートのバイナリ イメージ領域にはアプリ UUID があり、dSYM にも独自の UUID があります。

symbols -uuid MagicNumbers.dSYM

キーポイント:

  • symbolsdSYMのUUIDを読み取ることができます。
  • -uuidUUID情報のみが出力されます。
  • UUID は、load コマンドの一意の識別子です。
  • App と dSYM の UUID は一致している必要があります。
  • Bitcode App の dSYM は、App Store Connect から対応するバージョンをダウンロードする必要があります。

(34:03) ツールチェーンは無効な DWARF を生成する場合があります。 Apple は使用を推奨していますdwarfdumpエラーが見つかった場合は確認してフィードバックを送信します。

dwarfdump --verify MagicNumbers.dSYM

キーポイント:

  • dwarfdumpDWARFの内容を確認してください。
  • --verify整合性検証を実行します。
  • MagicNumbers.dSYM検証済みのデバッグ シンボル ファイルです。
  • エラーが発生すると、シンボル化の品質に影響が出る可能性があります。
  • 1 つのバイナリ DWARF データには 4GB の上限があります。大きすぎる場合は、コンポーネントを分割する必要があります。

(35:09) Instruments が関数名を見つけられず、dSYM があることを確認した場合は、署名と資格も確認してください。開発と構築のニーズget-task-allow資格とインストゥルメントのみがアプリをシンボル化する権限を持っています。

codesign --display -v --entitlements :- MagicApp.app

キーポイント:

  • codesignアプリのコード署名情報を表示します。
  • --display署名メタデータを表示します。
  • -vより詳細な情報を出力します。
  • --entitlements :-権利の内容を標準出力に出力します。
  • MagicApp.app確認するアプリバンドルです。

行方不明の場合get-task-allow、Xcode を確認してくださいCode Signing Inject Base Entitlementsビルド設定。 Xcode は通常、Profile アクションを使用するときにこれを自動的に設定します。

重要ポイント

  • 対処方法: dSYM UUID チェックを CI に追加します。 実行する価値がある理由: セッションでは、アプリの UUID と dSYM が一致する必要があることが強調されています。不一致があると、クラッシュ ログが完全にシンボル表示されなくなります。 開始方法: ビルド製品が生成された後に実行します。symbols -uuid YourApp.app/YourAppそしてsymbols -uuid YourApp.app.dSYM、アーカイブをアップロードする前に UUID を比較します。

  • 何をすべきか: クラッシュ アドレスの簡単なクエリ スクリプトを作成します。 実行する価値がある理由:atos単一のアドレスを関数名、ファイル名、行番号にマッピングできるため、ユーザーから送信されたクラッシュ ログのフラグメントを処理するのに適しています。 開始方法: バイナリ イメージからロード アドレスを抽出し、バックトレースからクラッシュ アドレスを抽出して、それを記述します。atos -o ... -arch ... -l ... -i ...

  • 対処方法: Instruments dSYM チェック手順をパフォーマンスのトラブルシューティング マニュアルに追加します。 実行する価値がある理由: Instruments のセッションの例は、dSYM が存在しないと、時間のかかるパスと通常のパスが同じように見えることを示しています。 開始方法: サンプリング結果にファイル名または行番号が欠落している場合は、まず対応する dSYM を見つけてから、UUID とget-task-allow entitlement。

  • 内容: リリース用のクリーンアップ シンボル除去戦略を構築します。 実行する価値がある理由:Strip Linked ProductそしてStrip Styleシンボル テーブルの可視性に影響を与える可能性があり、保持エラーによりボリュームが増加する可能性があり、過度のストリッピングにより診断機能が低下する可能性があります。 開始方法: ターゲットのストリップビルド設定を確認し、結合しますnmsymbols -onlyNListData直接シンボルが期待どおりであるかどうかを確認します。

  • 何をすべきか: 大規模プロジェクトの dSYM サイズ監視を設定します。 実行する価値がある理由: セッションでは、単一のバイナリ DWARF データには 4 GB の上限があり、大きすぎる dSYM はツールの処理に影響を与えると述べられました。 開始方法: アーカイブ後の各 dSYM のボリュームを数えます。上限に近いモジュールをフレームワークまたは独立したコンポーネントに分割し、各コンポーネントがより小さい dSYM を生成できるようにします。

関連セッション

コメント

GitHub Issues · utterances