1 /* 2 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2020, NTT DATA. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 #include <jni.h> 26 #include <jvmti.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 30 #define MAX_FRAMES 100 31 #define EXCEPTION_MSG_LEN 1024 32 33 #ifdef __cplusplus 34 extern "C" { 35 #endif 36 37 static jvmtiEnv *jvmti = NULL; 38 static jmethodID Thread_getId; 39 40 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 41 return (*jvm)->GetEnv(jvm, (void**) &jvmti, JVMTI_VERSION_11); 42 } 43 44 static jboolean is_same_thread(JNIEnv *env, jthread th1, jthread th2) { 45 jlong th1_id, th2_id; 46 jboolean result = JNI_FALSE; 47 48 th1_id = (*env)->CallLongMethod(env, th1, Thread_getId); 49 if (!(*env)->ExceptionOccurred(env)) { 50 th2_id = (*env)->CallLongMethod(env, th2, Thread_getId); 51 if (th1_id == th2_id) { 52 result = JNI_TRUE; 53 } 54 } 55 56 if ((*env)->ExceptionOccurred(env)) { 57 (*env)->ExceptionDescribe(env); 58 (*env)->FatalError(env, __FILE__); 59 } 60 61 return result; 62 } 63 64 JNIEXPORT void JNICALL Java_GetThreadListStackTraces_checkCallStacks(JNIEnv *env, jclass cls) { 65 jvmtiStackInfo *stack_info_1, *stack_info_2; 66 jthread *jthreads; 67 jint num_threads; 68 jvmtiError result; 69 jclass thread_class; 70 char exception_msg[EXCEPTION_MSG_LEN] = {0}; 71 jint i, j; 72 73 thread_class = (*env)->FindClass(env, "java/lang/Thread"); 74 if ((*env)->ExceptionOccurred(env)) { 75 return; 76 } 77 Thread_getId = (*env)->GetMethodID(env, thread_class, "getId", "()J"); 78 if ((*env)->ExceptionOccurred(env)) { 79 return; 80 } 81 82 /* Get all stack traces to compare */ 83 result = (*jvmti)->GetAllStackTraces(jvmti, MAX_FRAMES, 84 &stack_info_1, &num_threads); 85 if (result != JVMTI_ERROR_NONE) { 86 snprintf(exception_msg, sizeof(exception_msg), 87 "GetAllStackTraces(): result = %d", result); 88 (*env)->FatalError(env, exception_msg); 89 } 90 91 /* Create jthread array to pass GetThreadListStackTraces() */ 92 jthreads = (jthread *)malloc(sizeof(jthread) * num_threads); 93 for (i = 0; i < num_threads; i++) { 94 jthreads[i] = stack_info_1[i].thread; 95 } 96 97 /* Get all stack traces from GetThreadListStackTraces() (Test target) */ 98 result = (*jvmti)->GetThreadListStackTraces(jvmti, num_threads, jthreads, 99 MAX_FRAMES, &stack_info_2); 100 if (result != JVMTI_ERROR_NONE) { 101 snprintf(exception_msg, sizeof(exception_msg), 102 "GetThreadListStackTraces(): result = %d", result); 103 (*env)->FatalError(env, exception_msg); 104 } 105 106 /* Iterate all jvmtiStackInfo to check */ 107 for (i = 0; i < num_threads, *exception_msg != '\0'; i++) { 108 if (!is_same_thread(env, stack_info_1[i].thread, stack_info_2[i].thread)) { /* jvmtiStackInfo::thread */ 109 snprintf(exception_msg, sizeof(exception_msg), 110 "thread[%d] is different: stack_info_1 = %p, stack_info_2 = %p", 111 i, stack_info_1[i].thread, stack_info_2[i].thread); 112 } else if (stack_info_1[i].state != stack_info_2[i].state) { /* jvmtiStackInfo::state */ 113 snprintf(exception_msg, sizeof(exception_msg), 114 "state[%d] is different: stack_info_1 = %d, stack_info_2 = %d", 115 i, stack_info_1[i].state, stack_info_2[i].state); 116 } else if (stack_info_1[i].frame_count != stack_info_2[i].frame_count) { /* jvmtiStackInfo::frame_count */ 117 snprintf(exception_msg, sizeof(exception_msg), 118 "frame_count[%d] is different: stack_info_1 = %d, stack_info_2 = %d", 119 i, stack_info_1[i].frame_count, stack_info_2[i].frame_count); 120 } else { 121 /* Iterate all jvmtiFrameInfo to check */ 122 for (j = 0; j < stack_info_1[i].frame_count; j++) { 123 if (stack_info_1[i].frame_buffer[j].method != stack_info_2[i].frame_buffer[j].method) { /* jvmtiFrameInfo::method */ 124 snprintf(exception_msg, sizeof(exception_msg), 125 "thread [%d] frame_buffer[%d].method is different: stack_info_1 = %lx, stack_info_2 = %lx", 126 i, j, stack_info_1[i].frame_buffer[j].method, stack_info_1[i].frame_buffer[j].method); 127 break; 128 } else if (stack_info_1[i].frame_buffer[j].location != stack_info_2[i].frame_buffer[j].location) { /* jvmtiFrameInfo::location */ 129 snprintf(exception_msg, sizeof(exception_msg), 130 "thread [%d] frame_buffer[%d].location is different: stack_info_1 = %ld, stack_info_2 = %ld", 131 i, j, stack_info_1[i].frame_buffer[j].location, stack_info_1[i].frame_buffer[j].location); 132 break; 133 } 134 } 135 } 136 } 137 138 /* If error message is not empty, it is thrown as RuntimeException */ 139 if (*exception_msg != '\0') { 140 (*env)->FatalError(env, exception_msg); 141 } 142 143 (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info_1); 144 (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info_2); 145 } 146 147 #ifdef __cplusplus 148 } 149 #endif