Merge "Disallow duplicated classes on boot classpath."
diff --git a/Android.mk b/Android.mk
index 1a5daff..e4cc5c0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -352,13 +352,18 @@
# Module with both release and debug variants, as well as
# additional tools.
TARGET_RUNTIME_APEX := com.android.runtime.debug
+ APEX_TEST_MODULE := art-check-debug-apex-gen-fakebin
else
# Release module (without debug variants nor tools).
TARGET_RUNTIME_APEX := com.android.runtime.release
+ APEX_TEST_MODULE := art-check-release-apex-gen-fakebin
endif
LOCAL_MODULE := com.android.runtime
LOCAL_REQUIRED_MODULES := $(TARGET_RUNTIME_APEX)
+ifneq ($(HOST_OS),darwin)
+ LOCAL_REQUIRED_MODULES += $(APEX_TEST_MODULE)
+endif
# Clear locally used variable.
art_target_include_debug_build :=
@@ -458,19 +463,6 @@
include $(BUILD_PHONY_PACKAGE)
endif
-# Create dummy hidden API lists which are normally generated by the framework
-# but which we do not have in the master-art manifest.
-# We need to execute this now to ensure Makefile rules depending on these files can
-# be constructed.
-define build-art-hiddenapi
-$(shell if [ ! -d frameworks/base ]; then \
- mkdir -p ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING; \
- touch ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-flags.csv; \
- fi;)
-endef
-
-$(eval $(call build-art-hiddenapi))
-
########################################################################
# "m build-art" for quick minimal build
.PHONY: build-art
@@ -483,6 +475,42 @@
build-art-target: $(TARGET_OUT_EXECUTABLES)/art $(ART_TARGET_DEPENDENCIES) $(TARGET_CORE_IMG_OUTS)
########################################################################
+# Workaround for not using symbolic links for linker and bionic libraries
+# in a minimal setup (eg buildbot or golem).
+########################################################################
+
+PRIVATE_BIONIC_FILES := \
+ bin/bootstrap/linker \
+ bin/bootstrap/linker64 \
+ lib/bootstrap/libc.so \
+ lib/bootstrap/libm.so \
+ lib/bootstrap/libdl.so \
+ lib64/bootstrap/libc.so \
+ lib64/bootstrap/libm.so \
+ lib64/bootstrap/libdl.so \
+
+PRIVATE_RUNTIME_DEPENDENCY_LIBS := \
+ lib/libnativebridge.so \
+ lib64/libnativebridge.so \
+ lib/libnativehelper.so \
+ lib64/libnativehelper.so \
+ lib/libdexfile_external.so \
+ lib64/libdexfile_external.so \
+ lib/libnativeloader.so \
+ lib64/libnativeloader.so \
+
+.PHONY: standalone-apex-files
+standalone-apex-files: libc.bootstrap libdl.bootstrap libm.bootstrap linker com.android.runtime.debug
+ for f in $(PRIVATE_BIONIC_FILES); do \
+ tf=$(TARGET_OUT)/$$f; \
+ if [ -f $$tf ]; then cp -f $$tf $$(echo $$tf | sed 's,bootstrap/,,'); fi; \
+ done
+ for f in $(PRIVATE_RUNTIME_DEPENDENCY_LIBS); do \
+ tf=$(TARGET_OUT)/../apex/com.android.runtime.debug/$$f; \
+ if [ -f $$tf ]; then cp -f $$tf $(TARGET_OUT)/$$f; fi; \
+ done
+
+########################################################################
# Phony target for only building what go/lem requires for pushing ART on /data.
.PHONY: build-art-target-golem
@@ -514,7 +542,8 @@
$(TARGET_CORE_IMG_OUT_BASE).art \
$(TARGET_CORE_IMG_OUT_BASE)-interpreter.art \
libc.bootstrap libdl.bootstrap libm.bootstrap \
- icu-data-art-test
+ icu-data-art-test \
+ 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
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e28ce2b..f621bcb 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -53,6 +53,12 @@
# 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)
+
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
index b6d6600..e1b5b62 100644
--- a/adbconnection/adbconnection.cc
+++ b/adbconnection/adbconnection.cc
@@ -28,7 +28,7 @@
#include "jni/java_vm_ext.h"
#include "jni/jni_env_ext.h"
#include "mirror/throwable.h"
-#include "nativehelper/ScopedLocalRef.h"
+#include "nativehelper/scoped_local_ref.h"
#include "runtime-inl.h"
#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index 12eae89..55b8ae2 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -127,7 +127,6 @@
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := $(TARGET_TEST_CORE_JARS)
LOCAL_MODULE_PATH := $(3)
- LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT)
ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
LOCAL_MIN_SDK_VERSION := 19
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
@@ -143,7 +142,6 @@
LOCAL_DEX_PREOPT := false
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_test.mk $(4)
LOCAL_JAVA_LIBRARIES := $(HOST_TEST_CORE_JARS)
- LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION)
ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
LOCAL_MIN_SDK_VERSION := 19
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 67d85c1..21eee7a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -38,6 +38,7 @@
GetMethodSignature \
HiddenApi \
HiddenApiSignatures \
+ HiddenApiStubs \
ImageLayoutA \
ImageLayoutB \
IMTA \
@@ -188,7 +189,7 @@
ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed EmptyUncompressed StringLiterals
ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
-ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi
+ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi HiddenApiStubs
ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures
ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods VerifySoftFailDuringClinit
ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB
@@ -384,6 +385,7 @@
art_hiddenapi_tests \
art_imgdiag_tests \
art_libartbase_tests \
+ art_libartpalette_tests \
art_libdexfile_external_tests \
art_libdexfile_support_tests \
art_libdexfile_tests \
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index d53a7f2..4db2066 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -13,11 +13,21 @@
]
// - Base requirements (libraries).
art_runtime_base_native_shared_libs = [
+ "libadbconnection",
"libart",
"libart-compiler",
+ "libdexfile_external",
+ "libnativebridge",
+ "libnativehelper",
+ "libnativeloader",
"libopenjdkjvm",
"libopenjdkjvmti",
- "libadbconnection",
+ // TODO(b/124439236): Clean up the following libraries once "required"
+ // dependencies work with APEX libraries.
+ "libdt_fd_forward",
+ "libdt_socket",
+ "libjdwp",
+ "libnpt",
]
bionic_native_shared_libs = [
"libc",
@@ -29,17 +39,23 @@
]
// - Debug variants (binaries for which a 32-bit version is preferred).
art_runtime_debug_binaries_prefer32 = [
- "dex2oatd",
"dexoptanalyzerd",
"profmand",
]
+art_runtime_debug_binaries_prefer32_device = [
+ "dex2oatd",
+]
+art_runtime_debug_binaries_both_host = [
+ "dex2oatd",
+]
+
// - Debug variants (libraries).
art_runtime_debug_native_shared_libs = [
+ "libadbconnectiond",
"libartd",
"libartd-compiler",
"libopenjdkjvmd",
"libopenjdkjvmtid",
- "libadbconnectiond",
]
libcore_debug_native_shared_libs = [
"libopenjdkd",
@@ -79,17 +95,13 @@
art_tools_device_binaries = art_tools_common_binaries + art_tools_device_only_binaries
art_tools_host_binaries = art_tools_common_binaries + art_tools_host_only_binaries
-// Libcore native libraries.
-libcore_native_shared_libs = [
- "libjavacore",
- "libopenjdk",
- "libexpat",
- "libz",
- "libziparchive"
+// Libraries needed to use com.android.runtime.host for zipapex run-tests
+art_runtime_host_run_test_libs = [
+ "libartd-disassembler"
]
-// Java libraries
-libcore_target_java_libs = [
+// Core Java libraries.
+libcore_java_libs = [
"core-oj",
"core-libart",
"okhttp",
@@ -97,12 +109,48 @@
"apache-xml",
]
+// Native libraries that support the core Java libraries.
+libcore_native_shared_libs = [
+ "libandroidicu",
+ "libexpat",
+ "libicui18n",
+ "libicuuc",
+ "libjavacore",
+ "libopenjdk",
+ "libz",
+ "libziparchive",
+]
+
+libcore_native_device_only_shared_libs = libcore_native_shared_libs + [
+ // TODO(b/122876336): Remove libpac.so once it's migrated to Webview.
+ // libpac is used by frameworks, not by ART host.
+ "libpac",
+]
+
+// Temporary library includes for b/123591866 as all libraries are moved into the main art-apex.
+art_runtime_libraries_zipapex = [
+ "libnativebridge",
+ "libnativeloader",
+ "libnativehelper",
+ "libcutils",
+]
+
apex_key {
name: "com.android.runtime.key",
public_key: "com.android.runtime.avbpubkey",
private_key: "com.android.runtime.pem",
}
+android_app_certificate {
+ name: "com.android.runtime.debug.certificate",
+ certificate: "com.android.runtime.debug",
+}
+
+android_app_certificate {
+ name: "com.android.runtime.release.certificate",
+ certificate: "com.android.runtime.release",
+}
+
prebuilt_etc {
name: "com.android.runtime.ld.config.txt",
src: "ld.config.txt",
@@ -120,10 +168,10 @@
name: "com.android.runtime.release",
compile_multilib: "both",
manifest: "manifest.json",
- java_libs: libcore_target_java_libs,
+ java_libs: libcore_java_libs,
native_shared_libs: art_runtime_base_native_shared_libs
+ bionic_native_shared_libs
- + libcore_native_shared_libs,
+ + libcore_native_device_only_shared_libs,
multilib: {
both: {
// TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
@@ -141,6 +189,7 @@
prebuilts: art_runtime_data_file_prebuilts
+ ["com.android.runtime.ld.config.txt"],
key: "com.android.runtime.key",
+ certificate: ":com.android.runtime.release.certificate",
}
// "Debug" version of the Runtime APEX module (containing both release and
@@ -150,11 +199,11 @@
name: "com.android.runtime.debug",
compile_multilib: "both",
manifest: "manifest.json",
- java_libs: libcore_target_java_libs,
+ java_libs: libcore_java_libs,
native_shared_libs: art_runtime_base_native_shared_libs
+ art_runtime_debug_native_shared_libs
+ bionic_native_shared_libs
- + libcore_native_shared_libs
+ + libcore_native_device_only_shared_libs
+ libcore_debug_native_shared_libs,
multilib: {
both: {
@@ -165,7 +214,8 @@
},
prefer32: {
binaries: art_runtime_base_binaries_prefer32
- + art_runtime_debug_binaries_prefer32,
+ + art_runtime_debug_binaries_prefer32
+ + art_runtime_debug_binaries_prefer32_device,
},
first: {
binaries: art_tools_device_binaries,
@@ -174,6 +224,7 @@
prebuilts: art_runtime_data_file_prebuilts
+ ["com.android.runtime.ld.config.txt"],
key: "com.android.runtime.key",
+ certificate: ":com.android.runtime.debug.certificate",
}
// TODO: Do this better. art_apex will disable host builds when
@@ -181,22 +232,27 @@
// because binaries have different multilib classes and 'multilib: {}' isn't
// supported by target: { ... }.
// See b/120617876 for more information.
-art_apex {
+art_apex_test {
name: "com.android.runtime.host",
compile_multilib: "both",
payload_type: "zip",
host_supported: true,
device_supported: false,
manifest: "manifest.json",
+ 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,
+ + libcore_debug_native_shared_libs
+ + art_runtime_libraries_zipapex
+ + art_runtime_host_run_test_libs,
multilib: {
both: {
// TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
// (see `symlink_preferred_arch` in art/dalvikvm/Android.bp).
- binaries: art_runtime_base_binaries_both,
+ binaries: art_runtime_base_binaries_both
+ + art_runtime_debug_binaries_both_host,
},
first: {
binaries: art_tools_host_binaries
@@ -209,5 +265,83 @@
darwin: {
enabled: false,
},
+ linux_bionic: {
+ enabled: true,
+ multilib: {
+ both: {
+ native_shared_libs: bionic_native_shared_libs,
+ binaries: bionic_binaries_both,
+ }
+ }
+ },
+ },
+}
+
+python_binary_host {
+ name: "art-apex-tester",
+ srcs: ["art_apex_test.py"],
+ main: "art_apex_test.py",
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ },
+ },
+}
+
+// Genrules so we can run the checker, and empty Java library so that it gets executed.
+
+genrule {
+ name: "art-check-release-apex-gen",
+ srcs: [":com.android.runtime.release"],
+ tools: [
+ "art-apex-tester",
+ "debugfs",
+ ],
+ cmd: "$(location art-apex-tester)"
+ + " --debugfs $(location debugfs)"
+ + " --tmpdir $(genDir)"
+ + " $(in)"
+ + " && touch $(out)",
+ out: ["art-check-release-apex-gen.dummy"],
+}
+cc_prebuilt_binary {
+ name: "art-check-release-apex-gen-fakebin",
+ srcs: [":art-check-release-apex-gen"],
+ host_supported: true,
+ device_supported: false,
+ target: {
+ darwin: {
+ enabled: false, // No python3.
+ },
+ },
+}
+
+genrule {
+ name: "art-check-debug-apex-gen",
+ srcs: [":com.android.runtime.debug"],
+ tools: [
+ "art-apex-tester",
+ "debugfs",
+ ],
+ cmd: "$(location art-apex-tester)"
+ + " --debugfs $(location debugfs)"
+ + " --tmpdir $(genDir)"
+ + " --debug"
+ + " $(in)"
+ + " && touch $(out)",
+ out: ["art-check-debug-apex-gen.dummy"],
+}
+cc_prebuilt_binary {
+ name: "art-check-debug-apex-gen-fakebin",
+ srcs: [":art-check-debug-apex-gen"],
+ host_supported: true,
+ device_supported: false,
+ target: {
+ darwin: {
+ enabled: false, // No python3.
+ },
},
}
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
new file mode 100755
index 0000000..46d1e4e
--- /dev/null
+++ b/build/apex/art_apex_test.py
@@ -0,0 +1,667 @@
+#!/usr/bin/env python3
+
+# 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.
+#
+
+import argparse
+import logging
+import os
+import subprocess
+import sys
+import zipfile
+
+logging.basicConfig(format='%(message)s')
+
+class FSObject:
+ def __init__(self, name, is_dir, is_exec, is_symlink):
+ self.name = name
+ self.is_dir = is_dir
+ self.is_exec = is_exec
+ self.is_symlink = is_symlink
+ def __str__(self):
+ return '%s(dir=%r,exec=%r,symlink=%r)' % (self.name, self.is_dir, self.is_exec, self.is_symlink)
+
+class TargetApexProvider:
+ def __init__(self, apex, tmpdir, debugfs):
+ self._tmpdir = tmpdir
+ self._debugfs = debugfs
+ self._folder_cache = {}
+ self._payload = os.path.join(self._tmpdir, 'apex_payload.img')
+ # Extract payload to tmpdir.
+ zip = zipfile.ZipFile(apex)
+ zip.extract('apex_payload.img', tmpdir)
+
+ def __del__(self):
+ # Delete temps.
+ if os.path.exists(self._payload):
+ os.remove(self._payload)
+
+ def get(self, path):
+ dir, name = os.path.split(path)
+ if len(dir) == 0:
+ dir = '.'
+ map = self.read_dir(dir)
+ return map[name] if name in map else None
+
+ def read_dir(self, dir):
+ if dir in self._folder_cache:
+ return self._folder_cache[dir]
+ # Cannot use check_output as it will annoy with stderr.
+ process = subprocess.Popen([self._debugfs, '-R', 'ls -l -p %s' % (dir), self._payload],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ universal_newlines=True)
+ stdout, stderr = process.communicate()
+ res = str(stdout)
+ map = {}
+ # Debugfs output looks like this:
+ # debugfs 1.44.4 (18-Aug-2018)
+ # /12/040755/0/2000/.//
+ # /2/040755/1000/1000/..//
+ # /13/100755/0/2000/dalvikvm32/28456/
+ # /14/100755/0/2000/dexoptanalyzer/20396/
+ # /15/100755/0/2000/linker/1152724/
+ # /16/100755/0/2000/dex2oat/563508/
+ # /17/100755/0/2000/linker64/1605424/
+ # /18/100755/0/2000/profman/85304/
+ # /19/100755/0/2000/dalvikvm64/28576/
+ # | | | | | |
+ # | | | #- gid #- name #- size
+ # | | #- uid
+ # | #- type and permission bits
+ # #- inode nr (?)
+ #
+ # Note: could break just on '/' to avoid names with newlines.
+ for line in res.split("\n"):
+ if not line:
+ continue
+ comps = line.split('/')
+ if len(comps) != 8:
+ logging.warn('Could not break and parse line \'%s\'', line)
+ continue
+ bits = comps[2]
+ name = comps[5]
+ if len(bits) != 6:
+ logging.warn('Dont understand bits \'%s\'', bits)
+ continue
+ is_dir = True if bits[1] == '4' else False
+ def is_exec_bit(ch):
+ return True if int(ch) & 1 == 1 else False
+ is_exec = is_exec_bit(bits[3]) and is_exec_bit(bits[4]) and is_exec_bit(bits[5])
+ is_symlink = True if bits[1] == '2' else False
+ map[name] = FSObject(name, is_dir, is_exec, is_symlink)
+ self._folder_cache[dir] = map
+ return map
+
+class HostApexProvider:
+ def __init__(self, apex, tmpdir):
+ self._tmpdir = tmpdir
+ self._folder_cache = {}
+ self._payload = os.path.join(self._tmpdir, 'apex_payload.zip')
+ # Extract payload to tmpdir.
+ zip = zipfile.ZipFile(apex)
+ zip.extract('apex_payload.zip', tmpdir)
+
+ def __del__(self):
+ # Delete temps.
+ if os.path.exists(self._payload):
+ os.remove(self._payload)
+
+ def get(self, path):
+ dir, name = os.path.split(path)
+ if len(dir) == 0:
+ dir = ''
+ map = self.read_dir(dir)
+ return map[name] if name in map else None
+
+ def read_dir(self, dir):
+ if dir in self._folder_cache:
+ return self._folder_cache[dir]
+ if not self._folder_cache:
+ self.parse_zip()
+ if dir in self._folder_cache:
+ return self._folder_cache[dir]
+ return {}
+
+ def parse_zip(self):
+ zip = zipfile.ZipFile(self._payload)
+ infos = zip.infolist()
+ for zipinfo in infos:
+ path = zipinfo.filename
+
+ # Assume no empty file is stored.
+ assert path
+
+ def get_octal(val, index):
+ return (val >> (index * 3)) & 0x7;
+ def bits_is_exec(val):
+ # TODO: Enforce group/other, too?
+ return get_octal(val, 2) & 1 == 1
+
+ is_zipinfo = True
+ while path:
+ dir, base = os.path.split(path)
+ # TODO: If directories are stored, base will be empty.
+
+ if not dir in self._folder_cache:
+ self._folder_cache[dir] = {}
+ dir_map = self._folder_cache[dir]
+ if not base in dir_map:
+ if is_zipinfo:
+ bits = (zipinfo.external_attr >> 16) & 0xFFFF
+ is_dir = get_octal(bits, 4) == 4
+ is_symlink = get_octal(bits, 4) == 2
+ is_exec = bits_is_exec(bits)
+ else:
+ is_exec = False # Seems we can't get this easily?
+ is_symlink = False
+ is_dir = True
+ dir_map[base] = FSObject(base, is_dir, is_exec, is_symlink)
+ is_zipinfo = False
+ path = dir
+
+# DO NOT USE DIRECTLY! This is an "abstract" base class.
+class Checker:
+ def __init__(self, provider):
+ self._provider = provider
+ self._errors = 0
+
+ def fail(self, msg, *args):
+ self._errors += 1
+ logging.error(msg, args)
+
+ def error_count(self):
+ return self._errors
+ def reset_errors(self):
+ self._errors = 0
+
+ def is_file(self, file):
+ fs_object = self._provider.get(file)
+ if fs_object is None:
+ return (False, 'Could not find %s')
+ if fs_object.is_dir:
+ return (False, '%s is a directory')
+ return (True, '')
+
+ def check_file(self, file):
+ chk = self.is_file(file)
+ if not chk[0]:
+ self.fail(chk[1], file)
+ return chk[0]
+ def check_no_file(self, file):
+ chk = self.is_file(file)
+ if chk[0]:
+ self.fail('File %s does exist', file)
+ return not chk[0]
+
+ def check_binary(self, file):
+ path = 'bin/%s' % (file)
+ if not self.check_file(path):
+ return False
+ if not self._provider.get(path).is_exec:
+ self.fail('%s is not executable', path)
+ return False
+ return True
+
+ def check_binary_symlink(self, file):
+ path = 'bin/%s' % (file)
+ fs_object = self._provider.get(path)
+ if fs_object is None:
+ self.fail('Could not find %s', path)
+ return False
+ if fs_object.is_dir:
+ self.fail('%s is a directory', path)
+ return False
+ if not fs_object.is_symlink:
+ self.fail('%s is not a symlink', path)
+ return False
+ return True
+
+ def check_single_library(self, file):
+ res1 = self.is_file('lib/%s' % (file))
+ res2 = self.is_file('lib64/%s' % (file))
+ if not res1[0] and not res2[0]:
+ self.fail('Library missing: %s', file)
+ return False
+ return True
+
+ def check_no_library(self, file):
+ res1 = self.is_file('lib/%s' % (file))
+ res2 = self.is_file('lib64/%s' % (file))
+ if res1[0] or res2[0]:
+ self.fail('Library exists: %s', file)
+ return False
+ return True
+
+ def check_java_library(self, file):
+ return self.check_file('javalib/%s' % (file))
+
+ # Just here for docs purposes, even if it isn't good Python style.
+
+ def check_library(self, file):
+ raise NotImplementedError
+
+ def check_first_library(self, file):
+ raise NotImplementedError
+
+ def check_multilib_binary(self, file):
+ raise NotImplementedError
+
+ def check_prefer32_binary(self, file):
+ raise NotImplementedError
+
+
+class Arch32Checker(Checker):
+ def __init__(self, provider):
+ super().__init__(provider)
+
+ def check_multilib_binary(self, file):
+ return all([self.check_binary('%s32' % (file)),
+ self.check_no_file('bin/%s64' % (file)),
+ self.check_binary_symlink(file)])
+
+ def check_library(self, file):
+ # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
+ # the precision of this test?
+ return all([self.check_file('lib/%s' % (file)), self.check_no_file('lib64/%s' % (file))])
+
+ def check_first_library(self, file):
+ return self.check_library(file)
+
+ def check_prefer32_binary(self, file):
+ return self.check_binary('%s32' % (file))
+
+
+class Arch64Checker(Checker):
+ def __init__(self, provider):
+ super().__init__(provider)
+
+ def check_multilib_binary(self, file):
+ return all([self.check_no_file('bin/%s32' % (file)),
+ self.check_binary('%s64' % (file)),
+ self.check_binary_symlink(file)])
+
+ def check_library(self, file):
+ # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
+ # the precision of this test?
+ return all([self.check_no_file('lib/%s' % (file)), self.check_file('lib64/%s' % (file))])
+
+ def check_first_library(self, file):
+ return self.check_library(file)
+
+ def check_prefer32_binary(self, file):
+ return self.check_binary('%s64' % (file))
+
+
+class MultilibChecker(Checker):
+ def __init__(self, provider):
+ super().__init__(provider)
+
+ def check_multilib_binary(self, file):
+ return all([self.check_binary('%s32' % (file)),
+ self.check_binary('%s64' % (file)),
+ self.check_binary_symlink(file)])
+
+ def check_library(self, file):
+ # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
+ # the precision of this test?
+ return all([self.check_file('lib/%s' % (file)), self.check_file('lib64/%s' % (file))])
+
+ def check_first_library(self, file):
+ return all([self.check_no_file('lib/%s' % (file)), self.check_file('lib64/%s' % (file))])
+
+ def check_prefer32_binary(self, file):
+ return self.check_binary('%s32' % (file))
+
+
+class ReleaseChecker:
+ def __init__(self, checker):
+ self._checker = checker
+ def __str__(self):
+ return 'Release Checker'
+
+ def run(self):
+ # Check that the mounted image contains an APEX manifest.
+ self._checker.check_file('apex_manifest.json')
+
+ # Check that the mounted image contains ART base binaries.
+ self._checker.check_multilib_binary('dalvikvm')
+ self._checker.check_binary('dex2oat')
+ self._checker.check_binary('dexoptanalyzer')
+ self._checker.check_binary('profman')
+
+ # oatdump is only in device apex's due to build rules
+ # TODO: Check for it when it is also built for host.
+ # self._checker.check_binary('oatdump')
+
+ # Check that the mounted image contains Android Runtime libraries.
+ self._checker.check_library('libart-compiler.so')
+ self._checker.check_library('libart-dexlayout.so')
+ self._checker.check_library('libart.so')
+ self._checker.check_library('libartbase.so')
+ self._checker.check_library('libartpalette.so')
+ self._checker.check_no_library('libartpalette-system.so')
+ self._checker.check_library('libdexfile.so')
+ self._checker.check_library('libdexfile_external.so')
+ self._checker.check_library('libnativebridge.so')
+ self._checker.check_library('libnativehelper.so')
+ self._checker.check_library('libnativeloader.so')
+ self._checker.check_library('libopenjdkjvm.so')
+ self._checker.check_library('libopenjdkjvmti.so')
+ self._checker.check_library('libprofile.so')
+ # Check that the mounted image contains Android Core libraries.
+ # Note: host vs target libs are checked elsewhere.
+ self._checker.check_library('libjavacore.so')
+ self._checker.check_library('libopenjdk.so')
+ self._checker.check_library('libziparchive.so')
+ # Check that the mounted image contains additional required libraries.
+ self._checker.check_library('libadbconnection.so')
+
+ # TODO: Should we check for other libraries, such as:
+ #
+ # libbacktrace.so
+ # libbase.so
+ # liblog.so
+ # libsigchain.so
+ # libtombstoned_client.so
+ # libunwindstack.so
+ # libvixl.so
+ # libvixld.so
+ # ...
+ #
+ # ?
+
+ self._checker.check_java_library('core-oj.jar')
+ self._checker.check_java_library('core-libart.jar')
+ self._checker.check_java_library('okhttp.jar')
+ self._checker.check_java_library('bouncycastle.jar')
+ self._checker.check_java_library('apache-xml.jar')
+
+class ReleaseTargetChecker:
+ def __init__(self, checker):
+ self._checker = checker
+ def __str__(self):
+ return 'Release (Target) Checker'
+
+ def run(self):
+ # Check that the mounted image contains Android Core libraries.
+ self._checker.check_library('libandroidicu.so')
+ self._checker.check_library('libexpat.so')
+ self._checker.check_library('libicui18n.so')
+ self._checker.check_library('libicuuc.so')
+ self._checker.check_library('libpac.so')
+ self._checker.check_library('libz.so')
+
+class ReleaseHostChecker:
+ def __init__(self, checker):
+ self._checker = checker;
+ def __str__(self):
+ return 'Release (Host) Checker'
+
+ def run(self):
+ # Check that the mounted image contains Android Core libraries.
+ self._checker.check_library('libandroidicu-host.so')
+ self._checker.check_library('libexpat-host.so')
+ self._checker.check_library('libicui18n-host.so')
+ self._checker.check_library('libicuuc-host.so')
+ self._checker.check_library('libz-host.so')
+
+class DebugChecker:
+ def __init__(self, checker):
+ self._checker = checker
+ def __str__(self):
+ return 'Debug Checker'
+
+ def run(self):
+ # Check that the mounted image contains ART tools binaries.
+ self._checker.check_binary('dexdiag')
+ self._checker.check_binary('dexdump')
+ self._checker.check_binary('dexlist')
+
+ # Check that the mounted image contains ART debug binaries.
+ self._checker.check_binary('dex2oatd')
+ self._checker.check_binary('dexoptanalyzerd')
+ self._checker.check_binary('profmand')
+
+ # Check that the mounted image contains Android Runtime debug libraries.
+ self._checker.check_library('libartbased.so')
+ self._checker.check_library('libartd-compiler.so')
+ self._checker.check_library('libartd-dexlayout.so')
+ self._checker.check_library('libartd.so')
+ self._checker.check_library('libdexfiled.so')
+ self._checker.check_library('libopenjdkjvmd.so')
+ self._checker.check_library('libopenjdkjvmtid.so')
+ self._checker.check_library('libprofiled.so')
+ # Check that the mounted image contains Android Core debug libraries.
+ self._checker.check_library('libopenjdkd.so')
+ # Check that the mounted image contains additional required debug libraries.
+ self._checker.check_library('libadbconnectiond.so')
+
+class DebugTargetChecker:
+ def __init__(self, checker):
+ self._checker = checker
+ def __str__(self):
+ return 'Debug (Target) Checker'
+
+ def run(self):
+ # Check for files pulled in from debug target-only oatdump.
+ self._checker.check_binary('oatdump')
+ self._checker.check_first_library('libart-disassembler.so')
+
+def print_list(provider):
+ def print_list_impl(provider, path):
+ map = provider.read_dir(path)
+ if map is None:
+ return
+ map = dict(map)
+ if '.' in map:
+ del map['.']
+ if '..' in map:
+ del map['..']
+ for (_, val) in sorted(map.items()):
+ new_path = os.path.join(path, val.name)
+ print(new_path)
+ if val.is_dir:
+ print_list_impl(provider, new_path)
+ print_list_impl(provider, '')
+
+def print_tree(provider, title):
+ def get_vertical(has_next_list):
+ str = ''
+ for v in has_next_list:
+ str += '%s ' % ('│' if v else ' ')
+ return str
+ def get_last_vertical(last):
+ return '└── ' if last else '├── ';
+ def print_tree_impl(provider, path, has_next_list):
+ map = provider.read_dir(path)
+ if map is None:
+ return
+ map = dict(map)
+ if '.' in map:
+ del map['.']
+ if '..' in map:
+ del map['..']
+ key_list = list(sorted(map.keys()))
+ for i in range(0, len(key_list)):
+ val = map[key_list[i]]
+ prev = get_vertical(has_next_list)
+ last = get_last_vertical(i == len(key_list) - 1)
+ print('%s%s%s' % (prev, last, val.name))
+ if val.is_dir:
+ has_next_list.append(i < len(key_list) - 1)
+ print_tree_impl(provider, os.path.join(path, val.name), has_next_list)
+ has_next_list.pop()
+ print('%s' % (title))
+ print_tree_impl(provider, '', [])
+
+# Note: do not sys.exit early, for __del__ cleanup.
+def artApexTestMain(args):
+ if args.tree and args.debug:
+ logging.error("Both of --tree and --debug set")
+ return 1
+ if args.list and args.debug:
+ logging.error("Both of --list and --debug set")
+ return 1
+ if args.list and args.tree:
+ logging.error("Both of --list and --tree set")
+ return 1
+ if not args.tmpdir:
+ logging.error("Need a tmpdir.")
+ return 1
+ if not args.host and not args.debugfs:
+ logging.error("Need debugfs.")
+ return 1
+ if args.bitness not in ['32', '64', 'multilib', 'auto']:
+ logging.error('--bitness needs to be one of 32|64|multilib|auto')
+
+ try:
+ if args.host:
+ apex_provider = HostApexProvider(args.apex, args.tmpdir)
+ else:
+ apex_provider = TargetApexProvider(args.apex, args.tmpdir, args.debugfs)
+ except Exception as e:
+ logging.error('Failed to create provider: %s', e)
+ return 1
+
+ if args.tree:
+ print_tree(apex_provider, args.apex)
+ return 0
+ if args.list:
+ print_list(apex_provider)
+ return 0
+
+ checkers = []
+ if args.bitness == 'auto':
+ logging.warn('--bitness=auto, trying to autodetect. This may be incorrect!')
+ has_32 = apex_provider.get('lib') is not None
+ has_64 = apex_provider.get('lib64') is not None
+ if has_32 and has_64:
+ logging.warn(' Detected multilib')
+ args.bitness = 'multilib'
+ elif has_32:
+ logging.warn(' Detected 32-only')
+ args.bitness = '32'
+ elif has_64:
+ logging.warn(' Detected 64-only')
+ args.bitness = '64'
+ else:
+ logging.error(' Could not detect bitness, neither lib nor lib64 contained.')
+ print('%s' % (apex_provider._folder_cache))
+ return 1
+
+ if args.bitness == '32':
+ base_checker = Arch32Checker(apex_provider)
+ elif args.bitness == '64':
+ base_checker = Arch64Checker(apex_provider)
+ else:
+ assert args.bitness == 'multilib'
+ base_checker = MultilibChecker(apex_provider)
+
+ checkers.append(ReleaseChecker(base_checker))
+ if args.host:
+ checkers.append(ReleaseHostChecker(base_checker))
+ else:
+ checkers.append(ReleaseTargetChecker(base_checker))
+ if args.debug:
+ checkers.append(DebugChecker(base_checker))
+ if args.debug and not args.host:
+ checkers.append(DebugTargetChecker(base_checker))
+
+ failed = False
+ for checker in checkers:
+ logging.info('%s...', checker)
+ checker.run()
+ if base_checker.error_count() > 0:
+ logging.error('%s FAILED', checker)
+ failed = True
+ else:
+ logging.info('%s SUCCEEDED', checker)
+ base_checker.reset_errors()
+
+ return 1 if failed else 0
+
+def artApexTestDefault(parser):
+ if not 'ANDROID_PRODUCT_OUT' in os.environ:
+ logging.error('No-argument use requires ANDROID_PRODUCT_OUT')
+ sys.exit(1)
+ product_out = os.environ['ANDROID_PRODUCT_OUT']
+ if not 'ANDROID_HOST_OUT' in os.environ:
+ logging.error('No-argument use requires ANDROID_HOST_OUT')
+ sys.exit(1)
+ host_out = os.environ['ANDROID_HOST_OUT']
+
+ args = parser.parse_args(['dummy']) # For consistency.
+ args.debugfs = '%s/bin/debugfs' % (host_out)
+ args.tmpdir = '.'
+ args.tree = False
+ args.list = False
+ args.bitness = 'auto'
+ failed = False
+
+ if not os.path.exists(args.debugfs):
+ logging.error("Cannot find debugfs (default path %s). Please build it, e.g., m debugfs",
+ args.debugfs)
+ sys.exit(1)
+
+ # TODO: Add host support
+ configs= [
+ {'name': 'com.android.runtime.release', 'debug': False, 'host': False},
+ {'name': 'com.android.runtime.debug', 'debug': True, 'host': False},
+ ]
+
+ for config in configs:
+ logging.info(config['name'])
+ # TODO: Host will need different path.
+ args.apex = '%s/system/apex/%s.apex' % (product_out, config['name'])
+ if not os.path.exists(args.apex):
+ failed = True
+ logging.error("Cannot find APEX %s. Please build it first.", args.apex)
+ continue
+ args.debug = config['debug']
+ args.host = config['host']
+ exit_code = artApexTestMain(args)
+ if exit_code != 0:
+ failed = True
+
+ if failed:
+ sys.exit(1)
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description='Check integrity of a Runtime APEX.')
+
+ parser.add_argument('apex', help='apex file input')
+
+ parser.add_argument('--host', help='Check as host apex', action='store_true')
+
+ parser.add_argument('--debug', help='Check as debug apex', action='store_true')
+
+ parser.add_argument('--list', help='List all files', action='store_true')
+ parser.add_argument('--tree', help='Print directory tree', action='store_true')
+
+ parser.add_argument('--tmpdir', help='Directory for temp files')
+ parser.add_argument('--debugfs', help='Path to debugfs')
+
+ parser.add_argument('--bitness', help='Bitness to check, 32|64|multilib|auto', default='auto')
+
+ if len(sys.argv) == 1:
+ artApexTestDefault(parser)
+ else:
+ args = parser.parse_args()
+
+ if args is None:
+ sys.exit(1)
+
+ exit_code = artApexTestMain(args)
+ sys.exit(exit_code)
diff --git a/build/apex/com.android.runtime.debug.pk8 b/build/apex/com.android.runtime.debug.pk8
new file mode 100644
index 0000000..5eec5d8
--- /dev/null
+++ b/build/apex/com.android.runtime.debug.pk8
Binary files differ
diff --git a/build/apex/com.android.runtime.debug.x509.pem b/build/apex/com.android.runtime.debug.x509.pem
new file mode 100644
index 0000000..73402f5
--- /dev/null
+++ b/build/apex/com.android.runtime.debug.x509.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF0DCCA7igAwIBAgIJALbaJLNNAiRDMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+aWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRwwGgYDVQQD
+DBNjb20uYW5kcm9pZC5ydW50aW1lMCAXDTE5MDEyNTE3MTQ1NloYDzQ3NTYxMjIx
+MTcxNDU2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG
+A1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwH
+QW5kcm9pZDEcMBoGA1UEAwwTY29tLmFuZHJvaWQucnVudGltZTCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBAN1Ns75c0ZdLKnUvEuEotzJ0xyOLzOHYP3y6
+RzcwFyPf84aADc7rQDtjbmEuf9g9QpJhZAxe7G2Jg/wxqaxMW6wCfkpuElW5CAcj
+XQ0i12hRVtqePs5Z5bjzJ/8C7luWh82Vb/s2YoRPoKNXVWFT16CB4RMnw2nW5Uyo
+RHZ98N4MgFSGilafIc6Z0DImreTTwlEvcyKihVUSuzeyPG8CRjshw0C1Hqxt4a8J
+rxAgfPpd84Xo4etYePpVr2K5+vNAVwLpUdD48Y7q9peOJ0tbL8DSohudkzvZsQUo
+CfEfrVBfZv7aPnt6ZJYhcFo1WRBMYczKP4jWb0KgmF963ee3zliU1pXtIYsNBNth
+Mdvy3ml301tI7CKE5A3Yevm40VVqo+IDt7FNxoV3rKPhnO9vi/YqzX/1xMvAto8E
+9A5NvMTqHmS2P0wt1pt9KSuXXjoIAWaHZOATDkVI+jLjDrYFNdhqXVgbAaVtI60j
+lRaSWxzBr4o+g2W8ks/JgM2mwJ6qaTNoDMzg823MKzy/t3935sxm5GhFs9AY9Qz/
+4B3exqYUEFJLN6dJLCVppCmFCdCONSxN7bXPo+3b9LlZuKAOP17N04+eKcwXVeYz
+Z3a7SfyMzq+DtLhAn/TSliSbbCAVUxiOZnVX1nM0Gs3/BYCs0TUh2tSqO48pwDrx
+Pw7z9+m5AgMBAAGjUzBRMB0GA1UdDgQWBBRT9s/tu4uqtrglUFjQbwY5p+17DjAf
+BgNVHSMEGDAWgBRT9s/tu4uqtrglUFjQbwY5p+17DjAPBgNVHRMBAf8EBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4ICAQBNY5giwZCM0sE93Dj2zEe8qCRwIaV4bvSe744X
+Y1+405vdrNEHKPUfFn1xLSnhiGU3loZrP15lexmWvxycLNEy0UxZgq3eR7HuW6xp
+GIm9ttYDZEP+pL9hwew3jiR38NRRR1Ur1MsBNkZnCELC1W8RFWIi77Fsb4fj2mGn
+2R+2voBvVS5kjkytW079CEIsZN9RVYfERiKPCfJDa87kk0xduqyh7sDegQl0B2Ot
+R9KnD1dJZjbii2GRkhpJ/Ig17CQH3J8PY/SIt9L+QAchnIEF051sjbBRUJuPK9gL
+eBEkZkwD1JLqGO6fxkcjNx7MIevTnIBjX2Epr8luyRy7eR3TdBT3aRQcCUqBCi3i
+WxAVR5sOZ90INTXftFbztoklitpQ9mxKXgFr+xggL6u3BdJk1Nt9BsYmRzh5Bg+6
+1eMDBumy3JEA7whE8p75X9cSnKTNrDQU3DA5XzpIhmI91XJArBhBfxgqGxaTf0uq
+SfZRDfnaO456ZsZdKUy62mry6Vg/hvzX52x/HxDlSQWbpYp5t03hshaWxtNE376q
+GdqOoGRRWCvyWi/UOYzabp6czTjwV1JH9IU379CsqIO5UNJ2MM2re4TDXofefU1C
+6eiYihy28xDfIiCdretLRlvFYFF/5X5xby/XWsDA9sGlL5OOiXC6o0Pl9vbek2+T
+Ibx3Nw==
+-----END CERTIFICATE-----
diff --git a/build/apex/com.android.runtime.release.pk8 b/build/apex/com.android.runtime.release.pk8
new file mode 100644
index 0000000..c63efb8
--- /dev/null
+++ b/build/apex/com.android.runtime.release.pk8
Binary files differ
diff --git a/build/apex/com.android.runtime.release.x509.pem b/build/apex/com.android.runtime.release.x509.pem
new file mode 100644
index 0000000..4a7607a
--- /dev/null
+++ b/build/apex/com.android.runtime.release.x509.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF0DCCA7igAwIBAgIJAMtsu/wrkZurMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+aWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRwwGgYDVQQD
+DBNjb20uYW5kcm9pZC5ydW50aW1lMCAXDTE5MDEyNTE3MTU0MFoYDzQ3NTYxMjIx
+MTcxNTQwWjB8MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG
+A1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwH
+QW5kcm9pZDEcMBoGA1UEAwwTY29tLmFuZHJvaWQucnVudGltZTCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBAL+aGSc+HU69vV1VbZb6WjXMDrE2Jo+JjXLU
+yVS3o8qlQeqN0RFbsbwnihnwg2xBnM6JiskAcuocz87dDuEt1zUEInC3Hpt/C2eg
+GUZepbq8po+v+b04YlX3aTaYTFqMjU0aQkjOqhnmVxg+KHpvudlvKB3VhH3D61al
+RReQzgM/Q6aUxcr4Z8XwvzV3i0K5NjiSuSt14K2yIaheh2OTbbwtvm3d+0sQDco6
+1gl0l4rM4e+GjxgWVqx8mfKhd4HTS3YIBDWXR6DFPBARzVBIpZu2QK4U6Jdsy2wI
+xg8+d5KWAoNQb7IQK6LQy7Fbw3PNJDo4Ph39G2wNgeMemz8uSQ9FZujc0AgxBom6
+J+ad7zlJBhYFC4UIKBYrRfJCHTN3GLuLvhu0p0jNMfdQXF6Tv/iG9g8JdZ0QjeWm
+/K+h1p6LUAIUV0UP7j8nIdp0j6NqMywkoeRDYlVQV/XdI7BiQe9Z8yNbF5Y3CxWT
+hMfN9iby11ImPilzpgv39ORVjDQdxxcwhJg2Xuu1752cBxcHu3ZcR8AiB7PCksXu
+EpUrjjOH8eVxoG1JJ/na5elUg/H35Or+JYYd8I8Ad1/GRkPrnIBAGzuyntOsNs4t
+2CEnhmV6EkEH8KP8miTdaa5NdPIwFRIHVBHcrqsqdmrINvoJhaVRH7YwmFjv48ak
+N4OyW3oLAgMBAAGjUzBRMB0GA1UdDgQWBBRqVJ0tsEOyqhKiZOrOfRD1+jQFMDAf
+BgNVHSMEGDAWgBRqVJ0tsEOyqhKiZOrOfRD1+jQFMDAPBgNVHRMBAf8EBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4ICAQAs+I1tRWRPmhA+FqcRdlAcY2Vy7NO12hjWXCT9
+hqenGk1/VnhH8aZT5lXZNaWeKonT5W7XydyrjiF09ndZBGew0rEZh6sMXH+G/drT
+9JxvfHsCQGrmX32V1XgAoRjV1VpUYIb2747fFWHHbl5frowNj955pkfseeKilSnF
+orUl5uGNxr6iNaVEUDfXBWkHwipYVyejAqdHkCQDhLtDBWsiskKpLWmmNKuy2QXQ
+uoyUyfeSR1Y+pT83qgmGb1LFLiOqL9ZKPrsIP+tG4lYB8L4SrYJf4MgfoJaKQ8ed
+2jsd42MegvOB2vdMyLgkf7EM/9DpE4BLpAy2mNd1AccL9YQ+0ezruxh6sYklJWGe
+2bHEbZk0duoNPsA87ZNKfFVV2cNVwSg/22HHjGieMUyPIwyGIzsHId8XiwXpZhLX
+VyacOVRd0SjTWK5Pxj6g21NrrcMXvFeCbveucf2ljKVxBVSbQKt67YlXxd9nLZjN
+zHnJWzDwlWXbyvxheLVVGEo0cqRbhxYMxXd9dM01EXJmIWqS8t0+aw90KKPFITNv
+qpxXnF5JJm1CzeBDtpmfepDupUR1bWansOd0sUuiDF/H1UcDiuBUC643RET1vjhv
+MllsShSeC6KGm3WwE0bhcvA9IdZC8CA3Btzw2J9aJO1gbVZ6vRkH+21cfR07so4N
+yXgprQ==
+-----END CERTIFICATE-----
diff --git a/build/apex/ld.config.txt b/build/apex/ld.config.txt
index d0145e4..f624bd1 100644
--- a/build/apex/ld.config.txt
+++ b/build/apex/ld.config.txt
@@ -30,9 +30,26 @@
namespace.platform.asan.search.paths = /data/asan/system/${LIB}
namespace.platform.links = default
namespace.platform.link.default.shared_libs = libart.so:libartd.so
+namespace.platform.link.default.shared_libs += libdexfile_external.so
namespace.platform.link.default.shared_libs += libnativebridge.so
namespace.platform.link.default.shared_libs += libnativehelper.so
namespace.platform.link.default.shared_libs += libnativeloader.so
+namespace.platform.link.default.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.platform.link.default.shared_libs += libpac.so
+
+# /system/lib/libc.so, etc are symlinks to /bionic/lib/libc.so, etc.
+# Add /bionic/lib to the permitted paths because linker uses realpath(3)
+# to check the accessibility of the lib. We could add this to search.paths
+# instead but that makes the resolution of bionic libs be dependent on
+# the order of /system/lib and /bionic/lib in search.paths. If /bionic/lib
+# is after /system/lib, then /bionic/lib is never tried because libc.so
+# is always found in /system/lib but fails to pass the accessibility test
+# because of its realpath. It's better to not depend on the ordering if
+# possible.
+namespace.platform.permitted.paths = /bionic/${LIB}
+namespace.platform.asan.permitted.paths = /bionic/${LIB}
# Note that we don't need to link the default namespace with conscrypt:
# the runtime Java code and binaries do not explicitly load native libraries
@@ -50,7 +67,8 @@
namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = platform
+namespace.conscrypt.links = runtime,platform
+namespace.conscrypt.link.runtime.shared_libs = libjavacore.so
namespace.conscrypt.link.platform.shared_libs = libc.so
namespace.conscrypt.link.platform.shared_libs += libm.so
namespace.conscrypt.link.platform.shared_libs += libdl.so
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index 84c0f4f..95c1de9 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -17,6 +17,8 @@
# Run Android Runtime APEX tests.
+SCRIPT_DIR=$(dirname $0)
+
# Status of whole test script.
exit_status=0
# Status of current test suite.
@@ -31,17 +33,17 @@
exit 1
}
-which guestmount >/dev/null && which guestunmount >/dev/null && which virt-filesystems >/dev/null \
- || die "This script requires 'guestmount', 'guestunmount',
-and 'virt-filesystems' from libguestfs. On Debian-based systems, these tools
-can be installed with:
-
- sudo apt-get install libguestfs-tools
-"
-
[[ -n "$ANDROID_PRODUCT_OUT" ]] \
|| die "You need to source and lunch before you can use this script."
+[[ -n "$ANDROID_HOST_OUT" ]] \
+ || die "You need to source and lunch before you can use this script."
+
+if [ ! -e "$ANDROID_HOST_OUT/bin/debugfs" ] ; then
+ say "Could not find debugfs, building now."
+ make debugfs-host || die "Cannot build debugfs"
+fi
+
# Fail early.
set -e
@@ -75,15 +77,6 @@
shift
done
-if $print_image_tree_p; then
- which tree >/dev/null || die "This script requires the 'tree' tool.
-On Debian-based systems, this can be installed with:
-
- sudo apt-get install tree
-"
-fi
-
-
# build_apex APEX_MODULE
# ----------------------
# Build APEX package APEX_MODULE.
@@ -94,21 +87,22 @@
fi
}
-# maybe_list_apex_contents MOUNT_POINT
-# ------------------------------------
-# If any listing/printing option was used, honor them and display the contents
-# of the APEX payload at MOUNT_POINT.
-function maybe_list_apex_contents {
- local mount_point=$1
+# maybe_list_apex_contents_apex APEX TMPDIR [other]
+function maybe_list_apex_contents_apex {
+ local apex=$1
+ local tmpdir=$2
+ shift 2
- # List the contents of the mounted image using `find` (optional).
+ # List the contents of the apex in list form.
if $list_image_files_p; then
- say "Listing image files" && find "$mount_point"
+ say "Listing image files"
+ $SCRIPT_DIR/art_apex_test.py --list --tmpdir "$tmpdir" $@ $apex
fi
- # List the contents of the mounted image using `tree` (optional).
+ # List the contents of the apex in tree form.
if $print_image_tree_p; then
- say "Printing image tree" && ls -ld "$mount_point" && tree -aph --du "$mount_point"
+ say "Printing image tree"
+ $SCRIPT_DIR/art_apex_test.py --tree --tmpdir "$tmpdir" $@ $apex
fi
}
@@ -118,129 +112,11 @@
exit_status=1
}
-function check_file {
- [[ -f "$mount_point/$1" ]] || fail_check "Cannot find file '$1' in mounted image"
-}
-
-function check_binary {
- [[ -x "$mount_point/bin/$1" ]] || fail_check "Cannot find binary '$1' in mounted image"
-}
-
-function check_multilib_binary {
- # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
- # the precision of this test?
- [[ -x "$mount_point/bin/${1}32" ]] || [[ -x "$mount_point/bin/${1}64" ]] \
- || fail_check "Cannot find binary '$1' in mounted image"
-}
-
-function check_binary_symlink {
- [[ -h "$mount_point/bin/$1" ]] || fail_check "Cannot find symbolic link '$1' in mounted image"
-}
-
-function check_library {
- # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
- # the precision of this test?
- [[ -f "$mount_point/lib/$1" ]] || [[ -f "$mount_point/lib64/$1" ]] \
- || fail_check "Cannot find library '$1' in mounted image"
-}
-
-function check_java_library {
- [[ -x "$mount_point/javalib/$1" ]] || fail_check "Cannot find java library '$1' in mounted image"
-}
-
-# Check contents of APEX payload located in `$mount_point`.
-function check_release_contents {
- # Check that the mounted image contains an APEX manifest.
- check_file apex_manifest.json
-
- # Check that the mounted image contains ART base binaries.
- check_multilib_binary dalvikvm
- # TODO: Does not work yet (b/119942078).
- : check_binary_symlink dalvikvm
- check_binary dex2oat
- check_binary dexoptanalyzer
- check_binary profman
-
- # oatdump is only in device apex's due to build rules
- # TODO: Check for it when it is also built for host.
- : check_binary oatdump
-
- # Check that the mounted image contains Android Runtime libraries.
- check_library libart-compiler.so
- check_library libart-dexlayout.so
- check_library libart.so
- check_library libartbase.so
- check_library libdexfile.so
- check_library libopenjdkjvm.so
- check_library libopenjdkjvmti.so
- check_library libprofile.so
- # Check that the mounted image contains Android Core libraries.
- check_library "libexpat${host_suffix}.so"
- check_library libjavacore.so
- check_library libjavacrypto.so
- check_library libopenjdk.so
- check_library "libz${host_suffix}.so"
- check_library libziparchive.so
- # Check that the mounted image contains additional required libraries.
- check_library libadbconnection.so
-
- # TODO: Should we check for other libraries, such as:
- #
- # libbacktrace.so
- # libbase.so
- # liblog.so
- # libsigchain.so
- # libtombstoned_client.so
- # libunwindstack.so
- # libvixl.so
- # libvixld.so
- # ...
- #
- # ?
-
- # TODO: Enable for host
- if [ $1 != "com.android.runtime.host" ]; then
- check_java_library core-oj.jar
- check_java_library core-libart.jar
- check_java_library okhttp.jar
- check_java_library bouncycastle.jar
- check_java_library apache-xml.jar
- fi
-}
-
-# Check debug contents of APEX payload located in `$mount_point`.
-function check_debug_contents {
- # Check that the mounted image contains ART tools binaries.
- check_binary dexdiag
- check_binary dexdump
- check_binary dexlist
-
- # Check that the mounted image contains ART debug binaries.
- check_binary dex2oatd
- check_binary dexoptanalyzerd
- check_binary profmand
-
- # Check that the mounted image contains Android Runtime debug libraries.
- check_library libartbased.so
- check_library libartd-compiler.so
- check_library libartd-dexlayout.so
- check_library libartd.so
- check_library libdexfiled.so
- check_library libopenjdkjvmd.so
- check_library libopenjdkjvmtid.so
- check_library libprofiled.so
- # Check that the mounted image contains Android Core debug libraries.
- check_library libopenjdkd.so
- # Check that the mounted image contains additional required debug libraries.
- check_library libadbconnectiond.so
-}
-
# Testing target (device) APEX packages.
# ======================================
# Clean-up.
function cleanup_target {
- guestunmount "$mount_point"
rm -rf "$work_dir"
}
@@ -251,34 +127,6 @@
cleanup_target
}
-# setup_target_apex APEX_MODULE MOUNT_POINT
-# -----------------------------------------
-# Extract image from target APEX_MODULE and mount it in MOUNT_POINT.
-function setup_target_apex {
- local apex_module=$1
- local mount_point=$2
- local system_apexdir="$ANDROID_PRODUCT_OUT/system/apex"
- local apex_package="$system_apexdir/$apex_module.apex"
-
- say "Extracting and mounting image"
-
- # Extract the payload from the Android Runtime APEX.
- local image_filename="apex_payload.img"
- unzip -q "$apex_package" "$image_filename" -d "$work_dir"
- mkdir "$mount_point"
- local image_file="$work_dir/$image_filename"
-
- # Check filesystems in the image.
- local image_filesystems="$work_dir/image_filesystems"
- virt-filesystems -a "$image_file" >"$image_filesystems"
- # We expect a single partition (/dev/sda) in the image.
- local partition="/dev/sda"
- echo "$partition" | cmp "$image_filesystems" -
-
- # Mount the image from the Android Runtime APEX.
- guestmount -a "$image_file" -m "$partition" --ro "$mount_point"
-}
-
# Testing release APEX package (com.android.runtime.release).
# -----------------------------------------------------------
@@ -288,23 +136,23 @@
say "Processing APEX package $apex_module"
work_dir=$(mktemp -d)
-mount_point="$work_dir/image"
-host_suffix=""
trap finish_target EXIT
# Build the APEX package (optional).
build_apex "$apex_module"
-
-# Set up APEX package.
-setup_target_apex "$apex_module" "$mount_point"
+apex_path="$ANDROID_PRODUCT_OUT/system/apex/${apex_module}.apex"
# List the contents of the APEX image (optional).
-maybe_list_apex_contents "$mount_point"
+maybe_list_apex_contents_apex $apex_path $work_dir --debugfs $ANDROID_HOST_OUT/bin/debugfs
# Run tests on APEX package.
say "Checking APEX package $apex_module"
-check_release_contents "$apex_module"
+$SCRIPT_DIR/art_apex_test.py \
+ --tmpdir $work_dir \
+ --debugfs $ANDROID_HOST_OUT/bin/debugfs \
+ $apex_path \
+ || fail_check "Release checks failed"
# Clean up.
trap - EXIT
@@ -322,27 +170,24 @@
say "Processing APEX package $apex_module"
work_dir=$(mktemp -d)
-mount_point="$work_dir/image"
-host_suffix=""
trap finish_target EXIT
# Build the APEX package (optional).
build_apex "$apex_module"
-
-# Set up APEX package.
-setup_target_apex "$apex_module" "$mount_point"
+apex_path="$ANDROID_PRODUCT_OUT/system/apex/${apex_module}.apex"
# List the contents of the APEX image (optional).
-maybe_list_apex_contents "$mount_point"
+maybe_list_apex_contents_apex $apex_path $work_dir --debugfs $ANDROID_HOST_OUT/bin/debugfs
# Run tests on APEX package.
say "Checking APEX package $apex_module"
-check_release_contents "$apex_module"
-check_debug_contents
-# Check for files pulled in from debug target-only oatdump.
-check_binary oatdump
-check_library libart-disassembler.so
+$SCRIPT_DIR/art_apex_test.py \
+ --tmpdir $work_dir \
+ --debugfs $ANDROID_HOST_OUT/bin/debugfs \
+ --debug \
+ $apex_path \
+ || fail_check "Debug checks failed"
# Clean up.
trap - EXIT
@@ -367,51 +212,30 @@
cleanup_host
}
-# setup_host_apex APEX_MODULE MOUNT_POINT
-# ---------------------------------------
-# Extract Zip file from host APEX_MODULE and extract it in MOUNT_POINT.
-function setup_host_apex {
- local apex_module=$1
- local mount_point=$2
- local system_apexdir="$ANDROID_HOST_OUT/apex"
- local apex_package="$system_apexdir/$apex_module.zipapex"
-
- say "Extracting payload"
-
- # Extract the payload from the Android Runtime APEX.
- local image_filename="apex_payload.zip"
- unzip -q "$apex_package" "$image_filename" -d "$work_dir"
- mkdir "$mount_point"
- local image_file="$work_dir/$image_filename"
-
- # Unzipping the payload
- unzip -q "$image_file" -d "$mount_point"
-}
-
apex_module="com.android.runtime.host"
test_status=0
say "Processing APEX package $apex_module"
work_dir=$(mktemp -d)
-mount_point="$work_dir/zip"
-host_suffix="-host"
trap finish_host EXIT
# Build the APEX package (optional).
build_apex "$apex_module"
-
-# Set up APEX package.
-setup_host_apex "$apex_module" "$mount_point"
+apex_path="$ANDROID_HOST_OUT/apex/${apex_module}.zipapex"
# List the contents of the APEX image (optional).
-maybe_list_apex_contents "$mount_point"
+maybe_list_apex_contents_apex $apex_path $work_dir --host
# Run tests on APEX package.
say "Checking APEX package $apex_module"
-check_release_contents "$apex_module"
-check_debug_contents
+$SCRIPT_DIR/art_apex_test.py \
+ --tmpdir $work_dir \
+ --host \
+ --debug \
+ $apex_path \
+ || fail_check "Debug checks failed"
# Clean up.
trap - EXIT
diff --git a/build/art.go b/build/art.go
index 22f6410..4db8da2 100644
--- a/build/art.go
+++ b/build/art.go
@@ -253,8 +253,10 @@
ctx.AppendProperties(p)
}
+var testMapKey = android.NewOnceKey("artTests")
+
func testMap(config android.Config) map[string][]string {
- return config.Once("artTests", func() interface{} {
+ return config.Once(testMapKey, func() interface{} {
return make(map[string][]string)
}).(map[string][]string)
}
@@ -298,11 +300,11 @@
// 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", artApexBundleFactory)
+ android.RegisterModuleType("art_apex_test", artTestApexBundleFactory)
}
-func artApexBundleFactory() android.Module {
- module := apex.ApexBundleFactory()
+func artTestApexBundleFactory() android.Module {
+ module := apex.ApexBundleFactory( /*testApex*/ true)
android.AddLoadHook(module, func(ctx android.LoadHookContext) {
if envTrue(ctx, "HOST_PREFER_32_BIT") {
type props struct {
@@ -348,26 +350,7 @@
func libartDefaultsFactory() android.Module {
c := &codegenProperties{}
module := cc.DefaultsFactory(c)
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- codegen(ctx, c, true)
-
- type props struct {
- Target struct {
- Android struct {
- Shared_libs []string
- }
- }
- }
-
- p := &props{}
- // TODO: express this in .bp instead b/79671158
- if !envTrue(ctx, "ART_TARGET_LINUX") {
- p.Target.Android.Shared_libs = []string{
- "libmetricslogger",
- }
- }
- ctx.AppendProperties(p)
- })
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) { codegen(ctx, c, true) })
return module
}
@@ -375,27 +358,7 @@
func libartStaticDefaultsFactory() android.Module {
c := &codegenProperties{}
module := cc.DefaultsFactory(c)
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- codegen(ctx, c, true)
-
- type props struct {
- Target struct {
- Android struct {
- Static_libs []string
- }
- }
- }
-
- p := &props{}
- // TODO: express this in .bp instead b/79671158
- if !envTrue(ctx, "ART_TARGET_LINUX") {
- p.Target.Android.Static_libs = []string{
- "libmetricslogger",
- "libstatssocket",
- }
- }
- ctx.AppendProperties(p)
- })
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) { codegen(ctx, c, true) })
return module
}
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
index 81a2179..90be30b 100644
--- a/cmdline/cmdline.h
+++ b/cmdline/cmdline.h
@@ -23,13 +23,14 @@
#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/stringpiece.h"
+#include "base/string_view_cpp20.h"
#include "noop_compiler_callbacks.h"
#include "runtime.h"
@@ -151,14 +152,15 @@
std::string error_msg;
for (int i = 0; i < argc; i++) {
- const StringPiece option(argv[i]);
- if (option.starts_with("--boot-image=")) {
- boot_image_location_ = option.substr(strlen("--boot-image=")).data();
- } else if (option.starts_with("--instruction-set=")) {
- StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
- instruction_set_ = GetInstructionSetFromString(instruction_set_str.data());
+ 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.data());
+ fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str);
PrintUsage();
return false;
}
@@ -170,8 +172,8 @@
}
++i;
runtime_args_.push_back(argv[i]);
- } else if (option.starts_with("--output=")) {
- output_name_ = option.substr(strlen("--output=")).ToString();
+ } 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()) {
@@ -181,7 +183,7 @@
}
os_ = out_.get();
} else {
- ParseStatus parse_status = ParseCustom(option, &error_msg);
+ ParseStatus parse_status = ParseCustom(raw_option, option.length(), &error_msg);
if (parse_status == kParseUnknownArgument) {
fprintf(stderr, "Unknown argument %s\n", option.data());
@@ -315,7 +317,8 @@
}
protected:
- virtual ParseStatus ParseCustom(const StringPiece& option ATTRIBUTE_UNUSED,
+ virtual ParseStatus ParseCustom(const char* raw_option ATTRIBUTE_UNUSED,
+ size_t raw_option_length ATTRIBUTE_UNUSED,
std::string* error_msg ATTRIBUTE_UNUSED) {
return kParseUnknownArgument;
}
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 0d92b05..0ebaa5f 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -182,7 +182,6 @@
generated_sources: ["art_compiler_operator_srcs"],
shared_libs: [
"libbase",
- "libcutils", // for atrace.
],
include_dirs: ["art/disassembler"],
header_libs: [
@@ -197,7 +196,6 @@
name: "libart-compiler_static_base_defaults",
static_libs: [
"libbase",
- "libcutils",
],
}
@@ -256,9 +254,10 @@
},
shared_libs: [
"libart",
+ "libartbase",
+ "libartpalette",
"libprofile",
"libdexfile",
- "libartbase",
],
target: {
@@ -317,10 +316,11 @@
},
},
shared_libs: [
+ "libartbased",
"libartd",
+ "libartpalette",
"libprofiled",
"libdexfiled",
- "libartbased",
],
}
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index 658bdb3..2fc81c9 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -32,8 +32,6 @@
namespace art {
-constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT;
-
class CFITest : public dwarf::DwarfTest {
public:
void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str,
@@ -50,18 +48,14 @@
// Pretty-print CFI opcodes.
constexpr bool is64bit = false;
dwarf::DebugFrameOpCodeWriter<> initial_opcodes;
- dwarf::WriteCIE(is64bit, dwarf::Reg(8), initial_opcodes, kCFIFormat, &debug_frame_data_);
+ dwarf::WriteCIE(is64bit, dwarf::Reg(8), initial_opcodes, &debug_frame_data_);
std::vector<uintptr_t> debug_frame_patches;
dwarf::WriteFDE(is64bit,
- /* section_address= */ 0,
- /* cie_address= */ 0,
+ /* cie_pointer= */ 0,
/* code_address= */ 0,
actual_asm.size(),
actual_cfi,
- kCFIFormat,
- /* buffer_address= */ 0,
- &debug_frame_data_,
- &debug_frame_patches);
+ &debug_frame_data_);
ReformatCfi(Objdump(false, "-W"), &lines);
// Pretty-print assembly.
const uint8_t* asm_base = actual_asm.data();
diff --git a/compiler/debug/dwarf/dwarf_constants.h b/compiler/debug/dwarf/dwarf_constants.h
index 96f805e..7beb1fa 100644
--- a/compiler/debug/dwarf/dwarf_constants.h
+++ b/compiler/debug/dwarf/dwarf_constants.h
@@ -680,14 +680,6 @@
DW_EH_PE_aligned = 0x50,
};
-enum CFIFormat : uint8_t {
- // This is the original format as defined by the specification.
- // It is used for the .debug_frame section.
- DW_DEBUG_FRAME_FORMAT,
- // Slightly modified format used for the .eh_frame section.
- DW_EH_FRAME_FORMAT
-};
-
} // namespace dwarf
} // namespace art
diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
index 6512314..212fd63 100644
--- a/compiler/debug/dwarf/dwarf_test.cc
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -29,8 +29,6 @@
// Run the tests only on host since we need objdump.
#ifndef ART_TARGET_ANDROID
-constexpr CFIFormat kCFIFormat = DW_DEBUG_FRAME_FORMAT;
-
TEST_F(DwarfTest, DebugFrame) {
const bool is64bit = false;
@@ -122,31 +120,30 @@
DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)");
DebugFrameOpCodeWriter<> initial_opcodes;
- WriteCIE(is64bit, Reg(is64bit ? 16 : 8),
- initial_opcodes, kCFIFormat, &debug_frame_data_);
- std::vector<uintptr_t> debug_frame_patches;
- std::vector<uintptr_t> expected_patches = { 28 };
- WriteFDE(is64bit, 0, 0, 0x01000000, 0x01000000, ArrayRef<const uint8_t>(*opcodes.data()),
- kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches);
+ WriteCIE(is64bit, Reg(is64bit ? 16 : 8), initial_opcodes, &debug_frame_data_);
+ WriteFDE(is64bit,
+ /* cie_pointer= */ 0,
+ 0x01000000,
+ 0x01000000,
+ ArrayRef<const uint8_t>(*opcodes.data()),
+ &debug_frame_data_);
- EXPECT_EQ(expected_patches, debug_frame_patches);
CheckObjdumpOutput(is64bit, "-W");
}
TEST_F(DwarfTest, DebugFrame64) {
constexpr bool is64bit = true;
DebugFrameOpCodeWriter<> initial_opcodes;
- WriteCIE(is64bit, Reg(16),
- initial_opcodes, kCFIFormat, &debug_frame_data_);
+ WriteCIE(is64bit, Reg(16), initial_opcodes, &debug_frame_data_);
DebugFrameOpCodeWriter<> opcodes;
- std::vector<uintptr_t> debug_frame_patches;
- std::vector<uintptr_t> expected_patches = { 32 };
- WriteFDE(is64bit, 0, 0, 0x0100000000000000, 0x0200000000000000,
+ WriteFDE(is64bit,
+ /* cie_pointer= */ 0,
+ 0x0100000000000000,
+ 0x0200000000000000,
ArrayRef<const uint8_t>(*opcodes.data()),
- kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches);
+ &debug_frame_data_);
DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
- EXPECT_EQ(expected_patches, debug_frame_patches);
CheckObjdumpOutput(is64bit, "-W");
}
@@ -176,12 +173,13 @@
DW_CHECK_NEXT("DW_CFA_offset: r14 (r14)");
DW_CHECK_NEXT("DW_CFA_offset: r15 (r15)");
DebugFrameOpCodeWriter<> initial_opcodes;
- WriteCIE(is64bit, Reg(16),
- initial_opcodes, kCFIFormat, &debug_frame_data_);
- std::vector<uintptr_t> debug_frame_patches;
- WriteFDE(is64bit, 0, 0, 0x0100000000000000, 0x0200000000000000,
+ WriteCIE(is64bit, Reg(16), initial_opcodes, &debug_frame_data_);
+ WriteFDE(is64bit,
+ /* cie_pointer= */ 0,
+ 0x0100000000000000,
+ 0x0200000000000000,
ArrayRef<const uint8_t>(*opcodes.data()),
- kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches);
+ &debug_frame_data_);
CheckObjdumpOutput(is64bit, "-W");
}
@@ -236,12 +234,8 @@
DW_CHECK_NEXT("Entry\tDir\tTime\tSize\tName");
DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c");
- std::vector<uintptr_t> debug_line_patches;
- std::vector<uintptr_t> expected_patches = { 87 };
- WriteDebugLineTable(include_directories, files, opcodes,
- 0, &debug_line_data_, &debug_line_patches);
+ WriteDebugLineTable(include_directories, files, opcodes, &debug_line_data_);
- EXPECT_EQ(expected_patches, debug_line_patches);
CheckObjdumpOutput(is64bit, "-W");
}
@@ -276,9 +270,7 @@
std::vector<std::string> directories;
std::vector<FileEntry> files = { { "file.c", 0, 1000, 2000 } };
- std::vector<uintptr_t> debug_line_patches;
- WriteDebugLineTable(directories, files, opcodes,
- 0, &debug_line_data_, &debug_line_patches);
+ WriteDebugLineTable(directories, files, opcodes, &debug_line_data_);
CheckObjdumpOutput(is64bit, "-W -WL");
}
@@ -332,12 +324,8 @@
DW_CHECK_NEXT("DW_AT_high_pc DW_FORM_addr");
DW_CHECK("3 DW_TAG_compile_unit [no children]");
- std::vector<uintptr_t> debug_info_patches;
- std::vector<uintptr_t> expected_patches = { 16, 20, 29, 33, 42, 46 };
- dwarf::WriteDebugInfoCU(/* debug_abbrev_offset= */ 0, info,
- 0, &debug_info_data_, &debug_info_patches);
+ dwarf::WriteDebugInfoCU(/* debug_abbrev_offset= */ 0, info, &debug_info_data_);
- EXPECT_EQ(expected_patches, debug_info_patches);
CheckObjdumpOutput(is64bit, "-W");
}
diff --git a/compiler/debug/dwarf/headers.h b/compiler/debug/dwarf/headers.h
index 4a27178..3cc8ad8 100644
--- a/compiler/debug/dwarf/headers.h
+++ b/compiler/debug/dwarf/headers.h
@@ -41,14 +41,13 @@
void WriteCIE(bool is64bit,
Reg return_address_register,
const DebugFrameOpCodeWriter<Vector>& opcodes,
- CFIFormat format,
std::vector<uint8_t>* buffer) {
static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
Writer<> writer(buffer);
size_t cie_header_start_ = writer.data()->size();
writer.PushUint32(0); // Length placeholder.
- writer.PushUint32((format == DW_EH_FRAME_FORMAT) ? 0 : 0xFFFFFFFF); // CIE id.
+ writer.PushUint32(0xFFFFFFFF); // CIE id.
writer.PushUint8(1); // Version.
writer.PushString("zR");
writer.PushUleb128(DebugFrameOpCodeWriter<Vector>::kCodeAlignmentFactor);
@@ -56,19 +55,9 @@
writer.PushUleb128(return_address_register.num()); // ubyte in DWARF2.
writer.PushUleb128(1); // z: Augmentation data size.
if (is64bit) {
- if (format == DW_EH_FRAME_FORMAT) {
- writer.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata8); // R: Pointer encoding.
- } else {
- DCHECK(format == DW_DEBUG_FRAME_FORMAT);
- writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata8); // R: Pointer encoding.
- }
+ writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata8); // R: Pointer encoding.
} else {
- if (format == DW_EH_FRAME_FORMAT) {
- writer.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata4); // R: Pointer encoding.
- } else {
- DCHECK(format == DW_DEBUG_FRAME_FORMAT);
- writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata4); // R: Pointer encoding.
- }
+ writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata4); // R: Pointer encoding.
}
writer.PushData(opcodes.data());
writer.Pad(is64bit ? 8 : 4);
@@ -78,39 +67,16 @@
// Write frame description entry (FDE) to .debug_frame or .eh_frame section.
inline
void WriteFDE(bool is64bit,
- uint64_t section_address, // Absolute address of the section.
- uint64_t cie_address, // Absolute address of last CIE.
+ uint64_t cie_pointer, // Offset of relevant CIE in debug_frame setcion.
uint64_t code_address,
uint64_t code_size,
const ArrayRef<const uint8_t>& opcodes,
- CFIFormat format,
- uint64_t buffer_address, // Address of buffer in linked application.
- std::vector<uint8_t>* buffer,
- std::vector<uintptr_t>* patch_locations) {
- CHECK_GE(cie_address, section_address);
- CHECK_GE(buffer_address, section_address);
-
+ /*inout*/ std::vector<uint8_t>* buffer) {
Writer<> writer(buffer);
size_t fde_header_start = writer.data()->size();
writer.PushUint32(0); // Length placeholder.
- if (format == DW_EH_FRAME_FORMAT) {
- uint32_t cie_pointer = (buffer_address + buffer->size()) - cie_address;
- writer.PushUint32(cie_pointer);
- } else {
- DCHECK(format == DW_DEBUG_FRAME_FORMAT);
- uint32_t cie_pointer = cie_address - section_address;
- writer.PushUint32(cie_pointer);
- }
- if (format == DW_EH_FRAME_FORMAT) {
- // .eh_frame encodes the location as relative address.
- code_address -= buffer_address + buffer->size();
- } else {
- DCHECK(format == DW_DEBUG_FRAME_FORMAT);
- // Relocate code_address if it has absolute value.
- if (patch_locations != nullptr) {
- patch_locations->push_back(buffer_address + buffer->size() - section_address);
- }
- }
+ writer.PushUint32(cie_pointer);
+ // Relocate code_address if it has absolute value.
if (is64bit) {
writer.PushUint64(code_address);
writer.PushUint64(code_size);
@@ -124,37 +90,11 @@
writer.UpdateUint32(fde_header_start, writer.data()->size() - fde_header_start - 4);
}
-// Read singe FDE entry from 'data' (which is advanced).
-template<typename Addr>
-bool ReadFDE(const uint8_t** data, Addr* addr, Addr* size, ArrayRef<const uint8_t>* opcodes) {
- struct Header {
- uint32_t length;
- int32_t cie_pointer;
- Addr addr;
- Addr size;
- uint8_t augmentaion;
- uint8_t opcodes[];
- } PACKED(1);
- const Header* header = reinterpret_cast<const Header*>(*data);
- const size_t length = 4 + header->length;
- *data += length;
- if (header->cie_pointer == -1) {
- return false; // Not an FDE entry.
- }
- DCHECK_EQ(header->cie_pointer, 0); // Expects single CIE. Assumes DW_DEBUG_FRAME_FORMAT.
- *addr = header->addr;
- *size = header->size;
- *opcodes = ArrayRef<const uint8_t>(header->opcodes, length - offsetof(Header, opcodes));
- return true;
-}
-
// Write compilation unit (CU) to .debug_info section.
template<typename Vector>
void WriteDebugInfoCU(uint32_t debug_abbrev_offset,
const DebugInfoEntryWriter<Vector>& entries,
- size_t debug_info_offset, // offset from start of .debug_info.
- std::vector<uint8_t>* debug_info,
- std::vector<uintptr_t>* debug_info_patches) {
+ std::vector<uint8_t>* debug_info) {
static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
Writer<> writer(debug_info);
@@ -167,10 +107,6 @@
DCHECK_EQ(entries_offset, DebugInfoEntryWriter<Vector>::kCompilationUnitHeaderSize);
writer.PushData(entries.data());
writer.UpdateUint32(start, writer.data()->size() - start - 4);
- // Copy patch locations and make them relative to .debug_info section.
- for (uintptr_t patch_location : entries.GetPatchLocations()) {
- debug_info_patches->push_back(debug_info_offset + entries_offset + patch_location);
- }
}
struct FileEntry {
@@ -185,9 +121,7 @@
void WriteDebugLineTable(const std::vector<std::string>& include_directories,
const std::vector<FileEntry>& files,
const DebugLineOpCodeWriter<Vector>& opcodes,
- size_t debug_line_offset, // offset from start of .debug_line.
- std::vector<uint8_t>* debug_line,
- std::vector<uintptr_t>* debug_line_patches) {
+ std::vector<uint8_t>* debug_line) {
static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
Writer<> writer(debug_line);
@@ -218,13 +152,8 @@
}
writer.PushUint8(0); // Terminate file list.
writer.UpdateUint32(header_length_pos, writer.data()->size() - header_length_pos - 4);
- size_t opcodes_offset = writer.data()->size();
writer.PushData(opcodes.data());
writer.UpdateUint32(header_start, writer.data()->size() - header_start - 4);
- // Copy patch locations and make them relative to .debug_line section.
- for (uintptr_t patch_location : opcodes.GetPatchLocations()) {
- debug_line_patches->push_back(debug_line_offset + opcodes_offset + patch_location);
- }
}
} // namespace dwarf
diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h
index 27b70c8..c5fa647 100644
--- a/compiler/debug/elf_debug_frame_writer.h
+++ b/compiler/debug/elf_debug_frame_writer.h
@@ -29,9 +29,9 @@
namespace art {
namespace debug {
-static void WriteCIE(InstructionSet isa,
- dwarf::CFIFormat format,
- std::vector<uint8_t>* buffer) {
+static constexpr bool kWriteDebugFrameHdr = false;
+
+static void WriteCIE(InstructionSet isa, /*inout*/ std::vector<uint8_t>* buffer) {
using Reg = dwarf::Reg;
// Scratch registers should be marked as undefined. This tells the
// debugger that its value in the previous frame is not recoverable.
@@ -58,7 +58,7 @@
}
}
auto return_reg = Reg::ArmCore(14); // R14(LR).
- WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ WriteCIE(is64bit, return_reg, opcodes, buffer);
return;
}
case InstructionSet::kArm64: {
@@ -81,7 +81,7 @@
}
}
auto return_reg = Reg::Arm64Core(30); // R30(LR).
- WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ WriteCIE(is64bit, return_reg, opcodes, buffer);
return;
}
case InstructionSet::kMips:
@@ -105,7 +105,7 @@
}
}
auto return_reg = Reg::MipsCore(31); // R31(RA).
- WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ WriteCIE(is64bit, return_reg, opcodes, buffer);
return;
}
case InstructionSet::kX86: {
@@ -131,7 +131,7 @@
}
}
auto return_reg = Reg::X86Core(8); // R8(EIP).
- WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ WriteCIE(is64bit, return_reg, opcodes, buffer);
return;
}
case InstructionSet::kX86_64: {
@@ -157,7 +157,7 @@
}
}
auto return_reg = Reg::X86_64Core(16); // R16(RIP).
- WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ WriteCIE(is64bit, return_reg, opcodes, buffer);
return;
}
case InstructionSet::kNone:
@@ -169,10 +169,7 @@
template<typename ElfTypes>
void WriteCFISection(linker::ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
- dwarf::CFIFormat format,
- bool write_oat_patches) {
- CHECK(format == dwarf::DW_DEBUG_FRAME_FORMAT || format == dwarf::DW_EH_FRAME_FORMAT);
+ const ArrayRef<const MethodDebugInfo>& method_infos) {
typedef typename ElfTypes::Addr Elf_Addr;
// The methods can be written in any order.
@@ -199,80 +196,54 @@
});
std::vector<uint32_t> binary_search_table;
- std::vector<uintptr_t> patch_locations;
- if (format == dwarf::DW_EH_FRAME_FORMAT) {
+ if (kWriteDebugFrameHdr) {
binary_search_table.reserve(2 * sorted_method_infos.size());
- } else {
- patch_locations.reserve(sorted_method_infos.size());
}
- // Write .eh_frame/.debug_frame section.
- const bool is_debug_frame = format == dwarf::DW_DEBUG_FRAME_FORMAT;
- auto* cfi_section = (is_debug_frame ? builder->GetDebugFrame() : builder->GetEhFrame());
+ // Write .debug_frame section.
+ auto* cfi_section = builder->GetDebugFrame();
{
cfi_section->Start();
const bool is64bit = Is64BitInstructionSet(builder->GetIsa());
- const Elf_Addr cfi_address = (is_debug_frame ? 0 : cfi_section->GetAddress());
- const Elf_Addr cie_address = cfi_address;
- Elf_Addr buffer_address = cfi_address;
std::vector<uint8_t> buffer; // Small temporary buffer.
- WriteCIE(builder->GetIsa(), format, &buffer);
+ WriteCIE(builder->GetIsa(), &buffer);
cfi_section->WriteFully(buffer.data(), buffer.size());
- buffer_address += buffer.size();
buffer.clear();
for (const MethodDebugInfo* mi : sorted_method_infos) {
DCHECK(!mi->deduped);
DCHECK(!mi->cfi.empty());
const Elf_Addr code_address = mi->code_address +
(mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0);
- if (format == dwarf::DW_EH_FRAME_FORMAT) {
+ if (kWriteDebugFrameHdr) {
binary_search_table.push_back(dchecked_integral_cast<uint32_t>(code_address));
- binary_search_table.push_back(dchecked_integral_cast<uint32_t>(buffer_address));
+ binary_search_table.push_back(cfi_section->GetPosition());
}
- WriteFDE(is64bit, cfi_address, cie_address,
- code_address, mi->code_size,
- mi->cfi, format, buffer_address, &buffer,
- &patch_locations);
+ dwarf::WriteFDE(is64bit,
+ /* cie_pointer= */ 0,
+ code_address,
+ mi->code_size,
+ mi->cfi,
+ &buffer);
cfi_section->WriteFully(buffer.data(), buffer.size());
- buffer_address += buffer.size();
buffer.clear();
}
cfi_section->End();
}
- if (format == dwarf::DW_EH_FRAME_FORMAT) {
- auto* header_section = builder->GetEhFrameHdr();
- header_section->Start();
- uint32_t header_address = dchecked_integral_cast<int32_t>(header_section->GetAddress());
- // Write .eh_frame_hdr section.
- std::vector<uint8_t> buffer;
- dwarf::Writer<> header(&buffer);
+ if (kWriteDebugFrameHdr) {
+ std::vector<uint8_t> header_buffer;
+ dwarf::Writer<> header(&header_buffer);
header.PushUint8(1); // Version.
- // Encoding of .eh_frame pointer - libunwind does not honor datarel here,
- // so we have to use pcrel which means relative to the pointer's location.
- header.PushUint8(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4);
- // Encoding of binary search table size.
- header.PushUint8(dwarf::DW_EH_PE_udata4);
- // Encoding of binary search table addresses - libunwind supports only this
- // specific combination, which means relative to the start of .eh_frame_hdr.
- header.PushUint8(dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4);
- // .eh_frame pointer
- header.PushInt32(cfi_section->GetAddress() - (header_address + 4u));
- // Binary search table size (number of entries).
+ header.PushUint8(dwarf::DW_EH_PE_omit); // Encoding of .eh_frame pointer - none.
+ header.PushUint8(dwarf::DW_EH_PE_udata4); // Encoding of binary search table size.
+ header.PushUint8(dwarf::DW_EH_PE_udata4); // Encoding of binary search table data.
header.PushUint32(dchecked_integral_cast<uint32_t>(binary_search_table.size()/2));
- header_section->WriteFully(buffer.data(), buffer.size());
- // Binary search table.
- for (size_t i = 0; i < binary_search_table.size(); i++) {
- // Make addresses section-relative since we know the header address now.
- binary_search_table[i] -= header_address;
- }
+
+ auto* header_section = builder->GetDebugFrameHdr();
+ header_section->Start();
+ header_section->WriteFully(header_buffer.data(), header_buffer.size());
header_section->WriteFully(binary_search_table.data(), binary_search_table.size());
header_section->End();
- } else {
- if (write_oat_patches) {
- builder->WritePatches(".debug_frame.oat_patches",
- ArrayRef<const uintptr_t>(patch_locations));
- }
}
}
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index a63f241..05a4a3e 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -68,12 +68,8 @@
builder_->GetDebugInfo()->Start();
}
- void End(bool write_oat_patches) {
+ void End() {
builder_->GetDebugInfo()->End();
- if (write_oat_patches) {
- builder_->WritePatches(".debug_info.oat_patches",
- ArrayRef<const uintptr_t>(debug_info_patches_));
- }
builder_->WriteSection(".debug_abbrev", &debug_abbrev_buffer_);
if (!debug_loc_.empty()) {
builder_->WriteSection(".debug_loc", &debug_loc_);
@@ -85,7 +81,6 @@
private:
linker::ElfBuilder<ElfTypes>* builder_;
- std::vector<uintptr_t> debug_info_patches_;
std::vector<uint8_t> debug_abbrev_buffer_;
dwarf::DebugAbbrevWriter<> debug_abbrev_;
std::vector<uint8_t> debug_loc_;
@@ -281,10 +276,9 @@
CHECK_EQ(info_.Depth(), 0);
std::vector<uint8_t> buffer;
buffer.reserve(info_.data()->size() + KB);
- const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition();
// All compilation units share single table which is at the start of .debug_abbrev.
const size_t debug_abbrev_offset = 0;
- WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
+ WriteDebugInfoCU(debug_abbrev_offset, info_, &buffer);
owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
}
@@ -446,10 +440,9 @@
CHECK_EQ(info_.Depth(), 0);
std::vector<uint8_t> buffer;
buffer.reserve(info_.data()->size() + KB);
- const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition();
// All compilation units share single table which is at the start of .debug_abbrev.
const size_t debug_abbrev_offset = 0;
- WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
+ WriteDebugInfoCU(debug_abbrev_offset, info_, &buffer);
owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
}
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 0a13a92..f95912a 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -263,23 +263,17 @@
}
std::vector<uint8_t> buffer;
buffer.reserve(opcodes.data()->size() + KB);
- size_t offset = builder_->GetDebugLine()->GetPosition();
- WriteDebugLineTable(directories, files, opcodes, offset, &buffer, &debug_line_patches_);
+ WriteDebugLineTable(directories, files, opcodes, &buffer);
builder_->GetDebugLine()->WriteFully(buffer.data(), buffer.size());
return buffer.size();
}
- void End(bool write_oat_patches) {
+ void End() {
builder_->GetDebugLine()->End();
- if (write_oat_patches) {
- builder_->WritePatches(".debug_line.oat_patches",
- ArrayRef<const uintptr_t>(debug_line_patches_));
- }
}
private:
linker::ElfBuilder<ElfTypes>* builder_;
- std::vector<uintptr_t> debug_line_patches_;
};
} // namespace debug
diff --git a/compiler/debug/elf_debug_reader.h b/compiler/debug/elf_debug_reader.h
index 91b1b3e..1820e7c 100644
--- a/compiler/debug/elf_debug_reader.h
+++ b/compiler/debug/elf_debug_reader.h
@@ -22,6 +22,9 @@
#include "elf.h"
#include "xz_utils.h"
+#include <map>
+#include <string_view>
+
namespace art {
namespace debug {
@@ -29,75 +32,141 @@
//
// It is the bare minimum needed to read mini-debug-info symbols for unwinding.
// We use it to merge JIT mini-debug-infos together or to prune them after GC.
-// The consumed ELF file comes from ART JIT.
-template <typename ElfTypes, typename VisitSym, typename VisitFde>
-static void ReadElfSymbols(const uint8_t* elf, VisitSym visit_sym, VisitFde visit_fde) {
+template <typename ElfTypes>
+class ElfDebugReader {
+ public:
// Note that the input buffer might be misaligned.
typedef typename ElfTypes::Ehdr ALIGNED(1) Elf_Ehdr;
typedef typename ElfTypes::Shdr ALIGNED(1) Elf_Shdr;
typedef typename ElfTypes::Sym ALIGNED(1) Elf_Sym;
typedef typename ElfTypes::Addr ALIGNED(1) Elf_Addr;
- // Read and check the elf header.
- const Elf_Ehdr* header = reinterpret_cast<const Elf_Ehdr*>(elf);
- CHECK(header->checkMagic());
+ // Call Frame Information.
+ struct CFI {
+ uint32_t length; // Length excluding the size of this field.
+ int32_t cie_pointer; // Offset in the section or -1 for CIE.
- // Find sections that we are interested in.
- const Elf_Shdr* sections = reinterpret_cast<const Elf_Shdr*>(elf + header->e_shoff);
- const Elf_Shdr* strtab = nullptr;
- const Elf_Shdr* symtab = nullptr;
- const Elf_Shdr* debug_frame = nullptr;
- const Elf_Shdr* gnu_debugdata = nullptr;
- for (size_t i = 1 /* skip null section */; i < header->e_shnum; i++) {
- const Elf_Shdr* section = sections + i;
- const char* name = reinterpret_cast<const char*>(
- elf + sections[header->e_shstrndx].sh_offset + section->sh_name);
- if (strcmp(name, ".strtab") == 0) {
- strtab = section;
- } else if (strcmp(name, ".symtab") == 0) {
- symtab = section;
- } else if (strcmp(name, ".debug_frame") == 0) {
- debug_frame = section;
- } else if (strcmp(name, ".gnu_debugdata") == 0) {
- gnu_debugdata = section;
+ const uint8_t* data() const { return reinterpret_cast<const uint8_t*>(this); }
+ size_t size() const { return sizeof(uint32_t) + length; }
+ } PACKED(1);
+
+ // Common Information Entry.
+ struct CIE : public CFI {
+ } PACKED(1);
+
+ // Frame Description Entry.
+ struct FDE : public CFI {
+ Elf_Addr sym_addr;
+ Elf_Addr sym_size;
+ } PACKED(1);
+
+ explicit ElfDebugReader(ArrayRef<const uint8_t> file) : file_(file) {
+ header_ = Read<Elf_Ehdr>(/*offset=*/ 0);
+ CHECK(header_->checkMagic());
+ CHECK_EQ(header_->e_ehsize, sizeof(Elf_Ehdr));
+ CHECK_EQ(header_->e_shentsize, sizeof(Elf_Shdr));
+
+ // Find all ELF sections.
+ sections_ = Read<Elf_Shdr>(header_->e_shoff, header_->e_shnum);
+ for (const Elf_Shdr& section : sections_) {
+ const char* name = Read<char>(sections_[header_->e_shstrndx].sh_offset + section.sh_name);
+ section_map_[std::string_view(name)] = §ion;
+ }
+
+ // Decompressed embedded debug symbols, if any.
+ const Elf_Shdr* gnu_debugdata = section_map_[".gnu_debugdata"];
+ if (gnu_debugdata != nullptr) {
+ auto compressed = Read<uint8_t>(gnu_debugdata->sh_offset, gnu_debugdata->sh_size);
+ XzDecompress(compressed, &decompressed_gnu_debugdata_);
+ gnu_debugdata_reader_.reset(new ElfDebugReader(decompressed_gnu_debugdata_));
}
}
- // Visit symbols.
- if (symtab != nullptr && strtab != nullptr) {
- const Elf_Sym* symbols = reinterpret_cast<const Elf_Sym*>(elf + symtab->sh_offset);
- DCHECK_EQ(symtab->sh_entsize, sizeof(Elf_Sym));
- size_t count = symtab->sh_size / sizeof(Elf_Sym);
- for (size_t i = 1 /* skip null symbol */; i < count; i++) {
- Elf_Sym symbol = symbols[i];
- if (symbol.getBinding() != STB_LOCAL) { // Ignore local symbols (e.g. "$t").
- const uint8_t* name = elf + strtab->sh_offset + symbol.st_name;
- visit_sym(symbol, reinterpret_cast<const char*>(name));
+ explicit ElfDebugReader(std::vector<uint8_t>& file)
+ : ElfDebugReader(ArrayRef<const uint8_t>(file)) {
+ }
+
+ const Elf_Ehdr* GetHeader() { return header_; }
+
+ ArrayRef<Elf_Shdr> GetSections() { return sections_; }
+
+ const Elf_Shdr* GetSection(const char* name) { return section_map_[name]; }
+
+ template <typename VisitSym>
+ void VisitFunctionSymbols(VisitSym visit_sym) {
+ const Elf_Shdr* symtab = GetSection(".symtab");
+ const Elf_Shdr* strtab = GetSection(".strtab");
+ const Elf_Shdr* text = GetSection(".text");
+ if (symtab != nullptr && strtab != nullptr) {
+ CHECK_EQ(symtab->sh_entsize, sizeof(Elf_Sym));
+ size_t count = symtab->sh_size / sizeof(Elf_Sym);
+ for (const Elf_Sym& symbol : Read<Elf_Sym>(symtab->sh_offset, count)) {
+ if (symbol.getType() == STT_FUNC && §ions_[symbol.st_shndx] == text) {
+ visit_sym(symbol, Read<char>(strtab->sh_offset + symbol.st_name));
+ }
+ }
+ }
+ if (gnu_debugdata_reader_ != nullptr) {
+ gnu_debugdata_reader_->VisitFunctionSymbols(visit_sym);
+ }
+ }
+
+ template <typename VisitSym>
+ void VisitDynamicSymbols(VisitSym visit_sym) {
+ const Elf_Shdr* dynsym = GetSection(".dynsym");
+ const Elf_Shdr* dynstr = GetSection(".dynstr");
+ if (dynsym != nullptr && dynstr != nullptr) {
+ CHECK_EQ(dynsym->sh_entsize, sizeof(Elf_Sym));
+ size_t count = dynsym->sh_size / sizeof(Elf_Sym);
+ for (const Elf_Sym& symbol : Read<Elf_Sym>(dynsym->sh_offset, count)) {
+ visit_sym(symbol, Read<char>(dynstr->sh_offset + symbol.st_name));
}
}
}
- // Visit CFI (unwind) data.
- if (debug_frame != nullptr) {
- const uint8_t* data = elf + debug_frame->sh_offset;
- const uint8_t* end = data + debug_frame->sh_size;
- while (data < end) {
- Elf_Addr addr, size;
- ArrayRef<const uint8_t> opcodes;
- if (dwarf::ReadFDE<Elf_Addr>(&data, &addr, &size, &opcodes)) {
- visit_fde(addr, size, opcodes);
+ template <typename VisitCIE, typename VisitFDE>
+ void VisitDebugFrame(VisitCIE visit_cie, VisitFDE visit_fde) {
+ const Elf_Shdr* debug_frame = GetSection(".debug_frame");
+ if (debug_frame != nullptr) {
+ for (size_t offset = 0; offset < debug_frame->sh_size;) {
+ const CFI* entry = Read<CFI>(debug_frame->sh_offset + offset);
+ DCHECK_LE(entry->size(), debug_frame->sh_size - offset);
+ if (entry->cie_pointer == -1) {
+ visit_cie(Read<CIE>(debug_frame->sh_offset + offset));
+ } else {
+ const FDE* fde = Read<FDE>(debug_frame->sh_offset + offset);
+ visit_fde(fde, Read<CIE>(debug_frame->sh_offset + fde->cie_pointer));
+ }
+ offset += entry->size();
}
}
+ if (gnu_debugdata_reader_ != nullptr) {
+ gnu_debugdata_reader_->VisitDebugFrame(visit_cie, visit_fde);
+ }
}
- // Process embedded compressed ELF file.
- if (gnu_debugdata != nullptr) {
- ArrayRef<const uint8_t> compressed(elf + gnu_debugdata->sh_offset, gnu_debugdata->sh_size);
- std::vector<uint8_t> decompressed;
- XzDecompress(compressed, &decompressed);
- ReadElfSymbols<ElfTypes>(decompressed.data(), visit_sym, visit_fde);
+ private:
+ template<typename T>
+ const T* Read(size_t offset) {
+ DCHECK_LE(offset + sizeof(T), file_.size());
+ return reinterpret_cast<const T*>(file_.data() + offset);
}
-}
+
+ template<typename T>
+ ArrayRef<const T> Read(size_t offset, size_t count) {
+ DCHECK_LE(offset + count * sizeof(T), file_.size());
+ return ArrayRef<const T>(Read<T>(offset), count);
+ }
+
+ ArrayRef<const uint8_t> const file_;
+ const Elf_Ehdr* header_;
+ ArrayRef<const Elf_Shdr> sections_;
+ std::unordered_map<std::string_view, const Elf_Shdr*> section_map_;
+ std::vector<uint8_t> decompressed_gnu_debugdata_;
+ std::unique_ptr<ElfDebugReader> gnu_debugdata_reader_;
+
+ DISALLOW_COPY_AND_ASSIGN(ElfDebugReader);
+};
} // namespace debug
} // namespace art
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 393db3d..3b7363b 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -44,14 +44,12 @@
template <typename ElfTypes>
void WriteDebugInfo(linker::ElfBuilder<ElfTypes>* builder,
- const DebugInfo& debug_info,
- dwarf::CFIFormat cfi_format,
- bool write_oat_patches) {
+ const DebugInfo& debug_info) {
// Write .strtab and .symtab.
WriteDebugSymbols(builder, /* mini-debug-info= */ false, debug_info);
// Write .debug_frame.
- WriteCFISection(builder, debug_info.compiled_methods, cfi_format, write_oat_patches);
+ WriteCFISection(builder, debug_info.compiled_methods);
// Group the methods into compilation units based on class.
std::unordered_map<const dex::ClassDef*, ElfCompilationUnit> class_to_compilation_unit;
@@ -96,7 +94,7 @@
for (auto& compilation_unit : compilation_units) {
line_writer.WriteCompilationUnit(compilation_unit);
}
- line_writer.End(write_oat_patches);
+ line_writer.End();
}
// Write .debug_info section.
@@ -107,7 +105,7 @@
ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
cu_writer.Write(compilation_unit);
}
- info_writer.End(write_oat_patches);
+ info_writer.End();
}
}
@@ -127,15 +125,18 @@
new linker::ElfBuilder<ElfTypes>(isa, features, &out));
builder->Start(/* write_program_headers= */ false);
// Mirror ELF sections as NOBITS since the added symbols will reference them.
- builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size);
+ if (text_section_size != 0) {
+ builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size);
+ }
if (dex_section_size != 0) {
builder->GetDex()->AllocateVirtualMemory(dex_section_address, dex_section_size);
}
- WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
- WriteCFISection(builder.get(),
- debug_info.compiled_methods,
- dwarf::DW_DEBUG_FRAME_FORMAT,
- /* write_oat_patches= */ false);
+ if (!debug_info.Empty()) {
+ WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
+ }
+ if (!debug_info.compiled_methods.empty()) {
+ WriteCFISection(builder.get(), debug_info.compiled_methods);
+ }
builder->End();
CHECK(builder->Good());
std::vector<uint8_t> compressed_buffer;
@@ -193,41 +194,35 @@
// The compression is great help for multiple methods but it is not worth it for a
// single method due to the overheads so skip the compression here for performance.
WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
- WriteCFISection(builder.get(),
- debug_info.compiled_methods,
- dwarf::DW_DEBUG_FRAME_FORMAT,
- /* write_oat_patches= */ false);
+ WriteCFISection(builder.get(), debug_info.compiled_methods);
} else {
- WriteDebugInfo(builder.get(),
- debug_info,
- dwarf::DW_DEBUG_FRAME_FORMAT,
- /* write_oat_patches= */ false);
+ WriteDebugInfo(builder.get(), debug_info);
}
builder->End();
CHECK(builder->Good());
// Verify the ELF file by reading it back using the trivial reader.
if (kIsDebugBuild) {
using Elf_Sym = typename ElfTypes::Sym;
- using Elf_Addr = typename ElfTypes::Addr;
size_t num_syms = 0;
- size_t num_cfis = 0;
- ReadElfSymbols<ElfTypes>(
- buffer.data(),
- [&](Elf_Sym sym, const char*) {
- DCHECK_EQ(sym.st_value, method_info.code_address + CompiledMethod::CodeDelta(isa));
- DCHECK_EQ(sym.st_size, method_info.code_size);
- num_syms++;
- },
- [&](Elf_Addr addr, Elf_Addr size, ArrayRef<const uint8_t> opcodes) {
- DCHECK_EQ(addr, method_info.code_address);
- DCHECK_EQ(size, method_info.code_size);
- DCHECK_GE(opcodes.size(), method_info.cfi.size());
- DCHECK_EQ(memcmp(opcodes.data(), method_info.cfi.data(), method_info.cfi.size()), 0);
- num_cfis++;
- });
+ size_t num_cies = 0;
+ size_t num_fdes = 0;
+ using Reader = ElfDebugReader<ElfTypes>;
+ Reader reader(buffer);
+ reader.VisitFunctionSymbols([&](Elf_Sym sym, const char*) {
+ DCHECK_EQ(sym.st_value, method_info.code_address + CompiledMethod::CodeDelta(isa));
+ DCHECK_EQ(sym.st_size, method_info.code_size);
+ num_syms++;
+ });
+ reader.VisitDebugFrame([&](const Reader::CIE* cie ATTRIBUTE_UNUSED) {
+ num_cies++;
+ }, [&](const Reader::FDE* fde, const Reader::CIE* cie ATTRIBUTE_UNUSED) {
+ DCHECK_EQ(fde->sym_addr, method_info.code_address);
+ DCHECK_EQ(fde->sym_size, method_info.code_size);
+ num_fdes++;
+ });
DCHECK_EQ(num_syms, 1u);
- // CFI might be missing. TODO: Ensure we have CFI for all methods.
- DCHECK_LE(num_cfis, 1u);
+ DCHECK_LE(num_cies, 1u);
+ DCHECK_LE(num_fdes, 1u);
}
return buffer;
}
@@ -236,14 +231,13 @@
std::vector<uint8_t> PackElfFileForJIT(
InstructionSet isa,
const InstructionSetFeatures* features,
- std::vector<const uint8_t*>& added_elf_files,
+ std::vector<ArrayRef<const uint8_t>>& added_elf_files,
std::vector<const void*>& removed_symbols,
/*out*/ size_t* num_symbols) {
using ElfTypes = ElfRuntimeTypes;
using Elf_Addr = typename ElfTypes::Addr;
using Elf_Sym = typename ElfTypes::Sym;
CHECK_EQ(sizeof(Elf_Addr), static_cast<size_t>(GetInstructionSetPointerSize(isa)));
- const bool is64bit = Is64BitInstructionSet(isa);
auto is_removed_symbol = [&removed_symbols](Elf_Addr addr) {
const void* code_ptr = reinterpret_cast<const void*>(addr);
return std::binary_search(removed_symbols.begin(), removed_symbols.end(), code_ptr);
@@ -265,39 +259,26 @@
auto* symtab = builder->GetSymTab();
auto* debug_frame = builder->GetDebugFrame();
std::deque<Elf_Sym> symbols;
- std::vector<uint8_t> debug_frame_buffer;
- WriteCIE(isa, dwarf::DW_DEBUG_FRAME_FORMAT, &debug_frame_buffer);
+
+ using Reader = ElfDebugReader<ElfTypes>;
+ std::deque<Reader> readers;
+ for (ArrayRef<const uint8_t> added_elf_file : added_elf_files) {
+ readers.emplace_back(added_elf_file);
+ }
// Write symbols names. All other data is buffered.
strtab->Start();
strtab->Write(""); // strtab should start with empty string.
- for (const uint8_t* added_elf_file : added_elf_files) {
- ReadElfSymbols<ElfTypes>(
- added_elf_file,
- [&](Elf_Sym sym, const char* name) {
- if (is_removed_symbol(sym.st_value)) {
- return;
- }
- sym.st_name = strtab->Write(name);
- symbols.push_back(sym);
- min_address = std::min<uint64_t>(min_address, sym.st_value);
- max_address = std::max<uint64_t>(max_address, sym.st_value + sym.st_size);
- },
- [&](Elf_Addr addr, Elf_Addr size, ArrayRef<const uint8_t> opcodes) {
- if (is_removed_symbol(addr)) {
- return;
- }
- WriteFDE(is64bit,
- /*section_address=*/ 0,
- /*cie_address=*/ 0,
- addr,
- size,
- opcodes,
- dwarf::DW_DEBUG_FRAME_FORMAT,
- debug_frame_buffer.size(),
- &debug_frame_buffer,
- /*patch_locations=*/ nullptr);
- });
+ for (Reader& reader : readers) {
+ reader.VisitFunctionSymbols([&](Elf_Sym sym, const char* name) {
+ if (is_removed_symbol(sym.st_value)) {
+ return;
+ }
+ sym.st_name = strtab->Write(name);
+ symbols.push_back(sym);
+ min_address = std::min<uint64_t>(min_address, sym.st_value);
+ max_address = std::max<uint64_t>(max_address, sym.st_value + sym.st_size);
+ });
}
strtab->End();
@@ -315,7 +296,22 @@
// Add the CFI/unwind section.
debug_frame->Start();
- debug_frame->WriteFully(debug_frame_buffer.data(), debug_frame_buffer.size());
+ // ART always produces the same CIE, so we copy the first one and ignore the rest.
+ bool copied_cie = false;
+ for (Reader& reader : readers) {
+ reader.VisitDebugFrame([&](const Reader::CIE* cie) {
+ if (!copied_cie) {
+ debug_frame->WriteFully(cie->data(), cie->size());
+ copied_cie = true;
+ }
+ }, [&](const Reader::FDE* fde, const Reader::CIE* cie ATTRIBUTE_UNUSED) {
+ DCHECK(copied_cie);
+ DCHECK_EQ(fde->cie_pointer, 0);
+ if (!is_removed_symbol(fde->sym_addr)) {
+ debug_frame->WriteFully(fde->data(), fde->size());
+ }
+ });
+ }
debug_frame->End();
builder->End();
@@ -365,7 +361,7 @@
info_writer.Start();
ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
cu_writer.Write(types);
- info_writer.End(/* write_oat_patches= */ false);
+ info_writer.End();
builder->End();
CHECK(builder->Good());
@@ -375,14 +371,10 @@
// Explicit instantiations
template void WriteDebugInfo<ElfTypes32>(
linker::ElfBuilder<ElfTypes32>* builder,
- const DebugInfo& debug_info,
- dwarf::CFIFormat cfi_format,
- bool write_oat_patches);
+ const DebugInfo& debug_info);
template void WriteDebugInfo<ElfTypes64>(
linker::ElfBuilder<ElfTypes64>* builder,
- const DebugInfo& debug_info,
- dwarf::CFIFormat cfi_format,
- bool write_oat_patches);
+ const DebugInfo& debug_info);
} // namespace debug
} // namespace art
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 85ab356..90580b4 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -37,9 +37,7 @@
template <typename ElfTypes>
void WriteDebugInfo(
linker::ElfBuilder<ElfTypes>* builder,
- const DebugInfo& debug_info,
- dwarf::CFIFormat cfi_format,
- bool write_oat_patches);
+ const DebugInfo& debug_info);
std::vector<uint8_t> MakeMiniDebugInfo(
InstructionSet isa,
@@ -59,7 +57,7 @@
std::vector<uint8_t> PackElfFileForJIT(
InstructionSet isa,
const InstructionSetFeatures* features,
- std::vector<const uint8_t*>& added_elf_files,
+ std::vector<ArrayRef<const uint8_t>>& added_elf_files,
std::vector<const void*>& removed_symbols,
/*out*/ size_t* num_symbols);
diff --git a/compiler/debug/xz_utils.cc b/compiler/debug/xz_utils.cc
index a8f60ac..5b36209 100644
--- a/compiler/debug/xz_utils.cc
+++ b/compiler/debug/xz_utils.cc
@@ -43,12 +43,14 @@
});
}
-static void XzCompressChunk(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
+void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
// Configure the compression library.
XzInitCrc();
CLzma2EncProps lzma2Props;
Lzma2EncProps_Init(&lzma2Props);
lzma2Props.lzmaProps.level = 1; // Fast compression.
+ lzma2Props.lzmaProps.reduceSize = src.size(); // Size of data that will be compressed.
+ lzma2Props.blockSize = kChunkSize;
Lzma2EncProps_Normalize(&lzma2Props);
CXzProps props;
XzProps_Init(&props);
@@ -85,69 +87,6 @@
// Compress.
SRes res = Xz_Encode(&callbacks, &callbacks, &props, &callbacks);
CHECK_EQ(res, SZ_OK);
-}
-
-// Compress data while splitting it to smaller chunks to enable random-access reads.
-// The XZ file format supports this well, but the compression library does not.
-// Therefore compress the chunks separately and then glue them together manually.
-//
-// The XZ file format is described here: https://tukaani.org/xz/xz-file-format.txt
-// In short, the file format is: [header] [compressed_block]* [index] [footer]
-// Where [index] is: [num_records] ([compressed_size] [uncompressed_size])* [crc32]
-//
-void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
- uint8_t header[] = { 0xFD, '7', 'z', 'X', 'Z', 0, 0, 1, 0x69, 0x22, 0xDE, 0x36 };
- uint8_t footer[] = { 0, 1, 'Y', 'Z' };
- dst->insert(dst->end(), header, header + sizeof(header));
- std::vector<uint8_t> tmp;
- std::vector<uint32_t> index;
- for (size_t offset = 0; offset < src.size(); offset += kChunkSize) {
- size_t size = std::min(src.size() - offset, kChunkSize);
- tmp.clear();
- XzCompressChunk(src.SubArray(offset, size), &tmp);
- DCHECK_EQ(memcmp(tmp.data(), header, sizeof(header)), 0);
- DCHECK_EQ(memcmp(tmp.data() + tmp.size() - sizeof(footer), footer, sizeof(footer)), 0);
- uint32_t* index_size = reinterpret_cast<uint32_t*>(tmp.data() + tmp.size() - 8);
- DCHECK_ALIGNED(index_size, sizeof(uint32_t));
- size_t index_offset = tmp.size() - 16 - *index_size * 4;
- const uint8_t* index_ptr = tmp.data() + index_offset;
- uint8_t index_indicator = *(index_ptr++);
- CHECK_EQ(index_indicator, 0); // Mark the start of index (as opposed to compressed block).
- uint32_t num_records = DecodeUnsignedLeb128(&index_ptr);
- for (uint32_t i = 0; i < num_records; i++) {
- index.push_back(DecodeUnsignedLeb128(&index_ptr)); // Compressed size.
- index.push_back(DecodeUnsignedLeb128(&index_ptr)); // Uncompressed size.
- }
- // Copy the raw compressed block(s) located between the header and index.
- dst->insert(dst->end(), tmp.data() + sizeof(header), tmp.data() + index_offset);
- }
-
- // Write the index.
- uint32_t index_size_in_words;
- {
- tmp.clear();
- dwarf::Writer<> writer(&tmp);
- writer.PushUint8(0); // Index indicator.
- writer.PushUleb128(static_cast<uint32_t>(index.size()) / 2); // Record count.
- for (uint32_t i : index) {
- writer.PushUleb128(i);
- }
- writer.Pad(4);
- index_size_in_words = writer.size() / sizeof(uint32_t);
- writer.PushUint32(CrcCalc(tmp.data(), tmp.size()));
- dst->insert(dst->end(), tmp.begin(), tmp.end());
- }
-
- // Write the footer.
- {
- tmp.clear();
- dwarf::Writer<> writer(&tmp);
- writer.PushUint32(0); // CRC placeholder.
- writer.PushUint32(index_size_in_words);
- writer.PushData(footer, sizeof(footer));
- writer.UpdateUint32(0, CrcCalc(tmp.data() + 4, 6));
- dst->insert(dst->end(), tmp.begin(), tmp.end());
- }
// Decompress the data back and check that we get the original.
if (kIsDebugBuild) {
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e440eec..f39d8fc 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -17,13 +17,15 @@
#include "compiler_driver.h"
#include <unistd.h>
-#include <unordered_set>
-#include <vector>
#ifndef __APPLE__
#include <malloc.h> // For mallinfo
#endif
+#include <string_view>
+#include <unordered_set>
+#include <vector>
+
#include "android-base/logging.h"
#include "android-base/strings.h"
@@ -35,6 +37,7 @@
#include "base/enums.h"
#include "base/logging.h" // For VLOG
#include "base/stl_util.h"
+#include "base/string_view_cpp20.h"
#include "base/systrace.h"
#include "base/time_utils.h"
#include "base/timing_logger.h"
@@ -1154,7 +1157,7 @@
const PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
while (!klass->IsObjectClass()) {
const char* descriptor = klass->GetDescriptor(&temp);
- if (image_classes->find(StringPiece(descriptor)) != image_classes->end()) {
+ if (image_classes->find(std::string_view(descriptor)) != image_classes->end()) {
break; // Previously inserted.
}
image_classes->insert(descriptor);
@@ -1236,7 +1239,7 @@
bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES_SHARED(Locks::mutator_lock_) {
std::string temp;
- StringPiece name(klass->GetDescriptor(&temp));
+ std::string_view name(klass->GetDescriptor(&temp));
auto it = data_->image_class_descriptors_->find(name);
if (it != data_->image_class_descriptors_->end()) {
if (LIKELY(klass->IsResolved())) {
@@ -1776,11 +1779,15 @@
return false;
}
TimingLogger::ScopedTiming t("Fast Verify", timings);
+
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
- if (!verifier_deps->ValidateDependencies(class_loader, soa.Self())) {
+ std::string error_msg;
+
+ if (!verifier_deps->ValidateDependencies(class_loader, soa.Self(), &error_msg)) {
+ LOG(WARNING) << "Fast verification failed: " << error_msg;
return false;
}
@@ -1878,10 +1885,10 @@
// Merge all VerifierDeps into the main one.
verifier::VerifierDeps* verifier_deps = Thread::Current()->GetVerifierDeps();
for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
- verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps();
- worker->GetThread()->SetVerifierDeps(nullptr);
- verifier_deps->MergeWith(*thread_deps, GetCompilerOptions().GetDexFilesForOatFile());
- delete thread_deps;
+ std::unique_ptr<verifier::VerifierDeps> thread_deps(worker->GetThread()->GetVerifierDeps());
+ worker->GetThread()->SetVerifierDeps(nullptr); // We just took ownership.
+ verifier_deps->MergeWith(std::move(thread_deps),
+ GetCompilerOptions().GetDexFilesForOatFile());
}
Thread::Current()->SetVerifierDeps(nullptr);
}
@@ -2179,7 +2186,7 @@
// We need to initialize static fields, we only do this for image classes that aren't
// marked with the $NoPreloadHolder (which implies this should not be initialized
// early).
- can_init_static_fields = !StringPiece(descriptor).ends_with("$NoPreloadHolder;");
+ can_init_static_fields = !EndsWith(std::string_view(descriptor), "$NoPreloadHolder;");
} else {
CHECK(is_app_image);
// The boot image case doesn't need to recursively initialize the dependencies with
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 8d1ae3d..7dd743f 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -17,6 +17,7 @@
#include "compiler_options.h"
#include <fstream>
+#include <string_view>
#include "android-base/stringprintf.h"
@@ -144,7 +145,7 @@
// Historical note: We used to hold the set indirectly and there was a distinction between an
// empty set and a null, null meaning to include all classes. However, the distiction has been
// removed; if we don't have a profile, we treat it as an empty set of classes. b/77340429
- return image_classes_.find(StringPiece(descriptor)) != image_classes_.end();
+ return image_classes_.find(std::string_view(descriptor)) != image_classes_.end();
}
const VerificationResults* CompilerOptions::GetVerificationResults() const {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index bd12bf7..fccd9ca 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -361,13 +361,6 @@
private:
bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
- void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
- void ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage);
- void ParseNumDexMethods(const StringPiece& option, UsageFn Usage);
- void ParseTinyMethodMax(const StringPiece& option, UsageFn Usage);
- void ParseSmallMethodMax(const StringPiece& option, UsageFn Usage);
- void ParseLargeMethodMax(const StringPiece& option, UsageFn Usage);
- void ParseHugeMethodMax(const StringPiece& option, UsageFn Usage);
bool ParseRegisterAllocationStrategy(const std::string& option, std::string* error_msg);
CompilerFilter::Filter compiler_filter_;
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index d5ceafe..f978cc6 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -80,7 +80,7 @@
stack_maps.BeginMethod(4 * sizeof(void*), 0u, 0u, 0u);
stack_maps.BeginStackMapEntry(kDexPc, native_pc_offset);
stack_maps.EndStackMapEntry();
- stack_maps.EndMethod();
+ stack_maps.EndMethod(code_size);
ScopedArenaVector<uint8_t> stack_map = stack_maps.Encode();
const size_t stack_maps_size = stack_map.size();
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 0d35fec..2c20b32 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -22,7 +22,7 @@
#include "arch/instruction_set_features.h"
#include "art_method-inl.h"
#include "base/logging.h" // For VLOG
-#include "base/stringpiece.h"
+#include "base/string_view_cpp20.h"
#include "base/systrace.h"
#include "base/time_utils.h"
#include "base/timing_logger.h"
@@ -71,19 +71,19 @@
DCHECK_EQ(instruction_set, kRuntimeISA);
}
std::unique_ptr<const InstructionSetFeatures> instruction_set_features;
- for (const StringPiece option : runtime->GetCompilerOptions()) {
+ for (const std::string& option : runtime->GetCompilerOptions()) {
VLOG(compiler) << "JIT compiler option " << option;
std::string error_msg;
- if (option.starts_with("--instruction-set-variant=")) {
- StringPiece str = option.substr(strlen("--instruction-set-variant=")).data();
+ if (StartsWith(option, "--instruction-set-variant=")) {
+ const char* str = option.c_str() + strlen("--instruction-set-variant=");
VLOG(compiler) << "JIT instruction set variant " << str;
instruction_set_features = InstructionSetFeatures::FromVariant(
- instruction_set, str.as_string(), &error_msg);
+ instruction_set, str, &error_msg);
if (instruction_set_features == nullptr) {
LOG(WARNING) << "Error parsing " << option << " message=" << error_msg;
}
- } else if (option.starts_with("--instruction-set-features=")) {
- StringPiece str = option.substr(strlen("--instruction-set-features=")).data();
+ } else if (StartsWith(option, "--instruction-set-features=")) {
+ const char* str = option.c_str() + strlen("--instruction-set-features=");
VLOG(compiler) << "JIT instruction set features " << str;
if (instruction_set_features == nullptr) {
instruction_set_features = InstructionSetFeatures::FromVariant(
@@ -93,13 +93,16 @@
}
}
instruction_set_features =
- instruction_set_features->AddFeaturesFromString(str.as_string(), &error_msg);
+ instruction_set_features->AddFeaturesFromString(str, &error_msg);
if (instruction_set_features == nullptr) {
LOG(WARNING) << "Error parsing " << option << " message=" << error_msg;
}
}
}
+
if (instruction_set_features == nullptr) {
+ // '--instruction-set-features/--instruction-set-variant' were not used.
+ // Use build-time defined features.
instruction_set_features = InstructionSetFeatures::FromCppDefines();
}
compiler_options_->instruction_set_features_ = std::move(instruction_set_features);
diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h
index 6acce10..33e1866 100644
--- a/compiler/linker/elf_builder.h
+++ b/compiler/linker/elf_builder.h
@@ -50,17 +50,11 @@
// .dynamic - Tags which let the linker locate .dynsym.
// .strtab - Names for .symtab.
// .symtab - Debug symbols.
-// .eh_frame - Unwind information (CFI).
-// .eh_frame_hdr - Index of .eh_frame.
// .debug_frame - Unwind information (CFI).
-// .debug_frame.oat_patches - Addresses for relocation.
// .debug_info - Debug information.
-// .debug_info.oat_patches - Addresses for relocation.
// .debug_abbrev - Decoding information for .debug_info.
// .debug_str - Strings for .debug_info.
// .debug_line - Line number tables.
-// .debug_line.oat_patches - Addresses for relocation.
-// .text.oat_patches - Addresses for relocation.
// .shstrtab - Names of ELF sections.
// Elf_Shdr[] - Section headers.
//
@@ -535,11 +529,11 @@
dynsym_(this, ".dynsym", SHT_DYNSYM, SHF_ALLOC, &dynstr_),
hash_(this, ".hash", SHT_HASH, SHF_ALLOC, &dynsym_, 0, sizeof(Elf_Word), sizeof(Elf_Word)),
dynamic_(this, ".dynamic", SHT_DYNAMIC, SHF_ALLOC, &dynstr_, 0, kPageSize, sizeof(Elf_Dyn)),
- eh_frame_(this, ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
- eh_frame_hdr_(this, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0),
strtab_(this, ".strtab", 0, 1),
symtab_(this, ".symtab", SHT_SYMTAB, 0, &strtab_),
debug_frame_(this, ".debug_frame", SHT_PROGBITS, 0, nullptr, 0, sizeof(Elf_Addr), 0),
+ debug_frame_hdr_(
+ this, ".debug_frame_hdr.android", SHT_PROGBITS, 0, nullptr, 0, sizeof(Elf_Addr), 0),
debug_info_(this, ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0),
debug_line_(this, ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0),
shstrtab_(this, ".shstrtab", 0, 1),
@@ -558,7 +552,6 @@
dex_.phdr_flags_ = PF_R;
dynamic_.phdr_flags_ = PF_R | PF_W;
dynamic_.phdr_type_ = PT_DYNAMIC;
- eh_frame_hdr_.phdr_type_ = PT_GNU_EH_FRAME;
abiflags_.phdr_type_ = PT_MIPS_ABIFLAGS;
build_id_.phdr_type_ = PT_NOTE;
}
@@ -573,35 +566,11 @@
Section* GetDex() { return &dex_; }
StringSection* GetStrTab() { return &strtab_; }
SymbolSection* GetSymTab() { return &symtab_; }
- Section* GetEhFrame() { return &eh_frame_; }
- Section* GetEhFrameHdr() { return &eh_frame_hdr_; }
Section* GetDebugFrame() { return &debug_frame_; }
+ Section* GetDebugFrameHdr() { return &debug_frame_hdr_; }
Section* GetDebugInfo() { return &debug_info_; }
Section* GetDebugLine() { return &debug_line_; }
- // Encode patch locations as LEB128 list of deltas between consecutive addresses.
- // (exposed publicly for tests)
- static void EncodeOatPatches(const ArrayRef<const uintptr_t>& locations,
- std::vector<uint8_t>* buffer) {
- buffer->reserve(buffer->size() + locations.size() * 2); // guess 2 bytes per ULEB128.
- uintptr_t address = 0; // relative to start of section.
- for (uintptr_t location : locations) {
- DCHECK_GE(location, address) << "Patch locations are not in sorted order";
- EncodeUnsignedLeb128(buffer, dchecked_integral_cast<uint32_t>(location - address));
- address = location;
- }
- }
-
- void WritePatches(const char* name, const ArrayRef<const uintptr_t>& patch_locations) {
- std::vector<uint8_t> buffer;
- EncodeOatPatches(patch_locations, &buffer);
- std::unique_ptr<Section> s(new Section(this, name, SHT_OAT_PATCH, 0, nullptr, 0, 1, 0));
- s->Start();
- s->WriteFully(buffer.data(), buffer.size());
- s->End();
- other_sections_.push_back(std::move(s));
- }
-
void WriteSection(const char* name, const std::vector<uint8_t>* buffer) {
std::unique_ptr<Section> s(new Section(this, name, SHT_PROGBITS, 0, nullptr, 0, 1, 0));
s->Start();
@@ -633,9 +602,6 @@
// Note: loaded_size_ == 0 for tests that don't write .rodata, .text, .bss,
// .dynstr, dynsym, .hash and .dynamic. These tests should not read loaded_size_.
- // TODO: Either refactor the .eh_frame creation so that it counts towards loaded_size_,
- // or remove all support for .eh_frame. (The currently unused .eh_frame counts towards
- // the virtual_address_ but we don't consider it for loaded_size_.)
CHECK(loaded_size_ == 0 || loaded_size_ == RoundUp(virtual_address_, kPageSize))
<< loaded_size_ << " " << virtual_address_;
@@ -1079,11 +1045,10 @@
SymbolSection dynsym_;
CachedSection hash_;
CachedSection dynamic_;
- Section eh_frame_;
- Section eh_frame_hdr_;
StringSection strtab_;
SymbolSection symtab_;
Section debug_frame_;
+ Section debug_frame_hdr_;
Section debug_info_;
Section debug_line_;
StringSection shstrtab_;
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 54a1ae9..e35d502 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -845,8 +845,10 @@
// make one more attempt to get a constant in the array range.
ValueRange* existing_range = LookupValueRange(array_length, block);
if (existing_range != nullptr &&
- existing_range->IsConstantValueRange()) {
- ValueRange constant_array_range(&allocator_, lower, existing_range->GetLower());
+ existing_range->IsConstantValueRange() &&
+ existing_range->GetLower().GetConstant() > 0) {
+ ValueBound constant_upper(nullptr, existing_range->GetLower().GetConstant() - 1);
+ ValueRange constant_array_range(&allocator_, lower, constant_upper);
if (index_range->FitsIn(&constant_array_range)) {
ReplaceInstruction(bounds_check, index);
return;
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 9e2f5cd..122f27b 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -440,7 +440,7 @@
// Finalize instructions in assember;
Finalize(allocator);
- GetStackMapStream()->EndMethod();
+ GetStackMapStream()->EndMethod(GetAssembler()->CodeSize());
}
void CodeGenerator::Finalize(CodeAllocator* allocator) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 8204f1e..9c155f8 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -2093,6 +2093,8 @@
}
if (HasEmptyFrame()) {
+ // Ensure that the CFI opcode list is not empty.
+ GetAssembler()->cfi().Nop();
return;
}
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index 3689d1d..e8460a8 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -43,7 +43,6 @@
buckets_(allocator->AllocArray<Node*>(num_buckets_, kArenaAllocGvn)),
buckets_owned_(allocator, num_buckets_, false, kArenaAllocGvn),
num_entries_(0u) {
- // ArenaAllocator returns zeroed memory, so no need to set buckets to null.
DCHECK(IsPowerOfTwo(num_buckets_));
std::fill_n(buckets_, num_buckets_, nullptr);
buckets_owned_.SetInitialBits(num_buckets_);
@@ -57,8 +56,6 @@
buckets_(allocator->AllocArray<Node*>(num_buckets_, kArenaAllocGvn)),
buckets_owned_(allocator, num_buckets_, false, kArenaAllocGvn),
num_entries_(0u) {
- // ArenaAllocator returns zeroed memory, so entries of buckets_ and
- // buckets_owned_ are initialized to null and false, respectively.
DCHECK(IsPowerOfTwo(num_buckets_));
PopulateFromInternal(other);
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 8440e9a..96d6d2a 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1789,6 +1789,14 @@
invoke_type = kVirtual;
}
+ bool caller_dead_reference_safe = graph_->IsDeadReferenceSafe();
+ const dex::ClassDef& callee_class = resolved_method->GetClassDef();
+ // MethodContainsRSensitiveAccess is currently slow, but HasDeadReferenceSafeAnnotation()
+ // is currently rarely true.
+ bool callee_dead_reference_safe =
+ annotations::HasDeadReferenceSafeAnnotation(callee_dex_file, callee_class)
+ && !annotations::MethodContainsRSensitiveAccess(callee_dex_file, callee_class, method_index);
+
const int32_t caller_instruction_counter = graph_->GetCurrentInstructionId();
HGraph* callee_graph = new (graph_->GetAllocator()) HGraph(
graph_->GetAllocator(),
@@ -1797,6 +1805,7 @@
method_index,
codegen_->GetCompilerOptions().GetInstructionSet(),
invoke_type,
+ callee_dead_reference_safe,
graph_->IsDebuggable(),
/* osr= */ false,
caller_instruction_counter);
@@ -2023,6 +2032,13 @@
inline_stats_->AddTo(stats_);
}
+ if (caller_dead_reference_safe && !callee_dead_reference_safe) {
+ // Caller was dead reference safe, but is not anymore, since we inlined dead
+ // reference unsafe code. Prior transformations remain valid, since they did not
+ // affect the inlined code.
+ graph_->MarkDeadReferenceUnsafe();
+ }
+
return true;
}
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 2721cb5..2de0f0c 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -20,6 +20,7 @@
#include "art_method-inl.h"
#include "base/utils.h"
#include "class_linker.h"
+#include "class_root.h"
#include "dex/invoke_type.h"
#include "driver/compiler_options.h"
#include "gc/space/image_space.h"
@@ -362,4 +363,13 @@
return info;
}
+void IntrinsicVisitor::AssertNonMovableStringClass() {
+ if (kIsDebugBuild) {
+ Thread* const self = Thread::Current();
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ ObjPtr<mirror::Class> string_class = GetClassRoot<art::mirror::String>();
+ CHECK(!art::Runtime::Current()->GetHeap()->IsMovableObject(string_class));
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 50b13c8..ab68cce 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -142,6 +142,8 @@
protected:
IntrinsicVisitor() {}
+ static void AssertNonMovableStringClass();
+
private:
DISALLOW_COPY_AND_ASSIGN(IntrinsicVisitor);
};
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index ca790f6..ec5d17a 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1464,8 +1464,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp = str->klass_
__ Ldr(temp, MemOperand(str.X(), class_offset));
+ // /* HeapReference<Class> */ temp1 = arg->klass_
__ Ldr(temp1, MemOperand(arg.X(), class_offset));
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Cmp(temp, temp1);
__ B(&return_false, ne);
}
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 396ff62..f0aa92e 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -1529,8 +1529,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp = str->klass_
__ Ldr(temp, MemOperand(str, class_offset));
+ // /* HeapReference<Class> */ out = arg->klass_
__ Ldr(out, MemOperand(arg, class_offset));
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Cmp(temp, out);
__ B(ne, &return_false, /* is_far_target= */ false);
}
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 5b35974..3da0e57 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1575,8 +1575,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp1 = str->klass_
__ Lw(temp1, str, class_offset);
+ // /* HeapReference<Class> */ temp2 = arg->klass_
__ Lw(temp2, arg, class_offset);
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Bne(temp1, temp2, &return_false);
}
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index afaa4ca..3e68765 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1429,8 +1429,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp1 = str->klass_
__ Lw(temp1, str, class_offset);
+ // /* HeapReference<Class> */ temp2 = arg->klass_
__ Lw(temp2, arg, class_offset);
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Bnec(temp1, temp2, &return_false);
}
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 8747f06..de697f0 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1072,7 +1072,15 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // Also, because we use the loaded class references only to compare them, we
+ // don't need to unpoison them.
+ // /* HeapReference<Class> */ ecx = str->klass_
__ movl(ecx, Address(str, class_offset));
+ // if (ecx != /* HeapReference<Class> */ arg->klass_) return false
__ cmpl(ecx, Address(arg, class_offset));
__ j(kNotEqual, &return_false);
}
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 167c1d8..e79c0c9 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1275,7 +1275,15 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // Also, because we use the loaded class references only to compare them, we
+ // don't need to unpoison them.
+ // /* HeapReference<Class> */ rcx = str->klass_
__ movl(rcx, Address(str, class_offset));
+ // if (rcx != /* HeapReference<Class> */ arg->klass_) return false
__ cmpl(rcx, Address(arg, class_offset));
__ j(kNotEqual, &return_false);
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 48fb611..4670b3f 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -317,6 +317,7 @@
uint32_t method_idx,
InstructionSet instruction_set,
InvokeType invoke_type = kInvalidInvokeType,
+ bool dead_reference_safe = false,
bool debuggable = false,
bool osr = false,
int start_instruction_id = 0)
@@ -336,6 +337,7 @@
has_simd_(false),
has_loops_(false),
has_irreducible_loops_(false),
+ dead_reference_safe_(dead_reference_safe),
debuggable_(debuggable),
current_instruction_id_(start_instruction_id),
dex_file_(dex_file),
@@ -526,6 +528,12 @@
has_bounds_checks_ = value;
}
+ // Is the code known to be robust against eliminating dead references
+ // and the effects of early finalization?
+ bool IsDeadReferenceSafe() const { return dead_reference_safe_; }
+
+ void MarkDeadReferenceUnsafe() { dead_reference_safe_ = false; }
+
bool IsDebuggable() const { return debuggable_; }
// Returns a constant of the given type and value. If it does not exist
@@ -704,6 +712,14 @@
// so there might be false positives.
bool has_irreducible_loops_;
+ // Is the code known to be robust against eliminating dead references
+ // and the effects of early finalization? If false, dead reference variables
+ // are kept if they might be visible to the garbage collector.
+ // Currently this means that the class was declared to be dead-reference-safe,
+ // the method accesses no reachability-sensitive fields or data, and the same
+ // is true for any methods that were inlined into the current one.
+ bool dead_reference_safe_;
+
// Indicates whether the graph should be compiled in a way that
// ensures full debuggability. If false, we can apply more
// aggressive optimizations that may limit the level of debugging.
@@ -2339,10 +2355,6 @@
// 2) Their inputs are identical.
bool Equals(const HInstruction* other) const;
- // TODO: Remove this indirection when the [[pure]] attribute proposal (n3744)
- // is adopted and implemented by our C++ compiler(s). Fow now, we need to hide
- // the virtual function because the __attribute__((__pure__)) doesn't really
- // apply the strong requirement for virtual functions, preventing optimizations.
InstructionKind GetKind() const { return GetPackedField<InstructionKindField>(); }
virtual size_t ComputeHashCode() const {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 42dbc77..4f43b71 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -828,6 +828,29 @@
}
CodeItemDebugInfoAccessor code_item_accessor(dex_file, code_item, method_idx);
+
+ bool dead_reference_safe;
+ ArrayRef<const uint8_t> interpreter_metadata;
+ // For AOT compilation, we may not get a method, for example if its class is erroneous,
+ // possibly due to an unavailable superclass. JIT should always have a method.
+ DCHECK(Runtime::Current()->IsAotCompiler() || method != nullptr);
+ if (method != nullptr) {
+ const dex::ClassDef* containing_class;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ containing_class = &method->GetClassDef();
+ interpreter_metadata = method->GetQuickenedInfo();
+ }
+ // MethodContainsRSensitiveAccess is currently slow, but HasDeadReferenceSafeAnnotation()
+ // is currently rarely true.
+ dead_reference_safe =
+ annotations::HasDeadReferenceSafeAnnotation(dex_file, *containing_class)
+ && !annotations::MethodContainsRSensitiveAccess(dex_file, *containing_class, method_idx);
+ } else {
+ // If we could not resolve the class, conservatively assume it's dead-reference unsafe.
+ dead_reference_safe = false;
+ }
+
HGraph* graph = new (allocator) HGraph(
allocator,
arena_stack,
@@ -835,17 +858,12 @@
method_idx,
compiler_options.GetInstructionSet(),
kInvalidInvokeType,
+ dead_reference_safe,
compiler_driver->GetCompilerOptions().GetDebuggable(),
- osr);
+ /* osr= */ osr);
- ArrayRef<const uint8_t> interpreter_metadata;
- // For AOT compilation, we may not get a method, for example if its class is erroneous.
- // JIT should always have a method.
- DCHECK(Runtime::Current()->IsAotCompiler() || method != nullptr);
if (method != nullptr) {
graph->SetArtMethod(method);
- ScopedObjectAccess soa(Thread::Current());
- interpreter_metadata = method->GetQuickenedInfo();
}
std::unique_ptr<CodeGenerator> codegen(
@@ -963,6 +981,7 @@
method_idx,
compiler_options.GetInstructionSet(),
kInvalidInvokeType,
+ /* dead_reference_safe= */ true, // Intrinsics don't affect dead reference safety.
compiler_options.GetDebuggable(),
/* osr= */ false);
@@ -1152,7 +1171,8 @@
}
static ScopedArenaVector<uint8_t> CreateJniStackMap(ScopedArenaAllocator* allocator,
- const JniCompiledMethod& jni_compiled_method) {
+ const JniCompiledMethod& jni_compiled_method,
+ size_t code_size) {
// StackMapStream is quite large, so allocate it using the ScopedArenaAllocator
// to stay clear of the frame size limit.
std::unique_ptr<StackMapStream> stack_map_stream(
@@ -1162,7 +1182,7 @@
jni_compiled_method.GetCoreSpillMask(),
jni_compiled_method.GetFpSpillMask(),
/* num_dex_registers= */ 0);
- stack_map_stream->EndMethod();
+ stack_map_stream->EndMethod(code_size);
return stack_map_stream->Encode();
}
@@ -1220,8 +1240,8 @@
MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledNativeStub);
ScopedArenaAllocator stack_map_allocator(&arena_stack); // Will hold the stack map.
- ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(&stack_map_allocator,
- jni_compiled_method);
+ ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(
+ &stack_map_allocator, jni_compiled_method, jni_compiled_method.GetCode().size());
return CompiledMethod::SwapAllocCompiledMethod(
GetCompilerDriver()->GetCompiledMethodStorage(),
jni_compiled_method.GetInstructionSet(),
@@ -1271,8 +1291,8 @@
ArenaStack arena_stack(runtime->GetJitArenaPool());
// StackMapStream is large and it does not fit into this frame, so we need helper method.
ScopedArenaAllocator stack_map_allocator(&arena_stack); // Will hold the stack map.
- ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(&stack_map_allocator,
- jni_compiled_method);
+ ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(
+ &stack_map_allocator, jni_compiled_method, jni_compiled_method.GetCode().size());
uint8_t* stack_map_data = nullptr;
uint8_t* roots_data = nullptr;
uint32_t data_size = code_cache->ReserveData(self,
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 92d0b08..c883907 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -1155,10 +1155,11 @@
*
* (a) Non-environment uses of an instruction always make
* the instruction live.
- * (b) Environment uses of an instruction whose type is
- * object (that is, non-primitive), make the instruction live.
- * This is due to having to keep alive objects that have
- * finalizers deleting native objects.
+ * (b) Environment uses of an instruction whose type is object (that is, non-primitive), make the
+ * instruction live, unless the class has an @DeadReferenceSafe annotation.
+ * This avoids unexpected premature reference enqueuing or finalization, which could
+ * result in premature deletion of native objects. In the presence of @DeadReferenceSafe,
+ * object references are treated like primitive types.
* (c) When the graph has the debuggable property, environment uses
* of an instruction that has a primitive type make the instruction live.
* If the graph does not have the debuggable property, the environment
@@ -1287,6 +1288,7 @@
// When compiling in OSR mode, all loops in the compiled method may be entered
// from the interpreter via SuspendCheck; thus we need to preserve the environment.
if (env_holder->IsSuspendCheck() && graph->IsCompilingOsr()) return true;
+ if (graph -> IsDeadReferenceSafe()) return false;
return instruction->GetType() == DataType::Type::kReference;
}
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 60ca61c..e87f3c8 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -54,9 +54,10 @@
num_dex_registers_ = num_dex_registers;
}
-void StackMapStream::EndMethod() {
+void StackMapStream::EndMethod(size_t code_size) {
DCHECK(in_method_) << "Mismatched Begin/End calls";
in_method_ = false;
+ packed_code_size_ = StackMap::PackNativePc(code_size, instruction_set_);
// Read the stack masks now. The compiler might have updated them.
for (size_t i = 0; i < lazy_stack_masks_.size(); i++) {
@@ -66,6 +67,10 @@
stack_masks_.Dedup(stack_mask->GetRawStorage(), stack_mask->GetNumberOfBits());
}
}
+
+ for (size_t i = 0; i < stack_maps_.size(); i++) {
+ DCHECK_LE(stack_maps_[i][StackMap::kPackedNativePc], packed_code_size_);
+ }
}
void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
@@ -296,6 +301,7 @@
ScopedArenaVector<uint8_t> buffer(allocator_->Adapter(kArenaAllocStackMapStream));
BitMemoryWriter<ScopedArenaVector<uint8_t>> out(&buffer);
+ out.WriteVarint(packed_code_size_);
out.WriteVarint(packed_frame_size_);
out.WriteVarint(core_spill_mask_);
out.WriteVarint(fp_spill_mask_);
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 01c6bf9..164e902 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -62,7 +62,7 @@
size_t core_spill_mask,
size_t fp_spill_mask,
uint32_t num_dex_registers);
- void EndMethod();
+ void EndMethod(size_t code_size);
void BeginStackMapEntry(uint32_t dex_pc,
uint32_t native_pc_offset,
@@ -99,6 +99,7 @@
ScopedArenaAllocator* allocator_;
const InstructionSet instruction_set_;
+ uint32_t packed_code_size_ = 0;
uint32_t packed_frame_size_ = 0;
uint32_t core_spill_mask_ = 0;
uint32_t fp_spill_mask_ = 0;
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index d28f09f..cbd844f 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -61,7 +61,7 @@
stream.AddDexRegisterEntry(Kind::kConstant, -2); // Short location.
stream.EndStackMapEntry();
- stream.EndMethod();
+ stream.EndMethod(64 * kPcAlign);
ScopedArenaVector<uint8_t> memory = stream.Encode();
CodeInfo code_info(memory.data());
@@ -147,7 +147,7 @@
stream.AddDexRegisterEntry(Kind::kInFpuRegisterHigh, 1); // Short location.
stream.EndStackMapEntry();
- stream.EndMethod();
+ stream.EndMethod(256 * kPcAlign);
ScopedArenaVector<uint8_t> memory = stream.Encode();
CodeInfo code_info(memory.data());
@@ -317,7 +317,7 @@
stream.EndInlineInfoEntry();
stream.EndStackMapEntry();
- stream.EndMethod();
+ stream.EndMethod(64 * kPcAlign);
ScopedArenaVector<uint8_t> memory = stream.Encode();
CodeInfo code_info(memory.data());
@@ -372,7 +372,7 @@
stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
stream.EndStackMapEntry();
- stream.EndMethod();
+ stream.EndMethod(64 * kPcAlign);
ScopedArenaVector<uint8_t> memory = stream.Encode();
CodeInfo code_info(memory.data());
@@ -431,7 +431,7 @@
stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
stream.EndStackMapEntry();
- stream.EndMethod();
+ stream.EndMethod(66 * kPcAlign);
ScopedArenaVector<uint8_t> memory = stream.Encode();
CodeInfo ci(memory.data());
@@ -479,7 +479,7 @@
stream.AddDexRegisterEntry(Kind::kNone, 0);
stream.EndStackMapEntry();
- stream.EndMethod();
+ stream.EndMethod(68 * kPcAlign);
ScopedArenaVector<uint8_t> memory = stream.Encode();
CodeInfo code_info(memory.data());
@@ -578,7 +578,7 @@
stream.EndStackMapEntry();
- stream.EndMethod();
+ stream.EndMethod(78 * kPcAlign);
ScopedArenaVector<uint8_t> memory = stream.Encode();
CodeInfo ci(memory.data());
@@ -722,7 +722,7 @@
stream.BeginStackMapEntry(0, 8 * kPcAlign, 0x3, &sp_mask);
stream.EndStackMapEntry();
- stream.EndMethod();
+ stream.EndMethod(8 * kPcAlign);
ScopedArenaVector<uint8_t> memory = stream.Encode();
CodeInfo code_info(memory.data());
@@ -746,7 +746,7 @@
stream.AddDexRegisterEntry(Kind::kConstant, -2);
stream.EndStackMapEntry();
- stream.EndMethod();
+ stream.EndMethod(64 * kPcAlign);
ScopedArenaVector<uint8_t> memory = stream.Encode();
std::vector<uint8_t> out;
diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc
index dee83d1..841ff1c 100644
--- a/compiler/utils/swap_space.cc
+++ b/compiler/utils/swap_space.cc
@@ -115,12 +115,11 @@
? free_by_size_.end()
: free_by_size_.lower_bound(FreeBySizeEntry { size, free_by_start_.begin() });
if (it != free_by_size_.end()) {
- auto entry = it->free_by_start_entry;
- SpaceChunk old_chunk = *entry;
+ SpaceChunk old_chunk = *it->free_by_start_entry;
if (old_chunk.size == size) {
RemoveChunk(it);
} else {
- // Try to avoid deallocating and allocating the std::set<> nodes.
+ // Avoid deallocating and allocating the std::set<> nodes.
// This would be much simpler if we could use replace() from Boost.Bimap.
// The free_by_start_ map contains disjoint intervals ordered by the `ptr`.
@@ -128,24 +127,9 @@
it->free_by_start_entry->ptr += size;
it->free_by_start_entry->size -= size;
- // The free_by_size_ map is ordered by the `size` and then `free_by_start_entry->ptr`.
- // Adjusting the `ptr` above does not change that ordering but decreasing `size` can
- // push the node before the previous node(s).
- if (it == free_by_size_.begin()) {
- it->size -= size;
- } else {
- auto prev = it;
- --prev;
- FreeBySizeEntry new_value(old_chunk.size - size, entry);
- if (free_by_size_.key_comp()(*prev, new_value)) {
- it->size -= size;
- } else {
- // Changing in place would break the std::set<> ordering, we need to remove and insert.
- // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
- free_by_size_.erase(it);
- free_by_size_.insert(new_value);
- }
- }
+ auto node = free_by_size_.extract(it);
+ node.value().size -= size;
+ free_by_size_.insert(std::move(node));
}
return old_chunk.ptr;
} else {
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 092e931..5c6b815 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -449,6 +449,28 @@
has_unverified_classes;
}
+ // Load the dex file again with a new class loader, decode the VerifierDeps
+ // in `buffer`, allow the caller to modify the deps and then run validation.
+ template<typename Fn>
+ bool RunValidation(Fn fn, const std::vector<uint8_t>& buffer, std::string* error_msg) {
+ ScopedObjectAccess soa(Thread::Current());
+
+ jobject second_loader = LoadDex("VerifierDeps");
+ const auto& second_dex_files = GetDexFiles(second_loader);
+
+ VerifierDeps decoded_deps(second_dex_files, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* decoded_dex_deps =
+ decoded_deps.GetDexFileDeps(*second_dex_files.front());
+
+ // Let the test modify the dependencies.
+ fn(*decoded_dex_deps);
+
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> new_class_loader =
+ hs.NewHandle<mirror::ClassLoader>(soa.Decode<mirror::ClassLoader>(second_loader));
+ return decoded_deps.ValidateDependencies(new_class_loader, soa.Self(), error_msg);
+ }
+
std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
std::vector<const DexFile*> dex_files_;
const DexFile* primary_dex_file_;
@@ -1177,8 +1199,9 @@
}
TEST_F(VerifierDepsTest, VerifyDeps) {
- VerifyDexFile();
+ std::string error_msg;
+ VerifyDexFile();
ASSERT_EQ(1u, NumberOfCompiledDexFiles());
ASSERT_TRUE(HasEachKindOfRecord());
@@ -1186,249 +1209,166 @@
// the existing `class_loader_` may contain erroneous classes,
// that ClassLinker::FindClass won't return.
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- MutableHandle<mirror::ClassLoader> new_class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
- {
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_TRUE(verifier_deps_->ValidateDependencies(new_class_loader, soa.Self()));
- }
-
std::vector<uint8_t> buffer;
verifier_deps_->Encode(dex_files_, &buffer);
ASSERT_FALSE(buffer.empty());
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_TRUE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
+ // Check that dependencies are satisfied after decoding `buffer`.
+ ASSERT_TRUE(RunValidation([](VerifierDeps::DexFileDeps&) {}, buffer, &error_msg))
+ << error_msg;
- // Fiddle with the dependencies to make sure we catch any change and fail to verify.
+ // Mess with the dependencies to make sure we catch any change and fail to verify.
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ deps.assignable_types_.insert(*deps.unassignable_types_.begin());
+ }, buffer, &error_msg));
- {
- // Mess up with the assignable_types.
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- deps->assignable_types_.insert(*deps->unassignable_types_.begin());
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
+ // Mess with the unassignable_types.
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ deps.unassignable_types_.insert(*deps.assignable_types_.begin());
+ }, buffer, &error_msg));
- {
- // Mess up with the unassignable_types.
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- deps->unassignable_types_.insert(*deps->assignable_types_.begin());
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
+ // Mess with classes.
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ for (const auto& entry : deps.classes_) {
+ if (entry.IsResolved()) {
+ deps.classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any resolved classes";
+ UNREACHABLE();
+ }, buffer, &error_msg));
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ for (const auto& entry : deps.classes_) {
+ if (!entry.IsResolved()) {
+ deps.classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker - 1));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any unresolved classes";
+ UNREACHABLE();
+ }, buffer, &error_msg));
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ for (const auto& entry : deps.classes_) {
+ if (entry.IsResolved()) {
+ deps.classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), entry.GetAccessFlags() - 1));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any resolved classes";
+ UNREACHABLE();
+ }, buffer, &error_msg));
- // Mess up with classes.
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- for (const auto& entry : deps->classes_) {
- if (entry.IsResolved()) {
- deps->classes_.insert(VerifierDeps::ClassResolution(
- entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
+ // Mess with fields.
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ for (const auto& entry : deps.fields_) {
+ if (entry.IsResolved()) {
+ deps.fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ VerifierDeps::kUnresolvedMarker,
+ entry.GetDeclaringClassIndex()));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any resolved fields";
+ UNREACHABLE();
+ }, buffer, &error_msg));
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ for (const auto& entry : deps.fields_) {
+ if (!entry.IsResolved()) {
+ constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there.
+ deps.fields_.insert(VerifierDeps::FieldResolution(0 /* we know there is a field there */,
+ VerifierDeps::kUnresolvedMarker - 1,
+ kStringIndexZero));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any unresolved fields";
+ UNREACHABLE();
+ }, buffer, &error_msg));
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ for (const auto& entry : deps.fields_) {
+ if (entry.IsResolved()) {
+ deps.fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ entry.GetAccessFlags() - 1,
+ entry.GetDeclaringClassIndex()));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any resolved fields";
+ UNREACHABLE();
+ }, buffer, &error_msg));
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ for (const auto& entry : deps.fields_) {
+ constexpr dex::StringIndex kNewTypeIndex(0);
+ if (entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+ deps.fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ entry.GetAccessFlags(),
+ kNewTypeIndex));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any suitable fields";
+ UNREACHABLE();
+ }, buffer, &error_msg));
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- for (const auto& entry : deps->classes_) {
- if (!entry.IsResolved()) {
- deps->classes_.insert(VerifierDeps::ClassResolution(
- entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker - 1));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- for (const auto& entry : deps->classes_) {
- if (entry.IsResolved()) {
- deps->classes_.insert(VerifierDeps::ClassResolution(
- entry.GetDexTypeIndex(), entry.GetAccessFlags() - 1));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- // Mess up with fields.
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- for (const auto& entry : deps->fields_) {
- if (entry.IsResolved()) {
- deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
- VerifierDeps::kUnresolvedMarker,
- entry.GetDeclaringClassIndex()));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- for (const auto& entry : deps->fields_) {
- if (!entry.IsResolved()) {
- constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there.
- deps->fields_.insert(VerifierDeps::FieldResolution(0 /* we know there is a field there */,
- VerifierDeps::kUnresolvedMarker - 1,
- kStringIndexZero));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- for (const auto& entry : deps->fields_) {
- if (entry.IsResolved()) {
- deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
- entry.GetAccessFlags() - 1,
- entry.GetDeclaringClassIndex()));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- for (const auto& entry : deps->fields_) {
- constexpr dex::StringIndex kNewTypeIndex(0);
- if (entry.GetDeclaringClassIndex() != kNewTypeIndex) {
- deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
- entry.GetAccessFlags(),
- kNewTypeIndex));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- // Mess up with methods.
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
- for (const auto& entry : *methods) {
- if (entry.IsResolved()) {
- methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- VerifierDeps::kUnresolvedMarker,
- entry.GetDeclaringClassIndex()));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
- for (const auto& entry : *methods) {
- if (!entry.IsResolved()) {
- constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there.
- methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */,
- VerifierDeps::kUnresolvedMarker - 1,
- kStringIndexZero));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
- for (const auto& entry : *methods) {
- if (entry.IsResolved()) {
- methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- entry.GetAccessFlags() - 1,
- entry.GetDeclaringClassIndex()));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
- for (const auto& entry : *methods) {
- constexpr dex::StringIndex kNewTypeIndex(0);
- if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) {
- methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- entry.GetAccessFlags(),
- kNewTypeIndex));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
+ // Mess with methods.
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ VerifierDeps::kUnresolvedMarker,
+ entry.GetDeclaringClassIndex()));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any resolved methods";
+ UNREACHABLE();
+ }, buffer, &error_msg));
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
+ for (const auto& entry : *methods) {
+ if (!entry.IsResolved()) {
+ constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there.
+ methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */,
+ VerifierDeps::kUnresolvedMarker - 1,
+ kStringIndexZero));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any unresolved methods";
+ UNREACHABLE();
+ }, buffer, &error_msg));
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags() - 1,
+ entry.GetDeclaringClassIndex()));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any resolved methods";
+ UNREACHABLE();
+ }, buffer, &error_msg));
+ ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+ std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
+ for (const auto& entry : *methods) {
+ constexpr dex::StringIndex kNewTypeIndex(0);
+ if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ kNewTypeIndex));
+ return;
+ }
+ }
+ LOG(FATAL) << "Could not find any suitable methods";
+ UNREACHABLE();
+ }, buffer, &error_msg));
}
TEST_F(VerifierDepsTest, CompilerDriver) {
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 6a4a88e..20d41b4 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -64,8 +64,6 @@
target: {
android: {
- // For atrace.
- shared_libs: ["libcutils"],
static_libs: [
"libz",
],
@@ -93,11 +91,6 @@
cc_defaults {
name: "libart-dex2oat_static_base_defaults",
- target: {
- android: {
- static_libs: ["libcutils"],
- },
- },
static_libs: [
"libbase",
"libz",
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 35af918..1f18172 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -49,7 +49,6 @@
#include "base/os.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
-#include "base/stringpiece.h"
#include "base/time_utils.h"
#include "base/timing_logger.h"
#include "base/unix_file/fd_file.h"
@@ -296,6 +295,10 @@
UsageError(" Default: arm");
UsageError("");
UsageError(" --instruction-set-features=...,: Specify instruction set features");
+ UsageError(" On target the value 'runtime' can be used to detect features at run time.");
+ UsageError(" If target does not support run-time detection the value 'runtime'");
+ UsageError(" has the same effect as the value 'default'.");
+ UsageError(" Note: the value 'runtime' has no effect if it is used on host.");
UsageError(" Example: --instruction-set-features=div");
UsageError(" Default: default");
UsageError("");
@@ -875,9 +878,9 @@
oat_unstripped_ = std::move(parser_options->oat_symbols);
}
- // If no instruction set feature was given, use the default one for the target
- // instruction set.
- if (compiler_options_->instruction_set_features_.get() == nullptr) {
+ if (compiler_options_->instruction_set_features_ == nullptr) {
+ // '--instruction-set-features/--instruction-set-variant' were not used.
+ // Use features for the 'default' variant.
compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant(
compiler_options_->instruction_set_, "default", &parser_options->error_msg);
if (compiler_options_->instruction_set_features_ == nullptr) {
@@ -890,9 +893,9 @@
std::unique_ptr<const InstructionSetFeatures> runtime_features(
InstructionSetFeatures::FromCppDefines());
if (!compiler_options_->GetInstructionSetFeatures()->Equals(runtime_features.get())) {
- LOG(WARNING) << "Mismatch between dex2oat instruction set features ("
+ LOG(WARNING) << "Mismatch between dex2oat instruction set features to use ("
<< *compiler_options_->GetInstructionSetFeatures()
- << ") and those of dex2oat executable (" << *runtime_features
+ << ") and those from CPP defines (" << *runtime_features
<< ") for the command line:\n" << CommandLine();
}
}
@@ -1367,6 +1370,7 @@
LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg;
} else {
input_vdex_file_ = std::make_unique<VdexFile>(std::move(input_file));
+ VLOG(verifier) << "Doing fast verification with vdex from DexMetadata archive";
}
}
}
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 524bce0..d3bfb57 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <algorithm>
#include <regex>
#include <sstream>
#include <string>
@@ -28,6 +29,7 @@
#include "common_runtime_test.h"
+#include "arch/instruction_set_features.h"
#include "base/macros.h"
#include "base/mutex-inl.h"
#include "base/utils.h"
@@ -2315,4 +2317,38 @@
}));
}
+class Dex2oatISAFeaturesRuntimeDetectionTest : public Dex2oatTest {
+ protected:
+ void RunTest(const std::vector<std::string>& extra_args = {}) {
+ std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
+ std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
+
+ Copy(GetTestDexFileName(), dex_location);
+
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kSpeed,
+ extra_args));
+ }
+
+ std::string GetTestDexFileName() {
+ return GetDexSrc1();
+ }
+};
+
+TEST_F(Dex2oatISAFeaturesRuntimeDetectionTest, TestCurrentRuntimeFeaturesAsDex2OatArguments) {
+ std::vector<std::string> argv;
+ Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
+ auto option_pos =
+ std::find(std::begin(argv), std::end(argv), "--instruction-set-features=runtime");
+ if (InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+ EXPECT_TRUE(kIsTargetBuild);
+ EXPECT_NE(option_pos, std::end(argv));
+ } else {
+ EXPECT_EQ(option_pos, std::end(argv));
+ }
+
+ RunTest();
+}
+
} // namespace art
diff --git a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
index d8cbbaf..04a897e 100644
--- a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
+++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
@@ -140,50 +140,63 @@
| (((diff >> 20) & 1) << 26); // S
}
- bool Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
- const ArrayRef<const LinkerPatch>& method1_patches,
- const ArrayRef<const uint8_t>& method3_code,
- const ArrayRef<const LinkerPatch>& method3_patches,
- uint32_t distance_without_thunks) {
+ uint32_t Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
+ const ArrayRef<const LinkerPatch>& method1_patches,
+ const ArrayRef<const uint8_t>& last_method_code,
+ const ArrayRef<const LinkerPatch>& last_method_patches,
+ uint32_t distance_without_thunks) {
CHECK_EQ(distance_without_thunks % kArmAlignment, 0u);
uint32_t method1_offset =
kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
+ const uint32_t gap_start = method1_offset + method1_code.size();
- // We want to put the method3 at a very precise offset.
- const uint32_t method3_offset = method1_offset + distance_without_thunks;
- CHECK_ALIGNED(method3_offset, kArmAlignment);
+ // We want to put the last method at a very precise offset.
+ const uint32_t last_method_offset = method1_offset + distance_without_thunks;
+ CHECK_ALIGNED(last_method_offset, kArmAlignment);
+ const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader);
- // Calculate size of method2 so that we put method3 at the correct place.
- const uint32_t method1_end = method1_offset + method1_code.size();
- const uint32_t method2_offset =
- method1_end + CodeAlignmentSize(method1_end) + sizeof(OatQuickMethodHeader);
- const uint32_t method2_size = (method3_offset - sizeof(OatQuickMethodHeader) - method2_offset);
- std::vector<uint8_t> method2_raw_code(method2_size);
- ArrayRef<const uint8_t> method2_code(method2_raw_code);
- AddCompiledMethod(MethodRef(2u), method2_code);
+ // Fill the gap with intermediate methods in chunks of 2MiB and the first in [2MiB, 4MiB).
+ // (This allows deduplicating the small chunks to avoid using 32MiB of memory for +-16MiB
+ // offsets by this test. Making the first chunk bigger makes it easy to give all intermediate
+ // methods the same alignment of the end, so the thunk insertion adds a predictable size as
+ // long as it's after the first chunk.)
+ uint32_t method_idx = 2u;
+ constexpr uint32_t kSmallChunkSize = 2 * MB;
+ std::vector<uint8_t> gap_code;
+ uint32_t gap_size = gap_end - gap_start;
+ uint32_t num_small_chunks = std::max(gap_size / kSmallChunkSize, 1u) - 1u;
+ uint32_t chunk_start = gap_start;
+ uint32_t chunk_size = gap_size - num_small_chunks * kSmallChunkSize;
+ for (uint32_t i = 0; i <= num_small_chunks; ++i) { // num_small_chunks+1 iterations.
+ uint32_t chunk_code_size =
+ chunk_size - CodeAlignmentSize(chunk_start) - sizeof(OatQuickMethodHeader);
+ gap_code.resize(chunk_code_size, 0u);
+ AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(gap_code));
+ method_idx += 1u;
+ chunk_start += chunk_size;
+ chunk_size = kSmallChunkSize; // For all but the first chunk.
+ DCHECK_EQ(CodeAlignmentSize(gap_end), CodeAlignmentSize(chunk_start));
+ }
- AddCompiledMethod(MethodRef(3u), method3_code, method3_patches);
-
+ // Add the last method and link
+ AddCompiledMethod(MethodRef(method_idx), last_method_code, last_method_patches);
Link();
// Check assumptions.
CHECK_EQ(GetMethodOffset(1), method1_offset);
- CHECK_EQ(GetMethodOffset(2), method2_offset);
- auto result3 = method_offset_map_.FindMethodOffset(MethodRef(3));
- CHECK(result3.first);
+ auto last_result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
+ CHECK(last_result.first);
// There may be a thunk before method2.
- if (result3.second == method3_offset + 1 /* thumb mode */) {
- return false; // No thunk.
- } else {
+ if (last_result.second != last_method_offset + 1 /* thumb mode */) {
+ // Thunk present. Check that there's only one.
uint32_t thunk_end =
- CompiledCode::AlignCode(method3_offset - sizeof(OatQuickMethodHeader),
- InstructionSet::kThumb2) +
- MethodCallThunkSize();
+ CompiledCode::AlignCode(gap_end, InstructionSet::kThumb2) + MethodCallThunkSize();
uint32_t header_offset = thunk_end + CodeAlignmentSize(thunk_end);
- CHECK_EQ(result3.second, header_offset + sizeof(OatQuickMethodHeader) + 1 /* thumb mode */);
- return true; // Thunk present.
+ CHECK_EQ(last_result.second,
+ header_offset + sizeof(OatQuickMethodHeader) + 1 /* thumb mode */);
}
+ return method_idx;
}
uint32_t GetMethodOffset(uint32_t method_idx) {
@@ -447,32 +460,37 @@
TEST_F(Thumb2RelativePatcherTest, CallTrampolineTooFar) {
constexpr uint32_t missing_method_index = 1024u;
- auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
- constexpr uint32_t bl_offset_in_method3 = 3u * 2u; // After NOPs.
- ArrayRef<const uint8_t> method3_code(method3_raw_code);
- ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
- const LinkerPatch method3_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, missing_method_index),
+ auto last_method_raw_code = GenNopsAndBl(3u, kBlPlus0);
+ constexpr uint32_t bl_offset_in_last_method = 3u * 2u; // After NOPs.
+ ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
+ ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
+ const LinkerPatch last_method_patches[] = {
+ LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, missing_method_index),
};
constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
- ArrayRef<const LinkerPatch>(),
- method3_code,
- ArrayRef<const LinkerPatch>(method3_patches),
- just_over_max_negative_disp - bl_offset_in_method3);
- ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2.
+ uint32_t last_method_idx = Create2MethodsWithGap(
+ kNopCode,
+ ArrayRef<const LinkerPatch>(),
+ last_method_code,
+ ArrayRef<const LinkerPatch>(last_method_patches),
+ just_over_max_negative_disp - bl_offset_in_last_method);
+ uint32_t method1_offset = GetMethodOffset(1u);
+ uint32_t last_method_offset = GetMethodOffset(last_method_idx);
+ ASSERT_EQ(method1_offset,
+ last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp);
ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
// Check linked code.
- uint32_t method3_offset = GetMethodOffset(3u);
- uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(),
- InstructionSet::kThumb2);
- uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
- ASSERT_EQ(diff & 1u, 0u);
+ uint32_t thunk_offset = CompiledCode::AlignCode(
+ last_method_offset + last_method_code.size(), InstructionSet::kThumb2);
+ uint32_t diff =
+ thunk_offset - (last_method_offset + bl_offset_in_last_method + 4u /* PC adjustment */);
+ ASSERT_TRUE(IsAligned<2u>(diff));
ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits.
auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
+ ArrayRef<const uint8_t>(expected_code)));
EXPECT_TRUE(CheckThunk(thunk_offset));
}
@@ -481,17 +499,18 @@
constexpr uint32_t bl_offset_in_method1 = 3u * 2u; // After NOPs.
ArrayRef<const uint8_t> method1_code(method1_raw_code);
ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
+ const uint32_t kExpectedLastMethodIdx = 9u; // Based on 2MiB chunks in Create2MethodsWithGap().
const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
+ LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, kExpectedLastMethodIdx),
};
constexpr uint32_t max_positive_disp = 16 * MB - 2u + 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(method1_code,
- ArrayRef<const LinkerPatch>(method1_patches),
- kNopCode,
- ArrayRef<const LinkerPatch>(),
- bl_offset_in_method1 + max_positive_disp);
- ASSERT_FALSE(thunk_in_gap); // There should be no thunk.
+ uint32_t last_method_idx = Create2MethodsWithGap(method1_code,
+ ArrayRef<const LinkerPatch>(method1_patches),
+ kNopCode,
+ ArrayRef<const LinkerPatch>(),
+ bl_offset_in_method1 + max_positive_disp);
+ ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx);
// Check linked code.
auto expected_code = GenNopsAndBl(3u, kBlPlusMax);
@@ -499,25 +518,28 @@
}
TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarBefore) {
- auto method3_raw_code = GenNopsAndBl(2u, kBlPlus0);
- constexpr uint32_t bl_offset_in_method3 = 2u * 2u; // After NOPs.
- ArrayRef<const uint8_t> method3_code(method3_raw_code);
- ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
- const LinkerPatch method3_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
+ auto last_method_raw_code = GenNopsAndBl(2u, kBlPlus0);
+ constexpr uint32_t bl_offset_in_last_method = 2u * 2u; // After NOPs.
+ ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
+ ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
+ const LinkerPatch last_method_patches[] = {
+ LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u),
};
- constexpr uint32_t just_over_max_negative_disp = 16 * MB - 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
- ArrayRef<const LinkerPatch>(),
- method3_code,
- ArrayRef<const LinkerPatch>(method3_patches),
- just_over_max_negative_disp - bl_offset_in_method3);
- ASSERT_FALSE(thunk_in_gap); // There should be no thunk.
+ constexpr uint32_t max_negative_disp = 16 * MB - 4u /* PC adjustment */;
+ uint32_t last_method_idx = Create2MethodsWithGap(kNopCode,
+ ArrayRef<const LinkerPatch>(),
+ last_method_code,
+ ArrayRef<const LinkerPatch>(last_method_patches),
+ max_negative_disp - bl_offset_in_last_method);
+ uint32_t method1_offset = GetMethodOffset(1u);
+ uint32_t last_method_offset = GetMethodOffset(last_method_idx);
+ ASSERT_EQ(method1_offset, last_method_offset + bl_offset_in_last_method - max_negative_disp);
// Check linked code.
auto expected_code = GenNopsAndBl(2u, kBlMinusMax);
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
+ ArrayRef<const uint8_t>(expected_code)));
}
TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarAfter) {
@@ -525,61 +547,78 @@
constexpr uint32_t bl_offset_in_method1 = 2u * 2u; // After NOPs.
ArrayRef<const uint8_t> method1_code(method1_raw_code);
ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
+ const uint32_t kExpectedLastMethodIdx = 9u; // Based on 2MiB chunks in Create2MethodsWithGap().
const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, 3u),
+ LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, kExpectedLastMethodIdx),
};
constexpr uint32_t just_over_max_positive_disp = 16 * MB + 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(method1_code,
- ArrayRef<const LinkerPatch>(method1_patches),
- kNopCode,
- ArrayRef<const LinkerPatch>(),
- bl_offset_in_method1 + just_over_max_positive_disp);
- ASSERT_TRUE(thunk_in_gap);
+ uint32_t last_method_idx = Create2MethodsWithGap(
+ method1_code,
+ ArrayRef<const LinkerPatch>(method1_patches),
+ kNopCode,
+ ArrayRef<const LinkerPatch>(),
+ bl_offset_in_method1 + just_over_max_positive_disp);
+ ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx);
+ uint32_t method_after_thunk_idx = last_method_idx;
+ if (sizeof(OatQuickMethodHeader) < kArmAlignment) {
+ // The thunk needs to start on a kArmAlignment-aligned address before the address where the
+ // last method would have been if there was no thunk. If the size of the OatQuickMethodHeader
+ // is at least kArmAlignment, the thunk start shall fit between the previous filler method
+ // and that address. Otherwise, it shall be inserted before that filler method.
+ method_after_thunk_idx -= 1u;
+ }
uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t method3_offset = GetMethodOffset(3u);
- ASSERT_TRUE(IsAligned<kArmAlignment>(method3_offset));
- uint32_t method3_header_offset = method3_offset - sizeof(OatQuickMethodHeader);
+ uint32_t method_after_thunk_offset = GetMethodOffset(method_after_thunk_idx);
+ ASSERT_TRUE(IsAligned<kArmAlignment>(method_after_thunk_offset));
+ uint32_t method_after_thunk_header_offset =
+ method_after_thunk_offset - sizeof(OatQuickMethodHeader);
uint32_t thunk_size = MethodCallThunkSize();
- uint32_t thunk_offset = RoundDown(method3_header_offset - thunk_size, kArmAlignment);
+ uint32_t thunk_offset = RoundDown(method_after_thunk_header_offset - thunk_size, kArmAlignment);
DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
- method3_header_offset);
+ method_after_thunk_header_offset);
ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset));
uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */);
- ASSERT_EQ(diff & 1u, 0u);
- ASSERT_GE(diff, 16 * MB - (1u << 9)); // Simple encoding, unknown bits fit into the low 8 bits.
- auto expected_code = GenNopsAndBl(2u, 0xf3ffd700 | ((diff >> 1) & 0xffu));
+ ASSERT_TRUE(IsAligned<2u>(diff));
+ ASSERT_GE(diff, 16 * MB - (1u << 22)); // Simple encoding, unknown bits fit into imm10:imm11:0.
+ auto expected_code =
+ GenNopsAndBl(2u, 0xf000d000 | ((diff >> 1) & 0x7ffu) | (((diff >> 12) & 0x3ffu) << 16));
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
CheckThunk(thunk_offset);
}
TEST_F(Thumb2RelativePatcherTest, CallOtherJustTooFarBefore) {
- auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
- constexpr uint32_t bl_offset_in_method3 = 3u * 2u; // After NOPs.
- ArrayRef<const uint8_t> method3_code(method3_raw_code);
- ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
- const LinkerPatch method3_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, 1u),
+ auto last_method_raw_code = GenNopsAndBl(3u, kBlPlus0);
+ constexpr uint32_t bl_offset_in_last_method = 3u * 2u; // After NOPs.
+ ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
+ ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
+ const LinkerPatch last_method_patches[] = {
+ LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u),
};
constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
- ArrayRef<const LinkerPatch>(),
- method3_code,
- ArrayRef<const LinkerPatch>(method3_patches),
- just_over_max_negative_disp - bl_offset_in_method3);
- ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2.
+ uint32_t last_method_idx = Create2MethodsWithGap(
+ kNopCode,
+ ArrayRef<const LinkerPatch>(),
+ last_method_code,
+ ArrayRef<const LinkerPatch>(last_method_patches),
+ just_over_max_negative_disp - bl_offset_in_last_method);
+ uint32_t method1_offset = GetMethodOffset(1u);
+ uint32_t last_method_offset = GetMethodOffset(last_method_idx);
+ ASSERT_EQ(method1_offset,
+ last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp);
// Check linked code.
- uint32_t method3_offset = GetMethodOffset(3u);
- uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(),
- InstructionSet::kThumb2);
- uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
- ASSERT_EQ(diff & 1u, 0u);
+ uint32_t thunk_offset = CompiledCode::AlignCode(
+ last_method_offset + last_method_code.size(), InstructionSet::kThumb2);
+ uint32_t diff =
+ thunk_offset - (last_method_offset + bl_offset_in_last_method + 4u /* PC adjustment */);
+ ASSERT_TRUE(IsAligned<2u>(diff));
ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits.
auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
+ ArrayRef<const uint8_t>(expected_code)));
EXPECT_TRUE(CheckThunk(thunk_offset));
}
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
index 678574be2..9e54bbf 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
+++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
@@ -118,7 +118,7 @@
AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
const uint32_t gap_start = method1_offset + method1_code.size();
- // We want to put the method3 at a very precise offset.
+ // We want to put the last method at a very precise offset.
const uint32_t last_method_offset = method1_offset + distance_without_thunks;
CHECK_ALIGNED(last_method_offset, kArm64Alignment);
const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader);
@@ -638,7 +638,9 @@
constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4;
uint32_t last_method_idx = Create2MethodsWithGap(
- kNopCode, ArrayRef<const LinkerPatch>(), last_method_code,
+ kNopCode,
+ ArrayRef<const LinkerPatch>(),
+ last_method_code,
ArrayRef<const LinkerPatch>(last_method_patches),
just_over_max_negative_disp - bl_offset_in_last_method);
uint32_t method1_offset = GetMethodOffset(1u);
@@ -651,7 +653,7 @@
uint32_t thunk_offset =
CompiledCode::AlignCode(last_method_offset + last_method_code.size(), InstructionSet::kArm64);
uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
- CHECK_ALIGNED(diff, 4u);
+ ASSERT_TRUE(IsAligned<4u>(diff));
ASSERT_LT(diff, 128 * MB);
auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
@@ -664,9 +666,9 @@
constexpr uint32_t bl_offset_in_method1 = 1u * 4u; // After NOPs.
ArrayRef<const uint8_t> method1_code(method1_raw_code);
ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
- uint32_t expected_last_method_idx = 65; // Based on 2MiB chunks in Create2MethodsWithGap().
+ const uint32_t kExpectedLastMethodIdx = 65u; // Based on 2MiB chunks in Create2MethodsWithGap().
const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, expected_last_method_idx),
+ LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, kExpectedLastMethodIdx),
};
constexpr uint32_t max_positive_disp = 128 * MB - 4u;
@@ -675,7 +677,7 @@
kNopCode,
ArrayRef<const LinkerPatch>(),
bl_offset_in_method1 + max_positive_disp);
- ASSERT_EQ(expected_last_method_idx, last_method_idx);
+ ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx);
uint32_t method1_offset = GetMethodOffset(1u);
uint32_t last_method_offset = GetMethodOffset(last_method_idx);
@@ -716,9 +718,9 @@
constexpr uint32_t bl_offset_in_method1 = 0u * 4u; // After NOPs.
ArrayRef<const uint8_t> method1_code(method1_raw_code);
ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
- uint32_t expected_last_method_idx = 65; // Based on 2MiB chunks in Create2MethodsWithGap().
+ const uint32_t kExpectedLastMethodIdx = 65u; // Based on 2MiB chunks in Create2MethodsWithGap().
const LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, expected_last_method_idx),
+ LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, kExpectedLastMethodIdx),
};
constexpr uint32_t just_over_max_positive_disp = 128 * MB;
@@ -728,7 +730,7 @@
kNopCode,
ArrayRef<const LinkerPatch>(),
bl_offset_in_method1 + just_over_max_positive_disp);
- ASSERT_EQ(expected_last_method_idx, last_method_idx);
+ ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx);
uint32_t method_after_thunk_idx = last_method_idx;
if (sizeof(OatQuickMethodHeader) < kArm64Alignment) {
// The thunk needs to start on a kArm64Alignment-aligned address before the address where the
@@ -747,8 +749,9 @@
uint32_t thunk_offset = RoundDown(method_after_thunk_header_offset - thunk_size, kArm64Alignment);
DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
method_after_thunk_header_offset);
+ ASSERT_TRUE(IsAligned<kArm64Alignment>(thunk_offset));
uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1);
- CHECK_ALIGNED(diff, 4u);
+ ASSERT_TRUE(IsAligned<4u>(diff));
ASSERT_LT(diff, 128 * MB);
auto expected_code = GenNopsAndBl(0u, kBlPlus0 | (diff >> 2));
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
@@ -778,7 +781,7 @@
uint32_t thunk_offset =
CompiledCode::AlignCode(last_method_offset + last_method_code.size(), InstructionSet::kArm64);
uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
- CHECK_ALIGNED(diff, 4u);
+ ASSERT_TRUE(IsAligned<4u>(diff));
ASSERT_LT(diff, 128 * MB);
auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index 9fbcca4..7e0f29d 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -42,15 +42,6 @@
namespace art {
namespace linker {
-// .eh_frame and .debug_frame are almost identical.
-// Except for some minor formatting differences, the main difference
-// is that .eh_frame is allocated within the running program because
-// it is used by C++ exception handling (which we do not use so we
-// can choose either). C++ compilers generally tend to use .eh_frame
-// because if they need it sometimes, they might as well always use it.
-// Let's use .debug_frame because it is easier to strip or compress.
-constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT;
-
class DebugInfoTask : public Task {
public:
DebugInfoTask(InstructionSet isa,
@@ -261,7 +252,7 @@
template <typename ElfTypes>
void ElfWriterQuick<ElfTypes>::PrepareDebugInfo(const debug::DebugInfo& debug_info) {
- if (!debug_info.Empty() && compiler_options_.GetGenerateMiniDebugInfo()) {
+ if (compiler_options_.GetGenerateMiniDebugInfo()) {
// Prepare the mini-debug-info in background while we do other I/O.
Thread* self = Thread::Current();
debug_info_task_ = std::make_unique<DebugInfoTask>(
@@ -280,19 +271,17 @@
template <typename ElfTypes>
void ElfWriterQuick<ElfTypes>::WriteDebugInfo(const debug::DebugInfo& debug_info) {
- if (!debug_info.Empty()) {
- if (compiler_options_.GetGenerateMiniDebugInfo()) {
- // Wait for the mini-debug-info generation to finish and write it to disk.
- Thread* self = Thread::Current();
- DCHECK(debug_info_thread_pool_ != nullptr);
- debug_info_thread_pool_->Wait(self, true, false);
- builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult());
- }
- // The Strip method expects debug info to be last (mini-debug-info is not stripped).
- if (compiler_options_.GetGenerateDebugInfo()) {
- // Generate all the debug information we can.
- debug::WriteDebugInfo(builder_.get(), debug_info, kCFIFormat, true /* write_oat_patches */);
- }
+ if (compiler_options_.GetGenerateMiniDebugInfo()) {
+ // Wait for the mini-debug-info generation to finish and write it to disk.
+ Thread* self = Thread::Current();
+ DCHECK(debug_info_thread_pool_ != nullptr);
+ debug_info_thread_pool_->Wait(self, true, false);
+ builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult());
+ }
+ // The Strip method expects debug info to be last (mini-debug-info is not stripped).
+ if (!debug_info.Empty() && compiler_options_.GetGenerateDebugInfo()) {
+ // Generate all the debug information we can.
+ debug::WriteDebugInfo(builder_.get(), debug_info);
}
}
diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc
index b381765..e016f29 100644
--- a/dex2oat/linker/elf_writer_test.cc
+++ b/dex2oat/linker/elf_writer_test.cc
@@ -139,43 +139,5 @@
}
}
-TEST_F(ElfWriterTest, EncodeDecodeOatPatches) {
- const std::vector<std::vector<uintptr_t>> test_data {
- { 0, 4, 8, 15, 128, 200 },
- { 8, 8 + 127 },
- { 8, 8 + 128 },
- { },
- };
- for (const auto& patch_locations : test_data) {
- constexpr int32_t delta = 0x11235813;
-
- // Encode patch locations.
- std::vector<uint8_t> oat_patches;
- ElfBuilder<ElfTypes32>::EncodeOatPatches(ArrayRef<const uintptr_t>(patch_locations),
- &oat_patches);
-
- // Create buffer to be patched.
- std::vector<uint8_t> initial_data(256);
- for (size_t i = 0; i < initial_data.size(); i++) {
- initial_data[i] = i;
- }
-
- // Patch manually.
- std::vector<uint8_t> expected = initial_data;
- for (uintptr_t location : patch_locations) {
- using UnalignedAddress __attribute__((__aligned__(1))) = uint32_t;
- *reinterpret_cast<UnalignedAddress*>(expected.data() + location) += delta;
- }
-
- // Decode and apply patch locations.
- std::vector<uint8_t> actual = initial_data;
- ElfFileImpl32::ApplyOatPatches(
- oat_patches.data(), oat_patches.data() + oat_patches.size(), delta,
- actual.data(), actual.data() + actual.size());
-
- EXPECT_EQ(expected, actual);
- }
-}
-
} // namespace linker
} // namespace art
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 8c9dfb8..431ab90 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -21,6 +21,7 @@
#include <memory>
#include <string>
+#include <string_view>
#include <vector>
#include "android-base/stringprintf.h"
@@ -498,7 +499,7 @@
ObjPtr<mirror::Class> klass = class_linker_->FindSystemClass(soa.Self(), descriptor);
EXPECT_TRUE(klass != nullptr) << descriptor;
uint8_t* raw_klass = reinterpret_cast<uint8_t*>(klass.Ptr());
- if (image_classes.find(StringPiece(descriptor)) == image_classes.end()) {
+ if (image_classes.find(std::string_view(descriptor)) == image_classes.end()) {
EXPECT_TRUE(raw_klass >= image_end || raw_klass < image_begin) << descriptor;
} else {
// Image classes should be located inside the image.
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index 5aa8236..838510b 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -29,16 +29,18 @@
target: {
android: {
shared_libs: [
- "libdexfile",
"libartbase",
+ "libartpalette",
+ "libdexfile",
"libprofile",
"libbase",
],
},
not_windows: {
shared_libs: [
- "libdexfile",
"libartbase",
+ "libartpalette",
+ "libdexfile",
"libprofile",
"libbase",
],
@@ -46,8 +48,9 @@
windows: {
cflags: ["-Wno-thread-safety"],
static_libs: [
- "libdexfile",
"libartbase",
+ "libartpalette",
+ "libdexfile",
"libprofile",
"libbase",
],
diff --git a/dexlayout/dex_verify.cc b/dexlayout/dex_verify.cc
index 718d66f..83a7c69 100644
--- a/dexlayout/dex_verify.cc
+++ b/dexlayout/dex_verify.cc
@@ -22,6 +22,8 @@
#include <inttypes.h>
+#include <set>
+
#include "android-base/stringprintf.h"
namespace art {
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc
index 28d4048..ca9018d 100644
--- a/dexlayout/dexdiag.cc
+++ b/dexlayout/dexdiag.cc
@@ -22,12 +22,14 @@
#include <iostream>
#include <memory>
+#include <string>
+#include <string_view>
#include <vector>
#include "android-base/stringprintf.h"
#include "base/logging.h" // For InitLogging.
-#include "base/stringpiece.h"
+#include "base/string_view_cpp20.h"
#include "dexlayout.h"
#include "dex/dex_file.h"
@@ -462,14 +464,14 @@
std::vector<std::string> name_filters;
// TODO: add option to track usage by class name, etc.
for (int i = 1; i < argc - 1; ++i) {
- const StringPiece option(argv[i]);
+ const std::string_view option(argv[i]);
if (option == "--help") {
Usage(argv[0]);
return EXIT_SUCCESS;
} else if (option == "--verbose") {
g_verbose = true;
- } else if (option.starts_with("--contains=")) {
- std::string contains(option.substr(strlen("--contains=")).data());
+ } else if (StartsWith(option, "--contains=")) {
+ std::string contains(option.substr(strlen("--contains=")));
name_filters.push_back(contains);
} else {
Usage(argv[0]);
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 535f789..60076bf 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -25,6 +25,8 @@
#include <stdint.h>
#include <stdio.h>
+
+#include <set>
#include <unordered_map>
#include "dex/compact_dex_level.h"
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 92850f7..988c612 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -15,14 +15,16 @@
*/
#include <string>
+#include <string_view>
-#include "base/logging.h" // For InitLogging.
-#include "base/mutex.h"
-#include "base/os.h"
-#include "base/utils.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "base/file_utils.h"
+#include "base/logging.h" // For InitLogging.
+#include "base/mutex.h"
+#include "base/os.h"
+#include "base/string_view_cpp20.h"
+#include "base/utils.h"
#include "compiler_filter.h"
#include "class_loader_context.h"
#include "dex/dex_file.h"
@@ -155,56 +157,57 @@
}
for (int i = 0; i < argc; ++i) {
- const StringPiece option(argv[i]);
+ const char* raw_option = argv[i];
+ const std::string_view option(raw_option);
if (option == "--assume-profile-changed") {
assume_profile_changed_ = true;
- } else if (option.starts_with("--dex-file=")) {
- dex_file_ = option.substr(strlen("--dex-file=")).ToString();
- } else if (option.starts_with("--compiler-filter=")) {
- std::string filter_str = option.substr(strlen("--compiler-filter=")).ToString();
- if (!CompilerFilter::ParseCompilerFilter(filter_str.c_str(), &compiler_filter_)) {
- Usage("Invalid compiler filter '%s'", option.data());
+ } else if (StartsWith(option, "--dex-file=")) {
+ dex_file_ = std::string(option.substr(strlen("--dex-file=")));
+ } else if (StartsWith(option, "--compiler-filter=")) {
+ const char* filter_str = raw_option + strlen("--compiler-filter=");
+ if (!CompilerFilter::ParseCompilerFilter(filter_str, &compiler_filter_)) {
+ Usage("Invalid compiler filter '%s'", raw_option);
}
- } else if (option.starts_with("--isa=")) {
- std::string isa_str = option.substr(strlen("--isa=")).ToString();
- isa_ = GetInstructionSetFromString(isa_str.c_str());
+ } else if (StartsWith(option, "--isa=")) {
+ const char* isa_str = raw_option + strlen("--isa=");
+ isa_ = GetInstructionSetFromString(isa_str);
if (isa_ == InstructionSet::kNone) {
- Usage("Invalid isa '%s'", option.data());
+ Usage("Invalid isa '%s'", raw_option);
}
- } else if (option.starts_with("--image=")) {
- image_ = option.substr(strlen("--image=")).ToString();
+ } else if (StartsWith(option, "--image=")) {
+ image_ = std::string(option.substr(strlen("--image=")));
} else if (option == "--runtime-arg") {
if (i + 1 == argc) {
Usage("Missing argument for --runtime-arg\n");
}
++i;
runtime_args_.push_back(argv[i]);
- } else if (option.starts_with("--android-data=")) {
+ } else if (StartsWith(option, "--android-data=")) {
// Overwrite android-data if needed (oat file assistant relies on a valid directory to
// compute dalvik-cache folder). This is mostly used in tests.
- std::string new_android_data = option.substr(strlen("--android-data=")).ToString();
- setenv("ANDROID_DATA", new_android_data.c_str(), 1);
- } else if (option.starts_with("--downgrade")) {
+ const char* new_android_data = raw_option + strlen("--android-data=");
+ setenv("ANDROID_DATA", new_android_data, 1);
+ } else if (option == "--downgrade") {
downgrade_ = true;
- } else if (option.starts_with("--oat-fd")) {
- oat_fd_ = std::stoi(option.substr(strlen("--oat-fd=")).ToString(), nullptr, 0);
+ } else if (StartsWith(option, "--oat-fd=")) {
+ oat_fd_ = std::stoi(std::string(option.substr(strlen("--oat-fd="))), nullptr, 0);
if (oat_fd_ < 0) {
Usage("Invalid --oat-fd %d", oat_fd_);
}
- } else if (option.starts_with("--vdex-fd")) {
- vdex_fd_ = std::stoi(option.substr(strlen("--vdex-fd=")).ToString(), nullptr, 0);
+ } else if (StartsWith(option, "--vdex-fd=")) {
+ vdex_fd_ = std::stoi(std::string(option.substr(strlen("--vdex-fd="))), nullptr, 0);
if (vdex_fd_ < 0) {
Usage("Invalid --vdex-fd %d", vdex_fd_);
}
- } else if (option.starts_with("--zip-fd")) {
- zip_fd_ = std::stoi(option.substr(strlen("--zip-fd=")).ToString(), nullptr, 0);
- if (zip_fd_ < 0) {
- Usage("Invalid --zip-fd %d", zip_fd_);
- }
- } else if (option.starts_with("--class-loader-context=")) {
- context_str_ = option.substr(strlen("--class-loader-context=")).ToString();
+ } else if (StartsWith(option, "--zip-fd=")) {
+ zip_fd_ = std::stoi(std::string(option.substr(strlen("--zip-fd="))), nullptr, 0);
+ if (zip_fd_ < 0) {
+ Usage("Invalid --zip-fd %d", zip_fd_);
+ }
+ } else if (StartsWith(option, "--class-loader-context=")) {
+ context_str_ = std::string(option.substr(strlen("--class-loader-context=")));
} else {
- Usage("Unknown argument '%s'", option.data());
+ Usage("Unknown argument '%s'", raw_option);
}
}
diff --git a/imgdiag/Android.bp b/imgdiag/Android.bp
index 972c8f7..39720a0 100644
--- a/imgdiag/Android.bp
+++ b/imgdiag/Android.bp
@@ -31,9 +31,6 @@
"libbase",
],
target: {
- android: {
- shared_libs: ["libcutils"],
- },
host: {
shared_libs: ["libziparchive"],
},
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index a1edd00..5cd77e0 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -1674,23 +1674,27 @@
protected:
using Base = CmdlineArgs;
- ParseStatus ParseCustom(const StringPiece& option, std::string* error_msg) override {
+ ParseStatus ParseCustom(const char* raw_option,
+ size_t raw_option_length,
+ std::string* error_msg) override {
+ DCHECK_EQ(strlen(raw_option), raw_option_length);
{
- ParseStatus base_parse = Base::ParseCustom(option, error_msg);
+ ParseStatus base_parse = Base::ParseCustom(raw_option, raw_option_length, error_msg);
if (base_parse != kParseUnknownArgument) {
return base_parse;
}
}
- if (option.starts_with("--image-diff-pid=")) {
- const char* image_diff_pid = option.substr(strlen("--image-diff-pid=")).data();
+ std::string_view option(raw_option, raw_option_length);
+ if (StartsWith(option, "--image-diff-pid=")) {
+ const char* image_diff_pid = raw_option + strlen("--image-diff-pid=");
if (!android::base::ParseInt(image_diff_pid, &image_diff_pid_)) {
*error_msg = "Image diff pid out of range";
return kParseError;
}
- } else if (option.starts_with("--zygote-diff-pid=")) {
- const char* zygote_diff_pid = option.substr(strlen("--zygote-diff-pid=")).data();
+ } else if (StartsWith(option, "--zygote-diff-pid=")) {
+ const char* zygote_diff_pid = raw_option + strlen("--zygote-diff-pid=");
if (!android::base::ParseInt(zygote_diff_pid, &zygote_diff_pid_)) {
*error_msg = "Zygote diff pid out of range";
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 509b072..1ca7011 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -61,7 +61,7 @@
shared_libs: [
"liblog",
// For ashmem.
- "libcutils",
+ "libartpalette",
// For common macros.
"libbase",
],
@@ -80,7 +80,7 @@
"libz",
"liblog",
// For ashmem.
- "libcutils",
+ "libartpalette",
// For common macros.
"libbase",
],
@@ -99,7 +99,7 @@
"libz",
"liblog",
// For ashmem.
- "libcutils",
+ "libartpalette",
// For common macros.
"libbase",
],
@@ -128,7 +128,7 @@
name: "libartbase_static_base_defaults",
static_libs: [
"libbase",
- "libcutils",
+ "libartpalette",
"liblog",
"libz",
"libziparchive",
diff --git a/libartbase/base/hash_set.h b/libartbase/base/hash_set.h
index 42aa46f..99b3df4 100644
--- a/libartbase/base/hash_set.h
+++ b/libartbase/base/hash_set.h
@@ -139,7 +139,8 @@
std::hash<T>>::type;
struct DefaultStringEquals {
- // Allow comparison with anything that can be compared to std::string, for example StringPiece.
+ // Allow comparison with anything that can be compared to std::string,
+ // for example std::string_view.
template <typename T>
bool operator()(const std::string& lhs, const T& rhs) const {
return lhs == rhs;
diff --git a/libartbase/base/hash_set_test.cc b/libartbase/base/hash_set_test.cc
index 782a68b..0646967 100644
--- a/libartbase/base/hash_set_test.cc
+++ b/libartbase/base/hash_set_test.cc
@@ -20,12 +20,12 @@
#include <map>
#include <sstream>
#include <string>
+#include <string_view>
#include <unordered_set>
#include <vector>
#include <gtest/gtest.h>
-#include "base/stringpiece.h"
#include "hash_map.h"
namespace art {
@@ -365,11 +365,11 @@
ASSERT_EQ(*it, *cit);
}
-TEST_F(HashSetTest, StringSearchyStringPiece) {
+TEST_F(HashSetTest, StringSearchyStringView) {
const char* test_string = "dummy";
HashSet<std::string> hash_set;
HashSet<std::string>::iterator insert_pos = hash_set.insert(test_string);
- HashSet<std::string>::iterator it = hash_set.find(StringPiece(test_string));
+ HashSet<std::string>::iterator it = hash_set.find(std::string_view(test_string));
ASSERT_TRUE(it == insert_pos);
}
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index 2833750..ba2a7c6 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -796,13 +796,13 @@
// Shrink the reservation MemMap and update its `gMaps` entry.
std::lock_guard<std::mutex> mu(*mem_maps_lock_);
auto it = GetGMapsEntry(*this);
- // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
- gMaps->erase(it);
+ auto node = gMaps->extract(it);
begin_ += byte_count;
size_ -= byte_count;
base_begin_ = begin_;
base_size_ = size_;
- gMaps->emplace(base_begin_, this);
+ node.key() = base_begin_;
+ gMaps->insert(std::move(node));
}
}
@@ -1266,9 +1266,9 @@
std::lock_guard<std::mutex> mu(*mem_maps_lock_);
if (base_begin < aligned_base_begin) {
auto it = GetGMapsEntry(*this);
- // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
- gMaps->erase(it);
- gMaps->insert(std::make_pair(aligned_base_begin, this));
+ auto node = gMaps->extract(it);
+ node.key() = aligned_base_begin;
+ gMaps->insert(std::move(node));
}
base_begin_ = aligned_base_begin;
base_size_ = aligned_base_size;
diff --git a/libartbase/base/stl_util.h b/libartbase/base/stl_util.h
index 9d944b1..1e071ce 100644
--- a/libartbase/base/stl_util.h
+++ b/libartbase/base/stl_util.h
@@ -18,7 +18,6 @@
#define ART_LIBARTBASE_BASE_STL_UTIL_H_
#include <algorithm>
-#include <set>
#include <sstream>
#include <android-base/logging.h>
@@ -136,12 +135,6 @@
}
};
-// Merge `other` entries into `to_update`.
-template <typename T>
-static inline void MergeSets(std::set<T>& to_update, const std::set<T>& other) {
- to_update.insert(other.begin(), other.end());
-}
-
// Returns a copy of the passed vector that doesn't memory-own its entries.
template <typename T>
static inline std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
diff --git a/libartbase/base/string_view_cpp20.h b/libartbase/base/string_view_cpp20.h
new file mode 100644
index 0000000..2c11a2f
--- /dev/null
+++ b/libartbase/base/string_view_cpp20.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_STRING_VIEW_CPP20_H_
+#define ART_LIBARTBASE_BASE_STRING_VIEW_CPP20_H_
+
+#include <string_view>
+
+namespace art {
+
+// Replacement functions for std::string_view::starts_with(), ends_with()
+// which shall be available in C++20.
+#if __cplusplus >= 202000L
+#error "When upgrading to C++20, remove this error and file a bug to remove this workaround."
+#endif
+
+inline bool StartsWith(std::string_view sv, std::string_view prefix) {
+ return sv.substr(0u, prefix.size()) == prefix;
+}
+
+inline bool EndsWith(std::string_view sv, std::string_view suffix) {
+ return sv.size() >= suffix.size() && sv.substr(sv.size() - suffix.size()) == suffix;
+}
+
+} // namespace art
+
+#endif // ART_LIBARTBASE_BASE_STRING_VIEW_CPP20_H_
diff --git a/libartbase/base/stringpiece.h b/libartbase/base/stringpiece.h
deleted file mode 100644
index e8cc2c3..0000000
--- a/libartbase/base/stringpiece.h
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_LIBARTBASE_BASE_STRINGPIECE_H_
-#define ART_LIBARTBASE_BASE_STRINGPIECE_H_
-
-#include <string.h>
-#include <string>
-
-#include <android-base/logging.h>
-
-namespace art {
-
-// A string-like object that points to a sized piece of memory.
-//
-// Functions or methods may use const StringPiece& parameters to accept either
-// a "const char*" or a "string" value that will be implicitly converted to
-// a StringPiece. The implicit conversion means that it is often appropriate
-// to include this .h file in other files rather than forward-declaring
-// StringPiece as would be appropriate for most other Google classes.
-class StringPiece {
- public:
- // standard STL container boilerplate
- typedef char value_type;
- typedef const char* pointer;
- typedef const char& reference;
- typedef const char& const_reference;
- typedef size_t size_type;
- typedef ptrdiff_t difference_type;
- static constexpr size_type npos = size_type(-1);
- typedef const char* const_iterator;
- typedef const char* iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
- typedef std::reverse_iterator<iterator> reverse_iterator;
-
- // We provide non-explicit singleton constructors so users can pass
- // in a "const char*" or a "string" wherever a "StringPiece" is
- // expected.
- StringPiece() : ptr_(nullptr), length_(0) { }
- StringPiece(const char* str) // NOLINT implicit constructor desired
- : ptr_(str), length_((str == nullptr) ? 0 : strlen(str)) { }
- StringPiece(const std::string& str) // NOLINT implicit constructor desired
- : ptr_(str.data()), length_(str.size()) { }
- StringPiece(const char* offset, size_t len) : ptr_(offset), length_(len) { }
-
- // data() may return a pointer to a buffer with embedded NULs, and the
- // returned buffer may or may not be null terminated. Therefore it is
- // typically a mistake to pass data() to a routine that expects a NUL
- // terminated string.
- const char* data() const { return ptr_; }
- size_type size() const { return length_; }
- size_type length() const { return length_; }
- bool empty() const { return length_ == 0; }
-
- void clear() {
- ptr_ = nullptr;
- length_ = 0;
- }
- void set(const char* data_in, size_type len) {
- ptr_ = data_in;
- length_ = len;
- }
- void set(const char* str) {
- ptr_ = str;
- if (str != nullptr) {
- length_ = strlen(str);
- } else {
- length_ = 0;
- }
- }
- void set(const void* data_in, size_type len) {
- ptr_ = reinterpret_cast<const char*>(data_in);
- length_ = len;
- }
-
- char operator[](size_type i) const {
- DCHECK_LT(i, length_);
- return ptr_[i];
- }
-
- void remove_prefix(size_type n) {
- ptr_ += n;
- length_ -= n;
- }
-
- void remove_suffix(size_type n) {
- length_ -= n;
- }
-
- int compare(const StringPiece& x) const {
- int r = memcmp(ptr_, x.ptr_, std::min(length_, x.length_));
- if (r == 0) {
- if (length_ < x.length_) r = -1;
- else if (length_ > x.length_) r = +1;
- }
- return r;
- }
-
- std::string as_string() const {
- return std::string(data(), size());
- }
- // We also define ToString() here, since many other string-like
- // interfaces name the routine that converts to a C++ string
- // "ToString", and it's confusing to have the method that does that
- // for a StringPiece be called "as_string()". We also leave the
- // "as_string()" method defined here for existing code.
- std::string ToString() const {
- return std::string(data(), size());
- }
-
- void CopyToString(std::string* target) const {
- target->assign(ptr_, length_);
- }
-
- void AppendToString(std::string* target) const;
-
- // Does "this" start with "x"
- bool starts_with(const StringPiece& x) const {
- return ((length_ >= x.length_) &&
- (memcmp(ptr_, x.ptr_, x.length_) == 0));
- }
-
- // Does "this" end with "x"
- bool ends_with(const StringPiece& x) const {
- return ((length_ >= x.length_) &&
- (memcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
- }
-
- iterator begin() const { return ptr_; }
- iterator end() const { return ptr_ + length_; }
- const_reverse_iterator rbegin() const {
- return const_reverse_iterator(ptr_ + length_);
- }
- const_reverse_iterator rend() const {
- return const_reverse_iterator(ptr_);
- }
-
- size_type copy(char* buf, size_type n, size_type pos = 0) const {
- size_type ret = std::min(length_ - pos, n);
- memcpy(buf, ptr_ + pos, ret);
- return ret;
- }
-
- size_type find(const StringPiece& s, size_type pos = 0) const {
- if (length_ == 0 || pos > static_cast<size_type>(length_)) {
- return npos;
- }
- const char* result = std::search(ptr_ + pos, ptr_ + length_, s.ptr_, s.ptr_ + s.length_);
- const size_type xpos = result - ptr_;
- return xpos + s.length_ <= length_ ? xpos : npos;
- }
-
- size_type find(char c, size_type pos = 0) const {
- if (length_ == 0 || pos >= length_) {
- return npos;
- }
- const char* result = std::find(ptr_ + pos, ptr_ + length_, c);
- return result != ptr_ + length_ ? result - ptr_ : npos;
- }
-
- size_type rfind(const StringPiece& s, size_type pos = npos) const {
- if (length_ < s.length_) return npos;
- const size_t ulen = length_;
- if (s.length_ == 0) return std::min(ulen, pos);
-
- const char* last = ptr_ + std::min(ulen - s.length_, pos) + s.length_;
- const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
- return result != last ? result - ptr_ : npos;
- }
-
- size_type rfind(char c, size_type pos = npos) const {
- if (length_ == 0) return npos;
- for (int i = std::min(pos, static_cast<size_type>(length_ - 1));
- i >= 0; --i) {
- if (ptr_[i] == c) {
- return i;
- }
- }
- return npos;
- }
-
- StringPiece substr(size_type pos, size_type n = npos) const {
- if (pos > static_cast<size_type>(length_)) pos = length_;
- if (n > length_ - pos) n = length_ - pos;
- return StringPiece(ptr_ + pos, n);
- }
-
- int Compare(const StringPiece& rhs) const {
- const int r = memcmp(data(), rhs.data(), std::min(size(), rhs.size()));
- if (r != 0) {
- return r;
- }
- if (size() < rhs.size()) {
- return -1;
- } else if (size() > rhs.size()) {
- return 1;
- }
- return 0;
- }
-
- private:
- // Pointer to char data, not necessarily zero terminated.
- const char* ptr_;
- // Length of data.
- size_type length_;
-};
-
-// This large function is defined inline so that in a fairly common case where
-// one of the arguments is a literal, the compiler can elide a lot of the
-// following comparisons.
-inline bool operator==(const StringPiece& x, const StringPiece& y) {
- StringPiece::size_type len = x.size();
- if (len != y.size()) {
- return false;
- }
-
- const char* p1 = x.data();
- const char* p2 = y.data();
- if (p1 == p2) {
- return true;
- }
- if (len == 0) {
- return true;
- }
-
- // Test last byte in case strings share large common prefix
- if (p1[len-1] != p2[len-1]) return false;
- if (len == 1) return true;
-
- // At this point we can, but don't have to, ignore the last byte. We use
- // this observation to fold the odd-length case into the even-length case.
- len &= ~1;
-
- return memcmp(p1, p2, len) == 0;
-}
-
-inline bool operator==(const StringPiece& x, const char* y) {
- if (y == nullptr) {
- return x.size() == 0;
- } else {
- return strncmp(x.data(), y, x.size()) == 0 && y[x.size()] == '\0';
- }
-}
-
-inline bool operator!=(const StringPiece& x, const StringPiece& y) {
- return !(x == y);
-}
-
-inline bool operator!=(const StringPiece& x, const char* y) {
- return !(x == y);
-}
-
-inline bool operator<(const StringPiece& x, const StringPiece& y) {
- return x.Compare(y) < 0;
-}
-
-inline bool operator>(const StringPiece& x, const StringPiece& y) {
- return y < x;
-}
-
-inline bool operator<=(const StringPiece& x, const StringPiece& y) {
- return !(x > y);
-}
-
-inline bool operator>=(const StringPiece& x, const StringPiece& y) {
- return !(x < y);
-}
-
-inline std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
- o.write(piece.data(), piece.size());
- return o;
-}
-
-} // namespace art
-
-#endif // ART_LIBARTBASE_BASE_STRINGPIECE_H_
diff --git a/libartbase/base/systrace.h b/libartbase/base/systrace.h
index d995dce..30bba49 100644
--- a/libartbase/base/systrace.h
+++ b/libartbase/base/systrace.h
@@ -17,33 +17,52 @@
#ifndef ART_LIBARTBASE_BASE_SYSTRACE_H_
#define ART_LIBARTBASE_BASE_SYSTRACE_H_
-#define ATRACE_TAG ATRACE_TAG_DALVIK
-#include <cutils/trace.h>
-
#include <sstream>
#include <string>
#include "android-base/stringprintf.h"
#include "macros.h"
+#include "palette/palette.h"
namespace art {
+inline bool ATraceEnabled() {
+ int enabled = 0;
+ if (UNLIKELY(PaletteTraceEnabled(&enabled) == PaletteStatus::kOkay && enabled != 0)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+inline void ATraceBegin(const char* name) {
+ PaletteTraceBegin(name);
+}
+
+inline void ATraceEnd() {
+ PaletteTraceEnd();
+}
+
+inline void ATraceIntegerValue(const char* name, int32_t value) {
+ PaletteTraceIntegerValue(name, value);
+}
+
class ScopedTrace {
public:
explicit ScopedTrace(const char* name) {
- ATRACE_BEGIN(name);
+ ATraceBegin(name);
}
template <typename Fn>
explicit ScopedTrace(Fn fn) {
- if (ATRACE_ENABLED()) {
- ATRACE_BEGIN(fn().c_str());
+ if (UNLIKELY(ATraceEnabled())) {
+ ATraceBegin(fn().c_str());
}
}
explicit ScopedTrace(const std::string& name) : ScopedTrace(name.c_str()) {}
~ScopedTrace() {
- ATRACE_END();
+ ATraceEnd();
}
};
@@ -54,7 +73,7 @@
}
~ScopedTraceNoStart() {
- ATRACE_END();
+ ATraceEnd();
}
// Message helper for the macro. Do not use directly.
@@ -63,7 +82,7 @@
ScopedTraceMessageHelper() {
}
~ScopedTraceMessageHelper() {
- ATRACE_BEGIN(buffer_.str().c_str());
+ ATraceBegin(buffer_.str().c_str());
}
std::ostream& stream() {
@@ -77,7 +96,7 @@
#define SCOPED_TRACE \
::art::ScopedTraceNoStart APPEND_TOKENS_AFTER_EVAL(trace, __LINE__) ; \
- (ATRACE_ENABLED()) && ::art::ScopedTraceNoStart::ScopedTraceMessageHelper().stream()
+ (ATraceEnabled()) && ::art::ScopedTraceNoStart::ScopedTraceMessageHelper().stream()
} // namespace art
diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc
index b989d9e..30423a4 100644
--- a/libartbase/base/utils.cc
+++ b/libartbase/base/utils.cc
@@ -190,45 +190,6 @@
#endif
}
-static void ParseStringAfterChar(const std::string& s,
- char c,
- std::string* parsed_value,
- UsageFn Usage) {
- std::string::size_type colon = s.find(c);
- if (colon == std::string::npos) {
- Usage("Missing char %c in option %s\n", c, s.c_str());
- }
- // Add one to remove the char we were trimming until.
- *parsed_value = s.substr(colon + 1);
-}
-
-void ParseDouble(const std::string& option,
- char after_char,
- double min,
- double max,
- double* parsed_value,
- UsageFn Usage) {
- std::string substring;
- ParseStringAfterChar(option, after_char, &substring, Usage);
- bool sane_val = true;
- double value;
- if ((false)) {
- // TODO: this doesn't seem to work on the emulator. b/15114595
- std::stringstream iss(substring);
- iss >> value;
- // Ensure that we have a value, there was no cruft after it and it satisfies a sensible range.
- sane_val = iss.eof() && (value >= min) && (value <= max);
- } else {
- char* end = nullptr;
- value = strtod(substring.c_str(), &end);
- sane_val = *end == '\0' && value >= min && value <= max;
- }
- if (!sane_val) {
- Usage("Invalid double value %s for option %s\n", substring.c_str(), option.c_str());
- }
- *parsed_value = value;
-}
-
void SleepForever() {
while (true) {
usleep(1000000);
diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h
index 11472a8..9284950 100644
--- a/libartbase/base/utils.h
+++ b/libartbase/base/utils.h
@@ -30,7 +30,6 @@
#include "enums.h"
#include "globals.h"
#include "macros.h"
-#include "stringpiece.h"
namespace art {
@@ -91,44 +90,6 @@
return reinterpret_cast<const void*>(code);
}
-using UsageFn = void (*)(const char*, ...);
-
-template <typename T>
-static void ParseIntOption(const StringPiece& option,
- const std::string& option_name,
- T* out,
- UsageFn usage,
- bool is_long_option = true) {
- std::string option_prefix = option_name + (is_long_option ? "=" : "");
- DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix;
- const char* value_string = option.substr(option_prefix.size()).data();
- int64_t parsed_integer_value = 0;
- if (!android::base::ParseInt(value_string, &parsed_integer_value)) {
- usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value_string);
- }
- *out = dchecked_integral_cast<T>(parsed_integer_value);
-}
-
-template <typename T>
-static void ParseUintOption(const StringPiece& option,
- const std::string& option_name,
- T* out,
- UsageFn usage,
- bool is_long_option = true) {
- ParseIntOption(option, option_name, out, usage, is_long_option);
- if (*out < 0) {
- usage("%s passed a negative value %d", option_name.c_str(), *out);
- *out = 0;
- }
-}
-
-void ParseDouble(const std::string& option,
- char after_char,
- double min,
- double max,
- double* parsed_value,
- UsageFn Usage);
-
#if defined(__BIONIC__)
struct Arc4RandomGenerator {
typedef uint32_t result_type;
diff --git a/libartpalette/Android.bp b/libartpalette/Android.bp
new file mode 100644
index 0000000..778109d
--- /dev/null
+++ b/libartpalette/Android.bp
@@ -0,0 +1,116 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "libartpalette_defaults",
+ defaults: ["art_defaults"],
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
+// libartpalette-system is the implementation of the abstraction layer. It is
+// only available as a shared library on Android.
+art_cc_library {
+ name: "libartpalette-system",
+ defaults: ["libartpalette_defaults"],
+
+ target: {
+ android: {
+ srcs: ["system/palette_android.cc",],
+ header_libs: ["libbase_headers"],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libprocessgroup",
+ ],
+ },
+ host: {
+ header_libs: ["libbase_headers"],
+ srcs: ["system/palette_fake.cc",],
+ },
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+ static: {
+ enabled: false,
+ },
+ version_script: "libartpalette.map.txt",
+}
+
+// libartpalette is the dynamic loader of the platform abstraction
+// layer. It is only used on Android. For other targets, it just
+// implements a fake platform implementation.
+art_cc_library {
+ name: "libartpalette",
+ defaults: ["libartpalette_defaults"],
+ required: ["libartpalette-system"], // libartpalette.so dlopen()'s libartpalette-system.
+ header_libs: ["libbase_headers"],
+ target: {
+ // Targets supporting dlopen build the client library which loads
+ // and binds the methods in the libartpalette-system library.
+ android: {
+ srcs: ["apex/palette.cc"],
+ shared: {
+ shared_libs: ["liblog"],
+ },
+ static: {
+ static_libs: ["liblog"],
+ },
+ version_script: "libartpalette.map.txt",
+ },
+ linux_bionic: {
+ header_libs: ["libbase_headers"],
+ srcs: ["system/palette_fake.cc"],
+ shared: {
+ shared_libs: ["liblog"],
+ },
+ version_script: "libartpalette.map.txt",
+ },
+ linux_glibc: {
+ header_libs: ["libbase_headers"],
+ srcs: ["system/palette_fake.cc"],
+ shared: {
+ shared_libs: ["liblog"],
+ },
+ version_script: "libartpalette.map.txt",
+ },
+ // Targets without support for dlopen just use the sources for
+ // the system library which actually implements functionality.
+ darwin: {
+ enabled: true,
+ header_libs: ["libbase_headers"],
+ srcs: ["system/palette_fake.cc"],
+ },
+ windows: {
+ enabled: true,
+ header_libs: ["libbase_headers"],
+ srcs: ["system/palette_fake.cc"],
+ },
+ }
+}
+
+art_cc_test {
+ name: "art_libartpalette_tests",
+ defaults: ["art_gtest_defaults"],
+ host_supported: true,
+ srcs: ["apex/palette_test.cc"],
+ shared_libs: ["libartpalette"],
+ test_per_src: true,
+}
diff --git a/libartpalette/apex/palette.cc b/libartpalette/apex/palette.cc
new file mode 100644
index 0000000..0b391f8
--- /dev/null
+++ b/libartpalette/apex/palette.cc
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+#include "palette/palette.h"
+
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#include <android/log.h>
+#include <android-base/macros.h>
+
+namespace {
+
+// Logging tag.
+static constexpr const char* kLogTag = "libartpalette";
+
+// Name of the palette library present in the /system partition.
+static constexpr const char* kPaletteSystemLibrary = "libartpalette-system.so";
+
+// Generic method used when a dynamically loaded palette instance does not
+// support a method.
+enum PaletteStatus PaletteMethodNotSupported() {
+ return PaletteStatus::kNotSupported;
+}
+
+// Declare type aliases for pointers to each function in the interface.
+#define PALETTE_METHOD_TYPE_ALIAS(Name, ...) \
+ using Name ## Method = PaletteStatus(*)(__VA_ARGS__);
+PALETTE_METHOD_LIST(PALETTE_METHOD_TYPE_ALIAS)
+#undef PALETTE_METHOD_TYPE_ALIAS
+
+// Singleton class responsible for dynamically loading the palette library and
+// binding functions there to method pointers.
+class PaletteLoader {
+ public:
+ static PaletteLoader& Instance() {
+ static PaletteLoader instance;
+ return instance;
+ }
+
+ // Accessor methods to get instances of palette methods.
+#define PALETTE_LOADER_METHOD_ACCESSOR(Name, ...) \
+ Name ## Method Get ## Name ## Method() const { return Name ## Method ## _; }
+PALETTE_METHOD_LIST(PALETTE_LOADER_METHOD_ACCESSOR)
+#undef PALETTE_LOADER_METHOD_ACCESSOR
+
+ private:
+ PaletteLoader();
+
+ static void* OpenLibrary();
+ static void* GetMethod(void* palette_lib, const char* name);
+
+ // Handle to the palette library from dlopen().
+ void* palette_lib_;
+
+ // Fields to store pointers to palette methods.
+#define PALETTE_LOADER_METHOD_FIELD(Name, ...) \
+ const Name ## Method Name ## Method ## _;
+ PALETTE_METHOD_LIST(PALETTE_LOADER_METHOD_FIELD)
+#undef PALETTE_LOADER_METHOD_FIELD
+
+ DISALLOW_COPY_AND_ASSIGN(PaletteLoader);
+};
+
+void* PaletteLoader::OpenLibrary() {
+ void* handle = dlopen(kPaletteSystemLibrary, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
+ if (handle == nullptr) {
+ // dlerror message includes details of error and file being opened.
+ __android_log_assert(nullptr, kLogTag, "%s", dlerror());
+ }
+ return handle;
+}
+
+void* PaletteLoader::GetMethod(void* palette_lib, const char* name) {
+ void* method = nullptr;
+ if (palette_lib != nullptr) {
+ method = dlsym(palette_lib, name);
+ }
+ if (method == nullptr) {
+ return reinterpret_cast<void*>(PaletteMethodNotSupported);
+ }
+ // TODO(oth): consider new GetMethodSignature() in the Palette API which
+ // would allow sanity checking the type signatures.
+ return method;
+}
+
+PaletteLoader::PaletteLoader() :
+ palette_lib_(OpenLibrary())
+#define PALETTE_LOADER_BIND_METHOD(Name, ...) \
+ , Name ## Method ## _(reinterpret_cast<Name ## Method>(GetMethod(palette_lib_, #Name)))
+ PALETTE_METHOD_LIST(PALETTE_LOADER_BIND_METHOD)
+#undef PALETTE_LOADER_BIND_METHOD
+{
+}
+
+} // namespace
+
+extern "C" {
+
+enum PaletteStatus PaletteGetVersion(/*out*/int32_t* version) {
+ PaletteGetVersionMethod m = PaletteLoader::Instance().GetPaletteGetVersionMethod();
+ return m(version);
+}
+
+enum PaletteStatus PaletteSchedSetPriority(int32_t tid, int32_t java_priority) {
+ PaletteSchedSetPriorityMethod m = PaletteLoader::Instance().GetPaletteSchedSetPriorityMethod();
+ return m(tid, java_priority);
+}
+
+enum PaletteStatus PaletteSchedGetPriority(int32_t tid, /*out*/int32_t* java_priority) {
+ PaletteSchedGetPriorityMethod m = PaletteLoader::Instance().GetPaletteSchedGetPriorityMethod();
+ return m(tid, java_priority);
+}
+
+enum PaletteStatus PaletteTraceEnabled(/*out*/int32_t* enabled) {
+ PaletteTraceEnabledMethod m = PaletteLoader::Instance().GetPaletteTraceEnabledMethod();
+ return m(enabled);
+}
+
+enum PaletteStatus PaletteTraceBegin(/*in*/const char* name) {
+ PaletteTraceBeginMethod m = PaletteLoader::Instance().GetPaletteTraceBeginMethod();
+ return m(name);
+}
+
+enum PaletteStatus PaletteTraceEnd() {
+ PaletteTraceEndMethod m = PaletteLoader::Instance().GetPaletteTraceEndMethod();
+ return m();
+}
+
+enum PaletteStatus PaletteTraceIntegerValue(/*in*/const char* name, int32_t value) {
+ PaletteTraceIntegerValueMethod m = PaletteLoader::Instance().GetPaletteTraceIntegerValueMethod();
+ return m(name, value);
+}
+
+} // extern "C"
diff --git a/libartpalette/apex/palette_test.cc b/libartpalette/apex/palette_test.cc
new file mode 100644
index 0000000..8bbe0ee
--- /dev/null
+++ b/libartpalette/apex/palette_test.cc
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include "palette/palette.h"
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include "gtest/gtest.h"
+
+namespace {
+
+pid_t GetTid() {
+#ifdef __BIONIC__
+ return gettid();
+#else // __BIONIC__
+ return syscall(__NR_gettid);
+#endif // __BIONIC__
+}
+
+} // namespace
+
+class PaletteClientTest : public testing::Test {};
+
+TEST_F(PaletteClientTest, GetVersion) {
+ int32_t version = -1;
+ PaletteStatus status = PaletteGetVersion(&version);
+ ASSERT_EQ(PaletteStatus::kOkay, status);
+ ASSERT_GE(version, 1);
+}
+
+TEST_F(PaletteClientTest, SchedPriority) {
+ int32_t tid = GetTid();
+ int32_t saved_priority;
+ EXPECT_EQ(PaletteStatus::kOkay, PaletteSchedGetPriority(tid, &saved_priority));
+
+ EXPECT_EQ(PaletteStatus::kInvalidArgument, PaletteSchedSetPriority(tid, /*java_priority=*/ 0));
+ EXPECT_EQ(PaletteStatus::kInvalidArgument, PaletteSchedSetPriority(tid, /*java_priority=*/ -1));
+ EXPECT_EQ(PaletteStatus::kInvalidArgument, PaletteSchedSetPriority(tid, /*java_priority=*/ 11));
+
+ EXPECT_EQ(PaletteStatus::kOkay, PaletteSchedSetPriority(tid, /*java_priority=*/ 1));
+ EXPECT_EQ(PaletteStatus::kOkay, PaletteSchedSetPriority(tid, saved_priority));
+}
+
+TEST_F(PaletteClientTest, Trace) {
+ int32_t enabled;
+ EXPECT_EQ(PaletteStatus::kOkay, PaletteTraceEnabled(&enabled));
+ EXPECT_EQ(PaletteStatus::kOkay, PaletteTraceBegin("Hello world!"));
+ EXPECT_EQ(PaletteStatus::kOkay, PaletteTraceEnd());
+ EXPECT_EQ(PaletteStatus::kOkay, PaletteTraceIntegerValue("Beans", /*value=*/ 3));
+}
diff --git a/libartpalette/include/palette/palette.h b/libartpalette/include/palette/palette.h
new file mode 100644
index 0000000..1f58403
--- /dev/null
+++ b/libartpalette/include/palette/palette.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_H_
+#define ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_H_
+
+#include "palette_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// Palette method signatures are defined in palette_method_list.h.
+
+#define PALETTE_METHOD_DECLARATION(Name, ...) \
+ enum PaletteStatus Name(__VA_ARGS__);
+#include "palette_method_list.h"
+PALETTE_METHOD_LIST(PALETTE_METHOD_DECLARATION)
+#undef PALETTE_METHOD_DECLARATION
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_H_
diff --git a/libartpalette/include/palette/palette_method_list.h b/libartpalette/include/palette/palette_method_list.h
new file mode 100644
index 0000000..dc4ec52
--- /dev/null
+++ b/libartpalette/include/palette/palette_method_list.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_METHOD_LIST_H_
+#define ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_METHOD_LIST_H_
+
+#include <stdint.h>
+
+// Methods in version 1 API
+#define PALETTE_METHOD_LIST(M) \
+ M(PaletteGetVersion, /*out*/int32_t* version) \
+ M(PaletteSchedSetPriority, int32_t tid, int32_t java_priority) \
+ M(PaletteSchedGetPriority, int32_t tid, /*out*/int32_t* java_priority) \
+ M(PaletteTraceEnabled, /*out*/int32_t* enabled) \
+ M(PaletteTraceBegin, const char* name) \
+ M(PaletteTraceEnd) \
+ M(PaletteTraceIntegerValue, const char* name, int32_t value)
+
+#endif // ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_METHOD_LIST_H_
diff --git a/libartpalette/include/palette/palette_types.h b/libartpalette/include/palette/palette_types.h
new file mode 100644
index 0000000..837086e
--- /dev/null
+++ b/libartpalette/include/palette/palette_types.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_TYPES_H_
+#define ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_TYPES_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// Return values for palette functions.
+enum PaletteStatus {
+ kOkay = 0,
+ kCheckErrno = 1,
+ kInvalidArgument = 2,
+ kNotSupported = 3,
+};
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_TYPES_H_
diff --git a/libartpalette/libartpalette.map.txt b/libartpalette/libartpalette.map.txt
new file mode 100644
index 0000000..0920835
--- /dev/null
+++ b/libartpalette/libartpalette.map.txt
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+LIBARTPALETTE_1 {
+ global:
+ # --- VERSION 01 API ---
+ PaletteGetVersion;
+ PaletteSchedSetPriority;
+ PaletteSchedGetPriority;
+ PaletteTraceEnabled;
+ PaletteTraceBegin;
+ PaletteTraceEnd;
+ PaletteTraceIntegerValue;
+
+ local:
+ *;
+};
diff --git a/libartpalette/system/palette_android.cc b/libartpalette/system/palette_android.cc
new file mode 100644
index 0000000..aed3862
--- /dev/null
+++ b/libartpalette/system/palette_android.cc
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+
+#include "palette/palette.h"
+
+#include <errno.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <mutex>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <cutils/sched_policy.h>
+#include <cutils/trace.h>
+#include <log/event_tag_map.h>
+#include <utils/Thread.h>
+
+#include "palette_system.h"
+
+enum PaletteStatus PaletteGetVersion(int32_t* version) {
+ *version = art::palette::kPaletteVersion;
+ return PaletteStatus::kOkay;
+}
+
+// Conversion map for "nice" values.
+//
+// We use Android thread priority constants to be consistent with the rest
+// of the system. In some cases adjacent entries may overlap.
+//
+static const int kNiceValues[art::palette::kNumManagedThreadPriorities] = {
+ ANDROID_PRIORITY_LOWEST, // 1 (MIN_PRIORITY)
+ ANDROID_PRIORITY_BACKGROUND + 6,
+ ANDROID_PRIORITY_BACKGROUND + 3,
+ ANDROID_PRIORITY_BACKGROUND,
+ ANDROID_PRIORITY_NORMAL, // 5 (NORM_PRIORITY)
+ ANDROID_PRIORITY_NORMAL - 2,
+ ANDROID_PRIORITY_NORMAL - 4,
+ ANDROID_PRIORITY_URGENT_DISPLAY + 3,
+ ANDROID_PRIORITY_URGENT_DISPLAY + 2,
+ ANDROID_PRIORITY_URGENT_DISPLAY // 10 (MAX_PRIORITY)
+};
+
+enum PaletteStatus PaletteSchedSetPriority(int32_t tid, int32_t managed_priority) {
+ if (managed_priority < art::palette::kMinManagedThreadPriority ||
+ managed_priority > art::palette::kMaxManagedThreadPriority) {
+ return PaletteStatus::kInvalidArgument;
+ }
+ int new_nice = kNiceValues[managed_priority - art::palette::kMinManagedThreadPriority];
+
+ // TODO: b/18249098 The code below is broken. It uses getpriority() as a proxy for whether a
+ // thread is already in the SP_FOREGROUND cgroup. This is not necessarily true for background
+ // processes, where all threads are in the SP_BACKGROUND cgroup. This means that callers will
+ // have to call setPriority twice to do what they want :
+ //
+ // Thread.setPriority(Thread.MIN_PRIORITY); // no-op wrt to cgroups
+ // Thread.setPriority(Thread.MAX_PRIORITY); // will actually change cgroups.
+ if (new_nice >= ANDROID_PRIORITY_BACKGROUND) {
+ set_sched_policy(tid, SP_BACKGROUND);
+ } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
+ set_sched_policy(tid, SP_FOREGROUND);
+ }
+
+ if (setpriority(PRIO_PROCESS, tid, new_nice) != 0) {
+ return PaletteStatus::kCheckErrno;
+ }
+ return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteSchedGetPriority(int32_t tid, /*out*/int32_t* managed_priority) {
+ errno = 0;
+ int native_priority = getpriority(PRIO_PROCESS, tid);
+ if (native_priority == -1 && errno != 0) {
+ *managed_priority = art::palette::kNormalManagedThreadPriority;
+ return PaletteStatus::kCheckErrno;
+ }
+
+ for (int p = art::palette::kMinManagedThreadPriority;
+ p <= art::palette::kMaxManagedThreadPriority;
+ p = p + 1) {
+ int index = p - art::palette::kMinManagedThreadPriority;
+ if (native_priority >= kNiceValues[index]) {
+ *managed_priority = p;
+ return PaletteStatus::kOkay;
+ }
+ }
+ *managed_priority = art::palette::kMaxManagedThreadPriority;
+ return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceEnabled(/*out*/int32_t* enabled) {
+ *enabled = (ATRACE_ENABLED() != 0) ? 1 : 0;
+ return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceBegin(const char* name) {
+ ATRACE_BEGIN(name);
+ return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceEnd() {
+ ATRACE_END();
+ return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceIntegerValue(const char* name, int32_t value) {
+ ATRACE_INT(name, value);
+ return PaletteStatus::kOkay;
+}
diff --git a/libartpalette/system/palette_fake.cc b/libartpalette/system/palette_fake.cc
new file mode 100644
index 0000000..0961e77
--- /dev/null
+++ b/libartpalette/system/palette_fake.cc
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include "palette/palette.h"
+
+#include <map>
+#include <mutex>
+
+#include <android-base/macros.h> // For ATTRIBUTE_UNUSED
+
+#include "palette_system.h"
+
+enum PaletteStatus PaletteGetVersion(int32_t* version) {
+ *version = art::palette::kPaletteVersion;
+ return PaletteStatus::kOkay;
+}
+
+// Cached thread priority for testing. No thread priorities are ever affected.
+static std::mutex g_tid_priority_map_mutex;
+static std::map<int32_t, int32_t> g_tid_priority_map;
+
+enum PaletteStatus PaletteSchedSetPriority(int32_t tid, int32_t priority) {
+ if (priority < art::palette::kMinManagedThreadPriority ||
+ priority > art::palette::kMaxManagedThreadPriority) {
+ return PaletteStatus::kInvalidArgument;
+ }
+ std::lock_guard guard(g_tid_priority_map_mutex);
+ g_tid_priority_map[tid] = priority;
+ return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteSchedGetPriority(int32_t tid,
+ /*out*/int32_t* priority) {
+ std::lock_guard guard(g_tid_priority_map_mutex);
+ if (g_tid_priority_map.find(tid) == g_tid_priority_map.end()) {
+ g_tid_priority_map[tid] = art::palette::kNormalManagedThreadPriority;
+ }
+ *priority = g_tid_priority_map[tid];
+ return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceEnabled(/*out*/int32_t* enabled) {
+ *enabled = 0;
+ return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceBegin(const char* name ATTRIBUTE_UNUSED) {
+ return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceEnd() {
+ return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceIntegerValue(const char* name ATTRIBUTE_UNUSED,
+ int32_t value ATTRIBUTE_UNUSED) {
+ return PaletteStatus::kOkay;
+}
diff --git a/libartpalette/system/palette_system.h b/libartpalette/system/palette_system.h
new file mode 100644
index 0000000..b28e00d
--- /dev/null
+++ b/libartpalette/system/palette_system.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_LIBARTPALETTE_SYSTEM_PALETTE_SYSTEM_H_
+#define ART_LIBARTPALETTE_SYSTEM_PALETTE_SYSTEM_H_
+
+#include <stdint.h>
+
+namespace art {
+namespace palette {
+
+static constexpr int32_t kPaletteVersion = 1;
+
+// Managed thread definitions
+static constexpr int32_t kNormalManagedThreadPriority = 5;
+static constexpr int32_t kMinManagedThreadPriority = 1;
+static constexpr int32_t kMaxManagedThreadPriority = 10;
+static constexpr int32_t kNumManagedThreadPriorities =
+ kMaxManagedThreadPriority - kMinManagedThreadPriority + 1;
+
+} // namespace palette
+} // namespace art
+
+#endif // ART_LIBARTPALETTE_SYSTEM_PALETTE_SYSTEM_H_
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index 2f56a3d..2289a9c 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -47,9 +47,8 @@
shared_libs: [
// For MemMap.
"libartbase",
+ "libartpalette",
"liblog",
- // For atrace.
- "libcutils",
// For common macros.
"libbase",
],
@@ -64,9 +63,8 @@
"libz",
// For MemMap.
"libartbase",
+ "libartpalette",
"liblog",
- // For atrace.
- "libcutils",
// For common macros.
"libbase",
],
@@ -81,9 +79,8 @@
"libz",
// For MemMap.
"libartbase",
+ "libartpalette",
"liblog",
- // For atrace.
- "libcutils",
// For common macros.
"libbase",
],
@@ -102,7 +99,6 @@
name: "libdexfile_static_base_defaults",
static_libs: [
"libbase",
- "libcutils",
"liblog",
"libz",
"libziparchive",
@@ -209,6 +205,8 @@
cc_library_headers {
name: "libdexfile_external_headers",
host_supported: true,
+ vendor_available: true,
+ recovery_available: true,
header_libs: ["libbase_headers"],
export_header_lib_headers: ["libbase_headers"],
export_include_dirs: ["external/include"],
@@ -232,28 +230,9 @@
"libdexfile",
],
- // TODO(b/120670568): Enable this when linking bug is fixed.
- // stubs: {
- // symbol_file: "external/libdexfile_external.map.txt",
- // versions: ["1"],
- // },
-
- // Hide symbols using version scripts for targets that support it, i.e. all
- // but Darwin.
- // TODO(b/120670568): Clean this up when stubs above is enabled.
- target: {
- android: {
- version_script: "external/libdexfile_external.map.txt",
- },
- linux_bionic: {
- version_script: "external/libdexfile_external.map.txt",
- },
- linux_glibc: {
- version_script: "external/libdexfile_external.map.txt",
- },
- windows: {
- version_script: "external/libdexfile_external.map.txt",
- },
+ stubs: {
+ symbol_file: "external/libdexfile_external.map.txt",
+ versions: ["1"],
},
}
@@ -268,23 +247,37 @@
}
// Support library with a C++ API for accessing the libdexfile API for external
-// (non-ART) users. They should link to their own instance of this (either
-// statically or through linker namespaces).
+// (non-ART) users.
+//
+// This library dlopens libdexfile_external on first use, so there is no build
+// time dependency on dex file logic. It is therefore safe to use from binaries
+// compiled without dex file support, given they won't encounter any dex file
+// stack frames.
cc_library {
name: "libdexfile_support",
host_supported: true,
+ vendor_available: true,
+ recovery_available: true,
srcs: [
"external/dex_file_supp.cc",
],
+ required: ["libdexfile_external"],
+ shared_libs: ["liblog"],
header_libs: ["libdexfile_external_headers"],
- shared_libs: ["libdexfile_external"],
export_header_lib_headers: ["libdexfile_external_headers"],
+ target: {
+ recovery: {
+ cflags: ["-DNO_DEXFILE_SUPPORT"],
+ },
+ vendor: {
+ cflags: ["-DNO_DEXFILE_SUPPORT"],
+ },
+ },
}
art_cc_test {
name: "art_libdexfile_support_tests",
host_supported: true,
- test_per_src: true, // For consistency with other ART gtests.
srcs: [
"external/dex_file_supp_test.cc",
],
@@ -295,3 +288,36 @@
"libdexfile_support",
],
}
+
+cc_library_static {
+ name: "libdexfile_support_static",
+ host_supported: true,
+ defaults: ["libdexfile_static_defaults"],
+ srcs: [
+ "external/dex_file_supp.cc",
+ ],
+ cflags: ["-DSTATIC_LIB"],
+ // Using whole_static_libs here only as a "poor man's transitivity" kludge.
+ whole_static_libs: [
+ "libbase",
+ "libdexfile",
+ "libdexfile_external",
+ "liblog",
+ "libz",
+ "libziparchive",
+ ],
+ header_libs: ["libdexfile_external_headers"],
+ export_header_lib_headers: ["libdexfile_external_headers"],
+}
+
+art_cc_test {
+ name: "art_libdexfile_support_static_tests",
+ host_supported: true,
+ srcs: [
+ "external/dex_file_supp_test.cc",
+ ],
+ static_libs: [
+ "libbase",
+ "libdexfile_support_static",
+ ],
+}
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index 2af1e04..15ba9cc 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -22,7 +22,6 @@
#include "base/casts.h"
#include "base/iteration_range.h"
#include "base/leb128.h"
-#include "base/stringpiece.h"
#include "base/utils.h"
#include "class_iterator.h"
#include "compact_dex_file.h"
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index 39377a3..7db4de0 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -400,7 +400,7 @@
}
// Given a signature place the type ids into the given vector
-bool DexFile::CreateTypeList(const StringPiece& signature,
+bool DexFile::CreateTypeList(std::string_view signature,
dex::TypeIndex* return_type_idx,
std::vector<dex::TypeIndex>* param_type_idxs) const {
if (signature[0] != '(') {
@@ -450,20 +450,6 @@
return false; // failed to correctly parse return type
}
-const Signature DexFile::CreateSignature(const StringPiece& signature) const {
- dex::TypeIndex return_type_idx;
- std::vector<dex::TypeIndex> param_type_indices;
- bool success = CreateTypeList(signature, &return_type_idx, ¶m_type_indices);
- if (!success) {
- return Signature::NoSignature();
- }
- const ProtoId* proto_id = FindProtoId(return_type_idx, param_type_indices);
- if (proto_id == nullptr) {
- return Signature::NoSignature();
- }
- return Signature(this, *proto_id);
-}
-
int32_t DexFile::FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address) {
uint32_t min = 0;
uint32_t max = tries_size;
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index c7fbe78..4dae1c0 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -19,6 +19,7 @@
#include <memory>
#include <string>
+#include <string_view>
#include <vector>
#include <android-base/logging.h>
@@ -44,7 +45,6 @@
class MemMap;
class OatDexFile;
class StandardDexFile;
-class StringPiece;
class ZipArchive;
// Some instances of DexFile own the storage referred to by DexFile. Clients who create
@@ -479,14 +479,10 @@
}
// Given a signature place the type ids into the given vector, returns true on success
- bool CreateTypeList(const StringPiece& signature,
+ bool CreateTypeList(std::string_view signature,
dex::TypeIndex* return_type_idx,
std::vector<dex::TypeIndex>* param_type_idxs) const;
- // Create a Signature from the given string signature or return Signature::NoSignature if not
- // possible.
- const Signature CreateSignature(const StringPiece& signature) const;
-
// Returns the short form method descriptor for the given prototype.
const char* GetShorty(dex::ProtoIndex proto_idx) const;
@@ -618,6 +614,10 @@
return hiddenapi_class_data_;
}
+ ALWAYS_INLINE bool HasHiddenapiClassData() const {
+ return hiddenapi_class_data_ != nullptr;
+ }
+
const dex::AnnotationItem* GetAnnotationItem(const dex::AnnotationSetItem* set_item,
uint32_t index) const {
DCHECK_LE(index, set_item->size_);
diff --git a/libdexfile/dex/dex_instruction.cc b/libdexfile/dex/dex_instruction.cc
index 83663c5..f36a2aa 100644
--- a/libdexfile/dex/dex_instruction.cc
+++ b/libdexfile/dex/dex_instruction.cc
@@ -402,9 +402,9 @@
case INVOKE_VIRTUAL_QUICK:
if (file != nullptr) {
os << opcode << " {";
- uint32_t method_idx = VRegB_35c();
+ uint32_t vtable_offset = VRegB_35c();
DumpArgs(VRegA_35c());
- os << "}, // vtable@" << method_idx;
+ os << "}, // vtable@" << vtable_offset;
break;
}
FALLTHROUGH_INTENDED;
diff --git a/libdexfile/dex/signature-inl.h b/libdexfile/dex/signature-inl.h
index ccc7ea9..12ad1b3 100644
--- a/libdexfile/dex/signature-inl.h
+++ b/libdexfile/dex/signature-inl.h
@@ -19,7 +19,6 @@
#include "signature.h"
-#include "base/stringpiece.h"
#include "dex_file-inl.h"
namespace art {
@@ -37,13 +36,13 @@
uint32_t lhs_shorty_len; // For a shorty utf16 length == mutf8 length.
const char* lhs_shorty_data = dex_file_->StringDataAndUtf16LengthByIdx(proto_id_->shorty_idx_,
&lhs_shorty_len);
- StringPiece lhs_shorty(lhs_shorty_data, lhs_shorty_len);
+ std::string_view lhs_shorty(lhs_shorty_data, lhs_shorty_len);
{
uint32_t rhs_shorty_len;
const char* rhs_shorty_data =
rhs.dex_file_->StringDataAndUtf16LengthByIdx(rhs.proto_id_->shorty_idx_,
&rhs_shorty_len);
- StringPiece rhs_shorty(rhs_shorty_data, rhs_shorty_len);
+ std::string_view rhs_shorty(rhs_shorty_data, rhs_shorty_len);
if (lhs_shorty != rhs_shorty) {
return false; // Shorty mismatch.
}
@@ -57,7 +56,7 @@
return false; // Return type mismatch.
}
}
- if (lhs_shorty.find('L', 1) != StringPiece::npos) {
+ if (lhs_shorty.find('L', 1) != std::string_view::npos) {
const dex::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
const dex::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_);
// We found a reference parameter in the matching shorty, so both lists must be non-empty.
diff --git a/libdexfile/dex/signature.cc b/libdexfile/dex/signature.cc
index 34b4b55..ac00428 100644
--- a/libdexfile/dex/signature.cc
+++ b/libdexfile/dex/signature.cc
@@ -21,6 +21,8 @@
#include <ostream>
#include <type_traits>
+#include "base/string_view_cpp20.h"
+
namespace art {
using dex::TypeList;
@@ -55,26 +57,26 @@
return strcmp(return_type, "V") == 0;
}
-bool Signature::operator==(const StringPiece& rhs) const {
+bool Signature::operator==(std::string_view rhs) const {
if (dex_file_ == nullptr) {
return false;
}
- StringPiece tail(rhs);
- if (!tail.starts_with("(")) {
+ std::string_view tail(rhs);
+ if (!StartsWith(tail, "(")) {
return false; // Invalid signature
}
tail.remove_prefix(1); // "(";
const TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
if (params != nullptr) {
for (uint32_t i = 0; i < params->Size(); ++i) {
- StringPiece param(dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_));
- if (!tail.starts_with(param)) {
+ std::string_view param(dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_));
+ if (!StartsWith(tail, param)) {
return false;
}
tail.remove_prefix(param.length());
}
}
- if (!tail.starts_with(")")) {
+ if (!StartsWith(tail, ")")) {
return false;
}
tail.remove_prefix(1); // ")";
diff --git a/libdexfile/dex/signature.h b/libdexfile/dex/signature.h
index 235f37c..3fbb543 100644
--- a/libdexfile/dex/signature.h
+++ b/libdexfile/dex/signature.h
@@ -19,6 +19,7 @@
#include <iosfwd>
#include <string>
+#include <string_view>
#include <android-base/logging.h>
@@ -30,7 +31,6 @@
struct ProtoId;
} // namespace dex
class DexFile;
-class StringPiece;
// Abstract the signature of a method.
class Signature : public ValueObject {
@@ -49,7 +49,7 @@
return !(*this == rhs);
}
- bool operator==(const StringPiece& rhs) const;
+ bool operator==(std::string_view rhs) const;
private:
Signature(const DexFile* dex, const dex::ProtoId& proto) : dex_file_(dex), proto_id_(&proto) {
diff --git a/libdexfile/dex/standard_dex_file.h b/libdexfile/dex/standard_dex_file.h
index 838d4e3..48671c9 100644
--- a/libdexfile/dex/standard_dex_file.h
+++ b/libdexfile/dex/standard_dex_file.h
@@ -84,7 +84,10 @@
uint32_t GetCodeItemSize(const dex::CodeItem& item) const override;
size_t GetDequickenedSize() const override {
- return Size();
+ // JVMTI will run dex layout on standard dex files that have hidden API data,
+ // in order to remove that data. As dexlayout may increase the size of the dex file,
+ // be (very) conservative and add one MB to the size.
+ return Size() + (HasHiddenapiClassData() ? 1 * MB : 0);
}
private:
diff --git a/libdexfile/external/dex_file_supp.cc b/libdexfile/external/dex_file_supp.cc
index 5bd25fc..ba684fe 100644
--- a/libdexfile/external/dex_file_supp.cc
+++ b/libdexfile/external/dex_file_supp.cc
@@ -16,10 +16,74 @@
#include "art_api/dex_file_support.h"
+#include <dlfcn.h>
+#include <mutex>
+
+#ifndef STATIC_LIB
+// Not used in the static lib, so avoid a dependency on this header in
+// libdexfile_support_static.
+#include <log/log.h>
+#endif
+
namespace art_api {
namespace dex {
-DexFile::~DexFile() { ExtDexFileFree(ext_dex_file_); }
+#ifdef STATIC_LIB
+#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = DLFUNC
+#else
+#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = nullptr
+#endif
+
+DEFINE_DLFUNC_PTR(DexString, ExtDexFileMakeString);
+DEFINE_DLFUNC_PTR(DexString, ExtDexFileGetString);
+DEFINE_DLFUNC_PTR(DexString, ExtDexFileFreeString);
+DEFINE_DLFUNC_PTR(DexFile, ExtDexFileOpenFromMemory);
+DEFINE_DLFUNC_PTR(DexFile, ExtDexFileOpenFromFd);
+DEFINE_DLFUNC_PTR(DexFile, ExtDexFileGetMethodInfoForOffset);
+DEFINE_DLFUNC_PTR(DexFile, ExtDexFileGetAllMethodInfos);
+DEFINE_DLFUNC_PTR(DexFile, ExtDexFileFree);
+
+#undef DEFINE_DLFUNC_PTR
+
+void LoadLibdexfileExternal() {
+#if defined(STATIC_LIB)
+ // Nothing to do here since all function pointers are initialised statically.
+#elif defined(NO_DEXFILE_SUPPORT)
+ LOG_FATAL("Dex file support not available.");
+#else
+ static std::once_flag dlopen_once;
+ std::call_once(dlopen_once, []() {
+ constexpr char kLibdexfileExternalLib[] = "libdexfile_external.so";
+ void* handle =
+ dlopen(kLibdexfileExternalLib, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
+ LOG_ALWAYS_FATAL_IF(handle == nullptr, "Failed to load %s: %s",
+ kLibdexfileExternalLib, dlerror());
+
+#define SET_DLFUNC_PTR(CLASS, DLFUNC) \
+ do { \
+ CLASS::g_##DLFUNC = reinterpret_cast<decltype(DLFUNC)*>(dlsym(handle, #DLFUNC)); \
+ LOG_ALWAYS_FATAL_IF(CLASS::g_##DLFUNC == nullptr, \
+ "Failed to find %s in %s: %s", \
+ #DLFUNC, \
+ kLibdexfileExternalLib, \
+ dlerror()); \
+ } while (0)
+
+ SET_DLFUNC_PTR(DexString, ExtDexFileMakeString);
+ SET_DLFUNC_PTR(DexString, ExtDexFileGetString);
+ SET_DLFUNC_PTR(DexString, ExtDexFileFreeString);
+ SET_DLFUNC_PTR(DexFile, ExtDexFileOpenFromMemory);
+ SET_DLFUNC_PTR(DexFile, ExtDexFileOpenFromFd);
+ SET_DLFUNC_PTR(DexFile, ExtDexFileGetMethodInfoForOffset);
+ SET_DLFUNC_PTR(DexFile, ExtDexFileGetAllMethodInfos);
+ SET_DLFUNC_PTR(DexFile, ExtDexFileFree);
+
+#undef SET_DLFUNC_PTR
+ });
+#endif // !defined(NO_DEXFILE_SUPPORT) && !defined(STATIC_LIB)
+}
+
+DexFile::~DexFile() { g_ExtDexFileFree(ext_dex_file_); }
MethodInfo DexFile::AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info) {
return {ext_method_info.offset, ext_method_info.len, DexString(ext_method_info.name)};
diff --git a/libdexfile/external/include/art_api/dex_file_support.h b/libdexfile/external/include/art_api/dex_file_support.h
index 24222af..a98ff0e 100644
--- a/libdexfile/external/include/art_api/dex_file_support.h
+++ b/libdexfile/external/include/art_api/dex_file_support.h
@@ -33,17 +33,22 @@
namespace art_api {
namespace dex {
+// Loads the libdexfile_external.so library and sets up function pointers.
+// Aborts with a fatal error on any error. For internal use by the classes
+// below.
+void LoadLibdexfileExternal();
+
// Minimal std::string look-alike for a string returned from libdexfile.
class DexString final {
public:
DexString(DexString&& dex_str) noexcept : ext_string_(dex_str.ext_string_) {
- dex_str.ext_string_ = ExtDexFileMakeString("", 0);
+ dex_str.ext_string_ = MakeExtDexFileString("", 0);
}
explicit DexString(const char* str = "")
- : ext_string_(ExtDexFileMakeString(str, std::strlen(str))) {}
+ : ext_string_(MakeExtDexFileString(str, std::strlen(str))) {}
explicit DexString(std::string_view str)
- : ext_string_(ExtDexFileMakeString(str.data(), str.size())) {}
- ~DexString() { ExtDexFileFreeString(ext_string_); }
+ : ext_string_(MakeExtDexFileString(str.data(), str.size())) {}
+ ~DexString() { g_ExtDexFileFreeString(ext_string_); }
DexString& operator=(DexString&& dex_str) noexcept {
std::swap(ext_string_, dex_str.ext_string_);
@@ -52,36 +57,48 @@
const char* data() const {
size_t ignored;
- return ExtDexFileGetString(ext_string_, &ignored);
+ return g_ExtDexFileGetString(ext_string_, &ignored);
}
const char* c_str() const { return data(); }
size_t size() const {
size_t len;
- (void)ExtDexFileGetString(ext_string_, &len);
+ (void)g_ExtDexFileGetString(ext_string_, &len);
return len;
}
size_t length() const { return size(); }
operator std::string_view() const {
size_t len;
- const char* chars = ExtDexFileGetString(ext_string_, &len);
+ const char* chars = g_ExtDexFileGetString(ext_string_, &len);
return std::string_view(chars, len);
}
private:
+ friend void LoadLibdexfileExternal();
friend class DexFile;
friend bool operator==(const DexString&, const DexString&);
explicit DexString(const ExtDexFileString* ext_string) : ext_string_(ext_string) {}
const ExtDexFileString* ext_string_; // Owned instance. Never nullptr.
+ static decltype(ExtDexFileMakeString)* g_ExtDexFileMakeString;
+ static decltype(ExtDexFileGetString)* g_ExtDexFileGetString;
+ static decltype(ExtDexFileFreeString)* g_ExtDexFileFreeString;
+
+ static const struct ExtDexFileString* MakeExtDexFileString(const char* str, size_t size) {
+ if (UNLIKELY(g_ExtDexFileMakeString == nullptr)) {
+ LoadLibdexfileExternal();
+ }
+ return g_ExtDexFileMakeString(str, size);
+ }
+
DISALLOW_COPY_AND_ASSIGN(DexString);
};
inline bool operator==(const DexString& s1, const DexString& s2) {
size_t l1, l2;
- const char* str1 = ExtDexFileGetString(s1.ext_string_, &l1);
- const char* str2 = ExtDexFileGetString(s2.ext_string_, &l2);
+ const char* str1 = DexString::g_ExtDexFileGetString(s1.ext_string_, &l1);
+ const char* str2 = DexString::g_ExtDexFileGetString(s2.ext_string_, &l2);
// Use memcmp to avoid assumption about absence of null characters in the strings.
return l1 == l2 && !std::memcmp(str1, str2, l1);
}
@@ -120,9 +137,14 @@
size_t* size,
const std::string& location,
/*out*/ std::string* error_msg) {
+ if (UNLIKELY(g_ExtDexFileOpenFromMemory == nullptr)) {
+ // Load libdexfile_external.so in this factory function, so instance
+ // methods don't need to check this.
+ LoadLibdexfileExternal();
+ }
ExtDexFile* ext_dex_file;
const ExtDexFileString* ext_error_msg = nullptr;
- if (ExtDexFileOpenFromMemory(addr, size, location.c_str(), &ext_error_msg, &ext_dex_file)) {
+ if (g_ExtDexFileOpenFromMemory(addr, size, location.c_str(), &ext_error_msg, &ext_dex_file)) {
return std::unique_ptr<DexFile>(new DexFile(ext_dex_file));
}
*error_msg = (ext_error_msg == nullptr) ? "" : std::string(DexString(ext_error_msg));
@@ -138,9 +160,14 @@
off_t offset,
const std::string& location,
/*out*/ std::string* error_msg) {
+ if (UNLIKELY(g_ExtDexFileOpenFromFd == nullptr)) {
+ // Load libdexfile_external.so in this factory function, so instance
+ // methods don't need to check this.
+ LoadLibdexfileExternal();
+ }
ExtDexFile* ext_dex_file;
const ExtDexFileString* ext_error_msg = nullptr;
- if (ExtDexFileOpenFromFd(fd, offset, location.c_str(), &ext_error_msg, &ext_dex_file)) {
+ if (g_ExtDexFileOpenFromFd(fd, offset, location.c_str(), &ext_error_msg, &ext_dex_file)) {
return std::unique_ptr<DexFile>(new DexFile(ext_dex_file));
}
*error_msg = std::string(DexString(ext_error_msg));
@@ -154,10 +181,10 @@
// class and method name only.
MethodInfo GetMethodInfoForOffset(int64_t dex_offset, bool with_signature) {
ExtDexFileMethodInfo ext_method_info;
- if (ExtDexFileGetMethodInfoForOffset(ext_dex_file_,
- dex_offset,
- with_signature,
- &ext_method_info)) {
+ if (g_ExtDexFileGetMethodInfoForOffset(ext_dex_file_,
+ dex_offset,
+ with_signature,
+ &ext_method_info)) {
return AbsorbMethodInfo(ext_method_info);
}
return {/*offset=*/0, /*len=*/0, /*name=*/DexString()};
@@ -168,14 +195,15 @@
// gets the class and method name only.
std::vector<MethodInfo> GetAllMethodInfos(bool with_signature) {
MethodInfoVector res;
- ExtDexFileGetAllMethodInfos(ext_dex_file_,
- with_signature,
- AddMethodInfoCallback,
- static_cast<void*>(&res));
+ g_ExtDexFileGetAllMethodInfos(ext_dex_file_,
+ with_signature,
+ AddMethodInfoCallback,
+ static_cast<void*>(&res));
return res;
}
private:
+ friend void LoadLibdexfileExternal();
explicit DexFile(ExtDexFile* ext_dex_file) : ext_dex_file_(ext_dex_file) {}
ExtDexFile* ext_dex_file_; // Owned instance. nullptr only in moved-from zombies.
@@ -184,6 +212,12 @@
static MethodInfo AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info);
static void AddMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info, void* user_data);
+ static decltype(ExtDexFileOpenFromMemory)* g_ExtDexFileOpenFromMemory;
+ static decltype(ExtDexFileOpenFromFd)* g_ExtDexFileOpenFromFd;
+ static decltype(ExtDexFileGetMethodInfoForOffset)* g_ExtDexFileGetMethodInfoForOffset;
+ static decltype(ExtDexFileGetAllMethodInfos)* g_ExtDexFileGetAllMethodInfos;
+ static decltype(ExtDexFileFree)* g_ExtDexFileFree;
+
DISALLOW_COPY_AND_ASSIGN(DexFile);
};
diff --git a/libprofile/Android.bp b/libprofile/Android.bp
index fd32c5f..986adce 100644
--- a/libprofile/Android.bp
+++ b/libprofile/Android.bp
@@ -25,10 +25,8 @@
android: {
shared_libs: [
"libartbase",
+ "libartpalette",
"libdexfile",
- "libartbase",
- // For atrace.
- "libcutils",
"libbase",
],
static_libs: [
@@ -41,10 +39,8 @@
not_windows: {
shared_libs: [
"libartbase",
+ "libartpalette",
"libdexfile",
- "libartbase",
- // For atrace.
- "libcutils",
"libziparchive",
"libz",
"libbase",
@@ -55,10 +51,8 @@
cflags: ["-Wno-thread-safety"],
static_libs: [
"libartbase",
+ "libartpalette",
"libdexfile",
- "libartbase",
- // For atrace.
- "libcutils",
"libziparchive",
"libz",
"libbase",
@@ -78,7 +72,6 @@
name: "libprofile_static_base_defaults",
static_libs: [
"libbase",
- "libcutils",
"libz",
"libziparchive",
],
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 45f853b..fd0dee3 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -19,11 +19,6 @@
defaults: ["art_defaults"],
host_supported: true,
srcs: ["oatdump.cc"],
- target: {
- android: {
- shared_libs: ["libcutils"],
- },
- },
// b/79417743, oatdump 32-bit tests failed with clang lld
use_clang_lld: false,
header_libs: [
@@ -102,6 +97,11 @@
"libart-disassembler",
"libvixl",
],
+ // We need this to resolve libartpalette symbols
+ // correctly. Multiple source libraries depend on it.
+ // TODO(b/122885634): This is also necessary for the static lib ordering bug
+ // with APEX stubs.
+ group_static_libs: true,
}
art_cc_binary {
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index e82cd97..b50aa1c 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -66,7 +66,12 @@
.PHONY: dump-oat-boot-$(TARGET_ARCH)
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
dump-oat-boot-$(TARGET_ARCH): $(DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP)
- $(OATDUMP) $(addprefix --image=,$(DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \
+ $(OATDUMP) \
+ --runtime-arg \
+ -Xbootclasspath:$(call normalize-path-list, $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)) \
+ --runtime-arg \
+ -Xbootclasspath-locations:$(call normalize-path-list, $(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS)) \
+ $(addprefix --image=,$(DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \
--output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt --instruction-set=$(TARGET_ARCH)
@echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt
endif
@@ -74,7 +79,12 @@
ifdef TARGET_2ND_ARCH
.PHONY: dump-oat-boot-$(TARGET_2ND_ARCH)
dump-oat-boot-$(TARGET_2ND_ARCH): $(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP)
- $(OATDUMP) $(addprefix --image=,$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \
+ $(OATDUMP) \
+ --runtime-arg \
+ -Xbootclasspath:$(call normalize-path-list, $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)) \
+ --runtime-arg \
+ -Xbootclasspath-locations:$(call normalize-path-list, $(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS)) \
+ $(addprefix --image=,$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \
--output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt --instruction-set=$(TARGET_2ND_ARCH)
@echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt
endif
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 89826c6..c3abe6c 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -221,10 +221,7 @@
debug::DebugInfo debug_info{};
debug_info.compiled_methods = ArrayRef<const debug::MethodDebugInfo>(method_debug_infos_);
- debug::WriteDebugInfo(builder_.get(),
- debug_info,
- dwarf::DW_DEBUG_FRAME_FORMAT,
- /* write_oat_patches= */ true);
+ debug::WriteDebugInfo(builder_.get(), debug_info);
builder_->End();
@@ -3358,20 +3355,24 @@
protected:
using Base = CmdlineArgs;
- ParseStatus ParseCustom(const StringPiece& option, std::string* error_msg) override {
+ ParseStatus ParseCustom(const char* raw_option,
+ size_t raw_option_length,
+ std::string* error_msg) override {
+ DCHECK_EQ(strlen(raw_option), raw_option_length);
{
- ParseStatus base_parse = Base::ParseCustom(option, error_msg);
+ ParseStatus base_parse = Base::ParseCustom(raw_option, raw_option_length, error_msg);
if (base_parse != kParseUnknownArgument) {
return base_parse;
}
}
- if (option.starts_with("--oat-file=")) {
- oat_filename_ = option.substr(strlen("--oat-file=")).data();
- } else if (option.starts_with("--dex-file=")) {
- dex_filename_ = option.substr(strlen("--dex-file=")).data();
- } else if (option.starts_with("--image=")) {
- image_location_ = option.substr(strlen("--image=")).data();
+ std::string_view option(raw_option, raw_option_length);
+ if (StartsWith(option, "--oat-file=")) {
+ oat_filename_ = raw_option + strlen("--oat-file=");
+ } else if (StartsWith(option, "--dex-file=")) {
+ dex_filename_ = raw_option + strlen("--dex-file=");
+ } else if (StartsWith(option, "--image=")) {
+ image_location_ = raw_option + strlen("--image=");
} else if (option == "--no-dump:vmap") {
dump_vmap_ = false;
} else if (option =="--dump:code_info_stack_maps") {
@@ -3380,32 +3381,32 @@
disassemble_code_ = false;
} else if (option =="--header-only") {
dump_header_only_ = true;
- } else if (option.starts_with("--symbolize=")) {
- oat_filename_ = option.substr(strlen("--symbolize=")).data();
+ } else if (StartsWith(option, "--symbolize=")) {
+ oat_filename_ = raw_option + strlen("--symbolize=");
symbolize_ = true;
- } else if (option.starts_with("--only-keep-debug")) {
+ } else if (StartsWith(option, "--only-keep-debug")) {
only_keep_debug_ = true;
- } else if (option.starts_with("--class-filter=")) {
- class_filter_ = option.substr(strlen("--class-filter=")).data();
- } else if (option.starts_with("--method-filter=")) {
- method_filter_ = option.substr(strlen("--method-filter=")).data();
- } else if (option.starts_with("--list-classes")) {
+ } else if (StartsWith(option, "--class-filter=")) {
+ class_filter_ = raw_option + strlen("--class-filter=");
+ } else if (StartsWith(option, "--method-filter=")) {
+ method_filter_ = raw_option + strlen("--method-filter=");
+ } else if (StartsWith(option, "--list-classes")) {
list_classes_ = true;
- } else if (option.starts_with("--list-methods")) {
+ } else if (StartsWith(option, "--list-methods")) {
list_methods_ = true;
- } else if (option.starts_with("--export-dex-to=")) {
- export_dex_location_ = option.substr(strlen("--export-dex-to=")).data();
- } else if (option.starts_with("--addr2instr=")) {
- if (!android::base::ParseUint(option.substr(strlen("--addr2instr=")).data(), &addr2instr_)) {
+ } else if (StartsWith(option, "--export-dex-to=")) {
+ export_dex_location_ = raw_option + strlen("--export-dex-to=");
+ } else if (StartsWith(option, "--addr2instr=")) {
+ if (!android::base::ParseUint(raw_option + strlen("--addr2instr="), &addr2instr_)) {
*error_msg = "Address conversion failed";
return kParseError;
}
- } else if (option.starts_with("--app-image=")) {
- app_image_ = option.substr(strlen("--app-image=")).data();
- } else if (option.starts_with("--app-oat=")) {
- app_oat_ = option.substr(strlen("--app-oat=")).data();
- } else if (option.starts_with("--dump-imt=")) {
- imt_dump_ = option.substr(strlen("--dump-imt=")).data();
+ } else if (StartsWith(option, "--app-image=")) {
+ app_image_ = raw_option + strlen("--app-image=");
+ } else if (StartsWith(option, "--app-oat=")) {
+ app_oat_ = raw_option + strlen("--app-oat=");
+ } else if (StartsWith(option, "--dump-imt=")) {
+ imt_dump_ = std::string(option.substr(strlen("--dump-imt=")));
} else if (option == "--dump-imt-stats") {
imt_stat_dump_ = true;
} else {
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index 7b1de01..8505b0c 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -92,7 +92,7 @@
// Test is failing on target, b/77469384.
TEST_DISABLED_FOR_TARGET();
std::string error_msg;
- ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {}));
+ ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M"}));
ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
const std::string dex_location =
tmp_dir_+ "/" + android::base::Basename(GetTestDexFileName(GetAppBaseName().c_str())) +
@@ -109,7 +109,7 @@
TEST_DISABLED_FOR_ARM_AND_MIPS();
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
std::string error_msg;
- ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {}));
+ ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M"}));
ASSERT_TRUE(Exec(kStatic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
}
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index a3e06e6..da7eef9 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -100,9 +100,11 @@
// property from `original` to `new_dex_file`.
const art::DexFileLoader dex_file_loader;
- if (original.IsCompactDexFile()) {
+ if (original.IsCompactDexFile() || original.HasHiddenapiClassData()) {
// Since we are supposed to return a standard dex, convert back using dexlayout. It's OK to do
// this before unquickening.
+ // We also do dex layout for dex files that have hidden API data, as we want to remove that
+ // data.
art::Options options;
options.compact_dex_level_ = art::CompactDexLevel::kCompactDexLevelNone;
// Add a filter to only include the class that has the matching descriptor.
diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h
index d9b8a84..5b28e45 100644
--- a/openjdkjvmti/jvmti_weak_table-inl.h
+++ b/openjdkjvmti/jvmti_weak_table-inl.h
@@ -77,7 +77,7 @@
}
template <typename T>
-bool JvmtiWeakTable<T>::GetTagSlowPath(art::Thread* self, art::mirror::Object* obj, T* result) {
+bool JvmtiWeakTable<T>::GetTagSlowPath(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, T* result) {
// Under concurrent GC, there is a window between moving objects and sweeping of system
// weaks in which mutators are active. We may receive a to-space object pointer in obj,
// but still have from-space pointers in the table. Explicitly update the table once.
@@ -87,7 +87,7 @@
}
template <typename T>
-bool JvmtiWeakTable<T>::Remove(art::mirror::Object* obj, /* out */ T* tag) {
+bool JvmtiWeakTable<T>::Remove(art::ObjPtr<art::mirror::Object> obj, /* out */ T* tag) {
art::Thread* self = art::Thread::Current();
art::MutexLock mu(self, allow_disallow_lock_);
Wait(self);
@@ -95,7 +95,7 @@
return RemoveLocked(self, obj, tag);
}
template <typename T>
-bool JvmtiWeakTable<T>::RemoveLocked(art::mirror::Object* obj, T* tag) {
+bool JvmtiWeakTable<T>::RemoveLocked(art::ObjPtr<art::mirror::Object> obj, T* tag) {
art::Thread* self = art::Thread::Current();
allow_disallow_lock_.AssertHeld(self);
Wait(self);
@@ -104,7 +104,7 @@
}
template <typename T>
-bool JvmtiWeakTable<T>::RemoveLocked(art::Thread* self, art::mirror::Object* obj, T* tag) {
+bool JvmtiWeakTable<T>::RemoveLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, T* tag) {
auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj));
if (it != tagged_objects_.end()) {
if (tag != nullptr) {
@@ -132,7 +132,7 @@
}
template <typename T>
-bool JvmtiWeakTable<T>::Set(art::mirror::Object* obj, T new_tag) {
+bool JvmtiWeakTable<T>::Set(art::ObjPtr<art::mirror::Object> obj, T new_tag) {
art::Thread* self = art::Thread::Current();
art::MutexLock mu(self, allow_disallow_lock_);
Wait(self);
@@ -140,7 +140,7 @@
return SetLocked(self, obj, new_tag);
}
template <typename T>
-bool JvmtiWeakTable<T>::SetLocked(art::mirror::Object* obj, T new_tag) {
+bool JvmtiWeakTable<T>::SetLocked(art::ObjPtr<art::mirror::Object> obj, T new_tag) {
art::Thread* self = art::Thread::Current();
allow_disallow_lock_.AssertHeld(self);
Wait(self);
@@ -149,7 +149,7 @@
}
template <typename T>
-bool JvmtiWeakTable<T>::SetLocked(art::Thread* self, art::mirror::Object* obj, T new_tag) {
+bool JvmtiWeakTable<T>::SetLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, T new_tag) {
auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj));
if (it != tagged_objects_.end()) {
it->second = new_tag;
@@ -362,7 +362,7 @@
}
if (select) {
- art::mirror::Object* obj = pair.first.template Read<art::kWithReadBarrier>();
+ art::ObjPtr<art::mirror::Object> obj = pair.first.template Read<art::kWithReadBarrier>();
if (obj != nullptr) {
count++;
if (object_result_ptr != nullptr) {
@@ -386,14 +386,14 @@
}
template <typename T>
-art::mirror::Object* JvmtiWeakTable<T>::Find(T tag) {
+art::ObjPtr<art::mirror::Object> JvmtiWeakTable<T>::Find(T tag) {
art::Thread* self = art::Thread::Current();
art::MutexLock mu(self, allow_disallow_lock_);
Wait(self);
for (auto& pair : tagged_objects_) {
if (tag == pair.second) {
- art::mirror::Object* obj = pair.first.template Read<art::kWithReadBarrier>();
+ art::ObjPtr<art::mirror::Object> obj = pair.first.template Read<art::kWithReadBarrier>();
if (obj != nullptr) {
return obj;
}
diff --git a/openjdkjvmti/jvmti_weak_table.h b/openjdkjvmti/jvmti_weak_table.h
index cba8ef0..ea0d023 100644
--- a/openjdkjvmti/jvmti_weak_table.h
+++ b/openjdkjvmti/jvmti_weak_table.h
@@ -60,25 +60,25 @@
// Remove the mapping for the given object, returning whether such a mapping existed (and the old
// value).
- ALWAYS_INLINE bool Remove(art::mirror::Object* obj, /* out */ T* tag)
+ ALWAYS_INLINE bool Remove(art::ObjPtr<art::mirror::Object> obj, /* out */ T* tag)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_);
- ALWAYS_INLINE bool RemoveLocked(art::mirror::Object* obj, /* out */ T* tag)
+ ALWAYS_INLINE bool RemoveLocked(art::ObjPtr<art::mirror::Object> obj, /* out */ T* tag)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_);
// Set the mapping for the given object. Returns true if this overwrites an already existing
// mapping.
- ALWAYS_INLINE virtual bool Set(art::mirror::Object* obj, T tag)
+ ALWAYS_INLINE virtual bool Set(art::ObjPtr<art::mirror::Object> obj, T tag)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_);
- ALWAYS_INLINE virtual bool SetLocked(art::mirror::Object* obj, T tag)
+ ALWAYS_INLINE virtual bool SetLocked(art::ObjPtr<art::mirror::Object> obj, T tag)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_);
// Return the value associated with the given object. Returns true if the mapping exists, false
// otherwise.
- bool GetTag(art::mirror::Object* obj, /* out */ T* result)
+ bool GetTag(art::ObjPtr<art::mirror::Object> obj, /* out */ T* result)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_) {
art::Thread* self = art::Thread::Current();
@@ -87,7 +87,7 @@
return GetTagLocked(self, obj, result);
}
- bool GetTagLocked(art::mirror::Object* obj, /* out */ T* result)
+ bool GetTagLocked(art::ObjPtr<art::mirror::Object> obj, /* out */ T* result)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_) {
art::Thread* self = art::Thread::Current();
@@ -118,7 +118,7 @@
ALWAYS_INLINE void Unlock() RELEASE(allow_disallow_lock_);
ALWAYS_INLINE void AssertLocked() ASSERT_CAPABILITY(allow_disallow_lock_);
- ALWAYS_INLINE art::mirror::Object* Find(T tag)
+ ALWAYS_INLINE art::ObjPtr<art::mirror::Object> Find(T tag)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_);
@@ -132,16 +132,16 @@
private:
ALWAYS_INLINE
- bool SetLocked(art::Thread* self, art::mirror::Object* obj, T tag)
+ bool SetLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, T tag)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_);
ALWAYS_INLINE
- bool RemoveLocked(art::Thread* self, art::mirror::Object* obj, /* out */ T* tag)
+ bool RemoveLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, /* out */ T* tag)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_);
- bool GetTagLocked(art::Thread* self, art::mirror::Object* obj, /* out */ T* result)
+ bool GetTagLocked(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, /* out */ T* result)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_) {
auto it = tagged_objects_.find(art::GcRoot<art::mirror::Object>(obj));
@@ -165,7 +165,7 @@
// Slow-path for GetTag. We didn't find the object, but we might be storing from-pointers and
// are asked to retrieve with a to-pointer.
ALWAYS_INLINE
- bool GetTagSlowPath(art::Thread* self, art::mirror::Object* obj, /* out */ T* result)
+ bool GetTagSlowPath(art::Thread* self, art::ObjPtr<art::mirror::Object> obj, /* out */ T* result)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_);
diff --git a/openjdkjvmti/object_tagging.cc b/openjdkjvmti/object_tagging.cc
index 0a51bf2..d52933a 100644
--- a/openjdkjvmti/object_tagging.cc
+++ b/openjdkjvmti/object_tagging.cc
@@ -71,7 +71,7 @@
jvmti_env_, art::Thread::Current(), tag);
}
-bool ObjectTagTable::Set(art::mirror::Object* obj, jlong new_tag) {
+bool ObjectTagTable::Set(art::ObjPtr<art::mirror::Object> obj, jlong new_tag) {
if (new_tag == 0) {
jlong tmp;
return Remove(obj, &tmp);
@@ -79,7 +79,7 @@
return JvmtiWeakTable<jlong>::Set(obj, new_tag);
}
-bool ObjectTagTable::SetLocked(art::mirror::Object* obj, jlong new_tag) {
+bool ObjectTagTable::SetLocked(art::ObjPtr<art::mirror::Object> obj, jlong new_tag) {
if (new_tag == 0) {
jlong tmp;
return RemoveLocked(obj, &tmp);
diff --git a/openjdkjvmti/object_tagging.h b/openjdkjvmti/object_tagging.h
index ca05a05..bd72ce3 100644
--- a/openjdkjvmti/object_tagging.h
+++ b/openjdkjvmti/object_tagging.h
@@ -61,21 +61,21 @@
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_);
- bool Set(art::mirror::Object* obj, jlong tag) override
+ bool Set(art::ObjPtr<art::mirror::Object> obj, jlong tag) override
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_);
- bool SetLocked(art::mirror::Object* obj, jlong tag) override
+ bool SetLocked(art::ObjPtr<art::mirror::Object> obj, jlong tag) override
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_);
- jlong GetTagOrZero(art::mirror::Object* obj)
+ jlong GetTagOrZero(art::ObjPtr<art::mirror::Object> obj)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_) {
jlong tmp = 0;
GetTag(obj, &tmp);
return tmp;
}
- jlong GetTagOrZeroLocked(art::mirror::Object* obj)
+ jlong GetTagOrZeroLocked(art::ObjPtr<art::mirror::Object> obj)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_) {
jlong tmp = 0;
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index a8e220c..3ad1112 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -34,6 +34,7 @@
#include "android-base/stringprintf.h"
#include <mutex>
+#include <string_view>
#include <unordered_set>
#include "art_jvmti.h"
@@ -872,7 +873,7 @@
/*out*/jint* count_ptr,
/*out*/char*** classes) {
jvmtiError res = OK;
- std::set<art::StringPiece> unique_descriptors;
+ std::set<std::string_view> unique_descriptors;
std::vector<const char*> descriptors;
auto add_descriptor = [&](const char* desc) {
// Don't add duplicates.
diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc
index 795a68a..20feb78 100644
--- a/openjdkjvmti/ti_class_definition.cc
+++ b/openjdkjvmti/ti_class_definition.cc
@@ -136,7 +136,8 @@
const char* descriptor,
/*out*/std::vector<unsigned char>* dex_data)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
- std::unique_ptr<FixedUpDexFile> fixed_dex_file(FixedUpDexFile::Create(*dex_file, descriptor));
+ std::unique_ptr<FixedUpDexFile> fixed_dex_file(
+ FixedUpDexFile::Create(*dex_file, descriptor));
dex_data->resize(fixed_dex_file->Size());
memcpy(dex_data->data(), fixed_dex_file->Begin(), fixed_dex_file->Size());
}
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 3d175a8..e51ae4d 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -32,6 +32,7 @@
#include "ti_redefine.h"
#include <limits>
+#include <string_view>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
@@ -40,7 +41,6 @@
#include "art_jvmti.h"
#include "art_method-inl.h"
#include "base/array_ref.h"
-#include "base/stringpiece.h"
#include "class_linker-inl.h"
#include "class_root.h"
#include "debugger.h"
@@ -240,7 +240,7 @@
art::ScopedObjectAccess soa(self);
art::StackHandleScope<1> hs(self);
art::ObjPtr<art::mirror::Object> obj(self->DecodeJObject(klass));
- if (obj.IsNull()) {
+ if (obj.IsNull() || !obj->IsClass()) {
return ERR(INVALID_CLASS);
}
art::Handle<art::mirror::Class> h_klass(hs.NewHandle(obj->AsClass()));
@@ -256,7 +256,7 @@
art::ScopedObjectAccess soa(self);
art::StackHandleScope<1> hs(self);
art::ObjPtr<art::mirror::Object> obj(self->DecodeJObject(klass));
- if (obj.IsNull()) {
+ if (obj.IsNull() || !obj->IsClass()) {
return ERR(INVALID_CLASS);
}
art::Handle<art::mirror::Class> h_klass(hs.NewHandle(obj->AsClass()));
@@ -597,7 +597,7 @@
// Try and get the declared method. First try to get a virtual method then a direct method if that's
// not found.
static art::ArtMethod* FindMethod(art::Handle<art::mirror::Class> klass,
- art::StringPiece name,
+ std::string_view name,
art::Signature sig) REQUIRES_SHARED(art::Locks::mutator_lock_) {
DCHECK(!klass->IsProxyClass());
for (art::ArtMethod& m : klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize)) {
diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
index 051db4c..1021648 100644
--- a/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -47,6 +47,7 @@
#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "mirror/string.h"
+#include "mirror/throwable.h"
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_utf_chars.h"
#include "obj_ptr.h"
@@ -83,6 +84,17 @@
}
void ThreadStart(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ // Needs to be checked first because we might start these threads before we actually send the
+ // VMInit event.
+ if (self->IsSystemDaemon()) {
+ // System daemon threads are things like the finalizer or gc thread. It would be dangerous to
+ // allow agents to get in the way of these threads starting up. These threads include things
+ // like the HeapTaskDaemon and the finalizer daemon.
+ //
+ // This event can happen during the time before VMInit or just after zygote fork. Since the
+ // second is hard to distinguish we unfortunately cannot really check the state here.
+ return;
+ }
if (!started) {
// Runtime isn't started. We only expect at most the signal handler or JIT threads to be
// started here.
@@ -132,16 +144,35 @@
gThreadCallback.Post<ArtJvmtiEvent::kThreadStart>(art::Thread::Current());
}
+
+static void WaitForSystemDaemonStart(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ {
+ art::ScopedThreadStateChange strc(self, art::kNative);
+ JNIEnv* jni = self->GetJniEnv();
+ jni->CallStaticVoidMethod(art::WellKnownClasses::java_lang_Daemons,
+ art::WellKnownClasses::java_lang_Daemons_waitForDaemonStart);
+ }
+ if (self->IsExceptionPending()) {
+ LOG(WARNING) << "Exception occured when waiting for system daemons to start: "
+ << self->GetException()->Dump();
+ self->ClearException();
+ }
+}
+
void ThreadUtil::CacheData() {
// We must have started since it is now safe to cache our data;
gThreadCallback.started = true;
- art::ScopedObjectAccess soa(art::Thread::Current());
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
art::ObjPtr<art::mirror::Class> thread_class =
soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread);
CHECK(thread_class != nullptr);
context_class_loader_ = thread_class->FindDeclaredInstanceField("contextClassLoader",
"Ljava/lang/ClassLoader;");
CHECK(context_class_loader_ != nullptr);
+ // Now wait for all required system threads to come up before allowing the rest of loading to
+ // continue.
+ WaitForSystemDaemonStart(self);
}
void ThreadUtil::Unregister() {
diff --git a/profman/profman.cc b/profman/profman.cc
index 82d9df0..b29e743 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -25,6 +25,7 @@
#include <iostream>
#include <set>
#include <string>
+#include <string_view>
#include <unordered_set>
#include <vector>
@@ -36,7 +37,7 @@
#include "base/mem_map.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
-#include "base/stringpiece.h"
+#include "base/string_view_cpp20.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
@@ -188,6 +189,33 @@
exit(1);
}
+template <typename T>
+static void ParseUintOption(const char* raw_option,
+ std::string_view option_prefix,
+ T* out) {
+ DCHECK(EndsWith(option_prefix, "="));
+ DCHECK(StartsWith(raw_option, option_prefix)) << raw_option << " " << option_prefix;
+ const char* value_string = raw_option + option_prefix.size();
+ int64_t parsed_integer_value = 0;
+ if (!android::base::ParseInt(value_string, &parsed_integer_value)) {
+ std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
+ Usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value_string);
+ }
+ if (parsed_integer_value < 0) {
+ std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
+ Usage("%s passed a negative value %" PRId64, option_name.c_str(), parsed_integer_value);
+ }
+ if (static_cast<uint64_t>(parsed_integer_value) >
+ static_cast<std::make_unsigned_t<T>>(std::numeric_limits<T>::max())) {
+ std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
+ Usage("%s passed a value %" PRIu64 " above max (%" PRIu64 ")",
+ option_name.c_str(),
+ static_cast<uint64_t>(parsed_integer_value),
+ static_cast<uint64_t>(std::numeric_limits<T>::max()));
+ }
+ *out = dchecked_integral_cast<T>(parsed_integer_value);
+}
+
// TODO(calin): This class has grown too much from its initial design. Split the functionality
// into smaller, more contained pieces.
class ProfMan final {
@@ -226,7 +254,8 @@
}
for (int i = 0; i < argc; ++i) {
- const StringPiece option(argv[i]);
+ const char* raw_option = argv[i];
+ const std::string_view option(raw_option);
const bool log_options = false;
if (log_options) {
LOG(INFO) << "profman: option[" << i << "]=" << argv[i];
@@ -235,66 +264,60 @@
dump_only_ = true;
} else if (option == "--dump-classes-and-methods") {
dump_classes_and_methods_ = true;
- } else if (option.starts_with("--create-profile-from=")) {
- create_profile_from_file_ = option.substr(strlen("--create-profile-from=")).ToString();
- } else if (option.starts_with("--dump-output-to-fd=")) {
- ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage);
+ } else if (StartsWith(option, "--create-profile-from=")) {
+ create_profile_from_file_ = std::string(option.substr(strlen("--create-profile-from=")));
+ } else if (StartsWith(option, "--dump-output-to-fd=")) {
+ ParseUintOption(raw_option, "--dump-output-to-fd=", &dump_output_to_fd_);
} else if (option == "--generate-boot-image-profile") {
generate_boot_image_profile_ = true;
- } else if (option.starts_with("--boot-image-class-threshold=")) {
- ParseUintOption(option,
- "--boot-image-class-threshold",
- &boot_image_options_.image_class_theshold,
- Usage);
- } else if (option.starts_with("--boot-image-clean-class-threshold=")) {
- ParseUintOption(option,
- "--boot-image-clean-class-threshold",
- &boot_image_options_.image_class_clean_theshold,
- Usage);
- } else if (option.starts_with("--boot-image-sampled-method-threshold=")) {
- ParseUintOption(option,
- "--boot-image-sampled-method-threshold",
- &boot_image_options_.compiled_method_threshold,
- Usage);
- } else if (option.starts_with("--profile-file=")) {
- profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
- } else if (option.starts_with("--profile-file-fd=")) {
- ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_);
- } else if (option.starts_with("--reference-profile-file=")) {
- reference_profile_file_ = option.substr(strlen("--reference-profile-file=")).ToString();
- } else if (option.starts_with("--reference-profile-file-fd=")) {
- ParseUintOption(option, "--reference-profile-file-fd", &reference_profile_file_fd_, Usage);
- } else if (option.starts_with("--dex-location=")) {
- dex_locations_.push_back(option.substr(strlen("--dex-location=")).ToString());
- } else if (option.starts_with("--apk-fd=")) {
- ParseFdForCollection(option, "--apk-fd", &apks_fd_);
- } else if (option.starts_with("--apk=")) {
- apk_files_.push_back(option.substr(strlen("--apk=")).ToString());
- } else if (option.starts_with("--generate-test-profile=")) {
- test_profile_ = option.substr(strlen("--generate-test-profile=")).ToString();
- } else if (option.starts_with("--generate-test-profile-num-dex=")) {
- ParseUintOption(option,
- "--generate-test-profile-num-dex",
- &test_profile_num_dex_,
- Usage);
- } else if (option.starts_with("--generate-test-profile-method-percentage")) {
- ParseUintOption(option,
- "--generate-test-profile-method-percentage",
- &test_profile_method_percerntage_,
- Usage);
- } else if (option.starts_with("--generate-test-profile-class-percentage")) {
- ParseUintOption(option,
- "--generate-test-profile-class-percentage",
- &test_profile_class_percentage_,
- Usage);
- } else if (option.starts_with("--generate-test-profile-seed=")) {
- ParseUintOption(option, "--generate-test-profile-seed", &test_profile_seed_, Usage);
- } else if (option.starts_with("--copy-and-update-profile-key")) {
+ } else if (StartsWith(option, "--boot-image-class-threshold=")) {
+ ParseUintOption(raw_option,
+ "--boot-image-class-threshold=",
+ &boot_image_options_.image_class_theshold);
+ } else if (StartsWith(option, "--boot-image-clean-class-threshold=")) {
+ ParseUintOption(raw_option,
+ "--boot-image-clean-class-threshold=",
+ &boot_image_options_.image_class_clean_theshold);
+ } else if (StartsWith(option, "--boot-image-sampled-method-threshold=")) {
+ ParseUintOption(raw_option,
+ "--boot-image-sampled-method-threshold=",
+ &boot_image_options_.compiled_method_threshold);
+ } else if (StartsWith(option, "--profile-file=")) {
+ profile_files_.push_back(std::string(option.substr(strlen("--profile-file="))));
+ } else if (StartsWith(option, "--profile-file-fd=")) {
+ ParseFdForCollection(raw_option, "--profile-file-fd=", &profile_files_fd_);
+ } else if (StartsWith(option, "--reference-profile-file=")) {
+ reference_profile_file_ = std::string(option.substr(strlen("--reference-profile-file=")));
+ } else if (StartsWith(option, "--reference-profile-file-fd=")) {
+ ParseUintOption(raw_option, "--reference-profile-file-fd=", &reference_profile_file_fd_);
+ } else if (StartsWith(option, "--dex-location=")) {
+ dex_locations_.push_back(std::string(option.substr(strlen("--dex-location="))));
+ } else if (StartsWith(option, "--apk-fd=")) {
+ ParseFdForCollection(raw_option, "--apk-fd=", &apks_fd_);
+ } else if (StartsWith(option, "--apk=")) {
+ apk_files_.push_back(std::string(option.substr(strlen("--apk="))));
+ } else if (StartsWith(option, "--generate-test-profile=")) {
+ test_profile_ = std::string(option.substr(strlen("--generate-test-profile=")));
+ } else if (StartsWith(option, "--generate-test-profile-num-dex=")) {
+ ParseUintOption(raw_option,
+ "--generate-test-profile-num-dex=",
+ &test_profile_num_dex_);
+ } else if (StartsWith(option, "--generate-test-profile-method-percentage=")) {
+ ParseUintOption(raw_option,
+ "--generate-test-profile-method-percentage=",
+ &test_profile_method_percerntage_);
+ } else if (StartsWith(option, "--generate-test-profile-class-percentage=")) {
+ ParseUintOption(raw_option,
+ "--generate-test-profile-class-percentage=",
+ &test_profile_class_percentage_);
+ } else if (StartsWith(option, "--generate-test-profile-seed=")) {
+ ParseUintOption(raw_option, "--generate-test-profile-seed=", &test_profile_seed_);
+ } else if (option == "--copy-and-update-profile-key") {
copy_and_update_profile_key_ = true;
- } else if (option.starts_with("--store-aggregation-counters")) {
+ } else if (option == "--store-aggregation-counters") {
store_aggregation_counters_ = true;
} else {
- Usage("Unknown argument '%s'", option.data());
+ Usage("Unknown argument '%s'", raw_option);
}
}
@@ -1265,11 +1288,11 @@
}
private:
- static void ParseFdForCollection(const StringPiece& option,
- const char* arg_name,
+ static void ParseFdForCollection(const char* raw_option,
+ std::string_view option_prefix,
std::vector<int>* fds) {
int fd;
- ParseUintOption(option, arg_name, &fd, Usage);
+ ParseUintOption(raw_option, option_prefix, &fd);
fds->push_back(fd);
}
diff --git a/runtime/Android.bp b/runtime/Android.bp
index a3081e9..a08ba70 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -392,14 +392,11 @@
"jni_platform_headers",
],
shared_libs: [
+ "libartpalette",
"libnativebridge",
"libnativeloader",
"libbacktrace",
"liblog",
- // For atrace, properties, ashmem.
- "libcutils",
- // For set_sched_policy.
- "libprocessgroup",
// For common macros.
"libbase",
],
@@ -424,9 +421,9 @@
},
},
static_libs: [
+ "libartpalette",
"libbacktrace",
"libbase",
- "libcutils",
"libdexfile_external", // libunwindstack dependency
"libdexfile_support", // libunwindstack dependency
"liblog",
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index fcf3c75..fdf4dbd 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -319,8 +319,9 @@
bool has_atomic_ldrd_strd = has_atomic_ldrd_strd_;
bool has_div = has_div_;
bool has_armv8a = has_armv8a_;
- for (auto i = features.begin(); i != features.end(); i++) {
- std::string feature = android::base::Trim(*i);
+ for (const std::string& feature : features) {
+ DCHECK_EQ(android::base::Trim(feature), feature)
+ << "Feature name is not trimmed: '" << feature << "'";
if (feature == "div") {
has_div = true;
} else if (feature == "-div") {
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 4a2b9d5..196f358 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -315,8 +315,9 @@
bool has_lse = has_lse_;
bool has_fp16 = has_fp16_;
bool has_dotprod = has_dotprod_;
- for (auto i = features.begin(); i != features.end(); i++) {
- std::string feature = android::base::Trim(*i);
+ for (const std::string& feature : features) {
+ DCHECK_EQ(android::base::Trim(feature), feature)
+ << "Feature name is not trimmed: '" << feature << "'";
if (feature == "a53") {
is_a53 = true;
} else if (feature == "-a53") {
@@ -367,4 +368,17 @@
has_dotprod));
}
+std::unique_ptr<const InstructionSetFeatures>
+Arm64InstructionSetFeatures::AddRuntimeDetectedFeatures(
+ const InstructionSetFeatures *features) const {
+ const Arm64InstructionSetFeatures *arm64_features = features->AsArm64InstructionSetFeatures();
+ return std::unique_ptr<const InstructionSetFeatures>(
+ new Arm64InstructionSetFeatures(fix_cortex_a53_835769_,
+ fix_cortex_a53_843419_,
+ arm64_features->has_crc_,
+ arm64_features->has_lse_,
+ arm64_features->has_fp16_,
+ arm64_features->has_dotprod_));
+}
+
} // namespace art
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h
index 4ec8fa2..432b9ef 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.h
+++ b/runtime/arch/arm64/instruction_set_features_arm64.h
@@ -98,6 +98,9 @@
AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const override;
+ std::unique_ptr<const InstructionSetFeatures>
+ AddRuntimeDetectedFeatures(const InstructionSetFeatures *features) const override;
+
private:
Arm64InstructionSetFeatures(bool needs_a53_835769_fix,
bool needs_a53_843419_fix,
diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
index 99d6b0d..eef8f08 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
@@ -170,4 +170,54 @@
EXPECT_EQ(armv8_2a_cpu_features->AsBitmap(), 14U);
}
+TEST(Arm64InstructionSetFeaturesTest, IsRuntimeDetectionSupported) {
+ if (kRuntimeISA == InstructionSet::kArm64) {
+ EXPECT_TRUE(InstructionSetFeatures::IsRuntimeDetectionSupported());
+ }
+}
+
+TEST(Arm64InstructionSetFeaturesTest, FeaturesFromRuntimeDetection) {
+ if (kRuntimeISA != InstructionSet::kArm64) {
+ return;
+ }
+
+ std::unique_ptr<const InstructionSetFeatures> hwcap_features(
+ InstructionSetFeatures::FromHwcap());
+ std::unique_ptr<const InstructionSetFeatures> runtime_detected_features(
+ InstructionSetFeatures::FromRuntimeDetection());
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ EXPECT_NE(runtime_detected_features, nullptr);
+ EXPECT_TRUE(InstructionSetFeatures::IsRuntimeDetectionSupported());
+ EXPECT_TRUE(runtime_detected_features->Equals(hwcap_features.get()));
+ EXPECT_TRUE(runtime_detected_features->HasAtLeast(cpp_defined_features.get()));
+}
+
+TEST(Arm64InstructionSetFeaturesTest, AddFeaturesFromStringRuntime) {
+ std::unique_ptr<const InstructionSetFeatures> features(
+ InstructionSetFeatures::FromBitmap(InstructionSet::kArm64, 0x0));
+ std::unique_ptr<const InstructionSetFeatures> hwcap_features(
+ InstructionSetFeatures::FromHwcap());
+
+ std::string error_msg;
+ features = features->AddFeaturesFromString("runtime", &error_msg);
+
+ EXPECT_NE(features, nullptr);
+ EXPECT_TRUE(error_msg.empty());
+
+ if (kRuntimeISA == InstructionSet::kArm64) {
+ EXPECT_TRUE(features->Equals(hwcap_features.get()));
+ EXPECT_EQ(features->GetFeatureString(), hwcap_features->GetFeatureString());
+ }
+
+ std::unique_ptr<const InstructionSetFeatures> a53_features(
+ Arm64InstructionSetFeatures::FromVariant("cortex-a53", &error_msg));
+ features = a53_features->AddFeaturesFromString("runtime", &error_msg);
+ EXPECT_NE(features, nullptr);
+ EXPECT_TRUE(error_msg.empty()) << error_msg;
+ const Arm64InstructionSetFeatures *arm64_features = features->AsArm64InstructionSetFeatures();
+ EXPECT_TRUE(arm64_features->NeedFixCortexA53_835769());
+ EXPECT_TRUE(arm64_features->NeedFixCortexA53_843419());
+}
+
} // namespace art
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index 886b40a..c5c2d31 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <algorithm>
+
#include "instruction_set_features.h"
#include <algorithm>
@@ -113,6 +115,16 @@
UNREACHABLE();
}
+std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromRuntimeDetection() {
+ switch (kRuntimeISA) {
+#ifdef ART_TARGET_ANDROID
+ case InstructionSet::kArm64:
+ return Arm64InstructionSetFeatures::FromHwcap();
+#endif
+ default:
+ return nullptr;
+ }
+}
std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromCpuInfo() {
switch (kRuntimeISA) {
@@ -184,44 +196,57 @@
}
std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::AddFeaturesFromString(
- const std::string& feature_list, std::string* error_msg) const {
- if (feature_list.empty()) {
- *error_msg = "No instruction set features specified";
- return std::unique_ptr<const InstructionSetFeatures>();
- }
+ const std::string& feature_list, /* out */ std::string* error_msg) const {
std::vector<std::string> features;
Split(feature_list, ',', &features);
- bool use_default = false; // Have we seen the 'default' feature?
- bool first = false; // Is this first feature?
- for (auto it = features.begin(); it != features.end();) {
- if (use_default) {
- *error_msg = "Unexpected instruction set features after 'default'";
- return std::unique_ptr<const InstructionSetFeatures>();
- }
- std::string feature = android::base::Trim(*it);
- bool erase = false;
- if (feature == "default") {
- if (!first) {
- use_default = true;
- erase = true;
- } else {
- *error_msg = "Unexpected instruction set features before 'default'";
- return std::unique_ptr<const InstructionSetFeatures>();
- }
- }
- if (!erase) {
- ++it;
- } else {
- it = features.erase(it);
- }
- first = true;
+ std::transform(std::begin(features), std::end(features), std::begin(features),
+ [](const std::string &s) { return android::base::Trim(s); });
+ auto empty_strings_begin = std::copy_if(std::begin(features), std::end(features),
+ std::begin(features),
+ [](const std::string& s) { return !s.empty(); });
+ features.erase(empty_strings_begin, std::end(features));
+ if (features.empty()) {
+ *error_msg = "No instruction set features specified";
+ return nullptr;
}
- // Expectation: "default" is standalone, no other flags. But an empty features vector after
- // processing can also come along if the handled flags are the only ones in the list. So
- // logically, we check "default -> features.empty."
- DCHECK(!use_default || features.empty());
- return AddFeaturesFromSplitString(features, error_msg);
+ bool use_default = false;
+ bool use_runtime_detection = false;
+ for (const std::string& feature : features) {
+ if (feature == "default") {
+ if (features.size() > 1) {
+ *error_msg = "Specific instruction set feature(s) cannot be used when 'default' is used.";
+ return nullptr;
+ }
+ use_default = true;
+ features.pop_back();
+ break;
+ } else if (feature == "runtime") {
+ if (features.size() > 1) {
+ *error_msg = "Specific instruction set feature(s) cannot be used when 'runtime' is used.";
+ return nullptr;
+ }
+ use_runtime_detection = true;
+ features.pop_back();
+ break;
+ }
+ }
+ // Expectation: "default" and "runtime" are standalone, no other feature names.
+ // But an empty features vector after processing can also come along if the
+ // handled feature names are the only ones in the list. So
+ // logically, we check "default or runtime => features.empty."
+ DCHECK((!use_default && !use_runtime_detection) || features.empty());
+
+ std::unique_ptr<const InstructionSetFeatures> runtime_detected_features;
+ if (use_runtime_detection) {
+ runtime_detected_features = FromRuntimeDetection();
+ }
+
+ if (runtime_detected_features != nullptr) {
+ return AddRuntimeDetectedFeatures(runtime_detected_features.get());
+ } else {
+ return AddFeaturesFromSplitString(features, error_msg);
+ }
}
const ArmInstructionSetFeatures* InstructionSetFeatures::AsArmInstructionSetFeatures() const {
@@ -262,6 +287,12 @@
return std::find(begin, end, variant) != end;
}
+std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::AddRuntimeDetectedFeatures(
+ const InstructionSetFeatures *features ATTRIBUTE_UNUSED) const {
+ UNIMPLEMENTED(FATAL) << kRuntimeISA;
+ UNREACHABLE();
+}
+
std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs) {
os << "ISA: " << rhs.GetInstructionSet() << " Feature string: " << rhs.GetFeatureString();
return os;
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index f910a41..9222a7b 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -48,6 +48,20 @@
// Turn C pre-processor #defines into the equivalent instruction set features for kRuntimeISA.
static std::unique_ptr<const InstructionSetFeatures> FromCppDefines();
+ // Check if run-time detection of instruction set features is supported.
+ //
+ // Return: true - if run-time detection is supported on a target device.
+ // false - otherwise
+ static bool IsRuntimeDetectionSupported() {
+ return FromRuntimeDetection() != nullptr;
+ }
+
+ // Use run-time detection to get instruction set features.
+ //
+ // Return: a set of detected features or nullptr if runtime detection is not
+ // supported on a target.
+ static std::unique_ptr<const InstructionSetFeatures> FromRuntimeDetection();
+
// Process /proc/cpuinfo and use kRuntimeISA to produce InstructionSetFeatures.
static std::unique_ptr<const InstructionSetFeatures> FromCpuInfo();
@@ -126,6 +140,10 @@
AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const = 0;
+ // Add run-time detected architecture specific features in sub-classes.
+ virtual std::unique_ptr<const InstructionSetFeatures>
+ AddRuntimeDetectedFeatures(const InstructionSetFeatures *features ATTRIBUTE_UNUSED) const;
+
private:
DISALLOW_COPY_AND_ASSIGN(InstructionSetFeatures);
};
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index 3a39a2a..d9b2e3f 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <array>
+
#include "instruction_set_features.h"
#include <gtest/gtest.h>
@@ -161,4 +163,145 @@
<< "\nFeatures from build: " << *instruction_set_features.get();
}
+TEST(InstructionSetFeaturesTest, FeaturesFromRuntimeDetection) {
+ if (!InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+ EXPECT_EQ(InstructionSetFeatures::FromRuntimeDetection(), nullptr);
+ }
+}
+
+// The instruction set feature string must not contain 'default' together with
+// other feature names.
+//
+// Test that InstructionSetFeatures::AddFeaturesFromString returns nullptr and
+// an error is reported when the value 'default' is specified together
+// with other feature names in an instruction set feature string.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromStringWithDefaultAndOtherNames) {
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ std::vector<std::string> invalid_feature_strings = {
+ "a,default",
+ "default,a",
+ "a,default,b",
+ "a,b,default",
+ "default,a,b,c",
+ "a,b,default,c,d",
+ "a, default ",
+ " default , a",
+ "a, default , b",
+ "default,runtime"
+ };
+
+ for (const std::string& invalid_feature_string : invalid_feature_strings) {
+ std::string error_msg;
+ EXPECT_EQ(cpp_defined_features->AddFeaturesFromString(invalid_feature_string, &error_msg),
+ nullptr) << " Invalid feature string: '" << invalid_feature_string << "'";
+ EXPECT_EQ(error_msg,
+ "Specific instruction set feature(s) cannot be used when 'default' is used.");
+ }
+}
+
+// The instruction set feature string must not contain 'runtime' together with
+// other feature names.
+//
+// Test that InstructionSetFeatures::AddFeaturesFromString returns nullptr and
+// an error is reported when the value 'runtime' is specified together
+// with other feature names in an instruction set feature string.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromStringWithRuntimeAndOtherNames) {
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ std::vector<std::string> invalid_feature_strings = {
+ "a,runtime",
+ "runtime,a",
+ "a,runtime,b",
+ "a,b,runtime",
+ "runtime,a,b,c",
+ "a,b,runtime,c,d",
+ "a, runtime ",
+ " runtime , a",
+ "a, runtime , b",
+ "runtime,default"
+ };
+
+ for (const std::string& invalid_feature_string : invalid_feature_strings) {
+ std::string error_msg;
+ EXPECT_EQ(cpp_defined_features->AddFeaturesFromString(invalid_feature_string, &error_msg),
+ nullptr) << " Invalid feature string: '" << invalid_feature_string << "'";
+ EXPECT_EQ(error_msg,
+ "Specific instruction set feature(s) cannot be used when 'runtime' is used.");
+ }
+}
+
+// Spaces and multiple commas are ignores in a instruction set feature string.
+//
+// Test that a use of spaces and multiple commas with 'default' and 'runtime'
+// does not cause errors.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromValidStringContainingDefaultOrRuntime) {
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ std::vector<std::string> valid_feature_strings = {
+ "default",
+ ",,,default",
+ "default,,,,",
+ ",,,default,,,,",
+ "default, , , ",
+ " , , ,default",
+ " , , ,default, , , ",
+ " default , , , ",
+ ",,,runtime",
+ "runtime,,,,",
+ ",,,runtime,,,,",
+ "runtime, , , ",
+ " , , ,runtime",
+ " , , ,runtime, , , ",
+ " runtime , , , "
+ };
+ for (const std::string& valid_feature_string : valid_feature_strings) {
+ std::string error_msg;
+ EXPECT_NE(cpp_defined_features->AddFeaturesFromString(valid_feature_string, &error_msg),
+ nullptr) << " Valid feature string: '" << valid_feature_string << "'";
+ EXPECT_TRUE(error_msg.empty()) << error_msg;
+ }
+}
+
+// Spaces and multiple commas are ignores in a instruction set feature string.
+//
+// Test that a use of spaces and multiple commas without any feature names
+// causes errors.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromInvalidStringWithoutFeatureNames) {
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ std::vector<std::string> invalid_feature_strings = {
+ " ",
+ " ",
+ ",",
+ ",,",
+ " , , ,,,,,,",
+ "\t",
+ " \t ",
+ ",",
+ ",,",
+ " , , ,,,,,,"
+ };
+ for (const std::string& invalid_feature_string : invalid_feature_strings) {
+ std::string error_msg;
+ EXPECT_EQ(cpp_defined_features->AddFeaturesFromString(invalid_feature_string, &error_msg),
+ nullptr) << " Invalid feature string: '" << invalid_feature_string << "'";
+ EXPECT_EQ(error_msg, "No instruction set features specified");
+ }
+}
+
+TEST(InstructionSetFeaturesTest, AddFeaturesFromStringRuntime) {
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ std::string error_msg;
+
+ const std::unique_ptr<const InstructionSetFeatures> features =
+ cpp_defined_features->AddFeaturesFromString("runtime", &error_msg);
+ EXPECT_NE(features, nullptr);
+ EXPECT_TRUE(error_msg.empty()) << error_msg;
+ if (!InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+ EXPECT_TRUE(features->Equals(cpp_defined_features.get()));
+ }
+}
+
} // namespace art
diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc
index 952ed25..99ce536 100644
--- a/runtime/arch/mips/instruction_set_features_mips.cc
+++ b/runtime/arch/mips/instruction_set_features_mips.cc
@@ -214,8 +214,9 @@
bool mips_isa_gte2 = mips_isa_gte2_;
bool r6 = r6_;
bool msa = msa_;
- for (auto i = features.begin(); i != features.end(); i++) {
- std::string feature = android::base::Trim(*i);
+ for (const std::string& feature : features) {
+ DCHECK_EQ(android::base::Trim(feature), feature)
+ << "Feature name is not trimmed: '" << feature << "'";
if (feature == "fpu32") {
fpu_32bit = true;
} else if (feature == "-fpu32") {
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc
index ea9f84b..2031433 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64.cc
+++ b/runtime/arch/mips64/instruction_set_features_mips64.cc
@@ -114,8 +114,9 @@
Mips64InstructionSetFeatures::AddFeaturesFromSplitString(
const std::vector<std::string>& features, std::string* error_msg) const {
bool msa = msa_;
- for (auto i = features.begin(); i != features.end(); i++) {
- std::string feature = android::base::Trim(*i);
+ for (const std::string& feature : features) {
+ DCHECK_EQ(android::base::Trim(feature), feature)
+ << "Feature name is not trimmed: '" << feature << "'";
if (feature == "msa") {
msa = true;
} else if (feature == "-msa") {
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index e9e983c..0c3d26e 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -311,8 +311,9 @@
bool has_AVX = has_AVX_;
bool has_AVX2 = has_AVX2_;
bool has_POPCNT = has_POPCNT_;
- for (auto i = features.begin(); i != features.end(); i++) {
- std::string feature = android::base::Trim(*i);
+ for (const std::string& feature : features) {
+ DCHECK_EQ(android::base::Trim(feature), feature)
+ << "Feature name is not trimmed: '" << feature << "'";
if (feature == "ssse3") {
has_SSSE3 = true;
} else if (feature == "-ssse3") {
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index b0bed56..306c4eb 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1804,6 +1804,7 @@
PUSH ESI
PUSH EDX
movl 16(%esp), %edi // Load referrer.
+ movd %xmm7, %esi // Get target method index stored in xmm7, remember it in ESI.
// If the method is obsolete, just go through the dex cache miss slow path.
// The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
testl LITERAL(ACC_OBSOLETE_METHOD), ART_METHOD_ACCESS_FLAGS_OFFSET(%edi)
@@ -1814,8 +1815,7 @@
movl MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET(%edi), %edi // Load the resolved methods.
pushl ART_METHOD_JNI_OFFSET_32(%eax) // Push ImtConflictTable.
CFI_ADJUST_CFA_OFFSET(4)
- movd %xmm7, %eax // Get target method index stored in xmm7.
- movl %eax, %esi // Remember method index in ESI.
+ movl %esi, %eax // Copy the method index from ESI.
andl LITERAL(METHOD_DEX_CACHE_SIZE_MINUS_ONE), %eax // Calculate DexCache method slot index.
leal 0(%edi, %eax, 2 * __SIZEOF_POINTER__), %edi // Load DexCache method slot address.
mov %ecx, %edx // Make EDX:EAX == ECX:EBX so that LOCK CMPXCHG8B makes no changes.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index a8a648f..39bf6e8 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1654,7 +1654,7 @@
* rdi is the conflict ArtMethod.
* rax is a hidden argument that holds the target interface method's dex method index.
*
- * Note that this stub writes to r10 and rdi.
+ * Note that this stub writes to r10, r11, rax and rdi.
*/
DEFINE_FUNCTION art_quick_imt_conflict_trampoline
#if defined(__APPLE__)
@@ -1662,6 +1662,8 @@
int3
#else
movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer.
+ mov %eax, %r11d // Remember method index in R11.
+ PUSH rdx // Preserve RDX as we need to clobber it by LOCK CMPXCHG16B.
// If the method is obsolete, just go through the dex cache miss slow path.
// The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
testl LITERAL(ACC_OBSOLETE_METHOD), ART_METHOD_ACCESS_FLAGS_OFFSET(%r10)
@@ -1670,11 +1672,9 @@
movl MIRROR_CLASS_DEX_CACHE_OFFSET(%r10), %r10d // Load the DexCache (without read barrier).
UNPOISON_HEAP_REF r10d
movq MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET(%r10), %r10 // Load the resolved methods.
- mov %eax, %r11d // Remember method index in R11.
andl LITERAL(METHOD_DEX_CACHE_SIZE_MINUS_ONE), %eax // Calculate DexCache method slot index.
shll LITERAL(1), %eax // Multiply by 2 as entries have size 2 * __SIZEOF_POINTER__.
leaq 0(%r10, %rax, __SIZEOF_POINTER__), %r10 // Load DexCache method slot address.
- PUSH rdx // Preserve RDX as we need to clobber it by LOCK CMPXCHG16B.
mov %rcx, %rdx // Make RDX:RAX == RCX:RBX so that LOCK CMPXCHG16B makes no changes.
mov %rbx, %rax // (The actual value does not matter.)
lock cmpxchg16b (%r10) // Relaxed atomic load RDX:RAX from the dex cache slot.
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 07193b2..c7e41be 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -22,7 +22,6 @@
#include "arch/context.h"
#include "art_method-inl.h"
-#include "base/stringpiece.h"
#include "class_linker-inl.h"
#include "class_root.h"
#include "debugger.h"
@@ -166,12 +165,11 @@
}
}
-size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) {
- CHECK_LE(1U, shorty.length());
+size_t ArtMethod::NumArgRegisters(const char* shorty) {
+ CHECK_NE(shorty[0], '\0');
uint32_t num_registers = 0;
- for (size_t i = 1; i < shorty.length(); ++i) {
- char ch = shorty[i];
- if (ch == 'D' || ch == 'J') {
+ for (const char* s = shorty + 1; *s != '\0'; ++s) {
+ if (*s == 'D' || *s == 'J') {
num_registers += 2;
} else {
num_registers += 1;
@@ -547,11 +545,10 @@
ArrayRef<const uint8_t> ArtMethod::GetQuickenedInfo() {
const DexFile& dex_file = *GetDexFile();
const OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
- if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) {
+ if (oat_dex_file == nullptr) {
return ArrayRef<const uint8_t>();
}
- return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf(dex_file,
- GetDexMethodIndex());
+ return oat_dex_file->GetQuickenedInfoOf(dex_file, GetDexMethodIndex());
}
uint16_t ArtMethod::GetIndexFromQuickening(uint32_t dex_pc) {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index aabd093..feff91a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -49,7 +49,6 @@
class OatQuickMethodHeader;
class ProfilingInfo;
class ScopedObjectAccessAlreadyRunnable;
-class StringPiece;
class ShadowFrame;
namespace mirror {
@@ -386,7 +385,7 @@
}
// Number of 32bit registers that would be required to hold all the arguments
- static size_t NumArgRegisters(const StringPiece& shorty);
+ static size_t NumArgRegisters(const char* shorty);
ALWAYS_INLINE uint32_t GetDexMethodIndex() {
return dex_method_index_;
diff --git a/runtime/base/locks.h b/runtime/base/locks.h
index 57719f1..b7d8e31 100644
--- a/runtime/base/locks.h
+++ b/runtime/base/locks.h
@@ -71,6 +71,7 @@
kRosAllocGlobalLock,
kRosAllocBracketLock,
kRosAllocBulkFreeLock,
+ kAllocSpaceLock,
kTaggingLockLevel,
kTransactionLogLock,
kCustomTlsLock,
@@ -84,7 +85,6 @@
kReferenceQueueClearedReferencesLock,
kReferenceProcessorLock,
kJitDebugInterfaceLock,
- kAllocSpaceLock,
kBumpPointerSpaceBlockLock,
kArenaPoolLock,
kInternTableLock,
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 5a52818..7aec661 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -107,15 +107,15 @@
blocked_tid_(kLogLockContentions ? blocked_tid : 0),
owner_tid_(kLogLockContentions ? owner_tid : 0),
start_nano_time_(kLogLockContentions ? NanoTime() : 0) {
- if (ATRACE_ENABLED()) {
+ if (ATraceEnabled()) {
std::string msg = StringPrintf("Lock contention on %s (owner tid: %" PRIu64 ")",
mutex->GetName(), owner_tid);
- ATRACE_BEGIN(msg.c_str());
+ ATraceBegin(msg.c_str());
}
}
~ScopedContentionRecorder() {
- ATRACE_END();
+ ATraceEnd();
if (kLogLockContentions) {
uint64_t end_nano_time = NanoTime();
mutex_->RecordContention(blocked_tid_, owner_tid_, end_nano_time - start_nano_time_);
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index 0a4cddd..bd39192 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -144,12 +144,12 @@
void TimingLogger::StartTiming(const char* label) {
DCHECK(label != nullptr);
timings_.push_back(Timing(kind_, label));
- ATRACE_BEGIN(label);
+ ATraceBegin(label);
}
void TimingLogger::EndTiming() {
timings_.push_back(Timing(kind_, nullptr));
- ATRACE_END();
+ ATraceEnd();
}
uint64_t TimingLogger::GetTotalNs() const {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d29a6b7..65fe4e4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -25,6 +25,7 @@
#include <memory>
#include <queue>
#include <string>
+#include <string_view>
#include <tuple>
#include <unordered_map>
#include <utility>
@@ -1448,6 +1449,7 @@
static void UpdateInternStrings(
gc::space::ImageSpace* space,
+ bool use_preresolved_strings,
const SafeMap<mirror::String*, mirror::String*>& intern_remap)
REQUIRES_SHARED(Locks::mutator_lock_);
};
@@ -1463,8 +1465,10 @@
ScopedTrace app_image_timing("AppImage:Updating");
Thread* const self = Thread::Current();
- gc::Heap* const heap = Runtime::Current()->GetHeap();
+ Runtime* const runtime = Runtime::Current();
+ gc::Heap* const heap = runtime->GetHeap();
const ImageHeader& header = space->GetImageHeader();
+ bool load_app_image_startup_cache = runtime->LoadAppImageStartupCache();
{
// Register dex caches with the class loader.
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -1478,6 +1482,10 @@
class_linker->RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get());
}
+ if (!load_app_image_startup_cache) {
+ dex_cache->ClearPreResolvedStrings();
+ }
+
if (kIsDebugBuild) {
CHECK(new_class_set != nullptr);
mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
@@ -1544,11 +1552,13 @@
void AppImageLoadingHelper::UpdateInternStrings(
gc::space::ImageSpace* space,
+ bool use_preresolved_strings,
const SafeMap<mirror::String*, mirror::String*>& intern_remap) {
const uint8_t* target_base = space->Begin();
const ImageSection& sro_section =
space->GetImageHeader().GetImageStringReferenceOffsetsSection();
const size_t num_string_offsets = sro_section.Size() / sizeof(AppImageReferenceOffsetInfo);
+ InternTable* const intern_table = Runtime::Current()->GetInternTable();
VLOG(image)
<< "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = "
@@ -1581,24 +1591,29 @@
WriteBarrier::ForEveryFieldWrite(dex_cache);
dex_cache->GetStrings()[string_index].store(
mirror::StringDexCachePair(it->second, source.index));
+ } else if (!use_preresolved_strings) {
+ dex_cache->GetStrings()[string_index].store(
+ mirror::StringDexCachePair(intern_table->InternStrong(referred_string), source.index));
}
} else if (HasDexCachePreResolvedStringNativeRefTag(base_offset)) {
- base_offset = ClearDexCacheNativeRefTags(base_offset);
- DCHECK_ALIGNED(base_offset, 2);
+ if (use_preresolved_strings) {
+ base_offset = ClearDexCacheNativeRefTags(base_offset);
+ DCHECK_ALIGNED(base_offset, 2);
- ObjPtr<mirror::DexCache> dex_cache =
- reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset);
- uint32_t string_index = sro_base[offset_index].second;
+ ObjPtr<mirror::DexCache> dex_cache =
+ reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset);
+ uint32_t string_index = sro_base[offset_index].second;
- ObjPtr<mirror::String> referred_string =
- dex_cache->GetPreResolvedStrings()[string_index].Read();
- DCHECK(referred_string != nullptr);
+ ObjPtr<mirror::String> referred_string =
+ dex_cache->GetPreResolvedStrings()[string_index].Read();
+ DCHECK(referred_string != nullptr);
- auto it = intern_remap.find(referred_string.Ptr());
- if (it != intern_remap.end()) {
- // Because we are not using a helper function we need to mark the GC card manually.
- WriteBarrier::ForEveryFieldWrite(dex_cache);
- dex_cache->GetPreResolvedStrings()[string_index] = GcRoot<mirror::String>(it->second);
+ auto it = intern_remap.find(referred_string.Ptr());
+ if (it != intern_remap.end()) {
+ // Because we are not using a helper function we need to mark the GC card manually.
+ WriteBarrier::ForEveryFieldWrite(dex_cache);
+ dex_cache->GetPreResolvedStrings()[string_index] = GcRoot<mirror::String>(it->second);
+ }
}
} else {
uint32_t raw_member_offset = sro_base[offset_index].second;
@@ -1621,6 +1636,13 @@
/* kCheckTransaction= */ false,
kVerifyNone,
/* kIsVolatile= */ false>(member_offset, it->second);
+ } else if (!use_preresolved_strings) {
+ obj_ptr->SetFieldObject</* kTransactionActive= */ false,
+ /* kCheckTransaction= */ false,
+ kVerifyNone,
+ /* kIsVolatile= */ false>(
+ member_offset,
+ intern_table->InternStrong(referred_string));
}
}
}
@@ -1631,13 +1653,16 @@
// the strings they point to.
ScopedTrace timing("AppImage:InternString");
- InternTable* const intern_table = Runtime::Current()->GetInternTable();
+ Runtime* const runtime = Runtime::Current();
+ InternTable* const intern_table = runtime->GetInternTable();
+
+ const bool load_startup_cache = runtime->LoadAppImageStartupCache();
// Add the intern table, removing any conflicts. For conflicts, store the new address in a map
// for faster lookup.
// TODO: Optimize with a bitmap or bloom filter
SafeMap<mirror::String*, mirror::String*> intern_remap;
- intern_table->AddImageStringsToTable(space, [&](InternTable::UnorderedSet& interns)
+ auto func = [&](InternTable::UnorderedSet& interns)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::intern_table_lock_) {
const size_t non_boot_image_strings = intern_table->CountInterns(
@@ -1680,14 +1705,24 @@
CHECK(intern_table->LookupStrongLocked(string) == nullptr) << string->ToModifiedUtf8();
}
}
- });
+ };
- VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size();
+ bool update_intern_strings;
+ if (load_startup_cache) {
+ VLOG(image) << "AppImage:load_startup_cache";
+ // Only add the intern table if we are using the startup cache. Otherwise,
+ // UpdateInternStrings adds the strings to the intern table.
+ intern_table->AddImageStringsToTable(space, func);
+ update_intern_strings = kIsDebugBuild || !intern_remap.empty();
+ VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size();
+ } else {
+ update_intern_strings = true;
+ }
// For debug builds, always run the code below to get coverage.
- if (kIsDebugBuild || !intern_remap.empty()) {
+ if (update_intern_strings) {
// Slow path case is when there are conflicting intern strings to fix up.
- UpdateInternStrings(space, intern_remap);
+ UpdateInternStrings(space, /*use_preresolved_strings=*/ load_startup_cache, intern_remap);
}
}
@@ -3727,32 +3762,7 @@
}
}
if (initialize_oat_file_data) {
- // Initialize the .data.bimg.rel.ro section.
- if (!oat_file->GetBootImageRelocations().empty()) {
- uint8_t* reloc_begin = const_cast<uint8_t*>(oat_file->DataBimgRelRoBegin());
- CheckedCall(mprotect,
- "un-protect boot image relocations",
- reloc_begin,
- oat_file->DataBimgRelRoSize(),
- PROT_READ | PROT_WRITE);
- uint32_t boot_image_begin = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(
- Runtime::Current()->GetHeap()->GetBootImageSpaces().front()->Begin()));
- for (const uint32_t& relocation : oat_file->GetBootImageRelocations()) {
- const_cast<uint32_t&>(relocation) += boot_image_begin;
- }
- CheckedCall(mprotect,
- "protect boot image relocations",
- reloc_begin,
- oat_file->DataBimgRelRoSize(),
- PROT_READ);
- }
-
- // Initialize the .bss section.
- // TODO: Pre-initialize from boot/app image?
- ArtMethod* resolution_method = Runtime::Current()->GetResolutionMethod();
- for (ArtMethod*& entry : oat_file->GetBssMethods()) {
- entry = resolution_method;
- }
+ oat_file->InitializeRelocations();
}
jweak dex_cache_jweak = vm->AddWeakGlobalRef(self, dex_cache);
dex_cache->SetDexFile(&dex_file);
@@ -7040,11 +7050,13 @@
return FindSameNameAndSignature(cmp, rest...);
}
+namespace {
+
// Check that all vtable entries are present in this class's virtuals or are the same as a
// superclasses vtable entry.
-static void CheckClassOwnsVTableEntries(Thread* self,
- Handle<mirror::Class> klass,
- PointerSize pointer_size)
+void CheckClassOwnsVTableEntries(Thread* self,
+ Handle<mirror::Class> klass,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<2> hs(self);
Handle<mirror::PointerArray> check_vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
@@ -7074,47 +7086,185 @@
// Check to make sure the vtable does not have duplicates. Duplicates could cause problems when a
// method is overridden in a subclass.
-static void CheckVTableHasNoDuplicates(Thread* self,
- Handle<mirror::Class> klass,
- PointerSize pointer_size)
+template <PointerSize kPointerSize>
+void CheckVTableHasNoDuplicates(Thread* self, Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<1> hs(self);
Handle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
int32_t num_entries = vtable->GetLength();
- for (int32_t i = 0; i < num_entries; i++) {
- ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
- // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member maybe).
+
+ // Observations:
+ // * The older implementation was O(n^2) and got too expensive for apps with larger classes.
+ // * Many classes do not override Object functions (e.g., equals/hashCode/toString). Thus,
+ // for many classes outside of libcore a cross-dexfile check has to be run anyways.
+ // * In the cross-dexfile case, with the O(n^2), in the best case O(n) cross checks would have
+ // to be done. It is thus OK in a single-pass algorithm to read all data, anyways.
+ // * The single-pass algorithm will trade memory for speed, but that is OK.
+
+ CHECK_GT(num_entries, 0);
+
+ auto log_fn = [&vtable, &klass](int32_t i, int32_t j) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* m1 = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(i);
+ ArtMethod* m2 = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(j);
+ LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
+ << klass->PrettyClass() << " in method " << m1->PrettyMethod()
+ << " (0x" << std::hex << reinterpret_cast<uintptr_t>(m2) << ") and "
+ << m2->PrettyMethod() << " (0x" << std::hex
+ << reinterpret_cast<uintptr_t>(m2) << ")";
+ };
+ struct BaseHashType {
+ static size_t HashCombine(size_t seed, size_t val) {
+ return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+ }
+ };
+
+ // Check assuming all entries come from the same dex file.
+ {
+ // Find the first interesting method and its dex file.
+ int32_t start = 0;
+ for (; start < num_entries; ++start) {
+ ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(start);
+ // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member
+ // maybe).
+ if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+ vtable_entry->GetAccessFlags())) {
+ continue;
+ }
+ break;
+ }
+ if (start == num_entries) {
+ return;
+ }
+ const DexFile* dex_file =
+ vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(start)->
+ GetInterfaceMethodIfProxy(kPointerSize)->GetDexFile();
+
+ // Helper function to avoid logging if we have to run the cross-file checks.
+ auto check_fn = [&](bool log_warn) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Use a map to store seen entries, as the storage space is too large for a bitvector.
+ using PairType = std::pair<uint32_t, uint16_t>;
+ struct PairHash : BaseHashType {
+ size_t operator()(const PairType& key) const {
+ return BaseHashType::HashCombine(BaseHashType::HashCombine(0, key.first), key.second);
+ }
+ };
+ std::unordered_map<PairType, int32_t, PairHash> seen;
+ seen.reserve(2 * num_entries);
+ bool need_slow_path = false;
+ bool found_dup = false;
+ for (int i = start; i < num_entries; ++i) {
+ // Can use Unchecked here as the start loop already ensured that the arrays are correct
+ // wrt/ kPointerSize.
+ ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked<ArtMethod*, kPointerSize>(i);
+ if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+ vtable_entry->GetAccessFlags())) {
+ continue;
+ }
+ ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize);
+ if (dex_file != m->GetDexFile()) {
+ need_slow_path = true;
+ break;
+ }
+ const dex::MethodId* m_mid = &dex_file->GetMethodId(m->GetDexMethodIndex());
+ PairType pair = std::make_pair(m_mid->name_idx_.index_, m_mid->proto_idx_.index_);
+ auto it = seen.find(pair);
+ if (it != seen.end()) {
+ found_dup = true;
+ if (log_warn) {
+ log_fn(it->second, i);
+ }
+ } else {
+ seen.emplace(pair, i);
+ }
+ }
+ return std::make_pair(need_slow_path, found_dup);
+ };
+ std::pair<bool, bool> result = check_fn(/* log_warn= */ false);
+ if (!result.first) {
+ if (result.second) {
+ check_fn(/* log_warn= */ true);
+ }
+ return;
+ }
+ }
+
+ // Need to check across dex files.
+ struct Entry {
+ size_t cached_hash = 0;
+ const char* name = nullptr;
+ Signature signature = Signature::NoSignature();
+ uint32_t name_len = 0;
+
+ Entry(const DexFile* dex_file, const dex::MethodId& mid)
+ : name(dex_file->StringDataAndUtf16LengthByIdx(mid.name_idx_, &name_len)),
+ signature(dex_file->GetMethodSignature(mid)) {
+ }
+
+ bool operator==(const Entry& other) const {
+ if (name_len != other.name_len || strcmp(name, other.name) != 0) {
+ return false;
+ }
+ return signature == other.signature;
+ }
+ };
+ struct EntryHash {
+ size_t operator()(const Entry& key) const {
+ return key.cached_hash;
+ }
+ };
+ std::unordered_map<Entry, int32_t, EntryHash> map;
+ for (int32_t i = 0; i < num_entries; ++i) {
+ // Can use Unchecked here as the first loop already ensured that the arrays are correct
+ // wrt/ kPointerSize.
+ ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked<ArtMethod*, kPointerSize>(i);
+ // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member
+ // maybe).
if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
vtable_entry->GetAccessFlags())) {
continue;
}
- MethodNameAndSignatureComparator name_comparator(
- vtable_entry->GetInterfaceMethodIfProxy(pointer_size));
- for (int32_t j = i + 1; j < num_entries; j++) {
- ArtMethod* other_entry = vtable->GetElementPtrSize<ArtMethod*>(j, pointer_size);
- if (!klass->CanAccessMember(other_entry->GetDeclaringClass(),
- other_entry->GetAccessFlags())) {
- continue;
- }
- if (vtable_entry == other_entry ||
- name_comparator.HasSameNameAndSignature(
- other_entry->GetInterfaceMethodIfProxy(pointer_size))) {
- LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
- << klass->PrettyClass() << " in method " << vtable_entry->PrettyMethod()
- << " (0x" << std::hex << reinterpret_cast<uintptr_t>(vtable_entry) << ") and "
- << other_entry->PrettyMethod() << " (0x" << std::hex
- << reinterpret_cast<uintptr_t>(other_entry) << ")";
- }
+ ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize);
+ const DexFile* dex_file = m->GetDexFile();
+ const dex::MethodId& mid = dex_file->GetMethodId(m->GetDexMethodIndex());
+
+ Entry e(dex_file, mid);
+
+ size_t string_hash = std::hash<std::string_view>()(std::string_view(e.name, e.name_len));
+ size_t sig_hash = std::hash<std::string>()(e.signature.ToString());
+ e.cached_hash = BaseHashType::HashCombine(BaseHashType::HashCombine(0u, string_hash),
+ sig_hash);
+
+ auto it = map.find(e);
+ if (it != map.end()) {
+ log_fn(it->second, i);
+ } else {
+ map.emplace(e, i);
}
}
}
+void CheckVTableHasNoDuplicates(Thread* self,
+ Handle<mirror::Class> klass,
+ PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ switch (pointer_size) {
+ case PointerSize::k64:
+ CheckVTableHasNoDuplicates<PointerSize::k64>(self, klass);
+ break;
+ case PointerSize::k32:
+ CheckVTableHasNoDuplicates<PointerSize::k32>(self, klass);
+ break;
+ }
+}
+
static void SanityCheckVTable(Thread* self, Handle<mirror::Class> klass, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
CheckClassOwnsVTableEntries(self, klass, pointer_size);
CheckVTableHasNoDuplicates(self, klass, pointer_size);
}
+} // namespace
+
void ClassLinker::FillImtFromSuperClass(Handle<mirror::Class> klass,
ArtMethod* unimplemented_method,
ArtMethod* imt_conflict_method,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 2f37123..91cea79 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -18,6 +18,7 @@
#include <memory>
#include <string>
+#include <string_view>
#include "android-base/strings.h"
@@ -408,7 +409,7 @@
void AssertDexFileClass(ObjPtr<mirror::ClassLoader> class_loader, const std::string& descriptor)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ASSERT_TRUE(descriptor != nullptr);
+ ASSERT_FALSE(descriptor.empty());
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
Handle<mirror::Class> klass(
@@ -509,7 +510,7 @@
for (size_t i = 0; i < offsets.size(); i++) {
ArtField* field = is_static ? klass->GetStaticField(i) : klass->GetInstanceField(i);
- StringPiece field_name(field->GetName());
+ std::string_view field_name(field->GetName());
if (field_name != offsets[i].java_name) {
error = true;
}
@@ -518,7 +519,7 @@
for (size_t i = 0; i < offsets.size(); i++) {
CheckOffset& offset = offsets[i];
ArtField* field = is_static ? klass->GetStaticField(i) : klass->GetInstanceField(i);
- StringPiece field_name(field->GetName());
+ std::string_view field_name(field->GetName());
if (field_name != offsets[i].java_name) {
LOG(ERROR) << "JAVA FIELD ORDER MISMATCH NEXT LINE:";
}
@@ -1254,7 +1255,7 @@
EXPECT_TRUE(K->IsAssignableFrom(B.Get()));
EXPECT_TRUE(J->IsAssignableFrom(B.Get()));
- const Signature void_sig = I->GetDexCache()->GetDexFile()->CreateSignature("()V");
+ const std::string_view void_sig("()V");
ArtMethod* Ii = I->FindClassMethod("i", void_sig, kRuntimePointerSize);
ArtMethod* Jj1 = J->FindClassMethod("j1", void_sig, kRuntimePointerSize);
ArtMethod* Jj2 = J->FindClassMethod("j2", void_sig, kRuntimePointerSize);
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 62788b1..499bf28 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -390,8 +390,10 @@
// NoSuchFieldError
-void ThrowNoSuchFieldError(const StringPiece& scope, ObjPtr<mirror::Class> c,
- const StringPiece& type, const StringPiece& name) {
+void ThrowNoSuchFieldError(std::string_view scope,
+ ObjPtr<mirror::Class> c,
+ std::string_view type,
+ std::string_view name) {
std::ostringstream msg;
std::string temp;
msg << "No " << scope << "field " << name << " of type " << type
@@ -399,7 +401,7 @@
ThrowException("Ljava/lang/NoSuchFieldError;", c, msg.str().c_str());
}
-void ThrowNoSuchFieldException(ObjPtr<mirror::Class> c, const StringPiece& name) {
+void ThrowNoSuchFieldException(ObjPtr<mirror::Class> c, std::string_view name) {
std::ostringstream msg;
std::string temp;
msg << "No field " << name << " in class " << c->GetDescriptor(&temp);
@@ -408,7 +410,9 @@
// NoSuchMethodError
-void ThrowNoSuchMethodError(InvokeType type, ObjPtr<mirror::Class> c, const StringPiece& name,
+void ThrowNoSuchMethodError(InvokeType type,
+ ObjPtr<mirror::Class> c,
+ std::string_view name,
const Signature& signature) {
std::ostringstream msg;
std::string temp;
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index ca9c96a..c167d1b 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -17,6 +17,8 @@
#ifndef ART_RUNTIME_COMMON_THROWS_H_
#define ART_RUNTIME_COMMON_THROWS_H_
+#include <string_view>
+
#include "base/locks.h"
#include "obj_ptr.h"
@@ -31,7 +33,6 @@
class DexFile;
enum InvokeType : uint32_t;
class Signature;
-class StringPiece;
// AbstractMethodError
@@ -196,20 +197,20 @@
// NoSuchFieldError
-void ThrowNoSuchFieldError(const StringPiece& scope,
+void ThrowNoSuchFieldError(std::string_view scope,
ObjPtr<mirror::Class> c,
- const StringPiece& type,
- const StringPiece& name)
+ std::string_view type,
+ std::string_view name)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
-void ThrowNoSuchFieldException(ObjPtr<mirror::Class> c, const StringPiece& name)
+void ThrowNoSuchFieldException(ObjPtr<mirror::Class> c, std::string_view name)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
// NoSuchMethodError
void ThrowNoSuchMethodError(InvokeType type,
ObjPtr<mirror::Class> c,
- const StringPiece& name,
+ std::string_view name,
const Signature& signature)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index e75baf8..050be4a 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -26,6 +26,7 @@
#include "class_linker-inl.h"
#include "class_root.h"
#include "dex/dex_file-inl.h"
+#include "dex/dex_instruction-inl.h"
#include "jni/jni_internal.h"
#include "jvalue-inl.h"
#include "mirror/array-alloc-inl.h"
@@ -36,6 +37,7 @@
#include "mirror/object_array-inl.h"
#include "oat_file.h"
#include "obj_ptr-inl.h"
+#include "quicken_info.h"
#include "reflection.h"
#include "thread.h"
#include "well_known_classes.h"
@@ -146,32 +148,36 @@
return actual == expected;
}
-const AnnotationSetItem* FindAnnotationSetForField(ArtField* field)
+static const AnnotationSetItem* FindAnnotationSetForField(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t field_index)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile* dex_file = field->GetDexFile();
+ const AnnotationsDirectoryItem* annotations_dir = dex_file.GetAnnotationsDirectory(class_def);
+ if (annotations_dir == nullptr) {
+ return nullptr;
+ }
+ const FieldAnnotationsItem* field_annotations = dex_file.GetFieldAnnotations(annotations_dir);
+ if (field_annotations == nullptr) {
+ return nullptr;
+ }
+ uint32_t field_count = annotations_dir->fields_size_;
+ for (uint32_t i = 0; i < field_count; ++i) {
+ if (field_annotations[i].field_idx_ == field_index) {
+ return dex_file.GetFieldAnnotationSetItem(field_annotations[i]);
+ }
+ }
+ return nullptr;
+}
+
+static const AnnotationSetItem* FindAnnotationSetForField(ArtField* field)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> klass = field->GetDeclaringClass();
const dex::ClassDef* class_def = klass->GetClassDef();
if (class_def == nullptr) {
DCHECK(klass->IsProxyClass());
return nullptr;
}
- const AnnotationsDirectoryItem* annotations_dir = dex_file->GetAnnotationsDirectory(*class_def);
- if (annotations_dir == nullptr) {
- return nullptr;
- }
- const FieldAnnotationsItem* field_annotations =
- dex_file->GetFieldAnnotations(annotations_dir);
- if (field_annotations == nullptr) {
- return nullptr;
- }
- uint32_t field_index = field->GetDexFieldIndex();
- uint32_t field_count = annotations_dir->fields_size_;
- for (uint32_t i = 0; i < field_count; ++i) {
- if (field_annotations[i].field_idx_ == field_index) {
- return dex_file->GetFieldAnnotationSetItem(field_annotations[i]);
- }
- }
- return nullptr;
+ return FindAnnotationSetForField(*field->GetDexFile(), *class_def, field->GetDexFieldIndex());
}
const AnnotationItem* SearchAnnotationSet(const DexFile& dex_file,
@@ -276,9 +282,9 @@
return nullptr;
}
-const AnnotationSetItem* FindAnnotationSetForMethod(const DexFile& dex_file,
- const dex::ClassDef& class_def,
- uint32_t method_index) {
+static const AnnotationSetItem* FindAnnotationSetForMethod(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t method_index) {
const AnnotationsDirectoryItem* annotations_dir = dex_file.GetAnnotationsDirectory(class_def);
if (annotations_dir == nullptr) {
return nullptr;
@@ -329,7 +335,7 @@
return nullptr;
}
-const AnnotationSetItem* FindAnnotationSetForClass(const ClassData& klass)
+static const AnnotationSetItem* FindAnnotationSetForClass(const ClassData& klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile& dex_file = klass.GetDexFile();
const dex::ClassDef* class_def = klass.GetClassDef();
@@ -1310,6 +1316,191 @@
return access_flags;
}
+bool FieldIsReachabilitySensitive(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t field_index)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const AnnotationSetItem* annotation_set =
+ FindAnnotationSetForField(dex_file, class_def, field_index);
+ if (annotation_set == nullptr) {
+ return false;
+ }
+ const AnnotationItem* annotation_item = SearchAnnotationSet(dex_file, annotation_set,
+ "Ldalvik/annotation/optimization/ReachabilitySensitive;", DexFile::kDexVisibilityRuntime);
+ // TODO: We're missing the equivalent of DCheckNativeAnnotation (not a DCHECK). Does it matter?
+ return annotation_item != nullptr;
+}
+
+bool MethodIsReachabilitySensitive(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t method_index)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const AnnotationSetItem* annotation_set =
+ FindAnnotationSetForMethod(dex_file, class_def, method_index);
+ if (annotation_set == nullptr) {
+ return false;
+ }
+ const AnnotationItem* annotation_item = SearchAnnotationSet(dex_file, annotation_set,
+ "Ldalvik/annotation/optimization/ReachabilitySensitive;", DexFile::kDexVisibilityRuntime);
+ return annotation_item != nullptr;
+}
+
+static bool MethodIsReachabilitySensitive(const DexFile& dex_file,
+ uint32_t method_index)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(method_index < dex_file.NumMethodIds());
+ const dex::MethodId& method_id = dex_file.GetMethodId(method_index);
+ dex::TypeIndex class_index = method_id.class_idx_;
+ const dex::ClassDef * class_def = dex_file.FindClassDef(class_index);
+ return class_def != nullptr
+ && MethodIsReachabilitySensitive(dex_file, *class_def, method_index);
+}
+
+bool MethodContainsRSensitiveAccess(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t method_index)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // TODO: This is too slow to run very regularly. Currently this is only invoked in the
+ // presence of @DeadReferenceSafe, which will be rare. In the long run, we need to quickly
+ // check once whether a class has any @ReachabilitySensitive annotations. If not, we can
+ // immediately return false here for any method in that class.
+ uint32_t code_item_offset = dex_file.FindCodeItemOffset(class_def, method_index);
+ const dex::CodeItem* code_item = dex_file.GetCodeItem(code_item_offset);
+ CodeItemInstructionAccessor accessor(dex_file, code_item);
+ if (!accessor.HasCodeItem()) {
+ return false;
+ }
+ ArrayRef<const uint8_t> quicken_data;
+ const OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
+ if (oat_dex_file != nullptr) {
+ quicken_data = oat_dex_file->GetQuickenedInfoOf(dex_file, method_index);
+ }
+ const QuickenInfoTable quicken_info(quicken_data);
+ uint32_t quicken_index = 0;
+ for (DexInstructionIterator iter = accessor.begin(); iter != accessor.end(); ++iter) {
+ switch (iter->Opcode()) {
+ case Instruction::IGET:
+ case Instruction::IGET_QUICK:
+ case Instruction::IGET_WIDE:
+ case Instruction::IGET_WIDE_QUICK:
+ case Instruction::IGET_OBJECT:
+ case Instruction::IGET_OBJECT_QUICK:
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BOOLEAN_QUICK:
+ case Instruction::IGET_BYTE:
+ case Instruction::IGET_BYTE_QUICK:
+ case Instruction::IGET_CHAR:
+ case Instruction::IGET_CHAR_QUICK:
+ case Instruction::IGET_SHORT:
+ case Instruction::IGET_SHORT_QUICK:
+ case Instruction::IPUT:
+ case Instruction::IPUT_QUICK:
+ case Instruction::IPUT_WIDE:
+ case Instruction::IPUT_WIDE_QUICK:
+ case Instruction::IPUT_OBJECT:
+ case Instruction::IPUT_OBJECT_QUICK:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BOOLEAN_QUICK:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_BYTE_QUICK:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_CHAR_QUICK:
+ case Instruction::IPUT_SHORT:
+ case Instruction::IPUT_SHORT_QUICK:
+ {
+ uint32_t field_index;
+ if (iter->IsQuickened()) {
+ field_index = quicken_info.GetData(quicken_index);
+ } else {
+ field_index = iter->VRegC_22c();
+ }
+ DCHECK(field_index < dex_file.NumFieldIds());
+ // We only guarantee to pay attention to the annotation if it's in the same class,
+ // or a containing class, but it's OK to do so in other cases.
+ const dex::FieldId& field_id = dex_file.GetFieldId(field_index);
+ dex::TypeIndex class_index = field_id.class_idx_;
+ const dex::ClassDef * field_class_def = dex_file.FindClassDef(class_index);
+ // We do not handle the case in which the field is declared in a superclass, and
+ // don't claim to do so. The annotated field should normally be private.
+ if (field_class_def != nullptr
+ && FieldIsReachabilitySensitive(dex_file, *field_class_def, field_index)) {
+ return true;
+ }
+ }
+ break;
+ case Instruction::INVOKE_SUPER:
+ // Cannot call method in same class. TODO: Try an explicit superclass lookup for
+ // better "best effort"?
+ break;
+ case Instruction::INVOKE_INTERFACE:
+ // We handle an interface call just like a virtual call. We will find annotations
+ // on interface methods/fields visible to us, but not of the annotation is in a
+ // super-interface. Again, we could just ignore it.
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_DIRECT:
+ {
+ uint32_t called_method_index = iter->VRegB_35c();
+ if (MethodIsReachabilitySensitive(dex_file, called_method_index)) {
+ return true;
+ }
+ }
+ break;
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ {
+ uint32_t called_method_index = iter->VRegB_3rc();
+ if (MethodIsReachabilitySensitive(dex_file, called_method_index)) {
+ return true;
+ }
+ }
+ break;
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+ {
+ uint32_t called_method_index = quicken_info.GetData(quicken_index);
+ if (MethodIsReachabilitySensitive(dex_file, called_method_index)) {
+ return true;
+ }
+ }
+ break;
+ // We explicitly do not handle indirect ReachabilitySensitive accesses through VarHandles,
+ // etc. Thus we ignore INVOKE_CUSTOM / INVOKE_CUSTOM_RANGE / INVOKE_POLYMORPHIC /
+ // INVOKE_POLYMORPHIC_RANGE.
+ default:
+ // There is no way to add an annotation to array elements, and so far we've encountered no
+ // need for that, so we ignore AGET and APUT.
+ // It's impractical or impossible to garbage collect a class while one of its methods is
+ // on the call stack. We allow ReachabilitySensitive annotations on static methods and
+ // fields, but they can be safely ignored.
+ break;
+ }
+ if (QuickenInfoTable::NeedsIndexForInstruction(&iter.Inst())) {
+ ++quicken_index;
+ }
+ }
+ return false;
+}
+
+bool HasDeadReferenceSafeAnnotation(const DexFile& dex_file,
+ const dex::ClassDef& class_def)
+ // TODO: This should check outer classes as well.
+ // It's conservatively correct not to do so.
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const AnnotationsDirectoryItem* annotations_dir =
+ dex_file.GetAnnotationsDirectory(class_def);
+ if (annotations_dir == nullptr) {
+ return false;
+ }
+ const AnnotationSetItem* annotation_set = dex_file.GetClassAnnotationSet(annotations_dir);
+ if (annotation_set == nullptr) {
+ return false;
+ }
+ const AnnotationItem* annotation_item = SearchAnnotationSet(dex_file, annotation_set,
+ "Ldalvik/annotation/optimization/DeadReferenceSafe;", DexFile::kDexVisibilityRuntime);
+ return annotation_item != nullptr;
+}
+
ObjPtr<mirror::Object> GetAnnotationForClass(Handle<mirror::Class> klass,
Handle<mirror::Class> annotation_class) {
ClassData data(klass);
diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h
index 3625cee..018e87f 100644
--- a/runtime/dex/dex_file_annotations.h
+++ b/runtime/dex/dex_file_annotations.h
@@ -78,6 +78,7 @@
Handle<mirror::Class> annotation_class,
uint32_t visibility = DexFile::kDexVisibilityRuntime)
REQUIRES_SHARED(Locks::mutator_lock_);
+
// Check whether a method from the `dex_file` with the given `method_index`
// is annotated with @dalvik.annotation.optimization.FastNative or
// @dalvik.annotation.optimization.CriticalNative with build visibility.
@@ -85,6 +86,28 @@
uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file,
const dex::ClassDef& class_def,
uint32_t method_index);
+// Is the field from the `dex_file` with the given `field_index`
+// annotated with @dalvik.annotation.optimization.ReachabilitySensitive?
+bool FieldIsReachabilitySensitive(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t field_index);
+// Is the method from the `dex_file` with the given `method_index`
+// annotated with @dalvik.annotation.optimization.ReachabilitySensitive?
+bool MethodIsReachabilitySensitive(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t method_index);
+// Does the method from the `dex_file` with the given `method_index` contain an access to a field
+// annotated with @dalvik.annotation.optimization.ReachabilitySensitive, or a call to a method
+// with that annotation?
+// Class_def is the class defining the method. We consider only accessses to classes or methods
+// declared in the static type of the corresponding object. We may overlook accesses to annotated
+// fields or methods that are in neither class_def nor a containing (outer) class.
+bool MethodContainsRSensitiveAccess(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t method_index);
+// Is the given class annotated with @dalvik.annotation.optimization.DeadReferenceSafe?
+bool HasDeadReferenceSafeAnnotation(const DexFile& dex_file,
+ const dex::ClassDef& class_def);
// Class annotations.
ObjPtr<mirror::Object> GetAnnotationForClass(Handle<mirror::Class> klass,
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 5f62d75..8335799 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2637,7 +2637,7 @@
}
void Heap::TraceHeapSize(size_t heap_size) {
- ATRACE_INT("Heap size (KB)", heap_size / KB);
+ ATraceIntegerValue("Heap size (KB)", heap_size / KB);
}
size_t Heap::GetNativeBytes() {
@@ -2772,13 +2772,6 @@
} else {
LOG(FATAL) << "Invalid current allocator " << current_allocator_;
}
- if (IsGcConcurrent()) {
- // Disable concurrent GC check so that we don't have spammy JNI requests.
- // This gets recalculated in GrowForUtilization. It is important that it is disabled /
- // calculated in the same thread so that there aren't any races that can cause it to become
- // permanantly disabled. b/17942071
- concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
- }
CHECK(collector != nullptr)
<< "Could not find garbage collector with collector_type="
@@ -2878,6 +2871,15 @@
DCHECK_GE(now, last_update_time_gc_count_rate_histograms_);
uint64_t time_since_last_update = now - last_update_time_gc_count_rate_histograms_;
uint64_t num_of_windows = time_since_last_update / kGcCountRateHistogramWindowDuration;
+
+ // The computed number of windows can be incoherently high if NanoTime() is not monotonic.
+ // Setting a limit on its maximum value reduces the impact on CPU time in such cases.
+ if (num_of_windows > kGcCountRateHistogramMaxNumMissedWindows) {
+ LOG(WARNING) << "Reducing the number of considered missed Gc histogram windows from "
+ << num_of_windows << " to " << kGcCountRateHistogramMaxNumMissedWindows;
+ num_of_windows = kGcCountRateHistogramMaxNumMissedWindows;
+ }
+
if (time_since_last_update >= kGcCountRateHistogramWindowDuration) {
// Record the first window.
gc_count_rate_histogram_.AddValue(gc_count_last_window_ - 1); // Exclude the current run.
@@ -3653,14 +3655,15 @@
// If the throughput of the current sticky GC >= throughput of the non sticky collector, then
// do another sticky collection next.
- // We also check that the bytes allocated aren't over the footprint limit in order to prevent a
+ // We also check that the bytes allocated aren't over the target_footprint, or
+ // concurrent_start_bytes in case of concurrent GCs, in order to prevent a
// pathological case where dead objects which aren't reclaimed by sticky could get accumulated
// if the sticky GC throughput always remained >= the full/partial throughput.
size_t target_footprint = target_footprint_.load(std::memory_order_relaxed);
if (current_gc_iteration_.GetEstimatedThroughput() * sticky_gc_throughput_adjustment >=
non_sticky_collector->GetEstimatedMeanThroughput() &&
non_sticky_collector->NumberOfIterations() > 0 &&
- bytes_allocated <= target_footprint) {
+ bytes_allocated <= (IsGcConcurrent() ? concurrent_start_bytes_ : target_footprint)) {
next_gc_type_ = collector::kGcTypeSticky;
} else {
next_gc_type_ = non_sticky_gc_type;
@@ -3996,7 +3999,7 @@
// Return the ratio of the weighted native + java allocated bytes to its target value.
// A return value > 1.0 means we should collect. Significantly larger values mean we're falling
// behind.
-inline float Heap::NativeMemoryOverTarget(size_t current_native_bytes) {
+inline float Heap::NativeMemoryOverTarget(size_t current_native_bytes, bool is_gc_concurrent) {
// Collection check for native allocation. Does not enforce Java heap bounds.
// With adj_start_bytes defined below, effectively checks
// <java bytes allocd> + c1*<old native allocd> + c2*<new native allocd) >= adj_start_bytes,
@@ -4013,17 +4016,22 @@
+ old_native_bytes / kOldNativeDiscountFactor;
size_t add_bytes_allowed = static_cast<size_t>(
NativeAllocationGcWatermark() * HeapGrowthMultiplier());
- size_t adj_start_bytes = concurrent_start_bytes_ + add_bytes_allowed / kNewNativeDiscountFactor;
+ size_t java_gc_start_bytes = is_gc_concurrent
+ ? concurrent_start_bytes_
+ : target_footprint_.load(std::memory_order_relaxed);
+ size_t adj_start_bytes = UnsignedSum(java_gc_start_bytes,
+ add_bytes_allowed / kNewNativeDiscountFactor);
return static_cast<float>(GetBytesAllocated() + weighted_native_bytes)
/ static_cast<float>(adj_start_bytes);
}
}
-inline void Heap::CheckConcurrentGCForNative(Thread* self) {
+inline void Heap::CheckGCForNative(Thread* self) {
+ bool is_gc_concurrent = IsGcConcurrent();
size_t current_native_bytes = GetNativeBytes();
- float gc_urgency = NativeMemoryOverTarget(current_native_bytes);
+ float gc_urgency = NativeMemoryOverTarget(current_native_bytes, is_gc_concurrent);
if (UNLIKELY(gc_urgency >= 1.0)) {
- if (IsGcConcurrent()) {
+ if (is_gc_concurrent) {
RequestConcurrentGC(self, kGcCauseForNativeAlloc, /*force_full=*/true);
if (gc_urgency > kStopForNativeFactor
&& current_native_bytes > kHugeNativeAllocs) {
@@ -4042,7 +4050,7 @@
// About kNotifyNativeInterval allocations have occurred. Check whether we should garbage collect.
void Heap::NotifyNativeAllocations(JNIEnv* env) {
native_objects_notified_.fetch_add(kNotifyNativeInterval, std::memory_order_relaxed);
- CheckConcurrentGCForNative(ThreadForEnv(env));
+ CheckGCForNative(ThreadForEnv(env));
}
// Register a native allocation with an explicit size.
@@ -4054,7 +4062,7 @@
native_objects_notified_.fetch_add(1, std::memory_order_relaxed);
if (objects_notified % kNotifyNativeInterval == kNotifyNativeInterval - 1
|| bytes > kCheckImmediatelyThreshold) {
- CheckConcurrentGCForNative(ThreadForEnv(env));
+ CheckGCForNative(ThreadForEnv(env));
}
}
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 4c5d896..18dfbf5 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -917,9 +917,13 @@
return main_space_backup_ != nullptr;
}
+ // Size_t saturating arithmetic
static ALWAYS_INLINE size_t UnsignedDifference(size_t x, size_t y) {
return x > y ? x - y : 0;
}
+ static ALWAYS_INLINE size_t UnsignedSum(size_t x, size_t y) {
+ return x + y >= x ? x + y : std::numeric_limits<size_t>::max();
+ }
static ALWAYS_INLINE bool AllocatorHasAllocationStack(AllocatorType allocator_type) {
return
@@ -950,13 +954,13 @@
// Checks whether we should garbage collect:
ALWAYS_INLINE bool ShouldConcurrentGCForJava(size_t new_num_bytes_allocated);
- float NativeMemoryOverTarget(size_t current_native_bytes);
+ float NativeMemoryOverTarget(size_t current_native_bytes, bool is_gc_concurrent);
ALWAYS_INLINE void CheckConcurrentGCForJava(Thread* self,
size_t new_num_bytes_allocated,
ObjPtr<mirror::Object>* obj)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!*pending_task_lock_, !*gc_complete_lock_);
- void CheckConcurrentGCForNative(Thread* self)
+ void CheckGCForNative(Thread* self)
REQUIRES(!*pending_task_lock_, !*gc_complete_lock_);
accounting::ObjectStack* GetMarkStack() {
@@ -1495,6 +1499,8 @@
uint64_t blocking_gc_time_;
// The duration of the window for the GC count rate histograms.
static constexpr uint64_t kGcCountRateHistogramWindowDuration = MsToNs(10 * 1000); // 10s.
+ // Maximum number of missed histogram windows for which statistics will be collected.
+ static constexpr uint64_t kGcCountRateHistogramMaxNumMissedWindows = 100;
// The last time when the GC count rate histograms were updated.
// This is rounded by kGcCountRateHistogramWindowDuration (a multiple of 10s).
uint64_t last_update_time_gc_count_rate_histograms_;
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index af5e67a..1279997 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -29,15 +29,6 @@
#include "thread-inl.h"
#include "well_known_classes.h"
-#ifdef ART_TARGET_ANDROID
-#include <metricslogger/metrics_logger.h>
-using android::metricslogger::ComplexEventLogger;
-using android::metricslogger::ACTION_HIDDEN_API_ACCESSED;
-using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD;
-using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED;
-using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE;
-#endif
-
namespace art {
namespace hiddenapi {
@@ -182,28 +173,6 @@
return member_name_ == other.member_name_ && type_signature_ == other.type_signature_;
}
-#ifdef ART_TARGET_ANDROID
-// Convert an AccessMethod enum to a value for logging from the proto enum.
-// This method may look odd (the enum values are current the same), but it
-// prevents coupling the internal enum to the proto enum (which should never
-// be changed) so that we are free to change the internal one if necessary in
-// future.
-inline static int32_t GetEnumValueForLog(AccessMethod access_method) {
- switch (access_method) {
- case AccessMethod::kNone:
- return android::metricslogger::ACCESS_METHOD_NONE;
- case AccessMethod::kReflection:
- return android::metricslogger::ACCESS_METHOD_REFLECTION;
- case AccessMethod::kJNI:
- return android::metricslogger::ACCESS_METHOD_JNI;
- case AccessMethod::kLinking:
- return android::metricslogger::ACCESS_METHOD_LINKING;
- default:
- DCHECK(false);
- }
-}
-#endif
-
void MemberSignature::LogAccessToEventLog(AccessMethod access_method, bool access_denied) {
#ifdef ART_TARGET_ANDROID
if (access_method == AccessMethod::kLinking || access_method == AccessMethod::kNone) {
@@ -213,19 +182,32 @@
// None does not correspond to actual access, so should also be ignored.
return;
}
- ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED);
- log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method));
- if (access_denied) {
- log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1);
+ Runtime* runtime = Runtime::Current();
+ if (runtime->IsAotCompiler()) {
+ return;
}
+ JNIEnvExt* env = Thread::Current()->GetJniEnv();
const std::string& package_name = Runtime::Current()->GetProcessPackageName();
- if (!package_name.empty()) {
- log_maker.SetPackageName(package_name);
+ ScopedLocalRef<jstring> package_str(env, env->NewStringUTF(package_name.c_str()));
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ LOG(ERROR) << "Unable to allocate string for package name which called hidden api";
}
std::ostringstream signature_str;
Dump(signature_str);
- log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str());
- log_maker.Record();
+ ScopedLocalRef<jstring> signature_jstr(env,
+ env->NewStringUTF(signature_str.str().c_str()));
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ LOG(ERROR) << "Unable to allocate string for hidden api method signature";
+ }
+ env->CallStaticVoidMethod(WellKnownClasses::dalvik_system_VMRuntime,
+ WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed, package_str.get(),
+ signature_jstr.get(), static_cast<jint>(access_method), access_denied);
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ LOG(ERROR) << "Unable to report hidden api usage";
+ }
#else
UNUSED(access_method);
UNUSED(access_denied);
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 8bd59ea..0bdb5c8 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -45,11 +45,13 @@
return static_cast<EnforcementPolicy>(api_policy_int);
}
+// Hidden API access method
+// Thist must be kept in sync with VMRuntime.HiddenApiUsageLogger.ACCESS_METHOD_*
enum class AccessMethod {
- kNone, // internal test that does not correspond to an actual access by app
- kReflection,
- kJNI,
- kLinking,
+ kNone = 0, // internal test that does not correspond to an actual access by app
+ kReflection = 1,
+ kJNI = 2,
+ kLinking = 3,
};
// Represents the API domain of a caller/callee.
@@ -352,7 +354,7 @@
// This function might print warnings into the log if the member is hidden.
template<typename T>
inline bool ShouldDenyAccessToMember(T* member,
- std::function<AccessContext()> fn_get_access_context,
+ const std::function<AccessContext()>& fn_get_access_context,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 2e41a9d..aa11562 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -17,6 +17,7 @@
#include "interpreter.h"
#include <limits>
+#include <string_view>
#include "common_dex_operations.h"
#include "common_throws.h"
@@ -46,7 +47,7 @@
static void InterpreterJni(Thread* self,
ArtMethod* method,
- const StringPiece& shorty,
+ std::string_view shorty,
ObjPtr<mirror::Object> receiver,
uint32_t* args,
JValue* result)
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 72a8727..7a1b7eb 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -181,17 +181,21 @@
return param->AsString();
}
+static std::function<hiddenapi::AccessContext()> GetHiddenapiAccessContextFunction(
+ ShadowFrame* frame) {
+ return [=]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
+ };
+}
+
template<typename T>
static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame)
REQUIRES_SHARED(Locks::mutator_lock_) {
// All uses in this file are from reflection
constexpr hiddenapi::AccessMethod kAccessMethod = hiddenapi::AccessMethod::kReflection;
- return hiddenapi::ShouldDenyAccessToMember(
- member,
- [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
- },
- kAccessMethod);
+ return hiddenapi::ShouldDenyAccessToMember(member,
+ GetHiddenapiAccessContextFunction(frame),
+ kAccessMethod);
}
void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
@@ -390,22 +394,23 @@
Runtime* runtime = Runtime::Current();
bool transaction = runtime->IsActiveTransaction();
PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
+ auto fn_hiddenapi_access_context = GetHiddenapiAccessContextFunction(shadow_frame);
ObjPtr<mirror::Method> method;
if (transaction) {
if (pointer_size == PointerSize::k64) {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k64, true>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
} else {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k32, true>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
}
} else {
if (pointer_size == PointerSize::k64) {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k64, false>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
} else {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k32, false>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
}
}
if (method != nullptr && ShouldDenyAccessToMember(method->GetArtMethod(), shadow_frame)) {
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index 99f9387..a69429f 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -301,7 +301,7 @@
return; // Nothing to do.
}
- std::vector<const uint8_t*> added_elf_files;
+ std::vector<ArrayRef<const uint8_t>> added_elf_files;
std::vector<const void*> removed_symbols;
auto added_it = g_jit_debug_entries.begin();
auto removed_it = removed_entries.begin();
@@ -312,7 +312,8 @@
auto added_begin = added_it;
while (added_it != g_jit_debug_entries.end() &&
AlignDown(added_it->first, kGroupSize) == group_ptr) {
- added_elf_files.push_back((added_it++)->second->symfile_addr_);
+ JITCodeEntry* entry = (added_it++)->second;
+ added_elf_files.emplace_back(entry->symfile_addr_, entry->symfile_size_);
}
removed_symbols.clear();
while (removed_it != removed_entries.end() &&
diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h
index 17beb4b..51b7041 100644
--- a/runtime/jit/debugger_interface.h
+++ b/runtime/jit/debugger_interface.h
@@ -21,6 +21,7 @@
#include <vector>
#include "arch/instruction_set_features.h"
+#include "base/array_ref.h"
#include "base/locks.h"
namespace art {
@@ -33,7 +34,7 @@
typedef std::vector<uint8_t> PackElfFileForJITFunction(
InstructionSet isa,
const InstructionSetFeatures* features,
- std::vector<const uint8_t*>& added_elf_files,
+ std::vector<ArrayRef<const uint8_t>>& added_elf_files,
std::vector<const void*>& removed_symbols,
/*out*/ size_t* num_symbols);
diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index e54b807..df96d28 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -17,6 +17,7 @@
#include "java_vm_ext.h"
#include <dlfcn.h>
+#include <string_view>
#include "android-base/stringprintf.h"
@@ -25,6 +26,7 @@
#include "base/mutex-inl.h"
#include "base/sdk_version.h"
#include "base/stl_util.h"
+#include "base/string_view_cpp20.h"
#include "base/systrace.h"
#include "check_jni.h"
#include "dex/dex_file-inl.h"
@@ -566,8 +568,8 @@
return false;
}
// Perform checks based on class name.
- StringPiece class_name(method->GetDeclaringClassDescriptor());
- if (!trace_.empty() && class_name.find(trace_) != std::string::npos) {
+ std::string_view class_name(method->GetDeclaringClassDescriptor());
+ if (!trace_.empty() && class_name.find(trace_) != std::string_view::npos) {
return true;
}
if (!VLOG_IS_ON(third_party_jni)) {
@@ -575,7 +577,7 @@
}
// Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
// like part of Android.
- static const char* gBuiltInPrefixes[] = {
+ static const char* const gBuiltInPrefixes[] = {
"Landroid/",
"Lcom/android/",
"Lcom/google/android/",
@@ -586,7 +588,7 @@
"Lorg/apache/harmony/",
};
for (size_t i = 0; i < arraysize(gBuiltInPrefixes); ++i) {
- if (class_name.starts_with(gBuiltInPrefixes[i])) {
+ if (StartsWith(class_name, gBuiltInPrefixes[i])) {
return false;
}
}
diff --git a/runtime/jni/java_vm_ext_test.cc b/runtime/jni/java_vm_ext_test.cc
index dfe50cf..4a7b1ca 100644
--- a/runtime/jni/java_vm_ext_test.cc
+++ b/runtime/jni/java_vm_ext_test.cc
@@ -109,6 +109,7 @@
}
TEST_F(JavaVmExtTest, AttachCurrentThread_SmallStack) {
+ TEST_DISABLED_FOR_MEMORY_TOOL(); // b/123500163
pthread_t pthread;
pthread_attr_t attr;
const char* reason = __PRETTY_FUNCTION__;
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index af86cc0..7c0db30 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -442,8 +442,8 @@
template <bool kNative>
static ArtMethod* FindMethod(ObjPtr<mirror::Class> c,
- const StringPiece& name,
- const StringPiece& sig)
+ std::string_view name,
+ std::string_view sig)
REQUIRES_SHARED(Locks::mutator_lock_) {
auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (auto& method : c->GetMethods(pointer_size)) {
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index a6a5ba2..d9d88e1 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -224,15 +224,31 @@
}
}
-template<typename T, VerifyObjectFlags kVerifyFlags>
-inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
+template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSize(uint32_t idx) {
// C style casts here since we sometimes have T be a pointer, or sometimes an integer
// (for stack traces).
- if (ptr_size == PointerSize::k64) {
+ if (kPointerSize == PointerSize::k64) {
return (T)static_cast<uintptr_t>(AsLongArray<kVerifyFlags>()->GetWithoutChecks(idx));
}
return (T)static_cast<uintptr_t>(AsIntArray<kVerifyFlags>()->GetWithoutChecks(idx));
}
+template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSizeUnchecked(uint32_t idx) {
+ // C style casts here since we sometimes have T be a pointer, or sometimes an integer
+ // (for stack traces).
+ if (kPointerSize == PointerSize::k64) {
+ return (T)static_cast<uintptr_t>(AsLongArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+ }
+ return (T)static_cast<uintptr_t>(AsIntArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+}
+template<typename T, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
+ if (ptr_size == PointerSize::k64) {
+ return GetElementPtrSize<T, PointerSize::k64, kVerifyFlags>(idx);
+ }
+ return GetElementPtrSize<T, PointerSize::k32, kVerifyFlags>(idx);
+}
template<bool kTransactionActive, bool kUnchecked>
inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, PointerSize ptr_size) {
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 8816c61..2e894d5 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -32,6 +32,8 @@
class MANAGED Array : public Object {
public:
+ static constexpr size_t kFirstElementOffset = 12u;
+
// The size of a java.lang.Class representing an array.
static uint32_t ClassSize(PointerSize pointer_size);
@@ -79,6 +81,17 @@
<< "Array data offset isn't aligned with component size";
return MemberOffset(data_offset);
}
+ template <size_t kComponentSize>
+ static constexpr MemberOffset DataOffset() {
+ static_assert(IsPowerOfTwo(kComponentSize), "Invalid component size");
+ constexpr size_t data_offset = RoundUp(kFirstElementOffset, kComponentSize);
+ static_assert(RoundUp(data_offset, kComponentSize) == data_offset, "RoundUp fail");
+ return MemberOffset(data_offset);
+ }
+
+ static constexpr size_t FirstElementOffset() {
+ return OFFSETOF_MEMBER(Array, first_element_);
+ }
void* GetRawData(size_t component_size, int32_t index)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -86,12 +99,24 @@
+ (index * component_size);
return reinterpret_cast<void*>(data);
}
+ template <size_t kComponentSize>
+ void* GetRawData(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset<kComponentSize>().Int32Value() +
+ + (index * kComponentSize);
+ return reinterpret_cast<void*>(data);
+ }
const void* GetRawData(size_t component_size, int32_t index) const {
intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset(component_size).Int32Value() +
+ (index * component_size);
return reinterpret_cast<void*>(data);
}
+ template <size_t kComponentSize>
+ const void* GetRawData(int32_t index) const {
+ intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset<kComponentSize>().Int32Value() +
+ + (index * kComponentSize);
+ return reinterpret_cast<void*>(data);
+ }
// Returns true if the index is valid. If not, throws an ArrayIndexOutOfBoundsException and
// returns false.
@@ -132,11 +157,11 @@
const T* GetData() const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return reinterpret_cast<const T*>(GetRawData(sizeof(T), 0));
+ return reinterpret_cast<const T*>(GetRawData<sizeof(T)>(0));
}
T* GetData() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return reinterpret_cast<T*>(GetRawData(sizeof(T), 0));
+ return reinterpret_cast<T*>(GetRawData<sizeof(T)>(0));
}
T Get(int32_t i) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
@@ -196,6 +221,15 @@
template<typename T, VerifyObjectFlags kVerifyFlags = kVerifyNone>
T GetElementPtrSize(uint32_t idx, PointerSize ptr_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template<typename T, PointerSize kPtrSize, VerifyObjectFlags kVerifyFlags = kVerifyNone>
+ T GetElementPtrSize(uint32_t idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Same as GetElementPtrSize, but uses unchecked version of array conversion. It is thus not
+ // checked whether kPtrSize matches the underlying array. Only use after at least one invocation
+ // of GetElementPtrSize!
+ template<typename T, PointerSize kPtrSize, VerifyObjectFlags kVerifyFlags = kVerifyNone>
+ T GetElementPtrSizeUnchecked(uint32_t idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kVerifyNone>
void** ElementAddress(size_t index, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 53c9cc72..872095a 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -35,6 +35,7 @@
#include "gc/accounting/card_table-inl.h"
#include "gc/heap-inl.h"
#include "handle_scope-inl.h"
+#include "hidden_api.h"
#include "subtype_check.h"
#include "method.h"
#include "object-inl.h"
@@ -388,14 +389,14 @@
new_reference_offsets);
}
-bool Class::IsInSamePackage(const StringPiece& descriptor1, const StringPiece& descriptor2) {
+bool Class::IsInSamePackage(std::string_view descriptor1, std::string_view descriptor2) {
size_t i = 0;
size_t min_length = std::min(descriptor1.size(), descriptor2.size());
while (i < min_length && descriptor1[i] == descriptor2[i]) {
++i;
}
- if (descriptor1.find('/', i) != StringPiece::npos ||
- descriptor2.find('/', i) != StringPiece::npos) {
+ if (descriptor1.find('/', i) != std::string_view::npos ||
+ descriptor2.find('/', i) != std::string_view::npos) {
return false;
} else {
return true;
@@ -434,7 +435,7 @@
template <typename SignatureType>
static inline ArtMethod* FindInterfaceMethodWithSignature(ObjPtr<Class> klass,
- const StringPiece& name,
+ std::string_view name,
const SignatureType& signature,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -477,13 +478,13 @@
return nullptr;
}
-ArtMethod* Class::FindInterfaceMethod(const StringPiece& name,
- const StringPiece& signature,
+ArtMethod* Class::FindInterfaceMethod(std::string_view name,
+ std::string_view signature,
PointerSize pointer_size) {
return FindInterfaceMethodWithSignature(this, name, signature, pointer_size);
}
-ArtMethod* Class::FindInterfaceMethod(const StringPiece& name,
+ArtMethod* Class::FindInterfaceMethod(std::string_view name,
const Signature& signature,
PointerSize pointer_size) {
return FindInterfaceMethodWithSignature(this, name, signature, pointer_size);
@@ -495,7 +496,7 @@
// We always search by name and signature, ignoring the type index in the MethodId.
const DexFile& dex_file = *dex_cache->GetDexFile();
const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
- StringPiece name = dex_file.StringDataByIdx(method_id.name_idx_);
+ std::string_view name = dex_file.StringDataByIdx(method_id.name_idx_);
const Signature signature = dex_file.GetMethodSignature(method_id);
return FindInterfaceMethod(name, signature, pointer_size);
}
@@ -536,7 +537,7 @@
template <typename SignatureType>
static inline ArtMethod* FindClassMethodWithSignature(ObjPtr<Class> this_klass,
- const StringPiece& name,
+ std::string_view name,
const SignatureType& signature,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -590,13 +591,13 @@
}
-ArtMethod* Class::FindClassMethod(const StringPiece& name,
- const StringPiece& signature,
+ArtMethod* Class::FindClassMethod(std::string_view name,
+ std::string_view signature,
PointerSize pointer_size) {
return FindClassMethodWithSignature(this, name, signature, pointer_size);
}
-ArtMethod* Class::FindClassMethod(const StringPiece& name,
+ArtMethod* Class::FindClassMethod(std::string_view name,
const Signature& signature,
PointerSize pointer_size) {
return FindClassMethodWithSignature(this, name, signature, pointer_size);
@@ -623,7 +624,7 @@
const DexFile& dex_file = *dex_cache->GetDexFile();
const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
const Signature signature = dex_file.GetMethodSignature(method_id);
- StringPiece name; // Delay strlen() until actually needed.
+ std::string_view name; // Delay strlen() until actually needed.
// If we do not have a dex_cache match, try to find the declared method in this class now.
if (this_dex_cache != dex_cache && !GetDeclaredMethodsSlice(pointer_size).empty()) {
DCHECK(name.empty());
@@ -700,10 +701,10 @@
return uninherited_method; // Return the `uninherited_method` if any.
}
-ArtMethod* Class::FindConstructor(const StringPiece& signature, PointerSize pointer_size) {
+ArtMethod* Class::FindConstructor(std::string_view signature, PointerSize pointer_size) {
// Internal helper, never called on proxy classes. We can skip GetInterfaceMethodIfProxy().
DCHECK(!IsProxyClass());
- StringPiece name("<init>");
+ std::string_view name("<init>");
for (ArtMethod& method : GetDirectMethodsSliceUnchecked(pointer_size)) {
if (method.GetName() == name && method.GetSignature() == signature) {
return &method;
@@ -712,8 +713,7 @@
return nullptr;
}
-ArtMethod* Class::FindDeclaredDirectMethodByName(const StringPiece& name,
- PointerSize pointer_size) {
+ArtMethod* Class::FindDeclaredDirectMethodByName(std::string_view name, PointerSize pointer_size) {
for (auto& method : GetDirectMethods(pointer_size)) {
ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
if (name == np_method->GetName()) {
@@ -723,8 +723,7 @@
return nullptr;
}
-ArtMethod* Class::FindDeclaredVirtualMethodByName(const StringPiece& name,
- PointerSize pointer_size) {
+ArtMethod* Class::FindDeclaredVirtualMethodByName(std::string_view name, PointerSize pointer_size) {
for (auto& method : GetVirtualMethods(pointer_size)) {
ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
if (name == np_method->GetName()) {
@@ -812,8 +811,8 @@
// Custom binary search to avoid double comparisons from std::binary_search.
static ArtField* FindFieldByNameAndType(LengthPrefixedArray<ArtField>* fields,
- const StringPiece& name,
- const StringPiece& type)
+ std::string_view name,
+ std::string_view type)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (fields == nullptr) {
return nullptr;
@@ -826,9 +825,12 @@
ArtField& field = fields->At(mid);
// Fields are sorted by class, then name, then type descriptor. This is verified in dex file
// verifier. There can be multiple fields with the same in the same class name due to proguard.
- int result = StringPiece(field.GetName()).Compare(name);
+ // Note: std::string_view::compare() uses lexicographical comparison and treats the `char` as
+ // unsigned; for modified-UTF-8 without embedded nulls this is consistent with the
+ // CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues() ordering.
+ int result = std::string_view(field.GetName()).compare(name);
if (result == 0) {
- result = StringPiece(field.GetTypeDescriptor()).Compare(type);
+ result = std::string_view(field.GetTypeDescriptor()).compare(type);
}
if (result < 0) {
low = mid + 1;
@@ -852,7 +854,7 @@
return ret;
}
-ArtField* Class::FindDeclaredInstanceField(const StringPiece& name, const StringPiece& type) {
+ArtField* Class::FindDeclaredInstanceField(std::string_view name, std::string_view type) {
// Binary search by name. Interfaces are not relevant because they can't contain instance fields.
return FindFieldByNameAndType(GetIFieldsPtr(), name, type);
}
@@ -868,7 +870,7 @@
return nullptr;
}
-ArtField* Class::FindInstanceField(const StringPiece& name, const StringPiece& type) {
+ArtField* Class::FindInstanceField(std::string_view name, std::string_view type) {
// Is the field in this class, or any of its superclasses?
// Interfaces are not relevant because they can't contain instance fields.
for (ObjPtr<Class> c = this; c != nullptr; c = c->GetSuperClass()) {
@@ -892,8 +894,8 @@
return nullptr;
}
-ArtField* Class::FindDeclaredStaticField(const StringPiece& name, const StringPiece& type) {
- DCHECK(type != nullptr);
+ArtField* Class::FindDeclaredStaticField(std::string_view name, std::string_view type) {
+ DCHECK(!type.empty());
return FindFieldByNameAndType(GetSFieldsPtr(), name, type);
}
@@ -910,8 +912,8 @@
ArtField* Class::FindStaticField(Thread* self,
ObjPtr<Class> klass,
- const StringPiece& name,
- const StringPiece& type) {
+ std::string_view name,
+ std::string_view type) {
// Is the field in this class (or its interfaces), or any of its
// superclasses (or their interfaces)?
for (ObjPtr<Class> k = klass; k != nullptr; k = k->GetSuperClass()) {
@@ -961,8 +963,8 @@
ArtField* Class::FindField(Thread* self,
ObjPtr<Class> klass,
- const StringPiece& name,
- const StringPiece& type) {
+ std::string_view name,
+ std::string_view type) {
// Find a field using the JLS field resolution order
for (ObjPtr<Class> k = klass; k != nullptr; k = k->GetSuperClass()) {
// Is the field in this class?
@@ -1252,18 +1254,50 @@
return (type_id == nullptr) ? dex::TypeIndex() : dex_file.GetIndexForTypeId(*type_id);
}
+ALWAYS_INLINE
+static bool IsMethodPreferredOver(ArtMethod* orig_method,
+ bool orig_method_hidden,
+ ArtMethod* new_method,
+ bool new_method_hidden) {
+ DCHECK(new_method != nullptr);
+
+ // Is this the first result?
+ if (orig_method == nullptr) {
+ return true;
+ }
+
+ // Original method is hidden, the new one is not?
+ if (orig_method_hidden && !new_method_hidden) {
+ return true;
+ }
+
+ // We iterate over virtual methods first and then over direct ones,
+ // so we can never be in situation where `orig_method` is direct and
+ // `new_method` is virtual.
+ DCHECK(!orig_method->IsDirect() || new_method->IsDirect());
+
+ // Original method is synthetic, the new one is not?
+ if (orig_method->IsSynthetic() && !new_method->IsSynthetic()) {
+ return true;
+ }
+
+ return false;
+}
+
template <PointerSize kPointerSize, bool kTransactionActive>
ObjPtr<Method> Class::GetDeclaredMethodInternal(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args) {
- // Covariant return types permit the class to define multiple
- // methods with the same name and parameter types. Prefer to
- // return a non-synthetic method in such situations. We may
- // still return a synthetic method to handle situations like
- // escalated visibility. We never return miranda methods that
- // were synthesized by the runtime.
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context) {
+ // Covariant return types (or smali) permit the class to define
+ // multiple methods with the same name and parameter types.
+ // Prefer (in decreasing order of importance):
+ // 1) non-hidden method over hidden
+ // 2) virtual methods over direct
+ // 3) non-synthetic methods over synthetic
+ // We never return miranda methods that were synthesized by the runtime.
StackHandleScope<3> hs(self);
auto h_method_name = hs.NewHandle(name);
if (UNLIKELY(h_method_name == nullptr)) {
@@ -1272,8 +1306,13 @@
}
auto h_args = hs.NewHandle(args);
Handle<Class> h_klass = hs.NewHandle(klass);
+ constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kNone;
ArtMethod* result = nullptr;
+ bool result_hidden = false;
for (auto& m : h_klass->GetDeclaredVirtualMethods(kPointerSize)) {
+ if (m.IsMiranda()) {
+ continue;
+ }
auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize);
// May cause thread suspension.
ObjPtr<String> np_name = np_method->ResolveNameString();
@@ -1283,14 +1322,24 @@
}
continue;
}
- if (!m.IsMiranda()) {
- if (!m.IsSynthetic()) {
- return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
- }
- result = &m; // Remember as potential result if it's not a miranda method.
+ bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
+ if (!m_hidden && !m.IsSynthetic()) {
+ // Non-hidden, virtual, non-synthetic. Best possible result, exit early.
+ return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
+ } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
+ // Remember as potential result.
+ result = &m;
+ result_hidden = m_hidden;
}
}
- if (result == nullptr) {
+
+ if ((result != nullptr) && !result_hidden) {
+ // We have not found a non-hidden, virtual, non-synthetic method, but
+ // if we have found a non-hidden, virtual, synthetic method, we cannot
+ // do better than that later.
+ DCHECK(!result->IsDirect());
+ DCHECK(result->IsSynthetic());
+ } else {
for (auto& m : h_klass->GetDirectMethods(kPointerSize)) {
auto modifiers = m.GetAccessFlags();
if ((modifiers & kAccConstructor) != 0) {
@@ -1310,12 +1359,20 @@
continue;
}
DCHECK(!m.IsMiranda()); // Direct methods cannot be miranda methods.
- if ((modifiers & kAccSynthetic) == 0) {
+ bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
+ if (!m_hidden && !m.IsSynthetic()) {
+ // Non-hidden, direct, non-synthetic. Any virtual result could only have been
+ // hidden, therefore this is the best possible match. Exit now.
+ DCHECK((result == nullptr) || result_hidden);
return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
+ } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
+ // Remember as potential result.
+ result = &m;
+ result_hidden = m_hidden;
}
- result = &m; // Remember as potential result.
}
}
+
return result != nullptr
? Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, result)
: nullptr;
@@ -1326,25 +1383,29 @@
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k32, true>(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64, false>(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64, true>(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template <PointerSize kPointerSize, bool kTransactionActive>
ObjPtr<Constructor> Class::GetDeclaredConstructorInternal(
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 981ecf1..bde9b03 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -17,6 +17,8 @@
#ifndef ART_RUNTIME_MIRROR_CLASS_H_
#define ART_RUNTIME_MIRROR_CLASS_H_
+#include <string_view>
+
#include "base/bit_utils.h"
#include "base/casts.h"
#include "base/stride_iterator.h"
@@ -37,6 +39,10 @@
class TypeList;
} // namespace dex
+namespace hiddenapi {
+class AccessContext;
+} // namespace hiddenapi
+
template<typename T> class ArraySlice;
class ArtField;
class ArtMethod;
@@ -49,7 +55,6 @@
template<typename T> class LengthPrefixedArray;
enum class PointerSize : size_t;
class Signature;
-class StringPiece;
template<size_t kNumReferences> class PACKED(4) StackHandleScope;
class Thread;
@@ -552,7 +557,7 @@
// Returns true if this class is in the same packages as that class.
bool IsInSamePackage(ObjPtr<Class> that) REQUIRES_SHARED(Locks::mutator_lock_);
- static bool IsInSamePackage(const StringPiece& descriptor1, const StringPiece& descriptor2);
+ static bool IsInSamePackage(std::string_view descriptor1, std::string_view descriptor2);
// Returns true if this class can access that class.
bool CanAccess(ObjPtr<Class> that) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -702,10 +707,12 @@
REQUIRES_SHARED(Locks::mutator_lock_);
template <PointerSize kPointerSize, bool kTransactionActive>
- static ObjPtr<Method> GetDeclaredMethodInternal(Thread* self,
- ObjPtr<Class> klass,
- ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args)
+ static ObjPtr<Method> GetDeclaredMethodInternal(
+ Thread* self,
+ ObjPtr<Class> klass,
+ ObjPtr<String> name,
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context)
REQUIRES_SHARED(Locks::mutator_lock_);
template <PointerSize kPointerSize, bool kTransactionActive>
@@ -855,12 +862,12 @@
// in an interface without superinterfaces, see JLS 9.2, can be inherited, see JLS 9.4.1).
// TODO: Implement search for a unique maximally-specific non-abstract superinterface method.
- ArtMethod* FindInterfaceMethod(const StringPiece& name,
- const StringPiece& signature,
+ ArtMethod* FindInterfaceMethod(std::string_view name,
+ std::string_view signature,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindInterfaceMethod(const StringPiece& name,
+ ArtMethod* FindInterfaceMethod(std::string_view name,
const Signature& signature,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -894,12 +901,12 @@
// does not satisfy the request. Special consideration should be given to the case where this
// function returns a method that's not inherited (found in step 2, returned in step 4).
- ArtMethod* FindClassMethod(const StringPiece& name,
- const StringPiece& signature,
+ ArtMethod* FindClassMethod(std::string_view name,
+ std::string_view signature,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindClassMethod(const StringPiece& name,
+ ArtMethod* FindClassMethod(std::string_view name,
const Signature& signature,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -909,13 +916,13 @@
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindConstructor(const StringPiece& signature, PointerSize pointer_size)
+ ArtMethod* FindConstructor(std::string_view signature, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name, PointerSize pointer_size)
+ ArtMethod* FindDeclaredVirtualMethodByName(std::string_view name, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredDirectMethodByName(const StringPiece& name, PointerSize pointer_size)
+ ArtMethod* FindDeclaredDirectMethodByName(std::string_view name, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
ArtMethod* FindClassInitializer(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1030,12 +1037,12 @@
// Find a static or instance field using the JLS resolution order
static ArtField* FindField(Thread* self,
ObjPtr<Class> klass,
- const StringPiece& name,
- const StringPiece& type)
+ std::string_view name,
+ std::string_view type)
REQUIRES_SHARED(Locks::mutator_lock_);
// Finds the given instance field in this class or a superclass.
- ArtField* FindInstanceField(const StringPiece& name, const StringPiece& type)
+ ArtField* FindInstanceField(std::string_view name, std::string_view type)
REQUIRES_SHARED(Locks::mutator_lock_);
// Finds the given instance field in this class or a superclass, only searches classes that
@@ -1043,7 +1050,7 @@
ArtField* FindInstanceField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtField* FindDeclaredInstanceField(const StringPiece& name, const StringPiece& type)
+ ArtField* FindDeclaredInstanceField(std::string_view name, std::string_view type)
REQUIRES_SHARED(Locks::mutator_lock_);
ArtField* FindDeclaredInstanceField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx)
@@ -1052,8 +1059,8 @@
// Finds the given static field in this class or a superclass.
static ArtField* FindStaticField(Thread* self,
ObjPtr<Class> klass,
- const StringPiece& name,
- const StringPiece& type)
+ std::string_view name,
+ std::string_view type)
REQUIRES_SHARED(Locks::mutator_lock_);
// Finds the given static field in this class or superclass, only searches classes that
@@ -1064,7 +1071,7 @@
uint32_t dex_field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtField* FindDeclaredStaticField(const StringPiece& name, const StringPiece& type)
+ ArtField* FindDeclaredStaticField(std::string_view name, std::string_view type)
REQUIRES_SHARED(Locks::mutator_lock_);
ArtField* FindDeclaredStaticField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx)
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 47b621a..f0ad931 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -110,8 +110,7 @@
WriteBarrier::ForEveryFieldWrite(this);
}
-inline void DexCache::SetPreResolvedString(dex::StringIndex string_idx,
- ObjPtr<String> resolved) {
+inline void DexCache::SetPreResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) {
DCHECK(resolved != nullptr);
DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds());
GetPreResolvedStrings()[string_idx.index_] = GcRoot<mirror::String>(resolved);
@@ -122,6 +121,17 @@
WriteBarrier::ForEveryFieldWrite(this);
}
+inline void DexCache::ClearPreResolvedStrings() {
+ SetFieldPtr64</*kTransactionActive=*/false,
+ /*kCheckTransaction=*/false,
+ kVerifyNone,
+ GcRoot<mirror::String>*>(PreResolvedStringsOffset(), nullptr);
+ SetField32</*kTransactionActive=*/false,
+ /*bool kCheckTransaction=*/false,
+ kVerifyNone,
+ /*kIsVolatile=*/false>(NumPreResolvedStringsOffset(), 0);
+}
+
inline void DexCache::ClearString(dex::StringIndex string_idx) {
DCHECK(Runtime::Current()->IsAotCompiler());
uint32_t slot_idx = StringSlotIndex(string_idx);
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index c742928..b5619f8 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -283,6 +283,11 @@
ObjPtr<mirror::String> resolved)
ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
+ // Clear the preresolved string cache to prevent further usage. Not thread safe, so should only
+ // be called when the string cache is guaranteed to not be accessed.
+ void ClearPreResolvedStrings()
+ ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
+
// Clear a string for a string_idx, used to undo string intern transactions to make sure
// the string isn't kept live.
void ClearString(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 2c2ad9b..005e272 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -253,9 +253,13 @@
}
template<VerifyObjectFlags kVerifyFlags>
+inline IntArray* Object::AsIntArrayUnchecked() {
+ return down_cast<IntArray*>(this);
+}
+template<VerifyObjectFlags kVerifyFlags>
inline IntArray* Object::AsIntArray() {
DCHECK((IsIntArray<kVerifyFlags>()));
- return down_cast<IntArray*>(this);
+ return AsIntArrayUnchecked<kVerifyFlags>();
}
template<VerifyObjectFlags kVerifyFlags>
@@ -264,9 +268,13 @@
}
template<VerifyObjectFlags kVerifyFlags>
+inline LongArray* Object::AsLongArrayUnchecked() {
+ return down_cast<LongArray*>(this);
+}
+template<VerifyObjectFlags kVerifyFlags>
inline LongArray* Object::AsLongArray() {
DCHECK((IsLongArray<kVerifyFlags>()));
- return down_cast<LongArray*>(this);
+ return AsLongArrayUnchecked<kVerifyFlags>();
}
template<VerifyObjectFlags kVerifyFlags>
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index ba222f6..ca8867d8 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -223,11 +223,15 @@
bool IsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
IntArray* AsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ IntArray* AsIntArrayUnchecked() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
LongArray* AsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ LongArray* AsLongArrayUnchecked() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsFloatArray() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index f4b8ba5..27987c0 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -70,8 +70,6 @@
ASSERT_EQ(string->IsValueNull(), false);
// strlen is necessary because the 1-character string "\x00\x00" is interpreted as ""
ASSERT_TRUE(string->Equals(utf8_in) || (expected_utf16_length == 1 && strlen(utf8_in) == 0));
- ASSERT_TRUE(string->Equals(StringPiece(utf8_in)) ||
- (expected_utf16_length == 1 && strlen(utf8_in) == 0));
for (int32_t i = 0; i < expected_utf16_length; i++) {
EXPECT_EQ(utf16_expected[i], string->CharAt(i));
}
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 01315e7..bf99c37 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -234,19 +234,6 @@
}
}
-bool String::Equals(const uint16_t* that_chars, int32_t that_offset, int32_t that_length) {
- if (this->GetLength() != that_length) {
- return false;
- } else {
- for (int32_t i = 0; i < that_length; ++i) {
- if (this->CharAt(i) != that_chars[that_offset + i]) {
- return false;
- }
- }
- return true;
- }
-}
-
bool String::Equals(const char* modified_utf8) {
const int32_t length = GetLength();
int32_t i = 0;
@@ -274,30 +261,6 @@
return *modified_utf8 == '\0';
}
-bool String::Equals(const StringPiece& modified_utf8) {
- const int32_t length = GetLength();
- const char* p = modified_utf8.data();
- for (int32_t i = 0; i < length; ++i) {
- uint32_t ch = GetUtf16FromUtf8(&p);
-
- if (GetLeadingUtf16Char(ch) != CharAt(i)) {
- return false;
- }
-
- const uint16_t trailing = GetTrailingUtf16Char(ch);
- if (trailing != 0) {
- if (i == (length - 1)) {
- return false;
- }
-
- if (CharAt(++i) != trailing) {
- return false;
- }
- }
- }
- return true;
-}
-
// Create a modified UTF-8 encoded std::string from a java/lang/String object.
std::string String::ToModifiedUtf8() {
size_t byte_count = GetUtfLength();
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index c367957..4a7f4ae 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -27,7 +27,6 @@
template<class T> class Handle;
struct StringOffsets;
-class StringPiece;
class StubTest_ReadBarrierForRoot_Test;
namespace mirror {
@@ -154,27 +153,10 @@
static String* AllocFromModifiedUtf8(Thread* self, int32_t utf16_length, const char* utf8_data_in)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
- // TODO: This is only used in the interpreter to compare against
- // entries from a dex files constant pool (ArtField names). Should
- // we unify this with Equals(const StringPiece&); ?
bool Equals(const char* modified_utf8) REQUIRES_SHARED(Locks::mutator_lock_);
- // TODO: This is only used to compare DexCache.location with
- // a dex_file's location (which is an std::string). Do we really
- // need this in mirror::String just for that one usage ?
- bool Equals(const StringPiece& modified_utf8)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
bool Equals(ObjPtr<String> that) REQUIRES_SHARED(Locks::mutator_lock_);
- // Compare UTF-16 code point values not in a locale-sensitive manner
- int Compare(int32_t utf16_length, const char* utf8_data_in);
-
- // TODO: do we need this overload? give it a more intention-revealing name.
- bool Equals(const uint16_t* that_chars, int32_t that_offset,
- int32_t that_length)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Create a modified UTF-8 encoded std::string from a java/lang/String object.
std::string ToModifiedUtf8() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 7240357..6abc8d7 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -280,7 +280,7 @@
// This function is inlined and just helps to not have the VLOG and ATRACE check at all the
// potential tracing points.
void Monitor::AtraceMonitorLock(Thread* self, mirror::Object* obj, bool is_wait) {
- if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging) && ATRACE_ENABLED())) {
+ if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging) && ATraceEnabled())) {
AtraceMonitorLockImpl(self, obj, is_wait);
}
}
@@ -338,12 +338,12 @@
(obj == nullptr ? -1 : static_cast<int32_t>(reinterpret_cast<uintptr_t>(obj))),
(filename != nullptr ? filename : "null"),
line_number);
- ATRACE_BEGIN(tmp.c_str());
+ ATraceBegin(tmp.c_str());
}
void Monitor::AtraceMonitorUnlock() {
if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
- ATRACE_END();
+ ATraceEnd();
}
}
@@ -431,7 +431,7 @@
// If systrace logging is enabled, first look at the lock owner. Acquiring the monitor's
// lock and then re-acquiring the mutator lock can deadlock.
bool started_trace = false;
- if (ATRACE_ENABLED()) {
+ if (ATraceEnabled()) {
if (owner_ != nullptr) { // Did the owner_ give the lock up?
std::ostringstream oss;
std::string name;
@@ -450,7 +450,7 @@
oss << " blocking from "
<< ArtMethod::PrettyMethod(m) << "(" << (filename != nullptr ? filename : "null")
<< ":" << line_number << ")";
- ATRACE_BEGIN(oss.str().c_str());
+ ATraceBegin(oss.str().c_str());
started_trace = true;
}
}
@@ -581,7 +581,7 @@
}
}
if (started_trace) {
- ATRACE_END();
+ ATraceEnd();
}
self->SetMonitorEnterObject(nullptr);
monitor_lock_.Lock(self); // Reacquire locks in order.
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 26fc5e9..891ecef 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -149,6 +149,7 @@
HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12)
| (1 << 13),
PROFILE_SYSTEM_SERVER = 1 << 14,
+ USE_APP_IMAGE_STARTUP_CACHE = 1 << 16,
// bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value
// corresponding to hiddenapi::EnforcementPolicy
@@ -298,6 +299,10 @@
bool profile_system_server = (runtime_flags & PROFILE_SYSTEM_SERVER) == PROFILE_SYSTEM_SERVER;
runtime_flags &= ~PROFILE_SYSTEM_SERVER;
+ Runtime::Current()->SetLoadAppImageStartupCacheEnabled(
+ (runtime_flags & USE_APP_IMAGE_STARTUP_CACHE) != 0u);
+ runtime_flags &= ~USE_APP_IMAGE_STARTUP_CACHE;
+
if (runtime_flags != 0) {
LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags);
}
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 4115791..db62475 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -113,15 +113,18 @@
: hiddenapi::AccessContext(caller);
}
+static std::function<hiddenapi::AccessContext()> GetHiddenapiAccessContextFunction(Thread* self) {
+ return [=]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); };
+}
+
// Returns true if the first non-ClassClass caller up the stack should not be
// allowed access to `member`.
template<typename T>
ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::ShouldDenyAccessToMember(
- member,
- [&]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); },
- hiddenapi::AccessMethod::kReflection);
+ return hiddenapi::ShouldDenyAccessToMember(member,
+ GetHiddenapiAccessContextFunction(self),
+ hiddenapi::AccessMethod::kReflection);
}
// Returns true if a class member should be discoverable with reflection given
@@ -563,7 +566,8 @@
soa.Self(),
DecodeClass(soa, javaThis),
soa.Decode<mirror::String>(name),
- soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
+ soa.Decode<mirror::ObjectArray<mirror::Class>>(args),
+ GetHiddenapiAccessContextFunction(soa.Self())));
if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 88adad0..150fa78 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -44,13 +44,17 @@
#include "arch/instruction_set.h"
#include "base/aborting.h"
+#include "base/bit_utils.h"
#include "base/file_utils.h"
#include "base/memory_tool.h"
#include "base/mutex.h"
#include "base/os.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
+#include "class_linker.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
#include "oat_quick_method_header.h"
+#include "runtime.h"
#include "thread-current-inl.h"
#endif
@@ -63,6 +67,18 @@
static constexpr bool kUseAddr2line = !kIsTargetBuild;
+std::string FindAddr2line() {
+ if (!kIsTargetBuild) {
+ constexpr const char* kAddr2linePrebuiltPath =
+ "/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8/bin/x86_64-linux-addr2line";
+ const char* env_value = getenv("ANDROID_BUILD_TOP");
+ if (env_value != nullptr) {
+ return std::string(env_value) + kAddr2linePrebuiltPath;
+ }
+ }
+ return std::string("/usr/bin/addr2line");
+}
+
ALWAYS_INLINE
static inline void WritePrefix(std::ostream& os, const char* prefix, bool odd) {
if (prefix != nullptr) {
@@ -232,8 +248,9 @@
}
pipe->reset(); // Close early.
+ std::string addr2linePath = FindAddr2line();
const char* args[7] = {
- "/usr/bin/addr2line",
+ addr2linePath.c_str(),
"--functions",
"--inlines",
"--demangle",
@@ -274,11 +291,22 @@
}
static bool PcIsWithinQuickCode(ArtMethod* method, uintptr_t pc) NO_THREAD_SAFETY_ANALYSIS {
- uintptr_t code = reinterpret_cast<uintptr_t>(EntryPointToCodePointer(
- method->GetEntryPointFromQuickCompiledCode()));
- if (code == 0) {
+ const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
+ if (entry_point == nullptr) {
return pc == 0;
}
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (class_linker->IsQuickGenericJniStub(entry_point) ||
+ class_linker->IsQuickResolutionStub(entry_point) ||
+ class_linker->IsQuickToInterpreterBridge(entry_point)) {
+ return false;
+ }
+ // The backtrace library might have heuristically subracted instruction
+ // size from the pc, to pretend the pc is at the calling instruction.
+ if (reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) - pc <= 4) {
+ return false;
+ }
+ uintptr_t code = reinterpret_cast<uintptr_t>(EntryPointToCodePointer(entry_point));
uintptr_t code_size = reinterpret_cast<const OatQuickMethodHeader*>(code)[-1].GetCodeSize();
return code <= pc && pc <= (code + code_size);
}
@@ -314,7 +342,7 @@
if (kUseAddr2line) {
// Try to run it to see whether we have it. Push an argument so that it doesn't assume a.out
// and print to stderr.
- use_addr2line = (gAborting > 0) && RunCommand("addr2line -h");
+ use_addr2line = (gAborting > 0) && RunCommand(FindAddr2line() + " -h");
} else {
use_addr2line = false;
}
diff --git a/runtime/oat.h b/runtime/oat.h
index 88238d9..b824729 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,8 +31,8 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- // Last oat version changed reason: Partial boot image.
- static constexpr uint8_t kOatVersion[] = { '1', '6', '6', '\0' };
+ // Last oat version changed reason: Add code size to CodeInfo.
+ static constexpr uint8_t kOatVersion[] = { '1', '6', '7', '\0' };
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index e433cbc..67803b6 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -58,13 +58,14 @@
#include "elf_file.h"
#include "elf_utils.h"
#include "gc_root.h"
+#include "gc/heap.h"
#include "gc/space/image_space.h"
#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "oat.h"
#include "oat_file-inl.h"
#include "oat_file_manager.h"
-#include "runtime.h"
+#include "runtime-inl.h"
#include "vdex_file.h"
namespace art {
@@ -447,34 +448,6 @@
return true;
}
-static void DCheckIndexToBssMapping(OatFile* oat_file,
- uint32_t number_of_indexes,
- size_t slot_size,
- const IndexBssMapping* index_bss_mapping) {
- if (kIsDebugBuild && index_bss_mapping != nullptr) {
- size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes);
- const IndexBssMappingEntry* prev_entry = nullptr;
- for (const IndexBssMappingEntry& entry : *index_bss_mapping) {
- CHECK_ALIGNED_PARAM(entry.bss_offset, slot_size);
- // When loading a non-executable ElfOatFile, .bss symbols are not even
- // looked up, so we cannot verify the offset against BssSize().
- if (oat_file->IsExecutable()) {
- CHECK_LT(entry.bss_offset, oat_file->BssSize());
- }
- uint32_t mask = entry.GetMask(index_bits);
- CHECK_LE(POPCOUNT(mask) * slot_size, entry.bss_offset);
- size_t index_mask_span = (mask != 0u) ? 32u - index_bits - CTZ(mask) : 0u;
- CHECK_LE(index_mask_span, entry.GetIndex(index_bits));
- if (prev_entry != nullptr) {
- CHECK_LT(prev_entry->GetIndex(index_bits), entry.GetIndex(index_bits) - index_mask_span);
- }
- prev_entry = &entry;
- }
- CHECK(prev_entry != nullptr);
- CHECK_LT(prev_entry->GetIndex(index_bits), number_of_indexes);
- }
-}
-
bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* error_msg) {
if (!GetOatHeader().IsValid()) {
std::string cause = GetOatHeader().GetValidationErrorMessage();
@@ -813,12 +786,6 @@
this, &oat, i, dex_file_location, "string", &string_bss_mapping, error_msg)) {
return false;
}
- DCheckIndexToBssMapping(
- this, header->method_ids_size_, static_cast<size_t>(pointer_size), method_bss_mapping);
- DCheckIndexToBssMapping(
- this, header->type_ids_size_, sizeof(GcRoot<mirror::Class>), type_bss_mapping);
- DCheckIndexToBssMapping(
- this, header->string_ids_size_, sizeof(GcRoot<mirror::String>), string_bss_mapping);
std::string canonical_location =
DexFileLoader::GetDexCanonicalLocation(dex_file_name.c_str());
@@ -838,10 +805,10 @@
oat_dex_files_storage_.push_back(oat_dex_file);
// Add the location and canonical location (if different) to the oat_dex_files_ table.
- StringPiece key(oat_dex_file->GetDexFileLocation());
+ std::string_view key(oat_dex_file->GetDexFileLocation());
oat_dex_files_.Put(key, oat_dex_file);
if (canonical_location != dex_file_location) {
- StringPiece canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
+ std::string_view canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
oat_dex_files_.Put(canonical_key, oat_dex_file);
}
}
@@ -1664,7 +1631,7 @@
// without any performance loss, for example by not doing the first lock-free lookup.
const OatDexFile* oat_dex_file = nullptr;
- StringPiece key(dex_location);
+ std::string_view key(dex_location);
// Try to find the key cheaply in the oat_dex_files_ map which holds dex locations
// directly mentioned in the oat file and doesn't require locking.
auto primary_it = oat_dex_files_.find(key);
@@ -1683,7 +1650,7 @@
// We haven't seen this dex_location before, we must check the canonical location.
std::string dex_canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location);
if (dex_canonical_location != dex_location) {
- StringPiece canonical_key(dex_canonical_location);
+ std::string_view canonical_key(dex_canonical_location);
auto canonical_it = oat_dex_files_.find(canonical_key);
if (canonical_it != oat_dex_files_.end()) {
oat_dex_file = canonical_it->second;
@@ -1692,7 +1659,7 @@
// Copy the key to the string_cache_ and store the result in secondary map.
string_cache_.emplace_back(key.data(), key.length());
- StringPiece key_copy(string_cache_.back());
+ std::string_view key_copy(string_cache_.back());
secondary_oat_dex_files_.PutBefore(secondary_lb, key_copy, oat_dex_file);
}
}
@@ -1836,6 +1803,16 @@
reinterpret_cast<const OatMethodOffsets*>(methods_pointer));
}
+ArrayRef<const uint8_t> OatDexFile::GetQuickenedInfoOf(const DexFile& dex_file,
+ uint32_t dex_method_idx) const {
+ const OatFile* oat_file = GetOatFile();
+ if (oat_file == nullptr) {
+ return ArrayRef<const uint8_t>();
+ } else {
+ return oat_file->GetVdexFile()->GetQuickenedInfoOf(dex_file, dex_method_idx);
+ }
+}
+
const dex::ClassDef* OatDexFile::FindClassDef(const DexFile& dex_file,
const char* descriptor,
size_t hash) {
@@ -2009,6 +1986,82 @@
return oat_dex_file->GetOatClass(class_def_idx);
}
+static void DCheckIndexToBssMapping(const OatFile* oat_file,
+ uint32_t number_of_indexes,
+ size_t slot_size,
+ const IndexBssMapping* index_bss_mapping) {
+ if (kIsDebugBuild && index_bss_mapping != nullptr) {
+ size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes);
+ const IndexBssMappingEntry* prev_entry = nullptr;
+ for (const IndexBssMappingEntry& entry : *index_bss_mapping) {
+ CHECK_ALIGNED_PARAM(entry.bss_offset, slot_size);
+ CHECK_LT(entry.bss_offset, oat_file->BssSize());
+ uint32_t mask = entry.GetMask(index_bits);
+ CHECK_LE(POPCOUNT(mask) * slot_size, entry.bss_offset);
+ size_t index_mask_span = (mask != 0u) ? 32u - index_bits - CTZ(mask) : 0u;
+ CHECK_LE(index_mask_span, entry.GetIndex(index_bits));
+ if (prev_entry != nullptr) {
+ CHECK_LT(prev_entry->GetIndex(index_bits), entry.GetIndex(index_bits) - index_mask_span);
+ }
+ prev_entry = &entry;
+ }
+ CHECK(prev_entry != nullptr);
+ CHECK_LT(prev_entry->GetIndex(index_bits), number_of_indexes);
+ }
+}
+
+void OatFile::InitializeRelocations() const {
+ DCHECK(IsExecutable());
+
+ // Initialize the .data.bimg.rel.ro section.
+ if (!GetBootImageRelocations().empty()) {
+ uint8_t* reloc_begin = const_cast<uint8_t*>(DataBimgRelRoBegin());
+ CheckedCall(mprotect,
+ "un-protect boot image relocations",
+ reloc_begin,
+ DataBimgRelRoSize(),
+ PROT_READ | PROT_WRITE);
+ uint32_t boot_image_begin = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(
+ Runtime::Current()->GetHeap()->GetBootImageSpaces().front()->Begin()));
+ for (const uint32_t& relocation : GetBootImageRelocations()) {
+ const_cast<uint32_t&>(relocation) += boot_image_begin;
+ }
+ CheckedCall(mprotect,
+ "protect boot image relocations",
+ reloc_begin,
+ DataBimgRelRoSize(),
+ PROT_READ);
+ }
+
+ // Before initializing .bss, check the .bss mappings in debug mode.
+ if (kIsDebugBuild) {
+ PointerSize pointer_size = GetInstructionSetPointerSize(GetOatHeader().GetInstructionSet());
+ for (const OatDexFile* odf : GetOatDexFiles()) {
+ const DexFile::Header* header =
+ reinterpret_cast<const DexFile::Header*>(odf->GetDexFilePointer());
+ DCheckIndexToBssMapping(this,
+ header->method_ids_size_,
+ static_cast<size_t>(pointer_size),
+ odf->GetMethodBssMapping());
+ DCheckIndexToBssMapping(this,
+ header->type_ids_size_,
+ sizeof(GcRoot<mirror::Class>),
+ odf->GetTypeBssMapping());
+ DCheckIndexToBssMapping(this,
+ header->string_ids_size_,
+ sizeof(GcRoot<mirror::String>),
+ odf->GetStringBssMapping());
+ }
+ }
+
+ // Initialize the .bss section.
+ // TODO: Pre-initialize from boot/app image?
+ ArtMethod* resolution_method = Runtime::Current()->GetResolutionMethod();
+ for (ArtMethod*& entry : GetBssMethods()) {
+ entry = resolution_method;
+ }
+}
+
void OatDexFile::AssertAotCompiler() {
CHECK(Runtime::Current()->IsAotCompiler());
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 3e9c01f..04b666c 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -19,13 +19,13 @@
#include <list>
#include <string>
+#include <string_view>
#include <vector>
#include "base/array_ref.h"
#include "base/mutex.h"
#include "base/os.h"
#include "base/safe_map.h"
-#include "base/stringpiece.h"
#include "base/tracking_safe_map.h"
#include "class_status.h"
#include "compiler_filter.h"
@@ -325,6 +325,9 @@
ArrayRef<ArtMethod*> GetBssMethods() const;
ArrayRef<GcRoot<mirror::Object>> GetBssGcRoots() const;
+ // Initialize relocation sections (.data.bimg.rel.ro and .bss).
+ void InitializeRelocations() const;
+
// Returns the absolute dex location for the encoded relative dex location.
//
// If not null, abs_dex_location is used to resolve the absolute dex
@@ -398,12 +401,13 @@
// Owning storage for the OatDexFile objects.
std::vector<const OatDexFile*> oat_dex_files_storage_;
- // NOTE: We use a StringPiece as the key type to avoid a memory allocation on every
- // lookup with a const char* key. The StringPiece doesn't own its backing storage,
+ // NOTE: We use a std::string_view as the key type to avoid a memory allocation on every
+ // lookup with a const char* key. The std::string_view doesn't own its backing storage,
// therefore we're using the OatDexFile::dex_file_location_ as the backing storage
// for keys in oat_dex_files_ and the string_cache_ entries for the backing storage
// of keys in secondary_oat_dex_files_ and oat_dex_files_by_canonical_location_.
- typedef AllocationTrackingSafeMap<StringPiece, const OatDexFile*, kAllocatorTagOatFile> Table;
+ using Table =
+ AllocationTrackingSafeMap<std::string_view, const OatDexFile*, kAllocatorTagOatFile>;
// Map each location and canonical location (if different) retrieved from the
// oat file to its OatDexFile. This map doesn't change after it's constructed in Setup()
@@ -422,7 +426,7 @@
// Cache of strings. Contains the backing storage for keys in the secondary_oat_dex_files_
// and the lazily initialized oat_dex_files_by_canonical_location_.
- // NOTE: We're keeping references to contained strings in form of StringPiece and adding
+ // NOTE: We're keeping references to contained strings in form of std::string_view and adding
// new strings to the end. The adding of a new element must not touch any previously stored
// elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't.
mutable std::list<std::string> string_cache_ GUARDED_BY(secondary_lookup_lock_);
@@ -502,6 +506,9 @@
return dex_file_pointer_;
}
+ ArrayRef<const uint8_t> GetQuickenedInfoOf(const DexFile& dex_file,
+ uint32_t dex_method_idx) const;
+
// Looks up a class definition by its class descriptor. Hash must be
// ComputeModifiedUtf8Hash(descriptor).
static const dex::ClassDef* FindClassDef(const DexFile& dex_file,
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index 8798c69..6c123c4 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -35,6 +35,8 @@
uint32_t code_size)
: vmap_table_offset_(vmap_table_offset),
code_size_(code_size) {
+ DCHECK_NE(vmap_table_offset, 0u);
+ DCHECK_NE(code_size, 0u);
}
static OatQuickMethodHeader* FromCodePointer(const void* code_ptr) {
@@ -58,7 +60,7 @@
}
bool IsOptimized() const {
- return GetCodeSize() != 0 && vmap_table_offset_ != 0;
+ return (code_size_ & kCodeSizeMask) != 0 && vmap_table_offset_ != 0;
}
const uint8_t* GetOptimizedCodeInfoPtr() const {
@@ -76,7 +78,11 @@
}
uint32_t GetCodeSize() const {
- return code_size_ & kCodeSizeMask;
+ DCHECK(IsOptimized());
+ size_t code_size1 = code_size_ & kCodeSizeMask;
+ size_t code_size2 = CodeInfo::DecodeCodeSize(GetOptimizedCodeInfoPtr());
+ DCHECK_EQ(code_size1, code_size2);
+ return code_size2;
}
const uint32_t* GetCodeSizeAddr() const {
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 6fd691f..f516d0d 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -24,7 +24,6 @@
#include "base/file_utils.h"
#include "base/macros.h"
-#include "base/stringpiece.h"
#include "base/utils.h"
#include "debugger.h"
#include "gc/heap.h"
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index feade83..7ecfdc7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -199,6 +199,7 @@
};
namespace {
+
#ifdef __APPLE__
inline char** GetEnviron() {
// When Google Test is built as a framework on MacOS X, the environ variable
@@ -212,6 +213,11 @@
extern "C" char** environ;
inline char** GetEnviron() { return environ; }
#endif
+
+void CheckConstants() {
+ CHECK_EQ(mirror::Array::kFirstElementOffset, mirror::Array::FirstElementOffset());
+}
+
} // namespace
Runtime::Runtime()
@@ -284,6 +290,7 @@
verifier_logging_threshold_ms_(100) {
static_assert(Runtime::kCalleeSaveSize ==
static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
+ CheckConstants();
std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u);
interpreter::CheckInterpreterAsmConstants();
@@ -862,15 +869,22 @@
GetInstructionSetString(kRuntimeISA));
}
- // Send the initialized phase event. Send it before starting daemons, as otherwise
- // sending thread events becomes complicated.
+ StartDaemonThreads();
+
+ // Make sure the environment is still clean (no lingering local refs from starting daemon
+ // threads).
+ {
+ ScopedObjectAccess soa(self);
+ self->GetJniEnv()->AssertLocalsEmpty();
+ }
+
+ // Send the initialized phase event. Send it after starting the Daemon threads so that agents
+ // cannot delay the daemon threads from starting forever.
{
ScopedObjectAccess soa(self);
callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit);
}
- StartDaemonThreads();
-
{
ScopedObjectAccess soa(self);
self->GetJniEnv()->AssertLocalsEmpty();
@@ -1173,8 +1187,8 @@
compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler);
compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions);
- for (StringPiece option : Runtime::Current()->GetCompilerOptions()) {
- if (option.starts_with("--debuggable")) {
+ for (const std::string& option : Runtime::Current()->GetCompilerOptions()) {
+ if (option == "--debuggable") {
SetJavaDebuggable(true);
break;
}
@@ -2475,10 +2489,15 @@
instruction_set += GetInstructionSetString(kRuntimeISA);
argv->push_back(instruction_set);
- std::unique_ptr<const InstructionSetFeatures> features(InstructionSetFeatures::FromCppDefines());
- std::string feature_string("--instruction-set-features=");
- feature_string += features->GetFeatureString();
- argv->push_back(feature_string);
+ if (InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+ argv->push_back("--instruction-set-features=runtime");
+ } else {
+ std::unique_ptr<const InstructionSetFeatures> features(
+ InstructionSetFeatures::FromCppDefines());
+ std::string feature_string("--instruction-set-features=");
+ feature_string += features->GetFeatureString();
+ argv->push_back(feature_string);
+ }
}
void Runtime::CreateJitCodeCache(bool rwx_memory_allowed) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ee2c514..81c17a5 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -823,6 +823,14 @@
ThreadPool* const thread_pool_;
};
+ bool LoadAppImageStartupCache() const {
+ return load_app_image_startup_cache_;
+ }
+
+ void SetLoadAppImageStartupCacheEnabled(bool enabled) {
+ load_app_image_startup_cache_ = enabled;
+ }
+
private:
static void InitPlatformSignalHandlers();
@@ -1145,6 +1153,8 @@
uint32_t verifier_logging_threshold_ms_;
+ bool load_app_image_startup_cache_ = false;
+
// Note: See comments on GetFaultMessage.
friend std::string GetFaultMessageForAbortLogging();
friend class ScopedThreadPoolUsage;
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 62dec15..5d30b77 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -227,6 +227,7 @@
bool verbose,
InstructionSet instruction_set) const {
vios->Stream() << "CodeInfo BitSize=" << size_in_bits_
+ << " CodeSize:" << StackMap::UnpackNativePc(packed_code_size_, instruction_set)
<< " FrameSize:" << packed_frame_size_ * kStackAlignment
<< " CoreSpillMask:" << std::hex << core_spill_mask_
<< " FpSpillMask:" << std::hex << fp_spill_mask_
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 87133cf..59da923 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -438,8 +438,15 @@
// Accumulate code info size statistics into the given Stats tree.
static void CollectSizeStats(const uint8_t* code_info, /*out*/ Stats* parent);
+ ALWAYS_INLINE static size_t DecodeCodeSize(const uint8_t* data,
+ InstructionSet isa = kRuntimeISA) {
+ uint32_t packed_code_size = BitMemoryReader(data).ReadVarint();
+ return StackMap::UnpackNativePc(packed_code_size, isa);
+ }
+
ALWAYS_INLINE static QuickMethodFrameInfo DecodeFrameInfo(const uint8_t* data) {
BitMemoryReader reader(data);
+ reader.ReadVarint(); // Skip code size.
return QuickMethodFrameInfo(
reader.ReadVarint() * kStackAlignment, // Decode packed_frame_size_ and unpack.
reader.ReadVarint(), // core_spill_mask_.
@@ -461,6 +468,7 @@
// Invokes the callback with member pointer of each header field.
template<typename Callback>
ALWAYS_INLINE static void ForEachHeaderField(Callback callback) {
+ callback(&CodeInfo::packed_code_size_);
callback(&CodeInfo::packed_frame_size_);
callback(&CodeInfo::core_spill_mask_);
callback(&CodeInfo::fp_spill_mask_);
@@ -486,6 +494,7 @@
callback(&CodeInfo::dex_register_catalog_);
}
+ uint32_t packed_code_size_ = 0; // The size of native PC range.
uint32_t packed_frame_size_ = 0; // Frame size in kStackAlignment units.
uint32_t core_spill_mask_ = 0;
uint32_t fp_spill_mask_ = 0;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 309c04e..44b45cf 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -16,10 +16,6 @@
#include "thread.h"
-#if !defined(__APPLE__)
-#include <sched.h>
-#endif
-
#include <pthread.h>
#include <signal.h>
#include <sys/resource.h>
@@ -95,6 +91,7 @@
#include "oat_quick_method_header.h"
#include "obj_ptr-inl.h"
#include "object_lock.h"
+#include "palette/palette.h"
#include "quick/quick_method_frame_info.h"
#include "quick_exception_handler.h"
#include "read_barrier-inl.h"
@@ -4233,6 +4230,7 @@
Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
}
+
void Thread::ReleaseLongJumpContextInternal() {
// Each QuickExceptionHandler gets a long jump context and uses
// it for doing the long jump, after finding catch blocks/doing deoptimization.
@@ -4246,4 +4244,26 @@
delete tlsPtr_.long_jump_context;
}
+void Thread::SetNativePriority(int new_priority) {
+ // ART tests on JVM can reach this code path, use tid = 0 as shorthand for current thread.
+ PaletteStatus status = PaletteSchedSetPriority(0, new_priority);
+ CHECK(status == PaletteStatus::kOkay || status == PaletteStatus::kCheckErrno);
+}
+
+int Thread::GetNativePriority() {
+ int priority = 0;
+ // ART tests on JVM can reach this code path, use tid = 0 as shorthand for current thread.
+ PaletteStatus status = PaletteSchedGetPriority(0, &priority);
+ CHECK(status == PaletteStatus::kOkay || status == PaletteStatus::kCheckErrno);
+ return priority;
+}
+
+bool Thread::IsSystemDaemon() const {
+ if (GetPeer() == nullptr) {
+ return false;
+ }
+ return jni::DecodeArtField(
+ WellKnownClasses::java_lang_Thread_systemDaemon)->GetBoolean(GetPeer());
+}
+
} // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index 7a14fd7..ec276b5 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1230,6 +1230,8 @@
return this == jit_sensitive_thread_;
}
+ bool IsSystemDaemon() const REQUIRES_SHARED(Locks::mutator_lock_);
+
// Returns true if StrictMode events are traced for the current thread.
static bool IsSensitiveThread() {
if (is_sensitive_thread_hook_ != nullptr) {
diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc
index 24864f9..f333400 100644
--- a/runtime/thread_android.cc
+++ b/runtime/thread_android.cc
@@ -16,84 +16,8 @@
#include "thread.h"
-#include <errno.h>
-#include <limits.h>
-#include <sys/resource.h>
-#include <sys/time.h>
-
-#include <processgroup/sched_policy.h>
-#include <utils/threads.h>
-
-#include "base/macros.h"
-
namespace art {
-// Conversion map for "nice" values.
-//
-// We use Android thread priority constants to be consistent with the rest
-// of the system. In some cases adjacent entries may overlap.
-//
-static const int kNiceValues[10] = {
- ANDROID_PRIORITY_LOWEST, // 1 (MIN_PRIORITY)
- ANDROID_PRIORITY_BACKGROUND + 6,
- ANDROID_PRIORITY_BACKGROUND + 3,
- ANDROID_PRIORITY_BACKGROUND,
- ANDROID_PRIORITY_NORMAL, // 5 (NORM_PRIORITY)
- ANDROID_PRIORITY_NORMAL - 2,
- ANDROID_PRIORITY_NORMAL - 4,
- ANDROID_PRIORITY_URGENT_DISPLAY + 3,
- ANDROID_PRIORITY_URGENT_DISPLAY + 2,
- ANDROID_PRIORITY_URGENT_DISPLAY // 10 (MAX_PRIORITY)
-};
-
-void Thread::SetNativePriority(int newPriority) {
- if (newPriority < 1 || newPriority > 10) {
- LOG(WARNING) << "bad priority " << newPriority;
- newPriority = 5;
- }
-
- int newNice = kNiceValues[newPriority-1];
- pid_t tid = GetTid();
-
- // TODO: b/18249098 The code below is broken. It uses getpriority() as a proxy for whether a
- // thread is already in the SP_FOREGROUND cgroup. This is not necessarily true for background
- // processes, where all threads are in the SP_BACKGROUND cgroup. This means that callers will
- // have to call setPriority twice to do what they want :
- //
- // Thread.setPriority(Thread.MIN_PRIORITY); // no-op wrt to cgroups
- // Thread.setPriority(Thread.MAX_PRIORITY); // will actually change cgroups.
- if (newNice >= ANDROID_PRIORITY_BACKGROUND) {
- set_sched_policy(tid, SP_BACKGROUND);
- } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
- set_sched_policy(tid, SP_FOREGROUND);
- }
-
- if (setpriority(PRIO_PROCESS, tid, newNice) != 0) {
- PLOG(INFO) << *this << " setPriority(PRIO_PROCESS, " << tid << ", " << newNice << ") failed";
- }
-}
-
-int Thread::GetNativePriority() {
- errno = 0;
- int native_priority = getpriority(PRIO_PROCESS, 0);
- if (native_priority == -1 && errno != 0) {
- PLOG(WARNING) << "getpriority failed";
- return kNormThreadPriority;
- }
-
- int managed_priority = kMinThreadPriority;
- for (size_t i = 0; i < arraysize(kNiceValues); i++) {
- if (native_priority >= kNiceValues[i]) {
- break;
- }
- managed_priority++;
- }
- if (managed_priority > kMaxThreadPriority) {
- managed_priority = kMaxThreadPriority;
- }
- return managed_priority;
-}
-
void Thread::SetUpAlternateSignalStack() {
// Bionic does this for us.
}
diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc
index d05fecf..3ed4276 100644
--- a/runtime/thread_linux.cc
+++ b/runtime/thread_linux.cc
@@ -23,14 +23,6 @@
namespace art {
-void Thread::SetNativePriority(int) {
- // Do nothing.
-}
-
-int Thread::GetNativePriority() {
- return kNormThreadPriority;
-}
-
static void SigAltStack(stack_t* new_stack, stack_t* old_stack) {
if (sigaltstack(new_stack, old_stack) == -1) {
PLOG(FATAL) << "sigaltstack failed";
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 1b3b037..a5406ea 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -683,7 +683,7 @@
AssertThreadsAreSuspended(self, self);
}
}
- ATRACE_BEGIN((std::string("Mutator threads suspended for ") + cause).c_str());
+ ATraceBegin((std::string("Mutator threads suspended for ") + cause).c_str());
if (self != nullptr) {
VLOG(threads) << *self << " SuspendAll complete";
@@ -811,7 +811,7 @@
VLOG(threads) << "Thread[null] ResumeAll starting";
}
- ATRACE_END();
+ ATraceEnd();
ScopedTrace trace("Resuming mutator threads");
@@ -855,8 +855,8 @@
}
bool ThreadList::Resume(Thread* thread, SuspendReason reason) {
- // This assumes there was an ATRACE_BEGIN when we suspended the thread.
- ATRACE_END();
+ // This assumes there was an ATraceBegin when we suspended the thread.
+ ATraceEnd();
Thread* self = Thread::Current();
DCHECK_NE(thread, self);
@@ -987,10 +987,10 @@
// done.
if (thread->IsSuspended()) {
VLOG(threads) << "SuspendThreadByPeer thread suspended: " << *thread;
- if (ATRACE_ENABLED()) {
+ if (ATraceEnabled()) {
std::string name;
thread->GetThreadName(name);
- ATRACE_BEGIN(StringPrintf("SuspendThreadByPeer suspended %s for peer=%p", name.c_str(),
+ ATraceBegin(StringPrintf("SuspendThreadByPeer suspended %s for peer=%p", name.c_str(),
peer).c_str());
}
return thread;
@@ -1097,10 +1097,10 @@
// count, or else we've waited and it has self suspended) or is the current thread, we're
// done.
if (thread->IsSuspended()) {
- if (ATRACE_ENABLED()) {
+ if (ATraceEnabled()) {
std::string name;
thread->GetThreadName(name);
- ATRACE_BEGIN(StringPrintf("SuspendThreadByThreadId suspended %s id=%d",
+ ATraceBegin(StringPrintf("SuspendThreadByThreadId suspended %s id=%d",
name.c_str(), thread_id).c_str());
}
VLOG(threads) << "SuspendThreadByThreadId thread suspended: " << *thread;
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index 36890a6..09171a4 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -35,7 +35,7 @@
protected:
void VerifyClass(const std::string& descriptor)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ASSERT_TRUE(descriptor != nullptr);
+ ASSERT_FALSE(descriptor.empty());
Thread* self = Thread::Current();
ObjPtr<mirror::Class> klass = class_linker_->FindSystemClass(self, descriptor.c_str());
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 91be00d..150d35c 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -55,18 +55,22 @@
const NullType* NullType::instance_ = nullptr;
PrimitiveType::PrimitiveType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
: RegType(klass, descriptor, cache_id) {
CHECK(klass != nullptr);
CHECK(!descriptor.empty());
}
-Cat1Type::Cat1Type(ObjPtr<mirror::Class> klass, const StringPiece& descriptor, uint16_t cache_id)
+Cat1Type::Cat1Type(ObjPtr<mirror::Class> klass,
+ const std::string_view& descriptor,
+ uint16_t cache_id)
: PrimitiveType(klass, descriptor, cache_id) {
}
-Cat2Type::Cat2Type(ObjPtr<mirror::Class> klass, const StringPiece& descriptor, uint16_t cache_id)
+Cat2Type::Cat2Type(ObjPtr<mirror::Class> klass,
+ const std::string_view& descriptor,
+ uint16_t cache_id)
: PrimitiveType(klass, descriptor, cache_id) {
}
@@ -132,7 +136,7 @@
}
const DoubleHiType* DoubleHiType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new DoubleHiType(klass, descriptor, cache_id);
@@ -147,7 +151,7 @@
}
const DoubleLoType* DoubleLoType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new DoubleLoType(klass, descriptor, cache_id);
@@ -162,7 +166,7 @@
}
const LongLoType* LongLoType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new LongLoType(klass, descriptor, cache_id);
@@ -170,7 +174,7 @@
}
const LongHiType* LongHiType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new LongHiType(klass, descriptor, cache_id);
@@ -192,7 +196,7 @@
}
const FloatType* FloatType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new FloatType(klass, descriptor, cache_id);
@@ -207,7 +211,7 @@
}
const CharType* CharType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new CharType(klass, descriptor, cache_id);
@@ -222,7 +226,7 @@
}
const ShortType* ShortType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new ShortType(klass, descriptor, cache_id);
@@ -237,7 +241,7 @@
}
const ByteType* ByteType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new ByteType(klass, descriptor, cache_id);
@@ -252,7 +256,7 @@
}
const IntegerType* IntegerType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new IntegerType(klass, descriptor, cache_id);
@@ -267,7 +271,7 @@
}
const ConflictType* ConflictType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new ConflictType(klass, descriptor, cache_id);
@@ -282,7 +286,7 @@
}
const BooleanType* BooleanType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(BooleanType::instance_ == nullptr);
instance_ = new BooleanType(klass, descriptor, cache_id);
@@ -301,7 +305,7 @@
}
const UndefinedType* UndefinedType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new UndefinedType(klass, descriptor, cache_id);
@@ -316,7 +320,7 @@
}
PreciseReferenceType::PreciseReferenceType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
: RegType(klass, descriptor, cache_id) {
// Note: no check for IsInstantiable() here. We may produce this in case an InstantiationError
@@ -352,47 +356,47 @@
std::string UnresolvedReferenceType::Dump() const {
std::stringstream result;
- result << "Unresolved Reference" << ": " << PrettyDescriptor(GetDescriptor().as_string().c_str());
+ result << "Unresolved Reference: " << PrettyDescriptor(std::string(GetDescriptor()).c_str());
return result.str();
}
std::string UnresolvedUninitializedRefType::Dump() const {
std::stringstream result;
- result << "Unresolved And Uninitialized Reference" << ": "
- << PrettyDescriptor(GetDescriptor().as_string().c_str())
+ result << "Unresolved And Uninitialized Reference: "
+ << PrettyDescriptor(std::string(GetDescriptor()).c_str())
<< " Allocation PC: " << GetAllocationPc();
return result.str();
}
std::string UnresolvedUninitializedThisRefType::Dump() const {
std::stringstream result;
- result << "Unresolved And Uninitialized This Reference"
- << PrettyDescriptor(GetDescriptor().as_string().c_str());
+ result << "Unresolved And Uninitialized This Reference: "
+ << PrettyDescriptor(std::string(GetDescriptor()).c_str());
return result.str();
}
std::string ReferenceType::Dump() const {
std::stringstream result;
- result << "Reference" << ": " << mirror::Class::PrettyDescriptor(GetClass());
+ result << "Reference: " << mirror::Class::PrettyDescriptor(GetClass());
return result.str();
}
std::string PreciseReferenceType::Dump() const {
std::stringstream result;
- result << "Precise Reference" << ": "<< mirror::Class::PrettyDescriptor(GetClass());
+ result << "Precise Reference: " << mirror::Class::PrettyDescriptor(GetClass());
return result.str();
}
std::string UninitializedReferenceType::Dump() const {
std::stringstream result;
- result << "Uninitialized Reference" << ": " << mirror::Class::PrettyDescriptor(GetClass());
+ result << "Uninitialized Reference: " << mirror::Class::PrettyDescriptor(GetClass());
result << " Allocation PC: " << GetAllocationPc();
return result.str();
}
std::string UninitializedThisReferenceType::Dump() const {
std::stringstream result;
- result << "Uninitialized This Reference" << ": " << mirror::Class::PrettyDescriptor(GetClass());
+ result << "Uninitialized This Reference: " << mirror::Class::PrettyDescriptor(GetClass());
result << "Allocation PC: " << GetAllocationPc();
return result.str();
}
@@ -990,7 +994,7 @@
}
const NullType* NullType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new NullType(klass, descriptor, cache_id);
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 3369784..56073db 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -21,12 +21,12 @@
#include <limits>
#include <set>
#include <string>
+#include <string_view>
#include "base/arena_object.h"
#include "base/bit_vector.h"
#include "base/locks.h"
#include "base/macros.h"
-#include "base/stringpiece.h"
#include "dex/primitive.h"
#include "gc_root.h"
#include "handle_scope.h"
@@ -185,7 +185,7 @@
bool IsJavaLangObjectArray() const
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsInstantiableTypes() const REQUIRES_SHARED(Locks::mutator_lock_);
- const StringPiece& GetDescriptor() const {
+ const std::string_view& GetDescriptor() const {
DCHECK(HasClass() ||
(IsUnresolvedTypes() && !IsUnresolvedMergedReference() &&
!IsUnresolvedSuperClass()));
@@ -319,7 +319,7 @@
protected:
RegType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: descriptor_(descriptor),
klass_(klass),
@@ -336,7 +336,7 @@
virtual AssignmentType GetAssignmentTypeImpl() const = 0;
- const StringPiece descriptor_;
+ const std::string_view descriptor_;
mutable GcRoot<mirror::Class> klass_; // Non-const only due to moving classes.
const uint16_t cache_id_;
@@ -389,7 +389,7 @@
// Create the singleton instance.
static const ConflictType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -402,7 +402,7 @@
private:
ConflictType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -425,7 +425,7 @@
// Create the singleton instance.
static const UndefinedType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -438,7 +438,7 @@
private:
UndefinedType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -450,7 +450,7 @@
class PrimitiveType : public RegType {
public:
PrimitiveType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
bool HasClassVirtual() const override { return true; }
@@ -458,7 +458,8 @@
class Cat1Type : public PrimitiveType {
public:
- Cat1Type(ObjPtr<mirror::Class> klass, const StringPiece& descriptor,
+ Cat1Type(ObjPtr<mirror::Class> klass,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
};
@@ -467,7 +468,7 @@
bool IsInteger() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const IntegerType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const IntegerType* GetInstance() PURE;
@@ -479,7 +480,7 @@
private:
IntegerType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -492,7 +493,7 @@
bool IsBoolean() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const BooleanType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const BooleanType* GetInstance() PURE;
@@ -504,7 +505,7 @@
private:
BooleanType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -518,7 +519,7 @@
bool IsByte() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const ByteType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const ByteType* GetInstance() PURE;
@@ -530,7 +531,7 @@
private:
ByteType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -543,7 +544,7 @@
bool IsShort() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const ShortType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const ShortType* GetInstance() PURE;
@@ -554,7 +555,7 @@
}
private:
- ShortType(ObjPtr<mirror::Class> klass, const StringPiece& descriptor,
+ ShortType(ObjPtr<mirror::Class> klass, const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -567,7 +568,7 @@
bool IsChar() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const CharType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const CharType* GetInstance() PURE;
@@ -579,7 +580,7 @@
private:
CharType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -592,7 +593,7 @@
bool IsFloat() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const FloatType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const FloatType* GetInstance() PURE;
@@ -604,7 +605,7 @@
private:
FloatType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -615,7 +616,7 @@
class Cat2Type : public PrimitiveType {
public:
Cat2Type(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
};
@@ -625,7 +626,7 @@
bool IsLongLo() const override { return true; }
bool IsLong() const override { return true; }
static const LongLoType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const LongLoType* GetInstance() PURE;
@@ -637,7 +638,7 @@
private:
LongLoType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -650,7 +651,7 @@
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
bool IsLongHi() const override { return true; }
static const LongHiType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const LongHiType* GetInstance() PURE;
@@ -662,7 +663,7 @@
private:
LongHiType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -676,7 +677,7 @@
bool IsDoubleLo() const override { return true; }
bool IsDouble() const override { return true; }
static const DoubleLoType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const DoubleLoType* GetInstance() PURE;
@@ -688,7 +689,7 @@
private:
DoubleLoType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -701,7 +702,7 @@
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
bool IsDoubleHi() const override { return true; }
static const DoubleHiType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const DoubleHiType* GetInstance() PURE;
@@ -713,7 +714,7 @@
private:
DoubleHiType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -886,7 +887,7 @@
// Create the singleton instance.
static const NullType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -905,7 +906,7 @@
}
private:
- NullType(ObjPtr<mirror::Class> klass, const StringPiece& descriptor, uint16_t cache_id)
+ NullType(ObjPtr<mirror::Class> klass, const std::string_view& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -920,7 +921,7 @@
class UninitializedType : public RegType {
public:
UninitializedType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint32_t allocation_pc,
uint16_t cache_id)
: RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) {}
@@ -945,7 +946,7 @@
class UninitializedReferenceType final : public UninitializedType {
public:
UninitializedReferenceType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint32_t allocation_pc,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -964,8 +965,9 @@
// constructor.
class UnresolvedUninitializedRefType final : public UninitializedType {
public:
- UnresolvedUninitializedRefType(const StringPiece& descriptor,
- uint32_t allocation_pc, uint16_t cache_id)
+ UnresolvedUninitializedRefType(const std::string_view& descriptor,
+ uint32_t allocation_pc,
+ uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(nullptr, descriptor, allocation_pc, cache_id) {
CheckConstructorInvariants(this);
@@ -986,7 +988,7 @@
class UninitializedThisReferenceType final : public UninitializedType {
public:
UninitializedThisReferenceType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(klass, descriptor, 0, cache_id) {
@@ -1005,8 +1007,7 @@
class UnresolvedUninitializedThisRefType final : public UninitializedType {
public:
- UnresolvedUninitializedThisRefType(const StringPiece& descriptor,
- uint16_t cache_id)
+ UnresolvedUninitializedThisRefType(const std::string_view& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(nullptr, descriptor, 0, cache_id) {
CheckConstructorInvariants(this);
@@ -1027,7 +1028,7 @@
class ReferenceType final : public RegType {
public:
ReferenceType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -1052,7 +1053,7 @@
class PreciseReferenceType final : public RegType {
public:
PreciseReferenceType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1072,7 +1073,7 @@
// Common parent of unresolved types.
class UnresolvedType : public RegType {
public:
- UnresolvedType(const StringPiece& descriptor, uint16_t cache_id)
+ UnresolvedType(const std::string_view& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(nullptr, descriptor, cache_id) {}
@@ -1088,7 +1089,7 @@
// of this type must be conservative.
class UnresolvedReferenceType final : public UnresolvedType {
public:
- UnresolvedReferenceType(const StringPiece& descriptor, uint16_t cache_id)
+ UnresolvedReferenceType(const std::string_view& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UnresolvedType(descriptor, cache_id) {
CheckConstructorInvariants(this);
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index ceba748..7bff255 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -135,7 +135,7 @@
}
}
-bool RegTypeCache::MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise) {
+bool RegTypeCache::MatchDescriptor(size_t idx, const std::string_view& descriptor, bool precise) {
const RegType* entry = entries_[idx];
if (descriptor != entry->descriptor_) {
return false;
@@ -170,20 +170,20 @@
return klass;
}
-StringPiece RegTypeCache::AddString(const StringPiece& string_piece) {
- char* ptr = allocator_.AllocArray<char>(string_piece.length());
- memcpy(ptr, string_piece.data(), string_piece.length());
- return StringPiece(ptr, string_piece.length());
+std::string_view RegTypeCache::AddString(const std::string_view& str) {
+ char* ptr = allocator_.AllocArray<char>(str.length());
+ memcpy(ptr, str.data(), str.length());
+ return std::string_view(ptr, str.length());
}
const RegType& RegTypeCache::From(ObjPtr<mirror::ClassLoader> loader,
const char* descriptor,
bool precise) {
- StringPiece sp_descriptor(descriptor);
- // Try looking up the class in the cache first. We use a StringPiece to avoid continual strlen
- // operations on the descriptor.
+ std::string_view sv_descriptor(descriptor);
+ // Try looking up the class in the cache first. We use a std::string_view to avoid
+ // repeated strlen operations on the descriptor.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- if (MatchDescriptor(i, sp_descriptor, precise)) {
+ if (MatchDescriptor(i, sv_descriptor, precise)) {
return *(entries_[i]);
}
}
@@ -205,9 +205,9 @@
DCHECK(!(klass->IsAbstract()) || klass->IsArrayClass());
DCHECK(!klass->IsInterface());
entry =
- new (&allocator_) PreciseReferenceType(klass, AddString(sp_descriptor), entries_.size());
+ new (&allocator_) PreciseReferenceType(klass, AddString(sv_descriptor), entries_.size());
} else {
- entry = new (&allocator_) ReferenceType(klass, AddString(sp_descriptor), entries_.size());
+ entry = new (&allocator_) ReferenceType(klass, AddString(sv_descriptor), entries_.size());
}
return AddEntry(entry);
} else { // Class not resolved.
@@ -221,7 +221,7 @@
}
if (IsValidDescriptor(descriptor)) {
return AddEntry(
- new (&allocator_) UnresolvedReferenceType(AddString(sp_descriptor), entries_.size()));
+ new (&allocator_) UnresolvedReferenceType(AddString(sv_descriptor), entries_.size()));
} else {
// The descriptor is broken return the unknown type as there's nothing sensible that
// could be done at runtime
@@ -254,7 +254,7 @@
return nullptr;
}
-const RegType* RegTypeCache::InsertClass(const StringPiece& descriptor,
+const RegType* RegTypeCache::InsertClass(const std::string_view& descriptor,
ObjPtr<mirror::Class> klass,
bool precise) {
// No reference to the class was found, create new reference.
@@ -272,7 +272,7 @@
DCHECK(klass != nullptr);
const RegType* reg_type = FindClass(klass, precise);
if (reg_type == nullptr) {
- reg_type = InsertClass(AddString(StringPiece(descriptor)), klass, precise);
+ reg_type = InsertClass(AddString(std::string_view(descriptor)), klass, precise);
}
return *reg_type;
}
@@ -488,7 +488,7 @@
const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
UninitializedType* entry = nullptr;
- const StringPiece& descriptor(type.GetDescriptor());
+ const std::string_view& descriptor(type.GetDescriptor());
if (type.IsUnresolvedTypes()) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
const RegType* cur_entry = entries_[i];
@@ -525,7 +525,7 @@
RegType* entry;
if (uninit_type.IsUnresolvedTypes()) {
- const StringPiece& descriptor(uninit_type.GetDescriptor());
+ const std::string_view& descriptor(uninit_type.GetDescriptor());
for (size_t i = primitive_count_; i < entries_.size(); i++) {
const RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedReference() &&
@@ -575,7 +575,7 @@
const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
UninitializedType* entry;
- const StringPiece& descriptor(type.GetDescriptor());
+ const std::string_view& descriptor(type.GetDescriptor());
if (type.IsUnresolvedTypes()) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
const RegType* cur_entry = entries_[i];
@@ -656,7 +656,7 @@
return Conflict();
} else if (array.IsUnresolvedTypes()) {
DCHECK(!array.IsUnresolvedMergedReference()); // Caller must make sure not to ask for this.
- const std::string descriptor(array.GetDescriptor().as_string());
+ const std::string descriptor(array.GetDescriptor());
return FromDescriptor(loader, descriptor.c_str() + 1, false);
} else {
ObjPtr<mirror::Class> klass = array.GetClass()->GetComponentType();
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index d668222..a9a8116 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_H_
#include <stdint.h>
+#include <string_view>
#include <vector>
#include "base/casts.h"
@@ -32,7 +33,6 @@
class ClassLoader;
} // namespace mirror
class ScopedArenaAllocator;
-class StringPiece;
namespace verifier {
@@ -80,7 +80,7 @@
const RegType* FindClass(ObjPtr<mirror::Class> klass, bool precise) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Insert a new class with a specified descriptor, must not already be in the cache.
- const RegType* InsertClass(const StringPiece& descriptor,
+ const RegType* InsertClass(const std::string_view& descriptor,
ObjPtr<mirror::Class> klass,
bool precise)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -164,7 +164,7 @@
void FillPrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_);
ObjPtr<mirror::Class> ResolveClass(const char* descriptor, ObjPtr<mirror::ClassLoader> loader)
REQUIRES_SHARED(Locks::mutator_lock_);
- bool MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise)
+ bool MatchDescriptor(size_t idx, const std::string_view& descriptor, bool precise)
REQUIRES_SHARED(Locks::mutator_lock_);
const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -173,9 +173,9 @@
template <class RegTypeType>
RegTypeType& AddEntry(RegTypeType* new_entry) REQUIRES_SHARED(Locks::mutator_lock_);
- // Add a string piece to the arena allocator so that it stays live for the lifetime of the
- // verifier.
- StringPiece AddString(const StringPiece& string_piece);
+ // Add a string to the arena allocator so that it stays live for the lifetime of the
+ // verifier and return a string view.
+ std::string_view AddString(const std::string_view& str);
static void CreatePrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index bdcadd9..6793a0a 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -17,14 +17,15 @@
#include "verifier_deps.h"
#include <cstring>
+#include <sstream>
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/indenter.h"
#include "base/leb128.h"
#include "base/mutex-inl.h"
-#include "base/stl_util.h"
#include "compiler_callbacks.h"
+#include "dex/class_accessor-inl.h"
#include "dex/dex_file-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
@@ -46,21 +47,22 @@
VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files)
: VerifierDeps(dex_files, /*output_only=*/ true) {}
-void VerifierDeps::MergeWith(const VerifierDeps& other,
+void VerifierDeps::MergeWith(std::unique_ptr<VerifierDeps> other,
const std::vector<const DexFile*>& dex_files) {
- DCHECK(dex_deps_.size() == other.dex_deps_.size());
+ DCHECK(other != nullptr);
+ DCHECK_EQ(dex_deps_.size(), other->dex_deps_.size());
for (const DexFile* dex_file : dex_files) {
DexFileDeps* my_deps = GetDexFileDeps(*dex_file);
- const DexFileDeps& other_deps = *other.GetDexFileDeps(*dex_file);
+ DexFileDeps& other_deps = *other->GetDexFileDeps(*dex_file);
// We currently collect extra strings only on the main `VerifierDeps`,
// which should be the one passed as `this` in this method.
DCHECK(other_deps.strings_.empty());
- MergeSets(my_deps->assignable_types_, other_deps.assignable_types_);
- MergeSets(my_deps->unassignable_types_, other_deps.unassignable_types_);
- MergeSets(my_deps->classes_, other_deps.classes_);
- MergeSets(my_deps->fields_, other_deps.fields_);
- MergeSets(my_deps->methods_, other_deps.methods_);
- MergeSets(my_deps->unverified_classes_, other_deps.unverified_classes_);
+ my_deps->assignable_types_.merge(other_deps.assignable_types_);
+ my_deps->unassignable_types_.merge(other_deps.unassignable_types_);
+ my_deps->classes_.merge(other_deps.classes_);
+ my_deps->fields_.merge(other_deps.fields_);
+ my_deps->methods_.merge(other_deps.methods_);
+ my_deps->unverified_classes_.merge(other_deps.unverified_classes_);
}
}
@@ -690,6 +692,12 @@
}
}
+static inline std::string ToHex(uint32_t value) {
+ std::stringstream ss;
+ ss << std::hex << value << std::dec;
+ return ss.str();
+}
+
} // namespace
void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files,
@@ -849,9 +857,10 @@
}
bool VerifierDeps::ValidateDependencies(Handle<mirror::ClassLoader> class_loader,
- Thread* self) const {
+ Thread* self,
+ /* out */ std::string* error_msg) const {
for (const auto& entry : dex_deps_) {
- if (!VerifyDexFile(class_loader, *entry.first, *entry.second, self)) {
+ if (!VerifyDexFile(class_loader, *entry.first, *entry.second, self, error_msg)) {
return false;
}
}
@@ -862,10 +871,10 @@
// the same lookup pattern.
static ObjPtr<mirror::Class> FindClassAndClearException(ClassLinker* class_linker,
Thread* self,
- const char* name,
+ const std::string& name,
Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ObjPtr<mirror::Class> result = class_linker->FindClass(self, name, class_loader);
+ ObjPtr<mirror::Class> result = class_linker->FindClass(self, name.c_str(), class_loader);
if (result == nullptr) {
DCHECK(self->IsExceptionPending());
self->ClearException();
@@ -877,7 +886,8 @@
const DexFile& dex_file,
const std::set<TypeAssignability>& assignables,
bool expected_assignability,
- Thread* self) const {
+ Thread* self,
+ /* out */ std::string* error_msg) const {
StackHandleScope<2> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
MutableHandle<mirror::Class> source(hs.NewHandle<mirror::Class>(nullptr));
@@ -892,22 +902,19 @@
FindClassAndClearException(class_linker, self, source_desc.c_str(), class_loader));
if (destination == nullptr) {
- LOG(INFO) << "VerifiersDeps: Could not resolve class " << destination_desc;
+ *error_msg = "Could not resolve class " + destination_desc;
return false;
}
if (source == nullptr) {
- LOG(INFO) << "VerifierDeps: Could not resolve class " << source_desc;
+ *error_msg = "Could not resolve class " + source_desc;
return false;
}
DCHECK(destination->IsResolved() && source->IsResolved());
if (destination->IsAssignableFrom(source.Get()) != expected_assignability) {
- LOG(INFO) << "VerifierDeps: Class "
- << destination_desc
- << (expected_assignability ? " not " : " ")
- << "assignable from "
- << source_desc;
+ *error_msg = "Class " + destination_desc + (expected_assignability ? " not " : " ") +
+ "assignable from " + source_desc;
return false;
}
}
@@ -917,31 +924,27 @@
bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const std::set<ClassResolution>& classes,
- Thread* self) const {
+ Thread* self,
+ /* out */ std::string* error_msg) const {
StackHandleScope<1> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
for (const auto& entry : classes) {
- const char* descriptor = dex_file.StringByTypeIdx(entry.GetDexTypeIndex());
+ std::string descriptor = dex_file.StringByTypeIdx(entry.GetDexTypeIndex());
cls.Assign(FindClassAndClearException(class_linker, self, descriptor, class_loader));
if (entry.IsResolved()) {
if (cls == nullptr) {
- LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+ *error_msg = "Could not resolve class " + descriptor;
return false;
} else if (entry.GetAccessFlags() != GetAccessFlags(cls.Get())) {
- LOG(INFO) << "VerifierDeps: Unexpected access flags on class "
- << descriptor
- << std::hex
- << " (expected="
- << entry.GetAccessFlags()
- << ", actual="
- << GetAccessFlags(cls.Get()) << ")"
- << std::dec;
+ *error_msg = "Unexpected access flags on class " + descriptor
+ + " (expected=" + ToHex(entry.GetAccessFlags())
+ + ", actual=" + ToHex(GetAccessFlags(cls.Get())) + ")";
return false;
}
} else if (cls != nullptr) {
- LOG(INFO) << "VerifierDeps: Unexpected successful resolution of class " << descriptor;
+ *error_msg = "Unexpected successful resolution of class " + descriptor;
return false;
}
}
@@ -960,14 +963,16 @@
bool VerifierDeps::VerifyFields(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const std::set<FieldResolution>& fields,
- Thread* self) const {
+ Thread* self,
+ /* out */ std::string* error_msg) const {
// Check recorded fields are resolved the same way, have the same recorded class,
// and have the same recorded flags.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
for (const auto& entry : fields) {
const dex::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex());
- StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_));
- StringPiece type(dex_file.StringDataByIdx(dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
+ std::string_view name(dex_file.StringDataByIdx(field_id.name_idx_));
+ std::string_view type(
+ dex_file.StringDataByIdx(dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
// Only use field_id.class_idx_ when the entry is unresolved, which is rare.
// Otherwise, we might end up resolving an application class, which is expensive.
std::string expected_decl_klass = entry.IsResolved()
@@ -976,7 +981,7 @@
ObjPtr<mirror::Class> cls = FindClassAndClearException(
class_linker, self, expected_decl_klass.c_str(), class_loader);
if (cls == nullptr) {
- LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass;
+ *error_msg = "Could not resolve class " + expected_decl_klass;
return false;
}
DCHECK(cls->IsResolved());
@@ -985,25 +990,25 @@
if (entry.IsResolved()) {
std::string temp;
if (field == nullptr) {
- LOG(INFO) << "VerifierDeps: Could not resolve field "
- << GetFieldDescription(dex_file, entry.GetDexFieldIndex());
+ *error_msg = "Could not resolve field " +
+ GetFieldDescription(dex_file, entry.GetDexFieldIndex());
return false;
} else if (expected_decl_klass != field->GetDeclaringClass()->GetDescriptor(&temp)) {
- LOG(INFO) << "VerifierDeps: Unexpected declaring class for field resolution "
- << GetFieldDescription(dex_file, entry.GetDexFieldIndex())
- << " (expected=" << expected_decl_klass
- << ", actual=" << field->GetDeclaringClass()->GetDescriptor(&temp) << ")";
+ *error_msg = "Unexpected declaring class for field resolution "
+ + GetFieldDescription(dex_file, entry.GetDexFieldIndex())
+ + " (expected=" + expected_decl_klass
+ + ", actual=" + field->GetDeclaringClass()->GetDescriptor(&temp) + ")";
return false;
} else if (entry.GetAccessFlags() != GetAccessFlags(field)) {
- LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved field "
- << GetFieldDescription(dex_file, entry.GetDexFieldIndex())
- << std::hex << " (expected=" << entry.GetAccessFlags()
- << ", actual=" << GetAccessFlags(field) << ")" << std::dec;
+ *error_msg = "Unexpected access flags for resolved field "
+ + GetFieldDescription(dex_file, entry.GetDexFieldIndex())
+ + " (expected=" + ToHex(entry.GetAccessFlags())
+ + ", actual=" + ToHex(GetAccessFlags(field)) + ")";
return false;
}
} else if (field != nullptr) {
- LOG(INFO) << "VerifierDeps: Unexpected successful resolution of field "
- << GetFieldDescription(dex_file, entry.GetDexFieldIndex());
+ *error_msg = "Unexpected successful resolution of field "
+ + GetFieldDescription(dex_file, entry.GetDexFieldIndex());
return false;
}
}
@@ -1021,7 +1026,8 @@
bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const std::set<MethodResolution>& methods,
- Thread* self) const {
+ Thread* self,
+ /* out */ std::string* error_msg) const {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
PointerSize pointer_size = class_linker->GetImagePointerSize();
@@ -1039,7 +1045,7 @@
ObjPtr<mirror::Class> cls = FindClassAndClearException(
class_linker, self, expected_decl_klass.c_str(), class_loader);
if (cls == nullptr) {
- LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass;
+ *error_msg = "Could not resolve class " + expected_decl_klass;
return false;
}
DCHECK(cls->IsResolved());
@@ -1053,53 +1059,94 @@
if (entry.IsResolved()) {
std::string temp;
if (method == nullptr) {
- LOG(INFO) << "VerifierDeps: Could not resolve method "
- << GetMethodDescription(dex_file, entry.GetDexMethodIndex());
+ *error_msg = "Could not resolve method "
+ + GetMethodDescription(dex_file, entry.GetDexMethodIndex());
return false;
} else if (expected_decl_klass != method->GetDeclaringClass()->GetDescriptor(&temp)) {
- LOG(INFO) << "VerifierDeps: Unexpected declaring class for method resolution "
- << GetMethodDescription(dex_file, entry.GetDexMethodIndex())
- << " (expected="
- << expected_decl_klass
- << ", actual="
- << method->GetDeclaringClass()->GetDescriptor(&temp)
- << ")";
+ *error_msg = "Unexpected declaring class for method resolution "
+ + GetMethodDescription(dex_file, entry.GetDexMethodIndex())
+ + " (expected=" + expected_decl_klass
+ + ", actual=" + method->GetDeclaringClass()->GetDescriptor(&temp) + ")";
return false;
} else if (entry.GetAccessFlags() != GetAccessFlags(method)) {
- LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved method resolution "
- << GetMethodDescription(dex_file, entry.GetDexMethodIndex())
- << std::hex
- << " (expected="
- << entry.GetAccessFlags()
- << ", actual="
- << GetAccessFlags(method) << ")"
- << std::dec;
+ *error_msg = "Unexpected access flags for resolved method resolution "
+ + GetMethodDescription(dex_file, entry.GetDexMethodIndex())
+ + " (expected=" + ToHex(entry.GetAccessFlags())
+ + ", actual=" + ToHex(GetAccessFlags(method)) + ")";
return false;
}
} else if (method != nullptr) {
- LOG(INFO) << "VerifierDeps: Unexpected successful resolution of method "
- << GetMethodDescription(dex_file, entry.GetDexMethodIndex());
+ *error_msg = "Unexpected successful resolution of method "
+ + GetMethodDescription(dex_file, entry.GetDexMethodIndex());
return false;
}
}
return true;
}
+bool VerifierDeps::VerifyInternalClasses(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<dex::TypeIndex>& unverified_classes,
+ Thread* self,
+ /* out */ std::string* error_msg) const {
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+
+ for (ClassAccessor accessor : dex_file.GetClasses()) {
+ std::string descriptor = accessor.GetDescriptor();
+ ObjPtr<mirror::Class> cls = FindClassAndClearException(class_linker,
+ self,
+ descriptor,
+ class_loader);
+ if (UNLIKELY(cls == nullptr)) {
+ // Could not resolve class from the currently verified dex file.
+ // This can happen when the class fails to link. Check if this
+ // expected by looking in the `unverified_classes` set.
+ if (unverified_classes.find(accessor.GetClassDef().class_idx_) == unverified_classes.end()) {
+ *error_msg = "Failed to resolve internal class " + descriptor;
+ return false;
+ }
+ continue;
+ }
+
+ // Check that the class resolved into the same dex file. Otherwise there is
+ // a different class with the same descriptor somewhere in one of the parent
+ // class loaders.
+ if (&cls->GetDexFile() != &dex_file) {
+ *error_msg = "Class " + descriptor + " redefines a class in a parent class loader "
+ + "(dexFile expected=" + accessor.GetDexFile().GetLocation()
+ + ", actual=" + cls->GetDexFile().GetLocation() + ")";
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFileDeps& deps,
- Thread* self) const {
- bool result = VerifyAssignability(
- class_loader, dex_file, deps.assignable_types_, /* expected_assignability= */ true, self);
- result = result && VerifyAssignability(
- class_loader, dex_file, deps.unassignable_types_, /* expected_assignability= */ false, self);
-
- result = result && VerifyClasses(class_loader, dex_file, deps.classes_, self);
- result = result && VerifyFields(class_loader, dex_file, deps.fields_, self);
-
- result = result && VerifyMethods(class_loader, dex_file, deps.methods_, self);
-
- return result;
+ Thread* self,
+ /* out */ std::string* error_msg) const {
+ return VerifyInternalClasses(class_loader,
+ dex_file,
+ deps.unverified_classes_,
+ self,
+ error_msg) &&
+ VerifyAssignability(class_loader,
+ dex_file,
+ deps.assignable_types_,
+ /* expected_assignability= */ true,
+ self,
+ error_msg) &&
+ VerifyAssignability(class_loader,
+ dex_file,
+ deps.unassignable_types_,
+ /* expected_assignability= */ false,
+ self,
+ error_msg) &&
+ VerifyClasses(class_loader, dex_file, deps.classes_, self, error_msg) &&
+ VerifyFields(class_loader, dex_file, deps.fields_, self, error_msg) &&
+ VerifyMethods(class_loader, dex_file, deps.methods_, self, error_msg);
}
} // namespace verifier
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index dfd4a5c..fb4a4bf 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -63,7 +63,7 @@
// Merge `other` into this `VerifierDeps`'. `other` and `this` must be for the
// same set of dex files.
- void MergeWith(const VerifierDeps& other, const std::vector<const DexFile*>& dex_files);
+ void MergeWith(std::unique_ptr<VerifierDeps> other, const std::vector<const DexFile*>& dex_files);
// Record the verification status of the class at `type_idx`.
static void MaybeRecordVerificationStatus(const DexFile& dex_file,
@@ -114,7 +114,9 @@
void Dump(VariableIndentationOutputStream* vios) const;
// Verify the encoded dependencies of this `VerifierDeps` are still valid.
- bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader, Thread* self) const
+ bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader,
+ Thread* self,
+ /* out */ std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);
const std::set<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const {
@@ -287,14 +289,31 @@
bool VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFileDeps& deps,
- Thread* self) const
+ Thread* self,
+ /* out */ std::string* error_msg) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Check that classes which are to be verified using these dependencies
+ // are not eclipsed by classes in parent class loaders, e.g. when vdex was
+ // created against SDK stubs and the app redefines a non-public class on
+ // boot classpath, or simply if a class is added during an OTA. In such cases,
+ // dependencies do not include the dependencies on the presumed-internal class
+ // and verification must fail.
+ // TODO(dbrazdil): Encode a set of redefined classes during full verification.
+ // If such class is found to be redefined at runtime, dependencies remain valid.
+ bool VerifyInternalClasses(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<dex::TypeIndex>& unverified_classes,
+ Thread* self,
+ /* out */ std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);
bool VerifyAssignability(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const std::set<TypeAssignability>& assignables,
bool expected_assignability,
- Thread* self) const
+ Thread* self,
+ /* out */ std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Verify that the set of resolved classes at the point of creation
@@ -302,7 +321,8 @@
bool VerifyClasses(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const std::set<ClassResolution>& classes,
- Thread* self) const
+ Thread* self,
+ /* out */ std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Verify that the set of resolved fields at the point of creation
@@ -311,7 +331,8 @@
bool VerifyFields(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const std::set<FieldResolution>& classes,
- Thread* self) const
+ Thread* self,
+ /* out */ std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::verifier_deps_lock_);
@@ -321,7 +342,8 @@
bool VerifyMethods(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const std::set<MethodResolution>& methods,
- Thread* self) const
+ Thread* self,
+ /* out */ std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Map from DexFiles into dependencies collected from verification of their methods.
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index f61faa3..19fbf63 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -84,6 +84,7 @@
jmethodID WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath;
jmethodID WellKnownClasses::dalvik_system_VMRuntime_runFinalization;
+jmethodID WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed;
jmethodID WellKnownClasses::java_lang_Boolean_valueOf;
jmethodID WellKnownClasses::java_lang_Byte_valueOf;
jmethodID WellKnownClasses::java_lang_Character_valueOf;
@@ -91,6 +92,7 @@
jmethodID WellKnownClasses::java_lang_ClassNotFoundException_init;
jmethodID WellKnownClasses::java_lang_Daemons_start;
jmethodID WellKnownClasses::java_lang_Daemons_stop;
+jmethodID WellKnownClasses::java_lang_Daemons_waitForDaemonStart;
jmethodID WellKnownClasses::java_lang_Double_valueOf;
jmethodID WellKnownClasses::java_lang_Float_valueOf;
jmethodID WellKnownClasses::java_lang_Integer_valueOf;
@@ -131,6 +133,7 @@
jfieldID WellKnownClasses::java_lang_Thread_name;
jfieldID WellKnownClasses::java_lang_Thread_priority;
jfieldID WellKnownClasses::java_lang_Thread_nativePeer;
+jfieldID WellKnownClasses::java_lang_Thread_systemDaemon;
jfieldID WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
jfieldID WellKnownClasses::java_lang_ThreadGroup_groups;
jfieldID WellKnownClasses::java_lang_ThreadGroup_ngroups;
@@ -344,11 +347,13 @@
dalvik_system_BaseDexClassLoader_getLdLibraryPath = CacheMethod(env, dalvik_system_BaseDexClassLoader, false, "getLdLibraryPath", "()Ljava/lang/String;");
dalvik_system_VMRuntime_runFinalization = CacheMethod(env, dalvik_system_VMRuntime, true, "runFinalization", "(J)V");
+ dalvik_system_VMRuntime_hiddenApiUsed = CacheMethod(env, dalvik_system_VMRuntime, true, "hiddenApiUsed", "(Ljava/lang/String;Ljava/lang/String;IZ)V");
java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V");
java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V");
+ java_lang_Daemons_waitForDaemonStart = CacheMethod(env, java_lang_Daemons, true, "waitForDaemonStart", "()V");
java_lang_invoke_MethodHandles_lookup = CacheMethod(env, "java/lang/invoke/MethodHandles", true, "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;");
java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(env, "java/lang/invoke/MethodHandles$Lookup", false, "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;");
@@ -383,6 +388,7 @@
java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;");
java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I");
java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J");
+ java_lang_Thread_systemDaemon = CacheField(env, java_lang_Thread, false, "systemDaemon", "Z");
java_lang_Thread_unparkedBeforeStart = CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z");
java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I");
@@ -486,6 +492,7 @@
dalvik_system_BaseDexClassLoader_getLdLibraryPath = nullptr;
dalvik_system_VMRuntime_runFinalization = nullptr;
+ dalvik_system_VMRuntime_hiddenApiUsed = nullptr;
java_lang_Boolean_valueOf = nullptr;
java_lang_Byte_valueOf = nullptr;
java_lang_Character_valueOf = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index f0e98a8..3c5144f 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -93,6 +93,7 @@
static jmethodID dalvik_system_BaseDexClassLoader_getLdLibraryPath;
static jmethodID dalvik_system_VMRuntime_runFinalization;
+ static jmethodID dalvik_system_VMRuntime_hiddenApiUsed;
static jmethodID java_lang_Boolean_valueOf;
static jmethodID java_lang_Byte_valueOf;
static jmethodID java_lang_Character_valueOf;
@@ -100,6 +101,7 @@
static jmethodID java_lang_ClassNotFoundException_init;
static jmethodID java_lang_Daemons_start;
static jmethodID java_lang_Daemons_stop;
+ static jmethodID java_lang_Daemons_waitForDaemonStart;
static jmethodID java_lang_Double_valueOf;
static jmethodID java_lang_Float_valueOf;
static jmethodID java_lang_Integer_valueOf;
@@ -140,6 +142,7 @@
static jfieldID java_lang_Thread_name;
static jfieldID java_lang_Thread_priority;
static jfieldID java_lang_Thread_nativePeer;
+ static jfieldID java_lang_Thread_systemDaemon;
static jfieldID java_lang_Thread_unparkedBeforeStart;
static jfieldID java_lang_ThreadGroup_groups;
static jfieldID java_lang_ThreadGroup_ngroups;
diff --git a/test/004-StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc
index 81c27ec..6e53f30 100644
--- a/test/004-StackWalk/stack_walk_jni.cc
+++ b/test/004-StackWalk/stack_walk_jni.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <string_view>
+
#include "art_method-inl.h"
#include "check_reference_map_visitor.h"
#include "jni.h"
@@ -38,7 +40,7 @@
return true;
}
ArtMethod* m = GetMethod();
- StringPiece m_name(m->GetName());
+ std::string_view m_name(m->GetName());
// Given the method name and the number of times the method has been called,
// we know the Dex registers with live reference values. Assert that what we
diff --git a/test/005-annotations/expected.txt b/test/005-annotations/expected.txt
index ee5b0c7..b537c8f 100644
--- a/test/005-annotations/expected.txt
+++ b/test/005-annotations/expected.txt
@@ -109,4 +109,4 @@
Get annotation with missing class should not throw
Got expected TypeNotPresentException
-Got expected NoSuchFieldError
+Got expected Error for renamed enum
diff --git a/test/005-annotations/src/android/test/anno/TestAnnotations.java b/test/005-annotations/src/android/test/anno/TestAnnotations.java
index 8ea8e8e..a3e32f9 100644
--- a/test/005-annotations/src/android/test/anno/TestAnnotations.java
+++ b/test/005-annotations/src/android/test/anno/TestAnnotations.java
@@ -17,6 +17,7 @@
package android.test.anno;
import java.lang.annotation.Annotation;
+import java.lang.annotation.AnnotationFormatError;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -241,8 +242,8 @@
Annotation[] annos = m.getDeclaredAnnotations();
System.out.println(" annotations on METH " + m + ":");
}
- } catch (NoSuchFieldError expected) {
- System.out.println("Got expected NoSuchFieldError");
+ } catch (Error expected) {
+ System.out.println("Got expected Error for renamed enum");
}
// Test if annotations marked VISIBILITY_BUILD are visible to runtime in M and earlier.
diff --git a/test/1339-dead-reference-safe/check b/test/1339-dead-reference-safe/check
new file mode 100644
index 0000000..795cfac
--- /dev/null
+++ b/test/1339-dead-reference-safe/check
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+# DeadReferenceSafe result differs for interpreted mode. A real failure
+# will produce an extra line anyway.
+
+diff --ignore-matching-lines="DeadReferenceSafe count:" -q $1 $2
diff --git a/test/1339-dead-reference-safe/expected.txt b/test/1339-dead-reference-safe/expected.txt
new file mode 100644
index 0000000..abafce4
--- /dev/null
+++ b/test/1339-dead-reference-safe/expected.txt
@@ -0,0 +1,6 @@
+JNI_OnLoad called
+DeadReferenceUnsafe count: 5
+DeadReferenceSafe count: N
+ReachabilitySensitive count: 5
+ReachabilitySensitiveFun count: 5
+ReachabilityFence count: 5
diff --git a/test/1339-dead-reference-safe/info.txt b/test/1339-dead-reference-safe/info.txt
new file mode 100644
index 0000000..b6ad217
--- /dev/null
+++ b/test/1339-dead-reference-safe/info.txt
@@ -0,0 +1 @@
+Test that @DeadReferenceSafe and @ReachabilitySensitive have the intended effect.
diff --git a/test/1339-dead-reference-safe/src/DeadReferenceSafeTest.java b/test/1339-dead-reference-safe/src/DeadReferenceSafeTest.java
new file mode 100644
index 0000000..0c19084
--- /dev/null
+++ b/test/1339-dead-reference-safe/src/DeadReferenceSafeTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+import dalvik.annotation.optimization.DeadReferenceSafe;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@DeadReferenceSafe
+public final class DeadReferenceSafeTest {
+ static AtomicInteger nFinalized = new AtomicInteger(0);
+ private static final int INNER_ITERS = 10;
+ static int count;
+ static boolean interpreted;
+ int n = 1;
+
+ private static void $noinline$loop() {
+ DeadReferenceSafeTest x;
+ // The loop allocates INNER_ITERS DeadReferenceSafeTest objects.
+ for (int i = 0; i < INNER_ITERS; ++i) {
+ // We've allocated i objects so far.
+ x = new DeadReferenceSafeTest();
+ count += x.n;
+ // x is dead here.
+ if (i == 5) {
+ // With dead reference elimination, all 6 objects should have been finalized here.
+ // However the interpreter doesn't (yet?) play by the proper rules.
+ Main.$noinline$gcAndCheck(nFinalized, (interpreted ? 5 : 6), "DeadReferenceSafe",
+ "Failed to reclaim dead reference in @DeadReferenceSafe code!");
+ }
+ }
+ }
+
+ private static void reset(int expected_count) {
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ if (nFinalized.get() != expected_count) {
+ System.out.println("DeadReferenceSafeTest: Wrong number of finalized objects:"
+ + nFinalized.get());
+ }
+ nFinalized.set(0);
+ }
+
+ protected void finalize() {
+ nFinalized.incrementAndGet();
+ }
+
+ public static void runTest() {
+ try {
+ interpreted = !Main.ensureCompiled(DeadReferenceSafeTest.class, "$noinline$loop");
+ } catch (NoSuchMethodException e) {
+ System.out.println("Unexpectedly threw " + e);
+ }
+
+ $noinline$loop();
+
+ if (count != INNER_ITERS) {
+ System.out.println("DeadReferenceSafeTest: Final count wrong: " + count);
+ }
+ reset(INNER_ITERS);
+ }
+}
diff --git a/test/1339-dead-reference-safe/src/DeadReferenceUnsafeTest.java b/test/1339-dead-reference-safe/src/DeadReferenceUnsafeTest.java
new file mode 100644
index 0000000..84774da
--- /dev/null
+++ b/test/1339-dead-reference-safe/src/DeadReferenceUnsafeTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public final class DeadReferenceUnsafeTest {
+ static AtomicInteger nFinalized = new AtomicInteger(0);
+ private static final int INNER_ITERS = 10;
+ static int count;
+ int n = 1;
+
+ private static void $noinline$loop() {
+ DeadReferenceUnsafeTest x;
+ // The loop allocates INNER_ITERS DeadReferenceUnsafeTest objects.
+ for (int i = 0; i < INNER_ITERS; ++i) {
+ // We've allocated i objects so far.
+ x = new DeadReferenceUnsafeTest();
+ count += x.n;
+ // x is dead here.
+ if (i == 5) {
+ // Without dead reference elimination, the last object should be kept around,
+ // and only 5 objects should be relcaimed here.
+ Main.$noinline$gcAndCheck(nFinalized, 5, "DeadReferenceUnsafe",
+ "Failed to keep dead reference live in unannotated code!");
+ }
+ }
+ }
+
+ private static void reset(int expected_count) {
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ if (nFinalized.get() != expected_count) {
+ System.out.println("DeadReferenceUnsafeTest: Wrong number of finalized objects:"
+ + nFinalized.get());
+ }
+ nFinalized.set(0);
+ }
+
+ protected void finalize() {
+ nFinalized.incrementAndGet();
+ }
+
+ public static void runTest() {
+ try {
+ Main.ensureCompiled(DeadReferenceUnsafeTest.class, "$noinline$loop");
+ } catch (NoSuchMethodException e) {
+ System.out.println("Unexpectedly threw " + e);
+ }
+
+ $noinline$loop();
+
+ if (count != INNER_ITERS) {
+ System.out.println("DeadReferenceUnsafeTest: Final count wrong: " + count);
+ }
+ reset(INNER_ITERS);
+ }
+}
diff --git a/test/1339-dead-reference-safe/src/Main.java b/test/1339-dead-reference-safe/src/Main.java
new file mode 100644
index 0000000..46b533a
--- /dev/null
+++ b/test/1339-dead-reference-safe/src/Main.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class Main {
+
+ // Ensure that the "loop" method is compiled. Otherwise we currently have no real way to get rid
+ // of dead references. Return true if it looks like we succeeded.
+ public static boolean ensureCompiled(Class cls, String methodName) throws NoSuchMethodException {
+ Method m = cls.getDeclaredMethod(methodName);
+ if (isAotCompiled(cls, methodName)) {
+ return true;
+ } else {
+ ensureMethodJitCompiled(m);
+ if (hasJitCompiledEntrypoint(cls, methodName)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ // Garbage collect and check that the atomic counter has the expected value.
+ // Exped value of -1 means don't care.
+ // Noinline because we don't want the inlining here to interfere with the ReachabilitySensitive
+ // analysis.
+ public static void $noinline$gcAndCheck(AtomicInteger counter, int expected, String label,
+ String msg) {
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ int count = counter.get();
+ System.out.println(label + " count: " + count);
+ if (counter.get() != expected && expected != -1) {
+ System.out.println(msg);
+ }
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ // Run several variations of the same test with different reachability annotations, etc.
+ // Only the DeadReferenceSafeTest should finalize every previously allocated object.
+ DeadReferenceUnsafeTest.runTest();
+ DeadReferenceSafeTest.runTest();
+ ReachabilitySensitiveTest.runTest();
+ ReachabilitySensitiveFunTest.runTest();
+ ReachabilityFenceTest.runTest();
+ }
+ public static native void ensureMethodJitCompiled(Method meth);
+ public static native boolean hasJitCompiledEntrypoint(Class<?> cls, String methodName);
+ public static native boolean isAotCompiled(Class<?> cls, String methodName);
+}
diff --git a/test/1339-dead-reference-safe/src/ReachabilityFenceTest.java b/test/1339-dead-reference-safe/src/ReachabilityFenceTest.java
new file mode 100644
index 0000000..d4befde
--- /dev/null
+++ b/test/1339-dead-reference-safe/src/ReachabilityFenceTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+// DeadReferenceSafeTest, but with a reachabilityFence.
+
+import dalvik.annotation.optimization.DeadReferenceSafe;
+import java.lang.ref.Reference;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@DeadReferenceSafe
+public final class ReachabilityFenceTest {
+ static AtomicInteger nFinalized = new AtomicInteger(0);
+ private static final int INNER_ITERS = 10;
+ static int count;
+ int n = 1;
+
+ private static void $noinline$loop() {
+ ReachabilityFenceTest x;
+ // Each loop allocates INNER_ITERS ReachabilitySenstiveTest objects.
+ for (int i = 0; i < INNER_ITERS; ++i) {
+ // We've allocated i objects so far.
+ x = new ReachabilityFenceTest();
+ count += x.n;
+ // x is dead here.
+ if (i == 5) {
+ // The rechabilityFence should keep the last allocated object reachable.
+ // Thus the last instance should not be finalized.
+ Main.$noinline$gcAndCheck(nFinalized, 5, "ReachabilityFence",
+ "reachabilityFence failed to keep object live.");
+ }
+ Reference.reachabilityFence(x);
+ }
+ }
+
+ private static void reset(int expected_count) {
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ if (nFinalized.get() != expected_count) {
+ System.out.println("ReachabilityFenceTest: Wrong number of finalized objects:"
+ + nFinalized.get());
+ }
+ nFinalized.set(0);
+ }
+
+ protected void finalize() {
+ nFinalized.incrementAndGet();
+ }
+
+ public static void runTest() {
+ try {
+ Main.ensureCompiled(ReachabilityFenceTest.class, "$noinline$loop");
+ } catch (NoSuchMethodException e) {
+ System.out.println("Unexpectedly threw " + e);
+ }
+
+ $noinline$loop();
+
+ if (count != INNER_ITERS) {
+ System.out.println("ReachabilityFenceTest: Final count wrong: " + count);
+ }
+ reset(INNER_ITERS);
+ }
+}
diff --git a/test/1339-dead-reference-safe/src/ReachabilitySensitiveFunTest.java b/test/1339-dead-reference-safe/src/ReachabilitySensitiveFunTest.java
new file mode 100644
index 0000000..2c66146
--- /dev/null
+++ b/test/1339-dead-reference-safe/src/ReachabilitySensitiveFunTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+// DeadReferenceSafeTest, but with a ReachabilitySensitive annotation.
+
+import dalvik.annotation.optimization.DeadReferenceSafe;
+import dalvik.annotation.optimization.ReachabilitySensitive;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@DeadReferenceSafe
+public final class ReachabilitySensitiveFunTest {
+ static AtomicInteger nFinalized = new AtomicInteger(0);
+ private static final int INNER_ITERS = 10;
+ static int count;
+ int n = 1;
+ @ReachabilitySensitive
+ int getN() {
+ return n;
+ }
+
+ private static void $noinline$loop() {
+ ReachabilitySensitiveFunTest x;
+ // The loop allocates INNER_ITERS ReachabilitySensitiveTest objects.
+ for (int i = 0; i < INNER_ITERS; ++i) {
+ // We've allocated i objects so far.
+ x = new ReachabilitySensitiveFunTest();
+ // ReachabilitySensitive reference.
+ count += x.getN();
+ // x is dead here.
+ if (i == 5) {
+ // Since there is a ReachabilitySensitive call, x should be kept live
+ // until it is reassigned. Thus the last instance should not be finalized.
+ Main.$noinline$gcAndCheck(nFinalized, 5, "ReachabilitySensitiveFun",
+ "@ReachabilitySensitive call failed to keep object live.");
+ }
+ }
+ }
+
+ private static void reset(int expected_count) {
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ if (nFinalized.get() != expected_count) {
+ System.out.println("ReachabilitySensitiveFunTest: Wrong number of finalized objects:"
+ + nFinalized.get());
+ }
+ nFinalized.set(0);
+ }
+
+ protected void finalize() {
+ nFinalized.incrementAndGet();
+ }
+
+ public static void runTest() {
+ try {
+ Main.ensureCompiled(ReachabilitySensitiveFunTest.class, "$noinline$loop");
+ } catch (NoSuchMethodException e) {
+ System.out.println("Unexpectedly threw " + e);
+ }
+
+ $noinline$loop();
+
+ if (count != INNER_ITERS) {
+ System.out.println("ReachabilitySensitiveFunTest: Final count wrong: " + count);
+ }
+ reset(INNER_ITERS);
+ }
+}
diff --git a/test/1339-dead-reference-safe/src/ReachabilitySensitiveTest.java b/test/1339-dead-reference-safe/src/ReachabilitySensitiveTest.java
new file mode 100644
index 0000000..aff43b6
--- /dev/null
+++ b/test/1339-dead-reference-safe/src/ReachabilitySensitiveTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+// DeadReferenceSafeTest, but with a ReachabilitySensitive annotation.
+
+import dalvik.annotation.optimization.DeadReferenceSafe;
+import dalvik.annotation.optimization.ReachabilitySensitive;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@DeadReferenceSafe
+public final class ReachabilitySensitiveTest {
+ static AtomicInteger nFinalized = new AtomicInteger(0);
+ private static final int INNER_ITERS = 10;
+ static int count;
+ @ReachabilitySensitive
+ int n = 1;
+
+ private static void $noinline$loop() {
+ ReachabilitySensitiveTest x;
+ // The loop allocates INNER_ITERS ReachabilitySensitiveTest objects.
+ for (int i = 0; i < INNER_ITERS; ++i) {
+ // We've allocated i objects so far.
+ x = new ReachabilitySensitiveTest();
+ // ReachabilitySensitive reference.
+ count += x.n;
+ // x is dead here.
+ if (i == 5) {
+ // Since there is a ReachabilitySensitive reference to x.n, x should be kept live
+ // until it is reassigned. Thus the last instance should not be finalized.
+ Main.$noinline$gcAndCheck(nFinalized, 5, "ReachabilitySensitive",
+ "@ReachabilitySensitive failed to keep object live.");
+ }
+ }
+ }
+
+ private static void reset(int expected_count) {
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ if (nFinalized.get() != expected_count) {
+ System.out.println("ReachabilitySensitiveTest: Wrong number of finalized objects:"
+ + nFinalized.get());
+ }
+ nFinalized.set(0);
+ }
+
+ protected void finalize() {
+ nFinalized.incrementAndGet();
+ }
+
+ public static void runTest() {
+ try {
+ Main.ensureCompiled(ReachabilitySensitiveTest.class, "$noinline$loop");
+ } catch (NoSuchMethodException e) {
+ System.out.println("Unexpectedly threw " + e);
+ }
+
+ $noinline$loop();
+
+ if (count != INNER_ITERS) {
+ System.out.println("ReachabilitySensitiveTest: Final count wrong: " + count);
+ }
+ reset(INNER_ITERS);
+ }
+}
diff --git a/test/622-checker-bce-regressions/src/Main.java b/test/622-checker-bce-regressions/src/Main.java
index 6ba2644..595ade8 100644
--- a/test/622-checker-bce-regressions/src/Main.java
+++ b/test/622-checker-bce-regressions/src/Main.java
@@ -42,8 +42,22 @@
return j;
}
+ static public void $noinline$regressionTest123284765(String str) {
+ try {
+ int l = str.length();
+ if (l == 34) {
+ str.charAt(l);
+ fail();
+ }
+ } catch (StringIndexOutOfBoundsException expected) {
+ expectEquals(34, str.length());
+ }
+ }
+
public static void main(String[] args) {
expectEquals(8, doNotVisitAfterForwardBCE(array));
+ $noinline$regressionTest123284765("0123456789012345678901234567890123");
+ $noinline$regressionTest123284765("012345678901");
System.out.println("passed");
}
@@ -52,4 +66,8 @@
throw new Error("Expected: " + expected + ", found: " + result);
}
}
+
+ private static void fail() {
+ throw new Error("FAIL");
+ }
}
diff --git a/test/690-hiddenapi-same-name-methods/build b/test/690-hiddenapi-same-name-methods/build
new file mode 100644
index 0000000..c364b3b
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/690-hiddenapi-same-name-methods/expected.txt b/test/690-hiddenapi-same-name-methods/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv b/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv
new file mode 100644
index 0000000..001ab80
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv
@@ -0,0 +1,9 @@
+LSpecificClass;->foo()Ljava/lang/Double;,blacklist
+LDirectMethods;->foo()Ljava/lang/Integer;,blacklist
+LDirectMethods;->foo()Ljava/lang/Boolean;,blacklist
+LVirtualMethods;->foo()Ljava/lang/Integer;,blacklist
+LVirtualMethods;->foo()Ljava/lang/Boolean;,blacklist
+LSyntheticMethods;->foo()Ljava/lang/Integer;,blacklist
+LSyntheticMethods;->foo()Ljava/lang/Boolean;,blacklist
+LNonSyntheticMethods;->foo()Ljava/lang/Integer;,blacklist
+LNonSyntheticMethods;->foo()Ljava/lang/Boolean;,blacklist
\ No newline at end of file
diff --git a/test/690-hiddenapi-same-name-methods/info.txt b/test/690-hiddenapi-same-name-methods/info.txt
new file mode 100644
index 0000000..be5b195
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/info.txt
@@ -0,0 +1 @@
+Test that Class::GetDeclaredMethodInternal() takes hidden API into account.
\ No newline at end of file
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali
new file mode 100644
index 0000000..8564976
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali
@@ -0,0 +1,46 @@
+#
+# 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.
+#
+
+.class LDirectMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, non-synthetic method.
+
+# Non-hidden methods
+.method private foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method private foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali
new file mode 100644
index 0000000..f47219f
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali
@@ -0,0 +1,46 @@
+#
+# 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.
+#
+
+.class LNonSyntheticMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, virtual method.
+
+# Non-hidden methods
+.method public foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method public foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali
new file mode 100644
index 0000000..afb4d33
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali
@@ -0,0 +1,46 @@
+#
+# 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.
+#
+
+.class LSyntheticMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, virtual method.
+
+# Non-hidden methods
+.method public synthetic foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method public synthetic foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali
new file mode 100644
index 0000000..fb26c74
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali
@@ -0,0 +1,46 @@
+#
+# 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.
+#
+
+.class LVirtualMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, non-synthetic method.
+
+# Non-hidden methods
+.method public foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method public synthetic foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method public foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method public synthetic foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java b/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java
new file mode 100644
index 0000000..c404402
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java
@@ -0,0 +1,19 @@
+/*
+ * 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 interface GenericInterface<T extends Number> {
+ public T foo();
+}
diff --git a/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java b/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java
new file mode 100644
index 0000000..dd3a835
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java
@@ -0,0 +1,21 @@
+/*
+ * 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 SpecificClass implements GenericInterface<Double> {
+ public Double foo() {
+ return 42.0;
+ }
+}
diff --git a/test/690-hiddenapi-same-name-methods/src/Main.java b/test/690-hiddenapi-same-name-methods/src/Main.java
new file mode 100644
index 0000000..12cfdd7
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src/Main.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+ public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
+ System.loadLibrary(args[0]);
+
+ // Run the initialization routine. This will enable hidden API checks in
+ // the runtime, in case they are not enabled by default.
+ init();
+
+ // Load the '-ex' APK and attach it to the boot class path.
+ appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
+
+ // All test classes contain just methods named "foo" with different return types
+ // and access flags. Check that:
+ // (a) only the non-hidden ones are returned from getDeclaredMethods
+ // (they have return types Number and Double), and
+ // (b) getDeclaredMethod picks virtual/non-synthetic methods over direct/synthetic
+ // (the right one always has return type Number).
+ Class<?> covariantClass = Class.forName(JAVA_CLASS_NAME, true, BOOT_CLASS_LOADER);
+ checkMethodList(covariantClass, /* expectedLength= */ 1);
+ checkMethod(covariantClass);
+
+ String[] classes = new String[] {
+ "VirtualMethods",
+ "DirectMethods",
+ "SyntheticMethods",
+ "NonSyntheticMethods"
+ };
+ for (String className : classes) {
+ Class<?> klass = Class.forName(className, true, BOOT_CLASS_LOADER);
+ checkMethodList(klass, /* expectedLength= */ 2);
+ checkMethod(klass);
+ }
+ }
+
+ private static void checkMethodList(Class<?> klass, int expectedLength) {
+ String className = klass.getName();
+ Method[] methods = klass.getDeclaredMethods();
+ if (methods.length != expectedLength) {
+ throw new RuntimeException(className + ": expected " + expectedLength +
+ " declared method(s), got " + methods.length);
+ }
+ boolean hasNumberReturnType = false;
+ boolean hasDoubleReturnType = false;
+ for (Method method : methods) {
+ if (!METHOD_NAME.equals(method.getName())) {
+ throw new RuntimeException(className + ": expected declared method name: \"" + METHOD_NAME +
+ "\", got: \"" + method.getName() + "\"");
+ }
+ if (Number.class == method.getReturnType()) {
+ hasNumberReturnType = true;
+ } else if (Double.class == method.getReturnType()) {
+ hasDoubleReturnType = true;
+ }
+ }
+ if (methods.length >= 1 && !hasNumberReturnType) {
+ throw new RuntimeException(className + ": expected a method with return type \"Number\"");
+ }
+ if (methods.length >= 2 && !hasDoubleReturnType) {
+ throw new RuntimeException(className + ": expected a method with return type \"Double\"");
+ }
+ }
+
+ private static void checkMethod(Class<?> klass) throws NoSuchMethodException {
+ String className = klass.getName();
+ Method method = klass.getDeclaredMethod(METHOD_NAME);
+ if (!METHOD_NAME.equals(method.getName())) {
+ throw new RuntimeException(className + ": expected declared method name: \"" + METHOD_NAME +
+ "\", got: \"" + method.getName() + "\"");
+ } else if (Number.class != method.getReturnType()) {
+ throw new RuntimeException(className + ": expected method return type: \"Number\", got \"" +
+ method.getReturnType().toString() + "\"");
+ }
+ }
+
+ private static final String DEX_EXTRA = new File(System.getenv("DEX_LOCATION"),
+ "690-hiddenapi-same-name-methods-ex.jar").getAbsolutePath();
+
+ private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+
+ private static final String JAVA_CLASS_NAME = "SpecificClass";
+ private static final String METHOD_NAME = "foo";
+
+ // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
+ private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+ private static native void init();
+}
diff --git a/test/719-dm-verify-redefinition/check b/test/719-dm-verify-redefinition/check
new file mode 100644
index 0000000..9845eee
--- /dev/null
+++ b/test/719-dm-verify-redefinition/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+
+# Search for the redefinition line and remove unnecessary tags.
+sed -e 's/^dex2oat[d]\?\(\|32\|64\)\ W.*\] \(Fast verification failed: Class L[^;]*; redefines a class in a parent class loader\).*/\2/g' "$2" > "$2.tmp1"
+# Remove all other dex2oat/dalvikvm log lines.
+grep -v dex2oat "$2.tmp1" | grep -v dalvikvm >> "$2.tmp2"
+
+./default-check "$1" "$2.tmp2"
diff --git a/test/719-dm-verify-redefinition/expected.txt b/test/719-dm-verify-redefinition/expected.txt
new file mode 100644
index 0000000..e1ab7d1
--- /dev/null
+++ b/test/719-dm-verify-redefinition/expected.txt
@@ -0,0 +1,3 @@
+Fast verification failed: Class Ljava/util/BitSet; redefines a class in a parent class loader
+Hello, world!
+Correct resolution of boot class.
diff --git a/test/719-dm-verify-redefinition/info.txt b/test/719-dm-verify-redefinition/info.txt
new file mode 100644
index 0000000..1229bdb
--- /dev/null
+++ b/test/719-dm-verify-redefinition/info.txt
@@ -0,0 +1,2 @@
+Verifies that the vdex file from a DexMetadata archive is discarded
+if the app redefines boot classes.
diff --git a/test/719-dm-verify-redefinition/run b/test/719-dm-verify-redefinition/run
new file mode 100644
index 0000000..8e568b5
--- /dev/null
+++ b/test/719-dm-verify-redefinition/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+export ANDROID_LOG_TAGS='*:w'
+exec ${RUN} --external-log-tags --dm "${@}"
diff --git a/test/719-dm-verify-redefinition/src/Main.java b/test/719-dm-verify-redefinition/src/Main.java
new file mode 100644
index 0000000..37575b6
--- /dev/null
+++ b/test/719-dm-verify-redefinition/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+import java.util.BitSet;
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ if (BitSet.class.getClassLoader().equals(String.class.getClassLoader())) {
+ System.out.println("Correct resolution of boot class.");
+ } else {
+ System.out.println("Bogus resolution of boot class.");
+ }
+ }
+}
diff --git a/test/719-dm-verify-redefinition/src/java/util/BitSet.java b/test/719-dm-verify-redefinition/src/java/util/BitSet.java
new file mode 100644
index 0000000..5d91fd8
--- /dev/null
+++ b/test/719-dm-verify-redefinition/src/java/util/BitSet.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package java.util;
+
+public class BitSet {
+}
diff --git a/test/905-object-free/expected.txt b/test/905-object-free/expected.txt
index c226df7..dfcd7b6 100644
--- a/test/905-object-free/expected.txt
+++ b/test/905-object-free/expected.txt
@@ -10,4 +10,4 @@
---
[]
---
-Free counts 100000 100000
+Free counts 200000 200000
diff --git a/test/905-object-free/src/art/Test905.java b/test/905-object-free/src/art/Test905.java
index dddd1aa..367da99 100644
--- a/test/905-object-free/src/art/Test905.java
+++ b/test/905-object-free/src/art/Test905.java
@@ -20,6 +20,7 @@
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.function.BiConsumer;
public class Test905 {
// Taken from jdwp tests.
@@ -110,26 +111,59 @@
System.out.println("---");
}
- private static void stressAllocate(int i) {
+ private static void stressAllocate(int i, BiConsumer<Integer, Object> saver) {
Object obj = new Object();
Main.setTag(obj, i);
setTag2(obj, i + 1);
+ saver.accept(i, obj);
}
private static void stress() {
getCollectedTags(0);
getCollectedTags(1);
- // Allocate objects.
- for (int i = 1; i <= 100000; ++i) {
- stressAllocate(i);
+ final int num_obj = 400000;
+ final Object[] saved = new Object[num_obj/2];
+ // Allocate objects, Save every other one. We want to be sure that it's only the deleted objects
+ // that get their tags cleared and non-deleted objects correctly keep track of their tags.
+ for (int i = 1; i <= num_obj; ++i) {
+ stressAllocate(i, (idx, obj) -> {
+ if ((idx.intValue() - 1) % 2 == 0) {
+ saved[(idx.intValue() - 1)/2] = obj;
+ }
+ });
}
gcAndWait();
long[] freedTags1 = getCollectedTags(0);
long[] freedTags2 = getCollectedTags(1);
+ // Sort the freedtags
+ Arrays.sort(freedTags1);
+ Arrays.sort(freedTags2);
+ // Make sure we freed all the ones we expect to and both envs agree on this.
System.out.println("Free counts " + freedTags1.length + " " + freedTags2.length);
for (int i = 0; i < freedTags1.length; ++i) {
if (freedTags1[i] + 1 != freedTags2[i]) {
- System.out.println("Mismatched tags " + freedTags1[i] + " " + freedTags2[i]);
+ System.out.println("Mismatched tags " + (freedTags1[i] + 1) + " " + freedTags2[i]);
+ }
+ }
+ // Make sure the saved-tags aren't present.
+ for (int i = 0; i < saved.length; i++) {
+ // index = (tag - 1)/2 --> (index * 2) + 1 = tag
+ long expectedTag1 = (i * 2) + 1;
+ if (Main.getTag(saved[i]) != expectedTag1) {
+ System.out.println("Saved object has unexpected tag in env 1. Expected "
+ + expectedTag1 + " got " + Main.getTag(saved[i]));
+ }
+ if (getTag2(saved[i]) != 1 + expectedTag1) {
+ System.out.println("Saved object has unexpected tag in env 2. Expected "
+ + (expectedTag1 + 1) + " got " + getTag2(saved[i]));
+ }
+ if (Arrays.binarySearch(freedTags1, expectedTag1) >= 0) {
+ System.out.println("Saved object was marked as deleted in env 1. Object was "
+ + expectedTag1);
+ }
+ if (Arrays.binarySearch(freedTags2, expectedTag1 + 1) >= 0) {
+ System.out.println("Saved object was marked as deleted in env 2. Object was "
+ + (expectedTag1 + 1));
}
}
}
@@ -161,4 +195,5 @@
private static native void enableFreeTracking(boolean enable);
private static native long[] getCollectedTags(int index);
private static native void setTag2(Object o, long tag);
+ private static native long getTag2(Object o);
}
diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc
index bf86c9a..d85d9d3 100644
--- a/test/905-object-free/tracking_free.cc
+++ b/test/905-object-free/tracking_free.cc
@@ -18,6 +18,7 @@
#include <cstdio>
#include <iostream>
+#include <mutex>
#include <vector>
#include "android-base/logging.h"
@@ -33,17 +34,23 @@
namespace art {
namespace Test905ObjectFree {
+// The ObjectFree functions aren't required to be called on any particular thread so use these
+// mutexs to control access to the collected_tags lists.
+std::mutex ct1_mutex;
static std::vector<jlong> collected_tags1;
+std::mutex ct2_mutex;
static std::vector<jlong> collected_tags2;
jvmtiEnv* jvmti_env2;
static void JNICALL ObjectFree1(jvmtiEnv* ti_env, jlong tag) {
+ std::lock_guard<std::mutex> mu(ct1_mutex);
CHECK_EQ(ti_env, jvmti_env);
collected_tags1.push_back(tag);
}
static void JNICALL ObjectFree2(jvmtiEnv* ti_env, jlong tag) {
+ std::lock_guard<std::mutex> mu(ct2_mutex);
CHECK_EQ(ti_env, jvmti_env2);
collected_tags2.push_back(tag);
}
@@ -84,6 +91,7 @@
extern "C" JNIEXPORT jlongArray JNICALL Java_art_Test905_getCollectedTags(
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint index) {
+ std::lock_guard<std::mutex> mu((index == 0) ? ct1_mutex : ct2_mutex);
std::vector<jlong>& tags = (index == 0) ? collected_tags1 : collected_tags2;
jlongArray ret = env->NewLongArray(tags.size());
if (ret == nullptr) {
@@ -96,6 +104,14 @@
return ret;
}
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test905_getTag2(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj) {
+ jlong tag;
+ jvmtiError ret = jvmti_env2->GetTag(obj, &tag);
+ JvmtiErrorToException(env, jvmti_env, ret);
+ return tag;
+}
+
extern "C" JNIEXPORT void JNICALL Java_art_Test905_setTag2(
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject obj, jlong tag) {
jvmtiError ret = jvmti_env2->SetTag(obj, tag);
diff --git a/test/Android.bp b/test/Android.bp
index 467a717..d070d76 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -540,6 +540,6 @@
"art_debug_defaults",
"art_defaults",
],
- header_libs: ["libnativebridge-dummy-headers"],
+ header_libs: ["libnativebridge-headers"],
srcs: ["115-native-bridge/nativebridge.cc"],
}
diff --git a/test/HiddenApi/AbstractPackageClass.java b/test/HiddenApi/AbstractPackageClass.java
new file mode 100644
index 0000000..8f955ca
--- /dev/null
+++ b/test/HiddenApi/AbstractPackageClass.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+abstract class AbstractPackageClass {
+ public void publicMethod2() {}
+}
diff --git a/test/HiddenApi/PackageClass.java b/test/HiddenApi/PackageClass.java
new file mode 100644
index 0000000..eece100
--- /dev/null
+++ b/test/HiddenApi/PackageClass.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+class PackageClass extends AbstractPackageClass implements PublicInterface {
+ public void publicMethod1() {}
+}
diff --git a/test/HiddenApi/PublicInterface.java b/test/HiddenApi/PublicInterface.java
new file mode 100644
index 0000000..77a3709
--- /dev/null
+++ b/test/HiddenApi/PublicInterface.java
@@ -0,0 +1,20 @@
+/*
+ * 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 interface PublicInterface {
+ void publicMethod1();
+ void publicMethod2();
+}
diff --git a/test/HiddenApiStubs/HiddenApi b/test/HiddenApiStubs/HiddenApi
new file mode 100644
index 0000000..6841ab5
--- /dev/null
+++ b/test/HiddenApiStubs/HiddenApi
@@ -0,0 +1,19 @@
+/*
+ * 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 interface PublicInterface {
+ void publicMethod();
+}
diff --git a/test/HiddenApiStubs/PublicInterface.java b/test/HiddenApiStubs/PublicInterface.java
new file mode 100644
index 0000000..77a3709
--- /dev/null
+++ b/test/HiddenApiStubs/PublicInterface.java
@@ -0,0 +1,20 @@
+/*
+ * 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 interface PublicInterface {
+ void publicMethod1();
+ void publicMethod2();
+}
diff --git a/test/VerifierDeps/Iface.smali b/test/VerifierDeps/Iface.smali
index 8607307..1ee2358 100644
--- a/test/VerifierDeps/Iface.smali
+++ b/test/VerifierDeps/Iface.smali
@@ -1,18 +1,16 @@
-# /*
-# * 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.
-# */
+# 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.
.class public abstract interface LIface;
.super Ljava/lang/Object;
diff --git a/test/etc/default-build b/test/etc/default-build
index 5eba804..d203698 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -80,6 +80,12 @@
HAS_JASMIN_MULTIDEX=false
fi
+if [ -d smali-ex ]; then
+ HAS_SMALI_EX=true
+else
+ HAS_SMALI_EX=false
+fi
+
if [ -d src-ex ]; then
HAS_SRC_EX=true
else
@@ -454,13 +460,25 @@
javac_with_bootclasspath -d classes-tmp-for-ex `find src-art -name '*.java'`
src_tmp_for_ex="-cp classes-tmp-for-ex"
fi
- mkdir classes-ex
+ mkdir -p classes-ex
javac_with_bootclasspath -d classes-ex $src_tmp_for_ex `find src-ex -name '*.java'`
fi
if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then
make_dex classes-ex
+fi
+if [ "${HAS_SMALI_EX}" = "true" -a ${NEED_DEX} = "true" ]; then
+ # Compile Smali classes
+ ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes-ex.dex `find smali-ex -name '*.smali'`
+ if [[ ! -s smali_classes-ex.dex ]] ; then
+ fail "${SMALI} produced no output."
+ fi
+ # Merge smali files into classes-ex.dex.
+ make_dexmerge classes-ex.dex smali_classes-ex.dex
+fi
+
+if [[ -f classes-ex.dex ]]; then
# Apply hiddenapi on the dex files if the test has API list file(s).
if [ ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then
make_hiddenapi classes-ex.dex
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 50d5fee..9d79a0b 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -30,7 +30,8 @@
ANDROID_FLAGS=""
GDB=""
GDB_ARGS=""
-GDB_SERVER="gdbserver"
+GDBSERVER_DEVICE="gdbserver"
+GDBSERVER_HOST="gdbserver"
HAVE_IMAGE="y"
HOST="n"
BIONIC="n"
@@ -60,6 +61,8 @@
TIME_OUT_VALUE=1200 # 20 minutes.
fi
USE_GDB="n"
+USE_GDBSERVER="n"
+GDBSERVER_PORT=":5039"
USE_JVM="n"
USE_JVMTI="n"
VERIFY="y" # y=yes,n=no,s=softfail
@@ -267,6 +270,23 @@
DEBUGGER="y"
TIME_OUT="n"
shift
+ elif [ "x$1" = "x--gdbserver-port" ]; then
+ shift
+ GDBSERVER_PORT=$1
+ shift
+ elif [ "x$1" = "x--gdbserver-bin" ]; then
+ shift
+ GDBSERVER_HOST=$1
+ GDBSERVER_DEVICE=$1
+ shift
+ elif [ "x$1" = "x--gdbserver" ]; then
+ USE_GDBSERVER="y"
+ DEV_MODE="y"
+ TIME_OUT="n"
+ HOST="y"
+ ANDROID_ROOT="${ANDROID_HOST_OUT}"
+ ANDROID_RUNTIME_ROOT="${ANDROID_HOST_OUT}/com.android.runtime"
+ shift
elif [ "x$1" = "x--gdb" ]; then
USE_GDB="y"
DEV_MODE="y"
@@ -342,7 +362,7 @@
break
elif [ "x$1" = "x--64" ]; then
ISA="x86_64"
- GDB_SERVER="gdbserver64"
+ GDBSERVER_DEVICE="gdbserver64"
DALVIKVM="dalvikvm64"
LIBRARY_DIRECTORY="lib64"
TEST_DIRECTORY="nativetest64"
@@ -611,8 +631,11 @@
if [ "$USE_GDB" = "y" ]; then
- if [ "$HOST" = "n" ]; then
- GDB="$GDB_SERVER :5039"
+ if [ "$USE_GDBSERVER" = "y" ]; then
+ echo "Cannot pass both --gdb and --gdbserver at the same time!" >&2
+ exit 1
+ elif [ "$HOST" = "n" ]; then
+ GDB="$GDBSERVER_DEVICE $GDBSERVER_PORT"
else
if [ `uname` = "Darwin" ]; then
GDB=lldb
@@ -625,6 +648,12 @@
# gdbargs="--annotate=3 $gdbargs"
fi
fi
+elif [ "$USE_GDBSERVER" = "y" ]; then
+ if [ "$HOST" = "n" ]; then
+ echo "Cannot use --gdbserver in non-host configs" >&2
+ exit 1
+ fi
+ GDB="$GDBSERVER_HOST $GDBSERVER_PORT"
fi
if [ "$INTERPRETER" = "y" ]; then
@@ -761,8 +790,8 @@
if [ "$DEV_MODE" = "y" ]; then
zip_options=""
fi
- setupapex_cmdline="unzip -u ${zip_options} ${ZIPAPEX_LOC} apex_payload.zip -d ${DEX_LOCATION}"
- installapex_cmdline="unzip -u ${zip_options} ${DEX_LOCATION}/apex_payload.zip -d ${DEX_LOCATION}/zipapex"
+ setupapex_cmdline="unzip -o -u ${zip_options} ${ZIPAPEX_LOC} apex_payload.zip -d ${DEX_LOCATION}"
+ installapex_cmdline="unzip -o -u ${zip_options} ${DEX_LOCATION}/apex_payload.zip -d ${DEX_LOCATION}/zipapex"
BIN_DIR=$DEX_LOCATION/zipapex/bin
fi
@@ -1005,12 +1034,14 @@
export ANDROID_DATA="$DEX_LOCATION"
export ANDROID_ROOT="${ANDROID_ROOT}"
export ANDROID_RUNTIME_ROOT="${ANDROID_RUNTIME_ROOT}"
- export LD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
if [ "$USE_ZIPAPEX" = "y" ]; then
# Put the zipapex files in front of the ld-library-path
- export LD_LIBRARY_PATH="${ANDROID_DATA}/zipapex/${LIBRARY_DIRECTORY}:${LD_LIBRARY_PATH}"
+ export LD_LIBRARY_PATH="${ANDROID_DATA}/zipapex/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
+ export DYLD_LIBRARY_PATH="${ANDROID_DATA}/zipapex/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
+ else
+ export LD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
+ export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
fi
- export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
export PATH="$PATH:$BIN_DIR"
# Temporarily disable address space layout randomization (ASLR).
@@ -1066,7 +1097,7 @@
mkdir -p ${mkdir_locations} || exit 1
$setupapex_cmdline || { echo "zipapex extraction failed." >&2 ; exit 2; }
- $installapex_cmdline || { echo "zipapex install failed." >&2 ; exit 2; }
+ $installapex_cmdline || { echo "zipapex install failed. cmd was: ${installapex_cmdline}." >&2; find ${mkdir_locations} -type f >&2; exit 2; }
$linkroot_cmdline || { echo "create symlink android-root failed." >&2 ; exit 2; }
$linkroot_overlay_cmdline || { echo "overlay android-root failed." >&2 ; exit 2; }
$profman_cmdline || { echo "Profman failed." >&2 ; exit 2; }
@@ -1082,7 +1113,10 @@
if [ "$USE_GDB" = "y" ]; then
# When running under gdb, we cannot do piping and grepping...
- echo "Run 'gdbclient.py -p <pid printed below>' to debug."
+ $cmdline "$@"
+ elif [ "$USE_GDBSERVER" = "y" ]; then
+ echo "Connect to $GDBSERVER_PORT"
+ # When running under gdb, we cannot do piping and grepping...
$cmdline "$@"
else
if [ "$TIME_OUT" != "gdb" ]; then
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 9c01ba9..cf6e69c 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -534,8 +534,9 @@
"097-duplicate-method",
"138-duplicate-classes-check2",
"159-app-image-fields",
- "674-hiddenapi",
"649-vdex-duplicate-method",
+ "674-hiddenapi",
+ "690-hiddenapi-same-name-methods",
"804-class-extends-itself",
"921-hello-failure",
"999-redefine-hiddenapi"
@@ -555,6 +556,7 @@
"629-vdex-speed",
"647-jni-get-field-id",
"674-hiddenapi",
+ "690-hiddenapi-same-name-methods",
"944-transform-classloaders",
"999-redefine-hiddenapi"
],
@@ -1085,9 +1087,11 @@
"678-quickening",
"679-locks",
"688-shared-library",
+ "690-hiddenapi-same-name-methods",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1001-app-image-regions",
+ "1339-dead-reference-safe",
"1951-monitor-enter-no-suspend",
"1957-error-ext"],
"variant": "jvm",
@@ -1172,9 +1176,22 @@
"description": ["Tests are expected to fail with baseline."]
},
{
+ "tests": ["1339-dead-reference-safe"],
+ "variant": "debuggable",
+ "description": [ "Fails to eliminate dead reference when debuggable." ]
+ },
+ {
"tests": ["708-jit-cache-churn"],
"variant": "jit-on-first-use",
"bug": "b/120112467",
"description": [ "Fails on Android Build hosts with uncaught std::bad_alloc." ]
+ },
+ {
+ "tests": ["719-dm-verify-redefinition"],
+ "variant": "jvm | speed-profile | interp-ac | target | no-prebuild",
+ "description": ["Doesn't run on RI because of boot class redefintion.",
+ "Doesn't work with profiles because the run-test is not setup to",
+ "support both. It also needs full verification, so no interp-ac.",
+ "Requires zip, which isn't available on device"]
}
]
diff --git a/test/run-test b/test/run-test
index 67bcce7..5f78d17 100755
--- a/test/run-test
+++ b/test/run-test
@@ -306,6 +306,18 @@
run_args="${run_args} --gdb"
dev_mode="yes"
shift
+ elif [ "x$1" = "x--gdbserver-bin" ]; then
+ shift
+ run_args="${run_args} --gdbserver-bin $1"
+ shift
+ elif [ "x$1" = "x--gdbserver-port" ]; then
+ shift
+ run_args="${run_args} --gdbserver-port $1"
+ shift
+ elif [ "x$1" = "x--gdbserver" ]; then
+ run_args="${run_args} --gdbserver"
+ dev_mode="yes"
+ shift
elif [ "x$1" = "x--strace" ]; then
strace="yes"
run_args="${run_args} --timeout 1800 --invoke-with strace --invoke-with -o --invoke-with $tmp_dir/$strace_output"
@@ -716,7 +728,12 @@
echo " --with-agent <agent> Run the test with the given agent loaded with -agentpath:"
echo " --debuggable Whether to compile Java code for a debugger."
echo " --gdb Run under gdb; incompatible with some tests."
- echo " --gdb-arg Pass an option to gdb."
+ echo " --gdbserver Start gdbserver (defaults to port :5039)."
+ echo " --gdbserver-port <port>"
+ echo " Start gdbserver with the given COMM (see man gdbserver)."
+ echo " --gdbserver-bin <binary>"
+ echo " Use the given binary as gdbserver."
+ echo " --gdb-arg Pass an option to gdb or gdbserver."
echo " --build-only Build test files only (off by default)."
echo " --interpreter Enable interpreter only mode (off by default)."
echo " --jit Enable jit (off by default)."
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 139d1af..19f03c3 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -28,6 +28,7 @@
import argparse
import os
+import pathlib
import subprocess
import sys
@@ -108,7 +109,10 @@
run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
'art/test/testrunner/testrunner.py')]
test_flags = target.get('run-test', [])
- run_test_command += list(map(lambda a: a.format(SOONG_OUT_DIR=env.SOONG_OUT_DIR), test_flags))
+ out_dir = pathlib.PurePath(env.SOONG_OUT_DIR)
+ if not out_dir.is_absolute():
+ out_dir = pathlib.PurePath(env.ANDROID_BUILD_TOP).joinpath(out_dir)
+ run_test_command += list(map(lambda a: a.format(SOONG_OUT_DIR=str(out_dir)), test_flags))
# Let testrunner compute concurrency based on #cpus.
# b/65822340
# run_test_command += ['-j', str(n_threads)]
diff --git a/tools/art_verifier/art_verifier.cc b/tools/art_verifier/art_verifier.cc
index 0ef6c06..b4aa678 100644
--- a/tools/art_verifier/art_verifier.cc
+++ b/tools/art_verifier/art_verifier.cc
@@ -92,28 +92,32 @@
protected:
using Base = CmdlineArgs;
- ParseStatus ParseCustom(const StringPiece& option, std::string* error_msg) override {
+ ParseStatus ParseCustom(const char* raw_option,
+ size_t raw_option_length,
+ std::string* error_msg) override {
+ DCHECK_EQ(strlen(raw_option), raw_option_length);
{
- ParseStatus base_parse = Base::ParseCustom(option, error_msg);
+ ParseStatus base_parse = Base::ParseCustom(raw_option, raw_option_length, error_msg);
if (base_parse != kParseUnknownArgument) {
return base_parse;
}
}
- if (option.starts_with("--dex-file=")) {
- dex_filename_ = option.substr(strlen("--dex-file=")).data();
+ std::string_view option(raw_option, raw_option_length);
+ if (StartsWith(option, "--dex-file=")) {
+ dex_filename_ = raw_option + strlen("--dex-file=");
} else if (option == "--dex-file-verifier") {
dex_file_verifier_ = true;
} else if (option == "--verbose") {
method_verifier_verbose_ = true;
} else if (option == "--verbose-debug") {
method_verifier_verbose_debug_ = true;
- } else if (option.starts_with("--repetitions=")) {
+ } else if (StartsWith(option, "--repetitions=")) {
char* end;
- repetitions_ = strtoul(option.substr(strlen("--repetitions=")).data(), &end, 10);
- } else if (option.starts_with("--api-level=")) {
+ repetitions_ = strtoul(raw_option + strlen("--repetitions="), &end, 10);
+ } else if (StartsWith(option, "--api-level=")) {
char* end;
- api_level_ = strtoul(option.substr(strlen("--api-level=")).data(), &end, 10);
+ api_level_ = strtoul(raw_option + strlen("--api-level="), &end, 10);
} else {
return kParseUnknownArgument;
}
diff --git a/tools/build_linux_bionic.sh b/tools/build_linux_bionic.sh
index d3c1912..b401071 100755
--- a/tools/build_linux_bionic.sh
+++ b/tools/build_linux_bionic.sh
@@ -35,14 +35,22 @@
# Soong needs a bunch of variables set and will not run if they are missing.
# The default values of these variables is only contained in make, so use
# nothing to create the variables then remove all the other artifacts.
-build/soong/soong_ui.bash --make-mode nothing
+
+# TODO(b/123645297) Move hiddenapi steps to soong.
+#
+# Currently hiddenapi relies on .mk to build some of it's configuration files.
+# This prevents us from just cleaning using soong and forces us to do this
+# hacky workaround where we build the targets without linux_bionic and delete
+# the build-config files before going around again. If we fix this issue we can
+# change to only building 'nothing' instead.
+build/soong/soong_ui.bash --make-mode "$@"
+
if [ $? != 0 ]; then
exit 1
fi
out_dir=$(get_build_var OUT_DIR)
host_out=$(get_build_var HOST_OUT)
-mk_product_out=$(get_build_var PRODUCT_OUT)
# TODO(b/31559095) Figure out a better way to do this.
#
@@ -51,14 +59,17 @@
tmp_soong_var=$(mktemp --tmpdir soong.variables.bak.XXXXXX)
cat $out_dir/soong/soong.variables > ${tmp_soong_var}
-build/soong/soong_ui.bash --make-mode clean
-mkdir -p $out_dir/soong
-mkdir -p $mk_product_out
-# TODO(b/31559095) Soong will panic if this file isn't present. It contains
-# information from MAKE needed to let soong handle the invocation of dex2oat.
-# This would be great to have but for now isn't needed.
-echo "{}" > $mk_product_out/dexpreopt.config
+# See comment above about b/123645297 for why we cannot just do m clean. Clear
+# out all files except for intermediates and installed files.
+find $out_dir/ -maxdepth 1 -mindepth 1 \
+ -not -name soong \
+ -not -name host \
+ -not -name target | xargs -I '{}' rm -rf '{}'
+find $out_dir/soong/ -maxdepth 1 -mindepth 1 \
+ -not -name .intermediates \
+ -not -name host \
+ -not -name target | xargs -I '{}' rm -rf '{}'
python3 <<END - ${tmp_soong_var} ${out_dir}/soong/soong.variables
import json
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 755104b..e63e6f1 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -77,6 +77,7 @@
make_command+=" debuggerd su"
make_command+=" libstdc++ "
make_command+=" ${ANDROID_PRODUCT_OUT#"${ANDROID_BUILD_TOP}/"}/system/etc/public.libraries.txt"
+ make_command+=" standalone-apex-files"
if [[ -n "$ART_TEST_CHROOT" ]]; then
# These targets are needed for the chroot environment.
make_command+=" crash_dump event-log-tags"
diff --git a/tools/dexanalyze/Android.bp b/tools/dexanalyze/Android.bp
index a85bf56..a232a1b 100644
--- a/tools/dexanalyze/Android.bp
+++ b/tools/dexanalyze/Android.bp
@@ -24,11 +24,6 @@
"dexanalyze_experiments.cc",
"dexanalyze_strings.cc",
],
- target: {
- android: {
- shared_libs: ["libcutils"],
- },
- },
header_libs: [
"art_cmdlineparser_headers",
],
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index cde3d0a..3390a99 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -18,6 +18,8 @@
#include <iostream>
#include <map>
#include <set>
+#include <string>
+#include <string_view>
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
@@ -27,6 +29,7 @@
#include "base/mem_map.h"
#include "base/os.h"
#include "base/stl_util.h"
+#include "base/string_view_cpp20.h"
#include "base/unix_file/fd_file.h"
#include "dex/art_dex_file_loader.h"
#include "dex/class_accessor-inl.h"
@@ -110,12 +113,12 @@
bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); }
- std::string GetSuperclassDescriptor() const {
+ std::string_view GetSuperclassDescriptor() const {
return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : "";
}
- std::set<std::string> GetInterfaceDescriptors() const {
- std::set<std::string> list;
+ std::set<std::string_view> GetInterfaceDescriptors() const {
+ std::set<std::string_view> list;
const dex::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef());
for (uint32_t i = 0; ifaces != nullptr && i < ifaces->Size(); ++i) {
list.insert(dex_file_.StringByTypeIdx(ifaces->GetTypeItem(i).type_idx_));
@@ -124,6 +127,7 @@
}
inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
+ inline bool IsInterface() const { return HasAccessFlags(kAccInterface); }
inline bool Equals(const DexClass& other) const {
bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
@@ -140,7 +144,7 @@
uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; }
bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
- static std::string JoinStringSet(const std::set<std::string>& s) {
+ static std::string JoinStringSet(const std::set<std::string_view>& s) {
return "{" + ::android::base::Join(std::vector<std::string>(s.begin(), s.end()), ",") + "}";
}
};
@@ -193,7 +197,7 @@
inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); }
inline uint32_t HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
- inline std::string GetName() const {
+ inline std::string_view GetName() const {
return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId())
: item_.GetDexFile().GetFieldName(GetFieldId());
}
@@ -332,13 +336,13 @@
// See comment on Hierarchy::ForEachResolvableMember.
template<typename Fn>
bool ForEachResolvableMember(const DexMember& other, Fn fn) {
- return ForEachResolvableMember_Impl(other, fn) != ResolutionResult::kNotFound;
+ std::vector<HierarchyClass*> visited;
+ return ForEachResolvableMember_Impl(other, fn, true, true, visited);
}
// Returns true if this class contains at least one member matching `other`.
bool HasMatchingMember(const DexMember& other) {
- return ForEachMatchingMember(
- other, [](const DexMember&) { return true; }) != ResolutionResult::kNotFound;
+ return ForEachMatchingMember(other, [](const DexMember&) { return true; });
}
// Recursively iterates over all subclasses of this class and invokes `fn`
@@ -354,62 +358,60 @@
}
private:
- // Result of resolution which takes into account whether the member was found
- // for the first time or not. This is just a performance optimization to prevent
- // re-visiting previously visited members.
- // Note that order matters. When accumulating results, we always pick the maximum.
- enum class ResolutionResult {
- kNotFound,
- kFoundOld,
- kFoundNew,
- };
-
- inline ResolutionResult Accumulate(ResolutionResult a, ResolutionResult b) {
- return static_cast<ResolutionResult>(
- std::max(static_cast<unsigned>(a), static_cast<unsigned>(b)));
- }
-
template<typename Fn>
- ResolutionResult ForEachResolvableMember_Impl(const DexMember& other, Fn fn) {
- // First try to find a member matching `other` in this class.
- ResolutionResult foundInClass = ForEachMatchingMember(other, fn);
-
- switch (foundInClass) {
- case ResolutionResult::kFoundOld:
- // A matching member was found and previously explored. All subclasses
- // must have been explored too.
- break;
-
- case ResolutionResult::kFoundNew:
- // A matching member was found and this was the first time it was visited.
- // If it is a virtual method, visit all methods overriding/implementing it too.
- if (other.IsVirtualMethod()) {
- for (HierarchyClass* subclass : extended_by_) {
- subclass->ForEachOverridingMember(other, fn);
- }
- }
- break;
-
- case ResolutionResult::kNotFound:
- // A matching member was not found in this class. Explore the superclasses
- // and implemented interfaces.
- for (HierarchyClass* superclass : extends_) {
- foundInClass = Accumulate(
- foundInClass, superclass->ForEachResolvableMember_Impl(other, fn));
- }
- break;
+ bool ForEachResolvableMember_Impl(const DexMember& other,
+ Fn fn,
+ bool allow_explore_up,
+ bool allow_explore_down,
+ std::vector<HierarchyClass*> visited) {
+ if (std::find(visited.begin(), visited.end(), this) == visited.end()) {
+ visited.push_back(this);
+ } else {
+ return false;
}
- return foundInClass;
+ // First try to find a member matching `other` in this class.
+ bool found = ForEachMatchingMember(other, fn);
+
+ // If not found, see if it is inherited from parents. Note that this will not
+ // revisit parents already in `visited`.
+ if (!found && allow_explore_up) {
+ for (HierarchyClass* superclass : extends_) {
+ found |= superclass->ForEachResolvableMember_Impl(
+ other,
+ fn,
+ /* allow_explore_up */ true,
+ /* allow_explore_down */ false,
+ visited);
+ }
+ }
+
+ // If this is a virtual method, continue exploring into subclasses so as to visit
+ // all overriding methods. Allow subclasses to explore their superclasses if this
+ // is an interface. This is needed to find implementations of this interface's
+ // methods inherited from superclasses (b/122551864).
+ if (allow_explore_down && other.IsVirtualMethod()) {
+ for (HierarchyClass* subclass : extended_by_) {
+ subclass->ForEachResolvableMember_Impl(
+ other,
+ fn,
+ /* allow_explore_up */ GetOneDexClass().IsInterface(),
+ /* allow_explore_down */ true,
+ visited);
+ }
+ }
+
+ return found;
}
template<typename Fn>
- ResolutionResult ForEachMatchingMember(const DexMember& other, Fn fn) {
- ResolutionResult found = ResolutionResult::kNotFound;
+ bool ForEachMatchingMember(const DexMember& other, Fn fn) {
+ bool found = false;
auto compare_member = [&](const DexMember& member) {
+ // TODO(dbrazdil): Check whether class of `other` can access `member`.
if (member == other) {
- found = Accumulate(found, fn(member) ? ResolutionResult::kFoundNew
- : ResolutionResult::kFoundOld);
+ found = true;
+ fn(member);
}
};
for (const DexClass& dex_class : dex_classes_) {
@@ -423,20 +425,6 @@
return found;
}
- template<typename Fn>
- void ForEachOverridingMember(const DexMember& other, Fn fn) {
- CHECK(other.IsVirtualMethod());
- ResolutionResult found = ForEachMatchingMember(other, fn);
- if (found == ResolutionResult::kFoundOld) {
- // No need to explore further.
- return;
- } else {
- for (HierarchyClass* subclass : extended_by_) {
- subclass->ForEachOverridingMember(other, fn);
- }
- }
- }
-
// DexClass entries of this class found across all the provided dex files.
std::vector<DexClass> dex_classes_;
@@ -508,7 +496,7 @@
}
private:
- HierarchyClass* FindClass(const std::string& descriptor) {
+ HierarchyClass* FindClass(const std::string_view& descriptor) {
auto it = classes_.find(descriptor);
if (it == classes_.end()) {
return nullptr;
@@ -539,7 +527,7 @@
CHECK(superclass != nullptr);
klass.AddExtends(*superclass);
- for (const std::string& iface_desc : dex_klass.GetInterfaceDescriptors()) {
+ for (const std::string_view& iface_desc : dex_klass.GetInterfaceDescriptors()) {
HierarchyClass* iface = FindClass(iface_desc);
CHECK(iface != nullptr);
klass.AddExtends(*iface);
@@ -548,7 +536,7 @@
}
ClassPath& classpath_;
- std::map<std::string, HierarchyClass> classes_;
+ std::map<std::string_view, HierarchyClass> classes_;
};
// Builder of dex section containing hiddenapi flags.
@@ -890,45 +878,48 @@
argc--;
if (argc > 0) {
- const StringPiece command(argv[0]);
+ const char* raw_command = argv[0];
+ const std::string_view command(raw_command);
if (command == "encode") {
for (int i = 1; i < argc; ++i) {
- const StringPiece option(argv[i]);
- if (option.starts_with("--input-dex=")) {
- boot_dex_paths_.push_back(option.substr(strlen("--input-dex=")).ToString());
- } else if (option.starts_with("--output-dex=")) {
- output_dex_paths_.push_back(option.substr(strlen("--output-dex=")).ToString());
- } else if (option.starts_with("--api-flags=")) {
- api_flags_path_ = option.substr(strlen("--api-flags=")).ToString();
+ const char* raw_option = argv[i];
+ const std::string_view option(raw_option);
+ if (StartsWith(option, "--input-dex=")) {
+ boot_dex_paths_.push_back(std::string(option.substr(strlen("--input-dex="))));
+ } else if (StartsWith(option, "--output-dex=")) {
+ output_dex_paths_.push_back(std::string(option.substr(strlen("--output-dex="))));
+ } else if (StartsWith(option, "--api-flags=")) {
+ api_flags_path_ = std::string(option.substr(strlen("--api-flags=")));
} else if (option == "--no-force-assign-all") {
force_assign_all_ = false;
} else {
- Usage("Unknown argument '%s'", option.data());
+ Usage("Unknown argument '%s'", raw_option);
}
}
return Command::kEncode;
} else if (command == "list") {
for (int i = 1; i < argc; ++i) {
- const StringPiece option(argv[i]);
- if (option.starts_with("--boot-dex=")) {
- boot_dex_paths_.push_back(option.substr(strlen("--boot-dex=")).ToString());
- } else if (option.starts_with("--public-stub-classpath=")) {
+ const char* raw_option = argv[i];
+ const std::string_view option(raw_option);
+ if (StartsWith(option, "--boot-dex=")) {
+ boot_dex_paths_.push_back(std::string(option.substr(strlen("--boot-dex="))));
+ } else if (StartsWith(option, "--public-stub-classpath=")) {
stub_classpaths_.push_back(std::make_pair(
- option.substr(strlen("--public-stub-classpath=")).ToString(),
+ std::string(option.substr(strlen("--public-stub-classpath="))),
ApiList::Whitelist()));
- } else if (option.starts_with("--core-platform-stub-classpath=")) {
+ } else if (StartsWith(option, "--core-platform-stub-classpath=")) {
stub_classpaths_.push_back(std::make_pair(
- option.substr(strlen("--core-platform-stub-classpath=")).ToString(),
+ std::string(option.substr(strlen("--core-platform-stub-classpath="))),
ApiList::CorePlatformApi()));
- } else if (option.starts_with("--out-api-flags=")) {
- api_flags_path_ = option.substr(strlen("--out-api-flags=")).ToString();
+ } else if (StartsWith(option, "--out-api-flags=")) {
+ api_flags_path_ = std::string(option.substr(strlen("--out-api-flags=")));
} else {
- Usage("Unknown argument '%s'", option.data());
+ Usage("Unknown argument '%s'", raw_option);
}
}
return Command::kList;
} else {
- Usage("Unknown command '%s'", command.data());
+ Usage("Unknown command '%s'", raw_command);
}
} else {
Usage("No command specified");
@@ -1058,12 +1049,7 @@
std::string entry = boot_member.GetApiEntry();
auto it = boot_members.find(entry);
CHECK(it != boot_members.end());
- if (it->second.Contains(stub_api_list)) {
- return false; // has been marked before
- } else {
- it->second |= stub_api_list;
- return true; // marked for the first time
- }
+ it->second |= stub_api_list;
});
if (!resolved) {
unresolved.insert(stub_member.GetApiEntry());
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index 7ef5b3d..74feb8a 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -16,6 +16,8 @@
#include <fstream>
+#include "android-base/strings.h"
+
#include "base/unix_file/fd_file.h"
#include "base/zip_archive.h"
#include "common_runtime_test.h"
@@ -41,9 +43,9 @@
return file_path;
}
- std::unique_ptr<const DexFile> RunHiddenApi(const ScratchFile& flags_csv,
- const std::vector<std::string>& extra_args,
- ScratchFile* out_dex) {
+ std::unique_ptr<const DexFile> RunHiddenapiEncode(const ScratchFile& flags_csv,
+ const std::vector<std::string>& extra_args,
+ const ScratchFile& out_dex) {
std::string error;
ScratchFile in_dex;
std::unique_ptr<ZipArchive> jar(
@@ -68,18 +70,42 @@
argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
argv_str.push_back("encode");
argv_str.push_back("--input-dex=" + in_dex.GetFilename());
- argv_str.push_back("--output-dex=" + out_dex->GetFilename());
+ argv_str.push_back("--output-dex=" + out_dex.GetFilename());
argv_str.push_back("--api-flags=" + flags_csv.GetFilename());
argv_str.push_back("--no-force-assign-all");
int return_code = ExecAndReturnCode(argv_str, &error);
if (return_code == 0) {
- return OpenDex(*out_dex);
+ return OpenDex(out_dex);
} else {
LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code;
return nullptr;
}
}
+ bool RunHiddenapiList(const ScratchFile& out_flags_csv) {
+ std::string error;
+ std::string boot_jar = GetTestDexFileName("HiddenApi");
+ std::string stub_jar = GetTestDexFileName("HiddenApiStubs");
+ std::string boot_cp = android::base::Join(GetLibCoreDexFileNames(), ":");
+
+ std::vector<std::string> argv_str;
+ argv_str.push_back(GetHiddenApiCmd());
+ argv_str.push_back("list");
+ for (const std::string& core_jar : GetLibCoreDexFileNames()) {
+ argv_str.push_back("--boot-dex=" + core_jar);
+ }
+ argv_str.push_back("--boot-dex=" + boot_jar);
+ argv_str.push_back("--public-stub-classpath=" + boot_cp + ":" + stub_jar);
+ argv_str.push_back("--out-api-flags=" + out_flags_csv.GetFilename());
+ int return_code = ExecAndReturnCode(argv_str, &error);
+ if (return_code == 0) {
+ return true;
+ } else {
+ LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code;
+ return false;
+ }
+ }
+
std::unique_ptr<const DexFile> OpenDex(const ScratchFile& file) {
ArtDexFileLoader dex_loader;
std::string error_msg;
@@ -113,6 +139,31 @@
return ofs;
}
+ std::map<std::string, std::string> ReadFlagsCsvFile(const ScratchFile& file) {
+ std::ifstream ifs(file.GetFilename());
+ std::map<std::string, std::string> flags;
+
+ for (std::string line; std::getline(ifs, line);) {
+ std::size_t comma = line.find(",");
+ if (comma == std::string::npos) {
+ flags.emplace(line, "");
+ } else {
+ flags.emplace(line.substr(0, comma), line.substr(comma + 1));
+ }
+ }
+
+ return flags;
+ }
+
+ std::string SafeMapGet(const std::string& key, const std::map<std::string, std::string>& map) {
+ auto it = map.find(key);
+ if (it == map.end()) {
+ LOG(FATAL) << "Key not found: " << key;
+ UNREACHABLE();
+ }
+ return it->second;
+ }
+
const dex::ClassDef& FindClass(const char* desc, const DexFile& dex_file) {
const dex::TypeId* type_id = dex_file.FindTypeId(desc);
CHECK(type_id != nullptr) << "Could not find class " << desc;
@@ -220,7 +271,7 @@
<< "LMain;->ifield:LBadType1;,greylist" << std::endl
<< "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->ifield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIFieldHiddenFlags(*dex_file));
}
@@ -231,7 +282,7 @@
<< "LMain;->ifield:I,greylist" << std::endl
<< "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->ifield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file));
}
@@ -242,7 +293,7 @@
<< "LMain;->ifield:LBadType1;,greylist" << std::endl
<< "LMain;->ifield:I,greylist-max-o" << std::endl
<< "LMain;->ifield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIFieldHiddenFlags(*dex_file));
}
@@ -253,7 +304,7 @@
<< "LMain;->ifield:LBadType1;,greylist" << std::endl
<< "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->ifield:I,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIFieldHiddenFlags(*dex_file));
}
@@ -263,7 +314,7 @@
OpenStream(flags_csv)
<< "LMain;->ifield:LBadType1;,greylist" << std::endl
<< "LMain;->ifield:I,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -272,7 +323,7 @@
OpenStream(flags_csv)
<< "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->ifield:I,blacklist,greylist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -281,7 +332,7 @@
OpenStream(flags_csv)
<< "LMain;->ifield:I,greylist,greylist-max-o" << std::endl
<< "LMain;->ifield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -291,7 +342,7 @@
<< "LMain;->sfield:LBadType1;,greylist" << std::endl
<< "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->sfield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSFieldHiddenFlags(*dex_file));
}
@@ -302,7 +353,7 @@
<< "LMain;->sfield:Ljava/lang/Object;,greylist" << std::endl
<< "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->sfield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSFieldHiddenFlags(*dex_file));
}
@@ -313,7 +364,7 @@
<< "LMain;->sfield:LBadType1;,greylist" << std::endl
<< "LMain;->sfield:Ljava/lang/Object;,greylist-max-o" << std::endl
<< "LMain;->sfield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSFieldHiddenFlags(*dex_file));
}
@@ -324,7 +375,7 @@
<< "LMain;->sfield:LBadType1;,greylist" << std::endl
<< "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->sfield:Ljava/lang/Object;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSFieldHiddenFlags(*dex_file));
}
@@ -334,7 +385,7 @@
OpenStream(flags_csv)
<< "LMain;->sfield:LBadType1;,greylist" << std::endl
<< "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -343,7 +394,7 @@
OpenStream(flags_csv)
<< "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -352,7 +403,7 @@
OpenStream(flags_csv)
<< "LMain;->sfield:Ljava/lang/Object;,greylist,greylist-max-o" << std::endl
<< "LMain;->sfield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -362,7 +413,7 @@
<< "LMain;->imethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIMethodHiddenFlags(*dex_file));
}
@@ -373,7 +424,7 @@
<< "LMain;->imethod(J)V,greylist" << std::endl
<< "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIMethodHiddenFlags(*dex_file));
}
@@ -384,7 +435,7 @@
<< "LMain;->imethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->imethod(J)V,greylist-max-o" << std::endl
<< "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIMethodHiddenFlags(*dex_file));
}
@@ -395,7 +446,7 @@
<< "LMain;->imethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->imethod(J)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIMethodHiddenFlags(*dex_file));
}
@@ -405,7 +456,7 @@
OpenStream(flags_csv)
<< "LMain;->imethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->imethod(J)V,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -414,7 +465,7 @@
OpenStream(flags_csv)
<< "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->imethod(J)V,blacklist,greylist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -423,7 +474,7 @@
OpenStream(flags_csv)
<< "LMain;->imethod(J)V,greylist,greylist-max-o" << std::endl
<< "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -433,7 +484,7 @@
<< "LMain;->smethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSMethodHiddenFlags(*dex_file));
}
@@ -444,7 +495,7 @@
<< "LMain;->smethod(Ljava/lang/Object;)V,greylist" << std::endl
<< "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSMethodHiddenFlags(*dex_file));
}
@@ -455,7 +506,7 @@
<< "LMain;->smethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->smethod(Ljava/lang/Object;)V,greylist-max-o" << std::endl
<< "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSMethodHiddenFlags(*dex_file));
}
@@ -466,7 +517,7 @@
<< "LMain;->smethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->smethod(Ljava/lang/Object;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSMethodHiddenFlags(*dex_file));
}
@@ -476,7 +527,7 @@
OpenStream(flags_csv)
<< "LMain;->smethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -485,7 +536,7 @@
OpenStream(flags_csv)
<< "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -494,7 +545,7 @@
OpenStream(flags_csv)
<< "LMain;->smethod(Ljava/lang/Object;)V,greylist,greylist-max-o" << std::endl
<< "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -504,7 +555,7 @@
<< "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetINMethodHiddenFlags(*dex_file));
}
@@ -515,7 +566,7 @@
<< "LMain;->inmethod(C)V,greylist" << std::endl
<< "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetINMethodHiddenFlags(*dex_file));
}
@@ -526,7 +577,7 @@
<< "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->inmethod(C)V,greylist-max-o" << std::endl
<< "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetINMethodHiddenFlags(*dex_file));
}
@@ -537,7 +588,7 @@
<< "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->inmethod(C)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetINMethodHiddenFlags(*dex_file));
}
@@ -547,7 +598,7 @@
OpenStream(flags_csv)
<< "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->inmethod(C)V,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -556,7 +607,7 @@
OpenStream(flags_csv)
<< "LMain;->inmethod(C)V,blacklist,greylist" << std::endl
<< "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -565,7 +616,7 @@
OpenStream(flags_csv)
<< "LMain;->inmethod(C)V,greylist,greylist-max-o" << std::endl
<< "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -575,7 +626,7 @@
<< "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSNMethodHiddenFlags(*dex_file));
}
@@ -586,7 +637,7 @@
<< "LMain;->snmethod(Ljava/lang/Integer;)V,greylist" << std::endl
<< "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSNMethodHiddenFlags(*dex_file));
}
@@ -597,7 +648,7 @@
<< "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->snmethod(Ljava/lang/Integer;)V,greylist-max-o" << std::endl
<< "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSNMethodHiddenFlags(*dex_file));
}
@@ -608,7 +659,7 @@
<< "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSNMethodHiddenFlags(*dex_file));
}
@@ -618,7 +669,7 @@
OpenStream(flags_csv)
<< "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -627,7 +678,7 @@
OpenStream(flags_csv)
<< "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist" << std::endl
<< "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -636,8 +687,35 @@
OpenStream(flags_csv)
<< "LMain;->snmethod(Ljava/lang/Integer;)V,greylist,greylist-max-o" << std::endl
<< "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
+// The following tests use this class hierarchy:
+//
+// AbstractPackageClass PublicInterface
+// | |
+// | ┌----------------┘
+// | |
+// PackageClass
+//
+// Only PublicInterface is in stubs.
+
+// Test a method declared in PublicInterface and defined in PackageClass.
+TEST_F(HiddenApiTest, InterfaceMethodImplemented) {
+ ScratchFile flags_csv;
+ ASSERT_TRUE(RunHiddenapiList(flags_csv));
+ auto flags = ReadFlagsCsvFile(flags_csv);
+ ASSERT_EQ(SafeMapGet("LPackageClass;->publicMethod1()V", flags), "whitelist");
+}
+
+// Test a method declared in PublicInterface, defined in AbstractPackageClass and
+// inherited by PackageClass.
+TEST_F(HiddenApiTest, InterfaceMethodImplementedInParent) {
+ ScratchFile flags_csv;
+ ASSERT_TRUE(RunHiddenapiList(flags_csv));
+ auto flags = ReadFlagsCsvFile(flags_csv);
+ ASSERT_EQ(SafeMapGet("LAbstractPackageClass;->publicMethod2()V", flags), "whitelist");
+}
+
} // namespace art
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 25a4c82..9009a4e 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -32,6 +32,12 @@
]
},
{
+ description: "Timeouts on host with gcstress and debug.",
+ result: EXEC_FAILED,
+ modes: [host],
+ names: ["jsr166.StampedLockTest#testWriteAfterReadLock"]
+},
+{
description: "Sometimes times out with gcstress and debug.",
result: EXEC_FAILED,
bug: 78228743,
diff --git a/tools/ti-fast/README.md b/tools/ti-fast/README.md
index a0a7dd7..c8cf180 100644
--- a/tools/ti-fast/README.md
+++ b/tools/ti-fast/README.md
@@ -84,10 +84,15 @@
* `GarbageCollectionFinish`
+* `VMStart`
+
+* `VMInit`
+
+* `VMDeath`
+
All other events cannot be listened for by this agent. Most of these missing
events either require the use of other functions in order to be called
-(`FramePop`, `ObjectFree`, etc) or are only called once (`VMInit`, `VMDeath`,
-etc).
+(`FramePop`, `ObjectFree`, etc).
#### ART
> `art -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmti.so '-agentpath:libtifast.so=MethodEntry' -cp tmp/java/helloworld.dex -Xint helloworld`
diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc
index d02e549..8a6ac36 100644
--- a/tools/ti-fast/tifast.cc
+++ b/tools/ti-fast/tifast.cc
@@ -88,6 +88,9 @@
fun(MonitorWaited, EVENT(MONITOR_WAITED), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jboolean b1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, b1)) \
fun(ResourceExhausted, EVENT(RESOURCE_EXHAUSTED), (jvmtiEnv* jvmti, JNIEnv* jni, jint i1, const void* cv, const char* cc), (jvmti, jni, i1, cv, cc)) \
fun(VMObjectAlloc, EVENT(VM_OBJECT_ALLOC), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jclass klass, jlong l1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, klass, jlongContainer{.val = l1})) \
+ fun(VMInit, EVENT(VM_INIT), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread), (jvmti, jni, jthreadContainer{.thread = thread})) \
+ fun(VMStart, EVENT(VM_START), (jvmtiEnv* jvmti, JNIEnv* jni), (jvmti, jni)) \
+ fun(VMDeath, EVENT(VM_DEATH), (jvmtiEnv* jvmti, JNIEnv* jni), (jvmti, jni)) \
#define FOR_ALL_SUPPORTED_NO_JNI_EVENTS(fun) \
fun(CompiledMethodLoad, EVENT(COMPILED_METHOD_LOAD), (jvmtiEnv* jvmti, jmethodID meth, jint i1, const void* cv1, jint i2, const jvmtiAddrLocationMap* alm, const void* cv2), (jvmti, meth, i1, cv1, i2, alm, cv2)) \
diff --git a/tools/timeout_dumper/timeout_dumper.cc b/tools/timeout_dumper/timeout_dumper.cc
index e04aefb..08d2f4c 100644
--- a/tools/timeout_dumper/timeout_dumper.cc
+++ b/tools/timeout_dumper/timeout_dumper.cc
@@ -93,7 +93,7 @@
namespace addr2line {
constexpr const char* kAddr2linePath =
- "/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/bin/x86_64-linux-addr2line";
+ "/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8/bin/x86_64-linux-addr2line";
std::unique_ptr<std::string> FindAddr2line() {
const char* env_value = getenv("ANDROID_BUILD_TOP");
diff --git a/tools/tracefast-plugin/Android.bp b/tools/tracefast-plugin/Android.bp
index 1d7dd30..b7ae6c6 100644
--- a/tools/tracefast-plugin/Android.bp
+++ b/tools/tracefast-plugin/Android.bp
@@ -30,11 +30,6 @@
"libbase",
],
target: {
- android: {
- shared_libs: [
- "libcutils",
- ],
- },
darwin: {
enabled: false,
},
diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc
index 1dae93a..efb01f7 100644
--- a/tools/veridex/hidden_api.cc
+++ b/tools/veridex/hidden_api.cc
@@ -37,7 +37,7 @@
CHECK(success) << "Unknown ApiList flag: " << str;
CHECK(membership.IsValid()) << "Invalid ApiList: " << membership;
- if (sdk_uses_only != (membership == hiddenapi::ApiList::Whitelist())) {
+ if (sdk_uses_only != membership.Contains(hiddenapi::ApiList::Whitelist())) {
// Either we want only SDK uses and this is not a whitelist entry,
// or we want only non-SDK uses and this is a whitelist entry.
continue;