1 /*
   2  * Copyright (c) 2018, Google 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package MyPackage;
  25 
  26 import java.util.ArrayList;
  27 import java.util.List;
  28 
  29 /** API for handling the underlying heap sampling monitoring system. */
  30 public class HeapMonitor {
  31   private static int[][] arrays;
  32   private static int allocationIterations = 1000;
  33 
  34   static {
  35     try {
  36       System.loadLibrary("HeapMonitorTest");
  37     } catch (UnsatisfiedLinkError ule) {
  38       System.err.println("Could not load HeapMonitor library");
  39       System.err.println("java.library.path: " + System.getProperty("java.library.path"));
  40       throw ule;
  41     }
  42   }
  43 
  44   /** Set a specific sampling rate, 0 samples every allocation. */
  45   public native static void setSamplingRate(int rate);
  46   public native static void enableSamplingEvents();
  47   public native static boolean enableSamplingEventsForTwoThreads(Thread firstThread, Thread secondThread);
  48   public native static void disableSamplingEvents();
  49 
  50   /**
  51    * Allocate memory but first create a stack trace.
  52    *
  53    * @return list of frames for the allocation.
  54    */
  55   public static List<Frame> allocate() {
  56     int sum = 0;
  57     List<Frame> frames = new ArrayList<Frame>();
  58     allocate(frames);
  59     frames.add(new Frame("allocate", "()Ljava/util/List;", "HeapMonitor.java", 58));
  60     return frames;
  61   }
  62 
  63   private static void allocate(List<Frame> frames) {
  64     int sum = 0;
  65     for (int j = 0; j < allocationIterations; j++) {
  66       sum += actuallyAllocate();
  67     }
  68     frames.add(new Frame("actuallyAllocate", "()I", "HeapMonitor.java", 93));
  69     frames.add(new Frame("allocate", "(Ljava/util/List;)V", "HeapMonitor.java", 66));
  70   }
  71 
  72   public static List<Frame> repeatAllocate(int max) {
  73     List<Frame> frames = null;
  74     for (int i = 0; i < max; i++) {
  75       frames = allocate();
  76     }
  77     frames.add(new Frame("repeatAllocate", "(I)Ljava/util/List;", "HeapMonitor.java", 75));
  78     return frames;
  79   }
  80 
  81   private static int actuallyAllocate() {
  82     int sum = 0;
  83 
  84     // Let us assume that a 1-element array is 24 bytes of memory and we want
  85     // 2MB allocated.
  86     int iterations = (1 << 19) / 6;
  87 
  88     if (arrays == null) {
  89       arrays = new int[iterations][];
  90     }
  91 
  92     for (int i = 0; i < iterations; i++) {
  93       int tmp[] = new int[1];
  94       // Force it to be kept and, at the same time, wipe out any previous data.
  95       arrays[i] = tmp;
  96       sum += arrays[0][0];
  97     }
  98     return sum;
  99   }
 100 
 101   public static int allocateSize(int totalSize) {
 102     int sum = 0;
 103 
 104     // Let us assume that a 1-element array is 24 bytes.
 105     int iterations = totalSize / 24;
 106 
 107     if (arrays == null) {
 108       arrays = new int[iterations][];
 109     }
 110 
 111     System.out.println("Allocating for " + iterations);
 112     for (int i = 0; i < iterations; i++) {
 113       int tmp[] = new int[1];
 114 
 115       // Force it to be kept and, at the same time, wipe out any previous data.
 116       arrays[i] = tmp;
 117       sum += arrays[0][0];
 118     }
 119 
 120     return sum;
 121   }
 122 
 123   /** Remove the reference to the global array to free data at the next GC. */
 124   public static void freeStorage() {
 125     arrays = null;
 126   }
 127 
 128   public static int[][][] sampleEverything() {
 129     enableSamplingEvents();
 130     setSamplingRate(0);
 131 
 132     // Loop around an allocation loop and wait until the tlabs have settled.
 133     final int maxTries = 10;
 134     int[][][] result = new int[maxTries][][];
 135     for (int i = 0; i < maxTries; i++) {
 136       final int maxInternalTries = 400;
 137       result[i] = new int[maxInternalTries][];
 138 
 139       resetEventStorage();
 140       for (int j = 0; j < maxInternalTries; j++) {
 141         final int size = 1000;
 142         result[i][j] = new int[size];
 143       }
 144 
 145       int sampledEvents = sampledEvents();
 146       if (sampledEvents == maxInternalTries) {
 147         return result;
 148       }
 149     }
 150 
 151     throw new RuntimeException("Could not set the sampler");
 152   }
 153 
 154   public native static int sampledEvents();
 155   public native static boolean obtainedEvents(Frame[] frames);
 156   public native static boolean garbageContains(Frame[] frames);
 157   public native static boolean eventStorageIsEmpty();
 158   public native static void resetEventStorage();
 159   public native static int getEventStorageElementCount();
 160   public native static void forceGarbageCollection();
 161   public native static boolean enableVMEvents();
 162 
 163   public static boolean statsHaveExpectedNumberSamples(int expected, int acceptedErrorPercentage) {
 164     double actual = getEventStorageElementCount();
 165     double diffPercentage = Math.abs(actual - expected) / expected;
 166     return diffPercentage < acceptedErrorPercentage;
 167   }
 168 
 169   public static void setAllocationIterations(int iterations) {
 170     allocationIterations = iterations;
 171   }
 172 }