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 }