1# Writing TrackEvent Protos Synthetically 2This page acts as a reference guide to synthetically generate TrackEvent, 3Perfetto's native protobuf based tracing format. This allows using Perfetto's 4analysis and visualzation without using collecting traces using the Perfetto 5SDK. 6 7TrackEvent protos can be manually written using the 8[official protobuf library](https://protobuf.dev/reference/) or any other 9protobuf-compatible library. To be language-agnostic, the rest of this page 10will show examples using the 11[text format](https://protobuf.dev/reference/protobuf/textformat-spec/) 12representation of protobufs. 13 14The root container of the protobuf-based traces is the 15[Trace](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/trace/trace.proto) 16message which itself is simply a repeated field of 17[TracePacket](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/trace/trace_packet.proto) 18messages. 19 20## Thread-scoped (sync) slices 21NOTE: in the legacy JSON tracing format, this section correspond to B/E/I/X 22events with the associated M (metadata) events. 23 24Thread scoped slices are used to trace execution of functions on a single 25thread. As only one function runs on a single thread over time, this requires 26that child slices nest perfectly inside parent slices and do not partially 27overlap. 28 29 30 31This is corresponds to the following protos: 32``` 33# Emit this packet once *before* you emit the first event for this process. 34packet { 35 track_descriptor: { 36 uuid: 894893984 # 64-bit random number. 37 process: { 38 pid: 1234 # PID for your process. 39 process_name: "My process name" 40 } 41 } 42} 43 44# Emit this packet once *before* you emit the first event for this thread. 45packet { 46 track_descriptor: { 47 uuid: 49083589894 # 64-bit random number. 48 parent_uuid: 894893984 # UUID from above. 49 thread: { 50 pid: 1234 # PID for your process. 51 tid: 5678 # TID for your thread. 52 thread_name: "My thread name" 53 } 54 } 55} 56 57# The events for this thread. 58packet { 59 timestamp: 200 60 track_event: { 61 type: TYPE_SLICE_BEGIN 62 track_uuid: 49083589894 # Same random number from above. 63 name: "My special parent" 64 } 65 trusted_packet_sequence_id: 3903809 # Generate *once*, use throughout. 66} 67packet { 68 timestamp: 250 69 track_event: { 70 type: TYPE_SLICE_BEGIN 71 track_uuid: 49083589894 72 name: "My special child" 73 } 74 trusted_packet_sequence_id: 3903809 75} 76packet { 77 timestamp: 285 78 track_event { 79 type: TYPE_INSTANT 80 track_uuid: 49083589894 81 } 82 trusted_packet_sequence_id: 3903809 83} 84packet { 85 timestamp: 290 86 track_event: { 87 type: TYPE_SLICE_END 88 track_uuid: 49083589894 89 } 90 trusted_packet_sequence_id: 3903809 91} 92packet { 93 timestamp: 300 94 track_event: { 95 type: TYPE_SLICE_END 96 track_uuid: 49083589894 97 } 98 trusted_packet_sequence_id: 3903809 99} 100``` 101 102## Process-scoped (async) slices 103NOTE: in the legacy JSON tracing format, this section corresponds to b/e/n 104events with the associated M (metadata) events. 105 106Process-scoped slices are useful to trace execution of a "piece of work" across 107multiple threads of a process. A process-scoped slice can start on a thread 108A and end on a thread B. Examples include work submitted to thread pools 109and coroutines. 110 111Process tracks can be named corresponding to the executor and can also have 112child slices in an identical way to thread-scoped slices. Importantly, this 113means slices on a single track must **strictly nest** inside each other 114without overlapping. 115 116As separating each track in the UI can cause a lot of clutter, the UI 117visually merges process tracks with the same name in each process. Note that 118this **does not** change the data model (e.g. in trace processor 119tracks remain separated) as this is simply a visual grouping. 120 121 122 123This is corresponds to the following protos: 124``` 125# The first track associated with this process. 126packet { 127 track_descriptor { 128 uuid: 48948 # 64-bit random number. 129 name: "My special track" 130 process { 131 pid: 1234 # PID for your process 132 process_name: "My process name" 133 } 134 } 135} 136# The events for the first track. 137packet { 138 timestamp: 200 139 track_event { 140 type: TYPE_SLICE_BEGIN 141 track_uuid: 48948 # Same random number from above. 142 name: "My special parent A" 143 } 144 trusted_packet_sequence_id: 3903809 # Generate *once*, use throughout. 145} 146packet { 147 timestamp: 250 148 track_event { 149 type: TYPE_SLICE_BEGIN 150 track_uuid: 48948 151 name: "My special child" 152 } 153 trusted_packet_sequence_id: 3903809 154} 155packet { 156 timestamp: 290 157 track_event { 158 type: TYPE_SLICE_END 159 track_uuid: 48948 160 } 161 trusted_packet_sequence_id: 3903809 162} 163packet { 164 timestamp: 300 165 track_event { 166 type: TYPE_SLICE_END 167 track_uuid: 48948 168 } 169 trusted_packet_sequence_id: 3903809 170} 171 172# The second track associated with this process. Note how we make the above 173# track the "parent" of this track: this means that this track also is 174# associated to the same process. Note further this shows as the same visual 175# track in the UI but remains separate in the trace and data model. Emitting 176# these events on a separate track is necessary because these events overlap 177# *without* nesting with the above events. 178packet { 179 track_descriptor { 180 uuid: 2390190934 # 64-bit random number. 181 name: "My special track" 182 parent_uuid: 48948 183 } 184} 185# The events for the second track. 186packet { 187 timestamp: 230 188 track_event { 189 type: TYPE_SLICE_BEGIN 190 track_uuid: 2390190934 # Same random number from above. 191 name: "My special parent A" 192 } 193 trusted_packet_sequence_id: 3903809 194} 195packet { 196 timestamp: 260 197 track_event { 198 type: TYPE_SLICE_BEGIN 199 track_uuid: 2390190934 200 name: "My special child" 201 } 202 trusted_packet_sequence_id: 3903809 203} 204packet { 205 timestamp: 270 206 track_event { 207 type: TYPE_SLICE_END 208 track_uuid: 2390190934 209 } 210 trusted_packet_sequence_id: 3903809 211} 212packet { 213 timestamp: 295 214 track_event { 215 type: TYPE_SLICE_END 216 track_uuid: 2390190934 217 } 218 trusted_packet_sequence_id: 3903809 219} 220``` 221 222## Flows 223NOTE: in the legacy JSON tracing format, this section correspond to s/t/f 224events. 225 226Flows allow connecting any number of slices with arrows. The semantic meaning 227of the arrow varies across different applications but most commonly it is used 228to track work passing between threads or processes: e.g. the UI thread asks a 229background thread to do some work and notify when the result is available. 230 231NOTE: a single flow *cannot* fork ands imply represents a single stream of 232arrows from one slice to the next. See [this](https://source.chromium.org/chromium/chromium/src/+/main:third_party/perfetto/protos/perfetto/trace/perfetto_trace.proto;drc=ba05b783d9c29fe334a02913cf157ea1d415d37c;l=9604) comment for information. 233 234 235 236``` 237# The main thread of the process. 238packet { 239 track_descriptor { 240 uuid: 93094 241 thread { 242 pid: 100 243 tid: 100 244 thread_name: "Main thread" 245 } 246 } 247} 248packet { 249 timestamp: 200 250 track_event { 251 type: TYPE_SLICE_BEGIN 252 track_uuid: 93094 253 name: "Request generation" 254 flow_ids: 1055895987 # Random number used to track work 255 # across threads/processes. 256 } 257 trusted_packet_sequence_id: 3903809 258} 259packet { 260 timestamp: 300 261 track_event { 262 type: TYPE_SLICE_END 263 track_uuid: 93094 264 } 265 trusted_packet_sequence_id: 3903809 266} 267packet { 268 timestamp: 400 269 track_event { 270 type: TYPE_SLICE_BEGIN 271 track_uuid: 93094 272 name: "Process background result" 273 flow_ids: 1055895987 # Same as above. 274 } 275 trusted_packet_sequence_id: 3903809 276} 277packet { 278 timestamp: 500 279 track_event { 280 type: TYPE_SLICE_END 281 track_uuid: 93094 282 } 283 trusted_packet_sequence_id: 3903809 284} 285 286# The background thread of the process. 287packet { 288 track_descriptor { 289 uuid: 40489498 290 thread { 291 pid: 100 292 tid: 101 293 thread_name: "Background thread" 294 } 295 } 296} 297packet { 298 timestamp: 310 299 track_event { 300 type: TYPE_SLICE_BEGIN 301 track_uuid: 40489498 302 name: "Background work" 303 flow_ids: 1055895987 # Same as above. 304 } 305 trusted_packet_sequence_id: 3903809 306} 307packet { 308 timestamp: 385 309 track_event { 310 type: TYPE_SLICE_END 311 track_uuid: 40489498 312 } 313 trusted_packet_sequence_id: 3903809 314} 315``` 316 317## Counters 318NOTE: in the legacy JSON tracing format, this section correspond to C events. 319 320Counters are useful to represent continuous values which change with time. 321Common examples include CPU frequency, memory usage, battery charge etc. 322 323 324 325This corresponds to the following protos: 326``` 327# Counter track scoped to a process. 328packet { 329 track_descriptor { 330 uuid: 1388 331 process { 332 pid: 1024 333 process_name: "MySpecialProcess" 334 } 335 } 336} 337packet { 338 track_descriptor { 339 uuid: 4489498 340 parent_uuid: 1388 341 name: "My special counter" 342 counter {} 343 } 344} 345packet { 346 timestamp: 200 347 track_event { 348 type: TYPE_COUNTER 349 track_uuid: 4489498 350 counter_value: 34567 # Value at start 351 } 352 trusted_packet_sequence_id: 3903809 353} 354packet { 355 timestamp: 250 356 track_event { 357 type: TYPE_COUNTER 358 track_uuid: 4489498 359 counter_value: 67890 # Value goes up 360 } 361 trusted_packet_sequence_id: 3903809 362} 363packet { 364 timestamp: 300 365 track_event { 366 type: TYPE_COUNTER 367 track_uuid: 4489498 368 counter_value: 12345 # Value goes down 369 } 370 trusted_packet_sequence_id: 3903809 371} 372packet { 373 timestamp: 400 374 track_event { 375 type: TYPE_COUNTER 376 track_uuid: 4489498 377 counter_value: 12345 # Final value 378 } 379 trusted_packet_sequence_id: 3903809 380} 381``` 382 383## Interning 384NOTE: there is no equivalent to interning in the JSON tracing format. 385 386Interning is an advanced but powerful feature of the protobuf tracing format 387which allows allows for reducing the number of times long strings are emitted 388in the trace. 389 390Specifically, certain fields in the protobuf format allow associating an "iid" 391(interned id) to a string and using the iid to reference the string in all 392future packets. The most commonly used cases are slice names and category 393names 394 395Here is an example of a trace which makes use of interning to reduce the 396number of times a very long slice name is emitted: 397 398 399This corresponds to the following protos: 400``` 401packet { 402 track_descriptor { 403 uuid: 48948 # 64-bit random number. 404 name: "My special track" 405 process { 406 pid: 1234 # PID for your process 407 process_name: "My process name" 408 } 409 } 410} 411packet { 412 timestamp: 200 413 track_event { 414 type: TYPE_SLICE_BEGIN 415 track_uuid: 48948 # Same random number from above. 416 name_iid: 1 # References the string in interned_data 417 # (see below) 418 } 419 trusted_packet_sequence_id: 3903809 # Generate *once*, use throughout. 420 421 interned_data { 422 # Creates a mapping from the iid "1" to the string name: any |name_iid| field 423 # in this packet onwards will transparently be remapped to this string by trace 424 # processor. 425 # Note: iid 0 is *not* a valid IID and should not be used. 426 event_names { 427 iid: 1 428 name: "A very very very long slice name which we don't want to repeat" 429 } 430 } 431 432 first_packet_on_sequence: true # Indicates to trace processor that 433 # this is the first packet on the 434 # sequence. 435 previous_packet_dropped: true # Same as |first_packet_on_sequence|. 436 437 # Indicates to trace processor that this sequence resets the incremental state but 438 # also depends on incrtemental state state. 439 # 3 = SEQ_INCREMENTAL_STATE_CLEARED | SEQ_NEEDS_INCREMENTAL_STATE 440 sequence_flags: 3 441} 442packet { 443 timestamp: 201 444 track_event { 445 type: TYPE_SLICE_END 446 track_uuid: 48948 447 } 448 trusted_packet_sequence_id: 3903809 449} 450packet { 451 timestamp: 202 452 track_event { 453 type: TYPE_SLICE_BEGIN 454 track_uuid: 48948 # Same random number from above. 455 name_iid: 1 # References the string in interned_data 456 # above. 457 } 458 trusted_packet_sequence_id: 3903809 # Generate *once*, use throughout. 459 # 2 = SEQ_NEEDS_INCREMENTAL_STATE 460 sequence_flags: 2 461} 462packet { 463 timestamp: 203 464 track_event { 465 type: TYPE_SLICE_END 466 track_uuid: 48948 467 } 468 trusted_packet_sequence_id: 3903809 469} 470``` 471