Do native method bind in jvmti-stress

Extend the jvmti-stress test suite to intercept the NativeMethodBind
event for every method.

Also fix small issue where we tried to add a local reference to a null
thread in ti_method.cc if we had an event trigger before VMInit.

Test: ./test/testrunner/testrunner.py --host --jvmti-stress -j40
Bug: 37432636

Change-Id: I2d83fc460b18edf035ed7296b8e2b06cff3671e5
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index 2adabba..f7e5347 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -44,6 +44,7 @@
 #include "ScopedLocalRef.h"
 #include "thread-inl.h"
 #include "thread_list.h"
+#include "ti_phase.h"
 
 namespace openjdkjvmti {
 
@@ -54,12 +55,14 @@
       OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
     if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kNativeMethodBind)) {
       art::Thread* thread = art::Thread::Current();
+      art::JNIEnvExt* jnienv = thread->GetJniEnv();
       ScopedLocalRef<jthread> thread_jni(
-          thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer()));
+          jnienv, PhaseUtil::IsLivePhase() ? jnienv->AddLocalReference<jthread>(thread->GetPeer())
+                                           : nullptr);
       art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
       event_handler->DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(
           thread,
-          static_cast<JNIEnv*>(thread->GetJniEnv()),
+          static_cast<JNIEnv*>(jnienv),
           thread_jni.get(),
           art::jni::EncodeArtMethod(method),
           const_cast<void*>(cur_method),
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index fa49a35..e7d76dd 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -84,6 +84,48 @@
   return ReadIntoBuffer(data->out_temp_dex, dex);
 }
 
+static void doJvmtiMethodBind(jvmtiEnv* jvmtienv,
+                              JNIEnv* env,
+                              jthread thread,
+                              jmethodID m,
+                              void* address,
+                              /*out*/void** out_address) {
+  *out_address = address;
+  jvmtiThreadInfo info;
+  if (thread == nullptr) {
+    info.name = const_cast<char*>("<NULLPTR>");
+  } else if (jvmtienv->GetThreadInfo(thread, &info) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to get thread info!";
+    return;
+  }
+  char *fname, *fsig, *fgen;
+  char *cname, *cgen;
+  jclass klass = nullptr;
+  if (jvmtienv->GetMethodDeclaringClass(m, &klass) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to get method declaring class!";
+    return;
+  }
+  if (jvmtienv->GetMethodName(m, &fname, &fsig, &fgen) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to get method name!";
+    env->DeleteLocalRef(klass);
+    return;
+  }
+  if (jvmtienv->GetClassSignature(klass, &cname, &cgen) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to get class name!";
+    env->DeleteLocalRef(klass);
+    return;
+  }
+  LOG(INFO) << "Loading native method \"" << cname << "->" << fname << fsig << "\". Thread is "
+            << info.name;
+  jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cname));
+  jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(cgen));
+  jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fname));
+  jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fsig));
+  jvmtienv->Deallocate(reinterpret_cast<unsigned char*>(fgen));
+  env->DeleteLocalRef(klass);
+  return;
+}
+
 // The hook we are using.
 void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
                                          JNIEnv* jni_env ATTRIBUTE_UNUSED,
@@ -187,12 +229,19 @@
   jvmtiEventCallbacks cb;
   memset(&cb, 0, sizeof(cb));
   cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp;
+  cb.NativeMethodBind = doJvmtiMethodBind;
   cb.VMInit = EnsureVMClassloaderInitializedCB;
   if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
     LOG(ERROR) << "Unable to set class file load hook cb!";
     return 1;
   }
   if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+                                      JVMTI_EVENT_NATIVE_METHOD_BIND,
+                                      nullptr) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to enable JVMTI_EVENT_NATIVE_METHOD_BIND event!";
+    return 1;
+  }
+  if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                       JVMTI_EVENT_VM_INIT,
                                       nullptr) != JVMTI_ERROR_NONE) {
     LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";