@kylincloud/flamegraph 0.36.2 → 0.36.3
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/CHANGELOG.md +7 -0
- package/dist/FlameGraph/FlameGraphComponent/index.d.ts.map +1 -1
- package/dist/flamegraphRenderWorker.js +1 -1
- package/dist/flamegraphRenderWorker.js.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +4 -4
- package/dist/index.esm.js.map +1 -1
- package/dist/index.node.cjs.js +2 -2
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.esm.js +1 -1
- package/dist/index.node.esm.js.map +1 -1
- package/dist/workers/createFlamegraphRenderWorker.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/FlameGraph/FlameGraphComponent/index.tsx +32 -6
- package/src/workers/createFlamegraphRenderWorker.ts +18 -2
- package/src/workers/flamegraphRenderWorker.ts +75 -45
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createFlamegraphRenderWorker.d.ts","sourceRoot":"","sources":["../../src/workers/createFlamegraphRenderWorker.ts"],"names":[],"mappings":"AAAA,wBAAgB,4BAA4B,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"createFlamegraphRenderWorker.d.ts","sourceRoot":"","sources":["../../src/workers/createFlamegraphRenderWorker.ts"],"names":[],"mappings":"AAAA,wBAAgB,4BAA4B,IAAI,MAAM,CAsCrD"}
|
package/package.json
CHANGED
|
@@ -93,6 +93,16 @@ export default function FlameGraphComponent(props: FlamegraphProps) {
|
|
|
93
93
|
typeof window !== 'undefined' &&
|
|
94
94
|
typeof OffscreenCanvas !== 'undefined' &&
|
|
95
95
|
'transferControlToOffscreen' in HTMLCanvasElement.prototype;
|
|
96
|
+
const [enableWorker, setEnableWorker] = React.useState(useRenderWorker);
|
|
97
|
+
const workerStatusLoggedRef = useRef(false);
|
|
98
|
+
|
|
99
|
+
React.useEffect(() => {
|
|
100
|
+
if (typeof window === 'undefined' || workerStatusLoggedRef.current) return;
|
|
101
|
+
workerStatusLoggedRef.current = true;
|
|
102
|
+
if (!useRenderWorker) {
|
|
103
|
+
console.info('[flamegraph] OffscreenCanvas unavailable, using main thread renderer');
|
|
104
|
+
}
|
|
105
|
+
}, [useRenderWorker]);
|
|
96
106
|
|
|
97
107
|
// ====== 新增:提取 canvas 渲染需要的 i18n messages ======
|
|
98
108
|
const canvasMessages = useMemo(
|
|
@@ -153,10 +163,24 @@ export default function FlameGraphComponent(props: FlamegraphProps) {
|
|
|
153
163
|
);
|
|
154
164
|
|
|
155
165
|
useEffect(() => {
|
|
156
|
-
if (!
|
|
166
|
+
if (!enableWorker) {
|
|
157
167
|
return () => {};
|
|
158
168
|
}
|
|
159
|
-
|
|
169
|
+
let worker: Worker;
|
|
170
|
+
try {
|
|
171
|
+
worker = createFlamegraphRenderWorker();
|
|
172
|
+
} catch (err) {
|
|
173
|
+
console.warn('[flamegraph] render worker init failed, fallback to main thread', err);
|
|
174
|
+
setEnableWorker(false);
|
|
175
|
+
return () => {};
|
|
176
|
+
}
|
|
177
|
+
console.info('[flamegraph] render worker created');
|
|
178
|
+
worker.addEventListener('error', (event) => {
|
|
179
|
+
console.error('[flamegraph] render worker error', event);
|
|
180
|
+
});
|
|
181
|
+
worker.addEventListener('messageerror', (event) => {
|
|
182
|
+
console.error('[flamegraph] render worker message error', event);
|
|
183
|
+
});
|
|
160
184
|
renderWorkerRef.current = worker;
|
|
161
185
|
setTimeout(() => {
|
|
162
186
|
renderRectCanvas();
|
|
@@ -169,7 +193,7 @@ export default function FlameGraphComponent(props: FlamegraphProps) {
|
|
|
169
193
|
textOffscreenRef.current = null;
|
|
170
194
|
workerReadyRef.current = { rect: false, text: false };
|
|
171
195
|
};
|
|
172
|
-
}, [
|
|
196
|
+
}, [enableWorker]);
|
|
173
197
|
|
|
174
198
|
useResizeObserver(canvasRef, () => {
|
|
175
199
|
if (flamegraph) {
|
|
@@ -400,7 +424,7 @@ export default function FlameGraphComponent(props: FlamegraphProps) {
|
|
|
400
424
|
};
|
|
401
425
|
|
|
402
426
|
const ensureRenderWorkerCanvases = useCallback(() => {
|
|
403
|
-
if (!
|
|
427
|
+
if (!enableWorker || !renderWorkerRef.current) {
|
|
404
428
|
return false;
|
|
405
429
|
}
|
|
406
430
|
const worker = renderWorkerRef.current;
|
|
@@ -415,6 +439,7 @@ export default function FlameGraphComponent(props: FlamegraphProps) {
|
|
|
415
439
|
);
|
|
416
440
|
workerReadyRef.current.rect = true;
|
|
417
441
|
} catch (err) {
|
|
442
|
+
console.error('[flamegraph] offscreen init failed (rect)', err);
|
|
418
443
|
}
|
|
419
444
|
}
|
|
420
445
|
|
|
@@ -428,11 +453,12 @@ export default function FlameGraphComponent(props: FlamegraphProps) {
|
|
|
428
453
|
);
|
|
429
454
|
workerReadyRef.current.text = true;
|
|
430
455
|
} catch (err) {
|
|
456
|
+
console.error('[flamegraph] offscreen init failed (text)', err);
|
|
431
457
|
}
|
|
432
458
|
}
|
|
433
459
|
|
|
434
460
|
return true;
|
|
435
|
-
}, [
|
|
461
|
+
}, [enableWorker]);
|
|
436
462
|
|
|
437
463
|
// ====== 修改:添加 canvasMessages 依赖 ======
|
|
438
464
|
React.useEffect(() => {
|
|
@@ -463,7 +489,7 @@ export default function FlameGraphComponent(props: FlamegraphProps) {
|
|
|
463
489
|
kind: 'rect' | 'text',
|
|
464
490
|
options: { renderRects?: boolean; renderText?: boolean }
|
|
465
491
|
) => {
|
|
466
|
-
if (!
|
|
492
|
+
if (!enableWorker) {
|
|
467
493
|
return false;
|
|
468
494
|
}
|
|
469
495
|
if (!renderWorkerRef.current) {
|
|
@@ -11,7 +11,13 @@ export function createFlamegraphRenderWorker(): Worker {
|
|
|
11
11
|
'../../@kylincloud/flamegraph/dist/flamegraphRenderWorker.js',
|
|
12
12
|
metaUrl
|
|
13
13
|
);
|
|
14
|
-
|
|
14
|
+
try {
|
|
15
|
+
const worker = new Worker(viteDepsWorkerUrl, { type: 'module' });
|
|
16
|
+
return worker;
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.error('[flamegraph] worker create failed (vite deps)', err);
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
const workerUrl = isFromSrc
|
|
@@ -19,5 +25,15 @@ export function createFlamegraphRenderWorker(): Worker {
|
|
|
19
25
|
: isFromDist
|
|
20
26
|
? new URL('./flamegraphRenderWorker.js', metaUrl)
|
|
21
27
|
: new URL('./flamegraphRenderWorker.js', metaUrl);
|
|
22
|
-
|
|
28
|
+
try {
|
|
29
|
+
const worker = new Worker(workerUrl, { type: 'module' });
|
|
30
|
+
return worker;
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.error('[flamegraph] worker create failed', err, {
|
|
33
|
+
workerUrl: workerUrl.href,
|
|
34
|
+
isFromSrc,
|
|
35
|
+
isFromDist,
|
|
36
|
+
});
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
23
39
|
}
|
|
@@ -83,6 +83,14 @@ const state: {
|
|
|
83
83
|
},
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
+
const logWorkerError = (err: any, context: string) => {
|
|
87
|
+
try {
|
|
88
|
+
console.error('[flamegraph-worker] error', context, err);
|
|
89
|
+
} catch {
|
|
90
|
+
// ignore
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
86
94
|
const buildPalette = (payload: SerializablePalette): FlamegraphPalette => ({
|
|
87
95
|
name: payload.name,
|
|
88
96
|
goodColor: Color.rgb(...payload.goodColor),
|
|
@@ -100,53 +108,63 @@ const RECT_BUDGET_MS = 12;
|
|
|
100
108
|
const TEXT_BUDGET_MS = 8;
|
|
101
109
|
|
|
102
110
|
const runChunk = (kind: 'rect' | 'text', token: number) => {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
renderState.flamegraph
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
111
|
+
try {
|
|
112
|
+
const renderState = state.renderState[kind];
|
|
113
|
+
const payload = renderState.payload;
|
|
114
|
+
if (!payload || renderState.token !== token) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const canvas = kind === 'rect' ? state.rectCanvas : state.textCanvas;
|
|
118
|
+
if (!canvas) {
|
|
119
|
+
renderState.running = false;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!renderState.flamegraph) {
|
|
123
|
+
renderState.flamegraph = new Flamegraph(
|
|
124
|
+
payload.flamebearer,
|
|
125
|
+
canvas,
|
|
126
|
+
toMaybe(payload.focusedNode),
|
|
127
|
+
payload.fitMode,
|
|
128
|
+
payload.highlightQuery,
|
|
129
|
+
toMaybe(payload.zoom),
|
|
130
|
+
buildPalette(payload.palette),
|
|
131
|
+
payload.messages
|
|
132
|
+
);
|
|
133
|
+
}
|
|
125
134
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
135
|
+
const result = renderState.flamegraph.render({
|
|
136
|
+
renderRects: payload.renderRects,
|
|
137
|
+
renderText: payload.renderText,
|
|
138
|
+
timeBudgetMs: kind === 'rect' ? RECT_BUDGET_MS : TEXT_BUDGET_MS,
|
|
139
|
+
startI: renderState.nextI,
|
|
140
|
+
startJ: renderState.nextJ,
|
|
141
|
+
skipCanvasResize: !renderState.firstChunk,
|
|
142
|
+
skipDprScale: !renderState.firstChunk,
|
|
143
|
+
devicePixelRatio: payload.devicePixelRatio,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
renderState.firstChunk = false;
|
|
147
|
+
if (!result.done) {
|
|
148
|
+
renderState.nextI = result.nextI;
|
|
149
|
+
renderState.nextJ = result.nextJ;
|
|
150
|
+
setTimeout(() => runChunk(kind, token), 0);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
144
153
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
renderState.running = false;
|
|
155
|
+
renderState.nextI = 0;
|
|
156
|
+
renderState.nextJ = 0;
|
|
157
|
+
renderState.firstChunk = true;
|
|
158
|
+
renderState.flamegraph = null;
|
|
159
|
+
} catch (err) {
|
|
160
|
+
console.error('[flamegraph-worker] render error', err);
|
|
161
|
+
const renderState = state.renderState[kind];
|
|
162
|
+
renderState.running = false;
|
|
163
|
+
renderState.nextI = 0;
|
|
164
|
+
renderState.nextJ = 0;
|
|
165
|
+
renderState.firstChunk = true;
|
|
166
|
+
renderState.flamegraph = null;
|
|
167
|
+
}
|
|
150
168
|
};
|
|
151
169
|
|
|
152
170
|
const renderFlamegraph = (payload: FlamegraphRenderRequest['payload']) => {
|
|
@@ -190,3 +208,15 @@ self.onmessage = (event: MessageEvent<FlamegraphWorkerMessage>) => {
|
|
|
190
208
|
renderFlamegraph(msg.payload);
|
|
191
209
|
}
|
|
192
210
|
};
|
|
211
|
+
|
|
212
|
+
self.onerror = (event) => {
|
|
213
|
+
logWorkerError(event, 'self.error');
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
self.onmessageerror = (event) => {
|
|
217
|
+
logWorkerError(event, 'self.messageerror');
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
self.addEventListener('unhandledrejection', (event: PromiseRejectionEvent) => {
|
|
221
|
+
logWorkerError(event?.reason, 'self.unhandledrejection');
|
|
222
|
+
});
|