1 /* 2 * Copyright (c) 2021, 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 = nullptr; 36 37 // valid while a test is executed 38 static jobject testResultObject = nullptr; 39 static jclass testResultClass = nullptr; 40 // we log object values handling FieldModification event and this cause FieldAccess events are triggered. 41 // The flag to disable FieldAccess handling. 42 static bool disableAccessEvent = false; 43 44 static void reportError(const char *msg, int err) { 45 printf("%s, error: %d\n", msg, err); 46 } 47 48 static void printJValue(const char *prefix, JNIEnv *jni_env, char signature_type, jvalue value) { 49 // print new_value to ensure the value is valid 50 // use String.valueOf(...) to get string representation 51 /* 52 Z boolean 53 B byte 54 C char 55 S short 56 I int 57 J long 58 F float 59 D double 60 L fully-qualified-class ; fully-qualified-class 61 [ type type[] 62 */ 63 char signature[64] = {}; 64 if (signature_type == 'Q' || signature_type == 'L') { 65 snprintf(signature, sizeof(signature), "(Ljava/lang/Object;)Ljava/lang/String;"); 66 } else { 67 snprintf(signature, sizeof(signature), "(%c)Ljava/lang/String;", signature_type); 68 } 69 70 jclass clsString = jni_env->FindClass("java/lang/String"); 71 jmethodID mid = jni_env->GetStaticMethodID(clsString, "valueOf", signature); 72 jstring objJStr = (jstring)jni_env->CallStaticObjectMethodA(clsString, mid, &value); 73 74 const char* objStr = "UNKNOWN"; 75 if (objJStr != nullptr) { 76 objStr = jni_env->GetStringUTFChars(objJStr, nullptr); 77 } 78 79 printf(" %s is: '%s'\n", prefix, objStr); 80 fflush(0); 81 82 if (objJStr != nullptr) { 83 jni_env->ReleaseStringUTFChars(objJStr, objStr); 84 } 85 } 86 87 88 // logs the notification and updates currentTestResult 89 static void handleNotification(jvmtiEnv *jvmti, JNIEnv *jni_env, 90 jmethodID method, 91 jobject object, 92 jfieldID field, 93 jclass field_klass, 94 bool modified, 95 jlocation location) 96 { 97 jvmtiError err; 98 char *name = nullptr; 99 char *signature = nullptr; 100 char *mname = nullptr; 101 char *mgensig = nullptr; 102 jclass methodClass = nullptr; 103 char *csig = nullptr; 104 105 if (testResultObject == nullptr) { 106 // we are out of test 107 return; 108 } 109 110 err = jvmti->GetFieldName(field_klass, field, &name, &signature, nullptr); 111 if (err != JVMTI_ERROR_NONE) { 112 reportError("GetFieldName failed", err); 113 return; 114 } 115 116 err = jvmti->GetMethodName(method, &mname, nullptr, &mgensig); 117 if (err != JVMTI_ERROR_NONE) { 118 reportError("GetMethodName failed", err); 119 return; 120 } 121 122 err = jvmti->GetMethodDeclaringClass(method, &methodClass); 123 if (err != JVMTI_ERROR_NONE) { 124 reportError("GetMethodDeclaringClass failed", err); 125 return; 126 } 127 128 err = jvmti->GetClassSignature(methodClass, &csig, nullptr); 129 if (err != JVMTI_ERROR_NONE) { 130 reportError("GetClassSignature failed", err); 131 return; 132 } 133 134 printf(" \"class: %s method: %s%s\" %s field: \"%s\" (type '%s'), location: %d\n", 135 csig, mname, mgensig, modified ? "modified" : "accessed", name, signature, (int)location); 136 137 // For FieldModification event print current value. 138 // Note: this will cause FieldAccess event. 139 if (modified) { 140 jvalue curValue = {}; 141 switch (signature[0]) { 142 case 'L': 143 case 'Q': 144 curValue.l = jni_env->GetObjectField(object, field); break; 145 case 'Z': // boolean 146 curValue.z = jni_env->GetBooleanField(object, field); break; 147 case 'B': // byte 148 curValue.b = jni_env->GetByteField(object, field); break; 149 case 'C': // char 150 curValue.c = jni_env->GetCharField(object, field); break; 151 case 'S': // short 152 curValue.s = jni_env->GetShortField(object, field); break; 153 case 'I': // int 154 curValue.i = jni_env->GetIntField(object, field); break; 155 case 'J': // long 156 curValue.j = jni_env->GetLongField(object, field); break; 157 case 'F': // float 158 curValue.f = jni_env->GetFloatField(object, field); break; 159 case 'D': // double 160 curValue.d = jni_env->GetDoubleField(object, field); break; 161 default: 162 printf("ERROR: unexpected signature: %s\n", signature); 163 return; 164 } 165 printJValue("current value: ", jni_env, signature[0], curValue); 166 } 167 168 // set TestResult 169 if (testResultObject != nullptr && testResultClass != nullptr) { 170 jfieldID fieldID; 171 // field names in TestResult are "<field_name>_access"/"<field_name>_modify" 172 char *fieldName = (char *)malloc(strlen(name) + 16); 173 strcpy(fieldName, name); 174 strcat(fieldName, modified ? "_modify" : "_access"); 175 176 fieldID = jni_env->GetFieldID(testResultClass, fieldName, "Z"); 177 if (fieldID != nullptr) { 178 jni_env->SetBooleanField(testResultObject, fieldID, JNI_TRUE); 179 } else { 180 // the field is not interesting for the test 181 } 182 // clear any possible exception 183 jni_env->ExceptionClear(); 184 185 free(fieldName); 186 } 187 188 jvmti->Deallocate((unsigned char*)csig); 189 jvmti->Deallocate((unsigned char*)mname); 190 jvmti->Deallocate((unsigned char*)mgensig); 191 jvmti->Deallocate((unsigned char*)name); 192 jvmti->Deallocate((unsigned char*)signature); 193 } 194 195 static void JNICALL 196 onFieldAccess(jvmtiEnv *jvmti_env, 197 JNIEnv* jni_env, 198 jthread thread, 199 jmethodID method, 200 jlocation location, 201 jclass field_klass, 202 jobject object, 203 jfieldID field) 204 { 205 if (disableAccessEvent) { 206 return; 207 } 208 handleNotification(jvmti_env, jni_env, method, object, field, field_klass, false, location); 209 } 210 211 static void JNICALL 212 onFieldModification(jvmtiEnv *jvmti_env, 213 JNIEnv* jni_env, 214 jthread thread, 215 jmethodID method, 216 jlocation location, 217 jclass field_klass, 218 jobject object, 219 jfieldID field, 220 char signature_type, 221 jvalue new_value) 222 { 223 disableAccessEvent = true; 224 225 handleNotification(jvmti_env, jni_env, method, object, field, field_klass, true, location); 226 227 printJValue("new value", jni_env, signature_type, new_value); 228 229 disableAccessEvent = false; 230 } 231 232 233 JNIEXPORT jint JNICALL 234 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) 235 { 236 jvmtiError err; 237 jvmtiCapabilities caps = {}; 238 jvmtiEventCallbacks callbacks = {}; 239 jint res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1); 240 if (res != JNI_OK || jvmti == nullptr) { 241 reportError("GetEnv failed", res); 242 return JNI_ERR; 243 } 244 245 caps.can_generate_field_modification_events = 1; 246 caps.can_generate_field_access_events = 1; 247 caps.can_tag_objects = 1; 248 err = jvmti->AddCapabilities(&caps); 249 if (err != JVMTI_ERROR_NONE) { 250 reportError("Failed to set capabilities", err); 251 return JNI_ERR; 252 } 253 254 callbacks.FieldModification = &onFieldModification; 255 callbacks.FieldAccess = &onFieldAccess; 256 257 err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks)); 258 if (err != JVMTI_ERROR_NONE) { 259 reportError("Failed to set event callbacks", err); 260 return JNI_ERR; 261 } 262 263 err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIELD_ACCESS, nullptr); 264 if (err != JVMTI_ERROR_NONE) { 265 reportError("Failed to set access notifications", err); 266 return JNI_ERR; 267 } 268 269 err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIELD_MODIFICATION, nullptr); 270 if (err != JVMTI_ERROR_NONE) { 271 reportError("Failed to set modification notifications", err); 272 return JNI_ERR; 273 } 274 setbuf(stdout, nullptr); 275 return JNI_OK; 276 } 277 278 279 JNIEXPORT jboolean JNICALL 280 Java_FieldAccessModify_initWatchers(JNIEnv *env, jclass thisClass, jclass cls, jobject field) 281 { 282 jfieldID fieldId; 283 jvmtiError err; 284 285 if (jvmti == nullptr) { 286 reportError("jvmti is NULL", 0); 287 return JNI_FALSE; 288 } 289 290 fieldId = env->FromReflectedField(field); 291 292 err = jvmti->SetFieldModificationWatch(cls, fieldId); 293 if (err != JVMTI_ERROR_NONE) { 294 reportError("SetFieldModificationWatch failed", err); 295 return JNI_FALSE; 296 } 297 298 err = jvmti->SetFieldAccessWatch(cls, fieldId); 299 if (err != JVMTI_ERROR_NONE) { 300 reportError("SetFieldAccessWatch failed", err); 301 return JNI_FALSE; 302 } 303 304 return JNI_TRUE; 305 } 306 307 308 JNIEXPORT jboolean JNICALL 309 Java_FieldAccessModify_startTest(JNIEnv *env, jclass thisClass, jobject testResults) 310 { 311 testResultObject = env->NewGlobalRef(testResults); 312 testResultClass = (jclass)env->NewGlobalRef(env->GetObjectClass(testResultObject)); 313 314 return JNI_TRUE; 315 } 316 317 JNIEXPORT void JNICALL 318 Java_FieldAccessModify_stopTest(JNIEnv *env, jclass thisClass) 319 { 320 if (testResultObject != nullptr) { 321 env->DeleteGlobalRef(testResultObject); 322 testResultObject = nullptr; 323 } 324 if (testResultClass != nullptr) { 325 env->DeleteGlobalRef(testResultClass); 326 testResultClass = nullptr; 327 } 328 } 329 330 331 #ifdef __cplusplus 332 } 333 #endif 334