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