1// Copyright (C) 2019 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 {Engine} from '../common/engine'; 16import {fromNs} from '../common/time'; 17import {SliceDetails} from '../frontend/globals'; 18 19import {Controller} from './controller'; 20import {globals} from './globals'; 21 22export interface SelectionControllerArgs { 23 engine: Engine; 24} 25 26// This class queries the TP for the details on a specific slice that has 27// been clicked. 28export class SelectionController extends Controller<'main'> { 29 private lastSelectedSlice?: number; 30 constructor(private args: SelectionControllerArgs) { 31 super('main'); 32 } 33 34 run() { 35 const selection = globals.state.currentSelection; 36 if (selection === null || 37 selection.kind !== 'SLICE' || 38 selection.id === this.lastSelectedSlice) { 39 return; 40 } 41 const selectedSlice = selection.id; 42 this.lastSelectedSlice = selectedSlice; 43 44 if (selectedSlice !== undefined) { 45 const sqlQuery = `SELECT ts, dur, priority, end_state, utid FROM sched 46 WHERE row_id = ${selectedSlice}`; 47 this.args.engine.query(sqlQuery).then(result => { 48 // Check selection is still the same on completion of query. 49 const selection = globals.state.currentSelection; 50 if (result.numRecords === 1 && 51 selection && 52 selection.kind === 'SLICE' && 53 selection.id === selectedSlice) { 54 const ts = result.columns[0].longValues![0] as number; 55 const timeFromStart = fromNs(ts) - globals.state.traceTime.startSec; 56 const dur = fromNs(result.columns[1].longValues![0] as number); 57 const priority = result.columns[2].longValues![0] as number; 58 const endState = result.columns[3].stringValues![0]; 59 const selected: 60 SliceDetails = {ts: timeFromStart, dur, priority, endState}; 61 const utid = result.columns[4].longValues![0]; 62 this.schedulingDetails(ts, utid).then(wakeResult => { 63 Object.assign(selected, wakeResult); 64 globals.publish('SliceDetails', selected); 65 }); 66 } 67 }); 68 } 69 } 70 71 async schedulingDetails(ts: number, utid: number|Long) { 72 // Find the ts of the first sched_wakeup before the current slice. 73 const queryWakeupTs = `select ts from instants where name = 'sched_wakeup' 74 and ref = ${utid} and ts < ${ts} order by ts desc limit 1`; 75 const wakeupRow = await this.args.engine.queryOneRow(queryWakeupTs); 76 // Find the previous sched slice for the current utid. 77 const queryPrevSched = `select ts from sched where utid = ${utid} 78 and ts < ${ts} order by ts desc limit 1`; 79 const prevSchedRow = await this.args.engine.queryOneRow(queryPrevSched); 80 // If this is the first sched slice for this utid or if the wakeup found 81 // was after the previous slice then we know the wakeup was for this slice. 82 if (prevSchedRow[0] && wakeupRow[0] < prevSchedRow[0]) { 83 return undefined; 84 } 85 const wakeupTs = wakeupRow[0]; 86 // Find the sched slice with the utid of the waker running when the 87 // sched wakeup occurred. This is the waker. 88 const queryWaker = `select utid, cpu from sched where utid = 89 (select utid from raw where name = 'sched_wakeup' and ts = ${wakeupTs}) 90 and ts < ${wakeupTs} and ts + dur >= ${wakeupTs};`; 91 const wakerRow = await this.args.engine.queryOneRow(queryWaker); 92 if (wakerRow) { 93 return { 94 wakeupTs: fromNs(wakeupTs), 95 wakerUtid: wakerRow[0], 96 wakerCpu: wakerRow[1] 97 }; 98 } else { 99 return undefined; 100 } 101 } 102} 103