Flow tracing

ulis, 2003-11-24. A hacked procs flow tracing package that I use now intensively.


Result

 if 0 {
  ...
  | <>option:key -text
  |   <- -text
  | <>value:check string light
  |   <- light
  | <>option:key -bg
  |   <- -background
  | <>value:check color green4
  |   <- green4
  | <>option:key -abg
  |   <- -activebackground
  | <>value:check opt-color red1
  |   <- red1
  | >>post:process .lb fi
  | | <>create:gradient .lb red1
  | |   <- image1
  | | <>create:gradient .lb green4
  | |   <- image2
  ----{.lb.c coords 1 20 20}
  ----{.lb.c coords 2 20 20}
  ----{.lb.c coords 3 20 20}
  ----{.lb.c coords 4 20 20}
  | <<post:process .lb fi
  | <>post:process .lb fs
  | <>post:process .lb fc
  | <>post:process .lb fr
  <<::lightbutton::lb:create .lb -text light -bg green4 -abg red1
    <- .lb
  <<::lightbutton::lb:dispatch lb .lb -text light -bg green4 -abg red1
    <- .lb
  <>tk::ScreenChanged :0.0
  <>::lightbutton::lb:dispose .lb
 }

Use

 if 0 {
    # control if proc should be traced
    activate:trace
    deactivate:trace

    # control if enter/leave should be displayed
    start:trace
    stop:trace

    # control displayed procs list
    ignore:procs
    know:procs

    # display data
    display:data
 }

Package

  if {[info exists ::flow::version]} { return }
  namespace eval ::flow \
  {
  # beginning of ::flow namespace definition

  # ####################################
  #
  #   flow
  #
  variable version 0.9
  #
  #   ulis, (C) 2003
  #
  # ####################################

    # ==========================
    #
    # entry points
    #
    # ==========================

    namespace export activate:trace deactivate:trace
    namespace export start:trace stop:trace
    namespace export ignore:procs know:procs
    namespace export flow:display

    # ====================
    #
    #   global variables
    #
    # ====================

    variable {}
    set (active) 0
    set (start) 0
    set (ignore) {}
    set (last) ""

    # ==========================
    #
    # package
    #
    # ==========================

    package provide Flow $version

    package require Tcl 8.4

    rename ::proc ::flow::PROC

    # ==========================
    #
    # activate/deactivate
    #
    # ==========================

    # control if proc should be traced

    ::flow::PROC activate:trace {} { variable {}; incr (active) 1 }
    ::flow::PROC deactivate:trace {} { variable {}; incr (active) -1 }

    # ==========================
    #
    # start/stop
    #
    # ==========================

    # control if enter/leave should be displayed

    ::flow::PROC start:trace {} { variable {}; set (start) 1 }
    ::flow::PROC stop:trace {} { variable {}; set (start) 0 }

    # ==========================
    #
    # ignore/know
    #
    # ==========================

    # control displayed procs list

    ::flow::PROC ignore:procs {args} \
    {
      variable {}
      foreach proc $args \
      {
        if {[lsearch -exact $(ignore) $proc] == -1} \
        { lappend (ignore) $proc }
      }
    }

    ::flow::PROC know:procs {args} \
    {
      variable {}
      foreach proc $args \
      {
        if {[set n [lsearch -exact $(ignore) $proc]] != -1} \
        { set (ignore) [lreplace $(ignore) $n $n] }
      }
    }

    # ==========================
    #
    # display
    #
    # ==========================

    # display data

    ::flow::PROC flow:display {args} \
    {
      if {!$::flow::(active) || !$::flow::(start)} { return }
      set lvl [info level]
      set pref [string repeat "--" [incr lvl -1]]
      puts $pref[string map [list \n \n$pref] $args]
    }

    # ==========================
    #
    # trace mechanism
    #
    # ==========================

    ::flow::PROC ::proc {name parms script} \
    {
      uplevel 1 [list ::flow::PROC $name $parms $script]
      if {$::flow::(active)} \
      { uplevel 1 [list trace add execution $name {enter leave} ::flow::flow] }
    }

    ::flow::PROC ::flow::flow {args} \
    {
      if {!$::flow::(active) || !$::flow::(start)} { return }
      set lvl [info level]
      set pref [string repeat "| " [incr lvl -1]]
      switch [lindex $args end] \
      {
        enter \
        { 
          set cmd [lindex [lrange $args 0 end-1] 0]
          set n [string first " " $cmd]
          if {$n == -1} { set n end } else { incr n -1 }
          if {[lsearch -exact $::flow::(ignore) [string range $cmd 0 $n]] != -1} { return }
          if {$::flow::(last) != ""} { puts $::flow::(pref)>>$::flow::(last) }
          set ::flow::(last) $cmd
          set ::flow::(pref) $pref
        }
        leave \
        { 
          set cmd [lindex [lrange $args 0 end-3] 0]
          set n [string first " " $cmd]
          if {$n == -1} { set n end } else { incr n -1 }
          if {[lsearch -exact $::flow::(ignore) [string range $cmd 0 $n]] != -1} { return }
          if {$cmd == $::flow::(last)} { puts "$pref<>$cmd" } \
          else \
          { 
            if {$::flow::(last) != ""} { puts $::flow::(pref)>>$(last) }
            puts "$pref<<$cmd" 
          }
          set ::flow::(last) ""
          set code [lindex $args end-2]
          set result [lindex $args end-1]
          switch $code \
          {
            0       { if {$result != ""} { puts "$pref  <- $result" } }
            default { puts "$pref***$code: $result" }
          }
        }
      }
    }
  # end of ::flow namespace definition
  }

Staale, 2005-08-23. I'm not able to get this package working. Probably because I'm new to tcl. Could someone be kinde enogh to make my sample code work? (And maybe extend it?) When I run the code (with tclsh flowSample.tcl on windows 2000) I only get output from my own puts commands. Nothing from the flow package.

Sample usage

 # Copied the flow package code to a file flow.tcl and saved the file 
 # in path ./lib relative to my sample file (called flowUsage.tcl )
 #
 # Update the auto_path variable to contain the new ./lib path
 lappend auto_path [file join [file dirname [info script]] ./lib]
 #
 # Create a pkgIndex.tcl file in the ./lib folder
 pkg_mkIndex [file join [file dirname [info script]] ./lib] *.tcl

 # pkgIndex.tcl is used by the command "package require <name>"
 package require Flow

 #
 # Import the flow namespace
 namespace import flow::*

 #
 # Some methods to be called, creating flow
 proc test1 {} {puts "CALL to test1"; activate:trace; test2 }
 proc test2 {} {puts "CALL: to test2"; test3}
 proc test3 {} {puts "CALL: to test3"; test4}
 proc test4 {} {puts "CALL: to test4"}

 #
 # Main part of code
 puts "SCRIPT: starting"
 start:trace 
 test1
 flow:display
 puts "SCRIPT: ending"