{"version":3,"file":"UserTimingsHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/UserTimingsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2022 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\n/**\n * IMPORTANT!\n * See UserTimings.md in this directory for some handy documentation on\n * UserTimings and the trace events we parse currently.\n **/\nlet syntheticEvents: Array<Types.Events.SyntheticEventPair<Types.Events.PairableAsync>> = [];\n\n// There are two events dispatched for performance.measure calls: one to\n// represent the measured timing in the tracing clock (which we type as\n// PerformanceMeasure) and another one for the call itself (which we\n// type as UserTimingMeasure). The two events corresponding to the same\n// call are linked together by a common trace_id. The reason two events\n// are dispatched is because the first was originally added with the\n// implementation of the performance.measure API and it uses an\n// overridden timestamp and duration. To prevent breaking potential deps\n// created since then, a second event was added instead of changing the\n// params of the first.\nconst measureTraceByTraceId = new Map<number, Types.Events.UserTimingMeasure>();\nconst performanceMeasureEvents: Types.Events.PerformanceMeasure[] = [];\nconst performanceMarkEvents: Types.Events.PerformanceMark[] = [];\n\nconst consoleTimings: Array<Types.Events.ConsoleTimeBegin|Types.Events.ConsoleTimeEnd> = [];\n\nconst timestampEvents: Types.Events.ConsoleTimeStamp[] = [];\n\nexport interface UserTimingsData {\n  /**\n   * Events triggered with the performance.measure() API.\n   * https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure\n   */\n  performanceMeasures: readonly Types.Events.SyntheticUserTimingPair[];\n  /**\n   * Events triggered with the performance.mark() API.\n   * https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark\n   */\n  performanceMarks: readonly Types.Events.PerformanceMark[];\n  /**\n   * Events triggered with the console.time(), console.timeEnd() and\n   * console.timeLog() API.\n   * https://developer.mozilla.org/en-US/docs/Web/API/console/time\n   */\n  consoleTimings: readonly Types.Events.SyntheticConsoleTimingPair[];\n  /**\n   * Events triggered with the console.timeStamp() API\n   * https://developer.mozilla.org/en-US/docs/Web/API/console/timeStamp\n   */\n  timestampEvents: readonly Types.Events.ConsoleTimeStamp[];\n  /**\n   * Events triggered to trace the call to performance.measure itself,\n   * cached by trace_id.\n   */\n  measureTraceByTraceId: Map<number, Types.Events.UserTimingMeasure>;\n}\n\nexport function reset(): void {\n  syntheticEvents.length = 0;\n  performanceMeasureEvents.length = 0;\n  performanceMarkEvents.length = 0;\n  consoleTimings.length = 0;\n  timestampEvents.length = 0;\n  measureTraceByTraceId.clear();\n}\n\nconst resourceTimingNames = [\n  'workerStart',\n  'redirectStart',\n  'redirectEnd',\n  'fetchStart',\n  'domainLookupStart',\n  'domainLookupEnd',\n  'connectStart',\n  'connectEnd',\n  'secureConnectionStart',\n  'requestStart',\n  'responseStart',\n  'responseEnd',\n];\nconst navTimingNames = [\n  'navigationStart',\n  'unloadEventStart',\n  'unloadEventEnd',\n  'redirectStart',\n  'redirectEnd',\n  'fetchStart',\n  'commitNavigationEnd',\n  'domainLookupStart',\n  'domainLookupEnd',\n  'connectStart',\n  'connectEnd',\n  'secureConnectionStart',\n  'requestStart',\n  'responseStart',\n  'responseEnd',\n  'domLoading',\n  'domInteractive',\n  'domContentLoadedEventStart',\n  'domContentLoadedEventEnd',\n  'domComplete',\n  'loadEventStart',\n  'loadEventEnd',\n];\n// These are events dispatched under the blink.user_timing category\n// but that the user didn't add. Filter them out so that they do not\n// Appear in the timings track (they still appear in the main thread\n// flame chart).\nconst ignoredNames = [...resourceTimingNames, ...navTimingNames];\n\n/**\n * Similar to the default {@see Helpers.Trace.eventTimeComparator}\n * but with a twist:\n * In case of equal start and end times, always put the second event\n * first.\n *\n * Explanation:\n * User timing entries come as trace events dispatched when\n * performance.measure/mark is called. The trace events buffered in\n * devtools frontend are sorted by the start time. If their start time\n * is the same, then the event for the first call will appear first.\n *\n * When entries are meant to be stacked, the corresponding\n * performance.measure calls usually are done in bottom-up direction:\n * calls for children first and for parent later (because the call\n * is usually done when the measured task is over). This means that\n * when two user timing events have the start and end time, usually the\n * second event is the parent of the first. Hence the switch.\n *\n */\nfunction userTimingComparator(\n    a: Helpers.Trace.TimeSpan, b: Helpers.Trace.TimeSpan, originalArray: Helpers.Trace.TimeSpan[]): number {\n  const aBeginTime = a.ts;\n  const bBeginTime = b.ts;\n  if (aBeginTime < bBeginTime) {\n    return -1;\n  }\n  if (aBeginTime > bBeginTime) {\n    return 1;\n  }\n  const aDuration = a.dur ?? 0;\n  const bDuration = b.dur ?? 0;\n  const aEndTime = aBeginTime + aDuration;\n  const bEndTime = bBeginTime + bDuration;\n  if (aEndTime > bEndTime) {\n    return -1;\n  }\n  if (aEndTime < bEndTime) {\n    return 1;\n  }\n  // Prefer the event located in a further position in the original array.\n  return originalArray.indexOf(b) - originalArray.indexOf(a);\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n  if (ignoredNames.includes(event.name)) {\n    return;\n  }\n  if (Types.Events.isUserTimingMeasure(event)) {\n    measureTraceByTraceId.set(event.args.traceId, event);\n  }\n  if (Types.Events.isPerformanceMeasure(event)) {\n    performanceMeasureEvents.push(event);\n    return;\n  }\n  if (Types.Events.isPerformanceMark(event)) {\n    performanceMarkEvents.push(event);\n  }\n  if (Types.Events.isConsoleTime(event)) {\n    consoleTimings.push(event);\n  }\n  if (Types.Events.isConsoleTimeStamp(event)) {\n    timestampEvents.push(event);\n  }\n}\n\nexport async function finalize(): Promise<void> {\n  const asyncEvents = [...performanceMeasureEvents, ...consoleTimings];\n  syntheticEvents = Helpers.Trace.createMatchedSortedSyntheticEvents(asyncEvents);\n  syntheticEvents = syntheticEvents.sort((a, b) => userTimingComparator(a, b, [...syntheticEvents]));\n}\n\nexport function data(): UserTimingsData {\n  return {\n    performanceMeasures: syntheticEvents.filter(e => e.cat === 'blink.user_timing') as\n        Types.Events.SyntheticUserTimingPair[],\n    consoleTimings: syntheticEvents.filter(e => e.cat === 'blink.console') as Types.Events.SyntheticConsoleTimingPair[],\n    // TODO(crbug/41484172): UserTimingsHandler.test.ts fails if this is not copied.\n    performanceMarks: [...performanceMarkEvents],\n    timestampEvents: [...timestampEvents],\n    measureTraceByTraceId: new Map(measureTraceByTraceId),\n  };\n}\n"]}