summaryrefslogtreecommitdiff
path: root/hit/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'hit/src/com/android')
-rw-r--r--hit/src/com/android/hit/ArrayInstance.java188
-rw-r--r--hit/src/com/android/hit/ClassInstance.java208
-rw-r--r--hit/src/com/android/hit/ClassObj.java241
-rw-r--r--hit/src/com/android/hit/Heap.java185
-rw-r--r--hit/src/com/android/hit/HprofParser.java611
-rw-r--r--hit/src/com/android/hit/Instance.java117
-rw-r--r--hit/src/com/android/hit/Main.java97
-rw-r--r--hit/src/com/android/hit/Queries.java239
-rw-r--r--hit/src/com/android/hit/RootObj.java111
-rw-r--r--hit/src/com/android/hit/RootType.java53
-rw-r--r--hit/src/com/android/hit/StackFrame.java60
-rw-r--r--hit/src/com/android/hit/StackTrace.java64
-rw-r--r--hit/src/com/android/hit/State.java179
-rw-r--r--hit/src/com/android/hit/ThreadObj.java27
-rw-r--r--hit/src/com/android/hit/Types.java89
15 files changed, 2469 insertions, 0 deletions
diff --git a/hit/src/com/android/hit/ArrayInstance.java b/hit/src/com/android/hit/ArrayInstance.java
new file mode 100644
index 0000000..42e8803
--- /dev/null
+++ b/hit/src/com/android/hit/ArrayInstance.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.util.Set;
+
+public class ArrayInstance extends Instance {
+ private int mType;
+ private int mNumEntries;
+ private byte[] mData;
+
+ public ArrayInstance(long id, StackTrace stack, int type, int numEntries,
+ byte[] data) {
+ mId = id;
+ mStack = stack;
+ mType = type;
+ mNumEntries = numEntries;
+ mData = data;
+ }
+
+ public final void resolveReferences(State state) {
+ if (mType != Types.OBJECT) {
+ return;
+ }
+
+ /*
+ * mData holds a stream of object instance ids
+ * Spin through them all and list ourselves as a reference holder.
+ */
+ int idSize = Types.getTypeSize(mType);
+ final int N = mNumEntries;
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(mData);
+ DataInputStream dis = new DataInputStream(bais);
+
+ for (int i = 0; i < N; i++) {
+ long id;
+
+ try {
+ if (idSize == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ Instance instance = state.findReference(id);
+
+ if (instance != null) {
+ instance.addParent(this);
+ }
+ } catch (java.io.IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public final int getSize() {
+ return mData.length;
+ }
+
+ @Override
+ public final void visit(Set<Instance> resultSet, Filter filter) {
+ // If we're in the set then we and our children have been visited
+ if (resultSet.contains(this)) {
+ return;
+ }
+
+ if (null != filter) {
+ if (filter.accept(this)) {
+ resultSet.add(this);
+ }
+ } else {
+ resultSet.add(this);
+ }
+
+ if (mType != Types.OBJECT) {
+ return;
+ }
+
+ /*
+ * mData holds a stream of object instance ids
+ * Spin through them all and visit them
+ */
+ int idSize = Types.getTypeSize(mType);
+ final int N = mNumEntries;
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(mData);
+ DataInputStream dis = new DataInputStream(bais);
+ State state = mHeap.mState;
+
+ for (int i = 0; i < N; i++) {
+ long id;
+
+ try {
+ if (idSize == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ Instance instance = state.findReference(id);
+
+ if (instance != null) {
+ instance.visit(resultSet, filter);
+ }
+ } catch (java.io.IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public final String getTypeName() {
+ return Types.getTypeName(mType) + "[" + mNumEntries + "]";
+ }
+
+ public final String toString() {
+ return String.format("%s@0x08x", getTypeName(), mId);
+ }
+
+ @Override
+ public String describeReferenceTo(long referent) {
+ // If this isn't an object array then we can't refer to an object
+ if (mType != Types.OBJECT) {
+ return super.describeReferenceTo(referent);
+ }
+
+ int idSize = Types.getTypeSize(mType);
+ final int N = mNumEntries;
+ int numRefs = 0;
+ StringBuilder result = new StringBuilder("Elements [");
+ ByteArrayInputStream bais = new ByteArrayInputStream(mData);
+ DataInputStream dis = new DataInputStream(bais);
+
+ /*
+ * Spin through all the objects and build up a string describing
+ * all of the array elements that refer to the target object.
+ */
+ for (int i = 0; i < N; i++) {
+ long id;
+
+ try {
+ if (idSize == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ if (id == referent) {
+ numRefs++;
+
+ if (numRefs > 1) {
+ result.append(", ");
+ }
+
+ result.append(i);
+ }
+ } catch (java.io.IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (numRefs == 0) {
+ return super.describeReferenceTo(referent);
+ }
+
+ result.append("]");
+
+ return result.toString();
+ }
+}
diff --git a/hit/src/com/android/hit/ClassInstance.java b/hit/src/com/android/hit/ClassInstance.java
new file mode 100644
index 0000000..0869769
--- /dev/null
+++ b/hit/src/com/android/hit/ClassInstance.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Set;
+
+public class ClassInstance extends Instance {
+ private byte[] mFieldValues;
+
+ public ClassInstance(long id, StackTrace stack, long classId) {
+ mId = id;
+ mStack = stack;
+ mClassId = classId;
+ }
+
+ public final void loadFieldData(DataInputStream in, int numBytes)
+ throws IOException {
+ mFieldValues = new byte[numBytes];
+ in.readFully(mFieldValues);
+ }
+
+ @Override
+ public void resolveReferences(State state) {
+ ClassObj isa = mHeap.mState.findClass(mClassId);
+
+ resolve(state, isa, isa.mStaticFieldTypes, isa.mStaticFieldValues);
+ resolve(state, isa, isa.mFieldTypes, mFieldValues);
+ }
+
+ private void resolve(State state, ClassObj isa, int[] types,
+ byte[] values) {
+ ByteArrayInputStream bais = new ByteArrayInputStream(values);
+ DataInputStream dis = new DataInputStream(bais);
+ final int N = types.length;
+
+ /*
+ * Spin through the list of fields, find all object references,
+ * and list ourselves as a reference holder.
+ */
+ try {
+ for (int i = 0; i < N; i++) {
+ int type = types[i];
+ int size = Types.getTypeSize(type);
+
+ if (type == Types.OBJECT) {
+ long id;
+
+ if (size == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ Instance instance = state.findReference(id);
+
+ if (instance != null) {
+ instance.addParent(this);
+ }
+ } else {
+ dis.skipBytes(size);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public final int getSize() {
+ ClassObj isa = mHeap.mState.findClass(mClassId);
+
+ return isa.getSize();
+ }
+
+ @Override
+ public final void visit(Set<Instance> resultSet, Filter filter) {
+ if (resultSet.contains(this)) {
+ return;
+ }
+
+ if (filter != null) {
+ if (filter.accept(this)) {
+ resultSet.add(this);
+ }
+ } else {
+ resultSet.add(this);
+ }
+
+ State state = mHeap.mState;
+ ClassObj isa = state.findClass(mClassId);
+ int[] types = isa.mFieldTypes;
+ ByteArrayInputStream bais = new ByteArrayInputStream(mFieldValues);
+ DataInputStream dis = new DataInputStream(bais);
+ final int N = types.length;
+
+ /*
+ * Spin through the list of fields, find all object references,
+ * and list ourselves as a reference holder.
+ */
+ try {
+ for (int i = 0; i < N; i++) {
+ int type = types[i];
+ int size = Types.getTypeSize(type);
+
+ if (type == Types.OBJECT) {
+ long id;
+
+ if (size == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ Instance instance = state.findReference(id);
+
+ if (instance != null) {
+ instance.visit(resultSet, filter);
+ }
+ } else {
+ dis.skipBytes(size);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public final String getTypeName() {
+ ClassObj theClass = mHeap.mState.findClass(mClassId);
+
+ return theClass.mClassName;
+ }
+
+ public final String toString() {
+ return String.format("%s@0x%08x", getTypeName(), mId);
+ }
+
+ @Override
+ public String describeReferenceTo(long referent) {
+ ClassObj isa = mHeap.mState.findClass(mClassId);
+ int[] types = isa.mFieldTypes;
+ String[] fieldNames = isa.mFieldNames;
+ ByteArrayInputStream bais = new ByteArrayInputStream(mFieldValues);
+ DataInputStream dis = new DataInputStream(bais);
+ final int N = types.length;
+ StringBuilder result = new StringBuilder("Referenced in field(s):");
+ int numReferences = 0;
+
+ /*
+ * Spin through the list of fields, add info about the field
+ * references to the output text.
+ */
+ try {
+ for (int i = 0; i < N; i++) {
+ int type = types[i];
+ int size = Types.getTypeSize(type);
+
+ if (type == Types.OBJECT) {
+ long id;
+
+ if (size == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ if (id == referent) {
+ numReferences++;
+ result.append("\n ");
+ result.append(fieldNames[i]);
+ }
+ } else {
+ dis.skipBytes(size);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ /*
+ * TODO: perform a similar loop over the static fields of isa
+ */
+
+ if (numReferences == 0) {
+ return super.describeReferenceTo(referent);
+ }
+
+ return result.toString();
+ }
+}
diff --git a/hit/src/com/android/hit/ClassObj.java b/hit/src/com/android/hit/ClassObj.java
new file mode 100644
index 0000000..1e3ac28
--- /dev/null
+++ b/hit/src/com/android/hit/ClassObj.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+public class ClassObj extends Instance implements Comparable<ClassObj> {
+ String mClassName;
+ long mSuperclassId;
+
+ String[] mFieldNames;
+ int[] mFieldTypes;
+
+ String[] mStaticFieldNames;
+ int[] mStaticFieldTypes;
+ byte[] mStaticFieldValues;
+
+ ArrayList<Instance> mInstances = new ArrayList<Instance>();
+ Set<ClassObj> mSubclasses = new HashSet<ClassObj>();
+
+ int mSize;
+
+ public ClassObj(long id, StackTrace stack, String className) {
+ mId = id;
+ mStack = stack;
+ mClassName = className;
+ }
+
+ @Override
+ public final void resolveReferences(State state) {
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(mStaticFieldValues);
+ DataInputStream dis = new DataInputStream(bais);
+ int[] types = mStaticFieldTypes;
+ final int N = types.length;
+
+ /*
+ * Spin through the list of static fields, find all object references,
+ * and list ourselves as a reference holder. Also add them to
+ * the list of root objects.
+ */
+ try {
+ for (int i = 0; i < N; i++) {
+ int type = types[i];
+ int size = Types.getTypeSize(type);
+
+ if (type == Types.OBJECT) {
+ long id;
+
+ if (size == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ RootObj root = new RootObj(RootType.JAVA_STATIC, id);
+
+ if (id == 0) {
+ root.mComment = String.format(
+ "Static field %s:%s null",
+ mClassName,
+ mStaticFieldNames[i]);
+ } else {
+ Instance instance = state.findReference(id);
+
+ instance.addParent(this);
+
+ root.mComment = String.format(
+ "Static field %s:%s %s [%s] 0x%08x",
+ mClassName,
+ mStaticFieldNames[i],
+ instance.getTypeName(),
+ instance.mHeap.mName,
+ id);
+ }
+
+ mHeap.addRoot(root);
+ } else {
+ dis.skipBytes(size);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ // Lastly, add ourself as a subclass of our superclass
+ if (mSuperclassId != 0) {
+ ClassObj superclass = state.findClass(mSuperclassId);
+
+ superclass.addSubclass(this);
+ }
+ }
+
+ public final void addSubclass(ClassObj subclass) {
+ mSubclasses.add(subclass);
+ }
+
+ public final void dumpSubclasses() {
+ for (ClassObj subclass: mSubclasses) {
+ System.out.println(" " + subclass.mClassName);
+ }
+ }
+
+ public final String toString() {
+ return mClassName.replace('/', '.');
+ }
+
+ public final void addInstance(Instance instance) {
+ mInstances.add(instance);
+ }
+
+ public final void setSuperclassId(long id) {
+ mSuperclassId = id;
+ }
+
+ public final void setFieldNames(String[] names) {
+ mFieldNames = names;
+ }
+
+ public final void setFieldTypes(int[] types) {
+ mFieldTypes = types;
+ }
+
+ public final void setStaticFieldNames(String[] names) {
+ mStaticFieldNames = names;
+ }
+
+ public final void setStaticFieldTypes(int[] types) {
+ mStaticFieldTypes = types;
+ }
+
+ public final void setStaticFieldValues(byte[] values) {
+ mStaticFieldValues = values;
+ }
+
+ public final void dump() {
+ System.out.println("+---------- ClassObj dump for: " + mClassName);
+
+ System.out.println("+----- Static fields");
+
+ for (int i = 0; i < mStaticFieldNames.length; i++) {
+ System.out.println(mStaticFieldNames[i] + ": "
+ + mStaticFieldTypes[i]);
+ }
+
+ System.out.println("+----- Instance fields");
+
+ for (int i = 0; i < mFieldNames.length; i++) {
+ System.out.println(mFieldNames[i] + ": " + mFieldTypes[i]);
+ }
+ }
+
+ @Override
+ public final String getTypeName() {
+ return "class " + mClassName;
+ }
+
+ @Override
+ public final void visit(Set<Instance> resultSet, Filter filter) {
+ if (resultSet.contains(this)) {
+ return;
+ }
+
+ if (filter != null) {
+ if (filter.accept(this)) {
+ resultSet.add(this);
+ }
+ } else {
+ resultSet.add(this);
+ }
+
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(mStaticFieldValues);
+ DataInputStream dis = new DataInputStream(bais);
+ int[] types = mStaticFieldTypes;
+ final int N = types.length;
+ State state = mHeap.mState;
+
+ /*
+ * Spin through the list of static fields, find all object references,
+ * and visit them.
+ */
+ try {
+ for (int i = 0; i < N; i++) {
+ int type = types[i];
+ int size = Types.getTypeSize(type);
+
+ if (type == Types.OBJECT) {
+ long id;
+
+ if (size == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ Instance instance = state.findReference(id);
+
+ if (instance != null) {
+ instance.visit(resultSet, filter);
+ }
+ } else {
+ dis.skipBytes(size);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public final int compareTo(ClassObj o) {
+ return mClassName.compareTo(o.mClassName);
+ }
+
+ public final boolean equals(Object o) {
+ if (! (o instanceof ClassObj)) {
+ return false;
+ }
+
+ return 0 == compareTo((ClassObj) o);
+ }
+}
diff --git a/hit/src/com/android/hit/Heap.java b/hit/src/com/android/hit/Heap.java
new file mode 100644
index 0000000..37b15dd
--- /dev/null
+++ b/hit/src/com/android/hit/Heap.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class Heap {
+ String mName;
+
+ // List of individual stack frames
+ HashMap<Long, StackFrame> mFrames = new HashMap<Long, StackFrame>();
+
+ // List stack traces, which are lists of stack frames
+ HashMap<Integer, StackTrace> mTraces = new HashMap<Integer, StackTrace>();
+
+ // Root objects such as interned strings, jni locals, etc
+ ArrayList<RootObj> mRoots = new ArrayList<RootObj>();
+
+ // List of threads
+ HashMap<Integer, ThreadObj> mThreads = new HashMap<Integer, ThreadObj>();
+
+ // Class definitions
+ HashMap<Long, ClassObj> mClassesById = new HashMap<Long, ClassObj>();
+ HashMap<String, ClassObj> mClassesByName = new HashMap<String, ClassObj>();
+
+ // List of instances of above class definitions
+ HashMap<Long, Instance> mInstances = new HashMap<Long, Instance>();
+
+ // The super-state that this heap is part of
+ State mState;
+
+ public Heap(String name) {
+ mName = name;
+ }
+
+ public final void addStackFrame(StackFrame theFrame) {
+ mFrames.put(theFrame.mId, theFrame);
+ }
+
+ public final StackFrame getStackFrame(long id) {
+ return mFrames.get(id);
+ }
+
+ public final void addStackTrace(StackTrace theTrace) {
+ mTraces.put(theTrace.mSerialNumber, theTrace);
+ }
+
+ public final StackTrace getStackTrace(int traceSerialNumber) {
+ return mTraces.get(traceSerialNumber);
+ }
+
+ public final StackTrace getStackTraceAtDepth(int traceSerialNumber,
+ int depth) {
+ StackTrace trace = mTraces.get(traceSerialNumber);
+
+ if (trace != null) {
+ trace = trace.fromDepth(depth);
+ }
+
+ return trace;
+ }
+
+ public final void addRoot(RootObj root) {
+ root.mIndex = mRoots.size();
+ mRoots.add(root);
+ }
+
+ public final void addThread(ThreadObj thread, int serialNumber) {
+ mThreads.put(serialNumber, thread);
+ }
+
+ public final ThreadObj getThread(int serialNumber) {
+ return mThreads.get(serialNumber);
+ }
+
+ public final void addInstance(long id, Instance instance) {
+ mInstances.put(id, instance);
+ }
+
+ public final Instance getInstance(long id) {
+ return mInstances.get(id);
+ }
+
+ public final void addClass(long id, ClassObj theClass) {
+ mClassesById.put(id, theClass);
+ mClassesByName.put(theClass.mClassName, theClass);
+ }
+
+ public final ClassObj getClass(long id) {
+ return mClassesById.get(id);
+ }
+
+ public final ClassObj getClass(String name) {
+ return mClassesByName.get(name);
+ }
+
+ public final void dumpInstanceCounts() {
+ for (ClassObj theClass: mClassesById.values()) {
+ int count = theClass.mInstances.size();
+
+ if (count > 0) {
+ System.out.println(theClass + ": " + count);
+ }
+ }
+ }
+
+ public final void dumpSubclasses() {
+ for (ClassObj theClass: mClassesById.values()) {
+ int count = theClass.mSubclasses.size();
+
+ if (count > 0) {
+ System.out.println(theClass);
+ theClass.dumpSubclasses();
+ }
+ }
+ }
+
+ public final void dumpSizes() {
+ for (ClassObj theClass: mClassesById.values()) {
+ int size = 0;
+
+ for (Instance instance: theClass.mInstances) {
+ size += instance.getCompositeSize();
+ }
+
+ if (size > 0) {
+ System.out.println(theClass + ": base " + theClass.getSize()
+ + ", composite " + size);
+ }
+ }
+ }
+
+ /*
+ * Spin through all of the class instances and link them to their
+ * parent class definition objects. Then have each instance resolve
+ * its own internal object references.
+ */
+ public final void resolveInstanceRefs(State state) {
+ for (Instance instance : mInstances.values()) {
+ ClassObj theClass = mClassesById.get(instance.mClassId);
+
+ if (theClass == null) {
+ continue;
+ }
+
+ String name = theClass.mClassName;
+ String superclassName = "none";
+ ClassObj superClass = mClassesById.get(theClass.mSuperclassId);
+
+ if (superClass != null) {
+ superclassName = superClass.mClassName;
+ }
+
+ theClass.addInstance(instance);
+ instance.resolveReferences(state);
+ }
+ }
+
+ public final void resolveClassStatics(State state) {
+ for (ClassObj theClass: mClassesById.values()) {
+ theClass.resolveReferences(state);
+ }
+ }
+
+ public final void resolveRoots(State state) {
+ for (RootObj root: mRoots) {
+ root.resolveReferences(state);
+ }
+ }
+}
diff --git a/hit/src/com/android/hit/HprofParser.java b/hit/src/com/android/hit/HprofParser.java
new file mode 100644
index 0000000..98fef1e
--- /dev/null
+++ b/hit/src/com/android/hit/HprofParser.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.InputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.HashMap;
+
+public class HprofParser
+{
+ private static final int STRING_IN_UTF8 = 0x01;
+ private static final int LOAD_CLASS = 0x02;
+ private static final int UNLOAD_CLASS = 0x03; // unused
+ private static final int STACK_FRAME = 0x04;
+ private static final int STACK_TRACE = 0x05;
+ private static final int ALLOC_SITES = 0x06; // unused
+ private static final int HEAP_SUMMARY = 0x07;
+ private static final int START_THREAD = 0x0a; // unused
+ private static final int END_THREAD = 0x0b; // unused
+ private static final int HEAP_DUMP = 0x0c;
+ private static final int HEAP_DUMP_SEGMENT = 0x1c;
+ private static final int HEAP_DUMP_END = 0x2c;
+ private static final int CPU_SAMPLES = 0x0d; // unused
+ private static final int CONTROL_SETTINGS = 0x0e; // unused
+
+ private static final int ROOT_UNKNOWN = 0xff;
+ private static final int ROOT_JNI_GLOBAL = 0x01;
+ private static final int ROOT_JNI_LOCAL = 0x02;
+ private static final int ROOT_JAVA_FRAME = 0x03;
+ private static final int ROOT_NATIVE_STACK = 0x04;
+ private static final int ROOT_STICKY_CLASS = 0x05;
+ private static final int ROOT_THREAD_BLOCK = 0x06;
+ private static final int ROOT_MONITOR_USED = 0x07;
+ private static final int ROOT_THREAD_OBJECT = 0x08;
+ private static final int ROOT_CLASS_DUMP = 0x20;
+ private static final int ROOT_INSTANCE_DUMP = 0x21;
+ private static final int ROOT_OBJECT_ARRAY_DUMP = 0x22;
+ private static final int ROOT_PRIMITIVE_ARRAY_DUMP = 0x23;
+
+ /**
+ * Android format addition
+ *
+ * Specifies information about which heap certain objects came from.
+ * When a sub-tag of this type appears in a HPROF_HEAP_DUMP or
+ * HPROF_HEAP_DUMP_SEGMENT record, entries that follow it will be
+ * associated with the specified heap. The HEAP_DUMP_INFO data is reset
+ * at the end of the HEAP_DUMP[_SEGMENT]. Multiple HEAP_DUMP_INFO entries
+ * may appear in a single HEAP_DUMP[_SEGMENT].
+ *
+ * Format:
+ * u1: Tag value (0xFE)
+ * u4: heap ID
+ * ID: heap name string ID
+ */
+ private static final int ROOT_HEAP_DUMP_INFO = 0xfe;
+ private static final int ROOT_INTERNED_STRING = 0x89;
+ private static final int ROOT_FINALIZING = 0x8a;
+ private static final int ROOT_DEBUGGER = 0x8b;
+ private static final int ROOT_REFERENCE_CLEANUP = 0x8c;
+ private static final int ROOT_VM_INTERNAL = 0x8d;
+ private static final int ROOT_JNI_MONITOR = 0x8e;
+ private static final int ROOT_UNREACHABLE = 0x90;
+ private static final int ROOT_PRIMITIVE_ARRAY_NODATA= 0xc3;
+
+ DataInputStream mInput;
+ int mIdSize;
+ State mState;
+
+ byte[] mFieldBuffer = new byte[8];
+
+ /*
+ * These are only needed while parsing so are not kept as part of the
+ * heap data.
+ */
+ HashMap<Long, String> mStrings = new HashMap<Long, String>();
+ HashMap<Long, String> mClassNames = new HashMap<Long, String>();
+
+ public HprofParser(DataInputStream in) {
+ mInput = in;
+ }
+
+ public final State parse() {
+ State state = new State();
+ mState = state;
+
+ try {
+ String s = readNullTerminatedString();
+ DataInputStream in = mInput;
+
+ mIdSize = in.readInt();
+ Types.setIdSize(mIdSize);
+
+ in.readLong(); // Timestamp, ignored for now
+
+ while (true) {
+ int tag = in.readUnsignedByte();
+ int timestamp = in.readInt();
+ int length = in.readInt();
+
+ switch (tag) {
+ case STRING_IN_UTF8:
+ loadString(length - 4);
+ break;
+
+ case LOAD_CLASS:
+ loadClass();
+ break;
+
+ case STACK_FRAME:
+ loadStackFrame();
+ break;
+
+ case STACK_TRACE:
+ loadStackTrace();
+ break;
+
+ case HEAP_DUMP:
+ loadHeapDump(length);
+ mState.setToDefaultHeap();
+ break;
+
+ case HEAP_DUMP_SEGMENT:
+ loadHeapDump(length);
+ mState.setToDefaultHeap();
+ break;
+
+ default:
+ skipFully(length);
+ }
+
+ }
+ } catch (EOFException eof) {
+ // this is fine
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ mState.resolveReferences();
+
+ return state;
+ }
+
+ private String readNullTerminatedString() throws IOException {
+ StringBuilder s = new StringBuilder();
+ DataInputStream in = mInput;
+
+ for (int c = in.read(); c != 0; c = in.read()) {
+ s.append((char) c);
+ }
+
+ return s.toString();
+ }
+
+ private long readId() throws IOException {
+ switch (mIdSize) {
+ case 1: return mInput.readUnsignedByte();
+ case 2: return mInput.readUnsignedShort();
+ case 4: return ((long) mInput.readInt()) & 0x00000000ffffffffL;
+ case 8: return mInput.readLong();
+ }
+
+ throw new IllegalArgumentException("ID Length must be 1, 2, 4, or 8");
+ }
+
+ private String readUTF8(int length) throws IOException {
+ byte[] b = new byte[length];
+
+ mInput.read(b);
+
+ return new String(b, "utf-8");
+ }
+
+ private void loadString(int length) throws IOException {
+ long id = readId();
+ String string = readUTF8(length);
+
+ mStrings.put(id, string);
+ }
+
+ private void loadClass() throws IOException {
+ DataInputStream in = mInput;
+ int serial = in.readInt();
+ long id = readId();
+ int stackTrace = in.readInt(); // unused
+ String name = mStrings.get(readId());
+
+ mClassNames.put(id, name);
+ }
+
+ private void loadStackFrame() throws IOException {
+ long id = readId();
+ String methodName = mStrings.get(readId());
+ String methodSignature = mStrings.get(readId());
+ String sourceFile = mStrings.get(readId());
+ int serial = mInput.readInt();
+ int lineNumber = mInput.readInt();
+
+ StackFrame frame = new StackFrame(id, methodName, methodSignature,
+ sourceFile, serial, lineNumber);
+
+ mState.addStackFrame(frame);
+ }
+
+ private void loadStackTrace() throws IOException {
+ int serialNumber = mInput.readInt();
+ int threadSerialNumber = mInput.readInt();
+ final int numFrames = mInput.readInt();
+ StackFrame[] frames = new StackFrame[numFrames];
+
+ for (int i = 0; i < numFrames; i++) {
+ frames[i] = mState.getStackFrame(readId());
+ }
+
+ StackTrace trace = new StackTrace(serialNumber, threadSerialNumber,
+ frames);
+
+ mState.addStackTrace(trace);
+ }
+
+ private void loadHeapDump(int length) throws IOException {
+ DataInputStream in = mInput;
+
+ while (length > 0) {
+ int tag = in.readUnsignedByte();
+ length--;
+
+ switch (tag) {
+ case ROOT_UNKNOWN:
+ length -= loadBasicObj(RootType.UNKNOWN);
+ break;
+
+ case ROOT_JNI_GLOBAL:
+ length -= loadBasicObj(RootType.NATIVE_STATIC);
+ readId(); // ignored
+ length -= mIdSize;
+ break;
+
+ case ROOT_JNI_LOCAL:
+ length -= loadJniLocal();
+ break;
+
+ case ROOT_JAVA_FRAME:
+ length -= loadJavaFrame();
+ break;
+
+ case ROOT_NATIVE_STACK:
+ length -= loadNativeStack();
+ break;
+
+ case ROOT_STICKY_CLASS:
+ length -= loadBasicObj(RootType.SYSTEM_CLASS);
+ break;
+
+ case ROOT_THREAD_BLOCK:
+ length -= loadThreadBlock();
+ break;
+
+ case ROOT_MONITOR_USED:
+ length -= loadBasicObj(RootType.BUSY_MONITOR);
+ break;
+
+ case ROOT_THREAD_OBJECT:
+ length -= loadThreadObject();
+ break;
+
+ case ROOT_CLASS_DUMP:
+ length -= loadClassDump();
+ break;
+
+ case ROOT_INSTANCE_DUMP:
+ length -= loadInstanceDump();
+ break;
+
+ case ROOT_OBJECT_ARRAY_DUMP:
+ length -= loadObjectArrayDump();
+ break;
+
+ case ROOT_PRIMITIVE_ARRAY_DUMP:
+ length -= loadPrimitiveArrayDump();
+ break;
+
+ case ROOT_PRIMITIVE_ARRAY_NODATA:
+ System.err.println("+--- PRIMITIVE ARRAY NODATA DUMP");
+ length -= loadPrimitiveArrayDump();
+
+ throw new IllegalArgumentException(
+ "Don't know how to load a nodata array");
+
+ case ROOT_HEAP_DUMP_INFO:
+ int heapId = mInput.readInt();
+ long heapNameId = readId();
+ String heapName = mStrings.get(heapNameId);
+
+ mState.setHeapTo(heapId, heapName);
+ length -= 4 + mIdSize;
+ break;
+
+ case ROOT_INTERNED_STRING:
+ length -= loadBasicObj(RootType.INTERNED_STRING);
+ break;
+
+ case ROOT_FINALIZING:
+ length -= loadBasicObj(RootType.FINALIZING);
+ break;
+
+ case ROOT_DEBUGGER:
+ length -= loadBasicObj(RootType.DEBUGGER);
+ break;
+
+ case ROOT_REFERENCE_CLEANUP:
+ length -= loadBasicObj(RootType.REFERENCE_CLEANUP);
+ break;
+
+ case ROOT_VM_INTERNAL:
+ length -= loadBasicObj(RootType.VM_INTERNAL);
+ break;
+
+ case ROOT_JNI_MONITOR:
+ length -= loadJniMonitor();
+ break;
+
+ case ROOT_UNREACHABLE:
+ length -= loadBasicObj(RootType.UNREACHABLE);
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "loadHeapDump loop with unknown tag " + tag
+ + " with " + mInput.available()
+ + " bytes possibly remaining");
+ }
+ }
+ }
+
+ private int loadJniLocal() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ int stackFrameNumber = mInput.readInt();
+ ThreadObj thread = mState.getThread(threadSerialNumber);
+ StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
+ stackFrameNumber);
+ RootObj root = new RootObj(RootType.NATIVE_LOCAL, id,
+ threadSerialNumber, trace);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize + 4 + 4;
+ }
+
+ private int loadJavaFrame() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ int stackFrameNumber = mInput.readInt();
+ ThreadObj thread = mState.getThread(threadSerialNumber);
+ StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
+ stackFrameNumber);
+ RootObj root = new RootObj(RootType.JAVA_LOCAL, id, threadSerialNumber,
+ trace);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize + 4 + 4;
+ }
+
+ private int loadNativeStack() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ ThreadObj thread = mState.getThread(threadSerialNumber);
+ StackTrace trace = mState.getStackTrace(thread.mStackTrace);
+ RootObj root = new RootObj(RootType.NATIVE_STACK, id,
+ threadSerialNumber, trace);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize + 4;
+ }
+
+ private int loadBasicObj(RootType type) throws IOException {
+ long id = readId();
+ RootObj root = new RootObj(type, id);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize;
+ }
+
+ private int loadThreadBlock() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ ThreadObj thread = mState.getThread(threadSerialNumber);
+ StackTrace stack = mState.getStackTrace(thread.mStackTrace);
+ RootObj root = new RootObj(RootType.THREAD_BLOCK, id,
+ threadSerialNumber, stack);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize + 4;
+ }
+
+ private int loadThreadObject() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ int stackSerialNumber = mInput.readInt();
+ ThreadObj thread = new ThreadObj(id, stackSerialNumber);
+
+ mState.addThread(thread, threadSerialNumber);
+
+ return mIdSize + 4 + 4;
+ }
+
+ private int loadClassDump() throws IOException {
+ int bytesRead = 0;
+ DataInputStream in = mInput;
+ long id = readId();
+ int stackSerialNumber = in.readInt();
+ StackTrace stack = mState.getStackTrace(stackSerialNumber);
+ long superClassId = readId();
+ long classLoaderId = readId();
+ long signersId = readId();
+ long protectionDomainId = readId();
+ long reserved1 = readId();
+ long reserved2 = readId();
+ int instanceSize = in.readInt();
+
+ bytesRead = (7 * mIdSize) + 4 + 4;
+
+ // Skip over the constant pool
+ int numEntries = in.readUnsignedShort();
+ bytesRead += 2;
+
+ for (int i = 0; i < numEntries; i++) {
+ in.readUnsignedShort();
+ bytesRead += 2 + skipValue();
+ }
+
+ // Static fields
+ numEntries = in.readUnsignedShort();
+ bytesRead += 2;
+
+ String[] staticFieldNames = new String[numEntries];
+ int[] staticFieldTypes = new int[numEntries];
+ ByteArrayOutputStream staticFieldValues = new ByteArrayOutputStream();
+ byte[] buffer = mFieldBuffer;
+
+ for (int i = 0; i < numEntries; i++) {
+ staticFieldNames[i] = mStrings.get(readId());
+
+ int fieldType = in.readByte();
+ int fieldSize = Types.getTypeSize(fieldType);
+ staticFieldTypes[i] = fieldType;
+
+ in.readFully(buffer, 0, fieldSize);
+ staticFieldValues.write(buffer, 0, fieldSize);
+
+ bytesRead += mIdSize + 1 + fieldSize;
+ }
+
+ // Instance fields
+ numEntries = in.readUnsignedShort();
+ bytesRead += 2;
+
+ String[] names = new String[numEntries];
+ int[] types = new int[numEntries];
+
+ for (int i = 0; i < numEntries; i++) {
+ long fieldName = readId();
+ int type = in.readUnsignedByte();
+
+ names[i] = mStrings.get(fieldName);
+ types[i] = type;
+
+ bytesRead += mIdSize + 1;
+ }
+
+ ClassObj theClass = new ClassObj(id, stack, mClassNames.get(id));
+
+ theClass.setStaticFieldNames(staticFieldNames);
+ theClass.setStaticFieldTypes(staticFieldTypes);
+ theClass.setStaticFieldValues(staticFieldValues.toByteArray());
+
+ theClass.setSuperclassId(superClassId);
+ theClass.setFieldNames(names);
+ theClass.setFieldTypes(types);
+ theClass.setSize(instanceSize);
+
+ theClass.setHeap(mState.mCurrentHeap);
+
+ mState.addClass(id, theClass);
+
+ return bytesRead;
+ }
+
+ private int loadInstanceDump() throws IOException {
+ long id = readId();
+ int stackId = mInput.readInt();
+ StackTrace stack = mState.getStackTrace(stackId);
+ long classId = readId();
+ int remaining = mInput.readInt();
+ ClassInstance instance = new ClassInstance(id, stack, classId);
+
+ instance.loadFieldData(mInput, remaining);
+ instance.setHeap(mState.mCurrentHeap);
+ mState.addInstance(id, instance);
+
+ return mIdSize + 4 + mIdSize + 4 + remaining;
+ }
+
+ private int loadObjectArrayDump() throws IOException {
+ long id = readId();
+ int stackId = mInput.readInt();
+ StackTrace stack = mState.getStackTrace(stackId);
+ int numElements = mInput.readInt();
+ long classId = readId();
+ int totalBytes = numElements * mIdSize;
+ byte[] data = new byte[totalBytes];
+ String className = mClassNames.get(classId);
+
+ mInput.readFully(data);
+
+ ArrayInstance array = new ArrayInstance(id, stack, Types.OBJECT,
+ numElements, data);
+
+ array.mClassId = classId;
+ array.setHeap(mState.mCurrentHeap);
+ mState.addInstance(id, array);
+
+ return mIdSize + 4 + 4 + mIdSize + totalBytes;
+ }
+
+ private int loadPrimitiveArrayDump() throws IOException {
+ long id = readId();
+ int stackId = mInput.readInt();
+ StackTrace stack = mState.getStackTrace(stackId);
+ int numElements = mInput.readInt();
+ int type = mInput.readUnsignedByte();
+ int size = Types.getTypeSize(type);
+ int totalBytes = numElements * size;
+ byte[] data = new byte[totalBytes];
+
+ mInput.readFully(data);
+
+ ArrayInstance array = new ArrayInstance(id, stack, type, numElements,
+ data);
+
+ array.setHeap(mState.mCurrentHeap);
+ mState.addInstance(id, array);
+
+ return mIdSize + 4 + 4 + 1 + totalBytes;
+ }
+
+ private int loadJniMonitor() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ int stackDepth = mInput.readInt();
+ ThreadObj thread = mState.getThread(threadSerialNumber);
+ StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
+ stackDepth);
+ RootObj root = new RootObj(RootType.NATIVE_MONITOR, id,
+ threadSerialNumber, trace);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize + 4 + 4;
+ }
+
+ private int skipValue() throws IOException {
+ int type = mInput.readUnsignedByte();
+ int size = Types.getTypeSize(type);
+
+ skipFully(size);
+
+ return size + 1;
+ }
+
+ /*
+ * BufferedInputStream will not skip(int) the entire requested number
+ * of bytes if it extends past the current buffer boundary. So, this
+ * routine is needed to actually skip over the requested number of bytes
+ * using as many iterations as needed.
+ */
+ private void skipFully(long numBytes) throws IOException {
+ while (numBytes > 0) {
+ long skipped = mInput.skip(numBytes);
+
+ numBytes -= skipped;
+ }
+ }
+}
diff --git a/hit/src/com/android/hit/Instance.java b/hit/src/com/android/hit/Instance.java
new file mode 100644
index 0000000..6afa2b2
--- /dev/null
+++ b/hit/src/com/android/hit/Instance.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class Instance {
+ long mId;
+
+ // Id of the ClassObj of which this object is an instance
+ long mClassId;
+
+ // The stack in which this object was allocated
+ StackTrace mStack;
+
+ // The heap in which this object was allocated (app, zygote, etc)
+ Heap mHeap;
+
+ // The size of this object
+ int mSize;
+
+ public interface Filter {
+ public boolean accept(Instance instance);
+ }
+
+ // List of all objects that hold a live reference to this object
+ private ArrayList<Instance> mParents;
+
+ /*
+ * After the whole HPROF file is read and parsed this method will be
+ * called on all heap objects so that they can resolve their internal
+ * object references.
+ *
+ * The super-State is passed in because some object references (such
+ * as interned Strings and static class fields) may need to be searched
+ * for in a heap other than the one this Instance is in.
+ */
+ public abstract void resolveReferences(State state);
+
+ /*
+ * Some operations require gathering all the objects in a given section
+ * of the object graph. If non-null, the filter is applied to each
+ * node in the graph to determine if it should be added to the result
+ * set.
+ */
+ public abstract void visit(Set<Instance> resultSet, Filter filter);
+
+ public void setSize(int size) {
+ mSize = size;
+ }
+
+ public final int getCompositeSize() {
+ HashSet<Instance> set = new HashSet<Instance>();
+
+ visit(set, null);
+
+ int size = 0;
+
+ for (Instance instance: set) {
+ size += instance.getSize();
+ }
+
+ return size;
+ }
+
+ // Returns the instrinsic size of a given object
+ public int getSize() {
+ return mSize;
+ }
+
+ public abstract String getTypeName();
+
+ public void setHeap(Heap heap) {
+ mHeap = heap;
+ }
+
+ // Add to the list of objects that have a hard reference to this Instance
+ public void addParent(Instance parent) {
+ if (mParents == null) {
+ mParents = new ArrayList<Instance>();
+ }
+
+ mParents.add(parent);
+ }
+
+ public ArrayList<Instance> getParents() {
+ if (mParents == null) {
+ mParents = new ArrayList<Instance>();
+ }
+
+ return mParents;
+ }
+
+ /*
+ * If this object has a reference to the object identified by id, return
+ * a String describing the reference in detail.
+ */
+ public String describeReferenceTo(long id) {
+ return "No reference to 0x" + Long.toHexString(id);
+ }
+}
diff --git a/hit/src/com/android/hit/Main.java b/hit/src/com/android/hit/Main.java
new file mode 100644
index 0000000..4ed5c11
--- /dev/null
+++ b/hit/src/com/android/hit/Main.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.util.Map;
+import java.util.Set;
+
+public class Main
+{
+ public static void main(String argv[]) {
+ FileInputStream fis;
+ BufferedInputStream bis;
+ DataInputStream dis;
+
+ try {
+ fis = new FileInputStream(argv[0]);
+ bis = new BufferedInputStream(fis);
+ dis = new DataInputStream(bis);
+
+ State state = (new HprofParser(dis)).parse();
+
+ dis.close();
+
+ testClassesQuery(state);
+ testAllClassesQuery(state);
+ testFindInstancesOf(state);
+ testFindAllInstancesOf(state);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void testClassesQuery(State state) {
+ String[] x = new String[] {
+ "char[",
+ "javax.",
+ "org.xml.sax"
+ };
+
+ Map<String, Set<ClassObj>> someClasses = Queries.classes(state, x);
+
+ for (String thePackage: someClasses.keySet()) {
+ System.out.println("------------------- " + thePackage);
+
+ Set<ClassObj> classes = someClasses.get(thePackage);
+
+ for (ClassObj theClass: classes) {
+ System.out.println(" " + theClass.mClassName);
+ }
+ }
+ }
+
+ private static void testAllClassesQuery(State state) {
+ Map<String, Set<ClassObj>> allClasses = Queries.allClasses(state);
+
+ for (String thePackage: allClasses.keySet()) {
+ System.out.println("------------------- " + thePackage);
+
+ Set<ClassObj> classes = allClasses.get(thePackage);
+
+ for (ClassObj theClass: classes) {
+ System.out.println(" " + theClass.mClassName);
+ }
+ }
+ }
+
+ private static void testFindInstancesOf(State state) {
+ Instance[] instances = Queries.instancesOf(state, "java.lang.String");
+
+ System.out.println("There are " + instances.length + " Strings.");
+ }
+
+ private static void testFindAllInstancesOf(State state) {
+ Instance[] instances = Queries.allInstancesOf(state,
+ "android.graphics.drawable.Drawable");
+
+ System.out.println("There are " + instances.length
+ + " instances of Drawables and its subclasses.");
+ }
+}
diff --git a/hit/src/com/android/hit/Queries.java b/hit/src/com/android/hit/Queries.java
new file mode 100644
index 0000000..0dac796
--- /dev/null
+++ b/hit/src/com/android/hit/Queries.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+public class Queries {
+ /*
+ * NOTES: Here's a list of the queries that can be done in hat and
+ * how you'd perform a similar query here in hit:
+ *
+ * hat hit
+ * ------------------------------------------------------------------------
+ * allClasses classes
+ * allClassesWithPlatform allClasses
+ * class findClass
+ * instances instancesOf
+ * allInstances allInstancesOf
+ * object findObject
+ * showRoots getRoots
+ * newInstances newInstances
+ *
+ * reachableFrom make a call to findObject to get the target
+ * parent object, this will give you an Instance.
+ * Then call visit(Set, Filter) on that to have
+ * it build the set of objects in its subgraph.
+ *
+ * rootsTo make a call to findObject on the leaf node
+ * in question, this will give you an Instance.
+ * Instances have an ArrayList of all of the
+ * parent objects that refer to it. You can
+ * follow those parent links until you hit an
+ * object whose parent is null or a ThreadObj.
+ * You've not successfully traced the paths to
+ * the roots.
+ */
+
+ private static final String DEFAULT_PACKAGE = "<default>";
+
+ /*
+ * Produce a collection of all classes, broken down by package.
+ * The keys of the resultant map iterate in sorted package order.
+ * The values of the map are the classes defined in each package.
+ */
+ public static Map<String, Set<ClassObj>> allClasses(State state) {
+ return classes(state, null);
+ }
+
+ public static Map<String, Set<ClassObj>> classes(State state,
+ String[] excludedPrefixes) {
+ TreeMap<String, Set<ClassObj>> result =
+ new TreeMap<String, Set<ClassObj>>();
+
+ Set<ClassObj> classes = new TreeSet<ClassObj>();
+
+ // Build a set of all classes across all heaps
+ for (Heap heap: state.mHeaps.values()) {
+ classes.addAll(heap.mClassesById.values());
+ }
+
+ // Filter it if needed
+ if (excludedPrefixes != null) {
+ final int N = excludedPrefixes.length;
+ Iterator<ClassObj> iter = classes.iterator();
+
+ while (iter.hasNext()) {
+ ClassObj theClass = iter.next();
+ String classPath = theClass.toString();
+
+ for (int i = 0; i < N; i++) {
+ if (classPath.startsWith(excludedPrefixes[i])) {
+ iter.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ // Now that we have a final list of classes, group them by package
+ for (ClassObj theClass: classes) {
+ String packageName = DEFAULT_PACKAGE;
+ int lastDot = theClass.mClassName.lastIndexOf('.');
+
+ if (lastDot != -1) {
+ packageName = theClass.mClassName.substring(0, lastDot);
+ }
+
+ Set<ClassObj> classSet = result.get(packageName);
+
+ if (classSet == null) {
+ classSet = new TreeSet<ClassObj>();
+ result.put(packageName, classSet);
+ }
+
+ classSet.add(theClass);
+ }
+
+ return result;
+ }
+
+ /*
+ * It's sorta sad that this is a pass-through call, but it seems like
+ * having all of the hat-like query methods in one place is a good thing
+ * even if there is duplication of effort.
+ */
+ public static ClassObj findClass(State state, String name) {
+ return state.findClass(name);
+ }
+
+ /*
+ * Return an array of instances of the given class. This does not include
+ * instances of subclasses.
+ */
+ public static Instance[] instancesOf(State state, String baseClassName) {
+ ClassObj theClass = state.findClass(baseClassName);
+
+ if (theClass == null) {
+ throw new IllegalArgumentException("Class not found: "
+ + baseClassName);
+ }
+
+ Instance[] instances = new Instance[theClass.mInstances.size()];
+
+ return theClass.mInstances.toArray(instances);
+ }
+
+ /*
+ * Return an array of instances of the given class. This includes
+ * instances of subclasses.
+ */
+ public static Instance[] allInstancesOf(State state, String baseClassName) {
+ ClassObj theClass = state.findClass(baseClassName);
+
+ if (theClass == null) {
+ throw new IllegalArgumentException("Class not found: "
+ + baseClassName);
+ }
+
+ ArrayList<ClassObj> classList = new ArrayList<ClassObj>();
+
+ classList.add(theClass);
+ classList.addAll(traverseSubclasses(theClass));
+
+ ArrayList<Instance> instanceList = new ArrayList<Instance>();
+
+ for (ClassObj someClass: classList) {
+ instanceList.addAll(someClass.mInstances);
+ }
+
+ Instance[] result = new Instance[instanceList.size()];
+
+ instanceList.toArray(result);
+
+ return result;
+ }
+
+ private static ArrayList<ClassObj> traverseSubclasses(ClassObj base) {
+ ArrayList<ClassObj> result = new ArrayList<ClassObj>();
+
+ for (ClassObj subclass: base.mSubclasses) {
+ result.add(subclass);
+ result.addAll(traverseSubclasses(subclass));
+ }
+
+ return result;
+ }
+
+ /*
+ * Find a reference to an object based on its id. The id should be
+ * in hexadecimal.
+ */
+ public static Instance findObject(State state, String id) {
+ long id2 = Long.parseLong(id, 16);
+
+ return state.findReference(id2);
+ }
+
+ public static Collection<RootObj> getRoots(State state) {
+ HashSet<RootObj> result = new HashSet<RootObj>();
+
+ for (Heap heap: state.mHeaps.values()) {
+ result.addAll(heap.mRoots);
+ }
+
+ return result;
+ }
+
+ public static final Instance[] newInstances(State older, State newer) {
+ ArrayList<Instance> resultList = new ArrayList<Instance>();
+
+ for (Heap newHeap: newer.mHeaps.values()) {
+ Heap oldHeap = older.getHeap(newHeap.mName);
+
+ if (oldHeap == null) {
+ continue;
+ }
+
+ for (Instance instance: newHeap.mInstances.values()) {
+ Instance oldInstance = oldHeap.getInstance(instance.mId);
+
+ /*
+ * If this instance wasn't in the old heap, or was there,
+ * but that ID was for an obj of a different type, then we have
+ * a newly allocated object and we should report it in the
+ * results.
+ */
+ if ((oldInstance == null)
+ || (instance.mClassId != oldInstance.mClassId)) {
+ resultList.add(instance);
+ }
+ }
+ }
+
+ Instance[] resultArray = new Instance[resultList.size()];
+
+ return resultList.toArray(resultArray);
+ }
+}
diff --git a/hit/src/com/android/hit/RootObj.java b/hit/src/com/android/hit/RootObj.java
new file mode 100644
index 0000000..a9acd35
--- /dev/null
+++ b/hit/src/com/android/hit/RootObj.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.Set;
+
+public class RootObj extends Instance {
+ RootType mType = RootType.UNKNOWN;
+ int mIndex;
+ int mThread;
+
+ /*
+ * These two fields are only used by roots that are static
+ * fields of class objects
+ */
+ long mParent;
+ String mComment;
+
+ public RootObj(RootType type) {
+ this(type, 0, 0, null);
+ }
+
+ public RootObj(RootType type, long id) {
+ this(type, id, 0, null);
+ }
+
+ public RootObj(RootType type, long id, int thread, StackTrace stack) {
+ mType = type;
+ mId = id;
+ mThread = thread;
+ mStack = stack;
+ }
+
+ public final String getClassName(State state) {
+ ClassObj theClass;
+
+ if (mType == RootType.SYSTEM_CLASS) {
+ theClass = state.findClass(mId);
+ } else {
+ Instance instance = state.findReference(mId);
+
+ theClass = state.findClass(instance.mClassId);
+ }
+
+ if (theClass == null) {
+ return "no class defined!!";
+ }
+
+ return theClass.mClassName;
+ }
+
+ @Override
+ public final int getSize() {
+ Instance instance = null;
+
+ if (mType == RootType.SYSTEM_CLASS) {
+ instance = mHeap.mState.findClass(mId);
+ } else {
+ instance = mHeap.mState.findReference(mId);
+ }
+
+ if (instance == null) {
+ return 0;
+ }
+
+ return instance.getSize();
+ }
+
+ @Override
+ public final void visit(Set<Instance> resultSet, Filter filter) {
+ if (resultSet.contains(this)) {
+ return;
+ }
+
+ if (filter != null) {
+ if (filter.accept(this)) {
+ resultSet.add(this);
+ }
+ } else {
+ resultSet.add(this);
+ }
+ }
+
+ @Override
+ public final void resolveReferences(State state) {
+ // Nothing to do here
+ }
+
+ @Override
+ public final String getTypeName() {
+ return "root " + mType.getName();
+ }
+
+ public final String toString() {
+ return String.format("%s@0x08x", mType.getName(), mId);
+ }
+}
diff --git a/hit/src/com/android/hit/RootType.java b/hit/src/com/android/hit/RootType.java
new file mode 100644
index 0000000..209ed1b
--- /dev/null
+++ b/hit/src/com/android/hit/RootType.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public enum RootType {
+ UNREACHABLE (0, "unreachable object"),
+ INVALID_TYPE (1, "invalid type"),
+ INTERNED_STRING (2, "interned string"),
+ UNKNOWN (3, "unknown"),
+ SYSTEM_CLASS (4, "system class"),
+ VM_INTERNAL (5, "vm internal"),
+ DEBUGGER (6, "debugger"),
+ NATIVE_LOCAL (7, "native local"),
+ NATIVE_STATIC (8, "native static"),
+ THREAD_BLOCK (9, "thread block"),
+ BUSY_MONITOR (10, "busy monitor"),
+ NATIVE_MONITOR (11, "native monitor"),
+ REFERENCE_CLEANUP (12, "reference cleanup"),
+ FINALIZING (13, "finalizing"),
+ JAVA_LOCAL (14, "java local"),
+ NATIVE_STACK (15, "native stack"),
+ JAVA_STATIC (16, "java static");
+
+ private final int mType;
+ private final String mName;
+
+ RootType(int type, String name) {
+ mType = type;
+ mName = name;
+ }
+
+ public final int getType() {
+ return mType;
+ }
+
+ public final String getName() {
+ return mName;
+ }
+}
diff --git a/hit/src/com/android/hit/StackFrame.java b/hit/src/com/android/hit/StackFrame.java
new file mode 100644
index 0000000..2ae7f32
--- /dev/null
+++ b/hit/src/com/android/hit/StackFrame.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class StackFrame {
+ public static final int NO_LINE_NUMBER = 0;
+ public static final int UNKNOWN_LOCATION = -1;
+ public static final int COMPILED_METHOD = -2;
+ public static final int NATIVE_METHOD = -3;
+
+ long mId;
+ String mMethodName;
+ String mSignature;
+ String mFilename;
+ int mSerialNumber;
+ int mLineNumber;
+
+ public StackFrame(long id, String method, String sig, String file,
+ int serial, int line) {
+ mId = id;
+ mMethodName = method;
+ mSignature = sig;
+ mFilename = file;
+ mSerialNumber = serial;
+ mLineNumber = line;
+ }
+
+ private final String lineNumberString() {
+ switch (mLineNumber) {
+ case NO_LINE_NUMBER: return "No line number";
+ case UNKNOWN_LOCATION: return "Unknown line number";
+ case COMPILED_METHOD: return "Compiled method";
+ case NATIVE_METHOD: return "Native method";
+
+ default: return String.valueOf(mLineNumber);
+ }
+ }
+
+ public final String toString() {
+ return mMethodName
+ + mSignature.replace('/', '.')
+ + " - "
+ + mFilename + ":"
+ + lineNumberString();
+ }
+}
diff --git a/hit/src/com/android/hit/StackTrace.java b/hit/src/com/android/hit/StackTrace.java
new file mode 100644
index 0000000..53cb86d
--- /dev/null
+++ b/hit/src/com/android/hit/StackTrace.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class StackTrace {
+ int mSerialNumber;
+ int mThreadSerialNumber;
+ StackFrame[] mFrames;
+
+ /*
+ * For subsets of the stack frame we'll reference the parent list of frames
+ * but keep track of its offset into the parent's list of stack frame ids.
+ * This alleviates the need to constantly be duplicating subsections of the
+ * list of stack frame ids.
+ */
+ StackTrace mParent = null;
+ int mOffset = 0;
+
+ private StackTrace() {
+
+ }
+
+ public StackTrace(int serial, int thread, StackFrame[] frames) {
+ mSerialNumber = serial;
+ mThreadSerialNumber = thread;
+ mFrames = frames;
+ }
+
+ public final StackTrace fromDepth(int startingDepth) {
+ StackTrace result = new StackTrace();
+
+ if (mParent != null) {
+ result.mParent = mParent;
+ } else {
+ result.mParent = this;
+ }
+
+ result.mOffset = startingDepth + mOffset;
+
+ return result;
+ }
+
+ public final void dump() {
+ final int N = mFrames.length;
+
+ for (int i = 0; i < N; i++) {
+ System.out.println(mFrames[i].toString());
+ }
+ }
+}
diff --git a/hit/src/com/android/hit/State.java b/hit/src/com/android/hit/State.java
new file mode 100644
index 0000000..96c944d
--- /dev/null
+++ b/hit/src/com/android/hit/State.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/*
+ * State is a snapshot of all of the heaps, and related meta-data, for
+ * the runtime at a given instant.
+ *
+ * During parsing of the HPROF file HEAP_DUMP_INFO chunks change which heap
+ * is being referenced.
+ */
+public class State {
+ HashMap<Integer, Heap> mHeaps;
+ Heap mCurrentHeap;
+
+ public State() {
+ mHeaps = new HashMap<Integer, Heap>();
+ setToDefaultHeap();
+ }
+
+ public Heap setToDefaultHeap() {
+ return setHeapTo(0, "default");
+ }
+
+ public Heap setHeapTo(int id, String name) {
+ Heap heap = mHeaps.get(id);
+
+ if (heap == null) {
+ heap = new Heap(name);
+ heap.mState = this;
+ mHeaps.put(id, heap);
+ }
+
+ mCurrentHeap = heap;
+
+ return mCurrentHeap;
+ }
+
+ public Heap getHeap(int id) {
+ return mHeaps.get(id);
+ }
+
+ public Heap getHeap(String name) {
+ for (Heap heap: mHeaps.values()) {
+ if (heap.mName.equals(name)) {
+ return heap;
+ }
+ }
+
+ return null;
+ }
+
+ public final void addStackFrame(StackFrame theFrame) {
+ mCurrentHeap.addStackFrame(theFrame);
+ }
+
+ public final StackFrame getStackFrame(long id) {
+ return mCurrentHeap.getStackFrame(id);
+ }
+
+ public final void addStackTrace(StackTrace theTrace) {
+ mCurrentHeap.addStackTrace(theTrace);
+ }
+
+ public final StackTrace getStackTrace(int traceSerialNumber) {
+ return mCurrentHeap.getStackTrace(traceSerialNumber);
+ }
+
+ public final StackTrace getStackTraceAtDepth(int traceSerialNumber,
+ int depth) {
+ return mCurrentHeap.getStackTraceAtDepth(traceSerialNumber, depth);
+ }
+
+ public final void addRoot(RootObj root) {
+ mCurrentHeap.addRoot(root);
+ }
+
+ public final void addThread(ThreadObj thread, int serialNumber) {
+ mCurrentHeap.addThread(thread, serialNumber);
+ }
+
+ public final ThreadObj getThread(int serialNumber) {
+ return mCurrentHeap.getThread(serialNumber);
+ }
+
+ public final void addInstance(long id, Instance instance) {
+ mCurrentHeap.addInstance(id, instance);
+ }
+
+ public final void addClass(long id, ClassObj theClass) {
+ mCurrentHeap.addClass(id, theClass);
+ }
+
+ public final Instance findReference(long id) {
+ for (Heap heap: mHeaps.values()) {
+ Instance instance = heap.getInstance(id);
+
+ if (instance != null) {
+ return instance;
+ }
+ }
+
+ // Couldn't find an instance of a class, look for a class object
+ return findClass(id);
+ }
+
+ public final ClassObj findClass(long id) {
+ for (Heap heap: mHeaps.values()) {
+ ClassObj theClass = heap.getClass(id);
+
+ if (theClass != null) {
+ return theClass;
+ }
+ }
+
+ return null;
+ }
+
+ public final ClassObj findClass(String name) {
+ for (Heap heap: mHeaps.values()) {
+ ClassObj theClass = heap.getClass(name);
+
+ if (theClass != null) {
+ return theClass;
+ }
+ }
+
+ return null;
+ }
+
+ public final void dumpInstanceCounts() {
+ for (Heap heap: mHeaps.values()) {
+ System.out.println(
+ "+------------------ instance counts for heap: " + heap.mName);
+ heap.dumpInstanceCounts();
+ }
+ }
+
+ public final void dumpSizes() {
+ for (Heap heap: mHeaps.values()) {
+ System.out.println(
+ "+------------------ sizes for heap: " + heap.mName);
+ heap.dumpSizes();
+ }
+ }
+
+ public final void dumpSubclasses() {
+ for (Heap heap: mHeaps.values()) {
+ System.out.println(
+ "+------------------ subclasses for heap: " + heap.mName);
+ heap.dumpSubclasses();
+ }
+ }
+
+ public final void resolveReferences() {
+ for (Heap heap: mHeaps.values()) {
+ heap.resolveInstanceRefs(this);
+ heap.resolveClassStatics(this);
+ heap.resolveRoots(this);
+ }
+ }
+}
diff --git a/hit/src/com/android/hit/ThreadObj.java b/hit/src/com/android/hit/ThreadObj.java
new file mode 100644
index 0000000..32a73f0
--- /dev/null
+++ b/hit/src/com/android/hit/ThreadObj.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class ThreadObj {
+ long mId;
+ int mStackTrace;
+
+ public ThreadObj(long id, int stackTrace) {
+ mId = id;
+ mStackTrace = stackTrace;
+ }
+}
diff --git a/hit/src/com/android/hit/Types.java b/hit/src/com/android/hit/Types.java
new file mode 100644
index 0000000..62228ce
--- /dev/null
+++ b/hit/src/com/android/hit/Types.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class Types {
+ private static int mIdSize = 4;
+
+ public static final int OBJECT = 2;
+ public static final int BOOLEAN = 4;
+ public static final int CHAR = 5;
+ public static final int FLOAT = 6;
+ public static final int DOUBLE = 7;
+ public static final int BYTE = 8;
+ public static final int SHORT = 9;
+ public static final int INT = 10;
+ public static final int LONG = 11;
+
+ public static final void setIdSize(int size) {
+ mIdSize = size;
+ }
+
+ public static final int getTypeSize(int type) {
+ switch (type) {
+ case '[': return mIdSize; // array object
+ case 'L': return mIdSize; // object
+ case 'Z': return 1; // boolean
+ case 'C': return 2; // char
+ case 'F': return 4; // float
+ case 'D': return 8; // double
+ case 'B': return 1; // byte
+ case 'S': return 2; // short
+ case 'I': return 4; // int
+ case 'J': return 8; // long
+
+ case OBJECT: return mIdSize;
+ case BOOLEAN: return 1;
+ case CHAR: return 2;
+ case FLOAT: return 4;
+ case DOUBLE: return 8;
+ case BYTE: return 1;
+ case SHORT: return 2;
+ case INT: return 4;
+ case LONG: return 8;
+ }
+
+ throw new IllegalArgumentException("Illegal type signature: " + type);
+ }
+
+ public static final String getTypeName(int type) {
+ switch (type) {
+ case '[': return "array";
+ case 'L': return "object";
+ case 'Z': return "boolean";
+ case 'C': return "char";
+ case 'F': return "float";
+ case 'D': return "double";
+ case 'B': return "byte";
+ case 'S': return "short";
+ case 'I': return "int";
+ case 'J': return "long";
+
+ case OBJECT: return "object";
+ case BOOLEAN: return "boolean";
+ case CHAR: return "char";
+ case FLOAT: return "float";
+ case DOUBLE: return "double";
+ case BYTE: return "byte";
+ case SHORT: return "short";
+ case INT: return "int";
+ case LONG: return "long";
+ }
+
+ throw new IllegalArgumentException("Illegal type signature: " + type);
+ }
+}