| # Copyright 2019 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ Classes representing perfetto trace protobuf messages. |
| |
| This module makes use of neither python-protobuf library nor python classes |
| compiled from .proto definitions, because currently there's no way to |
| deploy those to all the places where telemetry is run. |
| |
| TODO(crbug.com/944078): Remove this module after the python-protobuf library |
| is deployed to all the bots. |
| |
| Definitions of perfetto messages can be found here: |
| https://android.googlesource.com/platform/external/perfetto/+/refs/heads/master/protos/perfetto/trace/ |
| """ |
| |
| import encoder |
| import wire_format |
| |
| |
| class TracePacket(object): |
| def __init__(self): |
| self.clock_snapshot = None |
| self.timestamp = None |
| self.timestamp_clock_id = None |
| self.interned_data = None |
| self.thread_descriptor = None |
| self.incremental_state_cleared = None |
| self.chrome_event = None |
| self.track_event = None |
| self.trusted_packet_sequence_id = None |
| self.chrome_benchmark_metadata = None |
| |
| def encode(self): |
| parts = [] |
| if self.chrome_event is not None: |
| tag = encoder.TagBytes(5, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = self.chrome_event.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| if self.clock_snapshot is not None: |
| tag = encoder.TagBytes(6, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = self.clock_snapshot.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| if self.timestamp is not None: |
| writer = encoder.UInt64Encoder(8, False, False) |
| writer(parts.append, self.timestamp) |
| if self.trusted_packet_sequence_id is not None: |
| writer = encoder.UInt32Encoder(10, False, False) |
| writer(parts.append, self.trusted_packet_sequence_id) |
| if self.track_event is not None: |
| tag = encoder.TagBytes(11, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = self.track_event.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| if self.interned_data is not None: |
| tag = encoder.TagBytes(12, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = self.interned_data.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| if self.incremental_state_cleared is not None: |
| writer = encoder.BoolEncoder(41, False, False) |
| writer(parts.append, self.incremental_state_cleared) |
| if self.thread_descriptor is not None: |
| tag = encoder.TagBytes(44, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = self.thread_descriptor.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| if self.chrome_benchmark_metadata is not None: |
| tag = encoder.TagBytes(48, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = self.chrome_benchmark_metadata.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| if self.timestamp_clock_id is not None: |
| writer = encoder.UInt32Encoder(58, False, False) |
| writer(parts.append, self.timestamp_clock_id) |
| |
| return b"".join(parts) |
| |
| |
| class InternedData(object): |
| def __init__(self): |
| self.event_category = None |
| self.legacy_event_name = None |
| |
| def encode(self): |
| parts = [] |
| if self.event_category is not None: |
| tag = encoder.TagBytes(1, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = self.event_category.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| if self.legacy_event_name is not None: |
| tag = encoder.TagBytes(2, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = self.legacy_event_name.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| |
| return b"".join(parts) |
| |
| |
| class EventCategory(object): |
| def __init__(self): |
| self.iid = None |
| self.name = None |
| |
| def encode(self): |
| if (self.iid is None or self.name is None): |
| raise RuntimeError("Missing mandatory fields.") |
| |
| parts = [] |
| writer = encoder.UInt32Encoder(1, False, False) |
| writer(parts.append, self.iid) |
| writer = encoder.StringEncoder(2, False, False) |
| writer(parts.append, self.name) |
| |
| return b"".join(parts) |
| |
| |
| LegacyEventName = EventCategory |
| |
| |
| class ThreadDescriptor(object): |
| def __init__(self): |
| self.pid = None |
| self.tid = None |
| |
| def encode(self): |
| if (self.pid is None or self.tid is None): |
| raise RuntimeError("Missing mandatory fields.") |
| |
| parts = [] |
| writer = encoder.UInt32Encoder(1, False, False) |
| writer(parts.append, self.pid) |
| writer = encoder.UInt32Encoder(2, False, False) |
| writer(parts.append, self.tid) |
| |
| return b"".join(parts) |
| |
| |
| class ChromeEventBundle(object): |
| def __init__(self): |
| self.metadata = [] |
| |
| def encode(self): |
| parts = [] |
| for item in self.metadata: |
| tag = encoder.TagBytes(2, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = item.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| |
| return b"".join(parts) |
| |
| |
| class TrackEvent(object): |
| def __init__(self): |
| self.legacy_event = None |
| self.category_iids = None |
| self.debug_annotations = [] |
| |
| def encode(self): |
| parts = [] |
| if self.category_iids is not None: |
| writer = encoder.UInt32Encoder(3, is_repeated=True, is_packed=False) |
| writer(parts.append, self.category_iids) |
| for annotation in self.debug_annotations: |
| tag = encoder.TagBytes(4, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = annotation.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| if self.legacy_event is not None: |
| tag = encoder.TagBytes(6, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = self.legacy_event.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| |
| return b"".join(parts) |
| |
| |
| class LegacyEvent(object): |
| def __init__(self): |
| self.phase = None |
| self.name_iid = None |
| |
| def encode(self): |
| parts = [] |
| if self.name_iid is not None: |
| writer = encoder.UInt32Encoder(1, False, False) |
| writer(parts.append, self.name_iid) |
| if self.phase is not None: |
| writer = encoder.Int32Encoder(2, False, False) |
| writer(parts.append, self.phase) |
| |
| return b"".join(parts) |
| |
| |
| class ChromeBenchmarkMetadata(object): |
| def __init__(self): |
| self.benchmark_start_time_us = None |
| self.story_run_time_us = None |
| self.benchmark_name = None |
| self.benchmark_description = None |
| self.story_name = None |
| self.story_tags = None |
| self.story_run_index = None |
| self.label = None |
| |
| def encode(self): |
| parts = [] |
| if self.benchmark_start_time_us is not None: |
| writer = encoder.Int64Encoder(1, False, False) |
| writer(parts.append, self.benchmark_start_time_us) |
| if self.story_run_time_us is not None: |
| writer = encoder.Int64Encoder(2, False, False) |
| writer(parts.append, self.story_run_time_us) |
| if self.benchmark_name is not None: |
| writer = encoder.StringEncoder(3, False, False) |
| writer(parts.append, self.benchmark_name) |
| if self.benchmark_description is not None: |
| writer = encoder.StringEncoder(4, False, False) |
| writer(parts.append, self.benchmark_description) |
| if self.label is not None: |
| writer = encoder.StringEncoder(5, False, False) |
| writer(parts.append, self.label) |
| if self.story_name is not None: |
| writer = encoder.StringEncoder(6, False, False) |
| writer(parts.append, self.story_name) |
| if self.story_tags is not None: |
| writer = encoder.StringEncoder(7, is_repeated=True, is_packed=False) |
| writer(parts.append, self.story_tags) |
| if self.story_run_index is not None: |
| writer = encoder.Int32Encoder(8, False, False) |
| writer(parts.append, self.story_run_index) |
| |
| return b"".join(parts) |
| |
| |
| def write_trace_packet(output, trace_packet): |
| tag = encoder.TagBytes(1, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| output.write(tag) |
| binary_data = trace_packet.encode() |
| encoder._EncodeVarint(output.write, len(binary_data)) |
| output.write(binary_data) |
| |
| |
| class DebugAnnotation(object): |
| def __init__(self): |
| self.name = None |
| self.int_value = None |
| self.double_value = None |
| self.string_value = None |
| |
| def encode(self): |
| if self.name is None: |
| raise RuntimeError("DebugAnnotation must have a name.") |
| if ((self.string_value is not None) + |
| (self.int_value is not None) + |
| (self.double_value is not None)) != 1: |
| raise RuntimeError("DebugAnnotation must have exactly one value.") |
| |
| parts = [] |
| writer = encoder.StringEncoder(10, False, False) |
| writer(parts.append, self.name) |
| if self.int_value is not None: |
| writer = encoder.Int64Encoder(4, False, False) |
| writer(parts.append, self.int_value) |
| if self.double_value is not None: |
| writer = encoder.DoubleEncoder(5, False, False) |
| writer(parts.append, self.double_value) |
| if self.string_value is not None: |
| writer = encoder.StringEncoder(6, False, False) |
| writer(parts.append, self.string_value) |
| |
| return b"".join(parts) |
| |
| |
| class ChromeMetadata(object): |
| def __init__(self): |
| self.name = None |
| self.string_value = None |
| |
| def encode(self): |
| if self.name is None or self.string_value is None: |
| raise RuntimeError("ChromeMetadata must have a name and a value.") |
| |
| parts = [] |
| writer = encoder.StringEncoder(1, False, False) |
| writer(parts.append, self.name) |
| writer = encoder.StringEncoder(2, False, False) |
| writer(parts.append, self.string_value) |
| |
| return b"".join(parts) |
| |
| |
| class Clock(object): |
| def __init__(self): |
| self.clock_id = None |
| self.timestamp = None |
| |
| def encode(self): |
| if self.clock_id is None or self.timestamp is None: |
| raise RuntimeError("Clock must have a clock_id and a timestamp.") |
| |
| parts = [] |
| writer = encoder.UInt32Encoder(1, False, False) |
| writer(parts.append, self.clock_id) |
| writer = encoder.UInt64Encoder(2, False, False) |
| writer(parts.append, self.timestamp) |
| |
| return b"".join(parts) |
| |
| |
| class ClockSnapshot(object): |
| def __init__(self): |
| self.clocks = [] |
| |
| def encode(self): |
| if len(self.clocks) < 2: |
| raise RuntimeError("ClockSnapshot must have at least two clocks.") |
| |
| parts = [] |
| for clock in self.clocks: |
| tag = encoder.TagBytes(1, wire_format.WIRETYPE_LENGTH_DELIMITED) |
| data = clock.encode() |
| length = encoder._VarintBytes(len(data)) |
| parts += [tag, length, data] |
| |
| return b"".join(parts) |