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 return '{}int{}_t'.format(signed
, sz
)
763 # Returns the equivalent C type of a TSDL enumeration.
765 # enum: TSDL enumeration of which to get the equivalent C type
766 def _get_enum_param_ctype(self
, enum
):
767 return self
._get
_obj
_param
_ctype
(enum
.integer
)
769 # Returns the equivalent C type of a TSDL floating point number.
771 # fp: TSDL floating point number of which to get the equivalent C type
772 def _get_floating_point_param_ctype(self
, fp
):
773 if fp
.exp_dig
== 8 and fp
.mant_dig
== 24 and fp
.align
== 32:
775 elif fp
.exp_dig
== 11 and fp
.mant_dig
== 53 and fp
.align
== 64:
780 # Returns the equivalent C type of a TSDL type.
782 # obj: TSDL type of which to get the equivalent C type
783 def _get_obj_param_ctype(self
, obj
):
784 return self
._obj
_param
_ctype
_cb
[type(obj
)](obj
)
786 # Returns the check offset overflow macro call string for a given size.
788 # size: size to check
789 def _get_chk_offset_v(self
, size
):
790 fmt
= '{}_CHK_OFFSET_V({}, {}, {});'
791 ret
= fmt
.format(self
._prefix
.upper(), self
._CTX
_AT
,
792 self
._CTX
_PACKET
_SIZE
, size
)
796 # Returns the check offset overflow macro call C line for a given size.
798 # size: size to check
799 def _get_chk_offset_v_cline(self
, size
):
800 return _CLine(self
._get
_chk
_offset
_v
(size
))
802 # Returns the offset alignment macro call string for a given alignment.
804 # size: new alignment
805 def _get_align_offset(self
, align
, at
=None):
809 fmt
= '{}_ALIGN_OFFSET({}, {});'
810 ret
= fmt
.format(self
._prefix
.upper(), at
, align
)
814 # Returns the offset alignment macro call C line for a given alignment.
816 # size: new alignment
817 def _get_align_offset_cline(self
, size
):
818 return _CLine(self
._get
_align
_offset
(size
))
820 # Converts a C source string with newlines to an array of C lines and
824 def _str_to_clines(self
, s
):
825 lines
= s
.split('\n')
827 return [_CLine(line
) for line
in lines
]
829 # Fills a given template with values and returns its C lines. The `prefix`
830 # and `ucprefix` template variable are automatically provided using the
831 # generator's context.
834 # kwargs: additional template variable values
835 def _template_to_clines(self
, tmpl
, **kwargs
):
836 s
= tmpl
.format(prefix
=self
._prefix
, ucprefix
=self
._prefix
.upper(),
839 return self
._str
_to
_clines
(s
)
841 # Returns the C lines for writing a TSDL structure field.
844 # src_name: C source pointer
845 # struct: TSDL structure
846 def _write_field_struct(self
, fname
, src_name
, struct
, scope_prefix
=None):
847 size
= self
._get
_struct
_size
(struct
)
848 size_bytes
= self
._get
_alignment
(size
, 8) // 8
849 dst
= self
._CTX
_BUF
_AT
_ADDR
852 # memcpy() is safe since barectf requires inner structures
854 self
._get
_chk
_offset
_v
_cline
(size
),
855 _CLine('memcpy({}, {}, {});'.format(dst
, src_name
, size_bytes
)),
856 _CLine('{} += {};'.format(self
._CTX
_AT
, size
)),
859 # Returns the C lines for writing a TSDL integer field.
862 # src_name: C source integer
863 # integer: TSDL integer
864 def _write_field_integer(self
, fname
, src_name
, integer
, scope_prefix
=None):
865 bo
= self
._BO
_SUFFIXES
_MAP
[integer
.byte_order
]
866 t
= self
._get
_obj
_param
_ctype
(integer
)
867 length
= self
._get
_obj
_size
(integer
)
869 return self
._template
_to
_clines
(barectf
.templates
.WRITE_INTEGER
,
870 sz
=length
, bo
=bo
, type=t
,
873 # Returns the C lines for writing a TSDL enumeration field.
876 # src_name: C source integer
877 # enum: TSDL enumeration
878 def _write_field_enum(self
, fname
, src_name
, enum
, scope_prefix
=None):
879 return self
._write
_field
_obj
(fname
, src_name
, enum
.integer
,
882 # Returns the C lines for writing a TSDL floating point number field.
885 # src_name: C source pointer
886 # floating_point: TSDL floating point number
887 def _write_field_floating_point(self
, fname
, src_name
, floating_point
,
889 bo
= self
._BO
_SUFFIXES
_MAP
[floating_point
.byte_order
]
890 t
= self
._get
_obj
_param
_ctype
(floating_point
)
891 length
= self
._get
_obj
_size
(floating_point
)
898 src_name_casted
= '*(({}*) &{})'.format(t
, src_name
)
900 return self
._template
_to
_clines
(barectf
.templates
.WRITE_INTEGER
,
901 sz
=length
, bo
=bo
, type=t
,
902 src_name
=src_name_casted
)
904 # Returns the C lines for writing either a TSDL array field or a
905 # TSDL sequence field.
908 # src_name: C source pointer
909 # arrayseq: TSDL array or sequence
910 # scope_prefix: preferred scope prefix
911 def _write_field_array_sequence(self
, fname
, src_name
, arrayseq
,
913 def length_index_varname(index
):
914 return 'lens_{}_{}'.format(fname
, index
)
916 # first pass: find all lengths to multiply
921 mulops
.append(arrayseq
.length
)
922 element
= arrayseq
.element
925 if tel
is pytsdl
.tsdl
.Array
or tel
is pytsdl
.tsdl
.Sequence
:
926 # another array/sequence; continue
933 # align the size of the repeating element (effective repeating size)
934 el_size
= self
._get
_obj
_size
(element
)
935 el_align
= self
._get
_obj
_alignment
(element
)
936 el_size
= self
._get
_alignment
(el_size
, el_align
)
938 # this effective size is part of the operands to multiply
939 mulops
.append(el_size
)
944 # fetch and save sequence lengths
947 for i
in range(len(mulops
)):
950 if type(mulop
) is list:
951 # offset variable to fetch
952 offvar
= self
._get
_seq
_length
_src
_name
(mulop
, scope_prefix
)
954 if type(offvar
) is int:
955 # environment constant
956 emulops
.append(str(offvar
))
959 # save buffer position
960 line
= 'ctx_at_bkup = {};'.format(self
._CTX
_AT
)
961 clines
.append(_CLine(line
))
963 # go back to field offset
964 line
= '{} = {};'.format(self
._CTX
_AT
, offvar
)
965 clines
.append(_CLine(line
))
967 # read value into specific variable
968 varname
= length_index_varname(i
)
969 emulops
.append(varname
)
970 varctype
= 'uint32_t'
971 fmt
= '{ctype} {cname} = *(({ctype}*) ({ctxbufataddr}));'
972 line
= fmt
.format(ctype
=varctype
, cname
=varname
,
973 ctxbufataddr
=self
._CTX
_BUF
_AT
_ADDR
)
974 clines
.append(_CLine(line
))
976 # restore buffer position
977 line
= '{} = ctx_at_bkup;'.format(self
._CTX
_AT
)
978 clines
.append(_CLine(line
))
980 emulops
.append(str(mulop
))
982 # write product of sizes in bits
983 mul
= ' * '.join(emulops
)
984 sz_bits_varname
= 'sz_bits_{}'.format(fname
)
985 sz_bytes_varname
= 'sz_bytes_{}'.format(fname
)
986 line
= 'uint32_t {} = {};'.format(sz_bits_varname
, mul
)
987 clines
.append(_CLine(line
))
990 clines
.append(self
._get
_chk
_offset
_v
_cline
(sz_bits_varname
))
992 # write product of sizes in bytes
993 line
= 'uint32_t {} = {};'.format(sz_bytes_varname
, sz_bits_varname
)
994 clines
.append(_CLine(line
))
995 line
= self
._get
_align
_offset
(8, at
=sz_bytes_varname
)
996 clines
.append(_CLine(line
))
997 line
= '{} >>= 3;'.format(sz_bytes_varname
)
998 clines
.append(_CLine(line
))
1001 dst
= self
._CTX
_BUF
_AT
_ADDR
1002 line
= 'memcpy({}, {}, {});'.format(dst
, src_name
, sz_bytes_varname
)
1003 clines
.append(_CLine(line
))
1004 line
= '{} += {};'.format(self
._CTX
_AT
, sz_bits_varname
)
1005 clines
.append(_CLine(line
))
1009 # Returns the C lines for writing a TSDL array field.
1012 # src_name: C source pointer
1014 # scope_prefix: preferred scope prefix
1015 def _write_field_array(self
, fname
, src_name
, array
, scope_prefix
=None):
1016 return self
._write
_field
_array
_sequence
(fname
, src_name
, array
,
1019 # Returns the C lines for writing a TSDL sequence field.
1022 # src_name: C source pointer
1023 # sequence: TSDL sequence
1024 # scope_prefix: preferred scope prefix
1025 def _write_field_sequence(self
, fname
, src_name
, sequence
, scope_prefix
):
1026 return self
._write
_field
_array
_sequence
(fname
, src_name
, sequence
,
1029 # Returns a trace packet header C source name out of a sequence length
1032 # length: sequence length expression
1033 def _get_tph_src_name(self
, length
):
1034 offvar
= self
._get
_offvar
_name
_from
_expr
(length
[3:], 'tph')
1036 return 'ctx->{}'.format(offvar
)
1038 # Returns an environment C source name out of a sequence length
1041 # length: sequence length expression
1042 def _get_env_src_name(self
, length
):
1043 if len(length
) != 2:
1044 _perror('invalid sequence length: "{}"'.format(self
._dot
_name
_to
_str
(length
)))
1048 if fname
not in self
._doc
.env
:
1049 _perror('cannot find field env.{}'.format(fname
))
1051 env_length
= self
._doc
.env
[fname
]
1053 if type(env_length
) is not int:
1054 _perror('env.{} is not a constant integer'.format(fname
))
1056 return self
._doc
.env
[fname
]
1058 # Returns a stream packet context C source name out of a sequence length
1061 # length: sequence length expression
1062 def _get_spc_src_name(self
, length
):
1063 offvar
= self
._get
_offvar
_name
_from
_expr
(length
[3:], 'spc')
1065 return 'ctx->{}'.format(offvar
)
1067 # Returns a stream event header C source name out of a sequence length
1070 # length: sequence length expression
1071 def _get_seh_src_name(self
, length
):
1072 return self
._get
_offvar
_name
_from
_expr
(length
[3:], 'seh')
1074 # Returns a stream event context C source name out of a sequence length
1077 # length: sequence length expression
1078 def _get_sec_src_name(self
, length
):
1079 return self
._get
_offvar
_name
_from
_expr
(length
[3:], 'sec')
1081 # Returns an event context C source name out of a sequence length
1084 # length: sequence length expression
1085 def _get_ec_src_name(self
, length
):
1086 return self
._get
_offvar
_name
_from
_expr
(length
[2:], 'ec')
1088 # Returns an event fields C source name out of a sequence length
1091 # length: sequence length expression
1092 def _get_ef_src_name(self
, length
):
1093 return self
._get
_offvar
_name
_from
_expr
(length
[2:], 'ef')
1095 # Returns a C source name out of a sequence length expression.
1097 # length: sequence length expression
1098 # scope_prefix: preferred scope prefix
1099 def _get_seq_length_src_name(self
, length
, scope_prefix
=None):
1100 length_dot
= self
._dot
_name
_to
_str
(length
)
1102 for prefix
, get_src_name
in self
._get
_src
_name
_funcs
.items():
1103 if length_dot
.startswith(prefix
):
1104 return get_src_name(length
)
1106 return self
._get
_offvar
_name
_from
_expr
(length
, scope_prefix
)
1108 # Returns the C lines for writing a TSDL string field.
1111 # src_name: C source pointer
1112 # string: TSDL string
1113 def _write_field_string(self
, fname
, src_name
, string
, scope_prefix
=None):
1117 sz_bytes_varname
= 'slen_bytes_{}'.format(fname
)
1118 line
= 'size_t {} = strlen({}) + 1;'.format(sz_bytes_varname
, src_name
)
1119 clines
.append(_CLine(line
))
1121 # check offset overflow
1122 sz_bits_varname
= 'slen_bits_{}'.format(fname
)
1123 line
= 'size_t {} = ({} << 3);'.format(sz_bits_varname
,
1125 clines
.append(_CLine(line
))
1126 cline
= self
._get
_chk
_offset
_v
_cline
(sz_bits_varname
)
1127 clines
.append(cline
)
1130 dst
= self
._CTX
_BUF
_AT
_ADDR
1131 line
= 'memcpy({}, {}, {});'.format(dst
, src_name
, sz_bytes_varname
)
1132 clines
.append(_CLine(line
))
1134 # update bit position
1135 line
= '{} += {};'.format(self
._CTX
_AT
, sz_bits_varname
)
1136 clines
.append(_CLine(line
))
1140 # Returns the C lines for writing a TSDL type field.
1143 # src_name: C source pointer
1145 # scope_prefix: preferred scope prefix
1146 def _write_field_obj(self
, fname
, src_name
, ftype
, scope_prefix
):
1147 return self
._write
_field
_obj
_cb
[type(ftype
)](fname
, src_name
, ftype
,
1150 # Returns an offset variable name out of an offset name.
1153 # prefix: offset variable name prefix
1154 def _get_offvar_name(self
, name
, prefix
=None):
1157 if prefix
is not None:
1158 parts
.append(prefix
)
1162 return '_'.join(parts
)
1164 # Returns an offset variable name out of an expression (array of
1167 # expr: array of strings
1168 # prefix: offset variable name prefix
1169 def _get_offvar_name_from_expr(self
, expr
, prefix
=None):
1170 return self
._get
_offvar
_name
('_'.join(expr
), prefix
)
1172 # Returns the C lines for writing a TSDL field.
1175 # ftype: TSDL field type
1176 # scope_name: scope name
1177 # scope_prefix: preferred scope prefix
1178 # param_name_cb: callback to get the C parameter name out of the
1180 def _field_to_clines(self
, fname
, ftype
, scope_name
, scope_prefix
,
1183 pname
= param_name_cb(fname
)
1184 align
= self
._get
_obj
_alignment
(ftype
)
1187 fmt
= '/* write {}.{} ({}) */'
1188 line
= fmt
.format(scope_name
, fname
,
1189 self
._TSDL
_TYPE
_NAMES
_MAP
[type(ftype
)])
1190 clines
.append(_CLine(line
))
1192 # align bit index before writing to the buffer
1193 cline
= self
._get
_align
_offset
_cline
(align
)
1194 clines
.append(cline
)
1196 # write offset variables
1197 if type(ftype
) is pytsdl
.tsdl
.Struct
:
1198 offvars_tree
= collections
.OrderedDict()
1199 self
._get
_struct
_size
(ftype
, offvars_tree
)
1200 offvars
= self
._flatten
_offvars
_tree
(offvars_tree
)
1202 # as many offset as there are child fields because a future
1203 # sequence could refer to any of those fields
1204 for lname
, offset
in offvars
.items():
1205 offvar
= self
._get
_offvar
_name
('_'.join([fname
, lname
]),
1207 fmt
= 'uint32_t {} = (uint32_t) {} + {};'
1208 line
= fmt
.format(offvar
, self
._CTX
_AT
, offset
);
1209 clines
.append(_CLine(line
))
1210 elif type(ftype
) is pytsdl
.tsdl
.Integer
:
1211 # offset of this simple field is the current bit index
1212 offvar
= self
._get
_offvar
_name
(fname
, scope_prefix
)
1213 line
= 'uint32_t {} = (uint32_t) {};'.format(offvar
, self
._CTX
_AT
)
1214 clines
.append(_CLine(line
))
1216 clines
+= self
._write
_field
_obj
(fname
, pname
, ftype
, scope_prefix
)
1220 # Joins C line groups and returns C lines.
1222 # cline_groups: C line groups to join
1223 def _join_cline_groups(self
, cline_groups
):
1224 if not cline_groups
:
1227 output_clines
= cline_groups
[0]
1229 for clines
in cline_groups
[1:]:
1230 output_clines
.append('')
1231 output_clines
+= clines
1233 return output_clines
1235 # Returns the C lines for writing a complete TSDL structure (top level
1238 # struct: TSDL structure
1239 # scope_name: scope name
1240 # scope_prefix: preferred scope prefix
1241 # param_name_cb: callback to get the C parameter name out of the
1243 def _struct_to_clines(self
, struct
, scope_name
, scope_prefix
,
1247 for fname
, ftype
in struct
.fields
.items():
1248 clines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1249 scope_prefix
, param_name_cb
)
1250 cline_groups
.append(clines
)
1252 return self
._join
_cline
_groups
(cline_groups
)
1254 # Returns the offset variables of a TSDL structure.
1256 # struct: TSDL structure
1257 def _get_struct_size_offvars(self
, struct
):
1258 offvars_tree
= collections
.OrderedDict()
1259 size
= self
._get
_struct
_size
(struct
, offvars_tree
)
1260 offvars
= self
._flatten
_offvars
_tree
(offvars_tree
)
1262 return size
, offvars
1264 # Returns the size and offset variables of the current trace packet header.
1265 def _get_tph_size_offvars(self
):
1266 return self
._get
_struct
_size
_offvars
(self
._doc
.trace
.packet_header
)
1268 # Returns the size and offset variables of the a stream packet context.
1270 # stream: TSDL stream
1271 def _get_spc_size_offvars(self
, stream
):
1272 return self
._get
_struct
_size
_offvars
(stream
.packet_context
)
1274 # Returns the C lines for the barectf context C structure entries for
1277 # prefix: offset variable names prefix
1278 # offvars: offset variables
1279 def _offvars_to_ctx_clines(self
, prefix
, offvars
):
1282 for name
in offvars
.keys():
1283 offvar
= self
._get
_offvar
_name
(name
, prefix
)
1284 clines
.append(_CLine('uint32_t {};'.format(offvar
)))
1288 # Generates a barectf context C structure.
1290 # stream: TSDL stream
1291 # hide_sid: True to hide the stream ID
1292 def _gen_barectf_ctx_struct(self
, stream
, hide_sid
=False):
1293 # get offset variables for both the packet header and packet context
1294 tph_size
, tph_offvars
= self
._get
_tph
_size
_offvars
()
1295 spc_size
, spc_offvars
= self
._get
_spc
_size
_offvars
(stream
)
1296 clines
= self
._offvars
_to
_ctx
_clines
('tph', tph_offvars
)
1297 clines
+= self
._offvars
_to
_ctx
_clines
('spc', spc_offvars
)
1300 clines_indented
= []
1301 for cline
in clines
:
1302 clines_indented
.append(_CLine('\t' + cline
))
1305 clock_cb
= '\t/* (no clock callback) */'
1307 if not self
._manual
_clock
:
1308 ctype
= self
._get
_clock
_ctype
(stream
)
1309 fmt
= '\t{} (*clock_cb)(void*);\n\tvoid* clock_cb_data;'
1310 clock_cb
= fmt
.format(ctype
)
1318 t
= barectf
.templates
.BARECTF_CTX
1319 struct
= t
.format(prefix
=self
._prefix
, sid
=sid
,
1320 ctx_fields
='\n'.join(clines_indented
),
1325 # Generates all barectf context C structures.
1326 def _gen_barectf_contexts_struct(self
):
1329 if len(self
._doc
.streams
) == 1:
1334 for stream
in self
._doc
.streams
.values():
1335 struct
= self
._gen
_barectf
_ctx
_struct
(stream
, hide_sid
)
1336 structs
.append(struct
)
1338 return '\n\n'.join(structs
)
1340 # Returns the C type of the clock used by the event header of a
1343 # stream: TSDL stream containing the event header to inspect
1344 def _get_clock_ctype(self
, stream
):
1345 return self
._get
_obj
_param
_ctype
(stream
.event_header
['timestamp'])
1347 # Generates the manual clock value C parameter for a given stream.
1349 # stream: TSDL stream
1350 def _gen_manual_clock_param(self
, stream
):
1351 return '{} param_clock'.format(self
._get
_clock
_ctype
(stream
))
1353 # Generates the body of a barectf_open() function.
1355 # stream: TSDL stream
1356 def _gen_barectf_func_open_body(self
, stream
):
1359 # keep clock value (for timestamp_begin)
1360 if self
._stream
_has
_timestamp
_begin
_end
(stream
):
1361 # get clock value ASAP
1362 clk_type
= self
._get
_clock
_ctype
(stream
)
1363 clk
= self
._gen
_get
_clock
_value
()
1364 line
= '{} clk_value = {};'.format(clk_type
, clk
)
1365 clines
.append(_CLine(line
))
1366 clines
.append(_CLine(''))
1368 # reset bit position to write the packet context (after packet header)
1369 spc_offset
= self
._get
_stream
_packet
_context
_offset
(stream
)
1371 line
= fmt
.format(self
._CTX
_AT
, spc_offset
)
1372 clines
.append(_CLine(line
))
1374 # bit position at beginning of event (to reset in case we run
1376 line
= 'uint32_t ctx_at_begin = {};'.format(self
._CTX
_AT
)
1377 clines
.append(_CLine(line
))
1378 clines
.append(_CLine(''))
1380 # packet context fields
1382 scope_name
= 'stream.packet.context'
1383 scope_prefix
= 'spc'
1385 for fname
, ftype
in stream
.packet_context
.fields
.items():
1387 if fname
== 'packet_size':
1388 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1390 lambda x
: 'ctx->packet_size')
1391 fcline_groups
.append(fclines
)
1393 # content size (skip)
1394 elif fname
== 'content_size':
1395 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1396 scope_prefix
, lambda x
: '0')
1397 fcline_groups
.append(fclines
)
1400 elif fname
== 'timestamp_begin':
1401 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1403 lambda x
: 'clk_value')
1404 fcline_groups
.append(fclines
)
1406 # timestamp_end (skip)
1407 elif fname
== 'timestamp_end':
1408 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1409 scope_prefix
, lambda x
: '0')
1410 fcline_groups
.append(fclines
)
1414 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1416 self
._spc
_fname
_to
_pname
)
1417 fcline_groups
.append(fclines
)
1420 fcline_groups
.append([_CLine('return 0;')])
1422 clines
+= self
._join
_cline
_groups
(fcline_groups
)
1425 cblock
= _CBlock(clines
)
1426 src
= self
._cblock
_to
_source
(cblock
)
1430 _SPC_KNOWN_FIELDS
= [
1437 # Generates a barectf_open() function.
1439 # stream: TSDL stream
1440 # gen_body: also generate function body
1441 # hide_sid: True to hide the stream ID
1442 def _gen_barectf_func_open(self
, stream
, gen_body
, hide_sid
=False):
1446 if self
._manual
_clock
:
1447 clock_param
= self
._gen
_manual
_clock
_param
(stream
)
1448 params
.append(clock_param
)
1451 for fname
, ftype
in stream
.packet_context
.fields
.items():
1452 if fname
in self
._SPC
_KNOWN
_FIELDS
:
1455 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1456 pname
= self
._spc
_fname
_to
_pname
(fname
)
1457 param
= '{} {}'.format(ptype
, pname
)
1458 params
.append(param
)
1463 params_str
= ',\n\t'.join([''] + params
)
1471 t
= barectf
.templates
.FUNC_OPEN
1472 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1477 func
+= self
._gen
_barectf
_func
_open
_body
(stream
)
1484 # Generates the body of a barectf_init() function.
1486 # stream: TSDL stream
1487 def _gen_barectf_func_init_body(self
, stream
):
1490 line
= 'uint32_t ctx_at_bkup;'
1491 clines
.append(_CLine(line
))
1493 # bit position at beginning of event (to reset in case we run
1495 line
= 'uint32_t ctx_at_begin = {};'.format(self
._CTX
_AT
)
1496 clines
.append(_CLine(line
))
1497 clines
.append(_CLine(''))
1499 # set context parameters
1500 clines
.append(_CLine("/* barectf context parameters */"))
1501 clines
.append(_CLine('ctx->buf = buf;'))
1502 clines
.append(_CLine('ctx->packet_size = buf_size * 8;'))
1503 clines
.append(_CLine('{} = 0;'.format(self
._CTX
_AT
)))
1505 if not self
._manual
_clock
:
1506 clines
.append(_CLine('ctx->clock_cb = clock_cb;'))
1507 clines
.append(_CLine('ctx->clock_cb_data = clock_cb_data;'))
1509 # set context offsets
1510 clines
.append(_CLine(''))
1511 clines
.append(_CLine("/* barectf context offsets */"))
1512 ph_size
, ph_offvars
= self
._get
_tph
_size
_offvars
()
1513 pc_size
, pc_offvars
= self
._get
_spc
_size
_offvars
(stream
)
1514 pc_alignment
= self
._get
_obj
_alignment
(stream
.packet_context
)
1515 pc_offset
= self
._get
_alignment
(ph_size
, pc_alignment
)
1517 for offvar
, offset
in ph_offvars
.items():
1518 offvar_field
= self
._get
_offvar
_name
(offvar
, 'tph')
1519 line
= 'ctx->{} = {};'.format(offvar_field
, offset
)
1520 clines
.append(_CLine(line
))
1522 for offvar
, offset
in pc_offvars
.items():
1523 offvar_field
= self
._get
_offvar
_name
(offvar
, 'spc')
1524 line
= 'ctx->{} = {};'.format(offvar_field
, pc_offset
+ offset
)
1525 clines
.append(_CLine(line
))
1527 clines
.append(_CLine(''))
1529 # packet header fields
1531 scope_name
= 'trace.packet.header'
1532 scope_prefix
= 'tph'
1534 for fname
, ftype
in self
._doc
.trace
.packet_header
.fields
.items():
1536 if fname
== 'magic':
1537 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1539 lambda x
: '0xc1fc1fc1UL')
1540 fcline_groups
.append(fclines
)
1543 elif fname
== 'stream_id':
1544 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1546 lambda x
: str(stream
.id))
1547 fcline_groups
.append(fclines
)
1550 fcline_groups
.append([_CLine('return 0;')])
1552 clines
+= self
._join
_cline
_groups
(fcline_groups
)
1555 cblock
= _CBlock(clines
)
1556 src
= self
._cblock
_to
_source
(cblock
)
1560 # Generates a barectf_init() function.
1562 # stream: TSDL stream
1563 # gen_body: also generate function body
1564 # hide_sid: True to hide the stream ID
1565 def _gen_barectf_func_init(self
, stream
, gen_body
, hide_sid
=False):
1574 if not self
._manual
_clock
:
1575 ts_ftype
= stream
.event_header
['timestamp']
1576 ts_ptype
= self
._get
_obj
_param
_ctype
(ts_ftype
)
1577 fmt
= ',\n\t{} (*clock_cb)(void*),\n\tvoid* clock_cb_data'
1578 params
= fmt
.format(ts_ptype
)
1580 t
= barectf
.templates
.FUNC_INIT
1581 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1586 func
+= self
._gen
_barectf
_func
_init
_body
(stream
)
1593 # Generates the C expression to get the clock value depending on
1594 # whether we're in manual clock mode or not.
1595 def _gen_get_clock_value(self
):
1596 if self
._manual
_clock
:
1597 return 'param_clock'
1599 return self
._CTX
_CALL
_CLOCK
_CB
1601 # Returns True if the given TSDL stream has timestamp_begin and
1602 # timestamp_end fields.
1604 # stream: TSDL stream to check
1605 def _stream_has_timestamp_begin_end(self
, stream
):
1606 return self
._has
_timestamp
_begin
_end
[stream
.id]
1608 # Returns the packet context offset (from the beginning of the
1609 # packet) of a given TSDL stream
1611 # stream: TSDL stream
1612 def _get_stream_packet_context_offset(self
, stream
):
1613 return self
._packet
_context
_offsets
[stream
.id]
1615 # Generates the C lines to write a barectf context field, saving
1616 # and restoring the current bit position accordingly.
1618 # src_name: C source name
1619 # prefix: offset variable prefix
1620 # name: offset variable name
1621 # integer: TSDL integer to write
1622 def _gen_write_ctx_field_integer(self
, src_name
, prefix
, name
, integer
):
1625 # save buffer position
1626 line
= 'ctx_at_bkup = {};'.format(self
._CTX
_AT
)
1627 clines
.append(_CLine(line
))
1629 # go back to field offset
1630 offvar
= self
._get
_offvar
_name
(name
, prefix
)
1631 line
= '{} = ctx->{};'.format(self
._CTX
_AT
, offvar
)
1632 clines
.append(_CLine(line
))
1635 clines
+= self
._write
_field
_integer
(None, src_name
, integer
)
1637 # restore buffer position
1638 line
= '{} = ctx_at_bkup;'.format(self
._CTX
_AT
)
1639 clines
.append(_CLine(line
))
1643 # Generates the body of a barectf_close() function.
1645 # stream: TSDL stream
1646 def _gen_barectf_func_close_body(self
, stream
):
1649 line
= 'uint32_t ctx_at_bkup;'
1650 clines
.append(_CLine(line
))
1652 # bit position at beginning of event (to reset in case we run
1654 line
= 'uint32_t ctx_at_begin = {};'.format(self
._CTX
_AT
)
1655 clines
.append(_CLine(line
))
1657 # update timestamp end if present
1658 if self
._stream
_has
_timestamp
_begin
_end
(stream
):
1659 clines
.append(_CLine(''))
1660 clines
.append(_CLine("/* update packet context's timestamp_end */"))
1662 # get clock value ASAP
1663 clk_type
= self
._get
_clock
_ctype
(stream
)
1664 clk
= self
._gen
_get
_clock
_value
()
1665 line
= '{} clk_value = {};'.format(clk_type
, clk
)
1666 clines
.append(_CLine(line
))
1668 # write timestamp_end
1669 timestamp_end_integer
= stream
.packet_context
['timestamp_end']
1670 clines
+= self
._gen
_write
_ctx
_field
_integer
('clk_value', 'spc',
1672 timestamp_end_integer
)
1674 # update content_size
1675 clines
.append(_CLine(''))
1676 clines
.append(_CLine("/* update packet context's content_size */"))
1677 content_size_integer
= stream
.packet_context
['content_size']
1678 clines
+= self
._gen
_write
_ctx
_field
_integer
('ctx_at_bkup', 'spc',
1680 content_size_integer
)
1683 clines
.append(_CLine('\n'))
1684 clines
.append(_CLine('return 0;'))
1687 cblock
= _CBlock(clines
)
1688 src
= self
._cblock
_to
_source
(cblock
)
1692 # Generates a barectf_close() function.
1694 # stream: TSDL stream
1695 # gen_body: also generate function body
1696 # hide_sid: True to hide the stream ID
1697 def _gen_barectf_func_close(self
, stream
, gen_body
, hide_sid
=False):
1706 if self
._manual
_clock
:
1707 clock_param
= self
._gen
_manual
_clock
_param
(stream
)
1708 params
= ',\n\t{}'.format(clock_param
)
1710 t
= barectf
.templates
.FUNC_CLOSE
1711 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1716 func
+= self
._gen
_barectf
_func
_close
_body
(stream
)
1723 # Generates all barectf_init() function.
1725 # gen_body: also generate function bodies
1726 def _gen_barectf_funcs_init(self
, gen_body
):
1729 if len(self
._doc
.streams
) == 1:
1734 for stream
in self
._doc
.streams
.values():
1735 funcs
.append(self
._gen
_barectf
_func
_init
(stream
, gen_body
,
1740 # Generates all barectf_open() function.
1742 # gen_body: also generate function bodies
1743 def _gen_barectf_funcs_open(self
, gen_body
):
1746 if len(self
._doc
.streams
) == 1:
1751 for stream
in self
._doc
.streams
.values():
1752 funcs
.append(self
._gen
_barectf
_func
_open
(stream
, gen_body
,
1757 # Generates the body of a barectf_trace() function.
1759 # stream: TSDL stream of TSDL event to trace
1760 # event: TSDL event to trace
1761 def _gen_barectf_func_trace_event_body(self
, stream
, event
):
1764 # get clock value ASAP
1765 clk_type
= self
._get
_clock
_ctype
(stream
)
1766 clk
= self
._gen
_get
_clock
_value
()
1767 line
= '{} clk_value = {};'.format(clk_type
, clk
)
1768 clines
.append(_CLine(line
))
1769 clines
.append(_CLine(''))
1771 # bit position backup (could be used)
1772 clines
.append(_CLine('uint32_t ctx_at_bkup;'))
1774 # bit position at beginning of event (to reset in case we run
1776 line
= 'uint32_t ctx_at_begin = {};'.format(self
._CTX
_AT
)
1777 clines
.append(_CLine(line
))
1778 clines
.append(_CLine(''))
1782 scope_name
= 'event.header'
1785 for fname
, ftype
in stream
.event_header
.fields
.items():
1788 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1790 lambda x
: str(event
.id))
1791 fcline_groups
.append(fclines
)
1794 elif fname
== 'timestamp':
1795 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
1797 lambda x
: 'clk_value')
1798 fcline_groups
.append(fclines
)
1800 # stream event context
1801 if stream
.event_context
is not None:
1802 fclines
= self
._struct
_to
_clines
(stream
.event_context
,
1803 'stream.event.context', 'sec',
1804 self
._sec
_fname
_to
_pname
)
1805 fcline_groups
.append(fclines
)
1808 if event
.context
is not None:
1809 fclines
= self
._struct
_to
_clines
(event
.context
,
1810 'event.context', 'ec',
1811 self
._ec
_fname
_to
_pname
)
1812 fcline_groups
.append(fclines
)
1815 if event
.fields
is not None:
1816 fclines
= self
._struct
_to
_clines
(event
.fields
,
1817 'event.fields', 'ef',
1818 self
._ef
_fname
_to
_pname
)
1819 fcline_groups
.append(fclines
)
1822 fcline_groups
.append([_CLine('return 0;')])
1824 clines
+= self
._join
_cline
_groups
(fcline_groups
)
1827 cblock
= _CBlock(clines
)
1828 src
= self
._cblock
_to
_source
(cblock
)
1832 # Generates a barectf_trace() function.
1834 # stream: TSDL stream containing the TSDL event to trace
1835 # event: TSDL event to trace
1836 # gen_body: also generate function body
1837 # hide_sid: True to hide the stream ID
1838 def _gen_barectf_func_trace_event(self
, stream
, event
, gen_body
, hide_sid
):
1842 if self
._manual
_clock
:
1843 clock_param
= self
._gen
_manual
_clock
_param
(stream
)
1844 params
.append(clock_param
)
1846 # stream event context params
1847 if stream
.event_context
is not None:
1848 for fname
, ftype
in stream
.event_context
.fields
.items():
1849 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1850 pname
= self
._sec
_fname
_to
_pname
(fname
)
1851 param
= '{} {}'.format(ptype
, pname
)
1852 params
.append(param
)
1854 # event context params
1855 if event
.context
is not None:
1856 for fname
, ftype
in event
.context
.fields
.items():
1857 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1858 pname
= self
._ec
_fname
_to
_pname
(fname
)
1859 param
= '{} {}'.format(ptype
, pname
)
1860 params
.append(param
)
1862 # event fields params
1863 if event
.fields
is not None:
1864 for fname
, ftype
in event
.fields
.fields
.items():
1865 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1866 pname
= self
._ef
_fname
_to
_pname
(fname
)
1867 param
= '{} {}'.format(ptype
, pname
)
1868 params
.append(param
)
1873 params_str
= ',\n\t'.join([''] + params
)
1881 t
= barectf
.templates
.FUNC_TRACE
1882 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1883 evname
=event
.name
, params
=params_str
)
1887 func
+= self
._gen
_barectf
_func
_trace
_event
_body
(stream
, event
)
1894 # Generates all barectf_trace() functions of a given TSDL stream.
1896 # stream: TSDL stream containing the TSDL events to trace
1897 # gen_body: also generate function body
1898 # hide_sid: True to hide the stream ID
1899 def _gen_barectf_funcs_trace_stream(self
, stream
, gen_body
, hide_sid
):
1902 for event
in stream
.events
:
1903 funcs
.append(self
._gen
_barectf
_func
_trace
_event
(stream
, event
,
1904 gen_body
, hide_sid
))
1908 # Generates all barectf_trace() function.
1910 # gen_body: also generate function bodies
1911 def _gen_barectf_funcs_trace(self
, gen_body
):
1914 if len(self
._doc
.streams
) == 1:
1919 for stream
in self
._doc
.streams
.values():
1920 funcs
+= self
._gen
_barectf
_funcs
_trace
_stream
(stream
, gen_body
,
1925 # Generates all barectf_close() function.
1927 # gen_body: also generate function bodies
1928 def _gen_barectf_funcs_close(self
, gen_body
):
1931 if len(self
._doc
.streams
) == 1:
1936 for stream
in self
._doc
.streams
.values():
1937 funcs
.append(self
._gen
_barectf
_func
_close
(stream
, gen_body
,
1942 # Generate all barectf functions
1944 # gen_body: also generate function bodies
1945 def _gen_barectf_functions(self
, gen_body
):
1946 init_funcs
= self
._gen
_barectf
_funcs
_init
(gen_body
)
1947 open_funcs
= self
._gen
_barectf
_funcs
_open
(gen_body
)
1948 close_funcs
= self
._gen
_barectf
_funcs
_close
(gen_body
)
1949 trace_funcs
= self
._gen
_barectf
_funcs
_trace
(gen_body
)
1951 return init_funcs
+ open_funcs
+ close_funcs
+ trace_funcs
1953 # Generates the barectf header C source
1954 def _gen_barectf_header(self
):
1955 ctx_structs
= self
._gen
_barectf
_contexts
_struct
()
1956 functions
= self
._gen
_barectf
_functions
(self
._static
_inline
)
1957 functions_str
= '\n\n'.join(functions
)
1958 t
= barectf
.templates
.HEADER
1959 header
= t
.format(prefix
=self
._prefix
, ucprefix
=self
._prefix
.upper(),
1960 barectf_ctx
=ctx_structs
, functions
=functions_str
)
1965 pytsdl
.tsdl
.ByteOrder
.BE
: 'BIG_ENDIAN',
1966 pytsdl
.tsdl
.ByteOrder
.LE
: 'LITTLE_ENDIAN',
1969 # Generates the barectf bitfield.h header.
1970 def _gen_barectf_bitfield_header(self
):
1971 header
= barectf
.templates
.BITFIELD
1972 header
= header
.replace('$prefix$', self
._prefix
)
1973 header
= header
.replace('$PREFIX$', self
._prefix
.upper())
1974 endian_def
= self
._BO
_DEF
_MAP
[self
._doc
.trace
.byte_order
]
1975 header
= header
.replace('$ENDIAN_DEF$', endian_def
)
1979 # Generates the main barectf C source file.
1980 def _gen_barectf_csrc(self
):
1981 functions
= self
._gen
_barectf
_functions
(True)
1982 functions_str
= '\n\n'.join(functions
)
1983 t
= barectf
.templates
.CSRC
1984 csrc
= t
.format(prefix
=self
._prefix
, ucprefix
=self
._prefix
.upper(),
1985 functions
=functions_str
)
1989 # Writes a file to the generator's output.
1992 # contents: file contents
1993 def _write_file(self
, name
, contents
):
1994 path
= os
.path
.join(self
._output
, name
)
1996 with
open(path
, 'w') as f
:
1998 except Exception as e
:
1999 _perror('cannot write "{}": {}'.format(path
, e
))
2001 # Converts a C block to actual C source lines.
2004 # indent: initial indentation
2005 def _cblock_to_source_lines(self
, cblock
, indent
=1):
2007 indentstr
= '\t' * indent
2010 if type(line
) is _CBlock
:
2011 src
+= self
._cblock
_to
_source
_lines
(line
, indent
+ 1)
2013 src
.append(indentstr
+ line
)
2017 # Converts a C block to an actual C source string.
2020 # indent: initial indentation
2021 def _cblock_to_source(self
, cblock
, indent
=1):
2022 lines
= self
._cblock
_to
_source
_lines
(cblock
, indent
)
2024 return '\n'.join(lines
)
2026 # Sets the generator parameters.
2027 def _set_params(self
):
2028 # streams have timestamp_begin/timestamp_end fields
2029 self
._has
_timestamp
_begin
_end
= {}
2031 for stream
in self
._doc
.streams
.values():
2032 has
= 'timestamp_begin' in stream
.packet_context
.fields
2033 self
._has
_timestamp
_begin
_end
[stream
.id] = has
2035 # packet header size with alignment
2036 self
._packet
_context
_offsets
= {}
2038 tph_size
= self
._get
_struct
_size
(self
._doc
.trace
.packet_header
)
2040 for stream
in self
._doc
.streams
.values():
2041 spc_alignment
= self
._get
_obj
_alignment
(stream
.packet_context
)
2042 spc_offset
= self
._get
_alignment
(tph_size
, spc_alignment
)
2043 self
._packet
_context
_offsets
[stream
.id] = spc_offset
2045 # Generates barectf C files.
2047 # metadata: metadata path
2048 # output: output directory
2050 # static_inline: generate static inline functions
2051 # manual_clock: do not use a clock callback: pass clock value to
2053 def gen_barectf(self
, metadata
, output
, prefix
, static_inline
,
2055 self
._metadata
= metadata
2056 self
._output
= output
2057 self
._prefix
= prefix
2058 self
._static
_inline
= static_inline
2059 self
._manual
_clock
= manual_clock
2063 self
._si
_str
= 'static inline '
2065 # open CTF metadata file
2066 _pinfo('opening CTF metadata file "{}"'.format(self
._metadata
))
2069 with
open(metadata
) as f
:
2070 self
._tsdl
= f
.read()
2072 _perror('cannot open/read CTF metadata file "{}"'.format(metadata
))
2074 # parse CTF metadata
2075 _pinfo('parsing CTF metadata file')
2078 self
._doc
= self
._parser
.parse(self
._tsdl
)
2079 except pytsdl
.parser
.ParseError
as e
:
2080 _perror('parse error: {}'.format(e
))
2082 # validate CTF metadata against barectf constraints
2083 _pinfo('validating CTF metadata file')
2084 self
._validate
_metadata
()
2085 _psuccess('CTF metadata file is valid')
2087 # set parameters for this generation
2091 _pinfo('generating barectf header files')
2092 header
= self
._gen
_barectf
_header
()
2093 self
._write
_file
('{}.h'.format(self
._prefix
), header
)
2094 header
= self
._gen
_barectf
_bitfield
_header
()
2095 self
._write
_file
('{}_bitfield.h'.format(self
._prefix
), header
)
2097 # generate C source file
2098 if not self
._static
_inline
:
2099 _pinfo('generating barectf C source file')
2100 csrc
= self
._gen
_barectf
_csrc
()
2101 self
._write
_file
('{}.c'.format(self
._prefix
), csrc
)
2107 args
= _parse_args()
2108 generator
= BarectfCodeGenerator()
2109 generator
.gen_barectf(args
.metadata
, args
.output
, args
.prefix
,
2110 args
.static_inline
, args
.manual_clock
)
This page took 0.079494 seconds and 5 git commands to generate.