Path: blob/master/test/hotspot/jtreg/serviceability/jvmti/FieldAccessWatch/libFieldAccessWatch.c
66646 views
/*1* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223#include <stdio.h>24#include <string.h>25#include <stdlib.h>26#include "jvmti.h"27#include "jni.h"2829#ifdef __cplusplus30extern "C" {31#endif323334static jvmtiEnv *jvmti = NULL;3536// valid while a test is executed37static jobject testResultObject = NULL;38static jclass testResultClass = NULL;39static jthread testThread = NULL;404142static void reportError(const char *msg, int err) {43printf("%s, error: %d\n", msg, err);44}454647// logs the notification and updates currentTestResult48static void handleNotification(JNIEnv *jni_env,49jthread thread,50jmethodID method,51jfieldID field,52jclass field_klass,53int modified,54jlocation location)55{56jvmtiError err;57char *name = NULL;58char *mname = NULL;59char *mgensig = NULL;60jclass methodClass = NULL;61char *csig = NULL;6263if (testResultObject == NULL) {64// we are out of test65return;66}6768if (!(*jni_env)->IsSameObject(jni_env, thread, testThread)) {69return; // skip events from unexpected threads70}7172err = (*jvmti)->GetFieldName(jvmti, field_klass, field, &name, NULL, NULL);73if (err != JVMTI_ERROR_NONE) {74reportError("GetFieldName failed", err);75return;76}7778err = (*jvmti)->GetMethodName(jvmti, method, &mname, NULL, &mgensig);79if (err != JVMTI_ERROR_NONE) {80reportError("GetMethodName failed", err);81return;82}8384err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &methodClass);85if (err != JVMTI_ERROR_NONE) {86reportError("GetMethodDeclaringClass failed", err);87return;88}8990err = (*jvmti)->GetClassSignature(jvmti, methodClass, &csig, NULL);91if (err != JVMTI_ERROR_NONE) {92reportError("GetClassSignature failed", err);93return;94}9596printf("\"class: %s method: %s%s\" %s field: \"%s\", location: %d\n",97csig, mname, mgensig, modified ? "modified" : "accessed", name, (int)location);9899// set TestResult100if (testResultObject != NULL && testResultClass != NULL) {101jfieldID fieldID;102// field names in TestResult are "<field_name>_access"/"<field_name>_modify"103char *fieldName = (char *)malloc(strlen(name) + 16);104strcpy(fieldName, name);105strcat(fieldName, modified ? "_modify" : "_access");106107fieldID = (*jni_env)->GetFieldID(jni_env, testResultClass, fieldName, "Z");108if (fieldID != NULL) {109(*jni_env)->SetBooleanField(jni_env, testResultObject, fieldID, JNI_TRUE);110} else {111// the field is not interesting for the test112}113// clear any possible exception114(*jni_env)->ExceptionClear(jni_env);115116free(fieldName);117}118119(*jvmti)->Deallocate(jvmti, (unsigned char*)csig);120(*jvmti)->Deallocate(jvmti, (unsigned char*)mname);121(*jvmti)->Deallocate(jvmti, (unsigned char*)mgensig);122(*jvmti)->Deallocate(jvmti, (unsigned char*)name);123}124125// recursively sets access and modification watchers for all126// fields of the object specified.127void setWatchers(JNIEnv *jni_env, const jobject obj)128{129jclass klass;130131if (obj == NULL) {132return;133}134135klass = (*jni_env)->GetObjectClass(jni_env, obj);136do {137jfieldID* klassFields = NULL;138jint fieldCount = 0;139int i;140jvmtiError err = (*jvmti)->GetClassFields(jvmti, klass, &fieldCount, &klassFields);141if (err != JVMTI_ERROR_NONE) {142reportError("Failed to get class fields", err);143return;144}145146for (i = 0; i < fieldCount; ++i) {147char *sig = NULL;148err = (*jvmti)->SetFieldModificationWatch(jvmti, klass, klassFields[i]);149if (err != JVMTI_ERROR_NONE && err != JVMTI_ERROR_DUPLICATE) {150reportError("Failed to set field modification", err);151return;152}153154err = (*jvmti)->SetFieldAccessWatch(jvmti, klass, klassFields[i]);155if (err != JVMTI_ERROR_NONE && err != JVMTI_ERROR_DUPLICATE) {156reportError("Failed to set field access", err);157return;158}159160err = (*jvmti)->GetFieldName(jvmti, klass, klassFields[i], NULL, &sig, NULL);161if (sig) {162if (sig[0] == 'L') {163jobject fieldVal = (*jni_env)->GetObjectField(jni_env, obj, klassFields[i]);164setWatchers(jni_env, fieldVal);165}166(*jvmti)->Deallocate(jvmti, (unsigned char*)sig);167}168}169170(*jvmti)->Deallocate(jvmti, (unsigned char*)klassFields);171172klass = (*jni_env)->GetSuperclass(jni_env, klass);173} while (klass != NULL);174}175176177static void JNICALL178onFieldAccess(jvmtiEnv *jvmti_env,179JNIEnv* jni_env,180jthread thread,181jmethodID method,182jlocation location,183jclass field_klass,184jobject object,185jfieldID field)186{187handleNotification(jni_env, thread, method, field, field_klass, 0, location);188}189190191static void JNICALL192onFieldModification(jvmtiEnv *jvmti_env,193JNIEnv* jni_env,194jthread thread,195jmethodID method,196jlocation location,197jclass field_klass,198jobject object,199jfieldID field,200char signature_type,201jvalue new_value)202{203handleNotification(jni_env, thread, method, field, field_klass, 1, location);204205if (signature_type == 'L') {206jobject newObject = new_value.l;207setWatchers(jni_env, newObject);208}209}210211212JNIEXPORT jint JNICALL213Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)214{215jvmtiError err;216jvmtiCapabilities caps;217jvmtiEventCallbacks callbacks;218jint res = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_1);219if (res != JNI_OK || jvmti == NULL) {220reportError("GetEnv failed", res);221return JNI_ERR;222}223224memset(&caps, 0, sizeof(caps));225caps.can_generate_field_modification_events = 1;226caps.can_generate_field_access_events = 1;227caps.can_tag_objects = 1;228err = (*jvmti)->AddCapabilities(jvmti, &caps);229if (err != JVMTI_ERROR_NONE) {230reportError("Failed to set capabilities", err);231return JNI_ERR;232}233234memset(&callbacks, 0, sizeof(callbacks));235callbacks.FieldModification = &onFieldModification;236callbacks.FieldAccess = &onFieldAccess;237238err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));239if (err != JVMTI_ERROR_NONE) {240reportError("Failed to set event callbacks", err);241return JNI_ERR;242}243244err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIELD_ACCESS, NULL);245if (err != JVMTI_ERROR_NONE) {246reportError("Failed to set access notifications", err);247return JNI_ERR;248}249250err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIELD_MODIFICATION, NULL);251if (err != JVMTI_ERROR_NONE) {252reportError("Failed to set modification notifications", err);253return JNI_ERR;254}255setbuf(stdout, NULL);256return JNI_OK;257}258259260JNIEXPORT jboolean JNICALL261Java_FieldAccessWatch_initWatchers(JNIEnv *env, jclass thisClass, jclass cls, jobject field, jthread thread)262{263jfieldID fieldId;264jvmtiError err;265266if (jvmti == NULL) {267reportError("jvmti is NULL", 0);268return JNI_FALSE;269}270271fieldId = (*env)->FromReflectedField(env, field);272273err = (*jvmti)->SetFieldModificationWatch(jvmti, cls, fieldId);274if (err != JVMTI_ERROR_NONE) {275reportError("SetFieldModificationWatch failed", err);276return JNI_FALSE;277}278279err = (*jvmti)->SetFieldAccessWatch(jvmti, cls, fieldId);280if (err != JVMTI_ERROR_NONE) {281reportError("SetFieldAccessWatch failed", err);282return JNI_FALSE;283}284285testThread = (jthread)(*env)->NewGlobalRef(env, thread);286287return JNI_TRUE;288}289290291JNIEXPORT jboolean JNICALL292Java_FieldAccessWatch_startTest(JNIEnv *env, jclass thisClass, jobject testResults)293{294testResultObject = (*env)->NewGlobalRef(env, testResults);295testResultClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, testResultObject));296297return JNI_TRUE;298}299300JNIEXPORT void JNICALL301Java_FieldAccessWatch_stopTest(JNIEnv *env, jclass thisClass)302{303if (testResultObject != NULL) {304(*env)->DeleteGlobalRef(env, testResultObject);305testResultObject = NULL;306}307if (testResultClass != NULL) {308(*env)->DeleteGlobalRef(env, testResultClass);309testResultClass = NULL;310}311}312313314#ifdef __cplusplus315}316#endif317318319320