あどけない話

Internet technologies

GHC でスタックトレース

これまで GHC では、スタックトレースを取ることが有効なデバッグ方法ではなかった。
なぜなら遅延評価では、(再帰であってもなくても)末尾呼び出しは単なるジャンプになるから、スタックを使わないのである。スタックに戻る場所を積むのは、case と of の中で評価される式だけだ。(つまり、ここは正格に評価される。)

この問題を解決するために GHC 7.4.2 から、わざわざスタックにログを残して、スタックトレースが取れるようになった。すなわち、最新の Haskell Platform をインストールしていれば、この機能を使えるということだ。

例として、以下のプログラムを考えよう。

module Main where

main :: IO ()
main = print $ foo 3 + 1

foo :: Int -> Int
foo x = x * 2 + bar x

bar :: Int -> Int
bar x = x + 2 - baz x

baz :: Int -> Int
baz x = x `div` 0

baz で 0 割り算が起こる。ここで、main -> foo -> bar -> baz というスタックトレースが取れるか実験してみる。

まず、上記のファイルを -prof -fprof-auto-top 付きでコンパイルする。

% ghc main.hs -prof -fprof-auto-top

単に実行すると、こんな感じ。

% ./main
main: divide by zero

スタックトレースを取るためには、+RTS -p -xc オプション付きで実行する。

% ./main +RTS -p -xc               
Exception (reporting due to +RTS -xc): (THUNK_2_0), stack trace:
GHC.Err.CAF --> evaluated by: Main.baz, called from Main.bar, called from Main.foo, called from Main.main, called from Main.CAF --> evaluated by: Main.bar, called from Main.foo, called from Main.main, called from Main.CAF --> evaluated by: Main.foo, called from Main.main, called from Main.CAF --> evaluated by: Main.main, called from Main.CAF main: divide by zero
めでたい。 GHCi からは、この機能は使えないようだ。