【问题标题】:Recommendations for "Dynamic/interactive" debugging of functions in R?R中函数的“动态/交互式”调试建议?
【发布时间】:2010-07-09 12:28:50
【问题描述】:

在调试函数时我通常使用

library(debug)
mtrace(FunctionName)
FunctionName(...)

这对我来说效果很好。

但是,有时我会尝试调试一个我不知道的复杂函数。在这种情况下,我可以发现在该函数内部还有一个我想“进入”(“调试”)的函数——以便更好地了解整个过程是如何工作的。

所以一种方法是这样做:

library(debug)
mtrace(FunctionName)
FunctionName(...)
# when finding a function I want to debug inside the function, run again:
mtrace(FunctionName.SubFunction)

问题是 - 是否有更好/更智能的方式来进行交互式调试(正如我所描述的)我可能会错过?

p.s:我知道那里有关于 SO 主题的各种问题(请参阅here)。然而,我无法遇到与我在这里提出的问题类似的问题/解决方案。

【问题讨论】:

    标签: debugging r


    【解决方案1】:

    不完全确定用例,但是当遇到问题时,可以调用函数traceback()。这将显示您的函数调用通过堆栈的路径,直到它遇到问题。如果您倾向于从顶部向下工作,则可以在进行函数调用之前对列表中给出的每个函数调用debug。然后你会从一开始就经历整个过程。

    下面是一个示例,说明如何以更系统的方式执行此操作,方法是创建一个逐步执行的函数:

    walk.through <- function() {
      tb <- unlist(.Traceback)
      if(is.null(tb)) stop("no traceback to use for debugging")
      assign("debug.fun.list", matrix(unlist(strsplit(tb, "\\(")), nrow=2)[1,], envir=.GlobalEnv)
      lapply(debug.fun.list, function(x) debug(get(x)))
      print(paste("Now debugging functions:", paste(debug.fun.list, collapse=",")))
    }
    
    unwalk.through <- function() {
      lapply(debug.fun.list, function(x) undebug(get(as.character(x))))
      print(paste("Now undebugging functions:", paste(debug.fun.list, collapse=",")))
      rm(list="debug.fun.list", envir=.GlobalEnv)
    }
    

    这是一个使用它的虚拟示例:

    foo <- function(x) { print(1); bar(2) }
    bar <- function(x) { x + a.variable.which.does.not.exist }
    foo(2)
    
    # now step through the functions
    walk.through() 
    foo(2)
    
    # undebug those functions again...
    unwalk.through()
    foo(2)
    

    IMO,这似乎不是最明智的做法。简单地进入发生问题的函数(即在最低级别)并向后工作会更有意义。

    我已经在"favorite debugging trick" 中概述了这个基本例程背后的逻辑。

    【讨论】:

    • 感谢 Shane,我可以将您的代码与 mtrace 一起使用,这在某些情况下可能会很好。但总的来说,我同意你关于自下而上调试的观点。
    • 嗨,Shane,再三考虑。我们可以从 traceback 中提取函数列表,以便我们只能在它们上运行您的函数吗?
    • 这正是我的 walk.through 函数所做的。
    • -1 对我来说,起床后第一件事就是阅读您的回复,而不是阅读它:(很好的答案和代码 - 我可以想象我会找到用途的案例!谢谢!
    【解决方案2】:

    我喜欢options(error=recover),详细的previously on SO。然后事情就停在错误的地方,人们可以进行检查。

    【讨论】:

    • 谢谢德克。我的问题源于我的代码中的错误来自以前的步骤以及奇怪的最终情况。我同意第一件事应该是你的选择。如果我们正在讨论这个问题,您是否认为可以使用类似于 mtrace(来自 {debug})而不是基本 browser() 来要求函数恢复?
    • 现在我做到了,它错误:[1] 1 bar(2) 中的错误:未找到对象'a.variable.which.does.not.exist' 总结期间错误:第一个参数无效
    • 我不知道 mtrace 是如何工作的,但是您可以创建自己的错误条件以正确使用它:options(error=function(x) mtrace(x, ...))
    • @Dirk:虽然我普遍同意这种方法,但我认为它并不能真正解决问题:他想遍历函数以查看在错误发生之前会发生什么......跨度>
    • @Tal:我不知道如何使用 mtrace:您必须自己弄清楚。但是您可以创建自己的自定义错误函数是我的一般观点。
    【解决方案3】:

    (我是 'mtrace' 所在的 'debug' 包的作者)

    如果 'SubFunction' 的定义位于 'MyFunction' 之外,那么您可以只跟踪 'SubFunction' 而不需要 mtrace 'MyFunction'。如果函数没有'mtrace'd,它们运行得更快,所以最好只根据需要进行mtrace。 (但你可能已经知道这些事情了!)

    如果 'MyFunction' 仅在 'SubFunction' 中定义,一个可能有帮助的技巧是在 'MyFunction' 中使用条件断点。你需要'mtrace(MyFunction)',然后运行它,当调试窗口出现时,找出'MyFunction'定义在哪一行。假设它是第17行。那么以下应该工作:

    D(n)> bp(1, F) # 不要再为 MyFunction 显示窗口了 D(n)> bp( 18, { mtrace(SubFunction); FALSE}) D(n)> 去()

    应该清楚这是做什么的(或者如果你尝试它就会是)。

    唯一的缺点是:每当您更改“MyFunction”的代码时都需要再次执行此操作,并且;跟踪“MyFunction”本身可能会出现减速。

    您还可以尝试将“debug.sub”参数添加到“MyFunction”,默认为 FALSE。在'MyFunction'的代码中,紧接着'SubFunction'的定义后加入这一行:

    if(debug.sub) mtrace(SubFunction)

    这避免了跟踪“MyFunction”本身的任何需要,但确实需要您能够更改其代码。

    【讨论】:

    • 你好马克,很高兴你加入讨论。我强烈支持您的软件包,并且喜欢使用它(几乎每天都使用它)。你给的建议很有趣。从上面的讨论中,我试图弄清楚如何处理 mtrace 的一种能力——即自动“mtrace”-ing 涉及 bug 的所有(或选定的)函数。 Shane 在他的回答中给出的功能很好地证明了这一点。当错误出现在断点附近时,需要就来了。再次感谢您提供所有代码和时间!
    猜你喜欢
    • 2018-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    相关资源
    最近更新 更多