From 0e86ed2cc7e67f423a25421627dad33a89f4a2a2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 1 Aug 2018 14:35:48 +0800 Subject: [PATCH] Add a troubleshooting page for the frequently asked errors (#341) * Add a troubleshooting page for the frequently asked errors * Update the wrong link and remove the unnecessary separator * Update Troubleshooting.md * Make tslint happy * Refine the words per review * Add the missing items parameter for showErrorMessage function * Log the troubleshooting usage * Add a reference in README to link to troubleshooting page * Update the fix steps for illegal request type error * Refine the description words for differnt errors * Add jdk troubleshooting item * Add a link to submit an issue * Add compilation as a try step for ClassNotFoundException * Add the missing license header * Resolve the review comments for troubleshooting doc * Extract dup code to a helper function * Merge activation event to usage data * Refactor showMessage utility method * Add opn to dependency list * update package-lock.json --- README.md | 3 ++ Troubleshooting.md | 85 ++++++++++++++++++++++++++++++++++++ package-lock.json | 13 ++++++ package.json | 1 + src/configurationProvider.ts | 73 ++++++++++++++++--------------- src/constants.ts | 3 ++ src/extension.ts | 68 ++++++++++++++--------------- src/hotCodeReplace.ts | 7 +-- src/logger.ts | 48 ++++++++++++++++++++ src/utility.ts | 74 +++++++++++++++++++++++++++++++ 10 files changed, 300 insertions(+), 75 deletions(-) create mode 100644 Troubleshooting.md create mode 100644 src/logger.ts create mode 100644 src/utility.ts diff --git a/README.md b/README.md index a60e610..174bb4b 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,9 @@ Please also check the documentation of [Language Support for Java by Red Hat](ht - `java.debug.settings.maxStringLength`: the maximum length of string displayed in "Variables" or "Debug Console" viewlet, the string longer than this length will be trimmed, defaults to `0` which means no trim is performed. - `java.debug.settings.enableHotCodeReplace`: enable hot code replace for Java code. Make sure the auto build is not disabled for [VSCode Java](https://github.com/redhat-developer/vscode-java). See the [wiki page](https://github.com/Microsoft/vscode-java-debug/wiki/Hot-Code-Replace) for more information about usages and limitations. +## Troubleshooting +Reference the [troubleshooting guide](https://github.com/Microsoft/vscode-java-debug/blob/master/Troubleshooting.md) for common errors. + ## Feedback and Questions You can find the full list of issues at [Issue Tracker](https://github.com/Microsoft/vscode-java-debug/issues). You can submit a [bug or feature suggestion](https://github.com/Microsoft/vscode-java-debug/issues/new), and participate community driven [![Gitter](https://badges.gitter.im/Microsoft/vscode-java-debug.svg)](https://gitter.im/Microsoft/vscode-java-debug) diff --git a/Troubleshooting.md b/Troubleshooting.md new file mode 100644 index 0000000..d1418cb --- /dev/null +++ b/Troubleshooting.md @@ -0,0 +1,85 @@ +# Troubleshooting + +This document provides the information needed to troubleshoot common errors of Debugger for Java (the debugger). If it does not cover the problem you are seeing, please [log an issue](https://github.com/Microsoft/vscode-java-debug/issues) instead. + +## Java Language Support extension fails to start. +The debugger works with [Language Support for Java(TM) by Red Hat](https://marketplace.visualstudio.com/items?itemName=redhat.java) (the language server) for source mapping and project support. If the language server fails to start, the debugger will not work as expected. Here is a simple way to check whether the language server is started. Open a .java or a Java project folder in VS Code, and then check the icon at the right side of the status bar. You should see the 👍 icon if the language server is loaded correctly. + + ![status indicator](https://raw.githubusercontent.com/redhat-developer/vscode-java/master/images/statusMarker.png). + +### Try: +1. If you get the error *"The JAVA_HOME environment variable points to a missing folder"* or *"Java runtime could not be located"*, please make sure that the environment variable JAVA_HOME points to a valid JDK. Otherwise, ignore this step. +2. Run VS Code command *"Java: Update project configuration"* to force the language server to reload the current project. +3. Try cleaning the stale workspace cache. Quit all VS Code processes and clean the following cache directory: +- Windows : %APPDATA%\Code\User\workspaceStorage\ +- MacOS : $HOME/Library/Application Support/Code/User/workspaceStorage/ +- Linux : $HOME/.config/Code/User/workspaceStorage/ +4. Try more [troubleshooting guide](https://github.com/redhat-developer/vscode-java/wiki/Troubleshooting) from the language server product site. + +## Failed to resolve classpath: +### Reason: +In launch mode, the debugger resolves the classpaths automatically based on the given `mainClass` and `projectName`. It looks for the class specified by `mainClass` as the entry point for launching an application. If there are multiple classes with the same name in the current workspace, the debugger uses the one inside the project specified by `projectName`. + +### Try: +1. Check whether the class name specified in `mainClass` exists and is in the right form. The debugger only works with fully qualified class names, e.g. `org.microsoft.app.Main`. +2. Check whether the `projectName` is correct. The actual project name is not always the same to the folder name you see in the File Explorer. Please check the value specified by `projectDescription/name` in the *.project* file, or the `artificatId` in the *pom.xml* for maven project, or the folder name for gradle project. +3. If the problem persists, please try to use the debugger to regenerate the debug configurations in *launch.json*. Remove the existing *launch.json* file and press F5. The debugger will automatically generate a new *launch.json* with the right debug configurations. + +## Request type "xyz" is not supported. Only "launch" and "attach" are supported. +### Reason: +The value specified in `request` option of *launch.json* is incorrect. + +### Try: +1. Reference the VS Code official document [launch configurations](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations) about how to configure *launch.json*. +2. Try to use the debugger to regenerate the debug configurations in *launch.json*. Remove the existing *launch.json* file and press F5. The debugger will automatically generate a new *launch.json* with the right debug configurations. + +## Failed to complete hot code replace: +### Reason: +This error indicates you are doing `Hot Code Replace`. The `Hot Code Replace` feature depends on the underlying JVM implementation. If you get this error, that indicates the new changes cannot be hot replaced by JVM. + +### Try: +1. Restart your application to apply the new changes. Or ignore the message, and continue to debug. +2. You can disable the hot code replace feature by changing the user setting `"java.debug.settings.enableHotCodeReplace": false`. + +## Please specify the host name and the port of the remote debuggee in the launch.json. +### Reason: +This error indicates you are debugging a remote Java application. The reason is that you don't configure the remote machine's host name and debug port correctly. + +### Try: +1. Check whether the remote Java application is launched in debug mode. The typical command to enable debug mode is like *"java -agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n -classpath \ MyMainClass"*, where the parameter *"address=5005"* represents the target JVM exposes *5005* as the debug port. +2. Check the debug port is not blocked by the remote machine's firewall. + +## Failed to evaluate. Reason: Cannot evaluate because the thread is resumed. +### Reason: +There are two possible reasons for this error. +- Reason 1: you try to evaluate an expression when the target thread is running. Evaluation only works when your program is on suspend, for example, stopping at a breakpoint or stepping in/out/over. +- Reason 2: you take the VS Code DEBUG CONSOLE view for program input by mistake. DEBUG CONSOLE only accepts input for evaluation, not for program console input. + +### Try: +1. For Reason 1, try to add a breakpoint and stop your program there, then evaluate the expression. +2. For Reason 2, try to change the `console` option in the *launch.json* to `externalTerminal` or `integratedTerminal`. This is the official solution for program console input. + +## The DEBUG CONSOLE throws Error: Could not find or load main class xyz +### Reason: +You configure the incorrect main class name in `mainClass` of *launch.json*. + +### Try: +1. Check whether the class name specified in `mainClass` exists and is in the right form. +2. If the problem persists, it's probably because the language server doesn't load your project correctly. Please reference *"Java Language Support extension fails to start"* paragraph for more troubleshooting info. + +## The DEBUG CONSOLE throws ClassNotFoundException +### Reason: +This error indicates your application attempts to reference some classes which are not found in the entire classpaths. + +### Try: +1. Check whether you configure the required libraries in the dependency settings file (e.g. *pom.xml*). +2. Run VS Code command *"Java: Force Java compilation"* to force the language server to rebuild the current project. +3. If the problem persists, it's probably because the language server doesn't load your project correctly. Please reference *"Java Language Support extension fails to start"* paragraph for more troubleshooting info. + +## Cannot find a class with the main method +### Reason: +When the `mainClass` is unconfigured in the *launch.json*, the debugger will resolve a class with main method automatically. This error indicates the debugger doesn't find any main class in the whole workspace. + +### Try: +1. Check at least one main class exists in your workspace. +2. If no main class exists, please create a main class first. Otherwise, it's probably because the language server fails to start. Please reference *"Java Language Support extension fails to start"* paragraph for more troubleshooting info. diff --git a/package-lock.json b/package-lock.json index 5a6c23b..5853434 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1742,6 +1742,11 @@ "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", "dev": true }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -2301,6 +2306,14 @@ "wrappy": "1" } }, + "opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "requires": { + "is-wsl": "1.1.0" + } + }, "orchestrator": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", diff --git a/package.json b/package.json index 0d8d177..4f0f8ab 100644 --- a/package.json +++ b/package.json @@ -384,6 +384,7 @@ "vscode": "^1.1.6" }, "dependencies": { + "opn": "^5.3.0", "vscode-extension-telemetry": "0.0.18" } } diff --git a/src/configurationProvider.ts b/src/configurationProvider.ts index 2ba8c48..26390f3 100644 --- a/src/configurationProvider.ts +++ b/src/configurationProvider.ts @@ -2,12 +2,13 @@ // Licensed under the MIT license. import * as vscode from "vscode"; -import TelemetryReporter from "vscode-extension-telemetry"; import * as commands from "./commands"; +import { logger, Type } from "./logger"; +import * as utility from "./utility"; export class JavaDebugConfigurationProvider implements vscode.DebugConfigurationProvider { private isUserSettingsDirty: boolean = true; - constructor(private _reporter: TelemetryReporter) { + constructor() { vscode.workspace.onDidChangeConfiguration((event) => { if (vscode.debug.activeDebugSession) { this.isUserSettingsDirty = false; @@ -132,25 +133,28 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration config.classPaths = result[1]; } if (this.isEmptyArray(config.classPaths) && this.isEmptyArray(config.modulePaths)) { - const hintMessage = "Cannot resolve the modulepaths/classpaths automatically, please specify the value in the launch.json."; - vscode.window.showErrorMessage(hintMessage); - this.log("usageError", hintMessage); + utility.showErrorMessageWithTroubleshooting({ + message: "Cannot resolve the modulepaths/classpaths automatically, please specify the value in the launch.json.", + type: Type.USAGEERROR, + }); return undefined; } } else if (config.request === "attach") { if (!config.hostName || !config.port) { - vscode.window.showErrorMessage("Please specify the host name and the port of the remote debuggee in the launch.json."); - this.log("usageError", "Please specify the host name and the port of the remote debuggee in the launch.json."); + utility.showErrorMessageWithTroubleshooting({ + message: "Please specify the host name and the port of the remote debuggee in the launch.json.", + type: Type.USAGEERROR, + }); return undefined; } } else { - const ans = await vscode.window.showErrorMessage( - // tslint:disable-next-line:max-line-length - "Request type \"" + config.request + "\" is not supported. Only \"launch\" and \"attach\" are supported.", "Open launch.json"); + const ans = await utility.showErrorMessageWithTroubleshooting({ + message: `Request type "${config.request}" is not supported. Only "launch" and "attach" are supported.`, + type: Type.USAGEERROR, + }, "Open launch.json"); if (ans === "Open launch.json") { await vscode.commands.executeCommand(commands.VSCODE_ADD_DEBUGCONFIGURATION); } - this.log("usageError", "Illegal request type in launch.json"); return undefined; } const debugServerPort = await startDebugSession(); @@ -158,40 +162,35 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration config.debugServer = debugServerPort; return config; } else { - this.log("exception", "Failed to start debug server."); + logger.logMessage(Type.EXCEPTION, "Failed to start debug server."); // Information for diagnostic: console.log("Cannot find a port for debugging session"); return undefined; } } catch (ex) { const errorMessage = (ex && ex.message) || ex; - vscode.window.showErrorMessage(String(errorMessage)); - if (this._reporter) { - const exception = (ex && ex.data && ex.data.cause) - || { stackTrace: [], detailMessage: String((ex && ex.message) || ex || "Unknown exception") }; - const properties = { - message: "", - stackTrace: "", - }; - if (exception && typeof exception === "object") { - properties.message = exception.detailMessage; - properties.stackTrace = (Array.isArray(exception.stackTrace) && JSON.stringify(exception.stackTrace)) - || String(exception.stackTrace); - } else { - properties.message = String(exception); - } - this._reporter.sendTelemetryEvent("exception", properties); + const exception = (ex && ex.data && ex.data.cause) + || { stackTrace: [], detailMessage: String((ex && ex.message) || ex || "Unknown exception") }; + const properties = { + message: "", + stackTrace: "", + }; + if (exception && typeof exception === "object") { + properties.message = exception.detailMessage; + properties.stackTrace = (Array.isArray(exception.stackTrace) && JSON.stringify(exception.stackTrace)) + || String(exception.stackTrace); + } else { + properties.message = String(exception); } + utility.showErrorMessageWithTroubleshooting({ + message: String(errorMessage), + type: Type.EXCEPTION, + details: properties, + }); return undefined; } } - private log(type: string, message: string) { - if (this._reporter) { - this._reporter.sendTelemetryEvent(type, { message }); - } - } - private isEmptyArray(configItems: any): boolean { return !Array.isArray(configItems) || !configItems.length; } @@ -199,8 +198,10 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration private async chooseMainClass(folder: vscode.WorkspaceFolder | undefined): Promise { const res = await resolveMainClass(folder ? folder.uri : undefined); if (res.length === 0) { - vscode.window.showErrorMessage( - "Cannot find a class with the main method."); + utility.showErrorMessageWithTroubleshooting({ + message: "Cannot find a class with the main method.", + type: Type.USAGEERROR, + }); return undefined; } const pickItems = res.map((item) => { diff --git a/src/constants.ts b/src/constants.ts index 99e5185..7e0e21a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + export const JAVA_LANGID: string = "java"; export const HCR_EVENT = "hotcodereplace"; export const USER_NOTIFICATION_EVENT = "usernotification"; diff --git a/src/extension.ts b/src/extension.ts index 9addecb..720c387 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,49 +3,41 @@ import * as path from "path"; import * as vscode from "vscode"; -import TelemetryReporter from "vscode-extension-telemetry"; import * as commands from "./commands"; import { JavaDebugConfigurationProvider } from "./configurationProvider"; import { HCR_EVENT, JAVA_LANGID, USER_NOTIFICATION_EVENT } from "./constants"; import { handleHotCodeReplaceCustomEvent, initializeHotCodeReplace } from "./hotCodeReplace"; +import { logger, Type } from "./logger"; +import * as utility from "./utility"; export function activate(context: vscode.ExtensionContext) { - // The reporter will be initialized by the later telemetry handler. - let reporter: TelemetryReporter = null; + logger.initialize(context); + logger.log(Type.ACTIVATEEXTENSION, {}); // TODO: Activation belongs to usage data, remove this line. + logger.log(Type.USAGEDATA, { + description: "activateExtension", + }); - // Telemetry. - const extensionPackage = require(context.asAbsolutePath("./package.json")); - if (extensionPackage) { - const packageInfo = { - name: extensionPackage.name, - version: extensionPackage.version, - aiKey: extensionPackage.aiKey, - }; - if (packageInfo.aiKey) { - reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); - reporter.sendTelemetryEvent("activateExtension", {}); - const measureKeys = ["duration"]; - vscode.debug.onDidTerminateDebugSession(() => { - fetchUsageData().then((ret) => { - if (Array.isArray(ret) && ret.length) { - ret.forEach((entry) => { - const commonProperties: any = {}; - const measureProperties: any = {}; - for (const key of Object.keys(entry)) { - if (measureKeys.indexOf(key) >= 0) { - measureProperties[key] = entry[key]; - } else { - commonProperties[key] = String(entry[key]); - } - } - reporter.sendTelemetryEvent(entry.scope === "exception" ? "exception" : "usageData", commonProperties, measureProperties); - }); + const measureKeys = ["duration"]; + vscode.debug.onDidTerminateDebugSession(() => { + fetchUsageData().then((ret) => { + if (Array.isArray(ret) && ret.length) { + ret.forEach((entry) => { + const commonProperties: any = {}; + const measureProperties: any = {}; + for (const key of Object.keys(entry)) { + if (measureKeys.indexOf(key) >= 0) { + measureProperties[key] = entry[key]; + } else { + commonProperties[key] = String(entry[key]); + } } + logger.log(entry.scope === "exception" ? Type.EXCEPTION : Type.USAGEDATA, commonProperties, measureProperties); }); - }); - } - } - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider("java", new JavaDebugConfigurationProvider(reporter))); + } + }); + }); + + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider("java", new JavaDebugConfigurationProvider())); context.subscriptions.push(vscode.commands.registerCommand("JavaDebug.SpecifyProgramArgs", async () => { return specifyProgramArguments(context); })); @@ -69,9 +61,13 @@ export function deactivate() { function handleUserNotification(customEvent) { if (customEvent.body.notificationType === "ERROR") { - vscode.window.showErrorMessage(customEvent.body.message); + utility.showErrorMessageWithTroubleshooting({ + message: customEvent.body.message, + }); } else if (customEvent.body.notificationType === "WARNING") { - vscode.window.showWarningMessage(customEvent.body.message); + utility.showWarningMessageWithTroubleshooting({ + message: customEvent.body.message, + }); } else { vscode.window.showInformationMessage(customEvent.body.message); } diff --git a/src/hotCodeReplace.ts b/src/hotCodeReplace.ts index f853d1c..b9187e4 100644 --- a/src/hotCodeReplace.ts +++ b/src/hotCodeReplace.ts @@ -4,6 +4,7 @@ import * as vscode from "vscode"; import { HCR_EVENT, JAVA_LANGID } from "./constants"; +import * as utility from "./utility"; const suppressedReasons: Set = new Set(); @@ -40,9 +41,9 @@ export function handleHotCodeReplaceCustomEvent(hcrEvent) { if (hcrEvent.body.changeType === HcrChangeType.ERROR || hcrEvent.body.changeType === HcrChangeType.WARNING) { if (!suppressedReasons.has(hcrEvent.body.message)) { - vscode.window.showInformationMessage( - `Hot code replace failed - ${hcrEvent.body.message}. Would you like to restart the debug session?`, - YES_BUTTON, NO_BUTTON, NEVER_BUTTON).then((res) => { + utility.showWarningMessageWithTroubleshooting({ + message: `Hot code replace failed - ${hcrEvent.body.message}. Would you like to restart the debug session?`, + }, YES_BUTTON, NO_BUTTON, NEVER_BUTTON).then((res) => { if (res === NEVER_BUTTON) { suppressedReasons.add(hcrEvent.body.message); } else if (res === YES_BUTTON) { diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..37df1b0 --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import * as vscode from "vscode"; +import TelemetryReporter from "vscode-extension-telemetry"; + +export enum Type { + EXCEPTION = "exception", + USAGEDATA = "usageData", + USAGEERROR = "usageError", + ACTIVATEEXTENSION = "activateExtension", // TODO: Activation belongs to usage data, remove this category. +} + +class Logger { + private reporter: TelemetryReporter = null; + + public initialize(context: vscode.ExtensionContext): void { + if (this.reporter) { + return; + } + + const extensionPackage = require(context.asAbsolutePath("./package.json")); + if (extensionPackage) { + const packageInfo = { + name: extensionPackage.name, + version: extensionPackage.version, + aiKey: extensionPackage.aiKey, + }; + if (packageInfo.aiKey) { + this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); + } + } + } + + public log(type: Type, properties?: { [key: string]: string; }, measures?: { [key: string]: number; }): void { + if (!this.reporter) { + return; + } + + this.reporter.sendTelemetryEvent(type, properties, measures); + } + + public logMessage(type: Type, message: string): void { + this.log(type, { message }); + } +} + +export const logger = new Logger(); diff --git a/src/utility.ts b/src/utility.ts new file mode 100644 index 0000000..e70b599 --- /dev/null +++ b/src/utility.ts @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import * as opn from "opn"; +import * as vscode from "vscode"; +import { logger, Type } from "./logger"; + +const TROUBLESHOOTING_LINK = "https://github.com/Microsoft/vscode-java-debug/blob/master/Troubleshooting.md"; +const LEARN_MORE = "Learn More"; + +interface IMessage { + message: string; + type?: Type; + details?: { [key: string]: string; }; +} + +function logMessage(message: IMessage): void { + if (!message.type) { + return; + } + + if (message.details) { + logger.log(message.type, message.details); + } else { + logger.logMessage(message.type, message.message); + } +} + +export async function showInformationMessage(message: IMessage, ...items: string[]): Promise { + logMessage(message); + return await vscode.window.showInformationMessage(message.message, ...items); +} + +export async function showWarningMessage(message: IMessage, ...items: string[]): Promise { + logMessage(message); + return await vscode.window.showWarningMessage(message.message, ...items); +} + +export async function showErrorMessage(message: IMessage, ...items: string[]): Promise { + logMessage(message); + return await vscode.window.showErrorMessage(message.message, ...items); +} + +export async function showInformationMessageWithTroubleshooting(message: IMessage, ...items: string[]): Promise { + const choice = await showInformationMessage(message, ...items, LEARN_MORE); + return handleTroubleshooting(choice, message.message); +} + +export async function showWarningMessageWithTroubleshooting(message: IMessage, ...items: string[]): Promise { + const choice = await showWarningMessage(message, ...items, LEARN_MORE); + return handleTroubleshooting(choice, message.message); +} + +export async function showErrorMessageWithTroubleshooting(message: IMessage, ...items: string[]): Promise { + const choice = await showErrorMessage(message, ...items, LEARN_MORE); + return handleTroubleshooting(choice, message.message); +} + +function openLink(url: string): void { + opn(url); +} + +function handleTroubleshooting(choice: string, message: string): string | undefined { + if (choice === LEARN_MORE) { + openLink(TROUBLESHOOTING_LINK); + logger.log(Type.USAGEDATA, { + troubleshooting: "yes", + troubleshootingMessage: message, + }); + return; + } + + return choice; +}