Life of a Perfetto tracing session

This document explains how producer, service and consumer interact end-to-end during a tracing session, with references to code and IPC requests / responses.

  1. One or more producers connect to the tracing service and sets up their IPC channels.
  2. Each producer advertises one or more data sources through the RegisterDataSource IPC. Nothing more happens on the Producer until this point. Nothing traces by default.
  3. A consumer connects to the tracing service and sets up the IPC channel.
  4. The consumer starts a tracing session sending a trace config to the service through the EnableTracing IPC.
  5. The service creates as many new trace buffers as specified in the config.
  6. The service iterates through the data_sources section of the trace config: for each entry, if a matching data source is found in the producer(s) (accordingly to what advertised in step 2):
  7. The service sends a SetupTracing IPC message, passing a shared memory buffer to the producer(s) (only once for each producer).
  8. The service sends a StartDataSource IPC message to each producer, for each data source configured in the trace config and present in the producer (if any).
  9. The producer creates one or more data source instance, as instructed in the previous step.
  10. Each data source instance creates one or more TraceWriter (typically one for each thread).
  11. Each TraceWriter writes one or more TracePacket.
  12. While doing so, each TraceWriter takes ownership of shared memory buffer's chunks, using the SharedMemoryArbiter.
  13. While writing a TracePacket, the TraceWriter will unavoidably cross the chunk boundary (typically 4KB, but can configured to be smaller).
  14. When this happens the TraceWriter will release the current chunk and acquire a new chunk through the SharedMemoryArbiter.
  15. The SharedMemoryArbiter will, out-of-band, send a CommitDataRequest to the service, requesting it to move some chunks of the shared memory buffer into the final trace buffer.
  16. If one or more long TracePacket were fragmented over several chunks, it is possible that some of these chunks are gone from the shared memory buffer and committed into the final trace buffer (step 16). In this case, the SharedMemoryArbiter will send an other CommitDataRequest IPC message to request the out-of-band patching of the chunk data into the final trace buffer.
  17. The service will check if the given chunk, identified by the tuple {ProducerID (unspoofable), WriterID, ChunkID} is still present in the trace buffer and if so will proceed to patch it (% checks).
  18. The consumer sends a FlushRequest to the service, asking it commit all data on flight in the trace buffers.
  19. The service, in turn, issues a Flush request to all producers involved in the trace session.
  20. The producer(s) will Flush() all their TraceWriter and reply to the service flush request.
  21. Once the service has received an ACK to all flush requests from all producers (or the flush timeout has expired) it replies to the consumer's FlushRequest
  22. The consumer optionally sends a DisableTracing IPC request to stop tracing and freeze the content of the trace buffers.
  23. The service will in turn broadcast a StopDataSource request for each data source in each Producer.
  24. At this point the Consumer issues a ReadBuffers IPC request (unless it did previously pass a file descriptor to the service when enabling the trace through the write_into_file field of the trace config).
  25. The service reads the trace buffers and streams all the TracePacket(s) back to the consumer.
  26. If a trace packet stored in the trace buffer is incomplete (e.g., a fragment is missing) or is marked as pending out-of-band patching, the given writer sequence is interrupted and no more packets for that sequence are read. Other packets for other TraceWriter sequences will be unaffected.
  27. The consumer sends a FreeBuffers (or simply disconnects).
  28. The service tears down all the trace buffers for the session.