| // Copyright (C) 2024 The Android Open Source Project | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use size file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| import {assertTrue} from '../../base/assert'; | |
| import {type duration, Time, type time} from '../../base/time'; | |
| import type {Engine} from '../../trace_processor/engine'; | |
| import { | |
| LONG, | |
| NUM, | |
| NUM_NULL, | |
| STR_NULL, | |
| } from '../../trace_processor/query_result'; | |
| import { | |
| constraintsToQuerySuffix, | |
| type SQLConstraints, | |
| } from '../../trace_processor/sql_utils'; | |
| import { | |
| asSchedSqlId, | |
| asThreadStateSqlId, | |
| asUtid, | |
| type SchedSqlId, | |
| type ThreadStateSqlId, | |
| type Utid, | |
| } from './core_types'; | |
| import {getThreadInfo, type ThreadInfo} from './thread'; | |
| import {getThreadState, getThreadStateFromConstraints} from './thread_state'; | |
| // Representation of a single thread state object, corresponding to | |
| // a row for the |thread_slice| table. | |
| export interface Sched { | |
| // Id into |sched| table. | |
| id: SchedSqlId; | |
| // Id of the corresponding entry in the |sched| table. | |
| threadStateId?: ThreadStateSqlId; | |
| // Timestamp of the beginning of this thread state in nanoseconds. | |
| ts: time; | |
| // Duration of this thread state in nanoseconds. | |
| dur: duration; | |
| cpu: number; | |
| priority: number; | |
| endState?: string; | |
| thread: ThreadInfo; | |
| } | |
| export interface SchedWakeupInfo { | |
| wakeupTs?: time; | |
| wakerUtid?: Utid; | |
| wakerCpu?: number; | |
| } | |
| // Gets a list of sched objects from Trace Processor with given | |
| // constraints. | |
| export async function getSchedFromConstraints( | |
| engine: Engine, | |
| constraints: SQLConstraints, | |
| ): Promise<Sched[]> { | |
| const query = await engine.query(` | |
| SELECT | |
| sched.id as schedSqlId, | |
| ( | |
| SELECT id | |
| FROM thread_state | |
| WHERE | |
| thread_state.ts = sched.ts | |
| AND thread_state.utid = sched.utid | |
| ) as threadStateSqlId, | |
| sched.ts, | |
| sched.dur, | |
| sched.cpu, | |
| sched.priority as priority, | |
| sched.end_state as endState, | |
| sched.utid | |
| FROM sched | |
| ${constraintsToQuerySuffix(constraints)}`); | |
| const it = query.iter({ | |
| schedSqlId: NUM, | |
| threadStateSqlId: NUM_NULL, | |
| ts: LONG, | |
| dur: LONG, | |
| cpu: NUM, | |
| priority: NUM, | |
| endState: STR_NULL, | |
| utid: NUM, | |
| }); | |
| const result: Sched[] = []; | |
| for (; it.valid(); it.next()) { | |
| result.push({ | |
| id: asSchedSqlId(it.schedSqlId), | |
| threadStateId: asThreadStateSqlId(it.threadStateSqlId ?? undefined), | |
| ts: Time.fromRaw(it.ts), | |
| dur: it.dur, | |
| priority: it.priority, | |
| endState: it.endState ?? undefined, | |
| cpu: it.cpu ?? undefined, | |
| thread: await getThreadInfo(engine, asUtid(it.utid)), | |
| }); | |
| } | |
| return result; | |
| } | |
| export async function getSched( | |
| engine: Engine, | |
| id: SchedSqlId, | |
| ): Promise<Sched | undefined> { | |
| const result = await getSchedFromConstraints(engine, { | |
| filters: [`sched.id=${id}`], | |
| }); | |
| assertTrue(result.length <= 1); | |
| if (result.length === 0) { | |
| return undefined; | |
| } | |
| return result[0]; | |
| } | |
| // Returns the thread and time of the wakeup that resulted in this running | |
| // sched slice. Omits wakeups that are known to be from interrupt context, | |
| // since we cannot always recover the correct waker cpu with the current | |
| // table layout. | |
| export async function getSchedWakeupInfo( | |
| engine: Engine, | |
| sched: Sched, | |
| ): Promise<SchedWakeupInfo | undefined> { | |
| const prevRunnable = await getThreadStateFromConstraints(engine, { | |
| filters: [ | |
| 'state = "R"', | |
| `ts + dur = ${sched.ts}`, | |
| `utid = ${sched.thread.utid}`, | |
| `(irq_context is null or irq_context = 0)`, | |
| ], | |
| }); | |
| if (prevRunnable.length === 0 || prevRunnable[0].wakerId === undefined) { | |
| return undefined; | |
| } | |
| const waker = await getThreadState(engine, prevRunnable[0].wakerId); | |
| if (waker === undefined) { | |
| return undefined; | |
| } | |
| return { | |
| wakerCpu: waker?.cpu, | |
| wakerUtid: prevRunnable[0].wakerUtid, | |
| wakeupTs: prevRunnable[0].ts, | |
| }; | |
| } |