diff options
-rw-r--r-- | openjdkjvmti/ti_heap.cc | 6 | ||||
-rw-r--r-- | openjdkjvmti/ti_method.cc | 3 | ||||
-rw-r--r-- | runtime/Android.bp | 2 | ||||
-rw-r--r-- | runtime/base/locks.cc | 6 | ||||
-rw-r--r-- | runtime/base/locks.h | 7 | ||||
-rw-r--r-- | runtime/class_linker_test.cc | 3 | ||||
-rw-r--r-- | runtime/jni/jni_env_ext.h | 2 | ||||
-rw-r--r-- | runtime/jni/jni_id_manager.cc | 418 | ||||
-rw-r--r-- | runtime/jni/jni_id_manager.h | 89 | ||||
-rw-r--r-- | runtime/jni/jni_internal.cc | 524 | ||||
-rw-r--r-- | runtime/jni/jni_internal.h | 51 | ||||
-rw-r--r-- | runtime/jni_id_type.h | 38 | ||||
-rw-r--r-- | runtime/mirror/class.cc | 126 | ||||
-rw-r--r-- | runtime/mirror/class.h | 17 | ||||
-rw-r--r-- | runtime/mirror/class_ext-inl.h | 69 | ||||
-rw-r--r-- | runtime/mirror/class_ext.h | 46 | ||||
-rw-r--r-- | runtime/parsed_options.cc | 5 | ||||
-rw-r--r-- | runtime/runtime.cc | 11 | ||||
-rw-r--r-- | runtime/runtime.h | 16 | ||||
-rw-r--r-- | runtime/runtime_options.def | 1 | ||||
-rw-r--r-- | runtime/thread.cc | 10 | ||||
-rw-r--r-- | runtime/thread.h | 3 | ||||
-rwxr-xr-x | test/testrunner/testrunner.py | 4 |
23 files changed, 1187 insertions, 270 deletions
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc index 3d99ed8fe6..898363a269 100644 --- a/openjdkjvmti/ti_heap.cc +++ b/openjdkjvmti/ti_heap.cc @@ -21,12 +21,15 @@ #include "base/macros.h" #include "base/mutex.h" #include "class_linker.h" +#include "class_root.h" #include "dex/primitive.h" #include "gc/heap-visit-objects-inl.h" #include "gc/heap.h" #include "gc_root-inl.h" +#include "handle_scope.h" #include "java_frame_root_info.h" #include "jni/jni_env_ext.h" +#include "jni/jni_id_manager.h" #include "jni/jni_internal.h" #include "jvmti_weak_table-inl.h" #include "mirror/class.h" @@ -36,9 +39,11 @@ #include "object_tagging.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" +#include "scoped_thread_state_change.h" #include "stack.h" #include "thread-inl.h" #include "thread_list.h" +#include "well_known_classes.h" namespace openjdkjvmti { @@ -1378,6 +1383,7 @@ jvmtiError HeapUtil::FollowReferences(jvmtiEnv* env, } { art::ScopedObjectAccess soa(self); // Now we know we have the shared lock. + art::jni::ScopedEnableSuspendAllJniIdQueries sjni; // make sure we can get JNI ids. art::ScopedThreadSuspension sts(self, art::kWaitingForVisitObjects); art::ScopedSuspendAll ssa("FollowReferences"); diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index d8ee981063..defcd54082 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -93,12 +93,13 @@ struct TiMethodCallback : public art::MethodCallback { ScopedLocalRef<jthread> thread_jni( jnienv, PhaseUtil::IsLivePhase() ? jnienv->AddLocalReference<jthread>(thread->GetPeer()) : nullptr); + jmethodID method_id = art::jni::EncodeArtMethod(method); art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); event_handler->DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>( thread, static_cast<JNIEnv*>(jnienv), thread_jni.get(), - art::jni::EncodeArtMethod(method), + method_id, const_cast<void*>(cur_method), new_method); } diff --git a/runtime/Android.bp b/runtime/Android.bp index 6b691fd0fd..db4ef0277b 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -121,6 +121,7 @@ libart_cc_defaults { "jni/check_jni.cc", "jni/java_vm_ext.cc", "jni/jni_env_ext.cc", + "jni/jni_id_manager.cc", "jni/jni_internal.cc", "linear_alloc.cc", "managed_stack.cc", @@ -479,6 +480,7 @@ gensrcs { "jdwp_provider.h", "jdwp/jdwp.h", "jdwp/jdwp_constants.h", + "jni_id_type.h", "lock_word.h", "oat.h", "object_callbacks.h", diff --git a/runtime/base/locks.cc b/runtime/base/locks.cc index 1cec44c27e..7404d0d4fc 100644 --- a/runtime/base/locks.cc +++ b/runtime/base/locks.cc @@ -76,6 +76,7 @@ ReaderWriterMutex* Locks::jni_globals_lock_ = nullptr; Mutex* Locks::jni_weak_globals_lock_ = nullptr; ReaderWriterMutex* Locks::dex_lock_ = nullptr; Mutex* Locks::native_debug_interface_lock_ = nullptr; +ReaderWriterMutex* Locks::jni_id_lock_ = nullptr; std::vector<BaseMutex*> Locks::expected_mutexes_on_weak_ref_access_; Atomic<const BaseMutex*> Locks::expected_mutexes_on_weak_ref_access_guard_; @@ -157,6 +158,7 @@ void Locks::Init() { DCHECK(user_code_suspension_lock_ != nullptr); DCHECK(dex_lock_ != nullptr); DCHECK(native_debug_interface_lock_ != nullptr); + DCHECK(jni_id_lock_ != nullptr); DCHECK(runtime_thread_pool_lock_ != nullptr); } else { // Create global locks in level order from highest lock level to lowest. @@ -317,6 +319,10 @@ void Locks::Init() { DCHECK(native_debug_interface_lock_ == nullptr); native_debug_interface_lock_ = new Mutex("Native debug interface lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kJniIdLock); + DCHECK(jni_id_lock_ == nullptr); + jni_id_lock_ = new ReaderWriterMutex("JNI id map lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kAbortLock); DCHECK(abort_lock_ == nullptr); abort_lock_ = new Mutex("abort lock", current_lock_level, true); diff --git a/runtime/base/locks.h b/runtime/base/locks.h index 77e304debd..4b85df0ea6 100644 --- a/runtime/base/locks.h +++ b/runtime/base/locks.h @@ -45,6 +45,7 @@ enum LockLevel : uint8_t { kUnexpectedSignalLock, kThreadSuspendCountLock, kAbortLock, + kJniIdLock, kNativeDebugInterfaceLock, kSignalHandlingLock, // A generic lock level for mutexs that should not allow any additional mutexes to be gained after @@ -353,8 +354,12 @@ class Locks { // Guards the magic global variables used by native tools (e.g. libunwind). static Mutex* native_debug_interface_lock_ ACQUIRED_AFTER(unexpected_signal_lock_); + // Guards the data structures responsible for keeping track of the JNI + // jmethodID/jfieldID <-> ArtMethod/ArtField mapping when using index-ids. + static ReaderWriterMutex* jni_id_lock_ ACQUIRED_AFTER(native_debug_interface_lock_); + // Have an exclusive logging thread. - static Mutex* logging_lock_ ACQUIRED_AFTER(native_debug_interface_lock_); + static Mutex* logging_lock_ ACQUIRED_AFTER(jni_id_lock_); // List of mutexes that we expect a thread may hold when accessing weak refs. This is used to // avoid a deadlock in the empty checkpoint while weak ref access is disabled (b/34964016). If we diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 2a7cbaa991..0ffbc4a56d 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -610,6 +610,8 @@ struct ClassOffsets : public CheckOffsets<mirror::Class> { struct ClassExtOffsets : public CheckOffsets<mirror::ClassExt> { ClassExtOffsets() : CheckOffsets<mirror::ClassExt>(false, "Ldalvik/system/ClassExt;") { + addOffset(OFFSETOF_MEMBER(mirror::ClassExt, instance_jfield_ids_), "instanceJfieldIDs"); + addOffset(OFFSETOF_MEMBER(mirror::ClassExt, jmethod_ids_), "jmethodIDs"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_dex_caches_), "obsoleteDexCaches"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_methods_), "obsoleteMethods"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_), "originalDexFile"); @@ -617,6 +619,7 @@ struct ClassExtOffsets : public CheckOffsets<mirror::ClassExt> { "preRedefineClassDefIndex"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, pre_redefine_dex_file_ptr_), "preRedefineDexFilePtr"); + addOffset(OFFSETOF_MEMBER(mirror::ClassExt, static_jfield_ids_), "staticJfieldIDs"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, verify_error_), "verifyError"); } }; diff --git a/runtime/jni/jni_env_ext.h b/runtime/jni/jni_env_ext.h index 61de07424f..924ff514e0 100644 --- a/runtime/jni/jni_env_ext.h +++ b/runtime/jni/jni_env_ext.h @@ -199,7 +199,7 @@ class JNIEnvExt : public JNIEnv { // If we are a JNI env for a daemon thread with a deleted runtime. bool runtime_deleted_; - friend class JNI; + template<bool kEnableIndexIds> friend class JNI; friend class ScopedJniEnvLocalRefState; friend class Thread; ART_FRIEND_TEST(JniInternalTest, JNIEnvExtOffsets); diff --git a/runtime/jni/jni_id_manager.cc b/runtime/jni/jni_id_manager.cc new file mode 100644 index 0000000000..16f4d5f6d5 --- /dev/null +++ b/runtime/jni/jni_id_manager.cc @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni_id_manager.h" + +#include "android-base/macros.h" +#include "art_field-inl.h" +#include "art_method-inl.h" +#include "base/enums.h" +#include "base/globals.h" +#include "base/locks.h" +#include "base/mutex.h" +#include "gc/allocation_listener.h" +#include "gc/heap.h" +#include "jni/jni_internal.h" +#include "mirror/array-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class.h" +#include "mirror/class_ext.h" +#include "mirror/object-inl.h" +#include "obj_ptr-inl.h" +#include "thread-inl.h" +#include "thread.h" +#include <algorithm> +#include <cstdint> +#include <type_traits> + +namespace art { +namespace jni { + +constexpr bool kTraceIds = false; + +// TODO This whole thing could be done lock & wait free (since we never remove anything from the +// ids list). It's not clear this would be worthwile though. + +namespace { + +static constexpr size_t IdToIndex(uintptr_t id) { + return id >> 1; +} + +static constexpr uintptr_t IndexToId(size_t index) { + return (index << 1) + 1; +} + +template <typename ArtType> +ObjPtr<mirror::PointerArray> GetOrCreateIds(Thread* self, + ObjPtr<mirror::Class> k, + ArtType* t, + /*out*/bool* allocation_failure) + REQUIRES_SHARED(Locks::mutator_lock_); + +template <> +ObjPtr<mirror::PointerArray> GetOrCreateIds(Thread* self, + ObjPtr<mirror::Class> k, + ArtField* field, + /*out*/bool* allocation_failure) { + ScopedExceptionStorage ses(self); + ObjPtr<mirror::PointerArray> res; + if (Locks::mutator_lock_->IsExclusiveHeld(self)) { + res = field->IsStatic() ? k->GetStaticFieldIds() : k->GetInstanceFieldIds(); + } else { + res = field->IsStatic() ? k->GetOrCreateStaticFieldIds() : k->GetOrCreateInstanceFieldIds(); + } + if (self->IsExceptionPending()) { + self->AssertPendingOOMException(); + ses.SuppressOldException("Failed to allocate maps for jmethodIDs. "); + *allocation_failure = true; + } else { + *allocation_failure = false; + } + return res; +} + +template <> +ObjPtr<mirror::PointerArray> GetOrCreateIds(Thread* self, + ObjPtr<mirror::Class> k, + ArtMethod* method, + /*out*/bool* allocation_failure) { + if (method->IsObsolete()) { + if (kTraceIds) { + LOG(INFO) << "jmethodID for Obsolete method " << method->PrettyMethod() << " requested!"; + } + // No ids array for obsolete methods. Just do a linear scan. + *allocation_failure = false; + return nullptr; + } + ObjPtr<mirror::PointerArray> res; + if (Locks::mutator_lock_->IsExclusiveHeld(self) || !Locks::mutator_lock_->IsSharedHeld(self)) { + res = k->GetMethodIds(); + } else { + res = k->GetOrCreateMethodIds(); + } + if (self->IsExceptionPending()) { + self->AssertPendingOOMException(); + *allocation_failure = true; + } else { + *allocation_failure = false; + } + return res; +} + +template <typename ArtType> +size_t GetIdOffset(ObjPtr<mirror::Class> k, ArtType* t, PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_); +template <> +size_t GetIdOffset(ObjPtr<mirror::Class> k, ArtField* f, PointerSize ptr_size ATTRIBUTE_UNUSED) { + return f->IsStatic() ? k->GetStaticFieldIdOffset(f) : k->GetInstanceFieldIdOffset(f); +} +template <> +size_t GetIdOffset(ObjPtr<mirror::Class> k, ArtMethod* method, PointerSize pointer_size) { + return method->IsObsolete() ? -1 : k->GetMethodIdOffset(method, pointer_size); +} + +// Calls the relevant PrettyMethod/PrettyField on the input. +template <typename ArtType> +std::string PrettyGeneric(ArtType* t) REQUIRES_SHARED(Locks::mutator_lock_); +template <> std::string PrettyGeneric(ArtMethod* f) { + return f->PrettyMethod(); +} +template <> std::string PrettyGeneric(ArtField* f) { + return f->PrettyField(); +} + +// Checks if the field or method is obsolete. +template <typename ArtType> bool IsObsolete(ArtType* t) REQUIRES_SHARED(Locks::mutator_lock_); +template <> bool IsObsolete(ArtField* t ATTRIBUTE_UNUSED) { + return false; +} +template <> bool IsObsolete(ArtMethod* t) { + return t->IsObsolete(); +} + +// Get the canonical (non-copied) version of the field or method. Only relevant for methods. +template <typename ArtType> ArtType* Canonicalize(ArtType* t) REQUIRES_SHARED(Locks::mutator_lock_); +template <> ArtField* Canonicalize(ArtField* t) { + return t; +} +template <> ArtMethod* Canonicalize(ArtMethod* t) { + if (UNLIKELY(t->IsCopied())) { + t = t->GetCanonicalMethod(); + } + return t; +} + +}; // namespace + +// We increment the id by 2 each time to allow us to use the LSB as a flag that the ID is an index +// and not a pointer. This gives us 2**31 unique methods that can be addressed on 32-bit art, which +// should be more than enough. +template <> uintptr_t JniIdManager::GetNextId<ArtField>() { + uintptr_t res = next_field_id_; + next_field_id_ += 2; + CHECK_GT(next_field_id_, res) << "jfieldID Overflow"; + return res; +} + +template <> uintptr_t JniIdManager::GetNextId<ArtMethod>() { + uintptr_t res = next_method_id_; + next_method_id_ += 2; + CHECK_GT(next_method_id_, res) << "jmethodID Overflow"; + return res; +} +template <> std::vector<ArtField*>& JniIdManager::GetGenericMap<ArtField>() { + return field_id_map_; +} + +template <> std::vector<ArtMethod*>& JniIdManager::GetGenericMap<ArtMethod>() { + return method_id_map_; +} +template <> size_t JniIdManager::GetLinearSearchStartId<ArtField>(ArtField* t ATTRIBUTE_UNUSED) { + return deferred_allocation_field_id_start_; +} + +template <> size_t JniIdManager::GetLinearSearchStartId<ArtMethod>(ArtMethod* m) { + if (m->IsObsolete()) { + return 1; + } else { + return deferred_allocation_method_id_start_; + } +} + +template <typename ArtType> uintptr_t JniIdManager::EncodeGenericId(ArtType* t) { + if (!Runtime::Current()->JniIdsAreIndices() || t == nullptr) { + return reinterpret_cast<uintptr_t>(t); + } + Thread* self = Thread::Current(); + ScopedExceptionStorage ses(self); + t = Canonicalize(t); + ObjPtr<mirror::Class> klass = t->GetDeclaringClass(); + DCHECK(!klass.IsNull()) << "Null declaring class " << PrettyGeneric(t); + size_t off = GetIdOffset(klass, t, kRuntimePointerSize); + bool allocation_failure = false; + ObjPtr<mirror::PointerArray> ids(GetOrCreateIds(self, klass, t, &allocation_failure)); + if (allocation_failure) { + self->AssertPendingOOMException(); + ses.SuppressOldException("OOM exception while trying to allocate JNI ids."); + return 0u; + } + uintptr_t cur_id = 0; + if (!ids.IsNull()) { + DCHECK_GT(ids->GetLength(), static_cast<int32_t>(off)) << " is " << PrettyGeneric(t); + cur_id = ids->GetElementPtrSize<uintptr_t>(off, kRuntimePointerSize); + } + if (cur_id != 0) { + return cur_id; + } + WriterMutexLock mu(self, *Locks::jni_id_lock_); + // Check the ids array for a racing id. + if (!ids.IsNull()) { + cur_id = ids->GetElementPtrSize<uintptr_t>(off, kRuntimePointerSize); + if (cur_id != 0) { + // We were racing some other thread and lost. + return cur_id; + } + } else { + // We cannot allocate anything here or don't have an ids array (we might be an obsolete method). + DCHECK(IsObsolete(t) || deferred_allocation_refcount_ > 0u) + << "deferred_allocation_refcount_: " << deferred_allocation_refcount_ + << " t: " << PrettyGeneric(t); + // Check to see if we raced and lost to another thread. + const std::vector<ArtType*>& vec = GetGenericMap<ArtType>(); + bool found = false; + // simple count-while. + size_t search_start_index = IdToIndex(GetLinearSearchStartId(t)); + size_t index = std::count_if(vec.cbegin() + search_start_index, + vec.cend(), + [&found, t](const ArtType* candidate) { + found = found || candidate == t; + return !found; + }) + + search_start_index; + if (found) { + // We were either racing some other thread and lost or this thread was asked to encode the + // same method multiple times while holding the mutator lock. + DCHECK_EQ(vec[index], t) << "Expected: " << PrettyGeneric(vec[index]) << " got " + << PrettyGeneric(t) << " at index " << index + << " (id: " << IndexToId(index) << ")."; + return IndexToId(index); + } + } + cur_id = GetNextId<ArtType>(); + size_t cur_index = IdToIndex(cur_id); + std::vector<ArtType*>& vec = GetGenericMap<ArtType>(); + vec.reserve(cur_index + 1); + vec.resize(std::max(vec.size(), cur_index + 1), nullptr); + vec[cur_index] = t; + if (ids.IsNull()) { + if (kIsDebugBuild && !IsObsolete(t)) { + CHECK_NE(deferred_allocation_refcount_, 0u) + << "Failed to allocate ids array despite not being forbidden from doing so!"; + Locks::mutator_lock_->AssertExclusiveHeld(self); + } + } else { + ids->SetElementPtrSize(off, reinterpret_cast<void*>(cur_id), kRuntimePointerSize); + } + return cur_id; +} + +jfieldID JniIdManager::EncodeFieldId(ArtField* field) { + auto* res = reinterpret_cast<jfieldID>(EncodeGenericId(field)); + if (kTraceIds && field != nullptr) { + LOG(INFO) << "Returning " << res << " for field " << field->PrettyField(); + } + return res; +} +jmethodID JniIdManager::EncodeMethodId(ArtMethod* method) { + auto* res = reinterpret_cast<jmethodID>(EncodeGenericId(method)); + if (kTraceIds && method != nullptr) { + LOG(INFO) << "Returning " << res << " for method " << method->PrettyMethod(); + } + return res; +} + +template <typename ArtType> ArtType* JniIdManager::DecodeGenericId(uintptr_t t) { + if (Runtime::Current()->JniIdsAreIndices() && (t % 2) == 1) { + ReaderMutexLock mu(Thread::Current(), *Locks::jni_id_lock_); + size_t index = IdToIndex(t); + DCHECK_GT(GetGenericMap<ArtType>().size(), index); + return GetGenericMap<ArtType>().at(index); + } else { + DCHECK_EQ((t % 2), 0u) << "id: " << t; + return reinterpret_cast<ArtType*>(t); + } +} + +ArtMethod* JniIdManager::DecodeMethodId(jmethodID method) { + return DecodeGenericId<ArtMethod>(reinterpret_cast<uintptr_t>(method)); +} + +ArtField* JniIdManager::DecodeFieldId(jfieldID field) { + return DecodeGenericId<ArtField>(reinterpret_cast<uintptr_t>(field)); +} + +// This whole defer system is an annoying requirement to allow us to generate IDs during heap-walks +// such as those required for instrumentation tooling. +// +// The defer system works with the normal id-assignment routine to ensure that all the class-ext +// data structures are eventually created and filled in. Basically how it works is the id-assignment +// function will check to see if it has a strong mutator-lock. If it does not then it will try to +// allocate the class-ext data structures normally and fail if it is unable to do so. In the case +// where mutator-lock is being held exclusive no attempt to allocate will be made and the thread +// will CHECK that allocations are being deferred (or that the method is obsolete, in which case +// there is no class-ext to store the method->id map in). +// +// Once the thread is done holding the exclusive mutator-lock it will go back and fill-in the +// class-ext data of all the methods that were added. We do this without the exclusive mutator-lock +// on a copy of the maps before we decrement the deferred refcount. This ensures that any other +// threads running at the same time know they need to perform a linear scan of the id-map. Since we +// don't have the mutator-lock anymore other threads can allocate the class-ext data, meaning our +// copy is fine. The only way additional methods could end up on the id-maps after our copy without +// having class-ext data is if another thread picked up the exclusive mutator-lock and added another +// defer, in which case that thread would fix-up the remaining ids. In this way we maintain eventual +// consistency between the class-ext method/field->id maps and the JniIdManager id->method/field +// maps. +// +// TODO It is possible that another thread to gain the mutator-lock and allocate new ids without +// calling StartDefer. This is basically a race that we should try to catch but doing so is +// rather difficult and since this defer system is only used in very rare circumstances unlikely to +// be worth the trouble. +void JniIdManager::StartDefer() { + Thread* self = Thread::Current(); + WriterMutexLock mu(self, *Locks::jni_id_lock_); + if (deferred_allocation_refcount_++ == 0) { + deferred_allocation_field_id_start_ = next_field_id_; + deferred_allocation_method_id_start_ = next_method_id_; + } +} + +void JniIdManager::EndDefer() { + // Fixup the method->id map. + Thread* self = Thread::Current(); + auto set_id = [&](auto* t, uintptr_t id) REQUIRES_SHARED(Locks::mutator_lock_) { + if (t == nullptr) { + return; + } + ObjPtr<mirror::Class> klass(t->GetDeclaringClass()); + size_t off = GetIdOffset(klass, t, kRuntimePointerSize); + bool alloc_failure = false; + ObjPtr<mirror::PointerArray> ids = GetOrCreateIds(self, klass, t, &alloc_failure); + CHECK(!alloc_failure) << "Could not allocate jni ids array!"; + if (ids.IsNull()) { + return; + } + if (kIsDebugBuild) { + uintptr_t old_id = ids->GetElementPtrSize<uintptr_t, kRuntimePointerSize>(off); + if (old_id != 0) { + DCHECK_EQ(old_id, id); + } + } + ids->SetElementPtrSize(off, reinterpret_cast<void*>(id), kRuntimePointerSize); + }; + // To ensure eventual consistency this depends on the fact that the method_id_map_ and + // field_id_map_ are the ultimate source of truth and no id is ever reused to be valid. It also + // relies on all threads always getting calling StartDefer if they are going to be allocating jni + // ids while suspended. If a thread tries to do so while it doesn't have a scope we could miss + // ids. + // TODO We should use roles or something to verify that this requirement is not broken. + // + // If another thread comes along and adds more methods to the list after + // copying either (1) the id-maps are already present for the method and everything is fine, (2) + // the thread is not suspended and so can create the ext-data and id lists or, (3) the thread also + // suspended everything and incremented the deferred_allocation_refcount_ so it will fix up new + // ids when it finishes. + Locks::mutator_lock_->AssertNotExclusiveHeld(self); + Locks::mutator_lock_->AssertSharedHeld(self); + std::vector<ArtMethod*> method_id_copy; + std::vector<ArtField*> field_id_copy; + uintptr_t method_start_id; + uintptr_t field_start_id; + { + ReaderMutexLock mu(self, *Locks::jni_id_lock_); + method_id_copy = method_id_map_; + field_id_copy = field_id_map_; + method_start_id = deferred_allocation_method_id_start_; + field_start_id = deferred_allocation_field_id_start_; + } + + for (size_t index = kIsDebugBuild ? 0 : IdToIndex(method_start_id); index < method_id_copy.size(); + ++index) { + set_id(method_id_copy[index], IndexToId(index)); + } + for (size_t index = kIsDebugBuild ? 0 : IdToIndex(field_start_id); index < field_id_copy.size(); + ++index) { + set_id(field_id_copy[index], IndexToId(index)); + } + WriterMutexLock mu(self, *Locks::jni_id_lock_); + DCHECK_GE(deferred_allocation_refcount_, 1u); + if (--deferred_allocation_refcount_ == 0) { + deferred_allocation_field_id_start_ = 0; + deferred_allocation_method_id_start_ = 0; + } +} + +ScopedEnableSuspendAllJniIdQueries::ScopedEnableSuspendAllJniIdQueries() + : manager_(Runtime::Current()->GetJniIdManager()) { + manager_->StartDefer(); +} + +ScopedEnableSuspendAllJniIdQueries::~ScopedEnableSuspendAllJniIdQueries() { + manager_->EndDefer(); +} + +}; // namespace jni +}; // namespace art diff --git a/runtime/jni/jni_id_manager.h b/runtime/jni/jni_id_manager.h new file mode 100644 index 0000000000..5e5c581be4 --- /dev/null +++ b/runtime/jni/jni_id_manager.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_JNI_JNI_ID_MANAGER_H_ +#define ART_RUNTIME_JNI_JNI_ID_MANAGER_H_ + +#include <atomic> +#include <jni.h> +#include <vector> + +#include "art_field.h" +#include "art_method.h" +#include "base/mutex.h" + +namespace art { +namespace jni { + +class ScopedEnableSuspendAllJniIdQueries; +class JniIdManager { + public: + ArtMethod* DecodeMethodId(jmethodID method) REQUIRES(!Locks::jni_id_lock_); + ArtField* DecodeFieldId(jfieldID field) REQUIRES(!Locks::jni_id_lock_); + jmethodID EncodeMethodId(ArtMethod* method) REQUIRES(!Locks::jni_id_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + jfieldID EncodeFieldId(ArtField* field) REQUIRES(!Locks::jni_id_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + + private: + template <typename ArtType> + uintptr_t EncodeGenericId(ArtType* t) REQUIRES(!Locks::jni_id_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + template <typename ArtType> + ArtType* DecodeGenericId(uintptr_t input) REQUIRES(!Locks::jni_id_lock_); + template <typename ArtType> std::vector<ArtType*>& GetGenericMap() REQUIRES(Locks::jni_id_lock_); + template <typename ArtType> uintptr_t GetNextId() REQUIRES(Locks::jni_id_lock_); + template <typename ArtType> + size_t GetLinearSearchStartId(ArtType* t) REQUIRES(Locks::jni_id_lock_); + + void StartDefer() REQUIRES(!Locks::jni_id_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + void EndDefer() REQUIRES(!Locks::jni_id_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + + uintptr_t next_method_id_ GUARDED_BY(Locks::jni_id_lock_) = 1u; + std::vector<ArtMethod*> method_id_map_ GUARDED_BY(Locks::jni_id_lock_); + uintptr_t next_field_id_ GUARDED_BY(Locks::jni_id_lock_) = 1u; + std::vector<ArtField*> field_id_map_ GUARDED_BY(Locks::jni_id_lock_); + + // If non-zero indicates that some thread is trying to allocate ids without being able to update + // the method->id mapping (due to not being able to allocate or something). In this case decode + // and encode need to do a linear scan of the lists. The ScopedEnableSuspendAllJniIdQueries struct + // will deal with fixing everything up. + size_t deferred_allocation_refcount_ GUARDED_BY(Locks::jni_id_lock_) = 0; + // min jmethodID that might not have it's method->id mapping filled in. + uintptr_t deferred_allocation_method_id_start_ GUARDED_BY(Locks::jni_id_lock_) = 0u; + // min jfieldID that might not have it's field->id mapping filled in. + uintptr_t deferred_allocation_field_id_start_ GUARDED_BY(Locks::jni_id_lock_) = 0u; + + friend class ScopedEnableSuspendAllJniIdQueries; +}; + +// A scope that will enable using the Encode/Decode JNI id functions with all threads suspended. +// This is required since normally we need to be able to allocate to encode new ids. This should +// only be used when absolutely required, for example to invoke user-callbacks during heap walking +// or similar. +class ScopedEnableSuspendAllJniIdQueries { + public: + ScopedEnableSuspendAllJniIdQueries() REQUIRES_SHARED(Locks::mutator_lock_); + ~ScopedEnableSuspendAllJniIdQueries() REQUIRES_SHARED(Locks::mutator_lock_); + + private: + JniIdManager* manager_; +}; + +} // namespace jni +} // namespace art + +#endif // ART_RUNTIME_JNI_JNI_ID_MANAGER_H_ diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc index af335f65ff..cd775d630f 100644 --- a/runtime/jni/jni_internal.cc +++ b/runtime/jni/jni_internal.cc @@ -428,6 +428,7 @@ static ObjPtr<mirror::Class> EnsureInitialized(Thread* self, ObjPtr<mirror::Clas return h_klass.Get(); } +template<bool kEnableIndexIds> static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class, const char* name, const char* sig, bool is_static) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -449,14 +450,16 @@ static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class, ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static"); return nullptr; } - return jni::EncodeArtMethod(method); + return jni::EncodeArtMethod<kEnableIndexIds>(method); } +template<bool kEnableIndexIds> static ObjPtr<mirror::ClassLoader> GetClassLoader(const ScopedObjectAccess& soa) REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr); // If we are running Runtime.nativeLoad, use the overriding ClassLoader it set. - if (method == jni::DecodeArtMethod(WellKnownClasses::java_lang_Runtime_nativeLoad)) { + if (method == + jni::DecodeArtMethod<kEnableIndexIds>(WellKnownClasses::java_lang_Runtime_nativeLoad)) { return soa.Decode<mirror::ClassLoader>(soa.Self()->GetClassLoaderOverride()); } // If we have a method, use its ClassLoader for context. @@ -482,6 +485,7 @@ static ObjPtr<mirror::ClassLoader> GetClassLoader(const ScopedObjectAccess& soa) return nullptr; } +template<bool kEnableIndexIds> static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, const char* name, const char* sig, bool is_static) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -534,7 +538,7 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con sig, name, c->GetDescriptor(&temp)); return nullptr; } - return jni::EncodeArtField(field); + return jni::EncodeArtField<kEnableIndexIds>(field); } static void ThrowAIOOBE(ScopedObjectAccess& soa, @@ -641,6 +645,7 @@ static ArtMethod* FindMethod(ObjPtr<mirror::Class> c, return nullptr; } +template <bool kEnableIndexIds> class JNI { public: static jint GetVersion(JNIEnv*) { @@ -661,7 +666,7 @@ class JNI { ObjPtr<mirror::Class> c = nullptr; if (runtime->IsStarted()) { StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetClassLoader(soa))); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetClassLoader<kEnableIndexIds>(soa))); c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader); } else { c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str()); @@ -672,7 +677,7 @@ class JNI { static jmethodID FromReflectedMethod(JNIEnv* env, jobject jlr_method) { CHECK_NON_NULL_ARGUMENT(jlr_method); ScopedObjectAccess soa(env); - return jni::EncodeArtMethod(ArtMethod::FromReflectedMethod(soa, jlr_method)); + return jni::EncodeArtMethod<kEnableIndexIds>(ArtMethod::FromReflectedMethod(soa, jlr_method)); } static jfieldID FromReflectedField(JNIEnv* env, jobject jlr_field) { @@ -684,7 +689,7 @@ class JNI { return nullptr; } ObjPtr<mirror::Field> field = ObjPtr<mirror::Field>::DownCast(obj_field); - return jni::EncodeArtField(field->GetArtField()); + return jni::EncodeArtField<kEnableIndexIds>(field->GetArtField()); } static jobject ToReflectedMethod(JNIEnv* env, jclass, jmethodID mid, jboolean) { @@ -935,7 +940,7 @@ class JNI { } if (c->IsStringClass()) { // Replace calls to String.<init> with equivalent StringFactory call. - jmethodID sf_mid = jni::EncodeArtMethod( + jmethodID sf_mid = jni::EncodeArtMethod<kEnableIndexIds>( WellKnownClasses::StringInitToStringFactory(jni::DecodeArtMethod(mid))); return CallStaticObjectMethodV(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args); } @@ -962,7 +967,7 @@ class JNI { } if (c->IsStringClass()) { // Replace calls to String.<init> with equivalent StringFactory call. - jmethodID sf_mid = jni::EncodeArtMethod( + jmethodID sf_mid = jni::EncodeArtMethod<kEnableIndexIds>( WellKnownClasses::StringInitToStringFactory(jni::DecodeArtMethod(mid))); return CallStaticObjectMethodA(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args); } @@ -983,7 +988,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT(name); CHECK_NON_NULL_ARGUMENT(sig); ScopedObjectAccess soa(env); - return FindMethodID(soa, java_class, name, sig, false); + return FindMethodID<kEnableIndexIds>(soa, java_class, name, sig, false); } static jmethodID GetStaticMethodID(JNIEnv* env, jclass java_class, const char* name, @@ -992,7 +997,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT(name); CHECK_NON_NULL_ARGUMENT(sig); ScopedObjectAccess soa(env); - return FindMethodID(soa, java_class, name, sig, true); + return FindMethodID<kEnableIndexIds>(soa, java_class, name, sig, true); } static jobject CallObjectMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { @@ -1524,7 +1529,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT(name); CHECK_NON_NULL_ARGUMENT(sig); ScopedObjectAccess soa(env); - return FindFieldID(soa, java_class, name, sig, false); + return FindFieldID<kEnableIndexIds>(soa, java_class, name, sig, false); } static jfieldID GetStaticFieldID(JNIEnv* env, jclass java_class, const char* name, @@ -1533,14 +1538,14 @@ class JNI { CHECK_NON_NULL_ARGUMENT(name); CHECK_NON_NULL_ARGUMENT(sig); ScopedObjectAccess soa(env); - return FindFieldID(soa, java_class, name, sig, true); + return FindFieldID<kEnableIndexIds>(soa, java_class, name, sig, true); } static jobject GetObjectField(JNIEnv* env, jobject obj, jfieldID fid) { CHECK_NON_NULL_ARGUMENT(obj); CHECK_NON_NULL_ARGUMENT(fid); ScopedObjectAccess soa(env); - ArtField* f = jni::DecodeArtField(fid); + ArtField* f = jni::DecodeArtField<kEnableIndexIds>(fid); NotifyGetField(f, obj); ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(obj); return soa.AddLocalReference<jobject>(f->GetObject(o)); @@ -1549,7 +1554,7 @@ class JNI { static jobject GetStaticObjectField(JNIEnv* env, jclass, jfieldID fid) { CHECK_NON_NULL_ARGUMENT(fid); ScopedObjectAccess soa(env); - ArtField* f = jni::DecodeArtField(fid); + ArtField* f = jni::DecodeArtField<kEnableIndexIds>(fid); NotifyGetField(f, nullptr); return soa.AddLocalReference<jobject>(f->GetObject(f->GetDeclaringClass())); } @@ -1558,7 +1563,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_object); CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); ScopedObjectAccess soa(env); - ArtField* f = jni::DecodeArtField(fid); + ArtField* f = jni::DecodeArtField<kEnableIndexIds>(fid); NotifySetObjectField(f, java_object, java_value); ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object); ObjPtr<mirror::Object> v = soa.Decode<mirror::Object>(java_value); @@ -1568,7 +1573,7 @@ class JNI { static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); ScopedObjectAccess soa(env); - ArtField* f = jni::DecodeArtField(fid); + ArtField* f = jni::DecodeArtField<kEnableIndexIds>(fid); NotifySetObjectField(f, nullptr, java_value); ObjPtr<mirror::Object> v = soa.Decode<mirror::Object>(java_value); f->SetObject<false>(f->GetDeclaringClass(), v); @@ -1578,7 +1583,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(instance); \ CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \ ScopedObjectAccess soa(env); \ - ArtField* f = jni::DecodeArtField(fid); \ + ArtField* f = jni::DecodeArtField<kEnableIndexIds>(fid); \ NotifyGetField(f, instance); \ ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \ return f->Get ##fn (o) @@ -1586,7 +1591,7 @@ class JNI { #define GET_STATIC_PRIMITIVE_FIELD(fn) \ CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \ ScopedObjectAccess soa(env); \ - ArtField* f = jni::DecodeArtField(fid); \ + ArtField* f = jni::DecodeArtField<kEnableIndexIds>(fid); \ NotifyGetField(f, nullptr); \ return f->Get ##fn (f->GetDeclaringClass()) @@ -1594,7 +1599,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT_RETURN_VOID(instance); \ CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \ ScopedObjectAccess soa(env); \ - ArtField* f = jni::DecodeArtField(fid); \ + ArtField* f = jni::DecodeArtField<kEnableIndexIds>(fid); \ NotifySetPrimitiveField(f, instance, JValue::FromPrimitive<decltype(value)>(value)); \ ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \ f->Set ##fn <false>(o, value) @@ -1602,7 +1607,7 @@ class JNI { #define SET_STATIC_PRIMITIVE_FIELD(fn, value) \ CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \ ScopedObjectAccess soa(env); \ - ArtField* f = jni::DecodeArtField(fid); \ + ArtField* f = jni::DecodeArtField<kEnableIndexIds>(fid); \ NotifySetPrimitiveField(f, nullptr, JValue::FromPrimitive<decltype(value)>(value)); \ f->Set ##fn <false>(f->GetDeclaringClass(), value) @@ -2881,244 +2886,253 @@ class JNI { } }; -const JNINativeInterface gJniNativeInterface = { - nullptr, // reserved0. - nullptr, // reserved1. - nullptr, // reserved2. - nullptr, // reserved3. - JNI::GetVersion, - JNI::DefineClass, - JNI::FindClass, - JNI::FromReflectedMethod, - JNI::FromReflectedField, - JNI::ToReflectedMethod, - JNI::GetSuperclass, - JNI::IsAssignableFrom, - JNI::ToReflectedField, - JNI::Throw, - JNI::ThrowNew, - JNI::ExceptionOccurred, - JNI::ExceptionDescribe, - JNI::ExceptionClear, - JNI::FatalError, - JNI::PushLocalFrame, - JNI::PopLocalFrame, - JNI::NewGlobalRef, - JNI::DeleteGlobalRef, - JNI::DeleteLocalRef, - JNI::IsSameObject, - JNI::NewLocalRef, - JNI::EnsureLocalCapacity, - JNI::AllocObject, - JNI::NewObject, - JNI::NewObjectV, - JNI::NewObjectA, - JNI::GetObjectClass, - JNI::IsInstanceOf, - JNI::GetMethodID, - JNI::CallObjectMethod, - JNI::CallObjectMethodV, - JNI::CallObjectMethodA, - JNI::CallBooleanMethod, - JNI::CallBooleanMethodV, - JNI::CallBooleanMethodA, - JNI::CallByteMethod, - JNI::CallByteMethodV, - JNI::CallByteMethodA, - JNI::CallCharMethod, - JNI::CallCharMethodV, - JNI::CallCharMethodA, - JNI::CallShortMethod, - JNI::CallShortMethodV, - JNI::CallShortMethodA, - JNI::CallIntMethod, - JNI::CallIntMethodV, - JNI::CallIntMethodA, - JNI::CallLongMethod, - JNI::CallLongMethodV, - JNI::CallLongMethodA, - JNI::CallFloatMethod, - JNI::CallFloatMethodV, - JNI::CallFloatMethodA, - JNI::CallDoubleMethod, - JNI::CallDoubleMethodV, - JNI::CallDoubleMethodA, - JNI::CallVoidMethod, - JNI::CallVoidMethodV, - JNI::CallVoidMethodA, - JNI::CallNonvirtualObjectMethod, - JNI::CallNonvirtualObjectMethodV, - JNI::CallNonvirtualObjectMethodA, - JNI::CallNonvirtualBooleanMethod, - JNI::CallNonvirtualBooleanMethodV, - JNI::CallNonvirtualBooleanMethodA, - JNI::CallNonvirtualByteMethod, - JNI::CallNonvirtualByteMethodV, - JNI::CallNonvirtualByteMethodA, - JNI::CallNonvirtualCharMethod, - JNI::CallNonvirtualCharMethodV, - JNI::CallNonvirtualCharMethodA, - JNI::CallNonvirtualShortMethod, - JNI::CallNonvirtualShortMethodV, - JNI::CallNonvirtualShortMethodA, - JNI::CallNonvirtualIntMethod, - JNI::CallNonvirtualIntMethodV, - JNI::CallNonvirtualIntMethodA, - JNI::CallNonvirtualLongMethod, - JNI::CallNonvirtualLongMethodV, - JNI::CallNonvirtualLongMethodA, - JNI::CallNonvirtualFloatMethod, - JNI::CallNonvirtualFloatMethodV, - JNI::CallNonvirtualFloatMethodA, - JNI::CallNonvirtualDoubleMethod, - JNI::CallNonvirtualDoubleMethodV, - JNI::CallNonvirtualDoubleMethodA, - JNI::CallNonvirtualVoidMethod, - JNI::CallNonvirtualVoidMethodV, - JNI::CallNonvirtualVoidMethodA, - JNI::GetFieldID, - JNI::GetObjectField, - JNI::GetBooleanField, - JNI::GetByteField, - JNI::GetCharField, - JNI::GetShortField, - JNI::GetIntField, - JNI::GetLongField, - JNI::GetFloatField, - JNI::GetDoubleField, - JNI::SetObjectField, - JNI::SetBooleanField, - JNI::SetByteField, - JNI::SetCharField, - JNI::SetShortField, - JNI::SetIntField, - JNI::SetLongField, - JNI::SetFloatField, - JNI::SetDoubleField, - JNI::GetStaticMethodID, - JNI::CallStaticObjectMethod, - JNI::CallStaticObjectMethodV, - JNI::CallStaticObjectMethodA, - JNI::CallStaticBooleanMethod, - JNI::CallStaticBooleanMethodV, - JNI::CallStaticBooleanMethodA, - JNI::CallStaticByteMethod, - JNI::CallStaticByteMethodV, - JNI::CallStaticByteMethodA, - JNI::CallStaticCharMethod, - JNI::CallStaticCharMethodV, - JNI::CallStaticCharMethodA, - JNI::CallStaticShortMethod, - JNI::CallStaticShortMethodV, - JNI::CallStaticShortMethodA, - JNI::CallStaticIntMethod, - JNI::CallStaticIntMethodV, - JNI::CallStaticIntMethodA, - JNI::CallStaticLongMethod, - JNI::CallStaticLongMethodV, - JNI::CallStaticLongMethodA, - JNI::CallStaticFloatMethod, - JNI::CallStaticFloatMethodV, - JNI::CallStaticFloatMethodA, - JNI::CallStaticDoubleMethod, - JNI::CallStaticDoubleMethodV, - JNI::CallStaticDoubleMethodA, - JNI::CallStaticVoidMethod, - JNI::CallStaticVoidMethodV, - JNI::CallStaticVoidMethodA, - JNI::GetStaticFieldID, - JNI::GetStaticObjectField, - JNI::GetStaticBooleanField, - JNI::GetStaticByteField, - JNI::GetStaticCharField, - JNI::GetStaticShortField, - JNI::GetStaticIntField, - JNI::GetStaticLongField, - JNI::GetStaticFloatField, - JNI::GetStaticDoubleField, - JNI::SetStaticObjectField, - JNI::SetStaticBooleanField, - JNI::SetStaticByteField, - JNI::SetStaticCharField, - JNI::SetStaticShortField, - JNI::SetStaticIntField, - JNI::SetStaticLongField, - JNI::SetStaticFloatField, - JNI::SetStaticDoubleField, - JNI::NewString, - JNI::GetStringLength, - JNI::GetStringChars, - JNI::ReleaseStringChars, - JNI::NewStringUTF, - JNI::GetStringUTFLength, - JNI::GetStringUTFChars, - JNI::ReleaseStringUTFChars, - JNI::GetArrayLength, - JNI::NewObjectArray, - JNI::GetObjectArrayElement, - JNI::SetObjectArrayElement, - JNI::NewBooleanArray, - JNI::NewByteArray, - JNI::NewCharArray, - JNI::NewShortArray, - JNI::NewIntArray, - JNI::NewLongArray, - JNI::NewFloatArray, - JNI::NewDoubleArray, - JNI::GetBooleanArrayElements, - JNI::GetByteArrayElements, - JNI::GetCharArrayElements, - JNI::GetShortArrayElements, - JNI::GetIntArrayElements, - JNI::GetLongArrayElements, - JNI::GetFloatArrayElements, - JNI::GetDoubleArrayElements, - JNI::ReleaseBooleanArrayElements, - JNI::ReleaseByteArrayElements, - JNI::ReleaseCharArrayElements, - JNI::ReleaseShortArrayElements, - JNI::ReleaseIntArrayElements, - JNI::ReleaseLongArrayElements, - JNI::ReleaseFloatArrayElements, - JNI::ReleaseDoubleArrayElements, - JNI::GetBooleanArrayRegion, - JNI::GetByteArrayRegion, - JNI::GetCharArrayRegion, - JNI::GetShortArrayRegion, - JNI::GetIntArrayRegion, - JNI::GetLongArrayRegion, - JNI::GetFloatArrayRegion, - JNI::GetDoubleArrayRegion, - JNI::SetBooleanArrayRegion, - JNI::SetByteArrayRegion, - JNI::SetCharArrayRegion, - JNI::SetShortArrayRegion, - JNI::SetIntArrayRegion, - JNI::SetLongArrayRegion, - JNI::SetFloatArrayRegion, - JNI::SetDoubleArrayRegion, - JNI::RegisterNatives, - JNI::UnregisterNatives, - JNI::MonitorEnter, - JNI::MonitorExit, - JNI::GetJavaVM, - JNI::GetStringRegion, - JNI::GetStringUTFRegion, - JNI::GetPrimitiveArrayCritical, - JNI::ReleasePrimitiveArrayCritical, - JNI::GetStringCritical, - JNI::ReleaseStringCritical, - JNI::NewWeakGlobalRef, - JNI::DeleteWeakGlobalRef, - JNI::ExceptionCheck, - JNI::NewDirectByteBuffer, - JNI::GetDirectBufferAddress, - JNI::GetDirectBufferCapacity, - JNI::GetObjectRefType, +template<bool kEnableIndexIds> +struct JniNativeInterfaceFunctions { + using JNIImpl = JNI<kEnableIndexIds>; + static constexpr JNINativeInterface gJniNativeInterface = { + nullptr, // reserved0. + nullptr, // reserved1. + nullptr, // reserved2. + nullptr, // reserved3. + JNIImpl::GetVersion, + JNIImpl::DefineClass, + JNIImpl::FindClass, + JNIImpl::FromReflectedMethod, + JNIImpl::FromReflectedField, + JNIImpl::ToReflectedMethod, + JNIImpl::GetSuperclass, + JNIImpl::IsAssignableFrom, + JNIImpl::ToReflectedField, + JNIImpl::Throw, + JNIImpl::ThrowNew, + JNIImpl::ExceptionOccurred, + JNIImpl::ExceptionDescribe, + JNIImpl::ExceptionClear, + JNIImpl::FatalError, + JNIImpl::PushLocalFrame, + JNIImpl::PopLocalFrame, + JNIImpl::NewGlobalRef, + JNIImpl::DeleteGlobalRef, + JNIImpl::DeleteLocalRef, + JNIImpl::IsSameObject, + JNIImpl::NewLocalRef, + JNIImpl::EnsureLocalCapacity, + JNIImpl::AllocObject, + JNIImpl::NewObject, + JNIImpl::NewObjectV, + JNIImpl::NewObjectA, + JNIImpl::GetObjectClass, + JNIImpl::IsInstanceOf, + JNIImpl::GetMethodID, + JNIImpl::CallObjectMethod, + JNIImpl::CallObjectMethodV, + JNIImpl::CallObjectMethodA, + JNIImpl::CallBooleanMethod, + JNIImpl::CallBooleanMethodV, + JNIImpl::CallBooleanMethodA, + JNIImpl::CallByteMethod, + JNIImpl::CallByteMethodV, + JNIImpl::CallByteMethodA, + JNIImpl::CallCharMethod, + JNIImpl::CallCharMethodV, + JNIImpl::CallCharMethodA, + JNIImpl::CallShortMethod, + JNIImpl::CallShortMethodV, + JNIImpl::CallShortMethodA, + JNIImpl::CallIntMethod, + JNIImpl::CallIntMethodV, + JNIImpl::CallIntMethodA, + JNIImpl::CallLongMethod, + JNIImpl::CallLongMethodV, + JNIImpl::CallLongMethodA, + JNIImpl::CallFloatMethod, + JNIImpl::CallFloatMethodV, + JNIImpl::CallFloatMethodA, + JNIImpl::CallDoubleMethod, + JNIImpl::CallDoubleMethodV, + JNIImpl::CallDoubleMethodA, + JNIImpl::CallVoidMethod, + JNIImpl::CallVoidMethodV, + JNIImpl::CallVoidMethodA, + JNIImpl::CallNonvirtualObjectMethod, + JNIImpl::CallNonvirtualObjectMethodV, + JNIImpl::CallNonvirtualObjectMethodA, + JNIImpl::CallNonvirtualBooleanMethod, + JNIImpl::CallNonvirtualBooleanMethodV, + JNIImpl::CallNonvirtualBooleanMethodA, + JNIImpl::CallNonvirtualByteMethod, + JNIImpl::CallNonvirtualByteMethodV, + JNIImpl::CallNonvirtualByteMethodA, + JNIImpl::CallNonvirtualCharMethod, + JNIImpl::CallNonvirtualCharMethodV, + JNIImpl::CallNonvirtualCharMethodA, + JNIImpl::CallNonvirtualShortMethod, + JNIImpl::CallNonvirtualShortMethodV, + JNIImpl::CallNonvirtualShortMethodA, + JNIImpl::CallNonvirtualIntMethod, + JNIImpl::CallNonvirtualIntMethodV, + JNIImpl::CallNonvirtualIntMethodA, + JNIImpl::CallNonvirtualLongMethod, + JNIImpl::CallNonvirtualLongMethodV, + JNIImpl::CallNonvirtualLongMethodA, + JNIImpl::CallNonvirtualFloatMethod, + JNIImpl::CallNonvirtualFloatMethodV, + JNIImpl::CallNonvirtualFloatMethodA, + JNIImpl::CallNonvirtualDoubleMethod, + JNIImpl::CallNonvirtualDoubleMethodV, + JNIImpl::CallNonvirtualDoubleMethodA, + JNIImpl::CallNonvirtualVoidMethod, + JNIImpl::CallNonvirtualVoidMethodV, + JNIImpl::CallNonvirtualVoidMethodA, + JNIImpl::GetFieldID, + JNIImpl::GetObjectField, + JNIImpl::GetBooleanField, + JNIImpl::GetByteField, + JNIImpl::GetCharField, + JNIImpl::GetShortField, + JNIImpl::GetIntField, + JNIImpl::GetLongField, + JNIImpl::GetFloatField, + JNIImpl::GetDoubleField, + JNIImpl::SetObjectField, + JNIImpl::SetBooleanField, + JNIImpl::SetByteField, + JNIImpl::SetCharField, + JNIImpl::SetShortField, + JNIImpl::SetIntField, + JNIImpl::SetLongField, + JNIImpl::SetFloatField, + JNIImpl::SetDoubleField, + JNIImpl::GetStaticMethodID, + JNIImpl::CallStaticObjectMethod, + JNIImpl::CallStaticObjectMethodV, + JNIImpl::CallStaticObjectMethodA, + JNIImpl::CallStaticBooleanMethod, + JNIImpl::CallStaticBooleanMethodV, + JNIImpl::CallStaticBooleanMethodA, + JNIImpl::CallStaticByteMethod, + JNIImpl::CallStaticByteMethodV, + JNIImpl::CallStaticByteMethodA, + JNIImpl::CallStaticCharMethod, + JNIImpl::CallStaticCharMethodV, + JNIImpl::CallStaticCharMethodA, + JNIImpl::CallStaticShortMethod, + JNIImpl::CallStaticShortMethodV, + JNIImpl::CallStaticShortMethodA, + JNIImpl::CallStaticIntMethod, + JNIImpl::CallStaticIntMethodV, + JNIImpl::CallStaticIntMethodA, + JNIImpl::CallStaticLongMethod, + JNIImpl::CallStaticLongMethodV, + JNIImpl::CallStaticLongMethodA, + JNIImpl::CallStaticFloatMethod, + JNIImpl::CallStaticFloatMethodV, + JNIImpl::CallStaticFloatMethodA, + JNIImpl::CallStaticDoubleMethod, + JNIImpl::CallStaticDoubleMethodV, + JNIImpl::CallStaticDoubleMethodA, + JNIImpl::CallStaticVoidMethod, + JNIImpl::CallStaticVoidMethodV, + JNIImpl::CallStaticVoidMethodA, + JNIImpl::GetStaticFieldID, + JNIImpl::GetStaticObjectField, + JNIImpl::GetStaticBooleanField, + JNIImpl::GetStaticByteField, + JNIImpl::GetStaticCharField, + JNIImpl::GetStaticShortField, + JNIImpl::GetStaticIntField, + JNIImpl::GetStaticLongField, + JNIImpl::GetStaticFloatField, + JNIImpl::GetStaticDoubleField, + JNIImpl::SetStaticObjectField, + JNIImpl::SetStaticBooleanField, + JNIImpl::SetStaticByteField, + JNIImpl::SetStaticCharField, + JNIImpl::SetStaticShortField, + JNIImpl::SetStaticIntField, + JNIImpl::SetStaticLongField, + JNIImpl::SetStaticFloatField, + JNIImpl::SetStaticDoubleField, + JNIImpl::NewString, + JNIImpl::GetStringLength, + JNIImpl::GetStringChars, + JNIImpl::ReleaseStringChars, + JNIImpl::NewStringUTF, + JNIImpl::GetStringUTFLength, + JNIImpl::GetStringUTFChars, + JNIImpl::ReleaseStringUTFChars, + JNIImpl::GetArrayLength, + JNIImpl::NewObjectArray, + JNIImpl::GetObjectArrayElement, + JNIImpl::SetObjectArrayElement, + JNIImpl::NewBooleanArray, + JNIImpl::NewByteArray, + JNIImpl::NewCharArray, + JNIImpl::NewShortArray, + JNIImpl::NewIntArray, + JNIImpl::NewLongArray, + JNIImpl::NewFloatArray, + JNIImpl::NewDoubleArray, + JNIImpl::GetBooleanArrayElements, + JNIImpl::GetByteArrayElements, + JNIImpl::GetCharArrayElements, + JNIImpl::GetShortArrayElements, + JNIImpl::GetIntArrayElements, + JNIImpl::GetLongArrayElements, + JNIImpl::GetFloatArrayElements, + JNIImpl::GetDoubleArrayElements, + JNIImpl::ReleaseBooleanArrayElements, + JNIImpl::ReleaseByteArrayElements, + JNIImpl::ReleaseCharArrayElements, + JNIImpl::ReleaseShortArrayElements, + JNIImpl::ReleaseIntArrayElements, + JNIImpl::ReleaseLongArrayElements, + JNIImpl::ReleaseFloatArrayElements, + JNIImpl::ReleaseDoubleArrayElements, + JNIImpl::GetBooleanArrayRegion, + JNIImpl::GetByteArrayRegion, + JNIImpl::GetCharArrayRegion, + JNIImpl::GetShortArrayRegion, + JNIImpl::GetIntArrayRegion, + JNIImpl::GetLongArrayRegion, + JNIImpl::GetFloatArrayRegion, + JNIImpl::GetDoubleArrayRegion, + JNIImpl::SetBooleanArrayRegion, + JNIImpl::SetByteArrayRegion, + JNIImpl::SetCharArrayRegion, + JNIImpl::SetShortArrayRegion, + JNIImpl::SetIntArrayRegion, + JNIImpl::SetLongArrayRegion, + JNIImpl::SetFloatArrayRegion, + JNIImpl::SetDoubleArrayRegion, + JNIImpl::RegisterNatives, + JNIImpl::UnregisterNatives, + JNIImpl::MonitorEnter, + JNIImpl::MonitorExit, + JNIImpl::GetJavaVM, + JNIImpl::GetStringRegion, + JNIImpl::GetStringUTFRegion, + JNIImpl::GetPrimitiveArrayCritical, + JNIImpl::ReleasePrimitiveArrayCritical, + JNIImpl::GetStringCritical, + JNIImpl::ReleaseStringCritical, + JNIImpl::NewWeakGlobalRef, + JNIImpl::DeleteWeakGlobalRef, + JNIImpl::ExceptionCheck, + JNIImpl::NewDirectByteBuffer, + JNIImpl::GetDirectBufferAddress, + JNIImpl::GetDirectBufferCapacity, + JNIImpl::GetObjectRefType, + }; }; const JNINativeInterface* GetJniNativeInterface() { - return &gJniNativeInterface; + // The template argument is passed down through the Encode/DecodeArtMethod/Field calls so if + // JniIdsAreIndices is false the calls will be a simple cast with no branches. This ensures that + // the normal case is still fast. + return Runtime::Current()->JniIdsAreIndices() + ? &JniNativeInterfaceFunctions<true>::gJniNativeInterface + : &JniNativeInterfaceFunctions<false>::gJniNativeInterface; } void (*gJniSleepForeverStub[])() = { diff --git a/runtime/jni/jni_internal.h b/runtime/jni/jni_internal.h index 4359074dbd..b6e106cc88 100644 --- a/runtime/jni/jni_internal.h +++ b/runtime/jni/jni_internal.h @@ -20,7 +20,9 @@ #include <jni.h> #include <iosfwd> +#include "base/locks.h" #include "base/macros.h" +#include "runtime.h" namespace art { @@ -41,24 +43,61 @@ void JniShutdownNativeCallerCheck(); namespace jni { +// We want to maintain a branchless fast-path for performance reasons. The JniIdManager is the +// ultimate source of truth for how the IDs are handed out but we inline the normal non-index cases +// here. + +template <bool kEnableIndexIds> +ALWAYS_INLINE +static bool IsIndexId(jmethodID mid) { + return kEnableIndexIds && ((reinterpret_cast<uintptr_t>(mid) % 2) != 0); +} + +template <bool kEnableIndexIds> +ALWAYS_INLINE +static bool IsIndexId(jfieldID fid) { + return kEnableIndexIds && ((reinterpret_cast<uintptr_t>(fid) % 2) != 0); +} + +template <bool kEnableIndexIds = true> ALWAYS_INLINE static inline ArtField* DecodeArtField(jfieldID fid) { - return reinterpret_cast<ArtField*>(fid); + if (IsIndexId<kEnableIndexIds>(fid)) { + return Runtime::Current()->GetJniIdManager()->DecodeFieldId(fid); + } else { + return reinterpret_cast<ArtField*>(fid); + } } +template <bool kEnableIndexIds = true> ALWAYS_INLINE -static inline jfieldID EncodeArtField(ArtField* field) { - return reinterpret_cast<jfieldID>(field); +static inline jfieldID EncodeArtField(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) { + if (kEnableIndexIds && Runtime::Current()->JniIdsAreIndices()) { + return Runtime::Current()->GetJniIdManager()->EncodeFieldId(field); + } else { + return reinterpret_cast<jfieldID>(field); + } } +template <bool kEnableIndexIds = true> ALWAYS_INLINE -static inline jmethodID EncodeArtMethod(ArtMethod* art_method) { - return reinterpret_cast<jmethodID>(art_method); +static inline jmethodID EncodeArtMethod(ArtMethod* art_method) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (kEnableIndexIds && Runtime::Current()->JniIdsAreIndices()) { + return Runtime::Current()->GetJniIdManager()->EncodeMethodId(art_method); + } else { + return reinterpret_cast<jmethodID>(art_method); + } } +template <bool kEnableIndexIds = true> ALWAYS_INLINE static inline ArtMethod* DecodeArtMethod(jmethodID method_id) { - return reinterpret_cast<ArtMethod*>(method_id); + if (IsIndexId<kEnableIndexIds>(method_id)) { + return Runtime::Current()->GetJniIdManager()->DecodeMethodId(method_id); + } else { + return reinterpret_cast<ArtMethod*>(method_id); + } } } // namespace jni diff --git a/runtime/jni_id_type.h b/runtime/jni_id_type.h new file mode 100644 index 0000000000..7802ec6c37 --- /dev/null +++ b/runtime/jni_id_type.h @@ -0,0 +1,38 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_JNI_ID_TYPE_H_ +#define ART_RUNTIME_JNI_ID_TYPE_H_ + +#include <iosfwd> + +namespace art { + +enum class JniIdType { + // All Jni method/field IDs are pointers to the corresponding Art{Field,Method} type + kPointer, + + // All Jni method/field IDs are indices into a table. + kIndices, + + // The current default provider. Used if you run -XjdwpProvider:default + kDefault = kPointer, +}; + +std::ostream& operator<<(std::ostream& os, const JniIdType& rhs); + +} // namespace art +#endif // ART_RUNTIME_JNI_ID_TYPE_H_ diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index a36fe1253c..c0a950d614 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -16,14 +16,17 @@ #include "class.h" +#include "android-base/macros.h" #include "android-base/stringprintf.h" +#include "array-inl.h" #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/enums.h" #include "base/logging.h" // For VLOG. #include "base/utils.h" #include "class-inl.h" -#include "class_ext.h" +#include "class_ext-inl.h" #include "class_linker-inl.h" #include "class_loader.h" #include "class_root.h" @@ -1547,5 +1550,126 @@ void Class::SetAccessFlagsDCheck(uint32_t new_access_flags) { (new_access_flags & kAccVerificationAttempted) != 0); } +ObjPtr<PointerArray> Class::GetMethodIds() { + ObjPtr<ClassExt> ext(GetExtData()); + if (ext.IsNull()) { + return nullptr; + } else { + return ext->GetJMethodIDs(); + } +} +ObjPtr<PointerArray> Class::GetOrCreateMethodIds() { + DCHECK(Runtime::Current()->JniIdsAreIndices()) << "JNI Ids are pointers!"; + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<Class> h_this(hs.NewHandle(this)); + ObjPtr<ClassExt> ext(EnsureExtDataPresent(h_this, self)); + if (ext.IsNull()) { + self->AssertPendingOOMException(); + return nullptr; + } + return ext->EnsureJMethodIDsArrayPresent(NumMethods()); +} + +ObjPtr<PointerArray> Class::GetStaticFieldIds() { + ObjPtr<ClassExt> ext(GetExtData()); + if (ext.IsNull()) { + return nullptr; + } else { + return ext->GetStaticJFieldIDs(); + } +} +ObjPtr<PointerArray> Class::GetOrCreateStaticFieldIds() { + DCHECK(Runtime::Current()->JniIdsAreIndices()) << "JNI Ids are pointers!"; + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<Class> h_this(hs.NewHandle(this)); + ObjPtr<ClassExt> ext(EnsureExtDataPresent(h_this, self)); + if (ext.IsNull()) { + self->AssertPendingOOMException(); + return nullptr; + } + return ext->EnsureStaticJFieldIDsArrayPresent(NumStaticFields()); +} +ObjPtr<PointerArray> Class::GetInstanceFieldIds() { + ObjPtr<ClassExt> ext(GetExtData()); + if (ext.IsNull()) { + return nullptr; + } else { + return ext->GetInstanceJFieldIDs(); + } +} +ObjPtr<PointerArray> Class::GetOrCreateInstanceFieldIds() { + DCHECK(Runtime::Current()->JniIdsAreIndices()) << "JNI Ids are pointers!"; + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<Class> h_this(hs.NewHandle(this)); + ObjPtr<ClassExt> ext(EnsureExtDataPresent(h_this, self)); + if (ext.IsNull()) { + self->AssertPendingOOMException(); + return nullptr; + } + return ext->EnsureInstanceJFieldIDsArrayPresent(NumInstanceFields()); +} + +size_t Class::GetStaticFieldIdOffset(ArtField* field) { + DCHECK_LT(reinterpret_cast<uintptr_t>(field), + reinterpret_cast<uintptr_t>(&*GetSFieldsPtr()->end())) + << "field not part of the current class. " << field->PrettyField() << " class is " + << PrettyClass(); + DCHECK_GE(reinterpret_cast<uintptr_t>(field), + reinterpret_cast<uintptr_t>(&*GetSFieldsPtr()->begin())) + << "field not part of the current class. " << field->PrettyField() << " class is " + << PrettyClass(); + uintptr_t start = reinterpret_cast<uintptr_t>(&GetSFieldsPtr()->At(0)); + uintptr_t fld = reinterpret_cast<uintptr_t>(field); + size_t res = (fld - start) / sizeof(ArtField); + DCHECK_EQ(&GetSFieldsPtr()->At(res), field) + << "Incorrect field computation expected: " << field->PrettyField() + << " got: " << GetSFieldsPtr()->At(res).PrettyField(); + return res; +} + +size_t Class::GetInstanceFieldIdOffset(ArtField* field) { + DCHECK_LT(reinterpret_cast<uintptr_t>(field), + reinterpret_cast<uintptr_t>(&*GetIFieldsPtr()->end())) + << "field not part of the current class. " << field->PrettyField() << " class is " + << PrettyClass(); + DCHECK_GE(reinterpret_cast<uintptr_t>(field), + reinterpret_cast<uintptr_t>(&*GetIFieldsPtr()->begin())) + << "field not part of the current class. " << field->PrettyField() << " class is " + << PrettyClass(); + uintptr_t start = reinterpret_cast<uintptr_t>(&GetIFieldsPtr()->At(0)); + uintptr_t fld = reinterpret_cast<uintptr_t>(field); + size_t res = (fld - start) / sizeof(ArtField); + DCHECK_EQ(&GetIFieldsPtr()->At(res), field) + << "Incorrect field computation expected: " << field->PrettyField() + << " got: " << GetIFieldsPtr()->At(res).PrettyField(); + return res; +} + +size_t Class::GetMethodIdOffset(ArtMethod* method, PointerSize pointer_size) { + DCHECK(GetMethodsSlice(kRuntimePointerSize).Contains(method)) + << "method not part of the current class. " << method->PrettyMethod() << "( " << reinterpret_cast<void*>(method) << ")" << " class is " + << PrettyClass() << [&]() REQUIRES_SHARED(Locks::mutator_lock_) { + std::ostringstream os; + os << " Methods are ["; + for (ArtMethod& m : GetMethodsSlice(kRuntimePointerSize)) { + os << m.PrettyMethod() << "( " << reinterpret_cast<void*>(&m) << "), "; + } + os << "]"; + return os.str(); + }(); + uintptr_t start = reinterpret_cast<uintptr_t>(&*GetMethodsSlice(pointer_size).begin()); + uintptr_t fld = reinterpret_cast<uintptr_t>(method); + size_t art_method_size = ArtMethod::Size(pointer_size); + size_t art_method_align = ArtMethod::Alignment(pointer_size); + size_t res = (fld - start) / art_method_size; + DCHECK_EQ(&GetMethodsPtr()->At(res, art_method_size, art_method_align), method) + << "Incorrect method computation expected: " << method->PrettyMethod() + << " got: " << GetMethodsPtr()->At(res, art_method_size, art_method_align).PrettyMethod(); + return res; +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 09d5532deb..144350f02e 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -1229,6 +1229,23 @@ class MANAGED Class final : public Object { void FixupNativePointers(Class* dest, PointerSize pointer_size, const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); + // Get or create the various jni id arrays in a lock-less thread safe manner. + ObjPtr<PointerArray> GetOrCreateMethodIds() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<PointerArray> GetMethodIds() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<PointerArray> GetOrCreateStaticFieldIds() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<PointerArray> GetStaticFieldIds() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<PointerArray> GetOrCreateInstanceFieldIds() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<PointerArray> GetInstanceFieldIds() REQUIRES_SHARED(Locks::mutator_lock_); + + // Calculate the index in the ifields_, methods_ or sfields_ arrays a method is located at. This + // is to be used with the above Get{,OrCreate}...Ids functions. + size_t GetStaticFieldIdOffset(ArtField* field) + REQUIRES_SHARED(Locks::mutator_lock_); + size_t GetInstanceFieldIdOffset(ArtField* field) + REQUIRES_SHARED(Locks::mutator_lock_); + size_t GetMethodIdOffset(ArtMethod* method, PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_); + private: template <typename T, VerifyObjectFlags kVerifyFlags, typename Visitor> void FixupNativePointer( diff --git a/runtime/mirror/class_ext-inl.h b/runtime/mirror/class_ext-inl.h index bf51654447..ead02eed3b 100644 --- a/runtime/mirror/class_ext-inl.h +++ b/runtime/mirror/class_ext-inl.h @@ -19,12 +19,81 @@ #include "class_ext.h" +#include "array-inl.h" #include "art_method-inl.h" +#include "handle_scope.h" #include "object-inl.h" namespace art { namespace mirror { +template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> +inline ObjPtr<PointerArray> ClassExt::EnsureJniIdsArrayPresent(MemberOffset off, size_t count) { + ObjPtr<PointerArray> existing( + GetFieldObject<PointerArray, kVerifyFlags, kReadBarrierOption>(off)); + if (!existing.IsNull()) { + return existing; + } + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); + Handle<ClassExt> h_this(hs.NewHandle(this)); + Handle<PointerArray> new_arr( + hs.NewHandle(Runtime::Current()->GetClassLinker()->AllocPointerArray(self, count))); + if (new_arr.IsNull()) { + // Fail. + self->AssertPendingOOMException(); + return nullptr; + } + bool set; + // Set the ext_data_ field using CAS semantics. + if (Runtime::Current()->IsActiveTransaction()) { + set = h_this->CasFieldObject<true>( + off, nullptr, new_arr.Get(), CASMode::kStrong, std::memory_order_seq_cst); + } else { + set = h_this->CasFieldObject<false>( + off, nullptr, new_arr.Get(), CASMode::kStrong, std::memory_order_seq_cst); + } + ObjPtr<PointerArray> ret( + set ? new_arr.Get() + : h_this->GetFieldObject<PointerArray, kVerifyFlags, kReadBarrierOption>(off)); + CHECK(!ret.IsNull()); + return ret; +} + +template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> +inline ObjPtr<PointerArray> ClassExt::EnsureJMethodIDsArrayPresent(size_t count) { + return EnsureJniIdsArrayPresent<kVerifyFlags, kReadBarrierOption>( + MemberOffset(OFFSET_OF_OBJECT_MEMBER(ClassExt, jmethod_ids_)), count); +} +template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> +inline ObjPtr<PointerArray> ClassExt::EnsureStaticJFieldIDsArrayPresent(size_t count) { + return EnsureJniIdsArrayPresent<kVerifyFlags, kReadBarrierOption>( + MemberOffset(OFFSET_OF_OBJECT_MEMBER(ClassExt, static_jfield_ids_)), count); +} +template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> +inline ObjPtr<PointerArray> ClassExt::EnsureInstanceJFieldIDsArrayPresent(size_t count) { + return EnsureJniIdsArrayPresent<kVerifyFlags, kReadBarrierOption>( + MemberOffset(OFFSET_OF_OBJECT_MEMBER(ClassExt, instance_jfield_ids_)), count); +} + +template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> +inline ObjPtr<PointerArray> ClassExt::GetInstanceJFieldIDs() { + return GetFieldObject<PointerArray, kVerifyFlags, kReadBarrierOption>( + OFFSET_OF_OBJECT_MEMBER(ClassExt, instance_jfield_ids_)); +} + +template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> +inline ObjPtr<PointerArray> ClassExt::GetStaticJFieldIDs() { + return GetFieldObject<PointerArray, kVerifyFlags, kReadBarrierOption>( + OFFSET_OF_OBJECT_MEMBER(ClassExt, static_jfield_ids_)); +} + +template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> +inline ObjPtr<PointerArray> ClassExt::GetJMethodIDs() { + return GetFieldObject<PointerArray, kVerifyFlags, kReadBarrierOption>( + OFFSET_OF_OBJECT_MEMBER(ClassExt, jmethod_ids_)); +} + inline ObjPtr<Object> ClassExt::GetVerifyError() { return GetFieldObject<ClassExt>(OFFSET_OF_OBJECT_MEMBER(ClassExt, verify_error_)); } diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h index 70bea33c1f..6fb225fd69 100644 --- a/runtime/mirror/class_ext.h +++ b/runtime/mirror/class_ext.h @@ -48,6 +48,33 @@ class MANAGED ClassExt : public Object { template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + ObjPtr<PointerArray> EnsureInstanceJFieldIDsArrayPresent(size_t count) + REQUIRES_SHARED(Locks::mutator_lock_); + + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + ObjPtr<PointerArray> GetInstanceJFieldIDs() REQUIRES_SHARED(Locks::mutator_lock_); + + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + ObjPtr<PointerArray> EnsureStaticJFieldIDsArrayPresent(size_t count) + REQUIRES_SHARED(Locks::mutator_lock_); + + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + ObjPtr<PointerArray> GetStaticJFieldIDs() REQUIRES_SHARED(Locks::mutator_lock_); + + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + ObjPtr<PointerArray> EnsureJMethodIDsArrayPresent(size_t count) + REQUIRES_SHARED(Locks::mutator_lock_); + + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + ObjPtr<PointerArray> GetJMethodIDs() REQUIRES_SHARED(Locks::mutator_lock_); + + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ObjPtr<PointerArray> GetObsoleteMethods() REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr<Object> GetOriginalDexFile() REQUIRES_SHARED(Locks::mutator_lock_); @@ -82,19 +109,36 @@ class MANAGED ClassExt : public Object { static ObjPtr<ClassExt> Alloc(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); private: + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + ObjPtr<PointerArray> EnsureJniIdsArrayPresent(MemberOffset off, size_t count) + REQUIRES_SHARED(Locks::mutator_lock_); + // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". + // An array containing the jfieldIDs assigned to each field in the corresponding position in the + // classes ifields_ array or '0' if no id has been assigned to that field yet. + HeapReference<PointerArray> instance_jfield_ids_; + + // An array containing the jmethodIDs assigned to each method in the corresponding position in + // the classes methods_ array or '0' if no id has been assigned to that method yet. + HeapReference<PointerArray> jmethod_ids_; + HeapReference<ObjectArray<DexCache>> obsolete_dex_caches_; HeapReference<PointerArray> obsolete_methods_; HeapReference<Object> original_dex_file_; + // An array containing the jfieldIDs assigned to each field in the corresponding position in the + // classes sfields_ array or '0' if no id has been assigned to that field yet. + HeapReference<PointerArray> static_jfield_ids_; + // The saved verification error of this class. HeapReference<Object> verify_error_; // Native pointer to DexFile and ClassDef index of this class before it was JVMTI-redefined. - int64_t pre_redefine_dex_file_ptr_; int32_t pre_redefine_class_def_index_; + int64_t pre_redefine_dex_file_ptr_; friend struct art::ClassExtOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(ClassExt); diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index a5e9d95ea6..afcf40d7b5 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -367,6 +367,10 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .WithType<bool>() .WithValueMap({{"false", false}, {"true", true}}) .IntoKey(M::FastClassNotFoundException) + .Define("-Xopaque-jni-ids:_") + .WithType<bool>() + .WithValueMap({{"true", true}, {"false", false}}) + .IntoKey(M::OpaqueJniIds) .Ignore({ "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa", "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_", @@ -784,6 +788,7 @@ void ParsedOptions::Usage(const char* fmt, ...) { "(Enable new and experimental agent support)\n"); UsageMessage(stream, " -Xexperimental:agents" "(Enable new and experimental agent support)\n"); + UsageMessage(stream, " -Xopaque-jni-ids:{true,false} (Use opauque integers for jni ids)\n"); UsageMessage(stream, "\n"); UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n"); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 12fda4382b..ad20575af7 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -94,7 +94,9 @@ #include "jit/jit_code_cache.h" #include "jit/profile_saver.h" #include "jni/java_vm_ext.h" +#include "jni/jni_id_manager.h" #include "jni/jni_internal.h" +#include "jni_id_type.h" #include "linear_alloc.h" #include "memory_representation.h" #include "mirror/array.h" @@ -1181,6 +1183,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { oat_file_manager_ = new OatFileManager; + jni_id_manager_ = new jni::JniIdManager; + Thread::SetSensitiveThreadHook(runtime_options.GetOrDefault(Opt::HookIsSensitiveThread)); Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold), runtime_options.GetOrDefault(Opt::StackDumpLockProfThreshold)); @@ -1302,6 +1306,13 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode); madvise_random_access_ = runtime_options.GetOrDefault(Opt::MadviseRandomAccess); + if (!runtime_options.Exists(Opt::OpaqueJniIds)) { + jni_ids_indirection_ = JniIdType::kDefault; + } else { + jni_ids_indirection_ = *runtime_options.Get(Opt::OpaqueJniIds) ? JniIdType::kIndices + : JniIdType::kPointer; + } + plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins); agent_specs_ = runtime_options.ReleaseOrDefault(Opt::AgentPath); // TODO Add back in -agentlib diff --git a/runtime/runtime.h b/runtime/runtime.h index 6e27a9f31a..65e3541aea 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -37,6 +37,8 @@ #include "gc_root.h" #include "instrumentation.h" #include "jdwp_provider.h" +#include "jni/jni_id_manager.h" +#include "jni_id_type.h" #include "obj_ptr.h" #include "offsets.h" #include "process_state.h" @@ -279,6 +281,10 @@ class Runtime { return class_linker_; } + jni::JniIdManager* GetJniIdManager() const { + return jni_id_manager_; + } + size_t GetDefaultStackSize() const { return default_stack_size_; } @@ -834,6 +840,10 @@ class Runtime { return jdwp_provider_; } + bool JniIdsAreIndices() const { + return jni_ids_indirection_ != JniIdType::kPointer; + } + uint32_t GetVerifierLoggingThresholdMs() const { return verifier_logging_threshold_ms_; } @@ -1005,6 +1015,8 @@ class Runtime { SignalCatcher* signal_catcher_; + jni::JniIdManager* jni_id_manager_; + std::unique_ptr<JavaVMExt> java_vm_; std::unique_ptr<jit::Jit> jit_; @@ -1188,6 +1200,10 @@ class Runtime { // The jdwp provider we were configured with. JdwpProvider jdwp_provider_; + // True if jmethodID and jfieldID are opaque Indices. When false (the default) these are simply + // pointers. This is set by -Xopaque-jni-ids:{true,false}. + JniIdType jni_ids_indirection_; + // Saved environment. class EnvSnapshot { public: diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 43a722937c..d2594b2040 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -77,6 +77,7 @@ RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true) RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, true) RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true) RUNTIME_OPTIONS_KEY (bool, MadviseRandomAccess, false) +RUNTIME_OPTIONS_KEY (bool, OpaqueJniIds, false) // -Xopaque-jni-ids:{true, false} RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITOsrThreshold) diff --git a/runtime/thread.cc b/runtime/thread.cc index 3e83f654b5..d5aea19e87 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -4280,8 +4280,16 @@ ScopedExceptionStorage::ScopedExceptionStorage(art::Thread* self) self_->ClearException(); } +void ScopedExceptionStorage::SuppressOldException(const char* message) { + CHECK(self_->IsExceptionPending()) << *self_; + ObjPtr<mirror::Throwable> old_suppressed(excp_.Get()); + excp_.Assign(self_->GetException()); + LOG(WARNING) << message << "Suppressing old exception: " << old_suppressed->Dump(); + self_->ClearException(); +} + ScopedExceptionStorage::~ScopedExceptionStorage() { - CHECK(!self_->IsExceptionPending()) << self_; + CHECK(!self_->IsExceptionPending()) << *self_; if (!excp_.IsNull()) { self_->SetException(excp_.Get()); } diff --git a/runtime/thread.h b/runtime/thread.h index 8fe9466112..9a230e2380 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1901,12 +1901,13 @@ class ThreadLifecycleCallback { class ScopedExceptionStorage { public: explicit ScopedExceptionStorage(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + void SuppressOldException(const char* message = "") REQUIRES_SHARED(Locks::mutator_lock_); ~ScopedExceptionStorage() REQUIRES_SHARED(Locks::mutator_lock_); private: Thread* self_; StackHandleScope<1> hs_; - Handle<mirror::Throwable> excp_; + MutableHandle<mirror::Throwable> excp_; }; std::ostream& operator<<(std::ostream& os, const Thread& thread); diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index ac1b2c6af4..379b2c6fbc 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -435,7 +435,7 @@ def run_tests(tests): options_test += ' --no-image' if debuggable == 'debuggable': - options_test += ' --debuggable' + options_test += ' --debuggable --runtime-option -Xopaque-jni-ids:true' if jvmti == 'jvmti-stress': options_test += ' --jvmti-trace-stress --jvmti-redefine-stress --jvmti-field-stress' @@ -797,7 +797,7 @@ def parse_test_name(test_name): It supports two types of test_name: 1) Like 001-HelloWorld. In this case, it will just verify if the test actually exists and if it does, it returns the testname. - 2) Like test-art-host-run-test-debug-prebuild-interpreter-no-relocate-ntrace-cms-checkjni-picimage-ndebuggable-001-HelloWorld32 + 2) Like test-art-host-run-test-debug-prebuild-interpreter-no-relocate-ntrace-cms-checkjni-pointer-ids-picimage-ndebuggable-001-HelloWorld32 In this case, it will parse all the variants and check if they are placed correctly. If yes, it will set the various VARIANT_TYPES to use the variants required to run the test. Again, it returns the test_name |