Memory Inspector を C/C++ デバッグ用に拡張する

Chrome 92 では、リニアメモリ バッファを検査するためのツールである メモリ インスペクタを導入しました。この記事では、C/C++ デバッグ用インスペクタの改善方法と、その過程で直面した技術的な課題について説明します。

C/C++ デバッグと Memory Inspector を初めて使用する場合は、関連するブログ投稿を以下に示します。

はじめに

メモリ インスペクタには、リニアメモリ バッファ用のより強力なデバッグ オプションが用意されています。C/C++ の場合、WebAssembly メモリ内の C/C++ メモリ オブジェクトを検査できます。

周囲の WebAssembly メモリからオブジェクトのバイトを見つけることが課題でした。オブジェクトの開始時点からオブジェクトのサイズとバイト数を把握しておく必要があります。次のスクリーンショットでは、10 要素の int32 配列の最初のバイトが選択されていますが、他のバイトが配列に属していることはすぐにはわかりません。オブジェクトに属するすべてのバイトを即座に認識できれば便利です。

1 つのバイトがハイライト表示された元のメモリ インスペクタのスクリーンショット

Memory Inspector でのオブジェクトのハイライト表示

Chrome 107 以降では、メモリ インスペクタで C/C++ メモリ オブジェクトのすべてのバイトがハイライト表示されます。これにより、周囲の記憶と区別しやすくなります。

配列が鮮やかにハイライト表示された、更新された Memory Inspector のスクリーンショット

Memory Inspector の動作については、以下の動画をご覧ください。Memory Inspector の配列 x を表示すると、メモリビューアにハイライト表示されたメモリが、そのすぐ上に新しいチップとともに表示されます。このチップには、ハイライト表示された思い出の名前と種類が表示されます。チップをクリックすると、オブジェクトのメモリに移動します。チップにカーソルを合わせると、× アイコンが表示されます。このアイコンをクリックすると、ハイライトが解除されます。

検査するオブジェクトの外のバイトを選択すると、気が散らないようハイライト表示のフォーカスがずれます。もう一度フォーカスを合わせるには、オブジェクトのバイトまたはチップをもう一度クリックします。

オブジェクトのハイライト表示のサポートは配列に限定されません。構造体、オブジェクト、ポインタを調べることもできます。これらの変更により、C/C++ アプリのメモリの探索がこれまで以上に簡単になりました。

試してみたい場合は、そこで次の作業が必要になります。

  • Chrome 107 以降を使用している。
  • C/C++ DWARF 拡張機能をインストールします。
  • DevTools > 設定] をタップします。 [設定] > [試験運用版] > [WebAssemble デバッグ: DWARF サポートを有効にする] で DWARF デバッグを有効にします。
  • こちらのデモページを開きます。
  • ページに記載されている手順に沿って操作します。

デバッグの例

このセクションでは、C/C++ のデバッグに Memory Inspector を使用する方法を示します。次のコードサンプルでは、プログラマが整数配列を作成し、ポインタ演算を使用して最後の要素を選択することにしました。残念ながら、プログラマーはポインタの計算を間違えてしまい、最後の要素を出力する代わりに、ナンセンスな値を出力しています。

#include <iostream>

int main()
{
    int numbers[] = {1, 2, 3, 4};
    int *ptr = numbers;
    int arraySize = sizeof(numbers)/sizeof(int);
    int* lastNumber = ptr + arraySize;  // Can you notice the bug here?
    std::cout <<../ *lastNumber <<../ '\n';
    return 0;
}

プログラマは Memory Inspector を使用して問題をデバッグします。こちらのデモを参考にしてください。まず、[Memory Inspector] で配列を検査し、numbers 配列に想定どおりに整数 1234 のみが含まれていることを確認します。

int32 配列を検査して開いた Memory Inspector のスクリーンショットすべての配列要素がハイライト表示されています。

次に、[スコープ] ペインで lastNumber 変数を開き、ポインタが配列外の整数を参照していることを確認します。この知識を利用して、プログラマーは行 8 のポインタ オフセットを誤ってカウントしていることに気付きます。ptr + arraySize - 1 になっているはずです。

開いたメモリ インスペクタのスクリーンショット。lastNumber という名前のポインタが指すメモリがハイライト表示されています。ハイライト表示されたメモリは、前にハイライト表示された配列の最後のバイトの直後にあります。

これは単なる例ですが、オブジェクトのハイライト表示によってメモリ オブジェクトのサイズと位置を効果的に伝える方法を示しています。これにより、C/C++ アプリのメモリ内で何が起こっているかをよりよく把握できます。

DevTools がハイライト表示する内容を判断する方法

このセクションでは、C/C++ デバッグを可能にするツールのエコシステムについて説明します。具体的には、DevTools、V8、C/C++ DWARF 拡張機能、Emscripten を使用して Chrome で C/C++ デバッグを可能にする方法について説明します。

DevTools で C/C++ デバッグを最大限に活用するには、次の 2 つが必要です。

  • Chrome にインストールされている C/C++ DWARF 拡張機能
  • こちらのブログ投稿に記載されているように、最新の Emscripten コンパイラで WebAssembly にコンパイルされた C/C++ ソースファイル

しかし、なぜこうなってしまったのでしょう。Chrome の JavaScript および WebAssembly エンジンである V8 は、C または C++ を実行できません。C/C++ から WebAssembly へのコンパイラである Emscripten を使用すると、C または C++ でビルドされたアプリを WebAssembly としてコンパイルし、ブラウザで実行できます。

コンパイル中に、emscripten は DWARF デバッグデータをバイナリに埋め込みます。大まかに言うと、このデータは拡張機能が、C/C++ 変数に対応する WebAssembly 変数を特定するために役立ちます。これにより、V8 が実際に WebAssembly を実行しているにもかかわらず、DevTools で C++ 変数を表示できます。DWARF デバッグデータの例については、こちらのブログ投稿をご覧ください。

それでは、lastNumber を公開するとどうなるでしょうか。メモリアイコンをクリックすると、DevTools によって検査対象の変数がすぐにチェックされます。次に、lastNumber のデータ型と場所について拡張機能にクエリを実行します。拡張機能がその情報で応答するとすぐに、Memory Inspector は関連するメモリスライスを表示し、そのタイプを把握してオブジェクトのサイズも表示できます。

前の例の lastNumber を見ると、lastNumber: int * を検査したはずなのに、メモリ インスペクタのチップに *lastNumber: int と表示されています。これはなぜでしょうか。インスペクタは、C++ スタイルのポインタ デリファレンスを使用して、表示されるオブジェクトのタイプを示します。ポインタを検査すると、インスペクタにそのポインタが指しているものが表示されます。

デバッガ ステップのハイライト表示

Memory Inspector でオブジェクトを表示し、デバッガでステップすると、Inspector は、ハイライトが引き続き適用可能であると判断した場合は、ハイライトを保持します。当初、この機能はロードマップにはありませんでしたが、デバッグ体験が損なわれることにすぐに気付きました。以下の動画のように、各ステップの後に配列を再検査しなければならないとしたらどうでしょう。

デバッガが新しいブレークポイントにヒットすると、Memory Inspector は再度 V8 と、前のハイライトに関連する変数の拡張機能をクエリします。次に、オブジェクトの位置とタイプを比較します。一致した場合、ハイライトは維持されます。上の動画では、配列 x に書き込む for ループがあります。これらのオペレーションでは配列の型や位置は変更されないため、配列はハイライト表示されたままになります。

ポインタにどのような影響があるか疑問に思われるかもしれません。ハイライト表示されたポインタを別のオブジェクトに再割り当てすると、ハイライト表示されたオブジェクトの古い位置と新しい位置が異なるため、ハイライト表示が消えます。新しくポイントされたオブジェクトは WebAssembly メモリ内の任意の場所に存在し、以前のメモリ位置とはあまり関係がない可能性が高いため、新しいメモリ位置にジャンプするよりもハイライトを削除するほうが明確です。ポインタを再度ハイライト表示するには、[スコープ] ペインでポインタのメモリアイコンをクリックします。

まとめ

この記事では、C/C++ デバッグ用の Memory Inspector の改善点について説明しました。この新機能により、C/C++ アプリのメモリのデバッグが簡単になることを願っています。さらに改善するためのご提案がありましたら、バグを報告してお知らせください。

次のステップ

詳しくは、次をご覧ください。