#!/usr/bin/tclsh #----------------------------------------------------------------------------- # This program analyses a devexec log as written by SICS. It should produce # a list of devices together with the time each device was active # in seconds. # # Mark Koennecke, January 2007 #---------------------------------------------------------------------------- # Some utility routines for processing an entry in the devexeclog. A line # has the form: # DEVEXEC:OP:DEVICE:SECONDS:NANOSECONDS # This is split into a list and accessor function are provided for various # items #--------------------------------------------------------------------------- proc parseLogLine {line} { set l [split $line :] set tst [lindex $l 0] if {[string compare $tst DEVEXEC] != 0} { error "Bad log line: $line" } return [lrange $l 1 end] } #-------------------------------------------------------------------------- proc getLogOp {logList} { return [lindex $logList 0] } #-------------------------------------------------------------------------- proc getLogDevice {logList} { return [lindex $logList 1] } #-------------------------------------------------------------------------- proc getLogSeconds {logList} { return [lindex $logList 2] } #-------------------------------------------------------------------------- proc getLogNanos {logList} { return [lindex $logList 3] } #------------------------------------------------------------------------- proc getStamp {logList} { return [lrange $logList 2 end] } #========================================================================== proc calcTimeDiff {sec1 nano1 sec2 nano2} { set secSum 0 set nanoSum 0 if {$sec2 > $sec1} { set nanoSum [expr 1000000 - $nano1] set secSum [expr $sec2 - $sec1 + 1] set nanoSum [expr $nanoSum + $nano2] } elseif {$sec2 == $sec1} { set secSum 0 set nanoSum [expr $nano2 - $nano1] } else { error "Bad time order: sec2 should be bigger then sec1" } return [list $secSum $nanoSum] } #========================================================================= # There are two arrays: # One is called devices and holds the device name and the total number # of seconds this device has run. There are special devices: # - nobeam for couting NOBEAM time. This has later to be subtracted from # counting times. # - unallocated time which can not be clearly given to some device # This might happen if the SICServer restarts whilst something is # running. # # The other one is running and holds all the devices which are currently # being run. For each such device a list will be held with seconds # and nanos. At each Start and stop, time differences to the previous # event will be calculated and added to the devices running. If more then # one device is running at a given time, the time will be distributed # equally to all devices. # # There is also a counter for devices which are currently running. # # This section now provides helper functions for dealing with these # arrays #======================================================================== set devRun 0 set devices(nobeam) 0 set devices(unaccounted) 0 set sicsRestart 0 #------------------------------------------------------------------------ proc addToDevice {dev sec nano} { upvar #0 devices devices set totalSec [expr double($sec) + double($nano)/1000000.0] if {[info exists devices($dev)] } { set devices($dev) [expr $devices($dev) + $totalSec] } else { set devices($dev) $totalSec } } #------------------------------------------------------------------------ proc addToRunning {dev sec nano} { upvar #0 running running devRun devRun if {[info exists running($dev)] } { set l $running($dev) set newSec [expr double([lindex $l 0]) + double($sec)] set newNano [expr double([lindex $l 1]) + double($nano)] set running($dev) [list $newSec $newNano] } else { set running($dev) [list $sec $nano] incr devRun } } #------------------------------------------------------------------------ proc stopRunning {dev} { upvar #0 running running devRun devRun devices devices set l $running($dev) addToDevice $dev [lindex $l 0] [lindex $l 1] incr devRun -1 unset running($dev) } #----------------------------------------------------------------------- proc isDevRunning {dev} { upvar #0 running running return [info exists running($dev)] } #------------------------------------------------------------------------ proc cancelAll {} { upvar #0 running running devRun devRun devices devices upvar #0 sicsRestart sicsRestart if {$devRun > 0} { incr sicsRestart } set runlist [array names running] foreach dev $runlist { puts stdout "Live restart on device $dev" set l $running($dev) addToDevice unaccounted [lindex $l 0] [lindex $l 1] unset running($dev) } set devRun 0 } #--------------------------------------------------------------------- proc addRunDiff {dev stamp lastStamp} { upvar #0 running running devRun devRun set diff [calcTimeDiff [lindex $lastStamp 0] \ [lindex $lastStamp 1] \ [lindex $stamp 0]\ [lindex $stamp 1]] if {![info exists running($dev)] } { addToRunning $dev 0 0 } set disSec [expr double([lindex $diff 0])/double($devRun)] set disNano [expr double([lindex $diff 1])/double($devRun)] set devlist [array names running] foreach d $devlist { addToRunning $d $disSec $disNano } } #--------------------------------------------------------------------- proc clearAll {} { upvar #0 devRun devRun __lastStamp lastStamp __nobeamStamp nobeamStamp upvar #0 devices devices running running sicsRestart sicsRestart set devRun 0 catch {unset lastStamp} catch {unset nobeamStamp} set l [array names devices] foreach d $l { unset devices($d) } set l [array names running] foreach d $l { unset running($d) } set devices(nobeam) 0 set devices(unaccounted) 0 set sicsRestart 0 } #======================================================================= # This section contains the code with the main interpretation and # analysis #======================================================================= proc analyzeLine {line after} { upvar #0 devRun devRun __lastStamp lastStamp __nobeamStamp nobeamStamp set log [parseLogLine $line] set afterSec [clock scan $after] set op [getLogOp $log] set t [getLogSeconds $log] if {$t < $afterSec} { return } switch $op { START { set dev [getLogDevice $log] if {[string compare $dev SICS] == 0} { cancelAll return } if {$devRun > 0} { set stamp [getStamp $log] addRunDiff $dev $stamp $lastStamp set lastStamp $stamp } else { if {![isDevRunning $dev] } { addToRunning $dev 0 0 set lastStamp [getStamp $log] } } } STOP { if {![info exists lastStamp]} { return } set dev [getLogDevice $log] if {[string compare $dev SICS] == 0} { cancelAll return } set stamp [getStamp $log] addRunDiff $dev $stamp $lastStamp if {[isDevRunning $dev] } { stopRunning $dev } if {$devRun == 0} { unset lastStamp } } NOBEAM { set nobeamStamp [getStamp $log] } CONTINUE { set stamp [getStamp $log] set diff [calcTimeDiff [lindex $nobeamStamp 0] \ [lindex $nobeamStamp 1] \ [lindex $stamp 0]\ [lindex $stamp 1]] addToDevice nobeam [lindex $diff 0] [lindex $diff 1] unset nobeamStamp } } } #========================================================================== proc printResult {} { upvar #0 devices devices sicsRestart sicsRestart set l [array names devices] puts stdout "DEVICE SECONDS" foreach dev $l { puts stdout [format "%-20s %12.2f" $dev $devices($dev)] } puts stdout [format "%-20s %12.2f" "Live Restarts" $sicsRestart] } #========================================================================= proc analyzeFile {filename after} { set f [open $filename r] while {[gets $f line] >= 0} { set status [catch {analyzeLine $line $after} msg] if {$status != 0} { puts stdout "ERROR: error $msg processing $line" } } close $f } #=============== MAIN Program =========================================== proc main {} { global argv if {[llength $argv] < 2} { puts stdout "Usage:\n\tanalysedevexeclog filename after" puts stdout "\t with after being a date in format MM/DD/YYYY" exit 1 } analyzeFile [lindex $argv 0] [lindex $argv 1] printResult } main exit 0