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