1 /*
2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of Oracle nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * This source code is provided to illustrate the usage of a given feature
34 * or technique and has been deliberately simplified. Additional steps
35 * required for a production-quality application, such as security checks,
36 * input validation and proper error handling, might not be present in
37 * this sample code.
38 */
39
40
41 /* This file contains all class, method and allocation event support functions,
42 * both JVMTI and BCI events.
43 * (See hprof_monitor.c for the monitor event handlers).
44 */
45
46 #include "hprof.h"
47
48 /* Private internal functions. */
49
50 /* Return a TraceIndex for the given thread. */
51 static TraceIndex
get_current(TlsIndex tls_index,JNIEnv * env,jboolean skip_init)52 get_current(TlsIndex tls_index, JNIEnv *env, jboolean skip_init)
53 {
54 TraceIndex trace_index;
55
56 trace_index = tls_get_trace(tls_index, env, gdata->max_trace_depth, skip_init);
57 return trace_index;
58 }
59
60 /* Return a ClassIndex for the given jclass, loader supplied or looked up. */
61 static ClassIndex
find_cnum(JNIEnv * env,jclass klass,jobject loader)62 find_cnum(JNIEnv *env, jclass klass, jobject loader)
63 {
64 LoaderIndex loader_index;
65 ClassIndex cnum;
66 char * signature;
67
68 HPROF_ASSERT(klass!=NULL);
69
70 /* Get the loader index */
71 loader_index = loader_find_or_create(env, loader);
72
73 /* Get the signature for this class */
74 getClassSignature(klass, &signature, NULL);
75
76 /* Find the ClassIndex for this class */
77 cnum = class_find_or_create(signature, loader_index);
78
79 /* Free the signature space */
80 jvmtiDeallocate(signature);
81
82 /* Make sure we save a global reference to this class in the table */
83 HPROF_ASSERT(cnum!=0);
84 (void)class_new_classref(env, cnum, klass);
85 return cnum;
86 }
87
88 /* Get the ClassIndex for the superClass of this jclass. */
89 static ClassIndex
get_super(JNIEnv * env,jclass klass)90 get_super(JNIEnv *env, jclass klass)
91 {
92 ClassIndex super_cnum;
93
94 super_cnum = 0;
95 WITH_LOCAL_REFS(env, 1) {
96 jclass super_klass;
97
98 super_klass = getSuperclass(env, klass);
99 if ( super_klass != NULL ) {
100 super_cnum = find_cnum(env, super_klass,
101 getClassLoader(super_klass));
102 }
103 } END_WITH_LOCAL_REFS;
104 return super_cnum;
105 }
106
107 /* Record an allocation. Could be jobject, jclass, jarray or primitive type. */
108 static void
any_allocation(JNIEnv * env,SerialNumber thread_serial_num,TraceIndex trace_index,jobject object)109 any_allocation(JNIEnv *env, SerialNumber thread_serial_num,
110 TraceIndex trace_index, jobject object)
111 {
112 SiteIndex site_index;
113 ClassIndex cnum;
114 jint size;
115 jclass klass;
116
117 /* NOTE: Normally the getObjectClass() and getClassLoader()
118 * would require a
119 * WITH_LOCAL_REFS(env, 1) {
120 * } END_WITH_LOCAL_REFS;
121 * but for performance reasons we skip it here.
122 */
123
124 /* Get and tag the klass */
125 klass = getObjectClass(env, object);
126 cnum = find_cnum(env, klass, getClassLoader(klass));
127 site_index = site_find_or_create(cnum, trace_index);
128 tag_class(env, klass, cnum, thread_serial_num, site_index);
129
130 /* Tag the object */
131 size = (jint)getObjectSize(object);
132 tag_new_object(object, OBJECT_NORMAL, thread_serial_num, size, site_index);
133 }
134
135 /* Handle a java.lang.Object.<init> object allocation. */
136 void
event_object_init(JNIEnv * env,jthread thread,jobject object)137 event_object_init(JNIEnv *env, jthread thread, jobject object)
138 {
139 /* Called via BCI Tracker class */
140
141 /* Be very careful what is called here, watch out for recursion. */
142
143 jint *pstatus;
144 TraceIndex trace_index;
145 SerialNumber thread_serial_num;
146
147 HPROF_ASSERT(env!=NULL);
148 HPROF_ASSERT(thread!=NULL);
149 HPROF_ASSERT(object!=NULL);
150
151 /* Prevent recursion into any BCI function for this thread (pstatus). */
152 if ( tls_get_tracker_status(env, thread, JNI_TRUE,
153 &pstatus, NULL, &thread_serial_num, &trace_index) == 0 ) {
154 (*pstatus) = 1;
155 any_allocation(env, thread_serial_num, trace_index, object);
156 (*pstatus) = 0;
157 }
158 }
159
160 /* Handle any newarray opcode allocation. */
161 void
event_newarray(JNIEnv * env,jthread thread,jobject object)162 event_newarray(JNIEnv *env, jthread thread, jobject object)
163 {
164 /* Called via BCI Tracker class */
165
166 /* Be very careful what is called here, watch out for recursion. */
167
168 jint *pstatus;
169 TraceIndex trace_index;
170 SerialNumber thread_serial_num;
171
172 HPROF_ASSERT(env!=NULL);
173 HPROF_ASSERT(thread!=NULL);
174 HPROF_ASSERT(object!=NULL);
175
176 /* Prevent recursion into any BCI function for this thread (pstatus). */
177 if ( tls_get_tracker_status(env, thread, JNI_FALSE,
178 &pstatus, NULL, &thread_serial_num, &trace_index) == 0 ) {
179 (*pstatus) = 1;
180 any_allocation(env, thread_serial_num, trace_index, object);
181 (*pstatus) = 0;
182 }
183 }
184
185 /* Handle tracking of a method call. */
186 void
event_call(JNIEnv * env,jthread thread,ClassIndex cnum,MethodIndex mnum)187 event_call(JNIEnv *env, jthread thread, ClassIndex cnum, MethodIndex mnum)
188 {
189 /* Called via BCI Tracker class */
190
191 /* Be very careful what is called here, watch out for recursion. */
192
193 TlsIndex tls_index;
194 jint *pstatus;
195
196 HPROF_ASSERT(env!=NULL);
197 HPROF_ASSERT(thread!=NULL);
198 if (cnum == 0 || cnum == gdata->tracker_cnum) {
199 jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
200 if ((*env)->ExceptionCheck(env)) {
201 (*env)->ExceptionClear(env);
202 HPROF_ERROR(JNI_TRUE,
203 "Could not find the java/lang/IllegalArgumentException class");
204 }
205 (*env)->ThrowNew(env, newExcCls, "Illegal cnum.");
206
207 return;
208 }
209
210 /* Prevent recursion into any BCI function for this thread (pstatus). */
211 if ( tls_get_tracker_status(env, thread, JNI_FALSE,
212 &pstatus, &tls_index, NULL, NULL) == 0 ) {
213 jmethodID method;
214
215 (*pstatus) = 1;
216 method = class_get_methodID(env, cnum, mnum);
217 if (method != NULL) {
218 tls_push_method(tls_index, method);
219 }
220
221 (*pstatus) = 0;
222 }
223 }
224
225 /* Handle tracking of an exception catch */
226 void
event_exception_catch(JNIEnv * env,jthread thread,jmethodID method,jlocation location,jobject exception)227 event_exception_catch(JNIEnv *env, jthread thread, jmethodID method,
228 jlocation location, jobject exception)
229 {
230 /* Called via JVMTI_EVENT_EXCEPTION_CATCH callback */
231
232 /* Be very careful what is called here, watch out for recursion. */
233
234 TlsIndex tls_index;
235 jint *pstatus;
236
237 HPROF_ASSERT(env!=NULL);
238 HPROF_ASSERT(thread!=NULL);
239 HPROF_ASSERT(method!=NULL);
240
241 /* Prevent recursion into any BCI function for this thread (pstatus). */
242 if ( tls_get_tracker_status(env, thread, JNI_FALSE,
243 &pstatus, &tls_index, NULL, NULL) == 0 ) {
244 (*pstatus) = 1;
245 tls_pop_exception_catch(tls_index, thread, method);
246 (*pstatus) = 0;
247 }
248 }
249
250 /* Handle tracking of a method return pop one (maybe more) methods. */
251 void
event_return(JNIEnv * env,jthread thread,ClassIndex cnum,MethodIndex mnum)252 event_return(JNIEnv *env, jthread thread, ClassIndex cnum, MethodIndex mnum)
253 {
254 /* Called via BCI Tracker class */
255
256 /* Be very careful what is called here, watch out for recursion. */
257
258 TlsIndex tls_index;
259 jint *pstatus;
260
261 HPROF_ASSERT(env!=NULL);
262 HPROF_ASSERT(thread!=NULL);
263
264 if (cnum == 0 || cnum == gdata->tracker_cnum) {
265 jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
266 if ((*env)->ExceptionCheck(env)) {
267 (*env)->ExceptionClear(env);
268 HPROF_ERROR(JNI_TRUE,
269 "Could not find the java/lang/IllegalArgumentException class");
270 }
271 (*env)->ThrowNew(env, newExcCls, "Illegal cnum.");
272
273 return;
274 }
275
276 /* Prevent recursion into any BCI function for this thread (pstatus). */
277 if ( tls_get_tracker_status(env, thread, JNI_FALSE,
278 &pstatus, &tls_index, NULL, NULL) == 0 ) {
279 jmethodID method;
280
281 (*pstatus) = 1;
282 method = class_get_methodID(env, cnum, mnum);
283 if (method != NULL) {
284 tls_pop_method(tls_index, thread, method);
285 }
286
287 (*pstatus) = 0;
288 }
289 }
290
291 /* Handle a class prepare (should have been already loaded) */
292 void
event_class_prepare(JNIEnv * env,jthread thread,jclass klass,jobject loader)293 event_class_prepare(JNIEnv *env, jthread thread, jclass klass, jobject loader)
294 {
295 /* Called via JVMTI_EVENT_CLASS_PREPARE event */
296
297 ClassIndex cnum;
298
299 HPROF_ASSERT(env!=NULL);
300 HPROF_ASSERT(thread!=NULL);
301 HPROF_ASSERT(klass!=NULL);
302
303 /* Find the ClassIndex for this class */
304 cnum = find_cnum(env, klass, loader);
305 class_add_status(cnum, CLASS_PREPARED);
306
307 }
308
309 /* Handle a class load (could have been already loaded) */
310 void
event_class_load(JNIEnv * env,jthread thread,jclass klass,jobject loader)311 event_class_load(JNIEnv *env, jthread thread, jclass klass, jobject loader)
312 {
313 /* Called via JVMTI_EVENT_CLASS_LOAD event or reset_class_load_status() */
314
315 ClassIndex cnum;
316
317 HPROF_ASSERT(env!=NULL);
318 HPROF_ASSERT(klass!=NULL);
319
320 /* Find the ClassIndex for this class */
321 cnum = find_cnum(env, klass, loader);
322
323 /* Always mark it as being in the load list */
324 class_add_status(cnum, CLASS_IN_LOAD_LIST);
325
326 /* If we are seeing this as a new loaded class, extra work */
327 if ( ! ( class_get_status(cnum) & CLASS_LOADED ) ) {
328 TraceIndex trace_index;
329 SiteIndex site_index;
330 ClassIndex super;
331 SerialNumber class_serial_num;
332 SerialNumber trace_serial_num;
333 SerialNumber thread_serial_num;
334 ObjectIndex class_object_index;
335 char *signature;
336
337 /* Get the TlsIndex and a TraceIndex for this location */
338 if ( thread == NULL ) {
339 /* This should be very rare, but if this class load was simulated
340 * from hprof_init.c due to a reset of the class load status,
341 * and it originated from a pre-VM_INIT event, the jthread
342 * would be NULL, or it was a jclass created that didn't get
343 * reported to us, like an array class or a primitive class?
344 */
345 trace_index = gdata->system_trace_index;
346 thread_serial_num = gdata->unknown_thread_serial_num;
347 } else {
348 TlsIndex tls_index;
349
350 tls_index = tls_find_or_create(env, thread);
351 trace_index = get_current(tls_index, env, JNI_FALSE);
352 thread_serial_num = tls_get_thread_serial_number(tls_index);
353 }
354
355 /* Get the SiteIndex for this location and a java.lang.Class object */
356 /* Note that the target cnum, not the cnum for java.lang.Class. */
357 site_index = site_find_or_create(cnum, trace_index);
358
359 /* Tag this java.lang.Class object */
360 tag_class(env, klass, cnum, thread_serial_num, site_index);
361
362 class_add_status(cnum, CLASS_LOADED);
363
364 class_serial_num = class_get_serial_number(cnum);
365 class_object_index = class_get_object_index(cnum);
366 trace_serial_num = trace_get_serial_number(trace_index);
367 signature = string_get(class_get_signature(cnum));
368
369 rawMonitorEnter(gdata->data_access_lock); {
370 io_write_class_load(class_serial_num, class_object_index,
371 trace_serial_num, signature);
372 } rawMonitorExit(gdata->data_access_lock);
373
374 super = get_super(env, klass);
375 class_set_super(cnum, super);
376 }
377
378 }
379
380 /* Handle a thread start event */
381 void
event_thread_start(JNIEnv * env,jthread thread)382 event_thread_start(JNIEnv *env, jthread thread)
383 {
384 /* Called via JVMTI_EVENT_THREAD_START event */
385
386 TlsIndex tls_index;
387 ObjectIndex object_index;
388 TraceIndex trace_index;
389 jlong tag;
390 SerialNumber thread_serial_num;
391
392 HPROF_ASSERT(env!=NULL);
393 HPROF_ASSERT(thread!=NULL);
394
395 tls_index = tls_find_or_create(env, thread);
396 thread_serial_num = tls_get_thread_serial_number(tls_index);
397 trace_index = get_current(tls_index, env, JNI_FALSE);
398
399 tag = getTag(thread);
400 if ( tag == (jlong)0 ) {
401 SiteIndex site_index;
402 jint size;
403
404 size = (jint)getObjectSize(thread);
405 site_index = site_find_or_create(gdata->thread_cnum, trace_index);
406 /* We create a new object with this thread's serial number */
407 object_index = object_new(site_index, size, OBJECT_NORMAL,
408 thread_serial_num);
409 } else {
410 object_index = tag_extract(tag);
411 /* Normally the Thread object is created and tagged before we get
412 * here, but the thread_serial_number on this object isn't what
413 * we want. So we update it to the serial number of this thread.
414 */
415 object_set_thread_serial_number(object_index, thread_serial_num);
416 }
417 tls_set_thread_object_index(tls_index, object_index);
418
419 WITH_LOCAL_REFS(env, 1) {
420 jvmtiThreadInfo threadInfo;
421 jvmtiThreadGroupInfo threadGroupInfo;
422 jvmtiThreadGroupInfo parentGroupInfo;
423
424 getThreadInfo(thread, &threadInfo);
425 getThreadGroupInfo(threadInfo.thread_group, &threadGroupInfo);
426 if ( threadGroupInfo.parent != NULL ) {
427 getThreadGroupInfo(threadGroupInfo.parent, &parentGroupInfo);
428 } else {
429 (void)memset(&parentGroupInfo, 0, sizeof(parentGroupInfo));
430 }
431
432 rawMonitorEnter(gdata->data_access_lock); {
433 io_write_thread_start(thread_serial_num,
434 object_index, trace_get_serial_number(trace_index),
435 threadInfo.name, threadGroupInfo.name, parentGroupInfo.name);
436 } rawMonitorExit(gdata->data_access_lock);
437
438 jvmtiDeallocate(threadInfo.name);
439 jvmtiDeallocate(threadGroupInfo.name);
440 jvmtiDeallocate(parentGroupInfo.name);
441
442 } END_WITH_LOCAL_REFS;
443 }
444
445 void
event_thread_end(JNIEnv * env,jthread thread)446 event_thread_end(JNIEnv *env, jthread thread)
447 {
448 /* Called via JVMTI_EVENT_THREAD_END event */
449 TlsIndex tls_index;
450
451 HPROF_ASSERT(env!=NULL);
452 HPROF_ASSERT(thread!=NULL);
453
454 tls_index = tls_find_or_create(env, thread);
455 rawMonitorEnter(gdata->data_access_lock); {
456 io_write_thread_end(tls_get_thread_serial_number(tls_index));
457 } rawMonitorExit(gdata->data_access_lock);
458 tls_thread_ended(env, tls_index);
459 setThreadLocalStorage(thread, (void*)NULL);
460 }
461