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 15 import {assertExists} from '../base/logging'; 16 import {Actions, DeferredAction} from '../common/actions'; 17 import {createEmptyState, State} from '../common/state'; 18 19 import {FrontendLocalState} from './frontend_local_state'; 20 import {RafScheduler} from './raf_scheduler'; 21 22 type Dispatch = (action: DeferredAction) => void; 23 type TrackDataStore = Map<string, {}>; 24 type QueryResultsStore = Map<string, {}>; 25 export interface SliceDetails { 26 ts?: number; 27 dur?: number; 28 priority?: number; 29 endState?: string; 30 wakeupTs?: number; 31 wakerUtid?: number; 32 wakerCpu?: number; 33 } 34 35 export interface QuantizedLoad { 36 startSec: number; 37 endSec: number; 38 load: number; 39 } 40 type OverviewStore = Map<string, QuantizedLoad[]>; 41 42 export interface ThreadDesc { 43 utid: number; 44 tid: number; 45 threadName: string; 46 pid?: number; 47 procName?: string; 48 } 49 type ThreadMap = Map<number, ThreadDesc>; 50 51 /** 52 * Global accessors for state/dispatch in the frontend. 53 */ 54 class Globals { 55 private _dispatch?: Dispatch = undefined; 56 private _controllerWorker?: Worker = undefined; 57 private _state?: State = undefined; 58 private _frontendLocalState?: FrontendLocalState = undefined; 59 private _rafScheduler?: RafScheduler = undefined; 60 61 // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. 62 private _trackDataStore?: TrackDataStore = undefined; 63 private _queryResults?: QueryResultsStore = undefined; 64 private _overviewStore?: OverviewStore = undefined; 65 private _threadMap?: ThreadMap = undefined; 66 private _sliceDetails?: SliceDetails = undefined; 67 private _pendingTrackRequests?: Set<string> = undefined; 68 69 initialize(dispatch: Dispatch, controllerWorker: Worker) { 70 this._dispatch = dispatch; 71 this._controllerWorker = controllerWorker; 72 this._state = createEmptyState(); 73 this._frontendLocalState = new FrontendLocalState(); 74 this._rafScheduler = new RafScheduler(); 75 76 // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. 77 this._trackDataStore = new Map<string, {}>(); 78 this._queryResults = new Map<string, {}>(); 79 this._overviewStore = new Map<string, QuantizedLoad[]>(); 80 this._threadMap = new Map<number, ThreadDesc>(); 81 this._sliceDetails = {}; 82 this._pendingTrackRequests = new Set<string>(); 83 } 84 85 get state(): State { 86 return assertExists(this._state); 87 } 88 89 set state(state: State) { 90 this._state = assertExists(state); 91 } 92 93 get dispatch(): Dispatch { 94 return assertExists(this._dispatch); 95 } 96 97 get frontendLocalState() { 98 return assertExists(this._frontendLocalState); 99 } 100 101 get rafScheduler() { 102 return assertExists(this._rafScheduler); 103 } 104 105 // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. 106 get overviewStore(): OverviewStore { 107 return assertExists(this._overviewStore); 108 } 109 110 get trackDataStore(): TrackDataStore { 111 return assertExists(this._trackDataStore); 112 } 113 114 get queryResults(): QueryResultsStore { 115 return assertExists(this._queryResults); 116 } 117 118 get threads() { 119 return assertExists(this._threadMap); 120 } 121 122 get sliceDetails() { 123 return assertExists(this._sliceDetails); 124 } 125 126 set sliceDetails(click: SliceDetails) { 127 this._sliceDetails = assertExists(click); 128 } 129 130 setTrackData(id: string, data: {}) { 131 this.trackDataStore.set(id, data); 132 assertExists(this._pendingTrackRequests).delete(id); 133 } 134 135 getCurResolution() { 136 // Truncate the resolution to the closest power of 10. 137 const resolution = this.frontendLocalState.timeScale.deltaPxToDuration(1); 138 return Math.pow(10, Math.floor(Math.log10(resolution))); 139 } 140 141 requestTrackData(trackId: string) { 142 const pending = assertExists(this._pendingTrackRequests); 143 if (pending.has(trackId)) return; 144 145 const {visibleWindowTime} = globals.frontendLocalState; 146 const resolution = this.getCurResolution(); 147 const start = visibleWindowTime.start - visibleWindowTime.duration; 148 const end = visibleWindowTime.end + visibleWindowTime.duration; 149 150 pending.add(trackId); 151 globals.dispatch(Actions.reqTrackData({ 152 trackId, 153 start, 154 end, 155 resolution, 156 })); 157 } 158 159 resetForTesting() { 160 this._dispatch = undefined; 161 this._state = undefined; 162 this._frontendLocalState = undefined; 163 this._rafScheduler = undefined; 164 165 // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. 166 this._trackDataStore = undefined; 167 this._queryResults = undefined; 168 this._overviewStore = undefined; 169 this._threadMap = undefined; 170 this._sliceDetails = undefined; 171 this._pendingTrackRequests = undefined; 172 } 173 174 // Used when switching to the legacy TraceViewer UI. 175 // Most resources are cleaned up by replacing the current |window| object, 176 // however pending RAFs and workers seem to outlive the |window| and need to 177 // be cleaned up explicitly. 178 shutdown() { 179 this._controllerWorker!.terminate(); 180 this._rafScheduler!.shutdown(); 181 } 182 } 183 184 export const globals = new Globals(); 185