aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--parse/sched.py136
1 files changed, 79 insertions, 57 deletions
diff --git a/parse/sched.py b/parse/sched.py
index a38c61b..b56324b 100644
--- a/parse/sched.py
+++ b/parse/sched.py
@@ -7,6 +7,7 @@ import subprocess
7from collections import defaultdict,namedtuple 7from collections import defaultdict,namedtuple
8from common import recordtype 8from common import recordtype
9from point import Measurement 9from point import Measurement
10from ctypes import *
10 11
11class TimeTracker: 12class TimeTracker:
12 '''Store stats for durations of time demarcated by sched_trace records.''' 13 '''Store stats for durations of time demarcated by sched_trace records.'''
@@ -38,33 +39,30 @@ class TimeTracker:
38TaskParams = namedtuple('TaskParams', ['wcet', 'period', 'cpu']) 39TaskParams = namedtuple('TaskParams', ['wcet', 'period', 'cpu'])
39TaskData = recordtype('TaskData', ['params', 'jobs', 'blocks', 'misses']) 40TaskData = recordtype('TaskData', ['params', 'jobs', 'blocks', 'misses'])
40 41
41# Map of event ids to corresponding class, binary format, and processing methods 42# Map of event ids to corresponding class and format
42RecordInfo = namedtuple('RecordInfo', ['clazz', 'fmt', 'method']) 43record_map = {}
43record_map = [0]*10
44 44
45# Common to all records
46HEADER_FORMAT = '<bbhi'
47HEADER_FIELDS = ['type', 'cpu', 'pid', 'job']
48RECORD_SIZE = 24 45RECORD_SIZE = 24
49
50NSEC_PER_MSEC = 1000000 46NSEC_PER_MSEC = 1000000
51 47
52def register_record(name, id, method, fmt, fields): 48def register_record(id, clazz):
53 '''Create record description from @fmt and @fields and map to @id, using 49 fields = clazz.FIELDS
54 @method to process parsed record.'''
55 # Format of binary data (see python struct documentation)
56 rec_fmt = HEADER_FORMAT + fmt
57 50
58 # Corresponding field data 51 fsize = lambda fields : sum([sizeof(list(f)[1]) for f in fields])
59 rec_fields = HEADER_FIELDS + fields 52 diff = RECORD_SIZE - fsize(SchedRecord.FIELDS) - fsize(fields)
60 if "when" not in rec_fields: # Force a "when" field for everything
61 rec_fields += ["when"]
62 53
63 # Create mutable class with the given fields 54 # Create extra padding fields to make record the proper size
64 field_class = recordtype(name, list(rec_fields)) 55 # Creating one big field of c_uint64 and giving it a size of 8*diff
65 clazz = type(name, (field_class, object), {}) 56 # _shoud_ work, but doesn't. This is an uglier way of accomplishing
57 # the same goal
58 for d in range(diff):
59 fields += [("extra%d" % d, c_char)]
66 60
67 record_map[id] = RecordInfo(clazz, rec_fmt, method) 61 # Create structure with fields and methods of clazz
62 clazz2 = type("Dummy%d" % id, (LittleEndianStructure,clazz),
63 {'_fields_': SchedRecord.FIELDS + fields,
64 '_pack_' : 1})
65 record_map[id] = clazz2
68 66
69def make_iterator(fname): 67def make_iterator(fname):
70 '''Iterate over (parsed record, processing method) in a 68 '''Iterate over (parsed record, processing method) in a
@@ -74,7 +72,6 @@ def make_iterator(fname):
74 return 72 return
75 73
76 f = open(fname, 'rb') 74 f = open(fname, 'rb')
77 max_type = len(record_map)
78 75
79 while True: 76 while True:
80 data = f.read(RECORD_SIZE) 77 data = f.read(RECORD_SIZE)
@@ -84,19 +81,15 @@ def make_iterator(fname):
84 except struct.error: 81 except struct.error:
85 break 82 break
86 83
87 rdata = record_map[type_num] if type_num <= max_type else 0 84 if type_num not in record_map:
88 if not rdata:
89 continue 85 continue
90 86
91 try: 87 clazz = record_map[type_num]
92 values = struct.unpack_from(rdata.fmt, data) 88 obj = clazz()
93 except struct.error: 89 obj.fill(data)
94 continue
95
96 obj = rdata.clazz(*values)
97 90
98 if obj.job != 1: 91 if obj.job != 1:
99 yield (obj, rdata.method) 92 yield obj
100 else: 93 else:
101 # Results from the first job are nonsense 94 # Results from the first job are nonsense
102 pass 95 pass
@@ -105,55 +98,84 @@ def read_data(task_dict, fnames):
105 '''Read records from @fnames and store per-pid stats in @task_dict.''' 98 '''Read records from @fnames and store per-pid stats in @task_dict.'''
106 buff = [] 99 buff = []
107 100
101 def get_time(record):
102 return record.when if hasattr(record, 'when') else 0
103
108 def add_record(itera): 104 def add_record(itera):
109 # Ordered insertion into buff 105 # Ordered insertion into buff
110 try: 106 try:
111 next_ret = itera.next() 107 arecord = itera.next()
112 except StopIteration: 108 except StopIteration:
113 return 109 return
114 110
115 arecord, method = next_ret
116 i = 0 111 i = 0
117 for (i, (brecord, m, t)) in enumerate(buff): 112 for (i, (brecord, _)) in enumerate(buff):
118 if brecord.when > arecord.when: 113 if get_time(brecord) > get_time(arecord):
119 break 114 break
120 buff.insert(i, (arecord, method, itera)) 115 buff.insert(i, (arecord, itera))
121 116
122 for fname in fnames: 117 for fname in fnames:
123 itera = make_iterator(fname) 118 itera = make_iterator(fname)
124 add_record(itera) 119 add_record(itera)
125 120
126 while buff: 121 while buff:
127 (record, method, itera) = buff.pop(0) 122 record, itera = buff.pop(0)
128 123
129 add_record(itera) 124 add_record(itera)
130 method(task_dict, record) 125 record.process(task_dict)
126
127class SchedRecord(object):
128 # Subclasses will have their FIELDs merged into this one
129 FIELDS = [('type', c_uint8), ('cpu', c_uint8),
130 ('pid', c_uint16), ('job', c_uint32)]
131
132 def fill(self, data):
133 memmove(addressof(self), data, RECORD_SIZE)
134
135 def process(self, task_dict):
136 raise NotImplementedError()
137
138class ParamRecord(SchedRecord):
139 FIELDS = [('wcet', c_uint32), ('period', c_uint32),
140 ('phase', c_uint32), ('partition', c_uint8)]
141
142 def process(self, task_dict):
143 params = TaskParams(self.wcet, self.period, self.partition)
144 task_dict[self.pid].params = params
145
146class ReleaseRecord(SchedRecord):
147 FIELDS = [('when', c_uint64), ('release', c_uint64)]
148
149 def process(self, task_dict):
150 data = task_dict[self.pid]
151 data.jobs += 1
152 if data.params:
153 data.misses.start_time(self, self.when + data.params.period)
154
155class CompletionRecord(SchedRecord):
156 FIELDS = [('when', c_uint64)]
131 157
132def process_completion(task_dict, record): 158 def process(self, task_dict):
133 task_dict[record.pid].misses.store_time(record) 159 task_dict[self.pid].misses.store_time(self)
134 160
135def process_release(task_dict, record): 161class BlockRecord(SchedRecord):
136 data = task_dict[record.pid] 162 FIELDS = [('when', c_uint64)]
137 data.jobs += 1
138 if data.params:
139 data.misses.start_time(record, record.when + data.params.period)
140 163
141def process_param(task_dict, record): 164 def process(self, task_dict):
142 params = TaskParams(record.wcet, record.period, record.partition) 165 task_dict[self.pid].blocks.start_time(self)
143 task_dict[record.pid].params = params
144 166
145def process_block(task_dict, record): 167class ResumeRecord(SchedRecord):
146 task_dict[record.pid].blocks.start_time(record) 168 FIELDS = [('when', c_uint64)]
147 169
148def process_resume(task_dict, record): 170 def process(self, task_dict):
149 task_dict[record.pid].blocks.store_time(record) 171 task_dict[self.pid].blocks.store_time(self)
150 172
151register_record('ResumeRecord', 9, process_resume, 'Q8x', ['when']) 173# Map records to sched_trace ids (see include/litmus/sched_trace.h
152register_record('BlockRecord', 8, process_block, 'Q8x', ['when']) 174register_record(2, ParamRecord)
153register_record('CompletionRecord', 7, process_completion, 'Q8x', ['when']) 175register_record(3, ReleaseRecord)
154register_record('ReleaseRecord', 3, process_release, 'QQ', ['when', 'release']) 176register_record(7, CompletionRecord)
155register_record('ParamRecord', 2, process_param, 'IIIcc2x', 177register_record(8, BlockRecord)
156 ['wcet','period','phase','partition', 'task_class']) 178register_record(9, ResumeRecord)
157 179
158def create_task_dict(data_dir, work_dir = None): 180def create_task_dict(data_dir, work_dir = None):
159 '''Parse sched trace files''' 181 '''Parse sched trace files'''