🕌

デバッグのコツ

2024/05/16に公開

比較する。

現状、期待通りに動作しているものとの比較を行うことで、異常の原因を探す手法です。
プログラミングの他、Linuxでのサーバ構築で威力を発揮します。
ただし、人間が目で確認するだけでは細かい違いを調べるのは難しいため、差分検出ツールを使います。

windowsであればwinmerge、macであればCompareMergeがオススメ。
この二つはフォルダ比較という機能を備えていて、同じディレクトリ構造であれば数百個のファイルでも一括で差分を洗い出すことができます。


CompareMerge

目で間違い探しをしていては抜け漏れが発生します。間違えない人でも無駄に疲れます。
しかしこれなら差分に色が塗られて一目瞭然ですね!

また、2つのファイルの差分を見るだけであればvscodeやeclipseなどのエディタでも可能なので、必ず使い方を把握しておきましょう。

Linuxではdiffコマンドを使用する事で、差分を表示できます。
サーバ設定でvimを使用する場合は、cpコマンドで修正前のバックアップをとり、編集後のファイルとの差分をdiffコマンドで確認する事が基本になります。

なお、差分ツールは行単位でテキストを比較するため、ソースコードや設定ファイルを作る時はそのつもりで書くべきです。
コード本体だけでなく、コメント文も改行位置を揃えて不要な差分が出ないように心がけると後が楽です。

例えばSQLを書くとして、

	SELECT col1,col2,col3
		,col4col5,col6,col7,col8 FROM table1

みたいに1行にづらづらかくと、カラムが増減したり、変更があったときに、ツールで差分を調べづらくなります。

	SELECT
		col1
		,col2
		,col3
		,col4

みたいにすると、比較したとき綺麗に出ます。
カンマの位置も揃えましょう。

JavaやPHPなどでクラス名を列挙するような場合も同様です。

ステップ実行する。

実行中のプログラムを処理の途中で一時停止して、1行づつ実行する手法です。
これが出来る開発環境の場合は画面やログに情報を出力するよりもこちらを積極的に活用するべきです。
停止中の行の時点での変数の値や、その時点で関数を実行した場合の結果を確認できます。

画面に値を出力して確認するやり方より優れていることとしては、どのタイミングで変数の値が変わったかをリアルタイムに確認できる事です。
同じ事を画面出力でやろうとするなら、大量にlog.debugなどの関数を埋め込む必要があるし、どの行のログなのかわかるように記述する必要があります。

もし画面遷移を伴う処理の途中だったりすると、表示したデバッグ情報を見るタイミングがない場合もあります。

また、本番環境で出すログは設計書の通りに出さなければならず、適当に確認するためだけのログはコミット前に削除しなければいけません。
ステップ実行ならその手間も不要になります。

デバッグツールの代表的なものを紹介します。
javascript : Chromeブラウザに標準で付属しています。
JAVA : eclipseの機能として標準搭載。
PHP : Xdebugという外部ライブラリを使用。こちらは初期設定の難易度が非常に高いですが、導入できるとデバッグ効率が10倍になります。


Chromeでjavascriptをステップ実行
ブレイクポイントで止めているので画面のテキストまだ変わっていませんがでデバッグ画面には変数の中身やオブジェクトの値が一目で全てわかるようになっているのが分かりますね!

フレームワークが提供するデバッグツールを利用する。

特定のフレームワークで使える強力なデバッグツールが存在します。
フレームワークを使った開発ではそういったものが存在しないか調べるようにしておきましょう。

Laravel : Laravel debugbar
Vue.js : Vue.js dev tools
WordPress: AdminBar

など。

こちらはルーティング情報や用意しているコントローラ・bladeテンプレートのパス、Http Request・送信メール、などなど、画面からは確認がしづらい情報が瞬時に確認できる強力なツールです。
こちらもあるとないとではデバッグ効率が10倍改善します。

「参考サイト」 Laravel debugbarの記事
https://qiita.com/goto_smv/items/b7be0985029ab3d03217

「Vue.js devtools」
https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=ja

REPL環境・プレイグラウンドを利用する。

フレームワークによっては画面を表示しなくてもコマンドで即時に結果を確認できる便利ツールが使える場合があります。

REPLの代表的なものとしてはLaravel tinker。
.envやconfigの値を瞬時に確認できたり、関数を試しうちしたりできます。

プレイグラウンドは、練習用のコードを環境構築なしで試し書きできるサービスです。
「PHP プレイグラウンド」のように検索すれば見つかると思います。
.htaccessのリダイレクト設定ファイルや、WebAPIなどの外部連携を使ったテストは非常に難易度が高いですが、プレイグラウンドを使う事で文法レベルのミスは発見できたりします。


techplayground.com

環境構築なしで、ブラウザでコードを実験できます。
ちょっと、言語標準関数の動きを知りたいとき、本物のコードで適当なことを書くと、元に戻らなくなったり、データベースにおかしな値が入って困ることも。
プレイグランドならメチャクチャなコードを書いても何も問題は起きません。練習に最適!

https://www.tehplayground.com/

chatGPTに聞く。

とりあえず、困ったらChatGPTに聞きましょう。
ネイティブ言語程度の事ならかなり期待できます。
またLaravelのようなネットの記事が活発に更新されるフレームワークでもかなりいいヒントを出してくれます。
文法エラーくらいならデバッグしてくれる場合もあるので、とりあえず質問してみる事をお勧めします。

注意点として、chatGPTはマイナーなフレームワークの事や、利用者の少ないライブラリの事を質問すると平気で嘘をつきます。
経験の浅いアシスタントくらいに考えて付き合うのがいいと思います。

リアルタイムログを流す。

リアルタイムにログを流す方法です。
eclipseでJAVAの開発を行なっている人は何も設定しなくてもコンソールタブに流してくれているのを見ていると思います。

LINUXでは以下のコマンドを使うと同じように流すことができます。

tail -f /var/log/xxxx.log

tail -f までが固定。あとはリアルタイムに表示したいファイル名。
なんとテキストを開き直さなくても、書き込まれた瞬間画面に表示されます。
凄すぎますね。
使える環境なら活用しない手はありません。
※残念ながらwindowsのTeraTermだとうまくいかなかったりするようです。

エラーログを読む。

エラーログを読みましょう、と言われて読めれば苦労しません。
Hello worldを出力するだけの数行のコードなら出てきたエラーを丸ごとコピーしてググれば答えがでると思います。

しかし実際の業務のエラーログは目で追いきれないほどの大量のエラーが出ます。
こうなるとググろうにもどの行をコピーすればいいのかすらわかりません。

また、ExceptionやErrorといった文言がでている箇所がリンクになっているからと、その行へ飛んでみても以下のような記述が書いてあるだけかもしれません。

throw new \NullPointerException();

この行をコピーしてググったところで、「初期化前のオプジェクト変数を参照しようとした場合に発生します」とか、汎用的な話しか出てきません。だからどうしたというのでしょうか?

コツとしては、まず自分が編集しているファイル名が出力されているログを探します。
また、出向先など、所属している会社名が名前空間として使用されているパッケージのクラスを探します。

会社名がoracleとかmicrosoftとか、言語やフレームワークの制作者になっているコードは基本的に正しいため、そこをいくら探してもバグは見つかりません。
彼らも人間ですのでバグは生み出しますが、99%以上のプログラマーは遭遇することがないくらいにまれです。

例えば以下のjavascriptを実行してみると

function changeGreeting() {
    const greetingElement = document.getElementById('greeting');

    let greetingText = '';
    const user = {familyName: 'いとう', personalName: 'かいじ'};

    greetingText += "こんにちは、<br>";
    // 意図的に存在しないプロパティを参照してエラーを発生させる
    greetingText += user.familyName + user.personalName;
    greetingText += "さん!";

    user.nonExistentMethod(); // 存在しない関数呼び出し。

    // 要素のテキストを変更
    greetingElement.innerHTML = greetingText;
}

このようなエラーが出ます。

21:27:21.282 script.js:12 Uncaught TypeError: user.nonExistentMethod is not a function
    at changeGreeting (script.js:12:10)
    at HTMLButtonElement.onclick (index.html:10:40)

ここで「changeGreeting」は上のソースコードで自分が書いたものですね。
「HTMLButtonElement.onclick」は備え付けの標準関数です。
なので「changeGreeting」をとっかかりに検索して調べます。

ちなみに、at at at at ・・・ と続いて大量に出力されるのは、上に表示されているもの程新しく実行された行ということです。

ですから、上から順番に見ていって、そのプロジェクトのオリジナル関数が表示されたところを探します。

まとめ

デバッグは想像を絶する根性がいる作業です。
だからこそ、使えるツールやコマンドは使いこなして原因解明に繋げていきましょう。

株式会社ONE WEDGE

【Serverlessで世の中をもっと楽しく】 ONE WEDGEはServerlessシステム開発を中核技術としてWeb系システム開発、AWS/GCPを利用した業務システム・サービス開発、PWAを用いたモバイル開発、Alexaスキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
https://onewedge.co.jp

Discussion