diff options
Diffstat (limited to 'gprofng/libcollector/jprofile.c')
-rw-r--r-- | gprofng/libcollector/jprofile.c | 1315 |
1 files changed, 1315 insertions, 0 deletions
diff --git a/gprofng/libcollector/jprofile.c b/gprofng/libcollector/jprofile.c new file mode 100644 index 00000000000..9daaa5a267e --- /dev/null +++ b/gprofng/libcollector/jprofile.c @@ -0,0 +1,1315 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" + +#if defined(GPROFNG_JAVA_PROFILING) +#include <alloca.h> +#include <dlfcn.h> /* dlsym() */ +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <sys/param.h> /* MAXPATHLEN */ + +#include <jni.h> +#include <jvmti.h> + +#include "gp-defs.h" +#include "collector.h" +#include "gp-experiment.h" +#include "tsd.h" + +/* TprintfT(<level>,...) definitions. Adjust per module as needed */ +#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings +#define DBG_LT1 1 // for configuration details, warnings +#define DBG_LT2 2 +#define DBG_LT3 3 + +/* ARCH_STRLEN is defined in dbe, copied here */ +#define ARCH_STRLEN(s) ((CALL_UTIL(strlen)(s) + 4 ) & ~0x3) + +/* call frame */ +typedef struct +{ + jint lineno; /* line number in the source file */ + jmethodID method_id; /* method executed in this frame */ +} JVMPI_CallFrame; + +/* call trace */ +typedef struct +{ + JNIEnv *env_id; /* Env where trace was recorded */ + jint num_frames; /* number of frames in this trace */ + JVMPI_CallFrame *frames; /* frames */ +} JVMPI_CallTrace; + +extern void __collector_jprofile_enable_synctrace (void); +int __collector_jprofile_start_attach (void); +static int init_interface (CollectorInterface*); +static int open_experiment (const char *); +static int close_experiment (void); +static int detach_experiment (void); +static void jprof_find_asyncgetcalltrace (void); +static char *apistr = NULL; + +static ModuleInterface module_interface = { + "*"SP_JCLASSES_FILE, /* description, exempt from limit */ + init_interface, /* initInterface */ + open_experiment, /* openExperiment */ + NULL, /* startDataCollection */ + NULL, /* stopDataCollection */ + close_experiment, /* closeExperiment */ + detach_experiment /* detachExperiment (fork child) */ +}; + +static CollectorInterface *collector_interface = NULL; +static CollectorModule jprof_hndl = COLLECTOR_MODULE_ERR; +static int __collector_java_attach = 0; +static JavaVM *jvm; +static jmethodID getResource = NULL; +static jmethodID toExternalForm = NULL; + +/* Java profiling thread specific data */ +typedef struct TSD_Entry +{ + JNIEnv *env; + hrtime_t tstamp; +} TSD_Entry; + +static unsigned tsd_key = COLLECTOR_TSD_INVALID_KEY; +static collector_mutex_t jclasses_lock = COLLECTOR_MUTEX_INITIALIZER; +static int java_gc_on = 0; +static int java_mem_mode = 0; +static int java_sync_mode = 0; +static int is_hotspot_vm = 0; +static void get_jvm_settings (); +static void rwrite (int fd, const void *buf, size_t nbyte); +static void addToDynamicArchive (const char* name, const unsigned char* class_data, int class_data_len); +static void (*AsyncGetCallTrace)(JVMPI_CallTrace*, jint, ucontext_t*) = NULL; +static void (*collector_heap_record)(int, int, void*) = NULL; +static void (*collector_jsync_begin)() = NULL; +static void (*collector_jsync_end)(hrtime_t, void *) = NULL; + +#define gethrtime collector_interface->getHiResTime + +/* + * JVMTI declarations + */ + +static jvmtiEnv *jvmti; +static void jvmti_VMInit (jvmtiEnv*, JNIEnv*, jthread); +static void jvmti_VMDeath (jvmtiEnv*, JNIEnv*); +static void jvmti_ThreadStart (jvmtiEnv*, JNIEnv*, jthread); +static void jvmti_ThreadEnd (jvmtiEnv*, JNIEnv*, jthread); +static void jvmti_CompiledMethodLoad (jvmtiEnv*, jmethodID, jint, const void*, + jint, const jvmtiAddrLocationMap*, const void*); +static void jvmti_CompiledMethodUnload (jvmtiEnv*, jmethodID, const void*); +static void jvmti_DynamicCodeGenerated (jvmtiEnv*, const char*, const void*, jint); +static void jvmti_ClassPrepare (jvmtiEnv*, JNIEnv*, jthread, jclass); +static void jvmti_ClassLoad (jvmtiEnv*, JNIEnv*, jthread, jclass); +//static void jvmti_ClassUnload( jvmtiEnv*, JNIEnv*, jthread, jclass ); +static void jvmti_MonitorEnter (jvmtiEnv *, JNIEnv*, jthread, jobject); +static void jvmti_MonitorEntered (jvmtiEnv *, JNIEnv*, jthread, jobject); +#if 0 +static void jvmti_MonitorWait (jvmtiEnv *, JNIEnv*, jthread, jobject, jlong); +static void jvmti_MonitorWaited (jvmtiEnv *, JNIEnv*, jthread, jobject, jboolean); +#endif +static void jvmti_ClassFileLoadHook (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jclass class_being_redefined, + jobject loader, const char* name, jobject protection_domain, + jint class_data_len, const unsigned char* class_data, + jint* new_class_data_len, unsigned char** new_class_data); +static void jvmti_GarbageCollectionStart (jvmtiEnv *); +static void +jvmti_GarbageCollectionFinish (jvmtiEnv *); +jvmtiEventCallbacks callbacks = { + jvmti_VMInit, // 50 jvmtiEventVMInit; + jvmti_VMDeath, // 51 jvmtiEventVMDeath; + jvmti_ThreadStart, // 52 jvmtiEventThreadStart; + jvmti_ThreadEnd, // 53 jvmtiEventThreadEnd; + jvmti_ClassFileLoadHook, // 54 jvmtiEventClassFileLoadHook; + jvmti_ClassLoad, // 55 jvmtiEventClassLoad; + jvmti_ClassPrepare, // 56 jvmtiEventClassPrepare; + NULL, // 57 reserved57; + NULL, // 58 jvmtiEventException; + NULL, // 59 jvmtiEventExceptionCatch; + NULL, // 60 jvmtiEventSingleStep; + NULL, // 61 jvmtiEventFramePop; + NULL, // 62 jvmtiEventBreakpoint; + NULL, // 63 jvmtiEventFieldAccess; + NULL, // 64 jvmtiEventFieldModification; + NULL, // 65 jvmtiEventMethodEntry; + NULL, // 66 jvmtiEventMethodExit; + NULL, // 67 jvmtiEventNativeMethodBind; + jvmti_CompiledMethodLoad, // 68 jvmtiEventCompiledMethodLoad; + jvmti_CompiledMethodUnload, // 69 jvmtiEventCompiledMethodUnload; + jvmti_DynamicCodeGenerated, // 70 jvmtiEventDynamicCodeGenerated; + NULL, // 71 jvmtiEventDataDumpRequest; + NULL, // 72 jvmtiEventDataResetRequest; + NULL, /*jvmti_MonitorWait,*/ // 73 jvmtiEventMonitorWait; + NULL, /*jvmti_MonitorWaited,*/ // 74 jvmtiEventMonitorWaited; + jvmti_MonitorEnter, // 75 jvmtiEventMonitorContendedEnter; + jvmti_MonitorEntered, // 76 jvmtiEventMonitorContendedEntered; + NULL, // 77 jvmtiEventMonitorContendedExit; + NULL, // 78 jvmtiEventReserved; + NULL, // 79 jvmtiEventReserved; + NULL, // 80 jvmtiEventReserved; + jvmti_GarbageCollectionStart, // 81 jvmtiEventGarbageCollectionStart; + jvmti_GarbageCollectionFinish, // 82 jvmtiEventGarbageCollectionFinish; + NULL, // 83 jvmtiEventObjectFree; + NULL // 84 jvmtiEventVMObjectAlloc; +}; + +typedef jint (JNICALL JNI_GetCreatedJavaVMs_t)(JavaVM **, jsize, jsize *); + +int +init_interface (CollectorInterface *_collector_interface) +{ + collector_interface = _collector_interface; + return COL_ERROR_NONE; +} + +static int +open_experiment (const char *exp) +{ + if (collector_interface == NULL) + return COL_ERROR_JAVAINIT; + TprintfT (0, "jprofile: open_experiment %s\n", exp); + const char *params = collector_interface->getParams (); + const char *args = params; + while (args) + { + if (__collector_strStartWith (args, "j:") == 0) + { + args += 2; + break; + } + args = CALL_UTIL (strchr)(args, ';'); + if (args) + args++; + } + if (args == NULL) /* Java profiling not specified */ + return COL_ERROR_JAVAINIT; + tsd_key = collector_interface->createKey (sizeof ( TSD_Entry), NULL, NULL); + if (tsd_key == (unsigned) - 1) + { + TprintfT (0, "jprofile: TSD key create failed.\n"); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n", + SP_JCMD_CERROR, COL_ERROR_JAVAINIT); + return COL_ERROR_JAVAINIT; + } + else + Tprintf (DBG_LT2, "jprofile: TSD key create succeeded %d.\n", tsd_key); + + args = params; + while (args) + { + if (__collector_strStartWith (args, "H:") == 0) + { + java_mem_mode = 1; + collector_heap_record = (void(*)(int, int, void*))dlsym (RTLD_DEFAULT, "__collector_heap_record"); + } +#if 0 + else if (__collector_strStartWith (args, "s:") == 0) + { + java_sync_mode = 1; + collector_jsync_begin = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_begin"); + collector_jsync_end = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_end"); + } +#endif + args = CALL_UTIL (strchr)(args, ';'); + if (args) + args++; + } + + /* synchronization tracing is enabled by the synctrace module, later in initialization */ + __collector_java_mode = 1; + java_gc_on = 1; + return COL_ERROR_NONE; +} + +/* routine called from the syntrace module to enable Java-API synctrace */ +void +__collector_jprofile_enable_synctrace () +{ + if (__collector_java_mode == 0) + { + TprintfT (DBG_LT1, "jprofile: not turning on Java synctrace; Java mode not enabled\n"); + return; + } + java_sync_mode = 1; + collector_jsync_begin = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_begin"); + collector_jsync_end = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_end"); + TprintfT (DBG_LT1, "jprofile: turning on Java synctrace, and requesting events\n"); +} + +int +__collector_jprofile_start_attach (void) +{ + if (!__collector_java_mode || __collector_java_asyncgetcalltrace_loaded) + return 0; + void *g_sHandle = RTLD_DEFAULT; + /* Now get the function addresses */ + JNI_GetCreatedJavaVMs_t *pfnGetCreatedJavaVMs; + pfnGetCreatedJavaVMs = (JNI_GetCreatedJavaVMs_t *) dlsym (g_sHandle, "JNI_GetCreatedJavaVMs"); + if (pfnGetCreatedJavaVMs != NULL) + { + TprintfT (0, "jprofile attach: pfnGetCreatedJavaVMs is detected.\n"); + JavaVM * vmBuf[1]; // XXXX only detect on jvm + jsize nVMs = 0; + (*pfnGetCreatedJavaVMs)(vmBuf, 1, &nVMs); + if (vmBuf[0] != NULL && nVMs > 0) + { + jvm = vmBuf[0]; + JNIEnv* jni_env = NULL; + (*jvm)->AttachCurrentThread (jvm, (void **) &jni_env, NULL); + Agent_OnLoad (jvm, NULL, NULL); + if ((*jvm)->GetEnv (jvm, (void **) &jni_env, JNI_VERSION_1_2) >= 0 && jni_env && jvmti) + { + jthread thread; + (*jvmti)->GetCurrentThread (jvmti, &thread); +#ifdef DEBUG + collector_thread_t tid; + tid = __collector_thr_self (); + TprintfT (0, "jprofile attach: AttachCurrentThread: thread: %lu jni_env=%p jthread=%p\n", + (unsigned long) tid, jni_env, thread); +#endif /* DEBUG */ + jvmti_VMInit (jvmti, jni_env, thread); + (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_COMPILED_METHOD_LOAD); + (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_DYNAMIC_CODE_GENERATED); + __collector_java_attach = 1; + (*jvm)->DetachCurrentThread (jvm); + } + } + } + return 0; +} + +static int +close_experiment (void) +{ + /* fixme XXXXX add content here */ + /* see detach_experiment() */ + __collector_java_mode = 0; + __collector_java_asyncgetcalltrace_loaded = 0; + __collector_java_attach = 0; + java_gc_on = 0; + java_mem_mode = 0; + java_sync_mode = 0; + is_hotspot_vm = 0; + __collector_mutex_init (&jclasses_lock); + tsd_key = COLLECTOR_TSD_INVALID_KEY; + TprintfT (0, "jprofile: experiment closed.\n"); + return 0; +} + +static int +detach_experiment (void) +/* fork child. Clean up state but don't write to experiment */ +{ + __collector_java_mode = 0; + java_gc_on = 0; + jvm = NULL; + java_mem_mode = 0; + java_sync_mode = 0; + is_hotspot_vm = 0; + jvmti = NULL; + apistr = NULL; + __collector_mutex_init (&jclasses_lock); + tsd_key = COLLECTOR_TSD_INVALID_KEY; + TprintfT (0, "jprofile: detached from experiment.\n"); + return 0; +} + +JNIEXPORT jint JNICALL +JVM_OnLoad (JavaVM *vm, char *options, void *reserved) +{ + jvmtiError err; + int use_jvmti = 0; + if (!__collector_java_mode) + { + TprintfT (DBG_LT1, "jprofile: JVM_OnLoad invoked with java mode disabled\n"); + return JNI_OK; + } + else + TprintfT (DBG_LT1, "jprofile: JVM_OnLoad invoked\n"); + jvm = vm; + jvmti = NULL; + if ((*jvm)->GetEnv (jvm, (void **) &jvmti, JVMTI_VERSION_1_0) >= 0 && jvmti) + { + TprintfT (DBG_LT1, "jprofile: JVMTI found\n"); + use_jvmti = 1; + } + if (!use_jvmti) + { + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\"/>\n", + SP_JCMD_CERROR, COL_ERROR_JVMNOTSUPP); + return JNI_ERR; + } + else + { + Tprintf (DBG_LT0, "\tjprofile: Initializing for JVMTI\n"); + apistr = "JVMTI 1.0"; + + // setup JVMTI + jvmtiCapabilities cpblts; + err = (*jvmti)->GetPotentialCapabilities (jvmti, &cpblts); + if (err == JVMTI_ERROR_NONE) + { + jvmtiCapabilities cpblts_set; + CALL_UTIL (memset)(&cpblts_set, 0, sizeof (cpblts_set)); + + /* Add only those capabilities that are among potential ones */ + cpblts_set.can_get_source_file_name = cpblts.can_get_source_file_name; + Tprintf (DBG_LT1, "\tjprofile: adding can_get_source_file_name capability: %u\n", cpblts.can_get_source_file_name); + + cpblts_set.can_generate_compiled_method_load_events = cpblts.can_generate_compiled_method_load_events; + Tprintf (DBG_LT1, "\tjprofile: adding can_generate_compiled_method_load_events capability: %u\n", cpblts.can_generate_compiled_method_load_events); + + if (java_sync_mode) + { + cpblts_set.can_generate_monitor_events = cpblts.can_generate_monitor_events; + Tprintf (DBG_LT1, "\tjprofile: adding can_generate_monitor_events capability: %u\n", cpblts.can_generate_monitor_events); + } + if (java_gc_on) + { + cpblts_set.can_generate_garbage_collection_events = cpblts.can_generate_garbage_collection_events; + Tprintf (DBG_LT1, "\tjprofile: adding can_generate_garbage_collection_events capability: %u\n", cpblts.can_generate_garbage_collection_events); + } + err = (*jvmti)->AddCapabilities (jvmti, &cpblts_set); + Tprintf (DBG_LT1, "\tjprofile: AddCapabilities() returns: %d\n", err); + } + err = (*jvmti)->SetEventCallbacks (jvmti, &callbacks, sizeof ( callbacks)); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_END, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); + if (java_gc_on) + { + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_START, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL); + } + if (java_mem_mode) + { + // err = (*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, <no event for heap tracing> , NULL ); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\"/>\n", + SP_JCMD_CWARN, COL_WARN_NO_JAVA_HEAP); + java_mem_mode = 0; + } + if (java_sync_mode) + { + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, NULL); + err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, NULL); + //err = (*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_WAIT, NULL ); + //err = (*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_WAITED, NULL ); + } + Tprintf (DBG_LT0, "\tjprofile: JVMTI initialized\n"); + } + + /* JVM still uses collector API on Solaris to notify us about dynamically generated code. + * If we ask it to generate events we'll end up with duplicate entries in the + * map file. + */ + if (use_jvmti) + { + err = (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_DYNAMIC_CODE_GENERATED); + err = (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_COMPILED_METHOD_LOAD); + } + Tprintf (DBG_LT1, "\tjprofile: JVM_OnLoad ok\n"); + return JNI_OK; +} + +/* This is currently just a placeholder */ +JNIEXPORT jint JNICALL +Agent_OnLoad (JavaVM *vm, char *options, void *reserved) +{ + return JVM_OnLoad (vm, options, reserved); +} + +static void +rwrite (int fd, const void *buf, size_t nbyte) +{ + size_t left = nbyte; + size_t res; + char *ptr = (char*) buf; + while (left > 0) + { + res = CALL_UTIL (write)(fd, ptr, left); + if (res == -1) + { + /* XXX: we can't write this record, we probably + * can't write anything else. Ignore. + */ + return; + } + left -= res; + ptr += res; + } +} + +void +get_jvm_settings () +{ + jint res; + JNIEnv *jni; + jclass jcls; + jmethodID jmid; + jstring jstrin; + jstring jstrout; + const char *str; + res = (*jvm)->GetEnv (jvm, (void **) &jni, JNI_VERSION_1_2); + if (res < 0) + return; + + /* I'm not checking if results are valid as JVM is extremely + * sensitive to exceptions that might occur during these JNI calls + * and will die with a fatal error later anyway. + */ + jcls = (*jni)->FindClass (jni, "java/lang/System"); + jmid = (*jni)->GetStaticMethodID (jni, jcls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"); + jstrin = (*jni)->NewStringUTF (jni, "java.class.path"); + jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); + str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; + if (str) + { + collector_interface->writeLog ("<setting %s=\"%s\"/>\n", SP_JCMD_SRCHPATH, str); + (*jni)->ReleaseStringUTFChars (jni, jstrout, str); + } + jstrin = (*jni)->NewStringUTF (jni, "sun.boot.class.path"); + jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); + str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; + if (str) + { + collector_interface->writeLog ("<setting %s=\"%s\"/>\n", SP_JCMD_SRCHPATH, str); + (*jni)->ReleaseStringUTFChars (jni, jstrout, str); + } + jstrin = (*jni)->NewStringUTF (jni, "java.home"); + jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); + str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; + if (str) + { + collector_interface->writeLog ("<setting %s=\"%s/../src.zip\"/>\n", SP_JCMD_SRCHPATH, str); + (*jni)->ReleaseStringUTFChars (jni, jstrout, str); + } + jstrin = (*jni)->NewStringUTF (jni, "java.vm.version"); + jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); + str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; + if (str) + { + (void) collector_interface->writeLog ("<profile name=\"jprofile\" %s=\"%s\" %s=\"%s\"/>\n", + SP_JCMD_JVERSION, str, "api", apistr != NULL ? apistr : "N/A"); + if (__collector_strStartWith (str, "1.4.2_02") < 0) + { + (void) collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\"/>\n", + SP_JCMD_CWARN, COL_WARN_OLDJAVA); + } + (*jni)->ReleaseStringUTFChars (jni, jstrout, str); + } + is_hotspot_vm = 0; + jstrin = (*jni)->NewStringUTF (jni, "sun.management.compiler"); + jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); + str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; + if (str && __collector_strncmp (str, "HotSpot", 7) == 0) + is_hotspot_vm = 1; + + /* Emulate System.setProperty( "collector.init", "true") */ + jmid = (*jni)->GetStaticMethodID (jni, jcls, "setProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + jstrin = (*jni)->NewStringUTF (jni, "collector.init"); + jstrout = (*jni)->NewStringUTF (jni, "true"); + (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin, jstrout); +} + +/* + * JVMTI code + */ + +static void +jvmti_VMInit (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) +{ + jint class_count = 0; + jclass *classes = NULL; + int i; + TprintfT (DBG_LT1, "jprofile: jvmti_VMInit called\n"); + get_jvm_settings (); + + /* determine loaded classes */ + (*jvmti_env)->GetLoadedClasses (jvmti_env, &class_count, &classes); + TprintfT (DBG_LT1, "jprofile: jvmti_VMInit initializing %d classes\n", class_count); + for (i = 0; i < class_count; i++) + { + // PushLocalFrame + jvmti_ClassPrepare (jvmti_env, jni_env, NULL, classes[i]); + // PopLocalFrame + // DeleteLocalRef( classes[i] ); + } + (*jvmti_env)->Deallocate (jvmti_env, (unsigned char*) classes); + getResource = (*jni_env)->GetMethodID (jni_env, (*jni_env)->FindClass (jni_env, "java/lang/ClassLoader"), "getResource", "(Ljava/lang/String;)Ljava/net/URL;"); + toExternalForm = (*jni_env)->GetMethodID (jni_env, (*jni_env)->FindClass (jni_env, "java/net/URL"), "toExternalForm", "()Ljava/lang/String;"); + + /* find the stack unwind routine */ + jprof_find_asyncgetcalltrace (); +} + +static void +jvmti_VMDeath (jvmtiEnv *jvmti_env, JNIEnv* jni_env) +{ + __collector_java_mode = 0; + TprintfT (DBG_LT1, "jprofile: jvmti_VMDeath event received\n"); +} + +static void +jvmti_ThreadStart (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) +{ + jvmtiError err; + jvmtiThreadInfo t_info; + char *thread_name, *group_name, *parent_name; + hrtime_t hrt; + collector_thread_t tid; + thread_name = group_name = parent_name = NULL; + hrt = gethrtime (); + tid = __collector_thr_self (); + TprintfT (DBG_LT1, "jprofile: jvmti_ThreadStart: thread: %lu jni_env=%p jthread=%p\n", + (unsigned long) tid, jni_env, thread); + err = (*jvmti_env)->GetThreadInfo (jvmti_env, thread, &t_info); + if (err == JVMTI_ERROR_NONE) + { + jvmtiThreadGroupInfo g_info; + thread_name = t_info.name; + if (t_info.thread_group) + { + err = (*jvmti_env)->GetThreadGroupInfo (jvmti_env, t_info.thread_group, &g_info); + if (err == JVMTI_ERROR_NONE) + { + group_name = g_info.name; + if (g_info.parent) + { + jvmtiThreadGroupInfo p_info; + err = (*jvmti_env)->GetThreadGroupInfo (jvmti_env, g_info.parent, &p_info); + if (err == JVMTI_ERROR_NONE) + { + parent_name = p_info.name; + // DeleteLocalRef( p_info.parent ); + } + // DeleteLocalRef( g_info.parent ); + } + } + } + // DeleteLocalRef( t_info.thread_group ); + // DeleteLocalRef( t_info.context_class_loader ); + } + if (thread_name == NULL) + thread_name = ""; + if (group_name == NULL) + group_name = ""; + if (parent_name == NULL) + parent_name = ""; + collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\" name=\"%s\" grpname=\"%s\" prntname=\"%s\" tid=\"%lu\" jthr=\"0x%lx\" jenv=\"0x%lx\"/>\n", + SP_JCMD_JTHRSTART, + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), + thread_name, + group_name, + parent_name, + (unsigned long) tid, + thread, + jni_env + ); + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd) + tsd->env = jni_env; +} + +static void +jvmti_ThreadEnd (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) +{ + hrtime_t hrt = gethrtime (); + collector_thread_t tid = __collector_thr_self (); + TprintfT (DBG_LT1, "jprofile: jvmti_ThreadEnd: thread: %lu jni_env=%p jthread=%p\n", + (unsigned long) tid, jni_env, thread); + + collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\" tid=\"%lu\" jthr=\"0x%lx\" jenv=\"0x%lx\"/>\n", + SP_JCMD_JTHREND, + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), + (unsigned long) tid, + thread, + jni_env + ); + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd) + tsd->env = NULL; +} + +/* The following definitions are borrowed from file jvmticmlr.h, part of jdk7 */ +typedef enum +{ + JVMTI_CMLR_DUMMY = 1, + JVMTI_CMLR_INLINE_INFO = 2 +} jvmtiCMLRKind; + +/* + * Record that represents arbitrary information passed through JVMTI + * CompiledMethodLoadEvent void pointer. + */ +typedef struct _jvmtiCompiledMethodLoadRecordHeader +{ + jvmtiCMLRKind kind; /* id for the kind of info passed in the record */ + jint majorinfoversion; /* major and minor info version values. Init'ed */ + jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */ + struct _jvmtiCompiledMethodLoadRecordHeader* next; +} jvmtiCompiledMethodLoadRecordHeader; + +/* + * Record that gives information about the methods on the compile-time + * stack at a specific pc address of a compiled method. Each element in + * the methods array maps to same element in the bcis array. + */ +typedef struct _PCStackInfo +{ + void* pc; /* the pc address for this compiled method */ + jint numstackframes; /* number of methods on the stack */ + jmethodID* methods; /* array of numstackframes method ids */ + jint* bcis; /* array of numstackframes bytecode indices */ +} PCStackInfo; + +/* + * Record that contains inlining information for each pc address of + * an nmethod. + */ +typedef struct _jvmtiCompiledMethodLoadInlineRecord +{ + jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */ + jint numpcs; /* number of pc descriptors in this nmethod */ + PCStackInfo* pcinfo; /* array of numpcs pc descriptors */ +} jvmtiCompiledMethodLoadInlineRecord; + +static void +jvmti_CompiledMethodLoad (jvmtiEnv *jvmti_env, jmethodID method, + jint code_size, const void *code_addr, jint map_length, + const jvmtiAddrLocationMap *map, + const void *compile_info) +{ + TprintfT (DBG_LT2, "jprofile: jvmti_CompiledMethodLoad: mid=0x%lx addr=%p sz=0x%lu map=%p info=%p\n", + (unsigned long) method, code_addr, (long) code_size, map, compile_info); + char name[32]; + CALL_UTIL (snprintf)(name, sizeof (name), "0x%lx", (unsigned long) method); + + /* Parse compile_info to get pc -> bci mapping. + * Don't interpret compile_info from JVMs other than HotSpot. + */ + int lntsize = 0; + DT_lineno *lntable = NULL; + if (compile_info != NULL && is_hotspot_vm) + { + Tprintf (DBG_LT2, "Mapping from compile_info:\n"); + jvmtiCompiledMethodLoadRecordHeader *currec = + (jvmtiCompiledMethodLoadRecordHeader*) compile_info; + while (currec != NULL) + { + if (currec->kind == JVMTI_CMLR_INLINE_INFO) + { + jvmtiCompiledMethodLoadInlineRecord *inrec = + (jvmtiCompiledMethodLoadInlineRecord*) currec; + if (inrec->numpcs <= 0) + break; + lntsize = inrec->numpcs; + lntable = (DT_lineno*) alloca (lntsize * sizeof (DT_lineno)); + PCStackInfo *pcrec = inrec->pcinfo; + DT_lineno *lnorec = lntable; + for (int i = 0; i < lntsize; ++i) + { + for (int j = pcrec->numstackframes - 1; j >= 0; --j) + if (pcrec->methods[j] == method) + { + lnorec->offset = (char*) pcrec->pc - (char*) code_addr; + lnorec->lineno = pcrec->bcis[j]; + Tprintf (DBG_LT2, " pc: 0x%lx bci: 0x%lx\n", + (long) lnorec->offset, (long) lnorec->lineno); + ++lnorec; + break; + } + ++pcrec; + } + break; + } + currec = currec->next; + } + } + else if (map != NULL) + { + Tprintf (DBG_LT2, "Mapping from jvmtiAddrLocationMap:\n"); + lntsize = map_length; + lntable = (DT_lineno*) alloca (lntsize * sizeof (DT_lineno)); + DT_lineno *lnorec = lntable; + for (int i = 0; i < map_length; ++i) + { + lnorec->offset = (char*) map[i].start_address - (char*) code_addr; + lnorec->lineno = (unsigned int) map[i].location; + Tprintf (DBG_LT2, " pc: 0x%lx bci: 0x%lx\n", + (long) lnorec->offset, (long) lnorec->lineno); + ++lnorec; + } + } + __collector_int_func_load (DFUNC_JAVA, name, NULL, (void*) code_addr, + code_size, lntsize, lntable); +} + +static void +jvmti_CompiledMethodUnload (jvmtiEnv *jvmti_env, jmethodID method, const void* code_addr) +{ + __collector_int_func_unload (DFUNC_API, (void*) code_addr); +} + +static void +jvmti_DynamicCodeGenerated (jvmtiEnv *jvmti_env, const char*name, const void *code_addr, jint code_size) +{ + __collector_int_func_load (DFUNC_API, (char*) name, NULL, (void*) code_addr, + code_size, 0, NULL); +} + +static void +addToDynamicArchive (const char* name, const unsigned char* class_data, int class_data_len) +{ + char path[MAXPATHLEN + 1]; + mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + mode_t dmode = fmode | S_IXUSR | S_IXGRP | S_IXOTH; + if (name == NULL) + name = ""; + const char *expdir = collector_interface->getExpDir (); + if (CALL_UTIL (strlen)(expdir) + + CALL_UTIL (strlen)(SP_DYNAMIC_CLASSES) + + CALL_UTIL (strlen)(name) + 8 > sizeof (path)) + return; + CALL_UTIL (snprintf)(path, sizeof (path), "%s/%s/%s.class", expdir, SP_DYNAMIC_CLASSES, name); + + /* Create all path components step by step starting with SP_DYNAMIC_CLASSES */ + char *str = path + CALL_UTIL (strlen)(expdir) + 1 + CALL_UTIL (strlen)(SP_DYNAMIC_CLASSES); + while (str) + { + *str = '\0'; + if (CALL_UTIL (mkdir)(path, dmode) != 0) + { + /* Checking for EEXIST is not enough, access() is more reliable */ + if (CALL_UTIL (access)(path, F_OK) != 0) + { + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_MKDIR, errno, path); + return; + } + } + *str++ = '/'; + str = CALL_UTIL (strchr)(str, '/'); + } + + int fd = CALL_UTIL (open)(path, O_WRONLY | O_CREAT | O_TRUNC, fmode); + if (fd < 0) + { + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_OVWOPEN, errno, path); + return; + } + rwrite (fd, class_data, class_data_len); + CALL_UTIL (close)(fd); +} + +static void +jvmti_ClassFileLoadHook (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jclass class_being_redefined, + jobject loader, const char* name, jobject protection_domain, jint class_data_len, + const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) +{ + jclass loaderlass; + int err; + jvmtiPhase phase_ptr; + char *cname = NULL; + (*jvmti_env)->GetPhase (jvmti_env, &phase_ptr); + + /* skip non live phases */ + if (phase_ptr != JVMTI_PHASE_LIVE) + return; + + /* skip system class loaders */ + if (!loader) + return; + loaderlass = (*jni_env)->GetObjectClass (jni_env, loader); + err = (*jvmti_env)->GetClassSignature (jvmti_env, loaderlass, &cname, NULL); + if (err != JVMTI_ERROR_NONE || !cname || *cname == (char) 0) + return; + + /* skip classes loaded with AppClassLoader (java.class.path) */ + if (__collector_strcmp (cname, "Lsun/misc/Launcher$AppClassLoader;") == 0) + return; + addToDynamicArchive (name, class_data, (int) class_data_len); +} + +#define NO_CLASS_NAME "<noname>" +#define NO_SOURCE_FILE "<Unknown>" + +static void +record_jclass (uint64_t class_id, hrtime_t hrt, const char *cname, const char *sname) +{ + size_t clen = ARCH_STRLEN (cname); + size_t slen = ARCH_STRLEN (sname); + size_t sz = sizeof (ARCH_jclass) + clen + slen; + ARCH_jclass *jcls = (ARCH_jclass*) alloca (sz); + jcls->comm.tsize = sz; + jcls->comm.type = ARCH_JCLASS; + jcls->class_id = class_id; + jcls->tstamp = hrt; + char *str = (char*) (jcls + 1); + size_t i = CALL_UTIL (strlcpy)(str, cname, clen); + str += i; + while (i++ < clen) + *str++ = (char) 0; /* pad with 0's */ + i = CALL_UTIL (strlcpy)(str, sname, slen); + str += i; + while (i++ < slen) + *str++ = (char) 0; /* pad with 0's */ + collector_interface->writeDataPacket (jprof_hndl, (CM_Packet*) jcls); +} + +static void +record_jmethod (uint64_t class_id, uint64_t method_id, + const char *mname, const char *msign) +{ + size_t mnlen = mname ? ARCH_STRLEN (mname) : 0; + size_t mslen = msign ? ARCH_STRLEN (msign) : 0; + size_t sz = sizeof (ARCH_jmethod) + mnlen + mslen; + ARCH_jmethod *jmth = (ARCH_jmethod*) alloca (sz); + if (jmth == NULL) + { + TprintfT (DBG_LT1, "jprofile: record_jmethod ERROR: failed to alloca(%ld)\n", (long) sz); + return; + } + jmth->comm.tsize = sz; + jmth->comm.type = ARCH_JMETHOD; + jmth->class_id = class_id; + jmth->method_id = method_id; + char *str = (char*) (jmth + 1); + if (mname) + { + size_t i = CALL_UTIL (strlcpy)(str, mname, mnlen); + str += i; + while (i++ < mnlen) + *str++ = (char) 0; /* pad with 0's */ + } + if (msign) + { + size_t i = CALL_UTIL (strlcpy)(str, msign, mslen); + str += i; + while (i++ < mslen) + *str++ = (char) 0; /* pad with 0's */ + } + collector_interface->writeDataPacket (jprof_hndl, (CM_Packet*) jmth); +} + +static void +jvmti_ClassPrepare (jvmtiEnv *jvmti_env, JNIEnv* jni_env, + jthread thread, jclass klass) +{ + hrtime_t hrt; + jint mnum; + jmethodID *mptr; + char *cname, *sname; + char *str1 = NULL; + int err = (*jvmti_env)->GetClassSignature (jvmti_env, klass, &str1, NULL); + if (err != JVMTI_ERROR_NONE || str1 == NULL || *str1 == (char) 0) + cname = NO_CLASS_NAME; + else + cname = str1; + if (*cname != 'L') + { + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassPrepare: GetClassSignature failed. err=%d cname=%s\n", err, cname); + return; + } + char *str2 = NULL; + err = (*jvmti_env)->GetSourceFileName (jvmti_env, klass, &str2); + if (err != JVMTI_ERROR_NONE || str2 == NULL || *str2 == (char) 0) + sname = NO_SOURCE_FILE; + else + sname = str2; + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassPrepare: cname=%s sname=%s\n", STR (cname), STR (sname)); + + /* Lock the whole file */ + __collector_mutex_lock (&jclasses_lock); + hrt = gethrtime (); + record_jclass ((unsigned long) klass, hrt, cname, sname); + (*jvmti_env)->Deallocate (jvmti_env, (unsigned char *) str1); + (*jvmti_env)->Deallocate (jvmti_env, (unsigned char *) str2); + err = (*jvmti_env)->GetClassMethods (jvmti_env, klass, &mnum, &mptr); + if (err == JVMTI_ERROR_NONE) + { + for (int i = 0; i < mnum; i++) + { + char *mname, *msign; + err = (*jvmti_env)->GetMethodName (jvmti_env, mptr[i], &mname, &msign, NULL); + if (err != JVMTI_ERROR_NONE) + continue; + record_jmethod ((unsigned long) klass, (unsigned long) mptr[i], mname, msign); + // DeleteLocalRef( mptr[i] ); + } + (*jvmti_env)->Deallocate (jvmti_env, (unsigned char*) mptr); + } + /* Unlock the file */ + __collector_mutex_unlock (&jclasses_lock); +} + +/* + * The CLASS_LOAD event is enabled to enable AsyncGetCallTrace + */ +static void +jvmti_ClassLoad (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jclass klass) +{ + char *cname; + char *str1 = NULL; + int err = (*jvmti_env)->GetClassSignature (jvmti_env, klass, &str1, NULL); + if (err != JVMTI_ERROR_NONE || str1 == NULL || *str1 == (char) 0) + cname = NO_CLASS_NAME; + else + cname = str1; + jstring str = NULL; + const char* resourceName; + jobject classLoader = NULL; + err = (*jvmti)->GetClassLoader (jvmti, klass, &classLoader); + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jprofile: jvmti_ClassLoad err=%d cname=%s\n", err, STR (cname)); + if (err == 0) + { + if (classLoader == NULL) + { + // bootstrap class loader + resourceName = ""; + } + else + { + char* name = (char *) alloca ((CALL_UTIL (strlen)(str1) + 32) * sizeof (char)); + CALL_UTIL (strlcpy)(name, str1 + 1, CALL_UTIL (strlen)(str1)); + name[CALL_UTIL (strlen)(name) - 1] = '\0'; // remove the last ';' + char* p; + for (p = name; *p != '\0'; p++) + if (*p == '.') + *p = '/'; + CALL_UTIL (strlcat)(name, ".class", CALL_UTIL (strlen)(name) + CALL_UTIL (strlen)(".class") + 1); + if (getResource == NULL || toExternalForm == NULL) + { + resourceName = ""; + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassLoad: class %s failed to get path with method missing\n", STR (cname)); + } + else + { + jobject url = (*jni_env)->CallObjectMethod (jni_env, classLoader, getResource, (*jni_env)->NewStringUTF (jni_env, name)); + if (url == NULL) + { + resourceName = ""; + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassLoad: class %s failed to get path\n", STR (cname)); + } + else + { + str = (jstring) (*jni_env)->CallObjectMethod (jni_env, url, toExternalForm); + resourceName = (*jni_env)->GetStringUTFChars (jni_env, str, NULL); + DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassLoad: ARCH_JCLASS_LOCATION(Ox%x) class_id=0x%lx className='%s' fileName '%s'\n", + (int) ARCH_JCLASS_LOCATION, (unsigned long) klass, STR (cname), STR (resourceName)); + size_t clen = ARCH_STRLEN (cname); + size_t slen = ARCH_STRLEN (resourceName); + size_t sz = sizeof (ARCH_jclass) + clen + slen; + ARCH_jclass_location *jcls = (ARCH_jclass_location*) alloca (sz); + jcls->comm.tsize = sz; + jcls->comm.type = ARCH_JCLASS_LOCATION; + jcls->class_id = (unsigned long) klass; + char *str = (char*) (jcls + 1); + size_t i = CALL_UTIL (strlcpy)(str, cname, clen); + str += i; + while (i++ < clen) + { + *str++ = (char) 0; /* pad with 0's */ + } + i = CALL_UTIL (strlcpy)(str, resourceName, slen); + str += i; + while (i++ < slen) + { + *str++ = (char) 0; /* pad with 0's */ + } + /* Lock the whole file */ + __collector_mutex_lock (&jclasses_lock); + collector_interface->writeDataPacket (jprof_hndl, (CM_Packet*) jcls); + /* Unlock the file */ + __collector_mutex_unlock (&jclasses_lock); + } + } + } + } +} + +static void +jvmti_MonitorEnter (jvmtiEnv *jvmti_env, JNIEnv* jni_env, + jthread thread, jobject object) +{ + if (collector_jsync_begin) + collector_jsync_begin (); + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd == NULL) + return; + tsd->tstamp = gethrtime (); +} + +static void +jvmti_MonitorEntered (jvmtiEnv *jvmti_env, JNIEnv* jni_env, + jthread thread, jobject object) +{ + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd == NULL) + return; + if (collector_jsync_end) + collector_jsync_end (tsd->tstamp, object); +} + +static void +jvmti_GarbageCollectionStart (jvmtiEnv *jvmti_env) +{ + hrtime_t hrt = gethrtime (); + collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\"/>\n", + SP_JCMD_GCSTART, + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC) + ); + TprintfT (DBG_LT1, "jprofile: jvmti_GarbageCollectionStart.\n"); +} + +static void +jvmti_GarbageCollectionFinish (jvmtiEnv *jvmti_env) +{ + hrtime_t hrt = gethrtime (); + collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\"/>\n", + SP_JCMD_GCEND, + (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC) + ); + TprintfT (DBG_LT1, "jprofile: jvmti_GarbageCollectionFinish.\n"); +} + +#if 0 +static void +jvmti_MonitorWait (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, + jobject object, jlong timed_out) +{ + if (collector_sync_begin) + collector_sync_begin (); + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd == NULL) + return; + tsd->tstamp = gethrtime (); +} + +static void +jvmti_MonitorWaited (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, + jobject object, jboolean timed_out) +{ + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd == NULL) + return; + if (collector_sync_end) + collector_sync_end (tsd->tstamp, object); +} +#endif + +static void +jprof_find_asyncgetcalltrace () +{ + void *jvmhandle; + if (__collector_VM_ReadByteInstruction == NULL) + __collector_VM_ReadByteInstruction = (int(*)()) dlsym (RTLD_DEFAULT, "Async_VM_ReadByteInstruction"); + + /* look for stack unwind function using default path */ + AsyncGetCallTrace = (void (*)(JVMPI_CallTrace*, jint, ucontext_t*)) + dlsym (RTLD_DEFAULT, "AsyncGetCallTrace"); + if (AsyncGetCallTrace != NULL) + { + __collector_java_asyncgetcalltrace_loaded = 1; + TprintfT (DBG_LT1, "jprofile: AsyncGetCallTrace found with RTLD_DEFAULT\n"); + } + else + { + /* not found there, find libjvm.so, and ask again */ + jvmhandle = dlopen ("libjvm.so", RTLD_LAZY | RTLD_NOLOAD); + if (jvmhandle != NULL) + { + AsyncGetCallTrace = (void (*)(JVMPI_CallTrace*, jint, ucontext_t*)) + dlsym (jvmhandle, "AsyncGetCallTrace"); + } + } + + if (AsyncGetCallTrace == NULL) + { + /* we could not find it -- write collector error */ + TprintfT (0, "jprofile: ERROR -- AsyncGetCallTrace not found in address space\n"); + char *err = dlerror (); + collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s</event>\n", + SP_JCMD_CERROR, COL_ERROR_JVMNOJSTACK, err ? err : ""); + __collector_java_mode = 0; + } + else + { + __collector_java_asyncgetcalltrace_loaded = 1; + TprintfT (DBG_LT1, "jprofile: AsyncGetCallTrace initialized in jprof_jvmpi_init_done_event\n"); + } +} + +int +__collector_ext_jstack_unwind (char *ptr, int sz, ucontext_t *uc) +{ + if (AsyncGetCallTrace == NULL) + { + TprintfT (DBG_LT0, "jprofile: __collector_ext_jstack_unwind: AsyncGetCallTrace is NULL\n"); + return 0; + } + + TSD_Entry *tsd = collector_interface->getKey (tsd_key); + if (tsd == NULL) + { + TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: tsd is NULL\n"); + return 0; + } + if (__collector_java_attach && tsd->env == NULL && jvmti != NULL && jvm != NULL) + { + TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: tsd->env is NULL under attach\n"); + JNIEnv* jni_env = NULL; + (*jvm)->GetEnv (jvm, (void **) &jni_env, JNI_VERSION_1_2); + tsd->env = jni_env; + } + if (tsd->env == NULL) + { + TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: tsd->env is NULL\n"); + return 0; + } + + /* skip the Java stack whenever another signal handler is present */ + if (uc->uc_link) + { + TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: uc->uc_link is non-NULL\n"); + return 0; + } + /* we don't expect Java frames in signal handlers, so + * unroll the list of saved contexts to the topmost one + */ + while (uc->uc_link) + uc = uc->uc_link; + Java_info *jinfo = (Java_info*) ptr; + jinfo->kind = JAVA_INFO; + jinfo->hsize = sizeof (Java_info); + ptr += sizeof (Java_info); + sz -= sizeof (Java_info); + + JVMPI_CallTrace jtrace; + jtrace.env_id = tsd->env; + jtrace.frames = (JVMPI_CallFrame*) ptr; + + /* nframes is how many frames we have room for */ + jint nframes = sz / sizeof (JVMPI_CallFrame); + +#if WSIZE(64) + /* bug 6909545: garbage in 64-bit JAVA_INFO */ + CALL_UTIL (memset)(jtrace.frames, 0, nframes * sizeof (JVMPI_CallFrame)); +#endif + +#if ARCH(SPARC) + // 21328946 JDK bug 8129933 causes <no java callstack recorded> on sparc-Linux + // convert from ucontext_t to sigcontext + struct sigcontext sctx; + sctx.sigc_regs.tpc = uc->uc_mcontext.mc_gregs[MC_PC]; + __collector_memcpy (sctx.sigc_regs.u_regs, &uc->uc_mcontext.mc_gregs[3], sizeof (sctx.sigc_regs.u_regs)); + uc = (ucontext_t *) (&sctx); +#endif /* SPARC */ + AsyncGetCallTrace (&jtrace, nframes, uc); + + if (jtrace.num_frames == nframes) + { + JVMPI_CallFrame *last = &jtrace.frames[nframes - 1]; + last->method_id = (jmethodID) SP_TRUNC_STACK_MARKER; + last->lineno = 0; + } + + /* nframes is how many frames we actually got */ + nframes = jtrace.num_frames; + TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: AsyncGetCallTrace jtrace.numframes = %d\n", nframes); + if (nframes <= 0) + { + /* negative values are error codes */ + TprintfT (0, "jprofile: __collector_ext_jstack_unwind: AsyncGetCallTrace returned error: jtrace.numframes = %d\n", nframes); + nframes = 1; + JVMPI_CallFrame *err = (JVMPI_CallFrame*) ptr; + err->lineno = jtrace.num_frames; // bci = error code + err->method_id = 0; // artificial method id + } + jinfo->hsize += nframes * sizeof (JVMPI_CallFrame); + return jinfo->hsize; +} + +/* + * Collector Java API implementation + */ +void +Java_com_sun_forte_st_collector_CollectorAPI__1sample(JNIEnv *jEnv, jclass jCls, jstring jName) +{ + JNIEnv *jni; + jint res = (*jvm)->GetEnv (jvm, (void **) &jni, JNI_VERSION_1_2); + if (res < 0) + return; + const char *name = jName ? (*jni)->GetStringUTFChars (jni, jName, NULL) : NULL; + __collector_sample ((char*) name); +} + +void +Java_com_sun_forte_st_collector_CollectorAPI__1pause(JNIEnv *jEnv, jclass jCls) +{ + __collector_pause_m ("JAPI"); +} + +void +Java_com_sun_forte_st_collector_CollectorAPI__1resume(JNIEnv *jEnv, jclass jCls) +{ + __collector_resume (); +} + +void +Java_com_sun_forte_st_collector_CollectorAPI__1terminate(JNIEnv *jEnv, jclass jCls) +{ + __collector_terminate_expt (); +} +#endif /* GPROFNG_JAVA_PROFILING */ + +static void init_module () __attribute__ ((constructor)); +static void +init_module () +{ +#if defined(GPROFNG_JAVA_PROFILING) + __collector_dlsym_guard = 1; + RegModuleFunc reg_module = (RegModuleFunc) dlsym (RTLD_DEFAULT, "__collector_register_module"); + __collector_dlsym_guard = 0; + if (reg_module) + { + jprof_hndl = reg_module (&module_interface); + TprintfT (0, "jprofile: init_module.\n"); + } +#endif /* GPROFNG_JAVA_PROFILING */ +} + +int __collector_java_mode = 0; +int __collector_java_asyncgetcalltrace_loaded = 0; |