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 /parse | |
| parent | f3106e83c7404e9de96117770f210cf6a207cc2d (diff) | |
Support PGM-based sched trace analysis
Diffstat (limited to 'parse')
| -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) | ||
