1 
   2 import sun.hotspot.WhiteBox;
   3 
   4 import java.util.ArrayList;
   5 import java.util.HashSet;
   6 import java.util.List;
   7 
   8 public class MetaspaceTestContext {
   9 
  10     long context;
  11 
  12     final long commitLimit;
  13     final long reserveLimit;
  14 
  15     int numArenasCreated;
  16     int numArenasDestroyed;
  17 
  18     HashSet<MetaspaceTestArena> arenaList = new HashSet<>();
  19 
  20     long allocatedWords;
  21     long numAllocated;
  22     long deallocatedWords;
  23     long numDeallocated;
  24     long allocationFailures;
  25 
  26 
  27     public MetaspaceTestContext(long commitLimit, long reserveLimit) {
  28         this.commitLimit = commitLimit;
  29         this.reserveLimit = reserveLimit;
  30         WhiteBox wb = WhiteBox.getWhiteBox();
  31         context = wb.createMetaspaceTestContext(commitLimit, reserveLimit);
  32         if (context == 0) {
  33             throw new RuntimeException("Failed to create context");
  34         }
  35     }
  36 
  37     // no limits
  38     public MetaspaceTestContext() {
  39         this(0, 0);
  40     }
  41 
  42     public void destroy() {
  43         if (context != 0) {
  44             WhiteBox wb = WhiteBox.getWhiteBox();
  45             wb.destroyMetaspaceTestContext(context);
  46             context = 0;
  47         }
  48     }
  49 
  50     public void purge() {
  51         if (context != 0) {
  52             WhiteBox wb = WhiteBox.getWhiteBox();
  53             wb.purgeMetaspaceTestContext(context);
  54         }
  55     }
  56 
  57     public MetaspaceTestArena createArena(boolean is_micro, long ceiling) {
  58         MetaspaceTestArena arena = null;
  59         if (context != 0) {
  60             WhiteBox wb = WhiteBox.getWhiteBox();
  61             long arena0 = wb.createArenaInTestContext(context, is_micro);
  62             if (arena0 == 0) {
  63                 throw new RuntimeException("Failed to create arena");
  64             }
  65             numArenasCreated++;
  66             arena = new MetaspaceTestArena(arena0, ceiling);
  67             arenaList.add(arena);
  68         }
  69         return arena;
  70     }
  71 
  72     public void destroyArena(MetaspaceTestArena a) {
  73         if (context != 0) {
  74             if (a.isLive()) {
  75                 WhiteBox wb = WhiteBox.getWhiteBox();
  76                 wb.destroyMetaspaceTestArena(a.arena);
  77                 numArenasDestroyed++;
  78             }
  79             arenaList.remove(a);
  80         }
  81     }
  82 
  83     public long committedWords() {
  84         long l = 0;
  85         if (context != 0) {
  86             WhiteBox wb = WhiteBox.getWhiteBox();
  87             l = wb.getTotalCommittedWordsInMetaspaceTestContext(context);
  88         }
  89         return l;
  90     }
  91 
  92     public long usedWords() {
  93         long l = 0;
  94         if (context != 0) {
  95             WhiteBox wb = WhiteBox.getWhiteBox();
  96             l = wb.getTotalUsedWordsInMetaspaceTestContext(context);
  97         }
  98         return l;
  99     }
 100 
 101     public int numLiveArenas() {
 102         return arenaList.size();
 103     }
 104 
 105     public void updateTotals() {
 106         allocatedWords = deallocatedWords = numAllocated = numDeallocated = 0;
 107         for (MetaspaceTestArena a : arenaList) {
 108             allocatedWords += a.allocatedWords;
 109             deallocatedWords += a.deallocatedWords;
 110             numAllocated += a.numAllocated;
 111             numDeallocated += a.numDeallocated;
 112             allocationFailures += a.numAllocationFailures;
 113         }
 114     }
 115 
 116     public void printToTTY() {
 117         if (context != 0) {
 118             WhiteBox wb = WhiteBox.getWhiteBox();
 119             wb.printMetaspaceTestContext(context);
 120         }
 121     }
 122 
 123     /**
 124      * Given usage and some context information for current live arenas, do a heuristic about whether the
 125      * Usage seems right for this case.
 126      */
 127     public void checkStatistics() {
 128 
 129 
 130         // Note:
 131         // Estimating Used and Committed is fuzzy, and we only have limited information here
 132         // (we know the current state, but not the history, which determines fragmentation and
 133         //  freelist occupancy).
 134         //
 135         // We do not want test which constantly generate false positives, so these checks are
 136         // somewhat loose and only meant to check for clear outliers, e.g. leaks.
 137 
 138         ///// used /////
 139 
 140         updateTotals();
 141 
 142         long usageMeasured = usedWords();
 143         long committedMeasured = committedWords();
 144 
 145         if (usageMeasured > committedMeasured) {
 146             throw new RuntimeException("Weirdness.");
 147         }
 148 
 149         if (deallocatedWords > allocatedWords) {
 150             throw new RuntimeException("Weirdness.");
 151         }
 152 
 153         // If no arenas are alive, usage should be zero and committed too (in reclaiming mode)
 154         if (numLiveArenas() == 0) {
 155             if (usageMeasured > 0) {
 156                 throw new RuntimeException("Usage > 0, expected 0");
 157             }
 158             if (Settings.settings().doesReclaim()) {
 159                 if (committedMeasured > 0) {
 160                     throw new RuntimeException("Committed > 0, expected 0");
 161                 }
 162             }
 163         }
 164 
 165         long expectedMinUsage = allocatedWords - deallocatedWords;
 166 
 167         if (usageMeasured < expectedMinUsage) {
 168             throw new RuntimeException("Usage too low: " + usageMeasured + " expected at least " + expectedMinUsage);
 169         }
 170 
 171         long expectedMaxUsage = allocatedWords;
 172 
 173         // This is necessary a bit fuzzy, since Metaspace usage consists of:
 174         // - whatever we allocated
 175         // - deallocated blocks in fbl
 176         // - remains of retired chunks in fbl
 177         // - overhead per allocation (padding for alignment, possibly allocation guards)
 178 
 179         // Overhead per allocation (see metaspaceArena.cpp, get_raw_allocation_word_size() )
 180         // Any allocation is 3 words least
 181         expectedMaxUsage += (numAllocated * 3);
 182         if (Settings.settings().usesAllocationGuards) {
 183             // Guards need space.
 184             expectedMaxUsage += (numAllocated * 2);
 185             // Also, they disable the fbl, so deallocated still counts as used.
 186             expectedMaxUsage += deallocatedWords;
 187         }
 188 
 189         // Lets add a overhead per arena. Each arena carries a free block list containing
 190         // deallocated/retired blocks. We do not know how much. In general, the free block list should not
 191         // accumulate a lot of memory but be drained in the course of allocating memory from the arena.
 192         long overheadPerArena = 1024 * 1024 * numLiveArenas();
 193         expectedMaxUsage += overheadPerArena;
 194 
 195         if (expectedMaxUsage < usageMeasured) {
 196             throw new RuntimeException("Usage seems high: " + usageMeasured + " expected at most " + expectedMaxUsage);
 197         }
 198 
 199         ///// Committed //////
 200 
 201         if (committedMeasured < expectedMinUsage) {
 202             throw new RuntimeException("Usage too low: " + usageMeasured + " expected at least " + expectedMinUsage);
 203         }
 204 
 205         // Max committed:
 206         // This is difficult to estimate, so just a rough guess.
 207         //
 208         // Committed space depends on:
 209         // 1) Usage (how much we allocated + overhead per allocation + free block list content)
 210         // 2) free space in used chunks
 211         // 3) committed chunks in freelist.
 212         //
 213         // Having only live usage numbers without history, (2) and (3) can only be roughly estimated. Since these
 214         // are stress tests,
 215         //
 216         long expectedMaxCommitted = usageMeasured;
 217         expectedMaxCommitted += Settings.rootChunkWordSize;
 218         if (Settings.settings().doesReclaim()) {
 219             expectedMaxCommitted *= 10.0;
 220         } else {
 221             expectedMaxCommitted *= 100.0;
 222         }
 223 
 224         if (committedMeasured > expectedMaxCommitted) {
 225             throw new RuntimeException("Committed seems high: " + committedMeasured + " expected at most " + expectedMaxCommitted);
 226         }
 227 
 228     }
 229 
 230     @java.lang.Override
 231     public java.lang.String toString() {
 232         return "MetaspaceTestContext{" +
 233                 "context=" + context +
 234                 ", commitLimit=" + commitLimit +
 235                 ", reserveLimit=" + reserveLimit +
 236                 ", numArenasCreated=" + numArenasCreated +
 237                 ", numArenasDestroyed=" + numArenasDestroyed +
 238                 ", numLiveArenas=" + numLiveArenas() +
 239                 ", allocatedWords=" + allocatedWords +
 240                 ", numAllocated=" + numAllocated +
 241                 ", deallocatedWords=" + deallocatedWords +
 242                 ", numDeallocated=" + numDeallocated +
 243                 ", allocationFailures=" + allocationFailures +
 244                 '}';
 245     }
 246 }