@@ -84,16 +84,145 @@ export class AndroidLockContentionEventSource {
8484
8585 constructor ( private readonly trace : Trace ) { }
8686
87- use ( eventId : number ) : QueryResult < LockContentionDetails | null > {
87+ use (
88+ eventId : number ,
89+ trackUri : string ,
90+ ) : QueryResult < LockContentionDetails | null > {
8891 return this . dataSlot . use ( {
89- key : eventId ,
90- queryFn : async ( ) => this . fetchDetails ( eventId ) ,
92+ key : { eventId, trackUri } ,
93+ queryFn : async ( ) => this . fetchDetails ( eventId , trackUri ) ,
9194 } ) ;
9295 }
9396
94- private async fetchDetails (
97+ async fetchDetails (
9598 eventId : number ,
99+ trackUri : string ,
96100 ) : Promise < LockContentionDetails | null > {
101+ let resolvedId = eventId ;
102+ const debugMatch = trackUri . match ( / ^ d e b u g \. t r a c k ( \d + ) (?: _ \d + ) ? $ / ) ;
103+
104+ const ownerTrackPrefix = 'com.android.AndroidLockContention#OwnerEvents' ;
105+ if ( trackUri . startsWith ( ownerTrackPrefix ) ) {
106+ const query = await this . trace . engine . query ( `
107+ SELECT original_id FROM __android_lock_contention_owner_events WHERE id = ${ eventId } LIMIT 1
108+ ` ) ;
109+ if ( query . numRows ( ) > 0 ) {
110+ resolvedId = query . firstRow ( { original_id : NUM } ) . original_id ;
111+ }
112+ } else if ( debugMatch ) {
113+ const tableId = debugMatch [ 1 ] ;
114+ const tableName = `__debug_track_${ tableId } ` ;
115+ const query = await this . trace . engine . query ( `
116+ SELECT raw_original_id FROM ${ tableName } WHERE id = ${ eventId } LIMIT 1
117+ ` ) ;
118+ if ( query . numRows ( ) > 0 ) {
119+ resolvedId = query . firstRow ( { raw_original_id : NUM } ) . raw_original_id ;
120+ }
121+ }
122+
123+ const typeQuery = await this . trace . engine . query ( `
124+ SELECT name, ts, dur FROM slice WHERE id = ${ resolvedId } LIMIT 1
125+ ` ) ;
126+ if ( typeQuery . numRows ( ) === 0 ) return null ;
127+ const typeRow = typeQuery . firstRow ( { name : STR , ts : LONG , dur : LONG } ) ;
128+
129+ const monitorQuery = await this . trace . engine . query ( `
130+ SELECT 1 FROM android_monitor_contention_chain WHERE id = ${ resolvedId } LIMIT 1
131+ ` ) ;
132+ const isMonitor = monitorQuery . numRows ( ) > 0 ;
133+
134+ if ( ! isMonitor ) {
135+ const tidMatch = typeRow . name . match ( / \( o w n e r t i d : ( \d + ) \) / ) ;
136+ const ownerTid = tidMatch ? parseInt ( tidMatch [ 1 ] , 10 ) : undefined ;
137+
138+ const blockedQuery = await this . trace . engine . query ( `
139+ SELECT
140+ t.tid as blocked_tid,
141+ t.name as blocked_name,
142+ t.is_main_thread,
143+ p.upid
144+ FROM slice s
145+ JOIN thread_track tt ON s.track_id = tt.id
146+ JOIN thread t USING (utid)
147+ LEFT JOIN process p USING (upid)
148+ WHERE s.id = ${ resolvedId }
149+ LIMIT 1
150+ ` ) ;
151+
152+ let blockedThreadName = 'Unknown Thread' ;
153+ let blockedThreadTid : number | null = null ;
154+ let isBlockedThreadMain = false ;
155+ let upid : number | null = null ;
156+ if ( blockedQuery . numRows ( ) > 0 ) {
157+ const r = blockedQuery . firstRow ( {
158+ blocked_tid : NUM ,
159+ blocked_name : STR_NULL ,
160+ is_main_thread : NUM_NULL ,
161+ upid : NUM_NULL ,
162+ } ) ;
163+ blockedThreadTid = r . blocked_tid ;
164+ blockedThreadName = r . blocked_name || 'Unknown Thread' ;
165+ isBlockedThreadMain = r . is_main_thread === 1 ;
166+ upid = r . upid ;
167+ }
168+
169+ let blockingThreadName = 'Unknown Thread' ;
170+ if ( ownerTid !== undefined ) {
171+ // First try same process
172+ if ( upid !== null ) {
173+ const blockingQuery = await this . trace . engine . query ( `
174+ SELECT name FROM thread WHERE tid = ${ ownerTid } AND upid = ${ upid } LIMIT 1
175+ ` ) ;
176+ if ( blockingQuery . numRows ( ) > 0 ) {
177+ blockingThreadName =
178+ blockingQuery . firstRow ( { name : STR_NULL } ) . name || 'Unknown Thread' ;
179+ }
180+ }
181+
182+ // Fallback to any process if still unknown
183+ if ( blockingThreadName === 'Unknown Thread' ) {
184+ const fallbackQuery = await this . trace . engine . query ( `
185+ SELECT name FROM thread WHERE tid = ${ ownerTid } LIMIT 1
186+ ` ) ;
187+ if ( fallbackQuery . numRows ( ) > 0 ) {
188+ blockingThreadName =
189+ fallbackQuery . firstRow ( { name : STR_NULL } ) . name || 'Unknown Thread' ;
190+ }
191+ }
192+ }
193+
194+ let blockingTrackUri : string | undefined = undefined ;
195+ if ( ownerTid !== undefined ) {
196+ blockingTrackUri = 'com.android.AndroidLockContention#OwnerEvents' ;
197+ }
198+
199+ return {
200+ id : resolvedId ,
201+ ts : Time . fromRaw ( typeRow . ts ) ,
202+ dur : typeRow . dur !== null ? Duration . fromRaw ( typeRow . dur ) : null ,
203+ monotonicDur : null ,
204+ waiterCount : 0 ,
205+ lockName : typeRow . name ,
206+ blockedThreadName,
207+ blockedThreadTid,
208+ isBlockedThreadMain,
209+ blockedMethod : '' ,
210+ blockedSrc : '' ,
211+ blockingThreadName,
212+ blockingThreadTid : ownerTid ?? null ,
213+ isBlockingThreadMain : false , // We don't easily know if owner is main thread without another query or join, but it's fine for now.
214+ blockingMethod : '' ,
215+ blockingSrc : '' ,
216+ parentId : null ,
217+ blockingTrackUri,
218+ binderReplyId : null ,
219+ blockingBinderTxnId : null ,
220+ waiters : [ ] ,
221+ threadStates : [ ] ,
222+ blockedFunctions : [ ] ,
223+ } ;
224+ }
225+
97226 const mainQuery = await this . trace . engine . query ( `
98227 SELECT
99228 id,
@@ -120,7 +249,7 @@ export class AndroidLockContentionEventSource {
120249 (SELECT id FROM thread_track WHERE utid = blocking_utid) as blocking_track_id,
121250 binder_reply_id
122251 FROM android_monitor_contention_chain
123- WHERE id = ${ eventId }
252+ WHERE id = ${ resolvedId }
124253 ` ) ;
125254
126255 if ( mainQuery . numRows ( ) === 0 ) return null ;
0 commit comments