OSDN Git Service

Support multiple instrumentation clients
authorSebastien Hertz <shertz@google.com>
Wed, 1 Apr 2015 14:34:17 +0000 (16:34 +0200)
committerSebastien Hertz <shertz@google.com>
Thu, 7 May 2015 21:11:36 +0000 (23:11 +0200)
Changes Instrumentation::ConfigureStubs to support multiple clients
that need different levels of instrumenation. A client is identified
by a string key used to save the desired instrumentation level.

Also adds regression gtest instrumentation_test and some cleanup.

Bug: 19829329

(cherry picked from commit 0462c4c87c39db6cfcd338f323844738109ac3c9)

Change-Id: I1fc24a86fcb7cb46d4be806895376c25cc0a0b3c

build/Android.gtest.mk
runtime/debugger.cc
runtime/instrumentation.cc
runtime/instrumentation.h
runtime/instrumentation_test.cc [new file with mode: 0644]
runtime/jit/jit.h
runtime/trace.cc
test/Instrumentation/Instrumentation.java [new file with mode: 0644]

index 5a3236d..730e61d 100644 (file)
@@ -26,6 +26,7 @@ GTEST_DEX_DIRECTORIES := \
   AllFields \
   ExceptionHandle \
   GetMethodSignature \
+  Instrumentation \
   Interfaces \
   Main \
   MultiDex \
@@ -64,6 +65,7 @@ ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Stati
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods
 ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
+ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
 ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
 ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
 ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex Nested
@@ -157,6 +159,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \
   runtime/handle_scope_test.cc \
   runtime/indenter_test.cc \
   runtime/indirect_reference_table_test.cc \
+  runtime/instrumentation_test.cc \
   runtime/intern_table_test.cc \
   runtime/interpreter/safe_math_test.cc \
   runtime/java_vm_ext_test.cc \
index 47371e5..9b33e50 100644 (file)
@@ -57,6 +57,9 @@
 
 namespace art {
 
+// The key identifying the debugger to update instrumentation.
+static constexpr const char* kDbgInstrumentationKey = "Debugger";
+
 static const size_t kMaxAllocRecordStackDepth = 16;  // Max 255.
 static const size_t kDefaultNumAllocRecords = 64*1024;  // Must be a power of 2. 2BE can hold 64k-1.
 
@@ -733,7 +736,7 @@ void Dbg::Disconnected() {
       instrumentation_events_ = 0;
     }
     if (RequiresDeoptimization()) {
-      runtime->GetInstrumentation()->DisableDeoptimization();
+      runtime->GetInstrumentation()->DisableDeoptimization(kDbgInstrumentationKey);
     }
     gDebuggerActive = false;
   }
@@ -3054,12 +3057,12 @@ void Dbg::ProcessDeoptimizationRequest(const DeoptimizationRequest& request) {
       break;
     case DeoptimizationRequest::kFullDeoptimization:
       VLOG(jdwp) << "Deoptimize the world ...";
-      instrumentation->DeoptimizeEverything();
+      instrumentation->DeoptimizeEverything(kDbgInstrumentationKey);
       VLOG(jdwp) << "Deoptimize the world DONE";
       break;
     case DeoptimizationRequest::kFullUndeoptimization:
       VLOG(jdwp) << "Undeoptimize the world ...";
-      instrumentation->UndeoptimizeEverything();
+      instrumentation->UndeoptimizeEverything(kDbgInstrumentationKey);
       VLOG(jdwp) << "Undeoptimize the world DONE";
       break;
     case DeoptimizationRequest::kSelectiveDeoptimization:
index e6c333d..f810bc8 100644 (file)
 
 #include "instrumentation.h"
 
-#include <sys/uio.h>
-
 #include <sstream>
 
 #include "arch/context.h"
 #include "atomic.h"
-#include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
 #include "nth_caller_visitor.h"
-#include "os.h"
-#include "scoped_thread_state_change.h"
 #include "thread.h"
 #include "thread_list.h"
 
 namespace art {
-
 namespace instrumentation {
 
-const bool kVerboseInstrumentation = false;
+constexpr bool kVerboseInstrumentation = false;
 
 static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg)
     EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -64,7 +58,7 @@ Instrumentation::Instrumentation()
       have_method_entry_listeners_(false), have_method_exit_listeners_(false),
       have_method_unwind_listeners_(false), have_dex_pc_listeners_(false),
       have_field_read_listeners_(false), have_field_write_listeners_(false),
-      have_exception_caught_listeners_(false),
+      have_exception_caught_listeners_(false), have_backward_branch_listeners_(false),
       deoptimized_methods_lock_("deoptimized methods lock"),
       deoptimization_enabled_(false),
       interpreter_handler_table_(kMainHandlerTable),
@@ -166,7 +160,7 @@ void Instrumentation::InstallStubsForMethod(mirror::ArtMethod* method) {
 // existing instrumentation frames.
 static void InstrumentationInstallStack(Thread* thread, void* arg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  struct InstallStackVisitor : public StackVisitor {
+  struct InstallStackVisitor FINAL : public StackVisitor {
     InstallStackVisitor(Thread* thread_in, Context* context, uintptr_t instrumentation_exit_pc)
         : StackVisitor(thread_in, context),
           instrumentation_stack_(thread_in->GetInstrumentationStack()),
@@ -175,7 +169,7 @@ static void InstrumentationInstallStack(Thread* thread, void* arg)
           last_return_pc_(0) {
     }
 
-    virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
       mirror::ArtMethod* m = GetMethod();
       if (m == nullptr) {
         if (kVerboseInstrumentation) {
@@ -306,7 +300,7 @@ static void InstrumentationInstallStack(Thread* thread, void* arg)
 // Removes the instrumentation exit pc as the return PC for every quick frame.
 static void InstrumentationRestoreStack(Thread* thread, void* arg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  struct RestoreStackVisitor : public StackVisitor {
+  struct RestoreStackVisitor FINAL : public StackVisitor {
     RestoreStackVisitor(Thread* thread_in, uintptr_t instrumentation_exit_pc,
                         Instrumentation* instrumentation)
         : StackVisitor(thread_in, nullptr), thread_(thread_in),
@@ -315,7 +309,7 @@ static void InstrumentationRestoreStack(Thread* thread, void* arg)
           instrumentation_stack_(thread_in->GetInstrumentationStack()),
           frames_removed_(0) {}
 
-    virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
       if (instrumentation_stack_->size() == 0) {
         return false;  // Stop.
       }
@@ -390,25 +384,29 @@ static void InstrumentationRestoreStack(Thread* thread, void* arg)
   }
 }
 
+static bool HasEvent(Instrumentation::InstrumentationEvent expected, uint32_t events) {
+  return (events & expected) != 0;
+}
+
 void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t events) {
   Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
-  if ((events & kMethodEntered) != 0) {
+  if (HasEvent(kMethodEntered, events)) {
     method_entry_listeners_.push_back(listener);
     have_method_entry_listeners_ = true;
   }
-  if ((events & kMethodExited) != 0) {
+  if (HasEvent(kMethodExited, events)) {
     method_exit_listeners_.push_back(listener);
     have_method_exit_listeners_ = true;
   }
-  if ((events & kMethodUnwind) != 0) {
+  if (HasEvent(kMethodUnwind, events)) {
     method_unwind_listeners_.push_back(listener);
     have_method_unwind_listeners_ = true;
   }
-  if ((events & kBackwardBranch) != 0) {
+  if (HasEvent(kBackwardBranch, events)) {
     backward_branch_listeners_.push_back(listener);
     have_backward_branch_listeners_ = true;
   }
-  if ((events & kDexPcMoved) != 0) {
+  if (HasEvent(kDexPcMoved, events)) {
     std::list<InstrumentationListener*>* modified;
     if (have_dex_pc_listeners_) {
       modified = new std::list<InstrumentationListener*>(*dex_pc_listeners_.get());
@@ -419,7 +417,7 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev
     dex_pc_listeners_.reset(modified);
     have_dex_pc_listeners_ = true;
   }
-  if ((events & kFieldRead) != 0) {
+  if (HasEvent(kFieldRead, events)) {
     std::list<InstrumentationListener*>* modified;
     if (have_field_read_listeners_) {
       modified = new std::list<InstrumentationListener*>(*field_read_listeners_.get());
@@ -430,7 +428,7 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev
     field_read_listeners_.reset(modified);
     have_field_read_listeners_ = true;
   }
-  if ((events & kFieldWritten) != 0) {
+  if (HasEvent(kFieldWritten, events)) {
     std::list<InstrumentationListener*>* modified;
     if (have_field_write_listeners_) {
       modified = new std::list<InstrumentationListener*>(*field_write_listeners_.get());
@@ -441,7 +439,7 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev
     field_write_listeners_.reset(modified);
     have_field_write_listeners_ = true;
   }
-  if ((events & kExceptionCaught) != 0) {
+  if (HasEvent(kExceptionCaught, events)) {
     std::list<InstrumentationListener*>* modified;
     if (have_exception_caught_listeners_) {
       modified = new std::list<InstrumentationListener*>(*exception_caught_listeners_.get());
@@ -458,102 +456,104 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev
 void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) {
   Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
 
-  if ((events & kMethodEntered) != 0) {
-    if (have_method_entry_listeners_) {
-      method_entry_listeners_.remove(listener);
-      have_method_entry_listeners_ = !method_entry_listeners_.empty();
-    }
+  if (HasEvent(kMethodEntered, events) && have_method_entry_listeners_) {
+    method_entry_listeners_.remove(listener);
+    have_method_entry_listeners_ = !method_entry_listeners_.empty();
   }
-  if ((events & kMethodExited) != 0) {
-    if (have_method_exit_listeners_) {
-      method_exit_listeners_.remove(listener);
-      have_method_exit_listeners_ = !method_exit_listeners_.empty();
-    }
+  if (HasEvent(kMethodExited, events) && have_method_exit_listeners_) {
+    method_exit_listeners_.remove(listener);
+    have_method_exit_listeners_ = !method_exit_listeners_.empty();
   }
-  if ((events & kMethodUnwind) != 0) {
-    if (have_method_unwind_listeners_) {
+  if (HasEvent(kMethodUnwind, events) && have_method_unwind_listeners_) {
       method_unwind_listeners_.remove(listener);
       have_method_unwind_listeners_ = !method_unwind_listeners_.empty();
-    }
   }
-  if ((events & kDexPcMoved) != 0) {
+  if (HasEvent(kBackwardBranch, events) && have_backward_branch_listeners_) {
+      backward_branch_listeners_.remove(listener);
+      have_backward_branch_listeners_ = !backward_branch_listeners_.empty();
+    }
+  if (HasEvent(kDexPcMoved, events) && have_dex_pc_listeners_) {
+    std::list<InstrumentationListener*>* modified =
+        new std::list<InstrumentationListener*>(*dex_pc_listeners_.get());
+    modified->remove(listener);
+    have_dex_pc_listeners_ = !modified->empty();
     if (have_dex_pc_listeners_) {
-      std::list<InstrumentationListener*>* modified =
-          new std::list<InstrumentationListener*>(*dex_pc_listeners_.get());
-      modified->remove(listener);
-      have_dex_pc_listeners_ = !modified->empty();
-      if (have_dex_pc_listeners_) {
-        dex_pc_listeners_.reset(modified);
-      } else {
-        dex_pc_listeners_.reset();
-        delete modified;
-      }
+      dex_pc_listeners_.reset(modified);
+    } else {
+      dex_pc_listeners_.reset();
+      delete modified;
     }
   }
-  if ((events & kFieldRead) != 0) {
+  if (HasEvent(kFieldRead, events) && have_field_read_listeners_) {
+    std::list<InstrumentationListener*>* modified =
+        new std::list<InstrumentationListener*>(*field_read_listeners_.get());
+    modified->remove(listener);
+    have_field_read_listeners_ = !modified->empty();
     if (have_field_read_listeners_) {
-      std::list<InstrumentationListener*>* modified =
-          new std::list<InstrumentationListener*>(*field_read_listeners_.get());
-      modified->remove(listener);
-      have_field_read_listeners_ = !modified->empty();
-      if (have_field_read_listeners_) {
-        field_read_listeners_.reset(modified);
-      } else {
-        field_read_listeners_.reset();
-        delete modified;
-      }
+      field_read_listeners_.reset(modified);
+    } else {
+      field_read_listeners_.reset();
+      delete modified;
     }
   }
-  if ((events & kFieldWritten) != 0) {
+  if (HasEvent(kFieldWritten, events) && have_field_write_listeners_) {
+    std::list<InstrumentationListener*>* modified =
+        new std::list<InstrumentationListener*>(*field_write_listeners_.get());
+    modified->remove(listener);
+    have_field_write_listeners_ = !modified->empty();
     if (have_field_write_listeners_) {
-      std::list<InstrumentationListener*>* modified =
-          new std::list<InstrumentationListener*>(*field_write_listeners_.get());
-      modified->remove(listener);
-      have_field_write_listeners_ = !modified->empty();
-      if (have_field_write_listeners_) {
-        field_write_listeners_.reset(modified);
-      } else {
-        field_write_listeners_.reset();
-        delete modified;
-      }
+      field_write_listeners_.reset(modified);
+    } else {
+      field_write_listeners_.reset();
+      delete modified;
     }
   }
-  if ((events & kExceptionCaught) != 0) {
+  if (HasEvent(kExceptionCaught, events) && have_exception_caught_listeners_) {
+    std::list<InstrumentationListener*>* modified =
+        new std::list<InstrumentationListener*>(*exception_caught_listeners_.get());
+    modified->remove(listener);
+    have_exception_caught_listeners_ = !modified->empty();
     if (have_exception_caught_listeners_) {
-      std::list<InstrumentationListener*>* modified =
-          new std::list<InstrumentationListener*>(*exception_caught_listeners_.get());
-      modified->remove(listener);
-      have_exception_caught_listeners_ = !modified->empty();
-      if (have_exception_caught_listeners_) {
-        exception_caught_listeners_.reset(modified);
-      } else {
-        exception_caught_listeners_.reset();
-        delete modified;
-      }
+      exception_caught_listeners_.reset(modified);
+    } else {
+      exception_caught_listeners_.reset();
+      delete modified;
     }
   }
   UpdateInterpreterHandlerTable();
 }
 
-void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) {
-  interpret_only_ = require_interpreter || forced_interpret_only_;
-  // Compute what level of instrumentation is required and compare to current.
-  int desired_level, current_level;
-  if (require_interpreter) {
-    desired_level = 2;
-  } else if (require_entry_exit_stubs) {
-    desired_level = 1;
-  } else {
-    desired_level = 0;
-  }
+Instrumentation::InstrumentationLevel Instrumentation::GetCurrentInstrumentationLevel() const {
   if (interpreter_stubs_installed_) {
-    current_level = 2;
+    return InstrumentationLevel::kInstrumentWithInterpreter;
   } else if (entry_exit_stubs_installed_) {
-    current_level = 1;
+    return InstrumentationLevel::kInstrumentWithInstrumentationStubs;
+  } else {
+    return InstrumentationLevel::kInstrumentNothing;
+  }
+}
+
+void Instrumentation::ConfigureStubs(const char* key, InstrumentationLevel desired_level) {
+  // Store the instrumentation level for this key or remove it.
+  if (desired_level == InstrumentationLevel::kInstrumentNothing) {
+    // The client no longer needs instrumentation.
+    requested_instrumentation_levels_.erase(key);
   } else {
-    current_level = 0;
+    // The client needs instrumentation.
+    requested_instrumentation_levels_.Overwrite(key, desired_level);
   }
-  if (desired_level == current_level) {
+
+  // Look for the highest required instrumentation level.
+  InstrumentationLevel requested_level = InstrumentationLevel::kInstrumentNothing;
+  for (const auto& v : requested_instrumentation_levels_) {
+    requested_level = std::max(requested_level, v.second);
+  }
+
+  interpret_only_ = (requested_level == InstrumentationLevel::kInstrumentWithInterpreter) ||
+                    forced_interpret_only_;
+
+  InstrumentationLevel current_level = GetCurrentInstrumentationLevel();
+  if (requested_level == current_level) {
     // We're already set.
     return;
   }
@@ -561,12 +561,14 @@ void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require
   Runtime* runtime = Runtime::Current();
   Locks::mutator_lock_->AssertExclusiveHeld(self);
   Locks::thread_list_lock_->AssertNotHeld(self);
-  if (desired_level > 0) {
-    if (require_interpreter) {
+  if (requested_level > InstrumentationLevel::kInstrumentNothing) {
+    if (requested_level == InstrumentationLevel::kInstrumentWithInterpreter) {
       interpreter_stubs_installed_ = true;
+      entry_exit_stubs_installed_ = true;
     } else {
-      CHECK(require_entry_exit_stubs);
+      CHECK_EQ(requested_level, InstrumentationLevel::kInstrumentWithInstrumentationStubs);
       entry_exit_stubs_installed_ = true;
+      interpreter_stubs_installed_ = false;
     }
     runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this);
     instrumentation_stubs_installed_ = true;
@@ -590,8 +592,7 @@ void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require
   }
 }
 
-static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg) {
-  UNUSED(arg);
+static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg ATTRIBUTE_UNUSED) {
   thread->ResetQuickAllocEntryPointsForThread();
 }
 
@@ -804,11 +805,11 @@ void Instrumentation::EnableDeoptimization() {
   deoptimization_enabled_ = true;
 }
 
-void Instrumentation::DisableDeoptimization() {
+void Instrumentation::DisableDeoptimization(const char* key) {
   CHECK_EQ(deoptimization_enabled_, true);
   // If we deoptimized everything, undo it.
   if (interpreter_stubs_installed_) {
-    UndeoptimizeEverything();
+    UndeoptimizeEverything(key);
   }
   // Undeoptimized selected methods.
   while (true) {
@@ -828,25 +829,35 @@ void Instrumentation::DisableDeoptimization() {
 
 // Indicates if instrumentation should notify method enter/exit events to the listeners.
 bool Instrumentation::ShouldNotifyMethodEnterExitEvents() const {
+  if (!HasMethodEntryListeners() && !HasMethodExitListeners()) {
+    return false;
+  }
   return !deoptimization_enabled_ && !interpreter_stubs_installed_;
 }
 
-void Instrumentation::DeoptimizeEverything() {
-  CHECK(!interpreter_stubs_installed_);
-  ConfigureStubs(false, true);
+void Instrumentation::DeoptimizeEverything(const char* key) {
+  CHECK(deoptimization_enabled_);
+  ConfigureStubs(key, InstrumentationLevel::kInstrumentWithInterpreter);
 }
 
-void Instrumentation::UndeoptimizeEverything() {
+void Instrumentation::UndeoptimizeEverything(const char* key) {
   CHECK(interpreter_stubs_installed_);
-  ConfigureStubs(false, false);
+  CHECK(deoptimization_enabled_);
+  ConfigureStubs(key, InstrumentationLevel::kInstrumentNothing);
 }
 
-void Instrumentation::EnableMethodTracing(bool require_interpreter) {
-  ConfigureStubs(!require_interpreter, require_interpreter);
+void Instrumentation::EnableMethodTracing(const char* key, bool needs_interpreter) {
+  InstrumentationLevel level;
+  if (needs_interpreter) {
+    level = InstrumentationLevel::kInstrumentWithInterpreter;
+  } else {
+    level = InstrumentationLevel::kInstrumentWithInstrumentationStubs;
+  }
+  ConfigureStubs(key, level);
 }
 
-void Instrumentation::DisableMethodTracing() {
-  ConfigureStubs(false, false);
+void Instrumentation::DisableMethodTracing(const char* key) {
+  ConfigureStubs(key, InstrumentationLevel::kInstrumentNothing);
 }
 
 const void* Instrumentation::GetQuickCodeFor(mirror::ArtMethod* method, size_t pointer_size) const {
@@ -896,7 +907,7 @@ void Instrumentation::MethodExitEventImpl(Thread* thread, mirror::Object* this_o
 void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_object,
                                         mirror::ArtMethod* method,
                                         uint32_t dex_pc) const {
-  if (have_method_unwind_listeners_) {
+  if (HasMethodUnwindListeners()) {
     for (InstrumentationListener* listener : method_unwind_listeners_) {
       listener->MethodUnwind(thread, this_object, method, dex_pc);
     }
@@ -906,11 +917,9 @@ void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_obj
 void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,
                                           mirror::ArtMethod* method,
                                           uint32_t dex_pc) const {
-  if (HasDexPcListeners()) {
-    std::shared_ptr<std::list<InstrumentationListener*>> original(dex_pc_listeners_);
-    for (InstrumentationListener* listener : *original.get()) {
-      listener->DexPcMoved(thread, this_object, method, dex_pc);
-    }
+  std::shared_ptr<std::list<InstrumentationListener*>> original(dex_pc_listeners_);
+  for (InstrumentationListener* listener : *original.get()) {
+    listener->DexPcMoved(thread, this_object, method, dex_pc);
   }
 }
 
@@ -924,22 +933,18 @@ void Instrumentation::BackwardBranchImpl(Thread* thread, mirror::ArtMethod* meth
 void Instrumentation::FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
                                          mirror::ArtMethod* method, uint32_t dex_pc,
                                          ArtField* field) const {
-  if (HasFieldReadListeners()) {
-    std::shared_ptr<std::list<InstrumentationListener*>> original(field_read_listeners_);
-    for (InstrumentationListener* listener : *original.get()) {
-      listener->FieldRead(thread, this_object, method, dex_pc, field);
-    }
+  std::shared_ptr<std::list<InstrumentationListener*>> original(field_read_listeners_);
+  for (InstrumentationListener* listener : *original.get()) {
+    listener->FieldRead(thread, this_object, method, dex_pc, field);
   }
 }
 
 void Instrumentation::FieldWriteEventImpl(Thread* thread, mirror::Object* this_object,
                                          mirror::ArtMethod* method, uint32_t dex_pc,
                                          ArtField* field, const JValue& field_value) const {
-  if (HasFieldWriteListeners()) {
-    std::shared_ptr<std::list<InstrumentationListener*>> original(field_write_listeners_);
-    for (InstrumentationListener* listener : *original.get()) {
-      listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value);
-    }
+  std::shared_ptr<std::list<InstrumentationListener*>> original(field_write_listeners_);
+  for (InstrumentationListener* listener : *original.get()) {
+    listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value);
   }
 }
 
index 8b7fcca..7d70d21 100644 (file)
 #include <map>
 
 #include "arch/instruction_set.h"
-#include "atomic.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "gc_root.h"
-#include "object_callbacks.h"
+#include "safe_map.h"
 
 namespace art {
 namespace mirror {
@@ -67,8 +66,6 @@ struct InstrumentationListener {
                              uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
 
   // Call-back for when a method is exited.
-  // TODO: its likely passing the return value would be useful, however, we may need to get and
-  //       parse the shorty to determine what kind of register holds the result.
   virtual void MethodExited(Thread* thread, mirror::Object* this_object,
                             mirror::ArtMethod* method, uint32_t dex_pc,
                             const JValue& return_value)
@@ -119,6 +116,12 @@ class Instrumentation {
     kBackwardBranch = 0x80,
   };
 
+  enum class InstrumentationLevel {
+    kInstrumentNothing,                   // execute without instrumentation
+    kInstrumentWithInstrumentationStubs,  // execute with instrumentation entry/exit stubs
+    kInstrumentWithInterpreter            // execute with interpreter
+  };
+
   Instrumentation();
 
   // Add a listener to be notified of the masked together sent of instrumentation events. This
@@ -138,7 +141,7 @@ class Instrumentation {
   void EnableDeoptimization()
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(deoptimized_methods_lock_);
-  void DisableDeoptimization()
+  void DisableDeoptimization(const char* key)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(deoptimized_methods_lock_);
   bool AreAllMethodsDeoptimized() const {
@@ -147,12 +150,12 @@ class Instrumentation {
   bool ShouldNotifyMethodEnterExitEvents() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Executes everything with interpreter.
-  void DeoptimizeEverything()
+  void DeoptimizeEverything(const char* key)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
 
   // Executes everything with compiled code (or interpreter if there is no code).
-  void UndeoptimizeEverything()
+  void UndeoptimizeEverything(const char* key)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
 
@@ -170,18 +173,19 @@ class Instrumentation {
       LOCKS_EXCLUDED(Locks::thread_list_lock_, deoptimized_methods_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Indicates whether the method has been deoptimized so it is executed with the interpreter.
   bool IsDeoptimized(mirror::ArtMethod* method)
       LOCKS_EXCLUDED(deoptimized_methods_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Enable method tracing by installing instrumentation entry/exit stubs.
-  void EnableMethodTracing(
-      bool require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners)
+  // Enable method tracing by installing instrumentation entry/exit stubs or interpreter.
+  void EnableMethodTracing(const char* key,
+                           bool needs_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
 
-  // Disable method tracing by uninstalling instrumentation entry/exit stubs.
-  void DisableMethodTracing()
+  // Disable method tracing by uninstalling instrumentation entry/exit stubs or interpreter.
+  void DisableMethodTracing(const char* key)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
 
@@ -236,6 +240,10 @@ class Instrumentation {
     return have_method_exit_listeners_;
   }
 
+  bool HasMethodUnwindListeners() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return have_method_unwind_listeners_;
+  }
+
   bool HasDexPcListeners() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return have_dex_pc_listeners_;
   }
@@ -355,8 +363,14 @@ class Instrumentation {
       LOCKS_EXCLUDED(deoptimized_methods_lock_);
 
  private:
+  InstrumentationLevel GetCurrentInstrumentationLevel() const;
+
   // Does the job of installing or removing instrumentation code within methods.
-  void ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter)
+  // In order to support multiple clients using instrumentation at the same time,
+  // the caller must pass a unique key (a string) identifying it so we remind which
+  // instrumentation level it needs. Therefore the current instrumentation level
+  // becomes the highest instrumentation level required by a client.
+  void ConfigureStubs(const char* key, InstrumentationLevel desired_instrumentation_level)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_,
                      deoptimized_methods_lock_);
@@ -452,6 +466,11 @@ class Instrumentation {
   // Do we have any backward branch listeners? Short-cut to avoid taking the instrumentation_lock_.
   bool have_backward_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
 
+  // Contains the instrumentation level required by each client of the instrumentation identified
+  // by a string key.
+  typedef SafeMap<const char*, InstrumentationLevel> InstrumentationLevelTable;
+  InstrumentationLevelTable requested_instrumentation_levels_ GUARDED_BY(Locks::mutator_lock_);
+
   // The event listeners, written to with the mutator_lock_ exclusively held.
   std::list<InstrumentationListener*> method_entry_listeners_ GUARDED_BY(Locks::mutator_lock_);
   std::list<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_);
@@ -481,9 +500,12 @@ class Instrumentation {
   size_t quick_alloc_entry_points_instrumentation_counter_
       GUARDED_BY(Locks::instrument_entrypoints_lock_);
 
+  friend class InstrumentationTest;  // For GetCurrentInstrumentationLevel and ConfigureStubs.
+
   DISALLOW_COPY_AND_ASSIGN(Instrumentation);
 };
 std::ostream& operator<<(std::ostream& os, const Instrumentation::InstrumentationEvent& rhs);
+std::ostream& operator<<(std::ostream& os, const Instrumentation::InstrumentationLevel& rhs);
 
 // An element in the instrumentation side stack maintained in art::Thread.
 struct InstrumentationStackFrame {
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
new file mode 100644 (file)
index 0000000..5afacb8
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instrumentation.h"
+
+#include "common_runtime_test.h"
+#include "common_throws.h"
+#include "class_linker-inl.h"
+#include "dex_file.h"
+#include "handle_scope-inl.h"
+#include "jvalue.h"
+#include "runtime.h"
+#include "scoped_thread_state_change.h"
+#include "thread_list.h"
+#include "thread-inl.h"
+
+namespace art {
+namespace instrumentation {
+
+class TestInstrumentationListener FINAL : public instrumentation::InstrumentationListener {
+ public:
+  TestInstrumentationListener()
+    : received_method_enter_event(false), received_method_exit_event(false),
+      received_method_unwind_event(false), received_dex_pc_moved_event(false),
+      received_field_read_event(false), received_field_written_event(false),
+      received_exception_caught_event(false), received_backward_branch_event(false) {}
+
+  virtual ~TestInstrumentationListener() {}
+
+  void MethodEntered(Thread* thread ATTRIBUTE_UNUSED,
+                     mirror::Object* this_object ATTRIBUTE_UNUSED,
+                     mirror::ArtMethod* method ATTRIBUTE_UNUSED,
+                     uint32_t dex_pc ATTRIBUTE_UNUSED)
+      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    received_method_enter_event = true;
+  }
+
+  void MethodExited(Thread* thread ATTRIBUTE_UNUSED,
+                    mirror::Object* this_object ATTRIBUTE_UNUSED,
+                    mirror::ArtMethod* method ATTRIBUTE_UNUSED,
+                    uint32_t dex_pc ATTRIBUTE_UNUSED,
+                    const JValue& return_value ATTRIBUTE_UNUSED)
+      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    received_method_exit_event = true;
+  }
+
+  void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED,
+                    mirror::Object* this_object ATTRIBUTE_UNUSED,
+                    mirror::ArtMethod* method ATTRIBUTE_UNUSED,
+                    uint32_t dex_pc ATTRIBUTE_UNUSED)
+      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    received_method_unwind_event = true;
+  }
+
+  void DexPcMoved(Thread* thread ATTRIBUTE_UNUSED,
+                  mirror::Object* this_object ATTRIBUTE_UNUSED,
+                  mirror::ArtMethod* method ATTRIBUTE_UNUSED,
+                  uint32_t new_dex_pc ATTRIBUTE_UNUSED)
+      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    received_dex_pc_moved_event = true;
+  }
+
+  void FieldRead(Thread* thread ATTRIBUTE_UNUSED,
+                 mirror::Object* this_object ATTRIBUTE_UNUSED,
+                 mirror::ArtMethod* method ATTRIBUTE_UNUSED,
+                 uint32_t dex_pc ATTRIBUTE_UNUSED,
+                 ArtField* field ATTRIBUTE_UNUSED)
+      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    received_field_read_event = true;
+  }
+
+  void FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
+                    mirror::Object* this_object ATTRIBUTE_UNUSED,
+                    mirror::ArtMethod* method ATTRIBUTE_UNUSED,
+                    uint32_t dex_pc ATTRIBUTE_UNUSED,
+                    ArtField* field ATTRIBUTE_UNUSED,
+                    const JValue& field_value ATTRIBUTE_UNUSED)
+      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    received_field_written_event = true;
+  }
+
+  void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED,
+                       mirror::Throwable* exception_object ATTRIBUTE_UNUSED)
+      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    received_exception_caught_event = true;
+  }
+
+  void BackwardBranch(Thread* thread ATTRIBUTE_UNUSED,
+                      mirror::ArtMethod* method ATTRIBUTE_UNUSED,
+                      int32_t dex_pc_offset ATTRIBUTE_UNUSED)
+      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    received_backward_branch_event = true;
+  }
+
+  void Reset() {
+    received_method_enter_event = false;
+    received_method_exit_event = false;
+    received_method_unwind_event = false;
+    received_dex_pc_moved_event = false;
+    received_field_read_event = false;
+    received_field_written_event = false;
+    received_exception_caught_event = false;
+    received_backward_branch_event = false;
+  }
+
+  bool received_method_enter_event;
+  bool received_method_exit_event;
+  bool received_method_unwind_event;
+  bool received_dex_pc_moved_event;
+  bool received_field_read_event;
+  bool received_field_written_event;
+  bool received_exception_caught_event;
+  bool received_backward_branch_event;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestInstrumentationListener);
+};
+
+class InstrumentationTest : public CommonRuntimeTest {
+ public:
+  // Unique keys used to test Instrumentation::ConfigureStubs.
+  static constexpr const char* kClientOneKey = "TestClient1";
+  static constexpr const char* kClientTwoKey = "TestClient2";
+
+  void CheckConfigureStubs(const char* key, Instrumentation::InstrumentationLevel level) {
+    ScopedObjectAccess soa(Thread::Current());
+    instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+    {
+      soa.Self()->TransitionFromRunnableToSuspended(kSuspended);
+      Runtime* runtime = Runtime::Current();
+      runtime->GetThreadList()->SuspendAll("Instrumentation::ConfigureStubs");
+      instr->ConfigureStubs(key, level);
+      runtime->GetThreadList()->ResumeAll();
+      soa.Self()->TransitionFromSuspendedToRunnable();
+    }
+  }
+
+  Instrumentation::InstrumentationLevel GetCurrentInstrumentationLevel() {
+    return Runtime::Current()->GetInstrumentation()->GetCurrentInstrumentationLevel();
+  }
+
+  size_t GetInstrumentationUserCount() {
+    ScopedObjectAccess soa(Thread::Current());
+    return Runtime::Current()->GetInstrumentation()->requested_instrumentation_levels_.size();
+  }
+
+  void TestEvent(uint32_t instrumentation_event) {
+    ScopedObjectAccess soa(Thread::Current());
+    instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+    TestInstrumentationListener listener;
+    {
+      soa.Self()->TransitionFromRunnableToSuspended(kSuspended);
+      Runtime* runtime = Runtime::Current();
+      runtime->GetThreadList()->SuspendAll("Add instrumentation listener");
+      instr->AddListener(&listener, instrumentation_event);
+      runtime->GetThreadList()->ResumeAll();
+      soa.Self()->TransitionFromSuspendedToRunnable();
+    }
+
+    mirror::ArtMethod* const event_method = nullptr;
+    mirror::Object* const event_obj = nullptr;
+    const uint32_t event_dex_pc = 0;
+
+    // Check the listener is registered and is notified of the event.
+    EXPECT_TRUE(HasEventListener(instr, instrumentation_event));
+    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event));
+    ReportEvent(instr, instrumentation_event, soa.Self(), event_method, event_obj, event_dex_pc);
+    EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event));
+
+    listener.Reset();
+    {
+      soa.Self()->TransitionFromRunnableToSuspended(kSuspended);
+      Runtime* runtime = Runtime::Current();
+      runtime->GetThreadList()->SuspendAll("Remove instrumentation listener");
+      instr->RemoveListener(&listener, instrumentation_event);
+      runtime->GetThreadList()->ResumeAll();
+      soa.Self()->TransitionFromSuspendedToRunnable();
+    }
+
+    // Check the listener is not registered and is not notified of the event.
+    EXPECT_FALSE(HasEventListener(instr, instrumentation_event));
+    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event));
+    ReportEvent(instr, instrumentation_event, soa.Self(), event_method, event_obj, event_dex_pc);
+    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event));
+  }
+
+  void DeoptimizeMethod(Thread* self, Handle<mirror::ArtMethod> method,
+                        bool enable_deoptimization)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Runtime* runtime = Runtime::Current();
+    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
+    self->TransitionFromRunnableToSuspended(kSuspended);
+    runtime->GetThreadList()->SuspendAll("Single method deoptimization");
+    if (enable_deoptimization) {
+      instrumentation->EnableDeoptimization();
+    }
+    instrumentation->Deoptimize(method.Get());
+    runtime->GetThreadList()->ResumeAll();
+    self->TransitionFromSuspendedToRunnable();
+  }
+
+  void UndeoptimizeMethod(Thread* self, Handle<mirror::ArtMethod> method,
+                          const char* key, bool disable_deoptimization)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Runtime* runtime = Runtime::Current();
+    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
+    self->TransitionFromRunnableToSuspended(kSuspended);
+    runtime->GetThreadList()->SuspendAll("Single method undeoptimization");
+    instrumentation->Undeoptimize(method.Get());
+    if (disable_deoptimization) {
+      instrumentation->DisableDeoptimization(key);
+    }
+    runtime->GetThreadList()->ResumeAll();
+    self->TransitionFromSuspendedToRunnable();
+  }
+
+  void DeoptimizeEverything(Thread* self, const char* key, bool enable_deoptimization)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Runtime* runtime = Runtime::Current();
+    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
+    self->TransitionFromRunnableToSuspended(kSuspended);
+    runtime->GetThreadList()->SuspendAll("Full deoptimization");
+    if (enable_deoptimization) {
+      instrumentation->EnableDeoptimization();
+    }
+    instrumentation->DeoptimizeEverything(key);
+    runtime->GetThreadList()->ResumeAll();
+    self->TransitionFromSuspendedToRunnable();
+  }
+
+  void UndeoptimizeEverything(Thread* self, const char* key, bool disable_deoptimization)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Runtime* runtime = Runtime::Current();
+    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
+    self->TransitionFromRunnableToSuspended(kSuspended);
+    runtime->GetThreadList()->SuspendAll("Full undeoptimization");
+    instrumentation->UndeoptimizeEverything(key);
+    if (disable_deoptimization) {
+      instrumentation->DisableDeoptimization(key);
+    }
+    runtime->GetThreadList()->ResumeAll();
+    self->TransitionFromSuspendedToRunnable();
+  }
+
+  void EnableMethodTracing(Thread* self, const char* key, bool needs_interpreter)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Runtime* runtime = Runtime::Current();
+    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
+    self->TransitionFromRunnableToSuspended(kSuspended);
+    runtime->GetThreadList()->SuspendAll("EnableMethodTracing");
+    instrumentation->EnableMethodTracing(key, needs_interpreter);
+    runtime->GetThreadList()->ResumeAll();
+    self->TransitionFromSuspendedToRunnable();
+  }
+
+  void DisableMethodTracing(Thread* self, const char* key)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Runtime* runtime = Runtime::Current();
+    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
+    self->TransitionFromRunnableToSuspended(kSuspended);
+    runtime->GetThreadList()->SuspendAll("EnableMethodTracing");
+    instrumentation->DisableMethodTracing(key);
+    runtime->GetThreadList()->ResumeAll();
+    self->TransitionFromSuspendedToRunnable();
+  }
+
+ private:
+  static bool HasEventListener(const instrumentation::Instrumentation* instr, uint32_t event_type)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    switch (event_type) {
+      case instrumentation::Instrumentation::kMethodEntered:
+        return instr->HasMethodEntryListeners();
+      case instrumentation::Instrumentation::kMethodExited:
+        return instr->HasMethodExitListeners();
+      case instrumentation::Instrumentation::kMethodUnwind:
+        return instr->HasMethodUnwindListeners();
+      case instrumentation::Instrumentation::kDexPcMoved:
+        return instr->HasDexPcListeners();
+      case instrumentation::Instrumentation::kFieldRead:
+        return instr->HasFieldReadListeners();
+      case instrumentation::Instrumentation::kFieldWritten:
+        return instr->HasFieldWriteListeners();
+      case instrumentation::Instrumentation::kExceptionCaught:
+        return instr->HasExceptionCaughtListeners();
+      case instrumentation::Instrumentation::kBackwardBranch:
+        return instr->HasBackwardBranchListeners();
+      default:
+        LOG(FATAL) << "Unknown instrumentation event " << event_type;
+        UNREACHABLE();
+    }
+  }
+
+  static void ReportEvent(const instrumentation::Instrumentation* instr, uint32_t event_type,
+                          Thread* self, mirror::ArtMethod* method, mirror::Object* obj,
+                          uint32_t dex_pc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    switch (event_type) {
+      case instrumentation::Instrumentation::kMethodEntered:
+        instr->MethodEnterEvent(self, obj, method, dex_pc);
+        break;
+      case instrumentation::Instrumentation::kMethodExited: {
+        JValue value;
+        instr->MethodExitEvent(self, obj, method, dex_pc, value);
+        break;
+      }
+      case instrumentation::Instrumentation::kMethodUnwind:
+        instr->MethodUnwindEvent(self, obj, method, dex_pc);
+        break;
+      case instrumentation::Instrumentation::kDexPcMoved:
+        instr->DexPcMovedEvent(self, obj, method, dex_pc);
+        break;
+      case instrumentation::Instrumentation::kFieldRead:
+        instr->FieldReadEvent(self, obj, method, dex_pc, nullptr);
+        break;
+      case instrumentation::Instrumentation::kFieldWritten: {
+        JValue value;
+        instr->FieldWriteEvent(self, obj, method, dex_pc, nullptr, value);
+        break;
+      }
+      case instrumentation::Instrumentation::kExceptionCaught: {
+        ThrowArithmeticExceptionDivideByZero();
+        mirror::Throwable* event_exception = self->GetException();
+        instr->ExceptionCaughtEvent(self, event_exception);
+        self->ClearException();
+        break;
+      }
+      case instrumentation::Instrumentation::kBackwardBranch:
+        instr->BackwardBranch(self, method, dex_pc);
+        break;
+      default:
+        LOG(FATAL) << "Unknown instrumentation event " << event_type;
+        UNREACHABLE();
+    }
+  }
+
+  static bool DidListenerReceiveEvent(const TestInstrumentationListener& listener,
+                                      uint32_t event_type) {
+    switch (event_type) {
+      case instrumentation::Instrumentation::kMethodEntered:
+        return listener.received_method_enter_event;
+      case instrumentation::Instrumentation::kMethodExited:
+        return listener.received_method_exit_event;
+      case instrumentation::Instrumentation::kMethodUnwind:
+        return listener.received_method_unwind_event;
+      case instrumentation::Instrumentation::kDexPcMoved:
+        return listener.received_dex_pc_moved_event;
+      case instrumentation::Instrumentation::kFieldRead:
+        return listener.received_field_read_event;
+      case instrumentation::Instrumentation::kFieldWritten:
+        return listener.received_field_written_event;
+      case instrumentation::Instrumentation::kExceptionCaught:
+        return listener.received_exception_caught_event;
+      case instrumentation::Instrumentation::kBackwardBranch:
+        return listener.received_backward_branch_event;
+      default:
+        LOG(FATAL) << "Unknown instrumentation event " << event_type;
+        UNREACHABLE();
+    }
+  }
+};
+
+TEST_F(InstrumentationTest, NoInstrumentation) {
+  ScopedObjectAccess soa(Thread::Current());
+  instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+  ASSERT_NE(instr, nullptr);
+
+  EXPECT_FALSE(instr->AreExitStubsInstalled());
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+  EXPECT_FALSE(instr->IsActive());
+  EXPECT_FALSE(instr->ShouldNotifyMethodEnterExitEvents());
+
+  // Test interpreter table is the default one.
+  EXPECT_EQ(instrumentation::kMainHandlerTable, instr->GetInterpreterHandlerTable());
+
+  // Check there is no registered listener.
+  EXPECT_FALSE(instr->HasDexPcListeners());
+  EXPECT_FALSE(instr->HasExceptionCaughtListeners());
+  EXPECT_FALSE(instr->HasFieldReadListeners());
+  EXPECT_FALSE(instr->HasFieldWriteListeners());
+  EXPECT_FALSE(instr->HasMethodEntryListeners());
+  EXPECT_FALSE(instr->HasMethodExitListeners());
+  EXPECT_FALSE(instr->IsActive());
+}
+
+// Test instrumentation listeners for each event.
+TEST_F(InstrumentationTest, MethodEntryEvent) {
+  TestEvent(instrumentation::Instrumentation::kMethodEntered);
+}
+
+TEST_F(InstrumentationTest, MethodExitEvent) {
+  TestEvent(instrumentation::Instrumentation::kMethodExited);
+}
+
+TEST_F(InstrumentationTest, MethodUnwindEvent) {
+  TestEvent(instrumentation::Instrumentation::kMethodUnwind);
+}
+
+TEST_F(InstrumentationTest, DexPcMovedEvent) {
+  TestEvent(instrumentation::Instrumentation::kDexPcMoved);
+}
+
+TEST_F(InstrumentationTest, FieldReadEvent) {
+  TestEvent(instrumentation::Instrumentation::kFieldRead);
+}
+
+TEST_F(InstrumentationTest, FieldWriteEvent) {
+  TestEvent(instrumentation::Instrumentation::kFieldWritten);
+}
+
+TEST_F(InstrumentationTest, ExceptionCaughtEvent) {
+  TestEvent(instrumentation::Instrumentation::kExceptionCaught);
+}
+
+TEST_F(InstrumentationTest, BackwardBranchEvent) {
+  TestEvent(instrumentation::Instrumentation::kBackwardBranch);
+}
+
+TEST_F(InstrumentationTest, DeoptimizeDirectMethod) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject class_loader = LoadDex("Instrumentation");
+  Runtime* const runtime = Runtime::Current();
+  instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader)));
+  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+  ASSERT_TRUE(klass != nullptr);
+  Handle<mirror::ArtMethod> method_to_deoptimize(
+      hs.NewHandle(klass->FindDeclaredDirectMethod("instanceMethod", "()V")));
+  ASSERT_TRUE(method_to_deoptimize.Get() != nullptr);
+
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+  EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize.Get()));
+
+  DeoptimizeMethod(soa.Self(), method_to_deoptimize, true);
+
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+  EXPECT_TRUE(instr->AreExitStubsInstalled());
+  EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize.Get()));
+
+  constexpr const char* instrumentation_key = "DeoptimizeDirectMethod";
+  UndeoptimizeMethod(soa.Self(), method_to_deoptimize, instrumentation_key, true);
+
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+  EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize.Get()));
+}
+
+TEST_F(InstrumentationTest, FullDeoptimization) {
+  ScopedObjectAccess soa(Thread::Current());
+  Runtime* const runtime = Runtime::Current();
+  instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+
+  constexpr const char* instrumentation_key = "FullDeoptimization";
+  DeoptimizeEverything(soa.Self(), instrumentation_key, true);
+
+  EXPECT_TRUE(instr->AreAllMethodsDeoptimized());
+  EXPECT_TRUE(instr->AreExitStubsInstalled());
+
+  UndeoptimizeEverything(soa.Self(), instrumentation_key, true);
+
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+}
+
+TEST_F(InstrumentationTest, MixedDeoptimization) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject class_loader = LoadDex("Instrumentation");
+  Runtime* const runtime = Runtime::Current();
+  instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader)));
+  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+  ASSERT_TRUE(klass != nullptr);
+  Handle<mirror::ArtMethod> method_to_deoptimize(
+      hs.NewHandle(klass->FindDeclaredDirectMethod("instanceMethod", "()V")));
+  ASSERT_TRUE(method_to_deoptimize.Get() != nullptr);
+
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+  EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize.Get()));
+
+  DeoptimizeMethod(soa.Self(), method_to_deoptimize, true);
+  // Deoptimizing a method does not change instrumentation level.
+  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
+            GetCurrentInstrumentationLevel());
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+  EXPECT_TRUE(instr->AreExitStubsInstalled());
+  EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize.Get()));
+
+  constexpr const char* instrumentation_key = "MixedDeoptimization";
+  DeoptimizeEverything(soa.Self(), instrumentation_key, false);
+  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter,
+            GetCurrentInstrumentationLevel());
+  EXPECT_TRUE(instr->AreAllMethodsDeoptimized());
+  EXPECT_TRUE(instr->AreExitStubsInstalled());
+  EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize.Get()));
+
+  UndeoptimizeEverything(soa.Self(), instrumentation_key, false);
+  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
+            GetCurrentInstrumentationLevel());
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+  EXPECT_TRUE(instr->AreExitStubsInstalled());
+  EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize.Get()));
+
+  UndeoptimizeMethod(soa.Self(), method_to_deoptimize, instrumentation_key, true);
+  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
+            GetCurrentInstrumentationLevel());
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+  EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize.Get()));
+}
+
+TEST_F(InstrumentationTest, MethodTracing_Interpreter) {
+  ScopedObjectAccess soa(Thread::Current());
+  Runtime* const runtime = Runtime::Current();
+  instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+
+  constexpr const char* instrumentation_key = "MethodTracing";
+  EnableMethodTracing(soa.Self(), instrumentation_key, true);
+  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter,
+            GetCurrentInstrumentationLevel());
+  EXPECT_TRUE(instr->AreAllMethodsDeoptimized());
+  EXPECT_TRUE(instr->AreExitStubsInstalled());
+
+  DisableMethodTracing(soa.Self(), instrumentation_key);
+  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
+            GetCurrentInstrumentationLevel());
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+}
+
+TEST_F(InstrumentationTest, MethodTracing_InstrumentationEntryExitStubs) {
+  ScopedObjectAccess soa(Thread::Current());
+  Runtime* const runtime = Runtime::Current();
+  instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+
+  constexpr const char* instrumentation_key = "MethodTracing";
+  EnableMethodTracing(soa.Self(), instrumentation_key, false);
+  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+            GetCurrentInstrumentationLevel());
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+  EXPECT_TRUE(instr->AreExitStubsInstalled());
+
+  DisableMethodTracing(soa.Self(), instrumentation_key);
+  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
+            GetCurrentInstrumentationLevel());
+  EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
+}
+
+// We use a macro to print the line number where the test is failing.
+#define CHECK_INSTRUMENTATION(_level, _user_count)                                      \
+  do {                                                                                  \
+    Instrumentation* const instr = Runtime::Current()->GetInstrumentation();            \
+    bool interpreter =                                                                  \
+      (_level == Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);    \
+    EXPECT_EQ(_level, GetCurrentInstrumentationLevel());                                \
+    EXPECT_EQ(_user_count, GetInstrumentationUserCount());                              \
+    if (instr->IsForcedInterpretOnly()) {                                               \
+      EXPECT_TRUE(instr->InterpretOnly());                                              \
+    } else if (interpreter) {                                                           \
+      EXPECT_TRUE(instr->InterpretOnly());                                              \
+    } else {                                                                            \
+      EXPECT_FALSE(instr->InterpretOnly());                                             \
+    }                                                                                   \
+    if (interpreter) {                                                                  \
+      EXPECT_TRUE(instr->AreAllMethodsDeoptimized());                                   \
+    } else {                                                                            \
+      EXPECT_FALSE(instr->AreAllMethodsDeoptimized());                                  \
+    }                                                                                   \
+  } while (false)
+
+TEST_F(InstrumentationTest, ConfigureStubs_Nothing) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Check no-op.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+TEST_F(InstrumentationTest, ConfigureStubs_InstrumentationStubs) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Check we can switch to instrumentation stubs
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+                        1U);
+
+  // Check we can disable instrumentation.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+TEST_F(InstrumentationTest, ConfigureStubs_Interpreter) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Check we can switch to interpreter
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
+
+  // Check we can disable instrumentation.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+TEST_F(InstrumentationTest, ConfigureStubs_InstrumentationStubsToInterpreter) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Configure stubs with instrumentation stubs.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+                        1U);
+
+  // Configure stubs with interpreter.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
+
+  // Check we can disable instrumentation.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+TEST_F(InstrumentationTest, ConfigureStubs_InterpreterToInstrumentationStubs) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Configure stubs with interpreter.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
+
+  // Configure stubs with instrumentation stubs.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+                        1U);
+
+  // Check we can disable instrumentation.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+TEST_F(InstrumentationTest,
+       ConfigureStubs_InstrumentationStubsToInterpreterToInstrumentationStubs) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Configure stubs with instrumentation stubs.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+                        1U);
+
+  // Configure stubs with interpreter.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
+
+  // Configure stubs with instrumentation stubs again.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+                        1U);
+
+  // Check we can disable instrumentation.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+TEST_F(InstrumentationTest, MultiConfigureStubs_Nothing) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Check kInstrumentNothing with two clients.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+TEST_F(InstrumentationTest, MultiConfigureStubs_InstrumentationStubs) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Configure stubs with instrumentation stubs for 1st client.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+                        1U);
+
+  // Configure stubs with instrumentation stubs for 2nd client.
+  CheckConfigureStubs(kClientTwoKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+                        2U);
+
+  // 1st client requests instrumentation deactivation but 2nd client still needs
+  // instrumentation stubs.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+                        1U);
+
+  // 2nd client requests instrumentation deactivation
+  CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+TEST_F(InstrumentationTest, MultiConfigureStubs_Interpreter) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Configure stubs with interpreter for 1st client.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
+
+  // Configure stubs with interpreter for 2nd client.
+  CheckConfigureStubs(kClientTwoKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 2U);
+
+  // 1st client requests instrumentation deactivation but 2nd client still needs interpreter.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
+
+  // 2nd client requests instrumentation deactivation
+  CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+TEST_F(InstrumentationTest, MultiConfigureStubs_InstrumentationStubsThenInterpreter) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Configure stubs with instrumentation stubs for 1st client.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+                        1U);
+
+  // Configure stubs with interpreter for 2nd client.
+  CheckConfigureStubs(kClientTwoKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 2U);
+
+  // 1st client requests instrumentation deactivation but 2nd client still needs interpreter.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
+
+  // 2nd client requests instrumentation deactivation
+  CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+TEST_F(InstrumentationTest, MultiConfigureStubs_InterpreterThenInstrumentationStubs) {
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+
+  // Configure stubs with interpreter for 1st client.
+  CheckConfigureStubs(kClientOneKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
+
+  // Configure stubs with instrumentation stubs for 2nd client.
+  CheckConfigureStubs(kClientTwoKey,
+                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 2U);
+
+  // 1st client requests instrumentation deactivation but 2nd client still needs
+  // instrumentation stubs.
+  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+                        1U);
+
+  // 2nd client requests instrumentation deactivation
+  CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
+  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
+}
+
+}  // namespace instrumentation
+}  // namespace art
index f5ad8b8..c698cfc 100644 (file)
@@ -19,8 +19,6 @@
 
 #include <unordered_map>
 
-#include "instrumentation.h"
-
 #include "atomic.h"
 #include "base/macros.h"
 #include "base/mutex.h"
index 9eca517..3b8feda 100644 (file)
@@ -126,6 +126,9 @@ Trace* volatile Trace::the_trace_ = nullptr;
 pthread_t Trace::sampling_pthread_ = 0U;
 std::unique_ptr<std::vector<mirror::ArtMethod*>> Trace::temp_stack_trace_;
 
+// The key identifying the tracer to update instrumentation.
+static constexpr const char* kTracerInstrumentationKey = "Tracer";
+
 static mirror::ArtMethod* DecodeTraceMethodId(uint32_t tmid) {
   return reinterpret_cast<mirror::ArtMethod*>(tmid & ~kTraceMethodActionMask);
 }
@@ -393,7 +396,7 @@ void Trace::Start(const char* trace_filename, int trace_fd, size_t buffer_size,
                                                    instrumentation::Instrumentation::kMethodExited |
                                                    instrumentation::Instrumentation::kMethodUnwind);
         // TODO: In full-PIC mode, we don't need to fully deopt.
-        runtime->GetInstrumentation()->EnableMethodTracing();
+        runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey);
       }
     }
   }
@@ -440,7 +443,7 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) {
       MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
       runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
     } else {
-      runtime->GetInstrumentation()->DisableMethodTracing();
+      runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
       runtime->GetInstrumentation()->RemoveListener(
           the_trace, instrumentation::Instrumentation::kMethodEntered |
           instrumentation::Instrumentation::kMethodExited |
@@ -522,7 +525,7 @@ void Trace::Pause() {
       MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
       runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
     } else {
-      runtime->GetInstrumentation()->DisableMethodTracing();
+      runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
       runtime->GetInstrumentation()->RemoveListener(the_trace,
                                                     instrumentation::Instrumentation::kMethodEntered |
                                                     instrumentation::Instrumentation::kMethodExited |
@@ -566,7 +569,7 @@ void Trace::Resume() {
                                                instrumentation::Instrumentation::kMethodExited |
                                                instrumentation::Instrumentation::kMethodUnwind);
     // TODO: In full-PIC mode, we don't need to fully deopt.
-    runtime->GetInstrumentation()->EnableMethodTracing();
+    runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey);
   }
 
   runtime->GetThreadList()->ResumeAll();
diff --git a/test/Instrumentation/Instrumentation.java b/test/Instrumentation/Instrumentation.java
new file mode 100644 (file)
index 0000000..09d4342
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Instrumentation {
+  // Direct method
+  private void instanceMethod() {
+    System.out.println("instanceMethod");
+  }
+}