summaryrefslogtreecommitdiffstats
path: root/unit_trace/trace_reader.py
diff options
context:
space:
mode:
authorMac Mollison <mollison@cs.unc.edu>2010-03-13 12:12:37 -0500
committerMac Mollison <mollison@cs.unc.edu>2010-03-13 12:12:37 -0500
commit122f457226f54ad23b7cd138512502e430e704dc (patch)
treefee0690936c3ae95255e559cd0fd09f0fa8c2ad4 /unit_trace/trace_reader.py
parent14a40b99735f09f6e70b8e897acbb622f9115ca3 (diff)
Further restructuring to create 'unit_trace' pkg
The unit_trace folder should be placed in /usr/local/lib/pythonX.Y/site-packages. This makes unit-trace submodules available from anywhere on the system.
Diffstat (limited to 'unit_trace/trace_reader.py')
-rw-r--r--unit_trace/trace_reader.py245
1 files changed, 245 insertions, 0 deletions
diff --git a/unit_trace/trace_reader.py b/unit_trace/trace_reader.py
new file mode 100644
index 0000000..a4ff964
--- /dev/null
+++ b/unit_trace/trace_reader.py
@@ -0,0 +1,245 @@
1###############################################################################
2# Description
3###############################################################################
4
5# trace_reader(files) returns an iterator which produces records
6# in order from the files given. (the param is a list of files.)
7#
8# Each record is just a Python object. It is guaranteed to have the following
9# attributes:
10# - 'pid': pid of the task
11# - 'job': job number for that task
12# - 'cpu', given by LITMUS
13# - 'when', given by LITMUS as a timestamp. LITMUS does not provide a
14# timestamp for all records. In this case, when is set to 0.
15# - 'type', a numerical value given by LITMUS
16# - 'type_name', a human-readable name defined in this module
17# - 'record_type', set to 'event' by this module (to distinguish from, e.g.,
18# error records produced elsewhere).
19# - Possible additional attributes, depending on the type of record.
20#
21# To find out exactly what attributes are set for each record type, look at
22# the trace-parsing information at the bottom of this file.
23
24###############################################################################
25# Imports
26###############################################################################
27
28import struct
29
30
31###############################################################################
32# Public functions
33###############################################################################
34
35# Generator function returning an iterable over records in a trace file.
36def trace_reader(files):
37
38 # Yield a record indicating the number of CPUs, used by the G-EDF test
39 class Obj: pass
40 record = Obj()
41 record.record_type = "meta"
42 record.type_name = "num_cpus"
43 record.num_cpus = len(files)
44 yield record
45
46 # Create iterators for each file and a buffer to store records in
47 file_iters = [] # file iterators
48 file_iter_buff = [] # file iterator buffers
49 for file in files:
50 file_iter = _get_file_iter(file)
51 file_iters.append(file_iter)
52 file_iter_buff.append([file_iter.next()])
53
54 # We keep 100 records in each buffer and then keep the buffer sorted
55 # This is because records may have been recorded slightly out of order
56 # This cannot guarantee records are produced in order, but it makes it
57 # overwhelmingly probably.
58 for x in range(0,len(file_iter_buff)):
59 for y in range(0,100):
60 file_iter_buff[x].append(file_iters[x].next())
61 for x in range(0,len(file_iter_buff)):
62 file_iter_buff[x] = sorted(file_iter_buff[x],key=lambda rec: rec.when)
63
64 # Remember the time of the last record. This way, we can make sure records
65 # truly are produced in monotonically increasing order by time and terminate
66 # fatally if they are not.
67 last_time = None
68
69 # Keep pulling records as long as we have a buffer
70 while len(file_iter_buff) > 0:
71
72 # Select the earliest record from those at the heads of the buffers
73 earliest = -1
74 buff_to_refill = -1
75 for x in range(0,len(file_iter_buff)):
76 if earliest==-1 or file_iter_buff[x][0].when < earliest.when:
77 earliest = file_iter_buff[x][0]
78 buff_to_refill = x
79
80 # Take it out of the buffer
81 del file_iter_buff[buff_to_refill][0]
82
83 # Try to append a new record to the buffer (if there is another) and
84 # then keep the buffer sorted
85 try:
86 file_iter_buff[buff_to_refill].append(file_iters[buff_to_refill].next())
87 file_iter_buff[buff_to_refill] = sorted(file_iter_buff[buff_to_refill],
88 key=lambda rec: rec.when)
89
90 # If there aren't any more records, fine. Unless the buffer is also empty.
91 # If that is the case, delete the buffer.
92 except StopIteration:
93 if len(file_iter_buff[buff_to_refill]) < 1:
94 del file_iter_buff[buff_to_refill]
95 del file_iters[buff_to_refill]
96
97 # Check for monotonically increasing time
98 if last_time is not None and earliest.when < last_time:
99 exit("FATAL ERROR: trace_reader.py: out-of-order record produced")
100 else:
101 last_time = earliest.when
102
103 # Yield the record
104 yield earliest
105
106###############################################################################
107# Private functions
108###############################################################################
109
110# Returns an iterator to pull records from a file
111def _get_file_iter(file):
112 f = open(file,'rb')
113 while True:
114 data = f.read(RECORD_HEAD_SIZE)
115 try:
116 type_num = struct.unpack_from('b',data)[0]
117 except struct.error:
118 break #We read to the end of the file
119 type = _get_type(type_num)
120 try:
121 values = struct.unpack_from(StHeader.format +
122 type.format,data)
123 record_dict = dict(zip(type.keys,values))
124 except struct.error:
125 f.close()
126 print "Invalid record detected, stopping."
127 exit()
128
129 # Convert the record_dict into an object
130 record = _dict2obj(record_dict)
131
132 # Give it a type name (easier to work with than type number)
133 record.type_name = _get_type_name(type_num)
134
135 # All records should have a 'record type' field.
136 # e.g. these are 'event's as opposed to 'error's
137 record.record_type = "event"
138
139 # If there is no timestamp, set the time to 0
140 if 'when' not in record.__dict__.keys():
141 record.when = 0
142
143 yield record
144
145# Convert a dict into an object
146def _dict2obj(d):
147 class Obj(object): pass
148 o = Obj()
149 for key in d.keys():
150 o.__dict__[key] = d[key]
151 return o
152
153###############################################################################
154# Trace record data types and accessor functions
155###############################################################################
156
157# Each class below represents a type of event record. The format attribute
158# specifies how to decode the binary record and the keys attribute
159# specifies how to name the pieces of information decoded. Note that all
160# event records have a common initial 24 bytes, represented by the StHeader
161# class.
162
163RECORD_HEAD_SIZE = 24
164
165class StHeader:
166 format = '<bbhi'
167 formatStr = struct.Struct(format)
168 keys = ['type','cpu','pid','job']
169 message = 'The header.'
170
171class StNameData:
172 format = '16s'
173 formatStr = struct.Struct(StHeader.format + format)
174 keys = StHeader.keys + ['name']
175 message = 'The name of the executable of this process.'
176
177class StParamData:
178 format = 'IIIc'
179 formatStr = struct.Struct(StHeader.format + format)
180 keys = StHeader.keys + ['wcet','period','phase','partition']
181 message = 'Regular parameters.'
182
183class StReleaseData:
184 format = 'QQ'
185 formatStr = struct.Struct(StHeader.format + format)
186 keys = StHeader.keys + ['when','deadline']
187 message = 'A job was/is going to be released.'
188
189#Not yet used by Sched Trace
190class StAssignedData:
191 format = 'Qc'
192 formatStr = struct.Struct(StHeader.format + format)
193 keys = StHeader.keys + ['when','target']
194 message = 'A job was assigned to a CPU.'
195
196class StSwitchToData:
197 format = 'QI'
198 formatStr = struct.Struct(StHeader.format + format)
199 keys = StHeader.keys + ['when','exec_time']
200 message = 'A process was switched to on a given CPU.'
201
202class StSwitchAwayData:
203 format = 'QI'
204 formatStr = struct.Struct(StHeader.format + format)
205 keys = StHeader.keys + ['when','exec_time']
206 message = 'A process was switched away on a given CPU.'
207
208class StCompletionData:
209 #format = 'Q3x?c'
210 format = 'Q3xcc'
211 formatStr = struct.Struct(StHeader.format + format)
212 keys = StHeader.keys + ['when','forced?','flags']
213 message = 'A job completed.'
214
215class StBlockData:
216 format = 'Q'
217 formatStr = struct.Struct(StHeader.format + format)
218 keys = StHeader.keys + ['when']
219 message = 'A task blocks.'
220
221class StResumeData:
222 format = 'Q'
223 formatStr = struct.Struct(StHeader.format + format)
224 keys = StHeader.keys + ['when']
225 message = 'A task resumes.'
226
227class StSysReleaseData:
228 format = 'QQ'
229 formatStr = struct.Struct(StHeader.format + format)
230 keys = StHeader.keys + ['when','release']
231 message = 'All tasks have checked in, task system released by user'
232
233# Return the binary data type, given the type_num
234def _get_type(type_num):
235 types = [None,StNameData,StParamData,StReleaseData,StAssignedData,
236 StSwitchToData,StSwitchAwayData,StCompletionData,StBlockData,
237 StResumeData,StSysReleaseData]
238 return types[type_num]
239
240# Return the type name, given the type_num (this is simply a convenience to
241# programmers of other modules)
242def _get_type_name(type_num):
243 type_names = [None,"name","params","release","assign","switch_to",
244 "switch_away","completion","block","resume","sys_release"]
245 return type_names[type_num]