github.com/luchsh/agentlib.go@v0.0.0-20221115155834-ffd0caec4d72/jgo/wrapper.c (about)

     1  //
     2  // Copyright 2020 chuanshenglu@gmail.com
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  // http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  #include "wrapper.h"
    18  #include <stdio.h>
    19  #include <stdlib.h>
    20  #include <string.h>
    21  #include <stdarg.h>
    22  
    23  // if should print debug output
    24  static jint debug_output = JNI_TRUE;
    25  
    26  #define DEBUG(code) do { \
    27    if (debug_output == JNI_TRUE) { \
    28      code; \
    29    }   \
    30  } while(0)
    31  
    32  #define ARRAY_LEN(arr)  (sizeof(arr) / sizeof(arr[0]))
    33  
    34  // Just to lable that this call is to Go funcs
    35  #define GO_CALL(code) do {\
    36    code; \
    37  } while(0)
    38  
    39  // Main loop of the forward thread to make JNI, JVMTI calls
    40  // in a correct Java thread context
    41  extern void MainForwardLoop();
    42  // primary interface to transport JVMTI event to GO level
    43  extern void OnJvmtiEvent(int event_id,
    44                           uintptr_t jvmti,
    45                           uintptr_t params_addr, // aruments are packed into uint64 array
    46                           int params_len);
    47  
    48  // entry of the newly create thread
    49  static void
    50  go_bridge_thread_entry(jvmtiEnv* jvmti_env,
    51                         JNIEnv* jni_env,
    52                         void* arg) {
    53    DEBUG(printf("C: Bridge thread started\n"));
    54  
    55    // enter Go realm
    56    GO_CALL(MainForwardLoop());
    57  }
    58  
    59  // Allocate and initialize a new java.lang.Thread object
    60  static jthread
    61  allocate_thread_obj(JNIEnv* env) {
    62    jclass clazz = (*env)->FindClass(env, "Ljava/lang/Thread;");
    63    if ((*env)->ExceptionOccurred(env) != NULL) {
    64      printf("Cannot find class java.lang.Thread\n");
    65      (*env)->ExceptionClear(env);
    66      return NULL;
    67    }
    68    jmethodID constructor = (*env)->GetMethodID(env, clazz, "<init>", "()V");
    69    if ((*env)->ExceptionOccurred(env) != NULL) {
    70      printf("Cannot get default constructor of thread class\n");
    71      (*env)->ExceptionClear(env);
    72      return NULL;
    73    }
    74    jthread thread_obj = (*env)->NewObject(env, clazz, constructor);
    75    if ((*env)->ExceptionOccurred(env) != NULL) {
    76      printf("Cannot create a new thread object\n");
    77      (*env)->ExceptionClear(env);
    78      return NULL;
    79    }
    80    return thread_obj;
    81  }
    82  
    83  // extra internal initialization work
    84  static void __internalOnVMInit(jvmtiEnv *jvmti,
    85              JNIEnv* jni,
    86              jthread thread) {
    87    DEBUG(printf("VMInit event triggered!\n"));
    88    // create a agent thread to forward commands from Go runtime to Java runtime
    89    // because neither JNIEnv or jvmtiEnv exists on Go's 'm'
    90    jthread thread_obj = allocate_thread_obj(jni);
    91    jvmtiError jvmti_res = (*jvmti)->RunAgentThread(jvmti,
    92                                         thread_obj,
    93                                         &go_bridge_thread_entry,
    94                                         NULL,
    95                                         JVMTI_THREAD_NORM_PRIORITY);
    96    if (jvmti_res != JVMTI_ERROR_NONE) {
    97      printf("Failed to start agent thread\n");
    98    }
    99  }
   100  
   101  // global JVMTI callback table
   102  static jvmtiEventCallbacks *_callbacks =NULL;
   103  
   104  // number of args (include jvmti and jni) for each event callback
   105  static size_t nof_args_for_event[] = {
   106      3, /* JVMTI_EVENT_VM_INIT = 50, */
   107      3, /* JVMTI_EVENT_VM_DEATH = 51, */
   108      3, /* JVMTI_EVENT_THREAD_START = 52, */
   109      3, /* JVMTI_EVENT_THREAD_END = 53, */
   110      10, /* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK = 54, */
   111      4, /* JVMTI_EVENT_CLASS_LOAD = 55, */
   112      4, /* JVMTI_EVENT_CLASS_PREPARE = 56, */
   113      2, /* JVMTI_EVENT_VM_START = 57, */
   114      8, /* JVMTI_EVENT_EXCEPTION = 58, */
   115      6, /* JVMTI_EVENT_EXCEPTION_CATCH = 59, */
   116      5, /* JVMTI_EVENT_SINGLE_STEP = 60, */
   117      5, /* JVMTI_EVENT_FRAME_POP = 61, */
   118      5, /* JVMTI_EVENT_BREAKPOINT = 62, */
   119      8, /* JVMTI_EVENT_FIELD_ACCESS = 63, */
   120      10, /* JVMTI_EVENT_FIELD_MODIFICATION = 64, */
   121      4, /* JVMTI_EVENT_METHOD_ENTRY = 65, */
   122      6, /* JVMTI_EVENT_METHOD_EXIT = 66, */
   123      6, /* JVMTI_EVENT_NATIVE_METHOD_BIND = 67, */
   124      7, /* JVMTI_EVENT_COMPILED_METHOD_LOAD = 68, */
   125      3, /* JVMTI_EVENT_COMPILED_METHOD_UNLOAD = 69, */
   126      4, /* JVMTI_EVENT_DYNAMIC_CODE_GENERATED = 70, */
   127      1, /* JVMTI_EVENT_DATA_DUMP_REQUEST = 71, */
   128      5, /* JVMTI_EVENT_MONITOR_WAIT = 73, */
   129      5, /* JVMTI_EVENT_MONITOR_WAITED = 74, */
   130      4, /* JVMTI_EVENT_MONITOR_CONTENDED_ENTER = 75, */
   131      4, /* JVMTI_EVENT_MONITOR_CONTENDED_ENTERED = 76, */
   132      5, /* JVMTI_EVENT_RESOURCE_EXHAUSTED = 80, */
   133      1, /* JVMTI_EVENT_GARBAGE_COLLECTION_START = 81, */
   134      1, /* JVMTI_EVENT_GARBAGE_COLLECTION_FINISH = 82, */
   135      2, /* JVMTI_EVENT_OBJECT_FREE = 83, */
   136      6, /* JVMTI_EVENT_VM_OBJECT_ALLOC = 84, */
   137      // Below are fake
   138      0, /* JVMTI_EVENT_AGENT_UNLOAD = 85, */
   139  };
   140  
   141  // Handler generator MACRO.
   142  // Can be simplified using C++ template function
   143  //   `template<int EventId> void onJvmtiEvent(jvmtiEnv* jvmti, ...);`
   144  // but it may involve SWIG, a little complex, do it later.
   145  #define GEN_JVMTI_EVENT_HANDLER(EVENT)                                    \
   146    static void onJvmtiEvent_ ## EVENT(jvmtiEnv* jvmti, ...) {              \
   147      DEBUG(printf("C: event " #EVENT " triggerred\n"));                    \
   148      size_t idx = EVENT - JVMTI_MIN_EVENT_TYPE_VAL;                        \
   149      size_t nof_args = nof_args_for_event[idx] - 1 /* rm jvmti */;         \
   150      uintptr_t args[nof_args];                                             \
   151      va_list ap;                                                           \
   152      va_start(ap, jvmti);                                                  \
   153      for (int i = 0; i < nof_args; ++i) {                                  \
   154        args[i] = va_arg(ap, uintptr_t);                                    \
   155      }                                                                     \
   156      va_end(ap);                                                           \
   157      if (EVENT == JVMTI_EVENT_VM_INIT) {                                   \
   158        __internalOnVMInit(jvmti, (JNIEnv*)(args[0]), (jthread)(args[1]));  \
   159      }                                                                     \
   160      OnJvmtiEvent(EVENT, (uintptr_t)jvmti, (uintptr_t)args, ARRAY_LEN(args)); \
   161    }
   162  
   163  // Generate event handler methods
   164  FOR_EACH_JVMTI_EVENT(GEN_JVMTI_EVENT_HANDLER)
   165  
   166  // a table to quickly map event index to corresponding handler method
   167  #define NOF_JVMTI_EVENTS  (JVMTI_MAX_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL + 1)
   168  #define EXPAND_JVMTI_EVENT_HANDLER(EVENT) (&onJvmtiEvent_##EVENT),
   169  static void* builtin_handler_table[NOF_JVMTI_EVENTS] = {
   170    FOR_EACH_JVMTI_EVENT(EXPAND_JVMTI_EVENT_HANDLER)
   171    /* fake events has separated entry */
   172    // TODO: wrong index
   173    &Agent_OnUnload, // JVMTI_EVENT_AGENT_UNLOAD
   174  };
   175  
   176  // a table containing all jvmti event names
   177  #define JVMTI_EVENT_NAME(EVENT)  (#EVENT),
   178  static const char* jvmti_event_names[NOF_JVMTI_EVENTS] = {
   179    FOR_EACH_JVMTI_EVENT(JVMTI_EVENT_NAME)
   180  };
   181  
   182  #define NOF_FAKE_JVMTI_EVENTS   (JVMTI_MAX_FAKE_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL + 1)
   183  static const char* jvmti_fake_event_names[NOF_FAKE_JVMTI_EVENTS] = {
   184    FOR_EACH_FAKE_JVMTI_EVENT(JVMTI_EVENT_NAME)
   185  };
   186  
   187  static const char* get_jvmti_event_name(int event_id) {
   188    if (event_id <= JVMTI_MAX_EVENT_TYPE_VAL && event_id >= JVMTI_MIN_EVENT_TYPE_VAL) {
   189      int idx = event_id - JVMTI_MIN_EVENT_TYPE_VAL;
   190      return jvmti_event_names[idx];
   191    } else if (event_id <= JVMTI_MAX_FAKE_EVENT_TYPE_VAL && event_id >= JVMTI_MIN_FAKE_EVENT_TYPE_VAL) {
   192      int idx = event_id - JVMTI_MIN_FAKE_EVENT_TYPE_VAL;
   193      return jvmti_fake_event_names[idx];
   194    }
   195    DEBUG(printf("C: no name for event id %d\n", event_id));
   196    return NULL;
   197  }
   198  
   199  // Setup JVMTI callbacks
   200  // NOTE: this method highly depends on the sequence and layout defined in jvmti.h
   201  static void linkLocalJvmtiCallback(int event_id) {
   202    void** ftable = (void**)&(_callbacks->VMInit); // VMInit is the first entry of the struct
   203    int idx = event_id - JVMTI_MIN_EVENT_TYPE_VAL;
   204    ftable[idx] = builtin_handler_table[idx];
   205  }
   206  
   207  void EnableJvmtiCallback(void* p, int event_id) {
   208    jvmtiEnv* jvmti = (jvmtiEnv*)p;
   209    //printf("%d-%d-%d", JVMTI_MIN_EVENT_TYPE_VAL, event_id, JVMTI_MAX_EVENT_TYPE_VAL);
   210    if (event_id >= JVMTI_MIN_EVENT_TYPE_VAL && event_id <= JVMTI_MAX_EVENT_TYPE_VAL) {
   211      if (jvmti == NULL) {
   212        printf("NULL jvmtiEnv pointer\n");
   213        return;
   214      }
   215   
   216      linkLocalJvmtiCallback(event_id);
   217  
   218      jvmtiError jvmti_res = (*jvmti)->SetEventCallbacks(jvmti, _callbacks, sizeof(jvmtiEventCallbacks));
   219      if (jvmti_res != JVMTI_ERROR_NONE) {
   220        printf("Failed to set jvmti callbacks: %s\n", get_jvmti_event_name(event_id));
   221        return;
   222      }
   223      jvmti_res = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, event_id, NULL);
   224      if (jvmti_res != JVMTI_ERROR_NONE) {
   225        printf("Failed to set jvmti callback notification mode: %s\n", get_jvmti_event_name(event_id));
   226        return;
   227      }
   228      DEBUG(printf("C: enabled event id=%d, name=%s\n", event_id, get_jvmti_event_name(event_id)));
   229    } else if (event_id >= JVMTI_MIN_FAKE_EVENT_TYPE_VAL && event_id <= JVMTI_MAX_FAKE_EVENT_TYPE_VAL) {
   230      // nothing to do
   231      DEBUG(printf("C: enabled event id=%d, name=%s\n", event_id, get_jvmti_event_name(event_id)));
   232    } else {
   233      DEBUG(printf("C: invalid event id=%d, name=%s\n", event_id, get_jvmti_event_name(event_id)));
   234    }
   235  }
   236  
   237  //export OnAgentLoad
   238  #ifdef __cplusplus
   239  extern "C" {
   240  #endif
   241  extern void OnAgentLoad(uintptr_t, uintptr_t, const char*);
   242  extern void OnAgentUnload();
   243  #ifdef __cplusplus
   244  }
   245  #endif
   246  
   247  // JVMTI start-up point
   248  jint Agent_OnLoad(JavaVM* javaVM, char* options, void* reserved) {
   249    DEBUG(printf("agent.go loaded\n"));
   250  
   251    jvmtiEnv* jvmti = NULL;
   252    jvmtiError jvmti_res = (*javaVM)->GetEnv(javaVM, (void**)&jvmti, JVMTI_VERSION_1_1);
   253    if (jvmti_res != JVMTI_ERROR_NONE || jvmti == NULL) {
   254      printf("Failed to get jvmtiEnv from JavaVM");
   255      return JNI_ERR;
   256    }
   257  
   258    if (_callbacks == NULL) {
   259      _callbacks = (jvmtiEventCallbacks*)malloc(sizeof(jvmtiEventCallbacks));
   260      memset(_callbacks, 0, sizeof(jvmtiEventCallbacks));
   261    }
   262  
   263    GO_CALL(OnAgentLoad((uintptr_t)javaVM, jvmti, options));
   264  
   265    return JNI_OK;
   266  }
   267  
   268  // destroy resources;
   269  void Agent_OnUnload(JavaVM* javaVM) {
   270    GO_CALL(OnAgentUnload());
   271  
   272    jvmtiEventCallbacks* cb = _callbacks;
   273    _callbacks = NULL;
   274    free(cb);
   275  }