Skip to content

Commit

Permalink
8318736: com/sun/jdi/JdwpOnThrowTest.java failed with "transport erro…
Browse files Browse the repository at this point in the history
…r 202: bind failed: Address already in use"

Reviewed-by: mbaesken
Backport-of: 28e1a33856504abfc003a283ece928fb87f6623c
  • Loading branch information
parttimenerd authored and GoeLin committed Feb 19, 2024
1 parent 6ecf198 commit ddc3e41
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 45 deletions.
15 changes: 3 additions & 12 deletions test/jdk/com/sun/jdi/JdwpOnThrowTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,11 @@ public class JdwpOnThrowTest {
private static AttachingConnector attachingConnector;

public static void main(String[] args) throws Exception {
int port = findFreePort();
try (Debuggee debuggee = Debuggee.launcher("ThrowCaughtException").setAddress("localhost:" + port)
.enableOnThrow("Ex", "Start").setSuspended(true).launch()) {
try (Debuggee debuggee = Debuggee.launcher("ThrowCaughtException")
.enableOnThrow("Ex").setSuspended(true).launch()) {
VirtualMachine vm = null;
try {
vm = attach("localhost", "" + port);
vm = attach("localhost", debuggee.getAddress());
EventQueue queue = vm.eventQueue();
log("Waiting for exception event");
long start = System.currentTimeMillis();
Expand Down Expand Up @@ -111,14 +110,6 @@ private static void verifyExceptionEvent(ExceptionEvent ex) throws Exception {
}
}

private static int findFreePort() {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static VirtualMachine attach(String address, String port) throws IOException {
if (attachingConnector == null) {
attachingConnector = (AttachingConnector)getConnector(ATTACH_CONNECTOR);
Expand Down
88 changes: 55 additions & 33 deletions test/jdk/com/sun/jdi/lib/jdb/Debuggee.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -24,6 +24,7 @@
package lib.jdb;

import jdk.test.lib.Utils;
import jdk.test.lib.util.Pair;
import jdk.test.lib.process.ProcessTools;

import java.io.Closeable;
Expand All @@ -32,6 +33,7 @@
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -65,12 +67,12 @@ public static Launcher launcher(String mainClass) {
public static class Launcher {
private final String mainClass;
private final List<String> options = new LinkedList<>();
private String vmOptions = null;
private String transport = "dt_socket";
private String address = null;
private boolean suspended = true;
private String onthrow = "";
private boolean waitForPortPrint = true;
private String expectedOutputBeforeThrow = "";
private static final String LAUNCH_ECHO_STRING = "Listen Args:";

private Launcher(String mainClass) {
this.mainClass = mainClass;
Expand All @@ -83,6 +85,10 @@ public Launcher addOptions(List<String> options) {
this.options.addAll(options);
return this;
}
public Launcher addVMOptions(String vmOptions) {
this.vmOptions = vmOptions;
return this;
}
// default is "dt_socket"
public Launcher setTransport(String value) {
transport = value;
Expand All @@ -99,17 +105,17 @@ public Launcher setSuspended(boolean value) {
return this;
}

// required to pass non null port with address and emit string before the throw
public Launcher enableOnThrow(String value, String expectedOutputBeforeThrow) {
this.onthrow = value;
this.waitForPortPrint = false;
this.expectedOutputBeforeThrow = expectedOutputBeforeThrow;
public Launcher enableOnThrow(String exceptionClassName) {
this.onthrow = exceptionClassName;
return this;
}

public ProcessBuilder prepare() {
List<String> debuggeeArgs = new LinkedList<>();
String onthrowArgs = onthrow.isEmpty() ? "" : ",onthrow=" + onthrow + ",launch=exit";
if (vmOptions != null) {
debuggeeArgs.add(vmOptions);
}
String onthrowArgs = onthrow.isEmpty() ? "" : ",onthrow=" + onthrow + ",launch=echo " + LAUNCH_ECHO_STRING;
debuggeeArgs.add("-agentlib:jdwp=transport=" + transport
+ (address == null ? "" : ",address=" + address)
+ ",server=y,suspend=" + (suspended ? "y" : "n")
Expand All @@ -120,41 +126,57 @@ public ProcessBuilder prepare() {
}

public Debuggee launch(String name) {
return new Debuggee(prepare(), name, waitForPortPrint, expectedOutputBeforeThrow);
return new Debuggee(prepare(), name,
onthrow.isEmpty() ?
Launcher::parseListenAddress :
Launcher::parseLaunchEchoListenAddress
);
}
public Debuggee launch() {
return launch("debuggee");
}
}

// starts the process, waits for "Listening for transport" output and detects transport/address
private Debuggee(ProcessBuilder pb, String name, boolean waitForPortPrint, String expectedOutputBeforeThrow) {
// debuggeeListen[0] - transport, debuggeeListen[1] - address
String[] debuggeeListen = new String[2];
Pattern listenRegexp = Pattern.compile("Listening for transport \\b(.+)\\b at address: \\b(.+)\\b");
if (!waitForPortPrint) {
try {
p = ProcessTools.startProcess(name, pb, s -> {output.add(s);}, s -> {
return s.equals(expectedOutputBeforeThrow);
}, 30, TimeUnit.SECONDS);
} catch (IOException | InterruptedException | TimeoutException ex) {
throw new RuntimeException("failed to launch debuggee", ex);
/**
* Parses debuggee output to get listening transport and address, printed by `launch=echo`.
* Returns null if the string specified does not contain required info.
*/
private static Pair<String, String> parseLaunchEchoListenAddress(String debuggeeOutput) {
Pattern listenRegexp = Pattern.compile(LAUNCH_ECHO_STRING + " \\b(.+)\\b \\b(.+)\\b");
Matcher m = listenRegexp.matcher(debuggeeOutput);
if (m.find()) {
return new Pair<String, String>(m.group(1), m.group(2));
}
transport = null;
address = null;
return;
return null;
}

/**
* Parses debuggee output to get listening transport and address, printed by `launch=echo`.
* Returns null if the string specified does not contain required info.
*/
private static Pair<String, String> parseListenAddress(String debuggeeOutput) {
Pattern listenRegexp = Pattern.compile("Listening for transport \\b(.+)\\b at address: \\b(.+)\\b");
Matcher m = listenRegexp.matcher(debuggeeOutput);
if (m.find()) {
return new Pair<String, String>(m.group(1), m.group(2));
}
return null;
}
}

// starts the process, waits until the provided addressDetector detects transport/address from the process output
private Debuggee(ProcessBuilder pb, String name, Function<String, Pair<String, String>> addressDetector) {
String[] debuggeeListen = new String[2];
try {
p = ProcessTools.startProcess(name, pb,
s -> output.add(s), // output consumer
s -> { // warm-up predicate
Matcher m = listenRegexp.matcher(s);
if (!m.matches()) {
return false;
s -> {
Pair<String, String> addr = addressDetector.apply(s);
if (addr != null) {
debuggeeListen[0] = addr.first;
debuggeeListen[1] = addr.second;
return true;
}
debuggeeListen[0] = m.group(1);
debuggeeListen[1] = m.group(2);
return true;
return false;
},
30, TimeUnit.SECONDS);
transport = debuggeeListen[0];
Expand Down

1 comment on commit ddc3e41

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.