#include <stdio.h> #include <stdlib.h> #include <string.h> #include <jni.h> #include <jvmti.h> #include <errno.h> #include <sys/types.h> #include <pthread.h> #include <time.h> /* Global static data */ static jvmtiEnv *jvmti; static long int gc_count = 0; static jrawMonitorID lock; int thread_started = 0; int ptime = 4; char cmd[1024]={'l','s','\0'}; pthread_mutex_t agnt_lock; pthread_cond_t agnt_cvar; static void JNICALL gc_listener(jvmtiEnv* jvmti, JNIEnv* jni, void *p) { struct timespec wait_time; int pid; pthread_mutex_lock(&agnt_lock); do { if (gc_count == 1) { clock_gettime(CLOCK_REALTIME, &wait_time); wait_time.tv_sec += ptime; if (pthread_cond_timedwait(&agnt_cvar, &agnt_lock, &wait_time) == ETIMEDOUT && gc_count == 1) { /* take defined action */ fprintf(stderr, "gc pause time exceeded defined value\n"); pid = fork(); if (pid == 0) { execvp(cmd, NULL); } } } pthread_cond_wait(&agnt_cvar, &agnt_lock); } while (1); } /* Creates a new jthread */ static jthread alloc_thread(JNIEnv *env) { jclass thrClass; jmethodID cid; jthread res; thrClass = (*env)->FindClass(env, "java/lang/Thread"); cid = (*env)->GetMethodID(env, thrClass, "<init>", "()V"); res = (*env)->NewObject(env, thrClass, cid); return res; } /* Callback for JVMTI_EVENT_VM_INIT */ static void JNICALL vm_init(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) { jvmtiError err; int ret; err = (*jvmti)->RunAgentThread(jvmti, alloc_thread(env), &gc_listener, NULL, JVMTI_THREAD_NORM_PRIORITY); if (err != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: failed to create gc agent thread, err=%d\n", err); } else { thread_started = (pthread_mutex_init(&agnt_lock, NULL) == 0)?(pthread_cond_init(&agnt_cvar, NULL) == 0)?1:0:0; } } /* Callback for JVMTI_EVENT_GARBAGE_COLLECTION_START */ static void JNICALL gc_start(jvmtiEnv* jvmti) { jvmtiError err; fprintf(stderr, "GarbageCollectionStart...\n"); if(thread_started) { pthread_mutex_lock(&agnt_lock); gc_count = 1; pthread_cond_signal(&agnt_cvar); pthread_mutex_unlock(&agnt_lock); } } /* Callback for JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */ static void JNICALL gc_finish(jvmtiEnv* jvmti_env) { jvmtiError err; fprintf(stderr, "GarbageCollectionFinish...\n"); if(thread_started) { pthread_mutex_lock(&agnt_lock); gc_count = 0; pthread_cond_signal(&agnt_cvar); pthread_mutex_unlock(&agnt_lock); } } /* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { jint rc; jvmtiError err; jvmtiCapabilities capabilities; jvmtiEventCallbacks callbacks; /* Get JVMTI environment */ rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1); if (rc != JNI_OK) { fprintf(stderr, "ERROR: Unable to create jvmtiEnv, GetEnv failed, error=%d\n", rc); return -1; } /* Get/Add JVMTI capabilities */ err = (*jvmti)->GetCapabilities(jvmti, &capabilities); if (err != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: GetCapabilities failed, error=%d\n", err); return -1; } capabilities.can_generate_garbage_collection_events = 1; err = (*jvmti)->AddCapabilities(jvmti, &capabilities); if (err != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: AddCapabilities failed, error=%d\n", err); return -1; } /* Set callbacks and enable event notifications */ memset(&callbacks, 0, sizeof(callbacks)); callbacks.VMInit = &vm_init; callbacks.GarbageCollectionStart = &gc_start; callbacks.GarbageCollectionFinish = &gc_finish; err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks)); if (err != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: SetEventCallbacks failed, error=%d\n", err); return -1; } err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL); if (err != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: SetEventNotificationMode for VM_INIT failed, error=%d\n", err); return -1; } err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_START, NULL); if (err != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: SetEventNotificationMode for GC_START failed, error=%d\n", err); return -1; } err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL); if (err != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: SetEventNotificationMode GC_FINISH failed, error=%d\n", err); return -1; } /* parse the agent options & set corresponding variables */ sscanf(options, "pausetime=%d,command=%s", &ptime, cmd); return 0; } /* Agent_OnUnload() is called last */ JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) { if(thread_started) { /* cleanup required */ } }