Fix relation between debuggable / JIT zygote.
- Move the logic to clear precompiled in ClassLinker.
- Add a null check on entries in ZygoteMap
- Avoid doing JIT zygote actions (precompile, remapping boot images)
when debuggable.
Test: android.jdwptunnel.cts.JdwpTunnelTest#testAttachDebuggerToProfileableApp
Change-Id: I9b5e391bb35aa04bbeba01b9b563b33f96395d2e
diff --git a/runtime/art_method.h b/runtime/art_method.h
index f769f52..70d8d15 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -214,6 +214,10 @@
}
bool IsPreCompiled() const {
+ if (IsIntrinsic()) {
+ // kAccCompileDontBother overlaps with kAccIntrinsicBits.
+ return false;
+ }
uint32_t expected = (kAccPreCompiled | kAccCompileDontBother);
return (GetAccessFlags() & expected) == expected;
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 8a27370..5dce6c5 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -980,6 +980,10 @@
}
void Jit::MapBootImageMethods() {
+ if (Runtime::Current()->IsJavaDebuggable()) {
+ LOG(INFO) << "Not mapping boot image methods due to process being debuggable";
+ return;
+ }
CHECK_NE(fd_methods_.get(), -1);
if (!code_cache_->GetZygoteMap()->CanMapBootImageMethods()) {
LOG(WARNING) << "Not mapping boot image methods due to error from zygote";
@@ -1227,7 +1231,10 @@
return;
}
Runtime* runtime = Runtime::Current();
- if (runtime->IsSystemServer() && UseJitCompilation() && HasImageWithProfile()) {
+ // If the runtime is debuggable, no need to precompile methods.
+ if (runtime->IsSystemServer() &&
+ UseJitCompilation() && HasImageWithProfile() &&
+ !runtime->IsJavaDebuggable()) {
thread_pool_->AddTask(Thread::Current(), new JitProfileTask(dex_files, class_loader));
}
}
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 3b8c2a7..0c3b18b 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -879,37 +879,28 @@
}
void JitCodeCache::TransitionToDebuggable() {
- // We want to discard JIT compiled methods that are non-debuggable. These are:
- // - Methods compiled by the zygote (where the compiled code is in the zygote exec
- // space)
- // - Methods that are precompiled in the method_code_map_.
- //
- // Also, we want to clear the precompiled flag to clear the effects of
- // GetSavedEntryPointOfPreCompiledMethod.
+ // Check that none of our methods have an entrypoint in the zygote exec
+ // space (this should be taken care of by
+ // ClassLinker::UpdateEntryPointsClassVisitor.
{
MutexLock mu(Thread::Current(), *Locks::jit_lock_);
- for (const auto& it : method_code_map_) {
- ArtMethod* method = it.second;
- if (IsInZygoteExecSpace(method->GetEntryPointFromQuickCompiledCode()) ||
- method->IsPreCompiled()) {
- method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
- }
- if (method->IsPreCompiled()) {
- method->ClearPreCompiled();
+ if (kIsDebugBuild) {
+ for (const auto& it : method_code_map_) {
+ ArtMethod* method = it.second;
+ DCHECK(!method->IsPreCompiled());
+ DCHECK(!IsInZygoteExecSpace(method->GetEntryPointFromQuickCompiledCode()));
}
}
// Not strictly necessary, but this map is useless now.
saved_compiled_methods_map_.clear();
}
- for (const auto& entry : zygote_map_) {
- ArtMethod* method = entry.method;
- if (IsInZygoteExecSpace(method->GetEntryPointFromQuickCompiledCode())) {
- method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
- }
- // We check if it's precompiled instead of DCHECKing it to support
- // TransitionToDebuggable being called multiple times.
- if (method->IsPreCompiled()) {
- method->ClearPreCompiled();
+ if (kIsDebugBuild) {
+ for (const auto& entry : zygote_map_) {
+ ArtMethod* method = entry.method;
+ if (method != nullptr) {
+ DCHECK(!method->IsPreCompiled());
+ DCHECK(!IsInZygoteExecSpace(method->GetEntryPointFromQuickCompiledCode()));
+ }
}
}
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index f46dc75..1bbe2a4 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -403,6 +403,11 @@
return region->IsValid() && !IsSharedRegion(*region);
}
+ // Return whether the given `ptr` is in the zygote executable memory space.
+ bool IsInZygoteExecSpace(const void* ptr) const {
+ return shared_region_.IsInExecSpace(ptr);
+ }
+
private:
JitCodeCache();
@@ -480,10 +485,6 @@
return shared_region_.IsInDataSpace(ptr);
}
- bool IsInZygoteExecSpace(const void* ptr) const {
- return shared_region_.IsInExecSpace(ptr);
- }
-
bool IsWeakAccessEnabled(Thread* self) const;
void WaitUntilInlineCacheAccessible(Thread* self)
REQUIRES(!Locks::jit_lock_)
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 1a83372..b62cd11 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -2829,6 +2829,20 @@
!m.IsProxyMethod()) {
instrumentation_->UpdateMethodsCodeForJavaDebuggable(&m, GetQuickToInterpreterBridge());
}
+
+ if (Runtime::Current()->GetJit() != nullptr &&
+ Runtime::Current()->GetJit()->GetCodeCache()->IsInZygoteExecSpace(code) &&
+ !m.IsNative()) {
+ DCHECK(!m.IsProxyMethod());
+ instrumentation_->UpdateMethodsCodeForJavaDebuggable(&m, GetQuickToInterpreterBridge());
+ }
+
+ if (m.IsPreCompiled()) {
+ // Precompilation is incompatible with debuggable, so clear the flag
+ // and update the entrypoint in case it has been compiled.
+ m.ClearPreCompiled();
+ instrumentation_->UpdateMethodsCodeForJavaDebuggable(&m, GetQuickToInterpreterBridge());
+ }
}
return true;
}