ART: Refactor JVMTI run tests

In preparation for code deduplication with CTS.

Bug: 32072923
Test: m
Change-Id: Ibbe005c94252bd29eae7f88aad301b0b20ddb80d
(cherry picked from commit 3f46c96568bef650ba6d9ce6ac8835d30877f243)
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc
index cbd7686..00776ca 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -24,8 +24,9 @@
 #include "base/macros.h"
 #include "jvmti.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test901HelloTi {
@@ -148,14 +149,14 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jint iflag, jboolean val) {
   jvmtiVerboseFlag flag = static_cast<jvmtiVerboseFlag>(iflag);
   jvmtiError result = jvmti_env->SetVerboseFlag(flag, val);
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkLivePhase(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jvmtiPhase current_phase;
   jvmtiError phase_result = jvmti_env->GetPhase(&current_phase);
-  if (JvmtiErrorToException(env, phase_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, phase_result)) {
     return JNI_FALSE;
   }
   return (current_phase == JVMTI_PHASE_LIVE) ? JNI_TRUE : JNI_FALSE;
diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc
index b85ed48..7f079a2 100644
--- a/test/903-hello-tagging/tagging.cc
+++ b/test/903-hello-tagging/tagging.cc
@@ -26,36 +26,25 @@
 #include "art_method-inl.h"
 #include "base/logging.h"
 #include "jvmti.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
 #include "utils.h"
 
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
 namespace art {
 namespace Test903HelloTagging {
 
-extern "C" JNIEXPORT void JNICALL Java_Main_setTag(JNIEnv* env ATTRIBUTE_UNUSED,
-                                                   jclass,
-                                                   jobject obj,
-                                                   jlong tag) {
+extern "C" JNIEXPORT void JNICALL Java_Main_setTag(JNIEnv* env, jclass, jobject obj, jlong tag) {
   jvmtiError ret = jvmti_env->SetTag(obj, tag);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error setting tag: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
-extern "C" JNIEXPORT jlong JNICALL Java_Main_getTag(JNIEnv* env ATTRIBUTE_UNUSED,
-                                                    jclass,
-                                                    jobject obj) {
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getTag(JNIEnv* env, jclass, jobject obj) {
   jlong tag = 0;
   jvmtiError ret = jvmti_env->GetTag(obj, &tag);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error getting tag: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
+    return 0;
   }
   return tag;
 }
@@ -86,11 +75,7 @@
                                                  &result_count,
                                                  result_object_array_ptr,
                                                  result_tag_array_ptr);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Failure running GetLoadedClasses: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
 
@@ -197,4 +182,3 @@
 
 }  // namespace Test903HelloTagging
 }  // namespace art
-
diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc
index cc6f681..303f954 100644
--- a/test/904-object-allocation/tracking.cc
+++ b/test/904-object-allocation/tracking.cc
@@ -24,10 +24,12 @@
 #include "jvmti.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
 #include "utils.h"
 
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
 namespace art {
 namespace Test904ObjectAllocation {
 
@@ -57,21 +59,16 @@
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_setupObjectAllocCallback(
-    JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
   jvmtiEventCallbacks callbacks;
   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
   callbacks.VMObjectAlloc = enable ? ObjectAllocated : nullptr;
 
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error setting callbacks: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_enableAllocationTracking(JNIEnv* env ATTRIBUTE_UNUSED,
+extern "C" JNIEXPORT void JNICALL Java_Main_enableAllocationTracking(JNIEnv* env,
                                                                      jclass,
                                                                      jthread thread,
                                                                      jboolean enable) {
@@ -79,14 +76,8 @@
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
       JVMTI_EVENT_VM_OBJECT_ALLOC,
       thread);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error enabling/disabling allocation tracking: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 }  // namespace Test904ObjectAllocation
 }  // namespace art
-
diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc
index c489309..68ce38d 100644
--- a/test/905-object-free/tracking_free.cc
+++ b/test/905-object-free/tracking_free.cc
@@ -24,10 +24,12 @@
 #include "jvmti.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
 #include "utils.h"
 
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
 namespace art {
 namespace Test905ObjectFree {
 
@@ -46,52 +48,39 @@
   collected_tags2.push_back(tag);
 }
 
-static void setupObjectFreeCallback(jvmtiEnv* env, jvmtiEventObjectFree callback) {
+static void setupObjectFreeCallback(JNIEnv* env, jvmtiEnv* jenv, jvmtiEventObjectFree callback) {
   jvmtiEventCallbacks callbacks;
   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
   callbacks.ObjectFree = callback;
-  jvmtiError ret = env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    env->GetErrorName(ret, &err);
-    printf("Error setting callbacks: %s\n", err);
-    env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  jvmtiError ret = jenv->SetEventCallbacks(&callbacks, sizeof(callbacks));
+  JvmtiErrorToException(env, jenv, ret);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_setupObjectFreeCallback(
     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
-  setupObjectFreeCallback(jvmti_env, ObjectFree1);
+  setupObjectFreeCallback(env, jvmti_env, ObjectFree1);
   JavaVM* jvm = nullptr;
   env->GetJavaVM(&jvm);
   CHECK_EQ(jvm->GetEnv(reinterpret_cast<void**>(&jvmti_env2), JVMTI_VERSION_1_2), 0);
   SetAllCapabilities(jvmti_env2);
-  setupObjectFreeCallback(jvmti_env2, ObjectFree2);
+  setupObjectFreeCallback(env, jvmti_env2, ObjectFree2);
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_enableFreeTracking(JNIEnv* env ATTRIBUTE_UNUSED,
+extern "C" JNIEXPORT void JNICALL Java_Main_enableFreeTracking(JNIEnv* env,
                                                                jclass klass ATTRIBUTE_UNUSED,
                                                                jboolean enable) {
   jvmtiError ret = jvmti_env->SetEventNotificationMode(
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
       JVMTI_EVENT_OBJECT_FREE,
       nullptr);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error enabling/disabling object-free callbacks: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
+    return;
   }
   ret = jvmti_env2->SetEventNotificationMode(
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
       JVMTI_EVENT_OBJECT_FREE,
       nullptr);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env2->GetErrorName(ret, &err);
-    printf("Error enabling/disabling object-free callbacks: %s\n", err);
-    jvmti_env2->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 extern "C" JNIEXPORT jlongArray JNICALL Java_Main_getCollectedTags(JNIEnv* env,
@@ -114,7 +103,7 @@
                                                     jobject obj,
                                                     jlong tag) {
   jvmtiError ret = jvmti_env2->SetTag(obj, tag);
-  JvmtiErrorToException(env, ret);
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 }  // namespace Test905ObjectFree
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index f2532de..74cb1e9 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -28,10 +28,12 @@
 #include "jni.h"
 #include "jvmti.h"
 #include "ScopedPrimitiveArray.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
 #include "utf.h"
 
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
 namespace art {
 namespace Test906IterateHeap {
 
@@ -52,7 +54,7 @@
   return config->Handle(class_tag, size, tag_ptr, length);
 }
 
-static bool Run(jint heap_filter, jclass klass_filter, IterationConfig* config) {
+static bool Run(JNIEnv* env, jint heap_filter, jclass klass_filter, IterationConfig* config) {
   jvmtiHeapCallbacks callbacks;
   memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
   callbacks.heap_iteration_callback = HeapIterationCallback;
@@ -61,17 +63,13 @@
                                                  klass_filter,
                                                  &callbacks,
                                                  config);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Failure running IterateThroughHeap: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return false;
   }
   return true;
 }
 
-extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapCount(JNIEnv* env ATTRIBUTE_UNUSED,
+extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapCount(JNIEnv* env,
                                                                     jclass klass ATTRIBUTE_UNUSED,
                                                                     jint heap_filter,
                                                                     jclass klass_filter,
@@ -99,7 +97,7 @@
   };
 
   CountIterationConfig config(0, stop_after);
-  Run(heap_filter, klass_filter, &config);
+  Run(env, heap_filter, klass_filter, &config);
 
   if (config.counter > config.stop_after) {
     printf("Error: more objects visited than signaled.");
@@ -135,7 +133,7 @@
   };
 
   DataIterationConfig config;
-  if (!Run(heap_filter, klass_filter, &config)) {
+  if (!Run(env, heap_filter, klass_filter, &config)) {
     return -1;
   }
 
@@ -154,7 +152,7 @@
   return static_cast<jint>(config.class_tags_.size());
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_iterateThroughHeapAdd(JNIEnv* env ATTRIBUTE_UNUSED,
+extern "C" JNIEXPORT void JNICALL Java_Main_iterateThroughHeapAdd(JNIEnv* env,
                                                                   jclass klass ATTRIBUTE_UNUSED,
                                                                   jint heap_filter,
                                                                   jclass klass_filter) {
@@ -175,7 +173,7 @@
   };
 
   AddIterationConfig config;
-  Run(heap_filter, klass_filter, &config);
+  Run(env, heap_filter, klass_filter, &config);
 }
 
 extern "C" JNIEXPORT jstring JNICALL Java_Main_iterateThroughHeapString(
@@ -228,7 +226,7 @@
 
   FindStringCallbacks fsc(tag);
   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fsc);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
   return env->NewStringUTF(fsc.data.c_str());
@@ -316,7 +314,7 @@
 
   FindArrayCallbacks fac(tag);
   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fac);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
   return env->NewStringUTF(fac.data.c_str());
@@ -403,7 +401,7 @@
 
   FindFieldCallbacks ffc(tag);
   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &ffc);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
   return env->NewStringUTF(ffc.data.c_str());
diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc
index 48ce2e2..1b973bf 100644
--- a/test/907-get-loaded-classes/get_loaded_classes.cc
+++ b/test/907-get-loaded-classes/get_loaded_classes.cc
@@ -25,8 +25,9 @@
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test907GetLoadedClasses {
diff --git a/test/908-gc-start-finish/gc_callbacks.cc b/test/908-gc-start-finish/gc_callbacks.cc
index 45148f8..4b9a23c 100644
--- a/test/908-gc-start-finish/gc_callbacks.cc
+++ b/test/908-gc-start-finish/gc_callbacks.cc
@@ -20,8 +20,10 @@
 #include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test908GcStartFinish {
@@ -45,7 +47,7 @@
   callbacks.GarbageCollectionStart = GarbageCollectionStart;
 
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  JvmtiErrorToException(env, ret);
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_enableGcTracking(JNIEnv* env,
@@ -55,14 +57,14 @@
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
       JVMTI_EVENT_GARBAGE_COLLECTION_START,
       nullptr);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
   ret = jvmti_env->SetEventNotificationMode(
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
       JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
       nullptr);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
 }
diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc
index fdc4cdb..429076c 100644
--- a/test/910-methods/methods.cc
+++ b/test/910-methods/methods.cc
@@ -21,8 +21,10 @@
 #include "jvmti.h"
 #include "ScopedLocalRef.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test910Methods {
@@ -35,11 +37,7 @@
   char* sig;
   char* gen;
   jvmtiError result = jvmti_env->GetMethodName(id, &name, &sig, &gen);
-  if (result != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(result, &err);
-    printf("Failure running GetMethodName: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -67,11 +65,7 @@
 
   // Also run GetMethodName with all parameter pointers null to check for segfaults.
   jvmtiError result2 = jvmti_env->GetMethodName(id, nullptr, nullptr, nullptr);
-  if (result2 != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(result2, &err);
-    printf("Failure running GetMethodName(null, null, null): %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, result2)) {
     return nullptr;
   }
 
@@ -84,11 +78,7 @@
 
   jclass declaring_class;
   jvmtiError result = jvmti_env->GetMethodDeclaringClass(id, &declaring_class);
-  if (result != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(result, &err);
-    printf("Failure running GetMethodDeclaringClass: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -101,11 +91,7 @@
 
   jint modifiers;
   jvmtiError result = jvmti_env->GetMethodModifiers(id, &modifiers);
-  if (result != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(result, &err);
-    printf("Failure running GetMethodModifiers: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return 0;
   }
 
@@ -118,7 +104,7 @@
 
   jint max_locals;
   jvmtiError result = jvmti_env->GetMaxLocals(id, &max_locals);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
 
@@ -131,7 +117,7 @@
 
   jint arguments;
   jvmtiError result = jvmti_env->GetArgumentsSize(id, &arguments);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
 
@@ -145,7 +131,7 @@
   jlong start;
   jlong end;
   jvmtiError result = jvmti_env->GetMethodLocation(id, &start, &end);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
 
@@ -159,7 +145,7 @@
   jlong start;
   jlong end;
   jvmtiError result = jvmti_env->GetMethodLocation(id, &start, &end);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
 
@@ -172,7 +158,7 @@
 
   jboolean is_native;
   jvmtiError result = jvmti_env->IsMethodNative(id, &is_native);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return JNI_FALSE;
   }
 
@@ -185,7 +171,7 @@
 
   jboolean is_obsolete;
   jvmtiError result = jvmti_env->IsMethodObsolete(id, &is_obsolete);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return JNI_FALSE;
   }
 
@@ -198,7 +184,7 @@
 
   jboolean is_synthetic;
   jvmtiError result = jvmti_env->IsMethodSynthetic(id, &is_synthetic);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return JNI_FALSE;
   }
 
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index 5a3a311..49cbb7e 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -26,8 +26,12 @@
 #include "jni.h"
 #include "jvmti.h"
 #include "ScopedLocalRef.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test911GetStackTrace {
@@ -68,7 +72,7 @@
     char* gen;
     {
       jvmtiError result2 = jvmti_env->GetMethodName(frames[method_index].method, &name, &sig, &gen);
-      if (JvmtiErrorToException(env, result2)) {
+      if (JvmtiErrorToException(env, jvmti_env, result2)) {
         return nullptr;
       }
     }
@@ -83,10 +87,7 @@
         // Accept absent info and native method errors.
         if (line_result != JVMTI_ERROR_ABSENT_INFORMATION &&
             line_result != JVMTI_ERROR_NATIVE_METHOD) {
-          char* err;
-          jvmti_env->GetErrorName(line_result, &err);
-          printf("Failure running GetLineNumberTable: %s\n", err);
-          jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+          JvmtiErrorToException(env, jvmti_env, line_result);
           return nullptr;
         }
         line_number_table = nullptr;
@@ -139,7 +140,7 @@
   jint count;
   {
     jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count);
-    if (JvmtiErrorToException(env, result)) {
+    if (JvmtiErrorToException(env, jvmti_env, result)) {
       return nullptr;
     }
   }
@@ -153,7 +154,7 @@
   jvmtiStackInfo* stack_infos;
   {
     jvmtiError result = jvmti_env->GetAllStackTraces(max, &stack_infos, &thread_count);
-    if (JvmtiErrorToException(env, result)) {
+    if (JvmtiErrorToException(env, jvmti_env, result)) {
       return nullptr;
     }
   }
@@ -189,7 +190,7 @@
                                                             threads.get(),
                                                             max,
                                                             &stack_infos);
-    if (JvmtiErrorToException(env, result)) {
+    if (JvmtiErrorToException(env, jvmti_env, result)) {
       return nullptr;
     }
   }
@@ -215,7 +216,7 @@
     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread) {
   jint count;
   jvmtiError result = jvmti_env->GetFrameCount(thread, &count);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
   return count;
@@ -227,7 +228,7 @@
   jlocation location;
 
   jvmtiError result = jvmti_env->GetFrameLocation(thread, depth, &method, &location);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -237,12 +238,12 @@
       {
         jclass decl_class;
         jvmtiError class_result = jvmti_env->GetMethodDeclaringClass(method, &decl_class);
-        if (JvmtiErrorToException(env, class_result)) {
+        if (JvmtiErrorToException(env, jvmti_env, class_result)) {
           return nullptr;
         }
         jint modifiers;
         jvmtiError mod_result = jvmti_env->GetMethodModifiers(method, &modifiers);
-        if (JvmtiErrorToException(env, mod_result)) {
+        if (JvmtiErrorToException(env, jvmti_env, mod_result)) {
           return nullptr;
         }
         constexpr jint kStatic = 0x8;
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 5bd34f6..4d84e39 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -27,8 +27,10 @@
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test912Classes {
@@ -233,7 +235,7 @@
   jint count = 0;
   jclass* classes = nullptr;
   jvmtiError result = jvmti_env->GetClassLoaderClasses(jclassloader, &count, &classes);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -251,7 +253,7 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
   jint major, minor;
   jvmtiError result = jvmti_env->GetClassVersionNumbers(klass, &minor, &major);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -270,7 +272,7 @@
   jvmtiError result = jenv->GetClassSignature(klass, &name, nullptr);
   if (result != JVMTI_ERROR_NONE) {
     if (jni_env != nullptr) {
-      JvmtiErrorToException(jni_env, result);
+      JvmtiErrorToException(jni_env, jenv, result);
     } else {
       printf("Failed to get class signature.\n");
     }
@@ -291,13 +293,13 @@
     jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
                                                          JVMTI_EVENT_CLASS_LOAD,
                                                          nullptr);
-    if (JvmtiErrorToException(env, ret)) {
+    if (JvmtiErrorToException(env, jvmti_env, ret)) {
       return;
     }
     ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
                                               JVMTI_EVENT_CLASS_PREPARE,
                                               nullptr);
-    JvmtiErrorToException(env, ret);
+    JvmtiErrorToException(env, jvmti_env, ret);
     return;
   }
 
@@ -306,20 +308,20 @@
   callbacks.ClassLoad = class_load;
   callbacks.ClassPrepare = class_prepare;
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
 
   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
                                             JVMTI_EVENT_CLASS_LOAD,
                                             nullptr);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
                                             JVMTI_EVENT_CLASS_PREPARE,
                                             nullptr);
-  JvmtiErrorToException(env, ret);
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 class ClassLoadPreparePrinter {
@@ -364,7 +366,7 @@
     jvmtiError result = jenv->GetThreadInfo(thread, &info);
     if (result != JVMTI_ERROR_NONE) {
       if (jni_env != nullptr) {
-        JvmtiErrorToException(jni_env, result);
+        JvmtiErrorToException(jni_env, jenv, result);
       } else {
         printf("Failed to get thread name.\n");
       }
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 66fc7be..999467f 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -34,8 +34,10 @@
 #include "thread-inl.h"
 #include "thread_list.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test913Heaps {
@@ -550,7 +552,7 @@
 
   FindStringCallbacks fsc;
   jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, initial_object, &callbacks, &fsc);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
 
@@ -648,7 +650,7 @@
 
   FindArrayCallbacks fac;
   jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, initial_object, &callbacks, &fac);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
   return env->NewStringUTF(fac.data.c_str());
@@ -738,7 +740,7 @@
 
   FindFieldCallbacks ffc;
   jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, initial_object, &callbacks, &ffc);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
   return env->NewStringUTF(ffc.data.c_str());
diff --git a/test/918-fields/fields.cc b/test/918-fields/fields.cc
index c659126..0c019e3 100644
--- a/test/918-fields/fields.cc
+++ b/test/918-fields/fields.cc
@@ -21,8 +21,9 @@
 #include "jvmti.h"
 #include "ScopedLocalRef.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test918Fields {
diff --git a/test/920-objects/objects.cc b/test/920-objects/objects.cc
index ad1431e..1dfb516 100644
--- a/test/920-objects/objects.cc
+++ b/test/920-objects/objects.cc
@@ -21,8 +21,8 @@
 #include "jvmti.h"
 #include "ScopedLocalRef.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "test_env.h"
 
 namespace art {
 namespace Test920Objects {
diff --git a/test/922-properties/properties.cc b/test/922-properties/properties.cc
index 3fd274e..948da6a 100644
--- a/test/922-properties/properties.cc
+++ b/test/922-properties/properties.cc
@@ -21,8 +21,10 @@
 #include "jvmti.h"
 #include "ScopedUtfChars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test922Properties {
@@ -32,7 +34,7 @@
   jint count;
   char** properties;
   jvmtiError result = jvmti_env->GetSystemProperties(&count, &properties);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -61,7 +63,7 @@
 
   char* value = nullptr;
   jvmtiError result = jvmti_env->GetSystemProperty(string.c_str(), &value);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -84,7 +86,7 @@
   }
 
   jvmtiError result = jvmti_env->SetSystemProperty(key_string.c_str(), value_string.c_str());
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return;
   }
 }
diff --git a/test/923-monitors/monitors.cc b/test/923-monitors/monitors.cc
index 131fc6a..60d5b5a 100644
--- a/test/923-monitors/monitors.cc
+++ b/test/923-monitors/monitors.cc
@@ -21,8 +21,9 @@
 #include "jvmti.h"
 #include "ScopedUtfChars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test923Monitors {
@@ -40,7 +41,7 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jrawMonitorID id;
   jvmtiError result = jvmti_env->CreateRawMonitor("dummy", &id);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return 0;
   }
   return MonitorToLong(id);
@@ -49,37 +50,37 @@
 extern "C" JNIEXPORT void JNICALL Java_Main_destroyRawMonitor(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
   jvmtiError result = jvmti_env->DestroyRawMonitor(LongToMonitor(l));
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorEnter(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
   jvmtiError result = jvmti_env->RawMonitorEnter(LongToMonitor(l));
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorExit(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
   jvmtiError result = jvmti_env->RawMonitorExit(LongToMonitor(l));
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorWait(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l, jlong millis) {
   jvmtiError result = jvmti_env->RawMonitorWait(LongToMonitor(l), millis);
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorNotify(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
   jvmtiError result = jvmti_env->RawMonitorNotify(LongToMonitor(l));
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorNotifyAll(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
   jvmtiError result = jvmti_env->RawMonitorNotifyAll(LongToMonitor(l));
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 }  // namespace Test923Monitors
diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc
index 14ea5af..bb040bd 100644
--- a/test/924-threads/threads.cc
+++ b/test/924-threads/threads.cc
@@ -23,8 +23,10 @@
 #include "jvmti.h"
 #include "ScopedLocalRef.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test924Threads {
@@ -36,7 +38,7 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jthread thread = nullptr;
   jvmtiError result = jvmti_env->GetCurrentThread(&thread);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
   return thread;
@@ -48,7 +50,7 @@
   memset(&info, 0, sizeof(jvmtiThreadInfo));
 
   jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -94,7 +96,7 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
   jint state;
   jvmtiError result = jvmti_env->GetThreadState(thread, &state);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return 0;
   }
   return state;
@@ -106,7 +108,7 @@
   jthread* threads;
 
   jvmtiError result = jvmti_env->GetAllThreads(&thread_count, &threads);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -124,7 +126,7 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
   void* tls;
   jvmtiError result = jvmti_env->GetThreadLocalStorage(thread, &tls);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return 0;
   }
   return static_cast<jlong>(reinterpret_cast<uintptr_t>(tls));
@@ -134,7 +136,7 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread, jlong val) {
   const void* tls = reinterpret_cast<void*>(static_cast<uintptr_t>(val));
   jvmtiError result = jvmti_env->SetThreadLocalStorage(thread, tls);
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 static void JNICALL ThreadEvent(jvmtiEnv* jvmti_env,
@@ -172,13 +174,13 @@
     jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
                                                          JVMTI_EVENT_THREAD_START,
                                                          nullptr);
-    if (JvmtiErrorToException(env, ret)) {
+    if (JvmtiErrorToException(env, jvmti_env, ret)) {
       return;
     }
     ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
                                               JVMTI_EVENT_THREAD_END,
                                               nullptr);
-    JvmtiErrorToException(env, ret);
+    JvmtiErrorToException(env, jvmti_env, ret);
     return;
   }
 
@@ -187,20 +189,20 @@
   callbacks.ThreadStart = ThreadStart;
   callbacks.ThreadEnd = ThreadEnd;
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
 
   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
                                             JVMTI_EVENT_THREAD_START,
                                             nullptr);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
                                             JVMTI_EVENT_THREAD_END,
                                             nullptr);
-  JvmtiErrorToException(env, ret);
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 }  // namespace Test924Threads
diff --git a/test/925-threadgroups/threadgroups.cc b/test/925-threadgroups/threadgroups.cc
index 2feaab0..1cd93be 100644
--- a/test/925-threadgroups/threadgroups.cc
+++ b/test/925-threadgroups/threadgroups.cc
@@ -23,8 +23,10 @@
 #include "jvmti.h"
 #include "ScopedLocalRef.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test925ThreadGroups {
@@ -38,7 +40,7 @@
   jthreadGroup* groups;
   jint group_count;
   jvmtiError result = jvmti_env->GetTopThreadGroups(&group_count, &groups);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -56,7 +58,7 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) {
   jvmtiThreadGroupInfo info;
   jvmtiError result = jvmti_env->GetThreadGroupInfo(group, &info);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -96,7 +98,7 @@
                                                         &threads,
                                                         &threadgroup_count,
                                                         &groups);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
diff --git a/test/927-timers/timers.cc b/test/927-timers/timers.cc
index 7b1d5c3..a67f5b4 100644
--- a/test/927-timers/timers.cc
+++ b/test/927-timers/timers.cc
@@ -22,8 +22,10 @@
 #include "jni.h"
 #include "jvmti.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test926Timers {
@@ -32,7 +34,7 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jint count;
   jvmtiError result = jvmti_env->GetAvailableProcessors(&count);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
   return count;
@@ -42,7 +44,7 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jlong time;
   jvmtiError result = jvmti_env->GetTime(&time);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
   return time;
@@ -52,7 +54,7 @@
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jvmtiTimerInfo info;
   jvmtiError result = jvmti_env->GetTimerInfo(&info);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
diff --git a/test/928-jni-table/jni_table.cc b/test/928-jni-table/jni_table.cc
index b5c0efd..3f4a93e 100644
--- a/test/928-jni-table/jni_table.cc
+++ b/test/928-jni-table/jni_table.cc
@@ -22,8 +22,9 @@
 #include "base/logging.h"
 #include "base/macros.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test927JNITable {
@@ -42,14 +43,14 @@
     JNIEnv* env, jclass klass) {
   // Get the current table, as the delegate.
   jvmtiError getorig_result = jvmti_env->GetJNIFunctionTable(&gOriginalEnv);
-  if (JvmtiErrorToException(env, getorig_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, getorig_result)) {
     return;
   }
 
   // Get the current table, as the override we'll install.
   JNINativeInterface* env_override;
   jvmtiError getoverride_result = jvmti_env->GetJNIFunctionTable(&env_override);
-  if (JvmtiErrorToException(env, getoverride_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, getoverride_result)) {
     return;
   }
 
@@ -58,7 +59,7 @@
 
   // Install the override.
   jvmtiError setoverride_result = jvmti_env->SetJNIFunctionTable(env_override);
-  if (JvmtiErrorToException(env, setoverride_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, setoverride_result)) {
     return;
   }
 
@@ -68,7 +69,7 @@
 
   // Install the "original." There is no real reset.
   jvmtiError setoverride2_result = jvmti_env->SetJNIFunctionTable(gOriginalEnv);
-  if (JvmtiErrorToException(env, setoverride2_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, setoverride2_result)) {
     return;
   }
 
diff --git a/test/929-search/search.cc b/test/929-search/search.cc
index ad7a053..bed4dfe 100644
--- a/test/929-search/search.cc
+++ b/test/929-search/search.cc
@@ -23,8 +23,9 @@
 #include "jvmti.h"
 #include "ScopedUtfChars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test929Search {
@@ -36,7 +37,7 @@
     return;
   }
   jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(utf.c_str());
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_addToSystemClassLoader(
@@ -46,7 +47,7 @@
     return;
   }
   jvmtiError result = jvmti_env->AddToSystemClassLoaderSearch(utf.c_str());
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 }  // namespace Test929Search
diff --git a/test/931-agent-thread/agent_thread.cc b/test/931-agent-thread/agent_thread.cc
index 2e6bd46..3ec8793 100644
--- a/test/931-agent-thread/agent_thread.cc
+++ b/test/931-agent-thread/agent_thread.cc
@@ -27,8 +27,9 @@
 #include "thread-inl.h"
 #include "well_known_classes.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test930AgentThread {
@@ -53,13 +54,13 @@
   // This thread is not the main thread.
   jthread this_thread;
   jvmtiError this_thread_result = jenv->GetCurrentThread(&this_thread);
-  CHECK(!JvmtiErrorToException(env, this_thread_result));
+  CHECK(!JvmtiErrorToException(env, jenv, this_thread_result));
   CHECK(!env->IsSameObject(this_thread, data->main_thread));
 
   // The thread is a daemon.
   jvmtiThreadInfo info;
   jvmtiError info_result = jenv->GetThreadInfo(this_thread, &info);
-  CHECK(!JvmtiErrorToException(env, info_result));
+  CHECK(!JvmtiErrorToException(env, jenv, info_result));
   CHECK(info.is_daemon);
 
   // The thread has the requested priority.
@@ -70,7 +71,7 @@
   jint thread_count;
   jthread* threads;
   jvmtiError threads_result = jenv->GetAllThreads(&thread_count, &threads);
-  CHECK(!JvmtiErrorToException(env, threads_result));
+  CHECK(!JvmtiErrorToException(env, jenv, threads_result));
   bool found = false;
   for (jint i = 0; i != thread_count; ++i) {
     if (env->IsSameObject(threads[i], this_thread)) {
@@ -111,7 +112,7 @@
 
   jthread main_thread;
   jvmtiError main_thread_result = jvmti_env->GetCurrentThread(&main_thread);
-  if (JvmtiErrorToException(env, main_thread_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, main_thread_result)) {
     return;
   }
 
@@ -121,7 +122,7 @@
   data.priority = JVMTI_THREAD_MIN_PRIORITY;
 
   jvmtiError result = jvmti_env->RunAgentThread(thread.get(), AgentMain, &data, data.priority);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return;
   }
 
@@ -133,7 +134,7 @@
     NanoSleep(1000 * 1000);
     jint thread_state;
     jvmtiError state_result = jvmti_env->GetThreadState(thread.get(), &thread_state);
-    if (JvmtiErrorToException(env, state_result)) {
+    if (JvmtiErrorToException(env, jvmti_env, state_result)) {
       return;
     }
     if (thread_state == 0 ||                                    // Was never alive.
diff --git a/test/933-misc-events/misc_events.cc b/test/933-misc-events/misc_events.cc
index 7043350..7b6c64d 100644
--- a/test/933-misc-events/misc_events.cc
+++ b/test/933-misc-events/misc_events.cc
@@ -23,8 +23,9 @@
 #include "jni.h"
 #include "jvmti.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test933MiscEvents {
@@ -42,14 +43,14 @@
   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
   callbacks.DataDumpRequest = DumpRequestCallback;
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
 
   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
                                             JVMTI_EVENT_DATA_DUMP_REQUEST,
                                             nullptr);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
 
@@ -65,7 +66,7 @@
   }
 
   ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_DATA_DUMP_REQUEST, nullptr);
-  JvmtiErrorToException(env, ret);
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 }  // namespace Test933MiscEvents
diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc
index 3b19ca5..72987eb 100644
--- a/test/936-search-onload/search_onload.cc
+++ b/test/936-search-onload/search_onload.cc
@@ -25,8 +25,9 @@
 #include "jvmti.h"
 #include "ScopedUtfChars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test936SearchOnload {
diff --git a/test/944-transform-classloaders/classloader.cc b/test/944-transform-classloaders/classloader.cc
index 7cb3c08..f46763c 100644
--- a/test/944-transform-classloaders/classloader.cc
+++ b/test/944-transform-classloaders/classloader.cc
@@ -20,8 +20,8 @@
 #include "mirror/class-inl.h"
 #include "ScopedLocalRef.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "test_env.h"
 
 namespace art {
 namespace Test944TransformClassloaders {
diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc
index 442836b..b9303dd 100644
--- a/test/945-obsolete-native/obsolete_native.cc
+++ b/test/945-obsolete-native/obsolete_native.cc
@@ -26,8 +26,10 @@
 #include "jni.h"
 #include "jvmti.h"
 #include "ScopedLocalRef.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test945ObsoleteNative {
diff --git a/test/980-redefine-object/redefine_object.cc b/test/980-redefine-object/redefine_object.cc
index daae087..6c8c4bd 100644
--- a/test/980-redefine-object/redefine_object.cc
+++ b/test/980-redefine-object/redefine_object.cc
@@ -24,8 +24,10 @@
 #include "jvmti.h"
 #include "ScopedUtfChars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test980RedefineObjects {
@@ -39,9 +41,11 @@
     JNIEnv* env, jclass TestWatcherClass ATTRIBUTE_UNUSED, jobject constructed) {
   char* sig = nullptr;
   char* generic_sig = nullptr;
-  if (JvmtiErrorToException(env, jvmti_env->GetClassSignature(env->GetObjectClass(constructed),
-                                                              &sig,
-                                                              &generic_sig))) {
+  if (JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->GetClassSignature(env->GetObjectClass(constructed),
+                                                         &sig,
+                                                         &generic_sig))) {
     // Exception.
     return;
   }
diff --git a/test/983-source-transform-verify/source_transform.cc b/test/983-source-transform-verify/source_transform.cc
index e8b7e13..3ef3c7c 100644
--- a/test/983-source-transform-verify/source_transform.cc
+++ b/test/983-source-transform-verify/source_transform.cc
@@ -37,8 +37,9 @@
 #include "thread-inl.h"
 #include "thread_list.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test983SourceTransformVerify {
diff --git a/test/Android.bp b/test/Android.bp
index ec40acf..2e8f5bb 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -249,6 +249,9 @@
         "common/runtime_state.cc",
         "ti-agent/common_load.cc",
         "ti-agent/common_helper.cc",
+        "ti-agent/jni_binder.cc",
+        "ti-agent/jvmti_helper.cc",
+        "ti-agent/test_env.cc",
         "901-hello-ti-agent/basics.cc",
         "903-hello-tagging/tagging.cc",
         "904-object-allocation/tracking.cc",
@@ -282,6 +285,7 @@
         "libbase",
     ],
     header_libs: ["libopenjdkjvmti_headers"],
+    include_dirs: ["art/test/ti-agent"],
 }
 
 art_cc_test_library {
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index 6316a9c..ab5dbcc 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "ti-agent/common_helper.h"
+#include "common_helper.h"
 
 #include <dlfcn.h>
 #include <stdio.h>
@@ -27,44 +27,15 @@
 #include "jni_internal.h"
 #include "jvmti.h"
 #include "scoped_thread_state_change-inl.h"
-#include "ScopedLocalRef.h"
 #include "stack.h"
-#include "ti-agent/common_load.h"
 #include "utils.h"
 
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
 namespace art {
-bool RuntimeIsJVM;
-
-bool IsJVM() {
-  return RuntimeIsJVM;
-}
-
-void SetAllCapabilities(jvmtiEnv* env) {
-  jvmtiCapabilities caps;
-  env->GetPotentialCapabilities(&caps);
-  env->AddCapabilities(&caps);
-}
-
-bool JvmtiErrorToException(JNIEnv* env, jvmtiError error) {
-  if (error == JVMTI_ERROR_NONE) {
-    return false;
-  }
-
-  ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
-  if (rt_exception.get() == nullptr) {
-    // CNFE should be pending.
-    return true;
-  }
-
-  char* err;
-  jvmti_env->GetErrorName(error, &err);
-
-  env->ThrowNew(rt_exception.get(), err);
-
-  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  return true;
-}
-
 
 template <bool is_redefine>
 static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
@@ -303,7 +274,7 @@
                                                        JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
                                                        nullptr);
   if (res != JVMTI_ERROR_NONE) {
-    JvmtiErrorToException(env, res);
+    JvmtiErrorToException(env, jvmti_env, res);
   }
 }
 
@@ -412,140 +383,4 @@
 
 }  // namespace common_transform
 
-static void BindMethod(jvmtiEnv* jenv,
-                       JNIEnv* env,
-                       jclass klass,
-                       jmethodID method) {
-  char* name;
-  char* signature;
-  jvmtiError name_result = jenv->GetMethodName(method, &name, &signature, nullptr);
-  if (name_result != JVMTI_ERROR_NONE) {
-    LOG(FATAL) << "Could not get methods";
-  }
-
-  std::string names[2];
-  if (IsJVM()) {
-    // TODO Get the JNI long name
-    char* klass_name;
-    jvmtiError klass_result = jenv->GetClassSignature(klass, &klass_name, nullptr);
-    if (klass_result == JVMTI_ERROR_NONE) {
-      std::string name_str(name);
-      std::string klass_str(klass_name);
-      names[0] = GetJniShortName(klass_str, name_str);
-      jenv->Deallocate(reinterpret_cast<unsigned char*>(klass_name));
-    } else {
-      LOG(FATAL) << "Could not get class name!";
-    }
-  } else {
-    ScopedObjectAccess soa(Thread::Current());
-    ArtMethod* m = jni::DecodeArtMethod(method);
-    names[0] = m->JniShortName();
-    names[1] = m->JniLongName();
-  }
-  for (const std::string& mangled_name : names) {
-    if (mangled_name == "") {
-      continue;
-    }
-    void* sym = dlsym(RTLD_DEFAULT, mangled_name.c_str());
-    if (sym == nullptr) {
-      continue;
-    }
-
-    JNINativeMethod native_method;
-    native_method.fnPtr = sym;
-    native_method.name = name;
-    native_method.signature = signature;
-
-    env->RegisterNatives(klass, &native_method, 1);
-
-    jenv->Deallocate(reinterpret_cast<unsigned char*>(name));
-    jenv->Deallocate(reinterpret_cast<unsigned char*>(signature));
-    return;
-  }
-
-  LOG(FATAL) << "Could not find " << names[0];
-}
-
-static jclass FindClassWithSystemClassLoader(JNIEnv* env, const char* class_name) {
-  // Find the system classloader.
-  ScopedLocalRef<jclass> cl_klass(env, env->FindClass("java/lang/ClassLoader"));
-  if (cl_klass.get() == nullptr) {
-    return nullptr;
-  }
-  jmethodID getsystemclassloader_method = env->GetStaticMethodID(cl_klass.get(),
-                                                                 "getSystemClassLoader",
-                                                                 "()Ljava/lang/ClassLoader;");
-  if (getsystemclassloader_method == nullptr) {
-    return nullptr;
-  }
-  ScopedLocalRef<jobject> cl(env, env->CallStaticObjectMethod(cl_klass.get(),
-                                                              getsystemclassloader_method));
-  if (cl.get() == nullptr) {
-    return nullptr;
-  }
-
-  // Create a String of the name.
-  std::string descriptor = android::base::StringPrintf("L%s;", class_name);
-  std::string dot_name = DescriptorToDot(descriptor.c_str());
-  ScopedLocalRef<jstring> name_str(env, env->NewStringUTF(dot_name.c_str()));
-
-  // Call Class.forName with it.
-  ScopedLocalRef<jclass> c_klass(env, env->FindClass("java/lang/Class"));
-  if (c_klass.get() == nullptr) {
-    return nullptr;
-  }
-  jmethodID forname_method = env->GetStaticMethodID(
-      c_klass.get(),
-      "forName",
-      "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
-  if (forname_method == nullptr) {
-    return nullptr;
-  }
-
-  return reinterpret_cast<jclass>(env->CallStaticObjectMethod(c_klass.get(),
-                                                              forname_method,
-                                                              name_str.get(),
-                                                              JNI_FALSE,
-                                                              cl.get()));
-}
-
-void BindFunctions(jvmtiEnv* jenv, JNIEnv* env, const char* class_name) {
-  // Use JNI to load the class.
-  ScopedLocalRef<jclass> klass(env, env->FindClass(class_name));
-  if (klass.get() == nullptr) {
-    // We may be called with the wrong classloader. Try explicitly using the system classloader.
-    env->ExceptionClear();
-    klass.reset(FindClassWithSystemClassLoader(env, class_name));
-    if (klass.get() == nullptr) {
-      LOG(FATAL) << "Could not load " << class_name;
-    }
-  }
-  BindFunctionsOnClass(jenv, env, klass.get());
-}
-
-void BindFunctionsOnClass(jvmtiEnv* jenv, JNIEnv* env, jclass klass) {
-  // Use JVMTI to get the methods.
-  jint method_count;
-  jmethodID* methods;
-  jvmtiError methods_result = jenv->GetClassMethods(klass, &method_count, &methods);
-  if (methods_result != JVMTI_ERROR_NONE) {
-    LOG(FATAL) << "Could not get methods";
-  }
-
-  // Check each method.
-  for (jint i = 0; i < method_count; ++i) {
-    jint modifiers;
-    jvmtiError mod_result = jenv->GetMethodModifiers(methods[i], &modifiers);
-    if (mod_result != JVMTI_ERROR_NONE) {
-      LOG(FATAL) << "Could not get methods";
-    }
-    constexpr jint kNative = static_cast<jint>(kAccNative);
-    if ((modifiers & kNative) != 0) {
-      BindMethod(jenv, env, klass, methods[i]);
-    }
-  }
-
-  jenv->Deallocate(reinterpret_cast<unsigned char*>(methods));
-}
-
 }  // namespace art
diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h
index f10356d..610019e 100644
--- a/test/ti-agent/common_helper.h
+++ b/test/ti-agent/common_helper.h
@@ -19,13 +19,11 @@
 
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
 
 namespace art {
+
 namespace common_redefine {
-
 jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
 }  // namespace common_redefine
 
 namespace common_retransform {
@@ -36,53 +34,6 @@
 jint OnLoad(JavaVM* vm, char* options, void* reserved);
 }  // namespace common_transform
 
-
-extern bool RuntimeIsJVM;
-
-bool IsJVM();
-
-template <typename T>
-static jobjectArray CreateObjectArray(JNIEnv* env,
-                                      jint length,
-                                      const char* component_type_descriptor,
-                                      T src) {
-  if (length < 0) {
-    return nullptr;
-  }
-
-  ScopedLocalRef<jclass> obj_class(env, env->FindClass(component_type_descriptor));
-  if (obj_class.get() == nullptr) {
-    return nullptr;
-  }
-
-  ScopedLocalRef<jobjectArray> ret(env, env->NewObjectArray(length, obj_class.get(), nullptr));
-  if (ret.get() == nullptr) {
-    return nullptr;
-  }
-
-  for (jint i = 0; i < length; ++i) {
-    jobject element = src(i);
-    env->SetObjectArrayElement(ret.get(), static_cast<jint>(i), element);
-    env->DeleteLocalRef(element);
-    if (env->ExceptionCheck()) {
-      return nullptr;
-    }
-  }
-
-  return ret.release();
-}
-
-void SetAllCapabilities(jvmtiEnv* env);
-
-bool JvmtiErrorToException(JNIEnv* env, jvmtiError error);
-
-// Load the class through JNI. Inspect it, find all native methods. Construct the corresponding
-// mangled name, run dlsym and bind the method.
-//
-// This will abort on failure.
-void BindFunctions(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name);
-void BindFunctionsOnClass(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass);
-
 }  // namespace art
 
 #endif  // ART_TEST_TI_AGENT_COMMON_HELPER_H_
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 303e824..c5d75c9 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#include "common_load.h"
-
 #include <jni.h>
 #include <stdio.h>
 
-#include "art_method-inl.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "common_helper.h"
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 #include "901-hello-ti-agent/basics.h"
 #include "909-attach-agent/attach.h"
@@ -31,8 +31,6 @@
 
 namespace art {
 
-jvmtiEnv* jvmti_env;
-
 namespace {
 
 using OnLoad   = jint (*)(JavaVM* vm, char* options, void* reserved);
@@ -154,8 +152,8 @@
   return true;
 }
 
-static void SetIsJVM(char* options) {
-  RuntimeIsJVM = strncmp(options, "jvm", 3) == 0;
+static void SetIsJVM(const char* options) {
+  SetJVM(strncmp(options, "jvm", 3) == 0);
 }
 
 static bool BindFunctionsAttached(JavaVM* vm, const char* class_name) {
diff --git a/test/ti-agent/jni_binder.cc b/test/ti-agent/jni_binder.cc
new file mode 100644
index 0000000..efc2af8
--- /dev/null
+++ b/test/ti-agent/jni_binder.cc
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2017 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 "jni_binder.h"
+
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
+
+namespace art {
+
+size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count) {
+  DCHECK_LE(byte_count, strlen(utf8));
+  size_t len = 0;
+  const char* end = utf8 + byte_count;
+  for (; utf8 < end; ++utf8) {
+    int ic = *utf8;
+    len++;
+    if (LIKELY((ic & 0x80) == 0)) {
+      // One-byte encoding.
+      continue;
+    }
+    // Two- or three-byte encoding.
+    utf8++;
+    if ((ic & 0x20) == 0) {
+      // Two-byte encoding.
+      continue;
+    }
+    utf8++;
+    if ((ic & 0x10) == 0) {
+      // Three-byte encoding.
+      continue;
+    }
+
+    // Four-byte encoding: needs to be converted into a surrogate
+    // pair.
+    utf8++;
+    len++;
+  }
+  return len;
+}
+
+static uint16_t GetTrailingUtf16Char(uint32_t maybe_pair) {
+  return static_cast<uint16_t>(maybe_pair >> 16);
+}
+
+static uint16_t GetLeadingUtf16Char(uint32_t maybe_pair) {
+  return static_cast<uint16_t>(maybe_pair & 0x0000FFFF);
+}
+
+static uint32_t GetUtf16FromUtf8(const char** utf8_data_in) {
+  const uint8_t one = *(*utf8_data_in)++;
+  if ((one & 0x80) == 0) {
+    // one-byte encoding
+    return one;
+  }
+
+  const uint8_t two = *(*utf8_data_in)++;
+  if ((one & 0x20) == 0) {
+    // two-byte encoding
+    return ((one & 0x1f) << 6) | (two & 0x3f);
+  }
+
+  const uint8_t three = *(*utf8_data_in)++;
+  if ((one & 0x10) == 0) {
+    return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
+  }
+
+  // Four byte encodings need special handling. We'll have
+  // to convert them into a surrogate pair.
+  const uint8_t four = *(*utf8_data_in)++;
+
+  // Since this is a 4 byte UTF-8 sequence, it will lie between
+  // U+10000 and U+1FFFFF.
+  //
+  // TODO: What do we do about values in (U+10FFFF, U+1FFFFF) ? The
+  // spec says they're invalid but nobody appears to check for them.
+  const uint32_t code_point = ((one & 0x0f) << 18) | ((two & 0x3f) << 12)
+      | ((three & 0x3f) << 6) | (four & 0x3f);
+
+  uint32_t surrogate_pair = 0;
+  // Step two: Write out the high (leading) surrogate to the bottom 16 bits
+  // of the of the 32 bit type.
+  surrogate_pair |= ((code_point >> 10) + 0xd7c0) & 0xffff;
+  // Step three : Write out the low (trailing) surrogate to the top 16 bits.
+  surrogate_pair |= ((code_point & 0x03ff) + 0xdc00) << 16;
+
+  return surrogate_pair;
+}
+
+static std::string MangleForJni(const std::string& s) {
+  std::string result;
+  size_t char_count = CountModifiedUtf8Chars(s.c_str(), s.length());
+  const char* cp = &s[0];
+  for (size_t i = 0; i < char_count; ++i) {
+    uint32_t ch = GetUtf16FromUtf8(&cp);
+    if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
+      result.push_back(ch);
+    } else if (ch == '.' || ch == '/') {
+      result += "_";
+    } else if (ch == '_') {
+      result += "_1";
+    } else if (ch == ';') {
+      result += "_2";
+    } else if (ch == '[') {
+      result += "_3";
+    } else {
+      const uint16_t leading = GetLeadingUtf16Char(ch);
+      const uint32_t trailing = GetTrailingUtf16Char(ch);
+
+      android::base::StringAppendF(&result, "_0%04x", leading);
+      if (trailing != 0) {
+        android::base::StringAppendF(&result, "_0%04x", trailing);
+      }
+    }
+  }
+  return result;
+}
+
+static std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) {
+  // Remove the leading 'L' and trailing ';'...
+  std::string class_name(class_descriptor);
+  CHECK_EQ(class_name[0], 'L') << class_name;
+  CHECK_EQ(class_name[class_name.size() - 1], ';') << class_name;
+  class_name.erase(0, 1);
+  class_name.erase(class_name.size() - 1, 1);
+
+  std::string short_name;
+  short_name += "Java_";
+  short_name += MangleForJni(class_name);
+  short_name += "_";
+  short_name += MangleForJni(method);
+  return short_name;
+}
+
+static void BindMethod(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass, jmethodID method) {
+  std::string name;
+  std::string signature;
+  std::string mangled_names[2];
+  {
+    char* name_cstr;
+    char* sig_cstr;
+    jvmtiError name_result = jvmti_env->GetMethodName(method, &name_cstr, &sig_cstr, nullptr);
+    CheckJvmtiError(jvmti_env, name_result);
+    CHECK(name_cstr != nullptr);
+    CHECK(sig_cstr != nullptr);
+    name = name_cstr;
+    signature = sig_cstr;
+
+    char* klass_name;
+    jvmtiError klass_result = jvmti_env->GetClassSignature(klass, &klass_name, nullptr);
+    CheckJvmtiError(jvmti_env, klass_result);
+
+    mangled_names[0] = GetJniShortName(klass_name, name);
+    // TODO: Long JNI name.
+
+    CheckJvmtiError(jvmti_env, Deallocate(jvmti_env, name_cstr));
+    CheckJvmtiError(jvmti_env, Deallocate(jvmti_env, sig_cstr));
+    CheckJvmtiError(jvmti_env, Deallocate(jvmti_env, klass_name));
+  }
+
+  for (const std::string& mangled_name : mangled_names) {
+    if (mangled_name.empty()) {
+      continue;
+    }
+    void* sym = dlsym(RTLD_DEFAULT, mangled_name.c_str());
+    if (sym == nullptr) {
+      continue;
+    }
+
+    JNINativeMethod native_method;
+    native_method.fnPtr = sym;
+    native_method.name = name.c_str();
+    native_method.signature = signature.c_str();
+
+    env->RegisterNatives(klass, &native_method, 1);
+
+    return;
+  }
+
+  LOG(FATAL) << "Could not find " << mangled_names[0];
+}
+
+static std::string DescriptorToDot(const char* descriptor) {
+  size_t length = strlen(descriptor);
+  if (length > 1) {
+    if (descriptor[0] == 'L' && descriptor[length - 1] == ';') {
+      // Descriptors have the leading 'L' and trailing ';' stripped.
+      std::string result(descriptor + 1, length - 2);
+      std::replace(result.begin(), result.end(), '/', '.');
+      return result;
+    } else {
+      // For arrays the 'L' and ';' remain intact.
+      std::string result(descriptor);
+      std::replace(result.begin(), result.end(), '/', '.');
+      return result;
+    }
+  }
+  // Do nothing for non-class/array descriptors.
+  return descriptor;
+}
+
+static jobject GetSystemClassLoader(JNIEnv* env) {
+  ScopedLocalRef<jclass> cl_klass(env, env->FindClass("java/lang/ClassLoader"));
+  CHECK(cl_klass.get() != nullptr);
+  jmethodID getsystemclassloader_method = env->GetStaticMethodID(cl_klass.get(),
+                                                                 "getSystemClassLoader",
+                                                                 "()Ljava/lang/ClassLoader;");
+  CHECK(getsystemclassloader_method != nullptr);
+  return env->CallStaticObjectMethod(cl_klass.get(), getsystemclassloader_method);
+}
+
+static jclass FindClassWithClassLoader(JNIEnv* env, const char* class_name, jobject class_loader) {
+  // Create a String of the name.
+  std::string descriptor = android::base::StringPrintf("L%s;", class_name);
+  std::string dot_name = DescriptorToDot(descriptor.c_str());
+  ScopedLocalRef<jstring> name_str(env, env->NewStringUTF(dot_name.c_str()));
+
+  // Call Class.forName with it.
+  ScopedLocalRef<jclass> c_klass(env, env->FindClass("java/lang/Class"));
+  CHECK(c_klass.get() != nullptr);
+  jmethodID forname_method = env->GetStaticMethodID(
+      c_klass.get(),
+      "forName",
+      "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
+  CHECK(forname_method != nullptr);
+
+  return static_cast<jclass>(env->CallStaticObjectMethod(c_klass.get(),
+                                                         forname_method,
+                                                         name_str.get(),
+                                                         JNI_FALSE,
+                                                         class_loader));
+}
+
+// Find the given classname. First try the implied classloader, then the system classloader,
+// then use JVMTI to find all classloaders.
+static jclass FindClass(jvmtiEnv* jvmti_env,
+                        JNIEnv* env,
+                        const char* class_name,
+                        jobject class_loader) {
+  if (class_loader != nullptr) {
+    return FindClassWithClassLoader(env, class_name, class_loader);
+  }
+
+  jclass from_implied = env->FindClass(class_name);
+  if (from_implied != nullptr) {
+    return from_implied;
+  }
+  env->ExceptionClear();
+
+  ScopedLocalRef<jobject> system_class_loader(env, GetSystemClassLoader(env));
+  CHECK(system_class_loader.get() != nullptr);
+  jclass from_system = FindClassWithClassLoader(env, class_name, system_class_loader.get());
+  if (from_system != nullptr) {
+    return from_system;
+  }
+  env->ExceptionClear();
+
+  // Look at the context classloaders of all threads.
+  jint thread_count;
+  jthread* threads;
+  CheckJvmtiError(jvmti_env, jvmti_env->GetAllThreads(&thread_count, &threads));
+  JvmtiUniquePtr threads_uptr = MakeJvmtiUniquePtr(jvmti_env, threads);
+
+  jclass result = nullptr;
+  for (jint t = 0; t != thread_count; ++t) {
+    // Always loop over all elements, as we need to free the local references.
+    if (result == nullptr) {
+      jvmtiThreadInfo info;
+      CheckJvmtiError(jvmti_env, jvmti_env->GetThreadInfo(threads[t], &info));
+      CheckJvmtiError(jvmti_env, Deallocate(jvmti_env, info.name));
+      if (info.thread_group != nullptr) {
+        env->DeleteLocalRef(info.thread_group);
+      }
+      if (info.context_class_loader != nullptr) {
+        result = FindClassWithClassLoader(env, class_name, info.context_class_loader);
+        env->ExceptionClear();
+        env->DeleteLocalRef(info.context_class_loader);
+      }
+    }
+    env->DeleteLocalRef(threads[t]);
+  }
+
+  if (result != nullptr) {
+    return result;
+  }
+
+  // TODO: Implement scanning *all* classloaders.
+  LOG(FATAL) << "Unimplemented";
+
+  return nullptr;
+}
+
+void BindFunctionsOnClass(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass) {
+  // Use JVMTI to get the methods.
+  jint method_count;
+  jmethodID* methods;
+  jvmtiError methods_result = jvmti_env->GetClassMethods(klass, &method_count, &methods);
+  CheckJvmtiError(jvmti_env, methods_result);
+
+  // Check each method.
+  for (jint i = 0; i < method_count; ++i) {
+    jint modifiers;
+    jvmtiError mod_result = jvmti_env->GetMethodModifiers(methods[i], &modifiers);
+    CheckJvmtiError(jvmti_env, mod_result);
+    constexpr jint kNative = static_cast<jint>(0x0100);
+    if ((modifiers & kNative) != 0) {
+      BindMethod(jvmti_env, env, klass, methods[i]);
+    }
+  }
+
+  CheckJvmtiError(jvmti_env, Deallocate(jvmti_env, methods));
+}
+
+void BindFunctions(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name, jobject class_loader) {
+  // Use JNI to load the class.
+  ScopedLocalRef<jclass> klass(env, FindClass(jvmti_env, env, class_name, class_loader));
+  CHECK(klass.get() != nullptr) << class_name;
+  BindFunctionsOnClass(jvmti_env, env, klass.get());
+}
+
+}  // namespace art
diff --git a/test/ti-agent/jni_binder.h b/test/ti-agent/jni_binder.h
new file mode 100644
index 0000000..6f96257
--- /dev/null
+++ b/test/ti-agent/jni_binder.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_TEST_TI_AGENT_JNI_BINDER_H_
+#define ART_TEST_TI_AGENT_JNI_BINDER_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace art {
+
+// Load the class through JNI. Inspect it, find all native methods. Construct the corresponding
+// mangled name, run dlsym and bind the method.
+//
+// This will abort on failure.
+void BindFunctions(jvmtiEnv* jvmti_env,
+                   JNIEnv* env,
+                   const char* class_name,
+                   jobject class_loader = nullptr);
+
+void BindFunctionsOnClass(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass);
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_JNI_BINDER_H_
diff --git a/test/ti-agent/jni_helper.h b/test/ti-agent/jni_helper.h
new file mode 100644
index 0000000..c48b0c0
--- /dev/null
+++ b/test/ti-agent/jni_helper.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_TEST_TI_AGENT_JNI_HELPER_H_
+#define ART_TEST_TI_AGENT_JNI_HELPER_H_
+
+#include "jni.h"
+#include "scoped_local_ref.h"
+
+namespace art {
+
+// Create an object array using a lambda that returns a local ref for each element.
+template <typename T>
+static inline jobjectArray CreateObjectArray(JNIEnv* env,
+                                             jint length,
+                                             const char* component_type_descriptor,
+                                             T src) {
+  if (length < 0) {
+    return nullptr;
+  }
+
+  ScopedLocalRef<jclass> obj_class(env, env->FindClass(component_type_descriptor));
+  if (obj_class.get() == nullptr) {
+    return nullptr;
+  }
+
+  ScopedLocalRef<jobjectArray> ret(env, env->NewObjectArray(length, obj_class.get(), nullptr));
+  if (ret.get() == nullptr) {
+    return nullptr;
+  }
+
+  for (jint i = 0; i < length; ++i) {
+    jobject element = src(i);
+    env->SetObjectArrayElement(ret.get(), static_cast<jint>(i), element);
+    env->DeleteLocalRef(element);
+    if (env->ExceptionCheck()) {
+      return nullptr;
+    }
+  }
+
+  return ret.release();
+}
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_JNI_HELPER_H_
diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc
new file mode 100644
index 0000000..598a30f
--- /dev/null
+++ b/test/ti-agent/jvmti_helper.cc
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 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 "jvmti_helper.h"
+
+#include <algorithm>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <sstream>
+#include <string.h>
+
+#include "android-base/logging.h"
+#include "scoped_local_ref.h"
+
+namespace art {
+
+void CheckJvmtiError(jvmtiEnv* env, jvmtiError error) {
+  if (error != JVMTI_ERROR_NONE) {
+    char* error_name;
+    jvmtiError name_error = env->GetErrorName(error, &error_name);
+    if (name_error != JVMTI_ERROR_NONE) {
+      LOG(FATAL) << "Unable to get error name for " << error;
+    }
+    LOG(FATAL) << "Unexpected error: " << error_name;
+  }
+}
+
+void SetAllCapabilities(jvmtiEnv* env) {
+  jvmtiCapabilities caps;
+  jvmtiError error1 = env->GetPotentialCapabilities(&caps);
+  CheckJvmtiError(env, error1);
+  jvmtiError error2 = env->AddCapabilities(&caps);
+  CheckJvmtiError(env, error2);
+}
+
+bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmti_env, jvmtiError error) {
+  if (error == JVMTI_ERROR_NONE) {
+    return false;
+  }
+
+  ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+  if (rt_exception.get() == nullptr) {
+    // CNFE should be pending.
+    return true;
+  }
+
+  char* err;
+  CheckJvmtiError(jvmti_env, jvmti_env->GetErrorName(error, &err));
+
+  env->ThrowNew(rt_exception.get(), err);
+
+  Deallocate(jvmti_env, err);
+  return true;
+}
+
+std::ostream& operator<<(std::ostream& os, const jvmtiError& rhs) {
+  switch (rhs) {
+    case JVMTI_ERROR_NONE:
+      return os << "NONE";
+    case JVMTI_ERROR_INVALID_THREAD:
+      return os << "INVALID_THREAD";
+    case JVMTI_ERROR_INVALID_THREAD_GROUP:
+      return os << "INVALID_THREAD_GROUP";
+    case JVMTI_ERROR_INVALID_PRIORITY:
+      return os << "INVALID_PRIORITY";
+    case JVMTI_ERROR_THREAD_NOT_SUSPENDED:
+      return os << "THREAD_NOT_SUSPENDED";
+    case JVMTI_ERROR_THREAD_SUSPENDED:
+      return os << "THREAD_SUSPENDED";
+    case JVMTI_ERROR_THREAD_NOT_ALIVE:
+      return os << "THREAD_NOT_ALIVE";
+    case JVMTI_ERROR_INVALID_OBJECT:
+      return os << "INVALID_OBJECT";
+    case JVMTI_ERROR_INVALID_CLASS:
+      return os << "INVALID_CLASS";
+    case JVMTI_ERROR_CLASS_NOT_PREPARED:
+      return os << "CLASS_NOT_PREPARED";
+    case JVMTI_ERROR_INVALID_METHODID:
+      return os << "INVALID_METHODID";
+    case JVMTI_ERROR_INVALID_LOCATION:
+      return os << "INVALID_LOCATION";
+    case JVMTI_ERROR_INVALID_FIELDID:
+      return os << "INVALID_FIELDID";
+    case JVMTI_ERROR_NO_MORE_FRAMES:
+      return os << "NO_MORE_FRAMES";
+    case JVMTI_ERROR_OPAQUE_FRAME:
+      return os << "OPAQUE_FRAME";
+    case JVMTI_ERROR_TYPE_MISMATCH:
+      return os << "TYPE_MISMATCH";
+    case JVMTI_ERROR_INVALID_SLOT:
+      return os << "INVALID_SLOT";
+    case JVMTI_ERROR_DUPLICATE:
+      return os << "DUPLICATE";
+    case JVMTI_ERROR_NOT_FOUND:
+      return os << "NOT_FOUND";
+    case JVMTI_ERROR_INVALID_MONITOR:
+      return os << "INVALID_MONITOR";
+    case JVMTI_ERROR_NOT_MONITOR_OWNER:
+      return os << "NOT_MONITOR_OWNER";
+    case JVMTI_ERROR_INTERRUPT:
+      return os << "INTERRUPT";
+    case JVMTI_ERROR_INVALID_CLASS_FORMAT:
+      return os << "INVALID_CLASS_FORMAT";
+    case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION:
+      return os << "CIRCULAR_CLASS_DEFINITION";
+    case JVMTI_ERROR_FAILS_VERIFICATION:
+      return os << "FAILS_VERIFICATION";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED:
+      return os << "UNSUPPORTED_REDEFINITION_METHOD_ADDED";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED:
+      return os << "UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED";
+    case JVMTI_ERROR_INVALID_TYPESTATE:
+      return os << "INVALID_TYPESTATE";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED:
+      return os << "UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED:
+      return os << "UNSUPPORTED_REDEFINITION_METHOD_DELETED";
+    case JVMTI_ERROR_UNSUPPORTED_VERSION:
+      return os << "UNSUPPORTED_VERSION";
+    case JVMTI_ERROR_NAMES_DONT_MATCH:
+      return os << "NAMES_DONT_MATCH";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED:
+      return os << "UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:
+      return os << "UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED";
+    case JVMTI_ERROR_UNMODIFIABLE_CLASS:
+      return os << "JVMTI_ERROR_UNMODIFIABLE_CLASS";
+    case JVMTI_ERROR_NOT_AVAILABLE:
+      return os << "NOT_AVAILABLE";
+    case JVMTI_ERROR_MUST_POSSESS_CAPABILITY:
+      return os << "MUST_POSSESS_CAPABILITY";
+    case JVMTI_ERROR_NULL_POINTER:
+      return os << "NULL_POINTER";
+    case JVMTI_ERROR_ABSENT_INFORMATION:
+      return os << "ABSENT_INFORMATION";
+    case JVMTI_ERROR_INVALID_EVENT_TYPE:
+      return os << "INVALID_EVENT_TYPE";
+    case JVMTI_ERROR_ILLEGAL_ARGUMENT:
+      return os << "ILLEGAL_ARGUMENT";
+    case JVMTI_ERROR_NATIVE_METHOD:
+      return os << "NATIVE_METHOD";
+    case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED:
+      return os << "CLASS_LOADER_UNSUPPORTED";
+    case JVMTI_ERROR_OUT_OF_MEMORY:
+      return os << "OUT_OF_MEMORY";
+    case JVMTI_ERROR_ACCESS_DENIED:
+      return os << "ACCESS_DENIED";
+    case JVMTI_ERROR_WRONG_PHASE:
+      return os << "WRONG_PHASE";
+    case JVMTI_ERROR_INTERNAL:
+      return os << "INTERNAL";
+    case JVMTI_ERROR_UNATTACHED_THREAD:
+      return os << "UNATTACHED_THREAD";
+    case JVMTI_ERROR_INVALID_ENVIRONMENT:
+      return os << "INVALID_ENVIRONMENT";
+  }
+  LOG(FATAL) << "Unexpected error type " << static_cast<int>(rhs);
+  __builtin_unreachable();
+}
+
+}  // namespace art
diff --git a/test/ti-agent/jvmti_helper.h b/test/ti-agent/jvmti_helper.h
new file mode 100644
index 0000000..66d88d0
--- /dev/null
+++ b/test/ti-agent/jvmti_helper.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_TEST_TI_AGENT_JVMTI_HELPER_H_
+#define ART_TEST_TI_AGENT_JVMTI_HELPER_H_
+
+#include "jni.h"
+#include "jvmti.h"
+#include <memory>
+#include <ostream>
+
+#include "android-base/logging.h"
+
+namespace art {
+
+// Add all capabilities to the given env.
+void SetAllCapabilities(jvmtiEnv* env);
+
+// Check whether the given error is NONE. If not, print out the corresponding error message
+// and abort.
+void CheckJvmtiError(jvmtiEnv* env, jvmtiError error);
+
+// Convert the given error to a RuntimeException with a message derived from the error. Returns
+// true on error, false if error is JVMTI_ERROR_NONE.
+bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmti_env, jvmtiError error);
+
+class JvmtiDeleter {
+ public:
+  JvmtiDeleter() : env_(nullptr) {}
+  explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {}
+
+  JvmtiDeleter(JvmtiDeleter&) = default;
+  JvmtiDeleter(JvmtiDeleter&&) = default;
+  JvmtiDeleter& operator=(const JvmtiDeleter&) = default;
+
+  void operator()(unsigned char* ptr) const {
+    CHECK(env_ != nullptr);
+    jvmtiError ret = env_->Deallocate(ptr);
+    CheckJvmtiError(env_, ret);
+  }
+
+ private:
+  mutable jvmtiEnv* env_;
+};
+
+using JvmtiUniquePtr = std::unique_ptr<unsigned char, JvmtiDeleter>;
+
+template <typename T>
+static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) {
+  return JvmtiUniquePtr(reinterpret_cast<unsigned char*>(mem), JvmtiDeleter(env));
+}
+
+template <typename T>
+static inline jvmtiError Deallocate(jvmtiEnv* env, T* mem) {
+  return env->Deallocate(reinterpret_cast<unsigned char*>(mem));
+}
+
+// To print jvmtiError. Does not rely on GetErrorName, so is an approximation.
+std::ostream& operator<<(std::ostream& os, const jvmtiError& rhs);
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_JVMTI_HELPER_H_
diff --git a/test/ti-agent/scoped_local_ref.h b/test/ti-agent/scoped_local_ref.h
new file mode 100644
index 0000000..0cd9891
--- /dev/null
+++ b/test/ti-agent/scoped_local_ref.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef ART_TEST_TI_AGENT_SCOPED_LOCAL_REF_H_
+#define ART_TEST_TI_AGENT_SCOPED_LOCAL_REF_H_
+
+#include "jni.h"
+
+#include <stddef.h>
+
+#include "android-base/macros.h"
+
+namespace art {
+
+template<typename T>
+class ScopedLocalRef {
+ public:
+  ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
+  }
+
+  ~ScopedLocalRef() {
+    reset();
+  }
+
+  void reset(T ptr = NULL) {
+    if (ptr != mLocalRef) {
+      if (mLocalRef != NULL) {
+        mEnv->DeleteLocalRef(mLocalRef);
+      }
+      mLocalRef = ptr;
+    }
+  }
+
+  T release() __attribute__((warn_unused_result)) {
+    T localRef = mLocalRef;
+    mLocalRef = NULL;
+    return localRef;
+  }
+
+  T get() const {
+    return mLocalRef;
+  }
+
+ private:
+  JNIEnv* const mEnv;
+  T mLocalRef;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
+};
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_SCOPED_LOCAL_REF_H_
diff --git a/test/ti-agent/scoped_utf_chars.h b/test/ti-agent/scoped_utf_chars.h
new file mode 100644
index 0000000..e8c9a11
--- /dev/null
+++ b/test/ti-agent/scoped_utf_chars.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef ART_TEST_TI_AGENT_SCOPED_UTF_CHARS_H_
+#define ART_TEST_TI_AGENT_SCOPED_UTF_CHARS_H_
+
+#include "jni.h"
+
+#include <string.h>
+
+#include "android-base/macros.h"
+
+namespace art {
+
+class ScopedUtfChars {
+ public:
+  ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
+    if (s == nullptr) {
+      utf_chars_ = nullptr;
+      // TODO: JniThrowNullPointerException(env, nullptr);
+    } else {
+      utf_chars_ = env->GetStringUTFChars(s, nullptr);
+    }
+  }
+
+  ScopedUtfChars(ScopedUtfChars&& rhs) :
+      env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
+    rhs.env_ = nullptr;
+    rhs.string_ = nullptr;
+    rhs.utf_chars_ = nullptr;
+  }
+
+  ~ScopedUtfChars() {
+    if (utf_chars_) {
+      env_->ReleaseStringUTFChars(string_, utf_chars_);
+    }
+  }
+
+  ScopedUtfChars& operator=(ScopedUtfChars&& rhs) {
+    if (this != &rhs) {
+      // Delete the currently owned UTF chars.
+      this->~ScopedUtfChars();
+
+      // Move the rhs ScopedUtfChars and zero it out.
+      env_ = rhs.env_;
+      string_ = rhs.string_;
+      utf_chars_ = rhs.utf_chars_;
+      rhs.env_ = nullptr;
+      rhs.string_ = nullptr;
+      rhs.utf_chars_ = nullptr;
+    }
+    return *this;
+  }
+
+  const char* c_str() const {
+    return utf_chars_;
+  }
+
+  size_t size() const {
+    return strlen(utf_chars_);
+  }
+
+  const char& operator[](size_t n) const {
+    return utf_chars_[n];
+  }
+
+ private:
+  JNIEnv* env_;
+  jstring string_;
+  const char* utf_chars_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
+};
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_SCOPED_UTF_CHARS_H_
diff --git a/test/ti-agent/common_load.h b/test/ti-agent/test_env.cc
similarity index 78%
copy from test/ti-agent/common_load.h
copy to test/ti-agent/test_env.cc
index e79a006..cf47f22 100644
--- a/test/ti-agent/common_load.h
+++ b/test/ti-agent/test_env.cc
@@ -14,15 +14,20 @@
  * limitations under the License.
  */
 
-#ifndef ART_TEST_TI_AGENT_COMMON_LOAD_H_
-#define ART_TEST_TI_AGENT_COMMON_LOAD_H_
-
-#include "jvmti.h"
+#include "test_env.h"
 
 namespace art {
 
-extern jvmtiEnv* jvmti_env;
+jvmtiEnv* jvmti_env = nullptr;
+
+static bool gRuntimeIsJVM = false;
+
+bool IsJVM() {
+  return gRuntimeIsJVM;
+}
+
+void SetJVM(bool b) {
+  gRuntimeIsJVM = b;
+}
 
 }  // namespace art
-
-#endif  // ART_TEST_TI_AGENT_COMMON_LOAD_H_
diff --git a/test/ti-agent/common_load.h b/test/ti-agent/test_env.h
similarity index 81%
rename from test/ti-agent/common_load.h
rename to test/ti-agent/test_env.h
index e79a006..2eb631c 100644
--- a/test/ti-agent/common_load.h
+++ b/test/ti-agent/test_env.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_TEST_TI_AGENT_COMMON_LOAD_H_
-#define ART_TEST_TI_AGENT_COMMON_LOAD_H_
+#ifndef ART_TEST_TI_AGENT_TEST_ENV_H_
+#define ART_TEST_TI_AGENT_TEST_ENV_H_
 
 #include "jvmti.h"
 
@@ -23,6 +23,9 @@
 
 extern jvmtiEnv* jvmti_env;
 
+bool IsJVM();
+void SetJVM(bool b);
+
 }  // namespace art
 
-#endif  // ART_TEST_TI_AGENT_COMMON_LOAD_H_
+#endif  // ART_TEST_TI_AGENT_TEST_ENV_H_