diff options
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | dex2oat/dex2oat_test.cc | 16 | ||||
-rw-r--r-- | openjdkjvmti/OpenjdkJvmTi.cc | 3 | ||||
-rw-r--r-- | openjdkjvmti/art_jvmti.h | 15 | ||||
-rw-r--r-- | openjdkjvmti/events-inl.h | 5 | ||||
-rw-r--r-- | openjdkjvmti/ti_breakpoint.cc | 8 | ||||
-rw-r--r-- | openjdkjvmti/ti_field.cc | 4 | ||||
-rw-r--r-- | openjdkjvmti/ti_field.h | 14 | ||||
-rw-r--r-- | openjdkjvmti/ti_stack.cc | 9 | ||||
-rw-r--r-- | runtime/class_linker.cc | 1 | ||||
-rw-r--r-- | runtime/gc/collector/concurrent_copying.cc | 8 | ||||
-rw-r--r-- | runtime/gc/heap.cc | 7 | ||||
-rw-r--r-- | runtime/gc/heap.h | 4 | ||||
-rwxr-xr-x | tools/golem/build-target.sh | 12 |
14 files changed, 79 insertions, 28 deletions
diff --git a/Android.bp b/Android.bp index 569179dc11..5e3a8d8bfa 100644 --- a/Android.bp +++ b/Android.bp @@ -12,6 +12,7 @@ art_static_dependencies = [ "libcutils", "libunwindbacktrace", "libunwind", + "libunwindstack", "libutils", "libbase", "liblz4", diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 1b731fc7f6..0e5776f39e 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1372,9 +1372,19 @@ TEST_F(Dex2oatTest, LayoutSections) { EXPECT_LT(code_item_offset - section_startup_only.offset_, section_startup_only.size_); ++startup_count; } else { - // If no flags are set, the method should be unused. - EXPECT_LT(code_item_offset - section_unused.offset_, section_unused.size_); - ++unused_count; + if (code_item_offset - section_unused.offset_ < section_unused.size_) { + // If no flags are set, the method should be unused ... + ++unused_count; + } else { + // or this method is part of the last code item and the end is 4 byte aligned. + ClassDataItemIterator it2(*dex_file, dex_file->GetClassData(*class_def)); + it2.SkipAllFields(); + for (; it2.HasNextDirectMethod() || it2.HasNextVirtualMethod(); it2.Next()) { + EXPECT_LE(it2.GetMethodCodeItemOffset(), code_item_offset); + } + uint32_t code_item_size = dex_file->FindCodeItemOffset(*class_def, method_idx); + EXPECT_EQ((code_item_offset + code_item_size) % 4, 0u); + } } } DCHECK(!it.HasNext()); diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index b30d45ae88..c2584e6944 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -1676,7 +1676,8 @@ extern const jvmtiInterface_1 gJvmtiInterface; ArtJvmTiEnv::ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler) : art_vm(runtime), local_data(nullptr), - capabilities() { + capabilities(), + event_info_mutex_("jvmtiEnv_EventInfoMutex") { object_tag_table = std::unique_ptr<ObjectTagTable>(new ObjectTagTable(event_handler, this)); functions = &gJvmtiInterface; } diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index ad405e8571..3edefaf412 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -43,6 +43,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/strlcpy.h" +#include "base/mutex.h" #include "events.h" #include "java_vm_ext.h" #include "jni_env_ext.h" @@ -59,6 +60,9 @@ namespace openjdkjvmti { class ObjectTagTable; +// Make sure that the DEFAULT_MUTEX_ACQUIRED_AFTER macro works. +using art::Locks; + // A structure that is a jvmtiEnv with additional information for the runtime. struct ArtJvmTiEnv : public jvmtiEnv { art::JavaVMExt* art_vm; @@ -77,12 +81,15 @@ struct ArtJvmTiEnv : public jvmtiEnv { // or by putting a list in the ClassExt of a field's DeclaringClass. // TODO Maybe just have an extension to let one put a watch on every field, that would probably be // good enough maybe since you probably want either a few or all/almost all of them. - std::unordered_set<art::ArtField*> access_watched_fields; - std::unordered_set<art::ArtField*> modify_watched_fields; + std::unordered_set<art::ArtField*> access_watched_fields GUARDED_BY(event_info_mutex_); + std::unordered_set<art::ArtField*> modify_watched_fields GUARDED_BY(event_info_mutex_); // Set of breakpoints is unique to each jvmtiEnv. - std::unordered_set<Breakpoint> breakpoints; - std::unordered_set<const art::ShadowFrame*> notify_frames; + std::unordered_set<Breakpoint> breakpoints GUARDED_BY(event_info_mutex_); + std::unordered_set<const art::ShadowFrame*> notify_frames GUARDED_BY(event_info_mutex_); + + // RW lock to protect access to all of the event data. + art::ReaderWriterMutex event_info_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER; ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler); diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index ab8e6def2d..3973e9432f 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -21,6 +21,7 @@ #include <type_traits> #include <tuple> +#include "base/mutex-inl.h" #include "events.h" #include "jni_internal.h" #include "nativehelper/ScopedLocalRef.h" @@ -276,6 +277,7 @@ inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kBreakpoint>( jthread jni_thread ATTRIBUTE_UNUSED, jmethodID jmethod, jlocation location) const { + art::ReaderMutexLock lk(art::Thread::Current(), env->event_info_mutex_); art::ArtMethod* method = art::jni::DecodeArtMethod(jmethod); return ShouldDispatchOnThread<ArtJvmtiEvent::kBreakpoint>(env, thread) && env->breakpoints.find({method, location}) != env->breakpoints.end(); @@ -292,6 +294,7 @@ inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFramePop>( const art::ShadowFrame* frame) const { // Search for the frame. Do this before checking if we need to send the event so that we don't // have to deal with use-after-free or the frames being reallocated later. + art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_); return env->notify_frames.erase(frame) != 0 && ShouldDispatchOnThread<ArtJvmtiEvent::kFramePop>(env, thread); } @@ -313,6 +316,7 @@ inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFieldModification>( jfieldID field, char type_char ATTRIBUTE_UNUSED, jvalue val ATTRIBUTE_UNUSED) const { + art::ReaderMutexLock lk(art::Thread::Current(), env->event_info_mutex_); return ShouldDispatchOnThread<ArtJvmtiEvent::kFieldModification>(env, thread) && env->modify_watched_fields.find( art::jni::DecodeArtField(field)) != env->modify_watched_fields.end(); @@ -329,6 +333,7 @@ inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFieldAccess>( jclass field_klass ATTRIBUTE_UNUSED, jobject object ATTRIBUTE_UNUSED, jfieldID field) const { + art::ReaderMutexLock lk(art::Thread::Current(), env->event_info_mutex_); return ShouldDispatchOnThread<ArtJvmtiEvent::kFieldAccess>(env, thread) && env->access_watched_fields.find( art::jni::DecodeArtField(field)) != env->access_watched_fields.end(); diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc index f5116a8080..75c8027b22 100644 --- a/openjdkjvmti/ti_breakpoint.cc +++ b/openjdkjvmti/ti_breakpoint.cc @@ -36,6 +36,7 @@ #include "art_jvmti.h" #include "art_method-inl.h" #include "base/enums.h" +#include "base/mutex-inl.h" #include "dex_file_annotations.h" #include "events-inl.h" #include "jni_internal.h" @@ -62,6 +63,7 @@ Breakpoint::Breakpoint(art::ArtMethod* m, jlocation loc) : method_(m), location_ } void BreakpointUtil::RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass) { + art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_); std::vector<Breakpoint> to_remove; for (const Breakpoint& b : env->breakpoints) { if (b.GetMethod()->GetDeclaringClass() == klass) { @@ -83,6 +85,7 @@ jvmtiError BreakpointUtil::SetBreakpoint(jvmtiEnv* jenv, jmethodID method, jloca // Need to get mutator_lock_ so we can find the interface version of any default methods. art::ScopedObjectAccess soa(art::Thread::Current()); art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod(); + art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_); if (location < 0 || static_cast<uint32_t>(location) >= art_method->GetCodeItem()->insns_size_in_code_units_) { return ERR(INVALID_LOCATION); @@ -102,8 +105,9 @@ jvmtiError BreakpointUtil::ClearBreakpoint(jvmtiEnv* jenv, jmethodID method, jlo } // Need to get mutator_lock_ so we can find the interface version of any default methods. art::ScopedObjectAccess soa(art::Thread::Current()); - auto pos = env->breakpoints.find( - /* Breakpoint */ {art::jni::DecodeArtMethod(method)->GetCanonicalMethod(), location}); + art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod(); + art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_); + auto pos = env->breakpoints.find(/* Breakpoint */ {art_method, location}); if (pos == env->breakpoints.end()) { return ERR(NOT_FOUND); } diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc index c45b926695..b8691837eb 100644 --- a/openjdkjvmti/ti_field.cc +++ b/openjdkjvmti/ti_field.cc @@ -189,6 +189,7 @@ jvmtiError FieldUtil::IsFieldSynthetic(jvmtiEnv* env ATTRIBUTE_UNUSED, jvmtiError FieldUtil::SetFieldModificationWatch(jvmtiEnv* jenv, jclass klass, jfieldID field) { ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv); + art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_); if (klass == nullptr) { return ERR(INVALID_CLASS); } @@ -205,6 +206,7 @@ jvmtiError FieldUtil::SetFieldModificationWatch(jvmtiEnv* jenv, jclass klass, jf jvmtiError FieldUtil::ClearFieldModificationWatch(jvmtiEnv* jenv, jclass klass, jfieldID field) { ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv); + art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_); if (klass == nullptr) { return ERR(INVALID_CLASS); } @@ -221,6 +223,7 @@ jvmtiError FieldUtil::ClearFieldModificationWatch(jvmtiEnv* jenv, jclass klass, jvmtiError FieldUtil::SetFieldAccessWatch(jvmtiEnv* jenv, jclass klass, jfieldID field) { ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv); + art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_); if (klass == nullptr) { return ERR(INVALID_CLASS); } @@ -237,6 +240,7 @@ jvmtiError FieldUtil::SetFieldAccessWatch(jvmtiEnv* jenv, jclass klass, jfieldID jvmtiError FieldUtil::ClearFieldAccessWatch(jvmtiEnv* jenv, jclass klass, jfieldID field) { ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv); + art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_); if (klass == nullptr) { return ERR(INVALID_CLASS); } diff --git a/openjdkjvmti/ti_field.h b/openjdkjvmti/ti_field.h index 8a229ed19d..3cf29f099a 100644 --- a/openjdkjvmti/ti_field.h +++ b/openjdkjvmti/ti_field.h @@ -35,6 +35,8 @@ #include "jni.h" #include "jvmti.h" +#include "art_jvmti.h" + namespace openjdkjvmti { class FieldUtil { @@ -61,10 +63,14 @@ class FieldUtil { jfieldID field, jboolean* is_synthetic_ptr); - static jvmtiError SetFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field); - static jvmtiError ClearFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field); - static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field); - static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field); + static jvmtiError SetFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field) + REQUIRES(!ArtJvmTiEnv::event_info_mutex_); + static jvmtiError ClearFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field) + REQUIRES(!ArtJvmTiEnv::event_info_mutex_); + static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) + REQUIRES(!ArtJvmTiEnv::event_info_mutex_); + static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) + REQUIRES(!ArtJvmTiEnv::event_info_mutex_); }; } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index e0c139954d..23cf659c67 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -1024,9 +1024,12 @@ jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) method, visitor.GetDexPc()); } - // Mark shadow frame as needs_notify_pop_ - shadow_frame->SetNotifyPop(true); - tienv->notify_frames.insert(shadow_frame); + { + art::WriterMutexLock lk(self, tienv->event_info_mutex_); + // Mark shadow frame as needs_notify_pop_ + shadow_frame->SetNotifyPop(true); + tienv->notify_frames.insert(shadow_frame); + } // Make sure can we will go to the interpreter and use the shadow frames. if (needs_instrument) { art::Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(target); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index d3eb29ba0b..cd7a94e3d1 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1819,6 +1819,7 @@ bool ClassLinker::AddImageSpace( if (kIsDebugBuild && app_image) { // This verification needs to happen after the classes have been added to the class loader. // Since it ensures classes are in the class table. + ScopedTrace trace("VerifyAppImage"); VerifyAppImage(header, class_loader, dex_caches, class_table, space); } diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 52b355dedd..4d4d8ffb58 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -2153,14 +2153,18 @@ void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t by mirror::Class* int_array_class = down_cast<mirror::Class*>( Mark(mirror::IntArray::GetArrayClass<kWithoutReadBarrier>())); CHECK(int_array_class != nullptr); - AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class); + if (ReadBarrier::kEnableToSpaceInvariantChecks) { + AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class); + } size_t component_size = int_array_class->GetComponentSize<kWithoutReadBarrier>(); CHECK_EQ(component_size, sizeof(int32_t)); size_t data_offset = mirror::Array::DataOffset(component_size).SizeValue(); if (data_offset > byte_size) { // An int array is too big. Use java.lang.Object. CHECK(java_lang_Object_ != nullptr); - AssertToSpaceInvariant(nullptr, MemberOffset(0), java_lang_Object_); + if (ReadBarrier::kEnableToSpaceInvariantChecks) { + AssertToSpaceInvariant(nullptr, MemberOffset(0), java_lang_Object_); + } CHECK_EQ(byte_size, (java_lang_Object_->GetObjectSize<kVerifyNone, kWithoutReadBarrier>())); dummy_obj->SetClass(java_lang_Object_); CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone>())); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 67e8a0d02f..7328063486 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -93,6 +93,9 @@ namespace gc { static constexpr size_t kCollectorTransitionStressIterations = 0; static constexpr size_t kCollectorTransitionStressWait = 10 * 1000; // Microseconds + +DEFINE_RUNTIME_DEBUG_FLAG(Heap, kStressCollectorTransition); + // Minimum amount of remaining bytes before a concurrent GC is triggered. static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB; static constexpr size_t kMaxConcurrentRemainingBytes = 512 * KB; @@ -889,7 +892,9 @@ void Heap::UpdateProcessState(ProcessState old_process_state, ProcessState new_p // the collector. Similarly, we invoke a full compaction for kCollectorTypeCC but don't // transition the collector. RequestCollectorTransition(background_collector_type_, - kIsDebugBuild ? 0 : kCollectorTransitionWait); + kStressCollectorTransition + ? 0 + : kCollectorTransitionWait); } } } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index d673b4ac29..7b4fab607f 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -25,6 +25,7 @@ #include "allocator_type.h" #include "arch/instruction_set.h" #include "atomic.h" +#include "base/logging.h" #include "base/mutex.h" #include "base/time_utils.h" #include "gc/collector/gc_type.h" @@ -155,6 +156,9 @@ class Heap { static constexpr uint64_t kHeapTrimWait = MsToNs(5000); // How long we wait after a transition request to perform a collector transition (nanoseconds). static constexpr uint64_t kCollectorTransitionWait = MsToNs(5000); + // Whether the transition-wait applies or not. Zero wait will stress the + // transition code and collector, but increases jank probability. + DECLARE_RUNTIME_DEBUG_FLAG(kStressCollectorTransition); // Create a heap with the requested sizes. The possible empty // image_file_names names specify Spaces to load based on diff --git a/tools/golem/build-target.sh b/tools/golem/build-target.sh index 8d8e2bbe6f..4ca2722ac9 100755 --- a/tools/golem/build-target.sh +++ b/tools/golem/build-target.sh @@ -147,12 +147,8 @@ get_build_var() { [[ -n $target_product ]] && extras+=" TARGET_PRODUCT=$target_product" [[ -n $target_build_variant ]] && extras+=" TARGET_BUILD_VARIANT=$target_build_variant" - # call dumpvar-$name from the makefile system. - (\cd "$(gettop)"; - CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \ - command make --no-print-directory -f build/core/config.mk \ - $extras \ - dumpvar-$varname) + # call dumpvar from the build system. + (\cd "$(gettop)"; env $extras build/soong/soong_ui.bash --dumpvar-mode $varname) } # Defaults from command-line. @@ -160,7 +156,7 @@ get_build_var() { mode="" # blank or 'golem' if --golem was specified. golem_target="" # --golem=$golem_target config="" # --machine-type=$config -j_arg="-j8" +j_arg="" showcommands="" simulate="" make_tarball="" @@ -353,7 +349,7 @@ fi # and maybe calls lunch). # -execute make "${j_arg}" "${make_target}" +execute build/soong/soong_ui.bash --make-mode "${j_arg}" "${make_target}" if $strip_symbols; then # Further reduce size by stripping symbols. |