test/jdk/jdk/jfr/event/gc/stacktrace/AllocationStackTrace.java
changeset 50113 caf115bb98ad
child 59053 ba6c248cae19
equal deleted inserted replaced
50112:7a2a740815b7 50113:caf115bb98ad
       
     1 /*
       
     2  * Copyright (c) 2015, 2018, 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.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package jdk.jfr.event.gc.stacktrace;
       
    26 
       
    27 import javax.management.MBeanServer;
       
    28 import java.lang.management.ManagementFactory;
       
    29 import com.sun.management.GarbageCollectorMXBean;
       
    30 
       
    31 import jdk.jfr.Recording;
       
    32 import jdk.jfr.consumer.RecordedEvent;
       
    33 import jdk.jfr.consumer.RecordedStackTrace;
       
    34 import jdk.jfr.consumer.RecordedFrame;
       
    35 import jdk.jfr.consumer.RecordedMethod;
       
    36 import jdk.jfr.consumer.RecordedThread;
       
    37 import jdk.test.lib.jfr.EventNames;
       
    38 import jdk.test.lib.jfr.Events;
       
    39 
       
    40 import java.util.List;
       
    41 import java.util.ArrayList;
       
    42 
       
    43 import java.net.URL;
       
    44 import java.net.URLClassLoader;
       
    45 import java.lang.reflect.InvocationHandler;
       
    46 import java.lang.reflect.Method;
       
    47 import java.lang.reflect.Proxy;
       
    48 
       
    49 abstract class MemoryAllocator {
       
    50 
       
    51     public static final int KB = 1024;
       
    52     public static final int MB = 1024 * KB;
       
    53 
       
    54     public static Object garbage = null;
       
    55 
       
    56     abstract public void allocate();
       
    57     public void clear() {
       
    58         garbage = null;
       
    59     }
       
    60 }
       
    61 
       
    62 class EdenMemoryAllocator extends MemoryAllocator {
       
    63 
       
    64     @Override
       
    65     public void allocate() {
       
    66         garbage = new byte[10 * KB];
       
    67     }
       
    68 }
       
    69 
       
    70 class HumongousMemoryAllocator extends MemoryAllocator {
       
    71 
       
    72     @Override
       
    73     public void allocate() {
       
    74         garbage = new byte[5 * MB];
       
    75     }
       
    76 }
       
    77 
       
    78 /**
       
    79  * Attempts to fill up young gen and allocate in old gen
       
    80  */
       
    81 class OldGenMemoryAllocator extends MemoryAllocator {
       
    82 
       
    83     private List<byte[]> list = new ArrayList<byte[]>();
       
    84     private int counter = 6000;
       
    85 
       
    86     @Override
       
    87     public void allocate() {
       
    88         if (counter-- > 0) {
       
    89             list.add(new byte[10 * KB]);
       
    90         } else {
       
    91             list = new ArrayList<byte[]>();
       
    92             counter = 6000;
       
    93         }
       
    94 
       
    95         garbage = list;
       
    96     }
       
    97 
       
    98     @Override
       
    99     public void clear(){
       
   100         list = null;
       
   101         super.clear();
       
   102     }
       
   103 }
       
   104 
       
   105 class MetaspaceMemoryAllocator extends MemoryAllocator {
       
   106 
       
   107     private static int counter = 0;
       
   108 
       
   109     /**
       
   110      * Imitates class loading. Each invocation of this method causes a new class
       
   111      * loader object is created and a new class is loaded by this class loader.
       
   112      * Method throws OOM when run out of memory.
       
   113      */
       
   114     static protected void loadNewClass() {
       
   115         try {
       
   116             String jarUrl = "file:" + (counter++) + ".jar";
       
   117             URL[] urls = new URL[]{new URL(jarUrl)};
       
   118             URLClassLoader cl = new URLClassLoader(urls);
       
   119             Proxy.newProxyInstance(
       
   120                     cl,
       
   121                     new Class[]{Foo.class},
       
   122                     new FooInvocationHandler(new FooBar()));
       
   123         } catch (java.net.MalformedURLException badThing) {
       
   124             // should never occur
       
   125             System.err.println("Unexpected error: " + badThing);
       
   126             throw new RuntimeException(badThing);
       
   127         }
       
   128     }
       
   129 
       
   130     @Override
       
   131     public void allocate() {
       
   132         try {
       
   133             loadNewClass();
       
   134         } catch (OutOfMemoryError e) {
       
   135             /* empty */
       
   136         }
       
   137     }
       
   138 
       
   139     public static interface Foo {
       
   140     }
       
   141 
       
   142     public static class FooBar implements Foo {
       
   143     }
       
   144 
       
   145     static class FooInvocationHandler implements InvocationHandler {
       
   146 
       
   147         private final Foo foo;
       
   148 
       
   149         FooInvocationHandler(Foo foo) {
       
   150             this.foo = foo;
       
   151         }
       
   152 
       
   153         @Override
       
   154         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       
   155             return method.invoke(foo, args);
       
   156         }
       
   157     }
       
   158 }
       
   159 
       
   160 /**
       
   161  * Utility class to peform JFR recording, GC provocation/detection and
       
   162  * stacktrace verification for related JFR events
       
   163  */
       
   164 public class AllocationStackTrace {
       
   165 
       
   166     /**
       
   167      * Tests event stacktrace for young GC if -XX:+UseSerialGC is used
       
   168      */
       
   169     public static void testDefNewAllocEvent() throws Exception {
       
   170         GarbageCollectorMXBean bean = garbageCollectorMXBean("Copy");
       
   171         MemoryAllocator memory = new EdenMemoryAllocator();
       
   172 
       
   173         String[] expectedStack = new String[]{
       
   174             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   175             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testDefNewAllocEvent"
       
   176         };
       
   177 
       
   178         testAllocEvent(bean, memory, expectedStack);
       
   179     }
       
   180 
       
   181     /**
       
   182      * Tests event stacktrace for old GC if -XX:+UseSerialGC is used
       
   183      */
       
   184     public static void testMarkSweepCompactAllocEvent() throws Exception {
       
   185         GarbageCollectorMXBean bean = garbageCollectorMXBean("MarkSweepCompact");
       
   186         MemoryAllocator memory = new OldGenMemoryAllocator();
       
   187 
       
   188         String[] expectedStack = new String[]{
       
   189             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   190             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMarkSweepCompactAllocEvent"
       
   191         };
       
   192 
       
   193         testAllocEvent(bean, memory, expectedStack);
       
   194     }
       
   195 
       
   196     /**
       
   197      * Tests event stacktrace during metaspace GC threshold if -XX:+UseSerialGC
       
   198      * is used
       
   199      */
       
   200     public static void testMetaspaceSerialGCAllocEvent() throws Exception {
       
   201         GarbageCollectorMXBean bean = garbageCollectorMXBean("MarkSweepCompact");
       
   202         MemoryAllocator memory = new MetaspaceMemoryAllocator();
       
   203 
       
   204         String[] expectedStack = new String[]{
       
   205             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   206             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMetaspaceSerialGCAllocEvent"
       
   207         };
       
   208 
       
   209         testAllocEvent(bean, memory, expectedStack);
       
   210     }
       
   211 
       
   212     /**
       
   213      * Tests event stacktrace for young GC if -XX:+UseConcMarkSweepGC is used
       
   214      */
       
   215     public static void testParNewAllocEvent() throws Exception {
       
   216         GarbageCollectorMXBean bean = garbageCollectorMXBean("ParNew");
       
   217         MemoryAllocator memory = new EdenMemoryAllocator();
       
   218 
       
   219         String[] expectedStack = new String[]{
       
   220             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   221             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testParNewAllocEvent"
       
   222         };
       
   223 
       
   224         testAllocEvent(bean, memory, expectedStack);
       
   225     }
       
   226 
       
   227     /**
       
   228      * Tests event stacktrace for old GC if -XX:+UseConcMarkSweepGC is used
       
   229      */
       
   230     public static void testConcMarkSweepAllocEvent() throws Exception {
       
   231         GarbageCollectorMXBean bean = garbageCollectorMXBean("ConcurrentMarkSweep");
       
   232         MemoryAllocator memory = new OldGenMemoryAllocator();
       
   233 
       
   234         String[] expectedStack = new String[]{
       
   235             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   236             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testConcMarkSweepAllocEvent"
       
   237         };
       
   238 
       
   239         testAllocEvent(bean, memory, expectedStack);
       
   240     }
       
   241 
       
   242     /**
       
   243      * Tests event stacktrace during metaspace GC threshold if
       
   244      * -XX:+UseConcMarkSweepGC is used
       
   245      */
       
   246     public static void testMetaspaceConcMarkSweepGCAllocEvent() throws Exception {
       
   247         GarbageCollectorMXBean bean = garbageCollectorMXBean("ConcurrentMarkSweep");
       
   248         MemoryAllocator memory = new MetaspaceMemoryAllocator();
       
   249 
       
   250         String[] expectedStack = new String[]{
       
   251             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   252             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMetaspaceConcMarkSweepGCAllocEvent"
       
   253         };
       
   254 
       
   255         testAllocEvent(bean, memory, expectedStack);
       
   256     }
       
   257 
       
   258     /**
       
   259      * Tests event stacktrace for young GC if -XX:+UseParallelGC is used
       
   260      */
       
   261     public static void testParallelScavengeAllocEvent() throws Exception {
       
   262         GarbageCollectorMXBean bean = garbageCollectorMXBean("PS Scavenge");
       
   263         MemoryAllocator memory = new EdenMemoryAllocator();
       
   264 
       
   265         String[] expectedStack = new String[]{
       
   266             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   267             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testParallelScavengeAllocEvent"
       
   268         };
       
   269 
       
   270         testAllocEvent(bean, memory, expectedStack);
       
   271     }
       
   272 
       
   273     /**
       
   274      * Tests event stacktrace for old GC if -XX:+UseParallelGC is used
       
   275      */
       
   276     public static void testParallelMarkSweepAllocEvent() throws Exception {
       
   277         GarbageCollectorMXBean bean = garbageCollectorMXBean("PS MarkSweep");
       
   278         MemoryAllocator memory = new OldGenMemoryAllocator();
       
   279 
       
   280         String[] expectedStack = new String[]{
       
   281             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   282             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testParallelMarkSweepAllocEvent"
       
   283         };
       
   284 
       
   285         testAllocEvent(bean, memory, expectedStack);
       
   286     }
       
   287 
       
   288     /**
       
   289      * Tests event stacktrace during metaspace GC threshold if
       
   290      * -XX:+UseParallelGC is used
       
   291      */
       
   292     public static void testMetaspaceParallelGCAllocEvent() throws Exception {
       
   293         GarbageCollectorMXBean bean = garbageCollectorMXBean("PS MarkSweep");
       
   294         MemoryAllocator memory = new MetaspaceMemoryAllocator();
       
   295 
       
   296         String[] expectedStack = new String[]{
       
   297             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   298             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMetaspaceParallelGCAllocEvent"
       
   299         };
       
   300 
       
   301         testAllocEvent(bean, memory, expectedStack);
       
   302     }
       
   303 
       
   304     /**
       
   305      * Tests event stacktrace for young GC if -XX:+UseG1GC is used
       
   306      */
       
   307     public static void testG1YoungAllocEvent() throws Exception {
       
   308         GarbageCollectorMXBean bean = garbageCollectorMXBean("G1 Young Generation");
       
   309         MemoryAllocator memory = new EdenMemoryAllocator();
       
   310 
       
   311         String[] expectedStack = new String[]{
       
   312             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   313             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testG1YoungAllocEvent"
       
   314         };
       
   315 
       
   316         testAllocEvent(bean, memory, expectedStack);
       
   317     }
       
   318 
       
   319     /**
       
   320      * Tests event stacktrace for old GC if -XX:+UseG1GC is used
       
   321      */
       
   322     public static void testG1OldAllocEvent() throws Exception {
       
   323         GarbageCollectorMXBean bean = garbageCollectorMXBean("G1 Old Generation");
       
   324         MemoryAllocator memory = new OldGenMemoryAllocator();
       
   325 
       
   326         String[] expectedStack = new String[]{
       
   327             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   328             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testG1OldAllocEvent"
       
   329         };
       
   330 
       
   331         testAllocEvent(bean, memory, expectedStack);
       
   332     }
       
   333 
       
   334     /**
       
   335      * Tests event stacktrace during metaspace GC threshold if -XX:+UseG1GC is
       
   336      * used
       
   337      */
       
   338     public static void testMetaspaceG1GCAllocEvent() throws Exception {
       
   339         GarbageCollectorMXBean bean = garbageCollectorMXBean("G1 Young Generation");
       
   340         MemoryAllocator memory = new MetaspaceMemoryAllocator();
       
   341 
       
   342         String[] expectedStack = new String[]{
       
   343             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   344             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMetaspaceG1GCAllocEvent"
       
   345         };
       
   346 
       
   347         testAllocEvent(bean, memory, expectedStack);
       
   348     }
       
   349 
       
   350     /**
       
   351      * Tests event stacktrace for GC caused by humongous allocations if
       
   352      * -XX:+UseG1GC is used
       
   353      */
       
   354     public static void testG1HumonAllocEvent() throws Exception {
       
   355         // G1 tries to reclaim humongous objects at every young collection
       
   356         // after doing a conservative estimate of its liveness
       
   357         GarbageCollectorMXBean bean = garbageCollectorMXBean("G1 Young Generation");
       
   358         MemoryAllocator memory = new HumongousMemoryAllocator();
       
   359 
       
   360         String[] expectedStack = new String[]{
       
   361             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent",
       
   362             "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testG1HumonAllocEvent"
       
   363         };
       
   364 
       
   365         testAllocEvent(bean, memory, expectedStack);
       
   366     }
       
   367 
       
   368     private static GarbageCollectorMXBean garbageCollectorMXBean(String name) throws Exception {
       
   369         MBeanServer server = ManagementFactory.getPlatformMBeanServer();
       
   370         GarbageCollectorMXBean bean = ManagementFactory.newPlatformMXBeanProxy(
       
   371                 server, "java.lang:type=GarbageCollector,name=" + name, GarbageCollectorMXBean.class);
       
   372         return bean;
       
   373     }
       
   374 
       
   375     /**
       
   376      * Performs JFR recording, GC provocation/detection and stacktrace
       
   377      * verification for JFR event. In case of verification failure
       
   378      * repeats several times.
       
   379      *
       
   380      * @param bean MX bean for desired GC
       
   381      * @param memory allocator for desired type of allocations
       
   382      * @param expectedStack array of expected frames
       
   383      */
       
   384     private static void testAllocEvent(GarbageCollectorMXBean bean, MemoryAllocator memory, String[] expectedStack) throws Exception {
       
   385         // The test checks the stacktrace of events and expects all the events are fired
       
   386         // in the current thread. But compilation may also trigger GC.
       
   387         // So to filter out such cases the test performs several iterations and expects
       
   388         // that the memory allocations made by the test will produce the desired JFR event.
       
   389         final int iterations = 5;
       
   390         for (int i = 0; i < iterations; i++) {
       
   391             if (allocAndCheck(bean, memory, expectedStack)) {
       
   392                 return;
       
   393             } else {
       
   394                 System.out.println("Attempt: " + i + " out of " + iterations+": no matching stack trace found.");
       
   395             }
       
   396             memory.clear();
       
   397         }
       
   398         throw new AssertionError("No matching stack trace found");
       
   399     }
       
   400 
       
   401     /**
       
   402      * Performs JFR recording, GC provocation/detection and stacktrace
       
   403      * verification for JFR event.
       
   404      *
       
   405      * @param bean MX bean for desired GC
       
   406      * @param memory allocator for desired type of allocations
       
   407      * @param expectedStack array of expected frames
       
   408      * @throws Exception
       
   409      */
       
   410     private static boolean allocAndCheck(GarbageCollectorMXBean bean, MemoryAllocator memory,
       
   411             String[] expectedStack) throws Exception {
       
   412         String threadName = Thread.currentThread().getName();
       
   413         String event = EventNames.AllocationRequiringGC;
       
   414 
       
   415         Recording r = new Recording();
       
   416         r.enable(event).withStackTrace();
       
   417         r.start();
       
   418 
       
   419         long prevCollectionCount = bean.getCollectionCount();
       
   420         long collectionCount = -1;
       
   421 
       
   422         long iterationCount = 0;
       
   423 
       
   424         do {
       
   425             memory.allocate();
       
   426             collectionCount = bean.getCollectionCount();
       
   427             iterationCount++;
       
   428         } while (collectionCount == prevCollectionCount);
       
   429 
       
   430         System.out.println("Allocation num: " + iterationCount);
       
   431         System.out.println("GC detected: " + collectionCount);
       
   432 
       
   433         r.stop();
       
   434         List<RecordedEvent> events = Events.fromRecording(r);
       
   435 
       
   436         System.out.println("JFR GC events found: " + events.size());
       
   437 
       
   438         // Find any event that matched the expected stack trace
       
   439         for (RecordedEvent e : events) {
       
   440             System.out.println("Event: " + e);
       
   441             RecordedThread thread = e.getThread();
       
   442             String eventThreadName = thread.getJavaName();
       
   443             if (!threadName.equals(eventThreadName)) {
       
   444                 continue;
       
   445             }
       
   446             if (matchingStackTrace(e.getStackTrace(), expectedStack)) {
       
   447                 return true;
       
   448             }
       
   449         }
       
   450         return false;
       
   451     }
       
   452 
       
   453     private static boolean matchingStackTrace(RecordedStackTrace stack, String[] expectedStack) {
       
   454         if (stack == null) {
       
   455             return false;
       
   456         }
       
   457 
       
   458         List<RecordedFrame> frames = stack.getFrames();
       
   459         int pos = findFramePos(frames, expectedStack[0]);
       
   460 
       
   461         if (pos == -1) {
       
   462             return false;
       
   463         }
       
   464 
       
   465         for (String expectedFrame : expectedStack) {
       
   466             RecordedFrame f = frames.get(pos++);
       
   467             String frame = frameToString(f);
       
   468 
       
   469             if (!frame.equals(expectedFrame)) {
       
   470                 return false;
       
   471             }
       
   472         }
       
   473 
       
   474         return true;
       
   475     }
       
   476 
       
   477     private static int findFramePos(List<RecordedFrame> frames, String frame) {
       
   478         int pos = 0;
       
   479 
       
   480         for (RecordedFrame f : frames) {
       
   481             if (frame.equals(frameToString(f))) {
       
   482                 return pos;
       
   483             }
       
   484             pos++;
       
   485         }
       
   486 
       
   487         return -1;
       
   488     }
       
   489 
       
   490     private static String frameToString(RecordedFrame f) {
       
   491         RecordedMethod m = f.getMethod();
       
   492         String methodName = m.getName();
       
   493         String className = m.getType().getName();
       
   494         return className + "." + methodName;
       
   495     }
       
   496 
       
   497 }