1 import static java.io.File.createTempFile; 2 import static java.lang.Long.parseLong; 3 import static java.lang.System.getProperty; 4 import static java.lang.management.ManagementFactory.getOperatingSystemMXBean; 5 import static java.nio.file.Files.readAllBytes; 6 import static jdk.test.lib.process.ProcessTools.createJavaProcessBuilder; 7 8 import java.io.File; 9 import java.io.IOException; 10 11 import com.sun.management.UnixOperatingSystemMXBean; 12 13 /* 14 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 15 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 16 * 17 * This code is free software; you can redistribute it and/or modify it 18 * under the terms of the GNU General Public License version 2 only, as 19 * published by the Free Software Foundation. 20 * 21 * This code is distributed in the hope that it will be useful, but WITHOUT 22 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 23 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 24 * version 2 for more details (a copy is included in the LICENSE file that 25 * accompanied this code). 26 * 27 * You should have received a copy of the GNU General Public License version 28 * 2 along with this work; if not, write to the Free Software Foundation, 29 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 30 * 31 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 32 * or visit www.oracle.com if you need additional information or have any 33 * questions. 34 */ 35 36 /* 37 * @test TestInheritFD 38 * @bug 8176717 8176809 39 * @summary a new process should not inherit open file descriptors 40 * @library /test/lib 41 * @modules java.base/jdk.internal.misc 42 * java.management 43 */ 44 45 /** 46 * Test that HotSpot does not leak logging file descriptors. 47 * 48 * This test is performed in three steps. The first VM starts a second VM with 49 * gc logging enabled. The second VM starts a third VM and redirects the third 50 * VMs output to the first VM, it then exits and hopefully closes its log file. 51 * 52 * The third VM waits for the second to exit and close its log file. After that, 53 * the third VM tries to rename the log file of the second VM. If it succeeds in 54 * doing so it means that the third VM did not inherit the open log file 55 * (windows can not rename opened files easily) 56 * 57 * The third VM communicates the success to rename the file by printing "CLOSED 58 * FD". The first VM checks that the string was printed by the third VM. 59 * 60 * On unix like systems, UnixOperatingSystemMXBean is used to check open file 61 * descriptors. 62 */ 63 64 public class TestInheritFD { 65 66 public static final String LEAKS_FD = "VM RESULT => LEAKS FD"; 67 public static final String RETAINS_FD = "VM RESULT => RETAINS FD"; 68 public static final String EXIT = "VM RESULT => VM EXIT"; 69 70 // first VM 71 public static void main(String[] args) throws Exception { 72 String logPath = createTempFile("logging", ".log").getName(); 73 File commFile = createTempFile("communication", ".txt"); 74 75 ProcessBuilder pb = createJavaProcessBuilder( 76 "-Xlog:gc:\"" + logPath + "\"", 77 "-Dtest.jdk=" + getProperty("test.jdk"), 78 VMStartedWithLogging.class.getName(), 79 logPath); 80 81 pb.redirectOutput(commFile); // use temp file to communicate between processes 82 pb.start(); 83 84 String out = ""; 85 do { 86 out = new String(readAllBytes(commFile.toPath())); 87 Thread.sleep(100); 88 System.out.println("SLEEP 100 millis"); 89 } while (!out.contains(EXIT)); 90 91 System.out.println(out); 92 if (out.contains(RETAINS_FD)) { 93 System.out.println("Log file was not inherited by third VM"); 94 } else { 95 throw new RuntimeException("could not match: " + RETAINS_FD); 96 } 97 } 98 99 static class VMStartedWithLogging { 100 // second VM 101 public static void main(String[] args) throws IOException, InterruptedException { 102 ProcessBuilder pb = createJavaProcessBuilder( 103 "-Dtest.jdk=" + getProperty("test.jdk"), 104 VMShouldNotInheritFileDescriptors.class.getName(), 105 args[0], 106 "" + ProcessHandle.current().pid(), 107 "" + (supportsUnixMXBean()?+unixNrFD():-1)); 108 pb.inheritIO(); // in future, redirect information from third VM to first VM 109 pb.start(); 110 } 111 } 112 113 static class VMShouldNotInheritFileDescriptors { 114 // third VM 115 public static void main(String[] args) throws InterruptedException { 116 File logFile = new File(args[0]); 117 long parentPid = parseLong(args[1]); 118 long parentFDCount = parseLong(args[2]); 119 120 if(supportsUnixMXBean()){ 121 long thisFDCount = unixNrFD(); 122 System.out.println("This VM FD-count (" + thisFDCount + ") should be strictly less than parent VM FD-count (" + parentFDCount + ") as log file should have been closed"); 123 System.out.println(thisFDCount<parentFDCount?RETAINS_FD:LEAKS_FD); 124 } else if (getProperty("os.name").toLowerCase().contains("win")) { 125 windows(logFile, parentPid); 126 } else { 127 System.out.println(LEAKS_FD); // default fail on unknown configuration 128 } 129 System.out.println(EXIT); 130 } 131 } 132 133 static boolean supportsUnixMXBean() { 134 return getOperatingSystemMXBean() instanceof UnixOperatingSystemMXBean; 135 } 136 137 static long unixNrFD() { 138 UnixOperatingSystemMXBean osBean = (UnixOperatingSystemMXBean) getOperatingSystemMXBean(); 139 return osBean.getOpenFileDescriptorCount(); 140 } 141 142 static void windows(File f, long parentPid) throws InterruptedException { 143 System.out.println("waiting for pid: " + parentPid); 144 ProcessHandle.of(parentPid).ifPresent(handle -> handle.onExit().join()); 145 System.out.println("trying to rename file to the same name: " + f); 146 System.out.println(f.renameTo(f)?RETAINS_FD:LEAKS_FD); // this parts communicates a closed file descriptor by printing "CLOSED FD" 147 }