From 3d30385d30e86095ff1113d5cc025e9008194baf Mon Sep 17 00:00:00 2001 From: Suzy Mueller Date: Mon, 8 Mar 2021 10:30:18 -0500 Subject: [PATCH] src/goDebugFactory: wait for dlv dap server to start in factory When we start dlv dap, we want to make sure it has started before sending the info back to vscode so it will be able to connect. Wait for the 'DAP server listening at' message before returning the host. Display the error for the user otherwise. Fixes golang/vscode-go#1270 Change-Id: Id710b67ceaa87b1f6dff84d8108ac61dfbe15707 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/300071 Trust: Suzy Mueller Run-TryBot: Suzy Mueller TryBot-Result: kokoro Reviewed-by: Hyang-Ah Hana Kim --- src/goDebugFactory.ts | 89 +++++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/src/goDebugFactory.ts b/src/goDebugFactory.ts index c6b8e1652d..5ebdef0d08 100644 --- a/src/goDebugFactory.ts +++ b/src/goDebugFactory.ts @@ -68,14 +68,11 @@ export async function startDapServer( } else { configuration.port = await getPort(); } - const dlvDapServer = spawnDlvDapServerProcess(configuration); - // Wait to give dlv-dap a chance to start before returning. - return await new Promise<{ port: number; host: string; dlvDapServer: ChildProcessWithoutNullStreams }>((resolve) => - setTimeout(() => resolve({ port: configuration.port, host: configuration.host, dlvDapServer }), 500) - ); + const dlvDapServer = await spawnDlvDapServerProcess(configuration); + return { dlvDapServer, port: configuration.port, host: configuration.host }; } -function spawnDlvDapServerProcess(launchArgs: DebugConfiguration) { +async function spawnDlvDapServerProcess(launchArgs: DebugConfiguration): Promise { const launchArgsEnv = launchArgs.env || {}; const env = Object.assign({}, process.env, launchArgsEnv); @@ -114,30 +111,66 @@ function spawnDlvDapServerProcess(launchArgs: DebugConfiguration) { appendToDebugConsole(`Running: ${dlvPath} ${dlvArgs.join(' ')}`); const dir = parseProgramArgSync(launchArgs).dirname; - const p = spawn(dlvPath, dlvArgs, { - cwd: dir, - env - }); - p.stderr.on('data', (chunk) => { - appendToDebugConsole(chunk.toString()); - }); - p.stdout.on('data', (chunk) => { - appendToDebugConsole(chunk.toString()); - }); - p.on('close', (code) => { - if (code) { - appendToDebugConsole(`Process exiting with code: ${code} signal: ${p.killed}`); - } else { - appendToDebugConsole(`Process exited normally: ${p.killed}`); - } - }); - p.on('error', (err) => { - if (err) { - appendToDebugConsole(`Error: ${err}`); - } + return await new Promise((resolve, reject) => { + const p = spawn(dlvPath, dlvArgs, { + cwd: dir, + env + }); + let started = false; + const timeoutToken: NodeJS.Timer = setTimeout( + () => reject(new Error('timed out while waiting for DAP server to start')), + 5_000 + ); + + const stopWaitingForServerToStart = (err?: string) => { + clearTimeout(timeoutToken); + started = true; + if (err) { + killProcessTree(p); // We do not need to wait for p to actually be killed. + reject(new Error(err)); + } else { + resolve(p); + } + }; + + p.stdout.on('data', (chunk) => { + if (!started) { + if (chunk.toString().startsWith('DAP server listening at:')) { + stopWaitingForServerToStart(); + } else { + stopWaitingForServerToStart( + `Expected 'DAP server listening at:' from debug adapter got '${chunk.toString()}'` + ); + } + } + appendToDebugConsole(chunk.toString()); + }); + p.stderr.on('data', (chunk) => { + if (!started) { + stopWaitingForServerToStart(`Unexpected error from dlv dap on start: '${chunk.toString()}'`); + } + appendToDebugConsole(chunk.toString()); + }); + p.on('close', (code) => { + if (!started) { + stopWaitingForServerToStart(`dlv dap closed with code: '${code}' signal: ${p.killed}`); + } + if (code) { + appendToDebugConsole(`Process exiting with code: ${code} signal: ${p.killed}`); + } else { + appendToDebugConsole(`Process exited normally: ${p.killed}`); + } + }); + p.on('error', (err) => { + if (!started) { + stopWaitingForServerToStart(`Unexpected error from dlv dap on start: '${err}'`); + } + if (err) { + appendToDebugConsole(`Error: ${err}`); + } + }); }); - return p; } function parseProgramArgSync(