1 # The MIT License (MIT)
3 # Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
5 # Permission is hereby granted, free of charge, to any person obtaining a copy
6 # of this software and associated documentation files (the "Software"), to deal
7 # in the Software without restriction, including without limitation the rights
8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 # copies of the Software, and to permit persons to whom the Software is
10 # furnished to do so, subject to the following conditions:
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 from barectf
import metadata
35 class ConfigError(RuntimeError):
36 def __init__(self
, msg
, prev
=None):
46 def __init__(self
, version
, prefix
, metadata
, options
):
48 self
.version
= version
49 self
.metadata
= metadata
50 self
.options
= options
52 def _validate_metadata(self
, meta
):
54 validator
= _MetadataTypesHistologyValidator()
55 validator
.validate(meta
)
56 validator
= _MetadataDynamicTypesValidator()
57 validator
.validate(meta
)
58 validator
= _MetadataSpecialFieldsValidator()
59 validator
.validate(meta
)
60 except Exception as e
:
61 raise ConfigError('metadata error', e
)
64 validator
= _BarectfMetadataValidator()
65 validator
.validate(meta
)
66 except Exception as e
:
67 raise ConfigError('barectf metadata error', e
)
69 def _augment_metadata_env(self
, meta
):
70 version_tuple
= barectf
.get_version_tuple()
73 'tracer_name': 'barectf',
74 'tracer_major': version_tuple
[0],
75 'tracer_minor': version_tuple
[1],
76 'tracer_patch': version_tuple
[2],
77 'barectf_gen_date': str(datetime
.datetime
.now().isoformat()),
80 base_env
.update(meta
.env
)
88 def version(self
, value
):
96 def metadata(self
, value
):
97 self
._validate
_metadata
(value
)
98 self
._augment
_metadata
_env
(value
)
99 self
._metadata
= value
106 def prefix(self
, value
):
107 if not _is_valid_identifier(value
):
108 raise ConfigError('configuration prefix must be a valid C identifier')
117 def options(self
, options
):
118 self
._options
= options
123 self
._gen
_prefix
_def
= False
124 self
._gen
_default
_stream
_def
= False
127 def gen_prefix_def(self
):
128 return self
._gen
_prefix
_def
130 @gen_prefix_def.setter
131 def gen_prefix_def(self
, value
):
132 self
._gen
_prefix
_def
= value
135 def gen_default_stream_def(self
):
136 return self
._gen
_default
_stream
_def
138 @gen_default_stream_def.setter
139 def gen_default_stream_def(self
, value
):
140 self
._gen
_default
_stream
_def
= value
143 def _is_assoc_array_prop(node
):
144 return isinstance(node
, dict)
147 def _is_array_prop(node
):
148 return isinstance(node
, list)
151 def _is_int_prop(node
):
152 return type(node
) is int
155 def _is_str_prop(node
):
156 return type(node
) is str
159 def _is_bool_prop(node
):
160 return type(node
) is bool
163 def _is_valid_alignment(align
):
164 return ((align
& (align
- 1)) == 0) and align
> 0
167 def _byte_order_str_to_bo(bo_str
):
168 bo_str
= bo_str
.lower()
171 return metadata
.ByteOrder
.LE
173 return metadata
.ByteOrder
.BE
176 def _encoding_str_to_encoding(encoding_str
):
177 encoding_str
= encoding_str
.lower()
179 if encoding_str
== 'utf-8' or encoding_str
== 'utf8':
180 return metadata
.Encoding
.UTF8
181 elif encoding_str
== 'ascii':
182 return metadata
.Encoding
.ASCII
183 elif encoding_str
== 'none':
184 return metadata
.Encoding
.NONE
187 _re_iden
= re
.compile(r
'^[a-zA-Z][a-zA-Z0-9_]*$')
188 _ctf_keywords
= set([
207 def _is_valid_identifier(iden
):
208 if not _re_iden
.match(iden
):
211 if _re_iden
in _ctf_keywords
:
217 def _get_first_unknown_prop(node
, known_props
):
218 for prop_name
in node
:
219 if prop_name
in known_props
:
225 # This validator validates the configured metadata for barectf specific
230 # * all header/contexts are at least byte-aligned
231 # * all integer and floating point number sizes to be <= 64
232 # * no inner structures, arrays, or variants
233 class _BarectfMetadataValidator
:
235 self
._type
_to
_validate
_type
_func
= {
236 metadata
.Integer
: self
._validate
_int
_type
,
237 metadata
.FloatingPoint
: self
._validate
_float
_type
,
238 metadata
.Enum
: self
._validate
_enum
_type
,
239 metadata
.String
: self
._validate
_string
_type
,
240 metadata
.Struct
: self
._validate
_struct
_type
,
241 metadata
.Array
: self
._validate
_array
_type
,
242 metadata
.Variant
: self
._validate
_variant
_type
,
245 def _validate_int_type(self
, t
, entity_root
):
247 raise ConfigError('integer type\'s size must be lesser than or equal to 64 bits')
249 def _validate_float_type(self
, t
, entity_root
):
251 raise ConfigError('floating point number type\'s size must be lesser than or equal to 64 bits')
253 def _validate_enum_type(self
, t
, entity_root
):
254 if t
.value_type
.size
> 64:
255 raise ConfigError('enumeration type\'s integer type\'s size must be lesser than or equal to 64 bits')
257 def _validate_string_type(self
, t
, entity_root
):
260 def _validate_struct_type(self
, t
, entity_root
):
262 raise ConfigError('inner structure types are not supported as of this version')
264 for field_name
, field_type
in t
.fields
.items():
265 if entity_root
and self
._cur
_entity
is _Entity
.TRACE_PACKET_HEADER
:
266 if field_name
== 'uuid':
271 self
._validate
_type
(field_type
, False)
272 except Exception as e
:
273 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
275 def _validate_array_type(self
, t
, entity_root
):
276 raise ConfigError('array types are not supported as of this version')
278 def _validate_variant_type(self
, t
, entity_root
):
279 raise ConfigError('variant types are not supported as of this version')
281 def _validate_type(self
, t
, entity_root
):
282 self
._type
_to
_validate
_type
_func
[type(t
)](t
, entity_root
)
284 def _validate_entity(self
, t
):
288 # make sure entity is byte-aligned
290 raise ConfigError('type\'s alignment must be at least byte-aligned')
292 # make sure entity is a structure
293 if type(t
) is not metadata
.Struct
:
294 raise ConfigError('expecting a structure type')
297 self
._validate
_type
(t
, True)
299 def _validate_entities_and_names(self
, meta
):
300 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
303 self
._validate
_entity
(meta
.trace
.packet_header_type
)
304 except Exception as e
:
305 raise ConfigError('invalid trace packet header type', e
)
307 for stream_name
, stream
in meta
.streams
.items():
308 if not _is_valid_identifier(stream_name
):
309 raise ConfigError('stream name "{}" is not a valid C identifier'.format(stream_name
))
311 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
314 self
._validate
_entity
(stream
.packet_context_type
)
315 except Exception as e
:
316 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
318 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
321 self
._validate
_entity
(stream
.event_header_type
)
322 except Exception as e
:
323 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
325 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
328 self
._validate
_entity
(stream
.event_context_type
)
329 except Exception as e
:
330 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
333 for ev_name
, ev
in stream
.events
.items():
334 if not _is_valid_identifier(ev_name
):
335 raise ConfigError('event name "{}" is not a valid C identifier'.format(ev_name
))
337 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
340 self
._validate
_entity
(ev
.context_type
)
341 except Exception as e
:
342 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
344 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
347 self
._validate
_entity
(ev
.payload_type
)
348 except Exception as e
:
349 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
351 if stream
.is_event_empty(ev
):
352 raise ConfigError('event "{}" is empty'.format(ev_name
))
353 except Exception as e
:
354 raise ConfigError('invalid stream "{}"'.format(stream_name
), e
)
356 def _validate_default_stream(self
, meta
):
357 if meta
.default_stream_name
:
358 if meta
.default_stream_name
not in meta
.streams
.keys():
359 raise ConfigError('default stream name ("{}") does not exist'.format(meta
.default_stream_name
))
361 def validate(self
, meta
):
362 self
._validate
_entities
_and
_names
(meta
)
363 self
._validate
_default
_stream
(meta
)
366 # This validator validates special fields of trace, stream, and event
367 # types. For example, if checks that the "stream_id" field exists in the
368 # trace packet header if there's more than one stream, and much more.
369 class _MetadataSpecialFieldsValidator
:
370 def _validate_trace_packet_header_type(self
, t
):
371 # needs "stream_id" field?
372 if len(self
._meta
.streams
) > 1:
375 raise ConfigError('need "stream_id" field in trace packet header type (more than one stream), but trace packet header type is missing')
377 if type(t
) is not metadata
.Struct
:
378 raise ConfigError('need "stream_id" field in trace packet header type (more than one stream), but trace packet header type is not a structure type')
380 if 'stream_id' not in t
.fields
:
381 raise ConfigError('need "stream_id" field in trace packet header type (more than one stream)')
383 # validate "magic" and "stream_id" types
384 if type(t
) is not metadata
.Struct
:
387 for i
, (field_name
, field_type
) in enumerate(t
.fields
.items()):
388 if field_name
== 'magic':
389 if type(field_type
) is not metadata
.Integer
:
390 raise ConfigError('"magic" field in trace packet header type must be an integer type')
392 if field_type
.signed
or field_type
.size
!= 32:
393 raise ConfigError('"magic" field in trace packet header type must be a 32-bit unsigned integer type')
396 raise ConfigError('"magic" field must be the first trace packet header type\'s field')
397 elif field_name
== 'stream_id':
398 if type(field_type
) is not metadata
.Integer
:
399 raise ConfigError('"stream_id" field in trace packet header type must be an integer type')
401 if field_type
.signed
:
402 raise ConfigError('"stream_id" field in trace packet header type must be an unsigned integer type')
404 # "id" size can fit all event IDs
405 if len(self
._meta
.streams
) > (1 << field_type
.size
):
406 raise ConfigError('"stream_id" field\' size in trace packet header type is too small for the number of trace streams')
407 elif field_name
== 'uuid':
408 if self
._meta
.trace
.uuid
is None:
409 raise ConfigError('"uuid" field in trace packet header type specified, but no trace UUID provided')
411 if type(field_type
) is not metadata
.Array
:
412 raise ConfigError('"uuid" field in trace packet header type must be an array')
414 if field_type
.length
!= 16:
415 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
417 element_type
= field_type
.element_type
419 if type(element_type
) is not metadata
.Integer
:
420 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned bytes')
422 if element_type
.size
!= 8:
423 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned bytes')
425 if element_type
.signed
:
426 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned bytes')
428 if element_type
.align
!= 8:
429 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned, byte-aligned bytes')
431 def _validate_trace(self
, meta
):
432 self
._validate
_trace
_packet
_header
_type
(meta
.trace
.packet_header_type
)
434 def _validate_stream_packet_context(self
, stream
):
435 t
= stream
.packet_context_type
438 raise ConfigError('missing "packet-context-type" property in stream object')
440 if type(t
) is not metadata
.Struct
:
441 raise ConfigError('"packet-context-type": expecting a structure type')
443 # "timestamp_begin", if exists, is an unsigned integer type,
447 if 'timestamp_begin' in t
.fields
:
448 ts_begin
= t
.fields
['timestamp_begin']
450 if type(ts_begin
) is not metadata
.Integer
:
451 raise ConfigError('"timestamp_begin" field in stream packet context type must be an integer type')
454 raise ConfigError('"timestamp_begin" field in stream packet context type must be an unsigned integer type')
456 if not ts_begin
.property_mappings
:
457 raise ConfigError('"timestamp_begin" field in stream packet context type must be mapped to a clock')
459 # "timestamp_end", if exists, is an unsigned integer type,
463 if 'timestamp_end' in t
.fields
:
464 ts_end
= t
.fields
['timestamp_end']
466 if type(ts_end
) is not metadata
.Integer
:
467 raise ConfigError('"timestamp_end" field in stream packet context type must be an integer type')
470 raise ConfigError('"timestamp_end" field in stream packet context type must be an unsigned integer type')
472 if not ts_end
.property_mappings
:
473 raise ConfigError('"timestamp_end" field in stream packet context type must be mapped to a clock')
475 # "timestamp_begin" and "timestamp_end" exist together
476 if (('timestamp_begin' in t
.fields
) ^
('timestamp_end' in t
.fields
)):
477 raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be defined together in stream packet context type')
479 # "timestamp_begin" and "timestamp_end" are mapped to the same clock
480 if ts_begin
is not None and ts_end
is not None:
481 if ts_begin
.property_mappings
[0].object.name
!= ts_end
.property_mappings
[0].object.name
:
482 raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be mapped to the same clock object in stream packet context type')
484 # "events_discarded", if exists, is an unsigned integer type
485 if 'events_discarded' in t
.fields
:
486 events_discarded
= t
.fields
['events_discarded']
488 if type(events_discarded
) is not metadata
.Integer
:
489 raise ConfigError('"events_discarded" field in stream packet context type must be an integer type')
491 if events_discarded
.signed
:
492 raise ConfigError('"events_discarded" field in stream packet context type must be an unsigned integer type')
494 # "packet_size" and "content_size" must exist
495 if 'packet_size' not in t
.fields
:
496 raise ConfigError('missing "packet_size" field in stream packet context type')
498 packet_size
= t
.fields
['packet_size']
500 # "content_size" and "content_size" must exist
501 if 'content_size' not in t
.fields
:
502 raise ConfigError('missing "content_size" field in stream packet context type')
504 content_size
= t
.fields
['content_size']
506 # "packet_size" is an unsigned integer type
507 if type(packet_size
) is not metadata
.Integer
:
508 raise ConfigError('"packet_size" field in stream packet context type must be an integer type')
510 if packet_size
.signed
:
511 raise ConfigError('"packet_size" field in stream packet context type must be an unsigned integer type')
513 # "content_size" is an unsigned integer type
514 if type(content_size
) is not metadata
.Integer
:
515 raise ConfigError('"content_size" field in stream packet context type must be an integer type')
517 if content_size
.signed
:
518 raise ConfigError('"content_size" field in stream packet context type must be an unsigned integer type')
520 # "packet_size" size should be greater than or equal to "content_size" size
521 if content_size
.size
> packet_size
.size
:
522 raise ConfigError('"content_size" field size must be lesser than or equal to "packet_size" field size')
524 def _validate_stream_event_header(self
, stream
):
525 t
= stream
.event_header_type
528 if len(stream
.events
) > 1:
531 raise ConfigError('need "id" field in stream event header type (more than one event), but stream event header type is missing')
533 if type(t
) is not metadata
.Struct
:
534 raise ConfigError('need "id" field in stream event header type (more than one event), but stream event header type is not a structure type')
536 if 'id' not in t
.fields
:
537 raise ConfigError('need "id" field in stream event header type (more than one event)')
539 # validate "id" and "timestamp" types
540 if type(t
) is not metadata
.Struct
:
543 # "timestamp", if exists, is an unsigned integer type,
545 if 'timestamp' in t
.fields
:
546 ts
= t
.fields
['timestamp']
548 if type(ts
) is not metadata
.Integer
:
549 raise ConfigError('"timestamp" field in stream event header type must be an integer type')
552 raise ConfigError('"timestamp" field in stream event header type must be an unsigned integer type')
554 if not ts
.property_mappings
:
555 raise ConfigError('"timestamp" field in stream event header type must be mapped to a clock')
560 # "id" is an unsigned integer type
561 if type(eid
) is not metadata
.Integer
:
562 raise ConfigError('"id" field in stream event header type must be an integer type')
565 raise ConfigError('"id" field in stream event header type must be an unsigned integer type')
567 # "id" size can fit all event IDs
568 if len(stream
.events
) > (1 << eid
.size
):
569 raise ConfigError('"id" field\' size in stream event header type is too small for the number of stream events')
571 def _validate_stream(self
, stream
):
572 self
._validate
_stream
_packet
_context
(stream
)
573 self
._validate
_stream
_event
_header
(stream
)
575 def validate(self
, meta
):
577 self
._validate
_trace
(meta
)
579 for stream
in meta
.streams
.values():
581 self
._validate
_stream
(stream
)
582 except Exception as e
:
583 raise ConfigError('invalid stream "{}"'.format(stream
.name
), e
)
586 class _MetadataDynamicTypesValidatorStackEntry
:
587 def __init__(self
, base_t
):
588 self
._base
_t
= base_t
596 def index(self
, value
):
604 def base_t(self
, value
):
608 # Entities. Order of values is important here.
610 class _Entity(enum
.IntEnum
):
611 TRACE_PACKET_HEADER
= 0
612 STREAM_PACKET_CONTEXT
= 1
613 STREAM_EVENT_HEADER
= 2
614 STREAM_EVENT_CONTEXT
= 3
619 # This validator validates dynamic metadata types, that is, it ensures
620 # variable-length array lengths and variant tags actually point to
621 # something that exists. It also checks that variable-length array
622 # lengths point to integer types and variant tags to enumeration types.
623 class _MetadataDynamicTypesValidator
:
625 self
._type
_to
_visit
_type
_func
= {
626 metadata
.Integer
: None,
627 metadata
.FloatingPoint
: None,
629 metadata
.String
: None,
630 metadata
.Struct
: self
._visit
_struct
_type
,
631 metadata
.Array
: self
._visit
_array
_type
,
632 metadata
.Variant
: self
._visit
_variant
_type
,
635 self
._cur
_trace
= None
636 self
._cur
_stream
= None
637 self
._cur
_event
= None
639 def _lookup_path_from_base(self
, path
, parts
, base
, start_index
,
640 base_is_current
, from_t
):
645 while index
< len(parts
):
649 if type(cur_t
) is metadata
.Struct
:
650 enumerated_items
= enumerate(cur_t
.fields
.items())
653 for i
, (field_name
, field_type
) in enumerated_items
:
654 if field_name
== part
:
656 found_path
.append((i
, field_type
))
659 raise ConfigError('invalid path "{}": cannot find field "{}" in structure type'.format(path
, part
))
660 elif type(cur_t
) is metadata
.Variant
:
661 enumerated_items
= enumerate(cur_t
.types
.items())
664 for i
, (type_name
, type_type
) in enumerated_items
:
665 if type_name
== part
:
667 found_path
.append((i
, type_type
))
670 raise ConfigError('invalid path "{}": cannot find type "{}" in variant type'.format(path
, part
))
672 raise ConfigError('invalid path "{}": requesting "{}" in a non-variant, non-structure type'.format(path
, part
))
677 # make sure that the pointed type is not the pointing type
679 raise ConfigError('invalid path "{}": pointing to self'.format(path
))
681 # if we're here, we found the type; however, it could be located
682 # _after_ the variant/VLA looking for it, if the pointing
683 # and pointed types are in the same entity, so compare the
684 # current stack entries indexes to our index path in that case
685 if not base_is_current
:
688 for index
, entry
in enumerate(self
._stack
):
689 if index
== len(found_path
):
690 # end of index path; valid so far
693 if found_path
[index
][0] > entry
.index
:
694 raise ConfigError('invalid path "{}": pointed type is after pointing type'.format(path
))
696 # also make sure that both pointed and pointing types share
697 # a common structure ancestor
698 for index
, entry
in enumerate(self
._stack
):
699 if index
== len(found_path
):
702 if entry
.base_t
is not found_path
[index
][1]:
703 # found common ancestor
704 if type(entry
.base_t
) is metadata
.Variant
:
705 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
709 def _lookup_path_from_top(self
, path
, parts
):
711 raise ConfigError('invalid path "{}": multipart relative path not supported'.format(path
))
714 index
= len(self
._stack
) - 1
717 # check stack entries in reversed order
718 for entry
in reversed(self
._stack
):
719 # structure base type
720 if type(entry
.base_t
) is metadata
.Struct
:
722 enumerated_items
= enumerate(entry
.base_t
.fields
.items())
724 # lookup each field, until the current visiting index is met
725 for i
, (field_name
, field_type
) in enumerated_items
:
729 if field_name
== find_name
:
733 elif type(entry
.base_t
) is metadata
.Variant
:
734 enumerated_items
= enumerate(entry
.base_t
.types
.items())
736 # lookup each type, until the current visiting index is met
737 for i
, (type_name
, type_type
) in enumerated_items
:
741 if type_name
== find_name
:
743 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
747 # nothing returned here: cannot find type
748 raise ConfigError('invalid path "{}": cannot find type in current context'.format(path
))
750 def _lookup_path(self
, path
, from_t
):
751 parts
= path
.lower().split('.')
753 base_is_current
= False
756 if parts
[0] == 'trace':
757 if parts
[1] == 'packet' and parts
[2] == 'header':
758 # make sure packet header exists
759 if self
._cur
_trace
.packet_header_type
is None:
760 raise ConfigError('invalid path "{}": no defined trace packet header type'.format(path
))
762 base
= self
._cur
_trace
.packet_header_type
764 if self
._cur
_entity
== _Entity
.TRACE_PACKET_HEADER
:
765 base_is_current
= True
767 raise ConfigError('invalid path "{}": unknown names after "trace"'.format(path
))
768 elif parts
[0] == 'stream':
769 if parts
[1] == 'packet' and parts
[2] == 'context':
770 if self
._cur
_entity
< _Entity
.STREAM_PACKET_CONTEXT
:
771 raise ConfigError('invalid path "{}": cannot access stream packet context here'.format(path
))
773 if self
._cur
_stream
.packet_context_type
is None:
774 raise ConfigError('invalid path "{}": no defined stream packet context type'.format(path
))
776 base
= self
._cur
_stream
.packet_context_type
778 if self
._cur
_entity
== _Entity
.STREAM_PACKET_CONTEXT
:
779 base_is_current
= True
780 elif parts
[1] == 'event':
781 if parts
[2] == 'header':
782 if self
._cur
_entity
< _Entity
.STREAM_EVENT_HEADER
:
783 raise ConfigError('invalid path "{}": cannot access stream event header here'.format(path
))
785 if self
._cur
_stream
.event_header_type
is None:
786 raise ConfigError('invalid path "{}": no defined stream event header type'.format(path
))
788 base
= self
._cur
_stream
.event_header_type
790 if self
._cur
_entity
== _Entity
.STREAM_EVENT_HEADER
:
791 base_is_current
= True
792 elif parts
[2] == 'context':
793 if self
._cur
_entity
< _Entity
.STREAM_EVENT_CONTEXT
:
794 raise ConfigError('invalid path "{}": cannot access stream event context here'.format(path
))
796 if self
._cur
_stream
.event_context_type
is None:
797 raise ConfigError('invalid path "{}": no defined stream event context type'.format(path
))
799 base
= self
._cur
_stream
.event_context_type
801 if self
._cur
_entity
== _Entity
.STREAM_EVENT_CONTEXT
:
802 base_is_current
= True
804 raise ConfigError('invalid path "{}": unknown names after "stream.event"'.format(path
))
806 raise ConfigError('invalid path "{}": unknown names after "stream"'.format(path
))
811 if len(parts
) >= 2 and base
is None:
812 if parts
[0] == 'event':
813 if parts
[1] == 'context':
814 if self
._cur
_entity
< _Entity
.EVENT_CONTEXT
:
815 raise ConfigError('invalid path "{}": cannot access event context here'.format(path
))
817 if self
._cur
_event
.context_type
is None:
818 raise ConfigError('invalid path "{}": no defined event context type'.format(path
))
820 base
= self
._cur
_event
.context_type
822 if self
._cur
_entity
== _Entity
.EVENT_CONTEXT
:
823 base_is_current
= True
824 elif parts
[1] == 'payload' or parts
[1] == 'fields':
825 if self
._cur
_entity
< _Entity
.EVENT_PAYLOAD
:
826 raise ConfigError('invalid path "{}": cannot access event payload here'.format(path
))
828 if self
._cur
_event
.payload_type
is None:
829 raise ConfigError('invalid path "{}": no defined event payload type'.format(path
))
831 base
= self
._cur
_event
.payload_type
833 if self
._cur
_entity
== _Entity
.EVENT_PAYLOAD
:
834 base_is_current
= True
836 raise ConfigError('invalid path "{}": unknown names after "event"'.format(path
))
842 return self
._lookup
_path
_from
_base
(path
, parts
, base
, start_index
,
843 base_is_current
, from_t
)
845 return self
._lookup
_path
_from
_top
(path
, parts
)
847 def _stack_reset(self
):
850 def _stack_push(self
, base_t
):
851 entry
= _MetadataDynamicTypesValidatorStackEntry(base_t
)
852 self
._stack
.append(entry
)
854 def _stack_pop(self
):
857 def _stack_incr_index(self
):
858 self
._stack
[-1].index
+= 1
860 def _visit_struct_type(self
, t
):
863 for field_name
, field_type
in t
.fields
.items():
865 self
._visit
_type
(field_type
)
866 except Exception as e
:
867 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
869 self
._stack
_incr
_index
()
873 def _visit_array_type(self
, t
):
874 if t
.is_variable_length
:
877 length_type
= self
._lookup
_path
(t
.length
, t
)
878 except Exception as e
:
879 raise ConfigError('invalid array type\'s length', e
)
881 # make sure length type an unsigned integer
882 if type(length_type
) is not metadata
.Integer
:
883 raise ConfigError('array type\'s length does not point to an integer type')
885 if length_type
.signed
:
886 raise ConfigError('array type\'s length does not point to an unsigned integer type')
888 self
._visit
_type
(t
.element_type
)
890 def _visit_variant_type(self
, t
):
893 tag_type
= self
._lookup
_path
(t
.tag
, t
)
894 except Exception as e
:
895 raise ConfigError('invalid variant type\'s tag', e
)
897 # make sure tag type is an enumeration
898 if type(tag_type
) is not metadata
.Enum
:
899 raise ConfigError('variant type\'s tag does not point to an enumeration type')
901 # verify that each variant type's type exists as an enumeration member
902 for tag_name
in t
.types
.keys():
903 if tag_name
not in tag_type
.members
:
904 raise ConfigError('cannot find variant type\'s type "{}" in pointed tag type'.format(tag_name
))
908 for type_name
, type_type
in t
.types
.items():
910 self
._visit
_type
(type_type
)
911 except Exception as e
:
912 raise ConfigError('in variant type\'s type "{}"'.format(type_name
), e
)
914 self
._stack
_incr
_index
()
918 def _visit_type(self
, t
):
922 if type(t
) in self
._type
_to
_visit
_type
_func
:
923 func
= self
._type
_to
_visit
_type
_func
[type(t
)]
928 def _visit_event(self
, ev
):
934 # visit event context type
936 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
939 self
._visit
_type
(ev
.context_type
)
940 except Exception as e
:
941 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
943 # visit event payload type
945 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
948 self
._visit
_type
(ev
.payload_type
)
949 except Exception as e
:
950 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
952 def _visit_stream(self
, stream
):
953 stream_name
= stream
.name
956 self
._cur
_stream
= stream
958 # reset current event
959 self
._cur
_event
= None
961 # visit stream packet context type
963 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
966 self
._visit
_type
(stream
.packet_context_type
)
967 except Exception as e
:
968 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
970 # visit stream event header type
972 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
975 self
._visit
_type
(stream
.event_header_type
)
976 except Exception as e
:
977 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
979 # visit stream event context type
981 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
984 self
._visit
_type
(stream
.event_context_type
)
985 except Exception as e
:
986 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
989 for ev
in stream
.events
.values():
991 self
._visit
_event
(ev
)
992 except Exception as e
:
993 raise ConfigError('invalid stream "{}"'.format(stream_name
))
995 def validate(self
, meta
):
997 self
._cur
_trace
= meta
.trace
999 # visit trace packet header type
1001 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
1004 self
._visit
_type
(meta
.trace
.packet_header_type
)
1005 except Exception as e
:
1006 raise ConfigError('invalid packet header type in trace', e
)
1009 for stream
in meta
.streams
.values():
1010 self
._visit
_stream
(stream
)
1013 # Since type inheritance allows types to be only partially defined at
1014 # any place in the configuration, this validator validates that actual
1015 # trace, stream, and event types are all complete and valid. Therefore
1016 # an invalid, but unusued type alias is accepted.
1017 class _MetadataTypesHistologyValidator
:
1019 self
._type
_to
_validate
_type
_histology
_func
= {
1020 metadata
.Integer
: self
._validate
_integer
_histology
,
1021 metadata
.FloatingPoint
: self
._validate
_float
_histology
,
1022 metadata
.Enum
: self
._validate
_enum
_histology
,
1023 metadata
.String
: self
._validate
_string
_histology
,
1024 metadata
.Struct
: self
._validate
_struct
_histology
,
1025 metadata
.Array
: self
._validate
_array
_histology
,
1026 metadata
.Variant
: self
._validate
_variant
_histology
,
1029 def _validate_integer_histology(self
, t
):
1032 raise ConfigError('missing integer type\'s size')
1034 def _validate_float_histology(self
, t
):
1035 # exponent digits is set
1036 if t
.exp_size
is None:
1037 raise ConfigError('missing floating point number type\'s exponent size')
1039 # mantissa digits is set
1040 if t
.mant_size
is None:
1041 raise ConfigError('missing floating point number type\'s mantissa size')
1043 # exponent and mantissa sum is a multiple of 8
1044 if (t
.exp_size
+ t
.mant_size
) % 8 != 0:
1045 raise ConfigError('floating point number type\'s mantissa and exponent sizes sum must be a multiple of 8')
1047 def _validate_enum_histology(self
, t
):
1048 # integer type is set
1049 if t
.value_type
is None:
1050 raise ConfigError('missing enumeration type\'s value type')
1052 # there's at least one member
1054 raise ConfigError('enumeration type needs at least one member')
1056 # no overlapping values and all values are valid considering
1060 if t
.value_type
.signed
:
1061 value_min
= -(1 << t
.value_type
.size
- 1)
1062 value_max
= (1 << (t
.value_type
.size
- 1)) - 1
1065 value_max
= (1 << t
.value_type
.size
) - 1
1067 for label
, value
in t
.members
.items():
1069 if value
[0] <= rg
[1] and rg
[0] <= value
[1]:
1070 raise ConfigError('enumeration type\'s member "{}" overlaps another member'.format(label
))
1072 fmt
= 'enumeration type\'s member "{}": value {} is outside the value type range [{}, {}]'
1074 if value
[0] < value_min
or value
[0] > value_max
:
1075 raise ConfigError(fmt
.format(label
, value
[0], value_min
, value_max
))
1077 if value
[1] < value_min
or value
[1] > value_max
:
1078 raise ConfigError(fmt
.format(label
, value
[1], value_min
, value_max
))
1080 ranges
.append(value
)
1082 def _validate_string_histology(self
, t
):
1086 def _validate_struct_histology(self
, t
):
1087 # all fields are valid
1088 for field_name
, field_type
in t
.fields
.items():
1090 self
._validate
_type
_histology
(field_type
)
1091 except Exception as e
:
1092 raise ConfigError('invalid structure type\'s field "{}"'.format(field_name
), e
)
1094 def _validate_array_histology(self
, t
):
1096 if t
.length
is None:
1097 raise ConfigError('missing array type\'s length')
1099 # element type is set
1100 if t
.element_type
is None:
1101 raise ConfigError('missing array type\'s element type')
1103 # element type is valid
1105 self
._validate
_type
_histology
(t
.element_type
)
1106 except Exception as e
:
1107 raise ConfigError('invalid array type\'s element type', e
)
1109 def _validate_variant_histology(self
, t
):
1112 raise ConfigError('missing variant type\'s tag')
1114 # there's at least one type
1116 raise ConfigError('variant type needs at least one type')
1118 # all types are valid
1119 for type_name
, type_t
in t
.types
.items():
1121 self
._validate
_type
_histology
(type_t
)
1122 except Exception as e
:
1123 raise ConfigError('invalid variant type\'s type "{}"'.format(type_name
), e
)
1125 def _validate_type_histology(self
, t
):
1129 self
._type
_to
_validate
_type
_histology
_func
[type(t
)](t
)
1131 def _validate_entity_type_histology(self
, t
):
1135 if type(t
) is not metadata
.Struct
:
1136 raise ConfigError('expecting a structure type')
1138 self
._validate
_type
_histology
(t
)
1140 def _validate_event_types_histology(self
, ev
):
1143 # validate event context type
1145 self
._validate
_entity
_type
_histology
(ev
.context_type
)
1146 except Exception as e
:
1147 raise ConfigError('invalid event context type for event "{}"'.format(ev_name
), e
)
1149 # validate event payload type
1151 self
._validate
_entity
_type
_histology
(ev
.payload_type
)
1152 except Exception as e
:
1153 raise ConfigError('invalid event payload type for event "{}"'.format(ev_name
), e
)
1155 def _validate_stream_types_histology(self
, stream
):
1156 stream_name
= stream
.name
1158 # validate stream packet context type
1160 self
._validate
_entity
_type
_histology
(stream
.packet_context_type
)
1161 except Exception as e
:
1162 raise ConfigError('invalid stream packet context type for stream "{}"'.format(stream_name
), e
)
1164 # validate stream event header type
1166 self
._validate
_entity
_type
_histology
(stream
.event_header_type
)
1167 except Exception as e
:
1168 raise ConfigError('invalid stream event header type for stream "{}"'.format(stream_name
), e
)
1170 # validate stream event context type
1172 self
._validate
_entity
_type
_histology
(stream
.event_context_type
)
1173 except Exception as e
:
1174 raise ConfigError('invalid stream event context type for stream "{}"'.format(stream_name
), e
)
1177 for ev
in stream
.events
.values():
1179 self
._validate
_event
_types
_histology
(ev
)
1180 except Exception as e
:
1181 raise ConfigError('invalid event in stream "{}"'.format(stream_name
), e
)
1183 def validate(self
, meta
):
1184 # validate trace packet header type
1186 self
._validate
_entity
_type
_histology
(meta
.trace
.packet_header_type
)
1187 except Exception as e
:
1188 raise ConfigError('invalid trace packet header type', e
)
1191 for stream
in meta
.streams
.values():
1192 self
._validate
_stream
_types
_histology
(stream
)
1195 class _YamlConfigParser
:
1196 def __init__(self
, include_dirs
, ignore_include_not_found
, dump_config
):
1197 self
._class
_name
_to
_create
_type
_func
= {
1198 'int': self
._create
_integer
,
1199 'integer': self
._create
_integer
,
1200 'flt': self
._create
_float
,
1201 'float': self
._create
_float
,
1202 'floating-point': self
._create
_float
,
1203 'enum': self
._create
_enum
,
1204 'enumeration': self
._create
_enum
,
1205 'str': self
._create
_string
,
1206 'string': self
._create
_string
,
1207 'struct': self
._create
_struct
,
1208 'structure': self
._create
_struct
,
1209 'array': self
._create
_array
,
1210 'var': self
._create
_variant
,
1211 'variant': self
._create
_variant
,
1213 self
._type
_to
_create
_type
_func
= {
1214 metadata
.Integer
: self
._create
_integer
,
1215 metadata
.FloatingPoint
: self
._create
_float
,
1216 metadata
.Enum
: self
._create
_enum
,
1217 metadata
.String
: self
._create
_string
,
1218 metadata
.Struct
: self
._create
_struct
,
1219 metadata
.Array
: self
._create
_array
,
1220 metadata
.Variant
: self
._create
_variant
,
1222 self
._include
_dirs
= include_dirs
1223 self
._ignore
_include
_not
_found
= ignore_include_not_found
1224 self
._dump
_config
= dump_config
1226 def _set_byte_order(self
, metadata_node
):
1227 if 'trace' not in metadata_node
:
1228 raise ConfigError('missing "trace" property (metadata)')
1230 trace_node
= metadata_node
['trace']
1232 if not _is_assoc_array_prop(trace_node
):
1233 raise ConfigError('"trace" property (metadata) must be an associative array')
1235 if 'byte-order' not in trace_node
:
1236 raise ConfigError('missing "byte-order" property (trace)')
1238 bo_node
= trace_node
['byte-order']
1240 if not _is_str_prop(bo_node
):
1241 raise ConfigError('"byte-order" property of trace object must be a string ("le" or "be")')
1243 self
._bo
= _byte_order_str_to_bo(bo_node
)
1245 if self
._bo
is None:
1246 raise ConfigError('invalid "byte-order" property (trace): must be "le" or "be"')
1248 def _lookup_type_alias(self
, name
):
1249 if name
in self
._tas
:
1250 return copy
.deepcopy(self
._tas
[name
])
1252 def _set_int_clock_prop_mapping(self
, int_obj
, prop_mapping_node
):
1253 unk_prop
= _get_first_unknown_prop(prop_mapping_node
, ['type', 'name', 'property'])
1256 raise ConfigError('unknown property in integer type object\'s clock property mapping: "{}"'.format(unk_prop
))
1258 if 'name' not in prop_mapping_node
:
1259 raise ConfigError('missing "name" property in integer type object\'s clock property mapping')
1261 if 'property' not in prop_mapping_node
:
1262 raise ConfigError('missing "property" property in integer type object\'s clock property mapping')
1264 clock_name
= prop_mapping_node
['name']
1265 prop
= prop_mapping_node
['property']
1267 if not _is_str_prop(clock_name
):
1268 raise ConfigError('"name" property of integer type object\'s clock property mapping must be a string')
1270 if not _is_str_prop(prop
):
1271 raise ConfigError('"property" property of integer type object\'s clock property mapping must be a string')
1273 if clock_name
not in self
._clocks
:
1274 raise ConfigError('invalid clock name "{}" in integer type object\'s clock property mapping'.format(clock_name
))
1277 raise ConfigError('invalid "property" property in integer type object\'s clock property mapping: "{}"'.format(prop
))
1279 mapped_clock
= self
._clocks
[clock_name
]
1280 int_obj
.property_mappings
.append(metadata
.PropertyMapping(mapped_clock
, prop
))
1282 def _get_first_unknown_type_prop(self
, type_node
, known_props
):
1283 kp
= known_props
+ ['inherit', 'class']
1285 if self
._version
>= 201:
1286 kp
.append('$inherit')
1288 return _get_first_unknown_prop(type_node
, kp
)
1290 def _create_integer(self
, obj
, node
):
1292 # create integer object
1293 obj
= metadata
.Integer()
1295 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1302 'property-mappings',
1306 raise ConfigError('unknown integer type object property: "{}"'.format(unk_prop
))
1312 if not _is_int_prop(size
):
1313 raise ConfigError('"size" property of integer type object must be an integer')
1316 raise ConfigError('invalid integer size: {}'.format(size
))
1322 align
= node
['align']
1325 obj
.set_default_align()
1327 if not _is_int_prop(align
):
1328 raise ConfigError('"align" property of integer type object must be an integer')
1330 if not _is_valid_alignment(align
):
1331 raise ConfigError('invalid alignment: {}'.format(align
))
1336 if 'signed' in node
:
1337 signed
= node
['signed']
1340 obj
.set_default_signed()
1342 if not _is_bool_prop(signed
):
1343 raise ConfigError('"signed" property of integer type object must be a boolean')
1348 if 'byte-order' in node
:
1349 byte_order
= node
['byte-order']
1351 if byte_order
is None:
1352 obj
.byte_order
= self
._bo
1354 if not _is_str_prop(byte_order
):
1355 raise ConfigError('"byte-order" property of integer type object must be a string ("le" or "be")')
1357 byte_order
= _byte_order_str_to_bo(byte_order
)
1359 if byte_order
is None:
1360 raise ConfigError('invalid "byte-order" property in integer type object')
1362 obj
.byte_order
= byte_order
1364 obj
.byte_order
= self
._bo
1371 obj
.set_default_base()
1373 if not _is_str_prop(base
):
1374 raise ConfigError('"base" property of integer type object must be a string ("bin", "oct", "dec", or "hex")')
1385 raise ConfigError('unknown "base" property value: "{}" ("bin", "oct", "dec", and "hex" are accepted)'.format(base
))
1390 if 'encoding' in node
:
1391 encoding
= node
['encoding']
1393 if encoding
is None:
1394 obj
.set_default_encoding()
1396 if not _is_str_prop(encoding
):
1397 raise ConfigError('"encoding" property of integer type object must be a string ("none", "ascii", or "utf-8")')
1399 encoding
= _encoding_str_to_encoding(encoding
)
1401 if encoding
is None:
1402 raise ConfigError('invalid "encoding" property in integer type object')
1404 obj
.encoding
= encoding
1407 if 'property-mappings' in node
:
1408 prop_mappings
= node
['property-mappings']
1410 if prop_mappings
is None:
1411 obj
.set_default_property_mappings()
1413 if not _is_array_prop(prop_mappings
):
1414 raise ConfigError('"property-mappings" property of integer type object must be an array')
1416 if len(prop_mappings
) > 1:
1417 raise ConfigError('length of "property-mappings" array in integer type object must be 1')
1419 for index
, prop_mapping
in enumerate(prop_mappings
):
1420 if not _is_assoc_array_prop(prop_mapping
):
1421 raise ConfigError('elements of "property-mappings" property of integer type object must be associative arrays')
1423 if 'type' not in prop_mapping
:
1424 raise ConfigError('missing "type" property in integer type object\'s "property-mappings" array\'s element #{}'.format(index
))
1426 prop_type
= prop_mapping
['type']
1428 if not _is_str_prop(prop_type
):
1429 raise ConfigError('"type" property of integer type object\'s "property-mappings" array\'s element #{} must be a string'.format(index
))
1431 if prop_type
== 'clock':
1432 self
._set
_int
_clock
_prop
_mapping
(obj
, prop_mapping
)
1434 raise ConfigError('unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type
, index
))
1438 def _create_float(self
, obj
, node
):
1440 # create floating point number object
1441 obj
= metadata
.FloatingPoint()
1443 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1450 raise ConfigError('unknown floating point number type object property: "{}"'.format(unk_prop
))
1456 if not _is_assoc_array_prop(size
):
1457 raise ConfigError('"size" property of floating point number type object must be an associative array')
1459 unk_prop
= _get_first_unknown_prop(size
, ['exp', 'mant'])
1462 raise ConfigError('unknown floating point number type object\'s "size" property: "{}"'.format(unk_prop
))
1467 if not _is_int_prop(exp
):
1468 raise ConfigError('"exp" property of floating point number type object\'s "size" property must be an integer')
1471 raise ConfigError('invalid floating point number exponent size: {}')
1478 if not _is_int_prop(mant
):
1479 raise ConfigError('"mant" property of floating point number type object\'s "size" property must be an integer')
1482 raise ConfigError('invalid floating point number mantissa size: {}')
1484 obj
.mant_size
= mant
1488 align
= node
['align']
1491 obj
.set_default_align()
1493 if not _is_int_prop(align
):
1494 raise ConfigError('"align" property of floating point number type object must be an integer')
1496 if not _is_valid_alignment(align
):
1497 raise ConfigError('invalid alignment: {}'.format(align
))
1502 if 'byte-order' in node
:
1503 byte_order
= node
['byte-order']
1505 if byte_order
is None:
1506 obj
.byte_order
= self
._bo
1508 if not _is_str_prop(byte_order
):
1509 raise ConfigError('"byte-order" property of floating point number type object must be a string ("le" or "be")')
1511 byte_order
= _byte_order_str_to_bo(byte_order
)
1513 if byte_order
is None:
1514 raise ConfigError('invalid "byte-order" property in floating point number type object')
1516 obj
.byte_order
= self
._bo
1520 def _create_enum(self
, obj
, node
):
1522 # create enumeration object
1523 obj
= metadata
.Enum()
1525 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1531 raise ConfigError('unknown enumeration type object property: "{}"'.format(unk_prop
))
1534 if 'value-type' in node
:
1535 value_type_node
= node
['value-type']
1538 obj
.value_type
= self
._create
_type
(value_type_node
)
1539 except Exception as e
:
1540 raise ConfigError('cannot create enumeration type\'s integer type', e
)
1543 if 'members' in node
:
1544 members_node
= node
['members']
1546 if not _is_array_prop(members_node
):
1547 raise ConfigError('"members" property of enumeration type object must be an array')
1550 last_value
= obj
.last_value
1552 if last_value
is None:
1555 cur
= last_value
+ 1
1557 for index
, m_node
in enumerate(members_node
):
1558 if not _is_str_prop(m_node
) and not _is_assoc_array_prop(m_node
):
1559 raise ConfigError('invalid enumeration member #{}: expecting a string or an associative array'.format(index
))
1561 if _is_str_prop(m_node
):
1566 unk_prop
= _get_first_unknown_prop(m_node
, [
1572 raise ConfigError('unknown enumeration type member object property: "{}"'.format(unk_prop
))
1574 if 'label' not in m_node
:
1575 raise ConfigError('missing "label" property in enumeration member #{}'.format(index
))
1577 label
= m_node
['label']
1579 if not _is_str_prop(label
):
1580 raise ConfigError('"label" property of enumeration member #{} must be a string'.format(index
))
1582 if 'value' not in m_node
:
1583 raise ConfigError('missing "value" property in enumeration member ("{}")'.format(label
))
1585 value
= m_node
['value']
1587 if not _is_int_prop(value
) and not _is_array_prop(value
):
1588 raise ConfigError('invalid enumeration member ("{}"): expecting an integer or an array'.format(label
))
1590 if _is_int_prop(value
):
1592 value
= (value
, value
)
1595 raise ConfigError('invalid enumeration member ("{}"): range must have exactly two items'.format(label
))
1601 raise ConfigError('invalid enumeration member ("{}"): invalid range ({} > {})'.format(label
, mn
, mx
))
1606 obj
.members
[label
] = value
1610 def _create_string(self
, obj
, node
):
1612 # create string object
1613 obj
= metadata
.String()
1615 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1620 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1623 if 'encoding' in node
:
1624 encoding
= node
['encoding']
1626 if encoding
is None:
1627 obj
.set_default_encoding()
1629 if not _is_str_prop(encoding
):
1630 raise ConfigError('"encoding" property of string type object must be a string ("none", "ascii", or "utf-8")')
1632 encoding
= _encoding_str_to_encoding(encoding
)
1634 if encoding
is None:
1635 raise ConfigError('invalid "encoding" property in string type object')
1637 obj
.encoding
= encoding
1641 def _create_struct(self
, obj
, node
):
1643 # create structure object
1644 obj
= metadata
.Struct()
1646 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1652 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1655 if 'min-align' in node
:
1656 min_align
= node
['min-align']
1658 if min_align
is None:
1659 obj
.set_default_min_align()
1661 if not _is_int_prop(min_align
):
1662 raise ConfigError('"min-align" property of structure type object must be an integer')
1664 if not _is_valid_alignment(min_align
):
1665 raise ConfigError('invalid minimum alignment: {}'.format(min_align
))
1667 obj
.min_align
= min_align
1670 if 'fields' in node
:
1671 fields
= node
['fields']
1674 obj
.set_default_fields()
1676 if not _is_assoc_array_prop(fields
):
1677 raise ConfigError('"fields" property of structure type object must be an associative array')
1679 for field_name
, field_node
in fields
.items():
1680 if not _is_valid_identifier(field_name
):
1681 raise ConfigError('"{}" is not a valid field name for structure type'.format(field_name
))
1684 obj
.fields
[field_name
] = self
._create
_type
(field_node
)
1685 except Exception as e
:
1686 raise ConfigError('cannot create structure type\'s field "{}"'.format(field_name
), e
)
1690 def _create_array(self
, obj
, node
):
1692 # create array object
1693 obj
= metadata
.Array()
1695 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1701 raise ConfigError('unknown array type object property: "{}"'.format(unk_prop
))
1704 if 'length' in node
:
1705 length
= node
['length']
1707 if not _is_int_prop(length
) and not _is_str_prop(length
):
1708 raise ConfigError('"length" property of array type object must be an integer or a string')
1710 if type(length
) is int and length
< 0:
1711 raise ConfigError('invalid static array length: {}'.format(length
))
1716 if 'element-type' in node
:
1717 element_type_node
= node
['element-type']
1720 obj
.element_type
= self
._create
_type
(node
['element-type'])
1721 except Exception as e
:
1722 raise ConfigError('cannot create array type\'s element type', e
)
1726 def _create_variant(self
, obj
, node
):
1728 # create variant object
1729 obj
= metadata
.Variant()
1731 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1737 raise ConfigError('unknown variant type object property: "{}"'.format(unk_prop
))
1743 if not _is_str_prop(tag
):
1744 raise ConfigError('"tag" property of variant type object must be a string')
1746 # do not validate variant tag for the moment; will be done in a
1752 types
= node
['types']
1754 if not _is_assoc_array_prop(types
):
1755 raise ConfigError('"types" property of variant type object must be an associative array')
1757 # do not validate type names for the moment; will be done in a
1759 for type_name
, type_node
in types
.items():
1760 if not _is_valid_identifier(type_name
):
1761 raise ConfigError('"{}" is not a valid type name for variant type'.format(type_name
))
1764 obj
.types
[type_name
] = self
._create
_type
(type_node
)
1765 except Exception as e
:
1766 raise ConfigError('cannot create variant type\'s type "{}"'.format(type_name
), e
)
1770 def _create_type(self
, type_node
):
1771 if type(type_node
) is str:
1772 t
= self
._lookup
_type
_alias
(type_node
)
1775 raise ConfigError('unknown type alias "{}"'.format(type_node
))
1779 if not _is_assoc_array_prop(type_node
):
1780 raise ConfigError('type objects must be associative arrays or strings (type alias name)')
1787 if self
._version
>= 200:
1788 if 'inherit' in type_node
:
1789 inherit_prop
= 'inherit'
1790 inherit_node
= type_node
[inherit_prop
]
1792 if self
._version
>= 201:
1793 if '$inherit' in type_node
:
1794 if inherit_node
is not None:
1795 raise ConfigError('cannot specify both "inherit" and "$inherit" properties of type object: prefer "$inherit"')
1797 inherit_prop
= '$inherit'
1798 inherit_node
= type_node
[inherit_prop
]
1800 if inherit_node
is not None and 'class' in type_node
:
1801 raise ConfigError('cannot specify both "{}" and "class" properties in type object'.format(inherit_prop
))
1803 if inherit_node
is not None:
1804 if not _is_str_prop(inherit_node
):
1805 raise ConfigError('"{}" property of type object must be a string'.format(inherit_prop
))
1807 base
= self
._lookup
_type
_alias
(inherit_node
)
1810 raise ConfigError('cannot inherit from type alias "{}": type alias does not exist at this point'.format(inherit_node
))
1812 func
= self
._type
_to
_create
_type
_func
[type(base
)]
1814 if 'class' not in type_node
:
1815 raise ConfigError('type objects which do not inherit must have a "class" property')
1817 class_name
= type_node
['class']
1819 if type(class_name
) is not str:
1820 raise ConfigError('type objects\' "class" property must be a string')
1822 if class_name
not in self
._class
_name
_to
_create
_type
_func
:
1823 raise ConfigError('unknown type class "{}"'.format(class_name
))
1826 func
= self
._class
_name
_to
_create
_type
_func
[class_name
]
1828 return func(base
, type_node
)
1830 def _register_type_aliases(self
, metadata_node
):
1833 if 'type-aliases' not in metadata_node
:
1836 ta_node
= metadata_node
['type-aliases']
1841 if not _is_assoc_array_prop(ta_node
):
1842 raise ConfigError('"type-aliases" property (metadata) must be an associative array')
1844 for ta_name
, ta_type
in ta_node
.items():
1845 if ta_name
in self
._tas
:
1846 raise ConfigError('duplicate type alias "{}"'.format(ta_name
))
1849 t
= self
._create
_type
(ta_type
)
1850 except Exception as e
:
1851 raise ConfigError('cannot create type alias "{}"'.format(ta_name
), e
)
1853 self
._tas
[ta_name
] = t
1855 def _create_clock(self
, node
):
1856 # create clock object
1857 clock
= metadata
.Clock()
1859 if not _is_assoc_array_prop(node
):
1860 raise ConfigError('clock objects must be associative arrays')
1872 if self
._version
>= 201:
1873 known_props
.append('$return-ctype')
1875 unk_prop
= _get_first_unknown_prop(node
, known_props
)
1878 raise ConfigError('unknown clock object property: "{}"'.format(unk_prop
))
1882 uuidp
= node
['uuid']
1885 clock
.set_default_uuid()
1887 if not _is_str_prop(uuidp
):
1888 raise ConfigError('"uuid" property of clock object must be a string')
1891 uuidp
= uuid
.UUID(uuidp
)
1893 raise ConfigError('malformed UUID (clock object): "{}"'.format(uuidp
))
1898 if 'description' in node
:
1899 desc
= node
['description']
1902 clock
.set_default_description()
1904 if not _is_str_prop(desc
):
1905 raise ConfigError('"description" property of clock object must be a string')
1907 clock
.description
= desc
1914 clock
.set_default_freq()
1916 if not _is_int_prop(freq
):
1917 raise ConfigError('"freq" property of clock object must be an integer')
1920 raise ConfigError('invalid clock frequency: {}'.format(freq
))
1925 if 'error-cycles' in node
:
1926 error_cycles
= node
['error-cycles']
1928 if error_cycles
is None:
1929 clock
.set_default_error_cycles()
1931 if not _is_int_prop(error_cycles
):
1932 raise ConfigError('"error-cycles" property of clock object must be an integer')
1934 if error_cycles
< 0:
1935 raise ConfigError('invalid clock error cycles: {}'.format(error_cycles
))
1937 clock
.error_cycles
= error_cycles
1940 if 'offset' in node
:
1941 offset
= node
['offset']
1944 clock
.set_default_offset_seconds()
1945 clock
.set_default_offset_cycles()
1947 if not _is_assoc_array_prop(offset
):
1948 raise ConfigError('"offset" property of clock object must be an associative array')
1950 unk_prop
= _get_first_unknown_prop(offset
, ['cycles', 'seconds'])
1953 raise ConfigError('unknown clock object\'s offset property: "{}"'.format(unk_prop
))
1956 if 'cycles' in offset
:
1957 offset_cycles
= offset
['cycles']
1959 if offset_cycles
is None:
1960 clock
.set_default_offset_cycles()
1962 if not _is_int_prop(offset_cycles
):
1963 raise ConfigError('"cycles" property of clock object\'s offset property must be an integer')
1965 if offset_cycles
< 0:
1966 raise ConfigError('invalid clock offset cycles: {}'.format(offset_cycles
))
1968 clock
.offset_cycles
= offset_cycles
1971 if 'seconds' in offset
:
1972 offset_seconds
= offset
['seconds']
1974 if offset_seconds
is None:
1975 clock
.set_default_offset_seconds()
1977 if not _is_int_prop(offset_seconds
):
1978 raise ConfigError('"seconds" property of clock object\'s offset property must be an integer')
1980 if offset_seconds
< 0:
1981 raise ConfigError('invalid clock offset seconds: {}'.format(offset_seconds
))
1983 clock
.offset_seconds
= offset_seconds
1986 if 'absolute' in node
:
1987 absolute
= node
['absolute']
1989 if absolute
is None:
1990 clock
.set_default_absolute()
1992 if not _is_bool_prop(absolute
):
1993 raise ConfigError('"absolute" property of clock object must be a boolean')
1995 clock
.absolute
= absolute
1998 # v2.0: "return-ctype"
1999 # v2.1+: "$return-ctype"
2000 return_ctype_node
= None
2002 if self
._version
>= 200:
2003 if 'return-ctype' in node
:
2004 return_ctype_prop
= 'return-ctype'
2005 return_ctype_node
= node
[return_ctype_prop
]
2007 if self
._version
>= 201:
2008 if '$return-ctype' in node
:
2009 if return_ctype_node
is not None:
2010 raise ConfigError('cannot specify both "return-ctype" and "$return-ctype" properties of clock object: prefer "$return-ctype"')
2012 return_ctype_prop
= '$return-ctype'
2013 return_ctype_node
= node
[return_ctype_prop
]
2015 if return_ctype_node
is not None:
2016 if return_ctype_node
is None:
2017 clock
.set_default_return_ctype()
2019 if not _is_str_prop(return_ctype_node
):
2020 raise ConfigError('"{}" property of clock object must be a string'.format(return_ctype_prop
))
2022 clock
.return_ctype
= return_ctype_node
2026 def _register_clocks(self
, metadata_node
):
2027 self
._clocks
= collections
.OrderedDict()
2029 if 'clocks' not in metadata_node
:
2032 clocks_node
= metadata_node
['clocks']
2034 if clocks_node
is None:
2037 if not _is_assoc_array_prop(clocks_node
):
2038 raise ConfigError('"clocks" property (metadata) must be an associative array')
2040 for clock_name
, clock_node
in clocks_node
.items():
2041 if not _is_valid_identifier(clock_name
):
2042 raise ConfigError('invalid clock name: "{}"'.format(clock_name
))
2044 if clock_name
in self
._clocks
:
2045 raise ConfigError('duplicate clock "{}"'.format(clock_name
))
2048 clock
= self
._create
_clock
(clock_node
)
2049 except Exception as e
:
2050 raise ConfigError('cannot create clock "{}"'.format(clock_name
), e
)
2052 clock
.name
= clock_name
2053 self
._clocks
[clock_name
] = clock
2055 def _create_env(self
, metadata_node
):
2056 env
= collections
.OrderedDict()
2058 if 'env' not in metadata_node
:
2061 env_node
= metadata_node
['env']
2063 if env_node
is None:
2066 if not _is_assoc_array_prop(env_node
):
2067 raise ConfigError('"env" property (metadata) must be an associative array')
2069 for env_name
, env_value
in env_node
.items():
2071 raise ConfigError('duplicate environment variable "{}"'.format(env_name
))
2073 if not _is_valid_identifier(env_name
):
2074 raise ConfigError('invalid environment variable name: "{}"'.format(env_name
))
2076 if not _is_int_prop(env_value
) and not _is_str_prop(env_value
):
2077 raise ConfigError('invalid environment variable value ("{}"): expecting integer or string'.format(env_name
))
2079 env
[env_name
] = env_value
2083 def _register_log_levels(self
, metadata_node
):
2084 self
._log
_levels
= dict()
2087 # v2.0: "log-levels"
2088 # v2.1+: "$log-levels"
2089 log_levels_node
= None
2091 if self
._version
>= 200:
2092 if 'log-levels' in metadata_node
:
2093 log_levels_prop
= 'log-levels'
2094 log_levels_node
= metadata_node
[log_levels_prop
]
2096 if self
._version
>= 201:
2097 if '$log-levels' in metadata_node
:
2098 if log_levels_node
is not None:
2099 raise ConfigError('cannot specify both "log-levels" and "$log-levels" properties of metadata object: prefer "$log-levels"')
2101 log_levels_prop
= '$log-levels'
2102 log_levels_node
= metadata_node
[log_levels_prop
]
2104 if log_levels_node
is None:
2107 if not _is_assoc_array_prop(log_levels_node
):
2108 raise ConfigError('"{}" property (metadata) must be an associative array'.format(log_levels_prop
))
2110 for ll_name
, ll_value
in log_levels_node
.items():
2111 if ll_name
in self
._log
_levels
:
2112 raise ConfigError('duplicate log level entry "{}"'.format(ll_name
))
2114 if not _is_int_prop(ll_value
):
2115 raise ConfigError('invalid log level entry ("{}"): expecting an integer'.format(ll_name
))
2118 raise ConfigError('invalid log level entry ("{}"): log level value must be positive'.format(ll_name
))
2120 self
._log
_levels
[ll_name
] = ll_value
2122 def _create_trace(self
, metadata_node
):
2123 # create trace object
2124 trace
= metadata
.Trace()
2126 if 'trace' not in metadata_node
:
2127 raise ConfigError('missing "trace" property (metadata)')
2129 trace_node
= metadata_node
['trace']
2131 if not _is_assoc_array_prop(trace_node
):
2132 raise ConfigError('"trace" property (metadata) must be an associative array')
2134 unk_prop
= _get_first_unknown_prop(trace_node
, [
2137 'packet-header-type',
2141 raise ConfigError('unknown trace object property: "{}"'.format(unk_prop
))
2143 # set byte order (already parsed)
2144 trace
.byte_order
= self
._bo
2147 if 'uuid' in trace_node
and trace_node
['uuid'] is not None:
2148 uuidp
= trace_node
['uuid']
2150 if not _is_str_prop(uuidp
):
2151 raise ConfigError('"uuid" property of trace object must be a string')
2154 uuidp
= uuid
.uuid1()
2157 uuidp
= uuid
.UUID(uuidp
)
2159 raise ConfigError('malformed UUID (trace object): "{}"'.format(uuidp
))
2163 # packet header type
2164 if 'packet-header-type' in trace_node
and trace_node
['packet-header-type'] is not None:
2166 ph_type
= self
._create
_type
(trace_node
['packet-header-type'])
2167 except Exception as e
:
2168 raise ConfigError('cannot create packet header type (trace)', e
)
2170 trace
.packet_header_type
= ph_type
2174 def _lookup_log_level(self
, ll
):
2175 if _is_int_prop(ll
):
2177 elif _is_str_prop(ll
) and ll
in self
._log
_levels
:
2178 return self
._log
_levels
[ll
]
2180 def _create_event(self
, event_node
):
2181 event
= metadata
.Event()
2183 if not _is_assoc_array_prop(event_node
):
2184 raise ConfigError('event objects must be associative arrays')
2186 unk_prop
= _get_first_unknown_prop(event_node
, [
2193 raise ConfigError('unknown event object property: "{}"'.format(unk_prop
))
2195 if 'log-level' in event_node
and event_node
['log-level'] is not None:
2196 ll_node
= event_node
['log-level']
2198 if _is_str_prop(ll_node
):
2199 ll_value
= self
._lookup
_log
_level
(event_node
['log-level'])
2201 if ll_value
is None:
2202 raise ConfigError('cannot find log level "{}"'.format(ll_node
))
2204 ll
= metadata
.LogLevel(event_node
['log-level'], ll_value
)
2205 elif _is_int_prop(ll_node
):
2207 raise ConfigError('invalid log level value {}: value must be positive'.format(ll_node
))
2209 ll
= metadata
.LogLevel(None, ll_node
)
2211 raise ConfigError('"log-level" property must be either a string or an integer')
2213 event
.log_level
= ll
2215 if 'context-type' in event_node
and event_node
['context-type'] is not None:
2216 ctx_type_node
= event_node
['context-type']
2219 t
= self
._create
_type
(event_node
['context-type'])
2220 except Exception as e
:
2221 raise ConfigError('cannot create event\'s context type object', e
)
2223 event
.context_type
= t
2225 if 'payload-type' in event_node
and event_node
['payload-type'] is not None:
2227 t
= self
._create
_type
(event_node
['payload-type'])
2228 except Exception as e
:
2229 raise ConfigError('cannot create event\'s payload type object', e
)
2231 event
.payload_type
= t
2235 def _create_stream(self
, stream_name
, stream_node
):
2236 stream
= metadata
.Stream()
2238 if not _is_assoc_array_prop(stream_node
):
2239 raise ConfigError('stream objects must be associative arrays')
2242 'packet-context-type',
2243 'event-header-type',
2244 'event-context-type',
2248 if self
._version
>= 202:
2249 known_props
.append('$default')
2251 unk_prop
= _get_first_unknown_prop(stream_node
, known_props
)
2256 if unk_prop
== '$default':
2257 add
= ' (use version 2.2 or greater)'
2259 raise ConfigError('unknown stream object property{}: "{}"'.format(add
, unk_prop
))
2261 if 'packet-context-type' in stream_node
and stream_node
['packet-context-type'] is not None:
2263 t
= self
._create
_type
(stream_node
['packet-context-type'])
2264 except Exception as e
:
2265 raise ConfigError('cannot create stream\'s packet context type object', e
)
2267 stream
.packet_context_type
= t
2269 if 'event-header-type' in stream_node
and stream_node
['event-header-type'] is not None:
2271 t
= self
._create
_type
(stream_node
['event-header-type'])
2272 except Exception as e
:
2273 raise ConfigError('cannot create stream\'s event header type object', e
)
2275 stream
.event_header_type
= t
2277 if 'event-context-type' in stream_node
and stream_node
['event-context-type'] is not None:
2279 t
= self
._create
_type
(stream_node
['event-context-type'])
2280 except Exception as e
:
2281 raise ConfigError('cannot create stream\'s event context type object', e
)
2283 stream
.event_context_type
= t
2285 if 'events' not in stream_node
:
2286 raise ConfigError('missing "events" property in stream object')
2288 events
= stream_node
['events']
2290 if events
is not None:
2291 if not _is_assoc_array_prop(events
):
2292 raise ConfigError('"events" property of stream object must be an associative array')
2295 raise ConfigError('at least one event is needed within a stream object')
2299 for ev_name
, ev_node
in events
.items():
2301 ev
= self
._create
_event
(ev_node
)
2302 except Exception as e
:
2303 raise ConfigError('cannot create event "{}"'.format(ev_name
), e
)
2307 stream
.events
[ev_name
] = ev
2310 if '$default' in stream_node
and stream_node
['$default'] is not None:
2311 default_node
= stream_node
['$default']
2313 if not _is_bool_prop(default_node
):
2314 raise ConfigError('invalid "$default" property in stream object: expecting a boolean')
2317 if self
._meta
.default_stream_name
is not None and self
._meta
.default_stream_name
!= stream_name
:
2318 fmt
= 'cannot specify more than one default stream (default stream already set to "{}")'
2319 raise ConfigError(fmt
.format(self
._meta
.default_stream_name
))
2321 self
._meta
.default_stream_name
= stream_name
2325 def _create_streams(self
, metadata_node
):
2326 streams
= collections
.OrderedDict()
2328 if 'streams' not in metadata_node
:
2329 raise ConfigError('missing "streams" property (metadata)')
2331 streams_node
= metadata_node
['streams']
2333 if not _is_assoc_array_prop(streams_node
):
2334 raise ConfigError('"streams" property (metadata) must be an associative array')
2336 if not streams_node
:
2337 raise ConfigError('at least one stream is needed (metadata)')
2341 for stream_name
, stream_node
in streams_node
.items():
2343 stream
= self
._create
_stream
(stream_name
, stream_node
)
2344 except Exception as e
:
2345 raise ConfigError('cannot create stream "{}"'.format(stream_name
), e
)
2348 stream
.name
= str(stream_name
)
2349 streams
[stream_name
] = stream
2354 def _create_metadata(self
, root
):
2355 self
._meta
= metadata
.Metadata()
2357 if 'metadata' not in root
:
2358 raise ConfigError('missing "metadata" property (configuration)')
2360 metadata_node
= root
['metadata']
2362 if not _is_assoc_array_prop(metadata_node
):
2363 raise ConfigError('"metadata" property (configuration) must be an associative array')
2374 if self
._version
>= 201:
2375 known_props
.append('$log-levels')
2377 if self
._version
>= 202:
2378 known_props
.append('$default-stream')
2380 unk_prop
= _get_first_unknown_prop(metadata_node
, known_props
)
2385 if unk_prop
== '$include':
2386 add
= ' (use version 2.1 or greater)'
2388 if unk_prop
== '$default-stream':
2389 add
= ' (use version 2.2 or greater)'
2391 raise ConfigError('unknown metadata property{}: "{}"'.format(add
, unk_prop
))
2393 if '$default-stream' in metadata_node
and metadata_node
['$default-stream'] is not None:
2394 default_stream_node
= metadata_node
['$default-stream']
2396 if not _is_str_prop(default_stream_node
):
2397 raise ConfigError('invalid "$default-stream" property (metadata): expecting a string')
2399 self
._meta
.default_stream_name
= default_stream_node
2401 self
._set
_byte
_order
(metadata_node
)
2402 self
._register
_clocks
(metadata_node
)
2403 self
._meta
.clocks
= self
._clocks
2404 self
._register
_type
_aliases
(metadata_node
)
2405 self
._meta
.env
= self
._create
_env
(metadata_node
)
2406 self
._meta
.trace
= self
._create
_trace
(metadata_node
)
2407 self
._register
_log
_levels
(metadata_node
)
2408 self
._meta
.streams
= self
._create
_streams
(metadata_node
)
2412 def _get_version(self
, root
):
2413 if 'version' not in root
:
2414 raise ConfigError('missing "version" property (configuration)')
2416 version_node
= root
['version']
2418 if not _is_str_prop(version_node
):
2419 raise ConfigError('"version" property (configuration) must be a string')
2421 version_node
= version_node
.strip()
2423 if version_node
not in ['2.0', '2.1', '2.2']:
2424 raise ConfigError('unsupported version ({}): versions 2.0, 2.1, and 2.2 are supported'.format(version_node
))
2426 # convert version string to comparable version integer
2427 parts
= version_node
.split('.')
2428 version
= int(parts
[0]) * 100 + int(parts
[1])
2432 def _get_prefix(self
, root
):
2433 def_prefix
= 'barectf_'
2435 if 'prefix' not in root
:
2438 prefix_node
= root
['prefix']
2440 if prefix_node
is None:
2443 if not _is_str_prop(prefix_node
):
2444 raise ConfigError('"prefix" property (configuration) must be a string')
2446 if not _is_valid_identifier(prefix_node
):
2447 raise ConfigError('"prefix" property (configuration) must be a valid C identifier')
2451 def _get_options(self
, root
):
2452 cfg_options
= ConfigOptions()
2454 if 'options' not in root
:
2457 options_node
= root
['options']
2459 if not _is_assoc_array_prop(options_node
):
2460 raise ConfigError('"options" property (configuration) must be an associative array')
2464 'gen-default-stream-def',
2466 unk_prop
= _get_first_unknown_prop(options_node
, known_props
)
2469 raise ConfigError('unknown configuration option property: "{}"'.format(unk_prop
))
2471 if 'gen-prefix-def' in options_node
and options_node
['gen-prefix-def'] is not None:
2472 gen_prefix_def_node
= options_node
['gen-prefix-def']
2474 if not _is_bool_prop(gen_prefix_def_node
):
2475 raise ConfigError('invalid configuration option "gen-prefix-def": expecting a boolean')
2477 cfg_options
.gen_prefix_def
= gen_prefix_def_node
2479 if 'gen-default-stream-def' in options_node
and options_node
['gen-default-stream-def'] is not None:
2480 gen_default_stream_def_node
= options_node
['gen-default-stream-def']
2482 if not _is_bool_prop(gen_default_stream_def_node
):
2483 raise ConfigError('invalid configuration option "gen-default-stream-def": expecting a boolean')
2485 cfg_options
.gen_default_stream_def
= gen_default_stream_def_node
2489 def _get_last_include_file(self
):
2490 if self
._include
_stack
:
2491 return self
._include
_stack
[-1]
2493 return self
._root
_yaml
_path
2495 def _load_include(self
, yaml_path
):
2496 for inc_dir
in self
._include
_dirs
:
2497 # current include dir + file name path
2498 # note: os.path.join() only takes the last arg if it's absolute
2499 inc_path
= os
.path
.join(inc_dir
, yaml_path
)
2501 # real path (symbolic links resolved)
2502 real_path
= os
.path
.realpath(inc_path
)
2504 # normalized path (weird stuff removed!)
2505 norm_path
= os
.path
.normpath(real_path
)
2507 if not os
.path
.isfile(norm_path
):
2508 # file does not exist: skip
2511 if norm_path
in self
._include
_stack
:
2512 base_path
= self
._get
_last
_include
_file
()
2513 raise ConfigError('in "{}": cannot recursively include file "{}"'.format(base_path
, norm_path
))
2515 self
._include
_stack
.append(norm_path
)
2518 return self
._yaml
_ordered
_load
(norm_path
)
2520 if not self
._ignore
_include
_not
_found
:
2521 base_path
= self
._get
_last
_include
_file
()
2522 raise ConfigError('in "{}": cannot include file "{}": file not found in include directories'.format(base_path
, yaml_path
))
2526 def _get_include_paths(self
, include_node
):
2527 if include_node
is None:
2530 if _is_str_prop(include_node
):
2531 return [include_node
]
2533 if _is_array_prop(include_node
):
2534 for include_path
in include_node
:
2535 if not _is_str_prop(include_path
):
2536 raise ConfigError('invalid include property: expecting array of strings')
2540 raise ConfigError('invalid include property: expecting string or array of strings')
2542 def _update_node(self
, base_node
, overlay_node
):
2543 for olay_key
, olay_value
in overlay_node
.items():
2544 if olay_key
in base_node
:
2545 base_value
= base_node
[olay_key
]
2547 if _is_assoc_array_prop(olay_value
) and _is_assoc_array_prop(base_value
):
2548 # merge dictionaries
2549 self
._update
_node
(base_value
, olay_value
)
2550 elif _is_array_prop(olay_value
) and _is_array_prop(base_value
):
2551 # append extension array items to base items
2552 base_value
+= olay_value
2554 # fall back to replacing
2555 base_node
[olay_key
] = olay_value
2557 base_node
[olay_key
] = olay_value
2559 def _process_node_include(self
, last_overlay_node
, name
,
2560 process_base_include_cb
,
2561 process_children_include_cb
=None):
2562 if not _is_assoc_array_prop(last_overlay_node
):
2563 raise ConfigError('{} objects must be associative arrays'.format(name
))
2565 # process children inclusions first
2566 if process_children_include_cb
:
2567 process_children_include_cb(last_overlay_node
)
2569 if '$include' in last_overlay_node
:
2570 include_node
= last_overlay_node
['$include']
2573 return last_overlay_node
2575 include_paths
= self
._get
_include
_paths
(include_node
)
2576 cur_base_path
= self
._get
_last
_include
_file
()
2579 # keep the include paths and remove the include property
2580 include_paths
= copy
.deepcopy(include_paths
)
2581 del last_overlay_node
['$include']
2583 for include_path
in include_paths
:
2584 # load raw YAML from included file
2585 overlay_node
= self
._load
_include
(include_path
)
2587 if overlay_node
is None:
2588 # cannot find include file, but we're ignoring those
2589 # errors, otherwise _load_include() itself raises
2593 # recursively process includes
2595 overlay_node
= process_base_include_cb(overlay_node
)
2596 except Exception as e
:
2597 raise ConfigError('in "{}"'.format(cur_base_path
), e
)
2599 # pop include stack now that we're done including
2600 del self
._include
_stack
[-1]
2602 # at this point, base_node is fully resolved (does not
2603 # contain any include property)
2604 if base_node
is None:
2605 base_node
= overlay_node
2607 self
._update
_node
(base_node
, overlay_node
)
2609 # finally, we update the latest base node with our last overlay
2611 if base_node
is None:
2612 # nothing was included, which is possible when we're
2613 # ignoring include errors
2614 return last_overlay_node
2616 self
._update
_node
(base_node
, last_overlay_node
)
2620 def _process_event_include(self
, event_node
):
2621 return self
._process
_node
_include
(event_node
, 'event',
2622 self
._process
_event
_include
)
2624 def _process_stream_include(self
, stream_node
):
2625 def process_children_include(stream_node
):
2626 if 'events' in stream_node
:
2627 events_node
= stream_node
['events']
2629 if not _is_assoc_array_prop(events_node
):
2630 raise ConfigError('"events" property must be an associative array')
2632 events_node_keys
= list(events_node
.keys())
2634 for key
in events_node_keys
:
2635 event_node
= events_node
[key
]
2638 events_node
[key
] = self
._process
_event
_include
(event_node
)
2639 except Exception as e
:
2640 raise ConfigError('cannot process includes of event object "{}"'.format(key
), e
)
2642 return self
._process
_node
_include
(stream_node
, 'stream',
2643 self
._process
_stream
_include
,
2644 process_children_include
)
2646 def _process_trace_include(self
, trace_node
):
2647 return self
._process
_node
_include
(trace_node
, 'trace',
2648 self
._process
_trace
_include
)
2650 def _process_clock_include(self
, clock_node
):
2651 return self
._process
_node
_include
(clock_node
, 'clock',
2652 self
._process
_clock
_include
)
2654 def _process_metadata_include(self
, metadata_node
):
2655 def process_children_include(metadata_node
):
2656 if 'trace' in metadata_node
:
2657 metadata_node
['trace'] = self
._process
_trace
_include
(metadata_node
['trace'])
2659 if 'clocks' in metadata_node
:
2660 clocks_node
= metadata_node
['clocks']
2662 if not _is_assoc_array_prop(clocks_node
):
2663 raise ConfigError('"clocks" property (metadata) must be an associative array')
2665 clocks_node_keys
= list(clocks_node
.keys())
2667 for key
in clocks_node_keys
:
2668 clock_node
= clocks_node
[key
]
2671 clocks_node
[key
] = self
._process
_clock
_include
(clock_node
)
2672 except Exception as e
:
2673 raise ConfigError('cannot process includes of clock object "{}"'.format(key
), e
)
2675 if 'streams' in metadata_node
:
2676 streams_node
= metadata_node
['streams']
2678 if not _is_assoc_array_prop(streams_node
):
2679 raise ConfigError('"streams" property (metadata) must be an associative array')
2681 streams_node_keys
= list(streams_node
.keys())
2683 for key
in streams_node_keys
:
2684 stream_node
= streams_node
[key
]
2687 streams_node
[key
] = self
._process
_stream
_include
(stream_node
)
2688 except Exception as e
:
2689 raise ConfigError('cannot process includes of stream object "{}"'.format(key
), e
)
2691 return self
._process
_node
_include
(metadata_node
, 'metadata',
2692 self
._process
_metadata
_include
,
2693 process_children_include
)
2695 def _process_root_includes(self
, root
):
2696 # The following config objects support includes:
2703 # We need to process the event includes first, then the stream
2704 # includes, then the trace includes, and finally the metadata
2707 # In each object, only one of the $include and $include-replace
2708 # special properties is allowed.
2710 # We keep a stack of absolute paths to included files to detect
2712 if 'metadata' in root
:
2713 root
['metadata'] = self
._process
_metadata
_include
(root
['metadata'])
2717 def _yaml_ordered_dump(self
, node
, **kwds
):
2718 class ODumper(yaml
.Dumper
):
2721 def dict_representer(dumper
, node
):
2722 return dumper
.represent_mapping(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2725 ODumper
.add_representer(collections
.OrderedDict
, dict_representer
)
2727 return yaml
.dump(node
, Dumper
=ODumper
, **kwds
)
2729 def _yaml_ordered_load(self
, yaml_path
):
2730 class OLoader(yaml
.Loader
):
2733 def construct_mapping(loader
, node
):
2734 loader
.flatten_mapping(node
)
2736 return collections
.OrderedDict(loader
.construct_pairs(node
))
2738 OLoader
.add_constructor(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2743 with
open(yaml_path
, 'r') as f
:
2744 node
= yaml
.load(f
, OLoader
)
2745 except (OSError, IOError) as e
:
2746 raise ConfigError('cannot open file "{}"'.format(yaml_path
))
2747 except Exception as e
:
2748 raise ConfigError('unknown error while trying to load file "{}"'.format(yaml_path
), e
)
2750 # loaded node must be an associate array
2751 if not _is_assoc_array_prop(node
):
2752 raise ConfigError('root of YAML file "{}" must be an associative array'.format(yaml_path
))
2757 self
._version
= None
2758 self
._include
_stack
= []
2760 def parse(self
, yaml_path
):
2762 self
._root
_yaml
_path
= yaml_path
2765 root
= self
._yaml
_ordered
_load
(yaml_path
)
2766 except Exception as e
:
2767 raise ConfigError('cannot parse YAML file "{}"'.format(yaml_path
), e
)
2769 if not _is_assoc_array_prop(root
):
2770 raise ConfigError('configuration must be an associative array')
2772 # get the config version
2773 self
._version
= self
._get
_version
(root
)
2781 if self
._version
>= 202:
2782 known_props
.append('options')
2784 unk_prop
= _get_first_unknown_prop(root
, known_props
)
2789 if unk_prop
== 'options':
2790 add
= ' (use version 2.2 or greater)'
2792 raise ConfigError('unknown configuration property{}: "{}"'.format(add
, unk_prop
))
2794 # process includes if supported
2795 if self
._version
>= 201:
2796 root
= self
._process
_root
_includes
(root
)
2798 # dump config if required
2799 if self
._dump
_config
:
2800 print(self
._yaml
_ordered
_dump
(root
, indent
=2,
2801 default_flow_style
=False))
2803 # get prefix and metadata
2804 prefix
= self
._get
_prefix
(root
)
2805 meta
= self
._create
_metadata
(root
)
2806 opts
= self
._get
_options
(root
)
2808 return Config(self
._version
, prefix
, meta
, opts
)
2811 def from_yaml_file(path
, include_dirs
, ignore_include_not_found
, dump_config
):
2813 parser
= _YamlConfigParser(include_dirs
, ignore_include_not_found
,
2815 cfg
= parser
.parse(path
)
2818 except Exception as e
:
2819 raise ConfigError('cannot create configuration from YAML file "{}"'.format(path
), e
)