|  | /* | 
|  | * Copyright (c) 2003, 2011, 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. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * This source code is provided to illustrate the usage of a given feature | 
|  | * or technique and has been deliberately simplified. Additional steps | 
|  | * required for a production-quality application, such as security checks, | 
|  | * input validation and proper error handling, might not be present in | 
|  | * this sample code. | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include "hprof.h" | 
|  |  | 
|  | /* This file contains the cpu loop for the option cpu=samples */ | 
|  |  | 
|  | /* The cpu_loop thread basically waits for gdata->sample_interval millisecs | 
|  | *   then wakes up, and for each running thread it gets their stack trace, | 
|  | *   and updates the traces with 'hits'. | 
|  | * | 
|  | * No threads are suspended or resumed, and the thread sampling is in the | 
|  | *   file hprof_tls.c, which manages all active threads. The sampling | 
|  | *   technique (what is sampled) is also in hprof_tls.c. | 
|  | * | 
|  | * No adjustments are made to the pause time or sample interval except | 
|  | *   by the user via the interval=n option (default is 10ms). | 
|  | * | 
|  | * This thread can cause havoc when started prematurely or not terminated | 
|  | *   properly, see cpu_sample_init() and cpu_term(), and their calls in hprof_init.c. | 
|  | * | 
|  | * The listener loop (hprof_listener.c) can dynamically turn on or off the | 
|  | *  sampling of all or selected threads. | 
|  | * | 
|  | */ | 
|  |  | 
|  | /* Private functions */ | 
|  |  | 
|  | static void JNICALL | 
|  | cpu_loop_function(jvmtiEnv *jvmti, JNIEnv *env, void *p) | 
|  | { | 
|  | int         loop_trip_counter; | 
|  | jboolean    cpu_loop_running; | 
|  |  | 
|  | loop_trip_counter          = 0; | 
|  |  | 
|  | rawMonitorEnter(gdata->cpu_loop_lock); { | 
|  | gdata->cpu_loop_running = JNI_TRUE; | 
|  | cpu_loop_running = gdata->cpu_loop_running; | 
|  | /* Notify cpu_sample_init() that we have started */ | 
|  | rawMonitorNotifyAll(gdata->cpu_loop_lock); | 
|  | } rawMonitorExit(gdata->cpu_loop_lock); | 
|  |  | 
|  | rawMonitorEnter(gdata->cpu_sample_lock); /* Only waits inside loop let go */ | 
|  |  | 
|  | while ( cpu_loop_running ) { | 
|  |  | 
|  | ++loop_trip_counter; | 
|  |  | 
|  | LOG3("cpu_loop()", "iteration", loop_trip_counter); | 
|  |  | 
|  | /* If a dump is in progress, we pause sampling. */ | 
|  | rawMonitorEnter(gdata->dump_lock); { | 
|  | if (gdata->dump_in_process) { | 
|  | gdata->pause_cpu_sampling = JNI_TRUE; | 
|  | } | 
|  | } rawMonitorExit(gdata->dump_lock); | 
|  |  | 
|  | /* Check to see if we need to pause sampling (listener_loop command) */ | 
|  | if (gdata->pause_cpu_sampling) { | 
|  |  | 
|  | /* | 
|  | * Pause sampling for now. Reset sample controls if | 
|  | * sampling is resumed again. | 
|  | */ | 
|  |  | 
|  | rawMonitorWait(gdata->cpu_sample_lock, 0); | 
|  |  | 
|  | rawMonitorEnter(gdata->cpu_loop_lock); { | 
|  | cpu_loop_running = gdata->cpu_loop_running; | 
|  | } rawMonitorExit(gdata->cpu_loop_lock); | 
|  |  | 
|  | /* Continue the while loop, which will terminate if done. */ | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* This is the normal short timed wait before getting a sample */ | 
|  | rawMonitorWait(gdata->cpu_sample_lock,  (jlong)gdata->sample_interval); | 
|  |  | 
|  | /* Make sure we really want to continue */ | 
|  | rawMonitorEnter(gdata->cpu_loop_lock); { | 
|  | cpu_loop_running = gdata->cpu_loop_running; | 
|  | } rawMonitorExit(gdata->cpu_loop_lock); | 
|  |  | 
|  | /* Break out if we are done */ | 
|  | if ( !cpu_loop_running ) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If a dump request came in after we checked at the top of | 
|  | * the while loop, then we catch that fact here. We | 
|  | * don't want to perturb the data that is being dumped so | 
|  | * we just ignore the data from this sampling loop. | 
|  | */ | 
|  | rawMonitorEnter(gdata->dump_lock); { | 
|  | if (gdata->dump_in_process) { | 
|  | gdata->pause_cpu_sampling = JNI_TRUE; | 
|  | } | 
|  | } rawMonitorExit(gdata->dump_lock); | 
|  |  | 
|  | /* Sample all the threads and update trace costs */ | 
|  | if ( !gdata->pause_cpu_sampling) { | 
|  | tls_sample_all_threads(env); | 
|  | } | 
|  |  | 
|  | /* Check to see if we need to finish */ | 
|  | rawMonitorEnter(gdata->cpu_loop_lock); { | 
|  | cpu_loop_running = gdata->cpu_loop_running; | 
|  | } rawMonitorExit(gdata->cpu_loop_lock); | 
|  |  | 
|  | } | 
|  | rawMonitorExit(gdata->cpu_sample_lock); | 
|  |  | 
|  | rawMonitorEnter(gdata->cpu_loop_lock); { | 
|  | /* Notify cpu_sample_term() that we are done. */ | 
|  | rawMonitorNotifyAll(gdata->cpu_loop_lock); | 
|  | } rawMonitorExit(gdata->cpu_loop_lock); | 
|  |  | 
|  | LOG2("cpu_loop()", "clean termination"); | 
|  | } | 
|  |  | 
|  | /* External functions */ | 
|  |  | 
|  | void | 
|  | cpu_sample_init(JNIEnv *env) | 
|  | { | 
|  | gdata->cpu_sampling  = JNI_TRUE; | 
|  |  | 
|  | /* Create the raw monitors needed */ | 
|  | gdata->cpu_loop_lock = createRawMonitor("HPROF cpu loop lock"); | 
|  | gdata->cpu_sample_lock = createRawMonitor("HPROF cpu sample lock"); | 
|  |  | 
|  | rawMonitorEnter(gdata->cpu_loop_lock); { | 
|  | createAgentThread(env, "HPROF cpu sampling thread", | 
|  | &cpu_loop_function); | 
|  | /* Wait for cpu_loop_function() to notify us it has started. */ | 
|  | rawMonitorWait(gdata->cpu_loop_lock, 0); | 
|  | } rawMonitorExit(gdata->cpu_loop_lock); | 
|  | } | 
|  |  | 
|  | void | 
|  | cpu_sample_off(JNIEnv *env, ObjectIndex object_index) | 
|  | { | 
|  | jint count; | 
|  |  | 
|  | count = 1; | 
|  | if (object_index != 0) { | 
|  | tls_set_sample_status(object_index, 0); | 
|  | count = tls_sum_sample_status(); | 
|  | } | 
|  | if ( count == 0 ) { | 
|  | gdata->pause_cpu_sampling = JNI_TRUE; | 
|  | } else { | 
|  | gdata->pause_cpu_sampling = JNI_FALSE; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | cpu_sample_on(JNIEnv *env, ObjectIndex object_index) | 
|  | { | 
|  | if ( gdata->cpu_loop_lock == NULL ) { | 
|  | cpu_sample_init(env); | 
|  | } | 
|  |  | 
|  | if (object_index == 0) { | 
|  | gdata->cpu_sampling             = JNI_TRUE; | 
|  | gdata->pause_cpu_sampling       = JNI_FALSE; | 
|  | } else { | 
|  | jint     count; | 
|  |  | 
|  | tls_set_sample_status(object_index, 1); | 
|  | count = tls_sum_sample_status(); | 
|  | if ( count > 0 ) { | 
|  | gdata->pause_cpu_sampling   = JNI_FALSE; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Notify the CPU sampling thread that sampling is on */ | 
|  | rawMonitorEnter(gdata->cpu_sample_lock); { | 
|  | rawMonitorNotifyAll(gdata->cpu_sample_lock); | 
|  | } rawMonitorExit(gdata->cpu_sample_lock); | 
|  |  | 
|  | } | 
|  |  | 
|  | void | 
|  | cpu_sample_term(JNIEnv *env) | 
|  | { | 
|  | gdata->pause_cpu_sampling   = JNI_FALSE; | 
|  | rawMonitorEnter(gdata->cpu_sample_lock); { | 
|  | /* Notify the CPU sampling thread to get out of any sampling Wait */ | 
|  | rawMonitorNotifyAll(gdata->cpu_sample_lock); | 
|  | } rawMonitorExit(gdata->cpu_sample_lock); | 
|  | rawMonitorEnter(gdata->cpu_loop_lock); { | 
|  | if ( gdata->cpu_loop_running ) { | 
|  | gdata->cpu_loop_running = JNI_FALSE; | 
|  | /* Wait for cpu_loop_function() thread to tell us it completed. */ | 
|  | rawMonitorWait(gdata->cpu_loop_lock, 0); | 
|  | } | 
|  | } rawMonitorExit(gdata->cpu_loop_lock); | 
|  | } |