1 /*
   2  * Copyright (c) 2013, 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 
  26 package jdk.jfr.event.gc.detailed;
  27 
  28 import java.time.Duration;
  29 import java.util.List;
  30 import java.util.Optional;
  31 import java.util.Random;
  32 
  33 import jdk.jfr.Recording;
  34 import jdk.jfr.consumer.RecordedEvent;
  35 import jdk.test.lib.Asserts;
  36 import jdk.test.lib.jfr.EventNames;
  37 import jdk.test.lib.jfr.Events;
  38 
  39 /**
  40  * @test
  41  * @key jfr
  42  * 
  43  * 
  44  * @library /lib /
  45  * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:G1HeapRegionSize=1m -Xmx64m -Xmn16m -XX:+UseG1GC jdk.jfr.event.gc.detailed.TestEvacuationInfoEvent
  46  */
  47 public class TestEvacuationInfoEvent {
  48     private final static String EVENT_INFO_NAME = EventNames.EvacuationInfo;
  49     private final static String EVENT_FAILED_NAME = EventNames.EvacuationFailed;
  50 
  51     public static void main(String[] args) throws Throwable {
  52         final long g1HeapRegionSize = 1024 * 1024;
  53         Recording recording = new Recording();
  54         recording.enable(EVENT_INFO_NAME).withThreshold(Duration.ofMillis(0));
  55         recording.enable(EVENT_FAILED_NAME).withThreshold(Duration.ofMillis(0));
  56         recording.start();
  57         allocate();
  58         recording.stop();
  59 
  60         List<RecordedEvent> events = Events.fromRecording(recording);
  61         Asserts.assertFalse(events.isEmpty(), "No events found");
  62         for (RecordedEvent event : events) {
  63             if (!Events.isEventType(event, EVENT_INFO_NAME)) {
  64                 continue;
  65             }
  66             System.out.println("Event: " + event);
  67 
  68             int setRegions = Events.assertField(event, "cSetRegions").atLeast(0).getValue();
  69             long setUsedAfter = Events.assertField(event, "cSetUsedAfter").atLeast(0L).getValue();
  70             long setUsedBefore = Events.assertField(event, "cSetUsedBefore").atLeast(setUsedAfter).getValue();
  71             int allocationRegions = Events.assertField(event, "allocationRegions").atLeast(0).getValue();
  72             long allocRegionsUsedBefore = Events.assertField(event, "allocRegionsUsedBefore").atLeast(0L).getValue();
  73             long allocRegionsUsedAfter = Events.assertField(event, "allocRegionsUsedAfter").atLeast(0L).getValue();
  74             long bytesCopied = Events.assertField(event, "bytesCopied").atLeast(0L).getValue();
  75             int regionsFreed = Events.assertField(event, "regionsFreed").atLeast(0).getValue();
  76 
  77             Asserts.assertEquals(allocRegionsUsedBefore + bytesCopied, allocRegionsUsedAfter, "allocRegionsUsedBefore + bytesCopied = allocRegionsUsedAfter");
  78             Asserts.assertGreaterThanOrEqual(setRegions, regionsFreed, "setRegions >= regionsFreed");
  79             Asserts.assertGreaterThanOrEqual(g1HeapRegionSize * allocationRegions, allocRegionsUsedAfter, "G1HeapRegionSize * allocationRegions >= allocationRegionsUsedAfter");
  80             Asserts.assertGreaterThanOrEqual(g1HeapRegionSize * setRegions, setUsedAfter, "G1HeapRegionSize * setRegions >= setUsedAfter");
  81             Asserts.assertGreaterThanOrEqual(g1HeapRegionSize * setRegions, setUsedBefore, "G1HeapRegionSize * setRegions >= setUsedBefore");
  82             Asserts.assertGreaterThanOrEqual(g1HeapRegionSize, allocRegionsUsedBefore, "G1HeapRegionSize >= allocRegionsUsedBefore");
  83 
  84             int gcId = Events.assertField(event, "gcId").getValue();
  85             boolean isEvacuationFailed = containsEvacuationFailed(events, gcId);
  86             if (isEvacuationFailed) {
  87                 Asserts.assertGreaterThan(setUsedAfter, 0L, "EvacuationFailure -> setUsedAfter > 0");
  88                 Asserts.assertGreaterThan(setRegions, regionsFreed, "EvacuationFailure -> setRegions > regionsFreed");
  89             } else {
  90                 Asserts.assertEquals(setUsedAfter, 0L, "No EvacuationFailure -> setUsedAfter = 0");
  91                 Asserts.assertEquals(setRegions, regionsFreed, "No EvacuationFailure -> setRegions = regionsFreed");
  92             }
  93         }
  94     }
  95 
  96     private static boolean containsEvacuationFailed(List<RecordedEvent> events, int gcId) {
  97         Optional<RecordedEvent> failedEvent = events.stream()
  98                                 .filter(e -> Events.isEventType(e, EVENT_FAILED_NAME))
  99                                 .filter(e -> gcId == (int)Events.assertField(e, "gcId").getValue())
 100                                 .findAny();
 101         System.out.println("Failed event: " + (failedEvent.isPresent() ? failedEvent.get() : "None"));
 102         return failedEvent.isPresent();
 103     }
 104 
 105     public static DummyObject[] dummys = new DummyObject[6000];
 106 
 107         /**
 108          * Allocate memory to trigger garbage collections.
 109          * We want the allocated objects to have different life time, because we want both "young" and "old" objects.
 110          * This is done by keeping the objects in an array and step the current index by a small random number in the loop.
 111          * The loop will continue until we have allocated a fixed number of bytes.
 112          */
 113         private static void allocate() {
 114             Random r = new Random(0);
 115             long bytesToAllocate = 256 * 1024 * 1024;
 116             int currPos = 0;
 117             while (bytesToAllocate > 0) {
 118                 int allocSize = 1000 + (r.nextInt(4000));
 119                 bytesToAllocate -= allocSize;
 120                 dummys[currPos] = new DummyObject(allocSize);
 121 
 122                 // Skip a few positions to get different duration on the objects.
 123                 currPos = (currPos + r.nextInt(20)) % dummys.length;
 124             }
 125             for (int c=0; c<dummys.length; c++) {
 126                 dummys[c] = null;
 127             }
 128             System.gc();
 129         }
 130 
 131         public static class DummyObject {
 132             public byte[] payload;
 133             DummyObject(int size) {
 134             payload = new byte[size];
 135         }
 136     }
 137 }