From: Philippe Proulx Date: Sat, 8 Nov 2014 08:54:50 +0000 (-0500) Subject: Create generator class, fix a few things X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=14e69d6b904a7b1f1f49227176e29035a58a917d;p=deliverable%2Fbarectf.git Create generator class, fix a few things --- diff --git a/barectf/cli.py b/barectf/cli.py index 20794a2..a0c65f0 100644 --- a/barectf/cli.py +++ b/barectf/cli.py @@ -72,717 +72,755 @@ def _parse_args(): return args -# TODO: prettify this function -def _validate_struct(struct): - # just in case we call this with the wrong type - if type(struct) is not pytsdl.tsdl.Struct: - raise RuntimeError('expecting a struct') - - # make sure inner structures are at least byte-aligned - if _get_obj_alignment(struct) < 8: - raise RuntimeError('inner struct must be at least byte-aligned') - - # check each field - for name, ftype in struct.fields.items(): - if type(ftype) is pytsdl.tsdl.Sequence: - raise RuntimeError('field "{}" is a dynamic array (not allowed here)'.format(name)) - elif type(ftype) is pytsdl.tsdl.Array: - # we need to check every element type until we find a terminal one - end = False - element = ftype.element - - while not end: - if type(element) is pytsdl.tsdl.Sequence: - raise RuntimeError('field "{}" contains a dynamic array (not allowed here)'.format(name)) - elif type(element) is pytsdl.tsdl.Variant: - raise RuntimeError('field "{}" contains a variant (unsupported)'.format(name)) - elif type(element) is pytsdl.tsdl.String: - raise RuntimeError('field "{}" contains a string (not allowed here)'.format(name)) - elif type(element) is pytsdl.tsdl.Struct: - _validate_struct(element) - elif type(element) is pytsdl.tsdl.Integer: - if _get_integer_size(element) > 64: - raise RuntimeError('integer field "{}" larger than 64-bit'.format(name)) - elif type(element) is pytsdl.tsdl.FloatingPoint: - if _get_floating_point_size(element) > 64: - raise RuntimeError('floating point field "{}" larger than 64-bit'.format(name)) - elif type(element) is pytsdl.tsdl.Enum: - if _get_enum_size(element) > 64: - raise RuntimeError('enum field "{}" larger than 64-bit'.format(name)) - - if type(element) is pytsdl.tsdl.Array: - # still an array, continue - element = element.element - else: - # found the terminal element - end = True - elif type(ftype) is pytsdl.tsdl.Variant: - raise RuntimeError('field "{}" is a variant (unsupported)'.format(name)) - elif type(ftype) is pytsdl.tsdl.String: - raise RuntimeError('field "{}" is a string (not allowed here)'.format(name)) - elif type(ftype) is pytsdl.tsdl.Struct: - _validate_struct(ftype) - elif type(ftype) is pytsdl.tsdl.Integer: - if _get_integer_size(ftype) > 64: - raise RuntimeError('integer field "{}" larger than 64-bit'.format(name)) - elif type(ftype) is pytsdl.tsdl.FloatingPoint: - if _get_floating_point_size(ftype) > 64: - raise RuntimeError('floating point field "{}" larger than 64-bit'.format(name)) - elif type(ftype) is pytsdl.tsdl.Enum: - if _get_enum_size(ftype) > 64: - raise RuntimeError('enum field "{}" larger than 64-bit'.format(name)) - - -def _validate_context_field(struct): - if type(struct) is not pytsdl.tsdl.Struct: - raise RuntimeError('expecting a struct') - - for name, ftype in struct.fields.items(): - if type(ftype) is pytsdl.tsdl.Variant: - raise RuntimeError('field "{}" is a variant (unsupported)'.format(name)) - elif type(ftype) is pytsdl.tsdl.Struct: - # validate inner structure against barectf constraints - _validate_struct(ftype) - - -def _validate_integer(integer, size=None, align=None, signed=None): - if type(integer) is not pytsdl.tsdl.Integer: - raise RuntimeError('expected integer') - - if size is not None: - if integer.size != size: - raise RuntimeError('expected {}-bit integer'.format(size)) - - if align is not None: - if integer.align != align: - raise RuntimeError('expected integer with {}-bit alignment'.format(align)) - - if signed is not None: - if integer.signed != signed: - raise RuntimeError('expected {} integer'.format('signed' if signed else 'unsigned')) +class _CBlock(list): + pass -def _validate_packet_header(packet_header): - try: - _validate_struct(packet_header) - except RuntimeError as e: - _perror('packet header: {}'.format(e)) +class _CLine(str): + pass - # magic must be the first field - if 'magic' in packet_header.fields: - if list(packet_header.fields.keys())[0] != 'magic': - _perror('packet header: "magic" must be the first field') - else: - _perror('packet header: missing "magic" field') - # magic must be a 32-bit unsigned integer, 32-bit aligned - try: - _validate_integer(packet_header['magic'], 32, 32, False) - except RuntimeError as e: - _perror('packet header: "magic": {}'.format(e)) +class BarectfCodeGenerator: + _CTX_AT = 'ctx->at' + _CTX_BUF = 'ctx->buf' + _CTX_BUF_SIZE = 'ctx->buf_size' + _CTX_BUF_AT = '{}[{} >> 3]'.format(_CTX_BUF, _CTX_AT) + _CTX_BUF_AT_ADDR = '&{}'.format(_CTX_BUF_AT) + + _bo_suffixes_map = { + pytsdl.tsdl.ByteOrder.BE: 'be', + pytsdl.tsdl.ByteOrder.LE: 'le', + } + + _tsdl_type_names_map = { + pytsdl.tsdl.Integer: 'integer', + pytsdl.tsdl.FloatingPoint: 'floating point', + pytsdl.tsdl.Enum: 'enumeration', + pytsdl.tsdl.String: 'string', + pytsdl.tsdl.Array: 'static array', + pytsdl.tsdl.Sequence: 'dynamic array', + pytsdl.tsdl.Struct: 'structure', + } + + def __init__(self): + self._parser = pytsdl.parser.Parser() + self._obj_size_cb = { + pytsdl.tsdl.Struct: self._get_struct_size, + pytsdl.tsdl.Integer: self._get_integer_size, + pytsdl.tsdl.Enum: self._get_enum_size, + pytsdl.tsdl.FloatingPoint: self._get_floating_point_size, + pytsdl.tsdl.Array: self._get_array_size, + } + self._obj_alignment_cb = { + pytsdl.tsdl.Struct: self._get_struct_alignment, + pytsdl.tsdl.Integer: self._get_integer_alignment, + pytsdl.tsdl.Enum: self._get_enum_alignment, + pytsdl.tsdl.FloatingPoint: self._get_floating_point_alignment, + pytsdl.tsdl.Array: self._get_array_alignment, + pytsdl.tsdl.Sequence: self._get_sequence_alignment, + pytsdl.tsdl.String: self._get_string_alignment, + } + self._obj_param_ctype_cb = { + pytsdl.tsdl.Struct: lambda obj: 'const void*', + pytsdl.tsdl.Integer: self._get_integer_param_ctype, + pytsdl.tsdl.Enum: self._get_enum_param_ctype, + pytsdl.tsdl.FloatingPoint: self._get_floating_point_param_ctype, + pytsdl.tsdl.Array: lambda obj: 'const void*', + pytsdl.tsdl.Sequence: lambda obj: 'const void*', + pytsdl.tsdl.String: lambda obj: 'const char*', + } + self._write_field_obj_cb = { + pytsdl.tsdl.Struct: self._write_field_struct, + pytsdl.tsdl.Integer: self._write_field_integer, + pytsdl.tsdl.Enum: self._write_field_enum, + pytsdl.tsdl.FloatingPoint: self._write_field_floating_point, + pytsdl.tsdl.Array: self._write_field_array, + pytsdl.tsdl.Sequence: self._write_field_sequence, + pytsdl.tsdl.String: self._write_field_string, + } + + # TODO: prettify this function + def _validate_struct(self, struct): + # just in case we call this with the wrong type + if type(struct) is not pytsdl.tsdl.Struct: + raise RuntimeError('expecting a struct') + + # make sure inner structures are at least byte-aligned + if self._get_obj_alignment(struct) < 8: + raise RuntimeError('inner struct must be at least byte-aligned') + + # check each field + for name, ftype in struct.fields.items(): + if type(ftype) is pytsdl.tsdl.Sequence: + raise RuntimeError('field "{}" is a dynamic array (not allowed here)'.format(name)) + elif type(ftype) is pytsdl.tsdl.Array: + # we need to check every element until we find a terminal one + element = ftype.element + + while True: + if type(element) is pytsdl.tsdl.Sequence: + raise RuntimeError('field "{}" contains a dynamic array (not allowed here)'.format(name)) + elif type(element) is pytsdl.tsdl.Variant: + raise RuntimeError('field "{}" contains a variant (unsupported)'.format(name)) + elif type(element) is pytsdl.tsdl.String: + raise RuntimeError('field "{}" contains a string (not allowed here)'.format(name)) + elif type(element) is pytsdl.tsdl.Struct: + _validate_struct(element) + elif type(element) is pytsdl.tsdl.Integer: + if self._get_obj_size(element) > 64: + raise RuntimeError('integer field "{}" larger than 64-bit'.format(name)) + elif type(element) is pytsdl.tsdl.FloatingPoint: + if self._get_obj_size(element) > 64: + raise RuntimeError('floating point field "{}" larger than 64-bit'.format(name)) + elif type(element) is pytsdl.tsdl.Enum: + if self._get_obj_size(element) > 64: + raise RuntimeError('enum field "{}" larger than 64-bit'.format(name)) + + if type(element) is pytsdl.tsdl.Array: + # still an array, continue + element = element.element + else: + # found the terminal element + break + elif type(ftype) is pytsdl.tsdl.Variant: + raise RuntimeError('field "{}" is a variant (unsupported)'.format(name)) + elif type(ftype) is pytsdl.tsdl.String: + raise RuntimeError('field "{}" is a string (not allowed here)'.format(name)) + elif type(ftype) is pytsdl.tsdl.Struct: + _validate_struct(ftype) + elif type(ftype) is pytsdl.tsdl.Integer: + if self._get_obj_size(ftype) > 64: + raise RuntimeError('integer field "{}" larger than 64-bit'.format(name)) + elif type(ftype) is pytsdl.tsdl.FloatingPoint: + if self._get_obj_size(ftype) > 64: + raise RuntimeError('floating point field "{}" larger than 64-bit'.format(name)) + elif type(ftype) is pytsdl.tsdl.Enum: + if self._get_obj_size(ftype) > 64: + raise RuntimeError('enum field "{}" larger than 64-bit'.format(name)) + + def _validate_context_field(self, struct): + if type(struct) is not pytsdl.tsdl.Struct: + raise RuntimeError('expecting a struct') + + for name, ftype in struct.fields.items(): + if type(ftype) is pytsdl.tsdl.Variant: + raise RuntimeError('field "{}" is a variant (unsupported)'.format(name)) + elif type(ftype) is pytsdl.tsdl.Struct: + # validate inner structure against barectf constraints + self._validate_struct(ftype) + + def _validate_integer(self, integer, size=None, align=None, + signed=None): + if type(integer) is not pytsdl.tsdl.Integer: + raise RuntimeError('expected integer') + + if size is not None: + if integer.size != size: + raise RuntimeError('expected {}-bit integer'.format(size)) + + if align is not None: + if integer.align != align: + raise RuntimeError('expected integer with {}-bit alignment'.format(align)) + + if signed is not None: + if integer.signed != signed: + raise RuntimeError('expected {} integer'.format('signed' if signed else 'unsigned')) + + def _validate_packet_header(self, packet_header): + try: + self._validate_struct(packet_header) + except RuntimeError as e: + _perror('packet header: {}'.format(e)) - # mandatory stream_id - if 'stream_id' not in packet_header.fields: - _perror('packet header: missing "stream_id" field') + # magic must be the first field + if 'magic' in packet_header.fields: + if list(packet_header.fields.keys())[0] != 'magic': + _perror('packet header: "magic" must be the first field') + else: + _perror('packet header: missing "magic" field') - # stream_id must be an unsigned integer - try: - _validate_integer(packet_header['stream_id'], signed=False) - except RuntimeError as e: - _perror('packet header: "stream_id": {}'.format(e)) + # magic must be a 32-bit unsigned integer, 32-bit aligned + try: + self._validate_integer(packet_header['magic'], 32, 32, False) + except RuntimeError as e: + _perror('packet header: "magic": {}'.format(e)) + # mandatory stream_id + if 'stream_id' not in packet_header.fields: + _perror('packet header: missing "stream_id" field') -def _dot_name_to_str(name): - return '.'.join(name) + # stream_id must be an unsigned integer + try: + self._validate_integer(packet_header['stream_id'], signed=False) + except RuntimeError as e: + _perror('packet header: "stream_id": {}'.format(e)) + def _dot_name_to_str(self, name): + return '.'.join(name) -def _compare_integers(int1, int2): - if type(int1) is not pytsdl.tsdl.Integer: - return False - if type(int2) is not pytsdl.tsdl.Integer: - return False + def _compare_integers(self, int1, int2): + if type(int1) is not pytsdl.tsdl.Integer: + return False - size = int1.size == int2.size - align = int1.align == int2.align - cmap = int1.map == int2.map - base = int1.base == int2.base - encoding = int1.encoding == int2.encoding - signed = int1.signed == int2.signed - comps = (size, align, cmap, base, encoding, signed) + if type(int2) is not pytsdl.tsdl.Integer: + return False - # True means 1 for sum() - return sum(comps) == len(comps) + size = int1.size == int2.size + align = int1.align == int2.align + cmap = int1.map == int2.map + base = int1.base == int2.base + encoding = int1.encoding == int2.encoding + signed = int1.signed == int2.signed + comps = (size, align, cmap, base, encoding, signed) + # True means 1 for sum() + return sum(comps) == len(comps) -def _validate_packet_context(doc, stream): - packet_context = stream.packet_context - sid = stream.id + def _validate_packet_context(self, stream): + packet_context = stream.packet_context + sid = stream.id - try: - _validate_struct(packet_context) - except RuntimeError as e: - _perror('stream {}: packet context: {}'.format(sid, e)) + try: + self._validate_struct(packet_context) + except RuntimeError as e: + _perror('stream {}: packet context: {}'.format(sid, e)) - fields = packet_context.fields + fields = packet_context.fields - # if timestamp_begin exists, timestamp_end must exist - if 'timestamp_begin' in fields or 'timestamp_end' in fields: - if 'timestamp_begin' not in fields or 'timestamp_end' not in fields: - _perror('stream {}: packet context: "timestamp_begin" must exist if "timestamp_end" exists'.format(sid)) - else: - # timestamp_begin and timestamp_end must have the same integer - # as the event header's timestamp field (should exist by now) - timestamp = stream.event_header['timestamp'] + # if timestamp_begin exists, timestamp_end must exist + if 'timestamp_begin' in fields or 'timestamp_end' in fields: + if 'timestamp_begin' not in fields or 'timestamp_end' not in fields: + _perror('stream {}: packet context: "timestamp_begin" must exist if "timestamp_end" exists'.format(sid)) + else: + # timestamp_begin and timestamp_end must have the same integer + # as the event header's timestamp field (should exist by now) + timestamp = stream.event_header['timestamp'] - if not _compare_integers(fields['timestamp_begin'], timestamp): - _perror('stream {}: packet context: "timestamp_begin": integer type different from event header\'s "timestamp" field'.format(sid)) + if not self._compare_integers(fields['timestamp_begin'], timestamp): + _perror('stream {}: packet context: "timestamp_begin": integer type different from event header\'s "timestamp" field'.format(sid)) - if not _compare_integers(fields['timestamp_end'], timestamp): - _perror('stream {}: packet context: "timestamp_end": integer type different from event header\'s "timestamp" field'.format(sid)) + if not self._compare_integers(fields['timestamp_end'], timestamp): + _perror('stream {}: packet context: "timestamp_end": integer type different from event header\'s "timestamp" field'.format(sid)) - # content_size must exist and be an unsigned integer - if 'content_size' not in fields: - _perror('stream {}: packet context: missing "content_size" field'.format(sid)) + # content_size must exist and be an unsigned integer + if 'content_size' not in fields: + _perror('stream {}: packet context: missing "content_size" field'.format(sid)) - try: - _validate_integer(fields['content_size'], 32, 32, False) - except: try: - _validate_integer(fields['content_size'], 64, 64, False) + self._validate_integer(fields['content_size'], 32, 32, False) except: - _perror('stream {}: packet context: "content_size": expecting unsigned 32-bit/64-bit integer'.format(sid)) + try: + self._validate_integer(fields['content_size'], 64, 64, False) + except: + _perror('stream {}: packet context: "content_size": expecting unsigned 32-bit/64-bit integer'.format(sid)) - # packet_size must exist and be an unsigned integer - if 'packet_size' not in fields: - _perror('stream {}: packet context: missing "packet_size" field'.format(sid)) + # packet_size must exist and be an unsigned integer + if 'packet_size' not in fields: + _perror('stream {}: packet context: missing "packet_size" field'.format(sid)) - try: - _validate_integer(fields['packet_size'], 32, 32, False) - except: try: - _validate_integer(fields['packet_size'], 64, 64, False) + self._validate_integer(fields['packet_size'], 32, 32, False) except: - _perror('stream {}: packet context: "packet_size": expecting unsigned 32-bit/64-bit integer'.format(sid)) + try: + self._validate_integer(fields['packet_size'], 64, 64, False) + except: + _perror('stream {}: packet context: "packet_size": expecting unsigned 32-bit/64-bit integer'.format(sid)) + + # if cpu_id exists, must be an unsigned integer + if 'cpu_id' in fields: + try: + self._validate_integer(fields['cpu_id'], signed=False) + except RuntimeError as e: + _perror('stream {}: packet context: "cpu_id": {}'.format(sid, e)) + + def _validate_event_header(self, stream): + event_header = stream.event_header + sid = stream.id - # if cpu_id exists, must be an unsigned integer - if 'cpu_id' in fields: try: - _validate_integer(fields['cpu_id'], signed=False) + self._validate_struct(event_header) except RuntimeError as e: - _perror('stream {}: packet context: "cpu_id": {}'.format(sid, e)) - - -def _validate_event_header(doc, stream): - event_header = stream.event_header - sid = stream.id - - try: - _validate_struct(event_header) - except RuntimeError as e: - _perror('stream {}: event header: {}'.format(sid, e)) - - fields = event_header.fields - - # id must exist and be an unsigned integer - if 'id' not in fields: - _perror('stream {}: event header: missing "id" field'.format(sid)) + _perror('stream {}: event header: {}'.format(sid, e)) - try: - _validate_integer(fields['id'], signed=False) - except RuntimeError as e: - _perror('stream {}: "id": {}'.format(sid, format(e))) + fields = event_header.fields + # id must exist and be an unsigned integer + if 'id' not in fields: + _perror('stream {}: event header: missing "id" field'.format(sid)) - # timestamp must exist, be an unsigned integer and be mapped to a valid clock - if 'timestamp' not in fields: - _perror('stream {}: event header: missing "timestamp" field'.format(sid)) - - try: - _validate_integer(fields['timestamp'], signed=False) - except RuntimeError as e: - _perror('stream {}: "timestamp": {}'.format(sid, format(e))) - - -def _validate_stream_event_context(doc, stream): - stream_event_context = stream.event_context - sid = stream.id - - if stream_event_context is None: - return - - try: - _validate_context_field(stream_event_context) - except RuntimeError as e: - _perror('stream {}: event context: {}'.format(sid, e)) - - -def _validate_all_scopes(doc): - # packet header - _validate_packet_header(doc.trace.packet_header) - - # stream stuff - for stream_id, stream in doc.streams.items(): - _validate_event_header(doc, stream) - _validate_packet_context(doc, stream) - _validate_stream_event_context(doc, stream) - - -def _validate_metadata(doc): - _validate_all_scopes(doc) - - -# 3, 4 -> 4 -# 4, 4 -> 4 -# 5, 4 -> 8 -# 6, 4 -> 8 -# 7, 4 -> 8 -# 8, 4 -> 8 -# 9, 4 -> 12 -def _get_alignment(at, align): - return (at + align - 1) & -align - - -def _offset_vars_tree_to_vars(offset_vars_tree, prefix='', - offset_vars=collections.OrderedDict()): - for name, offset in offset_vars_tree.items(): - varname = '{}_{}'.format(prefix, name) - - if isinstance(offset, dict): - _offset_vars_tree_to_vars(offset, varname, offset_vars) - else: - offset_vars[varname] = offset - - return offset_vars - - -# returns the size of a struct with _static size_ -def _get_struct_size(struct, offset_vars_tree=collections.OrderedDict(), - base_offset=0): - offset = 0 - - for fname, ftype in struct.fields.items(): - field_alignment = _get_obj_alignment(ftype) - offset = _get_alignment(offset, field_alignment) - - if type(ftype) is pytsdl.tsdl.Struct: - offset_vars_tree[fname] = collections.OrderedDict() - sz = _get_struct_size(ftype, offset_vars_tree[fname], - base_offset + offset) - else: - # only integers may act as sequence lengths - if type(ftype) is pytsdl.tsdl.Integer: - offset_vars_tree[fname] = base_offset + offset - - sz = _get_obj_size(ftype) - - offset += sz - - return offset - + try: + self._validate_integer(fields['id'], signed=False) + except RuntimeError as e: + _perror('stream {}: "id": {}'.format(sid, format(e))) -def _get_array_size(array): - element = array.element - # effective size of one element includes its alignment after its size - size = _get_obj_size(element) - align = _get_obj_alignment(element) + # timestamp must exist, be an unsigned integer and be mapped to a valid clock + if 'timestamp' not in fields: + _perror('stream {}: event header: missing "timestamp" field'.format(sid)) - return _get_alignment(size, align) * array.length + try: + self._validate_integer(fields['timestamp'], signed=False) + except RuntimeError as e: + _perror('stream {}: "timestamp": {}'.format(sid, format(e))) + def _validate_stream_event_context(self, stream): + stream_event_context = stream.event_context + sid = stream.id -def _get_enum_size(enum): - return _get_obj_size(enum.integer) + if stream_event_context is None: + return + try: + self._validate_context_field(stream_event_context) + except RuntimeError as e: + _perror('stream {}: event context: {}'.format(sid, e)) + + def _validate_all_scopes(self): + # packet header + self._validate_packet_header(self._doc.trace.packet_header) + + # stream stuff + for stream_id, stream in self._doc.streams.items(): + self._validate_event_header(stream) + self._validate_packet_context(stream) + self._validate_stream_event_context(stream) + + def _validate_metadata(self): + self._validate_all_scopes() + + # 3, 4 -> 4 + # 4, 4 -> 4 + # 5, 4 -> 8 + # 6, 4 -> 8 + # 7, 4 -> 8 + # 8, 4 -> 8 + # 9, 4 -> 12 + def _get_alignment(self, at, align): + return (at + align - 1) & -align + + # this converts a tree of offset variables: + # + # field + # a -> 0 + # b -> 8 + # other_struct + # field -> 16 + # yeah -> 20 + # c -> 32 + # len -> 36 + # + # to a flat dict: + # + # field_a -> 0 + # field_b -> 8 + # field_other_struct_field -> 16 + # field_other_struct_yeah -> 20 + # field_c -> 32 + # len -> 36 + def _offvars_tree_to_vars(self, offvars_tree, prefix=None, + off_vars=collections.OrderedDict()): + for name, offset in offvars_tree.items(): + if prefix is not None: + varname = '{}_{}'.format(prefix, name) + else: + varname = name -def _get_floating_point_size(floating_point): - return floating_point.exp_dig + floating_point.mant_dig + if isinstance(offset, dict): + self._offvars_tree_to_vars(offset, varname, off_vars) + else: + off_vars[varname] = offset + return off_vars -def _get_integer_size(integer): - return integer.size + # returns the size of a struct with _static size_ + def _get_struct_size(self, struct, + offvars_tree=collections.OrderedDict(), + base_offset=0): + offset = 0 + for fname, ftype in struct.fields.items(): + field_alignment = self._get_obj_alignment(ftype) + offset = self._get_alignment(offset, field_alignment) -_obj_size_cb = { - pytsdl.tsdl.Struct: _get_struct_size, - pytsdl.tsdl.Integer: _get_integer_size, - pytsdl.tsdl.Enum: _get_enum_size, - pytsdl.tsdl.FloatingPoint: _get_floating_point_size, - pytsdl.tsdl.Array: _get_array_size, -} + if type(ftype) is pytsdl.tsdl.Struct: + offvars_tree[fname] = collections.OrderedDict() + sz = self._get_struct_size(ftype, offvars_tree[fname], + base_offset + offset) + else: + # only integers may act as sequence lengths + if type(ftype) is pytsdl.tsdl.Integer: + offvars_tree[fname] = base_offset + offset + sz = self._get_obj_size(ftype) -def _get_obj_size(obj): - return _obj_size_cb[type(obj)](obj) + offset += sz + return offset -def _get_struct_alignment(struct): - if struct.align is not None: - return struct.align + def _get_array_size(self, array): + element = array.element - cur_align = 1 + # effective size of one element includes its alignment after its size + size = self._get_obj_size(element) + align = self._get_obj_alignment(element) - for fname, ftype in struct.fields.items(): - cur_align = max(_get_obj_alignment(ftype), cur_align) + return self._get_alignment(size, align) * array.length - return cur_align + def _get_enum_size(self, enum): + return self._get_obj_size(enum.integer) + def _get_floating_point_size(self, floating_point): + return floating_point.exp_dig + floating_point.mant_dig -def _get_integer_alignment(integer): - return integer.align + def _get_integer_size(self, integer): + return integer.size + def _get_obj_size(self, obj): + return self._obj_size_cb[type(obj)](obj) -def _get_floating_point_alignment(floating_point): - return floating_point.align + def _get_struct_alignment(self, struct): + if struct.align is not None: + return struct.align + cur_align = 1 -def _get_enum_alignment(enum): - return _get_obj_alignment(enum.integer) + for fname, ftype in struct.fields.items(): + cur_align = max(self._get_obj_alignment(ftype), cur_align) + return cur_align -def _get_string_alignment(string): - return 8 + def _get_integer_alignment(self, integer): + return integer.align -def _get_array_alignment(array): - return _get_obj_alignment(array.element) + def _get_floating_point_alignment(self, floating_point): + return floating_point.align + def _get_enum_alignment(self, enum): + return self._get_obj_alignment(enum.integer) -def _get_sequence_alignment(sequence): - return _get_obj_alignment(sequence.element) + def _get_string_alignment(self, string): + return 8 + def _get_array_alignment(self, array): + return self._get_obj_alignment(array.element) -_obj_alignment_cb = { - pytsdl.tsdl.Struct: _get_struct_alignment, - pytsdl.tsdl.Integer: _get_integer_alignment, - pytsdl.tsdl.Enum: _get_enum_alignment, - pytsdl.tsdl.FloatingPoint: _get_floating_point_alignment, - pytsdl.tsdl.Array: _get_array_alignment, - pytsdl.tsdl.Sequence: _get_sequence_alignment, - pytsdl.tsdl.String: _get_string_alignment, -} + def _get_sequence_alignment(self, sequence): + return self._get_obj_alignment(sequence.element) + def _get_obj_alignment(self, obj): + return self._obj_alignment_cb[type(obj)](obj) -def _get_obj_alignment(obj): - return _obj_alignment_cb[type(obj)](obj) + def _name_to_param_name(self, prefix, name): + return 'param_{}_{}'.format(prefix, name) + def _ev_f_name_to_param_name(self, name): + return self._name_to_param_name('evf', name) -_CTX_AT = 'ctx->at' -_CTX_BUF = 'ctx->buf' -_CTX_BUF_SIZE = 'ctx->buf_size' -_CTX_BUF_AT = '{}[{} >> 3]'.format(_CTX_BUF, _CTX_AT) -_CTX_BUF_AT_ADDR = '&{}'.format(_CTX_BUF_AT) -_ALIGN_OFFSET = 'ALIGN_OFFSET' -_CHECK_OFFSET_OVERFLOW_FMT = \ - 'CHECK_OFFSET_OVERFLOW({}, {}, {{}});'.format(_CTX_AT, _CTX_BUF_SIZE) + def _ev_c_name_to_param_name(self, name): + return self._name_to_param_name('evc', name) + def _ev_sec_name_to_param_name(self, name): + return self._name_to_param_name('evsec', name) -def _field_name_to_param_name(fname): - return 'param_{}'.format(fname) + def _ev_h_name_to_param_name(self, name): + return self._name_to_param_name('evh', name) + def _s_pc_name_to_param_name(self, name): + return self._name_to_param_name('spc', name) -def _get_integer_param_type(integer): - signed = 'u' if not integer.signed else '' + def _s_ph_name_to_param_name(self, name): + return self._name_to_param_name('sph', name) - if integer.size == 8: - sz = '8' - elif integer.size == 16: - sz = '16' - elif integer.size == 32: - sz = '32' - elif integer.size == 64: - sz = '64' - else: - # if the integer is signed and of uncommon size, the sign bit is - # at a custom position anyway so we use a 64-bit unsigned - signed = 'u' + def _get_integer_param_ctype(self, integer): + signed = 'u' if not integer.signed else '' - if integer.signed: + if integer.size == 8: + sz = '8' + elif integer.size == 16: + sz = '16' + elif integer.size == 32: + sz = '32' + elif integer.size == 64: sz = '64' else: - if integer.size < 16: - sz = '8' - elif integer.size < 32: - sz = '16' - elif integer.size < 64: - sz = '32' - else: - sz = '64' - - return '{}int{}_t'.format(signed, sz) - - -def _get_enum_param_type(enum): - return _get_obj_param_type(enum.integer) - - -def _get_floating_point_param_type(fp): - if fp.exp_dig == 8 and fp.mant_dig == 24 and fp.align == 32: - return 'float' - elif fp.exp_dig == 11 and fp.mant_dig == 53 and fp.align == 64: - return 'double' - else: - return 'uint64_t' - - -_obj_param_type_cb = { - pytsdl.tsdl.Struct: lambda obj: 'const void*', - pytsdl.tsdl.Integer: _get_integer_param_type, - pytsdl.tsdl.Enum: _get_enum_param_type, - pytsdl.tsdl.FloatingPoint: _get_floating_point_param_type, - pytsdl.tsdl.Array: lambda obj: 'const void*', - pytsdl.tsdl.Sequence: lambda obj: 'const void*', - pytsdl.tsdl.String: lambda obj: 'const char*', -} - - -def _get_obj_param_type(obj): - return _obj_param_type_cb[type(obj)](obj) - - -class _CBlock(list): - pass - - -class _CLine(str): - pass - - -def _get_check_offset_overflow_cline(size): - return _CLine(_CHECK_OFFSET_OVERFLOW_FMT.format(size)) - - -def _write_field_struct(doc, fname, struct): - size = _get_struct_size(struct) - size_bytes = _get_alignment(size, 8) // 8 - - dst = _CTX_BUF_AT_ADDR - src = _field_name_to_param_name(fname) + # if the integer is signed and of uncommon size, the sign bit is + # at a custom position anyway so we use a 64-bit unsigned + signed = 'u' - return [ - # memcpy() is safe since barectf requires inner structures - # to be byte-aligned - _get_check_offset_overflow_cline(size), - _CLine('memcpy({}, {}, {});'.format(dst, src, size_bytes)), - _CLine('{} += {};'.format(_CTX_AT, size)), - ] - - -_bo_suffixes_map = { - pytsdl.tsdl.ByteOrder.BE: 'be', - pytsdl.tsdl.ByteOrder.LE: 'le', -} - - -def _write_field_integer(doc, fname, integer): - bo = _bo_suffixes_map[integer.byte_order] - ptr = _CTX_BUF - t = _get_obj_param_type(integer) - start = _CTX_AT - length = _get_obj_size(integer) - value = _field_name_to_param_name(fname) - fmt = 'barectf_bitfield_write_{}({}, {}, {}, {}, {});' - - return [ - _get_check_offset_overflow_cline(length), - _CLine(fmt.format(bo, ptr, t, start, length, value)), - _CLine('{} += {};'.format(_CTX_AT, length)) - ] - - -def _write_field_enum(doc, fname, enum): - return _write_field_obj(doc, fname, enum.integer) - - -def _write_field_floating_point(doc, fname, floating_point): - bo = _bo_suffixes_map[floating_point.byte_order] - ptr = _CTX_BUF - t = _get_obj_param_type(floating_point) - start = _CTX_AT - length = _get_obj_size(floating_point) - value = _field_name_to_param_name(fname) - fmt = 'barectf_bitfield_write_{}({}, {}, {}, {}, {});' - - return [ - _get_check_offset_overflow_cline(length), - _CLine(fmt.format(bo, ptr, t, start, length, value)), - _CLine('{} += {};'.format(_CTX_AT, length)) - ] - - -def _write_field_array(doc, fname, array): - lines = [] - - # array index variable declaration - iv = 'ia_{}'.format(fname) - lines.append(_CLine('uint32_t {};'.format(iv))) - - # for loop using array's static length - line = 'for ({iv} = 0; {iv} < {l}; ++{iv}) {{'.format(iv=iv, l=array.length) - lines.append(_CLine(line)) - - # for loop statements - for_block = _CBlock() + if integer.signed: + sz = '64' + else: + if integer.size < 16: + sz = '8' + elif integer.size < 32: + sz = '16' + elif integer.size < 64: + sz = '32' + else: + sz = '64' - # align bit index before writing to the buffer - element_align = _get_obj_alignment(array.element) - line = '{}({}, {});'.format(_ALIGN_OFFSET, _CTX_AT, element_align) - for_block.append(_CLine(line)) + return '{}int{}_t'.format(signed, sz) - # write element to the buffer - for_block += _write_field_obj(doc, fname, array.element) - lines.append(for_block) + def _get_enum_param_ctype(self, enum): + return self._get_obj_param_ctype(enum.integer) - # for loop end - lines.append(_CLine('}')) + def _get_floating_point_param_ctype(self, fp): + if fp.exp_dig == 8 and fp.mant_dig == 24 and fp.align == 32: + return 'float' + elif fp.exp_dig == 11 and fp.mant_dig == 53 and fp.align == 64: + return 'double' + else: + return 'uint64_t' + + def _get_obj_param_ctype(self, obj): + return self._obj_param_ctype_cb[type(obj)](obj) + + def _get_chk_offset_v(self, size): + fmt = '{}_CHK_OFFSET_V({}, {}, {});' + ret = fmt.format(self._prefix.upper(), self._CTX_AT, + self._CTX_BUF_SIZE, size) + + return ret + + def _get_chk_offset_v_cline(self, size): + return _CLine(self._get_chk_offset_v(size)) + + def _get_align_offset(self, align): + fmt = '{}_ALIGN_OFFSET({}, {});' + ret = fmt.format(self._prefix.upper(), self._CTX_AT, align) + + return ret + + def _get_align_offset_cline(self, size): + return _CLine(self._get_align_offset(size)) + + def _write_field_struct(self, fname, src_name, struct): + size = self._get_struct_size(struct) + size_bytes = self._get_alignment(size, 8) // 8 + dst = self._CTX_BUF_AT_ADDR + + return [ + # memcpy() is safe since barectf requires inner structures + # to be byte-aligned + self._get_chk_offset_v_cline(size), + _CLine('memcpy({}, {}, {});'.format(dst, src_name, size_bytes)), + _CLine('{} += {};'.format(self._CTX_AT, size)), + ] + + def _write_field_integer(self, fname, src_name, integer): + bo = self._bo_suffixes_map[integer.byte_order] + ptr = self._CTX_BUF + t = self._get_obj_param_ctype(integer) + start = self._CTX_AT + length = self._get_obj_size(integer) + fmt = 'barectf_bitfield_write_{}({}, {}, {}, {}, {});' + + return [ + self._get_chk_offset_v_cline(length), + _CLine(fmt.format(bo, ptr, t, start, length, src_name)), + _CLine('{} += {};'.format(self._CTX_AT, length)) + ] + + def _write_field_enum(self, fname, src_name, enum): + return self._write_field_obj(fname, src_name, enum.integer) + + def _write_field_floating_point(self, fname, src_name, floating_point): + bo = self._bo_suffixes_map[floating_point.byte_order] + ptr = self._CTX_BUF + t = self._get_obj_param_ctype(floating_point) + start = self._CTX_AT + length = self._get_obj_size(floating_point) + fmt = 'barectf_bitfield_write_{}({}, {}, {}, {}, {});' + + return [ + self._get_chk_offset_v_cline(length), + _CLine(fmt.format(bo, ptr, t, start, length, src_name)), + _CLine('{} += {};'.format(self._CTX_AT, length)) + ] + + def _write_field_array(self, fname, src_name, array): + lines = [] - return lines + # array index variable declaration + iv = 'ia_{}'.format(fname) + lines.append(_CLine('uint32_t {};'.format(iv))) + # for loop using array's static length + line = 'for ({iv} = 0; {iv} < {l}; ++{iv}) {{'.format(iv=iv, l=array.length) + lines.append(_CLine(line)) -def _write_field_sequence(doc, fname, sequence): - return [ - _CLine('would write sequence here;'), - ] + # for loop statements + for_block = _CBlock() + # align bit index before writing to the buffer + element_align = self._get_obj_alignment(array.element) + cline = self._get_align_offset_cline(element_align) + for_block.append(cline) -def _write_field_string(doc, fname, string): - lines = [] + # write element to the buffer + for_block += self._write_field_obj(fname, src_name, array.element) + lines.append(for_block) - # source pointer (function parameter) - src = _field_name_to_param_name(fname) + # for loop end + lines.append(_CLine('}')) - # string index variable declaration - iv = 'is_{}'.format(fname) - lines.append(_CLine('uint32_t {};'.format(iv))) + return lines - # for loop; loop until the end of the source string is reached - fmt = "for ({iv} = 0; {src}[{iv}] != '\\0'; ++{iv}, {ctxat} += 8) {{" - lines.append(_CLine(fmt.format(iv=iv, src=src, ctxat=_CTX_AT))) + def _write_field_sequence(self, fname, src_name, sequence): + return [ + _CLine('would write sequence here;'), + ] - # for loop statements - for_block = _CBlock() + def _write_field_string(self, fname, src_name, string): + lines = [] - # check offset overflow - for_block.append(_get_check_offset_overflow_cline(8)) + # string index variable declaration + iv = 'is_{}'.format(fname) + lines.append(_CLine('uint32_t {};'.format(iv))) - # write byte to the buffer - line = '{} = {}[{}]'.format(_CTX_BUF_AT, src, iv) - for_block.append(_CLine(line)) + # for loop; loop until the end of the source string is reached + fmt = "for ({iv} = 0; {src}[{iv}] != '\\0'; ++{iv}, {ctxat} += 8) {{" + line = fmt.format(iv=iv, src=src_name, ctxat=self._CTX_AT) + lines.append(_CLine(line)) - # append for loop - lines.append(for_block) - lines.append(_CLine('}')) + # for loop statements + for_block = _CBlock() - # write NULL character to the buffer - lines.append(_CLine("{} = '\\0';".format(_CTX_BUF_AT))) - lines.append(_CLine('{} += 8;'.format(_CTX_AT))) + # check offset overflow + for_block.append(self._get_chk_offset_v_cline(8)) - return lines + # write byte to the buffer + fmt = '{dst} = {src}[{iv}];' + line = fmt.format(dst=self._CTX_BUF_AT, iv=iv, src=src_name) + for_block.append(_CLine(line)) + # append for loop + lines.append(for_block) + lines.append(_CLine('}')) -_write_field_obj_cb = { - pytsdl.tsdl.Struct: _write_field_struct, - pytsdl.tsdl.Integer: _write_field_integer, - pytsdl.tsdl.Enum: _write_field_enum, - pytsdl.tsdl.FloatingPoint: _write_field_floating_point, - pytsdl.tsdl.Array: _write_field_array, - pytsdl.tsdl.Sequence: _write_field_sequence, - pytsdl.tsdl.String: _write_field_string, -} + # write NULL character to the buffer + lines.append(_CLine("{} = '\\0';".format(self._CTX_BUF_AT))) + lines.append(_CLine('{} += 8;'.format(self._CTX_AT))) + return lines -def _write_field_obj(doc, fname, ftype): - return _write_field_obj_cb[type(ftype)](doc, fname, ftype) + def _write_field_obj(self, fname, src_name, ftype): + return self._write_field_obj_cb[type(ftype)](fname, src_name, ftype) + def _get_offvar_name(self, name): + return 'off_{}'.format(name) -def _struct_to_clines(doc, struct): - line_groups = [] + def _get_offvar_name_from_expr(self, expr): + return self._get_offvar_name('_'.join(expr)) - for fname, ftype in struct.fields.items(): + def _field_to_cline(self, fname, ftype, scope_name, param_name_cb): lines = [] - pname = _field_name_to_param_name(fname) - align = _get_obj_alignment(ftype) + pname = param_name_cb(fname) + align = self._get_obj_alignment(ftype) + + # group comment + fmt = '/* write {} field "{}" ({}) */' + line = fmt.format(scope_name, fname, + self._tsdl_type_names_map[type(ftype)]) + lines.append(_CLine(line)) # align bit index before writing to the buffer - line = '{}({}, {});'.format(_ALIGN_OFFSET, _CTX_AT, align) - lines.append(line) + cline = self._get_align_offset_cline(align) + lines.append(cline) # write offset variables if type(ftype) is pytsdl.tsdl.Struct: - offset_vars_tree = collections.OrderedDict() - _get_struct_size(ftype, offset_vars_tree) - offset_vars = _offset_vars_tree_to_vars(offset_vars_tree) + offvars_tree = collections.OrderedDict() + self._get_struct_size(ftype, offvars_tree) + off_vars = self._offvars_tree_to_vars(offvars_tree) # as many offset as there are child fields because a future # sequence could refer to any of those fields - for lname, offset in offset_vars.items(): - fmt = 'uint32_t off_{}_{} = {} + {};' - line = fmt.format(fname, lname, _CTX_AT, offset); + for lname, offset in off_vars.items(): + print(fname, lname) + offvar = self._get_offvar_name('_'.join([fname, lname])) + fmt = 'uint32_t {} = {} + {};' + line = fmt.format(offvar, self._CTX_AT, offset); lines.append(_CLine(line)) elif type(ftype) is pytsdl.tsdl.Integer: # offset of this simple field is the current bit index - line = 'uint32_t off_{} = {};'.format(fname, _CTX_AT) + offvar = self._get_offvar_name(fname) + line = 'uint32_t {} = {};'.format(offvar, self._CTX_AT) lines.append(_CLine(line)) - lines += _write_field_obj(doc, fname, ftype) - line_groups.append(lines) - - if not line_groups: - return line_groups + lines += self._write_field_obj(fname, pname, ftype) - output_lines = line_groups[0] + return lines - for lines in line_groups[1:]: - output_lines.append('') - output_lines += lines + def _struct_to_clines(self, struct, scope_name, param_name_cb): + line_groups = [] - return output_lines + for fname, ftype in struct.fields.items(): + lines = self._field_to_cline(fname, ftype, scope_name, + param_name_cb) + line_groups.append(lines) + if not line_groups: + return line_groups -def _cblock_to_source_lines(cblock, indent=1): - src = [] - indentstr = '\t' * indent + output_lines = line_groups[0] - for line in cblock: - if type(line) is _CBlock: - src += _cblock_to_source_lines(line, indent + 1) - else: - src.append(indentstr + line) + for lines in line_groups[1:]: + output_lines.append('') + output_lines += lines - return src + return output_lines + def _cblock_to_source_lines(self, cblock, indent=1): + src = [] + indentstr = '\t' * indent -def _cblock_to_source(cblock, indent=1): - lines = _cblock_to_source_lines(cblock, indent) + for line in cblock: + if type(line) is _CBlock: + src += self._cblock_to_source_lines(line, indent + 1) + else: + src.append(indentstr + line) - return '\n'.join(lines) + return src + def _cblock_to_source(self, cblock, indent=1): + lines = self._cblock_to_source_lines(cblock, indent) -def gen_barectf(metadata, output, prefix, static_inline, manual_clock): - # open CTF metadata file - try: - with open(metadata) as f: - tsdl = f.read() - except: - _perror('cannot open/read CTF metadata file "{}"'.format(metadata)) + return '\n'.join(lines) - # parse CTF metadata - parser = pytsdl.parser.Parser() + def gen_barectf(self, metadata, output, prefix, static_inline, + manual_clock): + self._metadata = metadata + self._output = output + self._prefix = prefix + self._static_inline = static_inline + self._manual_clock = manual_clock - try: - doc = parser.parse(tsdl) - except pytsdl.parser.ParseError as e: - _perror('parse error: {}'.format(e)) + # open CTF metadata file + try: + with open(metadata) as f: + self._tsdl = f.read() + except: + _perror('cannot open/read CTF metadata file "{}"'.format(metadata)) - # validate CTF metadata against barectf constraints - _validate_metadata(doc) + # parse CTF metadata + try: + self._doc = self._parser.parse(self._tsdl) + except pytsdl.parser.ParseError as e: + _perror('parse error: {}'.format(e)) - import json + # validate CTF metadata against barectf constraints + self._validate_metadata() - clines = _struct_to_clines(doc, doc.streams[0].get_event(0).fields) - print(_cblock_to_source(_CBlock(clines))) + clines = self._struct_to_clines(self._doc.streams[0].get_event(0).fields, + 'stream event context', + self._ev_f_name_to_param_name) + source = self._cblock_to_source(_CBlock(clines)) def run(): args = _parse_args() - gen_barectf(args.metadata, args.output, args.prefix, args.static_inline, - args.manual_clock) + generator = BarectfCodeGenerator() + generator.gen_barectf(args.metadata, args.output, args.prefix, + args.static_inline, args.manual_clock)