Add JVMTI Breakpoint and SingleStep functionality

Adds support for can_generate_single_step_events and
can_generate_breakpoint_events capabilities.

Tests follow in next CL

Bug: 62821960
Test: ./test.py --host -j40
Change-Id: I5e71b85d7a293784d08aea727dac22236d6fab14
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index e38f265..619a49a 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -27,6 +27,7 @@
            "fixed_up_dex_file.cc",
            "object_tagging.cc",
            "OpenjdkJvmTi.cc",
+           "ti_breakpoint.cc",
            "ti_class.cc",
            "ti_class_definition.cc",
            "ti_class_loader.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 0896210..e3768b3 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -48,6 +48,7 @@
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
 #include "thread_list.h"
+#include "ti_breakpoint.h"
 #include "ti_class.h"
 #include "ti_dump.h"
 #include "ti_field.h"
@@ -619,20 +620,17 @@
     return ERR(NOT_IMPLEMENTED);
   }
 
-  static jvmtiError SetBreakpoint(jvmtiEnv* env,
-                                  jmethodID method ATTRIBUTE_UNUSED,
-                                  jlocation location ATTRIBUTE_UNUSED) {
+
+  static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
-    return ERR(NOT_IMPLEMENTED);
+    return BreakpointUtil::SetBreakpoint(env, method, location);
   }
 
-  static jvmtiError ClearBreakpoint(jvmtiEnv* env,
-                                    jmethodID method ATTRIBUTE_UNUSED,
-                                    jlocation location ATTRIBUTE_UNUSED) {
+  static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
-    return ERR(NOT_IMPLEMENTED);
+    return BreakpointUtil::ClearBreakpoint(env, method, location);
   }
 
   static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) {
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index b5f1219..2d5d527 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -34,6 +34,7 @@
 
 #include <memory>
 #include <type_traits>
+#include <unordered_map>
 #include <unordered_set>
 
 #include <jni.h>
@@ -46,10 +47,12 @@
 #include "java_vm_ext.h"
 #include "jni_env_ext.h"
 #include "jvmti.h"
+#include "ti_breakpoint.h"
 
 namespace art {
 class ArtField;
-}
+class ArtMethod;
+}  // namespace art
 
 namespace openjdkjvmti {
 
@@ -76,6 +79,9 @@
   std::unordered_set<art::ArtField*> access_watched_fields;
   std::unordered_set<art::ArtField*> modify_watched_fields;
 
+  // Set of breakpoints is unique to each jvmtiEnv.
+  std::unordered_set<Breakpoint> breakpoints;
+
   ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler);
 
   static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) {
@@ -223,10 +229,10 @@
     .can_get_source_debug_extension                  = 1,
     .can_access_local_variables                      = 0,
     .can_maintain_original_method_order              = 0,
-    .can_generate_single_step_events                 = 0,
+    .can_generate_single_step_events                 = 1,
     .can_generate_exception_events                   = 0,
     .can_generate_frame_pop_events                   = 0,
-    .can_generate_breakpoint_events                  = 0,
+    .can_generate_breakpoint_events                  = 1,
     .can_suspend                                     = 0,
     .can_redefine_any_class                          = 0,
     .can_get_current_thread_cpu_time                 = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index af99233..f30d7ce 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -22,6 +22,7 @@
 #include "events.h"
 #include "jni_internal.h"
 #include "ScopedLocalRef.h"
+#include "ti_breakpoint.h"
 
 #include "art_jvmti.h"
 
@@ -217,6 +218,32 @@
   }
 }
 
+// Need to give custom specializations for Breakpoint since it needs to filter out which particular
+// methods/dex_pcs agents get notified on.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kBreakpoint>(art::Thread* thread,
+                                                                    JNIEnv* jnienv,
+                                                                    jthread jni_thread,
+                                                                    jmethodID jmethod,
+                                                                    jlocation location) const {
+  art::ArtMethod* method = art::jni::DecodeArtMethod(jmethod);
+  for (ArtJvmTiEnv* env : envs) {
+    // Search for a breakpoint on this particular method and location.
+    if (env != nullptr &&
+        ShouldDispatch<ArtJvmtiEvent::kBreakpoint>(env, thread) &&
+        env->breakpoints.find({method, location}) != env->breakpoints.end()) {
+      // We temporarily clear any pending exceptions so the event can call back into java code.
+      ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+      jnienv->ExceptionClear();
+      auto callback = impl::GetCallback<ArtJvmtiEvent::kBreakpoint>(env);
+      (*callback)(env, jnienv, jni_thread, jmethod, location);
+      if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+        jnienv->Throw(thr.get());
+      }
+    }
+  }
+}
+
 // Need to give custom specializations for FieldAccess and FieldModification since they need to
 // filter out which particular fields agents want to get notified on.
 // TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 989b9af..f749daa 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -423,14 +423,30 @@
     }
   }
 
-  // Call-back for when the dex pc moves in a method. We don't currently have any events associated
-  // with this.
-  void DexPcMoved(art::Thread* self ATTRIBUTE_UNUSED,
+  // Call-back for when the dex pc moves in a method.
+  void DexPcMoved(art::Thread* self,
                   art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
-                  art::ArtMethod* method ATTRIBUTE_UNUSED,
-                  uint32_t new_dex_pc ATTRIBUTE_UNUSED)
+                  art::ArtMethod* method,
+                  uint32_t new_dex_pc)
       REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
-    return;
+    DCHECK(!method->IsRuntimeMethod());
+    // Default methods might be copied to multiple classes. We need to get the canonical version of
+    // this method so that we can check for breakpoints correctly.
+    // TODO We should maybe do this on other events to ensure that we are consistent WRT default
+    // methods. This could interact with obsolete methods if we ever let interface redefinition
+    // happen though.
+    method = method->GetCanonicalMethod();
+    art::JNIEnvExt* jnienv = self->GetJniEnv();
+    jmethodID jmethod = art::jni::EncodeArtMethod(method);
+    jlocation location = static_cast<jlocation>(new_dex_pc);
+    // Step event is reported first according to the spec.
+    if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kSingleStep)) {
+      RunEventCallback<ArtJvmtiEvent::kSingleStep>(self, jnienv, jmethod, location);
+    }
+    // Next we do the Breakpoint events. The Dispatch code will filter the individual
+    if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kBreakpoint)) {
+      RunEventCallback<ArtJvmtiEvent::kBreakpoint>(self, jnienv, jmethod, location);
+    }
   }
 
   // Call-back for when we read from a field.
@@ -563,6 +579,9 @@
       return art::instrumentation::Instrumentation::kFieldWritten;
     case ArtJvmtiEvent::kFieldAccess:
       return art::instrumentation::Instrumentation::kFieldRead;
+    case ArtJvmtiEvent::kBreakpoint:
+    case ArtJvmtiEvent::kSingleStep:
+      return art::instrumentation::Instrumentation::kDexPcMoved;
     default:
       LOG(FATAL) << "Unknown event ";
       return 0;
@@ -580,6 +599,8 @@
                                        art::gc::kCollectorTypeInstrumentation);
   art::ScopedSuspendAll ssa("jvmti method tracing installation");
   if (enable) {
+    // TODO Depending on the features being used we should be able to avoid deoptimizing everything
+    // like we do here.
     if (!instr->AreAllMethodsDeoptimized()) {
       instr->EnableMethodTracing("jvmti-tracing", /*needs_interpreter*/true);
     }
@@ -601,6 +622,17 @@
       SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
       return;
 
+    case ArtJvmtiEvent::kBreakpoint:
+    case ArtJvmtiEvent::kSingleStep: {
+      ArtJvmtiEvent other = (event == ArtJvmtiEvent::kBreakpoint) ? ArtJvmtiEvent::kSingleStep
+                                                                  : ArtJvmtiEvent::kBreakpoint;
+      // We only need to do anything if there isn't already a listener installed/held-on by the
+      // other jvmti event that uses DexPcMoved.
+      if (!IsEventEnabledAnywhere(other)) {
+        SetupTraceListener(method_trace_listener_.get(), event, enable);
+      }
+      return;
+    }
     case ArtJvmtiEvent::kMethodEntry:
     case ArtJvmtiEvent::kMethodExit:
     case ArtJvmtiEvent::kFieldAccess:
diff --git a/runtime/openjdkjvmti/ti_breakpoint.cc b/runtime/openjdkjvmti/ti_breakpoint.cc
new file mode 100644
index 0000000..6d0e2c6
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_breakpoint.cc
@@ -0,0 +1,114 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <functional>
+
+#include "ti_breakpoint.h"
+
+#include "art_jvmti.h"
+#include "art_method-inl.h"
+#include "base/enums.h"
+#include "dex_file_annotations.h"
+#include "events-inl.h"
+#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/object_array-inl.h"
+#include "modifiers.h"
+#include "runtime_callbacks.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+#include "thread-current-inl.h"
+#include "thread_list.h"
+#include "ti_phase.h"
+
+namespace openjdkjvmti {
+
+size_t Breakpoint::hash() const {
+  return std::hash<uintptr_t> {}(reinterpret_cast<uintptr_t>(method_))
+      ^ std::hash<jlocation> {}(location_);
+}
+
+Breakpoint::Breakpoint(art::ArtMethod* m, jlocation loc) : method_(m), location_(loc) {
+  DCHECK(!m->IsDefault() || !m->IsCopied() || !m->IsInvokable())
+      << "Flags are: 0x" << std::hex << m->GetAccessFlags();
+}
+
+void BreakpointUtil::RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass) {
+  std::vector<Breakpoint> to_remove;
+  for (const Breakpoint& b : env->breakpoints) {
+    if (b.GetMethod()->GetDeclaringClass() == klass) {
+      to_remove.push_back(b);
+    }
+  }
+  for (const Breakpoint& b : to_remove) {
+    auto it = env->breakpoints.find(b);
+    DCHECK(it != env->breakpoints.end());
+    env->breakpoints.erase(it);
+  }
+}
+
+jvmtiError BreakpointUtil::SetBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
+  ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
+  if (method == nullptr) {
+    return ERR(INVALID_METHODID);
+  }
+  // Need to get mutator_lock_ so we can find the interface version of any default methods.
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod();
+  if (location < 0 || static_cast<uint32_t>(location) >=
+      art_method->GetCodeItem()->insns_size_in_code_units_) {
+    return ERR(INVALID_LOCATION);
+  }
+  auto res_pair = env->breakpoints.insert(/* Breakpoint */ {art_method, location});
+  if (!res_pair.second) {
+    // Didn't get inserted because it's already present!
+    return ERR(DUPLICATE);
+  }
+  return OK;
+}
+
+jvmtiError BreakpointUtil::ClearBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
+  ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
+  if (method == nullptr) {
+    return ERR(INVALID_METHODID);
+  }
+  // Need to get mutator_lock_ so we can find the interface version of any default methods.
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  auto pos = env->breakpoints.find(
+      /* Breakpoint */ {art::jni::DecodeArtMethod(method)->GetCanonicalMethod(), location});
+  if (pos == env->breakpoints.end()) {
+    return ERR(NOT_FOUND);
+  }
+  env->breakpoints.erase(pos);
+  return OK;
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_breakpoint.h b/runtime/openjdkjvmti/ti_breakpoint.h
new file mode 100644
index 0000000..c3dbef7
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_breakpoint.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "base/mutex.h"
+
+namespace art {
+class ArtMethod;
+namespace mirror {
+class Class;
+}  // namespace mirror
+}  // namespace art
+
+namespace openjdkjvmti {
+
+struct ArtJvmTiEnv;
+
+class Breakpoint {
+ public:
+  Breakpoint(art::ArtMethod* m, jlocation loc);
+
+  // Get the hash code of this breakpoint.
+  size_t hash() const;
+
+  bool operator==(const Breakpoint& other) const {
+    return method_ == other.method_ && location_ == other.location_;
+  }
+
+  art::ArtMethod* GetMethod() const {
+    return method_;
+  }
+
+  jlocation GetLocation() const {
+    return location_;
+  }
+
+ private:
+  art::ArtMethod* method_;
+  jlocation location_;
+};
+
+class BreakpointUtil {
+ public:
+  static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
+  static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
+  // Used by class redefinition to remove breakpoints on redefined classes.
+  static void RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass)
+      REQUIRES(art::Locks::mutator_lock_);
+};
+
+}  // namespace openjdkjvmti
+
+namespace std {
+template<> struct hash<openjdkjvmti::Breakpoint> {
+  size_t operator()(const openjdkjvmti::Breakpoint& b) const {
+    return b.hash();
+  }
+};
+
+}  // namespace std
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 5422f48..debee91 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -64,6 +64,7 @@
 #include "object_lock.h"
 #include "runtime.h"
 #include "ScopedLocalRef.h"
+#include "ti_breakpoint.h"
 #include "ti_class_loader.h"
 #include "transform.h"
 #include "verifier/method_verifier.h"
@@ -380,7 +381,7 @@
   art::jit::ScopedJitSuspend suspend_jit;
   // Get shared mutator lock so we can lock all the classes.
   art::ScopedObjectAccess soa(self);
-  Redefiner r(runtime, self, error_msg);
+  Redefiner r(env, runtime, self, error_msg);
   for (const ArtClassDefinition& def : definitions) {
     // Only try to transform classes that have been modified.
     if (def.IsModified()) {
@@ -1200,6 +1201,10 @@
   return true;
 }
 
+void Redefiner::ClassRedefinition::UnregisterJvmtiBreakpoints() {
+  BreakpointUtil::RemoveBreakpointsInClass(driver_->env_, GetMirrorClass());
+}
+
 void Redefiner::ClassRedefinition::UnregisterBreakpoints() {
   DCHECK(art::Dbg::IsDebuggerActive());
   art::JDWP::JdwpState* state = art::Dbg::GetJdwpState();
@@ -1342,6 +1347,7 @@
     // TODO Rewrite so we don't do a stack walk for each and every class.
     redef.FindAndAllocateObsoleteMethods(klass);
     redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFile());
+    redef.UnregisterJvmtiBreakpoints();
   }
   RestoreObsoleteMethodMapsIfUnneeded(holder);
   // TODO We should check for if any of the redefined methods are intrinsic methods here and, if any
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index ec4a8b2..27d7c3d 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -199,6 +199,8 @@
     void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_);
 
     void UnregisterBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_);
+    // This should be done with all threads suspended.
+    void UnregisterJvmtiBreakpoints() REQUIRES(art::Locks::mutator_lock_);
 
    private:
     Redefiner* driver_;
@@ -208,6 +210,7 @@
     art::ArrayRef<const unsigned char> original_dex_file_;
   };
 
+  ArtJvmTiEnv* env_;
   jvmtiError result_;
   art::Runtime* runtime_;
   art::Thread* self_;
@@ -216,10 +219,12 @@
   // mirror::Class difficult and confusing.
   std::string* error_msg_;
 
-  Redefiner(art::Runtime* runtime,
+  Redefiner(ArtJvmTiEnv* env,
+            art::Runtime* runtime,
             art::Thread* self,
             std::string* error_msg)
-      : result_(ERR(INTERNAL)),
+      : env_(env),
+        result_(ERR(INTERNAL)),
         runtime_(runtime),
         self_(self),
         redefinitions_(),