8242428: JVMTI thread operations should use Thread-Local Handshake

Reviewed-by: dcubed, sspitsyn, dholmes
This commit is contained in:
Yasumasa Suenaga 2020-07-11 23:28:02 +09:00
parent 09510a15a1
commit faf4d7ccb7
6 changed files with 308 additions and 67 deletions

View File

@ -1535,15 +1535,14 @@ JvmtiEnv::GetStackTrace(JavaThread* java_thread, jint start_depth, jint max_fram
jvmtiError err = JVMTI_ERROR_NONE;
// It is only safe to perform the direct operation on the current
// thread. All other usage needs to use a vm-safepoint-op for safety.
// thread. All other usage needs to use a direct handshake for safety.
if (java_thread == JavaThread::current()) {
err = get_stack_trace(java_thread, start_depth, max_frame_count, frame_buffer, count_ptr);
} else {
// JVMTI get stack trace at safepoint. Do not require target thread to
// be suspended.
VM_GetStackTrace op(this, java_thread, start_depth, max_frame_count, frame_buffer, count_ptr);
VMThread::execute(&op);
err = op.result();
// Get stack trace with handshake.
GetStackTraceClosure op(this, start_depth, max_frame_count, frame_buffer, count_ptr);
bool executed = Handshake::execute_direct(&op, java_thread);
err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
}
return err;
@ -1575,12 +1574,31 @@ JvmtiEnv::GetAllStackTraces(jint max_frame_count, jvmtiStackInfo** stack_info_pt
jvmtiError
JvmtiEnv::GetThreadListStackTraces(jint thread_count, const jthread* thread_list, jint max_frame_count, jvmtiStackInfo** stack_info_ptr) {
jvmtiError err = JVMTI_ERROR_NONE;
// JVMTI get stack traces at safepoint.
VM_GetThreadListStackTraces op(this, thread_count, thread_list, max_frame_count);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
*stack_info_ptr = op.stack_info();
if (thread_count == 1) {
// Use direct handshake if we need to get only one stack trace.
JavaThread *current_thread = JavaThread::current();
ThreadsListHandle tlh(current_thread);
JavaThread *java_thread;
err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), *thread_list, &java_thread, NULL);
if (err != JVMTI_ERROR_NONE) {
return err;
}
GetSingleStackTraceClosure op(this, current_thread, *thread_list, max_frame_count);
bool executed = Handshake::execute_direct(&op, java_thread);
err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
if (err == JVMTI_ERROR_NONE) {
*stack_info_ptr = op.stack_info();
}
} else {
// JVMTI get stack traces at safepoint.
VM_GetThreadListStackTraces op(this, thread_count, thread_list, max_frame_count);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
*stack_info_ptr = op.stack_info();
}
}
return err;
} /* end GetThreadListStackTraces */

View File

@ -816,13 +816,14 @@ JvmtiEnvBase::get_stack_trace(JavaThread *java_thread,
#ifdef ASSERT
uint32_t debug_bits = 0;
#endif
assert((SafepointSynchronize::is_at_safepoint() ||
java_thread->is_thread_fully_suspended(false, &debug_bits)),
"at safepoint or target thread is suspended");
Thread *current_thread = Thread::current();
assert(current_thread == java_thread ||
SafepointSynchronize::is_at_safepoint() ||
current_thread == java_thread->active_handshaker(),
"call by myself / at safepoint / at handshake");
int count = 0;
if (java_thread->has_last_Java_frame()) {
RegisterMap reg_map(java_thread);
Thread* current_thread = Thread::current();
ResourceMark rm(current_thread);
javaVFrame *jvf = java_thread->last_java_vframe(&reg_map);
HandleMark hm(current_thread);
@ -1154,8 +1155,14 @@ struct StackInfoNode {
// Note that either or both of thr and thread_oop
// may be null if the thread is new or has exited.
void
VM_GetMultipleStackTraces::fill_frames(jthread jt, JavaThread *thr, oop thread_oop) {
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
MultipleStackTracesCollector::fill_frames(jthread jt, JavaThread *thr, oop thread_oop) {
#ifdef ASSERT
Thread *current_thread = Thread::current();
assert(current_thread == thr ||
SafepointSynchronize::is_at_safepoint() ||
current_thread == thr->active_handshaker(),
"call by myself / at safepoint / at handshake");
#endif
jint state = 0;
struct StackInfoNode *node = NEW_RESOURCE_OBJ(struct StackInfoNode);
@ -1199,7 +1206,7 @@ VM_GetMultipleStackTraces::fill_frames(jthread jt, JavaThread *thr, oop thread_o
// Based on the stack information in the linked list, allocate memory
// block to return and fill it from the info in the linked list.
void
VM_GetMultipleStackTraces::allocate_and_fill_stacks(jint thread_count) {
MultipleStackTracesCollector::allocate_and_fill_stacks(jint thread_count) {
// do I need to worry about alignment issues?
jlong alloc_size = thread_count * sizeof(jvmtiStackInfo)
+ _frame_count_total * sizeof(jvmtiFrameInfo);
@ -1248,14 +1255,27 @@ VM_GetThreadListStackTraces::doit() {
// only return an error from here if we didn't get a valid
// thread_oop.
if (thread_oop == NULL) {
set_result(err);
_collector.set_result(err);
return;
}
// We have a valid thread_oop.
}
fill_frames(jt, java_thread, thread_oop);
_collector.fill_frames(jt, java_thread, thread_oop);
}
_collector.allocate_and_fill_stacks(_thread_count);
}
void
GetSingleStackTraceClosure::do_thread(Thread *target) {
assert(target->is_Java_thread(), "just checking");
JavaThread *jt = (JavaThread *)target;
oop thread_oop = jt->threadObj();
if (!jt->is_exiting() && thread_oop != NULL) {
ResourceMark rm;
_collector.fill_frames(_jthread, jt, thread_oop);
_collector.allocate_and_fill_stacks(1);
}
allocate_and_fill_stacks(_thread_count);
}
void
@ -1272,11 +1292,11 @@ VM_GetAllStackTraces::doit() {
!jt->is_hidden_from_external_view()) {
++_final_thread_count;
// Handle block of the calling thread is used to create local refs.
fill_frames((jthread)JNIHandles::make_local(_calling_thread, thread_oop),
jt, thread_oop);
_collector.fill_frames((jthread)JNIHandles::make_local(_calling_thread, thread_oop),
jt, thread_oop);
}
}
allocate_and_fill_stacks(_final_thread_count);
_collector.allocate_and_fill_stacks(_final_thread_count);
}
// Verifies that the top frame is a java frame in an expected state.
@ -1528,12 +1548,11 @@ GetCurrentContendedMonitorClosure::do_thread(Thread *target) {
}
void
VM_GetStackTrace::doit() {
_result = JVMTI_ERROR_THREAD_NOT_ALIVE;
ThreadsListHandle tlh;
if (_java_thread != NULL && tlh.includes(_java_thread)
&& !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) {
_result = ((JvmtiEnvBase *)_env)->get_stack_trace(_java_thread,
GetStackTraceClosure::do_thread(Thread *target) {
assert(target->is_Java_thread(), "just checking");
JavaThread *jt = (JavaThread *)target;
if (!jt->is_exiting() && jt->threadObj() != NULL) {
_result = ((JvmtiEnvBase *)_env)->get_stack_trace(jt,
_start_depth, _max_count,
_frame_buffer, _count_ptr);
}

View File

@ -437,11 +437,10 @@ public:
void do_thread(Thread *target);
};
// VM operation to get stack trace at safepoint.
class VM_GetStackTrace : public VM_Operation {
// HandshakeClosure to get stack trace.
class GetStackTraceClosure : public HandshakeClosure {
private:
JvmtiEnv *_env;
JavaThread *_java_thread;
jint _start_depth;
jint _max_count;
jvmtiFrameInfo *_frame_buffer;
@ -449,26 +448,25 @@ private:
jvmtiError _result;
public:
VM_GetStackTrace(JvmtiEnv *env, JavaThread *java_thread,
jint start_depth, jint max_count,
jvmtiFrameInfo* frame_buffer, jint* count_ptr) {
_env = env;
_java_thread = java_thread;
_start_depth = start_depth;
_max_count = max_count;
_frame_buffer = frame_buffer;
_count_ptr = count_ptr;
GetStackTraceClosure(JvmtiEnv *env, jint start_depth, jint max_count,
jvmtiFrameInfo* frame_buffer, jint* count_ptr)
: HandshakeClosure("GetStackTrace"),
_env(env),
_start_depth(start_depth),
_max_count(max_count),
_frame_buffer(frame_buffer),
_count_ptr(count_ptr),
_result(JVMTI_ERROR_THREAD_NOT_ALIVE) {
}
jvmtiError result() { return _result; }
VMOp_Type type() const { return VMOp_GetStackTrace; }
void doit();
void do_thread(Thread *target);
};
// forward declaration
struct StackInfoNode;
// VM operation to get stack trace at safepoint.
class VM_GetMultipleStackTraces : public VM_Operation {
// Get stack trace at safepoint or at direct handshake.
class MultipleStackTracesCollector {
private:
JvmtiEnv *_env;
jint _max_frame_count;
@ -482,58 +480,82 @@ private:
struct StackInfoNode *head() { return _head; }
void set_head(StackInfoNode *head) { _head = head; }
protected:
public:
MultipleStackTracesCollector(JvmtiEnv *env, jint max_frame_count)
: _env(env),
_max_frame_count(max_frame_count),
_stack_info(NULL),
_result(JVMTI_ERROR_NONE),
_frame_count_total(0),
_head(NULL) {
}
void set_result(jvmtiError result) { _result = result; }
void fill_frames(jthread jt, JavaThread *thr, oop thread_oop);
void allocate_and_fill_stacks(jint thread_count);
public:
VM_GetMultipleStackTraces(JvmtiEnv *env, jint max_frame_count) {
_env = env;
_max_frame_count = max_frame_count;
_frame_count_total = 0;
_head = NULL;
_result = JVMTI_ERROR_NONE;
}
VMOp_Type type() const { return VMOp_GetMultipleStackTraces; }
jvmtiStackInfo *stack_info() { return _stack_info; }
jvmtiError result() { return _result; }
};
// VM operation to get stack trace at safepoint.
class VM_GetAllStackTraces : public VM_GetMultipleStackTraces {
class VM_GetAllStackTraces : public VM_Operation {
private:
JavaThread *_calling_thread;
jint _final_thread_count;
MultipleStackTracesCollector _collector;
public:
VM_GetAllStackTraces(JvmtiEnv *env, JavaThread *calling_thread,
jint max_frame_count)
: VM_GetMultipleStackTraces(env, max_frame_count) {
_calling_thread = calling_thread;
: _calling_thread(calling_thread),
_final_thread_count(0),
_collector(env, max_frame_count) {
}
VMOp_Type type() const { return VMOp_GetAllStackTraces; }
void doit();
jint final_thread_count() { return _final_thread_count; }
jvmtiStackInfo *stack_info() { return _collector.stack_info(); }
jvmtiError result() { return _collector.result(); }
};
// VM operation to get stack trace at safepoint.
class VM_GetThreadListStackTraces : public VM_GetMultipleStackTraces {
class VM_GetThreadListStackTraces : public VM_Operation {
private:
jint _thread_count;
const jthread* _thread_list;
MultipleStackTracesCollector _collector;
public:
VM_GetThreadListStackTraces(JvmtiEnv *env, jint thread_count, const jthread* thread_list, jint max_frame_count)
: VM_GetMultipleStackTraces(env, max_frame_count) {
_thread_count = thread_count;
_thread_list = thread_list;
: _thread_count(thread_count),
_thread_list(thread_list),
_collector(env, max_frame_count) {
}
VMOp_Type type() const { return VMOp_GetThreadListStackTraces; }
void doit();
jvmtiStackInfo *stack_info() { return _collector.stack_info(); }
jvmtiError result() { return _collector.result(); }
};
// HandshakeClosure to get single stack trace.
class GetSingleStackTraceClosure : public HandshakeClosure {
private:
JavaThread *_calling_thread;
jthread _jthread;
MultipleStackTracesCollector _collector;
public:
GetSingleStackTraceClosure(JvmtiEnv *env, JavaThread *calling_thread,
jthread thread, jint max_frame_count)
: HandshakeClosure("GetSingleStackTrace"),
_calling_thread(calling_thread),
_jthread(thread),
_collector(env, max_frame_count) {
}
void do_thread(Thread *target);
jvmtiStackInfo *stack_info() { return _collector.stack_info(); }
jvmtiError result() { return _collector.result(); }
};
// VM operation to count stack frames at safepoint.
class VM_GetFrameCount : public VM_Operation {

View File

@ -79,8 +79,6 @@
template(UpdateForPopTopFrame) \
template(SetFramePop) \
template(GetObjectMonitorUsage) \
template(GetStackTrace) \
template(GetMultipleStackTraces) \
template(GetAllStackTraces) \
template(GetThreadListStackTraces) \
template(GetFrameCount) \

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, NTT DATA.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8242428
* @summary Verifies JVMTI GetThreadListStackTraces API with thread_count = 1
* @library /test/lib
* @run main/othervm/native -agentlib:OneGetThreadListStackTraces OneGetThreadListStackTraces
*
*/
public class OneGetThreadListStackTraces {
private static native void checkCallStacks(Thread thread);
public static void main(String[] args) throws Exception {
/* Check call stack native */
checkCallStacks(Thread.currentThread());
}
}

View File

@ -0,0 +1,142 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, NTT DATA.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <jni.h>
#include <jvmti.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_FRAMES 100
#define ERR_MSG_LEN 1024
#ifdef __cplusplus
extern "C" {
#endif
static jvmtiEnv *jvmti = NULL;
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
return jvm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_11);
}
static void check_frame_info(JNIEnv *env, jvmtiFrameInfo *fi1, jvmtiFrameInfo *fi2) {
char err_msg[ERR_MSG_LEN] = {0};
if (fi1->method != fi2->method) { /* jvmtiFrameInfo::method */
snprintf(err_msg, sizeof(err_msg),
"method is different: fi1 = %" PRIxPTR ", fi2 = %" PRIxPTR,
(uintptr_t)fi1->method, (uintptr_t)fi2->method);
env->FatalError(err_msg);
} else if (fi1->location != fi2->location) { /* jvmtiFrameInfo::location */
snprintf(err_msg, sizeof(err_msg),
"location is different: fi1 = %" PRId64 ", fi2 = %" PRId64,
(int64_t)fi1->location, (int64_t)fi2->location);
env->FatalError(err_msg);
}
}
static void check_stack_info(JNIEnv *env, jvmtiStackInfo *si1, jvmtiStackInfo *si2) {
char err_msg[ERR_MSG_LEN] = {0};
jboolean is_same = env->IsSameObject(si1->thread, si2->thread);
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->FatalError(__FILE__);
}
if (!is_same) { /* jvmtiStackInfo::thread */
snprintf(err_msg, sizeof(err_msg),
"thread is different: si1 = %" PRIxPTR ", si2 = %" PRIxPTR,
(uintptr_t)si1->thread, (uintptr_t)si2->thread);
env->FatalError(err_msg);
} else if (si1->state != si2->state) { /* jvmtiStackInfo::state */
snprintf(err_msg, sizeof(err_msg),
"state is different: si1 = %d, si2 = %d", si1->state, si2->state);
env->FatalError(err_msg);
} else if (si1->frame_count != si2->frame_count) { /* jvmtiStackInfo::frame_count */
snprintf(err_msg, sizeof(err_msg),
"frame_count is different: si1 = %d, si2 = %d",
si1->frame_count, si2->frame_count);
env->FatalError(err_msg);
} else {
/* Iterate all jvmtiFrameInfo to check */
for (int i = 0; i < si1->frame_count; i++) {
check_frame_info(env, &si1->frame_buffer[i], &si2->frame_buffer[i]);
}
}
}
JNIEXPORT void JNICALL Java_OneGetThreadListStackTraces_checkCallStacks(JNIEnv *env, jclass cls, jthread thread) {
jvmtiStackInfo *stack_info, *target_info, *target_one_info;
jvmtiError result;
char err_msg[ERR_MSG_LEN] = {0};
/* Get all stack traces */
jint num_threads;
result = jvmti->GetAllStackTraces(MAX_FRAMES, &stack_info, &num_threads);
if (result != JVMTI_ERROR_NONE) {
snprintf(err_msg, sizeof(err_msg),
"GetAllStackTraces(): result = %d", result);
env->FatalError(err_msg);
}
/* Find jvmtiStackInfo for `thread` (in arguments) */
jboolean is_same;
target_info = NULL;
for (jint i = 0; i < num_threads; i++) {
is_same = env->IsSameObject(stack_info[i].thread, thread);
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->FatalError(__FILE__);
}
if (is_same) {
target_info = &stack_info[i];
break;
}
}
if (target_info == NULL) {
env->FatalError("Target thread not found");
}
/*
* Get jvmtiStackInfo via GetThreadListStackTraces().
* It expects to perform in Thread Local Handshake because thread count is 1.
*/
result = jvmti->GetThreadListStackTraces(1, &thread,
MAX_FRAMES, &target_one_info);
if (result != JVMTI_ERROR_NONE) {
snprintf(err_msg, sizeof(err_msg),
"GetThreadListStackTraces(): result = %d", result);
env->FatalError(err_msg);
}
check_stack_info(env, target_info, target_one_info);
jvmti->Deallocate(reinterpret_cast<unsigned char *>(stack_info));
jvmti->Deallocate(reinterpret_cast<unsigned char *>(target_one_info));
}
#ifdef __cplusplus
}
#endif