diff options
author | Mac Mollison <mollison@cs.unc.edu> | 2010-03-13 12:12:37 -0500 |
---|---|---|
committer | Mac Mollison <mollison@cs.unc.edu> | 2010-03-13 12:12:37 -0500 |
commit | 122f457226f54ad23b7cd138512502e430e704dc (patch) | |
tree | fee0690936c3ae95255e559cd0fd09f0fa8c2ad4 /unit_trace/viz/schedule.py | |
parent | 14a40b99735f09f6e70b8e897acbb622f9115ca3 (diff) |
Further restructuring to create 'unit_trace' pkg
The unit_trace folder should be placed in
/usr/local/lib/pythonX.Y/site-packages.
This makes unit-trace submodules available from anywhere
on the system.
Diffstat (limited to 'unit_trace/viz/schedule.py')
-rw-r--r-- | unit_trace/viz/schedule.py | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/unit_trace/viz/schedule.py b/unit_trace/viz/schedule.py new file mode 100644 index 0000000..f842c8d --- /dev/null +++ b/unit_trace/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} | ||