/* * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.ext.jvmti; import java.util.concurrent.*; import com.sun.max.unsafe.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.ext.jvmti.JVMTIUtil.*; import com.sun.max.vm.jni.*; import com.sun.max.vm.object.*; import static com.sun.max.vm.ext.jvmti.JVMTIConstants.*; /** * Support for field watch events. * * This implementation depends on the current, inefficient, compilation strategy * of compiling field access events for every field in every class, whenever an agent has requested * the delivery of field events, and assumes that the delivery is not turned on and off * during the run. Specific field watches can therefore be turned on/off during as run as * that decision is made here and not in the compiled code. * * A much more efficient strategy would be to deopt on demand when a new watch is added, * although that might involve a substantial amount of recompilation. */ public class JVMTIFieldWatch { private static int ACCESS = 1; private static int MODIFICATION = 2; // Watch state handling for fields private static class WatchState { final int state; WatchState(int state) { this.state = state; } } public static final WatchState ACCESS_STATE = new WatchState(ACCESS); public static final WatchState MODIFICATION_STATE = new WatchState(MODIFICATION); public static final WatchState ACCESS_MODIFICATION_STATE = new WatchState(ACCESS | MODIFICATION); private static ConcurrentHashMap<FieldActor, WatchState> fieldMap = new ConcurrentHashMap<FieldActor, WatchState>(); static int setAccessWatch(Class klass, FieldActor fieldActor) { return setWatch(fieldActor, ACCESS_STATE); } static int setModificationWatch(Class klass, FieldActor fieldActor) { return setWatch(fieldActor, MODIFICATION_STATE); } static int setWatch(FieldActor fieldActor, WatchState stateToSet) { WatchState watchState = fieldMap.get(fieldActor); if (watchState != null) { if ((watchState.state & stateToSet.state) != 0) { return JVMTI_ERROR_DUPLICATE; } // other state must be set already fieldMap.put(fieldActor, ACCESS_MODIFICATION_STATE); } else { fieldMap.put(fieldActor, stateToSet); } return JVMTI_ERROR_NONE; } static int clearAccessWatch(Class klass, FieldActor fieldActor) { return clearWatch(fieldActor, ACCESS_STATE); } static int clearModificationWatch(Class klass, FieldActor fieldActor) { return clearWatch(fieldActor, MODIFICATION_STATE); } static int clearWatch(FieldActor fieldActor, WatchState stateToClear) { WatchState watchState = fieldMap.get(fieldActor); if (watchState == null) { return JVMTI_ERROR_NOT_FOUND; } else { if ((watchState.state & stateToClear.state) != 0) { WatchState other = other(stateToClear); if ((watchState.state & other.state) != 0) { fieldMap.put(fieldActor, other); } else { fieldMap.remove(fieldActor); } } } return JVMTI_ERROR_NONE; } private static WatchState other(WatchState watchState) { if (watchState == ACCESS_STATE) { return MODIFICATION_STATE; } else if (watchState == MODIFICATION_STATE) { return ACCESS_STATE; } else { assert false; return null; } } /** * Data handling for field events. * We use the {@link TypedData) "union" type to handle the modification values. */ static class FieldEventData extends TypedData { Object object; // object being accessed int offset; // offset to field boolean isStatic; // static field? FieldActor currentFieldActor; // temp use during delivery analysis } static void invokeFieldAccessCallback(Pointer callback, Pointer jvmtiEnv, JniHandle thread, FieldEventData data) { ClassActor classActor = checkInvoke(data); if (classActor != null) { JVMTICallbacks.invokeFieldWatchCallback(callback, jvmtiEnv, thread, Word.zero(), 0, // TODO set these values JniHandles.createLocalHandle(classActor.toJava()), JniHandles.createLocalHandle(data.object), FieldID.fromFieldActor(data.currentFieldActor), data.tag == FieldEventData.DATA_NONE ? 0 : signatureType(data.tag), Word.zero()); } } static void invokeFieldAccessCallback(JJVMTI.EventCallbacks callbackHandler, Thread thread, FieldEventData data) { ClassActor classActor = checkInvoke(data); if (classActor != null) { if (data.tag == FieldEventData.DATA_NONE) { callbackHandler.fieldAccess(thread, null, 0L, classActor, data.object, data.currentFieldActor); } else { callbackHandler.fieldModification(thread, null, 0L, classActor, data.object, data.currentFieldActor, data.asObject()); } } } private static ClassActor checkInvoke(FieldEventData data) { ClassActor classActor = ObjectAccess.readClassActor(data.object); if (data.isStatic) { data.currentFieldActor = classActor.findStaticFieldActor(data.offset); } else { data.currentFieldActor = classActor.findInstanceFieldActor(data.offset); } WatchState watchState = fieldMap.get(data.currentFieldActor); int watchStateToCheck = data.tag == FieldEventData.DATA_NONE ? ACCESS : MODIFICATION; if (watchState != null && (watchState.state & watchStateToCheck) != 0) { return classActor; } else { return null; } } private static byte signatureType(int tag) { return 'I'; } }