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