From c712c699126e16003ee69e2d82e51007ab2f58ea Mon Sep 17 00:00:00 2001 From: Gary Bressler Date: Sat, 20 Mar 2010 12:47:31 -0400 Subject: Significant performance improvements in the graphical rendering, as well as the eradication of several bugs Also, visualizer is now integrated into the main unit-trace command line tool (use -v option) Note that files are now taken in from the command line, no longer from the GUI --- unit_trace/viz/convert.py | 4 +- unit_trace/viz/draw.py | 42 +++++--- unit_trace/viz/schedule.py | 236 ++++++++++++++++++++++++++++----------------- unit_trace/viz/viewer.py | 1 - 4 files changed, 177 insertions(+), 106 deletions(-) (limited to 'unit_trace') diff --git a/unit_trace/viz/convert.py b/unit_trace/viz/convert.py index f37dd27..8ebdbdc 100644 --- a/unit_trace/viz/convert.py +++ b/unit_trace/viz/convert.py @@ -54,8 +54,8 @@ def convert_trace_to_schedule(stream): job.add_event(DeadlineEvent(record.deadline, cpu)))), 'switch_to' : (lambda : job.add_event(SwitchToEvent(record.when, cpu))), - 'switch_away' : lambda : - job.add_event(SwitchAwayEvent(record.when, cpu)), + 'switch_away' : (lambda : + job.add_event(SwitchAwayEvent(record.when, cpu))), 'assign' : (noop), 'completion' : (lambda : job.add_event(CompleteEvent(record.when, cpu))), diff --git a/unit_trace/viz/draw.py b/unit_trace/viz/draw.py index e28ed24..8c48744 100644 --- a/unit_trace/viz/draw.py +++ b/unit_trace/viz/draw.py @@ -316,7 +316,17 @@ class Canvas(object): self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS) x += maj_sep - def draw_bar(self, x, y, width, height, n, selected): + def _draw_bar_border_common(self, x, y, width, height, color, thickness, clip_side): + if clip_side is None: + self.draw_rect(x, y, width, height, color, thickness) + elif clip_side == AlignMode.LEFT: + self.draw_polyline([(x, y), (x + width, y), (x + width, y + height), (x, y + height)], + color, thickness) + elif clip_side == AlignMode.RIGHT: + self.draw_polyline([(x + width, y), (x, y), (x, y + height), (x + width, y + height)], + color, thickness) + + def draw_bar(self, x, y, width, height, n, clip_side, selected): """Draws a bar with a certain set of dimensions, using pattern ``n'' from the bar pattern list.""" @@ -325,12 +335,13 @@ class Canvas(object): # use a pattern to be pretty self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True) - self.draw_rect(x, y, width, height, color, thickness) + + self._draw_bar_border_common(x, y, width, height, color, thickness, clip_side) def add_sel_bar(self, x, y, width, height, event): self.add_sel_region(SelectableRegion(x, y, width, height, event)) - def draw_mini_bar(self, x, y, width, height, n, selected): + def draw_mini_bar(self, x, y, width, height, n, clip_side, selected): """Like the above, except it draws a miniature version. This is usually used for secondary purposes (i.e. to show jobs that _should_ have been running at a certain time). @@ -342,7 +353,8 @@ class Canvas(object): True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 1.5)}[selected] self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True) - self.draw_rect(x, y, width, height, color, thickness) + + self._draw_bar_border_common(x, y, width, height, color, thickness, clip_side) def add_sel_mini_bar(self, x, y, width, height, event): self.add_sel_region(SelectableRegion(x, y, width, height, event)) @@ -970,7 +982,7 @@ class Graph(object): a certain time.""" raise NotImplementedError - def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None): + def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None): """Draws a bar over a certain time period for some task, optionally labelling it.""" raise NotImplementedError @@ -979,7 +991,7 @@ class Graph(object): a certain time.""" raise NotImplementedError - def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None): + def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, clip_side=None, job_no=None): """Draws a mini bar over a certain time period for some task, optionally labelling it.""" raise NotImplementedError @@ -1099,7 +1111,7 @@ class TaskGraph(Graph): self.canvas.add_sel_deadline_arrow_big(x, y, height, event) - def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, selected=False): + def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): if start_time > end_time: raise ValueError("Litmus is not a time machine") @@ -1108,7 +1120,7 @@ class TaskGraph(Graph): width = self._get_bar_width(start_time, end_time) height = self._get_bar_height() - self.canvas.draw_bar(x, y, width, height, cpu_no, selected) + self.canvas.draw_bar(x, y, width, height, cpu_no, clip_side, selected) # if a job number is specified, we want to draw a superscript and subscript for the task and job number, respectively if job_no is not None: @@ -1128,7 +1140,7 @@ class TaskGraph(Graph): self.canvas.add_sel_bar(x, y, width, height, event) - def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, selected=False): + def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): if start_time > end_time: raise ValueError("Litmus is not a time machine") @@ -1137,7 +1149,7 @@ class TaskGraph(Graph): width = self._get_bar_width(start_time, end_time) height = self._get_mini_bar_height() - self.canvas.draw_mini_bar(x, y, width, height, Canvas.NULL_PATTERN, selected) + self.canvas.draw_mini_bar(x, y, width, height, Canvas.NULL_PATTERN, clip_side, selected) if job_no is not None: x += GraphFormat.MINI_BAR_LABEL_OFS @@ -1245,7 +1257,7 @@ class CpuGraph(Graph): x = self._get_time_xpos(time) y = self._get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0 - self.canvas.add_sel_suspend_triangle(x, y, height, event) + self.canvas.add_sel_resume_triangle(x, y, height, event) def draw_completion_marker_at_time(self, time, task_no, cpu_no, selected=False): height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR @@ -1312,7 +1324,7 @@ class CpuGraph(Graph): self.canvas.add_sel_deadline_arrow_small(x, y, height, event) - def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, selected=False): + def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): if start_time > end_time: raise ValueError("Litmus is not a time machine") @@ -1321,7 +1333,7 @@ class CpuGraph(Graph): width = self._get_bar_width(start_time, end_time) height = self._get_bar_height() - self.canvas.draw_bar(x, y, width, height, task_no, selected) + self.canvas.draw_bar(x, y, width, height, task_no, clip_side, selected) # if a job number is specified, we want to draw a superscript and subscript for the task and job number, respectively if job_no is not None: @@ -1339,7 +1351,7 @@ class CpuGraph(Graph): self.canvas.add_sel_bar(x, y, width, height, event) - def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, selected=False): + def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): if start_time > end_time: raise ValueError("Litmus is not a time machine") @@ -1348,7 +1360,7 @@ class CpuGraph(Graph): width = self._get_bar_width(start_time, end_time) height = self._get_mini_bar_height() - self.canvas.draw_mini_bar(x, y, width, height, task_no, selected) + self.canvas.draw_mini_bar(x, y, width, height, task_no, clip_side, selected) if job_no is not None: x += GraphFormat.MINI_BAR_LABEL_OFS diff --git a/unit_trace/viz/schedule.py b/unit_trace/viz/schedule.py index 4277f1a..5efee7d 100644 --- a/unit_trace/viz/schedule.py +++ b/unit_trace/viz/schedule.py @@ -11,6 +11,7 @@ import util import copy EVENT_LIST = None +SPAN_EVENTS = None class TimeSlotArray(object): """Represents another way of organizing the events. This structure organizes events by @@ -55,20 +56,47 @@ class TimeSlotArray(object): self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, event.__class__, time_slot, event) self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, event.__class__, time_slot, event) + + if event.__class__ in SPAN_END_EVENTS: + self.fill_span_event_from_end(event) + + def fill_span_event_from_end(self, event): + start_slot = None + if event.corresp_start_event is None: + start_slot = self.get_time_slot(event.get_job().get_task().get_schedule().start) - 1 + else: + start_slot = self.get_time_slot(event.corresp_start_event.get_time()) + end_slot = self.get_time_slot(event.get_time()) + + for slot in range(start_slot + 1, end_slot): + task_no = event.get_job().get_task().get_task_no() + cpu = event.get_cpu() - span_events = { SwitchAwayEvent : IsRunningDummy, InversionEndEvent : InversionDummy} - - for span_event in span_events: - if isinstance(event, span_event) and event.corresp_start_event is not None: - start_slot = self.get_time_slot(event.corresp_start_event.get_time()) - end_slot = time_slot - for slot in range(start_slot + 1, end_slot): - dummy = span_events[span_event](task_no, cpu) - dummy.corresp_start_event = event.corresp_start_event - dummy.corresp_end_event = event + dummy = SPAN_END_EVENTS[event.__class__](task_no, cpu) + dummy.corresp_start_event = event.corresp_start_event + dummy.corresp_end_event = event - self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, dummy.__class__, slot, dummy) - self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, dummy.__class__, slot, dummy) + self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, dummy.__class__, slot, dummy) + self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, dummy.__class__, slot, dummy) + + def fill_span_event_from_start(self, event): + end_slot = None + if event.corresp_end_event is None: + end_slot = self.get_time_slot(event.get_job().get_task().get_schedule().end) + 1 + else: + end_slot = self.get_time_slot(event.corresp_end_event.get_time()) + start_slot = self.get_time_slot(event.get_time()) + + for slot in range(start_slot + 1, end_slot): + task_no = event.get_job().get_task().get_task_no() + cpu = event.get_cpu() + + dummy = SPAN_START_EVENTS[event.__class__](task_no, cpu) + dummy.corresp_start_event = event + dummy.corresp_end_event = event.corresp_end_event + + self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, dummy.__class__, slot, dummy) + self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, dummy.__class__, slot, dummy) def iter_over_period(self, start, end, start_no, end_no, list_type, event_types): if self.array is None: @@ -142,18 +170,28 @@ class Schedule(object): self.set_time_params(time_per_maj) # we scan the graph task by task, and job by job - switches = {} - for event in EVENT_LIST: - switches[event] = None for task_no, task in enumerate(self.get_task_list()): + switches = {} + for event in EVENT_LIST: + switches[event] = None cur_cpu = [Event.NO_CPU] for job_no in sorted(task.get_jobs().keys()): job = task.get_jobs()[job_no] for event_time in sorted(job.get_events().keys()): # could have multiple events at the same time (unlikely but possible) for event in job.get_events()[event_time]: - #print 'task, job, event:', task.name, job.job_no, event.__class__.__name__ + print 'task, job, event:', task.name, job.job_no, event.__class__.__name__ event.scan(cur_cpu, switches) + + # What if one of the initial "span events" (switch to or inversion starting) never got a + # corresponding end event? Well, then we assume that the end event was simply outside of + # the range of whatever we read in. So we need to fill dummies starting from the initial + # event all the way to the end of the graph, so that the renderer can see the event no matter + # how far the user scrolls to the right. + for span_event in SPAN_START_EVENTS: + event = switches[span_event] + if event is not None: + self.time_slot_array.fill_span_event_from_start(event) def add_task(self, task): if task.name in self.tasks: @@ -311,7 +349,7 @@ class Event(DummyEvent): sched.start = time if sched.end is None or time > sched.end: sched.end = time - + sched.get_time_slot_array().add_event_to_time_slot(self) class ErrorEvent(Event): @@ -387,11 +425,59 @@ class CompleteEvent(Event): else: graph.draw_completion_marker_at_time(self.get_time(), self.get_job().get_task().get_task_no(), self.get_cpu(), self.is_selected()) + +class SwitchToEvent(Event): + def __init__(self, time, cpu): + super(SwitchToEvent, self).__init__(time, cpu) + self.layer = Canvas.BOTTOM_LAYER + self.corresp_end_event = None + + def __str__(self): + if self.corresp_end_event is None: + return 'Switch To (w/o Switch Away)' + self._common_str() + ', TIME=' \ + + str(self.get_time()) + return 'Scheduled' + self._common_str() + ', START=' \ + + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ + + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) + + def scan(self, cur_cpu, switches): + old_cur_cpu = cur_cpu[0] + cur_cpu[0] = self.get_cpu() + switches[SwitchToEvent] = self + self.corresp_end_event = None + if old_cur_cpu != Event.NO_CPU: + self.erroneous = True + print "currently scheduled somewhere, can't switch to a CPU" + + super(SwitchToEvent, self).scan(cur_cpu, switches) + + def render(self, graph, layer, prev_events, selectable=False): + if layer == self.layer: + end_time = None + clip = None + if self.corresp_end_event is None: + end_time = self.get_job().get_task().get_schedule().end + clip = AlignMode.RIGHT + else: + end_time = self.corresp_end_event.get_time() + + prev_events[self] = None + cpu = self.get_cpu() + task_no = self.get_job().get_task().get_task_no() + if selectable: + graph.add_sel_bar_at_time(self.get_time(), end_time, + task_no, cpu, self) + else: + graph.draw_bar_at_time(self.get_time(), end_time, + task_no, cpu, self.get_job().get_job_no(), + clip, self.is_selected()) + class SwitchAwayEvent(Event): def __init__(self, time, cpu): super(SwitchAwayEvent, self).__init__(time, cpu) self.layer = Canvas.BOTTOM_LAYER + self.corresp_start_event = None def __str__(self): if self.corresp_start_event is None: @@ -431,63 +517,19 @@ class SwitchAwayEvent(Event): prev_events[self] = None cpu = self.get_cpu() task_no = self.get_job().get_task().get_task_no() + start = self.get_job().get_task().get_schedule().start if selectable: - start = self.get_job().get_task().get_schedule().start graph.add_sel_bar_at_time(start, self.get_time(), task_no, cpu, self) else: graph.draw_bar_at_time(start, self.get_time(), - task_no, cpu, self.get_job().get_job_no(), self.is_selected()) + task_no, cpu, self.get_job().get_job_no(), + AlignMode.LEFT, self.is_selected()) else: if self.corresp_start_event in prev_events: return # already rendered the bar self.corresp_start_event.render(graph, layer, prev_events, selectable) -class SwitchToEvent(Event): - def __init__(self, time, cpu): - super(SwitchToEvent, self).__init__(time, cpu) - self.layer = Canvas.BOTTOM_LAYER - - def __str__(self): - if self.corresp_end_event is None: - return 'Switch To (w/o Switch Away)' + self._common_str() + ', TIME=' \ - + str(self.get_time()) - return 'Scheduled' + self._common_str() + ', START=' \ - + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ - + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) - - def scan(self, cur_cpu, switches): - old_cur_cpu = cur_cpu[0] - cur_cpu[0] = self.get_cpu() - switches[SwitchToEvent] = self - self.corresp_end_event = None - - if old_cur_cpu != Event.NO_CPU: - self.erroneous = True - print "currently scheduled somewhere, can't switch to a CPU" - - super(SwitchToEvent, self).scan(cur_cpu, switches) - - def render(self, graph, layer, prev_events, selectable=False): - if self.corresp_end_event is None: - return # fatally erroneous switch to - if layer == self.layer: - end_time = None - if self.corresp_end_event is None: - end_time = self.get_job().get_task().get_schedule().end - else: - end_time = self.corresp_end_event.get_time() - - prev_events[self] = None - cpu = self.get_cpu() - task_no = self.get_job().get_task().get_task_no() - if selectable: - graph.add_sel_bar_at_time(self.get_time(), end_time, - task_no, cpu, self) - else: - graph.draw_bar_at_time(self.get_time(), end_time, - task_no, cpu, self.get_job().get_job_no(), self.is_selected()) - class ReleaseEvent(Event): def __init__(self, time, cpu): super(ReleaseEvent, self).__init__(time, cpu) @@ -536,6 +578,7 @@ class InversionStartEvent(ErrorEvent): def __init__(self, time): super(InversionStartEvent, self).__init__(time, Event.NO_CPU) self.layer = Canvas.BOTTOM_LAYER + self.corresp_end_event = None def __str__(self): if self.corresp_end_event is None: @@ -553,28 +596,33 @@ class InversionStartEvent(ErrorEvent): super(InversionStartEvent, self).scan(cur_cpu, switches) def render(self, graph, layer, prev_events, selectable=False): - end_time = None - if self.corresp_end_event is None: - end_time = self.get_job().get_task().get_schedule().end - else: - end_time = self.corresp_end_event.get_time() - if layer == self.layer: - prev_events[self] = None - cpu = self.get_cpu() - task_no = self.get_job().get_task().get_task_no() - if selectable: - graph.add_sel_mini_bar_at_time(self.get_time(), end_time, - task_no, cpu, self) + end_time = None + clip = None + if self.corresp_end_event is None: + end_time = self.get_job().get_task().get_schedule().end + clip = AlignMode.RIGHT else: - graph.draw_mini_bar_at_time(self.get_time(), end_time, - task_no, cpu, self.get_job().get_job_no(), self.is_selected()) + end_time = self.corresp_end_event.get_time() + + if layer == self.layer: + prev_events[self] = None + cpu = self.get_cpu() + task_no = self.get_job().get_task().get_task_no() + if selectable: + graph.add_sel_mini_bar_at_time(self.get_time(), end_time, + task_no, cpu, self) + else: + graph.draw_mini_bar_at_time(self.get_time(), end_time, + task_no, cpu, self.get_job().get_job_no(), + clip, self.is_selected()) class InversionEndEvent(ErrorEvent): def __init__(self, time): super(InversionEndEvent, self).__init__(time, Event.NO_CPU) self.layer = Canvas.BOTTOM_LAYER + self.corresp_start_event = None def __str__(self): if self.corresp_start_event is None: @@ -607,13 +655,14 @@ class InversionEndEvent(ErrorEvent): prev_events[self] = None cpu = self.get_cpu() task_no = self.get_job().get_task().get_task_no() + start = self.get_job().get_task().get_schedule().start if selectable: - start = self.get_job().get_task().get_schedule().start graph.add_sel_mini_bar_at_time(start, self.get_time(), task_no, cpu, self) else: graph.draw_mini_bar_at_time(start, self.get_time(), - task_no, cpu, self.get_job().get_job_no(), self.is_selected()) + task_no, cpu, self.get_job().get_job_no(), + AlignMode.LEFT, self.is_selected()) else: if self.corresp_start_event in prev_events: return # already rendered the bar @@ -625,11 +674,14 @@ class InversionDummy(DummyEvent): self.layer = Canvas.BOTTOM_LAYER def render(self, graph, layer, prev_events, selectable=False): - if self.corresp_start_event is not None and self.corresp_start_event in prev_events: - return # we have already been rendered - if self.corresp_end_event is not None and self.corresp_end_event in prev_events: - return - self.corresp_start_event.render(graph, layer, prev_events, selectable) + if self.corresp_start_event is None: + if self.corresp_end_event in prev_events: + return # we have already been rendered + self.corresp_end_event.render(graph, layer, prev_events, selectable) + else: + if self.corresp_start_event in prev_events: + return # we have already been rendered + self.corresp_start_event.render(graph, layer, prev_events, selectable) class IsRunningDummy(DummyEvent): def __init__(self, time, cpu): @@ -637,12 +689,20 @@ class IsRunningDummy(DummyEvent): self.layer = Canvas.BOTTOM_LAYER def render(self, graph, layer, prev_events, selectable=False): - if self.corresp_start_event is None or self.corresp_start_event in prev_events: - return # we have already been rendered - self.corresp_start_event.render(graph, layer, prev_events, selectable) + if self.corresp_start_event is None: + if self.corresp_end_event in prev_events: + return # we have already been rendered + self.corresp_end_event.render(graph, layer, prev_events, selectable) + else: + if self.corresp_start_event in prev_events: + return # we have already been rendered + self.corresp_start_event.render(graph, layer, prev_events, selectable) EVENT_LIST = {SuspendEvent : None, ResumeEvent : None, CompleteEvent : None, SwitchAwayEvent : None, SwitchToEvent : None, ReleaseEvent : None, DeadlineEvent : None, IsRunningDummy : None, InversionStartEvent : None, InversionEndEvent : None, InversionDummy : None} + +SPAN_START_EVENTS = { SwitchToEvent : IsRunningDummy, InversionStartEvent : InversionDummy } +SPAN_END_EVENTS = { SwitchAwayEvent : IsRunningDummy, InversionEndEvent : InversionDummy} diff --git a/unit_trace/viz/viewer.py b/unit_trace/viz/viewer.py index 236a467..909da76 100644 --- a/unit_trace/viz/viewer.py +++ b/unit_trace/viz/viewer.py @@ -68,7 +68,6 @@ class GraphArea(gtk.DrawingArea): # window manager must have caused the expose event. So just update the # expose_event's bounding area. if not self.dirtied_regions or expose_event.send_event: - print 'forced expose' self.dirtied_regions = [(expose_event.area.x, expose_event.area.y, expose_event.area.width, expose_event.area.height)] -- cgit v1.2.2