Commit | Line | Data |
---|---|---|
4810b707 PP |
1 | # The MIT License (MIT) |
2 | # | |
3 | # Copyright (c) 2015-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.config_parse_common as barectf_config_parse_common | |
25 | from barectf.config_parse_common import _ConfigurationParseError | |
26 | from barectf.config_parse_common import _append_error_ctx | |
2d55dc7d | 27 | from barectf.config_parse_common import _MapNode |
4810b707 | 28 | import barectf.config as barectf_config |
2d55dc7d | 29 | from barectf.config import _OptFt, _OptStructFt |
4810b707 PP |
30 | import collections |
31 | import uuid | |
2d55dc7d PP |
32 | from barectf.typing import Count, Alignment, VersionNumber |
33 | from typing import Optional, List, Dict, Any, TextIO, Set, Iterable, Callable, Tuple, Type | |
34 | import typing | |
4810b707 PP |
35 | |
36 | ||
37 | # A barectf 3 YAML configuration parser. | |
38 | # | |
39 | # When you build such a parser, it parses the configuration node `node` | |
40 | # (already loaded from the file having the path `path`) and creates a | |
41 | # corresponding `barectf.Configuration` object which you can get with | |
42 | # the `config` property. | |
43 | # | |
44 | # See the comments of _parse() for more implementation details about the | |
45 | # parsing stages and general strategy. | |
46 | class _Parser(barectf_config_parse_common._Parser): | |
47 | # Builds a barectf 3 YAML configuration parser and parses the root | |
2d55dc7d PP |
48 | # configuration node `node` (already loaded from the file-like |
49 | # object `root_file`). | |
50 | def __init__(self, root_file: TextIO, node: barectf_config_parse_common._ConfigNodeV3, | |
51 | with_pkg_include_dir: bool, inclusion_dirs: Optional[List[str]], | |
52 | ignore_include_not_found: bool): | |
53 | super().__init__(root_file, node, with_pkg_include_dir, inclusion_dirs, | |
54 | ignore_include_not_found, VersionNumber(3)) | |
55 | self._ft_cls_name_to_create_method: Dict[str, Callable[[_MapNode], barectf_config._FieldType]] = { | |
4810b707 PP |
56 | 'unsigned-integer': self._create_int_ft, |
57 | 'signed-integer': self._create_int_ft, | |
58 | 'unsigned-enumeration': self._create_enum_ft, | |
59 | 'signed-enumeration': self._create_enum_ft, | |
60 | 'real': self._create_real_ft, | |
61 | 'string': self._create_string_ft, | |
62 | 'static-array': self._create_static_array_ft, | |
63 | 'structure': self._create_struct_ft, | |
64 | } | |
65 | self._parse() | |
66 | ||
67 | # Validates the alignment `alignment`, raising a | |
68 | # `_ConfigurationParseError` exception using `ctx_obj_name` if it's | |
69 | # invalid. | |
70 | @staticmethod | |
2d55dc7d | 71 | def _validate_alignment(alignment: Alignment, ctx_obj_name: str): |
4810b707 PP |
72 | assert alignment >= 1 |
73 | ||
74 | # check for power of two | |
75 | if (alignment & (alignment - 1)) != 0: | |
76 | raise _ConfigurationParseError(ctx_obj_name, | |
77 | f'Invalid alignment (not a power of two): {alignment}') | |
78 | ||
79 | # Validates the TSDL identifier `iden`, raising a | |
80 | # `_ConfigurationParseError` exception using `ctx_obj_name` and | |
81 | # `prop` to format the message if it's invalid. | |
82 | @staticmethod | |
2d55dc7d | 83 | def _validate_iden(iden: str, ctx_obj_name: str, prop: str): |
4810b707 PP |
84 | assert type(iden) is str |
85 | ctf_keywords = { | |
86 | 'align', | |
87 | 'callsite', | |
88 | 'clock', | |
89 | 'enum', | |
90 | 'env', | |
91 | 'event', | |
92 | 'floating_point', | |
93 | 'integer', | |
94 | 'stream', | |
95 | 'string', | |
96 | 'struct', | |
97 | 'trace', | |
98 | 'typealias', | |
99 | 'typedef', | |
100 | 'variant', | |
101 | } | |
102 | ||
103 | if iden in ctf_keywords: | |
104 | msg = f'Invalid {prop} (not a valid identifier): `{iden}`' | |
105 | raise _ConfigurationParseError(ctx_obj_name, msg) | |
106 | ||
107 | @staticmethod | |
2d55dc7d | 108 | def _alignment_prop(ft_node: _MapNode, prop_name: str) -> Alignment: |
4810b707 PP |
109 | alignment = ft_node.get(prop_name) |
110 | ||
111 | if alignment is not None: | |
112 | _Parser._validate_alignment(alignment, '`prop_name` property') | |
113 | ||
2d55dc7d | 114 | return Alignment(alignment) |
4810b707 PP |
115 | |
116 | @property | |
2d55dc7d PP |
117 | def _trace_type_node(self) -> _MapNode: |
118 | return self.config_node['trace']['type'] | |
4810b707 PP |
119 | |
120 | @staticmethod | |
2d55dc7d | 121 | def _byte_order_from_node(node: str) -> barectf_config.ByteOrder: |
4810b707 PP |
122 | return { |
123 | 'big-endian': barectf_config.ByteOrder.BIG_ENDIAN, | |
124 | 'little-endian': barectf_config.ByteOrder.LITTLE_ENDIAN, | |
125 | }[node] | |
126 | ||
127 | # Creates a bit array field type having the type `ft_type` from the | |
128 | # bit array field type node `ft_node`, passing the additional | |
129 | # `*args` to ft_type.__init__(). | |
2d55dc7d PP |
130 | def _create_common_bit_array_ft(self, ft_node: _MapNode, |
131 | ft_type: Type[barectf_config._BitArrayFieldType], | |
132 | default_alignment: Optional[Alignment], | |
133 | *args) -> barectf_config._BitArrayFieldType: | |
4810b707 PP |
134 | byte_order = self._byte_order_from_node(ft_node['byte-order']) |
135 | alignment = self._alignment_prop(ft_node, 'alignment') | |
136 | ||
137 | if alignment is None: | |
138 | alignment = default_alignment | |
139 | ||
140 | return ft_type(ft_node['size'], byte_order, alignment, *args) | |
141 | ||
142 | # Creates an integer field type having the type `ft_type` from the | |
143 | # integer field type node `ft_node`, passing the additional `*args` | |
144 | # to ft_type.__init__(). | |
2d55dc7d PP |
145 | def _create_common_int_ft(self, ft_node: _MapNode, |
146 | ft_type: Type[barectf_config._IntegerFieldType], *args) -> barectf_config._IntegerFieldType: | |
4810b707 PP |
147 | preferred_display_base = { |
148 | 'binary': barectf_config.DisplayBase.BINARY, | |
149 | 'octal': barectf_config.DisplayBase.OCTAL, | |
150 | 'decimal': barectf_config.DisplayBase.DECIMAL, | |
151 | 'hexadecimal': barectf_config.DisplayBase.HEXADECIMAL, | |
152 | }[ft_node.get('preferred-display-base', 'decimal')] | |
2d55dc7d PP |
153 | return typing.cast(barectf_config._IntegerFieldType, |
154 | self._create_common_bit_array_ft(ft_node, ft_type, None, | |
155 | preferred_display_base, *args)) | |
4810b707 PP |
156 | |
157 | # Creates an integer field type from the unsigned/signed integer | |
158 | # field type node `ft_node`. | |
2d55dc7d | 159 | def _create_int_ft(self, ft_node: _MapNode) -> barectf_config._IntegerFieldType: |
4810b707 PP |
160 | ft_type = { |
161 | 'unsigned-integer': barectf_config.UnsignedIntegerFieldType, | |
162 | 'signed-integer': barectf_config.SignedIntegerFieldType, | |
163 | }[ft_node['class']] | |
164 | return self._create_common_int_ft(ft_node, ft_type) | |
165 | ||
166 | # Creates an enumeration field type from the unsigned/signed | |
167 | # enumeration field type node `ft_node`. | |
2d55dc7d | 168 | def _create_enum_ft(self, ft_node: _MapNode) -> barectf_config._EnumerationFieldType: |
4810b707 PP |
169 | ft_type = { |
170 | 'unsigned-enumeration': barectf_config.UnsignedEnumerationFieldType, | |
171 | 'signed-enumeration': barectf_config.SignedEnumerationFieldType, | |
172 | }[ft_node['class']] | |
173 | mappings = collections.OrderedDict() | |
174 | ||
175 | for label, mapping_node in ft_node.get('mappings', {}).items(): | |
176 | ranges = set() | |
177 | ||
178 | for range_node in mapping_node: | |
179 | if type(range_node) is list: | |
180 | ranges.add(barectf_config.EnumerationFieldTypeMappingRange(range_node[0], | |
181 | range_node[1])) | |
182 | else: | |
183 | assert type(range_node) is int | |
184 | ranges.add(barectf_config.EnumerationFieldTypeMappingRange(range_node, | |
185 | range_node)) | |
186 | ||
187 | mappings[label] = barectf_config.EnumerationFieldTypeMapping(ranges) | |
188 | ||
2d55dc7d PP |
189 | return typing.cast(barectf_config._EnumerationFieldType, |
190 | self._create_common_int_ft(ft_node, ft_type, | |
191 | barectf_config.EnumerationFieldTypeMappings(mappings))) | |
4810b707 PP |
192 | |
193 | # Creates a real field type from the real field type node `ft_node`. | |
2d55dc7d PP |
194 | def _create_real_ft(self, ft_node: _MapNode) -> barectf_config.RealFieldType: |
195 | return typing.cast(barectf_config.RealFieldType, | |
196 | self._create_common_bit_array_ft(ft_node, barectf_config.RealFieldType, | |
197 | Alignment(8))) | |
4810b707 PP |
198 | |
199 | # Creates a string field type from the string field type node | |
200 | # `ft_node`. | |
2d55dc7d | 201 | def _create_string_ft(self, ft_node: _MapNode) -> barectf_config.StringFieldType: |
4810b707 PP |
202 | return barectf_config.StringFieldType() |
203 | ||
204 | # Creates a static array field type from the static array field type | |
205 | # node `ft_node`. | |
2d55dc7d | 206 | def _create_static_array_ft(self, ft_node: _MapNode) -> barectf_config.StaticArrayFieldType: |
4810b707 PP |
207 | prop_name = 'element-field-type' |
208 | ||
209 | try: | |
210 | element_ft = self._create_ft(ft_node[prop_name]) | |
211 | except _ConfigurationParseError as exc: | |
212 | _append_error_ctx(exc, f'`{prop_name}` property') | |
213 | ||
214 | return barectf_config.StaticArrayFieldType(ft_node['length'], element_ft) | |
215 | ||
216 | # Creates structure field type members from the structure field type | |
217 | # members node `members_node`. | |
218 | # | |
219 | # `prop_name` is the name of the property of which `members_node` is | |
220 | # the value. | |
2d55dc7d | 221 | def _create_struct_ft_members(self, members_node: List[_MapNode], prop_name: str): |
4810b707 | 222 | members = collections.OrderedDict() |
2d55dc7d | 223 | member_names: Set[str] = set() |
4810b707 PP |
224 | |
225 | for member_node in members_node: | |
226 | member_name, member_node = list(member_node.items())[0] | |
227 | ||
228 | if member_name in member_names: | |
229 | raise _ConfigurationParseError(f'`{prop_name}` property', | |
230 | f'Duplicate member `{member_name}`') | |
231 | ||
232 | self._validate_iden(member_name, f'`{prop_name}` property', | |
233 | 'structure field type member name') | |
234 | member_names.add(member_name) | |
235 | ft_prop_name = 'field-type' | |
236 | ft_node = member_node[ft_prop_name] | |
237 | ||
238 | try: | |
239 | if ft_node['class'] in ['structure', 'static-array']: | |
240 | raise _ConfigurationParseError(f'`{ft_prop_name}` property', | |
241 | 'Nested structure and static array field types are not supported') | |
242 | ||
243 | try: | |
244 | member_ft = self._create_ft(ft_node) | |
245 | except _ConfigurationParseError as exc: | |
246 | exc._append_ctx(f'`{ft_prop_name}` property') | |
247 | except _ConfigurationParseError as exc: | |
248 | _append_error_ctx(exc, f'Structure field type member `{member_name}`') | |
249 | ||
250 | members[member_name] = barectf_config.StructureFieldTypeMember(member_ft) | |
251 | ||
252 | return barectf_config.StructureFieldTypeMembers(members) | |
253 | ||
254 | # Creates a structure field type from the structure field type node | |
255 | # `ft_node`. | |
2d55dc7d | 256 | def _create_struct_ft(self, ft_node: _MapNode) -> barectf_config.StructureFieldType: |
4810b707 PP |
257 | minimum_alignment = self._alignment_prop(ft_node, 'minimum-alignment') |
258 | ||
259 | if minimum_alignment is None: | |
260 | minimum_alignment = 1 | |
261 | ||
262 | members = None | |
263 | prop_name = 'members' | |
264 | members_node = ft_node.get(prop_name) | |
265 | ||
266 | if members_node is not None: | |
267 | members = self._create_struct_ft_members(members_node, prop_name) | |
268 | ||
269 | return barectf_config.StructureFieldType(minimum_alignment, members) | |
270 | ||
271 | # Creates a field type from the field type node `ft_node`. | |
2d55dc7d | 272 | def _create_ft(self, ft_node: _MapNode) -> barectf_config._FieldType: |
4810b707 PP |
273 | return self._ft_cls_name_to_create_method[ft_node['class']](ft_node) |
274 | ||
275 | # Creates a field type from the field type node `parent_node[key]` | |
276 | # if it exists. | |
2d55dc7d | 277 | def _try_create_ft(self, parent_node: _MapNode, key: str) -> _OptFt: |
4810b707 | 278 | if key not in parent_node: |
2d55dc7d | 279 | return None |
4810b707 PP |
280 | |
281 | try: | |
282 | return self._create_ft(parent_node[key]) | |
283 | except _ConfigurationParseError as exc: | |
284 | _append_error_ctx(exc, f'`{key}` property') | |
285 | ||
2d55dc7d PP |
286 | # satisfy static type checker (never reached) |
287 | raise | |
288 | ||
289 | # Like _try_create_ft(), but casts the result's type to | |
290 | # `barectf_config.StructureFieldType` to satisfy static type | |
291 | # checkers. | |
292 | def _try_create_struct_ft(self, parent_node: _MapNode, key: str) -> _OptStructFt: | |
293 | return typing.cast(barectf_config.StructureFieldType, | |
294 | self._try_create_ft(parent_node, key)) | |
295 | ||
4810b707 PP |
296 | # Returns the total number of members in the structure field type |
297 | # node `ft_node` if it exists, otherwise 0. | |
298 | @staticmethod | |
2d55dc7d | 299 | def _total_struct_ft_node_members(ft_node: Optional[_MapNode]) -> Count: |
4810b707 | 300 | if ft_node is None: |
2d55dc7d | 301 | return Count(0) |
4810b707 PP |
302 | |
303 | members_node = ft_node.get('members') | |
304 | ||
305 | if members_node is None: | |
2d55dc7d | 306 | return Count(0) |
4810b707 | 307 | |
2d55dc7d | 308 | return Count(len(members_node)) |
4810b707 PP |
309 | |
310 | # Creates an event type from the event type node `ev_type_node` | |
311 | # named `name`. | |
312 | # | |
313 | # `ev_member_count` is the total number of structure field type | |
314 | # members within the event type so far (from the common part in its | |
315 | # stream type). For example, if the stream type has a event header | |
316 | # field type with `id` and `timestamp` members, then | |
317 | # `ev_member_count` is 2. | |
2d55dc7d | 318 | def _create_ev_type(self, name: str, ev_type_node: _MapNode, ev_member_count: Count) -> barectf_config.EventType: |
4810b707 PP |
319 | try: |
320 | self._validate_iden(name, '`name` property', 'event type name') | |
321 | ||
322 | # make sure the event type is not empty | |
323 | spec_ctx_ft_prop_name = 'specific-context-field-type' | |
324 | payload_ft_prop_name = 'payload-field-type' | |
ae76a54b PP |
325 | ev_member_count = Count(ev_member_count + self._total_struct_ft_node_members(ev_type_node.get(spec_ctx_ft_prop_name))) |
326 | ev_member_count = Count(ev_member_count + self._total_struct_ft_node_members(ev_type_node.get(payload_ft_prop_name))) | |
4810b707 PP |
327 | |
328 | if ev_member_count == 0: | |
329 | raise _ConfigurationParseError('Event type', 'Event type is empty (no members).') | |
330 | ||
331 | # create event type | |
332 | return barectf_config.EventType(name, ev_type_node.get('log-level'), | |
2d55dc7d PP |
333 | self._try_create_struct_ft(ev_type_node, |
334 | spec_ctx_ft_prop_name), | |
335 | self._try_create_struct_ft(ev_type_node, | |
336 | payload_ft_prop_name)) | |
4810b707 PP |
337 | except _ConfigurationParseError as exc: |
338 | _append_error_ctx(exc, f'Event type `{name}`') | |
339 | ||
2d55dc7d PP |
340 | # satisfy static type checker (never reached) |
341 | raise | |
342 | ||
4810b707 PP |
343 | # Returns the effective feature field type for the field type |
344 | # node `parent_node[key]`, if any. | |
345 | # | |
346 | # Returns: | |
347 | # | |
348 | # If `parent_node[key]` is `False`: | |
349 | # `None`. | |
350 | # | |
351 | # If `parent_node[key]` is `True`: | |
352 | # `barectf_config.DEFAULT_FIELD_TYPE`. | |
353 | # | |
354 | # If `parent_node[key]` doesn't exist: | |
355 | # `none` (parameter). | |
356 | # | |
357 | # Otherwise: | |
358 | # A created field type. | |
2d55dc7d | 359 | def _feature_ft(self, parent_node: _MapNode, key: str, none: Any = None) -> Any: |
4810b707 PP |
360 | if key not in parent_node: |
361 | # missing: default feature field type | |
362 | return none | |
363 | ||
364 | ft_node = parent_node[key] | |
365 | assert ft_node is not None | |
366 | ||
367 | if ft_node is True: | |
368 | # default feature field type | |
369 | return barectf_config.DEFAULT_FIELD_TYPE | |
370 | ||
371 | if ft_node is False: | |
372 | # disabled feature | |
373 | return None | |
374 | ||
375 | assert type(ft_node) is collections.OrderedDict | |
376 | return self._create_ft(ft_node) | |
377 | ||
2d55dc7d | 378 | def _create_stream_type(self, name: str, stream_type_node: _MapNode) -> barectf_config.StreamType: |
4810b707 PP |
379 | try: |
380 | # validate stream type's name | |
381 | self._validate_iden(name, '`name` property', 'stream type name') | |
382 | ||
383 | # get default clock type, if any | |
384 | def_clk_type = None | |
385 | prop_name = '$default-clock-type-name' | |
386 | def_clk_type_name = stream_type_node.get(prop_name) | |
387 | ||
388 | if def_clk_type_name is not None: | |
389 | try: | |
390 | def_clk_type = self._clk_type(def_clk_type_name, prop_name) | |
391 | except _ConfigurationParseError as exc: | |
392 | _append_error_ctx(exc, f'`{prop_name}` property') | |
393 | ||
394 | # create feature field types | |
395 | pkt_total_size_ft = barectf_config.DEFAULT_FIELD_TYPE | |
396 | pkt_content_size_ft = barectf_config.DEFAULT_FIELD_TYPE | |
397 | pkt_beginning_time_ft = None | |
398 | pkt_end_time_ft = None | |
399 | pkt_discarded_events_counter_ft = None | |
400 | ev_type_id_ft = barectf_config.DEFAULT_FIELD_TYPE | |
401 | ev_time_ft = None | |
402 | ||
403 | if def_clk_type is not None: | |
404 | # The stream type has a default clock type. Initialize | |
405 | # the packet beginning time, packet end time, and event | |
406 | # time field types to default field types. | |
407 | # | |
408 | # This means your stream type node only needs a default | |
409 | # clock type name to enable those features | |
410 | # automatically. Those features do not add any parameter | |
411 | # to the tracing event functions. | |
412 | pkt_beginning_time_ft = barectf_config.DEFAULT_FIELD_TYPE | |
413 | pkt_end_time_ft = barectf_config.DEFAULT_FIELD_TYPE | |
414 | ev_time_ft = barectf_config.DEFAULT_FIELD_TYPE | |
415 | ||
416 | features_node = stream_type_node.get('$features') | |
417 | ||
418 | if features_node is not None: | |
419 | # create packet feature field types | |
420 | pkt_node = features_node.get('packet') | |
421 | ||
422 | if pkt_node is not None: | |
423 | pkt_total_size_ft = self._feature_ft(pkt_node, 'total-size-field-type', | |
424 | pkt_total_size_ft) | |
425 | pkt_content_size_ft = self._feature_ft(pkt_node, 'content-size-field-type', | |
426 | pkt_content_size_ft) | |
427 | pkt_beginning_time_ft = self._feature_ft(pkt_node, 'beginning-time-field-type', | |
428 | pkt_beginning_time_ft) | |
429 | pkt_end_time_ft = self._feature_ft(pkt_node, 'end-time-field-type', | |
430 | pkt_end_time_ft) | |
431 | pkt_discarded_events_counter_ft = self._feature_ft(pkt_node, | |
432 | 'discarded-events-counter-field-type', | |
433 | pkt_discarded_events_counter_ft) | |
434 | ||
435 | # create event feature field types | |
436 | ev_node = features_node.get('event') | |
437 | type_id_ft_prop_name = 'type-id-field-type' | |
438 | ||
439 | if ev_node is not None: | |
440 | ev_type_id_ft = self._feature_ft(ev_node, type_id_ft_prop_name, ev_type_id_ft) | |
441 | ev_time_ft = self._feature_ft(ev_node, 'time-field-type', ev_time_ft) | |
442 | ||
443 | ev_types_prop_name = 'event-types' | |
444 | ev_type_count = len(stream_type_node[ev_types_prop_name]) | |
445 | ||
446 | try: | |
447 | if ev_type_id_ft is None and ev_type_count > 1: | |
448 | raise _ConfigurationParseError(f'`{type_id_ft_prop_name}` property', | |
449 | 'Event type ID field type feature is required because stream type has more than one event type') | |
450 | ||
2d55dc7d PP |
451 | if isinstance(ev_type_id_ft, barectf_config._IntegerFieldType): |
452 | ev_type_id_int_ft = typing.cast(barectf_config._IntegerFieldType, ev_type_id_ft) | |
453 | ||
454 | if ev_type_count > (1 << ev_type_id_int_ft.size): | |
455 | raise _ConfigurationParseError(f'`{type_id_ft_prop_name}` property', | |
456 | f'Field type\'s size ({ev_type_id_int_ft.size} bits) is too small to accomodate {ev_type_count} event types') | |
4810b707 PP |
457 | except _ConfigurationParseError as exc: |
458 | exc._append_ctx('`event` property') | |
459 | _append_error_ctx(exc, '`$features` property') | |
460 | ||
461 | pkt_features = barectf_config.StreamTypePacketFeatures(pkt_total_size_ft, | |
462 | pkt_content_size_ft, | |
463 | pkt_beginning_time_ft, | |
464 | pkt_end_time_ft, | |
7fffc7d1 PP |
465 | pkt_discarded_events_counter_ft, |
466 | default_byte_order=self._target_byte_order) | |
467 | ev_features = barectf_config.StreamTypeEventFeatures(ev_type_id_ft, ev_time_ft, | |
468 | default_byte_order=self._target_byte_order) | |
4810b707 PP |
469 | features = barectf_config.StreamTypeFeatures(pkt_features, ev_features) |
470 | ||
471 | # create packet context (structure) field type extra members | |
472 | pkt_ctx_ft_extra_members = None | |
473 | prop_name = 'packet-context-field-type-extra-members' | |
474 | pkt_ctx_ft_extra_members_node = stream_type_node.get(prop_name) | |
475 | ||
476 | if pkt_ctx_ft_extra_members_node is not None: | |
477 | pkt_ctx_ft_extra_members = self._create_struct_ft_members(pkt_ctx_ft_extra_members_node, | |
478 | prop_name) | |
479 | ||
480 | # check for illegal packet context field type member names | |
481 | reserved_member_names = { | |
482 | 'packet_size', | |
483 | 'content_size', | |
484 | 'timestamp_begin', | |
485 | 'timestamp_end', | |
486 | 'events_discarded', | |
487 | 'packet_seq_num', | |
488 | } | |
489 | ||
490 | for member_name in pkt_ctx_ft_extra_members: | |
491 | if member_name in reserved_member_names: | |
492 | raise _ConfigurationParseError(f'`{prop_name}` property', | |
493 | f'Packet context field type member name `{member_name}` is reserved.') | |
494 | ||
495 | # create event types | |
2d55dc7d | 496 | ev_header_common_ctx_member_count = Count(0) |
4810b707 PP |
497 | |
498 | if ev_features.type_id_field_type is not None: | |
2d55dc7d | 499 | ev_header_common_ctx_member_count = Count(ev_header_common_ctx_member_count + 1) |
4810b707 PP |
500 | |
501 | if ev_features.time_field_type is not None: | |
2d55dc7d | 502 | ev_header_common_ctx_member_count = Count(ev_header_common_ctx_member_count + 1) |
4810b707 PP |
503 | |
504 | ev_common_ctx_ft_prop_name = 'event-common-context-field-type' | |
505 | ev_common_ctx_ft_node = stream_type_node.get(ev_common_ctx_ft_prop_name) | |
ae76a54b | 506 | ev_header_common_ctx_member_count = Count(ev_header_common_ctx_member_count + self._total_struct_ft_node_members(ev_common_ctx_ft_node)) |
4810b707 PP |
507 | ev_types = set() |
508 | ||
509 | for ev_name, ev_type_node in stream_type_node[ev_types_prop_name].items(): | |
510 | ev_types.add(self._create_ev_type(ev_name, ev_type_node, ev_header_common_ctx_member_count)) | |
511 | ||
512 | # create stream type | |
7fffc7d1 | 513 | return barectf_config.StreamType(name, ev_types, def_clk_type, features, None, |
4810b707 | 514 | pkt_ctx_ft_extra_members, |
2d55dc7d PP |
515 | self._try_create_struct_ft(stream_type_node, |
516 | ev_common_ctx_ft_prop_name)) | |
4810b707 PP |
517 | except _ConfigurationParseError as exc: |
518 | _append_error_ctx(exc, f'Stream type `{name}`') | |
519 | ||
2d55dc7d PP |
520 | # satisfy static type checker (never reached) |
521 | raise | |
522 | ||
523 | def _clk_type(self, name: str, prop_name: str) -> barectf_config.ClockType: | |
4810b707 PP |
524 | clk_type = self._clk_types.get(name) |
525 | ||
526 | if clk_type is None: | |
527 | raise _ConfigurationParseError(f'`{prop_name}` property', | |
528 | f'Clock type `{name}` does not exist') | |
529 | ||
530 | return clk_type | |
531 | ||
2d55dc7d | 532 | def _create_clk_type(self, name: str, clk_type_node: _MapNode) -> barectf_config.ClockType: |
4810b707 PP |
533 | self._validate_iden(name, '`name` property', 'clock type name') |
534 | clk_type_uuid = None | |
535 | uuid_node = clk_type_node.get('uuid') | |
536 | ||
537 | if uuid_node is not None: | |
538 | clk_type_uuid = uuid.UUID(uuid_node) | |
539 | ||
540 | offset_seconds = 0 | |
2d55dc7d | 541 | offset_cycles = Count(0) |
4810b707 PP |
542 | offset_node = clk_type_node.get('offset') |
543 | ||
544 | if offset_node is not None: | |
545 | offset_seconds = offset_node.get('seconds', 0) | |
2d55dc7d | 546 | offset_cycles = offset_node.get('cycles', Count(0)) |
4810b707 PP |
547 | |
548 | return barectf_config.ClockType(name, clk_type_node.get('frequency', int(1e9)), | |
549 | clk_type_uuid, clk_type_node.get('description'), | |
550 | clk_type_node.get('precision', 0), | |
551 | barectf_config.ClockTypeOffset(offset_seconds, offset_cycles), | |
552 | clk_type_node.get('origin-is-unix-epoch', False)) | |
553 | ||
554 | def _create_clk_types(self): | |
555 | self._clk_types = {} | |
556 | ||
557 | for clk_type_name, clk_type_node in self._trace_type_node.get('clock-types', {}).items(): | |
558 | self._clk_types[clk_type_name] = self._create_clk_type(clk_type_name, clk_type_node) | |
559 | ||
560 | def _create_trace_type(self): | |
561 | try: | |
562 | # create clock types (_create_stream_type() needs them) | |
563 | self._create_clk_types() | |
564 | ||
565 | # get UUID | |
566 | trace_type_uuid = None | |
567 | uuid_node = self._trace_type_node.get('uuid') | |
568 | ||
569 | if uuid_node is not None: | |
570 | if uuid_node == 'auto': | |
571 | trace_type_uuid = uuid.uuid1() | |
572 | else: | |
573 | trace_type_uuid = uuid.UUID(uuid_node) | |
574 | ||
575 | # create feature field types | |
576 | magic_ft = barectf_config.DEFAULT_FIELD_TYPE | |
577 | uuid_ft = None | |
578 | stream_type_id_ft = barectf_config.DEFAULT_FIELD_TYPE | |
579 | ||
580 | if trace_type_uuid is not None: | |
581 | # Trace type has a UUID: initialize UUID field type to | |
582 | # a default field type. | |
583 | uuid_ft = barectf_config.DEFAULT_FIELD_TYPE | |
584 | ||
585 | features_node = self._trace_type_node.get('$features') | |
586 | stream_type_id_ft_prop_name = 'stream-type-id-field-type' | |
587 | ||
588 | if features_node is not None: | |
589 | magic_ft = self._feature_ft(features_node, 'magic-field-type', | |
590 | magic_ft) | |
591 | uuid_ft = self._feature_ft(features_node, 'uuid-field-type', uuid_ft) | |
592 | stream_type_id_ft = self._feature_ft(features_node, stream_type_id_ft_prop_name, | |
593 | stream_type_id_ft) | |
594 | ||
595 | stream_types_prop_name = 'stream-types' | |
596 | stream_type_count = len(self._trace_type_node[stream_types_prop_name]) | |
597 | ||
598 | try: | |
599 | if stream_type_id_ft is None and stream_type_count > 1: | |
600 | raise _ConfigurationParseError(f'`{stream_type_id_ft_prop_name}` property', | |
601 | 'Stream type ID field type feature is required because trace type has more than one stream type') | |
602 | ||
603 | if isinstance(stream_type_id_ft, barectf_config._FieldType) and stream_type_count > (1 << stream_type_id_ft.size): | |
604 | raise _ConfigurationParseError(f'`{stream_type_id_ft_prop_name}` property', | |
605 | f'Field type\'s size ({stream_type_id_ft.size} bits) is too small to accomodate {stream_type_count} stream types') | |
606 | except _ConfigurationParseError as exc: | |
607 | _append_error_ctx(exc, '`$features` property') | |
608 | ||
7fffc7d1 PP |
609 | features = barectf_config.TraceTypeFeatures(magic_ft, uuid_ft, stream_type_id_ft, |
610 | default_byte_order=self._target_byte_order) | |
4810b707 PP |
611 | |
612 | # create stream types | |
613 | stream_types = set() | |
614 | ||
615 | for stream_name, stream_type_node in self._trace_type_node[stream_types_prop_name].items(): | |
616 | stream_types.add(self._create_stream_type(stream_name, stream_type_node)) | |
617 | ||
618 | # create trace type | |
7fffc7d1 | 619 | return barectf_config.TraceType(stream_types, trace_type_uuid, features) |
4810b707 PP |
620 | except _ConfigurationParseError as exc: |
621 | _append_error_ctx(exc, 'Trace type') | |
622 | ||
623 | def _create_trace(self): | |
624 | try: | |
625 | trace_type = self._create_trace_type() | |
2d55dc7d | 626 | trace_node = self.config_node['trace'] |
4810b707 PP |
627 | env = None |
628 | env_node = trace_node.get('environment') | |
629 | ||
630 | if env_node is not None: | |
631 | # validate each environment variable name | |
632 | for name in env_node: | |
633 | self._validate_iden(name, '`environment` property', | |
634 | 'environment variable name') | |
635 | ||
636 | # the node already has the expected structure | |
637 | env = barectf_config.TraceEnvironment(env_node) | |
638 | ||
639 | return barectf_config.Trace(trace_type, env) | |
640 | ||
641 | except _ConfigurationParseError as exc: | |
642 | _append_error_ctx(exc, 'Trace') | |
643 | ||
644 | def _create_config(self): | |
645 | # create trace first | |
646 | trace = self._create_trace() | |
647 | ||
648 | # find default stream type, if any | |
649 | def_stream_type = None | |
650 | ||
651 | for stream_type_name, stream_type_node in self._trace_type_node['stream-types'].items(): | |
652 | prop_name = '$is-default' | |
653 | is_default = stream_type_node.get(prop_name) | |
654 | ||
655 | if is_default is True: | |
656 | if def_stream_type is not None: | |
657 | exc = _ConfigurationParseError(f'`{prop_name}` property', | |
658 | f'Duplicate default stream type (`{def_stream_type.name}`)') | |
659 | exc._append_ctx(f'Stream type `{stream_type_name}`') | |
660 | _append_error_ctx(exc, 'Trace type') | |
661 | ||
662 | def_stream_type = trace.type.stream_type(stream_type_name) | |
663 | ||
664 | # create clock type C type mapping | |
665 | clk_types_node = self._trace_type_node.get('clock-types') | |
666 | clk_type_c_types = None | |
667 | ||
668 | if clk_types_node is not None: | |
669 | clk_type_c_types = collections.OrderedDict() | |
670 | ||
671 | for stream_type in trace.type.stream_types: | |
672 | if stream_type.default_clock_type is None: | |
673 | continue | |
674 | ||
675 | clk_type_node = clk_types_node[stream_type.default_clock_type.name] | |
676 | c_type = clk_type_node.get('$c-type') | |
677 | ||
678 | if c_type is not None: | |
679 | clk_type_c_types[stream_type.default_clock_type] = c_type | |
680 | ||
681 | # create options | |
682 | iden_prefix_def = False | |
683 | def_stream_type_name_def = False | |
2d55dc7d PP |
684 | opts_node = self.config_node.get('options') |
685 | iden_prefix = 'barectf_' | |
686 | file_name_prefix = 'barectf' | |
4810b707 PP |
687 | |
688 | if opts_node is not None: | |
689 | code_gen_opts_node = opts_node.get('code-generation') | |
690 | ||
691 | if code_gen_opts_node is not None: | |
692 | prefix_node = code_gen_opts_node.get('prefix', 'barectf') | |
693 | ||
694 | if type(prefix_node) is str: | |
695 | # automatic prefixes | |
696 | iden_prefix = f'{prefix_node}_' | |
697 | file_name_prefix = prefix_node | |
698 | else: | |
699 | iden_prefix = prefix_node['identifier'] | |
700 | file_name_prefix = prefix_node['file-name'] | |
701 | ||
702 | header_opts = code_gen_opts_node.get('header') | |
703 | ||
704 | if header_opts is not None: | |
705 | iden_prefix_def = header_opts.get('identifier-prefix-definition', False) | |
706 | def_stream_type_name_def = header_opts.get('default-stream-type-name-definition', | |
707 | False) | |
708 | ||
709 | header_opts = barectf_config.ConfigurationCodeGenerationHeaderOptions(iden_prefix_def, | |
710 | def_stream_type_name_def) | |
711 | cg_opts = barectf_config.ConfigurationCodeGenerationOptions(iden_prefix, file_name_prefix, | |
712 | def_stream_type, header_opts, | |
713 | clk_type_c_types) | |
714 | opts = barectf_config.ConfigurationOptions(cg_opts) | |
715 | ||
716 | # create configuration | |
7fffc7d1 | 717 | self._config = barectf_config.Configuration(trace, self._target_byte_order, opts) |
4810b707 PP |
718 | |
719 | # Expands the field type aliases found in the trace type node. | |
720 | # | |
721 | # This method modifies the trace type node. | |
722 | # | |
723 | # When this method returns: | |
724 | # | |
725 | # * Any field type alias is replaced with its full field type | |
726 | # node equivalent. | |
727 | # | |
728 | # * The `$field-type-aliases` property of the trace type node is | |
729 | # removed. | |
730 | def _expand_ft_aliases(self): | |
2d55dc7d | 731 | def resolve_ft_alias_from(parent_node: _MapNode, key: str): |
4810b707 PP |
732 | if key not in parent_node: |
733 | return | |
734 | ||
735 | if type(parent_node[key]) not in [collections.OrderedDict, str]: | |
736 | return | |
737 | ||
2d55dc7d | 738 | self._resolve_ft_alias_from(ft_aliases_node, parent_node, key) |
4810b707 PP |
739 | |
740 | ft_aliases_node = self._trace_type_node['$field-type-aliases'] | |
741 | ||
742 | # Expand field type aliases within trace, stream, and event type | |
743 | # nodes. | |
744 | features_prop_name = '$features' | |
745 | ||
746 | try: | |
747 | features_node = self._trace_type_node.get(features_prop_name) | |
748 | ||
749 | if features_node is not None: | |
750 | try: | |
751 | resolve_ft_alias_from(features_node, 'magic-field-type') | |
752 | resolve_ft_alias_from(features_node, 'uuid-field-type') | |
753 | resolve_ft_alias_from(features_node, 'stream-type-id-field-type') | |
754 | except _ConfigurationParseError as exc: | |
755 | _append_error_ctx(exc, f'`{features_prop_name}` property') | |
756 | except _ConfigurationParseError as exc: | |
757 | _append_error_ctx(exc, 'Trace type') | |
758 | ||
759 | for stream_type_name, stream_type_node in self._trace_type_node['stream-types'].items(): | |
760 | try: | |
761 | features_node = stream_type_node.get(features_prop_name) | |
762 | ||
763 | if features_node is not None: | |
764 | try: | |
765 | pkt_prop_name = 'packet' | |
766 | pkt_node = features_node.get(pkt_prop_name) | |
767 | ||
768 | if pkt_node is not None: | |
769 | try: | |
770 | resolve_ft_alias_from(pkt_node, 'total-size-field-type') | |
771 | resolve_ft_alias_from(pkt_node, 'content-size-field-type') | |
772 | resolve_ft_alias_from(pkt_node, 'beginning-time-field-type') | |
773 | resolve_ft_alias_from(pkt_node, 'end-time-field-type') | |
774 | resolve_ft_alias_from(pkt_node, | |
775 | 'discarded-events-counter-field-type') | |
776 | except _ConfigurationParseError as exc: | |
777 | _append_error_ctx(exc, f'`{pkt_prop_name}` property') | |
778 | ||
779 | ev_prop_name = 'event' | |
780 | ev_node = features_node.get(ev_prop_name) | |
781 | ||
782 | if ev_node is not None: | |
783 | try: | |
784 | resolve_ft_alias_from(ev_node, 'type-id-field-type') | |
785 | resolve_ft_alias_from(ev_node, 'time-field-type') | |
786 | except _ConfigurationParseError as exc: | |
787 | _append_error_ctx(exc, f'`{ev_prop_name}` property') | |
788 | except _ConfigurationParseError as exc: | |
789 | _append_error_ctx(exc, f'`{features_prop_name}` property') | |
790 | ||
791 | pkt_ctx_ft_extra_members_prop_name = 'packet-context-field-type-extra-members' | |
792 | pkt_ctx_ft_extra_members_node = stream_type_node.get(pkt_ctx_ft_extra_members_prop_name) | |
793 | ||
794 | if pkt_ctx_ft_extra_members_node is not None: | |
795 | try: | |
796 | for member_node in pkt_ctx_ft_extra_members_node: | |
797 | member_node = list(member_node.values())[0] | |
798 | resolve_ft_alias_from(member_node, 'field-type') | |
799 | except _ConfigurationParseError as exc: | |
800 | _append_error_ctx(exc, f'`{pkt_ctx_ft_extra_members_prop_name}` property') | |
801 | ||
802 | resolve_ft_alias_from(stream_type_node, 'event-common-context-field-type') | |
803 | ||
804 | for ev_type_name, ev_type_node in stream_type_node['event-types'].items(): | |
805 | try: | |
806 | resolve_ft_alias_from(ev_type_node, 'specific-context-field-type') | |
807 | resolve_ft_alias_from(ev_type_node, 'payload-field-type') | |
808 | except _ConfigurationParseError as exc: | |
809 | _append_error_ctx(exc, f'Event type `{ev_type_name}`') | |
810 | except _ConfigurationParseError as exc: | |
811 | _append_error_ctx(exc, f'Stream type `{stream_type_name}`') | |
812 | ||
813 | # remove the (now unneeded) `$field-type-aliases` property | |
814 | del self._trace_type_node['$field-type-aliases'] | |
815 | ||
816 | # Applies field type inheritance to all field type nodes found in | |
817 | # the trace type node. | |
818 | # | |
819 | # This method modifies the trace type node. | |
820 | # | |
821 | # When this method returns, no field type node has an `$inherit` | |
822 | # property. | |
823 | def _apply_fts_inheritance(self): | |
2d55dc7d | 824 | def apply_ft_inheritance(parent_node: _MapNode, key: str): |
4810b707 PP |
825 | if key not in parent_node: |
826 | return | |
827 | ||
828 | if type(parent_node[key]) is not collections.OrderedDict: | |
829 | return | |
830 | ||
2d55dc7d | 831 | self._apply_ft_inheritance(parent_node, key) |
4810b707 PP |
832 | |
833 | features_prop_name = '$features' | |
834 | features_node = self._trace_type_node.get(features_prop_name) | |
835 | ||
836 | if features_node is not None: | |
837 | apply_ft_inheritance(features_node, 'magic-field-type') | |
838 | apply_ft_inheritance(features_node, 'uuid-field-type') | |
839 | apply_ft_inheritance(features_node, 'stream-type-id-field-type') | |
840 | ||
841 | for stream_type_node in self._trace_type_node['stream-types'].values(): | |
842 | features_node = stream_type_node.get(features_prop_name) | |
843 | ||
844 | if features_node is not None: | |
845 | pkt_node = features_node.get('packet') | |
846 | ||
847 | if pkt_node is not None: | |
848 | apply_ft_inheritance(pkt_node, 'total-size-field-type') | |
849 | apply_ft_inheritance(pkt_node, 'content-size-field-type') | |
850 | apply_ft_inheritance(pkt_node, 'beginning-time-field-type') | |
851 | apply_ft_inheritance(pkt_node, 'end-time-field-type') | |
852 | apply_ft_inheritance(pkt_node, 'discarded-events-counter-field-type') | |
853 | ||
854 | ev_node = features_node.get('event') | |
855 | ||
856 | if ev_node is not None: | |
857 | apply_ft_inheritance(ev_node, 'type-id-field-type') | |
858 | apply_ft_inheritance(ev_node, 'time-field-type') | |
859 | ||
860 | pkt_ctx_ft_extra_members_node = stream_type_node.get('packet-context-field-type-extra-members') | |
861 | ||
862 | if pkt_ctx_ft_extra_members_node is not None: | |
863 | for member_node in pkt_ctx_ft_extra_members_node: | |
864 | member_node = list(member_node.values())[0] | |
865 | apply_ft_inheritance(member_node, 'field-type') | |
866 | ||
867 | apply_ft_inheritance(stream_type_node, 'event-common-context-field-type') | |
868 | ||
869 | for ev_type_node in stream_type_node['event-types'].values(): | |
870 | apply_ft_inheritance(ev_type_node, 'specific-context-field-type') | |
871 | apply_ft_inheritance(ev_type_node, 'payload-field-type') | |
872 | ||
873 | # Normalizes structure field type member nodes. | |
874 | # | |
875 | # A structure field type member node can look like this: | |
876 | # | |
877 | # - msg: custom-string | |
878 | # | |
879 | # which is the equivalent of this: | |
880 | # | |
881 | # - msg: | |
882 | # field-type: custom-string | |
883 | # | |
884 | # This method normalizes form 1 to use form 2. | |
885 | def _normalize_struct_ft_member_nodes(self): | |
2d55dc7d | 886 | def normalize_members_node(members_node: List[_MapNode]): |
4810b707 PP |
887 | ft_prop_name = 'field-type' |
888 | ||
889 | for member_node in members_node: | |
890 | member_name, val_node = list(member_node.items())[0] | |
891 | ||
892 | if type(val_node) is str: | |
893 | member_node[member_name] = collections.OrderedDict({ | |
894 | ft_prop_name: val_node | |
895 | }) | |
896 | ||
897 | normalize_struct_ft_member_nodes(member_node[member_name], ft_prop_name) | |
898 | ||
2d55dc7d | 899 | def normalize_struct_ft_member_nodes(parent_node: _MapNode, key: str): |
4810b707 PP |
900 | if type(parent_node) is not collections.OrderedDict: |
901 | return | |
902 | ||
903 | ft_node = parent_node.get(key) | |
904 | ||
905 | if type(ft_node) is not collections.OrderedDict: | |
906 | return | |
907 | ||
2d55dc7d | 908 | ft_node = typing.cast(collections.OrderedDict, ft_node) |
4810b707 PP |
909 | members_nodes = ft_node.get('members') |
910 | ||
911 | if members_nodes is not None: | |
912 | normalize_members_node(members_nodes) | |
913 | ||
914 | prop_name = '$field-type-aliases' | |
915 | ft_aliases_node = self._trace_type_node.get(prop_name) | |
916 | ||
917 | if ft_aliases_node is not None: | |
918 | for alias in ft_aliases_node: | |
919 | normalize_struct_ft_member_nodes(ft_aliases_node, alias) | |
920 | ||
921 | features_prop_name = '$features' | |
922 | features_node = self._trace_type_node.get(features_prop_name) | |
923 | ||
924 | if features_node is not None: | |
925 | normalize_struct_ft_member_nodes(features_node, 'magic-field-type') | |
926 | normalize_struct_ft_member_nodes(features_node, 'uuid-field-type') | |
927 | normalize_struct_ft_member_nodes(features_node, 'stream-type-id-field-type') | |
928 | ||
929 | for stream_type_node in self._trace_type_node['stream-types'].values(): | |
930 | features_node = stream_type_node.get(features_prop_name) | |
931 | ||
932 | if features_node is not None: | |
933 | pkt_node = features_node.get('packet') | |
934 | ||
935 | if pkt_node is not None: | |
936 | normalize_struct_ft_member_nodes(pkt_node, 'total-size-field-type') | |
937 | normalize_struct_ft_member_nodes(pkt_node, 'content-size-field-type') | |
938 | normalize_struct_ft_member_nodes(pkt_node, 'beginning-time-field-type') | |
939 | normalize_struct_ft_member_nodes(pkt_node, 'end-time-field-type') | |
940 | normalize_struct_ft_member_nodes(pkt_node, | |
941 | 'discarded-events-counter-field-type') | |
942 | ||
943 | ev_node = features_node.get('event') | |
944 | ||
945 | if ev_node is not None: | |
946 | normalize_struct_ft_member_nodes(ev_node, 'type-id-field-type') | |
947 | normalize_struct_ft_member_nodes(ev_node, 'time-field-type') | |
948 | ||
949 | pkt_ctx_ft_extra_members_node = stream_type_node.get('packet-context-field-type-extra-members') | |
950 | ||
951 | if pkt_ctx_ft_extra_members_node is not None: | |
952 | normalize_members_node(pkt_ctx_ft_extra_members_node) | |
953 | ||
954 | normalize_struct_ft_member_nodes(stream_type_node, 'event-common-context-field-type') | |
955 | ||
956 | for ev_type_node in stream_type_node['event-types'].values(): | |
957 | normalize_struct_ft_member_nodes(ev_type_node, 'specific-context-field-type') | |
958 | normalize_struct_ft_member_nodes(ev_type_node, 'payload-field-type') | |
959 | ||
960 | # Calls _expand_ft_aliases() and _apply_fts_inheritance() if the | |
961 | # trace type node has a `$field-type-aliases` property. | |
962 | def _expand_fts(self): | |
963 | # Make sure that the current configuration node is valid | |
964 | # considering field types are not expanded yet. | |
2d55dc7d | 965 | self._schema_validator.validate(self.config_node, |
c3fa1a14 | 966 | 'config/3/config-pre-field-type-expansion') |
4810b707 PP |
967 | |
968 | prop_name = '$field-type-aliases' | |
969 | ft_aliases_node = self._trace_type_node.get(prop_name) | |
970 | ||
971 | if ft_aliases_node is None: | |
972 | # If there's no `'$field-type-aliases'` node, then there's | |
973 | # no field type aliases and therefore no possible | |
974 | # inheritance. | |
975 | if prop_name in self._trace_type_node: | |
976 | del self._trace_type_node[prop_name] | |
977 | ||
978 | return | |
979 | ||
980 | # normalize structure field type member nodes | |
981 | self._normalize_struct_ft_member_nodes() | |
982 | ||
983 | # first, expand field type aliases | |
984 | self._expand_ft_aliases() | |
985 | ||
986 | # next, apply inheritance to create effective field type nodes | |
987 | self._apply_fts_inheritance() | |
988 | ||
989 | # Substitute the event type node log level aliases with their | |
990 | # numeric equivalents. | |
991 | # | |
992 | # Removes the `$log-level-aliases` property of the trace type node. | |
993 | def _sub_log_level_aliases(self): | |
994 | # Make sure that the current configuration node is valid | |
995 | # considering log level aliases are not substituted yet. | |
2d55dc7d | 996 | self._schema_validator.validate(self.config_node, |
c3fa1a14 | 997 | 'config/3/config-pre-log-level-alias-sub') |
4810b707 PP |
998 | |
999 | log_level_aliases_prop_name = '$log-level-aliases' | |
1000 | log_level_aliases_node = self._trace_type_node.get(log_level_aliases_prop_name) | |
1001 | ||
1002 | if log_level_aliases_prop_name in self._trace_type_node: | |
1003 | del self._trace_type_node[log_level_aliases_prop_name] | |
1004 | ||
1005 | if log_level_aliases_node is None: | |
1006 | # no log level aliases | |
1007 | return | |
1008 | ||
1009 | # substitute log level aliases | |
1010 | for stream_type_name, stream_type_node in self._trace_type_node['stream-types'].items(): | |
1011 | try: | |
1012 | for ev_type_name, ev_type_node in stream_type_node['event-types'].items(): | |
1013 | try: | |
1014 | prop_name = 'log-level' | |
1015 | ll_node = ev_type_node.get(prop_name) | |
1016 | ||
1017 | if ll_node is None: | |
1018 | continue | |
1019 | ||
1020 | if type(ll_node) is str: | |
1021 | if ll_node not in log_level_aliases_node: | |
1022 | raise _ConfigurationParseError(f'`{prop_name}` property', | |
1023 | f'Log level alias `{ll_node}` does not exist') | |
1024 | ||
1025 | ev_type_node[prop_name] = log_level_aliases_node[ll_node] | |
1026 | except _ConfigurationParseError as exc: | |
1027 | _append_error_ctx(exc, f'Event type `{ev_type_name}`') | |
1028 | except _ConfigurationParseError as exc: | |
1029 | _append_error_ctx(exc, f'Stream type `{stream_type_name}`') | |
1030 | ||
1031 | # Generator of parent node and key pairs for all the nodes, | |
1032 | # recursively, of `node`. | |
1033 | # | |
1034 | # It is safe to delete a yielded node during the iteration. | |
1035 | @staticmethod | |
2d55dc7d | 1036 | def _props(node: Any) -> Iterable[Tuple[Any, str]]: |
4810b707 PP |
1037 | if type(node) is collections.OrderedDict: |
1038 | for key in list(node): | |
1039 | yield from _Parser._props(node[key]) | |
1040 | yield node, key | |
1041 | elif type(node) is list: | |
1042 | for item_node in node: | |
1043 | yield from _Parser._props(item_node) | |
1044 | ||
2d55dc7d PP |
1045 | def _trace_type_props(self) -> Iterable[Tuple[Any, str]]: |
1046 | yield from _Parser._props(self.config_node['trace']['type']) | |
4810b707 PP |
1047 | |
1048 | # Normalize the properties of the configuration node. | |
1049 | # | |
1050 | # This method, for each property of the trace type node: | |
1051 | # | |
1052 | # 1. Removes it if it's `None` (means default). | |
1053 | # | |
1054 | # 2. Chooses a specific `class` property value. | |
1055 | # | |
7fffc7d1 | 1056 | # 3. Chooses a specific `byte-order`/`target-byte-order` property |
4810b707 PP |
1057 | # value. |
1058 | # | |
1059 | # 4. Chooses a specific `preferred-display-base` property value. | |
1060 | # | |
1061 | # This method also applies 1. to the trace node's `environment` | |
1062 | # property. | |
1063 | def _normalize_props(self): | |
2d55dc7d | 1064 | def normalize_byte_order_prop(parent_node: _MapNode, key: str): |
4810b707 PP |
1065 | node = parent_node[key] |
1066 | ||
1067 | if node in ['be', 'big']: | |
1068 | parent_node[key] = 'big-endian' | |
1069 | elif node in ['le', 'little']: | |
1070 | parent_node[key] = 'little-endian' | |
1071 | ||
2d55dc7d | 1072 | trace_node = self.config_node['trace'] |
4810b707 | 1073 | trace_type_node = trace_node['type'] |
7fffc7d1 | 1074 | normalize_byte_order_prop(self.config_node, 'target-byte-order') |
4810b707 PP |
1075 | |
1076 | for parent_node, key in self._trace_type_props(): | |
1077 | node = parent_node[key] | |
1078 | ||
1079 | if node is None: | |
1080 | # a `None` property is equivalent to not having it | |
1081 | del parent_node[key] | |
1082 | continue | |
1083 | ||
1084 | if key == 'class' and type(node) is str: | |
1085 | # field type class aliases | |
1086 | if node in ['uint', 'unsigned-int']: | |
1087 | parent_node[key] = 'unsigned-integer' | |
1088 | elif node in ['sint', 'signed-int']: | |
1089 | parent_node[key] = 'signed-integer' | |
1090 | elif node in ['uenum', 'unsigned-enum']: | |
1091 | parent_node[key] = 'unsigned-enumeration' | |
1092 | elif node in ['senum', 'signed-enum']: | |
1093 | parent_node[key] = 'signed-enumeration' | |
1094 | elif node == 'str': | |
1095 | parent_node[key] = 'string' | |
1096 | elif node == 'struct': | |
1097 | parent_node[key] = 'structure' | |
1098 | elif key == 'byte-order' and type(node) is str: | |
1099 | # byte order aliases | |
1100 | normalize_byte_order_prop(parent_node, key) | |
1101 | elif key == 'preferred-display-base' and type(node) is str: | |
1102 | # display base aliases | |
1103 | if node == 'bin': | |
1104 | parent_node[key] = 'binary' | |
1105 | elif node == 'oct': | |
1106 | parent_node[key] = 'octal' | |
1107 | elif node == 'dec': | |
1108 | parent_node[key] = 'decimal' | |
1109 | elif node == 'hex': | |
1110 | parent_node[key] = 'hexadecimal' | |
1111 | ||
1112 | prop_name = 'environment' | |
1113 | ||
1114 | if prop_name in trace_node: | |
1115 | node = trace_node[prop_name] | |
1116 | ||
1117 | if node is None: | |
1118 | del trace_node[prop_name] | |
1119 | ||
7fffc7d1 PP |
1120 | # Substitutes missing/`None` `byte-order` properties with the |
1121 | # configuration node's target byte order (`target-byte-order` | |
1122 | # property). | |
4810b707 PP |
1123 | def _sub_ft_nodes_byte_order(self): |
1124 | ba_ft_class_names = { | |
1125 | 'unsigned-integer', | |
1126 | 'signed-integer', | |
1127 | 'unsigned-enumeration', | |
1128 | 'signed-enumeration', | |
1129 | 'real', | |
1130 | } | |
1131 | ||
2d55dc7d | 1132 | def set_ft_node_byte_order_prop(parent_node: _MapNode, key: str): |
4810b707 PP |
1133 | if key not in parent_node: |
1134 | return | |
1135 | ||
1136 | ft_node = parent_node[key] | |
1137 | ||
1138 | if type(ft_node) is not collections.OrderedDict: | |
1139 | return | |
1140 | ||
1141 | if ft_node['class'] in ba_ft_class_names: | |
1142 | prop_name = 'byte-order' | |
1143 | byte_order_node = ft_node.get(prop_name) | |
1144 | ||
1145 | if byte_order_node is None: | |
7fffc7d1 | 1146 | ft_node[prop_name] = self._target_byte_order_node |
4810b707 PP |
1147 | |
1148 | members_node = ft_node.get('members') | |
1149 | ||
1150 | if members_node is not None: | |
1151 | set_struct_ft_node_members_byte_order_prop(members_node) | |
1152 | ||
1153 | set_ft_node_byte_order_prop(ft_node, 'element-field-type') | |
1154 | ||
2d55dc7d | 1155 | def set_struct_ft_node_members_byte_order_prop(members_node: List[_MapNode]): |
4810b707 PP |
1156 | for member_node in members_node: |
1157 | member_name, member_node = list(member_node.items())[0] | |
1158 | ||
1159 | try: | |
1160 | set_ft_node_byte_order_prop(member_node, 'field-type') | |
1161 | except _ConfigurationParseError as exc: | |
1162 | _append_error_ctx(exc, f'Structure field type member `{member_name}`') | |
1163 | ||
7fffc7d1 PP |
1164 | self._target_byte_order_node = self.config_node['target-byte-order'] |
1165 | self._target_byte_order = self._byte_order_from_node(self._target_byte_order_node) | |
4810b707 PP |
1166 | features_prop_name = '$features' |
1167 | features_node = self._trace_type_node.get(features_prop_name) | |
1168 | ||
1169 | if features_node is not None: | |
1170 | try: | |
1171 | set_ft_node_byte_order_prop(features_node, 'magic-field-type') | |
1172 | set_ft_node_byte_order_prop(features_node, 'uuid-field-type') | |
1173 | set_ft_node_byte_order_prop(features_node, 'stream-type-id-field-type') | |
1174 | except _ConfigurationParseError as exc: | |
1175 | exc._append_ctx(exc, f'`{features_prop_name}` property') | |
1176 | _append_error_ctx(exc, 'Trace type') | |
1177 | ||
1178 | for stream_type_name, stream_type_node in self._trace_type_node['stream-types'].items(): | |
1179 | try: | |
1180 | features_node = stream_type_node.get(features_prop_name) | |
1181 | ||
1182 | if features_node is not None: | |
1183 | pkt_node = features_node.get('packet') | |
1184 | ||
1185 | if pkt_node is not None: | |
1186 | set_ft_node_byte_order_prop(pkt_node, 'total-size-field-type') | |
1187 | set_ft_node_byte_order_prop(pkt_node, 'content-size-field-type') | |
1188 | set_ft_node_byte_order_prop(pkt_node, 'beginning-time-field-type') | |
1189 | set_ft_node_byte_order_prop(pkt_node, 'end-time-field-type') | |
1190 | set_ft_node_byte_order_prop(pkt_node, | |
1191 | 'discarded-events-counter-field-type') | |
1192 | ||
1193 | ev_node = features_node.get('event') | |
1194 | ||
1195 | if ev_node is not None: | |
1196 | set_ft_node_byte_order_prop(ev_node, 'type-id-field-type') | |
1197 | set_ft_node_byte_order_prop(ev_node, 'time-field-type') | |
1198 | ||
1199 | prop_name = 'packet-context-field-type-extra-members' | |
1200 | pkt_ctx_ft_extra_members_node = stream_type_node.get(prop_name) | |
1201 | ||
1202 | if pkt_ctx_ft_extra_members_node is not None: | |
1203 | try: | |
1204 | set_struct_ft_node_members_byte_order_prop(pkt_ctx_ft_extra_members_node) | |
1205 | except _ConfigurationParseError as exc: | |
1206 | _append_error_ctx(exc, f'`{pkt_ctx_ft_extra_members_node}` property') | |
1207 | ||
1208 | set_ft_node_byte_order_prop(stream_type_node, 'event-common-context-field-type') | |
1209 | ||
1210 | for ev_type_name, ev_type_node in stream_type_node['event-types'].items(): | |
1211 | try: | |
1212 | set_ft_node_byte_order_prop(ev_type_node, 'specific-context-field-type') | |
1213 | set_ft_node_byte_order_prop(ev_type_node, 'payload-field-type') | |
1214 | except _ConfigurationParseError as exc: | |
1215 | _append_error_ctx(exc, f'Event type `{ev_type_name}`') | |
1216 | except _ConfigurationParseError as exc: | |
1217 | _append_error_ctx(exc, f'Stream type `{stream_type_name}`') | |
1218 | ||
1219 | # Processes the inclusions of the event type node `ev_type_node`, | |
1220 | # returning the effective node. | |
2d55dc7d | 1221 | def _process_ev_type_node_include(self, ev_type_node: _MapNode) -> _MapNode: |
4810b707 PP |
1222 | # Make sure the event type node is valid for the inclusion |
1223 | # processing stage. | |
c3fa1a14 | 1224 | self._schema_validator.validate(ev_type_node, 'config/3/event-type-pre-include') |
4810b707 PP |
1225 | |
1226 | # process inclusions | |
1227 | return self._process_node_include(ev_type_node, self._process_ev_type_node_include) | |
1228 | ||
1229 | # Processes the inclusions of the stream type node | |
1230 | # `stream_type_node`, returning the effective node. | |
2d55dc7d PP |
1231 | def _process_stream_type_node_include(self, stream_type_node: _MapNode) -> _MapNode: |
1232 | def process_children_include(stream_type_node: _MapNode): | |
4810b707 PP |
1233 | prop_name = 'event-types' |
1234 | ||
1235 | if prop_name in stream_type_node: | |
1236 | ev_types_node = stream_type_node[prop_name] | |
1237 | ||
1238 | for key in list(ev_types_node): | |
1239 | ev_types_node[key] = self._process_ev_type_node_include(ev_types_node[key]) | |
1240 | ||
1241 | # Make sure the stream type node is valid for the inclusion | |
1242 | # processing stage. | |
c3fa1a14 | 1243 | self._schema_validator.validate(stream_type_node, 'config/3/stream-type-pre-include') |
4810b707 PP |
1244 | |
1245 | # process inclusions | |
1246 | return self._process_node_include(stream_type_node, self._process_stream_type_node_include, | |
1247 | process_children_include) | |
1248 | ||
1249 | # Processes the inclusions of the clock type node `clk_type_node`, | |
1250 | # returning the effective node. | |
2d55dc7d | 1251 | def _process_clk_type_node_include(self, clk_type_node: _MapNode) -> _MapNode: |
4810b707 PP |
1252 | # Make sure the clock type node is valid for the inclusion |
1253 | # processing stage. | |
c3fa1a14 | 1254 | self._schema_validator.validate(clk_type_node, 'config/3/clock-type-pre-include') |
4810b707 PP |
1255 | |
1256 | # process inclusions | |
1257 | return self._process_node_include(clk_type_node, self._process_clk_type_node_include) | |
1258 | ||
1259 | # Processes the inclusions of the trace type node `trace_type_node`, | |
1260 | # returning the effective node. | |
2d55dc7d PP |
1261 | def _process_trace_type_node_include(self, trace_type_node: _MapNode) -> _MapNode: |
1262 | def process_children_include(trace_type_node: _MapNode): | |
4810b707 PP |
1263 | prop_name = 'clock-types' |
1264 | ||
1265 | if prop_name in trace_type_node: | |
1266 | clk_types_node = trace_type_node[prop_name] | |
1267 | ||
1268 | for key in list(clk_types_node): | |
1269 | clk_types_node[key] = self._process_clk_type_node_include(clk_types_node[key]) | |
1270 | ||
1271 | prop_name = 'stream-types' | |
1272 | ||
1273 | if prop_name in trace_type_node: | |
1274 | stream_types_node = trace_type_node[prop_name] | |
1275 | ||
1276 | for key in list(stream_types_node): | |
1277 | stream_types_node[key] = self._process_stream_type_node_include(stream_types_node[key]) | |
1278 | ||
1279 | # Make sure the trace type node is valid for the inclusion | |
1280 | # processing stage. | |
c3fa1a14 | 1281 | self._schema_validator.validate(trace_type_node, 'config/3/trace-type-pre-include') |
4810b707 PP |
1282 | |
1283 | # process inclusions | |
1284 | return self._process_node_include(trace_type_node, self._process_trace_type_node_include, | |
1285 | process_children_include) | |
1286 | ||
1287 | # Processes the inclusions of the trace node `trace_node`, returning | |
1288 | # the effective node. | |
2d55dc7d PP |
1289 | def _process_trace_node_include(self, trace_node: _MapNode) -> _MapNode: |
1290 | def process_children_include(trace_node: _MapNode): | |
4810b707 PP |
1291 | prop_name = 'type' |
1292 | trace_node[prop_name] = self._process_trace_type_node_include(trace_node[prop_name]) | |
1293 | ||
1294 | # Make sure the trace node is valid for the inclusion processing | |
1295 | # stage. | |
c3fa1a14 | 1296 | self._schema_validator.validate(trace_node, 'config/3/trace-pre-include') |
4810b707 PP |
1297 | |
1298 | # process inclusions | |
1299 | return self._process_node_include(trace_node, self._process_trace_node_include, | |
1300 | process_children_include) | |
1301 | ||
1302 | # Processes the inclusions of the configuration node, modifying it | |
1303 | # during the process. | |
1304 | def _process_config_includes(self): | |
1305 | # Process inclusions in this order: | |
1306 | # | |
1307 | # 1. Clock type node and event type nodes (the order between | |
1308 | # those is not important). | |
1309 | # | |
1310 | # 2. Stream type nodes. | |
1311 | # | |
1312 | # 3. Trace type node. | |
1313 | # | |
1314 | # 4. Trace node. | |
1315 | # | |
1316 | # This is because: | |
1317 | # | |
1318 | # * A trace node can include a trace type node, clock type | |
1319 | # nodes, stream type nodes, and event type nodes. | |
1320 | # | |
1321 | # * A trace type node can include clock type nodes, stream type | |
1322 | # nodes, and event type nodes. | |
1323 | # | |
1324 | # * A stream type node can include event type nodes. | |
1325 | # | |
1326 | # First, make sure the configuration node itself is valid for | |
1327 | # the inclusion processing stage. | |
c3fa1a14 | 1328 | self._schema_validator.validate(self.config_node, 'config/3/config-pre-include') |
4810b707 PP |
1329 | |
1330 | # Process trace node inclusions. | |
1331 | # | |
1332 | # self._process_trace_node_include() returns a new (or the same) | |
1333 | # trace node without any `$include` property in it, recursively. | |
2d55dc7d | 1334 | self.config_node['trace'] = self._process_trace_node_include(self.config_node['trace']) |
4810b707 PP |
1335 | |
1336 | def _parse(self): | |
1337 | # process configuration node inclusions | |
1338 | self._process_config_includes() | |
1339 | ||
1340 | # Expand field type nodes. | |
1341 | # | |
1342 | # This process: | |
1343 | # | |
1344 | # 1. Replaces field type aliases with "effective" field type | |
1345 | # nodes, recursively. | |
1346 | # | |
1347 | # After this step, the `$field-type-aliases` property of the | |
1348 | # trace type node is gone. | |
1349 | # | |
1350 | # 2. Applies inheritance, following the `$inherit` properties. | |
1351 | # | |
1352 | # After this step, field type nodes do not contain `$inherit` | |
1353 | # properties. | |
1354 | # | |
1355 | # This is done blindly, in that the process _doesn't_ validate | |
1356 | # field type nodes at this point. | |
1357 | self._expand_fts() | |
1358 | ||
1359 | # Substitute log level aliases. | |
1360 | # | |
1361 | # This process: | |
1362 | # | |
1363 | # 1. Replaces log level aliases in event type nodes with their | |
1364 | # numeric equivalents as found in the `$log-level-aliases` | |
1365 | # property of the trace type node. | |
1366 | # | |
1367 | # 2. Removes the `$log-level-aliases` property from the trace | |
1368 | # type node. | |
1369 | self._sub_log_level_aliases() | |
1370 | ||
1371 | # At this point, the configuration node must be valid as an | |
1372 | # effective configuration node. | |
c3fa1a14 | 1373 | self._schema_validator.validate(self.config_node, 'config/3/config') |
4810b707 PP |
1374 | |
1375 | # Normalize properties. | |
1376 | # | |
1377 | # This process removes `None` properties and chooses specific | |
1378 | # enumerators when aliases exist (for example, `big-endian` | |
1379 | # instead of `be`). | |
1380 | # | |
1381 | # The goal of this is that, if the user then gets this parser's | |
1382 | # `config_node` property, it has a normal and very readable | |
1383 | # form. | |
1384 | # | |
1385 | # It also makes _create_config() easier to implement because it | |
1386 | # doesn't need to check for `None` nodes or enumerator aliases. | |
1387 | self._normalize_props() | |
1388 | ||
1389 | # Set `byte-order` properties of bit array field type nodes | |
1390 | # missing one. | |
4810b707 PP |
1391 | self._sub_ft_nodes_byte_order() |
1392 | ||
1393 | # Create a barectf configuration object from the configuration | |
1394 | # node. | |
1395 | self._create_config() | |
1396 | ||
1397 | @property | |
2d55dc7d | 1398 | def config(self) -> barectf_config.Configuration: |
4810b707 PP |
1399 | return self._config |
1400 | ||
1401 | @property | |
2d55dc7d PP |
1402 | def config_node(self) -> _MapNode: |
1403 | return typing.cast(barectf_config_parse_common._ConfigNodeV3, self._root_node).config_node |