2f314e0f530872da52f50333181a106abc2b7894
1 # The MIT License (MIT)
3 # Copyright (c) 2014 Philippe Proulx <philippe.proulx@efficios.com>
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:
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
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
22 from termcolor
import cprint
, colored
23 import barectf
.templates
33 def _perror(msg
, exit_code
=1):
34 cprint('error: {}'.format(msg
), 'red', attrs
=['bold'], file=sys
.stderr
)
39 cprint(':: {}'.format(msg
), 'blue', attrs
=['bold'])
43 cprint('{}'.format(msg
), 'green', attrs
=['bold'])
47 ap
= argparse
.ArgumentParser()
49 ap
.add_argument('-O', '--output', metavar
='OUTPUT', action
='store',
51 help='output directory of C files')
52 ap
.add_argument('-p', '--prefix', metavar
='PREFIX', action
='store',
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')
63 args
= ap
.parse_args()
65 # validate output directory
66 if not os
.path
.isdir(args
.output
):
67 _perror('"{}" is not an existing directory'.format(args
.output
))
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
))
73 # validate that metadata file exists
74 if not os
.path
.isfile(args
.metadata
):
75 _perror('"{}" is not an existing file'.format(args
.metadata
))
88 class BarectfCodeGenerator
:
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)'
97 pytsdl
.tsdl
.ByteOrder
.BE
: 'be',
98 pytsdl
.tsdl
.ByteOrder
.LE
: 'le',
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',
112 self
._parser
= pytsdl
.parser
.Parser()
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
,
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
,
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*',
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
,
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
,
162 # Finds the terminal element of a TSDL array/sequence.
164 # arrayseq: array or sequence
165 def _find_arrayseq_element(self
, arrayseq
):
166 el
= arrayseq
.element
167 t
= type(arrayseq
.element
)
169 if t
is pytsdl
.tsdl
.Array
or t
is pytsdl
.tsdl
.Sequence
:
170 return self
._find
_arrayseq
_element
(el
)
174 # Validates an inner TSDL structure's field (constrained structure).
178 def _validate_struct_field(self
, fname
, ftype
, inner_struct
):
179 if type(ftype
) is pytsdl
.tsdl
.Sequence
:
181 raise RuntimeError('field "{}" is a dynamic array (not allowed here)'.format(fname
))
183 element
= self
._find
_arrayseq
_element
(ftype
)
184 self
._validate
_struct
_field
(fname
, element
, True)
185 elif type(ftype
) is pytsdl
.tsdl
.Array
:
186 # we need to check every element until we find a terminal one
187 element
= self
._find
_arrayseq
_element
(ftype
)
188 self
._validate
_struct
_field
(fname
, element
, True)
189 elif type(ftype
) is pytsdl
.tsdl
.Variant
:
190 raise RuntimeError('field "{}" contains a variant (unsupported)'.format(fname
))
191 elif type(ftype
) is pytsdl
.tsdl
.String
:
193 raise RuntimeError('field "{}" contains a string (not allowed here)'.format(fname
))
194 elif type(ftype
) is pytsdl
.tsdl
.Struct
:
195 self
._validate
_struct
(ftype
, True)
196 elif type(ftype
) is pytsdl
.tsdl
.Integer
:
197 if self
._get
_obj
_size
(ftype
) > 64:
198 raise RuntimeError('integer field "{}" larger than 64-bit'.format(fname
))
199 elif type(ftype
) is pytsdl
.tsdl
.FloatingPoint
:
200 if self
._get
_obj
_size
(ftype
) > 64:
201 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(fname
))
202 elif type(ftype
) is pytsdl
.tsdl
.Enum
:
203 if self
._get
_obj
_size
(ftype
) > 64:
204 raise RuntimeError('enum field "{}" larger than 64-bit'.format(fname
))
206 # Validates an inner TSDL structure (constrained).
208 # struct: TSDL structure to validate
209 def _validate_struct(self
, struct
, inner_struct
):
210 # just in case we call this with the wrong type
211 if type(struct
) is not pytsdl
.tsdl
.Struct
:
212 raise RuntimeError('expecting a struct')
214 # make sure inner structures are at least byte-aligned
216 if self
._get
_obj
_alignment
(struct
) < 8:
217 raise RuntimeError('inner struct must be at least byte-aligned')
220 for fname
, ftype
in struct
.fields
.items():
221 self
._validate
_struct
_field
(fname
, ftype
, inner_struct
)
223 # Validates a context or fields structure.
225 # struct: context/fields TSDL structure
226 def _validate_context_fields(self
, struct
):
227 if type(struct
) is not pytsdl
.tsdl
.Struct
:
228 raise RuntimeError('expecting a struct')
230 self
._validate
_struct
(struct
, False)
232 # Validates a TSDL integer with optional constraints.
234 # integer: TSDL integer to validate
235 # size: expected size (None for any size)
236 # align: expected alignment (None for any alignment)
237 # signed: expected signedness (None for any signedness)
238 def _validate_integer(self
, integer
, size
=None, align
=None,
240 if type(integer
) is not pytsdl
.tsdl
.Integer
:
241 raise RuntimeError('expected integer')
244 if integer
.size
!= size
:
245 raise RuntimeError('expected {}-bit integer'.format(size
))
247 if align
is not None:
248 if integer
.align
!= align
:
249 raise RuntimeError('expected integer with {}-bit alignment'.format(align
))
251 if signed
is not None:
252 if integer
.signed
!= signed
:
253 raise RuntimeError('expected {} integer'.format('signed' if signed
else 'unsigned'))
255 # Validates a packet header.
257 # packet_header: packet header TSDL structure to validate
258 def _validate_tph(self
, packet_header
):
260 self
._validate
_struct
(packet_header
, True)
261 except RuntimeError as e
:
262 _perror('packet header: {}'.format(e
))
264 # magic must be the first field
265 if 'magic' in packet_header
.fields
:
266 if list(packet_header
.fields
.keys())[0] != 'magic':
267 _perror('packet header: "magic" must be the first field')
269 _perror('packet header: missing "magic" field')
271 # magic must be a 32-bit unsigned integer, 32-bit aligned
273 self
._validate
_integer
(packet_header
['magic'], 32, 32, False)
274 except RuntimeError as e
:
275 _perror('packet header: "magic": {}'.format(e
))
277 # mandatory stream_id
278 if 'stream_id' not in packet_header
.fields
:
279 _perror('packet header: missing "stream_id" field')
281 # stream_id must be an unsigned integer
283 self
._validate
_integer
(packet_header
['stream_id'], signed
=False)
284 except RuntimeError as e
:
285 _perror('packet header: "stream_id": {}'.format(e
))
287 # only magic and stream_id allowed
288 if len(packet_header
.fields
) != 2:
289 _perror('packet header: only "magic" and "stream_id" fields are allowed')
291 # Converts a list of strings to a dotted representation. For
292 # example, ['trace', 'packet', 'header', 'magic'] is converted to
293 # 'trace.packet.header.magic'.
295 # name: list of strings to convert
296 def _dot_name_to_str(self
, name
):
297 return '.'.join(name
)
299 # Compares two TSDL integers. Returns True if they are the same.
301 # int1: first TSDL integer
302 # int2: second TSDL integer
303 def _compare_integers(self
, int1
, int2
):
304 if type(int1
) is not pytsdl
.tsdl
.Integer
:
307 if type(int2
) is not pytsdl
.tsdl
.Integer
:
310 size
= int1
.size
== int2
.size
311 align
= int1
.align
== int2
.align
312 cmap
= int1
.map == int2
.map
313 base
= int1
.base
== int2
.base
314 encoding
= int1
.encoding
== int2
.encoding
315 signed
= int1
.signed
== int2
.signed
316 comps
= (size
, align
, cmap
, base
, encoding
, signed
)
318 # True means 1 for sum()
319 return sum(comps
) == len(comps
)
321 # Validates a packet context.
323 # stream: TSDL stream containing the packet context to validate
324 def _validate_spc(self
, stream
):
325 packet_context
= stream
.packet_context
329 self
._validate
_struct
(packet_context
, True)
330 except RuntimeError as e
:
331 _perror('stream {}: packet context: {}'.format(sid
, e
))
333 fields
= packet_context
.fields
335 # if timestamp_begin exists, timestamp_end must exist
336 if 'timestamp_begin' in fields
or 'timestamp_end' in fields
:
337 if 'timestamp_begin' not in fields
or 'timestamp_end' not in fields
:
338 _perror('stream {}: packet context: "timestamp_begin" must exist if "timestamp_end" exists'.format(sid
))
340 # timestamp_begin and timestamp_end must have the same integer
341 # as the event header's timestamp field (should exist by now)
342 timestamp
= stream
.event_header
['timestamp']
344 if not self
._compare
_integers
(fields
['timestamp_begin'], timestamp
):
345 _perror('stream {}: packet context: "timestamp_begin": integer type different from event header\'s "timestamp" field'.format(sid
))
347 if not self
._compare
_integers
(fields
['timestamp_end'], timestamp
):
348 _perror('stream {}: packet context: "timestamp_end": integer type different from event header\'s "timestamp" field'.format(sid
))
350 # content_size must exist and be an unsigned integer
351 if 'content_size' not in fields
:
352 _perror('stream {}: packet context: missing "content_size" field'.format(sid
))
355 self
._validate
_integer
(fields
['content_size'], 32, 32, False)
358 self
._validate
_integer
(fields
['content_size'], 64, 64, False)
360 _perror('stream {}: packet context: "content_size": expecting unsigned 32-bit/64-bit integer'.format(sid
))
362 # packet_size must exist and be an unsigned integer
363 if 'packet_size' not in fields
:
364 _perror('stream {}: packet context: missing "packet_size" field'.format(sid
))
367 self
._validate
_integer
(fields
['packet_size'], 32, 32, False)
370 self
._validate
_integer
(fields
['packet_size'], 64, 64, False)
372 _perror('stream {}: packet context: "packet_size": expecting unsigned 32-bit/64-bit integer'.format(sid
))
374 # if cpu_id exists, must be an unsigned integer
375 if 'cpu_id' in fields
:
377 self
._validate
_integer
(fields
['cpu_id'], signed
=False)
378 except RuntimeError as e
:
379 _perror('stream {}: packet context: "cpu_id": {}'.format(sid
, e
))
381 # Validates an event header.
383 # stream: TSDL stream containing the event header to validate
384 def _validate_seh(self
, stream
):
385 event_header
= stream
.event_header
389 self
._validate
_struct
(event_header
, True)
390 except RuntimeError as e
:
391 _perror('stream {}: event header: {}'.format(sid
, e
))
393 fields
= event_header
.fields
395 # id must exist and be an unsigned integer
396 if 'id' not in fields
:
397 _perror('stream {}: event header: missing "id" field'.format(sid
))
400 self
._validate
_integer
(fields
['id'], signed
=False)
401 except RuntimeError as e
:
402 _perror('stream {}: "id": {}'.format(sid
, format(e
)))
404 # timestamp must exist, be an unsigned integer and be mapped to a valid clock
405 if 'timestamp' not in fields
:
406 _perror('stream {}: event header: missing "timestamp" field'.format(sid
))
409 self
._validate
_integer
(fields
['timestamp'], signed
=False)
410 except RuntimeError as e
:
411 _perror('stream {}: event header: "timestamp": {}'.format(sid
, format(e
)))
413 if fields
['timestamp'].map is None:
414 _perror('stream {}: event header: "timestamp" must be mapped to a valid clock'.format(sid
))
416 # id must be the first field, followed by timestamp
417 if list(fields
.keys())[0] != 'id':
418 _perror('stream {}: event header: "id" must be the first field'.format(sid
))
420 if list(fields
.keys())[1] != 'timestamp':
421 _perror('stream {}: event header: "timestamp" must be the second field'.format(sid
))
423 # only id and timestamp and allowed in event header
425 _perror('stream {}: event header: only "id" and "timestamp" fields are allowed'.format(sid
))
427 # Validates a strean event context.
429 # stream: TSDL stream containing the stream event context
430 def _validate_sec(self
, stream
):
431 stream_event_context
= stream
.event_context
434 if stream_event_context
is None:
438 self
._validate
_context
_fields
(stream_event_context
)
439 except RuntimeError as e
:
440 _perror('stream {}: event context: {}'.format(sid
, e
))
442 # Validates an event context.
444 # stream: TSDL stream containing the TSDL event
445 # event: TSDL event containing the context to validate
446 def _validate_ec(self
, stream
, event
):
447 event_context
= event
.context
451 if event_context
is None:
455 self
._validate
_context
_fields
(event_context
)
456 except RuntimeError as e
:
457 _perror('stream {}: event {}: context: {}'.format(sid
, eid
, e
))
459 # Validates an event fields.
461 # stream: TSDL stream containing the TSDL event
462 # event: TSDL event containing the fields to validate
463 def _validate_ef(self
, stream
, event
):
464 event_fields
= event
.fields
469 self
._validate
_context
_fields
(event_fields
)
470 except RuntimeError as e
:
471 _perror('stream {}: event {}: fields: {}'.format(sid
, eid
, e
))
473 # Validates a TSDL event.
475 # stream: TSDL stream containing the TSDL event
476 # event: TSDL event to validate
477 def _validate_event(self
, stream
, event
):
478 # name must be a compatible C identifier
479 if not re
.match(r
'^[a-zA-Z_][a-zA-Z0-9_]*$', event
.name
):
480 fmt
= 'stream {}: event {}: malformed event name: "{}"'
481 _perror(fmt
.format(stream
.id, event
.id, event
.name
))
483 self
._validate
_ec
(stream
, event
)
484 self
._validate
_ef
(stream
, event
)
486 # Validates a TSDL stream.
488 # stream: TSDL stream to validate
489 def _validate_stream(self
, stream
):
490 self
._validate
_seh
(stream
)
491 self
._validate
_spc
(stream
)
492 self
._validate
_sec
(stream
)
495 for event
in stream
.events
:
496 self
._validate
_event
(stream
, event
)
498 # Validates all TSDL scopes of the current TSDL document.
499 def _validate_all_scopes(self
):
501 self
._validate
_tph
(self
._doc
.trace
.packet_header
)
504 for stream
in self
._doc
.streams
.values():
505 self
._validate
_stream
(stream
)
507 # Validates the trace block.
508 def _validate_trace(self
):
509 # make sure a native byte order is specified
510 if self
._doc
.trace
.byte_order
is None:
511 _perror('native byte order (trace.byte_order) is not specified')
513 # Validates the current TSDL document.
514 def _validate_metadata(self
):
515 self
._validate
_trace
()
516 self
._validate
_all
_scopes
()
518 # Returns an aligned number.
528 # at: number to align
529 # align: alignment (power of two)
530 def _get_alignment(self
, at
, align
):
531 return (at
+ align
- 1) & -align
533 # Converts a tree of offset variables:
548 # field_other_struct_field -> 16
549 # field_other_struct_yeah -> 20
553 # offvars_tree: tree of offset variables
554 # prefix: offset variable name prefix
555 # offvars: flattened offset variables
556 def _flatten_offvars_tree(self
, offvars_tree
, prefix
=None,
559 offvars
= collections
.OrderedDict()
561 for name
, offset
in offvars_tree
.items():
562 if prefix
is not None:
563 varname
= '{}_{}'.format(prefix
, name
)
567 if isinstance(offset
, dict):
568 self
._flatten
_offvars
_tree
(offset
, varname
, offvars
)
570 offvars
[varname
] = offset
574 # Returns the size of a TSDL structure with _static size_ (must be
577 # struct: TSDL structure of which to get the size
578 # offvars_tree: optional offset variables tree (output)
579 # base_offset: base offsets for offset variables
580 def _get_struct_size(self
, struct
,
583 if offvars_tree
is None:
584 offvars_tree
= collections
.OrderedDict()
588 for fname
, ftype
in struct
.fields
.items():
589 field_alignment
= self
._get
_obj
_alignment
(ftype
)
590 offset
= self
._get
_alignment
(offset
, field_alignment
)
592 if type(ftype
) is pytsdl
.tsdl
.Struct
:
593 offvars_tree
[fname
] = collections
.OrderedDict()
594 sz
= self
._get
_struct
_size
(ftype
, offvars_tree
[fname
],
595 base_offset
+ offset
)
597 # only integers may act as sequence lengths
598 if type(ftype
) is pytsdl
.tsdl
.Integer
:
599 offvars_tree
[fname
] = base_offset
+ offset
601 sz
= self
._get
_obj
_size
(ftype
)
607 # Returns the size of a TSDL array.
609 # array: TSDL array of which to get the size
610 def _get_array_size(self
, array
):
611 element
= array
.element
613 # effective size of one element includes its alignment after its size
614 size
= self
._get
_obj
_size
(element
)
615 align
= self
._get
_obj
_alignment
(element
)
617 return self
._get
_alignment
(size
, align
) * array
.length
619 # Returns the size of a TSDL enumeration.
621 # enum: TSDL enumeration of which to get the size
622 def _get_enum_size(self
, enum
):
623 return self
._get
_obj
_size
(enum
.integer
)
625 # Returns the size of a TSDL floating point number.
627 # floating_point: TSDL floating point number of which to get the size
628 def _get_floating_point_size(self
, floating_point
):
629 return floating_point
.exp_dig
+ floating_point
.mant_dig
631 # Returns the size of a TSDL integer.
633 # integer: TSDL integer of which to get the size
634 def _get_integer_size(self
, integer
):
637 # Returns the size of a TSDL type.
639 # obj: TSDL type of which to get the size
640 def _get_obj_size(self
, obj
):
641 return self
._obj
_size
_cb
[type(obj
)](obj
)
643 # Returns the alignment of a TSDL structure.
645 # struct: TSDL structure of which to get the alignment
646 def _get_struct_alignment(self
, struct
):
647 if struct
.align
is not None:
652 for fname
, ftype
in struct
.fields
.items():
653 cur_align
= max(self
._get
_obj
_alignment
(ftype
), cur_align
)
657 # Returns the alignment of a TSDL integer.
659 # integer: TSDL integer of which to get the alignment
660 def _get_integer_alignment(self
, integer
):
663 # Returns the alignment of a TSDL floating point number.
665 # floating_point: TSDL floating point number of which to get the
667 def _get_floating_point_alignment(self
, floating_point
):
668 return floating_point
.align
670 # Returns the alignment of a TSDL enumeration.
672 # enum: TSDL enumeration of which to get the alignment
673 def _get_enum_alignment(self
, enum
):
674 return self
._get
_obj
_alignment
(enum
.integer
)
676 # Returns the alignment of a TSDL string.
678 # string: TSDL string of which to get the alignment
679 def _get_string_alignment(self
, string
):
682 # Returns the alignment of a TSDL array.
684 # array: TSDL array of which to get the alignment
685 def _get_array_alignment(self
, array
):
686 return self
._get
_obj
_alignment
(array
.element
)
688 # Returns the alignment of a TSDL sequence.
690 # sequence: TSDL sequence of which to get the alignment
691 def _get_sequence_alignment(self
, sequence
):
692 return self
._get
_obj
_alignment
(sequence
.element
)
694 # Returns the alignment of a TSDL type.
696 # obj: TSDL type of which to get the alignment
697 def _get_obj_alignment(self
, obj
):
698 return self
._obj
_alignment
_cb
[type(obj
)](obj
)
700 # Converts a field name to a C parameter name.
702 # You should not use this function directly, but rather use one
703 # of the _*_fname_to_pname() variants depending on your scope.
705 # prefix: parameter name prefix
707 def _fname_to_pname(self
, prefix
, fname
):
708 return 'param_{}_{}'.format(prefix
, fname
)
710 # Converts an event fields field name to a C parameter name.
713 def _ef_fname_to_pname(self
, fname
):
714 return self
._fname
_to
_pname
('ef', fname
)
716 # Converts an event context field name to a C parameter name.
719 def _ec_fname_to_pname(self
, fname
):
720 return self
._fname
_to
_pname
('ec', fname
)
722 # Converts a stream event context field name to a C parameter name.
725 def _sec_fname_to_pname(self
, fname
):
726 return self
._fname
_to
_pname
('sec', fname
)
728 # Converts an event header field name to a C parameter name.
731 def _eh_fname_to_pname(self
, fname
):
732 return self
._fname
_to
_pname
('eh', fname
)
734 # Converts a stream packet context field name to a C parameter name.
737 def _spc_fname_to_pname(self
, fname
):
738 return self
._fname
_to
_pname
('spc', fname
)
740 # Converts a trace packet header field name to a C parameter name.
743 def _tph_fname_to_pname(self
, fname
):
744 return self
._fname
_to
_pname
('tph', fname
)
746 # Returns the equivalent C type of a TSDL integer.
748 # integer: TSDL integer of which to get the equivalent C type
749 def _get_integer_param_ctype(self
, integer
):
750 signed
= 'u' if not integer
.signed
else ''
752 if integer
.size
== 8:
754 elif integer
.size
== 16:
756 elif integer
.size
== 32:
758 elif integer
.size
== 64:
761 # if the integer is signed and of uncommon size, the sign bit is
762 # at a custom position anyway so we use a 64-bit unsigned
768 if integer
.size
<= 8:
770 elif integer
.size
<= 16:
772 elif integer
.size
<= 32:
777 return '{}int{}_t'.format(signed
, sz
)
779 # Returns the equivalent C type of a TSDL enumeration.
781 # enum: TSDL enumeration of which to get the equivalent C type
782 def _get_enum_param_ctype(self
, enum
):
783 return self
._get
_obj
_param
_ctype
(enum
.integer
)
785 # Returns the equivalent C type of a TSDL floating point number.
787 # fp: TSDL floating point number of which to get the equivalent C type
788 def _get_floating_point_param_ctype(self
, fp
):
789 if fp
.exp_dig
== 8 and fp
.mant_dig
== 24 and fp
.align
== 32:
791 elif fp
.exp_dig
== 11 and fp
.mant_dig
== 53 and fp
.align
== 64:
796 # Returns the equivalent C type of a TSDL type.
798 # obj: TSDL type of which to get the equivalent C type
799 def _get_obj_param_ctype(self
, obj
):
800 return self
._obj
_param
_ctype
_cb
[type(obj
)](obj
)
802 # Returns the check offset overflow macro call string for a given size.
804 # size: size to check
805 def _get_chk_offset_v(self
, size
):
806 fmt
= '{}_CHK_OFFSET_V({}, {}, {});'
807 ret
= fmt
.format(self
._prefix
.upper(), self
._CTX
_AT
,
808 self
._CTX
_PACKET
_SIZE
, size
)
812 # Returns the check offset overflow macro call C line for a given size.
814 # size: size to check
815 def _get_chk_offset_v_cline(self
, size
):
816 return _CLine(self
._get
_chk
_offset
_v
(size
))
818 # Returns the offset alignment macro call string for a given alignment.
820 # size: new alignment
821 def _get_align_offset(self
, align
, at
=None):
825 fmt
= '{}_ALIGN_OFFSET({}, {});'
826 ret
= fmt
.format(self
._prefix
.upper(), at
, align
)
830 # Returns the offset alignment macro call C line for a given alignment.
832 # size: new alignment
833 def _get_align_offset_cline(self
, size
):
834 return _CLine(self
._get
_align
_offset
(size
))
836 # Converts a C source string with newlines to an array of C lines and
840 def _str_to_clines(self
, s
):
841 lines
= s
.split('\n')
843 return [_CLine(line
) for line
in lines
]
845 # Fills a given template with values and returns its C lines. The `prefix`
846 # and `ucprefix` template variable are automatically provided using the
847 # generator's context.
850 # kwargs: additional template variable values
851 def _template_to_clines(self
, tmpl
, **kwargs
):
852 s
= tmpl
.format(prefix
=self
._prefix
, ucprefix
=self
._prefix
.upper(),
855 return self
._str
_to
_clines
(s
)
857 # Returns the C lines for writing a TSDL structure field.
860 # src_name: C source pointer
861 # struct: TSDL structure
862 def _write_field_struct(self
, fname
, src_name
, struct
, scope_prefix
=None):
863 size
= self
._get
_struct
_size
(struct
)
864 size_bytes
= self
._get
_alignment
(size
, 8) // 8
865 dst
= self
._CTX
_BUF
_AT
_ADDR
868 # memcpy() is safe since barectf requires inner structures
870 self
._get
_chk
_offset
_v
_cline
(size
),
871 _CLine('memcpy({}, {}, {});'.format(dst
, src_name
, size_bytes
)),
872 _CLine('{} += {};'.format(self
._CTX
_AT
, size
)),
875 # Returns the C lines for writing a TSDL integer field.
878 # src_name: C source integer
879 # integer: TSDL integer
880 def _write_field_integer(self
, fname
, src_name
, integer
, scope_prefix
=None):
881 bo
= self
._BO
_SUFFIXES
_MAP
[integer
.byte_order
]
882 t
= self
._get
_obj
_param
_ctype
(integer
)
883 length
= self
._get
_obj
_size
(integer
)
885 return self
._template
_to
_clines
(barectf
.templates
.WRITE_INTEGER
,
886 sz
=length
, bo
=bo
, type=t
,
889 # Returns the C lines for writing a TSDL enumeration field.
892 # src_name: C source integer
893 # enum: TSDL enumeration
894 def _write_field_enum(self
, fname
, src_name
, enum
, scope_prefix
=None):
895 return self
._write
_field
_obj
(fname
, src_name
, enum
.integer
)
897 # Returns the C lines for writing a TSDL floating point number field.
900 # src_name: C source pointer
901 # floating_point: TSDL floating point number
902 def _write_field_floating_point(self
, fname
, src_name
, floating_point
,
904 bo
= self
._BO
_SUFFIXES
_MAP
[floating_point
.byte_order
]
905 t
= self
._get
_obj
_param
_ctype
(floating_point
)
906 length
= self
._get
_obj
_size
(floating_point
)
913 src_name_casted
= '({}) {}'.format(t
, src_name
)
915 return self
._template
_to
_clines
(barectf
.templates
.WRITE_INTEGER
,
916 sz
=length
, bo
=bo
, type=t
,
917 src_name
=src_name_casted
)
919 # Returns the C lines for writing either a TSDL array field or a
920 # TSDL sequence field.
923 # src_name: C source pointer
924 # arrayseq: TSDL array or sequence
925 # scope_prefix: preferred scope prefix
926 def _write_field_array_sequence(self
, fname
, src_name
, arrayseq
,
928 def length_index_varname(index
):
929 return 'lens_{}_{}'.format(fname
, index
)
931 # first pass: find all lengths to multiply
936 mulops
.append(arrayseq
.length
)
937 element
= arrayseq
.element
940 if tel
is pytsdl
.tsdl
.Array
or tel
is pytsdl
.tsdl
.Sequence
:
941 # another array/sequence; continue
948 # align the size of the repeating element (effective repeating size)
949 el_size
= self
._get
_obj
_size
(element
)
950 el_align
= self
._get
_obj
_alignment
(element
)
951 el_size
= self
._get
_alignment
(el_size
, el_align
)
953 # this effective size is part of the operands to multiply
954 mulops
.append(el_size
)
959 # fetch and save sequence lengths
962 for i
in range(len(mulops
)):
965 if type(mulop
) is list:
966 # offset variable to fetch
967 offvar
= self
._get
_seq
_length
_src
_name
(mulop
, scope_prefix
)
969 if type(offvar
) is int:
970 # environment constant
971 emulops
.append(str(offvar
))
974 # save buffer position
975 line
= 'ctx_at_bkup = {};'.format(self
._CTX
_AT
)
976 clines
.append(_CLine(line
))
978 # go back to field offset
979 line
= '{} = {};'.format(self
._CTX
_AT
, offvar
)
980 clines
.append(_CLine(line
))
982 # read value into specific variable
983 varname
= length_index_varname(i
)
984 emulops
.append(varname
)
985 varctype
= 'uint32_t'
986 fmt
= '{ctype} {cname} = *(({ctype}*) ({ctxbufataddr}));'
987 line
= fmt
.format(ctype
=varctype
, cname
=varname
,
988 ctxbufataddr
=self
._CTX
_BUF
_AT
_ADDR
)
989 clines
.append(_CLine(line
))
991 # restore buffer position
992 line
= '{} = ctx_at_bkup;'.format(self
._CTX
_AT
)
993 clines
.append(_CLine(line
))
995 emulops
.append(str(mulop
))
997 # write product of sizes in bits
998 mul
= ' * '.join(emulops
)
999 sz_bits_varname
= 'sz_bits_{}'.format(fname
)
1000 sz_bytes_varname
= 'sz_bytes_{}'.format(fname
)
1001 line
= 'uint32_t {} = {};'.format(sz_bits_varname
, mul
)
1002 clines
.append(_CLine(line
))
1005 clines
.append(self
._get
_chk
_offset
_v
_cline
(sz_bits_varname
))
1007 # write product of sizes in bytes
1008 line
= 'uint32_t {} = {};'.format(sz_bytes_varname
, sz_bits_varname
)
1009 clines
.append(_CLine(line
))
1010 line
= self
._get
_align
_offset
(8, at
=sz_bytes_varname
)
1011 clines
.append(_CLine(line
))
1012 line
= '{} >>= 3;'.format(sz_bytes_varname
)
1013 clines
.append(_CLine(line
))
1016 dst
= self
._CTX
_BUF
_AT
_ADDR
1017 line
= 'memcpy({}, {}, {});'.format(dst
, src_name
, sz_bytes_varname
)
1018 clines
.append(_CLine(line
))
1019 line
= '{} += {};'.format(self
._CTX
_AT
, sz_bits_varname
)
1020 clines
.append(_CLine(line
))
1024 # Returns the C lines for writing a TSDL array field.
1027 # src_name: C source pointer
1029 # scope_prefix: preferred scope prefix
1030 def _write_field_array(self
, fname
, src_name
, array
, scope_prefix
=None):
1031 return self
._write
_field
_array
_sequence
(fname
, src_name
, array
,
1034 # Returns the C lines for writing a TSDL sequence field.
1037 # src_name: C source pointer
1038 # sequence: TSDL sequence
1039 # scope_prefix: preferred scope prefix
1040 def _write_field_sequence(self
, fname
, src_name
, sequence
, scope_prefix
):
1041 return self
._write
_field
_array
_sequence
(fname
, src_name
, sequence
,
1044 # Returns a trace packet header C source name out of a sequence length
1047 # length: sequence length expression
1048 def _get_tph_src_name(self
, length
):
1049 offvar
= self
._get
_offvar
_name
_from
_expr
(length
[3:], 'tph')
1051 return 'ctx->{}'.format(offvar
)
1053 # Returns an environment C source name out of a sequence length
1056 # length: sequence length expression
1057 def _get_env_src_name(self
, length
):
1058 if len(length
) != 2:
1059 _perror('invalid sequence length: "{}"'.format(self
._dot
_name
_to
_str
(length
)))
1063 if fname
not in self
._doc
.env
:
1064 _perror('cannot find field env.{}'.format(fname
))
1066 env_length
= self
._doc
.env
[fname
]
1068 if type(env_length
) is not int:
1069 _perror('env.{} is not a constant integer'.format(fname
))
1071 return self
._doc
.env
[fname
]
1073 # Returns a stream packet context C source name out of a sequence length
1076 # length: sequence length expression
1077 def _get_spc_src_name(self
, length
):
1078 offvar
= self
._get
_offvar
_name
_from
_expr
(length
[3:], 'spc')
1080 return 'ctx->{}'.format(offvar
)
1082 # Returns a stream event header C source name out of a sequence length
1085 # length: sequence length expression
1086 def _get_seh_src_name(self
, length
):
1087 return self
._get
_offvar
_name
_from
_expr
(length
[3:], 'seh')
1089 # Returns a stream event context C source name out of a sequence length
1092 # length: sequence length expression
1093 def _get_sec_src_name(self
, length
):
1094 return self
._get
_offvar
_name
_from
_expr
(length
[3:], 'sec')
1096 # Returns an event context C source name out of a sequence length
1099 # length: sequence length expression
1100 def _get_ec_src_name(self
, length
):
1101 return self
._get
_offvar
_name
_from
_expr
(length
[2:], 'ec')
1103 # Returns an event fields C source name out of a sequence length
1106 # length: sequence length expression
1107 def _get_ef_src_name(self
, length
):
1108 return self
._get
_offvar
_name
_from
_expr
(length
[2:], 'ef')
1110 # Returns a C source name out of a sequence length expression.
1112 # length: sequence length expression
1113 # scope_prefix: preferred scope prefix
1114 def _get_seq_length_src_name(self
, length
, scope_prefix
=None):
1115 length_dot
= self
._dot
_name
_to
_str
(length
)
1117 for prefix
, get_src_name
in self
._get
_src
_name
_funcs
.items():
1118 if length_dot
.startswith(prefix
):
1119 return get_src_name(length
)
1121 return self
._get
_offvar
_name
_from
_expr
(length
, scope_prefix
)
1123 # Returns the C lines for writing a TSDL string field.
1126 # src_name: C source pointer
1127 # string: TSDL string
1128 def _write_field_string(self
, fname
, src_name
, string
, scope_prefix
=None):
1132 sz_bytes_varname
= 'slen_bytes_{}'.format(fname
)
1133 line
= 'size_t {} = strlen({}) + 1;'.format(sz_bytes_varname
, src_name
)
1134 clines
.append(_CLine(line
))
1136 # check offset overflow
1137 sz_bits_varname
= 'slen_bits_{}'.format(fname
)
1138 line
= 'size_t {} = ({} << 3);'.format(sz_bits_varname
,
1140 clines
.append(_CLine(line
))
1141 cline
= self
._get
_chk
_offset
_v
_cline
(sz_bits_varname
)
1142 clines
.append(cline
)
1145 dst
= self
._CTX
_BUF
_AT
_ADDR
1146 line
= 'memcpy({}, {}, {});'.format(dst
, src_name
, sz_bytes_varname
)
1147 clines
.append(_CLine(line
))
1149 # update bit position
1150 line
= '{} += {};'.format(self
._CTX
_AT
, sz_bits_varname
)
1151 clines
.append(_CLine(line
))
1155 # Returns the C lines for writing a TSDL type field.
1158 # src_name: C source pointer
1160 # scope_prefix: preferred scope prefix
1161 def _write_field_obj(self
, fname
, src_name
, ftype
, scope_prefix
):
1162 return self
._write
_field
_obj
_cb
[type(ftype
)](fname
, src_name
, ftype
,
1165 # Returns an offset variable name out of an offset name.
1168 # prefix: offset variable name prefix
1169 def _get_offvar_name(self
, name
, prefix
=None):
1172 if prefix
is not None:
1173 parts
.append(prefix
)
1177 return '_'.join(parts
)
1179 # Returns an offset variable name out of an expression (array of
1182 # expr: array of strings
1183 # prefix: offset variable name prefix
1184 def _get_offvar_name_from_expr(self
, expr
, prefix
=None):
1185 return self
._get
_offvar
_name
('_'.join(expr
), prefix
)
1187 # Returns the C lines for writing a TSDL field.
1190 # ftype: TSDL field type
1191 # scope_name: scope name
1192 # scope_prefix: preferred scope prefix
1193 # param_name_cb: callback to get the C parameter name out of the
1195 def _field_to_clines(self
, fname
, ftype
, scope_name
, scope_prefix
,
1198 pname
= param_name_cb(fname
)
1199 align
= self
._get
_obj
_alignment
(ftype
)
1202 fmt
= '/* write {}.{} ({}) */'
1203 line
= fmt
.format(scope_name
, fname
,
1204 self
._TSDL
_TYPE
_NAMES
_MAP
[type(ftype
)])
1205 clines
.append(_CLine(line
))
1207 # align bit index before writing to the buffer
1208 cline
= self
._get
_align
_offset
_cline
(align
)
1209 clines
.append(cline
)
1211 # write offset variables
1212 if type(ftype
) is pytsdl
.tsdl
.Struct
:
1213 offvars_tree
= collections
.OrderedDict()
1214 self
._get
_struct
_size
(ftype
, offvars_tree
)
1215 offvars
= self
._flatten
_offvars
_tree
(offvars_tree
)
1217 # as many offset as there are child fields because a future
1218 # sequence could refer to any of those fields
1219 for lname
, offset
in offvars
.items():
1220 offvar
= self
._get
_offvar
_name
('_'.join([fname
, lname
]),
1222 fmt
= 'uint32_t {} = (uint32_t) {} + {};'
1223 line
= fmt
.format(offvar
, self
._CTX
_AT
, offset
);
1224 clines
.append(_CLine(line
))
1225 elif type(ftype
) is pytsdl
.tsdl
.Integer
:
1226 # offset of this simple field is the current bit index
1227 offvar
= self
._get
_offvar
_name
(fname
, scope_prefix
)
1228 line
= 'uint32_t {} = (uint32_t) {};'.format(offvar
, self
._CTX
_AT
)
1229 clines
.append(_CLine(line
))
1231 clines
+= self
._write
_field
_obj
(fname
, pname
, ftype
, scope_prefix
)
1235 # Joins C line groups and returns C lines.
1237 # cline_groups: C line groups to join
1238 def _join_cline_groups(self
, cline_groups
):
1239 if not cline_groups
:
1242 output_clines
= cline_groups
[0]
1244 for clines
in cline_groups
[1:]:
1245 output_clines
.append('')
1246 output_clines
+= clines
1248 return output_clines
1250 # Returns the C lines for writing a complete TSDL structure (top level
1253 # struct: TSDL structure
1254 # scope_name: scope name
1255 # scope_prefix: preferred scope prefix
1256 # param_name_cb: callback to get the C parameter name out of the
1258 def _struct_to_clines(self
, struct
, scope_name
, scope_prefix
,
1262 for fname
, ftype
in struct
.fields
.items():
1263 clines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1264 scope_prefix
, param_name_cb
)
1265 cline_groups
.append(clines
)
1267 return self
._join
_cline
_groups
(cline_groups
)
1269 # Returns the offset variables of a TSDL structure.
1271 # struct: TSDL structure
1272 def _get_struct_size_offvars(self
, struct
):
1273 offvars_tree
= collections
.OrderedDict()
1274 size
= self
._get
_struct
_size
(struct
, offvars_tree
)
1275 offvars
= self
._flatten
_offvars
_tree
(offvars_tree
)
1277 return size
, offvars
1279 # Returns the size and offset variables of the current trace packet header.
1280 def _get_tph_size_offvars(self
):
1281 return self
._get
_struct
_size
_offvars
(self
._doc
.trace
.packet_header
)
1283 # Returns the size and offset variables of the a stream packet context.
1285 # stream: TSDL stream
1286 def _get_spc_size_offvars(self
, stream
):
1287 return self
._get
_struct
_size
_offvars
(stream
.packet_context
)
1289 # Returns the C lines for the barectf context C structure entries for
1292 # prefix: offset variable names prefix
1293 # offvars: offset variables
1294 def _offvars_to_ctx_clines(self
, prefix
, offvars
):
1297 for name
in offvars
.keys():
1298 offvar
= self
._get
_offvar
_name
(name
, prefix
)
1299 clines
.append(_CLine('uint32_t {};'.format(offvar
)))
1303 # Generates a barectf context C structure.
1305 # stream: TSDL stream
1306 # hide_sid: True to hide the stream ID
1307 def _gen_barectf_ctx_struct(self
, stream
, hide_sid
=False):
1308 # get offset variables for both the packet header and packet context
1309 tph_size
, tph_offvars
= self
._get
_tph
_size
_offvars
()
1310 spc_size
, spc_offvars
= self
._get
_spc
_size
_offvars
(stream
)
1311 clines
= self
._offvars
_to
_ctx
_clines
('tph', tph_offvars
)
1312 clines
+= self
._offvars
_to
_ctx
_clines
('spc', spc_offvars
)
1315 clines_indented
= []
1316 for cline
in clines
:
1317 clines_indented
.append(_CLine('\t' + cline
))
1320 clock_cb
= '\t/* (no clock callback) */'
1322 if not self
._manual
_clock
:
1323 ctype
= self
._get
_clock
_ctype
(stream
)
1324 fmt
= '\t{} (*clock_cb)(void*);\n\tvoid* clock_cb_data;'
1325 clock_cb
= fmt
.format(ctype
)
1333 t
= barectf
.templates
.BARECTF_CTX
1334 struct
= t
.format(prefix
=self
._prefix
, sid
=sid
,
1335 ctx_fields
='\n'.join(clines_indented
),
1340 # Generates all barectf context C structures.
1341 def _gen_barectf_contexts_struct(self
):
1344 if len(self
._doc
.streams
) == 1:
1349 for stream
in self
._doc
.streams
.values():
1350 struct
= self
._gen
_barectf
_ctx
_struct
(stream
, hide_sid
)
1351 structs
.append(struct
)
1353 return '\n\n'.join(structs
)
1355 # Returns the C type of the clock used by the event header of a
1358 # stream: TSDL stream containing the event header to inspect
1359 def _get_clock_ctype(self
, stream
):
1360 return self
._get
_obj
_param
_ctype
(stream
.event_header
['timestamp'])
1362 # Generates the manual clock value C parameter for a given stream.
1364 # stream: TSDL stream
1365 def _gen_manual_clock_param(self
, stream
):
1366 return '{} param_clock'.format(self
._get
_clock
_ctype
(stream
))
1368 # Generates the body of a barectf_open() function.
1370 # stream: TSDL stream
1371 def _gen_barectf_func_open_body(self
, stream
):
1374 # keep clock value (for timestamp_begin)
1375 if self
._stream
_has
_timestamp
_begin
_end
(stream
):
1376 # get clock value ASAP
1377 clk_type
= self
._get
_clock
_ctype
(stream
)
1378 clk
= self
._gen
_get
_clock
_value
()
1379 line
= '{} clk_value = {};'.format(clk_type
, clk
)
1380 clines
.append(_CLine(line
))
1381 clines
.append(_CLine(''))
1383 # reset bit position to write the packet context (after packet header)
1384 spc_offset
= self
._get
_stream
_packet
_context
_offset
(stream
)
1386 line
= fmt
.format(self
._CTX
_AT
, spc_offset
)
1387 clines
.append(_CLine(line
))
1389 # bit position at beginning of event (to reset in case we run
1391 line
= 'uint32_t ctx_at_begin = {};'.format(self
._CTX
_AT
)
1392 clines
.append(_CLine(line
))
1394 # packet context fields
1396 scope_name
= 'stream.packet.context'
1397 scope_prefix
= 'spc'
1399 for fname
, ftype
in stream
.packet_context
.fields
.items():
1401 if fname
== 'packet_size':
1402 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1404 lambda x
: 'ctx->packet_size')
1405 fcline_groups
.append(fclines
)
1407 # content size (skip)
1408 elif fname
== 'content_size':
1409 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1410 scope_prefix
, lambda x
: '0')
1411 fcline_groups
.append(fclines
)
1414 elif fname
== 'timestamp_begin':
1415 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1417 lambda x
: 'clk_value')
1418 fcline_groups
.append(fclines
)
1420 # timestamp_end (skip)
1421 elif fname
== 'timestamp_end':
1422 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1423 scope_prefix
, lambda x
: '0')
1424 fcline_groups
.append(fclines
)
1428 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1430 self
._spc
_fname
_to
_pname
)
1431 fcline_groups
.append(fclines
)
1434 fcline_groups
.append([_CLine('return 0;')])
1436 clines
+= self
._join
_cline
_groups
(fcline_groups
)
1439 cblock
= _CBlock(clines
)
1440 src
= self
._cblock
_to
_source
(cblock
)
1444 _SPC_KNOWN_FIELDS
= [
1451 # Generates a barectf_open() function.
1453 # stream: TSDL stream
1454 # gen_body: also generate function body
1455 # hide_sid: True to hide the stream ID
1456 def _gen_barectf_func_open(self
, stream
, gen_body
, hide_sid
=False):
1460 if self
._manual
_clock
:
1461 clock_param
= self
._gen
_manual
_clock
_param
(stream
)
1462 params
.append(clock_param
)
1465 for fname
, ftype
in stream
.packet_context
.fields
.items():
1466 if fname
in self
._SPC
_KNOWN
_FIELDS
:
1469 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1470 pname
= self
._spc
_fname
_to
_pname
(fname
)
1471 param
= '{} {}'.format(ptype
, pname
)
1472 params
.append(param
)
1477 params_str
= ',\n\t'.join([''] + params
)
1485 t
= barectf
.templates
.FUNC_OPEN
1486 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1491 func
+= self
._gen
_barectf
_func
_open
_body
(stream
)
1498 # Generates the body of a barectf_init() function.
1500 # stream: TSDL stream
1501 def _gen_barectf_func_init_body(self
, stream
):
1504 line
= 'uint32_t ctx_at_bkup;'
1505 clines
.append(_CLine(line
))
1507 # bit position at beginning of event (to reset in case we run
1509 line
= 'uint32_t ctx_at_begin = {};'.format(self
._CTX
_AT
)
1510 clines
.append(_CLine(line
))
1512 # set context parameters
1513 clines
.append(_CLine(''))
1514 clines
.append(_CLine("/* barectf context parameters */"))
1515 clines
.append(_CLine('ctx->buf = buf;'))
1516 clines
.append(_CLine('ctx->packet_size = buf_size * 8;'))
1517 clines
.append(_CLine('{} = 0;'.format(self
._CTX
_AT
)))
1519 if not self
._manual
_clock
:
1520 clines
.append(_CLine('ctx->clock_cb = clock_cb;'))
1521 clines
.append(_CLine('ctx->clock_cb_data = clock_cb_data;'))
1523 # set context offsets
1524 clines
.append(_CLine(''))
1525 clines
.append(_CLine("/* barectf context offsets */"))
1526 ph_size
, ph_offvars
= self
._get
_tph
_size
_offvars
()
1527 pc_size
, pc_offvars
= self
._get
_spc
_size
_offvars
(stream
)
1528 pc_alignment
= self
._get
_obj
_alignment
(stream
.packet_context
)
1529 pc_offset
= self
._get
_alignment
(ph_size
, pc_alignment
)
1531 for offvar
, offset
in ph_offvars
.items():
1532 offvar_field
= self
._get
_offvar
_name
(offvar
, 'tph')
1533 line
= 'ctx->{} = {};'.format(offvar_field
, offset
)
1534 clines
.append(_CLine(line
))
1536 for offvar
, offset
in pc_offvars
.items():
1537 offvar_field
= self
._get
_offvar
_name
(offvar
, 'spc')
1538 line
= 'ctx->{} = {};'.format(offvar_field
, pc_offset
+ offset
)
1539 clines
.append(_CLine(line
))
1541 clines
.append(_CLine(''))
1543 # packet header fields
1545 scope_name
= 'trace.packet.header'
1546 scope_prefix
= 'tph'
1548 for fname
, ftype
in self
._doc
.trace
.packet_header
.fields
.items():
1550 if fname
== 'magic':
1551 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1553 lambda x
: '0xc1fc1fc1UL')
1554 fcline_groups
.append(fclines
)
1557 elif fname
== 'stream_id':
1558 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1560 lambda x
: str(stream
.id))
1561 fcline_groups
.append(fclines
)
1564 fcline_groups
.append([_CLine('return 0;')])
1566 clines
+= self
._join
_cline
_groups
(fcline_groups
)
1569 cblock
= _CBlock(clines
)
1570 src
= self
._cblock
_to
_source
(cblock
)
1574 # Generates a barectf_init() function.
1576 # stream: TSDL stream
1577 # gen_body: also generate function body
1578 # hide_sid: True to hide the stream ID
1579 def _gen_barectf_func_init(self
, stream
, gen_body
, hide_sid
=False):
1588 if not self
._manual
_clock
:
1589 ts_ftype
= stream
.event_header
['timestamp']
1590 ts_ptype
= self
._get
_obj
_param
_ctype
(ts_ftype
)
1591 fmt
= ',\n\t{} (*clock_cb)(void*),\n\tvoid* clock_cb_data'
1592 params
= fmt
.format(ts_ptype
)
1594 t
= barectf
.templates
.FUNC_INIT
1595 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1600 func
+= self
._gen
_barectf
_func
_init
_body
(stream
)
1607 # Generates the C expression to get the clock value depending on
1608 # whether we're in manual clock mode or not.
1609 def _gen_get_clock_value(self
):
1610 if self
._manual
_clock
:
1611 return 'param_clock'
1613 return self
._CTX
_CALL
_CLOCK
_CB
1615 # Returns True if the given TSDL stream has timestamp_begin and
1616 # timestamp_end fields.
1618 # stream: TSDL stream to check
1619 def _stream_has_timestamp_begin_end(self
, stream
):
1620 return self
._has
_timestamp
_begin
_end
[stream
.id]
1622 # Returns the packet context offset (from the beginning of the
1623 # packet) of a given TSDL stream
1625 # stream: TSDL stream
1626 def _get_stream_packet_context_offset(self
, stream
):
1627 return self
._packet
_context
_offsets
[stream
.id]
1629 # Generates the C lines to write a barectf context field, saving
1630 # and restoring the current bit position accordingly.
1632 # src_name: C source name
1633 # prefix: offset variable prefix
1634 # name: offset variable name
1635 # integer: TSDL integer to write
1636 def _gen_write_ctx_field_integer(self
, src_name
, prefix
, name
, integer
):
1639 # save buffer position
1640 line
= 'ctx_at_bkup = {};'.format(self
._CTX
_AT
)
1641 clines
.append(_CLine(line
))
1643 # go back to field offset
1644 offvar
= self
._get
_offvar
_name
(name
, prefix
)
1645 line
= '{} = ctx->{};'.format(self
._CTX
_AT
, offvar
)
1646 clines
.append(_CLine(line
))
1649 clines
+= self
._write
_field
_integer
(None, src_name
, integer
)
1651 # restore buffer position
1652 line
= '{} = ctx_at_bkup;'.format(self
._CTX
_AT
)
1653 clines
.append(_CLine(line
))
1657 # Generates the body of a barectf_close() function.
1659 # stream: TSDL stream
1660 def _gen_barectf_func_close_body(self
, stream
):
1663 line
= 'uint32_t ctx_at_bkup;'
1664 clines
.append(_CLine(line
))
1666 # bit position at beginning of event (to reset in case we run
1668 line
= 'uint32_t ctx_at_begin = {};'.format(self
._CTX
_AT
)
1669 clines
.append(_CLine(line
))
1671 # update timestamp end if present
1672 if self
._stream
_has
_timestamp
_begin
_end
(stream
):
1673 clines
.append(_CLine(''))
1674 clines
.append(_CLine("/* update packet context's timestamp_end */"))
1676 # get clock value ASAP
1677 clk_type
= self
._get
_clock
_ctype
(stream
)
1678 clk
= self
._gen
_get
_clock
_value
()
1679 line
= '{} clk_value = {};'.format(clk_type
, clk
)
1680 clines
.append(_CLine(line
))
1682 # write timestamp_end
1683 timestamp_end_integer
= stream
.packet_context
['timestamp_end']
1684 clines
+= self
._gen
_write
_ctx
_field
_integer
('clk_value', 'spc',
1686 timestamp_end_integer
)
1688 # update content_size
1689 clines
.append(_CLine(''))
1690 clines
.append(_CLine("/* update packet context's content_size */"))
1691 content_size_integer
= stream
.packet_context
['content_size']
1692 clines
+= self
._gen
_write
_ctx
_field
_integer
('ctx_at_bkup', 'spc',
1694 content_size_integer
)
1697 clines
.append(_CLine('\n'))
1698 clines
.append(_CLine('return 0;'))
1701 cblock
= _CBlock(clines
)
1702 src
= self
._cblock
_to
_source
(cblock
)
1706 # Generates a barectf_close() function.
1708 # stream: TSDL stream
1709 # gen_body: also generate function body
1710 # hide_sid: True to hide the stream ID
1711 def _gen_barectf_func_close(self
, stream
, gen_body
, hide_sid
=False):
1720 if self
._manual
_clock
:
1721 clock_param
= self
._gen
_manual
_clock
_param
(stream
)
1722 params
= ',\n\t{}'.format(clock_param
)
1724 t
= barectf
.templates
.FUNC_CLOSE
1725 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1730 func
+= self
._gen
_barectf
_func
_close
_body
(stream
)
1737 # Generates all barectf_init() function.
1739 # gen_body: also generate function bodies
1740 def _gen_barectf_funcs_init(self
, gen_body
):
1743 if len(self
._doc
.streams
) == 1:
1748 for stream
in self
._doc
.streams
.values():
1749 funcs
.append(self
._gen
_barectf
_func
_init
(stream
, gen_body
,
1754 # Generates all barectf_open() function.
1756 # gen_body: also generate function bodies
1757 def _gen_barectf_funcs_open(self
, gen_body
):
1760 if len(self
._doc
.streams
) == 1:
1765 for stream
in self
._doc
.streams
.values():
1766 funcs
.append(self
._gen
_barectf
_func
_open
(stream
, gen_body
,
1771 # Generates the body of a barectf_trace() function.
1773 # stream: TSDL stream of TSDL event to trace
1774 # event: TSDL event to trace
1775 def _gen_barectf_func_trace_event_body(self
, stream
, event
):
1778 # get clock value ASAP
1779 clk_type
= self
._get
_clock
_ctype
(stream
)
1780 clk
= self
._gen
_get
_clock
_value
()
1781 line
= '{} clk_value = {};'.format(clk_type
, clk
)
1782 clines
.append(_CLine(line
))
1783 clines
.append(_CLine(''))
1785 # bit position backup (could be used)
1786 clines
.append(_CLine('uint32_t ctx_at_bkup;'))
1788 # bit position at beginning of event (to reset in case we run
1790 line
= 'uint32_t ctx_at_begin = {};'.format(self
._CTX
_AT
)
1791 clines
.append(_CLine(line
))
1795 scope_name
= 'event.header'
1798 for fname
, ftype
in stream
.event_header
.fields
.items():
1801 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1803 lambda x
: str(event
.id))
1804 fcline_groups
.append(fclines
)
1807 elif fname
== 'timestamp':
1808 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1810 lambda x
: 'clk_value')
1811 fcline_groups
.append(fclines
)
1813 # stream event context
1814 if stream
.event_context
is not None:
1815 fclines
= self
._struct
_to
_clines
(stream
.event_context
,
1816 'stream.event.context', 'sec',
1817 self
._sec
_fname
_to
_pname
)
1818 fcline_groups
.append(fclines
)
1821 if event
.context
is not None:
1822 fclines
= self
._struct
_to
_clines
(event
.context
,
1823 'event.context', 'ec',
1824 self
._ec
_fname
_to
_pname
)
1825 fcline_groups
.append(fclines
)
1828 if event
.fields
is not None:
1829 fclines
= self
._struct
_to
_clines
(event
.fields
,
1830 'event.fields', 'ef',
1831 self
._ef
_fname
_to
_pname
)
1832 fcline_groups
.append(fclines
)
1835 fcline_groups
.append([_CLine('return 0;')])
1837 clines
+= self
._join
_cline
_groups
(fcline_groups
)
1840 cblock
= _CBlock(clines
)
1841 src
= self
._cblock
_to
_source
(cblock
)
1845 # Generates a barectf_trace() function.
1847 # stream: TSDL stream containing the TSDL event to trace
1848 # event: TSDL event to trace
1849 # gen_body: also generate function body
1850 # hide_sid: True to hide the stream ID
1851 def _gen_barectf_func_trace_event(self
, stream
, event
, gen_body
, hide_sid
):
1855 if self
._manual
_clock
:
1856 clock_param
= self
._gen
_manual
_clock
_param
(stream
)
1857 params
.append(clock_param
)
1859 # stream event context params
1860 if stream
.event_context
is not None:
1861 for fname
, ftype
in stream
.event_context
.fields
.items():
1862 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1863 pname
= self
._sec
_fname
_to
_pname
(fname
)
1864 param
= '{} {}'.format(ptype
, pname
)
1865 params
.append(param
)
1867 # event context params
1868 if event
.context
is not None:
1869 for fname
, ftype
in event
.context
.fields
.items():
1870 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1871 pname
= self
._ec
_fname
_to
_pname
(fname
)
1872 param
= '{} {}'.format(ptype
, pname
)
1873 params
.append(param
)
1875 # event fields params
1876 if event
.fields
is not None:
1877 for fname
, ftype
in event
.fields
.fields
.items():
1878 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1879 pname
= self
._ef
_fname
_to
_pname
(fname
)
1880 param
= '{} {}'.format(ptype
, pname
)
1881 params
.append(param
)
1886 params_str
= ',\n\t'.join([''] + params
)
1894 t
= barectf
.templates
.FUNC_TRACE
1895 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1896 evname
=event
.name
, params
=params_str
)
1900 func
+= self
._gen
_barectf
_func
_trace
_event
_body
(stream
, event
)
1907 # Generates all barectf_trace() functions of a given TSDL stream.
1909 # stream: TSDL stream containing the TSDL events to trace
1910 # gen_body: also generate function body
1911 # hide_sid: True to hide the stream ID
1912 def _gen_barectf_funcs_trace_stream(self
, stream
, gen_body
, hide_sid
):
1915 for event
in stream
.events
:
1916 funcs
.append(self
._gen
_barectf
_func
_trace
_event
(stream
, event
,
1917 gen_body
, hide_sid
))
1921 # Generates all barectf_trace() function.
1923 # gen_body: also generate function bodies
1924 def _gen_barectf_funcs_trace(self
, gen_body
):
1927 if len(self
._doc
.streams
) == 1:
1932 for stream
in self
._doc
.streams
.values():
1933 funcs
+= self
._gen
_barectf
_funcs
_trace
_stream
(stream
, gen_body
,
1938 # Generates all barectf_close() function.
1940 # gen_body: also generate function bodies
1941 def _gen_barectf_funcs_close(self
, gen_body
):
1944 if len(self
._doc
.streams
) == 1:
1949 for stream
in self
._doc
.streams
.values():
1950 funcs
.append(self
._gen
_barectf
_func
_close
(stream
, gen_body
,
1955 # Generate all barectf functions
1957 # gen_body: also generate function bodies
1958 def _gen_barectf_functions(self
, gen_body
):
1959 init_funcs
= self
._gen
_barectf
_funcs
_init
(gen_body
)
1960 open_funcs
= self
._gen
_barectf
_funcs
_open
(gen_body
)
1961 close_funcs
= self
._gen
_barectf
_funcs
_close
(gen_body
)
1962 trace_funcs
= self
._gen
_barectf
_funcs
_trace
(gen_body
)
1964 return init_funcs
+ open_funcs
+ close_funcs
+ trace_funcs
1966 # Generates the barectf header C source
1967 def _gen_barectf_header(self
):
1968 ctx_structs
= self
._gen
_barectf
_contexts
_struct
()
1969 functions
= self
._gen
_barectf
_functions
(self
._static
_inline
)
1970 functions_str
= '\n\n'.join(functions
)
1971 t
= barectf
.templates
.HEADER
1972 header
= t
.format(prefix
=self
._prefix
, ucprefix
=self
._prefix
.upper(),
1973 barectf_ctx
=ctx_structs
, functions
=functions_str
)
1978 pytsdl
.tsdl
.ByteOrder
.BE
: 'BIG_ENDIAN',
1979 pytsdl
.tsdl
.ByteOrder
.LE
: 'LITTLE_ENDIAN',
1982 # Generates the barectf bitfield.h header.
1983 def _gen_barectf_bitfield_header(self
):
1984 header
= barectf
.templates
.BITFIELD
1985 header
= header
.replace('$prefix$', self
._prefix
)
1986 header
= header
.replace('$PREFIX$', self
._prefix
.upper())
1987 endian_def
= self
._BO
_DEF
_MAP
[self
._doc
.trace
.byte_order
]
1988 header
= header
.replace('$ENDIAN_DEF$', endian_def
)
1992 # Generates the main barectf C source file.
1993 def _gen_barectf_csrc(self
):
1994 functions
= self
._gen
_barectf
_functions
(True)
1995 functions_str
= '\n\n'.join(functions
)
1996 t
= barectf
.templates
.CSRC
1997 csrc
= t
.format(prefix
=self
._prefix
, ucprefix
=self
._prefix
.upper(),
1998 functions
=functions_str
)
2002 # Writes a file to the generator's output.
2005 # contents: file contents
2006 def _write_file(self
, name
, contents
):
2007 path
= os
.path
.join(self
._output
, name
)
2009 with
open(path
, 'w') as f
:
2011 except Exception as e
:
2012 _perror('cannot write "{}": {}'.format(path
, e
))
2014 # Converts a C block to actual C source lines.
2017 # indent: initial indentation
2018 def _cblock_to_source_lines(self
, cblock
, indent
=1):
2020 indentstr
= '\t' * indent
2023 if type(line
) is _CBlock
:
2024 src
+= self
._cblock
_to
_source
_lines
(line
, indent
+ 1)
2026 src
.append(indentstr
+ line
)
2030 # Converts a C block to an actual C source string.
2033 # indent: initial indentation
2034 def _cblock_to_source(self
, cblock
, indent
=1):
2035 lines
= self
._cblock
_to
_source
_lines
(cblock
, indent
)
2037 return '\n'.join(lines
)
2039 # Sets the generator parameters.
2040 def _set_params(self
):
2041 # streams have timestamp_begin/timestamp_end fields
2042 self
._has
_timestamp
_begin
_end
= {}
2044 for stream
in self
._doc
.streams
.values():
2045 has
= 'timestamp_begin' in stream
.packet_context
.fields
2046 self
._has
_timestamp
_begin
_end
[stream
.id] = has
2048 # packet header size with alignment
2049 self
._packet
_context
_offsets
= {}
2051 tph_size
= self
._get
_struct
_size
(self
._doc
.trace
.packet_header
)
2053 for stream
in self
._doc
.streams
.values():
2054 spc_alignment
= self
._get
_obj
_alignment
(stream
.packet_context
)
2055 spc_offset
= self
._get
_alignment
(tph_size
, spc_alignment
)
2056 self
._packet
_context
_offsets
[stream
.id] = spc_offset
2058 # Generates barectf C files.
2060 # metadata: metadata path
2061 # output: output directory
2063 # static_inline: generate static inline functions
2064 # manual_clock: do not use a clock callback: pass clock value to
2066 def gen_barectf(self
, metadata
, output
, prefix
, static_inline
,
2068 self
._metadata
= metadata
2069 self
._output
= output
2070 self
._prefix
= prefix
2071 self
._static
_inline
= static_inline
2072 self
._manual
_clock
= manual_clock
2076 self
._si
_str
= 'static inline '
2078 # open CTF metadata file
2079 _pinfo('opening CTF metadata file "{}"'.format(self
._metadata
))
2082 with
open(metadata
) as f
:
2083 self
._tsdl
= f
.read()
2085 _perror('cannot open/read CTF metadata file "{}"'.format(metadata
))
2087 # parse CTF metadata
2088 _pinfo('parsing CTF metadata file')
2091 self
._doc
= self
._parser
.parse(self
._tsdl
)
2092 except pytsdl
.parser
.ParseError
as e
:
2093 _perror('parse error: {}'.format(e
))
2095 # validate CTF metadata against barectf constraints
2096 _pinfo('validating CTF metadata file')
2097 self
._validate
_metadata
()
2098 _psuccess('CTF metadata file is valid')
2100 # set parameters for this generation
2104 _pinfo('generating barectf header files')
2105 header
= self
._gen
_barectf
_header
()
2106 self
._write
_file
('{}.h'.format(self
._prefix
), header
)
2107 header
= self
._gen
_barectf
_bitfield
_header
()
2108 self
._write
_file
('{}_bitfield.h'.format(self
._prefix
), header
)
2110 # generate C source file
2111 if not self
._static
_inline
:
2112 _pinfo('generating barectf C source file')
2113 csrc
= self
._gen
_barectf
_csrc
()
2114 self
._write
_file
('{}.c'.format(self
._prefix
), csrc
)
2120 args
= _parse_args()
2121 generator
= BarectfCodeGenerator()
2122 generator
.gen_barectf(args
.metadata
, args
.output
, args
.prefix
,
2123 args
.static_inline
, args
.manual_clock
)
This page took 0.07458 seconds and 3 git commands to generate.