blob: 77262898e6899c8cfef2bbd9f1abf775fbbce247 [file] [log] [blame]
/*
* 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.
*/
#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);
}