2014年5月9日金曜日

matlab: ここはどこ? dbstack

matlab の m ファイルの実行中に keyboard 命令で止まったとき,今どの m ファイル中の何行目で止まっているかを知るには,dbstack コマンドを実行すればよいという話.

デバッグのお供 keyboard 命令はとても便利なので,なじみのない人も続きを読んでいただきたい.では,行ってみよう.

関数やスクリプトのコードを書く際, .m ファイル中で
keyboard
と書いておくと,実行時にそこで一旦プログラムがストップし,その箇所の workspace にいる状態で,
K>>
という表示のプロンプトが現れる.すると,コードの「中」にいる状態で,変数を確認したり,何らかの操作を行ったりできる.

特にデバッグ作業において,エラー発生時に状況を解明するため,try-catch 構文と組み合わせた次のような使い方が便利である.

try
    % 処理A
catch ME
    keyboard
end

すると,処理Aの途中でエラーが出ると,その時の状態でプロンプト K>> を出して止まってくれる.つまり,関数内に keyboard 命令をおいた場合,関数ワークスペース内でプロンプトを出してくれる.このプロンプト上で,何が問題だったか解明していけるわけだ.また,現状についての情報の一部が,catch のところで ME と指定しているので,ME という変数(ここではオブジェクト.構造体のようなもの)に格納される.
※ただし,最近のバージョンではエラーハンドリングの方法が変わってた気もするのでご注意.try - catch 構文に関する何かだったような.

何かと便利な keyboard 命令であるが,困ることもある.複数の .m ファイルからなるプログラム群を使う場合,画面に K>> と出ても,どの .m ファイルの何行目の keyboard で止まっているのかわからないことがあるのだ.いちいち m ファイルエディタで「止まってます」マークをたどるのも大変な手間になる.

対策の一つは,前述の try-catch 構文の中で,catch 変数名 と書いておくことだ.例えば上記のように catch ME とすると,ME という変数が MException object に設定される.そこで,K>> となった後に,ME.stack という構造体配列を見ると,どの .m ファイルの何行目でエラーが出たかをたどることができる.だが,ME のフィールドを辿って表示していくのはやや面倒だ.また,実行した .m ファイルが自分で書いたものでない場合,どの変数が MException object になっているかは自明ではなく,それを知るにはいちいち whos を実行する必要があるし,そもそも変数名指定なしの catch が使われている場合,MException object が用意されていないかもしれない(近い情報は lasterror 命令で得られるかも).

MException object を用意していなくても,K>> が一体どこにいるのかを一発で教えてくれる命令がある.それが dbstack だ.

scr9.m:
try
    scr10
catch ME
    keyboard
end

scr10.m:
a = zeros(1000*1000*1000, 1000); % メモリ確保できないほど大きい変数.確実にエラー

という .m スクリプトを作って実行すると,現代の普通の PC では scr10 でエラーが発生し,
>> scr9
K>>
となる.ここで dbstack を実行すると,
K>> dbstack
> In scr9 at 4
K>> 
と返ってきて,scr9.m の 4 行目にいることがわかる(keyboard の行.end はまだ処理されていないと思われる).
なお,出力引数付きで実行すると,
K>> [ST,I] = dbstack
ST =
    file: 'scr9.m'
    name: 'scr9'
    line: 4
I =
    1
となって,第1出力引数(ここではST)にはる.I には current workspace index が入る.さらに,-completenames という引数を与えて実行すると,
K>> [ST,I] = dbstack('-completenames')
ST =
    file: 'c:\work\scr9.m'
    name: 'scr9'
    line: 4
I =
    1
のように,mファイルのフルパスを返してくれる.これは,MException object の stack フィールドに入る情報と同一のようだ.

以上のように,keyboard 命令を利用した m ファイルのデバッグにおいては,
  • 予めコード内に try-catch 構文が仕込めるなら,catch ME のようにして MException object を用意しておく.
  • keyboard 命令で m ファイルの実行が止まったら,dbstack と打てば今どこかわかる.mファイルのフルパスは dbstack('-completenames') でわかる.
という 2 つの小技を定番として推しておきたい.dbstack の更なる詳細は,例によって matlab のヘルプ文書を参照されたい.

0 件のコメント:

コメントを投稿