1 /* |
|
2 * Copyright (c) 2003, 2011, 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 /* Tracker class support functions. */ |
|
42 |
|
43 /* |
|
44 * This file contains the native support calls for the Tracker |
|
45 * class. These native methods are registered and not made extern. |
|
46 * Tracking is engaged by using JNI to assign to a static field in the |
|
47 * Tracker class. |
|
48 * |
|
49 * Just like JVMTI callbacks, it's best that we keep track of these so that |
|
50 * when the VM_DEATH happens we know to wait for them to complete. |
|
51 * |
|
52 * This file also contains the functions that will initialize the Tracker |
|
53 * interface for BCI and identify the Tracker methods to make sure |
|
54 * they are not included in any stack traces obtained from JVMTI. |
|
55 * |
|
56 * RFE: The performance of the java injected code calling native methods |
|
57 * could be an issue here, cpu=times seems to be the worst where |
|
58 * a native call is made for entry and exit, even on the smallest |
|
59 * Java method. The alternative would be to cache the data on |
|
60 * the Java side, and either push it out to the native side, or |
|
61 * use some kind of pull from the native side, or even using |
|
62 * shared memory or a socket. However having said that, the |
|
63 * current performance issues are more around sheer memory needed, |
|
64 * and repeated calls to GetThreadCpuTime(), which is being investigated. |
|
65 * |
|
66 */ |
|
67 |
|
68 #include "hprof.h" |
|
69 |
|
70 /* Macros to surround tracker based callback code. |
|
71 * Also see BEGIN_CALLBACK and END_CALLBACK in hprof_init.c. |
|
72 * If the VM_DEATH callback is active in the begining, then this callback |
|
73 * just blocks (it is assumed we don't want to return to the VM). |
|
74 * If the VM_DEATH callback is active at the end, then this callback |
|
75 * will notify the VM_DEATH callback if it's the last one. |
|
76 * |
|
77 * WARNING: No not 'return' or 'goto' out of the BEGIN_TRACKER_CALLBACK/END_TRACKER_CALLBACK |
|
78 * block, this will mess up the count. |
|
79 */ |
|
80 |
|
81 #define BEGIN_TRACKER_CALLBACK() \ |
|
82 { /* BEGIN OF TRACKER_CALLBACK */ \ |
|
83 jboolean bypass = JNI_TRUE; \ |
|
84 rawMonitorEnter(gdata->callbackLock); { \ |
|
85 if ( gdata->tracking_engaged != 0 ) { \ |
|
86 if (!gdata->vm_death_callback_active) { \ |
|
87 gdata->active_callbacks++; \ |
|
88 bypass = JNI_FALSE; \ |
|
89 } \ |
|
90 } \ |
|
91 } rawMonitorExit(gdata->callbackLock); \ |
|
92 if ( !bypass ) { \ |
|
93 /* BODY OF TRACKER_CALLBACK CODE */ |
|
94 |
|
95 #define END_TRACKER_CALLBACK() /* Part of bypass if body */ \ |
|
96 rawMonitorEnter(gdata->callbackLock); { \ |
|
97 gdata->active_callbacks--; \ |
|
98 if (gdata->active_callbacks < 0) { \ |
|
99 HPROF_ERROR(JNI_TRUE, "Problems tracking callbacks"); \ |
|
100 } \ |
|
101 if (gdata->vm_death_callback_active) { \ |
|
102 if (gdata->active_callbacks == 0) { \ |
|
103 rawMonitorNotifyAll(gdata->callbackLock); \ |
|
104 } \ |
|
105 } \ |
|
106 } rawMonitorExit(gdata->callbackLock); \ |
|
107 } \ |
|
108 } /* END OF TRACKER_CALLBACK */ |
|
109 |
|
110 |
|
111 /* |
|
112 * Class: Tracker |
|
113 * Method: nativeNewArray |
|
114 * Signature: (Ljava/lang/Object;Ljava/lang/Object;)V |
|
115 */ |
|
116 static void JNICALL |
|
117 Tracker_nativeNewArray |
|
118 (JNIEnv *env, jclass clazz, jobject thread, jobject obj) |
|
119 { |
|
120 BEGIN_TRACKER_CALLBACK() { |
|
121 event_newarray(env, thread, obj); |
|
122 } END_TRACKER_CALLBACK(); |
|
123 } |
|
124 |
|
125 /* |
|
126 * Class: Tracker |
|
127 * Method: nativeObjectInit |
|
128 * Signature: (Ljava/lang/Object;Ljava/lang/Object;)V |
|
129 */ |
|
130 static void JNICALL |
|
131 Tracker_nativeObjectInit |
|
132 (JNIEnv *env, jclass clazz, jobject thread, jobject obj) |
|
133 { |
|
134 BEGIN_TRACKER_CALLBACK() { |
|
135 event_object_init(env, thread, obj); |
|
136 } END_TRACKER_CALLBACK(); |
|
137 } |
|
138 |
|
139 /* |
|
140 * Class: Tracker |
|
141 * Method: nativeCallSite |
|
142 * Signature: (Ljava/lang/Object;II)V |
|
143 */ |
|
144 static void JNICALL |
|
145 Tracker_nativeCallSite |
|
146 (JNIEnv *env, jclass clazz, jobject thread, jint cnum, jint mnum) |
|
147 { |
|
148 BEGIN_TRACKER_CALLBACK() { |
|
149 event_call(env, thread, cnum, mnum); |
|
150 } END_TRACKER_CALLBACK(); |
|
151 } |
|
152 |
|
153 /* |
|
154 * Class: Tracker |
|
155 * Method: nativeReturnSite |
|
156 * Signature: (Ljava/lang/Object;II)V |
|
157 */ |
|
158 static void JNICALL |
|
159 Tracker_nativeReturnSite |
|
160 (JNIEnv *env, jclass clazz, jobject thread, jint cnum, jint mnum) |
|
161 { |
|
162 BEGIN_TRACKER_CALLBACK() { |
|
163 event_return(env, thread, cnum, mnum); |
|
164 } END_TRACKER_CALLBACK(); |
|
165 } |
|
166 |
|
167 |
|
168 /* ------------------------------------------------------------------- */ |
|
169 /* Set Java static field to turn on native code calls in Tracker. */ |
|
170 |
|
171 static void |
|
172 set_engaged(JNIEnv *env, jint engaged) |
|
173 { |
|
174 LOG3("set_engaged()", "engaging tracking", engaged); |
|
175 |
|
176 if ( ! gdata->bci ) { |
|
177 return; |
|
178 } |
|
179 rawMonitorEnter(gdata->callbackLock); { |
|
180 if ( gdata->tracking_engaged != engaged ) { |
|
181 jfieldID field; |
|
182 jclass tracker_class; |
|
183 |
|
184 tracker_class = class_get_class(env, gdata->tracker_cnum); |
|
185 gdata->tracking_engaged = 0; |
|
186 /* Activate or deactivate the injection code on the Java side */ |
|
187 HPROF_ASSERT(tracker_class!=NULL); |
|
188 exceptionClear(env); |
|
189 field = getStaticFieldID(env, tracker_class, |
|
190 TRACKER_ENGAGED_NAME, TRACKER_ENGAGED_SIG); |
|
191 setStaticIntField(env, tracker_class, field, engaged); |
|
192 exceptionClear(env); |
|
193 |
|
194 LOG3("set_engaged()", "tracking engaged", engaged); |
|
195 |
|
196 gdata->tracking_engaged = engaged; |
|
197 } |
|
198 } rawMonitorExit(gdata->callbackLock); |
|
199 } |
|
200 |
|
201 void |
|
202 tracker_engage(JNIEnv *env) |
|
203 { |
|
204 set_engaged(env, 0xFFFF); |
|
205 } |
|
206 |
|
207 void |
|
208 tracker_disengage(JNIEnv *env) |
|
209 { |
|
210 set_engaged(env, 0); |
|
211 } |
|
212 |
|
213 jboolean |
|
214 tracker_method(jmethodID method) |
|
215 { |
|
216 int i; |
|
217 |
|
218 if ( ! gdata->bci ) { |
|
219 return JNI_FALSE; |
|
220 } |
|
221 |
|
222 HPROF_ASSERT(method!=NULL); |
|
223 HPROF_ASSERT(gdata->tracker_method_count > 0); |
|
224 for ( i = 0 ; i < gdata->tracker_method_count ; i++ ) { |
|
225 HPROF_ASSERT(gdata->tracker_methods[i].method!=NULL); |
|
226 if ( method == gdata->tracker_methods[i].method ) { |
|
227 return JNI_TRUE; |
|
228 } |
|
229 } |
|
230 return JNI_FALSE; |
|
231 } |
|
232 |
|
233 static JNINativeMethod registry[4] = |
|
234 { |
|
235 { TRACKER_NEWARRAY_NATIVE_NAME, TRACKER_NEWARRAY_NATIVE_SIG, |
|
236 (void*)&Tracker_nativeNewArray }, |
|
237 { TRACKER_OBJECT_INIT_NATIVE_NAME, TRACKER_OBJECT_INIT_NATIVE_SIG, |
|
238 (void*)&Tracker_nativeObjectInit }, |
|
239 { TRACKER_CALL_NATIVE_NAME, TRACKER_CALL_NATIVE_SIG, |
|
240 (void*)&Tracker_nativeCallSite }, |
|
241 { TRACKER_RETURN_NATIVE_NAME, TRACKER_RETURN_NATIVE_SIG, |
|
242 (void*)&Tracker_nativeReturnSite } |
|
243 }; |
|
244 |
|
245 static struct { |
|
246 char *name; |
|
247 char *sig; |
|
248 } tracker_methods[] = |
|
249 { |
|
250 { TRACKER_NEWARRAY_NAME, TRACKER_NEWARRAY_SIG }, |
|
251 { TRACKER_OBJECT_INIT_NAME, TRACKER_OBJECT_INIT_SIG }, |
|
252 { TRACKER_CALL_NAME, TRACKER_CALL_SIG }, |
|
253 { TRACKER_RETURN_NAME, TRACKER_RETURN_SIG }, |
|
254 { TRACKER_NEWARRAY_NATIVE_NAME, TRACKER_NEWARRAY_NATIVE_SIG }, |
|
255 { TRACKER_OBJECT_INIT_NATIVE_NAME, TRACKER_OBJECT_INIT_NATIVE_SIG }, |
|
256 { TRACKER_CALL_NATIVE_NAME, TRACKER_CALL_NATIVE_SIG }, |
|
257 { TRACKER_RETURN_NATIVE_NAME, TRACKER_RETURN_NATIVE_SIG } |
|
258 }; |
|
259 |
|
260 void |
|
261 tracker_setup_class(void) |
|
262 { |
|
263 ClassIndex cnum; |
|
264 LoaderIndex loader_index; |
|
265 |
|
266 HPROF_ASSERT(gdata->tracker_cnum==0); |
|
267 loader_index = loader_find_or_create(NULL,NULL); |
|
268 cnum = class_find_or_create(TRACKER_CLASS_SIG, loader_index); |
|
269 gdata->tracker_cnum = cnum; |
|
270 HPROF_ASSERT(cnum!=0); |
|
271 class_add_status(cnum, CLASS_SPECIAL); |
|
272 } |
|
273 |
|
274 void |
|
275 tracker_setup_methods(JNIEnv *env) |
|
276 { |
|
277 ClassIndex cnum; |
|
278 LoaderIndex loader_index; |
|
279 int i; |
|
280 jclass object_class; |
|
281 jclass tracker_class; |
|
282 |
|
283 if ( ! gdata->bci ) { |
|
284 return; |
|
285 } |
|
286 |
|
287 loader_index = loader_find_or_create(NULL,NULL); |
|
288 cnum = class_find_or_create(OBJECT_CLASS_SIG, loader_index); |
|
289 object_class = class_get_class(env, cnum); |
|
290 tracker_class = class_get_class(env, gdata->tracker_cnum); |
|
291 |
|
292 CHECK_EXCEPTIONS(env) { |
|
293 registerNatives(env, tracker_class, registry, |
|
294 (int)sizeof(registry)/(int)sizeof(registry[0])); |
|
295 } END_CHECK_EXCEPTIONS; |
|
296 |
|
297 HPROF_ASSERT(tracker_class!=NULL); |
|
298 |
|
299 gdata->tracker_method_count = |
|
300 (int)sizeof(tracker_methods)/(int)sizeof(tracker_methods[0]); |
|
301 |
|
302 HPROF_ASSERT(gdata->tracker_method_count <= |
|
303 (int)(sizeof(gdata->tracker_methods)/sizeof(gdata->tracker_methods[0]))); |
|
304 |
|
305 CHECK_EXCEPTIONS(env) { |
|
306 gdata->object_init_method = getMethodID(env, object_class, |
|
307 OBJECT_INIT_NAME, OBJECT_INIT_SIG); |
|
308 for ( i=0 ; i < gdata->tracker_method_count ; i++ ) { |
|
309 gdata->tracker_methods[i].name = |
|
310 string_find_or_create(tracker_methods[i].name); |
|
311 gdata->tracker_methods[i].sig = |
|
312 string_find_or_create(tracker_methods[i].sig); |
|
313 gdata->tracker_methods[i].method = |
|
314 getStaticMethodID(env, tracker_class, |
|
315 tracker_methods[i].name, tracker_methods[i].sig); |
|
316 HPROF_ASSERT(gdata->tracker_methods[i].method!=NULL); |
|
317 LOG2("tracker_setup_methods(): Found", tracker_methods[i].name); |
|
318 } |
|
319 } END_CHECK_EXCEPTIONS; |
|
320 } |
|