| 75 | // socket via the `sessionId` field and can drive Profiler.* on the wedged VM |
| 76 | // worker (V8 samples it out-of-process even while it spins synchronously). |
| 77 | async function startWorkerProfiler() { |
| 78 | const ver = await (await fetch(`http://127.0.0.1:${remoteDebugPort}/json/version`)).json(); |
| 79 | const ws = new WebSocket(ver.webSocketDebuggerUrl); |
| 80 | await new Promise((res, rej) => { ws.onopen = res; ws.onerror = rej; }); |
| 81 | |
| 82 | let nextId = 1; |
| 83 | const pending = new Map(); |
| 84 | let workerSessionId = null; |
| 85 | const ready = { resolve: null }; |
| 86 | const workerReady = new Promise(r => { ready.resolve = r; }); |
| 87 | |
| 88 | ws.onmessage = (ev) => { |
| 89 | const msg = JSON.parse(ev.data); |
| 90 | if (msg.id && pending.has(msg.id)) { |
| 91 | const { resolve, reject } = pending.get(msg.id); |
| 92 | pending.delete(msg.id); |
| 93 | msg.error ? reject(new Error(JSON.stringify(msg.error))) : resolve(msg.result); |
| 94 | return; |
| 95 | } |
| 96 | if (msg.method === 'Target.attachedToTarget') { |
| 97 | const t = msg.params.targetInfo; |
| 98 | if (t.type === 'page') { |
| 99 | send(msg.params.sessionId, 'Target.setAutoAttach', |
| 100 | { autoAttach: true, flatten: true, waitForDebuggerOnStart: false }); |
| 101 | } else if ((t.type === 'worker' || t.type === 'dedicated_worker') && !workerSessionId) { |
| 102 | workerSessionId = msg.params.sessionId; |
| 103 | ready.resolve(); |
| 104 | } |
| 105 | } |
| 106 | }; |
| 107 | |
| 108 | function send(sessionId, method, params) { |
| 109 | const id = nextId++; |
| 110 | const payload = { id, method, params: params || {} }; |
| 111 | if (sessionId) payload.sessionId = sessionId; |
| 112 | return new Promise((resolve, reject) => { |
| 113 | pending.set(id, { resolve, reject }); |
| 114 | ws.send(JSON.stringify(payload)); |
| 115 | }); |
| 116 | } |
| 117 | |
| 118 | await send(null, 'Target.setAutoAttach', |
| 119 | { autoAttach: true, flatten: true, waitForDebuggerOnStart: false }); |
| 120 | append(`profiler:cdp-connected port=${remoteDebugPort}`); |
| 121 | |
| 122 | return { |
| 123 | async begin() { |
| 124 | await workerReady; |
| 125 | await send(workerSessionId, 'Profiler.enable'); |
| 126 | await send(workerSessionId, 'Profiler.setSamplingInterval', { interval: 150 }); |
| 127 | await send(workerSessionId, 'Profiler.start'); |
| 128 | append(`profiler:started workerSession=${workerSessionId}`); |
| 129 | }, |
| 130 | async stop() { |
| 131 | if (!workerSessionId) return null; |
| 132 | const { profile } = await send(workerSessionId, 'Profiler.stop'); |
| 133 | return profile; |
| 134 | }, |