UNPKG

@quick-game/cli

Version:

Command line interface for rapid qg development

164 lines 6.97 kB
// Copyright 2022 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Platform from '../../core/platform/platform.js'; import * as Handlers from './handlers/handlers.js'; import * as Helpers from './helpers/helpers.js'; import { TraceProcessor, TraceParseProgressEvent } from './Processor.js'; /** * The new trace engine model we are migrating to. The Model is responsible for * parsing arrays of raw trace events and storing the resulting data. It can * store multiple traces at once, and can return the data for any of them. * Currently as we migrate from the old engine to this, we are turning on the * model handlers incrementally as we need the data, to save performance costs * of running handlers that we do not use. Therefore, when the model is * constructed we pass through a set of handlers that should be used. Once we * have migrated all tracks in the Performance Panel to this model, we can * remove this ability to run a subset of handlers, as we will need all handlers * to be used at that point. For tests, if you want to construct a model with * all handlers, you can use the static `Model.createWithAllHandlers` method. **/ export class Model extends EventTarget { #traces = []; #nextNumberByDomain = new Map(); #recordingsAvailable = []; #lastRecordingIndex = 0; #processor; static createWithAllHandlers() { return new Model(Handlers.ModelHandlers); } static createWithRequiredHandlersForMigration() { return new Model(Handlers.Migration.ENABLED_TRACE_HANDLERS); } constructor(handlers) { super(); this.#processor = new TraceProcessor(handlers); } /** * Parses an array of trace events into a structured object containing all the * information parsed by the trace handlers. * You can `await` this function to pause execution until parsing is complete, * or instead rely on the `ModuleUpdateEvent` that is dispatched when the * parsing is finished. * * Once parsed, you then have to call the `traceParsedData` method, providing an * index of the trace you want to have the data for. This is because any model * can store a number of traces. Each trace is given an index, which starts at 0 * and increments by one as a new trace is parsed. * * @example * // Awaiting the parse method() to block until parsing complete * await this.traceModel.parse(events); * const data = this.traceModel.traceParsedData(0) * * @example * // Using an event listener to be notified when tracing is complete. * this.traceModel.addEventListener(Trace.ModelUpdateEvent.eventName, (event) => { * if(event.data.data === 'done') { * // trace complete * const data = this.traceModel.traceParsedData(0); * } * }); * void this.traceModel.parse(events); **/ async parse(traceEvents, config) { const metadata = config?.metadata || {}; const isFreshRecording = config?.isFreshRecording || false; // During parsing, periodically update any listeners on each processors' // progress (if they have any updates). const onTraceUpdate = (event) => { const { data } = event; this.dispatchEvent(new ModelUpdateEvent({ type: "PROGRESS_UPDATE" /* ModelUpdateType.PROGRESS_UPDATE */, data: data })); }; this.#processor.addEventListener(TraceParseProgressEvent.eventName, onTraceUpdate); // Create a parsed trace file. It will be populated with data from the processor. const file = { traceEvents, metadata, traceParsedData: null, }; try { // Wait for all outstanding promises before finishing the async execution, // but perform all tasks in parallel. await this.#processor.parse(traceEvents, isFreshRecording); this.#storeParsedFileData(file, this.#processor.data); // We only push the file onto this.#traces here once we know it's valid // and there's been no errors in the parsing. this.#traces.push(file); } catch (e) { throw e; } finally { // All processors have finished parsing, no more updates are expected. this.#processor.removeEventListener(TraceParseProgressEvent.eventName, onTraceUpdate); // Finally, update any listeners that all processors are 'done'. this.dispatchEvent(new ModelUpdateEvent({ type: "COMPLETE" /* ModelUpdateType.COMPLETE */, data: 'done' })); } } #storeParsedFileData(file, data) { file.traceParsedData = data; this.#lastRecordingIndex++; let recordingName = `Trace ${this.#lastRecordingIndex}`; let origin = null; if (file.traceParsedData) { origin = Helpers.Trace.extractOriginFromTrace(file.traceParsedData.Meta.mainFrameURL); if (origin) { const nextSequenceForDomain = Platform.MapUtilities.getWithDefault(this.#nextNumberByDomain, origin, () => 1); recordingName = `${origin} (${nextSequenceForDomain})`; this.#nextNumberByDomain.set(origin, nextSequenceForDomain + 1); } } this.#recordingsAvailable.push(recordingName); } /** * Returns the parsed trace data indexed by the order in which it was stored. * If no index is given, the last stored parsed data is returned. */ traceParsedData(index = this.#traces.length - 1) { if (!this.#traces[index]) { return null; } return this.#traces[index].traceParsedData; } metadata(index) { if (!this.#traces[index]) { return null; } return this.#traces[index].metadata; } traceEvents(index) { if (!this.#traces[index]) { return null; } return this.#traces[index].traceEvents; } size() { return this.#traces.length; } deleteTraceByIndex(recordingIndex) { this.#traces.splice(recordingIndex, 1); this.#recordingsAvailable.splice(recordingIndex, 1); } getRecordingsAvailable() { return this.#recordingsAvailable; } resetProcessor() { this.#processor.reset(); } } export class ModelUpdateEvent extends Event { data; static eventName = 'modelupdate'; constructor(data) { super(ModelUpdateEvent.eventName); this.data = data; } } export function isModelUpdateDataComplete(eventData) { return eventData.type === "COMPLETE" /* ModelUpdateType.COMPLETE */; } export function isModelUpdateDataProgress(eventData) { return eventData.type === "PROGRESS_UPDATE" /* ModelUpdateType.PROGRESS_UPDATE */; } //# sourceMappingURL=ModelImpl.js.map