in src/goDebugFactory.ts [479:599]
function spawnDlvDapServerProcess(
launchAttachArgs: vscode.DebugConfiguration,
host: string,
port: number,
log: (msg: string) => void,
logErr: (msg: string) => void,
logConsole: (msg: string) => void
): Promise<ChildProcess> {
const { dlvArgs, dlvPath, dir, env } = getSpawnConfig(launchAttachArgs, logErr);
// env does not include process.env. Construct the new env for process spawning
// by combining process.env.
const envForSpawn = env ? Object.assign({}, process.env, env) : undefined;
dlvArgs.push(`--listen=${host}:${port}`);
const onWindows = process.platform === 'win32';
if (!onWindows) {
dlvArgs.push('--log-dest=3');
}
const logDest = launchAttachArgs.logDest;
if (typeof logDest === 'number') {
logErr(`Using a file descriptor for 'logDest' (${logDest}) is not allowed.\n`);
throw new Error('Using a file descriptor for `logDest` is not allowed.');
}
if (logDest && !path.isAbsolute(logDest)) {
logErr(
`Using a relative path for 'logDest' (${logDest}) is not allowed.\nSee https://code.visualstudio.com/docs/editor/variables-reference if you want workspace-relative path.\n`
);
throw new Error('Using a relative path for `logDest` is not allowed');
}
if (logDest && onWindows) {
logErr(
'Using `logDest` or `--log-dest` is not supported on windows yet. See https://github.com/golang/vscode-go/issues/1472.'
);
throw new Error('Using `logDest` on windows is not allowed');
}
const logDestStream = logDest ? fs.createWriteStream(logDest) : undefined;
logConsole(`Starting: ${dlvPath} ${dlvArgs.join(' ')} from ${dir}\n`);
// TODO(hyangah): In module-module workspace mode, the program should be build in the super module directory
// where go.work (gopls.mod) file is present. Where dlv runs determines the build directory currently. Two options:
// 1) launch dlv in the super-module module directory and adjust launchArgs.cwd (--wd).
// 2) introduce a new buildDir launch attribute.
return new Promise<ChildProcess>((resolve, reject) => {
const p = spawn(dlvPath, dlvArgs, {
cwd: dir,
env: envForSpawn,
stdio: onWindows ? ['pipe', 'pipe', 'pipe'] : ['pipe', 'pipe', 'pipe', 'pipe'] // --log-dest=3 if !onWindows.
});
let started = false;
const timeoutToken: NodeJS.Timer = setTimeout(() => {
logConsole(`Delve DAP server (PID: ${p.pid}) is not responding`);
reject(new Error('timed out while waiting for DAP server to start'));
}, 30_000);
const stopWaitingForServerToStart = () => {
clearTimeout(timeoutToken);
started = true;
resolve(p);
};
p.stdout.on('data', (chunk) => {
const msg = chunk.toString();
if (!started && msg.startsWith('DAP server listening at:')) {
stopWaitingForServerToStart();
}
log(msg);
});
p.stderr.on('data', (chunk) => {
logErr(chunk.toString());
});
p.stdio[3]?.on('data', (chunk) => {
const msg = chunk.toString();
if (!started && msg.startsWith('DAP server listening at:')) {
stopWaitingForServerToStart();
}
if (logDestStream) {
// always false on windows.
// write to the specified file.
logDestStream?.write(chunk, (err) => {
if (err) {
logConsole(`Error writing to ${logDest}: ${err}, log may be incomplete.`);
}
});
} else {
logConsole(msg);
}
});
p.stdio[3]?.on('close', () => {
// always false on windows.
logDestStream?.end();
});
p.on('close', (code, signal) => {
// TODO: should we watch 'exit' instead?
// NOTE: log messages here may not appear in DEBUG CONSOLE if the termination of
// the process was triggered by debug adapter's dispose when dlv dap doesn't
// respond to disconnect on time. In that case, it's possible that the session
// is in the middle of teardown and DEBUG CONSOLE isn't accessible. Check
// Go Debug output channel.
if (typeof code === 'number') {
// The process exited on its own.
logConsole(`dlv dap (${p.pid}) exited with code: ${code}\n`);
} else if (code === null && signal) {
logConsole(`dlv dap (${p.pid}) was killed by signal: ${signal}\n`);
} else {
logConsole(`dlv dap (${p.pid}) terminated with code: ${code} signal: ${signal}\n`);
}
});
p.on('error', (err) => {
if (err) {
logConsole(`Error: ${err}\n`);
}
});
});
}