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