Skip to content

Commit

Permalink
Skip methods with JvmtiMountTransition annotation
Browse files Browse the repository at this point in the history
Java methods tagged with the JvmtiMountTransition annotation are
involved in transitioning between carrier to virtual threads, and
vice-versa.

These methods are not part of the thread's scope. So, they should be
skipped during introspection by all the relevant JVMTI functions.

This commit covers the remaining JVMTI functions which need to skip
methods tagged with the JvmtiMountTransition annotation.

Fixes: #17520

Signed-off-by: Babneet Singh <[email protected]>
  • Loading branch information
babsingh committed Aug 25, 2023
1 parent e41c032 commit 7e12846
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 56 deletions.
94 changes: 56 additions & 38 deletions runtime/jvmti/jvmtiExtensionMechanism.c
Original file line number Diff line number Diff line change
Expand Up @@ -1864,89 +1864,107 @@ jvmtiInternalGetStackTraceExtended(jvmtiEnv* env,


static UDATA
jvmtiInternalGetStackTraceIteratorExtended(J9VMThread * currentThread, J9StackWalkState * walkState)
jvmtiInternalGetStackTraceIteratorExtended(J9VMThread *currentThread, J9StackWalkState *walkState)
{
J9JVMTIStackTraceType type;
jmethodID methodID;
jvmtiFrameInfoExtended * frame_buffer;
UDATA frameCount;
jmethodID methodID = NULL;
jvmtiFrameInfoExtended *frame_buffer = NULL;
UDATA frameCount = 0;
J9JVMTIStackTraceType type = (J9JVMTIStackTraceType)(UDATA)walkState->userData2;
J9Method *method = walkState->method;

/* In extra info mode when method enter is enabled, exclude natives which have not had method enter reported for them */
#if JAVA_SPEC_VERSION >= 20
J9ROMMethod *romMethod = NULL;
U_32 extendedModifiers = 0;

type = (J9JVMTIStackTraceType) (UDATA) walkState->userData2;
if (type & J9JVMTI_STACK_TRACE_PRUNE_UNREPORTED_METHODS) {
if ((UDATA)walkState->pc == J9SF_FRAME_TYPE_NATIVE_METHOD) {
/* INL natives never have enter/exit reported */
/* walkState->method can never be NULL since the J9_STACKWALK_VISIBLE_ONLY flag is set. */
Assert_JVMTI_true(NULL != method);

romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method);
extendedModifiers = getExtendedModifiersDataFromROMMethod(romMethod);

if (J9_ARE_ANY_BITS_SET(extendedModifiers, CFR_METHOD_EXT_JVMTIMOUNTTRANSITION_ANNOTATION)) {
goto skip;
}
#endif /* JAVA_SPEC_VERSION >= 20 */

/* In extra info mode when method enter is enabled, exclude natives which have not had method enter reported for them. */
if (J9_ARE_ANY_BITS_SET(type, J9JVMTI_STACK_TRACE_PRUNE_UNREPORTED_METHODS)) {
if (J9SF_FRAME_TYPE_NATIVE_METHOD == (UDATA)walkState->pc) {
/* INL natives never have enter/exit reported. */
return J9_STACKWALK_KEEP_ITERATING;
}
#if defined(J9VM_INTERP_NATIVE_SUPPORT)
if ((UDATA)walkState->pc == J9SF_FRAME_TYPE_JNI_NATIVE_METHOD) {
if (walkState->frameFlags & J9_STACK_FLAGS_JIT_JNI_CALL_OUT_FRAME) {
/* Direct JNI inlined into JIT method */
if (J9SF_FRAME_TYPE_JNI_NATIVE_METHOD == (UDATA)walkState->pc) {
if (J9_ARE_ANY_BITS_SET(walkState->frameFlags, J9_STACK_FLAGS_JIT_JNI_CALL_OUT_FRAME)) {
/* Direct JNI inlined into JIT method. */
return J9_STACKWALK_KEEP_ITERATING;
}
}
/* Direct JNI thunks (method is native, jitInfo != NULL) do have enter/exit reported */
#endif
/* Direct JNI thunks (method is native, jitInfo != NULL) do have enter/exit reported. */
#endif /* defined(J9VM_INTERP_NATIVE_SUPPORT) */
}

frame_buffer = walkState->userData1;
if (frame_buffer != NULL) {
methodID = getCurrentMethodID(currentThread, walkState->method);
if (methodID == NULL) {
if (NULL != frame_buffer) {
methodID = getCurrentMethodID(currentThread, method);
if (NULL == methodID) {
walkState->userData1 = NULL;
return J9_STACKWALK_STOP_ITERATING;
}

frame_buffer->method = methodID;

if (type & J9JVMTI_STACK_TRACE_EXTRA_FRAME_INFO) {
/* Fill in the extended data */
#ifdef J9VM_INTERP_NATIVE_SUPPORT
if (walkState->jitInfo == NULL) {
if (J9_ARE_ANY_BITS_SET(type, J9JVMTI_STACK_TRACE_EXTRA_FRAME_INFO)) {
/* Fill in the extended data. */
#if defined(J9VM_INTERP_NATIVE_SUPPORT)
if (NULL == walkState->jitInfo) {
frame_buffer->type = COM_IBM_STACK_FRAME_EXTENDED_NOT_JITTED;
} else if (J9_ARE_ANY_BITS_SET(type, J9JVMTI_STACK_TRACE_MARK_INLINED_FRAMES) && (walkState->inlineDepth > 0)) {
frame_buffer->type = COM_IBM_STACK_FRAME_EXTENDED_INLINED;
} else {
frame_buffer->type = COM_IBM_STACK_FRAME_EXTENDED_JITTED;
}
#else
#else /* defined(J9VM_INTERP_NATIVE_SUPPORT) */
frame_buffer->type = COM_IBM_STACK_FRAME_EXTENDED_NOT_JITTED;
#endif
#endif /* defined(J9VM_INTERP_NATIVE_SUPPORT) */
frame_buffer->machinepc = -1; /* not supported yet */
}
}

if (type & J9JVMTI_STACK_TRACE_ENTRY_LOCAL_STORAGE) {
#ifdef J9VM_INTERP_NATIVE_SUPPORT
if ((jlocation) walkState->bytecodePCOffset == -1) {
frame_buffer->nativeFrameAddress = (void *) walkState->walkedEntryLocalStorage;
if (J9_ARE_ANY_BITS_SET(type, J9JVMTI_STACK_TRACE_ENTRY_LOCAL_STORAGE)) {
#if defined(J9VM_INTERP_NATIVE_SUPPORT)
if (-1 == (jlocation)walkState->bytecodePCOffset) {
frame_buffer->nativeFrameAddress = (void *)walkState->walkedEntryLocalStorage;
} else {
frame_buffer->nativeFrameAddress = NULL;
}
#else
#else /* defined(J9VM_INTERP_NATIVE_SUPPORT) */
frame_buffer->nativeFrameAddress = NULL;
#endif
#endif /* defined(J9VM_INTERP_NATIVE_SUPPORT) */
}

/* The location = -1 for native method case is handled in the stack walker */
frame_buffer->location = (jlocation) walkState->bytecodePCOffset;
/* The location = -1 for native method case is handled in the stack walker. */
frame_buffer->location = (jlocation)walkState->bytecodePCOffset;

/* If the location specifies a JBinvokeinterface, back it up to the JBinvokeinterface2 */
/* If the location specifies a JBinvokeinterface, back it up to the JBinvokeinterface2. */
if (!IS_SPECIAL_FRAME_PC(walkState->pc)) {
if (*(walkState->pc) == JBinvokeinterface) {
if (JBinvokeinterface == *(walkState->pc)) {
frame_buffer->location -= 2;
}
}

walkState->userData1 = frame_buffer + 1;
}

frameCount = (UDATA) walkState->userData3;
frameCount = (UDATA)walkState->userData3;
++frameCount;
walkState->userData3 = (void *) frameCount;
if (frameCount == (UDATA) walkState->userData4) {
walkState->userData3 = (void *)frameCount;
if (frameCount == (UDATA)walkState->userData4) {
return J9_STACKWALK_STOP_ITERATING;
}

#if JAVA_SPEC_VERSION >= 20
skip:
#endif /* JAVA_SPEC_VERSION >= 20 */
return J9_STACKWALK_KEEP_ITERATING;
}

Expand Down
24 changes: 24 additions & 0 deletions runtime/jvmti/jvmtiHelpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,30 @@ jvmtiTLSGet(J9VMThread *vmThread, j9object_t thread, UDATA key)
#endif /* JAVA_SPEC_VERSION >= 19 */
}

#if JAVA_SPEC_VERSION >= 20
UDATA
genericFrameIterator(J9VMThread *currentThread, J9StackWalkState *walkState)
{
J9Method *method = walkState->method;
J9ROMMethod *romMethod = NULL;
U_32 extendedModifiers = 0;

/* walkState->method can never be NULL since the J9_STACKWALK_VISIBLE_ONLY flag is set. */
Assert_JVMTI_true(NULL != method);

romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method);
extendedModifiers = getExtendedModifiersDataFromROMMethod(romMethod);

if (J9_ARE_ANY_BITS_SET(extendedModifiers, CFR_METHOD_EXT_JVMTIMOUNTTRANSITION_ANNOTATION)) {
/* The number of frames skipped is stored in userData1. */
UDATA framesSkipped = (UDATA)walkState->userData1;
walkState->userData1 = (void *)(framesSkipped + 1);
}

return J9_STACKWALK_KEEP_ITERATING;
}
#endif /* JAVA_SPEC_VERSION >= 20 */

UDATA
genericWalkStackFramesHelper(J9VMThread *currentThread, J9VMThread *targetThread, j9object_t threadObject, J9StackWalkState *walkState)
{
Expand Down
74 changes: 56 additions & 18 deletions runtime/jvmti/jvmtiStackFrame.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,11 @@ jvmtiGetFrameCount(jvmtiEnv* env,
J9StackWalkState walkState;
walkState.flags = J9_STACKWALK_INCLUDE_NATIVES | J9_STACKWALK_VISIBLE_ONLY;
walkState.skipCount = 0;
/* The number of frames skipped is stored in userData1. */
walkState.userData1 = (void *)0;
#if JAVA_SPEC_VERSION >= 20
walkState.frameWalkFunction = genericFrameIterator;
#endif /* JAVA_SPEC_VERSION >= 20 */

#if JAVA_SPEC_VERSION >= 19
if (NULL != targetThread)
Expand All @@ -398,7 +403,7 @@ jvmtiGetFrameCount(jvmtiEnv* env,
vmFuncs->resumeThreadForInspection(currentThread, targetThread);
}

rv_count = (jint) walkState.framesWalked;
rv_count = (jint)(walkState.framesWalked - (UDATA)walkState.userData1);

releaseVMThread(currentThread, targetThread, thread);
}
Expand Down Expand Up @@ -683,32 +688,56 @@ jvmtiNotifyFramePop(jvmtiEnv *env,


static UDATA
jvmtiInternalGetStackTraceIterator(J9VMThread * currentThread, J9StackWalkState * walkState)
jvmtiInternalGetStackTraceIterator(J9VMThread *currentThread, J9StackWalkState *walkState)
{
jmethodID methodID;
jmethodID methodID = NULL;
UDATA rc = J9_STACKWALK_KEEP_ITERATING;
J9Method *method = walkState->method;

#if JAVA_SPEC_VERSION >= 20
J9ROMMethod *romMethod = NULL;
U_32 extendedModifiers = 0;

/* walkState->method can never be NULL since the J9_STACKWALK_VISIBLE_ONLY flag is set. */
Assert_JVMTI_true(NULL != method);

romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method);
extendedModifiers = getExtendedModifiersDataFromROMMethod(romMethod);

if (J9_ARE_ANY_BITS_SET(extendedModifiers, CFR_METHOD_EXT_JVMTIMOUNTTRANSITION_ANNOTATION)) {
/* The number of frames skipped is stored in userData2. */
UDATA framesSkipped = (UDATA)walkState->userData2;
walkState->userData2 = (void *)(framesSkipped + 1);
goto skip;
}
#endif /* JAVA_SPEC_VERSION >= 20 */

methodID = getCurrentMethodID(currentThread, walkState->method);
if (methodID == NULL) {
methodID = getCurrentMethodID(currentThread, method);
if (NULL == methodID) {
walkState->userData1 = NULL;
return J9_STACKWALK_STOP_ITERATING;
rc = J9_STACKWALK_STOP_ITERATING;
} else {
jvmtiFrameInfo * frame_buffer = walkState->userData1;
jvmtiFrameInfo *frame_buffer = walkState->userData1;

frame_buffer->method = methodID;
/* The location = -1 for native method case is handled in the stack walker */
frame_buffer->location = (jlocation) walkState->bytecodePCOffset;
/* The location = -1 for native method case is handled in the stack walker. */
frame_buffer->location = (jlocation)walkState->bytecodePCOffset;

/* If the location specifies a JBinvokeinterface, back it up to the JBinvokeinterface2 */
/* If the location specifies a JBinvokeinterface, back it up to the JBinvokeinterface2. */

if (!IS_SPECIAL_FRAME_PC(walkState->pc)) {
if (*(walkState->pc) == JBinvokeinterface) {
if (JBinvokeinterface == *(walkState->pc)) {
frame_buffer->location -= 2;
}
}

walkState->userData1 = frame_buffer + 1;
return J9_STACKWALK_KEEP_ITERATING;
}

#if JAVA_SPEC_VERSION >= 20
skip:
#endif /* JAVA_SPEC_VERSION >= 20 */
return rc;
}


Expand Down Expand Up @@ -780,36 +809,45 @@ jvmtiInternalGetStackTrace(
jint *count_ptr)
{
J9StackWalkState walkState = {0};

UDATA framesWalked = 0;
walkState.flags = J9_STACKWALK_INCLUDE_NATIVES | J9_STACKWALK_VISIBLE_ONLY;
walkState.skipCount = 0;

/* The number of frames skipped is stored in userData1. */
walkState.userData1 = (void *)0;
#if JAVA_SPEC_VERSION >= 20
walkState.frameWalkFunction = genericFrameIterator;
#endif /* JAVA_SPEC_VERSION >= 20 */
genericWalkStackFramesHelper(currentThread, targetThread, threadObject, &walkState);
framesWalked = walkState.framesWalked - (UDATA)walkState.userData1;
if (start_depth == 0) {
/* This violates the spec, but matches JDK behaviour - allows querying an empty stack with start_depth == 0 */
walkState.skipCount = 0;
} else if (start_depth > 0) {
if (((UDATA) start_depth) >= walkState.framesWalked) {
if (((UDATA)start_depth) >= framesWalked) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
walkState.skipCount = (UDATA) start_depth;
} else {
if (((UDATA) -start_depth) > walkState.framesWalked) {
if (((UDATA)-start_depth) > framesWalked) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
walkState.skipCount = walkState.framesWalked + start_depth;
walkState.skipCount = framesWalked + start_depth;
}
walkState.maxFrames = max_frame_count;
walkState.flags = J9_STACKWALK_INCLUDE_NATIVES | J9_STACKWALK_VISIBLE_ONLY
| J9_STACKWALK_RECORD_BYTECODE_PC_OFFSET | J9_STACKWALK_COUNT_SPECIFIED
| J9_STACKWALK_ITERATE_FRAMES;
walkState.userData1 = frame_buffer;
/* The number of frames skipped is stored in userData2. */
walkState.userData2 = (void *)0;
walkState.frameWalkFunction = jvmtiInternalGetStackTraceIterator;

genericWalkStackFramesHelper(currentThread, targetThread, threadObject, &walkState);
framesWalked = walkState.framesWalked - (UDATA)walkState.userData2;

if (NULL == walkState.userData1) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
*count_ptr = (jint) walkState.framesWalked;
*count_ptr = (jint)framesWalked;
return JVMTI_ERROR_NONE;
}
11 changes: 11 additions & 0 deletions runtime/jvmti/jvmti_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,17 @@ suspendAgentBreakpoint(J9VMThread * currentThread, J9JVMTIAgentBreakpoint * agen
UDATA
findDecompileInfo(J9VMThread *currentThread, J9VMThread *targetThread, UDATA depth, J9StackWalkState *walkState);

#if JAVA_SPEC_VERSION >= 20
/**
* A helper to iterate through the frames of a thread.
* @param[in] currentThread current thread
* @param[in] walkState a stack walk state
* @return 0 on success and non-zero on failure
*/
UDATA
genericFrameIterator(J9VMThread *currentThread, J9StackWalkState *walkState);
#endif /* JAVA_SPEC_VERSION >= 20 */

/**
* A helper to walk a platform thread or virtual thread
* @param[in] currentThread current thread
Expand Down

0 comments on commit 7e12846

Please sign in to comment.