R: Interactive Tracing and Debugging of Calls to a Function or...
trace
R Documentation
Interactive Tracing and Debugging of Calls to a Function or Method
Description
A call to trace allows you to insert debugging code (e.g., a
call to browser or recover) at chosen
places in any function. A call to untrace cancels the tracing.
Specified methods can be traced the same way, without tracing all
calls to the generic function. Trace code (tracer) can be any
R expression. Tracing can be temporarily turned on or off globally
by calling tracingState.
The name (quoted or not) of a function to be traced or
untraced. For untrace or for trace with more than one
argument, more than one name can be given in the quoted form, and
the same action will be applied to each one.
tracer
either a function or an unevaluated expression. The
function will be called or the expression will be evaluated either
at the beginning of the call, or before those steps in the call
specified by the argument at.
See the details section.
exit
either a function or an unevaluated expression. The
function will be called or the expression will be evaluated on
exiting the function.
See the details section.
at
optional numeric vector or list. If supplied, tracer
will be called just before the corresponding step in the body of the
function.
See the details section.
print
If TRUE (as per default), a descriptive line is
printed before any trace expression is evaluated.
signature
If this argument is supplied, it should be a
signature for a method for function what. In this case, the
method, and not the function itself, is traced.
edit
For complicated tracing, such as tracing within a loop
inside the function, you will need to insert the desired calls by
editing the body of the function. If so, supply the edit
argument either as TRUE, or as the name of the editor you
want to use. Then trace() will call edit and
use the version of the function after you edit it. See the details
section for additional information.
where
where to look for the function to be
traced; by default, the top-level environment of the call to
trace.
An important use of this argument is to trace a function when it
is called from a package with a namespace. The current namespace mechanism
imports the functions to be called (with the exception of functions
in the base package). The functions being called are not the
same objects seen from the top-level (in general, the imported
packages may not even be attached). Therefore, you must ensure that
the correct versions are being traced. The way to do this is to set
argument where to a function in the namespace. The tracing
computations will then start looking in the environment of that
function (which will be the namespace of the corresponding package).
(Yes, it's subtle, but the semantics here are central to how
namespaces work in R.)
on
logical; a call to the support function tracingState returns TRUE
if tracing is globally turned on, FALSE otherwise. An
argument of one or the other of those values sets the state. If the
tracing state is FALSE, none of the trace actions will
actually occur (used, for example, by debugging functions to shut
off tracing during debugging).
expr, msg
arguments to the support function .doTrace, calls to
which are inserted into the modified function or method:
expr is the tracing action (such as a call to
browser(), and msg is a string identifying the
place where the trace action occurs.
default
If returnValue finds no return value (e.g.
a function exited because of an error, not a normal exit), it
will return default instead.
Details
The trace function operates by constructing a revised version
of the function (or of the method, if signature is supplied),
and assigning the new object back where the original was found.
If only the what argument is given, a line of trace printing is
produced for each call to the function (back compatible with the
earlier version of trace).
The object constructed by trace is from a class that extends
"function" and which contains the original, untraced version.
A call to untrace re-assigns this version.
If the argument tracer or exit is the name of a
function, the tracing expression will be a call to that function, with
no arguments. This is the easiest and most common case, with the
functions browser and recover the
likeliest candidates; the former browses in the frame of the function
being traced, and the latter allows browsing in any of the currently
active calls.
The tracer or exit argument can also be an unevaluated
expression (such as returned by a call to quote or
substitute). This expression itself is inserted in the
traced function, so it will typically involve arguments or local
objects in the traced function. An expression of this form is useful
if you only want to interact when certain conditions apply (and in
this case you probably want to supply print = FALSE in the call
to trace also).
When the at argument is supplied, it can be a vector of
integers referring to the substeps of the body of the function (this
only works if the body of the function is enclosed in { ...}. In
this case tracer is not called on entry, but instead
just before evaluating each of the steps listed in at. (Hint:
you don't want to try to count the steps in the printed version of a
function; instead, look at as.list(body(f)) to get the numbers
associated with the steps in function f.)
The at argument can also be a list of integer vectors. In
this case, each vector refers to a step nested within another step of
the function. For example, at = list(c(3,4))
will call the tracer just before the fourth step of the third step
of the function. See the example below.
Using setBreakpoint (from package utils) may be an
alternative, calling trace(...., at, ...).
The exit argument is called during on.exit
processing. In an on.exit expression, the experimental returnValue()
function may be called to obtain the value about to be returned by
the function. Calling this function in other circumstances will give
undefined results.
An intrinsic limitation in the exit argument is that it won't
work if the function itself uses on.exit with add=
FALSE (the default), since the existing calls will override the one
supplied by trace.
Tracing does not nest. Any call to trace replaces previously
traced versions of that function or method (except for edited
versions as discussed below), and untrace always
restores an untraced version. (Allowing nested tracing has too many
potentials for confusion and for accidentally leaving traced versions
behind.)
When the edit argument is used repeatedly with no call to
untrace on the same function or method in between, the
previously edited version is retained. If you want to throw away
all the previous tracing and then edit, call untrace before the next
call to trace. Editing may be combined with automatic
tracing; just supply the other arguments such as tracer, and
the edit argument as well. The edit = TRUE argument
uses the default editor (see edit).
Tracing primitive functions (builtins and specials) from the base
package works, but only by a special mechanism and not very
informatively. Tracing a primitive causes the primitive to be
replaced by a function with argument ... (only). You can get a bit
of information out, but not much. A warning message is issued when
trace is used on a primitive.
The practice of saving the traced version of the function back where
the function came from means that tracing carries over from one
session to another, if the traced function is saved in the
session image. (In the next session, untrace will remove the
tracing.) On the other hand, functions that were in a package, not in
the global environment, are not saved in the image, so tracing expires
with the session for such functions.
Tracing a method is basically just like tracing a function, with the
exception that the traced version is stored by a call to
setMethod rather than by direct assignment, and so is
the untraced version after a call to untrace.
The version of trace described here is largely compatible with
the version in S-Plus, although the two work by entirely different
mechanisms. The S-Plus trace uses the session frame, with the
result that tracing never carries over from one session to another (R
does not have a session frame). Another relevant distinction has
nothing directly to do with trace: The browser in S-Plus
allows changes to be made to the frame being browsed, and the changes
will persist after exiting the browser. The R browser allows changes,
but they disappear when the browser exits. This may be relevant in
that the S-Plus version allows you to experiment with code changes
interactively, but the R version does not. (A future revision may
include a ‘destructive’ browser for R.)
Value
In the simple version (just the first argument), trace returns
an invisible NULL.
Otherwise, the traced function(s) name(s). The relevant consequence is the
assignment that takes place.
untrace returns the function name invisibly.
tracingState returns the current global tracing state, and possibly
changes it.
When called during on.exit processing, returnValue returns
the value about to be returned by the exiting function. Behaviour in
other circumstances is undefined.
Note
Using trace() is conceptually a generalization of
debug, implemented differently. Namely by calling
browser via its tracer or exit argument.
The version of function tracing that includes any of the arguments
except for the function name requires the methods package
(because it uses special classes of objects to store and restore
versions of the traced functions).
If methods dispatch is not currently on, trace will load the
methods namespace, but will not put the methods package on the
search list.
References
Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988)
The New S Language.
Wadsworth & Brooks/Cole.
See Also
browser and recover, the likeliest
tracing functions;
also, quote and substitute for
constructing general expressions.
Examples
require(stats)
## Very simple use
trace(sum)
hist(rnorm(100)) # shows about 3-4 calls to sum()
untrace(sum)
## Show how pt() is called from inside power.t.test():
if(FALSE)
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)))
## instead, conditionally assign some data, and then browse
## on exit, but only then. Don't bother me otherwise
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
as.list(body(f))
as.list(body(f)[[3]])
## Now call the browser there
trace("f", quote(browser(skipCalls = 4)), at = list(c(3,4)))
## trace a utility function, with recover so we
## can browse in the calling functions as well.
trace("as.matrix", recover)
## turn off the tracing
untrace(c("f", "as.matrix"))
## Not run:
## trace calls to the function lm() that come from
## the nlme package.
## (The function nlme is in that package, and the package
## has a namespace, so the where= argument must be used
## to get the right version of lm)
trace(lm, exit = recover, where = asNamespace("nlme"))
## End(Not run)
Results
R version 3.3.1 (2016-06-21) -- "Bug in Your Hair"
Copyright (C) 2016 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.
R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.
Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.
> library(base)
> png(filename="/home/ddbj/snapshot/RGM3/R_rel/result/base/trace.Rd_%03d_medium.png", width=480, height=480)
> ### Name: trace
> ### Title: Interactive Tracing and Debugging of Calls to a Function or
> ### Method
> ### Aliases: trace untrace tracingState .doTrace returnValue
> ### Keywords: programming debugging
>
> ### ** Examples
>
> require(stats)
>
> ## Very simple use
> trace(sum)
> hist(rnorm(100)) # shows about 3-4 calls to sum()
trace: sum
trace: sum
trace: sum
> untrace(sum)
>
> ## Show how pt() is called from inside power.t.test():
> if(FALSE)
+ 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
Tracing function "pt" in package "stats"
[1] "pt"
> power.t.test(20, 1, power=0.8, sd=NULL) ##--> showing the ncp root finding:
tracing pt(*, ncp = 31622776.6016838)
tracing pt(*, ncp = 3.16227766016838e-07)
tracing pt(*, ncp = 1.54161033083713e-06)
tracing pt(*, ncp = 7.51534968562983e-06)
tracing pt(*, ncp = 3.66373136232471e-05)
tracing pt(*, ncp = 0.000178606521414343)
tracing pt(*, ncp = 0.000870697700373358)
tracing pt(*, ncp = 0.00424443508523481)
tracing pt(*, ncp = 0.0206864667531106)
tracing pt(*, ncp = 0.100722153608784)
tracing pt(*, ncp = 0.48783816205104)
tracing pt(*, ncp = 2.27122420983028)
tracing pt(*, ncp = 4.54127936549001)
tracing pt(*, ncp = 3.04551810443699)
tracing pt(*, ncp = 2.82497901757656)
tracing pt(*, ncp = 2.87564199633926)
tracing pt(*, ncp = 2.87492378604085)
tracing pt(*, ncp = 2.87476426843053)
tracing pt(*, ncp = 2.87492378604085)
Two-sample t test power calculation
n = 20
delta = 1
sd = 1.099952
sig.level = 0.05
power = 0.8
alternative = two.sided
NOTE: n is number in *each* group
> untrace(pt)
Untracing function "pt" in package "stats"
>
> 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)))
[1] "f"
>
> ## instead, conditionally assign some data, and then browse
> ## on exit, but only then. Don't bother me otherwise
>
> trace("f", quote(if(any(y < 0)) yOrig <- y),
+ exit = quote(if(exists("yOrig")) browser(skipCalls = 4)),
+ print = FALSE)
[1] "f"
>
> ## Enter the browser just before stop() is called. First, find
> ## the step numbers
>
> as.list(body(f))
[[1]]
`{`
[[2]]
on.exit(.doTrace(if (exists("yOrig")) browser(skipCalls = 4)))
[[3]]
{
.doTrace(if (any(y < 0))
yOrig <- y)
{
y <- pmax(y, 0.001)
if (x > 0)
x^y
else stop("x must be positive")
}
}
> as.list(body(f)[[3]])
[[1]]
`{`
[[2]]
.doTrace(if (any(y < 0)) yOrig <- y)
[[3]]
{
y <- pmax(y, 0.001)
if (x > 0)
x^y
else stop("x must be positive")
}
>
> ## Now call the browser there
>
> trace("f", quote(browser(skipCalls = 4)), at = list(c(3,4)))
[1] "f"
>
> ## trace a utility function, with recover so we
> ## can browse in the calling functions as well.
>
> trace("as.matrix", recover)
Tracing function "as.matrix" in package "base"
[1] "as.matrix"
>
>
> ## turn off the tracing
>
> untrace(c("f", "as.matrix"))
Untracing function "as.matrix" in package "base"
>
> ## Not run:
> ##D ## trace calls to the function lm() that come from
> ##D ## the nlme package.
> ##D ## (The function nlme is in that package, and the package
> ##D ## has a namespace, so the where= argument must be used
> ##D ## to get the right version of lm)
> ##D
> ##D trace(lm, exit = recover, where = asNamespace("nlme"))
> ## End(Not run)
>
>
>
>
>
> dev.off()
null device
1
>