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 }