OpenJDK / portola / portola
changeset 40673:1aff8fdbc143
Merge
author | duke |
---|---|
date | Wed, 05 Jul 2017 22:09:41 +0200 |
parents | 967c7d5073aa c55ef7ed9aa0 |
children | fb66c8bfcc34 |
files | test/lib/share/classes/jdk/test/lib/Asserts.java test/lib/share/classes/jdk/test/lib/JDKToolFinder.java test/lib/share/classes/jdk/test/lib/JDKToolLauncher.java test/lib/share/classes/jdk/test/lib/Platform.java test/lib/share/classes/jdk/test/lib/Utils.java test/lib/share/classes/jdk/test/lib/apps/LingeredApp.java test/lib/share/classes/jdk/test/lib/apps/LingeredAppWithDeadlock.java test/lib/share/classes/jdk/test/lib/hprof/HprofParser.java test/lib/share/classes/jdk/test/lib/hprof/README test/lib/share/classes/jdk/test/lib/hprof/model/AbstractJavaHeapObjectVisitor.java test/lib/share/classes/jdk/test/lib/hprof/model/ArrayTypeCodes.java test/lib/share/classes/jdk/test/lib/hprof/model/HackJavaValue.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaBoolean.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaByte.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaChar.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaClass.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaDouble.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaField.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaFloat.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaHeapObject.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaHeapObjectVisitor.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaInt.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaLazyReadObject.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaLong.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaObject.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaObjectArray.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaObjectRef.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaShort.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaStatic.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaThing.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaValue.java test/lib/share/classes/jdk/test/lib/hprof/model/JavaValueArray.java test/lib/share/classes/jdk/test/lib/hprof/model/ReachableExcludes.java test/lib/share/classes/jdk/test/lib/hprof/model/ReachableExcludesImpl.java test/lib/share/classes/jdk/test/lib/hprof/model/ReachableObjects.java test/lib/share/classes/jdk/test/lib/hprof/model/ReferenceChain.java test/lib/share/classes/jdk/test/lib/hprof/model/Root.java test/lib/share/classes/jdk/test/lib/hprof/model/Snapshot.java test/lib/share/classes/jdk/test/lib/hprof/model/StackFrame.java test/lib/share/classes/jdk/test/lib/hprof/model/StackTrace.java test/lib/share/classes/jdk/test/lib/hprof/parser/FileReadBuffer.java test/lib/share/classes/jdk/test/lib/hprof/parser/HprofReader.java test/lib/share/classes/jdk/test/lib/hprof/parser/MappedReadBuffer.java test/lib/share/classes/jdk/test/lib/hprof/parser/PositionDataInputStream.java test/lib/share/classes/jdk/test/lib/hprof/parser/PositionInputStream.java test/lib/share/classes/jdk/test/lib/hprof/parser/ReadBuffer.java test/lib/share/classes/jdk/test/lib/hprof/parser/Reader.java test/lib/share/classes/jdk/test/lib/hprof/util/ArraySorter.java test/lib/share/classes/jdk/test/lib/hprof/util/Comparer.java test/lib/share/classes/jdk/test/lib/hprof/util/CompositeEnumeration.java test/lib/share/classes/jdk/test/lib/hprof/util/Misc.java test/lib/share/classes/jdk/test/lib/hprof/util/VectorSorter.java test/lib/share/classes/jdk/test/lib/process/OutputAnalyzer.java test/lib/share/classes/jdk/test/lib/process/OutputBuffer.java test/lib/share/classes/jdk/test/lib/process/ProcessTools.java test/lib/share/classes/jdk/test/lib/process/StreamPumper.java |
diffstat | 146 files changed, 12580 insertions(+), 9849 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags-top-repo Fri Sep 02 02:41:12 2016 +0000 +++ b/.hgtags-top-repo Wed Jul 05 22:09:41 2017 +0200 @@ -376,3 +376,4 @@ 8728756c2f70a79a90188f4019cfd6b9a275765c jdk-9+131 a24702d4d5ab0015a5c553ed57f66fce7d85155e jdk-9+132 be1218f792a450dfb5d4b1f82616b9d95a6a732e jdk-9+133 +065724348690eda41fc69112278d8da6dcde548c jdk-9+134
--- a/common/autoconf/generated-configure.sh Fri Sep 02 02:41:12 2016 +0000 +++ b/common/autoconf/generated-configure.sh Wed Jul 05 22:09:41 2017 +0200 @@ -5095,7 +5095,7 @@ #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1470863189 +DATE_WHEN_GENERATED=1472718471 ############################################################################### # @@ -15944,6 +15944,8 @@ HOTSPOT_TARGET_CPU_DEFINE=S390 elif test "x$OPENJDK_TARGET_CPU" = xs390x; then HOTSPOT_TARGET_CPU_DEFINE=S390 + elif test "x$OPENJDK_TARGET_CPU" != x; then + HOTSPOT_TARGET_CPU_DEFINE=$(echo $OPENJDK_TARGET_CPU | tr a-z A-Z) fi @@ -16117,6 +16119,8 @@ HOTSPOT_BUILD_CPU_DEFINE=S390 elif test "x$OPENJDK_BUILD_CPU" = xs390x; then HOTSPOT_BUILD_CPU_DEFINE=S390 + elif test "x$OPENJDK_BUILD_CPU" != x; then + HOTSPOT_BUILD_CPU_DEFINE=$(echo $OPENJDK_BUILD_CPU | tr a-z A-Z) fi
--- a/common/autoconf/platform.m4 Fri Sep 02 02:41:12 2016 +0000 +++ b/common/autoconf/platform.m4 Wed Jul 05 22:09:41 2017 +0200 @@ -454,6 +454,8 @@ HOTSPOT_$1_CPU_DEFINE=S390 elif test "x$OPENJDK_$1_CPU" = xs390x; then HOTSPOT_$1_CPU_DEFINE=S390 + elif test "x$OPENJDK_$1_CPU" != x; then + HOTSPOT_$1_CPU_DEFINE=$(echo $OPENJDK_$1_CPU | tr a-z A-Z) fi AC_SUBST(HOTSPOT_$1_CPU_DEFINE)
--- a/make/CompileJavaModules.gmk Fri Sep 02 02:41:12 2016 +0000 +++ b/make/CompileJavaModules.gmk Wed Jul 05 22:09:41 2017 +0200 @@ -504,7 +504,7 @@ $($(MODULE)_ADD_JAVAC_FLAGS) \ --module-source-path $(MODULESOURCEPATH) \ --module-path $(MODULEPATH) \ - -system none, \ + --system none, \ )) TARGETS += $($(MODULE)) $($(MODULE)_COPY_EXTRA)
--- a/make/Images.gmk Fri Sep 02 02:41:12 2016 +0000 +++ b/make/Images.gmk Wed Jul 05 22:09:41 2017 +0200 @@ -116,8 +116,10 @@ JIMAGE_TARGET_FILE := bin/java$(EXE_SUFFIX) JLINK_ORDER_RESOURCES := **module-info.class +JLINK_JLI_CLASSES := ifeq ($(ENABLE_GENERATE_CLASSLIST), true) JLINK_ORDER_RESOURCES += @$(SUPPORT_OUTPUTDIR)/classlist/classlist + JLINK_JLI_CLASSES := --generate-jli-classes=@$(SUPPORT_OUTPUTDIR)/classlist/jli_trace.out endif JLINK_ORDER_RESOURCES += \ /java.base/java/** \ @@ -131,6 +133,7 @@ --endian $(OPENJDK_BUILD_CPU_ENDIAN) \ --release-info $(BASE_RELEASE_FILE) \ --order-resources=$(call CommaList, $(JLINK_ORDER_RESOURCES)) \ + $(JLINK_JLI_CLASSES) \ # ifeq ($(JLINK_KEEP_PACKAGED_MODULES), true)
--- a/make/Init.gmk Fri Sep 02 02:41:12 2016 +0000 +++ b/make/Init.gmk Wed Jul 05 22:09:41 2017 +0200 @@ -314,6 +314,9 @@ endif on-failure: + $(call CleanupSmartJavac) + $(call StopGlobalTimer) + $(call ReportBuildTimes) $(call PrintFailureReports) $(call PrintBuildLogFailures) $(PRINTF) "Hint: If caused by a warning, try configure --disable-warnings-as-errors.\n\n"
--- a/make/common/SetupJavaCompilers.gmk Fri Sep 02 02:41:12 2016 +0000 +++ b/make/common/SetupJavaCompilers.gmk Wed Jul 05 22:09:41 2017 +0200 @@ -88,7 +88,7 @@ $(eval $(call SetupJavaCompiler,GENERATE_USINGJDKBYTECODE, \ JVM := $(JAVA_SMALL), \ JAVAC := $(NEW_JAVAC), \ - FLAGS := --upgrade-module-path $(JDK_OUTPUTDIR)/modules -system none $(DISABLE_WARNINGS), \ + FLAGS := --upgrade-module-path $(JDK_OUTPUTDIR)/modules --system none $(DISABLE_WARNINGS), \ SERVER_DIR := $(SJAVAC_SERVER_DIR), \ SERVER_JVM := $(SJAVAC_SERVER_JAVA)))
--- a/make/common/TestFilesCompilation.gmk Fri Sep 02 02:41:12 2016 +0000 +++ b/make/common/TestFilesCompilation.gmk Wed Jul 05 22:09:41 2017 +0200 @@ -86,6 +86,7 @@ LANG := C, \ CFLAGS := $$($1_CFLAGS) $$($1_CFLAGS_$$($1_PREFIX)$$(name)), \ LDFLAGS := $$($1_LDFLAGS) $$($1_LDFLAGS_$$($1_PREFIX)$$(name)), \ + LIBS := $$($1_LIBS_$$($1_PREFIX)$$(name)), \ OPTIMIZATION := LOW, \ )) \ $$(eval $1 += $$(BUILD_TEST_$$(name)) ) \
--- a/make/test/BuildTestLib.gmk Fri Sep 02 02:41:12 2016 +0000 +++ b/make/test/BuildTestLib.gmk Wed Jul 05 22:09:41 2017 +0200 @@ -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 @@ -48,7 +48,7 @@ # test-lib.jar will contain only hprof classes until JDK-8081381 is resolved $(eval $(call SetupJavaCompilation, BUILD_TEST_LIB_JAR, \ SETUP := GENERATE_USINGJDKBYTECODE, \ - SRC := $(TEST_LIB_SOURCE_DIR)/share/classes/jdk/test/lib/hprof, \ + SRC := $(TEST_LIB_SOURCE_DIR)/jdk/test/lib/hprof, \ BIN := $(TEST_LIB_SUPPORT)/test-lib_classes, \ JAR := $(TEST_LIB_SUPPORT)/test-lib.jar, \ ))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/ClassFileInstaller.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,257 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.ByteArrayInputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * Dump a class file for a class on the class path in the current directory, or + * in the specified JAR file. This class is usually used when you build a class + * from a test library, but want to use this class in a sub-process. + * + * For example, to build the following library class: + * test/lib/sun/hotspot/WhiteBox.java + * + * You would use the following tags: + * + * @library /test/lib + * @build sun.hotspot.WhiteBox + * + * JTREG would build the class file under + * ${JTWork}/classes/test/lib/sun/hotspot/WhiteBox.class + * + * With you run your main test class using "@run main MyMainClass", JTREG would setup the + * -classpath to include "${JTWork}/classes/test/lib/", so MyMainClass would be able to + * load the WhiteBox class. + * + * However, if you run a sub process, and do not wish to use the exact same -classpath, + * You can use ClassFileInstaller to ensure that WhiteBox is available in the current + * directory of your test: + * + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * + * Or, you can use the -jar option to store the class in the specified JAR file. If a relative + * path name is given, the JAR file would be relative to the current directory of + * + * @run main ClassFileInstaller -jar myjar.jar sun.hotspot.WhiteBox + */ +public class ClassFileInstaller { + /** + * You can enable debug tracing of ClassFileInstaller by running JTREG with + * jtreg -DClassFileInstaller.debug=true ... <names of tests> + */ + public static boolean DEBUG = Boolean.getBoolean("ClassFileInstaller.debug"); + + /** + * @param args The names of the classes to dump + * @throws Exception + */ + public static void main(String... args) throws Exception { + if (args.length > 1 && args[0].equals("-jar")) { + if (args.length < 2) { + throw new RuntimeException("Usage: ClassFileInstaller <options> <classes>\n" + + "where possible options include:\n" + + " -jar <path> Write to the JAR file <path>"); + } + writeJar(args[1], null, args, 2, args.length); + } else { + if (DEBUG) { + System.out.println("ClassFileInstaller: Writing to " + System.getProperty("user.dir")); + } + for (String arg : args) { + writeClassToDisk(arg); + } + } + } + + public static class Manifest { + private InputStream in; + + private Manifest(InputStream in) { + this.in = in; + } + + static Manifest fromSourceFile(String fileName) throws Exception { + String pathName = System.getProperty("test.src") + File.separator + fileName; + return new Manifest(new FileInputStream(pathName)); + } + + // Example: + // String manifest = "Premain-Class: RedefineClassHelper\n" + + // "Can-Redefine-Classes: true\n"; + // ClassFileInstaller.writeJar("redefineagent.jar", + // ClassFileInstaller.Manifest.fromString(manifest), + // "RedefineClassHelper"); + static Manifest fromString(String manifest) throws Exception { + return new Manifest(new ByteArrayInputStream(manifest.getBytes())); + } + + public InputStream getInputStream() { + return in; + } + } + + private static void writeJar(String jarFile, Manifest manifest, String classes[], int from, int to) throws Exception { + if (DEBUG) { + System.out.println("ClassFileInstaller: Writing to " + getJarPath(jarFile)); + } + + (new File(jarFile)).delete(); + FileOutputStream fos = new FileOutputStream(jarFile); + ZipOutputStream zos = new ZipOutputStream(fos); + + // The manifest must be the first or second entry. See comments in JarInputStream + // constructor and JDK-5046178. + if (manifest != null) { + writeToDisk(zos, "META-INF/MANIFEST.MF", manifest.getInputStream()); + } + + for (int i=from; i<to; i++) { + writeClassToDisk(zos, classes[i]); + } + + zos.close(); + fos.close(); + } + + /* + * You can call ClassFileInstaller.writeJar() from your main test class instead of + * using "@run ClassFileInstaller -jar ...". E.g., + * + * String jarPath = ClassFileInstaller.getJarPath("myjar.jar", "sun.hotspot.WhiteBox") + * + * If you call this API, make sure you build ClassFileInstaller with the following tags: + * + * @library testlibrary + * @build ClassFileInstaller + */ + public static String writeJar(String jarFile, String... classes) throws Exception { + writeJar(jarFile, null, classes, 0, classes.length); + return getJarPath(jarFile); + } + + public static String writeJar(String jarFile, Manifest manifest, String... classes) throws Exception { + writeJar(jarFile, manifest, classes, 0, classes.length); + return getJarPath(jarFile); + } + + /** + * This returns the absolute path to the file specified in "@ClassFileInstaller -jar myjar.jar", + * In your test program, instead of using the JAR file name directly: + * + * String jarPath = "myjar.jar"; + * + * you should call this function, like: + * + * String jarPath = ClassFileInstaller.getJarPath("myjar.jar") + * + * The reasons are: + * (1) Using absolute path makes it easy to cut-and-paste from the JTR file and rerun your + * test in any directory. + * (2) In the future, we may make the JAR file name unique to avoid clobbering + * during parallel JTREG execution. + * + */ + public static String getJarPath(String jarFileName) { + return new File(jarFileName).getAbsolutePath(); + } + + public static void writeClassToDisk(String className) throws Exception { + writeClassToDisk((ZipOutputStream)null, className); + } + private static void writeClassToDisk(ZipOutputStream zos, String className) throws Exception { + writeClassToDisk(zos, className, ""); + } + + public static void writeClassToDisk(String className, String prependPath) throws Exception { + writeClassToDisk(null, className, prependPath); + } + private static void writeClassToDisk(ZipOutputStream zos, String className, String prependPath) throws Exception { + ClassLoader cl = ClassFileInstaller.class.getClassLoader(); + + // Convert dotted class name to a path to a class file + String pathName = className.replace('.', '/').concat(".class"); + InputStream is = cl.getResourceAsStream(pathName); + if (is == null) { + throw new RuntimeException("Failed to find " + pathName); + } + if (prependPath.length() > 0) { + pathName = prependPath + "/" + pathName; + } + writeToDisk(zos, pathName, is); + } + + public static void writeClassToDisk(String className, byte[] bytecode) throws Exception { + writeClassToDisk(null, className, bytecode); + } + private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode) throws Exception { + writeClassToDisk(zos, className, bytecode, ""); + } + + public static void writeClassToDisk(String className, byte[] bytecode, String prependPath) throws Exception { + writeClassToDisk(null, className, bytecode, prependPath); + } + private static void writeClassToDisk(ZipOutputStream zos, String className, byte[] bytecode, String prependPath) throws Exception { + // Convert dotted class name to a path to a class file + String pathName = className.replace('.', '/').concat(".class"); + if (prependPath.length() > 0) { + pathName = prependPath + "/" + pathName; + } + writeToDisk(zos, pathName, new ByteArrayInputStream(bytecode)); + } + + private static void writeToDisk(ZipOutputStream zos, String pathName, InputStream is) throws Exception { + if (DEBUG) { + System.out.println("ClassFileInstaller: Writing " + pathName); + } + if (zos != null) { + ZipEntry ze = new ZipEntry(pathName); + zos.putNextEntry(ze); + byte[] buf = new byte[1024]; + int len; + while ((len = is.read(buf))>0){ + zos.write(buf, 0, len); + } + } else { + // Create the class file's package directory + Path p = Paths.get(pathName); + if (pathName.contains("/")) { + Files.createDirectories(p.getParent()); + } + // Create the class file + Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING); + } + is.close(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/RedefineClassHelper.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.PrintWriter; +import java.lang.instrument.*; +import jdk.test.lib.InMemoryJavaCompiler; + +/* + * Helper class to write tests that redefine classes. + * When main method is run, it will create a redefineagent.jar that can be used + * with the -javaagent option to support redefining classes in jtreg tests. + * + * See sample test in test/testlibrary_tests/RedefineClassTest.java + */ +public class RedefineClassHelper { + + public static Instrumentation instrumentation; + public static void premain(String agentArgs, Instrumentation inst) { + instrumentation = inst; + } + + /** + * Redefine a class + * + * @param clazz Class to redefine + * @param javacode String with the new java code for the class to be redefined + */ + public static void redefineClass(Class clazz, String javacode) throws Exception { + byte[] bytecode = InMemoryJavaCompiler.compile(clazz.getName(), javacode); + redefineClass(clazz, bytecode); + } + + /** + * Redefine a class + * + * @param clazz Class to redefine + * @param bytecode byte[] with the new class + */ + public static void redefineClass(Class clazz, byte[] bytecode) throws Exception { + instrumentation.redefineClasses(new ClassDefinition(clazz, bytecode)); + } + + /** + * Main method to be invoked before test to create the redefineagent.jar + */ + public static void main(String[] args) throws Exception { + ClassFileInstaller.main("RedefineClassHelper"); + + PrintWriter pw = new PrintWriter("MANIFEST.MF"); + pw.println("Premain-Class: RedefineClassHelper"); + pw.println("Can-Redefine-Classes: true"); + pw.close(); + + sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar"); + if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "RedefineClassHelper.class" })) { + throw new Exception("jar operation failed"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/Asserts.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib; + +import java.util.Objects; + +/** + * Asserts that can be used for verifying assumptions in tests. + * + * An assertion will throw a {@link RuntimeException} if the assertion isn't true. + * All the asserts can be imported into a test by using a static import: + * + * <pre> + * {@code + * import static jdk.testlibrary.Asserts.*; + * } + * + * Always provide a message describing the assumption if the line number of the + * failing assertion isn't enough to understand why the assumption failed. For + * example, if the assertion is in a loop or in a method that is called + * multiple times, then the line number won't provide enough context to + * understand the failure. + * </pre> + */ +public class Asserts { + + /** + * Shorthand for {@link #assertLessThan(Comparable, Comparable)}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertLessThan(Comparable, Comparable) + */ + public static <T extends Comparable<T>> void assertLT(T lhs, T rhs) { + assertLessThan(lhs, rhs); + } + + /** + * Shorthand for {@link #assertLessThan(Comparable, Comparable, String)}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @see #assertLessThan(Comparable, Comparable, String) + */ + public static <T extends Comparable<T>> void assertLT(T lhs, T rhs, String msg) { + assertLessThan(lhs, rhs, msg); + } + + /** + * Calls {@link #assertLessThan(Comparable, Comparable, String)} with a default message. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertLessThan(Comparable, Comparable, String) + */ + public static <T extends Comparable<T>> void assertLessThan(T lhs, T rhs) { + assertLessThan(lhs, rhs, null); + } + + /** + * Asserts that {@code lhs} is less than {@code rhs}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static <T extends Comparable<T>>void assertLessThan(T lhs, T rhs, String msg) { + if (!(compare(lhs, rhs, msg) < 0)) { + msg = Objects.toString(msg, "assertLessThan") + + ": expected that " + Objects.toString(lhs) + + " < " + Objects.toString(rhs); + fail(msg); + } + } + + /** + * Shorthand for {@link #assertLessThanOrEqual(Comparable, Comparable)}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertLessThanOrEqual(Comparable, Comparable) + */ + public static <T extends Comparable<T>> void assertLTE(T lhs, T rhs) { + assertLessThanOrEqual(lhs, rhs); + } + + /** + * Shorthand for {@link #assertLessThanOrEqual(Comparable, Comparable, String)}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @see #assertLessThanOrEqual(Comparable, Comparable, String) + */ + public static <T extends Comparable<T>> void assertLTE(T lhs, T rhs, String msg) { + assertLessThanOrEqual(lhs, rhs, msg); + } + + /** + * Calls {@link #assertLessThanOrEqual(Comparable, Comparable, String)} with a default message. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertLessThanOrEqual(Comparable, Comparable, String) + */ + public static <T extends Comparable<T>> void assertLessThanOrEqual(T lhs, T rhs) { + assertLessThanOrEqual(lhs, rhs, null); + } + + /** + * Asserts that {@code lhs} is less than or equal to {@code rhs}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static <T extends Comparable<T>> void assertLessThanOrEqual(T lhs, T rhs, String msg) { + if (!(compare(lhs, rhs, msg) <= 0)) { + msg = Objects.toString(msg, "assertLessThanOrEqual") + + ": expected that " + Objects.toString(lhs) + + " <= " + Objects.toString(rhs); + fail(msg); + } + } + + /** + * Shorthand for {@link #assertEquals(Object, Object)}. + * + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertEquals(Object, Object) + */ + public static void assertEQ(Object lhs, Object rhs) { + assertEquals(lhs, rhs); + } + + /** + * Shorthand for {@link #assertEquals(Object, Object, String)}. + * + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @see #assertEquals(Object, Object, String) + */ + public static void assertEQ(Object lhs, Object rhs, String msg) { + assertEquals(lhs, rhs, msg); + } + + /** + * Calls {@link #assertEquals(java.lang.Object, java.lang.Object, java.lang.String)} with a default message. + * + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertEquals(Object, Object, String) + */ + public static void assertEquals(Object lhs, Object rhs) { + assertEquals(lhs, rhs, null); + } + + /** + * Asserts that {@code lhs} is equal to {@code rhs}. + * + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static void assertEquals(Object lhs, Object rhs, String msg) { + if ((lhs != rhs) && ((lhs == null) || !(lhs.equals(rhs)))) { + msg = Objects.toString(msg, "assertEquals") + + ": expected " + Objects.toString(lhs) + + " to equal " + Objects.toString(rhs); + fail(msg); + } + } + + /** + * Calls {@link #assertSame(java.lang.Object, java.lang.Object, java.lang.String)} with a default message. + * + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertSame(Object, Object, String) + */ + public static void assertSame(Object lhs, Object rhs) { + assertSame(lhs, rhs, null); + } + + /** + * Asserts that {@code lhs} is the same as {@code rhs}. + * + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static void assertSame(Object lhs, Object rhs, String msg) { + if (lhs != rhs) { + msg = Objects.toString(msg, "assertSame") + + ": expected " + Objects.toString(lhs) + + " to equal " + Objects.toString(rhs); + fail(msg); + } + } + + /** + * Shorthand for {@link #assertGreaterThanOrEqual(Comparable, Comparable)}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertGreaterThanOrEqual(Comparable, Comparable) + */ + public static <T extends Comparable<T>> void assertGTE(T lhs, T rhs) { + assertGreaterThanOrEqual(lhs, rhs); + } + + /** + * Shorthand for {@link #assertGreaterThanOrEqual(Comparable, Comparable, String)}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @see #assertGreaterThanOrEqual(Comparable, Comparable, String) + */ + public static <T extends Comparable<T>> void assertGTE(T lhs, T rhs, String msg) { + assertGreaterThanOrEqual(lhs, rhs, msg); + } + + /** + * Calls {@link #assertGreaterThanOrEqual(Comparable, Comparable, String)} with a default message. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertGreaterThanOrEqual(Comparable, Comparable, String) + */ + public static <T extends Comparable<T>> void assertGreaterThanOrEqual(T lhs, T rhs) { + assertGreaterThanOrEqual(lhs, rhs, null); + } + + /** + * Asserts that {@code lhs} is greater than or equal to {@code rhs}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static <T extends Comparable<T>> void assertGreaterThanOrEqual(T lhs, T rhs, String msg) { + if (!(compare(lhs, rhs, msg) >= 0)) { + msg = Objects.toString(msg, "assertGreaterThanOrEqual") + + ": expected " + Objects.toString(lhs) + + " >= " + Objects.toString(rhs); + fail(msg); + } + } + + /** + * Shorthand for {@link #assertGreaterThan(Comparable, Comparable)}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertGreaterThan(Comparable, Comparable) + */ + public static <T extends Comparable<T>> void assertGT(T lhs, T rhs) { + assertGreaterThan(lhs, rhs); + } + + /** + * Shorthand for {@link #assertGreaterThan(Comparable, Comparable, String)}. + * + * @param <T> a type + * @param lhs the left hand value + * @param rhs the right hand value + * @param msg A description of the assumption; {@code null} for a default message. + * @see #assertGreaterThan(Comparable, Comparable, String) + */ + public static <T extends Comparable<T>> void assertGT(T lhs, T rhs, String msg) { + assertGreaterThan(lhs, rhs, msg); + } + + /** + * Calls {@link #assertGreaterThan(Comparable, Comparable, String)} with a default message. + * + * @param <T> a type + * @param lhs the left hand value + * @param rhs the right hand value + * @see #assertGreaterThan(Comparable, Comparable, String) + */ + public static <T extends Comparable<T>> void assertGreaterThan(T lhs, T rhs) { + assertGreaterThan(lhs, rhs, null); + } + + /** + * Asserts that {@code lhs} is greater than {@code rhs}. + * + * @param <T> a type + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static <T extends Comparable<T>> void assertGreaterThan(T lhs, T rhs, String msg) { + if (!(compare(lhs, rhs, msg) > 0)) { + msg = Objects.toString(msg, "assertGreaterThan") + + ": expected " + Objects.toString(lhs) + + " > " + Objects.toString(rhs); + fail(msg); + } + } + + /** + * Shorthand for {@link #assertNotEquals(Object, Object)}. + * + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertNotEquals(Object, Object) + */ + public static void assertNE(Object lhs, Object rhs) { + assertNotEquals(lhs, rhs); + } + + /** + * Shorthand for {@link #assertNotEquals(Object, Object, String)}. + * + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @see #assertNotEquals(Object, Object, String) + */ + public static void assertNE(Object lhs, Object rhs, String msg) { + assertNotEquals(lhs, rhs, msg); + } + + /** + * Calls {@link #assertNotEquals(Object, Object, String)} with a default message. + * + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @see #assertNotEquals(Object, Object, String) + */ + public static void assertNotEquals(Object lhs, Object rhs) { + assertNotEquals(lhs, rhs, null); + } + + /** + * Asserts that {@code lhs} is not equal to {@code rhs}. + * + * @param lhs The left hand side of the comparison. + * @param rhs The right hand side of the comparison. + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static void assertNotEquals(Object lhs, Object rhs, String msg) { + if ((lhs == rhs) || (lhs != null && lhs.equals(rhs))) { + msg = Objects.toString(msg, "assertNotEquals") + + ": expected " + Objects.toString(lhs) + + " to not equal " + Objects.toString(rhs); + fail(msg); + } + } + + /** + * Calls {@link #assertNull(Object, String)} with a default message. + * + * @param o The reference assumed to be null. + * @see #assertNull(Object, String) + */ + public static void assertNull(Object o) { + assertNull(o, null); + } + + /** + * Asserts that {@code o} is null. + * + * @param o The reference assumed to be null. + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static void assertNull(Object o, String msg) { + assertEquals(o, null, msg); + } + + /** + * Calls {@link #assertNotNull(Object, String)} with a default message. + * + * @param o The reference assumed <i>not</i> to be null, + * @see #assertNotNull(Object, String) + */ + public static void assertNotNull(Object o) { + assertNotNull(o, null); + } + + /** + * Asserts that {@code o} is <i>not</i> null. + * + * @param o The reference assumed <i>not</i> to be null, + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static void assertNotNull(Object o, String msg) { + assertNotEquals(o, null, msg); + } + + /** + * Calls {@link #assertFalse(boolean, String)} with a default message. + * + * @param value The value assumed to be false. + * @see #assertFalse(boolean, String) + */ + public static void assertFalse(boolean value) { + assertFalse(value, null); + } + + /** + * Asserts that {@code value} is {@code false}. + * + * @param value The value assumed to be false. + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static void assertFalse(boolean value, String msg) { + if (value) { + msg = Objects.toString(msg, "assertFalse") + + ": expected false, was true"; + fail(msg); + } + } + + /** + * Calls {@link #assertTrue(boolean, String)} with a default message. + * + * @param value The value assumed to be true. + * @see #assertTrue(boolean, String) + */ + public static void assertTrue(boolean value) { + assertTrue(value, null); + } + + /** + * Asserts that {@code value} is {@code true}. + * + * @param value The value assumed to be true. + * @param msg A description of the assumption; {@code null} for a default message. + * @throws RuntimeException if the assertion is not true. + */ + public static void assertTrue(boolean value, String msg) { + if (!value) { + msg = Objects.toString(msg, "assertTrue") + + ": expected true, was false"; + fail(msg); + } + } + + private static <T extends Comparable<T>> int compare(T lhs, T rhs, String msg) { + if (lhs == null || rhs == null) { + fail(lhs, rhs, msg + ": values must be non-null:", ","); + } + return lhs.compareTo(rhs); + } + +/** + * Asserts that two strings are equal. + * + * If strings are not equals, then exception message + * will contain {@code msg} followed by list of mismatched lines. + * + * @param str1 First string to compare. + * @param str2 Second string to compare. + * @param msg A description of the assumption. + * @throws RuntimeException if strings are not equal. + */ + public static void assertStringsEqual(String str1, String str2, + String msg) { + String lineSeparator = System.getProperty("line.separator"); + String str1Lines[] = str1.split(lineSeparator); + String str2Lines[] = str2.split(lineSeparator); + + int minLength = Math.min(str1Lines.length, str2Lines.length); + String longestStringLines[] = ((str1Lines.length == minLength) ? + str2Lines : str1Lines); + + boolean stringsAreDifferent = false; + + StringBuilder messageBuilder = new StringBuilder(msg); + + messageBuilder.append("\n"); + + for (int line = 0; line < minLength; line++) { + if (!str1Lines[line].equals(str2Lines[line])) { + messageBuilder.append(String. + format("[line %d] '%s' differs " + + "from '%s'\n", + line, + str1Lines[line], + str2Lines[line])); + stringsAreDifferent = true; + } + } + + if (minLength < longestStringLines.length) { + String stringName = ((longestStringLines == str1Lines) ? + "first" : "second"); + messageBuilder.append(String.format("Only %s string contains " + + "following lines:\n", + stringName)); + stringsAreDifferent = true; + for(int line = minLength; line < longestStringLines.length; line++) { + messageBuilder.append(String. + format("[line %d] '%s'", line, + longestStringLines[line])); + } + } + + if (stringsAreDifferent) { + fail(messageBuilder.toString()); + } + } + + /** + * Returns a string formatted with a message and expected and actual values. + * @param lhs the actual value + * @param rhs the expected value + * @param message the actual value + * @param relation the asserted relationship between lhs and rhs + * @return a formatted string + */ + public static String format(Object lhs, Object rhs, String message, String relation) { + StringBuilder sb = new StringBuilder(80); + if (message != null) { + sb.append(message); + sb.append(' '); + } + sb.append("<"); + sb.append(Objects.toString(lhs)); + sb.append("> "); + sb.append(Objects.toString(relation, ",")); + sb.append(" <"); + sb.append(Objects.toString(rhs)); + sb.append(">"); + return sb.toString(); + } + + /** + * Fail reports a failure with message fail. + * + * @throws RuntimeException always + */ + public static void fail() { + fail("fail"); + } + + /** + * Fail reports a failure with a message. + * @param message for the failure + * @throws RuntimeException always + */ + public static void fail(String message) { + throw new RuntimeException(message); + } + + /** + * Fail reports a failure with a formatted message. + * + * @param lhs the actual value + * @param rhs the expected value + * @param message to be format before the expected and actual values + * @param relation the asserted relationship between lhs and rhs + * @throws RuntimeException always + */ + public static void fail(Object lhs, Object rhs, String message, String relation) { + throw new RuntimeException(format(lhs, rhs, message, relation)); + } + + /** + * Fail reports a failure with a message and a cause. + * @param message to be format before the expected and actual values + * @param cause the exception that caused this failure + * @throws RuntimeException always + */ + public static void fail(String message, Throwable cause) { + throw new RuntimeException(message, cause); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/BuildHelper.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib; + +import java.io.File; +import java.io.FileReader; +import java.util.Properties; + +public class BuildHelper { + + /** + * Commercial builds should have the BUILD_TYPE set to commercial + * within the release file, found at the root of the JDK. + */ + public static boolean isCommercialBuild() throws Exception { + String buildType = getReleaseProperty("BUILD_TYPE","notFound"); + return buildType.equals("commercial"); + } + + + /** + * Return the value for property key, or defaultValue if no property not found. + * If present, double quotes are trimmed. + */ + public static String getReleaseProperty(String key, String defaultValue) throws Exception { + Properties properties = getReleaseProperties(); + String value = properties.getProperty(key, defaultValue); + return trimDoubleQuotes(value); + } + + /** + * Return the value for property key, or null if no property not found. + * If present, double quotes are trimmed. + */ + public static String getReleaseProperty(String key) throws Exception { + return getReleaseProperty(key, null); + } + + /** + * Get properties from the release file + */ + public static Properties getReleaseProperties() throws Exception { + Properties properties = new Properties(); + properties.load(new FileReader(getReleaseFile())); + return properties; + } + + /** + * Every JDK has a release file in its root. + * @return A handler to the release file. + */ + public static File getReleaseFile() throws Exception { + String jdkPath = getJDKRoot(); + File releaseFile = new File(jdkPath,"release"); + if ( ! releaseFile.canRead() ) { + throw new Exception("Release file is not readable, or it is absent: " + + releaseFile.getCanonicalPath()); + } + return releaseFile; + } + + /** + * Returns path to the JDK under test. + * This path is obtained through the test.jdk property, usually set by JTREG. + */ + public static String getJDKRoot() { + String jdkPath = System.getProperty("test.jdk"); + if (jdkPath == null) { + throw new RuntimeException("System property 'test.jdk' not set. This property is normally set by jtreg. " + + "When running test separately, set this property using '-Dtest.jdk=/path/to/jdk'."); + } + return jdkPath; + } + + /** + * Trim double quotes from the beginning and the end of the given string. + * @param original string to trim. + * @return a new trimmed string. + */ + public static String trimDoubleQuotes(String original) { + if (original == null) { return null; } + String trimmed = original.replaceAll("^\"+|\"+$", ""); + return trimmed; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/ByteCodeLoader.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib; + +import java.security.SecureClassLoader; + +/** + * {@code ByteCodeLoader} can be used for easy loading of byte code already + * present in memory. + * + * {@code InMemoryCompiler} can be used for compiling source code in a string + * into byte code, which then can be loaded with {@code ByteCodeLoader}. + * + * @see InMemoryCompiler + */ +public class ByteCodeLoader extends SecureClassLoader { + private final String className; + private final byte[] byteCode; + private volatile Class<?> holder; + + /** + * Creates a new {@code ByteCodeLoader} ready to load a class with the + * given name and the given byte code. + * + * @param className The name of the class + * @param byteCode The byte code of the class + */ + public ByteCodeLoader(String className, byte[] byteCode) { + this.className = className; + this.byteCode = byteCode; + } + + @Override + public Class<?> loadClass(String name) throws ClassNotFoundException { + if (!name.equals(className)) { + return super.loadClass(name); + } + if (holder == null) { + synchronized(this) { + if (holder == null) { + holder = findClass(name); + } + } + } + return holder; + } + + @Override + protected Class<?> findClass(String name) throws ClassNotFoundException { + if (!name.equals(className)) { + throw new ClassNotFoundException(name); + } + + return defineClass(name, byteCode, 0, byteCode.length); + } + + /** + * Utility method for creating a new {@code ByteCodeLoader} and then + * directly load the given byte code. + * + * @param className The name of the class + * @param byteCode The byte code for the class + * @throws ClassNotFoundException if the class can't be loaded + * @return A {@see Class} object representing the class + */ + public static Class<?> load(String className, byte[] byteCode) throws ClassNotFoundException { + return new ByteCodeLoader(className, byteCode).loadClass(className); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/DynamicVMOption.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.test.lib; + +import com.sun.management.HotSpotDiagnosticMXBean; +import java.lang.management.ManagementFactory; + +/** + * A utility class to work with VM options which could be altered during + * execution. + * + * This class is a wrapper around {@code com.sun.management.VMOption}. + * It provides more convenient interface to read/write the values. + * + */ +public class DynamicVMOption { + + private final HotSpotDiagnosticMXBean mxBean; + + /** + * VM option name, like "MinHeapFreeRatio". + */ + public final String name; + + /** + * Creates an instance of DynamicVMOption. + * + * @param name the VM option name + */ + public DynamicVMOption(String name) { + this.name = name; + mxBean = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class); + } + + /** + * Sets a new value for the option. + * Trying to set not applicable value will cause IllegalArgumentException. + * Behavior with null is undefined, most likely NPE will be thrown. + * + * @param newValue the value to be set + * @see #getValue() + * @throws IllegalArgumentException if newValue is not applicable to the option + */ + public final void setValue(String newValue) { + mxBean.setVMOption(name, newValue); + } + + /** + * Returns the value of option. + * + * @return the current option value + * @see #setValue(java.lang.String) + */ + public final String getValue() { + return mxBean.getVMOption(name).getValue(); + } + + /** + * Returns true, if option is writable, false otherwise. + * + * @return true, if option is writable, false otherwise + */ + public final boolean isWriteable() { + return mxBean.getVMOption(name).isWriteable(); + } + + /** + * Checks if the given value is applicable for the option. + * + * This method tries to set the option to the new value. If no exception + * has been thrown the value is treated as valid. + * + * Calling this method will not change the option value. After an attempt + * to set a new value, the option will be restored to its previous value. + * + * @param value the value to verify + * @return true if option could be set to the given value + */ + public boolean isValidValue(String value) { + boolean isValid = true; + String oldValue = getValue(); + try { + setValue(value); + } catch (NullPointerException e) { + if (value == null) { + isValid = false; + } + } catch (IllegalArgumentException e) { + isValid = false; + } finally { + setValue(oldValue); + } + return isValid; + } + + /** + * Returns the value of the given VM option as String. + * + * This is a simple shortcut for {@code new DynamicVMOption(name).getValue()} + * + * @param name the name of VM option + * @return value as a string + * @see #getValue() + */ + public static String getString(String name) { + return new DynamicVMOption(name).getValue(); + } + + /** + * Returns the value of the given option as int. + * + * @param name the name of VM option + * @return value parsed as integer + * @see #getString(java.lang.String) + * + */ + public static int getInt(String name) { + return Integer.parseInt(getString(name)); + } + + /** + * Sets the VM option to a new value. + * + * This is a simple shortcut for {@code new DynamicVMOption(name).setValue(value)} + * + * @param name the name of VM option + * @param value the value to be set + * @see #setValue(java.lang.String) + */ + public static void setString(String name, String value) { + new DynamicVMOption(name).setValue(value); + } + + /** + * Sets the VM option value to a new integer value. + * + * @param name the name of VM option + * @param value the integer value to be set + * @see #setString(java.lang.String, java.lang.String) + */ + public static void setInt(String name, int value) { + new DynamicVMOption(name).setValue(Integer.toString(value)); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/FileInstaller.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,97 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; + +/** + * Copy a resource: file or directory recursively, using relative path(src and dst) + * which are applied to test source directory(src) and current directory(dst) + */ +public class FileInstaller { + /** + * @param args source and destination + * @throws IOException if an I/O error occurs + */ + public static void main(String[] args) throws IOException { + if (args.length != 2) { + throw new IllegalArgumentException("Unexpected number of arguments for file copy"); + } + Path src = Paths.get(Utils.TEST_SRC, args[0]).toAbsolutePath(); + Path dst = Paths.get(args[1]).toAbsolutePath(); + if (src.toFile().exists()) { + if (src.toFile().isDirectory()) { + Files.walkFileTree(src, new CopyFileVisitor(src, dst)); + } else { + Path dstDir = dst.getParent(); + if (!dstDir.toFile().exists()) { + Files.createDirectories(dstDir); + } + Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING); + } + } else { + throw new IOException("Can't find source " + src); + } + } + + private static class CopyFileVisitor extends SimpleFileVisitor<Path> { + private final Path copyFrom; + private final Path copyTo; + + public CopyFileVisitor(Path copyFrom, Path copyTo) { + this.copyFrom = copyFrom; + this.copyTo = copyTo; + } + + @Override + public FileVisitResult preVisitDirectory(Path file, + BasicFileAttributes attrs) throws IOException { + Path relativePath = file.relativize(copyFrom); + Path destination = copyTo.resolve(relativePath); + if (!destination.toFile().exists()) { + Files.createDirectories(destination); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, + BasicFileAttributes attrs) throws IOException { + if (!file.toFile().isFile()) { + return FileVisitResult.CONTINUE; + } + Path relativePath = copyFrom.relativize(file); + Path destination = copyTo.resolve(relativePath); + Files.copy(file, destination, StandardCopyOption.COPY_ATTRIBUTES); + return FileVisitResult.CONTINUE; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/InMemoryJavaCompiler.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import java.net.URI; +import java.util.Arrays; + +import javax.tools.ForwardingJavaFileManager; +import javax.tools.FileObject; +import javax.tools.JavaCompiler; +import javax.tools.JavaCompiler.CompilationTask; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +/** + * {@code InMemoryJavaCompiler} can be used for compiling a {@link + * CharSequence} to a {@code byte[]}. + * + * The compiler will not use the file system at all, instead using a {@link + * ByteArrayOutputStream} for storing the byte code. For the source code, any + * kind of {@link CharSequence} can be used, e.g. {@link String}, {@link + * StringBuffer} or {@link StringBuilder}. + * + * The {@code InMemoryCompiler} can easily be used together with a {@code + * ByteClassLoader} to easily compile and load source code in a {@link String}: + * + * <pre> + * {@code + * import jdk.test.lib.InMemoryJavaCompiler; + * import jdk.test.lib.ByteClassLoader; + * + * class Example { + * public static void main(String[] args) { + * String className = "Foo"; + * String sourceCode = "public class " + className + " {" + + * " public void bar() {" + + * " System.out.println("Hello from bar!");" + + * " }" + + * "}"; + * byte[] byteCode = InMemoryJavaCompiler.compile(className, sourceCode); + * Class fooClass = ByteClassLoader.load(className, byteCode); + * } + * } + * } + * </pre> + */ +public class InMemoryJavaCompiler { + private static class MemoryJavaFileObject extends SimpleJavaFileObject { + private final String className; + private final CharSequence sourceCode; + private final ByteArrayOutputStream byteCode; + + public MemoryJavaFileObject(String className, CharSequence sourceCode) { + super(URI.create("string:///" + className.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE); + this.className = className; + this.sourceCode = sourceCode; + this.byteCode = new ByteArrayOutputStream(); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return sourceCode; + } + + @Override + public OutputStream openOutputStream() throws IOException { + return byteCode; + } + + public byte[] getByteCode() { + return byteCode.toByteArray(); + } + + public String getClassName() { + return className; + } + } + + private static class FileManagerWrapper extends ForwardingJavaFileManager { + private MemoryJavaFileObject file; + + public FileManagerWrapper(MemoryJavaFileObject file) { + super(getCompiler().getStandardFileManager(null, null, null)); + this.file = file; + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, + Kind kind, FileObject sibling) + throws IOException { + if (!file.getClassName().equals(className)) { + throw new IOException("Expected class with name " + file.getClassName() + + ", but got " + className); + } + return file; + } + } + + /** + * Compiles the class with the given name and source code. + * + * @param className The name of the class + * @param sourceCode The source code for the class with name {@code className} + * @param options additional command line options + * @throws RuntimeException if the compilation did not succeed + * @return The resulting byte code from the compilation + */ + public static byte[] compile(String className, CharSequence sourceCode, String... options) { + MemoryJavaFileObject file = new MemoryJavaFileObject(className, sourceCode); + CompilationTask task = getCompilationTask(file, options); + + if(!task.call()) { + throw new RuntimeException("Could not compile " + className + " with source code " + sourceCode); + } + + return file.getByteCode(); + } + + private static JavaCompiler getCompiler() { + return ToolProvider.getSystemJavaCompiler(); + } + + private static CompilationTask getCompilationTask(MemoryJavaFileObject file, String... options) { + return getCompiler().getTask(null, new FileManagerWrapper(file), null, Arrays.asList(options), null, Arrays.asList(file)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/JDKToolFinder.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib; + +import java.io.FileNotFoundException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public final class JDKToolFinder { + + private JDKToolFinder() { + } + + /** + * Returns the full path to an executable in jdk/bin based on System + * property {@code test.jdk} or {@code compile.jdk} (both are set by the jtreg test suite) + * + * @return Full path to an executable in jdk/bin + */ + public static String getJDKTool(String tool) { + + // First try to find the executable in test.jdk + try { + return getTool(tool, "test.jdk"); + } catch (FileNotFoundException e) { + + } + + // Now see if it's available in compile.jdk + try { + return getTool(tool, "compile.jdk"); + } catch (FileNotFoundException e) { + throw new RuntimeException("Failed to find " + tool + + ", looked in test.jdk (" + System.getProperty("test.jdk") + + ") and compile.jdk (" + System.getProperty("compile.jdk") + ")"); + } + } + + /** + * Returns the full path to an executable in jdk/bin based on System + * property {@code compile.jdk} + * + * @return Full path to an executable in jdk/bin + */ + public static String getCompileJDKTool(String tool) { + try { + return getTool(tool, "compile.jdk"); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Returns the full path to an executable in jdk/bin based on System + * property {@code test.jdk} + * + * @return Full path to an executable in jdk/bin + */ + public static String getTestJDKTool(String tool) { + try { + return getTool(tool, "test.jdk"); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + private static String getTool(String tool, String property) throws FileNotFoundException { + String jdkPath = System.getProperty(property); + + if (jdkPath == null) { + throw new RuntimeException( + "System property '" + property + "' not set. This property is normally set by jtreg. " + + "When running test separately, set this property using '-D" + property + "=/path/to/jdk'."); + } + + Path toolName = Paths.get("bin", tool + (Platform.isWindows() ? ".exe" : "")); + + Path jdkTool = Paths.get(jdkPath, toolName.toString()); + if (!jdkTool.toFile().exists()) { + throw new FileNotFoundException("Could not find file " + jdkTool.toAbsolutePath()); + } + + return jdkTool.toAbsolutePath().toString(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/JDKToolLauncher.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import jdk.test.lib.process.ProcessTools; + +/** + * A utility for constructing command lines for starting JDK tool processes. + * + * The JDKToolLauncher can in particular be combined with a + * java.lang.ProcessBuilder to easily run a JDK tool. For example, the following + * code run {@code jmap -heap} against a process with GC logging turned on for + * the {@code jmap} process: + * + * <pre> + * {@code + * JDKToolLauncher jmap = JDKToolLauncher.create("jmap") + * .addVMArg("-XX:+PrintGC"); + * .addVMArg("-XX:+PrintGCDetails") + * .addToolArg("-heap") + * .addToolArg(pid); + * ProcessBuilder pb = new ProcessBuilder(jmap.getCommand()); + * Process p = pb.start(); + * } + * </pre> + */ +public class JDKToolLauncher { + private final String executable; + private final List<String> vmArgs = new ArrayList<String>(); + private final List<String> toolArgs = new ArrayList<String>(); + + private JDKToolLauncher(String tool, boolean useCompilerJDK) { + if (useCompilerJDK) { + executable = JDKToolFinder.getJDKTool(tool); + } else { + executable = JDKToolFinder.getTestJDKTool(tool); + } + vmArgs.addAll(Arrays.asList(ProcessTools.getPlatformSpecificVMArgs())); + } + + /** + * Creates a new JDKToolLauncher for the specified tool. Using tools path + * from the compiler JDK. + * + * @param tool + * The name of the tool + * @return A new JDKToolLauncher + */ + public static JDKToolLauncher create(String tool) { + return new JDKToolLauncher(tool, true); + } + + /** + * Creates a new JDKToolLauncher for the specified tool in the Tested JDK. + * + * @param tool + * The name of the tool + * + * @return A new JDKToolLauncher + */ + public static JDKToolLauncher createUsingTestJDK(String tool) { + return new JDKToolLauncher(tool, false); + } + + /** + * Adds an argument to the JVM running the tool. + * + * The JVM arguments are passed to the underlying JVM running the tool. + * Arguments will automatically be prepended with "-J". + * + * Any platform specific arguments required for running the tool are + * automatically added. + * + * + * @param arg + * The argument to VM running the tool + * @return The JDKToolLauncher instance + */ + public JDKToolLauncher addVMArg(String arg) { + vmArgs.add(arg); + return this; + } + + /** + * Adds an argument to the tool. + * + * @param arg + * The argument to the tool + * @return The JDKToolLauncher instance + */ + public JDKToolLauncher addToolArg(String arg) { + toolArgs.add(arg); + return this; + } + + /** + * Returns the command that can be used for running the tool. + * + * @return An array whose elements are the arguments of the command. + */ + public String[] getCommand() { + List<String> command = new ArrayList<String>(); + command.add(executable); + // Add -J in front of all vmArgs + for (String arg : vmArgs) { + command.add("-J" + arg); + } + command.addAll(toolArgs); + return command.toArray(new String[command.size()]); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/Platform.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib; + +import java.util.regex.Pattern; + +public class Platform { + public static final String vmName = System.getProperty("java.vm.name"); + public static final String vmInfo = System.getProperty("java.vm.info"); + private static final String osName = System.getProperty("os.name"); + private static final String dataModel = System.getProperty("sun.arch.data.model"); + private static final String vmVersion = System.getProperty("java.vm.version"); + private static final String jdkDebug = System.getProperty("jdk.debug"); + private static final String osArch = System.getProperty("os.arch"); + private static final String userName = System.getProperty("user.name"); + private static final String compiler = System.getProperty("sun.management.compiler"); + + public static boolean isClient() { + return vmName.endsWith(" Client VM"); + } + + public static boolean isServer() { + return vmName.endsWith(" Server VM"); + } + + public static boolean isGraal() { + return vmName.endsWith(" Graal VM"); + } + + public static boolean isZero() { + return vmName.endsWith(" Zero VM"); + } + + public static boolean isMinimal() { + return vmName.endsWith(" Minimal VM"); + } + + public static boolean isEmbedded() { + return vmName.contains("Embedded"); + } + + public static boolean isTieredSupported() { + return compiler.contains("Tiered Compilers"); + } + + public static boolean isInt() { + return vmInfo.contains("interpreted"); + } + + public static boolean isMixed() { + return vmInfo.contains("mixed"); + } + + public static boolean isComp() { + return vmInfo.contains("compiled"); + } + + public static boolean is32bit() { + return dataModel.equals("32"); + } + + public static boolean is64bit() { + return dataModel.equals("64"); + } + + public static boolean isAix() { + return isOs("aix"); + } + + public static boolean isLinux() { + return isOs("linux"); + } + + public static boolean isOSX() { + return isOs("mac"); + } + + public static boolean isSolaris() { + return isOs("sunos"); + } + + public static boolean isWindows() { + return isOs("win"); + } + + private static boolean isOs(String osname) { + return osName.toLowerCase().startsWith(osname.toLowerCase()); + } + + public static String getOsName() { + return osName; + } + + public static boolean isDebugBuild() { + return (jdkDebug.toLowerCase().contains("debug")); + } + + public static String getVMVersion() { + return vmVersion; + } + + // Returns true for sparc and sparcv9. + public static boolean isSparc() { + return isArch("sparc.*"); + } + + public static boolean isARM() { + return isArch("arm.*"); + } + + public static boolean isPPC() { + return isArch("ppc.*"); + } + + public static boolean isX86() { + // On Linux it's 'i386', Windows 'x86' without '_64' suffix. + return isArch("(i386)|(x86(?!_64))"); + } + + public static boolean isX64() { + // On OSX it's 'x86_64' and on other (Linux, Windows and Solaris) platforms it's 'amd64' + return isArch("(amd64)|(x86_64)"); + } + + public static boolean isAArch64() { + return isArch("aarch64"); + } + + public static String getOsArch() { + return osArch; + } + + /** + * Return a boolean for whether we expect to be able to attach + * the SA to our own processes on this system. + */ + public static boolean shouldSAAttach() throws Exception { + + if (isAix()) { + return false; // SA not implemented. + } else if (isLinux()) { + return canPtraceAttachLinux(); + } else if (isOSX()) { + return canAttachOSX(); + } else { + // Other platforms expected to work: + return true; + } + } + + /** + * On Linux, first check the SELinux boolean "deny_ptrace" and return false + * as we expect to be denied if that is "1". Then expect permission to attach + * if we are root, so return true. Then return false for an expected denial + * if "ptrace_scope" is 1, and true otherwise. + */ + public static boolean canPtraceAttachLinux() throws Exception { + + // SELinux deny_ptrace: + String deny_ptrace = Utils.fileAsString("/sys/fs/selinux/booleans/deny_ptrace"); + if (deny_ptrace != null && deny_ptrace.contains("1")) { + // ptrace will be denied: + return false; + } + + // YAMA enhanced security ptrace_scope: + // 0 - a process can PTRACE_ATTACH to any other process running under the same uid + // 1 - restricted ptrace: a process must be a children of the inferior or user is root + // 2 - only processes with CAP_SYS_PTRACE may use ptrace or user is root + // 3 - no attach: no processes may use ptrace with PTRACE_ATTACH + String ptrace_scope = Utils.fileAsString("/proc/sys/kernel/yama/ptrace_scope"); + if (ptrace_scope != null) { + if (ptrace_scope.startsWith("3")) { + return false; + } + if (!userName.equals("root") && !ptrace_scope.startsWith("0")) { + // ptrace will be denied: + return false; + } + } + // Otherwise expect to be permitted: + return true; + } + + /** + * On OSX, expect permission to attach only if we are root. + */ + public static boolean canAttachOSX() throws Exception { + return userName.equals("root"); + } + + private static boolean isArch(String archnameRE) { + return Pattern.compile(archnameRE, Pattern.CASE_INSENSITIVE) + .matcher(osArch) + .matches(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/Utils.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,694 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.Random; +import java.util.function.BooleanSupplier; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jdk.internal.misc.Unsafe; + +import static jdk.test.lib.Asserts.assertTrue; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * Common library for various test helper functions. + */ +public final class Utils { + + /** + * Returns the value of 'test.class.path' system property. + */ + public static final String TEST_CLASS_PATH = System.getProperty("test.class.path", "."); + + /** + * Returns the sequence used by operating system to separate lines. + */ + public static final String NEW_LINE = System.getProperty("line.separator"); + + /** + * Returns the value of 'test.vm.opts' system property. + */ + public static final String VM_OPTIONS = System.getProperty("test.vm.opts", "").trim(); + + /** + * Returns the value of 'test.java.opts' system property. + */ + public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim(); + + /** + * Returns the value of 'test.src' system property. + */ + public static final String TEST_SRC = System.getProperty("test.src", "").trim(); + + private static Unsafe unsafe = null; + + /** + * Defines property name for seed value. + */ + public static final String SEED_PROPERTY_NAME = "jdk.test.lib.random.seed"; + + /* (non-javadoc) + * Random generator with (or without) predefined seed. Depends on + * "jdk.test.lib.random.seed" property value. + */ + private static volatile Random RANDOM_GENERATOR; + + /** + * Contains the seed value used for {@link java.util.Random} creation. + */ + public static final long SEED = Long.getLong(SEED_PROPERTY_NAME, new Random().nextLong()); + /** + * Returns the value of 'test.timeout.factor' system property + * converted to {@code double}. + */ + public static final double TIMEOUT_FACTOR; + static { + String toFactor = System.getProperty("test.timeout.factor", "1.0"); + TIMEOUT_FACTOR = Double.parseDouble(toFactor); + } + + /** + * Returns the value of JTREG default test timeout in milliseconds + * converted to {@code long}. + */ + public static final long DEFAULT_TEST_TIMEOUT = TimeUnit.SECONDS.toMillis(120); + + private Utils() { + // Private constructor to prevent class instantiation + } + + /** + * Returns the list of VM options. + * + * @return List of VM options + */ + public static List<String> getVmOptions() { + return Arrays.asList(safeSplitString(VM_OPTIONS)); + } + + /** + * Returns the list of VM options with -J prefix. + * + * @return The list of VM options with -J prefix + */ + public static List<String> getForwardVmOptions() { + String[] opts = safeSplitString(VM_OPTIONS); + for (int i = 0; i < opts.length; i++) { + opts[i] = "-J" + opts[i]; + } + return Arrays.asList(opts); + } + + /** + * Returns the default JTReg arguments for a jvm running a test. + * This is the combination of JTReg arguments test.vm.opts and test.java.opts. + * @return An array of options, or an empty array if no options. + */ + public static String[] getTestJavaOpts() { + List<String> opts = new ArrayList<String>(); + Collections.addAll(opts, safeSplitString(VM_OPTIONS)); + Collections.addAll(opts, safeSplitString(JAVA_OPTIONS)); + return opts.toArray(new String[0]); + } + + /** + * Combines given arguments with default JTReg arguments for a jvm running a test. + * This is the combination of JTReg arguments test.vm.opts and test.java.opts + * @return The combination of JTReg test java options and user args. + */ + public static String[] addTestJavaOpts(String... userArgs) { + List<String> opts = new ArrayList<String>(); + Collections.addAll(opts, getTestJavaOpts()); + Collections.addAll(opts, userArgs); + return opts.toArray(new String[0]); + } + + /** + * Removes any options specifying which GC to use, for example "-XX:+UseG1GC". + * Removes any options matching: -XX:(+/-)Use*GC + * Used when a test need to set its own GC version. Then any + * GC specified by the framework must first be removed. + * @return A copy of given opts with all GC options removed. + */ + private static final Pattern useGcPattern = Pattern.compile( + "(?:\\-XX\\:[\\+\\-]Use.+GC)" + + "|(?:\\-Xconcgc)"); + public static List<String> removeGcOpts(List<String> opts) { + List<String> optsWithoutGC = new ArrayList<String>(); + for (String opt : opts) { + if (useGcPattern.matcher(opt).matches()) { + System.out.println("removeGcOpts: removed " + opt); + } else { + optsWithoutGC.add(opt); + } + } + return optsWithoutGC; + } + + /** + * Returns the default JTReg arguments for a jvm running a test without + * options that matches regular expressions in {@code filters}. + * This is the combination of JTReg arguments test.vm.opts and test.java.opts. + * @param filters Regular expressions used to filter out options. + * @return An array of options, or an empty array if no options. + */ + public static String[] getFilteredTestJavaOpts(String... filters) { + String options[] = getTestJavaOpts(); + + if (filters.length == 0) { + return options; + } + + List<String> filteredOptions = new ArrayList<String>(options.length); + Pattern patterns[] = new Pattern[filters.length]; + for (int i = 0; i < filters.length; i++) { + patterns[i] = Pattern.compile(filters[i]); + } + + for (String option : options) { + boolean matched = false; + for (int i = 0; i < patterns.length && !matched; i++) { + Matcher matcher = patterns[i].matcher(option); + matched = matcher.find(); + } + if (!matched) { + filteredOptions.add(option); + } + } + + return filteredOptions.toArray(new String[filteredOptions.size()]); + } + + /** + * Splits a string by white space. + * Works like String.split(), but returns an empty array + * if the string is null or empty. + */ + private static String[] safeSplitString(String s) { + if (s == null || s.trim().isEmpty()) { + return new String[] {}; + } + return s.trim().split("\\s+"); + } + + /** + * @return The full command line for the ProcessBuilder. + */ + public static String getCommandLine(ProcessBuilder pb) { + StringBuilder cmd = new StringBuilder(); + for (String s : pb.command()) { + cmd.append(s).append(" "); + } + return cmd.toString(); + } + + /** + * Returns the free port on the local host. + * The function will spin until a valid port number is found. + * + * @return The port number + * @throws InterruptedException if any thread has interrupted the current thread + * @throws IOException if an I/O error occurs when opening the socket + */ + public static int getFreePort() throws InterruptedException, IOException { + int port = -1; + + while (port <= 0) { + Thread.sleep(100); + + ServerSocket serverSocket = null; + try { + serverSocket = new ServerSocket(0); + port = serverSocket.getLocalPort(); + } finally { + serverSocket.close(); + } + } + + return port; + } + + /** + * Returns the name of the local host. + * + * @return The host name + * @throws UnknownHostException if IP address of a host could not be determined + */ + public static String getHostname() throws UnknownHostException { + InetAddress inetAddress = InetAddress.getLocalHost(); + String hostName = inetAddress.getHostName(); + + assertTrue((hostName != null && !hostName.isEmpty()), + "Cannot get hostname"); + + return hostName; + } + + /** + * Uses "jcmd -l" to search for a jvm pid. This function will wait + * forever (until jtreg timeout) for the pid to be found. + * @param key Regular expression to search for + * @return The found pid. + */ + public static int waitForJvmPid(String key) throws Throwable { + final long iterationSleepMillis = 250; + System.out.println("waitForJvmPid: Waiting for key '" + key + "'"); + System.out.flush(); + while (true) { + int pid = tryFindJvmPid(key); + if (pid >= 0) { + return pid; + } + Thread.sleep(iterationSleepMillis); + } + } + + /** + * Searches for a jvm pid in the output from "jcmd -l". + * + * Example output from jcmd is: + * 12498 sun.tools.jcmd.JCmd -l + * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar + * + * @param key A regular expression to search for. + * @return The found pid, or -1 if not found. + * @throws Exception If multiple matching jvms are found. + */ + public static int tryFindJvmPid(String key) throws Throwable { + OutputAnalyzer output = null; + try { + JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd"); + jcmdLauncher.addToolArg("-l"); + output = ProcessTools.executeProcess(jcmdLauncher.getCommand()); + output.shouldHaveExitValue(0); + + // Search for a line starting with numbers (pid), follwed by the key. + Pattern pattern = Pattern.compile("([0-9]+)\\s.*(" + key + ").*\\r?\\n"); + Matcher matcher = pattern.matcher(output.getStdout()); + + int pid = -1; + if (matcher.find()) { + pid = Integer.parseInt(matcher.group(1)); + System.out.println("findJvmPid.pid: " + pid); + if (matcher.find()) { + throw new Exception("Found multiple JVM pids for key: " + key); + } + } + return pid; + } catch (Throwable t) { + System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t)); + throw t; + } + } + + /** + * Adjusts the provided timeout value for the TIMEOUT_FACTOR + * @param tOut the timeout value to be adjusted + * @return The timeout value adjusted for the value of "test.timeout.factor" + * system property + */ + public static long adjustTimeout(long tOut) { + return Math.round(tOut * Utils.TIMEOUT_FACTOR); + } + + /** + * Return the contents of the named file as a single String, + * or null if not found. + * @param filename name of the file to read + * @return String contents of file, or null if file not found. + * @throws IOException + * if an I/O error occurs reading from the file or a malformed or + * unmappable byte sequence is read + */ + public static String fileAsString(String filename) throws IOException { + Path filePath = Paths.get(filename); + if (!Files.exists(filePath)) return null; + return new String(Files.readAllBytes(filePath)); + } + + /** + * @return Unsafe instance. + */ + public static synchronized Unsafe getUnsafe() { + if (unsafe == null) { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + unsafe = (Unsafe) f.get(null); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException("Unable to get Unsafe instance.", e); + } + } + return unsafe; + } + private static final char[] hexArray = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * Returns hex view of byte array + * + * @param bytes byte array to process + * @return Space separated hexadecimal string representation of bytes + */ + + public static String toHexString(byte[] bytes) { + char[] hexView = new char[bytes.length * 3]; + int i = 0; + for (byte b : bytes) { + hexView[i++] = hexArray[(b >> 4) & 0x0F]; + hexView[i++] = hexArray[b & 0x0F]; + hexView[i++] = ' '; + } + return new String(hexView); + } + + /** + * Returns {@link java.util.Random} generator initialized with particular seed. + * The seed could be provided via system property {@link Utils#SEED_PROPERTY_NAME} + * In case no seed is provided, the method uses a random number. + * The used seed printed to stdout. + * @return {@link java.util.Random} generator with particular seed. + */ + public static Random getRandomInstance() { + if (RANDOM_GENERATOR == null) { + synchronized (Utils.class) { + if (RANDOM_GENERATOR == null) { + RANDOM_GENERATOR = new Random(SEED); + System.out.printf("For random generator using seed: %d%n", SEED); + System.out.printf("To re-run test with same seed value please add \"-D%s=%d\" to command line.%n", SEED_PROPERTY_NAME, SEED); + } + } + } + return RANDOM_GENERATOR; + } + + /** + * Returns random element of non empty collection + * + * @param <T> a type of collection element + * @param collection collection of elements + * @return random element of collection + * @throws IllegalArgumentException if collection is empty + */ + public static <T> T getRandomElement(Collection<T> collection) + throws IllegalArgumentException { + if (collection.isEmpty()) { + throw new IllegalArgumentException("Empty collection"); + } + Random random = getRandomInstance(); + int elementIndex = 1 + random.nextInt(collection.size() - 1); + Iterator<T> iterator = collection.iterator(); + while (--elementIndex != 0) { + iterator.next(); + } + return iterator.next(); + } + + /** + * Returns random element of non empty array + * + * @param <T> a type of array element + * @param array array of elements + * @return random element of array + * @throws IllegalArgumentException if array is empty + */ + public static <T> T getRandomElement(T[] array) + throws IllegalArgumentException { + if (array == null || array.length == 0) { + throw new IllegalArgumentException("Empty or null array"); + } + Random random = getRandomInstance(); + return array[random.nextInt(array.length)]; + } + + /** + * Wait for condition to be true + * + * @param condition, a condition to wait for + */ + public static final void waitForCondition(BooleanSupplier condition) { + waitForCondition(condition, -1L, 100L); + } + + /** + * Wait until timeout for condition to be true + * + * @param condition, a condition to wait for + * @param timeout a time in milliseconds to wait for condition to be true + * specifying -1 will wait forever + * @return condition value, to determine if wait was successful + */ + public static final boolean waitForCondition(BooleanSupplier condition, + long timeout) { + return waitForCondition(condition, timeout, 100L); + } + + /** + * Wait until timeout for condition to be true for specified time + * + * @param condition, a condition to wait for + * @param timeout a time in milliseconds to wait for condition to be true, + * specifying -1 will wait forever + * @param sleepTime a time to sleep value in milliseconds + * @return condition value, to determine if wait was successful + */ + public static final boolean waitForCondition(BooleanSupplier condition, + long timeout, long sleepTime) { + long startTime = System.currentTimeMillis(); + while (!(condition.getAsBoolean() || (timeout != -1L + && ((System.currentTimeMillis() - startTime) > timeout)))) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new Error(e); + } + } + return condition.getAsBoolean(); + } + + /** + * Interface same as java.lang.Runnable but with + * method {@code run()} able to throw any Throwable. + */ + public static interface ThrowingRunnable { + void run() throws Throwable; + } + + /** + * Filters out an exception that may be thrown by the given + * test according to the given filter. + * + * @param test - method that is invoked and checked for exception. + * @param filter - function that checks if the thrown exception matches + * criteria given in the filter's implementation. + * @return - exception that matches the filter if it has been thrown or + * {@code null} otherwise. + * @throws Throwable - if test has thrown an exception that does not + * match the filter. + */ + public static Throwable filterException(ThrowingRunnable test, + Function<Throwable, Boolean> filter) throws Throwable { + try { + test.run(); + } catch (Throwable t) { + if (filter.apply(t)) { + return t; + } else { + throw t; + } + } + return null; + } + + /** + * Ensures a requested class is loaded + * @param aClass class to load + */ + public static void ensureClassIsLoaded(Class<?> aClass) { + if (aClass == null) { + throw new Error("Requested null class"); + } + try { + Class.forName(aClass.getName(), /* initialize = */ true, + ClassLoader.getSystemClassLoader()); + } catch (ClassNotFoundException e) { + throw new Error("Class not found", e); + } + } + /** + * @param parent a class loader to be the parent for the returned one + * @return an UrlClassLoader with urls made of the 'test.class.path' jtreg + * property and with the given parent + */ + public static URLClassLoader getTestClassPathURLClassLoader(ClassLoader parent) { + URL[] urls = Arrays.stream(TEST_CLASS_PATH.split(File.pathSeparator)) + .map(Paths::get) + .map(Path::toUri) + .map(x -> { + try { + return x.toURL(); + } catch (MalformedURLException ex) { + throw new Error("Test issue. JTREG property" + + " 'test.class.path'" + + " is not defined correctly", ex); + } + }).toArray(URL[]::new); + return new URLClassLoader(urls, parent); + } + + /** + * Runs runnable and checks that it throws expected exception. If exceptionException is null it means + * that we expect no exception to be thrown. + * @param runnable what we run + * @param expectedException expected exception + */ + public static void runAndCheckException(Runnable runnable, Class<? extends Throwable> expectedException) { + runAndCheckException(runnable, t -> { + if (t == null) { + if (expectedException != null) { + throw new AssertionError("Didn't get expected exception " + expectedException.getSimpleName()); + } + } else { + String message = "Got unexpected exception " + t.getClass().getSimpleName(); + if (expectedException == null) { + throw new AssertionError(message, t); + } else if (!expectedException.isAssignableFrom(t.getClass())) { + message += " instead of " + expectedException.getSimpleName(); + throw new AssertionError(message, t); + } + } + }); + } + + /** + * Runs runnable and makes some checks to ensure that it throws expected exception. + * @param runnable what we run + * @param checkException a consumer which checks that we got expected exception and raises a new exception otherwise + */ + public static void runAndCheckException(Runnable runnable, Consumer<Throwable> checkException) { + try { + runnable.run(); + checkException.accept(null); + } catch (Throwable t) { + checkException.accept(t); + } + } + + /** + * Converts to VM type signature + * + * @param type Java type to convert + * @return string representation of VM type + */ + public static String toJVMTypeSignature(Class<?> type) { + if (type.isPrimitive()) { + if (type == boolean.class) { + return "Z"; + } else if (type == byte.class) { + return "B"; + } else if (type == char.class) { + return "C"; + } else if (type == double.class) { + return "D"; + } else if (type == float.class) { + return "F"; + } else if (type == int.class) { + return "I"; + } else if (type == long.class) { + return "J"; + } else if (type == short.class) { + return "S"; + } else if (type == void.class) { + return "V"; + } else { + throw new Error("Unsupported type: " + type); + } + } + String result = type.getName().replaceAll("\\.", "/"); + if (!type.isArray()) { + return "L" + result + ";"; + } + return result; + } + + public static Object[] getNullValues(Class<?>... types) { + Object[] result = new Object[types.length]; + int i = 0; + for (Class<?> type : types) { + result[i++] = NULL_VALUES.get(type); + } + return result; + } + private static Map<Class<?>, Object> NULL_VALUES = new HashMap<>(); + static { + NULL_VALUES.put(boolean.class, false); + NULL_VALUES.put(byte.class, (byte) 0); + NULL_VALUES.put(short.class, (short) 0); + NULL_VALUES.put(char.class, '\0'); + NULL_VALUES.put(int.class, 0); + NULL_VALUES.put(long.class, 0L); + NULL_VALUES.put(float.class, 0.0f); + NULL_VALUES.put(double.class, 0.0d); + } + + /** + * Returns mandatory property value + * @param propName is a name of property to request + * @return a String with requested property value + */ + public static String getMandatoryProperty(String propName) { + Objects.requireNonNull(propName, "Requested null property"); + String prop = System.getProperty(propName); + Objects.requireNonNull(prop, + String.format("A mandatory property '%s' isn't set", propName)); + return prop; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/apps/LingeredApp.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,466 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.apps; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * This is a framework to launch an app that could be synchronized with caller + * to make further attach actions reliable across supported platforms + + * Caller example: + * SmartTestApp a = SmartTestApp.startApp(cmd); + * // do something + * a.stopApp(); + * + * or fine grained control + * + * a = new SmartTestApp("MyLock.lck"); + * a.createLock(); + * a.runApp(); + * a.waitAppReady(); + * // do something + * a.deleteLock(); + * a.waitAppTerminate(); + * + * Then you can work with app output and process object + * + * output = a.getAppOutput(); + * process = a.getProcess(); + * + */ +public class LingeredApp { + + private static final long spinDelay = 1000; + private static final int appWaitTime = 100; + + private final String lockFileName; + private long lockCreationTime; + private Process appProcess; + private final ArrayList<String> storedAppOutput; + + /* + * Drain child process output, store it into string array + */ + class InputGobbler extends Thread { + + InputStream is; + List<String> astr; + + InputGobbler(InputStream is, List<String> astr) { + this.is = is; + this.astr = astr; + } + + public void run() { + try { + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line = null; + while ((line = br.readLine()) != null) { + astr.add(line); + } + } catch (IOException ex) { + // pass + } + } + } + + /** + * Create LingeredApp object on caller side. Lock file have be a valid filename + * at writable location + * + * @param lockFileName - the name of lock file + */ + public LingeredApp(String lockFileName) { + this.lockFileName = lockFileName; + this.storedAppOutput = new ArrayList<String>(); + } + + public LingeredApp() { + final String lockName = UUID.randomUUID().toString() + ".lck"; + this.lockFileName = lockName; + this.storedAppOutput = new ArrayList<String>(); + } + + /** + * + * @return name of lock file + */ + public String getLockFileName() { + return this.lockFileName; + } + + /** + * + * @return name of testapp + */ + public String getAppName() { + return this.getClass().getName(); + } + + /** + * + * @return pid of java process running testapp + */ + public long getPid() { + if (appProcess == null) { + throw new RuntimeException("Process is not alive"); + } + return appProcess.getPid(); + } + + /** + * + * @return process object + */ + public Process getProcess() { + return appProcess; + } + + /** + * + * @return application output as string array. Empty array if application produced no output + */ + public List<String> getAppOutput() { + if (appProcess.isAlive()) { + throw new RuntimeException("Process is still alive. Can't get its output."); + } + return storedAppOutput; + } + + /* Make sure all part of the app use the same method to get dates, + as different methods could produce different results + */ + private static long epoch() { + return new Date().getTime(); + } + + private static long lastModified(String fileName) throws IOException { + Path path = Paths.get(fileName); + BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class); + return attr.lastModifiedTime().toMillis(); + } + + private static void setLastModified(String fileName, long newTime) throws IOException { + Path path = Paths.get(fileName); + FileTime fileTime = FileTime.fromMillis(newTime); + Files.setLastModifiedTime(path, fileTime); + } + + /** + * create lock + * + * @throws IOException + */ + public void createLock() throws IOException { + Path path = Paths.get(lockFileName); + // Files.deleteIfExists(path); + Files.createFile(path); + lockCreationTime = lastModified(lockFileName); + } + + /** + * Delete lock + * + * @throws IOException + */ + public void deleteLock() throws IOException { + try { + Path path = Paths.get(lockFileName); + Files.delete(path); + } catch (NoSuchFileException ex) { + // Lock already deleted. Ignore error + } + } + + public void waitAppTerminate() { + while (true) { + try { + appProcess.waitFor(); + break; + } catch (InterruptedException ex) { + // pass + } + } + } + + /** + * The app touches the lock file when it's started + * wait while it happens. Caller have to delete lock on wait error. + * + * @param timeout + * @throws java.io.IOException + */ + public void waitAppReady(long timeout) throws IOException { + long here = epoch(); + while (true) { + long epoch = epoch(); + if (epoch - here > (timeout * 1000)) { + throw new IOException("App waiting timeout"); + } + + // Live process should touch lock file every second + long lm = lastModified(lockFileName); + if (lm > lockCreationTime) { + break; + } + + // Make sure process didn't already exit + if (!appProcess.isAlive()) { + throw new IOException("App exited unexpectedly with " + appProcess.exitValue()); + } + + try { + Thread.sleep(spinDelay); + } catch (InterruptedException ex) { + // pass + } + } + } + + /** + * Run the app + * + * @param vmArguments + * @throws IOException + */ + public void runApp(List<String> vmArguments) + throws IOException { + + // We should always use testjava or throw an exception, + // so we can't use JDKToolFinder.getJDKTool("java"); + // that falls back to compile java on error + String jdkPath = System.getProperty("test.jdk"); + if (jdkPath == null) { + // we are not under jtreg, try env + Map<String, String> env = System.getenv(); + jdkPath = env.get("TESTJAVA"); + } + + if (jdkPath == null) { + throw new RuntimeException("Can't determine jdk path neither test.jdk property no TESTJAVA env are set"); + } + + String osname = System.getProperty("os.name"); + String javapath = jdkPath + ((osname.startsWith("window")) ? "/bin/java.exe" : "/bin/java"); + + List<String> cmd = new ArrayList<String>(); + cmd.add(javapath); + + + if (vmArguments == null) { + // Propagate test.vm.options to LingeredApp, filter out possible empty options + String testVmOpts[] = System.getProperty("test.vm.opts","").split("\\s+"); + for (String s : testVmOpts) { + if (!s.equals("")) { + cmd.add(s); + } + } + } + else{ + // Lets user manage LingeredApp options + cmd.addAll(vmArguments); + } + + // Make sure we set correct classpath to run the app + cmd.add("-cp"); + String classpath = System.getProperty("test.class.path"); + cmd.add((classpath == null) ? "." : classpath); + + cmd.add(this.getAppName()); + cmd.add(lockFileName); + + // Reporting + StringBuilder cmdLine = new StringBuilder(); + for (String strCmd : cmd) { + cmdLine.append("'").append(strCmd).append("' "); + } + + // A bit of verbosity + System.out.println("Command line: [" + cmdLine.toString() + "]"); + + ProcessBuilder pb = new ProcessBuilder(cmd); + // we don't expect any error output but make sure we are not stuck on pipe + // pb.redirectErrorStream(false); + pb.redirectError(ProcessBuilder.Redirect.INHERIT); + + appProcess = pb.start(); + + // Create pipe reader for process, and read stdin and stderr to array of strings + InputGobbler gb = new InputGobbler(appProcess.getInputStream(), storedAppOutput); + gb.start(); + } + + /** + * Delete lock file that signals app to terminate, then + * wait until app is actually terminated. + * @throws IOException + */ + public void stopApp() throws IOException { + deleteLock(); + waitAppTerminate(); + int exitcode = appProcess.exitValue(); + if (exitcode != 0) { + throw new IOException("LingeredApp terminated with non-zero exit code " + exitcode); + } + } + + /** + * High level interface for test writers + */ + /** + * Factory method that creates LingeredApp object with ready to use application + * lock name is autogenerated + * @param cmd - vm options, could be null to auto add testvm.options + * @return LingeredApp object + * @throws IOException + */ + public static LingeredApp startApp(List<String> cmd) throws IOException { + LingeredApp a = new LingeredApp(); + a.createLock(); + try { + a.runApp(cmd); + a.waitAppReady(appWaitTime); + } catch (Exception ex) { + a.deleteLock(); + throw ex; + } + + return a; + } + + /** + * Factory method that starts pre-created LingeredApp + * lock name is autogenerated + * @param cmd - vm options, could be null to auto add testvm.options + * @param theApp - app to start + * @return LingeredApp object + * @throws IOException + */ + + public static void startApp(List<String> cmd, LingeredApp theApp) throws IOException { + theApp.createLock(); + try { + theApp.runApp(cmd); + theApp.waitAppReady(appWaitTime); + } catch (Exception ex) { + theApp.deleteLock(); + throw ex; + } + } + + public static LingeredApp startApp() throws IOException { + return startApp(null); + } + + public static void stopApp(LingeredApp app) throws IOException { + if (app != null) { + // LingeredApp can throw an exception during the intialization, + // make sure we don't have cascade NPE + app.stopApp(); + } + } + + /** + * LastModified time might not work correctly in some cases it might + * cause later failures + */ + + public static boolean isLastModifiedWorking() { + boolean sane = true; + try { + long lm = lastModified("."); + if (lm == 0) { + System.err.println("SANITY Warning! The lastModifiedTime() doesn't work on this system, it returns 0"); + sane = false; + } + + long now = epoch(); + if (lm > now) { + System.err.println("SANITY Warning! The Clock is wrong on this system lastModifiedTime() > getTime()"); + sane = false; + } + + setLastModified(".", epoch()); + long lm1 = lastModified("."); + if (lm1 <= lm) { + System.err.println("SANITY Warning! The setLastModified doesn't work on this system"); + sane = false; + } + } + catch(IOException e) { + System.err.println("SANITY Warning! IOException during sanity check " + e); + sane = false; + } + + return sane; + } + + /** + * This part is the application it self + */ + public static void main(String args[]) { + + if (args.length != 1) { + System.err.println("Lock file name is not specified"); + System.exit(7); + } + + String theLockFileName = args[0]; + + try { + Path path = Paths.get(theLockFileName); + + while (Files.exists(path)) { + // Touch the lock to indicate our readiness + setLastModified(theLockFileName, epoch()); + Thread.sleep(spinDelay); + } + } catch (NoSuchFileException ex) { + // Lock deleted while we are setting last modified time. + // Ignore error and lets the app exits + } catch (Exception ex) { + System.err.println("LingeredApp ERROR: " + ex); + // Leave exit_code = 1 to Java launcher + System.exit(3); + } + + System.exit(0); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/apps/LingeredAppWithDeadlock.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2005, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.test.lib.apps; + +import java.util.concurrent.Phaser; + +public class LingeredAppWithDeadlock extends LingeredApp { + + private static final Object Lock1 = new Object(); + private static final Object Lock2 = new Object(); + + private static volatile int reachCount = 0; + + private static final Phaser p = new Phaser(2); + + private static class ThreadOne extends Thread { + public void run() { + // wait Lock2 is locked + p.arriveAndAwaitAdvance(); + synchronized (Lock1) { + // signal Lock1 is locked + p.arriveAndAwaitAdvance(); + synchronized (Lock2) { + reachCount += 1; + } + } + } + } + + private static class ThreadTwo extends Thread { + public void run() { + synchronized (Lock2) { + // signal Lock2 is locked + p.arriveAndAwaitAdvance(); + // wait Lock1 is locked + p.arriveAndAwaitAdvance(); + synchronized (Lock1) { + reachCount += 1; + } + } + } + } + + public static void main(String args[]) { + if (args.length != 1) { + System.err.println("Lock file name is not specified"); + System.exit(7); + } + + // Run two theads that should come to deadlock + new ThreadOne().start(); + new ThreadTwo().start(); + + if (reachCount > 0) { + // Not able to deadlock, exiting + System.exit(3); + } + + LingeredApp.main(args); + } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/cli/CPUSpecificCommandLineOptionTest.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.cli; + +import jdk.test.lib.cli.predicate.CPUSpecificPredicate; + +/** + * Base class for command line options tests that + * requires specific CPU arch or specific CPU features. + */ +public abstract class CPUSpecificCommandLineOptionTest + extends CommandLineOptionTest { + /** + * Creates new CPU specific test instance that does not + * require any CPU features. + * + * @param cpuArchPattern Regular expression that should + * match os.arch. + */ + public CPUSpecificCommandLineOptionTest(String cpuArchPattern) { + this(cpuArchPattern, null, null); + } + + /** + * Creates new CPU specific test instance that does not + * require from CPU support of {@code supportedCPUFeatures} features + * and no support of {@code unsupportedCPUFeatures}. + * + * @param cpuArchPattern Regular expression that should + * match os.arch. + * @param supportedCPUFeatures Array with names of features that + * should be supported by CPU. If {@code null}, + * then no features have to be supported. + * @param unsupportedCPUFeatures Array with names of features that + * should not be supported by CPU. + * If {@code null}, then CPU may support any + * features. + */ + public CPUSpecificCommandLineOptionTest(String cpuArchPattern, + String supportedCPUFeatures[], String unsupportedCPUFeatures[]) { + super(new CPUSpecificPredicate(cpuArchPattern, supportedCPUFeatures, + unsupportedCPUFeatures)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.cli; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.function.BooleanSupplier; + +import jdk.test.lib.process.ExitCode; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.Platform; +import jdk.test.lib.Utils; + +/** + * Base class for command line option tests. + */ +public abstract class CommandLineOptionTest { + public static final String UNLOCK_DIAGNOSTIC_VM_OPTIONS + = "-XX:+UnlockDiagnosticVMOptions"; + public static final String UNLOCK_EXPERIMENTAL_VM_OPTIONS + = "-XX:+UnlockExperimentalVMOptions"; + protected static final String UNRECOGNIZED_OPTION_ERROR_FORMAT + = "Unrecognized VM option '[+-]?%s(=.*)?'"; + protected static final String EXPERIMENTAL_OPTION_ERROR_FORMAT + = "VM option '%s' is experimental and must be enabled via " + + "-XX:\\+UnlockExperimentalVMOptions."; + protected static final String DIAGNOSTIC_OPTION_ERROR_FORMAT + = " VM option '%s' is diagnostic and must be enabled via " + + "-XX:\\+UnlockDiagnosticVMOptions."; + private static final String PRINT_FLAGS_FINAL_FORMAT = "%s\\s*:?=\\s*%s"; + + /** + * Verifies that JVM startup behavior matches our expectations. + * + * @param option an option that should be passed to JVM + * @param expectedMessages an array of patterns that should occur + * in JVM output. If {@code null} then + * JVM output could be empty. + * @param unexpectedMessages an array of patterns that should not + * occur in JVM output. If {@code null} then + * JVM output could be empty. + * @param exitErrorMessage message that will be shown if exit code is not + * as expected. + * @param wrongWarningMessage message that will be shown if warning + * messages are not as expected. + * @param exitCode expected exit code. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifyJVMStartup(String option, + String expectedMessages[], String unexpectedMessages[], + String exitErrorMessage, String wrongWarningMessage, + ExitCode exitCode) throws Throwable { + CommandLineOptionTest.verifyJVMStartup(expectedMessages, + unexpectedMessages, exitErrorMessage, + wrongWarningMessage, exitCode, false, option); + } + + /** + * Verifies that JVM startup behavior matches our expectations. + * + * @param expectedMessages an array of patterns that should occur + * in JVM output. If {@code null} then + * JVM output could be empty. + * @param unexpectedMessages an array of patterns that should not + * occur in JVM output. If {@code null} then + * JVM output could be empty. + * @param exitErrorMessage message that will be shown if exit code is not + * as expected. + * @param wrongWarningMessage message that will be shown if warning + * messages are not as expected. + * @param exitCode expected exit code. + * @param addTestVMOptions if {@code true} then test VM options will be + * passed to VM. + * @param options options that should be passed to VM in addition to mode + * flag. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifyJVMStartup(String expectedMessages[], + String unexpectedMessages[], String exitErrorMessage, + String wrongWarningMessage, ExitCode exitCode, + boolean addTestVMOptions, String... options) + throws Throwable { + List<String> finalOptions = new ArrayList<>(); + if (addTestVMOptions) { + Collections.addAll(finalOptions, ProcessTools.getVmInputArgs()); + Collections.addAll(finalOptions, Utils.getTestJavaOpts()); + } + Collections.addAll(finalOptions, options); + finalOptions.add("-version"); + + ProcessBuilder processBuilder + = ProcessTools.createJavaProcessBuilder(finalOptions.toArray( + new String[finalOptions.size()])); + OutputAnalyzer outputAnalyzer + = new OutputAnalyzer(processBuilder.start()); + + try { + outputAnalyzer.shouldHaveExitValue(exitCode.value); + } catch (RuntimeException e) { + String errorMessage = String.format( + "JVM process should have exit value '%d'.%n%s", + exitCode.value, exitErrorMessage); + throw new AssertionError(errorMessage, e); + } + + verifyOutput(expectedMessages, unexpectedMessages, + wrongWarningMessage, outputAnalyzer); + } + + /** + * Verifies that JVM startup behavior matches our expectations. + * + * @param expectedMessages an array of patterns that should occur in JVM + * output. If {@code null} then + * JVM output could be empty. + * @param unexpectedMessages an array of patterns that should not occur + * in JVM output. If {@code null} then + * JVM output could be empty. + * @param wrongWarningMessage message that will be shown if messages are + * not as expected. + * @param outputAnalyzer OutputAnalyzer instance + * @throws AssertionError if verification fails. + */ + public static void verifyOutput(String[] expectedMessages, + String[] unexpectedMessages, String wrongWarningMessage, + OutputAnalyzer outputAnalyzer) { + if (expectedMessages != null) { + for (String expectedMessage : expectedMessages) { + try { + outputAnalyzer.shouldMatch(expectedMessage); + } catch (RuntimeException e) { + String errorMessage = String.format( + "Expected message not found: '%s'.%n%s", + expectedMessage, wrongWarningMessage); + throw new AssertionError(errorMessage, e); + } + } + } + + if (unexpectedMessages != null) { + for (String unexpectedMessage : unexpectedMessages) { + try { + outputAnalyzer.shouldNotMatch(unexpectedMessage); + } catch (RuntimeException e) { + String errorMessage = String.format( + "Unexpected message found: '%s'.%n%s", + unexpectedMessage, wrongWarningMessage); + throw new AssertionError(errorMessage, e); + } + } + } + } + + /** + * Verifies that JVM startup behavior matches our expectations when type + * of newly started VM is the same as the type of current. + * + * @param expectedMessages an array of patterns that should occur + * in JVM output. If {@code null} then + * JVM output could be empty. + * @param unexpectedMessages an array of patterns that should not + * occur in JVM output. If {@code null} then + * JVM output could be empty. + * @param exitErrorMessage Message that will be shown if exit value is not + * as expected. + * @param wrongWarningMessage message that will be shown if warning + * messages are not as expected. + * @param exitCode expected exit code. + * @param options options that should be passed to VM in addition to mode + * flag. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifySameJVMStartup(String expectedMessages[], + String unexpectedMessages[], String exitErrorMessage, + String wrongWarningMessage, ExitCode exitCode, String... options) + throws Throwable { + List<String> finalOptions = new ArrayList<>(); + finalOptions.add(CommandLineOptionTest.getVMTypeOption()); + Collections.addAll(finalOptions, options); + + CommandLineOptionTest.verifyJVMStartup(expectedMessages, + unexpectedMessages, exitErrorMessage, + wrongWarningMessage, exitCode, false, + finalOptions.toArray(new String[finalOptions.size()])); + } + + /** + * Verifies that value of specified JVM option is the same as + * expected value. + * This method filter out option with {@code optionName} + * name from test java options. + * + * @param optionName a name of tested option. + * @param expectedValue expected value of tested option. + * @param optionErrorString message will be shown if option value is not as + * expected. + * @param additionalVMOpts additional options that should be + * passed to JVM. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifyOptionValue(String optionName, + String expectedValue, String optionErrorString, + String... additionalVMOpts) throws Throwable { + verifyOptionValue(optionName, expectedValue, optionErrorString, + true, additionalVMOpts); + } + + /** + * Verifies that value of specified JVM option is the same as + * expected value. + * This method filter out option with {@code optionName} + * name from test java options. + * + * @param optionName a name of tested option. + * @param expectedValue expected value of tested option. + * @param addTestVmOptions if {@code true}, then test VM options + * will be used. + * @param optionErrorString message will be shown if option value is not as + * expected. + * @param additionalVMOpts additional options that should be + * passed to JVM. + * @throws Throwable if verification fails or some other issues + * occur. + */ + public static void verifyOptionValue(String optionName, + String expectedValue, String optionErrorString, + boolean addTestVmOptions, String... additionalVMOpts) + throws Throwable { + List<String> vmOpts = new ArrayList<>(); + + if (addTestVmOptions) { + Collections.addAll(vmOpts, + Utils.getFilteredTestJavaOpts(optionName)); + } + Collections.addAll(vmOpts, additionalVMOpts); + Collections.addAll(vmOpts, "-XX:+PrintFlagsFinal", "-version"); + + ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( + vmOpts.toArray(new String[vmOpts.size()])); + + OutputAnalyzer outputAnalyzer + = new OutputAnalyzer(processBuilder.start()); + + try { + outputAnalyzer.shouldHaveExitValue(0); + } catch (RuntimeException e) { + String errorMessage = String.format( + "JVM should start with option '%s' without errors.", + optionName); + throw new AssertionError(errorMessage, e); + } + verifyOptionValue(optionName, expectedValue, optionErrorString, + outputAnalyzer); + } + + /** + * Verifies that value of specified JVM option is the same as + * expected value. + * + * @param optionName a name of tested option. + * @param expectedValue expected value of tested option. + * @param optionErrorString message will be shown if option value is not + * as expected. + * @param outputAnalyzer OutputAnalyzer instance + * @throws AssertionError if verification fails + */ + public static void verifyOptionValue(String optionName, + String expectedValue, String optionErrorString, + OutputAnalyzer outputAnalyzer) { + try { + outputAnalyzer.shouldMatch(String.format( + CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, + optionName, expectedValue)); + } catch (RuntimeException e) { + String errorMessage = String.format( + "Option '%s' is expected to have '%s' value%n%s", + optionName, expectedValue, + optionErrorString); + throw new AssertionError(errorMessage, e); + } + } + + /** + * Start VM with given options and values. + * Generates command line option flags from + * {@code optionNames} and {@code optionValues}. + * + * @param optionNames names of options to pass in + * @param optionValues values of option + * @param additionalVMOpts additional options that should be + * passed to JVM. + * @return output from vm process + */ + public static OutputAnalyzer startVMWithOptions(String[] optionNames, + String[] optionValues, + String... additionalVMOpts) throws Throwable { + List<String> vmOpts = new ArrayList<>(); + if (optionNames == null || optionValues == null || optionNames.length != optionValues.length) { + throw new IllegalArgumentException("optionNames and/or optionValues"); + } + + for (int i = 0; i < optionNames.length; i++) { + vmOpts.add(prepareFlag(optionNames[i], optionValues[i])); + } + Collections.addAll(vmOpts, additionalVMOpts); + Collections.addAll(vmOpts, "-version"); + + ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( + vmOpts.toArray(new String[vmOpts.size()])); + + return new OutputAnalyzer(processBuilder.start()); + } + + /** + * Verifies from the output that values of specified JVM options were the same as + * expected values. + * + * @param outputAnalyzer search output for expect options and values. + * @param optionNames names of tested options. + * @param expectedValues expected values of tested options. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifyOptionValuesFromOutput(OutputAnalyzer outputAnalyzer, + String[] optionNames, + String[] expectedValues) throws Throwable { + outputAnalyzer.shouldHaveExitValue(0); + for (int i = 0; i < optionNames.length; i++) { + outputAnalyzer.shouldMatch(String.format( + CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, + optionNames[i], expectedValues[i])); + } + } + + /** + * Verifies that value of specified JVM options are the same as + * expected values. + * Generates command line option flags from + * {@code optionNames} and {@code expectedValues}. + * + * @param optionNames names of tested options. + * @param expectedValues expected values of tested options. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifyOptionValues(String[] optionNames, + String[] expectedValues) throws Throwable { + OutputAnalyzer outputAnalyzer = startVMWithOptions(optionNames, expectedValues, "-XX:+PrintFlagsFinal"); + verifyOptionValuesFromOutput(outputAnalyzer, optionNames, expectedValues); + } + + /** + * Verifies that value of specified JVM when type of newly started VM + * is the same as the type of current. + * This method filter out option with {@code optionName} + * name from test java options. + * Only mode flag will be passed to VM in addition to + * {@code additionalVMOpts} + * + * @param optionName name of tested option. + * @param expectedValue expected value of tested option. + * @param optionErrorString message to show if option has another value + * @param additionalVMOpts additional options that should be + * passed to JVM. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifyOptionValueForSameVM(String optionName, + String expectedValue, String optionErrorString, + String... additionalVMOpts) throws Throwable { + List<String> finalOptions = new ArrayList<>(); + finalOptions.add(CommandLineOptionTest.getVMTypeOption()); + Collections.addAll(finalOptions, additionalVMOpts); + + CommandLineOptionTest.verifyOptionValue(optionName, expectedValue, + optionErrorString, false, + finalOptions.toArray(new String[finalOptions.size()])); + } + + /** + * Prepares boolean command line flag with name {@code name} according + * to it's {@code value}. + * + * @param name the name of option to be prepared + * @param value the value of option + * @return prepared command line flag + */ + public static String prepareBooleanFlag(String name, boolean value) { + return String.format("-XX:%c%s", (value ? '+' : '-'), name); + } + + /** + * Prepares numeric command line flag with name {@code name} by setting + * it's value to {@code value}. + * + * @param name the name of option to be prepared + * @param value the value of option + * @return prepared command line flag + */ + public static String prepareNumericFlag(String name, Number value) { + return String.format("-XX:%s=%s", name, value.toString()); + } + + /** + * Prepares generic command line flag with name {@code name} by setting + * it's value to {@code value}. + * + * @param name the name of option to be prepared + * @param value the value of option ("+" or "-" can be used instead of "true" or "false") + * @return prepared command line flag + */ + public static String prepareFlag(String name, String value) { + if (value.equals("+") || value.equalsIgnoreCase("true")) { + return "-XX:+" + name; + } else if (value.equals("-") || value.equalsIgnoreCase("false")) { + return "-XX:-" + name; + } else { + return "-XX:" + name + "=" + value; + } + } + + /** + * Returns message that should occur in VM output if option + * {@code optionName} if unrecognized. + * + * @param optionName the name of option for which message should be returned + * @return message saying that option {@code optionName} is unrecognized + */ + public static String getUnrecognizedOptionErrorMessage(String optionName) { + return String.format( + CommandLineOptionTest.UNRECOGNIZED_OPTION_ERROR_FORMAT, + optionName); + } + + /** + * Returns message that should occur in VM output if option + * {@code optionName} is experimental and + * -XX:+UnlockExperimentalVMOptions was not passed to VM. + * + * @param optionName the name of option for which message should be returned + * @return message saying that option {@code optionName} is experimental + */ + public static String getExperimentalOptionErrorMessage(String optionName) { + return String.format( + CommandLineOptionTest.EXPERIMENTAL_OPTION_ERROR_FORMAT, + optionName); + } + + /** + * Returns message that should occur in VM output if option + * {@code optionName} is diagnostic and -XX:+UnlockDiagnosticVMOptions + * was not passed to VM. + * + * @param optionName the name of option for which message should be returned + * @return message saying that option {@code optionName} is diganostic + */ + public static String getDiagnosticOptionErrorMessage(String optionName) { + return String.format( + CommandLineOptionTest.DIAGNOSTIC_OPTION_ERROR_FORMAT, + optionName); + } + + /** + * @return option required to start a new VM with the same type as current. + * @throws RuntimeException when VM type is unknown. + */ + private static String getVMTypeOption() { + if (Platform.isServer()) { + return "-server"; + } else if (Platform.isClient()) { + return "-client"; + } else if (Platform.isMinimal()) { + return "-minimal"; + } else if (Platform.isGraal()) { + return "-graal"; + } + throw new RuntimeException("Unknown VM mode."); + } + + private final BooleanSupplier predicate; + + /** + * Constructs new CommandLineOptionTest that will be executed only if + * predicate {@code predicate} return {@code true}. + * @param predicate a predicate responsible for test's preconditions check. + */ + public CommandLineOptionTest(BooleanSupplier predicate) { + this.predicate = predicate; + } + + /** + * Runs command line option test. + */ + public final void test() throws Throwable { + if (predicate.getAsBoolean()) { + runTestCases(); + } + } + + /** + * @throws Throwable if some issue happened during test cases execution. + */ + protected abstract void runTestCases() throws Throwable; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/cli/predicate/AndPredicate.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.cli.predicate; + +import java.util.function.BooleanSupplier; + +public class AndPredicate implements BooleanSupplier { + private final BooleanSupplier a; + private final BooleanSupplier b; + + public AndPredicate(BooleanSupplier a, BooleanSupplier b) { + this.a = a; + this.b = b; + } + + @Override + public boolean getAsBoolean() { + return a.getAsBoolean() && b.getAsBoolean(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/cli/predicate/CPUSpecificPredicate.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.cli.predicate; + +import jdk.test.lib.Platform; +import sun.hotspot.cpuinfo.CPUInfo; + +import java.util.function.BooleanSupplier; + +public class CPUSpecificPredicate implements BooleanSupplier { + private final String cpuArchPattern; + private final String supportedCPUFeatures[]; + private final String unsupportedCPUFeatures[]; + + public CPUSpecificPredicate(String cpuArchPattern, + String supportedCPUFeatures[], + String unsupportedCPUFeatures[]) { + this.cpuArchPattern = cpuArchPattern; + this.supportedCPUFeatures = supportedCPUFeatures; + this.unsupportedCPUFeatures = unsupportedCPUFeatures; + } + + @Override + public boolean getAsBoolean() { + if (!Platform.getOsArch().matches(cpuArchPattern)) { + System.out.println("CPU arch " + Platform.getOsArch() + " does not match " + cpuArchPattern); + return false; + } + + if (supportedCPUFeatures != null) { + for (String feature : supportedCPUFeatures) { + if (!CPUInfo.hasFeature(feature)) { + System.out.println("CPU does not support " + feature + + " feature"); + return false; + } + } + } + + if (unsupportedCPUFeatures != null) { + for (String feature : unsupportedCPUFeatures) { + if (CPUInfo.hasFeature(feature)) { + System.out.println("CPU support " + feature + " feature"); + return false; + } + } + } + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/cli/predicate/NotPredicate.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package jdk.test.lib.cli.predicate; + +import java.util.function.BooleanSupplier; + +public class NotPredicate implements BooleanSupplier { + private final BooleanSupplier s; + + public NotPredicate(BooleanSupplier s) { + this.s = s; + } + + @Override + public boolean getAsBoolean() { + return !s.getAsBoolean(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/cli/predicate/OrPredicate.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package jdk.test.lib.cli.predicate; + +import java.util.function.BooleanSupplier; + +public class OrPredicate implements BooleanSupplier { + private final BooleanSupplier a; + private final BooleanSupplier b; + + public OrPredicate(BooleanSupplier a, BooleanSupplier b) { + this.a = a; + this.b = b; + } + + @Override + public boolean getAsBoolean() { + return a.getAsBoolean() || b.getAsBoolean(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/dcmd/CommandExecutor.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,75 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.dcmd; + +import jdk.test.lib.process.OutputAnalyzer; + +/** + * Abstract base class for Diagnostic Command executors + */ +public abstract class CommandExecutor { + + /** + * Execute a diagnostic command + * + * @param cmd The diagnostic command to execute + * @return an {@link jdk.testlibrary.OutputAnalyzer} encapsulating the output of the command + * @throws CommandExecutorException if there is an exception on the "calling side" while trying to execute the + * Diagnostic Command. Exceptions thrown on the remote side are available as textual representations in + * stderr, regardless of the specific executor used. + */ + public final OutputAnalyzer execute(String cmd) throws CommandExecutorException { + return execute(cmd, false); + } + + /** + * Execute a diagnostic command + * + * @param cmd The diagnostic command to execute + * @param silent Do not print the command output + * @return an {@link jdk.testlibrary.OutputAnalyzer} encapsulating the output of the command + * @throws CommandExecutorException if there is an exception on the "calling side" while trying to execute the + * Diagnostic Command. Exceptions thrown on the remote side are available as textual representations in + * stderr, regardless of the specific executor used. + */ + public final OutputAnalyzer execute(String cmd, boolean silent) throws CommandExecutorException { + if (!silent) { + System.out.printf("Running DCMD '%s' through '%s'%n", cmd, this.getClass().getSimpleName()); + } + + OutputAnalyzer oa = executeImpl(cmd); + + if (!silent) { + System.out.println("---------------- stdout ----------------"); + System.out.println(oa.getStdout()); + System.out.println("---------------- stderr ----------------"); + System.out.println(oa.getStderr()); + System.out.println("----------------------------------------"); + System.out.println(); + } + return oa; + } + + protected abstract OutputAnalyzer executeImpl(String cmd) throws CommandExecutorException; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/dcmd/CommandExecutorException.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,36 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.dcmd; + +/** + * CommandExecutorException encapsulates exceptions thrown (on the "calling side") from the execution of Diagnostic + * Commands + */ +public class CommandExecutorException extends RuntimeException { + private static final long serialVersionUID = -7039597746579144280L; + + public CommandExecutorException(String message, Throwable e) { + super(message, e); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/dcmd/FileJcmdExecutor.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,81 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.dcmd; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; + +/** + * Executes Diagnostic Commands on the target VM (specified by pid) using the jcmd tool and its ability to read + * Diagnostic Commands from a file. + */ +public class FileJcmdExecutor extends PidJcmdExecutor { + + /** + * Instantiates a new FileJcmdExecutor targeting the current VM + */ + public FileJcmdExecutor() { + super(); + } + + /** + * Instantiates a new FileJcmdExecutor targeting the VM indicated by the given pid + * + * @param target Pid of the target VM + */ + public FileJcmdExecutor(String target) { + super(target); + } + + protected List<String> createCommandLine(String cmd) throws CommandExecutorException { + File cmdFile = createTempFile(); + writeCommandToTemporaryFile(cmd, cmdFile); + + return Arrays.asList(jcmdBinary, Long.toString(pid), + "-f", cmdFile.getAbsolutePath()); + } + + private void writeCommandToTemporaryFile(String cmd, File cmdFile) { + try (PrintWriter pw = new PrintWriter(cmdFile)) { + pw.println(cmd); + } catch (IOException e) { + String message = "Could not write to file: " + cmdFile.getAbsolutePath(); + throw new CommandExecutorException(message, e); + } + } + + private File createTempFile() { + try { + File cmdFile = File.createTempFile("input", "jcmd"); + cmdFile.deleteOnExit(); + return cmdFile; + } catch (IOException e) { + throw new CommandExecutorException("Could not create temporary file", e); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/dcmd/JMXExecutor.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,187 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.dcmd; + +import jdk.test.lib.process.OutputAnalyzer; + +import javax.management.*; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.lang.management.ManagementFactory; + +import java.util.HashMap; + +/** + * Executes Diagnostic Commands on the target VM (specified by a host/port combination or a full JMX Service URL) using + * the JMX interface. If the target is not the current VM, the JMX Remote interface must be enabled beforehand. + */ +public class JMXExecutor extends CommandExecutor { + + private final MBeanServerConnection mbs; + + /** + * Instantiates a new JMXExecutor targeting the current VM + */ + public JMXExecutor() { + super(); + mbs = ManagementFactory.getPlatformMBeanServer(); + } + + /** + * Instantiates a new JMXExecutor targeting the VM indicated by the given host/port combination or a full JMX + * Service URL + * + * @param target a host/port combination on the format "host:port" or a full JMX Service URL of the target VM + */ + public JMXExecutor(String target) { + String urlStr; + + if (target.matches("^\\w[\\w\\-]*(\\.[\\w\\-]+)*:\\d+$")) { + /* Matches "hostname:port" */ + urlStr = String.format("service:jmx:rmi:///jndi/rmi://%s/jmxrmi", target); + } else if (target.startsWith("service:")) { + urlStr = target; + } else { + throw new IllegalArgumentException("Could not recognize target string: " + target); + } + + try { + JMXServiceURL url = new JMXServiceURL(urlStr); + JMXConnector c = JMXConnectorFactory.connect(url, new HashMap<>()); + mbs = c.getMBeanServerConnection(); + } catch (IOException e) { + throw new CommandExecutorException("Could not initiate connection to target: " + target, e); + } + } + + protected OutputAnalyzer executeImpl(String cmd) throws CommandExecutorException { + String stdout = ""; + String stderr = ""; + + String[] cmdParts = cmd.split(" ", 2); + String operation = commandToMethodName(cmdParts[0]); + Object[] dcmdArgs = produceArguments(cmdParts); + String[] signature = {String[].class.getName()}; + + ObjectName beanName = getMBeanName(); + + try { + stdout = (String) mbs.invoke(beanName, operation, dcmdArgs, signature); + } + + /* Failures on the "local" side, the one invoking the command. */ + catch (ReflectionException e) { + Throwable cause = e.getCause(); + if (cause instanceof NoSuchMethodException) { + /* We want JMXExecutor to match the behavior of the other CommandExecutors */ + String message = "Unknown diagnostic command: " + operation; + stderr = exceptionTraceAsString(new IllegalArgumentException(message, e)); + } else { + rethrowExecutorException(operation, dcmdArgs, e); + } + } + + /* Failures on the "local" side, the one invoking the command. */ + catch (InstanceNotFoundException | IOException e) { + rethrowExecutorException(operation, dcmdArgs, e); + } + + /* Failures on the remote side, the one executing the invoked command. */ + catch (MBeanException e) { + stdout = exceptionTraceAsString(e); + } + + return new OutputAnalyzer(stdout, stderr); + } + + private void rethrowExecutorException(String operation, Object[] dcmdArgs, + Exception e) throws CommandExecutorException { + String message = String.format("Could not invoke: %s %s", operation, + String.join(" ", (String[]) dcmdArgs[0])); + throw new CommandExecutorException(message, e); + } + + private ObjectName getMBeanName() throws CommandExecutorException { + String MBeanName = "com.sun.management:type=DiagnosticCommand"; + + try { + return new ObjectName(MBeanName); + } catch (MalformedObjectNameException e) { + String message = "MBean not found: " + MBeanName; + throw new CommandExecutorException(message, e); + } + } + + private Object[] produceArguments(String[] cmdParts) { + Object[] dcmdArgs = {new String[0]}; /* Default: No arguments */ + + if (cmdParts.length == 2) { + dcmdArgs[0] = cmdParts[1].split(" "); + } + return dcmdArgs; + } + + /** + * Convert from diagnostic command to MBean method name + * + * Examples: + * help --> help + * VM.version --> vmVersion + * VM.command_line --> vmCommandLine + */ + private static String commandToMethodName(String cmd) { + String operation = ""; + boolean up = false; /* First letter is to be lower case */ + + /* + * If a '.' or '_' is encountered it is not copied, + * instead the next character will be converted to upper case + */ + for (char c : cmd.toCharArray()) { + if (('.' == c) || ('_' == c)) { + up = true; + } else if (up) { + operation = operation.concat(Character.toString(c).toUpperCase()); + up = false; + } else { + operation = operation.concat(Character.toString(c).toLowerCase()); + } + } + + return operation; + } + + private static String exceptionTraceAsString(Throwable cause) { + StringWriter sw = new StringWriter(); + cause.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/dcmd/JcmdExecutor.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,58 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.dcmd; + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import java.util.List; + +/** + * Base class for Diagnostic Command Executors using the jcmd tool + */ +public abstract class JcmdExecutor extends CommandExecutor { + protected String jcmdBinary; + + protected abstract List<String> createCommandLine(String cmd) throws CommandExecutorException; + + protected JcmdExecutor() { + jcmdBinary = JDKToolFinder.getJDKTool("jcmd"); + } + + protected OutputAnalyzer executeImpl(String cmd) throws CommandExecutorException { + List<String> commandLine = createCommandLine(cmd); + + try { + System.out.printf("Executing command '%s'%n", commandLine); + OutputAnalyzer output = ProcessTools.executeProcess(new ProcessBuilder(commandLine)); + System.out.printf("Command returned with exit code %d%n", output.getExitValue()); + + return output; + } catch (Exception e) { + String message = String.format("Caught exception while executing '%s'", commandLine); + throw new CommandExecutorException(message, e); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/dcmd/MainClassJcmdExecutor.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,57 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.dcmd; + +import java.util.Arrays; +import java.util.List; + +/** + * Executes Diagnostic Commands on the target VM (specified by main class) using the jcmd tool + */ +public class MainClassJcmdExecutor extends JcmdExecutor { + private final String mainClass; + + /** + * Instantiates a new MainClassJcmdExecutor targeting the current VM + */ + public MainClassJcmdExecutor() { + super(); + mainClass = System.getProperty("sun.java.command").split(" ")[0]; + } + + /** + * Instantiates a new MainClassJcmdExecutor targeting the VM indicated by the given main class + * + * @param target Main class of the target VM + */ + public MainClassJcmdExecutor(String target) { + super(); + mainClass = target; + } + + protected List<String> createCommandLine(String cmd) throws CommandExecutorException { + return Arrays.asList(jcmdBinary, mainClass, cmd); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/dcmd/PidJcmdExecutor.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,63 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.dcmd; + +import jdk.test.lib.process.ProcessTools; + +import java.util.Arrays; +import java.util.List; + +/** + * Executes Diagnostic Commands on the target VM (specified by pid) using the jcmd tool + */ +public class PidJcmdExecutor extends JcmdExecutor { + protected final long pid; + + /** + * Instantiates a new PidJcmdExecutor targeting the current VM + */ + public PidJcmdExecutor() { + super(); + try { + pid = ProcessTools.getProcessId(); + } catch (Exception e) { + throw new CommandExecutorException("Could not determine own pid", e); + } + } + + /** + * Instantiates a new PidJcmdExecutor targeting the VM indicated by the given pid + * + * @param target Pid of the target VM + */ + public PidJcmdExecutor(String target) { + super(); + pid = Long.valueOf(target); + } + + protected List<String> createCommandLine(String cmd) throws CommandExecutorException { + return Arrays.asList(jcmdBinary, Long.toString(pid), cmd); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/HprofParser.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,97 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.hprof; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; + +import jdk.test.lib.hprof.model.Snapshot; +import jdk.test.lib.hprof.parser.Reader; + +/** + * Helper class to parse a java heap dump file. + */ +public class HprofParser { + + public static void main(String[] args) throws Exception { + if (args.length < 1) { + System.out.println("No arguments supplied"); + } + File dump = new File(args[0]); + if (!dump.exists() || !dump.isFile()) { + throw new RuntimeException("The dump file does not exist or not a file"); + } + parse(dump); + } + + /** + * @see #parse(File, boolean, boolean, boolean) + */ + public static File parse(File dump) throws Exception { + return parse(dump, false, true, true); + } + + /** + * @see #parse(File, boolean, boolean, boolean) + */ + public static File parseWithDebugInfo(File dump) throws Exception { + return parse(dump, true, true, true); + } + + /** + * Parse a java heap dump file + * + * @param dump Heap dump file to parse + * @param debug Turn on/off debug file parsing + * @param callStack Turn on/off tracking of object allocation call stack + * @param calculateRefs Turn on/off tracking object allocation call stack + * @throws Exception + * @return File containing output from the parser + */ + public static File parse(File dump, boolean debug, boolean callStack, boolean calculateRefs) throws Exception { + File out = new File("hprof." + System.currentTimeMillis() + ".out"); + if (out.exists()) { + out.delete(); + } + + PrintStream psSystemOut = System.out; + try (PrintStream psHprof = new PrintStream(new BufferedOutputStream(new FileOutputStream(out.getAbsolutePath())))) { + System.setOut(psHprof); + + int debugLevel = debug ? 2 : 0; + try (Snapshot snapshot = Reader.readFile(dump.getAbsolutePath(), callStack, debugLevel)) { + System.out.println("Snapshot read, resolving..."); + snapshot.resolve(calculateRefs); + System.out.println("Snapshot resolved."); + } + } finally { + System.setOut(psSystemOut); + } + + return out; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/README Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,13 @@ +The jhat tool has been removed. jhat hprof file parser/validator +are needed for tests. The old packages for jhat were moved here: +com.sun.tools.hat.internal.model -> jdk.test.lib.hprof.model +com.sun.tools.hat.internal.parser -> jdk.test.lib.hprof.parser +com.sun.tools.hat.internal.util -> jdk.test.lib.hprof.util + +jhat was added in JDK 6 and its original implementation was from +java.net HAT project [1]. jhat is an experimental, unsupported tool. +There hasn't been much update to jhat tool in the JDK. In addition, +there are several better heap dump visualizer/analyzer emerged since +JDK 5/6 serviceability support. + +[1] https://java.net/projects/hat
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/AbstractJavaHeapObjectVisitor.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * A visitor for a JavaThing. @see JavaObject#visitReferencedObjects() + * + */ + + +abstract public class AbstractJavaHeapObjectVisitor + implements JavaHeapObjectVisitor { + abstract public void visit(JavaHeapObject other); + + /** + * Should the given field be excluded from the set of things visited? + * @return true if it should. + */ + public boolean exclude(JavaClass clazz, JavaField f) { + return false; + } + + /** + * @return true iff exclude might ever return true + */ + public boolean mightExclude() { + return false; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/ArrayTypeCodes.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Primitive array type codes as defined by VM specification. + * + */ +public interface ArrayTypeCodes { + // Typecodes for array elements. + // Refer to newarray instruction in VM Spec. + public static final int T_BOOLEAN = 4; + public static final int T_CHAR = 5; + public static final int T_FLOAT = 6; + public static final int T_DOUBLE = 7; + public static final int T_BYTE = 8; + public static final int T_SHORT = 9; + public static final int T_INT = 10; + public static final int T_LONG = 11; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/HackJavaValue.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * This is used to represent values that the program doesn't really understand. + * This includes the null vlaue, and unresolved references (which shouldn't + * happen in well-formed hprof files). + * + * + * @author Bill Foote + */ + + + + +public class HackJavaValue extends JavaValue { + + private String value; + private int size; + + public HackJavaValue(String value, int size) { + this.value = value; + this.size = size; + } + + public String toString() { + return value; + } + + public int getSize() { + return size; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaBoolean.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Represents a boolean (i.e. a boolean field in an instance). + * + * @author Bill Foote + */ + + +public class JavaBoolean extends JavaValue { + + boolean value; + + public JavaBoolean(boolean value) { + this.value = value; + } + + public String toString() { + return "" + value; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaByte.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Represents an byte (i.e. a byte field in an instance). + * + * @author Bill Foote + */ + + +public class JavaByte extends JavaValue { + + byte value; + + public JavaByte(byte value) { + this.value = value; + } + + public String toString() { + return "0x" + Integer.toString(((int) value) & 0xff, 16); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaChar.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Represents a char (i.e. a char field in an instance). + * + * @author Bill Foote + */ + + +public class JavaChar extends JavaValue { + + char value; + + public JavaChar(char value) { + this.value = value; + } + + public String toString() { + return "" + value; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaClass.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,503 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import java.util.Vector; +import java.util.Enumeration; +import jdk.test.lib.hprof.util.CompositeEnumeration; +import jdk.test.lib.hprof.parser.ReadBuffer; + +/** + * + * @author Bill Foote + */ + + +public class JavaClass extends JavaHeapObject { + // my id + private long id; + // my name + private String name; + + // These are JavaObjectRef before resolve + private JavaThing superclass; + private JavaThing loader; + private JavaThing signers; + private JavaThing protectionDomain; + + // non-static fields + private JavaField[] fields; + // static fields + private JavaStatic[] statics; + + private static final JavaClass[] EMPTY_CLASS_ARRAY = new JavaClass[0]; + // my subclasses + private JavaClass[] subclasses = EMPTY_CLASS_ARRAY; + + // my instances + private Vector<JavaHeapObject> instances = new Vector<JavaHeapObject>(); + + // Who I belong to. Set on resolve. + private Snapshot mySnapshot; + + // Size of an instance, including VM overhead + private int instanceSize; + // Total number of fields including inherited ones + private int totalNumFields; + + + public JavaClass(long id, String name, long superclassId, long loaderId, + long signersId, long protDomainId, + JavaField[] fields, JavaStatic[] statics, + int instanceSize) { + this.id = id; + this.name = name; + this.superclass = new JavaObjectRef(superclassId); + this.loader = new JavaObjectRef(loaderId); + this.signers = new JavaObjectRef(signersId); + this.protectionDomain = new JavaObjectRef(protDomainId); + this.fields = fields; + this.statics = statics; + this.instanceSize = instanceSize; + } + + public JavaClass(String name, long superclassId, long loaderId, + long signersId, long protDomainId, + JavaField[] fields, JavaStatic[] statics, + int instanceSize) { + this(-1L, name, superclassId, loaderId, signersId, + protDomainId, fields, statics, instanceSize); + } + + public final JavaClass getClazz() { + return mySnapshot.getJavaLangClass(); + } + + public final int getIdentifierSize() { + return mySnapshot.getIdentifierSize(); + } + + public final int getMinimumObjectSize() { + return mySnapshot.getMinimumObjectSize(); + } + + public void resolve(Snapshot snapshot) { + if (mySnapshot != null) { + return; + } + mySnapshot = snapshot; + resolveSuperclass(snapshot); + if (superclass != null) { + ((JavaClass) superclass).addSubclass(this); + } + + loader = loader.dereference(snapshot, null); + signers = signers.dereference(snapshot, null); + protectionDomain = protectionDomain.dereference(snapshot, null); + + for (int i = 0; i < statics.length; i++) { + statics[i].resolve(this, snapshot); + } + snapshot.getJavaLangClass().addInstance(this); + super.resolve(snapshot); + return; + } + + /** + * Resolve our superclass. This might be called well before + * all instances are available (like when reading deferred + * instances in a 1.2 dump file :-) Calling this is sufficient + * to be able to explore this class' fields. + */ + public void resolveSuperclass(Snapshot snapshot) { + if (superclass == null) { + // We must be java.lang.Object, so we have no superclass. + } else { + totalNumFields = fields.length; + superclass = superclass.dereference(snapshot, null); + if (superclass == snapshot.getNullThing()) { + superclass = null; + } else { + try { + JavaClass sc = (JavaClass) superclass; + sc.resolveSuperclass(snapshot); + totalNumFields += sc.totalNumFields; + } catch (ClassCastException ex) { + System.out.println("Warning! Superclass of " + name + " is " + superclass); + superclass = null; + } + } + } + } + + public boolean isString() { + return mySnapshot.getJavaLangString() == this; + } + + public boolean isClassLoader() { + return mySnapshot.getJavaLangClassLoader().isAssignableFrom(this); + } + + /** + * Get a numbered field from this class + */ + public JavaField getField(int i) { + if (i < 0 || i >= fields.length) { + throw new Error("No field " + i + " for " + name); + } + return fields[i]; + } + + /** + * Get the total number of fields that are part of an instance of + * this class. That is, include superclasses. + */ + public int getNumFieldsForInstance() { + return totalNumFields; + } + + /** + * Get a numbered field from all the fields that are part of instance + * of this class. That is, include superclasses. + */ + public JavaField getFieldForInstance(int i) { + if (superclass != null) { + JavaClass sc = (JavaClass) superclass; + if (i < sc.totalNumFields) { + return sc.getFieldForInstance(i); + } + i -= sc.totalNumFields; + } + return getField(i); + } + + /** + * Get the class responsible for field i, where i is a field number that + * could be passed into getFieldForInstance. + * + * @see JavaClass.getFieldForInstance() + */ + public JavaClass getClassForField(int i) { + if (superclass != null) { + JavaClass sc = (JavaClass) superclass; + if (i < sc.totalNumFields) { + return sc.getClassForField(i); + } + } + return this; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public boolean isArray() { + return name.indexOf('[') != -1; + } + + public Enumeration<JavaHeapObject> getInstances(boolean includeSubclasses) { + if (includeSubclasses) { + Enumeration<JavaHeapObject> res = instances.elements(); + for (int i = 0; i < subclasses.length; i++) { + res = new CompositeEnumeration(res, + subclasses[i].getInstances(true)); + } + return res; + } else { + return instances.elements(); + } + } + + /** + * @return a count of the instances of this class + */ + public int getInstancesCount(boolean includeSubclasses) { + int result = instances.size(); + if (includeSubclasses) { + for (int i = 0; i < subclasses.length; i++) { + result += subclasses[i].getInstancesCount(includeSubclasses); + } + } + return result; + } + + public JavaClass[] getSubclasses() { + return subclasses; + } + + /** + * This can only safely be called after resolve() + */ + public JavaClass getSuperclass() { + return (JavaClass) superclass; + } + + /** + * This can only safely be called after resolve() + */ + public JavaThing getLoader() { + return loader; + } + + /** + * This can only safely be called after resolve() + */ + public boolean isBootstrap() { + return loader == mySnapshot.getNullThing(); + } + + /** + * This can only safely be called after resolve() + */ + public JavaThing getSigners() { + return signers; + } + + /** + * This can only safely be called after resolve() + */ + public JavaThing getProtectionDomain() { + return protectionDomain; + } + + public JavaField[] getFields() { + return fields; + } + + /** + * Includes superclass fields + */ + public JavaField[] getFieldsForInstance() { + Vector<JavaField> v = new Vector<JavaField>(); + addFields(v); + JavaField[] result = new JavaField[v.size()]; + for (int i = 0; i < v.size(); i++) { + result[i] = v.elementAt(i); + } + return result; + } + + + public JavaStatic[] getStatics() { + return statics; + } + + // returns value of static field of given name + public JavaThing getStaticField(String name) { + for (int i = 0; i < statics.length; i++) { + JavaStatic s = statics[i]; + if (s.getField().getName().equals(name)) { + return s.getValue(); + } + } + return null; + } + + public String toString() { + return "class " + name; + } + + public int compareTo(JavaThing other) { + if (other instanceof JavaClass) { + return name.compareTo(((JavaClass) other).name); + } + return super.compareTo(other); + } + + + /** + * @return true iff a variable of type this is assignable from an instance + * of other + */ + public boolean isAssignableFrom(JavaClass other) { + if (this == other) { + return true; + } else if (other == null) { + return false; + } else { + return isAssignableFrom((JavaClass) other.superclass); + // Trivial tail recursion: I have faith in javac. + } + } + + /** + * Describe the reference that this thing has to target. This will only + * be called if target is in the array returned by getChildrenForRootset. + */ + public String describeReferenceTo(JavaThing target, Snapshot ss) { + for (int i = 0; i < statics.length; i++) { + JavaField f = statics[i].getField(); + if (f.hasId()) { + JavaThing other = statics[i].getValue(); + if (other == target) { + return "static field " + f.getName(); + } + } + } + return super.describeReferenceTo(target, ss); + } + + /** + * @return the size of an instance of this class. Gives 0 for an array + * type. + */ + public int getInstanceSize() { + return instanceSize + mySnapshot.getMinimumObjectSize(); + } + + + /** + * @return The size of all instances of this class. Correctly handles + * arrays. + */ + public long getTotalInstanceSize() { + int count = instances.size(); + if (count == 0 || !isArray()) { + return count * instanceSize; + } + + // array class and non-zero count, we have to + // get the size of each instance and sum it + long result = 0; + for (int i = 0; i < count; i++) { + JavaThing t = (JavaThing) instances.elementAt(i); + result += t.getSize(); + } + return result; + } + + /** + * @return the size of this object + */ + public int getSize() { + JavaClass cl = mySnapshot.getJavaLangClass(); + if (cl == null) { + return 0; + } else { + return cl.getInstanceSize(); + } + } + + public void visitReferencedObjects(JavaHeapObjectVisitor v) { + super.visitReferencedObjects(v); + JavaHeapObject sc = getSuperclass(); + if (sc != null) v.visit(getSuperclass()); + + JavaThing other; + other = getLoader(); + if (other instanceof JavaHeapObject) { + v.visit((JavaHeapObject)other); + } + other = getSigners(); + if (other instanceof JavaHeapObject) { + v.visit((JavaHeapObject)other); + } + other = getProtectionDomain(); + if (other instanceof JavaHeapObject) { + v.visit((JavaHeapObject)other); + } + + for (int i = 0; i < statics.length; i++) { + JavaField f = statics[i].getField(); + if (!v.exclude(this, f) && f.hasId()) { + other = statics[i].getValue(); + if (other instanceof JavaHeapObject) { + v.visit((JavaHeapObject) other); + } + } + } + } + + // package-privates below this point + final ReadBuffer getReadBuffer() { + return mySnapshot.getReadBuffer(); + } + + final void setNew(JavaHeapObject obj, boolean flag) { + mySnapshot.setNew(obj, flag); + } + + final boolean isNew(JavaHeapObject obj) { + return mySnapshot.isNew(obj); + } + + final StackTrace getSiteTrace(JavaHeapObject obj) { + return mySnapshot.getSiteTrace(obj); + } + + final void addReferenceFromRoot(Root root, JavaHeapObject obj) { + mySnapshot.addReferenceFromRoot(root, obj); + } + + final Root getRoot(JavaHeapObject obj) { + return mySnapshot.getRoot(obj); + } + + final Snapshot getSnapshot() { + return mySnapshot; + } + + void addInstance(JavaHeapObject inst) { + instances.addElement(inst); + } + + // Internals only below this point + private void addFields(Vector<JavaField> v) { + if (superclass != null) { + ((JavaClass) superclass).addFields(v); + } + for (int i = 0; i < fields.length; i++) { + v.addElement(fields[i]); + } + } + + private void addSubclassInstances(Vector<JavaHeapObject> v) { + for (int i = 0; i < subclasses.length; i++) { + subclasses[i].addSubclassInstances(v); + } + for (int i = 0; i < instances.size(); i++) { + v.addElement(instances.elementAt(i)); + } + } + + private void addSubclass(JavaClass sub) { + JavaClass newValue[] = new JavaClass[subclasses.length + 1]; + System.arraycopy(subclasses, 0, newValue, 0, subclasses.length); + newValue[subclasses.length] = sub; + subclasses = newValue; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaDouble.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Represents a double (i.e. a double field in an instance). + * + * @author Bill Foote + */ + + +public class JavaDouble extends JavaValue { + + double value; + + public JavaDouble(double value) { + this.value = value; + } + + public String toString() { + return Double.toString(value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaField.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + + +/** + * + * @author Bill Foote + */ + +public class JavaField { + + private String name; + private String signature; + + public JavaField(String name, String signature) { + this.name = name; + this.signature = signature; + } + + + /** + * @return true if the type of this field is something that has an ID. + * int fields, for exampe, don't. + */ + public boolean hasId() { + char ch = signature.charAt(0); + return (ch == '[' || ch == 'L'); + } + + public String getName() { + return name; + } + + public String getSignature() { + return signature; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaFloat.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Represents a float (i.e. a float field in an instance). + * + * @author Bill Foote + */ + + +public class JavaFloat extends JavaValue { + + float value; + + public JavaFloat(float value) { + this.value = value; + } + + public String toString() { + return Float.toString(value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaHeapObject.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import jdk.test.lib.hprof.util.Misc; + + +/** + * + * @author Bill Foote + */ + +/** + * Represents an object that's allocated out of the Java heap. It occupies + * memory in the VM, and is the sort of thing that in a JDK 1.1 VM had + * a handle. It can be a + * JavaClass, a JavaObjectArray, a JavaValueArray or a JavaObject. + */ + +public abstract class JavaHeapObject extends JavaThing { + + // + // Who we refer to. This is heavily optimized for space, because it's + // well worth trading a bit of speed for less swapping. + // referers and referersLen go through two phases: Building and + // resolved. When building, referers might have duplicates, but can + // be appended to. When resolved, referers has no duplicates or + // empty slots. + // + private JavaThing[] referers = null; + private int referersLen = 0; // -1 when resolved + + public abstract JavaClass getClazz(); + public abstract int getSize(); + public abstract long getId(); + + /** + * Do any initialization this thing needs after its data is read in. + * Subclasses that override this should call super.resolve(). + */ + public void resolve(Snapshot snapshot) { + StackTrace trace = snapshot.getSiteTrace(this); + if (trace != null) { + trace.resolve(snapshot); + } + } + + // + // Eliminate duplicates from referers, and size the array exactly. + // This sets us up to answer queries. See the comments around the + // referers data member for details. + // + void setupReferers() { + if (referersLen > 1) { + // Copy referers to map, screening out duplicates + Map<JavaThing, JavaThing> map = new HashMap<JavaThing, JavaThing>(); + for (int i = 0; i < referersLen; i++) { + if (map.get(referers[i]) == null) { + map.put(referers[i], referers[i]); + } + } + + // Now copy into the array + referers = new JavaThing[map.size()]; + map.keySet().toArray(referers); + } + referersLen = -1; + } + + + /** + * @return the id of this thing as hex string + */ + public String getIdString() { + return Misc.toHex(getId()); + } + + public String toString() { + return getClazz().getName() + "@" + getIdString(); + } + + /** + * @return the StackTrace of the point of allocation of this object, + * or null if unknown + */ + public StackTrace getAllocatedFrom() { + return getClazz().getSiteTrace(this); + } + + public boolean isNew() { + return getClazz().isNew(this); + } + + void setNew(boolean flag) { + getClazz().setNew(this, flag); + } + + /** + * Tell the visitor about all of the objects we refer to + */ + public void visitReferencedObjects(JavaHeapObjectVisitor v) { + v.visit(getClazz()); + } + + void addReferenceFrom(JavaHeapObject other) { + if (referersLen == 0) { + referers = new JavaThing[1]; // It was null + } else if (referersLen == referers.length) { + JavaThing[] copy = new JavaThing[(3 * (referersLen + 1)) / 2]; + System.arraycopy(referers, 0, copy, 0, referersLen); + referers = copy; + } + referers[referersLen++] = other; + // We just append to referers here. Measurements have shown that + // around 10% to 30% are duplicates, so it's better to just append + // blindly and screen out all the duplicates at once. + } + + void addReferenceFromRoot(Root r) { + getClazz().addReferenceFromRoot(r, this); + } + + /** + * If the rootset includes this object, return a Root describing one + * of the reasons why. + */ + public Root getRoot() { + return getClazz().getRoot(this); + } + + /** + * Tell who refers to us. + * + * @return an Enumeration of JavaHeapObject instances + */ + public Enumeration<JavaThing> getReferers() { + if (referersLen != -1) { + throw new RuntimeException("not resolved: " + getIdString()); + } + return new Enumeration<JavaThing>() { + + private int num = 0; + + public boolean hasMoreElements() { + return referers != null && num < referers.length; + } + + public JavaThing nextElement() { + return referers[num++]; + } + }; + } + + /** + * Given other, which the caller promises is in referers, determines if + * the reference is only a weak reference. + */ + public boolean refersOnlyWeaklyTo(Snapshot ss, JavaThing other) { + return false; + } + + /** + * Describe the reference that this thing has to target. This will only + * be called if target is in the array returned by getChildrenForRootset. + */ + public String describeReferenceTo(JavaThing target, Snapshot ss) { + return "??"; + } + + public boolean isHeapAllocated() { + return true; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaHeapObjectVisitor.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * A visitor for a JavaThing. @see JavaObject#visitReferencedObjects() + * + * @author Bill Foote + */ + + +public interface JavaHeapObjectVisitor { + public void visit(JavaHeapObject other); + + /** + * Should the given field be excluded from the set of things visited? + * @return true if it should. + */ + public boolean exclude(JavaClass clazz, JavaField f); + + /** + * @return true iff exclude might ever return true + */ + public boolean mightExclude(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaInt.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Represents an integer (i.e. an int field in an instance). + * + * @author Bill Foote + */ + + +public class JavaInt extends JavaValue { + + int value; + + public JavaInt(int value) { + this.value = value; + } + + public String toString() { + return "" + value; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaLazyReadObject.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import java.io.IOException; +import jdk.test.lib.hprof.parser.ReadBuffer; + +/* + * Base class for lazily read Java heap objects. + */ +public abstract class JavaLazyReadObject extends JavaHeapObject { + + // file offset from which this object data starts + private final long offset; + + protected JavaLazyReadObject(long offset) { + this.offset = offset; + } + + public final int getSize() { + return getValueLength() + getClazz().getMinimumObjectSize(); + } + + protected final long getOffset() { + return offset; + } + + // return the length of the data for this object + protected final int getValueLength() { + try { + return readValueLength(); + } catch (IOException exp) { + System.err.println("lazy read failed at offset " + offset); + exp.printStackTrace(); + return 0; + } + } + + // get this object's content as byte array + protected final byte[] getValue() { + try { + return readValue(); + } catch (IOException exp) { + System.err.println("lazy read failed at offset " + offset); + exp.printStackTrace(); + return Snapshot.EMPTY_BYTE_ARRAY; + } + } + + // get ID of this object + public final long getId() { + try { + ReadBuffer buf = getClazz().getReadBuffer(); + int idSize = getClazz().getIdentifierSize(); + if (idSize == 4) { + return ((long)buf.getInt(offset)) & Snapshot.SMALL_ID_MASK; + } else { + return buf.getLong(offset); + } + } catch (IOException exp) { + System.err.println("lazy read failed at offset " + offset); + exp.printStackTrace(); + return -1; + } + } + + protected abstract int readValueLength() throws IOException; + protected abstract byte[] readValue() throws IOException; + + // make Integer or Long for given object ID + protected static Number makeId(long id) { + if ((id & ~Snapshot.SMALL_ID_MASK) == 0) { + return (int)id; + } else { + return id; + } + } + + // get ID as long value from Number + protected static long getIdValue(Number num) { + long id = num.longValue(); + if (num instanceof Integer) { + id &= Snapshot.SMALL_ID_MASK; + } + return id; + } + + // read object ID from given index from given byte array + protected final long objectIdAt(int index, byte[] data) { + int idSize = getClazz().getIdentifierSize(); + if (idSize == 4) { + return ((long)intAt(index, data)) & Snapshot.SMALL_ID_MASK; + } else { + return longAt(index, data); + } + } + + // utility methods to read primitive types from byte array + protected static byte byteAt(int index, byte[] value) { + return value[index]; + } + + protected static boolean booleanAt(int index, byte[] value) { + return (value[index] & 0xff) == 0? false: true; + } + + protected static char charAt(int index, byte[] value) { + int b1 = ((int) value[index++] & 0xff); + int b2 = ((int) value[index++] & 0xff); + return (char) ((b1 << 8) + b2); + } + + protected static short shortAt(int index, byte[] value) { + int b1 = ((int) value[index++] & 0xff); + int b2 = ((int) value[index++] & 0xff); + return (short) ((b1 << 8) + b2); + } + + protected static int intAt(int index, byte[] value) { + int b1 = ((int) value[index++] & 0xff); + int b2 = ((int) value[index++] & 0xff); + int b3 = ((int) value[index++] & 0xff); + int b4 = ((int) value[index++] & 0xff); + return ((b1 << 24) + (b2 << 16) + (b3 << 8) + b4); + } + + protected static long longAt(int index, byte[] value) { + long val = 0; + for (int j = 0; j < 8; j++) { + val = val << 8; + int b = ((int)value[index++]) & 0xff; + val |= b; + } + return val; + } + + protected static float floatAt(int index, byte[] value) { + int val = intAt(index, value); + return Float.intBitsToFloat(val); + } + + protected static double doubleAt(int index, byte[] value) { + long val = longAt(index, value); + return Double.longBitsToDouble(val); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaLong.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Represents a long (i.e. a long field in an instance). + * + * @author Bill Foote + */ + + +public class JavaLong extends JavaValue { + + long value; + + public JavaLong(long value) { + this.value = value; + } + + public String toString() { + return Long.toString(value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaObject.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,334 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import java.io.IOException; +import jdk.test.lib.hprof.parser.ReadBuffer; + +/** + * Represents Java instance + * + * @author Bill Foote + */ +public class JavaObject extends JavaLazyReadObject { + + private Object clazz; // Number before resolve + // JavaClass after resolve + /** + * Construct a new JavaObject. + * + * @param classID id of the class object + * @param offset The offset of field data + */ + public JavaObject(long classID, long offset) { + super(offset); + this.clazz = makeId(classID); + } + + public void resolve(Snapshot snapshot) { + if (clazz instanceof JavaClass) { + return; + } + if (clazz instanceof Number) { + long classID = getIdValue((Number)clazz); + clazz = snapshot.findThing(classID); + if (! (clazz instanceof JavaClass)) { + warn("Class " + Long.toHexString(classID) + " not found, " + + "adding fake class!"); + int length; + ReadBuffer buf = snapshot.getReadBuffer(); + int idSize = snapshot.getIdentifierSize(); + long lenOffset = getOffset() + 2*idSize + 4; + try { + length = buf.getInt(lenOffset); + } catch (IOException exp) { + throw new RuntimeException(exp); + } + clazz = snapshot.addFakeInstanceClass(classID, length); + } + } else { + throw new InternalError("should not reach here"); + } + + JavaClass cl = (JavaClass) clazz; + cl.resolve(snapshot); + + // while resolving, parse fields in verbose mode. + // but, getFields calls parseFields in non-verbose mode + // to avoid printing warnings repeatedly. + parseFields(getValue(), true); + + cl.addInstance(this); + super.resolve(snapshot); + } + + /** + * Are we the same type as other? We are iff our clazz is the + * same type as other's. + */ + public boolean isSameTypeAs(JavaThing other) { + if (!(other instanceof JavaObject)) { + return false; + } + JavaObject oo = (JavaObject) other; + return getClazz().equals(oo.getClazz()); + } + + /** + * Return our JavaClass object. This may only be called after resolve. + */ + public JavaClass getClazz() { + return (JavaClass) clazz; + } + + public JavaThing[] getFields() { + // pass false to verbose mode so that dereference + // warnings are not printed. + return parseFields(getValue(), false); + } + + // returns the value of field of given name + public JavaThing getField(String name) { + JavaThing[] flds = getFields(); + JavaField[] instFields = getClazz().getFieldsForInstance(); + for (int i = 0; i < instFields.length; i++) { + if (instFields[i].getName().equals(name)) { + return flds[i]; + } + } + return null; + } + + public int compareTo(JavaThing other) { + if (other instanceof JavaObject) { + JavaObject oo = (JavaObject) other; + return getClazz().getName().compareTo(oo.getClazz().getName()); + } + return super.compareTo(other); + } + + public void visitReferencedObjects(JavaHeapObjectVisitor v) { + super.visitReferencedObjects(v); + JavaThing[] flds = getFields(); + for (int i = 0; i < flds.length; i++) { + if (flds[i] != null) { + if (v.mightExclude() + && v.exclude(getClazz().getClassForField(i), + getClazz().getFieldForInstance(i))) + { + // skip it + } else if (flds[i] instanceof JavaHeapObject) { + v.visit((JavaHeapObject) flds[i]); + } + } + } + } + + public boolean refersOnlyWeaklyTo(Snapshot ss, JavaThing other) { + if (ss.getWeakReferenceClass() != null) { + final int referentFieldIndex = ss.getReferentFieldIndex(); + if (ss.getWeakReferenceClass().isAssignableFrom(getClazz())) { + // + // REMIND: This introduces a dependency on the JDK + // implementation that is undesirable. + JavaThing[] flds = getFields(); + for (int i = 0; i < flds.length; i++) { + if (i != referentFieldIndex && flds[i] == other) { + return false; + } + } + return true; + } + } + return false; + } + + /** + * Describe the reference that this thing has to target. This will only + * be called if target is in the array returned by getChildrenForRootset. + */ + public String describeReferenceTo(JavaThing target, Snapshot ss) { + JavaThing[] flds = getFields(); + for (int i = 0; i < flds.length; i++) { + if (flds[i] == target) { + JavaField f = getClazz().getFieldForInstance(i); + return "field " + f.getName(); + } + } + return super.describeReferenceTo(target, ss); + } + + public String toString() { + if (getClazz().isString()) { + JavaThing value = getField("value"); + if (value instanceof JavaValueArray) { + return ((JavaValueArray)value).valueString(); + } else { + return "null"; + } + } else { + return super.toString(); + } + } + + // Internals only below this point + + /* + * Java instance record (HPROF_GC_INSTANCE_DUMP) looks as below: + * + * object ID + * stack trace serial number (int) + * class ID + * data length (int) + * byte[length] + */ + protected final int readValueLength() throws IOException { + JavaClass cl = getClazz(); + int idSize = cl.getIdentifierSize(); + long lengthOffset = getOffset() + 2*idSize + 4; + return cl.getReadBuffer().getInt(lengthOffset); + } + + protected final byte[] readValue() throws IOException { + JavaClass cl = getClazz(); + int idSize = cl.getIdentifierSize(); + ReadBuffer buf = cl.getReadBuffer(); + long offset = getOffset() + 2*idSize + 4; + int length = buf.getInt(offset); + if (length == 0) { + return Snapshot.EMPTY_BYTE_ARRAY; + } else { + byte[] res = new byte[length]; + buf.get(offset + 4, res); + return res; + } + } + + private JavaThing[] parseFields(byte[] data, boolean verbose) { + JavaClass cl = getClazz(); + int target = cl.getNumFieldsForInstance(); + JavaField[] fields = cl.getFields(); + JavaThing[] fieldValues = new JavaThing[target]; + Snapshot snapshot = cl.getSnapshot(); + int idSize = snapshot.getIdentifierSize(); + int fieldNo = 0; + // In the dump file, the fields are stored in this order: + // fields of most derived class (immediate class) are stored + // first and then the super class and so on. In this object, + // fields are stored in the reverse ("natural") order. i.e., + // fields of most super class are stored first. + + // target variable is used to compensate for the fact that + // the dump file starts field values from the leaf working + // upwards in the inheritance hierarchy, whereas JavaObject + // starts with the top of the inheritance hierarchy and works down. + target -= fields.length; + JavaClass currClass = cl; + int index = 0; + for (int i = 0; i < fieldValues.length; i++, fieldNo++) { + while (fieldNo >= fields.length) { + currClass = currClass.getSuperclass(); + fields = currClass.getFields(); + fieldNo = 0; + target -= fields.length; + } + JavaField f = fields[fieldNo]; + char sig = f.getSignature().charAt(0); + switch (sig) { + case 'L': + case '[': { + long id = objectIdAt(index, data); + index += idSize; + JavaObjectRef ref = new JavaObjectRef(id); + fieldValues[target+fieldNo] = ref.dereference(snapshot, f, verbose); + break; + } + case 'Z': { + byte value = byteAt(index, data); + index++; + fieldValues[target+fieldNo] = new JavaBoolean(value != 0); + break; + } + case 'B': { + byte value = byteAt(index, data); + index++; + fieldValues[target+fieldNo] = new JavaByte(value); + break; + } + case 'S': { + short value = shortAt(index, data); + index += 2; + fieldValues[target+fieldNo] = new JavaShort(value); + break; + } + case 'C': { + char value = charAt(index, data); + index += 2; + fieldValues[target+fieldNo] = new JavaChar(value); + break; + } + case 'I': { + int value = intAt(index, data); + index += 4; + fieldValues[target+fieldNo] = new JavaInt(value); + break; + } + case 'J': { + long value = longAt(index, data); + index += 8; + fieldValues[target+fieldNo] = new JavaLong(value); + break; + } + case 'F': { + float value = floatAt(index, data); + index += 4; + fieldValues[target+fieldNo] = new JavaFloat(value); + break; + } + case 'D': { + double value = doubleAt(index, data); + index += 8; + fieldValues[target+fieldNo] = new JavaDouble(value); + break; + } + default: + throw new RuntimeException("invalid signature: " + sig); + } + } + return fieldValues; + } + + private void warn(String msg) { + System.out.println("WARNING: " + msg); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaObjectArray.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import java.io.IOException; +import jdk.test.lib.hprof.parser.ReadBuffer; + +/** + * @author Bill Foote + */ +public class JavaObjectArray extends JavaLazyReadObject { + + private Object clazz; // Long before resolve, the class after resolve + + public JavaObjectArray(long classID, long offset) { + super(offset); + this.clazz = makeId(classID); + } + + public JavaClass getClazz() { + return (JavaClass) clazz; + } + + public void resolve(Snapshot snapshot) { + if (clazz instanceof JavaClass) { + return; + } + long classID = getIdValue((Number)clazz); + if (snapshot.isNewStyleArrayClass()) { + // Modern heap dumps do this + JavaThing t = snapshot.findThing(classID); + if (t instanceof JavaClass) { + clazz = (JavaClass) t; + } + } + if (!(clazz instanceof JavaClass)) { + JavaThing t = snapshot.findThing(classID); + if (t != null && t instanceof JavaClass) { + JavaClass el = (JavaClass) t; + String nm = el.getName(); + if (!nm.startsWith("[")) { + nm = "L" + el.getName() + ";"; + } + clazz = snapshot.getArrayClass(nm); + } + } + + if (!(clazz instanceof JavaClass)) { + clazz = snapshot.getOtherArrayType(); + } + ((JavaClass)clazz).addInstance(this); + super.resolve(snapshot); + } + + public JavaThing[] getValues() { + return getElements(); + } + + public JavaThing[] getElements() { + Snapshot snapshot = getClazz().getSnapshot(); + byte[] data = getValue(); + final int idSize = snapshot.getIdentifierSize(); + final int numElements = data.length / idSize; + JavaThing[] elements = new JavaThing[numElements]; + int index = 0; + for (int i = 0; i < elements.length; i++) { + long id = objectIdAt(index, data); + index += idSize; + elements[i] = snapshot.findThing(id); + } + return elements; + } + + public int compareTo(JavaThing other) { + if (other instanceof JavaObjectArray) { + return 0; + } + return super.compareTo(other); + } + + public int getLength() { + return getValueLength() / getClazz().getIdentifierSize(); + } + + public void visitReferencedObjects(JavaHeapObjectVisitor v) { + super.visitReferencedObjects(v); + JavaThing[] elements = getElements(); + for (int i = 0; i < elements.length; i++) { + if (elements[i] != null && elements[i] instanceof JavaHeapObject) { + v.visit((JavaHeapObject) elements[i]); + } + } + } + + /** + * Describe the reference that this thing has to target. This will only + * be called if target is in the array returned by getChildrenForRootset. + */ + public String describeReferenceTo(JavaThing target, Snapshot ss) { + JavaThing[] elements = getElements(); + for (int i = 0; i < elements.length; i++) { + if (elements[i] == target) { + return "Element " + i + " of " + this; + } + } + return super.describeReferenceTo(target, ss); + } + + /* + * Java object array record (HPROF_GC_OBJ_ARRAY_DUMP) + * looks as below: + * + * object ID + * stack trace serial number (int) + * array length (int) + * array class ID + * array element IDs + */ + protected final int readValueLength() throws IOException { + JavaClass cl = getClazz(); + ReadBuffer buf = cl.getReadBuffer(); + int idSize = cl.getIdentifierSize(); + long offset = getOffset() + idSize + 4; + int len = buf.getInt(offset); + return len * cl.getIdentifierSize(); + } + + protected final byte[] readValue() throws IOException { + JavaClass cl = getClazz(); + ReadBuffer buf = cl.getReadBuffer(); + int idSize = cl.getIdentifierSize(); + long offset = getOffset() + idSize + 4; + int len = buf.getInt(offset); + if (len == 0) { + return Snapshot.EMPTY_BYTE_ARRAY; + } else { + byte[] res = new byte[len * idSize]; + buf.get(offset + 4 + idSize, res); + return res; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaObjectRef.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import jdk.test.lib.hprof.util.Misc; + +/** + * A forward reference to an object. This is an intermediate representation + * for a JavaThing, when we have the thing's ID, but we might not have read + * the thing yet. + * + * @author Bill Foote + */ +public class JavaObjectRef extends JavaThing { + private long id; + + public JavaObjectRef(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public boolean isHeapAllocated() { + return true; + } + + public JavaThing dereference(Snapshot snapshot, JavaField field) { + return dereference(snapshot, field, true); + } + + public JavaThing dereference(Snapshot snapshot, JavaField field, boolean verbose) { + if (field != null && !field.hasId()) { + // If this happens, we must be a field that represents an int. + // (This only happens with .bod-style files) + return new JavaLong(id); + } + if (id == 0) { + return snapshot.getNullThing(); + } + JavaThing result = snapshot.findThing(id); + if (result == null) { + if (!snapshot.getUnresolvedObjectsOK() && verbose) { + String msg = "WARNING: Failed to resolve object id " + + Misc.toHex(id); + if (field != null) { + msg += " for field " + field.getName() + + " (signature " + field.getSignature() + ")"; + } + System.out.println(msg); + // Thread.dumpStack(); + } + result = new HackJavaValue("Unresolved object " + + Misc.toHex(id), 0); + } + return result; + } + + public int getSize() { + return 0; + } + + public String toString() { + return "Unresolved object " + Misc.toHex(id); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaShort.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Represents a short (i.e. a short field in an instance). + * + * @author Bill Foote + */ + + +public class JavaShort extends JavaValue { + + short value; + + public JavaShort(short value) { + this.value = value; + } + + public String toString() { + return "" + value; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaStatic.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * + * @author Bill Foote + */ + +/** + * Represents the value of a static field of a JavaClass + */ + +public class JavaStatic { + + private JavaField field; + private JavaThing value; + + public JavaStatic(JavaField field, JavaThing value) { + this.field = field; + this.value = value; + } + + public void resolve(JavaClass clazz, Snapshot snapshot) { + long id = -1; + if (value instanceof JavaObjectRef) { + id = ((JavaObjectRef)value).getId(); + } + value = value.dereference(snapshot, field); + if (value.isHeapAllocated() && + clazz.getLoader() == snapshot.getNullThing()) { + // static fields are only roots if they are in classes + // loaded by the root classloader. + JavaHeapObject ho = (JavaHeapObject) value; + String s = "Static reference from " + clazz.getName() + + "." + field.getName(); + snapshot.addRoot(new Root(id, clazz.getId(), + Root.JAVA_STATIC, s)); + } + } + + public JavaField getField() { + return field; + } + + public JavaThing getValue() { + return value; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaThing.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import java.util.Enumeration; +import java.util.Hashtable; + + +/** + * + * @author Bill Foote + */ + + +/** + * Represents a java "Thing". A thing is anything that can be the value of + * a field. This includes JavaHeapObject, JavaObjectRef, and JavaValue. + */ + +public abstract class JavaThing { + + protected JavaThing() { + } + + /** + * If this is a forward reference, figure out what it really + * refers to. + * + * @param snapshot The snapshot this is for + * @param field The field this thing represents. If null, it is + * assumed this thing is an object (and never a value). + */ + public JavaThing dereference(Snapshot shapshot, JavaField field) { + return this; + } + + + /** + * Are we the same type as other? + * + * @see JavaObject.isSameTypeAs() + */ + public boolean isSameTypeAs(JavaThing other) { + return getClass() == other.getClass(); + } + /** + * @return true iff this represents a heap-allocated object + */ + abstract public boolean isHeapAllocated(); + + /** + * @return the size of this object, in bytes, including VM overhead + */ + abstract public int getSize(); + + /** + * @return a human-readable string representation of this thing + */ + abstract public String toString(); + + /** + * Compare our string representation to other's + * @see java.lang.String.compareTo() + */ + public int compareTo(JavaThing other) { + return toString().compareTo(other.toString()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaValue.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Abstract base class for all value types (ints, longs, floats, etc.) + * + * @author Bill Foote + */ + + + + +public abstract class JavaValue extends JavaThing { + + protected JavaValue() { + } + + public boolean isHeapAllocated() { + return false; + } + + abstract public String toString(); + + public int getSize() { + // The size of a value is already accounted for in the class + // that has the data member. + return 0; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/JavaValueArray.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,433 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import jdk.test.lib.hprof.parser.ReadBuffer; +import java.io.IOException; + +/** + * An array of values, that is, an array of ints, boolean, floats or the like. + * + * @author Bill Foote + */ +public class JavaValueArray extends JavaLazyReadObject + /*imports*/ implements ArrayTypeCodes { + + private static String arrayTypeName(byte sig) { + switch (sig) { + case 'B': + return "byte[]"; + case 'Z': + return "boolean[]"; + case 'C': + return "char[]"; + case 'S': + return "short[]"; + case 'I': + return "int[]"; + case 'F': + return "float[]"; + case 'J': + return "long[]"; + case 'D': + return "double[]"; + default: + throw new RuntimeException("invalid array element sig: " + sig); + } + } + + private static int elementSize(byte type) { + switch (type) { + case T_BYTE: + case T_BOOLEAN: + return 1; + case T_CHAR: + case T_SHORT: + return 2; + case T_INT: + case T_FLOAT: + return 4; + case T_LONG: + case T_DOUBLE: + return 8; + default: + throw new RuntimeException("invalid array element type: " + type); + } + } + + /* + * Java primitive array record (HPROF_GC_PRIM_ARRAY_DUMP) looks + * as below: + * + * object ID + * stack trace serial number (int) + * length of the instance data (int) + * element type (byte) + * array data + */ + protected final int readValueLength() throws IOException { + JavaClass cl = getClazz(); + ReadBuffer buf = cl.getReadBuffer(); + int idSize = cl.getIdentifierSize(); + long offset = getOffset() + idSize + 4; + // length of the array + int len = buf.getInt(offset); + // typecode of array element type + byte type = buf.getByte(offset + 4); + return len * elementSize(type); + } + + protected final byte[] readValue() throws IOException { + JavaClass cl = getClazz(); + ReadBuffer buf = cl.getReadBuffer(); + int idSize = cl.getIdentifierSize(); + long offset = getOffset() + idSize + 4; + // length of the array + int length = buf.getInt(offset); + // typecode of array element type + byte type = buf.getByte(offset + 4); + if (length == 0) { + return Snapshot.EMPTY_BYTE_ARRAY; + } else { + length *= elementSize(type); + byte[] res = new byte[length]; + buf.get(offset + 5, res); + return res; + } + } + + // JavaClass set only after resolve. + private JavaClass clazz; + + // This field contains elementSignature byte and + // divider to be used to calculate length. Note that + // length of content byte[] is not same as array length. + // Actual array length is (byte[].length / divider) + private int data; + + // First 8 bits of data is used for element signature + private static final int SIGNATURE_MASK = 0x0FF; + + // Next 8 bits of data is used for length divider + private static final int LENGTH_DIVIDER_MASK = 0x0FF00; + + // Number of bits to shift to get length divider + private static final int LENGTH_DIVIDER_SHIFT = 8; + + public JavaValueArray(byte elementSignature, long offset) { + super(offset); + this.data = (elementSignature & SIGNATURE_MASK); + } + + public JavaClass getClazz() { + return clazz; + } + + public void visitReferencedObjects(JavaHeapObjectVisitor v) { + super.visitReferencedObjects(v); + } + + public void resolve(Snapshot snapshot) { + if (clazz instanceof JavaClass) { + return; + } + byte elementSig = getElementType(); + clazz = snapshot.findClass(arrayTypeName(elementSig)); + if (clazz == null) { + clazz = snapshot.getArrayClass("" + ((char) elementSig)); + } + getClazz().addInstance(this); + super.resolve(snapshot); + } + + public int getLength() { + int divider = (data & LENGTH_DIVIDER_MASK) >>> LENGTH_DIVIDER_SHIFT; + if (divider == 0) { + byte elementSignature = getElementType(); + switch (elementSignature) { + case 'B': + case 'Z': + divider = 1; + break; + case 'C': + case 'S': + divider = 2; + break; + case 'I': + case 'F': + divider = 4; + break; + case 'J': + case 'D': + divider = 8; + break; + default: + throw new RuntimeException("unknown primitive type: " + + elementSignature); + } + data |= (divider << LENGTH_DIVIDER_SHIFT); + } + return (getValueLength() / divider); + } + + public Object getElements() { + final int len = getLength(); + final byte et = getElementType(); + byte[] data = getValue(); + int index = 0; + switch (et) { + case 'Z': { + boolean[] res = new boolean[len]; + for (int i = 0; i < len; i++) { + res[i] = booleanAt(index, data); + index++; + } + return res; + } + case 'B': { + byte[] res = new byte[len]; + for (int i = 0; i < len; i++) { + res[i] = byteAt(index, data); + index++; + } + return res; + } + case 'C': { + char[] res = new char[len]; + for (int i = 0; i < len; i++) { + res[i] = charAt(index, data); + index += 2; + } + return res; + } + case 'S': { + short[] res = new short[len]; + for (int i = 0; i < len; i++) { + res[i] = shortAt(index, data); + index += 2; + } + return res; + } + case 'I': { + int[] res = new int[len]; + for (int i = 0; i < len; i++) { + res[i] = intAt(index, data); + index += 4; + } + return res; + } + case 'J': { + long[] res = new long[len]; + for (int i = 0; i < len; i++) { + res[i] = longAt(index, data); + index += 8; + } + return res; + } + case 'F': { + float[] res = new float[len]; + for (int i = 0; i < len; i++) { + res[i] = floatAt(index, data); + index += 4; + } + return res; + } + case 'D': { + double[] res = new double[len]; + for (int i = 0; i < len; i++) { + res[i] = doubleAt(index, data); + index += 8; + } + return res; + } + default: { + throw new RuntimeException("unknown primitive type?"); + } + } + } + + public byte getElementType() { + return (byte) (data & SIGNATURE_MASK); + } + + private void checkIndex(int index) { + if (index < 0 || index >= getLength()) { + throw new ArrayIndexOutOfBoundsException(index); + } + } + + private void requireType(char type) { + if (getElementType() != type) { + throw new RuntimeException("not of type : " + type); + } + } + + public boolean getBooleanAt(int index) { + checkIndex(index); + requireType('Z'); + return booleanAt(index, getValue()); + } + + public byte getByteAt(int index) { + checkIndex(index); + requireType('B'); + return byteAt(index, getValue()); + } + + public char getCharAt(int index) { + checkIndex(index); + requireType('C'); + return charAt(index << 1, getValue()); + } + + public short getShortAt(int index) { + checkIndex(index); + requireType('S'); + return shortAt(index << 1, getValue()); + } + + public int getIntAt(int index) { + checkIndex(index); + requireType('I'); + return intAt(index << 2, getValue()); + } + + public long getLongAt(int index) { + checkIndex(index); + requireType('J'); + return longAt(index << 3, getValue()); + } + + public float getFloatAt(int index) { + checkIndex(index); + requireType('F'); + return floatAt(index << 2, getValue()); + } + + public double getDoubleAt(int index) { + checkIndex(index); + requireType('D'); + return doubleAt(index << 3, getValue()); + } + + public String valueString() { + return valueString(true); + } + + public String valueString(boolean bigLimit) { + // Char arrays deserve special treatment + StringBuilder result; + byte[] value = getValue(); + int max = value.length; + byte elementSignature = getElementType(); + if (elementSignature == 'C') { + result = new StringBuilder(); + for (int i = 0; i < value.length; ) { + char val = charAt(i, value); + result.append(val); + i += 2; + } + } else { + int limit = 8; + if (bigLimit) { + limit = 1000; + } + result = new StringBuilder("{"); + int num = 0; + for (int i = 0; i < value.length; ) { + if (num > 0) { + result.append(", "); + } + if (num >= limit) { + result.append("... "); + break; + } + num++; + switch (elementSignature) { + case 'Z': { + boolean val = booleanAt(i, value); + if (val) { + result.append("true"); + } else { + result.append("false"); + } + i++; + break; + } + case 'B': { + int val = 0xFF & byteAt(i, value); + result.append("0x").append(Integer.toString(val, 16)); + i++; + break; + } + case 'S': { + short val = shortAt(i, value); + i += 2; + result.append(val); + break; + } + case 'I': { + int val = intAt(i, value); + i += 4; + result.append(val); + break; + } + case 'J': { // long + long val = longAt(i, value); + result.append(val); + i += 8; + break; + } + case 'F': { + float val = floatAt(i, value); + result.append(val); + i += 4; + break; + } + case 'D': { // double + double val = doubleAt(i, value); + result.append(val); + i += 8; + break; + } + default: { + throw new RuntimeException("unknown primitive type?"); + } + } + } + result.append('}'); + } + return result.toString(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/ReachableExcludes.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + + +/** + * This represents a set of data members that should be excluded from the + * reachable objects query. This is useful to exclude observers from the + * transitive closure of objects reachable from a given object, allowing + * some kind of real determination of the "size" of that object. + * + */ + +public interface ReachableExcludes { + /** + * @return true iff the given field is on the hitlist of excluded + * fields. + */ + public boolean isExcluded(String fieldName); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/ReachableExcludesImpl.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.BufferedReader; +import java.io.IOException; + +import java.util.Hashtable; + +/** + * This represents a set of data members that should be excluded from the + * reachable objects query. + * This is useful to exclude observers from the + * transitive closure of objects reachable from a given object, allowing + * some kind of real determination of the "size" of that object. + * + * @author Bill Foote + */ +public class ReachableExcludesImpl implements ReachableExcludes { + + private File excludesFile; + private long lastModified; + private Hashtable<String, String> methods; // Used as a bag + + /** + * Create a new ReachableExcludesImpl over the given file. The file will be + * re-read whenever the timestamp changes. + */ + public ReachableExcludesImpl(File excludesFile) { + this.excludesFile = excludesFile; + readFile(); + } + + private void readFileIfNeeded() { + if (excludesFile.lastModified() != lastModified) { + synchronized(this) { + if (excludesFile.lastModified() != lastModified) { + readFile(); + } + } + } + } + + private void readFile() { + long lm = excludesFile.lastModified(); + Hashtable<String, String> m = new Hashtable<String, String>(); + + try (BufferedReader r = new BufferedReader(new InputStreamReader( + new FileInputStream(excludesFile)))) { + String method; + while ((method = r.readLine()) != null) { + m.put(method, method); + } + lastModified = lm; + methods = m; // We want this to be atomic + } catch (IOException ex) { + System.out.println("Error reading " + excludesFile + ": " + ex); + } + } + + /** + * @return true iff the given field is on the histlist of excluded + * fields. + */ + public boolean isExcluded(String fieldName) { + readFileIfNeeded(); + return methods.get(fieldName) != null; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/ReachableObjects.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import java.util.Vector; +import java.util.Hashtable; +import java.util.Enumeration; + +import jdk.test.lib.hprof.util.ArraySorter; +import jdk.test.lib.hprof.util.Comparer; + +/** + * @author A. Sundararajan + */ + +public class ReachableObjects { + public ReachableObjects(JavaHeapObject root, + final ReachableExcludes excludes) { + this.root = root; + + final Hashtable<JavaHeapObject, JavaHeapObject> bag = new Hashtable<JavaHeapObject, JavaHeapObject>(); + final Hashtable<String, String> fieldsExcluded = new Hashtable<String, String>(); //Bag<String> + final Hashtable<String, String> fieldsUsed = new Hashtable<String, String>(); // Bag<String> + JavaHeapObjectVisitor visitor = new AbstractJavaHeapObjectVisitor() { + public void visit(JavaHeapObject t) { + // Size is zero for things like integer fields + if (t != null && t.getSize() > 0 && bag.get(t) == null) { + bag.put(t, t); + t.visitReferencedObjects(this); + } + } + + public boolean mightExclude() { + return excludes != null; + } + + public boolean exclude(JavaClass clazz, JavaField f) { + if (excludes == null) { + return false; + } + String nm = clazz.getName() + "." + f.getName(); + if (excludes.isExcluded(nm)) { + fieldsExcluded.put(nm, nm); + return true; + } else { + fieldsUsed.put(nm, nm); + return false; + } + } + }; + // Put the closure of root and all objects reachable from root into + // bag (depth first), but don't include root: + visitor.visit(root); + bag.remove(root); + + // Now grab the elements into a vector, and sort it in decreasing size + JavaThing[] things = new JavaThing[bag.size()]; + int i = 0; + for (Enumeration<JavaHeapObject> e = bag.elements(); e.hasMoreElements(); ) { + things[i++] = (JavaThing) e.nextElement(); + } + ArraySorter.sort(things, new Comparer() { + public int compare(Object lhs, Object rhs) { + JavaThing left = (JavaThing) lhs; + JavaThing right = (JavaThing) rhs; + int diff = right.getSize() - left.getSize(); + if (diff != 0) { + return diff; + } + return left.compareTo(right); + } + }); + this.reachables = things; + + this.totalSize = root.getSize(); + for (i = 0; i < things.length; i++) { + this.totalSize += things[i].getSize(); + } + + excludedFields = getElements(fieldsExcluded); + usedFields = getElements(fieldsUsed); + } + + public JavaHeapObject getRoot() { + return root; + } + + public JavaThing[] getReachables() { + return reachables; + } + + public long getTotalSize() { + return totalSize; + } + + public String[] getExcludedFields() { + return excludedFields; + } + + public String[] getUsedFields() { + return usedFields; + } + + private String[] getElements(Hashtable<?, ?> ht) { + Object[] keys = ht.keySet().toArray(); + int len = keys.length; + String[] res = new String[len]; + System.arraycopy(keys, 0, res, 0, len); + ArraySorter.sortArrayOfStrings(res); + return res; + } + + private JavaHeapObject root; + private JavaThing[] reachables; + private String[] excludedFields; + private String[] usedFields; + private long totalSize; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/ReferenceChain.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * Represents a chain of references to some target object + * + * @author Bill Foote + */ + +public class ReferenceChain { + + JavaHeapObject obj; // Object referred to + ReferenceChain next; // Next in chain + + public ReferenceChain(JavaHeapObject obj, ReferenceChain next) { + this.obj = obj; + this.next = next; + } + + public JavaHeapObject getObj() { + return obj; + } + + public ReferenceChain getNext() { + return next; + } + + public int getDepth() { + int count = 1; + ReferenceChain tmp = next; + while (tmp != null) { + count++; + tmp = tmp.next; + } + return count; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/Root.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import jdk.test.lib.hprof.util.Misc; + +/** + * + * @author Bill Foote + */ + + +/** + * Represents a member of the rootset, that is, one of the objects that + * the GC starts from when marking reachable objects. + */ + +public class Root { + + private long id; // ID of the JavaThing we refer to + private long refererId; // Thread or Class responsible for this, or 0 + private int index = -1; // Index in Snapshot.roots + private int type; + private String description; + private JavaHeapObject referer = null; + private StackTrace stackTrace = null; + + // Values for type. Higher values are more interesting -- see getType(). + // See also getTypeName() + public final static int INVALID_TYPE = 0; + public final static int UNKNOWN = 1; + public final static int SYSTEM_CLASS = 2; + + public final static int NATIVE_LOCAL = 3; + public final static int NATIVE_STATIC = 4; + public final static int THREAD_BLOCK = 5; + public final static int BUSY_MONITOR = 6; + public final static int JAVA_LOCAL = 7; + public final static int NATIVE_STACK = 8; + public final static int JAVA_STATIC = 9; + + + public Root(long id, long refererId, int type, String description) { + this(id, refererId, type, description, null); + } + + + public Root(long id, long refererId, int type, String description, + StackTrace stackTrace) { + this.id = id; + this.refererId = refererId; + this.type = type; + this.description = description; + this.stackTrace = stackTrace; + } + + public long getId() { + return id; + } + + public String getIdString() { + return Misc.toHex(id); + } + + public String getDescription() { + if ("".equals(description)) { + return getTypeName() + " Reference"; + } else { + return description; + } + } + + /** + * Return type. We guarantee that more interesting roots will have + * a type that is numerically higher. + */ + public int getType() { + return type; + } + + public String getTypeName() { + switch(type) { + case INVALID_TYPE: return "Invalid (?!?)"; + case UNKNOWN: return "Unknown"; + case SYSTEM_CLASS: return "System Class"; + case NATIVE_LOCAL: return "JNI Local"; + case NATIVE_STATIC: return "JNI Global"; + case THREAD_BLOCK: return "Thread Block"; + case BUSY_MONITOR: return "Busy Monitor"; + case JAVA_LOCAL: return "Java Local"; + case NATIVE_STACK: return "Native Stack (possibly Java local)"; + case JAVA_STATIC: return "Java Static"; + default: return "??"; + } + } + + /** + * Given two Root instances, return the one that is most interesting. + */ + public Root mostInteresting(Root other) { + if (other.type > this.type) { + return other; + } else { + return this; + } + } + + /** + * Get the object that's responsible for this root, if there is one. + * This will be null, a Thread object, or a Class object. + */ + public JavaHeapObject getReferer() { + return referer; + } + + /** + * @return the stack trace responsible for this root, or null if there + * is none. + */ + public StackTrace getStackTrace() { + return stackTrace; + } + + /** + * @return The index of this root in Snapshot.roots + */ + public int getIndex() { + return index; + } + + void resolve(Snapshot ss) { + if (refererId != 0) { + referer = ss.findThing(refererId); + } + if (stackTrace != null) { + stackTrace.resolve(ss); + } + } + + void setIndex(int i) { + index = i; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/Snapshot.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,635 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +import java.lang.ref.SoftReference; +import java.util.*; + +import jdk.test.lib.hprof.parser.ReadBuffer; +import jdk.test.lib.hprof.util.Misc; + +/** + * + * @author Bill Foote + */ + +/** + * Represents a snapshot of the Java objects in the VM at one instant. + * This is the top-level "model" object read out of a single .hprof or .bod + * file. + */ + +public class Snapshot implements AutoCloseable { + + public static final long SMALL_ID_MASK = 0x0FFFFFFFFL; + public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + private static final JavaField[] EMPTY_FIELD_ARRAY = new JavaField[0]; + private static final JavaStatic[] EMPTY_STATIC_ARRAY = new JavaStatic[0]; + + // all heap objects + private Hashtable<Number, JavaHeapObject> heapObjects = + new Hashtable<Number, JavaHeapObject>(); + + private Hashtable<Number, JavaClass> fakeClasses = + new Hashtable<Number, JavaClass>(); + + // all Roots in this Snapshot + private Vector<Root> roots = new Vector<Root>(); + + // name-to-class map + private Map<String, JavaClass> classes = + new TreeMap<String, JavaClass>(); + + // new objects relative to a baseline - lazily initialized + private volatile Map<JavaHeapObject, Boolean> newObjects; + + // allocation site traces for all objects - lazily initialized + private volatile Map<JavaHeapObject, StackTrace> siteTraces; + + // object-to-Root map for all objects + private Map<JavaHeapObject, Root> rootsMap = + new HashMap<JavaHeapObject, Root>(); + + // soft cache of finalizeable objects - lazily initialized + private SoftReference<Vector<?>> finalizablesCache; + + // represents null reference + private JavaThing nullThing; + + // java.lang.ref.Reference class + private JavaClass weakReferenceClass; + // index of 'referent' field in java.lang.ref.Reference class + private int referentFieldIndex; + + // java.lang.Class class + private JavaClass javaLangClass; + // java.lang.String class + private JavaClass javaLangString; + // java.lang.ClassLoader class + private JavaClass javaLangClassLoader; + + // unknown "other" array class + private volatile JavaClass otherArrayType; + // Stuff to exclude from reachable query + private ReachableExcludes reachableExcludes; + // the underlying heap dump buffer + private ReadBuffer readBuf; + + // True iff some heap objects have isNew set + private boolean hasNewSet; + private boolean unresolvedObjectsOK; + + // whether object array instances have new style class or + // old style (element) class. + private boolean newStyleArrayClass; + + // object id size in the heap dump + private int identifierSize = 4; + + // minimum object size - accounts for object header in + // most Java virtual machines - we assume 2 identifierSize + // (which is true for Sun's hotspot JVM). + private int minimumObjectSize; + + public Snapshot(ReadBuffer buf) { + nullThing = new HackJavaValue("<null>", 0); + readBuf = buf; + } + + public void setSiteTrace(JavaHeapObject obj, StackTrace trace) { + if (trace != null && trace.getFrames().length != 0) { + initSiteTraces(); + siteTraces.put(obj, trace); + } + } + + public StackTrace getSiteTrace(JavaHeapObject obj) { + if (siteTraces != null) { + return siteTraces.get(obj); + } else { + return null; + } + } + + public void setNewStyleArrayClass(boolean value) { + newStyleArrayClass = value; + } + + public boolean isNewStyleArrayClass() { + return newStyleArrayClass; + } + + public void setIdentifierSize(int size) { + identifierSize = size; + minimumObjectSize = 2 * size; + } + + public int getIdentifierSize() { + return identifierSize; + } + + public int getMinimumObjectSize() { + return minimumObjectSize; + } + + public void addHeapObject(long id, JavaHeapObject ho) { + heapObjects.put(makeId(id), ho); + } + + public void addRoot(Root r) { + r.setIndex(roots.size()); + roots.addElement(r); + } + + public void addClass(long id, JavaClass c) { + addHeapObject(id, c); + putInClassesMap(c); + } + + JavaClass addFakeInstanceClass(long classID, int instSize) { + // Create a fake class name based on ID. + String name = "unknown-class<@" + Misc.toHex(classID) + ">"; + + // Create fake fields convering the given instance size. + // Create as many as int type fields and for the left over + // size create byte type fields. + int numInts = instSize / 4; + int numBytes = instSize % 4; + JavaField[] fields = new JavaField[numInts + numBytes]; + int i; + for (i = 0; i < numInts; i++) { + fields[i] = new JavaField("unknown-field-" + i, "I"); + } + for (i = 0; i < numBytes; i++) { + fields[i + numInts] = new JavaField("unknown-field-" + + i + numInts, "B"); + } + + // Create fake instance class + JavaClass c = new JavaClass(name, 0, 0, 0, 0, fields, + EMPTY_STATIC_ARRAY, instSize); + // Add the class + addFakeClass(makeId(classID), c); + return c; + } + + + /** + * @return true iff it's possible that some JavaThing instances might + * isNew set + * + * @see JavaThing.isNew() + */ + public boolean getHasNewSet() { + return hasNewSet; + } + + // + // Used in the body of resolve() + // + private static class MyVisitor extends AbstractJavaHeapObjectVisitor { + JavaHeapObject t; + public void visit(JavaHeapObject other) { + other.addReferenceFrom(t); + } + } + + // To show heap parsing progress, we print a '.' after this limit + private static final int DOT_LIMIT = 5000; + + /** + * Called after reading complete, to initialize the structure + */ + public void resolve(boolean calculateRefs) { + System.out.println("Resolving " + heapObjects.size() + " objects..."); + + // First, resolve the classes. All classes must be resolved before + // we try any objects, because the objects use classes in their + // resolution. + javaLangClass = findClass("java.lang.Class"); + if (javaLangClass == null) { + System.out.println("WARNING: hprof file does not include java.lang.Class!"); + javaLangClass = new JavaClass("java.lang.Class", 0, 0, 0, 0, + EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0); + addFakeClass(javaLangClass); + } + javaLangString = findClass("java.lang.String"); + if (javaLangString == null) { + System.out.println("WARNING: hprof file does not include java.lang.String!"); + javaLangString = new JavaClass("java.lang.String", 0, 0, 0, 0, + EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0); + addFakeClass(javaLangString); + } + javaLangClassLoader = findClass("java.lang.ClassLoader"); + if (javaLangClassLoader == null) { + System.out.println("WARNING: hprof file does not include java.lang.ClassLoader!"); + javaLangClassLoader = new JavaClass("java.lang.ClassLoader", 0, 0, 0, 0, + EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0); + addFakeClass(javaLangClassLoader); + } + + for (JavaHeapObject t : heapObjects.values()) { + if (t instanceof JavaClass) { + t.resolve(this); + } + } + + // Now, resolve everything else. + for (JavaHeapObject t : heapObjects.values()) { + if (!(t instanceof JavaClass)) { + t.resolve(this); + } + } + + heapObjects.putAll(fakeClasses); + fakeClasses.clear(); + + weakReferenceClass = findClass("java.lang.ref.Reference"); + referentFieldIndex = 0; + if (weakReferenceClass != null) { + JavaField[] fields = weakReferenceClass.getFieldsForInstance(); + for (int i = 0; i < fields.length; i++) { + if ("referent".equals(fields[i].getName())) { + referentFieldIndex = i; + break; + } + } + } + + if (calculateRefs) { + calculateReferencesToObjects(); + System.out.print("Eliminating duplicate references"); + System.out.flush(); + // This println refers to the *next* step + } + int count = 0; + for (JavaHeapObject t : heapObjects.values()) { + t.setupReferers(); + ++count; + if (calculateRefs && count % DOT_LIMIT == 0) { + System.out.print("."); + System.out.flush(); + } + } + if (calculateRefs) { + System.out.println(""); + } + + // to ensure that Iterator.remove() on getClasses() + // result will throw exception.. + classes = Collections.unmodifiableMap(classes); + } + + private void calculateReferencesToObjects() { + System.out.print("Chasing references, expect " + + (heapObjects.size() / DOT_LIMIT) + " dots"); + System.out.flush(); + int count = 0; + MyVisitor visitor = new MyVisitor(); + for (JavaHeapObject t : heapObjects.values()) { + visitor.t = t; + // call addReferenceFrom(t) on all objects t references: + t.visitReferencedObjects(visitor); + ++count; + if (count % DOT_LIMIT == 0) { + System.out.print("."); + System.out.flush(); + } + } + System.out.println(); + for (Root r : roots) { + r.resolve(this); + JavaHeapObject t = findThing(r.getId()); + if (t != null) { + t.addReferenceFromRoot(r); + } + } + } + + public void markNewRelativeTo(Snapshot baseline) { + hasNewSet = true; + for (JavaHeapObject t : heapObjects.values()) { + boolean isNew; + long thingID = t.getId(); + if (thingID == 0L || thingID == -1L) { + isNew = false; + } else { + JavaThing other = baseline.findThing(t.getId()); + if (other == null) { + isNew = true; + } else { + isNew = !t.isSameTypeAs(other); + } + } + t.setNew(isNew); + } + } + + public Enumeration<JavaHeapObject> getThings() { + return heapObjects.elements(); + } + + + public JavaHeapObject findThing(long id) { + Number idObj = makeId(id); + JavaHeapObject jho = heapObjects.get(idObj); + return jho != null? jho : fakeClasses.get(idObj); + } + + public JavaHeapObject findThing(String id) { + return findThing(Misc.parseHex(id)); + } + + public JavaClass findClass(String name) { + if (name.startsWith("0x")) { + return (JavaClass) findThing(name); + } else { + return classes.get(name); + } + } + + /** + * Return an Iterator of all of the classes in this snapshot. + **/ + public Iterator<JavaClass> getClasses() { + // note that because classes is a TreeMap + // classes are already sorted by name + return classes.values().iterator(); + } + + public JavaClass[] getClassesArray() { + JavaClass[] res = new JavaClass[classes.size()]; + classes.values().toArray(res); + return res; + } + + public synchronized Enumeration<?> getFinalizerObjects() { + Vector<?> obj; + if (finalizablesCache != null && + (obj = finalizablesCache.get()) != null) { + return obj.elements(); + } + + JavaClass clazz = findClass("java.lang.ref.Finalizer"); + JavaObject queue = (JavaObject) clazz.getStaticField("queue"); + JavaThing tmp = queue.getField("head"); + Vector<JavaHeapObject> finalizables = new Vector<JavaHeapObject>(); + if (tmp != getNullThing()) { + JavaObject head = (JavaObject) tmp; + while (true) { + JavaHeapObject referent = (JavaHeapObject) head.getField("referent"); + JavaThing next = head.getField("next"); + if (next == getNullThing() || next.equals(head)) { + break; + } + head = (JavaObject) next; + finalizables.add(referent); + } + } + finalizablesCache = new SoftReference<Vector<?>>(finalizables); + return finalizables.elements(); + } + + public Enumeration<Root> getRoots() { + return roots.elements(); + } + + public Root[] getRootsArray() { + Root[] res = new Root[roots.size()]; + roots.toArray(res); + return res; + } + + public Root getRootAt(int i) { + return roots.elementAt(i); + } + + public ReferenceChain[] + rootsetReferencesTo(JavaHeapObject target, boolean includeWeak) { + Vector<ReferenceChain> fifo = new Vector<ReferenceChain>(); // This is slow... A real fifo would help + // Must be a fifo to go breadth-first + Hashtable<JavaHeapObject, JavaHeapObject> visited = new Hashtable<JavaHeapObject, JavaHeapObject>(); + // Objects are added here right after being added to fifo. + Vector<ReferenceChain> result = new Vector<ReferenceChain>(); + visited.put(target, target); + fifo.addElement(new ReferenceChain(target, null)); + + while (fifo.size() > 0) { + ReferenceChain chain = fifo.elementAt(0); + fifo.removeElementAt(0); + JavaHeapObject curr = chain.getObj(); + if (curr.getRoot() != null) { + result.addElement(chain); + // Even though curr is in the rootset, we want to explore its + // referers, because they might be more interesting. + } + Enumeration<JavaThing> referers = curr.getReferers(); + while (referers.hasMoreElements()) { + JavaHeapObject t = (JavaHeapObject) referers.nextElement(); + if (t != null && !visited.containsKey(t)) { + if (includeWeak || !t.refersOnlyWeaklyTo(this, curr)) { + visited.put(t, t); + fifo.addElement(new ReferenceChain(t, chain)); + } + } + } + } + + ReferenceChain[] realResult = new ReferenceChain[result.size()]; + for (int i = 0; i < result.size(); i++) { + realResult[i] = result.elementAt(i); + } + return realResult; + } + + public boolean getUnresolvedObjectsOK() { + return unresolvedObjectsOK; + } + + public void setUnresolvedObjectsOK(boolean v) { + unresolvedObjectsOK = v; + } + + public JavaClass getWeakReferenceClass() { + return weakReferenceClass; + } + + public int getReferentFieldIndex() { + return referentFieldIndex; + } + + public JavaThing getNullThing() { + return nullThing; + } + + public void setReachableExcludes(ReachableExcludes e) { + reachableExcludes = e; + } + + public ReachableExcludes getReachableExcludes() { + return reachableExcludes; + } + + // package privates + void addReferenceFromRoot(Root r, JavaHeapObject obj) { + Root root = rootsMap.get(obj); + if (root == null) { + rootsMap.put(obj, r); + } else { + rootsMap.put(obj, root.mostInteresting(r)); + } + } + + Root getRoot(JavaHeapObject obj) { + return rootsMap.get(obj); + } + + JavaClass getJavaLangClass() { + return javaLangClass; + } + + JavaClass getJavaLangString() { + return javaLangString; + } + + JavaClass getJavaLangClassLoader() { + return javaLangClassLoader; + } + + JavaClass getOtherArrayType() { + if (otherArrayType == null) { + synchronized(this) { + if (otherArrayType == null) { + addFakeClass(new JavaClass("[<other>", 0, 0, 0, 0, + EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, + 0)); + otherArrayType = findClass("[<other>"); + } + } + } + return otherArrayType; + } + + JavaClass getArrayClass(String elementSignature) { + JavaClass clazz; + synchronized(classes) { + clazz = findClass("[" + elementSignature); + if (clazz == null) { + clazz = new JavaClass("[" + elementSignature, 0, 0, 0, 0, + EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, 0); + addFakeClass(clazz); + // This is needed because the JDK only creates Class structures + // for array element types, not the arrays themselves. For + // analysis, though, we need to pretend that there's a + // JavaClass for the array type, too. + } + } + return clazz; + } + + ReadBuffer getReadBuffer() { + return readBuf; + } + + void setNew(JavaHeapObject obj, boolean isNew) { + initNewObjects(); + if (isNew) { + newObjects.put(obj, Boolean.TRUE); + } + } + + boolean isNew(JavaHeapObject obj) { + if (newObjects != null) { + return newObjects.get(obj) != null; + } else { + return false; + } + } + + // Internals only below this point + private Number makeId(long id) { + if (identifierSize == 4) { + return (int)id; + } else { + return id; + } + } + + private void putInClassesMap(JavaClass c) { + String name = c.getName(); + if (classes.containsKey(name)) { + // more than one class can have the same name + // if so, create a unique name by appending + // - and id string to it. + name += "-" + c.getIdString(); + } + classes.put(c.getName(), c); + } + + private void addFakeClass(JavaClass c) { + putInClassesMap(c); + c.resolve(this); + } + + private void addFakeClass(Number id, JavaClass c) { + fakeClasses.put(id, c); + addFakeClass(c); + } + + private synchronized void initNewObjects() { + if (newObjects == null) { + synchronized (this) { + if (newObjects == null) { + newObjects = new HashMap<JavaHeapObject, Boolean>(); + } + } + } + } + + private synchronized void initSiteTraces() { + if (siteTraces == null) { + synchronized (this) { + if (siteTraces == null) { + siteTraces = new HashMap<JavaHeapObject, StackTrace>(); + } + } + } + } + + @Override + public void close() throws Exception { + readBuf.close(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/StackFrame.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * + * @author Bill Foote + */ + + +/** + * Represents a stack frame. + */ + +public class StackFrame { + + // + // Values for the lineNumber data member. These are the same + // as the values used in the JDK 1.2 heap dump file. + // + public final static int LINE_NUMBER_UNKNOWN = -1; + public final static int LINE_NUMBER_COMPILED = -2; + public final static int LINE_NUMBER_NATIVE = -3; + + private String methodName; + private String methodSignature; + private String className; + private String sourceFileName; + private int lineNumber; + + public StackFrame(String methodName, String methodSignature, + String className, String sourceFileName, int lineNumber) { + this.methodName = methodName; + this.methodSignature = methodSignature; + this.className = className; + this.sourceFileName = sourceFileName; + this.lineNumber = lineNumber; + } + + public void resolve(Snapshot snapshot) { + } + + public String getMethodName() { + return methodName; + } + + public String getMethodSignature() { + return methodSignature; + } + + public String getClassName() { + return className; + } + + public String getSourceFileName() { + return sourceFileName; + } + + public String getLineNumber() { + switch(lineNumber) { + case LINE_NUMBER_UNKNOWN: + return "(unknown)"; + case LINE_NUMBER_COMPILED: + return "(compiled method)"; + case LINE_NUMBER_NATIVE: + return "(native method)"; + default: + return Integer.toString(lineNumber, 10); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/model/StackTrace.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.model; + +/** + * + * @author Bill Foote + */ + + +/** + * Represents a stack trace, that is, an ordered collection of stack frames. + */ + +public class StackTrace { + + private StackFrame[] frames; + + public StackTrace(StackFrame[] frames) { + this.frames = frames; + } + + /** + * @param depth. The minimum reasonable depth is 1. + * + * @return a (possibly new) StackTrace that is limited to depth. + */ + public StackTrace traceForDepth(int depth) { + if (depth >= frames.length) { + return this; + } else { + StackFrame[] f = new StackFrame[depth]; + System.arraycopy(frames, 0, f, 0, depth); + return new StackTrace(f); + } + } + + public void resolve(Snapshot snapshot) { + for (int i = 0; i < frames.length; i++) { + frames[i].resolve(snapshot); + } + } + + public StackFrame[] getFrames() { + return frames; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/parser/FileReadBuffer.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.parser; + +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * Implementation of ReadBuffer using a RandomAccessFile + * + * @author A. Sundararajan + */ +class FileReadBuffer implements ReadBuffer { + // underlying file to read + private RandomAccessFile file; + + FileReadBuffer(RandomAccessFile file) { + this.file = file; + } + + private void seek(long pos) throws IOException { + file.getChannel().position(pos); + } + + public synchronized void get(long pos, byte[] buf) throws IOException { + seek(pos); + file.read(buf); + } + + public synchronized char getChar(long pos) throws IOException { + seek(pos); + return file.readChar(); + } + + public synchronized byte getByte(long pos) throws IOException { + seek(pos); + return (byte) file.read(); + } + + public synchronized short getShort(long pos) throws IOException { + seek(pos); + return file.readShort(); + } + + public synchronized int getInt(long pos) throws IOException { + seek(pos); + return file.readInt(); + } + + public synchronized long getLong(long pos) throws IOException { + seek(pos); + return file.readLong(); + } + + @Override + public void close() throws Exception { + file.close(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/hprof/parser/HprofReader.java Wed Jul 05 22:09:41 2017 +0200 @@ -0,0 +1,899 @@ +/* + * Copyright (c) 1997, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * The Original Code is HAT. The Initial Developer of the + * Original Code is Bill Foote, with contributions from others + * at JavaSoft/Sun. + */ + +package jdk.test.lib.hprof.parser; + +import java.io.*; +import java.util.Date; +import java.util.Hashtable; +import jdk.test.lib.hprof.model.ArrayTypeCodes; +import jdk.test.lib.hprof.model.*; + +/** + * Object that's used to read a hprof file. + * + * @author Bill Foote + */ + +public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes { + + final static int MAGIC_NUMBER = 0x4a415641; + // That's "JAVA", the first part of "JAVA PROFILE ..." + private final static String[] VERSIONS = { + " PROFILE 1.0\0", + " PROFILE 1.0.1\0", + " PROFILE 1.0.2\0", + }; + + private final static int VERSION_JDK12BETA3 = 0; + private final static int VERSION_JDK12BETA4 = 1; + private final static int VERSION_JDK6 = 2; + // These version numbers are indices into VERSIONS. The instance data + // member version is set to one of these, and it drives decisions when + // reading the file. + // + // Version 1.0.1 added HPROF_GC_PRIM_ARRAY_DUMP, which requires no + // version-sensitive parsing. + // + // Version 1.0.1 changed the type of a constant pool entry from a signature + // to a typecode. + // + // Version 1.0.2 added HPROF_HEAP_DUMP_SEGMENT and HPROF_HEAP_DUMP_END + // to allow a large heap to be dumped as a sequence of heap dump segments. + // + // The HPROF agent in J2SE 1.2 through to 5.0 generate a version 1.0.1 + // file. In Java SE 6.0 the version is either 1.0.1 or 1.0.2 depending on + // the size of the heap (normally it will be 1.0.1 but for multi-GB + // heaps the heap dump will not fit in a HPROF_HEAP_DUMP record so the + // dump is generated as version 1.0.2). + + // + // Record types: + // + static final int HPROF_UTF8 = 0x01; + static final int HPROF_LOAD_CLASS = 0x02; + static final int HPROF_UNLOAD_CLASS = 0x03; + static final int HPROF_FRAME = 0x04; + static final int HPROF_TRACE = 0x05; + static final int HPROF_ALLOC_SITES = 0x06; + static final int HPROF_HEAP_SUMMARY = 0x07; + + static final int HPROF_START_THREAD = 0x0a; + static final int HPROF_END_THREAD = 0x0b; + + static final int HPROF_HEAP_DUMP = 0x0c; + + static final int HPROF_CPU_SAMPLES = 0x0d; + static final int HPROF_CONTROL_SETTINGS = 0x0e; + static final int HPROF_LOCKSTATS_WAIT_TIME = 0x10; + static final int HPROF_LOCKSTATS_HOLD_TIME = 0x11; + + static final int HPROF_GC_ROOT_UNKNOWN = 0xff; + static final int HPROF_GC_ROOT_JNI_GLOBAL = 0x01; + static final int HPROF_GC_ROOT_JNI_LOCAL = 0x02; + static final int HPROF_GC_ROOT_JAVA_FRAME = 0x03; + static final int HPROF_GC_ROOT_NATIVE_STACK = 0x04; + static final int HPROF_GC_ROOT_STICKY_CLASS = 0x05; + static final int HPROF_GC_ROOT_THREAD_BLOCK = 0x06; + static final int HPROF_GC_ROOT_MONITOR_USED = 0x07; + static final int HPROF_GC_ROOT_THREAD_OBJ = 0x08; + + static final int HPROF_GC_CLASS_DUMP = 0x20; + static final int HPROF_GC_INSTANCE_DUMP = 0x21; + static final int HPROF_GC_OBJ_ARRAY_DUMP = 0x22; + static final int HPROF_GC_PRIM_ARRAY_DUMP = 0x23; + + static final int HPROF_HEAP_DUMP_SEGMENT = 0x1c; + static final int HPROF_HEAP_DUMP_END = 0x2c; + + private final static int T_CLASS = 2; + + private int version; // The version of .hprof being read + + private int debugLevel; + private long currPos; // Current position in the file + + private int dumpsToSkip; + private boolean callStack; // If true, read the call stack of objects + + private int identifierSize; // Size, in bytes, of identifiers. + private Hashtable<Long, String> names; + + // Hashtable<Integer, ThreadObject>, used to map the thread sequence number + // (aka "serial number") to the thread object ID for + // HPROF_GC_ROOT_THREAD_OBJ. ThreadObject is a trivial inner class, + // at the end of this file. + private Hashtable<Integer, ThreadObject> threadObjects; + + // Hashtable<Long, String>, maps class object ID to class name + // (with / converted to .) + private Hashtable<Long, String> classNameFromObjectID; + + // Hashtable<Integer, Integer>, maps class serial # to class object ID + private Hashtable<Integer, String> classNameFromSerialNo; + + // Hashtable<Long, StackFrame> maps stack frame ID to StackFrame. + // Null if we're not tracking them. + private Hashtable<Long, StackFrame> stackFrames; + + // Hashtable<Integer, StackTrace> maps stack frame ID to StackTrace + // Null if we're not tracking them. + private Hashtable<Integer, StackTrace> stackTraces; + + private Snapshot snapshot; + + public HprofReader(String fileName, PositionDataInputStream in, + int dumpNumber, boolean callStack, int debugLevel) + throws IOException { + super(in); + RandomAccessFile file = new RandomAccessFile(fileName, "r"); + this.snapshot = new Snapshot(MappedReadBuffer.create(file)); + this.dumpsToSkip = dumpNumber - 1; + this.callStack = callStack; + this.debugLevel = debugLevel; + names = new Hashtable<Long, String>(); + threadObjects = new Hashtable<Integer, ThreadObject>(43); + classNameFromObjectID = new Hashtable<Long, String>(); + if (callStack) { + stackFrames = new Hashtable<Long, StackFrame>(43); + stackTraces = new Hashtable<Integer, StackTrace>(43); + classNameFromSerialNo = new Hashtable<Integer, String>(); + } + } + + public Snapshot read() throws IOException { + currPos = 4; // 4 because of the magic number + version = readVersionHeader(); + identifierSize = in.readInt(); + snapshot.setIdentifierSize(identifierSize); + if (version >= VERSION_JDK12BETA4) { + snapshot.setNewStyleArrayClass(true); + } else { + snapshot.setNewStyleArrayClass(false); + } + + currPos += 4; + if (identifierSize != 4 && identifierSize != 8) { + throw new IOException("I'm sorry, but I can't deal with an identifier size of " + identifierSize + ". I can only deal with 4 or 8."); + } + System.out.println("Dump file created " + (new Date(in.readLong()))); + currPos += 8; + + for (;;) { + int type; + try { + type = in.readUnsignedByte(); + } catch (EOFException ignored) { + break; + } + in.readInt(); // Timestamp of this record + // Length of record: readInt() will return negative value for record + // length >2GB. so store 32bit value in long to keep it unsigned. + long length = in.readInt() & 0xffffffffL; + if (debugLevel > 0) { + System.out.println("Read record type " + type + + ", length " + length + + " at position " + toHex(currPos)); + } + if (length < 0) { + throw new IOException("Bad record length of " + length + + " at byte " + toHex(currPos+5) + + " of file."); + } + currPos += 9 + length; + switch (type) { + case HPROF_UTF8: { + long id = readID(); + byte[] chars = new byte[(int)length - identifierSize]; + in.readFully(chars); + names.put(id, new String(chars)); + break; + } + case HPROF_LOAD_CLASS: { + int serialNo = in.readInt(); // Not used + long classID = readID(); + int stackTraceSerialNo = in.readInt(); + long classNameID = readID(); + Long classIdI = classID; + String nm = getNameFromID(classNameID).replace('/', '.'); + classNameFromObjectID.put(classIdI, nm); + if (classNameFromSerialNo != null) { + classNameFromSerialNo.put(serialNo, nm); + } + break; + } + + case HPROF_HEAP_DUMP: { + if (dumpsToSkip <= 0) { + try { + readHeapDump(length, currPos); + } catch (EOFException exp) { + handleEOF(exp, snapshot); + } + if (debugLevel > 0) { + System.out.println(" Finished processing instances in heap dump."); + } + return snapshot; + } else { + dumpsToSkip--; + skipBytes(length); + } + break; + } + + case HPROF_HEAP_DUMP_END: { + if (version >= VERSION_JDK6) { + if (dumpsToSkip <= 0) { + skipBytes(length); // should be no-op + return snapshot; + } else { + // skip this dump (of the end record for a sequence of dump segments) + dumpsToSkip--; + } + } else { + // HPROF_HEAP_DUMP_END only recognized in >= 1.0.2 + warn("Ignoring unrecognized record type " + type); + } + skipBytes(length); // should be no-op + break; + } + + case HPROF_HEAP_DUMP_SEGMENT: { + if (version >= VERSION_JDK6) { + if (dumpsToSkip <= 0) { + try { + // read the dump segment + readHeapDump(length, currPos); + } catch (EOFException exp) { + handleEOF(exp, snapshot); + } + } else { + // all segments comprising the heap dump will be skipped + skipBytes(length); + } + } else { + // HPROF_HEAP_DUMP_SEGMENT only recognized in >= 1.0.2 + warn("Ignoring unrecognized record type " + type); + skipBytes(length); + } + break; + } + + case HPROF_FRAME: { + if (stackFrames == null) { + skipBytes(length); + } else { + long id = readID(); + String methodName = getNameFromID(readID()); + String methodSig = getNameFromID(readID()); + String sourceFile = getNameFromID(readID()); + int classSer = in.readInt(); + String className = classNameFromSerialNo.get(classSer); + int lineNumber = in.readInt(); + if (lineNumber < StackFrame.LINE_NUMBER_NATIVE) { + warn("Weird stack frame line number: " + lineNumber); + lineNumber = StackFrame.LINE_NUMBER_UNKNOWN; + } + stackFrames.put(id, + new StackFrame(methodName, methodSig, + className, sourceFile, + lineNumber)); + } + break; + } + case HPROF_TRACE: { + if (stackTraces == null) { + skipBytes(length); + } else { + int serialNo = in.readInt(); + int threadSeq = in.readInt(); // Not used + StackFrame[] frames = new StackFrame[in.readInt()]; + for (int i = 0; i < frames.length; i++) { + long fid = readID(); + frames[i] = stackFrames.get(fid); + if (frames[i] == null) { + throw new IOException("Stack frame " + toHex(fid) + " not found"); + } + } + stackTraces.put(serialNo, + new StackTrace(frames)); + } + break; + } +