| /* |
| * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * - Neither the name of Oracle nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* Tracker class support functions. */ |
| |
| /* |
| * This file contains the native support calls for the Tracker |
| * class. These native methods are registered and not made extern. |
| * Tracking is engaged by using JNI to assign to a static field in the |
| * Tracker class. |
| * |
| * Just like JVMTI callbacks, it's best that we keep track of these so that |
| * when the VM_DEATH happens we know to wait for them to complete. |
| * |
| * This file also contains the functions that will initialize the Tracker |
| * interface for BCI and identify the Tracker methods to make sure |
| * they are not included in any stack traces obtained from JVMTI. |
| * |
| * RFE: The performance of the java injected code calling native methods |
| * could be an issue here, cpu=times seems to be the worst where |
| * a native call is made for entry and exit, even on the smallest |
| * Java method. The alternative would be to cache the data on |
| * the Java side, and either push it out to the native side, or |
| * use some kind of pull from the native side, or even using |
| * shared memory or a socket. However having said that, the |
| * current performance issues are more around sheer memory needed, |
| * and repeated calls to GetThreadCpuTime(), which is being investigated. |
| * |
| */ |
| |
| #include "hprof.h" |
| |
| /* Macros to surround tracker based callback code. |
| * Also see BEGIN_CALLBACK and END_CALLBACK in hprof_init.c. |
| * If the VM_DEATH callback is active in the begining, then this callback |
| * just blocks (it is assumed we don't want to return to the VM). |
| * If the VM_DEATH callback is active at the end, then this callback |
| * will notify the VM_DEATH callback if it's the last one. |
| * |
| * WARNING: No not 'return' or 'goto' out of the BEGIN_TRACKER_CALLBACK/END_TRACKER_CALLBACK |
| * block, this will mess up the count. |
| */ |
| |
| #define BEGIN_TRACKER_CALLBACK() \ |
| { /* BEGIN OF TRACKER_CALLBACK */ \ |
| jboolean bypass = JNI_TRUE; \ |
| rawMonitorEnter(gdata->callbackLock); { \ |
| if ( gdata->tracking_engaged != 0 ) { \ |
| if (!gdata->vm_death_callback_active) { \ |
| gdata->active_callbacks++; \ |
| bypass = JNI_FALSE; \ |
| } \ |
| } \ |
| } rawMonitorExit(gdata->callbackLock); \ |
| if ( !bypass ) { \ |
| /* BODY OF TRACKER_CALLBACK CODE */ |
| |
| #define END_TRACKER_CALLBACK() /* Part of bypass if body */ \ |
| rawMonitorEnter(gdata->callbackLock); { \ |
| gdata->active_callbacks--; \ |
| if (gdata->active_callbacks < 0) { \ |
| HPROF_ERROR(JNI_TRUE, "Problems tracking callbacks"); \ |
| } \ |
| if (gdata->vm_death_callback_active) { \ |
| if (gdata->active_callbacks == 0) { \ |
| rawMonitorNotifyAll(gdata->callbackLock); \ |
| } \ |
| } \ |
| } rawMonitorExit(gdata->callbackLock); \ |
| } \ |
| } /* END OF TRACKER_CALLBACK */ |
| |
| |
| /* |
| * Class: Tracker |
| * Method: nativeNewArray |
| * Signature: (Ljava/lang/Object;Ljava/lang/Object;)V |
| */ |
| static void JNICALL |
| Tracker_nativeNewArray |
| (JNIEnv *env, jclass clazz, jobject thread, jobject obj) |
| { |
| BEGIN_TRACKER_CALLBACK() { |
| event_newarray(env, thread, obj); |
| } END_TRACKER_CALLBACK(); |
| } |
| |
| /* |
| * Class: Tracker |
| * Method: nativeObjectInit |
| * Signature: (Ljava/lang/Object;Ljava/lang/Object;)V |
| */ |
| static void JNICALL |
| Tracker_nativeObjectInit |
| (JNIEnv *env, jclass clazz, jobject thread, jobject obj) |
| { |
| BEGIN_TRACKER_CALLBACK() { |
| event_object_init(env, thread, obj); |
| } END_TRACKER_CALLBACK(); |
| } |
| |
| /* |
| * Class: Tracker |
| * Method: nativeCallSite |
| * Signature: (Ljava/lang/Object;II)V |
| */ |
| static void JNICALL |
| Tracker_nativeCallSite |
| (JNIEnv *env, jclass clazz, jobject thread, jint cnum, jint mnum) |
| { |
| BEGIN_TRACKER_CALLBACK() { |
| event_call(env, thread, cnum, mnum); |
| } END_TRACKER_CALLBACK(); |
| } |
| |
| /* |
| * Class: Tracker |
| * Method: nativeReturnSite |
| * Signature: (Ljava/lang/Object;II)V |
| */ |
| static void JNICALL |
| Tracker_nativeReturnSite |
| (JNIEnv *env, jclass clazz, jobject thread, jint cnum, jint mnum) |
| { |
| BEGIN_TRACKER_CALLBACK() { |
| event_return(env, thread, cnum, mnum); |
| } END_TRACKER_CALLBACK(); |
| } |
| |
| |
| /* ------------------------------------------------------------------- */ |
| /* Set Java static field to turn on native code calls in Tracker. */ |
| |
| static void |
| set_engaged(JNIEnv *env, jint engaged) |
| { |
| LOG3("set_engaged()", "engaging tracking", engaged); |
| |
| if ( ! gdata->bci ) { |
| return; |
| } |
| rawMonitorEnter(gdata->callbackLock); { |
| if ( gdata->tracking_engaged != engaged ) { |
| jfieldID field; |
| jclass tracker_class; |
| |
| tracker_class = class_get_class(env, gdata->tracker_cnum); |
| gdata->tracking_engaged = 0; |
| /* Activate or deactivate the injection code on the Java side */ |
| HPROF_ASSERT(tracker_class!=NULL); |
| exceptionClear(env); |
| field = getStaticFieldID(env, tracker_class, |
| TRACKER_ENGAGED_NAME, TRACKER_ENGAGED_SIG); |
| setStaticIntField(env, tracker_class, field, engaged); |
| exceptionClear(env); |
| |
| LOG3("set_engaged()", "tracking engaged", engaged); |
| |
| gdata->tracking_engaged = engaged; |
| } |
| } rawMonitorExit(gdata->callbackLock); |
| } |
| |
| void |
| tracker_engage(JNIEnv *env) |
| { |
| set_engaged(env, 0xFFFF); |
| } |
| |
| void |
| tracker_disengage(JNIEnv *env) |
| { |
| set_engaged(env, 0); |
| } |
| |
| jboolean |
| tracker_method(jmethodID method) |
| { |
| int i; |
| |
| if ( ! gdata->bci ) { |
| return JNI_FALSE; |
| } |
| |
| HPROF_ASSERT(method!=NULL); |
| HPROF_ASSERT(gdata->tracker_method_count > 0); |
| for ( i = 0 ; i < gdata->tracker_method_count ; i++ ) { |
| HPROF_ASSERT(gdata->tracker_methods[i].method!=NULL); |
| if ( method == gdata->tracker_methods[i].method ) { |
| return JNI_TRUE; |
| } |
| } |
| return JNI_FALSE; |
| } |
| |
| static JNINativeMethod registry[4] = |
| { |
| { TRACKER_NEWARRAY_NATIVE_NAME, TRACKER_NEWARRAY_NATIVE_SIG, |
| (void*)&Tracker_nativeNewArray }, |
| { TRACKER_OBJECT_INIT_NATIVE_NAME, TRACKER_OBJECT_INIT_NATIVE_SIG, |
| (void*)&Tracker_nativeObjectInit }, |
| { TRACKER_CALL_NATIVE_NAME, TRACKER_CALL_NATIVE_SIG, |
| (void*)&Tracker_nativeCallSite }, |
| { TRACKER_RETURN_NATIVE_NAME, TRACKER_RETURN_NATIVE_SIG, |
| (void*)&Tracker_nativeReturnSite } |
| }; |
| |
| static struct { |
| char *name; |
| char *sig; |
| } tracker_methods[] = |
| { |
| { TRACKER_NEWARRAY_NAME, TRACKER_NEWARRAY_SIG }, |
| { TRACKER_OBJECT_INIT_NAME, TRACKER_OBJECT_INIT_SIG }, |
| { TRACKER_CALL_NAME, TRACKER_CALL_SIG }, |
| { TRACKER_RETURN_NAME, TRACKER_RETURN_SIG }, |
| { TRACKER_NEWARRAY_NATIVE_NAME, TRACKER_NEWARRAY_NATIVE_SIG }, |
| { TRACKER_OBJECT_INIT_NATIVE_NAME, TRACKER_OBJECT_INIT_NATIVE_SIG }, |
| { TRACKER_CALL_NATIVE_NAME, TRACKER_CALL_NATIVE_SIG }, |
| { TRACKER_RETURN_NATIVE_NAME, TRACKER_RETURN_NATIVE_SIG } |
| }; |
| |
| void |
| tracker_setup_class(void) |
| { |
| ClassIndex cnum; |
| LoaderIndex loader_index; |
| |
| HPROF_ASSERT(gdata->tracker_cnum==0); |
| loader_index = loader_find_or_create(NULL,NULL); |
| cnum = class_find_or_create(TRACKER_CLASS_SIG, loader_index); |
| gdata->tracker_cnum = cnum; |
| HPROF_ASSERT(cnum!=0); |
| class_add_status(cnum, CLASS_SPECIAL); |
| } |
| |
| void |
| tracker_setup_methods(JNIEnv *env) |
| { |
| ClassIndex cnum; |
| LoaderIndex loader_index; |
| int i; |
| jclass object_class; |
| jclass tracker_class; |
| |
| if ( ! gdata->bci ) { |
| return; |
| } |
| |
| loader_index = loader_find_or_create(NULL,NULL); |
| cnum = class_find_or_create(OBJECT_CLASS_SIG, loader_index); |
| object_class = class_get_class(env, cnum); |
| tracker_class = class_get_class(env, gdata->tracker_cnum); |
| |
| CHECK_EXCEPTIONS(env) { |
| registerNatives(env, tracker_class, registry, |
| (int)sizeof(registry)/(int)sizeof(registry[0])); |
| } END_CHECK_EXCEPTIONS; |
| |
| HPROF_ASSERT(tracker_class!=NULL); |
| |
| gdata->tracker_method_count = |
| (int)sizeof(tracker_methods)/(int)sizeof(tracker_methods[0]); |
| |
| HPROF_ASSERT(gdata->tracker_method_count <= |
| (int)(sizeof(gdata->tracker_methods)/sizeof(gdata->tracker_methods[0]))); |
| |
| CHECK_EXCEPTIONS(env) { |
| gdata->object_init_method = getMethodID(env, object_class, |
| OBJECT_INIT_NAME, OBJECT_INIT_SIG); |
| for ( i=0 ; i < gdata->tracker_method_count ; i++ ) { |
| gdata->tracker_methods[i].name = |
| string_find_or_create(tracker_methods[i].name); |
| gdata->tracker_methods[i].sig = |
| string_find_or_create(tracker_methods[i].sig); |
| gdata->tracker_methods[i].method = |
| getStaticMethodID(env, tracker_class, |
| tracker_methods[i].name, tracker_methods[i].sig); |
| HPROF_ASSERT(gdata->tracker_methods[i].method!=NULL); |
| LOG2("tracker_setup_methods(): Found", tracker_methods[i].name); |
| } |
| } END_CHECK_EXCEPTIONS; |
| } |