|
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 } |