diff options
-rw-r--r-- | runtime/instrumentation.cc | 2 | ||||
-rw-r--r-- | runtime/instrumentation.h | 27 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_goto_table_impl.cc | 162 |
3 files changed, 136 insertions, 55 deletions
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 481cbad..8316bc5 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -293,6 +293,7 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev have_exception_caught_listeners_ = true; } ConfigureStubs(require_entry_exit_stubs, require_interpreter); + UpdateInterpreterHandlerTable(); } void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) { @@ -341,6 +342,7 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t have_exception_caught_listeners_ = exception_caught_listeners_.size() > 0; } ConfigureStubs(require_entry_exit_stubs, require_interpreter); + UpdateInterpreterHandlerTable(); } void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) { diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 28f9555..7a0aaf7 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -38,6 +38,14 @@ namespace instrumentation { const bool kVerboseInstrumentation = false; +// Interpreter handler tables. +enum InterpreterHandlerTable { + kMainHandlerTable = 0, // Main handler table: no suspend check, no instrumentation. + kAlternativeHandlerTable = 1, // Alternative handler table: suspend check and/or instrumentation + // enabled. + kNumHandlerTables +}; + // Instrumentation event listener API. Registered listeners will get the appropriate call back for // the events they are listening for. The call backs supply the thread, method and dex_pc the event // occurred upon. The thread may or may not be Thread::Current(). @@ -95,7 +103,8 @@ class Instrumentation { interpret_only_(false), forced_interpret_only_(false), have_method_entry_listeners_(false), have_method_exit_listeners_(false), have_method_unwind_listeners_(false), have_dex_pc_listeners_(false), - have_exception_caught_listeners_(false) {} + have_exception_caught_listeners_(false), + interpreter_handler_table_(kMainHandlerTable) {} // Add a listener to be notified of the masked together sent of instrumentation events. This // suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy @@ -110,6 +119,10 @@ class Instrumentation { EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + InterpreterHandlerTable GetInterpreterHandlerTable() const { + return interpreter_handler_table_; + } + // Update the code of a method respecting any installed stubs. void UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const; @@ -149,6 +162,11 @@ class Instrumentation { return have_dex_pc_listeners_; } + bool IsActive() const { + return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ || + have_exception_caught_listeners_ || have_method_unwind_listeners_; + } + // Inform listeners that a method has been entered. A dex PC is provided as we may install // listeners into executing code and get method enter events for methods already on the stack. void MethodEnterEvent(Thread* thread, mirror::Object* this_object, @@ -215,6 +233,10 @@ class Instrumentation { EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + void UpdateInterpreterHandlerTable() { + interpreter_handler_table_ = IsActive() ? kAlternativeHandlerTable : kMainHandlerTable; + } + void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object, const mirror::ArtMethod* method, uint32_t dex_pc) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -267,6 +289,9 @@ class Instrumentation { std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list<InstrumentationListener*> exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_); + // Current interpreter handler table. This is updated each time the thread state flags are modified. + InterpreterHandlerTable interpreter_handler_table_; + DISALLOW_COPY_AND_ASSIGN(Instrumentation); }; diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index daf6f93..608cd41 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -51,14 +51,8 @@ namespace interpreter { } \ } while (false) -#define UPDATE_HANDLER_TABLE() \ - do { \ - if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ - currentHandlersTable = instrumentationHandlersTable; \ - } else { \ - currentHandlersTable = handlersTable; \ - } \ - } while (false); +#define UPDATE_HANDLER_TABLE() \ + currentHandlersTable = handlersTable[Runtime::Current()->GetInstrumentation()->GetInterpreterHandlerTable()] #define UNREACHABLE_CODE_CHECK() \ do { \ @@ -70,10 +64,77 @@ namespace interpreter { #define HANDLE_INSTRUCTION_START(opcode) op_##opcode: // NOLINT(whitespace/labels) #define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK() +/** + * Interpreter based on computed goto tables. + * + * Each instruction is associated to a handler. This handler is responsible for executing the + * instruction and jump to the next instruction's handler. + * In order to limit the cost of instrumentation, we have two handler tables: + * - the "main" handler table: it contains handlers for normal execution of each instruction without + * handling of instrumentation. + * - the "alternative" handler table: it contains alternative handlers which first handle + * instrumentation before jumping to the corresponding "normal" instruction's handler. + * + * When instrumentation is active, the interpreter uses the "alternative" handler table. Otherwise + * it uses the "main" handler table. + * + * The current handler table is the handler table being used by the interpreter. It is updated: + * - on backward branch (goto, if and switch instructions) + * - after invoke + * - when an exception is thrown. + * This allows to support an attaching debugger to an already running application for instance. + * + * For a fast handler table update, handler tables are stored in an array of handler tables. Each + * handler table is represented by the InterpreterHandlerTable enum which allows to associate it + * to an index in this array of handler tables ((see Instrumentation::GetInterpreterHandlerTable). + * + * Here's the current layout of this array of handler tables: + * + * ---------------------+---------------+ + * | NOP | (handler for NOP instruction) + * +---------------+ + * "main" | MOVE | (handler for MOVE instruction) + * handler table +---------------+ + * | ... | + * +---------------+ + * | UNUSED_FF | (handler for UNUSED_FF instruction) + * ---------------------+---------------+ + * | NOP | (alternative handler for NOP instruction) + * +---------------+ + * "alternative" | MOVE | (alternative handler for MOVE instruction) + * handler table +---------------+ + * | ... | + * +---------------+ + * | UNUSED_FF | (alternative handler for UNUSED_FF instruction) + * ---------------------+---------------+ + * + */ template<bool do_access_check> JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) { - bool do_assignability_check = do_access_check; + // Define handler tables: + // - The main handler table contains execution handlers for each instruction. + // - The alternative handler table contains prelude handlers which check for thread suspend and + // manage instrumentation before jumping to the execution handler. + static const void* const handlersTable[instrumentation::kNumHandlerTables][kNumPackedOpcodes] = { + { + // Main handler table. +#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code, +#include "dex_instruction_list.h" + DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER) +#undef DEX_INSTRUCTION_LIST +#undef INSTRUCTION_HANDLER + }, { + // Alternative handler table. +#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&alt_op_##code, +#include "dex_instruction_list.h" + DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER) +#undef DEX_INSTRUCTION_LIST +#undef INSTRUCTION_HANDLER + } + }; + + const bool do_assignability_check = do_access_check; if (UNLIKELY(!shadow_frame.HasReferenceArray())) { LOG(FATAL) << "Invalid shadow frame for interpreter use"; return JValue(); @@ -81,35 +142,17 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* self->VerifyStack(); uint32_t dex_pc = shadow_frame.GetDexPC(); - const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); + const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); + uint16_t inst_data; + const void* const* currentHandlersTable; + UPDATE_HANDLER_TABLE(); if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), 0); } } - const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); - uint16_t inst_data; - - // Define handlers table. - static const void* handlersTable[kNumPackedOpcodes] = { -#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code, -#include "dex_instruction_list.h" - DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER) -#undef DEX_INSTRUCTION_LIST -#undef INSTRUCTION_HANDLER - }; - - static const void* instrumentationHandlersTable[kNumPackedOpcodes] = { -#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&instrumentation_op_##code, -#include "dex_instruction_list.h" - DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER) -#undef DEX_INSTRUCTION_LIST -#undef INSTRUCTION_HANDLER - }; - - const void** currentHandlersTable; - UPDATE_HANDLER_TABLE(); // Jump to first instruction. ADVANCE(0); @@ -207,6 +250,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -222,6 +266,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -238,6 +283,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -253,6 +299,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -286,6 +333,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_PENDING_EXCEPTION(); } } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -547,8 +595,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -559,8 +607,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -571,8 +619,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -583,8 +631,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -595,8 +643,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -688,8 +736,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -704,8 +752,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -720,8 +768,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -736,8 +784,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -752,8 +800,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -768,8 +816,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -784,8 +832,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -800,8 +848,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -816,8 +864,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -832,8 +880,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -848,8 +896,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -864,8 +912,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -2306,8 +2354,10 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* CHECK(self->IsExceptionPending()); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc, this_object, instrumentation); @@ -2320,11 +2370,15 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } // Create alternative instruction handlers dedicated to instrumentation. -#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ - instrumentation_op_##code: { \ - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \ - shadow_frame.GetMethod(), dex_pc); \ - goto *handlersTable[Instruction::code]; \ +#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ + alt_op_##code: { \ + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); \ + if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ + instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \ + 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) |