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