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 def _get_first_unknown_type_prop(type_node
, known_props
):
191 kp
= known_props
+ ['inherit', 'class']
193 return _get_first_unknown_prop(type_node
, kp
)
196 # This validator validates the configured metadata for barectf specific
201 # * all header/contexts are at least byte-aligned
202 # * all integer and floating point number sizes to be <= 64
203 # * no inner structures, arrays, or variants
204 class _BarectfMetadataValidator
:
206 self
._type
_to
_validate
_type
_func
= {
207 metadata
.Integer
: self
._validate
_int
_type
,
208 metadata
.FloatingPoint
: self
._validate
_float
_type
,
209 metadata
.Enum
: self
._validate
_enum
_type
,
210 metadata
.String
: self
._validate
_string
_type
,
211 metadata
.Struct
: self
._validate
_struct
_type
,
212 metadata
.Array
: self
._validate
_array
_type
,
213 metadata
.Variant
: self
._validate
_variant
_type
,
216 def _validate_int_type(self
, t
, entity_root
):
218 raise ConfigError('integer type\'s size must be lesser than or equal to 64 bits')
220 def _validate_float_type(self
, t
, entity_root
):
222 raise ConfigError('floating point number type\'s size must be lesser than or equal to 64 bits')
224 def _validate_enum_type(self
, t
, entity_root
):
225 if t
.value_type
.size
> 64:
226 raise ConfigError('enumeration type\'s integer type\'s size must be lesser than or equal to 64 bits')
228 def _validate_string_type(self
, t
, entity_root
):
231 def _validate_struct_type(self
, t
, entity_root
):
233 raise ConfigError('inner structure types are not supported as of this version')
235 for field_name
, field_type
in t
.fields
.items():
236 if entity_root
and self
._cur
_entity
is _Entity
.TRACE_PACKET_HEADER
:
237 if field_name
== 'uuid':
242 self
._validate
_type
(field_type
, False)
243 except Exception as e
:
244 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
246 def _validate_array_type(self
, t
, entity_root
):
247 raise ConfigError('array types are not supported as of this version')
249 def _validate_variant_type(self
, t
, entity_root
):
250 raise ConfigError('variant types are not supported as of this version')
252 def _validate_type(self
, t
, entity_root
):
253 self
._type
_to
_validate
_type
_func
[type(t
)](t
, entity_root
)
255 def _validate_entity(self
, t
):
259 # make sure entity is byte-aligned
261 raise ConfigError('type\'s alignment must be at least byte-aligned')
263 # make sure entity is a structure
264 if type(t
) is not metadata
.Struct
:
265 raise ConfigError('expecting a structure type')
268 self
._validate
_type
(t
, True)
270 def _validate_entities_and_names(self
, meta
):
271 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
274 self
._validate
_entity
(meta
.trace
.packet_header_type
)
275 except Exception as e
:
276 raise ConfigError('invalid trace packet header type', e
)
278 for stream_name
, stream
in meta
.streams
.items():
279 if not is_valid_identifier(stream_name
):
280 raise ConfigError('stream name "{}" is not a valid C identifier'.format(stream_name
))
282 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
285 self
._validate
_entity
(stream
.packet_context_type
)
286 except Exception as e
:
287 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
289 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
292 self
._validate
_entity
(stream
.event_header_type
)
293 except Exception as e
:
294 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
296 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
299 self
._validate
_entity
(stream
.event_context_type
)
300 except Exception as e
:
301 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
304 for ev_name
, ev
in stream
.events
.items():
305 if not is_valid_identifier(ev_name
):
306 raise ConfigError('event name "{}" is not a valid C identifier'.format(ev_name
))
308 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
311 self
._validate
_entity
(ev
.context_type
)
312 except Exception as e
:
313 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
315 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
317 if ev
.payload_type
is None:
318 raise ConfigError('missing payload type in event "{}"'.format(ev_name
), e
)
321 self
._validate
_entity
(ev
.payload_type
)
322 except Exception as e
:
323 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
325 if not ev
.payload_type
.fields
:
326 raise ConfigError('empty payload type in event "{}"'.format(ev_name
), e
)
327 except Exception as e
:
328 raise ConfigError('invalid stream "{}"'.format(stream_name
), e
)
330 def validate(self
, meta
):
331 self
._validate
_entities
_and
_names
(meta
)
334 # This validator validates special fields of trace, stream, and event
335 # types. For example, if checks that the "stream_id" field exists in the
336 # trace packet header if there's more than one stream, and much more.
337 class _MetadataSpecialFieldsValidator
:
338 def _validate_trace_packet_header_type(self
, t
):
339 # needs "stream_id" field?
340 if len(self
._meta
.streams
) > 1:
343 raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is missing')
345 if type(t
) is not metadata
.Struct
:
346 raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is not a structure type')
348 if 'stream_id' not in t
.fields
:
349 raise ConfigError('need "stream_id" field in trace packet header type')
351 # validate "magic" and "stream_id" types
352 if type(t
) is not metadata
.Struct
:
355 for i
, (field_name
, field_type
) in enumerate(t
.fields
.items()):
356 if field_name
== 'magic':
357 if type(field_type
) is not metadata
.Integer
:
358 raise ConfigError('"magic" field in trace packet header type must be an integer type')
360 if field_type
.signed
or field_type
.size
!= 32:
361 raise ConfigError('"magic" field in trace packet header type must be a 32-bit unsigned integer type')
364 raise ConfigError('"magic" field must be the first trace packet header type\'s field')
365 elif field_name
== 'stream_id':
366 if type(field_type
) is not metadata
.Integer
:
367 raise ConfigError('"stream_id" field in trace packet header type must be an integer type')
369 if field_type
.signed
:
370 raise ConfigError('"stream_id" field in trace packet header type must be an unsigned integer type')
371 elif field_name
== 'uuid':
372 if self
._meta
.trace
.uuid
is None:
373 raise ConfigError('"uuid" field in trace packet header type specified, but no trace UUID provided')
375 if type(field_type
) is not metadata
.Array
:
376 raise ConfigError('"uuid" field in trace packet header type must be an array')
378 if field_type
.length
!= 16:
379 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
381 element_type
= field_type
.element_type
383 if type(element_type
) is not metadata
.Integer
:
384 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
386 if element_type
.size
!= 8:
387 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
389 if element_type
.align
!= 8:
390 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 byte-aligned bytes')
392 def _validate_trace(self
, meta
):
393 self
._validate
_trace
_packet
_header
_type
(meta
.trace
.packet_header_type
)
395 def _validate_stream_packet_context(self
, stream
):
396 t
= stream
.packet_context_type
401 if type(t
) is not metadata
.Struct
:
404 # "timestamp_begin", if exists, is an unsigned integer type,
406 if 'timestamp_begin' in t
.fields
:
407 ts_begin
= t
.fields
['timestamp_begin']
409 if type(ts_begin
) is not metadata
.Integer
:
410 raise ConfigError('"timestamp_begin" field in stream packet context type must be an integer type')
413 raise ConfigError('"timestamp_begin" field in stream packet context type must be an unsigned integer type')
415 if not ts_begin
.property_mappings
:
416 raise ConfigError('"timestamp_begin" field in stream packet context type must be mapped to a clock')
418 # "timestamp_end", if exists, is an unsigned integer type,
420 if 'timestamp_end' in t
.fields
:
421 ts_end
= t
.fields
['timestamp_end']
423 if type(ts_end
) is not metadata
.Integer
:
424 raise ConfigError('"timestamp_end" field in stream packet context type must be an integer type')
427 raise ConfigError('"timestamp_end" field in stream packet context type must be an unsigned integer type')
429 if not ts_end
.property_mappings
:
430 raise ConfigError('"timestamp_end" field in stream packet context type must be mapped to a clock')
432 # "timestamp_begin" and "timestamp_end" exist together
433 if (('timestamp_begin' in t
.fields
) ^
('timestamp_end' in t
.fields
)):
434 raise ConfigError('"timestamp_begin" and "timestamp_end" fields must be defined together in stream packet context type')
436 # "events_discarded", if exists, is an unsigned integer type
437 if 'events_discarded' in t
.fields
:
438 events_discarded
= t
.fields
['events_discarded']
440 if type(events_discarded
) is not metadata
.Integer
:
441 raise ConfigError('"events_discarded" field in stream packet context type must be an integer type')
443 if events_discarded
.signed
:
444 raise ConfigError('"events_discarded" field in stream packet context type must be an unsigned integer type')
446 # "packet_size" and "content_size" must exist
447 if 'packet_size' not in t
.fields
:
448 raise ConfigError('missing "packet_size" field in stream packet context type')
450 packet_size
= t
.fields
['packet_size']
452 # "content_size" and "content_size" must exist
453 if 'content_size' not in t
.fields
:
454 raise ConfigError('missing "content_size" field in stream packet context type')
456 content_size
= t
.fields
['content_size']
458 # "packet_size" is an unsigned integer type
459 if type(packet_size
) is not metadata
.Integer
:
460 raise ConfigError('"packet_size" field in stream packet context type must be an integer type')
462 if packet_size
.signed
:
463 raise ConfigError('"packet_size" field in stream packet context type must be an unsigned integer type')
465 # "content_size" is an unsigned integer type
466 if type(content_size
) is not metadata
.Integer
:
467 raise ConfigError('"content_size" field in stream packet context type must be an integer type')
469 if content_size
.signed
:
470 raise ConfigError('"content_size" field in stream packet context type must be an unsigned integer type')
472 def _validate_stream_event_header(self
, stream
):
473 t
= stream
.event_header_type
476 if len(stream
.events
) > 1:
479 raise ConfigError('need "id" field in stream event header type, but stream event header type is missing')
481 if type(t
) is not metadata
.Struct
:
482 raise ConfigError('need "id" field in stream event header type, but stream event header type is not a structure type')
484 if 'id' not in t
.fields
:
485 raise ConfigError('need "id" field in stream event header type')
487 # validate "id" and "timestamp" types
488 if type(t
) is not metadata
.Struct
:
491 # "timestamp", if exists, is an unsigned integer type,
493 if 'timestamp' in t
.fields
:
494 ts
= t
.fields
['timestamp']
496 if type(ts
) is not metadata
.Integer
:
497 raise ConfigError('"ts" field in stream event header type must be an integer type')
500 raise ConfigError('"ts" field in stream event header type must be an unsigned integer type')
502 if not ts
.property_mappings
:
503 raise ConfigError('"ts" field in stream event header type must be mapped to a clock')
505 # "id" is an unsigned integer type
509 if type(eid
) is not metadata
.Integer
:
510 raise ConfigError('"id" field in stream event header type must be an integer type')
513 raise ConfigError('"id" field in stream event header type must be an unsigned integer type')
515 def _validate_stream(self
, stream
):
516 self
._validate
_stream
_packet
_context
(stream
)
517 self
._validate
_stream
_event
_header
(stream
)
519 def validate(self
, meta
):
521 self
._validate
_trace
(meta
)
523 for stream
in meta
.streams
.values():
525 self
._validate
_stream
(stream
)
526 except Exception as e
:
527 raise ConfigError('invalid stream "{}"'.format(stream
.name
), e
)
530 class _MetadataDynamicTypesValidatorStackEntry
:
531 def __init__(self
, base_t
):
532 self
._base
_t
= base_t
540 def index(self
, value
):
548 def base_t(self
, value
):
552 # Entities. Order of values is important here.
554 class _Entity(enum
.IntEnum
):
555 TRACE_PACKET_HEADER
= 0
556 STREAM_PACKET_CONTEXT
= 1
557 STREAM_EVENT_HEADER
= 2
558 STREAM_EVENT_CONTEXT
= 3
563 # This validator validates dynamic metadata types, that is, it ensures
564 # variable-length array lengths and variant tags actually point to
565 # something that exists. It also checks that variable-length array
566 # lengths point to integer types and variant tags to enumeration types.
567 class _MetadataDynamicTypesValidator
:
569 self
._type
_to
_visit
_type
_func
= {
570 metadata
.Integer
: None,
571 metadata
.FloatingPoint
: None,
573 metadata
.String
: None,
574 metadata
.Struct
: self
._visit
_struct
_type
,
575 metadata
.Array
: self
._visit
_array
_type
,
576 metadata
.Variant
: self
._visit
_variant
_type
,
579 self
._cur
_trace
= None
580 self
._cur
_stream
= None
581 self
._cur
_event
= None
583 def _lookup_path_from_base(self
, path
, parts
, base
, start_index
,
584 base_is_current
, from_t
):
589 while index
< len(parts
):
593 if type(cur_t
) is metadata
.Struct
:
594 enumerated_items
= enumerate(cur_t
.fields
.items())
597 for i
, (field_name
, field_type
) in enumerated_items
:
598 if field_name
== part
:
600 found_path
.append((i
, field_type
))
603 raise ConfigError('invalid path "{}": cannot find field "{}" in structure type'.format(path
, part
))
604 elif type(cur_t
) is metadata
.Variant
:
605 enumerated_items
= enumerate(cur_t
.types
.items())
608 for i
, (type_name
, type_type
) in enumerated_items
:
609 if type_name
== part
:
611 found_path
.append((i
, type_type
))
614 raise ConfigError('invalid path "{}": cannot find type "{}" in variant type'.format(path
, part
))
616 raise ConfigError('invalid path "{}": requesting "{}" in a non-variant, non-structure type'.format(path
, part
))
621 # make sure that the pointed type is not the pointing type
623 raise ConfigError('invalid path "{}": pointing to self'.format(path
))
625 # if we're here, we found the type; however, it could be located
626 # _after_ the variant/VLA looking for it, if the pointing
627 # and pointed types are in the same entity, so compare the
628 # current stack entries indexes to our index path in that case
629 if not base_is_current
:
632 for index
, entry
in enumerate(self
._stack
):
633 if index
== len(found_path
):
634 # end of index path; valid so far
637 if found_path
[index
][0] > entry
.index
:
638 raise ConfigError('invalid path "{}": pointed type is after pointing type'.format(path
))
640 # also make sure that both pointed and pointing types share
641 # a common structure ancestor
642 for index
, entry
in enumerate(self
._stack
):
643 if index
== len(found_path
):
646 if entry
.base_t
is not found_path
[index
][1]:
647 # found common ancestor
648 if type(entry
.base_t
) is metadata
.Variant
:
649 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
653 def _lookup_path_from_top(self
, path
, parts
):
655 raise ConfigError('invalid path "{}": multipart relative path not supported'.format(path
))
658 index
= len(self
._stack
) - 1
661 # check stack entries in reversed order
662 for entry
in reversed(self
._stack
):
663 # structure base type
664 if type(entry
.base_t
) is metadata
.Struct
:
666 enumerated_items
= enumerate(entry
.base_t
.fields
.items())
668 # lookup each field, until the current visiting index is met
669 for i
, (field_name
, field_type
) in enumerated_items
:
673 if field_name
== find_name
:
677 elif type(entry
.base_t
) is metadata
.Variant
:
678 enumerated_items
= enumerate(entry
.base_t
.types
.items())
680 # lookup each type, until the current visiting index is met
681 for i
, (type_name
, type_type
) in enumerated_items
:
685 if type_name
== find_name
:
687 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path
))
691 # nothing returned here: cannot find type
692 raise ConfigError('invalid path "{}": cannot find type in current context'.format(path
))
694 def _lookup_path(self
, path
, from_t
):
695 parts
= path
.lower().split('.')
697 base_is_current
= False
700 if parts
[0] == 'trace':
701 if parts
[1] == 'packet' and parts
[2] == 'header':
702 # make sure packet header exists
703 if self
._cur
_trace
.packet_header_type
is None:
704 raise ConfigError('invalid path "{}": no defined trace packet header type'.format(path
))
706 base
= self
._cur
_trace
.packet_header_type
708 if self
._cur
_entity
== _Entity
.TRACE_PACKET_HEADER
:
709 base_is_current
= True
711 raise ConfigError('invalid path "{}": unknown names after "trace"'.format(path
))
712 elif parts
[0] == 'stream':
713 if parts
[1] == 'packet' and parts
[2] == 'context':
714 if self
._cur
_entity
< _Entity
.STREAM_PACKET_CONTEXT
:
715 raise ConfigError('invalid path "{}": cannot access stream packet context here'.format(path
))
717 if self
._cur
_stream
.packet_context_type
is None:
718 raise ConfigError('invalid path "{}": no defined stream packet context type'.format(path
))
720 base
= self
._cur
_stream
.packet_context_type
722 if self
._cur
_entity
== _Entity
.STREAM_PACKET_CONTEXT
:
723 base_is_current
= True
724 elif parts
[1] == 'event':
725 if parts
[2] == 'header':
726 if self
._cur
_entity
< _Entity
.STREAM_EVENT_HEADER
:
727 raise ConfigError('invalid path "{}": cannot access stream event header here'.format(path
))
729 if self
._cur
_stream
.event_header_type
is None:
730 raise ConfigError('invalid path "{}": no defined stream event header type'.format(path
))
732 base
= self
._cur
_stream
.event_header_type
734 if self
._cur
_entity
== _Entity
.STREAM_EVENT_HEADER
:
735 base_is_current
= True
736 elif parts
[2] == 'context':
737 if self
._cur
_entity
< _Entity
.STREAM_EVENT_CONTEXT
:
738 raise ConfigError('invalid path "{}": cannot access stream event context here'.format(path
))
740 if self
._cur
_stream
.event_context_type
is None:
741 raise ConfigError('invalid path "{}": no defined stream event context type'.format(path
))
743 base
= self
._cur
_stream
.event_context_type
745 if self
._cur
_entity
== _Entity
.STREAM_EVENT_CONTEXT
:
746 base_is_current
= True
748 raise ConfigError('invalid path "{}": unknown names after "stream.event"'.format(path
))
750 raise ConfigError('invalid path "{}": unknown names after "stream"'.format(path
))
755 if len(parts
) >= 2 and base
is None:
756 if parts
[0] == 'event':
757 if parts
[1] == 'context':
758 if self
._cur
_entity
< _Entity
.EVENT_CONTEXT
:
759 raise ConfigError('invalid path "{}": cannot access event context here'.format(path
))
761 if self
._cur
_event
.context_type
is None:
762 raise ConfigError('invalid path "{}": no defined event context type'.format(path
))
764 base
= self
._cur
_event
.context_type
766 if self
._cur
_entity
== _Entity
.EVENT_CONTEXT
:
767 base_is_current
= True
768 elif parts
[1] == 'payload' or parts
[1] == 'fields':
769 if self
._cur
_entity
< _Entity
.EVENT_PAYLOAD
:
770 raise ConfigError('invalid path "{}": cannot access event payload here'.format(path
))
772 if self
._cur
_event
.payload_type
is None:
773 raise ConfigError('invalid path "{}": no defined event payload type'.format(path
))
775 base
= self
._cur
_event
.payload_type
777 if self
._cur
_entity
== _Entity
.EVENT_PAYLOAD
:
778 base_is_current
= True
780 raise ConfigError('invalid path "{}": unknown names after "event"'.format(path
))
786 return self
._lookup
_path
_from
_base
(path
, parts
, base
, start_index
,
787 base_is_current
, from_t
)
789 return self
._lookup
_path
_from
_top
(path
, parts
)
791 def _stack_reset(self
):
794 def _stack_push(self
, base_t
):
795 entry
= _MetadataDynamicTypesValidatorStackEntry(base_t
)
796 self
._stack
.append(entry
)
798 def _stack_pop(self
):
801 def _stack_incr_index(self
):
802 self
._stack
[-1].index
+= 1
804 def _visit_struct_type(self
, t
):
807 for field_name
, field_type
in t
.fields
.items():
809 self
._visit
_type
(field_type
)
810 except Exception as e
:
811 raise ConfigError('in structure type\'s field "{}"'.format(field_name
), e
)
813 self
._stack
_incr
_index
()
817 def _visit_array_type(self
, t
):
821 length_type
= self
._lookup
_path
(t
.length
, t
)
822 except Exception as e
:
823 raise ConfigError('invalid array type\'s length', e
)
825 # make sure length type an unsigned integer
826 if type(length_type
) is not metadata
.Integer
:
827 raise ConfigError('array type\'s length does not point to an integer type')
829 if length_type
.signed
:
830 raise ConfigError('array type\'s length does not point to an unsigned integer type')
832 self
._visit
_type
(t
.element_type
)
834 def _visit_variant_type(self
, t
):
837 tag_type
= self
._lookup
_path
(t
.tag
, t
)
838 except Exception as e
:
839 raise ConfigError('invalid variant type\'s tag', e
)
841 # make sure tag type is an enumeration
842 if type(tag_type
) is not metadata
.Enum
:
843 raise ConfigError('variant type\'s tag does not point to an enumeration type')
845 # verify that each variant type's type exists as an enumeration member
846 for tag_name
in t
.types
.keys():
847 if tag_name
not in tag_type
.members
:
848 raise ConfigError('cannot find variant type\'s type "{}" in pointed tag type'.format(tag_name
))
852 for type_name
, type_type
in t
.types
.items():
854 self
._visit
_type
(type_type
)
855 except Exception as e
:
856 raise ConfigError('in variant type\'s type "{}"'.format(type_name
), e
)
858 self
._stack
_incr
_index
()
862 def _visit_type(self
, t
):
866 if type(t
) in self
._type
_to
_visit
_type
_func
:
867 func
= self
._type
_to
_visit
_type
_func
[type(t
)]
872 def _visit_event(self
, ev
):
878 # visit event context type
880 self
._cur
_entity
= _Entity
.EVENT_CONTEXT
883 self
._visit
_type
(ev
.context_type
)
884 except Exception as e
:
885 raise ConfigError('invalid context type in event "{}"'.format(ev_name
), e
)
887 # visit event payload type
889 self
._cur
_entity
= _Entity
.EVENT_PAYLOAD
892 self
._visit
_type
(ev
.payload_type
)
893 except Exception as e
:
894 raise ConfigError('invalid payload type in event "{}"'.format(ev_name
), e
)
896 def _visit_stream(self
, stream
):
897 stream_name
= stream
.name
900 self
._cur
_stream
= stream
902 # reset current event
903 self
._cur
_event
= None
905 # visit stream packet context type
907 self
._cur
_entity
= _Entity
.STREAM_PACKET_CONTEXT
910 self
._visit
_type
(stream
.packet_context_type
)
911 except Exception as e
:
912 raise ConfigError('invalid packet context type in stream "{}"'.format(stream_name
), e
)
914 # visit stream event header type
916 self
._cur
_entity
= _Entity
.STREAM_EVENT_HEADER
919 self
._visit
_type
(stream
.event_header_type
)
920 except Exception as e
:
921 raise ConfigError('invalid event header type in stream "{}"'.format(stream_name
), e
)
923 # visit stream event context type
925 self
._cur
_entity
= _Entity
.STREAM_EVENT_CONTEXT
928 self
._visit
_type
(stream
.event_context_type
)
929 except Exception as e
:
930 raise ConfigError('invalid event context type in stream "{}"'.format(stream_name
), e
)
933 for ev
in stream
.events
.values():
935 self
._visit
_event
(ev
)
936 except Exception as e
:
937 raise ConfigError('invalid stream "{}"'.format(stream_name
))
939 def validate(self
, meta
):
941 self
._cur
_trace
= meta
.trace
943 # visit trace packet header type
945 self
._cur
_entity
= _Entity
.TRACE_PACKET_HEADER
948 self
._visit
_type
(meta
.trace
.packet_header_type
)
949 except Exception as e
:
950 raise ConfigError('invalid packet header type in trace', e
)
953 for stream
in meta
.streams
.values():
954 self
._visit
_stream
(stream
)
957 # Since type inheritance allows types to be only partially defined at
958 # any place in the configuration, this validator validates that actual
959 # trace, stream, and event types are all complete and valid.
960 class _MetadataTypesHistologyValidator
:
962 self
._type
_to
_validate
_type
_histology
_func
= {
963 metadata
.Integer
: self
._validate
_integer
_histology
,
964 metadata
.FloatingPoint
: self
._validate
_float
_histology
,
965 metadata
.Enum
: self
._validate
_enum
_histology
,
966 metadata
.String
: self
._validate
_string
_histology
,
967 metadata
.Struct
: self
._validate
_struct
_histology
,
968 metadata
.Array
: self
._validate
_array
_histology
,
969 metadata
.Variant
: self
._validate
_variant
_histology
,
972 def _validate_integer_histology(self
, t
):
975 raise ConfigError('missing integer type\'s size')
977 def _validate_float_histology(self
, t
):
978 # exponent digits is set
979 if t
.exp_size
is None:
980 raise ConfigError('missing floating point number type\'s exponent size')
982 # mantissa digits is set
983 if t
.mant_size
is None:
984 raise ConfigError('missing floating point number type\'s mantissa size')
986 # exponent and mantissa sum is a multiple of 8
987 if (t
.exp_size
+ t
.mant_size
) % 8 != 0:
988 raise ConfigError('floating point number type\'s mantissa and exponent sizes sum must be a multiple of 8')
990 def _validate_enum_histology(self
, t
):
991 # integer type is set
992 if t
.value_type
is None:
993 raise ConfigError('missing enumeration type\'s integer type')
995 # there's at least one member
997 raise ConfigError('enumeration type needs at least one member')
999 # no overlapping values
1002 for label
, value
in t
.members
.items():
1004 if value
[0] <= rg
[1] and rg
[0] <= value
[1]:
1005 raise ConfigError('enumeration type\'s member "{}" overlaps another member'.format(label
))
1007 ranges
.append(value
)
1009 def _validate_string_histology(self
, t
):
1013 def _validate_struct_histology(self
, t
):
1014 # all fields are valid
1015 for field_name
, field_type
in t
.fields
.items():
1017 self
._validate
_type
_histology
(field_type
)
1018 except Exception as e
:
1019 raise ConfigError('invalid structure type\'s field "{}"'.format(field_name
), e
)
1021 def _validate_array_histology(self
, t
):
1023 if t
.length
is None:
1024 raise ConfigError('missing array type\'s length')
1026 # element type is set
1027 if t
.element_type
is None:
1028 raise ConfigError('missing array type\'s element type')
1030 # element type is valid
1032 self
._validate
_type
_histology
(t
.element_type
)
1033 except Exception as e
:
1034 raise ConfigError('invalid array type\'s element type', e
)
1036 def _validate_variant_histology(self
, t
):
1039 raise ConfigError('missing variant type\'s tag')
1041 # there's at least one type
1043 raise ConfigError('variant type needs at least one type')
1045 # all types are valid
1046 for type_name
, type_t
in t
.types
.items():
1048 self
._validate
_type
_histology
(type_t
)
1049 except Exception as e
:
1050 raise ConfigError('invalid variant type\'s type "{}"'.format(type_name
), e
)
1052 def _validate_type_histology(self
, t
):
1056 self
._type
_to
_validate
_type
_histology
_func
[type(t
)](t
)
1058 def _validate_entity_type_histology(self
, t
):
1062 # entity cannot be an array
1063 if type(t
) is metadata
.Array
:
1064 raise ConfigError('cannot use an array here')
1066 self
._validate
_type
_histology
(t
)
1068 def _validate_event_types_histology(self
, ev
):
1071 # validate event context type
1073 self
._validate
_entity
_type
_histology
(ev
.context_type
)
1074 except Exception as e
:
1075 raise ConfigError('invalid event context type for event "{}"'.format(ev_name
), e
)
1077 # validate event payload type
1078 if ev
.payload_type
is None:
1079 raise ConfigError('event payload type must exist in event "{}"'.format(ev_name
))
1081 # TODO: also check arrays, sequences, and variants
1082 if type(ev
.payload_type
) is metadata
.Struct
:
1083 if not ev
.payload_type
.fields
:
1084 raise ConfigError('event payload type must have at least one field for event "{}"'.format(ev_name
))
1087 self
._validate
_entity
_type
_histology
(ev
.payload_type
)
1088 except Exception as e
:
1089 raise ConfigError('invalid event payload type for event "{}"'.format(ev_name
), e
)
1091 def _validate_stream_types_histology(self
, stream
):
1092 stream_name
= stream
.name
1094 # validate stream packet context type
1096 self
._validate
_entity
_type
_histology
(stream
.packet_context_type
)
1097 except Exception as e
:
1098 raise ConfigError('invalid stream packet context type for stream "{}"'.format(stream_name
), e
)
1100 # validate stream event header type
1102 self
._validate
_entity
_type
_histology
(stream
.event_header_type
)
1103 except Exception as e
:
1104 raise ConfigError('invalid stream event header type for stream "{}"'.format(stream_name
), e
)
1106 # validate stream event context type
1108 self
._validate
_entity
_type
_histology
(stream
.event_context_type
)
1109 except Exception as e
:
1110 raise ConfigError('invalid stream event context type for stream "{}"'.format(stream_name
), e
)
1113 for ev
in stream
.events
.values():
1115 self
._validate
_event
_types
_histology
(ev
)
1116 except Exception as e
:
1117 raise ConfigError('invalid event in stream "{}"'.format(stream_name
), e
)
1119 def validate(self
, meta
):
1120 # validate trace packet header type
1122 self
._validate
_entity
_type
_histology
(meta
.trace
.packet_header_type
)
1123 except Exception as e
:
1124 raise ConfigError('invalid trace packet header type', e
)
1127 for stream
in meta
.streams
.values():
1128 self
._validate
_stream
_types
_histology
(stream
)
1131 class _YamlConfigParser
:
1133 self
._class
_name
_to
_create
_type
_func
= {
1134 'int': self
._create
_integer
,
1135 'integer': self
._create
_integer
,
1136 'flt': self
._create
_float
,
1137 'float': self
._create
_float
,
1138 'floating-point': self
._create
_float
,
1139 'enum': self
._create
_enum
,
1140 'enumeration': self
._create
_enum
,
1141 'str': self
._create
_string
,
1142 'string': self
._create
_string
,
1143 'struct': self
._create
_struct
,
1144 'structure': self
._create
_struct
,
1145 'array': self
._create
_array
,
1146 'var': self
._create
_variant
,
1147 'variant': self
._create
_variant
,
1149 self
._type
_to
_create
_type
_func
= {
1150 metadata
.Integer
: self
._create
_integer
,
1151 metadata
.FloatingPoint
: self
._create
_float
,
1152 metadata
.Enum
: self
._create
_enum
,
1153 metadata
.String
: self
._create
_string
,
1154 metadata
.Struct
: self
._create
_struct
,
1155 metadata
.Array
: self
._create
_array
,
1156 metadata
.Variant
: self
._create
_variant
,
1159 def _set_byte_order(self
, metadata_node
):
1160 if 'trace' not in metadata_node
:
1161 raise ConfigError('missing "trace" property (metadata)')
1163 trace_node
= metadata_node
['trace']
1165 if not _is_assoc_array_prop(trace_node
):
1166 raise ConfigError('"trace" property (metadata) must be an associative array')
1168 if 'byte-order' not in trace_node
:
1169 raise ConfigError('missing "byte-order" property (trace)')
1171 self
._bo
= _byte_order_str_to_bo(trace_node
['byte-order'])
1173 if self
._bo
is None:
1174 raise ConfigError('invalid "byte-order" property (trace): must be "le" or "be"')
1176 def _lookup_type_alias(self
, name
):
1177 if name
in self
._tas
:
1178 return copy
.deepcopy(self
._tas
[name
])
1180 def _set_int_clock_prop_mapping(self
, int_obj
, prop_mapping_node
):
1181 unk_prop
= _get_first_unknown_prop(prop_mapping_node
, ['type', 'name', 'property'])
1184 raise ConfigError('unknown property in integer type object\'s clock property mapping: "{}"'.format(unk_prop
))
1186 if 'name' not in prop_mapping_node
:
1187 raise ConfigError('missing "name" property in integer type object\'s clock property mapping')
1189 if 'property' not in prop_mapping_node
:
1190 raise ConfigError('missing "property" property in integer type object\'s clock property mapping')
1192 clock_name
= prop_mapping_node
['name']
1193 prop
= prop_mapping_node
['property']
1195 if not _is_str_prop(clock_name
):
1196 raise ConfigError('"name" property of integer type object\'s clock property mapping must be a string')
1198 if not _is_str_prop(prop
):
1199 raise ConfigError('"property" property of integer type object\'s clock property mapping must be a string')
1201 if clock_name
not in self
._clocks
:
1202 raise ConfigError('invalid clock name "{}" in integer type object\'s clock property mapping'.format(clock_name
))
1205 raise ConfigError('invalid "property" property in integer type object\'s clock property mapping: "{}"'.format(prop
))
1207 mapped_clock
= self
._clocks
[clock_name
]
1208 int_obj
.property_mappings
.append(metadata
.PropertyMapping(mapped_clock
, prop
))
1210 def _create_integer(self
, obj
, node
):
1212 # create integer object
1213 obj
= metadata
.Integer()
1215 unk_prop
= _get_first_unknown_type_prop(node
, [
1222 'property-mappings',
1226 raise ConfigError('unknown integer type object property: "{}"'.format(unk_prop
))
1232 if not _is_int_prop(size
):
1233 raise ConfigError('"size" property of integer type object must be an integer')
1236 raise ConfigError('invalid integer size: {}'.format(size
))
1242 align
= node
['align']
1244 if not _is_int_prop(align
):
1245 raise ConfigError('"align" property of integer type object must be an integer')
1247 if not _is_valid_alignment(align
):
1248 raise ConfigError('invalid alignment: {}'.format(align
))
1253 if 'signed' in node
:
1254 signed
= node
['signed']
1256 if not _is_bool_prop(signed
):
1257 raise ConfigError('"signed" property of integer type object must be a boolean')
1262 if 'byte-order' in node
:
1263 byte_order
= node
['byte-order']
1265 if not _is_str_prop(byte_order
):
1266 raise ConfigError('"byte-order" property of integer type object must be a string ("le" or "be")')
1268 byte_order
= _byte_order_str_to_bo(byte_order
)
1270 if byte_order
is None:
1271 raise ConfigError('invalid "byte-order" property in integer type object')
1273 byte_order
= self
._bo
1275 obj
.byte_order
= byte_order
1281 if not _is_str_prop(base
):
1282 raise ConfigError('"base" property of integer type object must be a string ("bin", "oct", "dec", or "hex")')
1296 if 'encoding' in node
:
1297 encoding
= node
['encoding']
1299 if not _is_str_prop(encoding
):
1300 raise ConfigError('"encoding" property of integer type object must be a string ("none", "ascii", or "utf-8")')
1302 encoding
= _encoding_str_to_encoding(encoding
)
1304 if encoding
is None:
1305 raise ConfigError('invalid "encoding" property in integer type object')
1307 obj
.encoding
= encoding
1310 if 'property-mappings' in node
:
1311 prop_mappings
= node
['property-mappings']
1313 if not _is_array_prop(prop_mappings
):
1314 raise ConfigError('"property-mappings" property of integer type object must be an array')
1316 if len(prop_mappings
) > 1:
1317 raise ConfigError('length of "property-mappings" array in integer type object must be 1')
1319 del obj
.property_mappings
[:]
1321 for index
, prop_mapping
in enumerate(prop_mappings
):
1322 if not _is_assoc_array_prop(prop_mapping
):
1323 raise ConfigError('elements of "property-mappings" property of integer type object must be associative arrays')
1325 if 'type' not in prop_mapping
:
1326 raise ConfigError('missing "type" property in integer type object\'s "property-mappings" array\'s element #{}'.format(index
))
1328 prop_type
= prop_mapping
['type']
1330 if not _is_str_prop(prop_type
):
1331 raise ConfigError('"type" property of integer type object\'s "property-mappings" array\'s element #{} must be a string'.format(index
))
1333 if prop_type
== 'clock':
1334 self
._set
_int
_clock
_prop
_mapping
(obj
, prop_mapping
)
1336 raise ConfigError('unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type
, index
))
1340 def _create_float(self
, obj
, node
):
1342 # create floating point number object
1343 obj
= metadata
.FloatingPoint()
1345 unk_prop
= _get_first_unknown_type_prop(node
, [
1352 raise ConfigError('unknown floating point number type object property: "{}"'.format(unk_prop
))
1358 if not _is_assoc_array_prop(size
):
1359 raise ConfigError('"size" property of floating point number type object must be an associative array')
1361 unk_prop
= _get_first_unknown_prop(node
, ['exp', 'mant'])
1366 if not _is_int_prop(exp
):
1367 raise ConfigError('"exp" property of floating point number type object\'s "size" property must be an integer')
1370 raise ConfigError('invalid floating point number exponent size: {}')
1377 if not _is_int_prop(mant
):
1378 raise ConfigError('"mant" property of floating point number type object\'s "size" property must be an integer')
1381 raise ConfigError('invalid floating point number mantissa size: {}')
1383 obj
.mant_size
= mant
1387 align
= node
['align']
1389 if not _is_int_prop(align
):
1390 raise ConfigError('"align" property of floating point number type object must be an integer')
1392 if not _is_valid_alignment(align
):
1393 raise ConfigError('invalid alignment: {}'.format(align
))
1398 if 'byte-order' in node
:
1399 byte_order
= node
['byte-order']
1401 if not _is_str_prop(byte_order
):
1402 raise ConfigError('"byte-order" property of floating point number type object must be a string ("le" or "be")')
1404 byte_order
= _byte_order_str_to_bo(byte_order
)
1406 if byte_order
is None:
1407 raise ConfigError('invalid "byte-order" property in floating point number type object')
1409 byte_order
= self
._bo
1411 obj
.byte_order
= byte_order
1415 def _create_enum(self
, obj
, node
):
1417 # create enumeration object
1418 obj
= metadata
.Enum()
1420 unk_prop
= _get_first_unknown_type_prop(node
, [
1426 raise ConfigError('unknown enumeration type object property: "{}"'.format(unk_prop
))
1429 if 'value-type' in node
:
1431 obj
.value_type
= self
._create
_type
(node
['value-type'])
1432 except Exception as e
:
1433 raise ConfigError('cannot create enumeration type\'s integer type', e
)
1436 if 'members' in node
:
1437 members_node
= node
['members']
1439 if not _is_array_prop(members_node
):
1440 raise ConfigError('"members" property of enumeration type object must be an array')
1444 for index
, m_node
in enumerate(members_node
):
1445 if not _is_str_prop(m_node
) and not _is_assoc_array_prop(m_node
):
1446 raise ConfigError('invalid enumeration member #{}: expecting a string or an associative array'.format(index
))
1448 if _is_str_prop(m_node
):
1453 if 'label' not in m_node
:
1454 raise ConfigError('missing "label" property in enumeration member #{}'.format(index
))
1456 label
= m_node
['label']
1458 if not _is_str_prop(label
):
1459 raise ConfigError('"label" property of enumeration member #{} must be a string'.format(index
))
1461 if 'value' not in m_node
:
1462 raise ConfigError('missing "value" property in enumeration member ("{}")'.format(label
))
1464 value
= m_node
['value']
1466 if not _is_int_prop(value
) and not _is_array_prop(value
):
1467 raise ConfigError('invalid enumeration member ("{}"): expecting an integer or an array'.format(label
))
1469 if _is_int_prop(value
):
1471 value
= (value
, value
)
1474 raise ConfigError('invalid enumeration member ("{}"): range must have exactly two items'.format(label
))
1480 raise ConfigError('invalid enumeration member ("{}"): invalid range ({} > {})'.format(label
, mn
, mx
))
1485 obj
.members
[label
] = value
1489 def _create_string(self
, obj
, node
):
1491 # create string object
1492 obj
= metadata
.String()
1494 unk_prop
= _get_first_unknown_type_prop(node
, [
1499 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1502 if 'encoding' in node
:
1503 encoding
= node
['encoding']
1505 if not _is_str_prop(encoding
):
1506 raise ConfigError('"encoding" property of string type object must be a string ("none", "ascii", or "utf-8")')
1508 encoding
= _encoding_str_to_encoding(encoding
)
1510 if encoding
is None:
1511 raise ConfigError('invalid "encoding" property in string type object')
1513 obj
.encoding
= encoding
1517 def _create_struct(self
, obj
, node
):
1519 # create structure object
1520 obj
= metadata
.Struct()
1522 unk_prop
= _get_first_unknown_type_prop(node
, [
1528 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop
))
1531 if 'min-align' in node
:
1532 min_align
= node
['min-align']
1534 if not _is_int_prop(min_align
):
1535 raise ConfigError('"min-align" property of structure type object must be an integer')
1537 if not _is_valid_alignment(min_align
):
1538 raise ConfigError('invalid minimum alignment: {}'.format(min_align
))
1540 obj
.min_align
= min_align
1543 if 'fields' in node
:
1544 fields
= node
['fields']
1546 if not _is_assoc_array_prop(fields
):
1547 raise ConfigError('"fields" property of structure type object must be an associative array')
1549 for field_name
, field_node
in fields
.items():
1550 if not is_valid_identifier(field_name
):
1551 raise ConfigError('"{}" is not a valid field name for structure type'.format(field_name
))
1554 obj
.fields
[field_name
] = self
._create
_type
(field_node
)
1555 except Exception as e
:
1556 raise ConfigError('cannot create structure type\'s field "{}"'.format(field_name
), e
)
1560 def _create_array(self
, obj
, node
):
1562 # create array object
1563 obj
= metadata
.Array()
1565 unk_prop
= _get_first_unknown_type_prop(node
, [
1571 raise ConfigError('unknown array type object property: "{}"'.format(unk_prop
))
1574 if 'length' in node
:
1575 length
= node
['length']
1577 if not _is_int_prop(length
) and not _is_str_prop(length
):
1578 raise ConfigError('"length" property of array type object must be an integer or a string')
1580 if type(length
) is int and length
< 0:
1581 raise ConfigError('invalid static array length: {}'.format(length
))
1586 if 'element-type' in node
:
1588 obj
.element_type
= self
._create
_type
(node
['element-type'])
1589 except Exception as e
:
1590 raise ConfigError('cannot create array type\'s element type', e
)
1594 def _create_variant(self
, obj
, node
):
1596 # create variant object
1597 obj
= metadata
.Variant()
1599 unk_prop
= _get_first_unknown_type_prop(node
, [
1605 raise ConfigError('unknown variant type object property: "{}"'.format(unk_prop
))
1611 if not _is_str_prop(tag
):
1612 raise ConfigError('"tag" property of variant type object must be a string')
1614 # do not validate variant tag for the moment; will be done in a
1620 types
= node
['types']
1622 if not _is_assoc_array_prop(types
):
1623 raise ConfigError('"types" property of variant type object must be an associative array')
1625 # do not validate type names for the moment; will be done in a
1627 for type_name
, type_node
in types
.items():
1628 if not is_valid_identifier(type_name
):
1629 raise ConfigError('"{}" is not a valid type name for variant type'.format(type_name
))
1632 obj
.types
[type_name
] = self
._create
_type
(type_node
)
1633 except Exception as e
:
1634 raise ConfigError('cannot create variant type\'s type "{}"'.format(type_name
), e
)
1638 def _create_type(self
, type_node
):
1639 if type(type_node
) is str:
1640 t
= self
._lookup
_type
_alias
(type_node
)
1643 raise ConfigError('unknown type alias "{}"'.format(type_node
))
1647 if not _is_assoc_array_prop(type_node
):
1648 raise ConfigError('type objects must be associative arrays')
1650 if 'inherit' in type_node
and 'class' in type_node
:
1651 raise ConfigError('cannot specify both "inherit" and "class" properties in type object')
1653 if 'inherit' in type_node
:
1654 inherit
= type_node
['inherit']
1656 if not _is_str_prop(inherit
):
1657 raise ConfigError('"inherit" property of type object must be a string')
1659 base
= self
._lookup
_type
_alias
(inherit
)
1662 raise ConfigError('cannot inherit from type alias "{}": type alias does not exist'.format(inherit
))
1664 func
= self
._type
_to
_create
_type
_func
[type(base
)]
1666 if 'class' not in type_node
:
1667 raise ConfigError('type objects which do not inherit must have a "class" property')
1669 class_name
= type_node
['class']
1671 if type(class_name
) is not str:
1672 raise ConfigError('type objects\' "class" property must be a string')
1674 if class_name
not in self
._class
_name
_to
_create
_type
_func
:
1675 raise ConfigError('unknown type class "{}"'.format(class_name
))
1678 func
= self
._class
_name
_to
_create
_type
_func
[class_name
]
1680 return func(base
, type_node
)
1682 def _register_type_aliases(self
, metadata_node
):
1685 if 'type-aliases' not in metadata_node
:
1688 ta_node
= metadata_node
['type-aliases']
1690 if not _is_assoc_array_prop(ta_node
):
1691 raise ConfigError('"type-aliases" property (metadata) must be an associative array')
1693 for ta_name
, ta_type
in ta_node
.items():
1694 if ta_name
in self
._tas
:
1695 raise ConfigError('duplicate type alias "{}"'.format(ta_name
))
1698 t
= self
._create
_type
(ta_type
)
1699 except Exception as e
:
1700 raise ConfigError('cannot create type alias "{}"'.format(ta_name
), e
)
1702 self
._tas
[ta_name
] = t
1704 def _create_clock(self
, node
):
1705 # create clock object
1706 clock
= metadata
.Clock()
1708 unk_prop
= _get_first_unknown_prop(node
, [
1719 raise ConfigError('unknown clock object property: "{}"'.format(unk_prop
))
1723 uuidp
= node
['uuid']
1725 if not _is_str_prop(uuidp
):
1726 raise ConfigError('"uuid" property of clock object must be a string')
1729 uuidp
= uuid
.UUID(uuidp
)
1731 raise ConfigError('malformed UUID (clock object): "{}"'.format(uuidp
))
1736 if 'description' in node
:
1737 desc
= node
['description']
1739 if not _is_str_prop(desc
):
1740 raise ConfigError('"description" property of clock object must be a string')
1742 clock
.description
= desc
1748 if not _is_int_prop(freq
):
1749 raise ConfigError('"freq" property of clock object must be an integer')
1752 raise ConfigError('invalid clock frequency: {}'.format(freq
))
1757 if 'error-cycles' in node
:
1758 error_cycles
= node
['error-cycles']
1760 if not _is_int_prop(error_cycles
):
1761 raise ConfigError('"error-cycles" property of clock object must be an integer')
1763 if error_cycles
< 0:
1764 raise ConfigError('invalid clock error cycles: {}'.format(error_cycles
))
1766 clock
.error_cycles
= error_cycles
1769 if 'offset' in node
:
1770 offset
= node
['offset']
1772 if not _is_assoc_array_prop(offset
):
1773 raise ConfigError('"offset" property of clock object must be an associative array')
1775 unk_prop
= _get_first_unknown_prop(offset
, ['cycles', 'seconds'])
1778 raise ConfigError('unknown clock object\'s offset property: "{}"'.format(unk_prop
))
1781 if 'cycles' in offset
:
1782 offset_cycles
= offset
['cycles']
1784 if not _is_int_prop(offset_cycles
):
1785 raise ConfigError('"cycles" property of clock object\'s offset property must be an integer')
1787 if offset_cycles
< 0:
1788 raise ConfigError('invalid clock offset cycles: {}'.format(offset_cycles
))
1790 clock
.offset_cycles
= offset_cycles
1793 if 'seconds' in offset
:
1794 offset_seconds
= offset
['seconds']
1796 if not _is_int_prop(offset_seconds
):
1797 raise ConfigError('"seconds" property of clock object\'s offset property must be an integer')
1799 if offset_seconds
< 0:
1800 raise ConfigError('invalid clock offset seconds: {}'.format(offset_seconds
))
1802 clock
.offset_seconds
= offset_seconds
1805 if 'absolute' in node
:
1806 absolute
= node
['absolute']
1808 if not _is_bool_prop(absolute
):
1809 raise ConfigError('"absolute" property of clock object must be a boolean')
1811 clock
.absolute
= absolute
1814 if 'return-ctype' in node
:
1815 ctype
= node
['return-ctype']
1817 if not _is_str_prop(ctype
):
1818 raise ConfigError('"return-ctype" property of clock object must be a string')
1820 clock
.return_ctype
= ctype
1824 def _register_clocks(self
, metadata_node
):
1825 self
._clocks
= collections
.OrderedDict()
1827 if 'clocks' not in metadata_node
:
1830 clocks_node
= metadata_node
['clocks']
1832 if not _is_assoc_array_prop(clocks_node
):
1833 raise ConfigError('"clocks" property (metadata) must be an associative array')
1835 for clock_name
, clock_node
in clocks_node
.items():
1836 if not is_valid_identifier(clock_name
):
1837 raise ConfigError('invalid clock name: "{}"'.format(clock_name
))
1839 if clock_name
in self
._clocks
:
1840 raise ConfigError('duplicate clock "{}"'.format(clock_name
))
1843 clock
= self
._create
_clock
(clock_node
)
1844 except Exception as e
:
1845 raise ConfigError('cannot create clock "{}"'.format(clock_name
), e
)
1847 clock
.name
= clock_name
1848 self
._clocks
[clock_name
] = clock
1850 def _create_env(self
, metadata_node
):
1851 env
= collections
.OrderedDict()
1853 if 'env' not in metadata_node
:
1856 env_node
= metadata_node
['env']
1858 if not _is_assoc_array_prop(env_node
):
1859 raise ConfigError('"env" property (metadata) must be an associative array')
1861 for env_name
, env_value
in env_node
.items():
1863 raise ConfigError('duplicate environment variable "{}"'.format(env_name
))
1865 if not is_valid_identifier(env_name
):
1866 raise ConfigError('invalid environment variable name: "{}"'.format(env_name
))
1868 if not _is_int_prop(env_value
) and not _is_str_prop(env_value
):
1869 raise ConfigError('invalid environment variable value ("{}"): expecting integer or string'.format(env_name
))
1871 env
[env_name
] = env_value
1875 def _register_log_levels(self
, metadata_node
):
1876 self
._log
_levels
= dict()
1878 if 'log-levels' not in metadata_node
:
1881 log_levels_node
= metadata_node
['log-levels']
1883 if not _is_assoc_array_prop(log_levels_node
):
1884 raise ConfigError('"log-levels" property (metadata) must be an associative array')
1886 for ll_name
, ll_value
in log_levels_node
.items():
1887 if ll_name
in self
._log
_levels
:
1888 raise ConfigError('duplicate log level entry "{}"'.format(ll_name
))
1890 if not _is_int_prop(ll_value
):
1891 raise ConfigError('invalid log level entry ("{}"): expecting an integer'.format(ll_name
))
1893 self
._log
_levels
[ll_name
] = ll_value
1895 def _create_trace(self
, metadata_node
):
1896 # create trace object
1897 trace
= metadata
.Trace()
1898 trace_node
= metadata_node
['trace']
1899 unk_prop
= _get_first_unknown_prop(trace_node
, [
1902 'packet-header-type',
1906 raise ConfigError('unknown trace object property: "{}"'.format(unk_prop
))
1908 # set byte order (already parsed)
1909 trace
.byte_order
= self
._bo
1912 if 'uuid' in trace_node
:
1913 uuidp
= trace_node
['uuid']
1915 if not _is_str_prop(uuidp
):
1916 raise ConfigError('"uuid" property of trace object must be a string')
1919 uuidp
= uuid
.uuid1()
1922 uuidp
= uuid
.UUID(uuidp
)
1924 raise ConfigError('malformed UUID (trace object): "{}"'.format(uuidp
))
1928 # packet header type
1929 if 'packet-header-type' in trace_node
:
1931 ph_type
= self
._create
_type
(trace_node
['packet-header-type'])
1932 except Exception as e
:
1933 raise ConfigError('cannot create packet header type (trace)', e
)
1935 trace
.packet_header_type
= ph_type
1939 def _lookup_log_level(self
, ll
):
1940 if _is_int_prop(ll
):
1942 elif _is_str_prop(ll
) and ll
in self
._log
_levels
:
1943 return self
._log
_levels
[ll
]
1945 def _create_event(self
, event_node
):
1946 event
= metadata
.Event()
1947 unk_prop
= _get_first_unknown_prop(event_node
, [
1954 raise ConfigError('unknown event object property: "{}"'.format(unk_prop
))
1956 if not _is_assoc_array_prop(event_node
):
1957 raise ConfigError('event objects must be associative arrays')
1959 if 'log-level' in event_node
:
1960 ll
= self
._lookup
_log
_level
(event_node
['log-level'])
1963 raise ConfigError('invalid "log-level" property')
1965 event
.log_level
= ll
1967 if 'context-type' in event_node
:
1969 t
= self
._create
_type
(event_node
['context-type'])
1970 except Exception as e
:
1971 raise ConfigError('cannot create event\'s context type object', e
)
1973 event
.context_type
= t
1975 if 'payload-type' not in event_node
:
1976 raise ConfigError('missing "payload-type" property in event object')
1979 t
= self
._create
_type
(event_node
['payload-type'])
1980 except Exception as e
:
1981 raise ConfigError('cannot create event\'s payload type object', e
)
1983 event
.payload_type
= t
1987 def _create_stream(self
, stream_node
):
1988 stream
= metadata
.Stream()
1989 unk_prop
= _get_first_unknown_prop(stream_node
, [
1990 'packet-context-type',
1991 'event-header-type',
1992 'event-context-type',
1997 raise ConfigError('unknown stream object property: "{}"'.format(unk_prop
))
1999 if not _is_assoc_array_prop(stream_node
):
2000 raise ConfigError('stream objects must be associative arrays')
2002 if 'packet-context-type' in stream_node
:
2004 t
= self
._create
_type
(stream_node
['packet-context-type'])
2005 except Exception as e
:
2006 raise ConfigError('cannot create stream\'s packet context type object', e
)
2008 stream
.packet_context_type
= t
2010 if 'event-header-type' in stream_node
:
2012 t
= self
._create
_type
(stream_node
['event-header-type'])
2013 except Exception as e
:
2014 raise ConfigError('cannot create stream\'s event header type object', e
)
2016 stream
.event_header_type
= t
2018 if 'event-context-type' in stream_node
:
2020 t
= self
._create
_type
(stream_node
['event-context-type'])
2021 except Exception as e
:
2022 raise ConfigError('cannot create stream\'s event context type object', e
)
2024 stream
.event_context_type
= t
2026 if 'events' not in stream_node
:
2027 raise ConfigError('missing "events" property in stream object')
2029 events
= stream_node
['events']
2031 if not _is_assoc_array_prop(events
):
2032 raise ConfigError('"events" property of stream object must be an associative array')
2035 raise ConfigError('at least one event is needed within a stream object')
2039 for ev_name
, ev_node
in events
.items():
2041 ev
= self
._create
_event
(ev_node
)
2042 except Exception as e
:
2043 raise ConfigError('cannot create event "{}"'.format(ev_name
), e
)
2047 stream
.events
[ev_name
] = ev
2052 def _create_streams(self
, metadata_node
):
2053 streams
= collections
.OrderedDict()
2055 if 'streams' not in metadata_node
:
2056 raise ConfigError('missing "streams" property (metadata)')
2058 streams_node
= metadata_node
['streams']
2060 if not _is_assoc_array_prop(streams_node
):
2061 raise ConfigError('"streams" property (metadata) must be an associative array')
2063 if not streams_node
:
2064 raise ConfigError('at least one stream is needed (metadata)')
2068 for stream_name
, stream_node
in streams_node
.items():
2070 stream
= self
._create
_stream
(stream_node
)
2071 except Exception as e
:
2072 raise ConfigError('cannot create stream "{}"'.format(stream_name
), e
)
2075 stream
.name
= str(stream_name
)
2076 streams
[stream_name
] = stream
2081 def _create_metadata(self
, root
):
2082 meta
= metadata
.Metadata()
2084 if 'metadata' not in root
:
2085 raise ConfigError('missing "metadata" property (root)')
2087 metadata_node
= root
['metadata']
2088 unk_prop
= _get_first_unknown_prop(metadata_node
, [
2098 raise ConfigError('unknown metadata property: "{}"'.format(unk_prop
))
2100 if not _is_assoc_array_prop(metadata_node
):
2101 raise ConfigError('"metadata" property (root) must be an associative array')
2103 self
._set
_byte
_order
(metadata_node
)
2104 self
._register
_clocks
(metadata_node
)
2105 meta
.clocks
= self
._clocks
2106 self
._register
_type
_aliases
(metadata_node
)
2107 meta
.env
= self
._create
_env
(metadata_node
)
2108 meta
.trace
= self
._create
_trace
(metadata_node
)
2109 self
._register
_log
_levels
(metadata_node
)
2110 meta
.streams
= self
._create
_streams
(metadata_node
)
2114 def _get_version(self
, root
):
2115 if 'version' not in root
:
2116 raise ConfigError('missing "version" property (root)')
2118 version_node
= root
['version']
2120 if not _is_str_prop(version_node
):
2121 raise ConfigError('"version" property (root) must be a string')
2123 version_node
= version_node
.strip()
2125 if version_node
not in ['2.0', '2.1']:
2126 raise ConfigError('unsupported version ({}): versions 2.0 and 2.1 are supported'.format(version_node
))
2128 # convert version string to comparable version integer
2129 parts
= version_node
.split('.')
2130 version
= int(parts
[0]) * 100 + int(parts
[1])
2134 def _get_prefix(self
, root
):
2135 if 'prefix' not in root
:
2138 prefix_node
= root
['prefix']
2140 if not _is_str_prop(prefix_node
):
2141 raise ConfigError('"prefix" property (root) must be a string')
2143 if not is_valid_identifier(prefix_node
):
2144 raise ConfigError('"prefix" property (root) must be a valid C identifier')
2148 def _yaml_ordered_load(self
, stream
):
2149 class OLoader(yaml
.Loader
):
2152 def construct_mapping(loader
, node
):
2153 loader
.flatten_mapping(node
)
2155 return collections
.OrderedDict(loader
.construct_pairs(node
))
2157 OLoader
.add_constructor(yaml
.resolver
.BaseResolver
.DEFAULT_MAPPING_TAG
,
2160 return yaml
.load(stream
, OLoader
)
2162 def parse(self
, yml
):
2164 root
= self
._yaml
_ordered
_load
(yml
)
2165 except Exception as e
:
2166 raise ConfigError('cannot parse YAML input', e
)
2168 if not _is_assoc_array_prop(root
):
2169 raise ConfigError('root must be an associative array')
2171 self
._version
= self
._get
_version
(root
)
2172 meta
= self
._create
_metadata
(root
)
2173 prefix
= self
._get
_prefix
(root
)
2175 return Config(self
._version
, prefix
, meta
)
2179 parser
= _YamlConfigParser()
2180 cfg
= parser
.parse(yml
)
2185 def from_yaml_file(path
):
2187 with
open(path
) as f
:
2188 return from_yaml(f
.read())
2189 except Exception as e
:
2190 raise ConfigError('cannot create configuration from YAML file'.format(e
), e
)