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