1 /* 2 * Copyright (c) 2014, 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 import java.io.File; 25 import java.io.BufferedReader; 26 import java.io.IOException; 27 import java.io.UncheckedIOException; 28 import java.lang.ProcessBuilder; 29 import java.nio.file.Files; 30 import java.nio.file.Path; 31 import java.nio.file.Paths; 32 import java.nio.file.attribute.UserPrincipal; 33 import java.time.Duration; 34 import java.time.Instant; 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.List; 38 import java.util.Objects; 39 import java.util.Optional; 40 import java.util.Random; 41 import java.util.concurrent.TimeUnit; 42 43 import jdk.testlibrary.Platform; 44 import jdk.testlibrary.Utils; 45 46 import org.testng.Assert; 47 import org.testng.annotations.Test; 48 import org.testng.TestNG; 49 50 /* 51 * @test 52 * @build jdk.testlibrary.* 53 * @library /lib/testlibrary 54 * @summary Functions of ProcessHandle.Info 55 * @author Roger Riggs 56 */ 57 58 public class InfoTest { 59 60 static String whoami; 61 62 static { 63 try { 64 // Create a file and take the username from the file 65 Path p = Paths.get("OwnerName.tmp"); 66 Files.createFile(p); 67 UserPrincipal owner = Files.getOwner(p); 68 whoami = owner.getName(); 69 Files.delete(p); 70 } catch (IOException ex) { 71 ex.printStackTrace(); 72 throw new UncheckedIOException("tmp file", ex); 73 } 74 } 75 76 // Main can be used to run the tests from the command line with only testng.jar. 77 @SuppressWarnings("raw_types") 78 public static void main(String[] args) { 79 Class<?>[] testclass = {InfoTest.class}; 80 TestNG testng = new TestNG(); 81 testng.setTestClasses(testclass); 82 testng.run(); 83 } 84 85 /** 86 * Test that cputime used shows up in ProcessHandle.info 87 */ 88 @Test 89 public static void test1() { 90 System.out.println("Note: when run in samevm mode the cputime of the " + 91 "test runner is included."); 92 ProcessHandle self = ProcessHandle.current(); 93 94 Duration someCPU = Duration.ofMillis(200L); 95 Instant end = Instant.now().plus(someCPU); 96 while (Instant.now().isBefore(end)) { 97 // waste the cpu 98 } 99 ProcessHandle.Info info = self.info(); 100 System.out.printf(" info: %s%n", info); 101 Optional<Duration> totalCpu = info.totalCpuDuration(); 102 if (totalCpu.isPresent() && (totalCpu.get().compareTo(someCPU) < 0)) { 103 Assert.fail("reported cputime less than expected: " + someCPU + ", " + 104 "actual: " + info.totalCpuDuration()); 105 } 106 } 107 108 /** 109 * Spawn a child with arguments and check they are visible via the ProcessHandle. 110 */ 111 @Test 112 public static void test2() { 113 try { 114 long cpuLoopTime = 100; // 100 ms 115 String[] extraArgs = {"pid", "parent", "stdin"}; 116 JavaChild p1 = JavaChild.spawnJavaChild((Object[])extraArgs); 117 Instant afterStart = Instant.now(); 118 119 try (BufferedReader lines = p1.outputReader()) { 120 Duration lastCpu = Duration.ofMillis(0L); 121 for (int j = 0; j < 10; j++) { 122 123 p1.sendAction("cpuloop", cpuLoopTime); 124 p1.sendAction("cputime", ""); 125 126 // Read cputime from child 127 Duration childCpuTime = null; 128 // Read lines from the child until the result from cputime is returned 129 String s; 130 while ((s = lines.readLine()) != null) { 131 String[] split = s.trim().split(" "); 132 if (split.length == 3 && split[1].equals("cputime")) { 133 long nanos = Long.valueOf(split[2]); 134 childCpuTime = Duration.ofNanos(nanos); 135 break; // found the result we're looking for 136 } 137 } 138 139 140 ProcessHandle.Info info = p1.info(); 141 System.out.printf(" info: %s%n", info); 142 143 if (info.user().isPresent()) { 144 String user = info.user().get(); 145 Assert.assertNotNull(user, "User name"); 146 Assert.assertEquals(user, whoami, "User name"); 147 } 148 149 Optional<String> command = info.command(); 150 if (command.isPresent()) { 151 String javaExe = System.getProperty("test.jdk") + 152 File.separator + "bin" + File.separator + "java"; 153 String expected = Platform.isWindows() ? javaExe + ".exe" : javaExe; 154 Path expectedPath = Paths.get(expected); 155 Path actualPath = Paths.get(command.get()); 156 Assert.assertTrue(Files.isSameFile(expectedPath, actualPath), 157 "Command: expected: " + javaExe + ", actual: " + command.get()); 158 } 159 160 if (info.arguments().isPresent()) { 161 String[] args = info.arguments().get(); 162 163 if (Platform.isLinux() || Platform.isOSX()) { 164 int offset = args.length - extraArgs.length; 165 for (int i = 0; i < extraArgs.length; i++) { 166 Assert.assertEquals(args[offset + i], extraArgs[i], 167 "Actual argument mismatch, index: " + i); 168 } 169 } else if (Platform.isSolaris()) { 170 Assert.assertEquals(args.length, 1, 171 "Expected argument list length: 1"); 172 Assert.assertNotNull(args[0], 173 "Expected an argument"); 174 } else { 175 System.out.printf("No argument test for OS: %s%n", Platform.getOsName()); 176 } 177 178 // Now check that the first argument is not the same as the executed command 179 if (args.length > 0) { 180 Assert.assertNotEquals(args[0], command, 181 "First argument should not be the executable: args[0]: " 182 + args[0] + ", command: " + command); 183 } 184 } 185 186 if (info.totalCpuDuration().isPresent()) { 187 Duration totalCPU = info.totalCpuDuration().get(); 188 Duration epsilon = Duration.ofMillis(200L); 189 if (childCpuTime != null) { 190 System.out.printf(" info.totalCPU: %s, childCpuTime: %s, diff: %s%n", 191 totalCPU.toNanos(), childCpuTime.toNanos(), 192 childCpuTime.toNanos() - totalCPU.toNanos()); 193 Assert.assertTrue(checkEpsilon(childCpuTime, totalCPU, epsilon), 194 childCpuTime + " should be within " + 195 epsilon + " of " + totalCPU); 196 } 197 Assert.assertTrue(totalCPU.toNanos() > 0L, 198 "total cpu time expected > 0ms, actual: " + totalCPU); 199 long t = Utils.adjustTimeout(10L); // Adjusted timeout seconds 200 Assert.assertTrue(totalCPU.toNanos() < lastCpu.toNanos() + t * 1_000_000_000L, 201 "total cpu time expected < " + t 202 + " seconds more than previous iteration, actual: " 203 + (totalCPU.toNanos() - lastCpu.toNanos())); 204 lastCpu = totalCPU; 205 } 206 207 if (info.startInstant().isPresent()) { 208 Instant startTime = info.startInstant().get(); 209 Assert.assertTrue(startTime.isBefore(afterStart), 210 "startTime after process spawn completed" 211 + startTime + " + > " + afterStart); 212 } 213 } 214 } 215 p1.waitFor(Utils.adjustTimeout(5), TimeUnit.SECONDS); 216 } catch (IOException | InterruptedException ie) { 217 ie.printStackTrace(System.out); 218 Assert.fail("unexpected exception", ie); 219 } 220 } 221 222 /** 223 * Spawn a child with arguments and check they are visible via the ProcessHandle. 224 */ 225 @Test 226 public static void test3() { 227 try { 228 for (int sleepTime : Arrays.asList(1, 2)) { 229 Process p = spawn("sleep", String.valueOf(sleepTime)); 230 ProcessHandle.Info info = p.info(); 231 System.out.printf(" info: %s%n", info); 232 233 if (info.user().isPresent()) { 234 String user = info.user().get(); 235 Assert.assertNotNull(user); 236 Assert.assertEquals(user, whoami); 237 } 238 if (info.command().isPresent()) { 239 String command = info.command().get(); 240 String expected = Platform.isWindows() ? "sleep.exe" : "sleep"; 241 Assert.assertTrue(command.endsWith(expected), "Command: expected: \'" + 242 expected + "\', actual: " + command); 243 244 // Verify the command exists and is executable 245 File exe = new File(command); 246 Assert.assertTrue(exe.exists(), "command must exist: " + exe); 247 Assert.assertTrue(exe.canExecute(), "command must be executable: " + exe); 248 } 249 if (info.arguments().isPresent()) { 250 String[] args = info.arguments().get(); 251 if (args.length > 0) { 252 Assert.assertEquals(args[0], String.valueOf(sleepTime)); 253 } 254 } 255 Assert.assertTrue(p.waitFor(15, TimeUnit.SECONDS)); 256 } 257 } catch (IOException | InterruptedException ex) { 258 ex.printStackTrace(System.out); 259 } finally { 260 // Destroy any children that still exist 261 ProcessUtil.destroyProcessTree(ProcessHandle.current()); 262 } 263 } 264 265 /** 266 * Cross check the cputime reported from java.management with that for the current process. 267 */ 268 @Test 269 public static void test4() { 270 Duration myCputime1 = ProcessUtil.MXBeanCpuTime(); 271 272 Optional<Duration> dur1 = ProcessHandle.current().info().totalCpuDuration(); 273 274 Duration myCputime2 = ProcessUtil.MXBeanCpuTime(); 275 276 Optional<Duration> dur2 = ProcessHandle.current().info().totalCpuDuration(); 277 278 if (dur1.isPresent() && dur2.isPresent()) { 279 Duration total1 = dur1.get(); 280 Duration total2 = dur2.get(); 281 System.out.printf(" total1 vs. mbean: %s, getProcessCpuTime: %s, diff: %s%n", 282 Objects.toString(total1), myCputime1, myCputime1.minus(total1)); 283 System.out.printf(" total2 vs. mbean: %s, getProcessCpuTime: %s, diff: %s%n", 284 Objects.toString(total2), myCputime2, myCputime2.minus(total2)); 285 286 Duration epsilon = Duration.ofMillis(200L); // Epsilon is 200ms. 287 Assert.assertTrue(checkEpsilon(myCputime1, myCputime2, epsilon), 288 myCputime1.toNanos() + " should be within " + epsilon 289 + " of " + myCputime2.toNanos()); 290 Assert.assertTrue(checkEpsilon(total1, total2, epsilon), 291 total1.toNanos() + " should be within " + epsilon 292 + " of " + total2.toNanos()); 293 Assert.assertTrue(checkEpsilon(myCputime1, total1, epsilon), 294 myCputime1.toNanos() + " should be within " + epsilon 295 + " of " + total1.toNanos()); 296 Assert.assertTrue(checkEpsilon(total1, myCputime2, epsilon), 297 total1.toNanos() + " should be within " + epsilon 298 + " of " + myCputime2.toNanos()); 299 Assert.assertTrue(checkEpsilon(myCputime2, total2, epsilon), 300 myCputime2.toNanos() + " should be within " + epsilon 301 + " of " + total2.toNanos()); 302 } 303 } 304 305 @Test 306 public static void test5() { 307 ProcessHandle self = ProcessHandle.current(); 308 Random r = new Random(); 309 for (int i = 0; i < 30; i++) { 310 Instant end = Instant.now().plusMillis(500L); 311 while (end.isBefore(Instant.now())) { 312 // burn the cpu time checking the time 313 long x = r.nextLong(); 314 } 315 if (self.info().totalCpuDuration().isPresent()) { 316 Duration totalCpu = self.info().totalCpuDuration().get(); 317 long infoTotalCputime = totalCpu.toNanos(); 318 long beanCputime = ProcessUtil.MXBeanCpuTime().toNanos(); 319 System.out.printf(" infoTotal: %12d, beanCpu: %12d, diff: %12d%n", 320 infoTotalCputime, beanCputime, beanCputime - infoTotalCputime); 321 } else { 322 break; // nothing to compare; continue 323 } 324 } 325 } 326 /** 327 * Check two Durations, the second should be greater than the first or 328 * within the supplied Epsilon. 329 * @param d1 a Duration - presumed to be shorter 330 * @param d2 a 2nd Duration - presumed to be greater (or within Epsilon) 331 * @param epsilon Epsilon the amount of overlap allowed 332 * @return true if d2 is greater than d1 or within epsilon, false otherwise 333 */ 334 static boolean checkEpsilon(Duration d1, Duration d2, Duration epsilon) { 335 if (d1.toNanos() <= d2.toNanos()) { 336 return true; 337 } 338 Duration diff = d1.minus(d2).abs(); 339 return diff.compareTo(epsilon) <= 0; 340 } 341 342 /** 343 * Spawn a native process with the provided arguments. 344 * @param command the executable of native process 345 * @param args to start a new process 346 * @return the Process that was started 347 * @throws IOException thrown by ProcessBuilder.start 348 */ 349 static Process spawn(String command, String... args) throws IOException { 350 ProcessBuilder pb = new ProcessBuilder(); 351 pb.inheritIO(); 352 List<String> list = new ArrayList<>(); 353 list.add(command); 354 for (String arg : args) 355 list.add(arg); 356 pb.command(list); 357 return pb.start(); 358 } 359 }