summaryrefslogtreecommitdiff
path: root/dalvik
diff options
context:
space:
mode:
Diffstat (limited to 'dalvik')
-rw-r--r--dalvik/MODULE_LICENSE_APACHE20
-rw-r--r--dalvik/src/main/java/dalvik/annotation/AndroidOnly.java39
-rw-r--r--dalvik/src/main/java/dalvik/annotation/AnnotationDefault.java30
-rw-r--r--dalvik/src/main/java/dalvik/annotation/BrokenTest.java38
-rw-r--r--dalvik/src/main/java/dalvik/annotation/EnclosingClass.java30
-rw-r--r--dalvik/src/main/java/dalvik/annotation/EnclosingMethod.java30
-rw-r--r--dalvik/src/main/java/dalvik/annotation/InnerClass.java30
-rw-r--r--dalvik/src/main/java/dalvik/annotation/KnownFailure.java40
-rw-r--r--dalvik/src/main/java/dalvik/annotation/MemberClasses.java30
-rw-r--r--dalvik/src/main/java/dalvik/annotation/SideEffect.java40
-rw-r--r--dalvik/src/main/java/dalvik/annotation/Signature.java30
-rw-r--r--dalvik/src/main/java/dalvik/annotation/TestLevel.java85
-rw-r--r--dalvik/src/main/java/dalvik/annotation/TestTarget.java59
-rw-r--r--dalvik/src/main/java/dalvik/annotation/TestTargetClass.java47
-rw-r--r--dalvik/src/main/java/dalvik/annotation/TestTargetNew.java64
-rw-r--r--dalvik/src/main/java/dalvik/annotation/TestTargets.java39
-rw-r--r--dalvik/src/main/java/dalvik/annotation/Throws.java30
-rw-r--r--dalvik/src/main/java/dalvik/annotation/ToBeFixed.java43
-rw-r--r--dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java81
-rw-r--r--dalvik/src/main/java/dalvik/bytecode/Opcodes.java452
-rw-r--r--dalvik/src/main/java/dalvik/system/AllocationLimitError.java41
-rw-r--r--dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java132
-rw-r--r--dalvik/src/main/java/dalvik/system/BlockGuard.java157
-rw-r--r--dalvik/src/main/java/dalvik/system/CloseGuard.java230
-rw-r--r--dalvik/src/main/java/dalvik/system/DalvikLogHandler.java47
-rw-r--r--dalvik/src/main/java/dalvik/system/DalvikLogging.java48
-rw-r--r--dalvik/src/main/java/dalvik/system/DexClassLoader.java59
-rw-r--r--dalvik/src/main/java/dalvik/system/DexFile.java302
-rw-r--r--dalvik/src/main/java/dalvik/system/DexPathList.java473
-rw-r--r--dalvik/src/main/java/dalvik/system/NativeStart.java39
-rw-r--r--dalvik/src/main/java/dalvik/system/PathClassLoader.java67
-rw-r--r--dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java41
-rw-r--r--dalvik/src/main/java/dalvik/system/SocketTagger.java80
-rw-r--r--dalvik/src/main/java/dalvik/system/StaleDexCacheError.java41
-rw-r--r--dalvik/src/main/java/dalvik/system/TemporaryDirectory.java38
-rw-r--r--dalvik/src/main/java/dalvik/system/VMDebug.java376
-rw-r--r--dalvik/src/main/java/dalvik/system/VMRuntime.java220
-rw-r--r--dalvik/src/main/java/dalvik/system/VMStack.java84
-rw-r--r--dalvik/src/main/java/dalvik/system/Zygote.java206
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java99
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java148
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java480
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java253
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java51
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java182
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/HprofData.java394
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/MalformedHprofException.java35
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java43
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java490
-rw-r--r--dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java44
-rw-r--r--dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java68
-rw-r--r--dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/Chunk.java64
-rw-r--r--dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/ChunkHandler.java137
-rw-r--r--dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java173
-rw-r--r--dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java84
-rw-r--r--dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/README.txt1
-rw-r--r--dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp68
-rw-r--r--dalvik/src/main/native/sub.mk17
-rw-r--r--dalvik/src/test/java/dalvik/system/profiler/SamplingProfilerTest.java309
59 files changed, 7058 insertions, 0 deletions
diff --git a/dalvik/MODULE_LICENSE_APACHE2 b/dalvik/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dalvik/MODULE_LICENSE_APACHE2
diff --git a/dalvik/src/main/java/dalvik/annotation/AndroidOnly.java b/dalvik/src/main/java/dalvik/annotation/AndroidOnly.java
new file mode 100644
index 0000000..da3c1c5
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/AndroidOnly.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test-case as Android-only, that is, it should not be executed on
+ * other systems.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface AndroidOnly {
+
+ /**
+ * Plain text reason for adding this annotation.
+ */
+ String value();
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/AnnotationDefault.java b/dalvik/src/main/java/dalvik/annotation/AnnotationDefault.java
new file mode 100644
index 0000000..98062fb
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/AnnotationDefault.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the AnnotationDefault attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface AnnotationDefault {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/BrokenTest.java b/dalvik/src/main/java/dalvik/annotation/BrokenTest.java
new file mode 100644
index 0000000..401d652
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/BrokenTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test case as broken. This means the test case should be fixed.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface BrokenTest {
+
+ /**
+ * Plain text reason for adding this annotation.
+ */
+ String value();
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/EnclosingClass.java b/dalvik/src/main/java/dalvik/annotation/EnclosingClass.java
new file mode 100644
index 0000000..2546c4c
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/EnclosingClass.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide part of the InnerClasses attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface EnclosingClass {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/EnclosingMethod.java b/dalvik/src/main/java/dalvik/annotation/EnclosingMethod.java
new file mode 100644
index 0000000..1a97155
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/EnclosingMethod.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the EnclosingMethod attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface EnclosingMethod {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/InnerClass.java b/dalvik/src/main/java/dalvik/annotation/InnerClass.java
new file mode 100644
index 0000000..054842f
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/InnerClass.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide part of the InnerClasses attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface InnerClass {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/KnownFailure.java b/dalvik/src/main/java/dalvik/annotation/KnownFailure.java
new file mode 100644
index 0000000..11e813d
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/KnownFailure.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test case as a known failure. This means the underlying
+ * implementation should be fixed. Seems to be similar to @code{@ToBeFixed}, so
+ * maybe the two can be merged at some point.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface KnownFailure {
+
+ /**
+ * Plain text reason for adding this annotation.
+ */
+ String value();
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/MemberClasses.java b/dalvik/src/main/java/dalvik/annotation/MemberClasses.java
new file mode 100644
index 0000000..b213be7
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/MemberClasses.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the MemberClasses list.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface MemberClasses {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/SideEffect.java b/dalvik/src/main/java/dalvik/annotation/SideEffect.java
new file mode 100644
index 0000000..b92e9bc
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/SideEffect.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test-case as either having a side-effect that other tests might
+ * notice or suffering from such a side effect. Such tests should be run in an
+ * isolated manner.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface SideEffect {
+
+ /**
+ * Plain text reason for adding this annotation.
+ */
+ String value();
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/Signature.java b/dalvik/src/main/java/dalvik/annotation/Signature.java
new file mode 100644
index 0000000..b96d7dd
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/Signature.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the Signature attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface Signature {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/TestLevel.java b/dalvik/src/main/java/dalvik/annotation/TestLevel.java
new file mode 100644
index 0000000..f62ea2f
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/TestLevel.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+/**
+ * Defines an enumeration of possible states a test case can be in.
+ *
+ * @hide
+ */
+public enum TestLevel {
+
+ /**
+ * Indicates that a test method completely tests its target API method.
+ */
+ COMPLETE,
+
+ /**
+ * Indicates that a test method partially tests its target API method and
+ * that together with all other {@code PARTIAL_COMPLETE} tests for the same
+ * method it is sufficient.
+ */
+ PARTIAL_COMPLETE,
+
+ /**
+ * Just for compatibility purposes, will be removed later.
+ */
+ PARTIAL_OK,
+
+ /**
+ * Indicates that a test method partially tests its target API method. It
+ * needs a second review phase to find out if the sum of all partial tests
+ * is sufficient for completely testing the target API method. If yes, the
+ * level has to be changed to {@code PARTIAL_COMPLETE}.
+ */
+ PARTIAL,
+
+ /**
+ * Indicates that a test method is known to not completely test an API
+ * method but the missing test steps are too complex and costly to
+ * implement. This level is positioned somewhere between {@code PARTIAL}
+ * and {@code COMPLETE}.
+ */
+ SUFFICIENT,
+
+ /**
+ * Indicates that a test method provides additional testing for an API
+ * method for which there already exists one {@code COMPLETE} or a set of
+ * {@code PARTIAL_COMPLETE} tests. This level may also be used for test
+ * methods that test a concept which can not be directly attributed to
+ * the specification of an API method or class.
+ */
+ ADDITIONAL,
+
+ /**
+ * Indicates that there is nothing to test in an API method, for example if
+ * the specification states that a method does nothing.
+ */
+ NOT_NECESSARY,
+
+ /**
+ * Indicates that it is very hard or impossible to test an API method.
+ */
+ NOT_FEASIBLE,
+
+ /**
+ * Indicates that the tests is either insufficient or wrong. Something needs
+ * to be done about it.
+ */
+ TODO,
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/TestTarget.java b/dalvik/src/main/java/dalvik/annotation/TestTarget.java
new file mode 100644
index 0000000..dcfd20c
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/TestTarget.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an annotation used be used within the TestInfo annotation. It
+ * specifies a single method target for the test (but can be used multiple
+ * times).
+ *
+ * @deprecated Obsolete
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.ANNOTATION_TYPE })
+@Deprecated
+public @interface TestTarget {
+
+ /**
+ * Specifies the name of the method that is being tested.
+ */
+ String methodName() default "";
+
+ /**
+ * Specifies the name of a concept being tested. Use this if
+ * {@code methodName} is not accurate enough. E.g. for
+ * {@link java.util.regex.Pattern#compile(String)} {@code methodName} is not
+ * sufficient since the String contains a pattern with its own syntax which
+ * has to be tested with different aspects. Areas concerned are e.g. JDBC
+ * (SELECT, INSERT, UPDATE, DELETE, ...), regex (character sets,
+ * operators,...), formatters (DecimalFormat, DateFormat, ChoiceFormat,
+ * ...), ...
+ */
+ String conceptName() default "";
+
+ /**
+ * Specifies the signature of the method that is being tested, in terms of
+ * Java classes.
+ */
+ Class<?>[] methodArgs() default {};
+
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/TestTargetClass.java b/dalvik/src/main/java/dalvik/annotation/TestTargetClass.java
new file mode 100644
index 0000000..e88040e
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/TestTargetClass.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an annotation for test classes that allows to link them to the class
+ * that is being tested. The current assumption is that the test are somewhat
+ * organized according to the API classes they test. Might be too strict for
+ * some cases.
+ *
+ * @deprecated Obsolete
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+@Deprecated
+public @interface TestTargetClass {
+
+ /**
+ * Specifies the class being tested.
+ */
+ Class<?> value();
+
+ /**
+ * Option to specify untested methods for the class.
+ * @hide
+ */
+ TestTargetNew[] untestedMethods() default {};
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/TestTargetNew.java b/dalvik/src/main/java/dalvik/annotation/TestTargetNew.java
new file mode 100644
index 0000000..80aebee
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/TestTargetNew.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an annotation used be used within the TestInfo annotation. It
+ * specifies a single method target for the test (but can be used multiple
+ * times).
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
+public @interface TestTargetNew {
+
+ /**
+ * Specifies the name of the API method that is being tested. This field
+ * may be left empty if the test target is a concept implemented in a
+ * class rather than a concrete API method.
+ */
+ String method() default "";
+
+ /**
+ * Specifies the signature of the API method that is being tested, in terms
+ * of Java classes.
+ */
+ Class<?>[] args() default {};
+
+ /**
+ * Specifies the class to which the tested method belongs. If this value is
+ * not provided, the class identified in @TestTargetClass is used by the
+ * test progress doclet.
+ */
+ Class<?> clazz() default void.class;
+
+ /**
+ * Specifies the level of coverage the tested API method has.
+ */
+ TestLevel level();
+
+ /**
+ * Specifies noteworthy plain-text information about the test, for example
+ * if something is NOT covered by the test method.
+ */
+ String notes() default "";
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/TestTargets.java b/dalvik/src/main/java/dalvik/annotation/TestTargets.java
new file mode 100644
index 0000000..207572c
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/TestTargets.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an annotation for test classes that allows to link them to the class
+ * that is being tested. The current assumption is that the test are somewhat
+ * organized according to the API classes they test. Might be too strict for
+ * some cases.
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface TestTargets {
+
+ /**
+ * Specifies the API methods that are tested by the annotated test method.
+ */
+ TestTargetNew[] value();
+}
diff --git a/dalvik/src/main/java/dalvik/annotation/Throws.java b/dalvik/src/main/java/dalvik/annotation/Throws.java
new file mode 100644
index 0000000..161116a
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/Throws.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the Exceptions attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface Throws {}
+
diff --git a/dalvik/src/main/java/dalvik/annotation/ToBeFixed.java b/dalvik/src/main/java/dalvik/annotation/ToBeFixed.java
new file mode 100644
index 0000000..751a038
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/ToBeFixed.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an annotation for test methods that indicate the test method
+ * need to be fixed in future.
+ * {@hide pending API Council approval}
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface ToBeFixed {
+
+ /**
+ * Specifies the related bug number on CTS bug tracking system.
+ */
+ String bug() default "";
+
+ /**
+ * Specifies why this method need to be fixed. If we think it's a bug, explain
+ * the expectation and the actual result.
+ */
+ String explanation() default "";
+}
diff --git a/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java b/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java
new file mode 100644
index 0000000..1209b2e
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.bytecode;
+
+/**
+ * Information about Dalvik opcodes.
+ */
+public final class OpcodeInfo {
+ /**
+ * The maximum possible value of a Dalvik opcode.
+ *
+ * <p><b>Note:</b>: This is constant in any given VM incarnation,
+ * but it is subject to change over time, so it is not appropriate
+ * to represent as a compile-time constant value.</p>
+ */
+ public static final int MAXIMUM_VALUE;
+
+ /**
+ * The maximum possible "packed value" of a Dalvik opcode. The
+ * packed value of an opcode is a denser representation that is
+ * only used when reporting usage statistics. The mapping between
+ * packed opcode values and regular opcode values is
+ * implementation-specific and may vary over time.
+ *
+ * <p><b>Note:</b>: This is constant in any given VM incarnation,
+ * but it is subject to change over time, so it is not appropriate
+ * to represent as a compile-time constant value.</p>
+ *
+ * @see dalvik.system.VMDebug.getInstructionCount()
+ */
+ public static final int MAXIMUM_PACKED_VALUE;
+
+ static {
+ /*
+ * See note on the definition of MAXIMUM_VALUE, above, for
+ * why it's not assigned directly on the declaration line.
+ *
+ * IMPORTANT NOTE: This assignment is generated automatically
+ * by the opcode-gen tool. Any edits will get wiped out the
+ * next time the tool is run.
+ */
+ // BEGIN(libcore-maximum-values); GENERATED AUTOMATICALLY BY opcode-gen
+ MAXIMUM_VALUE = 65535;
+ MAXIMUM_PACKED_VALUE = 255;
+ // END(libcore-maximum-values)
+ }
+
+ /**
+ * This class is not instantiable.
+ */
+ private OpcodeInfo() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Returns whether the given packed opcode value represents a
+ * method invocation operation. This includes most things that
+ * look like method invocation at the source level, but it notably
+ * excludes methods that are implemented directly in the VM as
+ * well as ones the VM knows to have empty implementations.
+ *
+ * @hide Unclear if this is useful enough to publish as supported API.
+ *
+ * @param opcode one of the values defined in {@link Opcodes}
+ */
+ public static native boolean isInvoke(int packedOpcode);
+}
diff --git a/dalvik/src/main/java/dalvik/bytecode/Opcodes.java b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
new file mode 100644
index 0000000..f758d65
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 dalvik.bytecode;
+
+/**
+ * A list of all normal (not implementation-specific) Dalvik opcodes.
+ */
+public interface Opcodes {
+ /*
+ * IMPORTANT NOTE: The contents of this file are mostly generated
+ * automatically by the opcode-gen tool. Any edits to the generated
+ * sections will get wiped out the next time the tool is run.
+ */
+
+ // BEGIN(libcore-opcodes); GENERATED AUTOMATICALLY BY opcode-gen
+ int OP_NOP = 0x0000;
+ int OP_MOVE = 0x0001;
+ int OP_MOVE_FROM16 = 0x0002;
+ int OP_MOVE_16 = 0x0003;
+ int OP_MOVE_WIDE = 0x0004;
+ int OP_MOVE_WIDE_FROM16 = 0x0005;
+ int OP_MOVE_WIDE_16 = 0x0006;
+ int OP_MOVE_OBJECT = 0x0007;
+ int OP_MOVE_OBJECT_FROM16 = 0x0008;
+ int OP_MOVE_OBJECT_16 = 0x0009;
+ int OP_MOVE_RESULT = 0x000a;
+ int OP_MOVE_RESULT_WIDE = 0x000b;
+ int OP_MOVE_RESULT_OBJECT = 0x000c;
+ int OP_MOVE_EXCEPTION = 0x000d;
+ int OP_RETURN_VOID = 0x000e;
+ int OP_RETURN = 0x000f;
+ int OP_RETURN_WIDE = 0x0010;
+ int OP_RETURN_OBJECT = 0x0011;
+ int OP_CONST_4 = 0x0012;
+ int OP_CONST_16 = 0x0013;
+ int OP_CONST = 0x0014;
+ int OP_CONST_HIGH16 = 0x0015;
+ int OP_CONST_WIDE_16 = 0x0016;
+ int OP_CONST_WIDE_32 = 0x0017;
+ int OP_CONST_WIDE = 0x0018;
+ int OP_CONST_WIDE_HIGH16 = 0x0019;
+ int OP_CONST_STRING = 0x001a;
+ int OP_CONST_STRING_JUMBO = 0x001b;
+ int OP_CONST_CLASS = 0x001c;
+ int OP_MONITOR_ENTER = 0x001d;
+ int OP_MONITOR_EXIT = 0x001e;
+ int OP_CHECK_CAST = 0x001f;
+ int OP_INSTANCE_OF = 0x0020;
+ int OP_ARRAY_LENGTH = 0x0021;
+ int OP_NEW_INSTANCE = 0x0022;
+ int OP_NEW_ARRAY = 0x0023;
+ int OP_FILLED_NEW_ARRAY = 0x0024;
+ int OP_FILLED_NEW_ARRAY_RANGE = 0x0025;
+ int OP_FILL_ARRAY_DATA = 0x0026;
+ int OP_THROW = 0x0027;
+ int OP_GOTO = 0x0028;
+ int OP_GOTO_16 = 0x0029;
+ int OP_GOTO_32 = 0x002a;
+ int OP_PACKED_SWITCH = 0x002b;
+ int OP_SPARSE_SWITCH = 0x002c;
+ int OP_CMPL_FLOAT = 0x002d;
+ int OP_CMPG_FLOAT = 0x002e;
+ int OP_CMPL_DOUBLE = 0x002f;
+ int OP_CMPG_DOUBLE = 0x0030;
+ int OP_CMP_LONG = 0x0031;
+ int OP_IF_EQ = 0x0032;
+ int OP_IF_NE = 0x0033;
+ int OP_IF_LT = 0x0034;
+ int OP_IF_GE = 0x0035;
+ int OP_IF_GT = 0x0036;
+ int OP_IF_LE = 0x0037;
+ int OP_IF_EQZ = 0x0038;
+ int OP_IF_NEZ = 0x0039;
+ int OP_IF_LTZ = 0x003a;
+ int OP_IF_GEZ = 0x003b;
+ int OP_IF_GTZ = 0x003c;
+ int OP_IF_LEZ = 0x003d;
+ int OP_AGET = 0x0044;
+ int OP_AGET_WIDE = 0x0045;
+ int OP_AGET_OBJECT = 0x0046;
+ int OP_AGET_BOOLEAN = 0x0047;
+ int OP_AGET_BYTE = 0x0048;
+ int OP_AGET_CHAR = 0x0049;
+ int OP_AGET_SHORT = 0x004a;
+ int OP_APUT = 0x004b;
+ int OP_APUT_WIDE = 0x004c;
+ int OP_APUT_OBJECT = 0x004d;
+ int OP_APUT_BOOLEAN = 0x004e;
+ int OP_APUT_BYTE = 0x004f;
+ int OP_APUT_CHAR = 0x0050;
+ int OP_APUT_SHORT = 0x0051;
+ int OP_IGET = 0x0052;
+ int OP_IGET_WIDE = 0x0053;
+ int OP_IGET_OBJECT = 0x0054;
+ int OP_IGET_BOOLEAN = 0x0055;
+ int OP_IGET_BYTE = 0x0056;
+ int OP_IGET_CHAR = 0x0057;
+ int OP_IGET_SHORT = 0x0058;
+ int OP_IPUT = 0x0059;
+ int OP_IPUT_WIDE = 0x005a;
+ int OP_IPUT_OBJECT = 0x005b;
+ int OP_IPUT_BOOLEAN = 0x005c;
+ int OP_IPUT_BYTE = 0x005d;
+ int OP_IPUT_CHAR = 0x005e;
+ int OP_IPUT_SHORT = 0x005f;
+ int OP_SGET = 0x0060;
+ int OP_SGET_WIDE = 0x0061;
+ int OP_SGET_OBJECT = 0x0062;
+ int OP_SGET_BOOLEAN = 0x0063;
+ int OP_SGET_BYTE = 0x0064;
+ int OP_SGET_CHAR = 0x0065;
+ int OP_SGET_SHORT = 0x0066;
+ int OP_SPUT = 0x0067;
+ int OP_SPUT_WIDE = 0x0068;
+ int OP_SPUT_OBJECT = 0x0069;
+ int OP_SPUT_BOOLEAN = 0x006a;
+ int OP_SPUT_BYTE = 0x006b;
+ int OP_SPUT_CHAR = 0x006c;
+ int OP_SPUT_SHORT = 0x006d;
+ int OP_INVOKE_VIRTUAL = 0x006e;
+ int OP_INVOKE_SUPER = 0x006f;
+ int OP_INVOKE_DIRECT = 0x0070;
+ int OP_INVOKE_STATIC = 0x0071;
+ int OP_INVOKE_INTERFACE = 0x0072;
+ int OP_INVOKE_VIRTUAL_RANGE = 0x0074;
+ int OP_INVOKE_SUPER_RANGE = 0x0075;
+ int OP_INVOKE_DIRECT_RANGE = 0x0076;
+ int OP_INVOKE_STATIC_RANGE = 0x0077;
+ int OP_INVOKE_INTERFACE_RANGE = 0x0078;
+ int OP_NEG_INT = 0x007b;
+ int OP_NOT_INT = 0x007c;
+ int OP_NEG_LONG = 0x007d;
+ int OP_NOT_LONG = 0x007e;
+ int OP_NEG_FLOAT = 0x007f;
+ int OP_NEG_DOUBLE = 0x0080;
+ int OP_INT_TO_LONG = 0x0081;
+ int OP_INT_TO_FLOAT = 0x0082;
+ int OP_INT_TO_DOUBLE = 0x0083;
+ int OP_LONG_TO_INT = 0x0084;
+ int OP_LONG_TO_FLOAT = 0x0085;
+ int OP_LONG_TO_DOUBLE = 0x0086;
+ int OP_FLOAT_TO_INT = 0x0087;
+ int OP_FLOAT_TO_LONG = 0x0088;
+ int OP_FLOAT_TO_DOUBLE = 0x0089;
+ int OP_DOUBLE_TO_INT = 0x008a;
+ int OP_DOUBLE_TO_LONG = 0x008b;
+ int OP_DOUBLE_TO_FLOAT = 0x008c;
+ int OP_INT_TO_BYTE = 0x008d;
+ int OP_INT_TO_CHAR = 0x008e;
+ int OP_INT_TO_SHORT = 0x008f;
+ int OP_ADD_INT = 0x0090;
+ int OP_SUB_INT = 0x0091;
+ int OP_MUL_INT = 0x0092;
+ int OP_DIV_INT = 0x0093;
+ int OP_REM_INT = 0x0094;
+ int OP_AND_INT = 0x0095;
+ int OP_OR_INT = 0x0096;
+ int OP_XOR_INT = 0x0097;
+ int OP_SHL_INT = 0x0098;
+ int OP_SHR_INT = 0x0099;
+ int OP_USHR_INT = 0x009a;
+ int OP_ADD_LONG = 0x009b;
+ int OP_SUB_LONG = 0x009c;
+ int OP_MUL_LONG = 0x009d;
+ int OP_DIV_LONG = 0x009e;
+ int OP_REM_LONG = 0x009f;
+ int OP_AND_LONG = 0x00a0;
+ int OP_OR_LONG = 0x00a1;
+ int OP_XOR_LONG = 0x00a2;
+ int OP_SHL_LONG = 0x00a3;
+ int OP_SHR_LONG = 0x00a4;
+ int OP_USHR_LONG = 0x00a5;
+ int OP_ADD_FLOAT = 0x00a6;
+ int OP_SUB_FLOAT = 0x00a7;
+ int OP_MUL_FLOAT = 0x00a8;
+ int OP_DIV_FLOAT = 0x00a9;
+ int OP_REM_FLOAT = 0x00aa;
+ int OP_ADD_DOUBLE = 0x00ab;
+ int OP_SUB_DOUBLE = 0x00ac;
+ int OP_MUL_DOUBLE = 0x00ad;
+ int OP_DIV_DOUBLE = 0x00ae;
+ int OP_REM_DOUBLE = 0x00af;
+ int OP_ADD_INT_2ADDR = 0x00b0;
+ int OP_SUB_INT_2ADDR = 0x00b1;
+ int OP_MUL_INT_2ADDR = 0x00b2;
+ int OP_DIV_INT_2ADDR = 0x00b3;
+ int OP_REM_INT_2ADDR = 0x00b4;
+ int OP_AND_INT_2ADDR = 0x00b5;
+ int OP_OR_INT_2ADDR = 0x00b6;
+ int OP_XOR_INT_2ADDR = 0x00b7;
+ int OP_SHL_INT_2ADDR = 0x00b8;
+ int OP_SHR_INT_2ADDR = 0x00b9;
+ int OP_USHR_INT_2ADDR = 0x00ba;
+ int OP_ADD_LONG_2ADDR = 0x00bb;
+ int OP_SUB_LONG_2ADDR = 0x00bc;
+ int OP_MUL_LONG_2ADDR = 0x00bd;
+ int OP_DIV_LONG_2ADDR = 0x00be;
+ int OP_REM_LONG_2ADDR = 0x00bf;
+ int OP_AND_LONG_2ADDR = 0x00c0;
+ int OP_OR_LONG_2ADDR = 0x00c1;
+ int OP_XOR_LONG_2ADDR = 0x00c2;
+ int OP_SHL_LONG_2ADDR = 0x00c3;
+ int OP_SHR_LONG_2ADDR = 0x00c4;
+ int OP_USHR_LONG_2ADDR = 0x00c5;
+ int OP_ADD_FLOAT_2ADDR = 0x00c6;
+ int OP_SUB_FLOAT_2ADDR = 0x00c7;
+ int OP_MUL_FLOAT_2ADDR = 0x00c8;
+ int OP_DIV_FLOAT_2ADDR = 0x00c9;
+ int OP_REM_FLOAT_2ADDR = 0x00ca;
+ int OP_ADD_DOUBLE_2ADDR = 0x00cb;
+ int OP_SUB_DOUBLE_2ADDR = 0x00cc;
+ int OP_MUL_DOUBLE_2ADDR = 0x00cd;
+ int OP_DIV_DOUBLE_2ADDR = 0x00ce;
+ int OP_REM_DOUBLE_2ADDR = 0x00cf;
+ int OP_ADD_INT_LIT16 = 0x00d0;
+ int OP_RSUB_INT = 0x00d1;
+ int OP_MUL_INT_LIT16 = 0x00d2;
+ int OP_DIV_INT_LIT16 = 0x00d3;
+ int OP_REM_INT_LIT16 = 0x00d4;
+ int OP_AND_INT_LIT16 = 0x00d5;
+ int OP_OR_INT_LIT16 = 0x00d6;
+ int OP_XOR_INT_LIT16 = 0x00d7;
+ int OP_ADD_INT_LIT8 = 0x00d8;
+ int OP_RSUB_INT_LIT8 = 0x00d9;
+ int OP_MUL_INT_LIT8 = 0x00da;
+ int OP_DIV_INT_LIT8 = 0x00db;
+ int OP_REM_INT_LIT8 = 0x00dc;
+ int OP_AND_INT_LIT8 = 0x00dd;
+ int OP_OR_INT_LIT8 = 0x00de;
+ int OP_XOR_INT_LIT8 = 0x00df;
+ int OP_SHL_INT_LIT8 = 0x00e0;
+ int OP_SHR_INT_LIT8 = 0x00e1;
+ int OP_USHR_INT_LIT8 = 0x00e2;
+ // END(libcore-opcodes)
+
+ /** Never implemented; do not use. */
+ int OP_CONST_CLASS_JUMBO = 0x00ff;
+ /** Never implemented; do not use. */
+ int OP_CHECK_CAST_JUMBO = 0x01ff;
+ /** Never implemented; do not use. */
+ int OP_INSTANCE_OF_JUMBO = 0x02ff;
+ /** Never implemented; do not use. */
+ int OP_NEW_INSTANCE_JUMBO = 0x03ff;
+ /** Never implemented; do not use. */
+ int OP_NEW_ARRAY_JUMBO = 0x04ff;
+ /** Never implemented; do not use. */
+ int OP_FILLED_NEW_ARRAY_JUMBO = 0x05ff;
+ /** Never implemented; do not use. */
+ int OP_IGET_JUMBO = 0x06ff;
+ /** Never implemented; do not use. */
+ int OP_IGET_WIDE_JUMBO = 0x07ff;
+ /** Never implemented; do not use. */
+ int OP_IGET_OBJECT_JUMBO = 0x08ff;
+ /** Never implemented; do not use. */
+ int OP_IGET_BOOLEAN_JUMBO = 0x09ff;
+ /** Never implemented; do not use. */
+ int OP_IGET_BYTE_JUMBO = 0x0aff;
+ /** Never implemented; do not use. */
+ int OP_IGET_CHAR_JUMBO = 0x0bff;
+ /** Never implemented; do not use. */
+ int OP_IGET_SHORT_JUMBO = 0x0cff;
+ /** Never implemented; do not use. */
+ int OP_IPUT_JUMBO = 0x0dff;
+ /** Never implemented; do not use. */
+ int OP_IPUT_WIDE_JUMBO = 0x0eff;
+ /** Never implemented; do not use. */
+ int OP_IPUT_OBJECT_JUMBO = 0x0fff;
+ /** Never implemented; do not use. */
+ int OP_IPUT_BOOLEAN_JUMBO = 0x10ff;
+ /** Never implemented; do not use. */
+ int OP_IPUT_BYTE_JUMBO = 0x11ff;
+ /** Never implemented; do not use. */
+ int OP_IPUT_CHAR_JUMBO = 0x12ff;
+ /** Never implemented; do not use. */
+ int OP_IPUT_SHORT_JUMBO = 0x13ff;
+ /** Never implemented; do not use. */
+ int OP_SGET_JUMBO = 0x14ff;
+ /** Never implemented; do not use. */
+ int OP_SGET_WIDE_JUMBO = 0x15ff;
+ /** Never implemented; do not use. */
+ int OP_SGET_OBJECT_JUMBO = 0x16ff;
+ /** Never implemented; do not use. */
+ int OP_SGET_BOOLEAN_JUMBO = 0x17ff;
+ /** Never implemented; do not use. */
+ int OP_SGET_BYTE_JUMBO = 0x18ff;
+ /** Never implemented; do not use. */
+ int OP_SGET_CHAR_JUMBO = 0x19ff;
+ /** Never implemented; do not use. */
+ int OP_SGET_SHORT_JUMBO = 0x1aff;
+ /** Never implemented; do not use. */
+ int OP_SPUT_JUMBO = 0x1bff;
+ /** Never implemented; do not use. */
+ int OP_SPUT_WIDE_JUMBO = 0x1cff;
+ /** Never implemented; do not use. */
+ int OP_SPUT_OBJECT_JUMBO = 0x1dff;
+ /** Never implemented; do not use. */
+ int OP_SPUT_BOOLEAN_JUMBO = 0x1eff;
+ /** Never implemented; do not use. */
+ int OP_SPUT_BYTE_JUMBO = 0x1fff;
+ /** Never implemented; do not use. */
+ int OP_SPUT_CHAR_JUMBO = 0x20ff;
+ /** Never implemented; do not use. */
+ int OP_SPUT_SHORT_JUMBO = 0x21ff;
+ /** Never implemented; do not use. */
+ int OP_INVOKE_VIRTUAL_JUMBO = 0x22ff;
+ /** Never implemented; do not use. */
+ int OP_INVOKE_SUPER_JUMBO = 0x23ff;
+ /** Never implemented; do not use. */
+ int OP_INVOKE_DIRECT_JUMBO = 0x24ff;
+ /** Never implemented; do not use. */
+ int OP_INVOKE_STATIC_JUMBO = 0x25ff;
+ /** Never implemented; do not use. */
+ int OP_INVOKE_INTERFACE_JUMBO = 0x26ff;
+
+ /*
+ * The rest of these are either generated by dexopt for optimized
+ * code, or inserted by the VM at runtime. They are never generated
+ * by "dx".
+ *
+ * They are all deprecated and will be removed in a future
+ * release, since these declarations are really of private implementation
+ * details that are subject to change.
+ */
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_IGET_WIDE_VOLATILE = 0xe8;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_IPUT_WIDE_VOLATILE = 0xe9;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_SGET_WIDE_VOLATILE = 0xea;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_SPUT_WIDE_VOLATILE = 0xeb;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_BREAKPOINT = 0xec;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_THROW_VERIFICATION_ERROR = 0xed;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_EXECUTE_INLINE = 0xee;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_EXECUTE_INLINE_RANGE = 0xef;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_INVOKE_DIRECT_EMPTY = 0xf0;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_IGET_QUICK = 0xf2;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_IGET_WIDE_QUICK = 0xf3;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_IGET_OBJECT_QUICK = 0xf4;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_IPUT_QUICK = 0xf5;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_IPUT_WIDE_QUICK = 0xf6;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_IPUT_OBJECT_QUICK = 0xf7;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_INVOKE_VIRTUAL_QUICK = 0xf8;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_INVOKE_VIRTUAL_QUICK_RANGE = 0xf9;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_INVOKE_SUPER_QUICK = 0xfa;
+
+ /**
+ * Implementation detail.
+ * @deprecated Implementation detail.
+ */
+ @Deprecated int OP_INVOKE_SUPER_QUICK_RANGE = 0xfb;
+}
diff --git a/dalvik/src/main/java/dalvik/system/AllocationLimitError.java b/dalvik/src/main/java/dalvik/system/AllocationLimitError.java
new file mode 100644
index 0000000..b3f8947
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/AllocationLimitError.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+/**
+ * Is thrown when an allocation limit is exceeded.
+ *
+ * @hide
+ */
+public class AllocationLimitError extends VirtualMachineError {
+ /**
+ * Creates a new exception instance and initializes it with default values.
+ */
+ public AllocationLimitError() {
+ super();
+ }
+
+ /**
+ * Creates a new exception instance and initializes it with a given message.
+ *
+ * @param detailMessage the error message
+ */
+ public AllocationLimitError(String detailMessage) {
+ super(detailMessage);
+ }
+}
+
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
new file mode 100644
index 0000000..d3ec95a
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * Base class for common functionality between various dex-based
+ * {@link ClassLoader} implementations.
+ */
+public class BaseDexClassLoader extends ClassLoader {
+ private final DexPathList pathList;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param dexPath the list of jar/apk files containing classes and
+ * resources, delimited by {@code File.pathSeparator}, which
+ * defaults to {@code ":"} on Android
+ * @param optimizedDirectory directory where optimized dex files
+ * should be written; may be {@code null}
+ * @param libraryPath the list of directories containing native
+ * libraries, delimited by {@code File.pathSeparator}; may be
+ * {@code null}
+ * @param parent the parent class loader
+ */
+ public BaseDexClassLoader(String dexPath, File optimizedDirectory,
+ String libraryPath, ClassLoader parent) {
+ super(parent);
+ this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ Class c = pathList.findClass(name);
+ if (c == null) {
+ throw new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
+ }
+ return c;
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ return pathList.findResource(name);
+ }
+
+ @Override
+ protected Enumeration<URL> findResources(String name) {
+ return pathList.findResources(name);
+ }
+
+ @Override
+ public String findLibrary(String name) {
+ return pathList.findLibrary(name);
+ }
+
+ /**
+ * Returns package information for the given package.
+ * Unfortunately, instances of this class don't really have this
+ * information, and as a non-secure {@code ClassLoader}, it isn't
+ * even required to, according to the spec. Yet, we want to
+ * provide it, in order to make all those hopeful callers of
+ * {@code myClass.getPackage().getName()} happy. Thus we construct
+ * a {@code Package} object the first time it is being requested
+ * and fill most of the fields with dummy values. The {@code
+ * Package} object is then put into the {@code ClassLoader}'s
+ * package cache, so we see the same one next time. We don't
+ * create {@code Package} objects for {@code null} arguments or
+ * for the default package.
+ *
+ * <p>There is a limited chance that we end up with multiple
+ * {@code Package} objects representing the same package: It can
+ * happen when when a package is scattered across different JAR
+ * files which were loaded by different {@code ClassLoader}
+ * instances. This is rather unlikely, and given that this whole
+ * thing is more or less a workaround, probably not worth the
+ * effort to address.
+ *
+ * @param name the name of the class
+ * @return the package information for the class, or {@code null}
+ * if there is no package information available for it
+ */
+ @Override
+ protected synchronized Package getPackage(String name) {
+ if (name != null && !name.isEmpty()) {
+ Package pack = super.getPackage(name);
+
+ if (pack == null) {
+ pack = definePackage(name, "Unknown", "0.0", "Unknown",
+ "Unknown", "0.0", "Unknown", null);
+ }
+
+ return pack;
+ }
+
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public String getLdLibraryPath() {
+ StringBuilder result = new StringBuilder();
+ for (File directory : pathList.getNativeLibraryDirectories()) {
+ if (result.length() > 0) {
+ result.append(':');
+ }
+ result.append(directory);
+ }
+ return result.toString();
+ }
+
+ @Override public String toString() {
+ return getClass().getName() + "[" + pathList + "]";
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/BlockGuard.java b/dalvik/src/main/java/dalvik/system/BlockGuard.java
new file mode 100644
index 0000000..d61f0e1
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/BlockGuard.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.SocketException;
+import java.nio.charset.Charsets;
+
+/**
+ * Mechanism to let threads set restrictions on what code is allowed
+ * to do in their thread.
+ *
+ * <p>This is meant for applications to prevent certain blocking
+ * operations from running on their main event loop (or "UI") threads.
+ *
+ * <p>Note that this is all best-effort to catch most accidental mistakes
+ * and isn't intended to be a perfect mechanism, nor provide any sort of
+ * security.
+ *
+ * @hide
+ */
+public final class BlockGuard {
+
+ // TODO: refactor class name to something more generic, since its scope is
+ // growing beyond just blocking/logging.
+
+ public static final int DISALLOW_DISK_WRITE = 0x01;
+ public static final int DISALLOW_DISK_READ = 0x02;
+ public static final int DISALLOW_NETWORK = 0x04;
+ public static final int PASS_RESTRICTIONS_VIA_RPC = 0x08;
+ public static final int PENALTY_LOG = 0x10;
+ public static final int PENALTY_DIALOG = 0x20;
+ public static final int PENALTY_DEATH = 0x40;
+
+ public interface Policy {
+ /**
+ * Called on disk writes.
+ */
+ void onWriteToDisk();
+
+ /**
+ * Called on disk reads.
+ */
+ void onReadFromDisk();
+
+ /**
+ * Called on network operations.
+ */
+ void onNetwork();
+
+ /**
+ * Returns the policy bitmask, for shipping over Binder calls
+ * to remote threads/processes and reinstantiating the policy
+ * there. The bits in the mask are from the DISALLOW_* and
+ * PENALTY_* constants.
+ */
+ int getPolicyMask();
+ }
+
+ public static class BlockGuardPolicyException extends RuntimeException {
+ // bitmask of DISALLOW_*, PENALTY_*, etc flags
+ private final int mPolicyState;
+ private final int mPolicyViolated;
+ private final String mMessage; // may be null
+
+ public BlockGuardPolicyException(int policyState, int policyViolated) {
+ this(policyState, policyViolated, null);
+ }
+
+ public BlockGuardPolicyException(int policyState, int policyViolated, String message) {
+ mPolicyState = policyState;
+ mPolicyViolated = policyViolated;
+ mMessage = message;
+ fillInStackTrace();
+ }
+
+ public int getPolicy() {
+ return mPolicyState;
+ }
+
+ public int getPolicyViolation() {
+ return mPolicyViolated;
+ }
+
+ public String getMessage() {
+ // Note: do not change this format casually. It's
+ // somewhat unfortunately Parceled and passed around
+ // Binder calls and parsed back into an Exception by
+ // Android's StrictMode. This was the least invasive
+ // option and avoided a gross mix of Java Serialization
+ // combined with Parcels.
+ return "policy=" + mPolicyState + " violation=" + mPolicyViolated +
+ (mMessage == null ? "" : (" msg=" + mMessage));
+ }
+ }
+
+ /**
+ * The default, permissive policy that doesn't prevent any operations.
+ */
+ public static final Policy LAX_POLICY = new Policy() {
+ public void onWriteToDisk() {}
+ public void onReadFromDisk() {}
+ public void onNetwork() {}
+ public int getPolicyMask() {
+ return 0;
+ }
+ };
+
+ private static ThreadLocal<Policy> threadPolicy = new ThreadLocal<Policy>() {
+ @Override protected Policy initialValue() {
+ return LAX_POLICY;
+ }
+ };
+
+ /**
+ * Get the current thread's policy.
+ *
+ * @return the current thread's policy. Never returns null.
+ * Will return the LAX_POLICY instance if nothing else is set.
+ */
+ public static Policy getThreadPolicy() {
+ return threadPolicy.get();
+ }
+
+ /**
+ * Sets the current thread's block guard policy.
+ *
+ * @param policy policy to set. May not be null. Use the public LAX_POLICY
+ * if you want to unset the active policy.
+ */
+ public static void setThreadPolicy(Policy policy) {
+ if (policy == null) {
+ throw new NullPointerException("policy == null");
+ }
+ threadPolicy.set(policy);
+ }
+
+ private BlockGuard() {}
+}
diff --git a/dalvik/src/main/java/dalvik/system/CloseGuard.java b/dalvik/src/main/java/dalvik/system/CloseGuard.java
new file mode 100644
index 0000000..df36867
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/CloseGuard.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+/**
+ * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
+ * resources that should have been cleaned up by explicit close
+ * methods (aka "explicit termination methods" in Effective Java).
+ * <p>
+ * A simple example: <pre> {@code
+ * class Foo {
+ *
+ * private final CloseGuard guard = CloseGuard.get();
+ *
+ * ...
+ *
+ * public Foo() {
+ * ...;
+ * guard.open("cleanup");
+ * }
+ *
+ * public void cleanup() {
+ * guard.close();
+ * ...;
+ * }
+ *
+ * protected void finalize() throws Throwable {
+ * try {
+ * if (guard != null) {
+ * guard.warnIfOpen();
+ * }
+ * cleanup();
+ * } finally {
+ * super.finalize();
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * In usage where the resource to be explicitly cleaned up are
+ * allocated after object construction, CloseGuard protection can
+ * be deferred. For example: <pre> {@code
+ * class Bar {
+ *
+ * private final CloseGuard guard = CloseGuard.get();
+ *
+ * ...
+ *
+ * public Bar() {
+ * ...;
+ * }
+ *
+ * public void connect() {
+ * ...;
+ * guard.open("cleanup");
+ * }
+ *
+ * public void cleanup() {
+ * guard.close();
+ * ...;
+ * }
+ *
+ * protected void finalize() throws Throwable {
+ * try {
+ * if (guard != null) {
+ * guard.warnIfOpen();
+ * }
+ * cleanup();
+ * } finally {
+ * super.finalize();
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * When used in a constructor calls to {@code open} should occur at
+ * the end of the constructor since an exception that would cause
+ * abrupt termination of the constructor will mean that the user will
+ * not have a reference to the object to cleanup explicitly. When used
+ * in a method, the call to {@code open} should occur just after
+ * resource acquisition.
+ *
+ * <p>
+ *
+ * Note that the null check on {@code guard} in the finalizer is to
+ * cover cases where a constructor throws an exception causing the
+ * {@code guard} to be uninitialized.
+ *
+ * @hide
+ */
+public final class CloseGuard {
+
+ /**
+ * Instance used when CloseGuard is disabled to avoid allocation.
+ */
+ private static final CloseGuard NOOP = new CloseGuard();
+
+ /**
+ * Enabled by default so we can catch issues early in VM startup.
+ * Note, however, that Android disables this early in its startup,
+ * but enables it with DropBoxing for system apps on debug builds.
+ */
+ private static volatile boolean ENABLED = true;
+
+ /**
+ * Hook for customizing how CloseGuard issues are reported.
+ */
+ private static volatile Reporter REPORTER = new DefaultReporter();
+
+ /**
+ * Returns a CloseGuard instance. If CloseGuard is enabled, {@code
+ * #open(String)} can be used to set up the instance to warn on
+ * failure to close. If CloseGuard is disabled, a non-null no-op
+ * instance is returned.
+ */
+ public static CloseGuard get() {
+ if (!ENABLED) {
+ return NOOP;
+ }
+ return new CloseGuard();
+ }
+
+ /**
+ * Used to enable or disable CloseGuard. Note that CloseGuard only
+ * warns if it is enabled for both allocation and finalization.
+ */
+ public static void setEnabled(boolean enabled) {
+ ENABLED = enabled;
+ }
+
+ /**
+ * Used to replace default Reporter used to warn of CloseGuard
+ * violations. Must be non-null.
+ */
+ public static void setReporter(Reporter reporter) {
+ if (reporter == null) {
+ throw new NullPointerException("reporter == null");
+ }
+ REPORTER = reporter;
+ }
+
+ /**
+ * Returns non-null CloseGuard.Reporter.
+ */
+ public static Reporter getReporter() {
+ return REPORTER;
+ }
+
+ private CloseGuard() {}
+
+ /**
+ * If CloseGuard is enabled, {@code open} initializes the instance
+ * with a warning that the caller should have explicitly called the
+ * {@code closer} method instead of relying on finalization.
+ *
+ * @param closer non-null name of explicit termination method
+ * @throws NullPointerException if closer is null, regardless of
+ * whether or not CloseGuard is enabled
+ */
+ public void open(String closer) {
+ // always perform the check for valid API usage...
+ if (closer == null) {
+ throw new NullPointerException("closer == null");
+ }
+ // ...but avoid allocating an allocationSite if disabled
+ if (this == NOOP || !ENABLED) {
+ return;
+ }
+ String message = "Explicit termination method '" + closer + "' not called";
+ allocationSite = new Throwable(message);
+ }
+
+ private Throwable allocationSite;
+
+ /**
+ * Marks this CloseGuard instance as closed to avoid warnings on
+ * finalization.
+ */
+ public void close() {
+ allocationSite = null;
+ }
+
+ /**
+ * If CloseGuard is enabled, logs a warning if the caller did not
+ * properly cleanup by calling an explicit close method
+ * before finalization. If CloseGuard is disabled, no action is
+ * performed.
+ */
+ public void warnIfOpen() {
+ if (allocationSite == null || !ENABLED) {
+ return;
+ }
+
+ String message =
+ ("A resource was acquired at attached stack trace but never released. "
+ + "See java.io.Closeable for information on avoiding resource leaks.");
+
+ REPORTER.report(message, allocationSite);
+ }
+
+ /**
+ * Interface to allow customization of reporting behavior.
+ */
+ public static interface Reporter {
+ public void report (String message, Throwable allocationSite);
+ }
+
+ /**
+ * Default Reporter which reports CloseGuard violations to the log.
+ */
+ private static final class DefaultReporter implements Reporter {
+ @Override public void report (String message, Throwable allocationSite) {
+ System.logW(message, allocationSite);
+ }
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/DalvikLogHandler.java b/dalvik/src/main/java/dalvik/system/DalvikLogHandler.java
new file mode 100644
index 0000000..a62ca3b
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/DalvikLogHandler.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * An optimized handler for efficient publishing of basic log messages.
+ * Implementers should also be subclasses of {@link java.util.logging.Handler}.
+ *
+ * <p>Unlike the default log handler, this API doesn't require intermediate
+ * objects to be allocated for log handling. It also includes a short tag, which
+ * may otherwise need to be calculated for each published message.
+ *
+ * @hide
+ */
+public interface DalvikLogHandler {
+
+ /**
+ * Publishes a log message. Unlike {@link
+ * java.util.logging.Handler#publish(java.util.logging.LogRecord)}, this
+ * method includes only the raw log message. Log messages that were created
+ * with additional fields (parameters, source methods, etc.) will flow
+ * through the conventional channels instead.
+ *
+ * @param tag the short (23 characters or fewer) logger tag identifying
+ * {@code logger}.
+ */
+ void publish(Logger source, String tag, Level level, String message);
+
+ // TODO: support messages with throwables?
+} \ No newline at end of file
diff --git a/dalvik/src/main/java/dalvik/system/DalvikLogging.java b/dalvik/src/main/java/dalvik/system/DalvikLogging.java
new file mode 100644
index 0000000..b5c7ad5
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/DalvikLogging.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+/**
+ * Utility methods for logging to {@code DalvikHandlers}.
+ *
+ * @hide
+ */
+public final class DalvikLogging {
+ private DalvikLogging() {}
+
+ /**
+ * Returns the short logger tag (up to 23 chars) for the given logger name.
+ * Traditionally loggers are named by fully-qualified Java classes; this
+ * method attempts to return a concise identifying part of such names.
+ */
+ public static String loggerNameToTag(String loggerName) {
+ // Anonymous logger.
+ if (loggerName == null) {
+ return "null";
+ }
+
+ int length = loggerName.length();
+ if (length <= 23) {
+ return loggerName;
+ }
+
+ int lastPeriod = loggerName.lastIndexOf(".");
+ return length - (lastPeriod + 1) <= 23
+ ? loggerName.substring(lastPeriod + 1)
+ : loggerName.substring(loggerName.length() - 23);
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/DexClassLoader.java b/dalvik/src/main/java/dalvik/system/DexClassLoader.java
new file mode 100644
index 0000000..ac2a70a
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/DexClassLoader.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+import java.io.File;
+
+/**
+ * A class loader that loads classes from {@code .jar} and {@code .apk} files
+ * containing a {@code classes.dex} entry. This can be used to execute code not
+ * installed as part of an application.
+ *
+ * <p>This class loader requires an application-private, writable directory to
+ * cache optimized classes. Use {@code Context.getDir(String, int)} to create
+ * such a directory: <pre> {@code
+ * File dexOutputDir = context.getDir("dex", 0);
+ * }</pre>
+ *
+ * <p><strong>Do not cache optimized classes on external storage.</strong>
+ * External storage does not provide access controls necessary to protect your
+ * application from code injection attacks.
+ */
+public class DexClassLoader extends BaseDexClassLoader {
+ /**
+ * Creates a {@code DexClassLoader} that finds interpreted and native
+ * code. Interpreted classes are found in a set of DEX files contained
+ * in Jar or APK files.
+ *
+ * <p>The path lists are separated using the character specified by the
+ * {@code path.separator} system property, which defaults to {@code :}.
+ *
+ * @param dexPath the list of jar/apk files containing classes and
+ * resources, delimited by {@code File.pathSeparator}, which
+ * defaults to {@code ":"} on Android
+ * @param optimizedDirectory directory where optimized dex files
+ * should be written; must not be {@code null}
+ * @param libraryPath the list of directories containing native
+ * libraries, delimited by {@code File.pathSeparator}; may be
+ * {@code null}
+ * @param parent the parent class loader
+ */
+ public DexClassLoader(String dexPath, String optimizedDirectory,
+ String libraryPath, ClassLoader parent) {
+ super(dexPath, new File(optimizedDirectory), libraryPath, parent);
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
new file mode 100644
index 0000000..8db3985
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Enumeration;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.StructStat;
+
+/**
+ * Manipulates DEX files. The class is similar in principle to
+ * {@link java.util.zip.ZipFile}. It is used primarily by class loaders.
+ * <p>
+ * Note we don't directly open and read the DEX file here. They're memory-mapped
+ * read-only by the VM.
+ */
+public final class DexFile {
+ private int mCookie;
+ private final String mFileName;
+ private final CloseGuard guard = CloseGuard.get();
+
+ /**
+ * Opens a DEX file from a given File object. This will usually be a ZIP/JAR
+ * file with a "classes.dex" inside.
+ *
+ * The VM will generate the name of the corresponding file in
+ * /data/dalvik-cache and open it, possibly creating or updating
+ * it first if system permissions allow. Don't pass in the name of
+ * a file in /data/dalvik-cache, as the named file is expected to be
+ * in its original (pre-dexopt) state.
+ *
+ * @param file
+ * the File object referencing the actual DEX file
+ *
+ * @throws IOException
+ * if an I/O error occurs, such as the file not being found or
+ * access rights missing for opening it
+ */
+ public DexFile(File file) throws IOException {
+ this(file.getPath());
+ }
+
+ /**
+ * Opens a DEX file from a given filename. This will usually be a ZIP/JAR
+ * file with a "classes.dex" inside.
+ *
+ * The VM will generate the name of the corresponding file in
+ * /data/dalvik-cache and open it, possibly creating or updating
+ * it first if system permissions allow. Don't pass in the name of
+ * a file in /data/dalvik-cache, as the named file is expected to be
+ * in its original (pre-dexopt) state.
+ *
+ * @param fileName
+ * the filename of the DEX file
+ *
+ * @throws IOException
+ * if an I/O error occurs, such as the file not being found or
+ * access rights missing for opening it
+ */
+ public DexFile(String fileName) throws IOException {
+ mCookie = openDexFile(fileName, null, 0);
+ mFileName = fileName;
+ guard.open("close");
+ //System.out.println("DEX FILE cookie is " + mCookie);
+ }
+
+ /**
+ * Opens a DEX file from a given filename, using a specified file
+ * to hold the optimized data.
+ *
+ * @param sourceName
+ * Jar or APK file with "classes.dex".
+ * @param outputName
+ * File that will hold the optimized form of the DEX data.
+ * @param flags
+ * Enable optional features.
+ */
+ private DexFile(String sourceName, String outputName, int flags) throws IOException {
+ if (outputName != null) {
+ try {
+ String parent = new File(outputName).getParent();
+ if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
+ throw new IllegalArgumentException("Optimized data directory " + parent
+ + " is not owned by the current user. Shared storage cannot protect"
+ + " your application from code injection attacks.");
+ }
+ } catch (ErrnoException ignored) {
+ // assume we'll fail with a more contextual error later
+ }
+ }
+
+ mCookie = openDexFile(sourceName, outputName, flags);
+ mFileName = sourceName;
+ guard.open("close");
+ //System.out.println("DEX FILE cookie is " + mCookie);
+ }
+
+ /**
+ * Open a DEX file, specifying the file in which the optimized DEX
+ * data should be written. If the optimized form exists and appears
+ * to be current, it will be used; if not, the VM will attempt to
+ * regenerate it.
+ *
+ * This is intended for use by applications that wish to download
+ * and execute DEX files outside the usual application installation
+ * mechanism. This function should not be called directly by an
+ * application; instead, use a class loader such as
+ * dalvik.system.DexClassLoader.
+ *
+ * @param sourcePathName
+ * Jar or APK file with "classes.dex". (May expand this to include
+ * "raw DEX" in the future.)
+ * @param outputPathName
+ * File that will hold the optimized form of the DEX data.
+ * @param flags
+ * Enable optional features. (Currently none defined.)
+ * @return
+ * A new or previously-opened DexFile.
+ * @throws IOException
+ * If unable to open the source or output file.
+ */
+ static public DexFile loadDex(String sourcePathName, String outputPathName,
+ int flags) throws IOException {
+
+ /*
+ * TODO: we may want to cache previously-opened DexFile objects.
+ * The cache would be synchronized with close(). This would help
+ * us avoid mapping the same DEX more than once when an app
+ * decided to open it multiple times. In practice this may not
+ * be a real issue.
+ */
+ return new DexFile(sourcePathName, outputPathName, flags);
+ }
+
+ /**
+ * Gets the name of the (already opened) DEX file.
+ *
+ * @return the file name
+ */
+ public String getName() {
+ return mFileName;
+ }
+
+ /**
+ * Closes the DEX file.
+ * <p>
+ * This may not be able to release any resources. If classes from this
+ * DEX file are still resident, the DEX file can't be unmapped.
+ *
+ * @throws IOException
+ * if an I/O error occurs during closing the file, which
+ * normally should not happen
+ */
+ public void close() throws IOException {
+ guard.close();
+ closeDexFile(mCookie);
+ mCookie = 0;
+ }
+
+ /**
+ * Loads a class. Returns the class on success, or a {@code null} reference
+ * on failure.
+ * <p>
+ * If you are not calling this from a class loader, this is most likely not
+ * going to do what you want. Use {@link Class#forName(String)} instead.
+ * <p>
+ * The method does not throw {@link ClassNotFoundException} if the class
+ * isn't found because it isn't reasonable to throw exceptions wildly every
+ * time a class is not found in the first DEX file we look at.
+ *
+ * @param name
+ * the class name, which should look like "java/lang/String"
+ *
+ * @param loader
+ * the class loader that tries to load the class (in most cases
+ * the caller of the method
+ *
+ * @return the {@link Class} object representing the class, or {@code null}
+ * if the class cannot be loaded
+ */
+ public Class loadClass(String name, ClassLoader loader) {
+ String slashName = name.replace('.', '/');
+ return loadClassBinaryName(slashName, loader);
+ }
+
+ /**
+ * See {@link #loadClass(String, ClassLoader)}.
+ *
+ * This takes a "binary" class name to better match ClassLoader semantics.
+ *
+ * @hide
+ */
+ public Class loadClassBinaryName(String name, ClassLoader loader) {
+ return defineClass(name, loader, mCookie);
+ }
+
+ private native static Class defineClass(String name, ClassLoader loader, int cookie);
+
+ /**
+ * Enumerate the names of the classes in this DEX file.
+ *
+ * @return an enumeration of names of classes contained in the DEX file, in
+ * the usual internal form (like "java/lang/String").
+ */
+ public Enumeration<String> entries() {
+ return new DFEnum(this);
+ }
+
+ /*
+ * Helper class.
+ */
+ private class DFEnum implements Enumeration<String> {
+ private int mIndex;
+ private String[] mNameList;
+
+ DFEnum(DexFile df) {
+ mIndex = 0;
+ mNameList = getClassNameList(mCookie);
+ }
+
+ public boolean hasMoreElements() {
+ return (mIndex < mNameList.length);
+ }
+
+ public String nextElement() {
+ return mNameList[mIndex++];
+ }
+ }
+
+ /* return a String array with class names */
+ native private static String[] getClassNameList(int cookie);
+
+ /**
+ * Called when the class is finalized. Makes sure the DEX file is closed.
+ *
+ * @throws IOException
+ * if an I/O error occurs during closing the file, which
+ * normally should not happen
+ */
+ @Override protected void finalize() throws Throwable {
+ try {
+ if (guard != null) {
+ guard.warnIfOpen();
+ }
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /*
+ * Open a DEX file. The value returned is a magic VM cookie. On
+ * failure, an IOException is thrown.
+ */
+ native private static int openDexFile(String sourceName, String outputName,
+ int flags) throws IOException;
+
+ /*
+ * Open a DEX file based on a {@code byte[]}. The value returned
+ * is a magic VM cookie. On failure, a RuntimeException is thrown.
+ */
+ native private static int openDexFile(byte[] fileContents);
+
+ /*
+ * Close DEX file.
+ */
+ native private static void closeDexFile(int cookie);
+
+ /**
+ * Returns true if the VM believes that the apk/jar file is out of date
+ * and should be passed through "dexopt" again.
+ *
+ * @param fileName the absolute path to the apk/jar file to examine.
+ * @return true if dexopt should be called on the file, false otherwise.
+ * @throws java.io.FileNotFoundException if fileName is not readable,
+ * not a file, or not present.
+ * @throws java.io.IOException if fileName is not a valid apk/jar file or
+ * if problems occur while parsing it.
+ * @throws java.lang.NullPointerException if fileName is null.
+ * @throws dalvik.system.StaleDexCacheError if the optimized dex file
+ * is stale but exists on a read-only partition.
+ */
+ native public static boolean isDexOptNeeded(String fileName)
+ throws FileNotFoundException, IOException;
+}
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
new file mode 100644
index 0000000..3d9ee3e
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.regex.Pattern;
+import java.util.zip.ZipFile;
+import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+import libcore.io.StructStat;
+import static libcore.io.OsConstants.*;
+
+/**
+ * A pair of lists of entries, associated with a {@code ClassLoader}.
+ * One of the lists is a dex/resource path &mdash; typically referred
+ * to as a "class path" &mdash; list, and the other names directories
+ * containing native code libraries. Class path entries may be any of:
+ * a {@code .jar} or {@code .zip} file containing an optional
+ * top-level {@code classes.dex} file as well as arbitrary resources,
+ * or a plain {@code .dex} file (with no possibility of associated
+ * resources).
+ *
+ * <p>This class also contains methods to use these lists to look up
+ * classes and resources.</p>
+ */
+/*package*/ final class DexPathList {
+ private static final String DEX_SUFFIX = ".dex";
+ private static final String JAR_SUFFIX = ".jar";
+ private static final String ZIP_SUFFIX = ".zip";
+ private static final String APK_SUFFIX = ".apk";
+
+ /** class definition context */
+ private final ClassLoader definingContext;
+
+ /**
+ * List of dex/resource (class path) elements.
+ * Should be called pathElements, but the Facebook app uses reflection
+ * to modify 'dexElements' (http://b/7726934).
+ */
+ private final Element[] dexElements;
+
+ /** List of native library directories. */
+ private final File[] nativeLibraryDirectories;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param definingContext the context in which any as-yet unresolved
+ * classes should be defined
+ * @param dexPath list of dex/resource path elements, separated by
+ * {@code File.pathSeparator}
+ * @param libraryPath list of native library directory path elements,
+ * separated by {@code File.pathSeparator}
+ * @param optimizedDirectory directory where optimized {@code .dex} files
+ * should be found and written to, or {@code null} to use the default
+ * system directory for same
+ */
+ public DexPathList(ClassLoader definingContext, String dexPath,
+ String libraryPath, File optimizedDirectory) {
+ if (definingContext == null) {
+ throw new NullPointerException("definingContext == null");
+ }
+
+ if (dexPath == null) {
+ throw new NullPointerException("dexPath == null");
+ }
+
+ if (optimizedDirectory != null) {
+ if (!optimizedDirectory.exists()) {
+ throw new IllegalArgumentException(
+ "optimizedDirectory doesn't exist: "
+ + optimizedDirectory);
+ }
+
+ if (!(optimizedDirectory.canRead()
+ && optimizedDirectory.canWrite())) {
+ throw new IllegalArgumentException(
+ "optimizedDirectory not readable/writable: "
+ + optimizedDirectory);
+ }
+ }
+
+ this.definingContext = definingContext;
+ this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory);
+ this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
+ }
+
+ @Override public String toString() {
+ return "DexPathList[" + Arrays.toString(dexElements) +
+ ",nativeLibraryDirectories=" + Arrays.toString(nativeLibraryDirectories) + "]";
+ }
+
+ /**
+ * For BaseDexClassLoader.getLdLibraryPath.
+ */
+ public File[] getNativeLibraryDirectories() {
+ return nativeLibraryDirectories;
+ }
+
+ /**
+ * Splits the given dex path string into elements using the path
+ * separator, pruning out any elements that do not refer to existing
+ * and readable files. (That is, directories are not included in the
+ * result.)
+ */
+ private static ArrayList<File> splitDexPath(String path) {
+ return splitPaths(path, null, false);
+ }
+
+ /**
+ * Splits the given library directory path string into elements
+ * using the path separator ({@code File.pathSeparator}, which
+ * defaults to {@code ":"} on Android, appending on the elements
+ * from the system library path, and pruning out any elements that
+ * do not refer to existing and readable directories.
+ */
+ private static File[] splitLibraryPath(String path) {
+ /*
+ * Native libraries may exist in both the system and
+ * application library paths, and we use this search order:
+ *
+ * 1. this class loader's library path for application
+ * libraries
+ * 2. the VM's library path from the system
+ * property for system libraries
+ *
+ * This order was reversed prior to Gingerbread; see http://b/2933456.
+ */
+ ArrayList<File> result = splitPaths(
+ path, System.getProperty("java.library.path", "."), true);
+ return result.toArray(new File[result.size()]);
+ }
+
+ /**
+ * Splits the given path strings into file elements using the path
+ * separator, combining the results and filtering out elements
+ * that don't exist, aren't readable, or aren't either a regular
+ * file or a directory (as specified). Either string may be empty
+ * or {@code null}, in which case it is ignored. If both strings
+ * are empty or {@code null}, or all elements get pruned out, then
+ * this returns a zero-element list.
+ */
+ private static ArrayList<File> splitPaths(String path1, String path2,
+ boolean wantDirectories) {
+ ArrayList<File> result = new ArrayList<File>();
+
+ splitAndAdd(path1, wantDirectories, result);
+ splitAndAdd(path2, wantDirectories, result);
+ return result;
+ }
+
+ /**
+ * Helper for {@link #splitPaths}, which does the actual splitting
+ * and filtering and adding to a result.
+ */
+ private static void splitAndAdd(String searchPath, boolean directoriesOnly,
+ ArrayList<File> resultList) {
+ if (searchPath == null) {
+ return;
+ }
+ for (String path : searchPath.split(":")) {
+ try {
+ StructStat sb = Libcore.os.stat(path);
+ if (!directoriesOnly || S_ISDIR(sb.st_mode)) {
+ resultList.add(new File(path));
+ }
+ } catch (ErrnoException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Makes an array of dex/resource path elements, one per element of
+ * the given array.
+ */
+ private static Element[] makeDexElements(ArrayList<File> files,
+ File optimizedDirectory) {
+ ArrayList<Element> elements = new ArrayList<Element>();
+
+ /*
+ * Open all files and load the (direct or contained) dex files
+ * up front.
+ */
+ for (File file : files) {
+ File zip = null;
+ DexFile dex = null;
+ String name = file.getName();
+
+ if (name.endsWith(DEX_SUFFIX)) {
+ // Raw dex file (not inside a zip/jar).
+ try {
+ dex = loadDexFile(file, optimizedDirectory);
+ } catch (IOException ex) {
+ System.logE("Unable to load dex file: " + file, ex);
+ }
+ } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
+ || name.endsWith(ZIP_SUFFIX)) {
+ zip = file;
+
+ try {
+ dex = loadDexFile(file, optimizedDirectory);
+ } catch (IOException ignored) {
+ /*
+ * IOException might get thrown "legitimately" by
+ * the DexFile constructor if the zip file turns
+ * out to be resource-only (that is, no
+ * classes.dex file in it). Safe to just ignore
+ * the exception here, and let dex == null.
+ */
+ }
+ } else if (file.isDirectory()) {
+ // We support directories for looking up resources.
+ // This is only useful for running libcore tests.
+ elements.add(new Element(file, true, null, null));
+ } else {
+ System.logW("Unknown file type for: " + file);
+ }
+
+ if ((zip != null) || (dex != null)) {
+ elements.add(new Element(file, false, zip, dex));
+ }
+ }
+
+ return elements.toArray(new Element[elements.size()]);
+ }
+
+ /**
+ * Constructs a {@code DexFile} instance, as appropriate depending
+ * on whether {@code optimizedDirectory} is {@code null}.
+ */
+ private static DexFile loadDexFile(File file, File optimizedDirectory)
+ throws IOException {
+ if (optimizedDirectory == null) {
+ return new DexFile(file);
+ } else {
+ String optimizedPath = optimizedPathFor(file, optimizedDirectory);
+ return DexFile.loadDex(file.getPath(), optimizedPath, 0);
+ }
+ }
+
+ /**
+ * Converts a dex/jar file path and an output directory to an
+ * output file path for an associated optimized dex file.
+ */
+ private static String optimizedPathFor(File path,
+ File optimizedDirectory) {
+ /*
+ * Get the filename component of the path, and replace the
+ * suffix with ".dex" if that's not already the suffix.
+ *
+ * We don't want to use ".odex", because the build system uses
+ * that for files that are paired with resource-only jar
+ * files. If the VM can assume that there's no classes.dex in
+ * the matching jar, it doesn't need to open the jar to check
+ * for updated dependencies, providing a slight performance
+ * boost at startup. The use of ".dex" here matches the use on
+ * files in /data/dalvik-cache.
+ */
+ String fileName = path.getName();
+ if (!fileName.endsWith(DEX_SUFFIX)) {
+ int lastDot = fileName.lastIndexOf(".");
+ if (lastDot < 0) {
+ fileName += DEX_SUFFIX;
+ } else {
+ StringBuilder sb = new StringBuilder(lastDot + 4);
+ sb.append(fileName, 0, lastDot);
+ sb.append(DEX_SUFFIX);
+ fileName = sb.toString();
+ }
+ }
+
+ File result = new File(optimizedDirectory, fileName);
+ return result.getPath();
+ }
+
+ /**
+ * Finds the named class in one of the dex files pointed at by
+ * this instance. This will find the one in the earliest listed
+ * path element. If the class is found but has not yet been
+ * defined, then this method will define it in the defining
+ * context that this instance was constructed with.
+ *
+ * @return the named class or {@code null} if the class is not
+ * found in any of the dex files
+ */
+ public Class findClass(String name) {
+ for (Element element : dexElements) {
+ DexFile dex = element.dexFile;
+
+ if (dex != null) {
+ Class clazz = dex.loadClassBinaryName(name, definingContext);
+ if (clazz != null) {
+ return clazz;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds the named resource in one of the zip/jar files pointed at
+ * by this instance. This will find the one in the earliest listed
+ * path element.
+ *
+ * @return a URL to the named resource or {@code null} if the
+ * resource is not found in any of the zip/jar files
+ */
+ public URL findResource(String name) {
+ for (Element element : dexElements) {
+ URL url = element.findResource(name);
+ if (url != null) {
+ return url;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds all the resources with the given name, returning an
+ * enumeration of them. If there are no resources with the given
+ * name, then this method returns an empty enumeration.
+ */
+ public Enumeration<URL> findResources(String name) {
+ ArrayList<URL> result = new ArrayList<URL>();
+
+ for (Element element : dexElements) {
+ URL url = element.findResource(name);
+ if (url != null) {
+ result.add(url);
+ }
+ }
+
+ return Collections.enumeration(result);
+ }
+
+ /**
+ * Finds the named native code library on any of the library
+ * directories pointed at by this instance. This will find the
+ * one in the earliest listed directory, ignoring any that are not
+ * readable regular files.
+ *
+ * @return the complete path to the library or {@code null} if no
+ * library was found
+ */
+ public String findLibrary(String libraryName) {
+ String fileName = System.mapLibraryName(libraryName);
+ for (File directory : nativeLibraryDirectories) {
+ String path = new File(directory, fileName).getPath();
+ if (IoUtils.canOpenReadOnly(path)) {
+ return path;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Element of the dex/resource file path
+ */
+ /*package*/ static class Element {
+ private final File file;
+ private final boolean isDirectory;
+ private final File zip;
+ private final DexFile dexFile;
+
+ private ZipFile zipFile;
+ private boolean initialized;
+
+ public Element(File file, boolean isDirectory, File zip, DexFile dexFile) {
+ this.file = file;
+ this.isDirectory = isDirectory;
+ this.zip = zip;
+ this.dexFile = dexFile;
+ }
+
+ @Override public String toString() {
+ if (isDirectory) {
+ return "directory \"" + file + "\"";
+ } else if (zip != null) {
+ return "zip file \"" + zip + "\"";
+ } else {
+ return "dex file \"" + dexFile + "\"";
+ }
+ }
+
+ public synchronized void maybeInit() {
+ if (initialized) {
+ return;
+ }
+
+ initialized = true;
+
+ if (isDirectory || zip == null) {
+ return;
+ }
+
+ try {
+ zipFile = new ZipFile(zip);
+ } catch (IOException ioe) {
+ /*
+ * Note: ZipException (a subclass of IOException)
+ * might get thrown by the ZipFile constructor
+ * (e.g. if the file isn't actually a zip/jar
+ * file).
+ */
+ System.logE("Unable to open zip file: " + file, ioe);
+ zipFile = null;
+ }
+ }
+
+ public URL findResource(String name) {
+ maybeInit();
+
+ // We support directories so we can run tests and/or legacy code
+ // that uses Class.getResource.
+ if (isDirectory) {
+ File resourceFile = new File(file, name);
+ if (resourceFile.exists()) {
+ try {
+ return resourceFile.toURI().toURL();
+ } catch (MalformedURLException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ if (zipFile == null || zipFile.getEntry(name) == null) {
+ /*
+ * Either this element has no zip/jar file (first
+ * clause), or the zip/jar file doesn't have an entry
+ * for the given name (second clause).
+ */
+ return null;
+ }
+
+ try {
+ /*
+ * File.toURL() is compliant with RFC 1738 in
+ * always creating absolute path names. If we
+ * construct the URL by concatenating strings, we
+ * might end up with illegal URLs for relative
+ * names.
+ */
+ return new URL("jar:" + file.toURL() + "!/" + name);
+ } catch (MalformedURLException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/NativeStart.java b/dalvik/src/main/java/dalvik/system/NativeStart.java
new file mode 100644
index 0000000..1382823
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/NativeStart.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+/**
+ * Dummy class used during JNI initialization. The JNI functions want
+ * to be able to create objects, and the VM needs to discard the references
+ * when the function returns. That gets a little weird when we're
+ * calling JNI functions from the C main(), and there's no Java stack frame
+ * to hitch the references onto.
+ *
+ * Rather than having some special-case code, we create this simple little
+ * class and pretend that it called the C main().
+ *
+ * This also comes in handy when a native thread attaches itself with the
+ * JNI AttachCurrentThread call. If they attach the thread and start
+ * creating objects, we need a fake frame to store stuff in.
+ */
+class NativeStart {
+ private NativeStart() {}
+
+ private static native void main(String[] dummy);
+
+ private static native void run();
+}
diff --git a/dalvik/src/main/java/dalvik/system/PathClassLoader.java b/dalvik/src/main/java/dalvik/system/PathClassLoader.java
new file mode 100644
index 0000000..32c5586
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/PathClassLoader.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+/**
+ * Provides a simple {@link ClassLoader} implementation that operates on a list
+ * of files and directories in the local file system, but does not attempt to
+ * load classes from the network. Android uses this class for its system class
+ * loader and for its application class loader(s).
+ */
+public class PathClassLoader extends BaseDexClassLoader {
+ /**
+ * Creates a {@code PathClassLoader} that operates on a given list of files
+ * and directories. This method is equivalent to calling
+ * {@link #PathClassLoader(String, String, ClassLoader)} with a
+ * {@code null} value for the second argument (see description there).
+ *
+ * @param dexPath the list of jar/apk files containing classes and
+ * resources, delimited by {@code File.pathSeparator}, which
+ * defaults to {@code ":"} on Android
+ * @param parent the parent class loader
+ */
+ public PathClassLoader(String dexPath, ClassLoader parent) {
+ super(dexPath, null, null, parent);
+ }
+
+ /**
+ * Creates a {@code PathClassLoader} that operates on two given
+ * lists of files and directories. The entries of the first list
+ * should be one of the following:
+ *
+ * <ul>
+ * <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as
+ * well as arbitrary resources.
+ * <li>Raw ".dex" files (not inside a zip file).
+ * </ul>
+ *
+ * The entries of the second list should be directories containing
+ * native library files.
+ *
+ * @param dexPath the list of jar/apk files containing classes and
+ * resources, delimited by {@code File.pathSeparator}, which
+ * defaults to {@code ":"} on Android
+ * @param libraryPath the list of directories containing native
+ * libraries, delimited by {@code File.pathSeparator}; may be
+ * {@code null}
+ * @param parent the parent class loader
+ */
+ public PathClassLoader(String dexPath, String libraryPath,
+ ClassLoader parent) {
+ super(dexPath, null, libraryPath, parent);
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java b/dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java
new file mode 100644
index 0000000..7a18bb5
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+/**
+ * Is thrown when the VM identifies a potential deadlock.
+ *
+ * @hide
+ */
+public class PotentialDeadlockError extends VirtualMachineError {
+ /**
+ * Creates a new exception instance and initializes it with default values.
+ */
+ public PotentialDeadlockError() {
+ super();
+ }
+
+ /**
+ * Creates a new exception instance and initializes it with a given message.
+ *
+ * @param detailMessage the error message
+ */
+ public PotentialDeadlockError(String detailMessage) {
+ super(detailMessage);
+ }
+}
+
diff --git a/dalvik/src/main/java/dalvik/system/SocketTagger.java b/dalvik/src/main/java/dalvik/system/SocketTagger.java
new file mode 100644
index 0000000..75242ce
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/SocketTagger.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+import java.io.FileDescriptor;
+import java.net.Socket;
+import java.net.SocketException;
+
+/**
+ * Callbacks for socket assignment and reassignment.
+ *
+ * @hide
+ */
+public abstract class SocketTagger {
+
+ private static SocketTagger tagger = new SocketTagger() {
+ @Override public void tag(FileDescriptor socketDescriptor) throws SocketException {}
+ @Override public void untag(FileDescriptor socketDescriptor) throws SocketException {}
+ };
+
+ /**
+ * Notified when {@code socketDescriptor} is either assigned to the current
+ * thread. The socket is either newly connected or reused from a connection
+ * pool. Implementations of this method should be thread-safe.
+ */
+ public abstract void tag(FileDescriptor socketDescriptor) throws SocketException;
+
+ /**
+ * Notified when {@code socketDescriptor} is released from the current
+ * thread to a connection pool. Implementations of this method should be
+ * thread-safe.
+ *
+ * <p><strong>Note:</strong> this method will not be invoked when the socket
+ * is closed.
+ */
+ public abstract void untag(FileDescriptor socketDescriptor) throws SocketException;
+
+ public final void tag(Socket socket) throws SocketException {
+ if (!socket.isClosed()) {
+ tag(socket.getFileDescriptor$());
+ }
+ }
+
+ public final void untag(Socket socket) throws SocketException {
+ if (!socket.isClosed()) {
+ untag(socket.getFileDescriptor$());
+ }
+ }
+
+ /**
+ * Sets this process' socket tagger to {@code tagger}.
+ */
+ public static synchronized void set(SocketTagger tagger) {
+ if (tagger == null) {
+ throw new NullPointerException("tagger == null");
+ }
+ SocketTagger.tagger = tagger;
+ }
+
+ /**
+ * Returns this process socket tagger.
+ */
+ public static synchronized SocketTagger get() {
+ return tagger;
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/StaleDexCacheError.java b/dalvik/src/main/java/dalvik/system/StaleDexCacheError.java
new file mode 100644
index 0000000..8bdd86a
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/StaleDexCacheError.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+/**
+ * Is thrown when the VM determines that a DEX file's cache is out of date, and
+ * that there is no way to recreate it.
+ *
+ * @hide
+ */
+public class StaleDexCacheError extends VirtualMachineError {
+ /**
+ * Creates a new exception instance and initializes it with default values.
+ */
+ public StaleDexCacheError() {
+ super();
+ }
+
+ /**
+ * Creates a new exception instance and initializes it with a given message.
+ *
+ * @param detailMessage the error message
+ */
+ public StaleDexCacheError(String detailMessage) {
+ super(detailMessage);
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/TemporaryDirectory.java b/dalvik/src/main/java/dalvik/system/TemporaryDirectory.java
new file mode 100644
index 0000000..f8fb0b1
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/TemporaryDirectory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+import java.io.File;
+
+/**
+ * Obsolete, for binary compatibility only.
+ *
+ * @hide
+ */
+public class TemporaryDirectory {
+ /**
+ * This method exists for binary compatibility only.
+ */
+ public static void setUpDirectory(String baseDir) {
+ }
+
+ /**
+ * This method exists for binary compatibility only.
+ */
+ public static synchronized void setUpDirectory(File baseDir) {
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
new file mode 100644
index 0000000..8f40165
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * Provides access to some VM-specific debug features. Though this class and
+ * many of its members are public, this class is meant to be wrapped in a more
+ * friendly way for use by application developers. On the Android platform, the
+ * recommended way to access this functionality is through the class
+ * <code>android.os.Debug</code>.
+ *
+ * @hide
+ */
+public final class VMDebug {
+ /**
+ * Specifies the default method trace data file name.
+ *
+ * @deprecated only used in one place, which is unused and deprecated
+ */
+ @Deprecated
+ static public final String DEFAULT_METHOD_TRACE_FILE_NAME = "/sdcard/dmtrace.trace";
+
+ /**
+ * flag for startMethodTracing(), which adds the results from
+ * startAllocCounting to the trace key file.
+ */
+ public static final int TRACE_COUNT_ALLOCS = 1;
+
+ /* constants for getAllocCount */
+ private static final int KIND_ALLOCATED_OBJECTS = 1<<0;
+ private static final int KIND_ALLOCATED_BYTES = 1<<1;
+ private static final int KIND_FREED_OBJECTS = 1<<2;
+ private static final int KIND_FREED_BYTES = 1<<3;
+ private static final int KIND_GC_INVOCATIONS = 1<<4;
+ private static final int KIND_CLASS_INIT_COUNT = 1<<5;
+ private static final int KIND_CLASS_INIT_TIME = 1<<6;
+ private static final int KIND_EXT_ALLOCATED_OBJECTS = 1<<12;
+ private static final int KIND_EXT_ALLOCATED_BYTES = 1<<13;
+ private static final int KIND_EXT_FREED_OBJECTS = 1<<14;
+ private static final int KIND_EXT_FREED_BYTES = 1<<15;
+
+ public static final int KIND_GLOBAL_ALLOCATED_OBJECTS =
+ KIND_ALLOCATED_OBJECTS;
+ public static final int KIND_GLOBAL_ALLOCATED_BYTES =
+ KIND_ALLOCATED_BYTES;
+ public static final int KIND_GLOBAL_FREED_OBJECTS =
+ KIND_FREED_OBJECTS;
+ public static final int KIND_GLOBAL_FREED_BYTES =
+ KIND_FREED_BYTES;
+ public static final int KIND_GLOBAL_GC_INVOCATIONS =
+ KIND_GC_INVOCATIONS;
+ public static final int KIND_GLOBAL_CLASS_INIT_COUNT =
+ KIND_CLASS_INIT_COUNT;
+ public static final int KIND_GLOBAL_CLASS_INIT_TIME =
+ KIND_CLASS_INIT_TIME;
+ public static final int KIND_GLOBAL_EXT_ALLOCATED_OBJECTS =
+ KIND_EXT_ALLOCATED_OBJECTS;
+ public static final int KIND_GLOBAL_EXT_ALLOCATED_BYTES =
+ KIND_EXT_ALLOCATED_BYTES;
+ public static final int KIND_GLOBAL_EXT_FREED_OBJECTS =
+ KIND_EXT_FREED_OBJECTS;
+ public static final int KIND_GLOBAL_EXT_FREED_BYTES =
+ KIND_EXT_FREED_BYTES;
+
+ public static final int KIND_THREAD_ALLOCATED_OBJECTS =
+ KIND_ALLOCATED_OBJECTS << 16;
+ public static final int KIND_THREAD_ALLOCATED_BYTES =
+ KIND_ALLOCATED_BYTES << 16;
+ public static final int KIND_THREAD_FREED_OBJECTS =
+ KIND_FREED_OBJECTS << 16;
+ public static final int KIND_THREAD_FREED_BYTES =
+ KIND_FREED_BYTES << 16;
+ public static final int KIND_THREAD_GC_INVOCATIONS =
+ KIND_GC_INVOCATIONS << 16;
+ public static final int KIND_THREAD_CLASS_INIT_COUNT =
+ KIND_CLASS_INIT_COUNT << 16;
+ public static final int KIND_THREAD_CLASS_INIT_TIME =
+ KIND_CLASS_INIT_TIME << 16;
+ public static final int KIND_THREAD_EXT_ALLOCATED_OBJECTS =
+ KIND_EXT_ALLOCATED_OBJECTS << 16;
+ public static final int KIND_THREAD_EXT_ALLOCATED_BYTES =
+ KIND_EXT_ALLOCATED_BYTES << 16;
+ public static final int KIND_THREAD_EXT_FREED_OBJECTS =
+ KIND_EXT_FREED_OBJECTS << 16;
+ public static final int KIND_THREAD_EXT_FREED_BYTES =
+ KIND_EXT_FREED_BYTES << 16;
+
+ public static final int KIND_ALL_COUNTS = 0xffffffff;
+
+ /* all methods are static */
+ private VMDebug() {}
+
+ /**
+ * Returns the time since the last known debugger activity.
+ *
+ * @return the time in milliseconds, or -1 if the debugger is not connected
+ */
+ public static native long lastDebuggerActivity();
+
+ /**
+ * Determines if debugging is enabled in this VM. If debugging is not
+ * enabled, a debugger cannot be attached.
+ *
+ * @return true if debugging is enabled
+ */
+ public static native boolean isDebuggingEnabled();
+
+ /**
+ * Determines if a debugger is currently attached.
+ *
+ * @return true if (and only if) a debugger is connected
+ */
+ public static native boolean isDebuggerConnected();
+
+ /**
+ * Returns an array of strings that identify VM features. This is
+ * used by DDMS to determine what sorts of operations the VM can
+ * perform.
+ */
+ public static native String[] getVmFeatureList();
+
+ /**
+ * Start method tracing with default name, size, and with <code>0</code>
+ * flags.
+ *
+ * @deprecated not used, not needed
+ */
+ @Deprecated
+ public static void startMethodTracing() {
+ startMethodTracing(DEFAULT_METHOD_TRACE_FILE_NAME, 0, 0);
+ }
+
+ /**
+ * Start method tracing, specifying a file name as well as a default
+ * buffer size. See <a
+ * href="{@docRoot}guide/developing/tools/traceview.html"> Running the
+ * Traceview Debugging Program</a> for information about reading
+ * trace files.
+ *
+ * <p>You can use either a fully qualified path and
+ * name, or just a name. If only a name is specified, the file will
+ * be created under the /sdcard/ directory. If a name is not given,
+ * the default is /sdcard/dmtrace.trace.</p>
+ *
+ * @param traceFileName name to give the trace file
+ * @param bufferSize the maximum size of both files combined. If passed
+ * as <code>0</code>, it defaults to 8MB.
+ * @param flags flags to control method tracing. The only one that
+ * is currently defined is {@link #TRACE_COUNT_ALLOCS}.
+ */
+ public static void startMethodTracing(String traceFileName, int bufferSize, int flags) {
+
+ if (traceFileName == null) {
+ throw new NullPointerException("traceFileName == null");
+ }
+
+ startMethodTracingNative(traceFileName, null, bufferSize, flags);
+ }
+
+ /**
+ * Like startMethodTracing(String, int, int), but taking an already-opened
+ * FileDescriptor in which the trace is written. The file name is also
+ * supplied simply for logging. Makes a dup of the file descriptor.
+ */
+ public static void startMethodTracing(String traceFileName,
+ FileDescriptor fd, int bufferSize, int flags)
+ {
+ if (traceFileName == null) {
+ throw new NullPointerException("traceFileName == null");
+ }
+ if (fd == null) {
+ throw new NullPointerException("fd == null");
+ }
+
+ startMethodTracingNative(traceFileName, fd, bufferSize, flags);
+ }
+
+ /**
+ * Starts method tracing without a backing file. When stopMethodTracing
+ * is called, the result is sent directly to DDMS. (If DDMS is not
+ * attached when tracing ends, the profiling data will be discarded.)
+ */
+ public static void startMethodTracingDdms(int bufferSize, int flags) {
+ startMethodTracingNative(null, null, bufferSize, flags);
+ }
+
+ /**
+ * Implements all startMethodTracing variants.
+ */
+ private static native void startMethodTracingNative(String traceFileName,
+ FileDescriptor fd, int bufferSize, int flags);
+
+ /**
+ * Determine whether method tracing is currently active.
+ */
+ public static native boolean isMethodTracingActive();
+
+ /**
+ * Stops method tracing.
+ */
+ public static native void stopMethodTracing();
+
+ /**
+ * Starts sending Dalvik method trace info to the emulator.
+ */
+ public static native void startEmulatorTracing();
+
+ /**
+ * Stops sending Dalvik method trace info to the emulator.
+ */
+ public static native void stopEmulatorTracing();
+
+ /**
+ * Get an indication of thread CPU usage. The value returned indicates the
+ * amount of time that the current thread has spent executing code or
+ * waiting for certain types of I/O.
+ * <p>
+ * The time is expressed in nanoseconds, and is only meaningful when
+ * compared to the result from an earlier call. Note that nanosecond
+ * resolution does not imply nanosecond accuracy.
+ *
+ * @return the CPU usage. A value of -1 means the system does not support
+ * this feature.
+ */
+ public static native long threadCpuTimeNanos();
+
+ /**
+ * Count the number and aggregate size of memory allocations between
+ * two points.
+ */
+ public static native void startAllocCounting();
+ public static native void stopAllocCounting();
+ public static native int getAllocCount(int kind);
+ public static native void resetAllocCount(int kinds);
+
+ /**
+ * This method exists for binary compatibility. It was part of
+ * the allocation limits API which was removed in Honeycomb.
+ */
+ @Deprecated
+ public static int setAllocationLimit(int limit) {
+ return -1;
+ }
+
+ /**
+ * This method exists for binary compatibility. It was part of
+ * the allocation limits API which was removed in Honeycomb.
+ */
+ @Deprecated
+ public static int setGlobalAllocationLimit(int limit) {
+ return -1;
+ }
+
+ /**
+ * Count the number of instructions executed between two points.
+ */
+ public static native void startInstructionCounting();
+ public static native void stopInstructionCounting();
+ public static native void getInstructionCount(int[] counts);
+ public static native void resetInstructionCount();
+
+ /**
+ * Dumps a list of loaded class to the log file.
+ */
+ public static native void printLoadedClasses(int flags);
+
+ /**
+ * Gets the number of loaded classes.
+ *
+ * @return the number of loaded classes
+ */
+ public static native int getLoadedClassCount();
+
+ /**
+ * Dumps "hprof" data to the specified file. This may cause a GC.
+ *
+ * The VM may create a temporary file in the same directory.
+ *
+ * @param filename Full pathname of output file (e.g. "/sdcard/dump.hprof").
+ * @throws UnsupportedOperationException if the VM was built without
+ * HPROF support.
+ * @throws IOException if an error occurs while opening or writing files.
+ */
+ public static void dumpHprofData(String filename) throws IOException {
+ if (filename == null) {
+ throw new NullPointerException("filename == null");
+ }
+ dumpHprofData(filename, null);
+ }
+
+ /**
+ * Collects "hprof" heap data and sends it to DDMS. This may cause a GC.
+ *
+ * @throws UnsupportedOperationException if the VM was built without
+ * HPROF support.
+ */
+ public static native void dumpHprofDataDdms();
+
+ /**
+ * Dumps "hprof" heap data to a file, by name or descriptor.
+ *
+ * @param fileName Name of output file. If fd is non-null, the
+ * file name is only used in log messages (and may be null).
+ * @param fd Descriptor of open file that will receive the output.
+ * If this is null, the fileName is used instead.
+ */
+ public static native void dumpHprofData(String fileName, FileDescriptor fd)
+ throws IOException;
+
+ /**
+ * Primes the register map cache.
+ */
+ public static native boolean cacheRegisterMap(String classAndMethodDesc);
+
+ /**
+ * Dumps the contents of the VM reference tables (e.g. JNI locals and
+ * globals) to the log file.
+ */
+ public static native void dumpReferenceTables();
+
+ /**
+ * Crashes the VM. Seriously. Dumps the interpreter stack trace for
+ * the current thread and then aborts the VM so you can see the native
+ * stack trace. Useful for figuring out how you got somewhere when
+ * lots of native code is involved.
+ */
+ public static native void crash();
+
+ /**
+ * Together with gdb, provide a handy way to stop the VM at user-tagged
+ * locations.
+ */
+ public static native void infopoint(int id);
+
+ /*
+ * Fake method, inserted into dmtrace output when the garbage collector
+ * runs. Not actually called.
+ */
+ private static void startGC() {}
+
+ /*
+ * Fake method, inserted into dmtrace output during class preparation
+ * (loading and linking, but not verification or initialization). Not
+ * actually called.
+ */
+ private static void startClassPrep() {}
+
+ /**
+ * Counts the instances of a class.
+ *
+ * @param klass the class to be counted.
+ * @param assignable if false, direct instances of klass are
+ * counted. If true, instances that are
+ * assignable to klass, as defined by
+ * {@link Class#isAssignableFrom} are counted.
+ * @return the number of matching instances.
+ */
+ public static native long countInstancesOfClass(Class klass, boolean assignable);
+}
diff --git a/dalvik/src/main/java/dalvik/system/VMRuntime.java b/dalvik/src/main/java/dalvik/system/VMRuntime.java
new file mode 100644
index 0000000..71098be
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/VMRuntime.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+/**
+ * Provides an interface to VM-global, Dalvik-specific features.
+ * An application cannot create its own Runtime instance, and must obtain
+ * one from the getRuntime method.
+ *
+ * @hide
+ */
+public final class VMRuntime {
+
+ /**
+ * Holds the VMRuntime singleton.
+ */
+ private static final VMRuntime THE_ONE = new VMRuntime();
+
+ /**
+ * Prevents this class from being instantiated.
+ */
+ private VMRuntime() {
+ }
+
+ /**
+ * Returns the object that represents the VM instance's Dalvik-specific
+ * runtime environment.
+ *
+ * @return the runtime object
+ */
+ public static VMRuntime getRuntime() {
+ return THE_ONE;
+ }
+
+ /**
+ * Returns a copy of the VM's command-line property settings.
+ * These are in the form "name=value" rather than "-Dname=value".
+ */
+ public native String[] properties();
+
+ /**
+ * Returns the VM's boot class path.
+ */
+ public native String bootClassPath();
+
+ /**
+ * Returns the VM's class path.
+ */
+ public native String classPath();
+
+ /**
+ * Returns the VM's version.
+ */
+ public native String vmVersion();
+
+ /**
+ * Gets the current ideal heap utilization, represented as a number
+ * between zero and one. After a GC happens, the Dalvik heap may
+ * be resized so that (size of live objects) / (size of heap) is
+ * equal to this number.
+ *
+ * @return the current ideal heap utilization
+ */
+ public native float getTargetHeapUtilization();
+
+ /**
+ * Sets the current ideal heap utilization, represented as a number
+ * between zero and one. After a GC happens, the Dalvik heap may
+ * be resized so that (size of live objects) / (size of heap) is
+ * equal to this number.
+ *
+ * <p>This is only a hint to the garbage collector and may be ignored.
+ *
+ * @param newTarget the new suggested ideal heap utilization.
+ * This value may be adjusted internally.
+ * @return the previous ideal heap utilization
+ * @throws IllegalArgumentException if newTarget is &lt;= 0.0 or &gt;= 1.0
+ */
+ public float setTargetHeapUtilization(float newTarget) {
+ if (newTarget <= 0.0f || newTarget >= 1.0f) {
+ throw new IllegalArgumentException(newTarget +
+ " out of range (0,1)");
+ }
+ /* Synchronize to make sure that only one thread gets
+ * a given "old" value if both update at the same time.
+ * Allows for reliable save-and-restore semantics.
+ */
+ synchronized (this) {
+ float oldTarget = getTargetHeapUtilization();
+ nativeSetTargetHeapUtilization(newTarget);
+ return oldTarget;
+ }
+ }
+
+ /**
+ * Sets the target SDK version. Should only be called before the
+ * app starts to run, because it may change the VM's behavior in
+ * dangerous ways. Use 0 to mean "current" (since callers won't
+ * necessarily know the actual current SDK version, and the
+ * allocated version numbers start at 1).
+ */
+ public native void setTargetSdkVersion(int targetSdkVersion);
+
+ /**
+ * This method exists for binary compatibility. It was part of a
+ * heap sizing API which was removed in Honeycomb.
+ */
+ @Deprecated
+ public long getMinimumHeapSize() {
+ return 0;
+ }
+
+ /**
+ * This method exists for binary compatibility. It was part of a
+ * heap sizing API which was removed in Honeycomb.
+ */
+ @Deprecated
+ public long setMinimumHeapSize(long size) {
+ return 0;
+ }
+
+ /**
+ * This method exists for binary compatibility. It used to
+ * perform a garbage collection that cleared SoftReferences.
+ */
+ @Deprecated
+ public void gcSoftReferences() {}
+
+ /**
+ * This method exists for binary compatibility. It is equivalent
+ * to {@link System#runFinalization}.
+ */
+ @Deprecated
+ public void runFinalizationSync() {
+ System.runFinalization();
+ }
+
+ /**
+ * Implements setTargetHeapUtilization().
+ *
+ * @param newTarget the new suggested ideal heap utilization.
+ * This value may be adjusted internally.
+ */
+ private native void nativeSetTargetHeapUtilization(float newTarget);
+
+ /**
+ * This method exists for binary compatibility. It was part of
+ * the external allocation API which was removed in Honeycomb.
+ */
+ @Deprecated
+ public boolean trackExternalAllocation(long size) {
+ return true;
+ }
+
+ /**
+ * This method exists for binary compatibility. It was part of
+ * the external allocation API which was removed in Honeycomb.
+ */
+ @Deprecated
+ public void trackExternalFree(long size) {}
+
+ /**
+ * This method exists for binary compatibility. It was part of
+ * the external allocation API which was removed in Honeycomb.
+ */
+ @Deprecated
+ public long getExternalBytesAllocated() {
+ return 0;
+ }
+
+ /**
+ * Tells the VM to enable the JIT compiler. If the VM does not have a JIT
+ * implementation, calling this method should have no effect.
+ */
+ public native void startJitCompilation();
+
+ /**
+ * Tells the VM to disable the JIT compiler. If the VM does not have a JIT
+ * implementation, calling this method should have no effect.
+ */
+ public native void disableJitCompilation();
+
+ /**
+ * Returns an array allocated in an area of the Java heap where it will never be moved.
+ * This is used to implement native allocations on the Java heap, such as DirectByteBuffers
+ * and Bitmaps.
+ */
+ public native Object newNonMovableArray(Class<?> componentType, int length);
+
+ /**
+ * Returns the address of array[0]. This differs from using JNI in that JNI might lie and
+ * give you the address of a copy of the array when in forcecopy mode.
+ */
+ public native long addressOf(Object array);
+
+ /**
+ * Removes any growth limits, allowing the application to allocate
+ * up to the maximum heap size.
+ */
+ public native void clearGrowthLimit();
+
+ /**
+ * Returns true if either a Java debugger or native debugger is active.
+ */
+ public native boolean isDebuggerActive();
+}
diff --git a/dalvik/src/main/java/dalvik/system/VMStack.java b/dalvik/src/main/java/dalvik/system/VMStack.java
new file mode 100644
index 0000000..9a2be23
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/VMStack.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+/**
+ * Provides a limited interface to the Dalvik VM stack. This class is mostly
+ * used for implementing security checks.
+ *
+ * @hide
+ */
+public final class VMStack {
+ /**
+ * Returns the defining class loader of the caller's caller.
+ *
+ * @return the requested class loader, or {@code null} if this is the
+ * bootstrap class loader.
+ */
+ native public static ClassLoader getCallingClassLoader();
+
+ /**
+ * Returns the class of the caller's caller's caller.
+ *
+ * @return the requested class, or {@code null}.
+ */
+ native public static Class<?> getStackClass2();
+
+ /**
+ * Creates an array of classes from the methods at the top of the stack.
+ * We continue until we reach the bottom of the stack or exceed the
+ * specified maximum depth.
+ * <p>
+ * The topmost stack frame (this method) and the one above that (the
+ * caller) are excluded from the array. Frames with java.lang.reflect
+ * classes are skipped over.
+ * <p>
+ * The classes in the array are the defining classes of the methods.
+ * <p>
+ * This is similar to Harmony's VMStack.getClasses, except that this
+ * implementation doesn't have a concept of "privileged" frames.
+ *
+ * @param maxDepth
+ * maximum number of classes to return, or -1 for all
+ * @return an array with classes for the most-recent methods on the stack
+ */
+ native public static Class<?>[] getClasses(int maxDepth);
+
+ /**
+ * Retrieves the stack trace from the specified thread.
+ *
+ * @param t
+ * thread of interest
+ * @return an array of stack trace elements, or null if the thread
+ * doesn't have a stack trace (e.g. because it exited)
+ */
+ native public static StackTraceElement[] getThreadStackTrace(Thread t);
+
+ /**
+ * Retrieves a partial stack trace from the specified thread into
+ * the provided array.
+ *
+ * @param t
+ * thread of interest
+ * @param stackTraceElements
+ * preallocated array for use when only the top of stack is
+ * desired. Unused elements will be filled with null values.
+ * @return the number of elements filled
+ */
+ native public static int fillStackTraceElements(Thread t,
+ StackTraceElement[] stackTraceElements);
+}
diff --git a/dalvik/src/main/java/dalvik/system/Zygote.java b/dalvik/src/main/java/dalvik/system/Zygote.java
new file mode 100644
index 0000000..9e96204
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/Zygote.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * 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 dalvik.system;
+
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+
+import java.io.File;
+
+/**
+ * Provides access to the Dalvik "zygote" feature, which allows a VM instance to
+ * be partially initialized and then fork()'d from the partially initialized
+ * state.
+ *
+ * @hide
+ */
+public class Zygote {
+ /*
+ * Bit values for "debugFlags" argument. The definitions are duplicated
+ * in the native code.
+ */
+ /** enable debugging over JDWP */
+ public static final int DEBUG_ENABLE_DEBUGGER = 1;
+ /** enable JNI checks */
+ public static final int DEBUG_ENABLE_CHECKJNI = 1 << 1;
+ /** enable Java programming language "assert" statements */
+ public static final int DEBUG_ENABLE_ASSERT = 1 << 2;
+ /** disable the JIT compiler */
+ public static final int DEBUG_ENABLE_SAFEMODE = 1 << 3;
+ /** Enable logging of third-party JNI activity. */
+ public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4;
+
+ /** No external storage should be mounted. */
+ public static final int MOUNT_EXTERNAL_NONE = 0;
+ /** Single-user external storage should be mounted. */
+ public static final int MOUNT_EXTERNAL_SINGLEUSER = 1;
+ /** Multi-user external storage should be mounted. */
+ public static final int MOUNT_EXTERNAL_MULTIUSER = 2;
+ /** All multi-user external storage should be mounted. */
+ public static final int MOUNT_EXTERNAL_MULTIUSER_ALL = 3;
+
+ /**
+ * When set by the system server, all subsequent apps will be launched in
+ * VM safe mode.
+ */
+ public static boolean systemInSafeMode = false;
+
+ private Zygote() {}
+
+ private static void preFork() {
+ Daemons.stop();
+ waitUntilAllThreadsStopped();
+ }
+
+ /**
+ * We must not fork until we're single-threaded again. Wait until /proc shows we're
+ * down to just one thread.
+ */
+ private static void waitUntilAllThreadsStopped() {
+ File tasks = new File("/proc/self/task");
+ while (tasks.list().length > 1) {
+ try {
+ // Experimentally, booting and playing about with a stingray, I never saw us
+ // go round this loop more than once with a 10ms sleep.
+ Thread.sleep(10);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+
+ private static void postFork() {
+ Daemons.start();
+ }
+
+ /**
+ * Forks a new Zygote instance, but does not leave the zygote mode.
+ * The current VM must have been started with the -Xzygote flag. The
+ * new child is expected to eventually call forkAndSpecialize()
+ *
+ * @return 0 if this is the child, pid of the child
+ * if this is the parent, or -1 on error
+ */
+ public static int fork() {
+ preFork();
+ int pid = nativeFork();
+ postFork();
+ return pid;
+ }
+
+ native public static int nativeFork();
+
+ /**
+ * Forks a new VM instance. The current VM must have been started
+ * with the -Xzygote flag. <b>NOTE: new instance keeps all
+ * root capabilities. The new process is expected to call capset()</b>.
+ *
+ * @param uid the UNIX uid that the new process should setuid() to after
+ * fork()ing and and before spawning any threads.
+ * @param gid the UNIX gid that the new process should setgid() to after
+ * fork()ing and and before spawning any threads.
+ * @param gids null-ok; a list of UNIX gids that the new process should
+ * setgroups() to after fork and before spawning any threads.
+ * @param debugFlags bit flags that enable debugging features.
+ * @param rlimits null-ok an array of rlimit tuples, with the second
+ * dimension having a length of 3 and representing
+ * (resource, rlim_cur, rlim_max). These are set via the posix
+ * setrlimit(2) call.
+ * @param seInfo null-ok a string specifying SEAndroid information for
+ * the new process.
+ * @param niceName null-ok a string specifying the process name.
+ *
+ * @return 0 if this is the child, pid of the child
+ * if this is the parent, or -1 on error.
+ */
+ public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
+ int[][] rlimits, int mountExternal, String seInfo, String niceName) {
+ preFork();
+ int pid = nativeForkAndSpecialize(
+ uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName);
+ postFork();
+ return pid;
+ }
+
+ native public static int nativeForkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
+ int[][] rlimits, int mountExternal, String seInfo, String niceName);
+
+ /**
+ * Special method to start the system server process. In addition to the
+ * common actions performed in forkAndSpecialize, the pid of the child
+ * process is recorded such that the death of the child process will cause
+ * zygote to exit.
+ *
+ * @param uid the UNIX uid that the new process should setuid() to after
+ * fork()ing and and before spawning any threads.
+ * @param gid the UNIX gid that the new process should setgid() to after
+ * fork()ing and and before spawning any threads.
+ * @param gids null-ok; a list of UNIX gids that the new process should
+ * setgroups() to after fork and before spawning any threads.
+ * @param debugFlags bit flags that enable debugging features.
+ * @param rlimits null-ok an array of rlimit tuples, with the second
+ * dimension having a length of 3 and representing
+ * (resource, rlim_cur, rlim_max). These are set via the posix
+ * setrlimit(2) call.
+ * @param permittedCapabilities argument for setcap()
+ * @param effectiveCapabilities argument for setcap()
+ *
+ * @return 0 if this is the child, pid of the child
+ * if this is the parent, or -1 on error.
+ */
+ public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
+ int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
+ preFork();
+ int pid = nativeForkSystemServer(
+ uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
+ postFork();
+ return pid;
+ }
+
+ native public static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
+ int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
+
+ /**
+ * Executes "/system/bin/sh -c &lt;command&gt;" using the exec() system call.
+ * This method throws a runtime exception if exec() failed, otherwise, this
+ * method never returns.
+ *
+ * @param command The shell command to execute.
+ */
+ public static void execShell(String command) {
+ String[] args = { "/system/bin/sh", "-c", command };
+ try {
+ Libcore.os.execv(args[0], args);
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Appends quotes shell arguments to the specified string builder.
+ * The arguments are quoted using single-quotes, escaped if necessary,
+ * prefixed with a space, and appended to the command.
+ *
+ * @param command A string builder for the shell command being constructed.
+ * @param args An array of argument strings to be quoted and appended to the command.
+ * @see #execShell(String)
+ */
+ public static void appendQuotedShellArgs(StringBuilder command, String[] args) {
+ for (String arg : args) {
+ command.append(" '").append(arg.replace("'", "'\\''")).append("'");
+ }
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java b/dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java
new file mode 100644
index 0000000..dbddcc6
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/AsciiHprofWriter.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * AsciiHprofWriter produces hprof compatible text output for use with
+ * third party tools such as PerfAnal.
+ */
+public final class AsciiHprofWriter {
+
+ private final HprofData data;
+ private final PrintWriter out;
+
+ /**
+ * Writes the provided data to the specified stream.
+ */
+ public static void write(HprofData data, OutputStream outputStream) throws IOException {
+ new AsciiHprofWriter(data, outputStream).write();
+ }
+
+ private AsciiHprofWriter(HprofData data, OutputStream outputStream) {
+ this.data = data;
+ this.out = new PrintWriter(outputStream);
+ }
+
+ private void write() throws IOException {
+ for (HprofData.ThreadEvent e : data.getThreadHistory()) {
+ out.println(e);
+ }
+
+ List<HprofData.Sample> samples
+ = new ArrayList<HprofData.Sample>(data.getSamples());
+ Collections.sort(samples, SAMPLE_COMPARATOR);
+ int total = 0;
+ for (HprofData.Sample sample : samples) {
+ HprofData.StackTrace stackTrace = sample.stackTrace;
+ int count = sample.count;
+ total += count;
+ out.printf("TRACE %d: (thread=%d)\n",
+ stackTrace.stackTraceId,
+ stackTrace.threadId);
+ for (StackTraceElement e : stackTrace.stackFrames) {
+ out.printf("\t%s\n", e);
+ }
+ }
+ Date now = new Date(data.getStartMillis());
+ // "CPU SAMPLES BEGIN (total = 826) Wed Jul 21 12:03:46 2010"
+ out.printf("CPU SAMPLES BEGIN (total = %d) %ta %tb %td %tT %tY\n",
+ total, now, now, now, now, now);
+ out.printf("rank self accum count trace method\n");
+ int rank = 0;
+ double accum = 0;
+ for (HprofData.Sample sample : samples) {
+ rank++;
+ HprofData.StackTrace stackTrace = sample.stackTrace;
+ int count = sample.count;
+ double self = (double)count/(double)total;
+ accum += self;
+
+ // " 1 65.62% 65.62% 542 300302 java.lang.Long.parseLong"
+ out.printf("% 4d% 6.2f%%% 6.2f%% % 7d % 5d %s.%s\n",
+ rank, self*100, accum*100, count, stackTrace.stackTraceId,
+ stackTrace.stackFrames[0].getClassName(),
+ stackTrace.stackFrames[0].getMethodName());
+ }
+ out.printf("CPU SAMPLES END\n");
+ out.flush();
+ }
+
+ private static final Comparator<HprofData.Sample> SAMPLE_COMPARATOR
+ = new Comparator<HprofData.Sample>() {
+ public int compare(HprofData.Sample s1, HprofData.Sample s2) {
+ return s2.count - s1.count;
+ }
+ };
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java
new file mode 100644
index 0000000..9720446
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprof.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Hprof binary format related constants shared between the
+ * BinaryHprofReader and BinaryHprofWriter.
+ */
+public final class BinaryHprof {
+ /**
+ * Currently code only supports 4 byte id size.
+ */
+ public static final int ID_SIZE = 4;
+
+ /**
+ * Prefix of valid magic values from the start of a binary hprof file.
+ */
+ static String MAGIC = "JAVA PROFILE ";
+
+ /**
+ * Returns the file's magic value as a String if found, otherwise null.
+ */
+ public static final String readMagic(DataInputStream in) {
+ try {
+ byte[] bytes = new byte[512];
+ for (int i = 0; i < bytes.length; i++) {
+ byte b = in.readByte();
+ if (b == '\0') {
+ String string = new String(bytes, 0, i, "UTF-8");
+ if (string.startsWith(MAGIC)) {
+ return string;
+ }
+ return null;
+ }
+ bytes[i] = b;
+ }
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public static enum Tag {
+
+ STRING_IN_UTF8(0x01, -ID_SIZE),
+ LOAD_CLASS(0x02, 4 + ID_SIZE + 4 + ID_SIZE),
+ UNLOAD_CLASS(0x03, 4),
+ STACK_FRAME(0x04, ID_SIZE + ID_SIZE + ID_SIZE + ID_SIZE + 4 + 4),
+ STACK_TRACE(0x05, -(4 + 4 + 4)),
+ ALLOC_SITES(0x06, -(2 + 4 + 4 + 4 + 8 + 8 + 4)),
+ HEAP_SUMMARY(0x07, 4 + 4 + 8 + 8),
+ START_THREAD(0x0a, 4 + ID_SIZE + 4 + ID_SIZE + ID_SIZE + ID_SIZE),
+ END_THREAD(0x0b, 4),
+ HEAP_DUMP(0x0c, -0),
+ HEAP_DUMP_SEGMENT(0x1c, -0),
+ HEAP_DUMP_END(0x2c, 0),
+ CPU_SAMPLES(0x0d, -(4 + 4)),
+ CONTROL_SETTINGS(0x0e, 4 + 2);
+
+ public final byte tag;
+
+ /**
+ * Minimum size in bytes.
+ */
+ public final int minimumSize;
+
+ /**
+ * Maximum size in bytes. 0 mean no specific limit.
+ */
+ public final int maximumSize;
+
+ private Tag(int tag, int size) {
+ this.tag = (byte) tag;
+ if (size > 0) {
+ // fixed size, max and min the same
+ this.minimumSize = size;
+ this.maximumSize = size;
+ } else {
+ // only minimum bound
+ this.minimumSize = -size;
+ this.maximumSize = 0;
+ }
+ }
+
+ private static final Map<Byte, Tag> BYTE_TO_TAG
+ = new HashMap<Byte, Tag>();
+
+ static {
+ for (Tag v : Tag.values()) {
+ BYTE_TO_TAG.put(v.tag, v);
+ }
+ }
+
+ public static Tag get(byte tag) {
+ return BYTE_TO_TAG.get(tag);
+ }
+
+ /**
+ * Returns null if the actual size meets expectations, or a
+ * String error message if not.
+ */
+ public String checkSize(int actual) {
+ if (actual < minimumSize) {
+ return "expected a minimial record size of " + minimumSize + " for " + this
+ + " but received " + actual;
+ }
+ if (maximumSize == 0) {
+ return null;
+ }
+ if (actual > maximumSize) {
+ return "expected a maximum record size of " + maximumSize + " for " + this
+ + " but received " + actual;
+ }
+ return null;
+ }
+ }
+
+ public static enum ControlSettings {
+ ALLOC_TRACES(0x01),
+ CPU_SAMPLING(0x02);
+
+ public final int bitmask;
+
+ private ControlSettings(int bitmask) {
+ this.bitmask = bitmask;
+ }
+ }
+
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java
new file mode 100644
index 0000000..75a17f3
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofReader.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <pre> {@code
+ * BinaryHprofReader reader = new BinaryHprofReader(new BufferedInputStream(inputStream));
+ * reader.setStrict(false); // for RI compatability
+ * reader.read();
+ * inputStream.close();
+ * reader.getVersion();
+ * reader.getHprofData();
+ * }</pre>
+ */
+public final class BinaryHprofReader {
+
+ private static final boolean TRACE = false;
+
+ private final DataInputStream in;
+
+ /**
+ * By default we try to strictly validate rules followed by
+ * our HprofWriter. For example, every end thread is preceded
+ * by a matching start thread.
+ */
+ private boolean strict = true;
+
+ /**
+ * version string from header after read has been performed,
+ * otherwise null. nullness used to detect if callers try to
+ * access data before read is called.
+ */
+ private String version;
+
+ private final Map<HprofData.StackTrace, int[]> stackTraces
+ = new HashMap<HprofData.StackTrace, int[]>();
+
+ private final HprofData hprofData = new HprofData(stackTraces);
+
+ private final Map<Integer, String> idToString = new HashMap<Integer, String>();
+ private final Map<Integer, String> idToClassName = new HashMap<Integer, String>();
+ private final Map<Integer, StackTraceElement> idToStackFrame
+ = new HashMap<Integer, StackTraceElement>();
+ private final Map<Integer, HprofData.StackTrace> idToStackTrace
+ = new HashMap<Integer, HprofData.StackTrace>();
+
+ /**
+ * Creates a BinaryHprofReader around the specified {@code
+ * inputStream}
+ */
+ public BinaryHprofReader(InputStream inputStream) throws IOException {
+ this.in = new DataInputStream(inputStream);
+ }
+
+ public boolean getStrict () {
+ return strict;
+ }
+
+ public void setStrict (boolean strict) {
+ if (version != null) {
+ throw new IllegalStateException("cannot set strict after read()");
+ }
+ this.strict = strict;
+ }
+
+ /**
+ * throws IllegalStateException if read() has not been called.
+ */
+ private void checkRead() {
+ if (version == null) {
+ throw new IllegalStateException("data access before read()");
+ }
+ }
+
+ public String getVersion() {
+ checkRead();
+ return version;
+ }
+
+ public HprofData getHprofData() {
+ checkRead();
+ return hprofData;
+ }
+
+ /**
+ * Read the hprof header and records from the input
+ */
+ public void read() throws IOException {
+ parseHeader();
+ parseRecords();
+ }
+
+ private void parseHeader() throws IOException {
+ if (TRACE) {
+ System.out.println("hprofTag=HEADER");
+ }
+ parseVersion();
+ parseIdSize();
+ parseTime();
+ }
+
+ private void parseVersion() throws IOException {
+ String version = BinaryHprof.readMagic(in);
+ if (version == null) {
+ throw new MalformedHprofException("Could not find HPROF version");
+ }
+ if (TRACE) {
+ System.out.println("\tversion=" + version);
+ }
+ this.version = version;
+ }
+
+ private void parseIdSize() throws IOException {
+ int idSize = in.readInt();
+ if (TRACE) {
+ System.out.println("\tidSize=" + idSize);
+ }
+ if (idSize != BinaryHprof.ID_SIZE) {
+ throw new MalformedHprofException("Unsupported identifier size: " + idSize);
+ }
+ }
+
+ private void parseTime() throws IOException {
+ long time = in.readLong();
+ if (TRACE) {
+ System.out.println("\ttime=" + Long.toHexString(time) + " " + new Date(time));
+ }
+ hprofData.setStartMillis(time);
+ }
+
+ private void parseRecords() throws IOException {
+ while (parseRecord()) {
+ ;
+ }
+ }
+
+ /**
+ * Read and process the next record. Returns true if a
+ * record was handled, false on EOF.
+ */
+ private boolean parseRecord() throws IOException {
+ int tagOrEOF = in.read();
+ if (tagOrEOF == -1) {
+ return false;
+ }
+ byte tag = (byte) tagOrEOF;
+ int timeDeltaInMicroseconds = in.readInt();
+ int recordLength = in.readInt();
+ BinaryHprof.Tag hprofTag = BinaryHprof.Tag.get(tag);
+ if (TRACE) {
+ System.out.println("hprofTag=" + hprofTag);
+ }
+ if (hprofTag == null) {
+ skipRecord(hprofTag, recordLength);
+ return true;
+ }
+ String error = hprofTag.checkSize(recordLength);
+ if (error != null) {
+ throw new MalformedHprofException(error);
+ }
+ switch (hprofTag) {
+ case CONTROL_SETTINGS:
+ parseControlSettings();
+ return true;
+
+ case STRING_IN_UTF8:
+ parseStringInUtf8(recordLength);
+ return true;
+
+ case START_THREAD:
+ parseStartThread();
+ return true;
+ case END_THREAD:
+ parseEndThread();
+ return true;
+
+ case LOAD_CLASS:
+ parseLoadClass();
+ return true;
+ case STACK_FRAME:
+ parseStackFrame();
+ return true;
+ case STACK_TRACE:
+ parseStackTrace(recordLength);
+ return true;
+
+ case CPU_SAMPLES:
+ parseCpuSamples(recordLength);
+ return true;
+
+ case UNLOAD_CLASS:
+ case ALLOC_SITES:
+ case HEAP_SUMMARY:
+ case HEAP_DUMP:
+ case HEAP_DUMP_SEGMENT:
+ case HEAP_DUMP_END:
+ default:
+ skipRecord(hprofTag, recordLength);
+ return true;
+ }
+ }
+
+ private void skipRecord(BinaryHprof.Tag hprofTag, long recordLength) throws IOException {
+ if (TRACE) {
+ System.out.println("\tskipping recordLength=" + recordLength);
+ }
+ long skipped = in.skip(recordLength);
+ if (skipped != recordLength) {
+ throw new EOFException("Expected to skip " + recordLength
+ + " bytes but only skipped " + skipped + " bytes");
+ }
+ }
+
+ private void parseControlSettings() throws IOException {
+ int flags = in.readInt();
+ short depth = in.readShort();
+ if (TRACE) {
+ System.out.println("\tflags=" + Integer.toHexString(flags));
+ System.out.println("\tdepth=" + depth);
+ }
+ hprofData.setFlags(flags);
+ hprofData.setDepth(depth);
+ }
+
+ private void parseStringInUtf8(int recordLength) throws IOException {
+ int stringId = in.readInt();
+ byte[] bytes = new byte[recordLength - BinaryHprof.ID_SIZE];
+ readFully(in, bytes);
+ String string = new String(bytes, "UTF-8");
+ if (TRACE) {
+ System.out.println("\tstring=" + string);
+ }
+ String old = idToString.put(stringId, string);
+ if (old != null) {
+ throw new MalformedHprofException("Duplicate string id: " + stringId);
+ }
+ }
+
+ private static void readFully(InputStream in, byte[] dst) throws IOException {
+ int offset = 0;
+ int byteCount = dst.length;
+ while (byteCount > 0) {
+ int bytesRead = in.read(dst, offset, byteCount);
+ if (bytesRead < 0) {
+ throw new EOFException();
+ }
+ offset += bytesRead;
+ byteCount -= bytesRead;
+ }
+ }
+
+ private void parseLoadClass() throws IOException {
+ int classId = in.readInt();
+ int classObjectId = readId();
+ // serial number apparently not a stack trace id. (int vs ID)
+ // we don't use this field.
+ int stackTraceSerialNumber = in.readInt();
+ String className = readString();
+ if (TRACE) {
+ System.out.println("\tclassId=" + classId);
+ System.out.println("\tclassObjectId=" + classObjectId);
+ System.out.println("\tstackTraceSerialNumber=" + stackTraceSerialNumber);
+ System.out.println("\tclassName=" + className);
+ }
+ String old = idToClassName.put(classId, className);
+ if (old != null) {
+ throw new MalformedHprofException("Duplicate class id: " + classId);
+ }
+ }
+
+ private int readId() throws IOException {
+ return in.readInt();
+ }
+
+ private String readString() throws IOException {
+ int id = readId();
+ if (id == 0) {
+ return null;
+ }
+ String string = idToString.get(id);
+ if (string == null) {
+ throw new MalformedHprofException("Unknown string id " + id);
+ }
+ return string;
+ }
+
+ private String readClass() throws IOException {
+ int id = readId();
+ String string = idToClassName.get(id);
+ if (string == null) {
+ throw new MalformedHprofException("Unknown class id " + id);
+ }
+ return string;
+ }
+
+ private void parseStartThread() throws IOException {
+ int threadId = in.readInt();
+ int objectId = readId();
+ // stack trace where thread was created.
+ // serial number apparently not a stack trace id. (int vs ID)
+ // we don't use this field.
+ int stackTraceSerialNumber = in.readInt();
+ String threadName = readString();
+ String groupName = readString();
+ String parentGroupName = readString();
+ if (TRACE) {
+ System.out.println("\tthreadId=" + threadId);
+ System.out.println("\tobjectId=" + objectId);
+ System.out.println("\tstackTraceSerialNumber=" + stackTraceSerialNumber);
+ System.out.println("\tthreadName=" + threadName);
+ System.out.println("\tgroupName=" + groupName);
+ System.out.println("\tparentGroupName=" + parentGroupName);
+ }
+ HprofData.ThreadEvent event
+ = HprofData.ThreadEvent.start(objectId, threadId,
+ threadName, groupName, parentGroupName);
+ hprofData.addThreadEvent(event);
+ }
+
+ private void parseEndThread() throws IOException {
+ int threadId = in.readInt();
+ if (TRACE) {
+ System.out.println("\tthreadId=" + threadId);
+ }
+ HprofData.ThreadEvent event = HprofData.ThreadEvent.end(threadId);
+ hprofData.addThreadEvent(event);
+ }
+
+ private void parseStackFrame() throws IOException {
+ int stackFrameId = readId();
+ String methodName = readString();
+ String methodSignature = readString();
+ String file = readString();
+ String className = readClass();
+ int line = in.readInt();
+ if (TRACE) {
+ System.out.println("\tstackFrameId=" + stackFrameId);
+ System.out.println("\tclassName=" + className);
+ System.out.println("\tmethodName=" + methodName);
+ System.out.println("\tmethodSignature=" + methodSignature);
+ System.out.println("\tfile=" + file);
+ System.out.println("\tline=" + line);
+ }
+ StackTraceElement stackFrame = new StackTraceElement(className, methodName, file, line);
+ StackTraceElement old = idToStackFrame.put(stackFrameId, stackFrame);
+ if (old != null) {
+ throw new MalformedHprofException("Duplicate stack frame id: " + stackFrameId);
+ }
+ }
+
+ private void parseStackTrace(int recordLength) throws IOException {
+ int stackTraceId = in.readInt();
+ int threadId = in.readInt();
+ int frames = in.readInt();
+ if (TRACE) {
+ System.out.println("\tstackTraceId=" + stackTraceId);
+ System.out.println("\tthreadId=" + threadId);
+ System.out.println("\tframes=" + frames);
+ }
+ int expectedLength = 4 + 4 + 4 + (frames * BinaryHprof.ID_SIZE);
+ if (recordLength != expectedLength) {
+ throw new MalformedHprofException("Expected stack trace record of size "
+ + expectedLength
+ + " based on number of frames but header "
+ + "specified a length of " + recordLength);
+ }
+ StackTraceElement[] stackFrames = new StackTraceElement[frames];
+ for (int i = 0; i < frames; i++) {
+ int stackFrameId = readId();
+ StackTraceElement stackFrame = idToStackFrame.get(stackFrameId);
+ if (TRACE) {
+ System.out.println("\tstackFrameId=" + stackFrameId);
+ System.out.println("\tstackFrame=" + stackFrame);
+ }
+ if (stackFrame == null) {
+ throw new MalformedHprofException("Unknown stack frame id " + stackFrameId);
+ }
+ stackFrames[i] = stackFrame;
+ }
+
+ HprofData.StackTrace stackTrace
+ = new HprofData.StackTrace(stackTraceId, threadId, stackFrames);
+ if (strict) {
+ hprofData.addStackTrace(stackTrace, new int[1]);
+ } else {
+ // The RI can have duplicate stacks, presumably they
+ // have a minor race if two samples with the same
+ // stack are taken around the same time. if we have a
+ // duplicate, just skip adding it to hprofData, but
+ // register it locally in idToStackFrame. if it seen
+ // in CPU_SAMPLES, we will find a StackTrace is equal
+ // to the first, so they will share a countCell.
+ int[] countCell = stackTraces.get(stackTrace);
+ if (countCell == null) {
+ hprofData.addStackTrace(stackTrace, new int[1]);
+ }
+ }
+
+ HprofData.StackTrace old = idToStackTrace.put(stackTraceId, stackTrace);
+ if (old != null) {
+ throw new MalformedHprofException("Duplicate stack trace id: " + stackTraceId);
+ }
+
+ }
+
+ private void parseCpuSamples(int recordLength) throws IOException {
+ int totalSamples = in.readInt();
+ int samplesCount = in.readInt();
+ if (TRACE) {
+ System.out.println("\ttotalSamples=" + totalSamples);
+ System.out.println("\tsamplesCount=" + samplesCount);
+ }
+ int expectedLength = 4 + 4 + (samplesCount * (4 + 4));
+ if (recordLength != expectedLength) {
+ throw new MalformedHprofException("Expected CPU samples record of size "
+ + expectedLength
+ + " based on number of samples but header "
+ + "specified a length of " + recordLength);
+ }
+ int total = 0;
+ for (int i = 0; i < samplesCount; i++) {
+ int count = in.readInt();
+ int stackTraceId = in.readInt();
+ if (TRACE) {
+ System.out.println("\tcount=" + count);
+ System.out.println("\tstackTraceId=" + stackTraceId);
+ }
+ HprofData.StackTrace stackTrace = idToStackTrace.get(stackTraceId);
+ if (stackTrace == null) {
+ throw new MalformedHprofException("Unknown stack trace id " + stackTraceId);
+ }
+ if (count == 0) {
+ throw new MalformedHprofException("Zero sample count for stack trace "
+ + stackTrace);
+ }
+ int[] countCell = stackTraces.get(stackTrace);
+ if (strict) {
+ if (countCell[0] != 0) {
+ throw new MalformedHprofException("Setting sample count of stack trace "
+ + stackTrace + " to " + count
+ + " found it was already initialized to "
+ + countCell[0]);
+ }
+ } else {
+ // Coalesce counts from duplicate stack traces.
+ // For more on this, see comments in parseStackTrace.
+ count += countCell[0];
+ }
+ countCell[0] = count;
+ total += count;
+ }
+ if (strict && totalSamples != total) {
+ throw new MalformedHprofException("Expected a total of " + totalSamples
+ + " samples but saw " + total);
+ }
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
new file mode 100644
index 0000000..5c29838
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/BinaryHprofWriter.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * BinaryHprofWriter produces hprof compatible binary output for use
+ * with third party tools. Such files can be converted to text with
+ * with {@link HprofBinaryToAscii} or read back in with {@link BinaryHprofReader}.
+ */
+public final class BinaryHprofWriter {
+
+ private int nextStringId = 1; // id 0 => null
+ private int nextClassId = 1;
+ private int nextStackFrameId = 1;
+ private final Map<String, Integer> stringToId = new HashMap<String, Integer>();
+ private final Map<String, Integer> classNameToId = new HashMap<String, Integer>();
+ private final Map<StackTraceElement, Integer> stackFrameToId
+ = new HashMap<StackTraceElement, Integer>();
+
+ private final HprofData data;
+ private final DataOutputStream out;
+
+ /**
+ * Writes the provided data to the specified stream.
+ */
+ public static void write(HprofData data, OutputStream outputStream) throws IOException {
+ new BinaryHprofWriter(data, outputStream).write();
+ }
+
+ private BinaryHprofWriter(HprofData data, OutputStream outputStream) {
+ this.data = data;
+ this.out = new DataOutputStream(outputStream);
+ }
+
+ private void write() throws IOException {
+ try {
+ writeHeader(data.getStartMillis());
+
+ writeControlSettings(data.getFlags(), data.getDepth());
+
+ for (HprofData.ThreadEvent event : data.getThreadHistory()) {
+ writeThreadEvent(event);
+ }
+
+ Set<HprofData.Sample> samples = data.getSamples();
+ int total = 0;
+ for (HprofData.Sample sample : samples) {
+ total += sample.count;
+ writeStackTrace(sample.stackTrace);
+ }
+ writeCpuSamples(total, samples);
+
+ } finally {
+ out.flush();
+ }
+ }
+
+ private void writeHeader(long dumpTimeInMilliseconds) throws IOException {
+ out.writeBytes(BinaryHprof.MAGIC + "1.0.2");
+ out.writeByte(0); // null terminated string
+ out.writeInt(BinaryHprof.ID_SIZE);
+ out.writeLong(dumpTimeInMilliseconds);
+ }
+
+ private void writeControlSettings(int flags, int depth) throws IOException {
+ if (depth > Short.MAX_VALUE) {
+ throw new IllegalArgumentException("depth too large for binary hprof: "
+ + depth + " > " + Short.MAX_VALUE);
+ }
+ writeRecordHeader(BinaryHprof.Tag.CONTROL_SETTINGS,
+ 0,
+ BinaryHprof.Tag.CONTROL_SETTINGS.maximumSize);
+ out.writeInt(flags);
+ out.writeShort((short) depth);
+ }
+
+ private void writeThreadEvent(HprofData.ThreadEvent e) throws IOException {
+ switch (e.type) {
+ case START:
+ writeStartThread(e);
+ return;
+ case END:
+ writeStopThread(e);
+ return;
+ }
+ throw new IllegalStateException(e.type.toString());
+ }
+
+ private void writeStartThread(HprofData.ThreadEvent e) throws IOException {
+ int threadNameId = writeString(e.threadName);
+ int groupNameId = writeString(e.groupName);
+ int parentGroupNameId = writeString(e.parentGroupName);
+ writeRecordHeader(BinaryHprof.Tag.START_THREAD,
+ 0,
+ BinaryHprof.Tag.START_THREAD.maximumSize);
+ out.writeInt(e.threadId);
+ writeId(e.objectId);
+ out.writeInt(0); // stack trace where thread was started unavailable
+ writeId(threadNameId);
+ writeId(groupNameId);
+ writeId(parentGroupNameId);
+ }
+
+ private void writeStopThread(HprofData.ThreadEvent e) throws IOException {
+ writeRecordHeader(BinaryHprof.Tag.END_THREAD,
+ 0,
+ BinaryHprof.Tag.END_THREAD.maximumSize);
+ out.writeInt(e.threadId);
+ }
+
+ private void writeRecordHeader(BinaryHprof.Tag hprofTag,
+ int timeDeltaInMicroseconds,
+ int recordLength) throws IOException {
+ String error = hprofTag.checkSize(recordLength);
+ if (error != null) {
+ throw new AssertionError(error);
+ }
+ out.writeByte(hprofTag.tag);
+ out.writeInt(timeDeltaInMicroseconds);
+ out.writeInt(recordLength);
+ }
+
+ private void writeId(int id) throws IOException {
+ out.writeInt(id);
+ }
+
+ /**
+ * Ensures that a string has been writen to the out and
+ * returns its ID. The ID of a null string is zero, and
+ * doesn't actually result in any output. In a string has
+ * already been written previously, the earlier ID will be
+ * returned and no output will be written.
+ */
+ private int writeString(String string) throws IOException {
+ if (string == null) {
+ return 0;
+ }
+ Integer identifier = stringToId.get(string);
+ if (identifier != null) {
+ return identifier;
+ }
+
+ int id = nextStringId++;
+ stringToId.put(string, id);
+
+ byte[] bytes = string.getBytes("UTF-8");
+ writeRecordHeader(BinaryHprof.Tag.STRING_IN_UTF8,
+ 0,
+ BinaryHprof.ID_SIZE + bytes.length);
+ out.writeInt(id);
+ out.write(bytes, 0, bytes.length);
+
+ return id;
+ }
+
+ private void writeCpuSamples(int totalSamples, Set<HprofData.Sample> samples)
+ throws IOException {
+ int samplesCount = samples.size();
+ if (samplesCount == 0) {
+ return;
+ }
+ writeRecordHeader(BinaryHprof.Tag.CPU_SAMPLES, 0, 4 + 4 + (samplesCount * (4 + 4)));
+ out.writeInt(totalSamples);
+ out.writeInt(samplesCount);
+ for (HprofData.Sample sample : samples) {
+ out.writeInt(sample.count);
+ out.writeInt(sample.stackTrace.stackTraceId);
+ }
+ }
+
+ private void writeStackTrace(HprofData.StackTrace stackTrace) throws IOException {
+ int frames = stackTrace.stackFrames.length;
+ int[] stackFrameIds = new int[frames];
+ for (int i = 0; i < frames; i++) {
+ stackFrameIds[i] = writeStackFrame(stackTrace.stackFrames[i]);
+ }
+ writeRecordHeader(BinaryHprof.Tag.STACK_TRACE,
+ 0,
+ 4 + 4 + 4 + (frames * BinaryHprof.ID_SIZE));
+ out.writeInt(stackTrace.stackTraceId);
+ out.writeInt(stackTrace.threadId);
+ out.writeInt(frames);
+ for (int stackFrameId : stackFrameIds) {
+ writeId(stackFrameId);
+ }
+ }
+
+ private int writeLoadClass(String className) throws IOException {
+ Integer identifier = classNameToId.get(className);
+ if (identifier != null) {
+ return identifier;
+ }
+ int id = nextClassId++;
+ classNameToId.put(className, id);
+
+ int classNameId = writeString(className);
+ writeRecordHeader(BinaryHprof.Tag.LOAD_CLASS,
+ 0,
+ BinaryHprof.Tag.LOAD_CLASS.maximumSize);
+ out.writeInt(id);
+ writeId(0); // class object ID
+ out.writeInt(0); // stack trace where class was loaded is unavailable
+ writeId(classNameId);
+
+ return id;
+ }
+
+ private int writeStackFrame(StackTraceElement stackFrame) throws IOException {
+ Integer identifier = stackFrameToId.get(stackFrame);
+ if (identifier != null) {
+ return identifier;
+ }
+
+ int id = nextStackFrameId++;
+ stackFrameToId.put(stackFrame, id);
+
+ int classId = writeLoadClass(stackFrame.getClassName());
+ int methodNameId = writeString(stackFrame.getMethodName());
+ int sourceId = writeString(stackFrame.getFileName());
+ writeRecordHeader(BinaryHprof.Tag.STACK_FRAME,
+ 0,
+ BinaryHprof.Tag.STACK_FRAME.maximumSize);
+ writeId(id);
+ writeId(methodNameId);
+ writeId(0); // method signature is unavailable from StackTraceElement
+ writeId(sourceId);
+ out.writeInt(classId);
+ out.writeInt(stackFrame.getLineNumber());
+
+ return id;
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java b/dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java
new file mode 100644
index 0000000..c20cc32
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/DalvikThreadSampler.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import dalvik.system.VMStack;
+import java.util.Arrays;
+
+class DalvikThreadSampler implements ThreadSampler {
+
+ private int depth;
+
+ /**
+ * Reusable storage for sampling sized for the specified depth.
+ */
+ private StackTraceElement[][] mutableStackTraceElements;
+
+ @Override public void setDepth(int depth) {
+ this.depth = depth;
+ this.mutableStackTraceElements = new StackTraceElement[depth+1][];
+ for (int i = 1; i < mutableStackTraceElements.length; i++) {
+ this.mutableStackTraceElements[i] = new StackTraceElement[i];
+ }
+ }
+
+ @Override public StackTraceElement[] getStackTrace(Thread thread) {
+ int count = VMStack.fillStackTraceElements(thread, mutableStackTraceElements[depth]);
+ if (count == 0) {
+ return null;
+ }
+ if (count < depth) {
+ System.arraycopy(mutableStackTraceElements[depth], 0,
+ mutableStackTraceElements[count], 0,
+ count);
+ }
+ return mutableStackTraceElements[count];
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java b/dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java
new file mode 100644
index 0000000..46c443d
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/HprofBinaryToAscii.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import java.io.BufferedInputStream;
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Run on device with:
+ * adb shell dalvikvm 'dalvik.system.profiler.HprofBinaryToAscii'
+ *
+ * Run on host with:
+ * java -classpath out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar
+ */
+public final class HprofBinaryToAscii {
+
+ /**
+ * Main entry point for HprofBinaryToAscii command line tool
+ */
+ public static void main(String[] args) {
+ System.exit(convert(args) ? 0 : 1);
+ }
+
+ /**
+ * Reads single file from arguments and attempts to read it as
+ * either a binary hprof file or a version with a text header.
+ */
+ private static boolean convert(String[] args) {
+
+ if (args.length != 1) {
+ usage("binary hprof file argument expected");
+ return false;
+ }
+ File file = new File(args[0]);
+ if (!file.exists()) {
+ usage("file " + file + " does not exist");
+ return false;
+ }
+
+ if (startsWithMagic(file)) {
+ HprofData hprofData;
+ try {
+ hprofData = readHprof(file);
+ } catch (IOException e) {
+ System.out.println("Problem reading binary hprof data from "
+ + file + ": " + e.getMessage());
+ return false;
+ }
+ return write(hprofData);
+ }
+
+ HprofData hprofData;
+ try {
+ hprofData = readSnapshot(file);
+ } catch (IOException e) {
+ System.out.println("Problem reading snapshot containing binary hprof data from "
+ + file + ": " + e.getMessage());
+ return false;
+ }
+ return write(hprofData);
+ }
+
+ /**
+ * Probe the start of file to see if it starts with a plausible
+ * binary hprof magic value. If so, it is returned. On any other
+ * case including unexpected errors, false is returned.
+ */
+ private static boolean startsWithMagic(File file) {
+ DataInputStream inputStream = null;
+ try {
+ inputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
+ return BinaryHprof.readMagic(inputStream) != null;
+ } catch (IOException e) {
+ return false;
+ } finally {
+ closeQuietly(inputStream);
+ }
+ }
+
+ /**
+ * Read and return an HprofData from a vanilla binary hprof file.
+ */
+ private static HprofData readHprof(File file) throws IOException {
+ InputStream inputStream = null;
+ try {
+ inputStream = new BufferedInputStream(new FileInputStream(file));
+ return read(inputStream);
+ } finally {
+ closeQuietly(inputStream);
+ }
+ }
+
+ /**
+ * Read a file looking for text header terminated by two newlines,
+ * then proceed to read binary hprof data.
+ */
+ private static HprofData readSnapshot(File file) throws IOException {
+ InputStream inputStream = null;
+ try {
+ inputStream = new BufferedInputStream(new FileInputStream(file));
+ int ch;
+ while ((ch = inputStream.read()) != -1) {
+ if (ch == '\n' && inputStream.read() == '\n') {
+ return read(inputStream);
+ }
+ }
+ throw new EOFException("Could not find expected header");
+ } finally {
+ closeQuietly(inputStream);
+ }
+ }
+
+ /**
+ * Read binary hprof data from the provided input stream and
+ * return the HprofData object.
+ */
+ private static HprofData read(InputStream inputStream) throws IOException {
+ BinaryHprofReader reader = new BinaryHprofReader(inputStream);
+ reader.setStrict(false);
+ reader.read();
+ return reader.getHprofData();
+ }
+
+ /**
+ * From IoUtils.closeQuietly but replicated for open source
+ * version.
+ */
+ private static void closeQuietly(Closeable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Write text verion of hprof data to standard output. Returns
+ * false on error.
+ */
+ private static boolean write(HprofData hprofData) {
+ try {
+ AsciiHprofWriter.write(hprofData, System.out);
+ } catch (IOException e) {
+ System.out.println("Problem writing ASCII hprof data: " + e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Prints usage error but does not exit.
+ */
+ private static void usage(String error) {
+ System.out.print("ERROR: ");
+ System.out.println(error);
+ System.out.println();
+ System.out.println("usage: HprofBinaryToAscii <binary-hprof-file>");
+ System.out.println();
+ System.out.println("Reads a binary hprof file and print it in ASCII format");
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/HprofData.java b/dalvik/src/main/java/dalvik/system/profiler/HprofData.java
new file mode 100644
index 0000000..7b44bb9
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/HprofData.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents sampling profiler data. Can be converted to ASCII or
+ * binary hprof-style output using {@link AsciiHprofWriter} or
+ * {@link BinaryHprofWriter}.
+ * <p>
+ * The data includes:
+ * <ul>
+ * <li>the start time of the last sampling period
+ * <li>the history of thread start and end events
+ * <li>stack traces with frequency counts
+ * <ul>
+ */
+public final class HprofData {
+
+ public static enum ThreadEventType { START, END };
+
+ /**
+ * ThreadEvent represents thread creation and death events for
+ * reporting. It provides a record of the thread and thread group
+ * names for tying samples back to their source thread.
+ */
+ public static final class ThreadEvent {
+
+ public final ThreadEventType type;
+ public final int objectId;
+ public final int threadId;
+ public final String threadName;
+ public final String groupName;
+ public final String parentGroupName;
+
+ public static ThreadEvent start(int objectId, int threadId, String threadName,
+ String groupName, String parentGroupName) {
+ return new ThreadEvent(ThreadEventType.START, objectId, threadId,
+ threadName, groupName, parentGroupName);
+ }
+
+ public static ThreadEvent end(int threadId) {
+ return new ThreadEvent(ThreadEventType.END, threadId);
+ }
+
+ private ThreadEvent(ThreadEventType type, int objectId, int threadId,
+ String threadName, String groupName, String parentGroupName) {
+ if (threadName == null) {
+ throw new NullPointerException("threadName == null");
+ }
+ this.type = ThreadEventType.START;
+ this.objectId = objectId;
+ this.threadId = threadId;
+ this.threadName = threadName;
+ this.groupName = groupName;
+ this.parentGroupName = parentGroupName;
+ }
+
+ private ThreadEvent(ThreadEventType type, int threadId) {
+ this.type = ThreadEventType.END;
+ this.objectId = -1;
+ this.threadId = threadId;
+ this.threadName = null;
+ this.groupName = null;
+ this.parentGroupName = null;
+ }
+
+ @Override public int hashCode() {
+ int result = 17;
+ result = 31 * result + objectId;
+ result = 31 * result + threadId;
+ result = 31 * result + hashCode(threadName);
+ result = 31 * result + hashCode(groupName);
+ result = 31 * result + hashCode(parentGroupName);
+ return result;
+ }
+
+ private static int hashCode(Object o) {
+ return (o == null) ? 0 : o.hashCode();
+ }
+
+ @Override public boolean equals(Object o) {
+ if (!(o instanceof ThreadEvent)) {
+ return false;
+ }
+ ThreadEvent event = (ThreadEvent) o;
+ return (this.type == event.type
+ && this.objectId == event.objectId
+ && this.threadId == event.threadId
+ && equal(this.threadName, event.threadName)
+ && equal(this.groupName, event.groupName)
+ && equal(this.parentGroupName, event.parentGroupName));
+ }
+
+ private static boolean equal(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
+ }
+
+ @Override public String toString() {
+ switch (type) {
+ case START:
+ return String.format(
+ "THREAD START (obj=%d, id = %d, name=\"%s\", group=\"%s\")",
+ objectId, threadId, threadName, groupName);
+ case END:
+ return String.format("THREAD END (id = %d)", threadId);
+ }
+ throw new IllegalStateException(type.toString());
+ }
+ }
+
+ /**
+ * A unique stack trace for a specific thread.
+ */
+ public static final class StackTrace {
+
+ public final int stackTraceId;
+ int threadId;
+ StackTraceElement[] stackFrames;
+
+ StackTrace() {
+ this.stackTraceId = -1;
+ }
+
+ public StackTrace(int stackTraceId, int threadId, StackTraceElement[] stackFrames) {
+ if (stackFrames == null) {
+ throw new NullPointerException("stackFrames == null");
+ }
+ this.stackTraceId = stackTraceId;
+ this.threadId = threadId;
+ this.stackFrames = stackFrames;
+ }
+
+ public int getThreadId() {
+ return threadId;
+ }
+
+ public StackTraceElement[] getStackFrames() {
+ return stackFrames;
+ }
+
+ @Override public int hashCode() {
+ int result = 17;
+ result = 31 * result + threadId;
+ result = 31 * result + Arrays.hashCode(stackFrames);
+ return result;
+ }
+
+ @Override public boolean equals(Object o) {
+ if (!(o instanceof StackTrace)) {
+ return false;
+ }
+ StackTrace s = (StackTrace) o;
+ return threadId == s.threadId && Arrays.equals(stackFrames, s.stackFrames);
+ }
+
+ @Override public String toString() {
+ StringBuilder frames = new StringBuilder();
+ if (stackFrames.length > 0) {
+ frames.append('\n');
+ for (StackTraceElement stackFrame : stackFrames) {
+ frames.append("\t at ");
+ frames.append(stackFrame);
+ frames.append('\n');
+ }
+ } else {
+ frames.append("<empty>");
+ }
+ return "StackTrace[stackTraceId=" + stackTraceId
+ + ", threadId=" + threadId
+ + ", frames=" + frames + "]";
+
+ }
+ }
+
+ /**
+ * A read only container combining a stack trace with its frequency.
+ */
+ public static final class Sample {
+
+ public final StackTrace stackTrace;
+ public final int count;
+
+ private Sample(StackTrace stackTrace, int count) {
+ if (stackTrace == null) {
+ throw new NullPointerException("stackTrace == null");
+ }
+ if (count < 0) {
+ throw new IllegalArgumentException("count < 0:" + count);
+ }
+ this.stackTrace = stackTrace;
+ this.count = count;
+ }
+
+ @Override public int hashCode() {
+ int result = 17;
+ result = 31 * result + stackTrace.hashCode();
+ result = 31 * result + count;
+ return result;
+ }
+
+ @Override public boolean equals(Object o) {
+ if (!(o instanceof Sample)) {
+ return false;
+ }
+ Sample s = (Sample) o;
+ return count == s.count && stackTrace.equals(s.stackTrace);
+ }
+
+ @Override public String toString() {
+ return "Sample[count=" + count + " " + stackTrace + "]";
+ }
+
+ }
+
+ /**
+ * Start of last sampling period.
+ */
+ private long startMillis;
+
+ /**
+ * CONTROL_SETTINGS flags
+ */
+ private int flags;
+
+ /**
+ * stack sampling depth
+ */
+ private int depth;
+
+ /**
+ * List of thread creation and death events.
+ */
+ private final List<ThreadEvent> threadHistory = new ArrayList<ThreadEvent>();
+
+ /**
+ * Map of thread id to a start ThreadEvent
+ */
+ private final Map<Integer, ThreadEvent> threadIdToThreadEvent
+ = new HashMap<Integer, ThreadEvent>();
+
+ /**
+ * Map of stack traces to a mutable sample count. The map is
+ * provided by the creator of the HprofData so only have
+ * mutable access to the int[] cells that contain the sample
+ * count. Only an unmodifiable iterator view is available to
+ * users of the HprofData.
+ */
+ private final Map<HprofData.StackTrace, int[]> stackTraces;
+
+ public HprofData(Map<StackTrace, int[]> stackTraces) {
+ if (stackTraces == null) {
+ throw new NullPointerException("stackTraces == null");
+ }
+ this.stackTraces = stackTraces;
+ }
+
+ /**
+ * The start time in milliseconds of the last profiling period.
+ */
+ public long getStartMillis() {
+ return startMillis;
+ }
+
+ /**
+ * Set the time for the start of the current sampling period.
+ */
+ public void setStartMillis(long startMillis) {
+ this.startMillis = startMillis;
+ }
+
+ /**
+ * Get the {@link BinaryHprof.ControlSettings} flags
+ */
+ public int getFlags() {
+ return flags;
+ }
+
+ /**
+ * Set the {@link BinaryHprof.ControlSettings} flags
+ */
+ public void setFlags(int flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * Get the stack sampling depth
+ */
+ public int getDepth() {
+ return depth;
+ }
+
+ /**
+ * Set the stack sampling depth
+ */
+ public void setDepth(int depth) {
+ this.depth = depth;
+ }
+
+ /**
+ * Return an unmodifiable history of start and end thread events.
+ */
+ public List<ThreadEvent> getThreadHistory() {
+ return Collections.unmodifiableList(threadHistory);
+ }
+
+ /**
+ * Return a new set containing the current sample data.
+ */
+ public Set<Sample> getSamples() {
+ Set<Sample> samples = new HashSet<Sample>(stackTraces.size());
+ for (Entry<StackTrace, int[]> e : stackTraces.entrySet()) {
+ StackTrace stackTrace = e.getKey();
+ int countCell[] = e.getValue();
+ int count = countCell[0];
+ Sample sample = new Sample(stackTrace, count);
+ samples.add(sample);
+ }
+ return samples;
+ }
+
+ /**
+ * Record an event in the thread history.
+ */
+ public void addThreadEvent(ThreadEvent event) {
+ if (event == null) {
+ throw new NullPointerException("event == null");
+ }
+ ThreadEvent old = threadIdToThreadEvent.put(event.threadId, event);
+ switch (event.type) {
+ case START:
+ if (old != null) {
+ throw new IllegalArgumentException("ThreadEvent already registered for id "
+ + event.threadId);
+ }
+ break;
+ case END:
+ // Do not assert that the END_THREAD matches a
+ // START_THREAD unless in strict mode. While thhis
+ // hold true in the binary hprof BinaryHprofWriter
+ // produces, it is not true of hprof files created
+ // by the RI. However, if there is an event
+ // already registed for a thread id, it should be
+ // the matching start, not a duplicate end.
+ if (old != null && old.type == ThreadEventType.END) {
+ throw new IllegalArgumentException("Duplicate ThreadEvent.end for id "
+ + event.threadId);
+ }
+ break;
+ }
+ threadHistory.add(event);
+ }
+
+ /**
+ * Record an stack trace and an associated int[] cell of
+ * sample cound for the stack trace. The caller is allowed
+ * retain a pointer to the cell to update the count. The
+ * SamplingProfiler intentionally does not present a mutable
+ * view of the count.
+ */
+ public void addStackTrace(StackTrace stackTrace, int[] countCell) {
+ if (!threadIdToThreadEvent.containsKey(stackTrace.threadId)) {
+ throw new IllegalArgumentException("Unknown thread id " + stackTrace.threadId);
+ }
+ int[] old = stackTraces.put(stackTrace, countCell);
+ if (old != null) {
+ throw new IllegalArgumentException("StackTrace already registered for id "
+ + stackTrace.stackTraceId + ":\n" + stackTrace);
+ }
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/MalformedHprofException.java b/dalvik/src/main/java/dalvik/system/profiler/MalformedHprofException.java
new file mode 100644
index 0000000..b4097b2
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/MalformedHprofException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import java.io.IOException;
+
+public final class MalformedHprofException extends IOException {
+
+ private static final long serialVersionUID = 8558990237047894213L;
+
+ MalformedHprofException(String message) {
+ super(message);
+ }
+ MalformedHprofException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ MalformedHprofException(Throwable cause) {
+ super(cause);
+ }
+}
+
diff --git a/dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java b/dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java
new file mode 100644
index 0000000..96e0b27
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/PortableThreadSampler.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import java.util.Arrays;
+
+/**
+ * ThreadSampler implementation that only uses Thread.getStackTrace()
+ * and therefore is portable.
+ */
+class PortableThreadSampler implements ThreadSampler {
+
+ private int depth;
+
+ @Override public void setDepth(int depth) {
+ this.depth = depth;
+ }
+
+ @Override public StackTraceElement[] getStackTrace(Thread thread) {
+ StackTraceElement[] stackFrames = thread.getStackTrace();
+ if (stackFrames.length == 0) {
+ return null;
+ }
+ if (stackFrames.length > depth) {
+ stackFrames = Arrays.copyOfRange(stackFrames, 0, depth);
+ }
+ return stackFrames;
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java b/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java
new file mode 100644
index 0000000..744977c
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * A sampling profiler. It currently is implemented without any
+ * virtual machine support, relying solely on {@code
+ * Thread.getStackTrace} to collect samples. As such, the overhead is
+ * higher than a native approach and it does not provide insight into
+ * where time is spent within native code, but it can still provide
+ * useful insight into where a program is spending time.
+ *
+ * <h3>Usage Example</h3>
+ *
+ * The following example shows how to use the {@code
+ * SamplingProfiler}. It samples the current thread's stack to a depth
+ * of 12 stack frame elements over two different measurement periods
+ * with samples taken every 100 milliseconds. In then prints the
+ * results in hprof format to the standard output.
+ *
+ * <pre> {@code
+ * ThreadSet threadSet = SamplingProfiler.newArrayThreadSet(Thread.currentThread());
+ * SamplingProfiler profiler = new SamplingProfiler(12, threadSet);
+ * profiler.start(100);
+ * // period of measurement
+ * profiler.stop();
+ * // period of non-measurement
+ * profiler.start(100);
+ * // another period of measurement
+ * profiler.stop();
+ * profiler.shutdown();
+ * AsciiHprofWriter.write(profiler.getHprofData(), System.out);
+ * }</pre>
+ */
+public final class SamplingProfiler {
+
+ /**
+ * Map of stack traces to a mutable sample count.
+ */
+ private final Map<HprofData.StackTrace, int[]> stackTraces
+ = new HashMap<HprofData.StackTrace, int[]>();
+
+ /**
+ * Data collected by the sampling profiler
+ */
+ private final HprofData hprofData = new HprofData(stackTraces);
+
+ /**
+ * Timer that is used for the lifetime of the profiler
+ */
+ private final Timer timer = new Timer("SamplingProfiler", true);
+
+ /**
+ * A sampler is created every time profiling starts and cleared
+ * every time profiling stops because once a {@code TimerTask} is
+ * canceled it cannot be reused.
+ */
+ private Sampler sampler;
+
+ /**
+ * The maximum number of {@code StackTraceElements} to retain in
+ * each stack.
+ */
+ private final int depth;
+
+ /**
+ * The {@code ThreadSet} that identifies which threads to sample.
+ */
+ private final ThreadSet threadSet;
+
+ /*
+ * Real hprof output examples don't start the thread and trace
+ * identifiers at one but seem to start at these arbitrary
+ * constants. It certainly seems useful to have relatively unique
+ * identifiers when manual searching hprof output.
+ */
+ private int nextThreadId = 200001;
+ private int nextStackTraceId = 300001;
+ private int nextObjectId = 1;
+
+ /**
+ * The threads currently known to the profiler for detecting
+ * thread start and end events.
+ */
+ private Thread[] currentThreads = new Thread[0];
+
+ /**
+ * Map of currently active threads to their identifiers. When
+ * threads disappear they are removed and only referenced by their
+ * identifiers to prevent retaining garbage threads.
+ */
+ private final Map<Thread, Integer> threadIds = new HashMap<Thread, Integer>();
+
+ /**
+ * Mutable {@code StackTrace} that is used for probing the {@link
+ * #stackTraces stackTraces} map without allocating a {@code
+ * StackTrace}. If {@link #addStackTrace addStackTrace} needs to
+ * be thread safe, have a single mutable instance would need to be
+ * reconsidered.
+ */
+ private final HprofData.StackTrace mutableStackTrace = new HprofData.StackTrace();
+
+ /**
+ * The {@code ThreadSampler} is used to produce a {@code
+ * StackTraceElement} array for a given thread. The array is
+ * expected to be {@link #depth depth} or less in length.
+ */
+ private final ThreadSampler threadSampler;
+
+ /**
+ * Create a sampling profiler that collects stacks with the
+ * specified depth from the threads specified by the specified
+ * thread collector.
+ *
+ * @param depth The maximum stack depth to retain for each sample
+ * similar to the hprof option of the same name. Any stack deeper
+ * than this will be truncated to this depth. A good starting
+ * value is 4 although it is not uncommon to need to raise this to
+ * get enough context to understand program behavior. While
+ * programs with extensive recursion may require a high value for
+ * depth, simply passing in a value for Integer.MAX_VALUE is not
+ * advised because of the significant memory need to retain such
+ * stacks and runtime overhead to compare stacks.
+ *
+ * @param threadSet The thread set specifies which threads to
+ * sample. In a general purpose program, all threads typically
+ * should be sample with a ThreadSet such as provided by {@link
+ * #newThreadGroupThreadSet newThreadGroupThreadSet}. For a
+ * benchmark a fixed set such as provided by {@link
+ * #newArrayThreadSet newArrayThreadSet} can reduce the overhead
+ * of profiling.
+ */
+ public SamplingProfiler(int depth, ThreadSet threadSet) {
+ this.depth = depth;
+ this.threadSet = threadSet;
+ this.threadSampler = findDefaultThreadSampler();
+ threadSampler.setDepth(depth);
+ hprofData.setFlags(BinaryHprof.ControlSettings.CPU_SAMPLING.bitmask);
+ hprofData.setDepth(depth);
+ }
+
+ private static ThreadSampler findDefaultThreadSampler() {
+ if ("Dalvik Core Library".equals(System.getProperty("java.specification.name"))) {
+ String className = "dalvik.system.profiler.DalvikThreadSampler";
+ try {
+ return (ThreadSampler) Class.forName(className).newInstance();
+ } catch (Exception e) {
+ System.out.println("Problem creating " + className + ": " + e);
+ }
+ }
+ return new PortableThreadSampler();
+ }
+
+ /**
+ * A ThreadSet specifies the set of threads to sample.
+ */
+ public static interface ThreadSet {
+ /**
+ * Returns an array containing the threads to be sampled. The
+ * array may be longer than the number of threads to be
+ * sampled, in which case the extra elements must be null.
+ */
+ public Thread[] threads();
+ }
+
+ /**
+ * Returns a ThreadSet for a fixed set of threads that will not
+ * vary at runtime. This has less overhead than a dynamically
+ * calculated set, such as {@link #newThreadGroupThreadSet}, which has
+ * to enumerate the threads each time profiler wants to collect
+ * samples.
+ */
+ public static ThreadSet newArrayThreadSet(Thread... threads) {
+ return new ArrayThreadSet(threads);
+ }
+
+ /**
+ * An ArrayThreadSet samples a fixed set of threads that does not
+ * vary over the life of the profiler.
+ */
+ private static class ArrayThreadSet implements ThreadSet {
+ private final Thread[] threads;
+ public ArrayThreadSet(Thread... threads) {
+ if (threads == null) {
+ throw new NullPointerException("threads == null");
+ }
+ this.threads = threads;
+ }
+ public Thread[] threads() {
+ return threads;
+ }
+ }
+
+ /**
+ * Returns a ThreadSet that is dynamically computed based on the
+ * threads found in the specified ThreadGroup and that
+ * ThreadGroup's children.
+ */
+ public static ThreadSet newThreadGroupThreadSet(ThreadGroup threadGroup) {
+ return new ThreadGroupThreadSet(threadGroup);
+ }
+
+ /**
+ * An ThreadGroupThreadSet sample the threads from the specified
+ * ThreadGroup and the ThreadGroup's children
+ */
+ private static class ThreadGroupThreadSet implements ThreadSet {
+ private final ThreadGroup threadGroup;
+ private Thread[] threads;
+ private int lastThread;
+
+ public ThreadGroupThreadSet(ThreadGroup threadGroup) {
+ if (threadGroup == null) {
+ throw new NullPointerException("threadGroup == null");
+ }
+ this.threadGroup = threadGroup;
+ resize();
+ }
+
+ private void resize() {
+ int count = threadGroup.activeCount();
+ // we can only tell if we had enough room for all active
+ // threads if we actually are larger than the the number of
+ // active threads. making it larger also leaves us room to
+ // tolerate additional threads without resizing.
+ threads = new Thread[count*2];
+ lastThread = 0;
+ }
+
+ public Thread[] threads() {
+ int threadCount;
+ while (true) {
+ threadCount = threadGroup.enumerate(threads);
+ if (threadCount == threads.length) {
+ resize();
+ } else {
+ break;
+ }
+ }
+ if (threadCount < lastThread) {
+ // avoid retaining pointers to threads that have ended
+ Arrays.fill(threads, threadCount, lastThread, null);
+ }
+ lastThread = threadCount;
+ return threads;
+ }
+ }
+
+ /**
+ * Starts profiler sampling at the specified rate.
+ *
+ * @param interval The number of milliseconds between samples
+ */
+ public void start(int interval) {
+ if (interval < 1) {
+ throw new IllegalArgumentException("interval < 1");
+ }
+ if (sampler != null) {
+ throw new IllegalStateException("profiling already started");
+ }
+ sampler = new Sampler();
+ hprofData.setStartMillis(System.currentTimeMillis());
+ timer.scheduleAtFixedRate(sampler, 0, interval);
+ }
+
+ /**
+ * Stops profiler sampling. It can be restarted with {@link
+ * #start(int)} to continue sampling.
+ */
+ public void stop() {
+ if (sampler == null) {
+ return;
+ }
+ synchronized(sampler) {
+ sampler.stop = true;
+ while (!sampler.stopped) {
+ try {
+ sampler.wait();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+ sampler = null;
+ }
+
+ /**
+ * Shuts down profiling after which it can not be restarted. It is
+ * important to shut down profiling when done to free resources
+ * used by the profiler. Shutting down the profiler also stops the
+ * profiling if that has not already been done.
+ */
+ public void shutdown() {
+ stop();
+ timer.cancel();
+ }
+
+ /**
+ * Returns the hprof data accumulated by the profiler since it was
+ * created. The profiler needs to be stopped, but not necessarily
+ * shut down, in order to access the data. If the profiler is
+ * restarted, there is no thread safe way to access the data.
+ */
+ public HprofData getHprofData() {
+ if (sampler != null) {
+ throw new IllegalStateException("cannot access hprof data while sampling");
+ }
+ return hprofData;
+ }
+
+ /**
+ * The Sampler does the real work of the profiler.
+ *
+ * At every sample time, it asks the thread set for the set
+ * of threads to sample. It maintains a history of thread creation
+ * and death events based on changes observed to the threads
+ * returned by the {@code ThreadSet}.
+ *
+ * For each thread to be sampled, a stack is collected and used to
+ * update the set of collected samples. Stacks are truncated to a
+ * maximum depth. There is no way to tell if a stack has been truncated.
+ */
+ private class Sampler extends TimerTask {
+
+ private boolean stop;
+ private boolean stopped;
+
+ private Thread timerThread;
+
+ public void run() {
+ synchronized(this) {
+ if (stop) {
+ cancel();
+ stopped = true;
+ notifyAll();
+ return;
+ }
+ }
+
+ if (timerThread == null) {
+ timerThread = Thread.currentThread();
+ }
+
+ // process thread creation and death first so that we
+ // assign thread ids to any new threads before allocating
+ // new stacks for them
+ Thread[] newThreads = threadSet.threads();
+ if (!Arrays.equals(currentThreads, newThreads)) {
+ updateThreadHistory(currentThreads, newThreads);
+ currentThreads = newThreads.clone();
+ }
+
+ for (Thread thread : currentThreads) {
+ if (thread == null) {
+ break;
+ }
+ if (thread == timerThread) {
+ continue;
+ }
+
+ StackTraceElement[] stackFrames = threadSampler.getStackTrace(thread);
+ if (stackFrames == null) {
+ continue;
+ }
+ recordStackTrace(thread, stackFrames);
+ }
+ }
+
+ /**
+ * Record a new stack trace. The thread should have been
+ * previously registered with addStartThread.
+ */
+ private void recordStackTrace(Thread thread, StackTraceElement[] stackFrames) {
+ Integer threadId = threadIds.get(thread);
+ if (threadId == null) {
+ throw new IllegalArgumentException("Unknown thread " + thread);
+ }
+ mutableStackTrace.threadId = threadId;
+ mutableStackTrace.stackFrames = stackFrames;
+
+ int[] countCell = stackTraces.get(mutableStackTrace);
+ if (countCell == null) {
+ countCell = new int[1];
+ // cloned because the ThreadSampler may reuse the array
+ StackTraceElement[] stackFramesCopy = stackFrames.clone();
+ HprofData.StackTrace stackTrace
+ = new HprofData.StackTrace(nextStackTraceId++, threadId, stackFramesCopy);
+ hprofData.addStackTrace(stackTrace, countCell);
+ }
+ countCell[0]++;
+ }
+
+ private void updateThreadHistory(Thread[] oldThreads, Thread[] newThreads) {
+ // thread start/stop shouldn't happen too often and
+ // these aren't too big, so hopefully this approach
+ // won't be too slow...
+ Set<Thread> n = new HashSet<Thread>(Arrays.asList(newThreads));
+ Set<Thread> o = new HashSet<Thread>(Arrays.asList(oldThreads));
+
+ // added = new-old
+ Set<Thread> added = new HashSet<Thread>(n);
+ added.removeAll(o);
+
+ // removed = old-new
+ Set<Thread> removed = new HashSet<Thread>(o);
+ removed.removeAll(n);
+
+ for (Thread thread : added) {
+ if (thread == null) {
+ continue;
+ }
+ if (thread == timerThread) {
+ continue;
+ }
+ addStartThread(thread);
+ }
+ for (Thread thread : removed) {
+ if (thread == null) {
+ continue;
+ }
+ if (thread == timerThread) {
+ continue;
+ }
+ addEndThread(thread);
+ }
+ }
+
+ /**
+ * Record that a newly noticed thread.
+ */
+ private void addStartThread(Thread thread) {
+ if (thread == null) {
+ throw new NullPointerException("thread == null");
+ }
+ int threadId = nextThreadId++;
+ Integer old = threadIds.put(thread, threadId);
+ if (old != null) {
+ throw new IllegalArgumentException("Thread already registered as " + old);
+ }
+
+ String threadName = thread.getName();
+ // group will become null when thread is terminated
+ ThreadGroup group = thread.getThreadGroup();
+ String groupName = group == null ? null : group.getName();
+ ThreadGroup parentGroup = group == null ? null : group.getParent();
+ String parentGroupName = parentGroup == null ? null : parentGroup.getName();
+
+ HprofData.ThreadEvent event
+ = HprofData.ThreadEvent.start(nextObjectId++, threadId,
+ threadName, groupName, parentGroupName);
+ hprofData.addThreadEvent(event);
+ }
+
+ /**
+ * Record that a thread has disappeared.
+ */
+ private void addEndThread(Thread thread) {
+ if (thread == null) {
+ throw new NullPointerException("thread == null");
+ }
+ Integer threadId = threadIds.remove(thread);
+ if (threadId == null) {
+ throw new IllegalArgumentException("Unknown thread " + thread);
+ }
+ HprofData.ThreadEvent event = HprofData.ThreadEvent.end(threadId);
+ hprofData.addThreadEvent(event);
+ }
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java b/dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java
new file mode 100644
index 0000000..104b16d
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/profiler/ThreadSampler.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import java.io.IOException;
+
+/**
+ * The {@code ThreadSampler} interfaces allows a profiler to choose
+ * between portable and VM specific implementations of thread
+ * sampling.
+ */
+public interface ThreadSampler {
+
+ /**
+ * Used to specify the maximum stack depth to collect.
+ */
+ public void setDepth(int depth);
+
+ /**
+ * Return a stack trace for the current thread limited by the
+ * maximum depth specified by {@link #setDepth setDepth}. May
+ * return null if no sample is availble for the thread, which may
+ * happen in cases such as thread termination. The resulting array
+ * should be copied before the next call to {@code getStackTrace}
+ * if the caller wants to use the results, since the {@code
+ * ThreadSampler} may reuse the array. Note that the elements
+ * themselves are immutable and do not need to be copied.
+ */
+ public StackTraceElement[] getStackTrace(Thread thread);
+}
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java b/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java
new file mode 100644
index 0000000..7b46d2e
--- /dev/null
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 org.apache.harmony.dalvik;
+
+/**
+ * Methods used to test calling into native code. The methods in this
+ * class are all effectively no-ops and may be used to test the mechanisms
+ * and performance of calling native methods.
+ */
+public final class NativeTestTarget {
+ /**
+ * This class is uninstantiable.
+ */
+ private NativeTestTarget() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * This is an empty native static method with no args, hooked up using
+ * JNI.
+ */
+ public static native void emptyJniStaticMethod0();
+
+ /**
+ * This is an empty native static method with six args, hooked up using
+ * JNI.
+ */
+ public static native void emptyJniStaticMethod6(int a, int b, int c,
+ int d, int e, int f);
+
+ /**
+ * This is an empty native static method with six args, hooked up
+ * using JNI. These have more complex args to show the cost of
+ * parsing the signature. All six values should be null
+ * references.
+ */
+ public static native void emptyJniStaticMethod6L(String a, String[] b,
+ int[][] c, Object d, Object[] e, Object[][][][] f);
+
+ /**
+ * This method is intended to be "inlined" by the virtual machine
+ * (e.g., given special treatment as an intrinsic).
+ */
+ public static void emptyInlineMethod() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * This method is intended to be defined in native code and hooked
+ * up using the virtual machine's special fast-path native linkage
+ * (as opposed to being hooked up using JNI).
+ */
+ public static native void emptyInternalStaticMethod();
+}
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/Chunk.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/Chunk.java
new file mode 100644
index 0000000..9b98893
--- /dev/null
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/Chunk.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 org.apache.harmony.dalvik.ddmc;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A chunk of DDM data. This is really just meant to hold a few pieces
+ * of data together.
+ *
+ * The "offset" and "length" fields are present so handlers can over-allocate
+ * or share byte buffers.
+ */
+public class Chunk {
+
+ /*
+ * Public members. Do not rename without updating the VM.
+ */
+ public int type; // chunk type
+ public byte[] data; // chunk data
+ public int offset, length; // position within "data"
+
+ /**
+ * Blank constructor. Fill in your own fields.
+ */
+ public Chunk() {}
+
+ /**
+ * Constructor with all fields.
+ */
+ public Chunk(int type, byte[] data, int offset, int length) {
+ this.type = type;
+ this.data = data;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ /**
+ * Construct from a ByteBuffer. The chunk is assumed to start at
+ * offset 0 and continue to the current position.
+ */
+ public Chunk(int type, ByteBuffer buf) {
+ this.type = type;
+
+ this.data = buf.array();
+ this.offset = buf.arrayOffset();
+ this.length = buf.position();
+ }
+}
+
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/ChunkHandler.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/ChunkHandler.java
new file mode 100644
index 0000000..5d0073b
--- /dev/null
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/ChunkHandler.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 org.apache.harmony.dalvik.ddmc;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Handle a chunk of data sent from a DDM server.
+ *
+ * To handle a chunk type, sub-class ChunkHandler and register your class
+ * with DdmServer.
+ */
+public abstract class ChunkHandler {
+
+ public static final ByteOrder CHUNK_ORDER = ByteOrder.BIG_ENDIAN;
+
+ public static final int CHUNK_FAIL = type("FAIL");
+
+
+ public ChunkHandler() {}
+
+ /**
+ * Called when the DDM server connects. The handler is allowed to
+ * send messages to the server.
+ */
+ public abstract void connected();
+
+ /**
+ * Called when the DDM server disconnects. Can be used to disable
+ * periodic transmissions or clean up saved state.
+ */
+ public abstract void disconnected();
+
+ /**
+ * Handle a single chunk of data. "request" includes the type and
+ * the chunk payload.
+ *
+ * Returns a response in a Chunk.
+ */
+ public abstract Chunk handleChunk(Chunk request);
+
+ /**
+ * Create a FAIL chunk. The "handleChunk" methods can use this to
+ * return an error message when they are not able to process a chunk.
+ */
+ public static Chunk createFailChunk(int errorCode, String msg) {
+ if (msg == null)
+ msg = "";
+
+ ByteBuffer out = ByteBuffer.allocate(8 + msg.length() * 2);
+ out.order(ChunkHandler.CHUNK_ORDER);
+ out.putInt(errorCode);
+ out.putInt(msg.length());
+ putString(out, msg);
+
+ return new Chunk(CHUNK_FAIL, out);
+ }
+
+ /**
+ * Utility function to wrap a ByteBuffer around a Chunk.
+ */
+ public static ByteBuffer wrapChunk(Chunk request) {
+ ByteBuffer in;
+
+ in = ByteBuffer.wrap(request.data, request.offset, request.length);
+ in.order(CHUNK_ORDER);
+ return in;
+ }
+
+
+ /**
+ * Utility function to copy a String out of a ByteBuffer.
+ *
+ * This is here because multiple chunk handlers can make use of it,
+ * and there's nowhere better to put it.
+ */
+ public static String getString(ByteBuffer buf, int len) {
+ char[] data = new char[len];
+ for (int i = 0; i < len; i++)
+ data[i] = buf.getChar();
+ return new String(data);
+ }
+
+ /**
+ * Utility function to copy a String into a ByteBuffer.
+ */
+ public static void putString(ByteBuffer buf, String str) {
+ int len = str.length();
+ for (int i = 0; i < len; i++)
+ buf.putChar(str.charAt(i));
+ }
+
+ /**
+ * Convert a 4-character string to a 32-bit type.
+ */
+ public static int type(String typeName) {
+ if (typeName.length() != 4) {
+ throw new IllegalArgumentException("Bad type name: " + typeName);
+ }
+ int result = 0;
+ for (int i = 0; i < 4; ++i) {
+ result = ((result << 8) | (typeName.charAt(i) & 0xff));
+ }
+ return result;
+ }
+
+ /**
+ * Convert an integer type to a 4-character string.
+ */
+ public static String name(int type)
+ {
+ char[] ascii = new char[4];
+
+ ascii[0] = (char) ((type >> 24) & 0xff);
+ ascii[1] = (char) ((type >> 16) & 0xff);
+ ascii[2] = (char) ((type >> 8) & 0xff);
+ ascii[3] = (char) (type & 0xff);
+
+ return new String(ascii);
+ }
+
+}
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
new file mode 100644
index 0000000..7717fd9
--- /dev/null
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 org.apache.harmony.dalvik.ddmc;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+
+
+/**
+ * This represents our connection to the DDM Server.
+ */
+public class DdmServer {
+
+ public static final int CLIENT_PROTOCOL_VERSION = 1;
+
+ private static HashMap<Integer,ChunkHandler> mHandlerMap =
+ new HashMap<Integer,ChunkHandler>();
+
+ private static final int CONNECTED = 1;
+ private static final int DISCONNECTED = 2;
+
+ private static volatile boolean mRegistrationComplete = false;
+ private static boolean mRegistrationTimedOut = false;
+
+
+ /**
+ * Don't instantiate; all members and methods are static.
+ */
+ private DdmServer() {}
+
+ /**
+ * Register an instance of the ChunkHandler class to handle a specific
+ * chunk type.
+ *
+ * Throws an exception if the type already has a handler registered.
+ */
+ public static void registerHandler(int type, ChunkHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException("handler == null");
+ }
+ synchronized (mHandlerMap) {
+ if (mHandlerMap.get(type) != null)
+ throw new RuntimeException("type " + Integer.toHexString(type)
+ + " already registered");
+
+ mHandlerMap.put(type, handler);
+ }
+ }
+
+ /**
+ * Unregister the existing handler for the specified type.
+ *
+ * Returns the old handler.
+ */
+ public static ChunkHandler unregisterHandler(int type) {
+ synchronized (mHandlerMap) {
+ return mHandlerMap.remove(type);
+ }
+ }
+
+ /**
+ * The application must call here after it finishes registering
+ * handlers.
+ */
+ public static void registrationComplete() {
+ // sync on mHandlerMap because it's convenient and makes a kind of
+ // sense
+ synchronized (mHandlerMap) {
+ mRegistrationComplete = true;
+ mHandlerMap.notifyAll();
+ }
+ }
+
+ /**
+ * Send a chunk of data to the DDM server. This takes the form of a
+ * JDWP "event", which does not elicit a response from the server.
+ *
+ * Use this for "unsolicited" chunks.
+ */
+ public static void sendChunk(Chunk chunk) {
+ nativeSendChunk(chunk.type, chunk.data, chunk.offset, chunk.length);
+ }
+
+ /* send a chunk to the DDM server */
+ native private static void nativeSendChunk(int type, byte[] data,
+ int offset, int length);
+
+ /*
+ * Called by the VM when the DDM server connects or disconnects.
+ */
+ private static void broadcast(int event)
+ {
+ synchronized (mHandlerMap) {
+ Collection values = mHandlerMap.values();
+ Iterator iter = values.iterator();
+
+ while (iter.hasNext()) {
+ ChunkHandler handler = (ChunkHandler) iter.next();
+ switch (event) {
+ case CONNECTED:
+ handler.connected();
+ break;
+ case DISCONNECTED:
+ handler.disconnected();
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+ }
+
+ /*
+ * This is called by the VM when a chunk arrives.
+ *
+ * For a DDM-aware application, we want to wait until the app has had
+ * a chance to register all of its chunk handlers. Otherwise, we'll
+ * end up dropping early-arriving packets on the floor.
+ *
+ * For a non-DDM-aware application, we'll end up waiting here forever
+ * if DDMS happens to connect. It's hard to know for sure that
+ * registration isn't going to happen, so we settle for a timeout.
+ */
+ private static Chunk dispatch(int type, byte[] data, int offset, int length)
+ {
+ ChunkHandler handler;
+
+ synchronized (mHandlerMap) {
+ /*
+ * If registration hasn't completed, and we haven't timed out
+ * waiting for it, wait a bit.
+ */
+ while (!mRegistrationComplete && !mRegistrationTimedOut) {
+ //System.out.println("dispatch() waiting for reg");
+ try {
+ mHandlerMap.wait(1000); // 1.0 sec
+ } catch (InterruptedException ie) {
+ continue;
+ }
+
+ if (!mRegistrationComplete) {
+ /* timed out, don't wait again */
+ mRegistrationTimedOut = true;
+ }
+ }
+
+ handler = mHandlerMap.get(type);
+ }
+ //System.out.println(" dispatch cont");
+
+ if (handler == null) {
+ return null;
+ }
+
+ Chunk chunk = new Chunk(type, data, offset, length);
+ return handler.handleChunk(chunk);
+ }
+}
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
new file mode 100644
index 0000000..01293b0
--- /dev/null
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 org.apache.harmony.dalvik.ddmc;
+
+/**
+ * Declarations for some VM-internal DDM stuff.
+ */
+public class DdmVmInternal {
+
+ /* do not instantiate */
+ private DdmVmInternal() {}
+
+ /**
+ * Enable thread notification.
+ *
+ * This is built into the VM, since that's where threads get managed.
+ */
+ native public static void threadNotify(boolean enable);
+
+ /**
+ * Enable heap info updates.
+ *
+ * This is built into the VM, since that's where the heap is managed.
+ *
+ * @param when when to send the next HPIF chunk
+ * @return true on success. false if 'when' is bad or if there was
+ * an internal error.
+ */
+ native public static boolean heapInfoNotify(int when);
+
+ /**
+ * Enable heap segment updates for the java (isNative == false) or
+ * native (isNative == true) heap.
+ *
+ * This is built into the VM, since that's where the heap is managed.
+ */
+ native public static boolean heapSegmentNotify(int when, int what,
+ boolean isNative);
+
+ /**
+ * Get status info for all threads. This is for the THST chunk.
+ *
+ * Returns a byte array with the THST data, or null if something
+ * went wrong.
+ */
+ native public static byte[] getThreadStats();
+
+ /**
+ * Get a stack trace for the specified thread ID. The ID can be found
+ * in the data from getThreadStats.
+ */
+ native public static StackTraceElement[] getStackTraceById(int threadId);
+
+ /**
+ * Enable or disable "recent allocation" tracking.
+ */
+ native public static void enableRecentAllocations(boolean enable);
+
+ /*
+ * Return a boolean indicating whether or not the "recent allocation"
+ * feature is currently enabled.
+ */
+ native public static boolean getRecentAllocationStatus();
+
+ /**
+ * Fill a buffer with data on recent heap allocations.
+ */
+ native public static byte[] getRecentAllocations();
+}
+
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/README.txt b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/README.txt
new file mode 100644
index 0000000..99def80
--- /dev/null
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/README.txt
@@ -0,0 +1 @@
+These classes are tied to the VM, e.g. the VM may invoke some of the methods.
diff --git a/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp b/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
new file mode 100644
index 0000000..50e2fc2
--- /dev/null
+++ b/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "JNIHelp.h"
+
+/*
+ * public static void emptyJniStaticMethod0()
+ *
+ * For benchmarks, a do-nothing JNI method with no arguments.
+ */
+static void emptyJniStaticMethod0(JNIEnv*, jclass)
+{
+ // This space intentionally left blank.
+}
+
+/*
+ * public static void emptyJniStaticMethod6(int a, int b, int c,
+ * int d, int e, int f)
+ *
+ * For benchmarks, a do-nothing JNI method with six arguments.
+ */
+static void emptyJniStaticMethod6(JNIEnv*, jclass,
+ int, int, int, int, int, int)
+{
+ // This space intentionally left blank.
+}
+
+/*
+ * public static void emptyJniStaticMethod6L(String a, String[] b,
+ * int[][] c, Object d, Object[] e, Object[][][][] f)
+ *
+ * For benchmarks, a do-nothing JNI method with six arguments.
+ */
+static void emptyJniStaticMethod6L(JNIEnv*, jclass,
+ jobject, jarray, jarray, jobject, jarray, jarray)
+{
+ // This space intentionally left blank.
+}
+
+static JNINativeMethod gMethods[] = {
+ { "emptyJniStaticMethod0", "()V", (void*)emptyJniStaticMethod0 },
+ { "emptyJniStaticMethod6", "(IIIIII)V", (void*)emptyJniStaticMethod6 },
+ { "emptyJniStaticMethod6L", "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V", (void*)emptyJniStaticMethod6L },
+};
+int register_org_apache_harmony_dalvik_NativeTestTarget(JNIEnv* env) {
+ int result = jniRegisterNativeMethods(env,
+ "org/apache/harmony/dalvik/NativeTestTarget",
+ gMethods, NELEM(gMethods));
+ if (result != 0) {
+ /* print warning, but allow to continue */
+ ALOGW("WARNING: NativeTestTarget not registered\n");
+ env->ExceptionClear();
+ }
+ return 0;
+}
diff --git a/dalvik/src/main/native/sub.mk b/dalvik/src/main/native/sub.mk
new file mode 100644
index 0000000..4adc8a1
--- /dev/null
+++ b/dalvik/src/main/native/sub.mk
@@ -0,0 +1,17 @@
+# -*- mode: makefile -*-
+# This file is included by the top-level libcore Android.mk.
+# It's not a normal makefile, so we don't include CLEAR_VARS
+# or BUILD_*_LIBRARY.
+
+LOCAL_SRC_FILES := \
+ org_apache_harmony_dalvik_NativeTestTarget.cpp
+
+#LOCAL_C_INCLUDES +=
+
+# Any shared/static libs that are listed here must also
+# be listed in libs/nativehelper/Android.mk.
+# TODO: fix this requirement
+
+#LOCAL_SHARED_LIBRARIES +=
+
+#LOCAL_STATIC_LIBRARIES +=
diff --git a/dalvik/src/test/java/dalvik/system/profiler/SamplingProfilerTest.java b/dalvik/src/test/java/dalvik/system/profiler/SamplingProfilerTest.java
new file mode 100644
index 0000000..d022cd0
--- /dev/null
+++ b/dalvik/src/test/java/dalvik/system/profiler/SamplingProfilerTest.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 dalvik.system.profiler;
+
+import dalvik.system.profiler.AsciiHprofWriter;
+import dalvik.system.profiler.BinaryHprofReader;
+import dalvik.system.profiler.BinaryHprofWriter;
+import dalvik.system.profiler.HprofData.Sample;
+import dalvik.system.profiler.HprofData.StackTrace;
+import dalvik.system.profiler.HprofData.ThreadEvent;
+import dalvik.system.profiler.HprofData;
+import dalvik.system.profiler.SamplingProfiler.ThreadSet;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.crypto.spec.DHParameterSpec;
+import junit.framework.TestCase;
+
+public class SamplingProfilerTest extends TestCase {
+
+ /**
+ * Run the SamplingProfiler to gather some data on an actual
+ * computation, then assert that it looks correct with test_HprofData.
+ */
+ public void test_SamplingProfiler() throws Exception {
+ ThreadSet threadSet = SamplingProfiler.newArrayThreadSet(Thread.currentThread());
+ SamplingProfiler profiler = new SamplingProfiler(12, threadSet);
+ profiler.start(100);
+ toBeMeasured();
+ profiler.stop();
+ profiler.shutdown();
+ test_HprofData(profiler.getHprofData(), true);
+ }
+
+ private static final String P_STR =
+ "9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e3"
+ + "1db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b";
+ private static final String G_STR =
+ "98ab7c5c431479d8645e33aa09758e0907c78747798d0968576f9877421a9089"
+ + "756f7876e76590b76765645c987976d764dd4564698a87585e64554984bb4445"
+ + "76e5764786f875b4456c";
+
+ private static final byte[] P = new BigInteger(P_STR,16).toByteArray();
+ private static final byte[] G = new BigInteger(G_STR,16).toByteArray();
+
+ private static void toBeMeasured () throws Exception {
+ long start = System.currentTimeMillis();
+ for (int i = 0; i < 10000; i++) {
+ BigInteger p = new BigInteger(P);
+ BigInteger g = new BigInteger(G);
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("DH");
+ gen.initialize(new DHParameterSpec(p, g), new SecureRandom());
+ }
+ long end = System.currentTimeMillis();
+ }
+
+ public void test_HprofData_null() throws Exception {
+ try {
+ new HprofData(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void test_HprofData_empty() throws Exception {
+ Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>();
+ HprofData hprofData = new HprofData(stackTraces);
+ test_HprofData(hprofData, true);
+ }
+
+ public void test_HprofData_timeMillis() throws Exception {
+ Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>();
+ HprofData hprofData = new HprofData(stackTraces);
+ long now = System.currentTimeMillis();
+ hprofData.setStartMillis(now);
+ assertEquals(now, hprofData.getStartMillis());
+ test_HprofData(hprofData, true);
+ }
+
+ public void test_HprofData_addThreadEvent_null() throws Exception {
+ Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>();
+ HprofData hprofData = new HprofData(stackTraces);
+ try {
+ hprofData.addThreadEvent(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ test_HprofData(hprofData, true);
+ }
+
+ public void test_HprofData_addThreadEvent() throws Exception {
+ Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>();
+ HprofData hprofData = new HprofData(stackTraces);
+
+ // should have nothing in the thread history to start
+ assertEquals(0, hprofData.getThreadHistory().size());
+
+ // add thread 1
+ final int threadId = 1;
+ final int objectId = 2;
+ ThreadEvent start1 = ThreadEvent.start(objectId, threadId,
+ "thread-name", "thread-group", "parent-group");
+ hprofData.addThreadEvent(start1);
+ assertEquals(Arrays.asList(start1), hprofData.getThreadHistory());
+ test_HprofData(hprofData, true);
+
+ // remove thread 2, which should not exist (but that's okay on the RI)
+ ThreadEvent end2 = ThreadEvent.end(threadId+1);
+ hprofData.addThreadEvent(end2);
+ assertEquals(Arrays.asList(start1, end2), hprofData.getThreadHistory());
+ test_HprofData(hprofData, false); // non-strict from here down because of this RI data
+
+ // remove thread 1, which should exist
+ ThreadEvent end1 = ThreadEvent.end(threadId);
+ hprofData.addThreadEvent(end1);
+ assertEquals(Arrays.asList(start1, end2, end1), hprofData.getThreadHistory());
+ test_HprofData(hprofData, false);
+
+ // remove thread 1 again, which should not exist (its not okay to have end followed by end)
+ try {
+ hprofData.addThreadEvent(ThreadEvent.end(threadId));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ assertEquals(Arrays.asList(start1, end2, end1), hprofData.getThreadHistory());
+ test_HprofData(hprofData, false);
+ }
+
+ public void test_HprofData_addStackTrace() throws Exception {
+ Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>();
+ HprofData hprofData = new HprofData(stackTraces);
+
+ // should have no samples to start
+ assertEquals(0, hprofData.getSamples().size());
+
+ // attempt to add a stack for a non-existent thread, should fail
+ final int stackTraceId = 1;
+ final int threadId = 2;
+ final int objectId = 3;
+ final int sampleCount = 4;
+ StackTraceElement[] stackFrames = new Throwable().getStackTrace();
+ final int[] countCell = new int[] { 4 };
+ StackTrace stackTrace = new StackTrace(stackTraceId, threadId, stackFrames);
+ try {
+ hprofData.addStackTrace(stackTrace, countCell);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // add the thread and add the event
+ ThreadEvent start = ThreadEvent.start(objectId, threadId,
+ "thread-name", "thread-group", "parent-group");
+ hprofData.addThreadEvent(start);
+ hprofData.addStackTrace(stackTrace, countCell);
+ Set<Sample> samples = hprofData.getSamples();
+ assertNotNull(samples);
+ assertNotSame(samples, hprofData.getSamples());
+ assertEquals(1, samples.size());
+ Sample sample = samples.iterator().next();
+ assertNotNull(sample);
+ assertEquals(stackTrace, sample.stackTrace);
+ assertEquals(sampleCount, sample.count);
+ test_HprofData(hprofData, true);
+
+ // confirm we can mutate the sample count, but that its not
+ // visible in the current sample, but it will be visible in a
+ // new one.
+ countCell[0] += 42;
+ assertEquals(sampleCount, sample.count);
+ Sample sample2 = hprofData.getSamples().iterator().next();
+ assertEquals(sampleCount + 42, sample2.count);
+ test_HprofData(hprofData, true);
+
+ // try to reuse the stackTraceId, should fail
+ try {
+ hprofData.addStackTrace(stackTrace, countCell);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ assertEquals(1, hprofData.getSamples().size());
+ test_HprofData(hprofData, true);
+
+ }
+
+ private void test_HprofData(HprofData hprofData, boolean strict) throws Exception {
+ assertHprofData(hprofData, strict);
+ test_HprofData_ascii(hprofData);
+ test_HprofData_binary(hprofData, strict);
+ }
+
+ /**
+ * Assert general properities of HprofData hold true.
+ */
+ private void assertHprofData(HprofData hprofData, boolean strict) throws Exception {
+ List<ThreadEvent> threadHistory = hprofData.getThreadHistory();
+ assertNotNull(threadHistory);
+ Set<Integer> threadsSeen = new HashSet<Integer>();
+ Set<Integer> threadsActive = new HashSet<Integer>();
+ for (ThreadEvent event : threadHistory) {
+ assertNotNull(event);
+ assertNotNull(event.type);
+ switch (event.type) {
+ case START:
+ assertNotNull(event.threadName);
+ assertTrue(threadsActive.add(event.threadId));
+ assertTrue(threadsSeen.add(event.threadId));
+ break;
+ case END:
+ assertEquals(-1, event.objectId);
+ assertNull(event.threadName);
+ assertNull(event.groupName);
+ assertNull(event.parentGroupName);
+ if (strict) {
+ assertTrue(threadsActive.remove(event.threadId));
+ }
+ break;
+ }
+ }
+
+ Set<Sample> samples = hprofData.getSamples();
+ assertNotNull(samples);
+ for (Sample sample : samples) {
+ assertNotNull(sample);
+ assertTrue(sample.count > 0);
+ assertNotNull(sample.stackTrace);
+ assertTrue(sample.stackTrace.stackTraceId != -1);
+ assertTrue(threadsSeen.contains(sample.stackTrace.getThreadId()));
+ assertNotNull(sample.stackTrace.getStackFrames());
+ }
+ }
+
+ /**
+ * Convert to HprofData to ASCII to see if it triggers any exceptions
+ */
+ private void test_HprofData_ascii(HprofData hprofData) throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ AsciiHprofWriter.write(hprofData, out);
+ assertFalse(out.toByteArray().length == 0);
+ }
+
+ /**
+ * Convert to HprofData to binary and then reparse as to
+ * HprofData. Make sure the accessible data is equivalent.
+ */
+ private void test_HprofData_binary(HprofData hprofData, boolean strict) throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ BinaryHprofWriter.write(hprofData, out);
+ out.close();
+
+ byte[] bytes = out.toByteArray();
+ assertFalse(bytes.length == 0);
+ if (false) {
+ File file = new File("/sdcard/java.hprof");
+ OutputStream debug = new FileOutputStream(file);
+ debug.write(bytes);
+ debug.close();
+ System.out.println("Wrote binary hprof data to " + file);
+ }
+
+ InputStream in = new ByteArrayInputStream(bytes);
+ BinaryHprofReader reader = new BinaryHprofReader(in);
+ assertTrue(reader.getStrict());
+ reader.read();
+ in.close();
+ assertEquals("JAVA PROFILE 1.0.2", reader.getVersion());
+ assertNotNull(reader.getHprofData());
+
+ HprofData parsed = reader.getHprofData();
+ assertHprofData(hprofData, strict);
+
+ assertEquals(Long.toHexString(hprofData.getStartMillis()),
+ Long.toHexString(parsed.getStartMillis()));
+ assertEquals(Long.toHexString(hprofData.getFlags()),
+ Long.toHexString(parsed.getFlags()));
+ assertEquals(Long.toHexString(hprofData.getDepth()),
+ Long.toHexString(parsed.getDepth()));
+ assertEquals(hprofData.getThreadHistory(),
+ parsed.getThreadHistory());
+ assertEquals(hprofData.getSamples(),
+ parsed.getSamples());
+ }
+}