hotspot/src/share/vm/prims/forte.cpp
// Forte Analyzer AsyncGetCallTrace() entry point. Currently supported
// on Linux X86, Solaris SPARC and Solaris X86.
//
// Async-safe version of GetCallTrace being called from a signal handler
// when a LWP gets interrupted by SIGPROF but the stack traces are filled
// with different content (see below).
//
// This function must only be called when JVM/TI
// CLASS_LOAD events have been enabled since agent startup. The enabled
// event will cause the jmethodIDs to be allocated at class load time.
// The jmethodIDs cannot be allocated in a signal handler because locks
// cannot be grabbed in a signal handler safely.
//
// void (*AsyncGetCallTrace)(ASGCT_CallTrace *trace, jint depth, void* ucontext)
//
// Called by the profiler to obtain the current method call stack trace for
// a given thread. The thread is identified by the env_id field in the
// ASGCT_CallTrace structure. The profiler agent should allocate a ASGCT_CallTrace
// structure with enough memory for the requested stack depth. The VM fills in
// the frames buffer and the num_frames field.
//
// Arguments:
//
// trace - trace data structure to be filled by the VM.
// depth - depth of the call stack trace.
// ucontext - ucontext_t of the LWP
//
// ASGCT_CallTrace:
// typedef struct {
// JNIEnv *env_id;
// jint num_frames;
// ASGCT_CallFrame *frames;
// } ASGCT_CallTrace;
//
// Fields:
// env_id - ID of thread which executed this trace.
// num_frames - number of frames in the trace.
// (< 0 indicates the frame is not walkable).
// frames - the ASGCT_CallFrames that make up this trace. Callee followed by callers.
//
// ASGCT_CallFrame:
// typedef struct {
// jint lineno;
// jmethodID method_id;
// } ASGCT_CallFrame;
//
// Fields:
// 1) For Java frame (interpreted and compiled),
// lineno - bci of the method being executed or -1 if bci is not available
// method_id - jmethodID of the method being executed
// 2) For native method
// lineno - (-3)
// method_id - jmethodID of the method being executed
extern "C" {
JNIEXPORT
void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) {
{- -------------------------------------------
(1)
---------------------------------------- -}
// This is if'd out because we no longer use thread suspension.
// However if someone wanted to backport this to a 5.0 jvm then this
// code would be important.
#if 0
if (SafepointSynchronize::is_synchronizing()) {
// The safepoint mechanism is trying to synchronize all the threads.
// Since this can involve thread suspension, it is not safe for us
// to be here. We can reduce the deadlock risk window by quickly
// returning to the SIGPROF handler. However, it is still possible
// for VMThread to catch us here or in the SIGPROF handler. If we
// are suspended while holding a resource and another thread blocks
// on that resource in the SIGPROF handler, then we will have a
// three-thread deadlock (VMThread, this thread, the other thread).
trace->num_frames = ticks_safepoint; // -10
return;
}
#endif
{- -------------------------------------------
(1)
---------------------------------------- -}
JavaThread* thread;
if (trace->env_id == NULL ||
(thread = JavaThread::thread_from_jni_environment(trace->env_id)) == NULL ||
thread->is_exiting()) {
// bad env_id, thread has exited or thread is exiting
trace->num_frames = ticks_thread_exit; // -8
return;
}
if (thread->in_deopt_handler()) {
// thread is in the deoptimization handler so return no frames
trace->num_frames = ticks_deopt; // -9
return;
}
assert(JavaThread::current() == thread,
"AsyncGetCallTrace must be called by the current interrupted thread");
if (!JvmtiExport::should_post_class_load()) {
trace->num_frames = ticks_no_class_load; // -1
return;
}
if (Universe::heap()->is_gc_active()) {
trace->num_frames = ticks_GC_active; // -2
return;
}
switch (thread->thread_state()) {
case _thread_new:
case _thread_uninitialized:
case _thread_new_trans:
// We found the thread on the threads list above, but it is too
// young to be useful so return that there are no Java frames.
trace->num_frames = 0;
break;
case _thread_in_native:
case _thread_in_native_trans:
case _thread_blocked:
case _thread_blocked_trans:
case _thread_in_vm:
case _thread_in_vm_trans:
{
frame fr;
// param isInJava == false - indicate we aren't in Java code
if (!thread->pd_get_top_frame_for_signal_handler(&fr, ucontext, false)) {
trace->num_frames = ticks_unknown_not_Java; // -3 unknown frame
} else {
if (!thread->has_last_Java_frame()) {
trace->num_frames = 0; // No Java frames
} else {
trace->num_frames = ticks_not_walkable_not_Java; // -4 non walkable frame by default
forte_fill_call_trace_given_top(thread, trace, depth, fr);
// This assert would seem to be valid but it is not.
// It would be valid if we weren't possibly racing a gc
// thread. A gc thread can make a valid interpreted frame
// look invalid. It's a small window but it does happen.
// The assert is left here commented out as a reminder.
// assert(trace->num_frames != ticks_not_walkable_not_Java, "should always be walkable");
}
}
}
break;
case _thread_in_Java:
case _thread_in_Java_trans:
{
frame fr;
// param isInJava == true - indicate we are in Java code
if (!thread->pd_get_top_frame_for_signal_handler(&fr, ucontext, true)) {
trace->num_frames = ticks_unknown_Java; // -5 unknown frame
} else {
trace->num_frames = ticks_not_walkable_Java; // -6, non walkable frame by default
forte_fill_call_trace_given_top(thread, trace, depth, fr);
}
}
break;
default:
// Unknown thread state
trace->num_frames = ticks_unknown_state; // -7
break;
}
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.