• 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
21// Represents a PerfettoSQL query as a protobuf.
22//
23// SQL is amazing for writing interactive queries and human readability and
24// development but it is really bad for machine readability. Specifically, given
25// an SQL query, it's very hard to figure out what the author "intended" from
26// that as the same query can be written in countless different ways. This
27// makes building automated tools which take SQL and represent the data flow
28// visually very difficult to build.
29//
30// The goal of this proto is *not* in any way to replace SQL. In fact that's
31// an explicit *non-goal*. Instead, the idea here is this proto encodes it is a
32// very restricted, well-defined subset of the functionality of SQL that we see
33// a lot of usage of when writing PerfettoSQL queries.
34//
35// Basically, trace analysis has a lot of common "patterns" when it comes to
36// writing queries and this proto aims to have a central place codifying those
37// so all Perfetto tooling can share a common interchange format. Specifically,
38// unlike SQL which is quite optimized for human readability, this proto is more
39// designed for easy machine consumption with a secondary goal to still be
40// pretty easy for humans to read/write/modify in small doses. Note that it
41// *will* be verbose to deal with complex instances of this proto.
42//
43// It will always be easy to go from this proto to PerfettoSQL: trace processor
44// exposes APIs for this. It's also easy to bring SQL directly into the proto
45// world through use of the `Sql` source (see below).
46message PerfettoSqlStructuredQuery {
47  // A table or view acting as the source of the query, possibly living in a
48  // PerfettoSQL module.
49  message Table {
50    // The name of the table or view to query. Required.
51    optional string table_name = 1;
52
53    // The name of the module this table lives in. Optional, does not need to
54    // be specified if the table exists in the global scope (e.g. track table,
55    // slice table, any table/function in the prelude). Required otherwise.
56    optional string module_name = 2;
57
58    // The name of the columns of this table which will be used by this query.
59    // Required.
60    //
61    // Note: specifying this field is *mandatory* at all times. In the future,
62    // this may become option for public tables in the standard library.
63    repeated string column_names = 3;
64  }
65
66  // A set of slices which will act as the source for the query. This is
67  // basically equivalent to doing a query on the "slice" table (+ the
68  // appropriate joins) followed by a filter on various columns.
69  //
70  // This message exists for *pure* human convinience as we expect this pattern
71  // to be very commonly used by users.
72  //
73  // Produces a source with the schema
74  // (id, ts, dur, slice_name, thread_name, process_name, track_name).
75  message SimpleSlices {
76    // Glob for the name of the slices. Optional.
77    optional string slice_name_glob = 1;
78
79    // Glob for the thread name of the slices. Optional.
80    optional string thread_name_glob = 2;
81
82    // Glob for the process name of the slices. Optional.
83    optional string process_name_glob = 3;
84
85    // Glob for the track name of the slices. Optional.
86    optional string track_name_glob = 4;
87  }
88
89  // An arbitrary SQL query to use as the source for the query.
90  message Sql {
91    // The SQL string. Required.
92    //
93    // Note: this string is currently required to be a single well-formed select
94    // statement. This means you cannot use CREATE PERFETTO TABLE/VIEW but *can*
95    // use WITH clauses, UNION and INTERSECT. This restriction may be dropped
96    // in the future.
97    optional string sql = 1;
98
99    // The name of columns which will be returned by the SQL. Required.
100    repeated string column_names = 2;
101
102    // SQL string that has to be run before running the SQL. Supports multi
103    // statement queries. Optional.
104    optional string preamble = 3;
105  }
106
107  // Performs a "time-based" intersection of data from `base` with multiple
108  // sets of intervals.
109  //
110  // Examples:
111  // The best way to understand this operator is through example usecases
112  //  1) Compute the CPU usage during some CUJs:
113  //    * `base` points to a query contain CPU scheduling data.
114  //    * `interval_intersect` points to a query containing the CUJ boundaries.
115  //  2) Compute the memory usage of a process during an app startup
116  //    * `base` points to a query contain the memory usage of that process over
117  //      time.
118  //    * `interval_intersect` points to a structued query containing the app
119  //      startups.
120  //  3) Compute dropped frames during an layout while scrolling
121  //    * `base` points to a strucuted query containing the dropped frames
122  //      over time.
123  //    * `interval_intersect` points to two structured queries: the first
124  //      containing the layout intervals, the second containing the scroll
125  //      intervals.
126  //
127  // Schema:
128  //  1) Every query in `interval_intersect` must have both `ts` and
129  //     `dur` columns. It must also have an `id` column: this is necessary
130  //     because of the current implementation; this may be relaxed in the
131  //     future.
132  //    * Both `ts` and `dur` columns. In this case, the `base` interval
133  //      must overlap with one interval from each of `interval_intersect`
134  //      to be included.
135  //    * `ts` column without `dur` column. In this case, the `base`
136  //      timestamp must lie *between* one interval from each of
137  //      `interval_intersect` to be included.
138  //  3) The query in `base` must also have an `id` column: this is necessary
139  //     because of the current implementation; this may be relaxed in the
140  //     future.
141  //
142  // Handling of `dur`:
143  // The `dur` column is treated specially. It is changed to have the amount of
144  // time for which intervals in `base` overlaps with all structured queries in
145  // `interval_intersect`.
146  //
147  // Overlap with multiple intervals:
148  // If one row in `base` overlaps with *multiple* sets of intervals from each
149  // query in `interval_intersect`, then *multiple* rows will be
150  // produced, one for each sets of overlaps.
151  //
152  // Example in ASCII art:
153  //   base:       [-----------]     [--------]
154  //   ii1 :                [-----------]
155  //   ii2 :      [---------------] [-----]
156  // output:                [--]     [--]
157  message IntervalIntersect {
158    // The base query
159    optional PerfettoSqlStructuredQuery base = 1;
160    repeated PerfettoSqlStructuredQuery interval_intersect = 2;
161  }
162
163  // An opaque id field for the query. The convention is to use underscores
164  // and lower case (foo_bar) but this is not enforced. Optional in the general
165  // case but strongly recommended for good error messages. Required in cases
166  // where this query is used as a "shared" query.
167  optional string id = 1;
168
169  // Represents the "source" of the query which will be translared to an SQL
170  // "FROM" clause. One of the following is required.
171  oneof source {
172    // Source is an SQL table, possible in a PerfettoSQL module.
173    Table table = 2;
174
175    // Source is an arbitrary snippet of SQL.
176    Sql sql = 3;
177
178    // Source is a simple set of slices.
179    SimpleSlices simple_slices = 4;
180
181    // Source is a nested query. Useful for aliasing columns,
182    // filtering etc.
183    PerfettoSqlStructuredQuery inner_query = 5;
184
185    // Source is a nested query with the given id which should be
186    // looked up in some external data structure.
187    //
188    // This field is quite special and cannot be used in all StructuredQuery
189    // contexts. It exists to share some common structured queries between many
190    // other structured queries and is only available in contexts where this is
191    // supported.
192    //
193    // Contexts where this is supported that we are aware of:
194    // 1) Trace-Based Metrics v2
195    //
196    // Please see the documentation of the embedding system for more context.
197    string inner_query_id = 6;
198
199    // Source is an interval intersect operation. See IntervalIntersect
200    // documentation for more information.
201    IntervalIntersect interval_intersect = 7;
202  }
203
204  // Represents a single filter on a column.
205  message Filter {
206    // The column name to be filtered. Required.
207    optional string column_name = 1;
208
209    // The operator to use to perform filtering. Required.
210    enum Operator {
211      UNKNOWN = 0;
212      EQUAL = 1;
213      NOT_EQUAL = 2;
214      LESS_THAN = 3;
215      LESS_THAN_EQUAL = 4;
216      GREATER_THAN = 5;
217      GREATER_THAN_EQUAL = 6;
218      IS_NULL = 8;
219      IS_NOT_NULL = 9;
220
221      // Unix GLOB. Only makes sense for string columns.
222      GLOB = 7;
223    }
224    optional Operator op = 2;
225
226    // The RHS for filtering. All values specified here will be ORed together
227    // allowing easy IN and GLOB IN filtering. If operation is different than
228    // IS_NULL or IS_NOT_NULL, at least one of these fields must be non-empty.
229    // Only the first non-empty field will be considered.
230    repeated string string_rhs = 3;
231    repeated double double_rhs = 4;
232    repeated int64 int64_rhs = 5;
233  }
234
235  // A set of filters which are ANDed together. Optional, can be empty.
236  repeated Filter filters = 8;
237
238  // Represents a GROUP BY + aggregation operation in SQL. Optional.
239  message GroupBy {
240    // The column names to group by. At least one column is required.
241    repeated string column_names = 1;
242
243    // The list of aggregations to perform.
244    message Aggregate {
245      enum Op {
246        UNSPECIFIED = 0;
247        COUNT = 1;
248        SUM = 2;
249        MIN = 3;
250        MAX = 4;
251        MEAN = 5;
252        MEDIAN = 6;
253        DURATION_WEIGHTED_MEAN = 7;
254      }
255
256      optional string column_name = 1;
257      optional Op op = 2;
258      optional string result_column_name = 3;
259    }
260    repeated Aggregate aggregates = 2;
261  }
262  optional GroupBy group_by = 9;
263
264  // Represents the selection of columns from the source. Maps to a SELECT
265  // operation. Optional.
266  //
267  // Depending on whether `group_by` was specified the columns available from
268  // the source will be different:
269  // * if `group_by` is specified, all the columns in `group_by.column_names`
270  //   and `group_by.aggregates.result_column_name` are available.
271  // * if `group_by` is not specified, all columns in the source are eligible.
272  //
273  // If this is *not* specified, all columns from the source will be output.
274  message SelectColumn {
275    // The existing name of the column from the source. Required.
276    optional string column_name = 1;
277
278    // The new name of the column. If not set, the name of the column is
279    // `column_name`. Optional.
280    optional string alias = 2;
281  }
282  repeated SelectColumn select_columns = 10;
283}
284