1 /*
2  * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /* @test
25  * @bug 4997445
26  * @summary Test that with server=y, when VM runs to System.exit() no error happens
27  * @library /test/lib
28  * @modules java.management
29  *          jdk.jdi
30  * @build VMConnection RunToExit Exit0
31  * @run driver RunToExit
32  */
33 import com.sun.jdi.Bootstrap;
34 import com.sun.jdi.VirtualMachine;
35 import com.sun.jdi.event.*;
36 import com.sun.jdi.connect.Connector;
37 import com.sun.jdi.connect.AttachingConnector;
38 import java.net.ConnectException;
39 import java.util.Map;
40 import java.util.List;
41 import java.util.Iterator;
42 import java.util.concurrent.TimeUnit;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
45 import java.util.stream.Collectors;
46 import jdk.test.lib.process.ProcessTools;
47 
48 public class RunToExit {
49 
50     /* Increment this when ERROR: seen */
51     static volatile int error_seen = 0;
52     static volatile boolean ready = false;
53 
54     /* port the debuggee is listening on */
55     private static String address;
56 
57     /*
58      * Find a connector by name
59      */
findConnector(String name)60     private static Connector findConnector(String name) {
61         List connectors = Bootstrap.virtualMachineManager().allConnectors();
62         Iterator iter = connectors.iterator();
63         while (iter.hasNext()) {
64             Connector connector = (Connector)iter.next();
65             if (connector.name().equals(name)) {
66                 return connector;
67             }
68         }
69         return null;
70     }
71 
72     /*
73      * Launch a server debuggee, detect debuggee listening port
74      */
launch(String class_name)75     private static Process launch(String class_name) throws Exception {
76         String args[] = new String[]{
77             "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=0",
78             class_name
79         };
80         args = VMConnection.insertDebuggeeVMOptions(args);
81 
82         ProcessBuilder launcher = ProcessTools.createJavaProcessBuilder(args);
83 
84         System.out.println(launcher.command().stream().collect(Collectors.joining(" ", "Starting: ", "")));
85 
86         Process p = ProcessTools.startProcess(
87             class_name,
88             launcher,
89             RunToExit::checkForError,
90             RunToExit::isTransportListening,
91             0,
92             TimeUnit.NANOSECONDS
93         );
94 
95         return p;
96     }
97 
98     /* warm-up predicate for debuggee */
99     private static Pattern listenRegexp = Pattern.compile("Listening for transport \\b(.+)\\b at address: \\b(.+)\\b");
100 
isTransportListening(String line)101     private static boolean isTransportListening(String line) {
102         Matcher m = listenRegexp.matcher(line);
103         if (!m.matches()) {
104             return false;
105         }
106         // address is 2nd group
107         address = m.group(2);
108         return true;
109     }
110 
checkForError(String line)111     private static void checkForError(String line) {
112         if (line.contains("ERROR:")) {
113             error_seen++;
114         }
115     }
116 
117     /*
118      * - Launch a server debuggee: server=y,suspend=y,address=0
119      * - detect the port debuggee is listening on
120      * - run it to VM death
121      * - verify we saw no error
122      */
main(String args[])123     public static void main(String args[]) throws Exception {
124         // launch the server debuggee
125         Process process = launch("Exit0");
126 
127         // attach to server debuggee and resume it so it can exit
128         AttachingConnector conn = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach");
129         Map conn_args = conn.defaultArguments();
130         Connector.IntegerArgument port_arg =
131             (Connector.IntegerArgument)conn_args.get("port");
132         port_arg.setValue(address);
133 
134         System.out.println("Connection arguments: " + conn_args);
135 
136         VirtualMachine vm = null;
137         while (vm == null) {
138             try {
139                 vm = conn.attach(conn_args);
140             } catch (ConnectException e) {
141                 e.printStackTrace(System.out);
142                 System.out.println("--- Debugee not ready. Retrying in 500ms. ---");
143                 Thread.sleep(500);
144             }
145         }
146 
147         // The first event is always a VMStartEvent, and it is always in
148         // an EventSet by itself.  Wait for it.
149         EventSet evtSet = vm.eventQueue().remove();
150         for (Event event: evtSet) {
151             if (event instanceof VMStartEvent) {
152                 break;
153             }
154             throw new RuntimeException("Test failed - debuggee did not start properly");
155         }
156         vm.eventRequestManager().deleteAllBreakpoints();
157         vm.resume();
158 
159         int exitCode = process.waitFor();
160 
161         // if the server debuggee ran cleanly, we assume we were clean
162         if (exitCode == 0 && error_seen == 0) {
163             System.out.println("Test passed - server debuggee cleanly terminated");
164         } else {
165             throw new RuntimeException("Test failed - server debuggee generated an error when it terminated, " +
166                 "exit code was " + exitCode + ", " + error_seen + " error(s) seen in debugee output.");
167         }
168     }
169 }
170