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 PP |
47 | # |
48 | # * Serialization and size computation templates to generate the | |
49 | # operation's source code for those functions. | |
50 | class _Op: | |
2394a4b4 | 51 | def __init__(self, ft: barectf_config._FieldType, names: List[str], templates: _OpTemplates): |
4810b707 | 52 | self._ft = ft |
d6483c83 PP |
53 | self._names = copy.copy(names) |
54 | self._templates = templates | |
e5aa0be3 | 55 | |
acfb8213 | 56 | @property |
1624d186 | 57 | def ft(self) -> barectf_config._FieldType: |
4810b707 | 58 | return self._ft |
acfb8213 PP |
59 | |
60 | @property | |
1624d186 | 61 | def names(self) -> List[str]: |
acfb8213 | 62 | return self._names |
e5aa0be3 | 63 | |
d6483c83 | 64 | @property |
1624d186 | 65 | def top_name(self) -> str: |
d6483c83 | 66 | return self._names[-1] |
e5aa0be3 | 67 | |
1624d186 | 68 | def _render_template(self, templ: barectf_template._Template, **kwargs) -> str: |
d6483c83 PP |
69 | return templ.render(op=self, root_ft_prefixes=_RootFtPrefixes, |
70 | root_ft_prefix_names=_ROOT_FT_PREFIX_NAMES, **kwargs) | |
71 | ||
1624d186 | 72 | def serialize_str(self, **kwargs) -> str: |
d6483c83 PP |
73 | return self._render_template(self._templates.serialize, **kwargs) |
74 | ||
1624d186 | 75 | def size_str(self, **kwargs) -> str: |
d6483c83 PP |
76 | return self._render_template(self._templates.size, **kwargs) |
77 | ||
78 | ||
2394a4b4 PP |
79 | # Compound operation. |
80 | # | |
81 | # A compound operation contains a list of suboperations (leaf or | |
82 | # compound). | |
83 | # | |
84 | # Get the suboperations of a compound operation with its `subops` | |
85 | # property. | |
86 | # | |
87 | # The templates of a compound operation handles its suboperations. | |
88 | class _CompoundOp(_Op): | |
89 | def __init__(self, ft: barectf_config._FieldType, names: List[str], templates: _OpTemplates, | |
90 | subops: List[Any] = None): | |
91 | super().__init__(ft, names, templates) | |
92 | self._subops = subops | |
93 | ||
94 | @property | |
95 | def subops(self): | |
96 | return self._subops | |
97 | ||
98 | ||
99 | # Leaf operation (abstract class). | |
100 | class _LeafOp(_Op): | |
101 | def __init__(self, offset_in_byte: Count, ft: barectf_config._FieldType, names: List[str], | |
102 | templates: _OpTemplates): | |
103 | super().__init__(ft, names, templates) | |
104 | assert offset_in_byte >= 0 and offset_in_byte < 8 | |
105 | self._offset_in_byte = offset_in_byte | |
106 | ||
107 | @property | |
108 | def offset_in_byte(self) -> Count: | |
109 | return self._offset_in_byte | |
110 | ||
111 | ||
d6483c83 | 112 | # An "align" operation. |
2394a4b4 | 113 | class _AlignOp(_LeafOp): |
1624d186 PP |
114 | def __init__(self, offset_in_byte: Count, ft: barectf_config._FieldType, names: List[str], |
115 | templates: _OpTemplates, value: Alignment): | |
d6483c83 | 116 | super().__init__(offset_in_byte, ft, names, 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): |
4810b707 | 126 | pass |
70e191bd | 127 | |
e5aa0be3 | 128 | |
1624d186 | 129 | _SpecSerializeWriteTemplates = Mapping[str, barectf_template._Template] |
1624d186 PP |
130 | |
131 | ||
2394a4b4 | 132 | # An operation builder. |
d6483c83 | 133 | # |
728fc4a7 PP |
134 | # Such a builder is closely connected to a `_CodeGen` object using it to |
135 | # find generic templates. | |
d6483c83 | 136 | # |
2394a4b4 PP |
137 | # Call build_for_root_ft() to make an operation builder create a |
138 | # compound operation for a given root structure field type, recursively, | |
139 | # and return it. | |
140 | class _OpBuilder: | |
324b1c40 | 141 | def __init__(self, cg: '_CodeGen'): |
1624d186 PP |
142 | self._last_alignment: Optional[Alignment] = None |
143 | self._last_bit_array_size: Optional[Count] = None | |
1624d186 PP |
144 | self._names: List[str] = [] |
145 | self._offset_in_byte = Count(0) | |
d6483c83 | 146 | self._cg = cg |
acfb8213 | 147 | |
2394a4b4 PP |
148 | # Creates and returns an operation for the root structure field type |
149 | # `ft` named `name`. | |
d6483c83 PP |
150 | # |
151 | # `spec_serialize_write_templates` is a mapping of first level | |
152 | # member names to specialized serialization "write" templates. | |
2394a4b4 PP |
153 | def build_for_root_ft(self, ft: barectf_config.StructureFieldType, name: str, |
154 | spec_serialize_write_templates: Optional[_SpecSerializeWriteTemplates] = None) -> _CompoundOp: | |
155 | assert ft is not None | |
e5aa0be3 | 156 | |
d6483c83 PP |
157 | if spec_serialize_write_templates is None: |
158 | spec_serialize_write_templates = {} | |
159 | ||
160 | assert type(ft) is barectf_config.StructureFieldType | |
161 | assert len(self._names) == 0 | |
2394a4b4 PP |
162 | ops = self._build_for_ft(ft, name, spec_serialize_write_templates) |
163 | assert len(ops) == 1 | |
164 | assert type(ops[0]) is _CompoundOp | |
165 | return typing.cast(_CompoundOp, ops[0]) | |
d6483c83 | 166 | |
2394a4b4 | 167 | # Creates and returns the operation(s) for a given field type `ft` |
d6483c83 PP |
168 | # named `name`. |
169 | # | |
2394a4b4 PP |
170 | # See build_for_root_ft() for `spec_serialize_write_templates`. |
171 | def _build_for_ft(self, ft: barectf_config._FieldType, name: str, | |
172 | spec_serialize_write_templates: _SpecSerializeWriteTemplates) -> List[_Op]: | |
1624d186 | 173 | def top_name() -> str: |
d6483c83 PP |
174 | return self._names[-1] |
175 | ||
2394a4b4 PP |
176 | # Creates and returns a "write" operation for the field type |
177 | # `ft`. | |
d6483c83 PP |
178 | # |
179 | # This function considers `spec_serialize_write_templates` to | |
180 | # override generic templates. | |
2394a4b4 | 181 | def create_write_op(ft: barectf_config._FieldType) -> _WriteOp: |
d6483c83 PP |
182 | assert type(ft) is not barectf_config.StructureFieldType |
183 | offset_in_byte = self._offset_in_byte | |
184 | ||
185 | if isinstance(ft, barectf_config._BitArrayFieldType): | |
1624d186 | 186 | self._offset_in_byte = Count((self._offset_in_byte + ft.size) % 8) |
d6483c83 | 187 | |
1624d186 | 188 | serialize_write_templ: Optional[barectf_template._Template] = None |
d6483c83 PP |
189 | |
190 | if len(self._names) == 2: | |
191 | serialize_write_templ = spec_serialize_write_templates.get(top_name()) | |
192 | ||
193 | if serialize_write_templ is None: | |
194 | if isinstance(ft, barectf_config._IntegerFieldType): | |
195 | serialize_write_templ = self._cg._serialize_write_int_statements_templ | |
196 | elif type(ft) is barectf_config.RealFieldType: | |
197 | serialize_write_templ = self._cg._serialize_write_real_statements_templ | |
198 | else: | |
199 | assert type(ft) is barectf_config.StringFieldType | |
200 | serialize_write_templ = self._cg._serialize_write_string_statements_templ | |
201 | ||
202 | size_write_templ = None | |
e5aa0be3 | 203 | |
d6483c83 PP |
204 | if isinstance(ft, barectf_config._BitArrayFieldType): |
205 | size_write_templ = self._cg._size_write_bit_array_statements_templ | |
206 | elif type(ft) is barectf_config.StringFieldType: | |
207 | size_write_templ = self._cg._size_write_string_statements_templ | |
208 | ||
2394a4b4 PP |
209 | return _WriteOp(offset_in_byte, ft, self._names, |
210 | _OpTemplates(serialize_write_templ, size_write_templ)) | |
d6483c83 | 211 | |
2394a4b4 | 212 | # Creates and returns an "align" operation for the field type |
d6483c83 PP |
213 | # `ft` if needed. |
214 | # | |
215 | # This function updates the builder's state. | |
2394a4b4 PP |
216 | def try_create_align_op(alignment: Alignment, do_align: bool, |
217 | ft: barectf_config._FieldType) -> Optional[_AlignOp]: | |
1624d186 PP |
218 | def align(v: Count, alignment: Alignment) -> Count: |
219 | return Count((v + (alignment - 1)) & -alignment) | |
d6483c83 PP |
220 | |
221 | offset_in_byte = self._offset_in_byte | |
1624d186 | 222 | self._offset_in_byte = Count(align(self._offset_in_byte, alignment) % 8) |
d6483c83 PP |
223 | |
224 | if do_align and alignment > 1: | |
2394a4b4 PP |
225 | return _AlignOp(offset_in_byte, ft, self._names, |
226 | _OpTemplates(self._cg._serialize_align_statements_templ, | |
227 | self._cg._size_align_statements_templ), | |
228 | alignment) | |
229 | ||
230 | return None | |
acfb8213 | 231 | |
d6483c83 PP |
232 | # Returns whether or not, considering the alignment requirement |
233 | # `align_req` and the builder's current state, we must create | |
234 | # and append an "align" operation. | |
1624d186 PP |
235 | def must_align(align_req: Alignment) -> bool: |
236 | return self._last_alignment != align_req or typing.cast(Count, self._last_bit_array_size) % align_req != 0 | |
acfb8213 | 237 | |
d6483c83 PP |
238 | # push field type's name to the builder's name stack initially |
239 | self._names.append(name) | |
acfb8213 | 240 | |
2394a4b4 PP |
241 | # operations to return |
242 | ops: List[_Op] = [] | |
243 | ||
4810b707 | 244 | if isinstance(ft, (barectf_config.StringFieldType, barectf_config._ArrayFieldType)): |
d6483c83 PP |
245 | assert type(ft) is barectf_config.StringFieldType or top_name() == 'uuid' |
246 | ||
247 | # strings and arrays are always byte-aligned | |
1624d186 PP |
248 | do_align = must_align(Alignment(8)) |
249 | self._last_alignment = Alignment(8) | |
250 | self._last_bit_array_size = Count(8) | |
2394a4b4 PP |
251 | op = try_create_align_op(Alignment(8), do_align, ft) |
252 | ||
253 | if op is not None: | |
254 | ops.append(op) | |
255 | ||
256 | ops.append(create_write_op(ft)) | |
4810b707 | 257 | else: |
d6483c83 | 258 | do_align = must_align(ft.alignment) |
4810b707 PP |
259 | self._last_alignment = ft.alignment |
260 | ||
261 | if type(ft) is barectf_config.StructureFieldType: | |
2394a4b4 | 262 | # reset last bit array size |
1624d186 | 263 | self._last_bit_array_size = typing.cast(Count, ft.alignment) |
acfb8213 | 264 | else: |
1624d186 PP |
265 | assert isinstance(ft, barectf_config._BitArrayFieldType) |
266 | ft = typing.cast(barectf_config._BitArrayFieldType, ft) | |
4810b707 | 267 | self._last_bit_array_size = ft.size |
70e191bd | 268 | |
2394a4b4 | 269 | init_align_op = try_create_align_op(ft.alignment, do_align, ft) |
70e191bd | 270 | |
4810b707 | 271 | if type(ft) is barectf_config.StructureFieldType: |
1624d186 | 272 | ft = typing.cast(barectf_config.StructureFieldType, ft) |
2394a4b4 PP |
273 | subops: List[_Op] = [] |
274 | ||
275 | if init_align_op is not None: | |
276 | # append structure field's alignment as a suboperation | |
277 | subops.append(init_align_op) | |
278 | ||
279 | # append suboperations for each member | |
4810b707 | 280 | for member_name, member in ft.members.items(): |
2394a4b4 PP |
281 | subops += self._build_for_ft(member.field_type, member_name, |
282 | spec_serialize_write_templates) | |
283 | ||
284 | # create structre field's compound operation | |
285 | ops.append(_CompoundOp(ft, self._names, | |
286 | _OpTemplates(self._cg._serialize_write_struct_statements_templ, | |
287 | self._cg._size_write_struct_statements_templ), | |
288 | subops)) | |
70e191bd | 289 | else: |
2394a4b4 PP |
290 | # leaf field: align + write |
291 | if init_align_op is not None: | |
292 | ops.append(init_align_op) | |
293 | ||
294 | ops.append(create_write_op(ft)) | |
d6483c83 PP |
295 | |
296 | # exiting for this field type: pop its name | |
297 | del self._names[-1] | |
70e191bd | 298 | |
2394a4b4 PP |
299 | return ops |
300 | ||
301 | ||
302 | _OptCompoundOp = Optional[_CompoundOp] | |
303 | ||
70e191bd | 304 | |
d6483c83 PP |
305 | # The operations for an event. |
306 | # | |
307 | # The available operations are: | |
308 | # | |
2394a4b4 PP |
309 | # * Specific context operation. |
310 | # * Payload operation. | |
1624d186 | 311 | class _EvOps: |
2394a4b4 PP |
312 | def __init__(self, spec_ctx_op: _OptCompoundOp, payload_op: _OptCompoundOp): |
313 | self._spec_ctx_op = spec_ctx_op | |
314 | self._payload_op = payload_op | |
70e191bd | 315 | |
d6483c83 | 316 | @property |
2394a4b4 PP |
317 | def spec_ctx_op(self) -> _OptCompoundOp: |
318 | return self._spec_ctx_op | |
acfb8213 | 319 | |
d6483c83 | 320 | @property |
2394a4b4 PP |
321 | def payload_op(self) -> _OptCompoundOp: |
322 | return self._payload_op | |
acfb8213 | 323 | |
70e191bd | 324 | |
1624d186 PP |
325 | _EvOpsMap = Mapping[barectf_config.EventType, _EvOps] |
326 | ||
327 | ||
d6483c83 PP |
328 | # The operations for a stream. |
329 | # | |
330 | # The available operations are: | |
331 | # | |
2394a4b4 PP |
332 | # * Packet header operation. |
333 | # * Packet context operation. | |
334 | # * Event header operation. | |
335 | # * Event common context operation. | |
1624d186 | 336 | # * Event operations (`_EvOps`). |
d6483c83 | 337 | class _StreamOps: |
2394a4b4 PP |
338 | def __init__(self, pkt_header_op: _OptCompoundOp, pkt_ctx_op: _CompoundOp, |
339 | ev_header_op: _OptCompoundOp, ev_common_ctx_op: _OptCompoundOp, ev_ops: _EvOpsMap): | |
340 | self._pkt_header_op = pkt_header_op | |
341 | self._pkt_ctx_op = pkt_ctx_op | |
342 | self._ev_header_op = ev_header_op | |
343 | self._ev_common_ctx_op = ev_common_ctx_op | |
344 | self._ev_ops = ev_ops | |
70e191bd | 345 | |
d6483c83 | 346 | @property |
2394a4b4 PP |
347 | def pkt_header_op(self) -> _OptCompoundOp: |
348 | return self._pkt_header_op | |
d6483c83 PP |
349 | |
350 | @property | |
2394a4b4 PP |
351 | def pkt_ctx_op(self) -> _CompoundOp: |
352 | return self._pkt_ctx_op | |
d6483c83 PP |
353 | |
354 | @property | |
2394a4b4 PP |
355 | def ev_header_op(self) -> _OptCompoundOp: |
356 | return self._ev_header_op | |
d6483c83 PP |
357 | |
358 | @property | |
2394a4b4 PP |
359 | def ev_common_ctx_op(self) -> _OptCompoundOp: |
360 | return self._ev_common_ctx_op | |
d6483c83 PP |
361 | |
362 | @property | |
1624d186 | 363 | def ev_ops(self) -> _EvOpsMap: |
d6483c83 PP |
364 | return self._ev_ops |
365 | ||
366 | ||
367 | # The C variable name prefixes for the six kinds of root field types. | |
1b49c7b8 | 368 | class _RootFtPrefixes: |
1c650e47 PP |
369 | PH = 'ph' |
370 | PC = 'pc' | |
371 | EH = 'eh' | |
372 | ECC = 'ecc' | |
373 | SC = 'sc' | |
374 | P = 'p' | |
d6483c83 PP |
375 | |
376 | ||
377 | # The human-readable names of the `_RootFtPrefixes` members. | |
378 | _ROOT_FT_PREFIX_NAMES = { | |
1c650e47 PP |
379 | _RootFtPrefixes.PH: 'packet header', |
380 | _RootFtPrefixes.PC: 'packet context', | |
381 | _RootFtPrefixes.EH: 'event header', | |
382 | _RootFtPrefixes.ECC: 'event common context', | |
383 | _RootFtPrefixes.SC: 'specific context', | |
384 | _RootFtPrefixes.P: 'payload', | |
acfb8213 | 385 | } |
e5aa0be3 PP |
386 | |
387 | ||
d6483c83 PP |
388 | # A named function parameter for a given field type. |
389 | _FtParam = collections.namedtuple('_FtParam', ['ft', 'name']) | |
390 | ||
391 | ||
2d18e033 PP |
392 | # C type abstract base class. |
393 | class _CType: | |
394 | def __init__(self, is_const: bool): | |
395 | self._is_const = is_const | |
396 | ||
397 | @property | |
398 | def is_const(self) -> bool: | |
399 | return self._is_const | |
400 | ||
401 | ||
402 | # Arithmetic C type. | |
403 | class _ArithCType(_CType): | |
404 | def __init__(self, name: str, is_const: bool): | |
405 | super().__init__(is_const) | |
406 | self._name = name | |
407 | ||
408 | @property | |
409 | def name(self) -> str: | |
410 | return self._name | |
411 | ||
412 | def __str__(self) -> str: | |
413 | return f'{"const " if self._is_const else ""}{self._name}' | |
414 | ||
415 | ||
416 | # Pointer C type. | |
417 | class _PointerCType(_CType): | |
418 | def __init__(self, pointed_c_type: _CType, is_const: bool): | |
419 | super().__init__(is_const) | |
420 | self._pointed_c_type = pointed_c_type | |
421 | ||
422 | @property | |
423 | def pointed_c_type(self) -> _CType: | |
424 | return self._pointed_c_type | |
425 | ||
426 | def __str__(self) -> str: | |
427 | s = str(self._pointed_c_type) | |
428 | ||
429 | if not s.endswith('*'): | |
430 | s += ' ' | |
431 | ||
432 | s += '*' | |
433 | ||
434 | if self._is_const: | |
435 | s += ' const' | |
436 | ||
437 | return s | |
438 | ||
439 | ||
d6483c83 PP |
440 | # A C code generator. |
441 | # | |
442 | # Such a code generator can generate: | |
443 | # | |
728fc4a7 PP |
444 | # * The bitfield header (gen_bitfield_header()). |
445 | # * The public header (gen_header()). | |
446 | # * The source code (gen_src()). | |
447 | class _CodeGen: | |
1624d186 | 448 | def __init__(self, cfg: barectf_config.Configuration): |
e5aa0be3 | 449 | self._cfg = cfg |
d6483c83 | 450 | self._iden_prefix = cfg.options.code_generation_options.identifier_prefix |
1624d186 | 451 | self._templ_filters: Mapping[str, Callable[..., Any]] = { |
d6483c83 PP |
452 | 'ft_c_type': self._ft_c_type, |
453 | 'open_func_params_str': self._open_func_params_str, | |
454 | 'trace_func_params_str': self._trace_func_params_str, | |
455 | 'serialize_ev_common_ctx_func_params_str': self._serialize_ev_common_ctx_func_params_str, | |
1b49c7b8 | 456 | } |
d6483c83 PP |
457 | self._func_proto_params_templ = self._create_template('func-proto-params.j2') |
458 | self._serialize_align_statements_templ = self._create_template('serialize-align-statements.j2') | |
459 | self._serialize_write_int_statements_templ = self._create_template('serialize-write-int-statements.j2') | |
460 | self._serialize_write_real_statements_templ = self._create_template('serialize-write-real-statements.j2') | |
461 | self._serialize_write_string_statements_templ = self._create_template('serialize-write-string-statements.j2') | |
2394a4b4 | 462 | self._serialize_write_struct_statements_templ = self._create_template('serialize-write-struct-statements.j2') |
d6483c83 PP |
463 | self._serialize_write_magic_statements_templ = self._create_template('serialize-write-magic-statements.j2') |
464 | self._serialize_write_uuid_statements_templ = self._create_template('serialize-write-uuid-statements.j2') | |
465 | self._serialize_write_stream_type_id_statements_templ = self._create_template('serialize-write-stream-type-id-statements.j2') | |
466 | self._serialize_write_time_statements_templ = self._create_template('serialize-write-time-statements.j2') | |
467 | self._serialize_write_packet_size_statements_templ = self._create_template('serialize-write-packet-size-statements.j2') | |
468 | self._serialize_write_skip_save_statements_templ = self._create_template('serialize-write-skip-save-statements.j2') | |
469 | self._serialize_write_ev_type_id_statements_templ = self._create_template('serialize-write-ev-type-id-statements.j2') | |
470 | self._size_align_statements_templ = self._create_template('size-align-statements.j2') | |
471 | self._size_write_bit_array_statements_templ = self._create_template('size-write-bit-array-statements.j2') | |
472 | self._size_write_string_statements_templ = self._create_template('size-write-string-statements.j2') | |
2394a4b4 | 473 | self._size_write_struct_statements_templ = self._create_template('size-write-struct-statements.j2') |
d6483c83 PP |
474 | |
475 | # Creates and returns a template named `name` which is a file | |
476 | # template if `is_file_template` is `True`. | |
477 | # | |
478 | # `name` is the file name, including the `.j2` extension, within the | |
479 | # `c` directory. | |
480 | # | |
481 | # Such a template has the filters custom filters | |
482 | # `self._templ_filters`. | |
1624d186 PP |
483 | def _create_template_base(self, name: str, |
484 | is_file_template: bool) -> barectf_template._Template: | |
d6483c83 PP |
485 | return barectf_template._Template(f'c/{name}', is_file_template, self._cfg, |
486 | self._templ_filters) | |
487 | ||
488 | # Creates and returns a non-file template named `name`. | |
489 | # | |
490 | # See _create_template_base() for `name`. | |
8c7c6ed2 | 491 | def _create_template(self, name: str) -> barectf_template._Template: |
d6483c83 | 492 | return self._create_template_base(name, False) |
8c7c6ed2 | 493 | |
d6483c83 PP |
494 | # Creates and returns a file template named `name`. |
495 | # | |
496 | # See _create_template_base() for `name`. | |
8c7c6ed2 | 497 | def _create_file_template(self, name: str) -> barectf_template._Template: |
d6483c83 | 498 | return self._create_template_base(name, True) |
8c7c6ed2 | 499 | |
d6483c83 | 500 | # Trace type of this code generator's barectf configuration. |
4810b707 | 501 | @property |
1624d186 | 502 | def _trace_type(self) -> barectf_config.TraceType: |
4810b707 | 503 | return self._cfg.trace.type |
27bc6f1e | 504 | |
2d18e033 PP |
505 | # Returns the C type for the field type `ft`, making it `const` if |
506 | # `is_const` is `True`. | |
507 | def _ft_c_type(self, ft: barectf_config._FieldType, is_const: bool = False): | |
4810b707 | 508 | if isinstance(ft, barectf_config._IntegerFieldType): |
1624d186 | 509 | ft = typing.cast(barectf_config._IntegerFieldType, ft) |
4810b707 PP |
510 | sign_prefix = 'u' if isinstance(ft, barectf_config.UnsignedIntegerFieldType) else '' |
511 | ||
512 | if ft.size <= 8: | |
513 | sz = 8 | |
514 | elif ft.size <= 16: | |
515 | sz = 16 | |
516 | elif ft.size <= 32: | |
517 | sz = 32 | |
518 | else: | |
d6483c83 | 519 | assert ft.size <= 64 |
4810b707 PP |
520 | sz = 64 |
521 | ||
2d18e033 | 522 | return _ArithCType(f'{sign_prefix}int{sz}_t', is_const) |
4810b707 | 523 | elif type(ft) is barectf_config.RealFieldType: |
1624d186 PP |
524 | ft = typing.cast(barectf_config.RealFieldType, ft) |
525 | ||
4810b707 | 526 | if ft.size == 32 and ft.alignment == 32: |
2d18e033 | 527 | s = 'float' |
4810b707 | 528 | elif ft.size == 64 and ft.alignment == 64: |
2d18e033 | 529 | s = 'double' |
4810b707 | 530 | else: |
2d18e033 | 531 | s = 'uint64_t' |
e18cf9d6 | 532 | |
2d18e033 | 533 | return _ArithCType(s, is_const) |
e5aa0be3 | 534 | else: |
4810b707 | 535 | assert type(ft) is barectf_config.StringFieldType |
2d18e033 | 536 | return _PointerCType(_ArithCType('char', True), is_const) |
e5aa0be3 | 537 | |
d6483c83 PP |
538 | # Returns the function prototype parameters for the members of the |
539 | # root structure field type `root_ft`. | |
540 | # | |
541 | # Each parameter has the prefix `name_prefix` followed with `_`. | |
542 | # | |
543 | # Members of which the name is in `exclude_set` are excluded. | |
1624d186 PP |
544 | def _proto_params_str(self, root_ft: Optional[barectf_config.StructureFieldType], |
545 | name_prefix: str, const_params: bool, | |
546 | exclude_set: Optional[Set[str]] = None, only_dyn: bool = False) -> str: | |
d6483c83 | 547 | if root_ft is None: |
1624d186 | 548 | return '' |
e5aa0be3 | 549 | |
4810b707 PP |
550 | if exclude_set is None: |
551 | exclude_set = set() | |
552 | ||
d6483c83 | 553 | params = [] |
e5aa0be3 | 554 | |
d6483c83 | 555 | for member_name, member in root_ft.members.items(): |
4810b707 | 556 | if member_name in exclude_set: |
e5aa0be3 PP |
557 | continue |
558 | ||
b622b24f PP |
559 | if only_dyn and not member.field_type.size_is_dynamic: |
560 | continue | |
561 | ||
d6483c83 | 562 | params.append(_FtParam(member.field_type, member_name)) |
e5aa0be3 | 563 | |
e18cf9d6 PP |
564 | return self._func_proto_params_templ.render(params=params, prefix=name_prefix, |
565 | const_params=const_params) | |
4810b707 | 566 | |
d6483c83 PP |
567 | # Returns the packet opening function prototype parameters for the |
568 | # stream type `stream_type`. | |
1624d186 PP |
569 | def _open_func_params_str(self, stream_type: barectf_config.StreamType, |
570 | const_params: bool) -> str: | |
d6483c83 | 571 | parts = [] |
1c650e47 | 572 | parts.append(self._proto_params_str(self._trace_type._pkt_header_ft, _RootFtPrefixes.PH, |
e18cf9d6 | 573 | const_params, {'magic', 'stream_id', 'uuid'})) |
e5aa0be3 | 574 | |
4810b707 PP |
575 | exclude_set = { |
576 | 'timestamp_begin', | |
577 | 'timestamp_end', | |
578 | 'packet_size', | |
579 | 'content_size', | |
580 | 'events_discarded', | |
581 | } | |
1c650e47 | 582 | parts.append(self._proto_params_str(stream_type._pkt_ctx_ft, _RootFtPrefixes.PC, |
e18cf9d6 | 583 | const_params, exclude_set)) |
d6483c83 | 584 | return ''.join(parts) |
e5aa0be3 | 585 | |
d6483c83 PP |
586 | # Returns the tracing function prototype parameters for the stream |
587 | # and event types `stream_ev_types`. | |
1624d186 PP |
588 | def _trace_func_params_str(self, stream_ev_types: Tuple[barectf_config.StreamType, |
589 | barectf_config.EventType], | |
590 | const_params: bool, only_dyn: bool = False): | |
d6483c83 PP |
591 | stream_type = stream_ev_types[0] |
592 | ev_type = stream_ev_types[1] | |
593 | parts = [] | |
e5aa0be3 | 594 | |
4810b707 | 595 | if stream_type._ev_header_ft is not None: |
1c650e47 | 596 | parts.append(self._proto_params_str(stream_type._ev_header_ft, _RootFtPrefixes.EH, |
b622b24f PP |
597 | const_params, {'id', 'timestamp'}, |
598 | only_dyn=only_dyn)) | |
4810b707 PP |
599 | |
600 | if stream_type.event_common_context_field_type is not None: | |
d6483c83 | 601 | parts.append(self._proto_params_str(stream_type.event_common_context_field_type, |
b622b24f PP |
602 | _RootFtPrefixes.ECC, const_params, |
603 | only_dyn=only_dyn)) | |
4810b707 PP |
604 | |
605 | if ev_type.specific_context_field_type is not None: | |
d6483c83 | 606 | parts.append(self._proto_params_str(ev_type.specific_context_field_type, |
b622b24f PP |
607 | _RootFtPrefixes.SC, const_params, |
608 | only_dyn=only_dyn)) | |
4810b707 PP |
609 | |
610 | if ev_type.payload_field_type is not None: | |
e18cf9d6 | 611 | parts.append(self._proto_params_str(ev_type.payload_field_type, _RootFtPrefixes.P, |
b622b24f | 612 | const_params, only_dyn=only_dyn)) |
e5aa0be3 | 613 | |
d6483c83 | 614 | return ''.join(parts) |
4810b707 | 615 | |
d6483c83 PP |
616 | # Returns the event header serialization function prototype |
617 | # parameters for the stream type `stream_type`. | |
1624d186 PP |
618 | def _serialize_ev_common_ctx_func_params_str(self, stream_type: barectf_config.StreamType, |
619 | const_params: bool) -> str: | |
d6483c83 | 620 | return self._proto_params_str(stream_type.event_common_context_field_type, |
324b1c40 | 621 | _RootFtPrefixes.ECC, const_params) |
e5aa0be3 | 622 | |
d6483c83 | 623 | # Generates the bitfield header file contents. |
1624d186 | 624 | def gen_bitfield_header(self) -> str: |
d6483c83 | 625 | return self._create_file_template('bitfield.h.j2').render() |
e5aa0be3 | 626 | |
d6483c83 | 627 | # Generates the public header file contents. |
1624d186 | 628 | def gen_header(self) -> str: |
d6483c83 | 629 | return self._create_file_template('barectf.h.j2').render(root_ft_prefixes=_RootFtPrefixes) |
3cb793a1 | 630 | |
d6483c83 | 631 | # Generates the source code file contents. |
1624d186 | 632 | def gen_src(self, header_file_name: str, bitfield_header_file_name: str) -> str: |
d6483c83 PP |
633 | # Creates and returns the operations for all the stream and for |
634 | # all their events. | |
1624d186 | 635 | def create_stream_ops() -> Mapping[barectf_config.StreamType, _StreamOps]: |
d6483c83 PP |
636 | stream_ser_ops = {} |
637 | ||
638 | for stream_type in self._trace_type.stream_types: | |
2394a4b4 PP |
639 | pkt_header_ser_op = None |
640 | builder = _OpBuilder(self) | |
d6483c83 PP |
641 | pkt_header_ft = self._trace_type._pkt_header_ft |
642 | ||
2394a4b4 | 643 | # packet header operations |
d6483c83 PP |
644 | if pkt_header_ft is not None: |
645 | spec_serialize_write_templates = { | |
646 | 'magic': self._serialize_write_magic_statements_templ, | |
647 | 'uuid': self._serialize_write_uuid_statements_templ, | |
648 | 'stream_id': self._serialize_write_stream_type_id_statements_templ, | |
649 | } | |
2394a4b4 PP |
650 | pkt_header_ser_op = builder.build_for_root_ft(pkt_header_ft, |
651 | _RootFtPrefixes.PH, | |
652 | spec_serialize_write_templates) | |
d6483c83 | 653 | |
2394a4b4 | 654 | # packet context operations |
d6483c83 PP |
655 | spec_serialize_write_templates = { |
656 | 'timestamp_begin': self._serialize_write_time_statements_templ, | |
657 | 'packet_size': self._serialize_write_packet_size_statements_templ, | |
658 | 'timestamp_end': self._serialize_write_skip_save_statements_templ, | |
659 | 'events_discarded': self._serialize_write_skip_save_statements_templ, | |
660 | 'content_size': self._serialize_write_skip_save_statements_templ, | |
661 | } | |
2394a4b4 PP |
662 | pkt_ctx_ser_op = builder.build_for_root_ft(stream_type._pkt_ctx_ft, |
663 | _RootFtPrefixes.PC, | |
664 | spec_serialize_write_templates) | |
d6483c83 | 665 | |
2394a4b4 PP |
666 | # event header operationss |
667 | builder = _OpBuilder(self) | |
668 | ev_header_ser_op = None | |
d6483c83 PP |
669 | |
670 | if stream_type._ev_header_ft is not None: | |
671 | spec_serialize_write_templates = { | |
672 | 'timestamp': self._serialize_write_time_statements_templ, | |
673 | 'id': self._serialize_write_ev_type_id_statements_templ, | |
674 | } | |
2394a4b4 PP |
675 | ev_header_ser_op = builder.build_for_root_ft(stream_type._ev_header_ft, |
676 | _RootFtPrefixes.EH, | |
677 | spec_serialize_write_templates) | |
d6483c83 | 678 | |
2394a4b4 PP |
679 | # event common context operations |
680 | ev_common_ctx_ser_op = None | |
d6483c83 PP |
681 | |
682 | if stream_type.event_common_context_field_type is not None: | |
2394a4b4 PP |
683 | ev_common_ctx_ser_op = builder.build_for_root_ft(stream_type.event_common_context_field_type, |
684 | _RootFtPrefixes.ECC) | |
d6483c83 | 685 | |
2394a4b4 | 686 | # operations specific to each event type |
d6483c83 PP |
687 | ev_ser_ops = {} |
688 | ||
689 | for ev_type in stream_type.event_types: | |
690 | ev_builder = copy.copy(builder) | |
691 | ||
2394a4b4 PP |
692 | # specific context operations |
693 | spec_ctx_ser_op = None | |
d6483c83 PP |
694 | |
695 | if ev_type.specific_context_field_type is not None: | |
2394a4b4 PP |
696 | spec_ctx_ser_op = ev_builder.build_for_root_ft(ev_type.specific_context_field_type, |
697 | _RootFtPrefixes.SC) | |
d6483c83 | 698 | |
2394a4b4 PP |
699 | # payload operations |
700 | payload_ser_op = None | |
d6483c83 PP |
701 | |
702 | if ev_type.payload_field_type is not None: | |
2394a4b4 PP |
703 | payload_ser_op = ev_builder.build_for_root_ft(ev_type.payload_field_type, |
704 | _RootFtPrefixes.P) | |
d6483c83 | 705 | |
2394a4b4 | 706 | ev_ser_ops[ev_type] = _EvOps(spec_ctx_ser_op, payload_ser_op) |
d6483c83 | 707 | |
2394a4b4 PP |
708 | stream_ser_ops[stream_type] = _StreamOps(pkt_header_ser_op, pkt_ctx_ser_op, |
709 | ev_header_ser_op, ev_common_ctx_ser_op, | |
d6483c83 PP |
710 | ev_ser_ops) |
711 | ||
712 | return stream_ser_ops | |
713 | ||
714 | # Returns the "write" operation for the packet context member | |
715 | # named `member_name` within the stream type `stream_type`. | |
1624d186 PP |
716 | def stream_op_pkt_ctx_op(stream_type: barectf_config.StreamType, member_name: str) -> _Op: |
717 | ret_op = None | |
718 | ||
2394a4b4 | 719 | for op in stream_ops[stream_type].pkt_ctx_op.subops: |
d6483c83 | 720 | if op.top_name == member_name and type(op) is _WriteOp: |
1624d186 PP |
721 | ret_op = op |
722 | break | |
723 | ||
724 | assert ret_op is not None | |
725 | return typing.cast(_Op, ret_op) | |
d6483c83 PP |
726 | |
727 | stream_ops = create_stream_ops() | |
c268d669 PP |
728 | c_src = self._create_file_template('barectf.c.j2').render(header_file_name=header_file_name, |
729 | bitfield_header_file_name=bitfield_header_file_name, | |
730 | root_ft_prefixes=_RootFtPrefixes, | |
731 | root_ft_prefix_names=_ROOT_FT_PREFIX_NAMES, | |
732 | stream_ops=stream_ops, | |
733 | stream_op_pkt_ctx_op=stream_op_pkt_ctx_op) | |
734 | ||
735 | # Jinja 2 makes it hard to have multiple contiguous blocks | |
736 | # delimited with empty lines when using a for loop, while not | |
737 | # also having an empty line at the end. | |
738 | # | |
739 | # Therefore, we often get this rendered pattern: | |
740 | # | |
741 | # /* ... */ | |
742 | # ...; | |
743 | # ...; | |
744 | # | |
745 | # /* ... */ | |
746 | # ...; | |
747 | # ...; | |
748 | # ...; | |
749 | # | |
750 | # } | |
751 | # | |
752 | # It's ugly, so fix it here. | |
753 | return re.sub(r'(\n)\s*\n(\s*})', r'\1\2', c_src) |