R: Interactive Tracing and Debugging of Calls to a Function or...

trace {base} | 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`.

### Usage

    trace(what, tracer, exit, at, print, signature,
          where = topenv(parent.frame()), edit = FALSE)
    untrace(what, signature = NULL, where = topenv(parent.frame()))

    tracingState(on = NULL)
    .doTrace(expr, msg)
    returnValue(default = NULL)

### Arguments

`what` | the name, possibly `quote()`d, 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. For “hidden” functions such as S3 methods in a namespace, `where = *` typically needs to be specified as well.
`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 functions from a package which are “hidden” or called from another package. The 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 (or that 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, restart or as a result of evaluating a return from a caller function), 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 an S4 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

    untrace(f) # (as it has changed f's body !)
    as.list(body(f))
    as.list(body(f)[[3]]) # -> stop(..) is [[4]]

    ## Now call the browser there

    trace("f", quote(browser(skipCalls = 4)), at = list(c(3,4)))
    ## Not run:
    f(-1,2) # --> enters browser just before stop(..)

    ## End(Not run)

    ## 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"))

    ## Not run:
    ## Useful to find how system2() is called in a higher-up function:
    trace(base::system2, quote(print(ls.str())))

    ## End(Not run)

    ##-------- Tracing hidden functions : need 'where = *'
    ##
    ## 'where' can be a function whose environment is meant:
    trace(quote(ar.yw.default), where = ar)
    a <- ar(rnorm(100)) # "Tracing ..."
    untrace(quote(ar.yw.default), where = ar)

    ## trace() more than one function simultaneously:
    ##         expression(E1, E2, ...)  here is equivalent to
    ##          c(quote(E1), quote(E2), quote(.*), ..)
    trace(expression(ar.yw, ar.yw.default), where = ar)
    a <- ar(rnorm(100)) # --> 2 x "Tracing ..."
    # and turn it off:
    untrace(expression(ar.yw, ar.yw.default), where = ar)

    ## 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)

[Package base version 3.5.1 Index]