// Copyright (C) 2019 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this 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 {Engine} from '../common/engine'; import {fromNs} from '../common/time'; import {SliceDetails} from '../frontend/globals'; import {Controller} from './controller'; import {globals} from './globals'; export interface SelectionControllerArgs { engine: Engine; } // This class queries the TP for the details on a specific slice that has // been clicked. export class SelectionController extends Controller<'main'> { private lastSelectedSlice?: number; constructor(private args: SelectionControllerArgs) { super('main'); } run() { const selection = globals.state.currentSelection; if (selection === null || selection.kind !== 'SLICE' || selection.id === this.lastSelectedSlice) { return; } const selectedSlice = selection.id; this.lastSelectedSlice = selectedSlice; if (selectedSlice !== undefined) { const sqlQuery = `SELECT ts, dur, priority, end_state, utid FROM sched WHERE row_id = ${selectedSlice}`; this.args.engine.query(sqlQuery).then(result => { // Check selection is still the same on completion of query. const selection = globals.state.currentSelection; if (result.numRecords === 1 && selection && selection.kind === 'SLICE' && selection.id === selectedSlice) { const ts = result.columns[0].longValues![0] as number; const timeFromStart = fromNs(ts) - globals.state.traceTime.startSec; const dur = fromNs(result.columns[1].longValues![0] as number); const priority = result.columns[2].longValues![0] as number; const endState = result.columns[3].stringValues![0]; const selected: SliceDetails = {ts: timeFromStart, dur, priority, endState}; const utid = result.columns[4].longValues![0]; this.schedulingDetails(ts, utid).then(wakeResult => { Object.assign(selected, wakeResult); globals.publish('SliceDetails', selected); }); } }); } } async schedulingDetails(ts: number, utid: number|Long) { // Find the ts of the first sched_wakeup before the current slice. const queryWakeupTs = `select ts from instants where name = 'sched_wakeup' and ref = ${utid} and ts < ${ts} order by ts desc limit 1`; const wakeupRow = await this.args.engine.queryOneRow(queryWakeupTs); // Find the previous sched slice for the current utid. const queryPrevSched = `select ts from sched where utid = ${utid} and ts < ${ts} order by ts desc limit 1`; const prevSchedRow = await this.args.engine.queryOneRow(queryPrevSched); // If this is the first sched slice for this utid or if the wakeup found // was after the previous slice then we know the wakeup was for this slice. if (prevSchedRow[0] && wakeupRow[0] < prevSchedRow[0]) { return undefined; } const wakeupTs = wakeupRow[0]; // Find the sched slice with the utid of the waker running when the // sched wakeup occurred. This is the waker. const queryWaker = `select utid, cpu from sched where utid = (select utid from raw where name = 'sched_wakeup' and ts = ${wakeupTs}) and ts < ${wakeupTs} and ts + dur >= ${wakeupTs};`; const wakerRow = await this.args.engine.queryOneRow(queryWaker); if (wakerRow) { return { wakeupTs: fromNs(wakeupTs), wakerUtid: wakerRow[0], wakerCpu: wakerRow[1] }; } else { return undefined; } } }