Memory Inspector のご紹介

Kim-Anh Tran
Kim-Anh Tran

この記事では、Chrome 91 で導入された Memory Inspector について説明します。ArrayBuffer、TypedArray、DataView、Wasm メモリを検査できます。

はじめに

ArrayBuffer のデータを理解したいと思ったことはありませんか?Memory Inspector が登場する前は、DevTools では ArrayBuffers について限られた分析情報しか得られませんでした。デバッグ セッション中の [Scope] ビューからの検査は配列バッファ内の 1 つの値のリストに限られていたため、データ全体を把握することが困難でした。たとえば、下の例では、[Scope] ビューに配列の展開可能な範囲としてバッファが表示されます。

DevTools のスコープビュー

バッファ内の特定の範囲に移動するのは困難であり、最終的にそのインデックスに到達するまでにユーザーが下にスクロールする必要があった。しかし、たとえ掲載順位への移動は簡単であっても、このように実際に値をinspectingするのは面倒です。これらの数値の意味を理解するのは困難です。特に、32 ビット整数など、単一バイトとして解釈すべきでない場合はどうなるでしょうか。

Memory Inspector を使用して値を検査する

Memory Inspector

Chrome 91 には、配列バッファを検査するツールである Memory Inspector が導入されています。バイナリデータを表示するメモリ インスペクション ツールをご覧になったことがあるかもしれません。バイナリ コンテンツをそのアドレスとともにグリッド形式で表示し、基礎となる値をさまざまな方法で解釈できるメモリ インスペクション ツールをご覧になったことがあるかもしれません。これが Memory Inspector で実現する機能です。Memory Inspector を使用すると、コンテンツを表示したり、コンテンツを操作したり、現在の値の解釈に使用するタイプを選択したりできるようになります。バイトのすぐ横に ASCII 値が表示され、ユーザーが別のエンディアンを選択できるようになっています。Memory Inspector の実際の動作は次のとおりです。

お試しになる場合は、Memory Inspector を開いて配列バッファ(または TypedArray、DataView、Wasm Memory)を表示する方法と、その使用方法について詳しくは、Memory Inspector のドキュメントをご覧ください。こちらのサンプル(JS、Wasm、C++ 用)をお試しください。

Memory Inspector の設計

このパートでは、Memory Inspector が Web Components を使用してどのように設計されているかを確認し、設計目標の一つとその実装方法を示します。詳細については、Memory Inspector の設計に関するドキュメントをご覧ください。

Jack が、Web Components を使用して UI コンポーネントを作成する方法に関する社内ガイドを公開している、ウェブ コンポーネントへの移行に関するブログ投稿をご覧になったことがあるかもしれません。Web Components への移行は Google の Memory Inspector での作業と重なり、Google は新しいシステムを試すことにしました。次の図は、Memory Inspector を作成するために構築したコンポーネントを示しています(社内では Linear Memory Inspector と呼んでいます)。

ウェブ コンポーネント

LinearMemoryInspector コンポーネントは、Memory Inspector のすべての要素を構成するサブコンポーネントを組み合わせた親コンポーネントです。基本的には Uint8Arrayaddress を受け取り、いずれかが変更されるたびにデータを子に伝播し、再レンダリングをトリガーします。LinearMemoryInspector 自体は、次の 3 つのサブコンポーネントをレンダリングします。

  1. LinearMemoryViewer(値を表示)、
  2. LinearMemoryNavigator(ナビゲーションを許可する)
  3. LinearMemoryValueInterpreter(基になるデータのさまざまな型解釈を示します)

後者はそれ自体が親コンポーネントであり、ValueInterpreterDisplay(値を表示する)と ValueInterpreterSettings(ディスプレイに表示する型の選択)をレンダリングします。

各コンポーネントは、必要に応じてコンポーネントを再利用できるように、UI の小さなコンポーネントを 1 つだけ表現するように設計されています。コンポーネントに新しいデータが設定されるたびに再レンダリングがトリガーされ、コンポーネントに設定されたデータに変更が反映されます。以下に、コンポーネントを使用したワークフローの一例を示します。ここでは、ユーザーがアドレスバーの住所を変更すると、新しいデータ(この場合は表示する住所)が設定され、更新がトリガーされます。

コンポーネントの図

LinearMemoryInspector は自身を LinearMemoryNavigator のリスナーとして追加します。addressChanged 関数は、address-changed イベントでトリガーされます。ユーザーが住所入力を編集するとすぐに、addressChanged 関数が呼び出されるように前述のイベントが送信されます。この関数は住所を内部に保存し、data(address, ..) セッターを使用してサブコンポーネントを更新するようになりました。サブコンポーネントは、その住所を内部で保存し、ビューを再レンダリングして、その特定の住所のコンテンツを表示します。

設計の目標: バッファサイズから独立したパフォーマンスとメモリ消費を実現する

Memory Inspector の設計時に念頭に置いた点の一つは、Memory Inspector のパフォーマンスをバッファサイズに依存させないようにすることでした。

前の部分で説明したように、LinearMemoryInspector コンポーネントは UInt8Array を受け取って値をレンダリングします。同時に、Memory Inspector はデータの一部のみを表示するため、Memory Inspector はデータ全体を保持する必要がないようにしたいと考えました(たとえば、Wasm Memory は 4 GB の大きさになることがあり、Memory Inspector に 4 GB を保存するのは避けたいものです)。

そこで、Memory Inspector の速度とメモリ消費量を、表示されている実際のバッファから切り離すために、LinearMemoryInspector コンポーネントが元のバッファのサブ範囲のみを保持するようにします。

これを機能させるには、LinearMemoryInspectormemoryOffsetouterMemoryLength という 2 つの引数を追加で取る必要があります。memoryOffset は、渡された Uint8Array の開始点を示すオフセットで、正しいデータアドレスをレンダリングするために必要です。outerMemoryLength は元のバッファの長さで、表示可能な範囲を把握するために必要です。

buffer

この情報により、実際にすべてのデータを配置しなくても、以前と同じビュー(address 周辺のコンテンツ)をレンダリングできます。それでは、異なる範囲にある別のアドレスがリクエストされた場合、どのように対処すればよいでしょうか。その場合、LinearMemoryInspectorRequestMemoryEvent をトリガーし、保持されている現在の範囲を更新します。以下に例を示します。

イベント トリガーのフロー図

この例では、ユーザーがメモリページに移動すると(Memory Inspector はデータのチャンクを表示するためにページングを使用しています)、これにより PageNavigationEvent がトリガーされ、それによって RequestMemoryEvent がトリガーされます。このイベントにより新しい範囲の取得が開始され、その後、データを設定して LinearMemoryInspector コンポーネントに伝播されます。そのため、新たに取得されたデータを表示しています。

ところで、実は知っていましたか?Wasm と C/C++ コードでメモリを検査することもできます

Memory Inspector は、JavaScript の ArrayBuffers で利用できるだけでなく、C/C++ の参照/ポインタが指す Wasm メモリとメモリの検査にも使用できます(DWARF 拡張機能を使用します。まだ試していない場合はお試しください)。最新のツールを使用して WebAssembly をデバッグするをご覧ください。ウェブ上の C++ のネイティブ デバッグを行う Memory Inspector の概要を以下に示します。

C++ でメモリを検査する

おわりに

この記事では、Memory Inspector を紹介し、その設計の一例も紹介しました。ArrayBuffer の状況を理解するうえで Memory Inspector がお役に立てば幸いです。もしよろしければ、ぜひお知らせください。また、バグを報告してください。

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

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

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

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

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