[Rd] Am I missing something about debugging?

Ross Boylan ross at biostat.ucsf.edu
Thu Jan 4 19:04:19 CET 2007


On Thu, 2007-01-04 at 17:06 +1100, Mark.Bravington at csiro.au wrote:
> It is possible to do some of these things with the 'debug' package-- the
> article in R-news 2003 #3 shows a few of the tricks. Suppose 'b1' calls
> 'c1'. If 'c1' exists as "permanent" function defined outside 'b1' (which
> I generally prefer, for clarity), then you can call 'mtrace( c1)' and
> 'c1' will be invoked whenever it's called-- you don't have to first
> 'mtrace' 'b1' and then manually call 'mtrace(c1)' while inside 'b1'.
Is the effect of mtrace permanent?  For example, if 
b1 <- function() {
  # stuff
  c1()
  # stuff
  c1()
}
And you mtrace(c1), will both calls to c1, as well as any outside of b1,
bring up the debugger?

I ask because sometimes the normal "step" semantics in debugging is more
useful, i.e., debug into the next call to c1 only.  As I understand it,
the debug package can put a one-time only breakpoint (with go), but only
in the body of the currently active function.

Am I correct that both the debug package and the regular debug require
explicitly removing debugging from a function to turn off debugging?

> 
> Even if 'c1' is defined inside the body of 'b1', you can get something
> similar by using conditional breakpoints, like this
> 
> > mtrace( b1)
> > # whatever you type to get 'b1' going
> D(17)> # now look at the code window for 'b1' and find the line just
> after the definition of 'c1'
> D(17)> # ... say that's on line 11
> D(17)> bp( 11, {mtrace( c1);FALSE})
> # which will auto-mtrace 'c1' without stopping; of course you could
> hardwire this in the code of 'b1' too
> 
If you invoke b1 multiple times, will the previous procedure result in
wrapping c1 multiple times, e.g., first time through c1 is replaced by
mtrace(c1); second time rewrites the already rewritten fucntion?  Is
that a problem?

> the point is that you can stick all sorts of code inside a conditional
> breakpoint to do other things-- if the expression returns FALSE then the
> breakpoint won't be triggered, but the side-effects will still happen.
> You can also use conditional breakpoints and 'skip' command to patch
> code on-the-fly, but I generally find it's too much trouble.
> 
> 
> Note also the trick of 
> D(17)> bp(1,F) 
> 
> which is useful if 'b1' will be called again within the lifetime of the
> current top-level expression and you actually don't want to stop.
Is bp(1, FALSE) equivalent to mtrace(f, false), if one is currently
debugging in f?

> 
> The point about context is subtle because of R's scoping rules-- should
> one look at lexical scope, or at things defined in calling functions?
> The former happens by default in the 'debug' package (ie if you type the
> name of something that can be "seen" from the current function, then the
> debugger will find it, even if it's not defined in the current frame).
> For the latter, though, if you are currently "inside" c1, then one way
> to do it is to use 'sys.parent()' or 'sys.parent(2)' or whatever to
> figure out the frame number of the "context" you want, then you could do
> e.g.

> D(18)> sp <- sys.frame( sys.parent( 2))
> D(18)> evalq( ls(), sp)
> 
> etc which is not too bad. It's worth experimenting with sys.call etc
> while inside my debugger, too-- I have gone to some lengths to try to
> ensure that those functions work the way that might be expected (even
> though they actually don't... long story).
> 
sys.parent and friends  didn't seem to work for me in vanilla R
debugging, so this sounds really useful to me.
> If you are 'mtrace'ing one of the calling functions as well, then you
> can also look at the frame numbers in the code windows to work out where
> to 'evalq'.
I thought the frame numbers shown in the debugger are numbered
successively for the call stack, and that these are not necessarily the
same as the frame numbers in R.  My understanding is that the latter are
not guaranteed to be consecutive (relative to the call stack).  From the
description of sys.parent:
     The parent frame of a function evaluation is the environment in
     which the function was called.  It is not necessarily numbered one
     less than the frame number of the current evaluation,

> 
> The current 'debug' package doesn't include a "watch window" (even
> though it's something I rely on heavily in Delphi, my main other
> language) mainly because R can get stuck figuring out what to display in
> that window. 
Just out of curiosity, how does that problem arise?  I'd expect showing
the variables in a frame to be straightforward.

> It's not that hard to do (I used ot have one in the Splus
> version of my debugger) and I might add one in future if demand is high
> enough. It would help if there was some way to "time-out" a
> calculation-- e.g. a 'time.try' function a la
> 
>   result <- time.try( { do.some.big.calculation}, 0.05)
> 
> which would return an object of class "too-slow" if the calculation
> takes more than 0.05s.
> 
> I'm certainly willing to consider adding other features to the 'debug'
> package if they are easy enough and demand is high enough! [And if I
> have time, which I mostly don't :( ]
> 
> Hope this is of some use
> 
> Mark
Thanks for the info.  It sounds as if the better handling of sys.* in
the debug package may get me enough ability to look up the stack to
satisfy me.  Or I may discover that the ESS .Last.value problem is
behind my inability to use sys.* in the regular browser.

P.S. Earlier in this thread I mentioned that viewing even basic objects
like lists can be awkward in regular debuggers (I was thinking mostly of
C++).  As a current example, there is thread that begins at
http://lists.kde.org/?t=116777858300001&r=1&w=2&n=13 on the trials of
viewing a single string (this is for KDE, which has a C++ core).



More information about the R-devel mailing list