aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern Brandenburg <bbb@mpi-sws.org>2016-03-22 15:53:55 -0400
committerBjoern Brandenburg <bbb@mpi-sws.org>2016-03-22 16:00:36 -0400
commit27474dfff59e40e1abf33e0b5dfe7a16dc73f4b1 (patch)
treeead56b3783117e666d008498120f8de7f6627bfe
parent17fedde91157d9e57a3c98c00c11ca03b0afcc78 (diff)
add st-draw: a pycairo based drawing tool for st_trace schedules
-rw-r--r--.gitignore3
-rw-r--r--sched_trace/__init__.py202
-rw-r--r--sched_trace/draw.py231
-rw-r--r--sched_trace/file.py58
-rw-r--r--sched_trace/format.py118
-rwxr-xr-xst-draw142
6 files changed, 754 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 2f993ac..8cdbb63 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
1.sconsign.dblite 1.sconsign.dblite
2*.o 2*.o
3*.d 3*.d
4*.pyc
5*.pdf
6*.bin
4~* 7~*
5*~ 8*~
6ftcat 9ftcat
diff --git a/sched_trace/__init__.py b/sched_trace/__init__.py
new file mode 100644
index 0000000..5db2c2d
--- /dev/null
+++ b/sched_trace/__init__.py
@@ -0,0 +1,202 @@
1from __future__ import division
2
3from collections import defaultdict
4from itertools import imap
5
6import heapq
7
8from .format import event, EVENTS_WITHOUT_TIMESTAMP, EVENTS
9from .file import SchedTraceFile
10
11def event_id(unpacked_record):
12 "Given a tuple from format.unpack(), return the ID of the event"
13 return unpacked_record[0]
14
15def event_cpu(unpacked_record):
16 "Given a tuple from format.unpack(), return the CPU that recorded the event"
17 return unpacked_record[1]
18
19def event_pid(unpacked_record):
20 "Given a tuple from format.unpack(), return the process ID of the event"
21 return unpacked_record[2]
22
23def event_job_id(unpacked_record):
24 "Given a tuple from format.unpack(), return the job ID of the event"
25 return unpacked_record[3]
26
27def event_name(unpacked_record):
28 "Given a tuple from format.unpack(), return the name of the event"
29 # first element of tuple from format.unpack() is event type
30 return event(unpacked_record[0])
31
32def event_time(unpacked_record):
33 "Given a tuple from format.unpack(), return the time of the event"
34 if unpacked_record[0] in EVENTS_WITHOUT_TIMESTAMP:
35 return 0
36 else:
37 return unpacked_record[4]
38
39def lookup_ids(events):
40 wanted = set()
41 for e in events:
42 if type(e) == int:
43 # directly add numeric IDs
44 wanted.add(e)
45 else:
46 # translate strings
47 wanted.add(EVENTS[e])
48 return wanted
49
50
51def cached(trace_attr):
52 cached_val = []
53 def check_cache(*args, **kargs):
54 if not cached_val:
55 cached_val.append(trace_attr(*args, **kargs))
56 return cached_val[0]
57 return check_cache
58
59class SchedTrace(object):
60 def __init__(self, trace_files):
61 self.traces = [SchedTraceFile(f) for f in trace_files]
62
63 self._names = None
64 self._wcets = None
65 self._periods = None
66 self._phases = None
67 self._partitions = None
68 self._sys_rels = None
69
70 def __len__(self):
71 return sum((len(t) for t in self.traces))
72
73 def __iter__(self):
74 for t in self.traces:
75 for rec in t:
76 yield rec
77
78 def events_of_type(self, *events):
79 wanted = lookup_ids(events)
80 for t in self.traces:
81 for rec in t.events_of_type(wanted):
82 yield rec
83
84 def events_of_type_chrono(self, *events):
85 wanted = lookup_ids(events)
86
87 def by_event_time(rec):
88 return (event_time(rec), rec)
89
90 trace_iters = [imap(by_event_time, t.events_of_type(wanted))
91 for t in self.traces]
92
93 for (when, rec) in heapq.merge(*trace_iters):
94 yield rec
95
96 def events_in_range_of_type(self, start=0, end=0, *events, **kargs):
97 if 'sorted' in kargs and kargs['sorted']:
98 all = self.events_of_type_chrono(*events)
99 else:
100 all = self.events_of_type(*events)
101
102 for rec in all:
103 if start <= event_time(rec) <= end:
104 yield rec
105
106 def active_in_interval(self, start=0, end=0):
107 tasks = set()
108 cores = set()
109 for rec in self.events_of_type('ST_SWITCH_TO', 'ST_SWITCH_AWAY', 'ST_RELEASE'):
110 if start <= event_time(rec) <= end:
111 tasks.add(event_pid(rec))
112 cores.add(event_cpu(rec))
113 return tasks, cores
114
115 def scheduling_intervals(self):
116 for t in self.traces:
117 for interval in t.scheduling_intervals():
118 yield interval
119
120 def scheduling_intervals_in_range(self, start=0, end=0):
121 for t in self.traces:
122 for (to, away) in t.scheduling_intervals():
123 if not (end < event_time(to) or event_time(away) < start):
124 yield (to, away)
125
126 def identify_tasks(self):
127 self._names = defaultdict(str)
128 self._wcets = defaultdict(int)
129 self._periods = defaultdict(int)
130 self._phases = defaultdict(int)
131 self._partitions = defaultdict(int)
132 param_id = EVENTS['ST_PARAM']
133 name_id = EVENTS['ST_NAME']
134 for rec in self.events_of_type(param_id, name_id):
135 pid = rec[2]
136 if rec[0] == param_id:
137 wcet, period, phase, partition = rec[-4:]
138 self._wcets[pid] = wcet
139 self._periods[pid] = period
140 self._phases[pid] = phase
141 self._partitions[pid] = partition
142 elif rec[0] == name_id:
143 self._names[pid] = rec[-1]
144
145 @property
146 def task_wcets(self):
147 if self._wcets is None:
148 self.identify_tasks()
149 return self._wcets
150
151 @property
152 def task_periods(self):
153 if self._periods is None:
154 self.identify_tasks()
155 return self._periods
156
157 @property
158 def task_phases(self):
159 if self._phases is None:
160 self.identify_tasks()
161 return self._phases
162
163 @property
164 def task_partitions(self):
165 if self._partitions is None:
166 self.identify_tasks()
167 return self._partitions
168
169 @property
170 def task_names(self):
171 if self._names is None:
172 self.identify_tasks()
173 return self._names
174
175 @property
176 @cached
177 def system_releases(self):
178 return [rec[-1] for rec in self.events_of_type('ST_SYS_RELEASE')]
179
180 @property
181 @cached
182 def earliest_event_time(self):
183 earliest = None
184 for t in self.traces:
185 for rec in t:
186 if event_time(rec) > 0:
187 if earliest is None or event_time(rec) < earliest:
188 earliest = event_time(rec)
189 break
190 return earliest
191
192 @property
193 @cached
194 def latest_event_time(self):
195 latest = None
196 for t in self.traces:
197 for rec in reversed(t):
198 if event_time(rec) > 0:
199 if latest is None or event_time(rec) > latest:
200 latest = event_time(rec)
201 break
202 return latest
diff --git a/sched_trace/draw.py b/sched_trace/draw.py
new file mode 100644
index 0000000..4ccf245
--- /dev/null
+++ b/sched_trace/draw.py
@@ -0,0 +1,231 @@
1from __future__ import division
2
3import sys
4import cairo
5
6from math import ceil
7
8from .format import EVENTS
9
10from . import event_time, event_pid, event_cpu
11
12ARROW_LINE_WIDTH = 2 # (pts)
13ARROW_WIDTH = 12 # (pts)
14ARROW_HEIGHT = 0.8 # (relative)
15ALLOC_HEIGHT = 0.5 # (relative)
16
17COLORS = [
18 (0, 0, 0),
19 (1, 0, 0),
20 (0, 0, 1),
21 (0, 1, 0),
22 (1, 1, 0),
23 (1, 0, 1),
24 (0, 1, 1),
25]
26
27def cpu_color(cpu):
28 return COLORS[cpu % len(COLORS)]
29
30MAJOR_TICK_FONT_SIZE = 24
31TASK_LABEL_FONT_SIZE = 18
32TASK_LABEL_COLOR = (0, 0, 0)
33
34GRID_WIDTH = 2
35MAJOR_TICK_WIDTH = 2
36MINOR_TICK_WIDTH = 1
37
38MAJOR_TICK_COLOR = (0.7, 0.7, 0.7)
39MINOR_TICK_COLOR = (0.8, 0.8, 0.8)
40
41GRID_COLOR = (0.7, 0.7, 0.7)
42
43JOB_EVENT_COLOR = (0, 0, 0)
44
45YRES = 100
46
47
48
49XLABEL_MARGIN = 72
50YLABEL_MARGIN = 200
51
52def center_text(c, x, y, msg):
53 x_bear, y_bear, width, height, x_adv, y_adv = c.text_extents(msg)
54 c.move_to(x, y)
55 c.rel_move_to(-width / 2, height)
56 c.show_text(msg)
57
58def vcenter_right_align_text(c, x, y, msg):
59 x_bear, y_bear, width, height, x_adv, y_adv = c.text_extents(msg)
60 c.move_to(x, y)
61 c.rel_move_to(-width, height/2)
62 c.show_text(msg)
63 return y_adv
64
65def render(opts, trace):
66 if opts.verbose:
67 print '[II] Identifying relevant tasks and CPUs...',
68 sys.stdout.flush()
69 tasks, cores = trace.active_in_interval(opts.start, opts.end)
70 if opts.verbose:
71 print '%d tasks, %d cores.' % (len(tasks), len(cores))
72
73 # scale is given in points/ms, we need points/ns
74 xscale = opts.xscale/1E6
75 yscale = opts.yscale/YRES
76 width = ceil(opts.margin * 2 + (opts.end - opts.start) * xscale + YLABEL_MARGIN)
77
78 height = ceil(opts.margin * 2 + (len(tasks) * YRES) * yscale + XLABEL_MARGIN)
79
80 if opts.verbose:
81 print '[II] Canvas size: %dpt x %dpt' % (width, height)
82
83 pdf = cairo.PDFSurface(opts.output, width, height)
84
85 task_idx = {}
86 for i, t in enumerate(sorted(tasks)):
87 task_idx[t] = i
88
89
90 c = cairo.Context(pdf)
91 c.translate(opts.margin + YLABEL_MARGIN, opts.margin)
92
93 # draw box
94# c.rectangle(0, 0, width - opts.margin * 2, height - opts.margin * 2)
95# c.stroke()
96
97 def xpos(x):
98 return (x - opts.start) * xscale
99
100 def ypos(y):
101 return (y * YRES) * yscale
102
103# c.scale(xscale, yscale)
104
105 # draw minor tick lines
106 if opts.minor_ticks:
107 c.set_source_rgb(*MINOR_TICK_COLOR)
108 c.set_line_width(MINOR_TICK_WIDTH)
109 time = opts.start
110 while time <= opts.end:
111 x = xpos(time)
112 y = ypos(len(tasks))
113 c.new_path()
114 c.move_to(x, y)
115 c.line_to(x, 0)
116 c.stroke()
117 time += opts.minor_ticks * 1E6
118
119
120 # draw major tick lines
121 if opts.major_ticks:
122 c.set_source_rgb(*MAJOR_TICK_COLOR)
123 c.set_line_width(MAJOR_TICK_WIDTH)
124 c.set_font_size(MAJOR_TICK_FONT_SIZE)
125 time = opts.start
126 while time <= opts.end:
127 x = xpos(time)
128 y = ypos(len(tasks) + 0.2)
129 c.new_path()
130 c.move_to(x, y)
131 c.line_to(x, 0)
132 c.stroke()
133 y = ypos(len(tasks) + 0.3)
134 center_text(c, x, y, "%dms" % ((time - opts.start) / 1E6))
135 time += opts.major_ticks * 1E6
136
137
138 # raw allocations
139 box_height = ALLOC_HEIGHT * YRES * yscale
140 for (to, away) in trace.scheduling_intervals_in_range(opts.start, opts.end):
141 delta = (event_time(away) - event_time(to)) * xscale
142 pid = event_pid(to)
143 y = ypos(task_idx[pid] + ALLOC_HEIGHT)
144 x = xpos(event_time(to))
145 c.new_path()
146 c.rectangle(x, y, delta, box_height)
147 c.set_source_rgb(*cpu_color(event_cpu(to)))
148 c.fill()
149
150 # draw task base lines
151 c.set_source_rgb(*GRID_COLOR)
152 c.set_line_width(GRID_WIDTH)
153 for t in tasks:
154 c.new_path()
155 y = ypos(task_idx[t] + 1)
156 x = xpos(opts.start)
157 c.move_to(x, y)
158 x = xpos(opts.end)
159 c.line_to(x, y)
160 c.stroke()
161
162 # draw releases and deadlines
163 c.set_source_rgb(*JOB_EVENT_COLOR)
164 c.set_line_width(ARROW_LINE_WIDTH)
165# c.set_line_cap(cairo.LINE_JOIN_ROUND)
166 c.set_line_join(cairo.LINE_JOIN_ROUND)
167 arrow_width = ARROW_WIDTH / 2
168 arrow_height = ARROW_HEIGHT * YRES * yscale
169 for rec in trace.events_in_range_of_type(opts.start, opts.end, 'ST_RELEASE'):
170 pid = event_pid(rec)
171 y = ypos(task_idx[pid] + 1)
172
173 rel = xpos(event_time(rec))
174 c.new_path()
175 c.move_to(rel, y - (GRID_WIDTH - 1))
176 c.rel_line_to(0, -arrow_height)
177 c.rel_line_to(-arrow_width, arrow_width)
178 c.rel_move_to(arrow_width, -arrow_width)
179 c.rel_line_to(arrow_width, arrow_width)
180 c.stroke()
181
182 dl = xpos(rec[-1])
183 c.new_path()
184 c.move_to(dl, y - arrow_height - (GRID_WIDTH - 1))
185 c.rel_line_to(0, arrow_height)
186 c.rel_line_to(-arrow_width, -arrow_width)
187 c.rel_move_to(arrow_width, arrow_width)
188 c.rel_line_to(arrow_width, -arrow_width)
189 c.stroke()
190
191 # draw job completions
192 for rec in trace.events_in_range_of_type(opts.start, opts.end, 'ST_COMPLETION'):
193 pid = event_pid(rec)
194 y = ypos(task_idx[pid] + 1)
195 x = xpos(event_time(rec))
196 c.new_path()
197 c.move_to(x, y - (GRID_WIDTH - 1))
198 c.rel_line_to(0, -arrow_height)
199 c.rel_move_to(-arrow_width, 0)
200 c.rel_line_to(2 * arrow_width, 0)
201 c.stroke()
202
203
204 # draw task labels
205 c.set_font_size(TASK_LABEL_FONT_SIZE)
206 c.set_source_rgb(*TASK_LABEL_COLOR)
207 for pid in tasks:
208 x = -24
209 y = ypos(task_idx[pid] + 0.25)
210 vcenter_right_align_text(c, x, y, "%s/%d" % (trace.task_names[pid], pid))
211 y = ypos(task_idx[pid] + 0.75)
212 vcenter_right_align_text(c, x, y,
213 "(%.2fms, %.2fms)" % (trace.task_wcets[pid] / 1E6,
214 trace.task_periods[pid] / 1E6))
215
216
217 if opts.verbose:
218 print '[II] Finishing PDF...',
219 sys.stdout.flush()
220 pdf.finish()
221 if opts.verbose:
222 print 'done.'
223
224 if opts.verbose:
225 print '[II] Flushing PDF...',
226 sys.stdout.flush()
227 pdf.flush()
228 if opts.verbose:
229 print 'done.'
230
231 del pdf
diff --git a/sched_trace/file.py b/sched_trace/file.py
new file mode 100644
index 0000000..5c18163
--- /dev/null
+++ b/sched_trace/file.py
@@ -0,0 +1,58 @@
1"""Convenience wrapper around mmap'ed sched_trace binary files.
2"""
3
4from .format import RECORD_LEN, unpack, get_event, EVENTS
5
6import mmap
7
8class SchedTraceFile(object):
9 def __init__(self, fname):
10 self.source = fname
11 with open(fname, 'rw') as f:
12 self.mem = mmap.mmap(f.fileno(), 0, mmap.MAP_PRIVATE)
13
14 def __len__(self):
15 return len(self.mem) / RECORD_LEN
16
17 def events_of_type(self, event_ids):
18 for i in xrange(len(self)):
19 if get_event(self.mem, i) in event_ids:
20 record = self.mem[i * RECORD_LEN:]
21 yield unpack(record)
22
23 def __iter__(self):
24 for i in xrange(len(self)):
25 record = self.mem[i * RECORD_LEN:]
26 yield unpack(record)
27
28 def __reversed__(self):
29 for i in reversed(xrange(len(self))):
30 record = self.mem[i * RECORD_LEN:]
31 yield unpack(record)
32
33 def raw_iter(self):
34 for i in xrange(len(self)):
35 record = self.mem[i * RECORD_LEN:(i + 1) * RECORD_LEN]
36 yield record
37
38 def scheduling_intervals(self):
39 to_id = EVENTS['ST_SWITCH_TO']
40 away_id = EVENTS['ST_SWITCH_AWAY']
41
42 # assumption: we are looking at a per-CPU trace
43 events = self.events_of_type(set((to_id, away_id)))
44
45 rec = events.next()
46 while True:
47 # fast-forward to next SWITCH_TO
48 while rec[0] != to_id:
49 rec = events.next()
50
51 to = rec
52 # next one on this CPU should be a SWITCH_AWAY
53 rec = events.next()
54 away = rec
55 # check for event ID and matching PID and CPU and monotonic time
56 if away[0] == away_id and away[1] == to[1] and away[2] == to[2] \
57 and to[4] <= away[4]:
58 yield (to, away)
diff --git a/sched_trace/format.py b/sched_trace/format.py
new file mode 100644
index 0000000..ca44a9a
--- /dev/null
+++ b/sched_trace/format.py
@@ -0,0 +1,118 @@
1"""Functions for reading and interpreting binary sched_trace files.
2"""
3
4import struct
5
6HEADER_LEN = 8 # bytes
7PAYLOAD_LEN = 16 # bytes
8RECORD_LEN = 24 # bytes
9
10EVENT_NAMES = [
11 "ST_NAME",
12 "ST_PARAM",
13 "ST_RELEASE",
14 "ST_ASSIGNED",
15 "ST_SWITCH_TO",
16 "ST_SWITCH_AWAY",
17 "ST_COMPLETION",
18 "ST_BLOCK",
19 "ST_RESUME",
20 "ST_ACTION",
21 "ST_SYS_RELEASE"
22]
23
24EVENTS = {}
25for i, ev in enumerate(EVENT_NAMES):
26 EVENTS[ev] = i + 1
27 EVENTS[i + 1] = ev
28
29def event(id):
30 return EVENTS[id] if id in EVENTS else id
31
32# struct st_trace_header {
33# u8 type; /* Of what type is this record? */
34# u8 cpu; /* On which CPU was it recorded? */
35# u16 pid; /* PID of the task. */
36# u32 job; /* The job sequence number. */
37# };
38
39def get_event(mmaped_trace, index):
40 offset = index * RECORD_LEN
41 # first byte in the record is the event type ID
42 return ord(mmaped_trace[offset])
43# return struct.unpack("B", raw_data[:1])[0]
44
45def unpack_header(raw_data):
46 """Returns (type, cpu, pid, job id)"""
47 return struct.unpack("BBHI", raw_data[:HEADER_LEN])
48
49def unpack_release(raw_data):
50 """Returns (release, deadline)"""
51 return struct.unpack("QQ", raw_data[HEADER_LEN:RECORD_LEN])
52
53def unpack_switch_to(raw_data):
54 """Returns (when, exec_time_so_far)"""
55 return struct.unpack("QI", raw_data[HEADER_LEN:RECORD_LEN - 4])
56
57def unpack_switch_away(raw_data):
58 """Returns (when, exec_time_so_far)"""
59 return struct.unpack("QQ", raw_data[HEADER_LEN:RECORD_LEN])
60
61def unpack_completion(raw_data):
62 """Returns (when, exec_time, forced)"""
63 (when, exec_time) = struct.unpack("QQ", raw_data[HEADER_LEN:RECORD_LEN])
64 return (when, exec_time >> 1, exec_time & 0x1)
65
66def unpack_block(raw_data):
67 """Returns (when,)"""
68 return struct.unpack("Q", raw_data[HEADER_LEN:RECORD_LEN - 8])
69
70def unpack_resume(raw_data):
71 """Returns (when,)"""
72 return struct.unpack("Q", raw_data[HEADER_LEN:RECORD_LEN - 8])
73
74def unpack_sys_release(raw_data):
75 """Returns (when, release)"""
76 return struct.unpack("QQ", raw_data[HEADER_LEN:RECORD_LEN])
77
78def unpack_name(raw_data):
79 """Returns (name,)"""
80 data = raw_data[HEADER_LEN:RECORD_LEN]
81 null_byte_idx = data.find('\0')
82 if null_byte_idx > -1:
83 data = data[:null_byte_idx]
84 return (data,)
85
86def unpack_param(raw_data):
87 """Returns (WCET, period, phase, partition)"""
88 return struct.unpack("IIIB", raw_data[HEADER_LEN:RECORD_LEN - 3])
89
90UNPACK = {
91 EVENTS['ST_RELEASE']: unpack_release,
92 EVENTS['ST_SWITCH_TO']: unpack_switch_to,
93 EVENTS['ST_SWITCH_AWAY']: unpack_switch_away,
94 EVENTS['ST_COMPLETION']: unpack_completion,
95 EVENTS['ST_BLOCK']: unpack_block,
96 EVENTS['ST_RESUME']: unpack_resume,
97 EVENTS['ST_SYS_RELEASE']: unpack_sys_release,
98 EVENTS['ST_NAME']: unpack_name,
99 EVENTS['ST_PARAM']: unpack_param,
100}
101
102EVENTS_WITHOUT_TIMESTAMP = frozenset([
103 EVENTS['ST_NAME'],
104 EVENTS['ST_PARAM'],
105])
106
107def when(raw_data):
108 ev = get_event(raw_data)
109 if ev in EVENTS_WITHOUT_TIMESTAMP:
110 return 0
111 else:
112 # always first element for all others
113 return UNPACK[ev](raw_data)[0]
114
115def unpack(raw_data):
116 hdr = unpack_header(raw_data)
117 payload = UNPACK[hdr[0]](raw_data)
118 return hdr + payload
diff --git a/st-draw b/st-draw
new file mode 100755
index 0000000..45b96f1
--- /dev/null
+++ b/st-draw
@@ -0,0 +1,142 @@
1#!/usr/bin/env python
2
3import argparse
4import sys
5import os.path
6
7import cairo
8
9from sched_trace import event_name, event_time, SchedTrace
10
11import sched_trace.draw
12
13def s2ns(secs):
14 return secs * 1E9
15
16def ms2ns(millis):
17 return millis * 1E6
18
19def ns2s(nanos):
20 return nanos / 1E9
21
22def parse_args():
23 parser = argparse.ArgumentParser(
24 description="Render a LITMUS^RT schedule trace as a PDF.")
25
26 parser.add_argument('-o', '--output',
27 help='name of the generated PDF file')
28
29 parser.add_argument('-x', '--xscale', type=float, default=36,
30 help="how many points (1/72'') per millisecond?")
31 parser.add_argument('-y', '--yscale', type=float, default=72,
32 help="how many points (1/72'') per task/core?")
33 parser.add_argument('--margin', type=float, default=36,
34 help="how many points (1/72'') of margin around the schedule?")
35
36 parser.add_argument('--major-ticks', type=float, default=10,
37 help="how many milliseconds per major tick? (zero means off)")
38 parser.add_argument('--minor-ticks', type=float, default=1,
39 help="how many milliseconds per minor tick? (zero means off)")
40
41 parser.add_argument('-f', '--from', type=float, dest='start',
42 help='draw schedule starting at time TIME', metavar='TIME')
43 parser.add_argument('-u', '--until', type=float, dest='end',
44 help='draw schedule up to time TIME', metavar='TIME')
45 parser.add_argument('-l', '--length', type=float, default=1000,
46 help='draw schedule for LENGTH time units')
47 parser.add_argument('-r', '--relative', action='store_true',
48 help='interpret -f/-u options relative to system release')
49
50 parser.add_argument('-v', '--verbose', action='store_true', default=False,
51 help='output some information while drawing')
52
53 parser.add_argument('file', nargs='*',
54 help='the binary trace files collected with st-trace')
55
56 return parser.parse_args()
57
58
59def default_output_name(input_files):
60 if not input_files:
61 return 'schedule.pdf'
62 candidate = input_files[0]
63 for i in xrange(len(candidate)):
64 if not all([f[i] == candidate[i] for f in input_files]):
65 break
66 candidate = os.path.basename(candidate[:i])
67 if candidate.endswith('_cpu='):
68 candidate = candidate.replace('_cpu=', '')
69 if not candidate:
70 return 'schedule.pdf'
71 return candidate + '.pdf'
72
73def main(args=sys.argv[1:]):
74 opts = parse_args()
75
76 if not opts.output:
77 opts.output = default_output_name(opts.file)
78
79 try:
80 trace = SchedTrace(opts.file)
81 except IOError, msg:
82 print 'Could not load trace files (%s)' % msg
83 sys.exit(1)
84
85 if opts.start:
86 # convert from ms
87 opts.start = ms2ns(opts.start)
88 # check if a relative time is given
89 if opts.relative:
90 if trace.system_releases:
91 opts.start += trace.system_releases[0]
92 else:
93 opts.start += trace.find_earliest_event
94
95 if opts.end:
96 # convert from ms
97 opts.end = ms2ns(opts.end)
98 # check if a relative time is given
99 if opts.relative:
100 if trace.system_releases:
101 opts.end += trace.system_releases[0]
102 else:
103 opts.end += trace.find_earliest_event
104
105 if not opts.start:
106 if opts.end and opts.length:
107 opts.start = opts.end - ms2ns(opts.length)
108 elif trace.system_releases:
109 opts.start = trace.system_releases[0]
110 else:
111 opts.start = trace.find_earliest_event
112
113 if not opts.end:
114 if opts.start and opts.length:
115 opts.end = opts.start + ms2ns(opts.length)
116 else:
117 opts.end = trace.latest_event_time
118
119 if opts.verbose:
120 print '[II] Rendering into file: %s' % opts.output
121 print '[II] Drawing %.2f seconds from time %.4fs until time %.4fs.' % \
122 (ns2s(opts.end - opts.start), ns2s(opts.start), ns2s(opts.end))
123 if len(trace.system_releases) == 1:
124 print '[II] The trace contains a system release at time %.4fs.' % \
125 (ns2s(trace.system_releases[0]))
126 elif len(trace.system_releases) > 1:
127 print '[!!] The trace contains multiple system releases: %s' % \
128 (' '.join(['%.4fs' % ns2s(x) for x in trace.system_releases]))
129 else:
130 print '[II] The trace does not contain a system release.'
131
132 sched_trace.draw.render(opts, trace)
133
134# for pid in trace.task_names:
135# print pid, '->', trace.task_names[pid], trace.task_wcets[pid], trace.task_periods[pid]
136
137# for (to, away) in trace.scheduling_intervals_in_range(trace.system_releases[0], trace.system_releases[0] + 1E9):
138# print to, away
139
140
141if __name__ == '__main__':
142 main()