Dynamic call graph

Richard Suchenwirth 2005-07-13 - Here's a simple sketch how you can get the "dynamic call graph" (which shows which procedure actually called which other in a program run). The idea is to overload the proc command, so that every proc body is augmented (in the beginning) with a call to a helper that records the names of caller and callee in a global array:

 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 callgraph

which 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 *,b

or 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