1 /*
2  * Copyright (c) 2018, 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 <stdlib.h>
27 #include "jvmti.h"
28 #include "jni.h"
29 
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 
35 static jvmtiEnv *jvmti = NULL;
36 
37 // valid while a test is executed
38 static jobject testResultObject = NULL;
39 static jclass testResultClass = NULL;
40 
41 
reportError(const char * msg,int err)42 static void reportError(const char *msg, int err) {
43     printf("%s, error: %d\n", msg, err);
44 }
45 
46 
47 // logs the notification and updates currentTestResult
handleNotification(JNIEnv * jni_env,jmethodID method,jfieldID field,jclass field_klass,int modified,jlocation location)48 static void handleNotification(JNIEnv *jni_env,
49     jmethodID method,
50     jfieldID field,
51     jclass field_klass,
52     int modified,
53     jlocation location)
54 {
55     jvmtiError err;
56     char *name = NULL;
57     char *mname = NULL;
58     char *mgensig = NULL;
59     jclass methodClass = NULL;
60     char *csig = NULL;
61 
62     if (testResultObject == NULL) {
63         // we are out of test
64         return;
65     }
66 
67     err = (*jvmti)->GetFieldName(jvmti, field_klass, field, &name, NULL, NULL);
68     if (err != JVMTI_ERROR_NONE) {
69         reportError("GetFieldName failed", err);
70         return;
71     }
72 
73     err = (*jvmti)->GetMethodName(jvmti, method, &mname, NULL, &mgensig);
74     if (err != JVMTI_ERROR_NONE) {
75         reportError("GetMethodName failed", err);
76         return;
77     }
78 
79     err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &methodClass);
80     if (err != JVMTI_ERROR_NONE) {
81         reportError("GetMethodDeclaringClass failed", err);
82         return;
83     }
84 
85     err = (*jvmti)->GetClassSignature(jvmti, methodClass, &csig, NULL);
86     if (err != JVMTI_ERROR_NONE) {
87         reportError("GetClassSignature failed", err);
88         return;
89     }
90 
91     printf("\"class: %s method: %s%s\" %s field: \"%s\", location: %d\n",
92         csig, mname, mgensig, modified ? "modified" : "accessed", name, (int)location);
93 
94     // set TestResult
95     if (testResultObject != NULL && testResultClass != NULL) {
96         jfieldID fieldID;
97         // field names in TestResult are "<field_name>_access"/"<field_name>_modify"
98         char *fieldName = (char *)malloc(strlen(name) + 16);
99         strcpy(fieldName, name);
100         strcat(fieldName, modified ? "_modify" : "_access");
101 
102         fieldID = (*jni_env)->GetFieldID(jni_env, testResultClass, fieldName, "Z");
103         if (fieldID != NULL) {
104             (*jni_env)->SetBooleanField(jni_env, testResultObject, fieldID, JNI_TRUE);
105         } else {
106             // the field is not interesting for the test
107         }
108         // clear any possible exception
109         (*jni_env)->ExceptionClear(jni_env);
110 
111         free(fieldName);
112     }
113 
114     (*jvmti)->Deallocate(jvmti, (unsigned char*)csig);
115     (*jvmti)->Deallocate(jvmti, (unsigned char*)mname);
116     (*jvmti)->Deallocate(jvmti, (unsigned char*)mgensig);
117     (*jvmti)->Deallocate(jvmti, (unsigned char*)name);
118 }
119 
120 // recursively sets access and modification watchers for all
121 // fields of the object specified.
setWatchers(JNIEnv * jni_env,const jobject obj)122 void setWatchers(JNIEnv *jni_env, const jobject obj)
123 {
124     jclass klass;
125 
126     if (obj == NULL) {
127         return;
128     }
129 
130     klass = (*jni_env)->GetObjectClass(jni_env, obj);
131     do {
132         jfieldID* klassFields = NULL;
133         jint fieldCount = 0;
134         int i;
135         jvmtiError err = (*jvmti)->GetClassFields(jvmti, klass, &fieldCount, &klassFields);
136         if (err != JVMTI_ERROR_NONE) {
137             reportError("Failed to get class fields", err);
138             return;
139         }
140 
141         for (i = 0; i < fieldCount; ++i) {
142             char *sig = NULL;
143             err = (*jvmti)->SetFieldModificationWatch(jvmti, klass, klassFields[i]);
144             if (err != JVMTI_ERROR_NONE && err != JVMTI_ERROR_DUPLICATE) {
145                 reportError("Failed to set field modification", err);
146                 return;
147             }
148 
149             err = (*jvmti)->SetFieldAccessWatch(jvmti, klass, klassFields[i]);
150             if (err != JVMTI_ERROR_NONE && err != JVMTI_ERROR_DUPLICATE) {
151                 reportError("Failed to set field access", err);
152                 return;
153             }
154 
155             err = (*jvmti)->GetFieldName(jvmti, klass, klassFields[i], NULL, &sig, NULL);
156             if (sig) {
157                 if (sig[0] == 'L') {
158                     jobject fieldVal = (*jni_env)->GetObjectField(jni_env, obj, klassFields[i]);
159                     setWatchers(jni_env, fieldVal);
160                 }
161                 (*jvmti)->Deallocate(jvmti, (unsigned char*)sig);
162             }
163         }
164 
165         (*jvmti)->Deallocate(jvmti, (unsigned char*)klassFields);
166 
167         klass = (*jni_env)->GetSuperclass(jni_env, klass);
168     } while (klass != NULL);
169 }
170 
171 
172 static void JNICALL
onFieldAccess(jvmtiEnv * jvmti_env,JNIEnv * jni_env,jthread thread,jmethodID method,jlocation location,jclass field_klass,jobject object,jfieldID field)173 onFieldAccess(jvmtiEnv *jvmti_env,
174             JNIEnv* jni_env,
175             jthread thread,
176             jmethodID method,
177             jlocation location,
178             jclass field_klass,
179             jobject object,
180             jfieldID field)
181 {
182     handleNotification(jni_env, method, field, field_klass, 0, location);
183 }
184 
185 
186 static void JNICALL
onFieldModification(jvmtiEnv * jvmti_env,JNIEnv * jni_env,jthread thread,jmethodID method,jlocation location,jclass field_klass,jobject object,jfieldID field,char signature_type,jvalue new_value)187 onFieldModification(jvmtiEnv *jvmti_env,
188             JNIEnv* jni_env,
189             jthread thread,
190             jmethodID method,
191             jlocation location,
192             jclass field_klass,
193             jobject object,
194             jfieldID field,
195             char signature_type,
196             jvalue new_value)
197 {
198     handleNotification(jni_env, method, field, field_klass, 1, location);
199 
200     if (signature_type == 'L') {
201         jobject newObject = new_value.l;
202         setWatchers(jni_env, newObject);
203     }
204 }
205 
206 
207 JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM * jvm,char * options,void * reserved)208 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
209 {
210     jvmtiError err;
211     jvmtiCapabilities caps = {0};
212     jvmtiEventCallbacks callbacks = {0};
213     jint res = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_1);
214     if (res != JNI_OK || jvmti == NULL) {
215         reportError("GetEnv failed", res);
216         return JNI_ERR;
217     }
218 
219     caps.can_generate_field_modification_events = 1;
220     caps.can_generate_field_access_events = 1;
221     caps.can_tag_objects = 1;
222     err = (*jvmti)->AddCapabilities(jvmti, &caps);
223     if (err != JVMTI_ERROR_NONE) {
224         reportError("Failed to set capabilities", err);
225         return JNI_ERR;
226     }
227 
228     callbacks.FieldModification = &onFieldModification;
229     callbacks.FieldAccess = &onFieldAccess;
230 
231     err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
232     if (err != JVMTI_ERROR_NONE) {
233         reportError("Failed to set event callbacks", err);
234         return JNI_ERR;
235     }
236 
237     err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIELD_ACCESS, NULL);
238     if (err != JVMTI_ERROR_NONE) {
239         reportError("Failed to set access notifications", err);
240         return JNI_ERR;
241     }
242 
243     err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIELD_MODIFICATION, NULL);
244     if (err != JVMTI_ERROR_NONE) {
245         reportError("Failed to set modification notifications", err);
246         return JNI_ERR;
247     }
248     setbuf(stdout, NULL);
249     return JNI_OK;
250 }
251 
252 
253 JNIEXPORT jboolean JNICALL
Java_FieldAccessWatch_initWatchers(JNIEnv * env,jclass thisClass,jclass cls,jobject field)254 Java_FieldAccessWatch_initWatchers(JNIEnv *env, jclass thisClass, jclass cls, jobject field)
255 {
256     jfieldID fieldId;
257     jvmtiError err;
258 
259     if (jvmti == NULL) {
260         reportError("jvmti is NULL", 0);
261         return JNI_FALSE;
262     }
263 
264     fieldId = (*env)->FromReflectedField(env, field);
265 
266     err = (*jvmti)->SetFieldModificationWatch(jvmti, cls, fieldId);
267     if (err != JVMTI_ERROR_NONE) {
268         reportError("SetFieldModificationWatch failed", err);
269         return JNI_FALSE;
270     }
271 
272     err = (*jvmti)->SetFieldAccessWatch(jvmti, cls, fieldId);
273     if (err != JVMTI_ERROR_NONE) {
274         reportError("SetFieldAccessWatch failed", err);
275         return JNI_FALSE;
276     }
277 
278     return JNI_TRUE;
279 }
280 
281 
282 JNIEXPORT jboolean JNICALL
Java_FieldAccessWatch_startTest(JNIEnv * env,jclass thisClass,jobject testResults)283 Java_FieldAccessWatch_startTest(JNIEnv *env, jclass thisClass, jobject testResults)
284 {
285     testResultObject = (*env)->NewGlobalRef(env, testResults);
286     testResultClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, testResultObject));
287 
288     return JNI_TRUE;
289 }
290 
291 JNIEXPORT void JNICALL
Java_FieldAccessWatch_stopTest(JNIEnv * env,jclass thisClass)292 Java_FieldAccessWatch_stopTest(JNIEnv *env, jclass thisClass)
293 {
294     if (testResultObject != NULL) {
295         (*env)->DeleteGlobalRef(env, testResultObject);
296         testResultObject = NULL;
297     }
298     if (testResultClass != NULL) {
299         (*env)->DeleteGlobalRef(env, testResultClass);
300         testResultClass = NULL;
301     }
302 }
303 
304 
305 #ifdef __cplusplus
306 }
307 #endif
308 
309