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