• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1--
2-- Copyright 2023 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.suspend;
18
19-- Extracts the blocking thread from a slice name
20CREATE PERFETTO FUNCTION android_extract_android_monitor_contention_blocking_thread(
21  -- Name of slice
22  slice_name STRING
23)
24-- Blocking thread
25RETURNS STRING AS
26SELECT STR_SPLIT(STR_SPLIT($slice_name, "with owner ", 1), " (", 0);
27
28-- Extracts the blocking thread tid from a slice name
29CREATE PERFETTO FUNCTION android_extract_android_monitor_contention_blocking_tid(
30  -- Name of slice
31  slice_name STRING
32)
33-- Blocking thread tid
34RETURNS INT AS
35SELECT CAST(STR_SPLIT(STR_SPLIT($slice_name, " (", 1), ")", 0) AS INT);
36
37-- Extracts the blocking method from a slice name
38CREATE PERFETTO FUNCTION android_extract_android_monitor_contention_blocking_method(
39  -- Name of slice
40  slice_name STRING
41)
42-- Blocking thread
43RETURNS STRING AS
44SELECT STR_SPLIT(STR_SPLIT($slice_name, ") at ", 1), "(", 0)
45    || "("
46    || STR_SPLIT(STR_SPLIT($slice_name, ") at ", 1), "(", 1);
47
48-- Extracts a shortened form of the blocking method name from a slice name.
49-- The shortened form discards the parameter and return
50-- types.
51CREATE PERFETTO FUNCTION android_extract_android_monitor_contention_short_blocking_method(
52  -- Name of slice
53  slice_name STRING
54)
55-- Blocking thread
56RETURNS STRING AS
57SELECT
58    STR_SPLIT(STR_SPLIT(android_extract_android_monitor_contention_blocking_method($slice_name), " ", 1), "(", 0);
59
60-- Extracts the monitor contention blocked method from a slice name
61CREATE PERFETTO FUNCTION android_extract_android_monitor_contention_blocked_method(
62  -- Name of slice
63  slice_name STRING
64)
65-- Blocking thread
66RETURNS STRING AS
67SELECT STR_SPLIT(STR_SPLIT($slice_name, "blocking from ", 1), "(", 0)
68    || "("
69    || STR_SPLIT(STR_SPLIT($slice_name, "blocking from ", 1), "(", 1);
70
71-- Extracts a shortened form of the monitor contention blocked method name
72-- from a slice name. The shortened form discards the parameter and return
73-- types.
74CREATE PERFETTO FUNCTION android_extract_android_monitor_contention_short_blocked_method(
75  -- Name of slice
76  slice_name STRING
77)
78-- Blocking thread
79RETURNS STRING AS
80SELECT
81    STR_SPLIT(STR_SPLIT(android_extract_android_monitor_contention_blocked_method($slice_name), " ", 1), "(", 0);
82
83-- Extracts the number of waiters on the monitor from a slice name
84CREATE PERFETTO FUNCTION android_extract_android_monitor_contention_waiter_count(
85  -- Name of slice
86  slice_name STRING
87)
88-- Count of waiters on the lock
89RETURNS INT AS
90SELECT CAST(STR_SPLIT(STR_SPLIT($slice_name, "waiters=", 1), " ", 0) AS INT);
91
92-- Extracts the monitor contention blocking source location from a slice name
93CREATE PERFETTO FUNCTION android_extract_android_monitor_contention_blocking_src(
94  -- Name of slice
95  slice_name STRING
96)
97-- Blocking thread
98RETURNS STRING AS
99SELECT STR_SPLIT(STR_SPLIT($slice_name, ")(", 1), ")", 0);
100
101-- Extracts the monitor contention blocked source location from a slice name
102CREATE PERFETTO FUNCTION android_extract_android_monitor_contention_blocked_src(
103  -- Name of slice
104  slice_name STRING
105)
106-- Blocking thread
107RETURNS STRING AS
108SELECT STR_SPLIT(STR_SPLIT($slice_name, ")(", 2), ")", 0);
109
110CREATE PERFETTO TABLE _valid_android_monitor_contention AS
111SELECT slice.id AS id
112FROM slice
113LEFT JOIN slice child
114  ON child.parent_id = slice.id
115LEFT JOIN slice grand_child
116  ON grand_child.parent_id = child.id
117WHERE
118  slice.name GLOB 'monitor contention*'
119  AND (child.name GLOB 'Lock contention*' OR child.name IS NULL)
120  AND (grand_child.name IS NULL)
121GROUP BY slice.id;
122
123-- Contains parsed monitor contention slices.
124--
125-- @column blocking_method Name of the method holding the lock.
126-- @column blocked_methhod Name of the method trying to acquire the lock.
127-- @column short_blocking_method Blocking_method without arguments and return types.
128-- @column short_blocked_method Blocked_method without arguments and return types.
129-- @column blocking_src File location of blocking_method in form <filename:linenumber>.
130-- @column blocked_src File location of blocked_method in form <filename:linenumber>.
131-- @column waiter_count Zero indexed number of threads trying to acquire the lock.
132-- @column blocking_utid Utid of thread holding the lock.
133-- @column blocking_thread_name Thread name of thread holding the lock.
134-- @column upid Upid of process experiencing lock contention.
135-- @column process_name Process name of process experiencing lock contention.
136-- @column id Slice id of lock contention.
137-- @column ts Timestamp of lock contention start.
138-- @column dur Wall clock duration of lock contention.
139-- @column monotonic_dur Monotonic clock duration of lock contention.
140-- @column track_id Thread track id of blocked thread.
141-- @column is_blocked_main_thread Whether the blocked thread is the main thread.
142-- @column is_blocking_main_thread Whether the blocking thread is the main thread.
143-- @column binder_reply_id Slice id of binder reply slice if lock contention was part of a binder txn.
144-- @column binder_reply_ts Timestamp of binder reply slice if lock contention was part of a binder txn.
145-- @column binder_reply_tid Tid of binder reply slice if lock contention was part of a binder txn.
146CREATE TABLE android_monitor_contention AS
147SELECT
148  android_extract_android_monitor_contention_blocking_method(slice.name) AS blocking_method,
149  android_extract_android_monitor_contention_blocked_method(slice.name)  AS blocked_method,
150  android_extract_android_monitor_contention_short_blocking_method(slice.name) AS short_blocking_method,
151  android_extract_android_monitor_contention_short_blocked_method(slice.name)  AS short_blocked_method,
152  android_extract_android_monitor_contention_blocking_src(slice.name) AS blocking_src,
153  android_extract_android_monitor_contention_blocked_src(slice.name) AS blocked_src,
154  android_extract_android_monitor_contention_waiter_count(slice.name) AS waiter_count,
155  thread.utid AS blocked_utid,
156  thread.name AS blocked_thread_name,
157  blocking_thread.utid AS blocking_utid,
158  android_extract_android_monitor_contention_blocking_thread(slice.name) AS blocking_thread_name,
159  android_extract_android_monitor_contention_blocking_tid(slice.name) AS blocking_tid,
160  thread.upid AS upid,
161  process.name AS process_name,
162  slice.id,
163  slice.ts,
164  slice.dur,
165  _extract_duration_without_suspend(slice.ts, slice.dur) AS monotonic_dur,
166  slice.track_id,
167  thread.is_main_thread AS is_blocked_thread_main,
168  thread.tid AS blocked_thread_tid,
169  blocking_thread.is_main_thread AS is_blocking_thread_main,
170  blocking_thread.tid AS blocking_thread_tid,
171  binder_reply.id AS binder_reply_id,
172  binder_reply.ts AS binder_reply_ts,
173  binder_reply_thread.tid AS binder_reply_tid,
174  process.pid
175FROM slice
176JOIN thread_track
177  ON thread_track.id = slice.track_id
178LEFT JOIN thread
179  USING (utid)
180LEFT JOIN process
181  USING (upid)
182LEFT JOIN ANCESTOR_SLICE(slice.id) binder_reply ON binder_reply.name = 'binder reply'
183LEFT JOIN thread_track binder_reply_thread_track ON binder_reply.track_id = binder_reply_thread_track.id
184LEFT JOIN thread binder_reply_thread ON binder_reply_thread_track.utid = binder_reply_thread.utid
185-- Before Android U, we didn't have blocking_thread tid (aosp/3000578). We do a LEFT JOIN instead
186-- of JOIN so that on older devices we can at least capture the list of contentions without edges.
187LEFT JOIN thread blocking_thread ON blocking_thread.tid = blocking_tid AND blocking_thread.upid = thread.upid
188JOIN _valid_android_monitor_contention ON _valid_android_monitor_contention.id = slice.id
189WHERE slice.name GLOB 'monitor contention*'
190  AND slice.dur != -1
191  AND short_blocking_method IS NOT NULL
192  AND short_blocked_method IS NOT NULL
193GROUP BY slice.id;
194
195CREATE INDEX _android_monitor_contention_blocking_utid_idx
196  ON android_monitor_contention (blocking_utid, ts);
197
198CREATE INDEX _android_monitor_contention_id_idx
199  ON android_monitor_contention (id);
200
201-- Monitor contention slices that are blocked by another monitor contention slice.
202-- They will have a |parent_id| field which is the id of the slice they are blocked by.
203CREATE PERFETTO TABLE _children AS
204SELECT parent.id AS parent_id, child.* FROM android_monitor_contention child
205JOIN android_monitor_contention parent ON parent.blocked_utid = child.blocking_utid
206AND child.ts BETWEEN parent.ts AND parent.ts + parent.dur;
207
208-- Monitor contention slices that are blocking another monitor contention slice.
209-- They will have a |child_id| field which is the id of the slice they are blocking.
210CREATE PERFETTO TABLE _parents AS
211SELECT parent.*, child.id AS child_id FROM android_monitor_contention parent
212JOIN android_monitor_contention child ON parent.blocked_utid = child.blocking_utid
213AND child.ts BETWEEN parent.ts AND parent.ts + parent.dur;
214
215-- Monitor contention slices that are neither blocking nor blocked by another monitor contention
216-- slice. They neither have |parent_id| nor |child_id| fields.
217CREATE TABLE _isolated AS
218WITH parents_and_children AS (
219 SELECT id FROM _children
220 UNION ALL
221 SELECT id FROM _parents
222), isolated AS (
223    SELECT id FROM android_monitor_contention
224    EXCEPT
225    SELECT id FROM parents_and_children
226  )
227SELECT * FROM android_monitor_contention JOIN isolated USING (id);
228
229-- Contains parsed monitor contention slices with the parent-child relationships.
230--
231-- @column parent_id Id of monitor contention slice blocking this contention.
232-- @column blocking_method Name of the method holding the lock.
233-- @column blocked_methhod Name of the method trying to acquire the lock.
234-- @column short_blocking_method Blocking_method without arguments and return types.
235-- @column short_blocked_method Blocked_method without arguments and return types.
236-- @column blocking_src File location of blocking_method in form <filename:linenumber>.
237-- @column blocked_src File location of blocked_method in form <filename:linenumber>.
238-- @column waiter_count Zero indexed number of threads trying to acquire the lock.
239-- @column blocking_utid Utid of thread holding the lock.
240-- @column blocking_thread_name Thread name of thread holding the lock.
241-- @column upid Upid of process experiencing lock contention.
242-- @column process_name Process name of process experiencing lock contention.
243-- @column id Slice id of lock contention.
244-- @column ts Timestamp of lock contention start.
245-- @column dur Wall clock duration of lock contention.
246-- @column monotonic_dur Monotonic clock duration of lock contention.
247-- @column track_id Thread track id of blocked thread.
248-- @column is_blocked_main_thread Whether the blocked thread is the main thread.
249-- @column is_blocking_main_thread Whether the blocking thread is the main thread.
250-- @column binder_reply_id Slice id of binder reply slice if lock contention was part of a binder txn.
251-- @column binder_reply_ts Timestamp of binder reply slice if lock contention was part of a binder txn.
252-- @column binder_reply_tid Tid of binder reply slice if lock contention was part of a binder txn.
253-- @column child_id Id of monitor contention slice blocked by this contention.
254CREATE TABLE android_monitor_contention_chain AS
255SELECT NULL AS parent_id, *, NULL AS child_id FROM _isolated
256UNION ALL
257SELECT c.*, p.child_id FROM _children c
258LEFT JOIN _parents p USING(id)
259UNION
260SELECT c.parent_id, p.* FROM _parents p
261LEFT JOIN _children c USING(id);
262
263CREATE INDEX _android_monitor_contention_chain_idx
264  ON android_monitor_contention_chain (blocking_method, blocking_utid, ts);
265
266-- First blocked node on a lock, i.e nodes with |waiter_count| = 0. The |dur| here is adjusted
267-- to only account for the time between the first thread waiting and the first thread to acquire
268-- the lock. That way, the thread state span joins below only compute the thread states where
269-- the blocking thread is actually holding the lock. This avoids counting the time when another
270-- waiter acquired the lock before the first waiter.
271CREATE PERFETTO VIEW _first_blocked_contention
272  AS
273SELECT start.id, start.blocking_utid, start.ts, MIN(end.ts + end.dur) - start.ts AS dur
274FROM android_monitor_contention_chain start
275JOIN android_monitor_contention_chain end
276  ON
277    start.blocking_utid = end.blocking_utid
278    AND start.blocking_method = end.blocking_method
279    AND end.ts BETWEEN start.ts AND start.ts + start.dur
280WHERE start.waiter_count = 0
281GROUP BY start.id;
282
283CREATE PERFETTO VIEW _blocking_thread_state
284AS
285SELECT utid AS blocking_utid, ts, dur, state, blocked_function
286FROM thread_state;
287
288-- Contains the span join of the first waiters in the |android_monitor_contention_chain| with their
289-- blocking_thread thread state.
290
291-- Note that we only span join the duration where the lock was actually held and contended.
292-- This can be less than the duration the lock was 'waited on' when a different waiter acquired the
293-- lock earlier than the first waiter.
294--
295-- @column parent_id Id of slice blocking the blocking_thread.
296-- @column blocking_method Name of the method holding the lock.
297-- @column blocked_methhod Name of the method trying to acquire the lock.
298-- @column short_blocking_method Blocking_method without arguments and return types.
299-- @column short_blocked_method Blocked_method without arguments and return types.
300-- @column blocking_src File location of blocking_method in form <filename:linenumber>.
301-- @column blocked_src File location of blocked_method in form <filename:linenumber>.
302-- @column waiter_count Zero indexed number of threads trying to acquire the lock.
303-- @column blocking_utid Utid of thread holding the lock.
304-- @column blocking_thread_name Thread name of thread holding the lock.
305-- @column upid Upid of process experiencing lock contention.
306-- @column process_name Process name of process experiencing lock contention.
307-- @column id Slice id of lock contention.
308-- @column ts Timestamp of lock contention start.
309-- @column dur Wall clock duration of lock contention.
310-- @column monotonic_dur Monotonic clock duration of lock contention.
311-- @column track_id Thread track id of blocked thread.
312-- @column is_blocked_main_thread Whether the blocked thread is the main thread.
313-- @column is_blocking_main_thread Whether the blocking thread is the main thread.
314-- @column binder_reply_id Slice id of binder reply slice if lock contention was part of a binder txn.
315-- @column binder_reply_ts Timestamp of binder reply slice if lock contention was part of a binder txn.
316-- @column binder_reply_tid Tid of binder reply slice if lock contention was part of a binder txn.
317-- @column blocking_utid Utid of the blocking |thread_state|.
318-- @column ts Timestamp of the blocking |thread_state|.
319-- @column state Thread state of the blocking thread.
320-- @column blocked_function Blocked kernel function of the blocking thread.
321CREATE VIRTUAL TABLE android_monitor_contention_chain_thread_state
322USING
323  SPAN_JOIN(_first_blocked_contention PARTITIONED blocking_utid,
324            _blocking_thread_state PARTITIONED blocking_utid);
325
326-- Aggregated thread_states on the 'blocking thread', the thread holding the lock.
327-- This builds on the data from |android_monitor_contention_chain| and
328-- for each contention slice, it returns the aggregated sum of all the thread states on the
329-- blocking thread.
330--
331-- Note that this data is only available for the first waiter on a lock.
332--
333CREATE PERFETTO VIEW android_monitor_contention_chain_thread_state_by_txn(
334  -- Slice id of the monitor contention.
335  id INT,
336  -- A |thread_state| that occurred in the blocking thread during the contention.
337  thread_state STRING,
338  -- Total time the blocking thread spent in the |thread_state| during contention.
339  thread_state_dur INT,
340  -- Count of all times the blocking thread entered |thread_state| during the contention.
341  thread_state_count INT
342) AS
343SELECT
344  id,
345  state AS thread_state,
346  SUM(dur) AS thread_state_dur,
347  COUNT(dur) AS thread_state_count
348FROM android_monitor_contention_chain_thread_state
349GROUP BY id, thread_state;
350
351-- Aggregated blocked_functions on the 'blocking thread', the thread holding the lock.
352-- This builds on the data from |android_monitor_contention_chain| and
353-- for each contention, it returns the aggregated sum of all the kernel
354-- blocked function durations on the blocking thread.
355--
356-- Note that this data is only available for the first waiter on a lock.
357CREATE PERFETTO VIEW android_monitor_contention_chain_blocked_functions_by_txn(
358  -- Slice id of the monitor contention.
359  id INT,
360  -- Blocked kernel function in a thread state in the blocking thread during the contention.
361  blocked_function STRING,
362  -- Total time the blocking thread spent in the |blocked_function| during the contention.
363  blocked_function_dur INT,
364  -- Count of all times the blocking thread executed the |blocked_function| during the contention.
365  blocked_function_count INT
366) AS
367SELECT
368  id,
369  blocked_function,
370  SUM(dur) AS blocked_function_dur,
371  COUNT(dur) AS blocked_function_count
372FROM android_monitor_contention_chain_thread_state
373WHERE blocked_function IS NOT NULL
374GROUP BY id, blocked_function;
375
376-- Returns a DAG of all Java lock contentions in a process.
377-- Each node in the graph is a <thread:Java method> pair.
378-- Each edge connects from a node waiting on a lock to a node holding a lock.
379-- The weights of each node represent the cumulative wall time the node blocked
380-- other nodes connected to it.
381CREATE PERFETTO FUNCTION android_monitor_contention_graph(
382  -- Upid of process to generate a lock graph for.
383  upid INT)
384RETURNS TABLE(
385  -- Pprof of lock graph.
386  pprof BYTES) AS
387WITH contention_chain AS (
388SELECT *,
389       IIF(blocked_thread_name GLOB 'binder:*', 'binder', blocked_thread_name)
390        AS blocked_thread_name_norm,
391       IIF(blocking_thread_name GLOB 'binder:*', 'binder', blocking_thread_name)
392        AS blocking_thread_name_norm
393FROM android_monitor_contention_chain WHERE upid = $upid
394GROUP BY id, parent_id
395), graph AS (
396SELECT
397  id,
398  dur,
399  CAT_STACKS(blocked_thread_name_norm || ':' || short_blocked_method,
400    blocking_thread_name_norm || ':' || short_blocking_method) AS stack
401FROM contention_chain
402WHERE parent_id IS NULL
403UNION ALL
404SELECT
405c.id,
406c.dur AS dur,
407  CAT_STACKS(blocked_thread_name_norm || ':' || short_blocked_method,
408             blocking_thread_name_norm || ':' || short_blocking_method, stack) AS stack
409FROM contention_chain c, graph AS p
410WHERE p.id = c.parent_id
411) SELECT EXPERIMENTAL_PROFILE(stack, 'duration', 'ns', dur) AS pprof
412  FROM graph;
413