From 274fe4adcb0610a9920be7814d9beb9cac6417ce Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 12 Apr 2016 16:33:24 +0100 Subject: [PATCH] Remove the JIT from the instrumentation framework. This was slowing down the interpreter for no reason. Also, call AddSamples for invoke-static and invoke-direct. Change-Id: I7b5641097f7741dd32feb1ce6af739fd27fb37c2 --- runtime/Android.mk | 1 - runtime/asm_support.h | 2 +- runtime/interpreter/interpreter.cc | 19 +- runtime/interpreter/interpreter_common.h | 17 ++ runtime/interpreter/interpreter_goto_table_impl.cc | 13 +- runtime/interpreter/interpreter_switch_impl.cc | 13 +- runtime/interpreter/mterp/mterp.cc | 17 +- runtime/jit/jit.cc | 215 +++++++++++++++-- runtime/jit/jit.h | 50 +++- runtime/jit/jit_instrumentation.cc | 262 --------------------- runtime/jit/jit_instrumentation.h | 142 ----------- runtime/runtime.cc | 11 +- test/141-class-unload/jni_unload.cc | 3 +- 13 files changed, 285 insertions(+), 480 deletions(-) delete mode 100644 runtime/jit/jit_instrumentation.cc delete mode 100644 runtime/jit/jit_instrumentation.h diff --git a/runtime/Android.mk b/runtime/Android.mk index c85907987..aa12c83ce 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -106,7 +106,6 @@ LIBART_COMMON_SRC_FILES := \ jit/debugger_interface.cc \ jit/jit.cc \ jit/jit_code_cache.cc \ - jit/jit_instrumentation.cc \ jit/offline_profiling_info.cc \ jit/profiling_info.cc \ jit/profile_saver.cc \ diff --git a/runtime/asm_support.h b/runtime/asm_support.h index d27d2f6c9..21725d327 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -20,7 +20,7 @@ #if defined(__cplusplus) #include "art_method.h" #include "gc/allocator/rosalloc.h" -#include "jit/jit_instrumentation.h" +#include "jit/jit.h" #include "lock_word.h" #include "mirror/class.h" #include "mirror/string.h" diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index baf4afea1..a43278228 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -285,16 +285,19 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item, } jit::Jit* jit = Runtime::Current()->GetJit(); - if (jit != nullptr && jit->CanInvokeCompiledCode(method)) { - JValue result; + if (jit != nullptr) { + jit->MethodEntered(self, shadow_frame.GetMethod()); + if (jit->CanInvokeCompiledCode(method)) { + JValue result; - // Pop the shadow frame before calling into compiled code. - self->PopShadowFrame(); - ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result); - // Push the shadow frame back as the caller will expect it. - self->PushShadowFrame(&shadow_frame); + // Pop the shadow frame before calling into compiled code. + self->PopShadowFrame(); + ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result); + // Push the shadow frame back as the caller will expect it. + self->PushShadowFrame(&shadow_frame); - return result; + return result; + } } } diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 19d971ead..fb9817514 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -34,6 +34,7 @@ #include "dex_instruction-inl.h" #include "entrypoints/entrypoint_utils-inl.h" #include "handle_scope-inl.h" +#include "jit/jit.h" #include "lambda/art_lambda_method.h" #include "lambda/box_table.h" #include "lambda/closure.h" @@ -628,6 +629,15 @@ static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instr result->SetJ(0); return false; } else { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + if (type == kVirtual || type == kInterface) { + jit->InvokeVirtualOrInterface( + self, receiver, sf_method, shadow_frame.GetDexPC(), called_method); + } + jit->AddSamples(self, sf_method, 1); + } + // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT. if (type == kVirtual || type == kInterface) { instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) { @@ -667,7 +677,14 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, result->SetJ(0); return false; } else { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + jit->InvokeVirtualOrInterface( + self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method); + jit->AddSamples(self, shadow_frame.GetMethod(), 1); + } instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT. if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) { instrumentation->InvokeVirtualOrInterface( self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method); diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index ce698fb68..c95af6f0f 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -22,7 +22,6 @@ #include "experimental_flags.h" #include "interpreter_common.h" #include "jit/jit.h" -#include "jit/jit_instrumentation.h" #include "safe_math.h" #include // std::unique_ptr @@ -67,7 +66,9 @@ namespace interpreter { #define BRANCH_INSTRUMENTATION(offset) \ do { \ - instrumentation->Branch(self, method, dex_pc, offset); \ + if (UNLIKELY(instrumentation->HasBranchListeners())) { \ + instrumentation->Branch(self, method, dex_pc, offset); \ + } \ JValue result; \ if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) { \ return result; \ @@ -76,8 +77,8 @@ namespace interpreter { #define HOTNESS_UPDATE() \ do { \ - if (jit_instrumentation_cache != nullptr) { \ - jit_instrumentation_cache->AddSamples(self, method, 1); \ + if (jit != nullptr) { \ + jit->AddSamples(self, method, 1); \ } \ } while (false) @@ -195,10 +196,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF const auto* const instrumentation = Runtime::Current()->GetInstrumentation(); ArtMethod* method = shadow_frame.GetMethod(); jit::Jit* jit = Runtime::Current()->GetJit(); - jit::JitInstrumentationCache* jit_instrumentation_cache = nullptr; - if (jit != nullptr) { - jit_instrumentation_cache = jit->GetInstrumentationCache(); - } // Jump to first instruction. ADVANCE(0); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 442e1915f..ca1d635d5 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -18,7 +18,6 @@ #include "experimental_flags.h" #include "interpreter_common.h" #include "jit/jit.h" -#include "jit/jit_instrumentation.h" #include "safe_math.h" #include // std::unique_ptr @@ -74,7 +73,9 @@ namespace interpreter { #define BRANCH_INSTRUMENTATION(offset) \ do { \ - instrumentation->Branch(self, method, dex_pc, offset); \ + if (UNLIKELY(instrumentation->HasBranchListeners())) { \ + instrumentation->Branch(self, method, dex_pc, offset); \ + } \ JValue result; \ if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) { \ if (interpret_one_instruction) { \ @@ -87,8 +88,8 @@ namespace interpreter { #define HOTNESS_UPDATE() \ do { \ - if (jit_instrumentation_cache != nullptr) { \ - jit_instrumentation_cache->AddSamples(self, method, 1); \ + if (jit != nullptr) { \ + jit->AddSamples(self, method, 1); \ } \ } while (false) @@ -115,10 +116,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, uint16_t inst_data; ArtMethod* method = shadow_frame.GetMethod(); jit::Jit* jit = Runtime::Current()->GetJit(); - jit::JitInstrumentationCache* jit_instrumentation_cache = nullptr; - if (jit != nullptr) { - jit_instrumentation_cache = jit->GetInstrumentationCache(); - } // TODO: collapse capture-variable+create-lambda into one opcode, then we won't need // to keep this live for the scope of the entire function call. diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 32c45fca7..f80068354 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -20,8 +20,6 @@ #include "interpreter/interpreter_common.h" #include "entrypoints/entrypoint_utils-inl.h" #include "mterp.h" -#include "jit/jit.h" -#include "jit/jit_instrumentation.h" #include "debugger.h" namespace art { @@ -652,10 +650,9 @@ extern "C" int MterpSetUpHotnessCountdown(ArtMethod* method, ShadowFrame* shadow int32_t countdown_value = jit::kJitHotnessDisabled; jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr) { - jit::JitInstrumentationCache* cache = jit->GetInstrumentationCache(); - int32_t warm_threshold = cache->WarmMethodThreshold(); - int32_t hot_threshold = cache->HotMethodThreshold(); - int32_t osr_threshold = cache->OSRMethodThreshold(); + int32_t warm_threshold = jit->WarmMethodThreshold(); + int32_t hot_threshold = jit->HotMethodThreshold(); + int32_t osr_threshold = jit->OSRMethodThreshold(); if (hotness_count < warm_threshold) { countdown_value = warm_threshold - hotness_count; } else if (hotness_count < hot_threshold) { @@ -666,7 +663,7 @@ extern "C" int MterpSetUpHotnessCountdown(ArtMethod* method, ShadowFrame* shadow countdown_value = jit::kJitCheckForOSR; } if (jit::Jit::ShouldUsePriorityThreadWeight()) { - int32_t priority_thread_weight = cache->PriorityThreadWeight(); + int32_t priority_thread_weight = jit->PriorityThreadWeight(); countdown_value = std::min(countdown_value, countdown_value / priority_thread_weight); } } @@ -692,7 +689,7 @@ extern "C" int16_t MterpAddHotnessBatch(ArtMethod* method, jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr) { int16_t count = shadow_frame->GetCachedHotnessCountdown() - shadow_frame->GetHotnessCountdown(); - jit->GetInstrumentationCache()->AddSamples(self, method, count); + jit->AddSamples(self, method, count); } return MterpSetUpHotnessCountdown(method, shadow_frame); } @@ -705,7 +702,7 @@ extern "C" bool MterpProfileBranch(Thread* self, ShadowFrame* shadow_frame, int uint32_t dex_pc = shadow_frame->GetDexPC(); jit::Jit* jit = Runtime::Current()->GetJit(); if ((jit != nullptr) && (offset <= 0)) { - jit->GetInstrumentationCache()->AddSamples(self, method, 1); + jit->AddSamples(self, method, 1); } int16_t countdown_value = MterpSetUpHotnessCountdown(method, shadow_frame); if (countdown_value == jit::kJitCheckForOSR) { @@ -725,7 +722,7 @@ extern "C" bool MterpMaybeDoOnStackReplacement(Thread* self, jit::Jit* jit = Runtime::Current()->GetJit(); if (offset <= 0) { // Keep updating hotness in case a compilation request was dropped. Eventually it will retry. - jit->GetInstrumentationCache()->AddSamples(self, method, 1); + jit->AddSamples(self, method, 1); } // Assumes caller has already determined that an OSR check is appropriate. return jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, result); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 3344346f0..4ba90c12c 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -23,7 +23,6 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" #include "jit_code_cache.h" -#include "jit_instrumentation.h" #include "oat_file_manager.h" #include "oat_quick_method_header.h" #include "offline_profiling_info.h" @@ -31,12 +30,15 @@ #include "runtime.h" #include "runtime_options.h" #include "stack_map.h" +#include "thread_list.h" #include "utils.h" namespace art { namespace jit { static constexpr bool kEnableOnStackReplacement = true; +// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. +static constexpr int kJitPoolThreadPthreadPriority = 9; // JIT compiler void* Jit::jit_library_handle_= nullptr; @@ -146,6 +148,16 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity()) << ", compile_threshold=" << options->GetCompileThreshold() << ", save_profiling_info=" << options->GetSaveProfilingInfo(); + + + jit->hot_method_threshold_ = options->GetCompileThreshold(); + jit->warm_method_threshold_ = options->GetWarmupThreshold(); + jit->osr_method_threshold_ = options->GetOsrThreshold(); + + jit->CreateThreadPool(); + + // Notify native debugger about the classes already loaded before the creation of the jit. + jit->DumpTypeInfoForLoadedTypes(Runtime::Current()->GetClassLinker()); return jit.release(); } @@ -233,13 +245,31 @@ bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { } void Jit::CreateThreadPool() { - CHECK(instrumentation_cache_.get() != nullptr); - instrumentation_cache_->CreateThreadPool(); + // There is a DCHECK in the 'AddSamples' method to ensure the tread pool + // is not null when we instrument. + thread_pool_.reset(new ThreadPool("Jit thread pool", 1)); + thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority); + thread_pool_->StartWorkers(Thread::Current()); } void Jit::DeleteThreadPool() { - if (instrumentation_cache_.get() != nullptr) { - instrumentation_cache_->DeleteThreadPool(Thread::Current()); + Thread* self = Thread::Current(); + DCHECK(Runtime::Current()->IsShuttingDown(self)); + if (thread_pool_ != nullptr) { + ThreadPool* cache = nullptr; + { + ScopedSuspendAll ssa(__FUNCTION__); + // Clear thread_pool_ field while the threads are suspended. + // A mutator in the 'AddSamples' method will check against it. + cache = thread_pool_.release(); + } + cache->StopWorkers(self); + cache->RemoveAllTasks(self); + // We could just suspend all threads, but we know those threads + // will finish in a short period, so it's not worth adding a suspend logic + // here. Besides, this is only done for shutdown. + cache->Wait(self, false, false); + delete cache; } } @@ -259,10 +289,7 @@ void Jit::StopProfileSaver() { } bool Jit::JitAtFirstUse() { - if (instrumentation_cache_ != nullptr) { - return instrumentation_cache_->HotMethodThreshold() == 0; - } - return false; + return HotMethodThreshold() == 0; } bool Jit::CanInvokeCompiledCode(ArtMethod* method) { @@ -285,17 +312,6 @@ Jit::~Jit() { } } -void Jit::CreateInstrumentationCache(size_t compile_threshold, - size_t warmup_threshold, - size_t osr_threshold, - uint16_t priority_thread_weight) { - instrumentation_cache_.reset( - new jit::JitInstrumentationCache(compile_threshold, - warmup_threshold, - osr_threshold, - priority_thread_weight)); -} - void Jit::NewTypeLoadedIfUsingJit(mirror::Class* type) { jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr && jit->generate_debug_info_) { @@ -480,5 +496,164 @@ void Jit::AddMemoryUsage(ArtMethod* method, size_t bytes) { memory_use_.AddValue(bytes); } +class JitCompileTask FINAL : public Task { + public: + enum TaskKind { + kAllocateProfile, + kCompile, + kCompileOsr + }; + + JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) { + ScopedObjectAccess soa(Thread::Current()); + // Add a global ref to the class to prevent class unloading until compilation is done. + klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass()); + CHECK(klass_ != nullptr); + } + + ~JitCompileTask() { + ScopedObjectAccess soa(Thread::Current()); + soa.Vm()->DeleteGlobalRef(soa.Self(), klass_); + } + + void Run(Thread* self) OVERRIDE { + ScopedObjectAccess soa(self); + if (kind_ == kCompile) { + VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_); + if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) { + VLOG(jit) << "Failed to compile method " << PrettyMethod(method_); + } + } else if (kind_ == kCompileOsr) { + VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_); + if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) { + VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_); + } + } else { + DCHECK(kind_ == kAllocateProfile); + if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) { + VLOG(jit) << "Start profiling " << PrettyMethod(method_); + } + } + } + + void Finalize() OVERRIDE { + delete this; + } + + private: + ArtMethod* const method_; + const TaskKind kind_; + jobject klass_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask); +}; + +void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count) { + if (thread_pool_ == nullptr) { + // Should only see this when shutting down. + DCHECK(Runtime::Current()->IsShuttingDown(self)); + return; + } + + if (method->IsClassInitializer() || method->IsNative()) { + // We do not want to compile such methods. + return; + } + DCHECK(thread_pool_ != nullptr); + DCHECK_GT(warm_method_threshold_, 0); + DCHECK_GT(hot_method_threshold_, warm_method_threshold_); + DCHECK_GT(osr_method_threshold_, hot_method_threshold_); + DCHECK_GE(priority_thread_weight_, 1); + DCHECK_LE(priority_thread_weight_, hot_method_threshold_); + + int32_t starting_count = method->GetCounter(); + if (Jit::ShouldUsePriorityThreadWeight()) { + count *= priority_thread_weight_; + } + int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; + if (starting_count < warm_method_threshold_) { + if (new_count >= warm_method_threshold_) { + bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); + if (success) { + VLOG(jit) << "Start profiling " << PrettyMethod(method); + } + + if (thread_pool_ == nullptr) { + // Calling ProfilingInfo::Create might put us in a suspended state, which could + // lead to the thread pool being deleted when we are shutting down. + DCHECK(Runtime::Current()->IsShuttingDown(self)); + return; + } + + if (!success) { + // We failed allocating. Instead of doing the collection on the Java thread, we push + // an allocation to a compiler thread, that will do the collection. + thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile)); + } + } + // Avoid jumping more than one state at a time. + new_count = std::min(new_count, hot_method_threshold_ - 1); + } else if (starting_count < hot_method_threshold_) { + if (new_count >= hot_method_threshold_) { + DCHECK(thread_pool_ != nullptr); + thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile)); + } + // Avoid jumping more than one state at a time. + new_count = std::min(new_count, osr_method_threshold_ - 1); + } else if (starting_count < osr_method_threshold_) { + if (new_count >= osr_method_threshold_) { + DCHECK(thread_pool_ != nullptr); + thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); + } + } + // Update hotness counter + method->SetCounter(new_count); +} + +void Jit::MethodEntered(Thread* thread, ArtMethod* method) { + if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) { + // The compiler requires a ProfilingInfo object. + ProfilingInfo::Create(thread, method, /* retry_allocation */ true); + JitCompileTask compile_task(method, JitCompileTask::kCompile); + compile_task.Run(thread); + return; + } + + ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); + // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it + // instead of interpreting the method. + // We avoid doing this if exit stubs are installed to not mess with the instrumentation. + // TODO(ngeoffray): Clean up instrumentation and code cache interactions. + if ((profiling_info != nullptr) && + (profiling_info->GetSavedEntryPoint() != nullptr) && + !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { + method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint()); + } else { + AddSamples(thread, method, 1); + } +} + +void Jit::InvokeVirtualOrInterface(Thread* thread, + mirror::Object* this_object, + ArtMethod* caller, + uint32_t dex_pc, + ArtMethod* callee ATTRIBUTE_UNUSED) { + ScopedAssertNoThreadSuspension ants(thread, __FUNCTION__); + DCHECK(this_object != nullptr); + ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*)); + if (info != nullptr) { + // Since the instrumentation is marked from the declaring class we need to mark the card so + // that mod-union tables and card rescanning know about the update. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass()); + info->AddInvokeInfo(dex_pc, this_object->GetClass()); + } +} + +void Jit::WaitForCompilationToFinish(Thread* self) { + if (thread_pool_ != nullptr) { + thread_pool_->Wait(self, false, false); + } +} + } // namespace jit } // namespace art diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index e2123666f..96f9608a9 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -34,9 +34,11 @@ struct RuntimeArgumentMap; namespace jit { class JitCodeCache; -class JitInstrumentationCache; class JitOptions; +static constexpr int16_t kJitCheckForOSR = -1; +static constexpr int16_t kJitHotnessDisabled = -2; + class Jit { public: static constexpr bool kStressMode = kIsDebugBuild; @@ -46,17 +48,16 @@ class Jit { static Jit* Create(JitOptions* options, std::string* error_msg); bool CompileMethod(ArtMethod* method, Thread* self, bool osr) SHARED_REQUIRES(Locks::mutator_lock_); - void CreateInstrumentationCache(size_t compile_threshold, - size_t warmup_threshold, - size_t osr_threshold, - uint16_t priority_thread_weight); void CreateThreadPool(); + const JitCodeCache* GetCodeCache() const { return code_cache_.get(); } + JitCodeCache* GetCodeCache() { return code_cache_.get(); } + void DeleteThreadPool(); // Dump interesting info: #methods compiled, code vs data size, compile / verify cumulative // loggers. @@ -68,10 +69,39 @@ class Jit { REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); - JitInstrumentationCache* GetInstrumentationCache() const { - return instrumentation_cache_.get(); + size_t OSRMethodThreshold() const { + return osr_method_threshold_; + } + + size_t HotMethodThreshold() const { + return hot_method_threshold_; + } + + size_t WarmMethodThreshold() const { + return warm_method_threshold_; } + uint16_t PriorityThreadWeight() const { + return priority_thread_weight_; + } + + // Wait until there is no more pending compilation tasks. + void WaitForCompilationToFinish(Thread* self); + + // Profiling methods. + void MethodEntered(Thread* thread, ArtMethod* method) + SHARED_REQUIRES(Locks::mutator_lock_); + + void AddSamples(Thread* self, ArtMethod* method, uint16_t samples) + SHARED_REQUIRES(Locks::mutator_lock_); + + void InvokeVirtualOrInterface(Thread* thread, + mirror::Object* this_object, + ArtMethod* caller, + uint32_t dex_pc, + ArtMethod* callee) + SHARED_REQUIRES(Locks::mutator_lock_); + // Starts the profile saver if the config options allow profile recording. // The profile will be stored in the specified `filename` and will contain // information collected from the given `code_paths` (a set of dex locations). @@ -137,11 +167,15 @@ class Jit { Histogram memory_use_ GUARDED_BY(lock_); Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - std::unique_ptr instrumentation_cache_; std::unique_ptr code_cache_; bool save_profiling_info_; static bool generate_debug_info_; + uint16_t hot_method_threshold_; + uint16_t warm_method_threshold_; + uint16_t osr_method_threshold_; + uint16_t priority_thread_weight_; + std::unique_ptr thread_pool_; DISALLOW_COPY_AND_ASSIGN(Jit); }; diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc deleted file mode 100644 index b2c0c2023..000000000 --- a/runtime/jit/jit_instrumentation.cc +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2014 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 "jit_instrumentation.h" - -#include "art_method-inl.h" -#include "jit.h" -#include "jit_code_cache.h" -#include "scoped_thread_state_change.h" -#include "thread_list.h" - -namespace art { -namespace jit { - -// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. -static constexpr int kJitPoolThreadPthreadPriority = 9; - -class JitCompileTask FINAL : public Task { - public: - enum TaskKind { - kAllocateProfile, - kCompile, - kCompileOsr - }; - - JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) { - ScopedObjectAccess soa(Thread::Current()); - // Add a global ref to the class to prevent class unloading until compilation is done. - klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass()); - CHECK(klass_ != nullptr); - } - - ~JitCompileTask() { - ScopedObjectAccess soa(Thread::Current()); - soa.Vm()->DeleteGlobalRef(soa.Self(), klass_); - } - - void Run(Thread* self) OVERRIDE { - ScopedObjectAccess soa(self); - if (kind_ == kCompile) { - VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_); - if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) { - VLOG(jit) << "Failed to compile method " << PrettyMethod(method_); - } - } else if (kind_ == kCompileOsr) { - VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_); - if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) { - VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_); - } - } else { - DCHECK(kind_ == kAllocateProfile); - if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) { - VLOG(jit) << "Start profiling " << PrettyMethod(method_); - } - } - } - - void Finalize() OVERRIDE { - delete this; - } - - private: - ArtMethod* const method_; - const TaskKind kind_; - jobject klass_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask); -}; - -JitInstrumentationCache::JitInstrumentationCache(uint16_t hot_method_threshold, - uint16_t warm_method_threshold, - uint16_t osr_method_threshold, - uint16_t priority_thread_weight) - : hot_method_threshold_(hot_method_threshold), - warm_method_threshold_(warm_method_threshold), - osr_method_threshold_(osr_method_threshold), - priority_thread_weight_(priority_thread_weight), - listener_(this) { -} - -void JitInstrumentationCache::CreateThreadPool() { - // Create the thread pool before setting the instrumentation, so that - // when the threads stopped being suspended, they can use it directly. - // There is a DCHECK in the 'AddSamples' method to ensure the tread pool - // is not null when we instrument. - thread_pool_.reset(new ThreadPool("Jit thread pool", 1)); - thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority); - thread_pool_->StartWorkers(Thread::Current()); - { - // Add Jit interpreter instrumentation, tells the interpreter when - // to notify the jit to compile something. - ScopedSuspendAll ssa(__FUNCTION__); - Runtime::Current()->GetInstrumentation()->AddListener( - &listener_, JitInstrumentationListener::kJitEvents); - } -} - -void JitInstrumentationCache::DeleteThreadPool(Thread* self) { - DCHECK(Runtime::Current()->IsShuttingDown(self)); - if (thread_pool_ != nullptr) { - // First remove the listener, to avoid having mutators enter - // 'AddSamples'. - ThreadPool* cache = nullptr; - { - ScopedSuspendAll ssa(__FUNCTION__); - Runtime::Current()->GetInstrumentation()->RemoveListener( - &listener_, JitInstrumentationListener::kJitEvents); - // Clear thread_pool_ field while the threads are suspended. - // A mutator in the 'AddSamples' method will check against it. - cache = thread_pool_.release(); - } - cache->StopWorkers(self); - cache->RemoveAllTasks(self); - // We could just suspend all threads, but we know those threads - // will finish in a short period, so it's not worth adding a suspend logic - // here. Besides, this is only done for shutdown. - cache->Wait(self, false, false); - delete cache; - } -} - -void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, uint16_t count) { - // Since we don't have on-stack replacement, some methods can remain in the interpreter longer - // than we want resulting in samples even after the method is compiled. Also, if the - // jit is no longer interested in hotness samples because we're shutting down, just return. - if (method->IsClassInitializer() || method->IsNative() || (thread_pool_ == nullptr)) { - if (thread_pool_ == nullptr) { - // Should only see this when shutting down. - DCHECK(Runtime::Current()->IsShuttingDown(self)); - } - return; - } - DCHECK(thread_pool_ != nullptr); - DCHECK_GT(warm_method_threshold_, 0); - DCHECK_GT(hot_method_threshold_, warm_method_threshold_); - DCHECK_GT(osr_method_threshold_, hot_method_threshold_); - DCHECK_GE(priority_thread_weight_, 1); - DCHECK_LE(priority_thread_weight_, hot_method_threshold_); - - int32_t starting_count = method->GetCounter(); - if (Jit::ShouldUsePriorityThreadWeight()) { - count *= priority_thread_weight_; - } - int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; - if (starting_count < warm_method_threshold_) { - if (new_count >= warm_method_threshold_) { - bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); - if (success) { - VLOG(jit) << "Start profiling " << PrettyMethod(method); - } - - if (thread_pool_ == nullptr) { - // Calling ProfilingInfo::Create might put us in a suspended state, which could - // lead to the thread pool being deleted when we are shutting down. - DCHECK(Runtime::Current()->IsShuttingDown(self)); - return; - } - - if (!success) { - // We failed allocating. Instead of doing the collection on the Java thread, we push - // an allocation to a compiler thread, that will do the collection. - thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile)); - } - } - // Avoid jumping more than one state at a time. - new_count = std::min(new_count, hot_method_threshold_ - 1); - } else if (starting_count < hot_method_threshold_) { - if (new_count >= hot_method_threshold_) { - DCHECK(thread_pool_ != nullptr); - thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile)); - } - // Avoid jumping more than one state at a time. - new_count = std::min(new_count, osr_method_threshold_ - 1); - } else if (starting_count < osr_method_threshold_) { - if (new_count >= osr_method_threshold_) { - DCHECK(thread_pool_ != nullptr); - thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); - } - } - // Update hotness counter - method->SetCounter(new_count); -} - -JitInstrumentationListener::JitInstrumentationListener(JitInstrumentationCache* cache) - : instrumentation_cache_(cache) { - CHECK(instrumentation_cache_ != nullptr); -} - -void JitInstrumentationListener::MethodEntered(Thread* thread, - mirror::Object* /*this_object*/, - ArtMethod* method, - uint32_t /*dex_pc*/) { - if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) { - // The compiler requires a ProfilingInfo object. - ProfilingInfo::Create(thread, method, /* retry_allocation */ true); - JitCompileTask compile_task(method, JitCompileTask::kCompile); - compile_task.Run(thread); - return; - } - - ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); - // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it - // instead of interpreting the method. - // We avoid doing this if exit stubs are installed to not mess with the instrumentation. - // TODO(ngeoffray): Clean up instrumentation and code cache interactions. - if ((profiling_info != nullptr) && - (profiling_info->GetSavedEntryPoint() != nullptr) && - !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { - method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint()); - } else { - instrumentation_cache_->AddSamples(thread, method, 1); - } -} - -void JitInstrumentationListener::Branch(Thread* thread, - ArtMethod* method, - uint32_t dex_pc ATTRIBUTE_UNUSED, - int32_t dex_pc_offset) { - if (dex_pc_offset < 0) { - // Increment method hotness if it is a backward branch. - instrumentation_cache_->AddSamples(thread, method, 1); - } -} - -void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread, - mirror::Object* this_object, - ArtMethod* caller, - uint32_t dex_pc, - ArtMethod* callee ATTRIBUTE_UNUSED) { - // We make sure we cannot be suspended, as the profiling info can be concurrently deleted. - instrumentation_cache_->AddSamples(thread, caller, 1); - DCHECK(this_object != nullptr); - ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*)); - if (info != nullptr) { - // Since the instrumentation is marked from the declaring class we need to mark the card so - // that mod-union tables and card rescanning know about the update. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass()); - info->AddInvokeInfo(dex_pc, this_object->GetClass()); - } -} - -void JitInstrumentationCache::WaitForCompilationToFinish(Thread* self) { - if (thread_pool_ != nullptr) { - thread_pool_->Wait(self, false, false); - } -} - -} // namespace jit -} // namespace art diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h deleted file mode 100644 index d0545f8e2..000000000 --- a/runtime/jit/jit_instrumentation.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2014 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_JIT_JIT_INSTRUMENTATION_H_ -#define ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_ - -#include - -#include "instrumentation.h" - -#include "atomic.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "gc_root.h" -#include "jni.h" -#include "object_callbacks.h" -#include "thread_pool.h" - -namespace art { -namespace mirror { - class Object; - class Throwable; -} // namespace mirror -class ArtField; -class ArtMethod; -union JValue; -class Thread; - -namespace jit { -static constexpr int16_t kJitCheckForOSR = -1; -static constexpr int16_t kJitHotnessDisabled = -2; - -class JitInstrumentationCache; - -class JitInstrumentationListener : public instrumentation::InstrumentationListener { - public: - explicit JitInstrumentationListener(JitInstrumentationCache* cache); - - void MethodEntered(Thread* thread, mirror::Object* /*this_object*/, - ArtMethod* method, uint32_t /*dex_pc*/) - OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_); - - void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/, - const JValue& /*return_value*/) - OVERRIDE { } - void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { } - void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/, - ArtField* /*field*/) OVERRIDE { } - void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/, - ArtField* /*field*/, const JValue& /*field_value*/) - OVERRIDE { } - void ExceptionCaught(Thread* /*thread*/, - mirror::Throwable* /*exception_object*/) OVERRIDE { } - - void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { } - - void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t dex_pc_offset) - OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_); - - void InvokeVirtualOrInterface(Thread* thread, - mirror::Object* this_object, - ArtMethod* caller, - uint32_t dex_pc, - ArtMethod* callee) - OVERRIDE - REQUIRES(Roles::uninterruptible_) - SHARED_REQUIRES(Locks::mutator_lock_); - - static constexpr uint32_t kJitEvents = - instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kInvokeVirtualOrInterface; - - private: - JitInstrumentationCache* const instrumentation_cache_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationListener); -}; - -// Keeps track of which methods are hot. -class JitInstrumentationCache { - public: - JitInstrumentationCache(uint16_t hot_method_threshold, - uint16_t warm_method_threshold, - uint16_t osr_method_threshold, - uint16_t priority_thread_weight); - void AddSamples(Thread* self, ArtMethod* method, uint16_t samples) - SHARED_REQUIRES(Locks::mutator_lock_); - void CreateThreadPool(); - void DeleteThreadPool(Thread* self); - - size_t OSRMethodThreshold() const { - return osr_method_threshold_; - } - - size_t HotMethodThreshold() const { - return hot_method_threshold_; - } - - size_t WarmMethodThreshold() const { - return warm_method_threshold_; - } - - size_t PriorityThreadWeight() const { - return priority_thread_weight_; - } - - // Wait until there is no more pending compilation tasks. - void WaitForCompilationToFinish(Thread* self); - - private: - uint16_t hot_method_threshold_; - uint16_t warm_method_threshold_; - uint16_t osr_method_threshold_; - uint16_t priority_thread_weight_; - JitInstrumentationListener listener_; - std::unique_ptr thread_pool_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationCache); -}; - -} // namespace jit -} // namespace art - -#endif // ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_ diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 37bb4c1fc..2489e45e4 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1921,16 +1921,7 @@ void Runtime::CreateJit() { } std::string error_msg; jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg)); - if (jit_.get() != nullptr) { - jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold(), - jit_options_->GetWarmupThreshold(), - jit_options_->GetOsrThreshold(), - jit_options_->GetPriorityThreadWeight()); - jit_->CreateThreadPool(); - - // Notify native debugger about the classes already loaded before the creation of the jit. - jit_->DumpTypeInfoForLoadedTypes(GetClassLinker()); - } else { + if (jit_.get() == nullptr) { LOG(WARNING) << "Failed to create JIT " << error_msg; } } diff --git a/test/141-class-unload/jni_unload.cc b/test/141-class-unload/jni_unload.cc index d913efe53..bbbb0a603 100644 --- a/test/141-class-unload/jni_unload.cc +++ b/test/141-class-unload/jni_unload.cc @@ -19,7 +19,6 @@ #include #include "jit/jit.h" -#include "jit/jit_instrumentation.h" #include "runtime.h" #include "thread-inl.h" @@ -29,7 +28,7 @@ namespace { extern "C" JNIEXPORT void JNICALL Java_IntHolder_waitForCompilation(JNIEnv*, jclass) { jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr) { - jit->GetInstrumentationCache()->WaitForCompilationToFinish(Thread::Current()); + jit->WaitForCompilationToFinish(Thread::Current()); } } -- 2.11.0