Fix: remove import cycles from `config.py`
[deliverable/barectf.git] / barectf / config.py
1 # The MIT License (MIT)
2 #
3 # Copyright (c) 2015-2020 Philippe Proulx <pproulx@efficios.com>
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 import barectf.version as barectf_version
25 import collections.abc
26 import collections
27 import datetime
28 import enum
29
30
31 @enum.unique
32 class ByteOrder(enum.Enum):
33 LITTLE_ENDIAN = 'le'
34 BIG_ENDIAN = 'be'
35
36
37 class _FieldType:
38 @property
39 def alignment(self):
40 raise NotImplementedError
41
42
43 class _BitArrayFieldType(_FieldType):
44 def __init__(self, size, byte_order=None, alignment=1):
45 self._size = size
46 self._byte_order = byte_order
47 self._alignment = alignment
48
49 @property
50 def size(self):
51 return self._size
52
53 @property
54 def byte_order(self):
55 return self._byte_order
56
57 @property
58 def alignment(self):
59 return self._alignment
60
61
62 class DisplayBase(enum.Enum):
63 BINARY = 2
64 OCTAL = 8
65 DECIMAL = 10
66 HEXADECIMAL = 16
67
68
69 class _IntegerFieldType(_BitArrayFieldType):
70 def __init__(self, size, byte_order=None, alignment=None,
71 preferred_display_base=DisplayBase.DECIMAL):
72 effective_alignment = 1
73
74 if alignment is None and size % 8 == 0:
75 effective_alignment = 8
76
77 super().__init__(size, byte_order, effective_alignment)
78 self._preferred_display_base = preferred_display_base
79
80 @property
81 def preferred_display_base(self):
82 return self._preferred_display_base
83
84
85 class UnsignedIntegerFieldType(_IntegerFieldType):
86 def __init__(self, *args):
87 super().__init__(*args)
88 self._mapped_clk_type_name = None
89
90
91 class SignedIntegerFieldType(_IntegerFieldType):
92 pass
93
94
95 class EnumerationFieldTypeMappingRange:
96 def __init__(self, lower, upper):
97 self._lower = lower
98 self._upper = upper
99
100 @property
101 def lower(self):
102 return self._lower
103
104 @property
105 def upper(self):
106 return self._upper
107
108 def __eq__(self, other):
109 if type(other) is not type(self):
110 return False
111
112 return (self._lower, self._upper) == (other._lower, other._upper)
113
114 def __hash__(self):
115 return hash((self._lower, self._upper))
116
117 def contains(self, value):
118 return self._lower <= value <= self._upper
119
120
121 class EnumerationFieldTypeMapping:
122 def __init__(self, ranges):
123 self._ranges = frozenset(ranges)
124
125 @property
126 def ranges(self):
127 return self._ranges
128
129 def ranges_contain_value(self, value):
130 return any([rg.contains(value) for rg in self._ranges])
131
132
133 class EnumerationFieldTypeMappings(collections.abc.Mapping):
134 def __init__(self, mappings):
135 self._mappings = {label: mapping for label, mapping in mappings.items()}
136
137 def __getitem__(self, key):
138 return self._mappings[key]
139
140 def __iter__(self):
141 return iter(self._mappings)
142
143 def __len__(self):
144 return len(self._mappings)
145
146
147 class _EnumerationFieldType(_IntegerFieldType):
148 def __init__(self, size, byte_order=None, alignment=None,
149 preferred_display_base=DisplayBase.DECIMAL, mappings=None):
150 super().__init__(size, byte_order, alignment, preferred_display_base)
151 self._mappings = EnumerationFieldTypeMappings({})
152
153 if mappings is not None:
154 self._mappings = EnumerationFieldTypeMappings(mappings)
155
156 @property
157 def mappings(self):
158 return self._mappings
159
160 def labels_for_value(self, value):
161 labels = set()
162
163 for label, mapping in self._mappings.items():
164 if mapping.ranges_contain_value(value):
165 labels.add(label)
166
167 return labels
168
169
170 class UnsignedEnumerationFieldType(_EnumerationFieldType, UnsignedIntegerFieldType):
171 pass
172
173
174 class SignedEnumerationFieldType(_EnumerationFieldType, SignedIntegerFieldType):
175 pass
176
177
178 class RealFieldType(_BitArrayFieldType):
179 pass
180
181
182 class StringFieldType(_FieldType):
183 @property
184 def alignment(self):
185 return 8
186
187
188 class _ArrayFieldType(_FieldType):
189 def __init__(self, element_field_type):
190 self._element_field_type = element_field_type
191
192 @property
193 def element_field_type(self):
194 return self._element_field_type
195
196 @property
197 def alignment(self):
198 return self._element_field_type.alignment
199
200
201 class StaticArrayFieldType(_ArrayFieldType):
202 def __init__(self, length, element_field_type):
203 super().__init__(element_field_type)
204 self._length = length
205
206 @property
207 def length(self):
208 return self._length
209
210
211 class StructureFieldTypeMember:
212 def __init__(self, field_type):
213 self._field_type = field_type
214
215 @property
216 def field_type(self):
217 return self._field_type
218
219
220 class StructureFieldTypeMembers(collections.abc.Mapping):
221 def __init__(self, members):
222 self._members = collections.OrderedDict()
223
224 for name, member in members.items():
225 assert type(member) is StructureFieldTypeMember
226 self._members[name] = member
227
228 def __getitem__(self, key):
229 return self._members[key]
230
231 def __iter__(self):
232 return iter(self._members)
233
234 def __len__(self):
235 return len(self._members)
236
237
238 class StructureFieldType(_FieldType):
239 def __init__(self, minimum_alignment=1, members=None):
240 self._minimum_alignment = minimum_alignment
241 self._members = StructureFieldTypeMembers({})
242
243 if members is not None:
244 self._members = StructureFieldTypeMembers(members)
245
246 self._set_alignment()
247
248 def _set_alignment(self):
249 self._alignment = self._minimum_alignment
250
251 for member in self._members.values():
252 if member.field_type.alignment > self._alignment:
253 self._alignment = member.field_type.alignment
254
255 @property
256 def minimum_alignment(self):
257 return self._minimum_alignment
258
259 @property
260 def alignment(self):
261 return self._alignment
262
263 @property
264 def members(self):
265 return self._members
266
267
268 class _UniqueByName:
269 def __eq__(self, other):
270 if type(other) is not type(self):
271 return False
272
273 return self._name == other._name
274
275 def __lt__(self, other):
276 assert type(self) is type(other)
277 return self._name < other._name
278
279 def __hash__(self):
280 return hash(self._name)
281
282
283 class EventType(_UniqueByName):
284 def __init__(self, name, log_level=None, specific_context_field_type=None,
285 payload_field_type=None):
286 self._id = None
287 self._name = name
288 self._log_level = log_level
289 self._specific_context_field_type = specific_context_field_type
290 self._payload_field_type = payload_field_type
291
292 @property
293 def id(self):
294 return self._id
295
296 @property
297 def name(self):
298 return self._name
299
300 @property
301 def log_level(self):
302 return self._log_level
303
304 @property
305 def specific_context_field_type(self):
306 return self._specific_context_field_type
307
308 @property
309 def payload_field_type(self):
310 return self._payload_field_type
311
312
313 class ClockTypeOffset:
314 def __init__(self, seconds=0, cycles=0):
315 self._seconds = seconds
316 self._cycles = cycles
317
318 @property
319 def seconds(self):
320 return self._seconds
321
322 @property
323 def cycles(self):
324 return self._cycles
325
326
327 class ClockType(_UniqueByName):
328 def __init__(self, name, frequency=int(1e9), uuid=None, description=None, precision=0,
329 offset=None, origin_is_unix_epoch=False):
330 self._name = name
331 self._frequency = frequency
332 self._uuid = uuid
333 self._description = description
334 self._precision = precision
335 self._offset = ClockTypeOffset()
336
337 if offset is not None:
338 self._offset = offset
339
340 self._origin_is_unix_epoch = origin_is_unix_epoch
341
342 @property
343 def name(self):
344 return self._name
345
346 @property
347 def frequency(self):
348 return self._frequency
349
350 @property
351 def uuid(self):
352 return self._uuid
353
354 @property
355 def description(self):
356 return self._description
357
358 @property
359 def precision(self):
360 return self._precision
361
362 @property
363 def offset(self):
364 return self._offset
365
366 @property
367 def origin_is_unix_epoch(self):
368 return self._origin_is_unix_epoch
369
370
371 DEFAULT_FIELD_TYPE = 'default'
372
373
374 class StreamTypePacketFeatures:
375 def __init__(self, total_size_field_type=DEFAULT_FIELD_TYPE,
376 content_size_field_type=DEFAULT_FIELD_TYPE, beginning_time_field_type=None,
377 end_time_field_type=None, discarded_events_counter_field_type=None):
378 def get_ft(user_ft):
379 if user_ft == DEFAULT_FIELD_TYPE:
380 return UnsignedIntegerFieldType(64)
381
382 return user_ft
383
384 self._total_size_field_type = get_ft(total_size_field_type)
385 self._content_size_field_type = get_ft(content_size_field_type)
386 self._beginning_time_field_type = get_ft(beginning_time_field_type)
387 self._end_time_field_type = get_ft(end_time_field_type)
388 self._discarded_events_counter_field_type = get_ft(discarded_events_counter_field_type)
389
390 @property
391 def total_size_field_type(self):
392 return self._total_size_field_type
393
394 @property
395 def content_size_field_type(self):
396 return self._content_size_field_type
397
398 @property
399 def beginning_time_field_type(self):
400 return self._beginning_time_field_type
401
402 @property
403 def end_time_field_type(self):
404 return self._end_time_field_type
405
406 @property
407 def discarded_events_counter_field_type(self):
408 return self._discarded_events_counter_field_type
409
410
411 class StreamTypeEventFeatures:
412 def __init__(self, type_id_field_type=DEFAULT_FIELD_TYPE, time_field_type=None):
413 def get_ft(user_field_type):
414 if user_field_type == DEFAULT_FIELD_TYPE:
415 return UnsignedIntegerFieldType(64)
416
417 return user_field_type
418
419 self._type_id_field_type = get_ft(type_id_field_type)
420 self._time_field_type = get_ft(time_field_type)
421
422 @property
423 def type_id_field_type(self):
424 return self._type_id_field_type
425
426 @property
427 def time_field_type(self):
428 return self._time_field_type
429
430
431 class StreamTypeFeatures:
432 def __init__(self, packet_features=None, event_features=None):
433 self._packet_features = StreamTypePacketFeatures()
434
435 if packet_features is not None:
436 self._packet_features = packet_features
437
438 self._event_features = StreamTypeEventFeatures()
439
440 if event_features is not None:
441 self._event_features = event_features
442
443 @property
444 def packet_features(self):
445 return self._packet_features
446
447 @property
448 def event_features(self):
449 return self._event_features
450
451
452 class StreamType(_UniqueByName):
453 def __init__(self, name, event_types, default_clock_type=None, features=None,
454 packet_context_field_type_extra_members=None,
455 event_common_context_field_type=None):
456 self._id = None
457 self._name = name
458 self._default_clock_type = default_clock_type
459 self._event_common_context_field_type = event_common_context_field_type
460 self._event_types = frozenset(event_types)
461
462 # assign unique IDs
463 for index, ev_type in enumerate(sorted(self._event_types, key=lambda evt: evt.name)):
464 assert ev_type._id is None
465 ev_type._id = index
466
467 self._set_features(features)
468 self._packet_context_field_type_extra_members = StructureFieldTypeMembers({})
469
470 if packet_context_field_type_extra_members is not None:
471 self._packet_context_field_type_extra_members = StructureFieldTypeMembers(packet_context_field_type_extra_members)
472
473 self._set_pkt_ctx_ft()
474 self._set_ev_header_ft()
475
476 def _set_features(self, features):
477 if features is not None:
478 self._features = features
479 return
480
481 ev_time_ft = None
482 pkt_beginning_time_ft = None
483 pkt_end_time_ft = None
484
485 if self._default_clock_type is not None:
486 # Automatic time field types because the stream type has a
487 # default clock type.
488 ev_time_ft = DEFAULT_FIELD_TYPE
489 pkt_beginning_time_ft = DEFAULT_FIELD_TYPE
490 pkt_end_time_ft = DEFAULT_FIELD_TYPE
491
492 self._features = StreamTypeFeatures(StreamTypePacketFeatures(beginning_time_field_type=pkt_beginning_time_ft,
493 end_time_field_type=pkt_end_time_ft),
494 StreamTypeEventFeatures(time_field_type=ev_time_ft))
495
496 def _set_ft_mapped_clk_type_name(self, ft):
497 if ft is None:
498 return
499
500 if self._default_clock_type is not None:
501 assert isinstance(ft, UnsignedIntegerFieldType)
502 ft._mapped_clk_type_name = self._default_clock_type.name
503
504 def _set_pkt_ctx_ft(self):
505 def add_member_if_exists(name, ft, set_mapped_clk_type_name=False):
506 nonlocal members
507
508 if ft is not None:
509 if set_mapped_clk_type_name:
510 self._set_ft_mapped_clk_type_name(ft)
511
512 members[name] = StructureFieldTypeMember(ft)
513
514 members = collections.OrderedDict([
515 (
516 'packet_size',
517 StructureFieldTypeMember(self._features.packet_features.total_size_field_type)
518 ),
519 (
520 'content_size',
521 StructureFieldTypeMember(self._features.packet_features.content_size_field_type)
522 )
523 ])
524
525 add_member_if_exists('timestamp_begin',
526 self._features.packet_features.beginning_time_field_type, True)
527 add_member_if_exists('timestamp_end', self._features.packet_features.end_time_field_type,
528 True)
529 add_member_if_exists('events_discarded',
530 self._features.packet_features.discarded_events_counter_field_type)
531
532 if self._packet_context_field_type_extra_members is not None:
533 for name, field_type in self._packet_context_field_type_extra_members.items():
534 assert name not in members
535 members[name] = field_type
536
537 self._pkt_ctx_ft = StructureFieldType(8, members)
538
539 def _set_ev_header_ft(self):
540 members = collections.OrderedDict()
541
542 if self._features.event_features.type_id_field_type is not None:
543 members['id'] = StructureFieldTypeMember(self._features.event_features.type_id_field_type)
544
545 if self._features.event_features.time_field_type is not None:
546 ft = self._features.event_features.time_field_type
547 self._set_ft_mapped_clk_type_name(ft)
548 members['timestamp'] = StructureFieldTypeMember(ft)
549
550 self._ev_header_ft = StructureFieldType(8, members)
551
552 @property
553 def id(self):
554 return self._id
555
556 @property
557 def name(self):
558 return self._name
559
560 @property
561 def default_clock_type(self):
562 return self._default_clock_type
563
564 @property
565 def features(self):
566 return self._features
567
568 @property
569 def packet_context_field_type_extra_members(self):
570 return self._packet_context_field_type_extra_members
571
572 @property
573 def event_common_context_field_type(self):
574 return self._event_common_context_field_type
575
576 @property
577 def event_types(self):
578 return self._event_types
579
580
581 class TraceTypeFeatures:
582 def __init__(self, magic_field_type=DEFAULT_FIELD_TYPE, uuid_field_type=None,
583 stream_type_id_field_type=DEFAULT_FIELD_TYPE):
584 def get_field_type(user_field_type, default_field_type):
585 if user_field_type == DEFAULT_FIELD_TYPE:
586 return default_field_type
587
588 return user_field_type
589
590 self._magic_field_type = get_field_type(magic_field_type, UnsignedIntegerFieldType(32))
591 self._uuid_field_type = get_field_type(uuid_field_type,
592 StaticArrayFieldType(16, UnsignedIntegerFieldType(8)))
593 self._stream_type_id_field_type = get_field_type(stream_type_id_field_type,
594 UnsignedIntegerFieldType(64))
595
596 @property
597 def magic_field_type(self):
598 return self._magic_field_type
599
600 @property
601 def uuid_field_type(self):
602 return self._uuid_field_type
603
604 @property
605 def stream_type_id_field_type(self):
606 return self._stream_type_id_field_type
607
608
609 class TraceType:
610 def __init__(self, stream_types, default_byte_order, uuid=None, features=None):
611 self._default_byte_order = default_byte_order
612 self._stream_types = frozenset(stream_types)
613
614 # assign unique IDs
615 for index, stream_type in enumerate(sorted(self._stream_types, key=lambda st: st.name)):
616 assert stream_type._id is None
617 stream_type._id = index
618
619 self._uuid = uuid
620 self._set_features(features)
621 self._set_pkt_header_ft()
622 self._set_fts_effective_byte_order()
623
624 def _set_features(self, features):
625 if features is not None:
626 self._features = features
627 return
628
629 # automatic UUID field type because the trace type has a UUID
630 uuid_ft = None if self._uuid is None else DEFAULT_FIELD_TYPE
631 self._features = TraceTypeFeatures(uuid_field_type=uuid_ft)
632
633 def _set_pkt_header_ft(self):
634 def add_member_if_exists(name, field_type):
635 nonlocal members
636
637 if field_type is not None:
638 members[name] = StructureFieldTypeMember(field_type)
639
640 members = collections.OrderedDict()
641 add_member_if_exists('magic', self._features.magic_field_type)
642 add_member_if_exists('uuid', self._features.uuid_field_type)
643 add_member_if_exists('stream_id', self._features.stream_type_id_field_type)
644 self._pkt_header_ft = StructureFieldType(8, members)
645
646 def _set_fts_effective_byte_order(self):
647 def set_ft_effective_byte_order(ft):
648 if ft is None:
649 return
650
651 if isinstance(ft, _BitArrayFieldType):
652 if ft._byte_order is None:
653 assert self._default_byte_order is not None
654 ft._byte_order = self._default_byte_order
655 elif isinstance(ft, StaticArrayFieldType):
656 set_ft_effective_byte_order(ft.element_field_type)
657 elif isinstance(ft, StructureFieldType):
658 for member in ft.members.values():
659 set_ft_effective_byte_order(member.field_type)
660
661 # packet header field type
662 set_ft_effective_byte_order(self._pkt_header_ft)
663
664 # stream type field types
665 for stream_type in self._stream_types:
666 set_ft_effective_byte_order(stream_type._pkt_ctx_ft)
667 set_ft_effective_byte_order(stream_type._ev_header_ft)
668 set_ft_effective_byte_order(stream_type._event_common_context_field_type)
669
670 # event type field types
671 for ev_type in stream_type.event_types:
672 set_ft_effective_byte_order(ev_type._specific_context_field_type)
673 set_ft_effective_byte_order(ev_type._payload_field_type)
674
675 @property
676 def default_byte_order(self):
677 return self._default_byte_order
678
679 @property
680 def uuid(self):
681 return self._uuid
682
683 @property
684 def stream_types(self):
685 return self._stream_types
686
687 def stream_type(self, name):
688 for cand_stream_type in self._stream_types:
689 if cand_stream_type.name == name:
690 return cand_stream_type
691
692 @property
693 def features(self):
694 return self._features
695
696
697 class TraceEnvironment(collections.abc.Mapping):
698 def __init__(self, environment):
699 self._env = {name: value for name, value in environment.items()}
700
701 def __getitem__(self, key):
702 return self._env[key]
703
704 def __iter__(self):
705 return iter(self._env)
706
707 def __len__(self):
708 return len(self._env)
709
710
711 class Trace:
712 def __init__(self, type, environment=None):
713 self._type = type
714 self._set_env(environment)
715
716 def _set_env(self, environment):
717 init_env = collections.OrderedDict([
718 ('domain', 'bare'),
719 ('tracer_name', 'barectf'),
720 ('tracer_major', barectf_version.__major_version__),
721 ('tracer_minor', barectf_version.__minor_version__),
722 ('tracer_patch', barectf_version.__patch_version__),
723 ('barectf_gen_date', str(datetime.datetime.now().isoformat())),
724 ])
725
726 if environment is None:
727 environment = {}
728
729 init_env.update(environment)
730 self._env = TraceEnvironment(init_env)
731
732 @property
733 def type(self):
734 return self._type
735
736 @property
737 def environment(self):
738 return self._env
739
740
741 class ClockTypeCTypes(collections.abc.Mapping):
742 def __init__(self, c_types):
743 self._c_types = {clk_type: c_type for clk_type, c_type in c_types.items()}
744
745 def __getitem__(self, key):
746 return self._c_types[key]
747
748 def __iter__(self):
749 return iter(self._c_types)
750
751 def __len__(self):
752 return len(self._c_types)
753
754
755 class ConfigurationCodeGenerationHeaderOptions:
756 def __init__(self, identifier_prefix_definition=False,
757 default_stream_type_name_definition=False):
758 self._identifier_prefix_definition = identifier_prefix_definition
759 self._default_stream_type_name_definition = default_stream_type_name_definition
760
761 @property
762 def identifier_prefix_definition(self):
763 return self._identifier_prefix_definition
764
765 @property
766 def default_stream_type_name_definition(self):
767 return self._default_stream_type_name_definition
768
769
770 class ConfigurationCodeGenerationOptions:
771 def __init__(self, identifier_prefix='barectf_', file_name_prefix='barectf',
772 default_stream_type=None, header_options=None, clock_type_c_types=None):
773 self._identifier_prefix = identifier_prefix
774 self._file_name_prefix = file_name_prefix
775 self._default_stream_type = default_stream_type
776
777 self._header_options = ConfigurationCodeGenerationHeaderOptions()
778
779 if header_options is not None:
780 self._header_options = header_options
781
782 self._clock_type_c_types = ClockTypeCTypes({})
783
784 if clock_type_c_types is not None:
785 self._clock_type_c_types = ClockTypeCTypes(clock_type_c_types)
786
787 @property
788 def identifier_prefix(self):
789 return self._identifier_prefix
790
791 @property
792 def file_name_prefix(self):
793 return self._file_name_prefix
794
795 @property
796 def default_stream_type(self):
797 return self._default_stream_type
798
799 @property
800 def header_options(self):
801 return self._header_options
802
803 @property
804 def clock_type_c_types(self):
805 return self._clock_type_c_types
806
807
808 class ConfigurationOptions:
809 def __init__(self, code_generation_options=None):
810 self._code_generation_options = ConfigurationCodeGenerationOptions()
811
812 if code_generation_options is not None:
813 self._code_generation_options = code_generation_options
814
815 @property
816 def code_generation_options(self):
817 return self._code_generation_options
818
819
820 class Configuration:
821 def __init__(self, trace, options=None):
822 self._trace = trace
823 self._options = ConfigurationOptions()
824
825 if options is not None:
826 self._options = options
827
828 clk_type_c_types = self._options.code_generation_options.clock_type_c_types
829
830 for stream_type in trace.type.stream_types:
831 def_clk_type = stream_type.default_clock_type
832
833 if def_clk_type is None:
834 continue
835
836 if def_clk_type not in clk_type_c_types:
837 clk_type_c_types._c_types[def_clk_type] = 'uint32_t'
838
839 @property
840 def trace(self):
841 return self._trace
842
843 @property
844 def options(self):
845 return self._options
This page took 0.047578 seconds and 5 git commands to generate.