hkoba blog

プログラマーです。プログラミング言語ミーハーです。ツッコミ歓迎です。よろしくどうぞ(能代口調)

perldebugger (perl -d) を楽に使うコツ的な話

この記事は Perl Advent Calendar 2018 の 12/4 の記事です

Perl Debugger (perl -d) を効率的に使うコツ的な話を書きます。 perl の標準デバッガなんて役に立たないと思っている人のための記事です。

TL;DR step/next は最低限に。break したい個所が予め決まっているなら、デバッグ対象コードにダミー関数の呼び出しを加える手も有る。

Perl Debugger とは

Perl Debuggerperl に標準で添付されたデバッガで、perl 起動時に -d オプションを渡すことで利用できます。使い方は一般的なデバッガとよく似ていて、

  • l 関数名 で関数のソースを表示
  • b 行番号b 関数名 で breakpoint を設定
  • c で実行を継続, s で関数の中までステップ実行, n で次の行まで実行

といった感じです。Term::ReadLine::Gnu などをインストールすると、lb で関数名のタブ補完も効くようになります。 (参考)

h で出るヘルプ画面には沢山コマンドが並びますが、 使うコマンドはそう多くはありません。よく使うコマンドに印をつけたので 参考にどうぞ。

f:id:hkoba501:20181203212405p:plain
perl debugger の help 画面(hkoba のよく使うものに青印をつけてあります)

動的にロードされるコードに breakpoint を仕込むには

そんな Perl Debugger ですが、いざバグ取りに使おうとした時に 肝心のデバッグしたい関数に breakpoint を置けなくて困ることがあります。 この問題は、Perl がその関数が定義されているモジュールをまだ読み込んでいない時に起こります。

(一応、 b load FILE コマンドを使えば、 require でファイルがロードされる所に breakpoint を仕込むことが出来る、らしいのですが、 FILE のパスの扱いで私は失敗してばかり…なので使わなくなってしまいました)

特に Plack や Mojolicious のようなフレームワークを用いた開発で、動的に読み込まれる .psgi ファイルやモジュールの中のコードをデバッガで調べたい場合に、 step/next を打ち続けて絶望する初心者も散見されます。

正攻法で行く場合

このように動的にロードされるコードをデバッグする場合、正攻法では動的ロードを行う側のコードの挙動を理解して、 step/next を打つ回数を減らすことが大事です。

手順としては、

  • そのフレームワークがモジュールの読み込みを行う関数を探し、
  • そのローダー関数に最初の breakpoint を置いて実行、
  • ローダー関数に達したら、おもむろに r で return して自分のコードを実際に読み込ませる
  • その後、自分のコードに breakpoint を置く

という流れになるでしょう。以下の例は、Mojolicious から呼ばれる startup をデバッグする流れです。

逆転の発想:breakpointを置くためだけのダミー関数を使う

ただ、上記のやり方は個々のフレームワークの内部実装に対するある程度の理解が必要になるため、 準備に時間がかかりますし、正直面倒です。

それよりも、もし breakpoint を仕込みたい個所が予め決まっていて、 かつそのコードを書き換えることが可能なケースであれば、 breakpoint を置くためだけの(中身のない)ダミー関数を使う方法が有効です。

すなわち、

  • ダミー関数の名前を決める(例えば main::breakpoint)
  • break したい個所に、そのダミー関数への呼び出しを書き入れる(ただし注意有り)
 # This method will run once at server start
 sub startup {
   my $self = shift;
+
+  main::breakpoint();

   # Load configuration from hash returned by "my_app.conf"
   my $config = $self->plugin('Config');
  • デバッガを起動
  • x sub main::breakpoint {} などしてダミー関数を定義
    • (予めプログラム内で定義しておいても良い)
  • b main::breakpoint でダミー関数に breakpoint を設定
  • c でダミー関数まで飛ぶ
  • r で該当コードに移る

この方法なら実質 c 一発で該当のダミー関数まで到達出来るため、途中のフレームワークの実装に 関する知識は実質的に不要になります。

注意点:ダミー関数を呼んだままでは本番でコケる

ただし、このままでは(ダミー関数は本番には存在しないので)、 本番で動かないコードになってしまいます。ですので、このダミー関数の存在の有無を perl$pkg->can($methodName) で確認してから呼ぶようにすると良いでしょう。

  if (my $sub = main->can('breakpoint')) {
      $sub->();
  }

手順としては、こんな感じです>

なお、自作のコードの場合は、ダミー関数の定義を正式なコードに予め含めておくほうが楽です。 その場合は main::breakpoint ではなく、もっと別の名前空間に入れる方が衝突のリスクが少なくて安全です。breakpoint だけを入れたモジュール XXX::Breakpoint みたいなものを用意しておく手も有るでしょう。

終わりに

Perl Debugger で今どきのコードをデバッグする時に困りがちな『 breakpoint を置くのが大変』問題を、breakpoint 用のダミー関数で軽減する方法について解説しました。

明日は @ytnobody さんです。