1 /*
   2  * Copyright (c) 2007, 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.
   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 nsk.share.gc.gp;
  25 
  26 import java.io.IOException;
  27 import java.io.PrintWriter;
  28 import java.io.StringWriter;
  29 import java.util.*;
  30 import nsk.share.gc.gp.array.*;
  31 import nsk.share.gc.gp.string.*;
  32 import nsk.share.gc.gp.list.*;
  33 import nsk.share.gc.gp.tree.*;
  34 import nsk.share.gc.gp.misc.*;
  35 import nsk.share.gc.gp.classload.*;
  36 import nsk.share.gc.Memory;
  37 import nsk.share.TestBug;
  38 import nsk.share.test.*;
  39 
  40 /**
  41  * Utility methods for garbage producers.
  42  */
  43 public final class GarbageUtils {
  44         private static final int ALLOCATION_LIMIT = 50000000; //50 Mb
  45         private static GarbageProducers garbageProducers;
  46         private static List<GarbageProducer> primitiveArrayProducers;
  47         private static List<GarbageProducer> arrayProducers;
  48         private static final GarbageProducer  byteArrayProducer = new ByteArrayProducer();
  49         public static enum OOM_TYPE {
  50             ANY (),
  51             HEAP("Java heap space"),
  52             METASPACE("Metaspace", "Compressed class space");
  53 
  54             private final String[] expectedStrings;
  55             OOM_TYPE(String... expectedStrings) {
  56                 this.expectedStrings = expectedStrings;
  57             }
  58 
  59             /**
  60                          * Returns true if the given error message matches
  61                          * one of expected strings.
  62                          */
  63                         public boolean accept(String errorMessage) {
  64                 if (expectedStrings == null || expectedStrings.length == 0 || errorMessage == null) {
  65                     return true;
  66                 }
  67                 for (String s: expectedStrings) {
  68                     if (errorMessage.indexOf(s) != -1) {
  69                         return true;
  70                     }
  71                 }
  72                 return false;
  73             }
  74         };
  75 
  76         // Force loading of OOM_TYPE and calling of enum contrusctors when loading GarbageUtils class.
  77         public static final Object[] thisIsGarbageArray_theOnlyPurposeForCreatingItAndDeclaringItPublicIsToInitializeIntancesOfOOMEnumberation = new Object[] { OOM_TYPE.ANY, OOM_TYPE.HEAP, OOM_TYPE.METASPACE };
  78 
  79         // Force early loading of classes that might otherwise unexpectedly fail
  80         // class loading during testing due to high memory pressure.
  81         public static final StringWriter preloadStringWriter = new StringWriter(1);
  82         public static final PrintWriter preloadPrintWriter = new PrintWriter(preloadStringWriter);
  83 
  84         private GarbageUtils() {
  85         }
  86 
  87         /**
  88          * Eat memory using execution controller that waits for 2 minutes.
  89          * @return number of OOME occured
  90          */
  91         public static int eatMemory() {
  92                 return eatMemory(2 * 60 * 1000);
  93         }
  94 
  95         /**
  96          * Eat memory using execution controller that waits for timeout.
  97          * @return number of OOME occured
  98          */
  99         public static int eatMemory(final long timeout) {
 100                 return eatMemory(new ExecutionController() {
 101                         final long initialTime = System.currentTimeMillis();
 102 
 103                                 @Override
 104                                 public void start(long stdIterations) {}
 105 
 106                                 @Override
 107                                 public boolean iteration() {return false;}
 108 
 109                                 @Override
 110                                 public boolean continueExecution() {
 111                                         return System.currentTimeMillis() - initialTime < timeout;
 112                                 }
 113 
 114                                 @Override
 115                                 public long getIteration() {return 0;}
 116 
 117                                 @Override
 118                                 public void finish() {}
 119                 });
 120         }
 121 
 122 
 123         /**
 124          * Eat memory using given execution controller and garbage producer.
 125          *
 126          * @param stresser execution controller
 127          * @param gp garbage producer
 128          * @return number of OOME occured
 129          */
 130         public static int eatMemory(ExecutionController stresser) {
 131             return eatMemory(stresser, byteArrayProducer, 50, 100, 2, OOM_TYPE.ANY);
 132         }
 133 
 134         /**
 135          * Eat memory using given execution controller and garbage producer.
 136          *
 137          * @param stresser execution controller
 138          * @param gp garbage producer
 139          * @return number of OOME occured
 140          */
 141         public static int eatMemory(ExecutionController stresser, GarbageProducer gp) {
 142             return eatMemory(stresser, gp, 50, 100, 2, OOM_TYPE.ANY);
 143         }
 144 
 145         /**
 146          * Eat memory using given garbage producer and given factor.
 147          *
 148          * @param gp garbage producer
 149          * @param factor factor to divide the array size by
 150          * @return number of OOME occured
 151          */
 152         public static int eatMemory(ExecutionController stresser, GarbageProducer gp, long factor) {
 153             return eatMemory(stresser, gp, 50, 100, factor, OOM_TYPE.ANY);
 154         }
 155 
 156         /**
 157          * Eat memory using default(byte[]) garbage producer.
 158          *
 159          * Note that this method can throw Failure if any exception
 160          * is thrown while eating memory. To avoid OOM while allocating
 161          * exception we preallocate it before the lunch starts. It means
 162          * that exception stack trace does not correspond to the place
 163          * where exception is thrown, but points at start of the method.
 164          *
 165          * @param stresser stresser
 166          * @param initialFactor determines which portion of initial memory initial chunk will be
 167          * @param minMemoryChunk determines when to stop
 168          * @param factor factor to divide the array size by
 169          * @return number of OOME occured
 170          */
 171         public static int eatMemory(ExecutionController stresser,long initialFactor, long minMemoryChunk, long factor) {
 172             return eatMemory(stresser, byteArrayProducer, initialFactor, minMemoryChunk, factor, OOM_TYPE.ANY);
 173         }
 174 
 175         /**
 176          * Eat memory using given garbage producer.
 177          *
 178          * Note that this method can throw Failure if any exception
 179          * is thrown while eating memory. To avoid OOM while allocating
 180          * exception we preallocate it before the lunch starts. It means
 181          * that exception stack trace does not correspond to the place
 182          * where exception is thrown, but points at start of the method.
 183          *
 184          * @param stresser stresser to use
 185          * @param gp garbage producer
 186          * @param initialFactor determines which portion of initial memory initial chunk will be
 187          * @param minMemoryChunk determines when to stop
 188          * @param factor factor to divide the array size by. A value of 0 means that method returns after first  OOME
 189          * @param type of OutOfMemory Exception: Java heap space or Metadata space
 190          * @return number of OOME occured
 191          */
 192         public static int eatMemory(ExecutionController stresser, GarbageProducer gp, long initialFactor, long minMemoryChunk, long factor) {
 193             return eatMemory(stresser, gp, initialFactor, minMemoryChunk, factor, OOM_TYPE.ANY);
 194         }
 195 
 196         /**
 197          * Eat memory using given garbage producer.
 198          *
 199          * Note that this method can throw Failure if any exception
 200          * is thrown while eating memory. To avoid OOM while allocating
 201          * exception we preallocate it before the lunch starts. It means
 202          * that exception stack trace does not correspond to the place
 203          * where exception is thrown, but points at start of the method.
 204          *
 205          * @param stresser stresser to use
 206          * @param gp garbage producer
 207          * @param initialFactor determines which portion of initial memory initial chunk will be
 208          * @param minMemoryChunk determines when to stop
 209          * @param factor factor to divide the array size by. A value of 0 means that method returns after first  OOME
 210          * @param type of OutOfMemory Exception: Java heap space or Metadata space
 211          * @return number of OOME occured
 212          */
 213         public static int eatMemory(ExecutionController stresser, GarbageProducer gp, long initialFactor, long minMemoryChunk, long factor, OOM_TYPE type) {
 214                 int numberOfOOMEs = 0;
 215                 try {
 216                         StringWriter sw = new StringWriter(10000);
 217                         PrintWriter pw = new PrintWriter(sw);
 218                         byte[] someMemory = new byte[200000]; //200 Kb
 219                         try {
 220                                 Runtime runtime = Runtime.getRuntime();
 221                                 long maxMemory = runtime.maxMemory();
 222                                 long maxMemoryChunk = maxMemory / initialFactor;
 223                                 long chunk = maxMemoryChunk;
 224                                 chunk = chunk > ALLOCATION_LIMIT ? ALLOCATION_LIMIT : chunk;
 225                                 int allocations = 0;
 226                                 List<Object> storage = new ArrayList<Object>();
 227 
 228                                 while (chunk > minMemoryChunk && stresser.continueExecution()) {
 229                                         try {
 230                                                 storage.add(gp.create(chunk));
 231                                                 if (Thread.currentThread().isInterrupted()) {
 232                                                         return numberOfOOMEs;
 233                                                 }
 234                                                 // if we are able to eat chunk*factor let
 235                                                 // try to increase size of chunk
 236                                                 if (chunk * factor < maxMemoryChunk
 237                                                         && factor != 0 && allocations++ == factor + 1) {
 238                                                     chunk = chunk * factor;
 239                                                     allocations = 0;
 240                                                 }
 241                                         } catch (OutOfMemoryError e) {
 242                                             someMemory = null;
 243                                             if (type != OOM_TYPE.ANY) {
 244                                                 e.printStackTrace(pw);
 245                                                 pw.close();
 246                                                 if (type.accept(sw.toString())) {
 247                                                     numberOfOOMEs++;
 248                                                 } else {
 249                                                     // Trying to catch situation when Java generates OOM different type that test trying to catch
 250                                                     throw new TestBug("Test throw OOM of unexpected type." + sw.toString());
 251                                                 }
 252                                             } else {
 253                                                numberOfOOMEs++;
 254                                             }
 255                                             allocations = 0;
 256                                             if (factor == 0) {
 257                                                 return numberOfOOMEs;
 258                                             } else {
 259                                                 chunk = chunk / factor;
 260                                             }
 261                                         }
 262                                 }
 263                         } catch (OutOfMemoryError e) {
 264                             someMemory = null;
 265                             if (type != OOM_TYPE.ANY) {
 266                                 e.printStackTrace(pw);
 267                                 pw.close();
 268                                 if (type.accept(sw.toString())) {
 269                                     numberOfOOMEs++;
 270                                 } else {
 271                                     // Trying to catch situation when Java generates OOM different type that test trying to catch
 272                                     throw new TestBug("Test throw OOM of unexpected type." + sw.toString());
 273                                 }
 274                             } else {
 275                                 numberOfOOMEs++;
 276                             }
 277                          // all memory is eaten now even before we start, just return
 278                         }
 279                 } catch (OutOfMemoryError e) {
 280                         numberOfOOMEs++;
 281                 }
 282                 return numberOfOOMEs;
 283         }
 284 
 285         /**
 286          * Get all primitive array producers.
 287          */
 288         public static List<GarbageProducer> getPrimitiveArrayProducers() {
 289                 return getGarbageProducers().getPrimitiveArrayProducers();
 290         }
 291 
 292         /**
 293          * Get all array producers.
 294          */
 295         public static List<GarbageProducer> getArrayProducers() {
 296                 return getGarbageProducers().getArrayProducers();
 297         }
 298 
 299         /**
 300          * Determine size of each object in array which will occupy given
 301          * memory with distribution determined by given memory strategy.
 302          */
 303         public static long getArraySize(long memory, MemoryStrategy memoryStrategy) {
 304                 return memoryStrategy.getSize(memory - Memory.getArrayExtraSize(), Memory.getReferenceSize());
 305         }
 306 
 307         /**
 308          * Determine object count in array which will occupy given
 309          * memory with distribution determined by given memory strategy.
 310          */
 311         public static int getArrayCount(long memory, MemoryStrategy memoryStrategy) {
 312                 return memoryStrategy.getCount(memory - Memory.getArrayExtraSize(), Memory.getReferenceSize());
 313         }
 314 
 315         /**
 316          * Get garbage producer by identifier.
 317          *
 318          * @param id identifier
 319          * @return garbage producer for this identifier
 320          */
 321         public static GarbageProducer getGarbageProducer(String id) {
 322                 if (id == null || id.equals("byteArr"))
 323                         return new ByteArrayProducer();
 324                 else if (id.equals("booleanArr"))
 325                         return new BooleanArrayProducer();
 326                 else if (id.equals("shortArr"))
 327                         return new ShortArrayProducer();
 328                 else if (id.equals("charArr"))
 329                         return new CharArrayProducer();
 330                 else if (id.equals("intArr"))
 331                         return new IntArrayProducer();
 332                 else if (id.equals("longArr"))
 333                         return new LongArrayProducer();
 334                 else if (id.equals("floatArr"))
 335                         return new FloatArrayProducer();
 336                 else if (id.equals("doubleArr"))
 337                         return new DoubleArrayProducer();
 338                 else if (id.equals("objectArr"))
 339                         return new ObjectArrayProducer();
 340                 else if (id.equals("randomString"))
 341                         return new RandomStringProducer();
 342                 else if (id.equals("simpleString"))
 343                         return new SimpleStringProducer();
 344                 else if (id.startsWith("interned("))
 345                         return new InternedStringProducer(getGarbageProducer(getInBrackets(id)));
 346                 else if (id.startsWith("linearList("))
 347                         return new LinearListProducer(MemoryStrategy.fromString(getInBrackets(id)));
 348                 else if (id.startsWith("circularList("))
 349                         return new CircularListProducer(MemoryStrategy.fromString(getInBrackets(id)));
 350                 else if (id.startsWith("nonbranchyTree("))
 351                         return new NonbranchyTreeProducer(MemoryStrategy.fromString(getInBrackets(id)));
 352                 else if (id.equals("class"))
 353                         return new GeneratedClassProducer();
 354                 else if (id.startsWith("hashed("))
 355                         return new HashedGarbageProducer(getGarbageProducer(getInBrackets(id)));
 356                 else if (id.startsWith("random("))
 357                         return new RandomProducer(getGarbageProducerList(getInBrackets(id)));
 358                 else if (id.startsWith("twofields("))
 359                         return new TwoFieldsObjectProducer(getGarbageProducer(getInBrackets(id)));
 360                 else if (id.startsWith("arrayof("))
 361                         return new ArrayOfProducer(getGarbageProducer(getInBrackets(id)));
 362                 else if (id.startsWith("trace("))
 363                         return new TraceProducer(getGarbageProducer(getInBrackets(id)));
 364                 else
 365                         throw new TestBug("Invalid garbage producer identifier: " + id);
 366         }
 367 
 368         private static String getInBrackets(String s) {
 369                 int n1 = s.indexOf('(');
 370                 if (n1 == -1)
 371                         throw new TestBug("Opening bracket not found: " + s);
 372                 int n2 = s.lastIndexOf(')');
 373                 if (n2 == -1)
 374                         throw new TestBug("Closing bracket not found: " + s);
 375                 return s.substring(n1 + 1, n2);
 376         }
 377 
 378         private static List<GarbageProducer> getGarbageProducerList(String s) {
 379                 if (s.equals("primitiveArrays"))
 380                         return getPrimitiveArrayProducers();
 381                 else if (s.equals("arrays"))
 382                         return getArrayProducers();
 383                 else {
 384                         String[] ids = s.split(",");
 385                         List<GarbageProducer> garbageProducers = new ArrayList<GarbageProducer>(ids.length);
 386                         for (int i = 0; i < ids.length; ++i)
 387                                 garbageProducers.add(getGarbageProducer(ids[i]));
 388                         return garbageProducers;
 389                         //throw new TestBug("Invalid id for list of garbage producers: " + id);
 390                 }
 391         }
 392 
 393         public static GarbageProducers getGarbageProducers() {
 394                 if (garbageProducers == null)
 395                         garbageProducers = new GarbageProducers();
 396                 return garbageProducers;
 397         }
 398 }