I would like to be able to trace execution into calls below the current function, or to follow execution as calls return. This is roughly the distinction between "step" and "next" in many debuggers. I would also like to be able to switch to a location further up the call stack than the location at which I enter the debugger, to see the context of the current operations. Are there ways to do these things with the R debugger? I've studied the man pages and FAQ's, and looked at the debug package, but I don't see a way except for manually calling debug on the function that is about to be called if I want to descend. That's quite awkward, particularly since it must be manually undone (the debug package may be better on that score). I'm also not entirely sure that such recursion (essentially, debugging within the debugger) is OK. I tried looking up the stack with things like sys.calls(), from within the browser, but they operated as if I were at the top level (e.g., sys.function(-1) gets an error that it can't go there). I was doing this in ess, and there's some chance the "can't write .Last.value" error (wording approximate) cause by having an old version is screwing things up). Since R is interpreted I would expect debugging to be a snap, but these limitations make me suspect there is something about the language design that makes implementing these facilities hard. For example, the browser as documented in the Green book has up and down functions to change the frame (p. 265); these are conspicuously absent in R. -- Ross Boylan wk: (415) 514-8146 185 Berry St #5700 ross at biostat.ucsf.edu Dept of Epidemiology and Biostatistics fax: (415) 514-8150 University of California, San Francisco San Francisco, CA 94107-1739 hm: (415) 550-1062
I don't think you're missing anything with the debug() function. It needs updating. I don't think there's any structural reason why you shouldn't be able to do the things you're talking about in R, but they haven't been implemented. Mark Bravington put together a package (called debug) that does more than debug() does, but I haven't used it much, and I don't know if it does what you want. I recently added things to the R parser to keep track of connections between R code and source files; that was partly meant as a first step towards improving the debugging facilities. I'd be happy to help anyone who wants to do the hard work, but I don't think I'll be able to work on it before next summer. (If you do decide to work on it, please let me know, just in case I do get a chance: no point duplicating effort.) Duncan Murdoch On 1/2/2007 5:08 PM, Ross Boylan wrote:> I would like to be able to trace execution into calls below the current > function, or to follow execution as calls return. This is roughly the > distinction between "step" and "next" in many debuggers. > > I would also like to be able to switch to a location further up the call > stack than the location at which I enter the debugger, to see the > context of the current operations. > > Are there ways to do these things with the R debugger? I've studied the > man pages and FAQ's, and looked at the debug package, but I don't see a > way except for manually calling debug on the function that is about to > be called if I want to descend. That's quite awkward, particularly > since it must be manually undone (the debug package may be better on > that score). I'm also not entirely sure that such recursion > (essentially, debugging within the debugger) is OK. > > I tried looking up the stack with things like sys.calls(), from within > the browser, but they operated as if I were at the top level (e.g., > sys.function(-1) gets an error that it can't go there). I was doing > this in ess, and there's some chance the "can't write .Last.value" error > (wording approximate) cause by having an old version is screwing things > up). > > Since R is interpreted I would expect debugging to be a snap, but these > limitations make me suspect there is something about the language design > that makes implementing these facilities hard. For example, the browser > as documented in the Green book has up and down functions to change the > frame (p. 265); these are conspicuously absent in R.
Rather than using debug, I generally like using recover. When called, it shows the call stack, and you can pick what to view. Using option(error=recover) triggers it on errors. You can't step through code as when using browser(), but I find jumping to different points on the call stack to be more useful. - Tom Tom Short EPRI Ross Boylan wrote:> > I would like to be able to trace execution into calls below the current > function, or to follow execution as calls return. This is roughly the > distinction between "step" and "next" in many debuggers. > > I would also like to be able to switch to a location further up the call > stack than the location at which I enter the debugger, to see the > context of the current operations. > > Are there ways to do these things with the R debugger? I've studied the > man pages and FAQ's, and looked at the debug package, but I don't see a > way except for manually calling debug on the function that is about to > be called if I want to descend. That's quite awkward, particularly > since it must be manually undone (the debug package may be better on > that score). I'm also not entirely sure that such recursion > (essentially, debugging within the debugger) is OK. > > I tried looking up the stack with things like sys.calls(), from within > the browser, but they operated as if I were at the top level (e.g., > sys.function(-1) gets an error that it can't go there). I was doing > this in ess, and there's some chance the "can't write .Last.value" error > (wording approximate) cause by having an old version is screwing things > up). > > Since R is interpreted I would expect debugging to be a snap, but these > limitations make me suspect there is something about the language design > that makes implementing these facilities hard. For example, the browser > as documented in the Green book has up and down functions to change the > frame (p. 265); these are conspicuously absent in R. > -- > Ross Boylan wk: (415) 514-8146 > 185 Berry St #5700 ross at biostat.ucsf.edu > Dept of Epidemiology and Biostatistics fax: (415) 514-8150 > University of California, San Francisco > San Francisco, CA 94107-1739 hm: (415) 550-1062 > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel > >-- View this message in context: http://www.nabble.com/Am-I-missing-something-about-debugging--tf2910389.html#a8134920 Sent from the R devel mailing list archive at Nabble.com.
Mark.Bravington at csiro.au
2007-Jan-04 06:06 UTC
[Rd] Am I missing something about debugging?
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'. 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' goingD(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 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. 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). 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'. 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. 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 Mark Bravington CSIRO Mathematical & Information Sciences Marine Laboratory Castray Esplanade Hobart 7001 TAS ph (+61) 3 6232 5118 fax (+61) 3 6232 5012 mob (+61) 438 315 623> -----Original Message----- > From: r-devel-bounces at r-project.org > [mailto:r-devel-bounces at r-project.org] On Behalf Of Ross Boylan > Sent: Wednesday, 3 January 2007 9:09 AM > To: R Development List > Subject: [Rd] Am I missing something about debugging? > > I would like to be able to trace execution into calls below > the current function, or to follow execution as calls return. > This is roughly the distinction between "step" and "next" in > many debuggers. > > I would also like to be able to switch to a location further > up the call stack than the location at which I enter the > debugger, to see the context of the current operations. > > Are there ways to do these things with the R debugger? I've > studied the man pages and FAQ's, and looked at the debug > package, but I don't see a way except for manually calling > debug on the function that is about to be called if I want to > descend. That's quite awkward, particularly since it must be > manually undone (the debug package may be better on that > score). I'm also not entirely sure that such recursion > (essentially, debugging within the debugger) is OK. > > I tried looking up the stack with things like sys.calls(), > from within the browser, but they operated as if I were at > the top level (e.g., > sys.function(-1) gets an error that it can't go there). I > was doing this in ess, and there's some chance the "can't > write .Last.value" error (wording approximate) cause by > having an old version is screwing things up). > > Since R is interpreted I would expect debugging to be a snap, > but these limitations make me suspect there is something > about the language design that makes implementing these > facilities hard. For example, the browser as documented in > the Green book has up and down functions to change the frame > (p. 265); these are conspicuously absent in R. > -- > Ross Boylan wk: (415) 514-8146 > 185 Berry St #5700 ross at biostat.ucsf.edu > Dept of Epidemiology and Biostatistics fax: (415) 514-8150 > University of California, San Francisco > San Francisco, CA 94107-1739 hm: (415) 550-1062 > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel > >
Mark.Bravington at csiro.au
2007-Jan-04 23:16 UTC
[Rd] Am I missing something about debugging?
Hi Ross> 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?Yes. But there are a couple of tricks you could use to avoid it. First (less neat), you could 'mtrace( b1)' and then set two fake conditional breakpoints inside 'b1': one of '{mtrace( c1);F}' before the first call to 'c1', and one of '{mtrace(c1,F);F}' just after. Second (neater), you could avoid 'mtrace'ing 'b1' altogether, and instead replace the default breakpoint at line 1 in 'c1' [SEE ** BELOW] with a conditional breakpoint. The conditional breakpoint would have to be a bit clever-- it might have to inspect the value of a variable in 'b1' or something like that. For example, suppose you wrote 'b1' like this: c1.call.count <- 1 c1( ...) repeat{ c1.call.count <- c1.call.count+1 blah.blah.blah() c1(...) } Then you could 'mtrace( c1)' and do 'bp( 1, { get( 'c1.call.count', envir=sys.frame( mvb.sys.parent()))==1, fname='c1')' before running 'b1'. Lo & behold, it will only stop in 'c1' on the first call, not on subsequent ones. [I guess I could add a pass count feature to the debugger, to make this easier.] Note that you can call 'bp' without actually being in debug mode, by using the 'fname' parameter-- the problem is that it is not usually obvious what line number to set the breakpoint at (though you can find out by inspecting 'tracees$f'). You can alternatively just 'mtrace( c1)' and then reset the breakpoint manually the first time it comes up. One thing I noticed while trying the stuff above, is that you do need to use 'mvb.sys.parent' instead of 'sys.parent' in the above. 'mvb.sys.parent' and friends are the "do what I mean, not what I say" equivalents of 'sys.parent' etc for use inside the debugger. The 'debug' package goes to some trouble to replace calls to 'sys.parent' with calls to the 'mvb...' equivalents, both in user input and in the debuggee function, but evidently doesn't do so inside breakpoint expressions. Consider it a feature... The frame stack is a thing of great weirdness, by the way; the same frame can appear several times in the stack, especially when debugging. I would not recommend trying to really understand it unless you really have to; the 'debug' package makes efforts to avoid you having to!> > 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?True, for the debug package. Not sure about vanilla R debugging (dissatisfaction with which drove me to write the 'debug' package-- so I don't use the vanilla stuff these days!).> > > > > 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?It won't cause a problem; the 'debug' package notices when a function has already been 'mtrace'd (by inspecting the actual function body) and doesn't add another "layer".> > > 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?[**] Not exactly-- but read on. If 'f' is running, you can't un-'mtrace' it. However, you can prevent the debugger *stopping* when it next enters 'f' (the difference then is only one of speed). When you 'mtrace' a function, a breakpoint is set on line 1, to ensure that the debugger halts for user input when the function is entered. (You'll see a red asterisk in the code window at line 1, to show that there's some kind of breakpoint there.) What 'bp(1,F)' does, is disable that breakpoint, so that the debugger will keep plowing through the code of 'f' without halting (until another breakpoint or an error). The code window isn't displayed until/unless the debugger actually halts.> > > > > 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 number shown in the 'debug' package (ie the number xxx in the "D(xxx)>" prompt, and in the separate code windows) is the true frame number. So I will quite often have two code windows visible, one saying "outerfunc(4)" and the other [currently active] saying "innerfunc(17)". Typing 'sys.nframe' will return 17 and 'sys.parent()' will return 4. again, not sure about vanilla debug.> > > > 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.I think the main issue is ensuring that the watch expressions don't take overwhelmingly long to display.... imagine if you have some really huge object resulting from a watch expression which takes 5 minutes to print-- how do you get R to display only the first 2 lines "quickly"? What about things that have weird attributes? What if there is a bug in the print method for the class of object, or a mismatch between the object's contents and the expectation of the print method? All sorts of awkwardness can & does happen... Sod's law at work, basically. I haven't looked into how far things like 'str' could make it painless these days. HTH Mark Mark Bravington CSIRO Mathematical & Information Sciences Marine Laboratory Castray Esplanade Hobart 7001 TAS ph (+61) 3 6232 5118 fax (+61) 3 6232 5012 mob (+61) 438 315 623> > > 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). > >