/* * Copyright (C) 2025 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_INSTRUMENTATION_INL_H_ #define ART_RUNTIME_INSTRUMENTATION_INL_H_ #include "instrumentation.h" #include "art_method-inl.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/heap.h" #include "jit/jit.h" #include "runtime.h" namespace art HIDDEN { namespace instrumentation { inline bool Instrumentation::CanUseAotCode(const void* quick_code) { if (quick_code == nullptr) { return false; } Runtime* runtime = Runtime::Current(); // For simplicity, we never use AOT code for debuggable. if (runtime->IsJavaDebuggable()) { return false; } 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 (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); } return true; } inline const void* Instrumentation::GetInitialEntrypoint(uint32_t method_access_flags, const void* aot_code) { if (!ArtMethod::IsInvokable(method_access_flags)) { return GetQuickToInterpreterBridge(); } // Special case if we need an initialization check. if (ArtMethod::NeedsClinitCheckBeforeCall(method_access_flags)) { // If we have code but the method needs a class initialization check before calling that code, // install the resolution stub that will perform the check. It will be replaced by the proper // entry point by `ClassLinker::FixupStaticTrampolines()` after initializing class. // Note: This mimics the logic in image_writer.cc that installs the resolution stub only // if we have compiled code or we can execute nterp, and the method needs a class // initialization check. return (aot_code != nullptr || ArtMethod::IsNative(method_access_flags)) ? GetQuickResolutionStub() : GetQuickToInterpreterBridge(); } // Use the provided AOT code if possible. if (CanUseAotCode(aot_code)) { return aot_code; } // Use default entrypoints. return ArtMethod::IsNative(method_access_flags) ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge(); } inline bool Instrumentation::InitialEntrypointNeedsInstrumentationStubs() { return IsForcedInterpretOnly() || EntryExitStubsInstalled(); } inline void Instrumentation::InitializeMethodsCode(ArtMethod* method, const void* entrypoint, PointerSize pointer_size) { if (kIsDebugBuild) { // Entrypoint should be uninitialized. CHECK(method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size) == nullptr) << method->PrettyMethod(); // We initialize the entrypoint while loading the class, well before the class // is verified and Nterp entrypoint is allowed. We prefer to check for resolved // because a verified class may lose its "verified" status (by becoming erroneous) // but the resolved status is always kept (as "resolved erroneous" if needed). CHECK(!method->GetDeclaringClass()->IsResolved()); CHECK_NE(entrypoint, interpreter::GetNterpEntryPoint()) << method->PrettyMethod(); if (InitialEntrypointNeedsInstrumentationStubs()) { const void* expected = method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge(); CHECK_EQ(entrypoint, expected) << method->PrettyMethod() << " " << method->IsNative(); } else if (method->NeedsClinitCheckBeforeCall()) { if (method->IsNative()) { CHECK_EQ(entrypoint, GetQuickResolutionStub()); } else { // We do not have the original `aot_code` to determine which entrypoint to expect. CHECK(entrypoint == GetQuickResolutionStub() || entrypoint == GetQuickToInterpreterBridge()); } } else { bool is_stub = (entrypoint == GetQuickToInterpreterBridge()) || (entrypoint == GetQuickGenericJniStub()) || (entrypoint == GetQuickResolutionStub()); const void* aot_code = is_stub ? nullptr : entrypoint; const void* initial = GetInitialEntrypoint(method->GetAccessFlags(), aot_code); CHECK_EQ(initial, entrypoint) << method->PrettyMethod() << " 0x" << std::hex << method->GetAccessFlags(); } } method->SetEntryPointFromQuickCompiledCodePtrSize(entrypoint, pointer_size); } } // namespace instrumentation } // namespace art #endif // ART_RUNTIME_INSTRUMENTATION_INL_H_