Merge "Take hidden API into account during getDeclaredMethod()"
diff --git a/Android.mk b/Android.mk
index 1a5daff..526cd59 100644
--- a/Android.mk
+++ b/Android.mk
@@ -483,6 +483,28 @@
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
+
+.PHONY: art-bionic-files
+art-bionic-files: libc.bootstrap libdl.bootstrap libm.bootstrap linker
+ 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
+
+########################################################################
# Phony target for only building what go/lem requires for pushing ART on /data.
.PHONY: build-art-target-golem
@@ -514,7 +536,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 \
+ art-bionic-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/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.gtest.mk b/build/Android.gtest.mk
index eac3511..21eee7a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -385,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..4a6637b 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -29,10 +29,16 @@
]
// - 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 = [
"libartd",
@@ -165,7 +171,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,
@@ -188,6 +195,8 @@
host_supported: true,
device_supported: false,
manifest: "manifest.json",
+ java_libs: libcore_target_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
@@ -196,7 +205,8 @@
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 +219,14 @@
darwin: {
enabled: false,
},
+ linux_bionic: {
+ enabled: true,
+ multilib: {
+ both: {
+ native_shared_libs: bionic_native_shared_libs,
+ binaries: bionic_binaries_both,
+ }
+ }
+ },
},
}
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
new file mode 100755
index 0000000..1abc466
--- /dev/null
+++ b/build/apex/art_apex_test.py
@@ -0,0 +1,656 @@
+#!/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('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('libexpat.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('libexpat-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/ld.config.txt b/build/apex/ld.config.txt
index b2637a9..9bf2ae5 100644
--- a/build/apex/ld.config.txt
+++ b/build/apex/ld.config.txt
@@ -33,6 +33,17 @@
namespace.platform.link.default.shared_libs += libnativebridge.so
namespace.platform.link.default.shared_libs += libnativehelper.so
namespace.platform.link.default.shared_libs += libnativeloader.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 +61,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
@@ -64,6 +76,6 @@
namespace.runtime.visible = true
namespace.runtime.links = default
namespace.runtime.link.default.allow_all_shared_libs = true
-namespace.runtime.links = platform
+namespace.runtime.links += platform
# TODO(b/119867084): Restrict fallback to platform namespace to PALette library.
namespace.runtime.link.platform.allow_all_shared_libs = true
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..5236e31 100644
--- a/build/art.go
+++ b/build/art.go
@@ -348,26 +348,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 +356,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/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/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 393db3d..e5c09aa 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -127,15 +127,21 @@
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,
+ dwarf::DW_DEBUG_FRAME_FORMAT,
+ /* write_oat_patches= */ false);
+ }
builder->End();
CHECK(builder->Good());
std::vector<uint8_t> compressed_buffer;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e440eec..d46cffb 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1765,6 +1765,42 @@
}
}
+// Returns true if any of the given dex files define a class from the boot classpath.
+static bool DexFilesRedefineBootClasses(
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ TimingLogger::ScopedTiming t("Fast Verify: Boot Class Redefinition Check", timings);
+
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ bool foundRedefinition = false;
+ for (const DexFile* dex_file : dex_files) {
+ for (ClassAccessor accessor : dex_file->GetClasses()) {
+ const char* descriptor = accessor.GetDescriptor();
+ StackHandleScope<1> hs_class(self);
+ Handle<mirror::Class> klass =
+ hs_class.NewHandle(class_linker->FindSystemClass(self, descriptor));
+ if (klass == nullptr) {
+ self->ClearException();
+ } else {
+ LOG(WARNING) << "Redefinition of boot class " << descriptor
+ << " App dex file: " << accessor.GetDexFile().GetLocation()
+ << " Boot dex file: " << klass->GetDexFile().GetLocation();
+ foundRedefinition = true;
+ if (!VLOG_IS_ON(verifier)) {
+ // If we are not in verbose mode, return early.
+ // Otherwise continue and log all the collisions for easier debugging.
+ return true;
+ }
+ }
+ }
+ }
+
+ return foundRedefinition;
+}
+
bool CompilerDriver::FastVerify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings,
@@ -1776,6 +1812,17 @@
return false;
}
TimingLogger::ScopedTiming t("Fast Verify", timings);
+
+ // We cannot do fast verification if the app redefines classes from the boot classpath.
+ // Vdex does not record resolution chains for boot classes and we might wrongfully
+ // resolve a class to the app when it should have been resolved to the boot classpath
+ // (e.g. if we verified against the SDK and the app redefines a boot class which is not
+ // in the SDK.)
+ if (DexFilesRedefineBootClasses(dex_files, timings)) {
+ LOG(WARNING) << "Found redefinition of boot classes. Not doing fast verification.";
+ return false;
+ }
+
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
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/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/nodes.h b/compiler/optimizing/nodes.h
index 48fb611..c70674b 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.
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 42dbc77..e8f8d32 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);
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/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..808ad6c 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1367,6 +1367,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/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index 9fbcca4..b3e8290 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -261,7 +261,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 +280,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, kCFIFormat, true /* write_oat_patches */);
}
}
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/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/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/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/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..f83f18c 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",
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index c7fbe78..8ea3c09 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -618,6 +618,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/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/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..8849a7a 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,9 @@
"libart-disassembler",
"libvixl",
],
+ // We need this to resolve libartpalette symbols
+ // correctly. Multiple source libraries depend on it.
+ 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_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/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/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/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..44b80df 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -547,11 +547,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/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/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..d72003c 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() {
@@ -2878,6 +2878,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.
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 4c5d896..6bdba12 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1495,6 +1495,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 fcab4c4..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.
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/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_stack_dump.cc b/runtime/native_stack_dump.cc
index 88adad0..a4425ce 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -50,7 +50,9 @@
#include "base/os.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
+#include "class_linker.h"
#include "oat_quick_method_header.h"
+#include "runtime.h"
#include "thread-current-inl.h"
#endif
@@ -63,6 +65,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 +246,9 @@
}
pipe->reset(); // Close early.
+ std::string addr2linePath = FindAddr2line();
const char* args[7] = {
- "/usr/bin/addr2line",
+ addr2linePath.c_str(),
"--functions",
"--inlines",
"--demangle",
@@ -274,11 +289,17 @@
}
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;
+ }
+ 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 +335,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_file.cc b/runtime/oat_file.cc
index e433cbc..d179b80 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1836,6 +1836,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) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 3e9c01f..1ba6e49 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -502,6 +502,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/thread.cc b/runtime/thread.cc
index 309c04e..4828aae 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,18 @@
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;
+}
+
} // namespace art
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/well_known_classes.cc b/runtime/well_known_classes.cc
index f61faa3..955a455 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;
@@ -344,6 +345,7 @@
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;");
@@ -486,6 +488,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..872b562 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;
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/719-dm-verify-redefinition/check b/test/719-dm-verify-redefinition/check
new file mode 100644
index 0000000..b5003bd
--- /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.*\] Found redefinition of boot classes\. Not doing fast verification\./Found redefinition of boot classes\. Not doing fast verification\./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..64fb4ea
--- /dev/null
+++ b/test/719-dm-verify-redefinition/expected.txt
@@ -0,0 +1,3 @@
+Found redefinition of boot classes. Not doing fast verification.
+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/etc/run-test-jar b/test/etc/run-test-jar
index 50d5fee..660c971 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -761,8 +761,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
@@ -1066,7 +1066,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; }
diff --git a/test/knownfailures.json b/test/knownfailures.json
index b9a0963..cf6e69c 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1091,6 +1091,7 @@
"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",
@@ -1175,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/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/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..6be243a 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+=" art-bionic-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/libcore_failures.txt b/tools/libcore_failures.txt
index 9b0873e..5177919 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -234,5 +234,12 @@
"libcore.libcore.icu.TimeZoneIntegrationTest#testTimeZoneDebugInfo",
"libcore.libcore.icu.TimeZoneIntegrationTest#testTzDataSetVersions"
]
+},
+{
+ description: "Expected networking failure on host / old systems: we expect 97 (EAFNOSUPPORT), but we get 22 (EINVAL)",
+ result: EXEC_FAILED,
+ names: [
+ "libcore.libcore.io.OsTest#testCrossFamilyBindConnectSendto"
+ ]
}
]
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/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;