Add event context/fields validation
[deliverable/barectf.git] / barectf / cli.py
1 # The MIT License (MIT)
2 #
3 # Copyright (c) 2014 Philippe Proulx <philippe.proulx@efficios.com>
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a copy
6 # of this software and associated documentation files (the "Software"), to deal
7 # in the Software without restriction, including without limitation the rights
8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 # copies of the Software, and to permit persons to whom the Software is
10 # furnished to do so, subject to the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 # THE SOFTWARE.
22 from termcolor import cprint, colored
23 import pytsdl.parser
24 import pytsdl.tsdl
25 import collections
26 import argparse
27 import sys
28 import os
29 import re
30
31
32 def _perror(msg, exit_code=1):
33 cprint('Error: {}'.format(msg), 'red', attrs=['bold'], file=sys.stderr)
34 sys.exit(exit_code)
35
36
37 def _pinfo(msg):
38 cprint(':: {}'.format(msg), 'blue', attrs=['bold'], file=sys.stderr)
39
40
41 def _parse_args():
42 ap = argparse.ArgumentParser()
43
44 ap.add_argument('-O', '--output', metavar='OUTPUT', action='store',
45 default=os.getcwd(),
46 help='output directory of C files')
47 ap.add_argument('-p', '--prefix', metavar='PREFIX', action='store',
48 default='barectf',
49 help='custom prefix for C function and structure names')
50 ap.add_argument('-s', '--static-inline', action='store_true',
51 help='generate static inline C functions')
52 ap.add_argument('-c', '--manual-clock', action='store_true',
53 help='do not use a clock callback: pass clock value to tracing functions')
54 ap.add_argument('metadata', metavar='METADATA', action='store',
55 help='CTF metadata input file')
56
57 # parse args
58 args = ap.parse_args()
59
60 # validate output directory
61 if not os.path.isdir(args.output):
62 _perror('"{}" is not an existing directory'.format(args.output))
63
64 # validate prefix
65 if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', args.prefix):
66 _perror('"{}" is not a valid C identifier'.format(args.prefix))
67
68 # validate that metadata file exists
69 if not os.path.isfile(args.metadata):
70 _perror('"{}" is not an existing file'.format(args.metadata))
71
72 return args
73
74
75 class _CBlock(list):
76 pass
77
78
79 class _CLine(str):
80 pass
81
82
83 class BarectfCodeGenerator:
84 _CTX_AT = 'ctx->at'
85 _CTX_BUF = 'ctx->buf'
86 _CTX_BUF_SIZE = 'ctx->buf_size'
87 _CTX_BUF_AT = '{}[{} >> 3]'.format(_CTX_BUF, _CTX_AT)
88 _CTX_BUF_AT_ADDR = '&{}'.format(_CTX_BUF_AT)
89
90 _bo_suffixes_map = {
91 pytsdl.tsdl.ByteOrder.BE: 'be',
92 pytsdl.tsdl.ByteOrder.LE: 'le',
93 }
94
95 _tsdl_type_names_map = {
96 pytsdl.tsdl.Integer: 'integer',
97 pytsdl.tsdl.FloatingPoint: 'floating point',
98 pytsdl.tsdl.Enum: 'enumeration',
99 pytsdl.tsdl.String: 'string',
100 pytsdl.tsdl.Array: 'static array',
101 pytsdl.tsdl.Sequence: 'dynamic array',
102 pytsdl.tsdl.Struct: 'structure',
103 }
104
105 def __init__(self):
106 self._parser = pytsdl.parser.Parser()
107 self._obj_size_cb = {
108 pytsdl.tsdl.Struct: self._get_struct_size,
109 pytsdl.tsdl.Integer: self._get_integer_size,
110 pytsdl.tsdl.Enum: self._get_enum_size,
111 pytsdl.tsdl.FloatingPoint: self._get_floating_point_size,
112 pytsdl.tsdl.Array: self._get_array_size,
113 }
114 self._obj_alignment_cb = {
115 pytsdl.tsdl.Struct: self._get_struct_alignment,
116 pytsdl.tsdl.Integer: self._get_integer_alignment,
117 pytsdl.tsdl.Enum: self._get_enum_alignment,
118 pytsdl.tsdl.FloatingPoint: self._get_floating_point_alignment,
119 pytsdl.tsdl.Array: self._get_array_alignment,
120 pytsdl.tsdl.Sequence: self._get_sequence_alignment,
121 pytsdl.tsdl.String: self._get_string_alignment,
122 }
123 self._obj_param_ctype_cb = {
124 pytsdl.tsdl.Struct: lambda obj: 'const void*',
125 pytsdl.tsdl.Integer: self._get_integer_param_ctype,
126 pytsdl.tsdl.Enum: self._get_enum_param_ctype,
127 pytsdl.tsdl.FloatingPoint: self._get_floating_point_param_ctype,
128 pytsdl.tsdl.Array: lambda obj: 'const void*',
129 pytsdl.tsdl.Sequence: lambda obj: 'const void*',
130 pytsdl.tsdl.String: lambda obj: 'const char*',
131 }
132 self._write_field_obj_cb = {
133 pytsdl.tsdl.Struct: self._write_field_struct,
134 pytsdl.tsdl.Integer: self._write_field_integer,
135 pytsdl.tsdl.Enum: self._write_field_enum,
136 pytsdl.tsdl.FloatingPoint: self._write_field_floating_point,
137 pytsdl.tsdl.Array: self._write_field_array,
138 pytsdl.tsdl.Sequence: self._write_field_sequence,
139 pytsdl.tsdl.String: self._write_field_string,
140 }
141
142 # TODO: prettify this function
143 def _validate_struct(self, struct):
144 # just in case we call this with the wrong type
145 if type(struct) is not pytsdl.tsdl.Struct:
146 raise RuntimeError('expecting a struct')
147
148 # make sure inner structures are at least byte-aligned
149 if self._get_obj_alignment(struct) < 8:
150 raise RuntimeError('inner struct must be at least byte-aligned')
151
152 # check each field
153 for fname, ftype in struct.fields.items():
154 if type(ftype) is pytsdl.tsdl.Sequence:
155 raise RuntimeError('field "{}" is a dynamic array (not allowed here)'.format(fname))
156 elif type(ftype) is pytsdl.tsdl.Array:
157 # we need to check every element until we find a terminal one
158 element = ftype.element
159
160 while True:
161 if type(element) is pytsdl.tsdl.Sequence:
162 raise RuntimeError('field "{}" contains a dynamic array (not allowed here)'.format(fname))
163 elif type(element) is pytsdl.tsdl.Variant:
164 raise RuntimeError('field "{}" contains a variant (unsupported)'.format(fname))
165 elif type(element) is pytsdl.tsdl.String:
166 raise RuntimeError('field "{}" contains a string (not allowed here)'.format(fname))
167 elif type(element) is pytsdl.tsdl.Struct:
168 _validate_struct(element)
169 elif type(element) is pytsdl.tsdl.Integer:
170 if self._get_obj_size(element) > 64:
171 raise RuntimeError('integer field "{}" larger than 64-bit'.format(fname))
172 elif type(element) is pytsdl.tsdl.FloatingPoint:
173 if self._get_obj_size(element) > 64:
174 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(fname))
175 elif type(element) is pytsdl.tsdl.Enum:
176 if self._get_obj_size(element) > 64:
177 raise RuntimeError('enum field "{}" larger than 64-bit'.format(fname))
178
179 if type(element) is pytsdl.tsdl.Array:
180 # still an array, continue
181 element = element.element
182 else:
183 # found the terminal element
184 break
185 elif type(ftype) is pytsdl.tsdl.Variant:
186 raise RuntimeError('field "{}" is a variant (unsupported)'.format(fname))
187 elif type(ftype) is pytsdl.tsdl.String:
188 raise RuntimeError('field "{}" is a string (not allowed here)'.format(fname))
189 elif type(ftype) is pytsdl.tsdl.Struct:
190 self._validate_struct(ftype)
191 elif type(ftype) is pytsdl.tsdl.Integer:
192 if self._get_obj_size(ftype) > 64:
193 raise RuntimeError('integer field "{}" larger than 64-bit'.format(fname))
194 elif type(ftype) is pytsdl.tsdl.FloatingPoint:
195 if self._get_obj_size(ftype) > 64:
196 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(fname))
197 elif type(ftype) is pytsdl.tsdl.Enum:
198 if self._get_obj_size(ftype) > 64:
199 raise RuntimeError('enum field "{}" larger than 64-bit'.format(fname))
200
201 def _validate_context_field(self, struct):
202 if type(struct) is not pytsdl.tsdl.Struct:
203 raise RuntimeError('expecting a struct')
204
205 for fname, ftype in struct.fields.items():
206 if type(ftype) is pytsdl.tsdl.Variant:
207 raise RuntimeError('field "{}" is a variant (unsupported)'.format(fname))
208 elif type(ftype) is pytsdl.tsdl.Struct:
209 # validate inner structure against barectf constraints
210 self._validate_struct(ftype)
211
212 def _validate_integer(self, integer, size=None, align=None,
213 signed=None):
214 if type(integer) is not pytsdl.tsdl.Integer:
215 raise RuntimeError('expected integer')
216
217 if size is not None:
218 if integer.size != size:
219 raise RuntimeError('expected {}-bit integer'.format(size))
220
221 if align is not None:
222 if integer.align != align:
223 raise RuntimeError('expected integer with {}-bit alignment'.format(align))
224
225 if signed is not None:
226 if integer.signed != signed:
227 raise RuntimeError('expected {} integer'.format('signed' if signed else 'unsigned'))
228
229 def _validate_packet_header(self, packet_header):
230 try:
231 self._validate_struct(packet_header)
232 except RuntimeError as e:
233 _perror('packet header: {}'.format(e))
234
235 # magic must be the first field
236 if 'magic' in packet_header.fields:
237 if list(packet_header.fields.keys())[0] != 'magic':
238 _perror('packet header: "magic" must be the first field')
239 else:
240 _perror('packet header: missing "magic" field')
241
242 # magic must be a 32-bit unsigned integer, 32-bit aligned
243 try:
244 self._validate_integer(packet_header['magic'], 32, 32, False)
245 except RuntimeError as e:
246 _perror('packet header: "magic": {}'.format(e))
247
248 # mandatory stream_id
249 if 'stream_id' not in packet_header.fields:
250 _perror('packet header: missing "stream_id" field')
251
252 # stream_id must be an unsigned integer
253 try:
254 self._validate_integer(packet_header['stream_id'], signed=False)
255 except RuntimeError as e:
256 _perror('packet header: "stream_id": {}'.format(e))
257
258 def _dot_name_to_str(self, name):
259 return '.'.join(name)
260
261 def _compare_integers(self, int1, int2):
262 if type(int1) is not pytsdl.tsdl.Integer:
263 return False
264
265 if type(int2) is not pytsdl.tsdl.Integer:
266 return False
267
268 size = int1.size == int2.size
269 align = int1.align == int2.align
270 cmap = int1.map == int2.map
271 base = int1.base == int2.base
272 encoding = int1.encoding == int2.encoding
273 signed = int1.signed == int2.signed
274 comps = (size, align, cmap, base, encoding, signed)
275
276 # True means 1 for sum()
277 return sum(comps) == len(comps)
278
279 def _validate_packet_context(self, stream):
280 packet_context = stream.packet_context
281 sid = stream.id
282
283 try:
284 self._validate_struct(packet_context)
285 except RuntimeError as e:
286 _perror('stream {}: packet context: {}'.format(sid, e))
287
288 fields = packet_context.fields
289
290 # if timestamp_begin exists, timestamp_end must exist
291 if 'timestamp_begin' in fields or 'timestamp_end' in fields:
292 if 'timestamp_begin' not in fields or 'timestamp_end' not in fields:
293 _perror('stream {}: packet context: "timestamp_begin" must exist if "timestamp_end" exists'.format(sid))
294 else:
295 # timestamp_begin and timestamp_end must have the same integer
296 # as the event header's timestamp field (should exist by now)
297 timestamp = stream.event_header['timestamp']
298
299 if not self._compare_integers(fields['timestamp_begin'], timestamp):
300 _perror('stream {}: packet context: "timestamp_begin": integer type different from event header\'s "timestamp" field'.format(sid))
301
302 if not self._compare_integers(fields['timestamp_end'], timestamp):
303 _perror('stream {}: packet context: "timestamp_end": integer type different from event header\'s "timestamp" field'.format(sid))
304
305 # content_size must exist and be an unsigned integer
306 if 'content_size' not in fields:
307 _perror('stream {}: packet context: missing "content_size" field'.format(sid))
308
309 try:
310 self._validate_integer(fields['content_size'], 32, 32, False)
311 except:
312 try:
313 self._validate_integer(fields['content_size'], 64, 64, False)
314 except:
315 _perror('stream {}: packet context: "content_size": expecting unsigned 32-bit/64-bit integer'.format(sid))
316
317 # packet_size must exist and be an unsigned integer
318 if 'packet_size' not in fields:
319 _perror('stream {}: packet context: missing "packet_size" field'.format(sid))
320
321 try:
322 self._validate_integer(fields['packet_size'], 32, 32, False)
323 except:
324 try:
325 self._validate_integer(fields['packet_size'], 64, 64, False)
326 except:
327 _perror('stream {}: packet context: "packet_size": expecting unsigned 32-bit/64-bit integer'.format(sid))
328
329 # if cpu_id exists, must be an unsigned integer
330 if 'cpu_id' in fields:
331 try:
332 self._validate_integer(fields['cpu_id'], signed=False)
333 except RuntimeError as e:
334 _perror('stream {}: packet context: "cpu_id": {}'.format(sid, e))
335
336 def _validate_event_header(self, stream):
337 event_header = stream.event_header
338 sid = stream.id
339
340 try:
341 self._validate_struct(event_header)
342 except RuntimeError as e:
343 _perror('stream {}: event header: {}'.format(sid, e))
344
345 fields = event_header.fields
346
347 # id must exist and be an unsigned integer
348 if 'id' not in fields:
349 _perror('stream {}: event header: missing "id" field'.format(sid))
350
351 try:
352 self._validate_integer(fields['id'], signed=False)
353 except RuntimeError as e:
354 _perror('stream {}: "id": {}'.format(sid, format(e)))
355
356
357 # timestamp must exist, be an unsigned integer and be mapped to a valid clock
358 if 'timestamp' not in fields:
359 _perror('stream {}: event header: missing "timestamp" field'.format(sid))
360
361 try:
362 self._validate_integer(fields['timestamp'], signed=False)
363 except RuntimeError as e:
364 _perror('stream {}: "timestamp": {}'.format(sid, format(e)))
365
366 def _validate_stream_event_context(self, stream):
367 stream_event_context = stream.event_context
368 sid = stream.id
369
370 if stream_event_context is None:
371 return
372
373 try:
374 self._validate_context_field(stream_event_context)
375 except RuntimeError as e:
376 _perror('stream {}: event context: {}'.format(sid, e))
377
378 def _validate_event_context(self, stream, event):
379 event_context = event.context
380 sid = stream.id
381 eid = event.id
382
383 if event_context is None:
384 return
385
386 try:
387 self._validate_context_field(event_context)
388 except RuntimeError as e:
389 _perror('stream {}: event {}: context: {}'.format(sid, eid, e))
390
391 def _validate_event_fields(self, stream, event):
392 event_fields = event.fields
393 sid = stream.id
394 eid = event.id
395
396 try:
397 self._validate_context_field(event_fields)
398 except RuntimeError as e:
399 _perror('stream {}: event {}: fields: {}'.format(sid, eid, e))
400
401 def _validate_all_scopes(self):
402 # packet header
403 self._validate_packet_header(self._doc.trace.packet_header)
404
405 # stream stuff
406 for stream in self._doc.streams.values():
407 self._validate_event_header(stream)
408 self._validate_packet_context(stream)
409 self._validate_stream_event_context(stream)
410
411 # event stuff
412 for event in stream.events:
413 self._validate_event_context(stream, event)
414 self._validate_event_fields(stream, event)
415
416 def _validate_metadata(self):
417 self._validate_all_scopes()
418
419 # 3, 4 -> 4
420 # 4, 4 -> 4
421 # 5, 4 -> 8
422 # 6, 4 -> 8
423 # 7, 4 -> 8
424 # 8, 4 -> 8
425 # 9, 4 -> 12
426 def _get_alignment(self, at, align):
427 return (at + align - 1) & -align
428
429 # this converts a tree of offset variables:
430 #
431 # field
432 # a -> 0
433 # b -> 8
434 # other_struct
435 # field -> 16
436 # yeah -> 20
437 # c -> 32
438 # len -> 36
439 #
440 # to a flat dict:
441 #
442 # field_a -> 0
443 # field_b -> 8
444 # field_other_struct_field -> 16
445 # field_other_struct_yeah -> 20
446 # field_c -> 32
447 # len -> 36
448 def _offvars_tree_to_vars(self, offvars_tree, prefix=None,
449 off_vars=collections.OrderedDict()):
450 for name, offset in offvars_tree.items():
451 if prefix is not None:
452 varname = '{}_{}'.format(prefix, name)
453 else:
454 varname = name
455
456 if isinstance(offset, dict):
457 self._offvars_tree_to_vars(offset, varname, off_vars)
458 else:
459 off_vars[varname] = offset
460
461 return off_vars
462
463 # returns the size of a struct with _static size_
464 def _get_struct_size(self, struct,
465 offvars_tree=collections.OrderedDict(),
466 base_offset=0):
467 offset = 0
468
469 for fname, ftype in struct.fields.items():
470 field_alignment = self._get_obj_alignment(ftype)
471 offset = self._get_alignment(offset, field_alignment)
472
473 if type(ftype) is pytsdl.tsdl.Struct:
474 offvars_tree[fname] = collections.OrderedDict()
475 sz = self._get_struct_size(ftype, offvars_tree[fname],
476 base_offset + offset)
477 else:
478 # only integers may act as sequence lengths
479 if type(ftype) is pytsdl.tsdl.Integer:
480 offvars_tree[fname] = base_offset + offset
481
482 sz = self._get_obj_size(ftype)
483
484 offset += sz
485
486 return offset
487
488 def _get_array_size(self, array):
489 element = array.element
490
491 # effective size of one element includes its alignment after its size
492 size = self._get_obj_size(element)
493 align = self._get_obj_alignment(element)
494
495 return self._get_alignment(size, align) * array.length
496
497 def _get_enum_size(self, enum):
498 return self._get_obj_size(enum.integer)
499
500 def _get_floating_point_size(self, floating_point):
501 return floating_point.exp_dig + floating_point.mant_dig
502
503 def _get_integer_size(self, integer):
504 return integer.size
505
506 def _get_obj_size(self, obj):
507 return self._obj_size_cb[type(obj)](obj)
508
509 def _get_struct_alignment(self, struct):
510 if struct.align is not None:
511 return struct.align
512
513 cur_align = 1
514
515 for fname, ftype in struct.fields.items():
516 cur_align = max(self._get_obj_alignment(ftype), cur_align)
517
518 return cur_align
519
520 def _get_integer_alignment(self, integer):
521 return integer.align
522
523 def _get_floating_point_alignment(self, floating_point):
524 return floating_point.align
525
526 def _get_enum_alignment(self, enum):
527 return self._get_obj_alignment(enum.integer)
528
529 def _get_string_alignment(self, string):
530 return 8
531
532 def _get_array_alignment(self, array):
533 return self._get_obj_alignment(array.element)
534
535 def _get_sequence_alignment(self, sequence):
536 return self._get_obj_alignment(sequence.element)
537
538 def _get_obj_alignment(self, obj):
539 return self._obj_alignment_cb[type(obj)](obj)
540
541 def _name_to_param_name(self, prefix, name):
542 return 'param_{}_{}'.format(prefix, name)
543
544 def _ev_f_name_to_param_name(self, name):
545 return self._name_to_param_name('evf', name)
546
547 def _ev_c_name_to_param_name(self, name):
548 return self._name_to_param_name('evc', name)
549
550 def _ev_sec_name_to_param_name(self, name):
551 return self._name_to_param_name('evsec', name)
552
553 def _ev_h_name_to_param_name(self, name):
554 return self._name_to_param_name('evh', name)
555
556 def _s_pc_name_to_param_name(self, name):
557 return self._name_to_param_name('spc', name)
558
559 def _s_ph_name_to_param_name(self, name):
560 return self._name_to_param_name('sph', name)
561
562 def _get_integer_param_ctype(self, integer):
563 signed = 'u' if not integer.signed else ''
564
565 if integer.size == 8:
566 sz = '8'
567 elif integer.size == 16:
568 sz = '16'
569 elif integer.size == 32:
570 sz = '32'
571 elif integer.size == 64:
572 sz = '64'
573 else:
574 # if the integer is signed and of uncommon size, the sign bit is
575 # at a custom position anyway so we use a 64-bit unsigned
576 signed = 'u'
577
578 if integer.signed:
579 sz = '64'
580 else:
581 if integer.size < 16:
582 sz = '8'
583 elif integer.size < 32:
584 sz = '16'
585 elif integer.size < 64:
586 sz = '32'
587 else:
588 sz = '64'
589
590 return '{}int{}_t'.format(signed, sz)
591
592 def _get_enum_param_ctype(self, enum):
593 return self._get_obj_param_ctype(enum.integer)
594
595 def _get_floating_point_param_ctype(self, fp):
596 if fp.exp_dig == 8 and fp.mant_dig == 24 and fp.align == 32:
597 return 'float'
598 elif fp.exp_dig == 11 and fp.mant_dig == 53 and fp.align == 64:
599 return 'double'
600 else:
601 return 'uint64_t'
602
603 def _get_obj_param_ctype(self, obj):
604 return self._obj_param_ctype_cb[type(obj)](obj)
605
606 def _get_chk_offset_v(self, size):
607 fmt = '{}_CHK_OFFSET_V({}, {}, {});'
608 ret = fmt.format(self._prefix.upper(), self._CTX_AT,
609 self._CTX_BUF_SIZE, size)
610
611 return ret
612
613 def _get_chk_offset_v_cline(self, size):
614 return _CLine(self._get_chk_offset_v(size))
615
616 def _get_align_offset(self, align):
617 fmt = '{}_ALIGN_OFFSET({}, {});'
618 ret = fmt.format(self._prefix.upper(), self._CTX_AT, align)
619
620 return ret
621
622 def _get_align_offset_cline(self, size):
623 return _CLine(self._get_align_offset(size))
624
625 def _write_field_struct(self, fname, src_name, struct):
626 size = self._get_struct_size(struct)
627 size_bytes = self._get_alignment(size, 8) // 8
628 dst = self._CTX_BUF_AT_ADDR
629
630 return [
631 # memcpy() is safe since barectf requires inner structures
632 # to be byte-aligned
633 self._get_chk_offset_v_cline(size),
634 _CLine('memcpy({}, {}, {});'.format(dst, src_name, size_bytes)),
635 _CLine('{} += {};'.format(self._CTX_AT, size)),
636 ]
637
638 def _write_field_integer(self, fname, src_name, integer):
639 bo = self._bo_suffixes_map[integer.byte_order]
640 ptr = self._CTX_BUF
641 t = self._get_obj_param_ctype(integer)
642 start = self._CTX_AT
643 length = self._get_obj_size(integer)
644 fmt = 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
645
646 return [
647 self._get_chk_offset_v_cline(length),
648 _CLine(fmt.format(bo, ptr, t, start, length, src_name)),
649 _CLine('{} += {};'.format(self._CTX_AT, length))
650 ]
651
652 def _write_field_enum(self, fname, src_name, enum):
653 return self._write_field_obj(fname, src_name, enum.integer)
654
655 def _write_field_floating_point(self, fname, src_name, floating_point):
656 bo = self._bo_suffixes_map[floating_point.byte_order]
657 ptr = self._CTX_BUF
658 t = self._get_obj_param_ctype(floating_point)
659 start = self._CTX_AT
660 length = self._get_obj_size(floating_point)
661 fmt = 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
662
663 return [
664 self._get_chk_offset_v_cline(length),
665 _CLine(fmt.format(bo, ptr, t, start, length, src_name)),
666 _CLine('{} += {};'.format(self._CTX_AT, length))
667 ]
668
669 def _write_field_array(self, fname, src_name, array):
670 lines = []
671
672 # array index variable declaration
673 iv = 'ia_{}'.format(fname)
674 lines.append(_CLine('uint32_t {};'.format(iv)))
675
676 # for loop using array's static length
677 line = 'for ({iv} = 0; {iv} < {l}; ++{iv}) {{'.format(iv=iv, l=array.length)
678 lines.append(_CLine(line))
679
680 # for loop statements
681 for_block = _CBlock()
682
683 # align bit index before writing to the buffer
684 element_align = self._get_obj_alignment(array.element)
685 cline = self._get_align_offset_cline(element_align)
686 for_block.append(cline)
687
688 # write element to the buffer
689 for_block += self._write_field_obj(fname, src_name, array.element)
690 lines.append(for_block)
691
692 # for loop end
693 lines.append(_CLine('}'))
694
695 return lines
696
697 def _write_field_sequence(self, fname, src_name, sequence):
698 return [
699 _CLine('would write sequence here;'),
700 ]
701
702 def _write_field_string(self, fname, src_name, string):
703 lines = []
704
705 # string index variable declaration
706 iv = 'is_{}'.format(fname)
707 lines.append(_CLine('uint32_t {};'.format(iv)))
708
709 # for loop; loop until the end of the source string is reached
710 fmt = "for ({iv} = 0; {src}[{iv}] != '\\0'; ++{iv}, {ctxat} += 8) {{"
711 line = fmt.format(iv=iv, src=src_name, ctxat=self._CTX_AT)
712 lines.append(_CLine(line))
713
714 # for loop statements
715 for_block = _CBlock()
716
717 # check offset overflow
718 for_block.append(self._get_chk_offset_v_cline(8))
719
720 # write byte to the buffer
721 fmt = '{dst} = {src}[{iv}];'
722 line = fmt.format(dst=self._CTX_BUF_AT, iv=iv, src=src_name)
723 for_block.append(_CLine(line))
724
725 # append for loop
726 lines.append(for_block)
727 lines.append(_CLine('}'))
728
729 # write NULL character to the buffer
730 lines.append(_CLine("{} = '\\0';".format(self._CTX_BUF_AT)))
731 lines.append(_CLine('{} += 8;'.format(self._CTX_AT)))
732
733 return lines
734
735 def _write_field_obj(self, fname, src_name, ftype):
736 return self._write_field_obj_cb[type(ftype)](fname, src_name, ftype)
737
738 def _get_offvar_name(self, name):
739 return 'off_{}'.format(name)
740
741 def _get_offvar_name_from_expr(self, expr):
742 return self._get_offvar_name('_'.join(expr))
743
744 def _field_to_cline(self, fname, ftype, scope_name, param_name_cb):
745 lines = []
746 pname = param_name_cb(fname)
747 align = self._get_obj_alignment(ftype)
748
749 # group comment
750 fmt = '/* write {} field "{}" ({}) */'
751 line = fmt.format(scope_name, fname,
752 self._tsdl_type_names_map[type(ftype)])
753 lines.append(_CLine(line))
754
755 # align bit index before writing to the buffer
756 cline = self._get_align_offset_cline(align)
757 lines.append(cline)
758
759 # write offset variables
760 if type(ftype) is pytsdl.tsdl.Struct:
761 offvars_tree = collections.OrderedDict()
762 self._get_struct_size(ftype, offvars_tree)
763 off_vars = self._offvars_tree_to_vars(offvars_tree)
764
765 # as many offset as there are child fields because a future
766 # sequence could refer to any of those fields
767 for lname, offset in off_vars.items():
768 offvar = self._get_offvar_name('_'.join([fname, lname]))
769 fmt = 'uint32_t {} = {} + {};'
770 line = fmt.format(offvar, self._CTX_AT, offset);
771 lines.append(_CLine(line))
772 elif type(ftype) is pytsdl.tsdl.Integer:
773 # offset of this simple field is the current bit index
774 offvar = self._get_offvar_name(fname)
775 line = 'uint32_t {} = {};'.format(offvar, self._CTX_AT)
776 lines.append(_CLine(line))
777
778 lines += self._write_field_obj(fname, pname, ftype)
779
780 return lines
781
782 def _struct_to_clines(self, struct, scope_name, param_name_cb):
783 line_groups = []
784
785 for fname, ftype in struct.fields.items():
786 lines = self._field_to_cline(fname, ftype, scope_name,
787 param_name_cb)
788 line_groups.append(lines)
789
790 if not line_groups:
791 return line_groups
792
793 output_lines = line_groups[0]
794
795 for lines in line_groups[1:]:
796 output_lines.append('')
797 output_lines += lines
798
799 return output_lines
800
801 def _cblock_to_source_lines(self, cblock, indent=1):
802 src = []
803 indentstr = '\t' * indent
804
805 for line in cblock:
806 if type(line) is _CBlock:
807 src += self._cblock_to_source_lines(line, indent + 1)
808 else:
809 src.append(indentstr + line)
810
811 return src
812
813 def _cblock_to_source(self, cblock, indent=1):
814 lines = self._cblock_to_source_lines(cblock, indent)
815
816 return '\n'.join(lines)
817
818 def gen_barectf(self, metadata, output, prefix, static_inline,
819 manual_clock):
820 self._metadata = metadata
821 self._output = output
822 self._prefix = prefix
823 self._static_inline = static_inline
824 self._manual_clock = manual_clock
825
826 # open CTF metadata file
827 try:
828 with open(metadata) as f:
829 self._tsdl = f.read()
830 except:
831 _perror('cannot open/read CTF metadata file "{}"'.format(metadata))
832
833 # parse CTF metadata
834 try:
835 self._doc = self._parser.parse(self._tsdl)
836 except pytsdl.parser.ParseError as e:
837 _perror('parse error: {}'.format(e))
838
839 # validate CTF metadata against barectf constraints
840 self._validate_metadata()
841
842 clines = self._struct_to_clines(self._doc.streams[0].get_event(0).fields,
843 'stream event context',
844 self._ev_f_name_to_param_name)
845 source = self._cblock_to_source(clines)
846 print(source)
847
848
849 def run():
850 args = _parse_args()
851 generator = BarectfCodeGenerator()
852 generator.gen_barectf(args.metadata, args.output, args.prefix,
853 args.static_inline, args.manual_clock)
This page took 0.049036 seconds and 5 git commands to generate.