blob: d0f0488b53212879731fd1e495578b7bfa54318c [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2006 Sun Microsystems, Inc. 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 Sun Microsystems 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#include "stdlib.h"
33
34#include "minst.h"
35#include "java_crw_demo.h"
36
37
38/* ------------------------------------------------------------------- */
39/* Some constant maximum sizes */
40
41#define MAX_TOKEN_LENGTH 80
42#define MAX_METHOD_NAME_LENGTH 256
43
44/* Some constant names that tie to Java class/method names.
45 * We assume the Java class whose static methods we will be calling
46 * looks like:
47 *
48 * public class Minst {
49 * private static int engaged;
50 * private static native void _method_entry(Object thr, int cnum, int mnum);
51 * public static void method_entry(int cnum, int mnum)
52 * {
53 * ...
54 * }
55 * }
56 *
57 */
58
59#define MINST_class Minst /* Name of class we are using */
60#define MINST_entry method_entry /* Name of java entry method */
61#define MINST_engaged engaged /* Name of java static field */
62
63/* C macros to create strings from tokens */
64#define _STRING(s) #s
65#define STRING(s) _STRING(s)
66
67/* ------------------------------------------------------------------- */
68
69/* Global agent data structure */
70
71typedef struct {
72 /* JVMTI Environment */
73 jvmtiEnv *jvmti;
74 jboolean vm_is_dead;
75 jboolean vm_is_started;
76 /* Data access Lock */
77 jrawMonitorID lock;
78 /* Options */
79 char *include;
80 char *exclude;
81 /* Class Count/ID */
82 jint ccount;
83} GlobalAgentData;
84
85static GlobalAgentData *gdata;
86
87/* Enter a critical section by doing a JVMTI Raw Monitor Enter */
88static void
89enter_critical_section(jvmtiEnv *jvmti)
90{
91 jvmtiError error;
92
93 error = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock);
94 check_jvmti_error(jvmti, error, "Cannot enter with raw monitor");
95}
96
97/* Exit a critical section by doing a JVMTI Raw Monitor Exit */
98static void
99exit_critical_section(jvmtiEnv *jvmti)
100{
101 jvmtiError error;
102
103 error = (*jvmti)->RawMonitorExit(jvmti, gdata->lock);
104 check_jvmti_error(jvmti, error, "Cannot exit with raw monitor");
105}
106
107/* Callback for JVMTI_EVENT_VM_START */
108static void JNICALL
109cbVMStart(jvmtiEnv *jvmti, JNIEnv *env)
110{
111 enter_critical_section(jvmti); {
112 /* Indicate VM has started */
113 gdata->vm_is_started = JNI_TRUE;
114 } exit_critical_section(jvmti);
115}
116
117/* Callback for JVMTI_EVENT_VM_INIT */
118static void JNICALL
119cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
120{
121 enter_critical_section(jvmti); {
122 jclass klass;
123 jfieldID field;
124
125 /* Register Natives for class whose methods we use */
126 klass = (*env)->FindClass(env, STRING(MINST_class));
127 if ( klass == NULL ) {
128 fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",
129 STRING(MINST_class));
130 }
131
132 /* Engage calls. */
133 field = (*env)->GetStaticFieldID(env, klass, STRING(MINST_engaged), "I");
134 if ( field == NULL ) {
135 fatal_error("ERROR: JNI: Cannot get field from %s\n",
136 STRING(MINST_class));
137 }
138 (*env)->SetStaticIntField(env, klass, field, 1);
139 } exit_critical_section(jvmti);
140}
141
142/* Callback for JVMTI_EVENT_VM_DEATH */
143static void JNICALL
144cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env)
145{
146 enter_critical_section(jvmti); {
147 jclass klass;
148 jfieldID field;
149
150 /* The VM has died. */
151 stdout_message("VMDeath\n");
152
153 /* Disengage calls in MINST_class. */
154 klass = (*env)->FindClass(env, STRING(MINST_class));
155 if ( klass == NULL ) {
156 fatal_error("ERROR: JNI: Cannot find %s with FindClass\n",
157 STRING(MINST_class));
158 }
159 field = (*env)->GetStaticFieldID(env, klass, STRING(MINST_engaged), "I");
160 if ( field == NULL ) {
161 fatal_error("ERROR: JNI: Cannot get field from %s\n",
162 STRING(MINST_class));
163 }
164 (*env)->SetStaticIntField(env, klass, field, -1);
165
166 /* The critical section here is important to hold back the VM death
167 * until all other callbacks have completed.
168 */
169
170 /* Since this critical section could be holding up other threads
171 * in other event callbacks, we need to indicate that the VM is
172 * dead so that the other callbacks can short circuit their work.
173 * We don't expect any further events after VmDeath but we do need
174 * to be careful that existing threads might be in our own agent
175 * callback code.
176 */
177 gdata->vm_is_dead = JNI_TRUE;
178
179 } exit_critical_section(jvmti);
180
181}
182
183/* Callback for JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
184static void JNICALL
185cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env,
186 jclass class_being_redefined, jobject loader,
187 const char* name, jobject protection_domain,
188 jint class_data_len, const unsigned char* class_data,
189 jint* new_class_data_len, unsigned char** new_class_data)
190{
191 enter_critical_section(jvmti); {
192 /* It's possible we get here right after VmDeath event, be careful */
193 if ( !gdata->vm_is_dead ) {
194
195 const char *classname;
196
197 /* Name could be NULL */
198 if ( name == NULL ) {
199 classname = java_crw_demo_classname(class_data, class_data_len,
200 NULL);
201 if ( classname == NULL ) {
202 fatal_error("ERROR: No classname inside classfile\n");
203 }
204 } else {
205 classname = strdup(name);
206 if ( classname == NULL ) {
207 fatal_error("ERROR: Out of malloc memory\n");
208 }
209 }
210
211 *new_class_data_len = 0;
212 *new_class_data = NULL;
213
214 /* The tracker class itself? */
215 if ( interested((char*)classname, "", gdata->include, gdata->exclude)
216 && strcmp(classname, STRING(MINST_class)) != 0 ) {
217 jint cnum;
218 int system_class;
219 unsigned char *new_image;
220 long new_length;
221
222 /* Get unique number for every class file image loaded */
223 cnum = gdata->ccount++;
224
225 /* Is it a system class? If the class load is before VmStart
226 * then we will consider it a system class that should
227 * be treated carefully. (See java_crw_demo)
228 */
229 system_class = 0;
230 if ( !gdata->vm_is_started ) {
231 system_class = 1;
232 }
233
234 new_image = NULL;
235 new_length = 0;
236
237 /* Call the class file reader/write demo code */
238 java_crw_demo(cnum,
239 classname,
240 class_data,
241 class_data_len,
242 system_class,
243 STRING(MINST_class), "L" STRING(MINST_class) ";",
244 STRING(MINST_entry), "(II)V",
245 NULL, NULL,
246 NULL, NULL,
247 NULL, NULL,
248 &new_image,
249 &new_length,
250 NULL,
251 NULL);
252
253 /* If we got back a new class image, return it back as "the"
254 * new class image. This must be JVMTI Allocate space.
255 */
256 if ( new_length > 0 ) {
257 unsigned char *jvmti_space;
258
259 jvmti_space = (unsigned char *)allocate(jvmti, (jint)new_length);
260 (void)memcpy((void*)jvmti_space, (void*)new_image, (int)new_length);
261 *new_class_data_len = (jint)new_length;
262 *new_class_data = jvmti_space; /* VM will deallocate */
263 }
264
265 /* Always free up the space we get from java_crw_demo() */
266 if ( new_image != NULL ) {
267 (void)free((void*)new_image); /* Free malloc() space with free() */
268 }
269 }
270 (void)free((void*)classname);
271 }
272 } exit_critical_section(jvmti);
273}
274
275/* Parse the options for this minst agent */
276static void
277parse_agent_options(char *options)
278{
279 char token[MAX_TOKEN_LENGTH];
280 char *next;
281
282 /* Parse options and set flags in gdata */
283 if ( options==NULL ) {
284 return;
285 }
286
287 /* Get the first token from the options string. */
288 next = get_token(options, ",=", token, sizeof(token));
289
290 /* While not at the end of the options string, process this option. */
291 while ( next != NULL ) {
292 if ( strcmp(token,"help")==0 ) {
293 stdout_message("The minst JVMTI demo agent\n");
294 stdout_message("\n");
295 stdout_message(" java -agent:minst[=options] ...\n");
296 stdout_message("\n");
297 stdout_message("The options are comma separated:\n");
298 stdout_message("\t help\t\t\t Print help information\n");
299 stdout_message("\t include=item\t\t Only these classes/methods\n");
300 stdout_message("\t exclude=item\t\t Exclude these classes/methods\n");
301 stdout_message("\n");
302 stdout_message("item\t Qualified class and/or method names\n");
303 stdout_message("\t\t e.g. (*.<init>;Foobar.method;sun.*)\n");
304 stdout_message("\n");
305 exit(0);
306 } else if ( strcmp(token,"include")==0 ) {
307 int used;
308 int maxlen;
309
310 maxlen = MAX_METHOD_NAME_LENGTH;
311 if ( gdata->include == NULL ) {
312 gdata->include = (char*)calloc(maxlen+1, 1);
313 used = 0;
314 } else {
315 used = (int)strlen(gdata->include);
316 gdata->include[used++] = ',';
317 gdata->include[used] = 0;
318 gdata->include = (char*)
319 realloc((void*)gdata->include, used+maxlen+1);
320 }
321 if ( gdata->include == NULL ) {
322 fatal_error("ERROR: Out of malloc memory\n");
323 }
324 /* Add this item to the list */
325 next = get_token(next, ",=", gdata->include+used, maxlen);
326 /* Check for token scan error */
327 if ( next==NULL ) {
328 fatal_error("ERROR: include option error\n");
329 }
330 } else if ( strcmp(token,"exclude")==0 ) {
331 int used;
332 int maxlen;
333
334 maxlen = MAX_METHOD_NAME_LENGTH;
335 if ( gdata->exclude == NULL ) {
336 gdata->exclude = (char*)calloc(maxlen+1, 1);
337 used = 0;
338 } else {
339 used = (int)strlen(gdata->exclude);
340 gdata->exclude[used++] = ',';
341 gdata->exclude[used] = 0;
342 gdata->exclude = (char*)
343 realloc((void*)gdata->exclude, used+maxlen+1);
344 }
345 if ( gdata->exclude == NULL ) {
346 fatal_error("ERROR: Out of malloc memory\n");
347 }
348 /* Add this item to the list */
349 next = get_token(next, ",=", gdata->exclude+used, maxlen);
350 /* Check for token scan error */
351 if ( next==NULL ) {
352 fatal_error("ERROR: exclude option error\n");
353 }
354 } else if ( token[0]!=0 ) {
355 /* We got a non-empty token and we don't know what it is. */
356 fatal_error("ERROR: Unknown option: %s\n", token);
357 }
358 /* Get the next token (returns NULL if there are no more) */
359 next = get_token(next, ",=", token, sizeof(token));
360 }
361}
362
363/* Agent_OnLoad: This is called immediately after the shared library is
364 * loaded. This is the first code executed.
365 */
366JNIEXPORT jint JNICALL
367Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
368{
369 static GlobalAgentData data;
370 jvmtiEnv *jvmti;
371 jvmtiError error;
372 jint res;
373 jvmtiCapabilities capabilities;
374 jvmtiEventCallbacks callbacks;
375
376 /* Setup initial global agent data area
377 * Use of static/extern data should be handled carefully here.
378 * We need to make sure that we are able to cleanup after ourselves
379 * so anything allocated in this library needs to be freed in
380 * the Agent_OnUnload() function.
381 */
382 (void)memset((void*)&data, 0, sizeof(data));
383 gdata = &data;
384
385 /* First thing we need to do is get the jvmtiEnv* or JVMTI environment */
386 res = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1);
387 if (res != JNI_OK) {
388 /* This means that the VM was unable to obtain this version of the
389 * JVMTI interface, this is a fatal error.
390 */
391 fatal_error("ERROR: Unable to access JVMTI Version 1 (0x%x),"
392 " is your JDK a 5.0 or newer version?"
393 " JNIEnv's GetEnv() returned %d\n",
394 JVMTI_VERSION_1, res);
395 }
396
397 /* Here we save the jvmtiEnv* for Agent_OnUnload(). */
398 gdata->jvmti = jvmti;
399
400 /* Parse any options supplied on java command line */
401 parse_agent_options(options);
402
403 /* Immediately after getting the jvmtiEnv* we need to ask for the
404 * capabilities this agent will need. In this case we need to make
405 * sure that we can get all class load hooks.
406 */
407 (void)memset(&capabilities,0, sizeof(capabilities));
408 capabilities.can_generate_all_class_hook_events = 1;
409 error = (*jvmti)->AddCapabilities(jvmti, &capabilities);
410 check_jvmti_error(jvmti, error, "Unable to get necessary JVMTI capabilities.");
411
412 /* Next we need to provide the pointers to the callback functions to
413 * to this jvmtiEnv*
414 */
415 (void)memset(&callbacks,0, sizeof(callbacks));
416 /* JVMTI_EVENT_VM_START */
417 callbacks.VMStart = &cbVMStart;
418 /* JVMTI_EVENT_VM_INIT */
419 callbacks.VMInit = &cbVMInit;
420 /* JVMTI_EVENT_VM_DEATH */
421 callbacks.VMDeath = &cbVMDeath;
422 /* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
423 callbacks.ClassFileLoadHook = &cbClassFileLoadHook;
424 error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
425 check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");
426
427 /* At first the only initial events we are interested in are VM
428 * initialization, VM death, and Class File Loads.
429 * Once the VM is initialized we will request more events.
430 */
431 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
432 JVMTI_EVENT_VM_START, (jthread)NULL);
433 check_jvmti_error(jvmti, error, "Cannot set event notification");
434 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
435 JVMTI_EVENT_VM_INIT, (jthread)NULL);
436 check_jvmti_error(jvmti, error, "Cannot set event notification");
437 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
438 JVMTI_EVENT_VM_DEATH, (jthread)NULL);
439 check_jvmti_error(jvmti, error, "Cannot set event notification");
440 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
441 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL);
442 check_jvmti_error(jvmti, error, "Cannot set event notification");
443
444 /* Here we create a raw monitor for our use in this agent to
445 * protect critical sections of code.
446 */
447 error = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &(gdata->lock));
448 check_jvmti_error(jvmti, error, "Cannot create raw monitor");
449
450 /* Add demo jar file to boot classpath */
451 add_demo_jar_to_bootclasspath(jvmti, "minst");
452
453 /* We return JNI_OK to signify success */
454 return JNI_OK;
455}
456
457/* Agent_OnUnload: This is called immediately before the shared library is
458 * unloaded. This is the last code executed.
459 */
460JNIEXPORT void JNICALL
461Agent_OnUnload(JavaVM *vm)
462{
463 /* Make sure all malloc/calloc/strdup space is freed */
464 if ( gdata->include != NULL ) {
465 (void)free((void*)gdata->include);
466 gdata->include = NULL;
467 }
468 if ( gdata->exclude != NULL ) {
469 (void)free((void*)gdata->exclude);
470 gdata->exclude = NULL;
471 }
472}