1 /*
  2  * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 #include <stdio.h>
 25 #include <string.h>
 26 #include <inttypes.h>
 27 #include "jvmti.h"
 28 #include "jvmti_common.h"
 29 
 30 extern "C" {
 31 
 32 
 33 #define PASSED 0
 34 #define STATUS_FAILED 2
 35 
 36 typedef struct {
 37   jfieldID fid;
 38   char *m_cls;
 39   char *m_name;
 40   char *m_sig;
 41   jlocation loc;
 42   char *f_cls;
 43   char *f_name;
 44   char *f_sig;
 45   jboolean is_static;
 46   jvalue val;
 47 } writable_watch_info;
 48 
 49 typedef struct {
 50   jfieldID fid;
 51   const char *m_cls;
 52   const char *m_name;
 53   const char *m_sig;
 54   jlocation loc;
 55   const char *f_cls;
 56   const char *f_name;
 57   const char *f_sig;
 58   jboolean is_static;
 59   jvalue val;
 60 } watch_info;
 61 
 62 static jvmtiEnv *jvmti;
 63 static jvmtiEventCallbacks callbacks;
 64 static jvmtiCapabilities caps;
 65 static jint result = PASSED;
 66 static volatile jboolean isVirtualExpected = JNI_FALSE;
 67 static int eventsExpected = 0;
 68 static int eventsCount = 0;
 69 static watch_info watches[] = {
 70     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 71         "Lfieldmod02a;", "staticBoolean", "Z", JNI_TRUE, {} },
 72     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 73         "Lfieldmod02a;", "staticByte", "B", JNI_TRUE, {} },
 74     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 75         "Lfieldmod02a;", "staticShort", "S", JNI_TRUE, {} },
 76     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 77         "Lfieldmod02a;", "staticInt", "I", JNI_TRUE, {} },
 78     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 79         "Lfieldmod02a;", "staticLong", "J", JNI_TRUE, {} },
 80     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 81         "Lfieldmod02a;", "staticFloat", "F", JNI_TRUE, {} },
 82     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 83         "Lfieldmod02a;", "staticDouble", "D", JNI_TRUE, {} },
 84     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 85         "Lfieldmod02a;", "staticChar", "C", JNI_TRUE, {} },
 86     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 87         "Lfieldmod02a;", "staticObject", "Ljava/lang/Object;", JNI_TRUE, {} },
 88     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 89         "Lfieldmod02a;", "staticArrInt", "[I", JNI_TRUE, {} },
 90 
 91     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 92         "Lfieldmod02a;", "instanceBoolean", "Z", JNI_FALSE, {} },
 93     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 94         "Lfieldmod02a;", "instanceByte", "B", JNI_FALSE, {} },
 95     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 96         "Lfieldmod02a;", "instanceShort", "S", JNI_FALSE, {} },
 97     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
 98         "Lfieldmod02a;", "instanceInt", "I", JNI_FALSE, {} },
 99     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
100         "Lfieldmod02a;", "instanceLong", "J", JNI_FALSE, {} },
101     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
102         "Lfieldmod02a;", "instanceFloat", "F", JNI_FALSE, {} },
103     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
104         "Lfieldmod02a;", "instanceDouble", "D", JNI_FALSE, {} },
105     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
106         "Lfieldmod02a;", "instanceChar", "C", JNI_FALSE, {} },
107     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
108         "Lfieldmod02a;", "instanceObject", "Ljava/lang/Object;", JNI_FALSE, {} },
109     { NULL, "Lfieldmod02;", "check", "(Ljava/lang/Object;)I", 0,
110         "Lfieldmod02a;", "instanceArrInt", "[I", JNI_FALSE, {} }
111 };
112 
113 void printValue(jvalue val, char *sig) {
114   switch (*sig) {
115     case 'J':
116       LOG("0x%x%08x", (jint)(val.j >> 32), (jint)val.j);
117       break;
118     case 'F':
119       LOG("%.3f", (double)val.f);
120       break;
121     case 'D':
122       LOG("%f", (double)val.d);
123       break;
124     case 'L':
125     case '[':
126       LOG("0x%p", val.l);
127       break;
128     case 'Z':
129       LOG("0x%x", val.z);
130       break;
131     case 'B':
132       LOG("%d", val.b);
133       break;
134     case 'S':
135       LOG("%d", val.s);
136       break;
137     case 'C':
138       LOG("0x%x", val.c);
139       break;
140     case 'I':
141       LOG("%d", val.i);
142       break;
143     default:
144       LOG("0x%x%08x", (jint)(val.j >> 32), (jint)val.j);
145       break;
146   }
147 }
148 
149 int isEqual(JNIEnv *jni, char *sig, jvalue v1, jvalue v2) {
150   switch (*sig) {
151     case 'J':
152       return (v1.j == v2.j);
153     case 'F':
154       return (v1.f == v2.f);
155     case 'D':
156       return (v1.d == v2.d);
157     case 'L':
158     case '[':
159       return jni->IsSameObject(v1.l, v2.l);
160     case 'Z':
161       return (v1.z == v2.z);
162     case 'B':
163       return (v1.b == v2.b);
164     case 'S':
165       return (v1.s == v2.s);
166     case 'C':
167       return (v1.c == v2.c);
168     case 'I':
169       return (v1.i == v2.i);
170     default:
171       return (1);
172   }
173 }
174 
175 void JNICALL FieldModification(jvmtiEnv *jvmti, JNIEnv *jni,
176                                jthread thr, jmethodID method, jlocation location,
177                                jclass field_klass, jobject obj,
178                                jfieldID field, char sig, jvalue new_value) {
179   jvmtiError err;
180   jclass cls;
181   writable_watch_info watch;
182   char *generic;
183   size_t i;
184 
185   eventsCount++;
186 
187   LOG(">>> retrieving modification watch info ...\n");
188 
189   watch.fid = field;
190   watch.loc = location;
191   watch.is_static = (obj == NULL) ? JNI_TRUE : JNI_FALSE;
192   watch.val = new_value;
193   err = jvmti->GetMethodDeclaringClass(method, &cls);
194   if (err != JVMTI_ERROR_NONE) {
195     LOG("(GetMethodDeclaringClass) unexpected error: %s (%d)\n",
196            TranslateError(err), err);
197     result = STATUS_FAILED;
198     return;
199   }
200   err = jvmti->GetClassSignature(cls,
201                                      &watch.m_cls, &generic);
202   if (err != JVMTI_ERROR_NONE) {
203     LOG("(GetClassSignature) unexpected error: %s (%d)\n",
204            TranslateError(err), err);
205     result = STATUS_FAILED;
206     return;
207   }
208   err = jvmti->GetMethodName(method, &watch.m_name, &watch.m_sig, &generic);
209   if (err != JVMTI_ERROR_NONE) {
210     LOG("(GetMethodName) unexpected error: %s (%d)\n",
211            TranslateError(err), err);
212     result = STATUS_FAILED;
213     return;
214   }
215   err = jvmti->GetClassSignature(field_klass,
216                                      &watch.f_cls, &generic);
217   if (err != JVMTI_ERROR_NONE) {
218     LOG("(GetClassSignature) unexpected error: %s (%d)\n",
219            TranslateError(err), err);
220     result = STATUS_FAILED;
221     return;
222   }
223   err = jvmti->GetFieldName(field_klass, field,
224                                 &watch.f_name, &watch.f_sig, &generic);
225   if (err != JVMTI_ERROR_NONE) {
226     LOG("(GetFieldName) unexpected error: %s (%d)\n",
227            TranslateError(err), err);
228     result = STATUS_FAILED;
229     return;
230   }
231 
232   LOG(">>>      class: \"%s\"\n", watch.m_cls);
233   LOG(">>>     method: \"%s%s\"\n", watch.m_name, watch.m_sig);
234   LOG(">>>   location: 0x%x%08x\n",
235          (jint)(watch.loc >> 32), (jint)watch.loc);
236   LOG(">>>  field cls: \"%s\"\n", watch.f_cls);
237   LOG(">>>      field: \"%s:%s\"\n", watch.f_name, watch.f_sig);
238   LOG(">>>     object: 0x%p\n", obj);
239   LOG(">>>  new value: ");
240   printValue(watch.val, watch.f_sig);
241   LOG("\n");
242 
243   for (i = 0; i < sizeof(watches)/sizeof(watch_info); i++) {
244     if (watch.fid == watches[i].fid) {
245       if (watch.m_cls == NULL ||
246           strcmp(watch.m_cls, watches[i].m_cls) != 0) {
247         LOG("(watch#%" PRIuPTR ") wrong class: \"%s\", expected: \"%s\"\n",
248                i, watch.m_cls, watches[i].m_cls);
249         result = STATUS_FAILED;
250       }
251       if (watch.m_name == NULL ||
252           strcmp(watch.m_name, watches[i].m_name) != 0) {
253         LOG("(watch#%" PRIuPTR ") wrong method name: \"%s\"",
254                i, watch.m_name);
255         LOG(", expected: \"%s\"\n", watches[i].m_name);
256         result = STATUS_FAILED;
257       }
258       if (watch.m_sig == NULL ||
259           strcmp(watch.m_sig, watches[i].m_sig) != 0) {
260         LOG("(watch#%" PRIuPTR ") wrong method sig: \"%s\"",
261                i, watch.m_sig);
262         LOG(", expected: \"%s\"\n", watches[i].m_sig);
263         result = STATUS_FAILED;
264       }
265       if (watch.loc != watches[i].loc) {
266         LOG("(watch#%" PRIuPTR ") wrong location: 0x%x%08x",
267                i, (jint)(watch.loc >> 32), (jint)watch.loc);
268         LOG(", expected: 0x%x%08x\n",
269                (jint)(watches[i].loc >> 32), (jint)watches[i].loc);
270         result = STATUS_FAILED;
271       }
272       if (watch.f_name == NULL ||
273           strcmp(watch.f_name, watches[i].f_name) != 0) {
274         LOG("(watch#%" PRIuPTR ") wrong field name: \"%s\"",
275                i, watch.f_name);
276         LOG(", expected: \"%s\"\n", watches[i].f_name);
277         result = STATUS_FAILED;
278       }
279       if (watch.f_sig == NULL ||
280           strcmp(watch.f_sig, watches[i].f_sig) != 0) {
281         LOG("(watch#%" PRIuPTR ") wrong field sig: \"%s\"",
282                i, watch.f_sig);
283         LOG(", expected: \"%s\"\n", watches[i].f_sig);
284         result = STATUS_FAILED;
285       }
286       if (watch.is_static != watches[i].is_static) {
287         LOG("(watch#%" PRIuPTR ") wrong field type: %s", i,
288                (watch.is_static == JNI_TRUE) ? "static" : "instance");
289         LOG(", expected: %s\n",
290                (watches[i].is_static == JNI_TRUE) ? "static" : "instance");
291         result = STATUS_FAILED;
292       }
293       if (!isEqual((JNIEnv *)jni, watch.f_sig, watch.val, watches[i].val)) {
294         LOG("(watch#%" PRIuPTR ") wrong new value: ", i);
295         printValue(watch.val, watch.f_sig);
296         LOG(", expected: ");
297         printValue(watches[i].val, watch.f_sig);
298         LOG("\n");
299         result = STATUS_FAILED;
300       }
301       jboolean isVirtual = jni->IsVirtualThread(thr);
302       if (isVirtualExpected != isVirtual) {
303         LOG("The thread IsVirtualThread %d differs from expected %d.\n", isVirtual, isVirtualExpected);
304         result = STATUS_FAILED;
305       }
306       return;
307     }
308   }
309   LOG("Unexpected field modification catched: 0x%p\n", watch.fid);
310   result = STATUS_FAILED;
311 }
312 
313 #ifdef STATIC_BUILD
314 JNIEXPORT jint JNICALL Agent_OnLoad_fieldmod02(JavaVM *jvm, char *options, void *reserved) {
315     return Agent_Initialize(jvm, options, reserved);
316 }
317 JNIEXPORT jint JNICALL Agent_OnAttach_fieldmod02(JavaVM *jvm, char *options, void *reserved) {
318     return Agent_Initialize(jvm, options, reserved);
319 }
320 JNIEXPORT jint JNI_OnLoad_fieldmod02(JavaVM *jvm, char *options, void *reserved) {
321     return JNI_VERSION_1_8;
322 }
323 #endif
324 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
325   jvmtiCapabilities caps;
326   jvmtiError err;
327   jint res;
328 
329   res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
330   if (res != JNI_OK || jvmti == NULL) {
331     LOG("Wrong result of a valid call to GetEnv!\n");
332     return JNI_ERR;
333   }
334 
335 
336   memset(&caps, 0, sizeof(jvmtiCapabilities));
337   caps.can_generate_field_modification_events = 1;
338   caps.can_support_virtual_threads = 1;
339 
340   err = jvmti->AddCapabilities(&caps);
341   if (err != JVMTI_ERROR_NONE) {
342     LOG("(AddCapabilities) unexpected error: %s (%d)\n",
343            TranslateError(err), err);
344     return JNI_ERR;
345   }
346 
347   err = jvmti->GetCapabilities(&caps);
348   if (err != JVMTI_ERROR_NONE) {
349     LOG("(GetCapabilities) unexpected error: %s (%d)\n",
350            TranslateError(err), err);
351     return JNI_ERR;
352   }
353 
354   if (caps.can_generate_field_modification_events) {
355     callbacks.FieldModification = &FieldModification;
356     err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
357     if (err != JVMTI_ERROR_NONE) {
358       LOG("(SetEventCallbacks) unexpected error: %s (%d)\n",
359              TranslateError(err), err);
360       return JNI_ERR;
361     }
362 
363     err = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
364                                           JVMTI_EVENT_FIELD_MODIFICATION, NULL);
365     if (err != JVMTI_ERROR_NONE) {
366       LOG("Failed to enable JVMTI_EVENT_FIELD_MODIFICATION: %s (%d)\n",
367              TranslateError(err), err);
368       return JNI_ERR;
369     }
370   } else {
371     LOG("Warning: FieldModification watch is not implemented\n");
372   }
373 
374   return JNI_OK;
375 }
376 
377 JNIEXPORT void JNICALL
378 Java_fieldmod02_getReady(JNIEnv *jni, jclass clz) {
379   jvmtiError err;
380   jclass cls;
381   jmethodID ctor;
382   jintArray arr1, arr2;
383   jobject obj1, obj2;
384   size_t i;
385   jthread thread;
386 
387   err = jvmti->GetCurrentThread(&thread);
388   if (err != JVMTI_ERROR_NONE) {
389     LOG("Failed to get current thread: %s (%d)\n", TranslateError(err), err);
390     result = STATUS_FAILED;
391     return;
392   }
393 
394   eventsCount = 0;
395   eventsExpected = 0;
396   isVirtualExpected = jni->IsVirtualThread(thread);
397 
398   LOG(">>> setting field modification watches ...\n");
399 
400   cls = jni->FindClass("fieldmod02a");
401   if (cls == NULL) {
402     LOG("Cannot find fieldmod02a class!\n");
403     result = STATUS_FAILED;
404     return;
405   }
406   for (i = 0; i < sizeof(watches)/sizeof(watch_info); i++) {
407     if (watches[i].is_static == JNI_TRUE) {
408       watches[i].fid = jni->GetStaticFieldID(
409           cls, watches[i].f_name, watches[i].f_sig);
410     } else {
411       watches[i].fid = jni->GetFieldID(
412           cls, watches[i].f_name, watches[i].f_sig);
413     }
414     if (watches[i].fid == NULL) {
415       LOG("Cannot get field ID for \"%s:%s\"\n",
416              watches[i].f_name, watches[i].f_sig);
417       result = STATUS_FAILED;
418       return;
419     }
420     err = jvmti->SetFieldModificationWatch(cls, watches[i].fid);
421     if (err == JVMTI_ERROR_NONE) {
422       eventsExpected++;
423     } else {
424       LOG("(SetFieldModificationWatch#%" PRIuPTR ") unexpected error: %s (%d)\n",
425              i, TranslateError(err), err);
426       result = STATUS_FAILED;
427     }
428   }
429 
430   ctor = jni->GetMethodID(cls, "<init>", "()V");
431   obj1 = jni->NewGlobalRef(jni->NewObject(cls, ctor));
432   obj2 = jni->NewGlobalRef(jni->NewObject(cls, ctor));
433   arr1 = (jintArray) jni->NewGlobalRef(jni->NewIntArray((jsize) 1));
434   arr2 = (jintArray) jni->NewGlobalRef(jni->NewIntArray((jsize) 1));
435 
436   watches[0].val.z = JNI_TRUE;
437   watches[1].val.b = 1;
438   watches[2].val.s = 2;
439   watches[3].val.i = 3;
440   watches[4].val.j = 4;
441   watches[5].val.f = 0.5F;
442   watches[6].val.d = 0.6;
443   watches[7].val.c = 0x7;
444   watches[8].val.l = obj1;
445   watches[9].val.l = arr1;
446 
447   watches[10].val.z = JNI_FALSE;
448   watches[11].val.b = 10;
449   watches[12].val.s = 20;
450   watches[13].val.i = 30;
451   watches[14].val.j = 40;
452   watches[15].val.f = 0.05F;
453   watches[16].val.d = 0.06;
454   watches[17].val.c = 0x70;
455   watches[18].val.l = obj2;
456   watches[19].val.l = arr2;
457 
458   LOG(">>> ... done\n");
459 
460 }
461 
462 JNIEXPORT jint JNICALL
463 Java_fieldmod02_check(JNIEnv *jni, jclass clz, jobject obj) {
464   jclass cls;
465 
466   LOG(">>> modifying fields ...\n");
467 
468 
469   cls = jni->FindClass("fieldmod02a");
470   if (cls == NULL) {
471     LOG("Cannot find fieldmod02a class!\n");
472     return STATUS_FAILED;
473   }
474 
475   jni->SetStaticBooleanField(cls, watches[0].fid, watches[0].val.z);
476   jni->SetStaticByteField(cls, watches[1].fid, watches[1].val.b);
477   jni->SetStaticShortField(cls, watches[2].fid, watches[2].val.s);
478   jni->SetStaticIntField(cls, watches[3].fid, watches[3].val.i);
479   jni->SetStaticLongField(cls, watches[4].fid, watches[4].val.j);
480   jni->SetStaticFloatField(cls, watches[5].fid, watches[5].val.f);
481   jni->SetStaticDoubleField(cls, watches[6].fid, watches[6].val.d);
482   jni->SetStaticCharField(cls, watches[7].fid, watches[7].val.c);
483   jni->SetStaticObjectField(cls, watches[8].fid, watches[8].val.l);
484   jni->SetStaticObjectField(cls, watches[9].fid, watches[9].val.l);
485 
486   jni->SetBooleanField(obj, watches[10].fid, watches[10].val.z);
487   jni->SetByteField(obj, watches[11].fid, watches[11].val.b);
488   jni->SetShortField(obj, watches[12].fid, watches[12].val.s);
489   jni->SetIntField(obj, watches[13].fid, watches[13].val.i);
490   jni->SetLongField(obj, watches[14].fid, watches[14].val.j);
491   jni->SetFloatField(obj, watches[15].fid, watches[15].val.f);
492   jni->SetDoubleField(obj, watches[16].fid, watches[16].val.d);
493   jni->SetCharField(obj, watches[17].fid, watches[17].val.c);
494   jni->SetObjectField(obj, watches[18].fid, watches[18].val.l);
495   jni->SetObjectField(obj, watches[19].fid, watches[19].val.l);
496 
497   LOG(">>> ... done\n");
498 
499   if (eventsCount != eventsExpected) {
500     LOG("Wrong number of field modification events: %d, expected: %d\n",
501            eventsCount, eventsExpected);
502     result = STATUS_FAILED;
503   }
504 
505   for (size_t i = 0; i < sizeof(watches)/sizeof(watch_info); i++) {
506     jvmtiError err = jvmti->ClearFieldModificationWatch(cls, watches[i].fid);
507     if (err == JVMTI_ERROR_NONE) {
508       eventsExpected++;
509     } else {
510       LOG("(ClearFieldModificationWatch#%" PRIuPTR ") unexpected error: %s (%d)\n",
511              i, TranslateError(err), err);
512       result = STATUS_FAILED;
513     }
514   }
515   return result;
516 }
517 
518 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
519   return Agent_Initialize(jvm, options, reserved);
520 }
521 
522 JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
523   return Agent_Initialize(jvm, options, reserved);
524 }
525 
526 }