diff options
-rw-r--r-- | unit_trace/sanitizer.py | 10 | ||||
-rw-r--r-- | unit_trace/trace_reader.py | 11 | ||||
-rw-r--r-- | unit_trace/viz/canvas.py | 80 | ||||
-rw-r--r-- | unit_trace/viz/convert.py | 31 | ||||
-rw-r--r-- | unit_trace/viz/graph.py | 94 | ||||
-rw-r--r-- | 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): | |||
14 | job_2s_released = [] # list of tasks which have released their job 2s | 14 | job_2s_released = [] # list of tasks which have released their job 2s |
15 | jobs_switched_to = [] | 15 | jobs_switched_to = [] |
16 | 16 | ||
17 | released = False | ||
18 | |||
17 | for record in stream: | 19 | for record in stream: |
18 | 20 | ||
19 | # Ignore records which are not events (e.g. the num_cpus record) | 21 | # Ignore records which are not events (e.g. the num_cpus record) |
@@ -21,6 +23,13 @@ def sanitizer(stream): | |||
21 | yield record | 23 | yield record |
22 | continue | 24 | continue |
23 | 25 | ||
26 | if record.type_name == 'release': | ||
27 | released = released or True | ||
28 | |||
29 | if record.type_name == 'action' and released: | ||
30 | yield record | ||
31 | continue | ||
32 | |||
24 | # All records with job < 2 are garbage | 33 | # All records with job < 2 are garbage |
25 | if record.job < 2: | 34 | if record.job < 2: |
26 | continue | 35 | continue |
@@ -50,4 +59,5 @@ def sanitizer(stream): | |||
50 | if (record.pid,record.job) not in jobs_switched_to: | 59 | if (record.pid,record.job) not in jobs_switched_to: |
51 | record.job -= 1 | 60 | record.job -= 1 |
52 | 61 | ||
62 | |||
53 | yield record | 63 | 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): | |||
91 | 91 | ||
92 | # Keep pulling records as long as we have a buffer | 92 | # Keep pulling records as long as we have a buffer |
93 | while len(file_iter_buff) > 0: | 93 | while len(file_iter_buff) > 0: |
94 | |||
95 | # Select the earliest record from those at the heads of the buffers | 94 | # Select the earliest record from those at the heads of the buffers |
96 | earliest = -1 | 95 | earliest = -1 |
97 | buff_to_refill = -1 | 96 | buff_to_refill = -1 |
@@ -205,6 +204,12 @@ class StHeader: | |||
205 | keys = ['type','cpu','pid','job'] | 204 | keys = ['type','cpu','pid','job'] |
206 | message = 'The header.' | 205 | message = 'The header.' |
207 | 206 | ||
207 | class StActionData: | ||
208 | format = 'Qb' | ||
209 | formatStr = struct.Struct(StHeader.format + format) | ||
210 | keys = StHeader.keys + ['when','action'] | ||
211 | message = 'An action was performed.' | ||
212 | |||
208 | class StNameData: | 213 | class StNameData: |
209 | format = '16s' | 214 | format = '16s' |
210 | formatStr = struct.Struct(StHeader.format + format) | 215 | formatStr = struct.Struct(StHeader.format + format) |
@@ -271,7 +276,7 @@ class StSysReleaseData: | |||
271 | def _get_type(type_num): | 276 | def _get_type(type_num): |
272 | types = [None,StNameData,StParamData,StReleaseData,StAssignedData, | 277 | types = [None,StNameData,StParamData,StReleaseData,StAssignedData, |
273 | StSwitchToData,StSwitchAwayData,StCompletionData,StBlockData, | 278 | StSwitchToData,StSwitchAwayData,StCompletionData,StBlockData, |
274 | StResumeData,StSysReleaseData] | 279 | StResumeData,StActionData,StSysReleaseData] |
275 | if type_num > len(types)-1 or type_num < 1: | 280 | if type_num > len(types)-1 or type_num < 1: |
276 | raise Exception | 281 | raise Exception |
277 | return types[type_num] | 282 | return types[type_num] |
@@ -280,5 +285,5 @@ def _get_type(type_num): | |||
280 | # programmers of other modules) | 285 | # programmers of other modules) |
281 | def _get_type_name(type_num): | 286 | def _get_type_name(type_num): |
282 | type_names = [None,"name","params","release","assign","switch_to", | 287 | type_names = [None,"name","params","release","assign","switch_to", |
283 | "switch_away","completion","block","resume","sys_release"] | 288 | "switch_away","completion","block","resume","action","sys_release"] |
284 | return type_names[type_num] | 289 | 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): | |||
443 | self.add_sel_region(SelectableRegion(x - big_arrowhead_height / Canvas.SQRT3, | 443 | self.add_sel_region(SelectableRegion(x - big_arrowhead_height / Canvas.SQRT3, |
444 | y, 2.0 * big_arrowhead_height / Canvas.SQRT3, height, event)) | 444 | y, 2.0 * big_arrowhead_height / Canvas.SQRT3, height, event)) |
445 | 445 | ||
446 | def draw_action_symbol(self, item, action, x, y, height, selected): | ||
447 | """Draws a release arrow: x, y should give the top (northernmost | ||
448 | point) of the arrow. The height includes the arrowhead.""" | ||
449 | |||
450 | color = {False : GraphFormat.BORDER_COLOR, | ||
451 | True : GraphFormat.HIGHLIGHT_COLOR}[selected] | ||
452 | |||
453 | colors = [self.get_bar_pattern(item).get_color_list()[0], color] | ||
454 | draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] | ||
455 | |||
456 | for i in range(0, 2): | ||
457 | color = colors[i] | ||
458 | draw_func = draw_funcs[i] | ||
459 | |||
460 | draw_func(self, [(x, y), (x - height / Canvas.SQRT3, \ | ||
461 | y - height), \ | ||
462 | (x + height / Canvas.SQRT3, \ | ||
463 | y - height), \ | ||
464 | (x, y)], color, GraphFormat.BORDER_THICKNESS) | ||
465 | |||
466 | self.draw_label(str(action), x, y - height / 2 - .1 * height, | ||
467 | GraphFormat.DEF_FOPTS_LABEL, | ||
468 | AlignMode.CENTER, AlignMode.CENTER, True) | ||
469 | |||
470 | def add_sel_action_symbol(self, x, y, height, event): | ||
471 | self.add_sel_region(SelectableRegion(x - height / Canvas.SQRT3, | ||
472 | y - height, 2.0 * height / Canvas.SQRT3, height, event)) | ||
473 | |||
446 | def draw_release_arrow_small(self, x, y, height, selected): | 474 | def draw_release_arrow_small(self, x, y, height, selected): |
447 | """Draws a small release arrow (most likely coming off the x-axis, although | 475 | """Draws a small release arrow (most likely coming off the x-axis, although |
448 | this method doesn't enforce this): x, y should give the top of the arrow""" | 476 | this method doesn't enforce this): x, y should give the top of the arrow""" |
449 | small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height | 477 | small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height |
450 | 478 | ||
451 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] | 479 | color = {False : GraphFormat.BORDER_COLOR, |
480 | True : GraphFormat.HIGHLIGHT_COLOR}[selected] | ||
452 | 481 | ||
453 | self.draw_line((x, y), (x - small_arrowhead_height, y + small_arrowhead_height), \ | 482 | self.draw_line((x, y), |
483 | (x - small_arrowhead_height, y + small_arrowhead_height), | ||
454 | color, GraphFormat.BORDER_THICKNESS) | 484 | color, GraphFormat.BORDER_THICKNESS) |
455 | self.draw_line((x, y), (x + small_arrowhead_height, y + small_arrowhead_height), \ | 485 | self.draw_line((x, y), (x + small_arrowhead_height, y + small_arrowhead_height), \ |
456 | color, GraphFormat.BORDER_THICKNESS) | 486 | color, GraphFormat.BORDER_THICKNESS) |
@@ -684,14 +714,35 @@ class CairoCanvas(Canvas): | |||
684 | self.surface.ctx.set_line_width(thickness * self.scale) | 714 | self.surface.ctx.set_line_width(thickness * self.scale) |
685 | self.surface.ctx.stroke() | 715 | self.surface.ctx.stroke() |
686 | 716 | ||
717 | def draw_circle(self, x, y, radius, fill_color, border_color, | ||
718 | thickness, do_snap=True): | ||
719 | p = self.surface.get_real_coor(x, y) | ||
720 | if do_snap: | ||
721 | p = (snap(p[0]), snap(p[1])) | ||
722 | |||
723 | self.surface.ctx.save() | ||
724 | self.surface.ctx.arc(p[0], p[1], radius, 0.0, 2 * math.pi) | ||
725 | self.surface.ctx.set_source_rgb(border_color[0], | ||
726 | border_color[1], | ||
727 | border_color[2]) | ||
728 | self.surface.ctx.set_line_width(thickness * self.scale) | ||
729 | self.surface.ctx.stroke() | ||
730 | self.surface.ctx.arc(p[0], p[1], radius, 0.0, 2 * math.pi) | ||
731 | self.surface.ctx.set_source_rgb(fill_color[0], | ||
732 | fill_color[1], | ||
733 | fill_color[2]) | ||
734 | self.surface.ctx.fill() | ||
735 | self.surface.ctx.restore() | ||
736 | |||
687 | def _polyline_common(self, coor_list, color, thickness, do_snap=True): | 737 | def _polyline_common(self, coor_list, color, thickness, do_snap=True): |
688 | scaled_coor_list = [self.scaled(coor[0], coor[1]) for coor in coor_list] | 738 | real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) \ |
689 | real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in scaled_coor_list] | 739 | for coor in coor_list] |
690 | 740 | ||
691 | self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1]) | 741 | self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1]) |
692 | if do_snap: | 742 | if do_snap: |
693 | for i in range(0, len(real_coor_list)): | 743 | for i in range(0, len(real_coor_list)): |
694 | real_coor_list[i] = (snap(real_coor_list[i][0]), snap(real_coor_list[i][1])) | 744 | real_coor_list[i] = (snap(real_coor_list[i][0]), |
745 | snap(real_coor_list[i][1])) | ||
695 | 746 | ||
696 | for coor in real_coor_list[1:]: | 747 | for coor in real_coor_list[1:]: |
697 | self.surface.ctx.line_to(coor[0], coor[1]) | 748 | self.surface.ctx.line_to(coor[0], coor[1]) |
@@ -753,8 +804,23 @@ class CairoCanvas(Canvas): | |||
753 | raise ValueError('Invalid alignment value') | 804 | raise ValueError('Invalid alignment value') |
754 | f_descent_factor, f_height_factor = valign_factors[valign] | 805 | f_descent_factor, f_height_factor = valign_factors[valign] |
755 | 806 | ||
756 | self._draw_label_common(text, x, y, fopts, x_bearing_factor, \ | 807 | return self._get_label_dim_common(text, x, y, fopts, x_bearing_factor, |
757 | f_descent_factor, width_factor, f_height_factor, do_snap) | 808 | f_descent_factor, width_factor, f_height_factor, |
809 | do_snap) | ||
810 | |||
811 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, | ||
812 | halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): | ||
813 | """Draws a label with the given parameters, with the given horizontal and vertical justification.""" | ||
814 | |||
815 | actual_x, actual_y, width, height, f_height = self.get_label_dim(text, x, y, fopts, halign, valign, do_snap) | ||
816 | actual_x, actual_y = self.surface.get_real_coor(actual_x, actual_y) | ||
817 | |||
818 | self.surface.ctx.select_font_face(fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) | ||
819 | self.surface.ctx.set_font_size(fopts.size * self.scale) | ||
820 | self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2]) | ||
821 | self.surface.ctx.move_to(actual_x, actual_y) | ||
822 | |||
823 | self.surface.ctx.show_text(text) | ||
758 | 824 | ||
759 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ | 825 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ |
760 | textfopts=GraphFormat.DEF_FOPTS_LABEL, sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ | 826 | 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): | |||
16 | return nums[type] | 16 | return nums[type] |
17 | 17 | ||
18 | def _get_job_from_record(sched, record): | 18 | def _get_job_from_record(sched, record): |
19 | tname = _pid_to_task_name(record.pid) | 19 | if record.pid == 0: |
20 | job_no = record.job | 20 | return None |
21 | if tname not in sched.get_tasks(): | 21 | else: |
22 | sched.add_task(Task(tname, [])) | 22 | tname = _pid_to_task_name(record.pid) |
23 | if job_no not in sched.get_tasks()[tname].get_jobs(): | 23 | job_no = record.job |
24 | sched.get_tasks()[tname].add_job(Job(job_no, [])) | 24 | if tname not in sched.get_tasks(): |
25 | job = sched.get_tasks()[tname].get_jobs()[job_no] | 25 | sched.add_task(Task(tname, [])) |
26 | return job | 26 | if job_no not in sched.get_tasks()[tname].get_jobs(): |
27 | sched.get_tasks()[tname].add_job(Job(job_no, [])) | ||
28 | job = sched.get_tasks()[tname].get_jobs()[job_no] | ||
29 | return job | ||
27 | 30 | ||
28 | def convert_trace_to_schedule(stream): | 31 | def convert_trace_to_schedule(stream): |
29 | """The main function of interest in this module. Coverts a stream of records | 32 | """The main function of interest in this module. Coverts a stream of records |
@@ -45,12 +48,20 @@ def convert_trace_to_schedule(stream): | |||
45 | if not hasattr(record, 'deadline'): | 48 | if not hasattr(record, 'deadline'): |
46 | record.deadline = None | 49 | record.deadline = None |
47 | 50 | ||
51 | # This whole method should be refactored for this posibility | ||
52 | if job is None: | ||
53 | if record.type_name == "action": | ||
54 | event = ActionEvent(record.when, cpu, record.action) | ||
55 | event.set_schedule(sched) | ||
56 | sched.add_jobless(event) | ||
57 | continue | ||
58 | |||
48 | actions = { | 59 | actions = { |
49 | 'name' : (noop), | 60 | 'name' : (noop), |
50 | 'params' : (noop), | 61 | 'params' : (noop), |
51 | 'release' : (lambda : | 62 | 'release' : (lambda : |
52 | (job.add_event(ReleaseEvent(record.when, cpu)), | 63 | (job.add_event(ReleaseEvent(record.when, cpu)), |
53 | job.add_event(DeadlineEvent(record.deadline, cpu)))), | 64 | job.add_event(DeadlineEvent(record.deadline, cpu)))), |
54 | 'switch_to' : (lambda : | 65 | 'switch_to' : (lambda : |
55 | job.add_event(SwitchToEvent(record.when, cpu))), | 66 | job.add_event(SwitchToEvent(record.when, cpu))), |
56 | 'switch_away' : (lambda : | 67 | 'switch_away' : (lambda : |
@@ -62,6 +73,8 @@ def convert_trace_to_schedule(stream): | |||
62 | job.add_event(SuspendEvent(record.when, cpu))), | 73 | job.add_event(SuspendEvent(record.when, cpu))), |
63 | 'resume' : (lambda : | 74 | 'resume' : (lambda : |
64 | job.add_event(ResumeEvent(record.when, cpu))), | 75 | job.add_event(ResumeEvent(record.when, cpu))), |
76 | 'action' : (lambda : | ||
77 | job.add_event(ActionEvent(record.when, cpu, record.action))), | ||
65 | 'sys_release' : (noop) | 78 | 'sys_release' : (noop) |
66 | } | 79 | } |
67 | 80 | ||
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) | |||
8 | update themselves, unlike the Canvas which can only overwrite itself.""" | 8 | update themselves, unlike the Canvas which can only overwrite itself.""" |
9 | 9 | ||
10 | class Graph(object): | 10 | class Graph(object): |
11 | DEF_BAR_PLIST = [Pattern([(0.0, 0.9, 0.9)]), Pattern([(0.9, 0.3, 0.0)]), Pattern([(0.9, 0.7, 0.0)]), | 11 | DEF_BAR_PLIST = [Pattern([(0.0, 0.9, 0.9)]), Pattern([(0.9, 0.3, 0.0)]), |
12 | Pattern([(0.0, 0.0, 0.8)]), Pattern([(0.0, 0.2, 0.9)]), Pattern([(0.0, 0.6, 0.6)]), | 12 | Pattern([(0.9, 0.7, 0.0)]), Pattern([(0.0, 0.0, 0.8)]), |
13 | Pattern([(0.0, 0.2, 0.9)]), Pattern([(0.0, 0.6, 0.6)]), | ||
13 | Pattern([(0.75, 0.75, 0.75)])] | 14 | Pattern([(0.75, 0.75, 0.75)])] |
14 | 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), | 15 | DEF_ITEM_CLIST = [(0.3, 0.0, 0.0), (0.0, 0.3, 0.0), (0.0, 0.0, 0.3), |
15 | (0.3, 0.0, 0.3)] | 16 | (0.3, 0.3, 0.0), (0.0, 0.3, 0.3), (0.3, 0.0, 0.3)] |
16 | 17 | ||
17 | def __init__(self, CanvasType, surface, start_time, end_time, y_item_list, attrs=GraphFormat(), | 18 | def __init__(self, CanvasType, surface, start_time, end_time, y_item_list, attrs=GraphFormat(), |
18 | item_clist=DEF_ITEM_CLIST, bar_plist=DEF_BAR_PLIST): | 19 | item_clist=DEF_ITEM_CLIST, bar_plist=DEF_BAR_PLIST): |
@@ -106,9 +107,12 @@ class Graph(object): | |||
106 | """get x so that x is at instant ``time'' on the graph""" | 107 | """get x so that x is at instant ``time'' on the graph""" |
107 | return self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + 1.0 * (time - self.start_time) / self.attrs.time_per_maj * self.attrs.maj_sep | 108 | return self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + 1.0 * (time - self.start_time) / self.attrs.time_per_maj * self.attrs.maj_sep |
108 | 109 | ||
110 | def get_item_yorigin(self, item_no): | ||
111 | return self.origin[1] - self._get_y_axis_height() + self.attrs.y_item_size * item_no; | ||
112 | |||
109 | def get_item_ypos(self, item_no): | 113 | def get_item_ypos(self, item_no): |
110 | """get y so that y is where the top of a bar would be in item #n's area""" | 114 | """get y so that y is where the top of a bar would be in item #n's area""" |
111 | return self.origin[1] - self._get_y_axis_height() + self.attrs.y_item_size * (item_no + 0.5 - GraphFormat.BAR_SIZE_FACTOR / 2.0) | 115 | return self.get_item_yorigin(item_no) + self.attrs.y_item_size * (0.5 - GraphFormat.BAR_SIZE_FACTOR / 2.0) |
112 | 116 | ||
113 | def _get_bar_width(self, start_time, end_time): | 117 | def _get_bar_width(self, start_time, end_time): |
114 | return 1.0 * (end_time - start_time) / self.attrs.time_per_maj * self.attrs.maj_sep | 118 | return 1.0 * (end_time - start_time) / self.attrs.time_per_maj * self.attrs.maj_sep |
@@ -167,7 +171,29 @@ class Graph(object): | |||
167 | raise NotImplementedError | 171 | raise NotImplementedError |
168 | 172 | ||
169 | def get_events_to_render(self, sched, regions, selectable=False): | 173 | def get_events_to_render(self, sched, regions, selectable=False): |
170 | raise NotImplementedError | 174 | slots = {} |
175 | |||
176 | self.min_time, self.max_time, self.min_item, self.max_item = None, None, None, None | ||
177 | for region in regions: | ||
178 | x, y, width, height = region | ||
179 | start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height) | ||
180 | self._recomp_min_max(start_time, end_time, start_item, end_item) | ||
181 | |||
182 | sched.get_time_slot_array().get_slots(slots, | ||
183 | start_time, end_time, start_item, end_item, | ||
184 | self.list_type) | ||
185 | |||
186 | events_to_render = {} | ||
187 | for layer in Canvas.LAYERS: | ||
188 | events_to_render[layer] = {} | ||
189 | |||
190 | for event in sched.get_time_slot_array().get_events(slots, | ||
191 | self.list_type, schedule.EVENT_LIST): | ||
192 | events_to_render[event.get_layer()][event] = None | ||
193 | for event in sched.get_jobless(): | ||
194 | events_to_render[event.get_layer()][event] = None | ||
195 | |||
196 | return events_to_render | ||
171 | 197 | ||
172 | def render_surface(self, sched, regions, selectable=False): | 198 | def render_surface(self, sched, regions, selectable=False): |
173 | if not selectable: | 199 | if not selectable: |
@@ -269,6 +295,16 @@ class Graph(object): | |||
269 | a certain time.""" | 295 | a certain time.""" |
270 | raise NotImplementedError | 296 | raise NotImplementedError |
271 | 297 | ||
298 | def draw_action_symbol_at_time(self, time, task_no, cpu_no, action, | ||
299 | job_no, selected=False): | ||
300 | """Draws an action symbol at a certain time for some task and job""" | ||
301 | raise NotImplementedError | ||
302 | |||
303 | def add_sel_action_symbol_at_time(self, time, task_no, cpu_no, event): | ||
304 | """Same as above, except instead of drawing adds a selectable region at | ||
305 | a certain time.""" | ||
306 | raise NotImplementedError | ||
307 | |||
272 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None): | 308 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None): |
273 | """Draws a bar over a certain time period for some task, optionally labelling it.""" | 309 | """Draws a bar over a certain time period for some task, optionally labelling it.""" |
274 | raise NotImplementedError | 310 | raise NotImplementedError |
@@ -391,6 +427,33 @@ class TaskGraph(Graph): | |||
391 | 427 | ||
392 | self.canvas.add_sel_deadline_arrow_big(x, y, height, event) | 428 | self.canvas.add_sel_deadline_arrow_big(x, y, height, event) |
393 | 429 | ||
430 | def draw_action_symbol_at_time(self, time, task_no, cpu_no, action, | ||
431 | job_no=None, selected=False): | ||
432 | x = self.get_time_xpos(time) | ||
433 | y = None | ||
434 | |||
435 | if task_no != -1: | ||
436 | y = self.get_item_ypos(task_no) | ||
437 | else: | ||
438 | y = self.origin[1] | ||
439 | |||
440 | height = 1.5 * (self.get_item_ypos(0) - self.get_item_yorigin(0)) | ||
441 | |||
442 | self.canvas.draw_action_symbol(cpu_no, action, x, y, height, selected) | ||
443 | |||
444 | def add_sel_action_symbol_at_time(self, time, task_no, cpu_no, event): | ||
445 | x = self.get_time_xpos(time) | ||
446 | y = None | ||
447 | |||
448 | if task_no != -1: | ||
449 | y = self.get_item_ypos(task_no) | ||
450 | else: | ||
451 | y = self.origin[1] | ||
452 | |||
453 | height = 1.5 * (self.get_item_ypos(0) - self.get_item_yorigin(0)) | ||
454 | |||
455 | self.canvas.add_sel_action_symbol(x, y, height, event) | ||
456 | |||
394 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): | 457 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): |
395 | if start_time > end_time: | 458 | if start_time > end_time: |
396 | raise ValueError("Litmus is not a time machine") | 459 | raise ValueError("Litmus is not a time machine") |
@@ -599,10 +662,27 @@ class CpuGraph(Graph): | |||
599 | height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR | 662 | height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR |
600 | 663 | ||
601 | x = self.get_time_xpos(time) | 664 | x = self.get_time_xpos(time) |
602 | y = self.origin[1] - height | 665 | y = self.get_item_ypos(task_no) |
603 | 666 | ||
604 | self.canvas.add_sel_deadline_arrow_small(x, y, height, event) | 667 | self.canvas.add_sel_deadline_arrow_small(x, y, height, event) |
605 | 668 | ||
669 | def draw_action_symbol_at_time(self, time, task_no, cpu_no, action, | ||
670 | job_no=None, selected=False): | ||
671 | x = self.get_time_xpos(time) | ||
672 | y = self.get_item_ypos(cpu_no) | ||
673 | |||
674 | height = 1.5 * (y - self.get_item_yorigin(cpu_no)) | ||
675 | |||
676 | self.canvas.draw_action_symbol(task_no, action, x, y, height, selected) | ||
677 | |||
678 | def add_sel_action_symbol_at_time(self, time, task_no, cpu_no, event): | ||
679 | x = self.get_time_xpos(time) | ||
680 | y = self.get_item_ypos(cpu_no) | ||
681 | |||
682 | height = 1.5 * (y - self.get_item_yorigin(cpu_no)) | ||
683 | |||
684 | self.canvas.add_sel_action_symbol(x, y, height, event) | ||
685 | |||
606 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): | 686 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): |
607 | if start_time > end_time: | 687 | if start_time > end_time: |
608 | raise ValueError("Litmus is not a time machine") | 688 | 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): | |||
138 | self.time_slot_array = None | 138 | self.time_slot_array = None |
139 | self.cur_task_no = 0 | 139 | self.cur_task_no = 0 |
140 | self.num_cpus = num_cpus | 140 | self.num_cpus = num_cpus |
141 | self.jobless = [] | ||
141 | for task in task_list: | 142 | for task in task_list: |
142 | self.add_task(task) | 143 | self.add_task(task) |
143 | 144 | ||
@@ -217,6 +218,23 @@ class Schedule(object): | |||
217 | task.task_no = self.cur_task_no | 218 | task.task_no = self.cur_task_no |
218 | self.cur_task_no += 1 | 219 | self.cur_task_no += 1 |
219 | 220 | ||
221 | def add_jobless(self, event): | ||
222 | self.jobless.append(event) | ||
223 | |||
224 | def sort_task_nos_numeric(self): | ||
225 | # sort task numbers by the numeric value of the task names. | ||
226 | nums = [] | ||
227 | |||
228 | for task_name in self.tasks: | ||
229 | nums.append((int(task_name), task_name)) | ||
230 | |||
231 | nums.sort(key=lambda t: t[0]) | ||
232 | for no, task in enumerate(nums): | ||
233 | self.tasks[task[1]].task_no = no | ||
234 | |||
235 | def get_jobless(self): | ||
236 | return self.jobless | ||
237 | |||
220 | def get_tasks(self): | 238 | def get_tasks(self): |
221 | return self.tasks | 239 | return self.tasks |
222 | 240 | ||
@@ -295,12 +313,12 @@ class DummyEvent(object): | |||
295 | event added by the application to speed things up or keep track of | 313 | event added by the application to speed things up or keep track of |
296 | something. Such an event won't be added to the schedule tree, but | 314 | something. Such an event won't be added to the schedule tree, but |
297 | might appear in the time slot array.""" | 315 | might appear in the time slot array.""" |
298 | |||
299 | def __init__(self, time, cpu): | 316 | def __init__(self, time, cpu): |
300 | self.time = time | 317 | self.time = time |
301 | self.cpu = cpu | 318 | self.cpu = cpu |
302 | self.job = None | 319 | self.job = None |
303 | self.layer = None | 320 | self.layer = None |
321 | self.saved_schedule = None | ||
304 | 322 | ||
305 | def __str__(self): | 323 | def __str__(self): |
306 | return '[Dummy Event]' | 324 | return '[Dummy Event]' |
@@ -311,6 +329,25 @@ class DummyEvent(object): | |||
311 | def get_cpu(self): | 329 | def get_cpu(self): |
312 | return self.cpu | 330 | return self.cpu |
313 | 331 | ||
332 | # Refactor, shouldn't depend on job | ||
333 | def get_schedule(self): | ||
334 | if self.saved_schedule is not None: | ||
335 | return self.saved_schedule | ||
336 | elif self.get_task() is not None: | ||
337 | return self.get_task().get_schedule() | ||
338 | else: | ||
339 | return None | ||
340 | |||
341 | # Needed for events not assigned to specific tasks | ||
342 | def set_schedule(self, schedule): | ||
343 | self.saved_schedule = schedule | ||
344 | |||
345 | def get_task(self): | ||
346 | if self.get_job() is not None: | ||
347 | return self.get_job().get_task() | ||
348 | else: | ||
349 | return None | ||
350 | |||
314 | def get_job(self): | 351 | def get_job(self): |
315 | return self.job | 352 | return self.job |
316 | 353 | ||
@@ -342,20 +379,34 @@ class Event(DummyEvent): | |||
342 | def __str__(self): | 379 | def __str__(self): |
343 | return self.get_name() + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | 380 | return self.get_name() + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) |
344 | 381 | ||
345 | def str_long(self): | 382 | def str_long(self, unit): |
346 | """Prints the event as a string, in ``long'' form.""" | 383 | if self.get_job() is not None: |
347 | return 'Event Type: ' + self.get_name() + \ | 384 | """Prints the event as a string, in ``long'' form.""" |
348 | '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ | 385 | return 'Event Information\n-----------------\n' + \ |
349 | '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ | 386 | 'Event Type: ' + self.get_name() + \ |
350 | self.get_job().get_job_no())) + \ | 387 | '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ |
351 | '\nCPU: ' + str(self.get_cpu()) + \ | 388 | '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ |
352 | '\nTime: ' + str(self.get_time()) | 389 | self.get_job().get_job_no())) + \ |
390 | '\nCPU: ' + str(self.get_cpu()) + \ | ||
391 | '\nTime: ' + _format_time(self.get_time(), unit) + \ | ||
392 | '\n\n' + self.get_job().str_long(unit) | ||
393 | else: | ||
394 | """Prints the event as a string, in ``long'' form.""" | ||
395 | return 'Event Information\n-----------------\n' + \ | ||
396 | 'Event Type: ' + self.get_name() + \ | ||
397 | '\nTask Name: None' + \ | ||
398 | '\nCPU: ' + str(self.get_cpu()) + \ | ||
399 | '\nTime: ' + _format_time(self.get_time(), unit) | ||
353 | 400 | ||
354 | def _common_str(self): | 401 | def _common_str(self): |
355 | job = self.get_job() | 402 | if self.get_job() is not None: |
356 | task = job.get_task() | 403 | job = self.get_job() |
357 | return ' for task ' + str(task.get_name()) + ': (TASK, JOB)=' + str((task.get_task_no(), \ | 404 | task = job.get_task() |
358 | job.get_job_no())) + ', CPU=' + str(self.get_cpu()) | 405 | return ' for task ' + str(task.get_name()) + ': (TASK, JOB)=' + \ |
406 | str((task.get_task_no(), job.get_job_no())) + \ | ||
407 | ', CPU=' + str(self.get_cpu()) | ||
408 | else: | ||
409 | return ', Cpu=' + str(self.get_cpu()) | ||
359 | 410 | ||
360 | def is_erroneous(self): | 411 | def is_erroneous(self): |
361 | """An erroneous event is where something with the event is not quite right, | 412 | """An erroneous event is where something with the event is not quite right, |
@@ -377,13 +428,56 @@ class Event(DummyEvent): | |||
377 | time in the scan, and ``switches'' gives the last time a certain switch | 428 | time in the scan, and ``switches'' gives the last time a certain switch |
378 | (e.g. SwitchToEvent, InversionStartEvent) occurred""" | 429 | (e.g. SwitchToEvent, InversionStartEvent) occurred""" |
379 | time = self.get_time() | 430 | time = self.get_time() |
380 | sched = self.get_job().get_task().get_schedule() | ||
381 | if sched.start is None or time < sched.start: | ||
382 | sched.start = time | ||
383 | if sched.end is None or time > sched.end: | ||
384 | sched.end = time | ||
385 | 431 | ||
386 | sched.get_time_slot_array().add_event_to_time_slot(self) | 432 | sched = self.get_schedule() |
433 | |||
434 | if sched is not None: | ||
435 | if sched.start is None or time < sched.start: | ||
436 | sched.start = time | ||
437 | if sched.end is None or time > sched.end: | ||
438 | sched.end = time | ||
439 | |||
440 | if item_nos is None: | ||
441 | item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), | ||
442 | TimeSlotArray.CPU_LIST : self.get_cpu() } | ||
443 | sched.get_time_slot_array().add_event_to_time_slot(self, item_nos) | ||
444 | |||
445 | self.fill_span_event_from_end() | ||
446 | |||
447 | def fill_span_event_from_start(self): | ||
448 | """This method exists for events that can ``range'' over a period of time | ||
449 | (e.g. SwitchAway and SwitchTo). In case a start event is not paired with | ||
450 | an end event, or vice versa, we want to fill in dummy events to range all | ||
451 | the way to the beginning or end. Since most events occur only at a specific | ||
452 | time, this is usually a no-op.""" | ||
453 | pass | ||
454 | |||
455 | def fill_span_event_from_end(self): | ||
456 | """The mirror image of the last method.""" | ||
457 | pass | ||
458 | |||
459 | class SpanEvent(Event): | ||
460 | def __init__(self, time, cpu, dummy_class): | ||
461 | super(SpanEvent, self).__init__(time, cpu) | ||
462 | self.dummy_class = dummy_class | ||
463 | |||
464 | class SpanDummy(DummyEvent): | ||
465 | def __init__(self): | ||
466 | super(SpanDummy, self).__init__(None, None) | ||
467 | |||
468 | def get_task(self): | ||
469 | if self.corresp_start_event is not None: | ||
470 | return self.corresp_start_event.get_task() | ||
471 | if self.corresp_end_event is not None: | ||
472 | return self.corresp_end_event.get_task() | ||
473 | return None | ||
474 | |||
475 | def get_schedule(self): | ||
476 | if self.corresp_start_event is not None: | ||
477 | return self.corresp_start_event.get_schedule() | ||
478 | if self.corresp_end_event is not None: | ||
479 | return self.corresp_end_event.get_schedule() | ||
480 | return None | ||
387 | 481 | ||
388 | class ErrorEvent(Event): | 482 | class ErrorEvent(Event): |
389 | pass | 483 | pass |
@@ -631,9 +725,42 @@ class DeadlineEvent(Event): | |||
631 | graph.add_sel_deadline_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | 725 | graph.add_sel_deadline_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), |
632 | self) | 726 | self) |
633 | else: | 727 | else: |
634 | graph.draw_deadline_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | 728 | graph.draw_deadline_arrow_at_time(self.get_time(), |
635 | self.get_job().get_job_no(), self.is_selected()) | 729 | self.get_job().get_task().get_task_no(), |
730 | self.get_job().get_job_no(), self.is_selected()) | ||
636 | 731 | ||
732 | class ActionEvent(Event): | ||
733 | def __init__(self, time, cpu, action): | ||
734 | super(ActionEvent, self).__init__(time, cpu) | ||
735 | self.layer = Canvas.TOP_LAYER | ||
736 | self.action = int(action) | ||
737 | |||
738 | def get_name(self): | ||
739 | return 'Action' | ||
740 | |||
741 | def scan(self, cur_cpu, switches): | ||
742 | item_nos = { TimeSlotArray.TASK_LIST : self.get_task().get_task_no(), | ||
743 | TimeSlotArray.CPU_LIST : TimeSlotArray.POST_ITEM_NO } | ||
744 | super(ActionEvent, self).scan(cur_cpu, switches, item_nos) | ||
745 | |||
746 | def render(self, graph, layer, prev_events, selectable=False): | ||
747 | prev_events[self] = None | ||
748 | if layer == Canvas.TOP_LAYER: | ||
749 | |||
750 | # TODO: need a more official way of doing this | ||
751 | task_no = -1 | ||
752 | job_no = -1 | ||
753 | if self.get_job() is not None: | ||
754 | task_no = self.get_job().get_task().get_task_no() | ||
755 | job_no = self.get_job().get_job_no() | ||
756 | |||
757 | if selectable: | ||
758 | graph.add_sel_action_symbol_at_time(self.get_time(), task_no, | ||
759 | self.get_cpu(), self) | ||
760 | else: | ||
761 | graph.draw_action_symbol_at_time(self.get_time(), task_no, | ||
762 | self.get_cpu(), self.action, | ||
763 | job_no, self.is_selected()) | ||
637 | 764 | ||
638 | class InversionStartEvent(ErrorEvent): | 765 | class InversionStartEvent(ErrorEvent): |
639 | def __init__(self, time): | 766 | def __init__(self, time): |
@@ -791,7 +918,7 @@ EVENT_LIST = {SuspendEvent : None, ResumeEvent : None, CompleteEvent : None, | |||
791 | SwitchAwayEvent : None, SwitchToEvent : None, ReleaseEvent : None, | 918 | SwitchAwayEvent : None, SwitchToEvent : None, ReleaseEvent : None, |
792 | DeadlineEvent : None, IsRunningDummy : None, | 919 | DeadlineEvent : None, IsRunningDummy : None, |
793 | InversionStartEvent : None, InversionEndEvent : None, | 920 | InversionStartEvent : None, InversionEndEvent : None, |
794 | InversionDummy : None} | 921 | InversionDummy : None, TaskDummy : None, CPUDummy : None, ActionEvent: None} |
795 | 922 | ||
796 | SPAN_START_EVENTS = { SwitchToEvent : IsRunningDummy, InversionStartEvent : InversionDummy } | 923 | SPAN_START_EVENTS = { SwitchToEvent : IsRunningDummy, InversionStartEvent : InversionDummy } |
797 | SPAN_END_EVENTS = { SwitchAwayEvent : IsRunningDummy, InversionEndEvent : InversionDummy} | 924 | SPAN_END_EVENTS = { SwitchAwayEvent : IsRunningDummy, InversionEndEvent : InversionDummy} |