1 /* 2 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2018, 2019, Google and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 package MyPackage; 26 27 import java.lang.management.ManagementFactory; 28 import java.util.ArrayList; 29 import java.util.List; 30 31 import com.sun.management.HotSpotDiagnosticMXBean; 32 import com.sun.management.VMOption; 33 34 /** API for handling the underlying heap sampling monitoring system. */ 35 public class HeapMonitor { 36 private static int[][] arrays; 37 private static int allocationIterations = 1000; 38 39 static { 40 try { 41 System.loadLibrary("HeapMonitorTest"); 42 } catch (UnsatisfiedLinkError ule) { 43 System.err.println("Could not load HeapMonitor library"); 44 System.err.println("java.library.path: " + System.getProperty("java.library.path")); 45 throw ule; 46 } 47 } 48 49 /** Set a specific sampling interval, 0 samples every allocation. */ setSamplingInterval(int interval)50 public native static void setSamplingInterval(int interval); enableSamplingEvents()51 public native static void enableSamplingEvents(); enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread)52 public native static boolean enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread); disableSamplingEvents()53 public native static void disableSamplingEvents(); 54 55 /** 56 * Allocate memory but first create a stack trace. 57 * 58 * @return list of frames for the allocation. 59 */ allocate()60 public static List<Frame> allocate() { 61 int sum = 0; 62 List<Frame> frames = new ArrayList<Frame>(); 63 allocate(frames); 64 frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 63)); 65 return frames; 66 } 67 allocate(List<Frame> frames)68 private static void allocate(List<Frame> frames) { 69 int sum = 0; 70 for (int j = 0; j < allocationIterations; j++) { 71 sum += actuallyAllocate(); 72 } 73 frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 98)); 74 frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 71)); 75 } 76 repeatAllocate(int max)77 public static List<Frame> repeatAllocate(int max) { 78 List<Frame> frames = null; 79 for (int i = 0; i < max; i++) { 80 frames = allocate(); 81 } 82 frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 80)); 83 return frames; 84 } 85 actuallyAllocate()86 private static int actuallyAllocate() { 87 int sum = 0; 88 89 // Let us assume that a 1-element array is 24 bytes of memory and we want 90 // 2MB allocated. 91 int iterations = (1 << 19) / 6; 92 93 if (arrays == null) { 94 arrays = new int[iterations][]; 95 } 96 97 for (int i = 0; i < iterations; i++) { 98 int tmp[] = new int[1]; 99 // Force it to be kept and, at the same time, wipe out any previous data. 100 arrays[i] = tmp; 101 sum += arrays[0][0]; 102 } 103 return sum; 104 } 105 106 private static long oneElementSize; getSize(Frame[] frames, boolean checkLines)107 private static native long getSize(Frame[] frames, boolean checkLines); getSize(Frame[] frames)108 private static long getSize(Frame[] frames) { 109 return getSize(frames, getCheckLines()); 110 } 111 112 // Calculate the size of a 1-element array in order to assess sampling interval 113 // via the HeapMonitorStatIntervalTest. 114 // This is done by allocating a 1-element array and then looking in the heap monitoring 115 // samples for the size of an object collected. calculateOneElementSize()116 public static void calculateOneElementSize() { 117 enableSamplingEvents(); 118 119 List<Frame> frameList = allocate(); 120 disableSamplingEvents(); 121 122 frameList.add(new Frame("calculateOneElementSize", "()V", "HeapMonitor.java", 119)); 123 Frame[] frames = frameList.toArray(new Frame[0]); 124 125 // Get the actual size. 126 oneElementSize = getSize(frames); 127 System.out.println("Element size is: " + oneElementSize); 128 129 if (oneElementSize == 0) { 130 throw new RuntimeException("Could get the size of a 1-element array."); 131 } 132 } 133 allocateSize(int totalSize)134 public static int allocateSize(int totalSize) { 135 if (oneElementSize == 0) { 136 throw new RuntimeException("Size of a 1-element array was not calculated."); 137 } 138 139 int sum = 0; 140 int iterations = (int) (totalSize / oneElementSize); 141 142 if (arrays == null || arrays.length < iterations) { 143 arrays = new int[iterations][]; 144 } 145 146 System.out.println("Allocating for " + iterations); 147 for (int i = 0; i < iterations; i++) { 148 int tmp[] = new int[1]; 149 150 // Force it to be kept and, at the same time, wipe out any previous data. 151 arrays[i] = tmp; 152 sum += arrays[0][0]; 153 } 154 155 return sum; 156 } 157 158 /** Remove the reference to the global array to free data at the next GC. */ freeStorage()159 public static void freeStorage() { 160 arrays = null; 161 } 162 sampleEverything()163 public static int[][][] sampleEverything() { 164 enableSamplingEvents(); 165 setSamplingInterval(0); 166 167 // Loop around an allocation loop and wait until the tlabs have settled. 168 final int maxTries = 10; 169 int[][][] result = new int[maxTries][][]; 170 for (int i = 0; i < maxTries; i++) { 171 final int maxInternalTries = 400; 172 result[i] = new int[maxInternalTries][]; 173 174 resetEventStorage(); 175 for (int j = 0; j < maxInternalTries; j++) { 176 final int size = 1000; 177 result[i][j] = new int[size]; 178 } 179 180 int sampledEvents = sampledEvents(); 181 if (sampledEvents == maxInternalTries) { 182 return result; 183 } 184 } 185 186 throw new RuntimeException("Could not set the sampler"); 187 } 188 allocateAndCheckFrames(boolean shouldFindFrames, boolean enableSampling)189 public static Frame[] allocateAndCheckFrames(boolean shouldFindFrames, 190 boolean enableSampling) { 191 if (!eventStorageIsEmpty()) { 192 throw new RuntimeException("Statistics should be null to begin with."); 193 } 194 195 // Put sampling rate to 100k to ensure samples are collected. 196 setSamplingInterval(100 * 1024); 197 198 if (enableSampling) { 199 enableSamplingEvents(); 200 } 201 202 List<Frame> frameList = allocate(); 203 frameList.add(new Frame("allocateAndCheckFrames", "(ZZ)[LMyPackage/Frame;", "HeapMonitor.java", 204 202)); 205 Frame[] frames = frameList.toArray(new Frame[0]); 206 207 boolean foundLive = obtainedEvents(frames); 208 boolean foundGarbage = garbageContains(frames); 209 if (shouldFindFrames) { 210 if (!foundLive && !foundGarbage) { 211 throw new RuntimeException("No expected events were found: " 212 + foundLive + ", " + foundGarbage); 213 } 214 } else { 215 if (foundLive || foundGarbage) { 216 throw new RuntimeException("Were not expecting events, but found some: " 217 + foundLive + ", " + foundGarbage); 218 } 219 } 220 221 return frames; 222 } 223 allocateAndCheckFrames()224 public static Frame[] allocateAndCheckFrames() { 225 return allocateAndCheckFrames(true, true); 226 } 227 sampledEvents()228 public native static int sampledEvents(); obtainedEvents(Frame[] frames, boolean checkLines)229 public native static boolean obtainedEvents(Frame[] frames, boolean checkLines); garbageContains(Frame[] frames, boolean checkLines)230 public native static boolean garbageContains(Frame[] frames, boolean checkLines); eventStorageIsEmpty()231 public native static boolean eventStorageIsEmpty(); resetEventStorage()232 public native static void resetEventStorage(); getEventStorageElementCount()233 public native static int getEventStorageElementCount(); forceGarbageCollection()234 public native static void forceGarbageCollection(); enableVMEvents()235 public native static boolean enableVMEvents(); 236 getCheckLines()237 private static boolean getCheckLines() { 238 boolean checkLines = true; 239 240 // Do not check lines for Graal since it is not always "precise" with BCIs at uncommon traps. 241 try { 242 HotSpotDiagnosticMXBean bean = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class); 243 244 VMOption enableJVMCI = bean.getVMOption("EnableJVMCI"); 245 VMOption useJVMCICompiler = bean.getVMOption("UseJVMCICompiler"); 246 String compiler = System.getProperty("jvmci.Compiler"); 247 248 checkLines = !(enableJVMCI.getValue().equals("true") 249 && useJVMCICompiler.getValue().equals("true") && compiler.equals("graal")); 250 } catch (Exception e) { 251 // NOP. 252 } 253 254 return checkLines; 255 } 256 obtainedEvents(Frame[] frames)257 public static boolean obtainedEvents(Frame[] frames) { 258 return obtainedEvents(frames, getCheckLines()); 259 } 260 garbageContains(Frame[] frames)261 public static boolean garbageContains(Frame[] frames) { 262 return garbageContains(frames, getCheckLines()); 263 } 264 statsHaveExpectedNumberSamples(int expected, int acceptedErrorPercentage)265 public static boolean statsHaveExpectedNumberSamples(int expected, int acceptedErrorPercentage) { 266 double actual = sampledEvents(); 267 double diffPercentage = 100 * Math.abs(actual - expected) / expected; 268 269 if (diffPercentage >= acceptedErrorPercentage) { 270 System.err.println("Unexpected high difference percentage: " + diffPercentage 271 + " due to the count being " + actual + " instead of " + expected); 272 } 273 return diffPercentage < acceptedErrorPercentage; 274 } 275 setAllocationIterations(int iterations)276 public static void setAllocationIterations(int iterations) { 277 allocationIterations = iterations; 278 } 279 } 280