source: trunk/p2p/test.tcl @ 1257

Last change on this file since 1257 was 1257, checked in by mmc, 16 years ago

Added a new Rappture::sysinfo command for querying system load
information. Updated the p2p software to use that command to
gauge the load of workers and execute a "perftest" executable
from time to time to measure worker output.

File size: 20.4 KB
Line 
1# ----------------------------------------------------------------------
2#  TEST HARNESS for the P2P infrastructure
3#
4#  This script drives the test setup and visualization for the P2P
5#  infrastructure.  It launches the authority server(s) and various
6#  workers, and helps to visualize their interactions.
7# ----------------------------------------------------------------------
8#  Michael McLennan (mmclennan@purdue.edu)
9# ======================================================================
10#  Copyright (c) 2008  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itcl
16
17# recognize other library files in this same directory
18set dir [file dirname [info script]]
19lappend auto_path $dir
20
21set time0 [clock seconds]
22set processes ""
23set nodes(all) ""
24set nodeRadius 15
25
26# ======================================================================
27#  SHAPES
28# ======================================================================
29itcl::class Shape {
30    private variable _canvas ""      ;# shape sits on this canvas
31    private variable _ranges ""      ;# list of time ranges for shape
32    private common _shapesOnCanvas   ;# maps canvas => list of shapes
33
34    public variable command ""  ;# command template used to create shape
35
36    constructor {canvas args} {
37        # add this shape to the list of shapes on this canvas
38        lappend _shapesOnCanvas($canvas) $this
39        set _canvas $canvas
40        eval configure $args
41    }
42    destructor {
43        # remove this shape from the list of shapes on the canvas
44        set i [lsearch $_shapesOnCanvas($_canvas) $this]
45        if {$i >= 0} {
46            set _shapesOnCanvas($_canvas) \
47                [lreplace $_shapesOnCanvas($_canvas) $i $i]
48        }
49    }
50
51    # ------------------------------------------------------------------
52    #  METHOD: addRange <time0> <time1>
53    #  Declares that this shape exists during the given time range
54    #  between <time0> and <time1>.
55    # ------------------------------------------------------------------
56    public method addRange {t0 t1} {
57        # see if there's any overlap with existing ranges
58        set ri0 -1
59        set ri1 -1
60        for {set i 0} {$i < [llength $_ranges]} {incr i} {
61            set pair [lindex $_ranges $i]
62            foreach {r0 r1} $pair break
63            if {$r0 >= $t0 && $r0 <= $t1} {
64                set ri0 $i
65            }
66            if {$r1 >= $t0 && $r1 <= $t1} {
67                set ri1 $i
68            }
69            incr i
70        }
71
72        if {$ri0 < 0 && $ri1 < 1} {
73            # doesn't overlap with anything -- insert in right place
74            for {set i 0} {$i < [llength $_ranges]} {incr i} {
75                set pair [lindex $_ranges $i]
76                foreach {r0 r1} $pair break
77                if {$t0 < $r0} break
78            }
79            set _ranges [linsert $_ranges $i [list $t0 $t1]]
80        } elseif {$ri0 >= 0 && $ri1 >= 0} {
81            # overlaps on both limits -- bridge the middle part
82            set r0 [lindex [lindex $_ranges $ri0] 0]
83            set r1 [lindex [lindex $_ranges $ri1] 1]
84            set _ranges [lreplace $_ranges $ri0 $ri1 [list $r0 $r1]]
85        } elseif {$ri0 >= 0} {
86            # overlaps on the lower limit
87            for {set i [expr {[llength $_ranges]-1}]} {$i >= 0} {incr i -1} {
88                set pair [lindex $_ranges $i]
89                foreach {r0 r1} $pair break
90                if {$r0 > $t0 && $r1 < $t1} {
91                    # remove any ranges completely contained in this one
92                    set _ranges [lreplace $_ranges $i $i]
93                }
94                set _ranges [lreplace $_ranges $ri0 $ri1 [list $r0 $r1]]
95            }
96            set pair [lindex $_ranges $ri0]
97            foreach {r0 r1} $pair break
98            set _ranges [lreplace $_ranges $ri0 $ri0 [list $r0 $t1]]
99        } else {
100            # overlaps on the upper limit
101            for {set i [expr {[llength $_ranges]-1}]} {$i >= 0} {incr i -1} {
102                set pair [lindex $_ranges $i]
103                foreach {r0 r1} $pair break
104                if {$r0 > $t0 && $r1 < $t1} {
105                    # remove any ranges completely contained in this one
106                    set _ranges [lreplace $_ranges $i $i]
107                }
108                set _ranges [lreplace $_ranges $ri0 $ri1 [list $r0 $r1]]
109            }
110            set pair [lindex $_ranges $ri1]
111            foreach {r0 r1} $pair break
112            set _ranges [lreplace $_ranges $ri1 $ri1 [list $t0 $r1]]
113        }
114    }
115
116    # ------------------------------------------------------------------
117    #  METHOD: exists <time>
118    #  Checks to see if this shape exists at the given <time>.
119    #  Returns 1 if so, and 0 otherwise.
120    # ------------------------------------------------------------------
121    public method exists {t} {
122        for {set i 0} {$i < [llength $_ranges]} {incr i} {
123            set pair [lindex $_ranges $i]
124            foreach {r0 r1} $pair break
125            if {$t >= $r0 && $t <= $r1} {
126                return 1
127            }
128        }
129        return 0
130    }
131
132    # ------------------------------------------------------------------
133    #  PROC: draw <canvas> <time>
134    #  Finds the list of shapes that exist on the given <canvas> at
135    #  the specified time, and executes all of their associated
136    #  -command templates, adding them to the canvas.
137    # ------------------------------------------------------------------
138    public proc draw {canvas time} {
139        if {[info exists _shapesOnCanvas($canvas)]} {
140            foreach obj $_shapesOnCanvas($canvas) {
141                if {[$obj exists $time]} {
142                    set cmd [$obj cget -command]
143                    regsub -all %c $cmd $canvas cmd
144                    eval $cmd
145                }
146            }
147        }
148    }
149
150    # ------------------------------------------------------------------
151    #  PROC: clear <canvas>
152    #  Removes all shapes associated with the specified <canvas>.
153    # ------------------------------------------------------------------
154    public proc clear {canvas} {
155        if {[info exists _shapesOnCanvas($canvas)]} {
156            eval itcl::delete object $_shapesOnCanvas($canvas)
157        }
158    }
159}
160
161# ======================================================================
162#  Build the main interface
163# ======================================================================
164frame .cntls
165pack .cntls -fill x
166
167button .cntls.start -text "Start" -command test_start
168pack .cntls.start -side left -padx 4 -pady 2
169
170button .cntls.stop -text "Stop" -command test_stop -state disabled
171pack .cntls.stop -side left -padx 4 -pady 2
172
173button .cntls.reload -text "Reload" -command test_reload
174pack .cntls.reload -side left -padx 4 -pady 2
175
176button .cntls.layout -text "New Layout" -command {
177    foreach key [array names nodes *-\[xy\]] {
178        unset nodes($key)
179    }
180    after idle test_reload
181}
182pack .cntls.layout -side left -padx 4 -pady 2
183
184frame .player
185pack .player -side bottom -fill x
186
187button .player.back -text "<" -command {test_frame_go -1 nudge}
188pack .player.back -side left -padx 4 -pady 2
189
190button .player.fwd -text ">" -command {test_frame_go 1 nudge}
191pack .player.fwd -side left -padx 4 -pady 2
192
193scale .player.scale -label "Frame" -orient horizontal \
194    -from 0 -to 1 -showvalue 0 -command {test_frame_go 1}
195pack .player.scale -side left -expand yes -fill x -padx 4 -pady 2
196
197frame .view
198pack .view -side bottom -anchor w
199label .view.show -text "Show:"
200grid .view.show -row 0 -column 0 -sticky e
201radiobutton .view.network -text "P2P Network" -variable view -value network -command test_view_change
202grid .view.network -row 0 -column 1 -sticky w
203radiobutton .view.traffic -text "Network traffic" -variable view -value traffic -command test_view_change
204grid .view.traffic -row 1 -column 1 -sticky w
205
206frame .diagram
207pack .diagram -expand yes -fill both
208canvas .diagram.network -width 500 -height 400
209canvas .diagram.traffic -width 500 -height 400
210
211after idle .view.traffic invoke
212
213proc test_stop {} {
214    global processes
215
216    # kill any existing processes
217    foreach job $processes {
218        exec kill $job
219    }
220    set processes ""
221
222    .cntls.stop configure -state disabled
223    .cntls.start configure -state normal
224
225    after idle test_reload
226}
227
228proc test_start {} {
229    global processes
230
231    # clean up existing log files...
232    foreach fname [glob -nocomplain /tmp/log*] {
233        file delete $fname
234    }
235
236    # launch a new authority server
237    lappend processes [exec tclsh authority.tcl &]
238
239    # launch a series of workers
240    for {set i 0} {$i < 20} {incr i} {
241        lappend processes [exec tclsh worker.tcl &]
242        after [expr {int(rand()*5000)}]
243    }
244
245    .cntls.start configure -state disabled
246    .cntls.stop configure -state normal
247}
248
249proc test_reload {} {
250    global time0 nodes actions nodeRadius
251
252    array set colors {
253        authority blue
254        worker gray
255    }
256
257    Shape::clear .diagram.network
258    Shape::clear .diagram.traffic
259    set tmax 0
260
261    #
262    # Scan through all files and generate positions for all nodes.
263    #
264    foreach fname [glob -nocomplain /tmp/log*] {
265        set fid [open $fname r]
266        set info [read $fid]
267        close $fid
268
269        # get the address for this host
270        regexp {started at port ([0-9]+)} $info match port
271        if {[regexp {options [^\n]+ ip ([^ ]+)} $info match ip]} {
272            set addr $ip:$port
273        } else {
274            set addr 127.0.0.1:$port
275        }
276
277        set lasttime ""
278        set info [split $info \n]
279        set first [lindex $info 0]
280        regexp {^([0-9]+/[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+) +(authority|worker)} $first match t0 type
281        set t0val [expr {([clock scan $t0]-$time0)*100}]
282
283        set last [lindex $info end-1]
284        regexp {^([0-9]+/[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+)} $last match t1
285        set t1val [expr {([clock scan $t1]-$time0)*100 + 99}]
286        set margin 20
287        set r $nodeRadius
288
289        set nodes($fname) $addr
290        set nodes($addr-log) $fname
291        set nodes($addr-type) $type
292        if {![info exists nodes($addr-x)]} {
293            set w [expr {[winfo width .diagram]-2*$margin}]
294            set h [expr {[winfo height .diagram]-2*$margin}]
295            set nodes($addr-x) [expr {int(rand()*$w) + $margin}]
296            set nodes($addr-y) [expr {int(rand()*$h) + $margin}]
297        }
298        set x $nodes($addr-x)
299        set y $nodes($addr-y)
300
301        foreach canv {.diagram.traffic .diagram.network} {
302            set s [Shape ::#auto $canv -command \
303                [list %c create oval [expr {$x-$r}] [expr {$y-$r}] \
304                [expr {$x+$r}] [expr {$y+$r}] \
305                -outline black -fill $colors($type) \
306                -tags [list $fname $fname-node]]]
307            $s addRange $t0val $t1val
308        }
309
310        append actions($t0val) "$type $addr online\n"
311        append actions($t1val) "$type $addr offline\n"
312
313        if {$t1val > $tmax} { set tmax $t1val }
314    }
315               
316    #
317    # Scan through files again and generate shapes for all messages
318    #
319    foreach fname [glob -nocomplain /tmp/log*] {
320        set fid [open $fname r]
321        set info [read $fid]
322        close $fid
323puts "\nscanning $fname"
324
325        catch {unset started}
326        set peerlist(addrs) ""
327        set peerlist(time) 0
328        set lasttime ""
329        set counter 0
330        foreach line [split $info \n] {
331            if {[regexp {^([0-9]+/[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+) (.+)$} $line match time mesg]} {
332                set tval [expr {([clock scan $time]-$time0)*100}]
333
334                if {$time == $lasttime} {
335                    set tval [expr {$tval + [incr counter]}]
336                }
337                set lasttime $time
338
339                if {$tval > $tmax} { set tmax $tval }
340
341                if {[regexp {accepted: +([^ ]+) +\((.+)\)} $mesg match addr cid]} {
342                    append actions($tval) $mesg \n
343                    set started(connect$cid-time) $tval
344                    set started(connect$cid-addr) ?
345
346                } elseif {[regexp {dropped: +([^ ]+) +\((.+)\)} $mesg match addr cid] && [info exists started(connect$cid-time)]} {
347                    append actions($tval) $mesg \n
348                    set from $nodes($fname)
349                    set x0 $nodes($from-x)
350                    set y0 $nodes($from-y)
351                    set x1 $nodes($addr-x)
352                    set y1 $nodes($addr-y)
353                    set s [Shape ::#auto .diagram.traffic -command \
354                        [list %c create line $x0 $y0 $x1 $y1 -width 3 -fill gray -tags [list $fname $fname-cnx]]]
355                    $s addRange $started(connect$cid-time) $tval
356
357                    unset started(connect$cid-time)
358                    unset started(connect$cid-addr)
359
360                } elseif {[regexp {server message from ([a-zA-Z0-9\.]+:[0-9]+) \(([a-z0-9]+)\): +(.+) => (.*)} $mesg match addr cid cmd result]} {
361                    if {![string match identity* $cmd]} {
362                        append actions($tval) $mesg \n
363                        set from $nodes($fname)
364                        set x0 $nodes($from-x)
365                        set y0 $nodes($from-y)
366                        set x1 $nodes($addr-x)
367                        set y1 $nodes($addr-y)
368                        set w [expr {[winfo width .diagram]/2}]
369
370                        set s [Shape ::#auto .diagram.traffic -command \
371                            [list %c create line [expr $x0-3] [expr $y0-3] [expr $x1-3] [expr $y1-3] -fill black -arrow first -tags transient]]
372                        $s addRange $tval $tval
373
374                        set s [Shape ::#auto .diagram.traffic -command \
375                            [list %c create text [expr {0.5*($x0+$x1)}] [expr {0.5*($y0+$y1)-1}] -width $w -fill black -anchor s -text $cmd -tags transient]]
376                        $s addRange $tval $tval
377
378                        if {"" != [string trim $result]} {
379                            if {[regexp {^ok:} $result]} {
380                                set color black
381                            } else {
382                                set color red
383                            }
384                            set s [Shape ::#auto .diagram.traffic -command \
385                                [list %c create line [expr $x0+3] [expr $y0+3] [expr $x1+3] [expr $y1+3] -fill $color -arrow last -tags transient]]
386                            $s addRange $tval $tval
387
388                            set s [Shape ::#auto .diagram.traffic -command \
389                                [list %c create text [expr {0.5*($x0+$x1)}] [expr {0.5*($y0+$y1)+1}] -width $w -fill $color -anchor n -text $result -tags transient]]
390                            $s addRange $tval $tval
391                        }
392                    }
393
394                    # no address for this client yet?  then save this info
395                    if {[info exists started(connect$cid-addr)]
396                           && $started(connect$cid-addr) == "?"} {
397                        set started(connect$cid-addr) $addr
398                    }
399
400                } elseif {[regexp {connected to peers: (.*)} $mesg match plist]} {
401                    # draw a highlight ring, indicating that peers have changed
402                    set from $nodes($fname)
403                    set x $nodes($from-x)
404                    set y $nodes($from-y)
405                    foreach canv {.diagram.traffic .diagram.network} {
406                        set s [Shape ::#auto $canv -command \
407                            [list %c create oval [expr {$x-$r-3}] [expr {$y-$r-3}] [expr {$x+$r+3}] [expr {$y+$r+3}] -outline red -fill "" -tags transient]]
408                        $s addRange $tval [expr {$tval+20}]
409                    }
410
411                    # draw lines from the previous peer list, now that
412                    # we know it's time range is done.
413                    foreach addr $peerlist(addrs) {
414                        set from $nodes($fname)
415                        set x0 $nodes($from-x)
416                        set y0 $nodes($from-y)
417                        set x1 $nodes($addr-x)
418                        set y1 $nodes($addr-y)
419                        set s [Shape ::#auto .diagram.network -command \
420                            [list %c create line $x0 $y0 $x1 $y1 -width 3 -fill gray -tags [list $fname $fname-cnx]]]
421                        $s addRange $peerlist(time) $tval
422                    }
423
424                    # save the start of this new peer list
425                    set peerlist(addrs) $plist
426                    set peerlist(time) $tval
427                }
428            }
429        }
430
431        # any connections hanging out that we haven't drawn?
432        foreach key [array names started connect*-addr] {
433            regexp {connect([^-]+)-addr} $key match cid
434            set from $nodes($fname)
435            set addr $started($key)
436            set x0 $nodes($from-x)
437            set y0 $nodes($from-y)
438            set x1 $nodes($addr-x)
439            set y1 $nodes($addr-y)
440            set s [Shape ::#auto .diagram.traffic -command \
441                [list %c create line $x0 $y0 $x1 $y1 -width 3 -fill gray -tags [list $fname $fname-cnx]]]
442            $s addRange $started(connect$cid-time) $tval
443
444            unset started(connect$cid-time)
445            unset started(connect$cid-addr)
446        }
447
448        .diagram.traffic bind $fname <Enter> "
449            %W itemconfigure $fname-node -outline red
450            %W itemconfigure $fname-cnx -fill red
451            %W raise $fname-cnx
452        "
453        .diagram.traffic bind $fname <Leave> "
454            %W itemconfigure $fname-node -outline black
455            %W itemconfigure $fname-cnx -fill gray
456            %W raise transient
457        "
458
459        foreach canv {.diagram.network .diagram.traffic} {
460            $canv bind $fname-node <ButtonPress-1> \
461                [list worker_node_click %W $fname %x %y]
462            $canv bind $fname-node <B1-Motion> \
463                [list worker_node_drag %W $fname %x %y]
464            $canv bind $fname-node <ButtonRelease-1> \
465                [list worker_node_release %W $fname %x %y]
466        }
467
468        # any peer connections that we haven't drawn?  then do it now
469        foreach addr $peerlist(addrs) {
470            set from $nodes($fname)
471            set x0 $nodes($from-x)
472            set y0 $nodes($from-y)
473            set x1 $nodes($addr-x)
474            set y1 $nodes($addr-y)
475            set s [Shape ::#auto .diagram.network -command \
476                [list %c create line $x0 $y0 $x1 $y1 -width 3 -fill gray -tags [list $fname $fname-cnx]]]
477            $s addRange $peerlist(time) $tmax
478        }
479
480        .diagram.network bind $fname <Enter> "
481            %W itemconfigure $fname-node -outline red
482            %W itemconfigure $fname-cnx -fill red
483            %W raise $fname-cnx
484        "
485        .diagram.network bind $fname <Leave> "
486            %W itemconfigure $fname-node -outline black
487            %W itemconfigure $fname-cnx -fill gray
488            %W raise transient
489        "
490    }
491    .player.scale configure -to $tmax
492
493    after cancel test_visualize
494    after idle test_visualize
495}
496
497proc worker_node_click {canv fname x y} {
498    $canv itemconfigure $fname-node -stipple gray50
499}
500
501proc worker_node_drag {canv fname x y} {
502    global nodeRadius
503    $canv itemconfigure $fname-node -stipple ""
504    $canv coords $fname-node [expr {$x-$nodeRadius}] [expr {$y-$nodeRadius}] [expr {$x+$nodeRadius}] [expr {$y+$nodeRadius}]
505}
506
507proc worker_node_release {canv fname x y} {
508    global move nodes
509
510    worker_node_drag $canv $fname $x $y
511    $canv itemconfigure $fname-node -stipple ""
512
513    set addr $nodes($fname)
514    set nodes($addr-x) $x
515    set nodes($addr-y) $y
516    after idle test_reload
517}
518
519proc test_frame_go {dir position} {
520    global actions
521
522    set tmax [.player.scale cget -to]
523    if {"nudge" == $position} {
524        set nframe [.player.scale get]
525        incr nframe $dir
526    } else {
527        set nframe $position
528    }
529
530    while {![info exists actions($nframe)] && $nframe > 0 && $nframe < $tmax} {
531        incr nframe $dir
532    }
533
534    # set the scale to this new position
535    .player.scale configure -command ""
536    .player.scale set $nframe
537    .player.scale configure -command {test_frame_go 1}
538
539    after cancel test_visualize
540    after idle test_visualize
541}
542
543proc test_visualize {} {
544    .diagram.traffic delete all
545    Shape::draw .diagram.traffic [.player.scale get]
546    .diagram.traffic raise transient
547
548    .diagram.network delete all
549    Shape::draw .diagram.network [.player.scale get]
550    .diagram.network raise transient
551}
552
553proc test_view_change {} {
554    global view
555    foreach widget [pack slaves .diagram] {
556        catch {pack forget $widget}
557    }
558    pack .diagram.$view -expand yes -fill both
559}
Note: See TracBrowser for help on using the repository browser.