Make instrumentation trampoline able to jump to jit code
In order to get the benefit of the instrumentation trampoline it must
be able to jump to jit code. This patch adds support for doing that
and adds code to ensure that we will not be jumping back and forth
between the trampoline and the interpreter all the time if the jit has
not yet compiled the current method.
Note we also disable the jit-gc when turning on these trampolines
since otherwise we could end up either sending instrumentation events
multiple times or running uninitialized memory.
Bug: 110263880
Test: ./test/testrunner/testrunner.py --host --runtime-option=-Xplugin:libtracefast-trampolined.so
Test: ./test/testrunner/testrunner.py --host --runtime-option=-Xplugin:libtracefast-trampolined.so --runtime-option=-Xjitthreshhold:0
Test: ./test.py --host
Change-Id: Ie6e92ec6367452fe4fde24d520d808b7af91d1b5
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 5e73603..ed449b5 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -735,8 +735,11 @@
ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize);
// Update the entrypoint if the ProfilingInfo has one. The interpreter will call it
- // instead of interpreting the method.
- if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() != nullptr)) {
+ // instead of interpreting the method. We don't update it for instrumentation as the entrypoint
+ // must remain the instrumentation entrypoint.
+ if ((profiling_info != nullptr) &&
+ (profiling_info->GetSavedEntryPoint() != nullptr) &&
+ (method->GetEntryPointFromQuickCompiledCode() != GetQuickInstrumentationEntryPoint())) {
Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(
method, profiling_info->GetSavedEntryPoint());
} else {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index fe154a9..e635ad9 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -174,7 +174,10 @@
bool use_ashmem = !generate_debug_info && !kIsTargetLinux && !kIsTargetFuchsia;
// With 'perf', we want a 1-1 mapping between an address and a method.
- bool garbage_collect_code = !generate_debug_info;
+ // We aren't able to keep method pointers live during the instrumentation method entry trampoline
+ // so we will just disable jit-gc if we are doing that.
+ bool garbage_collect_code = !generate_debug_info &&
+ !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled();
// We need to have 32 bit offsets from method headers in code cache which point to things
// in the data cache. If the maps are more than 4G apart, having multiple maps wouldn't work.
@@ -316,6 +319,17 @@
return code_map_->Begin() <= ptr && ptr < code_map_->End();
}
+bool JitCodeCache::WillExecuteJitCode(ArtMethod* method) {
+ ScopedObjectAccess soa(art::Thread::Current());
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+ if (ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
+ return true;
+ } else if (method->GetEntryPointFromQuickCompiledCode() == GetQuickInstrumentationEntryPoint()) {
+ return FindCompiledCodeForInstrumentation(method) != nullptr;
+ }
+ return false;
+}
+
bool JitCodeCache::ContainsMethod(ArtMethod* method) {
MutexLock mu(Thread::Current(), lock_);
if (UNLIKELY(method->IsNative())) {
@@ -348,6 +362,20 @@
return nullptr;
}
+const void* JitCodeCache::FindCompiledCodeForInstrumentation(ArtMethod* method) {
+ if (LIKELY(!GetGarbageCollectCode())) {
+ return nullptr;
+ }
+ ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize);
+ if (info == nullptr) {
+ return nullptr;
+ }
+ // When GC is disabled for trampoline tracing we will use SavedEntrypoint to hold the actual
+ // jit-compiled version of the method. If jit-gc is disabled for other reasons this will just be
+ // nullptr.
+ return info->GetSavedEntryPoint();
+}
+
class ScopedCodeCacheWrite : ScopedTrace {
public:
explicit ScopedCodeCacheWrite(const JitCodeCache* const code_cache)
@@ -978,6 +1006,8 @@
// checks should always pass.
DCHECK(!info->IsInUseByCompiler());
new_method->SetProfilingInfo(info);
+ // Get rid of the old saved entrypoint if it is there.
+ info->SetSavedEntryPoint(nullptr);
info->method_ = new_method;
}
// Update method_code_map_ to point to the new method.
@@ -1321,7 +1351,8 @@
if (GetLiveBitmap()->Test(allocation)) {
++it;
} else {
- method_headers.insert(OatQuickMethodHeader::FromCodePointer(code_ptr));
+ OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+ method_headers.insert(header);
it = method_code_map_.erase(it);
}
}
@@ -1786,13 +1817,18 @@
const OatQuickMethodHeader* header) {
DCHECK(!method->IsNative());
ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize);
+ const void* method_entrypoint = method->GetEntryPointFromQuickCompiledCode();
if ((profiling_info != nullptr) &&
(profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) {
+ // When instrumentation is set, the actual entrypoint is the one in the profiling info.
+ method_entrypoint = profiling_info->GetSavedEntryPoint();
// Prevent future uses of the compiled code.
profiling_info->SetSavedEntryPoint(nullptr);
}
- if (method->GetEntryPointFromQuickCompiledCode() == header->GetEntryPoint()) {
+ // Clear the method counter if we are running jitted code since we might want to jit this again in
+ // the future.
+ if (method_entrypoint == header->GetEntryPoint()) {
// The entrypoint is the one to invalidate, so we just update it to the interpreter entry point
// and clear the counter to get the method Jitted again.
Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index b056bc3..f7829de 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -154,6 +154,10 @@
// Return true if the code cache contains this pc.
bool ContainsPc(const void* pc) const;
+ // Returns true if either the method's entrypoint is JIT compiled code or it is the
+ // instrumentation entrypoint and we can jump to jit code for this method. For testing use only.
+ bool WillExecuteJitCode(ArtMethod* method) REQUIRES(!lock_);
+
// Return true if the code cache contains this method.
bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_);
@@ -269,6 +273,16 @@
garbage_collect_code_ = value;
}
+ bool GetGarbageCollectCode() const {
+ return garbage_collect_code_;
+ }
+
+ // If Jit-gc has been disabled (and instrumentation has been enabled) this will return the
+ // jit-compiled entrypoint for this method. Otherwise it will return null.
+ const void* FindCompiledCodeForInstrumentation(ArtMethod* method)
+ REQUIRES(!lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
// Take ownership of maps.
JitCodeCache(MemMap* code_map,