1 /*
   2  * Copyright (c) 2004, 2015, 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  *
  28  * @library /lib/testlibrary
  29  * @modules java.management
  30  *          jdk.jdi
  31  *
  32  * @build jdk.testlibrary.* VMConnection RunToExit Exit0
  33  * @run driver RunToExit
  34  */
  35 import java.net.ServerSocket;
  36 import com.sun.jdi.Bootstrap;
  37 import com.sun.jdi.VirtualMachine;
  38 import com.sun.jdi.event.*;
  39 import com.sun.jdi.connect.Connector;
  40 import com.sun.jdi.connect.AttachingConnector;
  41 import java.net.ConnectException;
  42 import java.util.Map;
  43 import java.util.List;
  44 import java.util.Iterator;
  45 import java.util.concurrent.TimeUnit;
  46 import java.util.stream.Collectors;
  47 import jdk.testlibrary.ProcessTools;
  48 
  49 public class RunToExit {
  50 
  51     /* Increment this when ERROR: seen */
  52     static volatile int error_seen = 0;
  53     static volatile boolean ready = false;
  54 
  55     /*
  56      * Find a connector by name
  57      */
  58     private static Connector findConnector(String name) {
  59         List connectors = Bootstrap.virtualMachineManager().allConnectors();
  60         Iterator iter = connectors.iterator();
  61         while (iter.hasNext()) {
  62             Connector connector = (Connector)iter.next();
  63             if (connector.name().equals(name)) {
  64                 return connector;
  65             }
  66         }
  67         return null;
  68     }
  69 
  70     /*
  71      * Launch a server debuggee with the given address
  72      */
  73     private static Process launch(String address, String class_name) throws Exception {
  74         String args[] = new String[]{
  75             "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address="
  76                 + address,
  77             class_name
  78         };
  79         args = VMConnection.insertDebuggeeVMOptions(args);
  80 
  81         ProcessBuilder launcher = ProcessTools.createJavaProcessBuilder(args);
  82 
  83         System.out.println(launcher.command().stream().collect(Collectors.joining(" ", "Starting: ", "")));
  84 
  85         Process p = ProcessTools.startProcess(
  86             class_name,
  87             launcher,
  88             RunToExit::checkForError,
  89             RunToExit::isTransportListening,
  90             0,
  91             TimeUnit.NANOSECONDS
  92         );
  93 
  94         return p;
  95     }
  96 
  97     private static boolean isTransportListening(String line) {
  98         return line.startsWith("Listening for transport dt_socket");
  99     }
 100 
 101     private static void checkForError(String line) {
 102         if (line.contains("ERROR:")) {
 103             error_seen++;
 104         }
 105     }
 106 
 107     /*
 108      * - pick a TCP port
 109      * - Launch a server debuggee: server=y,suspend=y,address=${port}
 110      * - run it to VM death
 111      * - verify we saw no error
 112      */
 113     public static void main(String args[]) throws Exception {
 114         // find a free port
 115         ServerSocket ss = new ServerSocket(0);
 116         int port = ss.getLocalPort();
 117         ss.close();
 118 
 119         String address = String.valueOf(port);
 120 
 121         // launch the server debuggee
 122         Process process = launch(address, "Exit0");
 123 
 124         // attach to server debuggee and resume it so it can exit
 125         AttachingConnector conn = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach");
 126         Map conn_args = conn.defaultArguments();
 127         Connector.IntegerArgument port_arg =
 128             (Connector.IntegerArgument)conn_args.get("port");
 129         port_arg.setValue(port);
 130 
 131         System.out.println("Connection arguments: " + conn_args);
 132 
 133         VirtualMachine vm = null;
 134         while (vm == null) {
 135             try {
 136                 vm = conn.attach(conn_args);
 137             } catch (ConnectException e) {
 138                 e.printStackTrace(System.out);
 139                 System.out.println("--- Debugee not ready. Retrying in 500ms. ---");
 140                 Thread.sleep(500);
 141             }
 142         }
 143 
 144         // The first event is always a VMStartEvent, and it is always in
 145         // an EventSet by itself.  Wait for it.
 146         EventSet evtSet = vm.eventQueue().remove();
 147         for (Event event: evtSet) {
 148             if (event instanceof VMStartEvent) {
 149                 break;
 150             }
 151             throw new RuntimeException("Test failed - debuggee did not start properly");
 152         }
 153         vm.eventRequestManager().deleteAllBreakpoints();
 154         vm.resume();
 155 
 156         int exitCode = process.waitFor();
 157 
 158         // if the server debuggee ran cleanly, we assume we were clean
 159         if (exitCode == 0 && error_seen == 0) {
 160             System.out.println("Test passed - server debuggee cleanly terminated");
 161         } else {
 162             throw new RuntimeException("Test failed - server debuggee generated an error when it terminated, " +
 163                 "exit code was " + exitCode + ", " + error_seen + " error(s) seen in debugee output.");
 164         }
 165     }
 166 }