diff options
Diffstat (limited to 'viz/schedule.py')
| -rw-r--r-- | viz/schedule.py | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/viz/schedule.py b/viz/schedule.py new file mode 100644 index 0000000..f842c8d --- /dev/null +++ b/viz/schedule.py | |||
| @@ -0,0 +1,571 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | """The data structures to store a schedule (task system), along with all | ||
| 4 | the job releases and other events that have occurred for each task. This gives | ||
| 5 | a high-level representation of a schedule that can be converted to, say, a | ||
| 6 | graphic.""" | ||
| 7 | |||
| 8 | from draw import * | ||
| 9 | import util | ||
| 10 | |||
| 11 | class TimeSlotArray(object): | ||
| 12 | """Represents another way of organizing the events. This structure organizes events by | ||
| 13 | the (approximate) time at which they occur. Events that occur at approximately the same | ||
| 14 | time are assigned the same ``slot'', and each slot organizes its events by task number | ||
| 15 | as well as by CPU.""" | ||
| 16 | |||
| 17 | TASK_LIST = 0 | ||
| 18 | CPU_LIST = 1 | ||
| 19 | |||
| 20 | def __init__(self, start, end, time_per_maj, num_tasks, num_cpus): | ||
| 21 | self.start = start | ||
| 22 | self.end = end | ||
| 23 | self.time_per_maj = time_per_maj | ||
| 24 | self.list_sizes = { TimeSlotArray.TASK_LIST : num_tasks, TimeSlotArray.CPU_LIST : num_cpus } | ||
| 25 | self.array = [{TimeSlotArray.TASK_LIST : [{} for i in range(0, num_tasks)], \ | ||
| 26 | TimeSlotArray.CPU_LIST : [{} for i in range (0, num_cpus)]} \ | ||
| 27 | for i in range(0, (end - start) // self.time_per_maj + 1)] | ||
| 28 | |||
| 29 | def get_time_slot(self, time): | ||
| 30 | return int((time - self.start) // self.time_per_maj) | ||
| 31 | |||
| 32 | def add_event_to_time_slot(self, event): | ||
| 33 | task_no = event.get_job().get_task().get_task_no() | ||
| 34 | cpu = event.get_cpu() | ||
| 35 | time_slot = self.get_time_slot(event.get_time()) | ||
| 36 | |||
| 37 | self.array[time_slot][TimeSlotArray.TASK_LIST][task_no][event.__class__] = event | ||
| 38 | self.array[time_slot][TimeSlotArray.CPU_LIST][cpu][event.__class__] = event | ||
| 39 | |||
| 40 | span_events = { SwitchAwayEvent : IsRunningDummy, InversionEndEvent : InversionDummy} | ||
| 41 | |||
| 42 | for span_event in span_events: | ||
| 43 | if isinstance(event, span_event) and not event.is_erroneous(): | ||
| 44 | start_slot = self.get_time_slot(event.corresp_start_event.get_time()) | ||
| 45 | end_slot = self.get_time_slot(event.get_time()) | ||
| 46 | for slot in range(start_slot + 1, end_slot): | ||
| 47 | dummy = span_events[span_event](task_no, cpu) | ||
| 48 | dummy.corresp_start_event = event.corresp_start_event | ||
| 49 | self.array[slot][TimeSlotArray.TASK_LIST][task_no][dummy.__class__] = dummy | ||
| 50 | self.array[slot][TimeSlotArray.CPU_LIST][cpu][dummy.__class__] = dummy | ||
| 51 | |||
| 52 | def iter_over_period(self, start, end, start_no, end_no, list_type, event_types): | ||
| 53 | if start > end: | ||
| 54 | raise ValueError('Litmus is not a time machine') | ||
| 55 | if start_no > end_no: | ||
| 56 | raise ValueError('start no should be less than end no') | ||
| 57 | |||
| 58 | start_slot = max(0, self.get_time_slot(start)) | ||
| 59 | end_slot = min(len(self.array), self.get_time_slot(end) + 2) | ||
| 60 | |||
| 61 | start_no = max(0, start_no) | ||
| 62 | end_no = min(self.list_sizes[list_type] - 1, end_no) | ||
| 63 | |||
| 64 | for slot in range(start_slot, end_slot): | ||
| 65 | for no in range(start_no, end_no + 1): | ||
| 66 | for type in event_types: | ||
| 67 | if type in self.array[slot][list_type][no]: | ||
| 68 | yield self.array[slot][list_type][no][type] | ||
| 69 | |||
| 70 | class Schedule(object): | ||
| 71 | """The total schedule (task system), consisting of a certain number of | ||
| 72 | tasks.""" | ||
| 73 | |||
| 74 | def __init__(self, name, num_cpus, task_list=[]): | ||
| 75 | self.name = name | ||
| 76 | self.tasks = {} | ||
| 77 | self.task_list = [] | ||
| 78 | self.time_slot_array = None | ||
| 79 | self.cur_task_no = 0 | ||
| 80 | self.num_cpus = num_cpus | ||
| 81 | for task in task_list: | ||
| 82 | self.add_task(task) | ||
| 83 | |||
| 84 | def set_time_params(self, time_per_maj=None): | ||
| 85 | if self.get_task_list() is None: | ||
| 86 | return (0, 0) | ||
| 87 | |||
| 88 | def find_extreme_time_sched(sched, cmp): | ||
| 89 | def find_extreme_time_task(task, cmp): | ||
| 90 | def find_extreme_time_job(job, cmp): | ||
| 91 | extreme_time = None | ||
| 92 | for time in job.get_events(): | ||
| 93 | if extreme_time is None or cmp(time, extreme_time) < 0: | ||
| 94 | extreme_time = time | ||
| 95 | return extreme_time | ||
| 96 | |||
| 97 | extreme_time = None | ||
| 98 | for job_no in task.get_jobs(): | ||
| 99 | time = find_extreme_time_job(task.get_jobs()[job_no], cmp) | ||
| 100 | if time is not None and (extreme_time is None or cmp(time, extreme_time) < 0): | ||
| 101 | extreme_time = time | ||
| 102 | return extreme_time | ||
| 103 | |||
| 104 | extreme_time = None | ||
| 105 | for task in sched.get_task_list(): | ||
| 106 | time = find_extreme_time_task(task, cmp) | ||
| 107 | if time is not None and (extreme_time is None or cmp(time, extreme_time) < 0): | ||
| 108 | extreme_time = time | ||
| 109 | |||
| 110 | return extreme_time | ||
| 111 | |||
| 112 | def earliest_cmp(x, y): | ||
| 113 | diff = x - y | ||
| 114 | if diff > 0.0: | ||
| 115 | return 1 | ||
| 116 | elif diff == 0.0: | ||
| 117 | return 0 | ||
| 118 | elif diff < 0.0: | ||
| 119 | return -1 | ||
| 120 | |||
| 121 | def latest_cmp(x, y): | ||
| 122 | diff = x - y | ||
| 123 | if diff < 0.0: | ||
| 124 | return 1 | ||
| 125 | elif diff == 0.0: | ||
| 126 | return 0 | ||
| 127 | elif diff > 0.0: | ||
| 128 | return -1 | ||
| 129 | |||
| 130 | self.start = find_extreme_time_sched(self, earliest_cmp) | ||
| 131 | self.end = find_extreme_time_sched(self, latest_cmp) | ||
| 132 | self.time_per_maj = time_per_maj | ||
| 133 | self.time_slot_array = None | ||
| 134 | if self.time_per_maj is not None: | ||
| 135 | self.time_slot_array = TimeSlotArray(self.start, self.end, time_per_maj, \ | ||
| 136 | len(self.task_list), self.num_cpus) | ||
| 137 | |||
| 138 | def get_time_slot_array(self): | ||
| 139 | return self.time_slot_array | ||
| 140 | |||
| 141 | def get_time_bounds(self): | ||
| 142 | return (self.start, self.end) | ||
| 143 | |||
| 144 | def scan(self, time_per_maj): | ||
| 145 | self.set_time_params(time_per_maj) | ||
| 146 | |||
| 147 | # we scan the graph task by task, and job by job | ||
| 148 | switches = {} | ||
| 149 | for event in EVENT_LIST: | ||
| 150 | switches[event] = None | ||
| 151 | for task_no, task in enumerate(self.get_task_list()): | ||
| 152 | cur_cpu = [Event.NO_CPU] | ||
| 153 | for job_no in sorted(task.get_jobs().keys()): | ||
| 154 | job = task.get_jobs()[job_no] | ||
| 155 | for event_time in sorted(job.get_events().keys()): | ||
| 156 | # could have multiple events at the same time (unlikely but possible) | ||
| 157 | for event in job.get_events()[event_time]: | ||
| 158 | print "task, job, event:", task.name, job.job_no, event.__class__.__name__ | ||
| 159 | event.scan(cur_cpu, switches) | ||
| 160 | |||
| 161 | def add_task(self, task): | ||
| 162 | if task.name in self.tasks: | ||
| 163 | raise ValueError("task already in list!") | ||
| 164 | self.tasks[task.name] = task | ||
| 165 | self.task_list.append(task) | ||
| 166 | task.schedule = self | ||
| 167 | task.task_no = self.cur_task_no | ||
| 168 | self.cur_task_no += 1 | ||
| 169 | |||
| 170 | def get_tasks(self): | ||
| 171 | return self.tasks | ||
| 172 | |||
| 173 | def get_task_list(self): | ||
| 174 | return self.task_list | ||
| 175 | |||
| 176 | def get_name(self): | ||
| 177 | return self.name | ||
| 178 | |||
| 179 | def get_num_cpus(self): | ||
| 180 | return self.num_cpus | ||
| 181 | |||
| 182 | class Task(object): | ||
| 183 | """Represents a task, including the set of jobs that were run under | ||
| 184 | this task.""" | ||
| 185 | |||
| 186 | def __init__(self, name, job_list=[]): | ||
| 187 | self.name = name | ||
| 188 | self.jobs = {} | ||
| 189 | self.task_no = None | ||
| 190 | self.schedule = None | ||
| 191 | for job in job_list: | ||
| 192 | self.add_job(job) | ||
| 193 | |||
| 194 | def add_job(self, job): | ||
| 195 | if job.job_no in self.jobs: | ||
| 196 | raise ScheduleError("a job is already being released at this time for this task") | ||
| 197 | self.jobs[job.job_no] = job | ||
| 198 | job.task = self | ||
| 199 | |||
| 200 | def get_schedule(self): | ||
| 201 | return self.schedule | ||
| 202 | |||
| 203 | def get_jobs(self): | ||
| 204 | return self.jobs | ||
| 205 | |||
| 206 | def get_task_no(self): | ||
| 207 | return self.task_no | ||
| 208 | |||
| 209 | def get_name(self): | ||
| 210 | return self.name | ||
| 211 | |||
| 212 | class Job(object): | ||
| 213 | """Represents a job, including everything that happens related to the job""" | ||
| 214 | def __init__(self, job_no, event_list=[]): | ||
| 215 | self.job_no = job_no | ||
| 216 | self.events = {} | ||
| 217 | self.task = None | ||
| 218 | for event in event_list: | ||
| 219 | self.add_event(event) | ||
| 220 | |||
| 221 | def add_event(self, event): | ||
| 222 | if event.time not in self.events: | ||
| 223 | self.events[event.time] = [] | ||
| 224 | self.events[event.time].append(event) | ||
| 225 | event.job = self | ||
| 226 | |||
| 227 | def get_events(self): | ||
| 228 | return self.events | ||
| 229 | |||
| 230 | def get_task(self): | ||
| 231 | return self.task | ||
| 232 | |||
| 233 | def get_job_no(self): | ||
| 234 | return self.job_no | ||
| 235 | |||
| 236 | class DummyEvent(object): | ||
| 237 | """Represents some event that occurs, but might not actually be | ||
| 238 | a full-fledged ``event'' in the schedule. It might instead be a dummy | ||
| 239 | event added by the application to speed things up or keep track of | ||
| 240 | something. Such an event won't be added to the schedule tree, but | ||
| 241 | might appear in the time slot array.""" | ||
| 242 | |||
| 243 | def __init__(self, time, cpu): | ||
| 244 | self.time = time | ||
| 245 | self.cpu = cpu | ||
| 246 | self.job = None | ||
| 247 | self.layer = None | ||
| 248 | |||
| 249 | def __str__(self): | ||
| 250 | return '[Dummy Event]' | ||
| 251 | |||
| 252 | def get_time(self): | ||
| 253 | return self.time | ||
| 254 | |||
| 255 | def get_cpu(self): | ||
| 256 | return self.cpu | ||
| 257 | |||
| 258 | def get_job(self): | ||
| 259 | return self.job | ||
| 260 | |||
| 261 | def get_layer(self): | ||
| 262 | return self.layer | ||
| 263 | |||
| 264 | def render(self, graph, layer, prev_events): | ||
| 265 | """Method that the visualizer calls to tell the event to render itself | ||
| 266 | Obviously only implemented by subclasses (actual event types)""" | ||
| 267 | raise NotImplementdError | ||
| 268 | |||
| 269 | class Event(DummyEvent): | ||
| 270 | """Represents an event that occurs while a job is running (e.g. get scheduled | ||
| 271 | on a CPU, block, ...)""" | ||
| 272 | NO_CPU = -1 | ||
| 273 | NUM_DEC_PLACES = 2 | ||
| 274 | |||
| 275 | def __init__(self, time, cpu): | ||
| 276 | super(Event, self).__init__(time, cpu) | ||
| 277 | self.erroneous = False | ||
| 278 | self.selected = False | ||
| 279 | |||
| 280 | def __str__(self): | ||
| 281 | return '[Event]' | ||
| 282 | |||
| 283 | def _common_str(self): | ||
| 284 | job = self.get_job() | ||
| 285 | task = job.get_task() | ||
| 286 | return ' for task ' + str(task.get_name()) + ': (TASK, JOB)=' + str((task.get_task_no(), \ | ||
| 287 | job.get_job_no())) + ', CPU=' + str(self.get_cpu()) | ||
| 288 | |||
| 289 | def is_erroneous(self): | ||
| 290 | """An erroneous event is where something with the event is not quite right, | ||
| 291 | something significantly wrong that we don't have logical information telling | ||
| 292 | us how we should render the event.""" | ||
| 293 | return self.erroneous | ||
| 294 | |||
| 295 | def is_selected(self): | ||
| 296 | """Returns whether the event has been selected by the user. (needed for rendering)""" | ||
| 297 | return self.selected | ||
| 298 | |||
| 299 | def set_selected(self, sel): | ||
| 300 | """Sets the event's state to selected.""" | ||
| 301 | self.selected = sel | ||
| 302 | |||
| 303 | def scan(self, cur_cpu, switches): | ||
| 304 | """Part of the procedure that walks through all the events and sets | ||
| 305 | some parameters that are unknown at first. For instance, a SwitchAwayEvent | ||
| 306 | should know when the previous corresponding SwitchToEvent occurred, but | ||
| 307 | the data does not tell us this, so we have to figure that out on our own | ||
| 308 | by scanning through the events. ``cur_cpu'' gives the current CPU at this | ||
| 309 | time in the scan, and ``switches'' gives the last time a certain switch | ||
| 310 | (e.g. SwitchToEvent, InversionStartEvent) occurred""" | ||
| 311 | |||
| 312 | self.get_job().get_task().get_schedule().get_time_slot_array().add_event_to_time_slot(self) | ||
| 313 | |||
| 314 | class ErrorEvent(Event): | ||
| 315 | pass | ||
| 316 | |||
| 317 | class SuspendEvent(Event): | ||
| 318 | def __init__(self, time, cpu): | ||
| 319 | super(SuspendEvent, self).__init__(time, cpu) | ||
| 320 | self.layer = Canvas.MIDDLE_LAYER | ||
| 321 | |||
| 322 | def __str__(self): | ||
| 323 | return 'Suspend' + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | ||
| 324 | |||
| 325 | def scan(self, cur_cpu, switches): | ||
| 326 | if self.get_cpu() != cur_cpu[0]: | ||
| 327 | self.erroneous = True | ||
| 328 | print "suspending on a CPU different from the CPU we are on!" | ||
| 329 | super(SuspendEvent, self).scan(cur_cpu, switches) | ||
| 330 | |||
| 331 | def render(self, graph, layer, prev_events): | ||
| 332 | if layer == self.layer: | ||
| 333 | prev_events[self] = None | ||
| 334 | graph.draw_suspend_triangle_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | ||
| 335 | self.get_cpu(), self.is_selected()) | ||
| 336 | graph.add_sel_suspend_triangle_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | ||
| 337 | self.get_cpu(), self) | ||
| 338 | |||
| 339 | class ResumeEvent(Event): | ||
| 340 | def __init__(self, time, cpu): | ||
| 341 | super(ResumeEvent, self).__init__(time, cpu) | ||
| 342 | self.layer = Canvas.MIDDLE_LAYER | ||
| 343 | |||
| 344 | def __str__(self): | ||
| 345 | return 'Resume' + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | ||
| 346 | |||
| 347 | def scan(self, cur_cpu, switches): | ||
| 348 | if cur_cpu[0] != Event.NO_CPU and cur_cpu[0] != self.get_cpu(): | ||
| 349 | self.erroneous = True | ||
| 350 | print "Resuming when currently scheduled on a CPU, but on a different CPU from the current CPU!" | ||
| 351 | super(ResumeEvent, self).scan(cur_cpu, switches) | ||
| 352 | |||
| 353 | def render(self, graph, layer, prev_events): | ||
| 354 | if layer == self.layer: | ||
| 355 | prev_events[self] = None | ||
| 356 | graph.draw_resume_triangle_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | ||
| 357 | self.get_cpu(), self.is_selected()) | ||
| 358 | graph.add_sel_resume_triangle_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | ||
| 359 | self.get_cpu(), self) | ||
| 360 | |||
| 361 | class CompleteEvent(Event): | ||
| 362 | def __init__(self, time, cpu): | ||
| 363 | super(CompleteEvent, self).__init__(time, cpu) | ||
| 364 | self.layer = Canvas.TOP_LAYER | ||
| 365 | |||
| 366 | def __str__(self): | ||
| 367 | return 'Complete' + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | ||
| 368 | |||
| 369 | def scan(self, cur_cpu, switches): | ||
| 370 | super(CompleteEvent, self).scan(cur_cpu, switches) | ||
| 371 | |||
| 372 | def render(self, graph, layer, prev_events): | ||
| 373 | if layer == Canvas.TOP_LAYER: | ||
| 374 | prev_events[self] = None | ||
| 375 | graph.draw_completion_marker_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | ||
| 376 | self.get_cpu(), self.is_selected()) | ||
| 377 | graph.add_sel_completion_marker_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | ||
| 378 | self.get_cpu(), self) | ||
| 379 | |||
| 380 | |||
| 381 | class SwitchAwayEvent(Event): | ||
| 382 | def __init__(self, time, cpu): | ||
| 383 | super(SwitchAwayEvent, self).__init__(time, cpu) | ||
| 384 | self.layer = Canvas.BOTTOM_LAYER | ||
| 385 | |||
| 386 | def __str__(self): | ||
| 387 | if self.corresp_start_event is None: | ||
| 388 | return 'Switch Away (w/o Switch To)' + self._common_str() + 'TIME=' \ | ||
| 389 | + self.get_time() | ||
| 390 | return str(self.corresp_start_event) | ||
| 391 | |||
| 392 | def scan(self, cur_cpu, switches): | ||
| 393 | old_cur_cpu = cur_cpu[0] | ||
| 394 | |||
| 395 | self.corresp_start_event = switches[SwitchToEvent] | ||
| 396 | |||
| 397 | cur_cpu[0] = Event.NO_CPU | ||
| 398 | switches[SwitchToEvent] = None | ||
| 399 | |||
| 400 | if self.corresp_start_event is not None: | ||
| 401 | self.corresp_start_event.corresp_end_event = self | ||
| 402 | |||
| 403 | if self.get_cpu() != old_cur_cpu: | ||
| 404 | self.erroneous = True | ||
| 405 | print "switching away from a CPU different from the CPU we are currently on" | ||
| 406 | if self.corresp_start_event is None: | ||
| 407 | self.erroneous = True | ||
| 408 | print "switch away was not matched by a corresponding switch to" | ||
| 409 | elif self.get_time() < self.corresp_start_event.get_time(): | ||
| 410 | self.erroneous = True | ||
| 411 | print "switching away from a processor before we switched to it?!" | ||
| 412 | |||
| 413 | super(SwitchAwayEvent, self).scan(cur_cpu, switches) | ||
| 414 | |||
| 415 | def render(self, graph, layer, prev_events): | ||
| 416 | if self.corresp_start_event is None or self.corresp_start_event in prev_events: | ||
| 417 | return # erroneous switch away or already rendered | ||
| 418 | self.corresp_start_event.render(graph, layer, prev_events) | ||
| 419 | |||
| 420 | class SwitchToEvent(Event): | ||
| 421 | def __init__(self, time, cpu): | ||
| 422 | super(SwitchToEvent, self).__init__(time, cpu) | ||
| 423 | self.layer = Canvas.BOTTOM_LAYER | ||
| 424 | |||
| 425 | def __str__(self): | ||
| 426 | if self.corresp_end_event is None: | ||
| 427 | return 'Switch To (w/o Switch Away)' + self._common_str() + ', TIME=' \ | ||
| 428 | + self.get_time() | ||
| 429 | return 'Scheduled' + self._common_str() + ', START=' \ | ||
| 430 | + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ | ||
| 431 | + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) | ||
| 432 | |||
| 433 | def scan(self, cur_cpu, switches): | ||
| 434 | old_cur_cpu = cur_cpu[0] | ||
| 435 | cur_cpu[0] = self.get_cpu() | ||
| 436 | switches[SwitchToEvent] = self | ||
| 437 | self.corresp_end_event = None | ||
| 438 | |||
| 439 | if old_cur_cpu != Event.NO_CPU: | ||
| 440 | self.erroneous = True | ||
| 441 | print "currently scheduled somewhere, can't switch to a CPU" | ||
| 442 | |||
| 443 | super(SwitchToEvent, self).scan(cur_cpu, switches) | ||
| 444 | |||
| 445 | def render(self, graph, layer, prev_events): | ||
| 446 | if self.is_erroneous(): | ||
| 447 | return # erroneous switch to | ||
| 448 | if layer == Canvas.BOTTOM_LAYER: | ||
| 449 | prev_events[self] = None | ||
| 450 | cpu = self.get_cpu() | ||
| 451 | task_no = self.get_job().get_task().get_task_no() | ||
| 452 | graph.draw_bar_at_time(self.get_time(), self.corresp_end_event.get_time(), | ||
| 453 | task_no, cpu, self.get_job().get_job_no(), self.is_selected()) | ||
| 454 | graph.add_sel_bar_at_time(self.get_time(), self.corresp_end_event.get_time(), | ||
| 455 | task_no, cpu, self) | ||
| 456 | |||
| 457 | class ReleaseEvent(Event): | ||
| 458 | def __init__(self, time, cpu): | ||
| 459 | super(ReleaseEvent, self).__init__(time, cpu) | ||
| 460 | self.layer = Canvas.TOP_LAYER | ||
| 461 | |||
| 462 | def __str__(self): | ||
| 463 | return 'Release' + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | ||
| 464 | |||
| 465 | def scan(self, cur_cpu, switches): | ||
| 466 | super(ReleaseEvent, self).scan(cur_cpu, switches) | ||
| 467 | |||
| 468 | def render(self, graph, layer, prev_events): | ||
| 469 | prev_events[self] = None | ||
| 470 | if layer == Canvas.TOP_LAYER: | ||
| 471 | graph.draw_release_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | ||
| 472 | self.get_job().get_job_no(), self.is_selected()) | ||
| 473 | graph.add_sel_release_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | ||
| 474 | self) | ||
| 475 | |||
| 476 | class DeadlineEvent(Event): | ||
| 477 | def __init__(self, time, cpu): | ||
| 478 | super(DeadlineEvent, self).__init__(time, cpu) | ||
| 479 | self.layer = Canvas.TOP_LAYER | ||
| 480 | |||
| 481 | def __str__(self): | ||
| 482 | return 'Deadline' + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | ||
| 483 | |||
| 484 | def scan(self, cur_cpu, switches): | ||
| 485 | super(DeadlineEvent, self).scan(cur_cpu, switches) | ||
| 486 | |||
| 487 | def render(self, graph, layer, prev_events): | ||
| 488 | prev_events[self] = None | ||
| 489 | if layer == Canvas.TOP_LAYER: | ||
| 490 | graph.draw_deadline_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | ||
| 491 | self.get_job().get_job_no(), self.is_selected()) | ||
| 492 | graph.add_sel_deadline_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | ||
| 493 | self) | ||
| 494 | |||
| 495 | class InversionStartEvent(ErrorEvent): | ||
| 496 | def __init__(self, time): | ||
| 497 | super(InversionStartEvent, self).__init__(time, Event.NO_CPU) | ||
| 498 | self.layer = Canvas.BOTTOM_LAYER | ||
| 499 | |||
| 500 | def __str__(self): | ||
| 501 | if self.corresp_end_event is None: | ||
| 502 | print 'Inversion Start (w/o Inversion End)' + self._common_str() \ | ||
| 503 | + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | ||
| 504 | return 'Priority Inversion' + self._common_str() + ', START=' \ | ||
| 505 | + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ | ||
| 506 | + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) | ||
| 507 | |||
| 508 | def scan(self, cur_cpu, switches): | ||
| 509 | switches[InversionStartEvent] = self | ||
| 510 | self.corresp_end_event = None | ||
| 511 | |||
| 512 | # the corresp_end_event should already be set | ||
| 513 | super(InversionStartEvent, self).scan(cur_cpu, switches) | ||
| 514 | |||
| 515 | def render(self, graph, layer, prev_events): | ||
| 516 | if layer == Canvas.BOTTOM_LAYER: | ||
| 517 | prev_events[self] = None | ||
| 518 | cpu = self.get_cpu() | ||
| 519 | task_no = self.get_job().get_task().get_task_no() | ||
| 520 | graph.draw_mini_bar_at_time(self.get_time(), self.corresp_end_event.get_time(), | ||
| 521 | task_no, cpu, self.get_job().get_job_no(), self.is_selected()) | ||
| 522 | graph.add_sel_mini_bar_at_time(self.get_time(), self.corresp_end_event.get_time(), | ||
| 523 | task_no, cpu, self) | ||
| 524 | |||
| 525 | class InversionEndEvent(ErrorEvent): | ||
| 526 | def __init__(self, time): | ||
| 527 | super(InversionEndEvent, self).__init__(time, Event.NO_CPU) | ||
| 528 | self.layer = Canvas.BOTTOM_LAYER | ||
| 529 | |||
| 530 | def __str__(self): | ||
| 531 | if self.corresp_start_event is None: | ||
| 532 | print 'Inversion End (w/o Inversion Start)' + self._common_str() \ | ||
| 533 | + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | ||
| 534 | |||
| 535 | return str(self.corresp_start_event) | ||
| 536 | |||
| 537 | def scan(self, cur_cpu, switches): | ||
| 538 | self.corresp_start_event = switches[InversionStartEvent] | ||
| 539 | |||
| 540 | cur_cpu[0] = Event.NO_CPU | ||
| 541 | switches[InversionStartEvent] = None | ||
| 542 | |||
| 543 | if self.corresp_start_event is not None: | ||
| 544 | self.corresp_start_event.corresp_end_event = self | ||
| 545 | |||
| 546 | if self.corresp_start_event is None: | ||
| 547 | self.erroneous = True | ||
| 548 | print "inversion end was not matched by a corresponding inversion start" | ||
| 549 | |||
| 550 | super(InversionEndEvent, self).scan(cur_cpu, switches) | ||
| 551 | |||
| 552 | def render(self, graph, layer, prev_events): | ||
| 553 | if self.corresp_start_event is None or self.corresp_start_event in prev_events: | ||
| 554 | return # erroneous inversion end or already rendered | ||
| 555 | self.corresp_start_event.render(graph, layer, prev_events) | ||
| 556 | |||
| 557 | class InversionDummy(DummyEvent): | ||
| 558 | def render(self, graph, layer, prev_events): | ||
| 559 | if self.corresp_start_event in prev_events: | ||
| 560 | return # we have already been rendered | ||
| 561 | self.corresp_start_event.render(graph, layer, prev_events) | ||
| 562 | |||
| 563 | class IsRunningDummy(DummyEvent): | ||
| 564 | def render(self, graph, layer, prev_events): | ||
| 565 | if self.corresp_start_event in prev_events: | ||
| 566 | return # we have already been rendered | ||
| 567 | self.corresp_start_event.render(graph, layer, prev_events) | ||
| 568 | |||
| 569 | EVENT_LIST = {SuspendEvent : None, ResumeEvent : None, CompleteEvent : None, SwitchAwayEvent : None, | ||
| 570 | SwitchToEvent : None, ReleaseEvent : None, DeadlineEvent : None, IsRunningDummy : None, | ||
| 571 | InversionStartEvent : None, InversionEndEvent : None, InversionDummy : None} | ||
