rename proc _proc _proc proc {name argl body} { _proc $name $argl callgraph'report\n$body } #-- This argument-less proc is prefixed to every proc defined with the overloaded command: _proc callgraph'report {} { if [catch {info level -2} res] {set res ""} set ::callgraph([lindex $res 0],[lindex [info level -1] 0]) "" }#--- Testing
proc a {} {b; c } proc b args {d; e} proc c {} {b; d} proc d {} {} proc e {} {} a parray callgraphwhich returns (the values are always "", a key a,b expresses that a called b):
callgraph(,a) = callgraph(,parray) = callgraph(a,b) = callgraph(a,c) = callgraph(b,d) = callgraph(b,e) = callgraph(c,b) = callgraph(c,d) =If there is nothing left of the comma, the procedure was called outside of any proc, i.e. interactively or at script toplevel. Note that parray was also instrumented, because its file was sourced after the overloading of proc. You can do further analyses on the callgraph array, for instance find out all callers of b:
array names callgraph *,bor all procedures that b called:
array names callgraph b,*Another enhancement would be to record the number an edge of the callgraph was traversed, by counting up:
_proc callgraph'report {} { if [catch {info level -2} res] {set res ""} set edge [lindex $res 0],[lindex [info level -1] 0] if [info exists ::callgraph($edge)] { incr ::callgraph($edge) } else {set ::callgraph($edge) 1} }
I used the same idea to generate a history of the procedure calls to stderr - VPT
_proc proc {name arg body} { uplevel [list _proc $name $arg "puts stderr \[string repeat { } \[info level]]$name\n$body"] }The uplevel is needed for commands within namespaces. (Thanks to Ralf Fassel) EvilSon
Here is some code that does Static call graph
Here is another approach to tracing procedure calls ... Pstack. The advantage to this approach is that only the procedures of interest are traced which is helpful when debugging. The output is also indented based on call depth. tjk