]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gprofng/libcollector/jprofile.c
Update year range in gprofng copyright notices
[thirdparty/binutils-gdb.git] / gprofng / libcollector / jprofile.c
CommitLineData
76bdc726 1/* Copyright (C) 2021-2023 Free Software Foundation, Inc.
bb368aad
VM
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
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 */
50typedef 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 */
57typedef 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
64extern void __collector_jprofile_enable_synctrace (void);
65int __collector_jprofile_start_attach (void);
66static int init_interface (CollectorInterface*);
67static int open_experiment (const char *);
68static int close_experiment (void);
69static int detach_experiment (void);
70static void jprof_find_asyncgetcalltrace (void);
71static char *apistr = NULL;
72
73static 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
83static CollectorInterface *collector_interface = NULL;
84static CollectorModule jprof_hndl = COLLECTOR_MODULE_ERR;
85static int __collector_java_attach = 0;
86static JavaVM *jvm;
87static jmethodID getResource = NULL;
88static jmethodID toExternalForm = NULL;
89
90/* Java profiling thread specific data */
91typedef struct TSD_Entry
92{
93 JNIEnv *env;
94 hrtime_t tstamp;
95} TSD_Entry;
96
97static unsigned tsd_key = COLLECTOR_TSD_INVALID_KEY;
98static collector_mutex_t jclasses_lock = COLLECTOR_MUTEX_INITIALIZER;
99static int java_gc_on = 0;
100static int java_mem_mode = 0;
101static int java_sync_mode = 0;
102static int is_hotspot_vm = 0;
103static void get_jvm_settings ();
104static void rwrite (int fd, const void *buf, size_t nbyte);
105static void addToDynamicArchive (const char* name, const unsigned char* class_data, int class_data_len);
106static void (*AsyncGetCallTrace)(JVMPI_CallTrace*, jint, ucontext_t*) = NULL;
107static void (*collector_heap_record)(int, int, void*) = NULL;
108static void (*collector_jsync_begin)() = NULL;
109static void (*collector_jsync_end)(hrtime_t, void *) = NULL;
110
111#define gethrtime collector_interface->getHiResTime
112
113/*
114 * JVMTI declarations
115 */
116
117static jvmtiEnv *jvmti;
118static void jvmti_VMInit (jvmtiEnv*, JNIEnv*, jthread);
119static void jvmti_VMDeath (jvmtiEnv*, JNIEnv*);
120static void jvmti_ThreadStart (jvmtiEnv*, JNIEnv*, jthread);
121static void jvmti_ThreadEnd (jvmtiEnv*, JNIEnv*, jthread);
122static void jvmti_CompiledMethodLoad (jvmtiEnv*, jmethodID, jint, const void*,
123 jint, const jvmtiAddrLocationMap*, const void*);
124static void jvmti_CompiledMethodUnload (jvmtiEnv*, jmethodID, const void*);
125static void jvmti_DynamicCodeGenerated (jvmtiEnv*, const char*, const void*, jint);
126static void jvmti_ClassPrepare (jvmtiEnv*, JNIEnv*, jthread, jclass);
127static void jvmti_ClassLoad (jvmtiEnv*, JNIEnv*, jthread, jclass);
128//static void jvmti_ClassUnload( jvmtiEnv*, JNIEnv*, jthread, jclass );
129static void jvmti_MonitorEnter (jvmtiEnv *, JNIEnv*, jthread, jobject);
130static void jvmti_MonitorEntered (jvmtiEnv *, JNIEnv*, jthread, jobject);
131#if 0
132static void jvmti_MonitorWait (jvmtiEnv *, JNIEnv*, jthread, jobject, jlong);
133static void jvmti_MonitorWaited (jvmtiEnv *, JNIEnv*, jthread, jobject, jboolean);
134#endif
135static 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);
139static void jvmti_GarbageCollectionStart (jvmtiEnv *);
140static void
141jvmti_GarbageCollectionFinish (jvmtiEnv *);
142jvmtiEventCallbacks 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
180typedef jint (JNICALL JNI_GetCreatedJavaVMs_t)(JavaVM **, jsize, jsize *);
181
182int
183init_interface (CollectorInterface *_collector_interface)
184{
185 collector_interface = _collector_interface;
186 return COL_ERROR_NONE;
187}
188
189static int
190open_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 */
249void
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
263int
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);
bb368aad
VM
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
299static int
300close_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
317static int
318detach_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
335JNIEXPORT jint JNICALL
336JVM_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 */
440JNIEXPORT jint JNICALL
441Agent_OnLoad (JavaVM *vm, char *options, void *reserved)
442{
443 return JVM_OnLoad (vm, options, reserved);
444}
445
446static void
447rwrite (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
467void
468get_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
544static void
545jvmti_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
571static void
572jvmti_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
578static void
579jvmti_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 thread,
632 jni_env
633 );
634 TSD_Entry *tsd = collector_interface->getKey (tsd_key);
635 if (tsd)
636 tsd->env = jni_env;
637}
638
639static void
640jvmti_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 thread,
652 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 */
660typedef 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 */
670typedef 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 */
683typedef 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 */
695typedef 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
702static void
703jvmti_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
773static void
774jvmti_CompiledMethodUnload (jvmtiEnv *jvmti_env, jmethodID method, const void* code_addr)
775{
776 __collector_int_func_unload (DFUNC_API, (void*) code_addr);
777}
778
779static void
780jvmti_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
786static void
787addToDynamicArchive (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
831static void
832jvmti_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
863static void
864record_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
886static void
887record_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
921static void
922jvmti_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 */
975static void
976jvmti_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
1057static void
1058jvmti_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
1069static void
1070jvmti_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
1080static void
1081jvmti_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
1091static void
1092jvmti_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
1103static void
1104jvmti_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
1115static void
1116jvmti_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
1127static void
1128jprof_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
1169int
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 */
1262void
1263Java_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
1273void
1274Java_com_sun_forte_st_collector_CollectorAPI__1pause(JNIEnv *jEnv, jclass jCls)
1275{
1276 __collector_pause_m ("JAPI");
1277}
1278
1279void
1280Java_com_sun_forte_st_collector_CollectorAPI__1resume(JNIEnv *jEnv, jclass jCls)
1281{
1282 __collector_resume ();
1283}
1284
1285void
1286Java_com_sun_forte_st_collector_CollectorAPI__1terminate(JNIEnv *jEnv, jclass jCls)
1287{
1288 __collector_terminate_expt ();
1289}
1290#endif /* GPROFNG_JAVA_PROFILING */
1291
1292static void init_module () __attribute__ ((constructor));
1293static void
1294init_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
1308int __collector_java_mode = 0;
1309int __collector_java_asyncgetcalltrace_loaded = 0;