diff options
author | 2017-03-31 12:49:07 -0700 | |
---|---|---|
committer | 2017-03-31 19:44:26 -0700 | |
commit | 027444b64dd52e1d2beea7aa525fbb8146a516bc (patch) | |
tree | ae746f4ee59787cae3f38e8f1c3c72e81e6fa693 | |
parent | 5b92c48f99391ae764e1699a22881f9d5cbce721 (diff) |
ART: TI Agent test library refactor
More refactoring, lowering the reliance on ART-provided functionality
and adding a library that includes all the code that can run without
ART.
Bug: 32072923
Test: m test-art-host
Change-Id: I67d84056a6fd7722c58855fccbdea3f6869b2efb
38 files changed, 691 insertions, 252 deletions
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc index 00776ca88c..20b227ab24 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -21,7 +21,7 @@ #include <jni.h> #include <stdio.h> #include <string.h> -#include "base/macros.h" +#include "android-base/macros.h" #include "jvmti.h" // Test infrastructure diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc index 7f079a259a..701b0c3817 100644 --- a/test/903-hello-tagging/tagging.cc +++ b/test/903-hello-tagging/tagging.cc @@ -19,14 +19,12 @@ #include <stdio.h> #include <vector> +#include "android-base/logging.h" #include "jni.h" -#include "ScopedLocalRef.h" -#include "ScopedPrimitiveArray.h" +#include "scoped_local_ref.h" +#include "scoped_primitive_array.h" -#include "art_method-inl.h" -#include "base/logging.h" #include "jvmti.h" -#include "utils.h" // Test infrastructure #include "jvmti_helper.h" diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc index 303f954b3b..c829496975 100644 --- a/test/904-object-allocation/tracking.cc +++ b/test/904-object-allocation/tracking.cc @@ -19,12 +19,11 @@ #include <stdio.h> #include <vector> -#include "base/logging.h" +#include "android-base/logging.h" #include "jni.h" #include "jvmti.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" -#include "utils.h" +#include "scoped_local_ref.h" +#include "scoped_utf_chars.h" // Test infrastructure #include "jvmti_helper.h" diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc index 68ce38d9d9..59b429ca40 100644 --- a/test/905-object-free/tracking_free.cc +++ b/test/905-object-free/tracking_free.cc @@ -19,12 +19,11 @@ #include <stdio.h> #include <vector> -#include "base/logging.h" +#include "android-base/logging.h" #include "jni.h" #include "jvmti.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" -#include "utils.h" +#include "scoped_local_ref.h" +#include "scoped_utf_chars.h" // Test infrastructure #include "jvmti_helper.h" diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc index 74cb1e9c1f..bb30074958 100644 --- a/test/906-iterate-heap/iterate_heap.cc +++ b/test/906-iterate-heap/iterate_heap.cc @@ -23,16 +23,18 @@ #include <stdio.h> #include <vector> +#include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "base/logging.h" + #include "jni.h" #include "jvmti.h" -#include "ScopedPrimitiveArray.h" -#include "utf.h" +#include "scoped_primitive_array.h" // Test infrastructure #include "jvmti_helper.h" #include "test_env.h" +#include "ti_macros.h" +#include "ti_utf.h" namespace art { namespace Test906IterateHeap { @@ -197,10 +199,10 @@ extern "C" JNIEXPORT jstring JNICALL Java_Main_iterateThroughHeapString( void* user_data) { FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data); if (*tag_ptr == p->tag_to_find) { - size_t utf_byte_count = CountUtf8Bytes(value, value_length); + size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length); std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]); memset(mod_utf.get(), 0, utf_byte_count + 1); - ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length); + ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length); if (!p->data.empty()) { p->data += "\n"; } diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc index 1b973bf2be..5ec56c4997 100644 --- a/test/907-get-loaded-classes/get_loaded_classes.cc +++ b/test/907-get-loaded-classes/get_loaded_classes.cc @@ -19,11 +19,12 @@ #include <stdio.h> #include <vector> -#include "base/macros.h" +#include "android-base/macros.h" + #include "jni.h" #include "jvmti.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" +#include "scoped_local_ref.h" +#include "scoped_utf_chars.h" // Test infrastructure #include "jni_helper.h" diff --git a/test/908-gc-start-finish/gc_callbacks.cc b/test/908-gc-start-finish/gc_callbacks.cc index 4b9a23cede..f186895f84 100644 --- a/test/908-gc-start-finish/gc_callbacks.cc +++ b/test/908-gc-start-finish/gc_callbacks.cc @@ -17,7 +17,8 @@ #include <stdio.h> #include <string.h> -#include "base/macros.h" +#include "android-base/macros.h" + #include "jni.h" #include "jvmti.h" diff --git a/test/909-attach-agent/attach.cc b/test/909-attach-agent/attach.cc index 67c756745f..0150e0962f 100644 --- a/test/909-attach-agent/attach.cc +++ b/test/909-attach-agent/attach.cc @@ -19,7 +19,9 @@ #include <jni.h> #include <stdio.h> #include <string.h> -#include "base/macros.h" + +#include "android-base/macros.h" + #include "jvmti.h" namespace art { diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc index 429076c386..ded4f09df7 100644 --- a/test/910-methods/methods.cc +++ b/test/910-methods/methods.cc @@ -16,10 +16,11 @@ #include <stdio.h> -#include "base/macros.h" +#include "android-base/macros.h" + #include "jni.h" #include "jvmti.h" -#include "ScopedLocalRef.h" +#include "scoped_local_ref.h" // Test infrastructure #include "jni_helper.h" diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc index 49cbb7e3c5..a499e90ad9 100644 --- a/test/911-get-stack-trace/stack_trace.cc +++ b/test/911-get-stack-trace/stack_trace.cc @@ -18,20 +18,19 @@ #include <memory> #include <stdio.h> +#include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "android-base/stringprintf.h" -#include "base/logging.h" -#include "base/macros.h" #include "jni.h" #include "jvmti.h" -#include "ScopedLocalRef.h" +#include "scoped_local_ref.h" // Test infrastructure #include "jni_binder.h" #include "jni_helper.h" #include "jvmti_helper.h" #include "test_env.h" +#include "ti_macros.h" namespace art { namespace Test911GetStackTrace { diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index 4d84e393d1..2636367548 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -16,14 +16,15 @@ #include <stdio.h> -#include "base/macros.h" +#include "android-base/macros.h" + #include "class_linker.h" #include "jni.h" #include "mirror/class_loader.h" #include "jvmti.h" #include "runtime.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" +#include "scoped_local_ref.h" +#include "scoped_utf_chars.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc index 999467f443..6a47ca139c 100644 --- a/test/913-heaps/heaps.cc +++ b/test/913-heaps/heaps.cc @@ -21,10 +21,10 @@ #include <iostream> #include <vector> +#include "android-base/macros.h" +#include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "base/logging.h" -#include "base/macros.h" #include "jit/jit.h" #include "jni.h" #include "native_stack_dump.h" diff --git a/test/918-fields/fields.cc b/test/918-fields/fields.cc index 0c019e3dbb..726c5cf161 100644 --- a/test/918-fields/fields.cc +++ b/test/918-fields/fields.cc @@ -16,10 +16,10 @@ #include <stdio.h> -#include "base/macros.h" +#include "android-base/macros.h" #include "jni.h" #include "jvmti.h" -#include "ScopedLocalRef.h" +#include "scoped_local_ref.h" // Test infrastructure #include "jni_helper.h" diff --git a/test/920-objects/objects.cc b/test/920-objects/objects.cc index 1dfb516c79..5263e753ed 100644 --- a/test/920-objects/objects.cc +++ b/test/920-objects/objects.cc @@ -16,10 +16,10 @@ #include <stdio.h> -#include "base/macros.h" +#include "android-base/macros.h" #include "jni.h" #include "jvmti.h" -#include "ScopedLocalRef.h" +#include "scoped_local_ref.h" // Test infrastructure #include "test_env.h" diff --git a/test/922-properties/properties.cc b/test/922-properties/properties.cc index 948da6a5bb..896e4c30af 100644 --- a/test/922-properties/properties.cc +++ b/test/922-properties/properties.cc @@ -16,10 +16,10 @@ #include <stdio.h> -#include "base/macros.h" +#include "android-base/macros.h" #include "jni.h" #include "jvmti.h" -#include "ScopedUtfChars.h" +#include "scoped_utf_chars.h" // Test infrastructure #include "jni_helper.h" diff --git a/test/923-monitors/monitors.cc b/test/923-monitors/monitors.cc index 60d5b5a941..6369a740e1 100644 --- a/test/923-monitors/monitors.cc +++ b/test/923-monitors/monitors.cc @@ -16,10 +16,10 @@ #include <stdio.h> -#include "base/macros.h" +#include "android-base/macros.h" #include "jni.h" #include "jvmti.h" -#include "ScopedUtfChars.h" +#include "scoped_utf_chars.h" // Test infrastructure #include "jvmti_helper.h" diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc index bb040bdb2b..a8b37ecd37 100644 --- a/test/924-threads/threads.cc +++ b/test/924-threads/threads.cc @@ -16,17 +16,17 @@ #include <stdio.h> +#include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "base/macros.h" -#include "base/logging.h" #include "jni.h" #include "jvmti.h" -#include "ScopedLocalRef.h" +#include "scoped_local_ref.h" // Test infrastructure #include "jni_helper.h" #include "jvmti_helper.h" #include "test_env.h" +#include "ti_macros.h" namespace art { namespace Test924Threads { diff --git a/test/925-threadgroups/threadgroups.cc b/test/925-threadgroups/threadgroups.cc index 1cd93bea3b..d55555355c 100644 --- a/test/925-threadgroups/threadgroups.cc +++ b/test/925-threadgroups/threadgroups.cc @@ -16,17 +16,17 @@ #include <stdio.h> +#include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "base/macros.h" -#include "base/logging.h" #include "jni.h" #include "jvmti.h" -#include "ScopedLocalRef.h" +#include "scoped_local_ref.h" // Test infrastructure #include "jni_helper.h" #include "jvmti_helper.h" #include "test_env.h" +#include "ti_macros.h" namespace art { namespace Test925ThreadGroups { diff --git a/test/927-timers/timers.cc b/test/927-timers/timers.cc index a67f5b47e0..55d3921cb6 100644 --- a/test/927-timers/timers.cc +++ b/test/927-timers/timers.cc @@ -16,9 +16,9 @@ #include <inttypes.h> +#include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "base/logging.h" -#include "base/macros.h" + #include "jni.h" #include "jvmti.h" @@ -26,6 +26,7 @@ #include "jni_helper.h" #include "jvmti_helper.h" #include "test_env.h" +#include "ti_macros.h" namespace art { namespace Test926Timers { diff --git a/test/928-jni-table/jni_table.cc b/test/928-jni-table/jni_table.cc index 3f4a93e147..26a6707f91 100644 --- a/test/928-jni-table/jni_table.cc +++ b/test/928-jni-table/jni_table.cc @@ -19,8 +19,8 @@ #include "jni.h" #include "jvmti.h" -#include "base/logging.h" -#include "base/macros.h" +#include "android-base/logging.h" +#include "android-base/macros.h" // Test infrastructure #include "jvmti_helper.h" diff --git a/test/929-search/search.cc b/test/929-search/search.cc index bed4dfeb65..5516105abe 100644 --- a/test/929-search/search.cc +++ b/test/929-search/search.cc @@ -16,12 +16,12 @@ #include <inttypes.h> +#include "android-base/logging.h" +#include "android-base/macros.h" #include "android-base/stringprintf.h" -#include "base/logging.h" -#include "base/macros.h" #include "jni.h" #include "jvmti.h" -#include "ScopedUtfChars.h" +#include "scoped_utf_chars.h" // Test infrastructure #include "jvmti_helper.h" diff --git a/test/931-agent-thread/agent_thread.cc b/test/931-agent-thread/agent_thread.cc index 3ec8793f89..f9af8cfe71 100644 --- a/test/931-agent-thread/agent_thread.cc +++ b/test/931-agent-thread/agent_thread.cc @@ -15,17 +15,14 @@ */ #include <inttypes.h> +#include <pthread.h> #include <sched.h> -#include "barrier.h" -#include "base/logging.h" -#include "base/macros.h" +#include "android-base/logging.h" +#include "android-base/macros.h" #include "jni.h" #include "jvmti.h" -#include "runtime.h" -#include "ScopedLocalRef.h" -#include "thread-inl.h" -#include "well_known_classes.h" +#include "scoped_local_ref.h" // Test infrastructure #include "jvmti_helper.h" @@ -37,13 +34,12 @@ namespace Test930AgentThread { struct AgentData { AgentData() : main_thread(nullptr), jvmti_env(nullptr), - b(2), priority(0) { } jthread main_thread; jvmtiEnv* jvmti_env; - Barrier b; + pthread_barrier_t b; jint priority; }; @@ -54,14 +50,21 @@ static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) { // This thread is not the main thread. jthread this_thread; jvmtiError this_thread_result = jenv->GetCurrentThread(&this_thread); - CHECK(!JvmtiErrorToException(env, jenv, this_thread_result)); + CheckJvmtiError(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, jenv, info_result)); + CheckJvmtiError(jenv, info_result); CHECK(info.is_daemon); + CheckJvmtiError(jenv, jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name))); + if (info.thread_group != nullptr) { + env->DeleteLocalRef(info.thread_group); + } + if (info.context_class_loader != nullptr) { + env->DeleteLocalRef(info.context_class_loader); + } // The thread has the requested priority. // TODO: Our thread priorities do not work on the host. @@ -71,7 +74,7 @@ static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) { jint thread_count; jthread* threads; jvmtiError threads_result = jenv->GetAllThreads(&thread_count, &threads); - CHECK(!JvmtiErrorToException(env, jenv, threads_result)); + CheckJvmtiError(jenv, threads_result); bool found = false; for (jint i = 0; i != thread_count; ++i) { if (env->IsSameObject(threads[i], this_thread)) { @@ -82,29 +85,53 @@ static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) { CHECK(found); // Done, let the main thread progress. - data->b.Pass(Thread::Current()); + int wait_result = pthread_barrier_wait(&data->b); + CHECK(wait_result == PTHREAD_BARRIER_SERIAL_THREAD || wait_result == 0); } extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread( JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { // Create a Thread object. - ScopedLocalRef<jobject> thread_name(env, - env->NewStringUTF("Agent Thread")); + ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF("Agent Thread")); if (thread_name.get() == nullptr) { return; } - ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread)); + ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread")); + if (thread_klass.get() == nullptr) { + return; + } + ScopedLocalRef<jobject> thread(env, env->AllocObject(thread_klass.get())); if (thread.get() == nullptr) { return; } + // Get a ThreadGroup from the current thread. We need a non-null one as we're gonna call a + // runtime-only constructor (so we can set priority and daemon state). + jvmtiThreadInfo cur_thread_info; + jvmtiError info_result = jvmti_env->GetThreadInfo(nullptr, &cur_thread_info); + if (JvmtiErrorToException(env, jvmti_env, info_result)) { + return; + } + CheckJvmtiError(jvmti_env, + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(cur_thread_info.name))); + ScopedLocalRef<jobject> thread_group(env, cur_thread_info.thread_group); + if (cur_thread_info.context_class_loader != nullptr) { + env->DeleteLocalRef(cur_thread_info.context_class_loader); + } + + jmethodID initID = env->GetMethodID(thread_klass.get(), + "<init>", + "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V"); + if (initID == nullptr) { + return; + } env->CallNonvirtualVoidMethod(thread.get(), - WellKnownClasses::java_lang_Thread, - WellKnownClasses::java_lang_Thread_init, - Runtime::Current()->GetMainThreadGroup(), + thread_klass.get(), + initID, + thread_group.get(), thread_name.get(), - kMinThreadPriority, + 0, JNI_FALSE); if (env->ExceptionCheck()) { return; @@ -120,18 +147,20 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread( data.main_thread = env->NewGlobalRef(main_thread); data.jvmti_env = jvmti_env; data.priority = JVMTI_THREAD_MIN_PRIORITY; + CHECK_EQ(0, pthread_barrier_init(&data.b, nullptr, 2)); jvmtiError result = jvmti_env->RunAgentThread(thread.get(), AgentMain, &data, data.priority); if (JvmtiErrorToException(env, jvmti_env, result)) { return; } - data.b.Wait(Thread::Current()); + int wait_result = pthread_barrier_wait(&data.b); + CHECK(wait_result == PTHREAD_BARRIER_SERIAL_THREAD || wait_result == 0); // Scheduling may mean that the agent thread is put to sleep. Wait until it's dead in an effort // to not unload the plugin and crash. for (;;) { - NanoSleep(1000 * 1000); + sleep(1); jint thread_state; jvmtiError state_result = jvmti_env->GetThreadState(thread.get(), &thread_state); if (JvmtiErrorToException(env, jvmti_env, state_result)) { @@ -144,9 +173,11 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread( } // Yield and sleep a bit more, to give the plugin time to tear down the native thread structure. sched_yield(); - NanoSleep(100 * 1000 * 1000); + sleep(1); env->DeleteGlobalRef(data.main_thread); + + pthread_barrier_destroy(&data.b); } } // namespace Test930AgentThread diff --git a/test/933-misc-events/misc_events.cc b/test/933-misc-events/misc_events.cc index 7b6c64d1fb..2b74c407e0 100644 --- a/test/933-misc-events/misc_events.cc +++ b/test/933-misc-events/misc_events.cc @@ -18,8 +18,8 @@ #include <signal.h> #include <sys/types.h> -#include "base/logging.h" -#include "base/macros.h" +#include "android-base/logging.h" +#include "android-base/macros.h" #include "jni.h" #include "jvmti.h" diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc index 72987eb358..b2ef05690f 100644 --- a/test/936-search-onload/search_onload.cc +++ b/test/936-search-onload/search_onload.cc @@ -23,7 +23,7 @@ #include "base/macros.h" #include "jni.h" #include "jvmti.h" -#include "ScopedUtfChars.h" +#include "scoped_utf_chars.h" // Test infrastructure #include "jvmti_helper.h" diff --git a/test/944-transform-classloaders/classloader.cc b/test/944-transform-classloaders/classloader.cc index f46763c2f1..698e023771 100644 --- a/test/944-transform-classloaders/classloader.cc +++ b/test/944-transform-classloaders/classloader.cc @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "base/macros.h" +#include "android-base/macros.h" #include "jni.h" #include "jvmti.h" #include "mirror/class-inl.h" -#include "ScopedLocalRef.h" +#include "scoped_local_ref.h" // Test infrastructure #include "test_env.h" @@ -26,7 +26,6 @@ namespace art { namespace Test944TransformClassloaders { - extern "C" JNIEXPORT jlong JNICALL Java_Main_getDexFilePointer(JNIEnv* env, jclass, jclass klass) { if (Runtime::Current() == nullptr) { env->ThrowNew(env->FindClass("java/lang/Exception"), diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc index b9303dda27..ee653a4a12 100644 --- a/test/945-obsolete-native/obsolete_native.cc +++ b/test/945-obsolete-native/obsolete_native.cc @@ -25,7 +25,7 @@ #include "base/macros.h" #include "jni.h" #include "jvmti.h" -#include "ScopedLocalRef.h" +#include "scoped_local_ref.h" // Test infrastructure #include "jni_binder.h" diff --git a/test/980-redefine-object/redefine_object.cc b/test/980-redefine-object/redefine_object.cc index 6c8c4bd2f0..1faf1a16a7 100644 --- a/test/980-redefine-object/redefine_object.cc +++ b/test/980-redefine-object/redefine_object.cc @@ -22,7 +22,7 @@ #include "base/macros.h" #include "jni.h" #include "jvmti.h" -#include "ScopedUtfChars.h" +#include "scoped_utf_chars.h" // Test infrastructure #include "jni_binder.h" diff --git a/test/Android.bp b/test/Android.bp index 2e8f5bb6f8..40f7edd1d2 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -241,29 +241,24 @@ art_cc_test_library { } art_cc_defaults { - name: "libtiagent-defaults", + name: "libtiagent-base-defaults", defaults: ["libartagent-defaults"], srcs: [ - // This is to get the IsInterpreted native method. - "common/stack_inspect.cc", - "common/runtime_state.cc", - "ti-agent/common_load.cc", - "ti-agent/common_helper.cc", + // These are the ART-independent parts. + "ti-agent/agent_startup.cc", "ti-agent/jni_binder.cc", "ti-agent/jvmti_helper.cc", "ti-agent/test_env.cc", - "901-hello-ti-agent/basics.cc", + // This is the list of non-special OnLoad things and excludes BCI and anything that depends + // on ART internals. "903-hello-tagging/tagging.cc", "904-object-allocation/tracking.cc", "905-object-free/tracking_free.cc", "906-iterate-heap/iterate_heap.cc", "907-get-loaded-classes/get_loaded_classes.cc", "908-gc-start-finish/gc_callbacks.cc", - "909-attach-agent/attach.cc", "910-methods/methods.cc", "911-get-stack-trace/stack_trace.cc", - "912-classes/classes.cc", - "913-heaps/heaps.cc", "918-fields/fields.cc", "920-objects/objects.cc", "922-properties/properties.cc", @@ -275,11 +270,6 @@ art_cc_defaults { "929-search/search.cc", "931-agent-thread/agent_thread.cc", "933-misc-events/misc_events.cc", - "936-search-onload/search_onload.cc", - "944-transform-classloaders/classloader.cc", - "945-obsolete-native/obsolete_native.cc", - "980-redefine-object/redefine_object.cc", - "983-source-transform-verify/source_transform.cc", ], shared_libs: [ "libbase", @@ -288,6 +278,29 @@ art_cc_defaults { include_dirs: ["art/test/ti-agent"], } +art_cc_defaults { + name: "libtiagent-defaults", + defaults: ["libtiagent-base-defaults"], + srcs: [ + // This is to get the IsInterpreted native method. + "common/stack_inspect.cc", + "common/runtime_state.cc", + // This includes the remaining test functions. We should try to refactor things to + // make this list smaller. + "ti-agent/common_helper.cc", + "ti-agent/common_load.cc", + "901-hello-ti-agent/basics.cc", + "909-attach-agent/attach.cc", + "912-classes/classes.cc", + "913-heaps/heaps.cc", + "936-search-onload/search_onload.cc", + "944-transform-classloaders/classloader.cc", + "945-obsolete-native/obsolete_native.cc", + "980-redefine-object/redefine_object.cc", + "983-source-transform-verify/source_transform.cc", + ], +} + art_cc_test_library { name: "libtiagent", defaults: ["libtiagent-defaults"], @@ -303,6 +316,12 @@ art_cc_test_library { shared_libs: ["libartd"], } +art_cc_test_library { + name: "libctstiagent", + defaults: ["libtiagent-base-defaults"], + export_include_dirs: ["ti-agent"], +} + cc_defaults { name: "libarttest-defaults", defaults: [ diff --git a/test/ti-agent/agent_startup.cc b/test/ti-agent/agent_startup.cc new file mode 100644 index 0000000000..b55db7b3af --- /dev/null +++ b/test/ti-agent/agent_startup.cc @@ -0,0 +1,95 @@ +/* + * 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 "agent_startup.h" + +#include "android-base/logging.h" +#include "android-base/macros.h" + +#include "jni_binder.h" +#include "jvmti_helper.h" + +namespace art { + +static constexpr const char* kMainClass = "Main"; + +static StartCallback gCallback = nullptr; + +// TODO: Check this. This may not work on device. The classloader containing the app's classes +// may not have been created at this point (i.e., if it's not the system classloader). +static void JNICALL VMInitCallback(jvmtiEnv* jvmti_env, + JNIEnv* jni_env, + jthread thread ATTRIBUTE_UNUSED) { + // Bind kMainClass native methods. + BindFunctions(jvmti_env, jni_env, kMainClass); + + if (gCallback != nullptr) { + gCallback(jvmti_env, jni_env); + gCallback = nullptr; + } + + // And delete the jvmtiEnv. + jvmti_env->DisposeEnvironment(); +} + +// Install a phase callback that will bind JNI functions on VMInit. +void BindOnLoad(JavaVM* vm, StartCallback callback) { + // Use a new jvmtiEnv. Otherwise we might collide with table changes. + jvmtiEnv* install_env; + if (vm->GetEnv(reinterpret_cast<void**>(&install_env), JVMTI_VERSION_1_0) != 0) { + LOG(FATAL) << "Could not get jvmtiEnv"; + } + SetAllCapabilities(install_env); + + { + jvmtiEventCallbacks callbacks; + memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); + callbacks.VMInit = VMInitCallback; + + CheckJvmtiError(install_env, install_env->SetEventCallbacks(&callbacks, sizeof(callbacks))); + } + + CheckJvmtiError(install_env, install_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_VM_INIT, + nullptr)); + + gCallback = callback; +} + +// Ensure binding of the Main class when the agent is started through OnAttach. +void BindOnAttach(JavaVM* vm, StartCallback callback) { + // Get a JNIEnv. As the thread is attached, we must not destroy it. + JNIEnv* env; + CHECK_EQ(0, vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) + << "Could not get JNIEnv"; + + jvmtiEnv* jvmti_env; + CHECK_EQ(0, vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) + << "Could not get jvmtiEnv"; + SetAllCapabilities(jvmti_env); + + BindFunctions(jvmti_env, env, kMainClass); + + if (callback != nullptr) { + callback(jvmti_env, env); + } + + if (jvmti_env->DisposeEnvironment() != JVMTI_ERROR_NONE) { + LOG(FATAL) << "Could not dispose temporary jvmtiEnv"; + } +} + +} // namespace art diff --git a/test/ti-agent/agent_startup.h b/test/ti-agent/agent_startup.h new file mode 100644 index 0000000000..49633202ab --- /dev/null +++ b/test/ti-agent/agent_startup.h @@ -0,0 +1,37 @@ +/* + * 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_AGENT_STARTUP_H_ +#define ART_TEST_TI_AGENT_AGENT_STARTUP_H_ + +#include <functional> + +#include "jni.h" +#include "jvmti.h" + +namespace art { + +using StartCallback = void(*)(jvmtiEnv*, JNIEnv*); + +// Ensure binding of the Main class when the agent is started through OnLoad. +void BindOnLoad(JavaVM* vm, StartCallback callback); + +// Ensure binding of the Main class when the agent is started through OnAttach. +void BindOnAttach(JavaVM* vm, StartCallback callback); + +} // namespace art + +#endif // ART_TEST_TI_AGENT_AGENT_STARTUP_H_ diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index c5d75c9468..9e7b75daf2 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -19,6 +19,8 @@ #include "base/logging.h" #include "base/macros.h" + +#include "agent_startup.h" #include "common_helper.h" #include "jni_binder.h" #include "jvmti_helper.h" @@ -42,45 +44,6 @@ struct AgentLib { OnAttach attach; }; -static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env, - JNIEnv* jni_env, - jthread thread ATTRIBUTE_UNUSED) { - // Bind Main native methods. - BindFunctions(jvmti_env, jni_env, "Main"); -} - -// Install a phase callback that will bind JNI functions on VMInit. -bool InstallBindCallback(JavaVM* vm) { - // Use a new jvmtiEnv. Otherwise we might collide with table changes. - jvmtiEnv* install_env; - if (vm->GetEnv(reinterpret_cast<void**>(&install_env), JVMTI_VERSION_1_0) != 0) { - return false; - } - SetAllCapabilities(install_env); - - { - jvmtiEventCallbacks callbacks; - memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); - callbacks.VMInit = VMInitCallback; - - jvmtiError install_error = install_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); - if (install_error != JVMTI_ERROR_NONE) { - return false; - } - } - - { - jvmtiError enable_error = install_env->SetEventNotificationMode(JVMTI_ENABLE, - JVMTI_EVENT_VM_INIT, - nullptr); - if (enable_error != JVMTI_ERROR_NONE) { - return false; - } - } - - return true; -} - // A trivial OnLoad implementation that only initializes the global jvmti_env. static jint MinimalOnLoad(JavaVM* vm, char* options ATTRIBUTE_UNUSED, @@ -156,26 +119,6 @@ static void SetIsJVM(const char* options) { SetJVM(strncmp(options, "jvm", 3) == 0); } -static bool BindFunctionsAttached(JavaVM* vm, const char* class_name) { - // Get a JNIEnv. As the thread is attached, we must not destroy it. - JNIEnv* env; - if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != 0) { - printf("Unable to get JNI env!\n"); - return false; - } - - jvmtiEnv* jenv; - if (vm->GetEnv(reinterpret_cast<void**>(&jenv), JVMTI_VERSION_1_0) != 0) { - printf("Unable to get jvmti env!\n"); - return false; - } - SetAllCapabilities(jenv); - - BindFunctions(jenv, env, class_name); - - return true; -} - } // namespace extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) { @@ -188,9 +131,7 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* SetIsJVM(remaining_options); - if (!InstallBindCallback(vm)) { - return 1; - } + BindOnLoad(vm, nullptr); AgentLib* lib = FindAgent(name_option); OnLoad fn = nullptr; @@ -214,7 +155,7 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void return -1; } - BindFunctionsAttached(vm, "Main"); + BindOnAttach(vm, nullptr); AgentLib* lib = FindAgent(name_option); if (lib == nullptr) { diff --git a/test/ti-agent/jni_binder.cc b/test/ti-agent/jni_binder.cc index efc2af81b1..b66c2c76e9 100644 --- a/test/ti-agent/jni_binder.cc +++ b/test/ti-agent/jni_binder.cc @@ -26,94 +26,16 @@ #include "jvmti_helper.h" #include "scoped_local_ref.h" #include "scoped_utf_chars.h" +#include "ti_utf.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()); + size_t char_count = ti::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); + uint32_t ch = ti::GetUtf16FromUtf8(&cp); if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) { result.push_back(ch); } else if (ch == '.' || ch == '/') { @@ -125,8 +47,8 @@ static std::string MangleForJni(const std::string& s) { } else if (ch == '[') { result += "_3"; } else { - const uint16_t leading = GetLeadingUtf16Char(ch); - const uint32_t trailing = GetTrailingUtf16Char(ch); + const uint16_t leading = ti::GetLeadingUtf16Char(ch); + const uint32_t trailing = ti::GetTrailingUtf16Char(ch); android::base::StringAppendF(&result, "_0%04x", leading); if (trailing != 0) { diff --git a/test/ti-agent/jni_helper.h b/test/ti-agent/jni_helper.h index c48b0c0c4f..0cbc6341fc 100644 --- a/test/ti-agent/jni_helper.h +++ b/test/ti-agent/jni_helper.h @@ -54,6 +54,19 @@ static inline jobjectArray CreateObjectArray(JNIEnv* env, return ret.release(); } +inline bool JniThrowNullPointerException(JNIEnv* env, const char* msg) { + if (env->ExceptionCheck()) { + env->ExceptionClear(); + } + + ScopedLocalRef<jclass> exc_class(env, env->FindClass("java/lang/NullPointerException")); + if (exc_class.get() == nullptr) { + return -1; + } + + return env->ThrowNew(exc_class.get(), msg) == JNI_OK; +} + } // namespace art #endif // ART_TEST_TI_AGENT_JNI_HELPER_H_ diff --git a/test/ti-agent/scoped_local_ref.h b/test/ti-agent/scoped_local_ref.h index 0cd9891199..daa1583457 100644 --- a/test/ti-agent/scoped_local_ref.h +++ b/test/ti-agent/scoped_local_ref.h @@ -35,9 +35,9 @@ class ScopedLocalRef { reset(); } - void reset(T ptr = NULL) { + void reset(T ptr = nullptr) { if (ptr != mLocalRef) { - if (mLocalRef != NULL) { + if (mLocalRef != nullptr) { mEnv->DeleteLocalRef(mLocalRef); } mLocalRef = ptr; @@ -46,7 +46,7 @@ class ScopedLocalRef { T release() __attribute__((warn_unused_result)) { T localRef = mLocalRef; - mLocalRef = NULL; + mLocalRef = nullptr; return localRef; } diff --git a/test/ti-agent/scoped_primitive_array.h b/test/ti-agent/scoped_primitive_array.h new file mode 100644 index 0000000000..1649ed997a --- /dev/null +++ b/test/ti-agent/scoped_primitive_array.h @@ -0,0 +1,154 @@ +/* + * 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_PRIMITIVE_ARRAY_H_ +#define ART_TEST_TI_AGENT_SCOPED_PRIMITIVE_ARRAY_H_ + +#include "jni.h" + +#include "android-base/macros.h" + +#include "jni_helper.h" + +namespace art { + +#ifdef POINTER_TYPE +#error POINTER_TYPE is defined. +#else +#define POINTER_TYPE(T) T* /* NOLINT */ +#endif + +#ifdef REFERENCE_TYPE +#error REFERENCE_TYPE is defined. +#else +#define REFERENCE_TYPE(T) T& /* NOLINT */ +#endif + +// ScopedBooleanArrayRO, ScopedByteArrayRO, ScopedCharArrayRO, ScopedDoubleArrayRO, +// ScopedFloatArrayRO, ScopedIntArrayRO, ScopedLongArrayRO, and ScopedShortArrayRO provide +// convenient read-only access to Java arrays from JNI code. This is cheaper than read-write +// access and should be used by default. +#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(PRIMITIVE_TYPE, NAME) \ + class Scoped ## NAME ## ArrayRO { \ + public: \ + explicit Scoped ## NAME ## ArrayRO(JNIEnv* env) \ + : mEnv(env), mJavaArray(nullptr), mRawArray(nullptr), mSize(0) {} \ + Scoped ## NAME ## ArrayRO(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \ + : mEnv(env) { \ + if (javaArray == nullptr) { \ + mJavaArray = nullptr; \ + mSize = 0; \ + mRawArray = nullptr; \ + JniThrowNullPointerException(env, nullptr); \ + } else { \ + reset(javaArray); \ + } \ + } \ + ~Scoped ## NAME ## ArrayRO() { \ + if (mRawArray != nullptr && mRawArray != mBuffer) { \ + mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, JNI_ABORT); \ + } \ + } \ + void reset(PRIMITIVE_TYPE ## Array javaArray) { \ + mJavaArray = javaArray; \ + mSize = mEnv->GetArrayLength(mJavaArray); \ + if (mSize <= kBufferSize) { \ + mEnv->Get ## NAME ## ArrayRegion(mJavaArray, 0, mSize, mBuffer); \ + mRawArray = mBuffer; \ + } else { \ + mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \ + } \ + } \ + const PRIMITIVE_TYPE* get() const { return mRawArray; } \ + PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \ + const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \ + size_t size() const { return mSize; } \ + private: \ + static constexpr jsize kBufferSize = 1024; \ + JNIEnv* const mEnv; \ + PRIMITIVE_TYPE ## Array mJavaArray; \ + POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \ + jsize mSize; \ + PRIMITIVE_TYPE mBuffer[kBufferSize]; \ + DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRO); \ + } + +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jboolean, Boolean); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jbyte, Byte); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jchar, Char); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jdouble, Double); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jfloat, Float); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jint, Int); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jlong, Long); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jshort, Short); + +#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO + +// ScopedBooleanArrayRW, ScopedByteArrayRW, ScopedCharArrayRW, ScopedDoubleArrayRW, +// ScopedFloatArrayRW, ScopedIntArrayRW, ScopedLongArrayRW, and ScopedShortArrayRW provide +// convenient read-write access to Java arrays from JNI code. These are more expensive, +// since they entail a copy back onto the Java heap, and should only be used when necessary. +#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(PRIMITIVE_TYPE, NAME) \ + class Scoped ## NAME ## ArrayRW { \ + public: \ + explicit Scoped ## NAME ## ArrayRW(JNIEnv* env) \ + : mEnv(env), mJavaArray(nullptr), mRawArray(nullptr) {} \ + Scoped ## NAME ## ArrayRW(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \ + : mEnv(env), mJavaArray(javaArray), mRawArray(nullptr) { \ + if (mJavaArray == nullptr) { \ + JniThrowNullPointerException(env, nullptr); \ + } else { \ + mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \ + } \ + } \ + ~Scoped ## NAME ## ArrayRW() { \ + if (mRawArray) { \ + mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, 0); \ + } \ + } \ + void reset(PRIMITIVE_TYPE ## Array javaArray) { \ + mJavaArray = javaArray; \ + mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \ + } \ + const PRIMITIVE_TYPE* get() const { return mRawArray; } \ + PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \ + const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \ + POINTER_TYPE(PRIMITIVE_TYPE) get() { return mRawArray; } \ + REFERENCE_TYPE(PRIMITIVE_TYPE) operator[](size_t n) { return mRawArray[n]; } \ + size_t size() const { return mEnv->GetArrayLength(mJavaArray); } \ + private: \ + JNIEnv* const mEnv; \ + PRIMITIVE_TYPE ## Array mJavaArray; \ + POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \ + DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRW); \ + } + +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jboolean, Boolean); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jbyte, Byte); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jchar, Char); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jdouble, Double); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jfloat, Float); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jint, Int); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jlong, Long); +INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jshort, Short); + +#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW +#undef POINTER_TYPE +#undef REFERENCE_TYPE + +} // namespace art + +#endif // ART_TEST_TI_AGENT_SCOPED_PRIMITIVE_ARRAY_H_ diff --git a/test/ti-agent/scoped_utf_chars.h b/test/ti-agent/scoped_utf_chars.h index e8c9a11539..422caaf84e 100644 --- a/test/ti-agent/scoped_utf_chars.h +++ b/test/ti-agent/scoped_utf_chars.h @@ -23,6 +23,8 @@ #include "android-base/macros.h" +#include "jni_helper.h" + namespace art { class ScopedUtfChars { @@ -30,7 +32,7 @@ class ScopedUtfChars { ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) { if (s == nullptr) { utf_chars_ = nullptr; - // TODO: JniThrowNullPointerException(env, nullptr); + JniThrowNullPointerException(env, nullptr); } else { utf_chars_ = env->GetStringUTFChars(s, nullptr); } diff --git a/test/ti-agent/ti_macros.h b/test/ti-agent/ti_macros.h new file mode 100644 index 0000000000..d91338324f --- /dev/null +++ b/test/ti-agent/ti_macros.h @@ -0,0 +1,26 @@ +/* + * 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_TI_MACROS_H_ +#define ART_TEST_TI_AGENT_TI_MACROS_H_ + +#include "android-base/macros.h" + +#define FINAL final +#define OVERRIDE override +#define UNREACHABLE __builtin_unreachable + +#endif // ART_TEST_TI_AGENT_TI_MACROS_H_ diff --git a/test/ti-agent/ti_utf.h b/test/ti-agent/ti_utf.h new file mode 100644 index 0000000000..341e1066c3 --- /dev/null +++ b/test/ti-agent/ti_utf.h @@ -0,0 +1,196 @@ +/* + * 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_TI_UTF_H_ +#define ART_TEST_TI_AGENT_TI_UTF_H_ + +#include <inttypes.h> +#include <string.h> + +#include "android-base/logging.h" + +namespace art { +namespace ti { + +inline 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; +} + +inline uint16_t GetTrailingUtf16Char(uint32_t maybe_pair) { + return static_cast<uint16_t>(maybe_pair >> 16); +} + +inline uint16_t GetLeadingUtf16Char(uint32_t maybe_pair) { + return static_cast<uint16_t>(maybe_pair & 0x0000FFFF); +} + +inline 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; +} + +inline void ConvertUtf16ToModifiedUtf8(char* utf8_out, + size_t byte_count, + const uint16_t* utf16_in, + size_t char_count) { + if (LIKELY(byte_count == char_count)) { + // Common case where all characters are ASCII. + const uint16_t *utf16_end = utf16_in + char_count; + for (const uint16_t *p = utf16_in; p < utf16_end;) { + *utf8_out++ = static_cast<char>(*p++); + } + return; + } + + // String contains non-ASCII characters. + while (char_count--) { + const uint16_t ch = *utf16_in++; + if (ch > 0 && ch <= 0x7f) { + *utf8_out++ = ch; + } else { + // Char_count == 0 here implies we've encountered an unpaired + // surrogate and we have no choice but to encode it as 3-byte UTF + // sequence. Note that unpaired surrogates can occur as a part of + // "normal" operation. + if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) { + const uint16_t ch2 = *utf16_in; + + // Check if the other half of the pair is within the expected + // range. If it isn't, we will have to emit both "halves" as + // separate 3 byte sequences. + if (ch2 >= 0xdc00 && ch2 <= 0xdfff) { + utf16_in++; + char_count--; + const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00; + *utf8_out++ = (code_point >> 18) | 0xf0; + *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80; + *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80; + *utf8_out++ = (code_point & 0x3f) | 0x80; + continue; + } + } + + if (ch > 0x07ff) { + // Three byte encoding. + *utf8_out++ = (ch >> 12) | 0xe0; + *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80; + *utf8_out++ = (ch & 0x3f) | 0x80; + } else /*(ch > 0x7f || ch == 0)*/ { + // Two byte encoding. + *utf8_out++ = (ch >> 6) | 0xc0; + *utf8_out++ = (ch & 0x3f) | 0x80; + } + } + } +} + +inline size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) { + size_t result = 0; + const uint16_t *end = chars + char_count; + while (chars < end) { + const uint16_t ch = *chars++; + if (LIKELY(ch != 0 && ch < 0x80)) { + result++; + continue; + } + if (ch < 0x800) { + result += 2; + continue; + } + if (ch >= 0xd800 && ch < 0xdc00) { + if (chars < end) { + const uint16_t ch2 = *chars; + // If we find a properly paired surrogate, we emit it as a 4 byte + // UTF sequence. If we find an unpaired leading or trailing surrogate, + // we emit it as a 3 byte sequence like would have done earlier. + if (ch2 >= 0xdc00 && ch2 < 0xe000) { + chars++; + result += 4; + continue; + } + } + } + result += 3; + } + return result; +} + +} // namespace ti +} // namespace art + +#endif // ART_TEST_TI_AGENT_TI_UTF_H_ |