みなさんはデバッガを使っていますか?

プログラマは新しくプログラムを書くよりも書かれたプログラムを見ている時間の方が長く、書かれたプログラムの動作を効率よく把握できるかは大事です。

仕様書や引き継ぎからはもちろんですが、実際にプログラムを動作させながらの確認も、内容を把握するために有効な手段の一つですね。

バグが発生した時の挙動の調査はもちろん、新しく自身が配属されたプロジェクトや機能を改修する前の動作把握でも、やはり、実際に動作をさせて確かめてみることは有効になってきます。

プログラマとして新しくプロジェクトに関わり既存のプログラムの動作を把握したい時、私の場合は自由にそのプログラムを動作させられる環境が無いかをまず確かめます。
自分が占有できる環境、例えば自分のPCに開発環境を構築できる手順が既にまとまっているならその手順を活用します。

効率的に動作を把握するため、次にデバッガが使えないかどうかを確かめます。

開発環境によってはセットアップするまでもなくデバッガを利用できますが、サーバサイドアプリケーションの開発環境なんかは大抵一手間かかります。
デバッガのセットアップ手順もプロジェクトごとにまとまっていれば良いですが、整理の手間やIDEへの手順の依存など、さまざま事情がありまとまっていないことも多いのでは無いでしょうか。

複雑なビジネスロジックが集約しがちなサーバサイドこそデバッガを利用することが有効になってきます。

人によっては初歩的な内容かもしれませんが、自分の体感としては、「案外デバッガを活用している人が多くない」ように感じています。

そんな人のために記事を書いてみました。

 

デバッガで出来ること

デバッガを利用する事で、プログラムが実行される時の挙動を何度も再試行を繰り返さずに確かめることができます。
簡素かつありがちな動作の把握方法として、プログラムの要所で画面上やログに意図的にテキストを出力して動作を確かめる、いわゆるプリントデバッグがありますが、試行回数が多くなる場合もあり、試行に時間がかかる処理だと掛け算で把握に時間がかかってしまいます。
ソースコードを追って把握する上で、デバッガを使う場合、そうでない場合でソースコードの複雑さによっては把握にかかる時間は大きな差になると考えられます。
ソースコードをシンプルに保ち、把握しやすくしておくことは素晴らしいですが、少々複雑にならざるを得ない状況も多々あるのではないでしょうか。

試行を繰り返す回数が少なくて済むように、デバッガでは下記のような確認が簡単にできます。

・確かめたい条件の元でプログラムが実行された時に、内部でどの処理が実行され、どの処理が実行されないか。

・確かめたい特定の時点でそれぞれの変数にどんな値が保持されているか。

・確かめたい特定の時点でのコールスタックの内容。

コールスタックとは、関数の呼び出しの経緯と、経緯上でそれぞれの関数が実行されている時点での変数の内容をメモリ上に記録している箇所です。
デバッガを使えば一度の実行でしっかり確認できるので、動作を確かめるためにログを書き出したり、画面上に書き出したりするよりは確認がスムーズです。

以下、具体的な内容になります。 言語や開発環境に関わらず、デバッガで出来る事や、そのインタフェースは似通っています。
ここであげるような事は大体どのデバッガでも当てはまるのでは無いでしょうか。

 

ステップ実行

動作を確認したい時点で停止して、一行ずつ処理を確かめます。
ブレークポイントを仕掛けてプログラムを実行すると停止します。
今回の場合はWebアプリケーションなので、ブラウザからページにアクセスするとプログラムが実行されます。

・続行 -> 次のブレークポイントまで実行。ブレークポイントがなければ以降の処理を全て実行。

・ステップオーバー -> 関数の呼び出しがある箇所でもない場所でもステップを実行して次のステップに移動。

・ステップイン -> 関数の呼び出しがある箇所なら中にジャンプ。ない場所ならステップを実行して次のステップに移動。

・ステップアウト -> 関数の呼び出し元まで処理を実行。途中にブレークポイントがあれば最初のブレークポイントまで実行。

試しにステップインで関数の中にジャンプしてみます。

make()の中にジャンプできました。

 

変数の確認

ステップ実行中に変数の内容が確認できます。
$thisのメンバー変数を展開してみています。

 

式の試行と結果の確認

ステップ実行中に任意の式が実行された場合にどんな結果が返るのかが確認できます。

 

コールスタックのジャンプ

ステップインで潜っていくうちに、呼び出し元がどこだったか気になることもあるかと思います。

また、ブレークポイントで止まったところから呼び出し元を遡りたい場合もあるのではないでしょうか。

コールスタックを遡って確認することができます。ジャンプして追いかけます。

追いかけた先での変数の確認や、式の試行もできます。

このような機能を活用すると、挙動を把握するためのプログラムの試行は少なくて済みますね!

VSCode、PHP、仮想マシンでの開発環境でのセットアップ例

上の例でも用いていますが、VSCodeは今やサーバサイドやフロントエンドのプログラミング環境としてはシェア率が高くなってきています。

サーバサイドのありがちな開発環境の構成としては、ローカルマシン上に仮想環境を立て、本番環境の構成に似せて構築し、ホストマシンからソースコードを同期させてプログラムを書きながら動作確認をするというスタイルではないでしょうか。

ここではPHPの開発の場合での設定例を示します。

PHP * VSCode * Dockerなどの仮想環境を想定しています。

PHPでデバッガを利用する場合はXdebugというPHPのエクステンションをインストールし、有効化しておく必要があります。

https://xdebug.org

VSCodeには標準でデバッガ機能が搭載されていますが、利用する言語や開発環境に合わせてそれらに特化したエクステンションを追加する必要があります。
今回はPHP向けのエクステンションPHP Debugを利用します。

https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug

公式のVSCodeでのリモートデバッグセットアップ手順はこちら。

https://code.visualstudio.com/docs/editor/debugging#_launch-configurations

大雑把な手順としては下記のようになります。

  1. ローカル開発環境にPHPのエクステンションXdebugをセットアップ

  2. VSCodeにPHP Debugエクステンションをセットアップ

  3. デバッガを起動しテストを開始

ローカル開発環境にXdebugをインストールします。仮想マシン上で動作させている場合は仮想マシンにログインしましょう。
CentOSなどRedhat互換環境の場合はyumからインストールできます。

$ sudo yum install php-pecl-xdebug

設定ファイルのphp.iniでXdebugの設定を有効化します。
Xdebugは執筆時点でメジャーリリースの3系が最新です。以前の2系とは設定値が異なるので注意が必要です。
インストールしているXdebugのバージョンはPHPをバージョン確認オプション付きで実行することでPHPのバージョンと合わせて確認ができます。

$ php -v
PHP 7.4.22 (cli) (built: Jul 29 2021 17:27:21) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Xdebug v3.0.4, Copyright (c) 2002-2021, by Derick Rethans
with Zend OPcache v7.4.22, Copyright (c), by Zend Technologies

Xdebug 3.0.4なので、3系を利用していることが判ります。

3系の場合、php.iniでの設定内容は下記のようになります。

https://xdebug.org/docs/install

; debugがステップ実行を行うため、developがエラーに関する詳細を出力するための設定値です。
xdebug.mode = debug, develop

xdebug.start_with_request = yes

; ホスト側のIP
; 仮想マシンにDockerを利用する場合、host.docker.internalはホストマシンのIPを解決してくれます。
; 仮想マシンがDocker以外の場合は適宜IPを調べて設定してください。
xdebug.client_host = host.docker.internal

xdebug.log = /tmp/xdebug.log

2系の場合、php.iniでの設定内容は下記のようになります。

https://2.xdebug.org/docs/remote

(こちらのリンクは2022年以降は閲覧出来なくなるようです。積極的にバージョン3を利用していかないといけないかも知れません。)

xdebug.remote_enable = 1
xdebug.remote_autostart = 1
xdebug.remote_host = host.docker.internal

; Xdebugとホストが通信する時のポート番号を指定します。デフォルト値は3系は9003、2系は9000です。デフォルトでも良いですが、ここでは3系に合わせます。
xdebug.remote_port = 9003
xdebug.remote_log = /tmp/xdebug.log

VSCodeにPHP Debugエクステンションをセットアップします。Felix BeckerのPHP Debugをインストールしましょう。

左端のデバッグアイコンから create a launch.json file を、Select environment からはPHPを選択しましょう。

デバッガの起動設定を記述するファイルlaunch.jsonが作成され、PHPでの設定例が書き出されます。

ただし、今回は設定例は活用しません。launch.jsonの中身を下記の内容で書き換えましょう。
pathMappingsでホストマシン側と仮想マシン側のルート同士を適宜紐づけてください。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for Xdebug",
            "type": "php",
            "request": "launch",
            "port": 9003,
            "pathMappings": {
                "/path/to/yourProjectRoot/": "${workspaceFolder}/"
            }
        }
    ]
}

 

デバッガを起動しテストを開始

ブレークポイントを仕掛け(停止したい箇所の行番号の左をクリック)、デバッグのビューから▶︎デバッガを起動し、ブレークポイントを仕掛けた箇所の処理が実行されるページにアクセスしましょう。
ブレークポイントでストップすれば成功です。上で挙げているようなデバッグができます。

うまく設定はできたでしょうか。

このようにデバッガを設定し活用することで、ソースコードを追いかける労力を小さく抑えることができます。

既存のソースコードがどのようになっているか、どのように改修する余地があるのか、チームでプロジェクトを進めているとメンバー間で認識に差が生じてしまうこともあるのですが、このような手順をチーム内で共有できていれば、そういった認識の差が広がるのを抑えられ、チームとしての連携も少しは取りやすくなるのではないでしょうか。

それでは次回のブログもお楽しみに!