• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1--
2-- Copyright 2024 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--     https://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-- Returns an instance of `RawMarkerTable` as defined in
17-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
18CREATE PERFETTO FUNCTION _export_firefox_thread_markers()
19RETURNS STRING AS
20SELECT
21  json_object(
22    'data', json_array(),
23    'name', json_array(),
24    'startTime', json_array(),
25    'endTime', json_array(),
26    'phase', json_array(),
27    'category', json_array(),
28    -- threadId?: Tid[]
29    'length', 0
30  );
31
32-- Returns an empty instance of `NativeSymbolTable` as defined in
33-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
34CREATE PERFETTO FUNCTION _export_firefox_native_symbol_table()
35RETURNS STRING AS
36SELECT
37  json_object(
38    'libIndex', json_array(),
39    'address', json_array(),
40    'name', json_array(),
41    'functionSize', json_array(),
42    'length', 0
43  );
44
45-- Returns an empty instance of `ResourceTable` as defined in
46-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
47CREATE PERFETTO FUNCTION _export_firefox_resource_table()
48RETURNS STRING AS
49SELECT
50  json_object(
51    'length', 0,
52    'lib', json_array(),
53    'name', json_array(),
54    'host', json_array(),
55    'type', json_array()
56  );
57
58-- Materialize this intermediate table and sort by `callsite_id` to speedup the
59-- generation of the stack_table further down.
60CREATE PERFETTO TABLE _export_to_firefox_table AS
61WITH
62  symbol AS (
63    SELECT
64      symbol_set_id,
65      rank() OVER (PARTITION BY symbol_set_id ORDER BY id DESC RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) - 1 AS inline_depth,
66      count() OVER (PARTITION BY symbol_set_id) - 1 AS max_inline_depth,
67      name
68    FROM stack_profile_symbol
69  ),
70  callsite_base AS (
71    SELECT
72      id,
73      parent_id,
74      name,
75      symbol_set_id,
76      iif(inline_count = 0, 0, inline_count - 1) AS max_inline_depth
77    FROM (
78      SELECT
79        spc.id,
80        spc.parent_id,
81        coalesce(s.name, spf.name, '') AS name,
82        sfp.symbol_set_id,
83        (
84          SELECT
85            count(*)
86          FROM stack_profile_symbol AS s
87          WHERE
88            s.symbol_set_id = sfp.symbol_set_id
89        ) AS inline_count
90      FROM stack_profile_callsite AS spc
91      JOIN stack_profile_frame AS spf
92        ON (
93          spc.frame_id = spf.id
94        )
95    )
96  ),
97  callsite_recursive AS (
98    SELECT
99      s.utid,
100      spc.id,
101      spc.parent_id,
102      spc.frame_id
103    FROM (
104      SELECT DISTINCT
105        callsite_id,
106        utid
107      FROM perf_sample
108    ) AS s
109    JOIN stack_profile_callsite AS spc
110      ON spc.id = s.callsite_id
111    UNION ALL
112    SELECT
113      child.utid,
114      parent.id,
115      parent.parent_id,
116      parent.frame_id
117    FROM callsite_recursive AS child
118    JOIN stack_profile_callsite AS parent
119      ON child.parent_id = parent.id
120  ),
121  unique_callsite AS (
122    SELECT DISTINCT
123      *
124    FROM callsite_recursive
125  ),
126  expanded_callsite AS (
127    SELECT
128      c.utid,
129      c.id,
130      c.parent_id,
131      c.frame_id,
132      coalesce(s.name, spf.name, '') AS name,
133      coalesce(s.inline_depth, 0) AS inline_depth,
134      coalesce(s.max_inline_depth, 0) AS max_inline_depth
135    FROM unique_callsite AS c
136    JOIN stack_profile_frame AS spf
137      ON (
138        c.frame_id = spf.id
139      )
140    LEFT JOIN symbol AS s
141      USING (symbol_set_id)
142  )
143SELECT
144  utid,
145  id AS callsite_id,
146  parent_id AS parent_callsite_id,
147  name,
148  inline_depth,
149  inline_depth = max_inline_depth AS is_most_inlined,
150  dense_rank() OVER (PARTITION BY utid ORDER BY id, inline_depth) - 1 AS stack_table_index,
151  dense_rank() OVER (PARTITION BY utid ORDER BY frame_id, inline_depth) - 1 AS frame_table_index,
152  dense_rank() OVER (PARTITION BY utid ORDER BY name) - 1 AS func_table_index,
153  dense_rank() OVER (PARTITION BY utid ORDER BY name) - 1 AS string_table_index
154FROM expanded_callsite
155ORDER BY
156  utid,
157  id;
158
159-- Returns an instance of `SamplesTable` as defined in
160-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
161-- for the given `utid`.
162CREATE PERFETTO FUNCTION _export_firefox_samples_table(
163    utid JOINID(thread.id)
164)
165RETURNS STRING AS
166WITH
167  samples_table AS (
168    SELECT
169      row_number() OVER (ORDER BY s.id) - 1 AS idx,
170      s.ts AS time,
171      t.stack_table_index AS stack
172    FROM perf_sample AS s
173    JOIN _export_to_firefox_table AS t
174      USING (utid, callsite_id)
175    WHERE
176      utid = $utid AND t.is_most_inlined
177  )
178SELECT
179  json_object(
180    -- responsiveness?: Array<?Milliseconds>
181    -- eventDelay?: Array<?Milliseconds>
182    'stack', json_group_array(stack ORDER BY idx),
183    'time', json_group_array(time ORDER BY idx),
184    'weight', NULL,
185    'weightType', 'samples',
186    -- threadCPUDelta?: Array<number | null>
187    -- threadId?: Tid[]
188    'length', count(*)
189  )
190FROM samples_table;
191
192-- Returns an instance of `StackTable` as defined in
193-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
194-- for the given `utid`.
195CREATE PERFETTO FUNCTION _export_firefox_stack_table(
196    utid JOINID(thread.id)
197)
198RETURNS STRING AS
199WITH
200  parent AS (
201    SELECT
202      *
203    FROM _export_to_firefox_table
204    WHERE
205      utid = $utid
206  ),
207  stack_table AS (
208    SELECT
209      stack_table_index AS idx,
210      frame_table_index AS frame,
211      0 AS category,
212      0 AS subcategory,
213      -- It is key that this lookup is fast. That is why we have materialized
214      -- the `_export_to_firefox_table` table and sorted it by `utid` and
215      -- `callsite_id`.
216      iif(
217        child.inline_depth = 0,
218        (
219          SELECT
220            stack_table_index
221          FROM parent
222          WHERE
223            child.parent_callsite_id = parent.callsite_id AND parent.is_most_inlined
224        ),
225        (
226          SELECT
227            stack_table_index
228          FROM parent
229          WHERE
230            child.callsite_id = parent.callsite_id
231            AND child.inline_depth - 1 = parent.inline_depth
232        )
233      ) AS prefix
234    FROM _export_to_firefox_table AS child
235    WHERE
236      child.utid = $utid
237  )
238SELECT
239  json_object(
240    'frame', json_group_array(frame ORDER BY idx),
241    'category', json_group_array(category ORDER BY idx),
242    'subcategory', json_group_array(subcategory ORDER BY idx),
243    'prefix', json_group_array(prefix ORDER BY idx),
244    'length', count(*)
245  )
246FROM stack_table;
247
248-- Returns an instance of `FrameTable` as defined in
249-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
250-- for the given `utid`.
251CREATE PERFETTO FUNCTION _export_firefox_frame_table(
252    utid JOINID(thread.id)
253)
254RETURNS STRING AS
255WITH
256  frame_table AS (
257    SELECT DISTINCT
258      frame_table_index AS idx,
259      -1 AS address,
260      inline_depth,
261      0 AS category,
262      0 AS subcategory,
263      func_table_index AS func,
264      NULL AS native_symbol,
265      NULL AS inner_window_id,
266      NULL AS implementation,
267      NULL AS line,
268      NULL AS column
269    FROM _export_to_firefox_table
270    WHERE
271      utid = $utid
272  )
273SELECT
274  json_object(
275    'address', json_group_array(address ORDER BY idx),
276    'inlineDepth', json_group_array(inline_depth ORDER BY idx),
277    'category', json_group_array(category ORDER BY idx),
278    'subcategory', json_group_array(subcategory ORDER BY idx),
279    'func', json_group_array(func ORDER BY idx),
280    'nativeSymbol', json_group_array(native_symbol ORDER BY idx),
281    'innerWindowID', json_group_array(inner_window_id ORDER BY idx),
282    'implementation', json_group_array(implementation ORDER BY idx),
283    'line', json_group_array(line ORDER BY idx),
284    'column', json_group_array(column ORDER BY idx),
285    'length', count(*)
286  )
287FROM frame_table;
288
289-- Returns an array of strings for the given `utid`.
290CREATE PERFETTO FUNCTION _export_firefox_string_array(
291    utid JOINID(thread.id)
292)
293RETURNS STRING AS
294WITH
295  string_table AS (
296    SELECT DISTINCT
297      string_table_index AS idx,
298      name AS str
299    FROM _export_to_firefox_table
300    WHERE
301      utid = $utid
302  )
303SELECT
304  json_group_array(str ORDER BY idx)
305FROM string_table;
306
307-- Returns an instance of `FuncTable` as defined in
308-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
309-- for the given `utid`.
310CREATE PERFETTO FUNCTION _export_firefox_func_table(
311    utid JOINID(thread.id)
312)
313RETURNS STRING AS
314WITH
315  func_table AS (
316    SELECT DISTINCT
317      func_table_index AS idx,
318      string_table_index AS name,
319      FALSE AS is_js,
320      FALSE AS relevant_for_js,
321      -1 AS resource,
322      NULL AS file_name,
323      NULL AS line_number,
324      NULL AS column_number
325    FROM _export_to_firefox_table
326    WHERE
327      utid = $utid
328  )
329SELECT
330  json_object(
331    'name', json_group_array(name ORDER BY idx),
332    'isJS', json_group_array(is_js ORDER BY idx),
333    'relevantForJS', json_group_array(relevant_for_js ORDER BY idx),
334    'resource', json_group_array(resource ORDER BY idx),
335    'fileName', json_group_array(file_name ORDER BY idx),
336    'lineNumber', json_group_array(line_number ORDER BY idx),
337    'columnNumber', json_group_array(column_number ORDER BY idx),
338    'length', count(*)
339  )
340FROM func_table;
341
342-- Returns an instance of `Thread` as defined in
343-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
344-- for the given `utid`.
345CREATE PERFETTO FUNCTION _export_firefox_thread(
346    utid JOINID(thread.id)
347)
348RETURNS STRING AS
349SELECT
350  -- jsTracer?: JsTracerTable
351  -- isPrivateBrowsing?: boolean
352  -- userContextId?: number
353  json_object(
354    'processType', 'default',
355    -- processStartupTime: Milliseconds
356    -- processShutdownTime: Milliseconds | null
357    -- registerTime: Milliseconds
358    -- unregisterTime: Milliseconds | null
359    -- pausedRanges: PausedRange[]
360    -- showMarkersInTimeline?: boolean
361    'name', coalesce(thread.name, ''),
362    'isMainThread', FALSE,
363    -- 'eTLD+1'?: string
364    -- processName?: string
365    -- isJsTracer?: boolean
366    'pid', coalesce(process.pid, 0),
367    'tid', coalesce(thread.tid, 0),
368    'samples', json(_export_firefox_samples_table($utid)),
369    -- jsAllocations?: JsAllocationsTable
370    -- nativeAllocations?: NativeAllocationsTable
371    'markers', json(_export_firefox_thread_markers()),
372    'stackTable', json(_export_firefox_stack_table($utid)),
373    'frameTable', json(_export_firefox_frame_table($utid)),
374    'stringArray', json(_export_firefox_string_array($utid)),
375    'funcTable', json(_export_firefox_func_table($utid)),
376    'resourceTable', json(_export_firefox_resource_table()),
377    'nativeSymbols', json(_export_firefox_native_symbol_table())
378  )
379FROM thread
380JOIN process
381  USING (upid)
382WHERE
383  utid = $utid;
384
385-- Returns an array of `Thread` instances as defined in
386-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
387-- for each thread present in the trace.
388CREATE PERFETTO FUNCTION _export_firefox_threads()
389RETURNS STRING AS
390SELECT
391  json_group_array(json(_export_firefox_thread(utid)))
392FROM thread;
393
394-- Returns an instance of `ProfileMeta` as defined in
395-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
396CREATE PERFETTO FUNCTION _export_firefox_meta()
397RETURNS STRING AS
398SELECT
399  -- device?: string
400  -- importedFrom?: string
401  -- usesOnlyOneStackType?: boolean
402  -- doesNotUseFrameImplementation?: boolean
403  -- sourceCodeIsNotOnSearchfox?: boolean
404  -- extra?: ExtraProfileInfoSection[]
405  -- initialVisibleThreads?: ThreadIndex[]
406  -- initialSelectedThreads?: ThreadIndex[]
407  -- keepProfileThreadOrder?: boolean
408  -- gramsOfCO2ePerKWh?: number
409  json_object(
410    'interval', 1,
411    'startTime', 0,
412    -- default
413    -- endTime?: Milliseconds
414    -- profilingStartTime?: Milliseconds
415    -- profilingEndTime?: Milliseconds
416    'processType', 0,
417    -- extensions?: ExtensionTable
418    'categories', json_array(
419      json_object('name', 'Other', 'color', 'grey', 'subcategories', json_array('Other'))
420    ),
421    'product', 'Perfetto',
422    'stackwalk', 1,
423    -- Taken from a generated profile
424    -- debug?: boolean
425    'version', 29,
426    -- Taken from a generated profile
427    'preprocessedProfileVersion', 48,
428    -- abi?: string
429    -- misc?: string
430    -- oscpu?: string
431    -- mainMemory?: Bytes
432    -- platform?: 'Android' | 'Windows' | 'Macintosh' | 'X11' | string
433    -- toolkit?: 'gtk' | 'gtk3' | 'windows' | 'cocoa' | 'android' | string
434    -- appBuildID?: string
435    -- arguments?: string
436    -- sourceURL?: string
437    -- physicalCPUs?: number
438    -- logicalCPUs?: number
439    -- CPUName?: string
440    -- symbolicated?: boolean
441    -- symbolicationNotSupported?: boolean
442    -- updateChannel?: 'default' | 'nightly' | 'nightly-try' | 'aurora' | 'beta' | 'release' | 'esr' | string
443    -- visualMetrics?: VisualMetrics
444    -- configuration?: ProfilerConfiguration
445    'markerSchema', json_array(),
446    'sampleUnits', json_object('time', 'ms', 'eventDelay', 'ms', 'threadCPUDelta', 'µs')
447  );
448
449-- Dumps all trace data as a Firefox profile json string
450-- See `Profile` in
451-- https://github.com/firefox-devtools/profiler/blob/main/src/types/profile.js
452-- Also
453-- https://firefox-source-docs.mozilla.org/tools/profiler/code-overview.html
454--
455-- You would probably want to download the generated json and then open at
456-- https://https://profiler.firefox.com
457-- You can easily do this from the UI via the following SQL
458-- `SELECT CAST(export_to_firefox_profile() AS BLOB) AS profile;`
459-- The result will have a link for you to download this json as a file.
460CREATE PERFETTO FUNCTION export_to_firefox_profile()
461-- Json profile
462RETURNS STRING AS
463SELECT
464  json_object(
465    'meta', json(_export_firefox_meta()),
466    'libs', json_array(),
467    'pages', NULL,
468    'counters', NULL,
469    'profilerOverhead', NULL,
470    'threads', json(_export_firefox_threads()),
471    'profilingLog', NULL,
472    'profileGatheringLog', NULL
473  );
474