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 47 th1_id = (*env)->CallLongMethod(env, th1, Thread_getId); 48 th2_id = (*env)->CallLongMethod(env, th2, Thread_getId); 49 50 return (th1_id == th2_id) ? JNI_TRUE : JNI_FALSE; 51 } 52 53 JNIEXPORT void JNICALL Java_GetThreadListStackTraces_checkCallStacks(JNIEnv *env, jclass cls) { 54 jvmtiStackInfo *stack_info_1, *stack_info_2; 55 jthread *jthreads; 56 jint num_threads; 57 jvmtiError result; 58 jclass exception_class, thread_class; 59 char exception_msg[EXCEPTION_MSG_LEN] = {0}; 60 jint i, j; 61 62 exception_class = (*env)->FindClass(env, "java/lang/RuntimeException"); 63 thread_class = (*env)->FindClass(env, "java/lang/Thread"); 64 Thread_getId = (*env)->GetMethodID(env, thread_class, "getId", "()J"); 65 66 /* Get all stack traces to compare */ 67 result = (*jvmti)->GetAllStackTraces(jvmti, MAX_FRAMES, 68 &stack_info_1, &num_threads); 69 if (result != JVMTI_ERROR_NONE) { 70 snprintf(exception_msg, sizeof(exception_msg), 71 "GetAllStackTraces(): result = %d", result); 72 (*env)->ThrowNew(env, exception_class, exception_msg); 73 return; 74 } 75 76 /* Create jthread array to pass GetThreadListStackTraces() */ 77 jthreads = (jthread *)malloc(sizeof(jthread) * num_threads); 78 for (i = 0; i < num_threads; i++) { 79 jthreads[i] = stack_info_1[i].thread; 80 } 81 82 /* Get all stack traces from GetThreadListStackTraces() (Test target) */ 83 result = (*jvmti)->GetThreadListStackTraces(jvmti, num_threads, jthreads, 84 MAX_FRAMES, &stack_info_2); 85 if (result != JVMTI_ERROR_NONE) { 86 snprintf(exception_msg, sizeof(exception_msg), 87 "GetThreadListStackTraces(): result = %d", result); 88 (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info_1); 89 (*env)->ThrowNew(env, exception_class, exception_msg); 90 return; 91 } 92 93 /* Iterate all jvmtiStackInfo to check */ 94 for (i = 0; i < num_threads; i++) { 95 if (!is_same_thread(env, stack_info_1[i].thread, stack_info_2[i].thread)) { /* jvmtiStackInfo::thread */ 96 snprintf(exception_msg, sizeof(exception_msg), 97 "thread[%d] is different: stack_info_1 = %p, stack_info_2 = %p", 98 i, stack_info_1[i].thread, stack_info_2[i].thread); 99 } else if (stack_info_1[i].state != stack_info_2[i].state) { /* jvmtiStackInfo::state */ 100 snprintf(exception_msg, sizeof(exception_msg), 101 "state[%d] is different: stack_info_1 = %d, stack_info_2 = %d", 102 i, stack_info_1[i].state, stack_info_2[i].state); 103 } else if (stack_info_1[i].frame_count != stack_info_2[i].frame_count) { /* jvmtiStackInfo::frame_count */ 104 snprintf(exception_msg, sizeof(exception_msg), 105 "frame_count[%d] is different: stack_info_1 = %d, stack_info_2 = %d", 106 i, stack_info_1[i].frame_count, stack_info_2[i].frame_count); 107 } else { 108 /* Iterate all jvmtiFrameInfo to check */ 109 for (j = 0; j < stack_info_1[i].frame_count; j++) { 110 if (stack_info_1[i].frame_buffer[j].method != stack_info_2[i].frame_buffer[j].method) { /* jvmtiFrameInfo::method */ 111 snprintf(exception_msg, sizeof(exception_msg), 112 "thread [%d] frame_buffer[%d].method is different: stack_info_1 = %lx, stack_info_2 = %lx", 113 i, j, stack_info_1[i].frame_buffer[j].method, stack_info_1[i].frame_buffer[j].method); 114 break; 115 } else if (stack_info_1[i].frame_buffer[j].location != stack_info_2[i].frame_buffer[j].location) { /* jvmtiFrameInfo::location */ 116 snprintf(exception_msg, sizeof(exception_msg), 117 "thread [%d] frame_buffer[%d].location is different: stack_info_1 = %ld, stack_info_2 = %ld", 118 i, j, stack_info_1[i].frame_buffer[j].location, stack_info_1[i].frame_buffer[j].location); 119 break; 120 } 121 } 122 } 123 } 124 125 (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info_1); 126 (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info_2); 127 128 /* If error message is not empty, it is thrown as RuntimeException */ 129 if (*exception_msg != '\0') { 130 (*env)->ThrowNew(env, exception_class, exception_msg); 131 } 132 } 133 134 #ifdef __cplusplus 135 } 136 #endif