aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlenn Elliott <gelliott@cs.unc.edu>2014-01-17 16:57:20 -0500
committerGlenn Elliott <gelliott@cs.unc.edu>2014-01-17 16:57:20 -0500
commitd90a8d25b1026ea41e4cd3041ad2ba03732b99a3 (patch)
treea7eccf0f6023273be9e6b8bcb58093563fbe06ce
parentf3106e83c7404e9de96117770f210cf6a207cc2d (diff)
Support PGM-based sched trace analysis
-rw-r--r--parse/sched.py211
1 files changed, 192 insertions, 19 deletions
diff --git a/parse/sched.py b/parse/sched.py
index a25ec15..652378b 100644
--- a/parse/sched.py
+++ b/parse/sched.py
@@ -11,7 +11,7 @@ from ctypes import *
11 11
12class TimeTracker: 12class TimeTracker:
13 '''Store stats for durations of time demarcated by sched_trace records.''' 13 '''Store stats for durations of time demarcated by sched_trace records.'''
14 def __init__(self): 14 def __init__(self, allow_negative = False):
15 self.begin = self.avg = self.max = self.num = self.next_job = 0 15 self.begin = self.avg = self.max = self.num = self.next_job = 0
16 16
17 # Count of times the job in start_time matched that in store_time 17 # Count of times the job in start_time matched that in store_time
@@ -19,6 +19,8 @@ class TimeTracker:
19 # And the times it didn't 19 # And the times it didn't
20 self.disjoints = 0 20 self.disjoints = 0
21 21
22 self.allow_negative = allow_negative
23
22 # Measurements are recorded in store_ time using the previous matching 24 # Measurements are recorded in store_ time using the previous matching
23 # record which was passed to store_time. This way, the last record for 25 # record which was passed to store_time. This way, the last record for
24 # any task is always skipped 26 # any task is always skipped
@@ -34,7 +36,7 @@ class TimeTracker:
34 if self.last_record: 36 if self.last_record:
35 self.matches += 1 37 self.matches += 1
36 38
37 if dur > 0: 39 if dur > 0 or self.allow_negative:
38 self.max = max(self.max, dur) 40 self.max = max(self.max, dur)
39 self.avg *= float(self.num / (self.num + 1)) 41 self.avg *= float(self.num / (self.num + 1))
40 self.num += 1 42 self.num += 1
@@ -56,8 +58,19 @@ class TimeTracker:
56 self.next_job = record.job 58 self.next_job = record.job
57 59
58# Data stored for each task 60# Data stored for each task
59TaskParams = namedtuple('TaskParams', ['wcet', 'period', 'cpu']) 61TaskParams = namedtuple('TaskParams', ['wcet', 'period', 'cpu'])
60TaskData = recordtype('TaskData', ['params', 'jobs', 'blocks', 'misses']) 62PgmTaskParams = namedtuple('PgmTaskParams', ['node_type', 'gid'])
63TaskData = recordtype('TaskData', ['params', 'pgm_params', 'jobs', 'blocks',
64 'response', 'lateness', 'misses',
65 'pgm_response', 'pgm_lateness', 'pgm_misses'])
66GraphParams = recordtype('GraphParams', ['gid', 'src', 'sink'])
67GraphData = recordtype('GraphData', ['params', 'jobs', 'response'])
68
69PGM_NOT_A_NODE = 0
70PGM_SRC = 1
71PGM_SINK = 2
72PGM_SRC_SINK = 3
73PGM_INTERNAL = 4
61 74
62# Map of event ids to corresponding class and format 75# Map of event ids to corresponding class and format
63record_map = {} 76record_map = {}
@@ -137,7 +150,7 @@ def make_iterator(fname):
137 # Results from the first job are nonsense 150 # Results from the first job are nonsense
138 pass 151 pass
139 152
140def read_data(task_dict, fnames): 153def read_data(task_dict, graph_dict, fnames):
141 '''Read records from @fnames and store per-pid stats in @task_dict.''' 154 '''Read records from @fnames and store per-pid stats in @task_dict.'''
142 buff = [] 155 buff = []
143 156
@@ -166,6 +179,7 @@ def read_data(task_dict, fnames):
166 179
167 add_record(itera) 180 add_record(itera)
168 record.process(task_dict) 181 record.process(task_dict)
182 record.process_pgm(task_dict, graph_dict)
169 183
170class SchedRecord(object): 184class SchedRecord(object):
171 # Subclasses will have their FIELDs merged into this one 185 # Subclasses will have their FIELDs merged into this one
@@ -178,28 +192,66 @@ class SchedRecord(object):
178 def process(self, task_dict): 192 def process(self, task_dict):
179 raise NotImplementedError() 193 raise NotImplementedError()
180 194
195 def process_pgm(self, task_dict, graph_dict):
196 pass
197
181class ParamRecord(SchedRecord): 198class ParamRecord(SchedRecord):
182 FIELDS = [('wcet', c_uint32), ('period', c_uint32), 199 FIELDS = [('wcet', c_uint32), ('period', c_uint32),
183 ('phase', c_uint32), ('partition', c_uint8)] 200 ('phase', c_uint32), ('partition', c_uint8),
201 ('class', c_uint8)]
184 202
185 def process(self, task_dict): 203 def process(self, task_dict):
186 params = TaskParams(self.wcet, self.period, self.partition) 204 params = TaskParams(self.wcet, self.period, self.partition)
187 task_dict[self.pid].params = params 205 task_dict[self.pid].params = params
188 206
189class ReleaseRecord(SchedRecord): 207class ReleaseRecord(SchedRecord):
190 FIELDS = [('when', c_uint64), ('release', c_uint64)] 208 # renames the 'release' field to 'when'
209 FIELDS = [('when', c_uint64), ('deadline', c_uint64)]
191 210
192 def process(self, task_dict): 211 def process(self, task_dict):
193 data = task_dict[self.pid] 212 data = task_dict[self.pid]
194 data.jobs += 1 213 data.jobs += 1
195 if data.params: 214 data.response.start_time(self, self.when)
196 data.misses.start_time(self, self.when + data.params.period) 215 data.misses.start_time(self, self.deadline)
216 data.lateness.start_time(self, self.deadline)
217
218 print ' release %d: r=%d d=%d' % (self.pid, self.when, self.deadline)
219
220 def process_pgm(self, task_dict, graph_dict):
221 data = task_dict[self.pid]
222 data.pgm_response.start_time(self, self.when)
223 data.pgm_misses.start_time(self, self.deadline)
224 data.pgm_lateness.start_time(self, self.deadline)
225
226 return
227 ntype = task_dict[self.pid].pgm_params.node_type
228 if ntype == PGM_SRC or ntype == PGM_SRC_SINK:
229 gid = task_dict[self.pid].pgm_params.graph_pid
230 gdata = graph_dict[gid]
231 gdata.jobs += 1
232 gdata.response.start_time(self, self.when)
197 233
198class CompletionRecord(SchedRecord): 234class CompletionRecord(SchedRecord):
199 FIELDS = [('when', c_uint64)] 235 FIELDS = [('when', c_uint64)]
200 236
201 def process(self, task_dict): 237 def process(self, task_dict):
202 task_dict[self.pid].misses.store_time(self) 238 data = task_dict[self.pid]
239 data.response.store_time(self)
240 data.misses.store_time(self)
241 data.lateness.store_time(self)
242
243 def process_pgm(self, task_dict, graph_dict):
244 data = task_dict[self.pid]
245 data.pgm_response.store_time(self)
246 data.pgm_misses.store_time(self)
247 data.pgm_lateness.store_time(self)
248
249 return
250 ntype = data.pgm_params.node_type
251 if ntype == PGM_SINK or ntype == PGM_SRC_SINK:
252 gid = data.pgm_params.graph_pid
253 gdata = graph_dict[gid]
254 gdata.response.store_time(self)
203 255
204class BlockRecord(SchedRecord): 256class BlockRecord(SchedRecord):
205 FIELDS = [('when', c_uint64)] 257 FIELDS = [('when', c_uint64)]
@@ -213,20 +265,85 @@ class ResumeRecord(SchedRecord):
213 def process(self, task_dict): 265 def process(self, task_dict):
214 task_dict[self.pid].blocks.store_time(self) 266 task_dict[self.pid].blocks.store_time(self)
215 267
268class SysReleaseRecord(SchedRecord):
269 FIELDS = [('when', c_uint64), ('release', c_uint64)]
270
271 def process(self, task_dict):
272 pass
273
274class PgmParamRecord(SchedRecord):
275 FIELDS = [('node_type', c_uint32), ('graph_pid', c_uint16)]
276
277 def process(self, task_dict):
278 pass
279
280 def process_pgm(self, task_dict, graph_dict):
281
282 pgm_params = PgmTaskParams(self.node_type, self.graph_pid)
283 task_dict[self.pid].pgm_params = pgm_params
284
285 print '%d: graph id = %d, node type = %d' % (self.pid, self.graph_pid, self.node_type)
286
287 if self.node_type == PGM_SRC or self.node_type == PGM_SINK or self.node_type == PGM_SRC_SINK:
288 graph_data = graph_dict[self.graph_pid]
289 if not graph_data.params:
290 graph_data.params = GraphParams(self.graph_pid, 0, 0)
291 if self.node_type == PGM_SRC:
292 assert graph_data.params.src == 0
293 graph_data.params.src = self.pid
294 elif self.node_type == PGM_SINK:
295 assert graph_data.params.sink == 0
296 graph_data.params.sink = self.pid
297 else:
298 assert graph_data.params.src == 0
299 assert graph_data.params.sink == 0
300 graph_data.params.src = self.pid
301 graph_data.params.sink = self.pid
302
303class PgmReleaseRecord(SchedRecord):
304 # renames the 'release' field to 'when'
305 FIELDS = [('when', c_uint64), ('deadline', c_uint64)]
306
307 def process(self, task_dict):
308 pass
309
310 def process_pgm(self, task_dict, graph_dict):
311 data = task_dict[self.pid]
312 data.pgm_response.start_time(self, self.when)
313 data.pgm_misses.start_time(self, self.deadline)
314 data.pgm_lateness.start_time(self, self.deadline)
315
316 print 'pgm_release %d: r=%d d=%d' % (self.pid, self.when, self.deadline)
317
318 return
319
320 ntype = data.pgm_params.node_type
321 if ntype == PGM_SRC or ntype == PGM_SRC_SINK:
322 gid = data.pgm_params.graph_pid
323 gdata = graph_dict[gid]
324 gdata.response.start_time(self, self.when)
325
216# Map records to sched_trace ids (see include/litmus/sched_trace.h 326# Map records to sched_trace ids (see include/litmus/sched_trace.h
217register_record(2, ParamRecord) 327register_record(2, ParamRecord)
218register_record(3, ReleaseRecord) 328register_record(3, ReleaseRecord)
219register_record(7, CompletionRecord) 329register_record(7, CompletionRecord)
220register_record(8, BlockRecord) 330register_record(8, BlockRecord)
221register_record(9, ResumeRecord) 331register_record(9, ResumeRecord)
332register_record(11, SysReleaseRecord)
333register_record(12, PgmParamRecord)
334register_record(13, PgmReleaseRecord)
222 335
223def create_task_dict(data_dir, work_dir = None): 336def create_trace_dict(data_dir, work_dir = None):
224 '''Parse sched trace files''' 337 '''Parse sched trace files'''
225 bin_files = conf.FILES['sched_data'].format(".*") 338 bin_files = conf.FILES['sched_data'].format(".*")
226 output_file = "%s/out-st" % work_dir 339 output_file = "%s/out-st" % work_dir
227 340
228 task_dict = defaultdict(lambda : 341 task_dict = defaultdict(lambda :
229 TaskData(None, 1, TimeTracker(), TimeTracker())) 342 TaskData(None, None, 1, TimeTracker(),
343 TimeTracker(), TimeTracker(True), TimeTracker(),
344 TimeTracker(), TimeTracker(True), TimeTracker()))
345 graph_dict = defaultdict(lambda:
346 GraphData(None, 1, TimeTracker()))
230 347
231 bin_names = [f for f in os.listdir(data_dir) if re.match(bin_files, f)] 348 bin_names = [f for f in os.listdir(data_dir) if re.match(bin_files, f)]
232 if not len(bin_names): 349 if not len(bin_names):
@@ -242,9 +359,9 @@ def create_task_dict(data_dir, work_dir = None):
242 359
243 # Gather per-task values 360 # Gather per-task values
244 bin_paths = ["%s/%s" % (data_dir,f) for f in bin_names] 361 bin_paths = ["%s/%s" % (data_dir,f) for f in bin_names]
245 read_data(task_dict, bin_paths) 362 read_data(task_dict, graph_dict, bin_paths)
246 363
247 return task_dict 364 return task_dict, graph_dict
248 365
249LOSS_MSG = """Found task missing more than %d%% of its scheduling records. 366LOSS_MSG = """Found task missing more than %d%% of its scheduling records.
250These won't be included in scheduling statistics!"""%(100*conf.MAX_RECORD_LOSS) 367These won't be included in scheduling statistics!"""%(100*conf.MAX_RECORD_LOSS)
@@ -253,8 +370,9 @@ Measurements like these are not included in scheduling statistics.
253If a measurement is missing, this is why.""" 370If a measurement is missing, this is why."""
254 371
255def extract_sched_data(result, data_dir, work_dir): 372def extract_sched_data(result, data_dir, work_dir):
256 task_dict = create_task_dict(data_dir, work_dir) 373 task_dict, graph_dict = create_trace_dict(data_dir, work_dir)
257 stat_data = defaultdict(list) 374 stat_data = defaultdict(list)
375 gstat_data = defaultdict(list)
258 376
259 # Group per-task values 377 # Group per-task values
260 for tdata in task_dict.itervalues(): 378 for tdata in task_dict.itervalues():
@@ -262,7 +380,12 @@ def extract_sched_data(result, data_dir, work_dir):
262 # Currently unknown where these invalid tasks come from... 380 # Currently unknown where these invalid tasks come from...
263 continue 381 continue
264 382
383 lateness = tdata.lateness
384 response = tdata.response
265 miss = tdata.misses 385 miss = tdata.misses
386 pgm_response = tdata.pgm_response
387 pgm_lateness = tdata.pgm_lateness
388 pgm_miss = tdata.pgm_misses
266 389
267 record_loss = float(miss.disjoints)/(miss.matches + miss.disjoints) 390 record_loss = float(miss.disjoints)/(miss.matches + miss.disjoints)
268 stat_data["record-loss"].append(record_loss) 391 stat_data["record-loss"].append(record_loss)
@@ -272,15 +395,62 @@ def extract_sched_data(result, data_dir, work_dir):
272 continue 395 continue
273 396
274 miss_ratio = float(miss.num) / miss.matches 397 miss_ratio = float(miss.num) / miss.matches
398 pgm_miss_ratio = float(pgm_miss.num) / pgm_miss.matches
399
400 # average job tardy by:
275 avg_tard = miss.avg * miss_ratio 401 avg_tard = miss.avg * miss_ratio
402 pgm_avg_tard = pgm_miss.avg * pgm_miss_ratio
276 403
404 # start with basic task information
277 stat_data["miss-ratio" ].append(miss_ratio) 405 stat_data["miss-ratio" ].append(miss_ratio)
278 406
279 stat_data["max-tard"].append(miss.max / tdata.params.period) 407 stat_data["max-response"].append(response.max)
280 stat_data["avg-tard"].append(avg_tard / tdata.params.period) 408 stat_data["avg-response"].append(response.avg)
409 stat_data["max-response-prop"].append(response.max / tdata.params.period)
410 stat_data["avg-response-prop"].append(response.avg / tdata.params.period)
411
412 stat_data["max-tard"].append(miss.max)
413 stat_data["avg-tard"].append(avg_tard)
414 stat_data["max-tard-prop"].append(miss.max / tdata.params.period)
415 stat_data["avg-tard-prop"].append(avg_tard / tdata.params.period)
416
417 stat_data["max-response"].append(lateness.max)
418 stat_data["avg-response"].append(lateness.avg)
419 stat_data["max-response-prop"].append(lateness.max / tdata.params.period)
420 stat_data["avg-response-prop"].append(lateness.avg / tdata.params.period)
421
422 # same data, but with PGM-adjusted release times (shifted deadlines)
423 stat_data["pgm-miss-ratio" ].append(pgm_miss_ratio)
424
425 stat_data["pgm-max-response"].append(pgm_response.max)
426 stat_data["pgm-avg-response"].append(pgm_response.avg)
427 stat_data["pgm-max-response-prop"].append(pgm_response.max / tdata.params.period)
428 stat_data["pgm-avg-response-prop"].append(pgm_response.avg / tdata.params.period)
429
430 stat_data["pgm-max-tard"].append(pgm_miss.max)
431 stat_data["pgm-avg-tard"].append(pgm_avg_tard)
432 stat_data["pgm-max-tard-prop"].append(pgm_miss.max / tdata.params.period)
433 stat_data["pgm-avg-tard-prop"].append(pgm_avg_tard / tdata.params.period)
434
435 stat_data["pgm-max-response"].append(pgm_lateness.max)
436 stat_data["pgm-avg-response"].append(pgm_lateness.avg)
437 stat_data["pgm-max-response-prop"].append(pgm_lateness.max / tdata.params.period)
438 stat_data["pgm-avg-response-prop"].append(pgm_lateness.avg / tdata.params.period)
439
440 for gdata in graph_dict.itervalues():
441 if not gdata.params:
442 continue
443
444 response = gdata.response
445 record_loss = float(response.disjoints)/(response.matches + response.disjoints)
446 gstat_data["graph-record-loss"].append(record_loss)
281 447
282 stat_data["avg-block"].append(tdata.blocks.avg / NSEC_PER_MSEC) 448 if record_los > conf.MAX_RECORD_LOSS:
283 stat_data["max-block"].append(tdata.blocks.max / NSEC_PER_MSEC) 449 log_once(LOSS_MSG)
450 continue
451
452 gstat_data["graph-max-response"].append(response.max)
453 gstat_data["graph-avg-response"].append(response.avg)
284 454
285 # Summarize value groups 455 # Summarize value groups
286 for name, data in stat_data.iteritems(): 456 for name, data in stat_data.iteritems():
@@ -288,3 +458,6 @@ def extract_sched_data(result, data_dir, work_dir):
288 log_once(SKIP_MSG, SKIP_MSG % name) 458 log_once(SKIP_MSG, SKIP_MSG % name)
289 continue 459 continue
290 result[name] = Measurement(str(name)).from_array(data) 460 result[name] = Measurement(str(name)).from_array(data)
461
462 for name, data in gstat_data.iteritems():
463 result[name] = Measurement(str(name)).from_array(data)