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 {assertExists} from '../base/logging'; 16import {DeferredAction} from '../common/actions'; 17import {AggregateData} from '../common/aggregation_data'; 18import {CurrentSearchResults, SearchSummary} from '../common/search_data'; 19import {CallsiteInfo, createEmptyState, State} from '../common/state'; 20 21import {FrontendLocalState} from './frontend_local_state'; 22import {RafScheduler} from './raf_scheduler'; 23import {ServiceWorkerController} from './service_worker_controller'; 24 25type Dispatch = (action: DeferredAction) => void; 26type TrackDataStore = Map<string, {}>; 27type QueryResultsStore = Map<string, {}>; 28type AggregateDataStore = Map<string, AggregateData>; 29type Description = Map<string, string>; 30export type Arg = string|{kind: 'SLICE', trackId: string, sliceId: number}; 31export type Args = Map<string, Arg>; 32export interface SliceDetails { 33 ts?: number; 34 dur?: number; 35 priority?: number; 36 endState?: string; 37 cpu?: number; 38 id?: number; 39 utid?: number; 40 wakeupTs?: number; 41 wakerUtid?: number; 42 wakerCpu?: number; 43 category?: string; 44 name?: string; 45 args?: Args; 46 description?: Description; 47} 48 49export interface CounterDetails { 50 startTime?: number; 51 value?: number; 52 delta?: number; 53 duration?: number; 54} 55 56export interface HeapProfileDetails { 57 type?: string; 58 id?: number; 59 ts?: number; 60 tsNs?: number; 61 pid?: number; 62 upid?: number; 63 flamegraph?: CallsiteInfo[]; 64 expandedCallsite?: CallsiteInfo; 65 viewingOption?: string; 66 expandedId?: number; 67} 68 69export interface CpuProfileDetails { 70 id?: number; 71 ts?: number; 72 utid?: number; 73 stack?: CallsiteInfo[]; 74} 75 76export interface QuantizedLoad { 77 startSec: number; 78 endSec: number; 79 load: number; 80} 81type OverviewStore = Map<string, QuantizedLoad[]>; 82 83export interface ThreadDesc { 84 utid: number; 85 tid: number; 86 threadName: string; 87 pid?: number; 88 procName?: string; 89} 90type ThreadMap = Map<number, ThreadDesc>; 91 92/** 93 * Global accessors for state/dispatch in the frontend. 94 */ 95class Globals { 96 private _dispatch?: Dispatch = undefined; 97 private _controllerWorker?: Worker = undefined; 98 private _state?: State = undefined; 99 private _frontendLocalState?: FrontendLocalState = undefined; 100 private _rafScheduler?: RafScheduler = undefined; 101 private _serviceWorkerController?: ServiceWorkerController = undefined; 102 103 // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. 104 private _trackDataStore?: TrackDataStore = undefined; 105 private _queryResults?: QueryResultsStore = undefined; 106 private _overviewStore?: OverviewStore = undefined; 107 private _aggregateDataStore?: AggregateDataStore = undefined; 108 private _threadMap?: ThreadMap = undefined; 109 private _sliceDetails?: SliceDetails = undefined; 110 private _counterDetails?: CounterDetails = undefined; 111 private _heapProfileDetails?: HeapProfileDetails = undefined; 112 private _cpuProfileDetails?: CpuProfileDetails = undefined; 113 private _numQueriesQueued = 0; 114 private _bufferUsage?: number = undefined; 115 private _recordingLog?: string = undefined; 116 117 private _currentSearchResults: CurrentSearchResults = { 118 sliceIds: new Float64Array(0), 119 tsStarts: new Float64Array(0), 120 utids: new Float64Array(0), 121 trackIds: [], 122 sources: [], 123 totalResults: 0, 124 }; 125 searchSummary: SearchSummary = { 126 tsStarts: new Float64Array(0), 127 tsEnds: new Float64Array(0), 128 count: new Uint8Array(0), 129 }; 130 131 // This variable is set by the is_internal_user.js script if the user is a 132 // googler. This is used to avoid exposing features that are not ready yet 133 // for public consumption. The gated features themselves are not secret. 134 isInternalUser = false; 135 136 initialize(dispatch: Dispatch, controllerWorker: Worker) { 137 this._dispatch = dispatch; 138 this._controllerWorker = controllerWorker; 139 this._state = createEmptyState(); 140 this._frontendLocalState = new FrontendLocalState(); 141 this._rafScheduler = new RafScheduler(); 142 this._serviceWorkerController = new ServiceWorkerController(); 143 144 // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. 145 this._trackDataStore = new Map<string, {}>(); 146 this._queryResults = new Map<string, {}>(); 147 this._overviewStore = new Map<string, QuantizedLoad[]>(); 148 this._aggregateDataStore = new Map<string, AggregateData>(); 149 this._threadMap = new Map<number, ThreadDesc>(); 150 this._sliceDetails = {}; 151 this._counterDetails = {}; 152 this._heapProfileDetails = {}; 153 this._cpuProfileDetails = {}; 154 } 155 156 get state(): State { 157 return assertExists(this._state); 158 } 159 160 set state(state: State) { 161 this._state = assertExists(state); 162 } 163 164 get dispatch(): Dispatch { 165 return assertExists(this._dispatch); 166 } 167 168 get frontendLocalState() { 169 return assertExists(this._frontendLocalState); 170 } 171 172 get rafScheduler() { 173 return assertExists(this._rafScheduler); 174 } 175 176 get serviceWorkerController() { 177 return assertExists(this._serviceWorkerController); 178 } 179 180 // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. 181 get overviewStore(): OverviewStore { 182 return assertExists(this._overviewStore); 183 } 184 185 get trackDataStore(): TrackDataStore { 186 return assertExists(this._trackDataStore); 187 } 188 189 get queryResults(): QueryResultsStore { 190 return assertExists(this._queryResults); 191 } 192 193 get threads() { 194 return assertExists(this._threadMap); 195 } 196 197 get sliceDetails() { 198 return assertExists(this._sliceDetails); 199 } 200 201 set sliceDetails(click: SliceDetails) { 202 this._sliceDetails = assertExists(click); 203 } 204 205 get counterDetails() { 206 return assertExists(this._counterDetails); 207 } 208 209 set counterDetails(click: CounterDetails) { 210 this._counterDetails = assertExists(click); 211 } 212 213 get aggregateDataStore(): AggregateDataStore { 214 return assertExists(this._aggregateDataStore); 215 } 216 217 get heapProfileDetails() { 218 return assertExists(this._heapProfileDetails); 219 } 220 221 set heapProfileDetails(click: HeapProfileDetails) { 222 this._heapProfileDetails = assertExists(click); 223 } 224 225 get cpuProfileDetails() { 226 return assertExists(this._cpuProfileDetails); 227 } 228 229 set cpuProfileDetails(click: CpuProfileDetails) { 230 this._cpuProfileDetails = assertExists(click); 231 } 232 233 set numQueuedQueries(value: number) { 234 this._numQueriesQueued = value; 235 } 236 237 get numQueuedQueries() { 238 return this._numQueriesQueued; 239 } 240 241 get bufferUsage() { 242 return this._bufferUsage; 243 } 244 245 get recordingLog() { 246 return this._recordingLog; 247 } 248 249 get currentSearchResults() { 250 return this._currentSearchResults; 251 } 252 253 set currentSearchResults(results: CurrentSearchResults) { 254 this._currentSearchResults = results; 255 } 256 257 setBufferUsage(bufferUsage: number) { 258 this._bufferUsage = bufferUsage; 259 } 260 261 setTrackData(id: string, data: {}) { 262 this.trackDataStore.set(id, data); 263 } 264 265 setRecordingLog(recordingLog: string) { 266 this._recordingLog = recordingLog; 267 } 268 269 setAggregateData(kind: string, data: AggregateData) { 270 this.aggregateDataStore.set(kind, data); 271 } 272 273 getCurResolution() { 274 // Truncate the resolution to the closest power of 2. 275 // This effectively means the resolution changes every 6 zoom levels. 276 const resolution = this.frontendLocalState.timeScale.deltaPxToDuration(1); 277 return Math.pow(2, Math.floor(Math.log2(resolution))); 278 } 279 280 makeSelection(action: DeferredAction<{}>) { 281 // A new selection should cancel the current search selection. 282 globals.frontendLocalState.searchIndex = -1; 283 globals.frontendLocalState.currentTab = 284 action.type === 'deselect' ? undefined : 'current_selection'; 285 globals.dispatch(action); 286 } 287 288 resetForTesting() { 289 this._dispatch = undefined; 290 this._state = undefined; 291 this._frontendLocalState = undefined; 292 this._rafScheduler = undefined; 293 this._serviceWorkerController = undefined; 294 295 // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. 296 this._trackDataStore = undefined; 297 this._queryResults = undefined; 298 this._overviewStore = undefined; 299 this._threadMap = undefined; 300 this._sliceDetails = undefined; 301 this._aggregateDataStore = undefined; 302 this._numQueriesQueued = 0; 303 this._currentSearchResults = { 304 sliceIds: new Float64Array(0), 305 tsStarts: new Float64Array(0), 306 utids: new Float64Array(0), 307 trackIds: [], 308 sources: [], 309 totalResults: 0, 310 }; 311 } 312 313 // Used when switching to the legacy TraceViewer UI. 314 // Most resources are cleaned up by replacing the current |window| object, 315 // however pending RAFs and workers seem to outlive the |window| and need to 316 // be cleaned up explicitly. 317 shutdown() { 318 this._controllerWorker!.terminate(); 319 this._rafScheduler!.shutdown(); 320 } 321} 322 323export const globals = new Globals(); 324