Home | History | Annotate | Download | only in ti-agent
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "common_helper.h"
     18 
     19 #include "jni.h"
     20 #include "jvmti.h"
     21 
     22 #include "jvmti_helper.h"
     23 #include "scoped_local_ref.h"
     24 #include "test_env.h"
     25 
     26 namespace art {
     27 
     28 namespace common_breakpoint {
     29 
     30 struct BreakpointData {
     31   jclass test_klass;
     32   jmethodID breakpoint_method;
     33   bool in_callback;
     34   bool allow_recursive;
     35 };
     36 
     37 extern "C" void breakpointCB(jvmtiEnv* jvmti,
     38                              JNIEnv* jnienv,
     39                              jthread thread,
     40                              jmethodID method,
     41                              jlocation location) {
     42   BreakpointData* data = nullptr;
     43   if (JvmtiErrorToException(jnienv, jvmti,
     44                             jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
     45     return;
     46   }
     47   if (data->in_callback && !data->allow_recursive) {
     48     return;
     49   }
     50   data->in_callback = true;
     51   jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
     52   jnienv->CallStaticVoidMethod(data->test_klass,
     53                                data->breakpoint_method,
     54                                thread,
     55                                method_arg,
     56                                static_cast<jlong>(location));
     57   jnienv->DeleteLocalRef(method_arg);
     58   data->in_callback = false;
     59 }
     60 
     61 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Breakpoint_getLineNumberTableNative(
     62     JNIEnv* env,
     63     jclass k ATTRIBUTE_UNUSED,
     64     jobject target) {
     65   jmethodID method = env->FromReflectedMethod(target);
     66   if (env->ExceptionCheck()) {
     67     return nullptr;
     68   }
     69   jint nlines;
     70   jvmtiLineNumberEntry* lines = nullptr;
     71   if (JvmtiErrorToException(env, jvmti_env,
     72                             jvmti_env->GetLineNumberTable(method, &nlines, &lines))) {
     73     return nullptr;
     74   }
     75   jintArray lines_array = env->NewIntArray(nlines);
     76   if (env->ExceptionCheck()) {
     77     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
     78     return nullptr;
     79   }
     80   jlongArray locs_array = env->NewLongArray(nlines);
     81   if (env->ExceptionCheck()) {
     82     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
     83     return nullptr;
     84   }
     85   ScopedLocalRef<jclass> object_class(env, env->FindClass("java/lang/Object"));
     86   if (env->ExceptionCheck()) {
     87     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
     88     return nullptr;
     89   }
     90   jobjectArray ret = env->NewObjectArray(2, object_class.get(), nullptr);
     91   if (env->ExceptionCheck()) {
     92     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
     93     return nullptr;
     94   }
     95   jint* temp_lines = env->GetIntArrayElements(lines_array, /*isCopy*/nullptr);
     96   jlong* temp_locs = env->GetLongArrayElements(locs_array, /*isCopy*/nullptr);
     97   for (jint i = 0; i < nlines; i++) {
     98     temp_lines[i] = lines[i].line_number;
     99     temp_locs[i] = lines[i].start_location;
    100   }
    101   env->ReleaseIntArrayElements(lines_array, temp_lines, 0);
    102   env->ReleaseLongArrayElements(locs_array, temp_locs, 0);
    103   env->SetObjectArrayElement(ret, 0, locs_array);
    104   env->SetObjectArrayElement(ret, 1, lines_array);
    105   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
    106   return ret;
    107 }
    108 
    109 extern "C" JNIEXPORT jlong JNICALL Java_art_Breakpoint_getStartLocation(JNIEnv* env,
    110                                                                         jclass k ATTRIBUTE_UNUSED,
    111                                                                         jobject target) {
    112   jmethodID method = env->FromReflectedMethod(target);
    113   if (env->ExceptionCheck()) {
    114     return 0;
    115   }
    116   jlong start = 0;
    117   jlong end = end;
    118   JvmtiErrorToException(env, jvmti_env, jvmti_env->GetMethodLocation(method, &start, &end));
    119   return start;
    120 }
    121 
    122 extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_clearBreakpoint(JNIEnv* env,
    123                                                                       jclass k ATTRIBUTE_UNUSED,
    124                                                                       jobject target,
    125                                                                       jlocation location) {
    126   jmethodID method = env->FromReflectedMethod(target);
    127   if (env->ExceptionCheck()) {
    128     return;
    129   }
    130   JvmtiErrorToException(env, jvmti_env, jvmti_env->ClearBreakpoint(method, location));
    131 }
    132 
    133 extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_setBreakpoint(JNIEnv* env,
    134                                                                     jclass k ATTRIBUTE_UNUSED,
    135                                                                     jobject target,
    136                                                                     jlocation location) {
    137   jmethodID method = env->FromReflectedMethod(target);
    138   if (env->ExceptionCheck()) {
    139     return;
    140   }
    141   JvmtiErrorToException(env, jvmti_env, jvmti_env->SetBreakpoint(method, location));
    142 }
    143 
    144 extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_startBreakpointWatch(
    145     JNIEnv* env,
    146     jclass k ATTRIBUTE_UNUSED,
    147     jclass method_klass,
    148     jobject method,
    149     jboolean allow_recursive,
    150     jthread thr) {
    151   BreakpointData* data = nullptr;
    152   if (JvmtiErrorToException(env,
    153                             jvmti_env,
    154                             jvmti_env->Allocate(sizeof(BreakpointData),
    155                                                 reinterpret_cast<unsigned char**>(&data)))) {
    156     return;
    157   }
    158   memset(data, 0, sizeof(BreakpointData));
    159   data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(method_klass));
    160   data->breakpoint_method = env->FromReflectedMethod(method);
    161   data->in_callback = false;
    162   data->allow_recursive = allow_recursive;
    163 
    164   void* old_data = nullptr;
    165   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
    166     return;
    167   } else if (old_data != nullptr) {
    168     ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
    169     env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
    170     return;
    171   }
    172   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
    173     return;
    174   }
    175   current_callbacks.Breakpoint = breakpointCB;
    176   if (JvmtiErrorToException(env,
    177                             jvmti_env,
    178                             jvmti_env->SetEventCallbacks(&current_callbacks,
    179                                                          sizeof(current_callbacks)))) {
    180     return;
    181   }
    182   if (JvmtiErrorToException(env,
    183                             jvmti_env,
    184                             jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    185                                                                 JVMTI_EVENT_BREAKPOINT,
    186                                                                 thr))) {
    187     return;
    188   }
    189 }
    190 
    191 extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_stopBreakpointWatch(
    192     JNIEnv* env,
    193     jclass k ATTRIBUTE_UNUSED,
    194     jthread thr) {
    195   if (JvmtiErrorToException(env, jvmti_env,
    196                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    197                                                                 JVMTI_EVENT_BREAKPOINT,
    198                                                                 thr))) {
    199     return;
    200   }
    201 }
    202 
    203 }  // namespace common_breakpoint
    204 
    205 }  // namespace art
    206