Add package inclusion dir. at the API level
[deliverable/barectf.git] / barectf / config_parse_v2.py
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 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 import collections
28 import copy
29
30
31 def _del_prop_if_exists(node, prop_name):
32 if prop_name in node:
33 del node[prop_name]
34
35
36 def _rename_prop(node, old_prop_name, new_prop_name):
37 if old_prop_name in node:
38 node[new_prop_name] = node[old_prop_name]
39 del node[old_prop_name]
40
41
42 def _copy_prop_if_exists(dst_node, src_node, src_prop_name, dst_prop_name=None):
43 if dst_prop_name is None:
44 dst_prop_name = src_prop_name
45
46 if src_prop_name in src_node:
47 dst_node[dst_prop_name] = copy.deepcopy(src_node[src_prop_name])
48
49
50 # A barectf 2 YAML configuration parser.
51 #
52 # The only purpose of such a parser is to transform the passed root
53 # configuration node so that it's a valid barectf 3 configuration node.
54 #
55 # The parser's `config_node` property is the equivalent barectf 3
56 # configuration node.
57 #
58 # See the comments of _parse() for more implementation details about the
59 # parsing stages and general strategy.
60 class _Parser(config_parse_common._Parser):
61 # Builds a barectf 2 YAML configuration parser and parses the root
62 # configuration node `node` (already loaded from `path`).
63 def __init__(self, path, node, with_pkg_include_dir, include_dirs, ignore_include_not_found):
64 super().__init__(path, node, with_pkg_include_dir, include_dirs, ignore_include_not_found, 2)
65 self._ft_cls_name_to_conv_method = {
66 'int': self._conv_int_ft_node,
67 'integer': self._conv_int_ft_node,
68 'enum': self._conv_enum_ft_node,
69 'enumeration': self._conv_enum_ft_node,
70 'flt': self._conv_real_ft_node,
71 'float': self._conv_real_ft_node,
72 'floating-point': self._conv_real_ft_node,
73 'str': self._conv_string_ft_node,
74 'string': self._conv_string_ft_node,
75 'array': self._conv_static_array_ft_node,
76 'struct': self._conv_struct_ft_node,
77 'structure': self._conv_struct_ft_node,
78 }
79 self._parse()
80
81 # Converts a v2 field type node to a v3 field type node and returns
82 # it.
83 def _conv_ft_node(self, v2_ft_node):
84 assert 'class' in v2_ft_node
85 cls = v2_ft_node['class']
86 assert cls in self._ft_cls_name_to_conv_method
87 return self._ft_cls_name_to_conv_method[cls](v2_ft_node)
88
89 def _conv_ft_node_if_exists(self, v2_parent_node, key):
90 if v2_parent_node is None:
91 return
92
93 if key not in v2_parent_node:
94 return
95
96 return self._conv_ft_node(v2_parent_node[key])
97
98 # Converts a v2 integer field type node to a v3 integer field type
99 # node and returns it.
100 def _conv_int_ft_node(self, v2_ft_node):
101 # copy v2 integer field type node
102 v3_ft_node = copy.deepcopy(v2_ft_node)
103
104 # signedness depends on the class, not a property
105 cls_name = 'uint'
106 prop_name = 'signed'
107 is_signed_node = v3_ft_node.get(prop_name)
108
109 if is_signed_node is True:
110 cls_name = 'sint'
111
112 v3_ft_node['class'] = cls_name
113 _del_prop_if_exists(v3_ft_node, prop_name)
114
115 # rename `align` property to `alignment`
116 _rename_prop(v3_ft_node, 'align', 'alignment')
117
118 # rename `base` property to `preferred-display-base`
119 _rename_prop(v3_ft_node, 'base', 'preferred-display-base')
120
121 # remove `encoding` property
122 _del_prop_if_exists(v3_ft_node, 'encoding')
123
124 # remove `property-mappings` property
125 _del_prop_if_exists(v3_ft_node, 'property-mappings')
126
127 return v3_ft_node
128
129 # Converts a v2 enumeration field type node to a v3 enumeration
130 # field type node and returns it.
131 def _conv_enum_ft_node(self, v2_ft_node):
132 # An enumeration field type _is_ an integer field type, so use a
133 # copy of the converted v2 value field type node.
134 v3_ft_node = copy.deepcopy(self._conv_ft_node(v2_ft_node['value-type']))
135
136 # transform class name accordingly
137 prop_name = 'class'
138 cls_name = 'uenum'
139
140 if v3_ft_node[prop_name] == 'sint':
141 cls_name = 'senum'
142
143 v3_ft_node[prop_name] = cls_name
144
145 # convert members to mappings
146 prop_name = 'members'
147 members_node = v2_ft_node.get(prop_name)
148
149 if members_node is not None:
150 mappings_node = collections.OrderedDict()
151 cur = 0
152
153 for member_node in members_node:
154 if type(member_node) is str:
155 label = member_node
156 v3_value_node = cur
157 cur += 1
158 else:
159 assert type(member_node) is collections.OrderedDict
160 label = member_node['label']
161 v2_value_node = member_node['value']
162
163 if type(v2_value_node) is int:
164 cur = v2_value_node + 1
165 v3_value_node = v2_value_node
166 else:
167 assert type(v2_value_node) is list
168 assert len(v2_value_node) == 2
169 v3_value_node = list(v2_value_node)
170 cur = v2_value_node[1] + 1
171
172 if label not in mappings_node:
173 mappings_node[label] = []
174
175 mappings_node[label].append(v3_value_node)
176
177 v3_ft_node['mappings'] = mappings_node
178
179 return v3_ft_node
180
181 # Converts a v2 real field type node to a v3 real field type node
182 # and returns it.
183 def _conv_real_ft_node(self, v2_ft_node):
184 # copy v2 real field type node
185 v3_ft_node = copy.deepcopy(v2_ft_node)
186
187 # set class to `real`
188 v3_ft_node['class'] = 'real'
189
190 # rename `align` property to `alignment`
191 _rename_prop(v3_ft_node, 'align', 'alignment')
192
193 # set `size` property to a single integer (total size, in bits)
194 prop_name = 'size'
195 v3_ft_node[prop_name] = v3_ft_node[prop_name]['exp'] + v3_ft_node[prop_name]['mant']
196
197 return v3_ft_node
198
199 # Converts a v2 string field type node to a v3 string field type
200 # node and returns it.
201 def _conv_string_ft_node(self, v2_ft_node):
202 # copy v2 string field type node
203 v3_ft_node = copy.deepcopy(v2_ft_node)
204
205 # remove `encoding` property
206 _del_prop_if_exists(v3_ft_node, 'encoding')
207
208 return v3_ft_node
209
210 # Converts a v2 array field type node to a v3 (static) array field
211 # type node and returns it.
212 def _conv_static_array_ft_node(self, v2_ft_node):
213 # class renamed to `static-array`
214 v3_ft_node = collections.OrderedDict({'class': 'static-array'})
215
216 # copy `length` property
217 _copy_prop_if_exists(v3_ft_node, v2_ft_node, 'length')
218
219 # convert element field type
220 v3_ft_node['element-field-type'] = self._conv_ft_node(v2_ft_node['element-type'])
221
222 return v3_ft_node
223
224 # Converts a v2 structure field type node to a v3 structure field
225 # type node and returns it.
226 def _conv_struct_ft_node(self, v2_ft_node):
227 # Create fresh v3 structure field type node, reusing the class
228 # of `v2_ft_node`.
229 v3_ft_node = collections.OrderedDict({'class': v2_ft_node['class']})
230
231 # rename `min-align` property to `minimum-alignment`
232 _copy_prop_if_exists(v3_ft_node, v2_ft_node, 'min-align', 'minimum-alignment')
233
234 # convert fields to members
235 prop_name = 'fields'
236
237 if prop_name in v2_ft_node:
238 members_node = []
239
240 for member_name, v2_member_ft_node in v2_ft_node[prop_name].items():
241 members_node.append(collections.OrderedDict({
242 member_name: collections.OrderedDict({
243 'field-type': self._conv_ft_node(v2_member_ft_node)
244 })
245 }))
246
247 v3_ft_node['members'] = members_node
248
249 return v3_ft_node
250
251 # Converts a v2 clock type node to a v3 clock type node and returns
252 # it.
253 def _conv_clk_type_node(self, v2_clk_type_node):
254 # copy v2 clock type node
255 v3_clk_type_node = copy.deepcopy(v2_clk_type_node)
256
257 # rename `freq` property to `frequency`
258 _rename_prop(v3_clk_type_node, 'freq', 'frequency')
259
260 # rename `error-cycles` property to `precision`
261 _rename_prop(v3_clk_type_node, 'error-cycles', 'precision')
262
263 # rename `absolute` property to `origin-is-unix-epoch`
264 _rename_prop(v3_clk_type_node, 'absolute', 'origin-is-unix-epoch')
265
266 # rename `$return-ctype`/`return-ctype` property to `$c-type`
267 new_prop_name = '$c-type'
268 _rename_prop(v3_clk_type_node, 'return-ctype', new_prop_name)
269 _rename_prop(v3_clk_type_node, '$return-ctype', new_prop_name)
270
271 return v3_clk_type_node
272
273 # Converts a v2 event type node to a v3 event type node and returns
274 # it.
275 def _conv_ev_type_node(self, v2_ev_type_node):
276 # create empty v3 event type node
277 v3_ev_type_node = collections.OrderedDict()
278
279 # copy `log-level` property
280 _copy_prop_if_exists(v3_ev_type_node, v2_ev_type_node, 'log-level')
281
282 # convert specific context field type node
283 v2_ft_node = v2_ev_type_node.get('context-type')
284
285 if v2_ft_node is not None:
286 v3_ev_type_node['specific-context-field-type'] = self._conv_ft_node(v2_ft_node)
287
288 # convert payload field type node
289 v2_ft_node = v2_ev_type_node.get('payload-type')
290
291 if v2_ft_node is not None:
292 v3_ev_type_node['payload-field-type'] = self._conv_ft_node(v2_ft_node)
293
294 return v3_ev_type_node
295
296 @staticmethod
297 def _set_v3_feature_ft_if_exists(v3_features_node, key, node):
298 val = node
299
300 if val is None:
301 val = False
302
303 v3_features_node[key] = val
304
305 # Converts a v2 stream type node to a v3 stream type node and
306 # returns it.
307 def _conv_stream_type_node(self, v2_stream_type_node):
308 # This function creates a v3 stream type features node from the
309 # packet context and event header field type nodes of a
310 # v2 stream type node.
311 def v3_features_node_from_v2_ft_nodes(v2_pkt_ctx_ft_fields_node,
312 v2_ev_header_ft_fields_node):
313 if v2_ev_header_ft_fields_node is None:
314 v2_ev_header_ft_fields_node = collections.OrderedDict()
315
316 v3_pkt_total_size_ft_node = self._conv_ft_node(v2_pkt_ctx_ft_fields_node['packet_size'])
317 v3_pkt_content_size_ft_node = self._conv_ft_node(v2_pkt_ctx_ft_fields_node['content_size'])
318 v3_pkt_beg_time_ft_node = self._conv_ft_node_if_exists(v2_pkt_ctx_ft_fields_node,
319 'timestamp_begin')
320 v3_pkt_end_time_ft_node = self._conv_ft_node_if_exists(v2_pkt_ctx_ft_fields_node,
321 'timestamp_end')
322 v3_pkt_disc_ev_counter_ft_node = self._conv_ft_node_if_exists(v2_pkt_ctx_ft_fields_node,
323 'events_discarded')
324 v3_ev_type_id_ft_node = self._conv_ft_node_if_exists(v2_ev_header_ft_fields_node, 'id')
325 v3_ev_time_ft_node = self._conv_ft_node_if_exists(v2_ev_header_ft_fields_node,
326 'timestamp')
327 v3_features_node = collections.OrderedDict()
328 v3_pkt_node = collections.OrderedDict()
329 v3_ev_node = collections.OrderedDict()
330 v3_pkt_node['total-size-field-type'] = v3_pkt_total_size_ft_node
331 v3_pkt_node['content-size-field-type'] = v3_pkt_content_size_ft_node
332 self._set_v3_feature_ft_if_exists(v3_pkt_node, 'beginning-time-field-type',
333 v3_pkt_beg_time_ft_node)
334 self._set_v3_feature_ft_if_exists(v3_pkt_node, 'end-time-field-type',
335 v3_pkt_end_time_ft_node)
336 self._set_v3_feature_ft_if_exists(v3_pkt_node, 'discarded-events-counter-field-type',
337 v3_pkt_disc_ev_counter_ft_node)
338 self._set_v3_feature_ft_if_exists(v3_ev_node, 'type-id-field-type',
339 v3_ev_type_id_ft_node)
340 self._set_v3_feature_ft_if_exists(v3_ev_node, 'time-field-type', v3_ev_time_ft_node)
341 v3_features_node['packet'] = v3_pkt_node
342 v3_features_node['event'] = v3_ev_node
343 return v3_features_node
344
345 def clk_type_name_from_v2_int_ft_node(v2_int_ft_node):
346 if v2_int_ft_node is None:
347 return
348
349 assert v2_int_ft_node['class'] in ('int', 'integer')
350 prop_mappings_node = v2_int_ft_node.get('property-mappings')
351
352 if prop_mappings_node is not None and len(prop_mappings_node) > 0:
353 return prop_mappings_node[0]['name']
354
355 # create empty v3 stream type node
356 v3_stream_type_node = collections.OrderedDict()
357
358 # rename `$default` property to `$is-default`
359 _copy_prop_if_exists(v3_stream_type_node, v2_stream_type_node, '$default', '$is-default')
360
361 # set default clock type node
362 pct_prop_name = 'packet-context-type'
363 v2_pkt_ctx_ft_fields_node = v2_stream_type_node[pct_prop_name]['fields']
364 eht_prop_name = 'event-header-type'
365 v2_ev_header_ft_fields_node = None
366 v2_ev_header_ft_node = v2_stream_type_node.get(eht_prop_name)
367
368 if v2_ev_header_ft_node is not None:
369 v2_ev_header_ft_fields_node = v2_ev_header_ft_node['fields']
370
371 def_clk_type_name = None
372
373 try:
374 ts_begin_prop_name = 'timestamp_begin'
375 ts_begin_clk_type_name = clk_type_name_from_v2_int_ft_node(v2_pkt_ctx_ft_fields_node.get(ts_begin_prop_name))
376 ts_end_prop_name = 'timestamp_end'
377 ts_end_clk_type_name = clk_type_name_from_v2_int_ft_node(v2_pkt_ctx_ft_fields_node.get(ts_end_prop_name))
378
379 if ts_begin_clk_type_name is not None and ts_end_clk_type_name is not None:
380 if ts_begin_clk_type_name != ts_end_clk_type_name:
381 raise _ConfigurationParseError(f'`{ts_begin_prop_name}`/`{ts_end_prop_name}` properties',
382 'Field types are not mapped to the same clock type')
383 except _ConfigurationParseError as exc:
384 _append_error_ctx(exc, f'`{pct_prop_name}` property')
385
386 try:
387 if def_clk_type_name is None and v2_ev_header_ft_fields_node is not None:
388 def_clk_type_name = clk_type_name_from_v2_int_ft_node(v2_ev_header_ft_fields_node.get('timestamp'))
389
390 if def_clk_type_name is None and ts_begin_clk_type_name is not None:
391 def_clk_type_name = ts_begin_clk_type_name
392
393 if def_clk_type_name is None and ts_end_clk_type_name is not None:
394 def_clk_type_name = ts_end_clk_type_name
395 except _ConfigurationParseError as exc:
396 _append_error_ctx(exc, f'`{eht_prop_name}` property')
397
398 if def_clk_type_name is not None:
399 v3_stream_type_node['$default-clock-type-name'] = def_clk_type_name
400
401 # set features node
402 v3_stream_type_node['$features'] = v3_features_node_from_v2_ft_nodes(v2_pkt_ctx_ft_fields_node,
403 v2_ev_header_ft_fields_node)
404
405 # set extra packet context field type members node
406 pkt_ctx_ft_extra_members = []
407 ctf_member_names = [
408 'packet_size',
409 'content_size',
410 'timestamp_begin',
411 'timestamp_end',
412 'events_discarded',
413 'packet_seq_num',
414 ]
415
416 for member_name, v2_ft_node in v2_pkt_ctx_ft_fields_node.items():
417 if member_name in ctf_member_names:
418 continue
419
420 pkt_ctx_ft_extra_members.append(collections.OrderedDict({
421 member_name: collections.OrderedDict({
422 'field-type': self._conv_ft_node(v2_ft_node)
423 })
424 }))
425
426 if len(pkt_ctx_ft_extra_members) > 0:
427 v3_stream_type_node['packet-context-field-type-extra-members'] = pkt_ctx_ft_extra_members
428
429 # convert event common context field type node
430 v2_ft_node = v2_stream_type_node.get('event-context-type')
431
432 if v2_ft_node is not None:
433 v3_stream_type_node['event-common-context-field-type'] = self._conv_ft_node(v2_ft_node)
434
435 # convert event type nodes
436 v3_event_types_node = collections.OrderedDict()
437
438 for ev_type_name, v2_ev_type_node in v2_stream_type_node['events'].items():
439 try:
440 v3_event_types_node[ev_type_name] = self._conv_ev_type_node(v2_ev_type_node)
441 except _ConfigurationParseError as exc:
442 _append_error_ctx(exc, f'Event type `{ev_type_name}`')
443
444 v3_stream_type_node['event-types'] = v3_event_types_node
445
446 return v3_stream_type_node
447
448 # Converts a v2 metadata node to a v3 trace node and returns it.
449 def _conv_meta_node(self, v2_meta_node):
450 def v3_features_node_from_v2_ft_node(v2_pkt_header_ft_node):
451 def set_if_exists(key, node):
452 return self._set_v3_feature_ft_if_exists(v3_features_node, key, node)
453
454 v2_pkt_header_ft_fields_node = collections.OrderedDict()
455
456 if v2_pkt_header_ft_node is not None:
457 v2_pkt_header_ft_fields_node = v2_pkt_header_ft_node['fields']
458
459 v3_magic_ft_node = self._conv_ft_node_if_exists(v2_pkt_header_ft_fields_node, 'magic')
460 v3_uuid_ft_node = self._conv_ft_node_if_exists(v2_pkt_header_ft_fields_node, 'uuid')
461 v3_stream_type_id_ft_node = self._conv_ft_node_if_exists(v2_pkt_header_ft_fields_node,
462 'stream_id')
463 v3_features_node = collections.OrderedDict()
464 set_if_exists('magic-field-type', v3_magic_ft_node)
465 set_if_exists('uuid-field-type', v3_uuid_ft_node)
466 set_if_exists('stream-type-id-field-type', v3_stream_type_id_ft_node)
467 return v3_features_node
468
469 v3_trace_node = collections.OrderedDict()
470 v3_trace_type_node = collections.OrderedDict()
471 v2_trace_node = v2_meta_node['trace']
472
473 # rename `byte-order` property to `$default-byte-order`
474 _copy_prop_if_exists(v3_trace_type_node, v2_trace_node, 'byte-order', '$default-byte-order')
475
476 # copy `uuid` property
477 _copy_prop_if_exists(v3_trace_type_node, v2_trace_node, 'uuid')
478
479 # copy `$log-levels`/`log-levels` property
480 new_prop_name = '$log-level-aliases'
481 _copy_prop_if_exists(v3_trace_type_node, v2_meta_node, 'log-levels', new_prop_name)
482 _copy_prop_if_exists(v3_trace_type_node, v2_meta_node, '$log-levels', new_prop_name)
483
484 # copy `clocks` property, converting clock type nodes
485 v2_clk_types_node = v2_meta_node.get('clocks')
486
487 if v2_clk_types_node is not None:
488 v3_clk_types_node = collections.OrderedDict()
489
490 for name, v2_clk_type_node in v2_clk_types_node.items():
491 v3_clk_types_node[name] = self._conv_clk_type_node(v2_clk_type_node)
492
493 v3_trace_type_node['clock-types'] = v3_clk_types_node
494
495 # set features node
496 v2_pkt_header_ft_node = v2_trace_node.get('packet-header-type')
497 v3_trace_type_node['$features'] = v3_features_node_from_v2_ft_node(v2_pkt_header_ft_node)
498
499 # convert stream type nodes
500 v3_stream_types_node = collections.OrderedDict()
501
502 for stream_type_name, v2_stream_type_node in v2_meta_node['streams'].items():
503 try:
504 v3_stream_types_node[stream_type_name] = self._conv_stream_type_node(v2_stream_type_node)
505 except _ConfigurationParseError as exc:
506 _append_error_ctx(exc, f'Stream type `{stream_type_name}`')
507
508 v3_trace_type_node['stream-types'] = v3_stream_types_node
509
510 # If `v2_meta_node` has a `$default-stream` property, find the
511 # corresponding v3 stream type node and set its `$is-default`
512 # property to `True`.
513 prop_name = '$default-stream'
514 v2_def_stream_type_node = v2_meta_node.get(prop_name)
515
516 if v2_def_stream_type_node is not None:
517 found = False
518
519 for stream_type_name, v3_stream_type_node in v3_stream_types_node.items():
520 if stream_type_name == v2_def_stream_type_node:
521 v3_stream_type_node['$is-default'] = True
522 found = True
523 break
524
525 if not found:
526 raise _ConfigurationParseError(f'`{prop_name}` property',
527 f'Stream type `{v2_def_stream_type_node}` does not exist')
528
529 # set environment node
530 v2_env_node = v2_meta_node.get('env')
531
532 if v2_env_node is not None:
533 v3_trace_node['environment'] = copy.deepcopy(v2_env_node)
534
535 # set v3 trace node's type node
536 v3_trace_node['type'] = v3_trace_type_node
537
538 return v3_trace_node
539
540 # Transforms the root configuration node into a valid v3
541 # configuration node.
542 def _transform_config_node(self):
543 # remove the `version` property
544 del self._root_node['version']
545
546 # relocate prefix and option nodes
547 prefix_prop_name = 'prefix'
548 v2_prefix_node = self._root_node.get(prefix_prop_name, 'barectf_')
549 _del_prop_if_exists(self._root_node, prefix_prop_name)
550 opt_prop_name = 'options'
551 v2_options_node = self._root_node.get(opt_prop_name)
552 _del_prop_if_exists(self._root_node, opt_prop_name)
553 code_gen_node = collections.OrderedDict()
554 v3_prefixes = config_parse_common._v3_prefixes_from_v2_prefix(v2_prefix_node)
555 v3_prefix_node = collections.OrderedDict([
556 ('identifier', v3_prefixes.identifier),
557 ('file-name', v3_prefixes.file_name),
558 ])
559 code_gen_node[prefix_prop_name] = v3_prefix_node
560
561 if v2_options_node is not None:
562 header_node = collections.OrderedDict()
563 _copy_prop_if_exists(header_node, v2_options_node, 'gen-prefix-def',
564 'identifier-prefix-definition')
565 _copy_prop_if_exists(header_node, v2_options_node, 'gen-default-stream-def',
566 'default-stream-type-name-definition')
567 code_gen_node['header'] = header_node
568
569 self._root_node[opt_prop_name] = collections.OrderedDict({
570 'code-generation': code_gen_node,
571 })
572
573 # convert the v2 metadata node into a v3 trace node
574 try:
575 self._root_node['trace'] = self._conv_meta_node(self._root_node['metadata'])
576 except _ConfigurationParseError as exc:
577 _append_error_ctx(exc, 'Metadata object')
578
579 del self._root_node['metadata']
580
581 # Expands the field type aliases found in the metadata node.
582 #
583 # This method modifies the metadata node.
584 #
585 # When this method returns:
586 #
587 # * Any field type alias is replaced with its full field type node
588 # equivalent.
589 #
590 # * The `type-aliases` property of metadata node is removed.
591 def _expand_ft_aliases(self):
592 meta_node = self._root_node['metadata']
593 ft_aliases_node = meta_node['type-aliases']
594
595 # Expand field type aliases within trace, stream, and event
596 # types now.
597 try:
598 self._resolve_ft_alias_from(ft_aliases_node, meta_node['trace'], 'packet-header-type')
599 except _ConfigurationParseError as exc:
600 _append_error_ctx(exc, 'Trace type')
601
602 for stream_type_name, stream_type_node in meta_node['streams'].items():
603 try:
604 self._resolve_ft_alias_from(ft_aliases_node, stream_type_node,
605 'packet-context-type')
606 self._resolve_ft_alias_from(ft_aliases_node, stream_type_node, 'event-header-type')
607 self._resolve_ft_alias_from(ft_aliases_node, stream_type_node,
608 'event-context-type')
609
610 for ev_type_name, ev_type_node in stream_type_node['events'].items():
611 try:
612 self._resolve_ft_alias_from(ft_aliases_node, ev_type_node, 'context-type')
613 self._resolve_ft_alias_from(ft_aliases_node, ev_type_node, 'payload-type')
614 except _ConfigurationParseError as exc:
615 _append_error_ctx(exc, f'Event type `{ev_type_name}`')
616 except _ConfigurationParseError as exc:
617 _append_error_ctx(exc, f'Stream type `{stream_type_name}`')
618
619 # remove the (now unneeded) `type-aliases` node
620 del meta_node['type-aliases']
621
622 # Applies field type inheritance to all field type nodes found in
623 # the metadata node.
624 #
625 # This method modifies the metadata node.
626 #
627 # When this method returns, no field type node has an `$inherit` or
628 # `inherit` property.
629 def _apply_fts_inheritance(self):
630 meta_node = self._root_node['metadata']
631 self._apply_ft_inheritance(meta_node['trace'], 'packet-header-type')
632
633 for stream_type_node in meta_node['streams'].values():
634 self._apply_ft_inheritance(stream_type_node, 'packet-context-type')
635 self._apply_ft_inheritance(stream_type_node, 'event-header-type')
636 self._apply_ft_inheritance(stream_type_node, 'event-context-type')
637
638 for ev_type_node in stream_type_node['events'].values():
639 self._apply_ft_inheritance(ev_type_node, 'context-type')
640 self._apply_ft_inheritance(ev_type_node, 'payload-type')
641
642 # Calls _expand_ft_aliases() and _apply_fts_inheritance() if the
643 # metadata node has a `type-aliases` property.
644 def _expand_fts(self):
645 # Make sure that the current configuration node is valid
646 # considering field types are not expanded yet.
647 self._schema_validator.validate(self._root_node,
648 '2/config/config-pre-field-type-expansion')
649
650 meta_node = self._root_node['metadata']
651 ft_aliases_node = meta_node.get('type-aliases')
652
653 if ft_aliases_node is None:
654 # If there's no `type-aliases` node, then there's no field
655 # type aliases and therefore no possible inheritance.
656 return
657
658 # first, expand field type aliases
659 self._expand_ft_aliases()
660
661 # next, apply inheritance to create effective field types
662 self._apply_fts_inheritance()
663
664 # Processes the inclusions of the event type node `ev_type_node`,
665 # returning the effective node.
666 def _process_ev_type_node_include(self, ev_type_node):
667 # Make sure the event type node is valid for the inclusion
668 # processing stage.
669 self._schema_validator.validate(ev_type_node, '2/config/event-type-pre-include')
670
671 # process inclusions
672 return self._process_node_include(ev_type_node, self._process_ev_type_node_include)
673
674 # Processes the inclusions of the stream type node
675 # `stream_type_node`, returning the effective node.
676 def _process_stream_type_node_include(self, stream_type_node):
677 def process_children_include(stream_type_node):
678 prop_name = 'events'
679
680 if prop_name in stream_type_node:
681 ev_types_node = stream_type_node[prop_name]
682
683 for key in list(ev_types_node):
684 ev_types_node[key] = self._process_ev_type_node_include(ev_types_node[key])
685
686 # Make sure the stream type node is valid for the inclusion
687 # processing stage.
688 self._schema_validator.validate(stream_type_node, '2/config/stream-type-pre-include')
689
690 # process inclusions
691 return self._process_node_include(stream_type_node, self._process_stream_type_node_include,
692 process_children_include)
693
694 # Processes the inclusions of the trace type node `trace_type_node`,
695 # returning the effective node.
696 def _process_trace_type_node_include(self, trace_type_node):
697 # Make sure the trace type node is valid for the inclusion
698 # processing stage.
699 self._schema_validator.validate(trace_type_node, '2/config/trace-type-pre-include')
700
701 # process inclusions
702 return self._process_node_include(trace_type_node, self._process_trace_type_node_include)
703
704 # Processes the inclusions of the clock type node `clk_type_node`,
705 # returning the effective node.
706 def _process_clk_type_node_include(self, clk_type_node):
707 # Make sure the clock type node is valid for the inclusion
708 # processing stage.
709 self._schema_validator.validate(clk_type_node, '2/config/clock-type-pre-include')
710
711 # process inclusions
712 return self._process_node_include(clk_type_node, self._process_clk_type_node_include)
713
714 # Processes the inclusions of the metadata node `meta_node`,
715 # returning the effective node.
716 def _process_meta_node_include(self, meta_node):
717 def process_children_include(meta_node):
718 prop_name = 'trace'
719
720 if prop_name in meta_node:
721 meta_node[prop_name] = self._process_trace_type_node_include(meta_node[prop_name])
722
723 prop_name = 'clocks'
724
725 if prop_name in meta_node:
726 clk_types_node = meta_node[prop_name]
727
728 for key in list(clk_types_node):
729 clk_types_node[key] = self._process_clk_type_node_include(clk_types_node[key])
730
731 prop_name = 'streams'
732
733 if prop_name in meta_node:
734 stream_types_node = meta_node[prop_name]
735
736 for key in list(stream_types_node):
737 stream_types_node[key] = self._process_stream_type_node_include(stream_types_node[key])
738
739 # Make sure the metadata node is valid for the inclusion
740 # processing stage.
741 self._schema_validator.validate(meta_node, '2/config/metadata-pre-include')
742
743 # process inclusions
744 return self._process_node_include(meta_node, self._process_meta_node_include,
745 process_children_include)
746
747 # Processes the inclusions of the configuration node, modifying it
748 # during the process.
749 def _process_config_includes(self):
750 # Process inclusions in this order:
751 #
752 # 1. Clock type node, event type nodes, and trace type nodes
753 # (the order between those is not important).
754 #
755 # 2. Stream type nodes.
756 #
757 # 3. Metadata node.
758 #
759 # This is because:
760 #
761 # * A metadata node can include clock type nodes, a trace type
762 # node, stream type nodes, and event type nodes (indirectly).
763 #
764 # * A stream type node can include event type nodes.
765 #
766 # First, make sure the configuration node itself is valid for
767 # the inclusion processing stage.
768 self._schema_validator.validate(self._root_node,
769 '2/config/config-pre-include')
770
771 # Process metadata node inclusions.
772 #
773 # self._process_meta_node_include() returns a new (or the same)
774 # metadata node without any `$include` property in it,
775 # recursively.
776 prop_name = 'metadata'
777 self._root_node[prop_name] = self._process_meta_node_include(self._root_node[prop_name])
778
779 def _parse(self):
780 # Make sure the configuration node is minimally valid, that is,
781 # it contains a valid `version` property.
782 #
783 # This step does not validate the whole configuration node yet
784 # because we don't have an effective configuration node; we
785 # still need to:
786 #
787 # * Process inclusions.
788 # * Expand field types (aliases and inheritance).
789 self._schema_validator.validate(self._root_node, '2/config/config-min')
790
791 # process configuration node inclusions
792 self._process_config_includes()
793
794 # Expand field type nodes.
795 #
796 # This process:
797 #
798 # 1. Replaces field type aliases with "effective" field type
799 # nodes, recursively.
800 #
801 # After this step, the `type-aliases` property of the
802 # metadata node is gone.
803 #
804 # 2. Applies inheritance, following the `$inherit`/`inherit`
805 # properties.
806 #
807 # After this step, field type nodes do not contain `$inherit`
808 # or `inherit` properties.
809 #
810 # This is done blindly, in that the process _doesn't_ validate
811 # field type nodes at this point.
812 #
813 # The reason we must do this here for a barectf 2 configuration,
814 # considering that barectf 3 also supports field type node
815 # aliases and inheritance, is that we need to find specific
816 # packet header and packet context field type member nodes (for
817 # example, `stream_id`, `packet_size`, or `timestamp_end`) to
818 # set the `$features` properties of barectf 3 trace type and
819 # stream type nodes. Those field type nodes can be aliases,
820 # contain aliases, or inherit from other nodes.
821 self._expand_fts()
822
823 # Validate the whole, (almost) effective configuration node.
824 #
825 # It's almost effective because the `log-level` property of
826 # event type nodes can be log level aliases. Log level aliases
827 # are also a feature of a barectf 3 configuration node,
828 # therefore this is compatible.
829 self._schema_validator.validate(self._root_node, '2/config/config')
830
831 # Transform the current configuration node into a valid v3
832 # configuration node.
833 self._transform_config_node()
834
835 @property
836 def config_node(self):
837 return config_parse_common._ConfigNodeV3(self._root_node)
This page took 0.047122 seconds and 4 git commands to generate.