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