--- old/src/hotspot/share/prims/jvmtiEnv.cpp 2020-07-09 09:19:42.905593976 +0900 +++ new/src/hotspot/share/prims/jvmtiEnv.cpp 2020-07-09 09:19:42.823591478 +0900 @@ -1535,15 +1535,14 @@ 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 @@ 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 */ --- old/src/hotspot/share/prims/jvmtiEnvBase.cpp 2020-07-09 09:19:43.228603814 +0900 +++ new/src/hotspot/share/prims/jvmtiEnvBase.cpp 2020-07-09 09:19:43.148601378 +0900 @@ -816,13 +816,14 @@ #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(®_map); HandleMark hm(current_thread); @@ -1154,8 +1155,14 @@ // 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 @@ // 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 @@ // 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 @@ !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 @@ } 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); } --- old/src/hotspot/share/prims/jvmtiEnvBase.hpp 2020-07-09 09:19:43.535613165 +0900 +++ new/src/hotspot/share/prims/jvmtiEnvBase.hpp 2020-07-09 09:19:43.454610698 +0900 @@ -437,11 +437,10 @@ 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 @@ 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 @@ 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 { --- old/src/hotspot/share/runtime/vmOperations.hpp 2020-07-09 09:19:43.832622211 +0900 +++ new/src/hotspot/share/runtime/vmOperations.hpp 2020-07-09 09:19:43.752619775 +0900 @@ -79,8 +79,6 @@ template(UpdateForPopTopFrame) \ template(SetFramePop) \ template(GetObjectMonitorUsage) \ - template(GetStackTrace) \ - template(GetMultipleStackTraces) \ template(GetAllStackTraces) \ template(GetThreadListStackTraces) \ template(GetFrameCount) \ --- /dev/null 2020-07-09 08:37:40.616819600 +0900 +++ new/test/hotspot/jtreg/serviceability/jvmti/GetThreadListStackTraces/OneGetThreadListStackTraces.java 2020-07-09 09:19:44.046628730 +0900 @@ -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()); + } +} --- /dev/null 2020-07-09 08:37:40.616819600 +0900 +++ new/test/hotspot/jtreg/serviceability/jvmti/GetThreadListStackTraces/libOneGetThreadListStackTraces.cpp 2020-07-09 09:19:44.373638690 +0900 @@ -0,0 +1,140 @@ +/* + * 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 +#include +#include +#include + +#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(&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 = %p, fi2 = %p", + fi1->method, fi2->method); + env->FatalError(err_msg); + } else if (fi1->location != fi2->location) { /* jvmtiFrameInfo::location */ + snprintf(err_msg, sizeof(err_msg), + "location is different: fi1 = %lld, fi2 = %lld", + fi1->location, 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 = %p, si2 = %p", si1->thread, 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(stack_info)); + jvmti->Deallocate(reinterpret_cast(target_one_info)); +} + +#ifdef __cplusplus +} +#endif