1// Copyright (C) 2018 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 {slowlyCountRows} from '../../common/query_iterator'; 16import {fromNs, toNs} from '../../common/time'; 17import {LIMIT} from '../../common/track_data'; 18import { 19 TrackController, 20 trackControllerRegistry 21} from '../../controller/track_controller'; 22 23import { 24 Config, 25 Data, 26 PROCESS_SUMMARY_TRACK, 27} from './common'; 28 29// This is the summary displayed when a process only contains chrome slices 30// and no cpu scheduling. 31 32class ProcessSummaryTrackController extends TrackController<Config, Data> { 33 static readonly kind = PROCESS_SUMMARY_TRACK; 34 private setup = false; 35 36 async onBoundsChange(start: number, end: number, resolution: number): 37 Promise<Data> { 38 const startNs = toNs(start); 39 const endNs = toNs(end); 40 41 if (this.setup === false) { 42 await this.query( 43 `create virtual table ${this.tableName('window')} using window;`); 44 45 let utids = [this.config.utid]; 46 if (this.config.upid) { 47 const threadQuery = await this.query( 48 `select utid from thread where upid=${this.config.upid}`); 49 utids = threadQuery.columns[0].longValues!; 50 } 51 52 const trackQuery = await this.query( 53 `select id from thread_track where utid in (${utids.join(',')})`); 54 const tracks = trackQuery.columns[0].longValues!; 55 56 const processSliceView = this.tableName('process_slice_view'); 57 await this.query( 58 `create view ${processSliceView} as ` + 59 // 0 as cpu is a dummy column to perform span join on. 60 `select ts, dur/${utids.length} as dur ` + 61 `from slice s ` + 62 `where depth = 0 and track_id in ` + 63 `(${tracks.join(',')})`); 64 await this.query(`create virtual table ${this.tableName('span')} 65 using span_join(${processSliceView}, 66 ${this.tableName('window')});`); 67 this.setup = true; 68 } 69 70 // |resolution| is in s/px we want # ns for 10px window: 71 // Max value with 1 so we don't end up with resolution 0. 72 const bucketSizeNs = Math.max(1, Math.round(resolution * 10 * 1e9)); 73 const windowStartNs = Math.floor(startNs / bucketSizeNs) * bucketSizeNs; 74 const windowDurNs = Math.max(1, endNs - windowStartNs); 75 76 this.query(`update ${this.tableName('window')} set 77 window_start=${windowStartNs}, 78 window_dur=${windowDurNs}, 79 quantum=${bucketSizeNs} 80 where rowid = 0;`); 81 82 return this.computeSummary( 83 fromNs(windowStartNs), end, resolution, bucketSizeNs); 84 } 85 86 private async computeSummary( 87 start: number, end: number, resolution: number, 88 bucketSizeNs: number): Promise<Data> { 89 const startNs = toNs(start); 90 const endNs = toNs(end); 91 const numBuckets = 92 Math.min(Math.ceil((endNs - startNs) / bucketSizeNs), LIMIT); 93 94 const query = `select 95 quantum_ts as bucket, 96 sum(dur)/cast(${bucketSizeNs} as float) as utilization 97 from ${this.tableName('span')} 98 group by quantum_ts 99 limit ${LIMIT}`; 100 101 const rawResult = await this.query(query); 102 const numRows = slowlyCountRows(rawResult); 103 104 const summary: Data = { 105 start, 106 end, 107 resolution, 108 length: numBuckets, 109 bucketSizeSeconds: fromNs(bucketSizeNs), 110 utilizations: new Float64Array(numBuckets), 111 }; 112 const cols = rawResult.columns; 113 for (let row = 0; row < numRows; row++) { 114 const bucket = +cols[0].longValues![row]; 115 if (bucket > numBuckets) { 116 continue; 117 } 118 summary.utilizations[bucket] = +cols[1].doubleValues![row]; 119 } 120 return summary; 121 } 122 123 onDestroy(): void { 124 if (this.setup) { 125 this.query(`drop table ${this.tableName('window')}`); 126 this.query(`drop table ${this.tableName('span')}`); 127 this.setup = false; 128 } 129 } 130} 131 132trackControllerRegistry.register(ProcessSummaryTrackController); 133