ためしてみよう、スタックトレース(stack backtrace)

JavaやLL言語ではよくある、「スタックトレース」を、OCamlでも出してみましょう。

ソース

mod1.ml(例外が発生):

let test () = (function (a::_) -> a) []

mod2.ml:

let test () = Mod1.test ()

main.ml:

let _ = Mod2.test ()

debug.ml

(* スタックトレースを出力するかどうかの設定 *)
let mode _ = true

コンパイル with ocamlbuild

_tagsで、camlp4を使うように設定する。

not "debug.ml":pp(camlp4o -filter Camlp4ExceptionTracer)

あとは、ocamlbuidでコンパイル

$ ocamlbuild main.byte

スタックトレースを出そう

普通に実行すると、スタックトレースがでてきます。

$ ./main.byte
camlp4-debug: exc: File "mod1.ml", line 1, characters 15-20: Pattern matching failed at File "mod1.ml", line 1, characters 37-39
camlp4-debug: exc: File "mod1.ml", line 1, characters 15-20: Pattern matching failed at File "mod2.ml", line 1, characters 24-26
Fatal error: exception Match_failure("mod1.ml", 1, 15)

やってること。

camlp4コマンドを直接使うと、何をやっているかが分ります。

$ camlp4o -filter Camlp4ExceptionTracer mod2.ml
let test () =
  try Mod1.test ()
  with | (Stream.Failure | Exit as exc) -> raise exc
  | exc ->
      (if Debug.mode "exc"
       then
         Format.eprintf
           "camlp4-debug: exc: %s at File \"mod2.ml\", line 1, characters 24-26@."
           (Printexc.to_string exc)
       else ();
       raise exc)

ようするに、全ての関数にtry..withを加えて、例外を出力するようにしているだけです。
あとCamlp4ExceptionTracerは、3.10からの新機能らしいです。参考:Camlp4: Major changes。他にも内包表記やliftが使えるようになるライブラリもあるらしい。