1# Copyright 2019 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5""" Classes representing perfetto trace protobuf messages. 6 7This module makes use of neither python-protobuf library nor python classes 8compiled from .proto definitions, because currently there's no way to 9deploy those to all the places where telemetry is run. 10 11TODO(crbug.com/944078): Remove this module after the python-protobuf library 12is deployed to all the bots. 13 14Definitions of perfetto messages can be found here: 15https://android.googlesource.com/platform/external/perfetto/+/refs/heads/master/protos/perfetto/trace/ 16""" 17 18import encoder 19import wire_format 20 21 22class TracePacket(object): 23 def __init__(self): 24 self.clock_snapshot = None 25 self.timestamp = None 26 self.timestamp_clock_id = None 27 self.interned_data = None 28 self.thread_descriptor = None 29 self.incremental_state_cleared = None 30 self.chrome_event = None 31 self.track_event = None 32 self.trusted_packet_sequence_id = None 33 self.chrome_benchmark_metadata = None 34 35 def encode(self): 36 parts = [] 37 if self.chrome_event is not None: 38 tag = encoder.TagBytes(5, wire_format.WIRETYPE_LENGTH_DELIMITED) 39 data = self.chrome_event.encode() 40 length = encoder._VarintBytes(len(data)) 41 parts += [tag, length, data] 42 if self.clock_snapshot is not None: 43 tag = encoder.TagBytes(6, wire_format.WIRETYPE_LENGTH_DELIMITED) 44 data = self.clock_snapshot.encode() 45 length = encoder._VarintBytes(len(data)) 46 parts += [tag, length, data] 47 if self.timestamp is not None: 48 writer = encoder.UInt64Encoder(8, False, False) 49 writer(parts.append, self.timestamp) 50 if self.trusted_packet_sequence_id is not None: 51 writer = encoder.UInt32Encoder(10, False, False) 52 writer(parts.append, self.trusted_packet_sequence_id) 53 if self.track_event is not None: 54 tag = encoder.TagBytes(11, wire_format.WIRETYPE_LENGTH_DELIMITED) 55 data = self.track_event.encode() 56 length = encoder._VarintBytes(len(data)) 57 parts += [tag, length, data] 58 if self.interned_data is not None: 59 tag = encoder.TagBytes(12, wire_format.WIRETYPE_LENGTH_DELIMITED) 60 data = self.interned_data.encode() 61 length = encoder._VarintBytes(len(data)) 62 parts += [tag, length, data] 63 if self.incremental_state_cleared is not None: 64 writer = encoder.BoolEncoder(41, False, False) 65 writer(parts.append, self.incremental_state_cleared) 66 if self.thread_descriptor is not None: 67 tag = encoder.TagBytes(44, wire_format.WIRETYPE_LENGTH_DELIMITED) 68 data = self.thread_descriptor.encode() 69 length = encoder._VarintBytes(len(data)) 70 parts += [tag, length, data] 71 if self.chrome_benchmark_metadata is not None: 72 tag = encoder.TagBytes(48, wire_format.WIRETYPE_LENGTH_DELIMITED) 73 data = self.chrome_benchmark_metadata.encode() 74 length = encoder._VarintBytes(len(data)) 75 parts += [tag, length, data] 76 if self.timestamp_clock_id is not None: 77 writer = encoder.UInt32Encoder(58, False, False) 78 writer(parts.append, self.timestamp_clock_id) 79 80 return b"".join(parts) 81 82 83class InternedData(object): 84 def __init__(self): 85 self.event_category = None 86 self.legacy_event_name = None 87 88 def encode(self): 89 parts = [] 90 if self.event_category is not None: 91 tag = encoder.TagBytes(1, wire_format.WIRETYPE_LENGTH_DELIMITED) 92 data = self.event_category.encode() 93 length = encoder._VarintBytes(len(data)) 94 parts += [tag, length, data] 95 if self.legacy_event_name is not None: 96 tag = encoder.TagBytes(2, wire_format.WIRETYPE_LENGTH_DELIMITED) 97 data = self.legacy_event_name.encode() 98 length = encoder._VarintBytes(len(data)) 99 parts += [tag, length, data] 100 101 return b"".join(parts) 102 103 104class EventCategory(object): 105 def __init__(self): 106 self.iid = None 107 self.name = None 108 109 def encode(self): 110 if (self.iid is None or self.name is None): 111 raise RuntimeError("Missing mandatory fields.") 112 113 parts = [] 114 writer = encoder.UInt32Encoder(1, False, False) 115 writer(parts.append, self.iid) 116 writer = encoder.StringEncoder(2, False, False) 117 writer(parts.append, self.name) 118 119 return b"".join(parts) 120 121 122LegacyEventName = EventCategory 123 124 125class ThreadDescriptor(object): 126 def __init__(self): 127 self.pid = None 128 self.tid = None 129 130 def encode(self): 131 if (self.pid is None or self.tid is None): 132 raise RuntimeError("Missing mandatory fields.") 133 134 parts = [] 135 writer = encoder.UInt32Encoder(1, False, False) 136 writer(parts.append, self.pid) 137 writer = encoder.UInt32Encoder(2, False, False) 138 writer(parts.append, self.tid) 139 140 return b"".join(parts) 141 142 143class ChromeEventBundle(object): 144 def __init__(self): 145 self.metadata = [] 146 147 def encode(self): 148 parts = [] 149 for item in self.metadata: 150 tag = encoder.TagBytes(2, wire_format.WIRETYPE_LENGTH_DELIMITED) 151 data = item.encode() 152 length = encoder._VarintBytes(len(data)) 153 parts += [tag, length, data] 154 155 return b"".join(parts) 156 157 158class TrackEvent(object): 159 def __init__(self): 160 self.legacy_event = None 161 self.category_iids = None 162 self.debug_annotations = [] 163 164 def encode(self): 165 parts = [] 166 if self.category_iids is not None: 167 writer = encoder.UInt32Encoder(3, is_repeated=True, is_packed=False) 168 writer(parts.append, self.category_iids) 169 for annotation in self.debug_annotations: 170 tag = encoder.TagBytes(4, wire_format.WIRETYPE_LENGTH_DELIMITED) 171 data = annotation.encode() 172 length = encoder._VarintBytes(len(data)) 173 parts += [tag, length, data] 174 if self.legacy_event is not None: 175 tag = encoder.TagBytes(6, wire_format.WIRETYPE_LENGTH_DELIMITED) 176 data = self.legacy_event.encode() 177 length = encoder._VarintBytes(len(data)) 178 parts += [tag, length, data] 179 180 return b"".join(parts) 181 182 183class LegacyEvent(object): 184 def __init__(self): 185 self.phase = None 186 self.name_iid = None 187 188 def encode(self): 189 parts = [] 190 if self.name_iid is not None: 191 writer = encoder.UInt32Encoder(1, False, False) 192 writer(parts.append, self.name_iid) 193 if self.phase is not None: 194 writer = encoder.Int32Encoder(2, False, False) 195 writer(parts.append, self.phase) 196 197 return b"".join(parts) 198 199 200class ChromeBenchmarkMetadata(object): 201 def __init__(self): 202 self.benchmark_start_time_us = None 203 self.story_run_time_us = None 204 self.benchmark_name = None 205 self.benchmark_description = None 206 self.story_name = None 207 self.story_tags = None 208 self.story_run_index = None 209 self.label = None 210 211 def encode(self): 212 parts = [] 213 if self.benchmark_start_time_us is not None: 214 writer = encoder.Int64Encoder(1, False, False) 215 writer(parts.append, self.benchmark_start_time_us) 216 if self.story_run_time_us is not None: 217 writer = encoder.Int64Encoder(2, False, False) 218 writer(parts.append, self.story_run_time_us) 219 if self.benchmark_name is not None: 220 writer = encoder.StringEncoder(3, False, False) 221 writer(parts.append, self.benchmark_name) 222 if self.benchmark_description is not None: 223 writer = encoder.StringEncoder(4, False, False) 224 writer(parts.append, self.benchmark_description) 225 if self.label is not None: 226 writer = encoder.StringEncoder(5, False, False) 227 writer(parts.append, self.label) 228 if self.story_name is not None: 229 writer = encoder.StringEncoder(6, False, False) 230 writer(parts.append, self.story_name) 231 if self.story_tags is not None: 232 writer = encoder.StringEncoder(7, is_repeated=True, is_packed=False) 233 writer(parts.append, self.story_tags) 234 if self.story_run_index is not None: 235 writer = encoder.Int32Encoder(8, False, False) 236 writer(parts.append, self.story_run_index) 237 238 return b"".join(parts) 239 240 241def write_trace_packet(output, trace_packet): 242 tag = encoder.TagBytes(1, wire_format.WIRETYPE_LENGTH_DELIMITED) 243 output.write(tag) 244 binary_data = trace_packet.encode() 245 encoder._EncodeVarint(output.write, len(binary_data)) 246 output.write(binary_data) 247 248 249class DebugAnnotation(object): 250 def __init__(self): 251 self.name = None 252 self.int_value = None 253 self.double_value = None 254 self.string_value = None 255 256 def encode(self): 257 if self.name is None: 258 raise RuntimeError("DebugAnnotation must have a name.") 259 if ((self.string_value is not None) + 260 (self.int_value is not None) + 261 (self.double_value is not None)) != 1: 262 raise RuntimeError("DebugAnnotation must have exactly one value.") 263 264 parts = [] 265 writer = encoder.StringEncoder(10, False, False) 266 writer(parts.append, self.name) 267 if self.int_value is not None: 268 writer = encoder.Int64Encoder(4, False, False) 269 writer(parts.append, self.int_value) 270 if self.double_value is not None: 271 writer = encoder.DoubleEncoder(5, False, False) 272 writer(parts.append, self.double_value) 273 if self.string_value is not None: 274 writer = encoder.StringEncoder(6, False, False) 275 writer(parts.append, self.string_value) 276 277 return b"".join(parts) 278 279 280class ChromeMetadata(object): 281 def __init__(self): 282 self.name = None 283 self.string_value = None 284 285 def encode(self): 286 if self.name is None or self.string_value is None: 287 raise RuntimeError("ChromeMetadata must have a name and a value.") 288 289 parts = [] 290 writer = encoder.StringEncoder(1, False, False) 291 writer(parts.append, self.name) 292 writer = encoder.StringEncoder(2, False, False) 293 writer(parts.append, self.string_value) 294 295 return b"".join(parts) 296 297 298class Clock(object): 299 def __init__(self): 300 self.clock_id = None 301 self.timestamp = None 302 303 def encode(self): 304 if self.clock_id is None or self.timestamp is None: 305 raise RuntimeError("Clock must have a clock_id and a timestamp.") 306 307 parts = [] 308 writer = encoder.UInt32Encoder(1, False, False) 309 writer(parts.append, self.clock_id) 310 writer = encoder.UInt64Encoder(2, False, False) 311 writer(parts.append, self.timestamp) 312 313 return b"".join(parts) 314 315 316class ClockSnapshot(object): 317 def __init__(self): 318 self.clocks = [] 319 320 def encode(self): 321 if len(self.clocks) < 2: 322 raise RuntimeError("ClockSnapshot must have at least two clocks.") 323 324 parts = [] 325 for clock in self.clocks: 326 tag = encoder.TagBytes(1, wire_format.WIRETYPE_LENGTH_DELIMITED) 327 data = clock.encode() 328 length = encoder._VarintBytes(len(data)) 329 parts += [tag, length, data] 330 331 return b"".join(parts) 332