From d2240e7a9b15d18dfd0f3cbe75f3adf1c9e7fabf Mon Sep 17 00:00:00 2001 From: Mac Mollison Date: Mon, 31 Jan 2011 16:40:54 -0500 Subject: Added support for visualizing arbitrary actions. (By Jonathan, cherry-picked by Mac) Conflicts: unit_trace/trace_reader.py unit_trace/viz/canvas.py unit_trace/viz/graph.py unit_trace/viz/schedule.py --- unit_trace/sanitizer.py | 10 +++ unit_trace/trace_reader.py | 11 ++- unit_trace/viz/canvas.py | 80 +++++++++++++++++++-- unit_trace/viz/convert.py | 31 +++++--- unit_trace/viz/graph.py | 94 +++++++++++++++++++++++-- unit_trace/viz/schedule.py | 171 +++++++++++++++++++++++++++++++++++++++------ 6 files changed, 349 insertions(+), 48 deletions(-) diff --git a/unit_trace/sanitizer.py b/unit_trace/sanitizer.py index 598379a..fc66170 100644 --- a/unit_trace/sanitizer.py +++ b/unit_trace/sanitizer.py @@ -14,6 +14,8 @@ def sanitizer(stream): job_2s_released = [] # list of tasks which have released their job 2s jobs_switched_to = [] + released = False + for record in stream: # Ignore records which are not events (e.g. the num_cpus record) @@ -21,6 +23,13 @@ def sanitizer(stream): yield record continue + if record.type_name == 'release': + released = released or True + + if record.type_name == 'action' and released: + yield record + continue + # All records with job < 2 are garbage if record.job < 2: continue @@ -50,4 +59,5 @@ def sanitizer(stream): if (record.pid,record.job) not in jobs_switched_to: record.job -= 1 + yield record diff --git a/unit_trace/trace_reader.py b/unit_trace/trace_reader.py index db36c13..c895cf2 100644 --- a/unit_trace/trace_reader.py +++ b/unit_trace/trace_reader.py @@ -91,7 +91,6 @@ def trace_reader(files, buffsize): # Keep pulling records as long as we have a buffer while len(file_iter_buff) > 0: - # Select the earliest record from those at the heads of the buffers earliest = -1 buff_to_refill = -1 @@ -205,6 +204,12 @@ class StHeader: keys = ['type','cpu','pid','job'] message = 'The header.' +class StActionData: + format = 'Qb' + formatStr = struct.Struct(StHeader.format + format) + keys = StHeader.keys + ['when','action'] + message = 'An action was performed.' + class StNameData: format = '16s' formatStr = struct.Struct(StHeader.format + format) @@ -271,7 +276,7 @@ class StSysReleaseData: def _get_type(type_num): types = [None,StNameData,StParamData,StReleaseData,StAssignedData, StSwitchToData,StSwitchAwayData,StCompletionData,StBlockData, - StResumeData,StSysReleaseData] + StResumeData,StActionData,StSysReleaseData] if type_num > len(types)-1 or type_num < 1: raise Exception return types[type_num] @@ -280,5 +285,5 @@ def _get_type(type_num): # programmers of other modules) def _get_type_name(type_num): type_names = [None,"name","params","release","assign","switch_to", - "switch_away","completion","block","resume","sys_release"] + "switch_away","completion","block","resume","action","sys_release"] return type_names[type_num] diff --git a/unit_trace/viz/canvas.py b/unit_trace/viz/canvas.py index ea73d98..c39c613 100644 --- a/unit_trace/viz/canvas.py +++ b/unit_trace/viz/canvas.py @@ -443,14 +443,44 @@ class Canvas(object): self.add_sel_region(SelectableRegion(x - big_arrowhead_height / Canvas.SQRT3, y, 2.0 * big_arrowhead_height / Canvas.SQRT3, height, event)) + def draw_action_symbol(self, item, action, x, y, height, selected): + """Draws a release arrow: x, y should give the top (northernmost + point) of the arrow. The height includes the arrowhead.""" + + color = {False : GraphFormat.BORDER_COLOR, + True : GraphFormat.HIGHLIGHT_COLOR}[selected] + + colors = [self.get_bar_pattern(item).get_color_list()[0], color] + draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] + + for i in range(0, 2): + color = colors[i] + draw_func = draw_funcs[i] + + draw_func(self, [(x, y), (x - height / Canvas.SQRT3, \ + y - height), \ + (x + height / Canvas.SQRT3, \ + y - height), \ + (x, y)], color, GraphFormat.BORDER_THICKNESS) + + self.draw_label(str(action), x, y - height / 2 - .1 * height, + GraphFormat.DEF_FOPTS_LABEL, + AlignMode.CENTER, AlignMode.CENTER, True) + + def add_sel_action_symbol(self, x, y, height, event): + self.add_sel_region(SelectableRegion(x - height / Canvas.SQRT3, + y - height, 2.0 * height / Canvas.SQRT3, height, event)) + def draw_release_arrow_small(self, x, y, height, selected): """Draws a small release arrow (most likely coming off the x-axis, although this method doesn't enforce this): x, y should give the top of the arrow""" small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height - color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] + color = {False : GraphFormat.BORDER_COLOR, + True : GraphFormat.HIGHLIGHT_COLOR}[selected] - self.draw_line((x, y), (x - small_arrowhead_height, y + small_arrowhead_height), \ + self.draw_line((x, y), + (x - small_arrowhead_height, y + small_arrowhead_height), color, GraphFormat.BORDER_THICKNESS) self.draw_line((x, y), (x + small_arrowhead_height, y + small_arrowhead_height), \ color, GraphFormat.BORDER_THICKNESS) @@ -684,14 +714,35 @@ class CairoCanvas(Canvas): self.surface.ctx.set_line_width(thickness * self.scale) self.surface.ctx.stroke() + def draw_circle(self, x, y, radius, fill_color, border_color, + thickness, do_snap=True): + p = self.surface.get_real_coor(x, y) + if do_snap: + p = (snap(p[0]), snap(p[1])) + + self.surface.ctx.save() + self.surface.ctx.arc(p[0], p[1], radius, 0.0, 2 * math.pi) + self.surface.ctx.set_source_rgb(border_color[0], + border_color[1], + border_color[2]) + self.surface.ctx.set_line_width(thickness * self.scale) + self.surface.ctx.stroke() + self.surface.ctx.arc(p[0], p[1], radius, 0.0, 2 * math.pi) + self.surface.ctx.set_source_rgb(fill_color[0], + fill_color[1], + fill_color[2]) + self.surface.ctx.fill() + self.surface.ctx.restore() + def _polyline_common(self, coor_list, color, thickness, do_snap=True): - scaled_coor_list = [self.scaled(coor[0], coor[1]) for coor in coor_list] - real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in scaled_coor_list] + real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) \ + for coor in coor_list] self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1]) if do_snap: for i in range(0, len(real_coor_list)): - real_coor_list[i] = (snap(real_coor_list[i][0]), snap(real_coor_list[i][1])) + real_coor_list[i] = (snap(real_coor_list[i][0]), + snap(real_coor_list[i][1])) for coor in real_coor_list[1:]: self.surface.ctx.line_to(coor[0], coor[1]) @@ -753,8 +804,23 @@ class CairoCanvas(Canvas): raise ValueError('Invalid alignment value') f_descent_factor, f_height_factor = valign_factors[valign] - self._draw_label_common(text, x, y, fopts, x_bearing_factor, \ - f_descent_factor, width_factor, f_height_factor, do_snap) + return self._get_label_dim_common(text, x, y, fopts, x_bearing_factor, + f_descent_factor, width_factor, f_height_factor, + do_snap) + + def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, + halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): + """Draws a label with the given parameters, with the given horizontal and vertical justification.""" + + actual_x, actual_y, width, height, f_height = self.get_label_dim(text, x, y, fopts, halign, valign, do_snap) + actual_x, actual_y = self.surface.get_real_coor(actual_x, actual_y) + + self.surface.ctx.select_font_face(fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) + self.surface.ctx.set_font_size(fopts.size * self.scale) + self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2]) + self.surface.ctx.move_to(actual_x, actual_y) + + self.surface.ctx.show_text(text) def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ textfopts=GraphFormat.DEF_FOPTS_LABEL, sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ diff --git a/unit_trace/viz/convert.py b/unit_trace/viz/convert.py index d19bc73..278a541 100644 --- a/unit_trace/viz/convert.py +++ b/unit_trace/viz/convert.py @@ -16,14 +16,17 @@ def get_type_num(type): return nums[type] def _get_job_from_record(sched, record): - tname = _pid_to_task_name(record.pid) - job_no = record.job - if tname not in sched.get_tasks(): - sched.add_task(Task(tname, [])) - if job_no not in sched.get_tasks()[tname].get_jobs(): - sched.get_tasks()[tname].add_job(Job(job_no, [])) - job = sched.get_tasks()[tname].get_jobs()[job_no] - return job + if record.pid == 0: + return None + else: + tname = _pid_to_task_name(record.pid) + job_no = record.job + if tname not in sched.get_tasks(): + sched.add_task(Task(tname, [])) + if job_no not in sched.get_tasks()[tname].get_jobs(): + sched.get_tasks()[tname].add_job(Job(job_no, [])) + job = sched.get_tasks()[tname].get_jobs()[job_no] + return job def convert_trace_to_schedule(stream): """The main function of interest in this module. Coverts a stream of records @@ -45,12 +48,20 @@ def convert_trace_to_schedule(stream): if not hasattr(record, 'deadline'): record.deadline = None + # This whole method should be refactored for this posibility + if job is None: + if record.type_name == "action": + event = ActionEvent(record.when, cpu, record.action) + event.set_schedule(sched) + sched.add_jobless(event) + continue + actions = { 'name' : (noop), 'params' : (noop), 'release' : (lambda : (job.add_event(ReleaseEvent(record.when, cpu)), - job.add_event(DeadlineEvent(record.deadline, cpu)))), + job.add_event(DeadlineEvent(record.deadline, cpu)))), 'switch_to' : (lambda : job.add_event(SwitchToEvent(record.when, cpu))), 'switch_away' : (lambda : @@ -62,6 +73,8 @@ def convert_trace_to_schedule(stream): job.add_event(SuspendEvent(record.when, cpu))), 'resume' : (lambda : job.add_event(ResumeEvent(record.when, cpu))), + 'action' : (lambda : + job.add_event(ActionEvent(record.when, cpu, record.action))), 'sys_release' : (noop) } diff --git a/unit_trace/viz/graph.py b/unit_trace/viz/graph.py index a10d9bd..f2021ce 100644 --- a/unit_trace/viz/graph.py +++ b/unit_trace/viz/graph.py @@ -8,11 +8,12 @@ than the canvas classes (time and task/cpu number rather than plain coordinates) update themselves, unlike the Canvas which can only overwrite itself.""" class Graph(object): - DEF_BAR_PLIST = [Pattern([(0.0, 0.9, 0.9)]), Pattern([(0.9, 0.3, 0.0)]), Pattern([(0.9, 0.7, 0.0)]), - Pattern([(0.0, 0.0, 0.8)]), Pattern([(0.0, 0.2, 0.9)]), Pattern([(0.0, 0.6, 0.6)]), + DEF_BAR_PLIST = [Pattern([(0.0, 0.9, 0.9)]), Pattern([(0.9, 0.3, 0.0)]), + Pattern([(0.9, 0.7, 0.0)]), Pattern([(0.0, 0.0, 0.8)]), + Pattern([(0.0, 0.2, 0.9)]), Pattern([(0.0, 0.6, 0.6)]), Pattern([(0.75, 0.75, 0.75)])] - DEF_ITEM_CLIST = [(0.3, 0.0, 0.0), (0.0, 0.3, 0.0), (0.0, 0.0, 0.3), (0.3, 0.3, 0.0), (0.0, 0.3, 0.3), - (0.3, 0.0, 0.3)] + DEF_ITEM_CLIST = [(0.3, 0.0, 0.0), (0.0, 0.3, 0.0), (0.0, 0.0, 0.3), + (0.3, 0.3, 0.0), (0.0, 0.3, 0.3), (0.3, 0.0, 0.3)] def __init__(self, CanvasType, surface, start_time, end_time, y_item_list, attrs=GraphFormat(), item_clist=DEF_ITEM_CLIST, bar_plist=DEF_BAR_PLIST): @@ -106,9 +107,12 @@ class Graph(object): """get x so that x is at instant ``time'' on the graph""" return self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + 1.0 * (time - self.start_time) / self.attrs.time_per_maj * self.attrs.maj_sep + def get_item_yorigin(self, item_no): + return self.origin[1] - self._get_y_axis_height() + self.attrs.y_item_size * item_no; + def get_item_ypos(self, item_no): """get y so that y is where the top of a bar would be in item #n's area""" - return self.origin[1] - self._get_y_axis_height() + self.attrs.y_item_size * (item_no + 0.5 - GraphFormat.BAR_SIZE_FACTOR / 2.0) + return self.get_item_yorigin(item_no) + self.attrs.y_item_size * (0.5 - GraphFormat.BAR_SIZE_FACTOR / 2.0) def _get_bar_width(self, start_time, end_time): return 1.0 * (end_time - start_time) / self.attrs.time_per_maj * self.attrs.maj_sep @@ -167,7 +171,29 @@ class Graph(object): raise NotImplementedError def get_events_to_render(self, sched, regions, selectable=False): - raise NotImplementedError + slots = {} + + self.min_time, self.max_time, self.min_item, self.max_item = None, None, None, None + for region in regions: + x, y, width, height = region + start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height) + self._recomp_min_max(start_time, end_time, start_item, end_item) + + sched.get_time_slot_array().get_slots(slots, + start_time, end_time, start_item, end_item, + self.list_type) + + events_to_render = {} + for layer in Canvas.LAYERS: + events_to_render[layer] = {} + + for event in sched.get_time_slot_array().get_events(slots, + self.list_type, schedule.EVENT_LIST): + events_to_render[event.get_layer()][event] = None + for event in sched.get_jobless(): + events_to_render[event.get_layer()][event] = None + + return events_to_render def render_surface(self, sched, regions, selectable=False): if not selectable: @@ -269,6 +295,16 @@ class Graph(object): a certain time.""" raise NotImplementedError + def draw_action_symbol_at_time(self, time, task_no, cpu_no, action, + job_no, selected=False): + """Draws an action symbol at a certain time for some task and job""" + raise NotImplementedError + + def add_sel_action_symbol_at_time(self, time, task_no, cpu_no, event): + """Same as above, except instead of drawing adds a selectable region at + a certain time.""" + raise NotImplementedError + 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 @@ -391,6 +427,33 @@ class TaskGraph(Graph): self.canvas.add_sel_deadline_arrow_big(x, y, height, event) + def draw_action_symbol_at_time(self, time, task_no, cpu_no, action, + job_no=None, selected=False): + x = self.get_time_xpos(time) + y = None + + if task_no != -1: + y = self.get_item_ypos(task_no) + else: + y = self.origin[1] + + height = 1.5 * (self.get_item_ypos(0) - self.get_item_yorigin(0)) + + self.canvas.draw_action_symbol(cpu_no, action, x, y, height, selected) + + def add_sel_action_symbol_at_time(self, time, task_no, cpu_no, event): + x = self.get_time_xpos(time) + y = None + + if task_no != -1: + y = self.get_item_ypos(task_no) + else: + y = self.origin[1] + + height = 1.5 * (self.get_item_ypos(0) - self.get_item_yorigin(0)) + + self.canvas.add_sel_action_symbol(x, y, height, event) + 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") @@ -599,10 +662,27 @@ class CpuGraph(Graph): height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR x = self.get_time_xpos(time) - y = self.origin[1] - height + y = self.get_item_ypos(task_no) self.canvas.add_sel_deadline_arrow_small(x, y, height, event) + def draw_action_symbol_at_time(self, time, task_no, cpu_no, action, + job_no=None, selected=False): + x = self.get_time_xpos(time) + y = self.get_item_ypos(cpu_no) + + height = 1.5 * (y - self.get_item_yorigin(cpu_no)) + + self.canvas.draw_action_symbol(task_no, action, x, y, height, selected) + + def add_sel_action_symbol_at_time(self, time, task_no, cpu_no, event): + x = self.get_time_xpos(time) + y = self.get_item_ypos(cpu_no) + + height = 1.5 * (y - self.get_item_yorigin(cpu_no)) + + self.canvas.add_sel_action_symbol(x, y, height, event) + 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") diff --git a/unit_trace/viz/schedule.py b/unit_trace/viz/schedule.py index a44ce8d..2301b0c 100644 --- a/unit_trace/viz/schedule.py +++ b/unit_trace/viz/schedule.py @@ -138,6 +138,7 @@ class Schedule(object): self.time_slot_array = None self.cur_task_no = 0 self.num_cpus = num_cpus + self.jobless = [] for task in task_list: self.add_task(task) @@ -217,6 +218,23 @@ class Schedule(object): task.task_no = self.cur_task_no self.cur_task_no += 1 + def add_jobless(self, event): + self.jobless.append(event) + + def sort_task_nos_numeric(self): + # sort task numbers by the numeric value of the task names. + nums = [] + + for task_name in self.tasks: + nums.append((int(task_name), task_name)) + + nums.sort(key=lambda t: t[0]) + for no, task in enumerate(nums): + self.tasks[task[1]].task_no = no + + def get_jobless(self): + return self.jobless + def get_tasks(self): return self.tasks @@ -295,12 +313,12 @@ class DummyEvent(object): event added by the application to speed things up or keep track of something. Such an event won't be added to the schedule tree, but might appear in the time slot array.""" - def __init__(self, time, cpu): self.time = time self.cpu = cpu self.job = None self.layer = None + self.saved_schedule = None def __str__(self): return '[Dummy Event]' @@ -311,6 +329,25 @@ class DummyEvent(object): def get_cpu(self): return self.cpu + # Refactor, shouldn't depend on job + def get_schedule(self): + if self.saved_schedule is not None: + return self.saved_schedule + elif self.get_task() is not None: + return self.get_task().get_schedule() + else: + return None + + # Needed for events not assigned to specific tasks + def set_schedule(self, schedule): + self.saved_schedule = schedule + + def get_task(self): + if self.get_job() is not None: + return self.get_job().get_task() + else: + return None + def get_job(self): return self.job @@ -342,20 +379,34 @@ class Event(DummyEvent): def __str__(self): return self.get_name() + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) - def str_long(self): - """Prints the event as a string, in ``long'' form.""" - return 'Event Type: ' + self.get_name() + \ - '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ - '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ - self.get_job().get_job_no())) + \ - '\nCPU: ' + str(self.get_cpu()) + \ - '\nTime: ' + str(self.get_time()) + def str_long(self, unit): + if self.get_job() is not None: + """Prints the event as a string, in ``long'' form.""" + return 'Event Information\n-----------------\n' + \ + 'Event Type: ' + self.get_name() + \ + '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ + '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ + self.get_job().get_job_no())) + \ + '\nCPU: ' + str(self.get_cpu()) + \ + '\nTime: ' + _format_time(self.get_time(), unit) + \ + '\n\n' + self.get_job().str_long(unit) + else: + """Prints the event as a string, in ``long'' form.""" + return 'Event Information\n-----------------\n' + \ + 'Event Type: ' + self.get_name() + \ + '\nTask Name: None' + \ + '\nCPU: ' + str(self.get_cpu()) + \ + '\nTime: ' + _format_time(self.get_time(), unit) def _common_str(self): - job = self.get_job() - task = job.get_task() - return ' for task ' + str(task.get_name()) + ': (TASK, JOB)=' + str((task.get_task_no(), \ - job.get_job_no())) + ', CPU=' + str(self.get_cpu()) + if self.get_job() is not None: + job = self.get_job() + task = job.get_task() + return ' for task ' + str(task.get_name()) + ': (TASK, JOB)=' + \ + str((task.get_task_no(), job.get_job_no())) + \ + ', CPU=' + str(self.get_cpu()) + else: + return ', Cpu=' + str(self.get_cpu()) def is_erroneous(self): """An erroneous event is where something with the event is not quite right, @@ -377,13 +428,56 @@ class Event(DummyEvent): time in the scan, and ``switches'' gives the last time a certain switch (e.g. SwitchToEvent, InversionStartEvent) occurred""" time = self.get_time() - sched = self.get_job().get_task().get_schedule() - if sched.start is None or time < sched.start: - 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) + sched = self.get_schedule() + + if sched is not None: + if sched.start is None or time < sched.start: + sched.start = time + if sched.end is None or time > sched.end: + sched.end = time + + if item_nos is None: + item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), + TimeSlotArray.CPU_LIST : self.get_cpu() } + sched.get_time_slot_array().add_event_to_time_slot(self, item_nos) + + self.fill_span_event_from_end() + + def fill_span_event_from_start(self): + """This method exists for events that can ``range'' over a period of time + (e.g. SwitchAway and SwitchTo). In case a start event is not paired with + an end event, or vice versa, we want to fill in dummy events to range all + the way to the beginning or end. Since most events occur only at a specific + time, this is usually a no-op.""" + pass + + def fill_span_event_from_end(self): + """The mirror image of the last method.""" + pass + +class SpanEvent(Event): + def __init__(self, time, cpu, dummy_class): + super(SpanEvent, self).__init__(time, cpu) + self.dummy_class = dummy_class + +class SpanDummy(DummyEvent): + def __init__(self): + super(SpanDummy, self).__init__(None, None) + + def get_task(self): + if self.corresp_start_event is not None: + return self.corresp_start_event.get_task() + if self.corresp_end_event is not None: + return self.corresp_end_event.get_task() + return None + + def get_schedule(self): + if self.corresp_start_event is not None: + return self.corresp_start_event.get_schedule() + if self.corresp_end_event is not None: + return self.corresp_end_event.get_schedule() + return None class ErrorEvent(Event): pass @@ -631,9 +725,42 @@ class DeadlineEvent(Event): graph.add_sel_deadline_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), self) else: - graph.draw_deadline_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), - self.get_job().get_job_no(), self.is_selected()) + graph.draw_deadline_arrow_at_time(self.get_time(), + self.get_job().get_task().get_task_no(), + self.get_job().get_job_no(), self.is_selected()) +class ActionEvent(Event): + def __init__(self, time, cpu, action): + super(ActionEvent, self).__init__(time, cpu) + self.layer = Canvas.TOP_LAYER + self.action = int(action) + + def get_name(self): + return 'Action' + + def scan(self, cur_cpu, switches): + item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), + TimeSlotArray.CPU_LIST : TimeSlotArray.POST_ITEM_NO } + super(ActionEvent, self).scan(cur_cpu, switches, item_nos) + + def render(self, graph, layer, prev_events, selectable=False): + prev_events[self] = None + if layer == Canvas.TOP_LAYER: + + # TODO: need a more official way of doing this + task_no = -1 + job_no = -1 + if self.get_job() is not None: + task_no = self.get_job().get_task().get_task_no() + job_no = self.get_job().get_job_no() + + if selectable: + graph.add_sel_action_symbol_at_time(self.get_time(), task_no, + self.get_cpu(), self) + else: + graph.draw_action_symbol_at_time(self.get_time(), task_no, + self.get_cpu(), self.action, + job_no, self.is_selected()) class InversionStartEvent(ErrorEvent): def __init__(self, time): @@ -791,7 +918,7 @@ EVENT_LIST = {SuspendEvent : None, ResumeEvent : None, CompleteEvent : None, SwitchAwayEvent : None, SwitchToEvent : None, ReleaseEvent : None, DeadlineEvent : None, IsRunningDummy : None, InversionStartEvent : None, InversionEndEvent : None, - InversionDummy : None} + InversionDummy : None, TaskDummy : None, CPUDummy : None, ActionEvent: None} SPAN_START_EVENTS = { SwitchToEvent : IsRunningDummy, InversionStartEvent : InversionDummy } SPAN_END_EVENTS = { SwitchAwayEvent : IsRunningDummy, InversionEndEvent : InversionDummy} -- cgit v1.2.2