config: prefer using $return-ctype prop in 2.1
[deliverable/barectf.git] / barectf / config.py
1 # The MIT License (MIT)
2 #
3 # Copyright (c) 2015 Philippe Proulx <pproulx@efficios.com>
4 #
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:
11 #
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
14 #
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
21 # THE SOFTWARE.
22
23 from barectf import metadata
24 import collections
25 import datetime
26 import barectf
27 import enum
28 import yaml
29 import uuid
30 import copy
31 import re
32
33
34 class ConfigError(RuntimeError):
35 def __init__(self, msg, prev=None):
36 super().__init__(msg)
37 self._prev = prev
38
39 @property
40 def prev(self):
41 return self._prev
42
43
44 class Config:
45 def __init__(self, version, prefix, metadata):
46 self.prefix = prefix
47 self.version = version
48 self.metadata = metadata
49
50 def _validate_metadata(self, meta):
51 try:
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)
60
61 try:
62 validator = _BarectfMetadataValidator()
63 validator.validate(meta)
64 except Exception as e:
65 raise ConfigError('barectf metadata error', e)
66
67 def _augment_metadata_env(self, meta):
68 env = meta.env
69
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())
77
78 @property
79 def version(self):
80 return self._version
81
82 @version.setter
83 def version(self, value):
84 self._version = value
85
86 @property
87 def metadata(self):
88 return self._metadata
89
90 @metadata.setter
91 def metadata(self, value):
92 self._validate_metadata(value)
93 self._augment_metadata_env(value)
94 self._metadata = value
95
96 @property
97 def prefix(self):
98 return self._prefix
99
100 @prefix.setter
101 def prefix(self, value):
102 if not is_valid_identifier(value):
103 raise ConfigError('prefix must be a valid C identifier')
104
105 self._prefix = value
106
107
108 def _is_assoc_array_prop(node):
109 return isinstance(node, dict)
110
111
112 def _is_array_prop(node):
113 return isinstance(node, list)
114
115
116 def _is_int_prop(node):
117 return type(node) is int
118
119
120 def _is_str_prop(node):
121 return type(node) is str
122
123
124 def _is_bool_prop(node):
125 return type(node) is bool
126
127
128 def _is_valid_alignment(align):
129 return ((align & (align - 1)) == 0) and align > 0
130
131
132 def _byte_order_str_to_bo(bo_str):
133 bo_str = bo_str.lower()
134
135 if bo_str == 'le':
136 return metadata.ByteOrder.LE
137 elif bo_str == 'be':
138 return metadata.ByteOrder.BE
139
140
141 def _encoding_str_to_encoding(encoding_str):
142 encoding_str = encoding_str.lower()
143
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
150
151
152 _re_iden = re.compile(r'^[a-zA-Z][a-zA-Z0-9_]*$')
153 _ctf_keywords = set([
154 'align',
155 'callsite',
156 'clock',
157 'enum',
158 'env',
159 'event',
160 'floating_point',
161 'integer',
162 'stream',
163 'string',
164 'struct',
165 'trace',
166 'typealias',
167 'typedef',
168 'variant',
169 ])
170
171
172 def is_valid_identifier(iden):
173 if not _re_iden.match(iden):
174 return False
175
176 if _re_iden in _ctf_keywords:
177 return False
178
179 return True
180
181
182 def _get_first_unknown_prop(node, known_props):
183 for prop_name in node:
184 if prop_name in known_props:
185 continue
186
187 return prop_name
188
189
190 def _get_first_unknown_type_prop(type_node, known_props):
191 kp = known_props + ['inherit', 'class']
192
193 return _get_first_unknown_prop(type_node, kp)
194
195
196 # This validator validates the configured metadata for barectf specific
197 # needs.
198 #
199 # barectf needs:
200 #
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:
205 def __init__(self):
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,
214 }
215
216 def _validate_int_type(self, t, entity_root):
217 if t.size > 64:
218 raise ConfigError('integer type\'s size must be lesser than or equal to 64 bits')
219
220 def _validate_float_type(self, t, entity_root):
221 if t.size > 64:
222 raise ConfigError('floating point number type\'s size must be lesser than or equal to 64 bits')
223
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')
227
228 def _validate_string_type(self, t, entity_root):
229 pass
230
231 def _validate_struct_type(self, t, entity_root):
232 if not entity_root:
233 raise ConfigError('inner structure types are not supported as of this version')
234
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':
238 # allow
239 continue
240
241 try:
242 self._validate_type(field_type, False)
243 except Exception as e:
244 raise ConfigError('in structure type\'s field "{}"'.format(field_name), e)
245
246 def _validate_array_type(self, t, entity_root):
247 raise ConfigError('array types are not supported as of this version')
248
249 def _validate_variant_type(self, t, entity_root):
250 raise ConfigError('variant types are not supported as of this version')
251
252 def _validate_type(self, t, entity_root):
253 self._type_to_validate_type_func[type(t)](t, entity_root)
254
255 def _validate_entity(self, t):
256 if t is None:
257 return
258
259 # make sure entity is byte-aligned
260 if t.align < 8:
261 raise ConfigError('type\'s alignment must be at least byte-aligned')
262
263 # make sure entity is a structure
264 if type(t) is not metadata.Struct:
265 raise ConfigError('expecting a structure type')
266
267 # validate types
268 self._validate_type(t, True)
269
270 def _validate_entities_and_names(self, meta):
271 self._cur_entity = _Entity.TRACE_PACKET_HEADER
272
273 try:
274 self._validate_entity(meta.trace.packet_header_type)
275 except Exception as e:
276 raise ConfigError('invalid trace packet header type', e)
277
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))
281
282 self._cur_entity = _Entity.STREAM_PACKET_CONTEXT
283
284 try:
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)
288
289 self._cur_entity = _Entity.STREAM_EVENT_HEADER
290
291 try:
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)
295
296 self._cur_entity = _Entity.STREAM_EVENT_CONTEXT
297
298 try:
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)
302
303 try:
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))
307
308 self._cur_entity = _Entity.EVENT_CONTEXT
309
310 try:
311 self._validate_entity(ev.context_type)
312 except Exception as e:
313 raise ConfigError('invalid context type in event "{}"'.format(ev_name), e)
314
315 self._cur_entity = _Entity.EVENT_PAYLOAD
316
317 if ev.payload_type is None:
318 raise ConfigError('missing payload type in event "{}"'.format(ev_name), e)
319
320 try:
321 self._validate_entity(ev.payload_type)
322 except Exception as e:
323 raise ConfigError('invalid payload type in event "{}"'.format(ev_name), e)
324
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)
329
330 def validate(self, meta):
331 self._validate_entities_and_names(meta)
332
333
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:
341 # yes
342 if t is None:
343 raise ConfigError('need "stream_id" field in trace packet header type, but trace packet header type is missing')
344
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')
347
348 if 'stream_id' not in t.fields:
349 raise ConfigError('need "stream_id" field in trace packet header type')
350
351 # validate "magic" and "stream_id" types
352 if type(t) is not metadata.Struct:
353 return
354
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')
359
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')
362
363 if i != 0:
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')
368
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')
374
375 if type(field_type) is not metadata.Array:
376 raise ConfigError('"uuid" field in trace packet header type must be an array')
377
378 if field_type.length != 16:
379 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
380
381 element_type = field_type.element_type
382
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')
385
386 if element_type.size != 8:
387 raise ConfigError('"uuid" field in trace packet header type must be an array of 16 bytes')
388
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')
391
392 def _validate_trace(self, meta):
393 self._validate_trace_packet_header_type(meta.trace.packet_header_type)
394
395 def _validate_stream_packet_context(self, stream):
396 t = stream.packet_context_type
397
398 if type(t) is None:
399 return
400
401 if type(t) is not metadata.Struct:
402 return
403
404 # "timestamp_begin", if exists, is an unsigned integer type,
405 # mapped to a clock
406 if 'timestamp_begin' in t.fields:
407 ts_begin = t.fields['timestamp_begin']
408
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')
411
412 if ts_begin.signed:
413 raise ConfigError('"timestamp_begin" field in stream packet context type must be an unsigned integer type')
414
415 if not ts_begin.property_mappings:
416 raise ConfigError('"timestamp_begin" field in stream packet context type must be mapped to a clock')
417
418 # "timestamp_end", if exists, is an unsigned integer type,
419 # mapped to a clock
420 if 'timestamp_end' in t.fields:
421 ts_end = t.fields['timestamp_end']
422
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')
425
426 if ts_end.signed:
427 raise ConfigError('"timestamp_end" field in stream packet context type must be an unsigned integer type')
428
429 if not ts_end.property_mappings:
430 raise ConfigError('"timestamp_end" field in stream packet context type must be mapped to a clock')
431
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')
435
436 # "events_discarded", if exists, is an unsigned integer type
437 if 'events_discarded' in t.fields:
438 events_discarded = t.fields['events_discarded']
439
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')
442
443 if events_discarded.signed:
444 raise ConfigError('"events_discarded" field in stream packet context type must be an unsigned integer type')
445
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')
449
450 packet_size = t.fields['packet_size']
451
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')
455
456 content_size = t.fields['content_size']
457
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')
461
462 if packet_size.signed:
463 raise ConfigError('"packet_size" field in stream packet context type must be an unsigned integer type')
464
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')
468
469 if content_size.signed:
470 raise ConfigError('"content_size" field in stream packet context type must be an unsigned integer type')
471
472 def _validate_stream_event_header(self, stream):
473 t = stream.event_header_type
474
475 # needs "id" field?
476 if len(stream.events) > 1:
477 # yes
478 if t is None:
479 raise ConfigError('need "id" field in stream event header type, but stream event header type is missing')
480
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')
483
484 if 'id' not in t.fields:
485 raise ConfigError('need "id" field in stream event header type')
486
487 # validate "id" and "timestamp" types
488 if type(t) is not metadata.Struct:
489 return
490
491 # "timestamp", if exists, is an unsigned integer type,
492 # mapped to a clock
493 if 'timestamp' in t.fields:
494 ts = t.fields['timestamp']
495
496 if type(ts) is not metadata.Integer:
497 raise ConfigError('"ts" field in stream event header type must be an integer type')
498
499 if ts.signed:
500 raise ConfigError('"ts" field in stream event header type must be an unsigned integer type')
501
502 if not ts.property_mappings:
503 raise ConfigError('"ts" field in stream event header type must be mapped to a clock')
504
505 # "id" is an unsigned integer type
506 if 'id' in t.fields:
507 eid = t.fields['id']
508
509 if type(eid) is not metadata.Integer:
510 raise ConfigError('"id" field in stream event header type must be an integer type')
511
512 if eid.signed:
513 raise ConfigError('"id" field in stream event header type must be an unsigned integer type')
514
515 def _validate_stream(self, stream):
516 self._validate_stream_packet_context(stream)
517 self._validate_stream_event_header(stream)
518
519 def validate(self, meta):
520 self._meta = meta
521 self._validate_trace(meta)
522
523 for stream in meta.streams.values():
524 try:
525 self._validate_stream(stream)
526 except Exception as e:
527 raise ConfigError('invalid stream "{}"'.format(stream.name), e)
528
529
530 class _MetadataDynamicTypesValidatorStackEntry:
531 def __init__(self, base_t):
532 self._base_t = base_t
533 self._index = 0
534
535 @property
536 def index(self):
537 return self._index
538
539 @index.setter
540 def index(self, value):
541 self._index = value
542
543 @property
544 def base_t(self):
545 return self._base_t
546
547 @base_t.setter
548 def base_t(self, value):
549 self._base_t = value
550
551
552 # Entities. Order of values is important here.
553 @enum.unique
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
559 EVENT_CONTEXT = 4
560 EVENT_PAYLOAD = 5
561
562
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:
568 def __init__(self):
569 self._type_to_visit_type_func = {
570 metadata.Integer: None,
571 metadata.FloatingPoint: None,
572 metadata.Enum: 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,
577 }
578
579 self._cur_trace = None
580 self._cur_stream = None
581 self._cur_event = None
582
583 def _lookup_path_from_base(self, path, parts, base, start_index,
584 base_is_current, from_t):
585 index = start_index
586 cur_t = base
587 found_path = []
588
589 while index < len(parts):
590 part = parts[index]
591 next_t = None
592
593 if type(cur_t) is metadata.Struct:
594 enumerated_items = enumerate(cur_t.fields.items())
595
596 # lookup each field
597 for i, (field_name, field_type) in enumerated_items:
598 if field_name == part:
599 next_t = field_type
600 found_path.append((i, field_type))
601
602 if next_t is None:
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())
606
607 # lookup each type
608 for i, (type_name, type_type) in enumerated_items:
609 if type_name == part:
610 next_t = type_type
611 found_path.append((i, type_type))
612
613 if next_t is None:
614 raise ConfigError('invalid path "{}": cannot find type "{}" in variant type'.format(path, part))
615 else:
616 raise ConfigError('invalid path "{}": requesting "{}" in a non-variant, non-structure type'.format(path, part))
617
618 cur_t = next_t
619 index += 1
620
621 # make sure that the pointed type is not the pointing type
622 if from_t is cur_t:
623 raise ConfigError('invalid path "{}": pointing to self'.format(path))
624
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:
630 return cur_t
631
632 for index, entry in enumerate(self._stack):
633 if index == len(found_path):
634 # end of index path; valid so far
635 break
636
637 if found_path[index][0] > entry.index:
638 raise ConfigError('invalid path "{}": pointed type is after pointing type'.format(path))
639
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):
644 break
645
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))
650
651 return cur_t
652
653 def _lookup_path_from_top(self, path, parts):
654 if len(parts) != 1:
655 raise ConfigError('invalid path "{}": multipart relative path not supported'.format(path))
656
657 find_name = parts[0]
658 index = len(self._stack) - 1
659 got_struct = False
660
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:
665 got_struct = True
666 enumerated_items = enumerate(entry.base_t.fields.items())
667
668 # lookup each field, until the current visiting index is met
669 for i, (field_name, field_type) in enumerated_items:
670 if i == entry.index:
671 break
672
673 if field_name == find_name:
674 return field_type
675
676 # variant base type
677 elif type(entry.base_t) is metadata.Variant:
678 enumerated_items = enumerate(entry.base_t.types.items())
679
680 # lookup each type, until the current visiting index is met
681 for i, (type_name, type_type) in enumerated_items:
682 if i == entry.index:
683 break
684
685 if type_name == find_name:
686 if not got_struct:
687 raise ConfigError('invalid path "{}": type cannot be reached because pointed and pointing types are in the same variant type'.format(path))
688
689 return type_type
690
691 # nothing returned here: cannot find type
692 raise ConfigError('invalid path "{}": cannot find type in current context'.format(path))
693
694 def _lookup_path(self, path, from_t):
695 parts = path.lower().split('.')
696 base = None
697 base_is_current = False
698
699 if len(parts) >= 3:
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))
705
706 base = self._cur_trace.packet_header_type
707
708 if self._cur_entity == _Entity.TRACE_PACKET_HEADER:
709 base_is_current = True
710 else:
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))
716
717 if self._cur_stream.packet_context_type is None:
718 raise ConfigError('invalid path "{}": no defined stream packet context type'.format(path))
719
720 base = self._cur_stream.packet_context_type
721
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))
728
729 if self._cur_stream.event_header_type is None:
730 raise ConfigError('invalid path "{}": no defined stream event header type'.format(path))
731
732 base = self._cur_stream.event_header_type
733
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))
739
740 if self._cur_stream.event_context_type is None:
741 raise ConfigError('invalid path "{}": no defined stream event context type'.format(path))
742
743 base = self._cur_stream.event_context_type
744
745 if self._cur_entity == _Entity.STREAM_EVENT_CONTEXT:
746 base_is_current = True
747 else:
748 raise ConfigError('invalid path "{}": unknown names after "stream.event"'.format(path))
749 else:
750 raise ConfigError('invalid path "{}": unknown names after "stream"'.format(path))
751
752 if base is not None:
753 start_index = 3
754
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))
760
761 if self._cur_event.context_type is None:
762 raise ConfigError('invalid path "{}": no defined event context type'.format(path))
763
764 base = self._cur_event.context_type
765
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))
771
772 if self._cur_event.payload_type is None:
773 raise ConfigError('invalid path "{}": no defined event payload type'.format(path))
774
775 base = self._cur_event.payload_type
776
777 if self._cur_entity == _Entity.EVENT_PAYLOAD:
778 base_is_current = True
779 else:
780 raise ConfigError('invalid path "{}": unknown names after "event"'.format(path))
781
782 if base is not None:
783 start_index = 2
784
785 if base is not None:
786 return self._lookup_path_from_base(path, parts, base, start_index,
787 base_is_current, from_t)
788 else:
789 return self._lookup_path_from_top(path, parts)
790
791 def _stack_reset(self):
792 self._stack = []
793
794 def _stack_push(self, base_t):
795 entry = _MetadataDynamicTypesValidatorStackEntry(base_t)
796 self._stack.append(entry)
797
798 def _stack_pop(self):
799 self._stack.pop()
800
801 def _stack_incr_index(self):
802 self._stack[-1].index += 1
803
804 def _visit_struct_type(self, t):
805 self._stack_push(t)
806
807 for field_name, field_type in t.fields.items():
808 try:
809 self._visit_type(field_type)
810 except Exception as e:
811 raise ConfigError('in structure type\'s field "{}"'.format(field_name), e)
812
813 self._stack_incr_index()
814
815 self._stack_pop()
816
817 def _visit_array_type(self, t):
818 if not t.is_static:
819 # find length type
820 try:
821 length_type = self._lookup_path(t.length, t)
822 except Exception as e:
823 raise ConfigError('invalid array type\'s length', e)
824
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')
828
829 if length_type.signed:
830 raise ConfigError('array type\'s length does not point to an unsigned integer type')
831
832 self._visit_type(t.element_type)
833
834 def _visit_variant_type(self, t):
835 # find tag type
836 try:
837 tag_type = self._lookup_path(t.tag, t)
838 except Exception as e:
839 raise ConfigError('invalid variant type\'s tag', e)
840
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')
844
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))
849
850 self._stack_push(t)
851
852 for type_name, type_type in t.types.items():
853 try:
854 self._visit_type(type_type)
855 except Exception as e:
856 raise ConfigError('in variant type\'s type "{}"'.format(type_name), e)
857
858 self._stack_incr_index()
859
860 self._stack_pop()
861
862 def _visit_type(self, t):
863 if t is None:
864 return
865
866 if type(t) in self._type_to_visit_type_func:
867 func = self._type_to_visit_type_func[type(t)]
868
869 if func is not None:
870 func(t)
871
872 def _visit_event(self, ev):
873 ev_name = ev.name
874
875 # set current event
876 self._cur_event = ev
877
878 # visit event context type
879 self._stack_reset()
880 self._cur_entity = _Entity.EVENT_CONTEXT
881
882 try:
883 self._visit_type(ev.context_type)
884 except Exception as e:
885 raise ConfigError('invalid context type in event "{}"'.format(ev_name), e)
886
887 # visit event payload type
888 self._stack_reset()
889 self._cur_entity = _Entity.EVENT_PAYLOAD
890
891 try:
892 self._visit_type(ev.payload_type)
893 except Exception as e:
894 raise ConfigError('invalid payload type in event "{}"'.format(ev_name), e)
895
896 def _visit_stream(self, stream):
897 stream_name = stream.name
898
899 # set current stream
900 self._cur_stream = stream
901
902 # reset current event
903 self._cur_event = None
904
905 # visit stream packet context type
906 self._stack_reset()
907 self._cur_entity = _Entity.STREAM_PACKET_CONTEXT
908
909 try:
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)
913
914 # visit stream event header type
915 self._stack_reset()
916 self._cur_entity = _Entity.STREAM_EVENT_HEADER
917
918 try:
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)
922
923 # visit stream event context type
924 self._stack_reset()
925 self._cur_entity = _Entity.STREAM_EVENT_CONTEXT
926
927 try:
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)
931
932 # visit events
933 for ev in stream.events.values():
934 try:
935 self._visit_event(ev)
936 except Exception as e:
937 raise ConfigError('invalid stream "{}"'.format(stream_name))
938
939 def validate(self, meta):
940 # set current trace
941 self._cur_trace = meta.trace
942
943 # visit trace packet header type
944 self._stack_reset()
945 self._cur_entity = _Entity.TRACE_PACKET_HEADER
946
947 try:
948 self._visit_type(meta.trace.packet_header_type)
949 except Exception as e:
950 raise ConfigError('invalid packet header type in trace', e)
951
952 # visit streams
953 for stream in meta.streams.values():
954 self._visit_stream(stream)
955
956
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:
961 def __init__(self):
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,
970 }
971
972 def _validate_integer_histology(self, t):
973 # size is set
974 if t.size is None:
975 raise ConfigError('missing integer type\'s size')
976
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')
981
982 # mantissa digits is set
983 if t.mant_size is None:
984 raise ConfigError('missing floating point number type\'s mantissa size')
985
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')
989
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')
994
995 # there's at least one member
996 if not t.members:
997 raise ConfigError('enumeration type needs at least one member')
998
999 # no overlapping values
1000 ranges = []
1001
1002 for label, value in t.members.items():
1003 for rg in ranges:
1004 if value[0] <= rg[1] and rg[0] <= value[1]:
1005 raise ConfigError('enumeration type\'s member "{}" overlaps another member'.format(label))
1006
1007 ranges.append(value)
1008
1009 def _validate_string_histology(self, t):
1010 # always valid
1011 pass
1012
1013 def _validate_struct_histology(self, t):
1014 # all fields are valid
1015 for field_name, field_type in t.fields.items():
1016 try:
1017 self._validate_type_histology(field_type)
1018 except Exception as e:
1019 raise ConfigError('invalid structure type\'s field "{}"'.format(field_name), e)
1020
1021 def _validate_array_histology(self, t):
1022 # length is set
1023 if t.length is None:
1024 raise ConfigError('missing array type\'s length')
1025
1026 # element type is set
1027 if t.element_type is None:
1028 raise ConfigError('missing array type\'s element type')
1029
1030 # element type is valid
1031 try:
1032 self._validate_type_histology(t.element_type)
1033 except Exception as e:
1034 raise ConfigError('invalid array type\'s element type', e)
1035
1036 def _validate_variant_histology(self, t):
1037 # tag is set
1038 if t.tag is None:
1039 raise ConfigError('missing variant type\'s tag')
1040
1041 # there's at least one type
1042 if not t.types:
1043 raise ConfigError('variant type needs at least one type')
1044
1045 # all types are valid
1046 for type_name, type_t in t.types.items():
1047 try:
1048 self._validate_type_histology(type_t)
1049 except Exception as e:
1050 raise ConfigError('invalid variant type\'s type "{}"'.format(type_name), e)
1051
1052 def _validate_type_histology(self, t):
1053 if t is None:
1054 return
1055
1056 self._type_to_validate_type_histology_func[type(t)](t)
1057
1058 def _validate_entity_type_histology(self, t):
1059 if t is None:
1060 return
1061
1062 # entity cannot be an array
1063 if type(t) is metadata.Array:
1064 raise ConfigError('cannot use an array here')
1065
1066 self._validate_type_histology(t)
1067
1068 def _validate_event_types_histology(self, ev):
1069 ev_name = ev.name
1070
1071 # validate event context type
1072 try:
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)
1076
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))
1080
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))
1085
1086 try:
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)
1090
1091 def _validate_stream_types_histology(self, stream):
1092 stream_name = stream.name
1093
1094 # validate stream packet context type
1095 try:
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)
1099
1100 # validate stream event header type
1101 try:
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)
1105
1106 # validate stream event context type
1107 try:
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)
1111
1112 # validate events
1113 for ev in stream.events.values():
1114 try:
1115 self._validate_event_types_histology(ev)
1116 except Exception as e:
1117 raise ConfigError('invalid event in stream "{}"'.format(stream_name), e)
1118
1119 def validate(self, meta):
1120 # validate trace packet header type
1121 try:
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)
1125
1126 # validate streams
1127 for stream in meta.streams.values():
1128 self._validate_stream_types_histology(stream)
1129
1130
1131 class _YamlConfigParser:
1132 def __init__(self):
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,
1148 }
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,
1157 }
1158
1159 def _set_byte_order(self, metadata_node):
1160 if 'trace' not in metadata_node:
1161 raise ConfigError('missing "trace" property (metadata)')
1162
1163 trace_node = metadata_node['trace']
1164
1165 if not _is_assoc_array_prop(trace_node):
1166 raise ConfigError('"trace" property (metadata) must be an associative array')
1167
1168 if 'byte-order' not in trace_node:
1169 raise ConfigError('missing "byte-order" property (trace)')
1170
1171 self._bo = _byte_order_str_to_bo(trace_node['byte-order'])
1172
1173 if self._bo is None:
1174 raise ConfigError('invalid "byte-order" property (trace): must be "le" or "be"')
1175
1176 def _lookup_type_alias(self, name):
1177 if name in self._tas:
1178 return copy.deepcopy(self._tas[name])
1179
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'])
1182
1183 if unk_prop:
1184 raise ConfigError('unknown property in integer type object\'s clock property mapping: "{}"'.format(unk_prop))
1185
1186 if 'name' not in prop_mapping_node:
1187 raise ConfigError('missing "name" property in integer type object\'s clock property mapping')
1188
1189 if 'property' not in prop_mapping_node:
1190 raise ConfigError('missing "property" property in integer type object\'s clock property mapping')
1191
1192 clock_name = prop_mapping_node['name']
1193 prop = prop_mapping_node['property']
1194
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')
1197
1198 if not _is_str_prop(prop):
1199 raise ConfigError('"property" property of integer type object\'s clock property mapping must be a string')
1200
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))
1203
1204 if prop != 'value':
1205 raise ConfigError('invalid "property" property in integer type object\'s clock property mapping: "{}"'.format(prop))
1206
1207 mapped_clock = self._clocks[clock_name]
1208 int_obj.property_mappings.append(metadata.PropertyMapping(mapped_clock, prop))
1209
1210 def _create_integer(self, obj, node):
1211 if obj is None:
1212 # create integer object
1213 obj = metadata.Integer()
1214
1215 unk_prop = _get_first_unknown_type_prop(node, [
1216 'size',
1217 'align',
1218 'signed',
1219 'byte-order',
1220 'base',
1221 'encoding',
1222 'property-mappings',
1223 ])
1224
1225 if unk_prop:
1226 raise ConfigError('unknown integer type object property: "{}"'.format(unk_prop))
1227
1228 # size
1229 if 'size' in node:
1230 size = node['size']
1231
1232 if not _is_int_prop(size):
1233 raise ConfigError('"size" property of integer type object must be an integer')
1234
1235 if size < 1:
1236 raise ConfigError('invalid integer size: {}'.format(size))
1237
1238 obj.size = size
1239
1240 # align
1241 if 'align' in node:
1242 align = node['align']
1243
1244 if not _is_int_prop(align):
1245 raise ConfigError('"align" property of integer type object must be an integer')
1246
1247 if not _is_valid_alignment(align):
1248 raise ConfigError('invalid alignment: {}'.format(align))
1249
1250 obj.align = align
1251
1252 # signed
1253 if 'signed' in node:
1254 signed = node['signed']
1255
1256 if not _is_bool_prop(signed):
1257 raise ConfigError('"signed" property of integer type object must be a boolean')
1258
1259 obj.signed = signed
1260
1261 # byte order
1262 if 'byte-order' in node:
1263 byte_order = node['byte-order']
1264
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")')
1267
1268 byte_order = _byte_order_str_to_bo(byte_order)
1269
1270 if byte_order is None:
1271 raise ConfigError('invalid "byte-order" property in integer type object')
1272 else:
1273 byte_order = self._bo
1274
1275 obj.byte_order = byte_order
1276
1277 # base
1278 if 'base' in node:
1279 base = node['base']
1280
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")')
1283
1284 if base == 'bin':
1285 base = 2
1286 elif base == 'oct':
1287 base = 8
1288 elif base == 'dec':
1289 base = 10
1290 elif base == 'hex':
1291 base = 16
1292
1293 obj.base = base
1294
1295 # encoding
1296 if 'encoding' in node:
1297 encoding = node['encoding']
1298
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")')
1301
1302 encoding = _encoding_str_to_encoding(encoding)
1303
1304 if encoding is None:
1305 raise ConfigError('invalid "encoding" property in integer type object')
1306
1307 obj.encoding = encoding
1308
1309 # property mappings
1310 if 'property-mappings' in node:
1311 prop_mappings = node['property-mappings']
1312
1313 if not _is_array_prop(prop_mappings):
1314 raise ConfigError('"property-mappings" property of integer type object must be an array')
1315
1316 if len(prop_mappings) > 1:
1317 raise ConfigError('length of "property-mappings" array in integer type object must be 1')
1318
1319 del obj.property_mappings[:]
1320
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')
1324
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))
1327
1328 prop_type = prop_mapping['type']
1329
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))
1332
1333 if prop_type == 'clock':
1334 self._set_int_clock_prop_mapping(obj, prop_mapping)
1335 else:
1336 raise ConfigError('unknown property mapping type "{}" in integer type object\'s "property-mappings" array\'s element #{}'.format(prop_type, index))
1337
1338 return obj
1339
1340 def _create_float(self, obj, node):
1341 if obj is None:
1342 # create floating point number object
1343 obj = metadata.FloatingPoint()
1344
1345 unk_prop = _get_first_unknown_type_prop(node, [
1346 'size',
1347 'align',
1348 'byte-order',
1349 ])
1350
1351 if unk_prop:
1352 raise ConfigError('unknown floating point number type object property: "{}"'.format(unk_prop))
1353
1354 # size
1355 if 'size' in node:
1356 size = node['size']
1357
1358 if not _is_assoc_array_prop(size):
1359 raise ConfigError('"size" property of floating point number type object must be an associative array')
1360
1361 unk_prop = _get_first_unknown_prop(node, ['exp', 'mant'])
1362
1363 if 'exp' in size:
1364 exp = size['exp']
1365
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')
1368
1369 if exp < 1:
1370 raise ConfigError('invalid floating point number exponent size: {}')
1371
1372 obj.exp_size = exp
1373
1374 if 'mant' in size:
1375 mant = size['mant']
1376
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')
1379
1380 if mant < 1:
1381 raise ConfigError('invalid floating point number mantissa size: {}')
1382
1383 obj.mant_size = mant
1384
1385 # align
1386 if 'align' in node:
1387 align = node['align']
1388
1389 if not _is_int_prop(align):
1390 raise ConfigError('"align" property of floating point number type object must be an integer')
1391
1392 if not _is_valid_alignment(align):
1393 raise ConfigError('invalid alignment: {}'.format(align))
1394
1395 obj.align = align
1396
1397 # byte order
1398 if 'byte-order' in node:
1399 byte_order = node['byte-order']
1400
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")')
1403
1404 byte_order = _byte_order_str_to_bo(byte_order)
1405
1406 if byte_order is None:
1407 raise ConfigError('invalid "byte-order" property in floating point number type object')
1408 else:
1409 byte_order = self._bo
1410
1411 obj.byte_order = byte_order
1412
1413 return obj
1414
1415 def _create_enum(self, obj, node):
1416 if obj is None:
1417 # create enumeration object
1418 obj = metadata.Enum()
1419
1420 unk_prop = _get_first_unknown_type_prop(node, [
1421 'value-type',
1422 'members',
1423 ])
1424
1425 if unk_prop:
1426 raise ConfigError('unknown enumeration type object property: "{}"'.format(unk_prop))
1427
1428 # value type
1429 if 'value-type' in node:
1430 try:
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)
1434
1435 # members
1436 if 'members' in node:
1437 members_node = node['members']
1438
1439 if not _is_array_prop(members_node):
1440 raise ConfigError('"members" property of enumeration type object must be an array')
1441
1442 cur = 0
1443
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))
1447
1448 if _is_str_prop(m_node):
1449 label = m_node
1450 value = (cur, cur)
1451 cur += 1
1452 else:
1453 if 'label' not in m_node:
1454 raise ConfigError('missing "label" property in enumeration member #{}'.format(index))
1455
1456 label = m_node['label']
1457
1458 if not _is_str_prop(label):
1459 raise ConfigError('"label" property of enumeration member #{} must be a string'.format(index))
1460
1461 if 'value' not in m_node:
1462 raise ConfigError('missing "value" property in enumeration member ("{}")'.format(label))
1463
1464 value = m_node['value']
1465
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))
1468
1469 if _is_int_prop(value):
1470 cur = value + 1
1471 value = (value, value)
1472 else:
1473 if len(value) != 2:
1474 raise ConfigError('invalid enumeration member ("{}"): range must have exactly two items'.format(label))
1475
1476 mn = value[0]
1477 mx = value[1]
1478
1479 if mn > mx:
1480 raise ConfigError('invalid enumeration member ("{}"): invalid range ({} > {})'.format(label, mn, mx))
1481
1482 value = (mn, mx)
1483 cur = mx + 1
1484
1485 obj.members[label] = value
1486
1487 return obj
1488
1489 def _create_string(self, obj, node):
1490 if obj is None:
1491 # create string object
1492 obj = metadata.String()
1493
1494 unk_prop = _get_first_unknown_type_prop(node, [
1495 'encoding',
1496 ])
1497
1498 if unk_prop:
1499 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop))
1500
1501 # encoding
1502 if 'encoding' in node:
1503 encoding = node['encoding']
1504
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")')
1507
1508 encoding = _encoding_str_to_encoding(encoding)
1509
1510 if encoding is None:
1511 raise ConfigError('invalid "encoding" property in string type object')
1512
1513 obj.encoding = encoding
1514
1515 return obj
1516
1517 def _create_struct(self, obj, node):
1518 if obj is None:
1519 # create structure object
1520 obj = metadata.Struct()
1521
1522 unk_prop = _get_first_unknown_type_prop(node, [
1523 'min-align',
1524 'fields',
1525 ])
1526
1527 if unk_prop:
1528 raise ConfigError('unknown string type object property: "{}"'.format(unk_prop))
1529
1530 # minimum alignment
1531 if 'min-align' in node:
1532 min_align = node['min-align']
1533
1534 if not _is_int_prop(min_align):
1535 raise ConfigError('"min-align" property of structure type object must be an integer')
1536
1537 if not _is_valid_alignment(min_align):
1538 raise ConfigError('invalid minimum alignment: {}'.format(min_align))
1539
1540 obj.min_align = min_align
1541
1542 # fields
1543 if 'fields' in node:
1544 fields = node['fields']
1545
1546 if not _is_assoc_array_prop(fields):
1547 raise ConfigError('"fields" property of structure type object must be an associative array')
1548
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))
1552
1553 try:
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)
1557
1558 return obj
1559
1560 def _create_array(self, obj, node):
1561 if obj is None:
1562 # create array object
1563 obj = metadata.Array()
1564
1565 unk_prop = _get_first_unknown_type_prop(node, [
1566 'length',
1567 'element-type',
1568 ])
1569
1570 if unk_prop:
1571 raise ConfigError('unknown array type object property: "{}"'.format(unk_prop))
1572
1573 # length
1574 if 'length' in node:
1575 length = node['length']
1576
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')
1579
1580 if type(length) is int and length < 0:
1581 raise ConfigError('invalid static array length: {}'.format(length))
1582
1583 obj.length = length
1584
1585 # element type
1586 if 'element-type' in node:
1587 try:
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)
1591
1592 return obj
1593
1594 def _create_variant(self, obj, node):
1595 if obj is None:
1596 # create variant object
1597 obj = metadata.Variant()
1598
1599 unk_prop = _get_first_unknown_type_prop(node, [
1600 'tag',
1601 'types',
1602 ])
1603
1604 if unk_prop:
1605 raise ConfigError('unknown variant type object property: "{}"'.format(unk_prop))
1606
1607 # tag
1608 if 'tag' in node:
1609 tag = node['tag']
1610
1611 if not _is_str_prop(tag):
1612 raise ConfigError('"tag" property of variant type object must be a string')
1613
1614 # do not validate variant tag for the moment; will be done in a
1615 # second phase
1616 obj.tag = tag
1617
1618 # element type
1619 if 'types' in node:
1620 types = node['types']
1621
1622 if not _is_assoc_array_prop(types):
1623 raise ConfigError('"types" property of variant type object must be an associative array')
1624
1625 # do not validate type names for the moment; will be done in a
1626 # second phase
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))
1630
1631 try:
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)
1635
1636 return obj
1637
1638 def _create_type(self, type_node):
1639 if type(type_node) is str:
1640 t = self._lookup_type_alias(type_node)
1641
1642 if t is None:
1643 raise ConfigError('unknown type alias "{}"'.format(type_node))
1644
1645 return t
1646
1647 if not _is_assoc_array_prop(type_node):
1648 raise ConfigError('type objects must be associative arrays')
1649
1650 if 'inherit' in type_node and 'class' in type_node:
1651 raise ConfigError('cannot specify both "inherit" and "class" properties in type object')
1652
1653 if 'inherit' in type_node:
1654 inherit = type_node['inherit']
1655
1656 if not _is_str_prop(inherit):
1657 raise ConfigError('"inherit" property of type object must be a string')
1658
1659 base = self._lookup_type_alias(inherit)
1660
1661 if base is None:
1662 raise ConfigError('cannot inherit from type alias "{}": type alias does not exist'.format(inherit))
1663
1664 func = self._type_to_create_type_func[type(base)]
1665 else:
1666 if 'class' not in type_node:
1667 raise ConfigError('type objects which do not inherit must have a "class" property')
1668
1669 class_name = type_node['class']
1670
1671 if type(class_name) is not str:
1672 raise ConfigError('type objects\' "class" property must be a string')
1673
1674 if class_name not in self._class_name_to_create_type_func:
1675 raise ConfigError('unknown type class "{}"'.format(class_name))
1676
1677 base = None
1678 func = self._class_name_to_create_type_func[class_name]
1679
1680 return func(base, type_node)
1681
1682 def _register_type_aliases(self, metadata_node):
1683 self._tas = dict()
1684
1685 if 'type-aliases' not in metadata_node:
1686 return
1687
1688 ta_node = metadata_node['type-aliases']
1689
1690 if not _is_assoc_array_prop(ta_node):
1691 raise ConfigError('"type-aliases" property (metadata) must be an associative array')
1692
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))
1696
1697 try:
1698 t = self._create_type(ta_type)
1699 except Exception as e:
1700 raise ConfigError('cannot create type alias "{}"'.format(ta_name), e)
1701
1702 self._tas[ta_name] = t
1703
1704 def _create_clock(self, node):
1705 # create clock object
1706 clock = metadata.Clock()
1707 known_props = [
1708 'uuid',
1709 'description',
1710 'freq',
1711 'error-cycles',
1712 'offset',
1713 'absolute',
1714 'return-ctype',
1715 ]
1716
1717 if self._version >= 201:
1718 known_props.append('$return-ctype')
1719
1720 unk_prop = _get_first_unknown_prop(node, known_props)
1721
1722 if unk_prop:
1723 raise ConfigError('unknown clock object property: "{}"'.format(unk_prop))
1724
1725 # UUID
1726 if 'uuid' in node:
1727 uuidp = node['uuid']
1728
1729 if not _is_str_prop(uuidp):
1730 raise ConfigError('"uuid" property of clock object must be a string')
1731
1732 try:
1733 uuidp = uuid.UUID(uuidp)
1734 except:
1735 raise ConfigError('malformed UUID (clock object): "{}"'.format(uuidp))
1736
1737 clock.uuid = uuidp
1738
1739 # description
1740 if 'description' in node:
1741 desc = node['description']
1742
1743 if not _is_str_prop(desc):
1744 raise ConfigError('"description" property of clock object must be a string')
1745
1746 clock.description = desc
1747
1748 # frequency
1749 if 'freq' in node:
1750 freq = node['freq']
1751
1752 if not _is_int_prop(freq):
1753 raise ConfigError('"freq" property of clock object must be an integer')
1754
1755 if freq < 1:
1756 raise ConfigError('invalid clock frequency: {}'.format(freq))
1757
1758 clock.freq = freq
1759
1760 # error cycles
1761 if 'error-cycles' in node:
1762 error_cycles = node['error-cycles']
1763
1764 if not _is_int_prop(error_cycles):
1765 raise ConfigError('"error-cycles" property of clock object must be an integer')
1766
1767 if error_cycles < 0:
1768 raise ConfigError('invalid clock error cycles: {}'.format(error_cycles))
1769
1770 clock.error_cycles = error_cycles
1771
1772 # offset
1773 if 'offset' in node:
1774 offset = node['offset']
1775
1776 if not _is_assoc_array_prop(offset):
1777 raise ConfigError('"offset" property of clock object must be an associative array')
1778
1779 unk_prop = _get_first_unknown_prop(offset, ['cycles', 'seconds'])
1780
1781 if unk_prop:
1782 raise ConfigError('unknown clock object\'s offset property: "{}"'.format(unk_prop))
1783
1784 # cycles
1785 if 'cycles' in offset:
1786 offset_cycles = offset['cycles']
1787
1788 if not _is_int_prop(offset_cycles):
1789 raise ConfigError('"cycles" property of clock object\'s offset property must be an integer')
1790
1791 if offset_cycles < 0:
1792 raise ConfigError('invalid clock offset cycles: {}'.format(offset_cycles))
1793
1794 clock.offset_cycles = offset_cycles
1795
1796 # seconds
1797 if 'seconds' in offset:
1798 offset_seconds = offset['seconds']
1799
1800 if not _is_int_prop(offset_seconds):
1801 raise ConfigError('"seconds" property of clock object\'s offset property must be an integer')
1802
1803 if offset_seconds < 0:
1804 raise ConfigError('invalid clock offset seconds: {}'.format(offset_seconds))
1805
1806 clock.offset_seconds = offset_seconds
1807
1808 # absolute
1809 if 'absolute' in node:
1810 absolute = node['absolute']
1811
1812 if not _is_bool_prop(absolute):
1813 raise ConfigError('"absolute" property of clock object must be a boolean')
1814
1815 clock.absolute = absolute
1816
1817 # return C type:
1818 # v2.0: "return-ctype"
1819 # v2.1+: "$return-ctype"
1820 return_ctype_node = None
1821
1822 if self._version >= 200:
1823 if 'return-ctype' in node:
1824 return_ctype_prop = 'return-ctype'
1825 return_ctype_node = node[return_ctype_prop]
1826
1827 if self._version >= 201:
1828 if '$return-ctype' in node:
1829 if return_ctype_node is not None:
1830 raise ConfigError('cannot specify both "return-ctype" and "$return-ctype" properties of clock object: prefer "$return-ctype"')
1831
1832 return_ctype_prop = '$return-ctype'
1833 return_ctype_node = node[return_ctype_prop]
1834
1835 if return_ctype_node is not None:
1836 if not _is_str_prop(return_ctype_node):
1837 raise ConfigError('"{}" property of clock object must be a string'.format(return_ctype_prop))
1838
1839 clock.return_ctype = return_ctype_node
1840
1841 return clock
1842
1843 def _register_clocks(self, metadata_node):
1844 self._clocks = collections.OrderedDict()
1845
1846 if 'clocks' not in metadata_node:
1847 return
1848
1849 clocks_node = metadata_node['clocks']
1850
1851 if not _is_assoc_array_prop(clocks_node):
1852 raise ConfigError('"clocks" property (metadata) must be an associative array')
1853
1854 for clock_name, clock_node in clocks_node.items():
1855 if not is_valid_identifier(clock_name):
1856 raise ConfigError('invalid clock name: "{}"'.format(clock_name))
1857
1858 if clock_name in self._clocks:
1859 raise ConfigError('duplicate clock "{}"'.format(clock_name))
1860
1861 try:
1862 clock = self._create_clock(clock_node)
1863 except Exception as e:
1864 raise ConfigError('cannot create clock "{}"'.format(clock_name), e)
1865
1866 clock.name = clock_name
1867 self._clocks[clock_name] = clock
1868
1869 def _create_env(self, metadata_node):
1870 env = collections.OrderedDict()
1871
1872 if 'env' not in metadata_node:
1873 return env
1874
1875 env_node = metadata_node['env']
1876
1877 if not _is_assoc_array_prop(env_node):
1878 raise ConfigError('"env" property (metadata) must be an associative array')
1879
1880 for env_name, env_value in env_node.items():
1881 if env_name in env:
1882 raise ConfigError('duplicate environment variable "{}"'.format(env_name))
1883
1884 if not is_valid_identifier(env_name):
1885 raise ConfigError('invalid environment variable name: "{}"'.format(env_name))
1886
1887 if not _is_int_prop(env_value) and not _is_str_prop(env_value):
1888 raise ConfigError('invalid environment variable value ("{}"): expecting integer or string'.format(env_name))
1889
1890 env[env_name] = env_value
1891
1892 return env
1893
1894 def _register_log_levels(self, metadata_node):
1895 self._log_levels = dict()
1896
1897 if 'log-levels' not in metadata_node:
1898 return
1899
1900 log_levels_node = metadata_node['log-levels']
1901
1902 if not _is_assoc_array_prop(log_levels_node):
1903 raise ConfigError('"log-levels" property (metadata) must be an associative array')
1904
1905 for ll_name, ll_value in log_levels_node.items():
1906 if ll_name in self._log_levels:
1907 raise ConfigError('duplicate log level entry "{}"'.format(ll_name))
1908
1909 if not _is_int_prop(ll_value):
1910 raise ConfigError('invalid log level entry ("{}"): expecting an integer'.format(ll_name))
1911
1912 self._log_levels[ll_name] = ll_value
1913
1914 def _create_trace(self, metadata_node):
1915 # create trace object
1916 trace = metadata.Trace()
1917 trace_node = metadata_node['trace']
1918 unk_prop = _get_first_unknown_prop(trace_node, [
1919 'byte-order',
1920 'uuid',
1921 'packet-header-type',
1922 ])
1923
1924 if unk_prop:
1925 raise ConfigError('unknown trace object property: "{}"'.format(unk_prop))
1926
1927 # set byte order (already parsed)
1928 trace.byte_order = self._bo
1929
1930 # UUID
1931 if 'uuid' in trace_node:
1932 uuidp = trace_node['uuid']
1933
1934 if not _is_str_prop(uuidp):
1935 raise ConfigError('"uuid" property of trace object must be a string')
1936
1937 if uuidp == 'auto':
1938 uuidp = uuid.uuid1()
1939 else:
1940 try:
1941 uuidp = uuid.UUID(uuidp)
1942 except:
1943 raise ConfigError('malformed UUID (trace object): "{}"'.format(uuidp))
1944
1945 trace.uuid = uuidp
1946
1947 # packet header type
1948 if 'packet-header-type' in trace_node:
1949 try:
1950 ph_type = self._create_type(trace_node['packet-header-type'])
1951 except Exception as e:
1952 raise ConfigError('cannot create packet header type (trace)', e)
1953
1954 trace.packet_header_type = ph_type
1955
1956 return trace
1957
1958 def _lookup_log_level(self, ll):
1959 if _is_int_prop(ll):
1960 return ll
1961 elif _is_str_prop(ll) and ll in self._log_levels:
1962 return self._log_levels[ll]
1963
1964 def _create_event(self, event_node):
1965 event = metadata.Event()
1966 unk_prop = _get_first_unknown_prop(event_node, [
1967 'log-level',
1968 'context-type',
1969 'payload-type',
1970 ])
1971
1972 if unk_prop:
1973 raise ConfigError('unknown event object property: "{}"'.format(unk_prop))
1974
1975 if not _is_assoc_array_prop(event_node):
1976 raise ConfigError('event objects must be associative arrays')
1977
1978 if 'log-level' in event_node:
1979 ll = self._lookup_log_level(event_node['log-level'])
1980
1981 if ll is None:
1982 raise ConfigError('invalid "log-level" property')
1983
1984 event.log_level = ll
1985
1986 if 'context-type' in event_node:
1987 try:
1988 t = self._create_type(event_node['context-type'])
1989 except Exception as e:
1990 raise ConfigError('cannot create event\'s context type object', e)
1991
1992 event.context_type = t
1993
1994 if 'payload-type' not in event_node:
1995 raise ConfigError('missing "payload-type" property in event object')
1996
1997 try:
1998 t = self._create_type(event_node['payload-type'])
1999 except Exception as e:
2000 raise ConfigError('cannot create event\'s payload type object', e)
2001
2002 event.payload_type = t
2003
2004 return event
2005
2006 def _create_stream(self, stream_node):
2007 stream = metadata.Stream()
2008 unk_prop = _get_first_unknown_prop(stream_node, [
2009 'packet-context-type',
2010 'event-header-type',
2011 'event-context-type',
2012 'events',
2013 ])
2014
2015 if unk_prop:
2016 raise ConfigError('unknown stream object property: "{}"'.format(unk_prop))
2017
2018 if not _is_assoc_array_prop(stream_node):
2019 raise ConfigError('stream objects must be associative arrays')
2020
2021 if 'packet-context-type' in stream_node:
2022 try:
2023 t = self._create_type(stream_node['packet-context-type'])
2024 except Exception as e:
2025 raise ConfigError('cannot create stream\'s packet context type object', e)
2026
2027 stream.packet_context_type = t
2028
2029 if 'event-header-type' in stream_node:
2030 try:
2031 t = self._create_type(stream_node['event-header-type'])
2032 except Exception as e:
2033 raise ConfigError('cannot create stream\'s event header type object', e)
2034
2035 stream.event_header_type = t
2036
2037 if 'event-context-type' in stream_node:
2038 try:
2039 t = self._create_type(stream_node['event-context-type'])
2040 except Exception as e:
2041 raise ConfigError('cannot create stream\'s event context type object', e)
2042
2043 stream.event_context_type = t
2044
2045 if 'events' not in stream_node:
2046 raise ConfigError('missing "events" property in stream object')
2047
2048 events = stream_node['events']
2049
2050 if not _is_assoc_array_prop(events):
2051 raise ConfigError('"events" property of stream object must be an associative array')
2052
2053 if not events:
2054 raise ConfigError('at least one event is needed within a stream object')
2055
2056 cur_id = 0
2057
2058 for ev_name, ev_node in events.items():
2059 try:
2060 ev = self._create_event(ev_node)
2061 except Exception as e:
2062 raise ConfigError('cannot create event "{}"'.format(ev_name), e)
2063
2064 ev.id = cur_id
2065 ev.name = ev_name
2066 stream.events[ev_name] = ev
2067 cur_id += 1
2068
2069 return stream
2070
2071 def _create_streams(self, metadata_node):
2072 streams = collections.OrderedDict()
2073
2074 if 'streams' not in metadata_node:
2075 raise ConfigError('missing "streams" property (metadata)')
2076
2077 streams_node = metadata_node['streams']
2078
2079 if not _is_assoc_array_prop(streams_node):
2080 raise ConfigError('"streams" property (metadata) must be an associative array')
2081
2082 if not streams_node:
2083 raise ConfigError('at least one stream is needed (metadata)')
2084
2085 cur_id = 0
2086
2087 for stream_name, stream_node in streams_node.items():
2088 try:
2089 stream = self._create_stream(stream_node)
2090 except Exception as e:
2091 raise ConfigError('cannot create stream "{}"'.format(stream_name), e)
2092
2093 stream.id = cur_id
2094 stream.name = str(stream_name)
2095 streams[stream_name] = stream
2096 cur_id += 1
2097
2098 return streams
2099
2100 def _create_metadata(self, root):
2101 meta = metadata.Metadata()
2102
2103 if 'metadata' not in root:
2104 raise ConfigError('missing "metadata" property (root)')
2105
2106 metadata_node = root['metadata']
2107 unk_prop = _get_first_unknown_prop(metadata_node, [
2108 'type-aliases',
2109 'log-levels',
2110 'trace',
2111 'env',
2112 'clocks',
2113 'streams',
2114 ])
2115
2116 if unk_prop:
2117 raise ConfigError('unknown metadata property: "{}"'.format(unk_prop))
2118
2119 if not _is_assoc_array_prop(metadata_node):
2120 raise ConfigError('"metadata" property (root) must be an associative array')
2121
2122 self._set_byte_order(metadata_node)
2123 self._register_clocks(metadata_node)
2124 meta.clocks = self._clocks
2125 self._register_type_aliases(metadata_node)
2126 meta.env = self._create_env(metadata_node)
2127 meta.trace = self._create_trace(metadata_node)
2128 self._register_log_levels(metadata_node)
2129 meta.streams = self._create_streams(metadata_node)
2130
2131 return meta
2132
2133 def _get_version(self, root):
2134 if 'version' not in root:
2135 raise ConfigError('missing "version" property (root)')
2136
2137 version_node = root['version']
2138
2139 if not _is_str_prop(version_node):
2140 raise ConfigError('"version" property (root) must be a string')
2141
2142 version_node = version_node.strip()
2143
2144 if version_node not in ['2.0', '2.1']:
2145 raise ConfigError('unsupported version ({}): versions 2.0 and 2.1 are supported'.format(version_node))
2146
2147 # convert version string to comparable version integer
2148 parts = version_node.split('.')
2149 version = int(parts[0]) * 100 + int(parts[1])
2150
2151 return version
2152
2153 def _get_prefix(self, root):
2154 if 'prefix' not in root:
2155 return 'barectf_'
2156
2157 prefix_node = root['prefix']
2158
2159 if not _is_str_prop(prefix_node):
2160 raise ConfigError('"prefix" property (root) must be a string')
2161
2162 if not is_valid_identifier(prefix_node):
2163 raise ConfigError('"prefix" property (root) must be a valid C identifier')
2164
2165 return prefix_node
2166
2167 def _yaml_ordered_load(self, stream):
2168 class OLoader(yaml.Loader):
2169 pass
2170
2171 def construct_mapping(loader, node):
2172 loader.flatten_mapping(node)
2173
2174 return collections.OrderedDict(loader.construct_pairs(node))
2175
2176 OLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
2177 construct_mapping)
2178
2179 return yaml.load(stream, OLoader)
2180
2181 def parse(self, yml):
2182 try:
2183 root = self._yaml_ordered_load(yml)
2184 except Exception as e:
2185 raise ConfigError('cannot parse YAML input', e)
2186
2187 if not _is_assoc_array_prop(root):
2188 raise ConfigError('root must be an associative array')
2189
2190 self._version = self._get_version(root)
2191 meta = self._create_metadata(root)
2192 prefix = self._get_prefix(root)
2193
2194 return Config(self._version, prefix, meta)
2195
2196
2197 def from_yaml(yml):
2198 parser = _YamlConfigParser()
2199 cfg = parser.parse(yml)
2200
2201 return cfg
2202
2203
2204 def from_yaml_file(path):
2205 try:
2206 with open(path) as f:
2207 return from_yaml(f.read())
2208 except Exception as e:
2209 raise ConfigError('cannot create configuration from YAML file'.format(e), e)
This page took 0.107746 seconds and 4 git commands to generate.