1 # The MIT License (MIT)
3 # Copyright (c) 2014-2020 Philippe Proulx <pproulx@efficios.com>
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 import barectf
.template
as barectf_template
25 import barectf
.config
as barectf_config
30 # A tuple containing serialization and size computation function
31 # templates for a given operation.
32 _OpTemplates
= collections
.namedtuple('_OpTemplates', ['serialize', 'size'])
35 # Base class of any operation within source code.
39 # * An offset at which to start to write within the current byte.
43 # * A list of names which, when joined with `_`, form the generic C
44 # source variable name.
46 # * Serialization and size computation templates to generate the
47 # operation's source code for those functions.
49 def __init__(self
, offset_in_byte
, ft
, names
, templates
):
50 assert(offset_in_byte
>= 0 and offset_in_byte
< 8)
51 self
._offset
_in
_byte
= offset_in_byte
53 self
._names
= copy
.copy(names
)
54 self
._templates
= templates
57 def offset_in_byte(self
):
58 return self
._offset
_in
_byte
70 return self
._names
[-1]
72 def _render_template(self
, templ
, **kwargs
):
73 return templ
.render(op
=self
, root_ft_prefixes
=_RootFtPrefixes
,
74 root_ft_prefix_names
=_ROOT_FT_PREFIX_NAMES
, **kwargs
)
76 def serialize_str(self
, **kwargs
):
77 return self
._render
_template
(self
._templates
.serialize
, **kwargs
)
79 def size_str(self
, **kwargs
):
80 return self
._render
_template
(self
._templates
.size
, **kwargs
)
83 # An "align" operation.
85 def __init__(self
, offset_in_byte
, ft
, names
, templates
, value
):
86 super().__init
__(offset_in_byte
, ft
, names
, templates
)
94 # A "write" operation.
99 # A builder of a chain of operations.
101 # Such a builder is closely connected to a `_CodeGen` object using it to
102 # find generic templates.
104 # Call append_root_ft() to make an operation builder append operations
105 # to itself for each member, recursively, of the structure field type.
107 # Get an operation builder's operations with its `ops` property.
109 def __init__(self
, cg
):
110 self
._last
_alignment
= None
111 self
._last
_bit
_array
_size
= None
114 self
._offset
_in
_byte
= 0
121 # Creates and appends the operations for the members, recursively,
122 # of the root structure field type `ft` named `name`.
124 # `spec_serialize_write_templates` is a mapping of first level
125 # member names to specialized serialization "write" templates.
126 def append_root_ft(self
, ft
, name
, spec_serialize_write_templates
=None):
130 if spec_serialize_write_templates
is None:
131 spec_serialize_write_templates
= {}
133 assert type(ft
) is barectf_config
.StructureFieldType
134 assert len(self
._names
) == 0
135 self
._append
_ft
(ft
, name
, spec_serialize_write_templates
)
137 # Creates and appends the operations of a given field type `ft`
140 # See append_root_ft() for `spec_serialize_write_templates`.
141 def _append_ft(self
, ft
, name
, spec_serialize_write_templates
):
143 return self
._names
[-1]
145 # Appends a "write" operation for the field type `ft`.
147 # This function considers `spec_serialize_write_templates` to
148 # override generic templates.
149 def append_write_op(ft
):
150 assert type(ft
) is not barectf_config
.StructureFieldType
151 offset_in_byte
= self
._offset
_in
_byte
153 if isinstance(ft
, barectf_config
._BitArrayFieldType
):
154 self
._offset
_in
_byte
+= ft
.size
155 self
._offset
_in
_byte
%= 8
157 serialize_write_templ
= None
159 if len(self
._names
) == 2:
160 serialize_write_templ
= spec_serialize_write_templates
.get(top_name())
162 if serialize_write_templ
is None:
163 if isinstance(ft
, barectf_config
._IntegerFieldType
):
164 serialize_write_templ
= self
._cg
._serialize
_write
_int
_statements
_templ
165 elif type(ft
) is barectf_config
.RealFieldType
:
166 serialize_write_templ
= self
._cg
._serialize
_write
_real
_statements
_templ
168 assert type(ft
) is barectf_config
.StringFieldType
169 serialize_write_templ
= self
._cg
._serialize
_write
_string
_statements
_templ
171 size_write_templ
= None
173 if isinstance(ft
, barectf_config
._BitArrayFieldType
):
174 size_write_templ
= self
._cg
._size
_write
_bit
_array
_statements
_templ
175 elif type(ft
) is barectf_config
.StringFieldType
:
176 size_write_templ
= self
._cg
._size
_write
_string
_statements
_templ
178 self
._ops
.append(_WriteOp(offset_in_byte
, ft
, self
._names
,
179 _OpTemplates(serialize_write_templ
, size_write_templ
)))
181 # Creates and appends an "align" operation for the field type
184 # This function updates the builder's state.
185 def try_append_align_op(alignment
, do_align
, ft
):
186 def align(v
, alignment
):
187 return (v
+ (alignment
- 1)) & -alignment
189 offset_in_byte
= self
._offset
_in
_byte
190 self
._offset
_in
_byte
= align(self
._offset
_in
_byte
, alignment
) % 8
192 if do_align
and alignment
> 1:
193 self
._ops
.append(_AlignOp(offset_in_byte
, ft
, self
._names
,
194 _OpTemplates(self
._cg
._serialize
_align
_statements
_templ
,
195 self
._cg
._size
_align
_statements
_templ
),
198 # Returns whether or not, considering the alignment requirement
199 # `align_req` and the builder's current state, we must create
200 # and append an "align" operation.
201 def must_align(align_req
):
202 return self
._last
_alignment
!= align_req
or self
._last
_bit
_array
_size
% align_req
!= 0
204 # push field type's name to the builder's name stack initially
205 self
._names
.append(name
)
207 if isinstance(ft
, (barectf_config
.StringFieldType
, barectf_config
._ArrayFieldType
)):
208 assert type(ft
) is barectf_config
.StringFieldType
or top_name() == 'uuid'
210 # strings and arrays are always byte-aligned
211 do_align
= must_align(8)
212 self
._last
_alignment
= 8
213 self
._last
_bit
_array
_size
= 8
214 try_append_align_op(8, do_align
, ft
)
217 do_align
= must_align(ft
.alignment
)
218 self
._last
_alignment
= ft
.alignment
220 if type(ft
) is barectf_config
.StructureFieldType
:
221 self
._last
_bit
_array
_size
= ft
.alignment
223 self
._last
_bit
_array
_size
= ft
.size
225 try_append_align_op(ft
.alignment
, do_align
, ft
)
227 if type(ft
) is barectf_config
.StructureFieldType
:
228 for member_name
, member
in ft
.members
.items():
229 self
._append
_ft
(member
.field_type
, member_name
, spec_serialize_write_templates
)
233 # exiting for this field type: pop its name
237 # The operations for an event.
239 # The available operations are:
241 # * Specific context operations.
242 # * Payload operations.
244 def __init__(self
, spec_ctx_ops
, payload_ops
):
245 self
._spec
_ctx
_ops
= copy
.copy(spec_ctx_ops
)
246 self
._payload
_ops
= copy
.copy(payload_ops
)
249 def spec_ctx_ops(self
):
250 return self
._spec
_ctx
_ops
253 def payload_ops(self
):
254 return self
._payload
_ops
257 # The operations for a stream.
259 # The available operations are:
261 # * Packet header operations.
262 # * Packet context operations.
263 # * Event header operations.
264 # * Event common context operations.
265 # * Event operations (`_EventOps`).
267 def __init__(self
, pkt_header_ops
, pkt_ctx_ops
, ev_header_ops
,
268 ev_common_ctx_ops
, ev_ops
):
269 self
._pkt
_header
_ops
= copy
.copy(pkt_header_ops
)
270 self
._pkt
_ctx
_ops
= copy
.copy(pkt_ctx_ops
)
271 self
._ev
_header
_ops
= copy
.copy(ev_header_ops
)
272 self
._ev
_common
_ctx
_ops
= copy
.copy(ev_common_ctx_ops
)
273 self
._ev
_ops
= copy
.copy(ev_ops
)
276 def pkt_header_ops(self
):
277 return self
._pkt
_header
_ops
280 def pkt_ctx_ops(self
):
281 return self
._pkt
_ctx
_ops
284 def ev_header_ops(self
):
285 return self
._ev
_header
_ops
288 def ev_common_ctx_ops(self
):
289 return self
._ev
_common
_ctx
_ops
296 # The C variable name prefixes for the six kinds of root field types.
297 class _RootFtPrefixes
:
306 # The human-readable names of the `_RootFtPrefixes` members.
307 _ROOT_FT_PREFIX_NAMES
= {
308 _RootFtPrefixes
.PH
: 'packet header',
309 _RootFtPrefixes
.PC
: 'packet context',
310 _RootFtPrefixes
.EH
: 'event header',
311 _RootFtPrefixes
.ECC
: 'event common context',
312 _RootFtPrefixes
.SC
: 'specific context',
313 _RootFtPrefixes
.P
: 'payload',
317 # A named function parameter for a given field type.
318 _FtParam
= collections
.namedtuple('_FtParam', ['ft', 'name'])
321 # A C code generator.
323 # Such a code generator can generate:
325 # * The bitfield header (gen_bitfield_header()).
326 # * The public header (gen_header()).
327 # * The source code (gen_src()).
329 def __init__(self
, cfg
):
331 self
._iden
_prefix
= cfg
.options
.code_generation_options
.identifier_prefix
332 self
._saved
_serialization
_ops
= {}
333 self
._templ
_filters
= {
334 'ft_c_type': self
._ft
_c
_type
,
335 'open_func_params_str': self
._open
_func
_params
_str
,
336 'trace_func_params_str': self
._trace
_func
_params
_str
,
337 'serialize_ev_common_ctx_func_params_str': self
._serialize
_ev
_common
_ctx
_func
_params
_str
,
339 self
._func
_proto
_params
_templ
= self
._create
_template
('func-proto-params.j2')
340 self
._serialize
_align
_statements
_templ
= self
._create
_template
('serialize-align-statements.j2')
341 self
._serialize
_write
_int
_statements
_templ
= self
._create
_template
('serialize-write-int-statements.j2')
342 self
._serialize
_write
_real
_statements
_templ
= self
._create
_template
('serialize-write-real-statements.j2')
343 self
._serialize
_write
_string
_statements
_templ
= self
._create
_template
('serialize-write-string-statements.j2')
344 self
._serialize
_write
_magic
_statements
_templ
= self
._create
_template
('serialize-write-magic-statements.j2')
345 self
._serialize
_write
_uuid
_statements
_templ
= self
._create
_template
('serialize-write-uuid-statements.j2')
346 self
._serialize
_write
_stream
_type
_id
_statements
_templ
= self
._create
_template
('serialize-write-stream-type-id-statements.j2')
347 self
._serialize
_write
_time
_statements
_templ
= self
._create
_template
('serialize-write-time-statements.j2')
348 self
._serialize
_write
_packet
_size
_statements
_templ
= self
._create
_template
('serialize-write-packet-size-statements.j2')
349 self
._serialize
_write
_skip
_save
_statements
_templ
= self
._create
_template
('serialize-write-skip-save-statements.j2')
350 self
._serialize
_write
_ev
_type
_id
_statements
_templ
= self
._create
_template
('serialize-write-ev-type-id-statements.j2')
351 self
._size
_align
_statements
_templ
= self
._create
_template
('size-align-statements.j2')
352 self
._size
_write
_bit
_array
_statements
_templ
= self
._create
_template
('size-write-bit-array-statements.j2')
353 self
._size
_write
_string
_statements
_templ
= self
._create
_template
('size-write-string-statements.j2')
355 # Creates and returns a template named `name` which is a file
356 # template if `is_file_template` is `True`.
358 # `name` is the file name, including the `.j2` extension, within the
361 # Such a template has the filters custom filters
362 # `self._templ_filters`.
363 def _create_template_base(self
, name
: str, is_file_template
: bool):
364 return barectf_template
._Template
(f
'c/{name}', is_file_template
, self
._cfg
,
367 # Creates and returns a non-file template named `name`.
369 # See _create_template_base() for `name`.
370 def _create_template(self
, name
: str) -> barectf_template
._Template
:
371 return self
._create
_template
_base
(name
, False)
373 # Creates and returns a file template named `name`.
375 # See _create_template_base() for `name`.
376 def _create_file_template(self
, name
: str) -> barectf_template
._Template
:
377 return self
._create
_template
_base
(name
, True)
379 # Trace type of this code generator's barectf configuration.
381 def _trace_type(self
):
382 return self
._cfg
.trace
.type
384 # Returns the C type for the field type `ft`, returning a `const` C
385 # type if `is_const` is `True`.
386 def _ft_c_type(self
, ft
, is_const
=False):
387 const_beg_str
= 'const '
389 if isinstance(ft
, barectf_config
._IntegerFieldType
):
390 sign_prefix
= 'u' if isinstance(ft
, barectf_config
.UnsignedIntegerFieldType
) else ''
402 return f
'{const_beg_str if is_const else ""}{sign_prefix}int{sz}_t'
403 elif type(ft
) is barectf_config
.RealFieldType
:
404 if ft
.size
== 32 and ft
.alignment
== 32:
406 elif ft
.size
== 64 and ft
.alignment
== 64:
411 return f
'{const_beg_str if is_const else ""}{c_type}'
413 assert type(ft
) is barectf_config
.StringFieldType
414 return f
'const char *{" const" if is_const else ""}'
416 # Returns the function prototype parameters for the members of the
417 # root structure field type `root_ft`.
419 # Each parameter has the prefix `name_prefix` followed with `_`.
421 # Members of which the name is in `exclude_set` are excluded.
422 def _proto_params_str(self
, root_ft
, name_prefix
, const_params
, exclude_set
=None,
427 if exclude_set
is None:
432 for member_name
, member
in root_ft
.members
.items():
433 if member_name
in exclude_set
:
436 if only_dyn
and not member
.field_type
.size_is_dynamic
:
439 params
.append(_FtParam(member
.field_type
, member_name
))
441 return self
._func
_proto
_params
_templ
.render(params
=params
, prefix
=name_prefix
,
442 const_params
=const_params
)
444 # Returns the packet opening function prototype parameters for the
445 # stream type `stream_type`.
446 def _open_func_params_str(self
, stream_type
, const_params
):
448 parts
.append(self
._proto
_params
_str
(self
._trace
_type
._pkt
_header
_ft
, _RootFtPrefixes
.PH
,
449 const_params
, {'magic', 'stream_id', 'uuid'}))
458 parts
.append(self
._proto
_params
_str
(stream_type
._pkt
_ctx
_ft
, _RootFtPrefixes
.PC
,
459 const_params
, exclude_set
))
460 return ''.join(parts
)
462 # Returns the tracing function prototype parameters for the stream
463 # and event types `stream_ev_types`.
464 def _trace_func_params_str(self
, stream_ev_types
, const_params
, only_dyn
=False):
465 stream_type
= stream_ev_types
[0]
466 ev_type
= stream_ev_types
[1]
469 if stream_type
._ev
_header
_ft
is not None:
470 parts
.append(self
._proto
_params
_str
(stream_type
._ev
_header
_ft
, _RootFtPrefixes
.EH
,
471 const_params
, {'id', 'timestamp'},
474 if stream_type
.event_common_context_field_type
is not None:
475 parts
.append(self
._proto
_params
_str
(stream_type
.event_common_context_field_type
,
476 _RootFtPrefixes
.ECC
, const_params
,
479 if ev_type
.specific_context_field_type
is not None:
480 parts
.append(self
._proto
_params
_str
(ev_type
.specific_context_field_type
,
481 _RootFtPrefixes
.SC
, const_params
,
484 if ev_type
.payload_field_type
is not None:
485 parts
.append(self
._proto
_params
_str
(ev_type
.payload_field_type
, _RootFtPrefixes
.P
,
486 const_params
, only_dyn
=only_dyn
))
488 return ''.join(parts
)
490 # Returns the event header serialization function prototype
491 # parameters for the stream type `stream_type`.
492 def _serialize_ev_common_ctx_func_params_str(self
, stream_type
, const_params
):
493 return self
._proto
_params
_str
(stream_type
.event_common_context_field_type
,
494 _RootFtPrefixes
.ECC
, const_params
);
496 # Generates the bitfield header file contents.
497 def gen_bitfield_header(self
):
498 return self
._create
_file
_template
('bitfield.h.j2').render()
500 # Generates the public header file contents.
501 def gen_header(self
):
502 return self
._create
_file
_template
('barectf.h.j2').render(root_ft_prefixes
=_RootFtPrefixes
)
504 # Generates the source code file contents.
505 def gen_src(self
, header_file_name
, bitfield_header_file_name
):
506 # Creates and returns the operations for all the stream and for
508 def create_stream_ops():
511 for stream_type
in self
._trace
_type
.stream_types
:
512 pkt_header_ser_ops
= []
513 builder
= _OpsBuilder(self
)
514 pkt_header_ft
= self
._trace
_type
._pkt
_header
_ft
516 # packet header serialization operations
517 if pkt_header_ft
is not None:
518 spec_serialize_write_templates
= {
519 'magic': self
._serialize
_write
_magic
_statements
_templ
,
520 'uuid': self
._serialize
_write
_uuid
_statements
_templ
,
521 'stream_id': self
._serialize
_write
_stream
_type
_id
_statements
_templ
,
523 builder
.append_root_ft(pkt_header_ft
, _RootFtPrefixes
.PH
,
524 spec_serialize_write_templates
)
525 pkt_header_ser_ops
= copy
.copy(builder
.ops
)
527 # packet context serialization operations
528 first_op_index
= len(builder
.ops
)
529 spec_serialize_write_templates
= {
530 'timestamp_begin': self
._serialize
_write
_time
_statements
_templ
,
531 'packet_size': self
._serialize
_write
_packet
_size
_statements
_templ
,
532 'timestamp_end': self
._serialize
_write
_skip
_save
_statements
_templ
,
533 'events_discarded': self
._serialize
_write
_skip
_save
_statements
_templ
,
534 'content_size': self
._serialize
_write
_skip
_save
_statements
_templ
,
536 builder
.append_root_ft(stream_type
._pkt
_ctx
_ft
, _RootFtPrefixes
.PC
,
537 spec_serialize_write_templates
)
538 pkt_ctx_ser_ops
= copy
.copy(builder
.ops
[first_op_index
:])
540 # event header serialization operations
541 builder
= _OpsBuilder(self
)
542 ev_header_ser_ops
= []
544 if stream_type
._ev
_header
_ft
is not None:
545 spec_serialize_write_templates
= {
546 'timestamp': self
._serialize
_write
_time
_statements
_templ
,
547 'id': self
._serialize
_write
_ev
_type
_id
_statements
_templ
,
549 builder
.append_root_ft(stream_type
._ev
_header
_ft
, _RootFtPrefixes
.EH
,
550 spec_serialize_write_templates
)
551 ev_header_ser_ops
= copy
.copy(builder
.ops
)
553 # event common context serialization operations
554 ev_common_ctx_ser_ops
= []
556 if stream_type
.event_common_context_field_type
is not None:
557 first_op_index
= len(builder
.ops
)
558 builder
.append_root_ft(stream_type
.event_common_context_field_type
,
560 ev_common_ctx_ser_ops
= copy
.copy(builder
.ops
[first_op_index
:])
562 # serialization operations specific to each event type
565 for ev_type
in stream_type
.event_types
:
566 ev_builder
= copy
.copy(builder
)
568 # specific context serialization operations
569 spec_ctx_ser_ops
= []
571 if ev_type
.specific_context_field_type
is not None:
572 first_op_index
= len(ev_builder
.ops
)
573 ev_builder
.append_root_ft(ev_type
.specific_context_field_type
,
575 spec_ctx_ser_ops
= copy
.copy(ev_builder
.ops
[first_op_index
:])
577 # payload serialization operations
580 if ev_type
.payload_field_type
is not None:
581 first_op_index
= len(ev_builder
.ops
)
582 ev_builder
.append_root_ft(ev_type
.payload_field_type
, _RootFtPrefixes
.P
)
583 payload_ser_ops
= copy
.copy(ev_builder
.ops
[first_op_index
:])
585 ev_ser_ops
[ev_type
] = _EventOps(spec_ctx_ser_ops
, payload_ser_ops
)
587 stream_ser_ops
[stream_type
] = _StreamOps(pkt_header_ser_ops
, pkt_ctx_ser_ops
,
588 ev_header_ser_ops
, ev_common_ctx_ser_ops
,
591 return stream_ser_ops
593 # Returns the "write" operation for the packet context member
594 # named `member_name` within the stream type `stream_type`.
595 def stream_op_pkt_ctx_op(stream_type
, member_name
):
596 for op
in stream_ops
[stream_type
].pkt_ctx_ops
:
597 if op
.top_name
== member_name
and type(op
) is _WriteOp
:
600 stream_ops
= create_stream_ops()
601 return self
._create
_file
_template
('barectf.c.j2').render(header_file_name
=header_file_name
,
602 bitfield_header_file_name
=bitfield_header_file_name
,
603 root_ft_prefixes
=_RootFtPrefixes
,
604 root_ft_prefix_names
=_ROOT_FT_PREFIX_NAMES
,
605 stream_ops
=stream_ops
,
606 stream_op_pkt_ctx_op
=stream_op_pkt_ctx_op
)