最新のツールを使用した WebAssembly のデバッグ

Ingvar Stepanyan
Ingvar Stepanyan

これまでの道のり

1 年前、Chrome は初期サポートを発表 を使用します。

基本的なステップ実行のサポートを紹介し、機会について説明しました。 代わりに DWARF 情報を使用する ソースマップは将来的に利用可能になります。

  • 変数名の解決
  • プリティ プリントのタイプ
  • ソース言語の式を評価する
  • ほか多数

本日は、期待していた機能が実現した様子をご紹介します Emscripten と Chrome DevTools のチームは 特に C および C++ アプリの場合は特にそうです。

なお、これはまだベータ版であることにご留意ください 更新するには、すべてのツールの最新バージョンを使用する必要があります。 ご自身の責任においてご利用ください。問題が発生した場合は、 https://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue.

前回と同じ単純な C の例から始めましょう。

#include <stdlib.h>

void assert_less(int x, int y) {
  if (x >= y) {
    abort();
  }
}

int main() {
  assert_less(10, 20);
  assert_less(30, 20);
}

コンパイルには、最新の Emscripten を使用します。 元の投稿と同様に -g フラグを渡して、デバッグ 含まれます

emcc -g temp.c -o temp.html

これで、生成されたページを localhost HTTP サーバー( (serve を使用した場合の例)、および 最新版の Chrome Canary で開いてください。

今度は、Chrome と統合するヘルパー拡張機能も必要です。 DevTools ですべてのデバッグ情報を WebAssembly ファイルにエンコードされますこちらの リンク: goo.gle/wasm-debugging-extension

また、DevTools で WebAssembly デバッグを有効にします。 テスト。Chrome DevTools を開いて、歯車アイコン()アイコンをクリックします。 [DevTools] ペインの右上で、[Experiments] パネルに移動します。 [WebAssembly Debugging: Enable DWARF support] チェックボックスをオンにします。

DevTools 設定の [Experiments] ペイン

[Settings] を閉じると、DevTools によって再読み込みが提案されます。 設定を適用するだけです1 回限りは以上です できます。

[ソース] パネルに戻り、[一時停止 例外(⏸ アイコン)を選択してから、[Pause on キャッチされた例外] をオンにします。 ページを再読み込みしてください。例外により DevTools が一時停止したことがわかります。

[一時停止した例外で一時停止] を有効にする方法を示す [Sources] パネルのスクリーンショット

デフォルトでは、Emscripten で生成されたグルーコードで停止しますが、 [Call Stack] ビューが表示されます。これは、 呼び出された元の C 行に移動できます。 abort:

DevTools が `assert_less` 関数で一時停止し、スコープビューに `x` と `y` の値が表示される

これで、[スコープ] ビューを見ると、元の名前を確認できます。 C/C++ コードで変数と値を宣言することで、インフラストラクチャを $localN のようなマングリングされた名前の意味と、それらが ソースコードが表示されます。

これは整数などのプリミティブ値だけでなく、 構造体、クラス、配列などの型も含まれます。

リッチ型のサポート

これらを示す、より複雑な例を見てみましょう。この マンデルブロ フラクタルを 次の C++ コードは次のようになります。

#include <SDL2/SDL.h>
#include <complex>

int main() {
  // Init SDL.
  int width = 600, height = 600;
  SDL_Init(SDL_INIT_VIDEO);
  SDL_Window* window;
  SDL_Renderer* renderer;
  SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL, &window,
                              &renderer);

  // Generate a palette with random colors.
  enum { MAX_ITER_COUNT = 256 };
  SDL_Color palette[MAX_ITER_COUNT];
  srand(time(0));
  for (int i = 0; i < MAX_ITER_COUNT; ++i) {
    palette[i] = {
        .r = (uint8_t)rand(),
        .g = (uint8_t)rand(),
        .b = (uint8_t)rand(),
        .a = 255,
    };
  }

  // Calculate and draw the Mandelbrot set.
  std::complex<double> center(0.5, 0.5);
  double scale = 4.0;
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      std::complex<double> point((double)x / width, (double)y / height);
      std::complex<double> c = (point - center) * scale;
      std::complex<double> z(0, 0);
      int i = 0;
      for (; i < MAX_ITER_COUNT - 1; i++) {
        z = z * z + c;
        if (abs(z) > 2.0)
          break;
      }
      SDL_Color color = palette[i];
      SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
      SDL_RenderDrawPoint(renderer, x, y);
    }
  }

  // Render everything we've drawn to the canvas.
  SDL_RenderPresent(renderer);

  // SDL_Quit();
}

ご覧のとおり、このアプリケーションはまだかなり小さく、1 つの ファイルには 50 行のコードが含まれていますが、今回は の SDL ライブラリなどの 画像や、画像から得られる複素数を C++ 標準ライブラリ。

上記と同じ -g フラグを使用してコンパイルし、 デバッグ情報を提供します。また、Emscripten に SDL2 の提供を依頼します。 任意のサイズのメモリを使用できます。

emcc -g mandelbrot.cc -o mandelbrot.html \
     -s USE_SDL=2 \
     -s ALLOW_MEMORY_GROWTH=1

生成されたページをブラウザでアクセスすると、 ランダムな色のフラクタル図形:

デモページ

DevTools を開くと、再び元の C++ ファイルが表示されます。この ただし、コードにエラーはないため、次のように設定します。 ブレークポイントを追加します。

再度ページを再読み込みすると、デバッガが C++ ソース:

`SDL_Init` 呼び出しで DevTools が一時停止する

右側にはすでにすべての変数が表示されていますが、width のみです。 height は現在初期化されているため、何も実行することは 検査します。

メインのマンデルブロ ループ内に別のブレークポイントを設定して、 少し前にスキップします。

ネストされたループ内で DevTools が一時停止する

この時点で、palette はランダムな色で塗りつぶされています。 配列自体も個々の配列自体も展開できます SDL_Color 構造を検証し、そのコンポーネントを検査して、 すべて正常です(たとえば、「アルファ」チャンネルは常に にします)。同様に、実際の値と元の値の状態を center 変数に格納された複素数の虚数部。

他の方法では見つけにくい、深くネストされたプロパティにアクセスする場合は、 [スコープ] ビューに移動するには、コンソール 評価も行われています。ただし、より複雑な C++ 式は サポートされていません。

`palette[10].r` の結果が表示されているコンソール パネル

実行を数回再開して、内部 x の状態を確認してみましょう。 再度 [スコープ] ビューを表示して、 変数名をウォッチリストに追加するか、コンソールで評価するか、 ソースコード内の変数にカーソルを合わせます。

ソース内の変数 `x` に関するツールチップ(値「3」が表示されている)

ここから、C++ ステートメントをステップインまたはステップ オーバーして、 他の変数も変化しています。

`color`、`point`、その他の変数の値が表示されているツールチップとスコープビュー

デバッグ情報があればうまくいきますが、 デバッグでビルドされていないコードをデバッグする場合、 選択肢はあるか?

未加工の WebAssembly デバッグ

たとえば、Emscripten に事前構築済みの SDL ライブラリを ソースから直接コンパイルするのではなく 現時点では、関連するソースをデバッガが見つける方法はありません。 もう一度 SDL_RenderDrawColor に移動しましょう。

`mandelbrot.wasm` の逆アセンブル ビューを示す DevTools

WebAssembly デバッグの生の体験に戻りました。

少し恐ろしく見えますが、ほとんどのウェブ デベロッパーはあまりそう思わないでしょう。 デコーダをデバッグする必要はあるか ビルドされたライブラリがデバッグ情報なしで サードパーティ ライブラリをご自身で管理できない場合、または 本番環境でのみ発生するバグの いずれかに遭遇した場合です

そのような場合に備えて、基本的な デバッグ経験も豊富です。

まず、WebAssembly デバッグを未加工の使用したことがある場合は、 逆アセンブル全体が 1 つのファイル内に表示され、 Sources エントリ wasm-53834e3e/ wasm-53834e3e-7 がどの関数に対応しているかを推測する回数が増える。

新しい名前生成スキーム

逆アセンブル ビューの名前も改善しました。これまでは 数値インデックスだけを使う 関数の場合は名前をまったく持たないこともあります

他の逆アセンブル ツールと同様の名前を生成します。名前には、 WebAssembly 名セクションのヒントを使用する インポート/エクスポートのパスができ 最後にすべて失敗した場合 $func123 など、アイテムの型とインデックスに基づいてオブジェクトが作成されます。Google Chat では 上のスクリーンショットでは、これはすでにわずかに スタックトレースと逆アセンブルが わかりやすくなっています

利用可能な型情報がない場合、検査が難しい可能性がある プリミティブ以外のすべての値(たとえば、ポインタは 通常の整数として格納され、その背後に何が保存されているかは できます。

メモリ検査

以前は、[Scope] ビューで env.memory で表される WebAssembly メモリ オブジェクトを展開して検索するだけでした。 表示されます。いくつかの簡単なシナリオでうまくいったが、 拡張が特に便利でデータの再解釈ができず データをバイト値以外の形式で指定します。そこで、便利な機能を 線形メモリ インスペクタも役立ちます。

env.memory を右クリックすると、新しい [メモリを検査] というオプションを使用します。

[Inspect Memory] が表示されている [Scope] ペインの [env.memory] のコンテキスト メニューアイテム

クリックすると、Memory Inspector が表示されます。 16 進数と ASCII ビューで WebAssembly メモリを検査できます。 特定の住所への移動、 保存できます。

メモリの 16 進数ビューと ASCII ビューを表示している DevTools の [Memory Inspector] ペイン

高度なシナリオと注意点

WebAssembly コードのプロファイリング

DevTools を開くと、WebAssembly コードが階層化される宛先 デバッグを有効にしますこのバージョンは動作が遅く、 つまり、console.timeperformance.now は などの方法でコードの速度を測定する方法を 表示される数字は実際のパフォーマンスを表すとは限らないため、 ありません。

代わりに、DevTools の [Performance] パネルを使用する必要があります。 これによりコードが最大速度で実行され さまざまな部門に費やされた時間の詳細な内訳:

さまざまな Wasm 関数を示すプロファイリング パネル

または、DevTools を閉じてアプリケーションを実行することもできます。 完了したら、それらを開いてコンソールを調べます。

プロファイリングのシナリオは今後改善する予定ですが、現時点では 注意すべき点がありますWebAssembly についてさらに詳しく WebAssembly コンパイル パイプラインに関するドキュメントをご覧ください。

異なるマシン(Docker / ホストなど)でのビルドとデバッグ

Docker、仮想マシン、リモートビルド サーバーでビルドする場合、 多くの場合、ソースファイルへのパスがファイル パスに 自分のファイル システムのパスと一致しない場合に、 確認できます。この場合、ファイルは Sources パネルの読み込みに失敗します。

この問題を修正するため、 C/C++ 拡張オプションを使用します。任意のパスを再マッピングしたり、 DevTools でソースの検索に役立ちます。

たとえば、ホストマシン上のプロジェクトが C:\src\my_project ですが、次の Docker コンテナ内でビルドされています。 そのパスが /mnt/c/src/my_project として表されていた場合、 これらのパスを接頭辞として指定することで、デバッグ中に元に戻すことができます。

C/C++ デバッグ拡張機能のオプション ページ

最初に一致した接頭辞が優先されます。他の C++ 言語に習熟している場合、 デバッガ。このオプションは set substitute-path コマンドに似ています。 target.source-map 設定を使用します。

最適化されたビルドのデバッグ

他の言語と同様に、デバッグが最適に機能するのは、 無効です。最適化では、関数同士のインライン化、並べ替えや コードの一部を完全に削除したり デバッガを混乱させ、ユーザーをユーザーとして操作してしまう可能性があります。

デバッグ エクスペリエンスは限定的であってもかまわないが、 最適化されたビルドをデバッグすると、最適化のほとんどが 関数のインライン化を除きます。残りの ただし、当面は -fno-inline を使用して -O レベルの最適化でコンパイルするときに無効にします。次に例を示します。

emcc -g temp.c -o temp.html \
     -O3 -fno-inline

デバッグ情報の分離

デバッグ情報では、コードに関する多くの詳細が保持されます。 型、変数、関数、スコープ、ロケーションなど、 デバッガにとって便利です。そのため、多くの場合、ファイアウォール ルールの 構築できます。

WebAssembly モジュールの読み込みとコンパイルを高速化するには、 このデバッグ情報を 表示されます。そのためには、Emscripten で -gseparate-dwarf=… フラグを次のように渡します。 ファイル名を指定します。

emcc -g temp.c -o temp.html \
     -gseparate-dwarf=temp.debug.wasm

この場合、メイン アプリケーションはファイル名のみを保存します。 temp.debug.wasm。ヘルパー拡張機能が特定して、 読み込む必要があるかもしれません。

この機能を上記のような最適化と組み合わせると、 ほぼ最適化された製品版ビルドを リリースするために使用できます ローカルのサイドファイルを使用してデバッグできます。この例では 保存された URL をオーバーライドして、拡張機能が サイドファイルを見つけます。次に例を示します。

emcc -g temp.c -o temp.html \
     -O3 -fno-inline \
     -gseparate-dwarf=temp.debug.wasm \
     -s SEPARATE_DWARF_URL=file://[local path to temp.debug.wasm]

続く

新機能がたくさんありました。

こうした新しい統合により、Chrome DevTools は実用的で JavaScript だけでなく、C / C++ アプリにも使用できる強力なデバッガです。 これまで以上に簡単にアプリを使用できます。 共有のクロス プラットフォーム ウェブに提供します。

しかし、私たちの取り組みはまだ終わっていません。このコースでは 次の段階として進めていきます

  • デバッグ時の粗いエッジのクリーンアップ。
  • カスタム型フォーマッタのサポートを追加しました。
  • 現在、 プロファイリング(WebAssembly アプリの場合)
  • 見つけやすくするためにコード カバレッジのサポートを追加しました。 ありません。
  • コンソール評価での式のサポートを改善しました。
  • 対応言語の追加。
  • …など

当面は、ご自身のコードで現在のベータ版をお試しいただき、見つかったものについてお知らせください。 問題の https://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue.

プレビュー チャンネルをダウンロードする

デフォルトの開発ブラウザとして Chrome の CanaryDev、または Beta を使用することを検討してください。これらのプレビュー チャンネルを使用すると、DevTools の最新機能にアクセスしたり、最先端のウェブ プラットフォーム API をテストしたり、ユーザーに先駆けてサイトの問題を検出したりできます。

Chrome DevTools チームへのお問い合わせ

以下のオプションを使用して、投稿の新機能や変更点、または DevTools に関連するその他のことについて話し合います。

  • ご提案やフィードバックは、crbug.com からお送りください。
  • DevTools の問題を報告するには、その他のオプションもっと見る) >ヘルプ >DevTools で DevTools の問題を報告します。
  • @ChromeDevTools でツイートしてください。
  • DevTools の新機能に関する YouTube 動画または DevTools のヒントの YouTube 動画にコメントを残してください。