New test/hotspot/jtreg/serviceability/jvmti/FieldAccessWatch/libFieldAccessWatch.c (original) (raw)

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