SML/NJでスタックトレースを表示する
SMLではコード中のほとんどどこからでも例外が投げられるのですが、それがどこから投げられたかを追跡するポータブルな方法は存在しません。
ただしSML/NJでは例外が投げられた時にスタックトレースを表示することが出来たので方法を紹介します。
#なお、ドキュメントは存在しない模様orz
実行例
以下のコードの場合の実行結果を示します。
error() で投げられる例外が、wrap1..4 の呼び出しを巻き戻して上がってきます。
fun error () = (raise Fail "error!";()) fun wrap1 () = error () fun wrap2 () = wrap1 () fun wrap3 () = wrap2 () fun wrap4 () = wrap3 () fun main' (_, _) = OS.Process.success before wrap4(); fun main (name, argv) = BackTrace.monitor(fn () => main'(name, argv))
実行した結果、以下のようなログが得られます。
$ sml @SMLload=stacktrace *** BACK-TRACE *** GOTO stacktrace.sml:7.7-7.42: StackTrace.error[2] (from: stacktrace.sml:8.18-8.26: StackTrace.wrap1[2]) GOTO stacktrace.sml:8.7-8.26: StackTrace.wrap1[2] (from: stacktrace.sml:9.18-9.26: StackTrace.wrap2[2]) GOTO stacktrace.sml:9.7-9.26: StackTrace.wrap2[2] (from: stacktrace.sml:10.18-10.26: StackTrace.wrap3[2]) GOTO stacktrace.sml:10.7-10.26: StackTrace.wrap3[2] (from: stacktrace.sml:11.18-11.26: StackTrace.wrap4[2]) CALL stacktrace.sml:11.7-11.26: StackTrace.wrap4[2] (from: stacktrace.sml:14.6-14.39: StackTrace.main'[2]) GOTO stacktrace.sml:13.7-14.39: StackTrace.main'[2] (from: stacktrace.sml:17.32-17.49: StackTrace.main[2]) GOTO stacktrace.sml:17.23-17.49: StackTrace.main[2] (from: stacktrace.sml:17.5-17.50: StackTrace.main[2]) CALL stacktrace.sml:16.7-17.50: StackTrace.main[2] (from: 4120-export.sml:1.38-1.90: XYZ_XXX_0123) stacktrace.sml:7.25-7.38: Fail: error!
いい感じですね。スタックの底から順に表示されるようです。
main関数から wrap4 の呼び出し以外は全部ジャンプになっているようです。賢い。
使い方
CMファイルで指定する方法と、REPLからその場で使う方法があります。
ちょっと普通と違うので注意。
CMファイルで指定
$smlnj-tdp/plugins.cm を依存関係に追加します。
group is $/basis.cm $smlnj-tdp/plugins.cm stacktrace.sml
ビルドの段階でTDPモードの指定と、セットアップ用に back-trace.cm を指定します。
この場合、ヒープファイル名の省略は出来ません。
$ ml-build -Ctdp.instrument=true \$smlnj-tdp/back-trace.cm stacktrace.cm StackTrace.main stacktrace
あとは普通に実行するだけ。
$ sml @SMLload=stacktrace
REPLから使用
いきなり $smlnj-tdp/back-trace.cm を読み込んだ後、内部モジュールのフラグを書き換えてから use します(makeではダメ)。
sml> CM.make "$smlnj-tdp/back-trace.cm"; sml> SMLofNJ.Internals.TDP := true; sml> use "stacktrace.sml"; sml> StackTrace.main(CommandLine.name(), CommandLine.arguments()); ..(上に同じ)..