Commit | Line | Data |
---|---|---|
e5aa0be3 PP |
1 | # The MIT License (MIT) |
2 | # | |
4a90140d | 3 | # Copyright (c) 2014-2020 Philippe Proulx <pproulx@efficios.com> |
e5aa0be3 | 4 | # |
1378f213 PP |
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: | |
e5aa0be3 | 12 | # |
1378f213 PP |
13 | # The above copyright notice and this permission notice shall be |
14 | # included in all copies or substantial portions of the Software. | |
e5aa0be3 | 15 | # |
1378f213 PP |
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. | |
e5aa0be3 | 23 | |
8c7c6ed2 | 24 | import barectf.template as barectf_template |
4810b707 | 25 | import barectf.config as barectf_config |
d6483c83 | 26 | import collections |
acfb8213 | 27 | import copy |
c268d669 | 28 | import re |
1624d186 PP |
29 | from typing import List, Optional, Mapping, Callable, Any, Set, Tuple |
30 | import typing | |
31 | from barectf.typing import Count, Alignment | |
4810b707 PP |
32 | |
33 | ||
d6483c83 PP |
34 | # A tuple containing serialization and size computation function |
35 | # templates for a given operation. | |
36 | _OpTemplates = collections.namedtuple('_OpTemplates', ['serialize', 'size']) | |
70e191bd PP |
37 | |
38 | ||
2394a4b4 | 39 | # Abstract base class of any operation within source code. |
d6483c83 PP |
40 | # |
41 | # Any operation has: | |
42 | # | |
d6483c83 PP |
43 | # * A field type. |
44 | # | |
2394a4b4 PP |
45 | # * A list of names which, when joined with `_`, form the generic |
46 | # C source variable name. | |
d6483c83 | 47 | # |
6bc97055 PP |
48 | # * A level: how deep this operation is within the operation tree. |
49 | # | |
d6483c83 PP |
50 | # * Serialization and size computation templates to generate the |
51 | # operation's source code for those functions. | |
52 | class _Op: | |
6bc97055 PP |
53 | def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count, |
54 | templates: _OpTemplates): | |
4810b707 | 55 | self._ft = ft |
d6483c83 | 56 | self._names = copy.copy(names) |
6bc97055 | 57 | self._level = level |
d6483c83 | 58 | self._templates = templates |
e5aa0be3 | 59 | |
acfb8213 | 60 | @property |
1624d186 | 61 | def ft(self) -> barectf_config._FieldType: |
4810b707 | 62 | return self._ft |
acfb8213 PP |
63 | |
64 | @property | |
1624d186 | 65 | def names(self) -> List[str]: |
acfb8213 | 66 | return self._names |
e5aa0be3 | 67 | |
6bc97055 PP |
68 | @property |
69 | def level(self) -> Count: | |
70 | return self._level | |
71 | ||
d6483c83 | 72 | @property |
1624d186 | 73 | def top_name(self) -> str: |
d6483c83 | 74 | return self._names[-1] |
e5aa0be3 | 75 | |
1624d186 | 76 | def _render_template(self, templ: barectf_template._Template, **kwargs) -> str: |
d6483c83 PP |
77 | return templ.render(op=self, root_ft_prefixes=_RootFtPrefixes, |
78 | root_ft_prefix_names=_ROOT_FT_PREFIX_NAMES, **kwargs) | |
79 | ||
1624d186 | 80 | def serialize_str(self, **kwargs) -> str: |
d6483c83 PP |
81 | return self._render_template(self._templates.serialize, **kwargs) |
82 | ||
1624d186 | 83 | def size_str(self, **kwargs) -> str: |
d6483c83 PP |
84 | return self._render_template(self._templates.size, **kwargs) |
85 | ||
86 | ||
2394a4b4 PP |
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): | |
6bc97055 PP |
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) | |
2394a4b4 PP |
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): | |
be9f12dc | 109 | pass |
2394a4b4 PP |
110 | |
111 | ||
d6483c83 | 112 | # An "align" operation. |
2394a4b4 | 113 | class _AlignOp(_LeafOp): |
be9f12dc PP |
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) | |
acfb8213 | 117 | self._value = value |
e5aa0be3 | 118 | |
acfb8213 | 119 | @property |
1624d186 | 120 | def value(self) -> Alignment: |
acfb8213 | 121 | return self._value |
e5aa0be3 | 122 | |
e5aa0be3 | 123 | |
d6483c83 | 124 | # A "write" operation. |
2394a4b4 | 125 | class _WriteOp(_LeafOp): |
be9f12dc PP |
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 | |
70e191bd | 135 | |
e5aa0be3 | 136 | |
1624d186 | 137 | _SpecSerializeWriteTemplates = Mapping[str, barectf_template._Template] |
1624d186 PP |
138 | |
139 | ||
2394a4b4 | 140 | # An operation builder. |
d6483c83 | 141 | # |
728fc4a7 PP |
142 | # Such a builder is closely connected to a `_CodeGen` object using it to |
143 | # find generic templates. | |
d6483c83 | 144 | # |
2394a4b4 PP |
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: | |
324b1c40 | 149 | def __init__(self, cg: '_CodeGen'): |
1624d186 | 150 | self._names: List[str] = [] |
6bc97055 | 151 | self._level = Count(0) |
be9f12dc | 152 | self._offset_in_byte: Optional[Count] = None |
d6483c83 | 153 | self._cg = cg |
acfb8213 | 154 | |
be9f12dc PP |
155 | # Whether or not we're within an array operation. |
156 | @property | |
157 | def _in_array(self): | |
158 | return self._level > 0 | |
159 | ||
2394a4b4 PP |
160 | # Creates and returns an operation for the root structure field type |
161 | # `ft` named `name`. | |
d6483c83 PP |
162 | # |
163 | # `spec_serialize_write_templates` is a mapping of first level | |
164 | # member names to specialized serialization "write" templates. | |
2394a4b4 PP |
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 | |
e5aa0be3 | 168 | |
d6483c83 PP |
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 | |
6bc97055 | 174 | assert self._level == 0 |
2394a4b4 PP |
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]) | |
d6483c83 | 179 | |
2394a4b4 | 180 | # Creates and returns the operation(s) for a given field type `ft` |
d6483c83 PP |
181 | # named `name`. |
182 | # | |
2394a4b4 PP |
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]: | |
1624d186 | 186 | def top_name() -> str: |
d6483c83 PP |
187 | return self._names[-1] |
188 | ||
2394a4b4 PP |
189 | # Creates and returns a "write" operation for the field type |
190 | # `ft`. | |
d6483c83 PP |
191 | # |
192 | # This function considers `spec_serialize_write_templates` to | |
193 | # override generic templates. | |
2394a4b4 | 194 | def create_write_op(ft: barectf_config._FieldType) -> _WriteOp: |
d6483c83 PP |
195 | assert type(ft) is not barectf_config.StructureFieldType |
196 | offset_in_byte = self._offset_in_byte | |
197 | ||
be9f12dc | 198 | if isinstance(ft, barectf_config._BitArrayFieldType) and self._offset_in_byte is not None: |
1624d186 | 199 | self._offset_in_byte = Count((self._offset_in_byte + ft.size) % 8) |
d6483c83 | 200 | |
1624d186 | 201 | serialize_write_templ: Optional[barectf_template._Template] = None |
d6483c83 PP |
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 | |
e5aa0be3 | 216 | |
d6483c83 PP |
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 | ||
be9f12dc PP |
222 | return _WriteOp(ft, self._names, self._level, |
223 | _OpTemplates(serialize_write_templ, size_write_templ), offset_in_byte) | |
d6483c83 | 224 | |
2394a4b4 | 225 | # Creates and returns an "align" operation for the field type |
d6483c83 PP |
226 | # `ft` if needed. |
227 | # | |
228 | # This function updates the builder's state. | |
be9f12dc | 229 | def try_create_align_op(alignment: Alignment, ft: barectf_config._FieldType) -> Optional[_AlignOp]: |
1624d186 PP |
230 | def align(v: Count, alignment: Alignment) -> Count: |
231 | return Count((v + (alignment - 1)) & -alignment) | |
d6483c83 | 232 | |
be9f12dc PP |
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) | |
d6483c83 | 240 | |
be9f12dc PP |
241 | if alignment > 1: |
242 | return _AlignOp(ft, self._names, self._level, | |
2394a4b4 PP |
243 | _OpTemplates(self._cg._serialize_align_statements_templ, |
244 | self._cg._size_align_statements_templ), | |
245 | alignment) | |
246 | ||
247 | return None | |
acfb8213 | 248 | |
6bc97055 PP |
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 | ||
d6483c83 PP |
253 | # push field type's name to the builder's name stack initially |
254 | self._names.append(name) | |
acfb8213 | 255 | |
2394a4b4 PP |
256 | # operations to return |
257 | ops: List[_Op] = [] | |
258 | ||
be9f12dc | 259 | if type(ft) is barectf_config.StringFieldType or self._names == [_RootFtPrefixes.PH, 'uuid']: |
6bc97055 | 260 | # strings and UUID array are always byte-aligned |
be9f12dc | 261 | op = try_create_align_op(Alignment(8), ft) |
2394a4b4 PP |
262 | |
263 | if op is not None: | |
264 | ops.append(op) | |
265 | ||
266 | ops.append(create_write_op(ft)) | |
4810b707 | 267 | else: |
6bc97055 | 268 | if ft_is_compound(ft): |
be9f12dc | 269 | self._offset_in_byte = None |
70e191bd | 270 | |
be9f12dc | 271 | init_align_op = try_create_align_op(ft.alignment, ft) |
6bc97055 | 272 | subops: List[_Op] = [] |
70e191bd | 273 | |
4810b707 | 274 | if type(ft) is barectf_config.StructureFieldType: |
1624d186 | 275 | ft = typing.cast(barectf_config.StructureFieldType, ft) |
2394a4b4 PP |
276 | |
277 | if init_align_op is not None: | |
be9f12dc PP |
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 `_StreamOps` and | |
283 | # `_EvOps` offer a single (structure field type) | |
284 | # operation. | |
2394a4b4 PP |
285 | subops.append(init_align_op) |
286 | ||
287 | # append suboperations for each member | |
4810b707 | 288 | for member_name, member in ft.members.items(): |
2394a4b4 PP |
289 | subops += self._build_for_ft(member.field_type, member_name, |
290 | spec_serialize_write_templates) | |
291 | ||
6bc97055 PP |
292 | # create structure field's compound operation |
293 | ops.append(_CompoundOp(ft, self._names, self._level, | |
2394a4b4 PP |
294 | _OpTemplates(self._cg._serialize_write_struct_statements_templ, |
295 | self._cg._size_write_struct_statements_templ), | |
6bc97055 | 296 | subops)) |
be9f12dc PP |
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 | |
6bc97055 PP |
300 | |
301 | if init_align_op is not None: | |
be9f12dc | 302 | ops.append(init_align_op) |
6bc97055 PP |
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 | ||
be9f12dc PP |
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)) | |
70e191bd | 322 | else: |
2394a4b4 PP |
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)) | |
d6483c83 PP |
328 | |
329 | # exiting for this field type: pop its name | |
330 | del self._names[-1] | |
70e191bd | 331 | |
2394a4b4 PP |
332 | return ops |
333 | ||
334 | ||
335 | _OptCompoundOp = Optional[_CompoundOp] | |
336 | ||
70e191bd | 337 | |
d6483c83 PP |
338 | # The operations for an event. |
339 | # | |
340 | # The available operations are: | |
341 | # | |
2394a4b4 PP |
342 | # * Specific context operation. |
343 | # * Payload operation. | |
1624d186 | 344 | class _EvOps: |
2394a4b4 PP |
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 | |
70e191bd | 348 | |
d6483c83 | 349 | @property |
2394a4b4 PP |
350 | def spec_ctx_op(self) -> _OptCompoundOp: |
351 | return self._spec_ctx_op | |
acfb8213 | 352 | |
d6483c83 | 353 | @property |
2394a4b4 PP |
354 | def payload_op(self) -> _OptCompoundOp: |
355 | return self._payload_op | |
acfb8213 | 356 | |
70e191bd | 357 | |
1624d186 PP |
358 | _EvOpsMap = Mapping[barectf_config.EventType, _EvOps] |
359 | ||
360 | ||
d6483c83 PP |
361 | # The operations for a stream. |
362 | # | |
363 | # The available operations are: | |
364 | # | |
2394a4b4 PP |
365 | # * Packet header operation. |
366 | # * Packet context operation. | |
367 | # * Event header operation. | |
368 | # * Event common context operation. | |
1624d186 | 369 | # * Event operations (`_EvOps`). |
d6483c83 | 370 | class _StreamOps: |
2394a4b4 PP |
371 | def __init__(self, pkt_header_op: _OptCompoundOp, pkt_ctx_op: _CompoundOp, |
372 | ev_header_op: _OptCompoundOp, ev_common_ctx_op: _OptCompoundOp, ev_ops: _EvOpsMap): | |
373 | self._pkt_header_op = pkt_header_op | |
374 | self._pkt_ctx_op = pkt_ctx_op | |
375 | self._ev_header_op = ev_header_op | |
376 | self._ev_common_ctx_op = ev_common_ctx_op | |
377 | self._ev_ops = ev_ops | |
70e191bd | 378 | |
d6483c83 | 379 | @property |
2394a4b4 PP |
380 | def pkt_header_op(self) -> _OptCompoundOp: |
381 | return self._pkt_header_op | |
d6483c83 PP |
382 | |
383 | @property | |
2394a4b4 PP |
384 | def pkt_ctx_op(self) -> _CompoundOp: |
385 | return self._pkt_ctx_op | |
d6483c83 PP |
386 | |
387 | @property | |
2394a4b4 PP |
388 | def ev_header_op(self) -> _OptCompoundOp: |
389 | return self._ev_header_op | |
d6483c83 PP |
390 | |
391 | @property | |
2394a4b4 PP |
392 | def ev_common_ctx_op(self) -> _OptCompoundOp: |
393 | return self._ev_common_ctx_op | |
d6483c83 PP |
394 | |
395 | @property | |
1624d186 | 396 | def ev_ops(self) -> _EvOpsMap: |
d6483c83 PP |
397 | return self._ev_ops |
398 | ||
399 | ||
400 | # The C variable name prefixes for the six kinds of root field types. | |
1b49c7b8 | 401 | class _RootFtPrefixes: |
1c650e47 PP |
402 | PH = 'ph' |
403 | PC = 'pc' | |
404 | EH = 'eh' | |
405 | ECC = 'ecc' | |
406 | SC = 'sc' | |
407 | P = 'p' | |
d6483c83 PP |
408 | |
409 | ||
410 | # The human-readable names of the `_RootFtPrefixes` members. | |
411 | _ROOT_FT_PREFIX_NAMES = { | |
1c650e47 PP |
412 | _RootFtPrefixes.PH: 'packet header', |
413 | _RootFtPrefixes.PC: 'packet context', | |
414 | _RootFtPrefixes.EH: 'event header', | |
415 | _RootFtPrefixes.ECC: 'event common context', | |
416 | _RootFtPrefixes.SC: 'specific context', | |
417 | _RootFtPrefixes.P: 'payload', | |
acfb8213 | 418 | } |
e5aa0be3 PP |
419 | |
420 | ||
d6483c83 PP |
421 | # A named function parameter for a given field type. |
422 | _FtParam = collections.namedtuple('_FtParam', ['ft', 'name']) | |
423 | ||
424 | ||
2d18e033 PP |
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 | ||
6bc97055 PP |
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 | ||
d6483c83 PP |
481 | # A C code generator. |
482 | # | |
483 | # Such a code generator can generate: | |
484 | # | |
728fc4a7 PP |
485 | # * The bitfield header (gen_bitfield_header()). |
486 | # * The public header (gen_header()). | |
487 | # * The source code (gen_src()). | |
488 | class _CodeGen: | |
1624d186 | 489 | def __init__(self, cfg: barectf_config.Configuration): |
e5aa0be3 | 490 | self._cfg = cfg |
d6483c83 | 491 | self._iden_prefix = cfg.options.code_generation_options.identifier_prefix |
1624d186 | 492 | self._templ_filters: Mapping[str, Callable[..., Any]] = { |
d6483c83 PP |
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_ev_common_ctx_func_params_str': self._serialize_ev_common_ctx_func_params_str, | |
6bc97055 PP |
497 | 'loop_var_name': _loop_var_name, |
498 | 'op_src_var_name': self._op_src_var_name, | |
1b49c7b8 | 499 | } |
d6483c83 PP |
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') | |
2394a4b4 | 505 | self._serialize_write_struct_statements_templ = self._create_template('serialize-write-struct-statements.j2') |
6bc97055 | 506 | self._serialize_write_static_array_statements_templ = self._create_template('serialize-write-static-array-statements.j2') |
be9f12dc | 507 | self._serialize_write_dynamic_array_statements_templ = self._create_template('serialize-write-dynamic-array-statements.j2') |
d6483c83 PP |
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_stream_type_id_statements_templ = self._create_template('serialize-write-stream-type-id-statements.j2') | |
511 | self._serialize_write_time_statements_templ = self._create_template('serialize-write-time-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_ev_type_id_statements_templ = self._create_template('serialize-write-ev-type-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') | |
2394a4b4 | 518 | self._size_write_struct_statements_templ = self._create_template('size-write-struct-statements.j2') |
6bc97055 | 519 | self._size_write_static_array_statements_templ = self._create_template('size-write-static-array-statements.j2') |
be9f12dc | 520 | self._size_write_dynamic_array_statements_templ = self._create_template('size-write-dynamic-array-statements.j2') |
d6483c83 PP |
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`. | |
1624d186 PP |
530 | def _create_template_base(self, name: str, |
531 | is_file_template: bool) -> barectf_template._Template: | |
d6483c83 PP |
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`. | |
8c7c6ed2 | 538 | def _create_template(self, name: str) -> barectf_template._Template: |
d6483c83 | 539 | return self._create_template_base(name, False) |
8c7c6ed2 | 540 | |
d6483c83 PP |
541 | # Creates and returns a file template named `name`. |
542 | # | |
543 | # See _create_template_base() for `name`. | |
8c7c6ed2 | 544 | def _create_file_template(self, name: str) -> barectf_template._Template: |
d6483c83 | 545 | return self._create_template_base(name, True) |
8c7c6ed2 | 546 | |
d6483c83 | 547 | # Trace type of this code generator's barectf configuration. |
4810b707 | 548 | @property |
1624d186 | 549 | def _trace_type(self) -> barectf_config.TraceType: |
4810b707 | 550 | return self._cfg.trace.type |
27bc6f1e | 551 | |
6bc97055 PP |
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 | ||
2d18e033 PP |
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): | |
4810b707 | 567 | if isinstance(ft, barectf_config._IntegerFieldType): |
1624d186 | 568 | ft = typing.cast(barectf_config._IntegerFieldType, ft) |
4810b707 PP |
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: | |
d6483c83 | 578 | assert ft.size <= 64 |
4810b707 PP |
579 | sz = 64 |
580 | ||
2d18e033 | 581 | return _ArithCType(f'{sign_prefix}int{sz}_t', is_const) |
4810b707 | 582 | elif type(ft) is barectf_config.RealFieldType: |
1624d186 PP |
583 | ft = typing.cast(barectf_config.RealFieldType, ft) |
584 | ||
4810b707 | 585 | if ft.size == 32 and ft.alignment == 32: |
2d18e033 | 586 | s = 'float' |
4810b707 | 587 | elif ft.size == 64 and ft.alignment == 64: |
2d18e033 | 588 | s = 'double' |
4810b707 | 589 | else: |
2d18e033 | 590 | s = 'uint64_t' |
e18cf9d6 | 591 | |
2d18e033 | 592 | return _ArithCType(s, is_const) |
6bc97055 | 593 | elif type(ft) is barectf_config.StringFieldType: |
2d18e033 | 594 | return _PointerCType(_ArithCType('char', True), is_const) |
6bc97055 | 595 | else: |
be9f12dc PP |
596 | assert isinstance(ft, barectf_config._ArrayFieldType) |
597 | ft = typing.cast(barectf_config._ArrayFieldType, ft) | |
6bc97055 | 598 | return _PointerCType(self._ft_c_type(ft.element_field_type, True), is_const) |
e5aa0be3 | 599 | |
d6483c83 PP |
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. | |
1624d186 PP |
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: | |
d6483c83 | 609 | if root_ft is None: |
1624d186 | 610 | return '' |
e5aa0be3 | 611 | |
4810b707 PP |
612 | if exclude_set is None: |
613 | exclude_set = set() | |
614 | ||
d6483c83 | 615 | params = [] |
e5aa0be3 | 616 | |
d6483c83 | 617 | for member_name, member in root_ft.members.items(): |
4810b707 | 618 | if member_name in exclude_set: |
e5aa0be3 PP |
619 | continue |
620 | ||
be9f12dc PP |
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: | |
b622b24f PP |
628 | continue |
629 | ||
d6483c83 | 630 | params.append(_FtParam(member.field_type, member_name)) |
e5aa0be3 | 631 | |
e18cf9d6 PP |
632 | return self._func_proto_params_templ.render(params=params, prefix=name_prefix, |
633 | const_params=const_params) | |
4810b707 | 634 | |
d6483c83 PP |
635 | # Returns the packet opening function prototype parameters for the |
636 | # stream type `stream_type`. | |
1624d186 PP |
637 | def _open_func_params_str(self, stream_type: barectf_config.StreamType, |
638 | const_params: bool) -> str: | |
d6483c83 | 639 | parts = [] |
1c650e47 | 640 | parts.append(self._proto_params_str(self._trace_type._pkt_header_ft, _RootFtPrefixes.PH, |
e18cf9d6 | 641 | const_params, {'magic', 'stream_id', 'uuid'})) |
e5aa0be3 | 642 | |
4810b707 PP |
643 | exclude_set = { |
644 | 'timestamp_begin', | |
645 | 'timestamp_end', | |
646 | 'packet_size', | |
647 | 'content_size', | |
648 | 'events_discarded', | |
649 | } | |
1c650e47 | 650 | parts.append(self._proto_params_str(stream_type._pkt_ctx_ft, _RootFtPrefixes.PC, |
e18cf9d6 | 651 | const_params, exclude_set)) |
d6483c83 | 652 | return ''.join(parts) |
e5aa0be3 | 653 | |
d6483c83 PP |
654 | # Returns the tracing function prototype parameters for the stream |
655 | # and event types `stream_ev_types`. | |
1624d186 PP |
656 | def _trace_func_params_str(self, stream_ev_types: Tuple[barectf_config.StreamType, |
657 | barectf_config.EventType], | |
658 | const_params: bool, only_dyn: bool = False): | |
d6483c83 PP |
659 | stream_type = stream_ev_types[0] |
660 | ev_type = stream_ev_types[1] | |
661 | parts = [] | |
e5aa0be3 | 662 | |
4810b707 | 663 | if stream_type._ev_header_ft is not None: |
1c650e47 | 664 | parts.append(self._proto_params_str(stream_type._ev_header_ft, _RootFtPrefixes.EH, |
b622b24f PP |
665 | const_params, {'id', 'timestamp'}, |
666 | only_dyn=only_dyn)) | |
4810b707 PP |
667 | |
668 | if stream_type.event_common_context_field_type is not None: | |
d6483c83 | 669 | parts.append(self._proto_params_str(stream_type.event_common_context_field_type, |
b622b24f PP |
670 | _RootFtPrefixes.ECC, const_params, |
671 | only_dyn=only_dyn)) | |
4810b707 PP |
672 | |
673 | if ev_type.specific_context_field_type is not None: | |
d6483c83 | 674 | parts.append(self._proto_params_str(ev_type.specific_context_field_type, |
b622b24f PP |
675 | _RootFtPrefixes.SC, const_params, |
676 | only_dyn=only_dyn)) | |
4810b707 PP |
677 | |
678 | if ev_type.payload_field_type is not None: | |
e18cf9d6 | 679 | parts.append(self._proto_params_str(ev_type.payload_field_type, _RootFtPrefixes.P, |
b622b24f | 680 | const_params, only_dyn=only_dyn)) |
e5aa0be3 | 681 | |
d6483c83 | 682 | return ''.join(parts) |
4810b707 | 683 | |
d6483c83 PP |
684 | # Returns the event header serialization function prototype |
685 | # parameters for the stream type `stream_type`. | |
1624d186 PP |
686 | def _serialize_ev_common_ctx_func_params_str(self, stream_type: barectf_config.StreamType, |
687 | const_params: bool) -> str: | |
d6483c83 | 688 | return self._proto_params_str(stream_type.event_common_context_field_type, |
324b1c40 | 689 | _RootFtPrefixes.ECC, const_params) |
e5aa0be3 | 690 | |
d6483c83 | 691 | # Generates the bitfield header file contents. |
1624d186 | 692 | def gen_bitfield_header(self) -> str: |
d6483c83 | 693 | return self._create_file_template('bitfield.h.j2').render() |
e5aa0be3 | 694 | |
d6483c83 | 695 | # Generates the public header file contents. |
1624d186 | 696 | def gen_header(self) -> str: |
d6483c83 | 697 | return self._create_file_template('barectf.h.j2').render(root_ft_prefixes=_RootFtPrefixes) |
3cb793a1 | 698 | |
d6483c83 | 699 | # Generates the source code file contents. |
1624d186 | 700 | def gen_src(self, header_file_name: str, bitfield_header_file_name: str) -> str: |
d6483c83 PP |
701 | # Creates and returns the operations for all the stream and for |
702 | # all their events. | |
1624d186 | 703 | def create_stream_ops() -> Mapping[barectf_config.StreamType, _StreamOps]: |
d6483c83 PP |
704 | stream_ser_ops = {} |
705 | ||
706 | for stream_type in self._trace_type.stream_types: | |
2394a4b4 PP |
707 | pkt_header_ser_op = None |
708 | builder = _OpBuilder(self) | |
d6483c83 PP |
709 | pkt_header_ft = self._trace_type._pkt_header_ft |
710 | ||
2394a4b4 | 711 | # packet header operations |
d6483c83 PP |
712 | if pkt_header_ft is not None: |
713 | spec_serialize_write_templates = { | |
714 | 'magic': self._serialize_write_magic_statements_templ, | |
715 | 'uuid': self._serialize_write_uuid_statements_templ, | |
716 | 'stream_id': self._serialize_write_stream_type_id_statements_templ, | |
717 | } | |
2394a4b4 PP |
718 | pkt_header_ser_op = builder.build_for_root_ft(pkt_header_ft, |
719 | _RootFtPrefixes.PH, | |
720 | spec_serialize_write_templates) | |
d6483c83 | 721 | |
2394a4b4 | 722 | # packet context operations |
d6483c83 PP |
723 | spec_serialize_write_templates = { |
724 | 'timestamp_begin': self._serialize_write_time_statements_templ, | |
725 | 'packet_size': self._serialize_write_packet_size_statements_templ, | |
726 | 'timestamp_end': self._serialize_write_skip_save_statements_templ, | |
727 | 'events_discarded': self._serialize_write_skip_save_statements_templ, | |
728 | 'content_size': self._serialize_write_skip_save_statements_templ, | |
729 | } | |
2394a4b4 PP |
730 | pkt_ctx_ser_op = builder.build_for_root_ft(stream_type._pkt_ctx_ft, |
731 | _RootFtPrefixes.PC, | |
732 | spec_serialize_write_templates) | |
d6483c83 | 733 | |
2394a4b4 PP |
734 | # event header operationss |
735 | builder = _OpBuilder(self) | |
736 | ev_header_ser_op = None | |
d6483c83 PP |
737 | |
738 | if stream_type._ev_header_ft is not None: | |
739 | spec_serialize_write_templates = { | |
740 | 'timestamp': self._serialize_write_time_statements_templ, | |
741 | 'id': self._serialize_write_ev_type_id_statements_templ, | |
742 | } | |
2394a4b4 PP |
743 | ev_header_ser_op = builder.build_for_root_ft(stream_type._ev_header_ft, |
744 | _RootFtPrefixes.EH, | |
745 | spec_serialize_write_templates) | |
d6483c83 | 746 | |
2394a4b4 PP |
747 | # event common context operations |
748 | ev_common_ctx_ser_op = None | |
d6483c83 PP |
749 | |
750 | if stream_type.event_common_context_field_type is not None: | |
2394a4b4 PP |
751 | ev_common_ctx_ser_op = builder.build_for_root_ft(stream_type.event_common_context_field_type, |
752 | _RootFtPrefixes.ECC) | |
d6483c83 | 753 | |
2394a4b4 | 754 | # operations specific to each event type |
d6483c83 PP |
755 | ev_ser_ops = {} |
756 | ||
757 | for ev_type in stream_type.event_types: | |
758 | ev_builder = copy.copy(builder) | |
759 | ||
2394a4b4 PP |
760 | # specific context operations |
761 | spec_ctx_ser_op = None | |
d6483c83 PP |
762 | |
763 | if ev_type.specific_context_field_type is not None: | |
2394a4b4 PP |
764 | spec_ctx_ser_op = ev_builder.build_for_root_ft(ev_type.specific_context_field_type, |
765 | _RootFtPrefixes.SC) | |
d6483c83 | 766 | |
2394a4b4 PP |
767 | # payload operations |
768 | payload_ser_op = None | |
d6483c83 PP |
769 | |
770 | if ev_type.payload_field_type is not None: | |
2394a4b4 PP |
771 | payload_ser_op = ev_builder.build_for_root_ft(ev_type.payload_field_type, |
772 | _RootFtPrefixes.P) | |
d6483c83 | 773 | |
2394a4b4 | 774 | ev_ser_ops[ev_type] = _EvOps(spec_ctx_ser_op, payload_ser_op) |
d6483c83 | 775 | |
2394a4b4 PP |
776 | stream_ser_ops[stream_type] = _StreamOps(pkt_header_ser_op, pkt_ctx_ser_op, |
777 | ev_header_ser_op, ev_common_ctx_ser_op, | |
d6483c83 PP |
778 | ev_ser_ops) |
779 | ||
780 | return stream_ser_ops | |
781 | ||
782 | # Returns the "write" operation for the packet context member | |
783 | # named `member_name` within the stream type `stream_type`. | |
1624d186 PP |
784 | def stream_op_pkt_ctx_op(stream_type: barectf_config.StreamType, member_name: str) -> _Op: |
785 | ret_op = None | |
786 | ||
2394a4b4 | 787 | for op in stream_ops[stream_type].pkt_ctx_op.subops: |
d6483c83 | 788 | if op.top_name == member_name and type(op) is _WriteOp: |
1624d186 PP |
789 | ret_op = op |
790 | break | |
791 | ||
792 | assert ret_op is not None | |
793 | return typing.cast(_Op, ret_op) | |
d6483c83 PP |
794 | |
795 | stream_ops = create_stream_ops() | |
c268d669 PP |
796 | c_src = self._create_file_template('barectf.c.j2').render(header_file_name=header_file_name, |
797 | bitfield_header_file_name=bitfield_header_file_name, | |
798 | root_ft_prefixes=_RootFtPrefixes, | |
799 | root_ft_prefix_names=_ROOT_FT_PREFIX_NAMES, | |
800 | stream_ops=stream_ops, | |
801 | stream_op_pkt_ctx_op=stream_op_pkt_ctx_op) | |
802 | ||
803 | # Jinja 2 makes it hard to have multiple contiguous blocks | |
804 | # delimited with empty lines when using a for loop, while not | |
805 | # also having an empty line at the end. | |
806 | # | |
807 | # Therefore, we often get this rendered pattern: | |
808 | # | |
809 | # /* ... */ | |
810 | # ...; | |
811 | # ...; | |
812 | # | |
813 | # /* ... */ | |
814 | # ...; | |
815 | # ...; | |
816 | # ...; | |
817 | # | |
818 | # } | |
819 | # | |
820 | # It's ugly, so fix it here. | |
821 | return re.sub(r'(\n)\s*\n(\s*})', r'\1\2', c_src) |