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
34 class ConfigError(RuntimeError):
35 def __init__(self
, msg
, prev
=None):
45 def __init__(self
, version
, prefix
, metadata
):
47 self
.version
= version
48 self
.metadata
= metadata
50 def _validate_metadata(self
, meta
):
52 validator
= _MetadataTypesHistologyValidator()
53 validator
.validate(meta
)
54 validator
= _MetadataDynamicTypesValidator()
55 validator
.validate(meta
)
56 validator
= _MetadataSpecialFieldsValidator()
57 validator
.validate(meta
)
58 except Exception as e
:
59 raise ConfigError('metadata error', e
)
62 validator
= _BarectfMetadataValidator()
63 validator
.validate(meta
)
64 except Exception as e
:
65 raise ConfigError('barectf metadata error', e
)
67 def _augment_metadata_env(self
, meta
):
70 env
['domain'] = 'bare'
71 env
['tracer_name'] = 'barectf'
72 version_tuple
= barectf
.get_version_tuple()
73 env
['tracer_major'] = version_tuple
[0]
74 env
['tracer_minor'] = version_tuple
[1]
75 env
['tracer_patch'] = version_tuple
[2]
76 env
['barectf_gen_date'] = str(datetime
.datetime
.now().isoformat())
83 def version(self
, value
):
91 def metadata(self
, value
):
92 self
._validate
_metadata
(value
)
93 self
._augment
_metadata
_env
(value
)
94 self
._metadata
= value
101 def prefix(self
, value
):
102 if not is_valid_identifier(value
):
103 raise ConfigError('prefix must be a valid C identifier')
108 def _is_assoc_array_prop(node
):
109 return isinstance(node
, dict)
112 def _is_array_prop(node
):
113 return isinstance(node
, list)
116 def _is_int_prop(node
):
117 return type(node
) is int
120 def _is_str_prop(node
):
121 return type(node
) is str
124 def _is_bool_prop(node
):
125 return type(node
) is bool
128 def _is_valid_alignment(align
):
129 return ((align
& (align
- 1)) == 0) and align
> 0
132 def _byte_order_str_to_bo(bo_str
):
133 bo_str
= bo_str
.lower()
136 return metadata
.ByteOrder
.LE
138 return metadata
.ByteOrder
.BE
141 def _encoding_str_to_encoding(encoding_str
):
142 encoding_str
= encoding_str
.lower()
144 if encoding_str
== 'utf-8' or encoding_str
== 'utf8':
145 return metadata
.Encoding
.UTF8
146 elif encoding_str
== 'ascii':
147 return metadata
.Encoding
.ASCII
148 elif encoding_str
== 'none':
149 return metadata
.Encoding
.NONE
152 _re_iden
= re
.compile(r
'^[a-zA-Z][a-zA-Z0-9_]*$')
153 _ctf_keywords
= set([
172 def is_valid_identifier(iden
):
173 if not _re_iden
.match(iden
):
176 if _re_iden
in _ctf_keywords
:
182 def _get_first_unknown_prop(node
, known_props
):
183 for prop_name
in node
:
184 if prop_name
in known_props
:
190 # This validator validates the configured metadata for barectf specific
195 # * all header/contexts are at least byte-aligned
196 # * all integer and floating point number sizes to be <= 64
197 # * no inner structures, arrays, or variants
198 class _BarectfMetadataValidator
:
200 self
._type
_to
_validate
_type
_func
= {
201 metadata
.Integer
: self
._validate
_int
_type
,
202 metadata
.FloatingPoint
: self
._validate
_float
_type
,
203 metadata
.Enum
: self
._validate
_enum
_type
,
204 metadata
.String
: self
._validate
_string
_type
,
205 metadata
.Struct
: self
._validate
_struct
_type
,
206 metadata
.Array
: self
._validate
_array
_type
,
207 metadata
.Variant
: self
._validate
_variant
_type
,
210 def _validate_int_type(self
, t
, entity_root
):
212 raise ConfigError('integer type\'s size must be lesser than or equal to 64 bits')
214 def _validate_float_type(self
, t
, entity_root
):
216 raise ConfigError('floating point number type\'s size must be lesser than or equal to 64 bits')
218 def _validate_enum_type(self
, t
, entity_root
):
219 if t
.value_type
.size
> 64:
220 raise ConfigError('enumeration type\'s integer type\'s size must be lesser than or equal to 64 bits')
222 def _validate_string_type(self
, t
, entity_root
):
225 def _validate_struct_type(self
, t
, entity_root
):
227 raise ConfigError('inner structure types are not supported as of this version')
229 for field_name
, field_type
in t
.fields
.items():
230 if entity_root
and self
._cur
_entity
is _Entity
.TRACE_PACKET_HEADER
:
231 if field_name
== 'uuid':
236 self
._validate
_type
(field_type
, False)
237 except Exception as e
:
238 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
240 def _validate_array_type(self
, t
, entity_root
):
241 raise ConfigError('array types are not supported as of this version')
243 def _validate_variant_type(self
, t
, entity_root
):
244 raise ConfigError('variant types are not supported as of this version')
246 def _validate_type(self
, t
, entity_root
):
247 self
._type
_to
_validate
_type
_func
[type(t
)](t
, entity_root
)
249 def _validate_entity(self
, t
):
253 # make sure entity is byte-aligned
255 raise ConfigError('type\'s alignment must be at least byte-aligned')
257 # make sure entity is a structure
258 if type(t
) is not metadata
.Struct
:
259 raise ConfigError('expecting a structure type')
262 self
._validate
_type
(t
, True)
264 def _validate_entities_and_names(self
, meta
):
265 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
268 self
._validate
_entity
(meta
.trace
.packet_header_type
)
269 except Exception as e
:
270 raise ConfigError('invalid trace packet header type', e
)
272 for stream_name
, stream
in meta
.streams
.items():
273 if not is_valid_identifier(stream_name
):
274 raise ConfigError('stream name "{}" is not a valid C identifier'.format(stream_name
))
276 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
279 self
._validate
_entity
(stream
.packet_context_type
)
280 except Exception as e
:
281 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
283 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
286 self
._validate
_entity
(stream
.event_header_type
)
287 except Exception as e
:
288 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
290 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
293 self
._validate
_entity
(stream
.event_context_type
)
294 except Exception as e
:
295 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
298 for ev_name
, ev
in stream
.events
.items():
299 if not is_valid_identifier(ev_name
):
300 raise ConfigError('event name "{}" is not a valid C identifier'.format(ev_name
))
302 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
305 self
._validate
_entity
(ev
.context_type
)
306 except Exception as e
:
307 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
309 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
311 if ev
.payload_type
is None:
312 raise ConfigError('missing payload type in event "{}"'.format(ev_name
), e
)
315 self
._validate
_entity
(ev
.payload_type
)
316 except Exception as e
:
317 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
319 if not ev
.payload_type
.fields
:
320 raise ConfigError('empty payload type in event "{}"'.format(ev_name
), e
)
321 except Exception as e
:
322 raise ConfigError('invalid stream "{}"'.format(stream_name
), e
)
324 def validate(self
, meta
):
325 self
._validate
_entities
_and
_names
(meta
)
328 # This validator validates special fields of trace, stream, and event
329 # types. For example, if checks that the "stream_id" field exists in the
330 # trace packet header if there's more than one stream, and much more.
331 class _MetadataSpecialFieldsValidator
:
332 def _validate_trace_packet_header_type(self
, t
):
333 # needs "stream_id" field?
334 if len(self
._meta
.streams
) > 1:
337 raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is missing')
339 if type(t
) is not metadata
.Struct
:
340 raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is not a structure type')
342 if 'stream_id' not in t
.fields
:
343 raise ConfigError('need "stream_id" field in trace packet header type')
345 # validate "magic" and "stream_id" types
346 if type(t
) is not metadata
.Struct
:
349 for i
, (field_name
, field_type
) in enumerate(t
.fields
.items()):
350 if field_name
== 'magic':
351 if type(field_type
) is not metadata
.Integer
:
352 raise ConfigError('"magic" field in trace packet header type must be an integer type')
354 if field_type
.signed
or field_type
.size
!= 32:
355 raise ConfigError('"magic" field in trace packet header type must be a 32-bit unsigned integer type')
358 raise ConfigError('"magic" field must be the first trace packet header type\'s field')
359 elif field_name
== 'stream_id':
360 if type(field_type
) is not metadata
.Integer
:
361 raise ConfigError('"stream_id" field in trace packet header type must be an integer type')
363 if field_type
.signed
:
364 raise ConfigError('"stream_id" field in trace packet header type must be an unsigned integer type')
365 elif field_name
== 'uuid':
366 if self
._meta
.trace
.uuid
is None:
367 raise ConfigError('"uuid" field in trace packet header type specified, but no trace UUID provided')
369 if type(field_type
) is not metadata
.Array
:
370 raise ConfigError('"uuid" field in trace packet header type must be an array')
372 if field_type
.length
!= 16:
373 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
375 element_type
= field_type
.element_type
377 if type(element_type
) is not metadata
.Integer
:
378 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
380 if element_type
.size
!= 8:
381 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
383 if element_type
.align
!= 8:
384 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 byte-aligned bytes')
386 def _validate_trace(self
, meta
):
387 self
._validate
_trace
_packet
_header
_type
(meta
.trace
.packet_header_type
)
389 def _validate_stream_packet_context(self
, stream
):
390 t
= stream
.packet_context_type
395 if type(t
) is not metadata
.Struct
:
398 # "timestamp_begin", if exists, is an unsigned integer type,
400 if 'timestamp_begin' in t
.fields
:
401 ts_begin
= t
.fields
['timestamp_begin']
403 if type(ts_begin
) is not metadata
.Integer
:
404 raise ConfigError('"timestamp_begin" field in stream packet context type must be an integer type')
407 raise ConfigError('"timestamp_begin" field in stream packet context type must be an unsigned integer type')
409 if not ts_begin
.property_mappings
:
410 raise ConfigError('"timestamp_begin" field in stream packet context type must be mapped to a clock')
412 # "timestamp_end", if exists, is an unsigned integer type,
414 if 'timestamp_end' in t
.fields
:
415 ts_end
= t
.fields
['timestamp_end']
417 if type(ts_end
) is not metadata
.Integer
:
418 raise ConfigError('"timestamp_end" field in stream packet context type must be an integer type')
421 raise ConfigError('"timestamp_end" field in stream packet context type must be an unsigned integer type')
423 if not ts_end
.property_mappings
:
424 raise ConfigError('"timestamp_end" field in stream packet context type must be mapped to a clock')
426 # "timestamp_begin" and "timestamp_end" exist together
427 if (('timestamp_begin' in t
.fields
) ^
('timestamp_end' in t
.fields
)):
428 raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be defined together in stream packet context type')
430 # "events_discarded", if exists, is an unsigned integer type
431 if 'events_discarded' in t
.fields
:
432 events_discarded
= t
.fields
['events_discarded']
434 if type(events_discarded
) is not metadata
.Integer
:
435 raise ConfigError('"events_discarded" field in stream packet context type must be an integer type')
437 if events_discarded
.signed
:
438 raise ConfigError('"events_discarded" field in stream packet context type must be an unsigned integer type')
440 # "packet_size" and "content_size" must exist
441 if 'packet_size' not in t
.fields
:
442 raise ConfigError('missing "packet_size" field in stream packet context type')
444 packet_size
= t
.fields
['packet_size']
446 # "content_size" and "content_size" must exist
447 if 'content_size' not in t
.fields
:
448 raise ConfigError('missing "content_size" field in stream packet context type')
450 content_size
= t
.fields
['content_size']
452 # "packet_size" is an unsigned integer type
453 if type(packet_size
) is not metadata
.Integer
:
454 raise ConfigError('"packet_size" field in stream packet context type must be an integer type')
456 if packet_size
.signed
:
457 raise ConfigError('"packet_size" field in stream packet context type must be an unsigned integer type')
459 # "content_size" is an unsigned integer type
460 if type(content_size
) is not metadata
.Integer
:
461 raise ConfigError('"content_size" field in stream packet context type must be an integer type')
463 if content_size
.signed
:
464 raise ConfigError('"content_size" field in stream packet context type must be an unsigned integer type')
466 def _validate_stream_event_header(self
, stream
):
467 t
= stream
.event_header_type
470 if len(stream
.events
) > 1:
473 raise ConfigError('need "id" field in stream event header type, but stream event header type is missing')
475 if type(t
) is not metadata
.Struct
:
476 raise ConfigError('need "id" field in stream event header type, but stream event header type is not a structure type')
478 if 'id' not in t
.fields
:
479 raise ConfigError('need "id" field in stream event header type')
481 # validate "id" and "timestamp" types
482 if type(t
) is not metadata
.Struct
:
485 # "timestamp", if exists, is an unsigned integer type,
487 if 'timestamp' in t
.fields
:
488 ts
= t
.fields
['timestamp']
490 if type(ts
) is not metadata
.Integer
:
491 raise ConfigError('"ts" field in stream event header type must be an integer type')
494 raise ConfigError('"ts" field in stream event header type must be an unsigned integer type')
496 if not ts
.property_mappings
:
497 raise ConfigError('"ts" field in stream event header type must be mapped to a clock')
499 # "id" is an unsigned integer type
503 if type(eid
) is not metadata
.Integer
:
504 raise ConfigError('"id" field in stream event header type must be an integer type')
507 raise ConfigError('"id" field in stream event header type must be an unsigned integer type')
509 def _validate_stream(self
, stream
):
510 self
._validate
_stream
_packet
_context
(stream
)
511 self
._validate
_stream
_event
_header
(stream
)
513 def validate(self
, meta
):
515 self
._validate
_trace
(meta
)
517 for stream
in meta
.streams
.values():
519 self
._validate
_stream
(stream
)
520 except Exception as e
:
521 raise ConfigError('invalid stream "{}"'.format(stream
.name
), e
)
524 class _MetadataDynamicTypesValidatorStackEntry
:
525 def __init__(self
, base_t
):
526 self
._base
_t
= base_t
534 def index(self
, value
):
542 def base_t(self
, value
):
546 # Entities. Order of values is important here.
548 class _Entity(enum
.IntEnum
):
549 TRACE_PACKET_HEADER
= 0
550 STREAM_PACKET_CONTEXT
= 1
551 STREAM_EVENT_HEADER
= 2
552 STREAM_EVENT_CONTEXT
= 3
557 # This validator validates dynamic metadata types, that is, it ensures
558 # variable-length array lengths and variant tags actually point to
559 # something that exists. It also checks that variable-length array
560 # lengths point to integer types and variant tags to enumeration types.
561 class _MetadataDynamicTypesValidator
:
563 self
._type
_to
_visit
_type
_func
= {
564 metadata
.Integer
: None,
565 metadata
.FloatingPoint
: None,
567 metadata
.String
: None,
568 metadata
.Struct
: self
._visit
_struct
_type
,
569 metadata
.Array
: self
._visit
_array
_type
,
570 metadata
.Variant
: self
._visit
_variant
_type
,
573 self
._cur
_trace
= None
574 self
._cur
_stream
= None
575 self
._cur
_event
= None
577 def _lookup_path_from_base(self
, path
, parts
, base
, start_index
,
578 base_is_current
, from_t
):
583 while index
< len(parts
):
587 if type(cur_t
) is metadata
.Struct
:
588 enumerated_items
= enumerate(cur_t
.fields
.items())
591 for i
, (field_name
, field_type
) in enumerated_items
:
592 if field_name
== part
:
594 found_path
.append((i
, field_type
))
597 raise ConfigError('invalid path "{}": cannot find field "{}" in structure type'.format(path
, part
))
598 elif type(cur_t
) is metadata
.Variant
:
599 enumerated_items
= enumerate(cur_t
.types
.items())
602 for i
, (type_name
, type_type
) in enumerated_items
:
603 if type_name
== part
:
605 found_path
.append((i
, type_type
))
608 raise ConfigError('invalid path "{}": cannot find type "{}" in variant type'.format(path
, part
))
610 raise ConfigError('invalid path "{}": requesting "{}" in a non-variant, non-structure type'.format(path
, part
))
615 # make sure that the pointed type is not the pointing type
617 raise ConfigError('invalid path "{}": pointing to self'.format(path
))
619 # if we're here, we found the type; however, it could be located
620 # _after_ the variant/VLA looking for it, if the pointing
621 # and pointed types are in the same entity, so compare the
622 # current stack entries indexes to our index path in that case
623 if not base_is_current
:
626 for index
, entry
in enumerate(self
._stack
):
627 if index
== len(found_path
):
628 # end of index path; valid so far
631 if found_path
[index
][0] > entry
.index
:
632 raise ConfigError('invalid path "{}": pointed type is after pointing type'.format(path
))
634 # also make sure that both pointed and pointing types share
635 # a common structure ancestor
636 for index
, entry
in enumerate(self
._stack
):
637 if index
== len(found_path
):
640 if entry
.base_t
is not found_path
[index
][1]:
641 # found common ancestor
642 if type(entry
.base_t
) is metadata
.Variant
:
643 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
647 def _lookup_path_from_top(self
, path
, parts
):
649 raise ConfigError('invalid path "{}": multipart relative path not supported'.format(path
))
652 index
= len(self
._stack
) - 1
655 # check stack entries in reversed order
656 for entry
in reversed(self
._stack
):
657 # structure base type
658 if type(entry
.base_t
) is metadata
.Struct
:
660 enumerated_items
= enumerate(entry
.base_t
.fields
.items())
662 # lookup each field, until the current visiting index is met
663 for i
, (field_name
, field_type
) in enumerated_items
:
667 if field_name
== find_name
:
671 elif type(entry
.base_t
) is metadata
.Variant
:
672 enumerated_items
= enumerate(entry
.base_t
.types
.items())
674 # lookup each type, until the current visiting index is met
675 for i
, (type_name
, type_type
) in enumerated_items
:
679 if type_name
== find_name
:
681 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
685 # nothing returned here: cannot find type
686 raise ConfigError('invalid path "{}": cannot find type in current context'.format(path
))
688 def _lookup_path(self
, path
, from_t
):
689 parts
= path
.lower().split('.')
691 base_is_current
= False
694 if parts
[0] == 'trace':
695 if parts
[1] == 'packet' and parts
[2] == 'header':
696 # make sure packet header exists
697 if self
._cur
_trace
.packet_header_type
is None:
698 raise ConfigError('invalid path "{}": no defined trace packet header type'.format(path
))
700 base
= self
._cur
_trace
.packet_header_type
702 if self
._cur
_entity
== _Entity
.TRACE_PACKET_HEADER
:
703 base_is_current
= True
705 raise ConfigError('invalid path "{}": unknown names after "trace"'.format(path
))
706 elif parts
[0] == 'stream':
707 if parts
[1] == 'packet' and parts
[2] == 'context':
708 if self
._cur
_entity
< _Entity
.STREAM_PACKET_CONTEXT
:
709 raise ConfigError('invalid path "{}": cannot access stream packet context here'.format(path
))
711 if self
._cur
_stream
.packet_context_type
is None:
712 raise ConfigError('invalid path "{}": no defined stream packet context type'.format(path
))
714 base
= self
._cur
_stream
.packet_context_type
716 if self
._cur
_entity
== _Entity
.STREAM_PACKET_CONTEXT
:
717 base_is_current
= True
718 elif parts
[1] == 'event':
719 if parts
[2] == 'header':
720 if self
._cur
_entity
< _Entity
.STREAM_EVENT_HEADER
:
721 raise ConfigError('invalid path "{}": cannot access stream event header here'.format(path
))
723 if self
._cur
_stream
.event_header_type
is None:
724 raise ConfigError('invalid path "{}": no defined stream event header type'.format(path
))
726 base
= self
._cur
_stream
.event_header_type
728 if self
._cur
_entity
== _Entity
.STREAM_EVENT_HEADER
:
729 base_is_current
= True
730 elif parts
[2] == 'context':
731 if self
._cur
_entity
< _Entity
.STREAM_EVENT_CONTEXT
:
732 raise ConfigError('invalid path "{}": cannot access stream event context here'.format(path
))
734 if self
._cur
_stream
.event_context_type
is None:
735 raise ConfigError('invalid path "{}": no defined stream event context type'.format(path
))
737 base
= self
._cur
_stream
.event_context_type
739 if self
._cur
_entity
== _Entity
.STREAM_EVENT_CONTEXT
:
740 base_is_current
= True
742 raise ConfigError('invalid path "{}": unknown names after "stream.event"'.format(path
))
744 raise ConfigError('invalid path "{}": unknown names after "stream"'.format(path
))
749 if len(parts
) >= 2 and base
is None:
750 if parts
[0] == 'event':
751 if parts
[1] == 'context':
752 if self
._cur
_entity
< _Entity
.EVENT_CONTEXT
:
753 raise ConfigError('invalid path "{}": cannot access event context here'.format(path
))
755 if self
._cur
_event
.context_type
is None:
756 raise ConfigError('invalid path "{}": no defined event context type'.format(path
))
758 base
= self
._cur
_event
.context_type
760 if self
._cur
_entity
== _Entity
.EVENT_CONTEXT
:
761 base_is_current
= True
762 elif parts
[1] == 'payload' or parts
[1] == 'fields':
763 if self
._cur
_entity
< _Entity
.EVENT_PAYLOAD
:
764 raise ConfigError('invalid path "{}": cannot access event payload here'.format(path
))
766 if self
._cur
_event
.payload_type
is None:
767 raise ConfigError('invalid path "{}": no defined event payload type'.format(path
))
769 base
= self
._cur
_event
.payload_type
771 if self
._cur
_entity
== _Entity
.EVENT_PAYLOAD
:
772 base_is_current
= True
774 raise ConfigError('invalid path "{}": unknown names after "event"'.format(path
))
780 return self
._lookup
_path
_from
_base
(path
, parts
, base
, start_index
,
781 base_is_current
, from_t
)
783 return self
._lookup
_path
_from
_top
(path
, parts
)
785 def _stack_reset(self
):
788 def _stack_push(self
, base_t
):
789 entry
= _MetadataDynamicTypesValidatorStackEntry(base_t
)
790 self
._stack
.append(entry
)
792 def _stack_pop(self
):
795 def _stack_incr_index(self
):
796 self
._stack
[-1].index
+= 1
798 def _visit_struct_type(self
, t
):
801 for field_name
, field_type
in t
.fields
.items():
803 self
._visit
_type
(field_type
)
804 except Exception as e
:
805 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
807 self
._stack
_incr
_index
()
811 def _visit_array_type(self
, t
):
815 length_type
= self
._lookup
_path
(t
.length
, t
)
816 except Exception as e
:
817 raise ConfigError('invalid array type\'s length', e
)
819 # make sure length type an unsigned integer
820 if type(length_type
) is not metadata
.Integer
:
821 raise ConfigError('array type\'s length does not point to an integer type')
823 if length_type
.signed
:
824 raise ConfigError('array type\'s length does not point to an unsigned integer type')
826 self
._visit
_type
(t
.element_type
)
828 def _visit_variant_type(self
, t
):
831 tag_type
= self
._lookup
_path
(t
.tag
, t
)
832 except Exception as e
:
833 raise ConfigError('invalid variant type\'s tag', e
)
835 # make sure tag type is an enumeration
836 if type(tag_type
) is not metadata
.Enum
:
837 raise ConfigError('variant type\'s tag does not point to an enumeration type')
839 # verify that each variant type's type exists as an enumeration member
840 for tag_name
in t
.types
.keys():
841 if tag_name
not in tag_type
.members
:
842 raise ConfigError('cannot find variant type\'s type "{}" in pointed tag type'.format(tag_name
))
846 for type_name
, type_type
in t
.types
.items():
848 self
._visit
_type
(type_type
)
849 except Exception as e
:
850 raise ConfigError('in variant type\'s type "{}"'.format(type_name
), e
)
852 self
._stack
_incr
_index
()
856 def _visit_type(self
, t
):
860 if type(t
) in self
._type
_to
_visit
_type
_func
:
861 func
= self
._type
_to
_visit
_type
_func
[type(t
)]
866 def _visit_event(self
, ev
):
872 # visit event context type
874 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
877 self
._visit
_type
(ev
.context_type
)
878 except Exception as e
:
879 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
881 # visit event payload type
883 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
886 self
._visit
_type
(ev
.payload_type
)
887 except Exception as e
:
888 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
890 def _visit_stream(self
, stream
):
891 stream_name
= stream
.name
894 self
._cur
_stream
= stream
896 # reset current event
897 self
._cur
_event
= None
899 # visit stream packet context type
901 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
904 self
._visit
_type
(stream
.packet_context_type
)
905 except Exception as e
:
906 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
908 # visit stream event header type
910 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
913 self
._visit
_type
(stream
.event_header_type
)
914 except Exception as e
:
915 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
917 # visit stream event context type
919 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
922 self
._visit
_type
(stream
.event_context_type
)
923 except Exception as e
:
924 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
927 for ev
in stream
.events
.values():
929 self
._visit
_event
(ev
)
930 except Exception as e
:
931 raise ConfigError('invalid stream "{}"'.format(stream_name
))
933 def validate(self
, meta
):
935 self
._cur
_trace
= meta
.trace
937 # visit trace packet header type
939 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
942 self
._visit
_type
(meta
.trace
.packet_header_type
)
943 except Exception as e
:
944 raise ConfigError('invalid packet header type in trace', e
)
947 for stream
in meta
.streams
.values():
948 self
._visit
_stream
(stream
)
951 # Since type inheritance allows types to be only partially defined at
952 # any place in the configuration, this validator validates that actual
953 # trace, stream, and event types are all complete and valid.
954 class _MetadataTypesHistologyValidator
:
956 self
._type
_to
_validate
_type
_histology
_func
= {
957 metadata
.Integer
: self
._validate
_integer
_histology
,
958 metadata
.FloatingPoint
: self
._validate
_float
_histology
,
959 metadata
.Enum
: self
._validate
_enum
_histology
,
960 metadata
.String
: self
._validate
_string
_histology
,
961 metadata
.Struct
: self
._validate
_struct
_histology
,
962 metadata
.Array
: self
._validate
_array
_histology
,
963 metadata
.Variant
: self
._validate
_variant
_histology
,
966 def _validate_integer_histology(self
, t
):
969 raise ConfigError('missing integer type\'s size')
971 def _validate_float_histology(self
, t
):
972 # exponent digits is set
973 if t
.exp_size
is None:
974 raise ConfigError('missing floating point number type\'s exponent size')
976 # mantissa digits is set
977 if t
.mant_size
is None:
978 raise ConfigError('missing floating point number type\'s mantissa size')
980 # exponent and mantissa sum is a multiple of 8
981 if (t
.exp_size
+ t
.mant_size
) % 8 != 0:
982 raise ConfigError('floating point number type\'s mantissa and exponent sizes sum must be a multiple of 8')
984 def _validate_enum_histology(self
, t
):
985 # integer type is set
986 if t
.value_type
is None:
987 raise ConfigError('missing enumeration type\'s integer type')
989 # there's at least one member
991 raise ConfigError('enumeration type needs at least one member')
993 # no overlapping values
996 for label
, value
in t
.members
.items():
998 if value
[0] <= rg
[1] and rg
[0] <= value
[1]:
999 raise ConfigError('enumeration type\'s member "{}" overlaps another member'.format(label
))
1001 ranges
.append(value
)
1003 def _validate_string_histology(self
, t
):
1007 def _validate_struct_histology(self
, t
):
1008 # all fields are valid
1009 for field_name
, field_type
in t
.fields
.items():
1011 self
._validate
_type
_histology
(field_type
)
1012 except Exception as e
:
1013 raise ConfigError('invalid structure type\'s field "{}"'.format(field_name
), e
)
1015 def _validate_array_histology(self
, t
):
1017 if t
.length
is None:
1018 raise ConfigError('missing array type\'s length')
1020 # element type is set
1021 if t
.element_type
is None:
1022 raise ConfigError('missing array type\'s element type')
1024 # element type is valid
1026 self
._validate
_type
_histology
(t
.element_type
)
1027 except Exception as e
:
1028 raise ConfigError('invalid array type\'s element type', e
)
1030 def _validate_variant_histology(self
, t
):
1033 raise ConfigError('missing variant type\'s tag')
1035 # there's at least one type
1037 raise ConfigError('variant type needs at least one type')
1039 # all types are valid
1040 for type_name
, type_t
in t
.types
.items():
1042 self
._validate
_type
_histology
(type_t
)
1043 except Exception as e
:
1044 raise ConfigError('invalid variant type\'s type "{}"'.format(type_name
), e
)
1046 def _validate_type_histology(self
, t
):
1050 self
._type
_to
_validate
_type
_histology
_func
[type(t
)](t
)
1052 def _validate_entity_type_histology(self
, t
):
1056 # entity cannot be an array
1057 if type(t
) is metadata
.Array
:
1058 raise ConfigError('cannot use an array here')
1060 self
._validate
_type
_histology
(t
)
1062 def _validate_event_types_histology(self
, ev
):
1065 # validate event context type
1067 self
._validate
_entity
_type
_histology
(ev
.context_type
)
1068 except Exception as e
:
1069 raise ConfigError('invalid event context type for event "{}"'.format(ev_name
), e
)
1071 # validate event payload type
1072 if ev
.payload_type
is None:
1073 raise ConfigError('event payload type must exist in event "{}"'.format(ev_name
))
1075 # TODO: also check arrays, sequences, and variants
1076 if type(ev
.payload_type
) is metadata
.Struct
:
1077 if not ev
.payload_type
.fields
:
1078 raise ConfigError('event payload type must have at least one field for event "{}"'.format(ev_name
))
1081 self
._validate
_entity
_type
_histology
(ev
.payload_type
)
1082 except Exception as e
:
1083 raise ConfigError('invalid event payload type for event "{}"'.format(ev_name
), e
)
1085 def _validate_stream_types_histology(self
, stream
):
1086 stream_name
= stream
.name
1088 # validate stream packet context type
1090 self
._validate
_entity
_type
_histology
(stream
.packet_context_type
)
1091 except Exception as e
:
1092 raise ConfigError('invalid stream packet context type for stream "{}"'.format(stream_name
), e
)
1094 # validate stream event header type
1096 self
._validate
_entity
_type
_histology
(stream
.event_header_type
)
1097 except Exception as e
:
1098 raise ConfigError('invalid stream event header type for stream "{}"'.format(stream_name
), e
)
1100 # validate stream event context type
1102 self
._validate
_entity
_type
_histology
(stream
.event_context_type
)
1103 except Exception as e
:
1104 raise ConfigError('invalid stream event context type for stream "{}"'.format(stream_name
), e
)
1107 for ev
in stream
.events
.values():
1109 self
._validate
_event
_types
_histology
(ev
)
1110 except Exception as e
:
1111 raise ConfigError('invalid event in stream "{}"'.format(stream_name
), e
)
1113 def validate(self
, meta
):
1114 # validate trace packet header type
1116 self
._validate
_entity
_type
_histology
(meta
.trace
.packet_header_type
)
1117 except Exception as e
:
1118 raise ConfigError('invalid trace packet header type', e
)
1121 for stream
in meta
.streams
.values():
1122 self
._validate
_stream
_types
_histology
(stream
)
1125 class _YamlConfigParser
:
1127 self
._class
_name
_to
_create
_type
_func
= {
1128 'int': self
._create
_integer
,
1129 'integer': self
._create
_integer
,
1130 'flt': self
._create
_float
,
1131 'float': self
._create
_float
,
1132 'floating-point': self
._create
_float
,
1133 'enum': self
._create
_enum
,
1134 'enumeration': self
._create
_enum
,
1135 'str': self
._create
_string
,
1136 'string': self
._create
_string
,
1137 'struct': self
._create
_struct
,
1138 'structure': self
._create
_struct
,
1139 'array': self
._create
_array
,
1140 'var': self
._create
_variant
,
1141 'variant': self
._create
_variant
,
1143 self
._type
_to
_create
_type
_func
= {
1144 metadata
.Integer
: self
._create
_integer
,
1145 metadata
.FloatingPoint
: self
._create
_float
,
1146 metadata
.Enum
: self
._create
_enum
,
1147 metadata
.String
: self
._create
_string
,
1148 metadata
.Struct
: self
._create
_struct
,
1149 metadata
.Array
: self
._create
_array
,
1150 metadata
.Variant
: self
._create
_variant
,
1153 def _set_byte_order(self
, metadata_node
):
1154 if 'trace' not in metadata_node
:
1155 raise ConfigError('missing "trace" property (metadata)')
1157 trace_node
= metadata_node
['trace']
1159 if not _is_assoc_array_prop(trace_node
):
1160 raise ConfigError('"trace" property (metadata) must be an associative array')
1162 if 'byte-order' not in trace_node
:
1163 raise ConfigError('missing "byte-order" property (trace)')
1165 self
._bo
= _byte_order_str_to_bo(trace_node
['byte-order'])
1167 if self
._bo
is None:
1168 raise ConfigError('invalid "byte-order" property (trace): must be "le" or "be"')
1170 def _lookup_type_alias(self
, name
):
1171 if name
in self
._tas
:
1172 return copy
.deepcopy(self
._tas
[name
])
1174 def _set_int_clock_prop_mapping(self
, int_obj
, prop_mapping_node
):
1175 unk_prop
= _get_first_unknown_prop(prop_mapping_node
, ['type', 'name', 'property'])
1178 raise ConfigError('unknown property in integer type object\'s clock property mapping: "{}"'.format(unk_prop
))
1180 if 'name' not in prop_mapping_node
:
1181 raise ConfigError('missing "name" property in integer type object\'s clock property mapping')
1183 if 'property' not in prop_mapping_node
:
1184 raise ConfigError('missing "property" property in integer type object\'s clock property mapping')
1186 clock_name
= prop_mapping_node
['name']
1187 prop
= prop_mapping_node
['property']
1189 if not _is_str_prop(clock_name
):
1190 raise ConfigError('"name" property of integer type object\'s clock property mapping must be a string')
1192 if not _is_str_prop(prop
):
1193 raise ConfigError('"property" property of integer type object\'s clock property mapping must be a string')
1195 if clock_name
not in self
._clocks
:
1196 raise ConfigError('invalid clock name "{}" in integer type object\'s clock property mapping'.format(clock_name
))
1199 raise ConfigError('invalid "property" property in integer type object\'s clock property mapping: "{}"'.format(prop
))
1201 mapped_clock
= self
._clocks
[clock_name
]
1202 int_obj
.property_mappings
.append(metadata
.PropertyMapping(mapped_clock
, prop
))
1204 def _get_first_unknown_type_prop(self
, type_node
, known_props
):
1205 kp
= known_props
+ ['inherit', 'class']
1207 if self
._version
>= 201:
1208 kp
.append('$inherit')
1210 return _get_first_unknown_prop(type_node
, kp
)
1212 def _create_integer(self
, obj
, node
):
1214 # create integer object
1215 obj
= metadata
.Integer()
1217 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1224 'property-mappings',
1228 raise ConfigError('unknown integer type object property: "{}"'.format(unk_prop
))
1234 if not _is_int_prop(size
):
1235 raise ConfigError('"size" property of integer type object must be an integer')
1238 raise ConfigError('invalid integer size: {}'.format(size
))
1244 align
= node
['align']
1246 if not _is_int_prop(align
):
1247 raise ConfigError('"align" property of integer type object must be an integer')
1249 if not _is_valid_alignment(align
):
1250 raise ConfigError('invalid alignment: {}'.format(align
))
1255 if 'signed' in node
:
1256 signed
= node
['signed']
1258 if not _is_bool_prop(signed
):
1259 raise ConfigError('"signed" property of integer type object must be a boolean')
1264 if 'byte-order' in node
:
1265 byte_order
= node
['byte-order']
1267 if not _is_str_prop(byte_order
):
1268 raise ConfigError('"byte-order" property of integer type object must be a string ("le" or "be")')
1270 byte_order
= _byte_order_str_to_bo(byte_order
)
1272 if byte_order
is None:
1273 raise ConfigError('invalid "byte-order" property in integer type object')
1275 byte_order
= self
._bo
1277 obj
.byte_order
= byte_order
1283 if not _is_str_prop(base
):
1284 raise ConfigError('"base" property of integer type object must be a string ("bin", "oct", "dec", or "hex")')
1298 if 'encoding' in node
:
1299 encoding
= node
['encoding']
1301 if not _is_str_prop(encoding
):
1302 raise ConfigError('"encoding" property of integer type object must be a string ("none", "ascii", or "utf-8")')
1304 encoding
= _encoding_str_to_encoding(encoding
)
1306 if encoding
is None:
1307 raise ConfigError('invalid "encoding" property in integer type object')
1309 obj
.encoding
= encoding
1312 if 'property-mappings' in node
:
1313 prop_mappings
= node
['property-mappings']
1315 if not _is_array_prop(prop_mappings
):
1316 raise ConfigError('"property-mappings" property of integer type object must be an array')
1318 if len(prop_mappings
) > 1:
1319 raise ConfigError('length of "property-mappings" array in integer type object must be 1')
1321 del obj
.property_mappings
[:]
1323 for index
, prop_mapping
in enumerate(prop_mappings
):
1324 if not _is_assoc_array_prop(prop_mapping
):
1325 raise ConfigError('elements of "property-mappings" property of integer type object must be associative arrays')
1327 if 'type' not in prop_mapping
:
1328 raise ConfigError('missing "type" property in integer type object\'s "property-mappings" array\'s element #{}'.format(index
))
1330 prop_type
= prop_mapping
['type']
1332 if not _is_str_prop(prop_type
):
1333 raise ConfigError('"type" property of integer type object\'s "property-mappings" array\'s element #{} must be a string'.format(index
))
1335 if prop_type
== 'clock':
1336 self
._set
_int
_clock
_prop
_mapping
(obj
, prop_mapping
)
1338 raise ConfigError('unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type
, index
))
1342 def _create_float(self
, obj
, node
):
1344 # create floating point number object
1345 obj
= metadata
.FloatingPoint()
1347 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1354 raise ConfigError('unknown floating point number type object property: "{}"'.format(unk_prop
))
1360 if not _is_assoc_array_prop(size
):
1361 raise ConfigError('"size" property of floating point number type object must be an associative array')
1363 unk_prop
= _get_first_unknown_prop(node
, ['exp', 'mant'])
1368 if not _is_int_prop(exp
):
1369 raise ConfigError('"exp" property of floating point number type object\'s "size" property must be an integer')
1372 raise ConfigError('invalid floating point number exponent size: {}')
1379 if not _is_int_prop(mant
):
1380 raise ConfigError('"mant" property of floating point number type object\'s "size" property must be an integer')
1383 raise ConfigError('invalid floating point number mantissa size: {}')
1385 obj
.mant_size
= mant
1389 align
= node
['align']
1391 if not _is_int_prop(align
):
1392 raise ConfigError('"align" property of floating point number type object must be an integer')
1394 if not _is_valid_alignment(align
):
1395 raise ConfigError('invalid alignment: {}'.format(align
))
1400 if 'byte-order' in node
:
1401 byte_order
= node
['byte-order']
1403 if not _is_str_prop(byte_order
):
1404 raise ConfigError('"byte-order" property of floating point number type object must be a string ("le" or "be")')
1406 byte_order
= _byte_order_str_to_bo(byte_order
)
1408 if byte_order
is None:
1409 raise ConfigError('invalid "byte-order" property in floating point number type object')
1411 byte_order
= self
._bo
1413 obj
.byte_order
= byte_order
1417 def _create_enum(self
, obj
, node
):
1419 # create enumeration object
1420 obj
= metadata
.Enum()
1422 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1428 raise ConfigError('unknown enumeration type object property: "{}"'.format(unk_prop
))
1431 if 'value-type' in node
:
1433 obj
.value_type
= self
._create
_type
(node
['value-type'])
1434 except Exception as e
:
1435 raise ConfigError('cannot create enumeration type\'s integer type', e
)
1438 if 'members' in node
:
1439 members_node
= node
['members']
1441 if not _is_array_prop(members_node
):
1442 raise ConfigError('"members" property of enumeration type object must be an array')
1446 for index
, m_node
in enumerate(members_node
):
1447 if not _is_str_prop(m_node
) and not _is_assoc_array_prop(m_node
):
1448 raise ConfigError('invalid enumeration member #{}: expecting a string or an associative array'.format(index
))
1450 if _is_str_prop(m_node
):
1455 if 'label' not in m_node
:
1456 raise ConfigError('missing "label" property in enumeration member #{}'.format(index
))
1458 label
= m_node
['label']
1460 if not _is_str_prop(label
):
1461 raise ConfigError('"label" property of enumeration member #{} must be a string'.format(index
))
1463 if 'value' not in m_node
:
1464 raise ConfigError('missing "value" property in enumeration member ("{}")'.format(label
))
1466 value
= m_node
['value']
1468 if not _is_int_prop(value
) and not _is_array_prop(value
):
1469 raise ConfigError('invalid enumeration member ("{}"): expecting an integer or an array'.format(label
))
1471 if _is_int_prop(value
):
1473 value
= (value
, value
)
1476 raise ConfigError('invalid enumeration member ("{}"): range must have exactly two items'.format(label
))
1482 raise ConfigError('invalid enumeration member ("{}"): invalid range ({} > {})'.format(label
, mn
, mx
))
1487 obj
.members
[label
] = value
1491 def _create_string(self
, obj
, node
):
1493 # create string object
1494 obj
= metadata
.String()
1496 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1501 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1504 if 'encoding' in node
:
1505 encoding
= node
['encoding']
1507 if not _is_str_prop(encoding
):
1508 raise ConfigError('"encoding" property of string type object must be a string ("none", "ascii", or "utf-8")')
1510 encoding
= _encoding_str_to_encoding(encoding
)
1512 if encoding
is None:
1513 raise ConfigError('invalid "encoding" property in string type object')
1515 obj
.encoding
= encoding
1519 def _create_struct(self
, obj
, node
):
1521 # create structure object
1522 obj
= metadata
.Struct()
1524 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1530 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1533 if 'min-align' in node
:
1534 min_align
= node
['min-align']
1536 if not _is_int_prop(min_align
):
1537 raise ConfigError('"min-align" property of structure type object must be an integer')
1539 if not _is_valid_alignment(min_align
):
1540 raise ConfigError('invalid minimum alignment: {}'.format(min_align
))
1542 obj
.min_align
= min_align
1545 if 'fields' in node
:
1546 fields
= node
['fields']
1548 if not _is_assoc_array_prop(fields
):
1549 raise ConfigError('"fields" property of structure type object must be an associative array')
1551 for field_name
, field_node
in fields
.items():
1552 if not is_valid_identifier(field_name
):
1553 raise ConfigError('"{}" is not a valid field name for structure type'.format(field_name
))
1556 obj
.fields
[field_name
] = self
._create
_type
(field_node
)
1557 except Exception as e
:
1558 raise ConfigError('cannot create structure type\'s field "{}"'.format(field_name
), e
)
1562 def _create_array(self
, obj
, node
):
1564 # create array object
1565 obj
= metadata
.Array()
1567 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1573 raise ConfigError('unknown array type object property: "{}"'.format(unk_prop
))
1576 if 'length' in node
:
1577 length
= node
['length']
1579 if not _is_int_prop(length
) and not _is_str_prop(length
):
1580 raise ConfigError('"length" property of array type object must be an integer or a string')
1582 if type(length
) is int and length
< 0:
1583 raise ConfigError('invalid static array length: {}'.format(length
))
1588 if 'element-type' in node
:
1590 obj
.element_type
= self
._create
_type
(node
['element-type'])
1591 except Exception as e
:
1592 raise ConfigError('cannot create array type\'s element type', e
)
1596 def _create_variant(self
, obj
, node
):
1598 # create variant object
1599 obj
= metadata
.Variant()
1601 unk_prop
= self
._get
_first
_unknown
_type
_prop
(node
, [
1607 raise ConfigError('unknown variant type object property: "{}"'.format(unk_prop
))
1613 if not _is_str_prop(tag
):
1614 raise ConfigError('"tag" property of variant type object must be a string')
1616 # do not validate variant tag for the moment; will be done in a
1622 types
= node
['types']
1624 if not _is_assoc_array_prop(types
):
1625 raise ConfigError('"types" property of variant type object must be an associative array')
1627 # do not validate type names for the moment; will be done in a
1629 for type_name
, type_node
in types
.items():
1630 if not is_valid_identifier(type_name
):
1631 raise ConfigError('"{}" is not a valid type name for variant type'.format(type_name
))
1634 obj
.types
[type_name
] = self
._create
_type
(type_node
)
1635 except Exception as e
:
1636 raise ConfigError('cannot create variant type\'s type "{}"'.format(type_name
), e
)
1640 def _create_type(self
, type_node
):
1641 if type(type_node
) is str:
1642 t
= self
._lookup
_type
_alias
(type_node
)
1645 raise ConfigError('unknown type alias "{}"'.format(type_node
))
1649 if not _is_assoc_array_prop(type_node
):
1650 raise ConfigError('type objects must be associative arrays')
1657 if self
._version
>= 200:
1658 if 'inherit' in type_node
:
1659 inherit_prop
= 'inherit'
1660 inherit_node
= type_node
[inherit_prop
]
1662 if self
._version
>= 201:
1663 if '$inherit' in type_node
:
1664 if inherit_node
is not None:
1665 raise ConfigError('cannot specify both "inherit" and "$inherit" properties of type object: prefer "$inherit"')
1667 inherit_prop
= '$inherit'
1668 inherit_node
= type_node
[inherit_prop
]
1670 if inherit_node
is not None and 'class' in type_node
:
1671 raise ConfigError('cannot specify both "{}" and "class" properties in type object'.format(inherit_prop
))
1673 if inherit_node
is not None:
1674 if not _is_str_prop(inherit_node
):
1675 raise ConfigError('"{}" property of type object must be a string'.format(inherit_prop
))
1677 base
= self
._lookup
_type
_alias
(inherit_node
)
1680 raise ConfigError('cannot inherit from type alias "{}": type alias does not exist at this point'.format(inherit_node
))
1682 func
= self
._type
_to
_create
_type
_func
[type(base
)]
1684 if 'class' not in type_node
:
1685 raise ConfigError('type objects which do not inherit must have a "class" property')
1687 class_name
= type_node
['class']
1689 if type(class_name
) is not str:
1690 raise ConfigError('type objects\' "class" property must be a string')
1692 if class_name
not in self
._class
_name
_to
_create
_type
_func
:
1693 raise ConfigError('unknown type class "{}"'.format(class_name
))
1696 func
= self
._class
_name
_to
_create
_type
_func
[class_name
]
1698 return func(base
, type_node
)
1700 def _register_type_aliases(self
, metadata_node
):
1703 if 'type-aliases' not in metadata_node
:
1706 ta_node
= metadata_node
['type-aliases']
1708 if not _is_assoc_array_prop(ta_node
):
1709 raise ConfigError('"type-aliases" property (metadata) must be an associative array')
1711 for ta_name
, ta_type
in ta_node
.items():
1712 if ta_name
in self
._tas
:
1713 raise ConfigError('duplicate type alias "{}"'.format(ta_name
))
1716 t
= self
._create
_type
(ta_type
)
1717 except Exception as e
:
1718 raise ConfigError('cannot create type alias "{}"'.format(ta_name
), e
)
1720 self
._tas
[ta_name
] = t
1722 def _create_clock(self
, node
):
1723 # create clock object
1724 clock
= metadata
.Clock()
1726 if not _is_assoc_array_prop(env_node
):
1727 raise ConfigError('clock objects must be associative arrays')
1739 if self
._version
>= 201:
1740 known_props
.append('$return-ctype')
1742 unk_prop
= _get_first_unknown_prop(node
, known_props
)
1745 raise ConfigError('unknown clock object property: "{}"'.format(unk_prop
))
1749 uuidp
= node
['uuid']
1751 if not _is_str_prop(uuidp
):
1752 raise ConfigError('"uuid" property of clock object must be a string')
1755 uuidp
= uuid
.UUID(uuidp
)
1757 raise ConfigError('malformed UUID (clock object): "{}"'.format(uuidp
))
1762 if 'description' in node
:
1763 desc
= node
['description']
1765 if not _is_str_prop(desc
):
1766 raise ConfigError('"description" property of clock object must be a string')
1768 clock
.description
= desc
1774 if not _is_int_prop(freq
):
1775 raise ConfigError('"freq" property of clock object must be an integer')
1778 raise ConfigError('invalid clock frequency: {}'.format(freq
))
1783 if 'error-cycles' in node
:
1784 error_cycles
= node
['error-cycles']
1786 if not _is_int_prop(error_cycles
):
1787 raise ConfigError('"error-cycles" property of clock object must be an integer')
1789 if error_cycles
< 0:
1790 raise ConfigError('invalid clock error cycles: {}'.format(error_cycles
))
1792 clock
.error_cycles
= error_cycles
1795 if 'offset' in node
:
1796 offset
= node
['offset']
1798 if not _is_assoc_array_prop(offset
):
1799 raise ConfigError('"offset" property of clock object must be an associative array')
1801 unk_prop
= _get_first_unknown_prop(offset
, ['cycles', 'seconds'])
1804 raise ConfigError('unknown clock object\'s offset property: "{}"'.format(unk_prop
))
1807 if 'cycles' in offset
:
1808 offset_cycles
= offset
['cycles']
1810 if not _is_int_prop(offset_cycles
):
1811 raise ConfigError('"cycles" property of clock object\'s offset property must be an integer')
1813 if offset_cycles
< 0:
1814 raise ConfigError('invalid clock offset cycles: {}'.format(offset_cycles
))
1816 clock
.offset_cycles
= offset_cycles
1819 if 'seconds' in offset
:
1820 offset_seconds
= offset
['seconds']
1822 if not _is_int_prop(offset_seconds
):
1823 raise ConfigError('"seconds" property of clock object\'s offset property must be an integer')
1825 if offset_seconds
< 0:
1826 raise ConfigError('invalid clock offset seconds: {}'.format(offset_seconds
))
1828 clock
.offset_seconds
= offset_seconds
1831 if 'absolute' in node
:
1832 absolute
= node
['absolute']
1834 if not _is_bool_prop(absolute
):
1835 raise ConfigError('"absolute" property of clock object must be a boolean')
1837 clock
.absolute
= absolute
1840 # v2.0: "return-ctype"
1841 # v2.1+: "$return-ctype"
1842 return_ctype_node
= None
1844 if self
._version
>= 200:
1845 if 'return-ctype' in node
:
1846 return_ctype_prop
= 'return-ctype'
1847 return_ctype_node
= node
[return_ctype_prop
]
1849 if self
._version
>= 201:
1850 if '$return-ctype' in node
:
1851 if return_ctype_node
is not None:
1852 raise ConfigError('cannot specify both "return-ctype" and "$return-ctype" properties of clock object: prefer "$return-ctype"')
1854 return_ctype_prop
= '$return-ctype'
1855 return_ctype_node
= node
[return_ctype_prop
]
1857 if return_ctype_node
is not None:
1858 if not _is_str_prop(return_ctype_node
):
1859 raise ConfigError('"{}" property of clock object must be a string'.format(return_ctype_prop
))
1861 clock
.return_ctype
= return_ctype_node
1865 def _register_clocks(self
, metadata_node
):
1866 self
._clocks
= collections
.OrderedDict()
1868 if 'clocks' not in metadata_node
:
1871 clocks_node
= metadata_node
['clocks']
1873 if not _is_assoc_array_prop(clocks_node
):
1874 raise ConfigError('"clocks" property (metadata) must be an associative array')
1876 for clock_name
, clock_node
in clocks_node
.items():
1877 if not is_valid_identifier(clock_name
):
1878 raise ConfigError('invalid clock name: "{}"'.format(clock_name
))
1880 if clock_name
in self
._clocks
:
1881 raise ConfigError('duplicate clock "{}"'.format(clock_name
))
1884 clock
= self
._create
_clock
(clock_node
)
1885 except Exception as e
:
1886 raise ConfigError('cannot create clock "{}"'.format(clock_name
), e
)
1888 clock
.name
= clock_name
1889 self
._clocks
[clock_name
] = clock
1891 def _create_env(self
, metadata_node
):
1892 env
= collections
.OrderedDict()
1894 if 'env' not in metadata_node
:
1897 env_node
= metadata_node
['env']
1899 if not _is_assoc_array_prop(env_node
):
1900 raise ConfigError('"env" property (metadata) must be an associative array')
1902 for env_name
, env_value
in env_node
.items():
1904 raise ConfigError('duplicate environment variable "{}"'.format(env_name
))
1906 if not is_valid_identifier(env_name
):
1907 raise ConfigError('invalid environment variable name: "{}"'.format(env_name
))
1909 if not _is_int_prop(env_value
) and not _is_str_prop(env_value
):
1910 raise ConfigError('invalid environment variable value ("{}"): expecting integer or string'.format(env_name
))
1912 env
[env_name
] = env_value
1916 def _register_log_levels(self
, metadata_node
):
1917 self
._log
_levels
= dict()
1919 if 'log-levels' not in metadata_node
:
1922 log_levels_node
= metadata_node
['log-levels']
1924 if not _is_assoc_array_prop(log_levels_node
):
1925 raise ConfigError('"log-levels" property (metadata) must be an associative array')
1927 for ll_name
, ll_value
in log_levels_node
.items():
1928 if ll_name
in self
._log
_levels
:
1929 raise ConfigError('duplicate log level entry "{}"'.format(ll_name
))
1931 if not _is_int_prop(ll_value
):
1932 raise ConfigError('invalid log level entry ("{}"): expecting an integer'.format(ll_name
))
1934 self
._log
_levels
[ll_name
] = ll_value
1936 def _create_trace(self
, metadata_node
):
1937 # create trace object
1938 trace
= metadata
.Trace()
1939 trace_node
= metadata_node
['trace']
1941 if not _is_assoc_array_prop(trace_node
):
1942 raise ConfigError('"trace" property (metadata) must be an associative array')
1944 unk_prop
= _get_first_unknown_prop(trace_node
, [
1947 'packet-header-type',
1951 raise ConfigError('unknown trace object property: "{}"'.format(unk_prop
))
1953 # set byte order (already parsed)
1954 trace
.byte_order
= self
._bo
1957 if 'uuid' in trace_node
:
1958 uuidp
= trace_node
['uuid']
1960 if not _is_str_prop(uuidp
):
1961 raise ConfigError('"uuid" property of trace object must be a string')
1964 uuidp
= uuid
.uuid1()
1967 uuidp
= uuid
.UUID(uuidp
)
1969 raise ConfigError('malformed UUID (trace object): "{}"'.format(uuidp
))
1973 # packet header type
1974 if 'packet-header-type' in trace_node
:
1976 ph_type
= self
._create
_type
(trace_node
['packet-header-type'])
1977 except Exception as e
:
1978 raise ConfigError('cannot create packet header type (trace)', e
)
1980 trace
.packet_header_type
= ph_type
1984 def _lookup_log_level(self
, ll
):
1985 if _is_int_prop(ll
):
1987 elif _is_str_prop(ll
) and ll
in self
._log
_levels
:
1988 return self
._log
_levels
[ll
]
1990 def _create_event(self
, event_node
):
1991 event
= metadata
.Event()
1993 if not _is_assoc_array_prop(event_node
):
1994 raise ConfigError('event objects must be associative arrays')
1996 unk_prop
= _get_first_unknown_prop(event_node
, [
2003 raise ConfigError('unknown event object property: "{}"'.format(unk_prop
))
2005 if 'log-level' in event_node
:
2006 ll
= self
._lookup
_log
_level
(event_node
['log-level'])
2009 raise ConfigError('invalid "log-level" property')
2011 event
.log_level
= ll
2013 if 'context-type' in event_node
:
2015 t
= self
._create
_type
(event_node
['context-type'])
2016 except Exception as e
:
2017 raise ConfigError('cannot create event\'s context type object', e
)
2019 event
.context_type
= t
2021 if 'payload-type' not in event_node
:
2022 raise ConfigError('missing "payload-type" property in event object')
2025 t
= self
._create
_type
(event_node
['payload-type'])
2026 except Exception as e
:
2027 raise ConfigError('cannot create event\'s payload type object', e
)
2029 event
.payload_type
= t
2033 def _create_stream(self
, stream_node
):
2034 stream
= metadata
.Stream()
2036 if not _is_assoc_array_prop(stream_node
):
2037 raise ConfigError('stream objects must be associative arrays')
2039 unk_prop
= _get_first_unknown_prop(stream_node
, [
2040 'packet-context-type',
2041 'event-header-type',
2042 'event-context-type',
2047 raise ConfigError('unknown stream object property: "{}"'.format(unk_prop
))
2049 if 'packet-context-type' in stream_node
:
2051 t
= self
._create
_type
(stream_node
['packet-context-type'])
2052 except Exception as e
:
2053 raise ConfigError('cannot create stream\'s packet context type object', e
)
2055 stream
.packet_context_type
= t
2057 if 'event-header-type' in stream_node
:
2059 t
= self
._create
_type
(stream_node
['event-header-type'])
2060 except Exception as e
:
2061 raise ConfigError('cannot create stream\'s event header type object', e
)
2063 stream
.event_header_type
= t
2065 if 'event-context-type' in stream_node
:
2067 t
= self
._create
_type
(stream_node
['event-context-type'])
2068 except Exception as e
:
2069 raise ConfigError('cannot create stream\'s event context type object', e
)
2071 stream
.event_context_type
= t
2073 if 'events' not in stream_node
:
2074 raise ConfigError('missing "events" property in stream object')
2076 events
= stream_node
['events']
2078 if not _is_assoc_array_prop(events
):
2079 raise ConfigError('"events" property of stream object must be an associative array')
2082 raise ConfigError('at least one event is needed within a stream object')
2086 for ev_name
, ev_node
in events
.items():
2088 ev
= self
._create
_event
(ev_node
)
2089 except Exception as e
:
2090 raise ConfigError('cannot create event "{}"'.format(ev_name
), e
)
2094 stream
.events
[ev_name
] = ev
2099 def _create_streams(self
, metadata_node
):
2100 streams
= collections
.OrderedDict()
2102 if 'streams' not in metadata_node
:
2103 raise ConfigError('missing "streams" property (metadata)')
2105 streams_node
= metadata_node
['streams']
2107 if not _is_assoc_array_prop(streams_node
):
2108 raise ConfigError('"streams" property (metadata) must be an associative array')
2110 if not streams_node
:
2111 raise ConfigError('at least one stream is needed (metadata)')
2115 for stream_name
, stream_node
in streams_node
.items():
2117 stream
= self
._create
_stream
(stream_node
)
2118 except Exception as e
:
2119 raise ConfigError('cannot create stream "{}"'.format(stream_name
), e
)
2122 stream
.name
= str(stream_name
)
2123 streams
[stream_name
] = stream
2128 def _create_metadata(self
, root
):
2129 meta
= metadata
.Metadata()
2131 if 'metadata' not in root
:
2132 raise ConfigError('missing "metadata" property (root)')
2134 if not _is_assoc_array_prop(metadata_node
):
2135 raise ConfigError('"metadata" property (root) must be an associative array')
2137 metadata_node
= root
['metadata']
2138 unk_prop
= _get_first_unknown_prop(metadata_node
, [
2148 raise ConfigError('unknown metadata property: "{}"'.format(unk_prop
))
2150 self
._set
_byte
_order
(metadata_node
)
2151 self
._register
_clocks
(metadata_node
)
2152 meta
.clocks
= self
._clocks
2153 self
._register
_type
_aliases
(metadata_node
)
2154 meta
.env
= self
._create
_env
(metadata_node
)
2155 meta
.trace
= self
._create
_trace
(metadata_node
)
2156 self
._register
_log
_levels
(metadata_node
)
2157 meta
.streams
= self
._create
_streams
(metadata_node
)
2161 def _get_version(self
, root
):
2162 if 'version' not in root
:
2163 raise ConfigError('missing "version" property (root)')
2165 version_node
= root
['version']
2167 if not _is_str_prop(version_node
):
2168 raise ConfigError('"version" property (root) must be a string')
2170 version_node
= version_node
.strip()
2172 if version_node
not in ['2.0', '2.1']:
2173 raise ConfigError('unsupported version ({}): versions 2.0 and 2.1 are supported'.format(version_node
))
2175 # convert version string to comparable version integer
2176 parts
= version_node
.split('.')
2177 version
= int(parts
[0]) * 100 + int(parts
[1])
2181 def _get_prefix(self
, root
):
2182 if 'prefix' not in root
:
2185 prefix_node
= root
['prefix']
2187 if not _is_str_prop(prefix_node
):
2188 raise ConfigError('"prefix" property (root) must be a string')
2190 if not is_valid_identifier(prefix_node
):
2191 raise ConfigError('"prefix" property (root) must be a valid C identifier')
2195 def _yaml_ordered_load(self
, stream
):
2196 class OLoader(yaml
.Loader
):
2199 def construct_mapping(loader
, node
):
2200 loader
.flatten_mapping(node
)
2202 return collections
.OrderedDict(loader
.construct_pairs(node
))
2204 OLoader
.add_constructor(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2207 return yaml
.load(stream
, OLoader
)
2209 def parse(self
, yml
):
2211 root
= self
._yaml
_ordered
_load
(yml
)
2212 except Exception as e
:
2213 raise ConfigError('cannot parse YAML input', e
)
2215 if not _is_assoc_array_prop(root
):
2216 raise ConfigError('root must be an associative array')
2218 self
._version
= self
._get
_version
(root
)
2219 meta
= self
._create
_metadata
(root
)
2220 prefix
= self
._get
_prefix
(root
)
2222 return Config(self
._version
, prefix
, meta
)
2226 parser
= _YamlConfigParser()
2227 cfg
= parser
.parse(yml
)
2232 def from_yaml_file(path
):
2234 with
open(path
) as f
:
2235 return from_yaml(f
.read())
2236 except Exception as e
:
2237 raise ConfigError('cannot create configuration from YAML file'.format(e
), e
)