1 /* Copyright (C) 2021-2024 Free Software Foundation, Inc. 2 Contributed by Oracle. 3 4 This file is part of GNU Binutils. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3, or (at your option) 9 any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, 51 Franklin Street - Fifth Floor, Boston, 19 MA 02110-1301, USA. */ 20 21 #include "config.h" 22 23 #if defined(GPROFNG_JAVA_PROFILING) 24 #include <alloca.h> 25 #include <dlfcn.h> /* dlsym() */ 26 #include <sys/stat.h> 27 #include <fcntl.h> 28 #include <unistd.h> 29 #include <errno.h> 30 #include <sys/param.h> /* MAXPATHLEN */ 31 #include <stddef.h> 32 #include <jni.h> 33 #include <jvmti.h> 34 35 #include "gp-defs.h" 36 #include "collector.h" 37 #include "gp-experiment.h" 38 #include "tsd.h" 39 40 /* TprintfT(<level>,...) definitions. Adjust per module as needed */ 41 #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings 42 #define DBG_LT1 1 // for configuration details, warnings 43 #define DBG_LT2 2 44 #define DBG_LT3 3 45 46 /* ARCH_STRLEN is defined in dbe, copied here */ 47 #define ARCH_STRLEN(s) ((CALL_UTIL(strlen)(s) + 4 ) & ~0x3) 48 49 /* call frame */ 50 typedef struct 51 { 52 jint lineno; /* line number in the source file */ 53 jmethodID method_id; /* method executed in this frame */ 54 } JVMPI_CallFrame; 55 56 /* call trace */ 57 typedef struct 58 { 59 JNIEnv *env_id; /* Env where trace was recorded */ 60 jint num_frames; /* number of frames in this trace */ 61 JVMPI_CallFrame *frames; /* frames */ 62 } JVMPI_CallTrace; 63 64 extern void __collector_jprofile_enable_synctrace (void); 65 int __collector_jprofile_start_attach (void); 66 static int init_interface (CollectorInterface*); 67 static int open_experiment (const char *); 68 static int close_experiment (void); 69 static int detach_experiment (void); 70 static void jprof_find_asyncgetcalltrace (void); 71 static char *apistr = NULL; 72 73 static ModuleInterface module_interface = { 74 "*"SP_JCLASSES_FILE, /* description, exempt from limit */ 75 init_interface, /* initInterface */ 76 open_experiment, /* openExperiment */ 77 NULL, /* startDataCollection */ 78 NULL, /* stopDataCollection */ 79 close_experiment, /* closeExperiment */ 80 detach_experiment /* detachExperiment (fork child) */ 81 }; 82 83 static CollectorInterface *collector_interface = NULL; 84 static CollectorModule jprof_hndl = COLLECTOR_MODULE_ERR; 85 static int __collector_java_attach = 0; 86 static JavaVM *jvm; 87 static jmethodID getResource = NULL; 88 static jmethodID toExternalForm = NULL; 89 90 /* Java profiling thread specific data */ 91 typedef struct TSD_Entry 92 { 93 JNIEnv *env; 94 hrtime_t tstamp; 95 } TSD_Entry; 96 97 static unsigned tsd_key = COLLECTOR_TSD_INVALID_KEY; 98 static collector_mutex_t jclasses_lock = COLLECTOR_MUTEX_INITIALIZER; 99 static int java_gc_on = 0; 100 static int java_mem_mode = 0; 101 static int java_sync_mode = 0; 102 static int is_hotspot_vm = 0; 103 static void get_jvm_settings (); 104 static void rwrite (int fd, const void *buf, size_t nbyte); 105 static void addToDynamicArchive (const char* name, const unsigned char* class_data, int class_data_len); 106 static void (*AsyncGetCallTrace)(JVMPI_CallTrace*, jint, ucontext_t*) = NULL; 107 static void (*collector_heap_record)(int, int, void*) = NULL; 108 static void (*collector_jsync_begin)() = NULL; 109 static void (*collector_jsync_end)(hrtime_t, void *) = NULL; 110 111 #define gethrtime collector_interface->getHiResTime 112 113 /* 114 * JVMTI declarations 115 */ 116 117 static jvmtiEnv *jvmti; 118 static void jvmti_VMInit (jvmtiEnv*, JNIEnv*, jthread); 119 static void jvmti_VMDeath (jvmtiEnv*, JNIEnv*); 120 static void jvmti_ThreadStart (jvmtiEnv*, JNIEnv*, jthread); 121 static void jvmti_ThreadEnd (jvmtiEnv*, JNIEnv*, jthread); 122 static void jvmti_CompiledMethodLoad (jvmtiEnv*, jmethodID, jint, const void*, 123 jint, const jvmtiAddrLocationMap*, const void*); 124 static void jvmti_CompiledMethodUnload (jvmtiEnv*, jmethodID, const void*); 125 static void jvmti_DynamicCodeGenerated (jvmtiEnv*, const char*, const void*, jint); 126 static void jvmti_ClassPrepare (jvmtiEnv*, JNIEnv*, jthread, jclass); 127 static void jvmti_ClassLoad (jvmtiEnv*, JNIEnv*, jthread, jclass); 128 //static void jvmti_ClassUnload( jvmtiEnv*, JNIEnv*, jthread, jclass ); 129 static void jvmti_MonitorEnter (jvmtiEnv *, JNIEnv*, jthread, jobject); 130 static void jvmti_MonitorEntered (jvmtiEnv *, JNIEnv*, jthread, jobject); 131 #if 0 132 static void jvmti_MonitorWait (jvmtiEnv *, JNIEnv*, jthread, jobject, jlong); 133 static void jvmti_MonitorWaited (jvmtiEnv *, JNIEnv*, jthread, jobject, jboolean); 134 #endif 135 static void jvmti_ClassFileLoadHook (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jclass class_being_redefined, 136 jobject loader, const char* name, jobject protection_domain, 137 jint class_data_len, const unsigned char* class_data, 138 jint* new_class_data_len, unsigned char** new_class_data); 139 static void jvmti_GarbageCollectionStart (jvmtiEnv *); 140 static void 141 jvmti_GarbageCollectionFinish (jvmtiEnv *); 142 jvmtiEventCallbacks callbacks = { 143 jvmti_VMInit, // 50 jvmtiEventVMInit; 144 jvmti_VMDeath, // 51 jvmtiEventVMDeath; 145 jvmti_ThreadStart, // 52 jvmtiEventThreadStart; 146 jvmti_ThreadEnd, // 53 jvmtiEventThreadEnd; 147 jvmti_ClassFileLoadHook, // 54 jvmtiEventClassFileLoadHook; 148 jvmti_ClassLoad, // 55 jvmtiEventClassLoad; 149 jvmti_ClassPrepare, // 56 jvmtiEventClassPrepare; 150 NULL, // 57 reserved57; 151 NULL, // 58 jvmtiEventException; 152 NULL, // 59 jvmtiEventExceptionCatch; 153 NULL, // 60 jvmtiEventSingleStep; 154 NULL, // 61 jvmtiEventFramePop; 155 NULL, // 62 jvmtiEventBreakpoint; 156 NULL, // 63 jvmtiEventFieldAccess; 157 NULL, // 64 jvmtiEventFieldModification; 158 NULL, // 65 jvmtiEventMethodEntry; 159 NULL, // 66 jvmtiEventMethodExit; 160 NULL, // 67 jvmtiEventNativeMethodBind; 161 jvmti_CompiledMethodLoad, // 68 jvmtiEventCompiledMethodLoad; 162 jvmti_CompiledMethodUnload, // 69 jvmtiEventCompiledMethodUnload; 163 jvmti_DynamicCodeGenerated, // 70 jvmtiEventDynamicCodeGenerated; 164 NULL, // 71 jvmtiEventDataDumpRequest; 165 NULL, // 72 jvmtiEventDataResetRequest; 166 NULL, /*jvmti_MonitorWait,*/ // 73 jvmtiEventMonitorWait; 167 NULL, /*jvmti_MonitorWaited,*/ // 74 jvmtiEventMonitorWaited; 168 jvmti_MonitorEnter, // 75 jvmtiEventMonitorContendedEnter; 169 jvmti_MonitorEntered, // 76 jvmtiEventMonitorContendedEntered; 170 NULL, // 77 jvmtiEventMonitorContendedExit; 171 NULL, // 78 jvmtiEventReserved; 172 NULL, // 79 jvmtiEventReserved; 173 NULL, // 80 jvmtiEventReserved; 174 jvmti_GarbageCollectionStart, // 81 jvmtiEventGarbageCollectionStart; 175 jvmti_GarbageCollectionFinish, // 82 jvmtiEventGarbageCollectionFinish; 176 NULL, // 83 jvmtiEventObjectFree; 177 NULL // 84 jvmtiEventVMObjectAlloc; 178 }; 179 180 typedef jint (JNICALL JNI_GetCreatedJavaVMs_t)(JavaVM **, jsize, jsize *); 181 182 int 183 init_interface (CollectorInterface *_collector_interface) 184 { 185 collector_interface = _collector_interface; 186 return COL_ERROR_NONE; 187 } 188 189 static int 190 open_experiment (const char *exp) 191 { 192 if (collector_interface == NULL) 193 return COL_ERROR_JAVAINIT; 194 TprintfT (0, "jprofile: open_experiment %s\n", exp); 195 const char *params = collector_interface->getParams (); 196 const char *args = params; 197 while (args) 198 { 199 if (__collector_strStartWith (args, "j:") == 0) 200 { 201 args += 2; 202 break; 203 } 204 args = CALL_UTIL (strchr)(args, ';'); 205 if (args) 206 args++; 207 } 208 if (args == NULL) /* Java profiling not specified */ 209 return COL_ERROR_JAVAINIT; 210 tsd_key = collector_interface->createKey (sizeof ( TSD_Entry), NULL, NULL); 211 if (tsd_key == (unsigned) - 1) 212 { 213 TprintfT (0, "jprofile: TSD key create failed.\n"); 214 collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n", 215 SP_JCMD_CERROR, COL_ERROR_JAVAINIT); 216 return COL_ERROR_JAVAINIT; 217 } 218 else 219 Tprintf (DBG_LT2, "jprofile: TSD key create succeeded %d.\n", tsd_key); 220 221 args = params; 222 while (args) 223 { 224 if (__collector_strStartWith (args, "H:") == 0) 225 { 226 java_mem_mode = 1; 227 collector_heap_record = (void(*)(int, int, void*))dlsym (RTLD_DEFAULT, "__collector_heap_record"); 228 } 229 #if 0 230 else if (__collector_strStartWith (args, "s:") == 0) 231 { 232 java_sync_mode = 1; 233 collector_jsync_begin = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_begin"); 234 collector_jsync_end = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_end"); 235 } 236 #endif 237 args = CALL_UTIL (strchr)(args, ';'); 238 if (args) 239 args++; 240 } 241 242 /* synchronization tracing is enabled by the synctrace module, later in initialization */ 243 __collector_java_mode = 1; 244 java_gc_on = 1; 245 return COL_ERROR_NONE; 246 } 247 248 /* routine called from the syntrace module to enable Java-API synctrace */ 249 void 250 __collector_jprofile_enable_synctrace () 251 { 252 if (__collector_java_mode == 0) 253 { 254 TprintfT (DBG_LT1, "jprofile: not turning on Java synctrace; Java mode not enabled\n"); 255 return; 256 } 257 java_sync_mode = 1; 258 collector_jsync_begin = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_begin"); 259 collector_jsync_end = (void(*)(hrtime_t, void *))dlsym (RTLD_DEFAULT, "__collector_jsync_end"); 260 TprintfT (DBG_LT1, "jprofile: turning on Java synctrace, and requesting events\n"); 261 } 262 263 int 264 __collector_jprofile_start_attach (void) 265 { 266 if (!__collector_java_mode || __collector_java_asyncgetcalltrace_loaded) 267 return 0; 268 void *g_sHandle = RTLD_DEFAULT; 269 /* Now get the function addresses */ 270 JNI_GetCreatedJavaVMs_t *pfnGetCreatedJavaVMs; 271 pfnGetCreatedJavaVMs = (JNI_GetCreatedJavaVMs_t *) dlsym (g_sHandle, "JNI_GetCreatedJavaVMs"); 272 if (pfnGetCreatedJavaVMs != NULL) 273 { 274 TprintfT (0, "jprofile attach: pfnGetCreatedJavaVMs is detected.\n"); 275 JavaVM * vmBuf[1]; // XXXX only detect on jvm 276 jsize nVMs = 0; 277 (*pfnGetCreatedJavaVMs)(vmBuf, 1, &nVMs); 278 if (vmBuf[0] != NULL && nVMs > 0) 279 { 280 jvm = vmBuf[0]; 281 JNIEnv* jni_env = NULL; 282 (*jvm)->AttachCurrentThread (jvm, (void **) &jni_env, NULL); 283 Agent_OnLoad (jvm, NULL, NULL); 284 if ((*jvm)->GetEnv (jvm, (void **) &jni_env, JNI_VERSION_1_2) >= 0 && jni_env && jvmti) 285 { 286 jthread thread; 287 (*jvmti)->GetCurrentThread (jvmti, &thread); 288 jvmti_VMInit (jvmti, jni_env, thread); 289 (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_COMPILED_METHOD_LOAD); 290 (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_DYNAMIC_CODE_GENERATED); 291 __collector_java_attach = 1; 292 (*jvm)->DetachCurrentThread (jvm); 293 } 294 } 295 } 296 return 0; 297 } 298 299 static int 300 close_experiment (void) 301 { 302 /* fixme XXXXX add content here */ 303 /* see detach_experiment() */ 304 __collector_java_mode = 0; 305 __collector_java_asyncgetcalltrace_loaded = 0; 306 __collector_java_attach = 0; 307 java_gc_on = 0; 308 java_mem_mode = 0; 309 java_sync_mode = 0; 310 is_hotspot_vm = 0; 311 __collector_mutex_init (&jclasses_lock); 312 tsd_key = COLLECTOR_TSD_INVALID_KEY; 313 TprintfT (0, "jprofile: experiment closed.\n"); 314 return 0; 315 } 316 317 static int 318 detach_experiment (void) 319 /* fork child. Clean up state but don't write to experiment */ 320 { 321 __collector_java_mode = 0; 322 java_gc_on = 0; 323 jvm = NULL; 324 java_mem_mode = 0; 325 java_sync_mode = 0; 326 is_hotspot_vm = 0; 327 jvmti = NULL; 328 apistr = NULL; 329 __collector_mutex_init (&jclasses_lock); 330 tsd_key = COLLECTOR_TSD_INVALID_KEY; 331 TprintfT (0, "jprofile: detached from experiment.\n"); 332 return 0; 333 } 334 335 JNIEXPORT jint JNICALL 336 JVM_OnLoad (JavaVM *vm, char *options, void *reserved) 337 { 338 jvmtiError err; 339 int use_jvmti = 0; 340 if (!__collector_java_mode) 341 { 342 TprintfT (DBG_LT1, "jprofile: JVM_OnLoad invoked with java mode disabled\n"); 343 return JNI_OK; 344 } 345 else 346 TprintfT (DBG_LT1, "jprofile: JVM_OnLoad invoked\n"); 347 jvm = vm; 348 jvmti = NULL; 349 if ((*jvm)->GetEnv (jvm, (void **) &jvmti, JVMTI_VERSION_1_0) >= 0 && jvmti) 350 { 351 TprintfT (DBG_LT1, "jprofile: JVMTI found\n"); 352 use_jvmti = 1; 353 } 354 if (!use_jvmti) 355 { 356 collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\"/>\n", 357 SP_JCMD_CERROR, COL_ERROR_JVMNOTSUPP); 358 return JNI_ERR; 359 } 360 else 361 { 362 Tprintf (DBG_LT0, "\tjprofile: Initializing for JVMTI\n"); 363 apistr = "JVMTI 1.0"; 364 365 // setup JVMTI 366 jvmtiCapabilities cpblts; 367 err = (*jvmti)->GetPotentialCapabilities (jvmti, &cpblts); 368 if (err == JVMTI_ERROR_NONE) 369 { 370 jvmtiCapabilities cpblts_set; 371 CALL_UTIL (memset)(&cpblts_set, 0, sizeof (cpblts_set)); 372 373 /* Add only those capabilities that are among potential ones */ 374 cpblts_set.can_get_source_file_name = cpblts.can_get_source_file_name; 375 Tprintf (DBG_LT1, "\tjprofile: adding can_get_source_file_name capability: %u\n", cpblts.can_get_source_file_name); 376 377 cpblts_set.can_generate_compiled_method_load_events = cpblts.can_generate_compiled_method_load_events; 378 Tprintf (DBG_LT1, "\tjprofile: adding can_generate_compiled_method_load_events capability: %u\n", cpblts.can_generate_compiled_method_load_events); 379 380 if (java_sync_mode) 381 { 382 cpblts_set.can_generate_monitor_events = cpblts.can_generate_monitor_events; 383 Tprintf (DBG_LT1, "\tjprofile: adding can_generate_monitor_events capability: %u\n", cpblts.can_generate_monitor_events); 384 } 385 if (java_gc_on) 386 { 387 cpblts_set.can_generate_garbage_collection_events = cpblts.can_generate_garbage_collection_events; 388 Tprintf (DBG_LT1, "\tjprofile: adding can_generate_garbage_collection_events capability: %u\n", cpblts.can_generate_garbage_collection_events); 389 } 390 err = (*jvmti)->AddCapabilities (jvmti, &cpblts_set); 391 Tprintf (DBG_LT1, "\tjprofile: AddCapabilities() returns: %d\n", err); 392 } 393 err = (*jvmti)->SetEventCallbacks (jvmti, &callbacks, sizeof ( callbacks)); 394 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL); 395 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL); 396 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL); 397 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL); 398 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); 399 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL); 400 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL); 401 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, NULL); 402 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_END, NULL); 403 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); 404 if (java_gc_on) 405 { 406 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_START, NULL); 407 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL); 408 } 409 if (java_mem_mode) 410 { 411 // err = (*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, <no event for heap tracing> , NULL ); 412 collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\"/>\n", 413 SP_JCMD_CWARN, COL_WARN_NO_JAVA_HEAP); 414 java_mem_mode = 0; 415 } 416 if (java_sync_mode) 417 { 418 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, NULL); 419 err = (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, NULL); 420 //err = (*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_WAIT, NULL ); 421 //err = (*jvmti)->SetEventNotificationMode( jvmti, JVMTI_ENABLE, JVMTI_EVENT_MONITOR_WAITED, NULL ); 422 } 423 Tprintf (DBG_LT0, "\tjprofile: JVMTI initialized\n"); 424 } 425 426 /* JVM still uses collector API on Solaris to notify us about dynamically generated code. 427 * If we ask it to generate events we'll end up with duplicate entries in the 428 * map file. 429 */ 430 if (use_jvmti) 431 { 432 err = (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_DYNAMIC_CODE_GENERATED); 433 err = (*jvmti)->GenerateEvents (jvmti, JVMTI_EVENT_COMPILED_METHOD_LOAD); 434 } 435 Tprintf (DBG_LT1, "\tjprofile: JVM_OnLoad ok\n"); 436 return JNI_OK; 437 } 438 439 /* This is currently just a placeholder */ 440 JNIEXPORT jint JNICALL 441 Agent_OnLoad (JavaVM *vm, char *options, void *reserved) 442 { 443 return JVM_OnLoad (vm, options, reserved); 444 } 445 446 static void 447 rwrite (int fd, const void *buf, size_t nbyte) 448 { 449 size_t left = nbyte; 450 size_t res; 451 char *ptr = (char*) buf; 452 while (left > 0) 453 { 454 res = CALL_UTIL (write)(fd, ptr, left); 455 if (res == -1) 456 { 457 /* XXX: we can't write this record, we probably 458 * can't write anything else. Ignore. 459 */ 460 return; 461 } 462 left -= res; 463 ptr += res; 464 } 465 } 466 467 void 468 get_jvm_settings () 469 { 470 jint res; 471 JNIEnv *jni; 472 jclass jcls; 473 jmethodID jmid; 474 jstring jstrin; 475 jstring jstrout; 476 const char *str; 477 res = (*jvm)->GetEnv (jvm, (void **) &jni, JNI_VERSION_1_2); 478 if (res < 0) 479 return; 480 481 /* I'm not checking if results are valid as JVM is extremely 482 * sensitive to exceptions that might occur during these JNI calls 483 * and will die with a fatal error later anyway. 484 */ 485 jcls = (*jni)->FindClass (jni, "java/lang/System"); 486 jmid = (*jni)->GetStaticMethodID (jni, jcls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"); 487 jstrin = (*jni)->NewStringUTF (jni, "java.class.path"); 488 jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); 489 str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; 490 if (str) 491 { 492 collector_interface->writeLog ("<setting %s=\"%s\"/>\n", SP_JCMD_SRCHPATH, str); 493 (*jni)->ReleaseStringUTFChars (jni, jstrout, str); 494 } 495 jstrin = (*jni)->NewStringUTF (jni, "sun.boot.class.path"); 496 jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); 497 str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; 498 if (str) 499 { 500 collector_interface->writeLog ("<setting %s=\"%s\"/>\n", SP_JCMD_SRCHPATH, str); 501 (*jni)->ReleaseStringUTFChars (jni, jstrout, str); 502 } 503 jstrin = (*jni)->NewStringUTF (jni, "java.home"); 504 jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); 505 str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; 506 if (str) 507 { 508 collector_interface->writeLog ("<setting %s=\"%s/../src.zip\"/>\n", SP_JCMD_SRCHPATH, str); 509 (*jni)->ReleaseStringUTFChars (jni, jstrout, str); 510 } 511 jstrin = (*jni)->NewStringUTF (jni, "java.vm.version"); 512 jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); 513 str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; 514 if (str) 515 { 516 (void) collector_interface->writeLog ("<profile name=\"jprofile\" %s=\"%s\" %s=\"%s\"/>\n", 517 SP_JCMD_JVERSION, str, "api", apistr != NULL ? apistr : "N/A"); 518 if (__collector_strStartWith (str, "1.4.2_02") < 0) 519 { 520 (void) collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\"/>\n", 521 SP_JCMD_CWARN, COL_WARN_OLDJAVA); 522 } 523 (*jni)->ReleaseStringUTFChars (jni, jstrout, str); 524 } 525 is_hotspot_vm = 0; 526 jstrin = (*jni)->NewStringUTF (jni, "sun.management.compiler"); 527 jstrout = (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin); 528 str = jstrout ? (*jni)->GetStringUTFChars (jni, jstrout, NULL) : NULL; 529 if (str && __collector_strncmp (str, "HotSpot", 7) == 0) 530 is_hotspot_vm = 1; 531 532 /* Emulate System.setProperty( "collector.init", "true") */ 533 jmid = (*jni)->GetStaticMethodID (jni, jcls, "setProperty", 534 "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); 535 jstrin = (*jni)->NewStringUTF (jni, "collector.init"); 536 jstrout = (*jni)->NewStringUTF (jni, "true"); 537 (*jni)->CallStaticObjectMethod (jni, jcls, jmid, jstrin, jstrout); 538 } 539 540 /* 541 * JVMTI code 542 */ 543 544 static void 545 jvmti_VMInit (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) 546 { 547 jint class_count = 0; 548 jclass *classes = NULL; 549 int i; 550 TprintfT (DBG_LT1, "jprofile: jvmti_VMInit called\n"); 551 get_jvm_settings (); 552 553 /* determine loaded classes */ 554 (*jvmti_env)->GetLoadedClasses (jvmti_env, &class_count, &classes); 555 TprintfT (DBG_LT1, "jprofile: jvmti_VMInit initializing %d classes\n", class_count); 556 for (i = 0; i < class_count; i++) 557 { 558 // PushLocalFrame 559 jvmti_ClassPrepare (jvmti_env, jni_env, NULL, classes[i]); 560 // PopLocalFrame 561 // DeleteLocalRef( classes[i] ); 562 } 563 (*jvmti_env)->Deallocate (jvmti_env, (unsigned char*) classes); 564 getResource = (*jni_env)->GetMethodID (jni_env, (*jni_env)->FindClass (jni_env, "java/lang/ClassLoader"), "getResource", "(Ljava/lang/String;)Ljava/net/URL;"); 565 toExternalForm = (*jni_env)->GetMethodID (jni_env, (*jni_env)->FindClass (jni_env, "java/net/URL"), "toExternalForm", "()Ljava/lang/String;"); 566 567 /* find the stack unwind routine */ 568 jprof_find_asyncgetcalltrace (); 569 } 570 571 static void 572 jvmti_VMDeath (jvmtiEnv *jvmti_env, JNIEnv* jni_env) 573 { 574 __collector_java_mode = 0; 575 TprintfT (DBG_LT1, "jprofile: jvmti_VMDeath event received\n"); 576 } 577 578 static void 579 jvmti_ThreadStart (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) 580 { 581 jvmtiError err; 582 jvmtiThreadInfo t_info; 583 char *thread_name, *group_name, *parent_name; 584 hrtime_t hrt; 585 collector_thread_t tid; 586 thread_name = group_name = parent_name = NULL; 587 hrt = gethrtime (); 588 tid = __collector_thr_self (); 589 TprintfT (DBG_LT1, "jprofile: jvmti_ThreadStart: thread: %lu jni_env=%p jthread=%p\n", 590 (unsigned long) tid, jni_env, thread); 591 err = (*jvmti_env)->GetThreadInfo (jvmti_env, thread, &t_info); 592 if (err == JVMTI_ERROR_NONE) 593 { 594 jvmtiThreadGroupInfo g_info; 595 thread_name = t_info.name; 596 if (t_info.thread_group) 597 { 598 err = (*jvmti_env)->GetThreadGroupInfo (jvmti_env, t_info.thread_group, &g_info); 599 if (err == JVMTI_ERROR_NONE) 600 { 601 group_name = g_info.name; 602 if (g_info.parent) 603 { 604 jvmtiThreadGroupInfo p_info; 605 err = (*jvmti_env)->GetThreadGroupInfo (jvmti_env, g_info.parent, &p_info); 606 if (err == JVMTI_ERROR_NONE) 607 { 608 parent_name = p_info.name; 609 // DeleteLocalRef( p_info.parent ); 610 } 611 // DeleteLocalRef( g_info.parent ); 612 } 613 } 614 } 615 // DeleteLocalRef( t_info.thread_group ); 616 // DeleteLocalRef( t_info.context_class_loader ); 617 } 618 if (thread_name == NULL) 619 thread_name = ""; 620 if (group_name == NULL) 621 group_name = ""; 622 if (parent_name == NULL) 623 parent_name = ""; 624 collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\" name=\"%s\" grpname=\"%s\" prntname=\"%s\" tid=\"%lu\" jthr=\"0x%lx\" jenv=\"0x%lx\"/>\n", 625 SP_JCMD_JTHRSTART, 626 (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), 627 thread_name, 628 group_name, 629 parent_name, 630 (unsigned long) tid, 631 (unsigned long) thread, 632 (unsigned long) jni_env 633 ); 634 TSD_Entry *tsd = collector_interface->getKey (tsd_key); 635 if (tsd) 636 tsd->env = jni_env; 637 } 638 639 static void 640 jvmti_ThreadEnd (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) 641 { 642 hrtime_t hrt = gethrtime (); 643 collector_thread_t tid = __collector_thr_self (); 644 TprintfT (DBG_LT1, "jprofile: jvmti_ThreadEnd: thread: %lu jni_env=%p jthread=%p\n", 645 (unsigned long) tid, jni_env, thread); 646 647 collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\" tid=\"%lu\" jthr=\"0x%lx\" jenv=\"0x%lx\"/>\n", 648 SP_JCMD_JTHREND, 649 (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC), 650 (unsigned long) tid, 651 (unsigned long) thread, 652 (unsigned long) jni_env 653 ); 654 TSD_Entry *tsd = collector_interface->getKey (tsd_key); 655 if (tsd) 656 tsd->env = NULL; 657 } 658 659 /* The following definitions are borrowed from file jvmticmlr.h, part of jdk7 */ 660 typedef enum 661 { 662 JVMTI_CMLR_DUMMY = 1, 663 JVMTI_CMLR_INLINE_INFO = 2 664 } jvmtiCMLRKind; 665 666 /* 667 * Record that represents arbitrary information passed through JVMTI 668 * CompiledMethodLoadEvent void pointer. 669 */ 670 typedef struct _jvmtiCompiledMethodLoadRecordHeader 671 { 672 jvmtiCMLRKind kind; /* id for the kind of info passed in the record */ 673 jint majorinfoversion; /* major and minor info version values. Init'ed */ 674 jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */ 675 struct _jvmtiCompiledMethodLoadRecordHeader* next; 676 } jvmtiCompiledMethodLoadRecordHeader; 677 678 /* 679 * Record that gives information about the methods on the compile-time 680 * stack at a specific pc address of a compiled method. Each element in 681 * the methods array maps to same element in the bcis array. 682 */ 683 typedef struct _PCStackInfo 684 { 685 void* pc; /* the pc address for this compiled method */ 686 jint numstackframes; /* number of methods on the stack */ 687 jmethodID* methods; /* array of numstackframes method ids */ 688 jint* bcis; /* array of numstackframes bytecode indices */ 689 } PCStackInfo; 690 691 /* 692 * Record that contains inlining information for each pc address of 693 * an nmethod. 694 */ 695 typedef struct _jvmtiCompiledMethodLoadInlineRecord 696 { 697 jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */ 698 jint numpcs; /* number of pc descriptors in this nmethod */ 699 PCStackInfo* pcinfo; /* array of numpcs pc descriptors */ 700 } jvmtiCompiledMethodLoadInlineRecord; 701 702 static void 703 jvmti_CompiledMethodLoad (jvmtiEnv *jvmti_env, jmethodID method, 704 jint code_size, const void *code_addr, jint map_length, 705 const jvmtiAddrLocationMap *map, 706 const void *compile_info) 707 { 708 TprintfT (DBG_LT2, "jprofile: jvmti_CompiledMethodLoad: mid=0x%lx addr=%p sz=0x%lu map=%p info=%p\n", 709 (unsigned long) method, code_addr, (long) code_size, map, compile_info); 710 char name[32]; 711 CALL_UTIL (snprintf)(name, sizeof (name), "0x%lx", (unsigned long) method); 712 713 /* Parse compile_info to get pc -> bci mapping. 714 * Don't interpret compile_info from JVMs other than HotSpot. 715 */ 716 int lntsize = 0; 717 DT_lineno *lntable = NULL; 718 if (compile_info != NULL && is_hotspot_vm) 719 { 720 Tprintf (DBG_LT2, "Mapping from compile_info:\n"); 721 jvmtiCompiledMethodLoadRecordHeader *currec = 722 (jvmtiCompiledMethodLoadRecordHeader*) compile_info; 723 while (currec != NULL) 724 { 725 if (currec->kind == JVMTI_CMLR_INLINE_INFO) 726 { 727 jvmtiCompiledMethodLoadInlineRecord *inrec = 728 (jvmtiCompiledMethodLoadInlineRecord*) currec; 729 if (inrec->numpcs <= 0) 730 break; 731 lntsize = inrec->numpcs; 732 lntable = (DT_lineno*) alloca (lntsize * sizeof (DT_lineno)); 733 PCStackInfo *pcrec = inrec->pcinfo; 734 DT_lineno *lnorec = lntable; 735 for (int i = 0; i < lntsize; ++i) 736 { 737 for (int j = pcrec->numstackframes - 1; j >= 0; --j) 738 if (pcrec->methods[j] == method) 739 { 740 lnorec->offset = (char*) pcrec->pc - (char*) code_addr; 741 lnorec->lineno = pcrec->bcis[j]; 742 Tprintf (DBG_LT2, " pc: 0x%lx bci: 0x%lx\n", 743 (long) lnorec->offset, (long) lnorec->lineno); 744 ++lnorec; 745 break; 746 } 747 ++pcrec; 748 } 749 break; 750 } 751 currec = currec->next; 752 } 753 } 754 else if (map != NULL) 755 { 756 Tprintf (DBG_LT2, "Mapping from jvmtiAddrLocationMap:\n"); 757 lntsize = map_length; 758 lntable = (DT_lineno*) alloca (lntsize * sizeof (DT_lineno)); 759 DT_lineno *lnorec = lntable; 760 for (int i = 0; i < map_length; ++i) 761 { 762 lnorec->offset = (char*) map[i].start_address - (char*) code_addr; 763 lnorec->lineno = (unsigned int) map[i].location; 764 Tprintf (DBG_LT2, " pc: 0x%lx bci: 0x%lx\n", 765 (long) lnorec->offset, (long) lnorec->lineno); 766 ++lnorec; 767 } 768 } 769 __collector_int_func_load (DFUNC_JAVA, name, NULL, (void*) code_addr, 770 code_size, lntsize, lntable); 771 } 772 773 static void 774 jvmti_CompiledMethodUnload (jvmtiEnv *jvmti_env, jmethodID method, const void* code_addr) 775 { 776 __collector_int_func_unload (DFUNC_API, (void*) code_addr); 777 } 778 779 static void 780 jvmti_DynamicCodeGenerated (jvmtiEnv *jvmti_env, const char*name, const void *code_addr, jint code_size) 781 { 782 __collector_int_func_load (DFUNC_API, (char*) name, NULL, (void*) code_addr, 783 code_size, 0, NULL); 784 } 785 786 static void 787 addToDynamicArchive (const char* name, const unsigned char* class_data, int class_data_len) 788 { 789 char path[MAXPATHLEN + 1]; 790 mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 791 mode_t dmode = fmode | S_IXUSR | S_IXGRP | S_IXOTH; 792 if (name == NULL) 793 name = ""; 794 const char *expdir = collector_interface->getExpDir (); 795 if (CALL_UTIL (strlen)(expdir) + 796 CALL_UTIL (strlen)(SP_DYNAMIC_CLASSES) + 797 CALL_UTIL (strlen)(name) + 8 > sizeof (path)) 798 return; 799 CALL_UTIL (snprintf)(path, sizeof (path), "%s/%s/%s.class", expdir, SP_DYNAMIC_CLASSES, name); 800 801 /* Create all path components step by step starting with SP_DYNAMIC_CLASSES */ 802 char *str = path + CALL_UTIL (strlen)(expdir) + 1 + CALL_UTIL (strlen)(SP_DYNAMIC_CLASSES); 803 while (str) 804 { 805 *str = '\0'; 806 if (CALL_UTIL (mkdir)(path, dmode) != 0) 807 { 808 /* Checking for EEXIST is not enough, access() is more reliable */ 809 if (CALL_UTIL (access)(path, F_OK) != 0) 810 { 811 collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", 812 SP_JCMD_CERROR, COL_ERROR_MKDIR, errno, path); 813 return; 814 } 815 } 816 *str++ = '/'; 817 str = CALL_UTIL (strchr)(str, '/'); 818 } 819 820 int fd = CALL_UTIL (open)(path, O_WRONLY | O_CREAT | O_TRUNC, fmode); 821 if (fd < 0) 822 { 823 collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n", 824 SP_JCMD_CERROR, COL_ERROR_OVWOPEN, errno, path); 825 return; 826 } 827 rwrite (fd, class_data, class_data_len); 828 CALL_UTIL (close)(fd); 829 } 830 831 static void 832 jvmti_ClassFileLoadHook (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jclass class_being_redefined, 833 jobject loader, const char* name, jobject protection_domain, jint class_data_len, 834 const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) 835 { 836 jclass loaderlass; 837 int err; 838 jvmtiPhase phase_ptr; 839 char *cname = NULL; 840 (*jvmti_env)->GetPhase (jvmti_env, &phase_ptr); 841 842 /* skip non live phases */ 843 if (phase_ptr != JVMTI_PHASE_LIVE) 844 return; 845 846 /* skip system class loaders */ 847 if (!loader) 848 return; 849 loaderlass = (*jni_env)->GetObjectClass (jni_env, loader); 850 err = (*jvmti_env)->GetClassSignature (jvmti_env, loaderlass, &cname, NULL); 851 if (err != JVMTI_ERROR_NONE || !cname || *cname == (char) 0) 852 return; 853 854 /* skip classes loaded with AppClassLoader (java.class.path) */ 855 if (__collector_strcmp (cname, "Lsun/misc/Launcher$AppClassLoader;") == 0) 856 return; 857 addToDynamicArchive (name, class_data, (int) class_data_len); 858 } 859 860 #define NO_CLASS_NAME "<noname>" 861 #define NO_SOURCE_FILE "<Unknown>" 862 863 static void 864 record_jclass (uint64_t class_id, hrtime_t hrt, const char *cname, const char *sname) 865 { 866 size_t clen = ARCH_STRLEN (cname); 867 size_t slen = ARCH_STRLEN (sname); 868 size_t sz = sizeof (ARCH_jclass) + clen + slen; 869 ARCH_jclass *jcls = (ARCH_jclass*) alloca (sz); 870 jcls->comm.tsize = sz; 871 jcls->comm.type = ARCH_JCLASS; 872 jcls->class_id = class_id; 873 jcls->tstamp = hrt; 874 char *str = (char*) (jcls + 1); 875 size_t i = CALL_UTIL (strlcpy)(str, cname, clen); 876 str += i; 877 while (i++ < clen) 878 *str++ = (char) 0; /* pad with 0's */ 879 i = CALL_UTIL (strlcpy)(str, sname, slen); 880 str += i; 881 while (i++ < slen) 882 *str++ = (char) 0; /* pad with 0's */ 883 collector_interface->writeDataPacket (jprof_hndl, (CM_Packet*) jcls); 884 } 885 886 static void 887 record_jmethod (uint64_t class_id, uint64_t method_id, 888 const char *mname, const char *msign) 889 { 890 size_t mnlen = mname ? ARCH_STRLEN (mname) : 0; 891 size_t mslen = msign ? ARCH_STRLEN (msign) : 0; 892 size_t sz = sizeof (ARCH_jmethod) + mnlen + mslen; 893 ARCH_jmethod *jmth = (ARCH_jmethod*) alloca (sz); 894 if (jmth == NULL) 895 { 896 TprintfT (DBG_LT1, "jprofile: record_jmethod ERROR: failed to alloca(%ld)\n", (long) sz); 897 return; 898 } 899 jmth->comm.tsize = sz; 900 jmth->comm.type = ARCH_JMETHOD; 901 jmth->class_id = class_id; 902 jmth->method_id = method_id; 903 char *str = (char*) (jmth + 1); 904 if (mname) 905 { 906 size_t i = CALL_UTIL (strlcpy)(str, mname, mnlen); 907 str += i; 908 while (i++ < mnlen) 909 *str++ = (char) 0; /* pad with 0's */ 910 } 911 if (msign) 912 { 913 size_t i = CALL_UTIL (strlcpy)(str, msign, mslen); 914 str += i; 915 while (i++ < mslen) 916 *str++ = (char) 0; /* pad with 0's */ 917 } 918 collector_interface->writeDataPacket (jprof_hndl, (CM_Packet*) jmth); 919 } 920 921 static void 922 jvmti_ClassPrepare (jvmtiEnv *jvmti_env, JNIEnv* jni_env, 923 jthread thread, jclass klass) 924 { 925 hrtime_t hrt; 926 jint mnum; 927 jmethodID *mptr; 928 char *cname, *sname; 929 char *str1 = NULL; 930 int err = (*jvmti_env)->GetClassSignature (jvmti_env, klass, &str1, NULL); 931 if (err != JVMTI_ERROR_NONE || str1 == NULL || *str1 == (char) 0) 932 cname = NO_CLASS_NAME; 933 else 934 cname = str1; 935 if (*cname != 'L') 936 { 937 DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassPrepare: GetClassSignature failed. err=%d cname=%s\n", err, cname); 938 return; 939 } 940 char *str2 = NULL; 941 err = (*jvmti_env)->GetSourceFileName (jvmti_env, klass, &str2); 942 if (err != JVMTI_ERROR_NONE || str2 == NULL || *str2 == (char) 0) 943 sname = NO_SOURCE_FILE; 944 else 945 sname = str2; 946 DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassPrepare: cname=%s sname=%s\n", STR (cname), STR (sname)); 947 948 /* Lock the whole file */ 949 __collector_mutex_lock (&jclasses_lock); 950 hrt = gethrtime (); 951 record_jclass ((unsigned long) klass, hrt, cname, sname); 952 (*jvmti_env)->Deallocate (jvmti_env, (unsigned char *) str1); 953 (*jvmti_env)->Deallocate (jvmti_env, (unsigned char *) str2); 954 err = (*jvmti_env)->GetClassMethods (jvmti_env, klass, &mnum, &mptr); 955 if (err == JVMTI_ERROR_NONE) 956 { 957 for (int i = 0; i < mnum; i++) 958 { 959 char *mname, *msign; 960 err = (*jvmti_env)->GetMethodName (jvmti_env, mptr[i], &mname, &msign, NULL); 961 if (err != JVMTI_ERROR_NONE) 962 continue; 963 record_jmethod ((unsigned long) klass, (unsigned long) mptr[i], mname, msign); 964 // DeleteLocalRef( mptr[i] ); 965 } 966 (*jvmti_env)->Deallocate (jvmti_env, (unsigned char*) mptr); 967 } 968 /* Unlock the file */ 969 __collector_mutex_unlock (&jclasses_lock); 970 } 971 972 /* 973 * The CLASS_LOAD event is enabled to enable AsyncGetCallTrace 974 */ 975 static void 976 jvmti_ClassLoad (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jclass klass) 977 { 978 char *cname; 979 char *str1 = NULL; 980 int err = (*jvmti_env)->GetClassSignature (jvmti_env, klass, &str1, NULL); 981 if (err != JVMTI_ERROR_NONE || str1 == NULL || *str1 == (char) 0) 982 cname = NO_CLASS_NAME; 983 else 984 cname = str1; 985 jstring str = NULL; 986 const char* resourceName; 987 jobject classLoader = NULL; 988 err = (*jvmti)->GetClassLoader (jvmti, klass, &classLoader); 989 DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jprofile: jvmti_ClassLoad err=%d cname=%s\n", err, STR (cname)); 990 if (err == 0) 991 { 992 if (classLoader == NULL) 993 { 994 // bootstrap class loader 995 resourceName = ""; 996 } 997 else 998 { 999 char* name = (char *) alloca ((CALL_UTIL (strlen)(str1) + 32) * sizeof (char)); 1000 CALL_UTIL (strlcpy)(name, str1 + 1, CALL_UTIL (strlen)(str1)); 1001 name[CALL_UTIL (strlen)(name) - 1] = '\0'; // remove the last ';' 1002 char* p; 1003 for (p = name; *p != '\0'; p++) 1004 if (*p == '.') 1005 *p = '/'; 1006 CALL_UTIL (strlcat)(name, ".class", CALL_UTIL (strlen)(name) + CALL_UTIL (strlen)(".class") + 1); 1007 if (getResource == NULL || toExternalForm == NULL) 1008 { 1009 resourceName = ""; 1010 DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassLoad: class %s failed to get path with method missing\n", STR (cname)); 1011 } 1012 else 1013 { 1014 jobject url = (*jni_env)->CallObjectMethod (jni_env, classLoader, getResource, (*jni_env)->NewStringUTF (jni_env, name)); 1015 if (url == NULL) 1016 { 1017 resourceName = ""; 1018 DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassLoad: class %s failed to get path\n", STR (cname)); 1019 } 1020 else 1021 { 1022 str = (jstring) (*jni_env)->CallObjectMethod (jni_env, url, toExternalForm); 1023 resourceName = (*jni_env)->GetStringUTFChars (jni_env, str, NULL); 1024 DprintfT (SP_DUMP_JAVA | SP_DUMP_TIME, "jvmti_ClassLoad: ARCH_JCLASS_LOCATION(Ox%x) class_id=0x%lx className='%s' fileName '%s'\n", 1025 (int) ARCH_JCLASS_LOCATION, (unsigned long) klass, STR (cname), STR (resourceName)); 1026 size_t clen = ARCH_STRLEN (cname); 1027 size_t slen = ARCH_STRLEN (resourceName); 1028 size_t sz = sizeof (ARCH_jclass) + clen + slen; 1029 ARCH_jclass_location *jcls = (ARCH_jclass_location*) alloca (sz); 1030 jcls->comm.tsize = sz; 1031 jcls->comm.type = ARCH_JCLASS_LOCATION; 1032 jcls->class_id = (unsigned long) klass; 1033 char *str = (char*) (jcls + 1); 1034 size_t i = CALL_UTIL (strlcpy)(str, cname, clen); 1035 str += i; 1036 while (i++ < clen) 1037 { 1038 *str++ = (char) 0; /* pad with 0's */ 1039 } 1040 i = CALL_UTIL (strlcpy)(str, resourceName, slen); 1041 str += i; 1042 while (i++ < slen) 1043 { 1044 *str++ = (char) 0; /* pad with 0's */ 1045 } 1046 /* Lock the whole file */ 1047 __collector_mutex_lock (&jclasses_lock); 1048 collector_interface->writeDataPacket (jprof_hndl, (CM_Packet*) jcls); 1049 /* Unlock the file */ 1050 __collector_mutex_unlock (&jclasses_lock); 1051 } 1052 } 1053 } 1054 } 1055 } 1056 1057 static void 1058 jvmti_MonitorEnter (jvmtiEnv *jvmti_env, JNIEnv* jni_env, 1059 jthread thread, jobject object) 1060 { 1061 if (collector_jsync_begin) 1062 collector_jsync_begin (); 1063 TSD_Entry *tsd = collector_interface->getKey (tsd_key); 1064 if (tsd == NULL) 1065 return; 1066 tsd->tstamp = gethrtime (); 1067 } 1068 1069 static void 1070 jvmti_MonitorEntered (jvmtiEnv *jvmti_env, JNIEnv* jni_env, 1071 jthread thread, jobject object) 1072 { 1073 TSD_Entry *tsd = collector_interface->getKey (tsd_key); 1074 if (tsd == NULL) 1075 return; 1076 if (collector_jsync_end) 1077 collector_jsync_end (tsd->tstamp, object); 1078 } 1079 1080 static void 1081 jvmti_GarbageCollectionStart (jvmtiEnv *jvmti_env) 1082 { 1083 hrtime_t hrt = gethrtime (); 1084 collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\"/>\n", 1085 SP_JCMD_GCSTART, 1086 (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC) 1087 ); 1088 TprintfT (DBG_LT1, "jprofile: jvmti_GarbageCollectionStart.\n"); 1089 } 1090 1091 static void 1092 jvmti_GarbageCollectionFinish (jvmtiEnv *jvmti_env) 1093 { 1094 hrtime_t hrt = gethrtime (); 1095 collector_interface->writeLog ("<event kind=\"%s\" tstamp=\"%u.%09u\"/>\n", 1096 SP_JCMD_GCEND, 1097 (unsigned) (hrt / NANOSEC), (unsigned) (hrt % NANOSEC) 1098 ); 1099 TprintfT (DBG_LT1, "jprofile: jvmti_GarbageCollectionFinish.\n"); 1100 } 1101 1102 #if 0 1103 static void 1104 jvmti_MonitorWait (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, 1105 jobject object, jlong timed_out) 1106 { 1107 if (collector_sync_begin) 1108 collector_sync_begin (); 1109 TSD_Entry *tsd = collector_interface->getKey (tsd_key); 1110 if (tsd == NULL) 1111 return; 1112 tsd->tstamp = gethrtime (); 1113 } 1114 1115 static void 1116 jvmti_MonitorWaited (jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, 1117 jobject object, jboolean timed_out) 1118 { 1119 TSD_Entry *tsd = collector_interface->getKey (tsd_key); 1120 if (tsd == NULL) 1121 return; 1122 if (collector_sync_end) 1123 collector_sync_end (tsd->tstamp, object); 1124 } 1125 #endif 1126 1127 static void 1128 jprof_find_asyncgetcalltrace () 1129 { 1130 void *jvmhandle; 1131 if (__collector_VM_ReadByteInstruction == NULL) 1132 __collector_VM_ReadByteInstruction = (int(*)()) dlsym (RTLD_DEFAULT, "Async_VM_ReadByteInstruction"); 1133 1134 /* look for stack unwind function using default path */ 1135 AsyncGetCallTrace = (void (*)(JVMPI_CallTrace*, jint, ucontext_t*)) 1136 dlsym (RTLD_DEFAULT, "AsyncGetCallTrace"); 1137 if (AsyncGetCallTrace != NULL) 1138 { 1139 __collector_java_asyncgetcalltrace_loaded = 1; 1140 TprintfT (DBG_LT1, "jprofile: AsyncGetCallTrace found with RTLD_DEFAULT\n"); 1141 } 1142 else 1143 { 1144 /* not found there, find libjvm.so, and ask again */ 1145 jvmhandle = dlopen ("libjvm.so", RTLD_LAZY | RTLD_NOLOAD); 1146 if (jvmhandle != NULL) 1147 { 1148 AsyncGetCallTrace = (void (*)(JVMPI_CallTrace*, jint, ucontext_t*)) 1149 dlsym (jvmhandle, "AsyncGetCallTrace"); 1150 } 1151 } 1152 1153 if (AsyncGetCallTrace == NULL) 1154 { 1155 /* we could not find it -- write collector error */ 1156 TprintfT (0, "jprofile: ERROR -- AsyncGetCallTrace not found in address space\n"); 1157 char *err = dlerror (); 1158 collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">%s</event>\n", 1159 SP_JCMD_CERROR, COL_ERROR_JVMNOJSTACK, err ? err : ""); 1160 __collector_java_mode = 0; 1161 } 1162 else 1163 { 1164 __collector_java_asyncgetcalltrace_loaded = 1; 1165 TprintfT (DBG_LT1, "jprofile: AsyncGetCallTrace initialized in jprof_jvmpi_init_done_event\n"); 1166 } 1167 } 1168 1169 int 1170 __collector_ext_jstack_unwind (char *ptr, int sz, ucontext_t *uc) 1171 { 1172 if (AsyncGetCallTrace == NULL) 1173 { 1174 TprintfT (DBG_LT0, "jprofile: __collector_ext_jstack_unwind: AsyncGetCallTrace is NULL\n"); 1175 return 0; 1176 } 1177 1178 TSD_Entry *tsd = collector_interface->getKey (tsd_key); 1179 if (tsd == NULL) 1180 { 1181 TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: tsd is NULL\n"); 1182 return 0; 1183 } 1184 if (__collector_java_attach && tsd->env == NULL && jvmti != NULL && jvm != NULL) 1185 { 1186 TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: tsd->env is NULL under attach\n"); 1187 JNIEnv* jni_env = NULL; 1188 (*jvm)->GetEnv (jvm, (void **) &jni_env, JNI_VERSION_1_2); 1189 tsd->env = jni_env; 1190 } 1191 if (tsd->env == NULL) 1192 { 1193 TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: tsd->env is NULL\n"); 1194 return 0; 1195 } 1196 1197 /* skip the Java stack whenever another signal handler is present */ 1198 if (uc->uc_link) 1199 { 1200 TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: uc->uc_link is non-NULL\n"); 1201 return 0; 1202 } 1203 /* we don't expect Java frames in signal handlers, so 1204 * unroll the list of saved contexts to the topmost one 1205 */ 1206 while (uc->uc_link) 1207 uc = uc->uc_link; 1208 Java_info *jinfo = (Java_info*) ptr; 1209 jinfo->kind = JAVA_INFO; 1210 jinfo->hsize = sizeof (Java_info); 1211 ptr += sizeof (Java_info); 1212 sz -= sizeof (Java_info); 1213 1214 JVMPI_CallTrace jtrace; 1215 jtrace.env_id = tsd->env; 1216 jtrace.frames = (JVMPI_CallFrame*) ptr; 1217 1218 /* nframes is how many frames we have room for */ 1219 jint nframes = sz / sizeof (JVMPI_CallFrame); 1220 1221 #if WSIZE(64) 1222 /* bug 6909545: garbage in 64-bit JAVA_INFO */ 1223 CALL_UTIL (memset)(jtrace.frames, 0, nframes * sizeof (JVMPI_CallFrame)); 1224 #endif 1225 1226 #if ARCH(SPARC) 1227 // 21328946 JDK bug 8129933 causes <no java callstack recorded> on sparc-Linux 1228 // convert from ucontext_t to sigcontext 1229 struct sigcontext sctx; 1230 sctx.sigc_regs.tpc = uc->uc_mcontext.mc_gregs[MC_PC]; 1231 __collector_memcpy (sctx.sigc_regs.u_regs, &uc->uc_mcontext.mc_gregs[3], sizeof (sctx.sigc_regs.u_regs)); 1232 uc = (ucontext_t *) (&sctx); 1233 #endif /* SPARC */ 1234 AsyncGetCallTrace (&jtrace, nframes, uc); 1235 1236 if (jtrace.num_frames == nframes) 1237 { 1238 JVMPI_CallFrame *last = &jtrace.frames[nframes - 1]; 1239 last->method_id = (jmethodID) SP_TRUNC_STACK_MARKER; 1240 last->lineno = 0; 1241 } 1242 1243 /* nframes is how many frames we actually got */ 1244 nframes = jtrace.num_frames; 1245 TprintfT (DBG_LT3, "jprofile: __collector_ext_jstack_unwind: AsyncGetCallTrace jtrace.numframes = %d\n", nframes); 1246 if (nframes <= 0) 1247 { 1248 /* negative values are error codes */ 1249 TprintfT (0, "jprofile: __collector_ext_jstack_unwind: AsyncGetCallTrace returned error: jtrace.numframes = %d\n", nframes); 1250 nframes = 1; 1251 JVMPI_CallFrame *err = (JVMPI_CallFrame*) ptr; 1252 err->lineno = jtrace.num_frames; // bci = error code 1253 err->method_id = 0; // artificial method id 1254 } 1255 jinfo->hsize += nframes * sizeof (JVMPI_CallFrame); 1256 return jinfo->hsize; 1257 } 1258 1259 /* 1260 * Collector Java API implementation 1261 */ 1262 void 1263 Java_com_sun_forte_st_collector_CollectorAPI__1sample(JNIEnv *jEnv, jclass jCls, jstring jName) 1264 { 1265 JNIEnv *jni; 1266 jint res = (*jvm)->GetEnv (jvm, (void **) &jni, JNI_VERSION_1_2); 1267 if (res < 0) 1268 return; 1269 const char *name = jName ? (*jni)->GetStringUTFChars (jni, jName, NULL) : NULL; 1270 __collector_sample ((char*) name); 1271 } 1272 1273 void 1274 Java_com_sun_forte_st_collector_CollectorAPI__1pause(JNIEnv *jEnv, jclass jCls) 1275 { 1276 __collector_pause_m ("JAPI"); 1277 } 1278 1279 void 1280 Java_com_sun_forte_st_collector_CollectorAPI__1resume(JNIEnv *jEnv, jclass jCls) 1281 { 1282 __collector_resume (); 1283 } 1284 1285 void 1286 Java_com_sun_forte_st_collector_CollectorAPI__1terminate(JNIEnv *jEnv, jclass jCls) 1287 { 1288 __collector_terminate_expt (); 1289 } 1290 #endif /* GPROFNG_JAVA_PROFILING */ 1291 1292 static void init_module () __attribute__ ((constructor)); 1293 static void 1294 init_module () 1295 { 1296 #if defined(GPROFNG_JAVA_PROFILING) 1297 __collector_dlsym_guard = 1; 1298 RegModuleFunc reg_module = (RegModuleFunc) dlsym (RTLD_DEFAULT, "__collector_register_module"); 1299 __collector_dlsym_guard = 0; 1300 if (reg_module) 1301 { 1302 jprof_hndl = reg_module (&module_interface); 1303 TprintfT (0, "jprofile: init_module.\n"); 1304 } 1305 #endif /* GPROFNG_JAVA_PROFILING */ 1306 } 1307 1308 int __collector_java_mode = 0; 1309 int __collector_java_asyncgetcalltrace_loaded = 0; 1310