Create generator class, fix a few things
[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 name, 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(name))
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(name))
163 elif type(element) is pytsdl.tsdl.Variant:
164 raise RuntimeError('field "{}" contains a variant (unsupported)'.format(name))
165 elif type(element) is pytsdl.tsdl.String:
166 raise RuntimeError('field "{}" contains a string (not allowed here)'.format(name))
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(name))
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(name))
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(name))
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(name))
187 elif type(ftype) is pytsdl.tsdl.String:
188 raise RuntimeError('field "{}" is a string (not allowed here)'.format(name))
189 elif type(ftype) is pytsdl.tsdl.Struct:
190 _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(name))
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(name))
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(name))
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 name, ftype in struct.fields.items():
206 if type(ftype) is pytsdl.tsdl.Variant:
207 raise RuntimeError('field "{}" is a variant (unsupported)'.format(name))
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
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 {}: "timestamp": {}'.format(sid, format(e)))
366
367 def _validate_stream_event_context(self, stream):
368 stream_event_context = stream.event_context
369 sid = stream.id
370
371 if stream_event_context is None:
372 return
373
374 try:
375 self._validate_context_field(stream_event_context)
376 except RuntimeError as e:
377 _perror('stream {}: event context: {}'.format(sid, e))
378
379 def _validate_all_scopes(self):
380 # packet header
381 self._validate_packet_header(self._doc.trace.packet_header)
382
383 # stream stuff
384 for stream_id, stream in self._doc.streams.items():
385 self._validate_event_header(stream)
386 self._validate_packet_context(stream)
387 self._validate_stream_event_context(stream)
388
389 def _validate_metadata(self):
390 self._validate_all_scopes()
391
392 # 3, 4 -> 4
393 # 4, 4 -> 4
394 # 5, 4 -> 8
395 # 6, 4 -> 8
396 # 7, 4 -> 8
397 # 8, 4 -> 8
398 # 9, 4 -> 12
399 def _get_alignment(self, at, align):
400 return (at + align - 1) & -align
401
402 # this converts a tree of offset variables:
403 #
404 # field
405 # a -> 0
406 # b -> 8
407 # other_struct
408 # field -> 16
409 # yeah -> 20
410 # c -> 32
411 # len -> 36
412 #
413 # to a flat dict:
414 #
415 # field_a -> 0
416 # field_b -> 8
417 # field_other_struct_field -> 16
418 # field_other_struct_yeah -> 20
419 # field_c -> 32
420 # len -> 36
421 def _offvars_tree_to_vars(self, offvars_tree, prefix=None,
422 off_vars=collections.OrderedDict()):
423 for name, offset in offvars_tree.items():
424 if prefix is not None:
425 varname = '{}_{}'.format(prefix, name)
426 else:
427 varname = name
428
429 if isinstance(offset, dict):
430 self._offvars_tree_to_vars(offset, varname, off_vars)
431 else:
432 off_vars[varname] = offset
433
434 return off_vars
435
436 # returns the size of a struct with _static size_
437 def _get_struct_size(self, struct,
438 offvars_tree=collections.OrderedDict(),
439 base_offset=0):
440 offset = 0
441
442 for fname, ftype in struct.fields.items():
443 field_alignment = self._get_obj_alignment(ftype)
444 offset = self._get_alignment(offset, field_alignment)
445
446 if type(ftype) is pytsdl.tsdl.Struct:
447 offvars_tree[fname] = collections.OrderedDict()
448 sz = self._get_struct_size(ftype, offvars_tree[fname],
449 base_offset + offset)
450 else:
451 # only integers may act as sequence lengths
452 if type(ftype) is pytsdl.tsdl.Integer:
453 offvars_tree[fname] = base_offset + offset
454
455 sz = self._get_obj_size(ftype)
456
457 offset += sz
458
459 return offset
460
461 def _get_array_size(self, array):
462 element = array.element
463
464 # effective size of one element includes its alignment after its size
465 size = self._get_obj_size(element)
466 align = self._get_obj_alignment(element)
467
468 return self._get_alignment(size, align) * array.length
469
470 def _get_enum_size(self, enum):
471 return self._get_obj_size(enum.integer)
472
473 def _get_floating_point_size(self, floating_point):
474 return floating_point.exp_dig + floating_point.mant_dig
475
476 def _get_integer_size(self, integer):
477 return integer.size
478
479 def _get_obj_size(self, obj):
480 return self._obj_size_cb[type(obj)](obj)
481
482 def _get_struct_alignment(self, struct):
483 if struct.align is not None:
484 return struct.align
485
486 cur_align = 1
487
488 for fname, ftype in struct.fields.items():
489 cur_align = max(self._get_obj_alignment(ftype), cur_align)
490
491 return cur_align
492
493 def _get_integer_alignment(self, integer):
494 return integer.align
495
496 def _get_floating_point_alignment(self, floating_point):
497 return floating_point.align
498
499 def _get_enum_alignment(self, enum):
500 return self._get_obj_alignment(enum.integer)
501
502 def _get_string_alignment(self, string):
503 return 8
504
505 def _get_array_alignment(self, array):
506 return self._get_obj_alignment(array.element)
507
508 def _get_sequence_alignment(self, sequence):
509 return self._get_obj_alignment(sequence.element)
510
511 def _get_obj_alignment(self, obj):
512 return self._obj_alignment_cb[type(obj)](obj)
513
514 def _name_to_param_name(self, prefix, name):
515 return 'param_{}_{}'.format(prefix, name)
516
517 def _ev_f_name_to_param_name(self, name):
518 return self._name_to_param_name('evf', name)
519
520 def _ev_c_name_to_param_name(self, name):
521 return self._name_to_param_name('evc', name)
522
523 def _ev_sec_name_to_param_name(self, name):
524 return self._name_to_param_name('evsec', name)
525
526 def _ev_h_name_to_param_name(self, name):
527 return self._name_to_param_name('evh', name)
528
529 def _s_pc_name_to_param_name(self, name):
530 return self._name_to_param_name('spc', name)
531
532 def _s_ph_name_to_param_name(self, name):
533 return self._name_to_param_name('sph', name)
534
535 def _get_integer_param_ctype(self, integer):
536 signed = 'u' if not integer.signed else ''
537
538 if integer.size == 8:
539 sz = '8'
540 elif integer.size == 16:
541 sz = '16'
542 elif integer.size == 32:
543 sz = '32'
544 elif integer.size == 64:
545 sz = '64'
546 else:
547 # if the integer is signed and of uncommon size, the sign bit is
548 # at a custom position anyway so we use a 64-bit unsigned
549 signed = 'u'
550
551 if integer.signed:
552 sz = '64'
553 else:
554 if integer.size < 16:
555 sz = '8'
556 elif integer.size < 32:
557 sz = '16'
558 elif integer.size < 64:
559 sz = '32'
560 else:
561 sz = '64'
562
563 return '{}int{}_t'.format(signed, sz)
564
565 def _get_enum_param_ctype(self, enum):
566 return self._get_obj_param_ctype(enum.integer)
567
568 def _get_floating_point_param_ctype(self, fp):
569 if fp.exp_dig == 8 and fp.mant_dig == 24 and fp.align == 32:
570 return 'float'
571 elif fp.exp_dig == 11 and fp.mant_dig == 53 and fp.align == 64:
572 return 'double'
573 else:
574 return 'uint64_t'
575
576 def _get_obj_param_ctype(self, obj):
577 return self._obj_param_ctype_cb[type(obj)](obj)
578
579 def _get_chk_offset_v(self, size):
580 fmt = '{}_CHK_OFFSET_V({}, {}, {});'
581 ret = fmt.format(self._prefix.upper(), self._CTX_AT,
582 self._CTX_BUF_SIZE, size)
583
584 return ret
585
586 def _get_chk_offset_v_cline(self, size):
587 return _CLine(self._get_chk_offset_v(size))
588
589 def _get_align_offset(self, align):
590 fmt = '{}_ALIGN_OFFSET({}, {});'
591 ret = fmt.format(self._prefix.upper(), self._CTX_AT, align)
592
593 return ret
594
595 def _get_align_offset_cline(self, size):
596 return _CLine(self._get_align_offset(size))
597
598 def _write_field_struct(self, fname, src_name, struct):
599 size = self._get_struct_size(struct)
600 size_bytes = self._get_alignment(size, 8) // 8
601 dst = self._CTX_BUF_AT_ADDR
602
603 return [
604 # memcpy() is safe since barectf requires inner structures
605 # to be byte-aligned
606 self._get_chk_offset_v_cline(size),
607 _CLine('memcpy({}, {}, {});'.format(dst, src_name, size_bytes)),
608 _CLine('{} += {};'.format(self._CTX_AT, size)),
609 ]
610
611 def _write_field_integer(self, fname, src_name, integer):
612 bo = self._bo_suffixes_map[integer.byte_order]
613 ptr = self._CTX_BUF
614 t = self._get_obj_param_ctype(integer)
615 start = self._CTX_AT
616 length = self._get_obj_size(integer)
617 fmt = 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
618
619 return [
620 self._get_chk_offset_v_cline(length),
621 _CLine(fmt.format(bo, ptr, t, start, length, src_name)),
622 _CLine('{} += {};'.format(self._CTX_AT, length))
623 ]
624
625 def _write_field_enum(self, fname, src_name, enum):
626 return self._write_field_obj(fname, src_name, enum.integer)
627
628 def _write_field_floating_point(self, fname, src_name, floating_point):
629 bo = self._bo_suffixes_map[floating_point.byte_order]
630 ptr = self._CTX_BUF
631 t = self._get_obj_param_ctype(floating_point)
632 start = self._CTX_AT
633 length = self._get_obj_size(floating_point)
634 fmt = 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
635
636 return [
637 self._get_chk_offset_v_cline(length),
638 _CLine(fmt.format(bo, ptr, t, start, length, src_name)),
639 _CLine('{} += {};'.format(self._CTX_AT, length))
640 ]
641
642 def _write_field_array(self, fname, src_name, array):
643 lines = []
644
645 # array index variable declaration
646 iv = 'ia_{}'.format(fname)
647 lines.append(_CLine('uint32_t {};'.format(iv)))
648
649 # for loop using array's static length
650 line = 'for ({iv} = 0; {iv} < {l}; ++{iv}) {{'.format(iv=iv, l=array.length)
651 lines.append(_CLine(line))
652
653 # for loop statements
654 for_block = _CBlock()
655
656 # align bit index before writing to the buffer
657 element_align = self._get_obj_alignment(array.element)
658 cline = self._get_align_offset_cline(element_align)
659 for_block.append(cline)
660
661 # write element to the buffer
662 for_block += self._write_field_obj(fname, src_name, array.element)
663 lines.append(for_block)
664
665 # for loop end
666 lines.append(_CLine('}'))
667
668 return lines
669
670 def _write_field_sequence(self, fname, src_name, sequence):
671 return [
672 _CLine('would write sequence here;'),
673 ]
674
675 def _write_field_string(self, fname, src_name, string):
676 lines = []
677
678 # string index variable declaration
679 iv = 'is_{}'.format(fname)
680 lines.append(_CLine('uint32_t {};'.format(iv)))
681
682 # for loop; loop until the end of the source string is reached
683 fmt = "for ({iv} = 0; {src}[{iv}] != '\\0'; ++{iv}, {ctxat} += 8) {{"
684 line = fmt.format(iv=iv, src=src_name, ctxat=self._CTX_AT)
685 lines.append(_CLine(line))
686
687 # for loop statements
688 for_block = _CBlock()
689
690 # check offset overflow
691 for_block.append(self._get_chk_offset_v_cline(8))
692
693 # write byte to the buffer
694 fmt = '{dst} = {src}[{iv}];'
695 line = fmt.format(dst=self._CTX_BUF_AT, iv=iv, src=src_name)
696 for_block.append(_CLine(line))
697
698 # append for loop
699 lines.append(for_block)
700 lines.append(_CLine('}'))
701
702 # write NULL character to the buffer
703 lines.append(_CLine("{} = '\\0';".format(self._CTX_BUF_AT)))
704 lines.append(_CLine('{} += 8;'.format(self._CTX_AT)))
705
706 return lines
707
708 def _write_field_obj(self, fname, src_name, ftype):
709 return self._write_field_obj_cb[type(ftype)](fname, src_name, ftype)
710
711 def _get_offvar_name(self, name):
712 return 'off_{}'.format(name)
713
714 def _get_offvar_name_from_expr(self, expr):
715 return self._get_offvar_name('_'.join(expr))
716
717 def _field_to_cline(self, fname, ftype, scope_name, param_name_cb):
718 lines = []
719 pname = param_name_cb(fname)
720 align = self._get_obj_alignment(ftype)
721
722 # group comment
723 fmt = '/* write {} field "{}" ({}) */'
724 line = fmt.format(scope_name, fname,
725 self._tsdl_type_names_map[type(ftype)])
726 lines.append(_CLine(line))
727
728 # align bit index before writing to the buffer
729 cline = self._get_align_offset_cline(align)
730 lines.append(cline)
731
732 # write offset variables
733 if type(ftype) is pytsdl.tsdl.Struct:
734 offvars_tree = collections.OrderedDict()
735 self._get_struct_size(ftype, offvars_tree)
736 off_vars = self._offvars_tree_to_vars(offvars_tree)
737
738 # as many offset as there are child fields because a future
739 # sequence could refer to any of those fields
740 for lname, offset in off_vars.items():
741 print(fname, lname)
742 offvar = self._get_offvar_name('_'.join([fname, lname]))
743 fmt = 'uint32_t {} = {} + {};'
744 line = fmt.format(offvar, self._CTX_AT, offset);
745 lines.append(_CLine(line))
746 elif type(ftype) is pytsdl.tsdl.Integer:
747 # offset of this simple field is the current bit index
748 offvar = self._get_offvar_name(fname)
749 line = 'uint32_t {} = {};'.format(offvar, self._CTX_AT)
750 lines.append(_CLine(line))
751
752 lines += self._write_field_obj(fname, pname, ftype)
753
754 return lines
755
756 def _struct_to_clines(self, struct, scope_name, param_name_cb):
757 line_groups = []
758
759 for fname, ftype in struct.fields.items():
760 lines = self._field_to_cline(fname, ftype, scope_name,
761 param_name_cb)
762 line_groups.append(lines)
763
764 if not line_groups:
765 return line_groups
766
767 output_lines = line_groups[0]
768
769 for lines in line_groups[1:]:
770 output_lines.append('')
771 output_lines += lines
772
773 return output_lines
774
775 def _cblock_to_source_lines(self, cblock, indent=1):
776 src = []
777 indentstr = '\t' * indent
778
779 for line in cblock:
780 if type(line) is _CBlock:
781 src += self._cblock_to_source_lines(line, indent + 1)
782 else:
783 src.append(indentstr + line)
784
785 return src
786
787 def _cblock_to_source(self, cblock, indent=1):
788 lines = self._cblock_to_source_lines(cblock, indent)
789
790 return '\n'.join(lines)
791
792 def gen_barectf(self, metadata, output, prefix, static_inline,
793 manual_clock):
794 self._metadata = metadata
795 self._output = output
796 self._prefix = prefix
797 self._static_inline = static_inline
798 self._manual_clock = manual_clock
799
800 # open CTF metadata file
801 try:
802 with open(metadata) as f:
803 self._tsdl = f.read()
804 except:
805 _perror('cannot open/read CTF metadata file "{}"'.format(metadata))
806
807 # parse CTF metadata
808 try:
809 self._doc = self._parser.parse(self._tsdl)
810 except pytsdl.parser.ParseError as e:
811 _perror('parse error: {}'.format(e))
812
813 # validate CTF metadata against barectf constraints
814 self._validate_metadata()
815
816 clines = self._struct_to_clines(self._doc.streams[0].get_event(0).fields,
817 'stream event context',
818 self._ev_f_name_to_param_name)
819 source = self._cblock_to_source(_CBlock(clines))
820
821
822 def run():
823 args = _parse_args()
824 generator = BarectfCodeGenerator()
825 generator.gen_barectf(args.metadata, args.output, args.prefix,
826 args.static_inline, args.manual_clock)
This page took 0.048271 seconds and 5 git commands to generate.