8213914: [TESTBUG] Several JFR VM events are not covered by tests
authormseledtsov
Tue, 27 Nov 2018 15:52:34 -0800
changeset 52704 64413aaea8ed
parent 52703 e7fdc9d9c376
child 52705 53a407ab6d22
8213914: [TESTBUG] Several JFR VM events are not covered by tests Summary: Fixed several relevant test issues, added a test to check event coverage Reviewed-by: egahlin
test/jdk/jdk/jfr/event/compiler/TestCompilerConfig.java
test/jdk/jdk/jfr/event/gc/detailed/TestEvacuationInfoEvent.java
test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java
test/jdk/jdk/jfr/event/runtime/TestSafepointEvents.java
test/lib/jdk/test/lib/jfr/EventNames.java
--- a/test/jdk/jdk/jfr/event/compiler/TestCompilerConfig.java	Tue Nov 27 18:35:16 2018 -0500
+++ b/test/jdk/jdk/jfr/event/compiler/TestCompilerConfig.java	Tue Nov 27 15:52:34 2018 -0800
@@ -40,7 +40,7 @@
  * @run main/othervm jdk.jfr.event.compiler.TestCompilerConfig
  */
 public class TestCompilerConfig {
-    private final static String EVENT_NAME = EventNames.CompilerConfig;
+    private final static String EVENT_NAME = EventNames.CompilerConfiguration;
 
     public static void main(String[] args) throws Exception {
         Recording recording = new Recording();
--- a/test/jdk/jdk/jfr/event/gc/detailed/TestEvacuationInfoEvent.java	Tue Nov 27 18:35:16 2018 -0500
+++ b/test/jdk/jdk/jfr/event/gc/detailed/TestEvacuationInfoEvent.java	Tue Nov 27 15:52:34 2018 -0800
@@ -45,7 +45,7 @@
  * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:G1HeapRegionSize=1m -Xmx64m -Xmn16m -XX:+UseG1GC jdk.jfr.event.gc.detailed.TestEvacuationInfoEvent
  */
 public class TestEvacuationInfoEvent {
-    private final static String EVENT_INFO_NAME = EventNames.EvacuationInfo;
+    private final static String EVENT_INFO_NAME = EventNames.EvacuationInformation;
     private final static String EVENT_FAILED_NAME = EventNames.EvacuationFailed;
 
     public static void main(String[] args) throws Throwable {
@@ -69,8 +69,8 @@
             long setUsedAfter = Events.assertField(event, "cSetUsedAfter").atLeast(0L).getValue();
             long setUsedBefore = Events.assertField(event, "cSetUsedBefore").atLeast(setUsedAfter).getValue();
             int allocationRegions = Events.assertField(event, "allocationRegions").atLeast(0).getValue();
-            long allocRegionsUsedBefore = Events.assertField(event, "allocRegionsUsedBefore").atLeast(0L).getValue();
-            long allocRegionsUsedAfter = Events.assertField(event, "allocRegionsUsedAfter").atLeast(0L).getValue();
+            long allocRegionsUsedBefore = Events.assertField(event, "allocationRegionsUsedBefore").atLeast(0L).getValue();
+            long allocRegionsUsedAfter = Events.assertField(event, "allocationRegionsUsedAfter").atLeast(0L).getValue();
             long bytesCopied = Events.assertField(event, "bytesCopied").atLeast(0L).getValue();
             int regionsFreed = Events.assertField(event, "regionsFreed").atLeast(0).getValue();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java	Tue Nov 27 15:52:34 2018 -0800
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2018, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package jdk.jfr.event.metadata;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import jdk.jfr.EventType;
+import jdk.jfr.Experimental;
+import jdk.jfr.FlightRecorder;
+import jdk.test.lib.jfr.EventNames;
+import jdk.test.lib.Utils;
+
+/**
+ * @test Check for JFR events not covered by tests
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main jdk.jfr.event.metadata.TestLookForUntestedEvents
+ */
+public class TestLookForUntestedEvents {
+    private static final Path jfrTestRoot = Paths.get(Utils.TEST_SRC).getParent().getParent();
+    private static final String MSG_SEPARATOR = "==========================";
+    private static Set<String> jfrEventTypes = new HashSet<>();
+
+    private static final Set<String> knownEventsMissingFromEventNames = new HashSet<>(
+        Arrays.asList(
+            // The Z* events below should be marked as experimental; see: JDK-8213966
+            "ZStatisticsSampler", "ZStatisticsCounter",
+            "ZPageAllocation", "ZThreadPhase"
+        )
+    );
+
+    private static final Set<String> hardToTestEvents = new HashSet<>(
+        Arrays.asList(
+            "DataLoss", "IntFlag", "ReservedStackActivation",
+            "DoubleFlag", "UnsignedLongFlagChanged", "IntFlagChanged",
+            "UnsignedIntFlag", "UnsignedIntFlagChanged", "DoubleFlagChanged")
+    );
+
+    // GC uses specific framework to test the events, instead of using event names literally.
+    // GC tests were inspected, as well as runtime output of GC tests.
+    // The following events below are know to be covered based on that inspection.
+    private static final Set<String> coveredGcEvents = new HashSet<>(
+        Arrays.asList(
+            "MetaspaceGCThreshold", "MetaspaceAllocationFailure", "MetaspaceOOM",
+            "MetaspaceChunkFreeListSummary", "G1HeapSummary", "ParallelOldGarbageCollection",
+            "OldGarbageCollection", "G1GarbageCollection", "GCPhasePause",
+            "GCPhasePauseLevel1", "GCPhasePauseLevel2", "GCPhasePauseLevel3",
+            "GCPhasePauseLevel4", "GCPhaseConcurrent")
+    );
+
+    // This is a "known failure list" for this test.
+    // NOTE: if the event is not covered, a bug should be open, and bug number
+    // noted in the comments for this set.
+    private static final Set<String> knownNotCoveredEvents = new HashSet<>(
+        // DumpReason: JDK-8213918, Shutdown: JDK-8213917
+        Arrays.asList("DumpReason", "Shutdown")
+    );
+
+
+    public static void main(String[] args) throws Exception {
+        for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) {
+            if (type.getAnnotation(Experimental.class) == null) {
+                jfrEventTypes.add(type.getName().replace("jdk.", ""));
+            }
+        }
+
+        checkEventNamesClass();
+        lookForEventsNotCoveredByTests();
+    }
+
+    // Look thru JFR tests to make sure JFR events are referenced in the tests
+    private static void lookForEventsNotCoveredByTests() throws Exception {
+        List<Path> paths = Files.walk(jfrTestRoot)
+            .filter(Files::isRegularFile)
+            .filter(path -> isJavaFile(path))
+            .collect(Collectors.toList());
+
+        Set<String> eventsNotCoveredByTest = new HashSet<>(jfrEventTypes);
+        for (String event : jfrEventTypes) {
+            for (Path p : paths) {
+                if (findStringInFile(p, event)) {
+                    eventsNotCoveredByTest.remove(event);
+                    break;
+                }
+            }
+        }
+
+        // Account for hard-to-test, experimental and GC tested events
+        eventsNotCoveredByTest.removeAll(hardToTestEvents);
+        eventsNotCoveredByTest.removeAll(coveredGcEvents);
+        eventsNotCoveredByTest.removeAll(knownNotCoveredEvents);
+
+        if (!eventsNotCoveredByTest.isEmpty()) {
+            print(MSG_SEPARATOR + " Events not covered by test");
+            for (String event: eventsNotCoveredByTest) {
+                print(event);
+            }
+            print(MSG_SEPARATOR);
+            throw new RuntimeException("Found JFR events not covered by tests");
+        }
+    }
+
+    // Make sure all the JFR events are accounted for in jdk.test.lib.jfr.EventNames
+    private static void checkEventNamesClass() throws Exception {
+        // jdk.test.lib.jfr.EventNames
+        Set<String> eventsFromEventNamesClass = new HashSet<>();
+        for (Field f : EventNames.class.getFields()) {
+            String name = f.getName();
+            if (!name.equals("PREFIX")) {
+                String eventName = (String) f.get(null);
+                eventName = eventName.replace(EventNames.PREFIX, "");
+                eventsFromEventNamesClass.add(eventName);
+            }
+        }
+
+        // Account for the events that are known to be missing from the EventNames.java
+        eventsFromEventNamesClass.addAll(knownEventsMissingFromEventNames);
+
+        if (!jfrEventTypes.equals(eventsFromEventNamesClass)) {
+            String exceptionMsg = "Events declared in jdk.test.lib.jfr.EventNames differ " +
+                         "from events returned by FlightRecorder.getEventTypes()";
+            print(MSG_SEPARATOR);
+            print(exceptionMsg);
+            print("");
+            printSetDiff(jfrEventTypes, eventsFromEventNamesClass,
+                        "jfrEventTypes", "eventsFromEventNamesClass");
+            print("");
+
+            print("This could be because:");
+            print("1) You forgot to write a unit test. Please do so in test/jdk/jdk/jfr/event/");
+            print("2) You wrote a unit test, but you didn't reference the event in");
+            print("   test/lib/jdk/test/lib/jfr/EventNames.java. ");
+            print("3) It is not feasible to test the event, not even a sanity test. ");
+            print("   Add the event name to test/lib/jdk/test/lib/jfr/EventNames.java ");
+            print("   and a short comment why it can't be tested");
+            print("4) The event is experimental. Please add 'experimental=\"true\"' to <Event> ");
+            print("   element in metadata.xml if it is a native event, or @Experimental if it is a ");
+            print("   Java event. The event will now not show up in JMC");
+            System.out.println(MSG_SEPARATOR);
+            throw new RuntimeException(exceptionMsg);
+        }
+    }
+
+    // ================ Helper methods
+    private static boolean isJavaFile(Path p) {
+        String fileName = p.getFileName().toString();
+        int i = fileName.lastIndexOf('.');
+        if ( (i < 0) || (i > fileName.length()) ) {
+            return false;
+        }
+        return "java".equals(fileName.substring(i+1));
+    }
+
+    private static boolean findStringInFile(Path p, String searchTerm) throws IOException {
+        long c = 0;
+        try (Stream<String> stream = Files.lines(p)) {
+            c = stream
+                .filter(line -> line.contains(searchTerm))
+                .count();
+        }
+        return (c != 0);
+    }
+
+    private static void printSetDiff(Set<String> a, Set<String> b,
+        String setAName, String setBName) {
+        if (a.size() > b.size()) {
+            a.removeAll(b);
+            System.out.printf("Set %s has more elements than set %s:", setAName, setBName);
+            System.out.println();
+            printSet(a);
+        } else {
+            b.removeAll(a);
+            System.out.printf("Set %s has more elements than set %s:", setBName, setAName);
+            System.out.println();
+            printSet(b);
+        }
+    }
+
+    private static void printSet(Set<String> set) {
+        for (String e : set) {
+            System.out.println(e);
+        }
+    }
+
+    private static void print(String s) {
+        System.out.println(s);
+    }
+}
--- a/test/jdk/jdk/jfr/event/runtime/TestSafepointEvents.java	Tue Nov 27 18:35:16 2018 -0500
+++ b/test/jdk/jdk/jfr/event/runtime/TestSafepointEvents.java	Tue Nov 27 15:52:34 2018 -0800
@@ -54,7 +54,7 @@
 
     static final String[] EVENT_NAMES = new String[] {
         EventNames.SafepointBegin,
-        EventNames.SafepointStateSyncronization,
+        EventNames.SafepointStateSynchronization,
         EventNames.SafepointWaitBlocked,
         EventNames.SafepointCleanup,
         EventNames.SafepointCleanupTask,
--- a/test/lib/jdk/test/lib/jfr/EventNames.java	Tue Nov 27 18:35:16 2018 -0500
+++ b/test/lib/jdk/test/lib/jfr/EventNames.java	Tue Nov 27 15:52:34 2018 -0800
@@ -54,7 +54,6 @@
     public final static String StringFlagChanged = PREFIX + "StringFlagChanged";
 
     // Runtime
-    public final static String VMException = PREFIX + "JavaErrorThrow";
     public final static String ThreadStart = PREFIX + "ThreadStart";
     public final static String ThreadEnd = PREFIX + "ThreadEnd";
     public final static String ThreadSleep = PREFIX + "ThreadSleep";
@@ -66,26 +65,26 @@
     public final static String ClassDefine = PREFIX + "ClassDefine";
     public final static String ClassUnload = PREFIX + "ClassUnload";
     public final static String SafepointBegin = PREFIX + "SafepointBegin";
-    public final static String SafepointStateSyncronization = PREFIX + "SafepointStateSynchronization";
+    public final static String SafepointStateSynchronization = PREFIX + "SafepointStateSynchronization";
     public final static String SafepointWaitBlocked = PREFIX + "SafepointWaitBlocked";
     public final static String SafepointCleanup = PREFIX + "SafepointCleanup";
     public final static String SafepointCleanupTask = PREFIX + "SafepointCleanupTask";
     public final static String SafepointEnd = PREFIX + "SafepointEnd";
     public final static String ExecuteVMOperation = PREFIX + "ExecuteVMOperation";
     public final static String Shutdown = PREFIX + "Shutdown";
-    public final static String VMError = PREFIX + "VMError";
     public final static String JavaThreadStatistics = PREFIX + "JavaThreadStatistics";
     public final static String ClassLoadingStatistics = PREFIX + "ClassLoadingStatistics";
     public final static String ClassLoaderStatistics = PREFIX + "ClassLoaderStatistics";
     public final static String ThreadAllocationStatistics = PREFIX + "ThreadAllocationStatistics";
     public final static String ExecutionSample = PREFIX + "ExecutionSample";
     public final static String NativeMethodSample = PREFIX + "NativeMethodSample";
-    public final static String ExecutionSampling = PREFIX + "ExecutionSampling";
     public final static String ThreadDump = PREFIX + "ThreadDump";
     public final static String OldObjectSample = PREFIX + "OldObjectSample";
     public final static String BiasedLockRevocation = PREFIX + "BiasedLockRevocation";
     public final static String BiasedLockSelfRevocation = PREFIX + "BiasedLockSelfRevocation";
     public final static String BiasedLockClassRevocation = PREFIX + "BiasedLockClassRevocation";
+    // This event is hard to test
+    public final static String ReservedStackActivation = PREFIX + "ReservedStackActivation";
 
     // GC
     public final static String GCHeapSummary = PREFIX + "GCHeapSummary";
@@ -100,12 +99,13 @@
     public final static String G1HeapRegionTypeChange = PREFIX + "G1HeapRegionTypeChange";
     public final static String TenuringDistribution = PREFIX + "TenuringDistribution";
     public final static String GarbageCollection = PREFIX + "GarbageCollection";
-    public final static String ParallelOldCollection = PREFIX + "ParallelOldGarbageCollection";
+    public final static String ParallelOldGarbageCollection = PREFIX + "ParallelOldGarbageCollection";
+    public final static String ParallelOldCollection = ParallelOldGarbageCollection;
     public final static String YoungGarbageCollection = PREFIX + "YoungGarbageCollection";
     public final static String OldGarbageCollection = PREFIX + "OldGarbageCollection";
     public final static String G1GarbageCollection = PREFIX + "G1GarbageCollection";
     public final static String G1MMU = PREFIX + "G1MMU";
-    public final static String EvacuationInfo = PREFIX + "EvacuationInfo";
+    public final static String EvacuationInformation = PREFIX + "EvacuationInformation";
     public final static String GCReferenceStatistics = PREFIX + "GCReferenceStatistics";
     public final static String ObjectCountAfterGC = PREFIX + "ObjectCountAfterGC";
     public final static String PromoteObjectInNewPLAB = PREFIX + "PromoteObjectInNewPLAB";
@@ -117,6 +117,7 @@
     public final static String GCPhasePauseLevel1 = PREFIX + "GCPhasePauseLevel1";
     public final static String GCPhasePauseLevel2 = PREFIX + "GCPhasePauseLevel2";
     public final static String GCPhasePauseLevel3 = PREFIX + "GCPhasePauseLevel3";
+    public final static String GCPhasePauseLevel4 = PREFIX + "GCPhasePauseLevel4";
     public final static String ObjectCount = PREFIX + "ObjectCount";
     public final static String GCConfiguration = PREFIX + "GCConfiguration";
     public final static String GCSurvivorConfiguration = PREFIX + "GCSurvivorConfiguration";
@@ -129,6 +130,7 @@
     public final static String G1BasicIHOP = PREFIX + "G1BasicIHOP";
     public final static String AllocationRequiringGC = PREFIX + "AllocationRequiringGC";
     public final static String GCPhaseParallel = PREFIX + "GCPhaseParallel";
+    public final static String GCPhaseConcurrent = PREFIX + "GCPhaseConcurrent";
 
     // Compiler
     public final static String Compilation = PREFIX + "Compilation";
@@ -136,7 +138,7 @@
     public final static String CompilationFailure = PREFIX + "CompilationFailure";
     public final static String CompilerInlining = PREFIX + "CompilerInlining";
     public final static String CompilerStatistics = PREFIX + "CompilerStatistics";
-    public final static String CompilerConfig = PREFIX + "CompilerConfiguration";
+    public final static String CompilerConfiguration = PREFIX + "CompilerConfiguration";
     public final static String CodeCacheStatistics = PREFIX + "CodeCacheStatistics";
     public final static String CodeCacheConfiguration = PREFIX + "CodeCacheConfiguration";
     public final static String CodeSweeperStatistics = PREFIX + "CodeSweeperStatistics";