summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMac Mollison <mollison@cs.unc.edu>2011-01-31 16:40:54 -0500
committerMac Mollison <mollison@cs.unc.edu>2011-01-31 16:40:54 -0500
commitd2240e7a9b15d18dfd0f3cbe75f3adf1c9e7fabf (patch)
treef7d164c21716b55f3d9195f055db73505bbf951d
parent85a089c42749e1394ae12321564109f25fa8154f (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.py10
-rw-r--r--unit_trace/trace_reader.py11
-rw-r--r--unit_trace/viz/canvas.py80
-rw-r--r--unit_trace/viz/convert.py31
-rw-r--r--unit_trace/viz/graph.py94
-rw-r--r--unit_trace/viz/schedule.py171
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
207class StActionData:
208 format = 'Qb'
209 formatStr = struct.Struct(StHeader.format + format)
210 keys = StHeader.keys + ['when','action']
211 message = 'An action was performed.'
212
208class StNameData: 213class 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:
271def _get_type(type_num): 276def _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)
281def _get_type_name(type_num): 286def _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
18def _get_job_from_record(sched, record): 18def _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
28def convert_trace_to_schedule(stream): 31def 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)
8update themselves, unlike the Canvas which can only overwrite itself.""" 8update themselves, unlike the Canvas which can only overwrite itself."""
9 9
10class Graph(object): 10class 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
459class SpanEvent(Event):
460 def __init__(self, time, cpu, dummy_class):
461 super(SpanEvent, self).__init__(time, cpu)
462 self.dummy_class = dummy_class
463
464class 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
388class ErrorEvent(Event): 482class 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
732class 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
638class InversionStartEvent(ErrorEvent): 765class 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
796SPAN_START_EVENTS = { SwitchToEvent : IsRunningDummy, InversionStartEvent : InversionDummy } 923SPAN_START_EVENTS = { SwitchToEvent : IsRunningDummy, InversionStartEvent : InversionDummy }
797SPAN_END_EVENTS = { SwitchAwayEvent : IsRunningDummy, InversionEndEvent : InversionDummy} 924SPAN_END_EVENTS = { SwitchAwayEvent : IsRunningDummy, InversionEndEvent : InversionDummy}