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 #include "hprof.h"
  42 
  43 /* This file contains the cpu loop for the option cpu=samples */
  44 
  45 /* The cpu_loop thread basically waits for gdata->sample_interval millisecs
  46  *   then wakes up, and for each running thread it gets their stack trace,
  47  *   and updates the traces with 'hits'.
  48  *
  49  * No threads are suspended or resumed, and the thread sampling is in the
  50  *   file hprof_tls.c, which manages all active threads. The sampling
  51  *   technique (what is sampled) is also in hprof_tls.c.
  52  *
  53  * No adjustments are made to the pause time or sample interval except
  54  *   by the user via the interval=n option (default is 10ms).
  55  *
  56  * This thread can cause havoc when started prematurely or not terminated
  57  *   properly, see cpu_sample_init() and cpu_term(), and their calls in hprof_init.c.
  58  *
  59  * The listener loop (hprof_listener.c) can dynamically turn on or off the
  60  *  sampling of all or selected threads.
  61  *
  62  */
  63 
  64 /* Private functions */
  65 
  66 static void JNICALL
  67 cpu_loop_function(jvmtiEnv *jvmti, JNIEnv *env, void *p)
  68 {
  69     int         loop_trip_counter;
  70     jboolean    cpu_loop_running;
  71 
  72     loop_trip_counter          = 0;
  73 
  74     rawMonitorEnter(gdata->cpu_loop_lock); {
  75         gdata->cpu_loop_running = JNI_TRUE;
  76         cpu_loop_running = gdata->cpu_loop_running;
  77         /* Notify cpu_sample_init() that we have started */
  78         rawMonitorNotifyAll(gdata->cpu_loop_lock);
  79     } rawMonitorExit(gdata->cpu_loop_lock);
  80 
  81     rawMonitorEnter(gdata->cpu_sample_lock); /* Only waits inside loop let go */
  82 
  83     while ( cpu_loop_running ) {
  84 
  85         ++loop_trip_counter;
  86 
  87         LOG3("cpu_loop()", "iteration", loop_trip_counter);
  88 
  89         /* If a dump is in progress, we pause sampling. */
  90         rawMonitorEnter(gdata->dump_lock); {
  91             if (gdata->dump_in_process) {
  92                 gdata->pause_cpu_sampling = JNI_TRUE;
  93             }
  94         } rawMonitorExit(gdata->dump_lock);
  95 
  96         /* Check to see if we need to pause sampling (listener_loop command) */
  97         if (gdata->pause_cpu_sampling) {
  98 
  99             /*
 100              * Pause sampling for now. Reset sample controls if
 101              * sampling is resumed again.
 102              */
 103 
 104             rawMonitorWait(gdata->cpu_sample_lock, 0);
 105 
 106             rawMonitorEnter(gdata->cpu_loop_lock); {
 107                 cpu_loop_running = gdata->cpu_loop_running;
 108             } rawMonitorExit(gdata->cpu_loop_lock);
 109 
 110             /* Continue the while loop, which will terminate if done. */
 111             continue;
 112         }
 113 
 114         /* This is the normal short timed wait before getting a sample */
 115         rawMonitorWait(gdata->cpu_sample_lock,  (jlong)gdata->sample_interval);
 116 
 117         /* Make sure we really want to continue */
 118         rawMonitorEnter(gdata->cpu_loop_lock); {
 119             cpu_loop_running = gdata->cpu_loop_running;
 120         } rawMonitorExit(gdata->cpu_loop_lock);
 121 
 122         /* Break out if we are done */
 123         if ( !cpu_loop_running ) {
 124             break;
 125         }
 126 
 127         /*
 128          * If a dump request came in after we checked at the top of
 129          * the while loop, then we catch that fact here. We
 130          * don't want to perturb the data that is being dumped so
 131          * we just ignore the data from this sampling loop.
 132          */
 133         rawMonitorEnter(gdata->dump_lock); {
 134             if (gdata->dump_in_process) {
 135                 gdata->pause_cpu_sampling = JNI_TRUE;
 136             }
 137         } rawMonitorExit(gdata->dump_lock);
 138 
 139         /* Sample all the threads and update trace costs */
 140         if ( !gdata->pause_cpu_sampling) {
 141             tls_sample_all_threads(env);
 142         }
 143 
 144         /* Check to see if we need to finish */
 145         rawMonitorEnter(gdata->cpu_loop_lock); {
 146             cpu_loop_running = gdata->cpu_loop_running;
 147         } rawMonitorExit(gdata->cpu_loop_lock);
 148 
 149     }
 150     rawMonitorExit(gdata->cpu_sample_lock);
 151 
 152     rawMonitorEnter(gdata->cpu_loop_lock); {
 153         /* Notify cpu_sample_term() that we are done. */
 154         rawMonitorNotifyAll(gdata->cpu_loop_lock);
 155     } rawMonitorExit(gdata->cpu_loop_lock);
 156 
 157     LOG2("cpu_loop()", "clean termination");
 158 }
 159 
 160 /* External functions */
 161 
 162 void
 163 cpu_sample_init(JNIEnv *env)
 164 {
 165     gdata->cpu_sampling  = JNI_TRUE;
 166 
 167     /* Create the raw monitors needed */
 168     gdata->cpu_loop_lock = createRawMonitor("HPROF cpu loop lock");
 169     gdata->cpu_sample_lock = createRawMonitor("HPROF cpu sample lock");
 170 
 171     rawMonitorEnter(gdata->cpu_loop_lock); {
 172         createAgentThread(env, "HPROF cpu sampling thread",
 173                             &cpu_loop_function);
 174         /* Wait for cpu_loop_function() to notify us it has started. */
 175         rawMonitorWait(gdata->cpu_loop_lock, 0);
 176     } rawMonitorExit(gdata->cpu_loop_lock);
 177 }
 178 
 179 void
 180 cpu_sample_off(JNIEnv *env, ObjectIndex object_index)
 181 {
 182     jint count;
 183 
 184     count = 1;
 185     if (object_index != 0) {
 186         tls_set_sample_status(object_index, 0);
 187         count = tls_sum_sample_status();
 188     }
 189     if ( count == 0 ) {
 190         gdata->pause_cpu_sampling = JNI_TRUE;
 191     } else {
 192         gdata->pause_cpu_sampling = JNI_FALSE;
 193     }
 194 }
 195 
 196 void
 197 cpu_sample_on(JNIEnv *env, ObjectIndex object_index)
 198 {
 199     if ( gdata->cpu_loop_lock == NULL ) {
 200         cpu_sample_init(env);
 201     }
 202 
 203     if (object_index == 0) {
 204         gdata->cpu_sampling             = JNI_TRUE;
 205         gdata->pause_cpu_sampling       = JNI_FALSE;
 206     } else {
 207         jint     count;
 208 
 209         tls_set_sample_status(object_index, 1);
 210         count = tls_sum_sample_status();
 211         if ( count > 0 ) {
 212             gdata->pause_cpu_sampling   = JNI_FALSE;
 213         }
 214     }
 215 
 216     /* Notify the CPU sampling thread that sampling is on */
 217     rawMonitorEnter(gdata->cpu_sample_lock); {
 218         rawMonitorNotifyAll(gdata->cpu_sample_lock);
 219     } rawMonitorExit(gdata->cpu_sample_lock);
 220 
 221 }
 222 
 223 void
 224 cpu_sample_term(JNIEnv *env)
 225 {
 226     gdata->pause_cpu_sampling   = JNI_FALSE;
 227     rawMonitorEnter(gdata->cpu_sample_lock); {
 228         /* Notify the CPU sampling thread to get out of any sampling Wait */
 229         rawMonitorNotifyAll(gdata->cpu_sample_lock);
 230     } rawMonitorExit(gdata->cpu_sample_lock);
 231     rawMonitorEnter(gdata->cpu_loop_lock); {
 232         if ( gdata->cpu_loop_running ) {
 233             gdata->cpu_loop_running = JNI_FALSE;
 234             /* Wait for cpu_loop_function() thread to tell us it completed. */
 235             rawMonitorWait(gdata->cpu_loop_lock, 0);
 236         }
 237     } rawMonitorExit(gdata->cpu_loop_lock);
 238 }