1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
###############################################################################
# Description
###############################################################################
# trace_reader(files) returns an iterator which produces records
# OUT OF ORDER from the files given. (the param is a list of files.)
#
# The non-naive trace_reader has a lot of complex logic which attempts to
# produce records in order (even though they are being pulled from multiple
# files which themselves are only approximately ordered). This trace_reader
# attempts to be as simple as possible and is used in the unit tests to
# make sure the total number of records read by the normal trace_reader is
# the same as the number of records read by this one.
###############################################################################
# Imports
###############################################################################
import struct
###############################################################################
# Public functions
###############################################################################
# Generator function returning an iterable over records in a trace file.
def trace_reader(files):
for file in files:
with open(file,'rb') as f:
while True:
data = f.read(RECORD_HEAD_SIZE)
try:
type_num = struct.unpack_from('b',data)[0]
except struct.error:
break #We read to the end of the file
type = _get_type(type_num)
try:
values = struct.unpack_from(StHeader.format +
type.format,data)
record_dict = dict(zip(type.keys,values))
except struct.error:
f.close()
print("Invalid record detected, stopping.")
exit()
# Convert the record_dict into an object
record = _dict2obj(record_dict)
# Give it a type name (easier to work with than type number)
record.type_name = _get_type_name(type_num)
# All records should have a 'record type' field.
# e.g. these are 'event's as opposed to 'error's
record.record_type = "event"
# If there is no timestamp, set the time to 0
if 'when' not in record.__dict__.keys():
record.when = 0
yield record
###############################################################################
# Private functions
###############################################################################
# Convert a dict into an object
def _dict2obj(d):
class Obj: pass
o = Obj()
for key in d.keys():
o.__dict__[key] = d[key]
return o
###############################################################################
# Trace record data types and accessor functions
###############################################################################
# Each class below represents a type of event record. The format attribute
# specifies how to decode the binary record and the keys attribute
# specifies how to name the pieces of information decoded. Note that all
# event records have a common initial 24 bytes, represented by the StHeader
# class.
RECORD_HEAD_SIZE = 24
class StHeader:
format = '<bbhi'
formatStr = struct.Struct(format)
keys = ['type','cpu','pid','job']
message = 'The header.'
class StNameData:
format = '16s'
formatStr = struct.Struct(StHeader.format + format)
keys = StHeader.keys + ['name']
message = 'The name of the executable of this process.'
class StParamData:
format = 'IIIc'
formatStr = struct.Struct(StHeader.format + format)
keys = StHeader.keys + ['wcet','period','phase','partition']
message = 'Regular parameters.'
class StReleaseData:
format = 'QQ'
formatStr = struct.Struct(StHeader.format + format)
keys = StHeader.keys + ['when','deadline']
message = 'A job was/is going to be released.'
#Not yet used by Sched Trace
class StAssignedData:
format = 'Qc'
formatStr = struct.Struct(StHeader.format + format)
keys = StHeader.keys + ['when','target']
message = 'A job was assigned to a CPU.'
class StSwitchToData:
format = 'QI'
formatStr = struct.Struct(StHeader.format + format)
keys = StHeader.keys + ['when','exec_time']
message = 'A process was switched to on a given CPU.'
class StSwitchAwayData:
format = 'QI'
formatStr = struct.Struct(StHeader.format + format)
keys = StHeader.keys + ['when','exec_time']
message = 'A process was switched away on a given CPU.'
class StCompletionData:
format = 'Q3x?c'
formatStr = struct.Struct(StHeader.format + format)
keys = StHeader.keys + ['when','forced?','flags']
message = 'A job completed.'
class StBlockData:
format = 'Q'
formatStr = struct.Struct(StHeader.format + format)
keys = StHeader.keys + ['when']
message = 'A task blocks.'
class StResumeData:
format = 'Q'
formatStr = struct.Struct(StHeader.format + format)
keys = StHeader.keys + ['when']
message = 'A task resumes.'
class StSysReleaseData:
format = 'QQ'
formatStr = struct.Struct(StHeader.format + format)
keys = StHeader.keys + ['when','release']
message = 'All tasks have checked in, task system released by user'
# Return the binary data type, given the type_num
def _get_type(type_num):
types = [None,StNameData,StParamData,StReleaseData,StAssignedData,
StSwitchToData,StSwitchAwayData,StCompletionData,StBlockData,
StResumeData,StSysReleaseData]
return types[type_num]
# Return the type name, given the type_num (this is simply a convenience to
# programmers of other modules)
def _get_type_name(type_num):
type_names = [None,"name","params","release","assign","switch_to",
"switch_away","completion","block","resume","sys_release"]
return type_names[type_num]
|