スタックトレースの賢い省略

Ruby は例外終了時にスタックトレースを表示しますが、長いときは以下のように省略をします。

$ ruby19 -e '
def fib(n)
  n <= 1 ? n : fib(n - 1) + fib(n - 2)
end
fib(30000)
'
-e:3:in `fib': stack level too deep (SystemStackError)
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
         ... 8174 levels...
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:5:in `<main>'

しかし長いトレースを無条件で省略するのでデバッグに必要な情報まで消える、という話が何度も問題提起されてきたけど、matz が変更に難色を示すために解決されなかったのでした。akr さんが「SystemStackError のときだけスタックトレースを省略する」という空気を読んだ方針を提案することで、ついに変更が受け入れられた、という美談 (RubyKaigi 2008 の「matz を説得する方法」) 。

でもこのせいで、Ctrl+C でとめたときに冗長なトレースが出ることがあるんですよね。こんなの。

$ ruby19 -e '
def fib(n)
  n <= 1 ? n : fib(n - 1) + fib(n - 2)
end
fib(3000)
'

-e:3:in `fib': Interrupt
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'
        from -e:3:in `fib'

わーっと 3000 行くらい続く。しかし、安易に Interrupt の場合も省略表示するようにすると、デバッグに必要なトレースまで省略される可能性がでてくる。


そこで、スタックトレース中の繰り返しを検出して賢く省略すればいいのではないかと思い、プロトタイプしてみました。こんな風に表示されます。

$ ruby19 -rprettytrace -e '
def fib(n)
  n <= 1 ? n : fib(n - 1) + fib(n - 2)
end
fib(3000)
'
-e:3:in `fib': Interrupt
        from -e:3:in `fib'
          from -e:3:in `fib'
          (repeat the above line 2988 times)
        from -e:3:in `fib'
        from -e:5:in `<main>'

んー、どうかなあ。
相互再帰もそれなりに。

$ ruby19 -rprettytrace -e '
def foo
  bar
end
def bar
  foo
end
foo
'
-e:6:in `bar': stack level too deep (SystemStackError)
        from -e:3:in `foo'
        from -e:6:in `bar'
        from -e:3:in `foo'
          from -e:6:in `bar'
          from -e:3:in `foo'
          (repeat the above lines 4363 times)
        from -e:6:in `bar'
        from -e:3:in `foo'
        from -e:8:in `<main>'

悪くはないと思うんだけど、ちょっと慣れが必要かなあ。あと、見た目がちょっといまいちかしら。もっといい表記はないものか。


プロトタイプのソースgithub にあります。試してみたい人は以下で。

$ gem install mame-prettytrace --source=http://gems.github.com/