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