blob: cd3381b33334ec058c2d69598b2cad35d0491bea [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Gregory Shimansky, Pavel Afremov
*/
/*
* JVMTI stack frame API
*/
#define LOG_DOMAIN "jvmti.stack"
#include "open/vm_method_access.h"
#include "jvmti_direct.h"
#include "jvmti_utils.h"
#include "interpreter_exports.h"
#include "jthread.h"
#include "vm_threads.h"
#include "environment.h"
#include "Class.h"
#include "cxxlog.h"
#include "thread_generic.h"
#include "suspend_checker.h"
#include "stack_trace.h"
#include "stack_iterator.h"
#include "jit_intf_cpp.h"
#include "thread_manager.h"
#include "cci.h"
#include "open/vm_class_info.h"
class JavaStackIterator
{
StackIterator* si;
/** index of current java stack frame */
jint depth;
/** number of inlines in current si frame */
U_32 inlined_num;
/** index of inlined frame in current si frame */
int inlined_depth;
bool past_end;
void update() {
Method_Handle method = NULL;
// skip non java frames
while (! si_is_past_end(si) && NULL == (method = si_get_method(si)))
si_goto_previous(si);
// end of stack
if (NULL == method) {
past_end = true;
return;
}
// hide vm specific stack tail
if (0 == strcmp("runImpl", method->get_name()->bytes)) {
const char* class_name = method->get_class()->get_name()->bytes;
if (0 == strcmp("java/lang/VMStart$MainThread", class_name)
|| 0 == strcmp("java/lang/Thread", class_name))
{
past_end = true;
return;
}
}
inlined_num = si_get_inline_depth(si);
inlined_depth = inlined_num;
}
public:
~JavaStackIterator() {
si_free(si);
}
JavaStackIterator(VM_thread *thread) {
si = si_create_from_native(thread);
depth = 0;
past_end = false;
update();
}
StackIterator* get_si() {
return si;
}
jint get_depth() {
return depth;
}
bool is_inlined() {
return 0 != inlined_depth;
}
bool is_jni() {
return si_is_native(si);
}
operator bool () {
return ! past_end;
}
void operator ++ (int) {
assert(! past_end);
depth++;
if (inlined_depth > 0) {
inlined_depth--;
} else {
si_goto_previous(si);
update();
}
}
void operator += (int frames_num) {
for (; frames_num > 0 && *this; frames_num--, (*this)++);
}
void get_location(jmethodID* p_method, jlocation* p_location) {
Method* method = si_get_method(si);
// jni method frame
if (si_is_native(si)) {
*p_method = (jmethodID) method;
*p_location = -1;
return;
}
// java frame
NativeCodePtr ip = si_get_ip(si);
// if ip points to next instruction
if (si_get_jit_context(si)->is_ip_past) {
ip = (NativeCodePtr) ((char*) ip - 1);
}
CodeChunkInfo *cci = si_get_code_chunk_info(si);
JIT *jit = cci->get_jit();
uint16 bc;
// inlined method frame
if (0 != inlined_depth) {
U_32 offset = (U_32) ((char*) ip -
(char*) cci->get_code_block_addr());
method = jit->get_inlined_method(
cci->get_inline_info(), offset, inlined_depth);
bc = jit->get_inlined_bc(
cci->get_inline_info(), offset, inlined_depth);
} else {
OpenExeJpdaError UNREF result = jit->get_bc_location_for_native(
method, ip, &bc);
assert(result == EXE_ERROR_NONE);
}
*p_method = (jmethodID) method;
*p_location = (jlocation) bc;
}
};
jthread getCurrentThread() {
jthread current_thread = jthread_self();
assert(current_thread);
return oh_copy_to_local_handle(current_thread);
}
jint get_thread_stack_depth(VM_thread *thread)
{
JavaStackIterator jsi(thread);
while (jsi)
jsi++;
return jsi.get_depth();
} // get_thread_stack_depth
static jvmtiError get_stack_trace_jit(
VM_thread* vm_thread,
jint start_depth,
jint max_frame_count,
jvmtiFrameInfo* frame_buffer,
jint* count_ptr)
{
jint start = start_depth;
if (start < 0) {
start = start + get_thread_stack_depth(vm_thread);
if (start < 0)
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
JavaStackIterator jsi(vm_thread);
// to be complient with RI we must break the spec.
// if stack is empty and start_depth is 0 RI doesn't return
// JVMTI_ERROR_ILLEGAL_ARGUMENT as it is required by spec.
if (start == 0 && ! jsi) {
*count_ptr = 0;
return JVMTI_ERROR_NONE;
}
jsi += start;
if (! jsi)
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
jint i = 0;
for (; i < max_frame_count && jsi; i++, jsi++) {
jsi.get_location(& frame_buffer[i].method, & frame_buffer[i].location);
}
*count_ptr = i;
return JVMTI_ERROR_NONE;
} // get_stack_trace_jit
/*
* Get Stack Trace
*
* Get information about the stack of a thread. If max_frame_count
* is less than the depth of the stack, the max_frame_count
* deepest frames are returned, otherwise the entire stack is
* returned. Deepest frames are at the beginning of the returned
* buffer.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiGetStackTrace(jvmtiEnv* env,
jthread thread,
jint start_depth,
jint max_frame_count,
jvmtiFrameInfo* frame_buffer,
jint* count_ptr)
{
TRACE("GetStackTrace called");
SuspendEnabledChecker sec;
jvmtiError err;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
// check error condition: JVMTI_ERROR_INVALID_THREAD
jint state;
err = jvmtiGetThreadState(env, thread, &state);
if (err != JVMTI_ERROR_NONE) {
return err;
}
// check error condition: JVMTI_ERROR_THREAD_NOT_ALIVE
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0) {
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
// check error condition: JVMTI_ERROR_NULL_POINTER
if (frame_buffer == 0) {
return JVMTI_ERROR_NULL_POINTER;
}
// check error condition: JVMTI_ERROR_NULL_POINTER
if (count_ptr == 0) {
return JVMTI_ERROR_NULL_POINTER;
}
// check error condition: JVMTI_ERROR_ILLEGAL_ARGUMENT
if (max_frame_count < 0) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
bool thread_suspended = false;
// Suspend thread before getting stacks
vm_thread_t vm_thread;
if (NULL != thread)
{
// Check that this thread is not current
vm_thread = jthread_get_vm_thread_ptr_safe(thread);
if (vm_thread != p_TLS_vmthread)
{
// to avoid suspension of each other due to race condition
// get global thread lock as it's done in hythread_suspend_all().
IDATA UNREF status = hythread_global_lock();
assert(TM_ERROR_NONE == status);
status = hythread_suspend_other((hythread_t)vm_thread);
assert(TM_ERROR_NONE == status);
thread_suspended = true;
status = hythread_global_unlock();
assert(TM_ERROR_NONE == status);
}
}
else
vm_thread = p_TLS_vmthread;
if (interpreter_enabled())
err = interpreter.interpreter_ti_getStackTrace(env,
vm_thread, start_depth, max_frame_count, frame_buffer, count_ptr);
else
err = get_stack_trace_jit(vm_thread, start_depth, max_frame_count, frame_buffer, count_ptr);
if (thread_suspended)
hythread_resume((hythread_t)vm_thread);
return err;
} // jvmtiGetStackTrace
/*
* Get All Stack Traces
*
* Get information about the stacks of all live threads.
* If max_frame_count is less than the depth of a stack,
* the max_frame_count deepest frames are returned for that
* thread, otherwise the entire stack is returned. Deepest
* frames are at the beginning of the returned buffer.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiGetAllStackTraces(jvmtiEnv* env,
jint max_frame_count,
jvmtiStackInfo** stack_info_ptr,
jint* thread_count_ptr)
{
TRACE("GetAllStackTraces called");
SuspendEnabledChecker sec;
jint count;
jthread *threads;
jvmtiError res;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
if (NULL == stack_info_ptr)
return JVMTI_ERROR_NULL_POINTER;
if (NULL == thread_count_ptr)
return JVMTI_ERROR_NULL_POINTER;
if (max_frame_count < 0)
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
// FIXME: new threads can be created before all threads are suspended
// event handler for thread_creating should block creation new threads
// at this moment
assert(hythread_is_suspend_enabled());
hythread_suspend_all(NULL, NULL);
res = jvmtiGetAllThreads(env, &count, &threads);
if (res != JVMTI_ERROR_NONE) {
hythread_resume_all(NULL);
return res;
}
jvmtiStackInfo *info;
// FIXME: memory leak in case of error
res = _allocate(sizeof(jvmtiStackInfo) * count +
sizeof(jvmtiFrameInfo) * max_frame_count * count,
(unsigned char **)&info);
if (JVMTI_ERROR_NONE != res) {
hythread_resume_all(NULL);
return res;
}
// getting thread states
for (int i = 0; i < count; i++) {
info[i].thread = threads[i];
res = jvmtiGetThreadState(env, threads[i], &info[i].state);
if (JVMTI_ERROR_NONE != res) {
_deallocate((unsigned char *) info);
hythread_resume_all(NULL);
return res;
}
// Frame_buffer pointer should pointer to the memory right
// after the jvmtiStackInfo structures
info[i].frame_buffer = ((jvmtiFrameInfo *)&info[count]) +
max_frame_count * i;
res = jvmtiGetStackTrace(env, info[i].thread, 0, max_frame_count,
info[i].frame_buffer, &info[i].frame_count);
if (JVMTI_ERROR_NONE != res) {
_deallocate((unsigned char *) info);
hythread_resume_all(NULL);
return res;
}
}
hythread_resume_all(NULL);
*thread_count_ptr = count;
*stack_info_ptr = info;
return JVMTI_ERROR_NONE;
} // jvmtiGetAllStackTraces
/*
* Get Thread List Stack Traces
*
* Get information about the stacks of the supplied threads.
* If max_frame_count is less than the depth of a stack, the
* max_frame_count deepest frames are returned for that thread,
* otherwise the entire stack is returned. Deepest frames are
* at the beginning of the returned buffer.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiGetThreadListStackTraces(jvmtiEnv* env,
jint thread_count,
const jthread* thread_list,
jint max_frame_count,
jvmtiStackInfo** stack_info_ptr)
{
TRACE("GetThreadListStackTraces called");
SuspendEnabledChecker sec;
jint count = thread_count;
const jthread *threads = thread_list;
jvmtiError res;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
if (NULL == stack_info_ptr)
return JVMTI_ERROR_NULL_POINTER;
if (NULL == thread_list)
return JVMTI_ERROR_NULL_POINTER;
if (thread_count < 0)
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
if (max_frame_count < 0)
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
jvmtiStackInfo *info;
res = _allocate(sizeof(jvmtiStackInfo) * count +
sizeof(jvmtiFrameInfo) * max_frame_count * count,
(unsigned char **)&info);
if (JVMTI_ERROR_NONE != res)
return res;
int i;
vm_thread_t self = jthread_self_vm_thread();
// stopping all threads
for(i = 0; i < count; i++) {
// FIXME: thread can be dead at this time
// event handler for thread death should block thread death
// until the function end.
info[i].thread = threads[i];
vm_thread_t vm_thread = jthread_get_vm_thread_ptr_safe(threads[i]);
if (self != vm_thread) {
IDATA UNREF status = hythread_suspend_other((hythread_t)vm_thread);
assert(TM_ERROR_NONE == status);
}
}
// getting thread states
for(i = 0; i < count; i++) {
// Frame_buffer pointer should pointer to the memory right
// after the jvmtiStackInfo structures
info[i].frame_buffer = ((jvmtiFrameInfo *)&info[count]) +
max_frame_count * i;
res = jvmtiGetStackTrace(env, info[i].thread, 0, max_frame_count,
info[i].frame_buffer, &info[i].frame_count);
// if thread was terminated before we got it's stack - return null frame_count
if (JVMTI_ERROR_THREAD_NOT_ALIVE == res)
info[i].frame_count = 0;
else if (JVMTI_ERROR_NONE != res)
break;
}
// unsuspend suspended threads.
for(i = 0; i < count; i++) {
vm_thread_t vm_thread = jthread_get_vm_thread_ptr_safe(threads[i]);
if (self != vm_thread) {
hythread_resume((hythread_t)vm_thread);
}
}
if (JVMTI_ERROR_NONE != res)
return res;
*stack_info_ptr = info;
return JVMTI_ERROR_NONE;
} // jvmtiGetThreadListStackTraces
/*
* Get Frame Count
*
* Get the number of frames currently in the specified thread's
* call stack.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiGetFrameCount(jvmtiEnv* env,
jthread thread,
jint* count_ptr)
{
TRACE("GetFrameCount called");
SuspendEnabledChecker sec;
jint state;
jvmtiError err;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
if (thread == 0) {
thread = getCurrentThread();
}
// check error condition: JVMTI_ERROR_INVALID_THREAD
err = env->GetThreadState(thread, &state);
if (err != JVMTI_ERROR_NONE) {
return err;
}
// check error condition: JVMTI_ERROR_THREAD_NOT_ALIVE
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0) {
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
// check error condition: JVMTI_ERROR_NULL_POINTER
if (count_ptr == 0) {
return JVMTI_ERROR_NULL_POINTER;
}
bool thread_suspended = false;
// Suspend thread before getting stacks
vm_thread_t vm_thread;
if (NULL != thread)
{
// Check that this thread is not current
vm_thread = jthread_get_vm_thread_ptr_safe(thread);
if (vm_thread != p_TLS_vmthread)
{
IDATA UNREF status = hythread_suspend_other((hythread_t)vm_thread);
assert(TM_ERROR_NONE == status);
thread_suspended = true;
}
}
else
vm_thread = p_TLS_vmthread;
jvmtiError errStack;
if (interpreter_enabled())
errStack = interpreter.interpreter_ti_get_frame_count(env,
vm_thread, count_ptr);
else
{
jint depth = get_thread_stack_depth(vm_thread);
*count_ptr = depth;
errStack = JVMTI_ERROR_NONE;
}
if (thread_suspended)
hythread_resume((hythread_t)vm_thread);
return errStack;
} // jvmtiGetFrameCount
/*
* Pop Frame
*
* Pop the topmost stack frame of thread's stack. Popping a frame
* takes you to the preceding frame.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiPopFrame(jvmtiEnv* env,
jthread thread)
{
TRACE("PopFrame called");
SuspendEnabledChecker sec;
jint state;
jvmtiError err;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
CHECK_CAPABILITY(can_pop_frame);
if (NULL == thread) {
return JVMTI_ERROR_INVALID_THREAD;
}
JNIEnv *jni_env = p_TLS_vmthread->jni_env;
jthread curr_thread = getCurrentThread();
if (jni_env->IsSameObject(thread, curr_thread) ) {
// cannot pop frame yourself
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
// get thread state
err = env->GetThreadState(thread, &state);
if (err != JVMTI_ERROR_NONE) {
return err;
}
// check thread state
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0) {
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
if( (state & JVMTI_THREAD_STATE_SUSPENDED) == 0) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
// check stack depth
hythread_t hy_thread = jthread_get_native_thread(thread);
vm_thread_t vm_thread = jthread_get_vm_thread(hy_thread);
jint depth;
if (interpreter_enabled()) {
err = interpreter.interpreter_ti_get_frame_count(env,
vm_thread, &depth);
} else {
depth = get_thread_stack_depth(vm_thread);
err = JVMTI_ERROR_NONE;
}
if (err != JVMTI_ERROR_NONE) {
return err;
}
if(depth <= 1) {
return JVMTI_ERROR_NO_MORE_FRAMES;
}
if (interpreter_enabled()) {
return interpreter.interpreter_ti_pop_frame(env, vm_thread);
} else {
return jvmti_jit_pop_frame(thread);
}
} // jvmtiPopFrame
/*
* Get Frame Location
*
* For a Java programming language frame, return the location of
* the instruction currently executing.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiGetFrameLocation(jvmtiEnv* env,
jthread thread,
jint depth,
jmethodID* method_ptr,
jlocation* location_ptr)
{
TRACE("GetFrameLocation called");
SuspendEnabledChecker sec;
jint state;
jvmtiError err;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
if (thread == 0) {
thread = getCurrentThread();
}
// check error condition: JVMTI_ERROR_INVALID_THREAD
err = env->GetThreadState(thread, &state);
if (err != JVMTI_ERROR_NONE) {
return err;
}
// check error condition: JVMTI_ERROR_THREAD_NOT_ALIVE
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0) {
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
// check error condition: JVMTI_ERROR_NULL_POINTER
if (location_ptr == NULL) {
return JVMTI_ERROR_NULL_POINTER;
}
// check error condition: JVMTI_ERROR_NULL_POINTER
if (method_ptr == NULL) {
return JVMTI_ERROR_NULL_POINTER;
}
// check error condition: JVMTI_ERROR_ILLEGAL_ARGUMENT
if (depth < 0) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
bool thread_suspended = false;
// Suspend thread before getting stacks
vm_thread_t vm_thread;
if (NULL != thread)
{
// Check that this thread is not current
vm_thread = jthread_get_vm_thread_ptr_safe(thread);
if (vm_thread != p_TLS_vmthread)
{
IDATA UNREF status = hythread_suspend_other((hythread_t)vm_thread);
assert(TM_ERROR_NONE == status);
thread_suspended = true;
}
}
else
vm_thread = p_TLS_vmthread;
jvmtiError errStack;
if (interpreter_enabled())
// check error condition: JVMTI_ERROR_ILLEGAL_ARGUMENT
// check error condition: JVMTI_ERROR_NO_MORE_FRAMES
errStack = interpreter.interpreter_ti_getFrameLocation(env,
jthread_get_vm_thread_ptr_safe(thread),
depth, method_ptr, location_ptr);
else
{
JavaStackIterator jsi(vm_thread);
jsi += depth;
if (jsi) {
jsi.get_location(method_ptr, location_ptr);
errStack = JVMTI_ERROR_NONE;
} else {
errStack = JVMTI_ERROR_NO_MORE_FRAMES;
}
}
if (thread_suspended)
hythread_resume((hythread_t)vm_thread);
return errStack;
} // jvmtiGetFrameLocation
/*
* Notify Frame Pop
*
* When the frame that is currently at depth is popped from the
* stack, generate a FramePop event. See the FramePop event for
* details. Only frames corresponding to non-native Java
* programming language methods can receive notification.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiNotifyFramePop(jvmtiEnv* env,
jthread thread,
jint depth)
{
TRACE("NotifyFramePop called: thread: "
<< thread << ", depth: " << depth);
SuspendEnabledChecker sec;
jint state;
jthread curr_thread = getCurrentThread();
jvmtiError err;
// Check given env & current phase.
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
CHECK_CAPABILITY(can_generate_frame_pop_events);
if (NULL == thread)
thread = curr_thread;
// check error condition: JVMTI_ERROR_INVALID_THREAD
err = env->GetThreadState(thread, &state);
if (err != JVMTI_ERROR_NONE) {
return err;
}
// check error condition: JVMTI_ERROR_THREAD_NOT_ALIVE
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0) {
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
// check error condition: JVMTI_ERROR_THREAD_NOT_SUSPENDED
JNIEnv *jni_env = p_TLS_vmthread->jni_env;
if (!jni_env->IsSameObject(thread,curr_thread)
&& ((state & JVMTI_THREAD_STATE_SUSPENDED) == 0)) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
// check error condition: JVMTI_ERROR_ILLEGAL_ARGUMENT
if (depth < 0) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
vm_thread_t vm_thread;
if (NULL != thread)
vm_thread = jthread_get_vm_thread_ptr_safe(thread);
else
vm_thread = p_TLS_vmthread;
if (interpreter_enabled())
// check error condition: JVMTI_ERROR_OPAQUE_FRAME
// check error condition: JVMTI_ERROR_NO_MORE_FRAMES
return interpreter.interpreter_ti_notify_frame_pop(env,
vm_thread, depth);
else
{
JavaStackIterator jsi(vm_thread);
jsi += depth;
if (! jsi)
return JVMTI_ERROR_NO_MORE_FRAMES;
if (jsi.is_jni() || jsi.is_inlined())
return JVMTI_ERROR_OPAQUE_FRAME;
// get method for TRACE message
Method* UNREF method = si_get_method(jsi.get_si());
// fast forward to teh end of stack
for (; jsi; jsi++);
jvmti_frame_pop_listener *new_listener =
(jvmti_frame_pop_listener *)STD_MALLOC(
sizeof(jvmti_frame_pop_listener));
assert(new_listener);
new_listener->depth = jsi.get_depth() - depth;
new_listener->env = reinterpret_cast<TIEnv *>(env);
new_listener->next = vm_thread->jvmti_thread.frame_pop_listener;
vm_thread->jvmti_thread.frame_pop_listener = new_listener;
TRACE("Pop listener is created: thread: "
<< vm_thread << ", listener: " << new_listener
<< ", env: " << new_listener->env << ", depth: " << new_listener->depth
<< " -> " << method);
return JVMTI_ERROR_NONE;
}
} // jvmtiNotifyFramePop