https://github.com/royitaqi created 
https://github.com/llvm/llvm-project/pull/159481

This helps lldb-dap (the binary) development. For example, after rebuilding the 
lldb-dap binary, one probably wants to restart the server so that it will use 
the newly built binary instead of a stale one.

With this patch, the modification time will be appended to the internal "spawn 
info" variable, which is displayed when prompting the user about restarting the 
server.

See screenshot below.
<img width="520" height="372" alt="screenshot" 
src="https://github.com/user-attachments/assets/79da55c8-28d3-4f17-b228-3ee22733a3a0";
 />


>From bb9541a27f618326abb1968c0d89313cd38dbbde Mon Sep 17 00:00:00 2001
From: Roy Shi <[email protected]>
Date: Wed, 17 Sep 2025 16:26:06 -0700
Subject: [PATCH] [vscode-lldb] Restart server when lldb-dap's modification
 time has changed

---
 lldb/tools/lldb-dap/.vscode/launch.json       | 10 +--
 lldb/tools/lldb-dap/package-lock.json         | 66 +++++++++++++++++++
 lldb/tools/lldb-dap/package.json              |  4 ++
 lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts | 31 ++++++---
 4 files changed, 97 insertions(+), 14 deletions(-)

diff --git a/lldb/tools/lldb-dap/.vscode/launch.json 
b/lldb/tools/lldb-dap/.vscode/launch.json
index 8241a5aca0354..524fc2dd5ce7b 100644
--- a/lldb/tools/lldb-dap/.vscode/launch.json
+++ b/lldb/tools/lldb-dap/.vscode/launch.json
@@ -14,11 +14,11 @@
                        ],
                        "outFiles": [
                                "${workspaceFolder}/out/**/*.js"
-                       ],
-                       "preLaunchTask": {
-                               "type": "npm",
-                               "script": "watch"
-                       }
+                       ]
+                       // "preLaunchTask": {
+                       //      "type": "npm",
+                       //      "script": "watch"
+                       // }
                }
        ]
 }
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<string[]> {
     return [
       path,
       ...args,
       ...Object.entries(env ?? {}).map(
         (entry) => String(entry[0]) + "=" + String(entry[1]),
       ),
+      `(${await this.getFileModifiedTimestamp(path)})`,
     ];
   }
+
+  private async getFileModifiedTimestamp(file: string): Promise<string | null> 
{
+    try {
+      if (!(await fs.pathExists(file))) {
+        return null;
+      }
+      return (await fs.promises.stat(file)).mtime.toLocaleString();
+    } catch (error) {
+      return null;
+    }
+  }
 }

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to