liboctave のエラーハンドラを自分で定義して,デバッグを効率化
liboctave を使っていると,例えば要素数の異なる列ベクトルを足そうとしたときに
fatal: operator +: nonconformant arguments (op1 len: 2, op2 len: 5)
というエラーが発生し,プログラムが正常終了する.正常終了とは exit(1) による終了で,
- コアファイル (core) がダンプされないのでバックトレースできない
- このためどの関数が operator+ を呼び出してエラーを発生させたのかわからない
という問題があり,デバッグしにくい.そこで, liboctave のエラーハンドラを自分で定義して,エラーが発生したときの振舞いを自分で決められるようにしよう.
liboctave のエラーハンドラを設定するには, octave/lo-error.h で定義されている set_liboctave_error_handler という関数を使う.この関数の宣言は
typedef void (*liboctave_error_handler) (const char *, ...); CRUFT_API extern void set_liboctave_error_handler (liboctave_error_handler f);
となっていて,自分で定義した関数を f に指定することで, liboctave のエラーハンドラを自分で定義できる.
デフォルトのエラーハンドラは(おそらく)libcruft/misc/lo-error.cで定義されている liboctave_fatal 関数のようで,この定義をソースから抜粋すると,
00048 static void 00049 verror (const char *name, const char *fmt, va_list args) 00050 { 00051 if (name) 00052 fprintf (stderr, "%s: ", name); 00053 00054 vfprintf (stderr, fmt, args); 00055 fprintf (stderr, "\n"); 00056 fflush (stderr); 00057 } 00086 void 00087 liboctave_fatal (const char *fmt, ...) 00088 { 00089 va_list args; 00090 va_start (args, fmt); 00091 verror ("fatal", fmt, args); 00092 va_end (args); 00093 00094 exit (1); 00095 }
となっている. va_list, va_start, va_end は「可変引数リスト」を扱うための型と関数群 (cstdarg で定義されている), verror は lo-error.c で定義されている関数だ. liboctave_fatal を自分で書き直して, set_liboctave_error_handler で指定しよう.
具体的には,lexit: デバッグを効率化する終了関数で紹介した lexit 終了関数を使って,バックトレースが表示されるようにする:
#include <libhoge.h> // lexit が定義されているヘッダ #include <cstdlib> #include <cstdarg> #include <iostream> #include <octave/config.h> #include <octave/dColVector.h> namespace some_namespace // 適当な名前空間 (lexit と同じ) { static void verror (const char *name, const char *fmt, va_list args) { if (name) fprintf (stderr, "%s: ", name); vfprintf (stderr, fmt, args); fprintf (stderr, "\n"); fflush (stderr); } void liboctave_error (const char *fmt, ...) { va_list args; va_start (args, fmt); verror ("liboctave fatal", fmt, args); va_end (args); lexit(btfail); // バックトレースを出力し,終了 } }; // end of some_namespace
テストしてみよう:
using namespace std; using namespace some_namespace; int main(int argc, char**argv) { set_liboctave_error_handler (&liboctave_error); ColumnVector x(2,1.0), y(0); cout<<(x+y)<<endl; return 0; }
このプログラムでは x と y の要素数が異なるから, x+y の演算に失敗する.このときプログラムは,
liboctave fatal: operator +: nonconformant arguments (op1 len: 2, op2 len: 0) ------ the program is terminated at octave-errorhandler.cpp:36: within the function: void some_namespace::liboctave_error(const char*, ...) backtrace: ./a.out(_ZN12some_namespace7_exitlv7__lexitENS0_10TExitLevelEiPKcS3_+0x611)[0x804aec1] ./a.out(_ZN12some_namespace7_exitlv6_lexitENS0_10TExitLevelEiPKcS3_+0x36)[0x804a694] ./a.out(_ZN12some_namespace15liboctave_errorEPKcz+0x4a)[0x8049fdd] /usr/lib/octave-3.0.1/liboctave.so(_Z19gripe_nonconformantPKcii+0x38)[0xb7afb0b8] /usr/lib/octave-3.0.1/liboctave.so(_ZplIdE6MArrayIT_ERKS2_S4_+0x116)[0xb7a86766] ./a.out(_ZplRK12ColumnVectorS1_+0x26)[0x804a634] ./a.out(main+0x64)[0x8049e64] /lib/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb74c6455] ./a.out(_ZNSt8ios_base4InitD1Ev+0x3d)[0x8049d11]
というエラーを出力する.バックトレースを調べれば,どの関数でエラーが発生したのかが分かり,デバッグがしやすくなるだろう.もちろん, lexit(btfail) の代わりに lexit(abort) を使ってコアファイルをダンプさせることで,より深いデバッグも可能だ.