Snapshot 776e91f2212c253732a2907b365bc1b510a91cba
Change-Id: Ic54c2364c02ef5817ad9dac0edd8c2d383c70aa7
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..18cf576
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,66 @@
+# -*- mode: makefile -*-
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+
+#
+# Include the definitions to build the Java code.
+#
+
+include $(LOCAL_PATH)/JavaLibrary.mk
+
+
+#
+# Include the definitions to build the native code.
+#
+
+include $(LOCAL_PATH)/NativeCode.mk
+
+#
+# Include the definitions for the Certificate Authority (CA) certificates
+#
+
+include $(LOCAL_PATH)/CaCerts.mk
+
+#
+# Disable test modules if LIBCORE_SKIP_TESTS environment variable is set.
+#
+
+ifneq ($(LIBCORE_SKIP_TESTS),)
+$(info ********************************************************************************)
+$(info * libcore tests are skipped because environment variable LIBCORE_SKIP_TESTS=$(LIBCORE_SKIP_TESTS))
+$(info ********************************************************************************)
+endif
+
+
+#
+# "m dalvik-host" for quick minimal host build
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+ .PHONY: dalvik-host
+ dalvik-host: \
+ dalvik \
+ $(HOST_OUT)/bin/dalvikvm \
+ $(HOST_OUT)/bin/dexopt \
+ $(HOST_OUT)/lib/libjavacore.so \
+ cacerts-host \
+ core-hostdex \
+ bouncycastle-hostdex \
+ apache-xml-hostdex \
+ apache-harmony-tests-hostdex \
+ $(call intermediates-dir-for,JAVA_LIBRARIES,core-tests,,COMMON)/classes.jar
+endif
diff --git a/CaCerts.mk b/CaCerts.mk
new file mode 100644
index 0000000..980d0fb
--- /dev/null
+++ b/CaCerts.mk
@@ -0,0 +1,59 @@
+# -*- mode: makefile -*-
+# 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.
+
+#
+# Definitions for installing Certificate Authority (CA) certificates
+#
+
+define all-files-under
+$(patsubst ./%,%, \
+ $(shell cd $(LOCAL_PATH) ; \
+ find $(1) -type f) \
+ )
+endef
+
+# $(1): module name
+# $(2): source file
+# $(3): destination directory
+define include-prebuilt-with-destination-directory
+include $$(CLEAR_VARS)
+LOCAL_MODULE := $(1)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/CaCerts.mk
+LOCAL_MODULE_STEM := $(notdir $(2))
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(3)
+LOCAL_SRC_FILES := $(2)
+include $$(BUILD_PREBUILT)
+endef
+
+cacerts := $(call all-files-under,luni/src/main/files/cacerts)
+
+cacerts_target_directory := $(TARGET_OUT)/etc/security/cacerts
+$(foreach cacert, $(cacerts), $(eval $(call include-prebuilt-with-destination-directory,target-cacert-$(notdir $(cacert)),$(cacert),$(cacerts_target_directory))))
+cacerts_target := $(addprefix $(cacerts_target_directory)/,$(foreach cacert,$(cacerts),$(notdir $(cacert))))
+.PHONY: cacerts_target
+cacerts: $(cacerts_target)
+
+# This is so that build/target/product/core.mk can use cacerts in PRODUCT_PACKAGES
+ALL_MODULES.cacerts.INSTALLED := $(cacerts_target)
+
+ifeq ($(WITH_HOST_DALVIK),true)
+cacerts_host_directory := $(HOST_OUT)/etc/security/cacerts
+$(foreach cacert, $(cacerts), $(eval $(call include-prebuilt-with-destination-directory,host-cacert-$(notdir $(cacert)),$(cacert),$(cacerts_host_directory))))
+endif
+cacerts_host := $(addprefix $(cacerts_host_directory)/,$(foreach cacert,$(cacerts),$(notdir $(cacert))))
+.PHONY: cacerts-host
+cacerts-host: $(cacerts_host)
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..1618f1b
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,50 @@
+# -*- mode: makefile -*-
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+#$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/Docs.mk b/Docs.mk
new file mode 100644
index 0000000..875a008
--- /dev/null
+++ b/Docs.mk
@@ -0,0 +1,29 @@
+# -*- mode: makefile -*-
+# List of libcore directories to include in documentation.
+# Shared between libcore and frameworks/base.
+
+define libcoredoc-all-java-files-under
+$(patsubst ./%,%, \
+ $(shell cd $(1) ; \
+ find $(2) -name "*.java" -and -not -name ".*") \
+ )
+endef
+
+# List of libcore javadoc source files
+#
+# Note dalvik/system is non-recursive to exclude dalvik.system.profiler
+#
+# $(1): directory for search (to support use from frameworks/base)
+define libcore_to_document
+ $(call libcoredoc-all-java-files-under,$(1), \
+ dalvik/src/main/java/dalvik/system/ -maxdepth 1) \
+ $(call libcoredoc-all-java-files-under,$(1), \
+ dalvik/src/main/java/dalvik/annotation \
+ dalvik/src/main/java/dalvik/bytecode \
+ json/src/main/java \
+ luni/src/main/java/java \
+ luni/src/main/java/javax \
+ luni/src/main/java/org/xml/sax \
+ luni/src/main/java/org/w3c \
+ xml/src/main/java/org/xmlpull/v1)
+endef
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
new file mode 100644
index 0000000..98de1f2
--- /dev/null
+++ b/JavaLibrary.mk
@@ -0,0 +1,207 @@
+# -*- mode: makefile -*-
+# 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.
+
+#
+# Definitions for building the Java library and associated tests.
+#
+
+#
+# Common definitions for host and target.
+#
+
+# libcore is divided into modules.
+#
+# The structure of each module is:
+#
+# src/
+# main/ # To be shipped on every device.
+# java/ # Java source for library code.
+# native/ # C++ source for library code.
+# resources/ # Support files.
+# test/ # Built only on demand, for testing.
+# java/ # Java source for tests.
+# native/ # C++ source for tests (rare).
+# resources/ # Support files.
+#
+# All subdirectories are optional (hence the "2> /dev/null"s below).
+
+define all-main-java-files-under
+$(foreach dir,$(1),$(patsubst ./%,%,$(shell cd $(LOCAL_PATH) && find $(dir)/src/main/java -name "*.java" 2> /dev/null)))
+endef
+
+define all-test-java-files-under
+$(foreach dir,$(1),$(patsubst ./%,%,$(shell cd $(LOCAL_PATH) && find $(dir)/src/test/java -name "*.java" 2> /dev/null)))
+endef
+
+define all-core-resource-dirs
+$(shell cd $(LOCAL_PATH) && ls -d */src/$(1)/{java,resources} 2> /dev/null)
+endef
+
+# The Java files and their associated resources.
+core_src_files := $(call all-main-java-files-under,dalvik dom json luni support xml)
+core_resource_dirs := $(call all-core-resource-dirs,main)
+test_resource_dirs := $(call all-core-resource-dirs,test)
+
+ifeq ($(EMMA_INSTRUMENT),true)
+ifneq ($(EMMA_INSTRUMENT_STATIC),true)
+ core_src_files += $(call all-java-files-under, ../external/emma/core ../external/emma/pregenerated)
+ core_resource_dirs += ../external/emma/core/res ../external/emma/pregenerated/res
+endif
+endif
+
+local_javac_flags=-encoding UTF-8
+#local_javac_flags+=-Xlint:all -Xlint:-serial,-deprecation,-unchecked
+local_javac_flags+=-Xmaxwarns 9999999
+
+#
+# Build for the target (device).
+#
+
+# Definitions to make the core library.
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(core_src_files)
+LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
+
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_DX_FLAGS := --core-library
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := core
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+LOCAL_REQUIRED_MODULES := tzdata
+
+include $(BUILD_JAVA_LIBRARY)
+
+core-intermediates := ${intermediates}
+
+
+# Make the core-tests library.
+ifeq ($(LIBCORE_SKIP_TESTS),)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-test-java-files-under,dalvik dom json luni support xml)
+LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_LIBRARIES := bouncycastle core core-junit
+LOCAL_STATIC_JAVA_LIBRARIES := sqlite-jdbc mockwebserver nist-pkix-tests
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_MODULE := core-tests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+include $(BUILD_STATIC_JAVA_LIBRARY)
+endif
+
+# This one's tricky. One of our tests needs to have a
+# resource with a "#" in its name, but Perforce doesn't
+# allow us to submit such a file. So we create it here
+# on-the-fly.
+TMP_RESOURCE_DIR := $(intermediates.COMMON)/tmp/
+TMP_RESOURCE_FILE := org/apache/harmony/luni/tests/java/lang/test\#.properties
+
+$(TMP_RESOURCE_DIR)$(TMP_RESOURCE_FILE):
+ @mkdir -p $(dir $@)
+ @echo "Hello, world!" > $@
+
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EXTRA_JAR_ARGS := $(extra_jar_args) -C "$(TMP_RESOURCE_DIR)" "$(TMP_RESOURCE_FILE)"
+$(LOCAL_INTERMEDIATE_TARGETS): $(TMP_RESOURCE_DIR)$(TMP_RESOURCE_FILE)
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+
+ # Definitions to make the core library.
+
+ include $(CLEAR_VARS)
+
+ LOCAL_SRC_FILES := $(core_src_files)
+ LOCAL_JAVA_RESOURCE_DIRS := $(core_resource_dirs)
+
+ LOCAL_NO_STANDARD_LIBRARIES := true
+ LOCAL_JAVACFLAGS := $(local_javac_flags)
+ LOCAL_DX_FLAGS := --core-library
+
+ LOCAL_BUILD_HOST_DEX := true
+
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := core-hostdex
+ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+ LOCAL_REQUIRED_MODULES := tzdata-host
+
+ include $(BUILD_HOST_JAVA_LIBRARY)
+
+ # Make the core-tests library.
+ ifeq ($(LIBCORE_SKIP_TESTS),)
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(call all-test-java-files-under,dalvik dom json luni support xml)
+ LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
+ LOCAL_NO_STANDARD_LIBRARIES := true
+ LOCAL_JAVA_LIBRARIES := bouncycastle-hostdex core-hostdex core-junit-hostdex
+ LOCAL_STATIC_JAVA_LIBRARIES := sqlite-jdbc-host mockwebserver-host nist-pkix-tests-host
+ LOCAL_JAVACFLAGS := $(local_javac_flags)
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := core-tests-hostdex
+ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+ LOCAL_BUILD_HOST_DEX := true
+ include $(BUILD_HOST_JAVA_LIBRARY)
+ endif
+endif
+
+#
+# Local droiddoc for faster libcore testing
+#
+#
+# Run with:
+# mm -j32 libcore-docs
+#
+# Main output:
+# ../out/target/common/docs/libcore/reference/packages.html
+#
+# All text for proofreading (or running tools over):
+# ../out/target/common/docs/libcore-proofread.txt
+#
+# TODO list of missing javadoc, etc:
+# ../out/target/common/docs/libcore-docs-todo.html
+#
+# Rerun:
+# rm -rf ../out/target/common/docs/libcore-timestamp && mm -j32 libcore-docs
+#
+include $(CLEAR_VARS)
+
+# for shared defintion of libcore_to_document
+include $(LOCAL_PATH)/Docs.mk
+
+LOCAL_SRC_FILES:=$(call libcore_to_document,$(LOCAL_PATH))
+# rerun doc generation without recompiling the java
+LOCAL_JAVA_LIBRARIES:=
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_MODULE_CLASS:=JAVA_LIBRARIES
+
+LOCAL_MODULE := libcore
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
+
+LOCAL_DROIDDOC_OPTIONS := \
+ -offlinemode \
+ -title "libcore" \
+ -proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
+ -todo ../$(LOCAL_MODULE)-docs-todo.html \
+ -hdf android.whichdoc offline
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
+
+include $(BUILD_DROIDDOC)
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..818f6c5
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,382 @@
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Android-specific code. ==
+ =========================================================================
+
+Android Code
+Copyright 2005-2008 The Android Open Source Project
+
+This product includes software developed as part of
+The Android Open Source Project (http://source.android.com).
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Apache Harmony distribution. ==
+ =========================================================================
+
+Apache Harmony
+Copyright 2006 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+Portions of Harmony were originally developed by
+Intel Corporation and are licensed to the Apache Software
+Foundation under the "Software Grant and Corporate Contribution
+License Agreement", informally known as the "Intel Harmony CLA".
+
+
+ =========================================================================
+ == NOTICE file for the ICU License. ==
+ =========================================================================
+
+Copyright (c) 1995-2009 International Business Machines Corporation and others
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
+SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+All trademarks and registered trademarks mentioned herein are the
+property of their respective owners.
+
+
+ =========================================================================
+ == NOTICE file for the JUnit License. ==
+ =========================================================================
+
+Common Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
+THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+ a) in the case of the initial Contributor, the initial code and
+ documentation distributed under this Agreement, and
+ b) in the case of each subsequent Contributor:
+
+ i) changes to the Program, and
+
+ ii) additions to the Program;
+
+ where such changes and/or additions to the Program originate
+ from and are distributed by that particular Contributor. A
+ Contribution 'originates' from a Contributor if it was added to
+ the Program by such Contributor itself or anyone acting on such
+ Contributor's behalf. Contributions do not include additions to
+ the Program which: (i) are separate modules of software
+ distributed in conjunction with the Program under their own
+ license agreement, and (ii) are not derivative works of the
+ Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its Contribution
+alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+ a) Subject to the terms of this Agreement, each Contributor
+ hereby grants Recipient a non-exclusive, worldwide, royalty-free
+ copyright license to reproduce, prepare derivative works of,
+ publicly display, publicly perform, distribute and sublicense
+ the Contribution of such Contributor, if any, and such
+ derivative works, in source code and object code form.
+
+ b) Subject to the terms of this Agreement, each Contributor
+ hereby grants Recipient a non-exclusive, worldwide, royalty-free
+ patent license under Licensed Patents to make, use, sell, offer
+ to sell, import and otherwise transfer the Contribution of such
+ Contributor, if any, in source code and object code form. This
+ patent license shall apply to the combination of the
+ Contribution and the Program if, at the time the Contribution is
+ added by the Contributor, such addition of the Contribution
+ causes such combination to be covered by the Licensed Patents.
+ The patent license shall not apply to any other combinations
+ which include the Contribution. No hardware per se is licensed
+ hereunder.
+
+ c) Recipient understands that although each Contributor grants
+ the licenses to its Contributions set forth herein, no
+ assurances are provided by any Contributor that the Program does
+ not infringe the patent or other intellectual property rights of
+ any other entity. Each Contributor disclaims any liability to
+ Recipient for claims brought by any other entity based on
+ infringement of intellectual property rights or otherwise. As a
+ condition to exercising the rights and licenses granted
+ hereunder, each Recipient hereby assumes sole responsibility to
+ secure any other intellectual property rights needed, if any.
+ For example, if a third party patent license is required to
+ allow Recipient to distribute the Program, it is Recipient's
+ responsibility to acquire that license before distributing the
+ Program.
+
+ d) Each Contributor represents that to its knowledge it has
+ sufficient copyright rights in its Contribution, if any, to
+ grant the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form
+under its own license agreement, provided that:
+
+ a) it complies with the terms and conditions of this Agreement; and
+
+ b) its license agreement:
+
+ i) effectively disclaims on behalf of all Contributors all
+ warranties and conditions, express and implied, including
+ warranties or conditions of title and non-infringement, and
+ implied warranties or conditions of merchantability and fitness
+ for a particular purpose;
+
+ ii) effectively excludes on behalf of all Contributors all
+ liability for damages, including direct, indirect, special,
+ incidental and consequential damages, such as lost profits;
+
+ iii) states that any provisions which differ from this Agreement
+ are offered by that Contributor alone and not by any other
+ party; and
+
+ iv) states that source code for the Program is available from
+ such Contributor, and informs licensees how to obtain it in a
+ reasonable manner on or through a medium customarily used for
+ software exchange.
+
+When the Program is made available in source code form:
+
+ a) it must be made available under this Agreement; and
+
+ b) a copy of this Agreement must be included with each copy of
+ the Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use
+of the Program, the Contributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for other Contributors. Therefore, if a
+Contributor includes the Program in a commercial product offering,
+such Contributor ("Commercial Contributor") hereby agrees to defend
+and indemnify every other Contributor ("Indemnified Contributor")
+against any losses, damages and costs (collectively "Losses") arising
+from claims, lawsuits and other legal actions brought by a third party
+against the Indemnified Contributor to the extent caused by the acts
+or omissions of such Commercial Contributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement.
+In order to qualify, an Indemnified Contributor must: a) promptly
+notify the Commercial Contributor in writing of such claim, and b)
+allow the Commercial Contributor to control, and cooperate with the
+Commercial Contributor in, the defense and any related settlement
+negotiations. The Indemnified Contributor may participate in any such
+claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement, including but not limited to
+the risks and costs of program errors, compliance with applicable
+laws, damage to or loss of data, programs or equipment, and
+unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against a Contributor with
+respect to a patent applicable to software (including a cross-claim or
+counterclaim in a lawsuit), then any patent licenses granted by that
+Contributor to such Recipient under this Agreement shall terminate as
+of the date such litigation is filed. In addition, if Recipient
+institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and
+survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and
+may only be modified in the following manner. The Agreement Steward
+reserves the right to publish new versions (including revisions) of
+this Agreement from time to time. No one other than the Agreement
+Steward has the right to modify this Agreement. IBM is the initial
+Agreement Steward. IBM may assign the responsibility to serve as the
+Agreement Steward to a suitable separate entity. Each new version of
+the Agreement will be given a distinguishing version number. The
+Program (including Contributions) may always be distributed subject to
+the version of the Agreement under which it was received. In addition,
+after a new version of the Agreement is published, Contributor may
+elect to distribute the Program (including its Contributions) under
+the new version. Except as expressly stated in Sections 2(a) and 2(b)
+above, Recipient receives no rights or licenses to the intellectual
+property of any Contributor under this Agreement, whether expressly,
+by implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this Agreement
+more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.
+
+
+ =========================================================================
+ == NOTICE file for the KXML License. ==
+ =========================================================================
+
+Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+ =========================================================================
+ == NOTICE file for the SQLite Java Wrapper License. ==
+ =========================================================================
+
+This software is copyrighted by Christian Werner <chw@ch-werner.de>
+and others. The following terms apply to all files associated with the
+software unless explicitly disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+
+ =========================================================================
+ == NOTICE file for the W3C License. ==
+ =========================================================================
+
+Copyright (c) 2000 World Wide Web Consortium, (Massachusetts Institute
+of Technology, Institut National de Recherche en Informatique et en
+Automatique, Keio University). All Rights Reserved. This program is
+distributed under the W3C's Software Intellectual Property License.
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See W3C License http://www.w3.org/Consortium/Legal/ for more details.
diff --git a/NativeCode.mk b/NativeCode.mk
new file mode 100644
index 0000000..b7c8b0e
--- /dev/null
+++ b/NativeCode.mk
@@ -0,0 +1,183 @@
+# -*- mode: makefile -*-
+# 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.
+
+#
+# Definitions for building the native code needed for the core library.
+#
+
+#
+# Common definitions for host and target.
+#
+
+# These two definitions are used to help sanity check what's put in
+# sub.mk. See, the "error" directives immediately below.
+core_magic_local_target := ...//::default:://...
+core_local_path := $(LOCAL_PATH)
+
+# Include a submakefile, resolve its source file locations,
+# and stick them on core_src_files. The submakefiles are
+# free to append to LOCAL_SRC_FILES, LOCAL_C_INCLUDES,
+# LOCAL_SHARED_LIBRARIES, or LOCAL_STATIC_LIBRARIES, but nothing
+# else. All other LOCAL_* variables will be ignored.
+#
+# $(1): directory containing the makefile to include
+define include-core-native-dir
+ LOCAL_SRC_FILES :=
+ include $(LOCAL_PATH)/$(1)/sub.mk
+ ifneq ($$(LOCAL_MODULE),$(core_magic_local_target))
+ $$(error $(LOCAL_PATH)/$(1)/sub.mk should not include CLEAR_VARS \
+ or define LOCAL_MODULE)
+ endif
+ ifneq ($$(LOCAL_PATH),$(core_local_path))
+ $$(error $(LOCAL_PATH)/$(1)/sub.mk should not define LOCAL_PATH)
+ endif
+ core_src_files += $$(addprefix $(1)/,$$(LOCAL_SRC_FILES))
+ LOCAL_SRC_FILES :=
+endef
+
+# Set up the default state. Note: We use CLEAR_VARS here, even though
+# we aren't quite defining a new rule yet, to make sure that the
+# sub.mk files don't see anything stray from the last rule that was
+# set up.
+
+# Set up the test library first
+ifeq ($(LIBCORE_SKIP_TESTS),)
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(core_magic_local_target)
+core_src_files :=
+
+# Include the sub.mk files.
+$(foreach dir, \
+ luni/src/test/native, \
+ $(eval $(call include-core-native-dir,$(dir))))
+
+# This is for the test library, so rename the variable.
+test_src_files := $(core_src_files)
+core_src_files :=
+
+# Extract out the allowed LOCAL_* variables. Note: $(sort) also
+# removes duplicates.
+test_c_includes := $(sort libcore/include $(LOCAL_C_INCLUDES) $(JNI_H_INCLUDE))
+test_shared_libraries := $(sort $(LOCAL_SHARED_LIBRARIES))
+test_static_libraries := $(sort $(LOCAL_STATIC_LIBRARIES))
+endif # LIBCORE_SKIP_TESTS
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(core_magic_local_target)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+core_src_files :=
+
+# Include the sub.mk files.
+$(foreach dir, \
+ dalvik/src/main/native luni/src/main/native, \
+ $(eval $(call include-core-native-dir,$(dir))))
+
+# Extract out the allowed LOCAL_* variables. Note: $(sort) also
+# removes duplicates.
+core_c_includes := $(sort libcore/include $(LOCAL_C_INCLUDES) $(JNI_H_INCLUDE))
+core_shared_libraries := $(sort $(LOCAL_SHARED_LIBRARIES))
+core_static_libraries := $(sort $(LOCAL_STATIC_LIBRARIES))
+
+
+#
+# Build for the target (device).
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror
+LOCAL_CFLAGS += $(core_cflags)
+LOCAL_CPPFLAGS += $(core_cppflags)
+ifeq ($(TARGET_ARCH),arm)
+# Ignore "note: the mangling of 'va_list' has changed in GCC 4.4"
+LOCAL_CFLAGS += -Wno-psabi
+endif
+
+# Define the rules.
+LOCAL_SRC_FILES := $(core_src_files)
+LOCAL_C_INCLUDES := $(core_c_includes)
+LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libexpat libicuuc libicui18n libssl libcrypto libz libnativehelper
+LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libjavacore
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+
+LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
+LOCAL_SHARED_LIBRARIES += libstlport
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+# Test library
+ifeq ($(LIBCORE_SKIP_TESTS),)
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror
+LOCAL_CFLAGS += $(core_cflags)
+LOCAL_CPPFLAGS += $(core_cppflags)
+ifeq ($(TARGET_ARCH),arm)
+# Ignore "note: the mangling of 'va_list' has changed in GCC 4.4"
+LOCAL_CFLAGS += -Wno-psabi
+endif
+
+# Define the rules.
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_C_INCLUDES := $(test_c_includes)
+LOCAL_SHARED_LIBRARIES := $(test_shared_libraries)
+LOCAL_STATIC_LIBRARIES := $(test_static_libraries)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libjavacoretests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+
+include $(BUILD_SHARED_LIBRARY)
+endif # LIBCORE_SKIP_TESTS
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+ include $(CLEAR_VARS)
+ # Define the rules.
+ LOCAL_SRC_FILES := $(core_src_files)
+ LOCAL_CFLAGS += $(core_cflags)
+ LOCAL_C_INCLUDES := $(core_c_includes)
+ LOCAL_CPPFLAGS += $(core_cppflags)
+ LOCAL_LDLIBS += -ldl -lpthread
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := libjavacore
+ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+ LOCAL_SHARED_LIBRARIES := $(core_shared_libraries) libexpat libicuuc libicui18n libssl libcrypto libz-host
+ LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
+ include $(BUILD_HOST_SHARED_LIBRARY)
+
+ ifeq ($(LIBCORE_SKIP_TESTS),)
+ include $(CLEAR_VARS)
+ # Define the rules.
+ LOCAL_SRC_FILES := $(test_src_files)
+ LOCAL_CFLAGS += $(core_cflags)
+ LOCAL_C_INCLUDES := $(test_c_includes)
+ LOCAL_CPPFLAGS += $(core_cppflags)
+ LOCAL_LDLIBS += -ldl -lpthread
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := libjavacoretests
+ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk
+ LOCAL_SHARED_LIBRARIES := $(test_shared_libraries)
+ LOCAL_STATIC_LIBRARIES := $(test_static_libraries)
+ include $(BUILD_HOST_SHARED_LIBRARY)
+ endif # LIBCORE_SKIP_TESTS
+endif
diff --git a/benchmarks/AdditionBenchmark.java b/benchmarks/AdditionBenchmark.java
new file mode 100644
index 0000000..a18856e
--- /dev/null
+++ b/benchmarks/AdditionBenchmark.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * What do various kinds of addition cost?
+ */
+public class AdditionBenchmark extends SimpleBenchmark {
+ public int timeAddConstantToLocalInt(int reps) {
+ int result = 0;
+ for (int i = 0; i < reps; ++i) {
+ result += 123;
+ }
+ return result;
+ }
+ public int timeAddTwoLocalInts(int reps) {
+ int result = 0;
+ int constant = 123;
+ for (int i = 0; i < reps; ++i) {
+ result += constant;
+ }
+ return result;
+ }
+ public long timeAddConstantToLocalLong(int reps) {
+ long result = 0;
+ for (int i = 0; i < reps; ++i) {
+ result += 123L;
+ }
+ return result;
+ }
+ public long timeAddTwoLocalLongs(int reps) {
+ long result = 0;
+ long constant = 123L;
+ for (int i = 0; i < reps; ++i) {
+ result += constant;
+ }
+ return result;
+ }
+ public float timeAddConstantToLocalFloat(int reps) {
+ float result = 0.0f;
+ for (int i = 0; i < reps; ++i) {
+ result += 123.0f;
+ }
+ return result;
+ }
+ public float timeAddTwoLocalFloats(int reps) {
+ float result = 0.0f;
+ float constant = 123.0f;
+ for (int i = 0; i < reps; ++i) {
+ result += constant;
+ }
+ return result;
+ }
+ public double timeAddConstantToLocalDouble(int reps) {
+ double result = 0.0;
+ for (int i = 0; i < reps; ++i) {
+ result += 123.0;
+ }
+ return result;
+ }
+ public double timeAddTwoLocalDoubles(int reps) {
+ double result = 0.0;
+ double constant = 123.0;
+ for (int i = 0; i < reps; ++i) {
+ result += constant;
+ }
+ return result;
+ }
+}
diff --git a/benchmarks/ArrayCopyBenchmark.java b/benchmarks/ArrayCopyBenchmark.java
new file mode 100644
index 0000000..75ad243
--- /dev/null
+++ b/benchmarks/ArrayCopyBenchmark.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.Arrays;
+
+public class ArrayCopyBenchmark extends SimpleBenchmark {
+ public void timeManualArrayCopy(int reps) {
+ char[] src = new char[8192];
+ for (int rep = 0; rep < reps; ++rep) {
+ char[] dst = new char[8192];
+ for (int i = 0; i < 8192; ++i) {
+ dst[i] = src[i];
+ }
+ }
+ }
+
+ public void time_System_arrayCopy(int reps) {
+ char[] src = new char[8192];
+ for (int rep = 0; rep < reps; ++rep) {
+ char[] dst = new char[8192];
+ System.arraycopy(src, 0, dst, 0, 8192);
+ }
+ }
+
+ public void time_Arrays_copyOf(int reps) {
+ char[] src = new char[8192];
+ for (int rep = 0; rep < reps; ++rep) {
+ char[] dst = Arrays.copyOf(src, 8192);
+ }
+ }
+
+ public void time_Arrays_copyOfRange(int reps) {
+ char[] src = new char[8192];
+ for (int rep = 0; rep < reps; ++rep) {
+ char[] dst = Arrays.copyOfRange(src, 0, 8192);
+ }
+ }
+}
diff --git a/benchmarks/ArrayIterationBenchmark.java b/benchmarks/ArrayIterationBenchmark.java
new file mode 100644
index 0000000..bdc255b
--- /dev/null
+++ b/benchmarks/ArrayIterationBenchmark.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * How do various ways of iterating through an array compare?
+ */
+public class ArrayIterationBenchmark extends SimpleBenchmark {
+ Foo[] mArray = new Foo[27];
+ {
+ for (int i = 0; i < mArray.length; ++i) mArray[i] = new Foo();
+ }
+ public void timeArrayIteration(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ int sum = 0;
+ for (int i = 0; i < mArray.length; i++) {
+ sum += mArray[i].mSplat;
+ }
+ }
+ }
+ public void timeArrayIterationCached(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ int sum = 0;
+ Foo[] localArray = mArray;
+ int len = localArray.length;
+
+ for (int i = 0; i < len; i++) {
+ sum += localArray[i].mSplat;
+ }
+ }
+ }
+ public void timeArrayIterationForEach(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ int sum = 0;
+ for (Foo a: mArray) {
+ sum += a.mSplat;
+ }
+ }
+ }
+}
diff --git a/benchmarks/ArrayListIterationBenchmark.java b/benchmarks/ArrayListIterationBenchmark.java
new file mode 100644
index 0000000..4e5f145
--- /dev/null
+++ b/benchmarks/ArrayListIterationBenchmark.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.ArrayList;
+
+/**
+ * Is a hand-coded counted loop through an ArrayList cheaper than enhanced for?
+ */
+public class ArrayListIterationBenchmark extends SimpleBenchmark {
+ ArrayList<Foo> mList = new ArrayList<Foo>();
+ {
+ for (int i = 0; i < 27; ++i) mList.add(new Foo());
+ }
+ public void timeArrayListIterationIndexed(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ int sum = 0;
+ ArrayList<Foo> list = mList;
+ int len = list.size();
+ for (int i = 0; i < len; ++i) {
+ sum += list.get(i).mSplat;
+ }
+ }
+ }
+ public void timeArrayListIterationForEach(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ int sum = 0;
+ for (Foo a : mList) {
+ sum += a.mSplat;
+ }
+ }
+ }
+}
diff --git a/benchmarks/BufferedZipFileBenchmark.java b/benchmarks/BufferedZipFileBenchmark.java
new file mode 100644
index 0000000..f4d3822
--- /dev/null
+++ b/benchmarks/BufferedZipFileBenchmark.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public final class BufferedZipFileBenchmark extends SimpleBenchmark {
+ @Param({"128", "1024", "8192", "65536"}) int compressedSize;
+ @Param({"4", "32", "128"}) int readSize;
+
+ private File file;
+
+ @Override protected void setUp() throws Exception {
+ file = File.createTempFile(getClass().getName(), ".zip");
+ file.deleteOnExit();
+
+ Random random = new Random(0);
+ ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
+ byte[] data = new byte[8192];
+ out.putNextEntry(new ZipEntry("entry.data"));
+ int written = 0;
+ while (written < compressedSize) {
+ random.nextBytes(data);
+ int toWrite = Math.min(compressedSize - written, data.length);
+ out.write(data, 0, toWrite);
+ written += toWrite;
+ }
+ out.close();
+ }
+
+ public void timeUnbufferedRead(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ ZipFile zipFile = new ZipFile(file);
+ ZipEntry entry = zipFile.getEntry("entry.data");
+ InputStream in = zipFile.getInputStream(entry);
+ byte[] buffer = new byte[readSize];
+ while (in.read(buffer) != -1) {
+ }
+ in.close();
+ zipFile.close();
+ }
+ }
+
+ public void timeBufferedRead(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ ZipFile zipFile = new ZipFile(file);
+ ZipEntry entry = zipFile.getEntry("entry.data");
+ InputStream in = new BufferedInputStream(zipFile.getInputStream(entry));
+ byte[] buffer = new byte[readSize];
+ while (in.read(buffer) != -1) {
+ }
+ in.close();
+ zipFile.close();
+ }
+ }
+}
diff --git a/benchmarks/FieldAccessBenchmark.java b/benchmarks/FieldAccessBenchmark.java
new file mode 100644
index 0000000..19cb060
--- /dev/null
+++ b/benchmarks/FieldAccessBenchmark.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * What does field access cost?
+ */
+public class FieldAccessBenchmark extends SimpleBenchmark {
+ private static class Inner {
+ public int publicInnerIntVal;
+ protected int protectedInnerIntVal;
+ private int privateInnerIntVal;
+ int packageInnerIntVal;
+ }
+ int intVal = 42;
+ final int finalIntVal = 42;
+ static int staticIntVal = 42;
+ static final int staticFinalIntVal = 42;
+ public int timeField(int reps) {
+ int result = 0;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = intVal;
+ }
+ return result;
+ }
+ public int timeFieldFinal(int reps) {
+ int result = 0;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = finalIntVal;
+ }
+ return result;
+ }
+ public int timeFieldStatic(int reps) {
+ int result = 0;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = staticIntVal;
+ }
+ return result;
+ }
+ public int timeFieldStaticFinal(int reps) {
+ int result = 0;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = staticFinalIntVal;
+ }
+ return result;
+ }
+ public int timeFieldCached(int reps) {
+ int result = 0;
+ int cachedIntVal = this.intVal;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = cachedIntVal;
+ }
+ return result;
+ }
+ public int timeFieldPrivateInnerClassPublicField(int reps) {
+ int result = 0;
+ Inner inner = new Inner();
+ for (int rep = 0; rep < reps; ++rep) {
+ result = inner.publicInnerIntVal;
+ }
+ return result;
+ }
+ public int timeFieldPrivateInnerClassProtectedField(int reps) {
+ int result = 0;
+ Inner inner = new Inner();
+ for (int rep = 0; rep < reps; ++rep) {
+ result = inner.protectedInnerIntVal;
+ }
+ return result;
+ }
+ public int timeFieldPrivateInnerClassPrivateField(int reps) {
+ int result = 0;
+ Inner inner = new Inner();
+ for (int rep = 0; rep < reps; ++rep) {
+ result = inner.privateInnerIntVal;
+ }
+ return result;
+ }
+ public int timeFieldPrivateInnerClassPackageField(int reps) {
+ int result = 0;
+ Inner inner = new Inner();
+ for (int rep = 0; rep < reps; ++rep) {
+ result = inner.packageInnerIntVal;
+ }
+ return result;
+ }
+}
diff --git a/benchmarks/Foo.java b/benchmarks/Foo.java
new file mode 100644
index 0000000..d288dd6
--- /dev/null
+++ b/benchmarks/Foo.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+/**
+ * A trivial class used by several benchmarks.
+ */
+public class Foo {
+ int mSplat;
+}
diff --git a/benchmarks/HashedCollectionsBenchmark.java b/benchmarks/HashedCollectionsBenchmark.java
new file mode 100644
index 0000000..a826271
--- /dev/null
+++ b/benchmarks/HashedCollectionsBenchmark.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+
+/**
+ * How do the various hash maps compare?
+ */
+public class HashedCollectionsBenchmark extends SimpleBenchmark {
+ public void timeHashMapGet(int reps) {
+ HashMap<String, String> map = new HashMap<String, String>();
+ map.put("hello", "world");
+ for (int i = 0; i < reps; ++i) {
+ map.get("hello");
+ }
+ }
+ public void timeHashMapGet_Synchronized(int reps) {
+ HashMap<String, String> map = new HashMap<String, String>();
+ synchronized (map) {
+ map.put("hello", "world");
+ }
+ for (int i = 0; i < reps; ++i) {
+ synchronized (map) {
+ map.get("hello");
+ }
+ }
+ }
+ public void timeHashtableGet(int reps) {
+ Hashtable<String, String> map = new Hashtable<String, String>();
+ map.put("hello", "world");
+ for (int i = 0; i < reps; ++i) {
+ map.get("hello");
+ }
+ }
+ public void timeLinkedHashMapGet(int reps) {
+ LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
+ map.put("hello", "world");
+ for (int i = 0; i < reps; ++i) {
+ map.get("hello");
+ }
+ }
+ public void timeConcurrentHashMapGet(int reps) {
+ ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
+ map.put("hello", "world");
+ for (int i = 0; i < reps; ++i) {
+ map.get("hello");
+ }
+ }
+}
diff --git a/benchmarks/MethodInvocationBenchmark.java b/benchmarks/MethodInvocationBenchmark.java
new file mode 100644
index 0000000..7a5e1b6
--- /dev/null
+++ b/benchmarks/MethodInvocationBenchmark.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Compares various kinds of method invocation.
+ */
+public class MethodInvocationBenchmark extends SimpleBenchmark {
+ interface I {
+ void emptyInterface();
+ }
+
+ static class C implements I {
+ private int field;
+
+ private int getField() {
+ return field;
+ }
+
+ public int timeInternalGetter(int reps) {
+ int result = 0;
+ for (int i = 0; i < reps; ++i) {
+ result = getField();
+ }
+ return result;
+ }
+
+ public int timeInternalFieldAccess(int reps) {
+ int result = 0;
+ for (int i = 0; i < reps; ++i) {
+ result = field;
+ }
+ return result;
+ }
+
+ public static void emptyStatic() {
+ }
+
+ public void emptyVirtual() {
+ }
+
+ public void emptyInterface() {
+ }
+ }
+
+ public void timeInternalGetter(int reps) {
+ new C().timeInternalGetter(reps);
+ }
+
+ public void timeInternalFieldAccess(int reps) {
+ new C().timeInternalFieldAccess(reps);
+ }
+
+ // Test an intrinsic.
+ public int timeStringLength(int reps) {
+ int result = 0;
+ for (int i = 0; i < reps; ++i) {
+ result = "hello, world!".length();
+ }
+ return result;
+ }
+
+ public void timeEmptyStatic(int reps) {
+ C c = new C();
+ for (int i = 0; i < reps; ++i) {
+ c.emptyStatic();
+ }
+ }
+
+ public void timeEmptyVirtual(int reps) {
+ C c = new C();
+ for (int i = 0; i < reps; ++i) {
+ c.emptyVirtual();
+ }
+ }
+
+ public void timeEmptyInterface(int reps) {
+ I c = new C();
+ for (int i = 0; i < reps; ++i) {
+ c.emptyInterface();
+ }
+ }
+
+ public static class Inner {
+ private int i;
+ private void privateMethod() { ++i; }
+ protected void protectedMethod() { ++i; }
+ public void publicMethod() { ++i; }
+ void packageMethod() { ++i; }
+ final void finalPackageMethod() { ++i; }
+ }
+
+ public void timePrivateInnerPublicMethod(int reps) {
+ Inner inner = new Inner();
+ for (int i = 0; i < reps; ++i) {
+ inner.publicMethod();
+ }
+ }
+
+ public void timePrivateInnerProtectedMethod(int reps) {
+ Inner inner = new Inner();
+ for (int i = 0; i < reps; ++i) {
+ inner.protectedMethod();
+ }
+ }
+
+ public void timePrivateInnerPrivateMethod(int reps) {
+ Inner inner = new Inner();
+ for (int i = 0; i < reps; ++i) {
+ inner.privateMethod();
+ }
+ }
+
+ public void timePrivateInnerPackageMethod(int reps) {
+ Inner inner = new Inner();
+ for (int i = 0; i < reps; ++i) {
+ inner.packageMethod();
+ }
+ }
+
+ public void timePrivateInnerFinalPackageMethod(int reps) {
+ Inner inner = new Inner();
+ for (int i = 0; i < reps; ++i) {
+ inner.finalPackageMethod();
+ }
+ }
+}
diff --git a/benchmarks/MultiplicationBenchmark.java b/benchmarks/MultiplicationBenchmark.java
new file mode 100644
index 0000000..b2f945b
--- /dev/null
+++ b/benchmarks/MultiplicationBenchmark.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * How much do various kinds of multiplication cost?
+ */
+public class MultiplicationBenchmark extends SimpleBenchmark {
+ public int timeMultiplyIntByConstant10(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result *= 10;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByConstant8(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result *= 8;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByVariable10(int reps) {
+ int result = 1;
+ int factor = 10;
+ for (int i = 0; i < reps; ++i) {
+ result *= factor;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByVariable8(int reps) {
+ int result = 1;
+ int factor = 8;
+ for (int i = 0; i < reps; ++i) {
+ result *= factor;
+ }
+ return result;
+ }
+}
diff --git a/benchmarks/StringIterationBenchmark.java b/benchmarks/StringIterationBenchmark.java
new file mode 100644
index 0000000..22c6ae2
--- /dev/null
+++ b/benchmarks/StringIterationBenchmark.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * How do the various schemes for iterating through a string compare?
+ */
+public class StringIterationBenchmark extends SimpleBenchmark {
+ public void timeStringIteration0(int reps) {
+ String s = "hello, world!";
+ for (int rep = 0; rep < reps; ++rep) {
+ char ch;
+ for (int i = 0; i < s.length(); ++i) {
+ ch = s.charAt(i);
+ }
+ }
+ }
+ public void timeStringIteration1(int reps) {
+ String s = "hello, world!";
+ for (int rep = 0; rep < reps; ++rep) {
+ char ch;
+ for (int i = 0, length = s.length(); i < length; ++i) {
+ ch = s.charAt(i);
+ }
+ }
+ }
+ public void timeStringIteration2(int reps) {
+ String s = "hello, world!";
+ for (int rep = 0; rep < reps; ++rep) {
+ char ch;
+ char[] chars = s.toCharArray();
+ for (int i = 0, length = chars.length; i < length; ++i) {
+ ch = chars[i];
+ }
+ }
+ }
+ public void timeStringToCharArray(int reps) {
+ String s = "hello, world!";
+ for (int rep = 0; rep < reps; ++rep) {
+ char[] chars = s.toCharArray();
+ }
+ }
+}
diff --git a/benchmarks/VirtualVersusInterfaceBenchmark.java b/benchmarks/VirtualVersusInterfaceBenchmark.java
new file mode 100644
index 0000000..f029c81
--- /dev/null
+++ b/benchmarks/VirtualVersusInterfaceBenchmark.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Is there a performance reason to "Prefer virtual over interface", as the
+ * Android documentation once claimed?
+ */
+public class VirtualVersusInterfaceBenchmark extends SimpleBenchmark {
+ public void timeMapPut(int reps) {
+ Map<String, String> map = new HashMap<String, String>();
+ for (int i = 0; i < reps; ++i) {
+ map.put("hello", "world");
+ }
+ }
+ public void timeHashMapPut(int reps) {
+ HashMap<String, String> map = new HashMap<String, String>();
+ for (int i = 0; i < reps; ++i) {
+ map.put("hello", "world");
+ }
+ }
+}
diff --git a/benchmarks/XmlParseBenchmark.java b/benchmarks/XmlParseBenchmark.java
new file mode 100644
index 0000000..b5e7b93
--- /dev/null
+++ b/benchmarks/XmlParseBenchmark.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xmlpull.v1.XmlPullParser;
+
+public class XmlParseBenchmark extends SimpleBenchmark {
+
+ @Param String xmlFile;
+ ByteArrayInputStream inputStream;
+
+ static List<String> xmlFileValues = Arrays.asList(
+ "/etc/apns-conf.xml",
+ "/etc/media_profiles.xml",
+ "/etc/permissions/features.xml"
+ );
+
+ private SAXParser saxParser;
+ private DocumentBuilder documentBuilder;
+ private Constructor<? extends XmlPullParser> kxmlConstructor;
+ private Constructor<? extends XmlPullParser> expatConstructor;
+
+ @SuppressWarnings("unchecked")
+ @Override protected void setUp() throws Exception {
+ byte[] xmlBytes = getXmlBytes();
+ inputStream = new ByteArrayInputStream(xmlBytes);
+ inputStream.mark(xmlBytes.length);
+
+ SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+ saxParser = saxParserFactory.newSAXParser();
+
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ documentBuilder = builderFactory.newDocumentBuilder();
+
+ kxmlConstructor = (Constructor) Class.forName("org.kxml2.io.KXmlParser").getConstructor();
+ expatConstructor = (Constructor) Class.forName("org.apache.harmony.xml.ExpatPullParser")
+ .getConstructor();
+ }
+
+ private byte[] getXmlBytes() throws IOException {
+ FileInputStream fileIn = new FileInputStream(xmlFile);
+ ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+ int count;
+ byte[] buffer = new byte[1024];
+ while ((count = fileIn.read(buffer)) != -1) {
+ bytesOut.write(buffer, 0, count);
+ }
+ fileIn.close();
+ return bytesOut.toByteArray();
+ }
+
+ public int timeSax(int reps) throws IOException, SAXException {
+ int elementCount = 0;
+ for (int i = 0; i < reps; i++) {
+ inputStream.reset();
+ ElementCounterSaxHandler elementCounterSaxHandler = new ElementCounterSaxHandler();
+ saxParser.parse(inputStream, elementCounterSaxHandler);
+ elementCount += elementCounterSaxHandler.elementCount;
+ }
+ return elementCount;
+ }
+
+ private static class ElementCounterSaxHandler extends DefaultHandler {
+ int elementCount = 0;
+ @Override public void startElement(String uri, String localName,
+ String qName, Attributes attributes) {
+ elementCount++;
+ }
+ }
+
+ public int timeDom(int reps) throws IOException, SAXException {
+ int elementCount = 0;
+ for (int i = 0; i < reps; i++) {
+ inputStream.reset();
+ Document document = documentBuilder.parse(inputStream);
+ elementCount += countDomElements(document.getDocumentElement());
+ }
+ return elementCount;
+ }
+
+ private int countDomElements(Node node) {
+ int result = 0;
+ for (; node != null; node = node.getNextSibling()) {
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ result++;
+ }
+ result += countDomElements(node.getFirstChild());
+ }
+ return result;
+ }
+
+ public int timeExpat(int reps) throws Exception {
+ return testXmlPull(expatConstructor, reps);
+ }
+
+ public int timeKxml(int reps) throws Exception {
+ return testXmlPull(kxmlConstructor, reps);
+ }
+
+ private int testXmlPull(Constructor<? extends XmlPullParser> constructor, int reps)
+ throws Exception {
+ int elementCount = 0;
+ for (int i = 0; i < reps; i++) {
+ inputStream.reset();
+ XmlPullParser xmlPullParser = constructor.newInstance();
+ xmlPullParser.setInput(inputStream, "UTF-8");
+ int type;
+ while ((type = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG) {
+ elementCount++;
+ }
+ }
+ }
+ return elementCount;
+ }
+
+ public static void main(String[] args) {
+ Runner.main(XmlParseBenchmark.class, args);
+ }
+}
diff --git a/benchmarks/regression/AnnotatedElementBenchmark.java b/benchmarks/regression/AnnotatedElementBenchmark.java
new file mode 100644
index 0000000..6c33968
--- /dev/null
+++ b/benchmarks/regression/AnnotatedElementBenchmark.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class AnnotatedElementBenchmark extends SimpleBenchmark {
+
+ private Class<?> type;
+ private Field field;
+ private Method method;
+
+ @Override protected void setUp() throws Exception {
+ type = Type.class;
+ field = Type.class.getField("field");
+ method = Type.class.getMethod("method", String.class);
+ }
+
+
+ // get annotations by member type and method
+
+ public void timeGetTypeAnnotations(int reps) {
+ for (int i = 0; i < reps; i++) {
+ type.getAnnotations();
+ }
+ }
+
+ public void timeGetFieldAnnotations(int reps) {
+ for (int i = 0; i < reps; i++) {
+ field.getAnnotations();
+ }
+ }
+
+ public void timeGetMethodAnnotations(int reps) {
+ for (int i = 0; i < reps; i++) {
+ method.getAnnotations();
+ }
+ }
+
+ public void timeGetParameterAnnotations(int reps) {
+ for (int i = 0; i < reps; i++) {
+ method.getParameterAnnotations();
+ }
+ }
+
+ public void timeGetTypeAnnotation(int reps) {
+ for (int i = 0; i < reps; i++) {
+ type.getAnnotation(Marker.class);
+ }
+ }
+
+ public void timeGetFieldAnnotation(int reps) {
+ for (int i = 0; i < reps; i++) {
+ field.getAnnotation(Marker.class);
+ }
+ }
+
+ public void timeGetMethodAnnotation(int reps) {
+ for (int i = 0; i < reps; i++) {
+ method.getAnnotation(Marker.class);
+ }
+ }
+
+ public void timeIsTypeAnnotationPresent(int reps) {
+ for (int i = 0; i < reps; i++) {
+ type.isAnnotationPresent(Marker.class);
+ }
+ }
+
+ public void timeIsFieldAnnotationPresent(int reps) {
+ for (int i = 0; i < reps; i++) {
+ field.isAnnotationPresent(Marker.class);
+ }
+ }
+
+ public void timeIsMethodAnnotationPresent(int reps) {
+ for (int i = 0; i < reps; i++) {
+ method.isAnnotationPresent(Marker.class);
+ }
+ }
+
+ // get annotations by result size
+
+ public void timeGetAllReturnsLargeAnnotation(int reps) {
+ for (int i = 0; i < reps; i++) {
+ HasLargeAnnotation.class.getAnnotations();
+ }
+ }
+
+ public void timeGetAllReturnsSmallAnnotation(int reps) {
+ for (int i = 0; i < reps; i++) {
+ HasSmallAnnotation.class.getAnnotations();
+ }
+ }
+
+ public void timeGetAllReturnsMarkerAnnotation(int reps) {
+ for (int i = 0; i < reps; i++) {
+ HasMarkerAnnotation.class.getAnnotations();
+ }
+ }
+
+ public void timeGetAllReturnsNoAnnotation(int reps) {
+ for (int i = 0; i < reps; i++) {
+ HasNoAnnotations.class.getAnnotations();
+ }
+ }
+
+ public void timeGetAllReturnsThreeAnnotations(int reps) {
+ for (int i = 0; i < reps; i++) {
+ HasThreeAnnotations.class.getAnnotations();
+ }
+ }
+
+
+ // get annotations with inheritance
+
+ public void timeGetAnnotationsOnSubclass(int reps) {
+ for (int i = 0; i < reps; i++) {
+ ExtendsHasThreeAnnotations.class.getAnnotations();
+ }
+ }
+
+ public void timeGetDeclaredAnnotationsOnSubclass(int reps) {
+ for (int i = 0; i < reps; i++) {
+ ExtendsHasThreeAnnotations.class.getAnnotations();
+ }
+ }
+
+
+ // the annotated elements
+
+ @Marker
+ public class Type {
+ @Marker public String field;
+ @Marker public void method(@Marker String parameter) {}
+ }
+
+ @Large(a = "on class", b = {"A", "B", "C" },
+ c = @Small(e="E1", f=1695938256, g=7264081114510713000L),
+ d = { @Small(e="E2", f=1695938256, g=7264081114510713000L) })
+ public class HasLargeAnnotation {}
+
+ @Small(e="E1", f=1695938256, g=7264081114510713000L)
+ public class HasSmallAnnotation {}
+
+ @Marker
+ public class HasMarkerAnnotation {}
+
+ public class HasNoAnnotations {}
+
+ @Large(a = "on class", b = {"A", "B", "C" },
+ c = @Small(e="E1", f=1695938256, g=7264081114510713000L),
+ d = { @Small(e="E2", f=1695938256, g=7264081114510713000L) })
+ @Small(e="E1", f=1695938256, g=7264081114510713000L)
+ @Marker
+ public class HasThreeAnnotations {}
+
+ public class ExtendsHasThreeAnnotations {}
+
+
+ // the annotations
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Marker {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Large {
+ String a() default "";
+ String[] b() default {};
+ Small c() default @Small;
+ Small[] d() default {};
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Small {
+ String e() default "";
+ int f() default 0;
+ long g() default 0L;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Runner.main(AnnotatedElementBenchmark.class, args);
+ }
+}
diff --git a/benchmarks/regression/BigIntegerBenchmark.java b/benchmarks/regression/BigIntegerBenchmark.java
new file mode 100644
index 0000000..841b901
--- /dev/null
+++ b/benchmarks/regression/BigIntegerBenchmark.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.math.BigInteger;
+import java.util.Random;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class BigIntegerBenchmark extends SimpleBenchmark {
+ public void timeRandomDivision(int reps) throws Exception {
+ Random r = new Random();
+ BigInteger x = new BigInteger(1024, r);
+ BigInteger y = new BigInteger(1024, r);
+ for (int i = 0; i < reps; ++i) {
+ x.divide(y);
+ }
+ }
+
+ public void timeRandomGcd(int reps) throws Exception {
+ Random r = new Random();
+ BigInteger x = new BigInteger(1024, r);
+ BigInteger y = new BigInteger(1024, r);
+ for (int i = 0; i < reps; ++i) {
+ x.gcd(y);
+ }
+ }
+
+ public void timeRandomMultiplication(int reps) throws Exception {
+ Random r = new Random();
+ BigInteger x = new BigInteger(1024, r);
+ BigInteger y = new BigInteger(1024, r);
+ for (int i = 0; i < reps; ++i) {
+ x.multiply(y);
+ }
+ }
+}
diff --git a/benchmarks/regression/BitSetBenchmark.java b/benchmarks/regression/BitSetBenchmark.java
new file mode 100644
index 0000000..ee91993
--- /dev/null
+++ b/benchmarks/regression/BitSetBenchmark.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.util.BitSet;
+
+public class BitSetBenchmark extends SimpleBenchmark {
+ @Param({ "1000", "10000" })
+ private int size;
+
+ private BitSet bs;
+
+ @Override protected void setUp() throws Exception {
+ bs = new BitSet(size);
+ }
+
+ public void timeIsEmptyTrue(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ if (!bs.isEmpty()) throw new RuntimeException();
+ }
+ }
+
+ public void timeIsEmptyFalse(int reps) {
+ bs.set(bs.size() - 1);
+ for (int i = 0; i < reps; ++i) {
+ if (bs.isEmpty()) throw new RuntimeException();
+ }
+ }
+
+ public void timeGet(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ bs.get(i % size);
+ }
+ }
+
+ public void timeClear(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ bs.clear(i % size);
+ }
+ }
+
+ public void timeSet(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ bs.set(i % size);
+ }
+ }
+
+ public void timeSetOn(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ bs.set(i % size, true);
+ }
+ }
+
+ public void timeSetOff(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ bs.set(i % size, false);
+ }
+ }
+}
diff --git a/benchmarks/regression/ByteBufferBenchmark.java b/benchmarks/regression/ByteBufferBenchmark.java
new file mode 100644
index 0000000..7812013
--- /dev/null
+++ b/benchmarks/regression/ByteBufferBenchmark.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.Arrays;
+import java.util.Collection;
+
+public class ByteBufferBenchmark extends SimpleBenchmark {
+ public enum MyByteOrder {
+ BIG(ByteOrder.BIG_ENDIAN), LITTLE(ByteOrder.LITTLE_ENDIAN);
+ final ByteOrder byteOrder;
+ MyByteOrder(ByteOrder byteOrder) {
+ this.byteOrder = byteOrder;
+ }
+ }
+
+ @Param private MyByteOrder byteOrder;
+
+ @Param({"true", "false"}) private boolean aligned;
+
+ enum MyBufferType {
+ DIRECT, HEAP, MAPPED;
+ }
+ @Param private MyBufferType bufferType;
+
+ public static ByteBuffer newBuffer(MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws IOException {
+ int size = aligned ? 8192 : 8192 + 8 + 1;
+ ByteBuffer result = null;
+ switch (bufferType) {
+ case DIRECT:
+ result = ByteBuffer.allocateDirect(size);
+ break;
+ case HEAP:
+ result = ByteBuffer.allocate(size);
+ break;
+ case MAPPED:
+ File tmpFile = new File("/sdcard/bm.tmp");
+ if (new File("/tmp").isDirectory()) {
+ // We're running on the desktop.
+ tmpFile = File.createTempFile("MappedByteBufferTest", ".tmp");
+ }
+ tmpFile.createNewFile();
+ tmpFile.deleteOnExit();
+ RandomAccessFile raf = new RandomAccessFile(tmpFile, "rw");
+ raf.setLength(8192*8);
+ FileChannel fc = raf.getChannel();
+ result = fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size());
+ break;
+ }
+ result.order(byteOrder.byteOrder);
+ result.position(aligned ? 0 : 1);
+ return result;
+ }
+
+ //
+ // peeking
+ //
+
+ public void timeByteBuffer_getByte(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.get();
+ }
+ }
+ }
+
+ public void timeByteBuffer_getByteArray(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ byte[] dst = new byte[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ src.position(aligned ? 0 : 1);
+ src.get(dst);
+ }
+ }
+ }
+
+ public void timeByteBuffer_getByte_indexed(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.get(i);
+ }
+ }
+ }
+
+ public void timeByteBuffer_getChar(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.getChar();
+ }
+ }
+ }
+
+ public void timeCharBuffer_getCharArray(int reps) throws Exception {
+ CharBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asCharBuffer();
+ char[] dst = new char[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ src.position(0);
+ src.get(dst);
+ }
+ }
+ }
+
+ public void timeByteBuffer_getChar_indexed(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.getChar(i * 2);
+ }
+ }
+ }
+
+ public void timeByteBuffer_getDouble(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.getDouble();
+ }
+ }
+ }
+
+ public void timeDoubleBuffer_getDoubleArray(int reps) throws Exception {
+ DoubleBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asDoubleBuffer();
+ double[] dst = new double[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ src.position(0);
+ src.get(dst);
+ }
+ }
+ }
+
+ public void timeByteBuffer_getFloat(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.getFloat();
+ }
+ }
+ }
+
+ public void timeFloatBuffer_getFloatArray(int reps) throws Exception {
+ FloatBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asFloatBuffer();
+ float[] dst = new float[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ src.position(0);
+ src.get(dst);
+ }
+ }
+ }
+
+ public void timeByteBuffer_getInt(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.getInt();
+ }
+ }
+ }
+
+ public void timeIntBuffer_getIntArray(int reps) throws Exception {
+ IntBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asIntBuffer();
+ int[] dst = new int[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ src.position(0);
+ src.get(dst);
+ }
+ }
+ }
+
+ public void timeByteBuffer_getLong(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.getLong();
+ }
+ }
+ }
+
+ public void timeLongBuffer_getLongArray(int reps) throws Exception {
+ LongBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asLongBuffer();
+ long[] dst = new long[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ src.position(0);
+ src.get(dst);
+ }
+ }
+ }
+
+ public void timeByteBuffer_getShort(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.getShort();
+ }
+ }
+ }
+
+ public void timeShortBuffer_getShortArray(int reps) throws Exception {
+ ShortBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asShortBuffer();
+ short[] dst = new short[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ src.position(0);
+ src.get(dst);
+ }
+ }
+ }
+
+ //
+ // poking
+ //
+
+ public void timeByteBuffer_putByte(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(0);
+ for (int i = 0; i < 1024; ++i) {
+ src.put((byte) 0);
+ }
+ }
+ }
+
+ public void timeByteBuffer_putByteArray(int reps) throws Exception {
+ ByteBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ byte[] src = new byte[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ dst.position(aligned ? 0 : 1);
+ dst.put(src);
+ }
+ }
+ }
+
+ public void timeByteBuffer_putChar(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.putChar(' ');
+ }
+ }
+ }
+
+ public void timeCharBuffer_putCharArray(int reps) throws Exception {
+ CharBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asCharBuffer();
+ char[] src = new char[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ dst.position(0);
+ dst.put(src);
+ }
+ }
+ }
+
+ public void timeByteBuffer_putDouble(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.putDouble(0.0);
+ }
+ }
+ }
+
+ public void timeDoubleBuffer_putDoubleArray(int reps) throws Exception {
+ DoubleBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asDoubleBuffer();
+ double[] src = new double[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ dst.position(0);
+ dst.put(src);
+ }
+ }
+ }
+
+ public void timeByteBuffer_putFloat(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.putFloat(0.0f);
+ }
+ }
+ }
+
+ public void timeFloatBuffer_putFloatArray(int reps) throws Exception {
+ FloatBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asFloatBuffer();
+ float[] src = new float[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ dst.position(0);
+ dst.put(src);
+ }
+ }
+ }
+
+ public void timeByteBuffer_putInt(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.putInt(0);
+ }
+ }
+ }
+
+ public void timeIntBuffer_putIntArray(int reps) throws Exception {
+ IntBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asIntBuffer();
+ int[] src = new int[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ dst.position(0);
+ dst.put(src);
+ }
+ }
+ }
+
+ public void timeByteBuffer_putLong(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.putLong(0L);
+ }
+ }
+ }
+
+ public void timeLongBuffer_putLongArray(int reps) throws Exception {
+ LongBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asLongBuffer();
+ long[] src = new long[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ dst.position(0);
+ dst.put(src);
+ }
+ }
+ }
+
+ public void timeByteBuffer_putShort(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ for (int i = 0; i < 1024; ++i) {
+ src.putShort((short) 0);
+ }
+ }
+ }
+
+ public void timeShortBuffer_putShortArray(int reps) throws Exception {
+ ShortBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType).asShortBuffer();
+ short[] src = new short[1024];
+ for (int rep = 0; rep < reps; ++rep) {
+ for (int i = 0; i < 1024; ++i) {
+ dst.position(0);
+ dst.put(src);
+ }
+ }
+ }
+
+/*
+ public void time_new_byteArray(int reps) throws Exception {
+ for (int rep = 0; rep < reps; ++rep) {
+ byte[] bs = new byte[8192];
+ }
+ }
+
+ public void time_ByteBuffer_allocate(int reps) throws Exception {
+ for (int rep = 0; rep < reps; ++rep) {
+ ByteBuffer bs = ByteBuffer.allocate(8192);
+ }
+ }
+ */
+}
diff --git a/benchmarks/regression/ByteBufferScalarVersusVectorBenchmark.java b/benchmarks/regression/ByteBufferScalarVersusVectorBenchmark.java
new file mode 100644
index 0000000..7c75deb
--- /dev/null
+++ b/benchmarks/regression/ByteBufferScalarVersusVectorBenchmark.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.Arrays;
+import java.util.Collection;
+
+public class ByteBufferScalarVersusVectorBenchmark extends SimpleBenchmark {
+ @Param private ByteBufferBenchmark.MyByteOrder byteOrder;
+ @Param({"true", "false"}) private boolean aligned;
+ @Param private ByteBufferBenchmark.MyBufferType bufferType;
+
+ public void timeManualByteBufferCopy(int reps) throws Exception {
+ ByteBuffer src = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ ByteBuffer dst = ByteBufferBenchmark.newBuffer(byteOrder, aligned, bufferType);
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(0);
+ dst.position(0);
+ for (int i = 0; i < 8192; ++i) {
+ dst.put(src.get());
+ }
+ }
+ }
+
+ public void timeByteBufferBulkGet(int reps) throws Exception {
+ ByteBuffer src = ByteBuffer.allocate(aligned ? 8192 : 8192 + 1);
+ byte[] dst = new byte[8192];
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ src.get(dst, 0, dst.length);
+ }
+ }
+
+ public void timeDirectByteBufferBulkGet(int reps) throws Exception {
+ ByteBuffer src = ByteBuffer.allocateDirect(aligned ? 8192 : 8192 + 1);
+ byte[] dst = new byte[8192];
+ for (int rep = 0; rep < reps; ++rep) {
+ src.position(aligned ? 0 : 1);
+ src.get(dst, 0, dst.length);
+ }
+ }
+}
diff --git a/benchmarks/regression/CharacterBenchmark.java b/benchmarks/regression/CharacterBenchmark.java
new file mode 100644
index 0000000..953513b
--- /dev/null
+++ b/benchmarks/regression/CharacterBenchmark.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Tests various Character methods, intended for testing multiple
+ * implementations against each other.
+ */
+public class CharacterBenchmark extends SimpleBenchmark {
+
+ @Param private CharacterSet characterSet;
+
+ @Param private Overload overload;
+
+ private char[] chars;
+
+ @Override protected void setUp() throws Exception {
+ this.chars = characterSet.chars;
+ }
+
+ public enum Overload { CHAR, INT }
+
+ @Override public double nanosToUnits(double nanos) {
+ return nanos / 65536;
+ }
+
+ public enum CharacterSet {
+ ASCII(128),
+ UNICODE(65536);
+ final char[] chars;
+ CharacterSet(int size) {
+ this.chars = new char[65536];
+ for (int i = 0; i < 65536; ++i) {
+ chars[i] = (char) (i % size);
+ }
+ }
+ }
+
+ // A fake benchmark to give us a baseline.
+ public boolean timeIsSpace(int reps) {
+ boolean dummy = false;
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ dummy ^= ((char) ch == ' ');
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ dummy ^= (ch == ' ');
+ }
+ }
+ }
+ return dummy;
+ }
+
+ public void timeDigit(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.digit(chars[ch], 10);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.digit((int) chars[ch], 10);
+ }
+ }
+ }
+ }
+
+ public void timeGetNumericValue(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.getNumericValue(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.getNumericValue((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeIsDigit(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isDigit(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isDigit((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeIsIdentifierIgnorable(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isIdentifierIgnorable(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isIdentifierIgnorable((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeIsJavaIdentifierPart(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isJavaIdentifierPart(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isJavaIdentifierPart((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeIsJavaIdentifierStart(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isJavaIdentifierStart(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isJavaIdentifierStart((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeIsLetter(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isLetter(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isLetter((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeIsLetterOrDigit(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isLetterOrDigit(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isLetterOrDigit((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeIsLowerCase(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isLowerCase(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isLowerCase((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeIsSpaceChar(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isSpaceChar(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isSpaceChar((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeIsUpperCase(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isUpperCase(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isUpperCase((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeIsWhitespace(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isWhitespace(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.isWhitespace((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeToLowerCase(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.toLowerCase(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.toLowerCase((int) chars[ch]);
+ }
+ }
+ }
+ }
+
+ public void timeToUpperCase(int reps) {
+ if (overload == Overload.CHAR) {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.toUpperCase(chars[ch]);
+ }
+ }
+ } else {
+ for (int i = 0; i < reps; ++i) {
+ for (int ch = 0; ch < 65536; ++ch) {
+ Character.toUpperCase((int) chars[ch]);
+ }
+ }
+ }
+ }
+}
diff --git a/benchmarks/regression/CharsetBenchmark.java b/benchmarks/regression/CharsetBenchmark.java
new file mode 100644
index 0000000..6ecada7
--- /dev/null
+++ b/benchmarks/regression/CharsetBenchmark.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.nio.charset.Charset;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class CharsetBenchmark extends SimpleBenchmark {
+ @Param({ "1", "10", "100", "1000", "10000" })
+ private int length;
+
+ // canonical => canonical charset name
+ // built-in => guaranteed-present charset
+ // special-case => libcore treats this charset specially for performance
+ @Param({
+ "UTF-16", // canonical, built-in, non-special-case
+ "UTF-8", // canonical, built-in, special-case
+ "UTF8", // non-canonical, built-in, special-case
+ "ISO-8859-1", // canonical, built-in, special-case
+ "8859_1", // non-canonical, built-in, special-case
+ "ISO-8859-2", // canonical, non-built-in, non-special-case
+ "8859_2", // non-canonical, non-built-in, non-special-case
+ "US-ASCII", // canonical, built-in, special-case
+ "ASCII" // non-canonical, built-in, special-case
+ })
+ private String name;
+
+ public void time_new_String_BString(int reps) throws Exception {
+ byte[] bytes = makeBytes(makeString(length));
+ for (int i = 0; i < reps; ++i) {
+ new String(bytes, name);
+ }
+ }
+
+ public void time_new_String_BII(int reps) throws Exception {
+ byte[] bytes = makeBytes(makeString(length));
+ for (int i = 0; i < reps; ++i) {
+ new String(bytes, 0, bytes.length);
+ }
+ }
+
+ public void time_new_String_BIIString(int reps) throws Exception {
+ byte[] bytes = makeBytes(makeString(length));
+ for (int i = 0; i < reps; ++i) {
+ new String(bytes, 0, bytes.length, name);
+ }
+ }
+
+ public void time_String_getBytes(int reps) throws Exception {
+ String string = makeString(length);
+ for (int i = 0; i < reps; ++i) {
+ string.getBytes(name);
+ }
+ }
+
+ // FIXME: benchmark this pure-java implementation for US-ASCII and ISO-8859-1 too!
+
+ /**
+ * Translates the given characters to US-ASCII or ISO-8859-1 bytes, using the fact that
+ * Unicode code points between U+0000 and U+007f inclusive are identical to US-ASCII, while
+ * U+0000 to U+00ff inclusive are identical to ISO-8859-1.
+ */
+ private static byte[] toDirectMappedBytes(char[] chars, int offset, int length, int maxValidChar) {
+ byte[] result = new byte[length];
+ int o = offset;
+ for (int i = 0; i < length; ++i) {
+ int ch = chars[o++];
+ result[i] = (byte) ((ch <= maxValidChar) ? ch : '?');
+ }
+ return result;
+ }
+
+ private static byte[] toUtf8Bytes(char[] chars, int offset, int length) {
+ UnsafeByteSequence result = new UnsafeByteSequence(length);
+ toUtf8Bytes(chars, offset, length, result);
+ return result.toByteArray();
+ }
+
+ private static void toUtf8Bytes(char[] chars, int offset, int length, UnsafeByteSequence out) {
+ final int end = offset + length;
+ for (int i = offset; i < end; ++i) {
+ int ch = chars[i];
+ if (ch < 0x80) {
+ // One byte.
+ out.write(ch);
+ } else if (ch < 0x800) {
+ // Two bytes.
+ out.write((ch >> 6) | 0xc0);
+ out.write((ch & 0x3f) | 0x80);
+ } else if (ch >= Character.MIN_SURROGATE && ch <= Character.MAX_SURROGATE) {
+ // A supplementary character.
+ char high = (char) ch;
+ char low = (i + 1 != end) ? chars[i + 1] : '\u0000';
+ if (!Character.isSurrogatePair(high, low)) {
+ out.write('?');
+ continue;
+ }
+ // Now we know we have a *valid* surrogate pair, we can consume the low surrogate.
+ ++i;
+ ch = Character.toCodePoint(high, low);
+ // Four bytes.
+ out.write((ch >> 18) | 0xf0);
+ out.write(((ch >> 12) & 0x3f) | 0x80);
+ out.write(((ch >> 6) & 0x3f) | 0x80);
+ out.write((ch & 0x3f) | 0x80);
+ } else {
+ // Three bytes.
+ out.write((ch >> 12) | 0xe0);
+ out.write(((ch >> 6) & 0x3f) | 0x80);
+ out.write((ch & 0x3f) | 0x80);
+ }
+ }
+ }
+
+ private static String makeString(int length) {
+ StringBuilder result = new StringBuilder(length);
+ for (int i = 0; i < length; ++i) {
+ result.append('A' + (i % 26));
+ }
+ return result.toString();
+ }
+
+ private static byte[] makeBytes(String s) {
+ try {
+ return s.getBytes("US-ASCII");
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/benchmarks/regression/CharsetForNameBenchmark.java b/benchmarks/regression/CharsetForNameBenchmark.java
new file mode 100644
index 0000000..87dfb8b
--- /dev/null
+++ b/benchmarks/regression/CharsetForNameBenchmark.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.nio.charset.Charset;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class CharsetForNameBenchmark extends SimpleBenchmark {
+ // canonical => canonical charset name
+ // built-in => guaranteed-present charset
+ // special-case => libcore treats this charset specially for performance
+ @Param({
+ "UTF-16", // canonical, built-in, non-special-case
+ "UTF-8", // canonical, built-in, special-case
+ "UTF8", // non-canonical, built-in, special-case
+ "ISO-8859-1", // canonical, built-in, special-case
+ "8859_1", // non-canonical, built-in, special-case
+ "ISO-8859-2", // canonical, non-built-in, non-special-case
+ "8859_2", // non-canonical, non-built-in, non-special-case
+ "US-ASCII", // canonical, built-in, special-case
+ "ASCII" // non-canonical, built-in, special-case
+ })
+ private String charsetName;
+
+ public void timeCharsetForName(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ Charset.forName(charsetName);
+ }
+ }
+}
diff --git a/benchmarks/regression/ChecksumBenchmark.java b/benchmarks/regression/ChecksumBenchmark.java
new file mode 100644
index 0000000..0e7bf8d
--- /dev/null
+++ b/benchmarks/regression/ChecksumBenchmark.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.util.zip.Adler32;
+import java.util.zip.CRC32;
+
+public class ChecksumBenchmark extends SimpleBenchmark {
+ public void timeAdler_block(int reps) throws Exception {
+ byte[] bytes = new byte[10000];
+ Adler32 adler = new Adler32();
+ for (int i = 0; i < reps; ++i) {
+ adler.update(bytes);
+ }
+ }
+ public void timeAdler_byte(int reps) throws Exception {
+ Adler32 adler = new Adler32();
+ for (int i = 0; i < reps; ++i) {
+ adler.update(1);
+ }
+ }
+ public void timeCrc_block(int reps) throws Exception {
+ byte[] bytes = new byte[10000];
+ CRC32 crc = new CRC32();
+ for (int i = 0; i < reps; ++i) {
+ crc.update(bytes);
+ }
+ }
+ public void timeCrc_byte(int reps) throws Exception {
+ CRC32 crc = new CRC32();
+ for (int i = 0; i < reps; ++i) {
+ crc.update(1);
+ }
+ }
+}
diff --git a/benchmarks/regression/CipherBenchmark.java b/benchmarks/regression/CipherBenchmark.java
new file mode 100644
index 0000000..56d549b
--- /dev/null
+++ b/benchmarks/regression/CipherBenchmark.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Cipher benchmarks. Only runs on AES currently because of the combinatorial
+ * explosion of the test as it stands.
+ */
+public class CipherBenchmark extends SimpleBenchmark {
+
+ private static final int DATA_SIZE = 8192;
+ private static final byte[] DATA = new byte[DATA_SIZE];
+
+ private static final int IV_SIZE = 16;
+
+ private static final byte[] IV = new byte[IV_SIZE];
+
+ static {
+ for (int i = 0; i < DATA_SIZE; i++) {
+ DATA[i] = (byte) i;
+ }
+ for (int i = 0; i < IV_SIZE; i++) {
+ IV[i] = (byte) i;
+ }
+ }
+
+ @Param private Algorithm algorithm;
+
+ public enum Algorithm {
+ AES,
+ };
+
+ @Param private Mode mode;
+
+ public enum Mode {
+ CBC,
+ CFB,
+ CTR,
+ ECB,
+ OFB,
+ };
+
+ @Param private Padding padding;
+
+ public enum Padding {
+ NOPADDING,
+ PKCS1PADDING,
+ };
+
+ @Param({"128", "192", "256"}) private int keySize;
+
+ @Param({"16", "32", "64", "128", "1024", "8192"}) private int inputSize;
+
+ @Param private Implementation implementation;
+
+ public enum Implementation { OpenSSL, BouncyCastle };
+
+ private String providerName;
+
+ // Key generation isn't part of the benchmark so cache the results
+ private static Map<Integer, SecretKey> KEY_SIZES = new HashMap<Integer, SecretKey>();
+
+ private String cipherAlgorithm;
+ private SecretKey key;
+
+ private byte[] output = new byte[DATA.length];
+
+ private Cipher cipherEncrypt;
+
+ private Cipher cipherDecrypt;
+
+ private AlgorithmParameterSpec spec;
+
+ @Override protected void setUp() throws Exception {
+ cipherAlgorithm = algorithm.toString() + "/" + mode.toString() + "/"
+ + padding.toString();
+
+ String keyAlgorithm = algorithm.toString();
+ key = KEY_SIZES.get(keySize);
+ if (key == null) {
+ KeyGenerator generator = KeyGenerator.getInstance(keyAlgorithm);
+ generator.init(keySize);
+ key = generator.generateKey();
+ KEY_SIZES.put(keySize, key);
+ }
+
+ switch (implementation) {
+ case OpenSSL:
+ providerName = "AndroidOpenSSL";
+ break;
+ case BouncyCastle:
+ providerName = "BC";
+ break;
+ default:
+ throw new RuntimeException(implementation.toString());
+ }
+
+ if (mode != Mode.ECB) {
+ spec = new IvParameterSpec(IV);
+ }
+
+ cipherEncrypt = Cipher.getInstance(cipherAlgorithm, providerName);
+ cipherEncrypt.init(Cipher.ENCRYPT_MODE, key, spec);
+
+ cipherDecrypt = Cipher.getInstance(cipherAlgorithm, providerName);
+ cipherDecrypt.init(Cipher.DECRYPT_MODE, key, spec);
+ }
+
+ public void timeEncrypt(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ cipherEncrypt.doFinal(DATA, 0, inputSize, output);
+ }
+ }
+
+ public void timeDecrypt(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ cipherDecrypt.doFinal(DATA, 0, inputSize, output);
+ }
+ }
+}
diff --git a/benchmarks/regression/DateToStringBenchmark.java b/benchmarks/regression/DateToStringBenchmark.java
new file mode 100644
index 0000000..15760c9
--- /dev/null
+++ b/benchmarks/regression/DateToStringBenchmark.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import android.text.format.DateFormat;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+public final class DateToStringBenchmark extends SimpleBenchmark {
+ Date date;
+ Calendar calendar;
+ SimpleDateFormat format;
+
+ @Override
+ protected void setUp() throws Exception {
+ date = new Date(0);
+ calendar = new GregorianCalendar();
+ calendar.setTime(date);
+ format = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
+ }
+
+ public void timeDateToString(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ date.toString();
+ }
+ }
+
+ public void timeDateToString_Formatter(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy").format(date);
+ }
+ }
+
+ public void timeDateToString_ClonedFormatter(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ ((SimpleDateFormat) format.clone()).format(date);
+ }
+ }
+
+ public void timeDateToString_AndroidDateFormat(int reps) {
+ for (int i = 0; i < reps; i++) {
+ DateFormat.format("EEE MMM dd kk:mm:ss zzz yyyy", calendar);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ Runner.main(DateToStringBenchmark.class, args);
+ }
+}
diff --git a/benchmarks/regression/DefaultCharsetBenchmark.java b/benchmarks/regression/DefaultCharsetBenchmark.java
new file mode 100644
index 0000000..bc7b013
--- /dev/null
+++ b/benchmarks/regression/DefaultCharsetBenchmark.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.nio.charset.Charset;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class DefaultCharsetBenchmark extends SimpleBenchmark {
+ public void time_defaultCharset(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ Charset.defaultCharset();
+ }
+ }
+}
diff --git a/benchmarks/regression/DigestBenchmark.java b/benchmarks/regression/DigestBenchmark.java
new file mode 100644
index 0000000..7d00fec
--- /dev/null
+++ b/benchmarks/regression/DigestBenchmark.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import com.android.org.bouncycastle.crypto.Digest;
+
+public class DigestBenchmark extends SimpleBenchmark {
+
+ private static final int DATA_SIZE = 8192;
+ private static final byte[] DATA = new byte[DATA_SIZE];
+ static {
+ for (int i = 0; i < DATA_SIZE; i++) {
+ DATA[i] = (byte)i;
+ }
+ }
+
+ @Param private Algorithm algorithm;
+
+ public enum Algorithm { MD5, SHA1, SHA256, SHA384, SHA512 };
+
+ @Param private Implementation implementation;
+
+ public enum Implementation { OPENSSL, BOUNCYCASTLE };
+
+ private Class<? extends Digest> digestClass;
+
+ @Override protected void setUp() throws Exception {
+ String className = "com.android.org.bouncycastle.crypto.digests.";
+ switch (implementation) {
+ case OPENSSL:
+ className += ("OpenSSLDigest$" + algorithm);
+ break;
+ case BOUNCYCASTLE:
+ className += (algorithm + "Digest");
+ break;
+ default:
+ throw new RuntimeException(implementation.toString());
+ }
+ this.digestClass = (Class<? extends Digest>)Class.forName(className);
+ }
+
+ public void time(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ Digest digest = digestClass.newInstance();
+ digest.update(DATA, 0, DATA_SIZE);
+ digest.doFinal(new byte[digest.getDigestSize()], 0);
+ }
+ }
+}
diff --git a/benchmarks/regression/DnsBenchmark.java b/benchmarks/regression/DnsBenchmark.java
new file mode 100644
index 0000000..2a71716
--- /dev/null
+++ b/benchmarks/regression/DnsBenchmark.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class DnsBenchmark extends SimpleBenchmark {
+ public void timeDns(int reps) throws Exception {
+ String[] hosts = new String[] {
+ "www.amazon.com",
+ "z-ecx.images-amazon.com",
+ "g-ecx.images-amazon.com",
+ "ecx.images-amazon.com",
+ "ad.doubleclick.com",
+ "bpx.a9.com",
+ "d3dtik4dz1nej0.cloudfront.net",
+ "uac.advertising.com",
+ "servedby.advertising.com",
+ "view.atdmt.com",
+ "rmd.atdmt.com",
+ "spe.atdmt.com",
+ "www.google.com",
+ "www.cnn.com",
+ "bad.host.mtv.corp.google.com",
+ };
+ for (int i = 0; i < reps; ++i) {
+ try {
+ InetAddress.getByName(hosts[i % hosts.length]);
+ } catch (UnknownHostException ex) {
+ }
+ }
+ }
+}
diff --git a/benchmarks/regression/DoPrivilegedBenchmark.java b/benchmarks/regression/DoPrivilegedBenchmark.java
new file mode 100644
index 0000000..effb284
--- /dev/null
+++ b/benchmarks/regression/DoPrivilegedBenchmark.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+public class DoPrivilegedBenchmark extends SimpleBenchmark {
+ public void timeDirect(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ String lineSeparator = System.getProperty("line.separator");
+ }
+ }
+
+ public void timeFastAndSlow(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ String lineSeparator;
+ if (System.getSecurityManager() == null) {
+ lineSeparator = System.getProperty("line.separator");
+ } else {
+ lineSeparator = AccessController.doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty("line.separator");
+ }
+ });
+ }
+ }
+ }
+
+ public void timeNewAction(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ String lineSeparator = AccessController.doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty("line.separator");
+ }
+ });
+ }
+ }
+
+ public void timeReusedAction(int reps) throws Exception {
+ final PrivilegedAction<String> action = new ReusableAction("line.separator");
+ for (int i = 0; i < reps; ++i) {
+ String lineSeparator = AccessController.doPrivileged(action);
+ }
+ }
+
+ private static final class ReusableAction implements PrivilegedAction<String> {
+ private final String propertyName;
+
+ public ReusableAction(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ public String run() {
+ return System.getProperty(propertyName);
+ }
+ }
+}
diff --git a/benchmarks/regression/DoubleBenchmark.java b/benchmarks/regression/DoubleBenchmark.java
new file mode 100644
index 0000000..aa692ac
--- /dev/null
+++ b/benchmarks/regression/DoubleBenchmark.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class DoubleBenchmark extends SimpleBenchmark {
+ private double d = 1.2;
+ private long l = 4608083138725491507L;
+
+ public void timeDoubleToLongBits(int reps) {
+ long result = 123;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = Double.doubleToLongBits(d);
+ }
+ if (result != l) {
+ throw new RuntimeException(Long.toString(result));
+ }
+ }
+
+ public void timeDoubleToRawLongBits(int reps) {
+ long result = 123;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = Double.doubleToRawLongBits(d);
+ }
+ if (result != l) {
+ throw new RuntimeException(Long.toString(result));
+ }
+ }
+
+ public void timeLongBitsToDouble(int reps) {
+ double result = 123.0;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = Double.longBitsToDouble(l);
+ }
+ if (result != d) {
+ throw new RuntimeException(Double.toString(result) + " " + Double.doubleToRawLongBits(result));
+ }
+ }
+}
diff --git a/benchmarks/regression/EqualsHashCodeBenchmark.java b/benchmarks/regression/EqualsHashCodeBenchmark.java
new file mode 100644
index 0000000..a15a41a
--- /dev/null
+++ b/benchmarks/regression/EqualsHashCodeBenchmark.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.net.URI;
+import java.net.URL;
+
+public final class EqualsHashCodeBenchmark extends SimpleBenchmark {
+ private enum Type {
+ URI() {
+ @Override Object newInstance(String text) throws Exception {
+ return new URI(text);
+ }
+ },
+ URL() {
+ @Override Object newInstance(String text) throws Exception {
+ return new URL(text);
+ }
+ };
+ abstract Object newInstance(String text) throws Exception;
+ }
+
+ @Param Type type;
+
+ Object a1;
+ Object a2;
+ Object b1;
+ Object b2;
+
+ @Override protected void setUp() throws Exception {
+ a1 = type.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
+ a2 = type.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
+ b1 = type.newInstance("http://developer.android.com/reference/java/net/URI.html");
+ b2 = type.newInstance("http://developer.android.com/reference/java/net/URI.html");
+ }
+
+ public void timeEquals(int reps) {
+ for (int i = 0; i < reps; i+=3) {
+ a1.equals(b1);
+ a1.equals(a2);
+ b1.equals(b2);
+ }
+ }
+
+ public void timeHashCode(int reps) {
+ for (int i = 0; i < reps; i+=2) {
+ a1.hashCode();
+ b1.hashCode();
+ }
+ }
+}
diff --git a/benchmarks/regression/ExpensiveObjectsBenchmark.java b/benchmarks/regression/ExpensiveObjectsBenchmark.java
new file mode 100644
index 0000000..535e298
--- /dev/null
+++ b/benchmarks/regression/ExpensiveObjectsBenchmark.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.text.*;
+import java.util.*;
+
+/**
+ * Benchmarks creation and cloning various expensive objects.
+ */
+public class ExpensiveObjectsBenchmark extends SimpleBenchmark {
+ public void timeNewDateFormatTimeInstance(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
+ df.format(System.currentTimeMillis());
+ }
+ }
+
+ public void timeClonedDateFormatTimeInstance(int reps) {
+ DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
+ for (int i = 0; i < reps; ++i) {
+ ((DateFormat) df.clone()).format(System.currentTimeMillis());
+ }
+ }
+
+ public void timeReusedDateFormatTimeInstance(int reps) {
+ DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
+ for (int i = 0; i < reps; ++i) {
+ synchronized (df) {
+ df.format(System.currentTimeMillis());
+ }
+ }
+ }
+
+ public void timeNewCollator(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ Collator.getInstance(Locale.US);
+ }
+ }
+
+ public void timeClonedCollator(int reps) {
+ Collator c = Collator.getInstance(Locale.US);
+ for (int i = 0; i < reps; ++i) {
+ c.clone();
+ }
+ }
+
+ public void timeNewDateFormatSymbols(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ new DateFormatSymbols(Locale.US);
+ }
+ }
+
+ public void timeClonedDateFormatSymbols(int reps) {
+ DateFormatSymbols dfs = new DateFormatSymbols(Locale.US);
+ for (int i = 0; i < reps; ++i) {
+ dfs.clone();
+ }
+ }
+
+ public void timeNewDecimalFormatSymbols(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ new DecimalFormatSymbols(Locale.US);
+ }
+ }
+
+ public void timeClonedDecimalFormatSymbols(int reps) {
+ DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
+ for (int i = 0; i < reps; ++i) {
+ dfs.clone();
+ }
+ }
+
+ public void timeNewNumberFormat(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ NumberFormat.getInstance(Locale.US);
+ }
+ }
+
+ public void timeClonedNumberFormat(int reps) {
+ NumberFormat nf = NumberFormat.getInstance(Locale.US);
+ for (int i = 0; i < reps; ++i) {
+ nf.clone();
+ }
+ }
+
+ public void timeNumberFormatTrivialFormatLong(int reps) {
+ NumberFormat nf = NumberFormat.getInstance(Locale.US);
+ for (int i = 0; i < reps; ++i) {
+ nf.format(1024L);
+ }
+ }
+
+ public void timeLongToString(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ Long.toString(1024L);
+ }
+ }
+
+ public void timeNumberFormatTrivialFormatDouble(int reps) {
+ NumberFormat nf = NumberFormat.getInstance(Locale.US);
+ for (int i = 0; i < reps; ++i) {
+ nf.format(1024.0);
+ }
+ }
+
+ public void timeNewSimpleDateFormat(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ new SimpleDateFormat();
+ }
+ }
+
+ public void timeClonedSimpleDateFormat(int reps) {
+ SimpleDateFormat sdf = new SimpleDateFormat();
+ for (int i = 0; i < reps; ++i) {
+ sdf.clone();
+ }
+ }
+
+ public void timeNewGregorianCalendar(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ new GregorianCalendar();
+ }
+ }
+
+ public void timeClonedGregorianCalendar(int reps) {
+ GregorianCalendar gc = new GregorianCalendar();
+ for (int i = 0; i < reps; ++i) {
+ gc.clone();
+ }
+ }
+}
diff --git a/benchmarks/regression/FloatBenchmark.java b/benchmarks/regression/FloatBenchmark.java
new file mode 100644
index 0000000..a92bb69
--- /dev/null
+++ b/benchmarks/regression/FloatBenchmark.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class FloatBenchmark extends SimpleBenchmark {
+ private float f = 1.2f;
+ private int i = 1067030938;
+
+ public void timeFloatToIntBits(int reps) {
+ int result = 123;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = Float.floatToIntBits(f);
+ }
+ if (result != i) {
+ throw new RuntimeException(Integer.toString(result));
+ }
+ }
+
+ public void timeFloatToRawIntBits(int reps) {
+ int result = 123;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = Float.floatToRawIntBits(f);
+ }
+ if (result != i) {
+ throw new RuntimeException(Integer.toString(result));
+ }
+ }
+
+ public void timeIntBitsToFloat(int reps) {
+ float result = 123.0f;
+ for (int rep = 0; rep < reps; ++rep) {
+ result = Float.intBitsToFloat(i);
+ }
+ if (result != f) {
+ throw new RuntimeException(Float.toString(result) + " " + Float.floatToRawIntBits(result));
+ }
+ }
+}
diff --git a/benchmarks/regression/FormatterBenchmark.java b/benchmarks/regression/FormatterBenchmark.java
new file mode 100644
index 0000000..0d4cf26
--- /dev/null
+++ b/benchmarks/regression/FormatterBenchmark.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.util.Formatter;
+import java.util.Locale;
+
+/**
+ * Compares Formatter against hand-written StringBuilder code.
+ */
+public class FormatterBenchmark extends SimpleBenchmark {
+ public void timeFormatter_NoFormatting(int reps) {
+ for (int i = 0; i < reps; i++) {
+ Formatter f = new Formatter();
+ f.format("this is a reasonably short string that doesn't actually need any formatting");
+ }
+ }
+
+ public void timeStringBuilder_NoFormatting(int reps) {
+ for (int i = 0; i < reps; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("this is a reasonably short string that doesn't actually need any formatting");
+ }
+ }
+
+ public void timeFormatter_OneInt(int reps) {
+ Integer value = Integer.valueOf(1024); // We're not trying to benchmark boxing here.
+ for (int i = 0; i < reps; i++) {
+ Formatter f = new Formatter();
+ f.format("this is a reasonably short string that has an int %d in it", value);
+ }
+ }
+
+ public void timeFormatter_OneIntArabic(int reps) {
+ Locale arabic = new Locale("ar");
+ Integer value = Integer.valueOf(1024); // We're not trying to benchmark boxing here.
+ for (int i = 0; i < reps; i++) {
+ Formatter f = new Formatter();
+ f.format(arabic, "this is a reasonably short string that has an int %d in it", value);
+ }
+ }
+
+ public void timeStringBuilder_OneInt(int reps) {
+ for (int i = 0; i < reps; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("this is a reasonably short string that has an int ");
+ sb.append(1024);
+ sb.append(" in it");
+ }
+ }
+
+ public void timeFormatter_OneHexInt(int reps) {
+ Integer value = Integer.valueOf(1024); // We're not trying to benchmark boxing here.
+ for (int i = 0; i < reps; i++) {
+ Formatter f = new Formatter();
+ f.format("this is a reasonably short string that has an int %x in it", value);
+ }
+ }
+
+ public void timeStringBuilder_OneHexInt(int reps) {
+ for (int i = 0; i < reps; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("this is a reasonably short string that has an int ");
+ sb.append(Integer.toHexString(1024));
+ sb.append(" in it");
+ }
+ }
+
+ public void timeFormatter_OneFloat(int reps) {
+ Float value = Float.valueOf(10.24f); // We're not trying to benchmark boxing here.
+ for (int i = 0; i < reps; i++) {
+ Formatter f = new Formatter();
+ f.format("this is a reasonably short string that has a float %f in it", value);
+ }
+ }
+
+ public void timeFormatter_OneFloat_dot2f(int reps) {
+ Float value = Float.valueOf(10.24f); // We're not trying to benchmark boxing here.
+ for (int i = 0; i < reps; i++) {
+ Formatter f = new Formatter();
+ f.format("this is a reasonably short string that has a float %.2f in it", value);
+ }
+ }
+
+ public void timeFormatter_TwoFloats(int reps) {
+ Float value = Float.valueOf(10.24f); // We're not trying to benchmark boxing here.
+ for (int i = 0; i < reps; i++) {
+ Formatter f = new Formatter();
+ f.format("this is a reasonably short string that has two floats %f and %f in it", value, value);
+ }
+ }
+
+ public void timeStringBuilder_OneFloat(int reps) {
+ for (int i = 0; i < reps; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("this is a reasonably short string that has a float ");
+ sb.append(10.24f);
+ sb.append(" in it");
+ }
+ }
+
+ public void timeFormatter_OneString(int reps) {
+ for (int i = 0; i < reps; i++) {
+ Formatter f = new Formatter();
+ f.format("this is a reasonably short string that has a string %s in it", "hello");
+ }
+ }
+
+ public void timeStringBuilder_OneString(int reps) {
+ for (int i = 0; i < reps; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("this is a reasonably short string that has a string ");
+ sb.append("hello");
+ sb.append(" in it");
+ }
+ }
+}
diff --git a/benchmarks/regression/HostnameVerifierBenchmark.java b/benchmarks/regression/HostnameVerifierBenchmark.java
new file mode 100644
index 0000000..e9218c4
--- /dev/null
+++ b/benchmarks/regression/HostnameVerifierBenchmark.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.io.ByteArrayInputStream;
+import java.net.URL;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * This benchmark makes a real HTTP connection to a handful of hosts and
+ * captures the served certificates as a byte array. It then verifies each
+ * certificate in the benchmark loop, being careful to convert from the
+ * byte[] to the certificate each time. Otherwise the certificate class
+ * caches previous results which skews the results of the benchmark: In practice
+ * each certificate instance is verified once and then released.
+ */
+public final class HostnameVerifierBenchmark extends SimpleBenchmark {
+
+ @Param({"android.clients.google.com",
+ "m.google.com",
+ "www.google.com",
+ "www.amazon.com",
+ "www.ubs.com"}) String host;
+
+ private String hostname;
+ private HostnameVerifier hostnameVerifier;
+ private byte[][] encodedCertificates;
+
+ @Override protected void setUp() throws Exception {
+ URL url = new URL("https", host, "/");
+ hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+ connection.setHostnameVerifier(new HostnameVerifier() {
+ public boolean verify(String hostname, SSLSession sslSession) {
+ try {
+ encodedCertificates = certificatesToBytes(sslSession.getPeerCertificates());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ HostnameVerifierBenchmark.this.hostname = hostname;
+ return true;
+ }
+ });
+ connection.getInputStream();
+ connection.disconnect();
+ }
+
+ public void timeVerify(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ final Certificate[] certificates = bytesToCertificates(encodedCertificates);
+ FakeSSLSession sslSession = new FakeSSLSession() {
+ @Override public Certificate[] getPeerCertificates() {
+ return certificates;
+ }
+ };
+ hostnameVerifier.verify(hostname, sslSession);
+ }
+ }
+
+ private byte[][] certificatesToBytes(Certificate[] certificates) throws Exception {
+ byte[][] result = new byte[certificates.length][];
+ for (int i = 0, certificatesLength = certificates.length; i < certificatesLength; i++) {
+ result[i] = certificates[i].getEncoded();
+ }
+ return result;
+ }
+
+ private Certificate[] bytesToCertificates(byte[][] encodedCertificates) throws Exception {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ Certificate[] result = new Certificate[encodedCertificates.length];
+ for (int i = 0; i < encodedCertificates.length; i++) {
+ result[i] = certificateFactory.generateCertificate(
+ new ByteArrayInputStream(encodedCertificates[i]));
+ }
+ return result;
+ }
+
+ private static class FakeSSLSession implements SSLSession {
+ public int getApplicationBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+ public String getCipherSuite() {
+ throw new UnsupportedOperationException();
+ }
+ public long getCreationTime() {
+ throw new UnsupportedOperationException();
+ }
+ public byte[] getId() {
+ throw new UnsupportedOperationException();
+ }
+ public long getLastAccessedTime() {
+ throw new UnsupportedOperationException();
+ }
+ public Certificate[] getLocalCertificates() {
+ throw new UnsupportedOperationException();
+ }
+ public Principal getLocalPrincipal() {
+ throw new UnsupportedOperationException();
+ }
+ public int getPacketBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain() {
+ throw new UnsupportedOperationException();
+ }
+ public Certificate[] getPeerCertificates() {
+ throw new UnsupportedOperationException();
+ }
+ public String getPeerHost() {
+ throw new UnsupportedOperationException();
+ }
+ public int getPeerPort() {
+ throw new UnsupportedOperationException();
+ }
+ public Principal getPeerPrincipal() {
+ throw new UnsupportedOperationException();
+ }
+ public String getProtocol() {
+ throw new UnsupportedOperationException();
+ }
+ public SSLSessionContext getSessionContext() {
+ throw new UnsupportedOperationException();
+ }
+ public Object getValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+ public String[] getValueNames() {
+ throw new UnsupportedOperationException();
+ }
+ public void invalidate() {
+ throw new UnsupportedOperationException();
+ }
+ public boolean isValid() {
+ throw new UnsupportedOperationException();
+ }
+ public void putValue(String name, Object value) {
+ throw new UnsupportedOperationException();
+ }
+ public void removeValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public static void main(String[] args) {
+ Runner.main(HostnameVerifierBenchmark.class, args);
+ }
+}
diff --git a/benchmarks/regression/IntConstantDivisionBenchmark.java b/benchmarks/regression/IntConstantDivisionBenchmark.java
new file mode 100644
index 0000000..498b783
--- /dev/null
+++ b/benchmarks/regression/IntConstantDivisionBenchmark.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class IntConstantDivisionBenchmark extends SimpleBenchmark {
+ public int timeDivideIntByConstant2(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result /= 2;
+ }
+ return result;
+ }
+ public int timeDivideIntByConstant8(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result /= 8;
+ }
+ return result;
+ }
+ public int timeDivideIntByConstant10(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result /= 10;
+ }
+ return result;
+ }
+ public int timeDivideIntByConstant100(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result /= 100;
+ }
+ return result;
+ }
+ public int timeDivideIntByConstant100_HandOptimized(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result = (int) ((0x51eb851fL * result) >>> 37);
+ }
+ return result;
+ }
+ public int timeDivideIntByConstant2048(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result /= 2048;
+ }
+ return result;
+ }
+ public int timeDivideIntByVariable2(int reps) {
+ int result = 1;
+ int factor = 2;
+ for (int i = 0; i < reps; ++i) {
+ result /= factor;
+ }
+ return result;
+ }
+ public int timeDivideIntByVariable10(int reps) {
+ int result = 1;
+ int factor = 10;
+ for (int i = 0; i < reps; ++i) {
+ result /= factor;
+ }
+ return result;
+ }
+}
diff --git a/benchmarks/regression/IntConstantMultiplicationBenchmark.java b/benchmarks/regression/IntConstantMultiplicationBenchmark.java
new file mode 100644
index 0000000..ca1349c
--- /dev/null
+++ b/benchmarks/regression/IntConstantMultiplicationBenchmark.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class IntConstantMultiplicationBenchmark extends SimpleBenchmark {
+ public int timeMultiplyIntByConstant6(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result *= 6;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByConstant7(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result *= 7;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByConstant8(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result *= 8;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByConstant8_Shift(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result <<= 3;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByConstant10(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result *= 10;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByConstant10_Shift(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result = (result + (result << 2)) << 1;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByConstant2047(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result *= 2047;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByConstant2048(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result *= 2048;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByConstant2049(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result *= 2049;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByVariable10(int reps) {
+ int result = 1;
+ int factor = 10;
+ for (int i = 0; i < reps; ++i) {
+ result *= factor;
+ }
+ return result;
+ }
+ public int timeMultiplyIntByVariable8(int reps) {
+ int result = 1;
+ int factor = 8;
+ for (int i = 0; i < reps; ++i) {
+ result *= factor;
+ }
+ return result;
+ }
+}
diff --git a/benchmarks/regression/IntConstantRemainderBenchmark.java b/benchmarks/regression/IntConstantRemainderBenchmark.java
new file mode 100644
index 0000000..6174721
--- /dev/null
+++ b/benchmarks/regression/IntConstantRemainderBenchmark.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class IntConstantRemainderBenchmark extends SimpleBenchmark {
+ public int timeRemainderIntByConstant2(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result %= 2;
+ }
+ return result;
+ }
+ public int timeRemainderIntByConstant8(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result %= 8;
+ }
+ return result;
+ }
+/*
+ public int timeRemainderIntByConstant10(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result %= 10;
+ }
+ return result;
+ }
+ public int timeRemainderIntByConstant100(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result %= 100;
+ }
+ return result;
+ }
+*/
+ public int timeRemainderIntByConstant2048(int reps) {
+ int result = 1;
+ for (int i = 0; i < reps; ++i) {
+ result %= 2048;
+ }
+ return result;
+ }
+ public int timeRemainderIntByVariable2(int reps) {
+ int result = 1;
+ int factor = 2;
+ for (int i = 0; i < reps; ++i) {
+ result %= factor;
+ }
+ return result;
+ }
+/*
+ public int timeRemainderIntByVariable10(int reps) {
+ int result = 1;
+ int factor = 10;
+ for (int i = 0; i < reps; ++i) {
+ result %= factor;
+ }
+ return result;
+ }
+*/
+}
diff --git a/benchmarks/regression/IntegerBenchmark.java b/benchmarks/regression/IntegerBenchmark.java
new file mode 100644
index 0000000..601bb5d
--- /dev/null
+++ b/benchmarks/regression/IntegerBenchmark.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class IntegerBenchmark extends SimpleBenchmark {
+ public int timeLongSignumBranch(int reps) {
+ int t = 0;
+ for (int i = 0; i < reps; ++i) {
+ t += signum1(-i);
+ t += signum1(0);
+ t += signum1(i);
+ }
+ return t;
+ }
+
+ public int timeLongSignumBranchFree(int reps) {
+ int t = 0;
+ for (int i = 0; i < reps; ++i) {
+ t += signum2(-i);
+ t += signum2(0);
+ t += signum2(i);
+ }
+ return t;
+ }
+
+ private static int signum1(long v) {
+ return v < 0 ? -1 : (v == 0 ? 0 : 1);
+ }
+
+ private static int signum2(long v) {
+ return ((int)(v >> 63)) | (int) (-v >>> 63); // Hacker's delight 2-7
+ }
+
+ public int timeLongBitCount_BitSet(int reps) {
+ int t = 0;
+ for (int i = 0; i < reps; ++i) {
+ t += pop((long) i);
+ }
+ return t;
+ }
+
+ private static int pop(long l) {
+ int count = popX(l & 0xffffffffL);
+ count += popX(l >>> 32);
+ return count;
+ }
+
+ private static int popX(long x) {
+ // BEGIN android-note
+ // delegate to Integer.bitCount(i); consider using native code
+ // END android-note
+ x = x - ((x >>> 1) & 0x55555555);
+ x = (x & 0x33333333) + ((x >>> 2) & 0x33333333);
+ x = (x + (x >>> 4)) & 0x0f0f0f0f;
+ x = x + (x >>> 8);
+ x = x + (x >>> 16);
+ return (int) x & 0x0000003f;
+ }
+
+ public int timeLongBitCount_2Int(int reps) {
+ int t = 0;
+ for (int i = 0; i < reps; ++i) {
+ t += pop2((long) i);
+ }
+ return t;
+ }
+
+ private static int pop2(long l) {
+ int count = Integer.bitCount((int) (l & 0xffffffffL));
+ count += Integer.bitCount((int) (l >>> 32));
+ return count;
+ }
+
+ public int timeLongBitCount_Long(int reps) {
+ int t = 0;
+ for (int i = 0; i < reps; ++i) {
+ t += Long.bitCount((long) i);
+ }
+ return t;
+ }
+
+ /**
+ * Table for Seal's algorithm for Number of Trailing Zeros. Hacker's Delight
+ * online, Figure 5-18 (http://www.hackersdelight.org/revisions.pdf)
+ * The entries whose value is -1 are never referenced.
+ */
+ private static final byte[] NTZ_TABLE = {
+ 32, 0, 1, 12, 2, 6, -1, 13, 3, -1, 7, -1, -1, -1, -1, 14,
+ 10, 4, -1, -1, 8, -1, -1, 25, -1, -1, -1, -1, -1, 21, 27, 15,
+ 31, 11, 5, -1, -1, -1, -1, -1, 9, -1, -1, 24, -1, -1, 20, 26,
+ 30, -1, -1, -1, -1, 23, -1, 19, 29, -1, 22, 18, 28, 17, 16, -1
+ };
+
+ private static int numberOfTrailingZerosHD(int i) {
+ // Seal's algorithm - Hacker's Delight 5-18
+ i &= -i;
+ i = (i << 4) + i; // x *= 17
+ i = (i << 6) + i; // x *= 65
+ i = (i << 16) - i; // x *= 65535
+ return NTZ_TABLE[i >>> 26];
+ }
+
+ private static int numberOfTrailingZerosOL(int i) {
+ return NTZ_TABLE[((i & -i) * 0x0450FBAF) >>> 26];
+ }
+
+ public int timeNumberOfTrailingZerosHD(int reps) {
+ int t = 0;
+ for (int i = 0; i < reps; ++i) {
+ t += numberOfTrailingZerosHD(i);
+ }
+ return t;
+ }
+
+ public int timeNumberOfTrailingZerosOL(int reps) {
+ int t = 0;
+ for (int i = 0; i < reps; ++i) {
+ t += numberOfTrailingZerosOL(i);
+ }
+ return t;
+ }
+}
diff --git a/benchmarks/regression/IntegralToStringBenchmark.java b/benchmarks/regression/IntegralToStringBenchmark.java
new file mode 100644
index 0000000..cab9e98
--- /dev/null
+++ b/benchmarks/regression/IntegralToStringBenchmark.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class IntegralToStringBenchmark extends SimpleBenchmark {
+
+ private static final int SMALL = 12;
+ private static final int MEDIUM = 12345;
+ private static final int LARGE = 12345678;
+
+ public void time_IntegerToString_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(SMALL);
+ }
+ }
+
+ public void time_IntegerToString_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(MEDIUM);
+ }
+ }
+
+ public void time_IntegerToString_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(LARGE);
+ }
+ }
+
+ public void time_IntegerToString2_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(SMALL, 2);
+ }
+ }
+
+ public void time_IntegerToString2_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(MEDIUM, 2);
+ }
+ }
+
+ public void time_IntegerToString2_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(LARGE, 2);
+ }
+ }
+
+ public void time_IntegerToString10_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(SMALL, 10);
+ }
+ }
+
+ public void time_IntegerToString10_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(MEDIUM, 10);
+ }
+ }
+
+ public void time_IntegerToString10_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(LARGE, 10);
+ }
+ }
+
+ public void time_IntegerToString16_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(SMALL, 16);
+ }
+ }
+
+ public void time_IntegerToString16_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(MEDIUM, 16);
+ }
+ }
+
+ public void time_IntegerToString16_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toString(LARGE, 16);
+ }
+ }
+
+ public void time_IntegerToBinaryString_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toBinaryString(SMALL);
+ }
+ }
+
+ public void time_IntegerToBinaryString_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toBinaryString(MEDIUM);
+ }
+ }
+
+ public void time_IntegerToBinaryString_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toBinaryString(LARGE);
+ }
+ }
+
+ public void time_IntegerToHexString_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toHexString(SMALL);
+ }
+ }
+
+ public void time_IntegerToHexString_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toHexString(MEDIUM);
+ }
+ }
+
+ public void time_IntegerToHexString_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Integer.toHexString(LARGE);
+ }
+ }
+
+ public void time_StringBuilder_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ new StringBuilder().append(SMALL);
+ }
+ }
+
+ public void time_StringBuilder_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ new StringBuilder().append(MEDIUM);
+ }
+ }
+
+ public void time_StringBuilder_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ new StringBuilder().append(LARGE);
+ }
+ }
+
+ public void time_Formatter_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ String.format("%d", SMALL);
+ }
+ }
+
+ public void time_Formatter_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ String.format("%d", MEDIUM);
+ }
+ }
+
+ public void time_Formatter_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ String.format("%d", LARGE);
+ }
+ }
+}
diff --git a/benchmarks/regression/JarFileBenchmark.java b/benchmarks/regression/JarFileBenchmark.java
new file mode 100644
index 0000000..626ca21
--- /dev/null
+++ b/benchmarks/regression/JarFileBenchmark.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.io.File;
+import java.util.jar.*;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class JarFileBenchmark extends SimpleBenchmark {
+ @Param({
+ "/system/framework/bouncycastle.jar",
+ "/system/framework/core.jar",
+ "/system/framework/framework.jar"
+ })
+ private String filename;
+
+ public void time(int reps) throws Exception {
+ File f = new File(filename);
+ for (int i = 0; i < reps; ++i) {
+ JarFile jf = new JarFile(f);
+ Manifest m = jf.getManifest();
+ jf.close();
+ }
+ }
+}
diff --git a/benchmarks/regression/KeyPairGeneratorBenchmark.java b/benchmarks/regression/KeyPairGeneratorBenchmark.java
new file mode 100644
index 0000000..762c935
--- /dev/null
+++ b/benchmarks/regression/KeyPairGeneratorBenchmark.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Map;
+
+public class KeyPairGeneratorBenchmark extends SimpleBenchmark {
+ @Param private Algorithm algorithm;
+
+ public enum Algorithm {
+ RSA,
+ DSA,
+ };
+
+ @Param private Implementation implementation;
+
+ public enum Implementation { OpenSSL, BouncyCastle };
+
+ private String generatorAlgorithm;
+ private KeyPairGenerator generator;
+ private SecureRandom random;
+
+ @Override protected void setUp() throws Exception {
+ this.generatorAlgorithm = algorithm.toString();
+
+ final String provider;
+ if (implementation == Implementation.BouncyCastle) {
+ provider = "BC";
+ } else {
+ provider = "AndroidOpenSSL";
+ }
+
+ this.generator = KeyPairGenerator.getInstance(generatorAlgorithm, provider);
+ this.random = SecureRandom.getInstance("SHA1PRNG");
+ this.generator.initialize(1024);
+ }
+
+ public void time(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ KeyPair keyPair = generator.generateKeyPair();
+ }
+ }
+}
diff --git a/benchmarks/regression/LoopingBackwardsBenchmark.java b/benchmarks/regression/LoopingBackwardsBenchmark.java
new file mode 100644
index 0000000..054eff9
--- /dev/null
+++ b/benchmarks/regression/LoopingBackwardsBenchmark.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Testing the old canard that looping backwards is faster.
+ *
+ * @author Kevin Bourrillion
+ */
+public class LoopingBackwardsBenchmark extends SimpleBenchmark {
+ @Param({"2", "20", "2000", "20000000"}) int max;
+
+ public int timeForwards(int reps) {
+ int dummy = 0;
+ for (int i = 0; i < reps; i++) {
+ for (int j = 0; j < max; j++) {
+ dummy += j;
+ }
+ }
+ return dummy;
+ }
+
+ public int timeBackwards(int reps) {
+ int dummy = 0;
+ for (int i = 0; i < reps; i++) {
+ for (int j = max - 1; j >= 0; j--) {
+ dummy += j;
+ }
+ }
+ return dummy;
+ }
+}
diff --git a/benchmarks/regression/MathBenchmark.java b/benchmarks/regression/MathBenchmark.java
new file mode 100644
index 0000000..25a871d
--- /dev/null
+++ b/benchmarks/regression/MathBenchmark.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Many of these tests are bogus in that the cost will vary wildly depending on inputs.
+ * For _my_ current purposes, that's okay. But beware!
+ */
+public class MathBenchmark extends SimpleBenchmark {
+ private final double d = 1.2;
+ private final float f = 1.2f;
+ private final int i = 1;
+ private final long l = 1L;
+
+ public void timeAbsD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.abs(d);
+ }
+ }
+
+ public void timeAbsF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.abs(f);
+ }
+ }
+
+ public void timeAbsI(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.abs(i);
+ }
+ }
+
+ public void timeAbsL(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.abs(l);
+ }
+ }
+
+ public void timeAcos(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.acos(d);
+ }
+ }
+
+ public void timeAsin(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.asin(d);
+ }
+ }
+
+ public void timeAtan(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.atan(d);
+ }
+ }
+
+ public void timeAtan2(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.atan2(3, 4);
+ }
+ }
+
+ public void timeCbrt(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.cbrt(d);
+ }
+ }
+
+ public void timeCeil(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.ceil(d);
+ }
+ }
+
+ public void timeCopySignD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.copySign(d, d);
+ }
+ }
+
+ public void timeCopySignF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.copySign(f, f);
+ }
+ }
+
+ public void timeCopySignD_strict(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.copySign(d, d);
+ }
+ }
+
+ public void timeCopySignF_strict(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.copySign(f, f);
+ }
+ }
+
+ public void timeCos(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.cos(d);
+ }
+ }
+
+ public void timeCosh(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.cosh(d);
+ }
+ }
+
+ public void timeExp(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.exp(d);
+ }
+ }
+
+ public void timeExpm1(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.expm1(d);
+ }
+ }
+
+ public void timeFloor(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.floor(d);
+ }
+ }
+
+ public void timeGetExponentD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.getExponent(d);
+ }
+ }
+
+ public void timeGetExponentF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.getExponent(f);
+ }
+ }
+
+ public void timeHypot(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.hypot(d, d);
+ }
+ }
+
+ public void timeIEEEremainder(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.IEEEremainder(d, d);
+ }
+ }
+
+ public void timeLog(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.log(d);
+ }
+ }
+
+ public void timeLog10(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.log10(d);
+ }
+ }
+
+ public void timeLog1p(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.log1p(d);
+ }
+ }
+
+ public void timeMaxD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.max(d, d);
+ }
+ }
+
+ public void timeMaxF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.max(f, f);
+ }
+ }
+
+ public void timeMaxI(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.max(i, i);
+ }
+ }
+
+ public void timeMaxL(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.max(l, l);
+ }
+ }
+
+ public void timeMinD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.min(d, d);
+ }
+ }
+
+ public void timeMinF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.min(f, f);
+ }
+ }
+
+ public void timeMinI(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.min(i, i);
+ }
+ }
+
+ public void timeMinL(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.min(l, l);
+ }
+ }
+
+ public void timeNextAfterD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.nextAfter(d, d);
+ }
+ }
+
+ public void timeNextAfterF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.nextAfter(f, f);
+ }
+ }
+
+ public void timeNextUpD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.nextUp(d);
+ }
+ }
+
+ public void timeNextUpF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.nextUp(f);
+ }
+ }
+
+ public void timePow(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.pow(d, d);
+ }
+ }
+
+ public void timeRandom(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.random();
+ }
+ }
+
+ public void timeRint(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.rint(d);
+ }
+ }
+
+ public void timeRoundD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.round(d);
+ }
+ }
+
+ public void timeRoundF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.round(f);
+ }
+ }
+
+ public void timeScalbD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.scalb(d, 5);
+ }
+ }
+
+ public void timeScalbF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.scalb(f, 5);
+ }
+ }
+
+ public void timeSignumD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.signum(d);
+ }
+ }
+
+ public void timeSignumF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.signum(f);
+ }
+ }
+
+ public void timeSin(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.sin(d);
+ }
+ }
+
+ public void timeSinh(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.sinh(d);
+ }
+ }
+
+ public void timeSqrt(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.sqrt(d);
+ }
+ }
+
+ public void timeTan(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.tan(d);
+ }
+ }
+
+ public void timeTanh(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.tanh(d);
+ }
+ }
+
+ public void timeToDegrees(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.toDegrees(d);
+ }
+ }
+
+ public void timeToRadians(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.toRadians(d);
+ }
+ }
+
+ public void timeUlpD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.ulp(d);
+ }
+ }
+
+ public void timeUlpF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Math.ulp(f);
+ }
+ }
+}
diff --git a/benchmarks/regression/MessageDigestBenchmark.java b/benchmarks/regression/MessageDigestBenchmark.java
new file mode 100644
index 0000000..c9d8074
--- /dev/null
+++ b/benchmarks/regression/MessageDigestBenchmark.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.security.MessageDigest;
+
+public class MessageDigestBenchmark extends SimpleBenchmark {
+
+ private static final int DATA_SIZE = 8192;
+ private static final byte[] DATA = new byte[DATA_SIZE];
+ static {
+ for (int i = 0; i < DATA_SIZE; i++) {
+ DATA[i] = (byte)i;
+ }
+ }
+
+ @Param private Algorithm algorithm;
+
+ public enum Algorithm { MD5, SHA1, SHA256, SHA384, SHA512 };
+
+ @Param private Provider provider;
+
+ public enum Provider { AndroidOpenSSL, BC };
+
+ public void time(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ MessageDigest digest = MessageDigest.getInstance(algorithm.toString(),
+ provider.toString());
+ digest.update(DATA, 0, DATA_SIZE);
+ digest.digest();
+ }
+ }
+}
diff --git a/benchmarks/regression/MutableIntBenchmark.java b/benchmarks/regression/MutableIntBenchmark.java
new file mode 100644
index 0000000..ee4f2d9
--- /dev/null
+++ b/benchmarks/regression/MutableIntBenchmark.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public final class MutableIntBenchmark extends SimpleBenchmark {
+
+ enum Kind {
+ ARRAY() {
+ int[] value = new int[1];
+
+ @Override void timeCreate(int reps) {
+ for (int i = 0; i < reps; i++) {
+ value = new int[] { 5 };
+ }
+ }
+ @Override void timeIncrement(int reps) {
+ for (int i = 0; i < reps; i++) {
+ value[0]++;
+ }
+ }
+ @Override int timeGet(int reps) {
+ int sum = 0;
+ for (int i = 0; i < reps; i++) {
+ sum += value[0];
+ }
+ return sum;
+ }
+ },
+ ATOMIC() {
+ AtomicInteger value = new AtomicInteger();
+
+ @Override void timeCreate(int reps) {
+ for (int i = 0; i < reps; i++) {
+ value = new AtomicInteger(5);
+ }
+ }
+ @Override void timeIncrement(int reps) {
+ for (int i = 0; i < reps; i++) {
+ value.incrementAndGet();
+ }
+ }
+ @Override int timeGet(int reps) {
+ int sum = 0;
+ for (int i = 0; i < reps; i++) {
+ sum += value.intValue();
+ }
+ return sum;
+ }
+ };
+
+ abstract void timeCreate(int reps);
+ abstract void timeIncrement(int reps);
+ abstract int timeGet(int reps);
+ }
+
+ @Param Kind kind;
+
+ public void timeCreate(int reps) {
+ kind.timeCreate(reps);
+ }
+
+ public void timeIncrement(int reps) {
+ kind.timeIncrement(reps);
+ }
+
+ public void timeGet(int reps) {
+ kind.timeGet(reps);
+ }
+
+ public static void main(String[] args) {
+ Runner.main(MutableIntBenchmark.class, args);
+ }
+}
diff --git a/benchmarks/regression/NativeMethodBenchmark.java b/benchmarks/regression/NativeMethodBenchmark.java
new file mode 100644
index 0000000..2e482ef
--- /dev/null
+++ b/benchmarks/regression/NativeMethodBenchmark.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+import org.apache.harmony.dalvik.NativeTestTarget;
+
+public class NativeMethodBenchmark extends SimpleBenchmark {
+ public void time_emptyJniStaticSynchronizedMethod0(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ NativeTestTarget.emptyJniStaticSynchronizedMethod0();
+ }
+ }
+
+ public void time_emptyJniSynchronizedMethod0(int reps) throws Exception {
+ NativeTestTarget n = new NativeTestTarget();
+ for (int i = 0; i < reps; ++i) {
+ n.emptyJniSynchronizedMethod0();
+ }
+ }
+
+ public void time_emptyJniStaticMethod0(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ NativeTestTarget.emptyJniStaticMethod0();
+ }
+ }
+
+ public void time_emptyJniMethod0(int reps) throws Exception {
+ NativeTestTarget n = new NativeTestTarget();
+ for (int i = 0; i < reps; ++i) {
+ n.emptyJniMethod0();
+ }
+ }
+
+ public void time_emptyJniStaticMethod6(int reps) throws Exception {
+ int a = -1;
+ int b = 0;
+ for (int i = 0; i < reps; ++i) {
+ NativeTestTarget.emptyJniStaticMethod6(a, b, 1, 2, 3, i);
+ }
+ }
+
+ public void time_emptyJniMethod6(int reps) throws Exception {
+ int a = -1;
+ int b = 0;
+ NativeTestTarget n = new NativeTestTarget();
+ for (int i = 0; i < reps; ++i) {
+ n.emptyJniMethod6(a, b, 1, 2, 3, i);
+ }
+ }
+
+ public void time_emptyJniStaticMethod6L(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ NativeTestTarget.emptyJniStaticMethod6L(null, null, null, null, null, null);
+ }
+ }
+
+ public void time_emptyJniMethod6L(int reps) throws Exception {
+ NativeTestTarget n = new NativeTestTarget();
+ for (int i = 0; i < reps; ++i) {
+ n.emptyJniMethod6L(null, null, null, null, null, null);
+ }
+ }
+
+}
diff --git a/benchmarks/regression/ParseBenchmark.java b/benchmarks/regression/ParseBenchmark.java
new file mode 100644
index 0000000..b44b429
--- /dev/null
+++ b/benchmarks/regression/ParseBenchmark.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParserFactory;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.xml.sax.InputSource;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Measure throughput of various parsers.
+ *
+ * <p>This benchmark requires that ParseBenchmarkData.zip is on the classpath.
+ * That file contains Twitter feed data, which is representative of what
+ * applications will be parsing.
+ */
+public final class ParseBenchmark extends SimpleBenchmark {
+
+ @Param Document document;
+ @Param Api api;
+
+ private enum Document {
+ TWEETS,
+ READER_SHORT,
+ READER_LONG
+ }
+
+ private enum Api {
+ ANDROID_STREAM("json") {
+ @Override Parser newParser() {
+ return new AndroidStreamParser();
+ }
+ },
+ ORG_JSON("json") {
+ @Override Parser newParser() {
+ return new OrgJsonParser();
+ }
+ },
+ XML_PULL("xml") {
+ @Override Parser newParser() {
+ return new GeneralXmlPullParser();
+ }
+ },
+ XML_DOM("xml") {
+ @Override Parser newParser() {
+ return new XmlDomParser();
+ }
+ },
+ XML_SAX("xml") {
+ @Override Parser newParser() {
+ return new XmlSaxParser();
+ }
+ };
+
+ final String extension;
+
+ private Api(String extension) {
+ this.extension = extension;
+ }
+
+ abstract Parser newParser();
+ }
+
+ private String text;
+ private Parser parser;
+
+ @Override protected void setUp() throws Exception {
+ text = resourceToString("/" + document.name() + "." + api.extension);
+ parser = api.newParser();
+ }
+
+ public void timeParse(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ parser.parse(text);
+ }
+ }
+
+ public static void main(String... args) throws Exception {
+ Runner.main(ParseBenchmark.class, args);
+ }
+
+ private static String resourceToString(String path) throws Exception {
+ InputStream in = ParseBenchmark.class.getResourceAsStream(path);
+ if (in == null) {
+ throw new IllegalArgumentException("No such file: " + path);
+ }
+
+ Reader reader = new InputStreamReader(in, "UTF-8");
+ char[] buffer = new char[8192];
+ StringWriter writer = new StringWriter();
+ int count;
+ while ((count = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, count);
+ }
+ reader.close();
+ return writer.toString();
+ }
+
+ interface Parser {
+ void parse(String data) throws Exception;
+ }
+
+ private static class AndroidStreamParser implements Parser {
+ @Override public void parse(String data) throws Exception {
+ android.util.JsonReader jsonReader
+ = new android.util.JsonReader(new StringReader(data));
+ readToken(jsonReader);
+ jsonReader.close();
+ }
+
+ public void readObject(android.util.JsonReader reader) throws IOException {
+ reader.beginObject();
+ while (reader.hasNext()) {
+ reader.nextName();
+ readToken(reader);
+ }
+ reader.endObject();
+ }
+
+ public void readArray(android.util.JsonReader reader) throws IOException {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ readToken(reader);
+ }
+ reader.endArray();
+ }
+
+ private void readToken(android.util.JsonReader reader) throws IOException {
+ switch (reader.peek()) {
+ case BEGIN_ARRAY:
+ readArray(reader);
+ break;
+ case BEGIN_OBJECT:
+ readObject(reader);
+ break;
+ case BOOLEAN:
+ reader.nextBoolean();
+ break;
+ case NULL:
+ reader.nextNull();
+ break;
+ case NUMBER:
+ reader.nextLong();
+ break;
+ case STRING:
+ reader.nextString();
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected token" + reader.peek());
+ }
+ }
+ }
+
+ private static class OrgJsonParser implements Parser {
+ @Override public void parse(String data) throws Exception {
+ if (data.startsWith("[")) {
+ new JSONArray(data);
+ } else if (data.startsWith("{")) {
+ new JSONObject(data);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ private static class GeneralXmlPullParser implements Parser {
+ @Override public void parse(String data) throws Exception {
+ XmlPullParser xmlParser = android.util.Xml.newPullParser();
+ xmlParser.setInput(new StringReader(data));
+ xmlParser.nextTag();
+ while (xmlParser.next() != XmlPullParser.END_DOCUMENT) {
+ xmlParser.getName();
+ xmlParser.getText();
+ }
+ }
+ }
+
+ private static class XmlDomParser implements Parser {
+ @Override public void parse(String data) throws Exception {
+ DocumentBuilderFactory.newInstance().newDocumentBuilder()
+ .parse(new InputSource(new StringReader(data)));
+ }
+ }
+
+ private static class XmlSaxParser implements Parser {
+ @Override public void parse(String data) throws Exception {
+ SAXParserFactory.newInstance().newSAXParser().parse(
+ new InputSource(new StringReader(data)), new DefaultHandler());
+ }
+ }
+}
diff --git a/benchmarks/regression/ParseBenchmarkData.zip b/benchmarks/regression/ParseBenchmarkData.zip
new file mode 100644
index 0000000..7838e8a
--- /dev/null
+++ b/benchmarks/regression/ParseBenchmarkData.zip
Binary files differ
diff --git a/benchmarks/regression/PriorityQueueBenchmark.java b/benchmarks/regression/PriorityQueueBenchmark.java
new file mode 100644
index 0000000..2fe661b
--- /dev/null
+++ b/benchmarks/regression/PriorityQueueBenchmark.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.Random;
+
+public class PriorityQueueBenchmark extends SimpleBenchmark {
+ @Param({"100", "1000", "10000"}) private int queueSize;
+ @Param({"0", "25", "50", "75", "100"}) private int hitRate;
+
+ private PriorityQueue<Integer> pq;
+ private PriorityQueue<Integer> usepq;
+ private List<Integer> seekElements;
+ private Random random = new Random(189279387L);
+
+ @Override protected void setUp() throws Exception {
+ pq = new PriorityQueue<Integer>();
+ usepq = new PriorityQueue<Integer>();
+ seekElements = new ArrayList<Integer>();
+ List<Integer> allElements = new ArrayList<Integer>();
+ int numShared = (int)(queueSize * ((double)hitRate / 100));
+ // the total number of elements we require to engineer a hit rate of hitRate%
+ int totalElements = 2 * queueSize - numShared;
+ for (int i = 0; i < totalElements; i++) {
+ allElements.add(i);
+ }
+ // shuffle these elements so that we get a reasonable distribution of missed elements
+ Collections.shuffle(allElements, random);
+ // add shared elements
+ for (int i = 0; i < numShared; i++) {
+ pq.add(allElements.get(i));
+ seekElements.add(allElements.get(i));
+ }
+ // add priority queue only elements (these won't be touched)
+ for (int i = numShared; i < queueSize; i++) {
+ pq.add(allElements.get(i));
+ }
+ // add non-priority queue elements (these will be misses)
+ for (int i = queueSize; i < totalElements; i++) {
+ seekElements.add(allElements.get(i));
+ }
+ usepq = new PriorityQueue<Integer>(pq);
+ // shuffle again so that elements are accessed in a different pattern than they were
+ // inserted
+ Collections.shuffle(seekElements, random);
+ }
+
+ public boolean timeRemove(int reps) {
+ boolean dummy = false;
+ int elementsSize = seekElements.size();
+ // At most allow the queue to empty 10%.
+ int resizingThreshold = queueSize / 10;
+ for (int i = 0; i < reps; i++) {
+ // Reset queue every so often. This will be called more often for smaller
+ // queueSizes, but since a copy is linear, it will also cost proportionally
+ // less, and hopefully it will approximately balance out.
+ if (i % resizingThreshold == 0) {
+ usepq = new PriorityQueue<Integer>(pq);
+ }
+ dummy = usepq.remove(seekElements.get(i % elementsSize));
+ }
+ return dummy;
+ }
+}
diff --git a/benchmarks/regression/PropertyAccessBenchmark.java b/benchmarks/regression/PropertyAccessBenchmark.java
new file mode 100644
index 0000000..cabd6ed
--- /dev/null
+++ b/benchmarks/regression/PropertyAccessBenchmark.java
@@ -0,0 +1,116 @@
+/*
+ * 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 benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public final class PropertyAccessBenchmark extends SimpleBenchmark {
+ private View view = new View();
+ private Method setX;
+ private GeneratedProperty generatedSetter = new GeneratedSetter();
+ private GeneratedProperty generatedField = new GeneratedField();
+ private Field x;
+ private Object[] argsBox = new Object[1];
+
+ @Override protected void setUp() throws Exception {
+ setX = View.class.getDeclaredMethod("setX", float.class);
+ x = View.class.getDeclaredField("x");
+ }
+
+ public void timeDirectSetter(int reps) {
+ for (int i = 0; i < reps; i++) {
+ view.setX(0.1f);
+ }
+ }
+
+ public void timeDirectFieldSet(int reps) {
+ for (int i = 0; i < reps; i++) {
+ view.x = 0.1f;
+ }
+ }
+
+ public void timeDirectSetterAndBoxing(int reps) {
+ for (int i = 0; i < reps; i++) {
+ Float value = 0.1f;
+ view.setX(value);
+ }
+ }
+
+ public void timeDirectFieldSetAndBoxing(int reps) {
+ for (int i = 0; i < reps; i++) {
+ Float value = 0.1f;
+ view.x = value;
+ }
+ }
+
+ public void timeReflectionSetterAndTwoBoxes(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ setX.invoke(view, 0.1f);
+ }
+ }
+
+ public void timeReflectionSetterAndOneBox(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ argsBox[0] = 0.1f;
+ setX.invoke(view, argsBox);
+ }
+ }
+
+ public void timeReflectionFieldSet(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ x.setFloat(view, 0.1f);
+ }
+ }
+
+ public void timeGeneratedSetter(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ generatedSetter.setFloat(view, 0.1f);
+ }
+ }
+
+ public void timeGeneratedFieldSet(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ generatedField.setFloat(view, 0.1f);
+ }
+ }
+
+ static class View {
+ float x;
+
+ public void setX(float x) {
+ this.x = x;
+ }
+ }
+
+ static interface GeneratedProperty {
+ void setFloat(View v, float f);
+ }
+
+ static class GeneratedSetter implements GeneratedProperty {
+ public void setFloat(View v, float f) {
+ v.setX(f);
+ }
+ }
+
+ static class GeneratedField implements GeneratedProperty {
+ public void setFloat(View v, float f) {
+ v.x = f;
+ }
+ }
+}
diff --git a/benchmarks/regression/RandomBenchmark.java b/benchmarks/regression/RandomBenchmark.java
new file mode 100644
index 0000000..0792805
--- /dev/null
+++ b/benchmarks/regression/RandomBenchmark.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.security.SecureRandom;
+import java.util.Random;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class RandomBenchmark extends SimpleBenchmark {
+ public void timeNewRandom(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ Random rng = new Random();
+ rng.nextInt();
+ }
+ }
+
+ public void timeReusedRandom(int reps) throws Exception {
+ Random rng = new Random();
+ for (int i = 0; i < reps; ++i) {
+ rng.nextInt();
+ }
+ }
+
+ public void timeReusedSecureRandom(int reps) throws Exception {
+ SecureRandom rng = new SecureRandom();
+ for (int i = 0; i < reps; ++i) {
+ rng.nextInt();
+ }
+ }
+
+ public void timeNewSecureRandom(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ SecureRandom rng = new SecureRandom();
+ rng.nextInt();
+ }
+ }
+}
diff --git a/benchmarks/regression/RealToStringBenchmark.java b/benchmarks/regression/RealToStringBenchmark.java
new file mode 100644
index 0000000..8914cf6
--- /dev/null
+++ b/benchmarks/regression/RealToStringBenchmark.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class RealToStringBenchmark extends SimpleBenchmark {
+
+ private static final float SMALL = -123.45f;
+ private static final float MEDIUM = -123.45e8f;
+ private static final float LARGE = -123.45e36f;
+
+ public void timeFloat_toString_NaN(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Float.toString(Float.NaN);
+ }
+ }
+
+ public void timeFloat_toString_NEGATIVE_INFINITY(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Float.toString(Float.NEGATIVE_INFINITY);
+ }
+ }
+
+ public void timeFloat_toString_POSITIVE_INFINITY(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Float.toString(Float.POSITIVE_INFINITY);
+ }
+ }
+
+ public void timeFloat_toString_zero(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Float.toString(0.0f);
+ }
+ }
+
+ public void timeFloat_toString_minusZero(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Float.toString(-0.0f);
+ }
+ }
+
+ public void timeFloat_toString_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Float.toString(SMALL);
+ }
+ }
+
+ public void timeFloat_toString_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Float.toString(MEDIUM);
+ }
+ }
+
+ public void timeFloat_toString_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Float.toString(LARGE);
+ }
+ }
+
+ public void timeStringBuilder_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ new StringBuilder().append(SMALL);
+ }
+ }
+
+ public void timeStringBuilder_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ new StringBuilder().append(MEDIUM);
+ }
+ }
+
+ public void timeStringBuilder_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ new StringBuilder().append(LARGE);
+ }
+ }
+
+ public void timeFormatter_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ String.format("%f", SMALL);
+ }
+ }
+
+ public void timeFormatter_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ String.format("%f", MEDIUM);
+ }
+ }
+
+ public void timeFormatter_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ String.format("%f", LARGE);
+ }
+ }
+
+ public void timeFormatter_dot2f_small(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ String.format("%.2f", SMALL);
+ }
+ }
+
+ public void timeFormatter_dot2f_medium(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ String.format("%.2f", MEDIUM);
+ }
+ }
+
+ public void timeFormatter_dot2f_large(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ String.format("%.2f", LARGE);
+ }
+ }
+}
diff --git a/benchmarks/regression/ReflectionBenchmark.java b/benchmarks/regression/ReflectionBenchmark.java
new file mode 100644
index 0000000..fe00fa2
--- /dev/null
+++ b/benchmarks/regression/ReflectionBenchmark.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.lang.reflect.*;
+
+public class ReflectionBenchmark extends SimpleBenchmark {
+ public void timeObject_getClass(int reps) throws Exception {
+ C c = new C();
+ for (int rep = 0; rep < reps; ++rep) {
+ c.getClass();
+ }
+ }
+
+ public void timeClass_getField(int reps) throws Exception {
+ Class<?> klass = C.class;
+ for (int rep = 0; rep < reps; ++rep) {
+ klass.getField("f");
+ }
+ }
+
+ public void timeClass_getDeclaredField(int reps) throws Exception {
+ Class<?> klass = C.class;
+ for (int rep = 0; rep < reps; ++rep) {
+ klass.getDeclaredField("f");
+ }
+ }
+
+ public void timeClass_getConstructor(int reps) throws Exception {
+ Class<?> klass = C.class;
+ for (int rep = 0; rep < reps; ++rep) {
+ klass.getConstructor();
+ }
+ }
+
+ public void timeClass_newInstance(int reps) throws Exception {
+ Class<?> klass = C.class;
+ Constructor constructor = klass.getConstructor();
+ for (int rep = 0; rep < reps; ++rep) {
+ constructor.newInstance();
+ }
+ }
+
+ public void timeClass_getMethod(int reps) throws Exception {
+ Class<?> klass = C.class;
+ for (int rep = 0; rep < reps; ++rep) {
+ klass.getMethod("m");
+ }
+ }
+
+ public void timeClass_getDeclaredMethod(int reps) throws Exception {
+ Class<?> klass = C.class;
+ for (int rep = 0; rep < reps; ++rep) {
+ klass.getDeclaredMethod("m");
+ }
+ }
+
+ public void timeField_setInt(int reps) throws Exception {
+ Class<?> klass = C.class;
+ Field f = klass.getDeclaredField("f");
+ C instance = new C();
+ for (int rep = 0; rep < reps; ++rep) {
+ f.setInt(instance, 1);
+ }
+ }
+
+ public void timeField_getInt(int reps) throws Exception {
+ Class<?> klass = C.class;
+ Field f = klass.getDeclaredField("f");
+ C instance = new C();
+ for (int rep = 0; rep < reps; ++rep) {
+ f.getInt(instance);
+ }
+ }
+
+ public void timeMethod_invokeV(int reps) throws Exception {
+ Class<?> klass = C.class;
+ Method m = klass.getDeclaredMethod("m");
+ C instance = new C();
+ for (int rep = 0; rep < reps; ++rep) {
+ m.invoke(instance);
+ }
+ }
+
+ public void timeMethod_invokeStaticV(int reps) throws Exception {
+ Class<?> klass = C.class;
+ Method m = klass.getDeclaredMethod("sm");
+ for (int rep = 0; rep < reps; ++rep) {
+ m.invoke(null);
+ }
+ }
+
+ public void timeMethod_invokeI(int reps) throws Exception {
+ Class<?> klass = C.class;
+ Method m = klass.getDeclaredMethod("setField", int.class);
+ C instance = new C();
+ for (int rep = 0; rep < reps; ++rep) {
+ m.invoke(instance, 1);
+ }
+ }
+
+ public void timeMethod_invokePreBoxedI(int reps) throws Exception {
+ Class<?> klass = C.class;
+ Method m = klass.getDeclaredMethod("setField", int.class);
+ C instance = new C();
+ Integer one = Integer.valueOf(1);
+ for (int rep = 0; rep < reps; ++rep) {
+ m.invoke(instance, one);
+ }
+ }
+
+ public void timeMethod_invokeStaticI(int reps) throws Exception {
+ Class<?> klass = C.class;
+ Method m = klass.getDeclaredMethod("setStaticField", int.class);
+ for (int rep = 0; rep < reps; ++rep) {
+ m.invoke(null, 1);
+ }
+ }
+
+ public void timeMethod_invokeStaticPreBoxedI(int reps) throws Exception {
+ Class<?> klass = C.class;
+ Method m = klass.getDeclaredMethod("setStaticField", int.class);
+ Integer one = Integer.valueOf(1);
+ for (int rep = 0; rep < reps; ++rep) {
+ m.invoke(null, one);
+ }
+ }
+
+ public void timeRegularMethodInvocation(int reps) throws Exception {
+ C instance = new C();
+ for (int rep = 0; rep < reps; ++rep) {
+ instance.setField(1);
+ }
+ }
+
+ public void timeRegularConstructor(int reps) throws Exception {
+ for (int rep = 0; rep < reps; ++rep) {
+ new C();
+ }
+ }
+
+ public static class C {
+ public static int sf = 0;
+ public int f = 0;
+
+ public C() {
+ // A non-empty constructor so we don't get optimized away.
+ f = 1;
+ }
+
+ public void m() {
+ }
+
+ public static void sm() {
+ }
+
+ public void setField(int value) {
+ f = value;
+ }
+
+ public static void setStaticField(int value) {
+ sf = value;
+ }
+ }
+}
diff --git a/benchmarks/regression/SSLSocketBenchmark.java b/benchmarks/regression/SSLSocketBenchmark.java
new file mode 100644
index 0000000..fd72a79
--- /dev/null
+++ b/benchmarks/regression/SSLSocketBenchmark.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URL;
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+
+public class SSLSocketBenchmark extends SimpleBenchmark {
+
+ private static final int BUFFER_SIZE = 8192;
+
+ final byte[] buffer = new byte[BUFFER_SIZE];
+
+ @Param private WebSite webSite;
+
+ public enum WebSite {
+ DOCS("https://docs.google.com"),
+ MAIL("https://mail.google.com"),
+ SITES("https://sites.google.com"),
+ WWW("https://www.google.com");
+ final InetAddress host;
+ final int port;
+ final byte[] request;
+ WebSite(String uri) {
+ try {
+ URL url = new URL(uri);
+
+ this.host = InetAddress.getByName(url.getHost());
+
+ int p = url.getPort();
+ String portString;
+ if (p == -1) {
+ this.port = 443;
+ portString = "";
+ } else {
+ this.port = p;
+ portString = ":" + port;
+ }
+
+ this.request = ("GET " + uri + " HTTP/1.0\r\n"
+ + "Host: " + host + portString + "\r\n"
+ +"\r\n").getBytes();
+
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Param private Implementation implementation;
+
+ public enum Implementation { OPENSSL, HARMONY };
+
+ private SocketFactory sf;
+
+ @Override protected void setUp() throws Exception {
+ SSLContext sslContext;
+ switch (implementation) {
+ case OPENSSL:
+ sslContext = SSLContext.getInstance("SSL", "AndroidOpenSSL");
+ break;
+ case HARMONY:
+ sslContext = SSLContext.getInstance("SSL", "HarmonyJSSE");
+ break;
+ default:
+ throw new RuntimeException(implementation.toString());
+ }
+ sslContext.init(null, null, null);
+ this.sf = sslContext.getSocketFactory();
+ }
+
+ public void time(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ Socket s = sf.createSocket(webSite.host, webSite.port);
+ OutputStream out = s.getOutputStream();
+ out.write(webSite.request);
+ InputStream in = s.getInputStream();
+ while (true) {
+ int n = in.read(buffer);
+ if (n == -1) {
+ break;
+ }
+ }
+ in.close();
+ }
+ }
+}
diff --git a/benchmarks/regression/SchemePrefixBenchmark.java b/benchmarks/regression/SchemePrefixBenchmark.java
new file mode 100644
index 0000000..d90fe29
--- /dev/null
+++ b/benchmarks/regression/SchemePrefixBenchmark.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class SchemePrefixBenchmark extends SimpleBenchmark {
+
+ enum Strategy {
+ JAVA() {
+ @Override String execute(String spec) {
+ int colon = spec.indexOf(':');
+
+ if (colon < 1) {
+ return null;
+ }
+
+ for (int i = 0; i < colon; i++) {
+ char c = spec.charAt(i);
+ if (!isValidSchemeChar(i, c)) {
+ return null;
+ }
+ }
+
+ return spec.substring(0, colon).toLowerCase(Locale.US);
+ }
+
+ private boolean isValidSchemeChar(int index, char c) {
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ return true;
+ }
+ if (index > 0 && ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')) {
+ return true;
+ }
+ return false;
+ }
+ },
+
+ REGEX() {
+ private final Pattern pattern = Pattern.compile("^([a-zA-Z][a-zA-Z0-9+\\-.]*):");
+
+ @Override String execute(String spec) {
+ Matcher matcher = pattern.matcher(spec);
+ if (matcher.find()) {
+ return matcher.group(1).toLowerCase(Locale.US);
+ } else {
+ return null;
+ }
+ }
+ };
+
+
+ abstract String execute(String spec);
+ }
+
+ @Param Strategy strategy;
+
+ public void timeSchemePrefix(int reps) {
+ for (int i = 0; i < reps; i++) {
+ strategy.execute("http://android.com");
+ }
+ }
+}
diff --git a/benchmarks/regression/SerializationBenchmark.java b/benchmarks/regression/SerializationBenchmark.java
new file mode 100644
index 0000000..bc958eb
--- /dev/null
+++ b/benchmarks/regression/SerializationBenchmark.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+public class SerializationBenchmark extends SimpleBenchmark {
+ private static byte[] bytes(Object o) throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+ ObjectOutputStream out = new ObjectOutputStream(baos);
+ out.writeObject(o);
+ out.close();
+ return baos.toByteArray();
+ }
+
+ public void timeReadIntArray(int reps) throws Exception {
+ int[] intArray = new int[256];
+ readSingleObject(reps, intArray);
+ }
+
+ public void timeWriteIntArray(int reps) throws Exception {
+ int[] intArray = new int[256];
+ writeSingleObject(reps, intArray);
+ }
+ public void timeReadArrayListInteger(int reps) throws Exception {
+ ArrayList<Integer> object = new ArrayList<Integer>();
+ for (int i = 0; i < 256; ++i) {
+ object.add(i);
+ }
+ readSingleObject(reps, object);
+ }
+
+ public void timeWriteArrayListInteger(int reps) throws Exception {
+ ArrayList<Integer> object = new ArrayList<Integer>();
+ for (int i = 0; i < 256; ++i) {
+ object.add(i);
+ }
+ writeSingleObject(reps, object);
+ }
+
+ public void timeReadString(int reps) throws Exception {
+ readSingleObject(reps, "hello");
+ }
+
+ public void timeReadObjectStreamClass(int reps) throws Exception {
+ // A special case because serialization itself requires this class.
+ // (This should really be a unit test.)
+ ObjectStreamClass osc = ObjectStreamClass.lookup(String.class);
+ readSingleObject(reps, osc);
+ }
+
+ public void timeWriteString(int reps) throws Exception {
+ // String is a special case that avoids JNI.
+ writeSingleObject(reps, "hello");
+ }
+
+ public void timeWriteObjectStreamClass(int reps) throws Exception {
+ // A special case because serialization itself requires this class.
+ // (This should really be a unit test.)
+ ObjectStreamClass osc = ObjectStreamClass.lookup(String.class);
+ writeSingleObject(reps, osc);
+ }
+
+ // This is a baseline for the others.
+ public void timeWriteNoObjects(int reps) throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+ ObjectOutputStream out = new ObjectOutputStream(baos);
+ for (int rep = 0; rep < reps; ++rep) {
+ out.reset();
+ baos.reset();
+ }
+ out.close();
+ }
+
+ private void readSingleObject(int reps, Object object) throws Exception {
+ byte[] bytes = bytes(object);
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ for (int rep = 0; rep < reps; ++rep) {
+ ObjectInputStream in = new ObjectInputStream(bais);
+ in.readObject();
+ in.close();
+ bais.reset();
+ }
+ }
+
+ private void writeSingleObject(int reps, Object o) throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+ ObjectOutputStream out = new ObjectOutputStream(baos);
+ for (int rep = 0; rep < reps; ++rep) {
+ out.writeObject(o);
+ out.reset();
+ baos.reset();
+ }
+ out.close();
+ }
+
+ public void timeWriteEveryKindOfField(int reps) throws Exception {
+ writeSingleObject(reps, new LittleBitOfEverything());
+ }
+ public void timeWriteSerializableBoolean(int reps) throws Exception {
+ writeSingleObject(reps, new SerializableBoolean());
+ }
+ public void timeWriteSerializableByte(int reps) throws Exception {
+ writeSingleObject(reps, new SerializableByte());
+ }
+ public void timeWriteSerializableChar(int reps) throws Exception {
+ writeSingleObject(reps, new SerializableChar());
+ }
+ public void timeWriteSerializableDouble(int reps) throws Exception {
+ writeSingleObject(reps, new SerializableDouble());
+ }
+ public void timeWriteSerializableFloat(int reps) throws Exception {
+ writeSingleObject(reps, new SerializableFloat());
+ }
+ public void timeWriteSerializableInt(int reps) throws Exception {
+ writeSingleObject(reps, new SerializableInt());
+ }
+ public void timeWriteSerializableLong(int reps) throws Exception {
+ writeSingleObject(reps, new SerializableLong());
+ }
+ public void timeWriteSerializableShort(int reps) throws Exception {
+ writeSingleObject(reps, new SerializableShort());
+ }
+ public void timeWriteSerializableReference(int reps) throws Exception {
+ writeSingleObject(reps, new SerializableReference());
+ }
+
+ public void timeReadEveryKindOfField(int reps) throws Exception {
+ readSingleObject(reps, new LittleBitOfEverything());
+ }
+ public void timeReadSerializableBoolean(int reps) throws Exception {
+ readSingleObject(reps, new SerializableBoolean());
+ }
+ public void timeReadSerializableByte(int reps) throws Exception {
+ readSingleObject(reps, new SerializableByte());
+ }
+ public void timeReadSerializableChar(int reps) throws Exception {
+ readSingleObject(reps, new SerializableChar());
+ }
+ public void timeReadSerializableDouble(int reps) throws Exception {
+ readSingleObject(reps, new SerializableDouble());
+ }
+ public void timeReadSerializableFloat(int reps) throws Exception {
+ readSingleObject(reps, new SerializableFloat());
+ }
+ public void timeReadSerializableInt(int reps) throws Exception {
+ readSingleObject(reps, new SerializableInt());
+ }
+ public void timeReadSerializableLong(int reps) throws Exception {
+ readSingleObject(reps, new SerializableLong());
+ }
+ public void timeReadSerializableShort(int reps) throws Exception {
+ readSingleObject(reps, new SerializableShort());
+ }
+ public void timeReadSerializableReference(int reps) throws Exception {
+ readSingleObject(reps, new SerializableReference());
+ }
+
+ public static class SerializableBoolean implements Serializable {
+ boolean z;
+ }
+ public static class SerializableByte implements Serializable {
+ byte b;
+ }
+ public static class SerializableChar implements Serializable {
+ char c;
+ }
+ public static class SerializableDouble implements Serializable {
+ double d;
+ }
+ public static class SerializableFloat implements Serializable {
+ float f;
+ }
+ public static class SerializableInt implements Serializable {
+ int i;
+ }
+ public static class SerializableLong implements Serializable {
+ long j;
+ }
+ public static class SerializableShort implements Serializable {
+ short s;
+ }
+ public static class SerializableReference implements Serializable {
+ Object l;
+ }
+
+ public static class LittleBitOfEverything implements Serializable {
+ boolean z;
+ byte b;
+ char c;
+ double d;
+ float f;
+ int i;
+ long j;
+ short s;
+ Object l;
+ }
+}
diff --git a/benchmarks/regression/SignatureBenchmark.java b/benchmarks/regression/SignatureBenchmark.java
new file mode 100644
index 0000000..383dcfd
--- /dev/null
+++ b/benchmarks/regression/SignatureBenchmark.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.harmony.xnet.provider.jsse.OpenSSLSignature;
+
+/**
+ * Tests RSA and DSA signature creation and verification.
+ */
+public class SignatureBenchmark extends SimpleBenchmark {
+
+ private static final int DATA_SIZE = 8192;
+ private static final byte[] DATA = new byte[DATA_SIZE];
+ static {
+ for (int i = 0; i < DATA_SIZE; i++) {
+ DATA[i] = (byte)i;
+ }
+ }
+ @Param private Algorithm algorithm;
+
+ public enum Algorithm {
+ MD5WithRSA,
+ SHA1WithRSA,
+ SHA256WithRSA,
+ SHA384WithRSA,
+ SHA512WithRSA,
+ SHA1withDSA
+ };
+
+ @Param private Implementation implementation;
+
+ public enum Implementation { OpenSSL, BouncyCastle };
+
+ // Key generation and signing aren't part of the benchmark for verification
+ // so cache the results
+ private static Map<String,KeyPair> KEY_PAIRS = new HashMap<String,KeyPair>();
+ private static Map<String,byte[]> SIGNATURES = new HashMap<String,byte[]>();
+
+ private String signatureAlgorithm;
+ private byte[] signature;
+ private PrivateKey privateKey;
+ private PublicKey publicKey;
+
+ @Override protected void setUp() throws Exception {
+ this.signatureAlgorithm = algorithm.toString();
+
+ String keyAlgorithm = signatureAlgorithm.substring(signatureAlgorithm.length() - 3 ,
+ signatureAlgorithm.length());
+ KeyPair keyPair = KEY_PAIRS.get(keyAlgorithm);
+ if (keyPair == null) {
+ KeyPairGenerator generator = KeyPairGenerator.getInstance(keyAlgorithm);
+ keyPair = generator.generateKeyPair();
+ KEY_PAIRS.put(keyAlgorithm, keyPair);
+ }
+ this.privateKey = keyPair.getPrivate();
+ this.publicKey = keyPair.getPublic();
+
+ this.signature = SIGNATURES.get(signatureAlgorithm);
+ if (this.signature == null) {
+ Signature signer = Signature.getInstance(signatureAlgorithm);
+ signer.initSign(keyPair.getPrivate());
+ signer.update(DATA);
+ this.signature = signer.sign();
+ SIGNATURES.put(signatureAlgorithm, signature);
+ }
+ }
+
+ public void timeSign(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ Signature signer;
+ switch (implementation) {
+ case OpenSSL:
+ signer = Signature.getInstance(signatureAlgorithm, "AndroidOpenSSL");
+ break;
+ case BouncyCastle:
+ signer = Signature.getInstance(signatureAlgorithm, "BC");
+ break;
+ default:
+ throw new RuntimeException(implementation.toString());
+ }
+ signer.initSign(privateKey);
+ signer.update(DATA);
+ signer.sign();
+ }
+ }
+
+ public void timeVerify(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ Signature verifier;
+ switch (implementation) {
+ case OpenSSL:
+ verifier = Signature.getInstance(signatureAlgorithm, "AndroidOpenSSL");
+ break;
+ case BouncyCastle:
+ verifier = Signature.getInstance(signatureAlgorithm, "BC");
+ break;
+ default:
+ throw new RuntimeException(implementation.toString());
+ }
+ verifier.initVerify(publicKey);
+ verifier.update(DATA);
+ verifier.verify(signature);
+ }
+ }
+}
diff --git a/benchmarks/regression/StrictMathBenchmark.java b/benchmarks/regression/StrictMathBenchmark.java
new file mode 100644
index 0000000..44c030a
--- /dev/null
+++ b/benchmarks/regression/StrictMathBenchmark.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Many of these tests are bogus in that the cost will vary wildly depending on inputs.
+ * For _my_ current purposes, that's okay. But beware!
+ */
+public class StrictMathBenchmark extends SimpleBenchmark {
+ private final double d = 1.2;
+ private final float f = 1.2f;
+ private final int i = 1;
+ private final long l = 1L;
+
+ public void timeAbsD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.abs(d);
+ }
+ }
+
+ public void timeAbsF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.abs(f);
+ }
+ }
+
+ public void timeAbsI(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.abs(i);
+ }
+ }
+
+ public void timeAbsL(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.abs(l);
+ }
+ }
+
+ public void timeAcos(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.acos(d);
+ }
+ }
+
+ public void timeAsin(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.asin(d);
+ }
+ }
+
+ public void timeAtan(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.atan(d);
+ }
+ }
+
+ public void timeAtan2(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.atan2(3, 4);
+ }
+ }
+
+ public void timeCbrt(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.cbrt(d);
+ }
+ }
+
+ public void timeCeil(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.ceil(d);
+ }
+ }
+
+ public void timeCopySignD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.copySign(d, d);
+ }
+ }
+
+ public void timeCopySignF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.copySign(f, f);
+ }
+ }
+
+ public void timeCos(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.cos(d);
+ }
+ }
+
+ public void timeCosh(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.cosh(d);
+ }
+ }
+
+ public void timeExp(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.exp(d);
+ }
+ }
+
+ public void timeExpm1(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.expm1(d);
+ }
+ }
+
+ public void timeFloor(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.floor(d);
+ }
+ }
+
+ public void timeGetExponentD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.getExponent(d);
+ }
+ }
+
+ public void timeGetExponentF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.getExponent(f);
+ }
+ }
+
+ public void timeHypot(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.hypot(d, d);
+ }
+ }
+
+ public void timeIEEEremainder(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.IEEEremainder(d, d);
+ }
+ }
+
+ public void timeLog(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.log(d);
+ }
+ }
+
+ public void timeLog10(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.log10(d);
+ }
+ }
+
+ public void timeLog1p(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.log1p(d);
+ }
+ }
+
+ public void timeMaxD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.max(d, d);
+ }
+ }
+
+ public void timeMaxF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.max(f, f);
+ }
+ }
+
+ public void timeMaxI(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.max(i, i);
+ }
+ }
+
+ public void timeMaxL(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.max(l, l);
+ }
+ }
+
+ public void timeMinD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.min(d, d);
+ }
+ }
+
+ public void timeMinF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.min(f, f);
+ }
+ }
+
+ public void timeMinI(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.min(i, i);
+ }
+ }
+
+ public void timeMinL(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.min(l, l);
+ }
+ }
+
+ public void timeNextAfterD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.nextAfter(d, d);
+ }
+ }
+
+ public void timeNextAfterF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.nextAfter(f, f);
+ }
+ }
+
+ public void timeNextUpD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.nextUp(d);
+ }
+ }
+
+ public void timeNextUpF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.nextUp(f);
+ }
+ }
+
+ public void timePow(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.pow(d, d);
+ }
+ }
+
+ public void timeRandom(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.random();
+ }
+ }
+
+ public void timeRint(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.rint(d);
+ }
+ }
+
+ public void timeRoundD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.round(d);
+ }
+ }
+
+ public void timeRoundF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.round(f);
+ }
+ }
+
+ public void timeScalbD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.scalb(d, 5);
+ }
+ }
+
+ public void timeScalbF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.scalb(f, 5);
+ }
+ }
+
+ public void timeSignumD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.signum(d);
+ }
+ }
+
+ public void timeSignumF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.signum(f);
+ }
+ }
+
+ public void timeSin(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.sin(d);
+ }
+ }
+
+ public void timeSinh(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.sinh(d);
+ }
+ }
+
+ public void timeSqrt(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.sqrt(d);
+ }
+ }
+
+ public void timeTan(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.tan(d);
+ }
+ }
+
+ public void timeTanh(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.tanh(d);
+ }
+ }
+
+ public void timeToDegrees(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.toDegrees(d);
+ }
+ }
+
+ public void timeToRadians(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.toRadians(d);
+ }
+ }
+
+ public void timeUlpD(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.ulp(d);
+ }
+ }
+
+ public void timeUlpF(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ StrictMath.ulp(f);
+ }
+ }
+}
diff --git a/benchmarks/regression/StringBenchmark.java b/benchmarks/regression/StringBenchmark.java
new file mode 100644
index 0000000..e09ee8b
--- /dev/null
+++ b/benchmarks/regression/StringBenchmark.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class StringBenchmark extends SimpleBenchmark {
+ enum StringLengths {
+ EMPTY(""),
+ SHORT("short"),
+ EIGHTY(makeString(80)),
+ EIGHT_KI(makeString(8192));
+ final String value;
+ private StringLengths(String value) { this.value = value; }
+ }
+ @Param private StringLengths s;
+
+ private static String makeString(int length) {
+ StringBuilder result = new StringBuilder(length);
+ for (int i = 0; i < length; ++i) {
+ result.append((char) i);
+ }
+ return result.toString();
+ }
+
+ public void timeHashCode(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ s.value.hashCode();
+ }
+ }
+}
diff --git a/benchmarks/regression/StringBuilderBenchmark.java b/benchmarks/regression/StringBuilderBenchmark.java
new file mode 100644
index 0000000..79eff2a
--- /dev/null
+++ b/benchmarks/regression/StringBuilderBenchmark.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+/**
+ * Tests the performance of various StringBuilder methods.
+ */
+public class StringBuilderBenchmark extends SimpleBenchmark {
+
+ @Param({"1", "10", "100"}) private int length;
+
+ public void timeAppendBoolean(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(true);
+ }
+ }
+ }
+
+ public void timeAppendChar(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append('c');
+ }
+ }
+ }
+
+ public void timeAppendCharArray(int reps) {
+ char[] chars = "chars".toCharArray();
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(chars);
+ }
+ }
+ }
+
+ public void timeAppendCharSequence(int reps) {
+ CharSequence cs = "chars";
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(cs);
+ }
+ }
+ }
+
+ public void timeAppendDouble(int reps) {
+ double d = 1.2;
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(d);
+ }
+ }
+ }
+
+ public void timeAppendFloat(int reps) {
+ float f = 1.2f;
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(f);
+ }
+ }
+ }
+
+ public void timeAppendInt(int reps) {
+ int n = 123;
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(n);
+ }
+ }
+ }
+
+ public void timeAppendLong(int reps) {
+ long l = 123;
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(l);
+ }
+ }
+ }
+
+ public void timeAppendObject(int reps) {
+ // We don't want to time the toString, so ensure we're calling a trivial one...
+ Object o = new Object() {
+ @Override public String toString() {
+ return "constant";
+ }
+ };
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(o);
+ }
+ }
+ }
+
+ public void timeAppendString(int reps) {
+ String s = "chars";
+ for (int i = 0; i < reps; ++i) {
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < length; ++j) {
+ sb.append(s);
+ }
+ }
+ }
+}
diff --git a/benchmarks/regression/StringCaseMappingBenchmark.java b/benchmarks/regression/StringCaseMappingBenchmark.java
new file mode 100644
index 0000000..ba5b59e
--- /dev/null
+++ b/benchmarks/regression/StringCaseMappingBenchmark.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import java.util.Locale;
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class StringCaseMappingBenchmark extends SimpleBenchmark {
+ enum Inputs {
+ EMPTY(""),
+
+ // TODO: include hairy inputs like turkish and greek.
+ // TODO: locale makes a difference too.
+
+ LOWER2(lower(2)),
+ UPPER2(upper(2)),
+ MIXED2(mixed(2)),
+
+ LOWER8(lower(8)),
+ UPPER8(upper(8)),
+ MIXED8(mixed(8)),
+
+ LOWER32(lower(32)),
+ UPPER32(upper(32)),
+ MIXED32(mixed(32)),
+
+ LOWER512(lower(512)),
+ UPPER512(upper(512)),
+ MIXED512(mixed(512)),
+
+ LOWER2048(lower(2048)),
+ UPPER2048(upper(2048)),
+ MIXED2048(mixed(2048)),
+
+ LOWER_1M(lower(1024*1024)),
+ UPPER_1M(upper(1024*1024)),
+ MIXED_1M(mixed(1024*1024));
+
+ final String value;
+ private Inputs(String value) { this.value = value; }
+ private static String lower(int length) {
+ return makeString(length, "a0b1c2d3e4f5g6h7i8j9klmnopqrstuvwxyz");
+ }
+ private static String upper(int length) {
+ return makeString(length, "A0B1C2D3E4F5G6H7I8J9KLMNOPQRSTUVWXYZ");
+ }
+ private static String mixed(int length) {
+ return makeString(length, "Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9KkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz");
+ }
+ private static String makeString(int length, String alphabet) {
+ StringBuilder sb = new StringBuilder(length);
+ for (int i = 0; i < length; ++i) {
+ sb.append(alphabet.charAt(i % alphabet.length()));
+ }
+ return sb.toString();
+ }
+ }
+ @Param private Inputs s;
+
+ public void timeToUpperCase_US(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ s.value.toUpperCase(Locale.US);
+ }
+ }
+
+ public void timeToLowerCase_US(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ s.value.toUpperCase(Locale.US);
+ }
+ }
+
+ public void timeToUpperCase_Ascii(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ toUpperCaseAscii(s.value);
+ }
+ }
+
+ public void timeToLowerCase_Ascii(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ toUpperCaseAscii(s.value);
+ }
+ }
+
+ public void timeToUpperCase_ICU(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ libcore.icu.ICU.toUpperCase(s.value, Locale.US.toString());
+ }
+ }
+
+ public void timeToLowerCase_ICU(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ libcore.icu.ICU.toLowerCase(s.value, Locale.US.toString());
+ }
+ }
+
+ public static String toUpperCaseAscii(String s) {
+ for (int i = 0, length = s.length(); i < length; i++) {
+ char c = s.charAt(i);
+ if (c < 'a' || c > 'z') {
+ continue; // fast path avoids allocation
+ }
+
+ // slow path: s contains lower case chars
+ char[] result = s.toCharArray();
+ for (; i < length; i++) {
+ c = result[i];
+ if (c >= 'a' && c <= 'z') {
+ result[i] -= ('a' - 'A');
+ }
+ }
+ return new String(result);
+ }
+ return s;
+ }
+
+ public static String toLowerCaseAscii(String s) {
+ for (int i = 0, length = s.length(); i < length; i++) {
+ char c = s.charAt(i);
+ if (c < 'A' || c > 'Z') {
+ continue; // fast path avoids allocation
+ }
+
+ // slow path: s contains upper case chars
+ char[] result = s.toCharArray();
+ for (; i < length; i++) {
+ c = result[i];
+ if (c >= 'A' && c <= 'Z') {
+ result[i] += ('a' - 'A');
+ }
+ }
+ return new String(result);
+ }
+ return s;
+ }
+}
diff --git a/benchmarks/regression/StringIsEmptyBenchmark.java b/benchmarks/regression/StringIsEmptyBenchmark.java
new file mode 100644
index 0000000..a3be80a
--- /dev/null
+++ b/benchmarks/regression/StringIsEmptyBenchmark.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class StringIsEmptyBenchmark extends SimpleBenchmark {
+ public void timeIsEmpty_NonEmpty(int reps) {
+ boolean result = true;
+ for (int i = 0; i < reps; ++i) {
+ result &= !("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".isEmpty());
+ }
+ if (!result) throw new RuntimeException();
+ }
+
+ public void timeIsEmpty_Empty(int reps) {
+ boolean result = true;
+ for (int i = 0; i < reps; ++i) {
+ result &= ("".isEmpty());
+ }
+ if (!result) throw new RuntimeException();
+ }
+
+ public void timeLengthEqualsZero(int reps) {
+ boolean result = true;
+ for (int i = 0; i < reps; ++i) {
+ result &= !("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".length() == 0);
+ }
+ if (!result) throw new RuntimeException();
+ }
+
+ public void timeEqualsEmpty(int reps) {
+ boolean result = true;
+ for (int i = 0; i < reps; ++i) {
+ result &= !"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".equals("");
+ }
+ if (!result) throw new RuntimeException();
+ }
+}
diff --git a/benchmarks/regression/StringLengthBenchmark.java b/benchmarks/regression/StringLengthBenchmark.java
new file mode 100644
index 0000000..962e395
--- /dev/null
+++ b/benchmarks/regression/StringLengthBenchmark.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class StringLengthBenchmark extends SimpleBenchmark {
+ public void timeLength(int reps) {
+ int length = 0;
+ for (int i = 0; i < reps; ++i) {
+ length = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".length();
+ }
+ if (length != 51) throw new RuntimeException();
+ }
+}
diff --git a/benchmarks/regression/StringSplitBenchmark.java b/benchmarks/regression/StringSplitBenchmark.java
new file mode 100644
index 0000000..37fdf8f
--- /dev/null
+++ b/benchmarks/regression/StringSplitBenchmark.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+import java.util.regex.Pattern;
+
+public class StringSplitBenchmark extends SimpleBenchmark {
+ public void timeStringSplitComma(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ "this,is,a,simple,example".split(",");
+ }
+ }
+
+ public void timeStringSplitLiteralDot(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ "this.is.a.simple.example".split("\\.");
+ }
+ }
+
+ public void timeStringSplitNewline(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ "this\nis\na\nsimple\nexample\n".split("\n");
+ }
+ }
+
+ public void timePatternSplitComma(int reps) {
+ Pattern p = Pattern.compile(",");
+ for (int i = 0; i < reps; ++i) {
+ p.split("this,is,a,simple,example");
+ }
+ }
+
+ public void timePatternSplitLiteralDot(int reps) {
+ Pattern p = Pattern.compile("\\.");
+ for (int i = 0; i < reps; ++i) {
+ p.split("this.is.a.simple.example");
+ }
+ }
+
+ public void timeStringSplitHard(int reps) {
+ for (int i = 0; i < reps; ++i) {
+ "this,is,a,harder,example".split("[,]");
+ }
+ }
+}
diff --git a/benchmarks/regression/StringToRealBenchmark.java b/benchmarks/regression/StringToRealBenchmark.java
new file mode 100644
index 0000000..ef7a633
--- /dev/null
+++ b/benchmarks/regression/StringToRealBenchmark.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class StringToRealBenchmark extends SimpleBenchmark {
+
+ @Param({
+ "NaN",
+ "-1",
+ "0",
+ "1",
+ "1.2",
+ "-123.45",
+ "-123.45e8",
+ "-123.45e36"
+ }) String string;
+
+ public void timeFloat_parseFloat(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Float.parseFloat(string);
+ }
+ }
+
+ public void timeDouble_parseDouble(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ Double.parseDouble(string);
+ }
+ }
+}
diff --git a/benchmarks/regression/SystemPropertiesBenchmark.java b/benchmarks/regression/SystemPropertiesBenchmark.java
new file mode 100644
index 0000000..9c675e1
--- /dev/null
+++ b/benchmarks/regression/SystemPropertiesBenchmark.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+
+public class SystemPropertiesBenchmark extends SimpleBenchmark {
+ public void timeGetProperties(int reps) throws Exception {
+ for (int i = 0; i < reps; ++i) {
+ // Force the properties to be recreated.
+ System.setProperties(null);
+ System.getProperties();
+ }
+ }
+}
diff --git a/benchmarks/regression/ThreadLocalBenchmark.java b/benchmarks/regression/ThreadLocalBenchmark.java
new file mode 100644
index 0000000..df0715b
--- /dev/null
+++ b/benchmarks/regression/ThreadLocalBenchmark.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+public class ThreadLocalBenchmark extends SimpleBenchmark {
+ private static final ThreadLocal<char[]> BUFFER = new ThreadLocal<char[]>() {
+ @Override protected char[] initialValue() {
+ return new char[20];
+ }
+ };
+
+ public void timeThreadLocal_get(int reps) {
+ for (int rep = 0; rep < reps; ++rep) {
+ BUFFER.get();
+ }
+ }
+}
diff --git a/benchmarks/regression/TimeZoneBenchmark.java b/benchmarks/regression/TimeZoneBenchmark.java
new file mode 100644
index 0000000..ccef392
--- /dev/null
+++ b/benchmarks/regression/TimeZoneBenchmark.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+import java.util.TimeZone;
+
+public class TimeZoneBenchmark extends SimpleBenchmark {
+ public void timeTimeZone_getDefault(int reps) throws Exception {
+ for (int rep = 0; rep < reps; ++rep) {
+ TimeZone.getDefault();
+ }
+ }
+
+ public void timeTimeZone_getTimeZoneUTC(int reps) throws Exception {
+ for (int rep = 0; rep < reps; ++rep) {
+ TimeZone.getTimeZone("UTC");
+ }
+ }
+
+ public void timeTimeZone_getTimeZone_default(int reps) throws Exception {
+ String defaultId = TimeZone.getDefault().getID();
+ for (int rep = 0; rep < reps; ++rep) {
+ TimeZone.getTimeZone(defaultId);
+ }
+ }
+
+ // A time zone with relatively few transitions.
+ public void timeTimeZone_getTimeZone_America_Caracas(int reps) throws Exception {
+ for (int rep = 0; rep < reps; ++rep) {
+ TimeZone.getTimeZone("America/Caracas");
+ }
+ }
+
+ // A time zone with a lot of transitions.
+ public void timeTimeZone_getTimeZone_America_Santiago(int reps) throws Exception {
+ for (int rep = 0; rep < reps; ++rep) {
+ TimeZone.getTimeZone("America/Santiago");
+ }
+ }
+
+ public void timeTimeZone_getTimeZone_GMT_plus_10(int reps) throws Exception {
+ for (int rep = 0; rep < reps; ++rep) {
+ TimeZone.getTimeZone("GMT+10");
+ }
+ }
+}
diff --git a/benchmarks/regression/URLConnectionBenchmark.java b/benchmarks/regression/URLConnectionBenchmark.java
new file mode 100644
index 0000000..e5ceec4
--- /dev/null
+++ b/benchmarks/regression/URLConnectionBenchmark.java
@@ -0,0 +1,132 @@
+/*
+ * 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 benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import tests.http.MockResponse;
+import tests.http.MockWebServer;
+
+public final class URLConnectionBenchmark extends SimpleBenchmark {
+
+ @Param({"0", "1024", "1048576"}) private int bodySize;
+ @Param({"2048"}) private int chunkSize;
+ @Param({"1024"}) private int readBufferSize;
+ @Param private ResponseHeaders responseHeaders;
+ @Param private TransferEncoding transferEncoding;
+ private byte[] readBuffer;
+
+ private MockWebServer server;
+ private URL url;
+
+ protected void setUp() throws Exception {
+ readBuffer = new byte[readBufferSize];
+ server = new MockWebServer();
+
+ MockResponse response = new MockResponse();
+ responseHeaders.apply(response);
+ transferEncoding.setBody(response, bodySize, chunkSize);
+ server.enqueue(response);
+
+ server.setContinuousServing(true);
+ server.play();
+
+ url = server.getUrl("/");
+ get(); // ensure the server has started its threads, etc.
+ }
+
+ protected void tearDown() throws Exception {
+ /*
+ * Entice the server to shut itself down gracefully. The shutdown method
+ * doesn't work on Dalvik because socket.close() doesn't release blocked
+ * threads. Instead, read the last continuously-served request, and then
+ * cause the server to close the otherwise-reusable HTTP connection.
+ */
+ server.setContinuousServing(false);
+ server.enqueue(new MockResponse().setDisconnectAtEnd(true));
+ get();
+ get();
+ server.shutdown();
+ }
+
+ public int timeGet(int reps) throws IOException {
+ int totalBytesRead = 0;
+ for (int i = 0; i < reps; i++) {
+ totalBytesRead += get();
+ }
+ return totalBytesRead;
+ }
+
+ private int get() throws IOException {
+ int totalBytesRead = 0;
+ URLConnection connection = url.openConnection();
+ InputStream in = connection.getInputStream();
+ int count;
+ while ((count = in.read(readBuffer)) != -1) {
+ totalBytesRead += count;
+ }
+ return totalBytesRead;
+ }
+
+ enum TransferEncoding {
+ FIXED_LENGTH,
+ CHUNKED;
+
+ void setBody(MockResponse response, int bodySize, int chunkSize) throws IOException {
+ if (this == TransferEncoding.FIXED_LENGTH) {
+ response.setBody(new byte[bodySize]);
+ } else if (this == TransferEncoding.CHUNKED) {
+ response.setChunkedBody(new byte[bodySize], chunkSize);
+ }
+ }
+ }
+
+ enum ResponseHeaders {
+ MINIMAL,
+ TYPICAL;
+
+ void apply(MockResponse response) {
+ if (this == TYPICAL) {
+ /* from http://api.twitter.com/1/statuses/public_timeline.json */
+ response.addHeader("Date: Wed, 30 Jun 2010 17:57:39 GMT");
+ response.addHeader("Server: hi");
+ response.addHeader("X-RateLimit-Remaining: 0");
+ response.addHeader("X-Runtime: 0.01637");
+ response.addHeader("Content-Type: application/json; charset=utf-8");
+ response.addHeader("X-RateLimit-Class: api_whitelisted");
+ response.addHeader("Cache-Control: no-cache, max-age=300");
+ response.addHeader("X-RateLimit-Reset: 1277920980");
+ response.addHeader("Set-Cookie: _twitter_sess=BAh7EDoOcmV0dXJuX3RvIjZodHRwOi8vZGV2L"
+ + "nR3aXR0ZXIuY29tL3BhZ2Vz%250AL3NpZ25faW5fd2l0aF90d2l0dGVyOgxjc3JmX2lkIiUw"
+ + "ODFhNGY2NTM5NjRm%250ANjY1N2M2NzcwNWI0MDlmZGZjZjoVaW5fbmV3X3VzZXJfZmxvdzA"
+ + "6EXRyYW5z%250AX3Byb21wdDAiKXNob3dfZGlzY292ZXJhYmlsaXR5X2Zvcl9qZXNzZXdpbH"
+ + "Nv%250AbjA6E3Nob3dfaGVscF9saW5rMDoTcGFzc3dvcmRfdG9rZW4iLWUyYjlhNmM3%250A"
+ + "MWJiNzI3NWNlZDI1NDY3MGMzZWNmMTE0MjI4N2EyNGE6D2NyZWF0ZWRfYXRs%250AKwhiM%2"
+ + "52F6JKQE6CXVzZXJpA8tE3iIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxl%250Acjo6Rmxhc2"
+ + "g6OkZsYXNoSGFzaHsABjoKQHVzZWR7ADoHaWQiJWZmMTNhM2Qx%250AZTU1YTkzMmYyMWM0M"
+ + "GNhZjU4NDVjMTQz--11250628c85830219438eb7eba96a541a9af4098; domain=.twitt"
+ + "er.com; path=/");
+ response.addHeader("Expires: Wed, 30 Jun 2010 18:02:39 GMT");
+ response.addHeader("Vary: Accept-Encoding");
+ }
+ }
+ }
+}
diff --git a/benchmarks/regression/XmlEntitiesBenchmark.java b/benchmarks/regression/XmlEntitiesBenchmark.java
new file mode 100644
index 0000000..4c5cc18
--- /dev/null
+++ b/benchmarks/regression/XmlEntitiesBenchmark.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import java.io.StringReader;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.xml.sax.InputSource;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+// http://code.google.com/p/android/issues/detail?id=18102
+public final class XmlEntitiesBenchmark extends SimpleBenchmark {
+
+ @Param({"10", "100", "1000"}) int length;
+ @Param({"0", "0.5", "1.0"}) float entityFraction;
+
+ private XmlPullParserFactory xmlPullParserFactory;
+ private DocumentBuilderFactory documentBuilderFactory;
+
+ /** a string like {@code <doc>&&++</doc>}. */
+ private String xml;
+
+ @Override protected void setUp() throws Exception {
+ xmlPullParserFactory = XmlPullParserFactory.newInstance();
+ documentBuilderFactory = DocumentBuilderFactory.newInstance();
+
+ StringBuilder xmlBuilder = new StringBuilder();
+ xmlBuilder.append("<doc>");
+ for (int i = 0; i < (length * entityFraction); i++) {
+ xmlBuilder.append("&");
+ }
+ while (xmlBuilder.length() < length) {
+ xmlBuilder.append("+");
+ }
+ xmlBuilder.append("</doc>");
+ xml = xmlBuilder.toString();
+ }
+
+ public void timeXmlParser(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ XmlPullParser parser = xmlPullParserFactory.newPullParser();
+ parser.setInput(new StringReader(xml));
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ }
+ }
+ }
+
+ public void timeDocumentBuilder(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ documentBuilder.parse(new InputSource(new StringReader(xml)));
+ }
+ }
+}
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 — typically referred
+ * to as a "class path" — 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 <= 0.0 or >= 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 <command>" 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 im