Common tasks

The checklists below show how to achieve some common tasks in the codebase.

Add a new ftrace event

  1. Find the format file for your event. The location of the file depends where tracefs is mounted but can often be found at /sys/kernel/debug/tracing/events/EVENT_GROUP/EVENT_NAME/format.
  2. Copy the format file into the codebase at src/traced/probes/ftrace/test/data/synthetic/events/EVENT_GROUP/EVENT_NAME/format.
  3. Add the event to src/tools/ftrace_proto_gen/event_list.
  4. Run tools/run_ftrace_proto_gen. This will update protos/perfetto/trace/ftrace/ftrace_event.proto and protos/perfetto/trace/ftrace/GROUP_NAME.proto.
  5. Run tools/gen_all out/YOUR_BUILD_DIRECTORY. This will update src/traced/probes/ftrace/ and protos/perfetto/trace/perfetto_trace.proto.
  6. If special handling in trace_processor is desired update src/trace_processor/importers/ftrace/ to parse the event.
  7. Upload and land your change as normal.

Here is an example change which added the ion/ion_stat event.

Contribute to SQL standard library

  1. Add or edit an SQL file inside perfetto/src/trace_processor/stdlib/.
  2. For a new file inside an existing module add the file to the corresponding
  3. For a new module (subdirectory of /stdlib/), module name (directory name) has to be added to the list in /stdlib/

Files inside the standard library have to be formatted in a very specific way, as its structure is used to generate documentation. There are presubmit checks, but they are not infallible.

NOTE: Break lines outside of import description will be ignored.

Example of properly formatted view in module android:

-- Count Binder transactions per process. -- -- @column process_name Name of the process that started the binder transaction. -- @column pid PID of the process that started the binder transaction. -- @column slice_name Name of the slice with binder transaction. -- @column event_count Number of binder transactions in process in slice. CREATE VIEW android_binder_metrics_by_process AS SELECT AS process_name, AS pid, AS slice_name, COUNT(*) AS event_count FROM slice INNER JOIN thread_track ON slice.track_id = INNER JOIN thread ON thread.utid = thread_track.utid INNER JOIN process ON thread.upid = process.upid WHERE GLOB 'binder*' GROUP BY process_name, slice_name;

Example of function in module common:

-- Extracts an int value with the given name from the metadata table. -- -- @arg name STRING The name of the metadata entry. -- @ret LONG int_value for the given name. NULL if there's no such entry. SELECT CREATE_FUNCTION( 'EXTRACT_INT_METADATA(name STRING)', 'LONG', 'SELECT int_value FROM metadata WHERE name = ($name)');

Example of view function in module android:

-- Given a launch id and GLOB for a slice name, returns columns for matching slices. -- -- @arg launch_id INT Id of launch. -- @arg slice_name STRING Name of slice with launch. -- @column slice_name Name of slice with launch. -- @column slice_ts INT Timestamp of slice start. -- @column slice_dur INT Duration of slice. -- @column thread_name STRING Name of thread with slice -- @column arg_set_id INT Arg set id. SELECT CREATE_VIEW_FUNCTION( 'ANDROID_SLICES_FOR_LAUNCH_AND_SLICE_NAME(launch_id INT, slice_name STRING)', 'slice_name STRING, slice_ts INT, slice_dur INT, thread_name STRING, arg_set_id INT', ' SELECT slice_name, slice_ts, slice_dur, thread_name, arg_set_id FROM thread_slices_for_all_launches WHERE launch_id = $launch_id AND slice_name GLOB $slice_name ' );

Add a new trace-based metric

  1. Create the proto file containing the metric in the protos/perfetto/metrics folder. The appropriate file should be updated as well.
  2. Import the proto in protos/perfetto/metrics/metrics.proto and add a field for the new message.
  3. Run tools/gen_all out/YOUR_BUILD_DIRECTORY. This will update the generated headers containing the descriptors for the proto.
  1. Add a new SQL file for the metric to src/trace_processor/metrics. The appropriate file should be updated as well.
  1. Build all targets in your out directory with tools/ninja -C out/YOUR_BUILD_DIRECTORY.
  2. Add a new diff test for the metric. This can be done by adding files to the tests.*.py files in a proper test/trace_processor subfolder.
  3. Run the newly added test with tools/ <path to trace processor binary>.
  4. Upload and land your change as normal.

Here is an example change which added the time_in_state metric.

Add a new trace processor table

  1. Create the new table in the appropriate header file in src/trace_processor/tables by copying one of the existing macro definitions.
  1. Register the table with the trace processor in the constructor for the TraceProcessorImpl class.
  2. If also implementing ingestion of events into the table:
  3. Modify the appropriate parser class in src/trace_processor/importers and add the code to add rows to the newly added table.
  4. Add a new diff test for the added parsing code and table.
  5. Run the newly added test with tools/ <path to trace processor shell binary>.
  6. Upload and land your change as normal.

Adding new derived events

As derived events depend on metrics, the initial steps are same as that of developing a metric (see above).

NOTE: the metric can be just an empty proto message during prototyping or if no summarization is necessary. However, generally if an event is important enough to display in the UI, it should also be tracked in benchmarks as a metric.

To extend a metric with annotations:

  1. Create a new table or view with the name <metric name>_event.
  1. List your metric in the initialiseHelperViews method of trace_controller.ts.
  2. Upload and land your change as normal.

The schema of the <metric name>_event table/view is as follows:

Name Type Presence Meaning
track_type string Mandatory 'slice' for slices, 'counter' for counters
track_name string Mandatory Name of the track to display in the UI. Also the track identifier i.e. all events with same track_name appear on the same track.
ts int64 Mandatory The timestamp of the event (slice or counter)
dur int64 Mandatory for slice, NULL for counter The duration of the slice
slice_name string Mandatory for slice, NULL for counter The name of the slice
value double Mandatory for counter, NULL for slice The value of the counter
group_name string Optional Name of the track group under which the track appears. All tracks with the same group_name are placed under the same group by that name. Tracks that lack this field or have NULL value in this field are displayed without any grouping.

Known issues:


Generally you do not have to worry about version skew between the UI and the trace_processor since they are built together at the same commit. However version skew can occur when using the --httpd mode which allows a native trace_processor instance to be used with the UI.

A common case is when the UI is more recent than trace_processor and depends on a new table definition. With older versions of trace_processor in --httpd mode the UI crashes attempting to query a non-existant table. To avoid this we use a version number. If the version number trace_processor reports is older than the one the UI was built with we prompt the user to update.

  1. Go to protos/perfetto/trace_processor/trace_processor.proto
  3. Add a comment explaining what has changed.