diff options
Diffstat (limited to 'runtime')
| -rw-r--r-- | runtime/asm_support.h | 2 | ||||
| -rw-r--r-- | runtime/debugger.cc | 76 | ||||
| -rw-r--r-- | runtime/debugger.h | 4 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_goto_table_impl.cc | 47 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_switch_impl.cc | 27 | ||||
| -rw-r--r-- | runtime/jdwp/jdwp_event.cc | 2 | ||||
| -rw-r--r-- | runtime/oat.h | 2 | ||||
| -rw-r--r-- | runtime/reflection.cc | 17 | ||||
| -rw-r--r-- | runtime/reflection.h | 4 | ||||
| -rw-r--r-- | runtime/thread.h | 18 | ||||
| -rw-r--r-- | runtime/utf.h | 6 | ||||
| -rw-r--r-- | runtime/utils.cc | 52 | ||||
| -rw-r--r-- | runtime/utils_test.cc | 23 |
13 files changed, 164 insertions, 116 deletions
diff --git a/runtime/asm_support.h b/runtime/asm_support.h index a115fbe0f4..de4783a5f8 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -89,7 +89,7 @@ ADD_TEST_EQ(THREAD_ID_OFFSET, art::Thread::ThinLockIdOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.card_table. -#define THREAD_CARD_TABLE_OFFSET 120 +#define THREAD_CARD_TABLE_OFFSET 128 ADD_TEST_EQ(THREAD_CARD_TABLE_OFFSET, art::Thread::CardTableOffset<__SIZEOF_POINTER__>().Int32Value()) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index d7363d8f0a..9b33e50881 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -235,13 +235,29 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati virtual ~DebugInstrumentationListener() {} void MethodEntered(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, - uint32_t dex_pc ATTRIBUTE_UNUSED) + uint32_t dex_pc) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (method->IsNative()) { // TODO: post location events is a suspension point and native method entry stubs aren't. return; } - Dbg::UpdateDebugger(thread, this_object, method, 0, Dbg::kMethodEntry, nullptr); + if (IsListeningToDexPcMoved()) { + // We also listen to kDexPcMoved instrumentation event so we know the DexPcMoved method is + // going to be called right after us. To avoid sending JDWP events twice for this location, + // we report the event in DexPcMoved. However, we must remind this is method entry so we + // send the METHOD_ENTRY event. And we can also group it with other events for this location + // like BREAKPOINT or SINGLE_STEP (or even METHOD_EXIT if this is a RETURN instruction). + thread->SetDebugMethodEntry(); + } else if (IsListeningToMethodExit() && IsReturn(method, dex_pc)) { + // We also listen to kMethodExited instrumentation event and the current instruction is a + // RETURN so we know the MethodExited method is going to be called right after us. To avoid + // sending JDWP events twice for this location, we report the event(s) in MethodExited. + // However, we must remind this is method entry so we send the METHOD_ENTRY event. And we can + // also group it with other events for this location like BREAKPOINT or SINGLE_STEP. + thread->SetDebugMethodEntry(); + } else { + Dbg::UpdateDebugger(thread, this_object, method, 0, Dbg::kMethodEntry, nullptr); + } } void MethodExited(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, @@ -251,14 +267,20 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati // TODO: post location events is a suspension point and native method entry stubs aren't. return; } - Dbg::UpdateDebugger(thread, this_object, method, dex_pc, Dbg::kMethodExit, &return_value); + uint32_t events = Dbg::kMethodExit; + if (thread->IsDebugMethodEntry()) { + // It is also the method entry. + DCHECK(IsReturn(method, dex_pc)); + events |= Dbg::kMethodEntry; + thread->ClearDebugMethodEntry(); + } + Dbg::UpdateDebugger(thread, this_object, method, dex_pc, events, &return_value); } - void MethodUnwind(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, - uint32_t dex_pc) + void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object ATTRIBUTE_UNUSED, + mirror::ArtMethod* method, uint32_t dex_pc) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // We're not recorded to listen to this kind of event, so complain. - UNUSED(thread, this_object, method, dex_pc); LOG(ERROR) << "Unexpected method unwind event in debugger " << PrettyMethod(method) << " " << dex_pc; } @@ -266,13 +288,27 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati void DexPcMoved(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, uint32_t new_dex_pc) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc, 0, nullptr); + if (IsListeningToMethodExit() && IsReturn(method, new_dex_pc)) { + // We also listen to kMethodExited instrumentation event and the current instruction is a + // RETURN so we know the MethodExited method is going to be called right after us. Like in + // MethodEntered, we delegate event reporting to MethodExited. + // Besides, if this RETURN instruction is the only one in the method, we can send multiple + // JDWP events in the same packet: METHOD_ENTRY, METHOD_EXIT, BREAKPOINT and/or SINGLE_STEP. + // Therefore, we must not clear the debug method entry flag here. + } else { + uint32_t events = 0; + if (thread->IsDebugMethodEntry()) { + // It is also the method entry. + events = Dbg::kMethodEntry; + thread->ClearDebugMethodEntry(); + } + Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc, events, nullptr); + } } - void FieldRead(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, - uint32_t dex_pc, ArtField* field) + void FieldRead(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, ArtField* field) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - UNUSED(thread); Dbg::PostFieldAccessEvent(method, dex_pc, this_object, field); } @@ -296,6 +332,26 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati } private: + static bool IsReturn(mirror::ArtMethod* method, uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile::CodeItem* code_item = method->GetCodeItem(); + const Instruction* instruction = Instruction::At(&code_item->insns_[dex_pc]); + return instruction->IsReturn(); + } + + static bool IsListeningToDexPcMoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return IsListeningTo(instrumentation::Instrumentation::kDexPcMoved); + } + + static bool IsListeningToMethodExit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return IsListeningTo(instrumentation::Instrumentation::kMethodExited); + } + + static bool IsListeningTo(instrumentation::Instrumentation::InstrumentationEvent event) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return (Dbg::GetInstrumentationEvents() & event) != 0; + } + DISALLOW_COPY_AND_ASSIGN(DebugInstrumentationListener); } gDebugInstrumentationListener; diff --git a/runtime/debugger.h b/runtime/debugger.h index fe90eb613e..789a0a4dca 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -714,6 +714,10 @@ class Dbg { static JDWP::JdwpState* GetJdwpState(); + static uint32_t GetInstrumentationEvents() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return instrumentation_events_; + } + private: static JDWP::JdwpError GetLocalValue(const StackVisitor& visitor, ScopedObjectAccessUnchecked& soa, int slot, diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 878efba1a5..dd1f55e6b2 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -156,7 +156,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); uint16_t inst_data; const void* const* currentHandlersTable; - bool notified_method_entry_event = false; UPDATE_HANDLER_TABLE(); if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing. if (kIsDebugBuild) { @@ -166,7 +165,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), 0); - notified_method_entry_event = true; } } @@ -264,9 +262,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } @@ -281,9 +276,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } @@ -299,9 +291,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } @@ -316,9 +305,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } @@ -352,9 +338,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } @@ -2510,26 +2493,16 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF // Note: we do not use the kReturn instruction flag here (to test the instruction is a return). The // compiler seems to not evaluate "(Instruction::FlagsOf(Instruction::code) & kReturn) != 0" to // a constant condition that would remove the "if" statement so the test is free. -#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ - alt_op_##code: { \ - if (Instruction::code != Instruction::RETURN_VOID && \ - Instruction::code != Instruction::RETURN_VOID_NO_BARRIER && \ - Instruction::code != Instruction::RETURN && \ - Instruction::code != Instruction::RETURN_WIDE && \ - Instruction::code != Instruction::RETURN_OBJECT) { \ - if (LIKELY(!notified_method_entry_event)) { \ - Runtime* runtime = Runtime::Current(); \ - const instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); \ - if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ - Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); \ - instrumentation->DexPcMovedEvent(self, this_object, shadow_frame.GetMethod(), dex_pc); \ - } \ - } else { \ - notified_method_entry_event = false; \ - } \ - } \ - UPDATE_HANDLER_TABLE(); \ - goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code]; \ +#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ + alt_op_##code: { \ + Runtime* const runtime = Runtime::Current(); \ + const instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); \ + if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ + Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); \ + instrumentation->DexPcMovedEvent(self, this_object, shadow_frame.GetMethod(), dex_pc); \ + } \ + UPDATE_HANDLER_TABLE(); \ + goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code]; \ } #include "dex_instruction_list.h" DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER) diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index a5e5299b34..0e3420ffb5 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -47,10 +47,7 @@ namespace interpreter { // Code to run before each dex instruction. #define PREAMBLE() \ do { \ - DCHECK(!inst->IsReturn()); \ - if (UNLIKELY(notified_method_entry_event)) { \ - notified_method_entry_event = false; \ - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ + if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \ shadow_frame.GetMethod(), dex_pc); \ } \ @@ -67,7 +64,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, self->VerifyStack(); uint32_t dex_pc = shadow_frame.GetDexPC(); - bool notified_method_entry_event = false; const auto* const instrumentation = Runtime::Current()->GetInstrumentation(); if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing. if (kIsDebugBuild) { @@ -76,7 +72,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), 0); - notified_method_entry_event = true; } } const uint16_t* const insns = code_item->insns_; @@ -171,19 +166,18 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, break; } case Instruction::RETURN_VOID_NO_BARRIER: { + PREAMBLE(); JValue result; self->AllowThreadSuspension(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } case Instruction::RETURN_VOID: { + PREAMBLE(); QuasiAtomic::ThreadFenceForConstructor(); JValue result; self->AllowThreadSuspension(); @@ -191,13 +185,11 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } case Instruction::RETURN: { + PREAMBLE(); JValue result; result.SetJ(0); result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data))); @@ -206,13 +198,11 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } case Instruction::RETURN_WIDE: { + PREAMBLE(); JValue result; result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data))); self->AllowThreadSuspension(); @@ -220,13 +210,11 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } case Instruction::RETURN_OBJECT: { + PREAMBLE(); JValue result; self->AllowThreadSuspension(); const size_t ref_idx = inst->VRegA_11x(inst_data); @@ -254,9 +242,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 1ec800fce6..ab3f2e4ddd 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -141,6 +141,8 @@ static bool NeedsFullDeoptimization(JdwpEventKind eventKind) { } } +// Returns the instrumentation event the DebugInstrumentationListener must +// listen to in order to properly report the given JDWP event to the debugger. static uint32_t GetInstrumentationEventFor(JdwpEventKind eventKind) { switch (eventKind) { case EK_BREAKPOINT: diff --git a/runtime/oat.h b/runtime/oat.h index a31e09a3cf..aaf442a77a 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '6', '1', '\0' }; + static constexpr uint8_t kOatVersion[] = { '0', '6', '2', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 329ceb561d..49e1b8edf6 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -520,23 +520,6 @@ JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnab return result; } -void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset, - JValue* result) { - // We want to make sure that the stack is not within a small distance from the - // protected region in case we are calling into a leaf function whose stack - // check has been elided. - if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { - ThrowStackOverflowError(self); - return; - } - uint32_t shorty_len; - const char* shorty = shadow_frame->GetMethod()->GetShorty(&shorty_len); - ArgArray arg_array(shorty, shorty_len); - arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset); - shadow_frame->GetMethod()->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, - shorty); -} - jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod, jobject javaReceiver, jobject javaArgs, size_t num_frames) { // We want to make sure that the stack is not within a small distance from the diff --git a/runtime/reflection.h b/runtime/reflection.h index 6305d68bd5..37f8a6af55 100644 --- a/runtime/reflection.h +++ b/runtime/reflection.h @@ -61,10 +61,6 @@ JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnab jobject obj, jmethodID mid, va_list args) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset, - JValue* result) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // num_frames is number of frames we look up for access check. jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject method, jobject receiver, jobject args, size_t num_frames = 1) diff --git a/runtime/thread.h b/runtime/thread.h index e766daabed..9346813ec3 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -752,6 +752,18 @@ class Thread { tls32_.ready_for_debug_invoke = ready; } + bool IsDebugMethodEntry() const { + return tls32_.debug_method_entry_; + } + + void SetDebugMethodEntry() { + tls32_.debug_method_entry_ = true; + } + + void ClearDebugMethodEntry() { + tls32_.debug_method_entry_ = false; + } + // Activates single step control for debugging. The thread takes the // ownership of the given SingleStepControl*. It is deleted by a call // to DeactivateSingleStepControl or upon thread destruction. @@ -1028,7 +1040,7 @@ class Thread { suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0), daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0), thread_exit_check_count(0), handling_signal_(false), suspended_at_suspend_check(false), - ready_for_debug_invoke(false) { + ready_for_debug_invoke(false), debug_method_entry_(false) { } union StateAndFlags state_and_flags; @@ -1077,6 +1089,10 @@ class Thread { // used to invoke method from the debugger which is only allowed when // the thread is suspended by an event. bool32_t ready_for_debug_invoke; + + // True if the thread enters a method. This is used to detect method entry + // event for the debugger. + bool32_t debug_method_entry_; } tls32_; struct PACKED(8) tls_64bit_sized_values { diff --git a/runtime/utf.h b/runtime/utf.h index dd38afa172..7f05248c29 100644 --- a/runtime/utf.h +++ b/runtime/utf.h @@ -87,9 +87,9 @@ size_t ComputeModifiedUtf8Hash(const char* chars); /* * Retrieve the next UTF-16 character or surrogate pair from a UTF-8 string. * single byte, 2-byte and 3-byte UTF-8 sequences result in a single UTF-16 - * character whereas 4-byte UTF-8 sequences result in a surrogate pair. Use - * GetLeadingUtf16Char and GetTrailingUtf16Char to process the return value - * of this function. + * character (possibly one half of a surrogate) whereas 4-byte UTF-8 sequences + * result in a surrogate pair. Use GetLeadingUtf16Char and GetTrailingUtf16Char + * to process the return value of this function. * * Advances "*utf8_data_in" to the start of the next character. * diff --git a/runtime/utils.cc b/runtime/utils.cc index 650214f67b..7986cdcbf9 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -827,14 +827,21 @@ bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) { */ const uint32_t pair = GetUtf16FromUtf8(pUtf8Ptr); - const uint16_t leading = GetLeadingUtf16Char(pair); - const uint32_t trailing = GetTrailingUtf16Char(pair); - if (trailing == 0) { - // Perform follow-up tests based on the high 8 bits of the - // lower surrogate. - switch (leading >> 8) { + // We have a surrogate pair resulting from a valid 4 byte UTF sequence. + // No further checks are necessary because 4 byte sequences span code + // points [U+10000, U+1FFFFF], which are valid codepoints in a dex + // identifier. Furthermore, GetUtf16FromUtf8 guarantees that each of + // the surrogate halves are valid and well formed in this instance. + if (GetTrailingUtf16Char(pair) != 0) { + return true; + } + + + // We've encountered a one, two or three byte UTF-8 sequence. The + // three byte UTF-8 sequence could be one half of a surrogate pair. + switch (leading >> 8) { case 0x00: // It's only valid if it's above the ISO-8859-1 high space (0xa0). return (leading > 0x00a0); @@ -842,9 +849,14 @@ bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) { case 0xd9: case 0xda: case 0xdb: - // It looks like a leading surrogate but we didn't find a trailing - // surrogate if we're here. - return false; + { + // We found a three byte sequence encoding one half of a surrogate. + // Look for the other half. + const uint32_t pair2 = GetUtf16FromUtf8(pUtf8Ptr); + const uint16_t trailing = GetLeadingUtf16Char(pair2); + + return (GetTrailingUtf16Char(pair2) == 0) && (0xdc00 <= trailing && trailing <= 0xdfff); + } case 0xdc: case 0xdd: case 0xde: @@ -855,21 +867,19 @@ bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) { case 0xff: // It's in the range that has spaces, controls, and specials. switch (leading & 0xfff8) { - case 0x2000: - case 0x2008: - case 0x2028: - case 0xfff0: - case 0xfff8: - return false; + case 0x2000: + case 0x2008: + case 0x2028: + case 0xfff0: + case 0xfff8: + return false; } - break; - } - - return true; + return true; + default: + return true; } - // We have a surrogate pair. Check that trailing surrogate is well formed. - return (trailing >= 0xdc00 && trailing <= 0xdfff); + UNREACHABLE(); } /* Return whether the pointed-at modified-UTF-8 encoded character is diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index 195de0c121..869d305120 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -521,4 +521,27 @@ TEST_F(UtilsTest, TestSleep) { EXPECT_GT(NanoTime() - start, MsToNs(1000)); } +TEST_F(UtilsTest, IsValidDescriptor) { + std::vector<uint8_t> descriptor( + { 'L', 'a', '/', 'b', '$', 0xed, 0xa0, 0x80, 0xed, 0xb0, 0x80, ';', 0x00 }); + EXPECT_TRUE(IsValidDescriptor(reinterpret_cast<char*>(&descriptor[0]))); + + std::vector<uint8_t> unpaired_surrogate( + { 'L', 'a', '/', 'b', '$', 0xed, 0xa0, 0x80, ';', 0x00 }); + EXPECT_FALSE(IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate[0]))); + + std::vector<uint8_t> unpaired_surrogate_at_end( + { 'L', 'a', '/', 'b', '$', 0xed, 0xa0, 0x80, 0x00 }); + EXPECT_FALSE(IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate_at_end[0]))); + + std::vector<uint8_t> invalid_surrogate( + { 'L', 'a', '/', 'b', '$', 0xed, 0xb0, 0x80, ';', 0x00 }); + EXPECT_FALSE(IsValidDescriptor(reinterpret_cast<char*>(&invalid_surrogate[0]))); + + std::vector<uint8_t> unpaired_surrogate_with_multibyte_sequence( + { 'L', 'a', '/', 'b', '$', 0xed, 0xb0, 0x80, 0xf0, 0x9f, 0x8f, 0xa0, ';', 0x00 }); + EXPECT_FALSE( + IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate_with_multibyte_sequence[0]))); +} + } // namespace art |
