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