1 # The MIT License (MIT)
3 # Copyright (c) 2015-2020 Philippe Proulx <pproulx@efficios.com>
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 from barectf
.config_parse_common
import _ConfigurationParseError
25 from barectf
.config_parse_common
import _append_error_ctx
26 import barectf
.config_parse_common
as config_parse_common
27 from barectf
.config_parse_common
import _MapNode
30 from barectf
.typing
import VersionNumber
, _OptStr
31 from typing
import Optional
, List
, Dict
, TextIO
, Union
, Callable
35 def _del_prop_if_exists(node
: _MapNode
, prop_name
: str):
40 def _rename_prop(node
: _MapNode
, old_prop_name
: str, new_prop_name
: str):
41 if old_prop_name
in node
:
42 node
[new_prop_name
] = node
[old_prop_name
]
43 del node
[old_prop_name
]
46 def _copy_prop_if_exists(dst_node
: _MapNode
, src_node
: _MapNode
, src_prop_name
: str,
47 dst_prop_name
: _OptStr
= None):
48 if dst_prop_name
is None:
49 dst_prop_name
= src_prop_name
51 if src_prop_name
in src_node
:
52 dst_node
[dst_prop_name
] = copy
.deepcopy(src_node
[src_prop_name
])
55 # A barectf 2 YAML configuration parser.
57 # The only purpose of such a parser is to transform the passed root
58 # configuration node so that it's a valid barectf 3 configuration node.
60 # The parser's `config_node` property is the equivalent barectf 3
63 # See the comments of _parse() for more implementation details about the
64 # parsing stages and general strategy.
65 class _Parser(config_parse_common
._Parser
):
66 # Builds a barectf 2 YAML configuration parser and parses the root
67 # configuration node `node` (already loaded from the file-like
68 # object `root_file`).
69 def __init__(self
, root_file
: TextIO
, node
: _MapNode
, with_pkg_include_dir
: bool,
70 include_dirs
: Optional
[List
[str]], ignore_include_not_found
: bool):
71 super().__init
__(root_file
, node
, with_pkg_include_dir
, include_dirs
,
72 ignore_include_not_found
, VersionNumber(2))
73 self
._ft
_cls
_name
_to
_conv
_method
: Dict
[str, Callable
[[_MapNode
], _MapNode
]] = {
74 'int': self
._conv
_int
_ft
_node
,
75 'integer': self
._conv
_int
_ft
_node
,
76 'enum': self
._conv
_enum
_ft
_node
,
77 'enumeration': self
._conv
_enum
_ft
_node
,
78 'flt': self
._conv
_real
_ft
_node
,
79 'float': self
._conv
_real
_ft
_node
,
80 'floating-point': self
._conv
_real
_ft
_node
,
81 'str': self
._conv
_string
_ft
_node
,
82 'string': self
._conv
_string
_ft
_node
,
83 'array': self
._conv
_array
_ft
_node
,
84 'struct': self
._conv
_struct
_ft
_node
,
85 'structure': self
._conv
_struct
_ft
_node
,
89 # Converts a v2 field type node to a v3 field type node and returns
91 def _conv_ft_node(self
, v2_ft_node
: _MapNode
) -> _MapNode
:
92 assert 'class' in v2_ft_node
93 cls
= v2_ft_node
['class']
94 assert cls
in self
._ft
_cls
_name
_to
_conv
_method
95 return self
._ft
_cls
_name
_to
_conv
_method
[cls
](v2_ft_node
)
97 def _conv_ft_node_if_exists(self
, v2_parent_node
: Optional
[_MapNode
], key
: str) -> Optional
[_MapNode
]:
98 if v2_parent_node
is None:
101 if key
not in v2_parent_node
:
104 return self
._conv
_ft
_node
(v2_parent_node
[key
])
106 # Converts a v2 integer field type node to a v3 integer field type
107 # node and returns it.
108 def _conv_int_ft_node(self
, v2_ft_node
: _MapNode
) -> _MapNode
:
109 # copy v2 integer field type node
110 v3_ft_node
= copy
.deepcopy(v2_ft_node
)
112 # signedness depends on the class, not a property
115 is_signed_node
= v3_ft_node
.get(prop_name
)
117 if is_signed_node
is True:
120 v3_ft_node
['class'] = cls_name
121 _del_prop_if_exists(v3_ft_node
, prop_name
)
123 # rename `align` property to `alignment`
124 _rename_prop(v3_ft_node
, 'align', 'alignment')
126 # rename `base` property to `preferred-display-base`
127 _rename_prop(v3_ft_node
, 'base', 'preferred-display-base')
129 # remove `encoding` property
130 _del_prop_if_exists(v3_ft_node
, 'encoding')
132 # remove `byte-order` property (the equivalent barectf 3
133 # configuration property is named `trace-byte-order`)
134 _del_prop_if_exists(v3_ft_node
, 'byte-order')
136 # remove `property-mappings` property
137 _del_prop_if_exists(v3_ft_node
, 'property-mappings')
141 # Converts a v2 enumeration field type node to a v3 enumeration
142 # field type node and returns it.
143 def _conv_enum_ft_node(self
, v2_ft_node
: _MapNode
) -> _MapNode
:
144 # An enumeration field type _is_ an integer field type, so use a
145 # copy of the converted v2 value field type node.
146 v3_ft_node
= copy
.deepcopy(self
._conv
_ft
_node
(v2_ft_node
['value-type']))
148 # transform class name accordingly
152 if v3_ft_node
[prop_name
] == 'sint':
155 v3_ft_node
[prop_name
] = cls_name
157 # convert members to mappings
158 prop_name
= 'members'
159 members_node
= v2_ft_node
.get(prop_name
)
161 if members_node
is not None:
162 mappings_node
: _MapNode
= collections
.OrderedDict()
165 for member_node
in members_node
:
166 v3_value_node
: Union
[int, List
[int]]
168 if type(member_node
) is str:
173 assert type(member_node
) is collections
.OrderedDict
174 label
= member_node
['label']
175 v2_value_node
= member_node
['value']
177 if type(v2_value_node
) is int:
178 cur
= v2_value_node
+ 1
179 v3_value_node
= v2_value_node
181 assert type(v2_value_node
) is list
182 assert len(v2_value_node
) == 2
183 v3_value_node
= list(v2_value_node
)
184 cur
= v2_value_node
[1] + 1
186 if label
not in mappings_node
:
187 mappings_node
[label
] = []
189 mappings_node
[label
].append(v3_value_node
)
191 v3_ft_node
['mappings'] = mappings_node
195 # Converts a v2 real field type node to a v3 real field type node
197 def _conv_real_ft_node(self
, v2_ft_node
: _MapNode
) -> _MapNode
:
198 # copy v2 real field type node
199 v3_ft_node
= copy
.deepcopy(v2_ft_node
)
201 # set class to `real`
202 v3_ft_node
['class'] = 'real'
204 # rename `align` property to `alignment`
205 _rename_prop(v3_ft_node
, 'align', 'alignment')
207 # set `size` property to a single integer (total size, in bits)
209 v3_ft_node
[prop_name
] = v3_ft_node
[prop_name
]['exp'] + v3_ft_node
[prop_name
]['mant']
213 # Converts a v2 string field type node to a v3 string field type
214 # node and returns it.
215 def _conv_string_ft_node(self
, v2_ft_node
: _MapNode
) -> _MapNode
:
216 # copy v2 string field type node
217 v3_ft_node
= copy
.deepcopy(v2_ft_node
)
219 # remove `encoding` property
220 _del_prop_if_exists(v3_ft_node
, 'encoding')
224 # Converts a v2 array field type node to a v3 (static) array field
225 # type node and returns it.
226 def _conv_array_ft_node(self
, v2_ft_node
: _MapNode
) -> _MapNode
:
227 # class renamed to `static-array` or `dynamic-array`
228 is_dynamic
= v2_ft_node
['length'] == 'dynamic'
229 array_type
= 'dynamic' if is_dynamic
else 'static'
230 v3_ft_node
: _MapNode
= collections
.OrderedDict({'class': f
'{array_type}-array'})
232 # copy `length` property if it's a static array field type
234 _copy_prop_if_exists(v3_ft_node
, v2_ft_node
, 'length')
236 # convert element field type
237 v3_ft_node
['element-field-type'] = self
._conv
_ft
_node
(v2_ft_node
['element-type'])
241 # Converts a v2 structure field type node to a v3 structure field
242 # type node and returns it.
243 def _conv_struct_ft_node(self
, v2_ft_node
: _MapNode
) -> _MapNode
:
244 # Create fresh v3 structure field type node, reusing the class
246 v3_ft_node
= collections
.OrderedDict({'class': v2_ft_node
['class']})
248 # rename `min-align` property to `minimum-alignment`
249 _copy_prop_if_exists(v3_ft_node
, v2_ft_node
, 'min-align', 'minimum-alignment')
251 # convert fields to members
254 if prop_name
in v2_ft_node
:
257 for member_name
, v2_member_ft_node
in v2_ft_node
[prop_name
].items():
258 members_node
.append(collections
.OrderedDict({
259 member_name
: collections
.OrderedDict({
260 'field-type': self
._conv
_ft
_node
(v2_member_ft_node
)
264 v3_ft_node
['members'] = members_node
268 # Converts a v2 clock type node to a v3 clock type node and returns
270 def _conv_clk_type_node(self
, v2_clk_type_node
: _MapNode
) -> _MapNode
:
271 # copy v2 clock type node
272 v3_clk_type_node
= copy
.deepcopy(v2_clk_type_node
)
274 # rename `freq` property to `frequency`
275 _rename_prop(v3_clk_type_node
, 'freq', 'frequency')
277 # rename `error-cycles` property to `precision`
278 _rename_prop(v3_clk_type_node
, 'error-cycles', 'precision')
280 # rename `absolute` property to `origin-is-unix-epoch`
281 _rename_prop(v3_clk_type_node
, 'absolute', 'origin-is-unix-epoch')
283 # rename `$return-ctype`/`return-ctype` property to `$c-type`
284 new_prop_name
= '$c-type'
285 _rename_prop(v3_clk_type_node
, 'return-ctype', new_prop_name
)
286 _rename_prop(v3_clk_type_node
, '$return-ctype', new_prop_name
)
288 return v3_clk_type_node
290 # Converts a v2 event record type node to a v3 event record type
291 # node and returns it.
292 def _conv_ert_node(self
, v2_ert_node
: _MapNode
) -> _MapNode
:
293 # create empty v3 event record type node
294 v3_ert_node
: _MapNode
= collections
.OrderedDict()
296 # copy `log-level` property
297 _copy_prop_if_exists(v3_ert_node
, v2_ert_node
, 'log-level')
299 # convert specific context field type node
300 v2_ft_node
= v2_ert_node
.get('context-type')
302 if v2_ft_node
is not None:
303 v3_ert_node
['specific-context-field-type'] = self
._conv
_ft
_node
(v2_ft_node
)
305 # convert payload field type node
306 v2_ft_node
= v2_ert_node
.get('payload-type')
308 if v2_ft_node
is not None:
309 v3_ert_node
['payload-field-type'] = self
._conv
_ft
_node
(v2_ft_node
)
314 def _set_v3_feature_ft_if_exists(v3_features_node
: _MapNode
, key
: str,
315 node
: Union
[Optional
[_MapNode
], bool]):
321 v3_features_node
[key
] = val
323 # Converts a v2 data stream type node to a v3 data stream type node
325 def _conv_dst_node(self
, v2_dst_node
: _MapNode
) -> _MapNode
:
326 # This function creates a v3 data stream type features node from
327 # the packet context and event record header field type nodes of
328 # a v2 data stream type node.
329 def v3_features_node_from_v2_ft_nodes(v2_pkt_ctx_ft_fields_node
: _MapNode
,
330 v2_er_header_ft_fields_node
: Optional
[_MapNode
]) -> _MapNode
:
331 if v2_er_header_ft_fields_node
is None:
332 v2_er_header_ft_fields_node
= collections
.OrderedDict()
334 v3_pkt_total_size_ft_node
= self
._conv
_ft
_node
(v2_pkt_ctx_ft_fields_node
['packet_size'])
335 v3_pkt_content_size_ft_node
= self
._conv
_ft
_node
(v2_pkt_ctx_ft_fields_node
['content_size'])
336 v3_pkt_beg_ts_ft_node
= self
._conv
_ft
_node
_if
_exists
(v2_pkt_ctx_ft_fields_node
,
338 v3_pkt_end_ts_ft_node
= self
._conv
_ft
_node
_if
_exists
(v2_pkt_ctx_ft_fields_node
,
340 v3_pkt_disc_er_counter_snap_ft_node
= self
._conv
_ft
_node
_if
_exists
(v2_pkt_ctx_ft_fields_node
,
342 v3_ert_id_ft_node
= self
._conv
_ft
_node
_if
_exists
(v2_er_header_ft_fields_node
, 'id')
343 v3_er_ts_ft_node
= self
._conv
_ft
_node
_if
_exists
(v2_er_header_ft_fields_node
,
345 v3_features_node
: _MapNode
= collections
.OrderedDict()
346 v3_pkt_node
: _MapNode
= collections
.OrderedDict()
347 v3_er_node
: _MapNode
= collections
.OrderedDict()
348 v3_pkt_node
['total-size-field-type'] = v3_pkt_total_size_ft_node
349 v3_pkt_node
['content-size-field-type'] = v3_pkt_content_size_ft_node
350 self
._set
_v
3_feature
_ft
_if
_exists
(v3_pkt_node
, 'beginning-timestamp-field-type',
351 v3_pkt_beg_ts_ft_node
)
352 self
._set
_v
3_feature
_ft
_if
_exists
(v3_pkt_node
, 'end-timestamp-field-type',
353 v3_pkt_end_ts_ft_node
)
354 self
._set
_v
3_feature
_ft
_if
_exists
(v3_pkt_node
,
355 'discarded-event-records-counter-snapshot-field-type',
356 v3_pkt_disc_er_counter_snap_ft_node
)
357 self
._set
_v
3_feature
_ft
_if
_exists
(v3_er_node
, 'type-id-field-type', v3_ert_id_ft_node
)
358 self
._set
_v
3_feature
_ft
_if
_exists
(v3_er_node
, 'timestamp-field-type', v3_er_ts_ft_node
)
359 v3_features_node
['packet'] = v3_pkt_node
360 v3_features_node
['event-record'] = v3_er_node
361 return v3_features_node
363 def clk_type_name_from_v2_int_ft_node(v2_int_ft_node
: Optional
[_MapNode
]) -> _OptStr
:
364 if v2_int_ft_node
is None:
367 assert v2_int_ft_node
['class'] in ('int', 'integer')
368 prop_mappings_node
= v2_int_ft_node
.get('property-mappings')
370 if prop_mappings_node
is not None and len(prop_mappings_node
) > 0:
371 return prop_mappings_node
[0]['name']
375 # create empty v3 data stream type node
376 v3_dst_node
: _MapNode
= collections
.OrderedDict()
378 # rename `$default` property to `$is-default`
379 _copy_prop_if_exists(v3_dst_node
, v2_dst_node
, '$default', '$is-default')
381 # set default clock type node
382 pct_prop_name
= 'packet-context-type'
383 v2_pkt_ctx_ft_fields_node
= v2_dst_node
[pct_prop_name
]['fields']
384 eht_prop_name
= 'event-header-type'
385 v2_er_header_ft_fields_node
= None
386 v2_er_header_ft_node
= v2_dst_node
.get(eht_prop_name
)
388 if v2_er_header_ft_node
is not None:
389 v2_er_header_ft_fields_node
= v2_er_header_ft_node
['fields']
391 def_clk_type_name
= None
394 ts_begin_prop_name
= 'timestamp_begin'
395 ts_begin_clk_type_name
= clk_type_name_from_v2_int_ft_node(v2_pkt_ctx_ft_fields_node
.get(ts_begin_prop_name
))
396 ts_end_prop_name
= 'timestamp_end'
397 ts_end_clk_type_name
= clk_type_name_from_v2_int_ft_node(v2_pkt_ctx_ft_fields_node
.get(ts_end_prop_name
))
399 if ts_begin_clk_type_name
is not None and ts_end_clk_type_name
is not None:
400 if ts_begin_clk_type_name
!= ts_end_clk_type_name
:
401 raise _ConfigurationParseError(f
'`{ts_begin_prop_name}`/`{ts_end_prop_name}` properties',
402 'Field types are not mapped to the same clock type')
403 except _ConfigurationParseError
as exc
:
404 _append_error_ctx(exc
, f
'`{pct_prop_name}` property')
407 if def_clk_type_name
is None and v2_er_header_ft_fields_node
is not None:
408 def_clk_type_name
= clk_type_name_from_v2_int_ft_node(v2_er_header_ft_fields_node
.get('timestamp'))
410 if def_clk_type_name
is None and ts_begin_clk_type_name
is not None:
411 def_clk_type_name
= ts_begin_clk_type_name
413 if def_clk_type_name
is None and ts_end_clk_type_name
is not None:
414 def_clk_type_name
= ts_end_clk_type_name
415 except _ConfigurationParseError
as exc
:
416 _append_error_ctx(exc
, f
'`{eht_prop_name}` property')
418 if def_clk_type_name
is not None:
419 v3_dst_node
['$default-clock-type-name'] = def_clk_type_name
422 v3_dst_node
['$features'] = v3_features_node_from_v2_ft_nodes(v2_pkt_ctx_ft_fields_node
,
423 v2_er_header_ft_fields_node
)
425 # set extra packet context field type members node
426 pkt_ctx_ft_extra_members
= []
436 for member_name
, v2_ft_node
in v2_pkt_ctx_ft_fields_node
.items():
437 if member_name
in ctf_member_names
:
440 pkt_ctx_ft_extra_members
.append(collections
.OrderedDict({
441 member_name
: collections
.OrderedDict({
442 'field-type': self
._conv
_ft
_node
(v2_ft_node
)
446 if len(pkt_ctx_ft_extra_members
) > 0:
447 v3_dst_node
['packet-context-field-type-extra-members'] = pkt_ctx_ft_extra_members
449 # convert event record common context field type node
450 v2_ft_node
= v2_dst_node
.get('event-context-type')
452 if v2_ft_node
is not None:
453 v3_dst_node
['event-record-common-context-field-type'] = self
._conv
_ft
_node
(v2_ft_node
)
455 # convert event record type nodes
456 v3_erts_node
= collections
.OrderedDict()
458 for ert_name
, v2_ert_node
in v2_dst_node
['events'].items():
460 v3_erts_node
[ert_name
] = self
._conv
_ert
_node
(v2_ert_node
)
461 except _ConfigurationParseError
as exc
:
462 _append_error_ctx(exc
, f
'Event record type `{ert_name}`')
464 v3_dst_node
['event-record-types'] = v3_erts_node
468 # Converts a v2 metadata node to a v3 trace node and returns it.
469 def _conv_meta_node(self
, v2_meta_node
: _MapNode
) -> _MapNode
:
470 def v3_features_node_from_v2_ft_node(v2_pkt_header_ft_node
: Optional
[_MapNode
]) -> _MapNode
:
471 def set_if_exists(key
, node
):
472 return self
._set
_v
3_feature
_ft
_if
_exists
(v3_features_node
, key
, node
)
474 v2_pkt_header_ft_fields_node
= collections
.OrderedDict()
476 if v2_pkt_header_ft_node
is not None:
477 v2_pkt_header_ft_fields_node
= v2_pkt_header_ft_node
['fields']
479 v3_magic_ft_node
= self
._conv
_ft
_node
_if
_exists
(v2_pkt_header_ft_fields_node
, 'magic')
480 v3_uuid_ft_node
= self
._conv
_ft
_node
_if
_exists
(v2_pkt_header_ft_fields_node
, 'uuid')
481 v3_dst_id_ft_node
= self
._conv
_ft
_node
_if
_exists
(v2_pkt_header_ft_fields_node
,
483 v3_features_node
: _MapNode
= collections
.OrderedDict()
484 set_if_exists('magic-field-type', v3_magic_ft_node
)
485 set_if_exists('uuid-field-type', v3_uuid_ft_node
)
486 set_if_exists('data-stream-type-id-field-type', v3_dst_id_ft_node
)
487 return v3_features_node
489 v3_trace_node
: _MapNode
= collections
.OrderedDict()
490 v3_trace_type_node
: _MapNode
= collections
.OrderedDict()
491 v2_trace_node
= v2_meta_node
['trace']
493 # copy `byte-order` property as `trace-byte-order` property
494 _copy_prop_if_exists(v3_trace_type_node
, v2_trace_node
, 'byte-order', 'trace-byte-order')
496 # copy `uuid` property
497 _copy_prop_if_exists(v3_trace_type_node
, v2_trace_node
, 'uuid')
499 # copy `$log-levels`/`log-levels` property
500 new_prop_name
= '$log-level-aliases'
501 _copy_prop_if_exists(v3_trace_type_node
, v2_meta_node
, 'log-levels', new_prop_name
)
502 _copy_prop_if_exists(v3_trace_type_node
, v2_meta_node
, '$log-levels', new_prop_name
)
504 # copy `clocks` property, converting clock type nodes
505 v2_clk_types_node
= v2_meta_node
.get('clocks')
507 if v2_clk_types_node
is not None:
508 v3_clk_types_node
= collections
.OrderedDict()
510 for name
, v2_clk_type_node
in v2_clk_types_node
.items():
511 v3_clk_types_node
[name
] = self
._conv
_clk
_type
_node
(v2_clk_type_node
)
513 v3_trace_type_node
['clock-types'] = v3_clk_types_node
516 v2_pkt_header_ft_node
= v2_trace_node
.get('packet-header-type')
517 v3_trace_type_node
['$features'] = v3_features_node_from_v2_ft_node(v2_pkt_header_ft_node
)
519 # convert data stream type nodes
520 v3_dsts_node
= collections
.OrderedDict()
522 for dst_name
, v2_dst_node
in v2_meta_node
['streams'].items():
524 v3_dsts_node
[dst_name
] = self
._conv
_dst
_node
(v2_dst_node
)
525 except _ConfigurationParseError
as exc
:
526 _append_error_ctx(exc
, f
'Data stream type `{dst_name}`')
528 v3_trace_type_node
['data-stream-types'] = v3_dsts_node
530 # If `v2_meta_node` has a `$default-stream` property, find the
531 # corresponding v3 data stream type node and set its
532 # `$is-default` property to `True`.
533 prop_name
= '$default-stream'
534 v2_def_dst_node
= v2_meta_node
.get(prop_name
)
536 if v2_def_dst_node
is not None:
539 for dst_name
, v3_dst_node
in v3_dsts_node
.items():
540 if dst_name
== v2_def_dst_node
:
541 v3_dst_node
['$is-default'] = True
546 raise _ConfigurationParseError(f
'`{prop_name}` property',
547 f
'Data stream type `{v2_def_dst_node}` does not exist')
549 # set environment node
550 v2_env_node
= v2_meta_node
.get('env')
552 if v2_env_node
is not None:
553 v3_trace_node
['environment'] = copy
.deepcopy(v2_env_node
)
555 # set v3 trace node's type node
556 v3_trace_node
['type'] = v3_trace_type_node
560 # Transforms the root configuration node into a valid v3
561 # configuration node.
562 def _transform_config_node(self
):
563 # remove the `version` property
564 del self
._root
_node
['version']
566 # relocate prefix and option nodes
567 prefix_prop_name
= 'prefix'
568 v2_prefix_node
= self
._root
_node
.get(prefix_prop_name
, 'barectf_')
569 _del_prop_if_exists(self
._root
_node
, prefix_prop_name
)
570 opt_prop_name
= 'options'
571 v2_options_node
= self
._root
_node
.get(opt_prop_name
)
572 _del_prop_if_exists(self
._root
_node
, opt_prop_name
)
573 code_gen_node
= collections
.OrderedDict()
574 v3_prefixes
= config_parse_common
._v
3_prefixes
_from
_v
2_prefix
(v2_prefix_node
)
575 v3_prefix_node
= collections
.OrderedDict([
576 ('identifier', v3_prefixes
.identifier
),
577 ('file-name', v3_prefixes
.file_name
),
579 code_gen_node
[prefix_prop_name
] = v3_prefix_node
581 if v2_options_node
is not None:
582 header_node
= collections
.OrderedDict()
583 _copy_prop_if_exists(header_node
, v2_options_node
, 'gen-prefix-def',
584 'identifier-prefix-definition')
585 _copy_prop_if_exists(header_node
, v2_options_node
, 'gen-default-stream-def',
586 'default-data-stream-type-name-definition')
587 code_gen_node
['header'] = header_node
589 self
._root
_node
[opt_prop_name
] = collections
.OrderedDict({
590 'code-generation': code_gen_node
,
593 # convert the v2 metadata node into a v3 trace node
595 self
._root
_node
['trace'] = self
._conv
_meta
_node
(self
._root
_node
['metadata'])
596 except _ConfigurationParseError
as exc
:
597 _append_error_ctx(exc
, 'Metadata object')
599 del self
._root
_node
['metadata']
601 # Expands the field type aliases found in the metadata node.
603 # This method modifies the metadata node.
605 # When this method returns:
607 # * Any field type alias is replaced with its full field type node
610 # * The `type-aliases` property of metadata node is removed.
611 def _expand_ft_aliases(self
):
612 meta_node
= self
._root
_node
['metadata']
613 ft_aliases_node
= meta_node
['type-aliases']
615 # Expand field type aliases within trace, data stream, and event
618 self
._resolve
_ft
_alias
_from
(ft_aliases_node
, meta_node
['trace'], 'packet-header-type')
619 except _ConfigurationParseError
as exc
:
620 _append_error_ctx(exc
, 'Trace type')
622 for dst_name
, dst_node
in meta_node
['streams'].items():
624 self
._resolve
_ft
_alias
_from
(ft_aliases_node
, dst_node
, 'packet-context-type')
625 self
._resolve
_ft
_alias
_from
(ft_aliases_node
, dst_node
, 'event-header-type')
626 self
._resolve
_ft
_alias
_from
(ft_aliases_node
, dst_node
, 'event-context-type')
628 for ert_name
, ert_node
in dst_node
['events'].items():
630 self
._resolve
_ft
_alias
_from
(ft_aliases_node
, ert_node
, 'context-type')
631 self
._resolve
_ft
_alias
_from
(ft_aliases_node
, ert_node
, 'payload-type')
632 except _ConfigurationParseError
as exc
:
633 _append_error_ctx(exc
, f
'Event record type `{ert_name}`')
634 except _ConfigurationParseError
as exc
:
635 _append_error_ctx(exc
, f
'Data stream type `{dst_name}`')
637 # remove the (now unneeded) `type-aliases` node
638 del meta_node
['type-aliases']
640 # Applies field type inheritance to all field type nodes found in
643 # This method modifies the metadata node.
645 # When this method returns, no field type node has an `$inherit` or
646 # `inherit` property.
647 def _apply_fts_inheritance(self
):
648 meta_node
= self
._root
_node
['metadata']
649 self
._apply
_ft
_inheritance
(meta_node
['trace'], 'packet-header-type')
651 for dst_node
in meta_node
['streams'].values():
652 self
._apply
_ft
_inheritance
(dst_node
, 'packet-context-type')
653 self
._apply
_ft
_inheritance
(dst_node
, 'event-header-type')
654 self
._apply
_ft
_inheritance
(dst_node
, 'event-context-type')
656 for ert_node
in dst_node
['events'].values():
657 self
._apply
_ft
_inheritance
(ert_node
, 'context-type')
658 self
._apply
_ft
_inheritance
(ert_node
, 'payload-type')
660 # Calls _expand_ft_aliases() and _apply_fts_inheritance() if the
661 # metadata node has a `type-aliases` property.
662 def _expand_fts(self
):
663 # Make sure that the current configuration node is valid
664 # considering field types are not expanded yet.
665 self
._schema
_validator
.validate(self
._root
_node
,
666 'config/2/config-pre-field-type-expansion')
668 meta_node
= self
._root
_node
['metadata']
669 ft_aliases_node
= meta_node
.get('type-aliases')
671 if ft_aliases_node
is None:
672 # If there's no `type-aliases` node, then there's no field
673 # type aliases and therefore no possible inheritance.
676 # first, expand field type aliases
677 self
._expand
_ft
_aliases
()
679 # next, apply inheritance to create effective field types
680 self
._apply
_fts
_inheritance
()
682 # Processes the inclusions of the event record type node `ert_node`,
683 # returning the effective node.
684 def _process_ert_node_include(self
, ert_node
: _MapNode
) -> _MapNode
:
685 # Make sure the event record type node is valid for the
686 # inclusion processing stage.
687 self
._schema
_validator
.validate(ert_node
, 'config/2/ert-pre-include')
690 return self
._process
_node
_include
(ert_node
, self
._process
_ert
_node
_include
)
692 # Processes the inclusions of the data stream type node `dst_node`,
693 # returning the effective node.
694 def _process_dst_node_include(self
, dst_node
: _MapNode
) -> _MapNode
:
695 def process_children_include(dst_node
):
698 if prop_name
in dst_node
:
699 erts_node
= dst_node
[prop_name
]
701 for key
in list(erts_node
):
702 erts_node
[key
] = self
._process
_ert
_node
_include
(erts_node
[key
])
704 # Make sure the data stream type node is valid for the inclusion
706 self
._schema
_validator
.validate(dst_node
, 'config/2/dst-pre-include')
709 return self
._process
_node
_include
(dst_node
, self
._process
_dst
_node
_include
,
710 process_children_include
)
712 # Processes the inclusions of the trace type node `trace_type_node`,
713 # returning the effective node.
714 def _process_trace_type_node_include(self
, trace_type_node
: _MapNode
) -> _MapNode
:
715 # Make sure the trace type node is valid for the inclusion
717 self
._schema
_validator
.validate(trace_type_node
, 'config/2/trace-type-pre-include')
720 return self
._process
_node
_include
(trace_type_node
, self
._process
_trace
_type
_node
_include
)
722 # Processes the inclusions of the clock type node `clk_type_node`,
723 # returning the effective node.
724 def _process_clk_type_node_include(self
, clk_type_node
: _MapNode
) -> _MapNode
:
725 # Make sure the clock type node is valid for the inclusion
727 self
._schema
_validator
.validate(clk_type_node
, 'config/2/clock-type-pre-include')
730 return self
._process
_node
_include
(clk_type_node
, self
._process
_clk
_type
_node
_include
)
732 # Processes the inclusions of the metadata node `meta_node`,
733 # returning the effective node.
734 def _process_meta_node_include(self
, meta_node
: _MapNode
) -> _MapNode
:
735 def process_children_include(meta_node
: _MapNode
):
738 if prop_name
in meta_node
:
739 meta_node
[prop_name
] = self
._process
_trace
_type
_node
_include
(meta_node
[prop_name
])
743 if prop_name
in meta_node
:
744 clk_types_node
= meta_node
[prop_name
]
746 for key
in list(clk_types_node
):
747 clk_types_node
[key
] = self
._process
_clk
_type
_node
_include
(clk_types_node
[key
])
749 prop_name
= 'streams'
751 if prop_name
in meta_node
:
752 dsts_node
= meta_node
[prop_name
]
754 for key
in list(dsts_node
):
755 dsts_node
[key
] = self
._process
_dst
_node
_include
(dsts_node
[key
])
757 # Make sure the metadata node is valid for the inclusion
759 self
._schema
_validator
.validate(meta_node
, 'config/2/metadata-pre-include')
762 return self
._process
_node
_include
(meta_node
, self
._process
_meta
_node
_include
,
763 process_children_include
)
765 # Processes the inclusions of the configuration node, modifying it
766 # during the process.
767 def _process_config_includes(self
):
768 # Process inclusions in this order:
770 # 1. Clock type node, event record type nodes, and trace type
771 # nodes (the order between those is not important).
773 # 2. Data stream type nodes.
779 # * A metadata node can include clock type nodes, a trace type
780 # node, data stream type nodes, and event record type nodes
783 # * A data stream type node can include event record type nodes.
785 # First, make sure the configuration node itself is valid for
786 # the inclusion processing stage.
787 self
._schema
_validator
.validate(self
._root
_node
,
788 'config/2/config-pre-include')
790 # Process metadata node inclusions.
792 # self._process_meta_node_include() returns a new (or the same)
793 # metadata node without any `$include` property in it,
795 prop_name
= 'metadata'
796 self
._root
_node
[prop_name
] = self
._process
_meta
_node
_include
(self
._root
_node
[prop_name
])
799 # Make sure the configuration node is minimally valid, that is,
800 # it contains a valid `version` property.
802 # This step does not validate the whole configuration node yet
803 # because we don't have an effective configuration node; we
806 # * Process inclusions.
807 # * Expand field types (aliases and inheritance).
808 self
._schema
_validator
.validate(self
._root
_node
, 'config/2/config-min')
810 # process configuration node inclusions
811 self
._process
_config
_includes
()
813 # Expand field type nodes.
817 # 1. Replaces field type aliases with "effective" field type
818 # nodes, recursively.
820 # After this step, the `type-aliases` property of the
821 # metadata node is gone.
823 # 2. Applies inheritance, following the `$inherit`/`inherit`
826 # After this step, field type nodes do not contain `$inherit`
827 # or `inherit` properties.
829 # This is done blindly, in that the process _doesn't_ validate
830 # field type nodes at this point.
832 # The reason we must do this here for a barectf 2 configuration,
833 # considering that barectf 3 also supports field type node
834 # aliases and inheritance, is that we need to find specific
835 # packet header and packet context field type member nodes (for
836 # example, `stream_id`, `packet_size`, or `timestamp_end`) to
837 # set the `$features` properties of barectf 3 trace type and
838 # data stream type nodes. Those field type nodes can be aliases,
839 # contain aliases, or inherit from other nodes.
842 # Validate the whole, (almost) effective configuration node.
844 # It's almost effective because the `log-level` property of
845 # event record type nodes can be log level aliases. Log level
846 # aliases are also a feature of a barectf 3 configuration node,
847 # therefore this is compatible.
848 self
._schema
_validator
.validate(self
._root
_node
, 'config/2/config')
850 # Transform the current configuration node into a valid v3
851 # configuration node.
852 self
._transform
_config
_node
()
855 def config_node(self
) -> config_parse_common
._ConfigNodeV
3:
856 return config_parse_common
._ConfigNodeV
3(typing
.cast(_MapNode
, self
._root
_node
))