diff options
author | 2016-11-18 16:03:10 +0000 | |
---|---|---|
committer | 2016-11-22 13:08:29 +0000 | |
commit | 340dafabc8e88378e395cda9027cf17726910e91 (patch) | |
tree | f742cfc9b9eb3fdf0245a66491d39fb841da7c01 | |
parent | 71d15102b52af67e8fe1193192aa2b4cd1956ae0 (diff) |
Use a per-thread VerifierDeps.
Avoid lock contention on a singleton VerifierDeps by allocating
temporary per-thread VerifierDeps that get merged after verification.
This saves around ~35% compile-times on interpret-only.
Only the creation of extra strings is guarded by a lock, for simplicity.
Test: test-art-host, test-art-target
bug: 32641252
bug: 30937355
Change-Id: I11a2367da882b58e39afa7b42cba2e74a209b75d
-rw-r--r-- | compiler/driver/compiler_driver.cc | 21 | ||||
-rw-r--r-- | compiler/verifier_deps_test.cc | 12 | ||||
-rw-r--r-- | runtime/base/mutex.cc | 4 | ||||
-rw-r--r-- | runtime/base/mutex.h | 4 | ||||
-rw-r--r-- | runtime/base/stl_util.h | 7 | ||||
-rw-r--r-- | runtime/entrypoints_order_test.cc | 4 | ||||
-rw-r--r-- | runtime/thread.cc | 2 | ||||
-rw-r--r-- | runtime/thread.h | 37 | ||||
-rw-r--r-- | runtime/thread_pool.cc | 1 | ||||
-rw-r--r-- | runtime/thread_pool.h | 7 | ||||
-rw-r--r-- | runtime/verifier/verifier_deps.cc | 136 | ||||
-rw-r--r-- | runtime/verifier/verifier_deps.h | 79 |
12 files changed, 195 insertions, 119 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index c62e2142b7..e155e106f8 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1980,8 +1980,16 @@ void CompilerDriver::Verify(jobject jclass_loader, // non boot image compilation. The verifier will need it to record the new dependencies. // Then dex2oat can update the vdex file with these new dependencies. if (!GetCompilerOptions().IsBootImage()) { + // Create the main VerifierDeps, and set it to this thread. Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps( new verifier::VerifierDeps(dex_files)); + Thread::Current()->SetVerifierDeps( + Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps()); + // Create per-thread VerifierDeps to avoid contention on the main one. + // We will merge them after verification. + for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) { + worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files)); + } } // Note: verification should not be pulling in classes anymore when compiling the boot image, // as all should have been resolved before. As such, doing this in parallel should still @@ -1995,6 +2003,19 @@ void CompilerDriver::Verify(jobject jclass_loader, parallel_thread_count_, timings); } + + if (!GetCompilerOptions().IsBootImage()) { + verifier::VerifierDeps* main_deps = + Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps(); + // Merge all VerifierDeps into the main one. + for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) { + verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps(); + worker->GetThread()->SetVerifierDeps(nullptr); + main_deps->MergeWith(*thread_deps, dex_files);; + delete thread_deps; + } + Thread::Current()->SetVerifierDeps(nullptr); + } } class VerifyClassVisitor : public CompilationVisitor { diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 03d3f4e290..dcf3619722 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -154,6 +154,7 @@ class VerifierDepsTest : public CommonCompilerTest { } CHECK(method != nullptr); + Thread::Current()->SetVerifierDeps(callbacks_->GetVerifierDeps()); MethodVerifier verifier(Thread::Current(), primary_dex_file_, dex_cache_handle, @@ -169,6 +170,7 @@ class VerifierDepsTest : public CommonCompilerTest { false /* verify to dump */, true /* allow_thread_suspension */); verifier.Verify(); + Thread::Current()->SetVerifierDeps(nullptr); return !verifier.HasFailures(); } @@ -230,7 +232,6 @@ class VerifierDepsTest : public CommonCompilerTest { const DexFile::TypeId* type_id = primary_dex_file_->FindTypeId(cls.c_str()); DCHECK(type_id != nullptr); dex::TypeIndex index = primary_dex_file_->GetIndexForTypeId(*type_id); - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (const auto& dex_dep : verifier_deps_->dex_deps_) { for (dex::TypeIndex entry : dex_dep.second->unverified_classes_) { if (index == entry) { @@ -246,7 +247,6 @@ class VerifierDepsTest : public CommonCompilerTest { bool HasAssignable(const std::string& expected_destination, const std::string& expected_source, bool expected_is_assignable) { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (auto& dex_dep : verifier_deps_->dex_deps_) { const DexFile& dex_file = *dex_dep.first; auto& storage = expected_is_assignable ? dex_dep.second->assignable_types_ @@ -268,7 +268,6 @@ class VerifierDepsTest : public CommonCompilerTest { bool HasClass(const std::string& expected_klass, bool expected_resolved, const std::string& expected_access_flags = "") { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (auto& dex_dep : verifier_deps_->dex_deps_) { for (auto& entry : dex_dep.second->classes_) { if (expected_resolved != entry.IsResolved()) { @@ -303,7 +302,6 @@ class VerifierDepsTest : public CommonCompilerTest { bool expected_resolved, const std::string& expected_access_flags = "", const std::string& expected_decl_klass = "") { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (auto& dex_dep : verifier_deps_->dex_deps_) { for (auto& entry : dex_dep.second->fields_) { if (expected_resolved != entry.IsResolved()) { @@ -357,7 +355,6 @@ class VerifierDepsTest : public CommonCompilerTest { bool expected_resolved, const std::string& expected_access_flags = "", const std::string& expected_decl_klass = "") { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (auto& dex_dep : verifier_deps_->dex_deps_) { auto& storage = (expected_kind == "direct") ? dex_dep.second->direct_methods_ : (expected_kind == "virtual") ? dex_dep.second->virtual_methods_ @@ -406,13 +403,10 @@ class VerifierDepsTest : public CommonCompilerTest { } size_t NumberOfCompiledDexFiles() { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); return verifier_deps_->dex_deps_.size(); } size_t HasEachKindOfRecord() { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); - bool has_strings = false; bool has_assignability = false; bool has_classes = false; @@ -463,8 +457,6 @@ TEST_F(VerifierDepsTest, StringToId) { ScopedObjectAccess soa(Thread::Current()); LoadDexFile(&soa); - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); - uint32_t id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;"); ASSERT_LT(id_Main1, primary_dex_file_->NumStringIds()); ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main1)); diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 5d922989d9..0970abda52 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -48,7 +48,7 @@ Mutex* Locks::mem_maps_lock_ = nullptr; Mutex* Locks::modify_ldt_lock_ = nullptr; MutatorMutex* Locks::mutator_lock_ = nullptr; Mutex* Locks::profiler_lock_ = nullptr; -Mutex* Locks::verifier_deps_lock_ = nullptr; +ReaderWriterMutex* Locks::verifier_deps_lock_ = nullptr; ReaderWriterMutex* Locks::oat_file_manager_lock_ = nullptr; Mutex* Locks::host_dlopen_handles_lock_ = nullptr; Mutex* Locks::reference_processor_lock_ = nullptr; @@ -1039,7 +1039,7 @@ void Locks::Init() { UPDATE_CURRENT_LOCK_LEVEL(kVerifierDepsLock); DCHECK(verifier_deps_lock_ == nullptr); - verifier_deps_lock_ = new Mutex("verifier deps lock", current_lock_level); + verifier_deps_lock_ = new ReaderWriterMutex("verifier deps lock", current_lock_level); UPDATE_CURRENT_LOCK_LEVEL(kHostDlOpenHandlesLock); DCHECK(host_dlopen_handles_lock_ == nullptr); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 74b786c38e..7e73e0dbd9 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -658,8 +658,8 @@ class Locks { // Guards opened oat files in OatFileManager. static ReaderWriterMutex* oat_file_manager_lock_ ACQUIRED_AFTER(modify_ldt_lock_); - // Guards verifier dependency collection in VerifierDeps. - static Mutex* verifier_deps_lock_ ACQUIRED_AFTER(oat_file_manager_lock_); + // Guards extra string entries for VerifierDeps. + static ReaderWriterMutex* verifier_deps_lock_ ACQUIRED_AFTER(oat_file_manager_lock_); // Guards dlopen_handles_ in DlOpenOatFile. static Mutex* host_dlopen_handles_lock_ ACQUIRED_AFTER(verifier_deps_lock_); diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h index a53dcea2d7..d5f375a5d9 100644 --- a/runtime/base/stl_util.h +++ b/runtime/base/stl_util.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_BASE_STL_UTIL_H_ #include <algorithm> +#include <set> #include <sstream> #include "base/logging.h" @@ -187,6 +188,12 @@ struct Identity { using type = T; }; +// Merge `other` entries into `to_update`. +template <typename T> +static inline void MergeSets(std::set<T>& to_update, const std::set<T>& other) { + to_update.insert(other.begin(), other.end()); +} + } // namespace art #endif // ART_RUNTIME_BASE_STL_UTIL_H_ diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index b0463d7f11..12836602d5 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -94,8 +94,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, opeer, jpeer, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jpeer, stack_begin, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_begin, stack_size, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_size, stack_trace_sample, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_trace_sample, wait_next, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_size, deps_or_stack_trace_sample, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, deps_or_stack_trace_sample, wait_next, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, wait_next, monitor_enter_object, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, monitor_enter_object, top_handle_scope, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, top_handle_scope, class_loader_override, sizeof(void*)); diff --git a/runtime/thread.cc b/runtime/thread.cc index b99df26706..65c86815b5 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1900,7 +1900,7 @@ Thread::~Thread() { } delete tlsPtr_.instrumentation_stack; delete tlsPtr_.name; - delete tlsPtr_.stack_trace_sample; + delete tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample; free(tlsPtr_.nested_signal_state); Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this); diff --git a/runtime/thread.h b/runtime/thread.h index b2983cc6de..3f13db1856 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -70,7 +70,8 @@ namespace mirror { } // namespace mirror namespace verifier { -class MethodVerifier; + class MethodVerifier; + class VerifierDeps; } // namespace verifier class ArtMethod; @@ -947,11 +948,25 @@ class Thread { } std::vector<ArtMethod*>* GetStackTraceSample() const { - return tlsPtr_.stack_trace_sample; + return tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample; } void SetStackTraceSample(std::vector<ArtMethod*>* sample) { - tlsPtr_.stack_trace_sample = sample; + DCHECK(sample == nullptr || tlsPtr_.deps_or_stack_trace_sample.verifier_deps == nullptr); + tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample = sample; + } + + verifier::VerifierDeps* GetVerifierDeps() const { + return tlsPtr_.deps_or_stack_trace_sample.verifier_deps; + } + + // It is the responsability of the caller to make sure the verifier_deps + // entry in the thread is cleared before destruction of the actual VerifierDeps + // object, or the thread. + void SetVerifierDeps(verifier::VerifierDeps* verifier_deps) { + DCHECK(verifier_deps == nullptr || + tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample == nullptr); + tlsPtr_.deps_or_stack_trace_sample.verifier_deps = verifier_deps; } uint64_t GetTraceClockBase() const { @@ -1378,7 +1393,7 @@ class Thread { tls_ptr_sized_values() : card_table(nullptr), exception(nullptr), stack_end(nullptr), managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), tmp_jni_env(nullptr), self(nullptr), opeer(nullptr), jpeer(nullptr), stack_begin(nullptr), stack_size(0), - stack_trace_sample(nullptr), wait_next(nullptr), monitor_enter_object(nullptr), + deps_or_stack_trace_sample(), wait_next(nullptr), monitor_enter_object(nullptr), top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr), instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr), stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr), @@ -1432,8 +1447,18 @@ class Thread { // Size of the stack. size_t stack_size; - // Pointer to previous stack trace captured by sampling profiler. - std::vector<ArtMethod*>* stack_trace_sample; + // Sampling profiler and AOT verification cannot happen on the same run, so we share + // the same entry for the stack trace and the verifier deps. + union DepsOrStackTraceSample { + DepsOrStackTraceSample() { + verifier_deps = nullptr; + stack_trace_sample = nullptr; + } + // Pointer to previous stack trace captured by sampling profiler. + std::vector<ArtMethod*>* stack_trace_sample; + // When doing AOT verification, per-thread VerifierDeps. + verifier::VerifierDeps* verifier_deps; + } deps_or_stack_trace_sample; // The next thread in the wait set this thread is part of or null if not waiting. Thread* wait_next; diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index 65fd99985b..d9d2ea31a2 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -85,6 +85,7 @@ void* ThreadPoolWorker::Callback(void* arg) { ThreadPoolWorker* worker = reinterpret_cast<ThreadPoolWorker*>(arg); Runtime* runtime = Runtime::Current(); CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, nullptr, false)); + worker->thread_ = Thread::Current(); // Do work until its time to shut down. worker->Run(); runtime->DetachCurrentThread(); diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h index 2ff33a6053..eaadfe0215 100644 --- a/runtime/thread_pool.h +++ b/runtime/thread_pool.h @@ -62,6 +62,8 @@ class ThreadPoolWorker { // Set the "nice" priorty for this worker. void SetPthreadPriority(int priority); + Thread* GetThread() const { return thread_; } + protected: ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name, size_t stack_size); static void* Callback(void* arg) REQUIRES(!Locks::mutator_lock_); @@ -71,6 +73,7 @@ class ThreadPoolWorker { const std::string name_; std::unique_ptr<MemMap> stack_; pthread_t pthread_; + Thread* thread_; private: friend class ThreadPool; @@ -84,6 +87,10 @@ class ThreadPool { return threads_.size(); } + const std::vector<ThreadPoolWorker*>& GetWorkers() const { + return threads_; + } + // Broadcast to the workers and tell them to empty out the work queue. void StartWorkers(Thread* self) REQUIRES(!task_queue_lock_); diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index a65e82b3df..fefe5a3faa 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -18,6 +18,7 @@ #include <cstring> +#include "base/stl_util.h" #include "compiler_callbacks.h" #include "leb128.h" #include "mirror/class-inl.h" @@ -28,7 +29,6 @@ namespace art { namespace verifier { VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files) { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (const DexFile* dex_file : dex_files) { DCHECK(GetDexFileDeps(*dex_file) == nullptr); std::unique_ptr<DexFileDeps> deps(new DexFileDeps()); @@ -36,6 +36,28 @@ VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files) { } } +void VerifierDeps::MergeWith(const VerifierDeps& other, + const std::vector<const DexFile*>& dex_files) { + DCHECK(dex_deps_.size() == other.dex_deps_.size()); + for (const DexFile* dex_file : dex_files) { + DexFileDeps* my_deps = GetDexFileDeps(*dex_file); + const DexFileDeps& other_deps = *other.GetDexFileDeps(*dex_file); + // We currently collect extra strings only on the main `VerifierDeps`, + // which should be the one passed as `this` in this method. + DCHECK(other_deps.strings_.empty()); + MergeSets(my_deps->assignable_types_, other_deps.assignable_types_); + MergeSets(my_deps->unassignable_types_, other_deps.unassignable_types_); + MergeSets(my_deps->classes_, other_deps.classes_); + MergeSets(my_deps->fields_, other_deps.fields_); + MergeSets(my_deps->direct_methods_, other_deps.direct_methods_); + MergeSets(my_deps->virtual_methods_, other_deps.virtual_methods_); + MergeSets(my_deps->interface_methods_, other_deps.interface_methods_); + for (dex::TypeIndex entry : other_deps.unverified_classes_) { + my_deps->unverified_classes_.push_back(entry); + } + } +} + VerifierDeps::DexFileDeps* VerifierDeps::GetDexFileDeps(const DexFile& dex_file) { auto it = dex_deps_.find(&dex_file); return (it == dex_deps_.end()) ? nullptr : it->second.get(); @@ -134,6 +156,38 @@ uint32_t VerifierDeps::GetFieldDeclaringClassStringId(const DexFile& dex_file, return GetClassDescriptorStringId(dex_file, field->GetDeclaringClass()); } +static inline VerifierDeps* GetMainVerifierDeps() { + // The main VerifierDeps is the one set in the compiler callbacks, which at the + // end of verification will have all the per-thread VerifierDeps merged into it. + CompilerCallbacks* callbacks = Runtime::Current()->GetCompilerCallbacks(); + if (callbacks == nullptr) { + return nullptr; + } + return callbacks->GetVerifierDeps(); +} + +static inline VerifierDeps* GetThreadLocalVerifierDeps() { + // During AOT, each thread has its own VerifierDeps, to avoid lock contention. At the end + // of full verification, these VerifierDeps will be merged into the main one. + if (!Runtime::Current()->IsAotCompiler()) { + return nullptr; + } + return Thread::Current()->GetVerifierDeps(); +} + +static bool FindExistingStringId(const std::vector<std::string>& strings, + const std::string& str, + uint32_t* found_id) { + uint32_t num_extra_ids = strings.size(); + for (size_t i = 0; i < num_extra_ids; ++i) { + if (strings[i] == str) { + *found_id = i; + return true; + } + } + return false; +} + uint32_t VerifierDeps::GetIdFromString(const DexFile& dex_file, const std::string& str) { const DexFile::StringId* string_id = dex_file.FindStringId(str.c_str()); if (string_id != nullptr) { @@ -144,25 +198,32 @@ uint32_t VerifierDeps::GetIdFromString(const DexFile& dex_file, const std::strin // String is not in the DEX file. Assign a new ID to it which is higher than // the number of strings in the DEX file. - DexFileDeps* deps = GetDexFileDeps(dex_file); + // We use the main `VerifierDeps` for adding new strings to simplify + // synchronization/merging of these entries between threads. + VerifierDeps* singleton = GetMainVerifierDeps(); + DexFileDeps* deps = singleton->GetDexFileDeps(dex_file); DCHECK(deps != nullptr); uint32_t num_ids_in_dex = dex_file.NumStringIds(); - uint32_t num_extra_ids = deps->strings_.size(); + uint32_t found_id; - for (size_t i = 0; i < num_extra_ids; ++i) { - if (deps->strings_[i] == str) { - return num_ids_in_dex + i; + { + ReaderMutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + if (FindExistingStringId(deps->strings_, str, &found_id)) { + return num_ids_in_dex + found_id; } } - - deps->strings_.push_back(str); - - uint32_t new_id = num_ids_in_dex + num_extra_ids; - CHECK_GE(new_id, num_ids_in_dex); // check for overflows - DCHECK_EQ(str, GetStringFromId(dex_file, new_id)); - - return new_id; + { + WriterMutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + if (FindExistingStringId(deps->strings_, str, &found_id)) { + return num_ids_in_dex + found_id; + } + deps->strings_.push_back(str); + uint32_t new_id = num_ids_in_dex + deps->strings_.size() - 1; + CHECK_GE(new_id, num_ids_in_dex); // check for overflows + DCHECK_EQ(str, singleton->GetStringFromId(dex_file, new_id)); + return new_id; + } } std::string VerifierDeps::GetStringFromId(const DexFile& dex_file, uint32_t string_id) const { @@ -216,7 +277,6 @@ void VerifierDeps::AddClassResolution(const DexFile& dex_file, return; } - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); dex_deps->classes_.emplace(ClassResolution(type_idx, GetAccessFlags(klass))); } @@ -235,7 +295,6 @@ void VerifierDeps::AddFieldResolution(const DexFile& dex_file, return; } - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); dex_deps->fields_.emplace(FieldResolution(field_idx, GetAccessFlags(field), GetFieldDeclaringClassStringId(dex_file, @@ -259,7 +318,6 @@ void VerifierDeps::AddMethodResolution(const DexFile& dex_file, return; } - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); MethodResolution method_tuple(method_idx, GetAccessFlags(method), GetMethodDeclaringClassStringId(dex_file, method_idx, method)); @@ -328,8 +386,6 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file, return; } - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); - // Get string IDs for both descriptors and store in the appropriate set. uint32_t destination_id = GetClassDescriptorStringId(dex_file, destination); uint32_t source_id = GetClassDescriptorStringId(dex_file, source); @@ -341,14 +397,6 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file, } } -static inline VerifierDeps* GetVerifierDepsSingleton() { - CompilerCallbacks* callbacks = Runtime::Current()->GetCompilerCallbacks(); - if (callbacks == nullptr) { - return nullptr; - } - return callbacks->GetVerifierDeps(); -} - void VerifierDeps::MaybeRecordVerificationStatus(const DexFile& dex_file, dex::TypeIndex type_idx, MethodVerifier::FailureKind failure_kind) { @@ -357,10 +405,9 @@ void VerifierDeps::MaybeRecordVerificationStatus(const DexFile& dex_file, return; } - VerifierDeps* singleton = GetVerifierDepsSingleton(); - if (singleton != nullptr) { - DexFileDeps* dex_deps = singleton->GetDexFileDeps(dex_file); - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); + if (thread_deps != nullptr) { + DexFileDeps* dex_deps = thread_deps->GetDexFileDeps(dex_file); dex_deps->unverified_classes_.push_back(type_idx); } } @@ -368,18 +415,18 @@ void VerifierDeps::MaybeRecordVerificationStatus(const DexFile& dex_file, void VerifierDeps::MaybeRecordClassResolution(const DexFile& dex_file, dex::TypeIndex type_idx, mirror::Class* klass) { - VerifierDeps* singleton = GetVerifierDepsSingleton(); - if (singleton != nullptr) { - singleton->AddClassResolution(dex_file, type_idx, klass); + VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); + if (thread_deps != nullptr) { + thread_deps->AddClassResolution(dex_file, type_idx, klass); } } void VerifierDeps::MaybeRecordFieldResolution(const DexFile& dex_file, uint32_t field_idx, ArtField* field) { - VerifierDeps* singleton = GetVerifierDepsSingleton(); - if (singleton != nullptr) { - singleton->AddFieldResolution(dex_file, field_idx, field); + VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); + if (thread_deps != nullptr) { + thread_deps->AddFieldResolution(dex_file, field_idx, field); } } @@ -387,9 +434,9 @@ void VerifierDeps::MaybeRecordMethodResolution(const DexFile& dex_file, uint32_t method_idx, MethodResolutionKind resolution_kind, ArtMethod* method) { - VerifierDeps* singleton = GetVerifierDepsSingleton(); - if (singleton != nullptr) { - singleton->AddMethodResolution(dex_file, method_idx, resolution_kind, method); + VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); + if (thread_deps != nullptr) { + thread_deps->AddMethodResolution(dex_file, method_idx, resolution_kind, method); } } @@ -398,9 +445,9 @@ void VerifierDeps::MaybeRecordAssignability(const DexFile& dex_file, mirror::Class* source, bool is_strict, bool is_assignable) { - VerifierDeps* singleton = GetVerifierDepsSingleton(); - if (singleton != nullptr) { - singleton->AddAssignability(dex_file, destination, source, is_strict, is_assignable); + VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); + if (thread_deps != nullptr) { + thread_deps->AddAssignability(dex_file, destination, source, is_strict, is_assignable); } } @@ -533,7 +580,6 @@ static inline void DecodeStringVector(const uint8_t** in, void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files, std::vector<uint8_t>* buffer) const { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); for (const DexFile* dex_file : dex_files) { const DexFileDeps& deps = *GetDexFileDeps(*dex_file); EncodeStringVector(buffer, deps.strings_); @@ -575,8 +621,6 @@ VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, } bool VerifierDeps::Equals(const VerifierDeps& rhs) const { - MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); - if (dex_deps_.size() != rhs.dex_deps_.size()) { return false; } diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index e35af41e3f..a12071b8b6 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -42,18 +42,19 @@ namespace verifier { // which are being compiled. Classes defined in DEX files outside of this set // (or synthesized classes without associated DEX files) are considered being // in the classpath. -// During code-flow verification, the MethodVerifier informs the VerifierDeps -// singleton about the outcome of every resolution and assignability test, and -// the singleton records them if their outcome may change with changes in the -// classpath. +// During code-flow verification, the MethodVerifier informs VerifierDeps +// about the outcome of every resolution and assignability test, and +// the VerifierDeps object records them if their outcome may change with +// changes in the classpath. class VerifierDeps { public: - explicit VerifierDeps(const std::vector<const DexFile*>& dex_files) - REQUIRES(!Locks::verifier_deps_lock_); + explicit VerifierDeps(const std::vector<const DexFile*>& dex_files); - VerifierDeps(const std::vector<const DexFile*>& dex_files, - ArrayRef<const uint8_t> data) - REQUIRES(!Locks::verifier_deps_lock_); + VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<const uint8_t> data); + + // Merge `other` into this `VerifierDeps`'. `other` and `this` must be for the + // same set of dex files. + void MergeWith(const VerifierDeps& other, const std::vector<const DexFile*>& dex_files); // Record the verification status of the class at `type_idx`. static void MaybeRecordVerificationStatus(const DexFile& dex_file, @@ -101,23 +102,15 @@ class VerifierDeps { // Serialize the recorded dependencies and store the data into `buffer`. // `dex_files` provides the order of the dex files in which the dependencies // should be emitted. - void Encode(const std::vector<const DexFile*>& dex_files, std::vector<uint8_t>* buffer) const - REQUIRES(!Locks::verifier_deps_lock_); + void Encode(const std::vector<const DexFile*>& dex_files, std::vector<uint8_t>* buffer) const; - // NO_THREAD_SAFETY_ANALYSIS as Dump iterates over dex_deps_, which is guarded by - // verifier_deps_lock_, but we expect Dump to be called once the deps collection is done. - void Dump(VariableIndentationOutputStream* vios) const - NO_THREAD_SAFETY_ANALYSIS; + void Dump(VariableIndentationOutputStream* vios) const; // Verify the encoded dependencies of this `VerifierDeps` are still valid. - // NO_THREAD_SAFETY_ANALYSIS, as this must be called on a read-only `VerifierDeps`. bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader, Thread* self) const - NO_THREAD_SAFETY_ANALYSIS; + REQUIRES_SHARED(Locks::mutator_lock_); - // NO_THREAD_SAFETY_ANALSYS, as this is queried when the VerifierDeps are - // fully created. - const std::vector<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const - NO_THREAD_SAFETY_ANALYSIS { + const std::vector<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const { return GetDexFileDeps(dex_file)->unverified_classes_; } @@ -200,13 +193,9 @@ class VerifierDeps { // Finds the DexFileDep instance associated with `dex_file`, or nullptr if // `dex_file` is not reported as being compiled. - // We disable thread safety analysis. The method only reads the key set of - // `dex_deps_` which stays constant after initialization. - DexFileDeps* GetDexFileDeps(const DexFile& dex_file) - NO_THREAD_SAFETY_ANALYSIS; + DexFileDeps* GetDexFileDeps(const DexFile& dex_file); - const DexFileDeps* GetDexFileDeps(const DexFile& dex_file) const - NO_THREAD_SAFETY_ANALYSIS; + const DexFileDeps* GetDexFileDeps(const DexFile& dex_file) const; // Returns true if `klass` is null or not defined in any of dex files which // were reported as being compiled. @@ -218,11 +207,10 @@ class VerifierDeps { // of the corresponding DexFileDeps structure (either provided or inferred from // `dex_file`). uint32_t GetIdFromString(const DexFile& dex_file, const std::string& str) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES(!Locks::verifier_deps_lock_); // Returns the string represented by `id`. - std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id) const - REQUIRES(Locks::verifier_deps_lock_); + std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id) const; // Returns the bytecode access flags of `element` (bottom 16 bits), or // `kUnresolvedMarker` if `element` is null. @@ -235,18 +223,16 @@ class VerifierDeps { uint32_t GetMethodDeclaringClassStringId(const DexFile& dex_file, uint32_t dex_method_idx, ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); uint32_t GetFieldDeclaringClassStringId(const DexFile& dex_file, uint32_t dex_field_idx, ArtField* field) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); // Returns a string ID of the descriptor of the class. uint32_t GetClassDescriptorStringId(const DexFile& dex_file, ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES(!Locks::verifier_deps_lock_); void AddClassResolution(const DexFile& dex_file, dex::TypeIndex type_idx, @@ -272,11 +258,9 @@ class VerifierDeps { mirror::Class* source, bool is_strict, bool is_assignable) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); - bool Equals(const VerifierDeps& rhs) const - REQUIRES(!Locks::verifier_deps_lock_); + bool Equals(const VerifierDeps& rhs) const; // Verify `dex_file` according to the `deps`, that is going over each // `DexFileDeps` field, and checking that the recorded information still @@ -285,16 +269,14 @@ class VerifierDeps { const DexFile& dex_file, const DexFileDeps& deps, Thread* self) const - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); bool VerifyAssignability(Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, const std::set<TypeAssignability>& assignables, bool expected_assignability, Thread* self) const - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); // Verify that the set of resolved classes at the point of creation // of this `VerifierDeps` is still the same. @@ -302,8 +284,7 @@ class VerifierDeps { const DexFile& dex_file, const std::set<ClassResolution>& classes, Thread* self) const - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); // Verify that the set of resolved fields at the point of creation // of this `VerifierDeps` is still the same, and each field resolves to the @@ -313,7 +294,7 @@ class VerifierDeps { const std::set<FieldResolution>& classes, Thread* self) const REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES(!Locks::verifier_deps_lock_); // Verify that the set of resolved methods at the point of creation // of this `VerifierDeps` is still the same, and each method resolves to the @@ -323,12 +304,10 @@ class VerifierDeps { const std::set<MethodResolution>& methods, MethodResolutionKind kind, Thread* self) const - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(Locks::verifier_deps_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); // Map from DexFiles into dependencies collected from verification of their methods. - std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_ - GUARDED_BY(Locks::verifier_deps_lock_); + std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_; friend class VerifierDepsTest; ART_FRIEND_TEST(VerifierDepsTest, StringToId); |