在Tcl中的errorInfo中堆栈跟踪信息

时间:2010-02-03 17:02:03

标签: tcl stack-trace

如果在交互模式下使用Tcl,我在其中输入以下内容:

set list {1 2 3 4 5}
set sum 0
foreach el $list {
    set sum [expr $sum + $element]
}

它将显示一条非常简洁的信息:

can't read "element": no such variable

但是当我使用

puts $errorInfo

它会显示:

can't read "element": no such variable
      while executing
"expr $sum + $element"
      ("foreach" body line 2)
      invoked from within
"foreach el $list {
      set sum [expr $sum + $element]
 }"

这就是我真正想要的。

问题是:在非交互模式下,当我想捕获此错误然后放入errorInfo以获取堆栈跟踪时,它只会显示简洁信息。如何获得如上所述的详细堆栈跟踪?非常感谢!

已编辑添加更多详情

说我有以下代码:

proc test1 {} {
    set list {1 2 3 4 5}
    set sum 0
    foreach el $list {
    if {[catch {set sum [expr $sum + $element]} err]} {
        puts $::errorInfo
    }
    break 
    }
}
proc test2 {} {
    foreach el $list {
    set list {1 2 3 4 5}
    set sum 0
    set sum [expr $sum + $element]
    }
}    
#test1
#test2

如果我取消注释“#test1”,它将显示:
不能读“元素”:没有这样的变量
执行时 “expr $ sum + $ element”

如果我取消注释“#test2”,它将显示:
不能读“元素”:没有这样的变量
    执行时 “expr $ sum + $ element”
    (程序“test2”第5行)
    从内部调用 “TEST2”
    (档案“./test.tcl”第137行)

我想要的当然是test2行为。如何使用catch?

显示此错误信息

3 个答案:

答案 0 :(得分:8)

您能说明如何以非交互模式捕获/放置信息吗?

如果你做了

if {[catch {...your...code...here...} err]} {
   puts "Error info $err"
}

然后,您所描述的行为是预期的 - $err只有“简洁信息”。您可能希望puts改为:

   puts "Error info $err\nFull info: $::errorInfo"

如果在proc或命名空间内调用catch以确保您使用的变量是实际的toplevel :: errorInfo,则需要前缀::

已编辑以解决后续问题

作为Colin answeredtest1test2中的堆栈跟踪会因您放置捕获的位置而有所不同。让我来说明一下。这是一些链式Tcl过程:

proc one {} {
  two
}
proc two {} {
  three
}
proc three {} {
  four
}
proc four {} {
  error "Yup, an error"
}

如果您评估

catch {four}
puts $::errorInfo

您将获得如下所示的堆栈跟踪:

Yup, an error
    while executing
"error "Yup, an error""
    (procedure "four" line 2)
    invoked from within
"four"

这是因为错误发生的位置(在four内)和捕获它的位置之间的堆栈跟踪,只有一个过程调用。

如果您反而意外地“更远”地捕获了错误:

catch {one}
puts $::errorInfo

catch语句与错误之间的堆栈跟踪包括过滤器onetwothreefour。这导致堆栈跟踪如下:

Yup, an error
    while executing
"error "Yup, an error""
    (procedure "four" line 2)
    invoked from within
"four"
    (procedure "three" line 2)
    invoked from within
"three"
    (procedure "two" line 2)
    invoked from within
"two"
    (procedure "one" line 2)
    invoked from within
"one"

所以...要匹配test1的示例,如果您重新定义three,如下所示:

proc three {} {
  catch {four}
  puts $::errorInfo
}

你评估了

if {[catch {one}]} {
   puts "Found an error"
}

会看到“发现错误”,因为three的正文捕获了错误,并打印了堆栈跟踪。堆栈跟踪仅包含catch语句和错误之间的调用 - 这(就像我的第一个示例)仅包含对four的调用。

因此,您将catch语句置于何处。


在相关说明中,您可以重新抛出错误,如果您愿意,可以保留堆栈跟踪。以下是three的新定义:

proc three {} {
  if {[catch {four} err]} {
    puts "Caught an error $err, re-throwing"
    error $err $::errorInfo
  }
}

现在,有了重新抛出的错误,你会看到:

tchsh% catch {one}
Caught an error Yup, an error, re-throwing
1
tclsh% set ::errorInfo
Yup, an error
    while executing
"error "Yup, an error""
    (procedure "four" line 2)
    invoked from within
"four"
    (procedure "three" line 2)
    invoked from within
"three"
    (procedure "two" line 2)
    invoked from within
"two"
    (procedure "one" line 2)
    invoked from within
"one"

答案 1 :(得分:1)

鉴于您对问题的进一步完善,以下内容可能对您有用:

# gets the stack up to the caller
proc get_stack {} {
    set result {}
    for {set i [expr {[info level] -1}]} {$i >0} {incr i -1} {
        lappend result [info level $i]
    }
    return $result
}

# formats the stack for display
proc format_stack {stackList} {
    return "\twhile executing: [join $stackList \n\twhile\ executing:\ ]"
}

# test function that has an error
proc test3 {x y} {
    set list {1 2 3 4 5}
    set sum 0
    foreach el $list {
        if {[catch {set sum [expr $sum + $element]} err]} {
            puts "$::errorInfo\n[format_stack [get_stack]]"
        }
        break 
    }
}

# wrapper test function, just so we can show the call stack more visibly
proc test4 {a b c} { 
    test3 A B
}

# and, we call it and show the output
% test4 1 2 3
can't read "element": no such variable
    while executing
"expr $sum + $element"
    while executing: test3 A B
    while executing: test4 1 2 3

答案 2 :(得分:0)

这取决于您捕获错误网站的距离。 errorInfo中的堆栈跟踪是在堆栈从错误站点展开回到第一个封闭捕获或顶层时构建的 - 请参阅tutorial。因此,如果你在proc中发现了错误,那么它就没有机会在errorInfo中构建堆栈跟踪。