Debug in R #2: How to enter an interactive debugging


デバッグモードに入る方法

いくつかある。基本的には、

  • debug: 既存の関数でお手軽にデバッグしたい。
  • browser: 自分で関数書いてます、デバッグしたい。
  • setBreakpoint: スクリプトファイルの行番号からデバッグしたい。
  • trace: いろいろやりたい。別記事で解説。

という感じだと思う。他にもあるかもしれない。

1. debug(function_name)

お手軽。既存の関数をデバッグしたい時に使える。ヤッてることは、その関数の先頭にbrowser()を仕込むのとほとんど同じ。

一度debug(fun)を設定すると、funの呼び出しの度にデバッグモードに入る。解除するには、undebug(fun)で。

debug(fun)したあとに、funを新たに定義したら、再度debug(fun)しなおす必要がある。

一度だけデバッグしたい時には、debugonce(fun)を使う。

実行例


> f <- function()print("hoge") # fを定義

> debug(f) # fをデバッグ設定

> f() # 呼び出し→デバッグモードに突入。
debugging in: f()
debug: print("hoge")
Browse[2]> c
[1] "hoge"
exiting from: f()

> f() # もう一度呼び出し→また突入
debugging in: f()
debug: print("hoge")
Browse[2]> c
[1] "hoge"
exiting from: f()

> undebug(f) # undebugする

> f() # 呼び出し→デバッグモードに入らない。
[1] "hoge"

> debug(f) # 再度デバッグ設定

> f() # 呼び出し→デバッグモードに突入。
debugging in: f()
debug: print("hoge")
Browse[2]> c
[1] "hoge"
exiting from: f()

> f <- function()print("boke") # fを再定義。

> f() # undebugしてないけど、debug(f)の後にf()を再定義してるので、デバッグモードに入らない。
[1] "boke"

> debugonce(f) # fを一度だけデバッグするように設定。

> f() # 呼び出し→デバッグモードに突入。
debugging in: f()
debug: print("boke")
Browse[2]> c
[1] "boke"

> f() # 二度目の呼び出し→デバッグモードに入らない。
[1] "boke"

2. browser

自分で関数を書いてて、ブレークポイント的なものを設定したい時に便利。条件設定とかできるから柔軟に使える。

基本

関数定義

f1 <- function() {
  a <- 1
  browser() # インタラクティブデバッグに入る
  b <- 2
  print(b)
}

実行例


> f1() # 関数呼び出し→browserが起動してデバッグに入る
Called from: f1()

Browse[1]> ls() # 変数リストを表示
[1] "a" # すでに a <- 1 は評価されてるので、aがある。bはまだ無い。
Browse[1]> a
[1] 1

Browse[1]> n # 次の評価式は b<-2
debug: b <- 2

Browse[2]> n # b <- 2を評価して、
debug: print(b) # 次の評価式はprint(b)

Browse[2]> ls() # b <- 2が評価されたのでbがある。
[1] "a" "b"

Browse[2]> n # print(b)が評価されて、デバッグ終了。
[1] 2

条件付きデバッグ

関数内で、ある条件が真の時だけデバッグしたい時がある。その時は条件付きデバッグが使える。条件付きデバッグするには、ifで明示的に条件分岐してbrowser()を呼び出す方法と、browserexpr引数に条件式を指定する方法がある。

関数定義

f2 <- function(x) {
  a <- 1
  if (x==1) browser() # 条件付きデバッグ
  b <- 2
  print(b)
}

f3 <- function(x) {
  a <- 1
  browser(expr = x==1) # 同じだけど条件がシンプルな場合はこっちが便利
  b <- 2
  print(b)
}

実行例

> f2(2) # 引数xに2を渡して関数呼び出し。デバッグに入らない。
[1] 2

> f2(1) # 引数xに1を渡して関数呼び出し。
Called from: f2(1) # デバッグに入る。
Browse[1]> c
[1] 2

> f3(2) # 同じ。
[1] 2

> f3(1) # 同じ。
Called from: f3(1)
Browse[1]> c
[1] 2

3. setBreakpoint

スクリプトファイルをsourceで読み込んで実行するときとかに、ファイルと行番号を指定してブレークポイントを設定できる。

スクリプトファイル (testrun01.R

## testrun01.R
##
f <- function() {
	print(1)
	print(2)
	print(3)
	print(4)
}

実行例

> source("testrun01.R") # まずは読み込むことが必要。
> setBreakpoint("testrun01.R", 6) # 6行目の前にブレークポイントを設定。
/Users/takahashi/Dropbox/dev/R/exampleR/debug-in-R/testrun01.R#6:
 f step 4 in <environment: R_GlobalEnv>

> f() # 関数呼び出し
[1] 1
[1] 2 # 5行目までは実行されて、

testrun01.R#6 # デバッグモードに入る
Called from: f()
Browse[1]> n # 次の評価式は print(3)
debug: print(3)
Browse[2]> c
[1] 3
[1] 4

スクリプトファイルのある行でどの関数が定義されるのか調べるには、findLineNum

実行例

> findLineNum("testrun01.R", 6)
/Users/takahashi/Dropbox/dev/R/exampleR/debug-in-R/testrun01.R#6:
 f step 4 in <environment: R_GlobalEnv>

以下余談だけど、setBreakpointは非常に使いづらいので、これはIDE経由で使うべきものであって、手で使うべきじゃない。スクリプトファイルを自分で書いてるなら、browserを仕込んだほうが楽です。
というのも、最初にsourceしないといけないことからわかるように、実際はスクリプトファイルの特定の行で止めてるというよりも、定義された関数のソースツリーから、行番号に対応する式を拾って、そこで止めてる、といった方が正しい。なので、ブレークポイントを設定する前に、関数が定義されている必要がある。なんで使いにくいかというと、(1)関数の中でしか止められない、(2)関数の定義と呼び出しを同じスクリプトでヤッてる場合に止める術がない、という理由。
(1)の方は、例えば、こういうスクリプトがあったとして、

スクリプト

## testrun02.R
##
a <- 1
print(a)

f <- function() {
	b <- 1
}

f()

f()の呼び出しの前で止めたいとする

実行例

> source("testrun02.R") # 読み込み
[1] 1 # あたりまえだけど、print(a)が実行されてる。実際はスクリプトの最後まで実行されてる。

> setBreakpoint("testrun01.R", 9) # f()の前で止めたい。
No source refs found. # そこ関数定義じゃありませんから。

となる。なので現実的にはf()の前にbrowser()を入れちゃったほうがいいと思う。但し、この場合でもstep-by-stepのデバッグはできない。

スクリプト

## testrun02.R
##
a <- 1
print(a)

f <- function() {
	b <- 1
}
browser()
f()
print(a)

実行例

> source("testrun02.R") # スクリプト読み込み
[1] 1
Browse[2]> ls() # 一応止まる。
[1] "a" "f"
Browse[2]> n # けど、step毎の実行はできない。
[1] 1

スクリプトのstep-by-stepでのデバッグの方法は今、探している。

4. trace

traceは、非常に柔軟なデバッグを可能にする。長くなったので、別記事で解説することにする。

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