1 /* 2 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of Oracle nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * This source code is provided to illustrate the usage of a given feature 34 * or technique and has been deliberately simplified. Additional steps 35 * required for a production-quality application, such as security checks, 36 * input validation and proper error handling, might not be present in 37 * this sample code. 38 */ 39 40 41 /* Tracker class support functions. */ 42 43 /* 44 * This file contains the native support calls for the Tracker 45 * class. These native methods are registered and not made extern. 46 * Tracking is engaged by using JNI to assign to a static field in the 47 * Tracker class. 48 * 49 * Just like JVMTI callbacks, it's best that we keep track of these so that 50 * when the VM_DEATH happens we know to wait for them to complete. 51 * 52 * This file also contains the functions that will initialize the Tracker 53 * interface for BCI and identify the Tracker methods to make sure 54 * they are not included in any stack traces obtained from JVMTI. 55 * 56 * RFE: The performance of the java injected code calling native methods 57 * could be an issue here, cpu=times seems to be the worst where 58 * a native call is made for entry and exit, even on the smallest 59 * Java method. The alternative would be to cache the data on 60 * the Java side, and either push it out to the native side, or 61 * use some kind of pull from the native side, or even using 62 * shared memory or a socket. However having said that, the 63 * current performance issues are more around sheer memory needed, 64 * and repeated calls to GetThreadCpuTime(), which is being investigated. 65 * 66 */ 67 68 #include "hprof.h" 69 70 /* Macros to surround tracker based callback code. 71 * Also see BEGIN_CALLBACK and END_CALLBACK in hprof_init.c. 72 * If the VM_DEATH callback is active in the begining, then this callback 73 * just blocks (it is assumed we don't want to return to the VM). 74 * If the VM_DEATH callback is active at the end, then this callback 75 * will notify the VM_DEATH callback if it's the last one. 76 * 77 * WARNING: No not 'return' or 'goto' out of the BEGIN_TRACKER_CALLBACK/END_TRACKER_CALLBACK 78 * block, this will mess up the count. 79 */ 80 81 #define BEGIN_TRACKER_CALLBACK() \ 82 { /* BEGIN OF TRACKER_CALLBACK */ \ 83 jboolean bypass = JNI_TRUE; \ 84 rawMonitorEnter(gdata->callbackLock); { \ 85 if ( gdata->tracking_engaged != 0 ) { \ 86 if (!gdata->vm_death_callback_active) { \ 87 gdata->active_callbacks++; \ 88 bypass = JNI_FALSE; \ 89 } \ 90 } \ 91 } rawMonitorExit(gdata->callbackLock); \ 92 if ( !bypass ) { \ 93 /* BODY OF TRACKER_CALLBACK CODE */ 94 95 #define END_TRACKER_CALLBACK() /* Part of bypass if body */ \ 96 rawMonitorEnter(gdata->callbackLock); { \ 97 gdata->active_callbacks--; \ 98 if (gdata->active_callbacks < 0) { \ 99 HPROF_ERROR(JNI_TRUE, "Problems tracking callbacks"); \ 100 } \ 101 if (gdata->vm_death_callback_active) { \ 102 if (gdata->active_callbacks == 0) { \ 103 rawMonitorNotifyAll(gdata->callbackLock); \ 104 } \ 105 } \ 106 } rawMonitorExit(gdata->callbackLock); \ 107 } \ 108 } /* END OF TRACKER_CALLBACK */ 109 110 111 /* 112 * Class: Tracker 113 * Method: nativeNewArray 114 * Signature: (Ljava/lang/Object;Ljava/lang/Object;)V 115 */ 116 static void JNICALL 117 Tracker_nativeNewArray 118 (JNIEnv *env, jclass clazz, jobject thread, jobject obj) 119 { 120 BEGIN_TRACKER_CALLBACK() { 121 event_newarray(env, thread, obj); 122 } END_TRACKER_CALLBACK(); 123 } 124 125 /* 126 * Class: Tracker 127 * Method: nativeObjectInit 128 * Signature: (Ljava/lang/Object;Ljava/lang/Object;)V 129 */ 130 static void JNICALL 131 Tracker_nativeObjectInit 132 (JNIEnv *env, jclass clazz, jobject thread, jobject obj) 133 { 134 BEGIN_TRACKER_CALLBACK() { 135 event_object_init(env, thread, obj); 136 } END_TRACKER_CALLBACK(); 137 } 138 139 /* 140 * Class: Tracker 141 * Method: nativeCallSite 142 * Signature: (Ljava/lang/Object;II)V 143 */ 144 static void JNICALL 145 Tracker_nativeCallSite 146 (JNIEnv *env, jclass clazz, jobject thread, jint cnum, jint mnum) 147 { 148 BEGIN_TRACKER_CALLBACK() { 149 event_call(env, thread, cnum, mnum); 150 } END_TRACKER_CALLBACK(); 151 } 152 153 /* 154 * Class: Tracker 155 * Method: nativeReturnSite 156 * Signature: (Ljava/lang/Object;II)V 157 */ 158 static void JNICALL 159 Tracker_nativeReturnSite 160 (JNIEnv *env, jclass clazz, jobject thread, jint cnum, jint mnum) 161 { 162 BEGIN_TRACKER_CALLBACK() { 163 event_return(env, thread, cnum, mnum); 164 } END_TRACKER_CALLBACK(); 165 } 166 167 168 /* ------------------------------------------------------------------- */ 169 /* Set Java static field to turn on native code calls in Tracker. */ 170 171 static void 172 set_engaged(JNIEnv *env, jint engaged) 173 { 174 LOG3("set_engaged()", "engaging tracking", engaged); 175 176 if ( ! gdata->bci ) { 177 return; 178 } 179 rawMonitorEnter(gdata->callbackLock); { 180 if ( gdata->tracking_engaged != engaged ) { 181 jfieldID field; 182 jclass tracker_class; 183 184 tracker_class = class_get_class(env, gdata->tracker_cnum); 185 gdata->tracking_engaged = 0; 186 /* Activate or deactivate the injection code on the Java side */ 187 HPROF_ASSERT(tracker_class!=NULL); 188 exceptionClear(env); 189 field = getStaticFieldID(env, tracker_class, 190 TRACKER_ENGAGED_NAME, TRACKER_ENGAGED_SIG); 191 setStaticIntField(env, tracker_class, field, engaged); 192 exceptionClear(env); 193 194 LOG3("set_engaged()", "tracking engaged", engaged); 195 196 gdata->tracking_engaged = engaged; 197 } 198 } rawMonitorExit(gdata->callbackLock); 199 } 200 201 void 202 tracker_engage(JNIEnv *env) 203 { 204 set_engaged(env, 0xFFFF); 205 } 206 207 void 208 tracker_disengage(JNIEnv *env) 209 { 210 set_engaged(env, 0); 211 } 212 213 jboolean 214 tracker_method(jmethodID method) 215 { 216 int i; 217 218 if ( ! gdata->bci ) { 219 return JNI_FALSE; 220 } 221 222 HPROF_ASSERT(method!=NULL); 223 HPROF_ASSERT(gdata->tracker_method_count > 0); 224 for ( i = 0 ; i < gdata->tracker_method_count ; i++ ) { 225 HPROF_ASSERT(gdata->tracker_methods[i].method!=NULL); 226 if ( method == gdata->tracker_methods[i].method ) { 227 return JNI_TRUE; 228 } 229 } 230 return JNI_FALSE; 231 } 232 233 static JNINativeMethod registry[4] = 234 { 235 { TRACKER_NEWARRAY_NATIVE_NAME, TRACKER_NEWARRAY_NATIVE_SIG, 236 (void*)&Tracker_nativeNewArray }, 237 { TRACKER_OBJECT_INIT_NATIVE_NAME, TRACKER_OBJECT_INIT_NATIVE_SIG, 238 (void*)&Tracker_nativeObjectInit }, 239 { TRACKER_CALL_NATIVE_NAME, TRACKER_CALL_NATIVE_SIG, 240 (void*)&Tracker_nativeCallSite }, 241 { TRACKER_RETURN_NATIVE_NAME, TRACKER_RETURN_NATIVE_SIG, 242 (void*)&Tracker_nativeReturnSite } 243 }; 244 245 static struct { 246 char *name; 247 char *sig; 248 } tracker_methods[] = 249 { 250 { TRACKER_NEWARRAY_NAME, TRACKER_NEWARRAY_SIG }, 251 { TRACKER_OBJECT_INIT_NAME, TRACKER_OBJECT_INIT_SIG }, 252 { TRACKER_CALL_NAME, TRACKER_CALL_SIG }, 253 { TRACKER_RETURN_NAME, TRACKER_RETURN_SIG }, 254 { TRACKER_NEWARRAY_NATIVE_NAME, TRACKER_NEWARRAY_NATIVE_SIG }, 255 { TRACKER_OBJECT_INIT_NATIVE_NAME, TRACKER_OBJECT_INIT_NATIVE_SIG }, 256 { TRACKER_CALL_NATIVE_NAME, TRACKER_CALL_NATIVE_SIG }, 257 { TRACKER_RETURN_NATIVE_NAME, TRACKER_RETURN_NATIVE_SIG } 258 }; 259 260 void 261 tracker_setup_class(void) 262 { 263 ClassIndex cnum; 264 LoaderIndex loader_index; 265 266 HPROF_ASSERT(gdata->tracker_cnum==0); 267 loader_index = loader_find_or_create(NULL,NULL); 268 cnum = class_find_or_create(TRACKER_CLASS_SIG, loader_index); 269 gdata->tracker_cnum = cnum; 270 HPROF_ASSERT(cnum!=0); 271 class_add_status(cnum, CLASS_SPECIAL); 272 } 273 274 void 275 tracker_setup_methods(JNIEnv *env) 276 { 277 ClassIndex cnum; 278 LoaderIndex loader_index; 279 int i; 280 jclass object_class; 281 jclass tracker_class; 282 283 if ( ! gdata->bci ) { 284 return; 285 } 286 287 loader_index = loader_find_or_create(NULL,NULL); 288 cnum = class_find_or_create(OBJECT_CLASS_SIG, loader_index); 289 object_class = class_get_class(env, cnum); 290 tracker_class = class_get_class(env, gdata->tracker_cnum); 291 292 CHECK_EXCEPTIONS(env) { 293 registerNatives(env, tracker_class, registry, 294 (int)sizeof(registry)/(int)sizeof(registry[0])); 295 } END_CHECK_EXCEPTIONS; 296 297 HPROF_ASSERT(tracker_class!=NULL); 298 299 gdata->tracker_method_count = 300 (int)sizeof(tracker_methods)/(int)sizeof(tracker_methods[0]); 301 302 HPROF_ASSERT(gdata->tracker_method_count <= 303 (int)(sizeof(gdata->tracker_methods)/sizeof(gdata->tracker_methods[0]))); 304 305 CHECK_EXCEPTIONS(env) { 306 gdata->object_init_method = getMethodID(env, object_class, 307 OBJECT_INIT_NAME, OBJECT_INIT_SIG); 308 for ( i=0 ; i < gdata->tracker_method_count ; i++ ) { 309 gdata->tracker_methods[i].name = 310 string_find_or_create(tracker_methods[i].name); 311 gdata->tracker_methods[i].sig = 312 string_find_or_create(tracker_methods[i].sig); 313 gdata->tracker_methods[i].method = 314 getStaticMethodID(env, tracker_class, 315 tracker_methods[i].name, tracker_methods[i].sig); 316 HPROF_ASSERT(gdata->tracker_methods[i].method!=NULL); 317 LOG2("tracker_setup_methods(): Found", tracker_methods[i].name); 318 } 319 } END_CHECK_EXCEPTIONS; 320 }