[R] Debugging functions defined (locally) inside another functions

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Fri Apr 12 15:36:00 CEST 2024


On 12/04/2024 8:15 a.m., Iago Giné Vázquez wrote:
> Hi all, I am trying to debug an error of a function g defined and used inside another function f of a package.
> So I have
> 
> f <- function(whatever){
>     ...
>     g <- function(whatever2){
>       ...
>     }
>     ...
> }
> 
> If I wanted to debug some thing directly inside f I would do debug(f). But this does not go inside g code. On the other hand, debug(g) does not work as g is not a defined function in the namespace of the package.
> 
> Is there some way to debug errors inside g?

The easiest case is if you have access to the source code.  Just put a 
browser() statement at the start of g, i.e. change it to

      g <- function(whatever2){
        browser()
        ...
      }

and it will break very similarly to what happens if you have set debug(g).

Another possibility if you have the source but don't want to edit it is 
to use trace.  Suppose that the definition of g is in source.R at lines 
100 to 120.  Then you can run

   setBreakpoint("source.R#101")

to set a breakpoint via trace() just before line 101 runs.  trace() has 
lots of options; it can just print things, or call browser(), etc.  They 
are available in setBreakpoint().

If you are executing code from a package and you don't have the source 
handy it's a bit tedious, but you can still do the search that 
setBreakpoint() does to find the source.  For example, let's set a 
breakpoint just before the print statement in g in this example:

  f <- function() {
    g <- function() {
      print("this is g")
    }
    print("this is f")
    g()
  }

You need to find the location of that line in f.  Look at as.list(body(f)):

 > as.list(body(f))
[[1]]
`{`

[[2]]
g <- function() {
     print("this is g")
}

[[3]]
print("this is f")

[[4]]
g()

So we need to look within entry 2:

 > as.list(body(f)[[2]])
[[1]]
`<-`

[[2]]
g

[[3]]
function() {
     print("this is g")
}

Continue drilling down:

 > as.list(body(f)[[c(2,3)]])
[[1]]
`function`

[[2]]
NULL

[[3]]
{
     print("this is g")
}

[[4]]
function() {
      print("this is g")
    }

 > as.list(body(f)[[c(2,3, 3)]])
[[1]]
`{`

[[2]]
print("this is g")

So now we know the print statement is at location c(2,3,3,2).  Set a 
browser call there:

 > trace(f, at=list(c(2,3,3,2)), tracer = quote(browser()))
[1] "f"
 > body(f)
{
     g <- function() {
         {
             .doTrace(browser(), "step 2,3,3,2")
             print("this is g")
         }
     }
     print("this is f")
     g()
}

Note that the "at" argument needs to be a list to drill down; if you 
just said at=c(2,3,3,2) it would set breakpoints at step 2 and 3.

Duncan Murdoch



More information about the R-help mailing list