Is there an easy way to identify all the functions called as a result of invoking a function? Getting the calling hierarchy too would be nice, but is definitely not essential. I'm trying to understand someone else's package, which is in a namespace and has some S3 functions. I could probably live without tracing the S3 functions. All the functions I want to trace are in R. The code passes functions around as arguments, so that the function being called is not always obvious from inspection of the source immediately around the call. I can imagine a solution that went something like this: 1. identify all functions by searching the sources for xxxx <- function( (probably only at the left margin, to avoid attempting to trace functions defined inside of functions). 2. Write a function that wraps another function to record the fact that it has been called. 3. Somehow replace all functions with their wrapped equivalents. 4. make the top level call. 5. inspect the data constructed by the wrapper. The code is recursive and iterative; manual stepping does not seem feasible. The package includes a lot of earlier versions of the code, and so I suspect that a lot of the code is not active. Thanks. Ross Boylan
function coverage
6 messages · R. Michael Weylandt, Duncan Murdoch, Hadley Wickham +2 more
Possibly you could trace() all the functions you're interested in. E.g.,
lapply(ls("package:stats"), trace) # Untested.
MW
On Mon, Jan 14, 2013 at 9:08 PM, Ross Boylan <ross at biostat.ucsf.edu> wrote:
Is there an easy way to identify all the functions called as a result of invoking a function? Getting the calling hierarchy too would be nice, but is definitely not essential. I'm trying to understand someone else's package, which is in a namespace and has some S3 functions. I could probably live without tracing the S3 functions. All the functions I want to trace are in R. The code passes functions around as arguments, so that the function being called is not always obvious from inspection of the source immediately around the call. I can imagine a solution that went something like this: 1. identify all functions by searching the sources for xxxx <- function( (probably only at the left margin, to avoid attempting to trace functions defined inside of functions). 2. Write a function that wraps another function to record the fact that it has been called. 3. Somehow replace all functions with their wrapped equivalents. 4. make the top level call. 5. inspect the data constructed by the wrapper. The code is recursive and iterative; manual stepping does not seem feasible. The package includes a lot of earlier versions of the code, and so I suspect that a lot of the code is not active. Thanks. Ross Boylan
______________________________________________ R-help at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-help PLEASE do read the posting guide http://www.R-project.org/posting-guide.html and provide commented, minimal, self-contained, reproducible code.
On 13-01-14 4:08 PM, Ross Boylan wrote:
Is there an easy way to identify all the functions called as a result of invoking a function? Getting the calling hierarchy too would be nice, but is definitely not essential.
I think codetools could do this reasonably well with the walkCode function, but I've never done it so I don't have sample code, and walkCode is mostly an internal function. The other way to do it is to run Rprof. It's only a sampling profiler, so you don't get complete coverage, but it sees what really happens at run-time. The proftools package can generate a call tree. (In R 3.0.0 you'll probably be able to extend this coverage analysis to the statement level, but it's not there yet.)
I'm trying to understand someone else's package, which is in a namespace and has some S3 functions. I could probably live without tracing the S3 functions. All the functions I want to trace are in R. The code passes functions around as arguments, so that the function being called is not always obvious from inspection of the source immediately around the call.
Right, that makes it hard. I don't know if walkCode could figure that stuff out, and the current Rprof won't know the original name if you do something like f <- mean f(3) The new stuff should be able to help in cases where the called function is written in R and is slow enough to be caught by the profiler. If you want to try it out and can compile R-devel for yourself, write to me and I'll send you a patch offline.
I can imagine a solution that went something like this: 1. identify all functions by searching the sources for xxxx <- function( (probably only at the left margin, to avoid attempting to trace functions defined inside of functions). 2. Write a function that wraps another function to record the fact that it has been called. 3. Somehow replace all functions with their wrapped equivalents. 4. make the top level call. 5. inspect the data constructed by the wrapper. The code is recursive and iterative; manual stepping does not seem feasible. The package includes a lot of earlier versions of the code, and so I suspect that a lot of the code is not active.
Duncan Murdoch
I think codetools could do this reasonably well with the walkCode function, but I've never done it so I don't have sample code, and walkCode is mostly an internal function.
There are a couple of approaches here: http://stackoverflow.com/questions/14276728/ Hadley
Chief Scientist, RStudio http://had.co.nz/
On 14/01/2013 22:25, Hadley Wickham wrote:
I think codetools could do this reasonably well with the walkCode function, but I've never done it so I don't have sample code, and walkCode is mostly an internal function.
There are a couple of approaches here: http://stackoverflow.com/questions/14276728/ Hadley
I've used foodweb in mvbutils for that kind of thing. HTH KJ
On Mon, 2013-01-14 at 13:08 -0800, Ross Boylan wrote:
Is there an easy way to identify all the functions called as a result of invoking a function? Getting the calling hierarchy too would be nice, but is definitely not essential. I'm trying to understand someone else's package, which is in a namespace and has some S3 functions. I could probably live without tracing the S3 functions. All the functions I want to trace are in R. The code passes functions around as arguments, so that the function being called is not always obvious from inspection of the source immediately around the call. I can imagine a solution that went something like this: 1. identify all functions by searching the sources for xxxx <- function( (probably only at the left margin, to avoid attempting to trace functions defined inside of functions). 2. Write a function that wraps another function to record the fact that it has been called. 3. Somehow replace all functions with their wrapped equivalents. 4. make the top level call. 5. inspect the data constructed by the wrapper. The code is recursive and iterative; manual stepping does not seem feasible. The package includes a lot of earlier versions of the code, and so I suspect that a lot of the code is not active. Thanks. Ross Boylan
Inspired particularly by Michael Weylandt's suggestion I developed the
following functions, with a typical usage indicated int he comments.
The file is cover.R. I'm not sure if attachments are allowed, so here
it is inline
-------------------------------------------
# cover.R determine functions called
# GPL 3.0 license
# Typical use
# mylog <- gzfile("try2.gz", "w")
# monitorNamespace("missreg2", "mylog")
# call one or more functions
# unmonitorNamespace("missreg2")
# close(mylog)
# monitor f (type character, name of function) in environmnet env for calls
# log results to connection con (type character, name of global variable)
monitor <- function(f, con, env=parent.frame()) {
tin <- parse(text=paste('writeLines("', f, ' in", ', con,")", sep=""))
tout <- parse(text=paste('writeLines("', f, ' out", ', con,")", sep=""))
trace(f, tin, tout, print=FALSE, where=env)
tout
}
unmonitor <- function(f, env=parent.frame()) {
untrace(f, where=env)
}
# monitor all functions in ns
# con is a global variable that receives the log
monitorNamespace <- function(ns, con){
e <- getNamespace(ns)
invisible(sapply(ls(e), function(nm) if (is.function(get(nm, envir=e, inherits=FALSE)))
monitor(nm, con, env=e)))
}
unmonitorNamespace <- function(ns){
e <- getNamespace(ns)
lapply(ls(e), function(nm) unmonitor(nm, env=e))
}
--------------------------------------------------------------
This mostly seemed to work, but for some reason the outerlevel call I
made is not recorded in the log. I thought this might be a result of
previous calls to trace or debug, or perhaps my having edited the
function and stuck it in my global namespace. I tried
detach("missreg2", unload=TRUE") and reloading the library; it didn't
help. And the function loaation looks like all the others that were
traced:
getAnywhere("rclusbin3")
getAnywhere("rclusbin3")
2 differing objects matching ?rclusbin3? were found
in the following places
package:missreg2
namespace:missreg2
which is exactly the same as other functions it did trace.
Thanks also to Duncan and Hadley for their ideas. I figured with the
profiler I'd always wonder if I'd missed something, and so went this low
tech route. Because of the missing log entry for the outer level
function, I'm still wondering if I got everything (since the reason the
outer level function wasn't traced might apply to others).