• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1--
2-- Copyright 2022 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
17INCLUDE PERFETTO MODULE android.process_metadata;
18
19INCLUDE PERFETTO MODULE android.suspend;
20
21INCLUDE PERFETTO MODULE slices.flow;
22
23-- Count Binder transactions per process.
24CREATE PERFETTO VIEW android_binder_metrics_by_process (
25  -- Name of the process that started the binder transaction.
26  process_name STRING,
27  -- PID of the process that started the binder transaction.
28  pid LONG,
29  -- Name of the slice with binder transaction.
30  slice_name STRING,
31  -- Number of binder transactions in process in slice.
32  event_count LONG
33) AS
34SELECT
35  process.name AS process_name,
36  process.pid AS pid,
37  slice.name AS slice_name,
38  count(*) AS event_count
39FROM slice
40JOIN thread_track
41  ON slice.track_id = thread_track.id
42JOIN thread
43  ON thread.utid = thread_track.utid
44JOIN process
45  ON thread.upid = process.upid
46WHERE
47  slice.name GLOB 'binder*'
48GROUP BY
49  process_name,
50  slice_name;
51
52CREATE PERFETTO TABLE _binder_txn_merged AS
53WITH
54  maybe_broken_binder_txn AS (
55    -- Fetch the broken binder txns first, i.e, the txns that have children slices
56    -- They may be broken because synchronous txns are typically blocked sleeping while
57    -- waiting for a response.
58    -- These broken txns will be excluded below in the binder_txn CTE
59    SELECT
60      ancestor.id
61    FROM slice
62    JOIN slice AS ancestor
63      ON ancestor.id = slice.parent_id
64    WHERE
65      ancestor.name = 'binder transaction'
66    GROUP BY
67      ancestor.id
68  ),
69  nested_binder_txn AS (
70    -- Detect the non-broken cases which are just nested binder txns
71    SELECT DISTINCT
72      root_node_id AS id
73    FROM _slice_following_flow!(maybe_broken_binder_txn)
74  ),
75  broken_binder_txn AS (
76    -- Exclude the nested txns from the 'maybe broken' set
77    SELECT
78      *
79    FROM maybe_broken_binder_txn
80    EXCEPT
81    SELECT
82      *
83    FROM nested_binder_txn
84  ),
85  -- Adding MATERIALIZED here matters in cases where there are few/no binder
86  -- transactions in the trace. Our cost estimation is not good enough to allow
87  -- the query planner to see through to this fact. Instead, our cost estimation
88  -- causes repeated queries on this table which is slow because it's an O(n)
89  -- query.
90  --
91  -- We should fix this by doing some (ideally all) of the following:
92  --  1) Add support for columnar tables in SQL which will allow for
93  --     "subsetting" the slice table to only contain binder transactions.
94  --  2) Make this query faster by adding improving string filtering.
95  --  3) Add caching so that even if these queries happen many times, they are
96  --     fast.
97  --  4) Improve cost estimation algorithm to allow the joins to happen the
98  --     right way around.
99  binder_txn AS MATERIALIZED (
100    SELECT
101      slice.id AS binder_txn_id,
102      process.name AS process_name,
103      thread.name AS thread_name,
104      thread.utid AS utid,
105      thread.tid AS tid,
106      process.pid AS pid,
107      process.upid AS upid,
108      slice.ts,
109      slice.dur,
110      thread.is_main_thread
111    FROM slice
112    JOIN thread_track
113      ON slice.track_id = thread_track.id
114    JOIN thread
115      USING (utid)
116    JOIN process
117      USING (upid)
118    LEFT JOIN broken_binder_txn
119      ON broken_binder_txn.id = slice.id
120    WHERE
121      slice.name = 'binder transaction' AND broken_binder_txn.id IS NULL
122  ),
123  binder_reply AS (
124    SELECT
125      binder_txn.*,
126      binder_reply.ts AS server_ts,
127      binder_reply.dur AS server_dur,
128      binder_reply.id AS binder_reply_id,
129      reply_thread.name AS server_thread,
130      reply_process.name AS server_process,
131      reply_thread.utid AS server_utid,
132      reply_thread.tid AS server_tid,
133      reply_process.pid AS server_pid,
134      reply_process.upid AS server_upid,
135      aidl.name AS aidl_name,
136      aidl.ts AS aidl_ts,
137      aidl.dur AS aidl_dur
138    FROM binder_txn
139    JOIN flow AS binder_flow
140      ON binder_txn.binder_txn_id = binder_flow.slice_out
141    JOIN slice AS binder_reply
142      ON binder_flow.slice_in = binder_reply.id
143    JOIN thread_track AS reply_thread_track
144      ON binder_reply.track_id = reply_thread_track.id
145    JOIN thread AS reply_thread
146      ON reply_thread.utid = reply_thread_track.utid
147    JOIN process AS reply_process
148      ON reply_process.upid = reply_thread.upid
149    LEFT JOIN slice AS aidl
150      ON aidl.parent_id = binder_reply.id
151      AND (
152        -- Filter for only server side AIDL slices as there are some client side ones for cpp
153        aidl.name GLOB 'AIDL::*Server'
154        OR aidl.name GLOB 'AIDL::*server'
155        OR aidl.name GLOB 'HIDL::*server'
156      )
157  )
158SELECT
159  min(aidl_name) AS aidl_name,
160  aidl_ts,
161  aidl_dur,
162  binder_txn_id,
163  process_name AS client_process,
164  thread_name AS client_thread,
165  upid AS client_upid,
166  utid AS client_utid,
167  tid AS client_tid,
168  pid AS client_pid,
169  is_main_thread,
170  ts AS client_ts,
171  dur AS client_dur,
172  binder_reply_id,
173  server_process,
174  server_thread,
175  server_upid,
176  server_utid,
177  server_tid,
178  server_pid,
179  server_ts,
180  server_dur
181FROM binder_reply
182WHERE
183  client_dur != -1 AND server_dur != -1 AND client_dur >= server_dur
184GROUP BY
185  process_name,
186  thread_name,
187  binder_txn_id,
188  binder_reply_id;
189
190CREATE PERFETTO TABLE _oom_score AS
191SELECT
192  process.upid,
193  cast_int!(c.value) AS value,
194  c.ts,
195  coalesce(lead(ts) OVER (PARTITION BY upid ORDER BY ts), trace_end()) AS end_ts
196FROM counter AS c
197JOIN process_counter_track AS t
198  ON c.track_id = t.id
199JOIN process
200  USING (upid)
201WHERE
202  t.name = 'oom_score_adj';
203
204CREATE PERFETTO INDEX _oom_score_idx ON _oom_score(upid, ts);
205
206-- Breakdown synchronous binder transactions per txn.
207-- It returns data about the client and server ends of every binder transaction.
208CREATE PERFETTO VIEW _sync_binder_metrics_by_txn AS
209SELECT
210  binder.*,
211  client_oom.value AS client_oom_score,
212  server_oom.value AS server_oom_score
213FROM _binder_txn_merged AS binder
214LEFT JOIN _oom_score AS client_oom
215  ON binder.client_upid = client_oom.upid
216  AND binder.client_ts BETWEEN client_oom.ts AND client_oom.end_ts
217LEFT JOIN _oom_score AS server_oom
218  ON binder.server_upid = server_oom.upid
219  AND binder.server_ts BETWEEN server_oom.ts AND server_oom.end_ts;
220
221CREATE PERFETTO VIEW _binder_txn AS
222SELECT
223  client_ts AS ts,
224  client_dur AS dur,
225  client_utid AS utid,
226  *
227FROM _sync_binder_metrics_by_txn;
228
229CREATE PERFETTO VIEW _binder_reply AS
230SELECT
231  server_ts AS ts,
232  server_dur AS dur,
233  server_utid AS utid,
234  *
235FROM _sync_binder_metrics_by_txn;
236
237CREATE VIRTUAL TABLE _sp_binder_txn_thread_state USING SPAN_JOIN (_binder_txn PARTITIONED utid, thread_state PARTITIONED utid);
238
239CREATE VIRTUAL TABLE _sp_binder_reply_thread_state USING SPAN_JOIN (_binder_reply PARTITIONED utid, thread_state PARTITIONED utid);
240
241-- Aggregated thread_states on the client and server side per binder txn
242-- This builds on the data from |_sync_binder_metrics_by_txn| and
243-- for each end (client and server) of the transaction, it returns
244-- the aggregated sum of all the thread state durations.
245-- The |thread_state_type| column represents whether a given 'aggregated thread_state'
246-- row is on the client or server side. 'binder_txn' is client side and 'binder_reply'
247-- is server side.
248CREATE PERFETTO VIEW android_sync_binder_thread_state_by_txn (
249  -- slice id of the binder txn
250  binder_txn_id LONG,
251  -- Client timestamp
252  client_ts TIMESTAMP,
253  -- Client tid
254  client_tid LONG,
255  -- slice id of the binder reply
256  binder_reply_id LONG,
257  -- Server timestamp
258  server_ts TIMESTAMP,
259  -- Server tid
260  server_tid LONG,
261  -- whether thread state is on the txn or reply side
262  thread_state_type STRING,
263  -- a thread_state that occurred in the txn
264  thread_state STRING,
265  -- aggregated dur of the |thread_state| in the txn
266  thread_state_dur DURATION,
267  -- aggregated count of the |thread_state| in the txn
268  thread_state_count LONG
269) AS
270SELECT
271  binder_txn_id,
272  client_ts,
273  client_tid,
274  binder_reply_id,
275  server_ts,
276  server_tid,
277  'binder_txn' AS thread_state_type,
278  state AS thread_state,
279  sum(dur) AS thread_state_dur,
280  count(dur) AS thread_state_count
281FROM _sp_binder_txn_thread_state
282GROUP BY
283  binder_txn_id,
284  binder_reply_id,
285  thread_state_type,
286  thread_state
287UNION ALL
288SELECT
289  binder_txn_id,
290  client_ts,
291  client_tid,
292  binder_reply_id,
293  server_ts,
294  server_tid,
295  'binder_reply' AS thread_state_type,
296  state AS thread_state,
297  sum(dur) AS thread_state_dur,
298  count(dur) AS thread_state_count
299FROM _sp_binder_reply_thread_state
300GROUP BY
301  binder_txn_id,
302  binder_reply_id,
303  thread_state_type,
304  thread_state;
305
306-- Aggregated blocked_functions on the client and server side per binder txn
307-- This builds on the data from |_sync_binder_metrics_by_txn| and
308-- for each end (client and server) of the transaction, it returns
309-- the aggregated sum of all the kernel blocked function durations.
310-- The |thread_state_type| column represents whether a given 'aggregated blocked_function'
311-- row is on the client or server side. 'binder_txn' is client side and 'binder_reply'
312-- is server side.
313CREATE PERFETTO VIEW android_sync_binder_blocked_functions_by_txn (
314  -- slice id of the binder txn
315  binder_txn_id LONG,
316  -- Client ts
317  client_ts TIMESTAMP,
318  -- Client tid
319  client_tid LONG,
320  -- slice id of the binder reply
321  binder_reply_id LONG,
322  -- Server ts
323  server_ts TIMESTAMP,
324  -- Server tid
325  server_tid LONG,
326  -- whether thread state is on the txn or reply side
327  thread_state_type STRING,
328  -- blocked kernel function in a thread state
329  blocked_function STRING,
330  -- aggregated dur of the |blocked_function| in the txn
331  blocked_function_dur DURATION,
332  -- aggregated count of the |blocked_function| in the txn
333  blocked_function_count LONG
334) AS
335SELECT
336  binder_txn_id,
337  client_ts,
338  client_tid,
339  binder_reply_id,
340  server_ts,
341  server_tid,
342  'binder_txn' AS thread_state_type,
343  blocked_function,
344  sum(dur) AS blocked_function_dur,
345  count(dur) AS blocked_function_count
346FROM _sp_binder_txn_thread_state
347WHERE
348  blocked_function IS NOT NULL
349GROUP BY
350  binder_txn_id,
351  binder_reply_id,
352  blocked_function
353UNION ALL
354SELECT
355  binder_txn_id,
356  client_ts,
357  client_tid,
358  binder_reply_id,
359  server_ts,
360  server_tid,
361  'binder_reply' AS thread_state_type,
362  blocked_function,
363  sum(dur) AS blocked_function_dur,
364  count(dur) AS blocked_function_count
365FROM _sp_binder_reply_thread_state
366WHERE
367  blocked_function IS NOT NULL
368GROUP BY
369  binder_txn_id,
370  binder_reply_id,
371  blocked_function;
372
373CREATE PERFETTO TABLE _async_binder_reply AS
374WITH
375  async_reply AS MATERIALIZED (
376    SELECT
377      id,
378      ts,
379      dur,
380      track_id,
381      name
382    FROM slice
383    WHERE
384      -- Filter for only server side AIDL slices as there are some client side ones for cpp
385      name GLOB 'AIDL::*Server'
386      OR name GLOB 'AIDL::*server'
387      OR name GLOB 'HIDL::*server'
388      OR name = 'binder async rcv'
389  )
390SELECT
391  *,
392  lead(name) OVER (PARTITION BY track_id ORDER BY ts) AS next_name,
393  lead(ts) OVER (PARTITION BY track_id ORDER BY ts) AS next_ts,
394  lead(dur) OVER (PARTITION BY track_id ORDER BY ts) AS next_dur
395FROM async_reply;
396
397CREATE PERFETTO TABLE _binder_async_txn_raw AS
398SELECT
399  slice.id AS binder_txn_id,
400  process.name AS client_process,
401  thread.name AS client_thread,
402  process.upid AS client_upid,
403  thread.utid AS client_utid,
404  thread.tid AS client_tid,
405  process.pid AS client_pid,
406  thread.is_main_thread,
407  slice.ts AS client_ts,
408  slice.dur AS client_dur
409FROM slice
410JOIN thread_track
411  ON slice.track_id = thread_track.id
412JOIN thread
413  USING (utid)
414JOIN process
415  USING (upid)
416WHERE
417  slice.name = 'binder transaction async';
418
419CREATE PERFETTO TABLE _binder_async_txn AS
420SELECT
421  iif(binder_reply.next_name = 'binder async rcv', NULL, binder_reply.next_name) AS aidl_name,
422  iif(binder_reply.next_name = 'binder async rcv', NULL, binder_reply.next_ts) AS aidl_ts,
423  iif(binder_reply.next_name = 'binder async rcv', NULL, binder_reply.next_dur) AS aidl_dur,
424  binder_txn.*,
425  binder_reply.id AS binder_reply_id,
426  reply_process.name AS server_process,
427  reply_thread.name AS server_thread,
428  reply_process.upid AS server_upid,
429  reply_thread.utid AS server_utid,
430  reply_thread.tid AS server_tid,
431  reply_process.pid AS server_pid,
432  binder_reply.ts AS server_ts,
433  binder_reply.dur AS server_dur
434FROM _binder_async_txn_raw AS binder_txn
435JOIN flow AS binder_flow
436  ON binder_txn.binder_txn_id = binder_flow.slice_out
437JOIN _async_binder_reply AS binder_reply
438  ON binder_flow.slice_in = binder_reply.id
439JOIN thread_track AS reply_thread_track
440  ON binder_reply.track_id = reply_thread_track.id
441JOIN thread AS reply_thread
442  ON reply_thread.utid = reply_thread_track.utid
443JOIN process AS reply_process
444  ON reply_process.upid = reply_thread.upid
445WHERE
446  binder_reply.name = 'binder async rcv';
447
448-- Breakdown asynchronous binder transactions per txn.
449-- It returns data about the client and server ends of every binder transaction async.
450CREATE PERFETTO VIEW _async_binder_metrics_by_txn AS
451SELECT
452  binder.*,
453  client_oom.value AS client_oom_score,
454  server_oom.value AS server_oom_score
455FROM _binder_async_txn AS binder
456LEFT JOIN _oom_score AS client_oom
457  ON binder.client_upid = client_oom.upid
458  AND binder.client_ts BETWEEN client_oom.ts AND client_oom.end_ts
459LEFT JOIN _oom_score AS server_oom
460  ON binder.server_upid = server_oom.upid
461  AND binder.server_ts BETWEEN server_oom.ts AND server_oom.end_ts;
462
463-- Breakdown binder transactions per txn.
464-- It returns data about the client and server ends of every binder transaction async.
465CREATE PERFETTO TABLE android_binder_txns (
466  -- Fully qualified name of the binder endpoint if existing.
467  aidl_name STRING,
468  -- Interface of the binder endpoint if existing.
469  interface STRING,
470  -- Method name of the binder endpoint if existing.
471  method_name STRING,
472  -- Timestamp the binder interface name was emitted. Proxy to 'ts' and 'dur' for async txns.
473  aidl_ts TIMESTAMP,
474  -- Duration of the binder interface name. Proxy to 'ts' and 'dur' for async txns.
475  aidl_dur DURATION,
476  -- Slice id of the binder txn.
477  binder_txn_id JOINID(slice.id),
478  -- Name of the client process.
479  client_process STRING,
480  -- Name of the client thread.
481  client_thread STRING,
482  -- Upid of the client process.
483  client_upid JOINID(process.id),
484  -- Utid of the client thread.
485  client_utid JOINID(thread.id),
486  -- Tid of the client thread.
487  client_tid LONG,
488  -- Pid of the client thread.
489  client_pid LONG,
490  -- Whether the txn was initiated from the main thread of the client process.
491  is_main_thread BOOL,
492  -- Timestamp of the client txn.
493  client_ts TIMESTAMP,
494  -- Wall clock dur of the client txn.
495  client_dur DURATION,
496  -- Slice id of the binder reply.
497  binder_reply_id JOINID(slice.id),
498  -- Name of the server process.
499  server_process STRING,
500  -- Name of the server thread.
501  server_thread STRING,
502  -- Upid of the server process.
503  server_upid JOINID(process.id),
504  -- Utid of the server thread.
505  server_utid JOINID(thread.id),
506  -- Tid of the server thread.
507  server_tid LONG,
508  -- Pid of the server thread.
509  server_pid LONG,
510  -- Timestamp of the server txn.
511  server_ts TIMESTAMP,
512  -- Wall clock dur of the server txn.
513  server_dur DURATION,
514  -- Oom score of the client process at the start of the txn.
515  client_oom_score LONG,
516  -- Oom score of the server process at the start of the reply.
517  server_oom_score LONG,
518  -- Whether the txn is synchronous or async (oneway).
519  is_sync BOOL,
520  -- Monotonic clock dur of the client txn.
521  client_monotonic_dur DURATION,
522  -- Monotonic clock dur of the server txn.
523  server_monotonic_dur DURATION,
524  -- Client package version_code.
525  client_package_version_code LONG,
526  -- Server package version_code.
527  server_package_version_code LONG,
528  -- Whether client package is debuggable.
529  is_client_package_debuggable BOOL,
530  -- Whether server package is debuggable.
531  is_server_package_debuggable BOOL
532) AS
533WITH
534  all_binder AS (
535    SELECT
536      *,
537      1 AS is_sync
538    FROM _sync_binder_metrics_by_txn
539    UNION ALL
540    SELECT
541      *,
542      0 AS is_sync
543    FROM _async_binder_metrics_by_txn
544  )
545SELECT
546  str_split(aidl_name, '::', 2) AS interface,
547  str_split(aidl_name, '::', 3) AS method_name,
548  all_binder.*,
549  _extract_duration_without_suspend(client_ts, client_dur) AS client_monotonic_dur,
550  _extract_duration_without_suspend(server_ts, server_dur) AS server_monotonic_dur,
551  client_process_metadata.version_code AS client_package_version_code,
552  server_process_metadata.version_code AS server_package_version_code,
553  client_process_metadata.debuggable AS is_client_package_debuggable,
554  server_process_metadata.debuggable AS is_server_package_debuggable
555FROM all_binder
556LEFT JOIN android_process_metadata AS client_process_metadata
557  ON all_binder.client_upid = client_process_metadata.upid
558LEFT JOIN android_process_metadata AS server_process_metadata
559  ON all_binder.server_upid = server_process_metadata.upid;
560
561-- Returns a DAG of all outgoing binder txns from a process.
562-- The roots of the graph are the threads making the txns and the graph flows from:
563-- thread -> server_process -> AIDL interface -> AIDL method.
564-- The weights of each node represent the wall execution time in the server_process.
565CREATE PERFETTO FUNCTION android_binder_outgoing_graph(
566    -- Upid of process to generate an outgoing graph for.
567    upid JOINID(process.id)
568)
569RETURNS TABLE (
570  -- Pprof of outgoing binder txns.
571  pprof BYTES
572) AS
573WITH
574  threads AS (
575    SELECT
576      binder_txn_id,
577      cat_stacks(client_thread) AS stack
578    FROM android_binder_txns
579    WHERE
580      (
581        NOT $upid IS NULL AND client_upid = $upid
582      ) OR (
583        $upid IS NULL
584      )
585  ),
586  server_process AS (
587    SELECT
588      binder_txn_id,
589      cat_stacks(stack, server_process) AS stack
590    FROM android_binder_txns
591    JOIN threads
592      USING (binder_txn_id)
593  ),
594  end_points AS (
595    SELECT
596      binder_txn_id,
597      cat_stacks(stack, str_split(aidl_name, '::', iif(aidl_name GLOB 'AIDL*', 2, 1))) AS stack
598    FROM android_binder_txns
599    JOIN server_process
600      USING (binder_txn_id)
601  ),
602  aidl_names AS (
603    SELECT
604      binder_txn_id,
605      server_dur,
606      cat_stacks(stack, str_split(aidl_name, '::', iif(aidl_name GLOB 'AIDL*', 3, 2))) AS stack
607    FROM android_binder_txns
608    JOIN end_points
609      USING (binder_txn_id)
610  )
611SELECT
612  experimental_profile(stack, 'duration', 'ns', server_dur) AS pprof
613FROM aidl_names;
614
615-- Returns a DAG of all incoming binder txns from a process.
616-- The roots of the graph are the clients making the txns and the graph flows from:
617-- client_process -> AIDL interface -> AIDL method.
618-- The weights of each node represent the wall execution time in the server_process.
619CREATE PERFETTO FUNCTION android_binder_incoming_graph(
620    -- Upid of process to generate an incoming graph for.
621    upid JOINID(process.id)
622)
623RETURNS TABLE (
624  -- Pprof of incoming binder txns.
625  pprof BYTES
626) AS
627WITH
628  client_process AS (
629    SELECT
630      binder_txn_id,
631      cat_stacks(client_process) AS stack
632    FROM android_binder_txns
633    WHERE
634      (
635        NOT $upid IS NULL AND server_upid = $upid
636      ) OR (
637        $upid IS NULL
638      )
639  ),
640  end_points AS (
641    SELECT
642      binder_txn_id,
643      cat_stacks(stack, str_split(aidl_name, '::', iif(aidl_name GLOB 'AIDL*', 2, 1))) AS stack
644    FROM android_binder_txns
645    JOIN client_process
646      USING (binder_txn_id)
647  ),
648  aidl_names AS (
649    SELECT
650      binder_txn_id,
651      server_dur,
652      cat_stacks(stack, str_split(aidl_name, '::', iif(aidl_name GLOB 'AIDL*', 3, 2))) AS stack
653    FROM android_binder_txns
654    JOIN end_points
655      USING (binder_txn_id)
656  )
657SELECT
658  experimental_profile(stack, 'duration', 'ns', server_dur) AS pprof
659FROM aidl_names;
660
661-- Returns a graph of all binder txns in a trace.
662-- The nodes are client_process and server_process.
663-- The weights of each node represent the wall execution time in the server_process.
664CREATE PERFETTO FUNCTION android_binder_graph(
665    -- Matches txns from client_processes greater than or equal to the OOM score.
666    min_client_oom_score LONG,
667    -- Matches txns from client_processes less than or equal to the OOM score.
668    max_client_oom_score LONG,
669    -- Matches txns to server_processes greater than or equal to the OOM score.
670    min_server_oom_score LONG,
671    -- Matches txns to server_processes less than or equal to the OOM score.
672    max_server_oom_score LONG
673)
674RETURNS TABLE (
675  -- Pprof of binder txns.
676  pprof BYTES
677) AS
678WITH
679  clients AS (
680    SELECT
681      binder_txn_id,
682      cat_stacks(client_process) AS stack
683    FROM android_binder_txns
684    WHERE
685      client_oom_score BETWEEN $min_client_oom_score AND $max_client_oom_score
686  ),
687  servers AS (
688    SELECT
689      binder_txn_id,
690      server_dur,
691      cat_stacks(stack, server_process) AS stack
692    FROM android_binder_txns
693    JOIN clients
694      USING (binder_txn_id)
695    WHERE
696      server_oom_score BETWEEN $min_server_oom_score AND $max_server_oom_score
697  )
698SELECT
699  experimental_profile(stack, 'duration', 'ns', server_dur) AS pprof
700FROM servers;
701