What is a fast way to set debugging code at a given line in a function?
debugging, r
Solution
Here is a convenient wrapper for detecting the piece:
library(codetools)
ff <- function(f, tar) {
cc <- function(e, w) {
if(length(w$pos) > 0 &&
grepl(w$tar, paste(deparse(e), collapse = "\n"), fixed = TRUE)) {
cat(rev(w$pos), ": ", deparse(e), "\n")
w$ret$vals <- c(w$ret$vals, list(rev(w$pos)))
}
w$pos <- c(0, w$pos)
for (ee in as.list(e)){
if (!missing(ee)) {
w$pos[1] <- w$pos[1] + 1
walkCode(ee, w)
}
}
}
w <- list(pos = c(),
tar = tar,
ret = new.env(),
handler = function(v, w) NULL,
call = cc,
leaf = function(e, w) NULL)
walkCode(body(f), w = w)
w$ret$vals
}
and then,
> r <- ff(pretty.default, "delta <- diff(range(z$l, z$u))/z$n")
12 : if (!eps.correct && z$n) { delta <- diff(range(z$l, z$u))/z$n if (any(small <- abs(s) < 1e-14 * delta)) s[small] <- 0 }
12 3 : { delta <- diff(range(z$l, z$u))/z$n if (any(small <- abs(s) < 1e-14 * delta)) s[small] <- 0 }
12 3 2 : delta <- diff(range(z$l, z$u))/z$n
> r
[[1]]
[1] 12
[[2]]
[1] 12 3
[[3]]
[1] 12 3 2
> r <- ff(model.frame.default, "stop(gettextf(\"factor '%s' has new level(s) %s\", nm, paste(nxl[m],")
26 3 2 4 3 4 4 4 3 : stop(gettextf("factor '%s' has new level(s) %s", nm, paste(nxl[m], collapse = ", ")), domain = NA)
> r
[[1]]
[1] 26 3 2 4 3 4 4 4 3
and you can define the tracer by contents:
traceby <- function(fun, tar, cer) {
untrace(deparse(substitute(fun)))
r <- ff(fun, tar)
r <- r[which.max(sapply(r, length))]
trace(what = deparse(substitute(fun)), tracer = cer, at = r)
}
then,
> traceby(pretty.default, "if (any(small <- abs(s) < 1e-14 * delta)) s[small] <- 0", quote(cat("\nThe value of delta is: ", delta, "\n\n")))
Untracing function "pretty.default" in package "base"
12 3 3 : if (any(small <- abs(s) < 1e-14 * delta)) s[small] <- 0
Tracing function "pretty.default" in package "base"
[1] "pretty.default"
> a <- pretty(c(1, 7843))
Tracing pretty.default(c(1, 7843)) step 12,3,3
The value of delta is: 2000
> b <- pretty(c(2, 23))
Tracing pretty.default(c(2, 23)) step 12,3,3
The value of delta is: 5
Problem
Preamble: R's `trace()` is a powerful debugging tool, allowing users to "insert debugging code at chosen places in any function". Unfortunately, using it from the command-line can be fairly laborious. As an artificial example, let's say I want to insert debugging code that will report the between-tick interval calculated by `pretty.default()`. I'd like to insert the code immediately after the value of `delta` is calculated, about four lines up from the bottom of the function definition. (Type `pretty.default` to see where I mean.) To indicate that line, I need to find which step in the code it corresponds to. The answer turns out to be step `list(c(12, 3, 3))`, which I zero in on by running through the following steps: ``` as.list(body(pretty.default)) as.list(as.list(body(pretty.default))[[12]]) as.list(as.list(as.list(body(pretty.default))[[12]])[[3]]) as.list(as.list(as.list(body(pretty.default))[[12]])[[3]])[[3]] ``` I can then insert debugging code like this: ``` trace(what = 'pretty.default', tracer = quote(cat("\nThe value of delta is: ", delta, "\n\n")), at = list(c(12,3,3))) ## Try it a <- pretty(c(1, 7843)) b <- pretty(c(2, 23)) ## Clean up untrace('pretty.default') ``` Questions: So here are my questions: Is there a way to print out a function (or a parsed version of it) with the lines nicely labeled by the steps to which they belong? (According to Venables and Ripley, S-plus has a function `tprint()` that "produces a numbered listing of the body of a function for use with the `at` argument of `trace`", but R seems to have no equivalent.) Alternatively, is there another easier way, from the command line, to quickly set debugging code for a specific line within a function? Addendum: I used the `pretty.default()` example because it is reasonably tame, but with real/interesting functions, repeatedly using `as.list()` quickly gets tiresome and distracting. Here's an example: ``` as.list(as.list(as.list(as.list(as.list(as.list(as.list(as.list(as.list(body(# model.frame.default))[[26]])[[3]])[[2]])[[4]])[[3]])[[4]])[[4]])[[4]])[[3]] ```