Open function body
[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 _CTX_CALL_CLOCK_CB = 'ctx->clock_cb(ctx->clock_cb_data)'
91
92 _bo_suffixes_map = {
93 pytsdl.tsdl.ByteOrder.BE: 'be',
94 pytsdl.tsdl.ByteOrder.LE: 'le',
95 }
96
97 _tsdl_type_names_map = {
98 pytsdl.tsdl.Integer: 'integer',
99 pytsdl.tsdl.FloatingPoint: 'floating point',
100 pytsdl.tsdl.Enum: 'enumeration',
101 pytsdl.tsdl.String: 'string',
102 pytsdl.tsdl.Array: 'static array',
103 pytsdl.tsdl.Sequence: 'dynamic array',
104 pytsdl.tsdl.Struct: 'structure',
105 }
106
107 def __init__(self):
108 self._parser = pytsdl.parser.Parser()
109 self._obj_size_cb = {
110 pytsdl.tsdl.Struct: self._get_struct_size,
111 pytsdl.tsdl.Integer: self._get_integer_size,
112 pytsdl.tsdl.Enum: self._get_enum_size,
113 pytsdl.tsdl.FloatingPoint: self._get_floating_point_size,
114 pytsdl.tsdl.Array: self._get_array_size,
115 }
116 self._obj_alignment_cb = {
117 pytsdl.tsdl.Struct: self._get_struct_alignment,
118 pytsdl.tsdl.Integer: self._get_integer_alignment,
119 pytsdl.tsdl.Enum: self._get_enum_alignment,
120 pytsdl.tsdl.FloatingPoint: self._get_floating_point_alignment,
121 pytsdl.tsdl.Array: self._get_array_alignment,
122 pytsdl.tsdl.Sequence: self._get_sequence_alignment,
123 pytsdl.tsdl.String: self._get_string_alignment,
124 }
125 self._obj_param_ctype_cb = {
126 pytsdl.tsdl.Struct: lambda obj: 'const void*',
127 pytsdl.tsdl.Integer: self._get_integer_param_ctype,
128 pytsdl.tsdl.Enum: self._get_enum_param_ctype,
129 pytsdl.tsdl.FloatingPoint: self._get_floating_point_param_ctype,
130 pytsdl.tsdl.Array: lambda obj: 'const void*',
131 pytsdl.tsdl.Sequence: lambda obj: 'const void*',
132 pytsdl.tsdl.String: lambda obj: 'const char*',
133 }
134 self._write_field_obj_cb = {
135 pytsdl.tsdl.Struct: self._write_field_struct,
136 pytsdl.tsdl.Integer: self._write_field_integer,
137 pytsdl.tsdl.Enum: self._write_field_enum,
138 pytsdl.tsdl.FloatingPoint: self._write_field_floating_point,
139 pytsdl.tsdl.Array: self._write_field_array,
140 pytsdl.tsdl.Sequence: self._write_field_sequence,
141 pytsdl.tsdl.String: self._write_field_string,
142 }
143
144 # TODO: prettify this function
145 def _validate_struct(self, struct):
146 # just in case we call this with the wrong type
147 if type(struct) is not pytsdl.tsdl.Struct:
148 raise RuntimeError('expecting a struct')
149
150 # make sure inner structures are at least byte-aligned
151 if self._get_obj_alignment(struct) < 8:
152 raise RuntimeError('inner struct must be at least byte-aligned')
153
154 # check each field
155 for fname, ftype in struct.fields.items():
156 if type(ftype) is pytsdl.tsdl.Sequence:
157 raise RuntimeError('field "{}" is a dynamic array (not allowed here)'.format(fname))
158 elif type(ftype) is pytsdl.tsdl.Array:
159 # we need to check every element until we find a terminal one
160 element = ftype.element
161
162 while True:
163 if type(element) is pytsdl.tsdl.Sequence:
164 raise RuntimeError('field "{}" contains a dynamic array (not allowed here)'.format(fname))
165 elif type(element) is pytsdl.tsdl.Variant:
166 raise RuntimeError('field "{}" contains a variant (unsupported)'.format(fname))
167 elif type(element) is pytsdl.tsdl.String:
168 raise RuntimeError('field "{}" contains a string (not allowed here)'.format(fname))
169 elif type(element) is pytsdl.tsdl.Struct:
170 _validate_struct(element)
171 elif type(element) is pytsdl.tsdl.Integer:
172 if self._get_obj_size(element) > 64:
173 raise RuntimeError('integer field "{}" larger than 64-bit'.format(fname))
174 elif type(element) is pytsdl.tsdl.FloatingPoint:
175 if self._get_obj_size(element) > 64:
176 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(fname))
177 elif type(element) is pytsdl.tsdl.Enum:
178 if self._get_obj_size(element) > 64:
179 raise RuntimeError('enum field "{}" larger than 64-bit'.format(fname))
180
181 if type(element) is pytsdl.tsdl.Array:
182 # still an array, continue
183 element = element.element
184 else:
185 # found the terminal element
186 break
187 elif type(ftype) is pytsdl.tsdl.Variant:
188 raise RuntimeError('field "{}" is a variant (unsupported)'.format(fname))
189 elif type(ftype) is pytsdl.tsdl.String:
190 raise RuntimeError('field "{}" is a string (not allowed here)'.format(fname))
191 elif type(ftype) is pytsdl.tsdl.Struct:
192 self._validate_struct(ftype)
193 elif type(ftype) is pytsdl.tsdl.Integer:
194 if self._get_obj_size(ftype) > 64:
195 raise RuntimeError('integer field "{}" larger than 64-bit'.format(fname))
196 elif type(ftype) is pytsdl.tsdl.FloatingPoint:
197 if self._get_obj_size(ftype) > 64:
198 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(fname))
199 elif type(ftype) is pytsdl.tsdl.Enum:
200 if self._get_obj_size(ftype) > 64:
201 raise RuntimeError('enum field "{}" larger than 64-bit'.format(fname))
202
203 def _validate_context_fields(self, struct):
204 if type(struct) is not pytsdl.tsdl.Struct:
205 raise RuntimeError('expecting a struct')
206
207 for fname, ftype in struct.fields.items():
208 if type(ftype) is pytsdl.tsdl.Variant:
209 raise RuntimeError('field "{}" is a variant (unsupported)'.format(fname))
210 elif type(ftype) is pytsdl.tsdl.Struct:
211 # validate inner structure against barectf constraints
212 self._validate_struct(ftype)
213
214 def _validate_integer(self, integer, size=None, align=None,
215 signed=None):
216 if type(integer) is not pytsdl.tsdl.Integer:
217 raise RuntimeError('expected integer')
218
219 if size is not None:
220 if integer.size != size:
221 raise RuntimeError('expected {}-bit integer'.format(size))
222
223 if align is not None:
224 if integer.align != align:
225 raise RuntimeError('expected integer with {}-bit alignment'.format(align))
226
227 if signed is not None:
228 if integer.signed != signed:
229 raise RuntimeError('expected {} integer'.format('signed' if signed else 'unsigned'))
230
231 def _validate_packet_header(self, packet_header):
232 try:
233 self._validate_struct(packet_header)
234 except RuntimeError as e:
235 _perror('packet header: {}'.format(e))
236
237 # magic must be the first field
238 if 'magic' in packet_header.fields:
239 if list(packet_header.fields.keys())[0] != 'magic':
240 _perror('packet header: "magic" must be the first field')
241 else:
242 _perror('packet header: missing "magic" field')
243
244 # magic must be a 32-bit unsigned integer, 32-bit aligned
245 try:
246 self._validate_integer(packet_header['magic'], 32, 32, False)
247 except RuntimeError as e:
248 _perror('packet header: "magic": {}'.format(e))
249
250 # mandatory stream_id
251 if 'stream_id' not in packet_header.fields:
252 _perror('packet header: missing "stream_id" field')
253
254 # stream_id must be an unsigned integer
255 try:
256 self._validate_integer(packet_header['stream_id'], signed=False)
257 except RuntimeError as e:
258 _perror('packet header: "stream_id": {}'.format(e))
259
260 # only magic and stream_id allowed
261 if len(packet_header.fields) != 2:
262 _perror('packet header: only "magic" and "stream_id" fields are allowed')
263
264 def _dot_name_to_str(self, name):
265 return '.'.join(name)
266
267 def _compare_integers(self, int1, int2):
268 if type(int1) is not pytsdl.tsdl.Integer:
269 return False
270
271 if type(int2) is not pytsdl.tsdl.Integer:
272 return False
273
274 size = int1.size == int2.size
275 align = int1.align == int2.align
276 cmap = int1.map == int2.map
277 base = int1.base == int2.base
278 encoding = int1.encoding == int2.encoding
279 signed = int1.signed == int2.signed
280 comps = (size, align, cmap, base, encoding, signed)
281
282 # True means 1 for sum()
283 return sum(comps) == len(comps)
284
285 def _validate_packet_context(self, stream):
286 packet_context = stream.packet_context
287 sid = stream.id
288
289 try:
290 self._validate_struct(packet_context)
291 except RuntimeError as e:
292 _perror('stream {}: packet context: {}'.format(sid, e))
293
294 fields = packet_context.fields
295
296 # if timestamp_begin exists, timestamp_end must exist
297 if 'timestamp_begin' in fields or 'timestamp_end' in fields:
298 if 'timestamp_begin' not in fields or 'timestamp_end' not in fields:
299 _perror('stream {}: packet context: "timestamp_begin" must exist if "timestamp_end" exists'.format(sid))
300 else:
301 # timestamp_begin and timestamp_end must have the same integer
302 # as the event header's timestamp field (should exist by now)
303 timestamp = stream.event_header['timestamp']
304
305 if not self._compare_integers(fields['timestamp_begin'], timestamp):
306 _perror('stream {}: packet context: "timestamp_begin": integer type different from event header\'s "timestamp" field'.format(sid))
307
308 if not self._compare_integers(fields['timestamp_end'], timestamp):
309 _perror('stream {}: packet context: "timestamp_end": integer type different from event header\'s "timestamp" field'.format(sid))
310
311 # content_size must exist and be an unsigned integer
312 if 'content_size' not in fields:
313 _perror('stream {}: packet context: missing "content_size" field'.format(sid))
314
315 try:
316 self._validate_integer(fields['content_size'], 32, 32, False)
317 except:
318 try:
319 self._validate_integer(fields['content_size'], 64, 64, False)
320 except:
321 _perror('stream {}: packet context: "content_size": expecting unsigned 32-bit/64-bit integer'.format(sid))
322
323 # packet_size must exist and be an unsigned integer
324 if 'packet_size' not in fields:
325 _perror('stream {}: packet context: missing "packet_size" field'.format(sid))
326
327 try:
328 self._validate_integer(fields['packet_size'], 32, 32, False)
329 except:
330 try:
331 self._validate_integer(fields['packet_size'], 64, 64, False)
332 except:
333 _perror('stream {}: packet context: "packet_size": expecting unsigned 32-bit/64-bit integer'.format(sid))
334
335 # if cpu_id exists, must be an unsigned integer
336 if 'cpu_id' in fields:
337 try:
338 self._validate_integer(fields['cpu_id'], signed=False)
339 except RuntimeError as e:
340 _perror('stream {}: packet context: "cpu_id": {}'.format(sid, e))
341
342 def _validate_event_header(self, stream):
343 event_header = stream.event_header
344 sid = stream.id
345
346 try:
347 self._validate_struct(event_header)
348 except RuntimeError as e:
349 _perror('stream {}: event header: {}'.format(sid, e))
350
351 fields = event_header.fields
352
353 # id must exist and be an unsigned integer
354 if 'id' not in fields:
355 _perror('stream {}: event header: missing "id" field'.format(sid))
356
357 try:
358 self._validate_integer(fields['id'], signed=False)
359 except RuntimeError as e:
360 _perror('stream {}: "id": {}'.format(sid, format(e)))
361
362
363 # timestamp must exist, be an unsigned integer and be mapped to a valid clock
364 if 'timestamp' not in fields:
365 _perror('stream {}: event header: missing "timestamp" field'.format(sid))
366
367 try:
368 self._validate_integer(fields['timestamp'], signed=False)
369 except RuntimeError as e:
370 _perror('stream {}: event header: "timestamp": {}'.format(sid, format(e)))
371
372 if fields['timestamp'].map is None:
373 _perror('stream {}: event header: "timestamp" must be mapped to a valid clock'.format(sid))
374
375 # id must be the first field, followed by timestamp
376 if list(fields.keys())[0] != 'id':
377 _perror('stream {}: event header: "id" must be the first field'.format(sid))
378
379 if list(fields.keys())[1] != 'timestamp':
380 _perror('stream {}: event header: "timestamp" must be the second field'.format(sid))
381
382 # only id and timestamp and allowed in event header
383 if len(fields) != 2:
384 _perror('stream {}: event header: only "id" and "timestamp" fields are allowed'.format(sid))
385
386 def _validate_stream_event_context(self, stream):
387 stream_event_context = stream.event_context
388 sid = stream.id
389
390 if stream_event_context is None:
391 return
392
393 try:
394 self._validate_context_fields(stream_event_context)
395 except RuntimeError as e:
396 _perror('stream {}: event context: {}'.format(sid, e))
397
398 def _validate_event_context(self, stream, event):
399 event_context = event.context
400 sid = stream.id
401 eid = event.id
402
403 if event_context is None:
404 return
405
406 try:
407 self._validate_context_fields(event_context)
408 except RuntimeError as e:
409 _perror('stream {}: event {}: context: {}'.format(sid, eid, e))
410
411 def _validate_event_fields(self, stream, event):
412 event_fields = event.fields
413 sid = stream.id
414 eid = event.id
415
416 try:
417 self._validate_context_fields(event_fields)
418 except RuntimeError as e:
419 _perror('stream {}: event {}: fields: {}'.format(sid, eid, e))
420
421 def _validate_all_scopes(self):
422 # packet header
423 self._validate_packet_header(self._doc.trace.packet_header)
424
425 # stream stuff
426 for stream in self._doc.streams.values():
427 self._validate_event_header(stream)
428 self._validate_packet_context(stream)
429 self._validate_stream_event_context(stream)
430
431 # event stuff
432 for event in stream.events:
433 self._validate_event_context(stream, event)
434 self._validate_event_fields(stream, event)
435
436 def _validate_metadata(self):
437 self._validate_all_scopes()
438
439 # 3, 4 -> 4
440 # 4, 4 -> 4
441 # 5, 4 -> 8
442 # 6, 4 -> 8
443 # 7, 4 -> 8
444 # 8, 4 -> 8
445 # 9, 4 -> 12
446 def _get_alignment(self, at, align):
447 return (at + align - 1) & -align
448
449 # this converts a tree of offset variables:
450 #
451 # field
452 # a -> 0
453 # b -> 8
454 # other_struct
455 # field -> 16
456 # yeah -> 20
457 # c -> 32
458 # len -> 36
459 #
460 # to a flat dict:
461 #
462 # field_a -> 0
463 # field_b -> 8
464 # field_other_struct_field -> 16
465 # field_other_struct_yeah -> 20
466 # field_c -> 32
467 # len -> 36
468 def _flatten_offvars_tree(self, offvars_tree, prefix=None,
469 offvars=None):
470 if offvars is None:
471 offvars = collections.OrderedDict()
472
473 for name, offset in offvars_tree.items():
474 if prefix is not None:
475 varname = '{}_{}'.format(prefix, name)
476 else:
477 varname = name
478
479 if isinstance(offset, dict):
480 self._flatten_offvars_tree(offset, varname, offvars)
481 else:
482 offvars[varname] = offset
483
484 return offvars
485
486 # returns the size of a struct with _static size_
487 def _get_struct_size(self, struct,
488 offvars_tree=None,
489 base_offset=0):
490 if offvars_tree is None:
491 offvars_tree = collections.OrderedDict()
492
493 offset = 0
494
495 for fname, ftype in struct.fields.items():
496 field_alignment = self._get_obj_alignment(ftype)
497 offset = self._get_alignment(offset, field_alignment)
498
499 if type(ftype) is pytsdl.tsdl.Struct:
500 offvars_tree[fname] = collections.OrderedDict()
501 sz = self._get_struct_size(ftype, offvars_tree[fname],
502 base_offset + offset)
503 else:
504 # only integers may act as sequence lengths
505 if type(ftype) is pytsdl.tsdl.Integer:
506 offvars_tree[fname] = base_offset + offset
507
508 sz = self._get_obj_size(ftype)
509
510 offset += sz
511
512 return offset
513
514 def _get_array_size(self, array):
515 element = array.element
516
517 # effective size of one element includes its alignment after its size
518 size = self._get_obj_size(element)
519 align = self._get_obj_alignment(element)
520
521 return self._get_alignment(size, align) * array.length
522
523 def _get_enum_size(self, enum):
524 return self._get_obj_size(enum.integer)
525
526 def _get_floating_point_size(self, floating_point):
527 return floating_point.exp_dig + floating_point.mant_dig
528
529 def _get_integer_size(self, integer):
530 return integer.size
531
532 def _get_obj_size(self, obj):
533 return self._obj_size_cb[type(obj)](obj)
534
535 def _get_struct_alignment(self, struct):
536 if struct.align is not None:
537 return struct.align
538
539 cur_align = 1
540
541 for fname, ftype in struct.fields.items():
542 cur_align = max(self._get_obj_alignment(ftype), cur_align)
543
544 return cur_align
545
546 def _get_integer_alignment(self, integer):
547 return integer.align
548
549 def _get_floating_point_alignment(self, floating_point):
550 return floating_point.align
551
552 def _get_enum_alignment(self, enum):
553 return self._get_obj_alignment(enum.integer)
554
555 def _get_string_alignment(self, string):
556 return 8
557
558 def _get_array_alignment(self, array):
559 return self._get_obj_alignment(array.element)
560
561 def _get_sequence_alignment(self, sequence):
562 return self._get_obj_alignment(sequence.element)
563
564 def _get_obj_alignment(self, obj):
565 return self._obj_alignment_cb[type(obj)](obj)
566
567 def _name_to_param_name(self, prefix, name):
568 return 'param_{}_{}'.format(prefix, name)
569
570 def _ev_f_name_to_param_name(self, name):
571 return self._name_to_param_name('evf', name)
572
573 def _ev_c_name_to_param_name(self, name):
574 return self._name_to_param_name('evc', name)
575
576 def _ev_sec_name_to_param_name(self, name):
577 return self._name_to_param_name('evsec', name)
578
579 def _ev_h_name_to_param_name(self, name):
580 return self._name_to_param_name('evh', name)
581
582 def _s_pc_name_to_param_name(self, name):
583 return self._name_to_param_name('spc', name)
584
585 def _s_ph_name_to_param_name(self, name):
586 return self._name_to_param_name('sph', name)
587
588 def _get_integer_param_ctype(self, integer):
589 signed = 'u' if not integer.signed else ''
590
591 if integer.size == 8:
592 sz = '8'
593 elif integer.size == 16:
594 sz = '16'
595 elif integer.size == 32:
596 sz = '32'
597 elif integer.size == 64:
598 sz = '64'
599 else:
600 # if the integer is signed and of uncommon size, the sign bit is
601 # at a custom position anyway so we use a 64-bit unsigned
602 signed = 'u'
603
604 if integer.signed:
605 sz = '64'
606 else:
607 if integer.size < 16:
608 sz = '8'
609 elif integer.size < 32:
610 sz = '16'
611 elif integer.size < 64:
612 sz = '32'
613 else:
614 sz = '64'
615
616 return '{}int{}_t'.format(signed, sz)
617
618 def _get_enum_param_ctype(self, enum):
619 return self._get_obj_param_ctype(enum.integer)
620
621 def _get_floating_point_param_ctype(self, fp):
622 if fp.exp_dig == 8 and fp.mant_dig == 24 and fp.align == 32:
623 return 'float'
624 elif fp.exp_dig == 11 and fp.mant_dig == 53 and fp.align == 64:
625 return 'double'
626 else:
627 return 'uint64_t'
628
629 def _get_obj_param_ctype(self, obj):
630 return self._obj_param_ctype_cb[type(obj)](obj)
631
632 def _get_chk_offset_v(self, size):
633 fmt = '{}_CHK_OFFSET_V({}, {}, {});'
634 ret = fmt.format(self._prefix.upper(), self._CTX_AT,
635 self._CTX_BUF_SIZE, size)
636
637 return ret
638
639 def _get_chk_offset_v_cline(self, size):
640 return _CLine(self._get_chk_offset_v(size))
641
642 def _get_align_offset(self, align):
643 fmt = '{}_ALIGN_OFFSET({}, {});'
644 ret = fmt.format(self._prefix.upper(), self._CTX_AT, align)
645
646 return ret
647
648 def _get_align_offset_cline(self, size):
649 return _CLine(self._get_align_offset(size))
650
651 def _write_field_struct(self, fname, src_name, struct):
652 size = self._get_struct_size(struct)
653 size_bytes = self._get_alignment(size, 8) // 8
654 dst = self._CTX_BUF_AT_ADDR
655
656 return [
657 # memcpy() is safe since barectf requires inner structures
658 # to be byte-aligned
659 self._get_chk_offset_v_cline(size),
660 _CLine('memcpy({}, {}, {});'.format(dst, src_name, size_bytes)),
661 _CLine('{} += {};'.format(self._CTX_AT, size)),
662 ]
663
664 def _write_field_integer(self, fname, src_name, integer):
665 bo = self._bo_suffixes_map[integer.byte_order]
666 ptr = self._CTX_BUF
667 t = self._get_obj_param_ctype(integer)
668 start = self._CTX_AT
669 length = self._get_obj_size(integer)
670 fmt = 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
671
672 return [
673 self._get_chk_offset_v_cline(length),
674 _CLine(fmt.format(bo, ptr, t, start, length, src_name)),
675 _CLine('{} += {};'.format(self._CTX_AT, length))
676 ]
677
678 def _write_field_enum(self, fname, src_name, enum):
679 return self._write_field_obj(fname, src_name, enum.integer)
680
681 def _write_field_floating_point(self, fname, src_name, floating_point):
682 bo = self._bo_suffixes_map[floating_point.byte_order]
683 ptr = self._CTX_BUF
684 t = self._get_obj_param_ctype(floating_point)
685 start = self._CTX_AT
686 length = self._get_obj_size(floating_point)
687 fmt = 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
688
689 return [
690 self._get_chk_offset_v_cline(length),
691 _CLine(fmt.format(bo, ptr, t, start, length, src_name)),
692 _CLine('{} += {};'.format(self._CTX_AT, length))
693 ]
694
695 def _write_field_array(self, fname, src_name, array):
696 clines = []
697
698 # array index variable declaration
699 iv = 'ia_{}'.format(fname)
700 clines.append(_CLine('uint32_t {};'.format(iv)))
701
702 # for loop using array's static length
703 line = 'for ({iv} = 0; {iv} < {l}; ++{iv}) {{'.format(iv=iv, l=array.length)
704 clines.append(_CLine(line))
705
706 # for loop statements
707 for_block = _CBlock()
708
709 # align bit index before writing to the buffer
710 element_align = self._get_obj_alignment(array.element)
711 cline = self._get_align_offset_cline(element_align)
712 for_block.append(cline)
713
714 # write element to the buffer
715 for_block += self._write_field_obj(fname, src_name, array.element)
716 clines.append(for_block)
717
718 # for loop end
719 clines.append(_CLine('}'))
720
721 return clines
722
723 def _write_field_sequence(self, fname, src_name, sequence):
724 return [
725 _CLine('would write sequence here;'),
726 ]
727
728 def _write_field_string(self, fname, src_name, string):
729 clines = []
730
731 # string index variable declaration
732 iv = 'is_{}'.format(fname)
733 clines.append(_CLine('uint32_t {};'.format(iv)))
734
735 # for loop; loop until the end of the source string is reached
736 fmt = "for ({iv} = 0; {src}[{iv}] != '\\0'; ++{iv}, {ctxat} += 8) {{"
737 line = fmt.format(iv=iv, src=src_name, ctxat=self._CTX_AT)
738 clines.append(_CLine(line))
739
740 # for loop statements
741 for_block = _CBlock()
742
743 # check offset overflow
744 for_block.append(self._get_chk_offset_v_cline(8))
745
746 # write byte to the buffer
747 fmt = '{dst} = {src}[{iv}];'
748 line = fmt.format(dst=self._CTX_BUF_AT, iv=iv, src=src_name)
749 for_block.append(_CLine(line))
750
751 # append for loop
752 clines.append(for_block)
753 clines.append(_CLine('}'))
754
755 # write NULL character to the buffer
756 clines.append(_CLine("{} = '\\0';".format(self._CTX_BUF_AT)))
757 clines.append(_CLine('{} += 8;'.format(self._CTX_AT)))
758
759 return clines
760
761 def _write_field_obj(self, fname, src_name, ftype):
762 return self._write_field_obj_cb[type(ftype)](fname, src_name, ftype)
763
764 def _get_offvar_name(self, name, prefix=None):
765 parts = ['off']
766
767 if prefix is not None:
768 parts.append(prefix)
769
770 parts.append(name)
771
772 return '_'.join(parts)
773
774 def _get_offvar_name_from_expr(self, expr):
775 return self._get_offvar_name('_'.join(expr))
776
777 def _field_to_clines(self, fname, ftype, scope_name, param_name_cb):
778 clines = []
779 pname = param_name_cb(fname)
780 align = self._get_obj_alignment(ftype)
781
782 # group comment
783 fmt = '/* write {} field "{}" ({}) */'
784 line = fmt.format(scope_name, fname,
785 self._tsdl_type_names_map[type(ftype)])
786 clines.append(_CLine(line))
787
788 # align bit index before writing to the buffer
789 cline = self._get_align_offset_cline(align)
790 clines.append(cline)
791
792 # write offset variables
793 if type(ftype) is pytsdl.tsdl.Struct:
794 offvars_tree = collections.OrderedDict()
795 self._get_struct_size(ftype, offvars_tree)
796 off_vars = self._flatten_offvars_tree(offvars_tree)
797
798 # as many offset as there are child fields because a future
799 # sequence could refer to any of those fields
800 for lname, offset in off_vars.items():
801 offvar = self._get_offvar_name('_'.join([fname, lname]))
802 fmt = 'uint32_t {} = {} + {};'
803 line = fmt.format(offvar, self._CTX_AT, offset);
804 clines.append(_CLine(line))
805 elif type(ftype) is pytsdl.tsdl.Integer:
806 # offset of this simple field is the current bit index
807 offvar = self._get_offvar_name(fname)
808 line = 'uint32_t {} = {};'.format(offvar, self._CTX_AT)
809 clines.append(_CLine(line))
810
811 clines += self._write_field_obj(fname, pname, ftype)
812
813 return clines
814
815 def _join_cline_groups(self, cline_groups):
816 if not cline_groups:
817 return cline_groups
818
819 output_clines = cline_groups[0]
820
821 for clines in cline_groups[1:]:
822 output_clines.append('')
823 output_clines += clines
824
825 return output_clines
826
827 def _struct_to_clines(self, struct, scope_name, param_name_cb):
828 cline_groups = []
829
830 for fname, ftype in struct.fields.items():
831 clines = self._field_to_clines(fname, ftype, scope_name,
832 param_name_cb)
833 cline_groups.append(clines)
834
835 return self._join_cline_groups(cline_groups)
836
837 def _get_struct_size_offvars(self, struct):
838 offvars_tree = collections.OrderedDict()
839 size = self._get_struct_size(struct, offvars_tree)
840 offvars = self._flatten_offvars_tree(offvars_tree)
841
842 return size, offvars
843
844 def _get_ph_size_offvars(self):
845 return self._get_struct_size_offvars(self._doc.trace.packet_header)
846
847 def _get_pc_size_offvars(self, stream):
848 return self._get_struct_size_offvars(stream.packet_context)
849
850 def _offvars_to_ctx_clines(self, prefix, offvars):
851 clines = []
852
853 for name in offvars.keys():
854 offvar = self._get_offvar_name(name, prefix)
855 clines.append(_CLine('uint32_t {};'.format(offvar)))
856
857 return clines
858
859 def _gen_barectf_ctx_struct(self, stream, hide_sid=False):
860 # get offset variables for both the packet header and packet context
861 ph_size, ph_offvars = self._get_ph_size_offvars()
862 pc_size, pc_offvars = self._get_pc_size_offvars(stream)
863 clines = self._offvars_to_ctx_clines('ph', ph_offvars)
864 clines += self._offvars_to_ctx_clines('pc', pc_offvars)
865
866 # indent C
867 clines_indented = []
868 for cline in clines:
869 clines_indented.append(_CLine('\t' + cline))
870
871 # clock callback
872 clock_cb = '\t/* (no clock callback) */'
873
874 if not self._manual_clock:
875 ctype = self._get_clock_type(stream)
876 fmt = '\t{} (*clock_cb)(void*),\n\tvoid* clock_cb_data;'
877 clock_cb = fmt.format(ctype)
878
879 # fill template
880 sid = ''
881
882 if not hide_sid:
883 sid = stream.id
884
885 t = barectf.templates.BARECTF_CTX
886 struct = t.format(prefix=self._prefix, sid=sid,
887 ctx_fields='\n'.join(clines_indented),
888 clock_cb=clock_cb)
889
890 return struct
891
892 def _gen_barectf_contexts_struct(self):
893 hide_sid = False
894
895 if len(self._doc.streams) == 1:
896 hide_sid = True
897
898 structs = []
899
900 for stream in self._doc.streams.values():
901 struct = self._gen_barectf_ctx_struct(stream, hide_sid)
902 structs.append(struct)
903
904 return '\n\n'.join(structs)
905
906 _packet_header_known_fields = [
907 'magic',
908 'stream_id',
909 ]
910
911 _packet_context_known_fields = [
912 'content_size',
913 'packet_size',
914 'timestamp_begin',
915 'timestamp_end',
916 ]
917
918 def _get_clock_type(self, stream):
919 return self._get_obj_param_ctype(stream.event_header['timestamp'])
920
921 def _gen_manual_clock_param(self, stream):
922 return '{} param_clock'.format(self._get_clock_type(stream))
923
924 def _gen_barectf_func_open(self, stream, gen_body, hide_sid=False):
925 params = []
926
927 # manual clock
928 if self._manual_clock:
929 clock_param = self._gen_manual_clock_param(stream)
930 params.append(clock_param)
931
932 # packet header
933 for fname, ftype in self._doc.trace.packet_header.fields.items():
934 if fname in self._packet_header_known_fields:
935 continue
936
937 ptype = self._get_obj_param_ctype(ftype)
938 pname = self._name_to_param_name('ph', fname)
939 param = '{} {}'.format(ptype, pname)
940 params.append(param)
941
942 # packet context
943 for fname, ftype in stream.packet_context.fields.items():
944 if fname in self._packet_context_known_fields:
945 continue
946
947 ptype = self._get_obj_param_ctype(ftype)
948 pname = self._name_to_param_name('pc', fname)
949 param = '{} {}'.format(ptype, pname)
950 params.append(param)
951
952 params_str = ''
953
954 if params:
955 params_str = ',\n\t' + ',\n\t'.join(params)
956
957 # fill template
958 sid = ''
959
960 if not hide_sid:
961 sid = stream.id
962
963 t = barectf.templates.FUNC_OPEN
964 func = t.format(si=self._si_str, prefix=self._prefix, sid=sid,
965 params=params_str)
966
967 if gen_body:
968 func += '\n{\n'
969
970 func += '\n}'
971 else:
972 func += ';'
973
974 return func
975
976 def _gen_barectf_funcs_open(self, gen_body):
977 hide_sid = False
978
979 if len(self._doc.streams) == 1:
980 hide_sid = True
981
982 funcs = []
983
984 for stream in self._doc.streams.values():
985 funcs.append(self._gen_barectf_func_open(stream, gen_body,
986 hide_sid))
987
988 return funcs
989
990 def _gen_barectf_func_init_body(self, stream):
991 clines = []
992
993 line = 'uint32_t ctx_at_bkup;'
994 clines.append(_CLine(line))
995
996 # set context parameters
997 clines.append(_CLine(''))
998 clines.append(_CLine("/* barectf context parameters */"))
999 clines.append(_CLine('ctx->buf = buf;'))
1000 clines.append(_CLine('ctx->buf_size = buf_size * 8;'))
1001 clines.append(_CLine('{} = 0;'.format(self._CTX_AT)))
1002
1003 if not self._manual_clock:
1004 clines.append(_CLine('ctx->clock_cb = clock_cb;'))
1005 clines.append(_CLine('ctx->clock_cb_data = clock_cb_data;'))
1006
1007 # set context offsets
1008 clines.append(_CLine(''))
1009 clines.append(_CLine("/* barectf context offsets */"))
1010 ph_size, ph_offvars = self._get_ph_size_offvars()
1011 pc_size, pc_offvars = self._get_pc_size_offvars(stream)
1012 pc_alignment = self._get_obj_alignment(stream.packet_context)
1013 pc_offset = self._get_alignment(ph_size, pc_alignment)
1014
1015 for offvar, offset in ph_offvars.items():
1016 offvar_field = self._get_offvar_name(offvar, 'ph')
1017 line = 'ctx->{} = {};'.format(offvar_field, offset)
1018 clines.append(_CLine(line))
1019
1020 for offvar, offset in pc_offvars.items():
1021 offvar_field = self._get_offvar_name(offvar, 'pc')
1022 line = 'ctx->{} = {};'.format(offvar_field, pc_offset + offset)
1023 clines.append(_CLine(line))
1024
1025 clines.append(_CLine(''))
1026
1027 # packet header fields
1028 fcline_groups = []
1029
1030 for fname, ftype in self._doc.trace.packet_header.fields.items():
1031 # magic number
1032 if fname == 'magic':
1033 fclines = self._field_to_clines(fname, ftype, 'packet.header',
1034 lambda x: '0xc1fc1fc1UL')
1035 fcline_groups.append(fclines)
1036
1037 # stream ID
1038 elif fname == 'stream_id':
1039 fclines = self._field_to_clines(fname, ftype, 'packet.header',
1040 lambda x: str(stream.id))
1041 fcline_groups.append(fclines)
1042
1043 clines += self._join_cline_groups(fcline_groups)
1044
1045 # get source
1046 cblock = _CBlock(clines)
1047 src = self._cblock_to_source(cblock)
1048
1049 return src
1050
1051 def _gen_barectf_func_init(self, stream, gen_body, hide_sid=False):
1052 # fill template
1053 sid = ''
1054
1055 if not hide_sid:
1056 sid = stream.id
1057
1058 params = ''
1059
1060 if not self._manual_clock:
1061 ts_ftype = stream.event_header['timestamp']
1062 ts_ptype = self._get_obj_param_ctype(ts_ftype)
1063 fmt = ',\n\t{} (*clock_cb)(void*),\n\tvoid* clock_cb_data'
1064 params = fmt.format(ts_ptype)
1065
1066 t = barectf.templates.FUNC_INIT
1067 func = t.format(si=self._si_str, prefix=self._prefix, sid=sid,
1068 params=params)
1069
1070 if gen_body:
1071 func += '\n{\n'
1072 func += self._gen_barectf_func_init_body(stream)
1073 func += '\n}'
1074 else:
1075 func += ';'
1076
1077 return func
1078
1079 def _gen_barectf_funcs_init(self, gen_body):
1080 hide_sid = False
1081
1082 if len(self._doc.streams) == 1:
1083 hide_sid = True
1084
1085 funcs = []
1086
1087 for stream in self._doc.streams.values():
1088 funcs.append(self._gen_barectf_func_init(stream, gen_body,
1089 hide_sid))
1090
1091 return funcs
1092
1093 def _gen_get_clock_value(self):
1094 if self._manual_clock:
1095 return 'param_clock'
1096 else:
1097 return self._CTX_CALL_CLOCK_CB
1098
1099 def _stream_has_timestamp_begin_end(self, stream):
1100 return self._has_timestamp_begin_end[stream.id]
1101
1102 def _gen_write_ctx_field_integer(self, src_name, prefix, name, obj):
1103 clines = []
1104
1105 # save buffer position
1106 line = 'ctx_at_bkup = {};'.format(self._CTX_AT)
1107 clines.append(_CLine(line))
1108
1109 # go back to field offset
1110 offvar = self._get_offvar_name(name, prefix)
1111 line = '{} = ctx->{};'.format(self._CTX_AT, offvar)
1112 clines.append(_CLine(line))
1113
1114 # write value
1115 clines += self._write_field_integer(None, src_name, obj)
1116
1117 # restore buffer position
1118 line = '{} = ctx_at_bkup;'.format(self._CTX_AT)
1119 clines.append(_CLine(line))
1120
1121 return clines
1122
1123 def _gen_barectf_func_close_body(self, stream):
1124 clines = []
1125
1126 line = 'uint32_t ctx_at_bkup;'
1127 clines.append(_CLine(line))
1128
1129 # update timestamp end if present
1130 if self._stream_has_timestamp_begin_end(stream):
1131 clines.append(_CLine(''))
1132 clines.append(_CLine("/* update packet context's timestamp_end */"))
1133
1134 # get clock value ASAP
1135 clk_type = self._get_clock_type(stream)
1136 clk = self._gen_get_clock_value()
1137 line = '{} clk_value = {};'.format(clk_type, clk)
1138 clines.append(_CLine(line))
1139
1140 # save buffer position
1141 timestamp_end_integer = stream.packet_context['timestamp_end']
1142 clines += self._gen_write_ctx_field_integer('clk_value', 'pc',
1143 'timestamp_end',
1144 timestamp_end_integer)
1145
1146 # update content_size
1147 clines.append(_CLine(''))
1148 clines.append(_CLine("/* update packet context's content_size */"))
1149 content_size_integer = stream.packet_context['content_size']
1150 clines += self._gen_write_ctx_field_integer('ctx_at_bkup', 'pc',
1151 'content_size',
1152 content_size_integer)
1153
1154 # get source
1155 cblock = _CBlock(clines)
1156 src = self._cblock_to_source(cblock)
1157
1158 return src
1159
1160 def _gen_barectf_func_close(self, stream, gen_body, hide_sid=False):
1161 # fill template
1162 sid = ''
1163
1164 if not hide_sid:
1165 sid = stream.id
1166
1167 params = ''
1168
1169 if self._manual_clock:
1170 clock_param = self._gen_manual_clock_param(stream)
1171 params = ',\n\t{}'.format(clock_param)
1172
1173 t = barectf.templates.FUNC_CLOSE
1174 func = t.format(si=self._si_str, prefix=self._prefix, sid=sid,
1175 params=params)
1176
1177 if gen_body:
1178 func += '\n{\n'
1179 func += self._gen_barectf_func_close_body(stream)
1180 func += '\n}'
1181 else:
1182 func += ';'
1183
1184 return func
1185
1186 def _gen_barectf_funcs_close(self, gen_body):
1187 hide_sid = False
1188
1189 if len(self._doc.streams) == 1:
1190 hide_sid = True
1191
1192 funcs = []
1193
1194 for stream in self._doc.streams.values():
1195 funcs.append(self._gen_barectf_func_close(stream, gen_body,
1196 hide_sid))
1197
1198 return funcs
1199
1200 def _gen_barectf_header(self):
1201 ctx_structs = self._gen_barectf_contexts_struct()
1202 init_funcs = self._gen_barectf_funcs_init(self._static_inline)
1203 open_funcs = self._gen_barectf_funcs_open(self._static_inline)
1204 close_funcs = self._gen_barectf_funcs_close(self._static_inline)
1205 functions = init_funcs + open_funcs + close_funcs
1206 functions_str = '\n\n'.join(functions)
1207 t = barectf.templates.HEADER
1208 header = t.format(prefix=self._prefix, ucprefix=self._prefix.upper(),
1209 barectf_ctx=ctx_structs, functions=functions_str)
1210
1211 return header
1212
1213 def _cblock_to_source_lines(self, cblock, indent=1):
1214 src = []
1215 indentstr = '\t' * indent
1216
1217 for line in cblock:
1218 if type(line) is _CBlock:
1219 src += self._cblock_to_source_lines(line, indent + 1)
1220 else:
1221 src.append(indentstr + line)
1222
1223 return src
1224
1225 def _cblock_to_source(self, cblock, indent=1):
1226 lines = self._cblock_to_source_lines(cblock, indent)
1227
1228 return '\n'.join(lines)
1229
1230 def _set_params(self):
1231 self._has_timestamp_begin_end = {}
1232
1233 for stream in self._doc.streams.values():
1234 has = 'timestamp_begin' in stream.packet_context.fields
1235 self._has_timestamp_begin_end[stream.id] = has
1236
1237 def gen_barectf(self, metadata, output, prefix, static_inline,
1238 manual_clock):
1239 self._metadata = metadata
1240 self._output = output
1241 self._prefix = prefix
1242 self._static_inline = static_inline
1243 self._manual_clock = manual_clock
1244 self._si_str = ''
1245
1246 if static_inline:
1247 self._si_str = 'static inline '
1248
1249 # open CTF metadata file
1250 try:
1251 with open(metadata) as f:
1252 self._tsdl = f.read()
1253 except:
1254 _perror('cannot open/read CTF metadata file "{}"'.format(metadata))
1255
1256 # parse CTF metadata
1257 try:
1258 self._doc = self._parser.parse(self._tsdl)
1259 except pytsdl.parser.ParseError as e:
1260 _perror('parse error: {}'.format(e))
1261
1262 # validate CTF metadata against barectf constraints
1263 self._validate_metadata()
1264
1265 # set parameters for this generation
1266 self._set_params()
1267
1268
1269 print(self._gen_barectf_header())
1270
1271 """
1272 clines = self._struct_to_clines(self._doc.streams[0].get_event(0).fields,
1273 'stream event context',
1274 self._ev_f_name_to_param_name)
1275 source = self._cblock_to_source(clines)
1276 print(source)
1277 """
1278
1279
1280 def run():
1281 args = _parse_args()
1282 generator = BarectfCodeGenerator()
1283 generator.gen_barectf(args.metadata, args.output, args.prefix,
1284 args.static_inline, args.manual_clock)
This page took 0.076724 seconds and 5 git commands to generate.