9 디버깅
9 디버깅 (Debugging)
코드에 문제가 생겼을 때, R은 실행을 특정 지점에서 멈추고 그 시점의 계산 상태를 들여다볼 수 있는 도구들을 몇 가지 제공해요. 이 장에서는 그 도구들을 하나씩 살펴볼게요.
출처: R 공식 매뉴얼
본문
디버깅은 언제나 한편으로는 "예술"에 가까운 일이었어요. 그런데 R은 코드 속에서 문제를 찾는 데 도움을 주는 도구를 몇 가지 갖추고 있어요. 이 도구들은 코드의 특정 지점에서 실행을 멈추게 하고, 그 순간의 계산 상태를 살펴볼 수 있게 해주죠.
대부분의 디버깅은 browser나 debug를 호출하는 방식으로 이뤄져요. 이 두 함수는 내부적으로 같은 메커니즘을 사용하고, 둘 다 사용자에게 특별한 프롬프트를 띄워줘요. 그 프롬프트에 어떤 명령이든 입력할 수 있는데, 명령이 평가되는 평가 환경(environment)은 바로 그 시점에 활성화된 환경입니다. 덕분에 지금 어떤 변수의 값이든 그대로 들여다볼 수 있어요.
다섯 가지 특별한 명령은 R이 다르게 해석하는데, 다음과 같아요.
RET(엔터) — 함수를 디버깅 중이라면 다음 문장으로 넘어가요.browser로 불린 상황이라면 실행을 계속해요.c/cont— 실행을 계속해요.n— 함수 안에서 다음 문장을 실행해요.browser프롬프트에서도 동작해요.where— 호출 스택(call stack)을 보여줘요.Q— 실행을 멈추고 곧바로 최상위 수준(top-level)로 뛰어나와요.
만약 위 특별 명령과 같은 이름을 가진 지역 변수가 있다면, 그 값을 get으로 접근할 수 있어요. 이름을 따옴표로 감싸서 get을 호출하면 현재 환경에서 그 값을 가져오죠.
디버거는 인터프리터로 해석되는 표현식에만 접근할 수 있어요. 만약 함수가 C 같은 외부 언어를 호출한다면, 그 언어 안의 문장에는 접근할 수 없어요. 실행은 R로 평가되는 다음 문장에서 멈추게 되죠. 컴파일된 코드를 디버깅하려면 gdb 같은 심볼릭 디버거를 쓰면 돼요.
9.1 browser
browser 함수를 호출하면 그 지점에서 R의 실행이 멈추고, 사용자에게 특별한 프롬프트가 제공돼요. browser에 전달하는 인자(argument)는 무시됩니다.
> foo <- function(s) {
+ c <- 3
+ browser()
+ }
> foo(4)
Called from: foo(4)
Browse[1]> s
[1] 4
Browse[1]> get("c")
[1] 3
Browse[1]>
9.2 debug / undebug
debug(fun)이라고 하면 어떤 함수든 디버거로 실행할 수 있어요. 이후 그 함수가 평가될 때마다 디버거가 호출되는데, 함수 본문의 문장들을 하나씩 제어하며 볼 수 있게 되죠. 각 문장이 실행되기 전에 그 문장이 출력되고 특별한 프롬프트가 제공돼요. 어떤 명령이든 입력할 수 있고, 위 표에 있는 명령들은 특별한 의미를 갖습니다.
디버깅을 끄려면 함수를 인자로 해서 undebug를 호출하면 돼요.
> debug(mean.default)
> mean(1:10)
debugging in: mean.default(1:10)
debug: {
if (na.rm)
x <- x[!is.na(x)]
trim <- trim[1]
n <- length(c(x, recursive = TRUE))
if (trim > 0) {
if (trim >= 0.5)
return(median(x, na.rm = FALSE))
lo <- floor(n * trim) + 1
hi <- n + 1 - lo
x <- sort(x, partial = unique(c(lo, hi)))[lo:hi]
n <- hi - lo + 1
}
sum(x)/n
}
Browse[1]>
debug: if (na.rm) x <- x[!is.na(x)]
Browse[1]>
debug: trim <- trim[1]
Browse[1]>
debug: n <- length(c(x, recursive = TRUE))
Browse[1]> c
exiting from: mean.default(1:10)
[1] 5.5
9.3 trace / untrace
R의 동작을 지켜보는 또 다른 방법은 trace 메커니즘이에요. trace에 추적하고 싶은 함수 이름 하나를 인자로 넣어서 호출하죠. 이름을 따옴표로 감싸지 않아도 되지만, 어떤 함수는 문법 오류를 피하려고 이름을 따옴표로 감싸야 할 때도 있어요.
함수에 trace를 걸어두면 그 함수가 평가될 때마다 해당 호출이 출력돼요. 이 메커니즘을 끄려면 함수를 인자로 해서 untrace를 호출하면 됩니다.
> trace("[<-")
> x <- 1:10
> x[3] <- 4
trace: "[<-"(*tmp*, 3, value = 4)
9.4 traceback
오류 때문에 최상위 수준으로 뛰어나오면, base 환경에 .Traceback이라는 특별한 변수가 놓여요. .Traceback은 오류가 발생한 시점에 활성 상태였던 각 함수 호출마다 하나의 항목을 갖는 문자 벡터(character vector)입니다. .Traceback의 내용은 traceback을 호출해서 살펴볼 수 있어요.
더 알아보기
- 이 장에서 다룬
browser,debug/undebug,trace/untrace,traceback은 모두 R 실행 환경에서 제공되는 기본 디버깅 도구예요. 각 함수의 자세한 사용법은 R의 도움말(?browser,?debug,?trace,?traceback)을 참고하면 돼요. - 외부 언어로 작성된 컴파일 코드를 디버깅할 때는
gdb같은 심볼릭 디버거가 필요하다는 점도 기억해두세요.