Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

Ruby アソシエーション開発助成金2021最終報告: “debug.gem”の利用体験・開発効率の改善

取り組んだこと

 前後期で一貫して取り組んだことは Chrome でのデバッグ機能の実装を通した利用体験・フレームワーク開発による開発効率の改善である。

 前期では、Chrome を使ったデバッグを問題なくできるところを目指し、外部ツール(Chrome、VSCode) とのインターフェースをテストするフレームワーク導入に向けて実装していった。後期では、Chrome DevTools の強みを活かしたリッチな体験、 プロトタイプを通して得られた知見をもとによりよいフレームワーク開発による開発効率の向上を目標に取り組んだ。

Chrome のデバッグ機能について

 前期では本プロジェクト開始前にプロトタイプとして導入されていたものを改善する形で主にバグ修正や Chrome DevTools 上の UI として提供されている機能をサポートしていった。 バグ修正については手元の環境で Chrome を用いてデバッグ機能を行いそれを元に見つかったバグを中心に修正を行っていった。提供されている機能のサポートについては、例外で止まる機能やブレークポイントなしでスクリプト実行を強制する機能などを実装した。他にもデバッガー起動時に Chrome を自動で開いて Chrome DevTools とデバッガーの接続を自動で行う機能や Chrome のコンソールで式評価をした際に標準出力に出力されるものをターミナルではなくコンソール上に出力されるものを開発した。

 後期では Chrome DevTools 独自の機能を用いた利用体験向上への試みや前期で挙がってきた課題の解決などを中心に行なった。体験向上の取り組みとして、多次元配列の評価結果のプレビュー機能や配列を表に変換させたものの表示機能、コンソール上に画像を表示させる機能などを実装した。前期では必須機能のサポートが中心だった為、 Pull Request はほとんどマージされたが、後期では体験向上のアイディアの模索が中心だった為、プロトタイプを作り検証 -> クローズする Pull Request が多かった。前期で挙がってきた課題としてはデバッガー起動時にユーザーが毎回 ScreenCast 画面を閉じなければならないということがあった。Chrome DevTools Frontend のコードを参考にしながら課題解決に取り組んだ。

テストフレームワーク開発について

 現在、"debug.gem"では Chrome デベロッパーツール、VSCode と連携してデバッグが可能であり、それぞれ独自のプロトコルを用いている。Chrome デベロッパーツールでは CDP(Chrome DevTools Protocol)、VSCode では DAP(Debug Adapter Protocol)が用いられている。これらのプロトコルに従って外部ツールと通信する部分が "debug.gem" には実装されていてその部分のテストができるフレームワーク開発に本プロジェクトで取り組んだ。

 本プロジェクトでは2種類のテストフレームワークを開発した。一つ目は以下のようにプロトコルベースの低レベルのテストケースを書くもので、テストケース作成時にはジェネレーターを用いる。実際の動作により近いテストが実現できる、テストジェネレーターを使うので作る際の負担が少ないなどの長所を持っているが、プロトコルを知らない開発者はどのような動作をしているのか分かりにくい、ジェネレーターを作成する際に誤った動作をするともう一度やり直さなくてはならない、などの問題もある。前期ではこの形のテストフレームワーク開発に取り組み、DAP サーバーのプロトコルベースでのテストを行うことができるものをマージするところまで完了した。

def test_1639301775
      run_dap_scenario PROGRAM do
        [
          *INITIALIZE_DAP_MSGS,
          {
            seq: 7,
            type: "event",
            event: "stopped",
            body: {
              reason: "pause",
        ...

 二つ目は以下のように抽象化された高レベルのテストケースを書くものである。こちらはプロトコルを意識することなくテストケースを書くことができる、一つのメソッドで CDP と DAP 両方のテストができる、などの長所を持っている一方で、実際のやりとりを完全には再現できない、それぞれのプロトコルにおける細かい違いに応じたテストができない、などの短所も含んでいる。一つのメソッドで CDP と DAP 両方のテストを行うような設計にしている。後期ではこのテストフレームワークの開発に取り組み、マージするところまで完了した。

 def test_break1
      run_protocol_scenario PROGRAM do
        req_add_breakpoints 5
        req_add_breakpoints 8
        req_continue
      ...

その他

 その他の取り組みとして、デバッガーの統合テストをするテストフレームワークやテストケースジェネレーターの改善・バグ修正などを行った。具体的にはデバッギーに出力 されるものが多いとタイムアウトになってしまうテストフレームワークのバグの修正や、テストケースジェネレーターではデバッグをしたソースコードが出力先のファイルに含まれていないかチェックしてなければ出力できるようになった。

Chrome を用いたデバッグ機能の実装

バグ修正

デバッガー終了時にエラーが出るバグ修正関連
  • Fix #329 by getting frame's path correctly
  • Continue the debuggee after detaching to Chrome
    • 繋いでいる状態で Chrome DevTools ページを閉じると、エラーが出て終了するバグ修正の Pull Request 。閉じた時に Chrome は Opcode に8(8は接続を閉じるという意味)を入れて送ってくるが、それに対応できていないのが原因だった。
    • この時、対応方法は以下の3通りだった。
      • デバッギーをexitで終了する。
      • デバッギーに continue コマンドを実行させる。
      • rdbg REPL に戻る。
    • gdb や リモートデバッグに合わせるのが良いと考え、デバッギーに continue コマンドを実行させる方法を採用した。
  • Fix bug that error occurs after detaching to Chrome
    • デタッチ後にエラーが出て終了するバグ修正の Pull Request 。スレッド終了後に readline メソッドが呼ばれてエラーが出てしまうということが原因だった。
Breakpoint 関連
ブレークポイントをセット後、それらを全て削除したがまだブレークポイントが残っているバグ修正に関する Pull Request 群
直接的にユーザーに影響を及ぼすものではないバグの修正に関するもの
マウスオーバーをした時に表示するオブジェクトに関するもの。当時はマウスオーバーされたオブジェクトを eval して返ってきた値を返すようにしていたが、グローバル変数など値が変わってしまうという問題があった。
コンスタント変数にマウスオーバーをした時にデバッガーが落ちるバグを修正関連
Chromeが自動で起動した後、 Chrome DevTools ページに飛ばないというバグの修正関連。1.4.0リリース直前にこのバグを発見した。
  • Fix bug that debugger sometimes doesn't open Chrome automatically
    • ワークアラウンドとして出したPull Request。Chrome からのレスポンスの取得回数を増やした結果、筆者の環境でバグが修正されたように見えたことを根拠とした。
  • Correct changes in #445
    • ワークアラウンドのPull Requestの変更を書き直したもの。Page Domain を有効にする"Page.enable"メソッドを送信することで指定したURLへと移動する"Page.navigate"メソッドを確実に実行できるようにした。
  • Send current page's frame id when debugger sends 'Page.navigate' method
    • "Page.navigate"メソッドの中にFrame Id をパラメーターとして送ることにしたもの。
    • 感覚ではあるが、上記の Correct changes in #445 でバグはだいぶ改善された。しかし、デバッガーを使い始める1回目にバグがまだ観測された(一度終了して再起動するとこのバグはほとんど観測されない)。ドキュメントを見たところ、"Page.navigate"メソッドを送る際に Frame Id のフィールドをパラメーターとして送らない場合トップフレームを Frame Id として取得するという内容があったの遷移するフレームがトップフレームではなかった場合失敗するのではないかという推測を立て Fame Id をパラメーターとして送るようにした。その結果、現在に至るまでバグは筆者の環境で観測されていないので、バグを修正することができたと判断している。
意図したオブジェクトが表示されないバグの修正関連
その他

リファクタリング

機能開発・改善

起動時に Chrome を自動で開いて Chrome DevTools のページに飛ぶ機能
  • Open Chrome automatically

    • 当初、以下の機能が実装されていたが断念した。
      • ポートなどの情報をファイルとして持たせる
        • なんらかの方法でそのファイルを書き換えることができた場合意図しない接続先にデバッガーが接続しようとしてしまうというセキュリティリスクがあり断念。
      • DevToolsActivePort ファイルからポートなどの情報を読み取る
        • 実験してみたところ、ポート番号が変わっても DevToolsActivePort ファイルが更新されなかったり、 DevToolsActivePort ファイルを用いたいくつかのプロジェクトでうまく動かないといった Issue が観測されたので断念。
  • Do not open Chrome automatically if "CONFIG[:chrome_path]" is an empty string

    • Chrome を自動で開きたくないケースがあると考え、環境変数で設定できるようにした。
Chrome のコンソールの式評価をした際に出力された標準出力をターミナルではなくコンソール状に出力するようにしたもの。
例外で止まる機能に関する Pull Request 群
ターゲットフレームにある変数を以下のように表示できるようにする為のPull Request 群。

Screen Shot 2021-11-27 at 10 47 26 PM

pp メソッドを拡張して Chrome のコンソールを使って表示できるようにしたもの。
エラーメッセージの改善に関する Pull Request 群
その他

Screen Shot 2021-11-27 at 10 47 26 PM

ドキュメント修正

CDP サーバー・DAP サーバーのテストフレームワーク開発

プロトコルベースのフレームワーク

CDP サーバー
  • Add tests for server_cdp.rb

    • 実験で作ったフレームワーク。イメージができたのでクローズした。
  • [WIP] 1. Add the test framework for CDP

    • テストフレームワークの1パターン目。以下のようにserver_cdp.rbと同じように書くような形にした。

    • def test_sample
            run_cdp_scenario PROGRAM do
              res1 = cdp_request 'Debugger.setBreakpointByUrl', condition: '', lineNumber: 7, url: "http://debuggee#{temp_file_path}"
              res2 = cdp_request 'Debugger.setBreakpointByUrl', condition: '', lineNumber: 5, url: "http://debuggee#{temp_file_path}"
              res3 = cdp_request 'Debugger.setBreakpointByUrl', condition: '', lineNumber: 3, url: "http://debuggee#{temp_file_path}"
              ...
    • メリット

      • 最も柔軟に書くことができる。
      • 行数が少なくて済む
    • デメリット

      • 可読性が低く CDP をよく知っている人にしか分からないものになっている。
  • [WIP] 2. Add the test framework for CDP

    • テストフレームワークの2パターン目。以下のようにJSON 形式でリクエスト・レスポンスを書くような形にした。テストジェネレーターも併せて作った。

    • def test_sample
            run_cdp_scenario RROGRAM do
              cdp_req({
                "id": 1,
                "method": "Page.getResourceTree",
                "params": {
                }
              })
              ...
    • メリット

      • パターン1と比べ可読性が高い。
    • デメリット

      • 毎回メソッドを通して呼んでいるので余計なカッコがついたりして可読性が下がる。
      • 行数が長くなる。
  • 3. Add the test framework for CDP

    • テストフレームワークの3パターン目。以下のようにプロトコルをそのまま書くような形にした。テストジェネレーターも併せて作った。

    • def test_sample
            run_cdp_scenario PROGRAM do
              {
                "id": 1,
                "type": "request"
                "method": "Page.getResourceTree",
                "params": {
              }
              ...
      • メリット

        • DAP と同じ形式なので読みやすい。
      • デメリット

        • 返り値を使ってリクエストを送ることができないなど柔軟性が低い。
          • 行数が長くなる。
DAP サーバー
テストケース作成

抽象化されたフレームワーク

その他

バグ修正

Debuggeeの出力が多いときテストフレームワークが動かないというバグを修正する為のPull Request 群
テストを実行したときに表示される警告を消す為のPull Request群
その他
  • Fix test
    • CIが落ちていたのでその修正。assert_finish メソッドをAPIから削除後、まだ消し忘れが残っていたことが原因だった。

リファクタリング

使われていない・意味のないコードの削除。
Rubocop で検出されたメッセージを参考に修正したもの。
その他
  • Use snake case instead of camel case
    • キャメルケースで変数が定義されていたのでスネークケースに変数を書き直したもの。VS Code のフィールド名であることを強調する為意図的にキャメルケースで書かれていたものだった為、マージされなかった。
  • Refactor the logic by removing an intermediate variable
    • 中間変数を削除するもの。テストが通らずクローズした。

機能開発・改善

ドキュメント修正

プロジェクトの評価

 本プロジェクト開始時のゴールに対する進捗は以下のようになっている。概ね、目標を達成することができた。

  • Chrome でのデバッグ機能を実現する CDP サーバーの開発
    • vscode-rdbg と遜色ない機能を持ったデバッグができる。
    • Chrome DevTools 上の UI として提供されている機能のサポートをする。
    • Chrome DevTools の強みを活かしたリッチな開発体験向上
  • CDPサーバー と DAPサーバー のテストフレームワークの開発
    • プロトコルベースでのテストフレームワーク
      • CDP サーバー
        • テストフレームワークがマージされる。
        • テストフレームワークが安定して動く。
      • DAP サーバー
        • テストフレームワークがマージされる。
        • テストフレームワークが安定して動く。
    • 抽象化されたテストフレームワーク
      • テストフレームワークがマージされる。
      • テストフレームワークが安定して動く。

本プロジェクトの社会的インパクト

 本プロジェクトにおける社会的インパクトは2つの点から言うことができる。

 1点目は利用体験の向上である。デバッガー起動時に Chrome DevTools の画面を自動で開く機能や変数のクラス情報を表示する機能などの VS Code を用いたデバッグと同様の機能を実現することができた。 Chrome を用いた JavaScript のデバッグに慣れ親しんでいるユーザーが “debug.gem” を使いやすくなった。

 2点目は“debug.gem” の開発効率の向上である。テストフレームワークが開発されたことで、 CDP や DAP サーバーの開発は変更が加わったとしてもバグが出にくくなり、レビュアーのレビューの負担を減らすことができた。

 また、Chrome DevTools Frontend に対応したデバッガーの事例は少なくノウハウもあまり世に出回っていない。今後 Chrome DevTools Frontend に対応したデバッガーを開発する開発者にとって “debug.gem” を参考になる代表例の一つということができるだろう。

謝辞

 本プロジェクトを進めるに当たって笹田耕一さんには Pull Request のレビューを始め沢山の助言をいただきました。笹田さんのレビューからはいつも気付かない視点を与えてくださり自分一人ではここまで来ることはできなかったと思っています。本当にありがとうございました。

 また、このような素晴らしい機会を与えてくださった Ruby アソシエーション関係者の皆様に感謝します。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment