[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;
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.JavaSdkVersionUtil;
import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
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 {
private static final boolean ourMayUseLauncher = !Boolean.getBoolean("idea.no.launcher");
@@ -27,14 +27,13 @@ public final class ProcessProxyFactoryImpl extends ProcessProxyFactory {
String mainClass = javaParameters.getMainClass();
if (ourMayUseLauncher && mainClass != null) {
String rtJarPath = JavaSdkUtil.getIdeaRtJarPath();
boolean runtimeJarFile = new File(rtJarPath).isFile() && FileUtil.isAncestor(PathManager.getHomePath(), rtJarPath, true);
var rtJarPath = Path.of(JavaSdkUtil.getIdeaRtJarPath());
var runtimeJarFile = Files.isRegularFile(rtJarPath) && rtJarPath.startsWith(PathManager.getHomePath());
if (runtimeJarFile || javaParameters.getModuleName() == null) {
try {
ProcessProxyImpl proxy = new ProcessProxyImpl(StringUtil.getShortName(mainClass));
String port = String.valueOf(proxy.getPortNumber());
String binPath = proxy.getBinPath();
JavaSdkVersion jdkVersion = JavaSdkVersionUtil.getJavaSdkVersion(javaParameters.getJdk());
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",
@@ -42,14 +41,13 @@ public final class ProcessProxyFactoryImpl extends ProcessProxyFactory {
}
if (runtimeJarFile) {
javaParameters.getVMParametersList().add("-javaagent:" + rtJarPath + '=' + port + ':' + binPath);
javaParameters.getVMParametersList().add("-javaagent:" + rtJarPath + '=' + port);
}
else {
JavaSdkUtil.addRtJar(javaParameters.getClassPath());
ParametersList vmParametersList = javaParameters.getVMParametersList();
vmParametersList.defineProperty(AppMainV2.LAUNCHER_PORT_NUMBER, port);
vmParametersList.defineProperty(AppMainV2.LAUNCHER_BIN_PATH, binPath);
boolean isJava21preview = JavaSdkVersion.JDK_21.equals(jdkVersion) &&
javaParameters.getVMParametersList().getParameters().contains(JavaParameters.JAVA_ENABLE_PREVIEW_PROPERTY);
@@ -79,4 +77,4 @@ public final class ProcessProxyFactoryImpl extends ProcessProxyFactory {
public ProcessProxy getAttachedProxy(ProcessHandler processHandler) {
return ProcessProxyImpl.KEY.get(processHandler);
}
}
}

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;
import com.intellij.execution.process.BaseOSProcessHandler;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.UnixProcessManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.ThrowableRunnable;
import com.intellij.util.system.CpuArch;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
@@ -21,12 +19,10 @@ import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
final class ProcessProxyImpl implements ProcessProxy {
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 int myPort;
@@ -37,7 +33,7 @@ final class ProcessProxyImpl implements ProcessProxy {
ProcessProxyImpl(String mainClass) throws IOException {
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))
.setOption(StandardSocketOptions.SO_REUSEADDR, true);
myPort = ((InetSocketAddress)channel.getLocalAddress()).getPort();
@@ -73,6 +69,7 @@ final class ProcessProxyImpl implements ProcessProxy {
});
}
@SuppressWarnings("SameParameterValue")
private void writeLine(String s) {
execute(() -> {
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
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) {
synchronized (myLock) {
return myPid > 0;
@@ -118,10 +99,7 @@ final class ProcessProxyImpl implements ProcessProxy {
@Override
public void sendBreak() {
if (SystemInfo.isWindows) {
writeLine("BREAK");
}
else if (SystemInfo.isUnix) {
if (!SystemInfo.isWindows) {
int pid;
synchronized (myLock) {
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;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Constructor;
@@ -17,54 +16,24 @@ import java.util.*;
*/
public final class AppMainV2 {
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";
private static final String LAUNCHER_MAIN_CLASS = "idea.launcher.main.class";
private static native void triggerControlBreak();
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) {
private static void startMonitor(final int portNumber) {
Thread t = new Thread("Monitor Ctrl-Break") {
@Override
public void run() {
try {
try (Socket client = new Socket("127.0.0.1", portNumber)) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "US-ASCII"))) {
while (true) {
String msg = reader.readLine();
if (msg == null || "TERM".equals(msg)) {
return;
}
else if ("BREAK".equals(msg)) {
if (helperLibLoaded) {
triggerControlBreak();
}
}
else if ("STOP".equals(msg)) {
System.exit(1);
}
}
try (
Socket client = new Socket("127.0.0.1", portNumber);
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "US-ASCII"))
) {
while (true) {
String msg = reader.readLine();
if (msg == null || "TERM".equals(msg)) {
return;
}
else if ("STOP".equals(msg)) {
System.exit(1);
}
}
}
@@ -77,9 +46,8 @@ public final class AppMainV2 {
public static void main(String[] args) throws Throwable {
try {
boolean helperLibLoaded = loadHelper(System.getProperty(LAUNCHER_BIN_PATH));
int portNumber = Integer.parseInt(System.getProperty(LAUNCHER_PORT_NUMBER));
startMonitor(portNumber, helperLibLoaded);
startMonitor(portNumber);
}
catch (Throwable t) {
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) {
try {
int p = args.indexOf(':');
if (p < 0) throw new IllegalArgumentException("incorrect parameter: " + args);
boolean helperLibLoaded = loadHelper(args.substring(p + 1));
int portNumber = Integer.parseInt(args.substring(0, p));
startMonitor(portNumber, helperLibLoaded);
int portNumber = Integer.parseInt(p > 0 ? args.substring(0, p) : args);
startMonitor(portNumber);
}
catch (Throwable t) {
System.err.println("Launcher failed - \"Dump Threads\" and \"Exit\" actions are unavailable (" + t.getMessage() + ')');