-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.web.tsx
138 lines (117 loc) · 3.07 KB
/
index.web.tsx
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
type ProfilerOptions = {
sampleInterval: number;
maxBufferSize: number;
};
type Profiler = {
new (options: ProfilerOptions): Profiler;
stop(): Promise<ProfilingTrace>;
};
declare global {
const Profiler: Profiler;
interface Window {
Profiler: Profiler;
}
}
type Sample = {
stackId?: number;
timestamp: number;
};
type Stack = {
frameId: number;
parentId?: number;
};
type Frame = {
name: string;
line?: number;
column?: number;
resourceId?: number;
};
type ProfilingTrace = {
frames: Frame[];
resources: string[];
samples: Sample[];
stacks: Stack[];
};
const NOT_STARTED_PROFILER = {
stop: (): Promise<ProfilingTrace> => {
console.warn('The profiler has not been started');
return Promise.resolve({
frames: [],
resources: [],
samples: [],
stacks: [],
});
},
};
let profiler = NOT_STARTED_PROFILER;
const SAMPLING_INTERVAL = 10;
const MAX_BUFFER_SIZE = 10000;
const MICRO_SECONDS_IN_MILLISECONDS = 1000;
export function startProfiling(): boolean {
if (typeof window.Profiler !== 'function') {
console.warn(
'Profiler is not available in this browser. Ignoring this profiler run...'
);
return false;
}
profiler = new Profiler({
sampleInterval: SAMPLING_INTERVAL,
maxBufferSize: MAX_BUFFER_SIZE,
});
return true;
}
export async function stopProfiling(
saveToDownloads = false,
fileName = `trace-${new Date().toISOString()}.cpuprofile.txt`
): Promise<string> {
const trace = await profiler.stop();
profiler = NOT_STARTED_PROFILER;
const hermesProfiler = convertToHermesProfilerFormat(trace);
downloadJsonFile(hermesProfiler, fileName);
if (!saveToDownloads) {
console.warn(
'Specifying `saveToDownloads=false` is not supported on web, as downloading is the only one possible way to store the trace file'
);
}
return fileName;
}
function downloadJsonFile(
exportObj: Record<string, unknown>,
exportName: string
) {
const dataStr =
'data:text/json;charset=utf-8,' +
encodeURIComponent(JSON.stringify(exportObj));
const downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute('href', dataStr);
downloadAnchorNode.setAttribute('download', exportName);
document.body.appendChild(downloadAnchorNode); // required for Firefox
downloadAnchorNode.click();
downloadAnchorNode.remove();
}
function convertToHermesProfilerFormat(profilingTrace: ProfilingTrace) {
const { frames, resources, samples, stacks } = profilingTrace;
return {
traceEvents: [],
samples: samples.map((sample) => ({
cpu: '-1',
name: '',
ts: sample.timestamp * MICRO_SECONDS_IN_MILLISECONDS,
pid: 1,
tid: '1',
weight: '1',
sf: sample.stackId ?? 1,
})),
stackFrames: stacks.map((stack) => {
const resourceId = frames[stack.frameId]?.resourceId;
return {
parent: stack.parentId,
category: 'JavaScript',
...frames[stack.frameId],
name: `${frames[stack.frameId]?.name}(${
resourceId ? resources[resourceId] : null
})`,
};
}),
};
}