Debug in R #3: Trace function calls


traceでフレキシブルなデバッグ

traceで、かなりフレキシブルなデバッグを仕込める。実際、setBreakpointtraceのラッパ。

trace(fun, hoge)funに対してhogeを埋め込める。traceを解除するにはuntrace(fun)で。

以下、traceの使い方を簡単に解説。

基本

まずは例から。

関数定義

f <- function() {
	print(1)
	print(2)
	print(3)
	print(4)
}

実行例

> trace(f, browser, at=c(3,5)) # traceでbrowserを仕込む。ここでは、3個目と5個目の式の評価前に仕込む。
[1] "f"

> f() # 関数呼び出し。
[1] 1
Tracing f() step 3 # step 3でbrowserが入る。
Called from: eval(expr, envir, enclos)

Browse[1]> n # 次の評価式は print(2)
debug: print(2) # なぜ3なのにprint(3)じゃないかはあとで説明。

Browse[2]> n
[1] 2
debug: print(3)

Browse[2]> c # 継続
[1] 3
Tracing f() step 5 # step 5でbrowserが入る。
Called from: eval(expr, envir, enclos)

Browse[1]> n
debug: print(4)
Browse[2]> c
[1] 4

と、いうように、関数内の任意の場所でインタラクティブデバッグを仕込むことができる(ブレークポイントを設定できる)。実際は、仕込む関数はbrowserじゃなくてもいい。

任意の関数を実行

traceの引数tracerには任意の関数または未評価の式を渡せる。例えば、その時点で環境内にある変数リストを得るためにprint(ls())したければ、

関数定義

g <- function() {
	a <- 1
	b <- 2
	c <- 3
	d <- 4
}

実行例

> trace(g, quote(print(ls())), at = c(3, 5)) # 3 step目と5 step目でprint(ls())する。
[1] "g"
> g() # 関数呼び出し
Tracing g() step 3 
[1] "a" # 3 step目でprint(ls())の結果
Tracing g() step 5 
[1] "a" "b" "c" # 5 step目でprint(ls())の結果

式の場合は未評価である必要があるので、quoteする必要があることに注意。前のbrowserの例では関数を渡してるんで、quoteは不要。

関数内のどこで止めるか:関数の式リスト

上の例で、at=c(3, 5)としてるけど、なんか狙った場所で止まってないと思うかもしれない。関数呼び出しでの式リストを得るには、as.list(body(fun))で、

実行例

> as.list(body(g))
[[1]]
`{`

[[2]]
a <- 1

[[3]]
b <- 2

[[4]]
c <- 3

[[5]]
d <- 4

と、最初の要素は{なので。このリスト見て、止めたいところをatに渡せばいい。

trace何してんの一体?

せっかく式リストの話まで出てきたので、traceが何やってんのか見といたほうが簡単に理解できるかも。

実行例

> g # gの定義
function() {
	a <- 1
	b <- 2
	c <- 3
	d <- 4
}
> as.list(body(g)) # gの式リスト
[[1]]
`{`

[[2]]
a <- 1

[[3]]
b <- 2

[[4]]
c <- 3

[[5]]
d <- 4

> trace(g, quote(print(ls())), at=c(3,5)) # traceを仕込む
[1] "g"
> as.list(body(g)) # gの式リスト
[[1]]
`{`

[[2]]
a <- 1

[[3]] # 本来の式の前に、tracerが埋めこまれている
{
    .doTrace(print(ls()), "step 3")
    b <- 2
}

[[4]]
c <- 3

[[5]] # こっちも。
{
    .doTrace(print(ls()), "step 5")
    d <- 4
}

と、言うように、実際には式リストの動的な変形をしてる、ということっぽい。これは、実行時に動的に未評価の式ツリーをいじる機能があるRだからこその技で、いかにもRがscheme(← lisp)由来と思わせるところ。

その他のtraceの使い方

1. tracer無しでtraceすると、呼び出しを確認できる。

実行例

> f <- function(n) for (i in 1:n) mean(1:i) # 引数nの回数だけmeanする関数。
> trace(mean) # meanをtracerなしでtrace
> f(3) # 3回mean。
trace: mean(1:i)
trace: mean(1:i)
trace: mean(1:i)
> f(5) # 5回
trace: mean(1:i)
trace: mean(1:i)
trace: mean(1:i)
trace: mean(1:i)
trace: mean(1:i)

2. S4 classとR5 reference classは確かdebugは使えなくて、traceじゃないと追っかけられなかったはず。S3/S4/R5のデバッグ方法は別記事にする。

3. recoverと組み合わせて、親フレームに行くことも可能なデバッグ。これもrecoverを別記事にする。

4. 他にも引数があって、色々できるので、詳細は?traceで。だけど、こういうのはIDE用に提供されてると考えたほうがよくて、手でいじるもんじゃないと思う。

About these ads

One thought on “Debug in R #3: Trace function calls

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s