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
32 def _perror(msg
, exit_code
=1):
33 cprint('Error: {}'.format(msg
), 'red', attrs
=['bold'], file=sys
.stderr
)
38 cprint(':: {}'.format(msg
), 'blue', attrs
=['bold'], file=sys
.stderr
)
42 ap
= argparse
.ArgumentParser()
44 ap
.add_argument('-O', '--output', metavar
='OUTPUT', action
='store',
46 help='output directory of C files')
47 ap
.add_argument('-p', '--prefix', metavar
='PREFIX', action
='store',
49 help='custom prefix for C function and structure names')
50 ap
.add_argument('-s', '--static-inline', action
='store_true',
51 help='generate static inline C functions')
52 ap
.add_argument('-c', '--manual-clock', action
='store_true',
53 help='do not use a clock callback: pass clock value to tracing functions')
54 ap
.add_argument('metadata', metavar
='METADATA', action
='store',
55 help='CTF metadata input file')
58 args
= ap
.parse_args()
60 # validate output directory
61 if not os
.path
.isdir(args
.output
):
62 _perror('"{}" is not an existing directory'.format(args
.output
))
65 if not re
.match(r
'^[a-zA-Z_][a-zA-Z0-9_]*$', args
.prefix
):
66 _perror('"{}" is not a valid C identifier'.format(args
.prefix
))
68 # validate that metadata file exists
69 if not os
.path
.isfile(args
.metadata
):
70 _perror('"{}" is not an existing file'.format(args
.metadata
))
83 class BarectfCodeGenerator
:
86 _CTX_BUF_SIZE
= 'ctx->buf_size'
87 _CTX_BUF_AT
= '{}[{} >> 3]'.format(_CTX_BUF
, _CTX_AT
)
88 _CTX_BUF_AT_ADDR
= '&{}'.format(_CTX_BUF_AT
)
91 pytsdl
.tsdl
.ByteOrder
.BE
: 'be',
92 pytsdl
.tsdl
.ByteOrder
.LE
: 'le',
95 _tsdl_type_names_map
= {
96 pytsdl
.tsdl
.Integer
: 'integer',
97 pytsdl
.tsdl
.FloatingPoint
: 'floating point',
98 pytsdl
.tsdl
.Enum
: 'enumeration',
99 pytsdl
.tsdl
.String
: 'string',
100 pytsdl
.tsdl
.Array
: 'static array',
101 pytsdl
.tsdl
.Sequence
: 'dynamic array',
102 pytsdl
.tsdl
.Struct
: 'structure',
106 self
._parser
= pytsdl
.parser
.Parser()
107 self
._obj
_size
_cb
= {
108 pytsdl
.tsdl
.Struct
: self
._get
_struct
_size
,
109 pytsdl
.tsdl
.Integer
: self
._get
_integer
_size
,
110 pytsdl
.tsdl
.Enum
: self
._get
_enum
_size
,
111 pytsdl
.tsdl
.FloatingPoint
: self
._get
_floating
_point
_size
,
112 pytsdl
.tsdl
.Array
: self
._get
_array
_size
,
114 self
._obj
_alignment
_cb
= {
115 pytsdl
.tsdl
.Struct
: self
._get
_struct
_alignment
,
116 pytsdl
.tsdl
.Integer
: self
._get
_integer
_alignment
,
117 pytsdl
.tsdl
.Enum
: self
._get
_enum
_alignment
,
118 pytsdl
.tsdl
.FloatingPoint
: self
._get
_floating
_point
_alignment
,
119 pytsdl
.tsdl
.Array
: self
._get
_array
_alignment
,
120 pytsdl
.tsdl
.Sequence
: self
._get
_sequence
_alignment
,
121 pytsdl
.tsdl
.String
: self
._get
_string
_alignment
,
123 self
._obj
_param
_ctype
_cb
= {
124 pytsdl
.tsdl
.Struct
: lambda obj
: 'const void*',
125 pytsdl
.tsdl
.Integer
: self
._get
_integer
_param
_ctype
,
126 pytsdl
.tsdl
.Enum
: self
._get
_enum
_param
_ctype
,
127 pytsdl
.tsdl
.FloatingPoint
: self
._get
_floating
_point
_param
_ctype
,
128 pytsdl
.tsdl
.Array
: lambda obj
: 'const void*',
129 pytsdl
.tsdl
.Sequence
: lambda obj
: 'const void*',
130 pytsdl
.tsdl
.String
: lambda obj
: 'const char*',
132 self
._write
_field
_obj
_cb
= {
133 pytsdl
.tsdl
.Struct
: self
._write
_field
_struct
,
134 pytsdl
.tsdl
.Integer
: self
._write
_field
_integer
,
135 pytsdl
.tsdl
.Enum
: self
._write
_field
_enum
,
136 pytsdl
.tsdl
.FloatingPoint
: self
._write
_field
_floating
_point
,
137 pytsdl
.tsdl
.Array
: self
._write
_field
_array
,
138 pytsdl
.tsdl
.Sequence
: self
._write
_field
_sequence
,
139 pytsdl
.tsdl
.String
: self
._write
_field
_string
,
142 # TODO: prettify this function
143 def _validate_struct(self
, struct
):
144 # just in case we call this with the wrong type
145 if type(struct
) is not pytsdl
.tsdl
.Struct
:
146 raise RuntimeError('expecting a struct')
148 # make sure inner structures are at least byte-aligned
149 if self
._get
_obj
_alignment
(struct
) < 8:
150 raise RuntimeError('inner struct must be at least byte-aligned')
153 for name
, ftype
in struct
.fields
.items():
154 if type(ftype
) is pytsdl
.tsdl
.Sequence
:
155 raise RuntimeError('field "{}" is a dynamic array (not allowed here)'.format(name
))
156 elif type(ftype
) is pytsdl
.tsdl
.Array
:
157 # we need to check every element until we find a terminal one
158 element
= ftype
.element
161 if type(element
) is pytsdl
.tsdl
.Sequence
:
162 raise RuntimeError('field "{}" contains a dynamic array (not allowed here)'.format(name
))
163 elif type(element
) is pytsdl
.tsdl
.Variant
:
164 raise RuntimeError('field "{}" contains a variant (unsupported)'.format(name
))
165 elif type(element
) is pytsdl
.tsdl
.String
:
166 raise RuntimeError('field "{}" contains a string (not allowed here)'.format(name
))
167 elif type(element
) is pytsdl
.tsdl
.Struct
:
168 _validate_struct(element
)
169 elif type(element
) is pytsdl
.tsdl
.Integer
:
170 if self
._get
_obj
_size
(element
) > 64:
171 raise RuntimeError('integer field "{}" larger than 64-bit'.format(name
))
172 elif type(element
) is pytsdl
.tsdl
.FloatingPoint
:
173 if self
._get
_obj
_size
(element
) > 64:
174 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(name
))
175 elif type(element
) is pytsdl
.tsdl
.Enum
:
176 if self
._get
_obj
_size
(element
) > 64:
177 raise RuntimeError('enum field "{}" larger than 64-bit'.format(name
))
179 if type(element
) is pytsdl
.tsdl
.Array
:
180 # still an array, continue
181 element
= element
.element
183 # found the terminal element
185 elif type(ftype
) is pytsdl
.tsdl
.Variant
:
186 raise RuntimeError('field "{}" is a variant (unsupported)'.format(name
))
187 elif type(ftype
) is pytsdl
.tsdl
.String
:
188 raise RuntimeError('field "{}" is a string (not allowed here)'.format(name
))
189 elif type(ftype
) is pytsdl
.tsdl
.Struct
:
190 _validate_struct(ftype
)
191 elif type(ftype
) is pytsdl
.tsdl
.Integer
:
192 if self
._get
_obj
_size
(ftype
) > 64:
193 raise RuntimeError('integer field "{}" larger than 64-bit'.format(name
))
194 elif type(ftype
) is pytsdl
.tsdl
.FloatingPoint
:
195 if self
._get
_obj
_size
(ftype
) > 64:
196 raise RuntimeError('floating point field "{}" larger than 64-bit'.format(name
))
197 elif type(ftype
) is pytsdl
.tsdl
.Enum
:
198 if self
._get
_obj
_size
(ftype
) > 64:
199 raise RuntimeError('enum field "{}" larger than 64-bit'.format(name
))
201 def _validate_context_field(self
, struct
):
202 if type(struct
) is not pytsdl
.tsdl
.Struct
:
203 raise RuntimeError('expecting a struct')
205 for name
, ftype
in struct
.fields
.items():
206 if type(ftype
) is pytsdl
.tsdl
.Variant
:
207 raise RuntimeError('field "{}" is a variant (unsupported)'.format(name
))
208 elif type(ftype
) is pytsdl
.tsdl
.Struct
:
209 # validate inner structure against barectf constraints
210 self
._validate
_struct
(ftype
)
212 def _validate_integer(self
, integer
, size
=None, align
=None,
214 if type(integer
) is not pytsdl
.tsdl
.Integer
:
215 raise RuntimeError('expected integer')
218 if integer
.size
!= size
:
219 raise RuntimeError('expected {}-bit integer'.format(size
))
221 if align
is not None:
222 if integer
.align
!= align
:
223 raise RuntimeError('expected integer with {}-bit alignment'.format(align
))
225 if signed
is not None:
226 if integer
.signed
!= signed
:
227 raise RuntimeError('expected {} integer'.format('signed' if signed
else 'unsigned'))
229 def _validate_packet_header(self
, packet_header
):
231 self
._validate
_struct
(packet_header
)
232 except RuntimeError as e
:
233 _perror('packet header: {}'.format(e
))
235 # magic must be the first field
236 if 'magic' in packet_header
.fields
:
237 if list(packet_header
.fields
.keys())[0] != 'magic':
238 _perror('packet header: "magic" must be the first field')
240 _perror('packet header: missing "magic" field')
242 # magic must be a 32-bit unsigned integer, 32-bit aligned
244 self
._validate
_integer
(packet_header
['magic'], 32, 32, False)
245 except RuntimeError as e
:
246 _perror('packet header: "magic": {}'.format(e
))
248 # mandatory stream_id
249 if 'stream_id' not in packet_header
.fields
:
250 _perror('packet header: missing "stream_id" field')
252 # stream_id must be an unsigned integer
254 self
._validate
_integer
(packet_header
['stream_id'], signed
=False)
255 except RuntimeError as e
:
256 _perror('packet header: "stream_id": {}'.format(e
))
258 def _dot_name_to_str(self
, name
):
259 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 {}: "timestamp": {}'.format(sid
, format(e
)))
367 def _validate_stream_event_context(self
, stream
):
368 stream_event_context
= stream
.event_context
371 if stream_event_context
is None:
375 self
._validate
_context
_field
(stream_event_context
)
376 except RuntimeError as e
:
377 _perror('stream {}: event context: {}'.format(sid
, e
))
379 def _validate_all_scopes(self
):
381 self
._validate
_packet
_header
(self
._doc
.trace
.packet_header
)
384 for stream_id
, stream
in self
._doc
.streams
.items():
385 self
._validate
_event
_header
(stream
)
386 self
._validate
_packet
_context
(stream
)
387 self
._validate
_stream
_event
_context
(stream
)
389 def _validate_metadata(self
):
390 self
._validate
_all
_scopes
()
399 def _get_alignment(self
, at
, align
):
400 return (at
+ align
- 1) & -align
402 # this converts a tree of offset variables:
417 # field_other_struct_field -> 16
418 # field_other_struct_yeah -> 20
421 def _offvars_tree_to_vars(self
, offvars_tree
, prefix
=None,
422 off_vars
=collections
.OrderedDict()):
423 for name
, offset
in offvars_tree
.items():
424 if prefix
is not None:
425 varname
= '{}_{}'.format(prefix
, name
)
429 if isinstance(offset
, dict):
430 self
._offvars
_tree
_to
_vars
(offset
, varname
, off_vars
)
432 off_vars
[varname
] = offset
436 # returns the size of a struct with _static size_
437 def _get_struct_size(self
, struct
,
438 offvars_tree
=collections
.OrderedDict(),
442 for fname
, ftype
in struct
.fields
.items():
443 field_alignment
= self
._get
_obj
_alignment
(ftype
)
444 offset
= self
._get
_alignment
(offset
, field_alignment
)
446 if type(ftype
) is pytsdl
.tsdl
.Struct
:
447 offvars_tree
[fname
] = collections
.OrderedDict()
448 sz
= self
._get
_struct
_size
(ftype
, offvars_tree
[fname
],
449 base_offset
+ offset
)
451 # only integers may act as sequence lengths
452 if type(ftype
) is pytsdl
.tsdl
.Integer
:
453 offvars_tree
[fname
] = base_offset
+ offset
455 sz
= self
._get
_obj
_size
(ftype
)
461 def _get_array_size(self
, array
):
462 element
= array
.element
464 # effective size of one element includes its alignment after its size
465 size
= self
._get
_obj
_size
(element
)
466 align
= self
._get
_obj
_alignment
(element
)
468 return self
._get
_alignment
(size
, align
) * array
.length
470 def _get_enum_size(self
, enum
):
471 return self
._get
_obj
_size
(enum
.integer
)
473 def _get_floating_point_size(self
, floating_point
):
474 return floating_point
.exp_dig
+ floating_point
.mant_dig
476 def _get_integer_size(self
, integer
):
479 def _get_obj_size(self
, obj
):
480 return self
._obj
_size
_cb
[type(obj
)](obj
)
482 def _get_struct_alignment(self
, struct
):
483 if struct
.align
is not None:
488 for fname
, ftype
in struct
.fields
.items():
489 cur_align
= max(self
._get
_obj
_alignment
(ftype
), cur_align
)
493 def _get_integer_alignment(self
, integer
):
496 def _get_floating_point_alignment(self
, floating_point
):
497 return floating_point
.align
499 def _get_enum_alignment(self
, enum
):
500 return self
._get
_obj
_alignment
(enum
.integer
)
502 def _get_string_alignment(self
, string
):
505 def _get_array_alignment(self
, array
):
506 return self
._get
_obj
_alignment
(array
.element
)
508 def _get_sequence_alignment(self
, sequence
):
509 return self
._get
_obj
_alignment
(sequence
.element
)
511 def _get_obj_alignment(self
, obj
):
512 return self
._obj
_alignment
_cb
[type(obj
)](obj
)
514 def _name_to_param_name(self
, prefix
, name
):
515 return 'param_{}_{}'.format(prefix
, name
)
517 def _ev_f_name_to_param_name(self
, name
):
518 return self
._name
_to
_param
_name
('evf', name
)
520 def _ev_c_name_to_param_name(self
, name
):
521 return self
._name
_to
_param
_name
('evc', name
)
523 def _ev_sec_name_to_param_name(self
, name
):
524 return self
._name
_to
_param
_name
('evsec', name
)
526 def _ev_h_name_to_param_name(self
, name
):
527 return self
._name
_to
_param
_name
('evh', name
)
529 def _s_pc_name_to_param_name(self
, name
):
530 return self
._name
_to
_param
_name
('spc', name
)
532 def _s_ph_name_to_param_name(self
, name
):
533 return self
._name
_to
_param
_name
('sph', name
)
535 def _get_integer_param_ctype(self
, integer
):
536 signed
= 'u' if not integer
.signed
else ''
538 if integer
.size
== 8:
540 elif integer
.size
== 16:
542 elif integer
.size
== 32:
544 elif integer
.size
== 64:
547 # if the integer is signed and of uncommon size, the sign bit is
548 # at a custom position anyway so we use a 64-bit unsigned
554 if integer
.size
< 16:
556 elif integer
.size
< 32:
558 elif integer
.size
< 64:
563 return '{}int{}_t'.format(signed
, sz
)
565 def _get_enum_param_ctype(self
, enum
):
566 return self
._get
_obj
_param
_ctype
(enum
.integer
)
568 def _get_floating_point_param_ctype(self
, fp
):
569 if fp
.exp_dig
== 8 and fp
.mant_dig
== 24 and fp
.align
== 32:
571 elif fp
.exp_dig
== 11 and fp
.mant_dig
== 53 and fp
.align
== 64:
576 def _get_obj_param_ctype(self
, obj
):
577 return self
._obj
_param
_ctype
_cb
[type(obj
)](obj
)
579 def _get_chk_offset_v(self
, size
):
580 fmt
= '{}_CHK_OFFSET_V({}, {}, {});'
581 ret
= fmt
.format(self
._prefix
.upper(), self
._CTX
_AT
,
582 self
._CTX
_BUF
_SIZE
, size
)
586 def _get_chk_offset_v_cline(self
, size
):
587 return _CLine(self
._get
_chk
_offset
_v
(size
))
589 def _get_align_offset(self
, align
):
590 fmt
= '{}_ALIGN_OFFSET({}, {});'
591 ret
= fmt
.format(self
._prefix
.upper(), self
._CTX
_AT
, align
)
595 def _get_align_offset_cline(self
, size
):
596 return _CLine(self
._get
_align
_offset
(size
))
598 def _write_field_struct(self
, fname
, src_name
, struct
):
599 size
= self
._get
_struct
_size
(struct
)
600 size_bytes
= self
._get
_alignment
(size
, 8) // 8
601 dst
= self
._CTX
_BUF
_AT
_ADDR
604 # memcpy() is safe since barectf requires inner structures
606 self
._get
_chk
_offset
_v
_cline
(size
),
607 _CLine('memcpy({}, {}, {});'.format(dst
, src_name
, size_bytes
)),
608 _CLine('{} += {};'.format(self
._CTX
_AT
, size
)),
611 def _write_field_integer(self
, fname
, src_name
, integer
):
612 bo
= self
._bo
_suffixes
_map
[integer
.byte_order
]
614 t
= self
._get
_obj
_param
_ctype
(integer
)
616 length
= self
._get
_obj
_size
(integer
)
617 fmt
= 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
620 self
._get
_chk
_offset
_v
_cline
(length
),
621 _CLine(fmt
.format(bo
, ptr
, t
, start
, length
, src_name
)),
622 _CLine('{} += {};'.format(self
._CTX
_AT
, length
))
625 def _write_field_enum(self
, fname
, src_name
, enum
):
626 return self
._write
_field
_obj
(fname
, src_name
, enum
.integer
)
628 def _write_field_floating_point(self
, fname
, src_name
, floating_point
):
629 bo
= self
._bo
_suffixes
_map
[floating_point
.byte_order
]
631 t
= self
._get
_obj
_param
_ctype
(floating_point
)
633 length
= self
._get
_obj
_size
(floating_point
)
634 fmt
= 'barectf_bitfield_write_{}({}, {}, {}, {}, {});'
637 self
._get
_chk
_offset
_v
_cline
(length
),
638 _CLine(fmt
.format(bo
, ptr
, t
, start
, length
, src_name
)),
639 _CLine('{} += {};'.format(self
._CTX
_AT
, length
))
642 def _write_field_array(self
, fname
, src_name
, array
):
645 # array index variable declaration
646 iv
= 'ia_{}'.format(fname
)
647 lines
.append(_CLine('uint32_t {};'.format(iv
)))
649 # for loop using array's static length
650 line
= 'for ({iv} = 0; {iv} < {l}; ++{iv}) {{'.format(iv
=iv
, l
=array
.length
)
651 lines
.append(_CLine(line
))
653 # for loop statements
654 for_block
= _CBlock()
656 # align bit index before writing to the buffer
657 element_align
= self
._get
_obj
_alignment
(array
.element
)
658 cline
= self
._get
_align
_offset
_cline
(element_align
)
659 for_block
.append(cline
)
661 # write element to the buffer
662 for_block
+= self
._write
_field
_obj
(fname
, src_name
, array
.element
)
663 lines
.append(for_block
)
666 lines
.append(_CLine('}'))
670 def _write_field_sequence(self
, fname
, src_name
, sequence
):
672 _CLine('would write sequence here;'),
675 def _write_field_string(self
, fname
, src_name
, string
):
678 # string index variable declaration
679 iv
= 'is_{}'.format(fname
)
680 lines
.append(_CLine('uint32_t {};'.format(iv
)))
682 # for loop; loop until the end of the source string is reached
683 fmt
= "for ({iv} = 0; {src}[{iv}] != '\\0'; ++{iv}, {ctxat} += 8) {{"
684 line
= fmt
.format(iv
=iv
, src
=src_name
, ctxat
=self
._CTX
_AT
)
685 lines
.append(_CLine(line
))
687 # for loop statements
688 for_block
= _CBlock()
690 # check offset overflow
691 for_block
.append(self
._get
_chk
_offset
_v
_cline
(8))
693 # write byte to the buffer
694 fmt
= '{dst} = {src}[{iv}];'
695 line
= fmt
.format(dst
=self
._CTX
_BUF
_AT
, iv
=iv
, src
=src_name
)
696 for_block
.append(_CLine(line
))
699 lines
.append(for_block
)
700 lines
.append(_CLine('}'))
702 # write NULL character to the buffer
703 lines
.append(_CLine("{} = '\\0';".format(self
._CTX
_BUF
_AT
)))
704 lines
.append(_CLine('{} += 8;'.format(self
._CTX
_AT
)))
708 def _write_field_obj(self
, fname
, src_name
, ftype
):
709 return self
._write
_field
_obj
_cb
[type(ftype
)](fname
, src_name
, ftype
)
711 def _get_offvar_name(self
, name
):
712 return 'off_{}'.format(name
)
714 def _get_offvar_name_from_expr(self
, expr
):
715 return self
._get
_offvar
_name
('_'.join(expr
))
717 def _field_to_cline(self
, fname
, ftype
, scope_name
, param_name_cb
):
719 pname
= param_name_cb(fname
)
720 align
= self
._get
_obj
_alignment
(ftype
)
723 fmt
= '/* write {} field "{}" ({}) */'
724 line
= fmt
.format(scope_name
, fname
,
725 self
._tsdl
_type
_names
_map
[type(ftype
)])
726 lines
.append(_CLine(line
))
728 # align bit index before writing to the buffer
729 cline
= self
._get
_align
_offset
_cline
(align
)
732 # write offset variables
733 if type(ftype
) is pytsdl
.tsdl
.Struct
:
734 offvars_tree
= collections
.OrderedDict()
735 self
._get
_struct
_size
(ftype
, offvars_tree
)
736 off_vars
= self
._offvars
_tree
_to
_vars
(offvars_tree
)
738 # as many offset as there are child fields because a future
739 # sequence could refer to any of those fields
740 for lname
, offset
in off_vars
.items():
742 offvar
= self
._get
_offvar
_name
('_'.join([fname
, lname
]))
743 fmt
= 'uint32_t {} = {} + {};'
744 line
= fmt
.format(offvar
, self
._CTX
_AT
, offset
);
745 lines
.append(_CLine(line
))
746 elif type(ftype
) is pytsdl
.tsdl
.Integer
:
747 # offset of this simple field is the current bit index
748 offvar
= self
._get
_offvar
_name
(fname
)
749 line
= 'uint32_t {} = {};'.format(offvar
, self
._CTX
_AT
)
750 lines
.append(_CLine(line
))
752 lines
+= self
._write
_field
_obj
(fname
, pname
, ftype
)
756 def _struct_to_clines(self
, struct
, scope_name
, param_name_cb
):
759 for fname
, ftype
in struct
.fields
.items():
760 lines
= self
._field
_to
_cline
(fname
, ftype
, scope_name
,
762 line_groups
.append(lines
)
767 output_lines
= line_groups
[0]
769 for lines
in line_groups
[1:]:
770 output_lines
.append('')
771 output_lines
+= lines
775 def _cblock_to_source_lines(self
, cblock
, indent
=1):
777 indentstr
= '\t' * indent
780 if type(line
) is _CBlock
:
781 src
+= self
._cblock
_to
_source
_lines
(line
, indent
+ 1)
783 src
.append(indentstr
+ line
)
787 def _cblock_to_source(self
, cblock
, indent
=1):
788 lines
= self
._cblock
_to
_source
_lines
(cblock
, indent
)
790 return '\n'.join(lines
)
792 def gen_barectf(self
, metadata
, output
, prefix
, static_inline
,
794 self
._metadata
= metadata
795 self
._output
= output
796 self
._prefix
= prefix
797 self
._static
_inline
= static_inline
798 self
._manual
_clock
= manual_clock
800 # open CTF metadata file
802 with
open(metadata
) as f
:
803 self
._tsdl
= f
.read()
805 _perror('cannot open/read CTF metadata file "{}"'.format(metadata
))
809 self
._doc
= self
._parser
.parse(self
._tsdl
)
810 except pytsdl
.parser
.ParseError
as e
:
811 _perror('parse error: {}'.format(e
))
813 # validate CTF metadata against barectf constraints
814 self
._validate
_metadata
()
816 clines
= self
._struct
_to
_clines
(self
._doc
.streams
[0].get_event(0).fields
,
817 'stream event context',
818 self
._ev
_f
_name
_to
_param
_name
)
819 source
= self
._cblock
_to
_source
(_CBlock(clines
))
824 generator
= BarectfCodeGenerator()
825 generator
.gen_barectf(args
.metadata
, args
.output
, args
.prefix
,
826 args
.static_inline
, args
.manual_clock
)
This page took 0.048271 seconds and 5 git commands to generate.