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