/var/lib/sorcery/modules/libtime
1 #!/bin/bash
2 #---------------------------------------------------------------------
3 ##
4 ## @Synopsis Set of functions used by gaze for time calculations
5 ## @Copyright (C) 2008 The Source Mage Team <http://www.sourcemage.org>
6 ##
7 ## This file holds various statistical functions and an interface to
8 ## the activity log for getting the input data.
9 #---------------------------------------------------------------------
10
11 #---------------------------------------------------------------------
12 ##
13 ## Computes all the casting times of the passed spell by inspecting
14 ## the activity log.
15 ##
16 ## @param spell
17 ## @param version (optional)
18 ##
19 ## @Stdout casting time(s)
20 #---------------------------------------------------------------------
21 function compute_cast_times() {
22 gawk -v spell=$1 -v version=$2 '
23 # log timestamps are in the "%Y%m%d:%H%M\(%z\)" format (20080625:0853(+0000))
24 # we need them in "%Y %m %d %H %M %S" and %s (epoch time). We ignore the
25 # timezone, since the time is stored in UTC and %z is always +0000
26 function since_epoch(time, date) {
27 # The date:
28 date = substr(time,1,4) " " substr(time,5,2) " " substr(time,7,2)
29 # The time (use 00 for seconds):
30 date = date " " substr(time,10,2) " " substr(time,12,2) " 00"
31 return mktime(date)
32 }
33
34 /^.*\tcast\t.*\t.*\t.*\t.*$/ {
35 # check the spell and version manually - literally
36 if ($3 != spell) next
37 if (version != "" && $4 != version) next
38
39 # check all valid start/succes pairs
40 if ($5 == "start") {
41 start_time = $1
42 start_version = $4
43 }
44 if ($5 == "success" && start_time != 0 && start_version == $4) {
45 succes_time = $1
46 print since_epoch(succes_time)-since_epoch(start_time)
47 start_time = 0
48 }
49 }' $ACTIVITY_LOG
50 }
51
52 #---------------------------------------------------------------------
53 ##
54 ## Display the time in seconds a spell took to compile and install.
55 ## @param spell
56 ## @Stdout cast time of spell in seconds
57 #---------------------------------------------------------------------
58 function compute_cast_time() {
59 # FIXME: make the default configurable
60 local spell=$1
61 local type=${2:---last}
62
63 compute_cast_times $spell |
64 case $type in
65 --last)
66 tail -n 1 ;;
67 --median)
68 compute_median ;;
69 --mean)
70 compute_mean ;;
71 --weigh-last)
72 compute_weighted_mean last-cast $(private_installed_version $spell) ;;
73 esac
74 }
75
76 #---------------------------------------------------------------------
77 ##
78 ## Display the time in seconds a spell took to compile and install.
79 ## All known algorithms are used (see compute_cast_time). Mean estimates
80 ## also show the estimation error.
81 ##
82 ## @param spell
83 ## @param verbosity 0-machine readable/quiet
84 ## @Stdout pretty-printed cast times of spell
85 ## @return 1 if there are no valid times available
86 ## @return 0 otherwise
87 #---------------------------------------------------------------------
88 function compute_all_cast_times() {
89 local spell=$1 verbosity=$2
90 local times time
91
92 times=$(compute_cast_times $spell)
93 [[ -z $times ]] && return 1
94
95 if [[ $verbosity == false ]]; then
96 echo -n $spell:
97 echo "$times" | tail -n 1
98 echo -n $spell:
99 echo "$times" | compute_median
100 echo -n $spell:
101 echo "$times" | compute_mean
102 echo -n $spell:
103 echo "$times" | compute_weighted_mean last-cast $(private_installed_version $spell)
104 echo -n $spell:n $(wc -l <<< "$times")
105 echo
106 else
107 message -n "Last cast time of ${SPELL_COLOR}$spell$DEFAULT_COLOR: "
108 time=$(echo "$times" | tail -n 1)
109 pretty_print_time $time
110
111 message -n "Median cast time of ${SPELL_COLOR}$spell$DEFAULT_COLOR: "
112 time=$(echo "$times" | compute_median)
113 pretty_print_time $time
114
115 message -n "Mean cast time of ${SPELL_COLOR}$spell$DEFAULT_COLOR: "
116 time=$(echo "$times" | compute_mean)
117 pretty_print_time $time
118
119 message -n "Weighted mean cast time of ${SPELL_COLOR}$spell$DEFAULT_COLOR: "
120 time=$(echo "$times" | compute_weighted_mean last-cast $(private_installed_version $spell))
121 pretty_print_time $time
122
123 message "Number of samples: $(wc -l <<< "$times")"
124 echo
125 fi
126
127 return 0
128 }
129 #---------------------------------------------------------------------
130 ##
131 ## Computes the mean of the passed arguments and its standard error
132 ##
133 ## @Stdin numbers separated by newlines
134 ## @Stdout mean and its error
135 #---------------------------------------------------------------------
136 function compute_mean() {
137 gawk '
138 BEGIN { mean = 0; variance = 0; }
139
140 { sum += $0; numbers[NR] = $0; }
141
142 END {
143 if (NR == 0) exit
144 mean = sum/NR;
145 if (NR == 1) {
146 error = 0;
147 } else {
148 for (i in numbers) {
149 variance += (numbers[i] - mean)^2;
150 }
151 error = int(sqrt(variance/(NR-1)/NR)*1.96 + 0.5);
152 }
153
154 print int(mean+0.5), error
155 }'
156 }
157
158 #---------------------------------------------------------------------
159 ##
160 ## Computes the weighted mean of the passed and some more arguments.
161 ## WARNING: only usable for cast times!
162 ##
163 ## @param type - weigh either just the times of the latest version
164 ##
165 ## @Stdin numbers separated by newlines
166 ## @Stdout mean value
167 #---------------------------------------------------------------------
168 function compute_weighted_mean() {
169 local type=$1
170 local version=$2
171
172 if [[ $type == last-cast ]]; then
173 local weight=10 #make this relative someday?
174 local i command
175 for ((i=1; i < $weight; i++)); do
176 command="p; $command"
177 done
178 {
179 # weigh the latest version by adding it another weight-1 times
180 # to the piped list of casting times of all versions
181 cat -
182 compute_cast_times $spell $version | sed -n "$command"
183 } | compute_mean
184 fi
185 }
186
187 #---------------------------------------------------------------------
188 ##
189 ## Computes the median of the passed arguments
190 ##
191 ## @Stdin numbers separated by newlines
192 ## @Stdout median value
193 #---------------------------------------------------------------------
194 function compute_median() {
195 gawk '
196 { numbers[NR] = $0; }
197
198 END {
199 if (NR == 0) exit
200 if (NR == 1) {
201 print numbers[0]
202 } else {
203 asort(numbers)
204 print (NR % 2) ? numbers[int(NR/2+1)] : (numbers[NR/2] + numbers[NR/2+1])/2
205 }
206 }'
207 }
208
209 #---------------------------------------------------------------------
210 ##
211 ## Computes the combined error of independent variables
212 ##
213 ## @param error for each variable
214 ## @param ...
215 ## @Stdout total error
216 #---------------------------------------------------------------------
217 function compute_total_error() {
218 tr ' ' '\n' <<< "$@" |
219 gawk '
220 { total += $0^2 }
221
222 END {
223 print int(sqrt(total))
224 }'
225 }
226
227 #---------------------------------------------------------------------
228 ##
229 ## Converts a time in seconds and the estimation error (if available)
230 ## into a human readable format
231 ##
232 ## @param time in seconds
233 ## @param estimation error in seconds (optional)
234 ## @Stdout time (DD:HH:MM or HH:MM or MM or "less than a minute")
235 #---------------------------------------------------------------------
236 function pretty_print_time() {
237 if [[ -z $2 ]]; then
238 pretty_print_time_sub "$@"
239 else
240 local time=$(pretty_print_time_sub $1)
241 echo -n "$time "
242 pretty_print_time_error $2
243 fi
244 }
245
246 #---------------------------------------------------------------------
247 ##
248 ## Converts a time in seconds into a human readable format.
249 ##
250 ## @param time in seconds
251 ## @Stdout time (DD:HH:MM or HH:MM or MM or "less than a minute")
252 #---------------------------------------------------------------------
253 function pretty_print_time_sub() {
254 # date can only print day of year starting with 1, so we can't use it,
255 # yet we have to display the number of days, since the total can be big
256 awk -v sum=$1 'END {
257 days = int(sum/3600/24)
258 sum -= days * 3600 * 24
259 hours = int(sum/3600)
260 sum -= hours * 3600
261 minutes = int(sum/60)
262 # sum is now < 3600
263
264 if (sum < 60 && days == 0 && hours == 0) {
265 print "less than a minute."
266 exit
267 }
268 if (days > 0) {
269 print days "d " hours "h " minutes "m"
270 } else if (hours > 0) {
271 print hours "h " minutes "m"
272 } else {
273 print minutes "m"
274 }
275 }' /dev/null
276 }
277
278 #---------------------------------------------------------------------
279 ##
280 ## Converts a time estimation error into a human readable format.
281 ## If the error is very small or 0, it isn't shown.
282 ##
283 ## @param estimation error in seconds
284 ## @Stdout time +/- error (see pretty_print_time)
285 #---------------------------------------------------------------------
286 function pretty_print_time_error() {
287 local error=$1
288
289 if (( $error < 60 )); then
290 error=0
291 else
292 error=$(pretty_print_time_sub $error)
293 fi
294
295 if [[ $error == 0 ]]; then
296 echo
297 else
298 echo "(+/- $error)"
299 fi
300 }
301
302 #---------------------------------------------------------------------
303 ## @License
304 ##
305 ## This software is free software; you can redistribute it and/or modify
306 ## it under the terms of the GNU General Public License as published by
307 ## the Free Software Foundation; either version 2 of the License, or
308 ## (at your option) any later version.
309 ##
310 ## This software is distributed in the hope that it will be useful,
311 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
312 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
313 ## GNU General Public License for more details.
314 ##
315 ## You should have received a copy of the GNU General Public License
316 ## along with this software; if not, write to the Free Software
317 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
318 ##
319 #---------------------------------------------------------------------