android 13 from xiaosuan

This commit is contained in:
cpeng 2025-08-25 08:21:32 +08:00
commit fb5520955d
9228 changed files with 1206826 additions and 0 deletions

24
.clang-format Normal file
View File

@ -0,0 +1,24 @@
---
BasedOnStyle: Google
---
Language: Cpp
AlignConsecutiveMacros: AcrossComments
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AttributeMacros: ['__', 'NO_RETURN']
BinPackArguments: false
BinPackParameters: false
BreakConstructorInitializers: BeforeColon
BreakBeforeTernaryOperators: false
ColumnLimit: 100
CommentPragmas: NOLINT:.*
ConstructorInitializerAllOnOneLineOrOnePerLine: true
Cpp11BracedListStyle: true
DerivePointerAlignment: false
FixNamespaceComments: true
PointerAlignment: Left
TabWidth: 2

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
JIT_ART
**/__pycache__/**
**/.idea
**/*.iml
**/*.pyc
**/*.swn

18
Android.bp Normal file
View File

@ -0,0 +1,18 @@
// If you're looking for ART global stuff, please see build/Android.bp.
package {
default_visibility: ["//art:__subpackages__"],
default_applicable_licenses: ["art_license"],
}
license {
name: "art_license",
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
"SPDX-license-identifier-BSD",
],
license_text: [
"NOTICE",
],
}

922
Android.mk Normal file
View File

@ -0,0 +1,922 @@
#
# 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.
#
LOCAL_PATH := $(call my-dir)
art_path := $(LOCAL_PATH)
include $(art_path)/tools/veridex/Android.mk
########################################################################
# clean-oat rules
#
include $(art_path)/build/Android.common_path.mk
.PHONY: clean-oat
clean-oat: clean-oat-host clean-oat-target
.PHONY: clean-oat-host
clean-oat-host:
find $(OUT_DIR) '(' -name '*.oat' -o -name '*.odex' -o -name '*.art' -o -name '*.vdex' ')' -a -type f | xargs rm -f
rm -rf $(TMPDIR)/*/test-*/dalvik-cache/*
rm -rf $(TMPDIR)/android-data/dalvik-cache/*
.PHONY: clean-oat-target
clean-oat-target:
$(ADB) root
$(ADB) wait-for-device remount
$(ADB) shell rm -rf $(ART_TARGET_NATIVETEST_DIR)
$(ADB) shell rm -rf $(ART_TARGET_TEST_DIR)
$(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*/*
$(ADB) shell rm -rf $(ART_DEXPREOPT_BOOT_JAR_DIR)/$(DEX2OAT_TARGET_ARCH)
$(ADB) shell rm -rf system/app/$(DEX2OAT_TARGET_ARCH)
ifdef TARGET_2ND_ARCH
$(ADB) shell rm -rf $(ART_DEXPREOPT_BOOT_JAR_DIR)/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)
$(ADB) shell rm -rf system/app/$($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_ARCH)
endif
$(ADB) shell rm -rf data/run-test/test-*/dalvik-cache/*
########################################################################
# cpplint rules to style check art source files
include $(art_path)/build/Android.cpplint.mk
########################################################################
# The art-tools package depends on helpers and tools that are useful for developers. Similar
# dependencies exist for the APEX builds for these tools (see build/apex/Android.bp).
ifneq ($(HOST_OS),darwin)
include $(CLEAR_VARS)
LOCAL_MODULE := art-tools
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
LOCAL_IS_HOST_MODULE := true
ifeq (true,$(my_art_module_source_build))
LOCAL_REQUIRED_MODULES := \
ahat \
dexdump \
hprof-conv \
# A subset of the tools are disabled when HOST_PREFER_32_BIT is defined as make reports that
# they are not supported on host (b/129323791). This is likely due to art_apex disabling host
# APEX builds when HOST_PREFER_32_BIT is set (b/120617876).
ifneq ($(HOST_PREFER_32_BIT),true)
LOCAL_REQUIRED_MODULES += \
dexdiag \
dexlist \
oatdump \
endif
else
# The developer tools available as prebuilts.
LOCAL_REQUIRED_MODULES := \
dexdump \
oatdump \
endif # ifeq (true,$(my_art_module_source_build))
include $(BUILD_PHONY_PACKAGE)
endif # HOST_OS != darwin
########################################################################
# Everything below is only available in ART source builds
# (ART_MODULE_BUILD_FROM_SOURCE=true).
########################################################################
# TODO(b/172480617): Clean up the platform dependencies on everything above and
# remove this condition.
ifeq (true,$(my_art_module_source_build))
########################################################################
# product rules
include $(art_path)/oatdump/Android.mk
include $(art_path)/tools/ahat/Android.mk
include $(art_path)/tools/dexfuzz/Android.mk
ART_HOST_DEPENDENCIES := \
$(ART_HOST_EXECUTABLES) \
$(ART_HOST_DEX_DEPENDENCIES) \
$(ART_HOST_SHARED_LIBRARY_DEPENDENCIES)
ifeq ($(ART_BUILD_HOST_DEBUG),true)
ART_HOST_DEPENDENCIES += $(ART_HOST_SHARED_LIBRARY_DEBUG_DEPENDENCIES)
endif
ART_TARGET_DEPENDENCIES := \
$(ART_TARGET_DEX_DEPENDENCIES)
########################################################################
# test rules
# All the dependencies that must be built ahead of sync-ing them onto the target device.
TEST_ART_TARGET_SYNC_DEPS := $(ADB_EXECUTABLE)
include $(art_path)/build/Android.common_test.mk
include $(art_path)/build/Android.gtest.mk
include $(art_path)/test/Android.run-test.mk
TEST_ART_TARGET_SYNC_DEPS += $(ART_TEST_TARGET_GTEST_DEPENDENCIES) $(ART_TEST_TARGET_RUN_TEST_DEPENDENCIES)
# Make sure /system is writable on the device.
TEST_ART_ADB_ROOT_AND_REMOUNT := \
($(ADB) root && \
$(ADB) wait-for-device remount && \
(($(ADB) shell touch /system/testfile && \
($(ADB) shell rm /system/testfile || true)) || \
($(ADB) disable-verity && \
$(ADB) reboot && \
$(ADB) wait-for-device root && \
$(ADB) wait-for-device remount)))
# "mm test-art" to build and run all tests on host and device
.PHONY: test-art
test-art:
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-gtest
test-art-gtest:
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-run-test
test-art-run-test:
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
########################################################################
# host test rules
test-art: test-art-host
test-art-gtest: test-art-host-gtest
test-art-run-test: test-art-host-run-test
VIXL_TEST_DEPENDENCY :=
# We can only run the vixl tests on 64-bit hosts (vixl testing issue) when its a
# top-level build (to declare the vixl test rule).
ifneq ($(HOST_PREFER_32_BIT),true)
ifeq ($(ONE_SHOT_MAKEFILE),)
VIXL_TEST_DEPENDENCY := run-vixl-tests
endif
endif
.PHONY: test-art-host-vixl
test-art-host-vixl: $(VIXL_TEST_DEPENDENCY)
# "mm test-art-host" to build and run all host tests.
.PHONY: test-art-host
test-art-host: test-art-host-gtest test-art-host-run-test \
test-art-host-vixl test-art-host-dexdump
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All host tests that run solely with the default compiler.
.PHONY: test-art-host-default
test-art-host-default: test-art-host-run-test-default
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All host tests that run solely with the optimizing compiler.
.PHONY: test-art-host-optimizing
test-art-host-optimizing: test-art-host-run-test-optimizing
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All host tests that run solely on the interpreter.
.PHONY: test-art-host-interpreter
test-art-host-interpreter: test-art-host-run-test-interpreter
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All host tests that run solely on the jit.
.PHONY: test-art-host-jit
test-art-host-jit: test-art-host-run-test-jit
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# Primary host architecture variants:
.PHONY: test-art-host$(ART_PHONY_TEST_HOST_SUFFIX)
test-art-host$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-gtest$(ART_PHONY_TEST_HOST_SUFFIX) \
test-art-host-run-test$(ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-default$(ART_PHONY_TEST_HOST_SUFFIX)
test-art-host-default$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-default$(ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-optimizing$(ART_PHONY_TEST_HOST_SUFFIX)
test-art-host-optimizing$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-optimizing$(ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-interpreter$(ART_PHONY_TEST_HOST_SUFFIX)
test-art-host-interpreter$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-interpreter$(ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-jit$(ART_PHONY_TEST_HOST_SUFFIX)
test-art-host-jit$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-jit$(ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# Secondary host architecture variants:
ifneq ($(HOST_PREFER_32_BIT),true)
.PHONY: test-art-host$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
test-art-host$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-gtest$(2ND_ART_PHONY_TEST_HOST_SUFFIX) \
test-art-host-run-test$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
test-art-host-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
test-art-host-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
test-art-host-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-jit$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
test-art-host-jit$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-jit$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
endif
# Dexdump/list regression test.
.PHONY: test-art-host-dexdump
test-art-host-dexdump: $(addprefix $(HOST_OUT_EXECUTABLES)/, dexdump dexlist)
ANDROID_HOST_OUT=$(realpath $(HOST_OUT)) art/test/dexdump/run-all-tests
########################################################################
# target test rules
test-art: test-art-target
test-art-gtest: test-art-target-gtest
test-art-run-test: test-art-target-run-test
# "mm test-art-target" to build and run all target tests.
.PHONY: test-art-target
test-art-target: test-art-target-gtest test-art-target-run-test
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All target tests that run solely with the default compiler.
.PHONY: test-art-target-default
test-art-target-default: test-art-target-run-test-default
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All target tests that run solely with the optimizing compiler.
.PHONY: test-art-target-optimizing
test-art-target-optimizing: test-art-target-run-test-optimizing
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All target tests that run solely on the interpreter.
.PHONY: test-art-target-interpreter
test-art-target-interpreter: test-art-target-run-test-interpreter
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All target tests that run solely on the jit.
.PHONY: test-art-target-jit
test-art-target-jit: test-art-target-run-test-jit
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# Primary target architecture variants:
.PHONY: test-art-target$(ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-gtest$(ART_PHONY_TEST_TARGET_SUFFIX) \
test-art-target-run-test$(ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-default$(ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target-default$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-default$(ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-jit$(ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target-jit$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-jit$(ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# Secondary target architecture variants:
ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX
.PHONY: test-art-target$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-gtest$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \
test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-jit$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target-jit$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-jit$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
endif
#######################
# Reset LOCAL_PATH because previous includes may override its value.
# Keep this after all "include $(art_path)/..." are done, and before any
# "include $(BUILD_...)".
LOCAL_PATH := $(art_path)
# Create canonical name -> file name symlink in the symbol directory for the
# debug APEX. The symbol files for it are installed to
# $(TARGET_OUT_UNSTRIPPED)/apex/com.android.art.debug. However, since it's
# available via /apex/com.android.art at runtime, create a symlink so that
# $(TARGET_OUT_UNSTRIPPED)/apex/com.android.art is linked to
# $(TARGET_OUT_UNSTRIPPED)/apex/$(TARGET_ART_APEX). We skip this for the release
# APEX which has com.android.art as $(TARGET_ART_APEX). Note that installation
# of the symlink is triggered by the apex_manifest.pb file which is the file
# that is guaranteed to be created regardless of the value of
# TARGET_FLATTEN_APEX.
# TODO(b/171419613): the symlink is disabled because the
# $OUT/symbols/apex/com.android.art name is taken by the com.android.art apex
# even when com.android.art.debug is selected by TARGET_ART_APEX.
# Disabling the symlink means that symbols for the com.android.art.debug apex
# will not be found.
ifeq ($(TARGET_FLATTEN_APEX),true)
art_apex_manifest_file := $(PRODUCT_OUT)/system/apex/$(TARGET_ART_APEX)/apex_manifest.pb
else
art_apex_manifest_file := $(PRODUCT_OUT)/apex/$(TARGET_ART_APEX)/apex_manifest.pb
endif
art_apex_symlink_timestamp := $(call intermediates-dir-for,FAKE,com.android.art)/symlink.timestamp
$(art_apex_manifest_file): $(art_apex_symlink_timestamp)
$(art_apex_manifest_file): PRIVATE_LINK_NAME := $(TARGET_OUT_UNSTRIPPED)/apex/com.android.art
$(art_apex_symlink_timestamp):
#ifeq ($(TARGET_ART_APEX),com.android.art)
# $(hide) if [ -L $(PRIVATE_LINK_NAME) ]; then rm -f $(PRIVATE_LINK_NAME); fi
#else
# $(hide) mkdir -p $(dir $(PRIVATE_LINK_NAME))
# $(hide) rm -rf $(PRIVATE_LINK_NAME)
# $(hide) ln -sf $(TARGET_ART_APEX) $(PRIVATE_LINK_NAME)
#endif
$(hide) touch $@
$(art_apex_symlink_timestamp): .KATI_SYMLINK_OUTPUTS := $(PRIVATE_LINK_NAME)
art_apex_manifest_file :=
#######################
# Fake packages for ART
# The art-runtime package depends on the core ART libraries and binaries. It exists so we can
# manipulate the set of things shipped, e.g., add debug versions and so on.
include $(CLEAR_VARS)
LOCAL_MODULE := art-runtime
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
# Reference the libraries and binaries in the appropriate APEX module, because
# they don't have platform variants. However if
# ART_MODULE_BUILD_FROM_SOURCE isn't true then the APEX
# modules are disabled, so Soong won't apply the APEX mutators to them, and
# then they are available with their plain names.
ifeq (true,$(ART_MODULE_BUILD_FROM_SOURCE))
art_module_lib = $(1).com.android.art
art_module_debug_lib = $(1).com.android.art.debug
else
art_module_lib = $(1)
art_module_debug_lib = $(1)
endif
# Base requirements.
LOCAL_REQUIRED_MODULES := \
$(call art_module_lib,dalvikvm) \
$(call art_module_lib,dex2oat) \
$(call art_module_lib,dexoptanalyzer) \
$(call art_module_lib,libart) \
$(call art_module_lib,libart-compiler) \
$(call art_module_lib,libopenjdkjvm) \
$(call art_module_lib,libopenjdkjvmti) \
$(call art_module_lib,odrefresh) \
$(call art_module_lib,profman) \
$(call art_module_lib,libadbconnection) \
$(call art_module_lib,libperfetto_hprof) \
# Potentially add in debug variants:
#
# * We will never add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = false.
# * We will always add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = true.
# * Otherwise, we will add them by default to eng builds.
art_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD)
ifneq (false,$(art_target_include_debug_build))
ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
art_target_include_debug_build := true
endif
ifeq (true,$(art_target_include_debug_build))
LOCAL_REQUIRED_MODULES += \
$(call art_module_debug_lib,dex2oatd) \
$(call art_module_debug_lib,dexoptanalyzerd) \
$(call art_module_debug_lib,libartd) \
$(call art_module_debug_lib,libartd-compiler) \
$(call art_module_debug_lib,libopenjdkd) \
$(call art_module_debug_lib,libopenjdkjvmd) \
$(call art_module_debug_lib,libopenjdkjvmtid) \
$(call art_module_debug_lib,profmand) \
$(call art_module_debug_lib,libadbconnectiond) \
$(call art_module_debug_lib,libperfetto_hprofd) \
endif
endif
art_module_lib :=
art_module_debug_lib :=
include $(BUILD_PHONY_PACKAGE)
####################################################################################################
# Fake packages to ensure generation of libopenjdkd when one builds with mm/mmm/mmma.
#
# The library is required for starting a runtime in debug mode, but libartd does not depend on it
# (dependency cycle otherwise).
#
# Note: * As the package is phony to create a dependency the package name is irrelevant.
# * We make MULTILIB explicit to "both," just to state here that we want both libraries on
# 64-bit systems, even if it is the default.
# ART on the host.
ifneq ($(HOST_OS),darwin)
ifeq ($(ART_BUILD_HOST_DEBUG),true)
include $(CLEAR_VARS)
LOCAL_MODULE := art-libartd-libopenjdkd-host-dependency
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
LOCAL_MULTILIB := both
LOCAL_REQUIRED_MODULES := libopenjdkd
LOCAL_IS_HOST_MODULE := true
include $(BUILD_PHONY_PACKAGE)
endif
endif # HOST_OS != darwin
########################################################################
# "m build-art" for quick minimal build
.PHONY: build-art
build-art: build-art-host
# For host, we extract the ICU data from the apex and install it to HOST_OUT/I18N_APEX.
$(HOST_I18N_DATA): $(TARGET_OUT)/apex/$(I18N_APEX).apex $(HOST_OUT)/bin/deapexer
$(call extract-from-apex,$(I18N_APEX))
rm -rf $(HOST_OUT)/$(I18N_APEX)
mkdir -p $(HOST_OUT)/$(I18N_APEX)/
cp -R $(TARGET_OUT)/apex/$(I18N_APEX)/etc/ $(HOST_OUT)/$(I18N_APEX)/
touch $@
$(HOST_TZDATA_DATA): $(TARGET_OUT)/apex/$(TZDATA_APEX).apex $(HOST_OUT)/bin/deapexer
$(call extract-from-apex,$(TZDATA_APEX))
rm -rf $(HOST_OUT)/$(TZDATA_APEX)
mkdir -p $(HOST_OUT)/$(TZDATA_APEX)/
cp -R $(TARGET_OUT)/apex/$(TZDATA_APEX)/etc/ $(HOST_OUT)/$(TZDATA_APEX)/
touch $@
.PHONY: build-art-host
build-art-host: $(HOST_OUT_EXECUTABLES)/art $(ART_HOST_DEPENDENCIES) $(HOST_CORE_IMG_OUTS) $(HOST_I18N_DATA) $(HOST_TZDATA_DATA)
build-art: build-art-target
.PHONY: build-art-target
build-art-target: $(TARGET_OUT_EXECUTABLES)/art $(ART_TARGET_DEPENDENCIES) $(TARGET_CORE_IMG_OUTS)
PRIVATE_ART_APEX_DEPENDENCY_FILES := \
bin/dalvikvm32 \
bin/dalvikvm64 \
bin/dalvikvm \
bin/dex2oat32 \
bin/dex2oat64 \
bin/dexdump \
PRIVATE_ART_APEX_DEPENDENCY_LIBS := \
lib/libadbconnection.so \
lib/libandroidio.so \
lib/libartbase.so \
lib/libart-compiler.so \
lib/libart-dexlayout.so \
lib/libart-disassembler.so \
lib/libartpalette.so \
lib/libart.so \
lib/libbacktrace.so \
lib/libdexfile.so \
lib/libdt_fd_forward.so \
lib/libdt_socket.so \
lib/libexpat.so \
lib/libjavacore.so \
lib/libjdwp.so \
lib/liblzma.so \
lib/libmeminfo.so \
lib/libnativebridge.so \
lib/libnativehelper.so \
lib/libnativeloader.so \
lib/libnpt.so \
lib/libopenjdkjvm.so \
lib/libopenjdkjvmti.so \
lib/libopenjdk.so \
lib/libpac.so \
lib/libprocinfo.so \
lib/libprofile.so \
lib/libsigchain.so \
lib/libunwindstack.so \
lib/libziparchive.so \
lib64/libadbconnection.so \
lib64/libandroidio.so \
lib64/libartbase.so \
lib64/libart-compiler.so \
lib64/libart-dexlayout.so \
lib64/libart-disassembler.so \
lib64/libartpalette.so \
lib64/libart.so \
lib64/libbacktrace.so \
lib64/libdexfile.so \
lib64/libdt_fd_forward.so \
lib64/libdt_socket.so \
lib64/libexpat.so \
lib64/libjavacore.so \
lib64/libjdwp.so \
lib64/liblzma.so \
lib64/libmeminfo.so \
lib64/libnativebridge.so \
lib64/libnativehelper.so \
lib64/libnativeloader.so \
lib64/libnpt.so \
lib64/libopenjdkjvm.so \
lib64/libopenjdkjvmti.so \
lib64/libopenjdk.so \
lib64/libpac.so \
lib64/libprocinfo.so \
lib64/libprofile.so \
lib64/libsigchain.so \
lib64/libunwindstack.so \
lib64/libziparchive.so \
PRIVATE_RUNTIME_APEX_DEPENDENCY_FILES := \
bin/linker \
bin/linker64 \
lib/bionic/libc.so \
lib/bionic/libdl.so \
lib/bionic/libdl_android.so \
lib/bionic/libm.so \
lib64/bionic/libc.so \
lib64/bionic/libdl.so \
lib64/bionic/libdl_android.so \
lib64/bionic/libm.so \
PRIVATE_CONSCRYPT_APEX_DEPENDENCY_LIBS := \
lib/libcrypto.so \
lib/libjavacrypto.so \
lib/libssl.so \
lib64/libcrypto.so \
lib64/libjavacrypto.so \
lib64/libssl.so \
PRIVATE_I18N_APEX_DEPENDENCY_LIBS := \
lib/libicu.so \
lib/libicui18n.so \
lib/libicu_jni.so \
lib/libicuuc.so \
lib64/libicu.so \
lib64/libicui18n.so \
lib64/libicu_jni.so \
lib64/libicuuc.so \
PRIVATE_STATSD_APEX_DEPENDENCY_LIBS := \
lib/libstatssocket.so \
lib64/libstatssocket.so \
# Extracts files from an APEX into a location. The APEX can be either a .apex
# file in $(TARGET_OUT)/apex, or a directory in the same location. Files are
# extracted to $(TARGET_OUT) with the same relative paths as under the APEX
# root.
# $(1): APEX base name
# $(2): List of files to extract, with paths relative to the APEX root
#
# "cp -d" below doesn't work on Darwin, but this is only used for Golem builds
# and won't run on mac anyway.
define extract-from-apex
apex_root=$(TARGET_OUT)/apex && \
apex_file=$$apex_root/$(1).apex && \
apex_dir=$$apex_root/$(1) && \
if [ -f $$apex_file ]; then \
rm -rf $$apex_dir && \
mkdir -p $$apex_dir && \
debugfs=$(HOST_OUT)/bin/debugfs_static && \
$(HOST_OUT)/bin/deapexer --debugfs_path $$debugfs extract $$apex_file $$apex_dir; \
fi && \
for f in $(2); do \
sf=$$apex_dir/$$f && \
df=$(TARGET_OUT)/$$f && \
if [ -f $$sf -o -h $$sf ]; then \
mkdir -p $$(dirname $$df) && \
cp -fd $$sf $$df; \
fi || exit 1; \
done
endef
# Copy or extract some required files from APEXes to the `system` (TARGET_OUT)
# directory. This is dangerous as these files could inadvertently stay in this
# directory and be included in a system image.
#
# This target is only used by Golem now.
#
# NB Android build does not use cp from:
# $ANDROID_BUILD_TOP/prebuilts/build-tools/path/{linux-x86,darwin-x86}
# which has a non-standard set of command-line flags.
#
# TODO(b/129332183): Remove this when Golem has full support for the
# ART APEX.
.PHONY: standalone-apex-files
standalone-apex-files: deapexer \
$(RELEASE_ART_APEX) \
$(RUNTIME_APEX) \
$(CONSCRYPT_APEX) \
$(I18N_APEX) \
$(STATSD_APEX) \
$(TZDATA_APEX)
$(call extract-from-apex,$(RELEASE_ART_APEX),\
$(PRIVATE_ART_APEX_DEPENDENCY_LIBS) $(PRIVATE_ART_APEX_DEPENDENCY_FILES))
# The Runtime APEX has the Bionic libs in ${LIB}/bionic subdirectories,
# so we need to move them up a level after extraction.
# Also, platform libraries are installed in prebuilts, so copy them over.
$(call extract-from-apex,$(RUNTIME_APEX),\
$(PRIVATE_RUNTIME_APEX_DEPENDENCY_FILES)) && \
for libdir in $(TARGET_OUT)/lib $(TARGET_OUT)/lib64; do \
if [ -d $$libdir/bionic ]; then \
mv -f $$libdir/bionic/*.so $$libdir; \
fi || exit 1; \
done && \
for libdir in $(TARGET_OUT)/lib $(TARGET_OUT)/lib64; do \
if [ -d $$libdir ]; then \
cp prebuilts/runtime/mainline/platform/impl/$(TARGET_ARCH)/*.so $$libdir; \
fi || exit 1; \
done
$(call extract-from-apex,$(CONSCRYPT_APEX),\
$(PRIVATE_CONSCRYPT_APEX_DEPENDENCY_LIBS))
$(call extract-from-apex,$(I18N_APEX),\
$(PRIVATE_I18N_APEX_DEPENDENCY_LIBS))
$(call extract-from-apex,$(STATSD_APEX),\
$(PRIVATE_STATSD_APEX_DEPENDENCY_LIBS))
$(call extract-from-apex,$(TZDATA_APEX),)
########################################################################
# Phony target for only building what go/lem requires for pushing ART on /data.
.PHONY: build-art-target-golem
ART_TARGET_PLATFORM_DEPENDENCIES := \
$(TARGET_OUT)/etc/public.libraries.txt \
$(TARGET_OUT_SHARED_LIBRARIES)/libcutils.so \
$(TARGET_OUT_SHARED_LIBRARIES)/liblz4.so \
$(TARGET_OUT_SHARED_LIBRARIES)/libprocessgroup.so \
$(TARGET_OUT_SHARED_LIBRARIES)/libprocinfo.so \
$(TARGET_OUT_SHARED_LIBRARIES)/libselinux.so \
$(TARGET_OUT_SHARED_LIBRARIES)/libtombstoned_client.so \
$(TARGET_OUT_SHARED_LIBRARIES)/libz.so \
# Also include libartbenchmark, we always include it when running golem.
# libstdc++ is needed when building for ART_TARGET_LINUX.
ART_TARGET_SHARED_LIBRARY_BENCHMARK := $(TARGET_OUT_SHARED_LIBRARIES)/libartbenchmark.so
build-art-target-golem: $(RELEASE_ART_APEX) com.android.runtime $(CONSCRYPT_APEX) \
$(TARGET_OUT_EXECUTABLES)/art \
$(TARGET_OUT_EXECUTABLES)/dex2oat_wrapper \
$(ART_TARGET_PLATFORM_DEPENDENCIES) \
$(ART_TARGET_SHARED_LIBRARY_BENCHMARK) \
$(PRODUCT_OUT)/apex/art_boot_images/javalib/$(TARGET_ARCH)/boot.art \
standalone-apex-files
# remove debug libraries from public.libraries.txt because golem builds
# won't have it.
sed -i '/libartd.so/d' $(TARGET_OUT)/etc/public.libraries.txt
sed -i '/libdexfiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt
sed -i '/libprofiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt
sed -i '/libartbased.so/d' $(TARGET_OUT)/etc/public.libraries.txt
########################################################################
# Phony target for building what go/lem requires on host.
.PHONY: build-art-host-golem
# Also include libartbenchmark, we always include it when running golem.
ART_HOST_SHARED_LIBRARY_BENCHMARK := $(ART_HOST_OUT_SHARED_LIBRARIES)/libartbenchmark.so
build-art-host-golem: build-art-host \
$(ART_HOST_SHARED_LIBRARY_BENCHMARK) \
$(HOST_OUT_EXECUTABLES)/dex2oat_wrapper
########################################################################
# Phony target for building what go/lem requires for syncing /system to target.
.PHONY: build-art-unbundled-golem
art_apex_jars := $(foreach pair,$(ART_APEX_JARS), $(call word-colon,2,$(pair)))
build-art-unbundled-golem: art-runtime linker oatdump $(art_apex_jars) conscrypt crash_dump
########################################################################
# Rules for building all dependencies for tests.
.PHONY: build-art-host-gtests build-art-host-run-tests build-art-host-tests
build-art-host-gtests: build-art-host $(ART_TEST_HOST_GTEST_DEPENDENCIES)
build-art-host-run-tests: build-art-host $(TEST_ART_RUN_TEST_DEPENDENCIES) $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES)
build-art-host-tests: build-art-host-gtests build-art-host-run-tests
.PHONY: build-art-target-gtests build-art-target-run-tests build-art-target-tests
build-art-target-gtests: build-art-target $(ART_TEST_TARGET_GTEST_DEPENDENCIES)
build-art-target-run-tests: build-art-target $(TEST_ART_RUN_TEST_DEPENDENCIES) $(ART_TEST_TARGET_RUN_TEST_DEPENDENCIES)
build-art-target-tests: build-art-target-gtests build-art-target-run-tests
########################################################################
# targets to switch back and forth from libdvm to libart
.PHONY: use-art
use-art:
$(ADB) root
$(ADB) wait-for-device shell stop
$(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libart.so
$(ADB) shell start
.PHONY: use-artd
use-artd:
$(ADB) root
$(ADB) wait-for-device shell stop
$(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
$(ADB) shell start
.PHONY: use-dalvik
use-dalvik:
$(ADB) root
$(ADB) wait-for-device shell stop
$(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libdvm.so
$(ADB) shell start
.PHONY: use-art-full
use-art-full:
$(ADB) root
$(ADB) wait-for-device shell stop
$(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
$(ADB) shell setprop dalvik.vm.dex2oat-filter \"\"
$(ADB) shell setprop dalvik.vm.image-dex2oat-filter \"\"
$(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libart.so
$(ADB) shell setprop dalvik.vm.usejit false
$(ADB) shell start
.PHONY: use-artd-full
use-artd-full:
$(ADB) root
$(ADB) wait-for-device shell stop
$(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
$(ADB) shell setprop dalvik.vm.dex2oat-filter \"\"
$(ADB) shell setprop dalvik.vm.image-dex2oat-filter \"\"
$(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
$(ADB) shell setprop dalvik.vm.usejit false
$(ADB) shell start
.PHONY: use-art-jit
use-art-jit:
$(ADB) root
$(ADB) wait-for-device shell stop
$(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
$(ADB) shell setprop dalvik.vm.dex2oat-filter "verify-at-runtime"
$(ADB) shell setprop dalvik.vm.image-dex2oat-filter "verify-at-runtime"
$(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libart.so
$(ADB) shell setprop dalvik.vm.usejit true
$(ADB) shell start
.PHONY: use-art-interpret-only
use-art-interpret-only:
$(ADB) root
$(ADB) wait-for-device shell stop
$(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
$(ADB) shell setprop dalvik.vm.dex2oat-filter "interpret-only"
$(ADB) shell setprop dalvik.vm.image-dex2oat-filter "interpret-only"
$(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libart.so
$(ADB) shell setprop dalvik.vm.usejit false
$(ADB) shell start
.PHONY: use-artd-interpret-only
use-artd-interpret-only:
$(ADB) root
$(ADB) wait-for-device shell stop
$(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
$(ADB) shell setprop dalvik.vm.dex2oat-filter "interpret-only"
$(ADB) shell setprop dalvik.vm.image-dex2oat-filter "interpret-only"
$(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
$(ADB) shell setprop dalvik.vm.usejit false
$(ADB) shell start
.PHONY: use-art-verify-none
use-art-verify-none:
$(ADB) root
$(ADB) wait-for-device shell stop
$(ADB) shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
$(ADB) shell setprop dalvik.vm.dex2oat-filter "verify-none"
$(ADB) shell setprop dalvik.vm.image-dex2oat-filter "verify-none"
$(ADB) shell setprop persist.sys.dalvik.vm.lib.2 libart.so
$(ADB) shell setprop dalvik.vm.usejit false
$(ADB) shell start
########################################################################
# Clear locally used variables.
TEST_ART_TARGET_SYNC_DEPS :=
# Helper target that depends on boot image creation.
#
# Can be used, for example, to dump initialization failures:
# m art-boot-image ART_BOOT_IMAGE_EXTRA_ARGS=--dump-init-failures=fails.txt
.PHONY: art-boot-image
art-boot-image: $(DEXPREOPT_IMAGE_boot_$(TARGET_ARCH))
.PHONY: art-job-images
art-job-images: \
art-boot-image \
$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) \
$(HOST_OUT_EXECUTABLES)/dex2oats \
$(HOST_OUT_EXECUTABLES)/dex2oatds \
$(HOST_OUT_EXECUTABLES)/profman
########################################################################
# Build a target that contains dex public SDK stubs for SDK version in the list.
# Zip files structure:
# public-sdk-28-stub.zip
# classes.dex
# public-sdk-29-stub.zip
# classes.dex
# public-sdk-30-stub.zip
# classes.dex
MIN_SDK_VERSION := 28
SDK_VERSIONS := $(call numbers_greater_or_equal_to,$(MIN_SDK_VERSION),$(TARGET_AVAIALBLE_SDK_VERSIONS))
# Create dex public SDK stubs.
define get_public_sdk_stub_dex
$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/public_sdk_$(1)_stub_intermediates/classes.dex
endef
# The input is the SDK version.
define create_public_sdk_dex
public_sdk_$(1)_stub := $$(call get_public_sdk_stub_dex,$(1))
$$(public_sdk_$(1)_stub): PRIVATE_MIN_SDK_VERSION := $(1)
$$(public_sdk_$(1)_stub): $$(call resolve-prebuilt-sdk-jar-path,$(1)) $$(DX) $$(ZIP2ZIP)
$$(transform-classes.jar-to-dex)
$$(call declare-1p-target,$$(public_sdk_$(1)_stub),art)
endef
$(foreach version,$(SDK_VERSIONS),$(eval $(call create_public_sdk_dex,$(version))))
# Create dex public SDK stubs zip.
define get_public_sdk_stub_zip
$(call intermediates-dir-for,PACKAGING,public_sdk_stub,HOST)/public-sdk-$(1)-stub.zip
endef
define create_public_sdk_zip
PUBLIC_SDK_$(1)_STUB_ZIP_PATH := $$(call get_public_sdk_stub_zip,$(1))
$$(PUBLIC_SDK_$(1)_STUB_ZIP_PATH): PRIVATE_SDK_STUBS_DEX_DIR := $$(dir $$(public_sdk_$(1)_stub))
$$(PUBLIC_SDK_$(1)_STUB_ZIP_PATH): $$(SOONG_ZIP) $$(public_sdk_$(1)_stub)
rm -f $$@
$$(SOONG_ZIP) -o $$@ -C $$(PRIVATE_SDK_STUBS_DEX_DIR) -D $$(PRIVATE_SDK_STUBS_DEX_DIR)
$$(call declare-1p-container,$$(PUBLIC_SDK_$(1)_STUB_ZIP_PATH),art)
$$(call declare-container-license-deps,$$(PUBLIC_SDK_$(1)_STUB_ZIP_PATH),$$(public_sdk_$(1)_stub),$$(PUBLIC_SDK_$(1)_STUB_PATH):)
endef
$(foreach version,$(SDK_VERSIONS),$(eval $(call create_public_sdk_zip,$(version))))
# Make the zip files available for prebuilts.
$(foreach version,$(SDK_VERSIONS),$(call dist-for-goals,sdk,$(call get_public_sdk_stub_zip,$(version))))
STUB_ZIP_FILES = $(foreach version,$(SDK_VERSIONS),$(call get_public_sdk_stub_zip,$(version)))
.PHONY: public_sdk_stubs
public_sdk_stubs: $(STUB_ZIP_FILES)
MIN_SDK_VERSION :=
SDK_VERSIONS :=
endif # ifeq (true,$(my_art_module_source_build))

33
CPPLINT.cfg Normal file
View File

@ -0,0 +1,33 @@
#
# Copyright (C) 2017 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.
#
# Don't search for additional CPPLINT.cfg in parent directories.
set noparent
# Use 'ART_' as the cpp header guard prefix (e.g. #ifndef ART_PATH_TO_FILE_H_).
root=..
# Limit line length.
linelength=100
# Ignore the following categories of errors, as specified by the filter:
# (the filter settings are concatenated together)
filter=-build/c++11
filter=-build/include
filter=-readability/function,-readability/streams,-readability/todo
filter=-runtime/printf,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn
# TODO: this should be re-enabled.
filter=-whitespace/line_length

126
CleanSpec.mk Normal file
View File

@ -0,0 +1,126 @@
# Copyright (C) 2014 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 $(PRODUCT_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_DIR) 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_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
# Switching to jemalloc requires deleting these files.
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libart_*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libartd_*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libart_*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libartd_*)
# Old Android Runtime APEX package, before the introduction of "release" and "debug" packages.
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.runtime.apex)
# Clean up ICU libraries moved to runtime apex
$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libandroidicu.so)
$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/lib*/libpac.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest*/art_libdexfile_support_tests/dex_file_supp_test)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest*/)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest*/)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest*/)
# Clean up duplicate compiles between static and shared compiles of libart and libartd
$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/art/runtime/libart/*shared*/obj)
$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/art/runtime/libartd/*shared*/obj)
# Force regeneration of .apex files after removal of time zone data files from the runtime APEX
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.runtime.*)
# Remove artifacts that used to be generated (as a workaround for
# improper Runtime APEX support) by tools/buildbot-build.sh via the
# `standalone-apex-files` Make rule.
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*)
# Remove artifacts that used to be generated (as a workaround for
# improper Runtime APEX support) by tools/buildbot-build.sh via the
# `icu-data-art-test` Make rule.
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/icu)
# Remove ART test target artifacts.
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest*/)
# Remove all APEX artifacts after the change to use the Testing
# Runtime APEX in lieu of the Debug Runtime APEX for ART testing.
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex)
# Remove the icu .dat file from /apex/com.android.runtime and the host equivalent.
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex)
$(call add-clean-step, rm -rf $(HOST_OUT)/com.android.runtime/etc/icu/*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/icu)
# Remove all APEX artifacts for the Runtime/ART APEX split.
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex)
$(call add-clean-step, rm -rf $(HOST_OUT)/apex)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/apex)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/apex)
# Remove dex2oat artifacts for boot image extensions (workaround for broken dependencies).
$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
# Remove empty dir for art APEX because it will be created on demand while mounting release|debug
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.art)
# Remove 'libart[d]?-simulator-container.so' which was briefly in the ART AREX.
$(call add-clean-step, find $(OUT_DIR)/soong/.intermediates/art -name 'libart*-simulator-container.so' -type f | xargs rm -f)
# Remove symbols/apex/com.android.art symlink (b/171406631)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/apex/com.android.art)
# art/tools/build_linux_bionic_tests.sh uses find here and can encounter
# libdexfile_external.so in incremental builds.
$(call add-clean-step, rm -rf $(HOST_OUT))
# Remove all dex2oat artifacts (workaround for broken dependencies).
$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************

3
METADATA Normal file
View File

@ -0,0 +1,3 @@
third_party {
license_type: RESTRICTED
}

0
MODULE_LICENSE_APACHE2 Normal file
View File

190
NOTICE Normal file
View File

@ -0,0 +1,190 @@
Copyright (c) 2005-2013, 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.
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.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) 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. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

19
OWNERS Normal file
View File

@ -0,0 +1,19 @@
# Bug component: 86431
dsrbecky@google.com
hboehm@google.com
jiakaiz@google.com
lokeshgidra@google.com
mast@google.com
miguelaranda@google.com
mingaleev@google.com
mythria@google.com
ngeoffray@google.com
nikitai@google.com
oth@google.com
prb@google.com
rpl@google.com
skvadrik@google.com
solanes@google.com
sorinbasca@google.com
vichang@google.com
vmarko@google.com

17
PREUPLOAD.cfg Normal file
View File

@ -0,0 +1,17 @@
[Hook Scripts]
check_generated_tests_up_to_date = tools/test_presubmit.py
hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
# TODO(b/189484095): Port libnativebridge tests to atest and enable in presubmit
# so we don't need the custom runtests script and this check.
check_libnativebridge_test_field = libnativebridge/tests/preupload_check_test_tag.sh ${PREUPLOAD_COMMIT_MESSAGE} ${PREUPLOAD_FILES}
[Builtin Hooks]
cpplint = true
bpfmt = true
gofmt = true
[Builtin Hooks Options]
# Cpplint prints nothing unless there were errors.
cpplint = --quiet ${PREUPLOAD_FILES}

2527
TEST_MAPPING Normal file

File diff suppressed because it is too large Load Diff

85
adbconnection/Android.bp Normal file
View File

@ -0,0 +1,85 @@
//
// Copyright (C) 2017 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.
//
// Build variants {target,host} x {debug,ndebug} x {32,64}
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "art_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
cc_defaults {
name: "adbconnection-defaults",
host_supported: true,
srcs: ["adbconnection.cc"],
defaults: ["art_defaults"],
// Note that this tool needs to be built for both 32-bit and 64-bit since it requires
// to be same ISA as what it is attached to.
compile_multilib: "both",
shared_libs: [
"libbase",
"libadbconnection_client",
],
target: {
host: {
},
darwin: {
enabled: false,
},
},
header_libs: [
"libnativehelper_header_only",
"dt_fd_forward_export",
],
required: [
"libjdwp",
"libdt_fd_forward",
],
}
art_cc_library {
name: "libadbconnection",
defaults: ["adbconnection-defaults"],
shared_libs: [
"libart",
"libartbase",
],
apex_available: [
"com.android.art",
"com.android.art.debug",
],
}
art_cc_library {
name: "libadbconnectiond",
defaults: [
"art_debug_defaults",
"adbconnection-defaults",
],
shared_libs: [
"libartd",
"libartbased",
],
apex_available: [
"com.android.art.debug",
],
}

View File

@ -0,0 +1,888 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <array>
#include <cstddef>
#include <iterator>
#include "adbconnection.h"
#include "adbconnection/client.h"
#include "android-base/endian.h"
#include "android-base/stringprintf.h"
#include "base/file_utils.h"
#include "base/globals.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "base/socket_peer_is_trusted.h"
#include "debugger.h"
#include "jni/java_vm_ext.h"
#include "jni/jni_env_ext.h"
#include "mirror/throwable.h"
#include "nativehelper/scoped_local_ref.h"
#include "runtime-inl.h"
#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
#include "well_known_classes.h"
#include "fd_transport.h"
#include "poll.h"
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <sys/eventfd.h>
#include <jni.h>
namespace adbconnection {
static constexpr size_t kJdwpHeaderLen = 11U;
/* DDM support */
static constexpr uint8_t kJdwpDdmCmdSet = 199U; // 0xc7, or 'G'+128
static constexpr uint8_t kJdwpDdmCmd = 1U;
// Messages sent from the transport
using dt_fd_forward::kListenStartMessage;
using dt_fd_forward::kListenEndMessage;
using dt_fd_forward::kAcceptMessage;
using dt_fd_forward::kCloseMessage;
using dt_fd_forward::kHandshakeCompleteMessage;
// Messages sent to the transport
using dt_fd_forward::kPerformHandshakeMessage;
using dt_fd_forward::kSkipHandshakeMessage;
using android::base::StringPrintf;
static constexpr const char kJdwpHandshake[14] = {
'J', 'D', 'W', 'P', '-', 'H', 'a', 'n', 'd', 's', 'h', 'a', 'k', 'e'
};
static constexpr int kEventfdLocked = 0;
static constexpr int kEventfdUnlocked = 1;
static constexpr size_t kPacketHeaderLen = 11;
static constexpr off_t kPacketSizeOff = 0;
static constexpr off_t kPacketIdOff = 4;
static constexpr off_t kPacketCommandSetOff = 9;
static constexpr off_t kPacketCommandOff = 10;
static constexpr uint8_t kDdmCommandSet = 199;
static constexpr uint8_t kDdmChunkCommand = 1;
static std::optional<AdbConnectionState> gState;
static std::optional<pthread_t> gPthread;
static bool IsDebuggingPossible() {
return art::Dbg::IsJdwpAllowed();
}
// Begin running the debugger.
void AdbConnectionDebuggerController::StartDebugger() {
// The debugger thread is started for a debuggable or profileable-from-shell process.
// The pid will be send to adbd for adb's "track-jdwp" and "track-app" services.
// The thread will also set up the jdwp tunnel if the process is debuggable.
if (IsDebuggingPossible() || art::Runtime::Current()->IsProfileableFromShell()) {
connection_->StartDebuggerThreads();
} else {
LOG(ERROR) << "Not starting debugger since process cannot load the jdwp agent.";
}
}
// The debugger should have already shut down since the runtime is ending. As far
// as the agent is concerned shutdown already happened when we went to kDeath
// state. We need to clean up our threads still though and this is a good time
// to do it since the runtime is still able to handle all the normal state
// transitions.
void AdbConnectionDebuggerController::StopDebugger() {
// Stop our threads.
gState->StopDebuggerThreads();
// Wait for our threads to actually return and cleanup the pthread.
if (gPthread.has_value()) {
void* ret_unused;
if (TEMP_FAILURE_RETRY(pthread_join(gPthread.value(), &ret_unused)) != 0) {
PLOG(ERROR) << "Failed to join debugger threads!";
}
gPthread.reset();
}
}
bool AdbConnectionDebuggerController::IsDebuggerConfigured() {
return IsDebuggingPossible() && !art::Runtime::Current()->GetJdwpOptions().empty();
}
void AdbConnectionDdmCallback::DdmPublishChunk(uint32_t type,
const art::ArrayRef<const uint8_t>& data) {
connection_->PublishDdmData(type, data);
}
class ScopedEventFdLock {
public:
explicit ScopedEventFdLock(int fd) : fd_(fd), data_(0) {
TEMP_FAILURE_RETRY(read(fd_, &data_, sizeof(data_)));
}
~ScopedEventFdLock() {
TEMP_FAILURE_RETRY(write(fd_, &data_, sizeof(data_)));
}
private:
int fd_;
uint64_t data_;
};
AdbConnectionState::AdbConnectionState(const std::string& agent_name)
: agent_name_(agent_name),
controller_(this),
ddm_callback_(this),
sleep_event_fd_(-1),
control_ctx_(nullptr, adbconnection_client_destroy),
local_agent_control_sock_(-1),
remote_agent_control_sock_(-1),
adb_connection_socket_(-1),
adb_write_event_fd_(-1),
shutting_down_(false),
agent_loaded_(false),
agent_listening_(false),
agent_has_socket_(false),
sent_agent_fds_(false),
performed_handshake_(false),
notified_ddm_active_(false),
next_ddm_id_(1),
started_debugger_threads_(false) {
// Add the startup callback.
art::ScopedObjectAccess soa(art::Thread::Current());
art::Runtime::Current()->GetRuntimeCallbacks()->AddDebuggerControlCallback(&controller_);
}
AdbConnectionState::~AdbConnectionState() {
// Remove the startup callback.
art::Thread* self = art::Thread::Current();
if (self != nullptr) {
art::ScopedObjectAccess soa(self);
art::Runtime::Current()->GetRuntimeCallbacks()->RemoveDebuggerControlCallback(&controller_);
}
}
static jobject CreateAdbConnectionThread(art::Thread* thr) {
JNIEnv* env = thr->GetJniEnv();
// Move to native state to talk with the jnienv api.
art::ScopedThreadStateChange stsc(thr, art::ThreadState::kNative);
ScopedLocalRef<jstring> thr_name(env, env->NewStringUTF(kAdbConnectionThreadName));
ScopedLocalRef<jobject> thr_group(
env,
env->GetStaticObjectField(art::WellKnownClasses::java_lang_ThreadGroup,
art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup));
return env->NewObject(art::WellKnownClasses::java_lang_Thread,
art::WellKnownClasses::java_lang_Thread_init,
thr_group.get(),
thr_name.get(),
/*Priority=*/ 0,
/*Daemon=*/ true);
}
struct CallbackData {
AdbConnectionState* this_;
jobject thr_;
};
static void* CallbackFunction(void* vdata) {
std::unique_ptr<CallbackData> data(reinterpret_cast<CallbackData*>(vdata));
art::Thread* self = art::Thread::Attach(kAdbConnectionThreadName,
true,
data->thr_);
CHECK(self != nullptr) << "threads_being_born_ should have ensured thread could be attached.";
// The name in Attach() is only for logging. Set the thread name. This is important so
// that the thread is no longer seen as starting up.
{
art::ScopedObjectAccess soa(self);
self->SetThreadName(kAdbConnectionThreadName);
}
// Release the peer.
JNIEnv* env = self->GetJniEnv();
env->DeleteGlobalRef(data->thr_);
data->thr_ = nullptr;
{
// The StartThreadBirth was called in the parent thread. We let the runtime know we are up
// before going into the provided code.
art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_);
art::Runtime::Current()->EndThreadBirth();
}
data->this_->RunPollLoop(self);
int detach_result = art::Runtime::Current()->GetJavaVM()->DetachCurrentThread();
CHECK_EQ(detach_result, 0);
return nullptr;
}
void AdbConnectionState::StartDebuggerThreads() {
// First do all the final setup we need.
CHECK_EQ(adb_write_event_fd_.get(), -1);
CHECK_EQ(sleep_event_fd_.get(), -1);
CHECK_EQ(local_agent_control_sock_.get(), -1);
CHECK_EQ(remote_agent_control_sock_.get(), -1);
sleep_event_fd_.reset(eventfd(kEventfdLocked, EFD_CLOEXEC));
CHECK_NE(sleep_event_fd_.get(), -1) << "Unable to create wakeup eventfd.";
adb_write_event_fd_.reset(eventfd(kEventfdUnlocked, EFD_CLOEXEC));
CHECK_NE(adb_write_event_fd_.get(), -1) << "Unable to create write-lock eventfd.";
{
art::ScopedObjectAccess soa(art::Thread::Current());
art::Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(&ddm_callback_);
}
// Setup the socketpair we use to talk to the agent.
bool has_sockets;
do {
has_sockets = android::base::Socketpair(AF_UNIX,
SOCK_SEQPACKET | SOCK_CLOEXEC,
0,
&local_agent_control_sock_,
&remote_agent_control_sock_);
} while (!has_sockets && errno == EINTR);
if (!has_sockets) {
PLOG(FATAL) << "Unable to create socketpair for agent control!";
}
// Next start the threads.
art::Thread* self = art::Thread::Current();
art::ScopedObjectAccess soa(self);
{
art::Runtime* runtime = art::Runtime::Current();
art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_);
if (runtime->IsShuttingDownLocked()) {
// The runtime is shutting down so we cannot create new threads. This shouldn't really happen.
LOG(ERROR) << "The runtime is shutting down when we are trying to start up the debugger!";
return;
}
runtime->StartThreadBirth();
}
ScopedLocalRef<jobject> thr(soa.Env(), CreateAdbConnectionThread(soa.Self()));
// Note: Using pthreads instead of std::thread to not abort when the thread cannot be
// created (exception support required).
std::unique_ptr<CallbackData> data(new CallbackData { this, soa.Env()->NewGlobalRef(thr.get()) });
started_debugger_threads_ = true;
gPthread.emplace();
int pthread_create_result = pthread_create(&gPthread.value(),
nullptr,
&CallbackFunction,
data.get());
if (pthread_create_result != 0) {
gPthread.reset();
started_debugger_threads_ = false;
// If the create succeeded the other thread will call EndThreadBirth.
art::Runtime* runtime = art::Runtime::Current();
soa.Env()->DeleteGlobalRef(data->thr_);
LOG(ERROR) << "Failed to create thread for adb-jdwp connection manager!";
art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
runtime->EndThreadBirth();
return;
}
data.release(); // NOLINT pthreads API.
}
static bool FlagsSet(int16_t data, int16_t flags) {
return (data & flags) == flags;
}
void AdbConnectionState::CloseFds() {
{
// Lock the write_event_fd so that concurrent PublishDdms will see that the connection is
// closed.
ScopedEventFdLock lk(adb_write_event_fd_);
// shutdown(adb_connection_socket_, SHUT_RDWR);
adb_connection_socket_.reset();
}
// If we didn't load anything we will need to do the handshake again.
performed_handshake_ = false;
// If the agent isn't loaded we might need to tell ddms code the connection is closed.
if (!agent_loaded_ && notified_ddm_active_) {
NotifyDdms(/*active=*/false);
}
}
void AdbConnectionState::NotifyDdms(bool active) {
art::ScopedObjectAccess soa(art::Thread::Current());
DCHECK_NE(notified_ddm_active_, active);
notified_ddm_active_ = active;
if (active) {
art::Dbg::DdmConnected();
} else {
art::Dbg::DdmDisconnected();
}
}
uint32_t AdbConnectionState::NextDdmId() {
// Just have a normal counter but always set the sign bit.
return (next_ddm_id_++) | 0x80000000;
}
void AdbConnectionState::PublishDdmData(uint32_t type, const art::ArrayRef<const uint8_t>& data) {
SendDdmPacket(NextDdmId(), DdmPacketType::kCmd, type, data);
}
void AdbConnectionState::SendDdmPacket(uint32_t id,
DdmPacketType packet_type,
uint32_t type,
art::ArrayRef<const uint8_t> data) {
// Get the write_event early to fail fast.
ScopedEventFdLock lk(adb_write_event_fd_);
if (adb_connection_socket_ == -1 || !performed_handshake_) {
VLOG(jdwp) << "Not sending ddms data of type "
<< StringPrintf("%c%c%c%c",
static_cast<char>(type >> 24),
static_cast<char>(type >> 16),
static_cast<char>(type >> 8),
static_cast<char>(type)) << " due to no connection!";
// Adb is not connected.
return;
}
// the adb_write_event_fd_ will ensure that the adb_connection_socket_ will not go away until
// after we have sent our data.
static constexpr uint32_t kDdmPacketHeaderSize =
kJdwpHeaderLen // jdwp command packet size
+ sizeof(uint32_t) // Type
+ sizeof(uint32_t); // length
alignas(sizeof(uint32_t)) std::array<uint8_t, kDdmPacketHeaderSize> pkt;
uint8_t* pkt_data = pkt.data();
// Write the length first.
*reinterpret_cast<uint32_t*>(pkt_data) = htonl(kDdmPacketHeaderSize + data.size());
pkt_data += sizeof(uint32_t);
// Write the id next;
*reinterpret_cast<uint32_t*>(pkt_data) = htonl(id);
pkt_data += sizeof(uint32_t);
// next the flags. (0 for cmd packet because DDMS).
*(pkt_data++) = static_cast<uint8_t>(packet_type);
switch (packet_type) {
case DdmPacketType::kCmd: {
// Now the cmd-set
*(pkt_data++) = kJdwpDdmCmdSet;
// Now the command
*(pkt_data++) = kJdwpDdmCmd;
break;
}
case DdmPacketType::kReply: {
// This is the error code bytes which are all 0
*(pkt_data++) = 0;
*(pkt_data++) = 0;
}
}
// These are at unaligned addresses so we need to do them manually.
// now the type.
uint32_t net_type = htonl(type);
memcpy(pkt_data, &net_type, sizeof(net_type));
pkt_data += sizeof(uint32_t);
// Now the data.size()
uint32_t net_len = htonl(data.size());
memcpy(pkt_data, &net_len, sizeof(net_len));
pkt_data += sizeof(uint32_t);
static uint32_t constexpr kIovSize = 2;
struct iovec iovs[kIovSize] = {
{ pkt.data(), pkt.size() },
{ const_cast<uint8_t*>(data.data()), data.size() },
};
// now pkt_header has the header.
// use writev to send the actual data.
ssize_t res = TEMP_FAILURE_RETRY(writev(adb_connection_socket_, iovs, kIovSize));
if (static_cast<size_t>(res) != (kDdmPacketHeaderSize + data.size())) {
PLOG(ERROR) << StringPrintf("Failed to send DDMS packet %c%c%c%c to debugger (%zd of %zu)",
static_cast<char>(type >> 24),
static_cast<char>(type >> 16),
static_cast<char>(type >> 8),
static_cast<char>(type),
res, data.size() + kDdmPacketHeaderSize);
} else {
VLOG(jdwp) << StringPrintf("sent DDMS packet %c%c%c%c to debugger %zu",
static_cast<char>(type >> 24),
static_cast<char>(type >> 16),
static_cast<char>(type >> 8),
static_cast<char>(type),
data.size() + kDdmPacketHeaderSize);
}
}
void AdbConnectionState::SendAgentFds(bool require_handshake) {
DCHECK(!sent_agent_fds_);
const char* message = require_handshake ? kPerformHandshakeMessage : kSkipHandshakeMessage;
union {
cmsghdr cm;
char buffer[CMSG_SPACE(dt_fd_forward::FdSet::kDataLength)];
} cm_un;
iovec iov;
iov.iov_base = const_cast<char*>(message);
iov.iov_len = strlen(message) + 1;
msghdr msg;
msg.msg_name = nullptr;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = cm_un.buffer;
msg.msg_controllen = sizeof(cm_un.buffer);
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(dt_fd_forward::FdSet::kDataLength);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
// Duplicate the fds before sending them.
android::base::unique_fd read_fd(art::DupCloexec(adb_connection_socket_));
CHECK_NE(read_fd.get(), -1) << "Failed to dup read_fd_: " << strerror(errno);
android::base::unique_fd write_fd(art::DupCloexec(adb_connection_socket_));
CHECK_NE(write_fd.get(), -1) << "Failed to dup write_fd: " << strerror(errno);
android::base::unique_fd write_lock_fd(art::DupCloexec(adb_write_event_fd_));
CHECK_NE(write_lock_fd.get(), -1) << "Failed to dup write_lock_fd: " << strerror(errno);
dt_fd_forward::FdSet {
read_fd.get(), write_fd.get(), write_lock_fd.get()
}.WriteData(CMSG_DATA(cmsg));
int res = TEMP_FAILURE_RETRY(sendmsg(local_agent_control_sock_, &msg, MSG_EOR));
if (res < 0) {
PLOG(ERROR) << "Failed to send agent adb connection fds.";
} else {
sent_agent_fds_ = true;
VLOG(jdwp) << "Fds have been sent to jdwp agent!";
}
}
android::base::unique_fd AdbConnectionState::ReadFdFromAdb() {
return android::base::unique_fd(adbconnection_client_receive_jdwp_fd(control_ctx_.get()));
}
bool AdbConnectionState::SetupAdbConnection() {
int sleep_ms = 500;
const int sleep_max_ms = 2 * 1000;
const char* isa = GetInstructionSetString(art::Runtime::Current()->GetInstructionSet());
const AdbConnectionClientInfo infos[] = {
{.type = AdbConnectionClientInfoType::pid,
.data.pid = static_cast<uint64_t>(getpid())},
{.type = AdbConnectionClientInfoType::debuggable,
.data.debuggable = IsDebuggingPossible()},
{.type = AdbConnectionClientInfoType::profileable,
.data.profileable = art::Runtime::Current()->IsProfileableFromShell()},
{.type = AdbConnectionClientInfoType::architecture,
// GetInstructionSetString() returns a null-terminating C-style string.
.data.architecture.name = isa,
.data.architecture.size = strlen(isa)},
};
const AdbConnectionClientInfo *info_ptrs[] = {&infos[0], &infos[1], &infos[2], &infos[3]};
while (!shutting_down_) {
// If adbd isn't running, because USB debugging was disabled or
// perhaps the system is restarting it for "adb root", the
// connect() will fail. We loop here forever waiting for it
// to come back.
//
// Waking up and polling every couple of seconds is generally a
// bad thing to do, but we only do this if the application is
// debuggable *and* adbd isn't running. Still, for the sake
// of battery life, we should consider timing out and giving
// up after a few minutes in case somebody ships an app with
// the debuggable flag set.
control_ctx_.reset(adbconnection_client_new(info_ptrs, std::size(infos)));
if (control_ctx_) {
return true;
}
// We failed to connect.
usleep(sleep_ms * 1000);
sleep_ms += (sleep_ms >> 1);
if (sleep_ms > sleep_max_ms) {
sleep_ms = sleep_max_ms;
}
}
return false;
}
void AdbConnectionState::RunPollLoop(art::Thread* self) {
DCHECK(IsDebuggingPossible() || art::Runtime::Current()->IsProfileableFromShell());
CHECK_NE(agent_name_, "");
CHECK_EQ(self->GetState(), art::ThreadState::kNative);
art::Locks::mutator_lock_->AssertNotHeld(self);
self->SetState(art::ThreadState::kWaitingInMainDebuggerLoop);
// shutting_down_ set by StopDebuggerThreads
while (!shutting_down_) {
// First, connect to adbd if we haven't already.
if (!control_ctx_ && !SetupAdbConnection()) {
LOG(ERROR) << "Failed to setup adb connection.";
return;
}
while (!shutting_down_ && control_ctx_) {
bool should_listen_on_connection = !agent_has_socket_ && !sent_agent_fds_;
struct pollfd pollfds[4] = {
{ sleep_event_fd_, POLLIN, 0 },
// -1 as an fd causes it to be ignored by poll
{ (agent_loaded_ ? local_agent_control_sock_ : -1), POLLIN, 0 },
// Check for the control_sock_ actually going away. Only do this if we don't have an active
// connection.
{ (adb_connection_socket_ == -1 ? adbconnection_client_pollfd(control_ctx_.get()) : -1),
POLLIN | POLLRDHUP, 0 },
// if we have not loaded the agent either the adb_connection_socket_ is -1 meaning we don't
// have a real connection yet or the socket through adb needs to be listened to for incoming
// data that the agent or this plugin can handle.
{ should_listen_on_connection ? adb_connection_socket_ : -1, POLLIN | POLLRDHUP, 0 }
};
int res = TEMP_FAILURE_RETRY(poll(pollfds, 4, -1));
if (res < 0) {
PLOG(ERROR) << "Failed to poll!";
return;
}
// We don't actually care about doing this we just use it to wake us up.
// const struct pollfd& sleep_event_poll = pollfds[0];
const struct pollfd& agent_control_sock_poll = pollfds[1];
const struct pollfd& control_sock_poll = pollfds[2];
const struct pollfd& adb_socket_poll = pollfds[3];
if (FlagsSet(agent_control_sock_poll.revents, POLLIN)) {
CHECK(IsDebuggingPossible()); // This path is unexpected for a profileable process.
DCHECK(agent_loaded_);
char buf[257];
res = TEMP_FAILURE_RETRY(recv(local_agent_control_sock_, buf, sizeof(buf) - 1, 0));
if (res < 0) {
PLOG(ERROR) << "Failed to read message from agent control socket! Retrying";
continue;
} else {
buf[res + 1] = '\0';
VLOG(jdwp) << "Local agent control sock has data: " << static_cast<const char*>(buf);
}
if (memcmp(kListenStartMessage, buf, sizeof(kListenStartMessage)) == 0) {
agent_listening_ = true;
if (adb_connection_socket_ != -1) {
SendAgentFds(/*require_handshake=*/ !performed_handshake_);
}
} else if (memcmp(kListenEndMessage, buf, sizeof(kListenEndMessage)) == 0) {
agent_listening_ = false;
} else if (memcmp(kHandshakeCompleteMessage, buf, sizeof(kHandshakeCompleteMessage)) == 0) {
if (agent_has_socket_) {
performed_handshake_ = true;
}
} else if (memcmp(kCloseMessage, buf, sizeof(kCloseMessage)) == 0) {
CloseFds();
agent_has_socket_ = false;
} else if (memcmp(kAcceptMessage, buf, sizeof(kAcceptMessage)) == 0) {
agent_has_socket_ = true;
sent_agent_fds_ = false;
// We will only ever do the handshake once so reset this.
performed_handshake_ = false;
} else {
LOG(ERROR) << "Unknown message received from debugger! '" << std::string(buf) << "'";
}
} else if (FlagsSet(control_sock_poll.revents, POLLIN)) {
if (!IsDebuggingPossible()) {
// For a profielable process, this path can execute when the adbd restarts.
control_ctx_.reset();
break;
}
bool maybe_send_fds = false;
{
// Hold onto this lock so that concurrent ddm publishes don't try to use an illegal fd.
ScopedEventFdLock sefdl(adb_write_event_fd_);
android::base::unique_fd new_fd(adbconnection_client_receive_jdwp_fd(control_ctx_.get()));
if (new_fd == -1) {
// Something went wrong. We need to retry getting the control socket.
control_ctx_.reset();
break;
} else if (adb_connection_socket_ != -1) {
// We already have a connection.
VLOG(jdwp) << "Ignoring second debugger. Accept then drop!";
if (new_fd >= 0) {
new_fd.reset();
}
} else {
VLOG(jdwp) << "Adb connection established with fd " << new_fd;
adb_connection_socket_ = std::move(new_fd);
maybe_send_fds = true;
}
}
if (maybe_send_fds && agent_loaded_ && agent_listening_) {
VLOG(jdwp) << "Sending fds as soon as we received them.";
// The agent was already loaded so this must be after a disconnection. Therefore have the
// transport perform the handshake.
SendAgentFds(/*require_handshake=*/ true);
}
} else if (FlagsSet(control_sock_poll.revents, POLLRDHUP)) {
// The other end of the adb connection just dropped it.
// Reset the connection since we don't have an active socket through the adb server.
// Note this path is expected for either debuggable or profileable processes.
DCHECK(!agent_has_socket_) << "We shouldn't be doing anything if there is already a "
<< "connection active";
control_ctx_.reset();
break;
} else if (FlagsSet(adb_socket_poll.revents, POLLIN)) {
CHECK(IsDebuggingPossible()); // This path is unexpected for a profileable process.
DCHECK(!agent_has_socket_);
if (!agent_loaded_) {
HandleDataWithoutAgent(self);
} else if (agent_listening_ && !sent_agent_fds_) {
VLOG(jdwp) << "Sending agent fds again on data.";
// Agent was already loaded so it can deal with the handshake.
SendAgentFds(/*require_handshake=*/ true);
}
} else if (FlagsSet(adb_socket_poll.revents, POLLRDHUP)) {
CHECK(IsDebuggingPossible()); // This path is unexpected for a profileable process.
DCHECK(!agent_has_socket_);
CloseFds();
} else {
VLOG(jdwp) << "Woke up poll without anything to do!";
}
}
}
}
static uint32_t ReadUint32AndAdvance(/*in-out*/uint8_t** in) {
uint32_t res;
memcpy(&res, *in, sizeof(uint32_t));
*in = (*in) + sizeof(uint32_t);
return ntohl(res);
}
void AdbConnectionState::HandleDataWithoutAgent(art::Thread* self) {
DCHECK(!agent_loaded_);
DCHECK(!agent_listening_);
// TODO Should we check in some other way if we are userdebug/eng?
CHECK(art::Dbg::IsJdwpAllowed());
// We try to avoid loading the agent which is expensive. First lets just perform the handshake.
if (!performed_handshake_) {
PerformHandshake();
return;
}
// Read the packet header to figure out if it is one we can handle. We only 'peek' into the stream
// to see if it's one we can handle. This doesn't change the state of the socket.
alignas(sizeof(uint32_t)) uint8_t packet_header[kPacketHeaderLen];
ssize_t res = TEMP_FAILURE_RETRY(recv(adb_connection_socket_.get(),
packet_header,
sizeof(packet_header),
MSG_PEEK));
// We want to be very careful not to change the socket state until we know we succeeded. This will
// let us fall-back to just loading the agent and letting it deal with everything.
if (res <= 0) {
// Close the socket. We either hit EOF or an error.
if (res < 0) {
PLOG(ERROR) << "Unable to peek into adb socket due to error. Closing socket.";
}
CloseFds();
return;
} else if (res < static_cast<int>(kPacketHeaderLen)) {
LOG(ERROR) << "Unable to peek into adb socket. Loading agent to handle this. Only read " << res;
AttachJdwpAgent(self);
return;
}
uint32_t full_len = ntohl(*reinterpret_cast<uint32_t*>(packet_header + kPacketSizeOff));
uint32_t pkt_id = ntohl(*reinterpret_cast<uint32_t*>(packet_header + kPacketIdOff));
uint8_t pkt_cmd_set = packet_header[kPacketCommandSetOff];
uint8_t pkt_cmd = packet_header[kPacketCommandOff];
if (pkt_cmd_set != kDdmCommandSet ||
pkt_cmd != kDdmChunkCommand ||
full_len < kPacketHeaderLen) {
VLOG(jdwp) << "Loading agent due to jdwp packet that cannot be handled by adbconnection.";
AttachJdwpAgent(self);
return;
}
uint32_t avail = -1;
res = TEMP_FAILURE_RETRY(ioctl(adb_connection_socket_.get(), FIONREAD, &avail));
if (res < 0) {
PLOG(ERROR) << "Failed to determine amount of readable data in socket! Closing connection";
CloseFds();
return;
} else if (avail < full_len) {
LOG(WARNING) << "Unable to handle ddm command in adbconnection due to insufficent data. "
<< "Expected " << full_len << " bytes but only " << avail << " are readable. "
<< "Loading jdwp agent to deal with this.";
AttachJdwpAgent(self);
return;
}
// Actually read the data.
std::vector<uint8_t> full_pkt;
full_pkt.resize(full_len);
res = TEMP_FAILURE_RETRY(recv(adb_connection_socket_.get(), full_pkt.data(), full_len, 0));
if (res < 0) {
PLOG(ERROR) << "Failed to recv data from adb connection. Closing connection";
CloseFds();
return;
}
DCHECK_EQ(memcmp(full_pkt.data(), packet_header, sizeof(packet_header)), 0);
size_t data_size = full_len - kPacketHeaderLen;
if (data_size < (sizeof(uint32_t) * 2)) {
// This is an error (the data isn't long enough) but to match historical behavior we need to
// ignore it.
return;
}
uint8_t* ddm_data = full_pkt.data() + kPacketHeaderLen;
uint32_t ddm_type = ReadUint32AndAdvance(&ddm_data);
uint32_t ddm_len = ReadUint32AndAdvance(&ddm_data);
if (ddm_len > data_size - (2 * sizeof(uint32_t))) {
// This is an error (the data isn't long enough) but to match historical behavior we need to
// ignore it.
return;
}
if (!notified_ddm_active_) {
NotifyDdms(/*active=*/ true);
}
uint32_t reply_type;
std::vector<uint8_t> reply;
if (!art::Dbg::DdmHandleChunk(self->GetJniEnv(),
ddm_type,
art::ArrayRef<const jbyte>(reinterpret_cast<const jbyte*>(ddm_data),
ddm_len),
/*out*/&reply_type,
/*out*/&reply)) {
// To match historical behavior we don't send any response when there is no data to reply with.
return;
}
SendDdmPacket(pkt_id,
DdmPacketType::kReply,
reply_type,
art::ArrayRef<const uint8_t>(reply));
}
void AdbConnectionState::PerformHandshake() {
CHECK(!performed_handshake_);
// Check to make sure we are able to read the whole handshake.
uint32_t avail = -1;
int res = TEMP_FAILURE_RETRY(ioctl(adb_connection_socket_.get(), FIONREAD, &avail));
if (res < 0 || avail < sizeof(kJdwpHandshake)) {
if (res < 0) {
PLOG(ERROR) << "Failed to determine amount of readable data for handshake!";
}
LOG(WARNING) << "Closing connection to broken client.";
CloseFds();
return;
}
// Perform the handshake.
char handshake_msg[sizeof(kJdwpHandshake)];
res = TEMP_FAILURE_RETRY(recv(adb_connection_socket_.get(),
handshake_msg,
sizeof(handshake_msg),
MSG_DONTWAIT));
if (res < static_cast<int>(sizeof(kJdwpHandshake)) ||
strncmp(handshake_msg, kJdwpHandshake, sizeof(kJdwpHandshake)) != 0) {
if (res < 0) {
PLOG(ERROR) << "Failed to read handshake!";
}
LOG(WARNING) << "Handshake failed!";
CloseFds();
return;
}
// Send the handshake back.
res = TEMP_FAILURE_RETRY(send(adb_connection_socket_.get(),
kJdwpHandshake,
sizeof(kJdwpHandshake),
0));
if (res < static_cast<int>(sizeof(kJdwpHandshake))) {
PLOG(ERROR) << "Failed to send jdwp-handshake response.";
CloseFds();
return;
}
performed_handshake_ = true;
}
void AdbConnectionState::AttachJdwpAgent(art::Thread* self) {
art::Runtime* runtime = art::Runtime::Current();
self->AssertNoPendingException();
runtime->AttachAgent(/* env= */ nullptr,
MakeAgentArg(),
/* class_loader= */ nullptr);
if (self->IsExceptionPending()) {
LOG(ERROR) << "Failed to load agent " << agent_name_;
art::ScopedObjectAccess soa(self);
self->GetException()->Dump();
self->ClearException();
return;
}
agent_loaded_ = true;
}
bool ContainsArgument(const std::string& opts, const char* arg) {
return opts.find(arg) != std::string::npos;
}
bool ValidateJdwpOptions(const std::string& opts) {
bool res = true;
// The adbconnection plugin requires that the jdwp agent be configured as a 'server' because that
// is what adb expects and otherwise we will hit a deadlock as the poll loop thread stops waiting
// for the fd's to be passed down.
if (ContainsArgument(opts, "server=n")) {
res = false;
LOG(ERROR) << "Cannot start jdwp debugging with server=n from adbconnection.";
}
// We don't start the jdwp agent until threads are already running. It is far too late to suspend
// everything.
if (ContainsArgument(opts, "suspend=y")) {
res = false;
LOG(ERROR) << "Cannot use suspend=y with late-init jdwp.";
}
return res;
}
std::string AdbConnectionState::MakeAgentArg() {
const std::string& opts = art::Runtime::Current()->GetJdwpOptions();
DCHECK(ValidateJdwpOptions(opts));
// TODO Get agent_name_ from something user settable?
return agent_name_ + "=" + opts + (opts.empty() ? "" : ",") +
"ddm_already_active=" + (notified_ddm_active_ ? "y" : "n") + "," +
// See the comment above for why we need to be server=y. Since the agent defaults to server=n
// we will add it if it wasn't already present for the convenience of the user.
(ContainsArgument(opts, "server=y") ? "" : "server=y,") +
// See the comment above for why we need to be suspend=n. Since the agent defaults to
// suspend=y we will add it if it wasn't already present.
(ContainsArgument(opts, "suspend=n") ? "" : "suspend=n,") +
"transport=dt_fd_forward,address=" + std::to_string(remote_agent_control_sock_);
}
void AdbConnectionState::StopDebuggerThreads() {
// The regular agent system will take care of unloading the agent (if needed).
shutting_down_ = true;
// Wakeup the poll loop.
uint64_t data = 1;
if (sleep_event_fd_ != -1) {
TEMP_FAILURE_RETRY(write(sleep_event_fd_, &data, sizeof(data)));
}
}
// The plugin initialization function.
extern "C" bool ArtPlugin_Initialize() {
DCHECK(art::Runtime::Current()->GetJdwpProvider() == art::JdwpProvider::kAdbConnection);
// TODO Provide some way for apps to set this maybe?
gState.emplace(kDefaultJdwpAgentName);
return ValidateJdwpOptions(art::Runtime::Current()->GetJdwpOptions());
}
extern "C" bool ArtPlugin_Deinitialize() {
// We don't actually have to do anything here. The debugger (if one was
// attached) was shutdown by the move to the kDeath runtime phase and the
// adbconnection threads were shutdown by StopDebugger.
return true;
}
} // namespace adbconnection

View File

@ -0,0 +1,179 @@
/*
* Copyright (C) 2017 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.
*/
#ifndef ART_ADBCONNECTION_ADBCONNECTION_H_
#define ART_ADBCONNECTION_ADBCONNECTION_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include <limits>
#include "android-base/unique_fd.h"
#include "adbconnection/client.h"
#include "base/mutex.h"
#include "base/array_ref.h"
#include "runtime_callbacks.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <jni.h>
namespace adbconnection {
static constexpr char kJdwpControlName[] = "\0jdwp-control";
static constexpr char kAdbConnectionThreadName[] = "ADB-JDWP Connection Control Thread";
// The default jdwp agent name.
static constexpr char kDefaultJdwpAgentName[] = "libjdwp.so";
class AdbConnectionState;
struct AdbConnectionDebuggerController : public art::DebuggerControlCallback {
explicit AdbConnectionDebuggerController(AdbConnectionState* connection)
: connection_(connection) {}
// Begin running the debugger.
void StartDebugger() override;
// The debugger should begin shutting down since the runtime is ending.
void StopDebugger() override;
bool IsDebuggerConfigured() override;
private:
AdbConnectionState* connection_;
};
enum class DdmPacketType : uint8_t { kReply = 0x80, kCmd = 0x00, };
struct AdbConnectionDdmCallback : public art::DdmCallback {
explicit AdbConnectionDdmCallback(AdbConnectionState* connection) : connection_(connection) {}
void DdmPublishChunk(uint32_t type,
const art::ArrayRef<const uint8_t>& data)
REQUIRES_SHARED(art::Locks::mutator_lock_);
private:
AdbConnectionState* connection_;
};
class AdbConnectionState {
public:
explicit AdbConnectionState(const std::string& name);
~AdbConnectionState();
// Called on the listening thread to start dealing with new input. thr is used to attach the new
// thread to the runtime.
void RunPollLoop(art::Thread* self);
// Sends ddms data over the socket, if there is one. This data is sent even if we haven't finished
// hand-shaking yet.
void PublishDdmData(uint32_t type, const art::ArrayRef<const uint8_t>& data);
// Stops debugger threads during shutdown.
void StopDebuggerThreads();
// If StartDebuggerThreads was called successfully.
bool DebuggerThreadsStarted() {
return started_debugger_threads_;
}
private:
uint32_t NextDdmId();
void StartDebuggerThreads();
// Tell adbd about the new runtime.
bool SetupAdbConnection();
std::string MakeAgentArg();
android::base::unique_fd ReadFdFromAdb();
void SendAgentFds(bool require_handshake);
void CloseFds();
void HandleDataWithoutAgent(art::Thread* self);
void PerformHandshake();
void AttachJdwpAgent(art::Thread* self);
void NotifyDdms(bool active);
void SendDdmPacket(uint32_t id,
DdmPacketType type,
uint32_t ddm_type,
art::ArrayRef<const uint8_t> data);
std::string agent_name_;
AdbConnectionDebuggerController controller_;
AdbConnectionDdmCallback ddm_callback_;
// Eventfd used to allow the StopDebuggerThreads function to wake up sleeping threads
android::base::unique_fd sleep_event_fd_;
// Context which wraps the socket which we use to talk to adbd.
std::unique_ptr<AdbConnectionClientContext, void(*)(AdbConnectionClientContext*)> control_ctx_;
// Socket that we use to talk to the agent (if it's loaded).
android::base::unique_fd local_agent_control_sock_;
// The fd of the socket the agent uses to talk to us. We need to keep it around in order to clean
// it up when the runtime goes away.
android::base::unique_fd remote_agent_control_sock_;
// The fd that is forwarded through adb to the client. This is guarded by the
// adb_write_event_fd_.
android::base::unique_fd adb_connection_socket_;
// The fd we send to the agent to let us synchronize access to the shared adb_connection_socket_.
// This is also used as a general lock for the adb_connection_socket_ on any threads other than
// the poll thread.
android::base::unique_fd adb_write_event_fd_;
std::atomic<bool> shutting_down_;
// True if we have loaded the agent library.
std::atomic<bool> agent_loaded_;
// True if the dt_fd_forward transport is listening for a new communication channel.
std::atomic<bool> agent_listening_;
// True if the dt_fd_forward transport has the socket. If so we don't do anything to the agent or
// the adb connection socket until connection goes away.
std::atomic<bool> agent_has_socket_;
std::atomic<bool> sent_agent_fds_;
std::atomic<bool> performed_handshake_;
bool notified_ddm_active_;
std::atomic<uint32_t> next_ddm_id_;
bool started_debugger_threads_;
friend struct AdbConnectionDebuggerController;
};
} // namespace adbconnection
#endif // ART_ADBCONNECTION_ADBCONNECTION_H_

52
artd/Android.bp Normal file
View File

@ -0,0 +1,52 @@
//
// Copyright (C) 2021 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "art_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
art_cc_binary {
name: "artd",
defaults: ["art_defaults"],
srcs: [
"artd.cc",
],
shared_libs: [
"artd-aidl-ndk",
"libartbase",
"libarttools",
"libbase",
"libbinder_ndk",
],
apex_available: [
"com.android.art",
"com.android.art.debug",
],
}
prebuilt_etc {
name: "com.android.art.artd.init.rc",
src: "artd.rc",
filename: "init.rc",
installable: false,
}

87
artd/artd.cc Normal file
View File

@ -0,0 +1,87 @@
/*
** Copyright 2021, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <string>
#define LOG_TAG "artd"
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <unistd.h>
#include <utils/Errors.h>
#include "aidl/android/os/BnArtd.h"
#include "base/logging.h"
#include "base/macros.h"
#include "tools/tools.h"
using ::ndk::ScopedAStatus;
namespace android {
namespace artd {
class Artd : public aidl::android::os::BnArtd {
constexpr static const char* const SERVICE_NAME = "artd";
public:
Artd() {}
/*
* Binder API
*/
ScopedAStatus isAlive(bool* _aidl_return) {
*_aidl_return = true;
return ScopedAStatus::ok();
}
/*
* Server API
*/
ScopedAStatus Start() {
LOG(INFO) << "Starting artd";
status_t ret = AServiceManager_addService(this->asBinder().get(), SERVICE_NAME);
if (ret != android::OK) {
return ScopedAStatus::fromStatus(ret);
}
ABinderProcess_startThreadPool();
return ScopedAStatus::ok();
}
};
} // namespace artd
} // namespace android
int main(const int argc __attribute__((unused)), char* argv[]) {
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(argv);
android::artd::Artd artd;
if (auto ret = artd.Start(); !ret.isOk()) {
LOG(ERROR) << "Unable to start artd: " << ret.getMessage();
exit(1);
}
ABinderProcess_joinThreadPool();
LOG(INFO) << "artd shutting down";
return 0;
}

19
artd/artd.rc Normal file
View File

@ -0,0 +1,19 @@
# Copyright (C) 2021 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.
# This service is disabled until b/192042812 is resolved.
service artd /apex/com.android.art/bin/artd
disabled
class core
user artd

53
artd/binder/Android.bp Normal file
View File

@ -0,0 +1,53 @@
//
// Copyright (C) 2021 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "art_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
aidl_interface {
name: "artd-aidl",
srcs: [
"android/os/IArtd.aidl",
],
host_supported: true,
backend: {
java: {
enabled: true,
},
cpp: {
enabled: false,
},
ndk: {
enabled: true,
apex_available: [
"com.android.art",
"com.android.art.debug",
"com.android.compos",
],
min_sdk_version: "31",
},
},
unstable: true,
visibility: [
"//system/tools/aidl/build",
"//art:__subpackages__",
],
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2021 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 android.os;
/** {@hide} */
interface IArtd {
// Test to see if the artd service is available.
boolean isAlive();
}

36
artd/tests/Android.bp Normal file
View File

@ -0,0 +1,36 @@
//
// Copyright (C) 2021 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
android_test {
name: "ArtdIntegrationTests",
srcs: ["src/**/*.java"],
static_libs: [
"androidx.test.rules",
"artd-aidl-java",
],
platform_apis: true,
test_suites: ["device-tests"],
certificate: "platform",
}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.art.artdtest">
<!-- Uses API introduced in O (26) -->
<uses-sdk
android:minSdkVersion="1"
android:targetSdkVersion="26" />
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.art.artdtest"
android:label="Integration test for Artd" />
</manifest>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<configuration description="Runs Artd Integration Tests">
<!--Test app needs to be installed when we change its settings below-->
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="test-file-name" value="ArtdIntegrationTests.apk"/>
<option name="cleanup-apks" value="true"/>
</target_preparer>
<option name="test-suite-tag" value="apct"/>
<option name="test-tag" value="ArtdIntegrationTests"/>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.art.artdtest"/>
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
</test>
<!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
</configuration>

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2021 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 com.android.art;
import android.os.IArtd;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.lang.IllegalStateException;
/**
* Integration tests for Artd.
*
* Run with "atest ArtdIntegrationTests".
*/
@RunWith(JUnit4.class)
public final class ArtdIntegrationTests {
private static final String TAG = "ArtdTests";
private static IArtd mArtd;
@Before
public void setUp() {
IBinder rawBinderService = ServiceManager.getService("artd");
if (rawBinderService == null) {
throw new IllegalStateException("Unable to fetch artd service from binder.");
}
mArtd = IArtd.Stub.asInterface(rawBinderService);
}
// Test basic build and publish functionality for Artd.
@Test
public void testLiveness() throws RemoteException {
Assert.assertTrue(mArtd.isAlive());
}
}

84
benchmark/Android.bp Normal file
View File

@ -0,0 +1,84 @@
//
// Copyright (C) 2015 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "art_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
art_cc_library {
name: "libartbenchmark",
host_supported: true,
defaults: ["art_defaults"],
srcs: [
"jni_loader.cc",
"jobject-benchmark/jobject_benchmark.cc",
"jni-perf/perf_jni.cc",
"micro-native/micro_native.cc",
"scoped-primitive-array/scoped_primitive_array.cc",
],
target: {
// This has to be duplicated for android and host to make sure it
// comes after the -Wframe-larger-than warnings inserted by art.go
// target-specific properties
android: {
cflags: ["-Wno-frame-larger-than="],
},
host: {
cflags: ["-Wno-frame-larger-than="],
},
},
header_libs: [
"libnativehelper_header_only",
],
// TODO(ngeoffray): find a way to link against the libraries in the apex.
shared_libs: [
"libart",
"libbase",
],
}
art_cc_library {
name: "libartbenchmark-micronative-host",
host_supported: true,
device_supported: false,
defaults: ["art_debug_defaults"],
srcs: [
"jni_loader.cc",
"micro-native/micro_native.cc",
],
shared_libs: [
],
static_libs: [
],
header_libs: ["jni_headers"],
stl: "libc++_static",
target: {
// This has to be duplicated for android and host to make sure it
// comes after the -Wframe-larger-than warnings inserted by art.go
// target-specific properties
android: {
cflags: ["-Wno-frame-larger-than="],
},
host: {
cflags: ["-Wno-frame-larger-than="],
},
},
}

View File

@ -0,0 +1 @@
Benchmarks for repeating const-class instructions in a loop.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
Benchmarks for repeating const-string instructions in a loop.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
Tests for measuring performance of JNI state changes.

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include "jni.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
namespace art {
namespace {
extern "C" JNIEXPORT void JNICALL Java_JniPerfBenchmark_perfJniEmptyCall(JNIEnv*, jobject) {}
extern "C" JNIEXPORT void JNICALL Java_JniPerfBenchmark_perfSOACall(JNIEnv* env, jobject) {
ScopedObjectAccess soa(env);
}
extern "C" JNIEXPORT void JNICALL Java_JniPerfBenchmark_perfSOAUncheckedCall(JNIEnv*, jobject) {
ScopedObjectAccessUnchecked soa(Thread::Current());
}
} // namespace
} // namespace art

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2015 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.
*/
public class JniPerfBenchmark {
private static final String MSG = "ABCDE";
native void perfJniEmptyCall();
native void perfSOACall();
native void perfSOAUncheckedCall();
public void timeFastJNI(int N) {
// TODO: This might be an intrinsic.
for (long i = 0; i < N; i++) {
char c = MSG.charAt(2);
}
}
public void timeEmptyCall(int N) {
for (long i = 0; i < N; i++) {
perfJniEmptyCall();
}
}
public void timeSOACall(int N) {
for (long i = 0; i < N; i++) {
perfSOACall();
}
}
public void timeSOAUncheckedCall(int N) {
for (long i = 0; i < N; i++) {
perfSOAUncheckedCall();
}
}
{
System.loadLibrary("artbenchmark");
}
}

32
benchmark/jni_loader.cc Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
extern void register_micro_native_methods(JNIEnv* env);
jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
// List of functions to call to register methods explicitly.
// Otherwise we use the regular JNI naming conventions to register implicitly.
register_micro_native_methods(env);
return JNI_VERSION_1_6;
}

View File

@ -0,0 +1,7 @@
Benchmark for jobject functions
Measures performance of:
Add/RemoveLocalRef
Add/RemoveGlobalRef
Add/RemoveWeakGlobalRef
Decoding local, weak, global, handle scope jobjects.

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "jni.h"
#include "jni/java_vm_ext.h"
#include "mirror/class-inl.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
namespace {
extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeAddRemoveLocal(
JNIEnv* env, jobject jobj, jint reps) {
ScopedObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
CHECK(obj != nullptr);
for (jint i = 0; i < reps; ++i) {
jobject ref = soa.Env()->AddLocalReference<jobject>(obj);
soa.Env()->DeleteLocalRef(ref);
}
}
extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeDecodeLocal(
JNIEnv* env, jobject jobj, jint reps) {
ScopedObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
CHECK(obj != nullptr);
jobject ref = soa.Env()->AddLocalReference<jobject>(obj);
for (jint i = 0; i < reps; ++i) {
CHECK_EQ(soa.Decode<mirror::Object>(ref), obj);
}
soa.Env()->DeleteLocalRef(ref);
}
extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeAddRemoveGlobal(
JNIEnv* env, jobject jobj, jint reps) {
ScopedObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
CHECK(obj != nullptr);
for (jint i = 0; i < reps; ++i) {
jobject ref = soa.Vm()->AddGlobalRef(soa.Self(), obj);
soa.Vm()->DeleteGlobalRef(soa.Self(), ref);
}
}
extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeDecodeGlobal(
JNIEnv* env, jobject jobj, jint reps) {
ScopedObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
CHECK(obj != nullptr);
jobject ref = soa.Vm()->AddGlobalRef(soa.Self(), obj);
for (jint i = 0; i < reps; ++i) {
CHECK_EQ(soa.Decode<mirror::Object>(ref), obj);
}
soa.Vm()->DeleteGlobalRef(soa.Self(), ref);
}
extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeAddRemoveWeakGlobal(
JNIEnv* env, jobject jobj, jint reps) {
ScopedObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
CHECK(obj != nullptr);
for (jint i = 0; i < reps; ++i) {
jobject ref = soa.Vm()->AddWeakGlobalRef(soa.Self(), obj);
soa.Vm()->DeleteWeakGlobalRef(soa.Self(), ref);
}
}
extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeDecodeWeakGlobal(
JNIEnv* env, jobject jobj, jint reps) {
ScopedObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
CHECK(obj != nullptr);
jobject ref = soa.Vm()->AddWeakGlobalRef(soa.Self(), obj);
for (jint i = 0; i < reps; ++i) {
CHECK_EQ(soa.Decode<mirror::Object>(ref), obj);
}
soa.Vm()->DeleteWeakGlobalRef(soa.Self(), ref);
}
extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeDecodeHandleScopeRef(
JNIEnv* env, jobject jobj, jint reps) {
ScopedObjectAccess soa(env);
for (jint i = 0; i < reps; ++i) {
soa.Decode<mirror::Object>(jobj);
}
}
} // namespace
} // namespace art

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2015 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.
*/
public class JObjectBenchmark {
public JObjectBenchmark() {
// Make sure to link methods before benchmark starts.
System.loadLibrary("artbenchmark");
timeAddRemoveLocal(1);
timeDecodeLocal(1);
timeAddRemoveGlobal(1);
timeDecodeGlobal(1);
timeAddRemoveWeakGlobal(1);
timeDecodeWeakGlobal(1);
timeDecodeHandleScopeRef(1);
}
public native void timeAddRemoveLocal(int reps);
public native void timeDecodeLocal(int reps);
public native void timeAddRemoveGlobal(int reps);
public native void timeDecodeGlobal(int reps);
public native void timeAddRemoveWeakGlobal(int reps);
public native void timeDecodeWeakGlobal(int reps);
public native void timeDecodeHandleScopeRef(int reps);
}

View File

@ -0,0 +1,146 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <stdio.h>
#ifndef NATIVE_METHOD
#define NATIVE_METHOD(className, functionName, signature) \
{ #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
#endif
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
#define GLUE4(a, b, c, d) a ## b ## c ## d
#define GLUE4_(a, b, c, d) GLUE4(a, b, c, d)
#define CLASS_NAME "benchmarks/MicroNative/java/NativeMethods"
#define CLASS_INFIX benchmarks_MicroNative_java_NativeMethods
#define NAME_NORMAL_JNI_METHOD(name) GLUE4_(Java_, CLASS_INFIX, _, name)
#define NAME_CRITICAL_JNI_METHOD(name) GLUE4_(JavaCritical_, CLASS_INFIX, _, name)
#define DEFINE_NORMAL_JNI_METHOD(ret, name) extern "C" JNIEXPORT ret JNICALL GLUE4_(Java_, CLASS_INFIX, _, name)
#define DEFINE_CRITICAL_JNI_METHOD(ret, name) extern "C" JNIEXPORT ret JNICALL GLUE4_(JavaCritical_, CLASS_INFIX, _, name)
static void NativeMethods_emptyJniStaticSynchronizedMethod0(JNIEnv*, jclass) { }
static void NativeMethods_emptyJniSynchronizedMethod0(JNIEnv*, jclass) { }
static JNINativeMethod gMethods_NormalOnly[] = {
NATIVE_METHOD(NativeMethods, emptyJniStaticSynchronizedMethod0, "()V"),
NATIVE_METHOD(NativeMethods, emptyJniSynchronizedMethod0, "()V"),
};
static void NativeMethods_emptyJniMethod0(JNIEnv*, jobject) { }
static void NativeMethods_emptyJniMethod6(JNIEnv*, jobject, int, int, int, int, int, int) { }
static void NativeMethods_emptyJniMethod6L(JNIEnv*, jobject, jobject, jarray, jarray, jobject,
jarray, jarray) { }
static void NativeMethods_emptyJniStaticMethod6L(JNIEnv*, jclass, jobject, jarray, jarray, jobject,
jarray, jarray) { }
static void NativeMethods_emptyJniStaticMethod0(JNIEnv*, jclass) { }
static void NativeMethods_emptyJniStaticMethod6(JNIEnv*, jclass, int, int, int, int, int, int) { }
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(NativeMethods, emptyJniMethod0, "()V"),
NATIVE_METHOD(NativeMethods, emptyJniMethod6, "(IIIIII)V"),
NATIVE_METHOD(NativeMethods, emptyJniMethod6L, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"),
NATIVE_METHOD(NativeMethods, emptyJniStaticMethod6L, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"),
NATIVE_METHOD(NativeMethods, emptyJniStaticMethod0, "()V"),
NATIVE_METHOD(NativeMethods, emptyJniStaticMethod6, "(IIIIII)V"),
};
static void NativeMethods_emptyJniMethod0_Fast(JNIEnv*, jobject) { }
static void NativeMethods_emptyJniMethod6_Fast(JNIEnv*, jobject, int, int, int, int, int, int) { }
static void NativeMethods_emptyJniMethod6L_Fast(JNIEnv*, jobject, jobject, jarray, jarray, jobject,
jarray, jarray) { }
static void NativeMethods_emptyJniStaticMethod6L_Fast(JNIEnv*, jclass, jobject, jarray, jarray,
jobject, jarray, jarray) { }
static void NativeMethods_emptyJniStaticMethod0_Fast(JNIEnv*, jclass) { }
static void NativeMethods_emptyJniStaticMethod6_Fast(JNIEnv*, jclass, int, int, int, int, int, int) { }
static JNINativeMethod gMethods_Fast[] = {
NATIVE_METHOD(NativeMethods, emptyJniMethod0_Fast, "()V"),
NATIVE_METHOD(NativeMethods, emptyJniMethod6_Fast, "(IIIIII)V"),
NATIVE_METHOD(NativeMethods, emptyJniMethod6L_Fast, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"),
NATIVE_METHOD(NativeMethods, emptyJniStaticMethod6L_Fast, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"),
NATIVE_METHOD(NativeMethods, emptyJniStaticMethod0_Fast, "()V"),
NATIVE_METHOD(NativeMethods, emptyJniStaticMethod6_Fast, "(IIIIII)V"),
};
// Have both a Java_ and a JavaCritical_ version of the same empty method.
// The runtime automatically selects the right one when doing a dlsym-based native lookup.
DEFINE_NORMAL_JNI_METHOD(void, emptyJniStaticMethod0_1Critical)(JNIEnv*, jclass) { }
DEFINE_CRITICAL_JNI_METHOD(void, emptyJniStaticMethod0_1Critical)() { }
DEFINE_NORMAL_JNI_METHOD(void, emptyJniStaticMethod6_1Critical)(JNIEnv*, jclass, int, int, int, int, int, int) { }
DEFINE_CRITICAL_JNI_METHOD(void, emptyJniStaticMethod6_1Critical)(int, int, int, int, int, int) { }
static JNINativeMethod gMethods_Critical[] = {
// Don't use NATIVE_METHOD because the name is mangled differently.
{ "emptyJniStaticMethod0_Critical", "()V",
reinterpret_cast<void*>(NAME_CRITICAL_JNI_METHOD(emptyJniStaticMethod0_1Critical)) },
{ "emptyJniStaticMethod6_Critical", "(IIIIII)V",
reinterpret_cast<void*>(NAME_CRITICAL_JNI_METHOD(emptyJniStaticMethod6_1Critical)) }
};
void jniRegisterNativeMethods(JNIEnv* env,
const char* className,
const JNINativeMethod* methods,
int numMethods) {
jclass c = env->FindClass(className);
if (c == nullptr) {
char* tmp;
const char* msg;
if (asprintf(&tmp,
"Native registration unable to find class '%s'; aborting...",
className) == -1) {
// Allocation failed, print default warning.
msg = "Native registration unable to find class; aborting...";
} else {
msg = tmp;
}
env->FatalError(msg);
}
if (env->RegisterNatives(c, methods, numMethods) < 0) {
char* tmp;
const char* msg;
if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
// Allocation failed, print default warning.
msg = "RegisterNatives failed; aborting...";
} else {
msg = tmp;
}
env->FatalError(msg);
}
}
void register_micro_native_methods(JNIEnv* env) {
jniRegisterNativeMethods(env, CLASS_NAME, gMethods_NormalOnly, NELEM(gMethods_NormalOnly));
jniRegisterNativeMethods(env, CLASS_NAME, gMethods, NELEM(gMethods));
jniRegisterNativeMethods(env, CLASS_NAME, gMethods_Fast, NELEM(gMethods_Fast));
if (env->FindClass("dalvik/annotation/optimization/CriticalNative") != nullptr) {
// Only register them explicitly if the annotation is present.
jniRegisterNativeMethods(env, CLASS_NAME, gMethods_Critical, NELEM(gMethods_Critical));
} else {
if (env->ExceptionCheck()) {
// It will throw NoClassDefFoundError
env->ExceptionClear();
}
}
// else let them be registered implicitly.
}

View File

@ -0,0 +1 @@
Tests for measuring performance of ScopedPrimitiveArray.

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "jni.h"
#include "nativehelper/scoped_primitive_array.h"
extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureByteArray(
JNIEnv* env, jclass, int reps, jbyteArray arr) {
jlong ret = 0;
for (jint i = 0; i < reps; ++i) {
ScopedByteArrayRO sc(env, arr);
ret += sc[0] + sc[sc.size() - 1];
}
return ret;
}
extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureShortArray(
JNIEnv* env, jclass, int reps, jshortArray arr) {
jlong ret = 0;
for (jint i = 0; i < reps; ++i) {
ScopedShortArrayRO sc(env, arr);
ret += sc[0] + sc[sc.size() - 1];
}
return ret;
}
extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureIntArray(
JNIEnv* env, jclass, int reps, jintArray arr) {
jlong ret = 0;
for (jint i = 0; i < reps; ++i) {
ScopedIntArrayRO sc(env, arr);
ret += sc[0] + sc[sc.size() - 1];
}
return ret;
}
extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureLongArray(
JNIEnv* env, jclass, int reps, jlongArray arr) {
jlong ret = 0;
for (jint i = 0; i < reps; ++i) {
ScopedLongArrayRO sc(env, arr);
ret += sc[0] + sc[sc.size() - 1];
}
return ret;
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2015 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.
*/
public class ScopedPrimitiveArrayBenchmark {
// Measure adds the first and last element of the array by using ScopedPrimitiveArray.
static native long measureByteArray(int reps, byte[] arr);
static native long measureShortArray(int reps, short[] arr);
static native long measureIntArray(int reps, int[] arr);
static native long measureLongArray(int reps, long[] arr);
static final int smallLength = 16;
static final int mediumLength = 256;
static final int largeLength = 8096;
static byte[] smallBytes = new byte[smallLength];
static byte[] mediumBytes = new byte[mediumLength];
static byte[] largeBytes = new byte[largeLength];
static short[] smallShorts = new short[smallLength];
static short[] mediumShorts = new short[mediumLength];
static short[] largeShorts = new short[largeLength];
static int[] smallInts = new int[smallLength];
static int[] mediumInts = new int[mediumLength];
static int[] largeInts = new int[largeLength];
static long[] smallLongs = new long[smallLength];
static long[] mediumLongs = new long[mediumLength];
static long[] largeLongs = new long[largeLength];
public void timeSmallBytes(int reps) {
measureByteArray(reps, smallBytes);
}
public void timeMediumBytes(int reps) {
measureByteArray(reps, mediumBytes);
}
public void timeLargeBytes(int reps) {
measureByteArray(reps, largeBytes);
}
public void timeSmallShorts(int reps) {
measureShortArray(reps, smallShorts);
}
public void timeMediumShorts(int reps) {
measureShortArray(reps, mediumShorts);
}
public void timeLargeShorts(int reps) {
measureShortArray(reps, largeShorts);
}
public void timeSmallInts(int reps) {
measureIntArray(reps, smallInts);
}
public void timeMediumInts(int reps) {
measureIntArray(reps, mediumInts);
}
public void timeLargeInts(int reps) {
measureIntArray(reps, largeInts);
}
public void timeSmallLongs(int reps) {
measureLongArray(reps, smallLongs);
}
public void timeMediumLongs(int reps) {
measureLongArray(reps, mediumLongs);
}
public void timeLargeLongs(int reps) {
measureLongArray(reps, largeLongs);
}
{
System.loadLibrary("artbenchmark");
}
}

View File

@ -0,0 +1 @@
Benchmarks for repeating String.indexOf() instructions in a loop.

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2016 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.
*/
public class StringIndexOfBenchmark {
public static final String string36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // length = 36
public void timeIndexOf0(int count) {
final char c = '0';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOf1(int count) {
final char c = '1';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOf2(int count) {
final char c = '2';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOf3(int count) {
final char c = '3';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOf4(int count) {
final char c = '4';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOf7(int count) {
final char c = '7';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOf8(int count) {
final char c = '8';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOfF(int count) {
final char c = 'F';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOfG(int count) {
final char c = 'G';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOfV(int count) {
final char c = 'V';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOfW(int count) {
final char c = 'W';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
public void timeIndexOf_(int count) {
final char c = '_';
String s = string36;
for (int i = 0; i < count; ++i) {
$noinline$indexOf(s, c);
}
}
static int $noinline$indexOf(String s, char c) {
if (doThrow) { throw new Error(); }
return s.indexOf(c);
}
public static boolean doThrow = false;
}

View File

@ -0,0 +1 @@
Benchmarks for the StringBuilder append pattern.

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2019 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.
*/
public class StringBuilderAppendBenchmark {
public static String string1 = "s1";
public static String string2 = "s2";
public static String longString1 = "This is a long string 1";
public static String longString2 = "This is a long string 2";
public static int int1 = 42;
public void timeAppendStrings(int count) {
String s1 = string1;
String s2 = string2;
int sum = 0;
for (int i = 0; i < count; ++i) {
String result = s1 + s2;
sum += result.length(); // Make sure the append is not optimized away.
}
if (sum != count * (s1.length() + s2.length())) {
throw new AssertionError();
}
}
public void timeAppendLongStrings(int count) {
String s1 = longString1;
String s2 = longString2;
int sum = 0;
for (int i = 0; i < count; ++i) {
String result = s1 + s2;
sum += result.length(); // Make sure the append is not optimized away.
}
if (sum != count * (s1.length() + s2.length())) {
throw new AssertionError();
}
}
public void timeAppendStringAndInt(int count) {
String s1 = string1;
int i1 = int1;
int sum = 0;
for (int i = 0; i < count; ++i) {
String result = s1 + i1;
sum += result.length(); // Make sure the append is not optimized away.
}
if (sum != count * (s1.length() + Integer.toString(i1).length())) {
throw new AssertionError();
}
}
}

View File

@ -0,0 +1 @@
Benchmarks for repeating check-cast and instance-of instructions in a loop.

View File

@ -0,0 +1,147 @@
/*
* Copyright (C) 2018 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.
*/
public class TypeCheckBenchmark {
public void timeCheckCastLevel1ToLevel1(int count) {
Object[] arr = arr1;
for (int i = 0; i < count; ++i) {
Level1 l1 = (Level1) arr[i & 1023];
}
}
public void timeCheckCastLevel2ToLevel1(int count) {
Object[] arr = arr2;
for (int i = 0; i < count; ++i) {
Level1 l1 = (Level1) arr[i & 1023];
}
}
public void timeCheckCastLevel3ToLevel1(int count) {
Object[] arr = arr3;
for (int i = 0; i < count; ++i) {
Level1 l1 = (Level1) arr[i & 1023];
}
}
public void timeCheckCastLevel9ToLevel1(int count) {
Object[] arr = arr9;
for (int i = 0; i < count; ++i) {
Level1 l1 = (Level1) arr[i & 1023];
}
}
public void timeCheckCastLevel9ToLevel2(int count) {
Object[] arr = arr9;
for (int i = 0; i < count; ++i) {
Level2 l2 = (Level2) arr[i & 1023];
}
}
public void timeInstanceOfLevel1ToLevel1(int count) {
int sum = 0;
Object[] arr = arr1;
for (int i = 0; i < count; ++i) {
if (arr[i & 1023] instanceof Level1) {
++sum;
}
}
result = sum;
}
public void timeInstanceOfLevel2ToLevel1(int count) {
int sum = 0;
Object[] arr = arr2;
for (int i = 0; i < count; ++i) {
if (arr[i & 1023] instanceof Level1) {
++sum;
}
}
result = sum;
}
public void timeInstanceOfLevel3ToLevel1(int count) {
int sum = 0;
Object[] arr = arr3;
for (int i = 0; i < count; ++i) {
if (arr[i & 1023] instanceof Level1) {
++sum;
}
}
result = sum;
}
public void timeInstanceOfLevel9ToLevel1(int count) {
int sum = 0;
Object[] arr = arr9;
for (int i = 0; i < count; ++i) {
if (arr[i & 1023] instanceof Level1) {
++sum;
}
}
result = sum;
}
public void timeInstanceOfLevel9ToLevel2(int count) {
int sum = 0;
Object[] arr = arr9;
for (int i = 0; i < count; ++i) {
if (arr[i & 1023] instanceof Level2) {
++sum;
}
}
result = sum;
}
public static Object[] createArray(int level) {
try {
Class<?>[] ls = {
null,
Level1.class,
Level2.class,
Level3.class,
Level4.class,
Level5.class,
Level6.class,
Level7.class,
Level8.class,
Level9.class,
};
Class<?> l = ls[level];
Object[] array = new Object[1024];
for (int i = 0; i < array.length; ++i) {
array[i] = l.newInstance();
}
return array;
} catch (Exception unexpected) {
throw new Error("Initialization failure!");
}
}
Object[] arr1 = createArray(1);
Object[] arr2 = createArray(2);
Object[] arr3 = createArray(3);
Object[] arr9 = createArray(9);
int result;
}
class Level1 { }
class Level2 extends Level1 { }
class Level3 extends Level2 { }
class Level4 extends Level3 { }
class Level5 extends Level4 { }
class Level6 extends Level5 { }
class Level7 extends Level6 { }
class Level8 extends Level7 { }
class Level9 extends Level8 { }

438
build/Android.bp Normal file
View File

@ -0,0 +1,438 @@
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "art_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
bootstrap_go_package {
name: "soong-art",
pkgPath: "android/soong/art",
deps: [
"blueprint",
"blueprint-pathtools",
"blueprint-proptools",
"soong",
"soong-android",
"soong-apex",
"soong-cc",
],
srcs: [
"art.go",
"codegen.go",
"makevars.go",
],
pluginFor: ["soong_build"],
}
art_clang_tidy_errors = [
"android-cloexec-open",
"bugprone-lambda-function-name",
"bugprone-unused-raii", // Protect scoped things like MutexLock.
"bugprone-virtual-near-miss",
"modernize-use-bool-literals",
"performance-implicit-conversion-in-loop",
"performance-unnecessary-copy-initialization",
]
art_clang_tidy_allowed = [
// Many files have these warnings. Move them to art_clang_tidy_errors
// when all files are free of these warnings.
"android-cloexec-dup",
"bugprone-argument-comment",
"bugprone-unused-return-value",
"misc-unused-using-decls",
"modernize-use-nullptr",
"modernize-use-using",
"performance-faster-string-find",
"performance-for-range-copy",
"performance-noexcept-move-constructor",
"performance-unnecessary-value-param",
]
art_clang_tidy_disabled = [
"-google-default-arguments",
// We have local stores that are only used for debug checks.
"-clang-analyzer-deadcode.DeadStores",
// We are OK with some static globals and that they can, in theory, throw.
"-cert-err58-cpp",
// We have lots of C-style variadic functions, and are OK with them. JNI ensures
// that working around this warning would be extra-painful.
"-cert-dcl50-cpp",
// "Modernization" we don't agree with.
"-modernize-use-auto",
"-modernize-return-braced-init-list",
"-modernize-use-default-member-init",
"-modernize-pass-by-value",
]
soong_config_module_type_import {
from: "art/build/SoongConfig.bp",
module_types: [
"art_module_art_global_defaults",
"art_module_cc_defaults",
"art_module_java_defaults",
"art_module_genrule_defaults",
"art_module_prebuilt_defaults",
],
}
art_module_art_global_defaults {
// Additional flags are computed by art.go
name: "art_defaults",
// Disable all ART Soong modules by default when ART prebuilts are in use.
// TODO(b/172480617): Clean up when sources are gone from the platform tree
// and we no longer need to support sources present when prebuilts are used.
enabled: false,
soong_config_variables: {
source_build: {
enabled: true,
},
},
// This is the default visibility for the //art package, but we repeat it
// here so that it gets merged with other visibility rules in modules
// extending these defaults.
visibility: ["//art:__subpackages__"],
cflags: [
// Base set of cflags used by all things ART.
"-fno-rtti",
"-ggdb3",
"-Wall",
"-Werror",
"-Wextra",
"-Wstrict-aliasing",
"-fstrict-aliasing",
"-Wunreachable-code",
"-Wredundant-decls",
"-Wshadow",
"-Wunused",
"-fvisibility=protected",
// Warn about thread safety violations with clang.
"-Wthread-safety",
// TODO(b/144045034): turn on -Wthread-safety-negative
//"-Wthread-safety-negative",
// Warn if switch fallthroughs aren't annotated.
"-Wimplicit-fallthrough",
// Enable float equality warnings.
"-Wfloat-equal",
// Enable warning of converting ints to void*.
"-Wint-to-void-pointer-cast",
// Enable warning of wrong unused annotations.
"-Wused-but-marked-unused",
// Enable warning for deprecated language features.
"-Wdeprecated",
// Enable warning for unreachable break & return.
"-Wunreachable-code-break",
"-Wunreachable-code-return",
// Disable warning for use of offsetof on non-standard layout type.
// We use it to implement OFFSETOF_MEMBER - see macros.h.
"-Wno-invalid-offsetof",
// Enable inconsistent-missing-override warning. This warning is disabled by default in
// Android.
"-Winconsistent-missing-override",
// Enable thread annotations for std::mutex, etc.
"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
],
arch: {
x86: {
avx2: {
cflags: [
"-mavx2",
"-mfma",
],
},
},
x86_64: {
avx2: {
cflags: [
"-mavx2",
"-mfma",
],
},
},
},
target: {
android: {
cflags: [
// To use oprofile_android --callgraph, uncomment this and recompile with
// mmma -j art
// "-fno-omit-frame-pointer",
// "-marm",
// "-mapcs",
],
},
linux: {
cflags: [
// Enable missing-noreturn only on non-Mac. As lots of things are not implemented for
// Apple, it's a pain.
"-Wmissing-noreturn",
],
},
linux_bionic: {
strip: {
// Do not strip art libs when building for linux-bionic.
// Otherwise we can't get any symbols out of crashes.
none: true,
},
},
darwin: {
enabled: false,
},
windows: {
// When the module is enabled globally in the soong_config_variables
// stanza above, it may get enabled on windows too for some module
// types. Hence we need to disable it explicitly.
// TODO(b/172480617): Clean up with that.
enabled: false,
},
host: {
cflags: [
// Bug: 15446488. We don't omit the frame pointer to work around
// clang/libunwind bugs that cause SEGVs in run-test-004-ThreadStress.
"-fno-omit-frame-pointer",
],
},
// The build assumes that all our x86/x86_64 hosts (such as buildbots and developer
// desktops) support at least sse4.2/popcount. This firstly implies that the ART
// runtime binary itself may exploit these features. Secondly, this implies that
// the ART runtime passes these feature flags to dex2oat and JIT by calling the
// method InstructionSetFeatures::FromCppDefines(). Since invoking dex2oat directly
// does not pick up these flags, cross-compiling from a x86/x86_64 host to a
// x86/x86_64 target should not be affected.
linux_x86: {
cflags: [
"-msse4.2",
"-mpopcnt",
],
},
linux_x86_64: {
cflags: [
"-msse4.2",
"-mpopcnt",
],
},
},
codegen: {
arm: {
cflags: ["-DART_ENABLE_CODEGEN_arm"],
},
arm64: {
cflags: ["-DART_ENABLE_CODEGEN_arm64"],
},
x86: {
cflags: ["-DART_ENABLE_CODEGEN_x86"],
},
x86_64: {
cflags: ["-DART_ENABLE_CODEGEN_x86_64"],
},
},
tidy_checks: art_clang_tidy_errors + art_clang_tidy_allowed + art_clang_tidy_disabled,
tidy_checks_as_errors: art_clang_tidy_errors,
tidy_flags: [
// The static analyzer treats DCHECK as always enabled; we sometimes get
// false positives when we use DCHECKs with code that relies on NDEBUG.
"-extra-arg=-UNDEBUG",
// clang-tidy complains about functions like:
// void foo() { CHECK(kIsFooEnabled); /* do foo... */ }
// not being marked noreturn if kIsFooEnabled is false.
"-extra-arg=-Wno-missing-noreturn",
// Because tidy doesn't like our flow checks for compile-time configuration and thinks that
// the following code is dead (it is, but not for all configurations), disable unreachable
// code detection in Clang for tidy builds. It is still on for regular build steps, so we
// will still get the "real" errors.
"-extra-arg=-Wno-unreachable-code",
],
min_sdk_version: "31",
}
// Used to generate binaries that can be backed by transparent hugepages.
cc_defaults {
name: "art_hugepage_defaults",
arch: {
arm64: {
ldflags: ["-z max-page-size=0x200000"],
},
x86_64: {
ldflags: ["-z max-page-size=0x200000"],
},
},
}
art_debug_defaults {
name: "art_debug_defaults",
defaults: ["art_defaults"],
visibility: ["//art:__subpackages__"],
cflags: [
"-DDYNAMIC_ANNOTATIONS_ENABLED=1",
"-DVIXL_DEBUG",
"-UNDEBUG",
],
asflags: [
"-UNDEBUG",
],
target: {
// This has to be duplicated for android and host to make sure it
// comes after the -Wframe-larger-than warnings inserted by art.go
// target-specific properties
android: {
cflags: ["-Wno-frame-larger-than="],
},
host: {
cflags: ["-Wno-frame-larger-than="],
},
},
}
// A version of conscrypt only for enabling the "-hostdex" version to test ART on host.
java_library {
// We need our own name to not clash with the conscrypt library.
name: "conscrypt-host",
installable: true,
hostdex: true,
static_libs: ["conscrypt-for-host"],
// Tests and build files rely on this file to be installed as "conscrypt-hostdex",
// therefore set a stem. Without it, the file would be installed as
// "conscrypt-host-hostdex".
stem: "conscrypt",
sdk_version: "core_platform",
target: {
hostdex: {
required: ["libjavacrypto"],
},
darwin: {
// required module "libjavacrypto" is disabled on darwin
enabled: false,
},
},
}
// A version of core-icu4j only for enabling the "-hostdex" version to test ART on host.
java_library {
// We need our own name to not clash with the core-icu4j library.
name: "core-icu4j-host",
installable: true,
hostdex: true,
static_libs: ["core-icu4j-for-host"],
// Tests and build files rely on this file to be installed as "core-icu4j-hostdex",
// therefore set a stem. Without it, the file would be installed as
// "core-icu4j-host-hostdex".
stem: "core-icu4j",
sdk_version: "core_platform",
target: {
hostdex: {
required: ["libicu_jni"],
},
},
}
// Defaults for different module types to enable them only when building ART
// from sources. TODO(b/172480617): Clean up when sources are gone from the
// platform tree and we no longer need to support sources present when prebuilts
// are used.
art_module_cc_defaults {
name: "art_module_source_build_defaults",
defaults_visibility: [
"//art:__subpackages__",
"//libcore:__subpackages__",
"//libnativehelper:__subpackages__",
],
enabled: false,
soong_config_variables: {
source_build: {
enabled: true,
},
},
target: {
windows: {
// Windows is disabled by default, but if we set enabled:true
// globally above we need to disable it explicitly.
enabled: false,
},
},
}
art_module_java_defaults {
name: "art_module_source_build_java_defaults",
defaults_visibility: [
"//art:__subpackages__",
"//libcore:__subpackages__",
"//libnativehelper:__subpackages__",
],
enabled: false,
soong_config_variables: {
source_build: {
enabled: true,
},
},
target: {
windows: {
enabled: false,
},
},
}
art_module_genrule_defaults {
name: "art_module_source_build_genrule_defaults",
defaults_visibility: [
"//art:__subpackages__",
"//libcore:__subpackages__",
"//libnativehelper:__subpackages__",
],
enabled: false,
soong_config_variables: {
source_build: {
enabled: true,
},
},
target: {
windows: {
enabled: false,
},
},
}
art_module_prebuilt_defaults {
name: "art_module_source_build_prebuilt_defaults",
defaults_visibility: [
"//art:__subpackages__",
"//libcore:__subpackages__",
"//libnativehelper:__subpackages__",
],
enabled: false,
soong_config_variables: {
source_build: {
enabled: true,
},
},
target: {
windows: {
enabled: false,
},
},
}

100
build/Android.common.mk Normal file
View File

@ -0,0 +1,100 @@
#
# 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.
#
ifndef ART_ANDROID_COMMON_MK
ART_ANDROID_COMMON_MK = true
ART_TARGET_SUPPORTED_ARCH := arm arm64 x86 x86_64
ART_HOST_SUPPORTED_ARCH := x86 x86_64
ART_DEXPREOPT_BOOT_JAR_DIR := apex/com.android.art/javalib
CONSCRYPT_DEXPREOPT_BOOT_JAR_DIR := apex/com.android.conscrypt/javalib
ifneq ($(HOST_OS),darwin)
ART_HOST_SUPPORTED_ARCH := x86 x86_64
else
# Mac OS doesn't support low-4GB allocation in a 64-bit process. So we won't be able to create
# our heaps.
ART_HOST_SUPPORTED_ARCH := x86
endif
ART_COVERAGE := false
ifeq ($(ART_COVERAGE),true)
# https://gcc.gnu.org/onlinedocs/gcc/Cross-profiling.html
GCOV_PREFIX := /data/local/tmp/gcov
# GCOV_PREFIX_STRIP is an integer that defines how many levels should be
# stripped off the beginning of the path. We want the paths in $GCOV_PREFIX to
# be relative to $ANDROID_BUILD_TOP so we can just adb pull from the top and not
# have to worry about placing things ourselves.
GCOV_PREFIX_STRIP := $(shell echo $(ANDROID_BUILD_TOP) | grep -o / | wc -l)
GCOV_ENV := GCOV_PREFIX=$(GCOV_PREFIX) GCOV_PREFIX_STRIP=$(GCOV_PREFIX_STRIP)
else
GCOV_ENV :=
endif
ifeq (,$(filter $(TARGET_ARCH),$(ART_TARGET_SUPPORTED_ARCH)))
$(warning unsupported TARGET_ARCH=$(TARGET_ARCH))
endif
ifeq (,$(filter $(HOST_ARCH),$(ART_HOST_SUPPORTED_ARCH)))
$(warning unsupported HOST_ARCH=$(HOST_ARCH))
endif
# Primary vs. secondary
2ND_TARGET_ARCH := $(TARGET_2ND_ARCH)
TARGET_INSTRUCTION_SET_FEATURES := $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)
2ND_TARGET_INSTRUCTION_SET_FEATURES := $($(TARGET_2ND_ARCH_VAR_PREFIX)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)
ifdef TARGET_2ND_ARCH
ifneq ($(filter %64,$(TARGET_ARCH)),)
ART_PHONY_TEST_TARGET_SUFFIX := 64
2ND_ART_PHONY_TEST_TARGET_SUFFIX := 32
else
ART_PHONY_TEST_TARGET_SUFFIX := 32
2ND_ART_PHONY_TEST_TARGET_SUFFIX :=
endif
else
ifneq ($(filter %64,$(TARGET_ARCH)),)
ART_PHONY_TEST_TARGET_SUFFIX := 64
2ND_ART_PHONY_TEST_TARGET_SUFFIX :=
else
ART_PHONY_TEST_TARGET_SUFFIX := 32
2ND_ART_PHONY_TEST_TARGET_SUFFIX :=
endif
endif
ART_HOST_SHLIB_EXTENSION := $(HOST_SHLIB_SUFFIX)
ART_HOST_SHLIB_EXTENSION ?= .so
ifeq ($(HOST_PREFER_32_BIT),true)
ART_PHONY_TEST_HOST_SUFFIX := 32
2ND_ART_PHONY_TEST_HOST_SUFFIX :=
ART_HOST_ARCH := x86
2ND_ART_HOST_ARCH :=
2ND_HOST_ARCH :=
ART_HOST_OUT_SHARED_LIBRARIES := $(2ND_HOST_OUT_SHARED_LIBRARIES)
2ND_ART_HOST_OUT_SHARED_LIBRARIES :=
else
ART_PHONY_TEST_HOST_SUFFIX := 64
2ND_ART_PHONY_TEST_HOST_SUFFIX := 32
ART_HOST_ARCH := x86_64
2ND_ART_HOST_ARCH := x86
2ND_HOST_ARCH := x86
ART_HOST_OUT_SHARED_LIBRARIES := $(HOST_OUT_SHARED_LIBRARIES)
2ND_ART_HOST_OUT_SHARED_LIBRARIES := $(2ND_HOST_OUT_SHARED_LIBRARIES)
endif
ADB_EXECUTABLE := $(HOST_OUT_EXECUTABLES)/adb
ADB ?= $(ADB_EXECUTABLE)
endif # ART_ANDROID_COMMON_MK

View File

@ -0,0 +1,83 @@
#
# 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.
#
ifndef ART_ANDROID_COMMON_BUILD_MK
ART_ANDROID_COMMON_BUILD_MK = true
include art/build/Android.common.mk
# These can be overridden via the environment or by editing to
# enable/disable certain build configuration.
#
# For example, to disable everything but the host debug build you use:
#
# (export ART_BUILD_TARGET_NDEBUG=false && export ART_BUILD_TARGET_DEBUG=false && export ART_BUILD_HOST_NDEBUG=false && ...)
#
# Beware that tests may use the non-debug build for performance, notable 055-enum-performance
#
ART_BUILD_TARGET_NDEBUG ?= true
ART_BUILD_TARGET_DEBUG ?= true
ART_BUILD_HOST_NDEBUG ?= true
ART_BUILD_HOST_DEBUG ?= true
ifeq ($(ART_BUILD_TARGET_NDEBUG),false)
$(info Disabling ART_BUILD_TARGET_NDEBUG)
endif
ifeq ($(ART_BUILD_TARGET_DEBUG),false)
$(info Disabling ART_BUILD_TARGET_DEBUG)
endif
ifeq ($(ART_BUILD_HOST_NDEBUG),false)
$(info Disabling ART_BUILD_HOST_NDEBUG)
endif
ifeq ($(ART_BUILD_HOST_DEBUG),false)
$(info Disabling ART_BUILD_HOST_DEBUG)
endif
# Enable the read barrier by default.
ART_USE_READ_BARRIER ?= true
# Default compact dex level to none.
ifeq ($(ART_DEFAULT_COMPACT_DEX_LEVEL),)
ART_DEFAULT_COMPACT_DEX_LEVEL := none
endif
ART_CPP_EXTENSION := .cc
ifndef LIBART_IMG_HOST_BASE_ADDRESS
$(error LIBART_IMG_HOST_BASE_ADDRESS unset)
endif
ifndef LIBART_IMG_TARGET_BASE_ADDRESS
$(error LIBART_IMG_TARGET_BASE_ADDRESS unset)
endif
# Support for disabling certain builds.
ART_BUILD_TARGET := false
ART_BUILD_HOST := false
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
ART_BUILD_TARGET := true
endif
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
ART_BUILD_TARGET := true
endif
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
ART_BUILD_HOST := true
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
ART_BUILD_HOST := true
endif
endif # ART_ANDROID_COMMON_BUILD_MK

View File

@ -0,0 +1,160 @@
#
# 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.
#
ifndef ART_ANDROID_COMMON_PATH_MK
ART_ANDROID_COMMON_PATH_MK := true
# We cannot build things that require host core images from prebuilts, because
# they aren't present there. Set up a variable to skip all build rules that
# relate to them, because `m checkbuild` complains on rules with nonexisting
# dependencies, even if they won't get called.
# TODO(b/172480617): Remove this when ART sources are no longer in platform manifests.
ifeq (true,$(ART_MODULE_BUILD_FROM_SOURCE))
my_art_module_source_build := true
else ifeq (false,$(ART_MODULE_BUILD_FROM_SOURCE))
my_art_module_source_build := false
else
$(error ART_MODULE_BUILD_FROM_SOURCE is neither true nor false - mk file ordering problem?)
endif
ifeq (true,$(my_art_module_source_build))
include art/build/Android.common.mk
include art/build/Android.common_build.mk
# Directory used for dalvik-cache on device.
ART_TARGET_DALVIK_CACHE_DIR := /data/dalvik-cache
# Directory used for gtests on device.
# $(TARGET_OUT_DATA_NATIVE_TESTS) will evaluate to the nativetest directory in the target part on
# the host, so we can strip everything but the directory to find out whether it is "nativetest" or
# "nativetest64."
ART_TARGET_NATIVETEST_DIR := /data/$(notdir $(TARGET_OUT_DATA_NATIVE_TESTS))/art
ART_TARGET_NATIVETEST_OUT := $(TARGET_OUT_DATA_NATIVE_TESTS)/art
# Directory used for oat tests on device.
ART_TARGET_TEST_DIR := /data/art-test
ART_TARGET_TEST_OUT := $(TARGET_OUT_DATA)/art-test
# Modules to compile for core.art.
CORE_IMG_JARS := core-oj core-libart okhttp bouncycastle apache-xml
HOST_CORE_IMG_JARS := $(addsuffix -hostdex,$(CORE_IMG_JARS))
TARGET_CORE_IMG_JARS := $(CORE_IMG_JARS)
HOST_CORE_IMG_DEX_LOCATIONS := $(foreach jar,$(HOST_CORE_IMG_JARS), $(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar)
ifeq ($(ART_TEST_ANDROID_ROOT),)
TARGET_CORE_IMG_DEX_LOCATIONS := $(foreach jar,$(TARGET_CORE_IMG_JARS),/$(ART_DEXPREOPT_BOOT_JAR_DIR)/$(jar).jar)
else
TARGET_CORE_IMG_DEX_LOCATIONS := $(foreach jar,$(TARGET_CORE_IMG_JARS),$(ART_TEST_ANDROID_ROOT)/$(jar).jar)
endif
HOST_CORE_IMG_DEX_FILES := $(foreach jar,$(HOST_CORE_IMG_JARS), $(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),t,COMMON)/javalib.jar)
TARGET_CORE_IMG_DEX_FILES := $(foreach jar,$(TARGET_CORE_IMG_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar).com.android.art.testing, ,COMMON)/javalib.jar)
# Also copy the jar files next to host boot.art image.
# `dexpreopt_bootjars.go` uses a single source of input regardless of variants, so we should use the
# same source for `CORE_IMG_JARS` to avoid checksum mismatches on the oat files.
HOST_BOOT_IMAGE_JARS := $(foreach jar,$(CORE_IMG_JARS),$(HOST_OUT)/apex/com.android.art/javalib/$(jar).jar)
CORE_IMG_JAR_DIR := $(OUT_DIR)/soong/$(PRODUCT_DEVICE)/dex_artjars_input
$(HOST_BOOT_IMAGE_JARS): $(HOST_OUT)/apex/com.android.art/javalib/%.jar : $(CORE_IMG_JAR_DIR)/%.jar
$(copy-file-to-target)
# We can still use the host variant of `conscrypt` and `core-icu4j` because they don't go into the
# primary boot image that is used in host gtests, and hence can't lead to checksum mismatches.
HOST_BOOT_IMAGE_JARS += $(HOST_OUT)/apex/com.android.conscrypt/javalib/conscrypt.jar
$(HOST_OUT)/apex/com.android.conscrypt/javalib/conscrypt.jar : $(HOST_OUT_JAVA_LIBRARIES)/conscrypt-hostdex.jar
$(copy-file-to-target)
HOST_BOOT_IMAGE_JARS += $(HOST_OUT)/apex/com.android.i18n/javalib/core-icu4j.jar
$(HOST_OUT)/apex/com.android.i18n/javalib/core-icu4j.jar : $(HOST_OUT_JAVA_LIBRARIES)/core-icu4j-hostdex.jar
$(copy-file-to-target)
HOST_CORE_IMG_OUTS += $(HOST_BOOT_IMAGE_JARS) $(HOST_BOOT_IMAGE) $(2ND_HOST_BOOT_IMAGE)
HOST_TEST_CORE_JARS := $(addsuffix -hostdex,$(CORE_IMG_JARS) core-icu4j conscrypt)
ART_HOST_DEX_DEPENDENCIES := $(foreach jar,$(HOST_TEST_CORE_JARS),$(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar)
ART_TARGET_DEX_DEPENDENCIES := com.android.art.testing com.android.conscrypt com.android.i18n
ART_CORE_SHARED_LIBRARIES := libjavacore libopenjdk libopenjdkjvm libopenjdkjvmti libjdwp
ART_CORE_SHARED_DEBUG_LIBRARIES := libopenjdkd libopenjdkjvmd libopenjdkjvmtid
ART_HOST_CORE_SHARED_LIBRARIES := $(ART_CORE_SHARED_LIBRARIES) libicuuc-host libicui18n-host libicu_jni
ART_HOST_SHARED_LIBRARY_DEPENDENCIES := $(foreach lib,$(ART_HOST_CORE_SHARED_LIBRARIES), $(ART_HOST_OUT_SHARED_LIBRARIES)/$(lib)$(ART_HOST_SHLIB_EXTENSION))
ART_HOST_SHARED_LIBRARY_DEBUG_DEPENDENCIES := $(foreach lib,$(ART_CORE_SHARED_DEBUG_LIBRARIES), $(ART_HOST_OUT_SHARED_LIBRARIES)/$(lib)$(ART_HOST_SHLIB_EXTENSION))
ifdef HOST_2ND_ARCH
ART_HOST_SHARED_LIBRARY_DEPENDENCIES += $(foreach lib,$(ART_HOST_CORE_SHARED_LIBRARIES), $(2ND_HOST_OUT_SHARED_LIBRARIES)/$(lib).so)
ART_HOST_SHARED_LIBRARY_DEBUG_DEPENDENCIES += $(foreach lib,$(ART_CORE_SHARED_DEBUG_LIBRARIES), $(2ND_HOST_OUT_SHARED_LIBRARIES)/$(lib).so)
endif
# Both the primary and the secondary arches of the libs are built by depending
# on the module name.
ART_DEBUG_TARGET_SHARED_LIBRARY_DEPENDENCIES := $(foreach lib,$(ART_CORE_SHARED_LIBRARIES), $(lib).com.android.art.debug)
ART_TARGET_SHARED_LIBRARY_DEBUG_DEPENDENCIES := $(foreach lib,$(ART_CORE_SHARED_DEBUG_LIBRARIES), $(TARGET_OUT_SHARED_LIBRARIES)/$(lib).so)
ifdef TARGET_2ND_ARCH
ART_TARGET_SHARED_LIBRARY_DEBUG_DEPENDENCIES += $(foreach lib,$(ART_CORE_SHARED_DEBUG_LIBRARIES), $(2ND_TARGET_OUT_SHARED_LIBRARIES)/$(lib).so)
endif
ART_CORE_DEBUGGABLE_EXECUTABLES_COMMON := \
dex2oat \
dexoptanalyzer \
imgdiag \
oatdump \
profman \
ART_CORE_DEBUGGABLE_EXECUTABLES_TARGET := $(ART_CORE_DEBUGGABLE_EXECUTABLES_COMMON) odrefresh
ART_CORE_DEBUGGABLE_EXECUTABLES_HOST := $(ART_CORE_DEBUGGABLE_EXECUTABLES_COMMON)
ART_CORE_EXECUTABLES := \
dalvikvm \
dexlist \
# Depend on the -target or -host phony targets generated by the build system
# for each module
ART_TARGET_EXECUTABLES :=
ifneq ($(ART_BUILD_TARGET_NDEBUG),false)
ART_TARGET_EXECUTABLES += $(foreach name,$(ART_CORE_EXECUTABLES) $(ART_CORE_DEBUGGABLE_EXECUTABLES_TARGET),$(name)-target)
endif
ifneq ($(ART_BUILD_TARGET_DEBUG),false)
ART_TARGET_EXECUTABLES += $(foreach name,$(ART_CORE_DEBUGGABLE_EXECUTABLES_TARGET),$(name)d-target)
endif
ART_HOST_EXECUTABLES :=
ifneq ($(ART_BUILD_HOST_NDEBUG),false)
ART_HOST_EXECUTABLES += $(foreach name,$(ART_CORE_EXECUTABLES) $(ART_CORE_DEBUGGABLE_EXECUTABLES_HOST),$(name)-host)
endif
ifneq ($(ART_BUILD_HOST_DEBUG),false)
ART_HOST_EXECUTABLES += $(foreach name,$(ART_CORE_DEBUGGABLE_EXECUTABLES_HOST),$(name)d-host)
endif
# Release ART APEX, included by default in "user" builds.
RELEASE_ART_APEX := com.android.art
# Debug ART APEX, included by default in "userdebug" and "eng"
# builds and used in ART device benchmarking.
DEBUG_ART_APEX := com.android.art.debug
# Testing ART APEX, used in ART device testing.
TESTING_ART_APEX := com.android.art.testing
RUNTIME_APEX := com.android.runtime
CONSCRYPT_APEX := com.android.conscrypt
I18N_APEX := com.android.i18n
STATSD_APEX := com.android.os.statsd
TZDATA_APEX := com.android.tzdata
# A phony file to create the ICU data file for host.
HOST_I18N_DATA := $(HOST_OUT)/$(I18N_APEX)/timestamp
# A phony file to create the tz data file for host.
HOST_TZDATA_DATA := $(HOST_OUT)/$(TZDATA_APEX)/timestamp
endif # ifeq (true,$(my_art_module_source_build))
endif # ART_ANDROID_COMMON_PATH_MK

View File

@ -0,0 +1,108 @@
#
# 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.
#
ifndef ART_ANDROID_COMMON_TEST_MK
ART_ANDROID_COMMON_TEST_MK = true
include art/build/Android.common_path.mk
# Directory used for temporary test files on the host.
# TMPDIR is always provided by the build system as $OUT_DIR-unique temporary directory.
ART_HOST_TEST_DIR := $(TMPDIR)/test-art
# List of known broken tests that we won't attempt to execute. The test name must be the full
# rule name such as test-art-host-oat-optimizing-HelloWorld64.
ART_TEST_KNOWN_BROKEN :=
# List of known failing tests that when executed won't cause test execution to not finish.
# The test name must be the full rule name such as test-art-host-oat-optimizing-HelloWorld64.
ART_TEST_KNOWN_FAILING :=
# Keep going after encountering a test failure?
ART_TEST_KEEP_GOING ?= true
# Do you want run-test to be quieter? run-tests will only show output if they fail.
ART_TEST_QUIET ?= true
# Define the command run on test failure. $(1) is the name of the test. Executed by the shell.
# If the test was a top-level make target (e.g. `test-art-host-gtest-codegen_test64`), the command
# fails with exit status 1 (returned by the last `grep` statement below).
# Otherwise (e.g., if the test was run as a prerequisite of a compound test command, such as
# `test-art-host-gtest-codegen_test`), the command does not fail, as this would break rules running
# ART_TEST_PREREQ_FINISHED as one of their actions, which expects *all* prerequisites *not* to fail.
define ART_TEST_FAILED
( [ -f $(ART_HOST_TEST_DIR)/skipped/$(1) ] || \
(mkdir -p $(ART_HOST_TEST_DIR)/failed/ && touch $(ART_HOST_TEST_DIR)/failed/$(1) && \
echo $(ART_TEST_KNOWN_FAILING) | grep -q $(1) \
&& (echo -e "$(1) \e[91mKNOWN FAILURE\e[0m") \
|| (echo -e "$(1) \e[91mFAILED\e[0m" >&2; echo $(MAKECMDGOALS) | grep -q -v $(1))))
endef
ifeq ($(ART_TEST_QUIET),true)
ART_TEST_ANNOUNCE_PASS := ( true )
ART_TEST_ANNOUNCE_RUN := ( true )
ART_TEST_ANNOUNCE_SKIP_FAILURE := ( true )
ART_TEST_ANNOUNCE_SKIP_BROKEN := ( true )
else
# Note the use of '=' and not ':=' is intentional since these are actually functions.
ART_TEST_ANNOUNCE_PASS = ( echo -e "$(1) \e[92mPASSED\e[0m" )
ART_TEST_ANNOUNCE_RUN = ( echo -e "$(1) \e[95mRUNNING\e[0m")
ART_TEST_ANNOUNCE_SKIP_FAILURE = ( echo -e "$(1) \e[93mSKIPPING DUE TO EARLIER FAILURE\e[0m" )
ART_TEST_ANNOUNCE_SKIP_BROKEN = ( echo -e "$(1) \e[93mSKIPPING BROKEN TEST\e[0m" )
endif
# Define the command run on test success. $(1) is the name of the test. Executed by the shell.
# The command checks prints "PASSED" then checks to see if this was a top-level make target (e.g.
# "mm test-art-host-oat-HelloWorld32"), if it was then it does nothing, otherwise it creates a file
# to be printed in the passing test summary.
define ART_TEST_PASSED
( $(call ART_TEST_ANNOUNCE_PASS,$(1)) && \
(echo $(MAKECMDGOALS) | grep -q $(1) || \
(mkdir -p $(ART_HOST_TEST_DIR)/passed/ && touch $(ART_HOST_TEST_DIR)/passed/$(1))))
endef
# Define the command run on test success of multiple prerequisites. $(1) is the name of the test.
# When the test is a top-level make target then a summary of the ran tests is produced. Executed by
# the shell.
define ART_TEST_PREREQ_FINISHED
(echo -e "$(1) \e[32mCOMPLETE\e[0m" && \
(echo $(MAKECMDGOALS) | grep -q -v $(1) || \
(([ -d $(ART_HOST_TEST_DIR)/passed/ ] \
&& (echo -e "\e[92mPASSING TESTS\e[0m" && ls -1 $(ART_HOST_TEST_DIR)/passed/) \
|| (echo -e "\e[91mNO TESTS PASSED\e[0m")) && \
([ -d $(ART_HOST_TEST_DIR)/skipped/ ] \
&& (echo -e "\e[93mSKIPPED TESTS\e[0m" && ls -1 $(ART_HOST_TEST_DIR)/skipped/) \
|| (echo -e "\e[92mNO TESTS SKIPPED\e[0m")) && \
([ -d $(ART_HOST_TEST_DIR)/failed/ ] \
&& (echo -e "\e[91mFAILING TESTS\e[0m" >&2 && ls -1 $(ART_HOST_TEST_DIR)/failed/ >&2) \
|| (echo -e "\e[92mNO TESTS FAILED\e[0m")) \
&& ([ ! -d $(ART_HOST_TEST_DIR)/failed/ ] && rm -r $(ART_HOST_TEST_DIR) \
|| (rm -r $(ART_HOST_TEST_DIR) && false)))))
endef
# Define the command executed by the shell ahead of running an art test. $(1) is the name of the
# test.
define ART_TEST_SKIP
((echo $(ART_TEST_KNOWN_BROKEN) | grep -q -v $(1) \
&& ([ ! -d $(ART_HOST_TEST_DIR)/failed/ ] || [ $(ART_TEST_KEEP_GOING) = true ])\
&& $(call ART_TEST_ANNOUNCE_RUN,$(1)) ) \
|| ((mkdir -p $(ART_HOST_TEST_DIR)/skipped/ && touch $(ART_HOST_TEST_DIR)/skipped/$(1) \
&& ([ -d $(ART_HOST_TEST_DIR)/failed/ ] \
&& $(call ART_TEST_ANNOUNCE_SKIP_FAILURE,$(1)) ) \
|| $(call ART_TEST_ANNOUNCE_SKIP_BROKEN,$(1)) ) && false))
endef
endif # ART_ANDROID_COMMON_TEST_MK

78
build/Android.cpplint.mk Normal file
View File

@ -0,0 +1,78 @@
#
# 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.
#
include art/build/Android.common_build.mk
# We need to be in art directory to properly initialize ART_CPPLINT_SRC variable.
LOCAL_PATH := $(art_path)
# Use upstream cpplint (toolpath from .repo/manifests/GLOBAL-PREUPLOAD.cfg).
ART_CPPLINT := tools/repohooks/tools/cpplint.py
# This file previously configured many cpplint settings.
# Everything that could be moved to CPPLINT.cfg has moved there.
# Please add new settings to CPPLINT.cfg over adding new flags in this file.
ART_CPPLINT_FLAGS :=
# No output when there are no errors.
ART_CPPLINT_QUIET := --quiet
# 1) Get list of all .h & .cc files in the art directory.
# 2) Prepends 'art/' to each of them to make the full name.
ART_CPPLINT_SRC := $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,*.h) $(call all-subdir-named-files,*$(ART_CPP_EXTENSION)))
# 1) Get list of all CPPLINT.cfg files in the art directory.
# 2) Prepends 'art/' to each of them to make the full name.
ART_CPPLINT_CFG := $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,CPPLINT.cfg))
# "mm cpplint-art" to verify we aren't regressing
# - files not touched since the last build are skipped (quite fast).
.PHONY: cpplint-art
cpplint-art: cpplint-art-phony
# "mm cpplint-art-all" to manually execute cpplint.py on all files (very slow).
.PHONY: cpplint-art-all
cpplint-art-all:
$(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_SRC)
OUT_CPPLINT := $(TARGET_COMMON_OUT_ROOT)/cpplint
# Build up the list of all targets for linting the ART source files.
ART_CPPLINT_TARGETS :=
define declare-art-cpplint-target
art_cpplint_file := $(1)
art_cpplint_touch := $$(OUT_CPPLINT)/$$(subst /,__,$$(art_cpplint_file))
$$(art_cpplint_touch): $$(art_cpplint_file) $(ART_CPPLINT) $(ART_CPPLINT_CFG) art/build/Android.cpplint.mk
$(hide) $(ART_CPPLINT) $(ART_CPPLINT_QUIET) $(ART_CPPLINT_FLAGS) $$<
$(hide) mkdir -p $$(dir $$@)
$(hide) touch $$@
ART_CPPLINT_TARGETS += $$(art_cpplint_touch)
endef
$(foreach file, $(ART_CPPLINT_SRC), $(eval $(call declare-art-cpplint-target,$(file))))
#$(info $(call declare-art-cpplint-target,$(firstword $(ART_CPPLINT_SRC))))
include $(CLEAR_VARS)
LOCAL_MODULE := cpplint-art-phony
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
LOCAL_MODULE_TAGS := optional
LOCAL_ADDITIONAL_DEPENDENCIES := $(ART_CPPLINT_TARGETS)
include $(BUILD_PHONY_PACKAGE)

516
build/Android.gtest.mk Normal file
View File

@ -0,0 +1,516 @@
#
# 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.
#
# Build rules are excluded from Mac, since we can not run ART tests there in the first place.
ifneq ($(HOST_OS),darwin)
LOCAL_PATH := $(call my-dir)
###################################################################################################
# Create module in testcases to hold all common data and tools needed for ART host tests.
# ART binary tools and libraries (automatic list of all art_cc_binary/art_cc_library modules).
my_files := $(ART_TESTCASES_CONTENT)
# Manually add system libraries that we need to run the host ART tools.
my_files += \
$(foreach lib, libbacktrace libbase libc++ libicu libicu_jni liblog libsigchain libunwindstack \
libziparchive libjavacore libandroidio libopenjdkd liblz4 liblzma, \
$(call intermediates-dir-for,SHARED_LIBRARIES,$(lib),HOST)/$(lib).so:lib64/$(lib).so \
$(call intermediates-dir-for,SHARED_LIBRARIES,$(lib),HOST,,2ND)/$(lib).so:lib/$(lib).so) \
$(foreach lib, libcrypto libz libicuuc libicui18n libexpat, \
$(call intermediates-dir-for,SHARED_LIBRARIES,$(lib),HOST)/$(lib).so:lib64/$(lib)-host.so \
$(call intermediates-dir-for,SHARED_LIBRARIES,$(lib),HOST,,2ND)/$(lib).so:lib/$(lib)-host.so)
# Add apex directories for art, conscrypt and i18n.
icu_data_file := $(firstword $(wildcard external/icu/icu4c/source/stubdata/icu*.dat))
my_files += $(foreach infix,_ _VDEX_,$(foreach suffix,$(HOST_ARCH) $(HOST_2ND_ARCH), \
$(DEXPREOPT_IMAGE$(infix)BUILT_INSTALLED_art_host_$(suffix))))
# `dexpreopt_bootjars.go` uses a single source of input regardless of variants, so we should use the
# same source for `CORE_IMG_JARS` to avoid checksum mismatches on the oat files. We can still use
# the host variant of `conscrypt` and `core-icu4j` because they don't go into the primary boot image
# that is used in host gtests, and hence can't lead to checksum mismatches.
my_files += \
$(foreach jar,$(CORE_IMG_JARS),\
$(OUT_DIR)/soong/$(PRODUCT_DEVICE)/dex_artjars_input/$(jar).jar:apex/com.android.art/javalib/$(jar).jar) \
$(HOST_OUT_JAVA_LIBRARIES)/conscrypt-hostdex.jar:apex/com.android.conscrypt/javalib/conscrypt.jar\
$(HOST_OUT_JAVA_LIBRARIES)/core-icu4j-hostdex.jar:apex/com.android.i18n/javalib/core-icu4j.jar \
$(icu_data_file):com.android.i18n/etc/icu/$(notdir $(icu_data_file))
# Create phony module that will copy all the data files into testcases directory.
# For now, this copies everything to "out/host/linux-x86/" subdirectory, since it
# is hard-coded in many places. TODO: Refactor tests to remove the need for this.
include $(CLEAR_VARS)
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE := art_common
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_CLASS := NATIVE_TESTS
LOCAL_MODULE_SUFFIX := .txt
LOCAL_COMPATIBILITY_SUITE := art-host-tests
LOCAL_COMPATIBILITY_SUPPORT_FILES := $(ART_TESTCASES_PREBUILT_CONTENT) \
$(foreach f,$(my_files),$(call word-colon,1,$f):out/host/linux-x86/$(call word-colon,2,$f))
include $(BUILD_SYSTEM)/base_rules.mk
$(LOCAL_BUILT_MODULE):
@mkdir -p $(dir $@)
echo "This directory contains common data and tools needed for ART host tests" > $@
my_files :=
include $(CLEAR_VARS)
###################################################################################################
# The path for which all the dex files are relative, not actually the current directory.
LOCAL_PATH := art/test
include art/build/Android.common_test.mk
include art/build/Android.common_path.mk
include art/build/Android.common_build.mk
# Deprecated core.art dependencies.
HOST_CORE_IMAGE_DEFAULT_32 :=
HOST_CORE_IMAGE_DEFAULT_64 :=
TARGET_CORE_IMAGE_DEFAULT_32 :=
TARGET_CORE_IMAGE_DEFAULT_64 :=
# The elf writer test has dependencies on core.oat.
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TARGET_CORE_IMAGE_DEFAULT_32)
# The two_runtimes_test test has dependencies on core.oat.
ART_GTEST_two_runtimes_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
ART_GTEST_two_runtimes_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TARGET_CORE_IMAGE_DEFAULT_32)
# The transaction test has dependencies on core.oat.
ART_GTEST_transaction_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
ART_GTEST_transaction_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TARGET_CORE_IMAGE_DEFAULT_32)
# The path for which all the source files are relative, not actually the current directory.
LOCAL_PATH := art
ART_TEST_MODULES_COMMON := \
art_cmdline_tests \
art_compiler_host_tests \
art_compiler_tests \
art_dex2oat_tests \
art_dexanalyze_tests \
art_dexdiag_tests \
art_dexdump_tests \
art_dexlayout_tests \
art_dexlist_tests \
art_dexoptanalyzer_tests \
art_hiddenapi_tests \
art_imgdiag_tests \
art_libartbase_tests \
art_libartpalette_tests \
art_libartservice_tests \
art_libarttools_tests \
art_libdexfile_external_tests \
art_libdexfile_support_static_tests \
art_libdexfile_support_tests \
art_libdexfile_tests \
art_libprofile_tests \
art_oatdump_tests \
art_profman_tests \
art_runtime_compiler_tests \
art_runtime_tests \
art_sigchain_tests \
ART_TEST_MODULES_TARGET := $(ART_TEST_MODULES_COMMON) art_odrefresh_tests
ART_TEST_MODULES_HOST := $(ART_TEST_MODULES_COMMON)
ART_TARGET_GTEST_NAMES := $(foreach tm,$(ART_TEST_MODULES_TARGET),\
$(foreach path,$(ART_TEST_LIST_device_$(TARGET_ARCH)_$(tm)),\
$(notdir $(path))\
)\
)
ART_HOST_GTEST_FILES := $(foreach m,$(ART_TEST_MODULES_HOST),\
$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_$(m)))
ifneq ($(HOST_PREFER_32_BIT),true)
2ND_ART_HOST_GTEST_FILES += $(foreach m,$(ART_TEST_MODULES_HOST),\
$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_$(m)))
endif
# Variables holding collections of gtest pre-requisits used to run a number of gtests.
ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_GTEST_RULES :=
ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_BUILD_RULES :=
ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_BUILD_RULES :=
ART_TEST_HOST_GTEST_BUILD_RULES :=
ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST_RULES :=
ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_BUILD_RULES :=
ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_BUILD_RULES :=
ART_TEST_TARGET_GTEST_BUILD_RULES :=
ART_TEST_HOST_GTEST_DEPENDENCIES :=
ART_TEST_TARGET_GTEST_DEPENDENCIES :=
ART_GTEST_TARGET_ANDROID_ROOT := '/system'
ifneq ($(ART_TEST_ANDROID_ROOT),)
ART_GTEST_TARGET_ANDROID_ROOT := $(ART_TEST_ANDROID_ROOT)
endif
ART_GTEST_TARGET_ANDROID_I18N_ROOT := '/apex/com.android.i18n'
ifneq ($(ART_TEST_ANDROID_I18N_ROOT),)
ART_GTEST_TARGET_ANDROID_I18N_ROOT := $(ART_TEST_ANDROID_I18N_ROOT)
endif
ART_GTEST_TARGET_ANDROID_ART_ROOT := '/apex/com.android.art'
ifneq ($(ART_TEST_ANDROID_ART_ROOT),)
ART_GTEST_TARGET_ANDROID_ART_ROOT := $(ART_TEST_ANDROID_ART_ROOT)
endif
ART_GTEST_TARGET_ANDROID_TZDATA_ROOT := '/apex/com.android.tzdata'
ifneq ($(ART_TEST_ANDROID_TZDATA_ROOT),)
ART_GTEST_TARGET_ANDROID_TZDATA_ROOT := $(ART_TEST_ANDROID_TZDATA_ROOT)
endif
# Define make rules for a host gtests.
# $(1): gtest name - the name of the test we're building such as leb128_test.
# $(2): path relative to $OUT to the test binary
# $(3): 2ND_ or undefined - used to differentiate between the primary and secondary architecture.
define define-art-gtest-rule-host
gtest_suffix := $(1)$$($(3)ART_PHONY_TEST_HOST_SUFFIX)
gtest_rule := test-art-host-gtest-$$(gtest_suffix)
gtest_build_rule := test-art-host-gtest-dependencies-$$(gtest_suffix)
gtest_output := $(call intermediates-dir-for,PACKAGING,art-host-gtest,HOST)/$$(gtest_suffix).xml
$$(call dist-for-goals,$$(gtest_rule),$$(gtest_output):gtest/$$(gtest_suffix))
$$(call declare-1p-target,$$(gtest_output))
gtest_exe := $(2)
# Dependencies for all host gtests.
gtest_deps := $$(ART_HOST_DEX_DEPENDENCIES) \
$$(ART_TEST_HOST_GTEST_DEPENDENCIES) \
$$(HOST_OUT)/$$(I18N_APEX)/timestamp \
$$(HOST_BOOT_IMAGE_JARS) \
$$($(3)ART_HOST_OUT_SHARED_LIBRARIES)/libicu_jni$$(ART_HOST_SHLIB_EXTENSION) \
$$($(3)ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$$(ART_HOST_SHLIB_EXTENSION) \
$$($(3)ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkd$$(ART_HOST_SHLIB_EXTENSION) \
$$(gtest_exe) \
$$(ART_GTEST_$(1)_HOST_DEPS) \
$(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX)) \
$(HOST_OUT_EXECUTABLES)/signal_dumper
# Note: The "host arch" Make variables defined in build/make/core/envsetup.mk
# and art/build/Android.common.mk have different meanings:
#
# * In build/make/core/envsetup.mk:
# * HOST_ARCH := x86_64
# * HOST_2ND_ARCH := x86
#
# * In art/build/Android.common.mk:
# * When `HOST_PREFER_32_BIT` is `true`:
# * ART_HOST_ARCH := x86
# * 2ND_ART_HOST_ARCH :=
# * 2ND_HOST_ARCH :=
# * Otherwise:
# * ART_HOST_ARCH := x86_64
# * 2ND_ART_HOST_ARCH := x86
# * 2ND_HOST_ARCH := x86
ifeq ($(HOST_PREFER_32_BIT),true)
gtest_deps += $$(2ND_HOST_BOOT_IMAGE) # Depend on the 32-bit boot image.
else
gtest_deps += $$($(3)HOST_BOOT_IMAGE)
endif
.PHONY: $$(gtest_build_rule)
$$(gtest_build_rule) : $$(gtest_exe) $$(gtest_deps)
.PHONY: $$(gtest_rule)
$$(gtest_rule): $$(gtest_output)
# Re-run the tests, even if nothing changed. Until the build system has a dedicated "no cache"
# option, claim to write a file that is never produced.
$$(gtest_output): .KATI_IMPLICIT_OUTPUTS := $$(gtest_output)-nocache
# Limit concurrent runs. Each test itself is already highly parallel (and thus memory hungry).
$$(gtest_output): .KATI_NINJA_POOL := highmem_pool
$$(gtest_output): NAME := $$(gtest_rule)
ifeq (,$(SANITIZE_HOST))
$$(gtest_output): $$(gtest_exe) $$(gtest_deps)
$(hide) ($$(call ART_TEST_SKIP,$$(NAME)) && \
timeout --foreground -k 120s 2400s $(HOST_OUT_EXECUTABLES)/signal_dumper -s 15 \
$$< --gtest_output=xml:$$@ && \
$$(call ART_TEST_PASSED,$$(NAME))) || $$(call ART_TEST_FAILED,$$(NAME))
else
# Note: envsetup currently exports ASAN_OPTIONS=detect_leaks=0 to suppress leak detection, as some
# build tools (e.g., ninja) intentionally leak. We want leak checks when we run our tests, so
# override ASAN_OPTIONS. b/37751350
# Note 2: Under sanitization, also capture the output, and run it through the stack tool on failure
# (with the x86-64 ABI, as this allows symbolization of both x86 and x86-64). We don't do this in
# general as it loses all the color output, and we have our own symbolization step when not running
# under ASAN.
$$(gtest_output): $$(gtest_exe) $$(gtest_deps)
$(hide) ($$(call ART_TEST_SKIP,$$(NAME)) && set -o pipefail && \
ASAN_OPTIONS=detect_leaks=1 timeout --foreground -k 120s 3600s \
$(HOST_OUT_EXECUTABLES)/signal_dumper -s 15 \
$$< --gtest_output=xml:$$@ 2>&1 | tee $$<.tmp.out >&2 && \
{ $$(call ART_TEST_PASSED,$$(NAME)) ; rm $$<.tmp.out ; }) || \
( grep -q AddressSanitizer $$<.tmp.out && export ANDROID_BUILD_TOP=`pwd` && \
{ echo "ABI: 'x86_64'" | cat - $$<.tmp.out | development/scripts/stack | tail -n 3000 ; } ; \
rm $$<.tmp.out ; $$(call ART_TEST_FAILED,$$(NAME)))
endif
ART_TEST_HOST_GTEST$$($(3)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(gtest_rule)
ART_TEST_HOST_GTEST_BUILD_RULES += $$(gtest_build_rule)
ART_TEST_HOST_GTEST$$($(3)ART_PHONY_TEST_HOST_SUFFIX)_BUILD_RULES += $$(gtest_build_rule)
ART_TEST_HOST_GTEST_RULES += $$(gtest_rule)
ART_TEST_HOST_GTEST_$(1)_RULES += $$(gtest_rule)
# Clear locally defined variables.
gtest_deps :=
gtest_exe :=
gtest_output :=
gtest_rule :=
gtest_suffix :=
endef # define-art-gtest-rule-host
ART_TEST_HOST_GTEST_DEPENDENCIES := $(HOST_I18N_DATA)
ART_TEST_TARGET_GTEST_DEPENDENCIES := $(TESTING_ART_APEX)
# Add the additional dependencies for the specified test
# $(1): test name
define add-art-gtest-dependencies
# Note that, both the primary and the secondary arches of the libs are built by depending
# on the module name.
gtest_deps := \
$$(ART_GTEST_$(1)_TARGET_DEPS) \
$(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_TARGET_GTEST_$(file)_DEX)) \
ART_TEST_TARGET_GTEST_DEPENDENCIES += $$(gtest_deps)
# Clear locally defined variables.
gtest_deps :=
endef # add-art-gtest-dependencies
# $(1): file name
# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture.
define define-art-gtest-host
art_gtest_filename := $(1)
include $$(CLEAR_VARS)
art_gtest_name := $$(notdir $$(basename $$(art_gtest_filename)))
ifndef ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES
ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES :=
endif
$$(eval $$(call define-art-gtest-rule-host,$$(art_gtest_name),$$(art_gtest_filename),$(2)))
# Clear locally defined variables.
art_gtest_filename :=
art_gtest_name :=
endef # define-art-gtest-host
# Define the rules to build and run gtests for both archs on host.
# $(1): test name
define define-art-gtest-host-both
art_gtest_name := $(1)
.PHONY: test-art-host-gtest-$$(art_gtest_name)
test-art-host-gtest-$$(art_gtest_name): $$(ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES)
$$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
# Clear now unused variables.
ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES :=
art_gtest_name :=
endef # define-art-gtest-host-both
ifeq ($(ART_BUILD_TARGET),true)
$(foreach name,$(ART_TARGET_GTEST_NAMES), $(eval $(call add-art-gtest-dependencies,$(name),)))
ART_TEST_TARGET_GTEST_DEPENDENCIES += \
com.android.i18n \
libjavacore.com.android.art.testing \
libopenjdkd.com.android.art.testing \
com.android.art.testing \
com.android.conscrypt
endif
ifeq ($(ART_BUILD_HOST),true)
$(foreach file,$(ART_HOST_GTEST_FILES), $(eval $(call define-art-gtest-host,$(file),)))
ifneq ($(HOST_PREFER_32_BIT),true)
$(foreach file,$(2ND_ART_HOST_GTEST_FILES), $(eval $(call define-art-gtest-host,$(file),2ND_)))
endif
# Rules to run the different architecture versions of the gtest.
$(foreach file,$(ART_HOST_GTEST_FILES), $(eval $(call define-art-gtest-host-both,$$(notdir $$(basename $$(file))))))
endif
# Define all the combinations of host/target and suffix such as:
# test-art-host-gtest or test-art-host-gtest64
# $(1): host or target
# $(2): HOST or TARGET
# $(3): undefined, 32 or 64
define define-test-art-gtest-combination
ifeq ($(1),host)
ifneq ($(2),HOST)
$$(error argument mismatch $(1) and ($2))
endif
else
ifneq ($(1),target)
$$(error found $(1) expected host or target)
endif
ifneq ($(2),TARGET)
$$(error argument mismatch $(1) and ($2))
endif
endif
rule_name := test-art-$(1)-gtest$(3)
dependencies := $$(ART_TEST_$(2)_GTEST$(3)_RULES)
.PHONY: $$(rule_name)
$$(rule_name): $$(dependencies) d8
$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
# Clear locally defined variables.
rule_name :=
dependencies :=
endef # define-test-art-gtest-combination
$(eval $(call define-test-art-gtest-combination,target,TARGET,))
$(eval $(call define-test-art-gtest-combination,target,TARGET,$(ART_PHONY_TEST_TARGET_SUFFIX)))
ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX
$(eval $(call define-test-art-gtest-combination,target,TARGET,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
endif
$(eval $(call define-test-art-gtest-combination,host,HOST,))
$(eval $(call define-test-art-gtest-combination,host,HOST,$(ART_PHONY_TEST_HOST_SUFFIX)))
ifneq ($(HOST_PREFER_32_BIT),true)
$(eval $(call define-test-art-gtest-combination,host,HOST,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
endif
# Define all the combinations of host/target and suffix such as:
# test-art-host-gtest-dependencies or test-art-host-gtest-dependencies64
# $(1): host or target
# $(2): HOST or TARGET
# $(3): undefined, 32 or 64
define define-test-art-gtest-dependency-combination
ifeq ($(1),host)
ifneq ($(2),HOST)
$$(error argument mismatch $(1) and ($2))
endif
else
ifneq ($(1),target)
$$(error found $(1) expected host or target)
endif
ifneq ($(2),TARGET)
$$(error argument mismatch $(1) and ($2))
endif
endif
rule_name := test-art-$(1)-gtest-dependencies$(3)
dependencies := $$(ART_TEST_$(2)_GTEST$(3)_BUILD_RULES)
.PHONY: $$(rule_name)
$$(rule_name): $$(dependencies) d8
# Clear locally defined variables.
rule_name :=
dependencies :=
endef # define-test-art-gtest-dependency-combination
# TODO Get target-deps working too
# $(eval $(call define-test-art-gtest-dependency-combination,target,TARGET,))
# $(eval $(call define-test-art-gtest-dependency-combination,target,TARGET,$(ART_PHONY_TEST_TARGET_SUFFIX)))
# ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX
# $(eval $(call define-test-art-gtest-dependency-combination,target,TARGET,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
# endif
$(eval $(call define-test-art-gtest-dependency-combination,host,HOST,))
$(eval $(call define-test-art-gtest-dependency-combination,host,HOST,$(ART_PHONY_TEST_HOST_SUFFIX)))
ifneq ($(HOST_PREFER_32_BIT),true)
$(eval $(call define-test-art-gtest-dependency-combination,host,HOST,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
endif
# Clear locally defined variables.
define-art-gtest-rule-target :=
define-art-gtest-rule-host :=
define-art-gtest :=
define-test-art-gtest-combination :=
RUNTIME_GTEST_COMMON_SRC_FILES :=
COMPILER_GTEST_COMMON_SRC_FILES :=
RUNTIME_GTEST_TARGET_SRC_FILES :=
RUNTIME_GTEST_HOST_SRC_FILES :=
COMPILER_GTEST_TARGET_SRC_FILES :=
COMPILER_GTEST_HOST_SRC_FILES :=
ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_GTEST_RULES :=
ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_BUILD_RULES :=
ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_BUILD_RULES :=
ART_TEST_HOST_GTEST_BUILD_RULES :=
ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST_RULES :=
ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_BUILD_RULES :=
ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_BUILD_RULES :=
ART_TEST_TARGET_GTEST_BUILD_RULES :=
ART_GTEST_TARGET_ANDROID_ROOT :=
ART_GTEST_TARGET_ANDROID_I18N_ROOT :=
ART_GTEST_TARGET_ANDROID_ART_ROOT :=
ART_GTEST_TARGET_ANDROID_TZDATA_ROOT :=
ART_GTEST_class_linker_test_DEX_DEPS :=
ART_GTEST_class_table_test_DEX_DEPS :=
ART_GTEST_compiler_driver_test_DEX_DEPS :=
ART_GTEST_dex_file_test_DEX_DEPS :=
ART_GTEST_exception_test_DEX_DEPS :=
ART_GTEST_elf_writer_test_HOST_DEPS :=
ART_GTEST_elf_writer_test_TARGET_DEPS :=
ART_GTEST_imtable_test_DEX_DEPS :=
ART_GTEST_jni_compiler_test_DEX_DEPS :=
ART_GTEST_jni_internal_test_DEX_DEPS :=
ART_GTEST_oat_file_assistant_test_DEX_DEPS :=
ART_GTEST_oat_file_assistant_test_HOST_DEPS :=
ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
ART_GTEST_dexanalyze_test_DEX_DEPS :=
ART_GTEST_dexoptanalyzer_test_DEX_DEPS :=
ART_GTEST_dexoptanalyzer_test_HOST_DEPS :=
ART_GTEST_dexoptanalyzer_test_TARGET_DEPS :=
ART_GTEST_image_space_test_DEX_DEPS :=
ART_GTEST_image_space_test_HOST_DEPS :=
ART_GTEST_image_space_test_TARGET_DEPS :=
ART_GTEST_dex2oat_test_DEX_DEPS :=
ART_GTEST_dex2oat_test_HOST_DEPS :=
ART_GTEST_dex2oat_test_TARGET_DEPS :=
ART_GTEST_dex2oat_image_test_DEX_DEPS :=
ART_GTEST_dex2oat_image_test_HOST_DEPS :=
ART_GTEST_dex2oat_image_test_TARGET_DEPS :=
ART_GTEST_object_test_DEX_DEPS :=
ART_GTEST_proxy_test_DEX_DEPS :=
ART_GTEST_reflection_test_DEX_DEPS :=
ART_GTEST_stub_test_DEX_DEPS :=
ART_GTEST_transaction_test_DEX_DEPS :=
ART_GTEST_dex2oat_environment_tests_DEX_DEPS :=
ART_GTEST_heap_verification_test_DEX_DEPS :=
ART_GTEST_verifier_deps_test_DEX_DEPS :=
$(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=))
$(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=))
ART_TEST_HOST_GTEST_MainStripped_DEX :=
ART_TEST_TARGET_GTEST_MainStripped_DEX :=
ART_TEST_HOST_GTEST_MainUncompressedAligned_DEX :=
ART_TEST_TARGET_GTEST_MainUncompressedAligned_DEX :=
ART_TEST_HOST_GTEST_EmptyUncompressed_DEX :=
ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX :=
ART_TEST_GTEST_VerifierDeps_SRC :=
ART_TEST_HOST_GTEST_VerifierDeps_DEX :=
ART_TEST_TARGET_GTEST_VerifierDeps_DEX :=
ART_TEST_GTEST_VerifySoftFailDuringClinit_SRC :=
ART_TEST_HOST_GTEST_VerifySoftFailDuringClinit_DEX :=
ART_TEST_TARGET_GTEST_VerifySoftFailDuringClinit_DEX :=
GTEST_DEX_DIRECTORIES :=
LOCAL_PATH :=
endif # ifneq ($(HOST_OS),darwin)

116
build/README.md Normal file
View File

@ -0,0 +1,116 @@
# Building the ART Module
ART is built as a module in the form of an APEX package, `com.android.art.apex`.
That package can be installed with `adb install` on a device running Android S
or later. It is also included in the system partition (in the `system/apex`
directory) of platform releases, to ensure it is always available.
The recommended way to build the ART Module is to use the `master-art` manifest,
which only has the sources and dependencies required for the module.
Currently it is also possible to build ART directly from sources in a platform
build, i.e. as has been the traditional way up until Android S. However that
method is being phased out.
The ART Module is available as a debug variant, `com.android.art.debug.apex`,
which has extra internal consistency checks enabled, and some debug tools. A
device cannot have both the non-debug and debug variants installed at once - it
may not boot then.
`com.google.android.art.apex` (note `.google.`) is the Google signed variant of
the module. It is also mutually exclusive with the other ones.
## Building as a module on `master-art`
1. Check out the `master-art` tree:
```
repo init -b master-art -u <repository url>
```
See the [Android source access
instructions](https://source.android.com/setup/build/downloading) for
further details.
2. Set up the development environment:
```
banchan com.android.art <arch>
export SOONG_ALLOW_MISSING_DEPENDENCIES=true
```
For Google internal builds on the internal master-art branch, specify
instead the Google variant of the module and product:
```
banchan com.google.android.art mainline_modules_<arch>
export SOONG_ALLOW_MISSING_DEPENDENCIES=true
```
`<arch>` is the device architecture, one of `arm`, `arm64`, `x86`, or
`x86_64`. Regardless of the device architecture, the build also includes the
usual host architectures, and 64/32-bit multilib for the 64-bit products.
To build the debug variant of the module, specify `com.android.art.debug`
instead of `com.android.art`. It is also possible to list both.
3. Build the module:
```
m apps_only dist
```
4. Install the module and reboot:
```
adb install out/dist/com.android.art.apex
adb reboot
```
The name of the APEX file depends on what you passed to `banchan`.
## Building as part of the base system image
NOTE: This method of building is slated to be obsoleted in favor of the
module build on `master-art` above (b/172480617).
1. Check out a full Android platform tree and lunch the appropriate product the
normal way.
2. Ensure the ART Module is built from source:
```
export ART_MODULE_BUILD_FROM_SOURCE=true
```
If this isn't set then the build may use prebuilts of the ART Module that
may be older than the sources.
3. Build the system image the normal way, for example:
```
m droid
```
## Prebuilts
Prebuilts are used for the ART Module dependencies that have sources outside the
`master-art` manifest. Conversely the ART Module may be a prebuilt when used in
platform builds of the base system image.
The locations of the prebuilts are:
* `prebuilts/runtime/mainline` for prebuilts and SDKs required to build the ART
Module.
See
[prebuilts/runtime/mainline/README.md](https://android.googlesource.com/platform/prebuilts/runtime/+/master/mainline/README.md)
for instructions on how to update them.
* `packages/modules/ArtPrebuilt` for the ART Module APEX packages, if present.
* `prebuilts/module_sdk/art` for the ART Module SDK and other tools, needed to
build platform images and other modules that depend on the ART Module.

95
build/SoongConfig.bp Normal file
View File

@ -0,0 +1,95 @@
// Set up Soong config variables.
// https://android.googlesource.com/platform/build/soong/+/master/README.md#soong-config-variables
// The source_build variable in the art_module namespace is used to enable the
// apex, sdk, and module_exports modules that make up the ART Module by calling
// $(call soong_config_set,art_module,source_build,true) in make. (which is
// set to the value of the ART_MODULE_BUILD_FROM_SOURCE variable)
// TODO(b/172480617): Clean up when ART source is no longer in the platform
// manifest.
soong_config_bool_variable {
name: "source_build",
}
soong_config_module_type {
name: "art_module_art_global_defaults",
module_type: "art_global_defaults",
config_namespace: "art_module",
bool_variables: ["source_build"],
properties: ["enabled"],
}
soong_config_module_type {
name: "art_module_apex_defaults",
module_type: "apex_defaults",
config_namespace: "art_module",
bool_variables: ["source_build"],
properties: ["enabled"],
}
soong_config_module_type {
name: "art_module_cc_defaults",
module_type: "cc_defaults",
config_namespace: "art_module",
bool_variables: ["source_build"],
properties: [
"enabled",
"target.android.test_for",
],
}
soong_config_module_type {
name: "art_module_cc_genrule",
module_type: "cc_genrule",
config_namespace: "art_module",
bool_variables: ["source_build"],
properties: ["enabled"],
}
soong_config_module_type {
name: "art_module_exports",
module_type: "module_exports",
config_namespace: "art_module",
bool_variables: ["source_build"],
properties: ["enabled"],
}
soong_config_module_type {
name: "art_module_java_defaults",
module_type: "java_defaults",
config_namespace: "art_module",
bool_variables: ["source_build"],
properties: ["enabled"],
}
soong_config_module_type {
name: "art_module_genrule_defaults",
module_type: "genrule_defaults",
config_namespace: "art_module",
bool_variables: ["source_build"],
properties: ["enabled"],
}
soong_config_module_type {
name: "art_module_prebuilt_defaults",
module_type: "prebuilt_defaults",
config_namespace: "art_module",
bool_variables: ["source_build"],
properties: ["enabled"],
}
soong_config_module_type {
name: "art_module_sdk",
module_type: "sdk",
config_namespace: "art_module",
bool_variables: ["source_build"],
properties: ["enabled"],
}
soong_config_module_type {
name: "art_module_sh_binary",
module_type: "sh_binary",
config_namespace: "art_module",
bool_variables: ["source_build"],
properties: ["enabled"],
}

600
build/apex/Android.bp Normal file
View File

@ -0,0 +1,600 @@
// ART APEX module
//
// Contains both the Android Managed Runtime (ART) and the Android Core Library
// (Libcore).
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "art_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
soong_config_module_type_import {
from: "art/build/SoongConfig.bp",
module_types: [
"art_module_apex_defaults",
"art_module_cc_defaults",
],
}
// Modules listed in LOCAL_REQUIRED_MODULES for module art-runtime in art/Android.mk.
// - Base requirements (binaries for which both 32- and 64-bit versions are built, if possible).
art_runtime_base_binaries_both = [
"dalvikvm",
]
art_runtime_base_binaries_both_on_device_first_on_host = [
"dex2oat",
]
// - Base requirements (binaries for which the "first" version is preferred on device
// (likely 64-bit) and on host).
art_runtime_base_binaries_first_on_device_first_on_host = [
"dexoptanalyzer",
"profman",
]
// - Base requirements (libraries).
//
// Note: ART on-device chroot-based testing and benchmarking is not yet using
// the ART APEX, meaning that copies of some of these libraries have to be
// installed in `/system` for the ART Buildbot set-up to work properly. This is
// done by the `standalone-apex-files` Make phony target, used by the ART
// Buildbot and Golem (see `art/Android.mk`). If you add libraries to this list,
// you may have to also add them to `PRIVATE_ART_APEX_DEPENDENCY_LIBS` in
// `art/Android.mk`.
// TODO(b/121117762): Remove this note when both the ART Buildbot and Golem use
// the ART APEX.
art_runtime_base_native_shared_libs_minus_libart = [
// External API (having APEX stubs).
"libdexfile",
"libnativebridge",
"libnativehelper",
"libnativeloader",
"libsigchain",
// libadbconnection is internal and loaded with dlopen(), but it cannot use
// "required" because of cyclic dependency (b/124505714).
"libadbconnection",
// TODO(b/124476339): Clean up the following libraries once "required"
// dependencies work with APEX libraries.
"libart-compiler",
"libartservice",
"libdt_fd_forward",
"libdt_socket",
"libjdwp",
"libnpt",
"libopenjdkjvm",
"libopenjdkjvmti",
// TODO(chriswailes): Make libarttools a dependency for another target
// when such a target exists
"libarttools",
]
// Actual version of ART runtime base libs, used in non-test ART APEXes.
art_runtime_base_native_shared_libs = ["libart"] +
art_runtime_base_native_shared_libs_minus_libart
// "Broken" version of ART runtime base libs, used for testing purposes.
art_runtime_base_broken_native_shared_libs = ["libart-broken"] +
art_runtime_base_native_shared_libs_minus_libart
art_runtime_base_native_device_only_shared_libs = [
"libperfetto_hprof",
]
bionic_native_shared_libs = [
// External API (having APEX stubs).
"libc",
"libm",
"libdl",
]
bionic_binaries_both = [
"linker",
// To validate the zip file generated by cloud server.
"ziptool",
]
// - Debug variants (binaries for which the "first" version is preferred on device
// (likely 64-bit) and on host).
art_runtime_debug_binaries_first_on_device_first_on_host = [
"dexoptanalyzerd",
"profmand",
]
art_runtime_debug_binaries_both_on_device_first_on_host = [
"dex2oatd",
]
// - Debug variants (libraries).
art_runtime_debug_native_shared_libs = [
"libadbconnectiond",
"libartd",
"libartd-compiler",
"libdexfiled",
"libopenjdkjvmd",
"libopenjdkjvmtid",
]
art_runtime_base_native_device_only_debug_shared_libs = [
"libperfetto_hprofd",
]
// Libraries needed to execute ART run-tests.
// TODO(b/124476339): When bug 124476339 is fixed, add these libraries as `runtime_libs`
// dependencies of `libartd-compiler`, and remove `art_runtime_run_test_libs`.
art_runtime_run_test_libs = [
"libart-disassembler",
"libartd-disassembler",
]
// Tools common to both device APEX and host APEX. Derived from art-tools in art/Android.mk.
art_tools_common_binaries = [
"dexdump",
"dexlist",
]
// Tools common to both device and host debug APEXes.
art_tools_debug_binaries = [
"dexanalyze",
"dexdiag",
"dexlayout",
"dexlayoutd",
]
art_tools_debug_binaries_both = [
"imgdiag",
"imgdiagd",
]
// Tools exclusively for the device APEX derived from art-tools in art/Android.mk.
art_tools_device_only_binaries = [
// oatdump cannot link with host linux_bionic due to not using clang lld;
// TODO: Make it work with clang lld.
"oatdump",
]
// Same, but for only for debug packages.
art_tools_debug_device_only_binaries = [
// oatdumpd cannot link with host linux_bionic due to not using clang lld;
// TODO: Make it work with clang lld.
"oatdumpd",
]
// Tools exclusively for the host APEX derived from art-tools in art/Android.mk.
art_tools_host_only_binaries = [
// FIXME: Does not work as-is, because `ahat` is defined in tools/ahat/Android.mk
// (same issue as for `libart_fake` above).
//"ahat",
"hprof-conv",
]
// Core Java libraries.
libcore_java_libs = [
"core-oj",
"core-libart",
"okhttp",
"bouncycastle",
"apache-xml",
]
// Create combined library which is used for compiling run-tests.
// This is much easier than trying to make the test depend on them directly,
// or than trying to make the test compilation depend on the apex module.
// Some of the components are only visible here (but not in test Android.bp).
java_library {
name: "art-run-test-bootclasspath",
sdk_version: "core_platform",
static_libs: libcore_java_libs + [
"framework-annotations-lib",
],
}
// Native libraries that support the core Java libraries.
//
// Note: ART on-device chroot-based testing and benchmarking is not yet using
// the ART APEX, meaning that copies of some of these libraries have to be
// installed in `/system` for the ART Buildbot set-up to work properly. This is
// done by the `standalone-apex-files` Make phony target, used by the ART
// Buildbot and Golem (see `art/Android.mk`). If you add libraries to this list,
// you may have to also add them to `PRIVATE_ART_APEX_DEPENDENCY_LIBS` in
// `art/Android.mk`.
// TODO(b/121117762): Remove this note when both the ART Buildbot and Golem use
// the ART APEX.
libcore_native_shared_libs = [
// External API (having APEX stubs).
"libandroidio",
// TODO(b/124476339): Clean up the following libraries once "required"
// dependencies work with APEX libraries.
"libexpat",
"libjavacore",
"libopenjdk",
]
libcore_debug_native_shared_libs = [
"libopenjdkd",
]
// Temporary library includes for b/123591866 as all libraries are moved into the main art-apex.
art_runtime_libraries_zipapex = [
"libnativebridge",
"libnativeloader",
"libnativehelper",
"libcutils",
]
android_app_certificate {
name: "com.android.art.certificate",
certificate: "com.android.art",
}
apex_key {
name: "com.android.art.key",
public_key: "com.android.art.avbpubkey",
private_key: "com.android.art.pem",
}
art_module_apex_defaults {
name: "com.android.art-defaults",
// Enable if ART_MODULE_BUILD_FROM_SOURCE is true
enabled: false,
soong_config_variables: {
source_build: {
enabled: true,
},
},
target: {
windows: {
// When the Soong config variable above sets enabled:true, it
// overrides the default false for targets, so we need to disable
// windows explicitly.
enabled: false,
},
},
}
// Default values shared by device ART APEXes.
apex_defaults {
name: "com.android.art-device-defaults-minus-odrefresh",
defaults: [
"com.android.art-defaults",
"s-launched-apex-module",
],
compile_multilib: "both",
manifest: "manifest-art.json",
bootclasspath_fragments: ["art-bootclasspath-fragment"],
systemserverclasspath_fragments: ["art-systemserverclasspath-fragment"],
compat_configs: ["libcore-platform-compat-config"],
java_libs: libcore_java_libs,
native_shared_libs: art_runtime_base_native_shared_libs +
art_runtime_base_native_device_only_shared_libs +
libcore_native_shared_libs,
binaries: [
"artd",
],
multilib: {
both: {
binaries: art_runtime_base_binaries_both +
art_runtime_base_binaries_both_on_device_first_on_host,
},
first: {
binaries: art_runtime_base_binaries_first_on_device_first_on_host +
art_tools_common_binaries +
art_tools_device_only_binaries,
},
},
key: "com.android.art.key",
required: [
"com.android.i18n",
],
prebuilts: [
"art-linker-config",
"com.android.art.artd.init.rc",
"current_sdkinfo",
],
// ART APEXes depend on bouncycastle which is disabled for PDK builds.
// Since the dependency is disabled, ART APEXes can't be built either.
// Disable the APEXes too. See b/157267166.
product_variables: {
pdk: {
enabled: false,
},
},
// Indicates that pre-installed version of this apex can be compressed.
// Whether it actually will be compressed is controlled on per-device basis.
compressible: true,
}
apex_defaults {
name: "com.android.art-device-defaults",
defaults: ["com.android.art-device-defaults-minus-odrefresh"],
multilib: {
first: {
binaries: ["odrefresh"],
},
},
}
// Default values shared by (device) Debug and Testing ART APEXes.
apex_defaults {
name: "com.android.art-devel-defaults",
defaults: ["com.android.art-device-defaults"],
native_shared_libs: art_runtime_base_native_device_only_debug_shared_libs +
art_runtime_run_test_libs +
art_runtime_debug_native_shared_libs +
libcore_debug_native_shared_libs,
multilib: {
both: {
binaries: art_tools_debug_binaries_both +
art_runtime_debug_binaries_both_on_device_first_on_host,
},
first: {
binaries: art_runtime_debug_binaries_first_on_device_first_on_host +
art_tools_debug_binaries +
art_tools_debug_device_only_binaries,
},
},
}
// "Broken" test APEX, only used for testing, including module
// `libart-broken` instead of `libart`.
apex_test {
name: "test_broken_com.android.art",
// Use of "s-launched-apex-module" does not imply that this is a released
// module.
defaults: [
"com.android.art-defaults",
"s-launched-apex-module",
],
// Only include native libraries in this test APEX. Don't include
// binaries (and maybe other artifacts) for now, as they pull
// the "non-broken" `libart` module into this test APEX and
// overwrite `libart-broken`. Maybe consider creating "broken"
// variants of binaries (and other artifacts)?
native_shared_libs: art_runtime_base_broken_native_shared_libs,
compile_multilib: "both",
key: "com.android.art.key",
manifest: "test_apex_manifest.json",
file_contexts: ":com.android.art-file_contexts",
certificate: ":com.android.art.certificate",
installable: false,
}
apex_test {
name: "test_jitzygote_com.android.art",
defaults: ["com.android.art-device-defaults-minus-odrefresh"],
multilib: {
first: {
binaries: ["odrefresh_broken"],
},
},
key: "com.android.art.key",
manifest: "test_apex_manifest.json",
file_contexts: ":com.android.art-file_contexts",
certificate: ":com.android.art.certificate",
installable: false,
}
// Release version of the ART APEX module (not containing debug
// variants nor tools), included in user builds. Also used for
// storage-constrained devices in userdebug and eng builds.
apex {
name: "com.android.art",
defaults: ["com.android.art-device-defaults"],
certificate: ":com.android.art.certificate",
}
// "Debug" version of the ART APEX module (containing both release and
// debug variants, as well as additional tools), included in userdebug and
// eng build.
apex {
name: "com.android.art.debug",
defaults: ["com.android.art-devel-defaults"],
certificate: ":com.android.art.certificate",
}
// ART gtests with dependencies on internal ART APEX libraries.
art_gtests = [
"art_cmdline_tests",
"art_compiler_tests",
"art_dex2oat_tests",
"art_dexanalyze_tests",
"art_dexdiag_tests",
"art_dexdump_tests",
"art_dexlayout_tests",
"art_dexlist_tests",
"art_dexoptanalyzer_tests",
"art_imgdiag_tests",
"art_libartbase_tests",
"art_libartpalette_tests",
"art_libartservice_tests",
"art_libarttools_tests",
"art_libdexfile_tests",
"art_libdexfile_support_tests",
"art_libprofile_tests",
"art_oatdump_tests",
"art_odrefresh_tests",
"art_profman_tests",
"art_runtime_compiler_tests",
"art_runtime_tests",
"art_sigchain_tests",
]
// "Testing" version of the ART APEX module (containing both release
// and debug variants, additional tools, and ART gtests), for testing
// purposes only.
apex_test {
name: "com.android.art.testing",
defaults: ["com.android.art-devel-defaults"],
file_contexts: ":com.android.art.debug-file_contexts",
certificate: ":com.android.art.certificate",
tests: art_gtests,
binaries: ["signal_dumper"], // Need signal_dumper for run-tests.
updatable: false,
}
// TODO: Do this better. art_apex_test_host will disable host builds when
// HOST_PREFER_32_BIT is set. We cannot simply use com.android.art.debug
// because binaries have different multilib classes and 'multilib: {}' isn't
// supported by target: { ... }.
// See b/120617876 for more information.
art_apex_test_host {
name: "com.android.art.host",
defaults: ["com.android.art-defaults"],
compile_multilib: "both",
payload_type: "zip",
host_supported: true,
device_supported: false,
manifest: "manifest-art.json",
updatable: false,
java_libs: libcore_java_libs,
ignore_system_library_special_case: true,
native_shared_libs: art_runtime_base_native_shared_libs +
art_runtime_debug_native_shared_libs +
libcore_native_shared_libs +
libcore_debug_native_shared_libs +
art_runtime_libraries_zipapex +
art_runtime_run_test_libs,
multilib: {
both: {
binaries: art_runtime_base_binaries_both +
art_tools_debug_binaries_both,
},
first: {
binaries: art_runtime_base_binaries_both_on_device_first_on_host +
art_runtime_base_binaries_first_on_device_first_on_host +
art_runtime_debug_binaries_first_on_device_first_on_host +
art_runtime_debug_binaries_both_on_device_first_on_host +
art_tools_common_binaries +
art_tools_debug_binaries + // Host APEX is always debug.
art_tools_host_only_binaries,
},
},
key: "com.android.art.key",
target: {
darwin: {
enabled: false,
},
linux_bionic: {
multilib: {
both: {
native_shared_libs: bionic_native_shared_libs,
binaries: bionic_binaries_both,
},
},
},
},
// ART APEXes depend on bouncycastle which is disabled for PDK builds.
// Since the dependency is disabled, ART APEXes can't be built either.
// Disable the APEXes too. See b/157267166.
product_variables: {
pdk: {
enabled: false,
},
},
}
python_binary_host {
name: "art-apex-tester",
srcs: ["art_apex_test.py"],
main: "art_apex_test.py",
}
// Genrules so we can run the checker, and empty Java library so that it gets executed.
art_check_apex_gen_stem = "$(location art-apex-tester)" +
" --deapexer $(location deapexer)" +
" --debugfs $(location debugfs_static)" +
" --tmpdir $(genDir)"
// The non-flattened APEXes are always checked, as they are always generated
// (even when APEX flattening is enabled).
genrule_defaults {
name: "art-check-apex-gen-defaults",
defaults: ["art_module_source_build_genrule_defaults"],
tools: [
"art-apex-tester",
"deapexer",
"debugfs_static",
],
}
art_module_cc_defaults {
name: "art-check-apex-gen-fakebin-defaults",
host_supported: true,
device_supported: false,
// Enable if ART_MODULE_BUILD_FROM_SOURCE is true
enabled: false,
soong_config_variables: {
source_build: {
enabled: true,
},
},
target: {
darwin: {
enabled: false, // No python3.
},
},
}
genrule {
name: "art-check-release-apex-gen",
defaults: ["art-check-apex-gen-defaults"],
srcs: [":com.android.art"],
cmd: art_check_apex_gen_stem +
" --flavor release" +
" $(in)" +
" && touch $(out)" +
" && chmod a+x $(out)",
out: ["art-check-release-apex-gen.unused"],
}
cc_prebuilt_binary {
name: "art-check-release-apex-gen-fakebin",
defaults: ["art-check-apex-gen-fakebin-defaults"],
srcs: [":art-check-release-apex-gen"],
}
genrule {
name: "art-check-debug-apex-gen",
defaults: ["art-check-apex-gen-defaults"],
srcs: [":com.android.art.debug"],
cmd: art_check_apex_gen_stem +
" --flavor debug" +
" $(in)" +
" && touch $(out)" +
" && chmod a+x $(out)",
out: ["art-check-debug-apex-gen.unused"],
}
cc_prebuilt_binary {
name: "art-check-debug-apex-gen-fakebin",
defaults: ["art-check-apex-gen-fakebin-defaults"],
srcs: [":art-check-debug-apex-gen"],
}
genrule {
name: "art-check-testing-apex-gen",
defaults: ["art-check-apex-gen-defaults"],
srcs: [":com.android.art.testing"],
cmd: art_check_apex_gen_stem +
" --flavor testing" +
" $(in)" +
" && touch $(out)" +
" && chmod a+x $(out)",
out: ["art-check-testing-apex-gen.unused"],
}
cc_prebuilt_binary {
name: "art-check-testing-apex-gen-fakebin",
defaults: ["art-check-apex-gen-fakebin-defaults"],
srcs: [":art-check-testing-apex-gen"],
}
linker_config {
name: "art-linker-config",
src: "linker.config.json",
installable: false,
}

1085
build/apex/art_apex_test.py Executable file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEArRrjkOjOEDk4/ZEdo6z7llFu8jNSkXvOWl95jRQ6ztgfM00y
VaqtI0xCdh/C3XI4b7xbhHq4rdZXHid+dpRzlq1BvQu3fPVjHeUItY9MOT0vfnHS
VrUIAHJxHtzMS+krtlNkqzhKtpvXBx4DbDP7B5KIDD276E6tn0ifqLBjsdJPkbso
+IkTMbykE/VBA7qe+Wx4v8Ay4iO1cfNREAlC6enXkxRXjCFz/MHz7CJjNjYmygmB
k5AGZMylpVU16OcRCMHJM+zjQ/DuktnGScRgZWw3xlgpR+13Ej6ayrAETqI7oAwP
2eDylVoccYdM/1wRFrYS7Ag6kk8xuvQr8015X3trVC5sM7dE/zEqqXo1yGGnUNdV
pGdexJ4f8fkHNCDmCKnPUSfkcZmJh3VuWk8w7mf5rvm9BzX3S9p6yEcoFWNT9Yxd
0TYU/Hl5E73+hvHWYkDfVxFK6GlCjPznkWKkOsA5M3HPsUAxcylfcW18uUcgx3sV
DCsBCjLXCxbOhu4RDkD5L7fA622kFiuhnLbVjyXDxF6Ip1jCqu8lm/ftDb3wnIDL
e5hP5JNxpC9mdu1dTojlM6ve5skOqjEPxJqOqclWHg6JGPA8oaJaqbVLNO2gd36U
siTZkHwxyOv8zqjwAKNY3jNGjELXgry+CBdV3URY1SDhRJ30X8ZXVJZW/acCAwEA
AQKCAgALboMoxrcVCzJgTH0QmhPjUW1qQUlqoip2fWehkXxwvIUS9j4kuijE8/xP
oLlVtn1To7THgvM/R7BpJWKMojEf+kElIujzL6FkEAQLOXNnNEs2pn2ljD8DCIu/
5gT33mYsnEVBqW4FsTT6G1lOhABH971UUZ9fMBL3OeyRT1TGIYVvslR6VVMXLcYI
K2InxONKxYcT4rV5ibIp3E+2J2Zr3C2IYQeHEY5/Wq+pIHw80EavgQE1pYVGkt09
lesBfoD5exK2gyZfDkIzIH6f0IAtMoBccOYJAf2jDs9aI1Wle8FESIejc9+RTWoj
dTP4iTP3s/575+82SlSWbBma77rcH4+gzcOnxoeJdzmpJYDqcK4tPeM62wKsD72q
LDTvbb9dF19g4yysCdeJwmTkYIGjYzWRsA6gWVT5MbMqqJPprYVQRQClpKjIFtaS
I1qjrgEqksbh7ZJd4018LappOcEMDtagWsz9CAmp0Tu1HlvFOMnTk2hCKE+QtDe6
COkIvz4sASS4XXxFRzyfF5l/LrMlAXCcX0YGvujZK/yxmVtZ7H6Mm8FZiRsPSboi
s45QqgzYttwdjJqYly+JyEFgyVrnFi1bVGSu3pwv9JF5wc/sJYV6cqptApco5LCI
M2xymtJD6MqYAJ9KpryaFb0HxEKP8NZykMz39nRqGotTP/HlwQKCAQEA2ng5JBym
dWIOXJ9BmkjidzkZRC+JtYaEEMT5+UvULRzQ0h+hjaiSz4P58+QEj8bSPL49njif
DzEwFQkxC+LL+FtcueGh4XTpB9hZiXIhakp1iL5iSJA211FjqQVwKb51Nx7qo1J7
gKg8yawID7vl8aU2ujwQpbAN1aZcY+rZBABV5YVSTw08a0O1w/5kaUms3FHCEETo
i3+Hzfra691dBTbCeD2fYNecNQnlv4S9VPId126c74Mu7GAI/yf8ti4weEDEhLB7
aMyYEkAf3uQiKxW7tSQvhOcJXTdBYfoFDXP5ysJob7RThzmbm4L8GixybVEY8A81
wBrSZHhZI9F8DQKCAQEAytejebi/SX+RymwnGJZOZeYPHNJN53Za6JSV77IrRMY2
BkS6VcO5fpJaSU9CBvwZCv+HvPo1hZJhaOmGcrgj80/LymLy66sm3hHDjixlBKhI
SVB4ivWjgxLKB08H5Xr1VdaqM2/4aIt3+hwV9hIA9uZAAAd/+4OO1OI8JbHlQxcn
IEKQJbTwk4kroVU23ydYydKrt6VXWkyYq3eODpuQFH+VSI5+YNbmIjmU7qN8iAEH
x2r4pSm5pkG8pxJOl4Am6pwfO0tozy7SJ3E7kL5DLWN1vr2pIVtfJQu/nZw4YP47
MLDpgdakQ/EjFmZBYknRy8TQHAcC1YvqMM9/Rq7PgwKCAQBEpOHPZvEmkNjSYXfL
cns12ssFkapDTzDP1BR3MExKoHM8kpPAXudCLMWszEhipKYKT/wsar4Pl/Tzpx+y
DGDqeEp6XVrv7DwMKv53IVU+gIbNoIRhKG8S2I/n272SYDWUTDKNfq0vj60J8PPX
fcKSWscHXTgd12OBbfQ5sODfUPusUme6Tv5c9bl3C8ehDXUzBL1lP0GSE1AoeFmx
IqzHpp0UCsi5NQXv5Fw8AQk8V5boyeilmSJ1QveQtI/C7mBsaG1XA9zC4QYnNd7N
ugDCaOHB2MzVhYJ7t5DjqBtOTtJ90vfdoVtdccxi4JU15CFQF9suEplg8wyIZQgd
KQVBAoIBAQCQmvRG9WchPZmwxjOE+rp/OGhzspWpOh4LCsptLAZerDOdemegdr0J
t8o30xIOKrCrv4mENpfrVnStNzYLGK1AaxWsfagSTFyUGfPgqlOF43ZNFdoprn5Y
FhAC79uARI0cGcISk3NzDGKG4njhiOo5GeJsYuxhYON1bqdUdCMuFhZlkC51Qy3y
7+ozxK0unz1T/CVA8dV5YXvBWaTjUxF/G4lQRY1g+jLsGULMca54wstJ5j/Gdx2L
ofec35c5uDmGLbCyM8tPUGCvj7DYOltnwy0QwuMNDbehkGOVN+aVdwi5aJW/Y0aN
zR2nfVSFfnzbGL8IDBOAK5PUIkjpGfyDAoIBAFxG99balQ7+dVhCdDlbE96zZO6u
ckgqIiU0F4nke53Svqy3kMIwcq975V2g0TKF+GPsLYDu2u1oHpBp0cEYErMolgcf
xWLjTc+7JgoeYJ9fhb1eGvet6L1znjInCFoRFnSGR6ctBZIg5ixIRsay6dBBFQVq
Fu1B/XJuc0ZdaPDmulOzyarAvBnsmZ8tkYg+O9RH7mpFfmjdWwNYpj86lUtOi9z7
qXFOZorylW2FhEwNUBeA6uvu/ThxtDCw5k3DMgaWdRwLUMxD0UwxjoFbIeyxGnH2
d4LbSNXp4vRDkfPiYW8GTep/pwcfFVtLX41IWkQke1i5PQ8uF2f3kCn2Eq0=
-----END RSA PRIVATE KEY-----

Binary file not shown.

View File

@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
MIIGHTCCBAWgAwIBAgIUGUj74fy+MBFtWuvs4ia1V8xBzfUwDQYJKoZIhvcNAQEL
BQAwgZwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
b2lkMRgwFgYDVQQDDA9jb20uYW5kcm9pZC5hcnQxIjAgBgkqhkiG9w0BCQEWE2Fu
ZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkwODE1MTM0MDA3WhgPNDc1NzA3MTExMzQw
MDdaMIGcMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UE
BwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5k
cm9pZDEYMBYGA1UEAwwPY29tLmFuZHJvaWQuYXJ0MSIwIAYJKoZIhvcNAQkBFhNh
bmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEAwa44WcQo0IKt5866YHNcsMOhCExtrC6/zL+WP0aX3CEnEIGocdXijyA/dyDZ
Cgrdh5FpPIUQaprhHYIsoklxYh5JocpbfH0wnkZhhyrjH+Lcks6GTlGNPxUa4c8e
vECwSM0RyO2oyxuwmVObmaz3poBXkikcFtE0rfNeRPD6j/SNaVf7lByrc7G+Ckz8
CoxTxhrKcvu9TEsp37fM+3lrXnAz49FsguCqEEAoH+JHHWE7OoCCaBIyCMk8ZCK3
39uAJSXeaoqW5WVDKw+ZKNJ3WL2xwB1QcYqG7ISp1rPY2mDUD0X8UpX1XljTToeQ
e8oTpl3z+jOX9Y9W7M0Pq16U7Ru7kFfLr6Xj14G74yrHgrcw6hCOKUjF0WJW4H27
ey6Lsn3ndj3IZYUERabB51q02yYu1b00X+ioa0uapucb4LU4663eSmHuWtpRj5Bq
waLLkWfFjmR4ct5ykKcULndB8VzZZgVc5PvGQDHNEB2OMoXj0DNH3Ey9V2Mtf1W6
Lp6lqjDUw0Ke36SvxXkMDf4PIIQJ355JWVAKkOGO4oIo3t2D3fLgrz8diwGi3dO8
EhOLJzaKh4qE3qHWp1x9aFfpYRF2qOWYQAi76CQ+8e/CzZDBxw+QGF8m4dRf7dxl
5I//5DBlcYPfeJ5iHpHrIcZtQjF2Gt7SDvJWrV7604jgwdMCAwEAAaNTMFEwHQYD
VR0OBBYEFFsPbt17BGjTSJ9ZR4RY3+lm19tfMB8GA1UdIwQYMBaAFFsPbt17BGjT
SJ9ZR4RY3+lm19tfMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
AD17YQ0AaRxZZGCFVUh/EOUIFCNP2j/4iYWeep/HYdQZ7xLWju6YQjoSLZ7ijbWw
IarkJiDgE9tT8C9KCNsQYR1ghG4OMppCypv2ZcfPdJZDyMQ3IfxbdPDywa1a1qZ/
QbvjPXei6KRlM/H0dFScM9TZqhKITGxih2eraZbvVyaPz/AHhbEn3BvN4l45IsAn
+K46uqBK8i6xm93nnPUp6yF3YppjC8TBfY0nSviNvce+XJ2izeGHrQ/IdLXucEby
TD7kcfFz1G5p8sSeqT6gc/SFJzjwuuwEUuKgq4HPiQOrQbFSvJ1kjSUdXlfs+o05
Ho4Fw+xrC6VFzmwWjkW4smpFT0MqpGE8buc36XOQFm5jCPF3sqjxwDxSXzpUeLt0
kmOp40rpeGMJ3AqWPr4vUmGVn6TPHLVKE3inETJLdO7Y6R0z39ccjKgomjEi8Mx9
oHKMGca96orY4kg+DOpnQ25LOwymJrBjrHnHIFgkRb4LrWibuqdMTwPAeHYxnnUe
Nv9rp2Usl8K1B9hSzm4pQwvsdVEE59SCYROU9qaymXBxR3TVlIOXqfIdOlTMf/0v
mR/02JvSHYbQiuqCYZkburQiPRxAH8DlJJFOVMMfuPmITXGNEsOonT2nqe36sfYz
ZIrC7hm5VeaEO8vExe6GV8gX5eIvFN+xuLxUf0FV7C7R
-----END CERTIFICATE-----

View File

@ -0,0 +1,10 @@
{
"permittedPaths": [
// JVMTI libraries used in ART testing are located under /data.
// TODO(b/171732668): Narrow this down.
"/data",
// For ART APEX primary boot image and odex files.
"/apex/com.android.art/javalib"
]
}

View File

@ -0,0 +1,10 @@
{
"name": "com.android.art",
"version": 330400000,
"provideNativeLibs": [
"libjdwp.so"
],
"requireNativeLibs": [
"libicu_jni.so"
]
}

235
build/apex/runtests.sh Executable file
View File

@ -0,0 +1,235 @@
#!/bin/bash
# Copyright (C) 2018 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.
#
set -e
# Run ART APEX tests.
SCRIPT_DIR=$(dirname $0)
# Status of whole test script.
exit_status=0
# Status of current test suite.
test_status=0
function say {
echo "$0: $*"
}
function die {
echo "$0: $*"
exit 1
}
if [[ -z "$ANDROID_BUILD_TOP" ]]; then
export ANDROID_BUILD_TOP=$(pwd)
if [[ ! -x "$ANDROID_BUILD_TOP/build/soong/soong_ui.bash" ]]; then
die "Run from the root of the Android tree, or run lunch/banchan/tapas first."
fi
fi
query_build_vars=(
HOST_OUT
PRODUCT_COMPRESSED_APEX
PRODUCT_OUT
TARGET_FLATTEN_APEX
)
vars="$($ANDROID_BUILD_TOP/build/soong/soong_ui.bash \
--dumpvars-mode --vars="${query_build_vars[*]}")"
# Assign to a variable and eval that, since bash ignores any error status from
# the command substitution if it's directly on the eval line.
eval $vars
# Switch the build system to unbundled mode in the reduced manifest branch.
if [ ! -d $ANDROID_BUILD_TOP/frameworks/base ]; then
export TARGET_BUILD_UNBUNDLED=true
fi
have_deapexer_p=false
if [[ "$TARGET_FLATTEN_APEX" != true ]]; then
if [ ! -e "$HOST_OUT/bin/deapexer" -o ! -e "$HOST_OUT/bin/debugfs_static" ] ; then
say "Could not find deapexer and/or debugfs_static, building now."
build/soong/soong_ui.bash --make-mode deapexer debugfs_static-host || \
die "Cannot build deapexer and debugfs_static"
fi
have_deapexer_p=true
fi
# Fail early.
set -e
build_apex_p=true
list_image_files_p=false
print_image_tree_p=false
print_file_sizes_p=false
function usage {
cat <<EOF
Usage: $0 [OPTION] [apexes...]
Build (optional) and run tests on ART APEX package (on host). Defaults to all
applicable APEXes if none is given on the command line.
-B, --skip-build skip the build step
-l, --list-files list the contents of the ext4 image (\`find\`-like style)
-t, --print-tree list the contents of the ext4 image (\`tree\`-like style)
-s, --print-sizes print the size in bytes of each file when listing contents
--bitness=32|64|multilib|auto passed on to art_apex_test.py for non-host APEXes
-h, --help display this help and exit
EOF
exit
}
device_bitness_arg=""
apex_modules=()
while [[ $# -gt 0 ]]; do
case "$1" in
(-B|--skip-build) build_apex_p=false;;
(-l|--list-files) list_image_files_p=true;;
(-t|--print-tree) print_image_tree_p=true;;
(-s|--print-sizes) print_file_sizes_p=true;;
(--bitness=*) device_bitness_arg=$1;;
(-h|--help) usage;;
(-*) die "Unknown option: '$1'
Try '$0 --help' for more information.";;
(*) apex_modules+=($1);;
esac
shift
done
# build_apex APEX_MODULES
# -----------------------
# Build APEX packages APEX_MODULES.
function build_apex {
if $build_apex_p; then
say "Building $@" && build/soong/soong_ui.bash --make-mode "$@" || die "Cannot build $@"
fi
}
# maybe_list_apex_contents_apex APEX TMPDIR [other]
function maybe_list_apex_contents_apex {
local print_options=()
if $print_file_sizes_p; then
print_options+=(--size)
fi
# List the contents of the apex in list form.
if $list_image_files_p; then
say "Listing image files"
$SCRIPT_DIR/art_apex_test.py --list ${print_options[@]} $@
fi
# List the contents of the apex in tree form.
if $print_image_tree_p; then
say "Printing image tree"
$SCRIPT_DIR/art_apex_test.py --tree ${print_options[@]} $@
fi
}
function fail_check {
echo "$0: FAILED: $*"
test_status=1
exit_status=1
}
if [ ${#apex_modules[@]} -eq 0 ]; then
# Test as many modules as possible.
apex_modules=(
"com.android.art"
"com.android.art.debug"
"com.android.art.testing"
)
if [[ "$HOST_PREFER_32_BIT" = true ]]; then
say "Skipping com.android.art.host, as \`HOST_PREFER_32_BIT\` equals \`true\`"
else
apex_modules+=("com.android.art.host")
fi
fi
# Build the APEX packages (optional).
build_apex ${apex_modules[@]}
# Clean-up.
function cleanup {
rm -rf "$work_dir"
}
# Garbage collection.
function finish {
# Don't fail early during cleanup.
set +e
cleanup
}
for apex_module in ${apex_modules[@]}; do
test_status=0
say "Checking APEX package $apex_module"
work_dir=$(mktemp -d)
trap finish EXIT
art_apex_test_args="--tmpdir $work_dir"
test_only_args=""
if [[ $apex_module = *.host ]]; then
apex_path="$HOST_OUT/apex/${apex_module}.zipapex"
art_apex_test_args="$art_apex_test_args --host"
test_only_args="--flavor debug"
# The host APEX is always built multilib.
art_apex_test_args="$art_apex_test_args --bitness=multilib"
else
art_apex_test_args="$art_apex_test_args $device_bitness_arg"
if [[ "$TARGET_FLATTEN_APEX" = true ]]; then
apex_path="$PRODUCT_OUT/system/apex/${apex_module}"
art_apex_test_args="$art_apex_test_args --flattened"
else
# Note: The Testing ART APEX is never built as a Compressed APEX.
if [[ "$PRODUCT_COMPRESSED_APEX" = true && $apex_module != *.testing ]]; then
apex_path="$PRODUCT_OUT/system/apex/${apex_module}.capex"
else
apex_path="$PRODUCT_OUT/system/apex/${apex_module}.apex"
fi
fi
if $have_deapexer_p; then
art_apex_test_args="$art_apex_test_args --deapexer $HOST_OUT/bin/deapexer"
art_apex_test_args="$art_apex_test_args --debugfs $HOST_OUT/bin/debugfs_static"
fi
case $apex_module in
(*.debug) test_only_args="--flavor debug";;
(*.testing) test_only_args="--flavor testing";;
(*) test_only_args="--flavor release";;
esac
fi
say "APEX package path: $apex_path"
# List the contents of the APEX image (optional).
maybe_list_apex_contents_apex $art_apex_test_args $apex_path
# Run tests on APEX package.
$SCRIPT_DIR/art_apex_test.py $art_apex_test_args $test_only_args $apex_path \
|| fail_check "Checks failed on $apex_module"
# Clean up.
trap - EXIT
cleanup
[[ "$test_status" = 0 ]] && say "$apex_module tests passed"
echo
done
[[ "$exit_status" = 0 ]] && say "All ART APEX tests passed"
exit $exit_status

View File

@ -0,0 +1,10 @@
{
"name": "com.android.art",
"version": 2147483647,
"provideNativeLibs": [
"libjdwp.so"
],
"requireNativeLibs": [
"libicu_jni.so"
]
}

479
build/art.go Normal file
View File

@ -0,0 +1,479 @@
// Copyright (C) 2016 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 art
import (
"fmt"
"log"
"path/filepath"
"strings"
"sync"
"github.com/google/blueprint/proptools"
"android/soong/android"
"android/soong/apex"
"android/soong/cc"
"android/soong/cc/config"
)
var supportedArches = []string{"arm", "arm64", "x86", "x86_64"}
func globalFlags(ctx android.LoadHookContext) ([]string, []string) {
var cflags []string
var asflags []string
opt := ctx.Config().GetenvWithDefault("ART_NDEBUG_OPT_FLAG", "-O3")
cflags = append(cflags, opt)
tlab := false
gcType := ctx.Config().GetenvWithDefault("ART_DEFAULT_GC_TYPE", "CMS")
if ctx.Config().IsEnvTrue("ART_TEST_DEBUG_GC") {
gcType = "SS"
tlab = true
}
cflags = append(cflags, "-DART_DEFAULT_GC_TYPE_IS_"+gcType)
if tlab {
cflags = append(cflags, "-DART_USE_TLAB=1")
}
if ctx.Config().IsEnvTrue("ART_HEAP_POISONING") {
cflags = append(cflags, "-DART_HEAP_POISONING=1")
asflags = append(asflags, "-DART_HEAP_POISONING=1")
}
if ctx.Config().IsEnvTrue("ART_USE_CXX_INTERPRETER") {
cflags = append(cflags, "-DART_USE_CXX_INTERPRETER=1")
}
if !ctx.Config().IsEnvFalse("ART_USE_READ_BARRIER") && ctx.Config().ArtUseReadBarrier() {
// Used to change the read barrier type. Valid values are BAKER, TABLELOOKUP.
// The default is BAKER.
barrierType := ctx.Config().GetenvWithDefault("ART_READ_BARRIER_TYPE", "BAKER")
cflags = append(cflags,
"-DART_USE_READ_BARRIER=1",
"-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1")
asflags = append(asflags,
"-DART_USE_READ_BARRIER=1",
"-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1")
}
if !ctx.Config().IsEnvFalse("ART_USE_GENERATIONAL_CC") {
cflags = append(cflags, "-DART_USE_GENERATIONAL_CC=1")
}
cdexLevel := ctx.Config().GetenvWithDefault("ART_DEFAULT_COMPACT_DEX_LEVEL", "fast")
cflags = append(cflags, "-DART_DEFAULT_COMPACT_DEX_LEVEL="+cdexLevel)
// We need larger stack overflow guards for ASAN, as the compiled code will have
// larger frame sizes. For simplicity, just use global not-target-specific cflags.
// Note: We increase this for both debug and non-debug, as the overflow gap will
// be compiled into managed code. We always preopt (and build core images) with
// the debug version. So make the gap consistent (and adjust for the worst).
if len(ctx.Config().SanitizeDevice()) > 0 || len(ctx.Config().SanitizeHost()) > 0 {
cflags = append(cflags,
"-DART_STACK_OVERFLOW_GAP_arm=8192",
"-DART_STACK_OVERFLOW_GAP_arm64=16384",
"-DART_STACK_OVERFLOW_GAP_x86=16384",
"-DART_STACK_OVERFLOW_GAP_x86_64=20480")
} else {
cflags = append(cflags,
"-DART_STACK_OVERFLOW_GAP_arm=8192",
"-DART_STACK_OVERFLOW_GAP_arm64=8192",
"-DART_STACK_OVERFLOW_GAP_x86=8192",
"-DART_STACK_OVERFLOW_GAP_x86_64=8192")
}
if ctx.Config().IsEnvTrue("ART_ENABLE_ADDRESS_SANITIZER") {
// Used to enable full sanitization, i.e., user poisoning, under ASAN.
cflags = append(cflags, "-DART_ENABLE_ADDRESS_SANITIZER=1")
asflags = append(asflags, "-DART_ENABLE_ADDRESS_SANITIZER=1")
}
if !ctx.Config().IsEnvFalse("USE_D8_DESUGAR") {
cflags = append(cflags, "-DUSE_D8_DESUGAR=1")
}
return cflags, asflags
}
func debugFlags(ctx android.LoadHookContext) []string {
var cflags []string
opt := ctx.Config().GetenvWithDefault("ART_DEBUG_OPT_FLAG", "-O2")
cflags = append(cflags, opt)
return cflags
}
func deviceFlags(ctx android.LoadHookContext) []string {
var cflags []string
deviceFrameSizeLimit := 1736
if len(ctx.Config().SanitizeDevice()) > 0 {
deviceFrameSizeLimit = 7400
}
cflags = append(cflags,
fmt.Sprintf("-Wframe-larger-than=%d", deviceFrameSizeLimit),
fmt.Sprintf("-DART_FRAME_SIZE_LIMIT=%d", deviceFrameSizeLimit),
)
cflags = append(cflags, "-DART_BASE_ADDRESS="+ctx.Config().LibartImgDeviceBaseAddress())
minDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA", "-0x1000000")
maxDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA", "0x1000000")
cflags = append(cflags, "-DART_BASE_ADDRESS_MIN_DELTA="+minDelta)
cflags = append(cflags, "-DART_BASE_ADDRESS_MAX_DELTA="+maxDelta)
return cflags
}
func hostFlags(ctx android.LoadHookContext) []string {
var cflags []string
hostFrameSizeLimit := 1736
if len(ctx.Config().SanitizeHost()) > 0 {
// art/test/137-cfi/cfi.cc
// error: stack frame size of 1944 bytes in function 'Java_Main_unwindInProcess'
hostFrameSizeLimit = 6400
}
cflags = append(cflags,
fmt.Sprintf("-Wframe-larger-than=%d", hostFrameSizeLimit),
fmt.Sprintf("-DART_FRAME_SIZE_LIMIT=%d", hostFrameSizeLimit),
)
cflags = append(cflags, "-DART_BASE_ADDRESS="+ctx.Config().LibartImgHostBaseAddress())
minDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA", "-0x1000000")
maxDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA", "0x1000000")
cflags = append(cflags, "-DART_BASE_ADDRESS_MIN_DELTA="+minDelta)
cflags = append(cflags, "-DART_BASE_ADDRESS_MAX_DELTA="+maxDelta)
if len(ctx.Config().SanitizeHost()) > 0 && !ctx.Config().IsEnvFalse("ART_ENABLE_ADDRESS_SANITIZER") {
// We enable full sanitization on the host by default.
cflags = append(cflags, "-DART_ENABLE_ADDRESS_SANITIZER=1")
}
clang_path := filepath.Join(config.ClangDefaultBase, ctx.Config().PrebuiltOS(), config.ClangDefaultVersion)
cflags = append(cflags, "-DART_CLANG_PATH=\""+clang_path+"\"")
return cflags
}
func globalDefaults(ctx android.LoadHookContext) {
type props struct {
Target struct {
Android struct {
Cflags []string
}
Host struct {
Cflags []string
}
}
Cflags []string
Asflags []string
Sanitize struct {
Recover []string
}
}
p := &props{}
p.Cflags, p.Asflags = globalFlags(ctx)
p.Target.Android.Cflags = deviceFlags(ctx)
p.Target.Host.Cflags = hostFlags(ctx)
if ctx.Config().IsEnvTrue("ART_DEX_FILE_ACCESS_TRACKING") {
p.Cflags = append(p.Cflags, "-DART_DEX_FILE_ACCESS_TRACKING")
p.Sanitize.Recover = []string{
"address",
}
}
ctx.AppendProperties(p)
}
// Hook that adds flags that are implicit for all cc_art_* modules.
func addImplicitFlags(ctx android.LoadHookContext) {
type props struct {
Target struct {
Android struct {
Cflags []string
}
}
}
p := &props{}
if ctx.Config().IsEnvTrue("ART_TARGET_LINUX") {
p.Target.Android.Cflags = []string{"-DART_TARGET", "-DART_TARGET_LINUX"}
} else {
p.Target.Android.Cflags = []string{"-DART_TARGET", "-DART_TARGET_ANDROID"}
}
ctx.AppendProperties(p)
}
func debugDefaults(ctx android.LoadHookContext) {
type props struct {
Cflags []string
}
p := &props{}
p.Cflags = debugFlags(ctx)
ctx.AppendProperties(p)
}
func customLinker(ctx android.LoadHookContext) {
linker := ctx.Config().Getenv("CUSTOM_TARGET_LINKER")
type props struct {
DynamicLinker string
}
p := &props{}
if linker != "" {
p.DynamicLinker = linker
}
ctx.AppendProperties(p)
}
func prefer32Bit(ctx android.LoadHookContext) {
type props struct {
Target struct {
Host struct {
Compile_multilib *string
}
}
}
p := &props{}
if ctx.Config().IsEnvTrue("HOST_PREFER_32_BIT") {
p.Target.Host.Compile_multilib = proptools.StringPtr("prefer32")
}
// Prepend to make it overridable in the blueprints. Note that it doesn't work
// to override the property in a cc_defaults module.
ctx.PrependProperties(p)
}
var testMapKey = android.NewOnceKey("artTests")
func testMap(config android.Config) map[string][]string {
return config.Once(testMapKey, func() interface{} {
return make(map[string][]string)
}).(map[string][]string)
}
func testInstall(ctx android.InstallHookContext) {
testMap := testMap(ctx.Config())
var name string
if ctx.Host() {
name = "host_"
} else {
name = "device_"
}
name += ctx.Arch().ArchType.String() + "_" + ctx.ModuleName()
artTestMutex.Lock()
defer artTestMutex.Unlock()
tests := testMap[name]
tests = append(tests, ctx.Path().String())
testMap[name] = tests
}
var testcasesContentKey = android.NewOnceKey("artTestcasesContent")
func testcasesContent(config android.Config) map[string]string {
return config.Once(testcasesContentKey, func() interface{} {
return make(map[string]string)
}).(map[string]string)
}
// Binaries and libraries also need to be copied in the testcases directory for
// running tests on host. This method adds module to the list of needed files.
// The 'key' is the file in testcases and 'value' is the path to copy it from.
// The actual copy will be done in make since soong does not do installations.
func addTestcasesFile(ctx android.InstallHookContext) {
if ctx.Os() != ctx.Config().BuildOS || ctx.Module().IsSkipInstall() {
return
}
testcasesContent := testcasesContent(ctx.Config())
artTestMutex.Lock()
defer artTestMutex.Unlock()
src := ctx.SrcPath().String()
path := strings.Split(ctx.Path().String(), "/")
// Keep last two parts of the install path (e.g. bin/dex2oat).
dst := strings.Join(path[len(path)-2:], "/")
if oldSrc, ok := testcasesContent[dst]; ok {
ctx.ModuleErrorf("Conflicting sources for %s: %s and %s", dst, oldSrc, src)
}
testcasesContent[dst] = src
}
var artTestMutex sync.Mutex
func init() {
artModuleTypes := []string{
"art_cc_library",
"art_cc_library_static",
"art_cc_binary",
"art_cc_test",
"art_cc_test_library",
"art_cc_defaults",
"libart_cc_defaults",
"libart_static_cc_defaults",
"art_global_defaults",
"art_debug_defaults",
"art_apex_test_host",
}
android.AddNeverAllowRules(
android.NeverAllow().
NotIn("art", "external/vixl").
ModuleType(artModuleTypes...))
android.RegisterModuleType("art_cc_library", artLibrary)
android.RegisterModuleType("art_cc_library_static", artStaticLibrary)
android.RegisterModuleType("art_cc_binary", artBinary)
android.RegisterModuleType("art_cc_test", artTest)
android.RegisterModuleType("art_cc_test_library", artTestLibrary)
android.RegisterModuleType("art_cc_defaults", artDefaultsFactory)
android.RegisterModuleType("libart_cc_defaults", libartDefaultsFactory)
android.RegisterModuleType("libart_static_cc_defaults", libartStaticDefaultsFactory)
android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory)
// TODO: This makes the module disable itself for host if HOST_PREFER_32_BIT is
// set. We need this because the multilib types of binaries listed in the apex
// rule must match the declared type. This is normally not difficult but HOST_PREFER_32_BIT
// changes this to 'prefer32' on all host binaries. Since HOST_PREFER_32_BIT is
// only used for testing we can just disable the module.
// See b/120617876 for more information.
android.RegisterModuleType("art_apex_test_host", artHostTestApexBundleFactory)
}
func artHostTestApexBundleFactory() android.Module {
module := apex.ApexBundleFactory(true)
android.AddLoadHook(module, func(ctx android.LoadHookContext) {
if ctx.Config().IsEnvTrue("HOST_PREFER_32_BIT") {
type props struct {
Target struct {
Host struct {
Enabled *bool
}
}
}
p := &props{}
p.Target.Host.Enabled = proptools.BoolPtr(false)
ctx.AppendProperties(p)
log.Print("Disabling host build of " + ctx.ModuleName() + " for HOST_PREFER_32_BIT=true")
}
})
return module
}
func artGlobalDefaultsFactory() android.Module {
module := artDefaultsFactory()
android.AddLoadHook(module, addImplicitFlags)
android.AddLoadHook(module, globalDefaults)
return module
}
func artDebugDefaultsFactory() android.Module {
module := artDefaultsFactory()
android.AddLoadHook(module, debugDefaults)
return module
}
func artDefaultsFactory() android.Module {
c := &codegenProperties{}
module := cc.DefaultsFactory(c)
android.AddLoadHook(module, func(ctx android.LoadHookContext) { codegen(ctx, c, staticAndSharedLibrary) })
return module
}
func libartDefaultsFactory() android.Module {
c := &codegenProperties{}
module := cc.DefaultsFactory(c)
android.AddLoadHook(module, func(ctx android.LoadHookContext) { codegen(ctx, c, staticAndSharedLibrary) })
return module
}
func libartStaticDefaultsFactory() android.Module {
c := &codegenProperties{}
module := cc.DefaultsFactory(c)
android.AddLoadHook(module, func(ctx android.LoadHookContext) { codegen(ctx, c, staticLibrary) })
return module
}
func artLibrary() android.Module {
module := cc.LibraryFactory()
installCodegenCustomizer(module, staticAndSharedLibrary)
android.AddLoadHook(module, addImplicitFlags)
android.AddInstallHook(module, addTestcasesFile)
return module
}
func artStaticLibrary() android.Module {
module := cc.LibraryStaticFactory()
installCodegenCustomizer(module, staticLibrary)
android.AddLoadHook(module, addImplicitFlags)
return module
}
func artBinary() android.Module {
module := cc.BinaryFactory()
android.AddLoadHook(module, addImplicitFlags)
android.AddLoadHook(module, customLinker)
android.AddLoadHook(module, prefer32Bit)
android.AddInstallHook(module, addTestcasesFile)
return module
}
func artTest() android.Module {
module := cc.TestFactory()
installCodegenCustomizer(module, binary)
android.AddLoadHook(module, addImplicitFlags)
android.AddLoadHook(module, customLinker)
android.AddLoadHook(module, prefer32Bit)
android.AddInstallHook(module, testInstall)
return module
}
func artTestLibrary() android.Module {
module := cc.TestLibraryFactory()
installCodegenCustomizer(module, staticAndSharedLibrary)
android.AddLoadHook(module, addImplicitFlags)
android.AddLoadHook(module, prefer32Bit)
android.AddInstallHook(module, testInstall)
return module
}

143
build/boot/Android.bp Normal file
View File

@ -0,0 +1,143 @@
// Copyright (C) 2021 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "art_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
// Encapsulate the contributions made by the com.android.art to the bootclasspath.
bootclasspath_fragment {
name: "art-bootclasspath-fragment",
image_name: "art",
// Must match the ART_APEX_JARS set in build/make/core/envsetup.mk
contents: [
"core-oj",
"core-libart",
"okhttp",
"bouncycastle",
"apache-xml",
],
api: {
stub_libs: [
// Stubs for the core-oj and core-libart. The other modules do not
// have any public API.
"art.module.public.api",
],
},
core_platform_api: {
stub_libs: [
// Core platform (aka. module_lib) stubs for all the non-coverage contents.
"art.module.public.api.stubs.module_lib",
],
},
// Additional properties to append when coverage is enabled, i.e. when
// EMMA_INSTRUMENT_FRAMEWORK=true
coverage: {
contents: [
"jacocoagent",
],
api: {
stub_libs: [
// Stubs for the jacocoagent.
"jacoco-stubs",
],
},
hidden_api: {
// Additional packages provided by jacoagent
package_prefixes: [
"com.vladium.emma.rt",
"org.jacoco",
"org.objectweb.asm",
],
},
},
visibility: [
"//art/build/apex",
"//art/build/sdk",
],
apex_available: [
"com.android.art",
"com.android.art.debug",
],
hidden_api: {
blocked: ["hiddenapi/hiddenapi-blocked.txt"],
max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
unsupported_packages: ["hiddenapi/hiddenapi-unsupported-packages.txt"],
// This module does not contain any split packages.
split_packages: [],
// The following packages currently only contain classes from this
// bootclasspath_fragment but some of their sub-packages contain classes
// from other bootckasspath modules. Packages should only be listed here
// when necessary for legacy purposes, new packages should match a
// package prefix.
single_packages: [
"android.system",
],
// The following packages and all their subpackages currently only
// contain classes from this bootclasspath_fragment. Listing a package
// here won't prevent other bootclasspath modules from adding classes in
// any of those packages but it will prevent them from adding those
// classes into an API surface, e.g. public, system, etc.. Doing so will
// result in a build failure due to inconsistent flags.
package_prefixes: [
"android.compat",
"com.android.okhttp",
"com.android.org.bouncycastle",
"com.android.org.kxml2",
"com.sun",
"dalvik",
"java",
"javax.annotation",
"javax.crypto",
"javax.net",
"javax.security",
"javax.sql",
"javax.xml",
"jdk",
"libcore",
"org.apache.harmony",
"org.apache.xalan",
"org.apache.xml",
"org.apache.xpath",
"org.json",
"org.w3c",
"org.xml",
"org.xmlpull",
"sun",
],
},
}
// Encapsulate the contributions made by the com.android.art to the systemserverclasspath.
systemserverclasspath_fragment {
name: "art-systemserverclasspath-fragment",
contents: ["service-art"],
apex_available: [
"com.android.art",
"com.android.art.debug",
],
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
# soong-team@ as the hiddenapi files are tightly coupled with Soong
file:platform/build/soong:/OWNERS
# compat-team@ for changes to hiddenapi files
file:tools/platform-compat:/OWNERS

View File

@ -0,0 +1,4 @@
Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V
Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V
Ldalvik/system/VMRuntime;->setTargetSdkVersionNative(I)V
Ljava/lang/invoke/MethodHandles$Lookup;->IMPL_LOOKUP:Ljava/lang/invoke/MethodHandles$Lookup;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
org.apache.xalan
org.apache.xalan.extensions
org.apache.xalan.processor
org.apache.xalan.res
org.apache.xalan.serialize
org.apache.xalan.templates
org.apache.xalan.transformer
org.apache.xalan.xslt
org.apache.xml.dtm
org.apache.xml.dtm.ref
org.apache.xml.dtm.ref.dom2dtm
org.apache.xml.dtm.ref.sax2dtm
org.apache.xml.res
org.apache.xml.serializer
org.apache.xml.serializer.dom3
org.apache.xml.serializer.utils
org.apache.xml.utils
org.apache.xml.utils.res
org.apache.xpath
org.apache.xpath.axes
org.apache.xpath.compiler
org.apache.xpath.domapi
org.apache.xpath.functions
org.apache.xpath.jaxp
org.apache.xpath.objects
org.apache.xpath.operations
org.apache.xpath.patterns
org.apache.xpath.res

3093
build/boot/preloaded-classes Normal file

File diff suppressed because it is too large Load Diff

130
build/build-art-module.sh Executable file
View File

@ -0,0 +1,130 @@
#!/bin/bash -e
# This script builds the APEX modules, SDKs and module exports that the ART
# Module provides.
if [ ! -e build/make/core/Makefile ]; then
echo "$0 must be run from the top of the tree"
exit 1
fi
skip_apex=
skip_module_sdk=
build_args=()
for arg; do
case "$arg" in
--skip-apex) skip_apex=true ;;
--skip-module-sdk) skip_module_sdk=true ;;
*) build_args+=("$arg") ;;
esac
shift
done
if [ -z "$skip_apex" ]; then
# Take the list of modules from MAINLINE_MODULES.
if [ -n "${MAINLINE_MODULES}" ]; then
read -r -a MAINLINE_MODULES <<< "${MAINLINE_MODULES}"
else
MAINLINE_MODULES=(
com.android.art
com.android.art.debug
)
fi
else
MAINLINE_MODULES=()
fi
# Take the list of products to build the modules for from
# MAINLINE_MODULE_PRODUCTS.
if [ -n "${MAINLINE_MODULE_PRODUCTS}" ]; then
read -r -a MAINLINE_MODULE_PRODUCTS <<< "${MAINLINE_MODULE_PRODUCTS}"
else
# The default products are the same as in
# build/soong/scripts/build-mainline-modules.sh.
MAINLINE_MODULE_PRODUCTS=(
art_module_arm
art_module_arm64
art_module_x86
art_module_x86_64
)
fi
MODULE_SDKS_AND_EXPORTS=()
if [ -z "$skip_module_sdk" ]; then
MODULE_SDKS_AND_EXPORTS=(
art-module-sdk
art-module-host-exports
art-module-test-exports
)
fi
echo_and_run() {
echo "$*"
"$@"
}
export OUT_DIR=${OUT_DIR:-out}
export DIST_DIR=${DIST_DIR:-${OUT_DIR}/dist}
# Use same build settings as build_unbundled_mainline_module.sh, for build
# consistency.
# TODO(mast): Call out to a common script for building APEXes.
export UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true
export TARGET_BUILD_VARIANT=${TARGET_BUILD_VARIANT:-"user"}
export TARGET_BUILD_DENSITY=alldpi
export TARGET_BUILD_TYPE=release
if [ ! -d frameworks/base ]; then
# Configure the build system for the reduced manifest branch.
export SOONG_ALLOW_MISSING_DEPENDENCIES=true
fi
if [ ${#MAINLINE_MODULES[*]} -gt 0 ]; then
(
export TARGET_BUILD_APPS="${MAINLINE_MODULES[*]}"
# We require .apex files here, so ensure we get them regardless of product
# settings.
export OVERRIDE_TARGET_FLATTEN_APEX=false
for product in ${MAINLINE_MODULE_PRODUCTS[*]}; do
echo_and_run build/soong/soong_ui.bash --make-mode \
TARGET_PRODUCT=${product} "${build_args[@]}" ${MAINLINE_MODULES[*]}
vars="$(TARGET_PRODUCT=${product} build/soong/soong_ui.bash \
--dumpvars-mode --vars="PRODUCT_OUT TARGET_ARCH")"
# Assign to a variable and eval that, since bash ignores any error status
# from the command substitution if it's directly on the eval line.
eval $vars
mkdir -p ${DIST_DIR}/${TARGET_ARCH}
for module in ${MAINLINE_MODULES[*]}; do
echo_and_run cp ${PRODUCT_OUT}/system/apex/${module}.apex \
${DIST_DIR}/${TARGET_ARCH}/
done
done
)
fi
if [ ${#MODULE_SDKS_AND_EXPORTS[*]} -gt 0 ]; then
# Create multi-arch SDKs in a different out directory. The multi-arch script
# uses Soong in --soong-only mode which cannot use the same directory as
# normal mode with make.
export OUT_DIR=${OUT_DIR}/aml
# Put the build system in apps building mode so we don't trip on platform
# dependencies, but there are no actual apps to build here.
export TARGET_BUILD_APPS=none
# We use force building LLVM components flag (even though we actually don't
# compile them) because we don't have bionic host prebuilts
# for them.
export FORCE_BUILD_LLVM_COMPONENTS=true
echo_and_run build/soong/scripts/build-aml-prebuilts.sh \
TARGET_PRODUCT=mainline_sdk "${build_args[@]}" ${MODULE_SDKS_AND_EXPORTS[*]}
rm -rf ${DIST_DIR}/mainline-sdks
mkdir -p ${DIST_DIR}
echo_and_run cp -r ${OUT_DIR}/soong/mainline-sdks ${DIST_DIR}/
fi

235
build/codegen.go Normal file
View File

@ -0,0 +1,235 @@
// Copyright (C) 2016 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 art
// This file implements the "codegen" property to apply different properties based on the currently
// selected codegen arches, which defaults to all arches on the host and the primary and secondary
// arches on the device.
import (
"sort"
"strings"
"android/soong/android"
)
type moduleType struct {
library bool
static bool
shared bool
}
var (
staticLibrary = moduleType{true, true, false}
sharedLibrary = moduleType{true, false, true}
staticAndSharedLibrary = moduleType{true, true, true}
binary = moduleType{false, false, false}
)
func codegen(ctx android.LoadHookContext, c *codegenProperties, t moduleType) {
var hostArches, deviceArches []string
e := ctx.Config().Getenv("ART_HOST_CODEGEN_ARCHS")
if e == "" {
hostArches = supportedArches
} else {
hostArches = strings.Split(e, " ")
}
e = ctx.Config().Getenv("ART_TARGET_CODEGEN_ARCHS")
if e == "" {
deviceArches = defaultDeviceCodegenArches(ctx)
} else {
deviceArches = strings.Split(e, " ")
}
getCodegenArchProperties := func(archName string) *codegenArchProperties {
var arch *codegenArchProperties
switch archName {
case "arm":
arch = &c.Codegen.Arm
case "arm64":
arch = &c.Codegen.Arm64
case "x86":
arch = &c.Codegen.X86
case "x86_64":
arch = &c.Codegen.X86_64
default:
ctx.ModuleErrorf("Unknown codegen architecture %q", archName)
}
return arch
}
appendCodegenSourceArchProperties := func(p *CodegenSourceArchProperties, archName string) {
arch := getCodegenArchProperties(archName)
p.Srcs = append(p.Srcs, arch.CodegenSourceArchProperties.Srcs...)
}
addCodegenSourceArchProperties := func(host bool, p *CodegenSourceArchProperties) {
type sourceProps struct {
Target struct {
Android *CodegenSourceArchProperties
Host *CodegenSourceArchProperties
}
}
sp := &sourceProps{}
if host {
sp.Target.Host = p
} else {
sp.Target.Android = p
}
ctx.AppendProperties(sp)
}
addCodegenArchProperties := func(host bool, archName string) {
type commonProps struct {
Target struct {
Android *CodegenCommonArchProperties
Host *CodegenCommonArchProperties
}
}
type libraryProps struct {
Target struct {
Android *CodegenLibraryArchProperties
Host *CodegenLibraryArchProperties
}
}
type sharedLibraryProps struct {
Target struct {
Android *CodegenLibraryArchSharedProperties
Host *CodegenLibraryArchSharedProperties
}
}
type staticLibraryProps struct {
Target struct {
Android *CodegenLibraryArchStaticProperties
Host *CodegenLibraryArchStaticProperties
}
}
arch := getCodegenArchProperties(archName)
cp := &commonProps{}
lp := &libraryProps{}
sharedLP := &sharedLibraryProps{}
staticLP := &staticLibraryProps{}
if host {
cp.Target.Host = &arch.CodegenCommonArchProperties
lp.Target.Host = &arch.CodegenLibraryArchProperties
sharedLP.Target.Host = &arch.CodegenLibraryArchSharedProperties
staticLP.Target.Host = &arch.CodegenLibraryArchStaticProperties
} else {
cp.Target.Android = &arch.CodegenCommonArchProperties
lp.Target.Android = &arch.CodegenLibraryArchProperties
sharedLP.Target.Android = &arch.CodegenLibraryArchSharedProperties
staticLP.Target.Android = &arch.CodegenLibraryArchStaticProperties
}
ctx.AppendProperties(cp)
if t.library {
ctx.AppendProperties(lp)
if t.static {
ctx.AppendProperties(staticLP)
}
if t.shared {
ctx.AppendProperties(sharedLP)
}
}
}
addCodegenProperties := func(host bool, arches []string) {
sourceProps := &CodegenSourceArchProperties{}
for _, arch := range arches {
appendCodegenSourceArchProperties(sourceProps, arch)
addCodegenArchProperties(host, arch)
}
sourceProps.Srcs = android.FirstUniqueStrings(sourceProps.Srcs)
addCodegenSourceArchProperties(host, sourceProps)
}
addCodegenProperties(false /* host */, deviceArches)
addCodegenProperties(true /* host */, hostArches)
}
// These properties are allowed to contain the same source file name in different architectures.
// They we will be deduplicated automatically.
type CodegenSourceArchProperties struct {
Srcs []string
}
type CodegenCommonArchProperties struct {
Cflags []string
Cppflags []string
}
type CodegenLibraryArchProperties struct {
Static_libs []string
Export_static_lib_headers []string
}
type CodegenLibraryArchStaticProperties struct {
Static struct {
Whole_static_libs []string
}
}
type CodegenLibraryArchSharedProperties struct {
Shared struct {
Shared_libs []string
Export_shared_lib_headers []string
}
}
type codegenArchProperties struct {
CodegenSourceArchProperties
CodegenCommonArchProperties
CodegenLibraryArchProperties
CodegenLibraryArchStaticProperties
CodegenLibraryArchSharedProperties
}
type codegenProperties struct {
Codegen struct {
Arm, Arm64, X86, X86_64 codegenArchProperties
}
}
func defaultDeviceCodegenArches(ctx android.LoadHookContext) []string {
arches := make(map[string]bool)
for _, a := range ctx.DeviceConfig().Arches() {
s := a.ArchType.String()
arches[s] = true
if s == "arm64" {
arches["arm"] = true
} else if s == "x86_64" {
arches["x86"] = true
}
}
ret := make([]string, 0, len(arches))
for a := range arches {
ret = append(ret, a)
}
sort.Strings(ret)
return ret
}
func installCodegenCustomizer(module android.Module, t moduleType) {
c := &codegenProperties{}
android.AddLoadHook(module, func(ctx android.LoadHookContext) { codegen(ctx, c, t) })
module.AddProperties(c)
}

78
build/makevars.go Normal file
View File

@ -0,0 +1,78 @@
// Copyright (C) 2016 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 art
import (
"path/filepath"
"sort"
"strings"
"android/soong/android"
"android/soong/cc/config"
)
var (
pctx = android.NewPackageContext("android/soong/art")
// Copy the following prebuilts to the testcases directory.
// The original prebuilts directory is not accessible when running tests remotely.
prebuiltToolsForTests = []string{
"bin/clang",
"bin/clang.real",
"bin/llvm-addr2line",
"bin/llvm-dwarfdump",
"bin/llvm-objdump",
"lib64/libc++.so.1",
}
)
func init() {
android.RegisterMakeVarsProvider(pctx, makeVarsProvider)
pctx.Import("android/soong/cc/config")
}
func makeVarsProvider(ctx android.MakeVarsContext) {
ctx.Strict("LIBART_IMG_HOST_BASE_ADDRESS", ctx.Config().LibartImgHostBaseAddress())
ctx.Strict("LIBART_IMG_TARGET_BASE_ADDRESS", ctx.Config().LibartImgDeviceBaseAddress())
testMap := testMap(ctx.Config())
var testNames []string
for name := range testMap {
testNames = append(testNames, name)
}
sort.Strings(testNames)
for _, name := range testNames {
ctx.Strict("ART_TEST_LIST_"+name, strings.Join(testMap[name], " "))
}
// Create list of copy commands to install the content of the testcases directory.
testcasesContent := testcasesContent(ctx.Config())
copy_cmds := []string{}
for _, key := range android.SortedStringKeys(testcasesContent) {
copy_cmds = append(copy_cmds, testcasesContent[key]+":"+key)
}
ctx.Strict("ART_TESTCASES_CONTENT", strings.Join(copy_cmds, " "))
// Add prebuilt tools.
clang_path := filepath.Join(config.ClangDefaultBase, ctx.Config().PrebuiltOS(), config.ClangDefaultVersion)
copy_cmds = []string{}
for _, tool := range prebuiltToolsForTests {
src := filepath.Join(clang_path, "/", tool)
copy_cmds = append(copy_cmds, src+":"+src)
}
ctx.Strict("ART_TESTCASES_PREBUILT_CONTENT", strings.Join(copy_cmds, " "))
}

277
build/sdk/Android.bp Normal file
View File

@ -0,0 +1,277 @@
// Copyright (C) 2020 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "art_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
soong_config_module_type_import {
from: "art/build/SoongConfig.bp",
module_types: [
"art_module_sdk",
"art_module_exports",
],
}
// Additional visibility to add to the prebuilt modules that are part of
// the snapshots of the ART sdk/module_exports to ensure that they are
// visible to each other.
prebuilt_visibility = [
// TODO(b/155921753): Restrict this when prebuilts are in their proper
// locations.
"//prebuilts:__subpackages__",
]
// The SDK for the art module apex.
art_module_sdk {
name: "art-module-sdk",
host_supported: true,
// Enable if ART_MODULE_BUILD_FROM_SOURCE is true
enabled: false,
soong_config_variables: {
source_build: {
enabled: true,
},
},
prebuilt_visibility: prebuilt_visibility,
traits: {
native_bridge_support: [
"jni_headers",
],
ramdisk_image_required: [
"jni_headers",
],
recovery_image_required: [
"jni_headers",
],
},
target: {
// Both android and host linux but not windows or darwin.
linux: {
native_header_libs: [
"jni_headers",
"libartpalette-headers",
"libnativehelper_header_only",
"libopenjdkjvmti_headers",
"odrefresh_headers",
],
native_shared_libs: [
"libandroidio",
"libdexfile",
"libnativebridge",
"libnativehelper",
"libnativeloader",
"libsigchain",
],
native_static_libs: [
"libdexfile_support",
"libdexfile_static",
"libnativehelper_lazy",
],
},
android: {
bootclasspath_fragments: [
// Adds the fragment and its contents to the sdk.
"art-bootclasspath-fragment",
],
systemserverclasspath_fragments: [
"art-systemserverclasspath-fragment",
],
compat_configs: [
"libcore-platform-compat-config",
],
java_header_libs: [
// Needed by any module that builds against any non-numeric
// sdk_version other than "none".
//
// This is actually only used for compiling Java 8 and kotlin.
// Java 9 uses system modules which encapsulates this
// internally.
"core-lambda-stubs",
// A special form or "core-lambda-stubs" for use in
// java_system_modules.
"core-lambda-stubs-for-system-modules",
// Needed when javac compiles code containing @Generated
// annotations produced by some code generation tools.
"core-generated-annotation-stubs",
],
java_sdk_libs: [
"art.module.public.api",
],
java_system_modules: [
"art-module-public-api-stubs-system-modules",
"art-module-lib-api-stubs-system-modules",
"art-module-intra-core-api-stubs-system-modules",
],
native_header_libs: [
"libnativeloader-headers",
],
native_shared_libs: [
"libnativebridge_lazy",
"libnativehelper_compat_libc++",
"libnativeloader_lazy",
],
},
linux_bionic: {
enabled: false,
},
darwin: {
enabled: false,
},
},
}
// Exported host tools and libraries.
art_module_exports {
name: "art-module-host-exports",
host_supported: true,
// Enable if ART_MODULE_BUILD_FROM_SOURCE is true
enabled: false,
soong_config_variables: {
source_build: {
enabled: true,
},
},
prebuilt_visibility: prebuilt_visibility,
java_libs: [
// Needed for grpc-grpc-java
"okhttp-norepackage",
],
target: {
host: {
// Set in target.host because the top level compile_multilib
// property is fixed to "both" in the sdk/module_exports
// implementation and cannot be overridden any other way.
compile_multilib: "64",
java_libs: [
"art.module.api.annotations",
],
native_binaries: [
"dex2oat",
"dex2oatd",
"dexdump",
"hiddenapi",
"oatdump",
"profman",
"veridex",
],
native_libs: [
// libdexfile dependencies for host tests.
// TODO(b/178691801): Support transitive internal library
// dependencies in module_export snapshots and clean this up.
"libartpalette",
"libartbase",
],
},
linux_bionic: {
enabled: false,
},
darwin: {
enabled: false,
},
windows: {
enabled: false,
},
},
}
// Exported tests and supporting libraries
art_module_exports {
name: "art-module-test-exports",
// Enable if ART_MODULE_BUILD_FROM_SOURCE is true
enabled: false,
soong_config_variables: {
source_build: {
enabled: true,
},
},
prebuilt_visibility: prebuilt_visibility,
java_libs: [
"core-compat-test-rules",
"core-test-rules",
"core-tests-support",
"okhttp-tests-nojarjar",
// Needed for CtsJvmtiDeviceRunTestAppBase.
"art_cts_jvmti_test_library",
"expected_cts_outputs",
// Needed for robolectric.
"core-libart-for-host",
"okhttp-for-host",
// Needed for CtsLibcore...TestCases
"libcore-expectations-knownfailures-jar",
"libcore-expectations-virtualdeviceknownfailures-jar",
// Needed for CtsLibcoreOkHttpTestCases
"okhttp-nojarjar",
],
java_tests: [
// Needed for CtsJdwpTestCases.
"apache-harmony-jdwp-tests",
"libcore-crypto-tests",
// Needed for CtsLibcoreOjTestCases
"core-ojtests-public",
// Needed for CtsLibcoreJsr166TestCases
"jsr166-tests",
// Needed for CtsLibcoreTestCases
"apache-harmony-tests",
"core-tests",
],
native_shared_libs: [
"libjavacoretests",
],
native_static_libs: [
// TODO(b/187288515): Providing this as a prebuilt introduces an issue
// with sdk_version propagation. Temporarily use the source library
// instead.
//"libctstiagent",
],
}

62
cmdline/Android.bp Normal file
View File

@ -0,0 +1,62 @@
//
// Copyright (C) 2016 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.
//
// TODO: this header library depends on libart. Find a way to express that.
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "art_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
cc_library_headers {
name: "art_cmdlineparser_headers",
defaults: ["art_defaults"],
host_supported: true,
export_include_dirs: ["."],
apex_available: [
"com.android.art.debug",
"com.android.art",
],
}
art_cc_defaults {
name: "art_cmdline_tests_defaults",
tidy_timeout_srcs: ["cmdline_parser_test.cc"],
srcs: ["cmdline_parser_test.cc"],
}
// Version of ART gtest `art_cmdline_tests` bundled with the ART APEX on target.
// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
art_cc_test {
name: "art_cmdline_tests",
defaults: [
"art_gtest_defaults",
"art_cmdline_tests_defaults",
],
}
// Standalone version of ART gtest `art_cmdline_tests`, not bundled with the ART APEX on target.
art_cc_test {
name: "art_standalone_cmdline_tests",
defaults: [
"art_standalone_gtest_defaults",
"art_cmdline_tests_defaults",
],
}

245
cmdline/README.md Normal file
View File

@ -0,0 +1,245 @@
Cmdline
===================
Introduction
-------------
This directory contains the classes that do common command line tool initialization and parsing. The
long term goal is eventually for all `art` command-line tools to be using these helpers.
----------
## Cmdline Parser
-------------
The `CmdlineParser` class provides a fluent interface using a domain-specific language to quickly
generate a type-safe value parser that process a user-provided list of strings (`argv`). Currently,
it can parse a string into a `VariantMap`, although in the future it might be desirable to parse
into any struct of any field.
To use, create a `CmdlineParser::Builder` and then chain the `Define` methods together with
`WithType` and `IntoXX` methods.
### Quick Start
For example, to save the values into a user-defined variant map:
```
struct FruitVariantMap : VariantMap {
static const Key<int> Apple;
static const Key<double> Orange;
static const Key<bool> Help;
};
// Note that some template boilerplate has been avoided for clarity.
// See variant_map_test.cc for how to completely define a custom map.
using FruitParser = CmdlineParser<FruitVariantMap, FruitVariantMap::Key>;
FruitParser MakeParser() {
auto&& builder = FruitParser::Builder();
builder.
.Define("--help")
.IntoKey(FruitVariantMap::Help)
Define("--apple:_")
.WithType<int>()
.IntoKey(FruitVariantMap::Apple)
.Define("--orange:_")
.WithType<double>()
.WithRange(0.0, 1.0)
.IntoKey(FruitVariantMap::Orange);
return builder.Build();
}
int main(char** argv, int argc) {
auto parser = MakeParser();
auto result = parser.parse(argv, argc));
if (result.isError()) {
std::cerr << result.getMessage() << std::endl;
return EXIT_FAILURE;
}
auto map = parser.GetArgumentsMap();
std::cout << "Help? " << map.GetOrDefault(FruitVariantMap::Help) << std::endl;
std::cout << "Apple? " << map.GetOrDefault(FruitVariantMap::Apple) << std::endl;
std::cout << "Orange? " << map.GetOrDefault(FruitVariantMap::Orange) << std::endl;
return EXIT_SUCCESS;
}
```
In the above code sample, we define a parser which is capable of parsing something like `--help
--apple:123 --orange:0.456` . It will error out automatically if invalid flags are given, or if the
appropriate flags are given but of the the wrong type/range. So for example, `--foo` will not parse
(invalid argument), neither will `--apple:fruit` (fruit is not an int) nor `--orange:1234` (1234 is
out of range of [0.0, 1.0])
### Argument Definitions in Detail
#### Define method
The 'Define' method takes one or more aliases for the argument. Common examples might be `{"-h",
"--help"}` where both `--help` and `-h` are aliases for the same argument.
The simplest kind of argument just tests for presence, but we often want to parse out a particular
type of value (such as an int or double as in the above `FruitVariantMap` example). To do that, a
_wildcard_ must be used to denote the location within the token that the type will be parsed out of.
For example with `-orange:_` the parse would know to check all tokens in an `argv` list for the
`-orange:` prefix and then strip it, leaving only the remains to be parsed.
#### WithType method (optional)
After an argument definition is provided, the parser builder needs to know what type the argument
will be in order to provide the type safety and make sure the rest of the argument definition is
correct as early as possible (in essence, everything but the parsing of the argument name is done at
compile time).
Everything that follows a `WithType<T>()` call is thus type checked to only take `T` values.
If this call is omitted, the parser generator assumes you are building a `Unit` type (i.e. an
argument that only cares about presence).
#### WithRange method (optional)
Some values will not make sense outside of a `[min, max]` range, so this is an option to quickly add
a range check without writing custom code. The range check is performed after the main parsing
happens and happens for any type implementing the `<=` operators.
#### WithValueMap (optional)
When parsing an enumeration, it might be very convenient to map a list of possible argument string
values into its runtime value.
With something like
```
.Define("-hello:_")
.WithValueMap({"world", kWorld},
{"galaxy", kGalaxy})
```
It will parse either `-hello:world` or `-hello:galaxy` only (and error out on other variations of
`-hello:whatever`), converting it to the type-safe value of `kWorld` or `kGalaxy` respectively.
This is meant to be another shorthand (like `WithRange`) to avoid writing a custom type parser. In
general it takes a variadic number of `pair<const char* /*arg name*/, T /*value*/>`.
#### WithValues (optional)
When an argument definition has multiple aliases with no wildcards, it might be convenient to
quickly map them into discrete values.
For example:
```
.Define({"-xinterpret", "-xnointerpret"})
.WithValues({true, false}
```
It will parse `-xinterpret` as `true` and `-xnointerpret` as `false`.
In general, it uses the position of the argument alias to map into the WithValues position value.
(Note that this method will not work when the argument definitions have a wildcard because there is
no way to position-ally match that).
#### AppendValues (optional)
By default, the argument is assumed to appear exactly once, and if the user specifies it more than
once, only the latest value is taken into account (and all previous occurrences of the argument are
ignored).
In some situations, we may want to accumulate the argument values instead of discarding the previous
ones.
For example
```
.Define("-D")
.WithType<std::vector<std::string>)()
.AppendValues()
```
Will parse something like `-Dhello -Dworld -Dbar -Dbaz` into `std::vector<std::string>{"hello",
"world", "bar", "baz"}`.
### Setting an argument parse target (required)
To complete an argument definition, the parser generator also needs to know where to save values.
Currently, only `IntoKey` is supported, but that may change in the future.
#### IntoKey (required)
This specifies that when a value is parsed, it will get saved into a variant map using the specific
key.
For example,
```
.Define("-help")
.IntoKey(Map::Help)
```
will save occurrences of the `-help` argument by doing a `Map.Set(Map::Help, ParsedValue("-help"))`
where `ParsedValue` is an imaginary function that parses the `-help` argment into a specific type
set by `WithType`.
### Ignoring unknown arguments
This is highly discouraged, but for compatibility with `JNI` which allows argument ignores, there is
an option to ignore any argument tokens that are not known to the parser. This is done with the
`Ignore` function which takes a list of argument definition names.
It's semantically equivalent to making a series of argument definitions that map to `Unit` but don't
get saved anywhere. Values will still get parsed as normal, so it will *not* ignore known arguments
with invalid values, only user-arguments for which it could not find a matching argument definition.
### Parsing custom types
Any type can be parsed from a string by specializing the `CmdlineType` class and implementing the
static interface provided by `CmdlineTypeParser`. It is recommended to inherit from
`CmdlineTypeParser` since it already provides default implementations for every method.
The `Parse` method should be implemented for most types. Some types will allow appending (such as an
`std::vector<std::string>` and are meant to be used with `AppendValues` in which case the
`ParseAndAppend` function should be implemented.
For example:
```
template <>
struct CmdlineType<double> : CmdlineTypeParser<double> {
Result Parse(const std::string& str) {
char* end = nullptr;
errno = 0;
double value = strtod(str.c_str(), &end);
if (*end != '\0') {
return Result::Failure("Failed to parse double from " + str);
}
if (errno == ERANGE) {
return Result::OutOfRange(
"Failed to parse double from " + str + "; overflow/underflow occurred");
}
return Result::Success(value);
}
static const char* Name() { return "double"; }
// note: Name() is just here for more user-friendly errors,
// but in the future we will use non-standard ways of getting the type name
// at compile-time and this will no longer be required
};
```
Will parse any non-append argument definitions with a type of `double`.
For an appending example:
```
template <>
struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
Result ParseAndAppend(const std::string& args,
std::vector<std::string>& existing_value) {
existing_value.push_back(args);
return Result::SuccessNoValue();
}
static const char* Name() { return "std::vector<std::string>"; }
};
```
Will parse multiple instances of the same argument repeatedly into the `existing_value` (which will
be default-constructed to `T{}` for the first occurrence of the argument).
#### What is a `Result`?
`Result` is a typedef for `CmdlineParseResult<T>` and it acts similar to a poor version of
`Either<Left, Right>` in Haskell. In particular, it would be similar to `Either< int ErrorCode,
Maybe<T> >`.
There are helpers like `Result::Success(value)`, `Result::Failure(string message)` and so on to
quickly construct these without caring about the type.
When successfully parsing a single value, `Result::Success(value)` should be used, and when
successfully parsing an appended value, use `Result::SuccessNoValue()` and write back the new value
into `existing_value` as an out-parameter.
When many arguments are parsed, the result is collapsed down to a `CmdlineResult` which acts as a
`Either<int ErrorCode, Unit>` where the right side simply indicates success. When values are
successfully stored, the parser will automatically save it into the target destination as a side
effect.

422
cmdline/cmdline.h Normal file
View File

@ -0,0 +1,422 @@
/*
* Copyright (C) 2014 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.
*/
#ifndef ART_CMDLINE_CMDLINE_H_
#define ART_CMDLINE_CMDLINE_H_
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <string>
#include <string_view>
#include "android-base/stringprintf.h"
#include "base/file_utils.h"
#include "base/logging.h"
#include "base/mutex.h"
#include "base/string_view_cpp20.h"
#include "noop_compiler_callbacks.h"
#include "runtime.h"
#if !defined(NDEBUG)
#define DBG_LOG LOG(INFO)
#else
#define DBG_LOG LOG(DEBUG)
#endif
namespace art {
// TODO: Move to <runtime/utils.h> and remove all copies of this function.
static bool LocationToFilename(const std::string& location, InstructionSet isa,
std::string* filename) {
bool has_system = false;
bool has_cache = false;
// image_location = /system/framework/boot.art
// system_image_filename = /system/framework/<image_isa>/boot.art
std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
if (OS::FileExists(system_filename.c_str())) {
has_system = true;
}
bool have_android_data = false;
bool dalvik_cache_exists = false;
bool is_global_cache = false;
std::string dalvik_cache;
GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache,
&have_android_data, &dalvik_cache_exists, &is_global_cache);
std::string cache_filename;
if (have_android_data && dalvik_cache_exists) {
// Always set output location even if it does not exist,
// so that the caller knows where to create the image.
//
// image_location = /system/framework/boot.art
// *image_filename = /data/dalvik-cache/<image_isa>/boot.art
std::string error_msg;
if (GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(),
&cache_filename, &error_msg)) {
has_cache = true;
}
}
if (has_system) {
*filename = system_filename;
return true;
} else if (has_cache) {
*filename = cache_filename;
return true;
} else {
*filename = system_filename;
return false;
}
}
static Runtime* StartRuntime(const char* boot_image_location,
InstructionSet instruction_set,
const std::vector<const char*>& runtime_args) {
CHECK(boot_image_location != nullptr);
RuntimeOptions options;
// We are more like a compiler than a run-time. We don't want to execute code.
{
static NoopCompilerCallbacks callbacks;
options.push_back(std::make_pair("compilercallbacks", &callbacks));
}
// Boot image location.
{
std::string boot_image_option;
boot_image_option += "-Ximage:";
boot_image_option += boot_image_location;
options.push_back(std::make_pair(boot_image_option, nullptr));
}
// Instruction set.
options.push_back(
std::make_pair("imageinstructionset",
reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
// Explicit runtime args.
for (const char* runtime_arg : runtime_args) {
options.push_back(std::make_pair(runtime_arg, nullptr));
}
// None of the command line tools need sig chain. If this changes we'll need
// to upgrade this option to a proper parameter.
options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
if (!Runtime::Create(options, false)) {
fprintf(stderr, "Failed to create runtime\n");
return nullptr;
}
// Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
// give it away now and then switch to a more manageable ScopedObjectAccess.
Thread::Current()->TransitionFromRunnableToSuspended(ThreadState::kNative);
return Runtime::Current();
}
struct CmdlineArgs {
enum ParseStatus {
kParseOk, // Parse successful. Do not set the error message.
kParseUnknownArgument, // Unknown argument. Do not set the error message.
kParseError, // Parse ok, but failed elsewhere. Print the set error message.
};
bool Parse(int argc, char** argv) {
// Skip over argv[0].
argv++;
argc--;
if (argc == 0) {
fprintf(stderr, "No arguments specified\n");
PrintUsage();
return false;
}
std::string error_msg;
for (int i = 0; i < argc; i++) {
const char* const raw_option = argv[i];
const std::string_view option(raw_option);
if (StartsWith(option, "--boot-image=")) {
boot_image_location_ = raw_option + strlen("--boot-image=");
} else if (StartsWith(option, "--instruction-set=")) {
const char* const instruction_set_str = raw_option + strlen("--instruction-set=");
instruction_set_ = GetInstructionSetFromString(instruction_set_str);
if (instruction_set_ == InstructionSet::kNone) {
fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str);
PrintUsage();
return false;
}
} else if (option == "--runtime-arg") {
if (i + 1 == argc) {
fprintf(stderr, "Missing argument for --runtime-arg\n");
PrintUsage();
return false;
}
++i;
runtime_args_.push_back(argv[i]);
} else if (StartsWith(option, "--output=")) {
output_name_ = std::string(option.substr(strlen("--output=")));
const char* filename = output_name_.c_str();
out_.reset(new std::ofstream(filename));
if (!out_->good()) {
fprintf(stderr, "Failed to open output filename %s\n", filename);
PrintUsage();
return false;
}
os_ = out_.get();
} else {
ParseStatus parse_status = ParseCustom(raw_option, option.length(), &error_msg);
if (parse_status == kParseUnknownArgument) {
fprintf(stderr, "Unknown argument %s\n", option.data());
}
if (parse_status != kParseOk) {
fprintf(stderr, "%s\n", error_msg.c_str());
PrintUsage();
return false;
}
}
}
DBG_LOG << "will call parse checks";
{
ParseStatus checks_status = ParseChecks(&error_msg);
if (checks_status != kParseOk) {
fprintf(stderr, "%s\n", error_msg.c_str());
PrintUsage();
return false;
}
}
return true;
}
virtual std::string GetUsage() const {
std::string usage;
usage += // Required.
" --boot-image=<file.art>: provide the image location for the boot class path.\n"
" Do not include the arch as part of the name, it is added automatically.\n"
" Example: --boot-image=/system/framework/boot.art\n"
" (specifies /system/framework/<arch>/boot.art as the image file)\n"
"\n";
usage += android::base::StringPrintf( // Optional.
" --instruction-set=(arm|arm64|x86|x86_64): for locating the image\n"
" file based on the image location set.\n"
" Example: --instruction-set=x86\n"
" Default: %s\n"
"\n",
GetInstructionSetString(kRuntimeISA));
usage +=
" --runtime-arg <argument> used to specify various arguments for the runtime\n"
" such as initial heap size, maximum heap size, and verbose output.\n"
" Use a separate --runtime-arg switch for each argument.\n"
" Example: --runtime-arg -Xms256m\n"
"\n";
usage += // Optional.
" --output=<file> may be used to send the output to a file.\n"
" Example: --output=/tmp/oatdump.txt\n"
"\n";
return usage;
}
// Specified by --boot-image.
const char* boot_image_location_ = nullptr;
// Specified by --instruction-set.
InstructionSet instruction_set_ = InstructionSet::kNone;
// Runtime arguments specified by --runtime-arg.
std::vector<const char*> runtime_args_;
// Specified by --output.
std::ostream* os_ = &std::cout;
std::unique_ptr<std::ofstream> out_; // If something besides cout is used
std::string output_name_;
virtual ~CmdlineArgs() {}
bool ParseCheckBootImage(std::string* error_msg) {
if (boot_image_location_ == nullptr) {
*error_msg = "--boot-image must be specified";
return false;
}
if (instruction_set_ == InstructionSet::kNone) {
LOG(WARNING) << "No instruction set given, assuming " << GetInstructionSetString(kRuntimeISA);
instruction_set_ = kRuntimeISA;
}
DBG_LOG << "boot image location: " << boot_image_location_;
// Checks for --boot-image location.
{
std::string boot_image_location = boot_image_location_;
size_t separator_pos = boot_image_location.find(':');
if (separator_pos != std::string::npos) {
boot_image_location = boot_image_location.substr(/*pos*/ 0u, /*size*/ separator_pos);
}
size_t file_name_idx = boot_image_location.rfind('/');
if (file_name_idx == std::string::npos) { // Prevent a InsertIsaDirectory check failure.
*error_msg = "Boot image location must have a / in it";
return false;
}
// Don't let image locations with the 'arch' in it through, since it's not a location.
// This prevents a common error "Could not create an image space..." when initing the Runtime.
if (file_name_idx != std::string::npos) {
std::string no_file_name = boot_image_location.substr(0, file_name_idx);
size_t ancestor_dirs_idx = no_file_name.rfind('/');
std::string parent_dir_name;
if (ancestor_dirs_idx != std::string::npos) {
parent_dir_name = no_file_name.substr(ancestor_dirs_idx + 1);
} else {
parent_dir_name = no_file_name;
}
DBG_LOG << "boot_image_location parent_dir_name was " << parent_dir_name;
if (GetInstructionSetFromString(parent_dir_name.c_str()) != InstructionSet::kNone) {
*error_msg = "Do not specify the architecture as part of the boot image location";
return false;
}
}
// Check that the boot image location points to a valid file name.
std::string file_name;
if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
*error_msg = android::base::StringPrintf(
"No corresponding file for location '%s' (filename '%s') exists",
boot_image_location.c_str(),
file_name.c_str());
return false;
}
DBG_LOG << "boot_image_filename does exist: " << file_name;
}
return true;
}
void PrintUsage() {
fprintf(stderr, "%s", GetUsage().c_str());
}
protected:
virtual ParseStatus ParseCustom(const char* raw_option ATTRIBUTE_UNUSED,
size_t raw_option_length ATTRIBUTE_UNUSED,
std::string* error_msg ATTRIBUTE_UNUSED) {
return kParseUnknownArgument;
}
virtual ParseStatus ParseChecks(std::string* error_msg ATTRIBUTE_UNUSED) {
return kParseOk;
}
};
template <typename Args = CmdlineArgs>
struct CmdlineMain {
int Main(int argc, char** argv) {
Locks::Init();
InitLogging(argv, Runtime::Abort);
std::unique_ptr<Args> args = std::unique_ptr<Args>(CreateArguments());
args_ = args.get();
DBG_LOG << "Try to parse";
if (args_ == nullptr || !args_->Parse(argc, argv)) {
return EXIT_FAILURE;
}
bool needs_runtime = NeedsRuntime();
std::unique_ptr<Runtime> runtime;
if (needs_runtime) {
std::string error_msg;
if (!args_->ParseCheckBootImage(&error_msg)) {
fprintf(stderr, "%s\n", error_msg.c_str());
args_->PrintUsage();
return EXIT_FAILURE;
}
runtime.reset(CreateRuntime(args.get()));
if (runtime == nullptr) {
return EXIT_FAILURE;
}
if (!ExecuteWithRuntime(runtime.get())) {
return EXIT_FAILURE;
}
} else {
if (!ExecuteWithoutRuntime()) {
return EXIT_FAILURE;
}
}
if (!ExecuteCommon()) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
// Override this function to create your own arguments.
// Usually will want to return a subtype of CmdlineArgs.
virtual Args* CreateArguments() {
return new Args();
}
// Override this function to do something else with the runtime.
virtual bool ExecuteWithRuntime(Runtime* runtime) {
CHECK(runtime != nullptr);
// Do nothing
return true;
}
// Does the code execution need a runtime? Sometimes it doesn't.
virtual bool NeedsRuntime() {
return true;
}
// Do execution without having created a runtime.
virtual bool ExecuteWithoutRuntime() {
return true;
}
// Continue execution after ExecuteWith[out]Runtime
virtual bool ExecuteCommon() {
return true;
}
virtual ~CmdlineMain() {}
protected:
Args* args_ = nullptr;
private:
Runtime* CreateRuntime(CmdlineArgs* args) {
CHECK(args != nullptr);
return StartRuntime(args->boot_image_location_, args->instruction_set_, args_->runtime_args_);
}
};
} // namespace art
#endif // ART_CMDLINE_CMDLINE_H_

View File

@ -0,0 +1,138 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_CMDLINE_PARSE_RESULT_H_
#define ART_CMDLINE_CMDLINE_PARSE_RESULT_H_
#include "cmdline_result.h"
#include "detail/cmdline_parser_detail.h"
namespace art {
// Result of a type-parsing attempt. If successful holds the strongly-typed value,
// otherwise it holds either a usage or a failure string message that should be displayed back
// to the user.
//
// CmdlineType::Parse/CmdlineType::ParseAndAppend must return this type.
template <typename T>
struct CmdlineParseResult : CmdlineResult {
using CmdlineResult::CmdlineResult;
// Create an error result with the usage error code and the specified message.
static CmdlineParseResult Usage(const std::string& message) {
return CmdlineParseResult(kUsage, message);
}
// Create an error result with the failure error code and no message.
static CmdlineParseResult<T> Failure() {
return CmdlineParseResult(kFailure);
}
// Create an error result with the failure error code and no message.
static CmdlineParseResult<T> Failure(const std::string& message) {
return CmdlineParseResult(kFailure, message);
}
// Create a successful result which holds the specified value.
static CmdlineParseResult<T> Success(const T& value) {
return CmdlineParseResult(value);
}
// Create a successful result, taking over the value.
static CmdlineParseResult<T> Success(T&& value) {
return CmdlineParseResult(std::forward<T>(value));
}
// Create succesful result, without any values. Used when a value was successfully appended
// into an existing object.
static CmdlineParseResult<T> SuccessNoValue() {
return CmdlineParseResult(T {});
}
// Create an error result with the OutOfRange error and the specified message.
static CmdlineParseResult<T> OutOfRange(const std::string& message) {
return CmdlineParseResult(kOutOfRange, message);
}
// Create an error result with the OutOfRange code and a custom message
// which is printed from the actual/min/max values.
// Values are converted to string using the ostream<< operator.
static CmdlineParseResult<T> OutOfRange(const T& value,
const T& min,
const T& max) {
return CmdlineParseResult(kOutOfRange,
"actual: " + art::detail::ToStringAny(value) +
", min: " + art::detail::ToStringAny(min) +
", max: " + art::detail::ToStringAny(max));
}
// Get a read-only reference to the underlying value.
// The result must have been successful and must have a value.
const T& GetValue() const {
assert(IsSuccess());
assert(has_value_);
return value_;
}
// Get a mutable reference to the underlying value.
// The result must have been successful and must have a value.
T& GetValue() {
assert(IsSuccess());
assert(has_value_);
return value_;
}
// Take over the value.
// The result must have been successful and must have a value.
T&& ReleaseValue() {
assert(IsSuccess());
assert(has_value_);
return std::move(value_);
}
// Whether or not the result has a value (e.g. created with Result::Success).
// Error results never have values, success results commonly, but not always, have values.
bool HasValue() const {
return has_value_;
}
// Cast an error-result from type T2 to T1.
// Safe since error-results don't store a typed value.
template <typename T2>
static CmdlineParseResult<T> CastError(const CmdlineParseResult<T2>& other) {
assert(other.IsError());
return CmdlineParseResult<T>(other.GetStatus());
}
// Make sure copying is allowed
CmdlineParseResult(const CmdlineParseResult&) = default;
// Make sure moving is cheap
CmdlineParseResult(CmdlineParseResult&&) noexcept = default;
private:
explicit CmdlineParseResult(const T& value)
: CmdlineResult(kSuccess), value_(value), has_value_(true) {}
explicit CmdlineParseResult(T&& value)
: CmdlineResult(kSuccess), value_(std::forward<T>(value)), has_value_(true) {}
CmdlineParseResult()
: CmdlineResult(kSuccess), value_(), has_value_(false) {}
T value_;
bool has_value_ = false;
};
} // namespace art
#endif // ART_CMDLINE_CMDLINE_PARSE_RESULT_H_

787
cmdline/cmdline_parser.h Normal file
View File

@ -0,0 +1,787 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_CMDLINE_PARSER_H_
#define ART_CMDLINE_CMDLINE_PARSER_H_
#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "base/indenter.h"
#include "base/variant_map.h"
#include "cmdline_parse_result.h"
#include "cmdline_result.h"
#include "cmdline_type_parser.h"
#include "cmdline_types.h"
#include "detail/cmdline_debug_detail.h"
#include "detail/cmdline_parse_argument_detail.h"
#include "detail/cmdline_parser_detail.h"
#include "token_range.h"
namespace art {
// Build a parser for command line arguments with a small domain specific language.
// Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing.
// Each argument must also have a VariantMap::Key<T> in order to do the T storage.
template <typename TVariantMap,
template <typename TKeyValue> class TVariantMapKey>
struct CmdlineParser {
template <typename TArg>
struct ArgumentBuilder;
struct Builder; // Build the parser.
struct UntypedArgumentBuilder; // Build arguments which weren't yet given a type.
private:
// Forward declare some functions that we need to use before fully-defining structs.
template <typename TArg>
static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent);
static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg);
// Allow argument definitions to save their values when they are parsed,
// without having a dependency on CmdlineParser or any of the builders.
//
// A shared pointer to the save destination is saved into the load/save argument callbacks.
//
// This also allows the underlying storage (i.e. a variant map) to be released
// to the user, without having to recreate all of the callbacks.
struct SaveDestination {
SaveDestination() : variant_map_(new TVariantMap()) {}
// Save value to the variant map.
template <typename TArg>
void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) {
variant_map_->Set(key, value);
}
// Get the existing value from a map, creating the value if it did not already exist.
template <typename TArg>
TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) {
auto* ptr = variant_map_->Get(key);
if (ptr == nullptr) {
variant_map_->Set(key, TArg());
ptr = variant_map_->Get(key);
assert(ptr != nullptr);
}
return *ptr;
}
protected:
// Release the map, clearing it as a side-effect.
// Future saves will be distinct from previous saves.
TVariantMap&& ReleaseMap() {
return std::move(*variant_map_);
}
// Get a read-only reference to the variant map.
const TVariantMap& GetMap() {
return *variant_map_;
}
// Clear all potential save targets.
void Clear() {
variant_map_->Clear();
}
private:
// Don't try to copy or move this. Just don't.
SaveDestination(const SaveDestination&) = delete;
SaveDestination(SaveDestination&&) = delete;
SaveDestination& operator=(const SaveDestination&) = delete;
SaveDestination& operator=(SaveDestination&&) = delete;
std::shared_ptr<TVariantMap> variant_map_;
// Allow the parser to change the underlying pointers when we release the underlying storage.
friend struct CmdlineParser;
};
public:
// Builder for the argument definition of type TArg. Do not use this type directly,
// it is only a separate type to provide compile-time enforcement against doing
// illegal builds.
template <typename TArg>
struct ArgumentBuilder {
// Add a range check to this argument.
ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) {
argument_info_.has_range_ = true;
argument_info_.min_ = min;
argument_info_.max_ = max;
return *this;
}
// Map the list of names into the list of values. List of names must not have
// any wildcards '_' in it.
//
// Do not use if a value map has already been set.
ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) {
SetValuesInternal(value_list);
return *this;
}
// When used with a single alias, map the alias into this value.
// Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
ArgumentBuilder<TArg> WithValue(const TArg& value) {
return WithValues({ value });
}
// Map the parsed string values (from _) onto a concrete value. If no wildcard
// has been specified, then map the value directly from the arg name (i.e.
// if there are multiple aliases, then use the alias to do the mapping).
//
// Do not use if a values list has already been set.
ArgumentBuilder<TArg>& WithValueMap(
std::initializer_list<std::pair<const char*, TArg>> key_value_list) {
assert(!argument_info_.has_value_list_);
argument_info_.has_value_map_ = true;
argument_info_.value_map_ = key_value_list;
return *this;
}
// If this argument is seen multiple times, successive arguments mutate the same value
// instead of replacing it with a new value.
ArgumentBuilder<TArg>& AppendValues() {
argument_info_.appending_values_ = true;
return *this;
}
ArgumentBuilder<TArg>& WithMetavar(const char* sv) {
argument_info_.metavar_ = sv;
return *this;
}
ArgumentBuilder<TArg>& WithHelp(const char* sv) {
argument_info_.help_ = sv;
return *this;
}
// Convenience type alias for the variant map key type definition.
using MapKey = TVariantMapKey<TArg>;
// Write the results of this argument into the key.
// To look up the parsed arguments, get the map and then use this key with VariantMap::Get
CmdlineParser::Builder& IntoKey(const MapKey& key) {
// Only capture save destination as a pointer.
// This allows the parser to later on change the specific save targets.
auto save_destination = save_destination_;
save_value_ = [save_destination, &key](TArg& value) {
save_destination->SaveToMap(key, value);
CMDLINE_DEBUG_LOG << "Saved value into map '"
<< detail::ToStringAny(value) << "'" << std::endl;
};
load_value_ = [save_destination, &key]() -> TArg& {
TArg& value = save_destination->GetOrCreateFromMap(key);
CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'"
<< std::endl;
return value;
};
save_value_specified_ = true;
load_value_specified_ = true;
CompleteArgument();
return parent_;
}
// Write the results of this argument into a variable pointed to by destination.
// An optional is used to tell whether the command line argument was present.
CmdlineParser::Builder& IntoLocation(std::optional<TArg>* destination) {
save_value_ = [destination](TArg& value) {
*destination = value;
};
load_value_ = [destination]() -> TArg& {
return destination->value();
};
save_value_specified_ = true;
load_value_specified_ = true;
CompleteArgument();
return parent_;
}
// Ensure we always move this when returning a new builder.
ArgumentBuilder(ArgumentBuilder&&) noexcept = default;
protected:
// Used by builder to internally ignore arguments by dropping them on the floor after parsing.
CmdlineParser::Builder& IntoIgnore() {
save_value_ = [](TArg& value) {
CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
};
load_value_ = []() -> TArg& {
assert(false && "Should not be appending values to ignored arguments");
__builtin_trap(); // Blow up.
};
save_value_specified_ = true;
load_value_specified_ = true;
CompleteArgument();
return parent_;
}
void SetValuesInternal(const std::vector<TArg>&& value_list) {
assert(!argument_info_.has_value_map_);
argument_info_.has_value_list_ = true;
argument_info_.value_list_ = value_list;
}
void SetNames(std::vector<const char*>&& names) {
argument_info_.names_ = names;
}
void SetNames(std::initializer_list<const char*> names) {
argument_info_.names_ = names;
}
void SetHelp(std::optional<const char*>&& val) {
argument_info_.help_ = val;
}
void SetCategory(std::optional<const char*>&& val) {
argument_info_.category_ = val;
}
void SetMetavar(std::optional<const char*>&& val) {
argument_info_.metavar_ = val;
}
private:
// Copying is bad. Move only.
ArgumentBuilder(const ArgumentBuilder&) = delete;
// Called by any function that doesn't chain back into this builder.
// Completes the argument builder and save the information into the main builder.
void CompleteArgument() {
assert(save_value_specified_ &&
"No Into... function called, nowhere to save parsed values to");
assert(load_value_specified_ &&
"No Into... function called, nowhere to load parsed values from");
argument_info_.CompleteArgument();
// Appending the completed argument is destructive. The object is no longer
// usable since all the useful information got moved out of it.
AppendCompletedArgument(parent_,
new detail::CmdlineParseArgument<TArg>(
std::move(argument_info_),
std::move(save_value_),
std::move(load_value_)));
}
friend struct CmdlineParser;
friend struct CmdlineParser::Builder;
friend struct CmdlineParser::UntypedArgumentBuilder;
ArgumentBuilder(CmdlineParser::Builder& parser,
std::shared_ptr<SaveDestination> save_destination)
: parent_(parser),
save_value_specified_(false),
load_value_specified_(false),
save_destination_(save_destination) {
save_value_ = [](TArg&) {
assert(false && "No save value function defined");
};
load_value_ = []() -> TArg& {
assert(false && "No load value function defined");
__builtin_trap(); // Blow up.
};
}
CmdlineParser::Builder& parent_;
std::function<void(TArg&)> save_value_;
std::function<TArg&(void)> load_value_;
bool save_value_specified_;
bool load_value_specified_;
detail::CmdlineParserArgumentInfo<TArg> argument_info_;
std::shared_ptr<SaveDestination> save_destination_;
};
struct UntypedArgumentBuilder {
// Set a type for this argument. The specific subcommand parser is looked up by the type.
template <typename TArg>
ArgumentBuilder<TArg> WithType() {
return CreateTypedBuilder<TArg>();
}
UntypedArgumentBuilder& WithHelp(const char* sv) {
SetHelp(sv);
return *this;
}
UntypedArgumentBuilder& WithCategory(const char* sv) {
SetCategory(sv);
return *this;
}
UntypedArgumentBuilder& WithMetavar(const char* sv) {
SetMetavar(sv);
return *this;
}
// When used with multiple aliases, map the position of the alias to the value position.
template <typename TArg>
ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
auto&& a = CreateTypedBuilder<TArg>();
a.WithValues(values);
return std::move(a);
}
// When used with a single alias, map the alias into this value.
// Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
template <typename TArg>
ArgumentBuilder<TArg> WithValue(const TArg& value) {
return WithValues({ value });
}
// Set the current building argument to target this key.
// When this command line argument is parsed, it can be fetched with this key.
Builder& IntoKey(const TVariantMapKey<Unit>& key) {
return CreateTypedBuilder<Unit>().IntoKey(key);
}
// Ensure we always move this when returning a new builder.
UntypedArgumentBuilder(UntypedArgumentBuilder&&) noexcept = default;
protected:
void SetNames(std::vector<const char*>&& names) {
names_ = std::move(names);
}
void SetNames(std::initializer_list<const char*> names) {
names_ = names;
}
void SetHelp(std::optional<const char*> sv) {
help_.swap(sv);
}
void SetMetavar(std::optional<const char*> sv) {
metavar_.swap(sv);
}
void SetCategory(std::optional<const char*> sv) {
category_.swap(sv);
}
private:
// No copying. Move instead.
UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
template <typename TArg>
ArgumentBuilder<TArg> CreateTypedBuilder() {
auto&& b = CreateArgumentBuilder<TArg>(parent_);
InitializeTypedBuilder(&b); // Type-specific initialization
b.SetNames(std::move(names_));
b.SetHelp(std::move(help_));
b.SetCategory(std::move(category_));
b.SetMetavar(std::move(metavar_));
return std::move(b);
}
template <typename TArg = Unit>
typename std::enable_if<std::is_same<TArg, Unit>::value>::type
InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
// Every Unit argument implicitly maps to a runtime value of Unit{}
std::vector<Unit> values(names_.size(), Unit{});
arg_builder->SetValuesInternal(std::move(values));
}
// No extra work for all other types
void InitializeTypedBuilder(void*) {}
template <typename TArg>
friend struct ArgumentBuilder;
friend struct Builder;
explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
// UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
CmdlineParser::Builder& parent_;
std::vector<const char*> names_;
std::optional<const char*> category_;
std::optional<const char*> help_;
std::optional<const char*> metavar_;
};
// Build a new parser given a chain of calls to define arguments.
struct Builder {
Builder() : save_destination_(new SaveDestination()) {}
// Define a single argument. The default type is Unit.
UntypedArgumentBuilder Define(const char* name) {
return Define({name});
}
Builder& ClearCategory() {
default_category_.reset();
return *this;
}
Builder& SetCategory(const char* sv) {
default_category_ = sv;
return *this;
}
Builder& OrderCategories(std::vector<const char*> categories) {
category_order_.swap(categories);
return *this;
}
// Define a single argument with multiple aliases.
UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
auto&& b = UntypedArgumentBuilder(*this);
b.SetNames(names);
b.SetCategory(default_category_);
return std::move(b);
}
// Whether the parser should give up on unrecognized arguments. Not recommended.
Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
ignore_unrecognized_ = ignore_unrecognized;
return *this;
}
// Provide a list of arguments to ignore for backwards compatibility.
Builder& Ignore(std::initializer_list<const char*> ignore_list) {
auto current_cat = default_category_;
default_category_ = "Ignored";
for (auto&& ignore_name : ignore_list) {
std::string ign = ignore_name;
// Ignored arguments are just like a regular definition which have very
// liberal parsing requirements (no range checks, no value checks).
// Unlike regular argument definitions, when a value gets parsed into its
// stronger type, we just throw it away.
if (ign.find('_') != std::string::npos) { // Does the arg-def have a wildcard?
// pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
assert(&builder == this);
(void)builder; // Ignore pointless unused warning, it's used in the assert.
} else {
// pretend this is a unit, e.g. -Xjitblocking
auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
assert(&builder == this);
(void)builder; // Ignore pointless unused warning, it's used in the assert.
}
}
ignore_list_ = ignore_list;
default_category_ = current_cat;
return *this;
}
// Finish building the parser; performs a check of the validity. Return value is moved, not
// copied. Do not call this more than once.
CmdlineParser Build() {
assert(!built_);
built_ = true;
auto&& p = CmdlineParser(ignore_unrecognized_,
std::move(ignore_list_),
save_destination_,
std::move(completed_arguments_),
std::move(category_order_));
return std::move(p);
}
protected:
void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
completed_arguments_.push_back(std::move(smart_ptr));
}
private:
// No copying now!
Builder(const Builder& other) = delete;
template <typename TArg>
friend struct ArgumentBuilder;
friend struct UntypedArgumentBuilder;
friend struct CmdlineParser;
bool built_ = false;
bool ignore_unrecognized_ = false;
std::vector<const char*> ignore_list_;
std::shared_ptr<SaveDestination> save_destination_;
std::optional<const char*> default_category_;
std::vector<const char*> category_order_;
std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
};
void DumpHelp(VariableIndentationOutputStream& vios);
CmdlineResult Parse(const std::string& argv) {
std::vector<std::string> tokenized;
Split(argv, ' ', &tokenized);
return Parse(TokenRange(std::move(tokenized)));
}
// Parse the arguments; storing results into the arguments map. Returns success value.
CmdlineResult Parse(const char* argv) {
return Parse(std::string(argv));
}
// Parse the arguments; storing the results into the arguments map. Returns success value.
// Assumes that argv[0] is a valid argument (i.e. not the program name).
CmdlineResult Parse(const std::vector<const char*>& argv) {
return Parse(TokenRange(argv.begin(), argv.end()));
}
// Parse the arguments; storing the results into the arguments map. Returns success value.
// Assumes that argv[0] is a valid argument (i.e. not the program name).
CmdlineResult Parse(const std::vector<std::string>& argv) {
return Parse(TokenRange(argv.begin(), argv.end()));
}
// Parse the arguments (directly from an int main(argv,argc)). Returns success value.
// Assumes that argv[0] is the program name, and ignores it.
CmdlineResult Parse(const char* argv[], int argc) {
return Parse(TokenRange(&argv[1], argc - 1)); // ignore argv[0] because it's the program name
}
// Look up the arguments that have been parsed; use the target keys to lookup individual args.
const TVariantMap& GetArgumentsMap() const {
return save_destination_->GetMap();
}
// Release the arguments map that has been parsed; useful for move semantics.
TVariantMap&& ReleaseArgumentsMap() {
return save_destination_->ReleaseMap();
}
// How many arguments were defined?
size_t CountDefinedArguments() const {
return completed_arguments_.size();
}
// Ensure we have a default move constructor.
CmdlineParser(CmdlineParser&&) noexcept = default;
// Ensure we have a default move assignment operator.
CmdlineParser& operator=(CmdlineParser&&) noexcept = default;
private:
friend struct Builder;
// Construct a new parser from the builder. Move all the arguments.
CmdlineParser(bool ignore_unrecognized,
std::vector<const char*>&& ignore_list,
std::shared_ptr<SaveDestination> save_destination,
std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments,
std::vector<const char*>&& category_order)
: ignore_unrecognized_(ignore_unrecognized),
ignore_list_(std::move(ignore_list)),
save_destination_(save_destination),
completed_arguments_(std::move(completed_arguments)),
category_order_(category_order) {
assert(save_destination != nullptr);
}
// Parse the arguments; storing results into the arguments map. Returns success value.
// The parsing will fail on the first non-success parse result and return that error.
//
// All previously-parsed arguments are cleared out.
// Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
// A partial parse will result only in a partial save of the arguments.
CmdlineResult Parse(TokenRange&& arguments_list) {
save_destination_->Clear();
for (size_t i = 0; i < arguments_list.Size(); ) {
TokenRange possible_name = arguments_list.Slice(i);
size_t best_match_size = 0; // How many tokens were matched in the best case.
size_t best_match_arg_idx = 0;
bool matched = false; // At least one argument definition has been matched?
// Find the closest argument definition for the remaining token range.
size_t arg_idx = 0;
for (auto&& arg : completed_arguments_) {
size_t local_match = arg->MaybeMatches(possible_name);
if (local_match > best_match_size) {
best_match_size = local_match;
best_match_arg_idx = arg_idx;
matched = true;
}
arg_idx++;
}
// Saw some kind of unknown argument
if (matched == false) {
if (UNLIKELY(ignore_unrecognized_)) { // This is usually off, we only need it for JNI.
// Consume 1 token and keep going, hopefully the next token is a good one.
++i;
continue;
}
// Common case:
// Bail out on the first unknown argument with an error.
return CmdlineResult(CmdlineResult::kUnknown,
std::string("Unknown argument: ") + possible_name[0]);
}
// Look at the best-matched argument definition and try to parse against that.
auto&& arg = completed_arguments_[best_match_arg_idx];
assert(arg->MaybeMatches(possible_name) == best_match_size);
// Try to parse the argument now, if we have enough tokens.
std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
size_t min_tokens;
size_t max_tokens;
std::tie(min_tokens, max_tokens) = num_tokens;
if ((i + min_tokens) > arguments_list.Size()) {
// expected longer command line but it was too short
// e.g. if the argv was only "-Xms" without specifying a memory option
CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
" num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
return CmdlineResult(CmdlineResult::kFailure,
std::string("Argument ") +
possible_name[0] + ": incomplete command line arguments, expected "
+ std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
" more tokens");
}
if (best_match_size > max_tokens || best_match_size < min_tokens) {
// Even our best match was out of range, so parsing would fail instantly.
return CmdlineResult(CmdlineResult::kFailure,
std::string("Argument ") + possible_name[0] + ": too few tokens "
"matched " + std::to_string(best_match_size)
+ " but wanted " + std::to_string(num_tokens.first));
}
// We have enough tokens to begin exact parsing.
TokenRange exact_range = possible_name.Slice(0, max_tokens);
size_t consumed_tokens = 1; // At least 1 if we ever want to try to resume parsing on error
CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
if (parse_attempt.IsError()) {
// We may also want to continue parsing the other tokens to gather more errors.
return parse_attempt;
} // else the value has been successfully stored into the map
assert(consumed_tokens > 0); // Don't hang in an infinite loop trying to parse
i += consumed_tokens;
// TODO: also handle ignoring arguments for backwards compatibility
} // for
return CmdlineResult(CmdlineResult::kSuccess);
}
bool ignore_unrecognized_ = false;
std::vector<const char*> ignore_list_;
std::shared_ptr<SaveDestination> save_destination_;
std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
std::vector<const char*> category_order_;
};
// This has to be defined after everything else, since we want the builders to call this.
template <typename TVariantMap,
template <typename TKeyValue> class TVariantMapKey>
template <typename TArg>
typename CmdlineParser<TVariantMap, TVariantMapKey>::template ArgumentBuilder<TArg>
CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
parent, parent.save_destination_);
}
// This has to be defined after everything else, since we want the builders to call this.
template <typename TVariantMap,
template <typename TKeyValue> class TVariantMapKey>
void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
detail::CmdlineParseArgumentAny* arg) {
builder.AppendCompletedArgument(arg);
}
template <typename TVariantMap,
template <typename TKeyValue> class TVariantMapKey>
void CmdlineParser<TVariantMap, TVariantMapKey>::DumpHelp(VariableIndentationOutputStream& vios) {
std::vector<detail::CmdlineParseArgumentAny*> uncat;
std::unordered_map<std::string, std::vector<detail::CmdlineParseArgumentAny*>> args;
for (const std::unique_ptr<detail::CmdlineParseArgumentAny>& it : completed_arguments_) {
auto cat = it->GetCategory();
if (cat.has_value()) {
if (args.find(*cat) == args.end()) {
args[*cat] = {};
}
args.at(*cat).push_back(it.get());
} else {
uncat.push_back(it.get());
}
}
args.erase("Ignored");
for (auto arg : uncat) {
arg->DumpHelp(vios);
vios.Stream();
}
for (auto it : category_order_) {
auto cur = args.find(it);
if (cur != args.end() && !cur->second.empty()) {
vios.Stream() << "The following " << it << " arguments are supported:" << std::endl;
ScopedIndentation si(&vios);
for (detail::CmdlineParseArgumentAny* arg : cur->second) {
arg->DumpHelp(vios);
vios.Stream();
}
args.erase(cur->first);
}
}
for (auto [cat, lst] : args) {
vios.Stream() << "The following " << cat << " arguments are supported:" << std::endl;
ScopedIndentation si(&vios);
for (auto& arg : completed_arguments_) {
arg->DumpHelp(vios);
vios.Stream();
}
}
if (!ignore_list_.empty()) {
vios.Stream() << "The following arguments are ignored for compatibility:" << std::endl;
ScopedIndentation si(&vios);
for (auto ign : ignore_list_) {
vios.Stream() << ign << std::endl;
}
}
}
} // namespace art
#endif // ART_CMDLINE_CMDLINE_PARSER_H_

View File

@ -0,0 +1,595 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cmdline_parser.h"
#include <numeric>
#include "gtest/gtest.h"
#include "base/utils.h"
#include "jdwp_provider.h"
#include "experimental_flags.h"
#include "parsed_options.h"
#include "runtime.h"
#include "runtime_options.h"
#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
reinterpret_cast<void*>(nullptr));
namespace art {
bool UsuallyEquals(double expected, double actual);
// This has a gtest dependency, which is why it's in the gtest only.
bool operator==(const ProfileSaverOptions& lhs, const ProfileSaverOptions& rhs) {
return lhs.enabled_ == rhs.enabled_ &&
lhs.min_save_period_ms_ == rhs.min_save_period_ms_ &&
lhs.save_resolved_classes_delay_ms_ == rhs.save_resolved_classes_delay_ms_ &&
lhs.hot_startup_method_samples_ == rhs.hot_startup_method_samples_ &&
lhs.min_methods_to_save_ == rhs.min_methods_to_save_ &&
lhs.min_classes_to_save_ == rhs.min_classes_to_save_ &&
lhs.min_notification_before_wake_ == rhs.min_notification_before_wake_ &&
lhs.max_notification_before_wake_ == rhs.max_notification_before_wake_;
}
bool UsuallyEquals(double expected, double actual) {
using FloatingPoint = ::testing::internal::FloatingPoint<double>;
FloatingPoint exp(expected);
FloatingPoint act(actual);
// Compare with ULPs instead of comparing with ==
return exp.AlmostEquals(act);
}
template <typename T>
bool UsuallyEquals(const T& expected, const T& actual,
typename std::enable_if<
detail::SupportsEqualityOperator<T>::value>::type* = nullptr) {
return expected == actual;
}
template <char Separator>
bool UsuallyEquals(const std::vector<std::string>& expected,
const ParseStringList<Separator>& actual) {
return expected == static_cast<std::vector<std::string>>(actual);
}
// Try to use memcmp to compare simple plain-old-data structs.
//
// This should *not* generate false positives, but it can generate false negatives.
// This will mostly work except for fields like float which can have different bit patterns
// that are nevertheless equal.
// If a test is failing because the structs aren't "equal" when they really are
// then it's recommended to implement operator== for it instead.
template <typename T, typename ... Ignore>
bool UsuallyEquals(const T& expected, const T& actual,
const Ignore& ... more ATTRIBUTE_UNUSED,
typename std::enable_if<std::is_pod<T>::value>::type* = nullptr,
typename std::enable_if<!detail::SupportsEqualityOperator<T>::value>::type* = nullptr
) {
return memcmp(std::addressof(expected), std::addressof(actual), sizeof(T)) == 0;
}
bool UsuallyEquals(const XGcOption& expected, const XGcOption& actual) {
return memcmp(std::addressof(expected), std::addressof(actual), sizeof(expected)) == 0;
}
bool UsuallyEquals(const char* expected, const std::string& actual) {
return std::string(expected) == actual;
}
template <typename TMap, typename TKey, typename T>
::testing::AssertionResult IsExpectedKeyValue(const T& expected,
const TMap& map,
const TKey& key) {
auto* actual = map.Get(key);
if (actual != nullptr) {
if (!UsuallyEquals(expected, *actual)) {
return ::testing::AssertionFailure()
<< "expected " << detail::ToStringAny(expected) << " but got "
<< detail::ToStringAny(*actual);
}
return ::testing::AssertionSuccess();
}
return ::testing::AssertionFailure() << "key was not in the map";
}
template <typename TMap, typename TKey, typename T>
::testing::AssertionResult IsExpectedDefaultKeyValue(const T& expected,
const TMap& map,
const TKey& key) {
const T& actual = map.GetOrDefault(key);
if (!UsuallyEquals(expected, actual)) {
return ::testing::AssertionFailure()
<< "expected " << detail::ToStringAny(expected) << " but got "
<< detail::ToStringAny(actual);
}
return ::testing::AssertionSuccess();
}
class CmdlineParserTest : public ::testing::Test {
public:
CmdlineParserTest() = default;
~CmdlineParserTest() = default;
protected:
using M = RuntimeArgumentMap;
using RuntimeParser = ParsedOptions::RuntimeParser;
static void SetUpTestCase() {
art::Locks::Init();
art::InitLogging(nullptr, art::Runtime::Abort); // argv = null
}
void SetUp() override {
parser_ = ParsedOptions::MakeParser(false); // do not ignore unrecognized options
}
static ::testing::AssertionResult IsResultSuccessful(const CmdlineResult& result) {
if (result.IsSuccess()) {
return ::testing::AssertionSuccess();
} else {
return ::testing::AssertionFailure()
<< result.GetStatus() << " with: " << result.GetMessage();
}
}
static ::testing::AssertionResult IsResultFailure(const CmdlineResult& result,
CmdlineResult::Status failure_status) {
if (result.IsSuccess()) {
return ::testing::AssertionFailure() << " got success but expected failure: "
<< failure_status;
} else if (result.GetStatus() == failure_status) {
return ::testing::AssertionSuccess();
}
return ::testing::AssertionFailure() << " expected failure " << failure_status
<< " but got " << result.GetStatus();
}
std::unique_ptr<RuntimeParser> parser_;
};
#define EXPECT_KEY_EXISTS(map, key) EXPECT_TRUE((map).Exists(key))
#define EXPECT_KEY_VALUE(map, key, expected) EXPECT_TRUE(IsExpectedKeyValue(expected, map, key))
#define EXPECT_DEFAULT_KEY_VALUE(map, key, expected) EXPECT_TRUE(IsExpectedDefaultKeyValue(expected, map, key))
#define _EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv) \
do { \
EXPECT_TRUE(IsResultSuccessful(parser_->Parse(argv))); \
EXPECT_EQ(0u, parser_->GetArgumentsMap().Size()); \
#define EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv) \
_EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv); \
} while (false)
#define EXPECT_SINGLE_PARSE_DEFAULT_VALUE(expected, argv, key)\
_EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv); \
RuntimeArgumentMap args = parser_->ReleaseArgumentsMap(); \
EXPECT_DEFAULT_KEY_VALUE(args, key, expected); \
} while (false) // NOLINT [readability/namespace] [5]
#define _EXPECT_SINGLE_PARSE_EXISTS(argv, key) \
do { \
EXPECT_TRUE(IsResultSuccessful(parser_->Parse(argv))); \
RuntimeArgumentMap args = parser_->ReleaseArgumentsMap(); \
EXPECT_EQ(1u, args.Size()); \
EXPECT_KEY_EXISTS(args, key); \
#define EXPECT_SINGLE_PARSE_EXISTS(argv, key) \
_EXPECT_SINGLE_PARSE_EXISTS(argv, key); \
} while (false)
#define EXPECT_SINGLE_PARSE_VALUE(expected, argv, key) \
_EXPECT_SINGLE_PARSE_EXISTS(argv, key); \
EXPECT_KEY_VALUE(args, key, expected); \
} while (false)
#define EXPECT_SINGLE_PARSE_VALUE_STR(expected, argv, key) \
EXPECT_SINGLE_PARSE_VALUE(std::string(expected), argv, key)
#define EXPECT_SINGLE_PARSE_FAIL(argv, failure_status) \
do { \
EXPECT_TRUE(IsResultFailure(parser_->Parse(argv), failure_status));\
RuntimeArgumentMap args = parser_->ReleaseArgumentsMap();\
EXPECT_EQ(0u, args.Size()); \
} while (false)
TEST_F(CmdlineParserTest, TestSimpleSuccesses) {
auto& parser = *parser_;
EXPECT_LT(0u, parser.CountDefinedArguments());
{
// Test case 1: No command line arguments
EXPECT_TRUE(IsResultSuccessful(parser.Parse("")));
RuntimeArgumentMap args = parser.ReleaseArgumentsMap();
EXPECT_EQ(0u, args.Size());
}
EXPECT_SINGLE_PARSE_EXISTS("-Xzygote", M::Zygote);
EXPECT_SINGLE_PARSE_VALUE(std::vector<std::string>({"/hello/world"}),
"-Xbootclasspath:/hello/world",
M::BootClassPath);
EXPECT_SINGLE_PARSE_VALUE(std::vector<std::string>({"/hello", "/world"}),
"-Xbootclasspath:/hello:/world",
M::BootClassPath);
EXPECT_SINGLE_PARSE_VALUE_STR("/hello/world", "-classpath /hello/world", M::ClassPath);
EXPECT_SINGLE_PARSE_VALUE(Memory<1>(234), "-Xss234", M::StackSize);
EXPECT_SINGLE_PARSE_VALUE(MemoryKiB(1234*MB), "-Xms1234m", M::MemoryInitialSize);
EXPECT_SINGLE_PARSE_VALUE(true, "-XX:EnableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
EXPECT_SINGLE_PARSE_VALUE(false, "-XX:DisableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
EXPECT_SINGLE_PARSE_VALUE(0.5, "-XX:HeapTargetUtilization=0.5", M::HeapTargetUtilization);
EXPECT_SINGLE_PARSE_VALUE(5u, "-XX:ParallelGCThreads=5", M::ParallelGCThreads);
} // TEST_F
TEST_F(CmdlineParserTest, TestSimpleFailures) {
// Test argument is unknown to the parser
EXPECT_SINGLE_PARSE_FAIL("abcdefg^%@#*(@#", CmdlineResult::kUnknown);
// Test value map substitution fails
EXPECT_SINGLE_PARSE_FAIL("-Xverify:whatever", CmdlineResult::kFailure);
// Test value type parsing failures
EXPECT_SINGLE_PARSE_FAIL("-Xsswhatever", CmdlineResult::kFailure); // invalid memory value
EXPECT_SINGLE_PARSE_FAIL("-Xms123", CmdlineResult::kFailure); // memory value too small
EXPECT_SINGLE_PARSE_FAIL("-XX:HeapTargetUtilization=0.0", CmdlineResult::kOutOfRange); // toosmal
EXPECT_SINGLE_PARSE_FAIL("-XX:HeapTargetUtilization=2.0", CmdlineResult::kOutOfRange); // toolarg
EXPECT_SINGLE_PARSE_FAIL("-XX:ParallelGCThreads=-5", CmdlineResult::kOutOfRange); // too small
EXPECT_SINGLE_PARSE_FAIL("-Xgc:blablabla", CmdlineResult::kUsage); // not a valid suboption
} // TEST_F
TEST_F(CmdlineParserTest, TestLogVerbosity) {
{
const char* log_args = "-verbose:"
"class,compiler,gc,heap,interpreter,jdwp,jni,monitor,profiler,signals,simulator,startup,"
"third-party-jni,threads,verifier,verifier-debug";
LogVerbosity log_verbosity = LogVerbosity();
log_verbosity.class_linker = true;
log_verbosity.compiler = true;
log_verbosity.gc = true;
log_verbosity.heap = true;
log_verbosity.interpreter = true;
log_verbosity.jdwp = true;
log_verbosity.jni = true;
log_verbosity.monitor = true;
log_verbosity.profiler = true;
log_verbosity.signals = true;
log_verbosity.simulator = true;
log_verbosity.startup = true;
log_verbosity.third_party_jni = true;
log_verbosity.threads = true;
log_verbosity.verifier = true;
log_verbosity.verifier_debug = true;
EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
}
{
const char* log_args = "-verbose:"
"class,compiler,gc,heap,jdwp,jni,monitor";
LogVerbosity log_verbosity = LogVerbosity();
log_verbosity.class_linker = true;
log_verbosity.compiler = true;
log_verbosity.gc = true;
log_verbosity.heap = true;
log_verbosity.jdwp = true;
log_verbosity.jni = true;
log_verbosity.monitor = true;
EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
}
EXPECT_SINGLE_PARSE_FAIL("-verbose:blablabla", CmdlineResult::kUsage); // invalid verbose opt
{
const char* log_args = "-verbose:deopt";
LogVerbosity log_verbosity = LogVerbosity();
log_verbosity.deopt = true;
EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
}
{
const char* log_args = "-verbose:collector";
LogVerbosity log_verbosity = LogVerbosity();
log_verbosity.collector = true;
EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
}
{
const char* log_args = "-verbose:oat";
LogVerbosity log_verbosity = LogVerbosity();
log_verbosity.oat = true;
EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
}
{
const char* log_args = "-verbose:dex";
LogVerbosity log_verbosity = LogVerbosity();
log_verbosity.dex = true;
EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
}
} // TEST_F
// TODO: Enable this b/19274810
TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) {
/*
* Test success
*/
{
XGcOption option_all_true{};
option_all_true.collector_type_ = gc::CollectorType::kCollectorTypeCMS;
option_all_true.verify_pre_gc_heap_ = true;
option_all_true.verify_pre_sweeping_heap_ = true;
option_all_true.verify_post_gc_heap_ = true;
option_all_true.verify_pre_gc_rosalloc_ = true;
option_all_true.verify_pre_sweeping_rosalloc_ = true;
option_all_true.verify_post_gc_rosalloc_ = true;
const char * xgc_args_all_true = "-Xgc:concurrent,"
"preverify,presweepingverify,postverify,"
"preverify_rosalloc,presweepingverify_rosalloc,"
"postverify_rosalloc,precise,"
"verifycardtable";
EXPECT_SINGLE_PARSE_VALUE(option_all_true, xgc_args_all_true, M::GcOption);
XGcOption option_all_false{};
option_all_false.collector_type_ = gc::CollectorType::kCollectorTypeMS;
option_all_false.verify_pre_gc_heap_ = false;
option_all_false.verify_pre_sweeping_heap_ = false;
option_all_false.verify_post_gc_heap_ = false;
option_all_false.verify_pre_gc_rosalloc_ = false;
option_all_false.verify_pre_sweeping_rosalloc_ = false;
option_all_false.verify_post_gc_rosalloc_ = false;
const char* xgc_args_all_false = "-Xgc:nonconcurrent,"
"nopreverify,nopresweepingverify,nopostverify,nopreverify_rosalloc,"
"nopresweepingverify_rosalloc,nopostverify_rosalloc,noprecise,noverifycardtable";
EXPECT_SINGLE_PARSE_VALUE(option_all_false, xgc_args_all_false, M::GcOption);
XGcOption option_all_default{};
const char* xgc_args_blank = "-Xgc:";
EXPECT_SINGLE_PARSE_VALUE(option_all_default, xgc_args_blank, M::GcOption);
}
/*
* Test failures
*/
EXPECT_SINGLE_PARSE_FAIL("-Xgc:blablabla", CmdlineResult::kUsage); // invalid Xgc opt
} // TEST_F
/*
* { "-XjdwpProvider:_" }
*/
TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) {
{
EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kUnset, "", M::JdwpProvider);
}
} // TEST_F
TEST_F(CmdlineParserTest, TestJdwpProviderDefault) {
const char* opt_args = "-XjdwpProvider:default";
EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kDefaultJdwpProvider, opt_args, M::JdwpProvider);
} // TEST_F
TEST_F(CmdlineParserTest, TestJdwpProviderNone) {
const char* opt_args = "-XjdwpProvider:none";
EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kNone, opt_args, M::JdwpProvider);
} // TEST_F
TEST_F(CmdlineParserTest, TestJdwpProviderAdbconnection) {
const char* opt_args = "-XjdwpProvider:adbconnection";
EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kAdbConnection, opt_args, M::JdwpProvider);
} // TEST_F
TEST_F(CmdlineParserTest, TestJdwpProviderHelp) {
EXPECT_SINGLE_PARSE_FAIL("-XjdwpProvider:help", CmdlineResult::kUsage);
} // TEST_F
TEST_F(CmdlineParserTest, TestJdwpProviderFail) {
EXPECT_SINGLE_PARSE_FAIL("-XjdwpProvider:blablabla", CmdlineResult::kFailure);
} // TEST_F
/*
* -D_ -D_ -D_ ...
*/
TEST_F(CmdlineParserTest, TestPropertiesList) {
/*
* Test successes
*/
{
std::vector<std::string> opt = {"hello"};
EXPECT_SINGLE_PARSE_VALUE(opt, "-Dhello", M::PropertiesList);
}
{
std::vector<std::string> opt = {"hello", "world"};
EXPECT_SINGLE_PARSE_VALUE(opt, "-Dhello -Dworld", M::PropertiesList);
}
{
std::vector<std::string> opt = {"one", "two", "three"};
EXPECT_SINGLE_PARSE_VALUE(opt, "-Done -Dtwo -Dthree", M::PropertiesList);
}
} // TEST_F
/*
* -Xcompiler-option foo -Xcompiler-option bar ...
*/
TEST_F(CmdlineParserTest, TestCompilerOption) {
/*
* Test successes
*/
{
std::vector<std::string> opt = {"hello"};
EXPECT_SINGLE_PARSE_VALUE(opt, "-Xcompiler-option hello", M::CompilerOptions);
}
{
std::vector<std::string> opt = {"hello", "world"};
EXPECT_SINGLE_PARSE_VALUE(opt,
"-Xcompiler-option hello -Xcompiler-option world",
M::CompilerOptions);
}
{
std::vector<std::string> opt = {"one", "two", "three"};
EXPECT_SINGLE_PARSE_VALUE(opt,
"-Xcompiler-option one -Xcompiler-option two -Xcompiler-option three",
M::CompilerOptions);
}
} // TEST_F
/*
* -Xjit, -Xnojit, -Xjitcodecachesize, Xjitcompilethreshold
*/
TEST_F(CmdlineParserTest, TestJitOptions) {
/*
* Test successes
*/
{
EXPECT_SINGLE_PARSE_VALUE(true, "-Xusejit:true", M::UseJitCompilation);
EXPECT_SINGLE_PARSE_VALUE(false, "-Xusejit:false", M::UseJitCompilation);
}
{
EXPECT_SINGLE_PARSE_VALUE(
MemoryKiB(16 * KB), "-Xjitinitialsize:16K", M::JITCodeCacheInitialCapacity);
EXPECT_SINGLE_PARSE_VALUE(
MemoryKiB(16 * MB), "-Xjitmaxsize:16M", M::JITCodeCacheMaxCapacity);
}
{
EXPECT_SINGLE_PARSE_VALUE(12345u, "-Xjitthreshold:12345", M::JITOptimizeThreshold);
}
} // TEST_F
/*
* -Xps-*
*/
TEST_F(CmdlineParserTest, ProfileSaverOptions) {
ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, 8, "abc", true);
EXPECT_SINGLE_PARSE_VALUE(opt,
"-Xjitsaveprofilinginfo "
"-Xps-min-save-period-ms:1 "
"-Xps-min-first-save-ms:2 "
"-Xps-save-resolved-classes-delay-ms:3 "
"-Xps-hot-startup-method-samples:4 "
"-Xps-min-methods-to-save:5 "
"-Xps-min-classes-to-save:6 "
"-Xps-min-notification-before-wake:7 "
"-Xps-max-notification-before-wake:8 "
"-Xps-profile-path:abc "
"-Xps-profile-boot-class-path",
M::ProfileSaverOpts);
} // TEST_F
/* -Xexperimental:_ */
TEST_F(CmdlineParserTest, TestExperimentalFlags) {
// Default
EXPECT_SINGLE_PARSE_DEFAULT_VALUE(ExperimentalFlags::kNone,
"",
M::Experimental);
// Disabled explicitly
EXPECT_SINGLE_PARSE_VALUE(ExperimentalFlags::kNone,
"-Xexperimental:none",
M::Experimental);
}
// -Xverify:_
TEST_F(CmdlineParserTest, TestVerify) {
EXPECT_SINGLE_PARSE_VALUE(verifier::VerifyMode::kNone, "-Xverify:none", M::Verify);
EXPECT_SINGLE_PARSE_VALUE(verifier::VerifyMode::kEnable, "-Xverify:remote", M::Verify);
EXPECT_SINGLE_PARSE_VALUE(verifier::VerifyMode::kEnable, "-Xverify:all", M::Verify);
EXPECT_SINGLE_PARSE_VALUE(verifier::VerifyMode::kSoftFail, "-Xverify:softfail", M::Verify);
}
TEST_F(CmdlineParserTest, TestIgnoreUnrecognized) {
RuntimeParser::Builder parserBuilder;
parserBuilder
.Define("-help")
.IntoKey(M::Help)
.IgnoreUnrecognized(true);
parser_.reset(new RuntimeParser(parserBuilder.Build()));
EXPECT_SINGLE_PARSE_EMPTY_SUCCESS("-non-existent-option");
EXPECT_SINGLE_PARSE_EMPTY_SUCCESS("-non-existent-option1 --non-existent-option-2");
} // TEST_F
TEST_F(CmdlineParserTest, TestIgnoredArguments) {
std::initializer_list<const char*> ignored_args = {
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:abdef",
"-Xdexopt:foobar", "-Xnoquithandler", "-Xjnigreflimit:ixnay", "-Xgenregmap", "-Xnogenregmap",
"-Xverifyopt:never", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:noop",
"-Xincludeselectedmethod", "-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:nosuchluck",
"-Xjitoffset:none", "-Xjitconfig:yes", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile",
"-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=1337"
};
// Check they are ignored when parsed one at a time
for (auto&& arg : ignored_args) {
SCOPED_TRACE(arg);
EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(arg);
}
// Check they are ignored when we pass it all together at once
std::vector<const char*> argv = ignored_args;
EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv);
} // TEST_F
TEST_F(CmdlineParserTest, MultipleArguments) {
EXPECT_TRUE(IsResultSuccessful(parser_->Parse(
"-help -XX:ForegroundHeapGrowthMultiplier=0.5 "
"-Xmethod-trace -XX:LargeObjectSpace=map")));
auto&& map = parser_->ReleaseArgumentsMap();
EXPECT_EQ(4u, map.Size());
EXPECT_KEY_VALUE(map, M::Help, Unit{});
EXPECT_KEY_VALUE(map, M::ForegroundHeapGrowthMultiplier, 0.5);
EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{});
EXPECT_KEY_VALUE(map, M::LargeObjectSpace, gc::space::LargeObjectSpaceType::kMap);
} // TEST_F
TEST_F(CmdlineParserTest, TypesNotInRuntime) {
using ParseCommaSeparatedIntList = ParseIntList<','>;
CmdlineType<ParseCommaSeparatedIntList> ct;
auto success0 =
CmdlineParseResult<ParseCommaSeparatedIntList>::Success(ParseCommaSeparatedIntList({1, 2, 3, 4}));
EXPECT_EQ(success0, ct.Parse("1,2,3,4"));
auto success1 =
CmdlineParseResult<ParseCommaSeparatedIntList>::Success(ParseCommaSeparatedIntList({0}));
EXPECT_EQ(success1, ct.Parse("1"));
EXPECT_FALSE(ct.Parse("").IsSuccess());
EXPECT_FALSE(ct.Parse(",").IsSuccess());
EXPECT_FALSE(ct.Parse("1,").IsSuccess());
EXPECT_FALSE(ct.Parse(",1").IsSuccess());
EXPECT_FALSE(ct.Parse("1a2").IsSuccess());
EXPECT_EQ(CmdlineResult::kOutOfRange, ct.Parse("1,10000000000000").GetStatus());
EXPECT_EQ(CmdlineResult::kOutOfRange, ct.Parse("-10000000000000,123").GetStatus());
} // TEST_F
} // namespace art

102
cmdline/cmdline_result.h Normal file
View File

@ -0,0 +1,102 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_CMDLINE_RESULT_H_
#define ART_CMDLINE_CMDLINE_RESULT_H_
#include <assert.h>
#include "base/utils.h"
namespace art {
// Result of an attempt to process the command line arguments. If fails, specifies
// the specific error code and an error message.
// Use the value-carrying CmdlineParseResult<T> to get an additional value out in a success case.
struct CmdlineResult {
enum Status {
kSuccess,
// Error codes:
kUsage,
kFailure,
kOutOfRange,
kUnknown,
};
// Short-hand for checking if the result was successful.
operator bool() const {
return IsSuccess();
}
// Check if the operation has succeeded.
bool IsSuccess() const { return status_ == kSuccess; }
// Check if the operation was not a success.
bool IsError() const { return status_ != kSuccess; }
// Get the specific status, regardless of whether it's failure or success.
Status GetStatus() const { return status_; }
// Get the error message, *must* only be called for error status results.
const std::string& GetMessage() const { assert(IsError()); return message_; }
// Constructor any status. No message.
explicit CmdlineResult(Status status) : status_(status) {}
// Constructor with an error status, copying the message.
CmdlineResult(Status status, const std::string& message)
: status_(status), message_(message) {
assert(status != kSuccess);
}
// Constructor with an error status, taking over the message.
CmdlineResult(Status status, std::string&& message)
: status_(status), message_(message) {
assert(status != kSuccess);
}
// Make sure copying exists
CmdlineResult(const CmdlineResult&) = default;
// Make sure moving is cheap
CmdlineResult(CmdlineResult&&) = default;
private:
const Status status_;
const std::string message_;
};
// TODO: code-generate this
static inline std::ostream& operator<<(std::ostream& stream, CmdlineResult::Status status) {
switch (status) {
case CmdlineResult::kSuccess:
stream << "kSuccess";
break;
case CmdlineResult::kUsage:
stream << "kUsage";
break;
case CmdlineResult::kFailure:
stream << "kFailure";
break;
case CmdlineResult::kOutOfRange:
stream << "kOutOfRange";
break;
case CmdlineResult::kUnknown:
stream << "kUnknown";
break;
default:
UNREACHABLE();
}
return stream;
}
} // namespace art
#endif // ART_CMDLINE_CMDLINE_RESULT_H_

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_CMDLINE_TYPE_PARSER_H_
#define ART_CMDLINE_CMDLINE_TYPE_PARSER_H_
#include "cmdline_parse_result.h"
namespace art {
// Base class for user-defined CmdlineType<T> specializations.
//
// Not strictly necessary, but if the specializations fail to Define all of these functions
// the compilation will fail.
template <typename T>
struct CmdlineTypeParser {
// Return value of parsing attempts. Represents a Success(T value) or an Error(int code)
using Result = CmdlineParseResult<T>;
// Parse a single value for an argument definition out of the wildcard component.
//
// e.g. if the argument definition was "foo:_", and the user-provided input was "foo:bar",
// then args is "bar".
Result Parse(const std::string& args ATTRIBUTE_UNUSED) {
assert(false);
return Result::Failure("Missing type specialization and/or value map");
}
// Parse a value and append it into the existing value so far, for argument
// definitions which are marked with AppendValues().
//
// The value is parsed out of the wildcard component as in Parse.
//
// If the initial value does not exist yet, a default value is created by
// value-initializing with 'T()'.
Result ParseAndAppend(const std::string& args ATTRIBUTE_UNUSED,
T& existing_value ATTRIBUTE_UNUSED) {
assert(false);
return Result::Failure("Missing type specialization and/or value map");
}
// Runtime type name of T, so that we can print more useful error messages.
static const char* Name() { assert(false); return "UnspecializedType"; }
static const char* DescribeType() { assert(false); return "UnspecializedType"; }
// Whether or not your type can parse argument definitions defined without a "_"
// e.g. -Xenable-profiler just mutates the existing profiler struct in-place
// so it doesn't need to do any parsing other than token recognition.
//
// If this is false, then either the argument definition has a _, from which the parsing
// happens, or the tokens get mapped to a value list/map from which a 1:1 matching occurs.
//
// This should almost *always* be false!
static constexpr bool kCanParseBlankless = false;
protected:
// Don't accidentally initialize instances of this directly; they will assert at runtime.
CmdlineTypeParser() = default;
};
} // namespace art
#endif // ART_CMDLINE_CMDLINE_TYPE_PARSER_H_

878
cmdline/cmdline_types.h Normal file
View File

@ -0,0 +1,878 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_CMDLINE_TYPES_H_
#define ART_CMDLINE_CMDLINE_TYPES_H_
#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
#include <list>
#include <ostream>
#include "android-base/parsebool.h"
#include "android-base/stringprintf.h"
#include "cmdline_type_parser.h"
#include "detail/cmdline_debug_detail.h"
#include "memory_representation.h"
#include "android-base/logging.h"
#include "android-base/strings.h"
// Includes for the types that are being specialized
#include <string>
#include "base/time_utils.h"
#include "base/logging.h"
#include "experimental_flags.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
#include "jdwp_provider.h"
#include "jit/profile_saver_options.h"
#include "plugin.h"
#include "read_barrier_config.h"
#include "ti/agent.h"
#include "unit.h"
namespace art {
// The default specialization will always fail parsing the type from a string.
// Provide your own specialization that inherits from CmdlineTypeParser<T>
// and implements either Parse or ParseAndAppend
// (only if the argument was defined with ::AppendValues()) but not both.
template <typename T>
struct CmdlineType : CmdlineTypeParser<T> {
};
// Specializations for CmdlineType<T> follow:
// Parse argument definitions for Unit-typed arguments.
template <>
struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
Result Parse(const std::string& args) {
if (args == "") {
return Result::Success(Unit{});
}
return Result::Failure("Unexpected extra characters " + args);
}
};
template <>
struct CmdlineType<bool> : CmdlineTypeParser<bool> {
Result Parse(const std::string& args) {
switch (::android::base::ParseBool(args)) {
case ::android::base::ParseBoolResult::kError:
return Result::Failure("Could not parse '" + args + "' as boolean");
case ::android::base::ParseBoolResult::kTrue:
return Result::Success(true);
case ::android::base::ParseBoolResult::kFalse:
return Result::Success(false);
}
}
static const char* DescribeType() { return "true|false|1|0|y|n|yes|no|on|off"; }
};
template <>
struct CmdlineType<JdwpProvider> : CmdlineTypeParser<JdwpProvider> {
/*
* Handle a single JDWP provider name. Must be either 'internal', 'default', or the file name of
* an agent. A plugin will make use of this and the jdwpOptions to set up jdwp when appropriate.
*/
Result Parse(const std::string& option) {
if (option == "help") {
return Result::Usage(
"Example: -XjdwpProvider:none to disable JDWP\n"
"Example: -XjdwpProvider:adbconnection for adb connection mediated jdwp implementation\n"
"Example: -XjdwpProvider:default for the default jdwp implementation\n");
} else if (option == "default") {
return Result::Success(JdwpProvider::kDefaultJdwpProvider);
} else if (option == "adbconnection") {
return Result::Success(JdwpProvider::kAdbConnection);
} else if (option == "none") {
return Result::Success(JdwpProvider::kNone);
} else {
return Result::Failure(std::string("not a valid jdwp provider: ") + option);
}
}
static const char* Name() { return "JdwpProvider"; }
static const char* DescribeType() { return "none|adbconnection|default"; }
};
template <size_t Divisor>
struct CmdlineType<Memory<Divisor>> : CmdlineTypeParser<Memory<Divisor>> {
using typename CmdlineTypeParser<Memory<Divisor>>::Result;
Result Parse(const std::string& arg) {
CMDLINE_DEBUG_LOG << "Parsing memory: " << arg << std::endl;
size_t val = ParseMemoryOption(arg.c_str(), Divisor);
CMDLINE_DEBUG_LOG << "Memory parsed to size_t value: " << val << std::endl;
if (val == 0) {
return Result::Failure(std::string("not a valid memory value, or not divisible by ")
+ std::to_string(Divisor));
}
return Result::Success(Memory<Divisor>(val));
}
// Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
// memory sizes. [kK] indicates kilobytes, [mM] megabytes, and
// [gG] gigabytes.
//
// "s" should point just past the "-Xm?" part of the string.
// "div" specifies a divisor, e.g. 1024 if the value must be a multiple
// of 1024.
//
// The spec says the -Xmx and -Xms options must be multiples of 1024. It
// doesn't say anything about -Xss.
//
// Returns 0 (a useless size) if "s" is malformed or specifies a low or
// non-evenly-divisible value.
//
static size_t ParseMemoryOption(const char* s, size_t div) {
// strtoul accepts a leading [+-], which we don't want,
// so make sure our string starts with a decimal digit.
if (isdigit(*s)) {
char* s2;
size_t val = strtoul(s, &s2, 10);
if (s2 != s) {
// s2 should be pointing just after the number.
// If this is the end of the string, the user
// has specified a number of bytes. Otherwise,
// there should be exactly one more character
// that specifies a multiplier.
if (*s2 != '\0') {
// The remainder of the string is either a single multiplier
// character, or nothing to indicate that the value is in
// bytes.
char c = *s2++;
if (*s2 == '\0') {
size_t mul;
if (c == '\0') {
mul = 1;
} else if (c == 'k' || c == 'K') {
mul = KB;
} else if (c == 'm' || c == 'M') {
mul = MB;
} else if (c == 'g' || c == 'G') {
mul = GB;
} else {
// Unknown multiplier character.
return 0;
}
if (val <= std::numeric_limits<size_t>::max() / mul) {
val *= mul;
} else {
// Clamp to a multiple of 1024.
val = std::numeric_limits<size_t>::max() & ~(1024-1);
}
} else {
// There's more than one character after the numeric part.
return 0;
}
}
// The man page says that a -Xm value must be a multiple of 1024.
if (val % div == 0) {
return val;
}
}
}
return 0;
}
static const char* Name() { return Memory<Divisor>::Name(); }
static const char* DescribeType() {
static std::string str;
if (str.empty()) {
str = "Memory with granularity of " + std::to_string(Divisor) + " bytes";
}
return str.c_str();
}
};
template <>
struct CmdlineType<double> : CmdlineTypeParser<double> {
Result Parse(const std::string& str) {
char* end = nullptr;
errno = 0;
double value = strtod(str.c_str(), &end);
if (*end != '\0') {
return Result::Failure("Failed to parse double from " + str);
}
if (errno == ERANGE) {
return Result::OutOfRange(
"Failed to parse double from " + str + "; overflow/underflow occurred");
}
return Result::Success(value);
}
static const char* Name() { return "double"; }
static const char* DescribeType() { return "double value"; }
};
template <typename T>
static inline CmdlineParseResult<T> ParseNumeric(const std::string& str) {
static_assert(sizeof(T) < sizeof(long long int), // NOLINT [runtime/int] [4]
"Current support is restricted.");
const char* begin = str.c_str();
char* end;
// Parse into a larger type (long long) because we can't use strtoul
// since it silently converts negative values into unsigned long and doesn't set errno.
errno = 0;
long long int result = strtoll(begin, &end, 10); // NOLINT [runtime/int] [4]
if (begin == end || *end != '\0' || errno == EINVAL) {
return CmdlineParseResult<T>::Failure("Failed to parse integer from " + str);
} else if ((errno == ERANGE) || // NOLINT [runtime/int] [4]
result < std::numeric_limits<T>::min() || result > std::numeric_limits<T>::max()) {
return CmdlineParseResult<T>::OutOfRange(
"Failed to parse integer from " + str + "; out of range");
}
return CmdlineParseResult<T>::Success(static_cast<T>(result));
}
template <>
struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
Result Parse(const std::string& str) {
return ParseNumeric<unsigned int>(str);
}
static const char* Name() { return "unsigned integer"; }
static const char* DescribeType() { return "unsigned integer value"; }
};
template <>
struct CmdlineType<int> : CmdlineTypeParser<int> {
Result Parse(const std::string& str) {
return ParseNumeric<int>(str);
}
static const char* Name() { return "integer"; }
static const char* DescribeType() { return "integer value"; }
};
// Lightweight nanosecond value type. Allows parser to convert user-input from milliseconds
// to nanoseconds automatically after parsing.
//
// All implicit conversion from uint64_t uses nanoseconds.
struct MillisecondsToNanoseconds {
// Create from nanoseconds.
MillisecondsToNanoseconds(uint64_t nanoseconds) : nanoseconds_(nanoseconds) { // NOLINT [runtime/explicit] [5]
}
// Create from milliseconds.
static MillisecondsToNanoseconds FromMilliseconds(unsigned int milliseconds) {
return MillisecondsToNanoseconds(MsToNs(milliseconds));
}
// Get the underlying nanoseconds value.
uint64_t GetNanoseconds() const {
return nanoseconds_;
}
// Get the milliseconds value [via a conversion]. Loss of precision will occur.
uint64_t GetMilliseconds() const {
return NsToMs(nanoseconds_);
}
// Get the underlying nanoseconds value.
operator uint64_t() const {
return GetNanoseconds();
}
// Default constructors/copy-constructors.
MillisecondsToNanoseconds() : nanoseconds_(0ul) {}
MillisecondsToNanoseconds(const MillisecondsToNanoseconds&) = default;
MillisecondsToNanoseconds(MillisecondsToNanoseconds&&) = default;
private:
uint64_t nanoseconds_;
};
template <>
struct CmdlineType<MillisecondsToNanoseconds> : CmdlineTypeParser<MillisecondsToNanoseconds> {
Result Parse(const std::string& str) {
CmdlineType<unsigned int> uint_parser;
CmdlineParseResult<unsigned int> res = uint_parser.Parse(str);
if (res.IsSuccess()) {
return Result::Success(MillisecondsToNanoseconds::FromMilliseconds(res.GetValue()));
} else {
return Result::CastError(res);
}
}
static const char* Name() { return "MillisecondsToNanoseconds"; }
static const char* DescribeType() { return "millisecond value"; }
};
template <>
struct CmdlineType<std::string> : CmdlineTypeParser<std::string> {
Result Parse(const std::string& args) {
return Result::Success(args);
}
Result ParseAndAppend(const std::string& args,
std::string& existing_value) {
if (existing_value.empty()) {
existing_value = args;
} else {
existing_value += ' ';
existing_value += args;
}
return Result::SuccessNoValue();
}
static const char* DescribeType() { return "string value"; }
};
template <>
struct CmdlineType<std::vector<Plugin>> : CmdlineTypeParser<std::vector<Plugin>> {
Result Parse(const std::string& args) {
assert(false && "Use AppendValues() for a Plugin vector type");
return Result::Failure("Unconditional failure: Plugin vector must be appended: " + args);
}
Result ParseAndAppend(const std::string& args,
std::vector<Plugin>& existing_value) {
existing_value.push_back(Plugin::Create(args));
return Result::SuccessNoValue();
}
static const char* Name() { return "std::vector<Plugin>"; }
static const char* DescribeType() { return "/path/to/libplugin.so"; }
};
template <>
struct CmdlineType<std::list<ti::AgentSpec>> : CmdlineTypeParser<std::list<ti::AgentSpec>> {
Result Parse(const std::string& args) {
assert(false && "Use AppendValues() for an Agent list type");
return Result::Failure("Unconditional failure: Agent list must be appended: " + args);
}
Result ParseAndAppend(const std::string& args,
std::list<ti::AgentSpec>& existing_value) {
existing_value.emplace_back(args);
return Result::SuccessNoValue();
}
static const char* Name() { return "std::list<ti::AgentSpec>"; }
static const char* DescribeType() { return "/path/to/libagent.so=options"; }
};
template <>
struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
Result Parse(const std::string& args) {
assert(false && "Use AppendValues() for a string vector type");
return Result::Failure("Unconditional failure: string vector must be appended: " + args);
}
Result ParseAndAppend(const std::string& args,
std::vector<std::string>& existing_value) {
existing_value.push_back(args);
return Result::SuccessNoValue();
}
static const char* Name() { return "std::vector<std::string>"; }
static const char* DescribeType() { return "string value"; }
};
template <>
struct CmdlineType<std::vector<int>> : CmdlineTypeParser<std::vector<int>> {
Result Parse(const std::string& args) {
assert(false && "Use AppendValues() for a int vector type");
return Result::Failure("Unconditional failure: string vector must be appended: " + args);
}
Result ParseAndAppend(const std::string& args,
std::vector<int>& existing_value) {
auto result = ParseNumeric<int>(args);
if (result.IsSuccess()) {
existing_value.push_back(result.GetValue());
} else {
return Result::CastError(result);
}
return Result::SuccessNoValue();
}
static const char* Name() { return "std::vector<int>"; }
static const char* DescribeType() { return "int values"; }
};
template <typename ArgType, char Separator>
struct ParseList {
explicit ParseList(std::vector<ArgType>&& list) : list_(list) {}
operator std::vector<ArgType>() const {
return list_;
}
operator std::vector<ArgType>&&() && {
return std::move(list_);
}
size_t Size() const {
return list_.size();
}
std::string Join() const {
return android::base::Join(list_, Separator);
}
ParseList() = default;
ParseList(const ParseList&) = default;
ParseList(ParseList&&) noexcept = default;
private:
std::vector<ArgType> list_;
};
template <char Separator>
using ParseIntList = ParseList<int, Separator>;
template <char Separator>
struct ParseStringList : public ParseList<std::string, Separator> {
explicit ParseStringList(std::vector<std::string>&& list) : ParseList<std::string, Separator>(std::move(list)) {}
static ParseStringList<Separator> Split(const std::string& str) {
std::vector<std::string> list;
art::Split(str, Separator, &list);
return ParseStringList<Separator>(std::move(list));
}
ParseStringList() = default;
ParseStringList(const ParseStringList&) = default;
ParseStringList(ParseStringList&&) noexcept = default;
};
template <char Separator>
struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringList<Separator>> {
using Result = CmdlineParseResult<ParseStringList<Separator>>;
Result Parse(const std::string& args) {
return Result::Success(ParseStringList<Separator>::Split(args));
}
static const char* Name() { return "ParseStringList<Separator>"; }
static const char* DescribeType() {
static std::string str;
if (str.empty()) {
str = android::base::StringPrintf("list separated by '%c'", Separator);
}
return str.c_str();
}
};
template <char Separator>
struct CmdlineType<ParseIntList<Separator>> : CmdlineTypeParser<ParseIntList<Separator>> {
using Result = CmdlineParseResult<ParseIntList<Separator>>;
Result Parse(const std::string& args) {
std::vector<int> list;
const char* pos = args.c_str();
errno = 0;
while (true) {
char* end = nullptr;
int64_t value = strtol(pos, &end, 10);
if (pos == end || errno == EINVAL) {
return Result::Failure("Failed to parse integer from " + args);
} else if ((errno == ERANGE) || // NOLINT [runtime/int] [4]
value < std::numeric_limits<int>::min() ||
value > std::numeric_limits<int>::max()) {
return Result::OutOfRange("Failed to parse integer from " + args + "; out of range");
}
list.push_back(static_cast<int>(value));
if (*end == '\0') {
break;
} else if (*end != Separator) {
return Result::Failure(std::string("Unexpected character: ") + *end);
}
pos = end + 1;
}
return Result::Success(ParseIntList<Separator>(std::move(list)));
}
static const char* Name() { return "ParseIntList<Separator>"; }
static const char* DescribeType() {
static std::string str;
if (str.empty()) {
str = android::base::StringPrintf("integer list separated by '%c'", Separator);
}
return str.c_str();
}
};
static gc::CollectorType ParseCollectorType(const std::string& option) {
if (option == "MS" || option == "nonconcurrent") {
return gc::kCollectorTypeMS;
} else if (option == "CMS" || option == "concurrent") {
return gc::kCollectorTypeCMS;
} else if (option == "SS") {
return gc::kCollectorTypeSS;
} else if (option == "CC") {
return gc::kCollectorTypeCC;
} else {
return gc::kCollectorTypeNone;
}
}
struct XGcOption {
// These defaults are used when the command line arguments for -Xgc:
// are either omitted completely or partially.
gc::CollectorType collector_type_ = gc::kCollectorTypeDefault;
bool verify_pre_gc_heap_ = false;
bool verify_pre_sweeping_heap_ = kIsDebugBuild;
bool generational_cc = kEnableGenerationalCCByDefault;
bool verify_post_gc_heap_ = false;
bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
bool verify_pre_sweeping_rosalloc_ = false;
bool verify_post_gc_rosalloc_ = false;
// Do no measurements for kUseTableLookupReadBarrier to avoid test timeouts. b/31679493
bool measure_ = kIsDebugBuild && !kUseTableLookupReadBarrier;
bool gcstress_ = false;
};
template <>
struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
Result Parse(const std::string& option) { // -Xgc: already stripped
XGcOption xgc{};
std::vector<std::string> gc_options;
Split(option, ',', &gc_options);
for (const std::string& gc_option : gc_options) {
gc::CollectorType collector_type = ParseCollectorType(gc_option);
if (collector_type != gc::kCollectorTypeNone) {
xgc.collector_type_ = collector_type;
} else if (gc_option == "preverify") {
xgc.verify_pre_gc_heap_ = true;
} else if (gc_option == "nopreverify") {
xgc.verify_pre_gc_heap_ = false;
} else if (gc_option == "presweepingverify") {
xgc.verify_pre_sweeping_heap_ = true;
} else if (gc_option == "nopresweepingverify") {
xgc.verify_pre_sweeping_heap_ = false;
} else if (gc_option == "generational_cc") {
// Note: Option "-Xgc:generational_cc" can be passed directly by
// app_process/zygote (see `android::AndroidRuntime::startVm`). If this
// option is ever deprecated, it should still be accepted (but ignored)
// for compatibility reasons (this should not prevent the runtime from
// starting up).
xgc.generational_cc = true;
} else if (gc_option == "nogenerational_cc") {
// Note: Option "-Xgc:nogenerational_cc" can be passed directly by
// app_process/zygote (see `android::AndroidRuntime::startVm`). If this
// option is ever deprecated, it should still be accepted (but ignored)
// for compatibility reasons (this should not prevent the runtime from
// starting up).
xgc.generational_cc = false;
} else if (gc_option == "postverify") {
xgc.verify_post_gc_heap_ = true;
} else if (gc_option == "nopostverify") {
xgc.verify_post_gc_heap_ = false;
} else if (gc_option == "preverify_rosalloc") {
xgc.verify_pre_gc_rosalloc_ = true;
} else if (gc_option == "nopreverify_rosalloc") {
xgc.verify_pre_gc_rosalloc_ = false;
} else if (gc_option == "presweepingverify_rosalloc") {
xgc.verify_pre_sweeping_rosalloc_ = true;
} else if (gc_option == "nopresweepingverify_rosalloc") {
xgc.verify_pre_sweeping_rosalloc_ = false;
} else if (gc_option == "postverify_rosalloc") {
xgc.verify_post_gc_rosalloc_ = true;
} else if (gc_option == "nopostverify_rosalloc") {
xgc.verify_post_gc_rosalloc_ = false;
} else if (gc_option == "gcstress") {
xgc.gcstress_ = true;
} else if (gc_option == "nogcstress") {
xgc.gcstress_ = false;
} else if (gc_option == "measure") {
xgc.measure_ = true;
} else if ((gc_option == "precise") ||
(gc_option == "noprecise") ||
(gc_option == "verifycardtable") ||
(gc_option == "noverifycardtable")) {
// Ignored for backwards compatibility.
} else {
return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
}
}
return Result::Success(std::move(xgc));
}
static const char* Name() { return "XgcOption"; }
static const char* DescribeType() {
return "MS|nonconccurent|concurrent|CMS|SS|CC|[no]preverify[_rosalloc]|"
"[no]presweepingverify[_rosalloc]|[no]generation_cc|[no]postverify[_rosalloc]|"
"[no]gcstress|measure|[no]precisce|[no]verifycardtable";
}
};
struct BackgroundGcOption {
// If background_collector_type_ is kCollectorTypeNone, it defaults to the
// XGcOption::collector_type_ after parsing options. If you set this to
// kCollectorTypeHSpaceCompact then we will do an hspace compaction when
// we transition to background instead of a normal collector transition.
gc::CollectorType background_collector_type_;
BackgroundGcOption(gc::CollectorType background_collector_type) // NOLINT [runtime/explicit] [5]
: background_collector_type_(background_collector_type) {}
BackgroundGcOption()
: background_collector_type_(gc::kCollectorTypeNone) {
}
operator gc::CollectorType() const { return background_collector_type_; }
};
template<>
struct CmdlineType<BackgroundGcOption>
: CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
Result Parse(const std::string& substring) {
// Special handling for HSpaceCompact since this is only valid as a background GC type.
if (substring == "HSpaceCompact") {
background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
} else {
gc::CollectorType collector_type = ParseCollectorType(substring);
if (collector_type != gc::kCollectorTypeNone) {
background_collector_type_ = collector_type;
} else {
return Result::Failure();
}
}
BackgroundGcOption res = *this;
return Result::Success(res);
}
static const char* Name() { return "BackgroundGcOption"; }
static const char* DescribeType() {
return "HSpaceCompact|MS|nonconccurent|CMS|concurrent|SS|CC";
}
};
template <>
struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
Result Parse(const std::string& options) {
LogVerbosity log_verbosity = LogVerbosity();
std::vector<std::string> verbose_options;
Split(options, ',', &verbose_options);
for (size_t j = 0; j < verbose_options.size(); ++j) {
if (verbose_options[j] == "class") {
log_verbosity.class_linker = true;
} else if (verbose_options[j] == "collector") {
log_verbosity.collector = true;
} else if (verbose_options[j] == "compiler") {
log_verbosity.compiler = true;
} else if (verbose_options[j] == "deopt") {
log_verbosity.deopt = true;
} else if (verbose_options[j] == "gc") {
log_verbosity.gc = true;
} else if (verbose_options[j] == "heap") {
log_verbosity.heap = true;
} else if (verbose_options[j] == "interpreter") {
log_verbosity.interpreter = true;
} else if (verbose_options[j] == "jdwp") {
log_verbosity.jdwp = true;
} else if (verbose_options[j] == "jit") {
log_verbosity.jit = true;
} else if (verbose_options[j] == "jni") {
log_verbosity.jni = true;
} else if (verbose_options[j] == "monitor") {
log_verbosity.monitor = true;
} else if (verbose_options[j] == "oat") {
log_verbosity.oat = true;
} else if (verbose_options[j] == "profiler") {
log_verbosity.profiler = true;
} else if (verbose_options[j] == "signals") {
log_verbosity.signals = true;
} else if (verbose_options[j] == "simulator") {
log_verbosity.simulator = true;
} else if (verbose_options[j] == "startup") {
log_verbosity.startup = true;
} else if (verbose_options[j] == "third-party-jni") {
log_verbosity.third_party_jni = true;
} else if (verbose_options[j] == "threads") {
log_verbosity.threads = true;
} else if (verbose_options[j] == "verifier") {
log_verbosity.verifier = true;
} else if (verbose_options[j] == "verifier-debug") {
log_verbosity.verifier_debug = true;
} else if (verbose_options[j] == "image") {
log_verbosity.image = true;
} else if (verbose_options[j] == "systrace-locks") {
log_verbosity.systrace_lock_logging = true;
} else if (verbose_options[j] == "plugin") {
log_verbosity.plugin = true;
} else if (verbose_options[j] == "agents") {
log_verbosity.agents = true;
} else if (verbose_options[j] == "dex") {
log_verbosity.dex = true;
} else {
return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
}
}
return Result::Success(log_verbosity);
}
static const char* Name() { return "LogVerbosity"; }
static const char* DescribeType() {
return "class|collector|compiler|deopt|gc|heap|interpreter|jdwp|jit|jni|monitor|oat|profiler|"
"signals|simulator|startup|third-party-jni|threads|verifier|verifier-debug|image|"
"systrace-locks|plugin|agents|dex";
}
};
template <>
struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> {
using Result = CmdlineParseResult<ProfileSaverOptions>;
private:
using StringResult = CmdlineParseResult<std::string>;
using DoubleResult = CmdlineParseResult<double>;
template <typename T>
static Result ParseInto(ProfileSaverOptions& options,
T ProfileSaverOptions::*pField,
CmdlineParseResult<T>&& result) {
assert(pField != nullptr);
if (result.IsSuccess()) {
options.*pField = result.ReleaseValue();
return Result::SuccessNoValue();
}
return Result::CastError(result);
}
static std::string RemovePrefix(const std::string& source) {
size_t prefix_idx = source.find(':');
if (prefix_idx == std::string::npos) {
return "";
}
return source.substr(prefix_idx + 1);
}
public:
Result ParseAndAppend(const std::string& option, ProfileSaverOptions& existing) {
// Special case which doesn't include a wildcard argument definition.
// We pass-it through as-is.
if (option == "-Xjitsaveprofilinginfo") {
existing.enabled_ = true;
return Result::SuccessNoValue();
}
if (option == "profile-boot-class-path") {
existing.profile_boot_class_path_ = true;
return Result::SuccessNoValue();
}
if (option == "profile-aot-code") {
existing.profile_aot_code_ = true;
return Result::SuccessNoValue();
}
if (option == "save-without-jit-notifications") {
existing.wait_for_jit_notifications_to_save_ = false;
return Result::SuccessNoValue();
}
// The rest of these options are always the wildcard from '-Xps-*'
std::string suffix = RemovePrefix(option);
if (android::base::StartsWith(option, "min-save-period-ms:")) {
CmdlineType<unsigned int> type_parser;
return ParseInto(existing,
&ProfileSaverOptions::min_save_period_ms_,
type_parser.Parse(suffix));
}
if (android::base::StartsWith(option, "min-first-save-ms:")) {
CmdlineType<unsigned int> type_parser;
return ParseInto(existing,
&ProfileSaverOptions::min_first_save_ms_,
type_parser.Parse(suffix));
}
if (android::base::StartsWith(option, "save-resolved-classes-delay-ms:")) {
CmdlineType<unsigned int> type_parser;
return ParseInto(existing,
&ProfileSaverOptions::save_resolved_classes_delay_ms_,
type_parser.Parse(suffix));
}
if (android::base::StartsWith(option, "hot-startup-method-samples:")) {
CmdlineType<unsigned int> type_parser;
return ParseInto(existing,
&ProfileSaverOptions::hot_startup_method_samples_,
type_parser.Parse(suffix));
}
if (android::base::StartsWith(option, "min-methods-to-save:")) {
CmdlineType<unsigned int> type_parser;
return ParseInto(existing,
&ProfileSaverOptions::min_methods_to_save_,
type_parser.Parse(suffix));
}
if (android::base::StartsWith(option, "min-classes-to-save:")) {
CmdlineType<unsigned int> type_parser;
return ParseInto(existing,
&ProfileSaverOptions::min_classes_to_save_,
type_parser.Parse(suffix));
}
if (android::base::StartsWith(option, "min-notification-before-wake:")) {
CmdlineType<unsigned int> type_parser;
return ParseInto(existing,
&ProfileSaverOptions::min_notification_before_wake_,
type_parser.Parse(suffix));
}
if (android::base::StartsWith(option, "max-notification-before-wake:")) {
CmdlineType<unsigned int> type_parser;
return ParseInto(existing,
&ProfileSaverOptions::max_notification_before_wake_,
type_parser.Parse(suffix));
}
if (android::base::StartsWith(option, "profile-path:")) {
existing.profile_path_ = suffix;
return Result::SuccessNoValue();
}
return Result::Failure(std::string("Invalid suboption '") + option + "'");
}
static const char* Name() { return "ProfileSaverOptions"; }
static const char* DescribeType() { return "string|unsigned integer"; }
static constexpr bool kCanParseBlankless = true;
};
template<>
struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> {
Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
if (option == "none") {
existing = ExperimentalFlags::kNone;
} else {
return Result::Failure(std::string("Unknown option '") + option + "'");
}
return Result::SuccessNoValue();
}
static const char* Name() { return "ExperimentalFlags"; }
static const char* DescribeType() { return "none"; }
};
} // namespace art
#endif // ART_CMDLINE_CMDLINE_TYPES_H_

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
#define ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
#include <iostream>
#ifndef CMDLINE_NDEBUG
#define CMDLINE_DEBUG_LOG std::cerr
#else
#define CMDLINE_DEBUG_LOG ::art::detail::debug_log_ignore()
#endif
namespace art {
// Implementation details for some template querying. Don't look inside if you hate templates.
namespace detail {
struct debug_log_ignore {
// Ignore most of the normal operator<< usage.
template <typename T>
debug_log_ignore& operator<<(const T&) { return *this; }
// Ignore std::endl and the like.
debug_log_ignore& operator<<(std::ostream& (*)(std::ostream&) ) { return *this; }
};
} // namespace detail // NOLINT [readability/namespace] [5]
} // namespace art
#endif // ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_

View File

@ -0,0 +1,566 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
#define ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
#include <assert.h>
#include <algorithm>
#include <functional>
#include <memory>
#include <numeric>
#include <string_view>
#include <type_traits>
#include <vector>
#include "android-base/strings.h"
#include "base/indenter.h"
#include "cmdline_parse_result.h"
#include "cmdline_types.h"
#include "token_range.h"
#include "unit.h"
namespace art {
// Implementation details for the parser. Do not look inside if you hate templates.
namespace detail {
// A non-templated base class for argument parsers. Used by the general parser
// to parse arguments, without needing to know the argument type at compile time.
//
// This is an application of the type erasure idiom.
struct CmdlineParseArgumentAny {
virtual ~CmdlineParseArgumentAny() {}
// Attempt to parse this argument starting at arguments[position].
// If the parsing succeeds, the parsed value will be saved as a side-effect.
//
// In most situations, the parsing will not match by returning kUnknown. In this case,
// no tokens were consumed and the position variable will not be updated.
//
// At other times, parsing may fail due to validation but the initial token was still matched
// (for example an out of range value, or passing in a string where an int was expected).
// In this case the tokens are still consumed, and the position variable will get incremented
// by all the consumed tokens.
//
// The # of tokens consumed by the parse attempt will be set as an out-parameter into
// consumed_tokens. The parser should skip this many tokens before parsing the next
// argument.
virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) = 0;
// How many tokens should be taken off argv for parsing this argument.
// For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
//
// A [min,max] range is returned to represent argument definitions with multiple
// value tokens. (e.g. {"-h", "-h " } would return [1,2]).
virtual std::pair<size_t, size_t> GetNumTokens() const = 0;
// Get the run-time typename of the argument type.
virtual const char* GetTypeName() const = 0;
// Try to do a close match, returning how many tokens were matched against this argument
// definition. More tokens is better.
//
// Do a quick match token-by-token, and see if they match.
// Any tokens with a wildcard in them are only matched up until the wildcard.
// If this is true, then the wildcard matching later on can still fail, so this is not
// a guarantee that the argument is correct, it's more of a strong hint that the
// user-provided input *probably* was trying to match this argument.
//
// Returns how many tokens were either matched (or ignored because there was a
// wildcard present). 0 means no match. If the Size() tokens are returned.
virtual size_t MaybeMatches(const TokenRange& tokens) = 0;
virtual void DumpHelp(VariableIndentationOutputStream& os) = 0;
virtual const std::optional<const char*>& GetCategory() = 0;
};
template <typename T>
using EnableIfNumeric = std::enable_if<std::is_arithmetic<T>::value>;
template <typename T>
using DisableIfNumeric = std::enable_if<!std::is_arithmetic<T>::value>;
// Argument definition information, created by an ArgumentBuilder and an UntypedArgumentBuilder.
template <typename TArg>
struct CmdlineParserArgumentInfo {
// This version will only be used if TArg is arithmetic and thus has the <= operators.
template <typename T = TArg> // Necessary to get SFINAE to kick in.
bool CheckRange(const TArg& value, typename EnableIfNumeric<T>::type* = nullptr) {
if (has_range_) {
return min_ <= value && value <= max_;
}
return true;
}
// This version will be used at other times when TArg is not arithmetic.
template <typename T = TArg>
bool CheckRange(const TArg&, typename DisableIfNumeric<T>::type* = nullptr) {
assert(!has_range_);
return true;
}
// Do a quick match token-by-token, and see if they match.
// Any tokens with a wildcard in them only match the prefix up until the wildcard.
//
// If this is true, then the wildcard matching later on can still fail, so this is not
// a guarantee that the argument is correct, it's more of a strong hint that the
// user-provided input *probably* was trying to match this argument.
size_t MaybeMatches(const TokenRange& token_list) const {
auto best_match = FindClosestMatch(token_list);
return best_match.second;
}
// Attempt to find the closest match (see MaybeMatches).
//
// Returns the token range that was the closest match and the # of tokens that
// this range was matched up until.
std::pair<const TokenRange*, size_t> FindClosestMatch(const TokenRange& token_list) const {
const TokenRange* best_match_ptr = nullptr;
size_t best_match = 0;
for (auto&& token_range : tokenized_names_) {
size_t this_match = token_range.MaybeMatches(token_list, std::string("_"));
if (this_match > best_match) {
best_match_ptr = &token_range;
best_match = this_match;
}
}
return std::make_pair(best_match_ptr, best_match);
}
template <typename T = TArg> // Necessary to get SFINAE to kick in.
void DumpHelp(VariableIndentationOutputStream& vios) {
// Separate arguments
vios.Stream() << std::endl;
for (auto cname : names_) {
std::string_view name = cname;
if (using_blanks_) {
name = name.substr(0, name.find("_"));
}
auto& os = vios.Stream();
auto print_once = [&]() {
os << name;
if (using_blanks_) {
if (has_value_map_) {
bool first = true;
for (auto [val, unused] : value_map_) {
os << (first ? "{" : "|") << val;
first = false;
}
os << "}";
} else if (metavar_.has_value()) {
os << *metavar_;
} else {
os << "{" << CmdlineType<T>::DescribeType() << "}";
}
}
};
print_once();
if (appending_values_) {
os << " [";
print_once();
os << "...]";
}
os << std::endl;
}
if (help_.has_value()) {
ScopedIndentation si(&vios);
vios.Stream() << *help_ << std::endl;
}
}
// Mark the argument definition as completed, do not mutate the object anymore after this
// call is done.
//
// Performs several checks of the validity and token calculations.
void CompleteArgument() {
assert(names_.size() >= 1);
assert(!is_completed_);
is_completed_ = true;
size_t blank_count = 0;
size_t token_count = 0;
size_t global_blank_count = 0;
size_t global_token_count = 0;
for (auto&& name : names_) {
std::string s(name);
size_t local_blank_count = std::count(s.begin(), s.end(), '_');
size_t local_token_count = std::count(s.begin(), s.end(), ' ');
if (global_blank_count != 0) {
assert(local_blank_count == global_blank_count
&& "Every argument descriptor string must have same amount of blanks (_)");
}
if (local_blank_count != 0) {
global_blank_count = local_blank_count;
blank_count++;
assert(local_blank_count == 1 && "More than one blank is not supported");
assert(s.back() == '_' && "The blank character must only be at the end of the string");
}
if (global_token_count != 0) {
assert(local_token_count == global_token_count
&& "Every argument descriptor string must have same amount of tokens (spaces)");
}
if (local_token_count != 0) {
global_token_count = local_token_count;
token_count++;
}
// Tokenize every name, turning it from a string to a token list.
tokenized_names_.clear();
for (auto&& name1 : names_) {
// Split along ' ' only, removing any duplicated spaces.
tokenized_names_.push_back(
TokenRange::Split(name1, {' '}).RemoveToken(" "));
}
// remove the _ character from each of the token ranges
// we will often end up with an empty token (i.e. ["-XX", "_"] -> ["-XX", ""]
// and this is OK because we still need an empty token to simplify
// range comparisons
simple_names_.clear();
for (auto&& tokenized_name : tokenized_names_) {
simple_names_.push_back(tokenized_name.RemoveCharacter('_'));
}
}
if (token_count != 0) {
assert(("Every argument descriptor string must have equal amount of tokens (spaces)" &&
token_count == names_.size()));
}
if (blank_count != 0) {
assert(("Every argument descriptor string must have an equal amount of blanks (_)" &&
blank_count == names_.size()));
}
using_blanks_ = blank_count > 0;
{
size_t smallest_name_token_range_size =
std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), ~(0u),
[](size_t min, const TokenRange& cur) {
return std::min(min, cur.Size());
});
size_t largest_name_token_range_size =
std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), 0u,
[](size_t max, const TokenRange& cur) {
return std::max(max, cur.Size());
});
token_range_size_ = std::make_pair(smallest_name_token_range_size,
largest_name_token_range_size);
}
if (has_value_list_) {
assert(names_.size() == value_list_.size()
&& "Number of arg descriptors must match number of values");
assert(!has_value_map_);
}
if (has_value_map_) {
if (!using_blanks_) {
assert(names_.size() == value_map_.size() &&
"Since no blanks were specified, each arg is mapped directly into a mapped "
"value without parsing; sizes must match");
}
assert(!has_value_list_);
}
if (!using_blanks_ && !CmdlineType<TArg>::kCanParseBlankless) {
assert((has_value_map_ || has_value_list_) &&
"Arguments without a blank (_) must provide either a value map or a value list");
}
TypedCheck();
}
// List of aliases for a single argument definition, e.g. {"-Xdex2oat", "-Xnodex2oat"}.
std::vector<const char*> names_;
// Is there at least 1 wildcard '_' in the argument definition?
bool using_blanks_ = false;
// [min, max] token counts in each arg def
std::pair<size_t, size_t> token_range_size_;
// contains all the names in a tokenized form, i.e. as a space-delimited list
std::vector<TokenRange> tokenized_names_;
// contains the tokenized names, but with the _ character stripped
std::vector<TokenRange> simple_names_;
// For argument definitions created with '.AppendValues()'
// Meaning that parsing should mutate the existing value in-place if possible.
bool appending_values_ = false;
// For argument definitions created with '.WithRange(min, max)'
bool has_range_ = false;
TArg min_;
TArg max_;
// For argument definitions created with '.WithValueMap'
bool has_value_map_ = false;
std::vector<std::pair<const char*, TArg>> value_map_;
// For argument definitions created with '.WithValues'
bool has_value_list_ = false;
std::vector<TArg> value_list_;
std::optional<const char*> help_;
std::optional<const char*> category_;
std::optional<const char*> metavar_;
// Make sure there's a default constructor.
CmdlineParserArgumentInfo() = default;
// Ensure there's a default move constructor.
CmdlineParserArgumentInfo(CmdlineParserArgumentInfo&&) noexcept = default;
private:
// Perform type-specific checks at runtime.
template <typename T = TArg>
void TypedCheck(typename std::enable_if<std::is_same<Unit, T>::value>::type* = 0) {
assert(!using_blanks_ &&
"Blanks are not supported in Unit arguments; since a Unit has no parse-able value");
}
void TypedCheck() {}
bool is_completed_ = false;
};
// A virtual-implementation of the necessary argument information in order to
// be able to parse arguments.
template <typename TArg>
struct CmdlineParseArgument : CmdlineParseArgumentAny {
CmdlineParseArgument(CmdlineParserArgumentInfo<TArg>&& argument_info,
std::function<void(TArg&)>&& save_argument,
std::function<TArg&(void)>&& load_argument)
: argument_info_(std::forward<decltype(argument_info)>(argument_info)),
save_argument_(std::forward<decltype(save_argument)>(save_argument)),
load_argument_(std::forward<decltype(load_argument)>(load_argument)) {
}
using UserTypeInfo = CmdlineType<TArg>;
virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) {
assert(arguments.Size() > 0);
assert(consumed_tokens != nullptr);
auto closest_match_res = argument_info_.FindClosestMatch(arguments);
size_t best_match_size = closest_match_res.second;
const TokenRange* best_match_arg_def = closest_match_res.first;
if (best_match_size > arguments.Size()) {
// The best match has more tokens than were provided.
// Shouldn't happen in practice since the outer parser does this check.
return CmdlineResult(CmdlineResult::kUnknown, "Size mismatch");
}
assert(best_match_arg_def != nullptr);
*consumed_tokens = best_match_arg_def->Size();
if (!argument_info_.using_blanks_) {
return ParseArgumentSingle(arguments.Join(' '));
}
// Extract out the blank value from arguments
// e.g. for a def of "foo:_" and input "foo:bar", blank_value == "bar"
std::string blank_value = "";
size_t idx = 0;
for (auto&& def_token : *best_match_arg_def) {
auto&& arg_token = arguments[idx];
// Does this definition-token have a wildcard in it?
if (def_token.find('_') == std::string::npos) {
// No, regular token. Match 1:1 against the argument token.
bool token_match = def_token == arg_token;
if (!token_match) {
return CmdlineResult(CmdlineResult::kFailure,
std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
+ " at token " + std::to_string(idx));
}
} else {
// This is a wild-carded token.
TokenRange def_split_wildcards = TokenRange::Split(def_token, {'_'});
// Extract the wildcard contents out of the user-provided arg_token.
std::unique_ptr<TokenRange> arg_matches =
def_split_wildcards.MatchSubstrings(arg_token, "_");
if (arg_matches == nullptr) {
return CmdlineResult(CmdlineResult::kFailure,
std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
+ ", with a wildcard pattern " + def_token
+ " at token " + std::to_string(idx));
}
// Get the corresponding wildcard tokens from arg_matches,
// and concatenate it to blank_value.
for (size_t sub_idx = 0;
sub_idx < def_split_wildcards.Size() && sub_idx < arg_matches->Size(); ++sub_idx) {
if (def_split_wildcards[sub_idx] == "_") {
blank_value += arg_matches->GetToken(sub_idx);
}
}
}
++idx;
}
return ParseArgumentSingle(blank_value);
}
virtual void DumpHelp(VariableIndentationOutputStream& os) {
argument_info_.DumpHelp(os);
}
virtual const std::optional<const char*>& GetCategory() {
return argument_info_.category_;
}
private:
virtual CmdlineResult ParseArgumentSingle(const std::string& argument) {
// TODO: refactor to use LookupValue for the value lists/maps
// Handle the 'WithValueMap(...)' argument definition
if (argument_info_.has_value_map_) {
for (auto&& value_pair : argument_info_.value_map_) {
const char* name = value_pair.first;
if (argument == name) {
return SaveArgument(value_pair.second);
}
}
// Error case: Fail, telling the user what the allowed values were.
std::vector<std::string> allowed_values;
for (auto&& value_pair : argument_info_.value_map_) {
const char* name = value_pair.first;
allowed_values.push_back(name);
}
std::string allowed_values_flat = android::base::Join(allowed_values, ',');
return CmdlineResult(CmdlineResult::kFailure,
"Argument value '" + argument + "' does not match any of known valid "
"values: {" + allowed_values_flat + "}");
}
// Handle the 'WithValues(...)' argument definition
if (argument_info_.has_value_list_) {
size_t arg_def_idx = 0;
for (auto&& value : argument_info_.value_list_) {
auto&& arg_def_token = argument_info_.names_[arg_def_idx];
if (arg_def_token == argument) {
return SaveArgument(value);
}
++arg_def_idx;
}
assert(arg_def_idx + 1 == argument_info_.value_list_.size() &&
"Number of named argument definitions must match number of values defined");
// Error case: Fail, telling the user what the allowed values were.
std::vector<std::string> allowed_values;
for (auto&& arg_name : argument_info_.names_) {
allowed_values.push_back(arg_name);
}
std::string allowed_values_flat = android::base::Join(allowed_values, ',');
return CmdlineResult(CmdlineResult::kFailure,
"Argument value '" + argument + "' does not match any of known valid"
"values: {" + allowed_values_flat + "}");
}
// Handle the regular case where we parsed an unknown value from a blank.
UserTypeInfo type_parser;
if (argument_info_.appending_values_) {
TArg& existing = load_argument_();
CmdlineParseResult<TArg> result = type_parser.ParseAndAppend(argument, existing);
assert(!argument_info_.has_range_);
return std::move(result);
}
CmdlineParseResult<TArg> result = type_parser.Parse(argument);
if (result.IsSuccess()) {
TArg& value = result.GetValue();
// Do a range check for 'WithRange(min,max)' argument definition.
if (!argument_info_.CheckRange(value)) {
return CmdlineParseResult<TArg>::OutOfRange(
value, argument_info_.min_, argument_info_.max_);
}
return SaveArgument(value);
}
// Some kind of type-specific parse error. Pass the result as-is.
CmdlineResult raw_result = std::move(result);
return raw_result;
}
public:
virtual const char* GetTypeName() const {
// TODO: Obviate the need for each type specialization to hardcode the type name
return UserTypeInfo::Name();
}
// How many tokens should be taken off argv for parsing this argument.
// For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
//
// A [min,max] range is returned to represent argument definitions with multiple
// value tokens. (e.g. {"-h", "-h " } would return [1,2]).
virtual std::pair<size_t, size_t> GetNumTokens() const {
return argument_info_.token_range_size_;
}
// See if this token range might begin the same as the argument definition.
virtual size_t MaybeMatches(const TokenRange& tokens) {
return argument_info_.MaybeMatches(tokens);
}
private:
CmdlineResult SaveArgument(const TArg& value) {
assert(!argument_info_.appending_values_
&& "If the values are being appended, then the updated parse value is "
"updated by-ref as a side effect and shouldn't be stored directly");
TArg val = value;
save_argument_(val);
return CmdlineResult(CmdlineResult::kSuccess);
}
CmdlineParserArgumentInfo<TArg> argument_info_;
std::function<void(TArg&)> save_argument_;
std::function<TArg&(void)> load_argument_;
};
} // namespace detail // NOLINT [readability/namespace] [5]
} // namespace art
#endif // ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
#define ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
#include <sstream>
#include <string>
#include <vector>
namespace art {
// Implementation details for some template querying. Don't look inside if you hate templates.
namespace detail {
template <typename T>
typename std::remove_reference<T>::type& FakeReference();
// SupportsInsertionOperator<T, TStream>::value will evaluate to a boolean,
// whose value is true if the TStream class supports the << operator against T,
// and false otherwise.
template <typename T2, typename TStream2 = std::ostream>
struct SupportsInsertionOperator {
private:
template <typename TStream, typename T>
static std::true_type InsertionOperatorTest(TStream& os, const T& value,
std::remove_reference<decltype(os << value)>* = 0); // NOLINT [whitespace/operators] [3]
template <typename TStream, typename ... T>
static std::false_type InsertionOperatorTest(TStream& os, const T& ... args);
public:
static constexpr bool value =
decltype(InsertionOperatorTest(FakeReference<TStream2>(), std::declval<T2>()))::value;
};
template <typename TLeft, typename TRight = TLeft, bool IsFloatingPoint = false>
struct SupportsEqualityOperatorImpl;
template <typename TLeft, typename TRight>
struct SupportsEqualityOperatorImpl<TLeft, TRight, false> {
private:
template <typename TL, typename TR>
static std::true_type EqualityOperatorTest(const TL& left, const TR& right,
std::remove_reference<decltype(left == right)>* = 0); // NOLINT [whitespace/operators] [3]
template <typename TL, typename ... T>
static std::false_type EqualityOperatorTest(const TL& left, const T& ... args);
public:
static constexpr bool value =
decltype(EqualityOperatorTest(std::declval<TLeft>(), std::declval<TRight>()))::value;
};
// Partial specialization when TLeft/TRight are both floating points.
// This is a work-around because decltype(floatvar1 == floatvar2)
// will not compile with clang:
// error: comparing floating point with == or != is unsafe [-Werror,-Wfloat-equal]
template <typename TLeft, typename TRight>
struct SupportsEqualityOperatorImpl<TLeft, TRight, true> {
static constexpr bool value = true;
};
// SupportsEqualityOperatorImpl<T1, T2>::value will evaluate to a boolean,
// whose value is true if T1 can be compared against T2 with ==,
// and false otherwise.
template <typename TLeft, typename TRight = TLeft>
struct SupportsEqualityOperator : // NOLINT [whitespace/labels] [4]
SupportsEqualityOperatorImpl<TLeft, TRight,
std::is_floating_point<TLeft>::value
&& std::is_floating_point<TRight>::value> {
};
// Convert any kind of type to an std::string, even if there's no
// serialization support for it. Unknown types get converted to an
// an arbitrary value.
//
// Meant for printing user-visible errors or unit test failures only.
template <typename T>
std::string ToStringAny(const T& value,
typename std::enable_if<
SupportsInsertionOperator<T>::value>::type* = nullptr) {
std::stringstream stream;
stream << value;
return stream.str();
}
template <typename T>
std::string ToStringAny(const std::vector<T> value,
typename std::enable_if<
SupportsInsertionOperator<T>::value>::type* = nullptr) {
std::stringstream stream;
stream << "vector{";
for (size_t i = 0; i < value.size(); ++i) {
stream << ToStringAny(value[i]);
if (i != value.size() - 1) {
stream << ',';
}
}
stream << "}";
return stream.str();
}
template <typename T>
std::string ToStringAny(const T&,
typename std::enable_if<
!SupportsInsertionOperator<T>::value>::type* = nullptr
) {
return std::string("(unknown type [no operator<< implemented] for )");
}
} // namespace detail // NOLINT [readability/namespace] [5]
} // namespace art
#endif // ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_MEMORY_REPRESENTATION_H_
#define ART_CMDLINE_MEMORY_REPRESENTATION_H_
#include <assert.h>
#include <ostream>
#include <string>
#include "base/bit_utils.h"
namespace art {
// An integral representation of bytes of memory.
// The underlying runtime size_t value is guaranteed to be a multiple of Divisor.
template <size_t kDivisor = 1024>
struct Memory {
static_assert(IsPowerOfTwo(kDivisor), "Divisor must be a power of 2");
static Memory<kDivisor> FromBytes(size_t bytes) {
assert(bytes % kDivisor == 0);
return Memory<kDivisor>(bytes);
}
Memory() : Value(0u) {}
Memory(size_t value) : Value(value) { // NOLINT [runtime/explicit] [5]
assert(value % kDivisor == 0);
}
operator size_t() const { return Value; }
size_t ToBytes() const {
return Value;
}
static const char* Name() {
static std::string str;
if (str.empty()) {
str = "Memory<" + std::to_string(kDivisor) + '>';
}
return str.c_str();
}
size_t Value;
};
template <size_t kDivisor>
std::ostream& operator<<(std::ostream& stream, Memory<kDivisor> memory) {
return stream << memory.Value << '*' << kDivisor;
}
using MemoryKiB = Memory<1024>;
} // namespace art
#endif // ART_CMDLINE_MEMORY_REPRESENTATION_H_

427
cmdline/token_range.h Normal file
View File

@ -0,0 +1,427 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_TOKEN_RANGE_H_
#define ART_CMDLINE_TOKEN_RANGE_H_
#include <assert.h>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include "android-base/strings.h"
namespace art {
// A range of tokens to make token matching algorithms easier.
//
// We try really hard to avoid copying and store only a pointer and iterators to the
// interiors of the vector, so a typical copy constructor never ends up doing a deep copy.
// It is up to the user to play nice and not to mutate the strings in-place.
//
// Tokens are only copied if a mutating operation is performed (and even then only
// if it *actually* mutates the token).
struct TokenRange {
// Short-hand for a vector of strings. A single string and a token is synonymous.
using TokenList = std::vector<std::string>;
// Copying-from-vector constructor.
explicit TokenRange(const TokenList& token_list)
: token_list_(new TokenList(token_list)),
begin_(token_list_->begin()),
end_(token_list_->end())
{}
// Copying-from-iterator constructor
template <typename ForwardIterator>
TokenRange(ForwardIterator it_begin, ForwardIterator it_end)
: token_list_(new TokenList(it_begin, it_end)),
begin_(token_list_->begin()),
end_(token_list_->end())
{}
#if 0
// Copying-from-vector constructor.
TokenRange(const TokenList& token_list ATTRIBUTE_UNUSED,
TokenList::const_iterator it_begin,
TokenList::const_iterator it_end)
: token_list_(new TokenList(it_begin, it_end)),
begin_(token_list_->begin()),
end_(token_list_->end()) {
assert(it_begin >= token_list.begin());
assert(it_end <= token_list.end());
}
#endif
// Copying from char array constructor, convertings into tokens (strings) along the way.
TokenRange(const char* token_list[], size_t length)
: token_list_(new TokenList(&token_list[0], &token_list[length])),
begin_(token_list_->begin()),
end_(token_list_->end())
{}
// Non-copying move-from-vector constructor. Takes over the token vector.
explicit TokenRange(TokenList&& token_list)
: token_list_(new TokenList(std::forward<TokenList>(token_list))),
begin_(token_list_->begin()),
end_(token_list_->end())
{}
// Non-copying constructor. Retain reference to existing list of tokens.
TokenRange(std::shared_ptr<TokenList> token_list,
TokenList::const_iterator it_begin,
TokenList::const_iterator it_end)
: token_list_(token_list),
begin_(it_begin),
end_(it_end) {
assert(it_begin >= token_list->begin());
assert(it_end <= token_list->end());
}
// Non-copying copy constructor.
TokenRange(const TokenRange&) = default;
// Non-copying move constructor.
TokenRange(TokenRange&&) = default;
// Non-copying constructor. Retains reference to an existing list of tokens, with offset.
explicit TokenRange(std::shared_ptr<TokenList> token_list)
: token_list_(token_list),
begin_(token_list_->begin()),
end_(token_list_->end())
{}
// Iterator type for begin() and end(). Guaranteed to be a RandomAccessIterator.
using iterator = TokenList::const_iterator;
// Iterator type for const begin() and const end(). Guaranteed to be a RandomAccessIterator.
using const_iterator = iterator;
// Create a token range by splitting a string. Each separator gets their own token.
// Since the separator are retained as tokens, it might be useful to call
// RemoveToken afterwards.
static TokenRange Split(const std::string& string, std::initializer_list<char> separators) {
TokenList new_token_list;
std::string tok;
for (auto&& c : string) {
for (char sep : separators) {
if (c == sep) {
// We spotted a separator character.
// Push back everything before the last separator as a new token.
// Push back the separator as a token.
if (!tok.empty()) {
new_token_list.push_back(tok);
tok = "";
}
new_token_list.push_back(std::string() + sep);
} else {
// Build up the token with another character.
tok += c;
}
}
}
if (!tok.empty()) {
new_token_list.push_back(tok);
}
return TokenRange(std::move(new_token_list));
}
// A RandomAccessIterator to the first element in this range.
iterator begin() const {
return begin_;
}
// A RandomAccessIterator to one past the last element in this range.
iterator end() const {
return end_;
}
// The size of the range, i.e. how many tokens are in it.
size_t Size() const {
return std::distance(begin_, end_);
}
// Are there 0 tokens in this range?
bool IsEmpty() const {
return Size() > 0;
}
// Look up a token by it's offset.
const std::string& GetToken(size_t offset) const {
assert(offset < Size());
return *(begin_ + offset);
}
// Does this token range equal the other range?
// Equality is defined as having both the same size, and
// each corresponding token being equal.
bool operator==(const TokenRange& other) const {
if (this == &other) {
return true;
}
if (Size() != other.Size()) {
return false;
}
return std::equal(begin(), end(), other.begin());
}
// Look up the token at the requested index.
const std::string& operator[](int index) const {
assert(index >= 0 && static_cast<size_t>(index) < Size());
return *(begin() + index);
}
// Does this current range start with the other range?
bool StartsWith(const TokenRange& other) const {
if (this == &other) {
return true;
}
if (Size() < other.Size()) {
return false;
}
auto& smaller = Size() < other.Size() ? *this : other;
auto& greater = Size() < other.Size() ? other : *this;
return std::equal(smaller.begin(), smaller.end(), greater.begin());
}
// Remove all characters 'c' from each token, potentially copying the underlying tokens.
TokenRange RemoveCharacter(char c) const {
TokenList new_token_list(begin(), end());
bool changed = false;
for (auto&& token : new_token_list) {
auto it = std::remove_if(token.begin(), token.end(), [&](char ch) {
if (ch == c) {
changed = true;
return true;
}
return false;
});
token.erase(it, token.end());
}
if (!changed) {
return *this;
}
return TokenRange(std::move(new_token_list));
}
// Remove all tokens matching this one, potentially copying the underlying tokens.
TokenRange RemoveToken(const std::string& token) {
return RemoveIf([&](const std::string& tok) { return tok == token; });
}
// Discard all empty tokens, potentially copying the underlying tokens.
TokenRange DiscardEmpty() const {
return RemoveIf([](const std::string& token) { return token.empty(); });
}
// Create a non-copying subset of this range.
// Length is trimmed so that the Slice does not go out of range.
TokenRange Slice(size_t offset, size_t length = std::string::npos) const {
assert(offset < Size());
if (length != std::string::npos && offset + length > Size()) {
length = Size() - offset;
}
iterator it_end;
if (length == std::string::npos) {
it_end = end();
} else {
it_end = begin() + offset + length;
}
return TokenRange(token_list_, begin() + offset, it_end);
}
// Try to match the string with tokens from this range.
// Each token is used to match exactly once (after which the next token is used, and so on).
// The matching happens from left-to-right in a non-greedy fashion.
// If the currently-matched token is the wildcard, then the new outputted token will
// contain as much as possible until the next token is matched.
//
// For example, if this == ["a:", "_", "b:] and "_" is the match string, then
// MatchSubstrings on "a:foob:" will yield: ["a:", "foo", "b:"]
//
// Since the string matching can fail (e.g. ["foo"] against "bar"), then this
// function can fail, in which cause it will return null.
std::unique_ptr<TokenRange> MatchSubstrings(const std::string& string,
const std::string& wildcard) const {
TokenList new_token_list;
size_t wildcard_idx = std::string::npos;
size_t string_idx = 0;
// Function to push all the characters matched as a wildcard so far
// as a brand new token. It resets the wildcard matching.
// Empty wildcards are possible and ok, but only if wildcard matching was on.
auto maybe_push_wildcard_token = [&]() {
if (wildcard_idx != std::string::npos) {
size_t wildcard_length = string_idx - wildcard_idx;
std::string wildcard_substr = string.substr(wildcard_idx, wildcard_length);
new_token_list.push_back(std::move(wildcard_substr));
wildcard_idx = std::string::npos;
}
};
for (iterator it = begin(); it != end(); ++it) {
const std::string& tok = *it;
if (tok == wildcard) {
maybe_push_wildcard_token();
wildcard_idx = string_idx;
continue;
}
size_t next_token_idx = string.find(tok);
if (next_token_idx == std::string::npos) {
// Could not find token at all
return nullptr;
} else if (next_token_idx != string_idx && wildcard_idx == std::string::npos) {
// Found the token at a non-starting location, and we weren't
// trying to parse the wildcard.
return nullptr;
}
new_token_list.push_back(string.substr(next_token_idx, tok.size()));
maybe_push_wildcard_token();
string_idx += tok.size();
}
size_t remaining = string.size() - string_idx;
if (remaining > 0) {
if (wildcard_idx == std::string::npos) {
// Some characters were still remaining in the string,
// but it wasn't trying to match a wildcard.
return nullptr;
}
}
// If some characters are remaining, the rest must be a wildcard.
string_idx += remaining;
maybe_push_wildcard_token();
return std::make_unique<TokenRange>(std::move(new_token_list));
}
// Do a quick match token-by-token, and see if they match.
// Any tokens with a wildcard in them are only matched up until the wildcard.
// If this is true, then the wildcard matching later on can still fail, so this is not
// a guarantee that the argument is correct, it's more of a strong hint that the
// user-provided input *probably* was trying to match this argument.
//
// Returns how many tokens were either matched (or ignored because there was a
// wildcard present). 0 means no match. If the size() tokens are returned.
size_t MaybeMatches(const TokenRange& token_list, const std::string& wildcard) const {
auto token_it = token_list.begin();
auto token_end = token_list.end();
auto name_it = begin();
auto name_end = end();
size_t matched_tokens = 0;
while (token_it != token_end && name_it != name_end) {
// Skip token matching when the corresponding name has a wildcard in it.
const std::string& name = *name_it;
size_t wildcard_idx = name.find(wildcard);
if (wildcard_idx == std::string::npos) { // No wildcard present
// Did the definition token match the user token?
if (name != *token_it) {
return matched_tokens;
}
} else {
std::string name_prefix = name.substr(0, wildcard_idx);
// Did the user token start with the up-to-the-wildcard prefix?
if (!StartsWith(*token_it, name_prefix)) {
return matched_tokens;
}
}
++token_it;
++name_it;
++matched_tokens;
}
// If we got this far, it's either a full match or the token list was too short.
return matched_tokens;
}
// Flatten the token range by joining every adjacent token with the separator character.
// e.g. ["hello", "world"].join('$') == "hello$world"
std::string Join(char separator) const {
TokenList tmp(begin(), end());
return android::base::Join(tmp, separator);
// TODO: Join should probably take an offset or iterators
}
private:
static bool StartsWith(const std::string& larger, const std::string& smaller) {
if (larger.size() >= smaller.size()) {
return std::equal(smaller.begin(), smaller.end(), larger.begin());
}
return false;
}
template <typename TPredicate>
TokenRange RemoveIf(const TPredicate& predicate) const {
// If any of the tokens in the token lists are empty, then
// we need to remove them and compress the token list into a smaller one.
bool remove = false;
for (auto it = begin_; it != end_; ++it) {
auto&& token = *it;
if (predicate(token)) {
remove = true;
break;
}
}
// Actually copy the token list and remove the tokens that don't match our predicate.
if (remove) {
auto token_list = std::make_shared<TokenList>(begin(), end());
TokenList::iterator new_end =
std::remove_if(token_list->begin(), token_list->end(), predicate);
token_list->erase(new_end, token_list->end());
assert(token_list_->size() > token_list->size() && "Nothing was actually removed!");
return TokenRange(token_list);
}
return *this;
}
const std::shared_ptr<std::vector<std::string>> token_list_;
const iterator begin_;
const iterator end_;
};
} // namespace art
#endif // ART_CMDLINE_TOKEN_RANGE_H_

37
cmdline/unit.h Normal file
View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_CMDLINE_UNIT_H_
#define ART_CMDLINE_UNIT_H_
namespace art {
// Used for arguments that simply indicate presence (e.g. "-help") without any values.
struct Unit {
// Historical note: We specified a user-defined constructor to avoid
// 'Conditional jump or move depends on uninitialised value(s)' errors
// when running Valgrind.
Unit() {}
Unit(const Unit&) = default;
~Unit() {}
bool operator==(Unit) const {
return true;
}
};
} // namespace art
#endif // ART_CMDLINE_UNIT_H_

556
compiler/Android.bp Normal file
View File

@ -0,0 +1,556 @@
//
// Copyright (C) 2012 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.
//
// TODO We should really separate out those files that are actually needed for both variants of an
// architecture into its own category. Currently we just include all of the 32bit variant in the
// 64bit variant. It also might be good to allow one to compile only the 64bit variant without the
// 32bit one.
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "art_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
}
art_cc_defaults {
name: "libart-compiler-defaults",
defaults: ["art_defaults"],
host_supported: true,
srcs: [
"compiled_method.cc",
"debug/elf_debug_writer.cc",
"dex/inline_method_analyser.cc",
"dex/verification_results.cc",
"driver/compiled_method_storage.cc",
"driver/compiler_options.cc",
"driver/dex_compilation_unit.cc",
"jit/jit_compiler.cc",
"jit/jit_logger.cc",
"jni/quick/calling_convention.cc",
"jni/quick/jni_compiler.cc",
"optimizing/block_builder.cc",
"optimizing/block_namer.cc",
"optimizing/bounds_check_elimination.cc",
"optimizing/builder.cc",
"optimizing/cha_guard_optimization.cc",
"optimizing/code_generator.cc",
"optimizing/code_generator_utils.cc",
"optimizing/code_sinking.cc",
"optimizing/constant_folding.cc",
"optimizing/constructor_fence_redundancy_elimination.cc",
"optimizing/data_type.cc",
"optimizing/dead_code_elimination.cc",
"optimizing/escape.cc",
"optimizing/execution_subgraph.cc",
"optimizing/graph_checker.cc",
"optimizing/graph_visualizer.cc",
"optimizing/gvn.cc",
"optimizing/induction_var_analysis.cc",
"optimizing/induction_var_range.cc",
"optimizing/inliner.cc",
"optimizing/instruction_builder.cc",
"optimizing/instruction_simplifier.cc",
"optimizing/intrinsic_objects.cc",
"optimizing/intrinsics.cc",
"optimizing/licm.cc",
"optimizing/linear_order.cc",
"optimizing/load_store_analysis.cc",
"optimizing/load_store_elimination.cc",
"optimizing/locations.cc",
"optimizing/loop_analysis.cc",
"optimizing/loop_optimization.cc",
"optimizing/nodes.cc",
"optimizing/optimization.cc",
"optimizing/optimizing_compiler.cc",
"optimizing/parallel_move_resolver.cc",
"optimizing/prepare_for_register_allocation.cc",
"optimizing/reference_type_propagation.cc",
"optimizing/register_allocation_resolver.cc",
"optimizing/register_allocator.cc",
"optimizing/register_allocator_graph_color.cc",
"optimizing/register_allocator_linear_scan.cc",
"optimizing/select_generator.cc",
"optimizing/scheduler.cc",
"optimizing/sharpening.cc",
"optimizing/side_effects_analysis.cc",
"optimizing/ssa_builder.cc",
"optimizing/ssa_liveness_analysis.cc",
"optimizing/ssa_phi_elimination.cc",
"optimizing/stack_map_stream.cc",
"optimizing/superblock_cloner.cc",
"trampolines/trampoline_compiler.cc",
"utils/assembler.cc",
"utils/jni_macro_assembler.cc",
"utils/swap_space.cc",
"compiler.cc",
],
codegen: {
arm: {
srcs: [
"jni/quick/arm/calling_convention_arm.cc",
"optimizing/code_generator_arm_vixl.cc",
"optimizing/code_generator_vector_arm_vixl.cc",
"optimizing/critical_native_abi_fixup_arm.cc",
"optimizing/instruction_simplifier_arm.cc",
"optimizing/instruction_simplifier_shared.cc",
"optimizing/intrinsics_arm_vixl.cc",
"optimizing/nodes_shared.cc",
"optimizing/scheduler_arm.cc",
"utils/arm/assembler_arm_vixl.cc",
"utils/arm/constants_arm.cc",
"utils/arm/jni_macro_assembler_arm_vixl.cc",
"utils/arm/managed_register_arm.cc",
],
},
arm64: {
srcs: [
"jni/quick/arm64/calling_convention_arm64.cc",
"optimizing/code_generator_arm64.cc",
"optimizing/code_generator_vector_arm64_neon.cc",
"optimizing/code_generator_vector_arm64_sve.cc",
"optimizing/scheduler_arm64.cc",
"optimizing/instruction_simplifier_arm64.cc",
"optimizing/intrinsics_arm64.cc",
"utils/arm64/assembler_arm64.cc",
"utils/arm64/jni_macro_assembler_arm64.cc",
"utils/arm64/managed_register_arm64.cc",
],
},
x86: {
srcs: [
"jni/quick/x86/calling_convention_x86.cc",
"optimizing/code_generator_x86.cc",
"optimizing/code_generator_vector_x86.cc",
"optimizing/intrinsics_x86.cc",
"optimizing/instruction_simplifier_x86_shared.cc",
"optimizing/instruction_simplifier_x86.cc",
"optimizing/pc_relative_fixups_x86.cc",
"optimizing/x86_memory_gen.cc",
"utils/x86/assembler_x86.cc",
"utils/x86/jni_macro_assembler_x86.cc",
"utils/x86/managed_register_x86.cc",
],
},
x86_64: {
srcs: [
"jni/quick/x86_64/calling_convention_x86_64.cc",
"optimizing/intrinsics_x86_64.cc",
"optimizing/instruction_simplifier_x86_64.cc",
"optimizing/code_generator_x86_64.cc",
"optimizing/code_generator_vector_x86_64.cc",
"utils/x86_64/assembler_x86_64.cc",
"utils/x86_64/jni_macro_assembler_x86_64.cc",
"utils/x86_64/managed_register_x86_64.cc",
],
},
},
static: {
cflags: ["-DART_STATIC_LIBART_COMPILER"],
},
generated_sources: ["art_compiler_operator_srcs"],
shared_libs: [
"libbase",
"liblzma", // libelffile(d) dependency; must be repeated here since it's a static lib.
],
header_libs: [
"art_cmdlineparser_headers", // For compiler_options.
"art_disassembler_headers",
"libnativehelper_header_only",
],
export_include_dirs: ["."],
}
cc_defaults {
name: "libart-compiler_static_base_defaults",
whole_static_libs: [
"libbase",
],
}
gensrcs {
name: "art_compiler_operator_srcs",
cmd: "$(location generate_operator_out) art/compiler $(in) > $(out)",
tools: ["generate_operator_out"],
srcs: [
"linker/linker_patch.h",
"optimizing/locations.h",
"optimizing/nodes.h",
"optimizing/optimizing_compiler_stats.h",
"utils/arm/constants_arm.h",
],
output_extension: "operator_out.cc",
}
art_cc_library {
name: "libart-compiler",
defaults: [
"libart-compiler-defaults",
"dex2oat-pgo-defaults",
"art_hugepage_defaults",
],
codegen: {
arm: {
// VIXL assembly support for ARM targets.
static_libs: [
"libvixl",
],
},
arm64: {
// VIXL assembly support for ARM64 targets.
static_libs: [
"libvixl",
],
},
},
shared_libs: [
"libart",
"libartbase",
"libartpalette",
"libprofile",
"libdexfile",
],
whole_static_libs: ["libelffile"],
runtime_libs: [
// `art::HGraphVisualizerDisassembler::HGraphVisualizerDisassembler` may dynamically load
// `libart-disassembler.so`.
"libart-disassembler",
],
target: {
android: {
lto: {
thin: true,
},
},
},
apex_available: [
"com.android.art",
"com.android.art.debug",
],
}
cc_defaults {
name: "libart-compiler_static_defaults",
defaults: [
"libart-compiler_static_base_defaults",
"libart-disassembler_static_defaults",
"libart_static_defaults",
"libartbase_static_defaults",
"libdexfile_static_defaults",
"libprofile_static_defaults",
],
whole_static_libs: ["libart-compiler"],
}
art_cc_library {
name: "libartd-compiler",
defaults: [
"art_debug_defaults",
"libart-compiler-defaults",
],
codegen: {
arm: {
// VIXL assembly support for ARM targets.
static_libs: [
"libvixld",
],
// Export vixl headers as they are included in this library's headers used by tests.
export_static_lib_headers: [
"libvixld",
],
},
arm64: {
// VIXL assembly support for ARM64 targets.
static_libs: [
"libvixld",
],
// Export vixl headers as they are included in this library's headers used by tests.
export_static_lib_headers: [
"libvixld",
],
},
},
shared_libs: [
"libartbased",
"libartd",
"libartpalette",
"libprofiled",
"libdexfiled",
],
whole_static_libs: ["libelffiled"],
runtime_libs: [
// `art::HGraphVisualizerDisassembler::HGraphVisualizerDisassembler` may dynamically load
// `libartd-disassembler.so`.
"libartd-disassembler",
],
apex_available: [
"com.android.art.debug",
// TODO(b/183882457): This lib doesn't go into com.android.art, but
// apex_available lists need to be the same for internal libs to avoid
// stubs, and this depends on libdexfiled.
"com.android.art",
],
}
cc_defaults {
name: "libartd-compiler_static_defaults",
defaults: [
"libart-compiler_static_base_defaults",
"libartbased_static_defaults",
"libartd-disassembler_static_defaults",
"libartd_static_defaults",
"libdexfiled_static_defaults",
"libprofiled_static_defaults",
],
whole_static_libs: ["libartd-compiler"],
}
// Properties common to `libart-compiler-gtest` and `libartd-compiler-gtest`.
art_cc_defaults {
name: "libart-compiler-gtest-common",
srcs: [
"common_compiler_test.cc",
],
shared_libs: [
"libbase",
],
}
art_cc_library {
name: "libart-compiler-gtest",
defaults: [
"libart-gtest-defaults",
"libart-compiler-gtest-common",
],
shared_libs: [
"libart-compiler",
"libart-disassembler",
"libartbase-art-gtest",
"libart-runtime-gtest",
],
}
art_cc_library {
name: "libartd-compiler-gtest",
defaults: [
"libartd-gtest-defaults",
"libart-compiler-gtest-common",
],
shared_libs: [
"libartd-compiler",
"libartd-disassembler",
"libartbased-art-gtest",
"libartd-runtime-gtest",
],
}
art_cc_defaults {
name: "art_compiler_tests_defaults",
data: [
":art-gtest-jars-ExceptionHandle",
":art-gtest-jars-Interfaces",
":art-gtest-jars-MyClassNatives",
],
tidy_timeout_srcs: [
"jni/jni_compiler_test.cc",
"optimizing/codegen_test.cc",
"optimizing/constant_folding_test.cc",
"optimizing/execution_subgraph_test.cc",
"optimizing/induction_var_range_test.cc",
"optimizing/load_store_elimination_test.cc",
"optimizing/ssa_test.cc",
],
srcs: [
"debug/dwarf/dwarf_test.cc",
"debug/src_map_elem_test.cc",
"driver/compiled_method_storage_test.cc",
"exception_test.cc",
"jni/jni_compiler_test.cc",
"linker/linker_patch_test.cc",
"linker/output_stream_test.cc",
"optimizing/bounds_check_elimination_test.cc",
"optimizing/constant_folding_test.cc",
"optimizing/data_type_test.cc",
"optimizing/dead_code_elimination_test.cc",
"optimizing/dominator_test.cc",
"optimizing/find_loops_test.cc",
"optimizing/graph_checker_test.cc",
"optimizing/graph_test.cc",
"optimizing/gvn_test.cc",
"optimizing/induction_var_analysis_test.cc",
"optimizing/induction_var_range_test.cc",
"optimizing/licm_test.cc",
"optimizing/linearize_test.cc",
"optimizing/live_interval_test.cc",
"optimizing/live_ranges_test.cc",
"optimizing/liveness_test.cc",
"optimizing/loop_optimization_test.cc",
"optimizing/nodes_test.cc",
"optimizing/nodes_vector_test.cc",
"optimizing/parallel_move_test.cc",
"optimizing/pretty_printer_test.cc",
"optimizing/reference_type_propagation_test.cc",
"optimizing/select_generator_test.cc",
"optimizing/side_effects_test.cc",
"optimizing/ssa_liveness_analysis_test.cc",
"optimizing/ssa_test.cc",
"optimizing/stack_map_test.cc",
"optimizing/superblock_cloner_test.cc",
"optimizing/suspend_check_test.cc",
"utils/atomic_dex_ref_map_test.cc",
"utils/dedupe_set_test.cc",
"utils/swap_space_test.cc",
"jni/jni_cfi_test.cc",
"optimizing/codegen_test.cc",
"optimizing/execution_subgraph_test.cc",
"optimizing/instruction_simplifier_test.cc",
"optimizing/load_store_analysis_test.cc",
"optimizing/load_store_elimination_test.cc",
"optimizing/optimizing_cfi_test.cc",
"optimizing/scheduler_test.cc",
],
codegen: {
arm: {
srcs: [
"utils/arm/managed_register_arm_test.cc",
],
},
arm64: {
srcs: [
"utils/arm64/managed_register_arm64_test.cc",
],
},
x86: {
srcs: [
"utils/x86/managed_register_x86_test.cc",
// This test is testing architecture-independent functionality,
// but happens to use x86 codegen as part of the test.
"optimizing/register_allocator_test.cc",
],
},
x86_64: {
srcs: [
// Is this test a bit-rotten copy of the x86 test? b/77951326
// "utils/x86_64/managed_register_x86_64_test.cc",
],
},
},
header_libs: [
"libart_simulator_headers",
"libnativehelper_header_only",
],
shared_libs: [
"libbacktrace",
"libnativeloader",
],
target: {
host: {
shared_libs: [
"libartd-simulator",
],
},
},
}
// Version of ART gtest `art_compiler_tests` bundled with the ART APEX on target.
// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
art_cc_test {
name: "art_compiler_tests",
defaults: [
"art_gtest_defaults",
"art_compiler_tests_defaults",
],
shared_libs: [
"libprofiled",
"libartd-compiler",
"libartd-simulator-container",
],
static_libs: [
"libvixld",
],
}
// Standalone version of ART gtest `art_compiler_tests`, not bundled with the ART APEX on target.
art_cc_test {
name: "art_standalone_compiler_tests",
defaults: [
"art_standalone_gtest_defaults",
"art_compiler_tests_defaults",
],
data: [":generate-boot-image"],
shared_libs: [
"libprofile",
"libart-compiler",
],
static_libs: [
// For now, link `libart-simulator-container` statically for simplicity,
// to save the added complexity to package it in test suites (along with
// other test artifacts) and install it on device during tests.
// TODO(b/192070541): Consider linking `libart-simulator-container`
// dynamically.
"libart-simulator-container",
"libvixl",
],
test_config: "art_standalone_compiler_tests.xml",
}
art_cc_test {
name: "art_compiler_host_tests",
device_supported: false,
defaults: [
"art_gtest_defaults",
],
tidy_timeout_srcs: [
"utils/x86/assembler_x86_test.cc",
"utils/x86_64/assembler_x86_64_test.cc",
],
codegen: {
arm: {
srcs: [
"utils/assembler_thumb_test.cc",
],
},
x86: {
srcs: [
"utils/x86/assembler_x86_test.cc",
],
},
x86_64: {
srcs: [
"utils/x86_64/assembler_x86_64_test.cc",
],
},
},
shared_libs: [
"libartd-compiler",
],
static_libs: [
"libvixld",
],
}

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<configuration description="Runs art_standalone_compiler_tests.">
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_compiler_tests->/data/local/tmp/art_standalone_compiler_tests/art_standalone_compiler_tests" />
<option name="append-bitness" value="true" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art-gtest-jars-ExceptionHandle.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-ExceptionHandle.jar" />
<option name="push" value="art-gtest-jars-Interfaces.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-Interfaces.jar" />
<option name="push" value="art-gtest-jars-MyClassNatives.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-MyClassNatives.jar" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="append-bitness" value="true" />
<option name="push-file" key="generate-boot-image" value="/data/local/tmp/art_standalone_compiler_tests/generate-boot-image" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="throw-if-cmd-fail" value="true" />
<option name="run-command" value="mkdir -p /data/local/tmp/art_standalone_compiler_tests/art_boot_images" />
<option name="run-command" value="/data/local/tmp/art_standalone_compiler_tests/generate-boot-image --output-dir=/data/local/tmp/art_standalone_compiler_tests/art_boot_images" />
<option name="teardown-command" value="rm -rf /data/local/tmp/art_standalone_compiler_tests/art_boot_images" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp/art_standalone_compiler_tests" />
<option name="module-name" value="art_standalone_compiler_tests" />
<option name="ld-library-path-32" value="/apex/com.android.art/lib" />
<option name="ld-library-path-64" value="/apex/com.android.art/lib64" />
</test>
<!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
one of the Mainline modules below is present on the device used for testing. -->
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
<!-- ART Mainline Module (internal version). -->
<option name="mainline-module-package-name" value="com.google.android.art" />
<!-- ART Mainline Module (external (AOSP) version). -->
<option name="mainline-module-package-name" value="com.android.art" />
</object>
<!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
</configuration>

156
compiler/cfi_test.h Normal file
View File

@ -0,0 +1,156 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef ART_COMPILER_CFI_TEST_H_
#define ART_COMPILER_CFI_TEST_H_
#include <memory>
#include <sstream>
#include <vector>
#include "arch/instruction_set.h"
#include "base/enums.h"
#include "debug/dwarf/dwarf_test.h"
#include "disassembler.h"
#include "dwarf/dwarf_constants.h"
#include "dwarf/headers.h"
#include "gtest/gtest.h"
#include "thread.h"
namespace art {
class CFITest : public dwarf::DwarfTest {
public:
void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str,
ArrayRef<const uint8_t> actual_asm,
ArrayRef<const uint8_t> actual_cfi) {
std::vector<std::string> lines;
// Print the raw bytes.
fprintf(f, "static constexpr uint8_t expected_asm_%s[] = {", isa_str);
HexDump(f, actual_asm);
fprintf(f, "\n};\n");
fprintf(f, "static constexpr uint8_t expected_cfi_%s[] = {", isa_str);
HexDump(f, actual_cfi);
fprintf(f, "\n};\n");
// Pretty-print CFI opcodes.
constexpr bool is64bit = false;
dwarf::DebugFrameOpCodeWriter<> initial_opcodes;
dwarf::WriteCIE(is64bit, dwarf::Reg(8), initial_opcodes, &debug_frame_data_);
std::vector<uintptr_t> debug_frame_patches;
dwarf::WriteFDE(is64bit,
/* cie_pointer= */ 0,
/* code_address= */ 0,
actual_asm.size(),
actual_cfi,
&debug_frame_data_);
ReformatCfi(Objdump(false, "-W"), &lines);
// Pretty-print assembly.
const uint8_t* asm_base = actual_asm.data();
const uint8_t* asm_end = asm_base + actual_asm.size();
auto* opts = new DisassemblerOptions(false,
asm_base,
asm_end,
true,
is64bit
? &Thread::DumpThreadOffset<PointerSize::k64>
: &Thread::DumpThreadOffset<PointerSize::k32>);
std::unique_ptr<Disassembler> disasm(Disassembler::Create(isa, opts));
std::stringstream stream;
const uint8_t* base = actual_asm.data() + (isa == InstructionSet::kThumb2 ? 1 : 0);
disasm->Dump(stream, base, base + actual_asm.size());
ReformatAsm(&stream, &lines);
// Print CFI and assembly interleaved.
std::stable_sort(lines.begin(), lines.end(), CompareByAddress);
for (const std::string& line : lines) {
fprintf(f, "// %s\n", line.c_str());
}
fprintf(f, "\n");
}
private:
// Helper - get offset just past the end of given string.
static size_t FindEndOf(const std::string& str, const char* substr) {
size_t pos = str.find(substr);
CHECK_NE(std::string::npos, pos);
return pos + strlen(substr);
}
// Spit to lines and remove raw instruction bytes.
static void ReformatAsm(std::stringstream* stream,
std::vector<std::string>* output) {
std::string line;
while (std::getline(*stream, line)) {
line = line.substr(0, FindEndOf(line, ": ")) +
line.substr(FindEndOf(line, "\t"));
size_t pos;
while ((pos = line.find(" ")) != std::string::npos) {
line = line.replace(pos, 2, " ");
}
while (!line.empty() && line.back() == ' ') {
line.pop_back();
}
output->push_back(line);
}
}
// Find interesting parts of objdump output and prefix the lines with address.
static void ReformatCfi(const std::vector<std::string>& lines,
std::vector<std::string>* output) {
std::string address;
for (const std::string& line : lines) {
if (line.find("DW_CFA_nop") != std::string::npos) {
// Ignore.
} else if (line.find("DW_CFA_advance_loc") != std::string::npos) {
// The last 8 characters are the address.
address = "0x" + line.substr(line.size() - 8);
} else if (line.find("DW_CFA_") != std::string::npos) {
std::string new_line(line);
// "bad register" warning is caused by always using host (x86) objdump.
const char* bad_reg = "bad register: ";
size_t pos;
if ((pos = new_line.find(bad_reg)) != std::string::npos) {
new_line = new_line.replace(pos, strlen(bad_reg), "");
}
// Remove register names in parentheses since they have x86 names.
if ((pos = new_line.find(" (")) != std::string::npos) {
new_line = new_line.replace(pos, FindEndOf(new_line, ")") - pos, "");
}
// Use the .cfi_ prefix.
new_line = ".cfi_" + new_line.substr(FindEndOf(new_line, "DW_CFA_"));
output->push_back(address + ": " + new_line);
}
}
}
// Compare strings by the address prefix.
static bool CompareByAddress(const std::string& lhs, const std::string& rhs) {
EXPECT_EQ(lhs[10], ':');
EXPECT_EQ(rhs[10], ':');
return strncmp(lhs.c_str(), rhs.c_str(), 10) < 0;
}
// Pretty-print byte array. 12 bytes per line.
static void HexDump(FILE* f, ArrayRef<const uint8_t> data) {
for (size_t i = 0; i < data.size(); i++) {
fprintf(f, i % 12 == 0 ? "\n " : " "); // Whitespace.
fprintf(f, "0x%02X,", data[i]);
}
}
};
} // namespace art
#endif // ART_COMPILER_CFI_TEST_H_

View File

@ -0,0 +1,308 @@
/*
* 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.
*/
#include "common_compiler_test.h"
#include <android-base/unique_fd.h>
#include <type_traits>
#include "arch/instruction_set_features.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/callee_save_type.h"
#include "base/casts.h"
#include "base/enums.h"
#include "base/memfd.h"
#include "base/utils.h"
#include "class_linker.h"
#include "compiled_method-inl.h"
#include "dex/descriptors_names.h"
#include "dex/verification_results.h"
#include "driver/compiled_method_storage.h"
#include "driver/compiler_options.h"
#include "jni/java_vm_ext.h"
#include "interpreter/interpreter.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
#include "mirror/object-inl.h"
#include "oat_quick_method_header.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "utils/atomic_dex_ref_map-inl.h"
namespace art {
class CommonCompilerTestImpl::CodeAndMetadata {
public:
CodeAndMetadata(CodeAndMetadata&& other) = default;
CodeAndMetadata(ArrayRef<const uint8_t> code,
ArrayRef<const uint8_t> vmap_table,
InstructionSet instruction_set) {
const uint32_t code_size = code.size();
CHECK_NE(code_size, 0u);
const uint32_t vmap_table_offset = vmap_table.empty() ? 0u
: sizeof(OatQuickMethodHeader) + vmap_table.size();
OatQuickMethodHeader method_header(vmap_table_offset);
const size_t code_alignment = GetInstructionSetAlignment(instruction_set);
DCHECK_ALIGNED_PARAM(kPageSize, code_alignment);
code_offset_ = RoundUp(vmap_table.size() + sizeof(method_header), code_alignment);
const uint32_t capacity = RoundUp(code_offset_ + code_size, kPageSize);
// Create a memfd handle with sufficient capacity.
android::base::unique_fd mem_fd(art::memfd_create_compat("test code", /*flags=*/ 0));
CHECK_GE(mem_fd.get(), 0);
int err = ftruncate(mem_fd, capacity);
CHECK_EQ(err, 0);
// Map the memfd contents for read/write.
std::string error_msg;
rw_map_ = MemMap::MapFile(capacity,
PROT_READ | PROT_WRITE,
MAP_SHARED,
mem_fd,
/*start=*/ 0,
/*low_4gb=*/ false,
/*filename=*/ "test code",
&error_msg);
CHECK(rw_map_.IsValid()) << error_msg;
// Store data.
uint8_t* code_addr = rw_map_.Begin() + code_offset_;
CHECK_ALIGNED_PARAM(code_addr, code_alignment);
CHECK_LE(vmap_table_offset, code_offset_);
memcpy(code_addr - vmap_table_offset, vmap_table.data(), vmap_table.size());
static_assert(std::is_trivially_copyable<OatQuickMethodHeader>::value, "Cannot use memcpy");
CHECK_LE(sizeof(method_header), code_offset_);
memcpy(code_addr - sizeof(method_header), &method_header, sizeof(method_header));
CHECK_LE(code_size, static_cast<size_t>(rw_map_.End() - code_addr));
memcpy(code_addr, code.data(), code_size);
// Sync data.
bool success = rw_map_.Sync();
CHECK(success);
success = FlushCpuCaches(rw_map_.Begin(), rw_map_.End());
CHECK(success);
// Map the data as read/executable.
rx_map_ = MemMap::MapFile(capacity,
PROT_READ | PROT_EXEC,
MAP_SHARED,
mem_fd,
/*start=*/ 0,
/*low_4gb=*/ false,
/*filename=*/ "test code",
&error_msg);
CHECK(rx_map_.IsValid()) << error_msg;
}
const void* GetCodePointer() const {
DCHECK(rx_map_.IsValid());
DCHECK_LE(code_offset_, rx_map_.Size());
return rx_map_.Begin() + code_offset_;
}
private:
MemMap rw_map_;
MemMap rx_map_;
uint32_t code_offset_;
DISALLOW_COPY_AND_ASSIGN(CodeAndMetadata);
};
std::unique_ptr<CompilerOptions> CommonCompilerTestImpl::CreateCompilerOptions(
InstructionSet instruction_set, const std::string& variant) {
std::unique_ptr<CompilerOptions> compiler_options = std::make_unique<CompilerOptions>();
compiler_options->instruction_set_ = instruction_set;
std::string error_msg;
compiler_options->instruction_set_features_ =
InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg);
CHECK(compiler_options->instruction_set_features_ != nullptr) << error_msg;
return compiler_options;
}
CommonCompilerTestImpl::CommonCompilerTestImpl() {}
CommonCompilerTestImpl::~CommonCompilerTestImpl() {}
const void* CommonCompilerTestImpl::MakeExecutable(ArrayRef<const uint8_t> code,
ArrayRef<const uint8_t> vmap_table,
InstructionSet instruction_set) {
CHECK_NE(code.size(), 0u);
code_and_metadata_.emplace_back(code, vmap_table, instruction_set);
return code_and_metadata_.back().GetCodePointer();
}
void CommonCompilerTestImpl::MakeExecutable(ArtMethod* method,
const CompiledMethod* compiled_method) {
CHECK(method != nullptr);
const void* method_code = nullptr;
// If the code size is 0 it means the method was skipped due to profile guided compilation.
if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0u) {
const void* code_ptr = MakeExecutable(compiled_method->GetQuickCode(),
compiled_method->GetVmapTable(),
compiled_method->GetInstructionSet());
method_code =
CompiledMethod::CodePointer(code_ptr, compiled_method->GetInstructionSet());
LOG(INFO) << "MakeExecutable " << method->PrettyMethod() << " code=" << method_code;
}
Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(
method, /*aot_code=*/ method_code);
}
void CommonCompilerTestImpl::SetUp() {
{
ScopedObjectAccess soa(Thread::Current());
Runtime* runtime = GetRuntime();
runtime->SetInstructionSet(instruction_set_);
for (uint32_t i = 0; i < static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType); ++i) {
CalleeSaveType type = CalleeSaveType(i);
if (!runtime->HasCalleeSaveMethod(type)) {
runtime->SetCalleeSaveMethod(runtime->CreateCalleeSaveMethod(), type);
}
}
}
}
void CommonCompilerTestImpl::ApplyInstructionSet() {
// Copy local instruction_set_ and instruction_set_features_ to *compiler_options_;
CHECK(instruction_set_features_ != nullptr);
if (instruction_set_ == InstructionSet::kThumb2) {
CHECK_EQ(InstructionSet::kArm, instruction_set_features_->GetInstructionSet());
} else {
CHECK_EQ(instruction_set_, instruction_set_features_->GetInstructionSet());
}
compiler_options_->instruction_set_ = instruction_set_;
compiler_options_->instruction_set_features_ =
InstructionSetFeatures::FromBitmap(instruction_set_, instruction_set_features_->AsBitmap());
CHECK(compiler_options_->instruction_set_features_->Equals(instruction_set_features_.get()));
}
void CommonCompilerTestImpl::OverrideInstructionSetFeatures(InstructionSet instruction_set,
const std::string& variant) {
instruction_set_ = instruction_set;
std::string error_msg;
instruction_set_features_ =
InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg);
CHECK(instruction_set_features_ != nullptr) << error_msg;
if (compiler_options_ != nullptr) {
ApplyInstructionSet();
}
}
void CommonCompilerTestImpl::SetUpRuntimeOptionsImpl() {
compiler_options_.reset(new CompilerOptions);
verification_results_.reset(new VerificationResults());
ApplyInstructionSet();
}
Compiler::Kind CommonCompilerTestImpl::GetCompilerKind() const {
return compiler_kind_;
}
void CommonCompilerTestImpl::SetCompilerKind(Compiler::Kind compiler_kind) {
compiler_kind_ = compiler_kind;
}
void CommonCompilerTestImpl::TearDown() {
code_and_metadata_.clear();
verification_results_.reset();
compiler_options_.reset();
}
void CommonCompilerTestImpl::CompileMethod(ArtMethod* method) {
CHECK(method != nullptr);
TimingLogger timings("CommonCompilerTestImpl::CompileMethod", false, false);
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
CompiledMethodStorage storage(/*swap_fd=*/ -1);
CompiledMethod* compiled_method = nullptr;
{
DCHECK(!Runtime::Current()->IsStarted());
Thread* self = Thread::Current();
StackHandleScope<2> hs(self);
std::unique_ptr<Compiler> compiler(
Compiler::Create(*compiler_options_, &storage, compiler_kind_));
const DexFile& dex_file = *method->GetDexFile();
Handle<mirror::DexCache> dex_cache =
hs.NewHandle(GetClassLinker()->FindDexCache(self, dex_file));
Handle<mirror::ClassLoader> class_loader = hs.NewHandle(method->GetClassLoader());
compiler_options_->verification_results_ = verification_results_.get();
if (method->IsNative()) {
compiled_method = compiler->JniCompile(method->GetAccessFlags(),
method->GetDexMethodIndex(),
dex_file,
dex_cache);
} else {
compiled_method = compiler->Compile(method->GetCodeItem(),
method->GetAccessFlags(),
method->GetInvokeType(),
method->GetClassDefIndex(),
method->GetDexMethodIndex(),
class_loader,
dex_file,
dex_cache);
}
compiler_options_->verification_results_ = nullptr;
}
CHECK(method != nullptr);
{
TimingLogger::ScopedTiming t2("MakeExecutable", &timings);
MakeExecutable(method, compiled_method);
}
CompiledMethod::ReleaseSwapAllocatedCompiledMethod(&storage, compiled_method);
}
void CommonCompilerTestImpl::CompileDirectMethod(Handle<mirror::ClassLoader> class_loader,
const char* class_name,
const char* method_name,
const char* signature) {
std::string class_descriptor(DotToDescriptor(class_name));
Thread* self = Thread::Current();
ClassLinker* class_linker = GetClassLinker();
ObjPtr<mirror::Class> klass =
class_linker->FindClass(self, class_descriptor.c_str(), class_loader);
CHECK(klass != nullptr) << "Class not found " << class_name;
auto pointer_size = class_linker->GetImagePointerSize();
ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size);
CHECK(method != nullptr && method->IsDirect()) << "Direct method not found: "
<< class_name << "." << method_name << signature;
CompileMethod(method);
}
void CommonCompilerTestImpl::CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader,
const char* class_name,
const char* method_name,
const char* signature) {
std::string class_descriptor(DotToDescriptor(class_name));
Thread* self = Thread::Current();
ClassLinker* class_linker = GetClassLinker();
ObjPtr<mirror::Class> klass =
class_linker->FindClass(self, class_descriptor.c_str(), class_loader);
CHECK(klass != nullptr) << "Class not found " << class_name;
auto pointer_size = class_linker->GetImagePointerSize();
ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size);
CHECK(method != nullptr && !method->IsDirect()) << "Virtual method not found: "
<< class_name << "." << method_name << signature;
CompileMethod(method);
}
void CommonCompilerTestImpl::ClearBootImageOption() {
compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
}
} // namespace art

View File

@ -0,0 +1,143 @@
/*
* 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.
*/
#ifndef ART_COMPILER_COMMON_COMPILER_TEST_H_
#define ART_COMPILER_COMMON_COMPILER_TEST_H_
#include <list>
#include <vector>
#include <jni.h>
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
#include "common_runtime_test.h"
#include "compiler.h"
#include "oat_file.h"
namespace art {
namespace mirror {
class ClassLoader;
} // namespace mirror
class CompiledMethod;
class CompilerOptions;
class CumulativeLogger;
class DexFile;
class TimingLogger;
class VerificationResults;
template<class T> class Handle;
class CommonCompilerTestImpl {
public:
static std::unique_ptr<CompilerOptions> CreateCompilerOptions(InstructionSet instruction_set,
const std::string& variant);
CommonCompilerTestImpl();
virtual ~CommonCompilerTestImpl();
// Create an executable copy of the code with given metadata.
const void* MakeExecutable(ArrayRef<const uint8_t> code,
ArrayRef<const uint8_t> vmap_table,
InstructionSet instruction_set);
void MakeExecutable(ArtMethod* method, const CompiledMethod* compiled_method)
REQUIRES_SHARED(Locks::mutator_lock_);
protected:
void SetUp();
void SetUpRuntimeOptionsImpl();
Compiler::Kind GetCompilerKind() const;
void SetCompilerKind(Compiler::Kind compiler_kind);
virtual CompilerFilter::Filter GetCompilerFilter() const {
return CompilerFilter::kDefaultCompilerFilter;
}
void TearDown();
void CompileMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
void CompileDirectMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name,
const char* method_name, const char* signature)
REQUIRES_SHARED(Locks::mutator_lock_);
void CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name,
const char* method_name, const char* signature)
REQUIRES_SHARED(Locks::mutator_lock_);
void ApplyInstructionSet();
void OverrideInstructionSetFeatures(InstructionSet instruction_set, const std::string& variant);
void ClearBootImageOption();
Compiler::Kind compiler_kind_ = Compiler::kOptimizing;
InstructionSet instruction_set_ =
(kRuntimeISA == InstructionSet::kArm) ? InstructionSet::kThumb2 : kRuntimeISA;
// Take the default set of instruction features from the build.
std::unique_ptr<const InstructionSetFeatures> instruction_set_features_
= InstructionSetFeatures::FromCppDefines();
std::unique_ptr<CompilerOptions> compiler_options_;
std::unique_ptr<VerificationResults> verification_results_;
protected:
virtual ClassLinker* GetClassLinker() = 0;
virtual Runtime* GetRuntime() = 0;
private:
class CodeAndMetadata;
std::vector<CodeAndMetadata> code_and_metadata_;
};
template <typename RuntimeBase>
class CommonCompilerTestBase : public CommonCompilerTestImpl, public RuntimeBase {
public:
void SetUp() override {
RuntimeBase::SetUp();
CommonCompilerTestImpl::SetUp();
}
void SetUpRuntimeOptions(RuntimeOptions* options) override {
RuntimeBase::SetUpRuntimeOptions(options);
CommonCompilerTestImpl::SetUpRuntimeOptionsImpl();
}
void TearDown() override {
CommonCompilerTestImpl::TearDown();
RuntimeBase::TearDown();
}
protected:
ClassLinker* GetClassLinker() override {
return RuntimeBase::class_linker_;
}
Runtime* GetRuntime() override {
return RuntimeBase::runtime_.get();
}
};
class CommonCompilerTest : public CommonCompilerTestBase<CommonRuntimeTest> {};
template <typename Param>
class CommonCompilerTestWithParam
: public CommonCompilerTestBase<CommonRuntimeTestWithParam<Param>> {};
} // namespace art
#endif // ART_COMPILER_COMMON_COMPILER_TEST_H_

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2017 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.
*/
#ifndef ART_COMPILER_COMPILED_METHOD_INL_H_
#define ART_COMPILER_COMPILED_METHOD_INL_H_
#include "compiled_method.h"
#include "base/array_ref.h"
#include "base/length_prefixed_array.h"
#include "linker/linker_patch.h"
namespace art {
inline ArrayRef<const uint8_t> CompiledCode::GetQuickCode() const {
return GetArray(quick_code_);
}
template <typename T>
inline ArrayRef<const T> CompiledCode::GetArray(const LengthPrefixedArray<T>* array) {
if (array == nullptr) {
return ArrayRef<const T>();
}
DCHECK_NE(array->size(), 0u);
return ArrayRef<const T>(&array->At(0), array->size());
}
inline ArrayRef<const uint8_t> CompiledMethod::GetVmapTable() const {
return GetArray(vmap_table_);
}
inline ArrayRef<const uint8_t> CompiledMethod::GetCFIInfo() const {
return GetArray(cfi_info_);
}
inline ArrayRef<const linker::LinkerPatch> CompiledMethod::GetPatches() const {
return GetArray(patches_);
}
} // namespace art
#endif // ART_COMPILER_COMPILED_METHOD_INL_H_

141
compiler/compiled_method.cc Normal file
View File

@ -0,0 +1,141 @@
/*
* 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.
*/
#include "compiled_method.h"
#include "driver/compiled_method_storage.h"
#include "utils/swap_space.h"
namespace art {
CompiledCode::CompiledCode(CompiledMethodStorage* storage,
InstructionSet instruction_set,
const ArrayRef<const uint8_t>& quick_code)
: storage_(storage),
quick_code_(storage->DeduplicateCode(quick_code)),
packed_fields_(InstructionSetField::Encode(instruction_set)) {
}
CompiledCode::~CompiledCode() {
GetStorage()->ReleaseCode(quick_code_);
}
bool CompiledCode::operator==(const CompiledCode& rhs) const {
if (quick_code_ != nullptr) {
if (rhs.quick_code_ == nullptr) {
return false;
} else if (quick_code_->size() != rhs.quick_code_->size()) {
return false;
} else {
return std::equal(quick_code_->begin(), quick_code_->end(), rhs.quick_code_->begin());
}
}
return (rhs.quick_code_ == nullptr);
}
size_t CompiledCode::AlignCode(size_t offset) const {
return AlignCode(offset, GetInstructionSet());
}
size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) {
return RoundUp(offset, GetInstructionSetAlignment(instruction_set));
}
size_t CompiledCode::CodeDelta() const {
return CodeDelta(GetInstructionSet());
}
size_t CompiledCode::CodeDelta(InstructionSet instruction_set) {
switch (instruction_set) {
case InstructionSet::kArm:
case InstructionSet::kArm64:
case InstructionSet::kX86:
case InstructionSet::kX86_64:
return 0;
case InstructionSet::kThumb2: {
// +1 to set the low-order bit so a BLX will switch to Thumb mode
return 1;
}
default:
LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
UNREACHABLE();
}
}
const void* CompiledCode::CodePointer(const void* code_pointer, InstructionSet instruction_set) {
switch (instruction_set) {
case InstructionSet::kArm:
case InstructionSet::kArm64:
case InstructionSet::kX86:
case InstructionSet::kX86_64:
return code_pointer;
case InstructionSet::kThumb2: {
uintptr_t address = reinterpret_cast<uintptr_t>(code_pointer);
// Set the low-order bit so a BLX will switch to Thumb mode
address |= 0x1;
return reinterpret_cast<const void*>(address);
}
default:
LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
UNREACHABLE();
}
}
CompiledMethod::CompiledMethod(CompiledMethodStorage* storage,
InstructionSet instruction_set,
const ArrayRef<const uint8_t>& quick_code,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& cfi_info,
const ArrayRef<const linker::LinkerPatch>& patches)
: CompiledCode(storage, instruction_set, quick_code),
vmap_table_(storage->DeduplicateVMapTable(vmap_table)),
cfi_info_(storage->DeduplicateCFIInfo(cfi_info)),
patches_(storage->DeduplicateLinkerPatches(patches)) {
}
CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
CompiledMethodStorage* storage,
InstructionSet instruction_set,
const ArrayRef<const uint8_t>& quick_code,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& cfi_info,
const ArrayRef<const linker::LinkerPatch>& patches) {
SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
CompiledMethod* ret = alloc.allocate(1);
alloc.construct(ret,
storage,
instruction_set,
quick_code,
vmap_table,
cfi_info, patches);
return ret;
}
void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage,
CompiledMethod* m) {
SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
alloc.destroy(m);
alloc.deallocate(m, 1);
}
CompiledMethod::~CompiledMethod() {
CompiledMethodStorage* storage = GetStorage();
storage->ReleaseLinkerPatches(patches_);
storage->ReleaseCFIInfo(cfi_info_);
storage->ReleaseVMapTable(vmap_table_);
}
} // namespace art

167
compiler/compiled_method.h Normal file
View File

@ -0,0 +1,167 @@
/*
* 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.
*/
#ifndef ART_COMPILER_COMPILED_METHOD_H_
#define ART_COMPILER_COMPILED_METHOD_H_
#include <memory>
#include <string>
#include <vector>
#include "arch/instruction_set.h"
#include "base/bit_field.h"
#include "base/bit_utils.h"
namespace art {
template <typename T> class ArrayRef;
class CompiledMethodStorage;
template<typename T> class LengthPrefixedArray;
namespace linker {
class LinkerPatch;
} // namespace linker
class CompiledCode {
public:
// For Quick to supply an code blob
CompiledCode(CompiledMethodStorage* storage,
InstructionSet instruction_set,
const ArrayRef<const uint8_t>& quick_code);
virtual ~CompiledCode();
InstructionSet GetInstructionSet() const {
return GetPackedField<InstructionSetField>();
}
ArrayRef<const uint8_t> GetQuickCode() const;
bool operator==(const CompiledCode& rhs) const;
// To align an offset from a page-aligned value to make it suitable
// for code storage. For example on ARM, to ensure that PC relative
// valu computations work out as expected.
size_t AlignCode(size_t offset) const;
static size_t AlignCode(size_t offset, InstructionSet instruction_set);
// returns the difference between the code address and a usable PC.
// mainly to cope with kThumb2 where the lower bit must be set.
size_t CodeDelta() const;
static size_t CodeDelta(InstructionSet instruction_set);
// Returns a pointer suitable for invoking the code at the argument
// code_pointer address. Mainly to cope with kThumb2 where the
// lower bit must be set to indicate Thumb mode.
static const void* CodePointer(const void* code_pointer, InstructionSet instruction_set);
protected:
static constexpr size_t kInstructionSetFieldSize =
MinimumBitsToStore(static_cast<size_t>(InstructionSet::kLast));
static constexpr size_t kNumberOfCompiledCodePackedBits = kInstructionSetFieldSize;
static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
template <typename T>
static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array);
CompiledMethodStorage* GetStorage() {
return storage_;
}
template <typename BitFieldType>
typename BitFieldType::value_type GetPackedField() const {
return BitFieldType::Decode(packed_fields_);
}
template <typename BitFieldType>
void SetPackedField(typename BitFieldType::value_type value) {
DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value)));
packed_fields_ = BitFieldType::Update(value, packed_fields_);
}
private:
using InstructionSetField = BitField<InstructionSet, 0u, kInstructionSetFieldSize>;
CompiledMethodStorage* const storage_;
// Used to store the compiled code.
const LengthPrefixedArray<uint8_t>* const quick_code_;
uint32_t packed_fields_;
};
class CompiledMethod final : public CompiledCode {
public:
// Constructs a CompiledMethod.
// Note: Consider using the static allocation methods below that will allocate the CompiledMethod
// in the swap space.
CompiledMethod(CompiledMethodStorage* storage,
InstructionSet instruction_set,
const ArrayRef<const uint8_t>& quick_code,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& cfi_info,
const ArrayRef<const linker::LinkerPatch>& patches);
virtual ~CompiledMethod();
static CompiledMethod* SwapAllocCompiledMethod(
CompiledMethodStorage* storage,
InstructionSet instruction_set,
const ArrayRef<const uint8_t>& quick_code,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& cfi_info,
const ArrayRef<const linker::LinkerPatch>& patches);
static void ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage, CompiledMethod* m);
bool IsIntrinsic() const {
return GetPackedField<IsIntrinsicField>();
}
// Marks the compiled method as being generated using an intrinsic codegen.
// Such methods have no relationships to their code items.
// This affects debug information generated at link time.
void MarkAsIntrinsic() {
DCHECK(!IsIntrinsic());
SetPackedField<IsIntrinsicField>(/* value= */ true);
}
ArrayRef<const uint8_t> GetVmapTable() const;
ArrayRef<const uint8_t> GetCFIInfo() const;
ArrayRef<const linker::LinkerPatch> GetPatches() const;
private:
static constexpr size_t kIsIntrinsicLsb = kNumberOfCompiledCodePackedBits;
static constexpr size_t kIsIntrinsicSize = 1u;
static constexpr size_t kNumberOfCompiledMethodPackedBits = kIsIntrinsicLsb + kIsIntrinsicSize;
static_assert(kNumberOfCompiledMethodPackedBits <= CompiledCode::kMaxNumberOfPackedBits,
"Too many packed fields.");
using IsIntrinsicField = BitField<bool, kIsIntrinsicLsb, kIsIntrinsicSize>;
// For quick code, holds code infos which contain stack maps, inline information, and etc.
const LengthPrefixedArray<uint8_t>* const vmap_table_;
// For quick code, a FDE entry for the debug_frame section.
const LengthPrefixedArray<uint8_t>* const cfi_info_;
// For quick code, linker patches needed by the method.
const LengthPrefixedArray<linker::LinkerPatch>* const patches_;
};
} // namespace art
#endif // ART_COMPILER_COMPILED_METHOD_H_

Some files were not shown because too many files have changed in this diff Show More