[java] retiring 'breakgen' DLLs from JVM process proxy (IJPL-16521)

(cherry picked from commit 3789c7f569bc1f220aa9c7524d36ac44a3754cf0)

IJ-CR-155182

GitOrigin-RevId: a7ff75cc9cc1a54c9c1b1628fb7c89f65cf8cf8d
This commit is contained in:
Roman Shevchenko
2025-02-14 11:36:44 +01:00
committed by intellij-monorepo-bot
parent 7efa647a39
commit e7a6ad4826
3 changed files with 27 additions and 85 deletions

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.execution.runners; package com.intellij.execution.runners;
import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionException;
@@ -12,11 +12,11 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaSdkVersionUtil; import com.intellij.openapi.projectRoots.JavaSdkVersionUtil;
import com.intellij.openapi.projectRoots.ex.JavaSdkUtil; import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtil;
import com.intellij.rt.execution.application.AppMainV2; import com.intellij.rt.execution.application.AppMainV2;
import java.io.File; import java.nio.file.Files;
import java.nio.file.Path;
public final class ProcessProxyFactoryImpl extends ProcessProxyFactory { public final class ProcessProxyFactoryImpl extends ProcessProxyFactory {
private static final boolean ourMayUseLauncher = !Boolean.getBoolean("idea.no.launcher"); private static final boolean ourMayUseLauncher = !Boolean.getBoolean("idea.no.launcher");
@@ -27,14 +27,13 @@ public final class ProcessProxyFactoryImpl extends ProcessProxyFactory {
String mainClass = javaParameters.getMainClass(); String mainClass = javaParameters.getMainClass();
if (ourMayUseLauncher && mainClass != null) { if (ourMayUseLauncher && mainClass != null) {
String rtJarPath = JavaSdkUtil.getIdeaRtJarPath(); var rtJarPath = Path.of(JavaSdkUtil.getIdeaRtJarPath());
boolean runtimeJarFile = new File(rtJarPath).isFile() && FileUtil.isAncestor(PathManager.getHomePath(), rtJarPath, true); var runtimeJarFile = Files.isRegularFile(rtJarPath) && rtJarPath.startsWith(PathManager.getHomePath());
if (runtimeJarFile || javaParameters.getModuleName() == null) { if (runtimeJarFile || javaParameters.getModuleName() == null) {
try { try {
ProcessProxyImpl proxy = new ProcessProxyImpl(StringUtil.getShortName(mainClass)); ProcessProxyImpl proxy = new ProcessProxyImpl(StringUtil.getShortName(mainClass));
String port = String.valueOf(proxy.getPortNumber()); String port = String.valueOf(proxy.getPortNumber());
String binPath = proxy.getBinPath();
JavaSdkVersion jdkVersion = JavaSdkVersionUtil.getJavaSdkVersion(javaParameters.getJdk()); JavaSdkVersion jdkVersion = JavaSdkVersionUtil.getJavaSdkVersion(javaParameters.getJdk());
if (jdkVersion != null && !jdkVersion.isAtLeast(JavaSdkVersion.JDK_1_7)) { if (jdkVersion != null && !jdkVersion.isAtLeast(JavaSdkVersion.JDK_1_7)) {
throw new ExecutionException(JavaBundle.message("error.message.ide.does.not.support.starting.processes.using.old.java", throw new ExecutionException(JavaBundle.message("error.message.ide.does.not.support.starting.processes.using.old.java",
@@ -42,14 +41,13 @@ public final class ProcessProxyFactoryImpl extends ProcessProxyFactory {
} }
if (runtimeJarFile) { if (runtimeJarFile) {
javaParameters.getVMParametersList().add("-javaagent:" + rtJarPath + '=' + port + ':' + binPath); javaParameters.getVMParametersList().add("-javaagent:" + rtJarPath + '=' + port);
} }
else { else {
JavaSdkUtil.addRtJar(javaParameters.getClassPath()); JavaSdkUtil.addRtJar(javaParameters.getClassPath());
ParametersList vmParametersList = javaParameters.getVMParametersList(); ParametersList vmParametersList = javaParameters.getVMParametersList();
vmParametersList.defineProperty(AppMainV2.LAUNCHER_PORT_NUMBER, port); vmParametersList.defineProperty(AppMainV2.LAUNCHER_PORT_NUMBER, port);
vmParametersList.defineProperty(AppMainV2.LAUNCHER_BIN_PATH, binPath);
boolean isJava21preview = JavaSdkVersion.JDK_21.equals(jdkVersion) && boolean isJava21preview = JavaSdkVersion.JDK_21.equals(jdkVersion) &&
javaParameters.getVMParametersList().getParameters().contains(JavaParameters.JAVA_ENABLE_PREVIEW_PROPERTY); javaParameters.getVMParametersList().getParameters().contains(JavaParameters.JAVA_ENABLE_PREVIEW_PROPERTY);

View File

@@ -1,15 +1,13 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.execution.runners; package com.intellij.execution.runners;
import com.intellij.execution.process.BaseOSProcessHandler; import com.intellij.execution.process.BaseOSProcessHandler;
import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.UnixProcessManager; import com.intellij.execution.process.UnixProcessManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.ThrowableRunnable; import com.intellij.util.ThrowableRunnable;
import com.intellij.util.system.CpuArch;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.IOException; import java.io.IOException;
@@ -21,12 +19,10 @@ import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler; import java.nio.channels.CompletionHandler;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
final class ProcessProxyImpl implements ProcessProxy { final class ProcessProxyImpl implements ProcessProxy {
static final Key<ProcessProxyImpl> KEY = Key.create("ProcessProxyImpl"); static final Key<ProcessProxyImpl> KEY = Key.create("ProcessProxyImpl");
private static final Path ourBreakgenHelper = SystemInfo.isWindows ? PathManager.findBinFile("breakgen.dll") : null;
private final AsynchronousChannelGroup myGroup; private final AsynchronousChannelGroup myGroup;
private final int myPort; private final int myPort;
@@ -37,7 +33,7 @@ final class ProcessProxyImpl implements ProcessProxy {
ProcessProxyImpl(String mainClass) throws IOException { ProcessProxyImpl(String mainClass) throws IOException {
myGroup = AsynchronousChannelGroup.withFixedThreadPool(1, r -> new Thread(r, "Process Proxy: " + mainClass)); myGroup = AsynchronousChannelGroup.withFixedThreadPool(1, r -> new Thread(r, "Process Proxy: " + mainClass));
AsynchronousServerSocketChannel channel = AsynchronousServerSocketChannel.open(myGroup) @SuppressWarnings("resource") var channel = AsynchronousServerSocketChannel.open(myGroup)
.bind(new InetSocketAddress("127.0.0.1", 0)) .bind(new InetSocketAddress("127.0.0.1", 0))
.setOption(StandardSocketOptions.SO_REUSEADDR, true); .setOption(StandardSocketOptions.SO_REUSEADDR, true);
myPort = ((InetSocketAddress)channel.getLocalAddress()).getPort(); myPort = ((InetSocketAddress)channel.getLocalAddress()).getPort();
@@ -73,6 +69,7 @@ final class ProcessProxyImpl implements ProcessProxy {
}); });
} }
@SuppressWarnings("SameParameterValue")
private void writeLine(String s) { private void writeLine(String s) {
execute(() -> { execute(() -> {
ByteBuffer out = ByteBuffer.wrap((s + '\n').getBytes(StandardCharsets.US_ASCII)); ByteBuffer out = ByteBuffer.wrap((s + '\n').getBytes(StandardCharsets.US_ASCII));
@@ -82,24 +79,8 @@ final class ProcessProxyImpl implements ProcessProxy {
}); });
} }
String getBinPath() {
if (SystemInfo.isWindows) {
if (ourBreakgenHelper != null) {
return ourBreakgenHelper.getParent().toString();
}
}
return PathManager.getBinPath();
}
@Override @Override
public boolean canSendBreak() { public boolean canSendBreak() {
if (SystemInfo.isWindows && (CpuArch.isArm64() || CpuArch.isIntel64() || CpuArch.isIntel32())) { // see also: `AppMainV2#loadHelper`
synchronized (myLock) {
if (myConnection == null) return false;
}
return ourBreakgenHelper != null;
}
if (SystemInfo.isUnix) { if (SystemInfo.isUnix) {
synchronized (myLock) { synchronized (myLock) {
return myPid > 0; return myPid > 0;
@@ -118,10 +99,7 @@ final class ProcessProxyImpl implements ProcessProxy {
@Override @Override
public void sendBreak() { public void sendBreak() {
if (SystemInfo.isWindows) { if (!SystemInfo.isWindows) {
writeLine("BREAK");
}
else if (SystemInfo.isUnix) {
int pid; int pid;
synchronized (myLock) { synchronized (myLock) {
pid = myPid; pid = myPid;

View File

@@ -1,8 +1,7 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.rt.execution.application; package com.intellij.rt.execution.application;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.lang.instrument.Instrumentation; import java.lang.instrument.Instrumentation;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
@@ -17,57 +16,27 @@ import java.util.*;
*/ */
public final class AppMainV2 { public final class AppMainV2 {
public static final String LAUNCHER_PORT_NUMBER = "idea.launcher.port"; public static final String LAUNCHER_PORT_NUMBER = "idea.launcher.port";
public static final String LAUNCHER_BIN_PATH = "idea.launcher.bin.path";
public static final String LAUNCHER_USE_JDK_21_PREVIEW = "idea.launcher.use.21.preview"; public static final String LAUNCHER_USE_JDK_21_PREVIEW = "idea.launcher.use.21.preview";
private static final String LAUNCHER_MAIN_CLASS = "idea.launcher.main.class"; private static final String LAUNCHER_MAIN_CLASS = "idea.launcher.main.class";
private static native void triggerControlBreak(); private static void startMonitor(final int portNumber) {
private static boolean loadHelper(String binPath) {
String osName = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
if (osName.startsWith("windows")) {
String arch = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH);
//noinspection SpellCheckingInspection
String libName = "x86_64".equals(arch) || "amd64".equals(arch) ? "breakgen64.dll" :
"aarch64".equals(arch) || "arm64".equals(arch) ? "breakgen64a.dll" :
"i386".equals(arch) || "x86".equals(arch) ? "breakgen.dll" :
null; // see also: `ProcessProxyImpl#canSendBreak`
if (libName != null) {
File libFile = new File(binPath, libName);
if (libFile.isFile()) {
System.load(libFile.getAbsolutePath());
return true;
}
}
}
return false;
}
private static void startMonitor(final int portNumber, final boolean helperLibLoaded) {
Thread t = new Thread("Monitor Ctrl-Break") { Thread t = new Thread("Monitor Ctrl-Break") {
@Override @Override
public void run() { public void run() {
try { try (
try (Socket client = new Socket("127.0.0.1", portNumber)) { Socket client = new Socket("127.0.0.1", portNumber);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "US-ASCII"))) { BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "US-ASCII"))
) {
while (true) { while (true) {
String msg = reader.readLine(); String msg = reader.readLine();
if (msg == null || "TERM".equals(msg)) { if (msg == null || "TERM".equals(msg)) {
return; return;
} }
else if ("BREAK".equals(msg)) {
if (helperLibLoaded) {
triggerControlBreak();
}
}
else if ("STOP".equals(msg)) { else if ("STOP".equals(msg)) {
System.exit(1); System.exit(1);
} }
} }
} }
}
}
catch (Exception ignored) { } catch (Exception ignored) { }
} }
}; };
@@ -77,9 +46,8 @@ public final class AppMainV2 {
public static void main(String[] args) throws Throwable { public static void main(String[] args) throws Throwable {
try { try {
boolean helperLibLoaded = loadHelper(System.getProperty(LAUNCHER_BIN_PATH));
int portNumber = Integer.parseInt(System.getProperty(LAUNCHER_PORT_NUMBER)); int portNumber = Integer.parseInt(System.getProperty(LAUNCHER_PORT_NUMBER));
startMonitor(portNumber, helperLibLoaded); startMonitor(portNumber);
} }
catch (Throwable t) { catch (Throwable t) {
System.err.println("Launcher failed - \"Dump Threads\" and \"Exit\" actions are unavailable (" + t.getMessage() + ')'); System.err.println("Launcher failed - \"Dump Threads\" and \"Exit\" actions are unavailable (" + t.getMessage() + ')');
@@ -263,10 +231,8 @@ public final class AppMainV2 {
public static void premain(String args) { public static void premain(String args) {
try { try {
int p = args.indexOf(':'); int p = args.indexOf(':');
if (p < 0) throw new IllegalArgumentException("incorrect parameter: " + args); int portNumber = Integer.parseInt(p > 0 ? args.substring(0, p) : args);
boolean helperLibLoaded = loadHelper(args.substring(p + 1)); startMonitor(portNumber);
int portNumber = Integer.parseInt(args.substring(0, p));
startMonitor(portNumber, helperLibLoaded);
} }
catch (Throwable t) { catch (Throwable t) {
System.err.println("Launcher failed - \"Dump Threads\" and \"Exit\" actions are unavailable (" + t.getMessage() + ')'); System.err.println("Launcher failed - \"Dump Threads\" and \"Exit\" actions are unavailable (" + t.getMessage() + ')');