summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Mythri Alle <mythria@google.com> 2022-03-23 12:49:30 +0000
committer Mythri Alle <mythria@google.com> 2022-07-12 13:03:15 +0000
commitfc067a360d14db5f84fd4b58e0dee6cb04ee759b (patch)
treea9217edb3a03e3937411407f704ad26e5887fb9a
parent0ae89052f7213701b8b3a782266e84b3d3600dbf (diff)
Introduce a flag to check if JITed code has instrumentation support
Introduce a new flag to identify if JITed code was compiled with instrumentation support. We used to check if the runtime is java debuggable to check for instrumentation support of JITed code. We only set the java debuggable at runtime init and never changed it after. So this check was sufficient since we always JIT code with instrumentation support in debuggable runtimes. We want to be able to change the runtime to debuggable after the runtime has started. As a first step, introduce a new flag to explicitly check if JITed code was compiled with instrumentation support. Use this flag to check if code needs entry / exit stubs and to check if code is async deoptimizeable. Bug: 222479430 Test: art/test.py Change-Id: Ibcaeab869aa8ce153920a801dcc60988411c775b
-rw-r--r--compiler/exception_test.cc7
-rw-r--r--compiler/jni/quick/jni_compiler.cc16
-rw-r--r--compiler/optimizing/code_generator.cc3
-rw-r--r--compiler/optimizing/optimizing_compiler.cc29
-rw-r--r--compiler/optimizing/stack_map_stream.cc5
-rw-r--r--compiler/optimizing/stack_map_stream.h4
-rw-r--r--compiler/optimizing/stack_map_test.cc56
-rw-r--r--dex2oat/linker/code_info_table_deduper_test.cc7
-rw-r--r--libartbase/base/bit_memory_region.h33
-rw-r--r--openjdkjvmti/events.cc2
-rw-r--r--runtime/check_reference_map_visitor.h2
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc2
-rw-r--r--runtime/instrumentation.cc98
-rw-r--r--runtime/quick_exception_handler.cc2
-rw-r--r--runtime/runtime.cc13
-rw-r--r--runtime/runtime.h3
-rw-r--r--runtime/stack_map.h11
-rw-r--r--runtime/thread.cc2
-rw-r--r--test/543-env-long-ref/env_long_ref.cc3
-rw-r--r--test/common/stack_inspect.cc3
20 files changed, 207 insertions, 94 deletions
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index 4471b93f17..a49c6c630e 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -78,7 +78,12 @@ class ExceptionTest : public CommonRuntimeTest {
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stack_maps(&allocator, kRuntimeISA);
- stack_maps.BeginMethod(4 * sizeof(void*), 0u, 0u, 0u);
+ stack_maps.BeginMethod(/* frame_size_in_bytes= */ 4 * sizeof(void*),
+ /* core_spill_mask= */ 0u,
+ /* fp_spill_mask= */ 0u,
+ /* num_dex_registers= */ 0u,
+ /* baseline= */ false,
+ /* debuggable= */ false);
stack_maps.BeginStackMapEntry(kDexPc, native_pc_offset);
stack_maps.EndStackMapEntry();
stack_maps.EndMethod(code_size);
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index d672500126..42072eb6e0 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -103,19 +103,19 @@ static JniCompiledMethod ArtJniCompileMethodInternal(const CompilerOptions& comp
// i.e. if the method was annotated with @CriticalNative
const bool is_critical_native = (access_flags & kAccCriticalNative) != 0u;
+ bool needs_entry_exit_hooks =
+ compiler_options.GetDebuggable() && compiler_options.IsJitCompiler();
+ // We don't support JITing stubs for critical native methods in debuggable runtimes yet.
+ // TODO(mythria): Add support required for calling method entry / exit hooks from critical native
+ // methods.
+ DCHECK_IMPLIES(needs_entry_exit_hooks, !is_critical_native);
+
// When walking the stack the top frame doesn't have a pc associated with it. We then depend on
// the invariant that we don't have JITed code when AOT code is available. In debuggable runtimes
// this invariant doesn't hold. So we tag the SP for JITed code to indentify if we are executing
// JITed code or AOT code. Since tagging involves additional instructions we tag only in
// debuggable runtimes.
- bool should_tag_sp = compiler_options.GetDebuggable() && compiler_options.IsJitCompiler();
-
- // We don't JIT stubs for critical native methods in debuggable runtimes.
- // TODO(mythria): Add support required for calling method entry / exit hooks from critical native
- // methods.
- bool needs_entry_exit_hooks = compiler_options.GetDebuggable() &&
- compiler_options.IsJitCompiler() &&
- !is_critical_native;
+ bool should_tag_sp = needs_entry_exit_hooks;
VLOG(jni) << "JniCompile: Method :: "
<< dex_file.PrettyMethod(method_idx, /* with signature */ true)
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 8bd4406332..d8fc3ba690 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -389,7 +389,8 @@ void CodeGenerator::Compile(CodeAllocator* allocator) {
core_spill_mask_,
fpu_spill_mask_,
GetGraph()->GetNumberOfVRegs(),
- GetGraph()->IsCompilingBaseline());
+ GetGraph()->IsCompilingBaseline(),
+ GetGraph()->IsDebuggable());
size_t frame_start = GetAssembler()->CodeSize();
GenerateFrameEntry();
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index a499c55757..b0fa251f55 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -1115,17 +1115,18 @@ CompiledMethod* OptimizingCompiler::Compile(const dex::CodeItem* code_item,
static ScopedArenaVector<uint8_t> CreateJniStackMap(ScopedArenaAllocator* allocator,
const JniCompiledMethod& jni_compiled_method,
- size_t code_size) {
+ size_t code_size,
+ bool debuggable) {
// StackMapStream is quite large, so allocate it using the ScopedArenaAllocator
// to stay clear of the frame size limit.
std::unique_ptr<StackMapStream> stack_map_stream(
new (allocator) StackMapStream(allocator, jni_compiled_method.GetInstructionSet()));
- stack_map_stream->BeginMethod(
- jni_compiled_method.GetFrameSize(),
- jni_compiled_method.GetCoreSpillMask(),
- jni_compiled_method.GetFpSpillMask(),
- /* num_dex_registers= */ 0,
- /* baseline= */ false);
+ stack_map_stream->BeginMethod(jni_compiled_method.GetFrameSize(),
+ jni_compiled_method.GetCoreSpillMask(),
+ jni_compiled_method.GetFpSpillMask(),
+ /* num_dex_registers= */ 0,
+ /* baseline= */ false,
+ debuggable);
stack_map_stream->EndMethod(code_size);
return stack_map_stream->Encode();
}
@@ -1187,8 +1188,11 @@ CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags,
MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledNativeStub);
ScopedArenaAllocator stack_map_allocator(&arena_stack); // Will hold the stack map.
- ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(
- &stack_map_allocator, jni_compiled_method, jni_compiled_method.GetCode().size());
+ ScopedArenaVector<uint8_t> stack_map =
+ CreateJniStackMap(&stack_map_allocator,
+ jni_compiled_method,
+ jni_compiled_method.GetCode().size(),
+ compiler_options.GetDebuggable() && compiler_options.IsJitCompiler());
return CompiledMethod::SwapAllocCompiledMethod(
GetCompiledMethodStorage(),
jni_compiled_method.GetInstructionSet(),
@@ -1249,8 +1253,11 @@ bool OptimizingCompiler::JitCompile(Thread* self,
ArenaStack arena_stack(runtime->GetJitArenaPool());
// StackMapStream is large and it does not fit into this frame, so we need helper method.
ScopedArenaAllocator stack_map_allocator(&arena_stack); // Will hold the stack map.
- ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(
- &stack_map_allocator, jni_compiled_method, jni_compiled_method.GetCode().size());
+ ScopedArenaVector<uint8_t> stack_map =
+ CreateJniStackMap(&stack_map_allocator,
+ jni_compiled_method,
+ jni_compiled_method.GetCode().size(),
+ compiler_options.GetDebuggable() && compiler_options.IsJitCompiler());
ArrayRef<const uint8_t> reserved_code;
ArrayRef<const uint8_t> reserved_data;
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index f55bbee1c8..c13a35567b 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -49,7 +49,8 @@ void StackMapStream::BeginMethod(size_t frame_size_in_bytes,
size_t core_spill_mask,
size_t fp_spill_mask,
uint32_t num_dex_registers,
- bool baseline) {
+ bool baseline,
+ bool debuggable) {
DCHECK(!in_method_) << "Mismatched Begin/End calls";
in_method_ = true;
DCHECK_EQ(packed_frame_size_, 0u) << "BeginMethod was already called";
@@ -60,6 +61,7 @@ void StackMapStream::BeginMethod(size_t frame_size_in_bytes,
fp_spill_mask_ = fp_spill_mask;
num_dex_registers_ = num_dex_registers;
baseline_ = baseline;
+ debuggable_ = debuggable;
if (kVerifyStackMaps) {
dchecks_.emplace_back([=](const CodeInfo& code_info) {
@@ -367,6 +369,7 @@ ScopedArenaVector<uint8_t> StackMapStream::Encode() {
uint32_t flags = (inline_infos_.size() > 0) ? CodeInfo::kHasInlineInfo : 0;
flags |= baseline_ ? CodeInfo::kIsBaseline : 0;
+ flags |= debuggable_ ? CodeInfo::kIsDebuggable : 0;
DCHECK_LE(flags, kVarintMax); // Ensure flags can be read directly as byte.
uint32_t bit_table_flags = 0;
ForEachBitTable([&bit_table_flags](size_t i, auto bit_table) {
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 27145a174c..1aaa6aee9e 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -64,7 +64,8 @@ class StackMapStream : public DeletableArenaObject<kArenaAllocStackMapStream> {
size_t core_spill_mask,
size_t fp_spill_mask,
uint32_t num_dex_registers,
- bool baseline = false);
+ bool baseline,
+ bool debuggable);
void EndMethod(size_t code_size);
void BeginStackMapEntry(uint32_t dex_pc,
@@ -125,6 +126,7 @@ class StackMapStream : public DeletableArenaObject<kArenaAllocStackMapStream> {
uint32_t fp_spill_mask_ = 0;
uint32_t num_dex_registers_ = 0;
bool baseline_;
+ bool debuggable_;
BitTableBuilder<StackMap> stack_maps_;
BitTableBuilder<RegisterMask> register_masks_;
BitmapTableBuilder stack_masks_;
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index f6a739e15a..23af1f7fa1 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -52,7 +52,12 @@ TEST(StackMapTest, Test1) {
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
size_t number_of_dex_registers = 2;
@@ -106,7 +111,12 @@ TEST(StackMapTest, Test2) {
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArtMethod art_method;
ArenaBitVector sp_mask1(&allocator, 0, true);
@@ -300,7 +310,12 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) {
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArtMethod art_method;
ArenaBitVector sp_mask1(&allocator, 0, true);
@@ -363,7 +378,12 @@ TEST(StackMapTest, TestNonLiveDexRegisters) {
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
uint32_t number_of_dex_registers = 2;
@@ -411,7 +431,12 @@ TEST(StackMapTest, TestShareDexRegisterMap) {
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
uint32_t number_of_dex_registers = 2;
@@ -467,7 +492,12 @@ TEST(StackMapTest, TestNoDexRegisterMap) {
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 1);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 1,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask);
@@ -512,7 +542,12 @@ TEST(StackMapTest, InlineTest) {
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArtMethod art_method;
ArenaBitVector sp_mask1(&allocator, 0, true);
@@ -702,7 +737,12 @@ TEST(StackMapTest, TestDeduplicateStackMask) {
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 0);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 0,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, true);
sp_mask.SetBit(1);
diff --git a/dex2oat/linker/code_info_table_deduper_test.cc b/dex2oat/linker/code_info_table_deduper_test.cc
index 8913b07a51..54b7dd5940 100644
--- a/dex2oat/linker/code_info_table_deduper_test.cc
+++ b/dex2oat/linker/code_info_table_deduper_test.cc
@@ -35,7 +35,12 @@ TEST(StackMapTest, TestDedupeBitTables) {
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
stream.BeginStackMapEntry(0, 64 * kPcAlign);
stream.AddDexRegisterEntry(Kind::kInStack, 0);
diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h
index c5224a5421..baac2f50ea 100644
--- a/libartbase/base/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -324,8 +324,37 @@ class BitMemoryRegion final : public ValueObject {
size_t bit_size_ = 0;
};
-constexpr uint32_t kVarintBits = 4; // Minimum number of bits used for varint.
-constexpr uint32_t kVarintMax = 11; // Maximum value which is stored "inline".
+// Minimum number of bits used for varint. A varint represents either a value stored "inline" or
+// the number of bytes that are required to encode the value.
+constexpr uint32_t kVarintBits = 4;
+// Maximum value which is stored "inline". We use the rest of the values to encode the number of
+// bytes required to encode the value when the value is greater than kVarintMax.
+// We encode any value less than or equal to 11 inline. We use 12, 13, 14 and 15
+// to represent that the value is encoded in 1, 2, 3 and 4 bytes respectively.
+//
+// For example if we want to encode 1, 15, 16, 7, 11, 256:
+//
+// Low numbers (1, 7, 11) are encoded inline. 15 and 12 are set with 12 to show
+// we need to load one byte for each to have their real values (15 and 12), and
+// 256 is set with 13 to show we need to load two bytes. This is done to
+// compress the values in the bit array and keep the size down. Where the actual value
+// is read from depends on the use case.
+//
+// Values greater than kVarintMax could be encoded as a separate list referred
+// to as InterleavedVarints (see ReadInterleavedVarints / WriteInterleavedVarints).
+// This is used when there are fixed number of fields like CodeInfo headers.
+// In our example the interleaved encoding looks like below:
+//
+// Meaning: 1--- 15-- 12-- 7--- 11-- 256- 15------- 12------- 256----------------
+// Bits: 0001 1100 1100 0111 1011 1101 0000 1111 0000 1100 0000 0001 0000 0000
+//
+// In other cases the value is recorded just following the size encoding. This is
+// referred as consecutive encoding (See ReadVarint / WriteVarint). In our
+// example the consecutively encoded varints looks like below:
+//
+// Meaning: 1--- 15-- 15------- 12-- 12------- 7--- 11-- 256- 256----------------
+// Bits: 0001 1100 0000 1100 1100 0000 1100 0111 1011 1101 0000 0001 0000 0000
+constexpr uint32_t kVarintMax = 11;
class BitMemoryReader {
public:
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 58601aa3fc..e23beecc99 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -1258,7 +1258,7 @@ void EventHandler::HandleLocalAccessCapabilityAdded() {
if (m.IsNative() || m.IsProxyMethod()) {
continue;
} else if (!runtime_->GetClassLinker()->IsQuickToInterpreterBridge(code) &&
- !runtime_->IsAsyncDeoptimizeable(reinterpret_cast<uintptr_t>(code))) {
+ !runtime_->IsAsyncDeoptimizeable(&m, reinterpret_cast<uintptr_t>(code))) {
runtime_->GetInstrumentation()->InitializeMethodsCode(&m, /*aot_code=*/ nullptr);
}
}
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index a7c3e45ae7..d0cb199aa2 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -91,7 +91,7 @@ class CheckReferenceMapVisitor : public StackVisitor {
CodeItemDataAccessor accessor(m->DexInstructionData());
uint16_t number_of_dex_registers = accessor.RegistersSize();
- if (!Runtime::Current()->IsAsyncDeoptimizeable(GetCurrentQuickFramePc())) {
+ if (!Runtime::Current()->IsAsyncDeoptimizeable(m, GetCurrentQuickFramePc())) {
// We can only guarantee dex register info presence for debuggable methods.
return;
}
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 7e3fdee3e1..fc7711027f 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -714,7 +714,7 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self,
if (UNLIKELY(instr->ShouldDeoptimizeCaller(self, sp))) {
ArtMethod* caller = QuickArgumentVisitor::GetOuterMethod(sp);
uintptr_t caller_pc = QuickArgumentVisitor::GetCallingPc(sp);
- DCHECK(Runtime::Current()->IsAsyncDeoptimizeable(caller_pc));
+ DCHECK(Runtime::Current()->IsAsyncDeoptimizeable(caller, caller_pc));
DCHECK(caller != nullptr);
VLOG(deopt) << "Forcing deoptimization on return from method " << method->PrettyMethod()
<< " to " << caller->PrettyMethod() << (force_frame_pop ? " for frame-pop" : "");
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 61b0e526b0..08e7a7e079 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -213,7 +213,7 @@ static bool IsProxyInit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_)
// Returns true if we need entry exit stub to call entry hooks. JITed code
// directly call entry / exit hooks and don't need the stub.
-static bool CodeNeedsEntryExitStub(const void* code, ArtMethod* method)
+static bool CodeNeedsEntryExitStub(const void* entry_point, ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Proxy.init should never have entry/exit stubs.
if (IsProxyInit(method)) {
@@ -222,32 +222,31 @@ static bool CodeNeedsEntryExitStub(const void* code, ArtMethod* method)
// In some tests runtime isn't setup fully and hence the entry points could
// be nullptr.
- if (code == nullptr) {
+ if (entry_point == nullptr) {
return true;
}
// Code running in the interpreter doesn't need entry/exit stubs.
- if (Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(code)) {
+ if (Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(entry_point)) {
return false;
}
- if (!Runtime::Current()->IsJavaDebuggable()) {
- return true;
+ // When jiting code for debuggable runtimes / instrumentation is active we generate the code to
+ // call method entry / exit hooks when required. Hence it is not required to update to
+ // instrumentation entry point for JITed code in debuggable mode.
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr && jit->GetCodeCache()->ContainsPc(entry_point)) {
+ // If JITed code was compiled with instrumentation support we don't need entry / exit stub.
+ OatQuickMethodHeader* header = OatQuickMethodHeader::FromEntryPoint(entry_point);
+ return !CodeInfo::IsDebuggable(header->GetOptimizedCodeInfoPtr());
}
- // Native methods don't need method entry / exit hooks in debuggable runtimes.
- // GenericJni trampoline and JITed JNI stubs handle entry / exit hooks
- if (method->IsNative()) {
+ // GenericJni trampoline can handle entry / exit hooks in debuggable runtimes.
+ if (Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(entry_point) &&
+ Runtime::Current()->IsJavaDebuggable()) {
return false;
}
- // When jiting code for debuggable apps we generate the code to call method
- // entry / exit hooks when required. Hence it is not required to update
- // to instrumentation entry point for JITed code in debuggable mode.
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) {
- return false;
- }
return true;
}
@@ -1669,7 +1668,7 @@ bool Instrumentation::ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp) {
}
if (NeedsSlowInterpreterForMethod(self, caller)) {
- if (!Runtime::Current()->IsAsyncDeoptimizeable(caller_pc)) {
+ if (!Runtime::Current()->IsAsyncDeoptimizeable(caller, caller_pc)) {
LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
<< caller->PrettyMethod();
return false;
@@ -1694,10 +1693,10 @@ bool Instrumentation::ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp) {
}
}
- if (should_deoptimize_frame && !Runtime::Current()->IsAsyncDeoptimizeable(caller_pc)) {
- LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
- << caller->PrettyMethod();
- return false;
+ if (should_deoptimize_frame && !Runtime::Current()->IsAsyncDeoptimizeable(caller, caller_pc)) {
+ LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
+ << caller->PrettyMethod();
+ return false;
}
return should_deoptimize_frame;
}
@@ -1776,36 +1775,39 @@ TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self,
// Restore the return value if it's a reference since it might have moved.
*reinterpret_cast<mirror::Object**>(gpr_result) = res.Get();
}
- if (deoptimize && Runtime::Current()->IsAsyncDeoptimizeable(*return_pc_addr)) {
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Deoptimizing "
- << visitor.caller->PrettyMethod()
- << " by returning from "
- << method->PrettyMethod()
- << " with result "
- << std::hex << return_value.GetJ() << std::dec
- << " in "
- << *self;
- }
- DeoptimizationMethodType deopt_method_type = GetDeoptimizationMethodType(method);
- self->PushDeoptimizationContext(return_value,
- is_ref,
- /* exception= */ nullptr,
- /* from_code= */ false,
- deopt_method_type);
- return GetTwoWordSuccessValue(*return_pc_addr,
- reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint()));
- } else {
- if (deoptimize && !Runtime::Current()->IsAsyncDeoptimizeable(*return_pc_addr)) {
- VLOG(deopt) << "Got a deoptimization request on un-deoptimizable " << method->PrettyMethod()
- << " at PC " << reinterpret_cast<void*>(*return_pc_addr);
- }
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Returning from " << method->PrettyMethod()
- << " to PC " << reinterpret_cast<void*>(*return_pc_addr);
+
+ if (deoptimize) {
+ if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller, *return_pc_addr)) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Deoptimizing "
+ << visitor.caller->PrettyMethod()
+ << " by returning from "
+ << method->PrettyMethod()
+ << " with result "
+ << std::hex << return_value.GetJ() << std::dec
+ << " in "
+ << *self;
+ }
+ DeoptimizationMethodType deopt_method_type = GetDeoptimizationMethodType(method);
+ self->PushDeoptimizationContext(return_value,
+ is_ref,
+ /* exception= */ nullptr,
+ /* from_code= */ false,
+ deopt_method_type);
+ return GetTwoWordSuccessValue(
+ *return_pc_addr, reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint()));
+ } else {
+ VLOG(deopt) << "Got a deoptimization request on un-deoptimizable "
+ << visitor.caller->PrettyMethod() << " at PC "
+ << reinterpret_cast<void*>(*return_pc_addr);
}
- return GetTwoWordSuccessValue(0, *return_pc_addr);
}
+
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Returning from " << method->PrettyMethod() << " to PC "
+ << reinterpret_cast<void*>(*return_pc_addr);
+ }
+ return GetTwoWordSuccessValue(0, *return_pc_addr);
}
uintptr_t Instrumentation::PopInstrumentationStackUntil(Thread* self, uintptr_t pop_until) const {
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 40a1c16905..1b43717860 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -406,7 +406,7 @@ class DeoptimizeStackVisitor final : public StackVisitor {
callee_method_ = method;
return true;
} else if (!single_frame_deopt_ &&
- !Runtime::Current()->IsAsyncDeoptimizeable(GetCurrentQuickFramePc())) {
+ !Runtime::Current()->IsAsyncDeoptimizeable(method, 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 "
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 72fa118f5b..3645695bce 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -3068,12 +3068,13 @@ bool Runtime::IsVerificationSoftFail() const {
return verify_ == verifier::VerifyMode::kSoftFail;
}
-bool Runtime::IsAsyncDeoptimizeable(uintptr_t code) const {
+bool Runtime::IsAsyncDeoptimizeable(ArtMethod* method, uintptr_t code) const {
if (OatQuickMethodHeader::NterpMethodHeader != nullptr) {
if (OatQuickMethodHeader::NterpMethodHeader->Contains(code)) {
return true;
}
}
+
// 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,
@@ -3081,8 +3082,14 @@ bool Runtime::IsAsyncDeoptimizeable(uintptr_t code) const {
// only rely on the JIT for debuggable apps.
// The JIT-zygote is not debuggable so we need to be sure to exclude code from the non-private
// region as well.
- return IsJavaDebuggable() && GetJit() != nullptr &&
- GetJit()->GetCodeCache()->PrivateRegionContainsPc(reinterpret_cast<const void*>(code));
+ if (GetJit() != nullptr &&
+ GetJit()->GetCodeCache()->PrivateRegionContainsPc(reinterpret_cast<const void*>(code))) {
+ // If the code is JITed code then check if it was compiled as debuggable.
+ const OatQuickMethodHeader* header = method->GetOatQuickMethodHeader(code);
+ return CodeInfo::IsDebuggable(header->GetOptimizedCodeInfoPtr());
+ }
+
+ return false;
}
LinearAlloc* Runtime::CreateLinearAlloc() {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 91f164465b..38741f1706 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -901,7 +901,8 @@ class Runtime {
// Returns if the code can be deoptimized asynchronously. Code may be compiled with some
// optimization that makes it impossible to deoptimize.
- bool IsAsyncDeoptimizeable(uintptr_t code) const REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsAsyncDeoptimizeable(ArtMethod* method, 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.
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 7a13dbd3ac..7876a67381 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -449,6 +449,10 @@ class CodeInfo {
return (*code_info_data & kIsBaseline) != 0;
}
+ ALWAYS_INLINE static bool IsDebuggable(const uint8_t* code_info_data) {
+ return (*code_info_data & kIsDebuggable) != 0;
+ }
+
private:
// Scan backward to determine dex register locations at given stack map.
void DecodeDexRegisterMap(uint32_t stack_map_index,
@@ -495,11 +499,16 @@ class CodeInfo {
enum Flags {
kHasInlineInfo = 1 << 0,
kIsBaseline = 1 << 1,
+ kIsDebuggable = 1 << 2,
};
// The CodeInfo starts with sequence of variable-length bit-encoded integers.
+ // (Please see kVarintMax for more details about encoding).
static constexpr size_t kNumHeaders = 7;
- uint32_t flags_ = 0; // Note that the space is limited to three bits.
+ // Note that the space for flags is limited to three bits. We use a custom encoding where we
+ // encode the value inline if it is less than kVarintMax. We want to access flags without
+ // decoding the entire CodeInfo so the value of flags cannot be more than kVarintMax.
+ uint32_t flags_ = 0;
uint32_t code_size_ = 0; // The size of native PC range in bytes.
uint32_t packed_frame_size_ = 0; // Frame size in kStackAlignment units.
uint32_t core_spill_mask_ = 0;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 0c146d5bf1..73c309cba3 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3772,7 +3772,7 @@ void Thread::QuickDeliverException(bool is_method_exit_exception) {
if (Dbg::IsForcedInterpreterNeededForException(this) || force_deopt || IsForceInterpreter()) {
NthCallerVisitor visitor(this, 0, false);
visitor.WalkStack();
- if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) {
+ if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller, visitor.caller_pc)) {
// method_type shouldn't matter due to exception handling.
const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
// Save the exception into the deoptimization context so it can be restored
diff --git a/test/543-env-long-ref/env_long_ref.cc b/test/543-env-long-ref/env_long_ref.cc
index 1c30d469f8..bfaf1f15bd 100644
--- a/test/543-env-long-ref/env_long_ref.cc
+++ b/test/543-env-long-ref/env_long_ref.cc
@@ -36,7 +36,8 @@ extern "C" JNIEXPORT void JNICALL Java_Main_lookForMyRegisters(JNIEnv*, jclass,
found = true;
// For optimized non-debuggable code do not expect dex register info to be present.
if (stack_visitor->GetCurrentShadowFrame() == nullptr &&
- !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetCurrentQuickFramePc())) {
+ !Runtime::Current()->IsAsyncDeoptimizeable(m,
+ stack_visitor->GetCurrentQuickFramePc())) {
return true;
}
uint32_t stack_value = 0;
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index 79c7a36a8e..7537705d21 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -195,7 +195,8 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_getThisOfCaller(
if (stack_visitor->GetMethod() == nullptr ||
stack_visitor->GetMethod()->IsNative() ||
(stack_visitor->GetCurrentShadowFrame() == nullptr &&
- !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetCurrentQuickFramePc()))) {
+ !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetMethod(),
+ stack_visitor->GetCurrentQuickFramePc()))) {
return true;
}
result = soa.AddLocalReference<jobject>(stack_visitor->GetThisObject());