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()
- trace(what, tracer, exit, at, print, signature, where = topenv(parent.frame()), edit = FALSE)
- browser(text = "", condition = NULL, expr = TRUE, skipCalls = 0L)
- as.list(body(myFun)).
- findLineNum() and setBreakpoint() if we use source() to get the functions. Or eval(parse(text=x)).
More complicated than debug(). To insert a browser() function, we don't need to modify the original function.
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.