8153764: Add auxiliary method that generates class by class prototype to gc testlibrary
authorkzhaldyb
Thu, 21 Apr 2016 18:01:10 +0300
changeset 38015 8dd94eb5dcfc
parent 38014 8731fa11f766
child 38016 c960918f0bfb
8153764: Add auxiliary method that generates class by class prototype to gc testlibrary Reviewed-by: dfazunen, iignatyev
hotspot/test/gc/testlibrary/Helpers.java
--- a/hotspot/test/gc/testlibrary/Helpers.java	Thu Apr 21 09:23:04 2016 -0400
+++ b/hotspot/test/gc/testlibrary/Helpers.java	Thu Apr 21 18:01:10 2016 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 
 public class Helpers {
 
@@ -45,14 +46,14 @@
 
     /**
      * According class file format theoretical amount of fields in class is u2 which is (256 * 256 - 1).
-     * Some service info takes place in constant pool and we really could make a class with only (256 * 256 - 29)
-     * fields.
+     * Some service info takes place in constant pool and we really could make a class with lesser amount of fields.
+     *
      * Since the exact value is not so important and I would like to avoid issues that may be caused by future changes/
-     * different archs etc I selected (256 * 256 - 32) for this constant.
+     * different archs etc I selected (256 * 256 - 1024) for this constant.
      * The test works with other values too but the smaller the number the more classes we need to generate and it takes
      * more time
      */
-    private static final int MAXIMUM_AMOUNT_OF_FIELDS_IN_CLASS = 256 * 256 - 32;
+    private static final int MAXIMUM_AMOUNT_OF_FIELDS_IN_CLASS = 256 * 256 - 1024;
 
     /**
      * Detects amount of extra bytes required to allocate a byte array.
@@ -140,6 +141,28 @@
     }
 
     /**
+     * Generates specified amount of long fields
+     * Result string will looks like this:
+     * <p>
+     * long f0;
+     * ...
+     * long fNNN;
+     *
+     * @param fieldCount count of long fields
+     * @return generated fields
+     */
+    private static String fieldsGenerator(long fieldCount) {
+        StringBuilder fieldsBuilder = new StringBuilder();
+
+        for (int i = 0; i < fieldCount; ++i) {
+            fieldsBuilder.append(String.format("long f%d;\n", i));
+        }
+
+        return fieldsBuilder.toString();
+    }
+
+
+    /**
      * Changes string from enum notation to class notation - i.e. "VERY_SMALL_CAT" to "VerySmallCat"
      *
      * @param enumName string in enum notation
@@ -216,22 +239,121 @@
             // for the last generated class we use specified class name
             String clsName = (i == generatedClassesCount - 1) ? className : prefix + i;
 
-            Helpers.compileClass(clsName, workDir,
-                    Helpers.generate(
-                            clsName,
-                            // for first generated class we don't have 'extends'
-                            (i == 0 ? null : prefix + (i - 1)),
-                            null,
-                            // for the last generated class we use different field count
-                            (i == generatedClassesCount - 1) ? fieldsInLastClassCount
-                                    : MAXIMUM_AMOUNT_OF_FIELDS_IN_CLASS));
+            // If we already have a file with the same name we do not create it again
+            if (Files.notExists(Paths.get(clsName + ".java"))) {
+                Helpers.compileClass(clsName, workDir,
+                        Helpers.generate(
+                                clsName,
+                                // for first generated class we don't have 'extends'
+                                (i == 0 ? null : prefix + (i - 1)),
+                                null,
+                                // for the last generated class we use different field count
+                                (i == generatedClassesCount - 1) ? fieldsInLastClassCount
+                                        : MAXIMUM_AMOUNT_OF_FIELDS_IN_CLASS));
+            } else {
+                System.out.println("Class " + clsName +
+                        ".java already exists, skipping class' generation and compilation");
+            }
+
         }
         return classLoader.loadClass(className);
     }
 
     /**
+     * Creates a class which instances will be approximately of the requested size.
+     * This method produces a java source from a class template by substituting values instead of parameters.
+     * Then the obtained source is compiled.
+     * Generated class will looks like this:
+     * classTemplate
+     * constructorTemplate
+     * long f0;
+     * ...
+     * long fNNN;
+     * <p>
+     * }
+     *
+     * @param className    generated class name
+     * @param baseClass    base class
+     * @param classTemplate class template - the first part of class. ${ClassName} and ${BaseClass} will be replaced
+     *                      with values from className and baseClass,one entry of ${Fields} will be replaced with
+     *                      generated long fields. Class template should look like this:
+     *                      imports;
+     *                      public class ${ClassName} extends ${BaseClass} {
+     *                         public ${ClassName}  { some code here;}
+     *                         some methods
+     *                         ${Fields}
+     *
+     *                      }
+     * @param constructorTemplate constructor template, ${ClassName} would be replaced on actual class name
+     * @param instanceSize size of generated class' instance. Size should be aligned by 8 bytes
+     * @param workDir      working dir where generated classes are put and compiled
+     * @param prefix       prefix for service classes (ones we use to create chain of inheritance).
+     *                     The names will be prefix_1, prefix_2,.., prefix_n
+     * @return Class object of generated and compiled class loaded in specified class loader
+     * @throws IOException if cannot write or read to workDir
+     */
+    public static void generateByTemplateAndCompile(String className, String baseClass, String classTemplate,
+                                                    String constructorTemplate, long instanceSize, Path workDir,
+                                                    String prefix) throws IOException {
+
+        if (instanceSize % SIZE_OF_LONG != 0L) {
+            throw new Error(String.format("Test bug: only sizes aligned by %d bytes are supported and %d was specified",
+                    SIZE_OF_LONG, instanceSize));
+        }
+
+        int instanceSizeWithoutObjectHeaderInWords =
+                (int) (instanceSize - WhiteBox.getWhiteBox().getObjectSize(new Object())) / SIZE_OF_LONG;
+
+        if (instanceSizeWithoutObjectHeaderInWords <= 0) {
+            throw new Error(String.format("Test bug: specified instance size is too small - %d."
+                    + " Cannot generate any classes", instanceSize));
+        }
+
+        int sizeOfLastFile = instanceSizeWithoutObjectHeaderInWords % MAXIMUM_AMOUNT_OF_FIELDS_IN_CLASS;
+        int generatedClassesCount = instanceSizeWithoutObjectHeaderInWords / MAXIMUM_AMOUNT_OF_FIELDS_IN_CLASS;
+
+        // Do all the classes have the maximum number of fields?
+        int fieldsInLastClassCount;
+
+        if (sizeOfLastFile == 0) {
+            fieldsInLastClassCount = MAXIMUM_AMOUNT_OF_FIELDS_IN_CLASS;
+        } else {
+            generatedClassesCount++;
+            fieldsInLastClassCount = sizeOfLastFile;
+        }
+
+        // first (generatedClassesCount - 1) classes are just fillers - just long fields and constructor
+        for (int i = 0; i < generatedClassesCount - 1; i++) {
+            String clsName = prefix + i;
+
+            Helpers.compileClass(clsName, workDir,
+                    Helpers.generate(
+                            clsName,
+                            // first generated class extends base class
+                            (i == 0 ? baseClass : prefix + (i - 1)),
+                            constructorTemplate.replace("${ClassName}", clsName),
+                            MAXIMUM_AMOUNT_OF_FIELDS_IN_CLASS));
+        }
+
+        // generating last class - the one with specified className
+        Helpers.compileClass(className, workDir,
+                classTemplate.replaceAll("\\$\\{ClassName\\}", className)
+                        // if no fillers were generated (generatedClassesCount == 1)
+                        // the last class should extends baseClass
+                        // otherwise it should extend last generated filler class which name is
+                        // prefix + (generatedClassesCount - 2)
+                        // generatedClassesCount is always not smaller than 1
+                        .replace("${BaseClass}",
+                                generatedClassesCount == 1 ? baseClass :
+                                        prefix + (generatedClassesCount - 2))
+                        .replace("${Fields}", fieldsGenerator(fieldsInLastClassCount))
+        );
+    }
+
+    /**
      * Waits until Concurent Mark Cycle finishes
-     * @param wb  Whitebox instance
+     *
+     * @param wb        Whitebox instance
      * @param sleepTime sleep time
      */
     public static void waitTillCMCFinished(WhiteBox wb, int sleepTime) {