1 # The MIT License (MIT)
3 # Copyright (c) 2015 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
):
48 self
.version
= version
49 self
.metadata
= metadata
51 def _validate_metadata(self
, meta
):
53 validator
= _MetadataTypesHistologyValidator()
54 validator
.validate(meta
)
55 validator
= _MetadataDynamicTypesValidator()
56 validator
.validate(meta
)
57 validator
= _MetadataSpecialFieldsValidator()
58 validator
.validate(meta
)
59 except Exception as e
:
60 raise ConfigError('metadata error', e
)
63 validator
= _BarectfMetadataValidator()
64 validator
.validate(meta
)
65 except Exception as e
:
66 raise ConfigError('barectf metadata error', e
)
68 def _augment_metadata_env(self
, meta
):
71 env
['domain'] = 'bare'
72 env
['tracer_name'] = 'barectf'
73 version_tuple
= barectf
.get_version_tuple()
74 env
['tracer_major'] = version_tuple
[0]
75 env
['tracer_minor'] = version_tuple
[1]
76 env
['tracer_patch'] = version_tuple
[2]
77 env
['barectf_gen_date'] = str(datetime
.datetime
.now().isoformat())
84 def version(self
, value
):
92 def metadata(self
, value
):
93 self
._validate
_metadata
(value
)
94 self
._augment
_metadata
_env
(value
)
95 self
._metadata
= value
102 def prefix(self
, value
):
103 if not is_valid_identifier(value
):
104 raise ConfigError('prefix must be a valid C identifier')
109 def _is_assoc_array_prop(node
):
110 return isinstance(node
, dict)
113 def _is_array_prop(node
):
114 return isinstance(node
, list)
117 def _is_int_prop(node
):
118 return type(node
) is int
121 def _is_str_prop(node
):
122 return type(node
) is str
125 def _is_bool_prop(node
):
126 return type(node
) is bool
129 def _is_valid_alignment(align
):
130 return ((align
& (align
- 1)) == 0) and align
> 0
133 def _byte_order_str_to_bo(bo_str
):
134 bo_str
= bo_str
.lower()
137 return metadata
.ByteOrder
.LE
139 return metadata
.ByteOrder
.BE
142 def _encoding_str_to_encoding(encoding_str
):
143 encoding_str
= encoding_str
.lower()
145 if encoding_str
== 'utf-8' or encoding_str
== 'utf8':
146 return metadata
.Encoding
.UTF8
147 elif encoding_str
== 'ascii':
148 return metadata
.Encoding
.ASCII
149 elif encoding_str
== 'none':
150 return metadata
.Encoding
.NONE
153 _re_iden
= re
.compile(r
'^[a-zA-Z][a-zA-Z0-9_]*$')
154 _ctf_keywords
= set([
173 def is_valid_identifier(iden
):
174 if not _re_iden
.match(iden
):
177 if _re_iden
in _ctf_keywords
:
183 def _get_first_unknown_prop(node
, known_props
):
184 for prop_name
in node
:
185 if prop_name
in known_props
:
191 # This validator validates the configured metadata for barectf specific
196 # * all header/contexts are at least byte-aligned
197 # * all integer and floating point number sizes to be <= 64
198 # * no inner structures, arrays, or variants
199 class _BarectfMetadataValidator
:
201 self
._type
_to
_validate
_type
_func
= {
202 metadata
.Integer
: self
._validate
_int
_type
,
203 metadata
.FloatingPoint
: self
._validate
_float
_type
,
204 metadata
.Enum
: self
._validate
_enum
_type
,
205 metadata
.String
: self
._validate
_string
_type
,
206 metadata
.Struct
: self
._validate
_struct
_type
,
207 metadata
.Array
: self
._validate
_array
_type
,
208 metadata
.Variant
: self
._validate
_variant
_type
,
211 def _validate_int_type(self
, t
, entity_root
):
213 raise ConfigError('integer type\'s size must be lesser than or equal to 64 bits')
215 def _validate_float_type(self
, t
, entity_root
):
217 raise ConfigError('floating point number type\'s size must be lesser than or equal to 64 bits')
219 def _validate_enum_type(self
, t
, entity_root
):
220 if t
.value_type
.size
> 64:
221 raise ConfigError('enumeration type\'s integer type\'s size must be lesser than or equal to 64 bits')
223 def _validate_string_type(self
, t
, entity_root
):
226 def _validate_struct_type(self
, t
, entity_root
):
228 raise ConfigError('inner structure types are not supported as of this version')
230 for field_name
, field_type
in t
.fields
.items():
231 if entity_root
and self
._cur
_entity
is _Entity
.TRACE_PACKET_HEADER
:
232 if field_name
== 'uuid':
237 self
._validate
_type
(field_type
, False)
238 except Exception as e
:
239 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
241 def _validate_array_type(self
, t
, entity_root
):
242 raise ConfigError('array types are not supported as of this version')
244 def _validate_variant_type(self
, t
, entity_root
):
245 raise ConfigError('variant types are not supported as of this version')
247 def _validate_type(self
, t
, entity_root
):
248 self
._type
_to
_validate
_type
_func
[type(t
)](t
, entity_root
)
250 def _validate_entity(self
, t
):
254 # make sure entity is byte-aligned
256 raise ConfigError('type\'s alignment must be at least byte-aligned')
258 # make sure entity is a structure
259 if type(t
) is not metadata
.Struct
:
260 raise ConfigError('expecting a structure type')
263 self
._validate
_type
(t
, True)
265 def _validate_entities_and_names(self
, meta
):
266 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
269 self
._validate
_entity
(meta
.trace
.packet_header_type
)
270 except Exception as e
:
271 raise ConfigError('invalid trace packet header type', e
)
273 for stream_name
, stream
in meta
.streams
.items():
274 if not is_valid_identifier(stream_name
):
275 raise ConfigError('stream name "{}" is not a valid C identifier'.format(stream_name
))
277 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
280 self
._validate
_entity
(stream
.packet_context_type
)
281 except Exception as e
:
282 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
284 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
287 self
._validate
_entity
(stream
.event_header_type
)
288 except Exception as e
:
289 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
291 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
294 self
._validate
_entity
(stream
.event_context_type
)
295 except Exception as e
:
296 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
299 for ev_name
, ev
in stream
.events
.items():
300 if not is_valid_identifier(ev_name
):
301 raise ConfigError('event name "{}" is not a valid C identifier'.format(ev_name
))
303 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
306 self
._validate
_entity
(ev
.context_type
)
307 except Exception as e
:
308 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
310 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
312 if ev
.payload_type
is None:
313 raise ConfigError('missing payload type in event "{}"'.format(ev_name
), e
)
316 self
._validate
_entity
(ev
.payload_type
)
317 except Exception as e
:
318 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
320 if not ev
.payload_type
.fields
:
321 raise ConfigError('empty payload type in event "{}"'.format(ev_name
), e
)
322 except Exception as e
:
323 raise ConfigError('invalid stream "{}"'.format(stream_name
), e
)
325 def validate(self
, meta
):
326 self
._validate
_entities
_and
_names
(meta
)
329 # This validator validates special fields of trace, stream, and event
330 # types. For example, if checks that the "stream_id" field exists in the
331 # trace packet header if there's more than one stream, and much more.
332 class _MetadataSpecialFieldsValidator
:
333 def _validate_trace_packet_header_type(self
, t
):
334 # needs "stream_id" field?
335 if len(self
._meta
.streams
) > 1:
338 raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is missing')
340 if type(t
) is not metadata
.Struct
:
341 raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is not a structure type')
343 if 'stream_id' not in t
.fields
:
344 raise ConfigError('need "stream_id" field in trace packet header type')
346 # validate "magic" and "stream_id" types
347 if type(t
) is not metadata
.Struct
:
350 for i
, (field_name
, field_type
) in enumerate(t
.fields
.items()):
351 if field_name
== 'magic':
352 if type(field_type
) is not metadata
.Integer
:
353 raise ConfigError('"magic" field in trace packet header type must be an integer type')
355 if field_type
.signed
or field_type
.size
!= 32:
356 raise ConfigError('"magic" field in trace packet header type must be a 32-bit unsigned integer type')
359 raise ConfigError('"magic" field must be the first trace packet header type\'s field')
360 elif field_name
== 'stream_id':
361 if type(field_type
) is not metadata
.Integer
:
362 raise ConfigError('"stream_id" field in trace packet header type must be an integer type')
364 if field_type
.signed
:
365 raise ConfigError('"stream_id" field in trace packet header type must be an unsigned integer type')
366 elif field_name
== 'uuid':
367 if self
._meta
.trace
.uuid
is None:
368 raise ConfigError('"uuid" field in trace packet header type specified, but no trace UUID provided')
370 if type(field_type
) is not metadata
.Array
:
371 raise ConfigError('"uuid" field in trace packet header type must be an array')
373 if field_type
.length
!= 16:
374 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
376 element_type
= field_type
.element_type
378 if type(element_type
) is not metadata
.Integer
:
379 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned bytes')
381 if element_type
.size
!= 8:
382 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned bytes')
384 if element_type
.signed
:
385 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned bytes')
387 if element_type
.align
!= 8:
388 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 unsigned, byte-aligned bytes')
390 def _validate_trace(self
, meta
):
391 self
._validate
_trace
_packet
_header
_type
(meta
.trace
.packet_header_type
)
393 def _validate_stream_packet_context(self
, stream
):
394 t
= stream
.packet_context_type
397 raise ConfigError('missing "packet-context-type" property in stream object')
399 if type(t
) is not metadata
.Struct
:
400 raise ConfigError('"packet-context-type": expecting a structure type')
402 # "timestamp_begin", if exists, is an unsigned integer type,
404 if 'timestamp_begin' in t
.fields
:
405 ts_begin
= t
.fields
['timestamp_begin']
407 if type(ts_begin
) is not metadata
.Integer
:
408 raise ConfigError('"timestamp_begin" field in stream packet context type must be an integer type')
411 raise ConfigError('"timestamp_begin" field in stream packet context type must be an unsigned integer type')
413 if not ts_begin
.property_mappings
:
414 raise ConfigError('"timestamp_begin" field in stream packet context type must be mapped to a clock')
416 # "timestamp_end", if exists, is an unsigned integer type,
418 if 'timestamp_end' in t
.fields
:
419 ts_end
= t
.fields
['timestamp_end']
421 if type(ts_end
) is not metadata
.Integer
:
422 raise ConfigError('"timestamp_end" field in stream packet context type must be an integer type')
425 raise ConfigError('"timestamp_end" field in stream packet context type must be an unsigned integer type')
427 if not ts_end
.property_mappings
:
428 raise ConfigError('"timestamp_end" field in stream packet context type must be mapped to a clock')
430 # "timestamp_begin" and "timestamp_end" exist together
431 if (('timestamp_begin' in t
.fields
) ^
('timestamp_end' in t
.fields
)):
432 raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be defined together in stream packet context type')
434 # "events_discarded", if exists, is an unsigned integer type
435 if 'events_discarded' in t
.fields
:
436 events_discarded
= t
.fields
['events_discarded']
438 if type(events_discarded
) is not metadata
.Integer
:
439 raise ConfigError('"events_discarded" field in stream packet context type must be an integer type')
441 if events_discarded
.signed
:
442 raise ConfigError('"events_discarded" field in stream packet context type must be an unsigned integer type')
444 # "packet_size" and "content_size" must exist
445 if 'packet_size' not in t
.fields
:
446 raise ConfigError('missing "packet_size" field in stream packet context type')
448 packet_size
= t
.fields
['packet_size']
450 # "content_size" and "content_size" must exist
451 if 'content_size' not in t
.fields
:
452 raise ConfigError('missing "content_size" field in stream packet context type')
454 content_size
= t
.fields
['content_size']
456 # "packet_size" is an unsigned integer type
457 if type(packet_size
) is not metadata
.Integer
:
458 raise ConfigError('"packet_size" field in stream packet context type must be an integer type')
460 if packet_size
.signed
:
461 raise ConfigError('"packet_size" field in stream packet context type must be an unsigned integer type')
463 # "content_size" is an unsigned integer type
464 if type(content_size
) is not metadata
.Integer
:
465 raise ConfigError('"content_size" field in stream packet context type must be an integer type')
467 if content_size
.signed
:
468 raise ConfigError('"content_size" field in stream packet context type must be an unsigned integer type')
470 # "packet_size" size should be greater than or equal to "content_size" size
471 if content_size
.size
> packet_size
.size
:
472 raise ConfigError('"content_size" field size must be lesser than or equal to "packet_size" field size')
474 def _validate_stream_event_header(self
, stream
):
475 t
= stream
.event_header_type
478 if len(stream
.events
) > 1:
481 raise ConfigError('need "id" field in stream event header type, but stream event header type is missing')
483 if type(t
) is not metadata
.Struct
:
484 raise ConfigError('need "id" field in stream event header type, but stream event header type is not a structure type')
486 if 'id' not in t
.fields
:
487 raise ConfigError('need "id" field in stream event header type')
489 # validate "id" and "timestamp" types
490 if type(t
) is not metadata
.Struct
:
493 # "timestamp", if exists, is an unsigned integer type,
495 if 'timestamp' in t
.fields
:
496 ts
= t
.fields
['timestamp']
498 if type(ts
) is not metadata
.Integer
:
499 raise ConfigError('"ts" field in stream event header type must be an integer type')
502 raise ConfigError('"ts" field in stream event header type must be an unsigned integer type')
504 if not ts
.property_mappings
:
505 raise ConfigError('"ts" field in stream event header type must be mapped to a clock')
507 # "id" is an unsigned integer type
511 if type(eid
) is not metadata
.Integer
:
512 raise ConfigError('"id" field in stream event header type must be an integer type')
515 raise ConfigError('"id" field in stream event header type must be an unsigned integer type')
517 def _validate_stream(self
, stream
):
518 self
._validate
_stream
_packet
_context
(stream
)
519 self
._validate
_stream
_event
_header
(stream
)
521 def validate(self
, meta
):
523 self
._validate
_trace
(meta
)
525 for stream
in meta
.streams
.values():
527 self
._validate
_stream
(stream
)
528 except Exception as e
:
529 raise ConfigError('invalid stream "{}"'.format(stream
.name
), e
)
532 class _MetadataDynamicTypesValidatorStackEntry
:
533 def __init__(self
, base_t
):
534 self
._base
_t
= base_t
542 def index(self
, value
):
550 def base_t(self
, value
):
554 # Entities. Order of values is important here.
556 class _Entity(enum
.IntEnum
):
557 TRACE_PACKET_HEADER
= 0
558 STREAM_PACKET_CONTEXT
= 1
559 STREAM_EVENT_HEADER
= 2
560 STREAM_EVENT_CONTEXT
= 3
565 # This validator validates dynamic metadata types, that is, it ensures
566 # variable-length array lengths and variant tags actually point to
567 # something that exists. It also checks that variable-length array
568 # lengths point to integer types and variant tags to enumeration types.
569 class _MetadataDynamicTypesValidator
:
571 self
._type
_to
_visit
_type
_func
= {
572 metadata
.Integer
: None,
573 metadata
.FloatingPoint
: None,
575 metadata
.String
: None,
576 metadata
.Struct
: self
._visit
_struct
_type
,
577 metadata
.Array
: self
._visit
_array
_type
,
578 metadata
.Variant
: self
._visit
_variant
_type
,
581 self
._cur
_trace
= None
582 self
._cur
_stream
= None
583 self
._cur
_event
= None
585 def _lookup_path_from_base(self
, path
, parts
, base
, start_index
,
586 base_is_current
, from_t
):
591 while index
< len(parts
):
595 if type(cur_t
) is metadata
.Struct
:
596 enumerated_items
= enumerate(cur_t
.fields
.items())
599 for i
, (field_name
, field_type
) in enumerated_items
:
600 if field_name
== part
:
602 found_path
.append((i
, field_type
))
605 raise ConfigError('invalid path "{}": cannot find field "{}" in structure type'.format(path
, part
))
606 elif type(cur_t
) is metadata
.Variant
:
607 enumerated_items
= enumerate(cur_t
.types
.items())
610 for i
, (type_name
, type_type
) in enumerated_items
:
611 if type_name
== part
:
613 found_path
.append((i
, type_type
))
616 raise ConfigError('invalid path "{}": cannot find type "{}" in variant type'.format(path
, part
))
618 raise ConfigError('invalid path "{}": requesting "{}" in a non-variant, non-structure type'.format(path
, part
))
623 # make sure that the pointed type is not the pointing type
625 raise ConfigError('invalid path "{}": pointing to self'.format(path
))
627 # if we're here, we found the type; however, it could be located
628 # _after_ the variant/VLA looking for it, if the pointing
629 # and pointed types are in the same entity, so compare the
630 # current stack entries indexes to our index path in that case
631 if not base_is_current
:
634 for index
, entry
in enumerate(self
._stack
):
635 if index
== len(found_path
):
636 # end of index path; valid so far
639 if found_path
[index
][0] > entry
.index
:
640 raise ConfigError('invalid path "{}": pointed type is after pointing type'.format(path
))
642 # also make sure that both pointed and pointing types share
643 # a common structure ancestor
644 for index
, entry
in enumerate(self
._stack
):
645 if index
== len(found_path
):
648 if entry
.base_t
is not found_path
[index
][1]:
649 # found common ancestor
650 if type(entry
.base_t
) is metadata
.Variant
:
651 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
655 def _lookup_path_from_top(self
, path
, parts
):
657 raise ConfigError('invalid path "{}": multipart relative path not supported'.format(path
))
660 index
= len(self
._stack
) - 1
663 # check stack entries in reversed order
664 for entry
in reversed(self
._stack
):
665 # structure base type
666 if type(entry
.base_t
) is metadata
.Struct
:
668 enumerated_items
= enumerate(entry
.base_t
.fields
.items())
670 # lookup each field, until the current visiting index is met
671 for i
, (field_name
, field_type
) in enumerated_items
:
675 if field_name
== find_name
:
679 elif type(entry
.base_t
) is metadata
.Variant
:
680 enumerated_items
= enumerate(entry
.base_t
.types
.items())
682 # lookup each type, until the current visiting index is met
683 for i
, (type_name
, type_type
) in enumerated_items
:
687 if type_name
== find_name
:
689 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
693 # nothing returned here: cannot find type
694 raise ConfigError('invalid path "{}": cannot find type in current context'.format(path
))
696 def _lookup_path(self
, path
, from_t
):
697 parts
= path
.lower().split('.')
699 base_is_current
= False
702 if parts
[0] == 'trace':
703 if parts
[1] == 'packet' and parts
[2] == 'header':
704 # make sure packet header exists
705 if self
._cur
_trace
.packet_header_type
is None:
706 raise ConfigError('invalid path "{}": no defined trace packet header type'.format(path
))
708 base
= self
._cur
_trace
.packet_header_type
710 if self
._cur
_entity
== _Entity
.TRACE_PACKET_HEADER
:
711 base_is_current
= True
713 raise ConfigError('invalid path "{}": unknown names after "trace"'.format(path
))
714 elif parts
[0] == 'stream':
715 if parts
[1] == 'packet' and parts
[2] == 'context':
716 if self
._cur
_entity
< _Entity
.STREAM_PACKET_CONTEXT
:
717 raise ConfigError('invalid path "{}": cannot access stream packet context here'.format(path
))
719 if self
._cur
_stream
.packet_context_type
is None:
720 raise ConfigError('invalid path "{}": no defined stream packet context type'.format(path
))
722 base
= self
._cur
_stream
.packet_context_type
724 if self
._cur
_entity
== _Entity
.STREAM_PACKET_CONTEXT
:
725 base_is_current
= True
726 elif parts
[1] == 'event':
727 if parts
[2] == 'header':
728 if self
._cur
_entity
< _Entity
.STREAM_EVENT_HEADER
:
729 raise ConfigError('invalid path "{}": cannot access stream event header here'.format(path
))
731 if self
._cur
_stream
.event_header_type
is None:
732 raise ConfigError('invalid path "{}": no defined stream event header type'.format(path
))
734 base
= self
._cur
_stream
.event_header_type
736 if self
._cur
_entity
== _Entity
.STREAM_EVENT_HEADER
:
737 base_is_current
= True
738 elif parts
[2] == 'context':
739 if self
._cur
_entity
< _Entity
.STREAM_EVENT_CONTEXT
:
740 raise ConfigError('invalid path "{}": cannot access stream event context here'.format(path
))
742 if self
._cur
_stream
.event_context_type
is None:
743 raise ConfigError('invalid path "{}": no defined stream event context type'.format(path
))
745 base
= self
._cur
_stream
.event_context_type
747 if self
._cur
_entity
== _Entity
.STREAM_EVENT_CONTEXT
:
748 base_is_current
= True
750 raise ConfigError('invalid path "{}": unknown names after "stream.event"'.format(path
))
752 raise ConfigError('invalid path "{}": unknown names after "stream"'.format(path
))
757 if len(parts
) >= 2 and base
is None:
758 if parts
[0] == 'event':
759 if parts
[1] == 'context':
760 if self
._cur
_entity
< _Entity
.EVENT_CONTEXT
:
761 raise ConfigError('invalid path "{}": cannot access event context here'.format(path
))
763 if self
._cur
_event
.context_type
is None:
764 raise ConfigError('invalid path "{}": no defined event context type'.format(path
))
766 base
= self
._cur
_event
.context_type
768 if self
._cur
_entity
== _Entity
.EVENT_CONTEXT
:
769 base_is_current
= True
770 elif parts
[1] == 'payload' or parts
[1] == 'fields':
771 if self
._cur
_entity
< _Entity
.EVENT_PAYLOAD
:
772 raise ConfigError('invalid path "{}": cannot access event payload here'.format(path
))
774 if self
._cur
_event
.payload_type
is None:
775 raise ConfigError('invalid path "{}": no defined event payload type'.format(path
))
777 base
= self
._cur
_event
.payload_type
779 if self
._cur
_entity
== _Entity
.EVENT_PAYLOAD
:
780 base_is_current
= True
782 raise ConfigError('invalid path "{}": unknown names after "event"'.format(path
))
788 return self
._lookup
_path
_from
_base
(path
, parts
, base
, start_index
,
789 base_is_current
, from_t
)
791 return self
._lookup
_path
_from
_top
(path
, parts
)
793 def _stack_reset(self
):
796 def _stack_push(self
, base_t
):
797 entry
= _MetadataDynamicTypesValidatorStackEntry(base_t
)
798 self
._stack
.append(entry
)
800 def _stack_pop(self
):
803 def _stack_incr_index(self
):
804 self
._stack
[-1].index
+= 1
806 def _visit_struct_type(self
, t
):
809 for field_name
, field_type
in t
.fields
.items():
811 self
._visit
_type
(field_type
)
812 except Exception as e
:
813 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
815 self
._stack
_incr
_index
()
819 def _visit_array_type(self
, t
):
820 if t
.is_variable_length
:
823 length_type
= self
._lookup
_path
(t
.length
, t
)
824 except Exception as e
:
825 raise ConfigError('invalid array type\'s length', e
)
827 # make sure length type an unsigned integer
828 if type(length_type
) is not metadata
.Integer
:
829 raise ConfigError('array type\'s length does not point to an integer type')
831 if length_type
.signed
:
832 raise ConfigError('array type\'s length does not point to an unsigned integer type')
834 self
._visit
_type
(t
.element_type
)
836 def _visit_variant_type(self
, t
):
839 tag_type
= self
._lookup
_path
(t
.tag
, t
)
840 except Exception as e
:
841 raise ConfigError('invalid variant type\'s tag', e
)
843 # make sure tag type is an enumeration
844 if type(tag_type
) is not metadata
.Enum
:
845 raise ConfigError('variant type\'s tag does not point to an enumeration type')
847 # verify that each variant type's type exists as an enumeration member
848 for tag_name
in t
.types
.keys():
849 if tag_name
not in tag_type
.members
:
850 raise ConfigError('cannot find variant type\'s type "{}" in pointed tag type'.format(tag_name
))
854 for type_name
, type_type
in t
.types
.items():
856 self
._visit
_type
(type_type
)
857 except Exception as e
:
858 raise ConfigError('in variant type\'s type "{}"'.format(type_name
), e
)
860 self
._stack
_incr
_index
()
864 def _visit_type(self
, t
):
868 if type(t
) in self
._type
_to
_visit
_type
_func
:
869 func
= self
._type
_to
_visit
_type
_func
[type(t
)]
874 def _visit_event(self
, ev
):
880 # visit event context type
882 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
885 self
._visit
_type
(ev
.context_type
)
886 except Exception as e
:
887 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
889 # visit event payload type
891 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
894 self
._visit
_type
(ev
.payload_type
)
895 except Exception as e
:
896 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
898 def _visit_stream(self
, stream
):
899 stream_name
= stream
.name
902 self
._cur
_stream
= stream
904 # reset current event
905 self
._cur
_event
= None
907 # visit stream packet context type
909 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
912 self
._visit
_type
(stream
.packet_context_type
)
913 except Exception as e
:
914 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
916 # visit stream event header type
918 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
921 self
._visit
_type
(stream
.event_header_type
)
922 except Exception as e
:
923 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
925 # visit stream event context type
927 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
930 self
._visit
_type
(stream
.event_context_type
)
931 except Exception as e
:
932 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
935 for ev
in stream
.events
.values():
937 self
._visit
_event
(ev
)
938 except Exception as e
:
939 raise ConfigError('invalid stream "{}"'.format(stream_name
))
941 def validate(self
, meta
):
943 self
._cur
_trace
= meta
.trace
945 # visit trace packet header type
947 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
950 self
._visit
_type
(meta
.trace
.packet_header_type
)
951 except Exception as e
:
952 raise ConfigError('invalid packet header type in trace', e
)
955 for stream
in meta
.streams
.values():
956 self
._visit
_stream
(stream
)
959 # Since type inheritance allows types to be only partially defined at
960 # any place in the configuration, this validator validates that actual
961 # trace, stream, and event types are all complete and valid. Therefore
962 # an invalid, but unusued type alias is accepted.
963 class _MetadataTypesHistologyValidator
:
965 self
._type
_to
_validate
_type
_histology
_func
= {
966 metadata
.Integer
: self
._validate
_integer
_histology
,
967 metadata
.FloatingPoint
: self
._validate
_float
_histology
,
968 metadata
.Enum
: self
._validate
_enum
_histology
,
969 metadata
.String
: self
._validate
_string
_histology
,
970 metadata
.Struct
: self
._validate
_struct
_histology
,
971 metadata
.Array
: self
._validate
_array
_histology
,
972 metadata
.Variant
: self
._validate
_variant
_histology
,
975 def _validate_integer_histology(self
, t
):
978 raise ConfigError('missing integer type\'s size')
980 def _validate_float_histology(self
, t
):
981 # exponent digits is set
982 if t
.exp_size
is None:
983 raise ConfigError('missing floating point number type\'s exponent size')
985 # mantissa digits is set
986 if t
.mant_size
is None:
987 raise ConfigError('missing floating point number type\'s mantissa size')
989 # exponent and mantissa sum is a multiple of 8
990 if (t
.exp_size
+ t
.mant_size
) % 8 != 0:
991 raise ConfigError('floating point number type\'s mantissa and exponent sizes sum must be a multiple of 8')
993 def _validate_enum_histology(self
, t
):
994 # integer type is set
995 if t
.value_type
is None:
996 raise ConfigError('missing enumeration type\'s value type')
998 # there's at least one member
1000 raise ConfigError('enumeration type needs at least one member')
1002 # no overlapping values and all values are valid considering
1006 if t
.value_type
.signed
:
1007 value_min
= -(1 << t
.value_type
.size
- 1)
1008 value_max
= (1 << (t
.value_type
.size
- 1)) - 1
1011 value_max
= (1 << t
.value_type
.size
) - 1
1013 for label
, value
in t
.members
.items():
1015 if value
[0] <= rg
[1] and rg
[0] <= value
[1]:
1016 raise ConfigError('enumeration type\'s member "{}" overlaps another member'.format(label
))
1018 fmt
= 'enumeration type\'s member "{}": value {} is outside the value type range [{}, {}]'
1020 if value
[0] < value_min
or value
[0] > value_max
:
1021 raise ConfigError(fmt
.format(label
, value
[0], value_min
, value_max
))
1023 if value
[1] < value_min
or value
[1] > value_max
:
1024 raise ConfigError(fmt
.format(label
, value
[1], value_min
, value_max
))
1026 ranges
.append(value
)
1028 def _validate_string_histology(self
, t
):
1032 def _validate_struct_histology(self
, t
):
1033 # all fields are valid
1034 for field_name
, field_type
in t
.fields
.items():
1036 self
._validate
_type
_histology
(field_type
)
1037 except Exception as e
:
1038 raise ConfigError('invalid structure type\'s field "{}"'.format(field_name
), e
)
1040 def _validate_array_histology(self
, t
):
1042 if t
.length
is None:
1043 raise ConfigError('missing array type\'s length')
1045 # element type is set
1046 if t
.element_type
is None:
1047 raise ConfigError('missing array type\'s element type')
1049 # element type is valid
1051 self
._validate
_type
_histology
(t
.element_type
)
1052 except Exception as e
:
1053 raise ConfigError('invalid array type\'s element type', e
)
1055 def _validate_variant_histology(self
, t
):
1058 raise ConfigError('missing variant type\'s tag')
1060 # there's at least one type
1062 raise ConfigError('variant type needs at least one type')
1064 # all types are valid
1065 for type_name
, type_t
in t
.types
.items():
1067 self
._validate
_type
_histology
(type_t
)
1068 except Exception as e
:
1069 raise ConfigError('invalid variant type\'s type "{}"'.format(type_name
), e
)
1071 def _validate_type_histology(self
, t
):
1075 self
._type
_to
_validate
_type
_histology
_func
[type(t
)](t
)
1077 def _validate_entity_type_histology(self
, t
):
1081 if type(t
) is not metadata
.Struct
:
1082 raise ConfigError('expecting a structure type')
1084 self
._validate
_type
_histology
(t
)
1086 def _validate_event_types_histology(self
, ev
):
1089 # validate event context type
1091 self
._validate
_entity
_type
_histology
(ev
.context_type
)
1092 except Exception as e
:
1093 raise ConfigError('invalid event context type for event "{}"'.format(ev_name
), e
)
1095 # validate event payload type
1096 if ev
.payload_type
is None:
1097 raise ConfigError('event payload type must exist in event "{}"'.format(ev_name
))
1099 # TODO: also check arrays, sequences, and variants
1100 if type(ev
.payload_type
) is metadata
.Struct
:
1101 if not ev
.payload_type
.fields
:
1102 raise ConfigError('event payload type must have at least one field for event "{}"'.format(ev_name
))
1105 self
._validate
_entity
_type
_histology
(ev
.payload_type
)
1106 except Exception as e
:
1107 raise ConfigError('invalid event payload type for event "{}"'.format(ev_name
), e
)
1109 def _validate_stream_types_histology(self
, stream
):
1110 stream_name
= stream
.name
1112 # validate stream packet context type
1114 self
._validate
_entity
_type
_histology
(stream
.packet_context_type
)
1115 except Exception as e
:
1116 raise ConfigError('invalid stream packet context type for stream "{}"'.format(stream_name
), e
)
1118 # validate stream event header type
1120 self
._validate
_entity
_type
_histology
(stream
.event_header_type
)
1121 except Exception as e
:
1122 raise ConfigError('invalid stream event header type for stream "{}"'.format(stream_name
), e
)
1124 # validate stream event context type
1126 self
._validate
_entity
_type
_histology
(stream
.event_context_type
)
1127 except Exception as e
:
1128 raise ConfigError('invalid stream event context type for stream "{}"'.format(stream_name
), e
)
1131 for ev
in stream
.events
.values():
1133 self
._validate
_event
_types
_histology
(ev
)
1134 except Exception as e
:
1135 raise ConfigError('invalid event in stream "{}"'.format(stream_name
), e
)
1137 def validate(self
, meta
):
1138 # validate trace packet header type
1140 self
._validate
_entity
_type
_histology
(meta
.trace
.packet_header_type
)
1141 except Exception as e
:
1142 raise ConfigError('invalid trace packet header type', e
)
1145 for stream
in meta
.streams
.values():
1146 self
._validate
_stream
_types
_histology
(stream
)
1149 class _YamlConfigParser
:
1150 def __init__(self
, include_dirs
, ignore_include_not_found
, dump_config
):
1151 self
._class
_name
_to
_create
_type
_func
= {
1152 'int': self
._create
_integer
,
1153 'integer': self
._create
_integer
,
1154 'flt': self
._create
_float
,
1155 'float': self
._create
_float
,
1156 'floating-point': self
._create
_float
,
1157 'enum': self
._create
_enum
,
1158 'enumeration': self
._create
_enum
,
1159 'str': self
._create
_string
,
1160 'string': self
._create
_string
,
1161 'struct': self
._create
_struct
,
1162 'structure': self
._create
_struct
,
1163 'array': self
._create
_array
,
1164 'var': self
._create
_variant
,
1165 'variant': self
._create
_variant
,
1167 self
._type
_to
_create
_type
_func
= {
1168 metadata
.Integer
: self
._create
_integer
,
1169 metadata
.FloatingPoint
: self
._create
_float
,
1170 metadata
.Enum
: self
._create
_enum
,
1171 metadata
.String
: self
._create
_string
,
1172 metadata
.Struct
: self
._create
_struct
,
1173 metadata
.Array
: self
._create
_array
,
1174 metadata
.Variant
: self
._create
_variant
,
1176 self
._include
_dirs
= include_dirs
1177 self
._ignore
_include
_not
_found
= ignore_include_not_found
1178 self
._dump
_config
= dump_config
1180 def _set_byte_order(self
, metadata_node
):
1181 if 'trace' not in metadata_node
:
1182 raise ConfigError('missing "trace" property (metadata)')
1184 trace_node
= metadata_node
['trace']
1186 if not _is_assoc_array_prop(trace_node
):
1187 raise ConfigError('"trace" property (metadata) must be an associative array')
1189 if 'byte-order' not in trace_node
:
1190 raise ConfigError('missing "byte-order" property (trace)')
1192 bo_node
= trace_node
['byte-order']
1194 if not _is_str_prop(bo_node
):
1195 raise ConfigError('"byte-order" property of trace object must be a string ("le" or "be")')
1197 self
._bo
= _byte_order_str_to_bo(bo_node
)
1199 if self
._bo
is None:
1200 raise ConfigError('invalid "byte-order" property (trace): must be "le" or "be"')
1202 def _lookup_type_alias(self
, name
):
1203 if name
in self
._tas
:
1204 return copy
.deepcopy(self
._tas
[name
])
1206 def _set_int_clock_prop_mapping(self
, int_obj
, prop_mapping_node
):
1207 unk_prop
= _get_first_unknown_prop(prop_mapping_node
, ['type', 'name', 'property'])
1210 raise ConfigError('unknown property in integer type object\'s clock property mapping: "{}"'.format(unk_prop
))
1212 if 'name' not in prop_mapping_node
:
1213 raise ConfigError('missing "name" property in integer type object\'s clock property mapping')
1215 if 'property' not in prop_mapping_node
:
1216 raise ConfigError('missing "property" property in integer type object\'s clock property mapping')
1218 clock_name
= prop_mapping_node
['name']
1219 prop
= prop_mapping_node
['property']
1221 if not _is_str_prop(clock_name
):
1222 raise ConfigError('"name" property of integer type object\'s clock property mapping must be a string')
1224 if not _is_str_prop(prop
):
1225 raise ConfigError('"property" property of integer type object\'s clock property mapping must be a string')
1227 if clock_name
not in self
._clocks
:
1228 raise ConfigError('invalid clock name "{}" in integer type object\'s clock property mapping'.format(clock_name
))
1231 raise ConfigError('invalid "property" property in integer type object\'s clock property mapping: "{}"'.format(prop
))
1233 mapped_clock
= self
._clocks
[clock_name
]
1234 int_obj
.property_mappings
.append(metadata
.PropertyMapping(mapped_clock
, prop
))
1236 def _get_first_unknown_type_prop(self
, type_node
, known_props
):
1237 kp
= known_props
+ ['inherit', 'class']
1239 if self
._version
>= 201:
1240 kp
.append('$inherit')
1242 return _get_first_unknown_prop(type_node
, kp
)
1244 def _create_integer(self
, obj
, node
):
1246 # create integer object
1247 obj
= metadata
.Integer()
1249 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1256 'property-mappings',
1260 raise ConfigError('unknown integer type object property: "{}"'.format(unk_prop
))
1266 if not _is_int_prop(size
):
1267 raise ConfigError('"size" property of integer type object must be an integer')
1270 raise ConfigError('invalid integer size: {}'.format(size
))
1276 align
= node
['align']
1278 if not _is_int_prop(align
):
1279 raise ConfigError('"align" property of integer type object must be an integer')
1281 if not _is_valid_alignment(align
):
1282 raise ConfigError('invalid alignment: {}'.format(align
))
1287 if 'signed' in node
:
1288 signed
= node
['signed']
1290 if not _is_bool_prop(signed
):
1291 raise ConfigError('"signed" property of integer type object must be a boolean')
1296 if 'byte-order' in node
:
1297 byte_order
= node
['byte-order']
1299 if not _is_str_prop(byte_order
):
1300 raise ConfigError('"byte-order" property of integer type object must be a string ("le" or "be")')
1302 byte_order
= _byte_order_str_to_bo(byte_order
)
1304 if byte_order
is None:
1305 raise ConfigError('invalid "byte-order" property in integer type object')
1307 byte_order
= self
._bo
1309 obj
.byte_order
= byte_order
1315 if not _is_str_prop(base
):
1316 raise ConfigError('"base" property of integer type object must be a string ("bin", "oct", "dec", or "hex")')
1327 raise ConfigError('unknown "base" property value: "{}" ("bin", "oct", "dec", and "hex" are accepted)'.format(base
))
1332 if 'encoding' in node
:
1333 encoding
= node
['encoding']
1335 if not _is_str_prop(encoding
):
1336 raise ConfigError('"encoding" property of integer type object must be a string ("none", "ascii", or "utf-8")')
1338 encoding
= _encoding_str_to_encoding(encoding
)
1340 if encoding
is None:
1341 raise ConfigError('invalid "encoding" property in integer type object')
1343 obj
.encoding
= encoding
1346 if 'property-mappings' in node
:
1347 prop_mappings
= node
['property-mappings']
1349 if not _is_array_prop(prop_mappings
):
1350 raise ConfigError('"property-mappings" property of integer type object must be an array')
1352 if len(prop_mappings
) > 1:
1353 raise ConfigError('length of "property-mappings" array in integer type object must be 1')
1355 del obj
.property_mappings
[:]
1357 for index
, prop_mapping
in enumerate(prop_mappings
):
1358 if not _is_assoc_array_prop(prop_mapping
):
1359 raise ConfigError('elements of "property-mappings" property of integer type object must be associative arrays')
1361 if 'type' not in prop_mapping
:
1362 raise ConfigError('missing "type" property in integer type object\'s "property-mappings" array\'s element #{}'.format(index
))
1364 prop_type
= prop_mapping
['type']
1366 if not _is_str_prop(prop_type
):
1367 raise ConfigError('"type" property of integer type object\'s "property-mappings" array\'s element #{} must be a string'.format(index
))
1369 if prop_type
== 'clock':
1370 self
._set
_int
_clock
_prop
_mapping
(obj
, prop_mapping
)
1372 raise ConfigError('unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type
, index
))
1376 def _create_float(self
, obj
, node
):
1378 # create floating point number object
1379 obj
= metadata
.FloatingPoint()
1381 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1388 raise ConfigError('unknown floating point number type object property: "{}"'.format(unk_prop
))
1394 if not _is_assoc_array_prop(size
):
1395 raise ConfigError('"size" property of floating point number type object must be an associative array')
1397 unk_prop
= _get_first_unknown_prop(size
, ['exp', 'mant'])
1400 raise ConfigError('unknown floating point number type object\'s "size" property: "{}"'.format(unk_prop
))
1405 if not _is_int_prop(exp
):
1406 raise ConfigError('"exp" property of floating point number type object\'s "size" property must be an integer')
1409 raise ConfigError('invalid floating point number exponent size: {}')
1416 if not _is_int_prop(mant
):
1417 raise ConfigError('"mant" property of floating point number type object\'s "size" property must be an integer')
1420 raise ConfigError('invalid floating point number mantissa size: {}')
1422 obj
.mant_size
= mant
1426 align
= node
['align']
1428 if not _is_int_prop(align
):
1429 raise ConfigError('"align" property of floating point number type object must be an integer')
1431 if not _is_valid_alignment(align
):
1432 raise ConfigError('invalid alignment: {}'.format(align
))
1437 if 'byte-order' in node
:
1438 byte_order
= node
['byte-order']
1440 if not _is_str_prop(byte_order
):
1441 raise ConfigError('"byte-order" property of floating point number type object must be a string ("le" or "be")')
1443 byte_order
= _byte_order_str_to_bo(byte_order
)
1445 if byte_order
is None:
1446 raise ConfigError('invalid "byte-order" property in floating point number type object')
1448 byte_order
= self
._bo
1450 obj
.byte_order
= byte_order
1454 def _create_enum(self
, obj
, node
):
1456 # create enumeration object
1457 obj
= metadata
.Enum()
1459 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1465 raise ConfigError('unknown enumeration type object property: "{}"'.format(unk_prop
))
1468 if 'value-type' in node
:
1470 obj
.value_type
= self
._create
_type
(node
['value-type'])
1471 except Exception as e
:
1472 raise ConfigError('cannot create enumeration type\'s integer type', e
)
1475 if 'members' in node
:
1476 members_node
= node
['members']
1478 if not _is_array_prop(members_node
):
1479 raise ConfigError('"members" property of enumeration type object must be an array')
1483 for index
, m_node
in enumerate(members_node
):
1484 if not _is_str_prop(m_node
) and not _is_assoc_array_prop(m_node
):
1485 raise ConfigError('invalid enumeration member #{}: expecting a string or an associative array'.format(index
))
1487 if _is_str_prop(m_node
):
1492 unk_prop
= _get_first_unknown_prop(m_node
, [
1498 raise ConfigError('unknown enumeration type member object property: "{}"'.format(unk_prop
))
1500 if 'label' not in m_node
:
1501 raise ConfigError('missing "label" property in enumeration member #{}'.format(index
))
1503 label
= m_node
['label']
1505 if not _is_str_prop(label
):
1506 raise ConfigError('"label" property of enumeration member #{} must be a string'.format(index
))
1508 if 'value' not in m_node
:
1509 raise ConfigError('missing "value" property in enumeration member ("{}")'.format(label
))
1511 value
= m_node
['value']
1513 if not _is_int_prop(value
) and not _is_array_prop(value
):
1514 raise ConfigError('invalid enumeration member ("{}"): expecting an integer or an array'.format(label
))
1516 if _is_int_prop(value
):
1518 value
= (value
, value
)
1521 raise ConfigError('invalid enumeration member ("{}"): range must have exactly two items'.format(label
))
1527 raise ConfigError('invalid enumeration member ("{}"): invalid range ({} > {})'.format(label
, mn
, mx
))
1532 obj
.members
[label
] = value
1536 def _create_string(self
, obj
, node
):
1538 # create string object
1539 obj
= metadata
.String()
1541 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1546 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1549 if 'encoding' in node
:
1550 encoding
= node
['encoding']
1552 if not _is_str_prop(encoding
):
1553 raise ConfigError('"encoding" property of string type object must be a string ("none", "ascii", or "utf-8")')
1555 encoding
= _encoding_str_to_encoding(encoding
)
1557 if encoding
is None:
1558 raise ConfigError('invalid "encoding" property in string type object')
1560 obj
.encoding
= encoding
1564 def _create_struct(self
, obj
, node
):
1566 # create structure object
1567 obj
= metadata
.Struct()
1569 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1575 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1578 if 'min-align' in node
:
1579 min_align
= node
['min-align']
1581 if not _is_int_prop(min_align
):
1582 raise ConfigError('"min-align" property of structure type object must be an integer')
1584 if not _is_valid_alignment(min_align
):
1585 raise ConfigError('invalid minimum alignment: {}'.format(min_align
))
1587 obj
.min_align
= min_align
1590 if 'fields' in node
:
1591 fields
= node
['fields']
1593 if not _is_assoc_array_prop(fields
):
1594 raise ConfigError('"fields" property of structure type object must be an associative array')
1596 for field_name
, field_node
in fields
.items():
1597 if not is_valid_identifier(field_name
):
1598 raise ConfigError('"{}" is not a valid field name for structure type'.format(field_name
))
1601 obj
.fields
[field_name
] = self
._create
_type
(field_node
)
1602 except Exception as e
:
1603 raise ConfigError('cannot create structure type\'s field "{}"'.format(field_name
), e
)
1607 def _create_array(self
, obj
, node
):
1609 # create array object
1610 obj
= metadata
.Array()
1612 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1618 raise ConfigError('unknown array type object property: "{}"'.format(unk_prop
))
1621 if 'length' in node
:
1622 length
= node
['length']
1624 if not _is_int_prop(length
) and not _is_str_prop(length
):
1625 raise ConfigError('"length" property of array type object must be an integer or a string')
1627 if type(length
) is int and length
< 0:
1628 raise ConfigError('invalid static array length: {}'.format(length
))
1633 if 'element-type' in node
:
1635 obj
.element_type
= self
._create
_type
(node
['element-type'])
1636 except Exception as e
:
1637 raise ConfigError('cannot create array type\'s element type', e
)
1641 def _create_variant(self
, obj
, node
):
1643 # create variant object
1644 obj
= metadata
.Variant()
1646 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1652 raise ConfigError('unknown variant type object property: "{}"'.format(unk_prop
))
1658 if not _is_str_prop(tag
):
1659 raise ConfigError('"tag" property of variant type object must be a string')
1661 # do not validate variant tag for the moment; will be done in a
1667 types
= node
['types']
1669 if not _is_assoc_array_prop(types
):
1670 raise ConfigError('"types" property of variant type object must be an associative array')
1672 # do not validate type names for the moment; will be done in a
1674 for type_name
, type_node
in types
.items():
1675 if not is_valid_identifier(type_name
):
1676 raise ConfigError('"{}" is not a valid type name for variant type'.format(type_name
))
1679 obj
.types
[type_name
] = self
._create
_type
(type_node
)
1680 except Exception as e
:
1681 raise ConfigError('cannot create variant type\'s type "{}"'.format(type_name
), e
)
1685 def _create_type(self
, type_node
):
1686 if type(type_node
) is str:
1687 t
= self
._lookup
_type
_alias
(type_node
)
1690 raise ConfigError('unknown type alias "{}"'.format(type_node
))
1694 if not _is_assoc_array_prop(type_node
):
1695 raise ConfigError('type objects must be associative arrays or strings (type alias name)')
1702 if self
._version
>= 200:
1703 if 'inherit' in type_node
:
1704 inherit_prop
= 'inherit'
1705 inherit_node
= type_node
[inherit_prop
]
1707 if self
._version
>= 201:
1708 if '$inherit' in type_node
:
1709 if inherit_node
is not None:
1710 raise ConfigError('cannot specify both "inherit" and "$inherit" properties of type object: prefer "$inherit"')
1712 inherit_prop
= '$inherit'
1713 inherit_node
= type_node
[inherit_prop
]
1715 if inherit_node
is not None and 'class' in type_node
:
1716 raise ConfigError('cannot specify both "{}" and "class" properties in type object'.format(inherit_prop
))
1718 if inherit_node
is not None:
1719 if not _is_str_prop(inherit_node
):
1720 raise ConfigError('"{}" property of type object must be a string'.format(inherit_prop
))
1722 base
= self
._lookup
_type
_alias
(inherit_node
)
1725 raise ConfigError('cannot inherit from type alias "{}": type alias does not exist at this point'.format(inherit_node
))
1727 func
= self
._type
_to
_create
_type
_func
[type(base
)]
1729 if 'class' not in type_node
:
1730 raise ConfigError('type objects which do not inherit must have a "class" property')
1732 class_name
= type_node
['class']
1734 if type(class_name
) is not str:
1735 raise ConfigError('type objects\' "class" property must be a string')
1737 if class_name
not in self
._class
_name
_to
_create
_type
_func
:
1738 raise ConfigError('unknown type class "{}"'.format(class_name
))
1741 func
= self
._class
_name
_to
_create
_type
_func
[class_name
]
1743 return func(base
, type_node
)
1745 def _register_type_aliases(self
, metadata_node
):
1748 if 'type-aliases' not in metadata_node
:
1751 ta_node
= metadata_node
['type-aliases']
1753 if not _is_assoc_array_prop(ta_node
):
1754 raise ConfigError('"type-aliases" property (metadata) must be an associative array')
1756 for ta_name
, ta_type
in ta_node
.items():
1757 if ta_name
in self
._tas
:
1758 raise ConfigError('duplicate type alias "{}"'.format(ta_name
))
1761 t
= self
._create
_type
(ta_type
)
1762 except Exception as e
:
1763 raise ConfigError('cannot create type alias "{}"'.format(ta_name
), e
)
1765 self
._tas
[ta_name
] = t
1767 def _create_clock(self
, node
):
1768 # create clock object
1769 clock
= metadata
.Clock()
1771 if not _is_assoc_array_prop(node
):
1772 raise ConfigError('clock objects must be associative arrays')
1784 if self
._version
>= 201:
1785 known_props
.append('$return-ctype')
1787 unk_prop
= _get_first_unknown_prop(node
, known_props
)
1790 raise ConfigError('unknown clock object property: "{}"'.format(unk_prop
))
1794 uuidp
= node
['uuid']
1796 if not _is_str_prop(uuidp
):
1797 raise ConfigError('"uuid" property of clock object must be a string')
1800 uuidp
= uuid
.UUID(uuidp
)
1802 raise ConfigError('malformed UUID (clock object): "{}"'.format(uuidp
))
1807 if 'description' in node
:
1808 desc
= node
['description']
1810 if not _is_str_prop(desc
):
1811 raise ConfigError('"description" property of clock object must be a string')
1813 clock
.description
= desc
1819 if not _is_int_prop(freq
):
1820 raise ConfigError('"freq" property of clock object must be an integer')
1823 raise ConfigError('invalid clock frequency: {}'.format(freq
))
1828 if 'error-cycles' in node
:
1829 error_cycles
= node
['error-cycles']
1831 if not _is_int_prop(error_cycles
):
1832 raise ConfigError('"error-cycles" property of clock object must be an integer')
1834 if error_cycles
< 0:
1835 raise ConfigError('invalid clock error cycles: {}'.format(error_cycles
))
1837 clock
.error_cycles
= error_cycles
1840 if 'offset' in node
:
1841 offset
= node
['offset']
1843 if not _is_assoc_array_prop(offset
):
1844 raise ConfigError('"offset" property of clock object must be an associative array')
1846 unk_prop
= _get_first_unknown_prop(offset
, ['cycles', 'seconds'])
1849 raise ConfigError('unknown clock object\'s offset property: "{}"'.format(unk_prop
))
1852 if 'cycles' in offset
:
1853 offset_cycles
= offset
['cycles']
1855 if not _is_int_prop(offset_cycles
):
1856 raise ConfigError('"cycles" property of clock object\'s offset property must be an integer')
1858 if offset_cycles
< 0:
1859 raise ConfigError('invalid clock offset cycles: {}'.format(offset_cycles
))
1861 clock
.offset_cycles
= offset_cycles
1864 if 'seconds' in offset
:
1865 offset_seconds
= offset
['seconds']
1867 if not _is_int_prop(offset_seconds
):
1868 raise ConfigError('"seconds" property of clock object\'s offset property must be an integer')
1870 if offset_seconds
< 0:
1871 raise ConfigError('invalid clock offset seconds: {}'.format(offset_seconds
))
1873 clock
.offset_seconds
= offset_seconds
1876 if 'absolute' in node
:
1877 absolute
= node
['absolute']
1879 if not _is_bool_prop(absolute
):
1880 raise ConfigError('"absolute" property of clock object must be a boolean')
1882 clock
.absolute
= absolute
1885 # v2.0: "return-ctype"
1886 # v2.1+: "$return-ctype"
1887 return_ctype_node
= None
1889 if self
._version
>= 200:
1890 if 'return-ctype' in node
:
1891 return_ctype_prop
= 'return-ctype'
1892 return_ctype_node
= node
[return_ctype_prop
]
1894 if self
._version
>= 201:
1895 if '$return-ctype' in node
:
1896 if return_ctype_node
is not None:
1897 raise ConfigError('cannot specify both "return-ctype" and "$return-ctype" properties of clock object: prefer "$return-ctype"')
1899 return_ctype_prop
= '$return-ctype'
1900 return_ctype_node
= node
[return_ctype_prop
]
1902 if return_ctype_node
is not None:
1903 if not _is_str_prop(return_ctype_node
):
1904 raise ConfigError('"{}" property of clock object must be a string'.format(return_ctype_prop
))
1906 clock
.return_ctype
= return_ctype_node
1910 def _register_clocks(self
, metadata_node
):
1911 self
._clocks
= collections
.OrderedDict()
1913 if 'clocks' not in metadata_node
:
1916 clocks_node
= metadata_node
['clocks']
1918 if not _is_assoc_array_prop(clocks_node
):
1919 raise ConfigError('"clocks" property (metadata) must be an associative array')
1921 for clock_name
, clock_node
in clocks_node
.items():
1922 if not is_valid_identifier(clock_name
):
1923 raise ConfigError('invalid clock name: "{}"'.format(clock_name
))
1925 if clock_name
in self
._clocks
:
1926 raise ConfigError('duplicate clock "{}"'.format(clock_name
))
1929 clock
= self
._create
_clock
(clock_node
)
1930 except Exception as e
:
1931 raise ConfigError('cannot create clock "{}"'.format(clock_name
), e
)
1933 clock
.name
= clock_name
1934 self
._clocks
[clock_name
] = clock
1936 def _create_env(self
, metadata_node
):
1937 env
= collections
.OrderedDict()
1939 if 'env' not in metadata_node
:
1942 env_node
= metadata_node
['env']
1944 if not _is_assoc_array_prop(env_node
):
1945 raise ConfigError('"env" property (metadata) must be an associative array')
1947 for env_name
, env_value
in env_node
.items():
1949 raise ConfigError('duplicate environment variable "{}"'.format(env_name
))
1951 if not is_valid_identifier(env_name
):
1952 raise ConfigError('invalid environment variable name: "{}"'.format(env_name
))
1954 if not _is_int_prop(env_value
) and not _is_str_prop(env_value
):
1955 raise ConfigError('invalid environment variable value ("{}"): expecting integer or string'.format(env_name
))
1957 env
[env_name
] = env_value
1961 def _register_log_levels(self
, metadata_node
):
1962 self
._log
_levels
= dict()
1965 # v2.0: "log-levels"
1966 # v2.1+: "$log-levels"
1967 log_levels_node
= None
1969 if self
._version
>= 200:
1970 if 'log-levels' in metadata_node
:
1971 log_levels_prop
= 'log-levels'
1972 log_levels_node
= metadata_node
[log_levels_prop
]
1974 if self
._version
>= 201:
1975 if '$log-levels' in metadata_node
:
1976 if log_levels_node
is not None:
1977 raise ConfigError('cannot specify both "log-levels" and "$log-levels" properties of metadata object: prefer "$log-levels"')
1979 log_levels_prop
= '$log-levels'
1980 log_levels_node
= metadata_node
[log_levels_prop
]
1982 if log_levels_node
is None:
1985 if not _is_assoc_array_prop(log_levels_node
):
1986 raise ConfigError('"{}" property (metadata) must be an associative array'.format(log_levels_prop
))
1988 for ll_name
, ll_value
in log_levels_node
.items():
1989 if ll_name
in self
._log
_levels
:
1990 raise ConfigError('duplicate log level entry "{}"'.format(ll_name
))
1992 if not _is_int_prop(ll_value
):
1993 raise ConfigError('invalid log level entry ("{}"): expecting an integer'.format(ll_name
))
1996 raise ConfigError('invalid log level entry ("{}"): log level value must be positive'.format(ll_name
))
1998 self
._log
_levels
[ll_name
] = ll_value
2000 def _create_trace(self
, metadata_node
):
2001 # create trace object
2002 trace
= metadata
.Trace()
2004 if 'trace' not in metadata_node
:
2005 raise ConfigError('missing "trace" property (metadata)')
2007 trace_node
= metadata_node
['trace']
2009 if not _is_assoc_array_prop(trace_node
):
2010 raise ConfigError('"trace" property (metadata) must be an associative array')
2012 unk_prop
= _get_first_unknown_prop(trace_node
, [
2015 'packet-header-type',
2019 raise ConfigError('unknown trace object property: "{}"'.format(unk_prop
))
2021 # set byte order (already parsed)
2022 trace
.byte_order
= self
._bo
2025 if 'uuid' in trace_node
:
2026 uuidp
= trace_node
['uuid']
2028 if not _is_str_prop(uuidp
):
2029 raise ConfigError('"uuid" property of trace object must be a string')
2032 uuidp
= uuid
.uuid1()
2035 uuidp
= uuid
.UUID(uuidp
)
2037 raise ConfigError('malformed UUID (trace object): "{}"'.format(uuidp
))
2041 # packet header type
2042 if 'packet-header-type' in trace_node
:
2044 ph_type
= self
._create
_type
(trace_node
['packet-header-type'])
2045 except Exception as e
:
2046 raise ConfigError('cannot create packet header type (trace)', e
)
2048 trace
.packet_header_type
= ph_type
2052 def _lookup_log_level(self
, ll
):
2053 if _is_int_prop(ll
):
2055 elif _is_str_prop(ll
) and ll
in self
._log
_levels
:
2056 return self
._log
_levels
[ll
]
2058 def _create_event(self
, event_node
):
2059 event
= metadata
.Event()
2061 if not _is_assoc_array_prop(event_node
):
2062 raise ConfigError('event objects must be associative arrays')
2064 unk_prop
= _get_first_unknown_prop(event_node
, [
2071 raise ConfigError('unknown event object property: "{}"'.format(unk_prop
))
2073 if 'log-level' in event_node
:
2074 ll_node
= event_node
['log-level']
2076 if _is_str_prop(ll_node
):
2077 ll
= self
._lookup
_log
_level
(event_node
['log-level'])
2080 raise ConfigError('cannot find log level "{}"'.format(ll_node
))
2081 elif _is_int_prop(ll_node
):
2083 raise ConfigError('invalid log level value {}: value must be positive'.format(ll_node
))
2087 raise ConfigError('"log-level" property must be either a string or an integer')
2089 event
.log_level
= ll
2091 if 'context-type' in event_node
:
2093 t
= self
._create
_type
(event_node
['context-type'])
2094 except Exception as e
:
2095 raise ConfigError('cannot create event\'s context type object', e
)
2097 event
.context_type
= t
2099 if 'payload-type' not in event_node
:
2100 raise ConfigError('missing "payload-type" property in event object')
2103 t
= self
._create
_type
(event_node
['payload-type'])
2104 except Exception as e
:
2105 raise ConfigError('cannot create event\'s payload type object', e
)
2107 event
.payload_type
= t
2111 def _create_stream(self
, stream_node
):
2112 stream
= metadata
.Stream()
2114 if not _is_assoc_array_prop(stream_node
):
2115 raise ConfigError('stream objects must be associative arrays')
2117 unk_prop
= _get_first_unknown_prop(stream_node
, [
2118 'packet-context-type',
2119 'event-header-type',
2120 'event-context-type',
2125 raise ConfigError('unknown stream object property: "{}"'.format(unk_prop
))
2127 if 'packet-context-type' in stream_node
:
2129 t
= self
._create
_type
(stream_node
['packet-context-type'])
2130 except Exception as e
:
2131 raise ConfigError('cannot create stream\'s packet context type object', e
)
2133 stream
.packet_context_type
= t
2135 if 'event-header-type' in stream_node
:
2137 t
= self
._create
_type
(stream_node
['event-header-type'])
2138 except Exception as e
:
2139 raise ConfigError('cannot create stream\'s event header type object', e
)
2141 stream
.event_header_type
= t
2143 if 'event-context-type' in stream_node
:
2145 t
= self
._create
_type
(stream_node
['event-context-type'])
2146 except Exception as e
:
2147 raise ConfigError('cannot create stream\'s event context type object', e
)
2149 stream
.event_context_type
= t
2151 if 'events' not in stream_node
:
2152 raise ConfigError('missing "events" property in stream object')
2154 events
= stream_node
['events']
2156 if not _is_assoc_array_prop(events
):
2157 raise ConfigError('"events" property of stream object must be an associative array')
2160 raise ConfigError('at least one event is needed within a stream object')
2164 for ev_name
, ev_node
in events
.items():
2166 ev
= self
._create
_event
(ev_node
)
2167 except Exception as e
:
2168 raise ConfigError('cannot create event "{}"'.format(ev_name
), e
)
2172 stream
.events
[ev_name
] = ev
2177 def _create_streams(self
, metadata_node
):
2178 streams
= collections
.OrderedDict()
2180 if 'streams' not in metadata_node
:
2181 raise ConfigError('missing "streams" property (metadata)')
2183 streams_node
= metadata_node
['streams']
2185 if not _is_assoc_array_prop(streams_node
):
2186 raise ConfigError('"streams" property (metadata) must be an associative array')
2188 if not streams_node
:
2189 raise ConfigError('at least one stream is needed (metadata)')
2193 for stream_name
, stream_node
in streams_node
.items():
2195 stream
= self
._create
_stream
(stream_node
)
2196 except Exception as e
:
2197 raise ConfigError('cannot create stream "{}"'.format(stream_name
), e
)
2200 stream
.name
= str(stream_name
)
2201 streams
[stream_name
] = stream
2206 def _create_metadata(self
, root
):
2207 meta
= metadata
.Metadata()
2209 if 'metadata' not in root
:
2210 raise ConfigError('missing "metadata" property (configuration)')
2212 metadata_node
= root
['metadata']
2214 if not _is_assoc_array_prop(metadata_node
):
2215 raise ConfigError('"metadata" property (configuration) must be an associative array')
2226 if self
._version
>= 201:
2227 known_props
.append('$log-levels')
2229 unk_prop
= _get_first_unknown_prop(metadata_node
, known_props
)
2234 if unk_prop
== '$include':
2235 add
= ' (use version 2.1 or greater)'
2237 raise ConfigError('unknown metadata property{}: "{}"'.format(add
, unk_prop
))
2239 self
._set
_byte
_order
(metadata_node
)
2240 self
._register
_clocks
(metadata_node
)
2241 meta
.clocks
= self
._clocks
2242 self
._register
_type
_aliases
(metadata_node
)
2243 meta
.env
= self
._create
_env
(metadata_node
)
2244 meta
.trace
= self
._create
_trace
(metadata_node
)
2245 self
._register
_log
_levels
(metadata_node
)
2246 meta
.streams
= self
._create
_streams
(metadata_node
)
2250 def _get_version(self
, root
):
2251 if 'version' not in root
:
2252 raise ConfigError('missing "version" property (configuration)')
2254 version_node
= root
['version']
2256 if not _is_str_prop(version_node
):
2257 raise ConfigError('"version" property (configuration) must be a string')
2259 version_node
= version_node
.strip()
2261 if version_node
not in ['2.0', '2.1']:
2262 raise ConfigError('unsupported version ({}): versions 2.0 and 2.1 are supported'.format(version_node
))
2264 # convert version string to comparable version integer
2265 parts
= version_node
.split('.')
2266 version
= int(parts
[0]) * 100 + int(parts
[1])
2270 def _get_prefix(self
, root
):
2271 if 'prefix' not in root
:
2274 prefix_node
= root
['prefix']
2276 if not _is_str_prop(prefix_node
):
2277 raise ConfigError('"prefix" property (configuration) must be a string')
2279 if not is_valid_identifier(prefix_node
):
2280 raise ConfigError('"prefix" property (configuration) must be a valid C identifier')
2284 def _get_last_include_file(self
):
2285 if self
._include
_stack
:
2286 return self
._include
_stack
[-1]
2288 return self
._root
_yaml
_path
2290 def _load_include(self
, yaml_path
):
2291 for inc_dir
in self
._include
_dirs
:
2292 # current include dir + file name path
2293 # note: os.path.join() only takes the last arg if it's absolute
2294 inc_path
= os
.path
.join(inc_dir
, yaml_path
)
2296 # real path (symbolic links resolved)
2297 real_path
= os
.path
.realpath(inc_path
)
2299 # normalized path (weird stuff removed!)
2300 norm_path
= os
.path
.normpath(real_path
)
2302 if not os
.path
.isfile(norm_path
):
2303 # file does not exist: skip
2306 if norm_path
in self
._include
_stack
:
2307 base_path
= self
._get
_last
_include
_file
()
2308 raise ConfigError('in "{}": cannot recursively include file "{}"'.format(base_path
, norm_path
))
2310 self
._include
_stack
.append(norm_path
)
2313 return self
._yaml
_ordered
_load
(norm_path
)
2315 if not self
._ignore
_include
_not
_found
:
2316 base_path
= self
._get
_last
_include
_file
()
2317 raise ConfigError('in "{}": cannot include file "{}": file not found in include directories'.format(base_path
, yaml_path
))
2321 def _get_include_paths(self
, include_node
):
2322 if _is_str_prop(include_node
):
2323 return [include_node
]
2324 elif _is_array_prop(include_node
):
2325 for include_path
in include_node
:
2326 if not _is_str_prop(include_path
):
2327 raise ConfigError('invalid include property: expecting array of strings')
2331 raise ConfigError('invalid include property: expecting string or array of strings')
2333 def _update_node(self
, base_node
, overlay_node
):
2334 for olay_key
, olay_value
in overlay_node
.items():
2335 if olay_key
in base_node
:
2336 base_value
= base_node
[olay_key
]
2338 if _is_assoc_array_prop(olay_value
) and _is_assoc_array_prop(base_value
):
2339 # merge dictionaries
2340 self
._update
_node
(base_value
, olay_value
)
2341 elif _is_array_prop(olay_value
) and _is_array_prop(base_value
):
2342 # append extension array items to base items
2343 base_value
+= olay_value
2345 # fall back to replacing
2346 base_node
[olay_key
] = olay_value
2348 base_node
[olay_key
] = olay_value
2350 def _process_node_include(self
, last_overlay_node
, name
,
2351 process_base_include_cb
,
2352 process_children_include_cb
=None):
2353 if not _is_assoc_array_prop(last_overlay_node
):
2354 raise ConfigError('{} objects must be associative arrays'.format(name
))
2356 # process children inclusions first
2357 if process_children_include_cb
:
2358 process_children_include_cb(last_overlay_node
)
2360 if '$include' in last_overlay_node
:
2361 include_node
= last_overlay_node
['$include']
2364 return last_overlay_node
2366 include_paths
= self
._get
_include
_paths
(include_node
)
2367 cur_base_path
= self
._get
_last
_include
_file
()
2370 # keep the include paths and remove the include property
2371 include_paths
= copy
.deepcopy(include_paths
)
2372 del last_overlay_node
['$include']
2374 for include_path
in include_paths
:
2375 # load raw YAML from included file
2376 overlay_node
= self
._load
_include
(include_path
)
2378 if overlay_node
is None:
2379 # cannot find include file, but we're ignoring those
2380 # errors, otherwise _load_include() itself raises
2384 # recursively process includes
2386 overlay_node
= process_base_include_cb(overlay_node
)
2387 except Exception as e
:
2388 raise ConfigError('in "{}"'.format(cur_base_path
), e
)
2390 # pop include stack now that we're done including
2391 del self
._include
_stack
[-1]
2393 # at this point, base_node is fully resolved (does not
2394 # contain any include property)
2395 if base_node
is None:
2396 base_node
= overlay_node
2398 self
._update
_node
(base_node
, overlay_node
)
2400 # finally, we update the latest base node with our last overlay
2402 if base_node
is None:
2403 # nothing was included, which is possible when we're
2404 # ignoring include errors
2405 return last_overlay_node
2407 self
._update
_node
(base_node
, last_overlay_node
)
2411 def _process_event_include(self
, event_node
):
2412 return self
._process
_node
_include
(event_node
, 'event',
2413 self
._process
_event
_include
)
2415 def _process_stream_include(self
, stream_node
):
2416 def process_children_include(stream_node
):
2417 if 'events' in stream_node
:
2418 events_node
= stream_node
['events']
2420 if not _is_assoc_array_prop(events_node
):
2421 raise ConfigError('"events" property must be an associative array')
2423 events_node_keys
= list(events_node
.keys())
2425 for key
in events_node_keys
:
2426 event_node
= events_node
[key
]
2429 events_node
[key
] = self
._process
_event
_include
(event_node
)
2430 except Exception as e
:
2431 raise ConfigError('cannot process includes of event object "{}"'.format(key
), e
)
2433 return self
._process
_node
_include
(stream_node
, 'stream',
2434 self
._process
_stream
_include
,
2435 process_children_include
)
2437 def _process_trace_include(self
, trace_node
):
2438 return self
._process
_node
_include
(trace_node
, 'trace',
2439 self
._process
_trace
_include
)
2441 def _process_clock_include(self
, clock_node
):
2442 return self
._process
_node
_include
(clock_node
, 'clock',
2443 self
._process
_clock
_include
)
2445 def _process_metadata_include(self
, metadata_node
):
2446 def process_children_include(metadata_node
):
2447 if 'trace' in metadata_node
:
2448 metadata_node
['trace'] = self
._process
_trace
_include
(metadata_node
['trace'])
2450 if 'clocks' in metadata_node
:
2451 clocks_node
= metadata_node
['clocks']
2453 if not _is_assoc_array_prop(clocks_node
):
2454 raise ConfigError('"clocks" property (metadata) must be an associative array')
2456 clocks_node_keys
= list(clocks_node
.keys())
2458 for key
in clocks_node_keys
:
2459 clock_node
= clocks_node
[key
]
2462 clocks_node
[key
] = self
._process
_clock
_include
(clock_node
)
2463 except Exception as e
:
2464 raise ConfigError('cannot process includes of clock object "{}"'.format(key
), e
)
2466 if 'streams' in metadata_node
:
2467 streams_node
= metadata_node
['streams']
2469 if not _is_assoc_array_prop(streams_node
):
2470 raise ConfigError('"streams" property (metadata) must be an associative array')
2472 streams_node_keys
= list(streams_node
.keys())
2474 for key
in streams_node_keys
:
2475 stream_node
= streams_node
[key
]
2478 streams_node
[key
] = self
._process
_stream
_include
(stream_node
)
2479 except Exception as e
:
2480 raise ConfigError('cannot process includes of stream object "{}"'.format(key
), e
)
2482 return self
._process
_node
_include
(metadata_node
, 'metadata',
2483 self
._process
_metadata
_include
,
2484 process_children_include
)
2486 def _process_root_includes(self
, root
):
2487 # The following config objects support includes:
2494 # We need to process the event includes first, then the stream
2495 # includes, then the trace includes, and finally the metadata
2498 # In each object, only one of the $include and $include-replace
2499 # special properties is allowed.
2501 # We keep a stack of absolute paths to included files to detect
2503 if 'metadata' in root
:
2504 root
['metadata'] = self
._process
_metadata
_include
(root
['metadata'])
2508 def _yaml_ordered_dump(self
, node
, **kwds
):
2509 class ODumper(yaml
.Dumper
):
2512 def dict_representer(dumper
, node
):
2513 return dumper
.represent_mapping(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2516 ODumper
.add_representer(collections
.OrderedDict
, dict_representer
)
2518 return yaml
.dump(node
, Dumper
=ODumper
, **kwds
)
2520 def _yaml_ordered_load(self
, yaml_path
):
2521 class OLoader(yaml
.Loader
):
2524 def construct_mapping(loader
, node
):
2525 loader
.flatten_mapping(node
)
2527 return collections
.OrderedDict(loader
.construct_pairs(node
))
2529 OLoader
.add_constructor(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2534 with
open(yaml_path
, 'r') as f
:
2535 node
= yaml
.load(f
, OLoader
)
2536 except (OSError, IOError) as e
:
2537 raise ConfigError('cannot open file "{}"'.format(yaml_path
))
2538 except Exception as e
:
2539 raise ConfigError('unknown error while trying to load file "{}"'.format(yaml_path
), e
)
2541 # loaded node must be an associate array
2542 if not _is_assoc_array_prop(node
):
2543 raise ConfigError('root of YAML file "{}" must be an associative array'.format(yaml_path
))
2548 self
._version
= None
2549 self
._include
_stack
= []
2551 def parse(self
, yaml_path
):
2553 self
._root
_yaml
_path
= yaml_path
2556 root
= self
._yaml
_ordered
_load
(yaml_path
)
2557 except Exception as e
:
2558 raise ConfigError('cannot parse YAML file "{}"'.format(yaml_path
), e
)
2560 if not _is_assoc_array_prop(root
):
2561 raise ConfigError('configuration must be an associative array')
2563 unk_prop
= _get_first_unknown_prop(root
, [
2570 raise ConfigError('unknown configuration property: "{}"'.format(unk_prop
))
2572 # get the config version
2573 self
._version
= self
._get
_version
(root
)
2575 # process includes if supported
2576 if self
._version
>= 201:
2577 root
= self
._process
_root
_includes
(root
)
2579 # dump config if required
2580 if self
._dump
_config
:
2581 print(self
._yaml
_ordered
_dump
(root
, indent
=2,
2582 default_flow_style
=False))
2584 # get prefix and metadata
2585 prefix
= self
._get
_prefix
(root
)
2586 meta
= self
._create
_metadata
(root
)
2588 return Config(self
._version
, prefix
, meta
)
2591 def from_yaml_file(path
, include_dirs
, ignore_include_not_found
, dump_config
):
2593 parser
= _YamlConfigParser(include_dirs
, ignore_include_not_found
,
2595 cfg
= parser
.parse(path
)
2598 except Exception as e
:
2599 raise ConfigError('cannot create configuration from YAML file "{}"'.format(path
), e
)