diff options
| author | Mac Mollison <mollison@cs.unc.edu> | 2011-01-31 16:40:54 -0500 |
|---|---|---|
| committer | Mac Mollison <mollison@cs.unc.edu> | 2011-01-31 16:40:54 -0500 |
| commit | d2240e7a9b15d18dfd0f3cbe75f3adf1c9e7fabf (patch) | |
| tree | f7d164c21716b55f3d9195f055db73505bbf951d | |
| parent | 85a089c42749e1394ae12321564109f25fa8154f (diff) | |
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
| -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} |
