--- old/test/hotspot/jtreg/ProblemList.txt 2019-08-12 15:08:04.000000000 -0700 +++ new/test/hotspot/jtreg/ProblemList.txt 2019-08-12 15:08:04.000000000 -0700 @@ -150,8 +150,6 @@ # :hotspot_containers -containers/docker/TestJcmdWithSideCar.java 8228850,8228960 generic-all - ############################################################################# --- old/test/hotspot/jtreg/containers/docker/EventGeneratorLoop.java 2019-08-12 15:08:05.000000000 -0700 +++ new/test/hotspot/jtreg/containers/docker/EventGeneratorLoop.java 2019-08-12 15:08:05.000000000 -0700 @@ -20,14 +20,16 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import jdk.jfr.Event; import jdk.jfr.Description; import jdk.jfr.Label; -// This class generates simple event in a loop -// for a specified time. -// Pass the time in seconds as a parameter. +// This class generates simple event in a loop for a specified time. public class EventGeneratorLoop { @Label("SimpleEvent") @@ -41,12 +43,19 @@ } + // Arguments: + // - time to run (in seconds) - mandatory + // - full file path to signal the start of main - optional public static void main(String[] args) throws Exception { if ((args.length < 1) || (args[0] == null)) { throw new IllegalArgumentException("Expecting one argument: time to run (seconds)"); } int howLong = Integer.parseInt(args[0]); + if ((args.length > 1) && (args[1] != null)) { + signalStartOfMain(args[1]); + } + for (int i=0; i < howLong; i++) { SimpleEvent ev = new SimpleEvent(); ev.msg = "Hello"; @@ -57,4 +66,10 @@ System.out.print("."); } } + + private static void signalStartOfMain(String fileName) throws Exception { + Path p = Paths.get(fileName); + Files.createFile(p); + p.toFile().deleteOnExit(); +} } --- old/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java 2019-08-12 15:08:06.000000000 -0700 +++ new/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java 2019-08-12 15:08:06.000000000 -0700 @@ -37,6 +37,7 @@ * @build EventGeneratorLoop * @run driver TestJcmdWithSideCar */ +import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; @@ -55,6 +56,7 @@ private static final String IMAGE_NAME = Common.imageName("jfr-jcmd"); private static final int TIME_TO_RUN_MAIN_PROCESS = (int) (30 * Utils.TIMEOUT_FACTOR); // seconds private static final String MAIN_CONTAINER_NAME = "test-container-main"; + private static final String MAIN_METHOD_STARTED_SIGNAL_FILE = "mainMethodStarted.signal"; public static void main(String[] args) throws Exception { if (!DockerTestUtils.canTestDocker()) { @@ -70,12 +72,15 @@ waitForMainContainerToStart(500, 10); t.checkForErrors(); + waitForContainerMainMethod(); - OutputAnalyzer jcmdOut = testCase01(); - long mainProcPid = findProcess(jcmdOut, "EventGeneratorLoop"); + long mainProcPid = testCase01(); - t.assertIsAlive(); - testCase02(mainProcPid); + // Excluding the test case below until JDK-8228850 is fixed + // JDK-8228850: jhsdb jinfo fails with ClassCastException: + // s.j.h.oops.TypeArray cannot be cast to s.j.h.oops.Instance + // t.assertIsAlive(); + // testCase02(mainProcPid); // JCMD does not work in sidecar configuration, except for "jcmd -l". // Including this test case to assist in reproduction of the problem. @@ -90,12 +95,17 @@ } - // Run "jcmd -l" in a sidecar container and find a process that runs EventGeneratorLoop - private static OutputAnalyzer testCase01() throws Exception { - return runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "-l") + // Run "jcmd -l" in a sidecar container, find a target process. + private static long testCase01() throws Exception { + OutputAnalyzer out = runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "-l") .shouldHaveExitValue(0) - .shouldContain("sun.tools.jcmd.JCmd") - .shouldContain("EventGeneratorLoop"); + .shouldContain("sun.tools.jcmd.JCmd"); + long pid = findProcess(out, "EventGeneratorLoop"); + if (pid == -1) { + throw new RuntimeException("Could not find specified process"); + } + + return pid; } // run jhsdb jinfo (jhsdb uses PTRACE) @@ -122,9 +132,11 @@ DockerRunOptions opts = commonDockerOpts("EventGeneratorLoop"); opts.addDockerOpts("--cap-add=SYS_PTRACE") .addDockerOpts("--name", MAIN_CONTAINER_NAME) - .addDockerOpts("-v", "/tmp") + .addDockerOpts("--volume", "/tmp") + .addDockerOpts("--volume", Paths.get(".").toAbsolutePath() + ":/workdir/") .addJavaOpts("-XX:+UsePerfData") - .addClassOptions("" + TIME_TO_RUN_MAIN_PROCESS); + .addClassOptions("" + TIME_TO_RUN_MAIN_PROCESS) + .addClassOptions("/workdir/" + MAIN_METHOD_STARTED_SIGNAL_FILE); DockerThread t = new DockerThread(opts); t.start(); @@ -134,9 +146,7 @@ private static void waitForMainContainerToStart(int delayMillis, int count) throws Exception { boolean started = false; for(int i=0; i < count; i++) { - try { - Thread.sleep(delayMillis); - } catch (InterruptedException e) {} + sleep(delayMillis); if (isMainContainerRunning()) { started = true; break; @@ -176,13 +186,14 @@ return DockerTestUtils.execute(cmd); } + // Returns PID if a matching process, or -1 if not found. private static long findProcess(OutputAnalyzer out, String name) throws Exception { List l = out.asLines() .stream() .filter(s -> s.contains(name)) .collect(Collectors.toList()); if (l.isEmpty()) { - throw new RuntimeException("Could not find matching process"); + return -1; } String psInfo = l.get(0); System.out.println("findProcess(): psInfo: " + psInfo); @@ -197,6 +208,37 @@ .addJavaOpts("-cp", "/test-classes/"); } + // Wait for the "main()" method in the container to start. + private static void waitForContainerMainMethod() { + waitForSignalFile(MAIN_METHOD_STARTED_SIGNAL_FILE, 10*1000, 500); + } + + // Wait for a file in a specified location (aka signal file) to be created. + // fn - file name to wait for + // howLong - howLong (milliseconds) + // delay - delay between the attempts (milliseconds) + private static void waitForSignalFile(String fn, long howLong, long delay) { + long expiration = System.currentTimeMillis() + howLong; + File f = Paths.get(".", fn).toFile(); + + do { + if (f.exists()) { + return; + } + sleep(delay); + } while (System.currentTimeMillis() < expiration); + + String msg = "Timed out while waiting for a signal file " + fn; + throw new RuntimeException(msg); + } + + private static void sleep(long delay) { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + System.out.println("InterruptedException" + e.getMessage()); + } + } static class DockerThread extends Thread { DockerRunOptions runOpts;