在R中捕获错误后如何获取堆栈跟踪信息

22

这与其他一些问题有关,但我似乎无法理解如何应用答案,因此我提出了一个新问题。

我正在尝试解决一个看起来像这样的代码片段中的不具有信息性的错误:

tryCatch(MainLoop(), 
  error=function(e) { fatal(lgr, paste('caught fatal error:', as.character(e))); 
                      exit.status <<- 1 })

问题在于错误似乎与嵌入在库函数中的某些内容有关:

Error in nrow(x): (subscript) logical subscript too long

代码中没有nrow,因为上面的 C 级错误仅适用于一种类型的索引,而这种索引从未出现在我的任何 nrow 调用中。

因此,我真的很想从那个 tryCatch 中获得堆栈跟踪。这是一个类似的问题:

x <- function() { y(); }
y <- function() { z(); }
z <- function() { stop("asdf") }

> x()
Error in z() : asdf
> tryCatch(x(), error=function(e) { print(conditionCall(e)) } )
z()
> tryCatch(x(), error=function(e) { dump.frames() } )
> last.dump
$`tryCatch(x(), error = function(e) {
    dump.frames()
})`
<environment: 0x1038e43b8>

$`tryCatchList(expr, classes, parentenv, handlers)`
<environment: 0x1038e4c60>

$`tryCatchOne(expr, names, parentenv, handlers[[1]])`
<environment: 0x1038e4918>

$`value[[3]](cond)`
<environment: 0x1038ea578>

attr(,"error.message")
[1] "asdf"
attr(,"class")
[1] "dump.frames"

如何获取包括对 y() 的调用的堆栈跟踪?我必须停止使用 tryCatch 吗?有更好的方法吗?


你在错误发生后尝试了哪些调试方法? traceback() options(error=recover)? 后者不会让你进入堆栈吗?即使在库中,它也应该这样做,直到至少遇到C代码。如果你遇到了C代码,那么你可能需要使用带有R的GDB。 - Robert
这是针对在生产环境中运行的代码,我遇到了一个在开发中尚未复现的错误,因此不幸的是无法进入恢复模式...我希望更好的仪器设备能够更准确地指出错误。谢谢! - Harlan
4个回答

20

为了交互式使用,可以使用 trace(stop, quote(print(sys.calls()))) 打印在调用 stop() 时的调用堆栈。

来源: ?tryCatch

 The function 'tryCatch' evaluates its expression argument in a
 context where the handlers provided in the '...'  argument are
 available.

然而

 Calling handlers are established by 'withCallingHandlers'...
 the handler is called... in the context where the condition
 was signaled...

所以

>     withCallingHandlers(x(), error=function(e) print(sys.calls()))
[[1]]
withCallingHandlers(x(), error = function(e) print(sys.calls()))

[[2]]
x()

[[3]]
y()

[[4]]
z()

[[5]]
stop("asdf")

[[6]]
.handleSimpleError(function (e) 
print(sys.calls()), "asdf", quote(z()))

[[7]]
h(simpleError(msg, call))

Error in z() : asdf

如果有内部的tryCatch,则会受到阻碍。

withCallingHandlers({
    tryCatch(x(), error=function(e) stop("oops"))
}, error=function(e) print(sys.calls()))

由于我们只能在tryCatch已经“处理”错误后才能访问调用栈。


1
这很有道理,看起来能够工作 - 谢谢!我已经用 withCallingHandlers 替换了 tryCatch,并在错误处理程序块中放置了以下方便的表达式:dump.frames(format(Sys.time(), format="dump_%Y%m%d%H%M%S"), to.file=TRUE)。希望现在我能够找出这个 bug... - Harlan

3

我很喜欢evaluate::try_capture_stack()

x <- function() {
  y()
}
y <- function() {
  z()
}
z <- function() {
  stop("asdf")
}
env <- environment()
e <- evaluate::try_capture_stack(quote(x()), env)
names(e)
#> [1] "message" "call"    "calls"
e$calls
#> [[1]]
#> x()
#> 
#> [[2]]
#> y()
#> 
#> [[3]]
#> z()
#> 
#> [[4]]
#> stop("asdf")

3
是的,这是可能的。虽然代码不太优雅,但输出非常有用!欢迎任何评论!
我将其放在我的misc包中,如果您需要文档,请从那里使用。 https://github.com/brry/berryFunctions/blob/master/R/tryStack.R 下一个CRAN版本计划很快发布,在此之前:
devtools::install_github("brry/berryFunctions")
# or use:
source("http://raw.githubusercontent.com/brry/berryFunctions/master/R/instGit.R")
instGit("brry/berryFunctions")

library(berryFunctions)
?tryStack

这是快速参考:

tryStack <- function(
expr,
silent=FALSE
)
{
tryenv <- new.env()
out <- try(withCallingHandlers(expr, error=function(e)
  {
  stack <- sys.calls()
  stack <- stack[-(2:7)]
  stack <- head(stack, -2)
  stack <- sapply(stack, deparse)
  if(!silent && isTRUE(getOption("show.error.messages"))) 
    cat("This is the error stack: ", stack, sep="\n")
  assign("stackmsg", value=paste(stack,collapse="\n"), envir=tryenv)
  }), silent=silent)
if(inherits(out, "try-error")) out[2] <- tryenv$stackmsg
out
}

lower <- function(a) a+10
upper <- function(b) {plot(b, main=b) ; lower(b) }

d <- tryStack(upper(4))
d <- tryStack(upper("4"))
cat(d[2])

d <- 尝试堆栈(upper("4"))

这是错误堆栈:

尝试堆栈(upper("4"))

upper("4")

lower(b)

a + 10的错误:二进制运算符的非数值参数


0
我来晚了,但我发现最好的方法是在你所尝试的函数中使用一个退出处理程序。
main <- function()
{
  on.exit({
    msg <- capture.output(traceback())
    if (msg != "No traceback available ")
    {
      print(msg)
    }
  }
  )
  # rest of code
}

withCallingHandlers(
  expr =
    {
      main()
    },
  error = function(e) 
  {
    print(e)
  }
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接