diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2014-01-17 16:57:20 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2014-01-17 16:57:20 -0500 |
commit | d90a8d25b1026ea41e4cd3041ad2ba03732b99a3 (patch) | |
tree | a7eccf0f6023273be9e6b8bcb58093563fbe06ce | |
parent | f3106e83c7404e9de96117770f210cf6a207cc2d (diff) |
Support PGM-based sched trace analysis
-rw-r--r-- | parse/sched.py | 211 |
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 | ||
12 | class TimeTracker: | 12 | class 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 |
59 | TaskParams = namedtuple('TaskParams', ['wcet', 'period', 'cpu']) | 61 | TaskParams = namedtuple('TaskParams', ['wcet', 'period', 'cpu']) |
60 | TaskData = recordtype('TaskData', ['params', 'jobs', 'blocks', 'misses']) | 62 | PgmTaskParams = namedtuple('PgmTaskParams', ['node_type', 'gid']) |
63 | TaskData = recordtype('TaskData', ['params', 'pgm_params', 'jobs', 'blocks', | ||
64 | 'response', 'lateness', 'misses', | ||
65 | 'pgm_response', 'pgm_lateness', 'pgm_misses']) | ||
66 | GraphParams = recordtype('GraphParams', ['gid', 'src', 'sink']) | ||
67 | GraphData = recordtype('GraphData', ['params', 'jobs', 'response']) | ||
68 | |||
69 | PGM_NOT_A_NODE = 0 | ||
70 | PGM_SRC = 1 | ||
71 | PGM_SINK = 2 | ||
72 | PGM_SRC_SINK = 3 | ||
73 | PGM_INTERNAL = 4 | ||
61 | 74 | ||
62 | # Map of event ids to corresponding class and format | 75 | # Map of event ids to corresponding class and format |
63 | record_map = {} | 76 | record_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 | ||
140 | def read_data(task_dict, fnames): | 153 | def 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 | ||
170 | class SchedRecord(object): | 184 | class 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 | |||
181 | class ParamRecord(SchedRecord): | 198 | class 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 | ||
189 | class ReleaseRecord(SchedRecord): | 207 | class 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 | ||
198 | class CompletionRecord(SchedRecord): | 234 | class 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 | ||
204 | class BlockRecord(SchedRecord): | 256 | class 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 | ||
268 | class SysReleaseRecord(SchedRecord): | ||
269 | FIELDS = [('when', c_uint64), ('release', c_uint64)] | ||
270 | |||
271 | def process(self, task_dict): | ||
272 | pass | ||
273 | |||
274 | class 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 | |||
303 | class 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 |
217 | register_record(2, ParamRecord) | 327 | register_record(2, ParamRecord) |
218 | register_record(3, ReleaseRecord) | 328 | register_record(3, ReleaseRecord) |
219 | register_record(7, CompletionRecord) | 329 | register_record(7, CompletionRecord) |
220 | register_record(8, BlockRecord) | 330 | register_record(8, BlockRecord) |
221 | register_record(9, ResumeRecord) | 331 | register_record(9, ResumeRecord) |
332 | register_record(11, SysReleaseRecord) | ||
333 | register_record(12, PgmParamRecord) | ||
334 | register_record(13, PgmReleaseRecord) | ||
222 | 335 | ||
223 | def create_task_dict(data_dir, work_dir = None): | 336 | def 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 | ||
249 | LOSS_MSG = """Found task missing more than %d%% of its scheduling records. | 366 | LOSS_MSG = """Found task missing more than %d%% of its scheduling records. |
250 | These won't be included in scheduling statistics!"""%(100*conf.MAX_RECORD_LOSS) | 367 | These 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. | |||
253 | If a measurement is missing, this is why.""" | 370 | If a measurement is missing, this is why.""" |
254 | 371 | ||
255 | def extract_sched_data(result, data_dir, work_dir): | 372 | def 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) | ||