Advanced TCL Interpreter in TCL

I have implemented many TCL extensions for a specific tool in the field of formal methods (extensions are implemented in C, but I do not want the solution to rely on this fact). Thus, users of my tool can use TCL for prototyping algorithms. Many of them are just a linear list of commands (they are powerful), for example:

my_read_file f my_do_something abc my_do_something_else abc 

Now I'm interested in time. You can modify the script to get:

 puts [time [my_read_file f] 1] puts [time [my_do_something abc] 1] puts [time [my_do_something_else abc] 1] 

Instead, I want to define an xsource procedure that executes a TCL script and input / write time for all my commands. Some kind of profiler. I wrote a naive implementation where the main idea is this:

  set f [open [lindex $argv 0] r] set inputLine "" while {[gets $f line] >= 0} { set d [expr [string length $line] - 1] if { $d >= 0 } { if { [string index $line 0] != "#" } { if {[string index $line $d] == "\\"} { set inputLine "$inputLine [string trimright [string range $line 0 [expr $d - 1]]]" } else { set inputLine "$inputLine $line" set inputLine [string trimleft $inputLine] puts $inputLine puts [time {eval $inputLine} 1] } set inputLine "" } } } 

It works for a linear list of commands and even allows you to add comments and commands over several lines. But it fails if the user uses instructions, loops, and procedure definitions. Can you suggest a better approach? It should be a clean TCL script with as many extensions as possible.

+4
source share
3 answers

One way to do what you ask is to use execution traces . Here's a script that can do just that:

 package require Tcl 8.5 # The machinery for tracking command execution times; prints the time taken # upon termination of the command. More info is available too (eg, did the # command have an exception) but isn't printed here. variable timerStack {} proc timerEnter {cmd op} { variable timerStack lappend timerStack [clock microseconds] } proc timerLeave {cmd code result op} { variable timerStack set now [clock microseconds] set then [lindex $timerStack end] set timerStack [lrange $timerStack 0 end-1] # Remove this length check to print everything out; could be a lot! # Alternatively, modify the comparison to print more stack frames. if {[llength $timerStack] < 1} { puts "[expr {$now-$then}]: $cmd" } } # Add the magic! trace add execution source enterstep timerEnter trace add execution source leavestep timerLeave # And invoke the magic, magically source [set argv [lassign $argv argv0];set argv0] # Alternatively, if you don't want argument rewriting, just do: # source yourScript.tcl 

Then you would call it that (assuming you put it in a file called timer.tcl ):

 tclsh8.5 timer.tcl yourScript.tcl 

Keep in mind that this script has a significant amount of overhead, as it blocks many of the optimization strategies that are commonly used. It will not make much difference for use where you make real meat in your own C code, but when you have many cycles in Tcl, you will notice a lot.

+5
source

You can wrap your teams that you want to measure. And name wrappers are exactly the same as the original ones (renaming original procs before). After that, when the instrumental command is executed, it actually executes a shell that executes the original procedure and measures the execution time. Example below (Tcl 8.5).

 proc instrument {procs} { set skip_procs {proc rename instrument puts time subst uplevel return} foreach p $procs { if {$p ni $skip_procs} { uplevel [subst -nocommands { rename $p __$p proc $p {args} { puts "$p: [time {set r [__$p {*}\$args]}]" return \$r } }] } } } proc my_proc {a} { set r 1 for {set i 1} {$i <= $a} {incr i} { set r [expr {$r * $i}] } return $r } proc my_another_proc {ab} { set r 0 for {set i $a} {$i <= $b} {incr i} { incr r $i } return $r } instrument [info commands my_*] puts "100 = [my_proc 100]" puts "200 = [my_proc 100]" puts "100 - 200 = [my_another_proc 100 200]" 
+2
source

You can see the command "info complete". This may tell you that what you have accumulated so far looks complete in terms of the most common Tcl syntax markers. It will handle command input that can be distributed across multiple physical lines.

+1
source

Source: https://habr.com/ru/post/1379026/


All Articles