Rename "time" -> "timestamp" (terminology)
[barectf.git] / barectf / cgen.py
1 # The MIT License (MIT)
2 #
3 # Copyright (c) 2014-2020 Philippe Proulx <pproulx@efficios.com>
4 #
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:
12 #
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
15 #
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.
23
24 import barectf.template as barectf_template
25 import barectf.config as barectf_config
26 import collections
27 import copy
28 import re
29 from typing import List, Optional, Mapping, Callable, Any, Set, Tuple
30 import typing
31 from barectf.typing import Count, Alignment
32
33
34 # A tuple containing serialization and size computation function
35 # templates for a given operation.
36 _OpTemplates = collections.namedtuple('_OpTemplates', ['serialize', 'size'])
37
38
39 # Abstract base class of any operation within source code.
40 #
41 # Any operation has:
42 #
43 # * A field type.
44 #
45 # * A list of names which, when joined with `_`, form the generic
46 # C source variable name.
47 #
48 # * A level: how deep this operation is within the operation tree.
49 #
50 # * Serialization and size computation templates to generate the
51 # operation's source code for those functions.
52 class _Op:
53 def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count,
54 templates: _OpTemplates):
55 self._ft = ft
56 self._names = copy.copy(names)
57 self._level = level
58 self._templates = templates
59
60 @property
61 def ft(self) -> barectf_config._FieldType:
62 return self._ft
63
64 @property
65 def names(self) -> List[str]:
66 return self._names
67
68 @property
69 def level(self) -> Count:
70 return self._level
71
72 @property
73 def top_name(self) -> str:
74 return self._names[-1]
75
76 def _render_template(self, templ: barectf_template._Template, **kwargs) -> str:
77 return templ.render(op=self, root_ft_prefixes=_RootFtPrefixes,
78 root_ft_prefix_names=_ROOT_FT_PREFIX_NAMES, **kwargs)
79
80 def serialize_str(self, **kwargs) -> str:
81 return self._render_template(self._templates.serialize, **kwargs)
82
83 def size_str(self, **kwargs) -> str:
84 return self._render_template(self._templates.size, **kwargs)
85
86
87 # Compound operation.
88 #
89 # A compound operation contains a list of suboperations (leaf or
90 # compound).
91 #
92 # Get the suboperations of a compound operation with its `subops`
93 # property.
94 #
95 # The templates of a compound operation handles its suboperations.
96 class _CompoundOp(_Op):
97 def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count,
98 templates: _OpTemplates, subops: List[Any] = None):
99 super().__init__(ft, names, level, templates)
100 self._subops = subops
101
102 @property
103 def subops(self):
104 return self._subops
105
106
107 # Leaf operation (abstract class).
108 class _LeafOp(_Op):
109 pass
110
111
112 # An "align" operation.
113 class _AlignOp(_LeafOp):
114 def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count,
115 templates: _OpTemplates, value: Alignment):
116 super().__init__(ft, names, level, templates)
117 self._value = value
118
119 @property
120 def value(self) -> Alignment:
121 return self._value
122
123
124 # A "write" operation.
125 class _WriteOp(_LeafOp):
126 def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count,
127 templates: _OpTemplates, offset_in_byte: Optional[Count]):
128 super().__init__(ft, names, level, templates)
129 assert offset_in_byte is None or (offset_in_byte >= 0 and offset_in_byte < 8)
130 self._offset_in_byte = offset_in_byte
131
132 @property
133 def offset_in_byte(self) -> Optional[Count]:
134 return self._offset_in_byte
135
136
137 _SpecSerializeWriteTemplates = Mapping[str, barectf_template._Template]
138
139
140 # An operation builder.
141 #
142 # Such a builder is closely connected to a `_CodeGen` object using it to
143 # find generic templates.
144 #
145 # Call build_for_root_ft() to make an operation builder create a
146 # compound operation for a given root structure field type, recursively,
147 # and return it.
148 class _OpBuilder:
149 def __init__(self, cg: '_CodeGen'):
150 self._names: List[str] = []
151 self._level = Count(0)
152 self._offset_in_byte: Optional[Count] = None
153 self._cg = cg
154
155 # Whether or not we're within an array operation.
156 @property
157 def _in_array(self):
158 return self._level > 0
159
160 # Creates and returns an operation for the root structure field type
161 # `ft` named `name`.
162 #
163 # `spec_serialize_write_templates` is a mapping of first level
164 # member names to specialized serialization "write" templates.
165 def build_for_root_ft(self, ft: barectf_config.StructureFieldType, name: str,
166 spec_serialize_write_templates: Optional[_SpecSerializeWriteTemplates] = None) -> _CompoundOp:
167 assert ft is not None
168
169 if spec_serialize_write_templates is None:
170 spec_serialize_write_templates = {}
171
172 assert type(ft) is barectf_config.StructureFieldType
173 assert len(self._names) == 0
174 assert self._level == 0
175 ops = self._build_for_ft(ft, name, spec_serialize_write_templates)
176 assert len(ops) == 1
177 assert type(ops[0]) is _CompoundOp
178 return typing.cast(_CompoundOp, ops[0])
179
180 # Creates and returns the operation(s) for a given field type `ft`
181 # named `name`.
182 #
183 # See build_for_root_ft() for `spec_serialize_write_templates`.
184 def _build_for_ft(self, ft: barectf_config._FieldType, name: str,
185 spec_serialize_write_templates: _SpecSerializeWriteTemplates) -> List[_Op]:
186 def top_name() -> str:
187 return self._names[-1]
188
189 # Creates and returns a "write" operation for the field type
190 # `ft`.
191 #
192 # This function considers `spec_serialize_write_templates` to
193 # override generic templates.
194 def create_write_op(ft: barectf_config._FieldType) -> _WriteOp:
195 assert type(ft) is not barectf_config.StructureFieldType
196 offset_in_byte = self._offset_in_byte
197
198 if isinstance(ft, barectf_config._BitArrayFieldType) and self._offset_in_byte is not None:
199 self._offset_in_byte = Count((self._offset_in_byte + ft.size) % 8)
200
201 serialize_write_templ: Optional[barectf_template._Template] = None
202
203 if len(self._names) == 2:
204 serialize_write_templ = spec_serialize_write_templates.get(top_name())
205
206 if serialize_write_templ is None:
207 if isinstance(ft, barectf_config._IntegerFieldType):
208 serialize_write_templ = self._cg._serialize_write_int_statements_templ
209 elif type(ft) is barectf_config.RealFieldType:
210 serialize_write_templ = self._cg._serialize_write_real_statements_templ
211 else:
212 assert type(ft) is barectf_config.StringFieldType
213 serialize_write_templ = self._cg._serialize_write_string_statements_templ
214
215 size_write_templ = None
216
217 if isinstance(ft, barectf_config._BitArrayFieldType):
218 size_write_templ = self._cg._size_write_bit_array_statements_templ
219 elif type(ft) is barectf_config.StringFieldType:
220 size_write_templ = self._cg._size_write_string_statements_templ
221
222 return _WriteOp(ft, self._names, self._level,
223 _OpTemplates(serialize_write_templ, size_write_templ), offset_in_byte)
224
225 # Creates and returns an "align" operation for the field type
226 # `ft` if needed.
227 #
228 # This function updates the builder's state.
229 def try_create_align_op(alignment: Alignment, ft: barectf_config._FieldType) -> Optional[_AlignOp]:
230 def align(v: Count, alignment: Alignment) -> Count:
231 return Count((v + (alignment - 1)) & -alignment)
232
233 if self._offset_in_byte is None and alignment % 8 == 0:
234 self._offset_in_byte = Count(0)
235 else:
236 if self._in_array:
237 self._offset_in_byte = None
238 elif self._offset_in_byte is not None:
239 self._offset_in_byte = Count(align(self._offset_in_byte, alignment) % 8)
240
241 if alignment > 1:
242 return _AlignOp(ft, self._names, self._level,
243 _OpTemplates(self._cg._serialize_align_statements_templ,
244 self._cg._size_align_statements_templ),
245 alignment)
246
247 return None
248
249 # Returns whether or not `ft` is a compound field type.
250 def ft_is_compound(ft: barectf_config._FieldType) -> bool:
251 return isinstance(ft, (barectf_config.StructureFieldType, barectf_config.StaticArrayFieldType))
252
253 # push field type's name to the builder's name stack initially
254 self._names.append(name)
255
256 # operations to return
257 ops: List[_Op] = []
258
259 if type(ft) is barectf_config.StringFieldType or self._names == [_RootFtPrefixes.PH, 'uuid']:
260 # strings and UUID array are always byte-aligned
261 op = try_create_align_op(Alignment(8), ft)
262
263 if op is not None:
264 ops.append(op)
265
266 ops.append(create_write_op(ft))
267 else:
268 if ft_is_compound(ft):
269 self._offset_in_byte = None
270
271 init_align_op = try_create_align_op(ft.alignment, ft)
272 subops: List[_Op] = []
273
274 if type(ft) is barectf_config.StructureFieldType:
275 ft = typing.cast(barectf_config.StructureFieldType, ft)
276
277 if init_align_op is not None:
278 # Append structure field's alignment as a
279 # suboperation.
280 #
281 # This is not strictly needed (could be appended to
282 # `ops`), but the properties of `_DsOps` and
283 # `_ErOps` offer a single (structure field type)
284 # operation.
285 subops.append(init_align_op)
286
287 # append suboperations for each member
288 for member_name, member in ft.members.items():
289 subops += self._build_for_ft(member.field_type, member_name,
290 spec_serialize_write_templates)
291
292 # create structure field's compound operation
293 ops.append(_CompoundOp(ft, self._names, self._level,
294 _OpTemplates(self._cg._serialize_write_struct_statements_templ,
295 self._cg._size_write_struct_statements_templ),
296 subops))
297 elif isinstance(ft, barectf_config._ArrayFieldType):
298 ft = typing.cast(barectf_config._ArrayFieldType, ft)
299 assert ft.alignment == 1 or init_align_op is not None
300
301 if init_align_op is not None:
302 ops.append(init_align_op)
303
304 # append element's suboperations
305 self._level = Count(self._level + 1)
306 subops += self._build_for_ft(ft.element_field_type,
307 f'[{_loop_var_name(Count(self._level - 1))}]',
308 spec_serialize_write_templates)
309 self._level = Count(self._level - 1)
310
311 # select the right templates
312 if type(ft) is barectf_config.StaticArrayFieldType:
313 templates = _OpTemplates(self._cg._serialize_write_static_array_statements_templ,
314 self._cg._size_write_static_array_statements_templ)
315 else:
316 assert type(ft) is barectf_config.DynamicArrayFieldType
317 templates = _OpTemplates(self._cg._serialize_write_dynamic_array_statements_templ,
318 self._cg._size_write_dynamic_array_statements_templ)
319
320 # create array field's compound operation
321 ops.append(_CompoundOp(ft, self._names, self._level, templates, subops))
322 else:
323 # leaf field: align + write
324 if init_align_op is not None:
325 ops.append(init_align_op)
326
327 ops.append(create_write_op(ft))
328
329 # exiting for this field type: pop its name
330 del self._names[-1]
331
332 return ops
333
334
335 _OptCompoundOp = Optional[_CompoundOp]
336
337
338 # The operations for an event record.
339 #
340 # The available operations are:
341 #
342 # * Specific context operation.
343 # * Payload operation.
344 class _ErOps:
345 def __init__(self, spec_ctx_op: _OptCompoundOp, payload_op: _OptCompoundOp):
346 self._spec_ctx_op = spec_ctx_op
347 self._payload_op = payload_op
348
349 @property
350 def spec_ctx_op(self) -> _OptCompoundOp:
351 return self._spec_ctx_op
352
353 @property
354 def payload_op(self) -> _OptCompoundOp:
355 return self._payload_op
356
357
358 _ErOpsMap = Mapping[barectf_config.EventRecordType, _ErOps]
359
360
361 # The operations for a data stream.
362 #
363 # The available operations are:
364 #
365 # * Packet header operation.
366 # * Packet context operation.
367 # * Event record header operation.
368 # * Event record common context operation.
369 # * Event record operations (`_ErOps`).
370 class _DsOps:
371 def __init__(self, pkt_header_op: _OptCompoundOp, pkt_ctx_op: _CompoundOp,
372 er_header_op: _OptCompoundOp, er_common_ctx_op: _OptCompoundOp, er_ops: _ErOpsMap):
373 self._pkt_header_op = pkt_header_op
374 self._pkt_ctx_op = pkt_ctx_op
375 self._er_header_op = er_header_op
376 self._er_common_ctx_op = er_common_ctx_op
377 self._er_ops = er_ops
378
379 @property
380 def pkt_header_op(self) -> _OptCompoundOp:
381 return self._pkt_header_op
382
383 @property
384 def pkt_ctx_op(self) -> _CompoundOp:
385 return self._pkt_ctx_op
386
387 @property
388 def er_header_op(self) -> _OptCompoundOp:
389 return self._er_header_op
390
391 @property
392 def er_common_ctx_op(self) -> _OptCompoundOp:
393 return self._er_common_ctx_op
394
395 @property
396 def er_ops(self) -> _ErOpsMap:
397 return self._er_ops
398
399
400 # The C variable name prefixes for the six kinds of root field types.
401 class _RootFtPrefixes:
402 PH = 'ph'
403 PC = 'pc'
404 ERH = 'erh'
405 ERCC = 'ercc'
406 SC = 'sc'
407 P = 'p'
408
409
410 # The human-readable names of the `_RootFtPrefixes` members.
411 _ROOT_FT_PREFIX_NAMES = {
412 _RootFtPrefixes.PH: 'packet header',
413 _RootFtPrefixes.PC: 'packet context',
414 _RootFtPrefixes.ERH: 'event record header',
415 _RootFtPrefixes.ERCC: 'event record common context',
416 _RootFtPrefixes.SC: 'specific context',
417 _RootFtPrefixes.P: 'payload',
418 }
419
420
421 # A named function parameter for a given field type.
422 _FtParam = collections.namedtuple('_FtParam', ['ft', 'name'])
423
424
425 # C type abstract base class.
426 class _CType:
427 def __init__(self, is_const: bool):
428 self._is_const = is_const
429
430 @property
431 def is_const(self) -> bool:
432 return self._is_const
433
434
435 # Arithmetic C type.
436 class _ArithCType(_CType):
437 def __init__(self, name: str, is_const: bool):
438 super().__init__(is_const)
439 self._name = name
440
441 @property
442 def name(self) -> str:
443 return self._name
444
445 def __str__(self) -> str:
446 return f'{"const " if self._is_const else ""}{self._name}'
447
448
449 # Pointer C type.
450 class _PointerCType(_CType):
451 def __init__(self, pointed_c_type: _CType, is_const: bool):
452 super().__init__(is_const)
453 self._pointed_c_type = pointed_c_type
454
455 @property
456 def pointed_c_type(self) -> _CType:
457 return self._pointed_c_type
458
459 def __str__(self) -> str:
460 s = str(self._pointed_c_type)
461
462 if not s.endswith('*'):
463 s += ' '
464
465 s += '*'
466
467 if self._is_const:
468 s += ' const'
469
470 return s
471
472
473 # Returns the name of a loop variable given a nesting level `level`.
474 def _loop_var_name(level: Count) -> str:
475 if level < 3:
476 return 'ijk'[level]
477
478 return f'k{level - 2}'
479
480
481 # A C code generator.
482 #
483 # Such a code generator can generate:
484 #
485 # * The bitfield header (gen_bitfield_header()).
486 # * The public header (gen_header()).
487 # * The source code (gen_src()).
488 class _CodeGen:
489 def __init__(self, cfg: barectf_config.Configuration):
490 self._cfg = cfg
491 self._iden_prefix = cfg.options.code_generation_options.identifier_prefix
492 self._templ_filters: Mapping[str, Callable[..., Any]] = {
493 'ft_c_type': self._ft_c_type,
494 'open_func_params_str': self._open_func_params_str,
495 'trace_func_params_str': self._trace_func_params_str,
496 'serialize_er_common_ctx_func_params_str': self._serialize_er_common_ctx_func_params_str,
497 'loop_var_name': _loop_var_name,
498 'op_src_var_name': self._op_src_var_name,
499 }
500 self._func_proto_params_templ = self._create_template('func-proto-params.j2')
501 self._serialize_align_statements_templ = self._create_template('serialize-align-statements.j2')
502 self._serialize_write_int_statements_templ = self._create_template('serialize-write-int-statements.j2')
503 self._serialize_write_real_statements_templ = self._create_template('serialize-write-real-statements.j2')
504 self._serialize_write_string_statements_templ = self._create_template('serialize-write-string-statements.j2')
505 self._serialize_write_struct_statements_templ = self._create_template('serialize-write-struct-statements.j2')
506 self._serialize_write_static_array_statements_templ = self._create_template('serialize-write-static-array-statements.j2')
507 self._serialize_write_dynamic_array_statements_templ = self._create_template('serialize-write-dynamic-array-statements.j2')
508 self._serialize_write_magic_statements_templ = self._create_template('serialize-write-magic-statements.j2')
509 self._serialize_write_uuid_statements_templ = self._create_template('serialize-write-uuid-statements.j2')
510 self._serialize_write_dst_id_statements_templ = self._create_template('serialize-write-dst-id-statements.j2')
511 self._serialize_write_timestamp_statements_templ = self._create_template('serialize-write-timestamp-statements.j2')
512 self._serialize_write_packet_size_statements_templ = self._create_template('serialize-write-packet-size-statements.j2')
513 self._serialize_write_skip_save_statements_templ = self._create_template('serialize-write-skip-save-statements.j2')
514 self._serialize_write_ert_id_statements_templ = self._create_template('serialize-write-ert-id-statements.j2')
515 self._size_align_statements_templ = self._create_template('size-align-statements.j2')
516 self._size_write_bit_array_statements_templ = self._create_template('size-write-bit-array-statements.j2')
517 self._size_write_string_statements_templ = self._create_template('size-write-string-statements.j2')
518 self._size_write_struct_statements_templ = self._create_template('size-write-struct-statements.j2')
519 self._size_write_static_array_statements_templ = self._create_template('size-write-static-array-statements.j2')
520 self._size_write_dynamic_array_statements_templ = self._create_template('size-write-dynamic-array-statements.j2')
521
522 # Creates and returns a template named `name` which is a file
523 # template if `is_file_template` is `True`.
524 #
525 # `name` is the file name, including the `.j2` extension, within the
526 # `c` directory.
527 #
528 # Such a template has the filters custom filters
529 # `self._templ_filters`.
530 def _create_template_base(self, name: str,
531 is_file_template: bool) -> barectf_template._Template:
532 return barectf_template._Template(f'c/{name}', is_file_template, self._cfg,
533 self._templ_filters)
534
535 # Creates and returns a non-file template named `name`.
536 #
537 # See _create_template_base() for `name`.
538 def _create_template(self, name: str) -> barectf_template._Template:
539 return self._create_template_base(name, False)
540
541 # Creates and returns a file template named `name`.
542 #
543 # See _create_template_base() for `name`.
544 def _create_file_template(self, name: str) -> barectf_template._Template:
545 return self._create_template_base(name, True)
546
547 # Trace type of this code generator's barectf configuration.
548 @property
549 def _trace_type(self) -> barectf_config.TraceType:
550 return self._cfg.trace.type
551
552 # Returns the name of a source variable for the operation `op`.
553 def _op_src_var_name(self, op: _LeafOp) -> str:
554 s = ''
555
556 for index, name in enumerate(op.names):
557 if index > 0 and not name.startswith('['):
558 s += '_'
559
560 s += name
561
562 return s
563
564 # Returns the C type for the field type `ft`, making it `const` if
565 # `is_const` is `True`.
566 def _ft_c_type(self, ft: barectf_config._FieldType, is_const: bool = False):
567 if isinstance(ft, barectf_config._IntegerFieldType):
568 ft = typing.cast(barectf_config._IntegerFieldType, ft)
569 sign_prefix = 'u' if isinstance(ft, barectf_config.UnsignedIntegerFieldType) else ''
570
571 if ft.size <= 8:
572 sz = 8
573 elif ft.size <= 16:
574 sz = 16
575 elif ft.size <= 32:
576 sz = 32
577 else:
578 assert ft.size <= 64
579 sz = 64
580
581 return _ArithCType(f'{sign_prefix}int{sz}_t', is_const)
582 elif type(ft) is barectf_config.RealFieldType:
583 ft = typing.cast(barectf_config.RealFieldType, ft)
584
585 if ft.size == 32 and ft.alignment == 32:
586 s = 'float'
587 elif ft.size == 64 and ft.alignment == 64:
588 s = 'double'
589 else:
590 s = 'uint64_t'
591
592 return _ArithCType(s, is_const)
593 elif type(ft) is barectf_config.StringFieldType:
594 return _PointerCType(_ArithCType('char', True), is_const)
595 else:
596 assert isinstance(ft, barectf_config._ArrayFieldType)
597 ft = typing.cast(barectf_config._ArrayFieldType, ft)
598 return _PointerCType(self._ft_c_type(ft.element_field_type, True), is_const)
599
600 # Returns the function prototype parameters for the members of the
601 # root structure field type `root_ft`.
602 #
603 # Each parameter has the prefix `name_prefix` followed with `_`.
604 #
605 # Members of which the name is in `exclude_set` are excluded.
606 def _proto_params_str(self, root_ft: Optional[barectf_config.StructureFieldType],
607 name_prefix: str, const_params: bool,
608 exclude_set: Optional[Set[str]] = None, only_dyn: bool = False) -> str:
609 if root_ft is None:
610 return ''
611
612 if exclude_set is None:
613 exclude_set = set()
614
615 params = []
616
617 for member_name, member in root_ft.members.items():
618 if member_name in exclude_set:
619 continue
620
621 is_dyn = member.field_type.size_is_dynamic
622
623 if isinstance(member.field_type, barectf_config.UnsignedIntegerFieldType):
624 ft = typing.cast(barectf_config.UnsignedIntegerFieldType, member.field_type)
625 is_dyn = is_dyn or ft._is_len
626
627 if only_dyn and not is_dyn:
628 continue
629
630 params.append(_FtParam(member.field_type, member_name))
631
632 return self._func_proto_params_templ.render(params=params, prefix=name_prefix,
633 const_params=const_params)
634
635 # Returns the packet opening function prototype parameters for the
636 # data stream type `dst`.
637 def _open_func_params_str(self, dst: barectf_config.DataStreamType, const_params: bool) -> str:
638 parts = []
639 parts.append(self._proto_params_str(self._trace_type._pkt_header_ft, _RootFtPrefixes.PH,
640 const_params, {'magic', 'stream_id', 'uuid'}))
641
642 exclude_set = {
643 'timestamp_begin',
644 'timestamp_end',
645 'packet_size',
646 'content_size',
647 'events_discarded',
648 }
649 parts.append(self._proto_params_str(dst._pkt_ctx_ft, _RootFtPrefixes.PC, const_params,
650 exclude_set))
651 return ''.join(parts)
652
653 # Returns the tracing function prototype parameters for the data
654 # stream and event record types `ds_er_types`.
655 def _trace_func_params_str(self, ds_er_types: Tuple[barectf_config.DataStreamType,
656 barectf_config.EventRecordType],
657 const_params: bool, only_dyn: bool = False):
658 dst = ds_er_types[0]
659 ert = ds_er_types[1]
660 parts = []
661
662 if dst._er_header_ft is not None:
663 parts.append(self._proto_params_str(dst._er_header_ft, _RootFtPrefixes.ERH,
664 const_params, {'id', 'timestamp'},
665 only_dyn=only_dyn))
666
667 if dst.event_record_common_context_field_type is not None:
668 parts.append(self._proto_params_str(dst.event_record_common_context_field_type,
669 _RootFtPrefixes.ERCC, const_params,
670 only_dyn=only_dyn))
671
672 if ert.specific_context_field_type is not None:
673 parts.append(self._proto_params_str(ert.specific_context_field_type,
674 _RootFtPrefixes.SC, const_params,
675 only_dyn=only_dyn))
676
677 if ert.payload_field_type is not None:
678 parts.append(self._proto_params_str(ert.payload_field_type, _RootFtPrefixes.P,
679 const_params, only_dyn=only_dyn))
680
681 return ''.join(parts)
682
683 # Returns the event record common context serialization function
684 # prototype parameters for the data stream type `dst`.
685 def _serialize_er_common_ctx_func_params_str(self, dst: barectf_config.DataStreamType,
686 const_params: bool) -> str:
687 return self._proto_params_str(dst.event_record_common_context_field_type,
688 _RootFtPrefixes.ERCC, const_params)
689
690 # Generates the bitfield header file contents.
691 def gen_bitfield_header(self) -> str:
692 return self._create_file_template('bitfield.h.j2').render()
693
694 # Generates the public header file contents.
695 def gen_header(self) -> str:
696 return self._create_file_template('barectf.h.j2').render(root_ft_prefixes=_RootFtPrefixes)
697
698 # Generates the source code file contents.
699 def gen_src(self, header_file_name: str, bitfield_header_file_name: str) -> str:
700 # Creates and returns the operations for all the data stream and
701 # for all their event records.
702 def create_ds_ops() -> Mapping[barectf_config.DataStreamType, _DsOps]:
703 ds_ops = {}
704
705 for dst in self._trace_type.data_stream_types:
706 pkt_header_op = None
707 builder = _OpBuilder(self)
708 pkt_header_ft = self._trace_type._pkt_header_ft
709
710 # packet header operations
711 if pkt_header_ft is not None:
712 spec_serialize_write_templates = {
713 'magic': self._serialize_write_magic_statements_templ,
714 'uuid': self._serialize_write_uuid_statements_templ,
715 'stream_id': self._serialize_write_dst_id_statements_templ,
716 }
717 pkt_header_op = builder.build_for_root_ft(pkt_header_ft,
718 _RootFtPrefixes.PH,
719 spec_serialize_write_templates)
720
721 # packet context operation
722 spec_serialize_write_templates = {
723 'timestamp_begin': self._serialize_write_timestamp_statements_templ,
724 'packet_size': self._serialize_write_packet_size_statements_templ,
725 'timestamp_end': self._serialize_write_skip_save_statements_templ,
726 'events_discarded': self._serialize_write_skip_save_statements_templ,
727 'content_size': self._serialize_write_skip_save_statements_templ,
728 }
729 pkt_ctx_op = builder.build_for_root_ft(dst._pkt_ctx_ft, _RootFtPrefixes.PC,
730 spec_serialize_write_templates)
731
732 # event record header operation
733 builder = _OpBuilder(self)
734 er_header_op = None
735
736 if dst._er_header_ft is not None:
737 spec_serialize_write_templates = {
738 'timestamp': self._serialize_write_timestamp_statements_templ,
739 'id': self._serialize_write_ert_id_statements_templ,
740 }
741 er_header_op = builder.build_for_root_ft(dst._er_header_ft, _RootFtPrefixes.ERH,
742 spec_serialize_write_templates)
743
744 # event record common context operation
745 er_common_ctx_op = None
746
747 if dst.event_record_common_context_field_type is not None:
748 er_common_ctx_op = builder.build_for_root_ft(dst.event_record_common_context_field_type,
749 _RootFtPrefixes.ERCC)
750
751 # operations specific to each event record type
752 er_ops = {}
753
754 for ert in dst.event_record_types:
755 ev_builder = copy.copy(builder)
756
757 # specific context operation
758 spec_ctx_op = None
759
760 if ert.specific_context_field_type is not None:
761 spec_ctx_op = ev_builder.build_for_root_ft(ert.specific_context_field_type,
762 _RootFtPrefixes.SC)
763
764 # payload operation
765 payload_op = None
766
767 if ert.payload_field_type is not None:
768 payload_op = ev_builder.build_for_root_ft(ert.payload_field_type,
769 _RootFtPrefixes.P)
770
771 er_ops[ert] = _ErOps(spec_ctx_op, payload_op)
772
773 ds_ops[dst] = _DsOps(pkt_header_op, pkt_ctx_op, er_header_op, er_common_ctx_op,
774 er_ops)
775
776 return ds_ops
777
778 # Returns the "write" operation for the packet context member
779 # named `member_name` within the data stream type `dst`.
780 def ds_op_pkt_ctx_op(dst: barectf_config.DataStreamType, member_name: str) -> _Op:
781 ret_op = None
782
783 for op in ds_ops[dst].pkt_ctx_op.subops:
784 if op.top_name == member_name and type(op) is _WriteOp:
785 ret_op = op
786 break
787
788 assert ret_op is not None
789 return typing.cast(_Op, ret_op)
790
791 ds_ops = create_ds_ops()
792 c_src = self._create_file_template('barectf.c.j2').render(header_file_name=header_file_name,
793 bitfield_header_file_name=bitfield_header_file_name,
794 root_ft_prefixes=_RootFtPrefixes,
795 root_ft_prefix_names=_ROOT_FT_PREFIX_NAMES,
796 ds_ops=ds_ops,
797 ds_op_pkt_ctx_op=ds_op_pkt_ctx_op)
798
799 # Jinja 2 makes it hard to have multiple contiguous blocks
800 # delimited with empty lines when using a for loop, while not
801 # also having an empty line at the end.
802 #
803 # Therefore, we often get this rendered pattern:
804 #
805 # /* ... */
806 # ...;
807 # ...;
808 #
809 # /* ... */
810 # ...;
811 # ...;
812 # ...;
813 #
814 # }
815 #
816 # It's ugly, so fix it here.
817 return re.sub(r'(\n)\s*\n(\s*})', r'\1\2', c_src)
This page took 0.047282 seconds and 4 git commands to generate.