Merge "Update apex rules for dex2oatd"
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 f00da9c..67d85c1 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -198,7 +198,7 @@
ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
-ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex MainUncompressed MultiDexUncompressed
+ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex MainUncompressed MultiDexUncompressed MainStripped Nested MultiDexModifiedSecondary
ART_GTEST_oat_test_DEX_DEPS := Main
ART_GTEST_oat_writer_test_DEX_DEPS := Main
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
@@ -216,6 +216,7 @@
ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps VerifierDepsMulti MultiDex
ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler
ART_GTEST_oatdump_app_test_DEX_DEPS := ProfileTestMultiDex
+ART_GTEST_oatdump_test_DEX_DEPS := ProfileTestMultiDex
# The elf writer test has dependencies on core.oat.
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index d35ceae..9b5c638 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -18,7 +18,6 @@
"libopenjdkjvm",
"libopenjdkjvmti",
"libadbconnection",
- "libjavacrypto",
]
bionic_native_shared_libs = [
"libc",
@@ -86,9 +85,13 @@
art_tools_device_binaries = art_tools_common_binaries + art_tools_device_only_binaries
art_tools_host_binaries = art_tools_common_binaries + art_tools_host_only_binaries
-// (Some) Libcore native libraries.
+// Libcore native libraries.
libcore_native_shared_libs = [
+ "libjavacore",
"libopenjdk",
+ "libexpat",
+ "libz",
+ "libziparchive"
]
// Java libraries
diff --git a/build/apex/ld.config.txt b/build/apex/ld.config.txt
index d5eb9fa..d0145e4 100644
--- a/build/apex/ld.config.txt
+++ b/build/apex/ld.config.txt
@@ -8,7 +8,7 @@
dir.runtime = /apex/com.android.runtime/bin/
[runtime]
-additional.namespaces = platform,conscrypt
+additional.namespaces = platform,conscrypt,runtime
# Keep in sync with the runtime namespace in /system/etc/ld.config.txt.
namespace.default.isolated = true
@@ -54,3 +54,16 @@
namespace.conscrypt.link.platform.shared_libs = libc.so
namespace.conscrypt.link.platform.shared_libs += libm.so
namespace.conscrypt.link.platform.shared_libs += libdl.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace is an alias for the default namespace.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
+namespace.runtime.links = default
+namespace.runtime.link.default.allow_all_shared_libs = true
+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 4c3eb0a..155709a 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -178,8 +178,12 @@
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
@@ -275,7 +279,7 @@
echo "$partition" | cmp "$image_filesystems" -
# Mount the image from the Android Runtime APEX.
- guestmount -a "$image_file" -m "$partition" "$mount_point"
+ guestmount -a "$image_file" -m "$partition" --ro "$mount_point"
}
# Testing release APEX package (com.android.runtime.release).
@@ -288,6 +292,7 @@
work_dir=$(mktemp -d)
mount_point="$work_dir/image"
+host_suffix=""
trap finish_target EXIT
@@ -321,6 +326,7 @@
work_dir=$(mktemp -d)
mount_point="$work_dir/image"
+host_suffix=""
trap finish_target EXIT
@@ -392,6 +398,7 @@
work_dir=$(mktemp -d)
mount_point="$work_dir/zip"
+host_suffix="-host"
trap finish_host EXIT
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/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/oatdump/oatdump_image_test.cc b/oatdump/oatdump_image_test.cc
index de48b04..0a076f0 100644
--- a/oatdump/oatdump_image_test.cc
+++ b/oatdump/oatdump_image_test.cc
@@ -40,13 +40,13 @@
TEST_F(OatDumpTest, TestOatImage) {
TEST_DISABLED_FOR_ARM_AND_MIPS();
std::string error_msg;
- ASSERT_TRUE(Exec(kDynamic, kModeOat, {}, kListAndCode));
+ ASSERT_TRUE(Exec(kDynamic, kModeCoreOat, {}, kListAndCode));
}
TEST_F(OatDumpTest, TestOatImageStatic) {
TEST_DISABLED_FOR_ARM_AND_MIPS();
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
std::string error_msg;
- ASSERT_TRUE(Exec(kStatic, kModeOat, {}, kListAndCode));
+ ASSERT_TRUE(Exec(kStatic, kModeCoreOat, {}, kListAndCode));
}
} // namespace art
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index e6936f6..7b1de01 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <android-base/file.h>
+
#include "oatdump_test.h"
namespace art {
@@ -90,8 +92,11 @@
// Test is failing on target, b/77469384.
TEST_DISABLED_FOR_TARGET();
std::string error_msg;
+ ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {}));
ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
- const std::string dex_location = tmp_dir_+ "/core-oj-hostdex.jar_export.dex";
+ const std::string dex_location =
+ tmp_dir_+ "/" + android::base::Basename(GetTestDexFileName(GetAppBaseName().c_str())) +
+ "_export.dex";
const std::string dexdump2 = GetExecutableFilePath("dexdump2",
/*is_debug=*/false,
/*is_static=*/false);
@@ -104,6 +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(Exec(kStatic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
}
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index dfa659b..3ead8de 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -90,6 +90,7 @@
enum Mode {
kModeOat,
+ kModeCoreOat,
kModeOatWithBootImage,
kModeArt,
kModeSymbolize,
@@ -199,9 +200,11 @@
exec_argv.push_back("--instruction-set=" + std::string(
GetInstructionSetString(kRuntimeISA)));
exec_argv.push_back("--oat-file=" + GetAppOdexName());
+ } else if (mode == kModeCoreOat) {
+ exec_argv.push_back("--oat-file=" + core_oat_location_);
} else {
CHECK_EQ(static_cast<size_t>(mode), static_cast<size_t>(kModeOat));
- exec_argv.push_back("--oat-file=" + core_oat_location_);
+ exec_argv.push_back("--oat-file=" + GetAppOdexName());
}
}
exec_argv.insert(exec_argv.end(), args.begin(), args.end());
diff --git a/runtime/Android.bp b/runtime/Android.bp
index b89eb02..a3081e9 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -396,8 +396,10 @@
"libnativeloader",
"libbacktrace",
"liblog",
- // For atrace, properties, ashmem, set_sched_policy.
+ // For atrace, properties, ashmem.
"libcutils",
+ // For set_sched_policy.
+ "libprocessgroup",
// For common macros.
"libbase",
],
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/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..8020f86 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -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/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 841ace5..f908c62 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -438,6 +438,7 @@
const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation());
const MethodReferenceCollection::IndexVector& indices = pair.second;
VLOG(profiler) << "Location " << dex_file->GetLocation()
+ << " base_location=" << base_location
<< " found=" << (locations.find(base_location) != locations.end())
<< " indices size=" << indices.size();
if (locations.find(base_location) != locations.end()) {
@@ -799,11 +800,38 @@
static void AddTrackedLocationsToMap(const std::string& output_filename,
const std::vector<std::string>& code_paths,
SafeMap<std::string, std::set<std::string>>* map) {
+ std::vector<std::string> code_paths_and_filenames;
+ // The dex locations are sometimes set to the filename instead of the full path.
+ // So make sure we have both "locations" when tracking what needs to be profiled.
+ // - apps + system server have filenames
+ // - boot classpath elements have full paths
+
+ // TODO(calin, ngeoffray, vmarko) This is an workaround for using filanames as
+ // dex locations - needed to prebuilt with a partial boot image
+ // (commit: c4a924d8c74241057d957d360bf31cd5cd0e4f9c).
+ // We should find a better way which allows us to do the tracking based on full paths.
+ for (const std::string& path : code_paths) {
+ size_t last_sep_index = path.find_last_of('/');
+ if (last_sep_index == path.size() - 1) {
+ // Should not happen, but anyone can register code paths so better be prepared and ignore
+ // such locations.
+ continue;
+ }
+ std::string filename = last_sep_index == std::string::npos
+ ? path
+ : path.substr(last_sep_index + 1);
+
+ code_paths_and_filenames.push_back(path);
+ code_paths_and_filenames.push_back(filename);
+ }
+
auto it = map->find(output_filename);
if (it == map->end()) {
- map->Put(output_filename, std::set<std::string>(code_paths.begin(), code_paths.end()));
+ map->Put(
+ output_filename,
+ std::set<std::string>(code_paths_and_filenames.begin(), code_paths_and_filenames.end()));
} else {
- it->second.insert(code_paths.begin(), code_paths.end());
+ it->second.insert(code_paths_and_filenames.begin(), code_paths_and_filenames.end());
}
}
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/oat_file.cc b/runtime/oat_file.cc
index f4a8c50..d179b80 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1410,20 +1410,23 @@
std::string OatFile::ResolveRelativeEncodedDexLocation(
const char* abs_dex_location, const std::string& rel_dex_location) {
- // For host, we still do resolution as the rel_dex_location might be absolute
- // for a target dex (for example /system/foo/foo.apk).
- if (abs_dex_location != nullptr && (rel_dex_location[0] != '/' || !kIsTargetBuild)) {
- // Strip :classes<N>.dex used for secondary multidex files.
+ if (abs_dex_location != nullptr) {
std::string base = DexFileLoader::GetBaseLocation(rel_dex_location);
+ // Strip :classes<N>.dex used for secondary multidex files.
std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(rel_dex_location);
-
- // Check if the base is a suffix of the provided abs_dex_location.
- std::string target_suffix = ((rel_dex_location[0] != '/') ? "/" : "") + base;
- std::string abs_location(abs_dex_location);
- if (abs_location.size() > target_suffix.size()) {
- size_t pos = abs_location.size() - target_suffix.size();
- if (abs_location.compare(pos, std::string::npos, target_suffix) == 0) {
- return abs_location + multidex_suffix;
+ if (!kIsTargetBuild) {
+ // For host, we still do resolution as the rel_dex_location might be absolute
+ // for a target dex (for example /system/foo/foo.apk).
+ return std::string(abs_dex_location) + multidex_suffix;
+ } else if (rel_dex_location[0] != '/') {
+ // Check if the base is a suffix of the provided abs_dex_location.
+ std::string target_suffix = ((rel_dex_location[0] != '/') ? "/" : "") + base;
+ std::string abs_location(abs_dex_location);
+ if (abs_location.size() > target_suffix.size()) {
+ size_t pos = abs_location.size() - target_suffix.size();
+ if (abs_location.compare(pos, std::string::npos, target_suffix) == 0) {
+ return abs_location + multidex_suffix;
+ }
}
}
}
@@ -1833,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/oat_file_test.cc b/runtime/oat_file_test.cc
index b547113..ce09da4d 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -35,10 +35,6 @@
OatFile::ResolveRelativeEncodedDexLocation(
nullptr, "/data/app/foo/base.apk"));
- EXPECT_EQ(std::string("/system/framework/base.apk"),
- OatFile::ResolveRelativeEncodedDexLocation(
- "/data/app/foo/base.apk", "/system/framework/base.apk"));
-
EXPECT_EQ(std::string("/data/app/foo/base.apk"),
OatFile::ResolveRelativeEncodedDexLocation(
"/data/app/foo/base.apk", "base.apk"));
@@ -55,13 +51,29 @@
OatFile::ResolveRelativeEncodedDexLocation(
"/data/app/foo/base.apk", "base.apk!classes11.dex"));
- EXPECT_EQ(std::string("base.apk"),
- OatFile::ResolveRelativeEncodedDexLocation(
- "/data/app/foo/sludge.apk", "base.apk"));
-
- EXPECT_EQ(std::string("o/base.apk"),
- OatFile::ResolveRelativeEncodedDexLocation(
- "/data/app/foo/base.apk", "o/base.apk"));
+ // Host and target differ in their way of handling locations
+ // that are prefix of one another, due to boot image files.
+ if (kIsTargetBuild) {
+ EXPECT_EQ(std::string("/system/framework/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "/system/framework/base.apk"));
+ EXPECT_EQ(std::string("base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/sludge.apk", "base.apk"));
+ EXPECT_EQ(std::string("o/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "o/base.apk"));
+ } else {
+ EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "/system/framework/base.apk"));
+ EXPECT_EQ(std::string("/data/app/foo/sludge.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/sludge.apk", "base.apk"));
+ EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "o/base.apk"));
+ }
}
TEST_F(OatFileTest, LoadOat) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a86bc94..feade83 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1752,7 +1752,8 @@
// libcore can't because it's the library that implements System.loadLibrary!
{
std::string error_msg;
- if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, nullptr, &error_msg)) {
+ if (!java_vm_->LoadNativeLibrary(
+ env, "libjavacore.so", nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg;
}
}
@@ -1761,7 +1762,8 @@
? "libopenjdkd.so"
: "libopenjdk.so";
std::string error_msg;
- if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, nullptr, &error_msg)) {
+ if (!java_vm_->LoadNativeLibrary(
+ env, kOpenJdkLibrary, nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg;
}
}
diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc
index 8ff6c52..24864f9 100644
--- a/runtime/thread_android.cc
+++ b/runtime/thread_android.cc
@@ -21,7 +21,7 @@
#include <sys/resource.h>
#include <sys/time.h>
-#include <cutils/sched_policy.h>
+#include <processgroup/sched_policy.h>
#include <utils/threads.h>
#include "base/macros.h"
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/knownfailures.json b/test/knownfailures.json
index 9c01ba9..e32d23d 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1088,6 +1088,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",
@@ -1172,6 +1173,11 @@
"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",
diff --git a/tools/libcore_gcstress_failures.txt b/tools/libcore_gcstress_failures.txt
index 34ede69..9af2f64 100644
--- a/tools/libcore_gcstress_failures.txt
+++ b/tools/libcore_gcstress_failures.txt
@@ -32,8 +32,9 @@
"libcore.java.text.DecimalFormatTest#testCurrencySymbolSpacing",
"libcore.java.text.SimpleDateFormatTest#testLocales",
"org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_remove",
- "org.apache.harmony.tests.java.text.DateFormatTest#test_getAvailableLocales",
+ "org.apache.harmony.tests.java.lang.ProcessManagerTest#testSleep",
"org.apache.harmony.tests.java.lang.String2Test#test_getBytes",
+ "org.apache.harmony.tests.java.text.DateFormatTest#test_getAvailableLocales",
"org.apache.harmony.tests.java.util.TimerTest#testOverdueTaskExecutesImmediately",
"org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext"]
}
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;