--- /dev/null 2020-07-03 13:21:10.126784900 +0900 +++ new/test/hotspot/jtreg/serviceability/jvmti/GetThreadListStackTraces/libGetThreadListStackTraces.c 2020-07-03 15:19:16.711166220 +0900 @@ -0,0 +1,156 @@ +/* + * 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 +#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; + jboolean result = JNI_FALSE; + + th1_id = (*env)->CallLongMethod(env, th1, Thread_getId); + if (!(*env)->ExceptionOccurred(env)) { + th2_id = (*env)->CallLongMethod(env, th2, Thread_getId); + if (th1_id == th2_id) { + result = JNI_TRUE; + } + } + + if ((*env)->ExceptionOccurred(env)) { + (*env)->ExceptionDescribe(env); + (*env)->FatalError(env, __FILE__); + } + + return result; +} + +JNIEXPORT void JNICALL Java_GetThreadListStackTraces_checkCallStacks(JNIEnv *env, jclass cls) { + jvmtiStackInfo *stack_info_1, *stack_info_2; + jthread *jthreads; + jint num_threads; + jvmtiError result; + jclass thread_class; + char exception_msg[EXCEPTION_MSG_LEN] = {0}; + jint i, j; + + thread_class = (*env)->FindClass(env, "java/lang/Thread"); + if ((*env)->ExceptionOccurred(env)) { + return; + } + Thread_getId = (*env)->GetMethodID(env, thread_class, "getId", "()J"); + if ((*env)->ExceptionOccurred(env)) { + return; + } + + /* Get all stack traces to compare */ + result = (*jvmti)->GetAllStackTraces(jvmti, MAX_FRAMES, + &stack_info_1, &num_threads); + if (result != JVMTI_ERROR_NONE) { + snprintf(exception_msg, sizeof(exception_msg), + "GetAllStackTraces(): result = %d", result); + (*env)->FatalError(env, exception_msg); + } + + /* Create jthread array to pass GetThreadListStackTraces() */ + jthreads = (jthread *)malloc(sizeof(jthread) * num_threads); + if (jthreads == NULL) { + snprintf(exception_msg, sizeof(exception_msg), + "malloc(): errno = %d", errno); + (*env)->FatalError(env, exception_msg); + } + for (i = 0; i < num_threads; i++) { + jthreads[i] = stack_info_1[i].thread; + } + + /* Get all stack traces from GetThreadListStackTraces() (Test target) */ + result = (*jvmti)->GetThreadListStackTraces(jvmti, num_threads, jthreads, + MAX_FRAMES, &stack_info_2); + if (result != JVMTI_ERROR_NONE) { + snprintf(exception_msg, sizeof(exception_msg), + "GetThreadListStackTraces(): result = %d", result); + (*env)->FatalError(env, exception_msg); + } + + /* Iterate all jvmtiStackInfo to check */ + for (i = 0; i < num_threads, *exception_msg != '\0'; i++) { + if (!is_same_thread(env, stack_info_1[i].thread, stack_info_2[i].thread)) { /* jvmtiStackInfo::thread */ + snprintf(exception_msg, sizeof(exception_msg), + "thread[%d] is different: stack_info_1 = %p, stack_info_2 = %p", + i, stack_info_1[i].thread, stack_info_2[i].thread); + } else if (stack_info_1[i].state != stack_info_2[i].state) { /* jvmtiStackInfo::state */ + snprintf(exception_msg, sizeof(exception_msg), + "state[%d] is different: stack_info_1 = %d, stack_info_2 = %d", + i, stack_info_1[i].state, stack_info_2[i].state); + } else if (stack_info_1[i].frame_count != stack_info_2[i].frame_count) { /* jvmtiStackInfo::frame_count */ + snprintf(exception_msg, sizeof(exception_msg), + "frame_count[%d] is different: stack_info_1 = %d, stack_info_2 = %d", + i, stack_info_1[i].frame_count, stack_info_2[i].frame_count); + } else { + /* Iterate all jvmtiFrameInfo to check */ + for (j = 0; j < stack_info_1[i].frame_count; j++) { + if (stack_info_1[i].frame_buffer[j].method != stack_info_2[i].frame_buffer[j].method) { /* jvmtiFrameInfo::method */ + snprintf(exception_msg, sizeof(exception_msg), + "thread [%d] frame_buffer[%d].method is different: stack_info_1 = %lx, stack_info_2 = %lx", + i, j, stack_info_1[i].frame_buffer[j].method, stack_info_1[i].frame_buffer[j].method); + break; + } else if (stack_info_1[i].frame_buffer[j].location != stack_info_2[i].frame_buffer[j].location) { /* jvmtiFrameInfo::location */ + snprintf(exception_msg, sizeof(exception_msg), + "thread [%d] frame_buffer[%d].location is different: stack_info_1 = %ld, stack_info_2 = %ld", + i, j, stack_info_1[i].frame_buffer[j].location, stack_info_1[i].frame_buffer[j].location); + break; + } + } + } + } + + /* If error message is not empty, it is thrown as RuntimeException */ + if (*exception_msg != '\0') { + (*env)->FatalError(env, exception_msg); + } + + free(jthreads); + (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info_1); + (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info_2); +} + +#ifdef __cplusplus +} +#endif