|
| 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | +// Licensed under the MIT license. |
| 3 | + |
| 4 | +import * as path from "path"; |
| 5 | +import { DebugConfiguration, window } from "vscode"; |
| 6 | +import { getProcesses, getProcessTree } from "./processTree"; |
| 7 | + |
| 8 | +const JAVA_PATTERN = /(?:java|javaw|j9|j9w)$/i; |
| 9 | +const DEBUG_MODE_PATTERN = /(-agentlib|-Xrunjdwp):\S*(address=[^\s,]+)/i; |
| 10 | + |
| 11 | +interface IJavaProcess { |
| 12 | + pid: number; |
| 13 | + command: string; |
| 14 | + args: string; |
| 15 | + hostName: string; |
| 16 | + debugPort: number; |
| 17 | +} |
| 18 | + |
| 19 | +export async function resolveProcessId(config: DebugConfiguration): Promise<boolean> { |
| 20 | + let javaProcess; |
| 21 | + // tslint:disable-next-line |
| 22 | + if (!config.processId || config.processId === "${command:PickJavaProcess}") { |
| 23 | + javaProcess = await pickJavaProcess(); |
| 24 | + } else { |
| 25 | + javaProcess = await resolveJavaProcess(parseInt(String(config.processId), 10)); |
| 26 | + if (!javaProcess) { |
| 27 | + throw new Error(`Attach to process: pid '${config.processId}' doesn't look like a debuggable Java process. ` |
| 28 | + + `Please ensure the process has enabled debug mode with vmArgs like ` |
| 29 | + + `'-agentlib:jdwp=transport=dt_socket,server=y,address=5005.'`); |
| 30 | + } |
| 31 | + } |
| 32 | + |
| 33 | + if (javaProcess) { |
| 34 | + config.processId = undefined; |
| 35 | + config.hostName = javaProcess.hostName; |
| 36 | + config.port = javaProcess.debugPort; |
| 37 | + } |
| 38 | + |
| 39 | + return !!javaProcess; |
| 40 | +} |
| 41 | + |
| 42 | +function convertToJavaProcess(pid: number, command: string, args: string): IJavaProcess | undefined { |
| 43 | + if (process.platform === "win32" && command.indexOf("\\??\\") === 0) { |
| 44 | + // remove leading device specifier |
| 45 | + command = command.replace("\\??\\", ""); |
| 46 | + } |
| 47 | + |
| 48 | + const simpleName = path.basename(command, ".exe"); |
| 49 | + if (JAVA_PATTERN.test(simpleName) && args) { |
| 50 | + const match = args.match(DEBUG_MODE_PATTERN); |
| 51 | + if (match && match.length) { |
| 52 | + const address = match[2].split("=")[1].split(":"); |
| 53 | + const hostName = address.length > 1 ? address[0] : "127.0.0.1"; |
| 54 | + const debugPort = parseInt(address[address.length - 1], 10); |
| 55 | + const exeName = path.basename(command); |
| 56 | + const binPath = path.dirname(command); |
| 57 | + const commandPath = path.basename(binPath) === "bin" ? |
| 58 | + path.join(path.basename(path.dirname(binPath)), "bin", exeName) : exeName; |
| 59 | + return { |
| 60 | + pid, |
| 61 | + command: commandPath, |
| 62 | + args, |
| 63 | + hostName, |
| 64 | + debugPort, |
| 65 | + }; |
| 66 | + } |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | +async function pickJavaProcess(): Promise<IJavaProcess> { |
| 71 | + try { |
| 72 | + const javaProcesses: IJavaProcess[] = []; |
| 73 | + await getProcesses((pid: number, ppid: number, command: string, args: string, date: number) => { |
| 74 | + const javaProcess = convertToJavaProcess(pid, command, args); |
| 75 | + if (javaProcess) { |
| 76 | + javaProcesses.push(javaProcess); |
| 77 | + } |
| 78 | + }); |
| 79 | + |
| 80 | + if (!javaProcesses.length) { |
| 81 | + throw new Error("Process picker: No debuggable Java process found. Please ensure enable debugging for " |
| 82 | + + "your application with vmArgs like '-agentlib:jdwp=transport=dt_socket,server=y,address=5005'."); |
| 83 | + } |
| 84 | + |
| 85 | + const items = javaProcesses.map((process) => { |
| 86 | + return { |
| 87 | + label: process.command, |
| 88 | + description: process.args, |
| 89 | + detail: `process id: ${process.pid}, debug port: ${process.debugPort}`, |
| 90 | + process, |
| 91 | + }; |
| 92 | + }); |
| 93 | + |
| 94 | + const pick = await window.showQuickPick(items, { |
| 95 | + placeHolder: "Pick Java process to attach to", |
| 96 | + }); |
| 97 | + |
| 98 | + if (pick) { |
| 99 | + return pick.process; |
| 100 | + } |
| 101 | + } catch (error) { |
| 102 | + throw new Error("Process picker failed: " + error); |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +async function resolveJavaProcess(pid: number): Promise<IJavaProcess | undefined> { |
| 107 | + const processTree = await getProcessTree(pid); |
| 108 | + if (!processTree || processTree.pid !== pid) { |
| 109 | + return undefined; |
| 110 | + } |
| 111 | + |
| 112 | + return convertToJavaProcess(processTree.pid, processTree.command, processTree.args); |
| 113 | +} |
0 commit comments