summaryrefslogtreecommitdiffstats
path: root/files/stat_log.py
diff options
context:
space:
mode:
Diffstat (limited to 'files/stat_log.py')
-rwxr-xr-xfiles/stat_log.py394
1 files changed, 394 insertions, 0 deletions
diff --git a/files/stat_log.py b/files/stat_log.py
new file mode 100755
index 0000000..2dbe3ee
--- /dev/null
+++ b/files/stat_log.py
@@ -0,0 +1,394 @@
1#!/usr/bin/python
2import sys
3import copy
4
5P_NULL = 0
6P_NEW = 1
7P_READY = 2
8P_RUNNING = 4
9P_WAITING = 8
10P_EXIT = 16
11
12S_STATE = 0
13S_TIME = 1
14
15HZ = 100
16
17graph_title = r"""
18-----===< COOL GRAPHIC OF SCHEDULER >===-----
19
20 [Symbol] [Meaning]
21 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
22 number PID or tick
23 "-" New or Exit
24 "#" Running
25 "|" Ready
26 ":" Waiting
27 / Running with
28 "+" -| Ready
29 \and/or Waiting
30
31-----===< !!!!!!!!!!!!!!!!!!!!!!!!! >===-----
32"""
33
34usage = """
35Usage:
36%s /path/to/process.log [PID1] [PID2] ... [-x PID1 [PID2] ... ] [-m] [-g]
37
38Example:
39# Include process 6, 7, 8 and 9 in statistics only. (Unit: tick)
40%s /path/to/process.log 6 7 8 9
41
42# Exclude process 0 and 1 from statistics. (Unit: tick)
43%s /path/to/process.log -x 0 1
44
45# Include process 6 and 7 only and print a COOL "graphic"! (Unit: millisecond)
46%s /path/to/process.log 6 7 -m -g
47
48# Include all processes and print a COOL "graphic"! (Unit: tick)
49%s /path/to/process.log -g
50"""
51
52class MyError(Exception):
53 pass
54
55class DuplicateNew(MyError):
56 def __init__(self, pid):
57 args = "More than one 'N' for process %d." % pid
58 MyError.__init__(self, args)
59
60class UnknownState(MyError):
61 def __init__(self, state):
62 args = "Unknown state '%s' found." % state
63 MyError.__init__(self, args)
64
65class BadTime(MyError):
66 def __init__(self, time):
67 args = "The time '%d' is bad. It should >= previous line's time." % time
68 MyError.__init__(self, args)
69
70class TaskHasExited(MyError):
71 def __init__(self, state):
72 args = "The process has exited. Why it enter '%s' state again?" % state
73 MyError.__init__(self, args)
74
75class BadFormat(MyError):
76 def __init__(self):
77 args = "Bad log format"
78 MyError.__init__(self, args)
79
80class RepeatState(MyError):
81 def __init__(self, pid):
82 args = "Previous state of process %d is identical with this line." % (pid)
83 MyError.__init__(self, args)
84
85class SameLine(MyError):
86 def __init__(self):
87 args = "It is a clone of previous line."
88 MyError.__init__(self, args)
89
90class NoNew(MyError):
91 def __init__(self, pid, state):
92 args = "The first state of process %d is '%s'. Why not 'N'?" % (pid, state)
93 MyError.__init__(self, args)
94
95class statistics:
96 def __init__(self, pool, include, exclude):
97 if include:
98 self.pool = process_pool()
99 for process in pool:
100 if process.getpid() in include:
101 self.pool.add(process)
102 else:
103 self.pool = copy.copy(pool)
104
105 if exclude:
106 for pid in exclude:
107 if self.pool.get_process(pid):
108 self.pool.remove(pid)
109
110 def list_pid(self):
111 l = []
112 for process in self.pool:
113 l.append(process.getpid())
114 return l
115
116 def average_turnaround(self):
117 if len(self.pool) == 0:
118 return 0
119 sum = 0
120 for process in self.pool:
121 sum += process.turnaround_time()
122 return float(sum) / len(self.pool)
123
124 def average_waiting(self):
125 if len(self.pool) == 0:
126 return 0
127 sum = 0
128 for process in self.pool:
129 sum += process.waiting_time()
130 return float(sum) / len(self.pool)
131
132 def begin_time(self):
133 begin = 0xEFFFFF
134 for p in self.pool:
135 if p.begin_time() < begin:
136 begin = p.begin_time()
137 return begin
138
139 def end_time(self):
140 end = 0
141 for p in self.pool:
142 if p.end_time() > end:
143 end = p.end_time()
144 return end
145
146 def throughput(self):
147 return len(self.pool) * HZ / float(self.end_time() - self.begin_time())
148
149 def print_graphic(self):
150 begin = self.begin_time()
151 end = self.end_time()
152
153 print graph_title
154
155 for i in range(begin, end+1):
156 line = "%5d " % i
157 for p in self.pool:
158 state = p.get_state(i)
159 if state & P_NEW:
160 line += "-"
161 elif state == P_READY or state == P_READY | P_WAITING:
162 line += "|"
163 elif state == P_RUNNING:
164 line += "#"
165 elif state == P_WAITING:
166 line += ":"
167 elif state & P_EXIT:
168 line += "-"
169 elif state == P_NULL:
170 line += " "
171 elif state & P_RUNNING:
172 line += "+"
173 else:
174 assert False
175 if p.get_state(i-1) != state and state != P_NULL:
176 line += "%-3d" % p.getpid()
177 else:
178 line += " "
179 print line
180
181class process_pool:
182 def __init__(self):
183 self.list = []
184
185 def get_process(self, pid):
186 for process in self.list:
187 if process.getpid() == pid:
188 return process
189 return None
190
191 def remove(self, pid):
192 for process in self.list:
193 if process.getpid() == pid:
194 self.list.remove(process)
195
196 def new(self, pid, time):
197 p = self.get_process(pid)
198 if p:
199 if pid != 0:
200 raise DuplicateNew(pid)
201 else:
202 p.states=[(P_NEW, time)]
203 else:
204 p = process(pid, time)
205 self.list.append(p)
206 return p
207
208 def add(self, p):
209 self.list.append(p)
210
211 def __len__(self):
212 return len(self.list)
213
214 def __iter__(self):
215 return iter(self.list)
216
217class process:
218 def __init__(self, pid, time):
219 self.pid = pid
220 self.states = [(P_NEW, time)]
221
222 def getpid(self):
223 return self.pid
224
225 def change_state(self, state, time):
226 last_state, last_time = self.states[-1]
227 if state == P_NEW:
228 raise DuplicateNew(pid)
229 if time < last_time:
230 raise BadTime(time)
231 if last_state == P_EXIT:
232 raise TaskHasExited(state)
233 if last_state == state and self.pid != 0: # task 0 can have duplicate state
234 raise RepeatState(self.pid)
235
236 self.states.append((state, time))
237
238 def get_state(self, time):
239 rval = P_NULL
240 combo = P_NULL
241 if self.begin_time() <= time <= self.end_time():
242 for state, s_time in self.states:
243 if s_time < time:
244 rval = state
245 elif s_time == time:
246 combo |= state
247 else:
248 break
249 if combo:
250 rval = combo
251 return rval
252
253 def turnaround_time(self):
254 return self.states[-1][S_TIME] - self.states[0][S_TIME]
255
256 def waiting_time(self):
257 return self.state_last_time(P_READY)
258
259 def cpu_time(self):
260 return self.state_last_time(P_RUNNING)
261
262 def io_time(self):
263 return self.state_last_time(P_WAITING)
264
265 def state_last_time(self, state):
266 time = 0
267 state_begin = 0
268 for s,t in self.states:
269 if s == state:
270 state_begin = t
271 elif state_begin != 0:
272 assert state_begin <= t
273 time += t - state_begin
274 state_begin = 0
275 return time
276
277
278 def begin_time(self):
279 return self.states[0][S_TIME]
280
281 def end_time(self):
282 return self.states[-1][S_TIME]
283
284# Enter point
285if len(sys.argv) < 2:
286 print usage.replace("%s", sys.argv[0])
287 sys.exit(0)
288
289# parse arguments
290include = []
291exclude = []
292unit_ms = False
293graphic = False
294ex_mark = False
295
296try:
297 for arg in sys.argv[2:]:
298 if arg == '-m':
299 unit_ms = True
300 continue
301 if arg == '-g':
302 graphic = True
303 continue
304 if not ex_mark:
305 if arg == '-x':
306 ex_mark = True
307 else:
308 include.append(int(arg))
309 else:
310 exclude.append(int(arg))
311except ValueError:
312 print "Bad argument '%s'" % arg
313 sys.exit(-1)
314
315# parse log file and construct processes
316processes = process_pool()
317
318f = open(sys.argv[1], "r")
319
320# Patch process 0's New & Run state
321processes.new(0, 40).change_state(P_RUNNING, 40)
322
323try:
324 prev_time = 0
325 prev_line = ""
326 for lineno, line in enumerate(f):
327
328 if line == prev_line:
329 raise SameLine
330 prev_line = line
331
332 fields = line.split("\t")
333 if len(fields) != 3:
334 raise BadFormat
335
336 pid = int(fields[0])
337 s = fields[1].upper()
338
339 time = int(fields[2])
340 if time < prev_time:
341 raise BadTime(time)
342 prev_time = time
343
344 p = processes.get_process(pid)
345
346 state = P_NULL
347 if s == 'N':
348 processes.new(pid, time)
349 elif s == 'J':
350 state = P_READY
351 elif s == 'R':
352 state = P_RUNNING
353 elif s == 'W':
354 state = P_WAITING
355 elif s == 'E':
356 state = P_EXIT
357 else:
358 raise UnknownState(s)
359 if state != P_NULL:
360 if not p:
361 raise NoNew(pid, s)
362 p.change_state(state, time)
363except MyError, err:
364 print "Error at line %d: %s" % (lineno+1, err)
365 sys.exit(0)
366
367# Stats
368stats = statistics(processes, include, exclude)
369att = stats.average_turnaround()
370awt = stats.average_waiting()
371if unit_ms:
372 unit = "ms"
373 att *= 1000/HZ
374 awt *= 1000/HZ
375else:
376 unit = "tick"
377print "(Unit: %s)" % unit
378print "Process Turnaround Waiting CPU Burst I/O Burst"
379for pid in stats.list_pid():
380 p = processes.get_process(pid)
381 tt = p.turnaround_time()
382 wt = p.waiting_time()
383 cpu = p.cpu_time()
384 io = p.io_time()
385
386 if unit_ms:
387 print "%7d %10d %7d %9d %9d" % (pid, tt*1000/HZ, wt*1000/HZ, cpu*1000/HZ, io*1000/HZ)
388 else:
389 print "%7d %10d %7d %9d %9d" % (pid, tt, wt, cpu, io)
390print "Average: %10.2f %7.2f" % (att, awt)
391print "Throughout: %.2f/s" % (stats.throughput())
392
393if graphic:
394 stats.print_graphic()