Skip to content

Commit

Permalink
Optimize JVMTI watched fields
Browse files Browse the repository at this point in the history
Add a tag bit to J9Class indicating that there are watched fields
associated with the class or one of its superclasses.  Don't call the
field access hooks in the interpreter or JNI unless this bit is set.

Signed-off-by: Graham Chapman <graham_chapman@ca.ibm.com>
  • Loading branch information
gacholio committed Jun 13, 2018
1 parent 91dac0c commit 0d3a38b
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 107 deletions.
48 changes: 25 additions & 23 deletions runtime/jvmti/jvmtiHook.c
Original file line number Diff line number Diff line change
Expand Up @@ -1138,29 +1138,31 @@ static jfieldID
findWatchedField(J9VMThread *currentThread, J9JVMTIEnv * j9env, UDATA isWrite, UDATA isStatic, UDATA tag, J9Class * fieldClass)
{
jfieldID result = NULL;
J9Class *declaringClass = NULL;
J9JVMTIWatchedClass *watchedClass = NULL;
UDATA index = findFieldIndexFromOffset(currentThread, fieldClass, tag, isStatic, &declaringClass);
watchedClass = hashTableFind(j9env->watchedClasses, &declaringClass);
if (NULL != watchedClass) {
UDATA *watchBits = (UDATA*)&watchedClass->watchBits;
UDATA found = FALSE;
if (J9JVMTI_CLASS_REQUIRES_ALLOCATED_J9JVMTI_WATCHED_FIELD_ACCESS_BITS(declaringClass)) {
watchBits = watchedClass->watchBits;
}
if (isWrite) {
found = watchBits[J9JVMTI_WATCHED_FIELD_ARRAY_INDEX(index)] & J9JVMTI_WATCHED_FIELD_MODIFICATION_BIT(index);
} else {
found = watchBits[J9JVMTI_WATCHED_FIELD_ARRAY_INDEX(index)] & J9JVMTI_WATCHED_FIELD_ACCESS_BIT(index);
}
if (found) {
/* In order for a watch to have been placed, the fieldID for the field in question
* must already have been created (it's a parameter to the JVMTI calls).
*/
void **jniIDs = declaringClass->jniIDs;
Assert_JVMTI_notNull(jniIDs);
result = (jfieldID)(jniIDs[index + declaringClass->romClass->romMethodCount]);
Assert_JVMTI_notNull(result);
if (J9_ARE_ANY_BITS_SET(fieldClass->classFlags, J9ClassHasWatchedFields)) {
J9Class *declaringClass = NULL;
J9JVMTIWatchedClass *watchedClass = NULL;
UDATA index = findFieldIndexFromOffset(currentThread, fieldClass, tag, isStatic, &declaringClass);
watchedClass = hashTableFind(j9env->watchedClasses, &declaringClass);
if (NULL != watchedClass) {
UDATA *watchBits = (UDATA*)&watchedClass->watchBits;
UDATA found = FALSE;
if (J9JVMTI_CLASS_REQUIRES_ALLOCATED_J9JVMTI_WATCHED_FIELD_ACCESS_BITS(declaringClass)) {
watchBits = watchedClass->watchBits;
}
if (isWrite) {
found = watchBits[J9JVMTI_WATCHED_FIELD_ARRAY_INDEX(index)] & J9JVMTI_WATCHED_FIELD_MODIFICATION_BIT(index);
} else {
found = watchBits[J9JVMTI_WATCHED_FIELD_ARRAY_INDEX(index)] & J9JVMTI_WATCHED_FIELD_ACCESS_BIT(index);
}
if (found) {
/* In order for a watch to have been placed, the fieldID for the field in question
* must already have been created (it's a parameter to the JVMTI calls).
*/
void **jniIDs = declaringClass->jniIDs;
Assert_JVMTI_notNull(jniIDs);
result = (jfieldID)(jniIDs[index + declaringClass->romClass->romMethodCount]);
Assert_JVMTI_notNull(result);
}
}
}
return result;
Expand Down
16 changes: 14 additions & 2 deletions runtime/jvmti/jvmtiWatchedField.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,17 @@ setFieldWatch(jvmtiEnv* env,
rc = JVMTI_ERROR_DUPLICATE;
} else {
*watchBits |= watchBit;
/* Tag this class and all its subclasses as having watched fields.
* If this class is already tagged, so are the subclasses.
*/
if (J9_ARE_NO_BITS_SET(clazz->classFlags, J9ClassHasWatchedFields)) {
J9SubclassWalkState subclassState;
J9Class *subclass = allSubclassesStartDo(clazz, &subclassState, TRUE);
while (NULL != subclass) {
subclass->classFlags |= J9ClassHasWatchedFields;
subclass = allSubclassesNextDo(&subclassState);
}
}
if (J9_FSD_ENABLED(vm)) {
vm->jitConfig->jitDataBreakpointAdded(currentThread);
}
Expand Down Expand Up @@ -250,8 +261,9 @@ clearFieldWatch(jvmtiEnv* env,
if (J9_FSD_ENABLED(vm)) {
vm->jitConfig->jitDataBreakpointRemoved(currentThread);
}
/* Consider checking for any remaining watches on the class
* and removing the hash table entry if there are none.
/* Consider checking for no remaining watches on the class
* and removing the watched fields bit from the class (and
* subclasses) and removing the hash table entry.
*/
}
}
Expand Down
4 changes: 2 additions & 2 deletions runtime/oti/j9consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,8 @@ extern "C" {
#define J9_JAVA_METHOD_OBJECT_CONSTRUCTOR 0x400000
#define J9_JAVA_CLASS_GC_SPECIAL 0x800000
#define J9_JAVA_CLASS_HAS_VERIFY_DATA 0x800000
#define J9_JAVA_METHOD_DEBUGGABLE 0x1000000
#define J9_JAVA_CLASS_UNUSED_1000000 0x1000000
#define J9_JAVA_METHOD_UNUSED_1000000 0x1000000
#define J9_JAVA_CLASS_CONTENDED 0x1000000
#define J9_JAVA_CLASS_HAS_FINAL_FIELDS 0x2000000
#define J9_JAVA_METHOD_HAS_GENERIC_SIGNATURE 0x2000000
#define J9_JAVA_CLASS_HOT_SWAPPED_OUT 0x4000000
Expand Down
7 changes: 4 additions & 3 deletions runtime/oti/j9nonbuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,15 @@
/* @ddr_namespace: map_to_type=J9JavaClassFlags */

/* Constants from J9JavaClassFlags */
#define J9ClassDoNotAttemptToSetInitCache 0x1
#define J9ClassHasIllegalFinalFieldModifications 0x2
#define J9ClassReusedStatics 0x4
#define J9ClassContainsJittedMethods 0x8
#define J9ClassContainsMethodsPresentInMCCHash 0x10
#define J9ClassGCScanned 0x20
#define J9ClassIsAnonymous 0x40
#define J9ClassIsDerivedValueType 0x80
#define J9ClassDoNotAttemptToSetInitCache 0x1
#define J9ClassHasIllegalFinalFieldModifications 0x2
#define J9ClassReusedStatics 0x4
#define J9ClassHasWatchedFields 0x100

/* @ddr_namespace: map_to_type=J9FieldFlags */

Expand Down
63 changes: 36 additions & 27 deletions runtime/vm/BytecodeInterpreter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6092,13 +6092,15 @@ done:;
valueAddress = J9STATICADDRESS(flagsAndClass, valueOffset);
#if defined(DO_HOOKS)
if (J9_EVENT_IS_HOOKED(_vm->hookInterface, J9HOOK_VM_GET_STATIC_FIELD)) {
updateVMStruct(REGISTER_ARGS);
J9Class *fieldClass = (J9Class*)(classAndFlags & ~(UDATA)J9StaticFieldRefFlagBits);
ALWAYS_TRIGGER_J9HOOK_VM_GET_STATIC_FIELD(_vm->hookInterface, _currentThread, _literals, _pc - _literals->bytecodes, fieldClass, valueAddress);
VMStructHasBeenUpdated(REGISTER_ARGS);
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
goto done;
if (J9_ARE_ANY_BITS_SET(fieldClass->classFlags, J9ClassHasWatchedFields)) {
updateVMStruct(REGISTER_ARGS);
ALWAYS_TRIGGER_J9HOOK_VM_GET_STATIC_FIELD(_vm->hookInterface, _currentThread, _literals, _pc - _literals->bytecodes, fieldClass, valueAddress);
VMStructHasBeenUpdated(REGISTER_ARGS);
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
goto done;
}
}
}
#endif
Expand Down Expand Up @@ -6181,13 +6183,15 @@ done:;
valueAddress = J9STATICADDRESS(flagsAndClass, valueOffset);
#if defined(DO_HOOKS)
if (J9_EVENT_IS_HOOKED(_vm->hookInterface, J9HOOK_VM_PUT_STATIC_FIELD)) {
updateVMStruct(REGISTER_ARGS);
J9Class *fieldClass = (J9Class*)(classAndFlags & ~(UDATA)J9StaticFieldRefFlagBits);
ALWAYS_TRIGGER_J9HOOK_VM_PUT_STATIC_FIELD(_vm->hookInterface, _currentThread, _literals, _pc - _literals->bytecodes, fieldClass, valueAddress, _sp);
VMStructHasBeenUpdated(REGISTER_ARGS);
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
goto done;
if (J9_ARE_ANY_BITS_SET(fieldClass->classFlags, J9ClassHasWatchedFields)) {
updateVMStruct(REGISTER_ARGS);
ALWAYS_TRIGGER_J9HOOK_VM_PUT_STATIC_FIELD(_vm->hookInterface, _currentThread, _literals, _pc - _literals->bytecodes, fieldClass, valueAddress, _sp);
VMStructHasBeenUpdated(REGISTER_ARGS);
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
goto done;
}
}
}
#endif
Expand Down Expand Up @@ -6252,22 +6256,24 @@ done:;
if (NULL == objectref) {
rc = THROW_NPE;
} else {
bool isVolatile = (0 != (flags & J9AccVolatile));
#if defined(DO_HOOKS)
if (J9_EVENT_IS_HOOKED(_vm->hookInterface, J9HOOK_VM_GET_FIELD)) {
updateVMStruct(REGISTER_ARGS);
ALWAYS_TRIGGER_J9HOOK_VM_GET_FIELD(_vm->hookInterface, _currentThread, _literals, _pc - _literals->bytecodes, objectLocation, valueOffset);
VMStructHasBeenUpdated(REGISTER_ARGS);
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
goto done;
if (J9_ARE_ANY_BITS_SET(J9OBJECT_CLAZZ(_currentThread, objectref)->classFlags, J9ClassHasWatchedFields)) {
updateVMStruct(REGISTER_ARGS);
ALWAYS_TRIGGER_J9HOOK_VM_GET_FIELD(_vm->hookInterface, _currentThread, _literals, _pc - _literals->bytecodes, objectLocation, valueOffset);
VMStructHasBeenUpdated(REGISTER_ARGS);
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
goto done;
}
objectref = *objectLocation;
}
objectref = *objectLocation;
}
#endif

{
UDATA const newValueOffset = valueOffset + J9_OBJECT_HEADER_SIZE;
bool isVolatile = (0 != (flags & J9AccVolatile));
if (flags & J9FieldSizeDouble) {
_sp += (slotsToPop - 2);
*(U_64*)_sp = _objectAccessBarrier.inlineMixedObjectReadU64(_currentThread, objectref, newValueOffset, isVolatile);
Expand Down Expand Up @@ -6340,13 +6346,16 @@ done:;
#if defined(DO_HOOKS)
if (J9_EVENT_IS_HOOKED(_vm->hookInterface, J9HOOK_VM_PUT_FIELD)) {
j9object_t *objAddress = (j9object_t*)_sp + ((flags & J9FieldSizeDouble) ? 2 : 1);
if (NULL != *objAddress) {
updateVMStruct(REGISTER_ARGS);
ALWAYS_TRIGGER_J9HOOK_VM_PUT_FIELD(_vm->hookInterface, _currentThread, _literals, _pc - _literals->bytecodes, objAddress, valueOffset, _sp);
VMStructHasBeenUpdated(REGISTER_ARGS);
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
goto done;
j9object_t objectref = *objAddress;
if (NULL != objectref) {
if (J9_ARE_ANY_BITS_SET(J9OBJECT_CLAZZ(_currentThread, objectref)->classFlags, J9ClassHasWatchedFields)) {
updateVMStruct(REGISTER_ARGS);
ALWAYS_TRIGGER_J9HOOK_VM_PUT_FIELD(_vm->hookInterface, _currentThread, _literals, _pc - _literals->bytecodes, objAddress, valueOffset, _sp);
VMStructHasBeenUpdated(REGISTER_ARGS);
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
goto done;
}
}
}
}
Expand Down
40 changes: 24 additions & 16 deletions runtime/vm/createramclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2374,7 +2374,7 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas
* + AccClassHasJDBCNatives (set during native method binding, not inherited)
* + AccClassGCSpecial (set during internal class load hook and inherited)
*
* + AccClassNeedsPerTenantInitialization (from romClass->extraModifiers and inherited)
* + AccClassIsContended (from romClass->extraModifiers and inherited)
* + AccClassHasFinalFields (from romClass->extraModifiers and inherited)
* + AccClassHotSwappedOut (not set during creation, not inherited)
* + AccClassDying (not set during creation, inherited but that can't actually occur)
Expand All @@ -2384,18 +2384,18 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas
* + AccClassFinalizeNeeded (from romClass->extraModifiers and inherited, cleared for empty finalize)
* + AccClassCloneable (from romClass->extraModifiers and inherited)
* extendedClassFlags - what does each bit represent?
* classFlags - what does each bit represent?
*
* 0000 0000 0000 0000 0000 0000 0000 0000
* + DoNotAttemptToSetInitCache
* + Unused
* + ClassReusedStatics
* + ClassContainsJittedMethods
* + HasIllegalFinalFieldModifications
* + ReusedStatics
* + ContainsJittedMethods
*
* + ClassContainsMethodsPresentInMCCHash
* + ClassGCScanned
* + ClassIsAnonymous
* + Unused
* + ContainsMethodsPresentInMCCHash
* + GCScanned
* + IsAnonymous
* + J9ClassHasWatchedFields (inherited)
*
* + Unused
* + Unused
Expand Down Expand Up @@ -2805,14 +2805,22 @@ internalCreateRAMClassFromROMClass(J9VMThread *vmThread, J9ClassLoader *classLoa
goto retry;
}

if ((NULL != result) && (0 != (J9_FINDCLASS_FLAG_ANON & options))) {
/* if anonClass replace classLoader with hostClassLoader, no one can know about anonClassLoader */
result->classLoader = hostClassLoader;
if (NULL != result->classObject) {
/* no object is created when doing hotswapping */
J9VMJAVALANGCLASS_SET_CLASSLOADER(vmThread, result->classObject, hostClassLoader->classLoaderObject);
if (NULL != result) {
UDATA classFlags = result->classFlags;
if (NULL != superclass) {
/* Watched fields tag is inherited from the superclass */
classFlags |= (superclass->classFlags & J9ClassHasWatchedFields);
}
if (0 != (J9_FINDCLASS_FLAG_ANON & options)) {
/* if anonClass replace classLoader with hostClassLoader, no one can know about anonClassLoader */
result->classLoader = hostClassLoader;
if (NULL != result->classObject) {
/* no object is created when doing hotswapping */
J9VMJAVALANGCLASS_SET_CLASSLOADER(vmThread, result->classObject, hostClassLoader->classLoaderObject);
}
classFlags |= J9ClassIsAnonymous;
}
result->classFlags |= J9ClassIsAnonymous;
result->classFlags = classFlags;
}

return result;
Expand Down
32 changes: 17 additions & 15 deletions runtime/vm/jnifield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,34 +159,36 @@ class FieldEvents {
static void triggerGetEvents(J9VMThread *vmThread, J9JNIFieldID *j9FieldID, j9object_t object)
{
J9JavaVM *vm = vmThread->javaVM;
J9HookInterface **vmHook = vm->internalVMFunctions->getVMHookInterface(vm);

if (J9_EVENT_IS_HOOKED(vm->hookInterface, J9HOOK_VM_GET_FIELD)) {
if (J9_ARE_ANY_BITS_SET(J9OBJECT_CLAZZ(currentThread, object)->classFlags, J9ClassHasWatchedFields)) {
J9StackWalkState *walkState = vmThread->stackWalkState;

if ((*vmHook)->J9HookIsEnabled(vmHook, J9HOOK_VM_GET_FIELD)) {
J9StackWalkState *walkState = vmThread->stackWalkState;
initWalkState(vmThread, walkState);
vmThread->javaVM->walkStackFrames(vmThread, walkState);

initWalkState(vmThread, walkState);
vmThread->javaVM->walkStackFrames(vmThread, walkState);

if (NULL != walkState->method) {
TRIGGER_J9HOOK_VM_GET_FIELD(vmThread->javaVM->hookInterface, vmThread, walkState->method, 0, &object, j9FieldID->offset);
if (NULL != walkState->method) {
ALWAYS_TRIGGER_J9HOOK_VM_GET_FIELD(vm->hookInterface, vmThread, walkState->method, 0, &object, j9FieldID->offset);
}
}
}
}

static void triggerSetEvents(J9VMThread *vmThread, J9JNIFieldID *j9FieldID, j9object_t object, void *pvalue)
{
J9JavaVM *vm = vmThread->javaVM;
J9HookInterface **vmHook = vm->internalVMFunctions->getVMHookInterface(vm);

if ((*vmHook)->J9HookIsEnabled(vmHook, J9HOOK_VM_PUT_FIELD)) {
J9StackWalkState *walkState = vmThread->stackWalkState;
if (J9_EVENT_IS_HOOKED(vm->hookInterface, J9HOOK_VM_PUT_FIELD)) {
if (J9_ARE_ANY_BITS_SET(J9OBJECT_CLAZZ(currentThread, object)->classFlags, J9ClassHasWatchedFields)) {
J9StackWalkState *walkState = vmThread->stackWalkState;

initWalkState(vmThread, walkState);
initWalkState(vmThread, walkState);

vmThread->javaVM->walkStackFrames(vmThread, walkState);
vmThread->javaVM->walkStackFrames(vmThread, walkState);

if (NULL != walkState->method) {
TRIGGER_J9HOOK_VM_PUT_FIELD(vmThread->javaVM->hookInterface, vmThread, walkState->method, 0, &object, j9FieldID->offset, pvalue);
if (NULL != walkState->method) {
ALWAYS_TRIGGER_J9HOOK_VM_PUT_FIELD(vm->hookInterface, vmThread, walkState->method, 0, &object, j9FieldID->offset, pvalue);
}
}
}

Expand Down
Loading

0 comments on commit 0d3a38b

Please sign in to comment.