Merge "Fix arm64 build warning (promoted to error)."
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0746e0c..ad436d0 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1804,6 +1804,7 @@
if (dex_cache_image_class_lookup_required_) {
MoveImageClassesToClassTable();
}
+ // TODO: why isn't this a ReaderMutexLock?
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
for (std::pair<const size_t, GcRoot<mirror::Class> >& it : class_table_) {
mirror::Class* c = it.second.Read();
@@ -1813,18 +1814,75 @@
}
}
-static bool GetClassesVisitor(mirror::Class* c, void* arg) {
+static bool GetClassesVisitorSet(mirror::Class* c, void* arg) {
std::set<mirror::Class*>* classes = reinterpret_cast<std::set<mirror::Class*>*>(arg);
classes->insert(c);
return true;
}
+struct GetClassesVisitorArrayArg {
+ Handle<mirror::ObjectArray<mirror::Class>>* classes;
+ int32_t index;
+ bool success;
+};
+
+static bool GetClassesVisitorArray(mirror::Class* c, void* varg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ GetClassesVisitorArrayArg* arg = reinterpret_cast<GetClassesVisitorArrayArg*>(varg);
+ if (arg->index < (*arg->classes)->GetLength()) {
+ (*arg->classes)->Set(arg->index, c);
+ arg->index++;
+ return true;
+ } else {
+ arg->success = false;
+ return false;
+ }
+}
+
void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) {
- std::set<mirror::Class*> classes;
- VisitClasses(GetClassesVisitor, &classes);
- for (mirror::Class* klass : classes) {
- if (!visitor(klass, arg)) {
- return;
+ // TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem
+ // is avoiding duplicates.
+ if (!kMovingClasses) {
+ std::set<mirror::Class*> classes;
+ VisitClasses(GetClassesVisitorSet, &classes);
+ for (mirror::Class* klass : classes) {
+ if (!visitor(klass, arg)) {
+ return;
+ }
+ }
+ } else {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ObjectArray<mirror::Class>> classes =
+ hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
+ GetClassesVisitorArrayArg local_arg;
+ local_arg.classes = &classes;
+ local_arg.success = false;
+ // We size the array assuming classes won't be added to the class table during the visit.
+ // If this assumption fails we iterate again.
+ while (!local_arg.success) {
+ size_t class_table_size;
+ {
+ ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ class_table_size = class_table_.size();
+ }
+ mirror::Class* class_type = mirror::Class::GetJavaLangClass();
+ mirror::Class* array_of_class = FindArrayClass(self, &class_type);
+ classes.Assign(
+ mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size));
+ CHECK(classes.Get() != nullptr); // OOME.
+ local_arg.index = 0;
+ local_arg.success = true;
+ VisitClasses(GetClassesVisitorArray, &local_arg);
+ }
+ for (int32_t i = 0; i < classes->GetLength(); ++i) {
+ // If the class table shrank during creation of the clases array we expect null elements. If
+ // the class table grew then the loop repeats. If classes are created after the loop has
+ // finished then we don't visit.
+ mirror::Class* klass = classes->Get(i);
+ if (klass != nullptr && !visitor(klass, arg)) {
+ return;
+ }
}
}
}
@@ -2309,7 +2367,9 @@
size_t end = declaring_class->NumVirtualMethods();
bool found = false;
for (size_t i = 0; i < end; i++) {
- if (declaring_class->GetVirtualMethod(i) == method) {
+ // Check method index instead of identity in case of duplicate method definitions.
+ if (method->GetDexMethodIndex() ==
+ declaring_class->GetVirtualMethod(i)->GetDexMethodIndex()) {
found = true;
break;
}
@@ -2716,6 +2776,8 @@
klass->SetVirtualMethods(virtuals);
}
size_t class_def_method_index = 0;
+ uint32_t last_dex_method_index = DexFile::kDexNoIndex;
+ size_t last_class_def_method_index = 0;
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
StackHandleScope<1> hs(self);
Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
@@ -2725,7 +2787,15 @@
}
klass->SetDirectMethod(i, method.Get());
LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
- method->SetMethodIndex(class_def_method_index);
+ uint32_t it_method_index = it.GetMemberIndex();
+ if (last_dex_method_index == it_method_index) {
+ // duplicate case
+ method->SetMethodIndex(last_class_def_method_index);
+ } else {
+ method->SetMethodIndex(class_def_method_index);
+ last_dex_method_index = it_method_index;
+ last_class_def_method_index = class_def_method_index;
+ }
class_def_method_index++;
}
for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a7a68b7..7750c8e 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -237,12 +237,14 @@
}
void VisitClasses(ClassVisitor* visitor, void* arg)
- LOCKS_EXCLUDED(dex_lock_)
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Less efficient variant of VisitClasses that doesn't hold the classlinker_classes_lock_
- // when calling the visitor.
+
+ // Less efficient variant of VisitClasses that copies the class_table_ into secondary storage
+ // so that it can visit individual classes without holding the doesn't hold the
+ // Locks::classlinker_classes_lock_. As the Locks::classlinker_classes_lock_ isn't held this code
+ // can race with insertion and deletion of classes while the visitor is being called.
void VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg)
- LOCKS_EXCLUDED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags)
@@ -623,6 +625,33 @@
ConstHandle<mirror::ArtMethod> prototype)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Class* LookupClassFromTableLocked(const char* descriptor,
+ const mirror::ClassLoader* class_loader,
+ size_t hash)
+ SHARED_LOCKS_REQUIRED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+
+ mirror::Class* UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash)
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void MoveImageClassesToClassTable() LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Class* LookupClassFromImage(const char* descriptor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // EnsureResolved is called to make sure that a class in the class_table_ has been resolved
+ // before returning it to the caller. Its the responsibility of the thread that placed the class
+ // in the table to make it resolved. The thread doing resolution must notify on the class' lock
+ // when resolution has occurred. This happens in mirror::Class::SetStatus. As resolution may
+ // retire a class, the version of the class in the table is returned and this may differ from
+ // the class passed in.
+ mirror::Class* EnsureResolved(Thread* self, const char* descriptor, mirror::Class* klass)
+ WARN_UNUSED SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
std::vector<const DexFile*> boot_class_path_;
mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
@@ -646,32 +675,6 @@
// the classes into the class_table_ to avoid dex cache based searches.
Atomic<uint32_t> failed_dex_cache_class_lookups_;
- mirror::Class* LookupClassFromTableLocked(const char* descriptor,
- const mirror::ClassLoader* class_loader,
- size_t hash)
- SHARED_LOCKS_REQUIRED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
-
- mirror::Class* UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash)
- LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void MoveImageClassesToClassTable() LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::Class* LookupClassFromImage(const char* descriptor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- // EnsureResolved is called to make sure that a class in the class_table_ has been resolved
- // before returning it to the caller. Its the responsibility of the thread that placed the class
- // in the table to make it resolved. The thread doing resolution must notify on the class' lock
- // when resolution has occurred. This happens in mirror::Class::SetStatus. As resolution may
- // retire a class, the version of the class in the table is returned and this may differ from
- // the class passed in.
- mirror::Class* EnsureResolved(Thread* self, const char* descriptor, mirror::Class* klass)
- WARN_UNUSED SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// indexes into class_roots_.
// needs to be kept in sync with class_roots_descriptors_.
enum ClassRoot {
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index e1c532e..d834d4d 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -114,11 +114,6 @@
// We don't fail here because SetStackEndForStackOverflow will print better diagnostics.
}
- if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
- // Remove extra entry pushed onto second stack during method tracing.
- Runtime::Current()->GetInstrumentation()->PopMethodForUnwind(self, false);
- }
-
self->SetStackEndForStackOverflow(); // Allow space on the stack for constructor to execute.
JNIEnvExt* env = self->GetJniEnv();
std::string msg("stack size ");
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 5d0d8d2..494781a 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -120,7 +120,7 @@
desired_collector_type_(foreground_collector_type_),
heap_trim_request_lock_(nullptr),
last_trim_time_(0),
- heap_transition_or_trim_target_time_(0),
+ last_heap_transition_time_(0),
heap_trim_request_pending_(false),
parallel_gc_threads_(parallel_gc_threads),
conc_gc_threads_(conc_gc_threads),
@@ -916,35 +916,6 @@
}
void Heap::DoPendingTransitionOrTrim() {
- Thread* self = Thread::Current();
- CollectorType desired_collector_type;
- // Wait until we reach the desired transition time.
- while (true) {
- uint64_t wait_time;
- {
- MutexLock mu(self, *heap_trim_request_lock_);
- desired_collector_type = desired_collector_type_;
- uint64_t current_time = NanoTime();
- if (current_time >= heap_transition_or_trim_target_time_) {
- break;
- }
- wait_time = heap_transition_or_trim_target_time_ - current_time;
- }
- ScopedThreadStateChange tsc(self, kSleeping);
- usleep(wait_time / 1000); // Usleep takes microseconds.
- }
- // Launch homogeneous space compaction if it is desired.
- if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
- if (!CareAboutPauseTimes()) {
- PerformHomogeneousSpaceCompact();
- }
- // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory.
- desired_collector_type = collector_type_;
- return;
- }
- // Transition the collector if the desired collector type is not the same as the current
- // collector type.
- TransitionCollector(desired_collector_type);
if (!CareAboutPauseTimes()) {
// Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
// about pauses.
@@ -956,7 +927,23 @@
<< PrettyDuration(NanoTime() - start_time);
runtime->GetThreadList()->ResumeAll();
}
- // Do a heap trim if it is needed.
+ if (NanoTime() - last_heap_transition_time_ > kCollectorTransitionWait) {
+ // Launch homogeneous space compaction if it is desired.
+ if (desired_collector_type_ == kCollectorTypeHomogeneousSpaceCompact) {
+ if (!CareAboutPauseTimes()) {
+ PerformHomogeneousSpaceCompact();
+ last_heap_transition_time_ = NanoTime();
+ }
+ desired_collector_type_ = collector_type_;
+ } else {
+ // Transition the collector if the desired collector type is not the same as the current
+ // collector type.
+ TransitionCollector(desired_collector_type_);
+ last_heap_transition_time_ = NanoTime();
+ }
+ }
+ // Do a heap trim if it is needed. This is good to do even with hspace compaction since it may
+ // trim the native heap and dlmalloc spaces.
Trim();
}
@@ -2990,8 +2977,6 @@
if (desired_collector_type_ == desired_collector_type) {
return;
}
- heap_transition_or_trim_target_time_ =
- std::max(heap_transition_or_trim_target_time_, NanoTime() + delta_time);
desired_collector_type_ = desired_collector_type;
}
SignalHeapTrimDaemon(self);
@@ -3027,10 +3012,6 @@
return;
}
heap_trim_request_pending_ = true;
- uint64_t current_time = NanoTime();
- if (heap_transition_or_trim_target_time_ < current_time) {
- heap_transition_or_trim_target_time_ = current_time + kHeapTrimWait;
- }
}
// Notify the daemon thread which will actually do the heap trim.
SignalHeapTrimDaemon(self);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9742277..3bfa748 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -830,8 +830,8 @@
Mutex* heap_trim_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
// When we want to perform the next heap trim (nano seconds).
uint64_t last_trim_time_ GUARDED_BY(heap_trim_request_lock_);
- // When we want to perform the next heap transition (nano seconds) or heap trim.
- uint64_t heap_transition_or_trim_target_time_ GUARDED_BY(heap_trim_request_lock_);
+ // When we last performed a heap transition or hspace compact.
+ uint64_t last_heap_transition_time_;
// If we have a heap trim request pending.
bool heap_trim_request_pending_ GUARDED_BY(heap_trim_request_lock_);
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index df6055d..c3c8c25 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -17,9 +17,11 @@
#include <stdlib.h>
#include "debugger.h"
+#include "instruction_set.h"
#include "java_vm_ext.h"
#include "jni_internal.h"
#include "JNIHelp.h"
+#include "ScopedUtfChars.h"
#include "thread-inl.h"
#if defined(HAVE_PRCTL)
@@ -102,17 +104,27 @@
return reinterpret_cast<jlong>(self);
}
-static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags) {
+static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags,
+ jstring instruction_set) {
Thread* thread = reinterpret_cast<Thread*>(token);
// Our system thread ID, etc, has changed so reset Thread state.
thread->InitAfterFork();
EnableDebugFeatures(debug_flags);
- Runtime::Current()->DidForkFromZygote();
+
+ Runtime::NativeBridgeAction action = Runtime::NativeBridgeAction::kUnload;
+ if (instruction_set != nullptr) {
+ ScopedUtfChars isa_string(env, instruction_set);
+ InstructionSet isa = GetInstructionSetFromString(isa_string.c_str());
+ if (isa != kNone && isa != kRuntimeISA) {
+ action = Runtime::NativeBridgeAction::kInitialize;
+ }
+ }
+ Runtime::Current()->DidForkFromZygote(action);
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ZygoteHooks, nativePreFork, "()J"),
- NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JI)V"),
+ NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JILjava/lang/String;)V"),
};
void register_dalvik_system_ZygoteHooks(JNIEnv* env) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 5adb5d0..89ad505 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -428,7 +428,7 @@
return false;
}
} else {
- DidForkFromZygote();
+ DidForkFromZygote(NativeBridgeAction::kInitialize);
}
StartDaemonThreads();
@@ -506,9 +506,19 @@
#endif
}
-void Runtime::DidForkFromZygote() {
+void Runtime::DidForkFromZygote(NativeBridgeAction action) {
is_zygote_ = false;
+ switch (action) {
+ case NativeBridgeAction::kUnload:
+ android::UnloadNativeBridge();
+ break;
+
+ case NativeBridgeAction::kInitialize:
+ android::InitializeNativeBridge();
+ break;
+ }
+
// Create the thread pool.
heap_->CreateThreadPool();
@@ -833,8 +843,34 @@
self->ClearException();
// Look for a native bridge.
+ //
+ // The intended flow here is, in the case of a running system:
+ //
+ // Runtime::Init() (zygote):
+ // LoadNativeBridge -> dlopen from cmd line parameter.
+ // |
+ // V
+ // Runtime::Start() (zygote):
+ // No-op wrt native bridge.
+ // |
+ // | start app
+ // V
+ // DidForkFromZygote(action)
+ // action = kUnload -> dlclose native bridge.
+ // action = kInitialize -> initialize library
+ //
+ //
+ // The intended flow here is, in the case of a simple dalvikvm call:
+ //
+ // Runtime::Init():
+ // LoadNativeBridge -> dlopen from cmd line parameter.
+ // |
+ // V
+ // Runtime::Start():
+ // DidForkFromZygote(kInitialize) -> try to initialize any native bridge given.
+ // No-op wrt native bridge.
native_bridge_library_filename_ = options->native_bridge_library_filename_;
- android::SetupNativeBridge(native_bridge_library_filename_.c_str(), &native_bridge_art_callbacks_);
+ android::LoadNativeBridge(native_bridge_library_filename_.c_str(), &native_bridge_art_callbacks_);
VLOG(startup) << "Runtime::Setup native bridge library: "
<< (native_bridge_library_filename_.empty() ?
"(empty)" : native_bridge_library_filename_);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 632806f..a0993ca 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -393,9 +393,13 @@
void SetStatsEnabled(bool new_state);
+ enum class NativeBridgeAction { // private
+ kUnload,
+ kInitialize
+ };
void PreZygoteFork();
bool InitZygote();
- void DidForkFromZygote();
+ void DidForkFromZygote(NativeBridgeAction action);
const instrumentation::Instrumentation* GetInstrumentation() const {
return &instrumentation_;
diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt
index 5b41606..808d968 100644
--- a/test/115-native-bridge/expected.txt
+++ b/test/115-native-bridge/expected.txt
@@ -1,5 +1,5 @@
-Ready for native bridge tests.
Native bridge initialized.
+Ready for native bridge tests.
Checking for support.
Getting trampoline for JNI_OnLoad with shorty (null).
Test ART callbacks: all JNI function number is 9.
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 7da57dd..773a950 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -193,21 +193,6 @@
TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
-# Tests that are broken with tracing.
-TEST_ART_BROKEN_TRACE_RUN_TESTS := \
- 004-SignalTest \
- 018-stack-overflow \
- 097-duplicate-method \
- 107-int-math2
-
-ifneq (,$(filter trace,$(TRACE_TYPES)))
- ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
- $(COMPILER_TYPES),$(RELOCATE_TYPES),trace,$(GC_TYPES),$(JNI_TYPES), \
- $(IMAGE_TYPES), $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(ALL_ADDRESS_SIZES))
-endif
-
-TEST_ART_BROKEN_TRACE_RUN_TESTS :=
-
# Tests that are broken with GC stress.
TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
004-SignalTest
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 54a0865..4485590 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -208,7 +208,16 @@
fi
cd $ANDROID_BUILD_TOP
-# If we are execing /bin/false we might not be on the same ISA as libsigchain.so
-# ld.so will helpfully warn us of this. Unfortunately this messes up our error
-# checking so we will just filter out the error with a grep.
-$mkdir_cmd && $prebuild_cmd && LD_PRELOAD=libsigchain.so $cmdline "$@" 2>&1 | grep -v -E "^ERROR: ld\.so: object '.+\.so' from LD_PRELOAD cannot be preloaded: ignored\.$"
+
+$mkdir_cmd || exit 1
+$prebuild_cmd || exit 2
+
+if [ "$GDB" = "y" ]; then
+ # When running under gdb, we cannot do piping and grepping...
+ LD_PRELOAD=libsigchain.so $cmdline "$@"
+else
+ # If we are execing /bin/false we might not be on the same ISA as libsigchain.so
+ # ld.so will helpfully warn us of this. Unfortunately this messes up our error
+ # checking so we will just filter out the error with a grep.
+ LD_PRELOAD=libsigchain.so $cmdline "$@" 2>&1 | grep -v -E "^ERROR: ld\.so: object '.+\.so' from LD_PRELOAD cannot be preloaded: ignored\.$"
+fi
\ No newline at end of file