mtrace
sets or clears debugging mode for a function; mtrace.off
clears debugging mode for all functions; check.for.tracees
shows which functions are in debugging mode.
# Usual: mtrace( fname) or mtrace( fname, F) or mtrace( apackage:::afunction)
mtrace( fname, tracing=TRUE, char.fname, from=mvb.sys.parent(), update.tracees=TRUE, return.envs=FALSE)
mtrace.off()
check.for.tracees( where=1)
::
or :::
) or list/environment (via $
)x
, use char.fname=x
. If you want to turn off tracing while doing so, mtrace( char=x, F)
won't work because of argument matching rules; you need mtrace( char.fname=x, tracing=F)
.fname
(not usually needed)mtraced
versionmtrace
by default returns an invisible copy of the modified function body. If you set return.envs=TRUE
, it will instead return a list of the environments in which the function has been modified. This is only intended for "internal use".
check.for.tracees
returns a character vector of function names.
mtrace(myfun)
modifies the body code of myfun
, and also stores debugging information about myfun
in tracees$myfun
. Next time the function is invoked, the modified debug-ready version will be called instead of the orginal. mtrace
does not modify source code (or other) attributes, so myfun
will "look" exactly the same afterwards. mtrace(myfun,FALSE)
restores myfun
to normal. mtrace.off
unmtraces all mtraced functions (see below for exceptions).Because mtrace
modifies function bodies (possibly in several places, if namespaced packages are involved), calling save.image
or save
while functions are still mtrace
d is probably not a good idea-- if the saved image is reloaded in a new R session, the debugger won't know how to handle the previously mtrace
d functions, and an error message will be given if they are invoked. The Save
and Save.pos
functions in package mvbutils will get round this without your having to manually untrace and retrace functions.
If you do see a "maybe saved before being un-mtraced?" error message when myfun
is invoked, all is not lost; you can restore myfun
to health via mtrace(myfun,F)
, or put it properly into debugging mode via mtrace(myfun)
. mtrace.off
won't work in such cases, because myfun
isn't included in tracees
.
check.for.tracees
checks for functions which have been mtrace
d, but only in one directory. By contrast, names(tracees)
will return all functions that are currently known to be mtrace
d. However, unlike check.for.tracees
, names(tracees)
won't show functions that were saved during a previous R session in an mtrace
d state.
mtrace.off
will try to untrace all functions. Specifically, it deals with those returned by names( tracees)
and/or check.for.tracees( 1)
. It doesn't currently deal with methods of reference-class and S4-class objects, for which you'll need to call mtrace(..., tracing=FALSE)
manually.
mtrace
puts a breakpoint (see bp
) at line 1, but clears all other breakpoints.
mtrace
can handle mlocal
functions, but not (yet) do.in.envir
functions-- the latter appear as monolithic statements in the code window. See package mvbutils for more details.
If you use fixr
to edit functions, mtrace
will automatically be re-applied when an updated function file is sourced back in. Otherwise, you'll need to call mtrace
manually after updating a function.
Finding functions
mtrace
by default looks for a function in the following places: first in the frame stack, then in the search path, then in all namespaces, then in all S3 methods tables. If several copies of a function are found, all will get modified (mtraced) to the same code; ditto when unmtracing.
For functions that live somewhere unusual, you'll need to set the from
argument. One case is for functions that live inside a list, such as family-functions like poisson
for GLMs. Another case is as follows. Suppose there is a function f
which first defines functions g
and h
, then calls g
. Now suppose you have mtrace
d f
and then g
from inside f
, and that g
is currently running. If you now want to mtrace(h)
, the problem is that h
is not visible from the frame of g
. To tell mtrace
where to find g
, call mtrace( h, from=sys.parent())
. [You can also replace sys.parent()
with the absolute frame number of f
, if f
has been mtrace
d and its code window is visible.] mtrace
will then look through the enclosing environments of from
until it finds a definition of h
.
If myfun
has been defined in a namespaced package, then there may be several copies of myfun
in the system, different ones being used at different times. mtrace
will change them all; see fun.locator
if you really want to know more.
If mtrace(bar)
is called while function foo
is being debugged (mtrace(foo)
having previously been called), and bar
has been redefined within foo
or a parent environment of foo
, then only the redefined copy of bar
will be mtrace
d.
S4 and reference class methods
S4 methods can be mtrace
d, but like much about S4 it's clunky; see package?debug
. Reference class methods can be mtrace
d easily after an object has been instantiated. You might call this "object-level" mtracing, because it only works for one object of each class at a time. To mtrace
e.g. the edit
method in the example for "?ReferenceClasses", just do:
mtrace( edit, from=xx) # NB will force a method into existence even if it's not been invoked yet mtrace( edit, from=xx, FALSE) # to clear it; mtrace.off() won't work properly
You can also do "class-level" mtracing, so that all subsequently-created objects of that class will use the mtrace
d version. Just do this:
mtrace( edit, from=mEditor$def@refMethods) xx <- mEditor$new( ...) mtrace( edit, from=mEditor$def@refMethods, FALSE) # to clear it; mtrace.off() won't work properly
In the "class-level" case, xx
will still have an mtrace
d version of edit
even after the mtrace( from=mEditor..., FALSE)
. You'll need to use the "object-level" technique to clear it.
As of April 2011, methods are only set up inside a ref-class object when they are first accessed, not when the object is created. mtrace
(actually fun.locator
) works round this.
LimitationsProbably many; but the main one I'm aware of, is the inability to have mtrace
on simultaneously for two functions that have the same name but that have different bodies and live in different places. In theory, the solution is for me to incorporate "location" into the function-level debug info in mtracees
, but I've not been able to figure out a good general-purpose way to do so. If this describes your particular debugging hell, you certainly have my sympathy...
## Not run:
# mtrace(glm) # turns tracing on
# names( tracees) # "glm"
# check.for.tracees( "package:base") # "glm"
# glm(stupid.args) # voila le debugger
# qqq() # back to command prompt
# mtrace( glm, FALSE)
# mtrace.off() # turns it off for all functions
# mtrace( debug:::setup.debug.admin) # woe betide ye
# ## End(Not run)
Run the code above in your browser using DataLab