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 } writable_watch_info;
 47 
 48 typedef struct {
 49   jfieldID fid;
 50   const char *m_cls;
 51   const char *m_name;
 52   const char *m_sig;
 53   jlocation loc;
 54   const char *f_cls;
 55   const char *f_name;
 56   const char *f_sig;
 57   jboolean is_static;
 58 } watch_info;
 59 
 60 static jvmtiEnv *jvmti;
 61 static jvmtiEventCallbacks callbacks;
 62 static jvmtiCapabilities caps;
 63 static jint result = PASSED;
 64 static volatile jboolean isVirtualExpected = JNI_FALSE;
 65 static int eventsExpected = 0;
 66 static int eventsCount = 0;
 67 static watch_info watches[] = {
 68     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 69         "Lfieldacc02a;", "staticBoolean", "Z", JNI_TRUE },
 70     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 71         "Lfieldacc02a;", "staticByte", "B", JNI_TRUE },
 72     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 73         "Lfieldacc02a;", "staticShort", "S", JNI_TRUE },
 74     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 75         "Lfieldacc02a;", "staticInt", "I", JNI_TRUE },
 76     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 77         "Lfieldacc02a;", "staticLong", "J", JNI_TRUE },
 78     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 79         "Lfieldacc02a;", "staticFloat", "F", JNI_TRUE },
 80     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 81         "Lfieldacc02a;", "staticDouble", "D", JNI_TRUE },
 82     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 83         "Lfieldacc02a;", "staticChar", "C", JNI_TRUE },
 84     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 85         "Lfieldacc02a;", "staticObject", "Ljava/lang/Object;", JNI_TRUE },
 86     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 87         "Lfieldacc02a;", "staticArrInt", "[I", JNI_TRUE },
 88 
 89     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 90         "Lfieldacc02a;", "instanceBoolean", "Z", JNI_FALSE },
 91     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 92         "Lfieldacc02a;", "instanceByte", "B", JNI_FALSE },
 93     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 94         "Lfieldacc02a;", "instanceShort", "S", JNI_FALSE },
 95     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 96         "Lfieldacc02a;", "instanceInt", "I", JNI_FALSE },
 97     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
 98         "Lfieldacc02a;", "instanceLong", "J", JNI_FALSE },
 99     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
100         "Lfieldacc02a;", "instanceFloat", "F", JNI_FALSE },
101     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
102         "Lfieldacc02a;", "instanceDouble", "D", JNI_FALSE },
103     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
104         "Lfieldacc02a;", "instanceChar", "C", JNI_FALSE },
105     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
106         "Lfieldacc02a;", "instanceObject", "Ljava/lang/Object;", JNI_FALSE },
107     { NULL, "Lfieldacc02;", "check", "(Ljava/lang/Object;)I", 0,
108         "Lfieldacc02a;", "instanceArrInt", "[I", JNI_FALSE }
109 };
110 
111 void JNICALL FieldAccess(jvmtiEnv *jvmti, JNIEnv *jni,
112                          jthread thr, jmethodID method,
113                          jlocation location, jclass field_klass, jobject obj, jfieldID field) {
114   jvmtiError err;
115   jclass cls;
116   writable_watch_info watch;
117   char *generic;
118   size_t i;
119 
120   eventsCount++;
121   LOG(">>> retrieving access watch info ...\n");
122 
123   watch.fid = field;
124   watch.loc = location;
125   watch.is_static = (obj == NULL) ? JNI_TRUE : JNI_FALSE;
126   err = jvmti->GetMethodDeclaringClass(method, &cls);
127   if (err != JVMTI_ERROR_NONE) {
128     LOG("(GetMethodDeclaringClass) unexpected error: %s (%d)\n",
129            TranslateError(err), err);
130     result = STATUS_FAILED;
131     return;
132   }
133   err = jvmti->GetClassSignature(cls,
134                                      &watch.m_cls, &generic);
135   if (err != JVMTI_ERROR_NONE) {
136     LOG("(GetClassSignature) unexpected error: %s (%d)\n",
137            TranslateError(err), err);
138     result = STATUS_FAILED;
139     return;
140   }
141   err = jvmti->GetMethodName(method,
142                                  &watch.m_name, &watch.m_sig, &generic);
143   if (err != JVMTI_ERROR_NONE) {
144     LOG("(GetMethodName) unexpected error: %s (%d)\n",
145            TranslateError(err), err);
146     result = STATUS_FAILED;
147     return;
148   }
149   err = jvmti->GetClassSignature(field_klass,
150                                      &watch.f_cls, &generic);
151   if (err != JVMTI_ERROR_NONE) {
152     LOG("(GetClassSignature) unexpected error: %s (%d)\n",
153            TranslateError(err), err);
154     result = STATUS_FAILED;
155     return;
156   }
157   err = jvmti->GetFieldName(field_klass, field,
158                                 &watch.f_name, &watch.f_sig, &generic);
159   if (err != JVMTI_ERROR_NONE) {
160     LOG("(GetFieldName) unexpected error: %s (%d)\n",
161            TranslateError(err), err);
162     result = STATUS_FAILED;
163     return;
164   }
165 
166   LOG(">>>      class: \"%s\"\n", watch.m_cls);
167   LOG(">>>     method: \"%s%s\"\n", watch.m_name, watch.m_sig);
168   LOG(">>>   location: 0x%x%08x\n",
169          (jint)(watch.loc >> 32), (jint)watch.loc);
170   LOG(">>>  field cls: \"%s\"\n", watch.f_cls);
171   LOG(">>>      field: \"%s:%s\"\n", watch.f_name, watch.f_sig);
172   LOG(">>>     object: 0x%p\n", obj);
173 
174   for (i = 0; i < sizeof(watches)/sizeof(watch_info); i++) {
175     if (watch.fid == watches[i].fid) {
176       if (watch.m_cls == NULL ||
177           strcmp(watch.m_cls, watches[i].m_cls) != 0) {
178         LOG("(watch#%" PRIuPTR ") wrong class: \"%s\", expected: \"%s\"\n",
179                i, watch.m_cls, watches[i].m_cls);
180         result = STATUS_FAILED;
181       }
182       if (watch.m_name == NULL ||
183           strcmp(watch.m_name, watches[i].m_name) != 0) {
184         LOG("(watch#%" PRIuPTR ") wrong method name: \"%s\"",
185                i, watch.m_name);
186         LOG(", expected: \"%s\"\n", watches[i].m_name);
187         result = STATUS_FAILED;
188       }
189       if (watch.m_sig == NULL ||
190           strcmp(watch.m_sig, watches[i].m_sig) != 0) {
191         LOG("(watch#%" PRIuPTR ") wrong method sig: \"%s\"",
192                i, watch.m_sig);
193         LOG(", expected: \"%s\"\n", watches[i].m_sig);
194         result = STATUS_FAILED;
195       }
196       if (watch.loc != watches[i].loc) {
197         LOG("(watch#%" PRIuPTR ") wrong location: 0x%x%08x",
198                i, (jint)(watch.loc >> 32), (jint)watch.loc);
199         LOG(", expected: 0x%x%08x\n",
200                (jint)(watches[i].loc >> 32), (jint)watches[i].loc);
201         result = STATUS_FAILED;
202       }
203       if (watch.f_name == NULL ||
204           strcmp(watch.f_name, watches[i].f_name) != 0) {
205         LOG("(watch#%" PRIuPTR ") wrong field name: \"%s\"",
206                i, watch.f_name);
207         LOG(", expected: \"%s\"\n", watches[i].f_name);
208         result = STATUS_FAILED;
209       }
210       if (watch.f_sig == NULL ||
211           strcmp(watch.f_sig, watches[i].f_sig) != 0) {
212         LOG("(watch#%" PRIuPTR ") wrong field sig: \"%s\"",
213                i, watch.f_sig);
214         LOG(", expected: \"%s\"\n", watches[i].f_sig);
215         result = STATUS_FAILED;
216       }
217       if (watch.is_static != watches[i].is_static) {
218         LOG("(watch#%" PRIuPTR ") wrong field type: %s", i,
219                (watch.is_static == JNI_TRUE) ? "static" : "instance");
220         LOG(", expected: %s\n",
221                (watches[i].is_static == JNI_TRUE) ? "static" : "instance");
222         result = STATUS_FAILED;
223       }
224       jboolean isVirtual = jni->IsVirtualThread(thr);
225       if (isVirtualExpected != isVirtual) {
226         LOG("The thread IsVirtualThread %d differs from expected %d.\n", isVirtual, isVirtualExpected);
227         result = STATUS_FAILED;
228       }
229       return;
230     }
231   }
232   LOG("Unexpected field access catched: 0x%p\n", watch.fid);
233   result = STATUS_FAILED;
234 }
235 
236 #ifdef STATIC_BUILD
237 JNIEXPORT jint JNICALL Agent_OnLoad_fieldacc02(JavaVM *jvm, char *options, void *reserved) {
238     return Agent_Initialize(jvm, options, reserved);
239 }
240 JNIEXPORT jint JNICALL Agent_OnAttach_fieldacc02(JavaVM *jvm, char *options, void *reserved) {
241     return Agent_Initialize(jvm, options, reserved);
242 }
243 JNIEXPORT jint JNI_OnLoad_fieldacc02(JavaVM *jvm, char *options, void *reserved) {
244     return JNI_VERSION_1_8;
245 }
246 #endif
247 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
248   jvmtiCapabilities caps;
249   jvmtiError err;
250   jint res;
251 
252   res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
253   if (res != JNI_OK || jvmti == NULL) {
254     LOG("Wrong result of a valid call to GetEnv!\n");
255     return JNI_ERR;
256   }
257 
258   memset(&caps, 0, sizeof(jvmtiCapabilities));
259   caps.can_generate_field_access_events = 1;
260   caps.can_support_virtual_threads = 1;
261 
262   err = jvmti->AddCapabilities(&caps);
263   if (err != JVMTI_ERROR_NONE) {
264     LOG("(AddCapabilities) unexpected error: %s (%d)\n",
265            TranslateError(err), err);
266     return JNI_ERR;
267   }
268 
269   err = jvmti->GetCapabilities(&caps);
270   if (err != JVMTI_ERROR_NONE) {
271     LOG("(GetCapabilities) unexpected error: %s (%d)\n",
272            TranslateError(err), err);
273     return JNI_ERR;
274   }
275 
276   if (caps.can_generate_field_access_events) {
277     callbacks.FieldAccess = &FieldAccess;
278     err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
279     if (err != JVMTI_ERROR_NONE) {
280       LOG("(SetEventCallbacks) unexpected error: %s (%d)\n",
281              TranslateError(err), err);
282       return JNI_ERR;
283     }
284 
285     err = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
286                                           JVMTI_EVENT_FIELD_ACCESS, NULL);
287     if (err != JVMTI_ERROR_NONE) {
288       LOG("Failed to enable JVMTI_EVENT_FIELD_ACCESS: %s (%d)\n",
289              TranslateError(err), err);
290       return JNI_ERR;
291     }
292   } else {
293     LOG("Warning: FieldAccess watch is not implemented\n");
294   }
295 
296   return JNI_OK;
297 }
298 
299 JNIEXPORT void JNICALL Java_fieldacc02_getReady(JNIEnv *jni, jclass clz) {
300   jvmtiError err;
301   jclass cls;
302   size_t i;
303   jthread thread;
304 
305   LOG(">>> setting field access watches ...\n");
306 
307   cls = jni->FindClass("fieldacc02a");
308   if (cls == NULL) {
309     LOG("Cannot find fieldacc02a class!\n");
310     result = STATUS_FAILED;
311     return;
312   }
313 
314   err = jvmti->GetCurrentThread(&thread);
315   if (err != JVMTI_ERROR_NONE) {
316     LOG("Failed to get current thread: %s (%d)\n", TranslateError(err), err);
317     result = STATUS_FAILED;
318     return;
319   }
320 
321   eventsCount = 0;
322   eventsExpected = 0;
323   isVirtualExpected = jni->IsVirtualThread(thread);
324 
325   for (i = 0; i < sizeof(watches)/sizeof(watch_info); i++) {
326     if (watches[i].is_static == JNI_TRUE) {
327       watches[i].fid = jni->GetStaticFieldID(
328           cls, watches[i].f_name, watches[i].f_sig);
329     } else {
330       watches[i].fid = jni->GetFieldID(
331           cls, watches[i].f_name, watches[i].f_sig);
332     }
333     if (watches[i].fid == NULL) {
334       LOG("Cannot find field \"%s\"!\n", watches[i].f_name);
335       result = STATUS_FAILED;
336       return;
337     }
338     err = jvmti->SetFieldAccessWatch(cls, watches[i].fid);
339     if (err == JVMTI_ERROR_NONE) {
340       eventsExpected++;
341     } else {
342       LOG("(SetFieldAccessWatch#%" PRIuPTR ") unexpected error: %s (%d)\n",
343              i, TranslateError(err), err);
344       result = STATUS_FAILED;
345     }
346   }
347 
348   LOG(">>> ... done\n");
349 
350 }
351 
352 JNIEXPORT jint JNICALL
353 Java_fieldacc02_check(JNIEnv *jni, jclass clz, jobject obj) {
354   jclass cls;
355 
356   LOG(">>> accessing fields ...\n");
357 
358   cls = jni->FindClass("fieldacc02a");
359   if (cls == NULL) {
360     LOG("Cannot find fieldacc02a class!\n");
361     return STATUS_FAILED;
362   }
363 
364   jni->GetStaticBooleanField(cls, watches[0].fid);
365   jni->GetStaticByteField(cls, watches[1].fid);
366   jni->GetStaticShortField(cls, watches[2].fid);
367   jni->GetStaticIntField(cls, watches[3].fid);
368   jni->GetStaticLongField(cls, watches[4].fid);
369   jni->GetStaticFloatField(cls, watches[5].fid);
370   jni->GetStaticDoubleField(cls, watches[6].fid);
371   jni->GetStaticCharField(cls, watches[7].fid);
372   jni->GetStaticObjectField(cls, watches[8].fid);
373   jni->GetStaticObjectField(cls, watches[9].fid);
374 
375   jni->GetBooleanField(obj, watches[10].fid);
376   jni->GetByteField(obj, watches[11].fid);
377   jni->GetShortField(obj, watches[12].fid);
378   jni->GetIntField(obj, watches[13].fid);
379   jni->GetLongField(obj, watches[14].fid);
380   jni->GetFloatField(obj, watches[15].fid);
381   jni->GetDoubleField(obj, watches[16].fid);
382   jni->GetCharField(obj, watches[17].fid);
383   jni->GetObjectField(obj, watches[18].fid);
384   jni->GetObjectField(obj, watches[19].fid);
385 
386   LOG(">>> ... done\n");
387 
388   if (eventsCount != eventsExpected) {
389     LOG("Wrong number of field access events: %d, expected: %d\n",
390            eventsCount, eventsExpected);
391     result = STATUS_FAILED;
392   }
393 
394   for (size_t i = 0; i < sizeof(watches)/sizeof(watch_info); i++) {
395     jvmtiError err = jvmti->ClearFieldAccessWatch(cls, watches[i].fid);
396     if (err == JVMTI_ERROR_NONE) {
397       eventsExpected++;
398     } else {
399       LOG("(ClearFieldAccessWatch#%" PRIuPTR ") unexpected error: %s (%d)\n",
400              i, TranslateError(err), err);
401       result = STATUS_FAILED;
402     }
403   }
404   return result;
405 }
406 
407 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
408   return Agent_Initialize(jvm, options, reserved);
409 }
410 
411 JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
412   return Agent_Initialize(jvm, options, reserved);
413 }
414 
415 }