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
)
92 pytsdl
.tsdl
.ByteOrder
.BE
: 'be',
93 pytsdl
.tsdl
.ByteOrder
.LE
: 'le',
96 _tsdl_type_names_map
= {
97 pytsdl
.tsdl
.Integer
: 'integer',
98 pytsdl
.tsdl
.FloatingPoint
: 'floating point',
99 pytsdl
.tsdl
.Enum
: 'enumeration',
100 pytsdl
.tsdl
.String
: 'string',
101 pytsdl
.tsdl
.Array
: 'static array',
102 pytsdl
.tsdl
.Sequence
: 'dynamic array',
103 pytsdl
.tsdl
.Struct
: 'structure',
107 self
._parser
= pytsdl
.parser
.Parser()
108 self
._obj
_size
_cb
= {
109 pytsdl
.tsdl
.Struct
: self
._get
_struct
_size
,
110 pytsdl
.tsdl
.Integer
: self
._get
_integer
_size
,
111 pytsdl
.tsdl
.Enum
: self
._get
_enum
_size
,
112 pytsdl
.tsdl
.FloatingPoint
: self
._get
_floating
_point
_size
,
113 pytsdl
.tsdl
.Array
: self
._get
_array
_size
,
115 self
._obj
_alignment
_cb
= {
116 pytsdl
.tsdl
.Struct
: self
._get
_struct
_alignment
,
117 pytsdl
.tsdl
.Integer
: self
._get
_integer
_alignment
,
118 pytsdl
.tsdl
.Enum
: self
._get
_enum
_alignment
,
119 pytsdl
.tsdl
.FloatingPoint
: self
._get
_floating
_point
_alignment
,
120 pytsdl
.tsdl
.Array
: self
._get
_array
_alignment
,
121 pytsdl
.tsdl
.Sequence
: self
._get
_sequence
_alignment
,
122 pytsdl
.tsdl
.String
: self
._get
_string
_alignment
,
124 self
._obj
_param
_ctype
_cb
= {
125 pytsdl
.tsdl
.Struct
: lambda obj
: 'const void*',
126 pytsdl
.tsdl
.Integer
: self
._get
_integer
_param
_ctype
,
127 pytsdl
.tsdl
.Enum
: self
._get
_enum
_param
_ctype
,
128 pytsdl
.tsdl
.FloatingPoint
: self
._get
_floating
_point
_param
_ctype
,
129 pytsdl
.tsdl
.Array
: lambda obj
: 'const void*',
130 pytsdl
.tsdl
.Sequence
: lambda obj
: 'const void*',
131 pytsdl
.tsdl
.String
: lambda obj
: 'const char*',
133 self
._write
_field
_obj
_cb
= {
134 pytsdl
.tsdl
.Struct
: self
._write
_field
_struct
,
135 pytsdl
.tsdl
.Integer
: self
._write
_field
_integer
,
136 pytsdl
.tsdl
.Enum
: self
._write
_field
_enum
,
137 pytsdl
.tsdl
.FloatingPoint
: self
._write
_field
_floating
_point
,
138 pytsdl
.tsdl
.Array
: self
._write
_field
_array
,
139 pytsdl
.tsdl
.Sequence
: self
._write
_field
_sequence
,
140 pytsdl
.tsdl
.String
: self
._write
_field
_string
,
143 # TODO: prettify this function
144 def _validate_struct(self
, struct
):
145 # just in case we call this with the wrong type
146 if type(struct
) is not pytsdl
.tsdl
.Struct
:
147 raise RuntimeError('expecting a struct')
149 # make sure inner structures are at least byte-aligned
150 if self
._get
_obj
_alignment
(struct
) < 8:
151 raise RuntimeError('inner struct must be at least byte-aligned')
154 for fname
, ftype
in struct
.fields
.items():
155 if type(ftype
) is pytsdl
.tsdl
.Sequence
:
156 raise RuntimeError('field "{}" is a dynamic array (not allowed here)'.format(fname
))
157 elif type(ftype
) is pytsdl
.tsdl
.Array
:
158 # we need to check every element until we find a terminal one
159 element
= ftype
.element
162 if type(element
) is pytsdl
.tsdl
.Sequence
:
163 raise RuntimeError('field "{}" contains a dynamic array (not allowed here)'.format(fname
))
164 elif type(element
) is pytsdl
.tsdl
.Variant
:
165 raise RuntimeError('field "{}" contains a variant (unsupported)'.format(fname
))
166 elif type(element
) is pytsdl
.tsdl
.String
:
167 raise RuntimeError('field "{}" contains a string (not allowed here)'.format(fname
))
168 elif type(element
) is pytsdl
.tsdl
.Struct
:
169 _validate_struct(element
)
170 elif type(element
) is pytsdl
.tsdl
.Integer
:
171 if self
._get
_obj
_size
(element
) > 64:
172 raise RuntimeError('integer field "{}" larger than 64-bit'.format(fname
))
173 elif type(element
) is pytsdl
.tsdl
.FloatingPoint
:
174 if self
._get
_obj
_size
(element
) > 64:
175 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(fname
))
176 elif type(element
) is pytsdl
.tsdl
.Enum
:
177 if self
._get
_obj
_size
(element
) > 64:
178 raise RuntimeError('enum field "{}" larger than 64-bit'.format(fname
))
180 if type(element
) is pytsdl
.tsdl
.Array
:
181 # still an array, continue
182 element
= element
.element
184 # found the terminal element
186 elif type(ftype
) is pytsdl
.tsdl
.Variant
:
187 raise RuntimeError('field "{}" is a variant (unsupported)'.format(fname
))
188 elif type(ftype
) is pytsdl
.tsdl
.String
:
189 raise RuntimeError('field "{}" is a string (not allowed here)'.format(fname
))
190 elif type(ftype
) is pytsdl
.tsdl
.Struct
:
191 self
._validate
_struct
(ftype
)
192 elif type(ftype
) is pytsdl
.tsdl
.Integer
:
193 if self
._get
_obj
_size
(ftype
) > 64:
194 raise RuntimeError('integer field "{}" larger than 64-bit'.format(fname
))
195 elif type(ftype
) is pytsdl
.tsdl
.FloatingPoint
:
196 if self
._get
_obj
_size
(ftype
) > 64:
197 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(fname
))
198 elif type(ftype
) is pytsdl
.tsdl
.Enum
:
199 if self
._get
_obj
_size
(ftype
) > 64:
200 raise RuntimeError('enum field "{}" larger than 64-bit'.format(fname
))
202 def _validate_context_fields(self
, struct
):
203 if type(struct
) is not pytsdl
.tsdl
.Struct
:
204 raise RuntimeError('expecting a struct')
206 for fname
, ftype
in struct
.fields
.items():
207 if type(ftype
) is pytsdl
.tsdl
.Variant
:
208 raise RuntimeError('field "{}" is a variant (unsupported)'.format(fname
))
209 elif type(ftype
) is pytsdl
.tsdl
.Struct
:
210 # validate inner structure against barectf constraints
211 self
._validate
_struct
(ftype
)
213 def _validate_integer(self
, integer
, size
=None, align
=None,
215 if type(integer
) is not pytsdl
.tsdl
.Integer
:
216 raise RuntimeError('expected integer')
219 if integer
.size
!= size
:
220 raise RuntimeError('expected {}-bit integer'.format(size
))
222 if align
is not None:
223 if integer
.align
!= align
:
224 raise RuntimeError('expected integer with {}-bit alignment'.format(align
))
226 if signed
is not None:
227 if integer
.signed
!= signed
:
228 raise RuntimeError('expected {} integer'.format('signed' if signed
else 'unsigned'))
230 def _validate_packet_header(self
, packet_header
):
232 self
._validate
_struct
(packet_header
)
233 except RuntimeError as e
:
234 _perror('packet header: {}'.format(e
))
236 # magic must be the first field
237 if 'magic' in packet_header
.fields
:
238 if list(packet_header
.fields
.keys())[0] != 'magic':
239 _perror('packet header: "magic" must be the first field')
241 _perror('packet header: missing "magic" field')
243 # magic must be a 32-bit unsigned integer, 32-bit aligned
245 self
._validate
_integer
(packet_header
['magic'], 32, 32, False)
246 except RuntimeError as e
:
247 _perror('packet header: "magic": {}'.format(e
))
249 # mandatory stream_id
250 if 'stream_id' not in packet_header
.fields
:
251 _perror('packet header: missing "stream_id" field')
253 # stream_id must be an unsigned integer
255 self
._validate
_integer
(packet_header
['stream_id'], signed
=False)
256 except RuntimeError as e
:
257 _perror('packet header: "stream_id": {}'.format(e
))
259 def _dot_name_to_str(self
, name
):
260 return '.'.join(name
)
262 def _compare_integers(self
, int1
, int2
):
263 if type(int1
) is not pytsdl
.tsdl
.Integer
:
266 if type(int2
) is not pytsdl
.tsdl
.Integer
:
269 size
= int1
.size
== int2
.size
270 align
= int1
.align
== int2
.align
271 cmap
= int1
.map == int2
.map
272 base
= int1
.base
== int2
.base
273 encoding
= int1
.encoding
== int2
.encoding
274 signed
= int1
.signed
== int2
.signed
275 comps
= (size
, align
, cmap
, base
, encoding
, signed
)
277 # True means 1 for sum()
278 return sum(comps
) == len(comps
)
280 def _validate_packet_context(self
, stream
):
281 packet_context
= stream
.packet_context
285 self
._validate
_struct
(packet_context
)
286 except RuntimeError as e
:
287 _perror('stream {}: packet context: {}'.format(sid
, e
))
289 fields
= packet_context
.fields
291 # if timestamp_begin exists, timestamp_end must exist
292 if 'timestamp_begin' in fields
or 'timestamp_end' in fields
:
293 if 'timestamp_begin' not in fields
or 'timestamp_end' not in fields
:
294 _perror('stream {}: packet context: "timestamp_begin" must exist if "timestamp_end" exists'.format(sid
))
296 # timestamp_begin and timestamp_end must have the same integer
297 # as the event header's timestamp field (should exist by now)
298 timestamp
= stream
.event_header
['timestamp']
300 if not self
._compare
_integers
(fields
['timestamp_begin'], timestamp
):
301 _perror('stream {}: packet context: "timestamp_begin": integer type different from event header\'s "timestamp" field'.format(sid
))
303 if not self
._compare
_integers
(fields
['timestamp_end'], timestamp
):
304 _perror('stream {}: packet context: "timestamp_end": integer type different from event header\'s "timestamp" field'.format(sid
))
306 # content_size must exist and be an unsigned integer
307 if 'content_size' not in fields
:
308 _perror('stream {}: packet context: missing "content_size" field'.format(sid
))
311 self
._validate
_integer
(fields
['content_size'], 32, 32, False)
314 self
._validate
_integer
(fields
['content_size'], 64, 64, False)
316 _perror('stream {}: packet context: "content_size": expecting unsigned 32-bit/64-bit integer'.format(sid
))
318 # packet_size must exist and be an unsigned integer
319 if 'packet_size' not in fields
:
320 _perror('stream {}: packet context: missing "packet_size" field'.format(sid
))
323 self
._validate
_integer
(fields
['packet_size'], 32, 32, False)
326 self
._validate
_integer
(fields
['packet_size'], 64, 64, False)
328 _perror('stream {}: packet context: "packet_size": expecting unsigned 32-bit/64-bit integer'.format(sid
))
330 # if cpu_id exists, must be an unsigned integer
331 if 'cpu_id' in fields
:
333 self
._validate
_integer
(fields
['cpu_id'], signed
=False)
334 except RuntimeError as e
:
335 _perror('stream {}: packet context: "cpu_id": {}'.format(sid
, e
))
337 def _validate_event_header(self
, stream
):
338 event_header
= stream
.event_header
342 self
._validate
_struct
(event_header
)
343 except RuntimeError as e
:
344 _perror('stream {}: event header: {}'.format(sid
, e
))
346 fields
= event_header
.fields
348 # id must exist and be an unsigned integer
349 if 'id' not in fields
:
350 _perror('stream {}: event header: missing "id" field'.format(sid
))
353 self
._validate
_integer
(fields
['id'], signed
=False)
354 except RuntimeError as e
:
355 _perror('stream {}: "id": {}'.format(sid
, format(e
)))
358 # timestamp must exist, be an unsigned integer and be mapped to a valid clock
359 if 'timestamp' not in fields
:
360 _perror('stream {}: event header: missing "timestamp" field'.format(sid
))
363 self
._validate
_integer
(fields
['timestamp'], signed
=False)
364 except RuntimeError as e
:
365 _perror('stream {}: event header: "timestamp": {}'.format(sid
, format(e
)))
367 if fields
['timestamp'].map is None:
368 _perror('stream {}: event header: "timestamp" must be mapped to a valid clock'.format(sid
))
370 def _validate_stream_event_context(self
, stream
):
371 stream_event_context
= stream
.event_context
374 if stream_event_context
is None:
378 self
._validate
_context
_fields
(stream_event_context
)
379 except RuntimeError as e
:
380 _perror('stream {}: event context: {}'.format(sid
, e
))
382 def _validate_event_context(self
, stream
, event
):
383 event_context
= event
.context
387 if event_context
is None:
391 self
._validate
_context
_fields
(event_context
)
392 except RuntimeError as e
:
393 _perror('stream {}: event {}: context: {}'.format(sid
, eid
, e
))
395 def _validate_event_fields(self
, stream
, event
):
396 event_fields
= event
.fields
401 self
._validate
_context
_fields
(event_fields
)
402 except RuntimeError as e
:
403 _perror('stream {}: event {}: fields: {}'.format(sid
, eid
, e
))
405 def _validate_all_scopes(self
):
407 self
._validate
_packet
_header
(self
._doc
.trace
.packet_header
)
410 for stream
in self
._doc
.streams
.values():
411 self
._validate
_event
_header
(stream
)
412 self
._validate
_packet
_context
(stream
)
413 self
._validate
_stream
_event
_context
(stream
)
416 for event
in stream
.events
:
417 self
._validate
_event
_context
(stream
, event
)
418 self
._validate
_event
_fields
(stream
, event
)
420 def _validate_metadata(self
):
421 self
._validate
_all
_scopes
()
430 def _get_alignment(self
, at
, align
):
431 return (at
+ align
- 1) & -align
433 # this converts a tree of offset variables:
448 # field_other_struct_field -> 16
449 # field_other_struct_yeah -> 20
452 def _flatten_offvars_tree(self
, offvars_tree
, prefix
=None,
455 offvars
= collections
.OrderedDict()
457 for name
, offset
in offvars_tree
.items():
458 if prefix
is not None:
459 varname
= '{}_{}'.format(prefix
, name
)
463 if isinstance(offset
, dict):
464 self
._flatten
_offvars
_tree
(offset
, varname
, offvars
)
466 offvars
[varname
] = offset
470 # returns the size of a struct with _static size_
471 def _get_struct_size(self
, struct
,
474 if offvars_tree
is None:
475 offvars_tree
= collections
.OrderedDict()
479 for fname
, ftype
in struct
.fields
.items():
480 field_alignment
= self
._get
_obj
_alignment
(ftype
)
481 offset
= self
._get
_alignment
(offset
, field_alignment
)
483 if type(ftype
) is pytsdl
.tsdl
.Struct
:
484 offvars_tree
[fname
] = collections
.OrderedDict()
485 sz
= self
._get
_struct
_size
(ftype
, offvars_tree
[fname
],
486 base_offset
+ offset
)
488 # only integers may act as sequence lengths
489 if type(ftype
) is pytsdl
.tsdl
.Integer
:
490 offvars_tree
[fname
] = base_offset
+ offset
492 sz
= self
._get
_obj
_size
(ftype
)
498 def _get_array_size(self
, array
):
499 element
= array
.element
501 # effective size of one element includes its alignment after its size
502 size
= self
._get
_obj
_size
(element
)
503 align
= self
._get
_obj
_alignment
(element
)
505 return self
._get
_alignment
(size
, align
) * array
.length
507 def _get_enum_size(self
, enum
):
508 return self
._get
_obj
_size
(enum
.integer
)
510 def _get_floating_point_size(self
, floating_point
):
511 return floating_point
.exp_dig
+ floating_point
.mant_dig
513 def _get_integer_size(self
, integer
):
516 def _get_obj_size(self
, obj
):
517 return self
._obj
_size
_cb
[type(obj
)](obj
)
519 def _get_struct_alignment(self
, struct
):
520 if struct
.align
is not None:
525 for fname
, ftype
in struct
.fields
.items():
526 cur_align
= max(self
._get
_obj
_alignment
(ftype
), cur_align
)
530 def _get_integer_alignment(self
, integer
):
533 def _get_floating_point_alignment(self
, floating_point
):
534 return floating_point
.align
536 def _get_enum_alignment(self
, enum
):
537 return self
._get
_obj
_alignment
(enum
.integer
)
539 def _get_string_alignment(self
, string
):
542 def _get_array_alignment(self
, array
):
543 return self
._get
_obj
_alignment
(array
.element
)
545 def _get_sequence_alignment(self
, sequence
):
546 return self
._get
_obj
_alignment
(sequence
.element
)
548 def _get_obj_alignment(self
, obj
):
549 return self
._obj
_alignment
_cb
[type(obj
)](obj
)
551 def _name_to_param_name(self
, prefix
, name
):
552 return 'param_{}_{}'.format(prefix
, name
)
554 def _ev_f_name_to_param_name(self
, name
):
555 return self
._name
_to
_param
_name
('evf', name
)
557 def _ev_c_name_to_param_name(self
, name
):
558 return self
._name
_to
_param
_name
('evc', name
)
560 def _ev_sec_name_to_param_name(self
, name
):
561 return self
._name
_to
_param
_name
('evsec', name
)
563 def _ev_h_name_to_param_name(self
, name
):
564 return self
._name
_to
_param
_name
('evh', name
)
566 def _s_pc_name_to_param_name(self
, name
):
567 return self
._name
_to
_param
_name
('spc', name
)
569 def _s_ph_name_to_param_name(self
, name
):
570 return self
._name
_to
_param
_name
('sph', name
)
572 def _get_integer_param_ctype(self
, integer
):
573 signed
= 'u' if not integer
.signed
else ''
575 if integer
.size
== 8:
577 elif integer
.size
== 16:
579 elif integer
.size
== 32:
581 elif integer
.size
== 64:
584 # if the integer is signed and of uncommon size, the sign bit is
585 # at a custom position anyway so we use a 64-bit unsigned
591 if integer
.size
< 16:
593 elif integer
.size
< 32:
595 elif integer
.size
< 64:
600 return '{}int{}_t'.format(signed
, sz
)
602 def _get_enum_param_ctype(self
, enum
):
603 return self
._get
_obj
_param
_ctype
(enum
.integer
)
605 def _get_floating_point_param_ctype(self
, fp
):
606 if fp
.exp_dig
== 8 and fp
.mant_dig
== 24 and fp
.align
== 32:
608 elif fp
.exp_dig
== 11 and fp
.mant_dig
== 53 and fp
.align
== 64:
613 def _get_obj_param_ctype(self
, obj
):
614 return self
._obj
_param
_ctype
_cb
[type(obj
)](obj
)
616 def _get_chk_offset_v(self
, size
):
617 fmt
= '{}_CHK_OFFSET_V({}, {}, {});'
618 ret
= fmt
.format(self
._prefix
.upper(), self
._CTX
_AT
,
619 self
._CTX
_BUF
_SIZE
, size
)
623 def _get_chk_offset_v_cline(self
, size
):
624 return _CLine(self
._get
_chk
_offset
_v
(size
))
626 def _get_align_offset(self
, align
):
627 fmt
= '{}_ALIGN_OFFSET({}, {});'
628 ret
= fmt
.format(self
._prefix
.upper(), self
._CTX
_AT
, align
)
632 def _get_align_offset_cline(self
, size
):
633 return _CLine(self
._get
_align
_offset
(size
))
635 def _write_field_struct(self
, fname
, src_name
, struct
):
636 size
= self
._get
_struct
_size
(struct
)
637 size_bytes
= self
._get
_alignment
(size
, 8) // 8
638 dst
= self
._CTX
_BUF
_AT
_ADDR
641 # memcpy() is safe since barectf requires inner structures
643 self
._get
_chk
_offset
_v
_cline
(size
),
644 _CLine('memcpy({}, {}, {});'.format(dst
, src_name
, size_bytes
)),
645 _CLine('{} += {};'.format(self
._CTX
_AT
, size
)),
648 def _write_field_integer(self
, fname
, src_name
, integer
):
649 bo
= self
._bo
_suffixes
_map
[integer
.byte_order
]
651 t
= self
._get
_obj
_param
_ctype
(integer
)
653 length
= self
._get
_obj
_size
(integer
)
654 fmt
= 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
657 self
._get
_chk
_offset
_v
_cline
(length
),
658 _CLine(fmt
.format(bo
, ptr
, t
, start
, length
, src_name
)),
659 _CLine('{} += {};'.format(self
._CTX
_AT
, length
))
662 def _write_field_enum(self
, fname
, src_name
, enum
):
663 return self
._write
_field
_obj
(fname
, src_name
, enum
.integer
)
665 def _write_field_floating_point(self
, fname
, src_name
, floating_point
):
666 bo
= self
._bo
_suffixes
_map
[floating_point
.byte_order
]
668 t
= self
._get
_obj
_param
_ctype
(floating_point
)
670 length
= self
._get
_obj
_size
(floating_point
)
671 fmt
= 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
674 self
._get
_chk
_offset
_v
_cline
(length
),
675 _CLine(fmt
.format(bo
, ptr
, t
, start
, length
, src_name
)),
676 _CLine('{} += {};'.format(self
._CTX
_AT
, length
))
679 def _write_field_array(self
, fname
, src_name
, array
):
682 # array index variable declaration
683 iv
= 'ia_{}'.format(fname
)
684 clines
.append(_CLine('uint32_t {};'.format(iv
)))
686 # for loop using array's static length
687 line
= 'for ({iv} = 0; {iv} < {l}; ++{iv}) {{'.format(iv
=iv
, l
=array
.length
)
688 clines
.append(_CLine(line
))
690 # for loop statements
691 for_block
= _CBlock()
693 # align bit index before writing to the buffer
694 element_align
= self
._get
_obj
_alignment
(array
.element
)
695 cline
= self
._get
_align
_offset
_cline
(element_align
)
696 for_block
.append(cline
)
698 # write element to the buffer
699 for_block
+= self
._write
_field
_obj
(fname
, src_name
, array
.element
)
700 clines
.append(for_block
)
703 clines
.append(_CLine('}'))
707 def _write_field_sequence(self
, fname
, src_name
, sequence
):
709 _CLine('would write sequence here;'),
712 def _write_field_string(self
, fname
, src_name
, string
):
715 # string index variable declaration
716 iv
= 'is_{}'.format(fname
)
717 clines
.append(_CLine('uint32_t {};'.format(iv
)))
719 # for loop; loop until the end of the source string is reached
720 fmt
= "for ({iv} = 0; {src}[{iv}] != '\\0'; ++{iv}, {ctxat} += 8) {{"
721 line
= fmt
.format(iv
=iv
, src
=src_name
, ctxat
=self
._CTX
_AT
)
722 clines
.append(_CLine(line
))
724 # for loop statements
725 for_block
= _CBlock()
727 # check offset overflow
728 for_block
.append(self
._get
_chk
_offset
_v
_cline
(8))
730 # write byte to the buffer
731 fmt
= '{dst} = {src}[{iv}];'
732 line
= fmt
.format(dst
=self
._CTX
_BUF
_AT
, iv
=iv
, src
=src_name
)
733 for_block
.append(_CLine(line
))
736 clines
.append(for_block
)
737 clines
.append(_CLine('}'))
739 # write NULL character to the buffer
740 clines
.append(_CLine("{} = '\\0';".format(self
._CTX
_BUF
_AT
)))
741 clines
.append(_CLine('{} += 8;'.format(self
._CTX
_AT
)))
745 def _write_field_obj(self
, fname
, src_name
, ftype
):
746 return self
._write
_field
_obj
_cb
[type(ftype
)](fname
, src_name
, ftype
)
748 def _get_offvar_name(self
, name
, prefix
=None):
751 if prefix
is not None:
756 return '_'.join(parts
)
758 def _get_offvar_name_from_expr(self
, expr
):
759 return self
._get
_offvar
_name
('_'.join(expr
))
761 def _field_to_cline(self
, fname
, ftype
, scope_name
, param_name_cb
):
763 pname
= param_name_cb(fname
)
764 align
= self
._get
_obj
_alignment
(ftype
)
767 fmt
= '/* write {} field "{}" ({}) */'
768 line
= fmt
.format(scope_name
, fname
,
769 self
._tsdl
_type
_names
_map
[type(ftype
)])
770 clines
.append(_CLine(line
))
772 # align bit index before writing to the buffer
773 cline
= self
._get
_align
_offset
_cline
(align
)
776 # write offset variables
777 if type(ftype
) is pytsdl
.tsdl
.Struct
:
778 offvars_tree
= collections
.OrderedDict()
779 self
._get
_struct
_size
(ftype
, offvars_tree
)
780 off_vars
= self
._flatten
_offvars
_tree
(offvars_tree
)
782 # as many offset as there are child fields because a future
783 # sequence could refer to any of those fields
784 for lname
, offset
in off_vars
.items():
785 offvar
= self
._get
_offvar
_name
('_'.join([fname
, lname
]))
786 fmt
= 'uint32_t {} = {} + {};'
787 line
= fmt
.format(offvar
, self
._CTX
_AT
, offset
);
788 clines
.append(_CLine(line
))
789 elif type(ftype
) is pytsdl
.tsdl
.Integer
:
790 # offset of this simple field is the current bit index
791 offvar
= self
._get
_offvar
_name
(fname
)
792 line
= 'uint32_t {} = {};'.format(offvar
, self
._CTX
_AT
)
793 clines
.append(_CLine(line
))
795 clines
+= self
._write
_field
_obj
(fname
, pname
, ftype
)
799 def _join_cline_groups(self
, cline_groups
):
803 output_clines
= cline_groups
[0]
805 for clines
in cline_groups
[1:]:
806 output_clines
.append('')
807 output_clines
+= clines
811 def _struct_to_clines(self
, struct
, scope_name
, param_name_cb
):
814 for fname
, ftype
in struct
.fields
.items():
815 clines
= self
._field
_to
_cline
(fname
, ftype
, scope_name
,
817 cline_groups
.append(clines
)
819 return self
._join
_cline
_groups
(cline_groups
)
821 def _get_struct_size_offvars(self
, struct
):
822 offvars_tree
= collections
.OrderedDict()
823 size
= self
._get
_struct
_size
(struct
, offvars_tree
)
824 offvars
= self
._flatten
_offvars
_tree
(offvars_tree
)
828 def _get_ph_size_offvars(self
):
829 return self
._get
_struct
_size
_offvars
(self
._doc
.trace
.packet_header
)
831 def _get_pc_size_offvars(self
, stream
):
832 return self
._get
_struct
_size
_offvars
(stream
.packet_context
)
834 def _offvars_to_ctx_clines(self
, prefix
, offvars
):
837 for name
in offvars
.keys():
838 offvar
= self
._get
_offvar
_name
(name
, prefix
)
839 clines
.append(_CLine('uint32_t {};'.format(offvar
)))
843 def _get_barectf_ctx_struct(self
, stream
, hide_sid
=False):
844 # get offset variables for both the packet header and packet context
845 ph_size
, ph_offvars
= self
._get
_ph
_size
_offvars
()
846 pc_size
, pc_offvars
= self
._get
_pc
_size
_offvars
(stream
)
847 clines
= self
._offvars
_to
_ctx
_clines
('ph', ph_offvars
)
848 clines
+= self
._offvars
_to
_ctx
_clines
('pc', pc_offvars
)
853 clines_indented
.append(_CLine('\t' + cline
))
861 t
= barectf
.templates
.BARECTF_CTX
862 struct
= t
.format(prefix
=self
._prefix
, sid
=sid
,
863 ctx_fields
='\n'.join(clines_indented
))
867 def _get_barectf_contexts_struct(self
):
870 if len(self
._doc
.streams
) == 1:
875 for stream
in self
._doc
.streams
.values():
876 struct
= self
._get
_barectf
_ctx
_struct
(stream
, hide_sid
)
877 structs
.append(struct
)
879 return '\n\n'.join(structs
)
881 def _get_barectf_header(self
):
882 ctx_structs
= self
._get
_barectf
_contexts
_struct
()
883 t
= barectf
.templates
.HEADER
885 header
= t
.format(prefix
=self
._prefix
, ucprefix
=self
._prefix
.upper(),
886 barectf_ctx
=ctx_structs
, functions
='')
890 def _cblock_to_source_lines(self
, cblock
, indent
=1):
892 indentstr
= '\t' * indent
895 if type(line
) is _CBlock
:
896 src
+= self
._cblock
_to
_source
_lines
(line
, indent
+ 1)
898 src
.append(indentstr
+ line
)
902 def _cblock_to_source(self
, cblock
, indent
=1):
903 lines
= self
._cblock
_to
_source
_lines
(cblock
, indent
)
905 return '\n'.join(lines
)
907 def gen_barectf(self
, metadata
, output
, prefix
, static_inline
,
909 self
._metadata
= metadata
910 self
._output
= output
911 self
._prefix
= prefix
912 self
._static
_inline
= static_inline
913 self
._manual
_clock
= manual_clock
915 # open CTF metadata file
917 with
open(metadata
) as f
:
918 self
._tsdl
= f
.read()
920 _perror('cannot open/read CTF metadata file "{}"'.format(metadata
))
924 self
._doc
= self
._parser
.parse(self
._tsdl
)
925 except pytsdl
.parser
.ParseError
as e
:
926 _perror('parse error: {}'.format(e
))
928 # validate CTF metadata against barectf constraints
929 self
._validate
_metadata
()
931 print(self
._get
_barectf
_header
())
934 clines = self._struct_to_clines(self._doc.streams[0].get_event(0).fields,
935 'stream event context',
936 self._ev_f_name_to_param_name)
937 source = self._cblock_to_source(clines)
944 generator
= BarectfCodeGenerator()
945 generator
.gen_barectf(args
.metadata
, args
.output
, args
.prefix
,
946 args
.static_inline
, args
.manual_clock
)
This page took 0.090135 seconds and 5 git commands to generate.