Version 3 of Dynamic call graph

Updated 2005-07-13 18:40:55

if 0 {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

if 0 {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}
 }

Here is some code that does Static call graph


Category Debugging | Arts and crafts of Tcl-Tk programming }