############################################################################### # 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 = '