/* * 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; 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_OneGetThreadListStackTraces_checkCallStacks(JNIEnv *env, jclass cls, jthread thread) { jvmtiStackInfo *stack_info, *target_info, *target_one_info; jint num_threads; jvmtiError result; jclass thread_class; char exception_msg[EXCEPTION_MSG_LEN] = {0}; jint i; 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 */ 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)->FatalError(env, exception_msg); } /* 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) { (*env)->FatalError(env, "Target thread not found"); } /* * 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); (*env)->FatalError(env, exception_msg); } 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; } } } /* If error message is not empty, it is thrown as RuntimeException */ if (*exception_msg != '\0') { (*env)->FatalError(env, exception_msg); } (*jvmti)->Deallocate(jvmti, (unsigned char *)stack_info); (*jvmti)->Deallocate(jvmti, (unsigned char *)target_one_info); } #ifdef __cplusplus } #endif