# HG changeset patch # User egahlin # Date 1401201244 -7200 # Node ID dbbd3423538ccb99bdd599d83292da5e754e83c0 # Parent 8e5e92e0530efe71d3ba00a9cf7866727d7ea858 Patch diff --git a/test/ProblemList.txt b/test/ProblemList.txt --- a/test/ProblemList.txt +++ b/test/ProblemList.txt @@ -253,9 +253,6 @@ # jdk_tools -# 8028474 -sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh generic-all - # Tests take too long, on sparcs see 7143279 tools/pack200/CommandLineTests.java solaris-all, macosx-all tools/pack200/Pack200Test.java solaris-all, macosx-all diff --git a/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java b/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java --- a/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java +++ b/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,228 +21,266 @@ * questions. */ -import java.util.concurrent.CountDownLatch; -import java.util.regex.*; -import java.util.*; +import java.io.IOException; import java.net.URISyntaxException; -import java.io.IOException; -import sun.jvmstat.monitor.*; -import sun.jvmstat.monitor.event.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Semaphore; -public class MonitorVmStartTerminate { +import jdk.testlibrary.OutputBuffer; +import jdk.testlibrary.ProcessTools; +import sun.jvmstat.monitor.MonitorException; +import sun.jvmstat.monitor.MonitoredHost; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.MonitoredVmUtil; +import sun.jvmstat.monitor.VmIdentifier; +import sun.jvmstat.monitor.event.HostEvent; +import sun.jvmstat.monitor.event.HostListener; +import sun.jvmstat.monitor.event.VmStatusChangeEvent; - private static final int SLEEPERS = 10; - private static final int SLEEPTIME = 5000; // sleep time for a sleeper - private static final int EXECINTERVAL = 3000; // wait time between exec's +/* + * @test + * @bug 4990825 + * @summary attach to external but local JVM processes + * @library /lib/testlibrary + * @run build jdk.testlibrary.ProcessTools + * @run main/othervm MonitorVmStartTerminate + */ - public static void main(String args[]) throws Exception { +/* - long now = System.currentTimeMillis(); + Tests starts ten Java processes, each with a unique id. - String sleeperArgs = SLEEPTIME + " " + now; - String sleeperPattern = "Sleeper " + sleeperArgs + " \\d+$"; + Each prcess creates a file named after the id and then it waits for + the test to remove the file, at which point the process exits + + The processes are monitored by the test to make sure notifications + are sent when they are started/stopped. + + To avoid Java processes beeing left behind, in case of an unexpected + failure, shutdown hooks are installed that remove files when the test + exits. If files are not removed due to a JVM crash, the Java processes + will exit themselved after 1000 s. + +*/ +public final class MonitorVmStartTerminate { + + private static final int PROCESS_COUNT = 10; + private static final int PROCESS_TIMEOUT_IN_SECONDS = 1000; + + public static void main(String... args) throws Exception { MonitoredHost host = MonitoredHost.getMonitoredHost("localhost"); - host.setInterval(200); + host.setInterval(1); // 1 ms - Matcher matcher = Pattern.compile(sleeperPattern).matcher(""); - SleeperListener listener = new SleeperListener(host, matcher, SLEEPERS); + String id = UUID.randomUUID().toString(); + + List javaProcesses = new ArrayList<>(); + for (int i = 0; i < PROCESS_COUNT; i++) { + javaProcesses.add(new JavaProcess(id + "_" + i)); + } + + Listener listener = new Listener(host, javaProcesses); host.addHostListener(listener); + for (JavaProcess javaProcess : javaProcesses) { + javaProcess.start(); + } - SleeperStarter ss = new SleeperStarter(SLEEPERS, EXECINTERVAL, - sleeperArgs); - ss.start(); + // Wait for all processes to start before terminating + // them, so pids are not reused within a poll interval. + System.out.println("Waiting for all processes to get started notification"); + listener.started.acquire(PROCESS_COUNT); - System.out.println("Waiting for " - + SLEEPERS + " sleepers to terminate"); - try { - ss.join(); - } catch (InterruptedException e) { - throw new Exception("Timed out waiting for sleepers"); + for (JavaProcess javaProcess : javaProcesses) { + javaProcess.terminate(); } - listener.waitForSleepersToStart(); - listener.waitForSleepersToTerminate(); + System.out.println("Waiting for all processes to get terminated notification"); + listener.terminated.acquire(PROCESS_COUNT); + + host.removeHostListener(listener); } - public static class SleeperListener implements HostListener { + private static final class Listener implements HostListener { + private final Semaphore started = new Semaphore(0); + private final Semaphore terminated = new Semaphore(0); + private final MonitoredHost host; + private final List processes; - private final List targets = new ArrayList<>(); - private final CountDownLatch terminateLatch; - private final CountDownLatch startLatch; - private final MonitoredHost host; - private final Matcher patternMatcher; - - public SleeperListener(MonitoredHost host, Matcher matcher, int count) { + public Listener(MonitoredHost host, List processes) { this.host = host; - this.patternMatcher = matcher; - this.terminateLatch = new CountDownLatch(count); - this.startLatch = new CountDownLatch(count); + this.processes = processes; + printStatus(); } - public void waitForSleepersToTerminate() throws InterruptedException { - terminateLatch.await(); + @Override + @SuppressWarnings("unchecked") + public void vmStatusChanged(VmStatusChangeEvent event) { + started.release(countProcesses(event.getStarted())); + terminated.release(countProcesses(event.getTerminated())); + printStatus(); } - public void waitForSleepersToStart() throws InterruptedException { - startLatch.await(); + private void printStatus() { + System.out.printf("started=%d, terminated=%d\n", + started.availablePermits(), terminated.availablePermits()); } - private void printList(Set list, String msg) { - System.out.println(msg + ":"); - for (Integer lvmid : list) { - try { - VmIdentifier vmid = new VmIdentifier("//" + lvmid.intValue()); - MonitoredVm target = host.getMonitoredVm(vmid); + @Override + public void disconnected(HostEvent arg0) { + // ignore + } - StringMonitor cmdMonitor = - (StringMonitor)target.findByName("sun.rt.javaCommand"); - String cmd = cmdMonitor.stringValue(); + private int countProcesses(Set ids) { + int count = 0; + for (Integer id : ids) { + for (JavaProcess jp : processes) { + if (hasMainArgs(id, jp.getUniqueId()) + || id.equals(jp.getPid())) { + jp.setPid(id); + count++; + } + } + } + return count; + } - System.out.println("\t" + lvmid.intValue() + ": " - + "\"" + cmd + "\"" + ": "); - } catch (URISyntaxException e) { - System.err.println("Unexpected URISyntaxException: " - + e.getMessage()); - } catch (MonitorException e) { - System.out.println("\t" + lvmid.intValue() - + ": error reading monitoring data: " - + " target possibly terminated?"); - } + private boolean hasMainArgs(Integer id, String args) { + try { + VmIdentifier vmid = new VmIdentifier("//" + id.intValue()); + MonitoredVm target = host.getMonitoredVm(vmid); + return args.equals(MonitoredVmUtil.mainArgs(target)); + } catch (URISyntaxException | MonitorException e) { + // ok. process probably not running + } + return false; + } + } + + public final static class JavaProcess { + + private static final class ShutdownHook extends Thread { + private final JavaProcess javaProcess; + + public ShutdownHook(JavaProcess javaProcess) { + this.javaProcess = javaProcess; + } + + public void run() { + javaProcess.terminate(); } } - - private int addStarted(Set started) { - int found = 0; - for (Integer lvmid : started) { - try { - VmIdentifier vmid = new VmIdentifier("//" + lvmid.intValue()); - MonitoredVm target = host.getMonitoredVm(vmid); - - StringMonitor cmdMonitor = - (StringMonitor)target.findByName("sun.rt.javaCommand"); - String cmd = cmdMonitor.stringValue(); - - patternMatcher.reset(cmd); - System.out.print("Started: " + lvmid.intValue() - + ": " + "\"" + cmd + "\"" + ": "); - - if (patternMatcher.matches()) { - System.out.println("matches pattern - recorded"); - targets.add(lvmid); - found++; - } - else { - System.out.println("does not match pattern - ignored"); - } - } catch (URISyntaxException e) { - System.err.println("Unexpected URISyntaxException: " - + e.getMessage()); - } catch (MonitorException e) { - System.err.println("Unexpected MonitorException: " - + e.getMessage()); - } - } - return found; - } - - private int removeTerminated(Set terminated) { - int found = 0; - for (Integer lvmid : terminated) { - /* - * we don't attempt to attach to the target here as it's - * now dead and has no jvmstat share memory file. Just see - * if the process id is among those that we saved when we - * started the targets (note - duplicated allowed and somewhat - * expected on windows); - */ - System.out.print("Terminated: " + lvmid.intValue() + ": "); - if (targets.contains(lvmid)) { - System.out.println("matches pattern - termination recorded"); - targets.remove(lvmid); - found++; - } - else { - System.out.println("does not match pattern - ignored"); - } - } - return found; - } - - @SuppressWarnings("unchecked") - public void vmStatusChanged(VmStatusChangeEvent ev) { - printList(ev.getActive(), "Active"); - printList(ev.getStarted(), "Started"); - printList(ev.getTerminated(), "Terminated"); - - int recentlyStarted = addStarted(ev.getStarted()); - int recentlyTerminated = removeTerminated(ev.getTerminated()); - - for (int i = 0; i < recentlyTerminated; i++) { - terminateLatch.countDown(); - } - for (int i = 0; i < recentlyStarted; i++) { - startLatch.countDown(); + public static void main(String[] args) throws InterruptedException { + try { + Path path = Paths.get(args[0]); + createFile(path); + waitForRemoval(path); + } catch (Throwable t) { + t.printStackTrace(); + System.exit(1); } } - public void disconnected(HostEvent ev) { - } - } - - public static class SleeperStarter extends Thread { - - private final JavaProcess[] processes; - private final int execInterval; - private final String args; - - public SleeperStarter(int sleepers, int execInterval, String args) { - this.execInterval = execInterval; - this.args = args; - this.processes = new JavaProcess[sleepers]; + private static void createFile(Path path) throws IOException { + Files.write(path, new byte[0], StandardOpenOption.CREATE); + if (!Files.exists(path)) { + throw new Error("Newly created file " + path + + " does not exist!"); + } } - private synchronized int active() { - int active = processes.length; - for(JavaProcess jp : processes) { + private static void waitForRemoval(Path path) { + + int count = 0; + // Timeout to avoid process hanging around + while (count < PROCESS_TIMEOUT_IN_SECONDS) { + System.out.print("Waiting for " + path + " to be removed, " + + count + " s"); try { - jp.exitValue(); - active--; - } catch (IllegalThreadStateException e) { - // process hasn't exited yet + Thread.sleep(1000); + } catch (InterruptedException e) { + // ignore } + if (!Files.exists(path)) { + return; + } + count++; } - return active; + System.exit(1); } - public void run() { - System.out.println("Starting " + processes.length + " sleepers"); + private final String uniqueIdentifier; + private final ShutdownHook shutdownHook; + private volatile Integer pid; - String[] classpath = { - "-classpath", - System.getProperty("java.class.path") - }; + public JavaProcess(String uniqueIdentifier) { + this.uniqueIdentifier = uniqueIdentifier; + this.shutdownHook = new ShutdownHook(this); + } - for (int i = 0; i < processes.length; i++) { - try { - System.out.println("Starting Sleeper " + i); - synchronized(this) { - processes[i] = new JavaProcess("Sleeper", args + " " + i); - processes[i].addOptions(classpath); - } - processes[i].start(); - Thread.sleep(execInterval); - } catch (InterruptedException ignore) { - } catch (IOException e) { - System.err.println( - "IOException trying to start Sleeper " + i + ": " - + e.getMessage()); - } - } + public Integer getPid() { + return pid; + } - // spin waiting for the processes to terminate - while (active() > 0) ; + public void setPid(Integer id) { + this.pid = id; + } + + /** + * Starts a Java process asynchronously. + * + * The process runs until {@link #stop()} is called. If test exits + * unexpectedly the process will be cleaned up by a shutdown hook. + * + * @throws Exception + */ + public void start() throws Exception { + Runtime.getRuntime().addShutdownHook(shutdownHook); + System.out.println("Starting " + getUniqueId()); + + Runnable r = new Runnable() { + @Override + public void run() { + try { + executeJava(); + } catch (Throwable t) { + t.printStackTrace(); + } + } + }; + new Thread(r).start(); + } + + public void terminate() { + try { + System.out.println("Terminating " + uniqueIdentifier); + Files.delete(Paths.get(uniqueIdentifier)); + } catch (IOException e) { + e.printStackTrace(); + } + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } + + private void executeJava() throws Exception, IOException { + String className = JavaProcess.class.getName(); + String classPath = System.getProperty("test.classes"); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-cp", + classPath, className, uniqueIdentifier); + OutputBuffer ob = ProcessTools.getOutput(pb.start()); + System.out.println("Java Process " + getUniqueId() + " stder:" + + ob.getStderr()); + System.err.println("Java Process " + getUniqueId() + " stdout:" + + ob.getStdout()); + } + + public String getUniqueId() { + return uniqueIdentifier; } } } - diff --git a/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh b/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh deleted file mode 100644 --- a/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# @test -# @bug 4990825 -# @summary attach to external but local JVM processes -# @library ../../testlibrary -# @build Sleeper -# @build JavaProcess -# @build MonitorVmStartTerminate -# @run shell MonitorVmStartTerminate.sh -# - -. ${TESTSRC-.}/../../testlibrary/utils.sh - -setup -verify_os - -JAVA="${TESTJAVA}/bin/java" -CP=${TESTJAVA}${FS}lib${FS}tools.jar${PS}${TESTCLASSES}${PS}${TESTCLASSES}${FS}..${FS}..${FS}testlibrary - -${JAVA} ${TESTVMOPTS} -classpath ${CP} MonitorVmStartTerminate diff --git a/test/sun/jvmstat/testlibrary/JavaProcess.java b/test/sun/jvmstat/testlibrary/JavaProcess.java deleted file mode 100644 --- a/test/sun/jvmstat/testlibrary/JavaProcess.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * - */ - -import java.io.*; - -public class JavaProcess { - - protected Process process = null; - - private String classname; - private StringBuilder classArgs; - private StringBuilder javaOptions; - - private static String java = System.getProperty("java.home") - + File.separator + "bin" - + File.separator + "java"; - - public JavaProcess(String classname) { - this(classname, "", ""); - } - - public JavaProcess(String classname, String classArgs) { - this(classname, "", classArgs); - } - - public JavaProcess(String classname, String javaOptions, String classArgs) { - this.classname = classname; - this.javaOptions = new StringBuilder(javaOptions); - this.classArgs = new StringBuilder(classArgs); - } - - /** - * add java options to the java command - */ - public void addOptions(String[] opts) { - if (javaOptions != null && javaOptions.length() > 0) { - javaOptions.append(" "); - } - - for (int i = 0; i < opts.length; i++) { - if (i != 0) { - javaOptions.append(" "); - } - javaOptions.append(opts[i]); - } - } - - /** - * add arguments to the class arguments - */ - public void addArguments(String[] args) { - if (classArgs != null && classArgs.length() > 0) { - classArgs.append(" "); - } - - for (int i = 0; i < args.length; i++) { - if (i != 0) { - classArgs.append(" "); - } - classArgs.append(args[i]); - } - } - - /** - * start the java process - */ - public void start() throws IOException { - if (process != null) { - return; - } - - String javaCommand = java + " " + javaOptions + " " - + classname + " " + classArgs; - - System.out.println("exec'ing: " + javaCommand); - - process = Runtime.getRuntime().exec(javaCommand); - } - - /** - * destroy the java process - */ - public void destroy() { - if (process != null) { - process.destroy(); - } - process = null; - } - - public int exitValue() { - if (process != null) { - return process.exitValue(); - } - throw new RuntimeException("exitValue called with process == null"); - } - - public InputStream getErrorStream() { - if (process != null) { - return process.getErrorStream(); - } - throw new RuntimeException( - "getErrorStream() called with process == null"); - } - - public InputStream getInputStream() { - if (process != null) { - return process.getInputStream(); - } - throw new RuntimeException( - "getInputStream() called with process == null"); - } - - public OutputStream getOutputStream() { - if (process != null) { - return process.getOutputStream(); - } - throw new RuntimeException( - "getOutputStream() called with process == null"); - } - - public int waitFor() throws InterruptedException { - if (process != null) { - return process.waitFor(); - } - throw new RuntimeException("waitFor() called with process == null"); - } -} diff --git a/test/sun/jvmstat/testlibrary/Sleeper.java b/test/sun/jvmstat/testlibrary/Sleeper.java deleted file mode 100644 --- a/test/sun/jvmstat/testlibrary/Sleeper.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * - */ -public class Sleeper { - - private static final String USAGE = "Sleeper [ms]"; - - public static void main(String argv[]) { - - long sleep = Long.MAX_VALUE; // sleep indefinately if no arg is given - - // if argv[0] is present, use it to set the sleep time. ignore - // all other arguments - some tests pass additional arguments to - // differentiate or associate multiple sleeper processes. - if (argv.length >= 1) { - try { - sleep = Long.parseLong(argv[0]); - } - catch (NumberFormatException e) { - System.err.println(USAGE); - System.exit(1); - } - } - - try { Thread.sleep(sleep); } catch (InterruptedException e) {}; - } -}