List the call stack

Difference between version 15 and 16 - Previous - Next
The following will list the entire call stack at entry to a proc or method. 

======
    proc x {a} {
       puts "Entered x, args $a"
       set distanceToTop [info level]
       for {set i 1} {$i < $distanceToTop} {incr i} {
          set callerlevel [expr {$distanceToTop - $i}]
          puts "CALLER $callerlevel: [info level $callerlevel]"
      }
    # ...
       return
    }
======

[KPV] Here's another version which prints out argument values
(even default one). (See also [Pstack].)
======
proc stacktrace {} {
    set stack "Stack trace:\n"
    for {set i 1} {$i < [info level]} {incr i} {
        set lvl [info level -$i]
        set pname [lindex $lvl 0]
        append stack [string repeat " " $i]$pname
        foreach value [lrange $lvl 1 end] arg [info args $pname] {
            if {$value eq ""} {
                info default $pname $arg value
            }
            append stack " $arg='$value'"
        }
        append stack \n
    }
    return $stack
}

# Testing code

proc A { } {
    B 3
}
proc B { x } {
    C [incr x] 2
}
proc C { x y {z 5} } {
    D [incr x] [incr y] $z
}
proc D { x y z} {
    E [incr x] [incr y] [incr z]
}
proc E {x y z } {
    stacktrace
}
A
======

[Napier] notes that the above solutions will not work if you are using TclOO as [info args my] will result in an error.

[DKF]: Yes, there's other mechanisms for introspecting TclOO methods on the stack. The [self class]/[self method] command ''when run in the right stack scope'' will be useful, as will [info class definition] and/or [info object definition]: [self class] and [self method] say what is running, and [info class definition] is like [info args] and [info body] rolled into one. (Methods aren't ''quite'' procedures. They're just very similar.)

[Martyn Smith] Notes that this does not work for namespace procedures either which are not qualified when called, the [info args $pname] generates an error because pname is not qualified.

======
namespace eval ns {
    proc a {} {
        puts ns::a
        b hello
    }
    proc b {x} {
        puts ns::b
        puts "=========\n[stacktrace]\n=========="
    }
}
ns::a
======

CRN: For solving namespace issue you can use '''info frame'''. Here a small exemple :
======
proc callstack {} {
    set stack [list "Stacktrace:"]

    for {set i 1} {$i < [info level]} {incr i} {
        set level [info level -$i]
        set frame [info frame -$i]

        if {[dict exists $frame proc]} {
            set pname [dict get $frame proc]
            set pargs [lrange $level 1 end]
            lappend stack " - $pname"
            foreach arg $pargs {
                lappend stack "   * $arg"
            }
        } else {
            lappend stack " - **unknown stack item**: $level $frame"
        }
    }

    return [join $stack "\n"]
}
======
[DcK] Building an error handler to report issues to monitoring systems like Sentry,
I needed a more programmatic-oriented stacktrace, and that's exactly what you can
get with by combining [info frame -$n] and [info level -$n] information. As frames can go
further than level, the second one is only partially available.

Based on crn code that becomes:
======
proc callstack {} {
    set frames {}

    set maxlevel [info level]
    for {set i 0} {$i < [info frame]} {incr i} {
        set frame [info frame -$i]
        if {[dict exists $frame level]} {
            set level [dict get $frame level]
            if {$level < $maxlevel} {
                dict append frame level_info [info level -$level]
            }
        }
        lappend frames $frame
    }

    return $frames
}
======


<<categories>> Debugging