-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgeneric-trace-to-devtools-trace.mjs
122 lines (102 loc) · 3.68 KB
/
generic-trace-to-devtools-trace.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Take a trace captured from chrome://tracing or perfetto (but converted to json)…
// And convert it to a trace that DevTools can load as first-class. (not falling back to isGenericTrace handling)
import {loadTraceEventsFromFile, saveTrace} from './trace-file-utils.mjs';
const passedArg = process.argv[2];
const tracefilename = passedArg ? passedArg : './myjansatta.json';
/** @type {TraceEvent[]} */
const events = loadTraceEventsFromFile(tracefilename);
console.log(events.length);
let minTs = Infinity;
const threads = [];
const pidToThreadLookup = {};
const eventToThread = new Map();
class Thread {
constructor(pid, tid, thread_name) {
this.pid = pid;
this.tid = tid;
this.thread_name = thread_name;
this.events = [];
this.label;
this.process_name;
threads.push(this);
pidToThreadLookup[pid] = pidToThreadLookup[pid] ?? {};
pidToThreadLookup[pid][tid] = this;
}
}
// metadataevents are always at the start
for (const e of events.slice(0, 1000)) {
if (e.ph === 'M' && e.name === 'thread_name') {
const thread =
threads.find(t => t.tid === e.tid && t.pid === e.pid) ??
new Thread(e.pid, e.tid, e.args.name);
eventToThread.set(e, thread);
}
if (e.ph === 'M' && e.name === 'process_name') {
threads.filter(t => t.pid === e.pid).forEach(t => (t.process_name = e.args.name));
}
}
// Loop over all. assign events to threads. collect minTs
for (const e of events) {
if (e.ph === 'M' && e.name === 'process_labels') {
const thread = pidToThreadLookup[e.pid][e.tid];
thread.label = e.args.labels;
}
if (e.ph === 'M') continue; // dont attempt with metadata
const thread = pidToThreadLookup[e.pid][e.tid];
if (!thread) throw new Error(`no thread for ${e.pid} ${e.tid}`);
thread.events.push(e);
if (e.ts !== 0) {
minTs = Math.min(minTs, e.ts);
}
}
// Find busiest main thread and frame
const rendererMains = threads.filter(
t => t.thread_name === 'CrRendererMain' && t.process_name === 'Renderer'
);
rendererMains.sort((aThread, bThread) => bThread.events.length - aThread.events.length);
const busiestMainThread = rendererMains.at(0);
const frameToCount = new Map();
busiestMainThread.events.forEach(e => {
const frame = e.args.frame ?? e.args.data?.frame;
if (!frame || typeof frame !== 'string') return;
let count = frameToCount.get(frame) ?? 0;
count++;
frameToCount.set(frame, count);
});
const busiestFrame = Array.from(frameToCount.entries())
.sort(([aFrame, aCount], [bBrame, bCount]) => bCount - aCount)
.at(0);
const busiestFrameId = busiestFrame?.at(0) ?? 'NOFRAMEIDFOUND';
/**
* @return {LH.TraceEvent}
*/
function createFakeTracingStartedInPage() {
// TODO: migrate to TracingStartedInBrowser cuz this one is old.
return {
pid: busiestMainThread.pid,
tid: busiestMainThread.tid,
cat: 'devtools.timeline',
ts: minTs,
ph: 'I',
s: 't',
cat: 'disabled-by-default-devtools.timeline',
name: 'TracingStartedInPage',
args: {
data: {
frameTreeNodeId: 1,
page: busiestFrameId,
persistentIds: true,
frames: [
{frame: busiestFrameId, url: 'https://sdflkdsf.com', name: busiestMainThread.label, processId: busiestMainThread.pid},
],
},
},
dur: 0,
};
}
// startedinpage is LEGACY behavior but... i need it right now cuz the inbrowser path aint working. and im too lazy to figure out why
events.push(createFakeTracingStartedInPage());
// events.push({...createFakeTracingStartedInPage(), name: 'TracingStartedInBrowser'});
// events.sort((a, b) => a.ts - b.ts); // apparently this still works even with startedinpage is at the end.
await saveTrace({traceEvents: events}, `${tracefilename}.dt.json`);
console.log('done');