devtools-tracing 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -27,6 +27,7 @@ var fs = __toESM(require("node:fs"));
27
27
  var zlib = __toESM(require("node:zlib"));
28
28
  var import__ = require("../");
29
29
  var SelectorTimingsKey = import__.Trace.Types.Events.SelectorTimingsKey;
30
+ var microToMilli = (us) => import__.Trace.Helpers.Timing.microToMilli(import__.Trace.Types.Timing.Micro(us));
30
31
  async function run(tracePath) {
31
32
  (0, import__.initDevToolsTracing)();
32
33
  const fileData = fs.readFileSync(tracePath);
@@ -66,55 +67,69 @@ async function run(tracePath) {
66
67
  }
67
68
  const allTimings = [...selectorMap.values()];
68
69
  const TOP_N = 15;
70
+ const truncateSelector = (s) => s.length > 80 ? `${s.slice(0, 77)}...` : s;
69
71
  const byElapsed = allTimings.sort(
70
72
  (a, b) => b[SelectorTimingsKey.Elapsed] - a[SelectorTimingsKey.Elapsed]
71
73
  ).slice(0, TOP_N);
72
74
  console.log(`
73
75
  === Top ${TOP_N} CSS Selectors by Elapsed Time ===
74
76
  `);
75
- for (const t of byElapsed) {
76
- const elapsedMs = (t[SelectorTimingsKey.Elapsed] / 1e3).toFixed(2);
77
- console.log(
78
- ` ${elapsedMs}ms | attempts: ${t[SelectorTimingsKey.MatchAttempts]} | matches: ${t[SelectorTimingsKey.MatchCount]} | ${t[SelectorTimingsKey.Selector]}`
79
- );
80
- }
77
+ console.table(
78
+ Object.fromEntries(
79
+ byElapsed.map((t, i) => [
80
+ i + 1,
81
+ {
82
+ selector: truncateSelector(t[SelectorTimingsKey.Selector]),
83
+ "elapsed (ms)": microToMilli(t[SelectorTimingsKey.Elapsed]),
84
+ match_attempts: t[SelectorTimingsKey.MatchAttempts],
85
+ match_count: t[SelectorTimingsKey.MatchCount],
86
+ fast_reject_count: t[SelectorTimingsKey.FastRejectCount]
87
+ }
88
+ ])
89
+ )
90
+ );
81
91
  const byAttempts = [...selectorMap.values()].sort(
82
92
  (a, b) => b[SelectorTimingsKey.MatchAttempts] - a[SelectorTimingsKey.MatchAttempts]
83
93
  ).slice(0, TOP_N);
84
94
  console.log(`
85
95
  === Top ${TOP_N} CSS Selectors by Match Attempts ===
86
96
  `);
87
- for (const t of byAttempts) {
88
- const elapsedMs = (t[SelectorTimingsKey.Elapsed] / 1e3).toFixed(2);
89
- console.log(
90
- ` ${t[SelectorTimingsKey.MatchAttempts]} attempts | ${elapsedMs}ms | matches: ${t[SelectorTimingsKey.MatchCount]} | ${t[SelectorTimingsKey.Selector]}`
91
- );
92
- }
93
- const invalidatedNodes = selectorStatsData.invalidatedNodeList;
94
- console.log(
95
- `
96
- === Invalidation Tracking (${invalidatedNodes.length} invalidated nodes) ===
97
- `
97
+ console.table(
98
+ Object.fromEntries(
99
+ byAttempts.map((t, i) => [
100
+ i + 1,
101
+ {
102
+ selector: truncateSelector(t[SelectorTimingsKey.Selector]),
103
+ match_attempts: t[SelectorTimingsKey.MatchAttempts],
104
+ "elapsed (ms)": microToMilli(t[SelectorTimingsKey.Elapsed]),
105
+ match_count: t[SelectorTimingsKey.MatchCount],
106
+ fast_reject_count: t[SelectorTimingsKey.FastRejectCount]
107
+ }
108
+ ])
109
+ )
98
110
  );
99
- const invalidationSelectorCounts = /* @__PURE__ */ new Map();
100
- for (const node of invalidatedNodes) {
101
- for (const sel of node.selectorList) {
102
- const count = invalidationSelectorCounts.get(sel.selector) ?? 0;
103
- invalidationSelectorCounts.set(sel.selector, count + 1);
104
- }
111
+ const invalidationsData = parsedTraceData.Invalidations;
112
+ const allInvalidations = [];
113
+ for (const [, invalidations] of invalidationsData.invalidationsForEvent) {
114
+ allInvalidations.push(...invalidations);
105
115
  }
106
- const topInvalidationSelectors = [...invalidationSelectorCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, TOP_N);
116
+ const { groupedByReason, backendNodeIds } = (0, import__.generateInvalidationsList)(allInvalidations);
117
+ const reasons = Object.entries(groupedByReason).sort((a, b) => b[1].length - a[1].length);
107
118
  console.log(
108
- ` Top ${TOP_N} selectors causing invalidations:
119
+ `
120
+ === Invalidation Tracking (${allInvalidations.length} invalidations, ${backendNodeIds.size} unique nodes) ===
109
121
  `
110
122
  );
111
- for (const [selector, count] of topInvalidationSelectors) {
112
- console.log(` ${count}x | ${selector}`);
113
- }
114
- const subtreeCount = invalidatedNodes.filter((n) => n.subtree).length;
115
- console.log(
116
- `
117
- Subtree invalidations: ${subtreeCount} / ${invalidatedNodes.length} total`
123
+ console.table(
124
+ Object.fromEntries(
125
+ reasons.map(([reason, invalidations], i) => [
126
+ i + 1,
127
+ {
128
+ reason,
129
+ count: invalidations.length
130
+ }
131
+ ])
132
+ )
118
133
  );
119
134
  let totalElapsedUs = 0;
120
135
  let totalMatchAttempts = 0;
@@ -127,10 +142,12 @@ async function run(tracePath) {
127
142
  console.log(`
128
143
  === Totals ===
129
144
  `);
130
- console.log(` Unique selectors: ${allTimings.length}`);
131
- console.log(` Total elapsed: ${(totalElapsedUs / 1e3).toFixed(2)}ms`);
132
- console.log(` Total match attempts: ${totalMatchAttempts}`);
133
- console.log(` Total match count: ${totalMatchCount}`);
145
+ console.table({
146
+ "Unique selectors": allTimings.length,
147
+ "Total elapsed (ms)": microToMilli(totalElapsedUs),
148
+ "Total match attempts": totalMatchAttempts,
149
+ "Total match count": totalMatchCount
150
+ });
134
151
  }
135
152
 
136
153
  // commands/inp.ts
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { initDevToolsTracing } from './src/init.js';
2
2
  export { statsForTimeRange, entryIsVisibleInTimeline } from './src/timeline.js';
3
+ export { generateInvalidationsList } from './src/invalidations.js';
3
4
  export { createSourceMapResolver, symbolicateTrace } from './src/sourcemap.js';
4
5
  export type { SymbolicateOptions, SymbolicateResult } from './src/sourcemap.js';
5
6
  import * as Trace from './lib/front_end/models/trace/trace.js';
package/dist/index.js CHANGED
@@ -10998,6 +10998,7 @@ __export(index_exports, {
10998
10998
  Trace: () => trace_exports,
10999
10999
  createSourceMapResolver: () => createSourceMapResolver,
11000
11000
  entryIsVisibleInTimeline: () => entryIsVisibleInTimeline,
11001
+ generateInvalidationsList: () => generateInvalidationsList,
11001
11002
  initDevToolsTracing: () => initDevToolsTracing,
11002
11003
  statsForTimeRange: () => statsForTimeRange,
11003
11004
  symbolicateTrace: () => symbolicateTrace
@@ -42414,6 +42415,51 @@ function entryIsVisibleInTimeline(entry, parsedTrace) {
42414
42415
  return eventStyle && !eventStyle.hidden || eventIsTiming;
42415
42416
  }
42416
42417
 
42418
+ // src/invalidations.ts
42419
+ function generateInvalidationsList(invalidations) {
42420
+ const groupedByReason = {};
42421
+ const backendNodeIds2 = /* @__PURE__ */ new Set();
42422
+ for (const invalidation of invalidations) {
42423
+ backendNodeIds2.add(invalidation.args.data.nodeId);
42424
+ let reason = invalidation.args.data.reason || "unknown";
42425
+ if (reason === "unknown" && types_exports.Events.isScheduleStyleInvalidationTracking(invalidation) && invalidation.args.data.invalidatedSelectorId) {
42426
+ switch (invalidation.args.data.invalidatedSelectorId) {
42427
+ case "attribute":
42428
+ reason = "Attribute";
42429
+ if (invalidation.args.data.changedAttribute) {
42430
+ reason += ` (${invalidation.args.data.changedAttribute})`;
42431
+ }
42432
+ break;
42433
+ case "class":
42434
+ reason = "Class";
42435
+ if (invalidation.args.data.changedClass) {
42436
+ reason += ` (${invalidation.args.data.changedClass})`;
42437
+ }
42438
+ break;
42439
+ case "id":
42440
+ reason = "Id";
42441
+ if (invalidation.args.data.changedId) {
42442
+ reason += ` (${invalidation.args.data.changedId})`;
42443
+ }
42444
+ break;
42445
+ }
42446
+ }
42447
+ if (reason === "PseudoClass" && types_exports.Events.isStyleRecalcInvalidationTracking(invalidation) && invalidation.args.data.extraData) {
42448
+ reason += invalidation.args.data.extraData;
42449
+ }
42450
+ if (reason === "Attribute" && types_exports.Events.isStyleRecalcInvalidationTracking(invalidation) && invalidation.args.data.extraData) {
42451
+ reason += ` (${invalidation.args.data.extraData})`;
42452
+ }
42453
+ if (reason === "StyleInvalidator") {
42454
+ continue;
42455
+ }
42456
+ const existing = groupedByReason[reason] || [];
42457
+ existing.push(invalidation);
42458
+ groupedByReason[reason] = existing;
42459
+ }
42460
+ return { groupedByReason, backendNodeIds: backendNodeIds2 };
42461
+ }
42462
+
42417
42463
  // lib/front_end/core/sdk/sdk.ts
42418
42464
  var sdk_exports = {};
42419
42465
  __export(sdk_exports, {
@@ -86844,6 +86890,7 @@ function decodeDataUrl(dataUrl) {
86844
86890
  Trace,
86845
86891
  createSourceMapResolver,
86846
86892
  entryIsVisibleInTimeline,
86893
+ generateInvalidationsList,
86847
86894
  initDevToolsTracing,
86848
86895
  statsForTimeRange,
86849
86896
  symbolicateTrace
@@ -0,0 +1,6 @@
1
+ import type * as Protocol from '../lib/front_end/generated/protocol.js';
2
+ import * as Trace from '../lib/front_end/models/trace/trace.js';
3
+ export declare function generateInvalidationsList(invalidations: Trace.Types.Events.InvalidationTrackingEvent[]): {
4
+ groupedByReason: Record<string, Trace.Types.Events.InvalidationTrackingEvent[]>;
5
+ backendNodeIds: Set<Protocol.DOM.BackendNodeId>;
6
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devtools-tracing",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Utilities for working with trace files",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/commands/inp.ts DELETED
@@ -1,27 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as zlib from 'node:zlib';
3
-
4
- import { initDevToolsTracing, Trace } from '../';
5
-
6
- export async function run(tracePath: string) {
7
- initDevToolsTracing();
8
-
9
- const fileData = fs.readFileSync(tracePath);
10
- const decompressedData = tracePath.endsWith('.gz')
11
- ? zlib.gunzipSync(fileData)
12
- : fileData;
13
- const traceData = JSON.parse(
14
- decompressedData.toString()
15
- ) as Trace.Types.File.TraceFile;
16
-
17
- const processor = Trace.Processor.TraceProcessor.createWithAllHandlers();
18
- await processor.parse(traceData.traceEvents, {});
19
- const insights = processor.insights!.get('NO_NAVIGATION');
20
- const longestInteractionEvent =
21
- insights!.model.INPBreakdown.longestInteractionEvent!;
22
- const inp = longestInteractionEvent.dur / 1000;
23
- console.log({
24
- insights: insights?.model.INPBreakdown,
25
- inp,
26
- });
27
- }
@@ -1,145 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as zlib from 'node:zlib';
3
-
4
- import { initDevToolsTracing, Trace } from '../';
5
-
6
- const SelectorTimingsKey = Trace.Types.Events.SelectorTimingsKey;
7
-
8
- export async function run(tracePath: string) {
9
- initDevToolsTracing();
10
-
11
- const fileData = fs.readFileSync(tracePath);
12
- const decompressedData = tracePath.endsWith('.gz')
13
- ? zlib.gunzipSync(fileData)
14
- : fileData;
15
- const traceData = JSON.parse(
16
- decompressedData.toString(),
17
- ) as Trace.Types.File.TraceFile;
18
-
19
- const traceModel = Trace.TraceModel.Model.createWithAllHandlers({
20
- debugMode: true,
21
- enableAnimationsFrameHandler: false,
22
- maxInvalidationEventsPerEvent: 20,
23
- showAllEvents: false,
24
- });
25
- await traceModel.parse(traceData.traceEvents, {
26
- isCPUProfile: false,
27
- isFreshRecording: false,
28
- metadata: traceData.metadata,
29
- showAllEvents: false,
30
- });
31
- const parsedTrace = traceModel.parsedTrace(0)!;
32
- const parsedTraceData = parsedTrace.data;
33
-
34
- // --- Selector Stats ---
35
- const selectorStatsData = parsedTraceData.SelectorStats;
36
- const selectorMap = new Map<
37
- string,
38
- Trace.Types.Events.SelectorTiming
39
- >();
40
-
41
- for (const [, value] of selectorStatsData.dataForRecalcStyleEvent) {
42
- for (const timing of value.timings) {
43
- const key =
44
- timing[SelectorTimingsKey.Selector] +
45
- '_' +
46
- timing[SelectorTimingsKey.StyleSheetId];
47
- const existing = selectorMap.get(key);
48
- if (existing) {
49
- existing[SelectorTimingsKey.Elapsed] +=
50
- timing[SelectorTimingsKey.Elapsed];
51
- existing[SelectorTimingsKey.MatchAttempts] +=
52
- timing[SelectorTimingsKey.MatchAttempts];
53
- existing[SelectorTimingsKey.MatchCount] +=
54
- timing[SelectorTimingsKey.MatchCount];
55
- existing[SelectorTimingsKey.FastRejectCount] +=
56
- timing[SelectorTimingsKey.FastRejectCount];
57
- } else {
58
- selectorMap.set(key, { ...timing });
59
- }
60
- }
61
- }
62
-
63
- const allTimings = [...selectorMap.values()];
64
- const TOP_N = 15;
65
-
66
- // Top selectors by elapsed time
67
- const byElapsed = allTimings
68
- .sort(
69
- (a, b) =>
70
- b[SelectorTimingsKey.Elapsed] - a[SelectorTimingsKey.Elapsed],
71
- )
72
- .slice(0, TOP_N);
73
-
74
- console.log(`\n=== Top ${TOP_N} CSS Selectors by Elapsed Time ===\n`);
75
- for (const t of byElapsed) {
76
- const elapsedMs = (t[SelectorTimingsKey.Elapsed] / 1000).toFixed(2);
77
- console.log(
78
- ` ${elapsedMs}ms | attempts: ${t[SelectorTimingsKey.MatchAttempts]} | matches: ${t[SelectorTimingsKey.MatchCount]} | ${t[SelectorTimingsKey.Selector]}`,
79
- );
80
- }
81
-
82
- // Top selectors by match attempts
83
- const byAttempts = [...selectorMap.values()]
84
- .sort(
85
- (a, b) =>
86
- b[SelectorTimingsKey.MatchAttempts] -
87
- a[SelectorTimingsKey.MatchAttempts],
88
- )
89
- .slice(0, TOP_N);
90
-
91
- console.log(`\n=== Top ${TOP_N} CSS Selectors by Match Attempts ===\n`);
92
- for (const t of byAttempts) {
93
- const elapsedMs = (t[SelectorTimingsKey.Elapsed] / 1000).toFixed(2);
94
- console.log(
95
- ` ${t[SelectorTimingsKey.MatchAttempts]} attempts | ${elapsedMs}ms | matches: ${t[SelectorTimingsKey.MatchCount]} | ${t[SelectorTimingsKey.Selector]}`,
96
- );
97
- }
98
-
99
- // --- Invalidation Tracking ---
100
- const invalidatedNodes = selectorStatsData.invalidatedNodeList;
101
- console.log(
102
- `\n=== Invalidation Tracking (${invalidatedNodes.length} invalidated nodes) ===\n`,
103
- );
104
-
105
- // Aggregate selectors that caused invalidations
106
- const invalidationSelectorCounts = new Map<string, number>();
107
- for (const node of invalidatedNodes) {
108
- for (const sel of node.selectorList) {
109
- const count = invalidationSelectorCounts.get(sel.selector) ?? 0;
110
- invalidationSelectorCounts.set(sel.selector, count + 1);
111
- }
112
- }
113
-
114
- const topInvalidationSelectors = [...invalidationSelectorCounts.entries()]
115
- .sort((a, b) => b[1] - a[1])
116
- .slice(0, TOP_N);
117
-
118
- console.log(
119
- ` Top ${TOP_N} selectors causing invalidations:\n`,
120
- );
121
- for (const [selector, count] of topInvalidationSelectors) {
122
- console.log(` ${count}x | ${selector}`);
123
- }
124
-
125
- // Subtree vs single-node invalidations
126
- const subtreeCount = invalidatedNodes.filter((n) => n.subtree).length;
127
- console.log(
128
- `\n Subtree invalidations: ${subtreeCount} / ${invalidatedNodes.length} total`,
129
- );
130
-
131
- // --- Totals ---
132
- let totalElapsedUs = 0;
133
- let totalMatchAttempts = 0;
134
- let totalMatchCount = 0;
135
- for (const t of allTimings) {
136
- totalElapsedUs += t[SelectorTimingsKey.Elapsed];
137
- totalMatchAttempts += t[SelectorTimingsKey.MatchAttempts];
138
- totalMatchCount += t[SelectorTimingsKey.MatchCount];
139
- }
140
- console.log(`\n=== Totals ===\n`);
141
- console.log(` Unique selectors: ${allTimings.length}`);
142
- console.log(` Total elapsed: ${(totalElapsedUs / 1000).toFixed(2)}ms`);
143
- console.log(` Total match attempts: ${totalMatchAttempts}`);
144
- console.log(` Total match count: ${totalMatchCount}`);
145
- }
@@ -1,114 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as zlib from 'node:zlib';
3
-
4
- import {
5
- initDevToolsTracing,
6
- createSourceMapResolver,
7
- symbolicateTrace,
8
- Trace,
9
- } from '../';
10
-
11
- export async function run(tracePath: string) {
12
- initDevToolsTracing();
13
-
14
- const fileData = fs.readFileSync(tracePath);
15
- const decompressedData = tracePath.endsWith('.gz')
16
- ? zlib.gunzipSync(fileData)
17
- : fileData;
18
- const traceData = JSON.parse(
19
- decompressedData.toString(),
20
- ) as Trace.Types.File.TraceFile;
21
-
22
- const traceModel = Trace.TraceModel.Model.createWithAllHandlers();
23
- const resolveSourceMap = createSourceMapResolver({
24
- fetch: verboseFetch,
25
- });
26
-
27
- await traceModel.parse(traceData.traceEvents, {
28
- isCPUProfile: false,
29
- isFreshRecording: false,
30
- metadata: traceData.metadata,
31
- showAllEvents: false,
32
- resolveSourceMap,
33
- });
34
-
35
- const parsedTrace = traceModel.parsedTrace(0)!;
36
- const parsedTraceData = parsedTrace.data;
37
- const scripts = parsedTraceData.Scripts.scripts;
38
-
39
- const metadataSourceMaps = traceData.metadata?.sourceMaps?.length ?? 0;
40
- console.log(`Found ${scripts.length} scripts in trace`);
41
- console.log(`Source maps in trace metadata: ${metadataSourceMaps}\n`);
42
-
43
- // Diagnostic: show script properties to explain resolution results.
44
- let withUrl = 0;
45
- let withFrame = 0;
46
- let withSourceMapUrl = 0;
47
- let withElided = 0;
48
- for (const script of scripts) {
49
- if (script.url) withUrl++;
50
- if (script.frame) withFrame++;
51
- if (script.sourceMapUrl) withSourceMapUrl++;
52
- if (script.sourceMapUrlElided) withElided++;
53
- }
54
- console.log('Script breakdown:');
55
- console.log(` with url: ${withUrl}`);
56
- console.log(` with frame: ${withFrame}`);
57
- console.log(` with sourceMapUrl: ${withSourceMapUrl}`);
58
- console.log(` with sourceMapUrlElided: ${withElided}`);
59
- console.log(` (resolution requires url + frame + sourceMapUrl/elided)\n`);
60
-
61
- let resolvedCount = 0;
62
- for (const script of scripts) {
63
- if (!script.sourceMap) {
64
- continue;
65
- }
66
- resolvedCount++;
67
-
68
- const sourceMap = script.sourceMap;
69
- const sourceURLs = sourceMap.sourceURLs();
70
-
71
- console.log(
72
- ` ${script.url || script.scriptId} -> ${sourceURLs.length} sources, ${sourceMap.mappings().length} mappings`,
73
- );
74
- }
75
-
76
- console.log(
77
- `Resolved source maps for ${resolvedCount} of ${scripts.length} scripts\n`,
78
- );
79
-
80
- // Symbolicate the raw trace events in-place using resolved source maps.
81
- const result = symbolicateTrace(traceData.traceEvents, scripts);
82
- console.log(
83
- `Symbolicated ${result.symbolicatedFrames} frames across ${result.symbolicatedEvents} events`,
84
- );
85
-
86
- // Write the symbolicated trace to a new file.
87
- const outPath = tracePath.replace(/(\.(json|json\.gz))$/i, '.symbolicated$1');
88
- if (outPath === tracePath) {
89
- console.error(
90
- 'Could not determine output path (expected .json or .json.gz extension)',
91
- );
92
- process.exit(1);
93
- }
94
- const outputJson = JSON.stringify(traceData);
95
- if (outPath.endsWith('.gz')) {
96
- fs.writeFileSync(outPath, zlib.gzipSync(outputJson));
97
- } else {
98
- fs.writeFileSync(outPath, outputJson);
99
- }
100
- console.log(`Wrote symbolicated trace to ${outPath}`);
101
- }
102
-
103
- async function verboseFetch(url: string): Promise<Response> {
104
- console.log(`[sourcemap] fetching ${url}`);
105
- const response = await fetch(url);
106
- if (!response.ok) {
107
- console.warn(
108
- `[sourcemap] ${url} -> ${response.status} ${response.statusText}`,
109
- );
110
- } else {
111
- console.log(`[sourcemap] ${url} -> ok`);
112
- }
113
- return response;
114
- }
package/commands/stats.ts DELETED
@@ -1,58 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as zlib from 'node:zlib';
3
-
4
- import {
5
- initDevToolsTracing,
6
- entryIsVisibleInTimeline,
7
- statsForTimeRange,
8
- Trace,
9
- } from '../';
10
-
11
- export async function run(tracePath: string) {
12
- initDevToolsTracing();
13
-
14
- const fileData = fs.readFileSync(tracePath);
15
- const decompressedData = tracePath.endsWith('.gz')
16
- ? zlib.gunzipSync(fileData)
17
- : fileData;
18
- const traceData = JSON.parse(
19
- decompressedData.toString(),
20
- ) as Trace.Types.File.TraceFile;
21
-
22
- const traceModel = Trace.TraceModel.Model.createWithAllHandlers({
23
- debugMode: true,
24
- enableAnimationsFrameHandler: false,
25
- maxInvalidationEventsPerEvent: 20,
26
- showAllEvents: false,
27
- });
28
- await traceModel.parse(traceData.traceEvents, {
29
- isCPUProfile: false,
30
- isFreshRecording: false,
31
- metadata: traceData.metadata,
32
- showAllEvents: false,
33
- });
34
- const parsedTrace = traceModel.parsedTrace(0)!;
35
- const parsedTraceData = parsedTrace!.data!;
36
-
37
- const startTime = Trace.Helpers.Timing.microToMilli(
38
- parsedTraceData.Meta.traceBounds.min,
39
- );
40
- const endTime = Trace.Helpers.Timing.microToMilli(
41
- parsedTraceData.Meta.traceBounds.max,
42
- );
43
-
44
- const threads = Trace.Handlers.Threads.threadsInTrace(parsedTrace.data);
45
- const mainThread = threads.find(
46
- (t) => t.type === Trace.Handlers.Threads.ThreadType.MAIN_THREAD,
47
- );
48
- if (!mainThread) {
49
- throw new Error('No renderer main thread found in trace file');
50
- }
51
-
52
- const rendererEvents = [...mainThread.entries].filter((e) =>
53
- entryIsVisibleInTimeline(e, parsedTrace),
54
- );
55
- const stats = statsForTimeRange(rendererEvents, startTime, endTime);
56
-
57
- console.log(stats);
58
- }