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