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