• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2025 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17syntax = "proto2";
18
19package perfetto.protos;
20
21import "protos/perfetto/perfetto_sql/structured_query.proto";
22
23// The spec for a v2 trace-based metric.
24//
25// Conceptually, a v2 trace-based metric is very similar to metrics in other
26// analytics system: it corresponds to a "value", some numerical property of
27// the trace which can be measured and a set of "dimensions" which correspond to
28// extra context about that value. Metrics also have an "id" which uniquely
29// identifies them within a single trace summary.
30//
31// Finally, the `query` field specified how trace processor should compute the
32// metric from the trace. We use the standard `PerfettoSqlStructuredQuery` proto
33// for this: please see the documentation there for more details on writing it.
34//
35// For a simple example: suppose you wanted to average memory usage broken down
36// by process name. Since the PerfettoSQL Standard Library already has
37// primitives for this, this is easily accomplished with the following spec:
38//
39// ```
40// id: "memory_per_process"
41// dimensions: "process_name"
42// value: "avg_rss_and_swap"
43// query: {
44//   table: {
45//     table_name: "memory_rss_and_swap_per_process"
46//     module_name: "linux.memory.process"
47//   }
48//   group_by: {
49//     column_names: "process_name"
50//     aggregates: {
51//       column_name: "rss_and_swap"
52//       op: DURATION_WEIGHTED_MEAN
53//       result_column_name: "avg_rss_and_swap"
54//     }
55//   }
56// }
57// ```
58//
59// A common usecase is to restrict the period of interest to only certain time
60// periods of interest, for example, only the time spaned by a test run or a
61// Critical User Journey (CUJ). We can use the `interval_intersect` operation
62// for this.
63//
64// Suppose the CUJ of interest was represented by a slice matched by the glob
65// `<J>Cuj*`. The spec would look like:
66//
67// ```
68// id: "memory_per_process_and_cuj"
69// dimensions: "process_name"
70// value: "avg_rss_and_swap"
71// query: {
72//   interval_intersect: {
73//      base: {
74//        table: {
75//          table_name: "memory_rss_and_swap_per_process"
76//          module_name: "linux.memory.process"
77//        }
78//      }
79//      interval_intersect: {
80//        simple_slices: {
81//          slice_name_glob: "<J>Cuj*"
82//        }
83//        select_columns: {
84//          column_name: "slice_name"
85//          alias: "cuj_name"
86//        }
87//      }
88//   }
89//   group_by: {
90//     column_names: "process_name"
91//     column_names: "cuj_name"
92//     aggregates: {
93//       column_name: "rss_and_swap"
94//       op: DURATION_WEIGHTED_MEAN
95//       result_column_name: "avg_rss_and_swap"
96//     }
97//   }
98// }
99// ```
100//
101// A more complex example might: suppose you wanted to find the total CPU time
102// of the `foo` slice in the `bar` thread while the `baz` CUJ (represented by
103// a slice in `system_server`) was happening. You can accomplish that with the
104// spec:
105// ```
106// id: "sum_foo_cpu_time_during_baz"
107// value: "sum_cpu_time"
108// query: {
109//   interval_intersect: {
110//      base: {
111//        table: {
112//          table_name: "thread_slice_cpu_time"
113//          module_name: "linux.memory.process"
114//        }
115//        filters: {
116//          column_name: "thread_name"
117//          op: EQUAL
118//          string_rhs: "bar"
119//        }
120//      }
121//      interval_intersect: {
122//        simple_slices: {
123//          slice_name_glob: "baz"
124//          process_name_glob: "system_server"
125//        }
126//      }
127//   }
128//   group_by: {
129//     aggregates: {
130//       column_name: "cpu_time"
131//       op: SUM
132//       result_column_name: "sum_cpu_time"
133//     }
134//   }
135// }
136// ```
137//
138//
139// Note: if you are familiar with v1 trace-based metrics, there is a pretty big
140// difference between the two: while v1 metrics were very flexible with respect
141// to their output schema, v2 metrics give up that flexibility in exchange for
142// being able to build general pupose systems which consume the result of
143// metrics. This makes it possible e.g. to have an automatic metric viewer in
144// the Perfetto UI visualizing the results of running a metric.
145message TraceMetricV2Spec {
146  // The id of the metric. An opaque field but the convention is to use
147  // lowecase + underscores (i.e. foo_bar). Note however this is not enforced.
148  // Required.
149  optional string id = 1;
150
151  // The columns from `query` which will act as the "dimensions" for the metric.
152  // For a given set of dimensions, there must be exactly *one* value emitted.
153  // Optional.
154  repeated string dimensions = 2;
155
156  // The column from `query` which will act as the "value" for the metric. This
157  // must be a column containing only integers/doubles/nulls. Strings are *not*
158  // supported: prefer making the string a dimension and then *counting* the
159  // number of strings as the value.
160  // Required.
161  optional string value = 3;
162
163  // The structured query which will be used to compute the metric. See the
164  // documentation of `PerfettoSqlStructuredQuery` for more information.
165  // Required.
166  optional PerfettoSqlStructuredQuery query = 4;
167}
168
169// The output containing all the values for a single v2 trace-based metric.
170//
171// Note: see `TraceMetricV2Spec` for commentary on what a trace-based metric
172// is.
173//
174// For the `memory_per_process` example above, the output proto might look
175// something like:
176// ```
177// row: {
178//   value: 123456
179//   dimensions: {
180//     string_value: "my_special_process"
181//   }
182// }
183// row: {
184//   value: 9876
185//   dimensions: {
186//     string_value: "/bin/init"
187//   }
188// }
189// spec {
190//   id: "memory_per_process"
191//   dimensions: "process_name"
192//   value: "rss_and_swap"
193//   query: {
194//     table: {
195//       table_name: "memory_rss_and_swap_per_process"
196//       module_name: "linux.memory.process"
197//     }
198//   }
199// }
200// ```
201//
202// And for the `memory_per_process_and_cuj` example:
203// ```
204// row: {
205//   value: 123456
206//   dimensions: {
207//     string_value: "<J>CujFoo"
208//     string_value: "my_special_process"
209//   }
210// }
211// row: {
212//   value: 9876
213//   dimensions: {
214//     string_value: "<J>CujBar"
215//     string_value: "/bin/init"
216//   }
217// }
218// spec {
219//   ...(contents of spec)
220// }
221// ```
222// Note: if value of a row is NULL, the row will not be emitted.
223message TraceMetricV2 {
224  // A single metric row corresponding to a value associated with a (unique) set
225  // of dimensions
226  message MetricRow {
227    // The value of the metric associated with the `dimensions`.
228    optional double value = 1;
229
230    // The dimensions that `value` should be associated with. The order of
231    // dimensions matches precisely the order of dimension names given by the
232    // `spec`.
233    message Dimension {
234      message Null {}
235      oneof value_oneof {
236        string string_value = 1;
237        int64 int64_value = 2;
238        double double_value = 3;
239        Null null_value = 4;
240      }
241    }
242    repeated Dimension dimension = 2;
243  }
244  repeated MetricRow row = 1;
245
246  // The spec for the metric. This is simply an echo of the spec which was
247  // passed in to compute the metric. Useful for knowing what the dimension
248  // names/value names are.
249  optional TraceMetricV2Spec spec = 2;
250}
251