# THE SOFTWARE.
from termcolor import cprint, colored
import argparse
+import pytsdl.tsdl
+import pytsdl.parser
import sys
import os
import re
return args
-def gen_barectf(output, prefix, static_inline, manual_clock):
+def _validate_struct(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.Sequence:
+ raise RuntimeError('field "{}" is a dynamic array'.format(name))
+ elif type(ftype) is pytsdl.tsdl.Array:
+ end = False
+ element = ftype.element
+
+ while not end:
+ if type(element) is pytsdl.tsdl.Sequence:
+ raise RuntimeError('field "{}" contains a dynamic array'.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'.format(name))
+ elif type(element) is pytsdl.tsdl.Struct:
+ _validate_struct(element)
+
+ if type(element) is pytsdl.tsdl.Array:
+ element = element.element
+ else:
+ 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'.format(name))
+ elif type(ftype) is pytsdl.tsdl.Struct:
+ _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'))
+
+
+def _validate_packet_header(packet_header):
+ try:
+ _validate_struct(packet_header)
+ except RuntimeError as e:
+ _perror('packet header: {}'.format(e))
+
+ # 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))
+
+ # mandatory stream_id
+ if 'stream_id' not in packet_header.fields:
+ _perror('packet header: missing "stream_id" 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))
+
+
+def _dot_name_to_str(name):
+ return '.'.join(name)
+
+
+def _validate_clock(doc, name):
+ msg = '"{}" does not name an existing clock'.format(_dot_name_to_str(name))
+
+ if len(name) != 3:
+ raise RuntimeError(msg)
+
+ if name[0] != 'clock' or name[2] != 'value':
+ raise RuntimeError()
+
+ if name[1] not in doc.clocks:
+ raise RuntimeError(msg)
+
+
+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
+
+ 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)
+
+ return sum(comps) == len(comps)
+
+
+def _validate_packet_context(doc, 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))
+
+ 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 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 _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))
+
+ try:
+ _validate_integer(fields['content_size'], 32, 32, False)
+ except:
+ try:
+ _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))
+
+ try:
+ _validate_integer(fields['packet_size'], 32, 32, False)
+ except:
+ try:
+ _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:
+ _validate_integer(fields['cpu_id'], signed=False)
+ 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))
+
+ try:
+ _validate_integer(fields['id'], signed=False)
+ except RuntimeError as e:
+ _perror('stream {}: "id": {}'.format(sid, format(e)))
+
+
+ # 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)))
+
+ if fields['timestamp'].map is None:
+ _perror('stream {}: "timestamp": integer must be mapped to an existing clock'.format(sid))
+
+ try:
+ _validate_clock(doc, fields['timestamp'].map)
+ except RuntimeError as e:
+ _perror('stream {}: "timestamp": integer must be mapped to an existing clock'.format(sid))
+
+
+def _validate_headers_contexts(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)
+
+
+def _validate_metadata(doc):
+ _validate_headers_contexts(doc)
+
+
+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))
+
+ # parse CTF metadata
+ parser = pytsdl.parser.Parser()
+
+ try:
+ doc = parser.parse(tsdl)
+ except pytsdl.parser.ParseError as e:
+ _perror('parse error: {}'.format(e))
+
+ # validate CTF metadata against barectf constraints
+ _validate_metadata(doc)
+
+ _pinfo(metadata)
_pinfo(output)
_pinfo(prefix)
_pinfo(static_inline)
def run():
args = _parse_args()
- gen_barectf(args.output, args.prefix, args.static_inline,
+ gen_barectf(args.metadata, args.output, args.prefix, args.static_inline,
args.manual_clock)