Skip to content

Commit

Permalink
Merge pull request #18000 from fengxue-IS/tracePinned2
Browse files Browse the repository at this point in the history
Add support for jdk.tracePinnedThreads system property
  • Loading branch information
keithc-ca committed Sep 13, 2023
2 parents c0fab8f + b8cfdc3 commit 869cc38
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 20 deletions.
58 changes: 58 additions & 0 deletions jcl/src/java.base/share/classes/java/lang/PinnedThreadPrinter.java
@@ -0,0 +1,58 @@
/*[INCLUDE-IF JAVA_SPEC_VERSION >= 21]*/
/*******************************************************************************
* Copyright IBM Corp. and others 2023
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
* or the Apache License, Version 2.0 which accompanies this distribution and
* is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* This Source Code may also be made available under the following
* Secondary Licenses when the conditions for such availability set
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
* General Public License, version 2 with the GNU Classpath
* Exception [1] and GNU General Public License, version 2 with the
* OpenJDK Assembly Exception [2].
*
* [1] https://www.gnu.org/software/classpath/license.html
* [2] https://openjdk.org/legal/assembly-exception.html
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
*******************************************************************************/
package java.lang;

import java.io.PrintStream;
import java.lang.StackWalker.StackFrame;
import java.lang.StackWalker.StackFrameImpl;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static java.lang.StackWalker.Option.*;

/**
* Prints the stack trace of Pinned Thread that is attempting to yield.
*/
final class PinnedThreadPrinter {
private static final StackWalker STACKWALKER;

static {
STACKWALKER = StackWalker.getInstance(Set.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE));
STACKWALKER.setGetMonitorsFlag();
}

static void printStackTrace(PrintStream out, boolean printAll) {
out.println(Thread.currentThread());
List<StackFrame> stackFrames = STACKWALKER.walk(s -> s.collect(Collectors.toList()));
for (int i = 0; i < stackFrames.size(); i++) {
StackFrameImpl sti = (StackFrameImpl)stackFrames.get(i);
Object[] monitors = sti.getMonitors();

if (monitors != null) {
out.println(" " + sti.toString() + " <== monitors:" + monitors.length);
} else if (sti.isNativeMethod() || (printAll && (sti.getDeclaringClass() != PinnedThreadPrinter.class))) {
out.println(" " + sti.toString());
}
}
}
}
36 changes: 30 additions & 6 deletions jcl/src/java.base/share/classes/java/lang/StackWalker.java
Expand Up @@ -55,9 +55,16 @@
public final class StackWalker {

private static final int DEFAULT_BUFFER_SIZE = 1;
private final static int J9_RETAIN_CLASS_REFERENCE = 1;
private final static int J9_SHOW_REFLECT_FRAMES = 2;
private final static int J9_SHOW_HIDDEN_FRAMES = 4;

/* Java StackWalker flag constants cloned from java_lang_StackWalker.cpp. */
private static final int J9_RETAIN_CLASS_REFERENCE = 0x1;
private static final int J9_SHOW_REFLECT_FRAMES = 0x2;
private static final int J9_SHOW_HIDDEN_FRAMES = 0x4;
/* 0x8 flag used by VM constant J9_FRAME_VALID */
/*[IF JAVA_SPEC_VERSION >= 21]*/
/* Internal flag for retrieving monitor info for stack frames. */
private static final int J9_GET_MONITORS = 0x10;
/*[ENDIF] JAVA_SPEC_VERSION >= 21 */

final Set<Option> walkerOptions;

Expand Down Expand Up @@ -97,6 +104,15 @@ private StackWalker(Set<Option> options, int estimatedDepth) {
}
}

/*[IF JAVA_SPEC_VERSION >= 21]*/
/**
* Set the J9_GET_MONITORS flag.
*/
void setGetMonitorsFlag() {
flags |= J9_GET_MONITORS;
}
/*[ENDIF] JAVA_SPEC_VERSION >= 21 */

/**
* Factory method to create a StackWalker instance with no options set.
*
Expand Down Expand Up @@ -201,7 +217,7 @@ public Class<?> getCallerClass() {
return clientsCaller.getDeclaringClass();
}

private native static <T> T walkWrapperImpl(int flags, String walkerMethod,
private static native <T> T walkWrapperImpl(int flags, String walkerMethod,
Function<? super Stream<StackFrame>, ? extends T> function);

/**
Expand Down Expand Up @@ -255,7 +271,7 @@ public <T> T walk(Function<? super Stream<StackFrame>, ? extends T> function) {
private ContinuationScope scope;
private Continuation cont;

private native static <T> T walkContinuationImpl(int flags, Function<? super Stream<StackFrame>, ? extends T> function, Continuation cont);
private static native <T> T walkContinuationImpl(int flags, Function<? super Stream<StackFrame>, ? extends T> function, Continuation cont);

static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption) {
return newInstance(options, extendedOption, null, null);
Expand Down Expand Up @@ -379,7 +395,7 @@ default String getDescriptor() {
/*[ENDIF] JAVA_SPEC_VERSION >= 10 */
}

final static class StackFrameImpl implements StackFrame {
static final class StackFrameImpl implements StackFrame {

private Class<?> declaringClass;
private String fileName;
Expand All @@ -390,6 +406,9 @@ final static class StackFrameImpl implements StackFrame {
private Module frameModule;
private String methodName;
private String methodSignature;
/*[IF JAVA_SPEC_VERSION >= 21]*/
private Object[] monitors;
/*[ENDIF] JAVA_SPEC_VERSION >= 21 */
boolean callerSensitive;

@Override
Expand Down Expand Up @@ -492,6 +511,11 @@ public java.lang.String getDescriptor() {
}
/*[ENDIF] JAVA_SPEC_VERSION >= 10 */

/*[IF JAVA_SPEC_VERSION >= 21]*/
protected Object[] getMonitors() {
return monitors;
}
/*[ENDIF] JAVA_SPEC_VERSION >= 21 */
}

static class PermissionSingleton {
Expand Down
97 changes: 83 additions & 14 deletions runtime/jcl/common/java_lang_StackWalker.cpp
Expand Up @@ -31,11 +31,17 @@
#include "VMHelpers.hpp"

extern "C" {
#define RETAIN_CLASS_REFERENCE 1
#define SHOW_REFLECT_FRAMES 2
#define SHOW_HIDDEN_FRAMES 4
#define FRAME_VALID 8
#define FRAME_FILTER_MASK (RETAIN_CLASS_REFERENCE | SHOW_REFLECT_FRAMES | SHOW_HIDDEN_FRAMES)
/* Internal flag constants, equivalent Java flags defined in StackWalker class. */
#define J9_RETAIN_CLASS_REFERENCE 0x1
#define J9_SHOW_REFLECT_FRAMES 0x2
#define J9_SHOW_HIDDEN_FRAMES 0x4
#define J9_FRAME_VALID 0x8
#if JAVA_SPEC_VERSION >= 21
#define J9_GET_MONITORS 0x10
#define J9_FRAME_FILTER_MASK (J9_RETAIN_CLASS_REFERENCE | J9_SHOW_REFLECT_FRAMES | J9_SHOW_HIDDEN_FRAMES | J9_GET_MONITORS)
#else /* JAVA_SPEC_VERSION >= 21 */
#define J9_FRAME_FILTER_MASK (J9_RETAIN_CLASS_REFERENCE | J9_SHOW_REFLECT_FRAMES | J9_SHOW_HIDDEN_FRAMES)
#endif /* JAVA_SPEC_VERSION >= 21 */

static UDATA stackFrameFilter(J9VMThread * currentThread, J9StackWalkState * walkState);

Expand All @@ -62,7 +68,7 @@ stackFrameFilter(J9VMThread * currentThread, J9StackWalkState * walkState)
walkState->userData2 = NULL; /* Iteration will skip hidden frames and stop at the true caller of stackWalkerMethod. */
}
}
} else if (J9_ARE_NO_BITS_SET((UDATA) (walkState->userData1), SHOW_REFLECT_FRAMES | SHOW_HIDDEN_FRAMES)
} else if (J9_ARE_NO_BITS_SET((UDATA) (walkState->userData1), J9_SHOW_REFLECT_FRAMES | J9_SHOW_HIDDEN_FRAMES)
&& VM_VMHelpers::isReflectionMethod(currentThread, walkState->method)
) {
/* skip reflection/MethodHandleInvoke frames */
Expand Down Expand Up @@ -92,10 +98,33 @@ Java_java_lang_StackWalker_walkWrapperImpl(JNIEnv *env, jclass clazz, jint flags
| J9_STACKWALK_INCLUDE_NATIVES | J9_STACKWALK_VISIBLE_ONLY;
/* If -XX:+ShowHiddenFrames and StackWalker.SHOW_HIDDEN_FRAMES option has not been set, skip hidden method frames */
if (J9_ARE_NO_BITS_SET(vm->runtimeFlags, J9_RUNTIME_SHOW_HIDDEN_FRAMES)
&& J9_ARE_NO_BITS_SET((UDATA)flags, SHOW_HIDDEN_FRAMES)
&& J9_ARE_NO_BITS_SET((UDATA)flags, J9_SHOW_HIDDEN_FRAMES)
) {
walkState->flags |= J9_STACKWALK_SKIP_HIDDEN_FRAMES;
}

#if JAVA_SPEC_VERSION >= 21
J9ObjectMonitorInfo *info = NULL;
PORT_ACCESS_FROM_JAVAVM(vm);
if (J9_ARE_ALL_BITS_SET((UDATA)flags, J9_GET_MONITORS)) {
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
IDATA infoLen = vmFuncs->getOwnedObjectMonitors(vmThread, vmThread, NULL, 0);
if (infoLen > 0) {
info = (J9ObjectMonitorInfo *)j9mem_allocate_memory(infoLen * sizeof(J9ObjectMonitorInfo), J9MEM_CATEGORY_VM_JCL);
if (NULL != info) {
IDATA rc = vmFuncs->getOwnedObjectMonitors(vmThread, vmThread, info, infoLen);
if (rc >= 0) {
walkState->userData3 = info;
walkState->userData4 = (void *)infoLen;
}
} else {
vmFuncs->setNativeOutOfMemoryError(vmThread, 0, 0);
return NULL;
}
}
}
#endif /* JAVA_SPEC_VERSION >= 21 */

walkState->frameWalkFunction = stackFrameFilter;
const char * walkerMethodChars = env->GetStringUTFChars(stackWalkerMethod, NULL);
if (NULL == walkerMethodChars) { /* native out of memory exception pending */
Expand All @@ -108,7 +137,7 @@ Java_java_lang_StackWalker_walkWrapperImpl(JNIEnv *env, jclass clazz, jint flags
walkState->userData1 = (void *)(UDATA)flags;
if (J9SF_FRAME_TYPE_END_OF_STACK != walkState->pc) {
/* indicate the we have the topmost client method's frame */
walkState->userData1 = (void *)((UDATA) walkState->userData1 | FRAME_VALID);
walkState->userData1 = (void *)((UDATA) walkState->userData1 | J9_FRAME_VALID);
}

jmethodID walkImplMID = JCL_CACHE_GET(env, MID_java_lang_StackWalker_walkImpl);
Expand All @@ -122,6 +151,13 @@ Java_java_lang_StackWalker_walkWrapperImpl(JNIEnv *env, jclass clazz, jint flags
if (NULL != walkerMethodChars) {
env->ReleaseStringUTFChars(stackWalkerMethod, walkerMethodChars);
}

#if JAVA_SPEC_VERSION >= 21
if (NULL != info) {
j9mem_free_memory(info);
}
#endif /* JAVA_SPEC_VERSION >= 21 */

vmThread->stackWalkState = newWalkState.previous;

return result;
Expand Down Expand Up @@ -149,7 +185,7 @@ Java_java_lang_StackWalker_walkContinuationImpl(JNIEnv *env, jclass clazz, jint
| J9_STACKWALK_INCLUDE_NATIVES | J9_STACKWALK_VISIBLE_ONLY;
/* If -XX:+ShowHiddenFrames and StackWalker.SHOW_HIDDEN_FRAMES option has not been set, skip hidden method frames */
if (J9_ARE_NO_BITS_SET(vm->runtimeFlags, J9_RUNTIME_SHOW_HIDDEN_FRAMES)
&& J9_ARE_NO_BITS_SET((UDATA)flags, SHOW_HIDDEN_FRAMES)
&& J9_ARE_NO_BITS_SET((UDATA)flags, J9_SHOW_HIDDEN_FRAMES)
) {
walkState.flags |= J9_STACKWALK_SKIP_HIDDEN_FRAMES;
}
Expand All @@ -163,7 +199,7 @@ Java_java_lang_StackWalker_walkContinuationImpl(JNIEnv *env, jclass clazz, jint
walkState.userData1 = (void *)(UDATA)flags;
if (J9SF_FRAME_TYPE_END_OF_STACK != walkState.pc) {
/* indicate the we have the topmost client method's frame */
walkState.userData1 = (void *)((UDATA) walkState.userData1 | FRAME_VALID);
walkState.userData1 = (void *)((UDATA) walkState.userData1 | J9_FRAME_VALID);
}

jmethodID walkImplMID = JCL_CACHE_GET(env, MID_java_lang_StackWalker_walkImpl);
Expand All @@ -190,16 +226,16 @@ Java_java_lang_StackWalker_getImpl(JNIEnv *env, jobject clazz, jlong walkStateP)

enterVMFromJNI(vmThread);

if (J9_ARE_NO_BITS_SET((UDATA) (walkState->userData1), FRAME_VALID)) {
if (J9_ARE_NO_BITS_SET((UDATA) (walkState->userData1), J9_FRAME_VALID)) {
/* skip over the current frame */
walkState->userData1 = (void *) ((UDATA)walkState->userData1 & FRAME_FILTER_MASK);
walkState->userData1 = (void *) ((UDATA)walkState->userData1 & J9_FRAME_FILTER_MASK);
if (J9_STACKWALK_RC_NONE != vm->walkStackFrames(vmThread, walkState)) {
vmFuncs->setNativeOutOfMemoryError(vmThread, 0, 0);
goto _done;
}
}
/* clear the valid bit */
walkState->userData1 = (void *) (((UDATA) walkState->userData1 & FRAME_FILTER_MASK));
walkState->userData1 = (void *) (((UDATA) walkState->userData1 & J9_FRAME_FILTER_MASK));

if (J9SF_FRAME_TYPE_END_OF_STACK != walkState->pc) {
J9Class * frameClass = J9VMJAVALANGSTACKWALKERSTACKFRAMEIMPL_OR_NULL(vm);
Expand All @@ -218,7 +254,7 @@ Java_java_lang_StackWalker_getImpl(JNIEnv *env, jobject clazz, jlong walkStateP)
PUSH_OBJECT_IN_SPECIAL_FRAME(vmThread, frame);

/* set the class object if requested */
if (J9_ARE_ANY_BITS_SET((UDATA) walkState->userData1, RETAIN_CLASS_REFERENCE)) {
if (J9_ARE_ANY_BITS_SET((UDATA) walkState->userData1, J9_RETAIN_CLASS_REFERENCE)) {
j9object_t classObject = J9VM_J9CLASS_TO_HEAPCLASS(ramClass);
J9VMJAVALANGSTACKWALKERSTACKFRAMEIMPL_SET_DECLARINGCLASS(vmThread, frame, classObject);
}
Expand Down Expand Up @@ -279,6 +315,39 @@ Java_java_lang_StackWalker_getImpl(JNIEnv *env, jobject clazz, jlong walkStateP)
J9VMJAVALANGSTACKWALKERSTACKFRAMEIMPL_SET_CALLERSENSITIVE(vmThread, PEEK_OBJECT_IN_SPECIAL_FRAME(vmThread, 0), TRUE);
}

#if JAVA_SPEC_VERSION >= 21
if (J9_ARE_ALL_BITS_SET((UDATA)walkState->userData1, J9_GET_MONITORS)) {
J9ObjectMonitorInfo *monitorInfo = (J9ObjectMonitorInfo *)walkState->userData3;
IDATA *monitorCount = (IDATA *)(&walkState->userData4);

/* Temp fields to find the number of monitors hold by this frame. */
J9ObjectMonitorInfo *tempInfo = monitorInfo;
U_32 count = 0;
/* Use a while loop as there may be more than one lock taken in a stack frame. */
while ((0 != *monitorCount) && ((UDATA)tempInfo->depth == walkState->framesWalked)) {
count += 1;
tempInfo += 1;
(*monitorCount) -= 1;
}
if (count > 0) {
J9Class *arrayClass = fetchArrayClass(vmThread, J9VMJAVALANGOBJECT(vm));
j9object_t monitorArray = mmFuncs->J9AllocateIndexableObject(vmThread, arrayClass, count, J9_GC_ALLOCATE_OBJECT_INSTRUMENTABLE);
if (NULL == monitorArray) {
vmFuncs->setHeapOutOfMemoryError(vmThread);
goto _pop_frame;
}
J9VMJAVALANGSTACKWALKERSTACKFRAMEIMPL_SET_MONITORS(vmThread, PEEK_OBJECT_IN_SPECIAL_FRAME(vmThread, 0), monitorArray);
for (U_32 i = 0; i < count; i++) {
J9JAVAARRAYOFOBJECT_STORE(vmThread, monitorArray, i, monitorInfo->object);
monitorInfo += 1;
}

/* Store the updated progress back in userData for the next callback. */
walkState->userData3 = monitorInfo;
}
}
#endif /* JAVA_SPEC_VERSION >= 21 */

_pop_frame:
DROP_OBJECT_IN_SPECIAL_FRAME(vmThread);
}
Expand Down
1 change: 1 addition & 0 deletions runtime/oti/vmconstantpool.xml
Expand Up @@ -219,6 +219,7 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-ex
<fieldref class="java/lang/StackWalker$StackFrameImpl" name="methodName" signature="Ljava/lang/String;" versions="9-"/>
<fieldref class="java/lang/StackWalker$StackFrameImpl" name="methodSignature" signature="Ljava/lang/String;" versions="9-"/>
<fieldref class="java/lang/StackWalker$StackFrameImpl" name="frameModule" signature="Ljava/lang/Module;" versions="9-"/>
<fieldref class="java/lang/StackWalker$StackFrameImpl" name="monitors" signature="[Ljava/lang/Object;" versions="21-"/>

<!-- Common field references shared between OpenJ9 and OpenJDK Thread. -->
<fieldref class="java/lang/Thread" name="contextClassLoader" signature="Ljava/lang/ClassLoader;"/>
Expand Down

0 comments on commit 869cc38

Please sign in to comment.