Debug
Jump to navigation
Jump to search
R
How to debug an R code
- How to Easily and Efficiently Conquer Errors in Your Code
- Chapter 11 Debugging R code from the book What They Forgot to Teach You About R
- Debugging in R – How to Easily Overcome Errors in Your Code?. Examples are provided.
debug(), debugonce() and isdebugged() functions
trace() and browser(), findLineNum() and setBreakpoint()
- browser(text = "", condition = NULL, expr = TRUE, skipCalls = 0L)
# It still runs like for-loop lapply(1:3, function(i){ browser(); rnorm(i)})
- findLineNum() and setBreakpoint() if we use source() to get the functions. Or eval(parse(text=x)).
- as.list(body(myFun)).
- trace() inserts arbitrary code at any position in an existing function. trace() is occasionally useful when you’re debugging code that you don’t have the source for.
More complicated than debug(). To insert a browser() function, we don't need to modify the original function.
browser() debug statement R
myFun <- function() { x <- 8:1 y <- 1:8 plot(y~x) lines(y~x) text(x,y, letters[1:8], pos=3) } as.list(body(myFun)) # [[1]] # `{` # # [[2]] # x <- 8:1 # # [[3]] # y <- 1:8 # # [[4]] # plot(y ~ x) # # ... More ... trace(myFun, browser, at = 4) myFun() untrace(myFun)
There are several examples in ?trace().
## Very simple use. Not useful trace(sum) hist(rnorm(100)) # shows about 3-4 calls to sum() untrace(sum) ## Show how pt() is called from inside power.t.test(): trace(pt) ## would show ~20 calls, but we want to see more: trace(pt, tracer = quote(cat(sprintf("tracing pt(*, ncp = %.15g)\n", ncp))), print = FALSE) # <- not showing typical extra power.t.test(20, 1, power=0.8, sd=NULL) ##--> showing the ncp root finding: untrace(pt) f <- function(x, y) { y <- pmax(y, 0.001) if (x > 0) x ^ y else stop("x must be positive") } ## arrange to call the browser on entering and exiting ## function f trace("f", quote(browser(skipCalls = 4)), exit = quote(browser(skipCalls = 4))) trace("f", quote(if(any(y < 0)) yOrig <- y), exit = quote(if(exists("yOrig")) browser(skipCalls = 4)), print = FALSE) ## Enter the browser just before stop() is called. First, find ## the step numbers untrace(f) # (as it has changed f's body !) as.list(body(f)) as.list(body(f)[[3]]) # -> stop(..) is [[4]] trace("f", quote(browser(skipCalls = 4)), at = list(c(3,4))) f(-1,2) ## trace a utility function, with recover so we ## can browse in the calling functions as well. trace("as.matrix", recover) ## turn off the tracing (that happened above) untrace(c("f", "as.matrix"))
How to quit debugging
press Q and then ENTER
list or undebug all debugged functions
https://stackoverflow.com/a/12807637
Using assign() in functions
For example, insert the following line to your function
assign(envir=globalenv(), "GlobalVar", localvar)
options(error)
options( error = rlang::entrace, rlang_backtrace_on_error = "branch")
Debug lapply()/sapply()
- https://stackoverflow.com/questions/1395622/debugging-lapply-sapply-calls
- https://stat.ethz.ch/R-manual/R-devel/library/utils/html/recover.html. Use options(error=NULL) to turn it off.
Debugging with RStudio
- https://www.rstudio.com/resources/videos/debugging-techniques-in-rstudio/
- https://github.com/ajmcoqui/debuggingRStudio/blob/master/RStudio_Debugging_Cheatsheet.pdf
- https://support.rstudio.com/hc/en-us/articles/205612627-Debugging-with-RStudio
Debug R source code
Build R with debug information
- R -> Build R from its source on Windows
- http://www.stats.uwo.ca/faculty/murdoch/software/debuggingR/ (defunct)
- http://www.stats.uwo.ca/faculty/murdoch/software/debuggingR/gdb.shtml (defunct)
- Build R with debug information (see the discussion here). Cf output messages from running ./configure and make using the default options.
$ ./configure --help $ ./configure --enable-R-shlib --with-valgrind-instrumentation=2 \ --with-system-valgrind-headers \ CFLAGS='-g -O0 -fPIC' \ FFLAGS='-g -O0 -fPIC' \ CXXFLAGS='-g -O0 -fPIC' \ FCFLAGS='-g -O0 -fPIC' $ make -j4 $ sudo make install
- My note of debugging cor() function
- Using gdb to debug R packages with native code (Video) The steps to debug is given below.
# Make sure to create a file <src/Makevars> with something like: CFLAGS=-ggdb -O0 # Or more generally # CFLAGS=-Wall -Wextra -pedantic -O0 -ggdb # CXXFLAGS=-Wall -Wextra -pedantic -O0 -ggdb # FFLAGS=-Wall -Wextra -pedantic -O0 -ggdb $ tree nidemo $ R CMD INSTALL nidemo $ cat bug.R $ R -f bug.R $ R -d gdb (gdb) r > library(nidemo) > Ctrl+C (gdb) b nid_buggy_freq (gdb) c # continue > buggy_freq("nidemo/DESCRIPTION") # stop at breakpoint 1 (gdb) list (gdb) n # step through (gdb) # press RETURN a few times until you see the bug (gdb) d 1 # delete the first break point (gdb) b Rf_error # R's C entry point for the error function (gdb) c > buggy_freq("nidemo/DESCRIPTION") (gdb) bt 5 # last 5 stack frames (gdb) frame 2 (gdb) list (gdb) p freq_data (gdb) p ans (gdb) call Rf_PrintValues(ans) (gdb) call Rf_PrintValues(fname) (gdb) q # Edit buggy.c $ R CMD INSTALL nidemo # re-install the package $ R -f bug.R $ R -d gdb (gdb) run > source("bug.R") # error happened (gdb) bt 5 # show the last 5 frames (gdb) frame 2 (gdb) list (gdb) frame 1 (gdb) list (gdb) p file (gdb) p fh (gdb) q # Edit buggy.c $ R CMD INSTALL nidemo $ R -f bug.R
- Compiled code from "R packages" by Hadley Wickham
- Debugging C/C++ code from Bioconductor (case study)
- Same idea for the Rcpp situation. See What are productive ways to debug Rcpp compiled code loaded in R (on OS X Mavericks)?
.Call
- Writing R Extensions manual.
- R’s C interface from Advanced R by Hadley Wickham
Registering native routines
https://cran.rstudio.com/doc/manuals/r-release/R-exts.html#Registering-native-routines
Pay attention to the prefix argument .fixes (eg .fixes = "C_") in useDynLib() function in the NAMESPACE file.
Example of debugging cor() function
Note that R's cor() function called a C function cor().
stats::cor .... .Call(C_cor, x, y, na.method, method == "kendall")
A step-by-step screenshot of debugging using the GNU debugger gdb can be found on my Github repository https://github.com/arraytools/r-debug.