Fix string write
[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'])
40
41
42 def _psuccess(msg):
43 cprint('{}'.format(msg), 'green', attrs=['bold'])
44
45
46 def _parse_args():
47 ap = argparse.ArgumentParser()
48
49 ap.add_argument('-O', '--output', metavar='OUTPUT', action='store',
50 default=os.getcwd(),
51 help='output directory of C files')
52 ap.add_argument('-p', '--prefix', metavar='PREFIX', action='store',
53 default='barectf',
54 help='custom prefix for C function and structure names')
55 ap.add_argument('-s', '--static-inline', action='store_true',
56 help='generate static inline C functions')
57 ap.add_argument('-c', '--manual-clock', action='store_true',
58 help='do not use a clock callback: pass clock value to tracing functions')
59 ap.add_argument('metadata', metavar='METADATA', action='store',
60 help='CTF metadata input file')
61
62 # parse args
63 args = ap.parse_args()
64
65 # validate output directory
66 if not os.path.isdir(args.output):
67 _perror('"{}" is not an existing directory'.format(args.output))
68
69 # validate prefix
70 if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', args.prefix):
71 _perror('"{}" is not a valid C identifier'.format(args.prefix))
72
73 # validate that metadata file exists
74 if not os.path.isfile(args.metadata):
75 _perror('"{}" is not an existing file'.format(args.metadata))
76
77 return args
78
79
80 class _CBlock(list):
81 pass
82
83
84 class _CLine(str):
85 pass
86
87
88 class BarectfCodeGenerator:
89 _CTX_AT = 'ctx->at'
90 _CTX_BUF = 'ctx->buf'
91 _CTX_PACKET_SIZE = 'ctx->packet_size'
92 _CTX_BUF_AT = '{}[{} >> 3]'.format(_CTX_BUF, _CTX_AT)
93 _CTX_BUF_AT_ADDR = '&{}'.format(_CTX_BUF_AT)
94 _CTX_CALL_CLOCK_CB = 'ctx->clock_cb(ctx->clock_cb_data)'
95
96 _BO_SUFFIXES_MAP = {
97 pytsdl.tsdl.ByteOrder.BE: 'be',
98 pytsdl.tsdl.ByteOrder.LE: 'le',
99 }
100
101 _TSDL_TYPE_NAMES_MAP = {
102 pytsdl.tsdl.Integer: 'integer',
103 pytsdl.tsdl.FloatingPoint: 'floating point',
104 pytsdl.tsdl.Enum: 'enumeration',
105 pytsdl.tsdl.String: 'string',
106 pytsdl.tsdl.Array: 'static array',
107 pytsdl.tsdl.Sequence: 'dynamic array',
108 pytsdl.tsdl.Struct: 'structure',
109 }
110
111 def __init__(self):
112 self._parser = pytsdl.parser.Parser()
113
114 self._obj_size_cb = {
115 pytsdl.tsdl.Struct: self._get_struct_size,
116 pytsdl.tsdl.Integer: self._get_integer_size,
117 pytsdl.tsdl.Enum: self._get_enum_size,
118 pytsdl.tsdl.FloatingPoint: self._get_floating_point_size,
119 pytsdl.tsdl.Array: self._get_array_size,
120 }
121
122 self._obj_alignment_cb = {
123 pytsdl.tsdl.Struct: self._get_struct_alignment,
124 pytsdl.tsdl.Integer: self._get_integer_alignment,
125 pytsdl.tsdl.Enum: self._get_enum_alignment,
126 pytsdl.tsdl.FloatingPoint: self._get_floating_point_alignment,
127 pytsdl.tsdl.Array: self._get_array_alignment,
128 pytsdl.tsdl.Sequence: self._get_sequence_alignment,
129 pytsdl.tsdl.String: self._get_string_alignment,
130 }
131
132 self._obj_param_ctype_cb = {
133 pytsdl.tsdl.Struct: lambda obj: 'const void*',
134 pytsdl.tsdl.Integer: self._get_integer_param_ctype,
135 pytsdl.tsdl.Enum: self._get_enum_param_ctype,
136 pytsdl.tsdl.FloatingPoint: self._get_floating_point_param_ctype,
137 pytsdl.tsdl.Array: lambda obj: 'const void*',
138 pytsdl.tsdl.Sequence: lambda obj: 'const void*',
139 pytsdl.tsdl.String: lambda obj: 'const char*',
140 }
141
142 self._write_field_obj_cb = {
143 pytsdl.tsdl.Struct: self._write_field_struct,
144 pytsdl.tsdl.Integer: self._write_field_integer,
145 pytsdl.tsdl.Enum: self._write_field_enum,
146 pytsdl.tsdl.FloatingPoint: self._write_field_floating_point,
147 pytsdl.tsdl.Array: self._write_field_array,
148 pytsdl.tsdl.Sequence: self._write_field_sequence,
149 pytsdl.tsdl.String: self._write_field_string,
150 }
151
152 self._get_src_name_funcs = {
153 'trace.packet.header.': self._get_tph_src_name,
154 'env.': self._get_env_src_name,
155 'stream.packet.context.': self._get_spc_src_name,
156 'stream.event.header.': self._get_seh_src_name,
157 'stream.event.context.': self._get_sec_src_name,
158 'event.context.': self._get_ec_src_name,
159 'event.fields.': self._get_ef_src_name,
160 }
161
162 # Validates an inner TSDL structure's field (constrained structure).
163 #
164 # fname: field name
165 # ftype: TSDL object
166 def _validate_inner_struct_field(self, fname, ftype):
167 if type(ftype) is pytsdl.tsdl.Sequence:
168 raise RuntimeError('field "{}" is a dynamic array (not allowed here)'.format(fname))
169 elif type(ftype) is pytsdl.tsdl.Array:
170 # we need to check every element until we find a terminal one
171 self._validate_inner_struct_field(fname, ftype.element)
172 elif type(ftype) is pytsdl.tsdl.Variant:
173 raise RuntimeError('field "{}" is a variant (unsupported)'.format(fname))
174 elif type(ftype) is pytsdl.tsdl.String:
175 raise RuntimeError('field "{}" is a string (not allowed here)'.format(fname))
176 elif type(ftype) is pytsdl.tsdl.Struct:
177 self._validate_inner_struct(ftype)
178 elif type(ftype) is pytsdl.tsdl.Integer:
179 if self._get_obj_size(ftype) > 64:
180 raise RuntimeError('integer field "{}" larger than 64-bit'.format(fname))
181 elif type(ftype) is pytsdl.tsdl.FloatingPoint:
182 if self._get_obj_size(ftype) > 64:
183 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(fname))
184 elif type(ftype) is pytsdl.tsdl.Enum:
185 if self._get_obj_size(ftype) > 64:
186 raise RuntimeError('enum field "{}" larger than 64-bit'.format(fname))
187
188 # Validates an inner TSDL structure (constrained).
189 #
190 # struct: TSDL structure to validate
191 def _validate_inner_struct(self, struct):
192 # just in case we call this with the wrong type
193 if type(struct) is not pytsdl.tsdl.Struct:
194 raise RuntimeError('expecting a struct')
195
196 # make sure inner structures are at least byte-aligned
197 if self._get_obj_alignment(struct) < 8:
198 raise RuntimeError('inner struct must be at least byte-aligned')
199
200 # check each field
201 for fname, ftype in struct.fields.items():
202 self._validate_inner_struct_field(fname, ftype)
203
204 # Validates a context or fields structure.
205 #
206 # struct: context/fields TSDL structure
207 def _validate_context_fields(self, struct):
208 if type(struct) is not pytsdl.tsdl.Struct:
209 raise RuntimeError('expecting a struct')
210
211 for fname, ftype in struct.fields.items():
212 if type(ftype) is pytsdl.tsdl.Variant:
213 raise RuntimeError('field "{}" is a variant (unsupported)'.format(fname))
214 elif type(ftype) is pytsdl.tsdl.Struct:
215 # validate inner structure against barectf constraints
216 self._validate_inner_struct(ftype)
217 elif type(ftype) is pytsdl.tsdl.Array or type(ftype) is pytsdl.tsdl.Sequence:
218 done = False
219 el = ftype
220
221 while not done:
222 if type(el) is pytsdl.tsdl.Struct:
223 self._validate_inner_struct(el)
224 if type(el) is pytsdl.tsdl.Array or type(el) is pytsdl.tsdl.Sequence:
225 el = el.element
226 continue
227
228 done = True
229
230 # Validates a TSDL integer with optional constraints.
231 #
232 # integer: TSDL integer to validate
233 # size: expected size (None for any size)
234 # align: expected alignment (None for any alignment)
235 # signed: expected signedness (None for any signedness)
236 def _validate_integer(self, integer, size=None, align=None,
237 signed=None):
238 if type(integer) is not pytsdl.tsdl.Integer:
239 raise RuntimeError('expected integer')
240
241 if size is not None:
242 if integer.size != size:
243 raise RuntimeError('expected {}-bit integer'.format(size))
244
245 if align is not None:
246 if integer.align != align:
247 raise RuntimeError('expected integer with {}-bit alignment'.format(align))
248
249 if signed is not None:
250 if integer.signed != signed:
251 raise RuntimeError('expected {} integer'.format('signed' if signed else 'unsigned'))
252
253 # Validates a packet header.
254 #
255 # packet_header: packet header TSDL structure to validate
256 def _validate_tph(self, packet_header):
257 try:
258 self._validate_inner_struct(packet_header)
259 except RuntimeError as e:
260 _perror('packet header: {}'.format(e))
261
262 # magic must be the first field
263 if 'magic' in packet_header.fields:
264 if list(packet_header.fields.keys())[0] != 'magic':
265 _perror('packet header: "magic" must be the first field')
266 else:
267 _perror('packet header: missing "magic" field')
268
269 # magic must be a 32-bit unsigned integer, 32-bit aligned
270 try:
271 self._validate_integer(packet_header['magic'], 32, 32, False)
272 except RuntimeError as e:
273 _perror('packet header: "magic": {}'.format(e))
274
275 # mandatory stream_id
276 if 'stream_id' not in packet_header.fields:
277 _perror('packet header: missing "stream_id" field')
278
279 # stream_id must be an unsigned integer
280 try:
281 self._validate_integer(packet_header['stream_id'], signed=False)
282 except RuntimeError as e:
283 _perror('packet header: "stream_id": {}'.format(e))
284
285 # only magic and stream_id allowed
286 if len(packet_header.fields) != 2:
287 _perror('packet header: only "magic" and "stream_id" fields are allowed')
288
289 # Converts a list of strings to a dotted representation. For
290 # example, ['trace', 'packet', 'header', 'magic'] is converted to
291 # 'trace.packet.header.magic'.
292 #
293 # name: list of strings to convert
294 def _dot_name_to_str(self, name):
295 return '.'.join(name)
296
297 # Compares two TSDL integers. Returns True if they are the same.
298 #
299 # int1: first TSDL integer
300 # int2: second TSDL integer
301 def _compare_integers(self, int1, int2):
302 if type(int1) is not pytsdl.tsdl.Integer:
303 return False
304
305 if type(int2) is not pytsdl.tsdl.Integer:
306 return False
307
308 size = int1.size == int2.size
309 align = int1.align == int2.align
310 cmap = int1.map == int2.map
311 base = int1.base == int2.base
312 encoding = int1.encoding == int2.encoding
313 signed = int1.signed == int2.signed
314 comps = (size, align, cmap, base, encoding, signed)
315
316 # True means 1 for sum()
317 return sum(comps) == len(comps)
318
319 # Validates a packet context.
320 #
321 # stream: TSDL stream containing the packet context to validate
322 def _validate_spc(self, stream):
323 packet_context = stream.packet_context
324 sid = stream.id
325
326 try:
327 self._validate_inner_struct(packet_context)
328 except RuntimeError as e:
329 _perror('stream {}: packet context: {}'.format(sid, e))
330
331 fields = packet_context.fields
332
333 # if timestamp_begin exists, timestamp_end must exist
334 if 'timestamp_begin' in fields or 'timestamp_end' in fields:
335 if 'timestamp_begin' not in fields or 'timestamp_end' not in fields:
336 _perror('stream {}: packet context: "timestamp_begin" must exist if "timestamp_end" exists'.format(sid))
337 else:
338 # timestamp_begin and timestamp_end must have the same integer
339 # as the event header's timestamp field (should exist by now)
340 timestamp = stream.event_header['timestamp']
341
342 if not self._compare_integers(fields['timestamp_begin'], timestamp):
343 _perror('stream {}: packet context: "timestamp_begin": integer type different from event header\'s "timestamp" field'.format(sid))
344
345 if not self._compare_integers(fields['timestamp_end'], timestamp):
346 _perror('stream {}: packet context: "timestamp_end": integer type different from event header\'s "timestamp" field'.format(sid))
347
348 # content_size must exist and be an unsigned integer
349 if 'content_size' not in fields:
350 _perror('stream {}: packet context: missing "content_size" field'.format(sid))
351
352 try:
353 self._validate_integer(fields['content_size'], 32, 32, False)
354 except:
355 try:
356 self._validate_integer(fields['content_size'], 64, 64, False)
357 except:
358 _perror('stream {}: packet context: "content_size": expecting unsigned 32-bit/64-bit integer'.format(sid))
359
360 # packet_size must exist and be an unsigned integer
361 if 'packet_size' not in fields:
362 _perror('stream {}: packet context: missing "packet_size" field'.format(sid))
363
364 try:
365 self._validate_integer(fields['packet_size'], 32, 32, False)
366 except:
367 try:
368 self._validate_integer(fields['packet_size'], 64, 64, False)
369 except:
370 _perror('stream {}: packet context: "packet_size": expecting unsigned 32-bit/64-bit integer'.format(sid))
371
372 # if cpu_id exists, must be an unsigned integer
373 if 'cpu_id' in fields:
374 try:
375 self._validate_integer(fields['cpu_id'], signed=False)
376 except RuntimeError as e:
377 _perror('stream {}: packet context: "cpu_id": {}'.format(sid, e))
378
379 # Validates an event header.
380 #
381 # stream: TSDL stream containing the event header to validate
382 def _validate_seh(self, stream):
383 event_header = stream.event_header
384 sid = stream.id
385
386 try:
387 self._validate_inner_struct(event_header)
388 except RuntimeError as e:
389 _perror('stream {}: event header: {}'.format(sid, e))
390
391 fields = event_header.fields
392
393 # id must exist and be an unsigned integer
394 if 'id' not in fields:
395 _perror('stream {}: event header: missing "id" field'.format(sid))
396
397 try:
398 self._validate_integer(fields['id'], signed=False)
399 except RuntimeError as e:
400 _perror('stream {}: "id": {}'.format(sid, format(e)))
401
402 # timestamp must exist, be an unsigned integer and be mapped to a valid clock
403 if 'timestamp' not in fields:
404 _perror('stream {}: event header: missing "timestamp" field'.format(sid))
405
406 try:
407 self._validate_integer(fields['timestamp'], signed=False)
408 except RuntimeError as e:
409 _perror('stream {}: event header: "timestamp": {}'.format(sid, format(e)))
410
411 if fields['timestamp'].map is None:
412 _perror('stream {}: event header: "timestamp" must be mapped to a valid clock'.format(sid))
413
414 # id must be the first field, followed by timestamp
415 if list(fields.keys())[0] != 'id':
416 _perror('stream {}: event header: "id" must be the first field'.format(sid))
417
418 if list(fields.keys())[1] != 'timestamp':
419 _perror('stream {}: event header: "timestamp" must be the second field'.format(sid))
420
421 # only id and timestamp and allowed in event header
422 if len(fields) != 2:
423 _perror('stream {}: event header: only "id" and "timestamp" fields are allowed'.format(sid))
424
425 # Validates a strean event context.
426 #
427 # stream: TSDL stream containing the stream event context
428 def _validate_sec(self, stream):
429 stream_event_context = stream.event_context
430 sid = stream.id
431
432 if stream_event_context is None:
433 return
434
435 try:
436 self._validate_context_fields(stream_event_context)
437 except RuntimeError as e:
438 _perror('stream {}: event context: {}'.format(sid, e))
439
440 # Validates an event context.
441 #
442 # stream: TSDL stream containing the TSDL event
443 # event: TSDL event containing the context to validate
444 def _validate_ec(self, stream, event):
445 event_context = event.context
446 sid = stream.id
447 eid = event.id
448
449 if event_context is None:
450 return
451
452 try:
453 self._validate_context_fields(event_context)
454 except RuntimeError as e:
455 _perror('stream {}: event {}: context: {}'.format(sid, eid, e))
456
457 # Validates an event fields.
458 #
459 # stream: TSDL stream containing the TSDL event
460 # event: TSDL event containing the fields to validate
461 def _validate_ef(self, stream, event):
462 event_fields = event.fields
463 sid = stream.id
464 eid = event.id
465
466 try:
467 self._validate_context_fields(event_fields)
468 except RuntimeError as e:
469 _perror('stream {}: event {}: fields: {}'.format(sid, eid, e))
470
471 # Validates a TSDL event.
472 #
473 # stream: TSDL stream containing the TSDL event
474 # event: TSDL event to validate
475 def _validate_event(self, stream, event):
476 # name must be a compatible C identifier
477 if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', event.name):
478 fmt = 'stream {}: event {}: malformed event name: "{}"'
479 _perror(fmt.format(stream.id, event.id, event.name))
480
481 self._validate_ec(stream, event)
482 self._validate_ef(stream, event)
483
484 # Validates a TSDL stream.
485 #
486 # stream: TSDL stream to validate
487 def _validate_stream(self, stream):
488 self._validate_seh(stream)
489 self._validate_spc(stream)
490 self._validate_sec(stream)
491
492 # event stuff
493 for event in stream.events:
494 self._validate_event(stream, event)
495
496 # Validates all TSDL scopes of the current TSDL document.
497 def _validate_all_scopes(self):
498 # packet header
499 self._validate_tph(self._doc.trace.packet_header)
500
501 # stream stuff
502 for stream in self._doc.streams.values():
503 self._validate_stream(stream)
504
505 # Validates the trace block.
506 def _validate_trace(self):
507 # make sure a native byte order is specified
508 if self._doc.trace.byte_order is None:
509 _perror('native byte order (trace.byte_order) is not specified')
510
511 # Validates the current TSDL document.
512 def _validate_metadata(self):
513 self._validate_trace()
514 self._validate_all_scopes()
515
516 # Returns an aligned number.
517 #
518 # 3, 4 -> 4
519 # 4, 4 -> 4
520 # 5, 4 -> 8
521 # 6, 4 -> 8
522 # 7, 4 -> 8
523 # 8, 4 -> 8
524 # 9, 4 -> 12
525 #
526 # at: number to align
527 # align: alignment (power of two)
528 def _get_alignment(self, at, align):
529 return (at + align - 1) & -align
530
531 # Converts a tree of offset variables:
532 #
533 # field
534 # a -> 0
535 # b -> 8
536 # other_struct
537 # field -> 16
538 # yeah -> 20
539 # c -> 32
540 # len -> 36
541 #
542 # to a flat dict:
543 #
544 # field_a -> 0
545 # field_b -> 8
546 # field_other_struct_field -> 16
547 # field_other_struct_yeah -> 20
548 # field_c -> 32
549 # len -> 36
550 #
551 # offvars_tree: tree of offset variables
552 # prefix: offset variable name prefix
553 # offvars: flattened offset variables
554 def _flatten_offvars_tree(self, offvars_tree, prefix=None,
555 offvars=None):
556 if offvars is None:
557 offvars = collections.OrderedDict()
558
559 for name, offset in offvars_tree.items():
560 if prefix is not None:
561 varname = '{}_{}'.format(prefix, name)
562 else:
563 varname = name
564
565 if isinstance(offset, dict):
566 self._flatten_offvars_tree(offset, varname, offvars)
567 else:
568 offvars[varname] = offset
569
570 return offvars
571
572 # Returns the size of a TSDL structure with _static size_ (must be
573 # validated first).
574 #
575 # struct: TSDL structure of which to get the size
576 # offvars_tree: optional offset variables tree (output)
577 # base_offset: base offsets for offset variables
578 def _get_struct_size(self, struct,
579 offvars_tree=None,
580 base_offset=0):
581 if offvars_tree is None:
582 offvars_tree = collections.OrderedDict()
583
584 offset = 0
585
586 for fname, ftype in struct.fields.items():
587 field_alignment = self._get_obj_alignment(ftype)
588 offset = self._get_alignment(offset, field_alignment)
589
590 if type(ftype) is pytsdl.tsdl.Struct:
591 offvars_tree[fname] = collections.OrderedDict()
592 sz = self._get_struct_size(ftype, offvars_tree[fname],
593 base_offset + offset)
594 else:
595 # only integers may act as sequence lengths
596 if type(ftype) is pytsdl.tsdl.Integer:
597 offvars_tree[fname] = base_offset + offset
598
599 sz = self._get_obj_size(ftype)
600
601 offset += sz
602
603 return offset
604
605 # Returns the size of a TSDL array.
606 #
607 # array: TSDL array of which to get the size
608 def _get_array_size(self, array):
609 element = array.element
610
611 # effective size of one element includes its alignment after its size
612 size = self._get_obj_size(element)
613 align = self._get_obj_alignment(element)
614
615 return self._get_alignment(size, align) * array.length
616
617 # Returns the size of a TSDL enumeration.
618 #
619 # enum: TSDL enumeration of which to get the size
620 def _get_enum_size(self, enum):
621 return self._get_obj_size(enum.integer)
622
623 # Returns the size of a TSDL floating point number.
624 #
625 # floating_point: TSDL floating point number of which to get the size
626 def _get_floating_point_size(self, floating_point):
627 return floating_point.exp_dig + floating_point.mant_dig
628
629 # Returns the size of a TSDL integer.
630 #
631 # integer: TSDL integer of which to get the size
632 def _get_integer_size(self, integer):
633 return integer.size
634
635 # Returns the size of a TSDL type.
636 #
637 # obj: TSDL type of which to get the size
638 def _get_obj_size(self, obj):
639 return self._obj_size_cb[type(obj)](obj)
640
641 # Returns the alignment of a TSDL structure.
642 #
643 # struct: TSDL structure of which to get the alignment
644 def _get_struct_alignment(self, struct):
645 if struct.align is not None:
646 return struct.align
647
648 cur_align = 1
649
650 for fname, ftype in struct.fields.items():
651 cur_align = max(self._get_obj_alignment(ftype), cur_align)
652
653 return cur_align
654
655 # Returns the alignment of a TSDL integer.
656 #
657 # integer: TSDL integer of which to get the alignment
658 def _get_integer_alignment(self, integer):
659 return integer.align
660
661 # Returns the alignment of a TSDL floating point number.
662 #
663 # floating_point: TSDL floating point number of which to get the
664 # alignment
665 def _get_floating_point_alignment(self, floating_point):
666 return floating_point.align
667
668 # Returns the alignment of a TSDL enumeration.
669 #
670 # enum: TSDL enumeration of which to get the alignment
671 def _get_enum_alignment(self, enum):
672 return self._get_obj_alignment(enum.integer)
673
674 # Returns the alignment of a TSDL string.
675 #
676 # string: TSDL string of which to get the alignment
677 def _get_string_alignment(self, string):
678 return 8
679
680 # Returns the alignment of a TSDL array.
681 #
682 # array: TSDL array of which to get the alignment
683 def _get_array_alignment(self, array):
684 return self._get_obj_alignment(array.element)
685
686 # Returns the alignment of a TSDL sequence.
687 #
688 # sequence: TSDL sequence of which to get the alignment
689 def _get_sequence_alignment(self, sequence):
690 return self._get_obj_alignment(sequence.element)
691
692 # Returns the alignment of a TSDL type.
693 #
694 # obj: TSDL type of which to get the alignment
695 def _get_obj_alignment(self, obj):
696 return self._obj_alignment_cb[type(obj)](obj)
697
698 # Converts a field name to a C parameter name.
699 #
700 # You should not use this function directly, but rather use one
701 # of the _*_fname_to_pname() variants depending on your scope.
702 #
703 # prefix: parameter name prefix
704 # fname: field name
705 def _fname_to_pname(self, prefix, fname):
706 return 'param_{}_{}'.format(prefix, fname)
707
708 # Converts an event fields field name to a C parameter name.
709 #
710 # fname: field name
711 def _ef_fname_to_pname(self, fname):
712 return self._fname_to_pname('ef', fname)
713
714 # Converts an event context field name to a C parameter name.
715 #
716 # fname: field name
717 def _ec_fname_to_pname(self, fname):
718 return self._fname_to_pname('ec', fname)
719
720 # Converts a stream event context field name to a C parameter name.
721 #
722 # fname: field name
723 def _sec_fname_to_pname(self, fname):
724 return self._fname_to_pname('sec', fname)
725
726 # Converts an event header field name to a C parameter name.
727 #
728 # fname: field name
729 def _eh_fname_to_pname(self, fname):
730 return self._fname_to_pname('eh', fname)
731
732 # Converts a stream packet context field name to a C parameter name.
733 #
734 # fname: field name
735 def _spc_fname_to_pname(self, fname):
736 return self._fname_to_pname('spc', fname)
737
738 # Converts a trace packet header field name to a C parameter name.
739 #
740 # fname: field name
741 def _tph_fname_to_pname(self, fname):
742 return self._fname_to_pname('tph', fname)
743
744 # Returns the equivalent C type of a TSDL integer.
745 #
746 # integer: TSDL integer of which to get the equivalent C type
747 def _get_integer_param_ctype(self, integer):
748 signed = 'u' if not integer.signed else ''
749
750 if integer.size == 8:
751 sz = '8'
752 elif integer.size == 16:
753 sz = '16'
754 elif integer.size == 32:
755 sz = '32'
756 elif integer.size == 64:
757 sz = '64'
758 else:
759 # if the integer is signed and of uncommon size, the sign bit is
760 # at a custom position anyway so we use a 64-bit unsigned
761 signed = 'u'
762
763 if integer.signed:
764 sz = '64'
765 else:
766 if integer.size < 16:
767 sz = '8'
768 elif integer.size < 32:
769 sz = '16'
770 elif integer.size < 64:
771 sz = '32'
772 else:
773 sz = '64'
774
775 return '{}int{}_t'.format(signed, sz)
776
777 # Returns the equivalent C type of a TSDL enumeration.
778 #
779 # enum: TSDL enumeration of which to get the equivalent C type
780 def _get_enum_param_ctype(self, enum):
781 return self._get_obj_param_ctype(enum.integer)
782
783 # Returns the equivalent C type of a TSDL floating point number.
784 #
785 # fp: TSDL floating point number of which to get the equivalent C type
786 def _get_floating_point_param_ctype(self, fp):
787 if fp.exp_dig == 8 and fp.mant_dig == 24 and fp.align == 32:
788 return 'float'
789 elif fp.exp_dig == 11 and fp.mant_dig == 53 and fp.align == 64:
790 return 'double'
791 else:
792 return 'uint64_t'
793
794 # Returns the equivalent C type of a TSDL type.
795 #
796 # obj: TSDL type of which to get the equivalent C type
797 def _get_obj_param_ctype(self, obj):
798 return self._obj_param_ctype_cb[type(obj)](obj)
799
800 # Returns the check offset overflow macro call string for a given size.
801 #
802 # size: size to check
803 def _get_chk_offset_v(self, size):
804 fmt = '{}_CHK_OFFSET_V({}, {}, {});'
805 ret = fmt.format(self._prefix.upper(), self._CTX_AT,
806 self._CTX_PACKET_SIZE, size)
807
808 return ret
809
810 # Returns the check offset overflow macro call C line for a given size.
811 #
812 # size: size to check
813 def _get_chk_offset_v_cline(self, size):
814 return _CLine(self._get_chk_offset_v(size))
815
816 # Returns the offset alignment macro call string for a given alignment.
817 #
818 # size: new alignment
819 def _get_align_offset(self, align, at=None):
820 if at is None:
821 at = self._CTX_AT
822
823 fmt = '{}_ALIGN_OFFSET({}, {});'
824 ret = fmt.format(self._prefix.upper(), at, align)
825
826 return ret
827
828 # Returns the offset alignment macro call C line for a given alignment.
829 #
830 # size: new alignment
831 def _get_align_offset_cline(self, size):
832 return _CLine(self._get_align_offset(size))
833
834 # Converts a C source string with newlines to an array of C lines and
835 # returns it.
836 #
837 # s: C source string
838 def _str_to_clines(self, s):
839 lines = s.split('\n')
840
841 return [_CLine(line) for line in lines]
842
843 # Fills a given template with values and returns its C lines. The `prefix`
844 # and `ucprefix` template variable are automatically provided using the
845 # generator's context.
846 #
847 # tmpl: template
848 # kwargs: additional template variable values
849 def _template_to_clines(self, tmpl, **kwargs):
850 s = tmpl.format(prefix=self._prefix, ucprefix=self._prefix.upper(),
851 **kwargs)
852
853 return self._str_to_clines(s)
854
855 # Returns the C lines for writing a TSDL structure field.
856 #
857 # fname: field name
858 # src_name: C source pointer
859 # struct: TSDL structure
860 def _write_field_struct(self, fname, src_name, struct, scope_prefix=None):
861 size = self._get_struct_size(struct)
862 size_bytes = self._get_alignment(size, 8) // 8
863 dst = self._CTX_BUF_AT_ADDR
864
865 return [
866 # memcpy() is safe since barectf requires inner structures
867 # to be byte-aligned
868 self._get_chk_offset_v_cline(size),
869 _CLine('memcpy({}, {}, {});'.format(dst, src_name, size_bytes)),
870 _CLine('{} += {};'.format(self._CTX_AT, size)),
871 ]
872
873 # Returns the C lines for writing a TSDL integer field.
874 #
875 # fname: field name
876 # src_name: C source integer
877 # integer: TSDL integer
878 def _write_field_integer(self, fname, src_name, integer, scope_prefix=None):
879 bo = self._BO_SUFFIXES_MAP[integer.byte_order]
880 t = self._get_obj_param_ctype(integer)
881 length = self._get_obj_size(integer)
882
883 return self._template_to_clines(barectf.templates.WRITE_INTEGER,
884 sz=length, bo=bo, type=t,
885 src_name=src_name)
886
887 # Returns the C lines for writing a TSDL enumeration field.
888 #
889 # fname: field name
890 # src_name: C source integer
891 # enum: TSDL enumeration
892 def _write_field_enum(self, fname, src_name, enum, scope_prefix=None):
893 return self._write_field_obj(fname, src_name, enum.integer)
894
895 # Returns the C lines for writing a TSDL floating point number field.
896 #
897 # fname: field name
898 # src_name: C source pointer
899 # floating_point: TSDL floating point number
900 def _write_field_floating_point(self, fname, src_name, floating_point,
901 scope_prefix=None):
902 bo = self._BO_SUFFIXES_MAP[floating_point.byte_order]
903 t = self._get_obj_param_ctype(floating_point)
904 length = self._get_obj_size(floating_point)
905
906 if t == 'float':
907 t = 'uint32_t'
908 elif t == 'double':
909 t = 'uint64_t'
910
911 src_name_casted = '({}) {}'.format(t, src_name)
912
913 return self._template_to_clines(barectf.templates.WRITE_INTEGER,
914 sz=length, bo=bo, type=t,
915 src_name=src_name_casted)
916
917 # Returns the C lines for writing either a TSDL array field or a
918 # TSDL sequence field.
919 #
920 # fname: field name
921 # src_name: C source pointer
922 # arrayseq: TSDL array or sequence
923 # scope_prefix: preferred scope prefix
924 def _write_field_array_sequence(self, fname, src_name, arrayseq,
925 scope_prefix):
926 def length_index_varname(index):
927 return 'lens_{}_{}'.format(fname, index)
928
929 # first pass: find all lengths to multiply
930 mulops = []
931 done = False
932
933 while not done:
934 mulops.append(arrayseq.length)
935 element = arrayseq.element
936 tel = type(element)
937
938 if tel is pytsdl.tsdl.Array or tel is pytsdl.tsdl.Sequence:
939 # another array/sequence; continue
940 arrayseq = element
941 continue
942
943 # found the end
944 done = True
945
946 # align the size of the repeating element (effective repeating size)
947 el_size = self._get_obj_size(element)
948 el_align = self._get_obj_alignment(element)
949 el_size = self._get_alignment(el_size, el_align)
950
951 # this effective size is part of the operands to multiply
952 mulops.append(el_size)
953
954 # clines
955 clines = []
956
957 # fetch and save sequence lengths
958 emulops = []
959
960 for i in range(len(mulops)):
961 mulop = mulops[i]
962
963 if type(mulop) is list:
964 # offset variable to fetch
965 offvar = self._get_seq_length_src_name(mulop, scope_prefix)
966
967 if type(offvar) is int:
968 # environment constant
969 emulops.append(str(offvar))
970 continue
971
972 # save buffer position
973 line = 'ctx_at_bkup = {};'.format(self._CTX_AT)
974 clines.append(_CLine(line))
975
976 # go back to field offset
977 line = '{} = {};'.format(self._CTX_AT, offvar)
978 clines.append(_CLine(line))
979
980 # read value into specific variable
981 varname = length_index_varname(i)
982 emulops.append(varname)
983 varctype = 'uint32_t'
984 fmt = '{ctype} {cname} = *(({ctype}*) ({ctxbufataddr}));'
985 line = fmt.format(ctype=varctype, cname=varname,
986 ctxbufataddr=self._CTX_BUF_AT_ADDR)
987 clines.append(_CLine(line))
988
989 # restore buffer position
990 line = '{} = ctx_at_bkup;'.format(self._CTX_AT)
991 clines.append(_CLine(line))
992 else:
993 emulops.append(str(mulop))
994
995 # write product of sizes in bits
996 mul = ' * '.join(emulops)
997 sz_bits_varname = 'sz_bits_{}'.format(fname)
998 sz_bytes_varname = 'sz_bytes_{}'.format(fname)
999 line = 'uint32_t {} = {};'.format(sz_bits_varname, mul)
1000 clines.append(_CLine(line))
1001
1002 # check overflow
1003 clines.append(self._get_chk_offset_v_cline(sz_bits_varname))
1004
1005 # write product of sizes in bytes
1006 line = 'uint32_t {} = {};'.format(sz_bytes_varname, sz_bits_varname)
1007 clines.append(_CLine(line))
1008 line = self._get_align_offset(8, at=sz_bytes_varname)
1009 clines.append(_CLine(line))
1010 line = '{} >>= 3;'.format(sz_bytes_varname)
1011 clines.append(_CLine(line))
1012
1013 # memcpy()
1014 dst = self._CTX_BUF_AT_ADDR
1015 line = 'memcpy({}, {}, {});'.format(dst, src_name, sz_bytes_varname)
1016 clines.append(_CLine(line))
1017 line = '{} += {};'.format(self._CTX_AT, sz_bits_varname)
1018 clines.append(_CLine(line))
1019
1020 return clines
1021
1022 # Returns the C lines for writing a TSDL array field.
1023 #
1024 # fname: field name
1025 # src_name: C source pointer
1026 # array: TSDL array
1027 # scope_prefix: preferred scope prefix
1028 def _write_field_array(self, fname, src_name, array, scope_prefix=None):
1029 return self._write_field_array_sequence(fname, src_name, array,
1030 scope_prefix)
1031
1032 # Returns the C lines for writing a TSDL sequence field.
1033 #
1034 # fname: field name
1035 # src_name: C source pointer
1036 # sequence: TSDL sequence
1037 # scope_prefix: preferred scope prefix
1038 def _write_field_sequence(self, fname, src_name, sequence, scope_prefix):
1039 return self._write_field_array_sequence(fname, src_name, sequence,
1040 scope_prefix)
1041
1042 # Returns a trace packet header C source name out of a sequence length
1043 # expression.
1044 #
1045 # length: sequence length expression
1046 def _get_tph_src_name(self, length):
1047 offvar = self._get_offvar_name_from_expr(length[3:], 'tph')
1048
1049 return 'ctx->{}'.format(offvar)
1050
1051 # Returns an environment C source name out of a sequence length
1052 # expression.
1053 #
1054 # length: sequence length expression
1055 def _get_env_src_name(self, length):
1056 if len(length) != 2:
1057 _perror('invalid sequence length: "{}"'.format(self._dot_name_to_str(length)))
1058
1059 fname = length[1]
1060
1061 if fname not in self._doc.env:
1062 _perror('cannot find field env.{}'.format(fname))
1063
1064 env_length = self._doc.env[fname]
1065
1066 if type(env_length) is not int:
1067 _perror('env.{} is not a constant integer'.format(fname))
1068
1069 return self._doc.env[fname]
1070
1071 # Returns a stream packet context C source name out of a sequence length
1072 # expression.
1073 #
1074 # length: sequence length expression
1075 def _get_spc_src_name(self, length):
1076 offvar = self._get_offvar_name_from_expr(length[3:], 'spc')
1077
1078 return 'ctx->{}'.format(offvar)
1079
1080 # Returns a stream event header C source name out of a sequence length
1081 # expression.
1082 #
1083 # length: sequence length expression
1084 def _get_seh_src_name(self, length):
1085 return self._get_offvar_name_from_expr(length[3:], 'seh')
1086
1087 # Returns a stream event context C source name out of a sequence length
1088 # expression.
1089 #
1090 # length: sequence length expression
1091 def _get_sec_src_name(self, length):
1092 return self._get_offvar_name_from_expr(length[3:], 'sec')
1093
1094 # Returns an event context C source name out of a sequence length
1095 # expression.
1096 #
1097 # length: sequence length expression
1098 def _get_ec_src_name(self, length):
1099 return self._get_offvar_name_from_expr(length[2:], 'ec')
1100
1101 # Returns an event fields C source name out of a sequence length
1102 # expression.
1103 #
1104 # length: sequence length expression
1105 def _get_ef_src_name(self, length):
1106 return self._get_offvar_name_from_expr(length[2:], 'ef')
1107
1108 # Returns a C source name out of a sequence length expression.
1109 #
1110 # length: sequence length expression
1111 # scope_prefix: preferred scope prefix
1112 def _get_seq_length_src_name(self, length, scope_prefix=None):
1113 length_dot = self._dot_name_to_str(length)
1114
1115 for prefix, get_src_name in self._get_src_name_funcs.items():
1116 if length_dot.startswith(prefix):
1117 return get_src_name(length)
1118
1119 return self._get_offvar_name_from_expr(length, scope_prefix)
1120
1121 # Returns the C lines for writing a TSDL string field.
1122 #
1123 # fname: field name
1124 # src_name: C source pointer
1125 # string: TSDL string
1126 def _write_field_string(self, fname, src_name, string, scope_prefix=None):
1127 clines = []
1128
1129 # get string length
1130 sz_bytes_varname = 'slen_bytes_{}'.format(fname)
1131 line = 'size_t {} = strlen({}) + 1;'.format(sz_bytes_varname, src_name)
1132 clines.append(_CLine(line))
1133
1134 # check offset overflow
1135 sz_bits_varname = 'slen_bits_{}'.format(fname)
1136 line = 'size_t {} = ({} << 3);'.format(sz_bits_varname,
1137 sz_bytes_varname)
1138 clines.append(_CLine(line))
1139 cline = self._get_chk_offset_v_cline(sz_bits_varname)
1140 clines.append(cline)
1141
1142 # memcpy()
1143 dst = self._CTX_BUF_AT_ADDR
1144 line = 'memcpy({}, {}, {});'.format(dst, src_name, sz_bytes_varname)
1145 clines.append(_CLine(line))
1146
1147 # update bit position
1148 line = '{} += {};'.format(self._CTX_AT, sz_bits_varname)
1149 clines.append(_CLine(line))
1150
1151 return clines
1152
1153 # Returns the C lines for writing a TSDL type field.
1154 #
1155 # fname: field name
1156 # src_name: C source pointer
1157 # ftype: TSDL type
1158 # scope_prefix: preferred scope prefix
1159 def _write_field_obj(self, fname, src_name, ftype, scope_prefix):
1160 return self._write_field_obj_cb[type(ftype)](fname, src_name, ftype,
1161 scope_prefix)
1162
1163 # Returns an offset variable name out of an offset name.
1164 #
1165 # name: offset name
1166 # prefix: offset variable name prefix
1167 def _get_offvar_name(self, name, prefix=None):
1168 parts = ['off']
1169
1170 if prefix is not None:
1171 parts.append(prefix)
1172
1173 parts.append(name)
1174
1175 return '_'.join(parts)
1176
1177 # Returns an offset variable name out of an expression (array of
1178 # strings).
1179 #
1180 # expr: array of strings
1181 # prefix: offset variable name prefix
1182 def _get_offvar_name_from_expr(self, expr, prefix=None):
1183 return self._get_offvar_name('_'.join(expr), prefix)
1184
1185 # Returns the C lines for writing a TSDL field.
1186 #
1187 # fname: field name
1188 # ftype: TSDL field type
1189 # scope_name: scope name
1190 # scope_prefix: preferred scope prefix
1191 # param_name_cb: callback to get the C parameter name out of the
1192 # field name
1193 def _field_to_clines(self, fname, ftype, scope_name, scope_prefix,
1194 param_name_cb):
1195 clines = []
1196 pname = param_name_cb(fname)
1197 align = self._get_obj_alignment(ftype)
1198
1199 # group comment
1200 fmt = '/* write {}.{} ({}) */'
1201 line = fmt.format(scope_name, fname,
1202 self._TSDL_TYPE_NAMES_MAP[type(ftype)])
1203 clines.append(_CLine(line))
1204
1205 # align bit index before writing to the buffer
1206 cline = self._get_align_offset_cline(align)
1207 clines.append(cline)
1208
1209 # write offset variables
1210 if type(ftype) is pytsdl.tsdl.Struct:
1211 offvars_tree = collections.OrderedDict()
1212 self._get_struct_size(ftype, offvars_tree)
1213 offvars = self._flatten_offvars_tree(offvars_tree)
1214
1215 # as many offset as there are child fields because a future
1216 # sequence could refer to any of those fields
1217 for lname, offset in offvars.items():
1218 offvar = self._get_offvar_name('_'.join([fname, lname]),
1219 scope_prefix)
1220 fmt = 'uint32_t {} = (uint32_t) {} + {};'
1221 line = fmt.format(offvar, self._CTX_AT, offset);
1222 clines.append(_CLine(line))
1223 elif type(ftype) is pytsdl.tsdl.Integer:
1224 # offset of this simple field is the current bit index
1225 offvar = self._get_offvar_name(fname, scope_prefix)
1226 line = 'uint32_t {} = (uint32_t) {};'.format(offvar, self._CTX_AT)
1227 clines.append(_CLine(line))
1228
1229 clines += self._write_field_obj(fname, pname, ftype, scope_prefix)
1230
1231 return clines
1232
1233 # Joins C line groups and returns C lines.
1234 #
1235 # cline_groups: C line groups to join
1236 def _join_cline_groups(self, cline_groups):
1237 if not cline_groups:
1238 return cline_groups
1239
1240 output_clines = cline_groups[0]
1241
1242 for clines in cline_groups[1:]:
1243 output_clines.append('')
1244 output_clines += clines
1245
1246 return output_clines
1247
1248 # Returns the C lines for writing a complete TSDL structure (top level
1249 # scope).
1250 #
1251 # struct: TSDL structure
1252 # scope_name: scope name
1253 # scope_prefix: preferred scope prefix
1254 # param_name_cb: callback to get the C parameter name out of the
1255 # field name
1256 def _struct_to_clines(self, struct, scope_name, scope_prefix,
1257 param_name_cb):
1258 cline_groups = []
1259
1260 for fname, ftype in struct.fields.items():
1261 clines = self._field_to_clines(fname, ftype, scope_name,
1262 scope_prefix, param_name_cb)
1263 cline_groups.append(clines)
1264
1265 return self._join_cline_groups(cline_groups)
1266
1267 # Returns the offset variables of a TSDL structure.
1268 #
1269 # struct: TSDL structure
1270 def _get_struct_size_offvars(self, struct):
1271 offvars_tree = collections.OrderedDict()
1272 size = self._get_struct_size(struct, offvars_tree)
1273 offvars = self._flatten_offvars_tree(offvars_tree)
1274
1275 return size, offvars
1276
1277 # Returns the size and offset variables of the current trace packet header.
1278 def _get_tph_size_offvars(self):
1279 return self._get_struct_size_offvars(self._doc.trace.packet_header)
1280
1281 # Returns the size and offset variables of the a stream packet context.
1282 #
1283 # stream: TSDL stream
1284 def _get_spc_size_offvars(self, stream):
1285 return self._get_struct_size_offvars(stream.packet_context)
1286
1287 # Returns the C lines for the barectf context C structure entries for
1288 # offsets.
1289 #
1290 # prefix: offset variable names prefix
1291 # offvars: offset variables
1292 def _offvars_to_ctx_clines(self, prefix, offvars):
1293 clines = []
1294
1295 for name in offvars.keys():
1296 offvar = self._get_offvar_name(name, prefix)
1297 clines.append(_CLine('uint32_t {};'.format(offvar)))
1298
1299 return clines
1300
1301 # Generates a barectf context C structure.
1302 #
1303 # stream: TSDL stream
1304 # hide_sid: True to hide the stream ID
1305 def _gen_barectf_ctx_struct(self, stream, hide_sid=False):
1306 # get offset variables for both the packet header and packet context
1307 tph_size, tph_offvars = self._get_tph_size_offvars()
1308 spc_size, spc_offvars = self._get_spc_size_offvars(stream)
1309 clines = self._offvars_to_ctx_clines('tph', tph_offvars)
1310 clines += self._offvars_to_ctx_clines('spc', spc_offvars)
1311
1312 # indent C
1313 clines_indented = []
1314 for cline in clines:
1315 clines_indented.append(_CLine('\t' + cline))
1316
1317 # clock callback
1318 clock_cb = '\t/* (no clock callback) */'
1319
1320 if not self._manual_clock:
1321 ctype = self._get_clock_ctype(stream)
1322 fmt = '\t{} (*clock_cb)(void*);\n\tvoid* clock_cb_data;'
1323 clock_cb = fmt.format(ctype)
1324
1325 # fill template
1326 sid = ''
1327
1328 if not hide_sid:
1329 sid = stream.id
1330
1331 t = barectf.templates.BARECTF_CTX
1332 struct = t.format(prefix=self._prefix, sid=sid,
1333 ctx_fields='\n'.join(clines_indented),
1334 clock_cb=clock_cb)
1335
1336 return struct
1337
1338 # Generates all barectf context C structures.
1339 def _gen_barectf_contexts_struct(self):
1340 hide_sid = False
1341
1342 if len(self._doc.streams) == 1:
1343 hide_sid = True
1344
1345 structs = []
1346
1347 for stream in self._doc.streams.values():
1348 struct = self._gen_barectf_ctx_struct(stream, hide_sid)
1349 structs.append(struct)
1350
1351 return '\n\n'.join(structs)
1352
1353 # Returns the C type of the clock used by the event header of a
1354 # TSDL stream.
1355 #
1356 # stream: TSDL stream containing the event header to inspect
1357 def _get_clock_ctype(self, stream):
1358 return self._get_obj_param_ctype(stream.event_header['timestamp'])
1359
1360 # Generates the manual clock value C parameter for a given stream.
1361 #
1362 # stream: TSDL stream
1363 def _gen_manual_clock_param(self, stream):
1364 return '{} param_clock'.format(self._get_clock_ctype(stream))
1365
1366 # Generates the body of a barectf_open() function.
1367 #
1368 # stream: TSDL stream
1369 def _gen_barectf_func_open_body(self, stream):
1370 clines = []
1371
1372 # keep clock value (for timestamp_begin)
1373 if self._stream_has_timestamp_begin_end(stream):
1374 # get clock value ASAP
1375 clk_type = self._get_clock_ctype(stream)
1376 clk = self._gen_get_clock_value()
1377 line = '{} clk_value = {};'.format(clk_type, clk)
1378 clines.append(_CLine(line))
1379 clines.append(_CLine(''))
1380
1381 # packet context fields
1382 fcline_groups = []
1383 scope_name = 'stream.packet.context'
1384 scope_prefix = 'spc'
1385
1386 for fname, ftype in stream.packet_context.fields.items():
1387 # packet size
1388 if fname == 'packet_size':
1389 fclines = self._field_to_clines(fname, ftype, scope_name,
1390 scope_prefix,
1391 lambda x: 'ctx->packet_size')
1392 fcline_groups.append(fclines)
1393
1394 # content size (skip)
1395 elif fname == 'content_size':
1396 fclines = self._field_to_clines(fname, ftype, scope_name,
1397 scope_prefix, lambda x: '0')
1398 fcline_groups.append(fclines)
1399
1400 # timestamp_begin
1401 elif fname == 'timestamp_begin':
1402 fclines = self._field_to_clines(fname, ftype, scope_name,
1403 scope_prefix,
1404 lambda x: 'clk_value')
1405 fcline_groups.append(fclines)
1406
1407 # timestamp_end (skip)
1408 elif fname == 'timestamp_end':
1409 fclines = self._field_to_clines(fname, ftype, scope_name,
1410 scope_prefix, lambda x: '0')
1411 fcline_groups.append(fclines)
1412
1413 # anything else
1414 else:
1415 fclines = self._field_to_clines(fname, ftype, scope_name,
1416 scope_prefix,
1417 self._spc_fname_to_pname)
1418 fcline_groups.append(fclines)
1419
1420 # return 0
1421 fcline_groups.append([_CLine('return 0;')])
1422
1423 clines += self._join_cline_groups(fcline_groups)
1424
1425 # get source
1426 cblock = _CBlock(clines)
1427 src = self._cblock_to_source(cblock)
1428
1429 return src
1430
1431 _SPC_KNOWN_FIELDS = [
1432 'content_size',
1433 'packet_size',
1434 'timestamp_begin',
1435 'timestamp_end',
1436 ]
1437
1438 # Generates a barectf_open() function.
1439 #
1440 # stream: TSDL stream
1441 # gen_body: also generate function body
1442 # hide_sid: True to hide the stream ID
1443 def _gen_barectf_func_open(self, stream, gen_body, hide_sid=False):
1444 params = []
1445
1446 # manual clock
1447 if self._manual_clock:
1448 clock_param = self._gen_manual_clock_param(stream)
1449 params.append(clock_param)
1450
1451 # packet context
1452 for fname, ftype in stream.packet_context.fields.items():
1453 if fname in self._SPC_KNOWN_FIELDS:
1454 continue
1455
1456 ptype = self._get_obj_param_ctype(ftype)
1457 pname = self._spc_fname_to_pname(fname)
1458 param = '{} {}'.format(ptype, pname)
1459 params.append(param)
1460
1461 params_str = ''
1462
1463 if params:
1464 params_str = ',\n\t'.join([''] + params)
1465
1466 # fill template
1467 sid = ''
1468
1469 if not hide_sid:
1470 sid = stream.id
1471
1472 t = barectf.templates.FUNC_OPEN
1473 func = t.format(si=self._si_str, prefix=self._prefix, sid=sid,
1474 params=params_str)
1475
1476 if gen_body:
1477 func += '\n{\n'
1478 func += self._gen_barectf_func_open_body(stream)
1479 func += '\n}'
1480 else:
1481 func += ';'
1482
1483 return func
1484
1485 # Generates the body of a barectf_init() function.
1486 #
1487 # stream: TSDL stream
1488 def _gen_barectf_func_init_body(self, stream):
1489 clines = []
1490
1491 line = 'uint32_t ctx_at_bkup;'
1492 clines.append(_CLine(line))
1493
1494 # set context parameters
1495 clines.append(_CLine(''))
1496 clines.append(_CLine("/* barectf context parameters */"))
1497 clines.append(_CLine('ctx->buf = buf;'))
1498 clines.append(_CLine('ctx->packet_size = buf_size * 8;'))
1499 clines.append(_CLine('{} = 0;'.format(self._CTX_AT)))
1500
1501 if not self._manual_clock:
1502 clines.append(_CLine('ctx->clock_cb = clock_cb;'))
1503 clines.append(_CLine('ctx->clock_cb_data = clock_cb_data;'))
1504
1505 # set context offsets
1506 clines.append(_CLine(''))
1507 clines.append(_CLine("/* barectf context offsets */"))
1508 ph_size, ph_offvars = self._get_tph_size_offvars()
1509 pc_size, pc_offvars = self._get_spc_size_offvars(stream)
1510 pc_alignment = self._get_obj_alignment(stream.packet_context)
1511 pc_offset = self._get_alignment(ph_size, pc_alignment)
1512
1513 for offvar, offset in ph_offvars.items():
1514 offvar_field = self._get_offvar_name(offvar, 'tph')
1515 line = 'ctx->{} = {};'.format(offvar_field, offset)
1516 clines.append(_CLine(line))
1517
1518 for offvar, offset in pc_offvars.items():
1519 offvar_field = self._get_offvar_name(offvar, 'spc')
1520 line = 'ctx->{} = {};'.format(offvar_field, pc_offset + offset)
1521 clines.append(_CLine(line))
1522
1523 clines.append(_CLine(''))
1524
1525 # packet header fields
1526 fcline_groups = []
1527 scope_name = 'trace.packet.header'
1528 scope_prefix = 'tph'
1529
1530 for fname, ftype in self._doc.trace.packet_header.fields.items():
1531 # magic number
1532 if fname == 'magic':
1533 fclines = self._field_to_clines(fname, ftype, scope_name,
1534 scope_prefix,
1535 lambda x: '0xc1fc1fc1UL')
1536 fcline_groups.append(fclines)
1537
1538 # stream ID
1539 elif fname == 'stream_id':
1540 fclines = self._field_to_clines(fname, ftype, scope_name,
1541 scope_prefix,
1542 lambda x: str(stream.id))
1543 fcline_groups.append(fclines)
1544
1545 # return 0
1546 fcline_groups.append([_CLine('return 0;')])
1547
1548 clines += self._join_cline_groups(fcline_groups)
1549
1550 # get source
1551 cblock = _CBlock(clines)
1552 src = self._cblock_to_source(cblock)
1553
1554 return src
1555
1556 # Generates a barectf_init() function.
1557 #
1558 # stream: TSDL stream
1559 # gen_body: also generate function body
1560 # hide_sid: True to hide the stream ID
1561 def _gen_barectf_func_init(self, stream, gen_body, hide_sid=False):
1562 # fill template
1563 sid = ''
1564
1565 if not hide_sid:
1566 sid = stream.id
1567
1568 params = ''
1569
1570 if not self._manual_clock:
1571 ts_ftype = stream.event_header['timestamp']
1572 ts_ptype = self._get_obj_param_ctype(ts_ftype)
1573 fmt = ',\n\t{} (*clock_cb)(void*),\n\tvoid* clock_cb_data'
1574 params = fmt.format(ts_ptype)
1575
1576 t = barectf.templates.FUNC_INIT
1577 func = t.format(si=self._si_str, prefix=self._prefix, sid=sid,
1578 params=params)
1579
1580 if gen_body:
1581 func += '\n{\n'
1582 func += self._gen_barectf_func_init_body(stream)
1583 func += '\n}'
1584 else:
1585 func += ';'
1586
1587 return func
1588
1589 # Generates the C expression to get the clock value depending on
1590 # whether we're in manual clock mode or not.
1591 def _gen_get_clock_value(self):
1592 if self._manual_clock:
1593 return 'param_clock'
1594 else:
1595 return self._CTX_CALL_CLOCK_CB
1596
1597 # Returns True if the given TSDL stream has timestamp_begin and
1598 # timestamp_end fields.
1599 #
1600 # stream: TSDL stream to check
1601 def _stream_has_timestamp_begin_end(self, stream):
1602 return self._has_timestamp_begin_end[stream.id]
1603
1604 # Generates the C lines to write a barectf context field, saving
1605 # and restoring the current bit position accordingly.
1606 #
1607 # src_name: C source name
1608 # prefix: offset variable prefix
1609 # name: offset variable name
1610 # integer: TSDL integer to write
1611 def _gen_write_ctx_field_integer(self, src_name, prefix, name, integer):
1612 clines = []
1613
1614 # save buffer position
1615 line = 'ctx_at_bkup = {};'.format(self._CTX_AT)
1616 clines.append(_CLine(line))
1617
1618 # go back to field offset
1619 offvar = self._get_offvar_name(name, prefix)
1620 line = '{} = ctx->{};'.format(self._CTX_AT, offvar)
1621 clines.append(_CLine(line))
1622
1623 # write value
1624 clines += self._write_field_integer(None, src_name, integer)
1625
1626 # restore buffer position
1627 line = '{} = ctx_at_bkup;'.format(self._CTX_AT)
1628 clines.append(_CLine(line))
1629
1630 return clines
1631
1632 # Generates the body of a barectf_close() function.
1633 #
1634 # stream: TSDL stream
1635 def _gen_barectf_func_close_body(self, stream):
1636 clines = []
1637
1638 line = 'uint32_t ctx_at_bkup;'
1639 clines.append(_CLine(line))
1640
1641 # update timestamp end if present
1642 if self._stream_has_timestamp_begin_end(stream):
1643 clines.append(_CLine(''))
1644 clines.append(_CLine("/* update packet context's timestamp_end */"))
1645
1646 # get clock value ASAP
1647 clk_type = self._get_clock_ctype(stream)
1648 clk = self._gen_get_clock_value()
1649 line = '{} clk_value = {};'.format(clk_type, clk)
1650 clines.append(_CLine(line))
1651
1652 # write timestamp_end
1653 timestamp_end_integer = stream.packet_context['timestamp_end']
1654 clines += self._gen_write_ctx_field_integer('clk_value', 'spc',
1655 'timestamp_end',
1656 timestamp_end_integer)
1657
1658 # update content_size
1659 clines.append(_CLine(''))
1660 clines.append(_CLine("/* update packet context's content_size */"))
1661 content_size_integer = stream.packet_context['content_size']
1662 clines += self._gen_write_ctx_field_integer('ctx_at_bkup', 'spc',
1663 'content_size',
1664 content_size_integer)
1665
1666 # return 0
1667 clines.append(_CLine('\n'))
1668 clines.append(_CLine('return 0;'))
1669
1670 # get source
1671 cblock = _CBlock(clines)
1672 src = self._cblock_to_source(cblock)
1673
1674 return src
1675
1676 # Generates a barectf_close() function.
1677 #
1678 # stream: TSDL stream
1679 # gen_body: also generate function body
1680 # hide_sid: True to hide the stream ID
1681 def _gen_barectf_func_close(self, stream, gen_body, hide_sid=False):
1682 # fill template
1683 sid = ''
1684
1685 if not hide_sid:
1686 sid = stream.id
1687
1688 params = ''
1689
1690 if self._manual_clock:
1691 clock_param = self._gen_manual_clock_param(stream)
1692 params = ',\n\t{}'.format(clock_param)
1693
1694 t = barectf.templates.FUNC_CLOSE
1695 func = t.format(si=self._si_str, prefix=self._prefix, sid=sid,
1696 params=params)
1697
1698 if gen_body:
1699 func += '\n{\n'
1700 func += self._gen_barectf_func_close_body(stream)
1701 func += '\n}'
1702 else:
1703 func += ';'
1704
1705 return func
1706
1707 # Generates all barectf_init() function.
1708 #
1709 # gen_body: also generate function bodies
1710 def _gen_barectf_funcs_init(self, gen_body):
1711 hide_sid = False
1712
1713 if len(self._doc.streams) == 1:
1714 hide_sid = True
1715
1716 funcs = []
1717
1718 for stream in self._doc.streams.values():
1719 funcs.append(self._gen_barectf_func_init(stream, gen_body,
1720 hide_sid))
1721
1722 return funcs
1723
1724 # Generates all barectf_open() function.
1725 #
1726 # gen_body: also generate function bodies
1727 def _gen_barectf_funcs_open(self, gen_body):
1728 hide_sid = False
1729
1730 if len(self._doc.streams) == 1:
1731 hide_sid = True
1732
1733 funcs = []
1734
1735 for stream in self._doc.streams.values():
1736 funcs.append(self._gen_barectf_func_open(stream, gen_body,
1737 hide_sid))
1738
1739 return funcs
1740
1741 # Generates the body of a barectf_trace() function.
1742 #
1743 # stream: TSDL stream of TSDL event to trace
1744 # event: TSDL event to trace
1745 def _gen_barectf_func_trace_event_body(self, stream, event):
1746 clines = []
1747
1748 clines.append(_CLine('uint32_t ctx_at_bkup;'))
1749
1750 # get clock value ASAP
1751 clk_type = self._get_clock_ctype(stream)
1752 clk = self._gen_get_clock_value()
1753 line = '{} clk_value = {};'.format(clk_type, clk)
1754 clines.append(_CLine(line))
1755 clines.append(_CLine(''))
1756
1757 # event header
1758 fcline_groups = []
1759 scope_name = 'event.header'
1760 scope_prefix = 'eh'
1761
1762 for fname, ftype in stream.event_header.fields.items():
1763 # id
1764 if fname == 'id':
1765 fclines = self._field_to_clines(fname, ftype, scope_name,
1766 scope_prefix,
1767 lambda x: str(event.id))
1768 fcline_groups.append(fclines)
1769
1770 # timestamp
1771 elif fname == 'timestamp':
1772 fclines = self._field_to_clines(fname, ftype, scope_name,
1773 scope_prefix,
1774 lambda x: 'clk_value')
1775 fcline_groups.append(fclines)
1776
1777 # stream event context
1778 if stream.event_context is not None:
1779 fclines = self._struct_to_clines(stream.event_context,
1780 'stream.event.context', 'sec',
1781 self._sec_fname_to_pname)
1782 fcline_groups.append(fclines)
1783
1784 # event context
1785 if event.context is not None:
1786 fclines = self._struct_to_clines(event.context,
1787 'event.context', 'ec',
1788 self._ec_fname_to_pname)
1789 fcline_groups.append(fclines)
1790
1791 # event fields
1792 if event.fields is not None:
1793 fclines = self._struct_to_clines(event.fields,
1794 'event.fields', 'ef',
1795 self._ef_fname_to_pname)
1796 fcline_groups.append(fclines)
1797
1798 # return 0
1799 fcline_groups.append([_CLine('return 0;')])
1800
1801 clines += self._join_cline_groups(fcline_groups)
1802
1803 # get source
1804 cblock = _CBlock(clines)
1805 src = self._cblock_to_source(cblock)
1806
1807 return src
1808
1809 # Generates a barectf_trace() function.
1810 #
1811 # stream: TSDL stream containing the TSDL event to trace
1812 # event: TSDL event to trace
1813 # gen_body: also generate function body
1814 # hide_sid: True to hide the stream ID
1815 def _gen_barectf_func_trace_event(self, stream, event, gen_body, hide_sid):
1816 params = []
1817
1818 # manual clock
1819 if self._manual_clock:
1820 clock_param = self._gen_manual_clock_param(stream)
1821 params.append(clock_param)
1822
1823 # stream event context params
1824 if stream.event_context is not None:
1825 for fname, ftype in stream.event_context.fields.items():
1826 ptype = self._get_obj_param_ctype(ftype)
1827 pname = self._sec_fname_to_pname(fname)
1828 param = '{} {}'.format(ptype, pname)
1829 params.append(param)
1830
1831 # event context params
1832 if event.context is not None:
1833 for fname, ftype in event.context.fields.items():
1834 ptype = self._get_obj_param_ctype(ftype)
1835 pname = self._ec_fname_to_pname(fname)
1836 param = '{} {}'.format(ptype, pname)
1837 params.append(param)
1838
1839 # event fields params
1840 if event.fields is not None:
1841 for fname, ftype in event.fields.fields.items():
1842 ptype = self._get_obj_param_ctype(ftype)
1843 pname = self._ef_fname_to_pname(fname)
1844 param = '{} {}'.format(ptype, pname)
1845 params.append(param)
1846
1847 params_str = ''
1848
1849 if params:
1850 params_str = ',\n\t'.join([''] + params)
1851
1852 # fill template
1853 sid = ''
1854
1855 if not hide_sid:
1856 sid = stream.id
1857
1858 t = barectf.templates.FUNC_TRACE
1859 func = t.format(si=self._si_str, prefix=self._prefix, sid=sid,
1860 evname=event.name, params=params_str)
1861
1862 if gen_body:
1863 func += '\n{\n'
1864 func += self._gen_barectf_func_trace_event_body(stream, event)
1865 func += '\n}'
1866 else:
1867 func += ';'
1868
1869 return func
1870
1871 # Generates all barectf_trace() functions of a given TSDL stream.
1872 #
1873 # stream: TSDL stream containing the TSDL events to trace
1874 # gen_body: also generate function body
1875 # hide_sid: True to hide the stream ID
1876 def _gen_barectf_funcs_trace_stream(self, stream, gen_body, hide_sid):
1877 funcs = []
1878
1879 for event in stream.events:
1880 funcs.append(self._gen_barectf_func_trace_event(stream, event,
1881 gen_body, hide_sid))
1882
1883 return funcs
1884
1885 # Generates all barectf_trace() function.
1886 #
1887 # gen_body: also generate function bodies
1888 def _gen_barectf_funcs_trace(self, gen_body):
1889 hide_sid = False
1890
1891 if len(self._doc.streams) == 1:
1892 hide_sid = True
1893
1894 funcs = []
1895
1896 for stream in self._doc.streams.values():
1897 funcs += self._gen_barectf_funcs_trace_stream(stream, gen_body,
1898 hide_sid)
1899
1900 return funcs
1901
1902 # Generates all barectf_close() function.
1903 #
1904 # gen_body: also generate function bodies
1905 def _gen_barectf_funcs_close(self, gen_body):
1906 hide_sid = False
1907
1908 if len(self._doc.streams) == 1:
1909 hide_sid = True
1910
1911 funcs = []
1912
1913 for stream in self._doc.streams.values():
1914 funcs.append(self._gen_barectf_func_close(stream, gen_body,
1915 hide_sid))
1916
1917 return funcs
1918
1919 # Generate all barectf functions
1920 #
1921 # gen_body: also generate function bodies
1922 def _gen_barectf_functions(self, gen_body):
1923 init_funcs = self._gen_barectf_funcs_init(gen_body)
1924 open_funcs = self._gen_barectf_funcs_open(gen_body)
1925 close_funcs = self._gen_barectf_funcs_close(gen_body)
1926 trace_funcs = self._gen_barectf_funcs_trace(gen_body)
1927
1928 return init_funcs + open_funcs + close_funcs + trace_funcs
1929
1930 # Generates the barectf header C source
1931 def _gen_barectf_header(self):
1932 ctx_structs = self._gen_barectf_contexts_struct()
1933 functions = self._gen_barectf_functions(self._static_inline)
1934 functions_str = '\n\n'.join(functions)
1935 t = barectf.templates.HEADER
1936 header = t.format(prefix=self._prefix, ucprefix=self._prefix.upper(),
1937 barectf_ctx=ctx_structs, functions=functions_str)
1938
1939 return header
1940
1941 _BO_DEF_MAP = {
1942 pytsdl.tsdl.ByteOrder.BE: 'BIG_ENDIAN',
1943 pytsdl.tsdl.ByteOrder.LE: 'LITTLE_ENDIAN',
1944 }
1945
1946 # Generates the barectf bitfield.h header.
1947 def _gen_barectf_bitfield_header(self):
1948 header = barectf.templates.BITFIELD
1949 header = header.replace('$prefix$', self._prefix)
1950 header = header.replace('$PREFIX$', self._prefix.upper())
1951 endian_def = self._BO_DEF_MAP[self._doc.trace.byte_order]
1952 header = header.replace('$ENDIAN_DEF$', endian_def)
1953
1954 return header
1955
1956 # Generates the main barectf C source file.
1957 def _gen_barectf_csrc(self):
1958 functions = self._gen_barectf_functions(True)
1959 functions_str = '\n\n'.join(functions)
1960 t = barectf.templates.CSRC
1961 csrc = t.format(prefix=self._prefix, ucprefix=self._prefix.upper(),
1962 functions=functions_str)
1963
1964 return csrc
1965
1966 # Writes a file to the generator's output.
1967 #
1968 # name: file name
1969 # contents: file contents
1970 def _write_file(self, name, contents):
1971 path = os.path.join(self._output, name)
1972 try:
1973 with open(path, 'w') as f:
1974 f.write(contents)
1975 except Exception as e:
1976 _perror('cannot write "{}": {}'.format(path, e))
1977
1978 # Converts a C block to actual C source lines.
1979 #
1980 # cblock: C block
1981 # indent: initial indentation
1982 def _cblock_to_source_lines(self, cblock, indent=1):
1983 src = []
1984 indentstr = '\t' * indent
1985
1986 for line in cblock:
1987 if type(line) is _CBlock:
1988 src += self._cblock_to_source_lines(line, indent + 1)
1989 else:
1990 src.append(indentstr + line)
1991
1992 return src
1993
1994 # Converts a C block to an actual C source string.
1995 #
1996 # cblock: C block
1997 # indent: initial indentation
1998 def _cblock_to_source(self, cblock, indent=1):
1999 lines = self._cblock_to_source_lines(cblock, indent)
2000
2001 return '\n'.join(lines)
2002
2003 # Sets the generator parameters.
2004 def _set_params(self):
2005 # streams have timestamp_begin/timestamp_end fields
2006 self._has_timestamp_begin_end = {}
2007
2008 for stream in self._doc.streams.values():
2009 has = 'timestamp_begin' in stream.packet_context.fields
2010 self._has_timestamp_begin_end[stream.id] = has
2011
2012 # Generates barectf C files.
2013 #
2014 # metadata: metadata path
2015 # output: output directory
2016 # prefix: prefix
2017 # static_inline: generate static inline functions
2018 # manual_clock: do not use a clock callback: pass clock value to
2019 # tracing functions
2020 def gen_barectf(self, metadata, output, prefix, static_inline,
2021 manual_clock):
2022 self._metadata = metadata
2023 self._output = output
2024 self._prefix = prefix
2025 self._static_inline = static_inline
2026 self._manual_clock = manual_clock
2027 self._si_str = ''
2028
2029 if static_inline:
2030 self._si_str = 'static inline '
2031
2032 # open CTF metadata file
2033 _pinfo('opening CTF metadata file "{}"'.format(self._metadata))
2034
2035 try:
2036 with open(metadata) as f:
2037 self._tsdl = f.read()
2038 except:
2039 _perror('cannot open/read CTF metadata file "{}"'.format(metadata))
2040
2041 # parse CTF metadata
2042 _pinfo('parsing CTF metadata file')
2043
2044 try:
2045 self._doc = self._parser.parse(self._tsdl)
2046 except pytsdl.parser.ParseError as e:
2047 _perror('parse error: {}'.format(e))
2048
2049 # validate CTF metadata against barectf constraints
2050 _pinfo('validating CTF metadata file')
2051 self._validate_metadata()
2052 _psuccess('CTF metadata file is valid')
2053
2054 # set parameters for this generation
2055 self._set_params()
2056
2057 # generate header
2058 _pinfo('generating barectf header files')
2059 header = self._gen_barectf_header()
2060 self._write_file('{}.h'.format(self._prefix), header)
2061 header = self._gen_barectf_bitfield_header()
2062 self._write_file('{}_bitfield.h'.format(self._prefix), header)
2063
2064 # generate C source file
2065 if not self._static_inline:
2066 _pinfo('generating barectf C source file')
2067 csrc = self._gen_barectf_csrc()
2068 self._write_file('{}.c'.format(self._prefix), csrc)
2069
2070 _psuccess('done')
2071
2072
2073 def run():
2074 args = _parse_args()
2075 generator = BarectfCodeGenerator()
2076 generator.gen_barectf(args.metadata, args.output, args.prefix,
2077 args.static_inline, args.manual_clock)
This page took 0.0767330000000001 seconds and 5 git commands to generate.