Merge "Revert "Revert "Make --debuggable rely on JIT code."""
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 148ce4f..eaac0b4 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -102,7 +102,7 @@
/* no_inline_from */ nullptr,
/* include_patch_information */ false,
CompilerOptions::kDefaultTopKProfileThreshold,
- Runtime::Current()->IsDebuggable(),
+ Runtime::Current()->IsJavaDebuggable(),
CompilerOptions::kDefaultGenerateDebugInfo,
/* implicit_null_checks */ true,
/* implicit_so_checks */ true,
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 8933dd0..c2275ac 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -125,7 +125,7 @@
class_path = OatFile::kSpecialSharedLibrary;
}
argv.push_back(class_path);
- if (runtime->IsDebuggable()) {
+ if (runtime->IsJavaDebuggable()) {
argv.push_back("--debuggable");
}
runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0e524db..8669367 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2860,9 +2860,12 @@
return true;
}
- if (runtime->IsFullyDeoptable()) {
- // We need to be able to deoptimize at any time so we should always just ignore precompiled
- // code and go to the interpreter assuming we don't already have jitted code.
+ if (runtime->IsJavaDebuggable()) {
+ // For simplicity, we ignore precompiled code and go to the interpreter
+ // assuming we don't already have jitted code.
+ // We could look at the oat file where `quick_code` is being defined,
+ // and check whether it's been compiled debuggable, but we decided to
+ // only rely on the JIT for debuggable apps.
jit::Jit* jit = Runtime::Current()->GetJit();
return (jit == nullptr) || !jit->GetCodeCache()->ContainsPc(quick_code);
}
@@ -2870,18 +2873,13 @@
if (runtime->IsNativeDebuggable()) {
DCHECK(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse());
// If we are doing native debugging, ignore application's AOT code,
- // since we want to JIT it with extra stackmaps for native debugging.
- // On the other hand, keep all AOT code from the boot image, since the
- // blocking JIT would results in non-negligible performance impact.
+ // since we want to JIT it (at first use) with extra stackmaps for native
+ // debugging. We keep however all AOT code from the boot image,
+ // since the JIT-at-first-use is blocking and would result in non-negligible
+ // startup performance impact.
return !runtime->GetHeap()->IsInBootImageOatFile(quick_code);
}
- if (Dbg::IsDebuggerActive()) {
- // Boot image classes may be AOT-compiled as non-debuggable.
- // This is not suitable for the Java debugger, so ignore the AOT code.
- return runtime->GetHeap()->IsInBootImageOatFile(quick_code);
- }
-
return false;
}
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 22a3163..1a0cec0 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -588,29 +588,6 @@
return !Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly();
}
-// Used to patch boot image method entry point to interpreter bridge.
-class UpdateEntryPointsClassVisitor : public ClassVisitor {
- public:
- explicit UpdateEntryPointsClassVisitor(instrumentation::Instrumentation* instrumentation)
- : instrumentation_(instrumentation) {}
-
- bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES(Locks::mutator_lock_) {
- auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- for (auto& m : klass->GetMethods(pointer_size)) {
- const void* code = m.GetEntryPointFromQuickCompiledCode();
- if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) &&
- !m.IsNative() &&
- !m.IsProxyMethod()) {
- instrumentation_->UpdateMethodsCodeFromDebugger(&m, GetQuickToInterpreterBridge());
- }
- }
- return true;
- }
-
- private:
- instrumentation::Instrumentation* const instrumentation_;
-};
-
void Dbg::GoActive() {
// Enable all debugging features, including scans for breakpoints.
// This is a no-op if we're already active.
@@ -639,14 +616,16 @@
}
Runtime* runtime = Runtime::Current();
- // Since boot image code may be AOT compiled as not debuggable, we need to patch
- // entry points of methods in boot image to interpreter bridge.
- // However, the performance cost of this is non-negligible during native-debugging due to the
+ // Best effort deoptimization if the runtime is non-Java debuggable. This happens when
+ // ro.debuggable is set, but the application is not debuggable, or when a standalone
+ // dalvikvm invocation is not passed the debuggable option (-Xcompiler-option --debuggable).
+ //
+ // The performance cost of this is non-negligible during native-debugging due to the
// forced JIT, so we keep the AOT code in that case in exchange for limited native debugging.
- if (!runtime->GetInstrumentation()->IsForcedInterpretOnly() && !runtime->IsNativeDebuggable()) {
- ScopedObjectAccess soa(self);
- UpdateEntryPointsClassVisitor visitor(runtime->GetInstrumentation());
- runtime->GetClassLinker()->VisitClasses(&visitor);
+ if (!runtime->IsJavaDebuggable() &&
+ !runtime->GetInstrumentation()->IsForcedInterpretOnly() &&
+ !runtime->IsNativeDebuggable()) {
+ runtime->DeoptimizeBootImage();
}
ScopedSuspendAll ssa(__FUNCTION__);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index eb76fb6..bde9009 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -781,15 +781,19 @@
// If caller_pc is the instrumentation exit stub, the stub will check to see if deoptimization
// should be done and it knows the real return pc.
if (UNLIKELY(caller_pc != reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) &&
- Dbg::IsForcedInterpreterNeededForUpcall(self, caller) &&
- Runtime::Current()->IsDeoptimizeable(caller_pc))) {
- // Push the context of the deoptimization stack so we can restore the return value and the
- // exception before executing the deoptimized frames.
- self->PushDeoptimizationContext(
- result, shorty[0] == 'L', /* from_code */ false, self->GetException());
+ Dbg::IsForcedInterpreterNeededForUpcall(self, caller))) {
+ if (!Runtime::Current()->IsAsyncDeoptimizeable(caller_pc)) {
+ LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
+ << caller->PrettyMethod();
+ } else {
+ // Push the context of the deoptimization stack so we can restore the return value and the
+ // exception before executing the deoptimized frames.
+ self->PushDeoptimizationContext(
+ result, shorty[0] == 'L', /* from_code */ false, self->GetException());
- // Set special exception to cause deoptimization.
- self->SetException(Thread::GetDeoptimizationException());
+ // Set special exception to cause deoptimization.
+ self->SetException(Thread::GetDeoptimizationException());
+ }
}
// No need to restore the args since the method has already been run by the interpreter.
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index bbd6d35..3005c2a 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -105,10 +105,9 @@
method->SetEntryPointFromQuickCompiledCode(quick_code);
}
-bool Instrumentation::NeedDebugVersionForBootImageCode(ArtMethod* method, const void* code) const
- REQUIRES_SHARED(Locks::mutator_lock_) {
+bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) {
return Dbg::IsDebuggerActive() &&
- Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) &&
+ Runtime::Current()->IsJavaDebuggable() &&
!method->IsNative() &&
!method->IsProxyMethod();
}
@@ -132,9 +131,10 @@
if ((forced_interpret_only_ || IsDeoptimized(method)) && !method->IsNative()) {
new_quick_code = GetQuickToInterpreterBridge();
} else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
- new_quick_code = class_linker->GetQuickOatCodeFor(method);
- if (NeedDebugVersionForBootImageCode(method, new_quick_code)) {
+ if (NeedDebugVersionFor(method)) {
new_quick_code = GetQuickToInterpreterBridge();
+ } else {
+ new_quick_code = class_linker->GetQuickOatCodeFor(method);
}
} else {
new_quick_code = GetQuickResolutionStub();
@@ -148,13 +148,14 @@
// class, all its static methods code will be set to the instrumentation entry point.
// For more details, see ClassLinker::FixupStaticTrampolines.
if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
- new_quick_code = class_linker->GetQuickOatCodeFor(method);
- if (NeedDebugVersionForBootImageCode(method, new_quick_code)) {
+ if (NeedDebugVersionFor(method)) {
// Oat code should not be used. Don't install instrumentation stub and
// use interpreter for instrumentation.
new_quick_code = GetQuickToInterpreterBridge();
} else if (entry_exit_stubs_installed_) {
new_quick_code = GetQuickInstrumentationEntryPoint();
+ } else {
+ new_quick_code = class_linker->GetQuickOatCodeFor(method);
}
} else {
new_quick_code = GetQuickResolutionStub();
@@ -731,10 +732,12 @@
UpdateMethodsCodeImpl(method, quick_code);
}
-void Instrumentation::UpdateMethodsCodeFromDebugger(ArtMethod* method, const void* quick_code) {
- // When debugger attaches, we may update the entry points of all methods of a class
- // to the interpreter bridge. A method's declaring class might not be in resolved
- // state yet in that case.
+void Instrumentation::UpdateMethodsCodeForJavaDebuggable(ArtMethod* method,
+ const void* quick_code) {
+ // When the runtime is set to Java debuggable, we may update the entry points of
+ // all methods of a class to the interpreter bridge. A method's declaring class
+ // might not be in resolved state yet in that case, so we bypass the DCHECK in
+ // UpdateMethodsCode.
UpdateMethodsCodeImpl(method, quick_code);
}
@@ -819,10 +822,9 @@
!method->GetDeclaringClass()->IsInitialized()) {
UpdateEntrypoints(method, GetQuickResolutionStub());
} else {
- const void* quick_code = class_linker->GetQuickOatCodeFor(method);
- if (NeedDebugVersionForBootImageCode(method, quick_code)) {
- quick_code = GetQuickToInterpreterBridge();
- }
+ const void* quick_code = NeedDebugVersionFor(method)
+ ? GetQuickToInterpreterBridge()
+ : class_linker->GetQuickOatCodeFor(method);
UpdateEntrypoints(method, quick_code);
}
@@ -1114,7 +1116,7 @@
bool deoptimize = (visitor.caller != nullptr) &&
(interpreter_stubs_installed_ || IsDeoptimized(visitor.caller) ||
Dbg::IsForcedInterpreterNeededForUpcall(self, visitor.caller));
- if (deoptimize && Runtime::Current()->IsDeoptimizeable(*return_pc)) {
+ if (deoptimize && Runtime::Current()->IsAsyncDeoptimizeable(*return_pc)) {
if (kVerboseInstrumentation) {
LOG(INFO) << "Deoptimizing "
<< visitor.caller->PrettyMethod()
@@ -1132,6 +1134,10 @@
return GetTwoWordSuccessValue(*return_pc,
reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint()));
} else {
+ if (deoptimize && !Runtime::Current()->IsAsyncDeoptimizeable(*return_pc)) {
+ LOG(WARNING) << "Got a deoptimization request on un-deoptimizable " << method->PrettyMethod()
+ << " at PC " << reinterpret_cast<void*>(*return_pc);
+ }
if (kVerboseInstrumentation) {
LOG(INFO) << "Returning from " << method->PrettyMethod()
<< " to PC " << reinterpret_cast<void*>(*return_pc);
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 05c0aaa..39606f7 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -239,7 +239,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
// Update the code of a method respecting any installed stubs from debugger.
- void UpdateMethodsCodeFromDebugger(ArtMethod* method, const void* quick_code)
+ void UpdateMethodsCodeForJavaDebuggable(ArtMethod* method, const void* quick_code)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
// Get the quick code for the given method. More efficient than asking the class linker as it
@@ -264,7 +264,7 @@
// Code is in boot image oat file which isn't compiled as debuggable.
// Need debug version (interpreter or jitted) if that's the case.
- bool NeedDebugVersionForBootImageCode(ArtMethod* method, const void* code) const
+ bool NeedDebugVersionFor(ArtMethod* method) const
REQUIRES_SHARED(Locks::mutator_lock_);
bool AreExitStubsInstalled() const {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 6336cdd..45611a9 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -594,7 +594,7 @@
VLOG(jit) << "JIT discarded jitted code due to invalid single-implementation assumptions.";
return nullptr;
}
- DCHECK(cha_single_implementation_list.empty() || !Runtime::Current()->IsDebuggable())
+ DCHECK(cha_single_implementation_list.empty() || !Runtime::Current()->IsJavaDebuggable())
<< "Should not be using cha on debuggable apps/runs!";
for (ArtMethod* single_impl : cha_single_implementation_list) {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 10fc90b..fd22d9e 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -71,7 +71,7 @@
static void EnableDebugFeatures(uint32_t debug_flags) {
// Must match values in com.android.internal.os.Zygote.
enum {
- DEBUG_ENABLE_DEBUGGER = 1,
+ DEBUG_ENABLE_JDWP = 1,
DEBUG_ENABLE_CHECKJNI = 1 << 1,
DEBUG_ENABLE_ASSERT = 1 << 2,
DEBUG_ENABLE_SAFEMODE = 1 << 3,
@@ -79,6 +79,7 @@
DEBUG_GENERATE_DEBUG_INFO = 1 << 5,
DEBUG_ALWAYS_JIT = 1 << 6,
DEBUG_NATIVE_DEBUGGABLE = 1 << 7,
+ DEBUG_JAVA_DEBUGGABLE = 1 << 8,
};
Runtime* const runtime = Runtime::Current();
@@ -100,11 +101,11 @@
debug_flags &= ~DEBUG_ENABLE_JNI_LOGGING;
}
- Dbg::SetJdwpAllowed((debug_flags & DEBUG_ENABLE_DEBUGGER) != 0);
- if ((debug_flags & DEBUG_ENABLE_DEBUGGER) != 0) {
+ Dbg::SetJdwpAllowed((debug_flags & DEBUG_ENABLE_JDWP) != 0);
+ if ((debug_flags & DEBUG_ENABLE_JDWP) != 0) {
EnableDebugger();
}
- debug_flags &= ~DEBUG_ENABLE_DEBUGGER;
+ debug_flags &= ~DEBUG_ENABLE_JDWP;
const bool safe_mode = (debug_flags & DEBUG_ENABLE_SAFEMODE) != 0;
if (safe_mode) {
@@ -130,6 +131,14 @@
debug_flags &= ~DEBUG_ALWAYS_JIT;
}
+ if ((debug_flags & DEBUG_JAVA_DEBUGGABLE) != 0) {
+ runtime->AddCompilerOption("--debuggable");
+ runtime->SetJavaDebuggable(true);
+ // Deoptimize the boot image as it may be non-debuggable.
+ runtime->DeoptimizeBootImage();
+ debug_flags &= ~DEBUG_JAVA_DEBUGGABLE;
+ }
+
if ((debug_flags & DEBUG_NATIVE_DEBUGGABLE) != 0) {
runtime->AddCompilerOption("--debuggable");
runtime->AddCompilerOption("--generate-debug-info");
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 8554fa2..b19ace5 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -530,7 +530,7 @@
class_path = OatFile::kSpecialSharedLibrary;
}
argv.push_back(class_path);
- if (runtime->IsDebuggable()) {
+ if (runtime->IsJavaDebuggable()) {
argv.push_back("--debuggable");
}
runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index ccc5f7a..9113f83 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -300,8 +300,6 @@
.Define("-Xplugin:_")
.WithType<std::vector<Plugin>>().AppendValues()
.IntoKey(M::Plugins)
- .Define("-Xfully-deoptable")
- .IntoKey(M::FullyDeoptable)
.Define("-XX:ThreadSuspendTimeout=_") // in ms
.WithType<MillisecondsToNanoseconds>() // store as ns
.IntoKey(M::ThreadSuspendTimeout)
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 8d758a4..4e76951 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -347,9 +347,11 @@
callee_method_ = method;
return true;
} else if (!single_frame_deopt_ &&
- !Runtime::Current()->IsDeoptimizeable(GetCurrentQuickFramePc())) {
+ !Runtime::Current()->IsAsyncDeoptimizeable(GetCurrentQuickFramePc())) {
// We hit some code that's not deoptimizeable. However, Single-frame deoptimization triggered
// from compiled code is always allowed since HDeoptimize always saves the full environment.
+ LOG(WARNING) << "Got request to deoptimize un-deoptimizable method "
+ << method->PrettyMethod();
FinishStackWalk();
return false; // End stack walk.
} else {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index b30e510..693b8f4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -245,7 +245,7 @@
force_native_bridge_(false),
is_native_bridge_loaded_(false),
is_native_debuggable_(false),
- is_fully_deoptable_(false),
+ is_java_debuggable_(false),
zygote_max_failed_boots_(0),
experimental_flags_(ExperimentalFlags::kNone),
oat_file_manager_(nullptr),
@@ -826,14 +826,6 @@
return IsShuttingDownLocked();
}
-bool Runtime::IsDebuggable() const {
- if (IsFullyDeoptable()) {
- return true;
- }
- const OatFile* oat_file = GetOatFileManager().GetPrimaryOatFile();
- return oat_file != nullptr && oat_file->IsDebuggable();
-}
-
void Runtime::StartDaemonThreads() {
ScopedTrace trace(__FUNCTION__);
VLOG(startup) << "Runtime::StartDaemonThreads entering";
@@ -1039,6 +1031,12 @@
compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler);
compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions);
+ for (StringPiece option : Runtime::Current()->GetCompilerOptions()) {
+ if (option.starts_with("--debuggable")) {
+ SetJavaDebuggable(true);
+ break;
+ }
+ }
image_compiler_options_ = runtime_options.ReleaseOrDefault(Opt::ImageCompilerOptions);
image_location_ = runtime_options.GetOrDefault(Opt::Image);
@@ -1053,8 +1051,6 @@
verify_ = runtime_options.GetOrDefault(Opt::Verify);
allow_dex_file_fallback_ = !runtime_options.Exists(Opt::NoDexFileFallback);
- is_fully_deoptable_ = runtime_options.Exists(Opt::FullyDeoptable);
-
no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);
@@ -1259,6 +1255,11 @@
ScopedTrace trace2("AddImageStringsToTable");
GetInternTable()->AddImagesStringsToTable(heap_->GetBootImageSpaces());
}
+ if (IsJavaDebuggable()) {
+ // Now that we have loaded the boot image, deoptimize its methods if we are running
+ // debuggable, as the code may have been compiled non-debuggable.
+ DeoptimizeBootImage();
+ }
} else {
std::vector<std::string> dex_filenames;
Split(boot_class_path_string_, ':', &dex_filenames);
@@ -1405,7 +1406,7 @@
}
// Is the process debuggable? Otherwise, do not attempt to load the plugin.
- if (!runtime->IsDebuggable()) {
+ if (!runtime->IsJavaDebuggable()) {
*error_msg = "Process is not debuggable.";
return false;
}
@@ -2206,9 +2207,15 @@
return verify_ == verifier::VerifyMode::kSoftFail;
}
-bool Runtime::IsDeoptimizeable(uintptr_t code) const
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return !heap_->IsInBootImageOatFile(reinterpret_cast<void *>(code));
+bool Runtime::IsAsyncDeoptimizeable(uintptr_t code) const {
+ // We only support async deopt (ie the compiled code is not explicitly asking for
+ // deopt, but something else like the debugger) in debuggable JIT code.
+ // We could look at the oat file where `code` is being defined,
+ // and check whether it's been compiled debuggable, but we decided to
+ // only rely on the JIT for debuggable apps.
+ return IsJavaDebuggable() &&
+ GetJit() != nullptr &&
+ GetJit()->GetCodeCache()->ContainsPc(reinterpret_cast<const void*>(code));
}
LinearAlloc* Runtime::CreateLinearAlloc() {
@@ -2292,4 +2299,43 @@
return callbacks_.get();
}
+// Used to patch boot image method entry point to interpreter bridge.
+class UpdateEntryPointsClassVisitor : public ClassVisitor {
+ public:
+ explicit UpdateEntryPointsClassVisitor(instrumentation::Instrumentation* instrumentation)
+ : instrumentation_(instrumentation) {}
+
+ bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ for (auto& m : klass->GetMethods(pointer_size)) {
+ const void* code = m.GetEntryPointFromQuickCompiledCode();
+ if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) &&
+ !m.IsNative() &&
+ !m.IsProxyMethod()) {
+ instrumentation_->UpdateMethodsCodeForJavaDebuggable(&m, GetQuickToInterpreterBridge());
+ }
+ }
+ return true;
+ }
+
+ private:
+ instrumentation::Instrumentation* const instrumentation_;
+};
+
+void Runtime::SetJavaDebuggable(bool value) {
+ is_java_debuggable_ = value;
+ // Do not call DeoptimizeBootImage just yet, the runtime may still be starting up.
+}
+
+void Runtime::DeoptimizeBootImage() {
+ // If we've already started and we are setting this runtime to debuggable,
+ // we patch entry points of methods in boot image to interpreter bridge, as
+ // boot image code may be AOT compiled as not debuggable.
+ if (!GetInstrumentation()->IsForcedInterpretOnly()) {
+ ScopedObjectAccess soa(Thread::Current());
+ UpdateEntryPointsClassVisitor visitor(GetInstrumentation());
+ GetClassLinker()->VisitClasses(&visitor);
+ }
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index f7d6810..30b1756 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -434,7 +434,7 @@
kInitialize
};
- jit::Jit* GetJit() {
+ jit::Jit* GetJit() const {
return jit_.get();
}
@@ -569,15 +569,14 @@
return jit_options_.get();
}
- bool IsDebuggable() const;
-
- bool IsFullyDeoptable() const {
- return is_fully_deoptable_;
+ bool IsJavaDebuggable() const {
+ return is_java_debuggable_;
}
- void SetFullyDeoptable(bool value) {
- is_fully_deoptable_ = value;
- }
+ void SetJavaDebuggable(bool value);
+
+ // Deoptimize the boot image, called for Java debuggable apps.
+ void DeoptimizeBootImage();
bool IsNativeDebuggable() const {
return is_native_debuggable_;
@@ -639,9 +638,9 @@
return zygote_no_threads_;
}
- // Returns if the code can be deoptimized. Code may be compiled with some
+ // Returns if the code can be deoptimized asynchronously. Code may be compiled with some
// optimization that makes it impossible to deoptimize.
- bool IsDeoptimizeable(uintptr_t code) const REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsAsyncDeoptimizeable(uintptr_t code) const REQUIRES_SHARED(Locks::mutator_lock_);
// Returns a saved copy of the environment (getenv/setenv values).
// Used by Fork to protect against overwriting LD_LIBRARY_PATH, etc.
@@ -863,8 +862,8 @@
// Whether we are running under native debugger.
bool is_native_debuggable_;
- // Whether we are expected to be deoptable at all points.
- bool is_fully_deoptable_;
+ // Whether Java code needs to be debuggable.
+ bool is_java_debuggable_;
// The maximum number of failed boots we allow before pruning the dalvik cache
// and trying again. This option is only inspected when we're running as a
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index ad748b0..e68a1b2 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -123,7 +123,6 @@
RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentLib) // -agentlib:<libname>=<options>
RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentPath) // -agentpath:<libname>=<options>
RUNTIME_OPTIONS_KEY (std::vector<Plugin>, Plugins) // -Xplugin:<library>
-RUNTIME_OPTIONS_KEY (Unit, FullyDeoptable) // -Xfully-deoptable
// Not parse-able from command line, but can be provided explicitly.
// (Do not add anything here that is defined in ParsedOptions::MakeParser)
diff --git a/runtime/thread.cc b/runtime/thread.cc
index ae87569..3c7a71a 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2852,13 +2852,16 @@
if (Dbg::IsForcedInterpreterNeededForException(this)) {
NthCallerVisitor visitor(this, 0, false);
visitor.WalkStack();
- if (Runtime::Current()->IsDeoptimizeable(visitor.caller_pc)) {
+ if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) {
// Save the exception into the deoptimization context so it can be restored
// before entering the interpreter.
PushDeoptimizationContext(
JValue(), /*is_reference */ false, /* from_code */ false, exception);
artDeoptimize(this);
UNREACHABLE();
+ } else {
+ LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
+ << visitor.caller->PrettyMethod();
}
}
diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc
index d3a033b..6cea673 100644
--- a/test/466-get-live-vreg/get_live_vreg_jni.cc
+++ b/test/466-get-live-vreg/get_live_vreg_jni.cc
@@ -47,7 +47,7 @@
uint32_t value = 0;
if (GetCurrentQuickFrame() != nullptr &&
GetCurrentOatQuickMethodHeader()->IsOptimized() &&
- !Runtime::Current()->IsDebuggable()) {
+ !Runtime::Current()->IsJavaDebuggable()) {
CHECK_EQ(GetVReg(m, dex_register_of_first_parameter, kIntVReg, &value), false);
} else {
CHECK(GetVReg(m, dex_register_of_first_parameter, kIntVReg, &value));
diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run
index 0664592..4a2eb34 100755
--- a/test/909-attach-agent/run
+++ b/test/909-attach-agent/run
@@ -22,10 +22,12 @@
fi
./default-run "$@" --android-runtime-option -Xplugin:${plugin} \
- --android-runtime-option -Xfully-deoptable \
+ --android-runtime-option -Xcompiler-option \
+ --android-runtime-option --debuggable \
--args agent:${agent}=909-attach-agent
-./default-run "$@" --android-runtime-option -Xfully-deoptable \
+./default-run "$@" --android-runtime-option -Xcompiler-option \
+ --android-runtime-option --debuggable \
--args agent:${agent}=909-attach-agent
./default-run "$@" --args agent:${agent}=909-attach-agent
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index e156c65..1b4f195 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -607,6 +607,7 @@
TEST_ART_BROKEN_OPTIMIZING_NONDEBUGGABLE_RUN_TESTS := \
454-get-vreg \
457-regs \
+ 602-deoptimizeable
ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 774f398..751aa95 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -344,7 +344,7 @@
else
FLAGS="${FLAGS} -agentpath:${agent}=${TEST_NAME},art"
FLAGS="${FLAGS} -Xplugin:${plugin}"
- FLAGS="${FLAGS} -Xfully-deoptable"
+ FLAGS="${FLAGS} -Xcompiler-option --debuggable"
# Always make the compilation be debuggable.
COMPILE_FLAGS="${COMPILE_FLAGS} --debuggable"
fi