1 if [ -z "${__stopwatch_sh_loaded}" ]
3 __stopwatch_sh_loaded=yes
5 __stopwatch_sh_VERSION="Stopwatch v0.1"
6 __stopwatch_sh_DESCRIPTION="Stopwatch implementation supporting multiple tagged timers"
7 __stopwatch_sh_COPYRIGHT=$( cat <<"END_OF_COPYRIGHT"
9 Copyright (c) 2000-2012, CyberLeo
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
15 * Redistributions of the source code must retain the above copyright
16 notice, this list of conditions, and the following disclaimer.
17 * Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions, and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20 * Neither the name of the organization nor the names of its contributors
21 may be used to endorse or promote products derived from this software
22 without specific prior written permission.
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
28 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
30 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 HOWEVER CAUSED AND ON ANY THEORY OF LIABILTY, WHETHER IN CONTRACT, STRICT
32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 kvs="$(mktemp -t ".stopwatch.XXXXXXXX")"
40 trap "rm -f '${kvs}'; exit" exit hup int term kill
46 ${__stopwatch_sh_VERSION}
47 ${__stopwatch_sh_DESCRIPTION}
49 ${__stopwatch_sh_COPYRIGHT}
51 Usage: stopwatch <tag> <start|lap|stop|reset>
53 Simple bourne shell based stopwatch
54 Requires 'kvs' and the id 'stopwatch' therein
55 so make sure you set 'kvs' before sourcing this script if necessary.
57 After loading, the following commands are available:
62 'Lap' a stopwatch (emit timer value without stopping)
65 Stop a stopwatch; can be resumed later with 'start'
68 Reset a stopwatch back to zero
71 Return the bare time, and delta (if any) since last start
74 Internal functions available:
76 stopwatch_humany_duration <usec>
77 Transform an integer number of microseconds into a human-readable string
78 (Ex: 3d14h10m3.223s or 7m10.013s )
80 stopwatch_set_message_callback <command>
81 Register a callback to format event messages emitted when a stopwatch is
82 commanded. The callback will receive the following parameters:
83 <command> - one of: starting, started, lap, get, stopping, stopped, reset
84 <tag> - stopwatch name
85 <time> - the current time on the stopwatch, if any
86 <delta> - the time since the last change or inquiry (for lap and stopping)
87 The commands are as follows:
88 starting - called start while stopped
89 started - called start while running
90 lap - called lap while running
91 get - called lap while stopped
92 stopping - called stop while running
93 stopped - called stop while stopped
95 time - called to return only the formatted time strings
101 case "$(uname -s)" in
104 echo $(( $(date +%s%N) / 1000 ))
109 "${_root}/lib/sh/usec" || echo $(( $(date +%s) * 1000000 ))
113 echo "Unsupported platform: $(uname -s)" >&2
118 # Format stopwatch event messages (Can be overridden; see
119 # 'stopwatch_set_message_callback')
120 _stopwatch_message_callback() {
123 time="${3:+$(stopwatch_humany_duration ${3})}"
124 delta="${4:+$(stopwatch_humany_duration ${4})}"
126 starting) printf "Stopwatch: '%s' starts%s.\n" "${tag}" "${time:+ at ${time}}";;
127 started) printf "Stopwatch: '%s' is already running.\n" "${tag}" ;;
128 lap) printf "Stopwatch: '%s' is running: %s%s\n" "${tag}" "${time}" "${delta:+ (${delta} since last lap)}" ;;
129 get) printf "Stopwatch: '%s' is stopped: %s\n" "${tag}" "${time}" ;;
130 stopping) printf "Stopwatch: '%s' stops at %s%s.\n" "${tag}" "${time}" "${delta:+ (${delta} since last start)}" ;;
131 stopped) printf "Stopwatch: '%s' is stopped%s.\n" "${tag}" "${time:+ at ${time}}" ;;
132 reset) printf "Stopwatch: '%s' is reset to zero.\n" "${tag}" ;;
133 time) printf "%s %s\n" "${time}" "${delta}" ;;
134 *) printf "Stopwatch: No message for command %s\n" "${command}" ;;
138 # Set the message callback
139 stopwatch_set_message_callback() {
140 _stopwatch_registered_message_callback="${1:-_stopwatch_message_callback}"
141 kvs_set stopwatch message_callback "${_stopwatch_registered_message_callback}"
144 # Fetch the message callback; default if unset
145 stopwatch_get_message_callback() {
146 [ "${_stopwatch_registered_message_callback}" ] ||
147 _stopwatch_registered_message_callback="$(kvs_get stopwatch messsage_callback)"
148 if [ "${_stopwatch_registered_message_callback}" ]
150 echo "${_stopwatch_registered_message_callback}"
152 _stopwatch_registered_message_callback=_stopwatch_message_callback
153 echo _stopwatch_message_callback
157 # Try and format a passed integer number of seconds into something more
158 # easily parseable by a human (like 6m10s or something)
159 stopwatch_humany_duration() {
160 local usec secs mins hurs days
161 [ "${1}" -a "$(echo "${1}" | tr -Cd '0-9')" = "${1}" ] || return 255
162 usec=$(( ${1} % 1000000 ))
163 secs=$(( ${1} / 1000000 ))
166 [ "${secs}" -lt 60 ] || {
167 mins=$(( ${secs} / 60 ))
168 secs=$(( ${secs} % 60 ))
170 [ "${mins}" -lt 60 ] || {
171 hurs=$(( ${mins} / 60 ))
172 mins=$(( ${mins} % 60 ))
174 [ "${mins}" -gt 0 ] || mins=""
175 [ "${hurs}" -lt 24 ] || {
176 days=$(( ${hurs} / 24 ))
177 hurs=$(( ${hurs} % 24 ))
179 [ "${hurs}" -gt 0 ] || hurs=""
181 printf "%s%s%s%s.%03ds" "${days:+${days}d}" "${hurs:+${hurs}h}" "${mins:+${mins}m}" "${secs}" "$(( ${usec} / 1000 ))"
184 # Start a named stopwatch, but do nothing if the named stopwatch is already
187 name="${1:-stopwatch}"
188 [ "${nao}" ] || nao="$(_stopwatch_usec)"
189 # Is the stopwatch running?
190 if kvs_has_key stopwatch "${name}"
192 eval "$(stopwatch_get_message_callback) started ${name}"
194 # start the stopwatch
195 accum="$(kvs_get stopwatch "${name}_accumulator")"
196 kvs_set stopwatch "${name}" "${nao}"
197 [ "${accum}" ] && kvs_set stopwatch "${name}_laptime" "${nao}"
198 eval "$(stopwatch_get_message_callback) starting ${name} ${accum}"
202 # 'Lap' the named stopwatch: print out a line indicating how long the named
203 # stopwatch has been running, but do not stop nor reset the stopwatch.
205 name="${1:-stopwatch}"
206 [ "${nao}" ] || nao="$(_stopwatch_usec)"
207 # Is the stopwatch running?
208 if kvs_has_key stopwatch "${name}"
210 # emit a line indicating how long it has been running
211 start="$(kvs_get stopwatch "${name}")"
212 accum="$(kvs_get stopwatch "${name}_accumulator")"
213 laptm="$(kvs_get stopwatch "${name}_laptime")"
214 delta="$(( ${nao} - ${start} ))"
215 [ "${laptm}" ] && laptm="$(( ${nao} - ${laptm} ))"
216 [ "${accum}" ] && delta="$(( ${delta} + ${accum} ))"
217 kvs_set stopwatch "${name}_laptime" "${nao}"
218 eval "$(stopwatch_get_message_callback) lap ${name} ${delta} ${laptm}"
220 accum="$(kvs_get stopwatch "${name}_accumulator")"
221 eval "$(stopwatch_get_message_callback) get ${name} ${accum}"
225 # Stop the named stopwatch and print out a line indicating how long it had
226 # been running up until that point; do not reset the stopwatch.
228 name="${1:-stopwatch}"
229 [ "${nao}" ] || nao="$(_stopwatch_usec)"
230 if kvs_has_key stopwatch "${name}"
232 start="$(kvs_get stopwatch "${name}")"
233 accum="$(kvs_get stopwatch "${name}_accumulator")"
234 delta="$(( ${nao} - ${start} ))"
236 [ "${accum}" ] && delta="$(( ${delta} + ${accum} ))"
237 kvs_set stopwatch "${name}_accumulator" "${delta}"
238 kvs_unset stopwatch "${name}"
239 kvs_unset stopwatch "${name}_laptime"
240 eval "$(stopwatch_get_message_callback) stopping ${name} ${delta} ${diffs}"
242 accum="$(kvs_get stopwatch "${name}_accumulator")"
243 eval "$(stopwatch_get_message_callback) stopped ${name} ${accum}"
247 # Reset the named stopwatch back to zero.
249 name="${1:-stopwatch}"
250 [ "${nao}" ] || nao="$(_stopwatch_usec)"
251 kvs_unset stopwatch "${name}"
252 kvs_unset stopwatch "${name}_accumulator"
253 kvs_unset stopwatch "${name}_laptime"
254 eval "$(stopwatch_get_message_callback) reset ${name}"
257 # Emit the current time and delta
259 name="${1:-stopwatch}"
260 [ "${nao}" ] || nao="$(_stopwatch_usec)"
261 if kvs_has_key stopwatch "${name}"
264 start="$(kvs_get stopwatch "${name}")"
265 accum="$(kvs_get stopwatch "${name}_accumulator")"
266 delta="$(( ${nao} - ${start} ))"
268 [ "${accum}" ] && delta="$(( ${delta} + ${accum} ))"
269 eval "$(stopwatch_get_message_callback) time ${name} ${delta} ${diffs}"
270 elif kvs_has_key stopwatch "${name}_accumulator"
273 accum="$(kvs_get stopwatch "${name}_accumulator")"
274 eval "$(stopwatch_get_message_callback) time ${name} ${accum} 0"
277 eval "$(stopwatch_get_message_callback) time ${name} 0 0"
282 [ "${1}" -a "${2}" ] || stopwatch_help;
283 nao="$(_stopwatch_usec)"
285 [Ss][Tt][Aa][Rr][Tt]) stopwatch_start "${1}" ;;
286 [Ll][Aa][Pp]) stopwatch_lap "${1}" ;;
287 [Ss][Tt][Oo][Pp]) stopwatch_stop "${1}" ;;
288 [Rr][Ee][Ss][Ee][Tt]) stopwatch_reset "${1}" ;;
289 [Tt][Ii][Mm][Ee]) stopwatch_time "${1}" ;;