1// Copyright (C) 2021 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {THREAD_STATE_TRACK_KIND} from '../../public/track_kinds'; 16import {Trace} from '../../public/trace'; 17import {PerfettoPlugin} from '../../public/plugin'; 18import {getThreadUriPrefix, getTrackName} from '../../public/utils'; 19import {NUM, NUM_NULL, STR_NULL} from '../../trace_processor/query_result'; 20import {ThreadStateTrack} from './thread_state_track'; 21import {removeFalsyValues} from '../../base/array_utils'; 22import {TrackNode} from '../../public/workspace'; 23import {ThreadStateSelectionAggregator} from './thread_state_selection_aggregator'; 24import ProcessThreadGroupsPlugin from '../dev.perfetto.ProcessThreadGroups'; 25 26function uriForThreadStateTrack(upid: number | null, utid: number): string { 27 return `${getThreadUriPrefix(upid, utid)}_state`; 28} 29 30export default class implements PerfettoPlugin { 31 static readonly id = 'dev.perfetto.ThreadState'; 32 static readonly dependencies = [ProcessThreadGroupsPlugin]; 33 34 async onTraceLoad(ctx: Trace): Promise<void> { 35 const {engine} = ctx; 36 37 ctx.selection.registerAreaSelectionAggregator( 38 new ThreadStateSelectionAggregator(), 39 ); 40 41 const result = await engine.query(` 42 include perfetto module viz.threads; 43 include perfetto module viz.summary.threads; 44 45 select 46 utid, 47 t.upid, 48 tid, 49 t.name as threadName, 50 is_main_thread as isMainThread, 51 is_kernel_thread as isKernelThread 52 from _threads_with_kernel_flag t 53 join _sched_summary using (utid) 54 `); 55 56 const it = result.iter({ 57 utid: NUM, 58 upid: NUM_NULL, 59 tid: NUM_NULL, 60 threadName: STR_NULL, 61 isMainThread: NUM_NULL, 62 isKernelThread: NUM, 63 }); 64 for (; it.valid(); it.next()) { 65 const {utid, upid, tid, threadName, isMainThread, isKernelThread} = it; 66 const title = getTrackName({ 67 utid, 68 tid, 69 threadName, 70 kind: THREAD_STATE_TRACK_KIND, 71 }); 72 73 const uri = uriForThreadStateTrack(upid, utid); 74 ctx.tracks.registerTrack({ 75 uri, 76 title, 77 tags: { 78 kind: THREAD_STATE_TRACK_KIND, 79 utid, 80 upid: upid ?? undefined, 81 ...(isKernelThread === 1 && {kernelThread: true}), 82 }, 83 chips: removeFalsyValues([ 84 isKernelThread === 0 && isMainThread === 1 && 'main thread', 85 ]), 86 track: new ThreadStateTrack(ctx, uri, utid), 87 }); 88 89 const group = ctx.plugins 90 .getPlugin(ProcessThreadGroupsPlugin) 91 .getGroupForThread(utid); 92 const track = new TrackNode({uri, title, sortOrder: 10}); 93 group?.addChildInOrder(track); 94 } 95 96 ctx.selection.registerSqlSelectionResolver({ 97 sqlTableName: 'thread_state', 98 callback: async (id: number) => { 99 const result = await ctx.engine.query(` 100 select 101 thread_state.utid, 102 thread.upid 103 from 104 thread_state 105 join thread on thread_state.utid = thread.id 106 where thread_state.id = ${id} 107 `); 108 109 const {upid, utid} = result.firstRow({ 110 upid: NUM_NULL, 111 utid: NUM, 112 }); 113 114 return { 115 eventId: id, 116 trackUri: uriForThreadStateTrack(upid, utid), 117 }; 118 }, 119 }); 120 } 121} 122