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 29 #define MAX_FRAMES 100 30 #define EXCEPTION_MSG_LEN 1024 31 32 #ifdef __cplusplus 33 extern "C" { 34 #endif 35 36 static jvmtiEnv *jvmti = NULL; 37 static jmethodID Thread_getId; 38 39 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 40 return (*jvm)->GetEnv(jvm, (void**) &jvmti, JVMTI_VERSION_11); 41 } 42 43 static jboolean is_same_thread(JNIEnv *env, jthread th1, jthread th2) { 44 jlong th1_id, th2_id; 45 46 th1_id = (*env)->CallLongMethod(env, th1, Thread_getId); 47 th2_id = (*env)->CallLongMethod(env, th2, Thread_getId); 48 49 return (th1_id == th2_id) ? JNI_TRUE : JNI_FALSE; 50 } 51 52 JNIEXPORT void JNICALL Java_OneGetThreadListStackTraces_checkCallStacks(JNIEnv *env, jclass cls, jthread thread) { 53 jvmtiStackInfo *stack_info, *target_info, *target_one_info; 54 jint num_threads; 55 jvmtiError result; 56 jclass exception_class, thread_class; 57 char exception_msg[EXCEPTION_MSG_LEN] = {0}; 58 jint i; 59 60 exception_class = (*env)->FindClass(env, "java/lang/RuntimeException"); 61 thread_class = (*env)->FindClass(env, "java/lang/Thread"); 62 Thread_getId = (*env)->GetMethodID(env, thread_class, "getId", "()J"); 63 64 /* Get all stack traces */ 65 result = (*jvmti)->GetAllStackTraces(jvmti, MAX_FRAMES, 66 &stack_info, &num_threads); 67 if (result != JVMTI_ERROR_NONE) { 68 snprintf(exception_msg, sizeof(exception_msg), 69 "GetAllStackTraces(): result = %d", result); 70 (*env)->ThrowNew(env, exception_class, exception_msg); 71 return; 72 } 73 74 /* Find jvmtiStackInfo for `thread` (in arguments) */ 75 target_info = NULL; 76 for (i = 0; i < num_threads; i++) { 77 if (is_same_thread(env, stack_info[i].thread, thread)) { 78 target_info = &stack_info[i]; 79 break; 80 } 81 } 82 if (target_info == NULL) { 83 (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info); 84 (*env)->ThrowNew(env, exception_class, "Target thread not found"); 85 return; 86 } 87 88 /* 89 * Get jvmtiStackInfo via GetThreadListStackTraces(). 90 * It expects to perform in Thread Local Handshake because thread count is 1. 91 */ 92 result = (*jvmti)->GetThreadListStackTraces(jvmti, 1, &thread, 93 MAX_FRAMES, &target_one_info); 94 if (result != JVMTI_ERROR_NONE) { 95 snprintf(exception_msg, sizeof(exception_msg), 96 "GetThreadListStackTraces(): result = %d", result); 97 (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info); 98 (*env)->ThrowNew(env, exception_class, exception_msg); 99 return; 100 } 101 102 if (!is_same_thread(env, target_info->thread, target_one_info->thread)) { /* jvmtiStackinfo::thread */ 103 snprintf(exception_msg, sizeof(exception_msg), 104 "thread is different: target_info = %p, target_one_info = %p", 105 target_info->thread, target_one_info->thread); 106 } else if (target_info->state != target_one_info->state) { /* jvmtiStackInfo::state */ 107 snprintf(exception_msg, sizeof(exception_msg), 108 "state is different: target_info = %d, target_one_info = %d", 109 target_info->state, target_one_info->state); 110 } else if (target_info->frame_count != target_one_info->frame_count) { /* jvmtiStackInfo::frame_count */ 111 snprintf(exception_msg, sizeof(exception_msg), 112 "frame_count is different: target_info = %d, target_one_info = %d", 113 target_info->frame_count, target_one_info->frame_count); 114 } else { 115 /* Iterate all jvmtiFrameInfo to check */ 116 for (i = 0; i < target_info->frame_count; i++) { 117 if (target_info->frame_buffer[i].method != target_one_info->frame_buffer[i].method) { /* jvmtiFrameInfo::method */ 118 snprintf(exception_msg, sizeof(exception_msg), 119 "frame_buffer[%d].method is different: target_info = %lx, target_one_info = %lx", 120 i, target_info->frame_buffer[i].method, target_one_info->frame_buffer[i].method); 121 break; 122 } else if (target_info->frame_buffer[i].location != target_one_info->frame_buffer[i].location) { /* jvmtiFrameInfo::location */ 123 snprintf(exception_msg, sizeof(exception_msg), 124 "frame_buffer[%d].location is different: target_info = %ld, target_one_info = %ld", 125 i, target_info->frame_buffer[i].location, target_one_info->frame_buffer[i].location); 126 break; 127 } 128 } 129 } 130 131 (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info); 132 (*jvmti)->Deallocate(jvmti, (unsigned char *)target_one_info); 133 134 /* If error message is not empty, it is thrown as RuntimeException */ 135 if (*exception_msg != '\0') { 136 (*env)->ThrowNew(env, exception_class, exception_msg); 137 } 138 } 139 140 #ifdef __cplusplus 141 } 142 #endif