diff --git a/lldb/tools/lldb-dap/package-lock.json b/lldb/tools/lldb-dap/package-lock.json index 26db1ce6df2fd..92143f7f37268 100644 --- a/lldb/tools/lldb-dap/package-lock.json +++ b/lldb/tools/lldb-dap/package-lock.json @@ -8,7 +8,11 @@ "name": "lldb-dap", "version": "0.2.16", "license": "Apache 2.0 License with LLVM exceptions", + "dependencies": { + "fs-extra": "^11.3.2" + }, "devDependencies": { + "@types/fs-extra": "^11.0.4", "@types/node": "^18.19.41", "@types/tabulator-tables": "^6.2.10", "@types/vscode": "1.75.0", @@ -835,6 +839,27 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "18.19.75", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.75.tgz", @@ -1748,6 +1773,20 @@ "dev": true, "optional": true }, + "node_modules/fs-extra": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", + "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1877,6 +1916,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2094,6 +2139,18 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -3182,6 +3239,15 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index 6566ba3bdee13..3633e292a6264 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -27,7 +27,11 @@ "categories": [ "Debuggers" ], + "dependencies": { + "fs-extra": "^11.0.4" + }, "devDependencies": { + "@types/fs-extra": "^11.0.4", "@types/node": "^18.19.41", "@types/tabulator-tables": "^6.2.10", "@types/vscode": "1.75.0", diff --git a/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts b/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts index 774be50053a17..59dd8a7a6b250 100644 --- a/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts +++ b/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts @@ -1,3 +1,4 @@ +import * as fs from 'fs-extra'; import * as child_process from "node:child_process"; import { isDeepStrictEqual } from "util"; import * as vscode from "vscode"; @@ -54,7 +55,7 @@ export class LLDBDapServer implements vscode.Disposable { return this.serverInfo; } - this.serverInfo = new Promise((resolve, reject) => { + this.serverInfo = new Promise(async (resolve, reject) => { const process = child_process.spawn(dapPath, dapArgs, options); process.on("error", (error) => { reject(error); @@ -82,7 +83,7 @@ export class LLDBDapServer implements vscode.Disposable { } }); this.serverProcess = process; - this.serverSpawnInfo = this.getSpawnInfo(dapPath, dapArgs, options?.env); + this.serverSpawnInfo = await this.getSpawnInfo(dapPath, dapArgs, options?.env); }); return this.serverInfo; } @@ -104,13 +105,13 @@ export class LLDBDapServer implements vscode.Disposable { return true; } - const newSpawnInfo = this.getSpawnInfo(dapPath, args, env); + const newSpawnInfo = await this.getSpawnInfo(dapPath, args, env); if (isDeepStrictEqual(this.serverSpawnInfo, newSpawnInfo)) { return true; } const userInput = await vscode.window.showInformationMessage( - "The arguments to lldb-dap have changed. Would you like to restart the server?", + "The lldb-dap binary and/or the arguments to it have changed. Would you like to restart the server?", { modal: true, detail: `An existing lldb-dap server (${this.serverProcess.pid}) is running with different arguments. @@ -131,8 +132,7 @@ Restarting the server will interrupt any existing debug sessions and start a new switch (userInput) { case "Restart": this.serverProcess.kill(); - this.serverProcess = undefined; - this.serverInfo = undefined; + this.cleanUp(this.serverProcess); return true; case "Use Existing": return true; @@ -149,27 +149,40 @@ Restarting the server will interrupt any existing debug sessions and start a new this.cleanUp(this.serverProcess); } - cleanUp(process: child_process.ChildProcessWithoutNullStreams) { + private cleanUp(process: child_process.ChildProcessWithoutNullStreams) { // If the following don't equal, then the fields have already been updated // (either a new process has started, or the fields were already cleaned // up), and so the cleanup should be skipped. if (this.serverProcess === process) { this.serverProcess = undefined; this.serverInfo = undefined; + this.serverSpawnInfo = undefined; } } - getSpawnInfo( + private async getSpawnInfo( path: string, args: string[], env: NodeJS.ProcessEnv | { [key: string]: string } | undefined, - ): string[] { + ): Promise { return [ path, ...args, ...Object.entries(env ?? {}).map( (entry) => String(entry[0]) + "=" + String(entry[1]), ), + `(${await this.getFileModifiedTimestamp(path)})`, ]; } + + private async getFileModifiedTimestamp(file: string): Promise { + try { + if (!(await fs.pathExists(file))) { + return null; + } + return (await fs.promises.stat(file)).mtime.toLocaleString(); + } catch (error) { + return null; + } + } }