lighthouse
Version:
Automated auditing, performance metrics, and best practices for the web.
228 lines • 9.94 kB
TypeScript
export type CpuProfile = {
id: string;
pid: number;
tid: number;
startTime: number;
nodes: Required<LH.TraceCpuProfile>["nodes"];
samples: Array<number>;
timeDeltas: Array<number>;
};
export type ProfilerRange = Required<Required<LH.TraceEvent["args"]>["data"]>["_syntheticProfilerRange"];
export type SynthethicEvent = LH.TraceEvent & {
args: {
data: {
_syntheticProfilerRange: ProfilerRange;
};
};
};
export type SynthethicTaskNode = Omit<LH.Artifacts.TaskNode, "event"> & {
event: SynthethicEvent;
endEvent: SynthethicEvent;
};
/**
* @fileoverview
*
* This model converts the `Profile` and `ProfileChunk` mega trace events from the `disabled-by-default-v8.cpu_profiler`
* category into B/E-style trace events that main-thread-tasks.js already knows how to parse into a task tree.
*
* The V8 CPU profiler measures where time is being spent by sampling the stack (See https://www.jetbrains.com/help/profiler/Profiling_Guidelines__Choosing_the_Right_Profiling_Mode.html
* for a generic description of the differences between tracing and sampling).
*
* A `Profile` event is a record of the stack that was being executed at different sample points in time.
* It has a structure like this:
*
* nodes: [function A, function B, function C]
* samples: [node with id 2, node with id 1, ...]
* timeDeltas: [4125μs since last sample, 121μs since last sample, ...]
*
* Note that this is subtly different from the protocol-based Crdp.Profiler.Profile type.
*
* Helpful prior art:
* @see https://cs.chromium.org/chromium/src/third_party/devtools-frontend/src/front_end/sdk/CPUProfileDataModel.js?sq=package:chromium&g=0&l=42
* @see https://github.com/v8/v8/blob/99ca333b0efba3236954b823101315aefeac51ab/tools/profile.js
* @see https://github.com/jlfwong/speedscope/blob/9ed1eb192cb7e9dac43a5f25bd101af169dc654a/src/import/chrome.ts#L200
*/
/**
* @typedef CpuProfile
* @property {string} id
* @property {number} pid
* @property {number} tid
* @property {number} startTime
* @property {Required<LH.TraceCpuProfile>['nodes']} nodes
* @property {Array<number>} samples
* @property {Array<number>} timeDeltas
*/
/** @typedef {Required<Required<LH.TraceEvent['args']>['data']>['_syntheticProfilerRange']} ProfilerRange */
/** @typedef {LH.TraceEvent & {args: {data: {_syntheticProfilerRange: ProfilerRange}}}} SynthethicEvent */
/** @typedef {Omit<LH.Artifacts.TaskNode, 'event'> & {event: SynthethicEvent, endEvent: SynthethicEvent}} SynthethicTaskNode */
export class CpuProfileModel {
/**
* @param {LH.TraceEvent | undefined} event
* @return {event is SynthethicEvent}
*/
static isSyntheticEvent(event: LH.TraceEvent | undefined): event is SynthethicEvent;
/**
* @param {LH.Artifacts.TaskNode} task
* @return {task is SynthethicTaskNode}
*/
static isSyntheticTask(task: LH.Artifacts.TaskNode): task is SynthethicTaskNode;
/**
* Finds all the tasks that started or ended (depending on `type`) within the provided time range.
* Uses a memory index to remember the place in the array the last invocation left off to avoid
* re-traversing the entire array, but note that this index might still be slightly off from the
* true start position.
*
* @param {Array<{startTime: number, endTime: number}>} knownTasks
* @param {{type: 'startTime'|'endTime', initialIndex: number, earliestPossibleTimestamp: number, latestPossibleTimestamp: number}} options
*/
static _getTasksInRange(knownTasks: Array<{
startTime: number;
endTime: number;
}>, options: {
type: "startTime" | "endTime";
initialIndex: number;
earliestPossibleTimestamp: number;
latestPossibleTimestamp: number;
}): {
tasks: {
startTime: number;
endTime: number;
}[];
lastIndex: number;
};
/**
* Given a particular time range and a set of known true tasks, find the correct timestamp to use
* for a transition between tasks.
*
* Because the sampling profiler only provides a *range* of start/stop function boundaries, this
* method uses knowledge of a known set of tasks to find the most accurate timestamp for a particular
* range. For example, if we know that a function ended between 800ms and 810ms, we can use the
* knowledge that a toplevel task ended at 807ms to use 807ms as the correct endtime for this function.
*
* @param {{syntheticTask: SynthethicTaskNode, eventType: 'start'|'end', allEventsAtTs: {naive: Array<SynthethicEvent>, refined: Array<SynthethicEvent>}, knownTaskStartTimeIndex: number, knownTaskEndTimeIndex: number, knownTasksByStartTime: Array<{startTime: number, endTime: number}>, knownTasksByEndTime: Array<{startTime: number, endTime: number}>}} data
* @return {{timestamp: number, lastStartTimeIndex: number, lastEndTimeIndex: number}}
*/
static _findEffectiveTimestamp(data: {
syntheticTask: SynthethicTaskNode;
eventType: "start" | "end";
allEventsAtTs: {
naive: Array<SynthethicEvent>;
refined: Array<SynthethicEvent>;
};
knownTaskStartTimeIndex: number;
knownTaskEndTimeIndex: number;
knownTasksByStartTime: Array<{
startTime: number;
endTime: number;
}>;
knownTasksByEndTime: Array<{
startTime: number;
endTime: number;
}>;
}): {
timestamp: number;
lastStartTimeIndex: number;
lastEndTimeIndex: number;
};
/**
* Creates B/E-style trace events from a CpuProfile object created by `collectProfileEvents()`
*
* @param {CpuProfile} profile
* @param {Array<LH.Artifacts.TaskNode>} tasks
* @return {Array<LH.TraceEvent>}
*/
static synthesizeTraceEvents(profile: CpuProfile, tasks: Array<LH.Artifacts.TaskNode>): Array<LH.TraceEvent>;
/**
* Merges the data of all the `ProfileChunk` trace events into a single CpuProfile object for consumption
* by `synthesizeTraceEvents()`.
*
* @param {Array<LH.TraceEvent>} traceEvents
* @return {Array<CpuProfile>}
*/
static collectProfileEvents(traceEvents: Array<LH.TraceEvent>): Array<CpuProfile>;
/**
* @param {CpuProfile} profile
*/
constructor(profile: CpuProfile);
_profile: CpuProfile;
_nodesById: Map<number, {
id: number;
callFrame: {
functionName: string;
url?: string;
};
parent?: number;
}>;
_activeNodeArraysById: Map<number, number[]>;
/**
* Initialization function to enable O(1) access to nodes by node ID.
* @return {Map<number, CpuProfile['nodes'][0]>}
*/
_createNodeMap(): Map<number, CpuProfile["nodes"][0]>;
/**
* Initialization function to enable O(1) access to the set of active nodes in the stack by node ID.
* @return {Map<number, Array<number>>}
*/
_createActiveNodeArrays(): Map<number, Array<number>>;
/**
* Returns all the node IDs in a stack when a specific nodeId is at the top of the stack
* (i.e. a stack's node ID and the node ID of all of its parents).
*
* @param {number} nodeId
* @return {Array<number>}
*/
_getActiveNodeIds(nodeId: number): Array<number>;
/**
* Generates the necessary B/E-style trace events for a single transition from stack A to stack B
* at the given latest timestamp (includes possible range in event.args.data).
*
* Example:
*
* latestPossibleTimestamp 1234
* previousNodeIds 1,2,3
* currentNodeIds 1,2,4
*
* yields [end 3 at ts 1234, begin 4 at ts 1234]
*
* @param {number} earliestPossibleTimestamp
* @param {number} latestPossibleTimestamp
* @param {Array<number>} previousNodeIds
* @param {Array<number>} currentNodeIds
* @return {Array<SynthethicEvent>}
*/
_synthesizeTraceEventsForTransition(earliestPossibleTimestamp: number, latestPossibleTimestamp: number, previousNodeIds: Array<number>, currentNodeIds: Array<number>): Array<SynthethicEvent>;
/**
* Creates the B/E-style trace events using only data from the profile itself. Each B/E event will
* include the actual _range_ the timestamp could have been in its metadata that is used for
* refinement later.
*
* @return {Array<SynthethicEvent>}
*/
_synthesizeNaiveTraceEvents(): Array<SynthethicEvent>;
/**
* Creates a copy of B/E-style trace events with refined timestamps using knowledge from the
* tasks that have definitive timestamps.
*
* With the sampling profiler we know that a function started/ended _sometime between_ two points,
* but not exactly when. Using the information from other tasks gives us more information to be
* more precise with timings and allows us to create a valid task tree later on.
*
* @param {Array<{startTime: number, endTime: number}>} knownTasks
* @param {Array<SynthethicTaskNode>} syntheticTasks
* @param {Array<SynthethicEvent>} syntheticEvents
* @return {Array<SynthethicEvent>}
*/
_refineTraceEventsWithTasks(knownTasks: Array<{
startTime: number;
endTime: number;
}>, syntheticTasks: Array<SynthethicTaskNode>, syntheticEvents: Array<SynthethicEvent>): Array<SynthethicEvent>;
/**
* Creates B/E-style trace events from a CpuProfile object created by `collectProfileEvents()`.
* An optional set of tasks can be passed in to refine the start/end times.
*
* @param {Array<LH.Artifacts.TaskNode>} [knownTaskNodes]
* @return {Array<LH.TraceEvent>}
*/
synthesizeTraceEvents(knownTaskNodes?: Array<LH.Artifacts.TaskNode>): Array<LH.TraceEvent>;
}
//# sourceMappingURL=cpu-profile-model.d.ts.map