summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/instrumentation.cc2
-rw-r--r--runtime/instrumentation.h27
-rw-r--r--runtime/interpreter/interpreter_goto_table_impl.cc162
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)