56 */ |
55 */ |
57 private static Connector findConnector(String name) { |
56 private static Connector findConnector(String name) { |
58 List<Connector> connectors = Bootstrap.virtualMachineManager().allConnectors(); |
57 List<Connector> connectors = Bootstrap.virtualMachineManager().allConnectors(); |
59 Iterator<Connector> iter = connectors.iterator(); |
58 Iterator<Connector> iter = connectors.iterator(); |
60 while (iter.hasNext()) { |
59 while (iter.hasNext()) { |
61 Connector connector = (Connector)iter.next(); |
60 Connector connector = iter.next(); |
62 if (connector.name().equals(name)) { |
61 if (connector.name().equals(name)) { |
63 return connector; |
62 return connector; |
64 } |
63 } |
65 } |
64 } |
66 return null; |
65 return null; |
67 } |
66 } |
68 |
67 |
69 /* |
68 private static void log(Object s) { |
70 * Launch a server debuggee with the given address |
69 System.out.println(String.valueOf(s)); |
71 */ |
|
72 private static LaunchResult launch(String address, String class_name) throws Exception { |
|
73 String[] args = VMConnection.insertDebuggeeVMOptions(new String[] { |
|
74 "-agentlib:jdwp=transport=dt_socket" + |
|
75 ",server=y" + ",suspend=y" + ",address=" + address, |
|
76 class_name |
|
77 }); |
|
78 |
|
79 ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args); |
|
80 |
|
81 final AtomicBoolean success = new AtomicBoolean(); |
|
82 final AtomicBoolean bindFailed = new AtomicBoolean(); |
|
83 Process p = ProcessTools.startProcess( |
|
84 class_name, |
|
85 pb, |
|
86 (line) -> { |
|
87 // 'Listening for transport dt_socket at address: xxxxx' |
|
88 // indicates the debuggee is ready to accept connections |
|
89 if (line.contains("Listening for transport dt_socket at address:")) { |
|
90 success.set(true); |
|
91 return true; |
|
92 } |
|
93 // 'Address already in use' indicates |
|
94 // the debuggee has failed to start due to busy port. |
|
95 if (line.contains("Address already in use")) { |
|
96 bindFailed.set(true); |
|
97 return true; |
|
98 } |
|
99 return false; |
|
100 }, |
|
101 Integer.MAX_VALUE, |
|
102 TimeUnit.MILLISECONDS |
|
103 ); |
|
104 |
|
105 return new LaunchResult(success.get() ? p : null, |
|
106 bindFailed.get()); |
|
107 } |
70 } |
108 |
71 |
109 /* |
|
110 * - pick a TCP port |
|
111 * - Launch a server debuggee: server=y,suspend=y,address=${port} |
|
112 * - run it to VM death |
|
113 * - verify we saw no error |
|
114 */ |
|
115 public static void main(String args[]) throws Exception { |
72 public static void main(String args[]) throws Exception { |
116 // Launch the server debuggee |
73 // Launch the server debugee |
117 int port = 0; |
74 log("Starting debuggee..."); |
118 Process process = null; |
75 try (Debuggee debuggee = Debuggee.launcher("Exit0").launch()) { |
119 while (process == null) { |
76 log("Debuggee started."); |
120 port = Utils.getFreePort(); |
77 int port = Integer.parseInt(debuggee.getAddress()); |
121 String address = String.valueOf(port); |
78 log("Debuggee port: " + port); |
122 LaunchResult launchResult = launch(address, "Exit0"); |
79 |
123 process = launchResult.getProcess(); |
80 log("testcase 1..."); |
124 if (launchResult.isBindFailed()) { |
81 // Connect to the debuggee and handshake with garbage |
125 System.out.println("Port " + port + " already in use. Trying to restart debuggee with a new one..."); |
82 Socket s = new Socket("localhost", port); |
126 Thread.sleep(100); |
83 s.getOutputStream().write("Here's a poke in the eye".getBytes("UTF-8")); |
127 } else if (process == null ) { |
84 s.close(); |
128 throw new RuntimeException("Unable to start debugee"); |
85 |
|
86 log("testcase 2..."); |
|
87 // Re-connect and do a partial handshake - don't disconnect |
|
88 // Re-connect just after disconnect may cause "connection refused" error (see JDK-8192057) |
|
89 Exception error = null; |
|
90 long retryDelay = 20; |
|
91 for (int retry = 0; retry < 5; retry++) { |
|
92 if (error != null) { |
|
93 try { |
|
94 Thread.sleep(retryDelay); |
|
95 } catch (InterruptedException ex) { |
|
96 // ignore |
|
97 } |
|
98 retryDelay *= 2; |
|
99 error = null; |
|
100 } |
|
101 try { |
|
102 log("retry: " + retry); |
|
103 s = new Socket("localhost", port); |
|
104 s.getOutputStream().write("JDWP-".getBytes("UTF-8")); |
|
105 break; |
|
106 } catch (ConnectException ex) { |
|
107 log("got exception: " + ex.toString()); |
|
108 error = ex; |
|
109 } |
129 } |
110 } |
|
111 if (error != null) { |
|
112 throw error; |
|
113 } |
|
114 |
|
115 log("cleaning..."); |
|
116 // Attach to server debuggee and resume it so it can exit |
|
117 AttachingConnector conn = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach"); |
|
118 Map<String, Argument> conn_args = conn.defaultArguments(); |
|
119 Connector.IntegerArgument port_arg = |
|
120 (Connector.IntegerArgument)conn_args.get("port"); |
|
121 port_arg.setValue(port); |
|
122 VirtualMachine vm = conn.attach(conn_args); |
|
123 |
|
124 // The first event is always a VMStartEvent, and it is always in |
|
125 // an EventSet by itself. Wait for it. |
|
126 EventSet evtSet = vm.eventQueue().remove(); |
|
127 for (Event event : evtSet) { |
|
128 if (event instanceof VMStartEvent) { |
|
129 break; |
|
130 } |
|
131 throw new RuntimeException("Test failed - debuggee did not start properly"); |
|
132 } |
|
133 |
|
134 vm.eventRequestManager().deleteAllBreakpoints(); |
|
135 vm.resume(); |
|
136 |
|
137 debuggee.waitFor(10, TimeUnit.SECONDS); |
130 } |
138 } |
131 |
|
132 // Connect to the debuggee and handshake with garbage |
|
133 Socket s = new Socket("localhost", port); |
|
134 s.getOutputStream().write("Here's a poke in the eye".getBytes("UTF-8")); |
|
135 s.close(); |
|
136 |
|
137 // Re-connect and to a partial handshake - don't disconnect |
|
138 s = new Socket("localhost", port); |
|
139 s.getOutputStream().write("JDWP-".getBytes("UTF-8")); |
|
140 |
|
141 |
|
142 // Attach to server debuggee and resume it so it can exit |
|
143 AttachingConnector conn = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach"); |
|
144 Map<String, Argument> conn_args = conn.defaultArguments(); |
|
145 Connector.IntegerArgument port_arg = |
|
146 (Connector.IntegerArgument)conn_args.get("port"); |
|
147 port_arg.setValue(port); |
|
148 VirtualMachine vm = conn.attach(conn_args); |
|
149 |
|
150 // The first event is always a VMStartEvent, and it is always in |
|
151 // an EventSet by itself. Wait for it. |
|
152 EventSet evtSet = vm.eventQueue().remove(); |
|
153 for (Event event: evtSet) { |
|
154 if (event instanceof VMStartEvent) { |
|
155 break; |
|
156 } |
|
157 throw new RuntimeException("Test failed - debuggee did not start properly"); |
|
158 } |
|
159 |
|
160 vm.eventRequestManager().deleteAllBreakpoints(); |
|
161 vm.resume(); |
|
162 |
|
163 process.waitFor(); |
|
164 } |
139 } |
165 |
|
166 private static class LaunchResult { |
|
167 |
|
168 private final Process p; |
|
169 private final boolean bindFailed; |
|
170 |
|
171 public LaunchResult(Process p, boolean bindFailed) { |
|
172 this.p = p; |
|
173 this.bindFailed = bindFailed; |
|
174 } |
|
175 |
|
176 public Process getProcess() { |
|
177 return p; |
|
178 } |
|
179 |
|
180 public boolean isBindFailed() { |
|
181 return bindFailed; |
|
182 } |
|
183 |
|
184 } |
|
185 |
|
186 } |
140 } |