--- /dev/null 2020-06-24 14:55:59.786443500 +0900 +++ new/test/hotspot/jtreg/serviceability/jvmti/GetThreadListStackTraces/libOneGetThreadListStackTraces.c 2020-06-24 15:18:11.337227139 +0900 @@ -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 +#include +#include + +#define MAX_FRAMES 100 +#define EXCEPTION_MSG_LEN 1024 + +#ifdef __cplusplus +extern "C" { +#endif + +static jvmtiEnv *jvmti = NULL; +static jmethodID Thread_getId; + +JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + return (*jvm)->GetEnv(jvm, (void**) &jvmti, JVMTI_VERSION_11); +} + +static jboolean is_same_thread(JNIEnv *env, jthread th1, jthread th2) { + jlong th1_id, th2_id; + + th1_id = (*env)->CallLongMethod(env, th1, Thread_getId); + th2_id = (*env)->CallLongMethod(env, th2, Thread_getId); + + return (th1_id == th2_id) ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT void JNICALL Java_OneGetThreadListStackTraces_checkCallStacks(JNIEnv *env, jclass cls, jthread thread) { + jvmtiStackInfo *stack_info, *target_info, *target_one_info; + jint num_threads; + jvmtiError result; + jclass exception_class, thread_class; + char exception_msg[EXCEPTION_MSG_LEN] = {0}; + jint i; + + exception_class = (*env)->FindClass(env, "java/lang/RuntimeException"); + thread_class = (*env)->FindClass(env, "java/lang/Thread"); + Thread_getId = (*env)->GetMethodID(env, thread_class, "getId", "()J"); + + /* Get all stack traces */ + result = (*jvmti)->GetAllStackTraces(jvmti, MAX_FRAMES, + &stack_info, &num_threads); + if (result != JVMTI_ERROR_NONE) { + snprintf(exception_msg, sizeof(exception_msg), + "GetAllStackTraces(): result = %d", result); + (*env)->ThrowNew(env, exception_class, exception_msg); + return; + } + + /* Find jvmtiStackInfo for `thread` (in arguments) */ + target_info = NULL; + for (i = 0; i < num_threads; i++) { + if (is_same_thread(env, stack_info[i].thread, thread)) { + target_info = &stack_info[i]; + break; + } + } + if (target_info == NULL) { + (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info); + (*env)->ThrowNew(env, exception_class, "Target thread not found"); + return; + } + + /* + * Get jvmtiStackInfo via GetThreadListStackTraces(). + * It expects to perform in Thread Local Handshake because thread count is 1. + */ + result = (*jvmti)->GetThreadListStackTraces(jvmti, 1, &thread, + MAX_FRAMES, &target_one_info); + if (result != JVMTI_ERROR_NONE) { + snprintf(exception_msg, sizeof(exception_msg), + "GetThreadListStackTraces(): result = %d", result); + (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info); + (*env)->ThrowNew(env, exception_class, exception_msg); + return; + } + + if (!is_same_thread(env, target_info->thread, target_one_info->thread)) { /* jvmtiStackinfo::thread */ + snprintf(exception_msg, sizeof(exception_msg), + "thread is different: target_info = %p, target_one_info = %p", + target_info->thread, target_one_info->thread); + } else if (target_info->state != target_one_info->state) { /* jvmtiStackInfo::state */ + snprintf(exception_msg, sizeof(exception_msg), + "state is different: target_info = %d, target_one_info = %d", + target_info->state, target_one_info->state); + } else if (target_info->frame_count != target_one_info->frame_count) { /* jvmtiStackInfo::frame_count */ + snprintf(exception_msg, sizeof(exception_msg), + "frame_count is different: target_info = %d, target_one_info = %d", + target_info->frame_count, target_one_info->frame_count); + } else { + /* Iterate all jvmtiFrameInfo to check */ + for (i = 0; i < target_info->frame_count; i++) { + if (target_info->frame_buffer[i].method != target_one_info->frame_buffer[i].method) { /* jvmtiFrameInfo::method */ + snprintf(exception_msg, sizeof(exception_msg), + "frame_buffer[%d].method is different: target_info = %lx, target_one_info = %lx", + i, target_info->frame_buffer[i].method, target_one_info->frame_buffer[i].method); + break; + } else if (target_info->frame_buffer[i].location != target_one_info->frame_buffer[i].location) { /* jvmtiFrameInfo::location */ + snprintf(exception_msg, sizeof(exception_msg), + "frame_buffer[%d].location is different: target_info = %ld, target_one_info = %ld", + i, target_info->frame_buffer[i].location, target_one_info->frame_buffer[i].location); + break; + } + } + } + + (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info); + (*jvmti)->Deallocate(jvmti, (unsigned char *)target_one_info); + + /* If error message is not empty, it is thrown as RuntimeException */ + if (*exception_msg != '\0') { + (*env)->ThrowNew(env, exception_class, exception_msg); + } +} + +#ifdef __cplusplus +} +#endif