// Copyright (C) 2018 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 {assertExists} from '../base/logging'; import {Actions, DeferredAction} from '../common/actions'; import {createEmptyState, State} from '../common/state'; import {FrontendLocalState} from './frontend_local_state'; import {RafScheduler} from './raf_scheduler'; type Dispatch = (action: DeferredAction) => void; type TrackDataStore = Map; type QueryResultsStore = Map; export interface SliceDetails { ts?: number; dur?: number; priority?: number; endState?: string; wakeupTs?: number; wakerUtid?: number; wakerCpu?: number; } export interface QuantizedLoad { startSec: number; endSec: number; load: number; } type OverviewStore = Map; export interface ThreadDesc { utid: number; tid: number; threadName: string; pid?: number; procName?: string; } type ThreadMap = Map; /** * Global accessors for state/dispatch in the frontend. */ class Globals { private _dispatch?: Dispatch = undefined; private _controllerWorker?: Worker = undefined; private _state?: State = undefined; private _frontendLocalState?: FrontendLocalState = undefined; private _rafScheduler?: RafScheduler = undefined; // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. private _trackDataStore?: TrackDataStore = undefined; private _queryResults?: QueryResultsStore = undefined; private _overviewStore?: OverviewStore = undefined; private _threadMap?: ThreadMap = undefined; private _sliceDetails?: SliceDetails = undefined; private _pendingTrackRequests?: Set = undefined; initialize(dispatch: Dispatch, controllerWorker: Worker) { this._dispatch = dispatch; this._controllerWorker = controllerWorker; this._state = createEmptyState(); this._frontendLocalState = new FrontendLocalState(); this._rafScheduler = new RafScheduler(); // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. this._trackDataStore = new Map(); this._queryResults = new Map(); this._overviewStore = new Map(); this._threadMap = new Map(); this._sliceDetails = {}; this._pendingTrackRequests = new Set(); } get state(): State { return assertExists(this._state); } set state(state: State) { this._state = assertExists(state); } get dispatch(): Dispatch { return assertExists(this._dispatch); } get frontendLocalState() { return assertExists(this._frontendLocalState); } get rafScheduler() { return assertExists(this._rafScheduler); } // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. get overviewStore(): OverviewStore { return assertExists(this._overviewStore); } get trackDataStore(): TrackDataStore { return assertExists(this._trackDataStore); } get queryResults(): QueryResultsStore { return assertExists(this._queryResults); } get threads() { return assertExists(this._threadMap); } get sliceDetails() { return assertExists(this._sliceDetails); } set sliceDetails(click: SliceDetails) { this._sliceDetails = assertExists(click); } setTrackData(id: string, data: {}) { this.trackDataStore.set(id, data); assertExists(this._pendingTrackRequests).delete(id); } getCurResolution() { // Truncate the resolution to the closest power of 10. const resolution = this.frontendLocalState.timeScale.deltaPxToDuration(1); return Math.pow(10, Math.floor(Math.log10(resolution))); } requestTrackData(trackId: string) { const pending = assertExists(this._pendingTrackRequests); if (pending.has(trackId)) return; const {visibleWindowTime} = globals.frontendLocalState; const resolution = this.getCurResolution(); const start = visibleWindowTime.start - visibleWindowTime.duration; const end = visibleWindowTime.end + visibleWindowTime.duration; pending.add(trackId); globals.dispatch(Actions.reqTrackData({ trackId, start, end, resolution, })); } resetForTesting() { this._dispatch = undefined; this._state = undefined; this._frontendLocalState = undefined; this._rafScheduler = undefined; // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads. this._trackDataStore = undefined; this._queryResults = undefined; this._overviewStore = undefined; this._threadMap = undefined; this._sliceDetails = undefined; this._pendingTrackRequests = undefined; } // Used when switching to the legacy TraceViewer UI. // Most resources are cleaned up by replacing the current |window| object, // however pending RAFs and workers seem to outlive the |window| and need to // be cleaned up explicitly. shutdown() { this._controllerWorker!.terminate(); this._rafScheduler!.shutdown(); } } export const globals = new Globals();