In addition to debug() and browser(), you can also enter debug mode by setting “editor breakpoints” in RStudio by clicking to the left of the line in RStudio, or by selecting the line and typing shift+F9.
To debug a function which is defined inside another function, single-step through to the end of its definition, and then call debug on its name. If you want to debug a function not starting at the very beginning, use trace(..., at = *) or setBreakpoint .
I'd say that debugging is an art form, so there's no clear silver bullet. There are good strategies for debugging in any language, and they apply here too (e.g. read this nice article). For instance, the first thing is to reproduce the problem...if you can't do that, then you need to get more information (e.g. with logging). Once you can reproduce it, you need to reduce it down to the source.
Rather than a "trick", I would say that I have a favorite debugging routine:
traceback()
: that shows you where the error occurred, which is especially useful if you have several nested functions.options(error=recover)
; this immediately switches into browser mode where the error occurs, so you can browse the workspace from there.debug()
function and step through the script line by line. The best new trick in R 2.10 (when working with script files) is to use the findLineNum()
and setBreakpoint()
functions.
As a final comment: depending upon the error, it is also very helpful to set try()
or tryCatch()
statements around external function calls (especially when dealing with S4 classes). That will sometimes provide even more information, and it also gives you more control over how errors are handled at run time.
These related questions have a lot of suggestions:
The best walkthrough I've seen so far is:
http://www.biostat.jhsph.edu/%7Erpeng/docs/R-debug-tools.pdf
Anybody agree/disagree?
As was pointed out to me in another question, Rprof()
and summaryRprof()
are nice tools to find slow parts of your program that might benefit from speeding up or moving to a C/C++ implementation. This probably applies more if you're doing simulation work or other compute- or data-intensive activities. The profr
package can help visualizing the results.
I'm on a bit of a learn-about-debugging kick, so another suggestion from another thread:
options(warn=2)
to treat warnings like errorsYou can also use options
to drop you right into the heat of the action when an error or warning occurs, using your favorite debugging function of choice. For instance:
options(error=recover)
to run recover()
when an error occurs, as Shane noted (and as is documented in the R debugging guide. Or any other handy function you would find useful to have run.And another two methods from one of @Shane's links:
try()
to return more information on it..inform=TRUE
(from the plyr package) as an option to the apply command@JoshuaUlrich also pointed out a neat way of using the conditional abilities of the classic browser()
command to turn on/off debugging:
browser(expr=isTRUE(getOption("myDebug")))
options(myDebug=TRUE)
myBrowse <- browser(expr=isTRUE(getOption("myDebug")))
and then call with myBrowse()
since it uses globals.Then there are the new functions available in R 2.10:
findLineNum()
takes a source file name and line number and returns the function and environment. This seems to be helpful when you source()
a .R file and it returns an error at line #n, but you need to know what function is located at line #n.setBreakpoint()
takes a source file name and line number and sets a breakpoint there The codetools package, and particularly its checkUsage
function can be particularly helpful in quickly picking up syntax and stylistic errors that a compiler would typically report (unused locals, undefined global functions and variables, partial argument matching, and so forth).
setBreakpoint()
is a more user-friendly front-end to trace()
. Details on the internals of how this works are available in a recent R Journal article.
If you are trying to debug someone else's package, once you have located the problem you can over-write their functions with fixInNamespace
and assignInNamespace
, but do not use this in production code.
None of this should preclude the tried-and-true standard R debugging tools, some of which are above and others of which are not. In particular, the post-mortem debugging tools are handy when you have a time-consuming bunch of code that you'd rather not re-run.
Finally, for tricky problems which don't seem to throw an error message, you can use options(error=dump.frames)
as detailed in this question:
Error without an error being thrown
At some point, glm.fit
is being called. That means one of the functions you call or one of the functions called by those functions is using either glm
, glm.fit
.
Also, as I mention in my comment above, that is a warning not an error, which makes a big difference. You can't trigger any of R's debugging tools from a warning (with default options before someone tells me I am wrong ;-).
If we change the options to turn warnings into errors then we can start to use R's debugging tools. From ?options
we have:
‘warn’: sets the handling of warning messages. If ‘warn’ is
negative all warnings are ignored. If ‘warn’ is zero (the
default) warnings are stored until the top-level function
returns. If fewer than 10 warnings were signalled they will
be printed otherwise a message saying how many (max 50) were
signalled. An object called ‘last.warning’ is created and
can be printed through the function ‘warnings’. If ‘warn’ is
one, warnings are printed as they occur. If ‘warn’ is two or
larger all warnings are turned into errors.
So if you run
options(warn = 2)
then run your code, R will throw an error. At which point, you could run
traceback()
to see the call stack. Here is an example.
> options(warn = 2)
> foo <- function(x) bar(x + 2)
> bar <- function(y) warning("don't want to use 'y'!")
> foo(1)
Error in bar(x + 2) : (converted from warning) don't want to use 'y'!
> traceback()
7: doWithOneRestart(return(expr), restart)
6: withOneRestart(expr, restarts[[1L]])
5: withRestarts({
.Internal(.signalCondition(simpleWarning(msg, call), msg,
call))
.Internal(.dfltWarn(msg, call))
}, muffleWarning = function() NULL)
4: .signalSimpleWarning("don't want to use 'y'!", quote(bar(x +
2)))
3: warning("don't want to use 'y'!")
2: bar(x + 2)
1: foo(1)
Here you can ignore the frames marked 4:
and higher. We see that foo
called bar
and that bar
generated the warning. That should show you which functions were calling glm.fit
.
If you now want to debug this, we can turn to another option to tell R to enter the debugger when it encounters an error, and as we have made warnings errors we will get a debugger when the original warning is triggered. For that you should run:
options(error = recover)
Here is an example:
> options(error = recover)
> foo(1)
Error in bar(x + 2) : (converted from warning) don't want to use 'y'!
Enter a frame number, or 0 to exit
1: foo(1)
2: bar(x + 2)
3: warning("don't want to use 'y'!")
4: .signalSimpleWarning("don't want to use 'y'!", quote(bar(x + 2)))
5: withRestarts({
6: withOneRestart(expr, restarts[[1]])
7: doWithOneRestart(return(expr), restart)
Selection:
You can then step into any of those frames to see what was happening when the warning was thrown.
To reset the above options to their default, enter
options(error = NULL, warn = 0)
As for the specific warning you quote, it is highly likely that you need to allow more iterations in the code. Once you've found out what is calling glm.fit
, work out how to pass it the control
argument using glm.control
- see ?glm.control
.
So browser()
, traceback()
and debug()
walk into a bar, but trace()
waits outside and keeps the motor running.
By inserting browser
somewhere in your function, the execution will halt and wait for your input. You can move forward using n (or Enter), run the entire chunk (iteration) with c, finish the current loop/function with f, or quit with Q; see ?browser
.
With debug
, you get the same effect as with browser, but this stops the execution of a function at its beginning. Same shortcuts apply. This function will be in a "debug" mode until you turn it off using undebug
(that is, after debug(foo)
, running the function foo
will enter "debug" mode every time until you run undebug(foo)
).
A more transient alternative is debugonce
, which will remove the "debug" mode from the function after the next time it's evaluated.
traceback
will give you the flow of execution of functions all the way up to where something went wrong (an actual error).
You can insert code bits (i.e. custom functions) in functions using trace
, for example browser
. This is useful for functions from packages and you're too lazy to get the nicely folded source code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With