diff options
| author | Bjoern Brandenburg <bbb@mpi-sws.org> | 2016-03-22 15:53:55 -0400 |
|---|---|---|
| committer | Bjoern Brandenburg <bbb@mpi-sws.org> | 2016-03-22 16:00:36 -0400 |
| commit | 27474dfff59e40e1abf33e0b5dfe7a16dc73f4b1 (patch) | |
| tree | ead56b3783117e666d008498120f8de7f6627bfe | |
| parent | 17fedde91157d9e57a3c98c00c11ca03b0afcc78 (diff) | |
add st-draw: a pycairo based drawing tool for st_trace schedules
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | sched_trace/__init__.py | 202 | ||||
| -rw-r--r-- | sched_trace/draw.py | 231 | ||||
| -rw-r--r-- | sched_trace/file.py | 58 | ||||
| -rw-r--r-- | sched_trace/format.py | 118 | ||||
| -rwxr-xr-x | st-draw | 142 |
6 files changed, 754 insertions, 0 deletions
| @@ -1,6 +1,9 @@ | |||
| 1 | .sconsign.dblite | 1 | .sconsign.dblite |
| 2 | *.o | 2 | *.o |
| 3 | *.d | 3 | *.d |
| 4 | *.pyc | ||
| 5 | |||
| 6 | *.bin | ||
| 4 | ~* | 7 | ~* |
| 5 | *~ | 8 | *~ |
| 6 | ftcat | 9 | ftcat |
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 @@ | |||
| 1 | from __future__ import division | ||
| 2 | |||
| 3 | from collections import defaultdict | ||
| 4 | from itertools import imap | ||
| 5 | |||
| 6 | import heapq | ||
| 7 | |||
| 8 | from .format import event, EVENTS_WITHOUT_TIMESTAMP, EVENTS | ||
| 9 | from .file import SchedTraceFile | ||
| 10 | |||
| 11 | def event_id(unpacked_record): | ||
| 12 | "Given a tuple from format.unpack(), return the ID of the event" | ||
| 13 | return unpacked_record[0] | ||
| 14 | |||
| 15 | def event_cpu(unpacked_record): | ||
| 16 | "Given a tuple from format.unpack(), return the CPU that recorded the event" | ||
| 17 | return unpacked_record[1] | ||
| 18 | |||
| 19 | def event_pid(unpacked_record): | ||
| 20 | "Given a tuple from format.unpack(), return the process ID of the event" | ||
| 21 | return unpacked_record[2] | ||
| 22 | |||
| 23 | def 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 | |||
| 27 | def 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 | |||
| 32 | def 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 | |||
| 39 | def 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 | |||
| 51 | def 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 | |||
| 59 | class 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: | ||
