UNPKG

chrome-devtools-frontend

Version:
275 lines (241 loc) • 9.04 kB
// Copyright 2023 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 type * as SDK from '../../../core/sdk/sdk.js'; import type * as Protocol from '../../../generated/protocol.js'; import type * as CrUXManager from '../../../models/crux-manager/crux-manager.js'; import type {TraceWindowMicro} from './Timing.js'; import type {Event, LegacyTimelineFrame, ProcessID, SampleIndex, ThreadID} from './TraceEvents.js'; export interface TraceFile { traceEvents: readonly Event[]; metadata: MetaData; } export interface Breadcrumb { window: TraceWindowMicro; child: Breadcrumb|null; } export const enum DataOrigin { CPU_PROFILE = 'CPUProfile', TRACE_EVENTS = 'TraceEvents', } /** * The Entries link can have 3 stated: * 1. The Link creation is not started yet, meaning only the button that needs to be clicked to start creating the link is visible. * 2. Pending to event - the creation is started, but the entry that the link points to has not been chosen yet * 3. Link connected - final state, both entries present */ export const enum EntriesLinkState { CREATION_NOT_STARTED = 'creation_not_started', PENDING_TO_EVENT = 'pending_to_event', CONNECTED = 'connected', } export const enum EventKeyType { RAW_EVENT = 'r', SYNTHETIC_EVENT = 's', PROFILE_CALL = 'p', LEGACY_TIMELINE_FRAME = 'l', } /** * Represents an object that is saved in the file when user created annotations in the timeline. * * Expected to add more annotations. */ export interface SerializedAnnotations { entryLabels: EntryLabelAnnotationSerialized[]; labelledTimeRanges: TimeRangeAnnotationSerialized[]; linksBetweenEntries: EntriesLinkAnnotationSerialized[]; } /** * Represents an object that is used to store the Entry Label annotation that is created when a user creates a label for an entry in the timeline. */ export interface EntryLabelAnnotation { type: 'ENTRY_LABEL'; entry: Event|LegacyTimelineFrame; label: string; } /** * Represents an object that is used to store the Labelled Time Range Annotation that is created when a user creates a Time Range Selection in the timeline. */ export interface TimeRangeAnnotation { type: 'TIME_RANGE'; label: string; bounds: TraceWindowMicro; } export interface EntriesLinkAnnotation { type: 'ENTRIES_LINK'; state: EntriesLinkState; entryFrom: Event; entryTo?: Event; } /** * Represents an object that is saved in the file when a user creates a label for an entry in the timeline. */ export interface EntryLabelAnnotationSerialized { entry: SerializableKey; label: string; } /** * Represents an object that is saved in the file when a user creates a time range with a label in the timeline. */ export interface TimeRangeAnnotationSerialized { bounds: TraceWindowMicro; label: string; } /** * Represents an object that is saved in the file when a user creates a link between entries in the timeline. */ export interface EntriesLinkAnnotationSerialized { entryFrom: SerializableKey; entryTo: SerializableKey; } /** * `Annotation` are the user-created annotations that are saved into the metadata. * Those annotations are rendered on the timeline by `Overlays.ts` * * TODO: Implement other OverlayAnnotations (annotated time ranges, links between entries). * TODO: Save/load overlay annotations to/from the trace file. */ export type Annotation = EntryLabelAnnotation|TimeRangeAnnotation|EntriesLinkAnnotation; export function isTimeRangeAnnotation(annotation: Annotation): annotation is TimeRangeAnnotation { return annotation.type === 'TIME_RANGE'; } export function isEntryLabelAnnotation(annotation: Annotation): annotation is EntryLabelAnnotation { return annotation.type === 'ENTRY_LABEL'; } export function isEntriesLinkAnnotation(annotation: Annotation): annotation is EntriesLinkAnnotation { return annotation.type === 'ENTRIES_LINK'; } // Serializable keys are created for trace events to be able to save // references to timeline events in a trace file. These keys enable // user modifications that can be saved. See go/cpq:event-data-json for // more details on the key format. export type RawEventKey = `${EventKeyType.RAW_EVENT}-${number}`; export type SyntheticEventKey = `${EventKeyType.SYNTHETIC_EVENT}-${number}`; export type ProfileCallKey = `${EventKeyType.PROFILE_CALL}-${ProcessID}-${ThreadID}-${SampleIndex}-${Protocol.integer}`; export type LegacyTimelineFrameKey = `${EventKeyType.LEGACY_TIMELINE_FRAME}-${number}`; export type SerializableKey = RawEventKey|ProfileCallKey|SyntheticEventKey|LegacyTimelineFrameKey; // Serializable keys values objects contain data that maps the keys to original Trace Events export interface RawEventKeyValues { type: EventKeyType.RAW_EVENT; rawIndex: number; } export interface SyntheticEventKeyValues { type: EventKeyType.SYNTHETIC_EVENT; rawIndex: number; } export interface ProfileCallKeyValues { type: EventKeyType.PROFILE_CALL; processID: ProcessID; threadID: ThreadID; sampleIndex: SampleIndex; protocol: Protocol.integer; } export interface LegacyTimelineFrameKeyValues { type: EventKeyType.LEGACY_TIMELINE_FRAME; rawIndex: number; } export type SerializableKeyValues = RawEventKeyValues|ProfileCallKeyValues|SyntheticEventKeyValues|LegacyTimelineFrameKeyValues; export interface Modifications { entriesModifications: { // Entries hidden by the user hiddenEntries: SerializableKey[], // Entries that parent a hiddenEntry expandableEntries: SerializableKey[], }; initialBreadcrumb: Breadcrumb; annotations: SerializedAnnotations; } // IMPORTANT: this is the same as PerfUI.FlameChart.PersistedGroupConfig // However, the PerfUI code should not depend on the model/trace, and similarly // this model cannot depend on that code, so we duplicate it. export interface TrackVisualConfig { hidden: boolean; expanded: boolean; originalIndex: number; visualIndex: number; } /** * Stores the visual config if the user has modified it. Split into "main" and * "network" so we can pass the relevant config into the right data provider. */ export interface PersistedTraceVisualConfig { main: TrackVisualConfig[]|null; network: TrackVisualConfig[]|null; } /** * Trace metadata that we persist to the file. This will allow us to * store specifics for the trace, e.g., which tracks should be visible * on load. */ export interface MetaData { source?: 'DevTools'; startTime?: string; emulatedDeviceTitle?: string; // Only set if network throttling is active. networkThrottling?: string; // Only set if network throttling is active. networkThrottlingConditions?: Omit<SDK.NetworkManager.Conditions, 'title'>; // Only set if CPU throttling is active. cpuThrottling?: number; hardwareConcurrency?: number; dataOrigin?: DataOrigin; enhancedTraceVersion?: number; modifications?: Modifications; cruxFieldData?: CrUXManager.PageResult[]; /** Currently only stores JS maps, not CSS. This never stores data url source maps. */ sourceMaps?: MetadataSourceMap[]; visualTrackConfig?: PersistedTraceVisualConfig; } interface MetadataSourceMap { url: string; /** If not defined, then this was a data url. */ sourceMapUrl?: string; sourceMap: SDK.SourceMap.SourceMapV3; } export type Contents = TraceFile|Event[]; export function traceEventKeyToValues(key: SerializableKey): SerializableKeyValues { const parts = key.split('-'); const type = parts[0]; switch (type) { case EventKeyType.PROFILE_CALL: if (parts.length !== 5 || !(parts.every((part, i) => i === 0 || typeof part === 'number' || !isNaN(parseInt(part, 10))))) { throw new Error(`Invalid ProfileCallKey: ${key}`); } return { type: parts[0], processID: parseInt(parts[1], 10), threadID: parseInt(parts[2], 10), sampleIndex: parseInt(parts[3], 10), protocol: parseInt(parts[4], 10), } as ProfileCallKeyValues; case EventKeyType.RAW_EVENT: if (parts.length !== 2 || !(typeof parts[1] === 'number' || !isNaN(parseInt(parts[1], 10)))) { throw new Error(`Invalid RawEvent Key: ${key}`); } return { type: parts[0], rawIndex: parseInt(parts[1], 10), } as RawEventKeyValues; case EventKeyType.SYNTHETIC_EVENT: if (parts.length !== 2 || !(typeof parts[1] === 'number' || !isNaN(parseInt(parts[1], 10)))) { throw new Error(`Invalid SyntheticEvent Key: ${key}`); } return { type: parts[0], rawIndex: parseInt(parts[1], 10), } as SyntheticEventKeyValues; case EventKeyType.LEGACY_TIMELINE_FRAME: { if (parts.length !== 2 || Number.isNaN(parseInt(parts[1], 10))) { throw new Error(`Invalid LegacyTimelineFrame Key: ${key}`); } return { type, rawIndex: parseInt(parts[1], 10), }; } default: throw new Error(`Unknown trace event key: ${key}`); } }