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