https://github.com/award999 created https://github.com/llvm/llvm-project/pull/146884
- Add `winston` dependency (MIT license) to handle logging setup - Have an `OutputChannel` to log user facing information, errors, warnings - Write a verose log under the provided `logUri` to capture further diagnostics - Introduce `verboseLogging` setting to increase `OutputChannel` verbosity and write DAP session logs Issue: #146880 >From 87be6e74be1348e4cd162cec6e58a2370a11cafd Mon Sep 17 00:00:00 2001 From: Adam Ward <award...@apple.com> Date: Thu, 3 Jul 2025 09:13:41 -0400 Subject: [PATCH] Logging setup for lldb-dap extension - Add `winston` dependency (MIT license) to handle logging setup - Have an `OutputChannel` to log user facing information, errors, warnings - Write a verose log under the provided `logUri` to capture further diagnostics - Introduce `verboseLogging` setting to increase `OutputChannel` verbosity and write DAP session logs Issue: #146880 --- lldb/tools/lldb-dap/package-lock.json | 242 +++++++++++++++++- lldb/tools/lldb-dap/package.json | 9 +- .../lldb-dap/src-ts/debug-adapter-factory.ts | 37 ++- .../src-ts/debug-configuration-provider.ts | 11 +- .../lldb-dap/src-ts/debug-session-tracker.ts | 13 +- lldb/tools/lldb-dap/src-ts/extension.ts | 21 +- lldb/tools/lldb-dap/src-ts/logger.ts | 88 +++++++ 7 files changed, 404 insertions(+), 17 deletions(-) create mode 100644 lldb/tools/lldb-dap/src-ts/logger.ts diff --git a/lldb/tools/lldb-dap/package-lock.json b/lldb/tools/lldb-dap/package-lock.json index af90a9573aee6..f9f071ae7e41a 100644 --- a/lldb/tools/lldb-dap/package-lock.json +++ b/lldb/tools/lldb-dap/package-lock.json @@ -15,7 +15,9 @@ "@vscode/vsce": "^3.2.2", "prettier": "^3.4.2", "prettier-plugin-curly": "^0.3.1", - "typescript": "^5.7.3" + "typescript": "^5.7.3", + "winston": "^3.17.0", + "winston-transport": "^4.9.0" }, "engines": { "vscode": "^1.75.0" @@ -318,6 +320,28 @@ "node": ">=6.9.0" } }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -399,6 +423,13 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/vscode": { "version": "1.75.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.75.0.tgz", @@ -642,6 +673,13 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -858,6 +896,17 @@ "node": ">=16" } }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -873,6 +922,28 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1114,6 +1185,13 @@ "dev": true, "license": "MIT" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true, + "license": "MIT" + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1207,6 +1285,20 @@ "pend": "~1.2.0" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true, + "license": "MIT" + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -1494,8 +1586,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "optional": true + "dev": true }, "node_modules/ini": { "version": "1.3.8", @@ -1504,6 +1595,13 @@ "dev": true, "optional": true }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -1530,6 +1628,19 @@ "node": ">=8" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -1673,6 +1784,13 @@ "prebuild-install": "^7.0.1" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true, + "license": "MIT" + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -1741,6 +1859,24 @@ "dev": true, "license": "MIT" }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1951,6 +2087,16 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -2200,7 +2346,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -2230,6 +2375,16 @@ } ] }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/sax": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", @@ -2410,6 +2565,26 @@ "simple-concat": "^1.0.0" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/stoppable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", @@ -2426,7 +2601,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, - "optional": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -2587,6 +2761,13 @@ "node": ">=6" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -2597,6 +2778,16 @@ "node": ">=14.14" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -2683,8 +2874,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "optional": true + "dev": true }, "node_modules/uuid": { "version": "8.3.2", @@ -2712,6 +2902,44 @@ "node": ">= 8" } }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index b150dee792c34..9e1439d1f797f 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -34,7 +34,9 @@ "@vscode/vsce": "^3.2.2", "prettier": "^3.4.2", "prettier-plugin-curly": "^0.3.1", - "typescript": "^5.7.3" + "typescript": "^5.7.3", + "winston": "^3.17.0", + "winston-transport": "^4.9.0" }, "activationEvents": [ "onDebug", @@ -110,6 +112,11 @@ "additionalProperties": { "type": "string" } + }, + "lldb-dap.verboseLogging": { + "type": "boolean", + "description": "Enable verbose logging to the \"LLDB-DAP\" output channel for the running debug session. Also will write LLDB-DAP session logs to the Extension's log folder if the `lldb-dap.log-path` setting is not explicitly set.", + "default": false } } }, diff --git a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts index b5db45b56d6a6..81460da8a2323 100644 --- a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts +++ b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts @@ -5,6 +5,7 @@ import * as child_process from "child_process"; import * as fs from "node:fs/promises"; import { ConfigureButton, OpenSettingsButton } from "./ui/show-error-message"; import { ErrorWithNotification } from "./ui/error-with-notification"; +import { Logger } from "./logger"; const exec = util.promisify(child_process.execFile); @@ -156,16 +157,34 @@ async function getDAPArguments( .get<string[]>("arguments", []); } +/** + * Formats the given date as a string in the form "YYYYMMdd". + * + * @param date The date to format as a string. + * @returns The formatted date. + */ +function formatDate(date: Date): string { + const year = date.getFullYear().toString().padStart(4, "0"); + const month = (date.getMonth() + 1).toString().padStart(2, "0"); + const day = date.getDate().toString().padStart(2, "0"); + const hour = date.getHours().toString().padStart(2, "0"); + const minute = date.getMinutes().toString().padStart(2, "0"); + const seconds = date.getSeconds().toString().padStart(2, "0"); + return year + month + day + hour + minute + seconds; +} + /** * Creates a new {@link vscode.DebugAdapterExecutable} based on the provided workspace folder and * debug configuration. Assumes that the given debug configuration is for a local launch of lldb-dap. * + * @param logger The {@link Logger} to get default session log location * @param workspaceFolder The {@link vscode.WorkspaceFolder} that the debug session will be launched within * @param configuration The {@link vscode.DebugConfiguration} that will be launched * @throws An {@link ErrorWithNotification} if something went wrong * @returns The {@link vscode.DebugAdapterExecutable} that can be used to launch lldb-dap */ export async function createDebugAdapterExecutable( + logger: Logger, workspaceFolder: vscode.WorkspaceFolder | undefined, configuration: vscode.DebugConfiguration, ): Promise<vscode.DebugAdapterExecutable> { @@ -174,6 +193,8 @@ export async function createDebugAdapterExecutable( let env: { [key: string]: string } = {}; if (log_path) { env["LLDBDAP_LOG"] = log_path; + } else if (vscode.workspace.getConfiguration("lldb-dap").get("verboseLogging", false)) { + env["LLDBDAP_LOG"] = logger.logFilePath(`lldb-dap-session-${formatDate(new Date())}.log`); } const configEnvironment = config.get<{ [key: string]: string }>("environment") || {}; @@ -188,6 +209,11 @@ export async function createDebugAdapterExecutable( }; const dbgArgs = await getDAPArguments(workspaceFolder, configuration); + logger.info(`lldb-dap path: ${dapPath}`); + logger.info(`lldb-dap args: ${dbgArgs}`); + logger.info(`cwd: ${dbgOptions.cwd}`); + logger.info(`env: ${JSON.stringify(configEnvironment)}`); + return new vscode.DebugAdapterExecutable(dapPath, dbgArgs, dbgOptions); } @@ -198,18 +224,26 @@ export async function createDebugAdapterExecutable( export class LLDBDapDescriptorFactory implements vscode.DebugAdapterDescriptorFactory { + constructor(private readonly logger: Logger) {} + async createDebugAdapterDescriptor( session: vscode.DebugSession, executable: vscode.DebugAdapterExecutable | undefined, ): Promise<vscode.DebugAdapterDescriptor | undefined> { + this.logger.info(`Creating debug adapter for session "${session.name}"`); + this.logger.debug(`Session "${session.name}" debug configuration:`); + this.logger.debug(JSON.stringify(session.configuration, undefined, 2)); if (executable) { - throw new Error( + const error = new Error( "Setting the debug adapter executable in the package.json is not supported.", ); + this.logger.error(error); + throw error; } // Use a server connection if the debugAdapterPort is provided if (session.configuration.debugAdapterPort) { + this.logger.info(`Spawning debug adapter server on port ${session.configuration.debugAdapterPort}`); return new vscode.DebugAdapterServer( session.configuration.debugAdapterPort, session.configuration.debugAdapterHostname, @@ -217,6 +251,7 @@ export class LLDBDapDescriptorFactory } return createDebugAdapterExecutable( + this.logger, session.workspaceFolder, session.configuration, ); diff --git a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts index 316ffaf47c3d2..65a63a85c9036 100644 --- a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts +++ b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts @@ -5,6 +5,7 @@ import { LLDBDapServer } from "./lldb-dap-server"; import { createDebugAdapterExecutable } from "./debug-adapter-factory"; import { ConfigureButton, showErrorMessage } from "./ui/show-error-message"; import { ErrorWithNotification } from "./ui/error-with-notification"; +import { Logger } from "./logger"; const exec = util.promisify(child_process.execFile); @@ -71,13 +72,16 @@ const configurations: Record<string, DefaultConfig> = { export class LLDBDapConfigurationProvider implements vscode.DebugConfigurationProvider { - constructor(private readonly server: LLDBDapServer) {} + constructor(private readonly server: LLDBDapServer, private readonly logger: Logger) {} async resolveDebugConfiguration( folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, token?: vscode.CancellationToken, ): Promise<vscode.DebugConfiguration> { + this.logger.info(`Resolving debug configuration for "${debugConfiguration.name}"`); + this.logger.debug("Initial debug configuration:"); + this.logger.debug(JSON.stringify(debugConfiguration, undefined, 2)); let config = vscode.workspace.getConfiguration("lldb-dap"); for (const [key, cfg] of Object.entries(configurations)) { if (Reflect.has(debugConfiguration, key)) { @@ -152,6 +156,7 @@ export class LLDBDapConfigurationProvider // Always try to create the debug adapter executable as this will show the user errors // if there are any. const executable = await createDebugAdapterExecutable( + this.logger, folder, debugConfiguration, ); @@ -184,8 +189,12 @@ export class LLDBDapConfigurationProvider } } + this.logger.debug("Resolved debug configuration:"); + this.logger.debug(JSON.stringify(debugConfiguration, undefined, 2)); + return debugConfiguration; } catch (error) { + this.logger.error(error); // Show a better error message to the user if possible if (!(error instanceof ErrorWithNotification)) { throw error; diff --git a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts index 50db1e1c3a7b0..a5424b6ce4ae8 100644 --- a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts +++ b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts @@ -1,10 +1,12 @@ import { DebugProtocol } from "@vscode/debugprotocol"; import * as vscode from "vscode"; +import { Logger } from "./logger"; /** A helper type for mapping event types to their corresponding data type. */ // prettier-ignore interface EventMap { "module": DebugProtocol.ModuleEvent; + "exited": DebugProtocol.ExitedEvent; } /** A type assertion to check if a ProtocolMessage is an event or if it is a specific event. */ @@ -47,7 +49,7 @@ export class DebugSessionTracker onDidChangeModules: vscode.Event<vscode.DebugSession | undefined> = this.modulesChanged.event; - constructor() { + constructor(private logger: Logger) { this.onDidChangeModules(this.moduleChangedListener, this); vscode.debug.onDidChangeActiveDebugSession((session) => this.modulesChanged.fire(session), @@ -62,8 +64,12 @@ export class DebugSessionTracker createDebugAdapterTracker( session: vscode.DebugSession, ): vscode.ProviderResult<vscode.DebugAdapterTracker> { + this.logger.info(`Starting debug session "${session.name}"`); + let stopping = false; return { + onError: (error) => !stopping && this.logger.error(error), // Can throw benign read errors when shutting down onDidSendMessage: (message) => this.onDidSendMessage(session, message), + onWillStopSession: () => stopping = true, onExit: () => this.onExit(session), }; } @@ -134,6 +140,11 @@ export class DebugSessionTracker } this.modules.set(session, modules); this.modulesChanged.fire(session); + } else if (isEvent(message, "exited")) { + // The vscode.DebugAdapterTracker#onExit event is sometimes called with + // exitCode = undefined but the exit event from LLDB-DAP always has the "exitCode" + const { exitCode } = message.body; + this.logger.info(`Session "${session.name}" exited with code ${exitCode}`); } } } diff --git a/lldb/tools/lldb-dap/src-ts/extension.ts b/lldb/tools/lldb-dap/src-ts/extension.ts index c8e5146e29cea..fe5f3a0100907 100644 --- a/lldb/tools/lldb-dap/src-ts/extension.ts +++ b/lldb/tools/lldb-dap/src-ts/extension.ts @@ -1,3 +1,4 @@ +import * as path from "path"; import * as vscode from "vscode"; import { LLDBDapDescriptorFactory } from "./debug-adapter-factory"; @@ -10,28 +11,31 @@ import { ModulesDataProvider, ModuleProperty, } from "./ui/modules-data-provider"; +import { Logger } from "./logger"; /** * This class represents the extension and manages its life cycle. Other extensions * using it as as library should use this class as the main entry point. */ export class LLDBDapExtension extends DisposableContext { - constructor() { + constructor(logger: Logger, outputChannel: vscode.OutputChannel) { super(); const lldbDapServer = new LLDBDapServer(); - const sessionTracker = new DebugSessionTracker(); + const sessionTracker = new DebugSessionTracker(logger); this.pushSubscription( + logger, + outputChannel, lldbDapServer, sessionTracker, vscode.debug.registerDebugConfigurationProvider( "lldb-dap", - new LLDBDapConfigurationProvider(lldbDapServer), + new LLDBDapConfigurationProvider(lldbDapServer, logger), ), vscode.debug.registerDebugAdapterDescriptorFactory( "lldb-dap", - new LLDBDapDescriptorFactory(), + new LLDBDapDescriptorFactory(logger), ), vscode.debug.registerDebugAdapterTrackerFactory( "lldb-dap", @@ -54,6 +58,11 @@ export class LLDBDapExtension extends DisposableContext { /** * This is the entry point when initialized by VS Code. */ -export function activate(context: vscode.ExtensionContext) { - context.subscriptions.push(new LLDBDapExtension()); +export async function activate(context: vscode.ExtensionContext) { + await vscode.workspace.fs.createDirectory(context.logUri); + const outputChannel = vscode.window.createOutputChannel("LLDB-DAP"); + const logger = new Logger((name) => path.join(context.logUri.fsPath, name), outputChannel); + logger.info("LLDB-Dap extension activating..."); + context.subscriptions.push(new LLDBDapExtension(logger, outputChannel)); + logger.info("LLDB-Dap extension activated"); } diff --git a/lldb/tools/lldb-dap/src-ts/logger.ts b/lldb/tools/lldb-dap/src-ts/logger.ts new file mode 100644 index 0000000000000..15da25ff0e28d --- /dev/null +++ b/lldb/tools/lldb-dap/src-ts/logger.ts @@ -0,0 +1,88 @@ +import * as vscode from "vscode"; +import * as winston from "winston"; +import * as Transport from "winston-transport"; + +class OutputChannelTransport extends Transport { + constructor(private readonly ouptutChannel: vscode.OutputChannel) { + super(); + } + + public log(info: any, next: () => void): void { + this.ouptutChannel.appendLine(info[Symbol.for('message')]); + next(); + } +} + +export class Logger implements vscode.Disposable { + private disposables: vscode.Disposable[] = []; + private logger: winston.Logger; + + constructor(public readonly logFilePath: (name: string) => string, ouptutChannel: vscode.OutputChannel) { + const ouptutChannelTransport = new OutputChannelTransport(ouptutChannel); + ouptutChannelTransport.level = this.outputChannelLevel(); + this.logger = winston.createLogger({ + transports: [ + new winston.transports.File({ filename: logFilePath("lldb-dap-extension.log"), level: "debug" }), // File logging at the 'debug' level + ouptutChannelTransport + ], + format: winston.format.combine( + winston.format.errors({ stack: true }), + winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), + winston.format.printf(msg => `[${msg.timestamp}][${msg.level}] ${msg.message} ${msg.stack ? msg.stack : ''}`), + ), + }); + if (process.env.NODE_ENV !== 'production') { + this.logger.add(new winston.transports.Console({ + level: "error" + })); + } + this.disposables.push( + { + dispose: () => this.logger.close() + }, + vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration("lldb-dap.verboseLogging")) { + ouptutChannelTransport.level = this.outputChannelLevel(); + } + }) + ); + } + + debug(message: any) { + this.logger.debug(this.normalizeMessage(message)); + } + + info(message: any) { + this.logger.info(this.normalizeMessage(message)); + } + + warn(message: any) { + this.logger.warn(this.normalizeMessage(message)); + } + + error(message: any) { + if (message instanceof Error) { + this.logger.error(message); + return; + } + this.logger.error(this.normalizeMessage(message)); + } + + private normalizeMessage(message: any) { + if (typeof message === "string") { + return message; + } else if (typeof message === "object") { + return JSON.stringify(message); + } + return `${message}`; + } + + private outputChannelLevel(): string { + return vscode.workspace.getConfiguration("lldb-dap").get("verboseLogging", false) ? + "debug" : "info"; + } + + dispose() { + this.disposables.forEach(d => d.dispose()); + } +} \ No newline at end of file _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits