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'], file=sys
.stderr
)
43 ap
= argparse
.ArgumentParser()
45 ap
.add_argument('-O', '--output', metavar
='OUTPUT', action
='store',
47 help='output directory of C files')
48 ap
.add_argument('-p', '--prefix', metavar
='PREFIX', action
='store',
50 help='custom prefix for C function and structure names')
51 ap
.add_argument('-s', '--static-inline', action
='store_true',
52 help='generate static inline C functions')
53 ap
.add_argument('-c', '--manual-clock', action
='store_true',
54 help='do not use a clock callback: pass clock value to tracing functions')
55 ap
.add_argument('metadata', metavar
='METADATA', action
='store',
56 help='CTF metadata input file')
59 args
= ap
.parse_args()
61 # validate output directory
62 if not os
.path
.isdir(args
.output
):
63 _perror('"{}" is not an existing directory'.format(args
.output
))
66 if not re
.match(r
'^[a-zA-Z_][a-zA-Z0-9_]*$', args
.prefix
):
67 _perror('"{}" is not a valid C identifier'.format(args
.prefix
))
69 # validate that metadata file exists
70 if not os
.path
.isfile(args
.metadata
):
71 _perror('"{}" is not an existing file'.format(args
.metadata
))
84 class BarectfCodeGenerator
:
87 _CTX_BUF_SIZE
= 'ctx->buf_size'
88 _CTX_BUF_AT
= '{}[{} >> 3]'.format(_CTX_BUF
, _CTX_AT
)
89 _CTX_BUF_AT_ADDR
= '&{}'.format(_CTX_BUF_AT
)
90 _CTX_CALL_CLOCK_CB
= 'ctx->clock_cb(ctx->clock_cb_data)'
93 pytsdl
.tsdl
.ByteOrder
.BE
: 'be',
94 pytsdl
.tsdl
.ByteOrder
.LE
: 'le',
97 _tsdl_type_names_map
= {
98 pytsdl
.tsdl
.Integer
: 'integer',
99 pytsdl
.tsdl
.FloatingPoint
: 'floating point',
100 pytsdl
.tsdl
.Enum
: 'enumeration',
101 pytsdl
.tsdl
.String
: 'string',
102 pytsdl
.tsdl
.Array
: 'static array',
103 pytsdl
.tsdl
.Sequence
: 'dynamic array',
104 pytsdl
.tsdl
.Struct
: 'structure',
108 self
._parser
= pytsdl
.parser
.Parser()
109 self
._obj
_size
_cb
= {
110 pytsdl
.tsdl
.Struct
: self
._get
_struct
_size
,
111 pytsdl
.tsdl
.Integer
: self
._get
_integer
_size
,
112 pytsdl
.tsdl
.Enum
: self
._get
_enum
_size
,
113 pytsdl
.tsdl
.FloatingPoint
: self
._get
_floating
_point
_size
,
114 pytsdl
.tsdl
.Array
: self
._get
_array
_size
,
116 self
._obj
_alignment
_cb
= {
117 pytsdl
.tsdl
.Struct
: self
._get
_struct
_alignment
,
118 pytsdl
.tsdl
.Integer
: self
._get
_integer
_alignment
,
119 pytsdl
.tsdl
.Enum
: self
._get
_enum
_alignment
,
120 pytsdl
.tsdl
.FloatingPoint
: self
._get
_floating
_point
_alignment
,
121 pytsdl
.tsdl
.Array
: self
._get
_array
_alignment
,
122 pytsdl
.tsdl
.Sequence
: self
._get
_sequence
_alignment
,
123 pytsdl
.tsdl
.String
: self
._get
_string
_alignment
,
125 self
._obj
_param
_ctype
_cb
= {
126 pytsdl
.tsdl
.Struct
: lambda obj
: 'const void*',
127 pytsdl
.tsdl
.Integer
: self
._get
_integer
_param
_ctype
,
128 pytsdl
.tsdl
.Enum
: self
._get
_enum
_param
_ctype
,
129 pytsdl
.tsdl
.FloatingPoint
: self
._get
_floating
_point
_param
_ctype
,
130 pytsdl
.tsdl
.Array
: lambda obj
: 'const void*',
131 pytsdl
.tsdl
.Sequence
: lambda obj
: 'const void*',
132 pytsdl
.tsdl
.String
: lambda obj
: 'const char*',
134 self
._write
_field
_obj
_cb
= {
135 pytsdl
.tsdl
.Struct
: self
._write
_field
_struct
,
136 pytsdl
.tsdl
.Integer
: self
._write
_field
_integer
,
137 pytsdl
.tsdl
.Enum
: self
._write
_field
_enum
,
138 pytsdl
.tsdl
.FloatingPoint
: self
._write
_field
_floating
_point
,
139 pytsdl
.tsdl
.Array
: self
._write
_field
_array
,
140 pytsdl
.tsdl
.Sequence
: self
._write
_field
_sequence
,
141 pytsdl
.tsdl
.String
: self
._write
_field
_string
,
144 # TODO: prettify this function
145 def _validate_struct(self
, struct
):
146 # just in case we call this with the wrong type
147 if type(struct
) is not pytsdl
.tsdl
.Struct
:
148 raise RuntimeError('expecting a struct')
150 # make sure inner structures are at least byte-aligned
151 if self
._get
_obj
_alignment
(struct
) < 8:
152 raise RuntimeError('inner struct must be at least byte-aligned')
155 for fname
, ftype
in struct
.fields
.items():
156 if type(ftype
) is pytsdl
.tsdl
.Sequence
:
157 raise RuntimeError('field "{}" is a dynamic array (not allowed here)'.format(fname
))
158 elif type(ftype
) is pytsdl
.tsdl
.Array
:
159 # we need to check every element until we find a terminal one
160 element
= ftype
.element
163 if type(element
) is pytsdl
.tsdl
.Sequence
:
164 raise RuntimeError('field "{}" contains a dynamic array (not allowed here)'.format(fname
))
165 elif type(element
) is pytsdl
.tsdl
.Variant
:
166 raise RuntimeError('field "{}" contains a variant (unsupported)'.format(fname
))
167 elif type(element
) is pytsdl
.tsdl
.String
:
168 raise RuntimeError('field "{}" contains a string (not allowed here)'.format(fname
))
169 elif type(element
) is pytsdl
.tsdl
.Struct
:
170 _validate_struct(element
)
171 elif type(element
) is pytsdl
.tsdl
.Integer
:
172 if self
._get
_obj
_size
(element
) > 64:
173 raise RuntimeError('integer field "{}" larger than 64-bit'.format(fname
))
174 elif type(element
) is pytsdl
.tsdl
.FloatingPoint
:
175 if self
._get
_obj
_size
(element
) > 64:
176 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(fname
))
177 elif type(element
) is pytsdl
.tsdl
.Enum
:
178 if self
._get
_obj
_size
(element
) > 64:
179 raise RuntimeError('enum field "{}" larger than 64-bit'.format(fname
))
181 if type(element
) is pytsdl
.tsdl
.Array
:
182 # still an array, continue
183 element
= element
.element
185 # found the terminal element
187 elif type(ftype
) is pytsdl
.tsdl
.Variant
:
188 raise RuntimeError('field "{}" is a variant (unsupported)'.format(fname
))
189 elif type(ftype
) is pytsdl
.tsdl
.String
:
190 raise RuntimeError('field "{}" is a string (not allowed here)'.format(fname
))
191 elif type(ftype
) is pytsdl
.tsdl
.Struct
:
192 self
._validate
_struct
(ftype
)
193 elif type(ftype
) is pytsdl
.tsdl
.Integer
:
194 if self
._get
_obj
_size
(ftype
) > 64:
195 raise RuntimeError('integer field "{}" larger than 64-bit'.format(fname
))
196 elif type(ftype
) is pytsdl
.tsdl
.FloatingPoint
:
197 if self
._get
_obj
_size
(ftype
) > 64:
198 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(fname
))
199 elif type(ftype
) is pytsdl
.tsdl
.Enum
:
200 if self
._get
_obj
_size
(ftype
) > 64:
201 raise RuntimeError('enum field "{}" larger than 64-bit'.format(fname
))
203 def _validate_context_fields(self
, struct
):
204 if type(struct
) is not pytsdl
.tsdl
.Struct
:
205 raise RuntimeError('expecting a struct')
207 for fname
, ftype
in struct
.fields
.items():
208 if type(ftype
) is pytsdl
.tsdl
.Variant
:
209 raise RuntimeError('field "{}" is a variant (unsupported)'.format(fname
))
210 elif type(ftype
) is pytsdl
.tsdl
.Struct
:
211 # validate inner structure against barectf constraints
212 self
._validate
_struct
(ftype
)
214 def _validate_integer(self
, integer
, size
=None, align
=None,
216 if type(integer
) is not pytsdl
.tsdl
.Integer
:
217 raise RuntimeError('expected integer')
220 if integer
.size
!= size
:
221 raise RuntimeError('expected {}-bit integer'.format(size
))
223 if align
is not None:
224 if integer
.align
!= align
:
225 raise RuntimeError('expected integer with {}-bit alignment'.format(align
))
227 if signed
is not None:
228 if integer
.signed
!= signed
:
229 raise RuntimeError('expected {} integer'.format('signed' if signed
else 'unsigned'))
231 def _validate_packet_header(self
, packet_header
):
233 self
._validate
_struct
(packet_header
)
234 except RuntimeError as e
:
235 _perror('packet header: {}'.format(e
))
237 # magic must be the first field
238 if 'magic' in packet_header
.fields
:
239 if list(packet_header
.fields
.keys())[0] != 'magic':
240 _perror('packet header: "magic" must be the first field')
242 _perror('packet header: missing "magic" field')
244 # magic must be a 32-bit unsigned integer, 32-bit aligned
246 self
._validate
_integer
(packet_header
['magic'], 32, 32, False)
247 except RuntimeError as e
:
248 _perror('packet header: "magic": {}'.format(e
))
250 # mandatory stream_id
251 if 'stream_id' not in packet_header
.fields
:
252 _perror('packet header: missing "stream_id" field')
254 # stream_id must be an unsigned integer
256 self
._validate
_integer
(packet_header
['stream_id'], signed
=False)
257 except RuntimeError as e
:
258 _perror('packet header: "stream_id": {}'.format(e
))
260 # only magic and stream_id allowed
261 if len(packet_header
.fields
) != 2:
262 _perror('packet header: only "magic" and "stream_id" fields are allowed')
264 def _dot_name_to_str(self
, name
):
265 return '.'.join(name
)
267 def _compare_integers(self
, int1
, int2
):
268 if type(int1
) is not pytsdl
.tsdl
.Integer
:
271 if type(int2
) is not pytsdl
.tsdl
.Integer
:
274 size
= int1
.size
== int2
.size
275 align
= int1
.align
== int2
.align
276 cmap
= int1
.map == int2
.map
277 base
= int1
.base
== int2
.base
278 encoding
= int1
.encoding
== int2
.encoding
279 signed
= int1
.signed
== int2
.signed
280 comps
= (size
, align
, cmap
, base
, encoding
, signed
)
282 # True means 1 for sum()
283 return sum(comps
) == len(comps
)
285 def _validate_packet_context(self
, stream
):
286 packet_context
= stream
.packet_context
290 self
._validate
_struct
(packet_context
)
291 except RuntimeError as e
:
292 _perror('stream {}: packet context: {}'.format(sid
, e
))
294 fields
= packet_context
.fields
296 # if timestamp_begin exists, timestamp_end must exist
297 if 'timestamp_begin' in fields
or 'timestamp_end' in fields
:
298 if 'timestamp_begin' not in fields
or 'timestamp_end' not in fields
:
299 _perror('stream {}: packet context: "timestamp_begin" must exist if "timestamp_end" exists'.format(sid
))
301 # timestamp_begin and timestamp_end must have the same integer
302 # as the event header's timestamp field (should exist by now)
303 timestamp
= stream
.event_header
['timestamp']
305 if not self
._compare
_integers
(fields
['timestamp_begin'], timestamp
):
306 _perror('stream {}: packet context: "timestamp_begin": integer type different from event header\'s "timestamp" field'.format(sid
))
308 if not self
._compare
_integers
(fields
['timestamp_end'], timestamp
):
309 _perror('stream {}: packet context: "timestamp_end": integer type different from event header\'s "timestamp" field'.format(sid
))
311 # content_size must exist and be an unsigned integer
312 if 'content_size' not in fields
:
313 _perror('stream {}: packet context: missing "content_size" field'.format(sid
))
316 self
._validate
_integer
(fields
['content_size'], 32, 32, False)
319 self
._validate
_integer
(fields
['content_size'], 64, 64, False)
321 _perror('stream {}: packet context: "content_size": expecting unsigned 32-bit/64-bit integer'.format(sid
))
323 # packet_size must exist and be an unsigned integer
324 if 'packet_size' not in fields
:
325 _perror('stream {}: packet context: missing "packet_size" field'.format(sid
))
328 self
._validate
_integer
(fields
['packet_size'], 32, 32, False)
331 self
._validate
_integer
(fields
['packet_size'], 64, 64, False)
333 _perror('stream {}: packet context: "packet_size": expecting unsigned 32-bit/64-bit integer'.format(sid
))
335 # if cpu_id exists, must be an unsigned integer
336 if 'cpu_id' in fields
:
338 self
._validate
_integer
(fields
['cpu_id'], signed
=False)
339 except RuntimeError as e
:
340 _perror('stream {}: packet context: "cpu_id": {}'.format(sid
, e
))
342 def _validate_event_header(self
, stream
):
343 event_header
= stream
.event_header
347 self
._validate
_struct
(event_header
)
348 except RuntimeError as e
:
349 _perror('stream {}: event header: {}'.format(sid
, e
))
351 fields
= event_header
.fields
353 # id must exist and be an unsigned integer
354 if 'id' not in fields
:
355 _perror('stream {}: event header: missing "id" field'.format(sid
))
358 self
._validate
_integer
(fields
['id'], signed
=False)
359 except RuntimeError as e
:
360 _perror('stream {}: "id": {}'.format(sid
, format(e
)))
363 # timestamp must exist, be an unsigned integer and be mapped to a valid clock
364 if 'timestamp' not in fields
:
365 _perror('stream {}: event header: missing "timestamp" field'.format(sid
))
368 self
._validate
_integer
(fields
['timestamp'], signed
=False)
369 except RuntimeError as e
:
370 _perror('stream {}: event header: "timestamp": {}'.format(sid
, format(e
)))
372 if fields
['timestamp'].map is None:
373 _perror('stream {}: event header: "timestamp" must be mapped to a valid clock'.format(sid
))
375 # id must be the first field, followed by timestamp
376 if list(fields
.keys())[0] != 'id':
377 _perror('stream {}: event header: "id" must be the first field'.format(sid
))
379 if list(fields
.keys())[1] != 'timestamp':
380 _perror('stream {}: event header: "timestamp" must be the second field'.format(sid
))
382 # only id and timestamp and allowed in event header
384 _perror('stream {}: event header: only "id" and "timestamp" fields are allowed'.format(sid
))
386 def _validate_stream_event_context(self
, stream
):
387 stream_event_context
= stream
.event_context
390 if stream_event_context
is None:
394 self
._validate
_context
_fields
(stream_event_context
)
395 except RuntimeError as e
:
396 _perror('stream {}: event context: {}'.format(sid
, e
))
398 def _validate_event_context(self
, stream
, event
):
399 event_context
= event
.context
403 if event_context
is None:
407 self
._validate
_context
_fields
(event_context
)
408 except RuntimeError as e
:
409 _perror('stream {}: event {}: context: {}'.format(sid
, eid
, e
))
411 def _validate_event_fields(self
, stream
, event
):
412 event_fields
= event
.fields
417 self
._validate
_context
_fields
(event_fields
)
418 except RuntimeError as e
:
419 _perror('stream {}: event {}: fields: {}'.format(sid
, eid
, e
))
421 def _validate_all_scopes(self
):
423 self
._validate
_packet
_header
(self
._doc
.trace
.packet_header
)
426 for stream
in self
._doc
.streams
.values():
427 self
._validate
_event
_header
(stream
)
428 self
._validate
_packet
_context
(stream
)
429 self
._validate
_stream
_event
_context
(stream
)
432 for event
in stream
.events
:
433 self
._validate
_event
_context
(stream
, event
)
434 self
._validate
_event
_fields
(stream
, event
)
436 def _validate_metadata(self
):
437 self
._validate
_all
_scopes
()
446 def _get_alignment(self
, at
, align
):
447 return (at
+ align
- 1) & -align
449 # this converts a tree of offset variables:
464 # field_other_struct_field -> 16
465 # field_other_struct_yeah -> 20
468 def _flatten_offvars_tree(self
, offvars_tree
, prefix
=None,
471 offvars
= collections
.OrderedDict()
473 for name
, offset
in offvars_tree
.items():
474 if prefix
is not None:
475 varname
= '{}_{}'.format(prefix
, name
)
479 if isinstance(offset
, dict):
480 self
._flatten
_offvars
_tree
(offset
, varname
, offvars
)
482 offvars
[varname
] = offset
486 # returns the size of a struct with _static size_
487 def _get_struct_size(self
, struct
,
490 if offvars_tree
is None:
491 offvars_tree
= collections
.OrderedDict()
495 for fname
, ftype
in struct
.fields
.items():
496 field_alignment
= self
._get
_obj
_alignment
(ftype
)
497 offset
= self
._get
_alignment
(offset
, field_alignment
)
499 if type(ftype
) is pytsdl
.tsdl
.Struct
:
500 offvars_tree
[fname
] = collections
.OrderedDict()
501 sz
= self
._get
_struct
_size
(ftype
, offvars_tree
[fname
],
502 base_offset
+ offset
)
504 # only integers may act as sequence lengths
505 if type(ftype
) is pytsdl
.tsdl
.Integer
:
506 offvars_tree
[fname
] = base_offset
+ offset
508 sz
= self
._get
_obj
_size
(ftype
)
514 def _get_array_size(self
, array
):
515 element
= array
.element
517 # effective size of one element includes its alignment after its size
518 size
= self
._get
_obj
_size
(element
)
519 align
= self
._get
_obj
_alignment
(element
)
521 return self
._get
_alignment
(size
, align
) * array
.length
523 def _get_enum_size(self
, enum
):
524 return self
._get
_obj
_size
(enum
.integer
)
526 def _get_floating_point_size(self
, floating_point
):
527 return floating_point
.exp_dig
+ floating_point
.mant_dig
529 def _get_integer_size(self
, integer
):
532 def _get_obj_size(self
, obj
):
533 return self
._obj
_size
_cb
[type(obj
)](obj
)
535 def _get_struct_alignment(self
, struct
):
536 if struct
.align
is not None:
541 for fname
, ftype
in struct
.fields
.items():
542 cur_align
= max(self
._get
_obj
_alignment
(ftype
), cur_align
)
546 def _get_integer_alignment(self
, integer
):
549 def _get_floating_point_alignment(self
, floating_point
):
550 return floating_point
.align
552 def _get_enum_alignment(self
, enum
):
553 return self
._get
_obj
_alignment
(enum
.integer
)
555 def _get_string_alignment(self
, string
):
558 def _get_array_alignment(self
, array
):
559 return self
._get
_obj
_alignment
(array
.element
)
561 def _get_sequence_alignment(self
, sequence
):
562 return self
._get
_obj
_alignment
(sequence
.element
)
564 def _get_obj_alignment(self
, obj
):
565 return self
._obj
_alignment
_cb
[type(obj
)](obj
)
567 def _name_to_param_name(self
, prefix
, name
):
568 return 'param_{}_{}'.format(prefix
, name
)
570 def _ev_f_name_to_param_name(self
, name
):
571 return self
._name
_to
_param
_name
('evf', name
)
573 def _ev_c_name_to_param_name(self
, name
):
574 return self
._name
_to
_param
_name
('evc', name
)
576 def _ev_sec_name_to_param_name(self
, name
):
577 return self
._name
_to
_param
_name
('evsec', name
)
579 def _ev_h_name_to_param_name(self
, name
):
580 return self
._name
_to
_param
_name
('evh', name
)
582 def _s_pc_name_to_param_name(self
, name
):
583 return self
._name
_to
_param
_name
('spc', name
)
585 def _s_ph_name_to_param_name(self
, name
):
586 return self
._name
_to
_param
_name
('sph', name
)
588 def _get_integer_param_ctype(self
, integer
):
589 signed
= 'u' if not integer
.signed
else ''
591 if integer
.size
== 8:
593 elif integer
.size
== 16:
595 elif integer
.size
== 32:
597 elif integer
.size
== 64:
600 # if the integer is signed and of uncommon size, the sign bit is
601 # at a custom position anyway so we use a 64-bit unsigned
607 if integer
.size
< 16:
609 elif integer
.size
< 32:
611 elif integer
.size
< 64:
616 return '{}int{}_t'.format(signed
, sz
)
618 def _get_enum_param_ctype(self
, enum
):
619 return self
._get
_obj
_param
_ctype
(enum
.integer
)
621 def _get_floating_point_param_ctype(self
, fp
):
622 if fp
.exp_dig
== 8 and fp
.mant_dig
== 24 and fp
.align
== 32:
624 elif fp
.exp_dig
== 11 and fp
.mant_dig
== 53 and fp
.align
== 64:
629 def _get_obj_param_ctype(self
, obj
):
630 return self
._obj
_param
_ctype
_cb
[type(obj
)](obj
)
632 def _get_chk_offset_v(self
, size
):
633 fmt
= '{}_CHK_OFFSET_V({}, {}, {});'
634 ret
= fmt
.format(self
._prefix
.upper(), self
._CTX
_AT
,
635 self
._CTX
_BUF
_SIZE
, size
)
639 def _get_chk_offset_v_cline(self
, size
):
640 return _CLine(self
._get
_chk
_offset
_v
(size
))
642 def _get_align_offset(self
, align
):
643 fmt
= '{}_ALIGN_OFFSET({}, {});'
644 ret
= fmt
.format(self
._prefix
.upper(), self
._CTX
_AT
, align
)
648 def _get_align_offset_cline(self
, size
):
649 return _CLine(self
._get
_align
_offset
(size
))
651 def _write_field_struct(self
, fname
, src_name
, struct
):
652 size
= self
._get
_struct
_size
(struct
)
653 size_bytes
= self
._get
_alignment
(size
, 8) // 8
654 dst
= self
._CTX
_BUF
_AT
_ADDR
657 # memcpy() is safe since barectf requires inner structures
659 self
._get
_chk
_offset
_v
_cline
(size
),
660 _CLine('memcpy({}, {}, {});'.format(dst
, src_name
, size_bytes
)),
661 _CLine('{} += {};'.format(self
._CTX
_AT
, size
)),
664 def _write_field_integer(self
, fname
, src_name
, integer
):
665 bo
= self
._bo
_suffixes
_map
[integer
.byte_order
]
667 t
= self
._get
_obj
_param
_ctype
(integer
)
669 length
= self
._get
_obj
_size
(integer
)
670 fmt
= 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
673 self
._get
_chk
_offset
_v
_cline
(length
),
674 _CLine(fmt
.format(bo
, ptr
, t
, start
, length
, src_name
)),
675 _CLine('{} += {};'.format(self
._CTX
_AT
, length
))
678 def _write_field_enum(self
, fname
, src_name
, enum
):
679 return self
._write
_field
_obj
(fname
, src_name
, enum
.integer
)
681 def _write_field_floating_point(self
, fname
, src_name
, floating_point
):
682 bo
= self
._bo
_suffixes
_map
[floating_point
.byte_order
]
684 t
= self
._get
_obj
_param
_ctype
(floating_point
)
686 length
= self
._get
_obj
_size
(floating_point
)
687 fmt
= 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
690 self
._get
_chk
_offset
_v
_cline
(length
),
691 _CLine(fmt
.format(bo
, ptr
, t
, start
, length
, src_name
)),
692 _CLine('{} += {};'.format(self
._CTX
_AT
, length
))
695 def _write_field_array(self
, fname
, src_name
, array
):
698 # array index variable declaration
699 iv
= 'ia_{}'.format(fname
)
700 clines
.append(_CLine('uint32_t {};'.format(iv
)))
702 # for loop using array's static length
703 line
= 'for ({iv} = 0; {iv} < {l}; ++{iv}) {{'.format(iv
=iv
, l
=array
.length
)
704 clines
.append(_CLine(line
))
706 # for loop statements
707 for_block
= _CBlock()
709 # align bit index before writing to the buffer
710 element_align
= self
._get
_obj
_alignment
(array
.element
)
711 cline
= self
._get
_align
_offset
_cline
(element_align
)
712 for_block
.append(cline
)
714 # write element to the buffer
715 for_block
+= self
._write
_field
_obj
(fname
, src_name
, array
.element
)
716 clines
.append(for_block
)
719 clines
.append(_CLine('}'))
723 def _write_field_sequence(self
, fname
, src_name
, sequence
):
725 _CLine('would write sequence here;'),
728 def _write_field_string(self
, fname
, src_name
, string
):
731 # string index variable declaration
732 iv
= 'is_{}'.format(fname
)
733 clines
.append(_CLine('uint32_t {};'.format(iv
)))
735 # for loop; loop until the end of the source string is reached
736 fmt
= "for ({iv} = 0; {src}[{iv}] != '\\0'; ++{iv}, {ctxat} += 8) {{"
737 line
= fmt
.format(iv
=iv
, src
=src_name
, ctxat
=self
._CTX
_AT
)
738 clines
.append(_CLine(line
))
740 # for loop statements
741 for_block
= _CBlock()
743 # check offset overflow
744 for_block
.append(self
._get
_chk
_offset
_v
_cline
(8))
746 # write byte to the buffer
747 fmt
= '{dst} = {src}[{iv}];'
748 line
= fmt
.format(dst
=self
._CTX
_BUF
_AT
, iv
=iv
, src
=src_name
)
749 for_block
.append(_CLine(line
))
752 clines
.append(for_block
)
753 clines
.append(_CLine('}'))
755 # write NULL character to the buffer
756 clines
.append(_CLine("{} = '\\0';".format(self
._CTX
_BUF
_AT
)))
757 clines
.append(_CLine('{} += 8;'.format(self
._CTX
_AT
)))
761 def _write_field_obj(self
, fname
, src_name
, ftype
):
762 return self
._write
_field
_obj
_cb
[type(ftype
)](fname
, src_name
, ftype
)
764 def _get_offvar_name(self
, name
, prefix
=None):
767 if prefix
is not None:
772 return '_'.join(parts
)
774 def _get_offvar_name_from_expr(self
, expr
):
775 return self
._get
_offvar
_name
('_'.join(expr
))
777 def _field_to_clines(self
, fname
, ftype
, scope_name
, param_name_cb
):
779 pname
= param_name_cb(fname
)
780 align
= self
._get
_obj
_alignment
(ftype
)
783 fmt
= '/* write {}.{} ({}) */'
784 line
= fmt
.format(scope_name
, fname
,
785 self
._tsdl
_type
_names
_map
[type(ftype
)])
786 clines
.append(_CLine(line
))
788 # align bit index before writing to the buffer
789 cline
= self
._get
_align
_offset
_cline
(align
)
792 # write offset variables
793 if type(ftype
) is pytsdl
.tsdl
.Struct
:
794 offvars_tree
= collections
.OrderedDict()
795 self
._get
_struct
_size
(ftype
, offvars_tree
)
796 off_vars
= self
._flatten
_offvars
_tree
(offvars_tree
)
798 # as many offset as there are child fields because a future
799 # sequence could refer to any of those fields
800 for lname
, offset
in off_vars
.items():
801 offvar
= self
._get
_offvar
_name
('_'.join([fname
, lname
]))
802 fmt
= 'uint32_t {} = {} + {};'
803 line
= fmt
.format(offvar
, self
._CTX
_AT
, offset
);
804 clines
.append(_CLine(line
))
805 elif type(ftype
) is pytsdl
.tsdl
.Integer
:
806 # offset of this simple field is the current bit index
807 offvar
= self
._get
_offvar
_name
(fname
)
808 line
= 'uint32_t {} = {};'.format(offvar
, self
._CTX
_AT
)
809 clines
.append(_CLine(line
))
811 clines
+= self
._write
_field
_obj
(fname
, pname
, ftype
)
815 def _join_cline_groups(self
, cline_groups
):
819 output_clines
= cline_groups
[0]
821 for clines
in cline_groups
[1:]:
822 output_clines
.append('')
823 output_clines
+= clines
827 def _struct_to_clines(self
, struct
, scope_name
, param_name_cb
):
830 for fname
, ftype
in struct
.fields
.items():
831 clines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
833 cline_groups
.append(clines
)
835 return self
._join
_cline
_groups
(cline_groups
)
837 def _get_struct_size_offvars(self
, struct
):
838 offvars_tree
= collections
.OrderedDict()
839 size
= self
._get
_struct
_size
(struct
, offvars_tree
)
840 offvars
= self
._flatten
_offvars
_tree
(offvars_tree
)
844 def _get_ph_size_offvars(self
):
845 return self
._get
_struct
_size
_offvars
(self
._doc
.trace
.packet_header
)
847 def _get_pc_size_offvars(self
, stream
):
848 return self
._get
_struct
_size
_offvars
(stream
.packet_context
)
850 def _offvars_to_ctx_clines(self
, prefix
, offvars
):
853 for name
in offvars
.keys():
854 offvar
= self
._get
_offvar
_name
(name
, prefix
)
855 clines
.append(_CLine('uint32_t {};'.format(offvar
)))
859 def _gen_barectf_ctx_struct(self
, stream
, hide_sid
=False):
860 # get offset variables for both the packet header and packet context
861 ph_size
, ph_offvars
= self
._get
_ph
_size
_offvars
()
862 pc_size
, pc_offvars
= self
._get
_pc
_size
_offvars
(stream
)
863 clines
= self
._offvars
_to
_ctx
_clines
('ph', ph_offvars
)
864 clines
+= self
._offvars
_to
_ctx
_clines
('pc', pc_offvars
)
869 clines_indented
.append(_CLine('\t' + cline
))
872 clock_cb
= '\t/* (no clock callback) */'
874 if not self
._manual
_clock
:
875 ctype
= self
._get
_clock
_type
(stream
)
876 fmt
= '\t{} (*clock_cb)(void*),\n\tvoid* clock_cb_data;'
877 clock_cb
= fmt
.format(ctype
)
885 t
= barectf
.templates
.BARECTF_CTX
886 struct
= t
.format(prefix
=self
._prefix
, sid
=sid
,
887 ctx_fields
='\n'.join(clines_indented
),
892 def _gen_barectf_contexts_struct(self
):
895 if len(self
._doc
.streams
) == 1:
900 for stream
in self
._doc
.streams
.values():
901 struct
= self
._gen
_barectf
_ctx
_struct
(stream
, hide_sid
)
902 structs
.append(struct
)
904 return '\n\n'.join(structs
)
906 _packet_context_known_fields
= [
913 def _get_clock_type(self
, stream
):
914 return self
._get
_obj
_param
_ctype
(stream
.event_header
['timestamp'])
916 def _gen_manual_clock_param(self
, stream
):
917 return '{} param_clock'.format(self
._get
_clock
_type
(stream
))
919 def _gen_barectf_func_open_body(self
, stream
):
922 # update timestamp end if present
923 if self
._stream
_has
_timestamp
_begin
_end
(stream
):
924 # get clock value ASAP
925 clk_type
= self
._get
_clock
_type
(stream
)
926 clk
= self
._gen
_get
_clock
_value
()
927 line
= '{} clk_value = {};'.format(clk_type
, clk
)
928 clines
.append(_CLine(line
))
929 clines
.append(_CLine(''))
931 # packet context fields
933 scope_name
= 'stream.packet.context'
935 for fname
, ftype
in stream
.packet_context
.fields
.items():
937 if fname
== 'packet_size':
938 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
939 lambda x
: 'ctx->buffer_size')
940 fcline_groups
.append(fclines
)
942 # content size (skip)
943 elif fname
== 'content_size':
944 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
946 fcline_groups
.append(fclines
)
949 elif fname
== 'timestamp_begin':
950 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
951 lambda x
: 'clk_value')
952 fcline_groups
.append(fclines
)
954 # timestamp_end (skip)
955 elif fname
== 'timestamp_end':
956 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
958 fcline_groups
.append(fclines
)
962 fclines
= self
._field
_to
_clines
(fname
, ftype
, scope_name
,
963 self
._s
_pc
_name
_to
_param
_name
)
964 fcline_groups
.append(fclines
)
966 clines
+= self
._join
_cline
_groups
(fcline_groups
)
969 cblock
= _CBlock(clines
)
970 src
= self
._cblock
_to
_source
(cblock
)
974 def _gen_barectf_func_open(self
, stream
, gen_body
, hide_sid
=False):
978 if self
._manual
_clock
:
979 clock_param
= self
._gen
_manual
_clock
_param
(stream
)
980 params
.append(clock_param
)
983 for fname
, ftype
in stream
.packet_context
.fields
.items():
984 if fname
in self
._packet
_context
_known
_fields
:
987 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
988 pname
= self
._s
_pc
_name
_to
_param
_name
(fname
)
989 param
= '{} {}'.format(ptype
, pname
)
995 params_str
= ',\n\t'.join([''] + params
)
1003 t
= barectf
.templates
.FUNC_OPEN
1004 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1009 func
+= self
._gen
_barectf
_func
_open
_body
(stream
)
1016 def _gen_barectf_func_init_body(self
, stream
):
1019 line
= 'uint32_t ctx_at_bkup;'
1020 clines
.append(_CLine(line
))
1022 # set context parameters
1023 clines
.append(_CLine(''))
1024 clines
.append(_CLine("/* barectf context parameters */"))
1025 clines
.append(_CLine('ctx->buf = buf;'))
1026 clines
.append(_CLine('ctx->buf_size = buf_size * 8;'))
1027 clines
.append(_CLine('{} = 0;'.format(self
._CTX
_AT
)))
1029 if not self
._manual
_clock
:
1030 clines
.append(_CLine('ctx->clock_cb = clock_cb;'))
1031 clines
.append(_CLine('ctx->clock_cb_data = clock_cb_data;'))
1033 # set context offsets
1034 clines
.append(_CLine(''))
1035 clines
.append(_CLine("/* barectf context offsets */"))
1036 ph_size
, ph_offvars
= self
._get
_ph
_size
_offvars
()
1037 pc_size
, pc_offvars
= self
._get
_pc
_size
_offvars
(stream
)
1038 pc_alignment
= self
._get
_obj
_alignment
(stream
.packet_context
)
1039 pc_offset
= self
._get
_alignment
(ph_size
, pc_alignment
)
1041 for offvar
, offset
in ph_offvars
.items():
1042 offvar_field
= self
._get
_offvar
_name
(offvar
, 'ph')
1043 line
= 'ctx->{} = {};'.format(offvar_field
, offset
)
1044 clines
.append(_CLine(line
))
1046 for offvar
, offset
in pc_offvars
.items():
1047 offvar_field
= self
._get
_offvar
_name
(offvar
, 'pc')
1048 line
= 'ctx->{} = {};'.format(offvar_field
, pc_offset
+ offset
)
1049 clines
.append(_CLine(line
))
1051 clines
.append(_CLine(''))
1053 # packet header fields
1056 for fname
, ftype
in self
._doc
.trace
.packet_header
.fields
.items():
1058 if fname
== 'magic':
1059 fclines
= self
._field
_to
_clines
(fname
, ftype
, 'packet.header',
1060 lambda x
: '0xc1fc1fc1UL')
1061 fcline_groups
.append(fclines
)
1064 elif fname
== 'stream_id':
1065 fclines
= self
._field
_to
_clines
(fname
, ftype
, 'packet.header',
1066 lambda x
: str(stream
.id))
1067 fcline_groups
.append(fclines
)
1069 clines
+= self
._join
_cline
_groups
(fcline_groups
)
1072 cblock
= _CBlock(clines
)
1073 src
= self
._cblock
_to
_source
(cblock
)
1077 def _gen_barectf_func_init(self
, stream
, gen_body
, hide_sid
=False):
1086 if not self
._manual
_clock
:
1087 ts_ftype
= stream
.event_header
['timestamp']
1088 ts_ptype
= self
._get
_obj
_param
_ctype
(ts_ftype
)
1089 fmt
= ',\n\t{} (*clock_cb)(void*),\n\tvoid* clock_cb_data'
1090 params
= fmt
.format(ts_ptype
)
1092 t
= barectf
.templates
.FUNC_INIT
1093 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1098 func
+= self
._gen
_barectf
_func
_init
_body
(stream
)
1105 def _gen_get_clock_value(self
):
1106 if self
._manual
_clock
:
1107 return 'param_clock'
1109 return self
._CTX
_CALL
_CLOCK
_CB
1111 def _stream_has_timestamp_begin_end(self
, stream
):
1112 return self
._has
_timestamp
_begin
_end
[stream
.id]
1114 def _gen_write_ctx_field_integer(self
, src_name
, prefix
, name
, obj
):
1117 # save buffer position
1118 line
= 'ctx_at_bkup = {};'.format(self
._CTX
_AT
)
1119 clines
.append(_CLine(line
))
1121 # go back to field offset
1122 offvar
= self
._get
_offvar
_name
(name
, prefix
)
1123 line
= '{} = ctx->{};'.format(self
._CTX
_AT
, offvar
)
1124 clines
.append(_CLine(line
))
1127 clines
+= self
._write
_field
_integer
(None, src_name
, obj
)
1129 # restore buffer position
1130 line
= '{} = ctx_at_bkup;'.format(self
._CTX
_AT
)
1131 clines
.append(_CLine(line
))
1135 def _gen_barectf_func_close_body(self
, stream
):
1138 line
= 'uint32_t ctx_at_bkup;'
1139 clines
.append(_CLine(line
))
1141 # update timestamp end if present
1142 if self
._stream
_has
_timestamp
_begin
_end
(stream
):
1143 clines
.append(_CLine(''))
1144 clines
.append(_CLine("/* update packet context's timestamp_end */"))
1146 # get clock value ASAP
1147 clk_type
= self
._get
_clock
_type
(stream
)
1148 clk
= self
._gen
_get
_clock
_value
()
1149 line
= '{} clk_value = {};'.format(clk_type
, clk
)
1150 clines
.append(_CLine(line
))
1152 # write timestamp_end
1153 timestamp_end_integer
= stream
.packet_context
['timestamp_end']
1154 clines
+= self
._gen
_write
_ctx
_field
_integer
('clk_value', 'pc',
1156 timestamp_end_integer
)
1158 # update content_size
1159 clines
.append(_CLine(''))
1160 clines
.append(_CLine("/* update packet context's content_size */"))
1161 content_size_integer
= stream
.packet_context
['content_size']
1162 clines
+= self
._gen
_write
_ctx
_field
_integer
('ctx_at_bkup', 'pc',
1164 content_size_integer
)
1167 cblock
= _CBlock(clines
)
1168 src
= self
._cblock
_to
_source
(cblock
)
1172 def _gen_barectf_func_close(self
, stream
, gen_body
, hide_sid
=False):
1181 if self
._manual
_clock
:
1182 clock_param
= self
._gen
_manual
_clock
_param
(stream
)
1183 params
= ',\n\t{}'.format(clock_param
)
1185 t
= barectf
.templates
.FUNC_CLOSE
1186 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1191 func
+= self
._gen
_barectf
_func
_close
_body
(stream
)
1198 def _gen_barectf_funcs_init(self
, gen_body
):
1201 if len(self
._doc
.streams
) == 1:
1206 for stream
in self
._doc
.streams
.values():
1207 funcs
.append(self
._gen
_barectf
_func
_init
(stream
, gen_body
,
1212 def _gen_barectf_funcs_open(self
, gen_body
):
1215 if len(self
._doc
.streams
) == 1:
1220 for stream
in self
._doc
.streams
.values():
1221 funcs
.append(self
._gen
_barectf
_func
_open
(stream
, gen_body
,
1226 def _gen_barectf_func_trace_event(self
, stream
, event
, gen_body
, hide_sid
):
1230 if self
._manual
_clock
:
1231 clock_param
= self
._gen
_manual
_clock
_param
(stream
)
1232 params
.append(clock_param
)
1234 # stream event context params
1235 if stream
.event_context
is not None:
1236 for fname
, ftype
in stream
.event_context
.fields
.items():
1237 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1238 pname
= self
._ev
_sec
_name
_to
_param
_name
(fname
)
1239 param
= '{} {}'.format(ptype
, pname
)
1240 params
.append(param
)
1242 # event context params
1243 if event
.context
is not None:
1244 for fname
, ftype
in event
.context
.fields
.items():
1245 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1246 pname
= self
._ev
_c
_name
_to
_param
_name
(fname
)
1247 param
= '{} {}'.format(ptype
, pname
)
1248 params
.append(param
)
1250 # event fields params
1251 if event
.fields
is not None:
1252 for fname
, ftype
in event
.fields
.fields
.items():
1253 ptype
= self
._get
_obj
_param
_ctype
(ftype
)
1254 pname
= self
._ev
_f
_name
_to
_param
_name
(fname
)
1255 param
= '{} {}'.format(ptype
, pname
)
1256 params
.append(param
)
1261 params_str
= ',\n\t'.join([''] + params
)
1269 t
= barectf
.templates
.FUNC_TRACE
1270 func
= t
.format(si
=self
._si
_str
, prefix
=self
._prefix
, sid
=sid
,
1271 evname
=event
.name
, params
=params_str
)
1275 #func += self._gen_barectf_func_open_body(stream)
1282 def _gen_barectf_funcs_trace_stream(self
, stream
, gen_body
, hide_sid
):
1285 for event
in stream
.events
:
1286 funcs
.append(self
._gen
_barectf
_func
_trace
_event
(stream
, event
,
1287 gen_body
, hide_sid
))
1291 def _gen_barectf_funcs_trace(self
, gen_body
):
1294 if len(self
._doc
.streams
) == 1:
1299 for stream
in self
._doc
.streams
.values():
1300 funcs
+= self
._gen
_barectf
_funcs
_trace
_stream
(stream
, gen_body
,
1305 def _gen_barectf_funcs_close(self
, gen_body
):
1308 if len(self
._doc
.streams
) == 1:
1313 for stream
in self
._doc
.streams
.values():
1314 funcs
.append(self
._gen
_barectf
_func
_close
(stream
, gen_body
,
1319 def _gen_barectf_header(self
):
1320 ctx_structs
= self
._gen
_barectf
_contexts
_struct
()
1321 init_funcs
= self
._gen
_barectf
_funcs
_init
(self
._static
_inline
)
1322 open_funcs
= self
._gen
_barectf
_funcs
_open
(self
._static
_inline
)
1323 close_funcs
= self
._gen
_barectf
_funcs
_close
(self
._static
_inline
)
1324 trace_funcs
= self
._gen
_barectf
_funcs
_trace
(self
._static
_inline
)
1325 functions
= init_funcs
+ open_funcs
+ close_funcs
+ trace_funcs
1326 functions_str
= '\n\n'.join(functions
)
1327 t
= barectf
.templates
.HEADER
1328 header
= t
.format(prefix
=self
._prefix
, ucprefix
=self
._prefix
.upper(),
1329 barectf_ctx
=ctx_structs
, functions
=functions_str
)
1333 def _cblock_to_source_lines(self
, cblock
, indent
=1):
1335 indentstr
= '\t' * indent
1338 if type(line
) is _CBlock
:
1339 src
+= self
._cblock
_to
_source
_lines
(line
, indent
+ 1)
1341 src
.append(indentstr
+ line
)
1345 def _cblock_to_source(self
, cblock
, indent
=1):
1346 lines
= self
._cblock
_to
_source
_lines
(cblock
, indent
)
1348 return '\n'.join(lines
)
1350 def _set_params(self
):
1351 self
._has
_timestamp
_begin
_end
= {}
1353 for stream
in self
._doc
.streams
.values():
1354 has
= 'timestamp_begin' in stream
.packet_context
.fields
1355 self
._has
_timestamp
_begin
_end
[stream
.id] = has
1357 def gen_barectf(self
, metadata
, output
, prefix
, static_inline
,
1359 self
._metadata
= metadata
1360 self
._output
= output
1361 self
._prefix
= prefix
1362 self
._static
_inline
= static_inline
1363 self
._manual
_clock
= manual_clock
1367 self
._si
_str
= 'static inline '
1369 # open CTF metadata file
1371 with
open(metadata
) as f
:
1372 self
._tsdl
= f
.read()
1374 _perror('cannot open/read CTF metadata file "{}"'.format(metadata
))
1376 # parse CTF metadata
1378 self
._doc
= self
._parser
.parse(self
._tsdl
)
1379 except pytsdl
.parser
.ParseError
as e
:
1380 _perror('parse error: {}'.format(e
))
1382 # validate CTF metadata against barectf constraints
1383 self
._validate
_metadata
()
1385 # set parameters for this generation
1389 print(self
._gen
_barectf
_header
())
1392 clines = self._struct_to_clines(self._doc.streams[0].get_event(0).fields,
1393 'stream event context',
1394 self._ev_f_name_to_param_name)
1395 source = self._cblock_to_source(clines)
1401 args
= _parse_args()
1402 generator
= BarectfCodeGenerator()
1403 generator
.gen_barectf(args
.metadata
, args
.output
, args
.prefix
,
1404 args
.static_inline
, args
.manual_clock
)