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 <errno.h>
  28 #include <stdio.h>
  29 #include <stdlib.h>
  30 
  31 #define MAX_FRAMES 100
  32 #define ERR_MSG_LEN 1024
  33 
  34 #ifdef __cplusplus
  35 extern "C" {
  36 #endif
  37 
  38 static jvmtiEnv *jvmti = NULL;
  39 
  40 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
  41   return jvm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_11);
  42 }
  43 
  44 static void check_frame_info(JNIEnv *env, jvmtiFrameInfo *fi1, jvmtiFrameInfo *fi2) {
  45   char err_msg[ERR_MSG_LEN] = {0};
  46   if (fi1->method != fi2->method) { /* jvmtiFrameInfo::method */
  47     snprintf(err_msg, sizeof(err_msg),
  48              "method is different: fi1 = %lx, fi2 = %lx",
  49              fi1->method, fi2->method);
  50     env->FatalError(err_msg);
  51   } else if (fi1->location != fi2->location) { /* jvmtiFrameInfo::location */
  52     snprintf(err_msg, sizeof(err_msg),
  53              "location is different: fi1 = %ld, fi2 = %ld",
  54              fi1->location, fi2->location);
  55     env->FatalError(err_msg);
  56   }
  57 }
  58 
  59 static void check_stack_info(JNIEnv *env, jvmtiStackInfo *si1, jvmtiStackInfo *si2) {
  60   char err_msg[ERR_MSG_LEN] = {0};
  61 
  62   jboolean is_same = env->IsSameObject(si1->thread, si2->thread);
  63   if (env->ExceptionOccurred()) {
  64     env->ExceptionDescribe();
  65     env->FatalError(__FILE__);
  66   }
  67 
  68   if (!is_same) { /* jvmtiStackInfo::thread */
  69     snprintf(err_msg, sizeof(err_msg),
  70              "thread is different: si1 = %p, si2 = %p", si1->thread, si2->thread);
  71     env->FatalError(err_msg);
  72   } else if (si1->state != si2->state) { /* jvmtiStackInfo::state */
  73     snprintf(err_msg, sizeof(err_msg),
  74              "state is different: si1 = %d, si2 = %d", si1->state, si2->state);
  75     env->FatalError(err_msg);
  76   } else if (si1->frame_count != si2->frame_count) { /* jvmtiStackInfo::frame_count */
  77     snprintf(err_msg, sizeof(err_msg),
  78              "frame_count is different: si1 = %d, si2 = %d",
  79              si1->frame_count, si2->frame_count);
  80     env->FatalError(err_msg);
  81   } else {
  82     /* Iterate all jvmtiFrameInfo to check */
  83     for (int i = 0; i < si1->frame_count; i++) {
  84       check_frame_info(env, &si1->frame_buffer[i], &si2->frame_buffer[i]);
  85     }
  86   }
  87 }
  88 
  89 JNIEXPORT void JNICALL Java_GetThreadListStackTraces_checkCallStacks(JNIEnv *env, jclass cls) {
  90   jvmtiError result;
  91   char err_msg[ERR_MSG_LEN] = {0};
  92   jthread *jthreads;
  93   jint num_threads;
  94   jvmtiStackInfo *si1, *si2;
  95 
  96   /* Get all stack traces to compare */
  97   result = jvmti->GetAllStackTraces(MAX_FRAMES, &si1, &num_threads);
  98   if (result != JVMTI_ERROR_NONE) {
  99     snprintf(err_msg, sizeof(err_msg),
 100              "GetAllStackTraces(): result = %d", result);
 101     env->FatalError(err_msg);
 102   }
 103 
 104   /* Create jthread array to pass GetThreadListStackTraces() */
 105   jthreads = reinterpret_cast<jthread *>(malloc(sizeof(jthread) * num_threads));
 106   if (jthreads == NULL) {
 107     snprintf(err_msg, sizeof(err_msg), "malloc(): errno = %d", errno);
 108     env->FatalError(err_msg);
 109   }
 110   for (jint i = 0; i < num_threads; i++) {
 111     jthreads[i] = si1[i].thread;
 112   }
 113 
 114   /* Get all stack traces from GetThreadListStackTraces() (Test target) */
 115   result = jvmti->GetThreadListStackTraces(num_threads, jthreads, MAX_FRAMES, &si2);
 116   if (result != JVMTI_ERROR_NONE) {
 117     snprintf(err_msg, sizeof(err_msg),
 118              "GetThreadListStackTraces(): result = %d", result);
 119     env->FatalError(err_msg);
 120   }
 121 
 122   /* Iterate all jvmtiStackInfo to check */
 123   for (jint i = 0; i < num_threads; i++) {
 124     check_stack_info(env, si1, si2);
 125   }
 126 
 127   free(jthreads);
 128   jvmti->Deallocate(reinterpret_cast<unsigned char *>(si1));
 129   jvmti->Deallocate(reinterpret_cast<unsigned char *>(si2));
 130 }
 131 
 132 JNIEXPORT void JNICALL Java_OneGetThreadListStackTraces_checkCallStacks(JNIEnv *env, jclass cls, jthread thread) {
 133   jvmtiStackInfo *stack_info, *target_info, *target_one_info;
 134   jvmtiError result;
 135   char err_msg[ERR_MSG_LEN] = {0};
 136 
 137   /* Get all stack traces */
 138   jint num_threads;
 139   result = jvmti->GetAllStackTraces(MAX_FRAMES, &stack_info, &num_threads);
 140   if (result != JVMTI_ERROR_NONE) {
 141     snprintf(err_msg, sizeof(err_msg),
 142              "GetAllStackTraces(): result = %d", result);
 143     env->FatalError(err_msg);
 144   }
 145 
 146   /* Find jvmtiStackInfo for `thread` (in arguments) */
 147   jboolean is_same;
 148   target_info = NULL;
 149   for (jint i = 0; i < num_threads; i++) {
 150     is_same = env->IsSameObject(stack_info[i].thread, thread);
 151     if (env->ExceptionOccurred()) {
 152       env->ExceptionDescribe();
 153       env->FatalError(__FILE__);
 154     }
 155     if (is_same) {
 156       target_info = &stack_info[i];
 157       break;
 158     }
 159   }
 160   if (target_info == NULL) {
 161     env->FatalError("Target thread not found");
 162   }
 163 
 164   /*
 165    * Get jvmtiStackInfo via GetThreadListStackTraces().
 166    * It expects to perform in Thread Local Handshake because thread count is 1.
 167    */
 168   result = jvmti->GetThreadListStackTraces(1, &thread,
 169                                            MAX_FRAMES, &target_one_info);
 170   if (result != JVMTI_ERROR_NONE) {
 171     snprintf(err_msg, sizeof(err_msg),
 172              "GetThreadListStackTraces(): result = %d", result);
 173     env->FatalError(err_msg);
 174   }
 175 
 176   check_stack_info(env, target_info, target_one_info);
 177 
 178   jvmti->Deallocate(reinterpret_cast<unsigned char *>(stack_info));
 179   jvmti->Deallocate(reinterpret_cast<unsigned char *>(target_one_info));
 180 }
 181 
 182 #ifdef __cplusplus
 183 }
 184 #endif