1 /* 2 * Copyright (c) 2014, 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.compiler; 27 28 import java.lang.management.MemoryPoolMXBean; 29 import java.lang.reflect.Method; 30 import java.time.Instant; 31 import java.util.ArrayList; 32 import java.util.List; 33 34 import jdk.jfr.Recording; 35 import jdk.jfr.consumer.RecordedEvent; 36 import jdk.test.lib.Asserts; 37 import jdk.test.lib.jfr.EventNames; 38 import jdk.test.lib.jfr.Events; 39 import sun.hotspot.WhiteBox; 40 import sun.hotspot.code.BlobType; 41 import sun.hotspot.code.CodeBlob; 42 43 /** 44 * Test for events: vm/code_sweeper/sweep vm/code_cache/full vm/compiler/failure 45 * 46 * We verify: 1. That sweptCount >= flushedCount + zombifiedCount 2. That 47 * sweepIndex increases by 1. 3. We should get at least one of each of the 48 * events listed above. 49 * 50 * NOTE! The test is usually able to trigger the events but not always. If an 51 * event is received, the event is verified. If an event is missing, we do NOT 52 * fail. 53 */ 54 /** 55 * @test TestCodeSweeper 56 * @key jfr 57 * 58 * @library /lib / 59 * @build sun.hotspot.WhiteBox 60 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 61 * sun.hotspot.WhiteBox$WhiteBoxPermission 62 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI jdk.jfr.event.compiler.TestCodeSweeper 63 */ 64 65 public class TestCodeSweeper { 66 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 67 private static final int COMP_LEVEL_SIMPLE = 1; 68 private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; 69 private static final int SIZE = 1; 70 private static final String METHOD_NAME = "verifyFullEvent"; 71 private static final String pathSweep = EventNames.SweepCodeCache; 72 private static final String pathFull = EventNames.CodeCacheFull; 73 private static final String pathFailure = EventNames.CompilationFailure; 74 public static final long SEGMENT_SIZE = WhiteBox.getWhiteBox().getUintxVMFlag("CodeCacheSegmentSize"); 75 public static final long MIN_BLOCK_LENGTH = WhiteBox.getWhiteBox().getUintxVMFlag("CodeCacheMinBlockLength"); 76 public static final long MIN_ALLOCATION = SEGMENT_SIZE * MIN_BLOCK_LENGTH; 77 private static final double CACHE_USAGE_COEF = 0.95d; 78 79 public static void main(String[] args) throws Throwable { 80 Asserts.assertTrue(BlobType.getAvailable().contains(BlobType.All), "Test does not support SegmentedCodeCache"); 81 82 System.out.println("************************************************"); 83 System.out.println("This test will warn that the code cache is full."); 84 System.out.println("That is expected and is the purpose of the test."); 85 System.out.println("************************************************"); 86 87 Recording r = new Recording(); 88 r.enable(pathSweep); 89 r.enable(pathFull); 90 r.enable(pathFailure); 91 r.start(); 92 provokeEvents(); 93 r.stop(); 94 95 int countEventSweep = 0; 96 int countEventFull = 0; 97 int countEventFailure = 0; 98 99 List<RecordedEvent> events = Events.fromRecording(r); 100 Events.hasEvents(events); 101 for (RecordedEvent event : events) { 102 switch (event.getEventType().getName()) { 103 case pathSweep: 104 countEventSweep++; 105 verifySingleSweepEvent(event); 106 break; 107 case pathFull: 108 countEventFull++; 109 verifyFullEvent(event); 110 break; 111 case pathFailure: 112 countEventFailure++; 113 verifyFailureEvent(event); 114 break; 115 } 116 } 117 118 System.out.println(String.format("eventCount: %d, %d, %d", countEventSweep, countEventFull, countEventFailure)); 119 } 120 121 private static boolean canAllocate(double size, long maxSize, MemoryPoolMXBean bean) { 122 // Don't fill too much to have space for adapters. So, stop after crossing 95% and 123 // don't allocate in case we'll cross 97% on next allocation. 124 double used = bean.getUsage().getUsed(); 125 return (used <= CACHE_USAGE_COEF * maxSize) && 126 (used + size <= (CACHE_USAGE_COEF + 0.02d) * maxSize); 127 } 128 129 private static void provokeEvents() throws NoSuchMethodException, InterruptedException { 130 // Prepare for later, since we don't want to trigger any compilation 131 // setting this up. 132 Method method = TestCodeSweeper.class.getDeclaredMethod(METHOD_NAME, new Class[] { RecordedEvent.class }); 133 String directive = "[{ match: \"" + TestCodeSweeper.class.getName().replace('.', '/') 134 + "." + METHOD_NAME + "\", " + "BackgroundCompilation: false }]"; 135 136 // Fill up code heaps until they are almost full 137 // to trigger the vm/code_sweeper/sweep event. 138 ArrayList<Long> blobs = new ArrayList<>(); 139 MemoryPoolMXBean bean = BlobType.All.getMemoryPool(); 140 long max = bean.getUsage().getMax(); 141 long headerSize = getHeaderSize(BlobType.All); 142 long minAllocationUnit = Math.max(1, MIN_ALLOCATION - headerSize); 143 long stopAt = max - minAllocationUnit; 144 long addr = 0; 145 146 // First allocate big blobs to speed things up 147 for (long size = 100_000 * minAllocationUnit; size > 0; size /= 10) { 148 while (canAllocate(size, max, bean) && 149 (addr = WHITE_BOX.allocateCodeBlob(size, BlobType.All.id)) != 0) { 150 blobs.add(addr); 151 } 152 } 153 154 // Now allocate small blobs until the heap is almost full 155 while (bean.getUsage().getUsed() < stopAt && 156 (addr = WHITE_BOX.allocateCodeBlob(SIZE, BlobType.All.id)) != 0) { 157 blobs.add(addr); 158 } 159 160 // Trigger the vm/code_cache/full event by compiling one more 161 // method. This also triggers the vm/compiler/failure event. 162 if (!WHITE_BOX.enqueueMethodForCompilation(method, COMP_LEVEL_FULL_OPTIMIZATION)) { 163 WHITE_BOX.enqueueMethodForCompilation(method, COMP_LEVEL_SIMPLE); 164 } 165 166 Thread.sleep(5000); 167 168 // Free memory 169 for (Long blob : blobs) { 170 WHITE_BOX.freeCodeBlob(blob); 171 } 172 } 173 174 private static void verifyFullEvent(RecordedEvent event) throws Throwable { 175 Events.assertField(event, "codeBlobType").notEmpty(); 176 Events.assertField(event, "unallocatedCapacity").atLeast(0L); 177 Events.assertField(event, "entryCount").atLeast(0); 178 Events.assertField(event, "methodCount").atLeast(0); 179 Events.assertField(event, "adaptorCount").atLeast(0); 180 Events.assertField(event, "fullCount").atLeast(0); 181 182 // Verify startAddress <= commitedTopAddress <= reservedTopAddress. 183 // Addresses may be so big that they overflow a long (treated as a 184 // negative value), convert value to an octal string and compare the 185 // string. 186 String startAddress = Long.toOctalString(Events.assertField(event, "startAddress").getValue()); 187 String commitedTopAddress = Long.toOctalString(Events.assertField(event, "commitedTopAddress").getValue()); 188 String reservedTopAddress = Long.toOctalString(Events.assertField(event, "reservedTopAddress").getValue()); 189 Asserts.assertTrue(isOctalLessOrEqual(startAddress, commitedTopAddress), "startAddress<=commitedTopAddress: " + startAddress + "<=" + commitedTopAddress); 190 Asserts.assertTrue(isOctalLessOrEqual(commitedTopAddress, reservedTopAddress), "commitedTopAddress<=reservedTopAddress: " + commitedTopAddress + "<=" + reservedTopAddress); 191 } 192 193 private static void verifyFailureEvent(RecordedEvent event) throws Throwable { 194 Events.assertField(event, "failureMessage").notEmpty(); 195 Events.assertField(event, "compileId").atLeast(0); 196 } 197 198 private static void verifySingleSweepEvent(RecordedEvent event) throws Throwable { 199 int flushedCount = Events.assertField(event, "flushedCount").atLeast(0).getValue(); 200 int zombifiedCount = Events.assertField(event, "zombifiedCount").atLeast(0).getValue(); 201 Events.assertField(event, "sweptCount").atLeast(flushedCount + zombifiedCount); 202 Events.assertField(event, "sweepId").atLeast(0); 203 Asserts.assertGreaterThanOrEqual(event.getStartTime(), Instant.EPOCH, "startTime was < 0"); 204 Asserts.assertGreaterThanOrEqual(event.getEndTime(), event.getStartTime(), "startTime was > endTime"); 205 } 206 207 /** Returns true if less <= bigger. */ 208 private static boolean isOctalLessOrEqual(String less, String bigger) { 209 if (less.length() > bigger.length()) { 210 return false; 211 } 212 if (less.length() < bigger.length()) { 213 return true; 214 } 215 return less.compareTo(bigger) <= 0; 216 } 217 218 public static final long getHeaderSize(BlobType btype) { 219 long addr = WHITE_BOX.allocateCodeBlob(0, btype.id); 220 int size = CodeBlob.getCodeBlob(addr).size; 221 WHITE_BOX.freeCodeBlob(addr); 222 return size; 223 } 224 }