Stack trace information in errorInfo in Tcl

If you are using Tcl interactively, in which I enter the following:

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

it will show a fragment of very short information:

 can't read "element": no such variable 

but when i use

 puts $errorInfo 

it shows:

 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] }" 

what i really want.

The problem is that in silent mode, when I want to catch this error and then put errorInfo to get the stack trace, it will just display a brief information. How can I get a detailed stack trace as above? Many thanks!

Edited to add additional information

Let's say I have the following code:

 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 

If I uncomment "# test1", it will show:
cannot read "element": no such variable
when executing "expr $ sum + $ element"

If I uncomment "# test2", it will show:
can't read the "element": no such variable
when executing "expr $ sum + $ element"
(procedure "test2" line 5)
called from within "Test2"
(file ". /test.tcl", line 137)

I want, of course, the behavior of test2. How can I display this error information using catch?

+4
source share
3 answers

Can you show how you capture / put information in non-interactive mode?

If you did

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

Then the behavior that you described is expected - $err has only "brief information". Instead of puts you might want to:

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

The :: prefix is ​​required if your catch is called inside a proc or namespace to make sure that the variable you are using is the actual toplevel :: errorInfo.

Edited for addressing

As Colin answered , the stack traces found in your test1 and test2 are different in that you put catch. Let me illustrate. Here are a few Tcl chains:

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

If you rate

 catch {four} puts $::errorInfo 

You will only get a stack trace that looks like this:

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

This is because the stack trace between where the error occurred (inside four ) and where you caught it, there is only one procedure call.

If instead you caught the “next” error as follows:

 catch {one} puts $::errorInfo 

The stack trace between the catch statement and the error includes procs one , two , three and four . This leads to a stack trace as follows:

 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" 

So ... to match your example for test1 , if you redefined three as follows:

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

And you rated

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

You might not see the “error found” because the body of three caught the error and printed a stack trace. A stack trace containing calls between the catch statement and the error - which (like my first example) consists of just calling four .

So where do you put your catch expressions.


In the corresponding note, you can re-throw the error by saving the stack trace if you want. Here's the new definition for three :

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

Now, with a second error, you will see the following:

 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" 
+8
source

Given your further clarification of your question, the following may work for you:

 # 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 {xy} { 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 {abc} { test3 AB } # 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 AB while executing: test4 1 2 3 
+1
source

It depends on how close to the site the errors you get. The stack trace in errorInfo is built as the stack unwinds from the error site to the first spanning catch or top level - see the tutorial . Therefore, if you have a catch in proc where an error occurs, it has no way to build a stack trace in errorInfo.

0
source

Source: https://habr.com/ru/post/1300201/


All Articles