# HG changeset patch # User ykantser # Date 1429862741 -7200 # Fri Apr 24 10:05:41 2015 +0200 # Node ID e2bbdbbd3fe0c01797cfb138e8fafce3a9859fbc # Parent 5b4071be73882f07aff9b1bf015534c8855e74d0 8059047: Extract parser/validator from jhat for use in tests diff --git a/test/lib/share/classes/jdk/test/lib/hprof/HprofParser.java b/test/lib/share/classes/jdk/test/lib/hprof/HprofParser.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/HprofParser.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, 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.File; +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 { + + /** + * @see #parse(File, boolean, boolean, boolean) + */ + public static void parse(File dump) throws Exception { + parse(dump, false, true, true); + } + + /** + * @see #parse(File, boolean, boolean, boolean) + */ + public static void parseWithDebugInfo(File dump) throws Exception { + 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 + */ + public static void parse(File dump, boolean debug, boolean callStack, boolean calculateRefs) throws Exception { + int debugLevel = debug ? 2 : 0; + Snapshot snapshot = Reader.readFile(dump.getAbsolutePath(), callStack, debugLevel); + + System.out.println("Snapshot read, resolving..."); + snapshot.resolve(calculateRefs); + System.out.println("Snapshot resolved."); + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/README b/test/lib/share/classes/jdk/test/lib/hprof/README new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/README @@ -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 diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/AbstractJavaHeapObjectVisitor.java b/test/lib/share/classes/jdk/test/lib/hprof/model/AbstractJavaHeapObjectVisitor.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/AbstractJavaHeapObjectVisitor.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/ArrayTypeCodes.java b/test/lib/share/classes/jdk/test/lib/hprof/model/ArrayTypeCodes.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/ArrayTypeCodes.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1997, 2015, 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; +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/HackJavaValue.java b/test/lib/share/classes/jdk/test/lib/hprof/model/HackJavaValue.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/HackJavaValue.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaBoolean.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaBoolean.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaBoolean.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaByte.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaByte.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaByte.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 2015, 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); + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaChar.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaChar.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaChar.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaClass.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaClass.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaClass.java @@ -0,0 +1,503 @@ +/* + * Copyright (c) 1997, 2015, 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 instances = new Vector(); + + // 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 getInstances(boolean includeSubclasses) { + if (includeSubclasses) { + Enumeration 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 v = new Vector(); + 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 v) { + if (superclass != null) { + ((JavaClass) superclass).addFields(v); + } + for (int i = 0; i < fields.length; i++) { + v.addElement(fields[i]); + } + } + + private void addSubclassInstances(Vector 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; + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaDouble.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaDouble.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaDouble.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997, 2015, 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); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaField.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaField.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaField.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaFloat.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaFloat.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaFloat.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997, 2015, 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); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaHeapObject.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaHeapObject.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaHeapObject.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1997, 2015, 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 map = new HashMap(); + 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 getReferers() { + if (referersLen != -1) { + throw new RuntimeException("not resolved: " + getIdString()); + } + return new Enumeration() { + + 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaHeapObjectVisitor.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaHeapObjectVisitor.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaHeapObjectVisitor.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1997, 2015, 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(); +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaInt.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaInt.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaInt.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaLazyReadObject.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaLazyReadObject.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaLazyReadObject.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 1997, 2015, 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); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaLong.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaLong.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaLong.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997, 2015, 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); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaObject.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaObject.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaObject.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) 1997, 2015, 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); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaObjectArray.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaObjectArray.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaObjectArray.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaObjectRef.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaObjectRef.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaObjectRef.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1997, 2015, 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); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaShort.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaShort.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaShort.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaStatic.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaStatic.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaStatic.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1997, 2015, 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; + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaThing.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaThing.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaThing.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1997, 2015, 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()); + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaValue.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaValue.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaValue.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/JavaValueArray.java b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaValueArray.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/JavaValueArray.java @@ -0,0 +1,433 @@ +/* + * Copyright (c) 1997, 2015, 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(); + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/ReachableExcludes.java b/test/lib/share/classes/jdk/test/lib/hprof/model/ReachableExcludes.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/ReachableExcludes.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1997, 2015, 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); +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/ReachableExcludesImpl.java b/test/lib/share/classes/jdk/test/lib/hprof/model/ReachableExcludesImpl.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/ReachableExcludesImpl.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 1997, 2015, 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 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 m = new Hashtable(); + + 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; + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/ReachableObjects.java b/test/lib/share/classes/jdk/test/lib/hprof/model/ReachableObjects.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/ReachableObjects.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 1997, 2015, 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 bag = new Hashtable(); + final Hashtable fieldsExcluded = new Hashtable(); //Bag + final Hashtable fieldsUsed = new Hashtable(); // Bag + 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 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; +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/ReferenceChain.java b/test/lib/share/classes/jdk/test/lib/hprof/model/ReferenceChain.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/ReferenceChain.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/Root.java b/test/lib/share/classes/jdk/test/lib/hprof/model/Root.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/Root.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 1997, 2015, 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; + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/Snapshot.java b/test/lib/share/classes/jdk/test/lib/hprof/model/Snapshot.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/Snapshot.java @@ -0,0 +1,630 @@ +/* + * Copyright (c) 1997, 2015, 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 { + + public static 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 heapObjects = + new Hashtable(); + + private Hashtable fakeClasses = + new Hashtable(); + + // all Roots in this Snapshot + private Vector roots = new Vector(); + + // name-to-class map + private Map classes = + new TreeMap(); + + // new objects relative to a baseline - lazily initialized + private volatile Map newObjects; + + // allocation site traces for all objects - lazily initialized + private volatile Map siteTraces; + + // object-to-Root map for all objects + private Map rootsMap = + new HashMap(); + + // soft cache of finalizeable objects - lazily initialized + private SoftReference> 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("", 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"); + if (weakReferenceClass == null) { // JDK 1.1.x + weakReferenceClass = findClass("sun.misc.Ref"); + referentFieldIndex = 0; + } else { + 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 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 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 finalizables = new Vector(); + 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>(finalizables); + return finalizables.elements(); + } + + public Enumeration 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 fifo = new Vector(); // This is slow... A real fifo would help + // Must be a fifo to go breadth-first + Hashtable visited = new Hashtable(); + // Objects are added here right after being added to fifo. + Vector result = new Vector(); + 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 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("[", 0, 0, 0, 0, + EMPTY_FIELD_ARRAY, EMPTY_STATIC_ARRAY, + 0)); + otherArrayType = findClass("["); + } + } + } + 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(); + } + } + } + } + + private synchronized void initSiteTraces() { + if (siteTraces == null) { + synchronized (this) { + if (siteTraces == null) { + siteTraces = new HashMap(); + } + } + } + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/StackFrame.java b/test/lib/share/classes/jdk/test/lib/hprof/model/StackFrame.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/StackFrame.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 1997, 2015, 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); + } + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/model/StackTrace.java b/test/lib/share/classes/jdk/test/lib/hprof/model/StackTrace.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/model/StackTrace.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1997, 2015, 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; + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/parser/FileReadBuffer.java b/test/lib/share/classes/jdk/test/lib/hprof/parser/FileReadBuffer.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/parser/FileReadBuffer.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1997, 2015, 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(); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/parser/HprofReader.java b/test/lib/share/classes/jdk/test/lib/hprof/parser/HprofReader.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/parser/HprofReader.java @@ -0,0 +1,892 @@ +/* + * Copyright (c) 1997, 2015, 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 names; + + // Hashtable, 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 threadObjects; + + // Hashtable, maps class object ID to class name + // (with / converted to .) + private Hashtable classNameFromObjectID; + + // Hashtable, maps class serial # to class object ID + private Hashtable classNameFromSerialNo; + + // Hashtable maps stack frame ID to StackFrame. + // Null if we're not tracking them. + private Hashtable stackFrames; + + // Hashtable maps stack frame ID to StackTrace + // Null if we're not tracking them. + private Hashtable 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(); + threadObjects = new Hashtable(43); + classNameFromObjectID = new Hashtable(); + if (callStack) { + stackFrames = new Hashtable(43); + stackTraces = new Hashtable(43); + classNameFromSerialNo = new Hashtable(); + } + } + + 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; + } + case HPROF_UNLOAD_CLASS: + case HPROF_ALLOC_SITES: + case HPROF_START_THREAD: + case HPROF_END_THREAD: + case HPROF_HEAP_SUMMARY: + case HPROF_CPU_SAMPLES: + case HPROF_CONTROL_SETTINGS: + case HPROF_LOCKSTATS_WAIT_TIME: + case HPROF_LOCKSTATS_HOLD_TIME: + { + // Ignore these record types + skipBytes(length); + break; + } + default: { + skipBytes(length); + warn("Ignoring unrecognized record type " + type); + } + } + } + + return snapshot; + } + + private void skipBytes(long length) throws IOException { + in.skipBytes((int)length); + } + + private int readVersionHeader() throws IOException { + int candidatesLeft = VERSIONS.length; + boolean[] matched = new boolean[VERSIONS.length]; + for (int i = 0; i < candidatesLeft; i++) { + matched[i] = true; + } + + int pos = 0; + while (candidatesLeft > 0) { + char c = (char) in.readByte(); + currPos++; + for (int i = 0; i < VERSIONS.length; i++) { + if (matched[i]) { + if (c != VERSIONS[i].charAt(pos)) { // Not matched + matched[i] = false; + --candidatesLeft; + } else if (pos == VERSIONS[i].length() - 1) { // Full match + return i; + } + } + } + ++pos; + } + throw new IOException("Version string not recognized at byte " + (pos+3)); + } + + private void readHeapDump(long bytesLeft, long posAtEnd) throws IOException { + while (bytesLeft > 0) { + int type = in.readUnsignedByte(); + if (debugLevel > 0) { + System.out.println(" Read heap sub-record type " + type + + " at position " + + toHex(posAtEnd - bytesLeft)); + } + bytesLeft--; + switch(type) { + case HPROF_GC_ROOT_UNKNOWN: { + long id = readID(); + bytesLeft -= identifierSize; + snapshot.addRoot(new Root(id, 0, Root.UNKNOWN, "")); + break; + } + case HPROF_GC_ROOT_THREAD_OBJ: { + long id = readID(); + int threadSeq = in.readInt(); + int stackSeq = in.readInt(); + bytesLeft -= identifierSize + 8; + threadObjects.put(threadSeq, + new ThreadObject(id, stackSeq)); + break; + } + case HPROF_GC_ROOT_JNI_GLOBAL: { + long id = readID(); + long globalRefId = readID(); // Ignored, for now + bytesLeft -= 2*identifierSize; + snapshot.addRoot(new Root(id, 0, Root.NATIVE_STATIC, "")); + break; + } + case HPROF_GC_ROOT_JNI_LOCAL: { + long id = readID(); + int threadSeq = in.readInt(); + int depth = in.readInt(); + bytesLeft -= identifierSize + 8; + ThreadObject to = getThreadObjectFromSequence(threadSeq); + StackTrace st = getStackTraceFromSerial(to.stackSeq); + if (st != null) { + st = st.traceForDepth(depth+1); + } + snapshot.addRoot(new Root(id, to.threadId, + Root.NATIVE_LOCAL, "", st)); + break; + } + case HPROF_GC_ROOT_JAVA_FRAME: { + long id = readID(); + int threadSeq = in.readInt(); + int depth = in.readInt(); + bytesLeft -= identifierSize + 8; + ThreadObject to = getThreadObjectFromSequence(threadSeq); + StackTrace st = getStackTraceFromSerial(to.stackSeq); + if (st != null) { + st = st.traceForDepth(depth+1); + } + snapshot.addRoot(new Root(id, to.threadId, + Root.JAVA_LOCAL, "", st)); + break; + } + case HPROF_GC_ROOT_NATIVE_STACK: { + long id = readID(); + int threadSeq = in.readInt(); + bytesLeft -= identifierSize + 4; + ThreadObject to = getThreadObjectFromSequence(threadSeq); + StackTrace st = getStackTraceFromSerial(to.stackSeq); + snapshot.addRoot(new Root(id, to.threadId, + Root.NATIVE_STACK, "", st)); + break; + } + case HPROF_GC_ROOT_STICKY_CLASS: { + long id = readID(); + bytesLeft -= identifierSize; + snapshot.addRoot(new Root(id, 0, Root.SYSTEM_CLASS, "")); + break; + } + case HPROF_GC_ROOT_THREAD_BLOCK: { + long id = readID(); + int threadSeq = in.readInt(); + bytesLeft -= identifierSize + 4; + ThreadObject to = getThreadObjectFromSequence(threadSeq); + StackTrace st = getStackTraceFromSerial(to.stackSeq); + snapshot.addRoot(new Root(id, to.threadId, + Root.THREAD_BLOCK, "", st)); + break; + } + case HPROF_GC_ROOT_MONITOR_USED: { + long id = readID(); + bytesLeft -= identifierSize; + snapshot.addRoot(new Root(id, 0, Root.BUSY_MONITOR, "")); + break; + } + case HPROF_GC_CLASS_DUMP: { + int bytesRead = readClass(); + bytesLeft -= bytesRead; + break; + } + case HPROF_GC_INSTANCE_DUMP: { + int bytesRead = readInstance(); + bytesLeft -= bytesRead; + break; + } + case HPROF_GC_OBJ_ARRAY_DUMP: { + int bytesRead = readArray(false); + bytesLeft -= bytesRead; + break; + } + case HPROF_GC_PRIM_ARRAY_DUMP: { + int bytesRead = readArray(true); + bytesLeft -= bytesRead; + break; + } + default: { + throw new IOException("Unrecognized heap dump sub-record type: " + type); + } + } + } + if (bytesLeft != 0) { + warn("Error reading heap dump or heap dump segment: Byte count is " + bytesLeft + " instead of 0"); + skipBytes(bytesLeft); + } + if (debugLevel > 0) { + System.out.println(" Finished heap sub-records."); + } + } + + private long readID() throws IOException { + return (identifierSize == 4)? + (Snapshot.SMALL_ID_MASK & (long)in.readInt()) : in.readLong(); + } + + // + // Read a java value. If result is non-null, it's expected to be an + // array of one element. We use it to fake multiple return values. + // @returns the number of bytes read + // + private int readValue(JavaThing[] resultArr) throws IOException { + byte type = in.readByte(); + return 1 + readValueForType(type, resultArr); + } + + private int readValueForType(byte type, JavaThing[] resultArr) + throws IOException { + if (version >= VERSION_JDK12BETA4) { + type = signatureFromTypeId(type); + } + return readValueForTypeSignature(type, resultArr); + } + + private int readValueForTypeSignature(byte type, JavaThing[] resultArr) + throws IOException { + switch (type) { + case '[': + case 'L': { + long id = readID(); + if (resultArr != null) { + resultArr[0] = new JavaObjectRef(id); + } + return identifierSize; + } + case 'Z': { + int b = in.readByte(); + if (b != 0 && b != 1) { + warn("Illegal boolean value read"); + } + if (resultArr != null) { + resultArr[0] = new JavaBoolean(b != 0); + } + return 1; + } + case 'B': { + byte b = in.readByte(); + if (resultArr != null) { + resultArr[0] = new JavaByte(b); + } + return 1; + } + case 'S': { + short s = in.readShort(); + if (resultArr != null) { + resultArr[0] = new JavaShort(s); + } + return 2; + } + case 'C': { + char ch = in.readChar(); + if (resultArr != null) { + resultArr[0] = new JavaChar(ch); + } + return 2; + } + case 'I': { + int val = in.readInt(); + if (resultArr != null) { + resultArr[0] = new JavaInt(val); + } + return 4; + } + case 'J': { + long val = in.readLong(); + if (resultArr != null) { + resultArr[0] = new JavaLong(val); + } + return 8; + } + case 'F': { + float val = in.readFloat(); + if (resultArr != null) { + resultArr[0] = new JavaFloat(val); + } + return 4; + } + case 'D': { + double val = in.readDouble(); + if (resultArr != null) { + resultArr[0] = new JavaDouble(val); + } + return 8; + } + default: { + throw new IOException("Bad value signature: " + type); + } + } + } + + private ThreadObject getThreadObjectFromSequence(int threadSeq) + throws IOException { + ThreadObject to = threadObjects.get(threadSeq); + if (to == null) { + throw new IOException("Thread " + threadSeq + + " not found for JNI local ref"); + } + return to; + } + + private String getNameFromID(long id) throws IOException { + return getNameFromID(Long.valueOf(id)); + } + + private String getNameFromID(Long id) throws IOException { + if (id.longValue() == 0L) { + return ""; + } + String result = names.get(id); + if (result == null) { + warn("Name not found at " + toHex(id.longValue())); + return "unresolved name " + toHex(id.longValue()); + } + return result; + } + + private StackTrace getStackTraceFromSerial(int ser) throws IOException { + if (stackTraces == null) { + return null; + } + StackTrace result = stackTraces.get(ser); + if (result == null) { + warn("Stack trace not found for serial # " + ser); + } + return result; + } + + // + // Handle a HPROF_GC_CLASS_DUMP + // Return number of bytes read + // + private int readClass() throws IOException { + long id = readID(); + StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); + long superId = readID(); + long classLoaderId = readID(); + long signersId = readID(); + long protDomainId = readID(); + long reserved1 = readID(); + long reserved2 = readID(); + int instanceSize = in.readInt(); + int bytesRead = 7 * identifierSize + 8; + + int numConstPoolEntries = in.readUnsignedShort(); + bytesRead += 2; + for (int i = 0; i < numConstPoolEntries; i++) { + int index = in.readUnsignedShort(); // unused + bytesRead += 2; + bytesRead += readValue(null); // We ignore the values + } + + int numStatics = in.readUnsignedShort(); + bytesRead += 2; + JavaThing[] valueBin = new JavaThing[1]; + JavaStatic[] statics = new JavaStatic[numStatics]; + for (int i = 0; i < numStatics; i++) { + long nameId = readID(); + bytesRead += identifierSize; + byte type = in.readByte(); + bytesRead++; + bytesRead += readValueForType(type, valueBin); + String fieldName = getNameFromID(nameId); + if (version >= VERSION_JDK12BETA4) { + type = signatureFromTypeId(type); + } + String signature = "" + ((char) type); + JavaField f = new JavaField(fieldName, signature); + statics[i] = new JavaStatic(f, valueBin[0]); + } + + int numFields = in.readUnsignedShort(); + bytesRead += 2; + JavaField[] fields = new JavaField[numFields]; + for (int i = 0; i < numFields; i++) { + long nameId = readID(); + bytesRead += identifierSize; + byte type = in.readByte(); + bytesRead++; + String fieldName = getNameFromID(nameId); + if (version >= VERSION_JDK12BETA4) { + type = signatureFromTypeId(type); + } + String signature = "" + ((char) type); + fields[i] = new JavaField(fieldName, signature); + } + String name = classNameFromObjectID.get(id); + if (name == null) { + warn("Class name not found for " + toHex(id)); + name = "unknown-name@" + toHex(id); + } + JavaClass c = new JavaClass(id, name, superId, classLoaderId, signersId, + protDomainId, fields, statics, + instanceSize); + snapshot.addClass(id, c); + snapshot.setSiteTrace(c, stackTrace); + + return bytesRead; + } + + private String toHex(long addr) { + return jdk.test.lib.hprof.util.Misc.toHex(addr); + } + + // + // Handle a HPROF_GC_INSTANCE_DUMP + // Return number of bytes read + // + private int readInstance() throws IOException { + long start = in.position(); + long id = readID(); + StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); + long classID = readID(); + int bytesFollowing = in.readInt(); + int bytesRead = (2 * identifierSize) + 8 + bytesFollowing; + JavaObject jobj = new JavaObject(classID, start); + skipBytes(bytesFollowing); + snapshot.addHeapObject(id, jobj); + snapshot.setSiteTrace(jobj, stackTrace); + return bytesRead; + } + + // + // Handle a HPROF_GC_OBJ_ARRAY_DUMP or HPROF_GC_PRIM_ARRAY_DUMP + // Return number of bytes read + // + private int readArray(boolean isPrimitive) throws IOException { + long start = in.position(); + long id = readID(); + StackTrace stackTrace = getStackTraceFromSerial(in.readInt()); + int num = in.readInt(); + int bytesRead = identifierSize + 8; + long elementClassID; + if (isPrimitive) { + elementClassID = in.readByte(); + bytesRead++; + } else { + elementClassID = readID(); + bytesRead += identifierSize; + } + + // Check for primitive arrays: + byte primitiveSignature = 0x00; + int elSize = 0; + if (isPrimitive || version < VERSION_JDK12BETA4) { + switch ((int)elementClassID) { + case T_BOOLEAN: { + primitiveSignature = (byte) 'Z'; + elSize = 1; + break; + } + case T_CHAR: { + primitiveSignature = (byte) 'C'; + elSize = 2; + break; + } + case T_FLOAT: { + primitiveSignature = (byte) 'F'; + elSize = 4; + break; + } + case T_DOUBLE: { + primitiveSignature = (byte) 'D'; + elSize = 8; + break; + } + case T_BYTE: { + primitiveSignature = (byte) 'B'; + elSize = 1; + break; + } + case T_SHORT: { + primitiveSignature = (byte) 'S'; + elSize = 2; + break; + } + case T_INT: { + primitiveSignature = (byte) 'I'; + elSize = 4; + break; + } + case T_LONG: { + primitiveSignature = (byte) 'J'; + elSize = 8; + break; + } + } + if (version >= VERSION_JDK12BETA4 && primitiveSignature == 0x00) { + throw new IOException("Unrecognized typecode: " + + elementClassID); + } + } + if (primitiveSignature != 0x00) { + int size = elSize * num; + bytesRead += size; + JavaValueArray va = new JavaValueArray(primitiveSignature, start); + skipBytes(size); + snapshot.addHeapObject(id, va); + snapshot.setSiteTrace(va, stackTrace); + } else { + int sz = num * identifierSize; + bytesRead += sz; + JavaObjectArray arr = new JavaObjectArray(elementClassID, start); + skipBytes(sz); + snapshot.addHeapObject(id, arr); + snapshot.setSiteTrace(arr, stackTrace); + } + return bytesRead; + } + + private byte signatureFromTypeId(byte typeId) throws IOException { + switch (typeId) { + case T_CLASS: { + return (byte) 'L'; + } + case T_BOOLEAN: { + return (byte) 'Z'; + } + case T_CHAR: { + return (byte) 'C'; + } + case T_FLOAT: { + return (byte) 'F'; + } + case T_DOUBLE: { + return (byte) 'D'; + } + case T_BYTE: { + return (byte) 'B'; + } + case T_SHORT: { + return (byte) 'S'; + } + case T_INT: { + return (byte) 'I'; + } + case T_LONG: { + return (byte) 'J'; + } + default: { + throw new IOException("Invalid type id of " + typeId); + } + } + } + + private void handleEOF(EOFException exp, Snapshot snapshot) { + if (debugLevel > 0) { + exp.printStackTrace(); + } + warn("Unexpected EOF. Will miss information..."); + // we have EOF, we have to tolerate missing references + snapshot.setUnresolvedObjectsOK(true); + } + + private void warn(String msg) { + System.out.println("WARNING: " + msg); + } + + // + // A trivial data-holder class for HPROF_GC_ROOT_THREAD_OBJ. + // + private class ThreadObject { + + long threadId; + int stackSeq; + + ThreadObject(long threadId, int stackSeq) { + this.threadId = threadId; + this.stackSeq = stackSeq; + } + } + +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/parser/MappedReadBuffer.java b/test/lib/share/classes/jdk/test/lib/hprof/parser/MappedReadBuffer.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/parser/MappedReadBuffer.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1997, 2015, 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; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; + +/** + * Implementation of ReadBuffer using mapped file buffer + * + * @author A. Sundararajan + */ +class MappedReadBuffer implements ReadBuffer { + private MappedByteBuffer buf; + + MappedReadBuffer(MappedByteBuffer buf) { + this.buf = buf; + } + + // factory method to create correct ReadBuffer for a given file + static ReadBuffer create(RandomAccessFile file) throws IOException { + FileChannel ch = file.getChannel(); + long size = ch.size(); + // if file size is more than 2 GB and when file mapping is + // configured (default), use mapped file reader + if (canUseFileMap() && (size <= Integer.MAX_VALUE)) { + MappedByteBuffer buf; + try { + buf = ch.map(FileChannel.MapMode.READ_ONLY, 0, size); + ch.close(); + return new MappedReadBuffer(buf); + } catch (IOException exp) { + exp.printStackTrace(); + System.err.println("File mapping failed, will use direct read"); + // fall through + } + } // else fall through + return new FileReadBuffer(file); + } + + private static boolean canUseFileMap() { + // set jhat.disableFileMap to any value other than "false" + // to disable file mapping + String prop = System.getProperty("jhat.disableFileMap"); + return prop == null || prop.equals("false"); + } + + private void seek(long pos) throws IOException { + assert pos <= Integer.MAX_VALUE : "position overflow"; + buf.position((int)pos); + } + + public synchronized void get(long pos, byte[] res) throws IOException { + seek(pos); + buf.get(res); + } + + public synchronized char getChar(long pos) throws IOException { + seek(pos); + return buf.getChar(); + } + + public synchronized byte getByte(long pos) throws IOException { + seek(pos); + return buf.get(); + } + + public synchronized short getShort(long pos) throws IOException { + seek(pos); + return buf.getShort(); + } + + public synchronized int getInt(long pos) throws IOException { + seek(pos); + return buf.getInt(); + } + + public synchronized long getLong(long pos) throws IOException { + seek(pos); + return buf.getLong(); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/parser/PositionDataInputStream.java b/test/lib/share/classes/jdk/test/lib/hprof/parser/PositionDataInputStream.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/parser/PositionDataInputStream.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1997, 2015, 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.DataInputStream; +import java.io.InputStream; + +/** + * A DataInputStream that keeps track of total bytes read + * (in effect 'position' in stream) so far. + * + */ +public class PositionDataInputStream extends DataInputStream { + public PositionDataInputStream(InputStream in) { + super(in instanceof PositionInputStream? + in : new PositionInputStream(in)); + } + + public boolean markSupported() { + return false; + } + + public void mark(int readLimit) { + throw new UnsupportedOperationException("mark"); + } + + public void reset() { + throw new UnsupportedOperationException("reset"); + } + + public long position() { + return ((PositionInputStream)in).position(); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/parser/PositionInputStream.java b/test/lib/share/classes/jdk/test/lib/hprof/parser/PositionInputStream.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/parser/PositionInputStream.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1997, 2015, 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.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * InputStream that keeps track of total bytes read (in effect + * 'position' in stream) from the input stream. + * + */ +public class PositionInputStream extends FilterInputStream { + private long position = 0L; + + public PositionInputStream(InputStream in) { + super(in); + } + + public int read() throws IOException { + int res = super.read(); + if (res != -1) position++; + return res; + } + + public int read(byte[] b, int off, int len) throws IOException { + int res = super.read(b, off, len); + if (res != -1) position += res; + return res; + } + + public long skip(long n) throws IOException { + long res = super.skip(n); + position += res; + return res; + } + + public boolean markSupported() { + return false; + } + + public void mark(int readLimit) { + throw new UnsupportedOperationException("mark"); + } + + public void reset() { + throw new UnsupportedOperationException("reset"); + } + + public long position() { + return position; + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/parser/ReadBuffer.java b/test/lib/share/classes/jdk/test/lib/hprof/parser/ReadBuffer.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/parser/ReadBuffer.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1997, 2015, 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; + +/** + * Positionable read only buffer + * + * @author A. Sundararajan + */ +public interface ReadBuffer { + // read methods - only byte array and int primitive types. + // read position has to be specified always. + public void get(long pos, byte[] buf) throws IOException; + public char getChar(long pos) throws IOException; + public byte getByte(long pos) throws IOException; + public short getShort(long pos) throws IOException; + public int getInt(long pos) throws IOException; + public long getLong(long pos) throws IOException; +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/parser/Reader.java b/test/lib/share/classes/jdk/test/lib/hprof/parser/Reader.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/parser/Reader.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1997, 2015, 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 jdk.test.lib.hprof.model.*; + +/** + * Abstract base class for reading object dump files. A reader need not be + * thread-safe. + * + * @author Bill Foote + */ + + +public abstract class Reader { + protected PositionDataInputStream in; + + protected Reader(PositionDataInputStream in) { + this.in = in; + } + + /** + * Read a snapshot from a data input stream. It is assumed that the magic + * number has already been read. + */ + abstract public Snapshot read() throws IOException; + + /** + * Read a snapshot from a file. + * + * @param heapFile The name of a file containing a heap dump + * @param callStack If true, read the call stack of allocaation sites + */ + public static Snapshot readFile(String heapFile, boolean callStack, + int debugLevel) + throws IOException { + int dumpNumber = 1; + int pos = heapFile.lastIndexOf('#'); + if (pos > -1) { + String num = heapFile.substring(pos+1, heapFile.length()); + try { + dumpNumber = Integer.parseInt(num, 10); + } catch (java.lang.NumberFormatException ex) { + String msg = "In file name \"" + heapFile + + "\", a dump number was " + + "expected after the :, but \"" + + num + "\" was found instead."; + System.err.println(msg); + throw new IOException(msg); + } + heapFile = heapFile.substring(0, pos); + } + try (PositionDataInputStream in = new PositionDataInputStream( + new BufferedInputStream(new FileInputStream(heapFile)))) { + int i = in.readInt(); + if (i == HprofReader.MAGIC_NUMBER) { + Reader r + = new HprofReader(heapFile, in, dumpNumber, + callStack, debugLevel); + return r.read(); + } else { + throw new IOException("Unrecognized magic number: " + i); + } + } + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/util/ArraySorter.java b/test/lib/share/classes/jdk/test/lib/hprof/util/ArraySorter.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/util/ArraySorter.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1997, 2015, 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.util; +import java.util.*; + +/** + * A singleton utility class that sorts an array of objects. + *

+ * Use: + *

+ *
+ *  Stuff[] arr = ...;
+ *  ArraySorter.sort(arr, new Comparer() {
+ *      public int compare(Object lhs, Object rhs) {
+ *          return ((String) lhs).compareTo((String) rhs);
+ *      }
+ *  });
+ * 
+ * + * @author Bill Foote + */ + +public class ArraySorter { + + /** + * Sort the given array, using c for comparison + **/ + static public void sort(Object[] arr, Comparer c) { + quickSort(arr, c, 0, arr.length-1); + } + + + /** + * Sort an array of strings, using String.compareTo() + **/ + static public void sortArrayOfStrings(Object[] arr) { + sort(arr, new Comparer() { + public int compare(Object lhs, Object rhs) { + return ((String) lhs).compareTo((String) rhs); + } + }); + } + + + static private void swap(Object[] arr, int a, int b) { + Object tmp = arr[a]; + arr[a] = arr[b]; + arr[b] = tmp; + } + + // + // Sorts arr between from and to, inclusive. This is a quick, off-the-top- + // of-my-head quicksort: I haven't put any thought into optimizing it. + // I _did_ put thought into making sure it's safe (it will always + // terminate). Worst-case it's O(n^2), but it will usually run in + // in O(n log n). It's well-behaved if the list is already sorted, + // or nearly so. + // + static private void quickSort(Object[] arr, Comparer c, int from, int to) { + if (to <= from) + return; + int mid = (from + to) / 2; + if (mid != from) + swap(arr, mid, from); + Object pivot = arr[from]; // Simple-minded, but reasonable + int highestBelowPivot = from - 1; + int low = from+1; + int high = to; + // We now move low and high toward each other, maintaining the + // invariants: + // arr[i] <= pivot for all i < low + // arr[i] > pivot for all i > high + // As long as these invariants hold, and every iteration makes + // progress, we are safe. + while (low <= high) { + int cmp = c.compare(arr[low], pivot); + if (cmp <= 0) { // arr[low] <= pivot + if (cmp < 0) { + highestBelowPivot = low; + } + low++; + } else { + int c2; + for (;;) { + // arr[high] > pivot: + c2 = c.compare(arr[high], pivot); + if (c2 > 0) { + high--; + if (low > high) { + break; + } + } else { + break; + } + } + // At this point, low is never == high, BTW + if (low <= high) { + swap(arr, low, high); + if (c2 < 0) { + highestBelowPivot = low; + } + low++; + high--; + } + } + } + // At this point, low == high+1 + // Now we just need to sort from from..highestBelowPivot + // and from high+1..to + if (highestBelowPivot > from) { + // pivot == pivot, so ensure algorithm terminates + swap(arr, from, highestBelowPivot); + quickSort(arr, c, from, highestBelowPivot-1); + } + quickSort(arr, c, high+1, to); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/util/Comparer.java b/test/lib/share/classes/jdk/test/lib/hprof/util/Comparer.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/util/Comparer.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1997, 2015, 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.util; + +/** + * Base class for comparison of two objects. + * @see VectorSorter + * + * @author Bill Foote + */ + +abstract public class Comparer { + + /** + * @return a number <, == or > 0 depending on lhs compared to rhs + * @see java.lang.String.compareTo + **/ + abstract public int compare(Object lhs, Object rhs); +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/util/CompositeEnumeration.java b/test/lib/share/classes/jdk/test/lib/hprof/util/CompositeEnumeration.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/util/CompositeEnumeration.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1997, 2015, 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.util; + +import java.util.Enumeration; +import java.util.NoSuchElementException; +import jdk.test.lib.hprof.model.JavaHeapObject; + +public class CompositeEnumeration implements Enumeration { + Enumeration e1; + Enumeration e2; + + public CompositeEnumeration(Enumeration e1, Enumeration e2) { + this.e1 = e1; + this.e2 = e2; + } + + public boolean hasMoreElements() { + return e1.hasMoreElements() || e2.hasMoreElements(); + } + + public JavaHeapObject nextElement() { + if (e1.hasMoreElements()) { + return e1.nextElement(); + } + + if (e2.hasMoreElements()) { + return e2.nextElement(); + } + + throw new NoSuchElementException(); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/util/Misc.java b/test/lib/share/classes/jdk/test/lib/hprof/util/Misc.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/util/Misc.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1997, 2015, 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.util; +import java.util.*; + +/** + * Miscellaneous functions I couldn't think of a good place to put. + * + * @author Bill Foote + */ + + +public class Misc { + + private static char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + public final static String toHex(int addr) { + char[] buf = new char[8]; + int i = 0; + for (int s = 28; s >= 0; s -= 4) { + buf[i++] = digits[(addr >> s) & 0xf]; + } + return "0x" + new String(buf); + } + + public final static String toHex(long addr) { + return "0x" + Long.toHexString(addr); + } + + public final static long parseHex(String value) { + long result = 0; + if (value.length() < 2 || value.charAt(0) != '0' || + value.charAt(1) != 'x') { + return -1L; + } + for(int i = 2; i < value.length(); i++) { + result *= 16; + char ch = value.charAt(i); + if (ch >= '0' && ch <= '9') { + result += (ch - '0'); + } else if (ch >= 'a' && ch <= 'f') { + result += (ch - 'a') + 10; + } else if (ch >= 'A' && ch <= 'F') { + result += (ch - 'A') + 10; + } else { + throw new NumberFormatException("" + ch + + " is not a valid hex digit"); + } + } + return result; + } + + public static String encodeHtml(String str) { + final int len = str.length(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < len; i++) { + char ch = str.charAt(i); + if (ch == '<') { + sb.append("<"); + } else if (ch == '>') { + sb.append(">"); + } else if (ch == '"') { + sb.append("""); + } else if (ch == '\'') { + sb.append("'"); + } else if (ch == '&') { + sb.append("&"); + } else if (ch < ' ') { + sb.append("&#").append((int)ch).append(';'); + } else { + int c = (ch & 0xFFFF); + if (c > 127) { + sb.append("&#").append(c).append(';'); + } else { + sb.append(ch); + } + } + } + return sb.toString(); + } +} diff --git a/test/lib/share/classes/jdk/test/lib/hprof/util/VectorSorter.java b/test/lib/share/classes/jdk/test/lib/hprof/util/VectorSorter.java new file mode 100644 --- /dev/null +++ b/test/lib/share/classes/jdk/test/lib/hprof/util/VectorSorter.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 1997, 2015, 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.util; +import java.util.*; + +/** + * A singleton utility class that sorts a vector. + *

+ * Use: + *

+ *
+ *  Vector v =   ;
+ *  VectorSorter.sort(v, new Comparer() {
+ *      public int compare(Object lhs, Object rhs) {
+ *          return ((String) lhs).compareTo((String) rhs);
+ *      }
+ *  });
+ * 
+ * + * @author Bill Foote + */ + + +public class VectorSorter { + + /** + * Sort the given vector, using c for comparison + **/ + static public void sort(Vector v, Comparer c) { + quickSort(v, c, 0, v.size()-1); + } + + + /** + * Sort a vector of strings, using String.compareTo() + **/ + static public void sortVectorOfStrings(Vector v) { + sort(v, new Comparer() { + public int compare(Object lhs, Object rhs) { + return ((String) lhs).compareTo((String) rhs); + } + }); + } + + + static private void swap(Vector v, int a, int b) { + Object tmp = v.elementAt(a); + v.setElementAt(v.elementAt(b), a); + v.setElementAt(tmp, b); + } + + // + // Sorts v between from and to, inclusive. This is a quick, off-the-top- + // of-my-head quicksort: I haven't put any thought into optimizing it. + // I _did_ put thought into making sure it's safe (it will always + // terminate). Worst-case it's O(n^2), but it will usually run in + // in O(n log n). It's well-behaved if the list is already sorted, + // or nearly so. + // + static private void quickSort(Vector v, Comparer c, int from, int to) { + if (to <= from) + return; + int mid = (from + to) / 2; + if (mid != from) + swap(v, mid, from); + Object pivot = v.elementAt(from); + // Simple-minded, but reasonable + int highestBelowPivot = from - 1; + int low = from+1; + int high = to; + // We now move low and high toward eachother, maintaining the + // invariants: + // v[i] <= pivot for all i < low + // v[i] > pivot for all i > high + // As long as these invariants hold, and every iteration makes + // progress, we are safe. + while (low <= high) { + int cmp = c.compare(v.elementAt(low), pivot); + if (cmp <= 0) { // v[low] <= pivot + if (cmp < 0) { + highestBelowPivot = low; + } + low++; + } else { + int c2; + for (;;) { + c2 = c.compare(v.elementAt(high), pivot); + // v[high] > pivot: + if (c2 > 0) { + high--; + if (low > high) { + break; + } + } else { + break; + } + } + // At this point, low is never == high + if (low <= high) { + swap(v, low, high); + if (c2 < 0) { + highestBelowPivot = low; + } + low++; + high--; + } + } + } + // Now we just need to sort from from..highestBelowPivot + // and from high+1..to + if (highestBelowPivot > from) { + // pivot == pivot, so ensure algorithm terminates + swap(v, from, highestBelowPivot); + quickSort(v, c, from, highestBelowPivot-1); + } + quickSort(v, c, high+1, to); + } +}