Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Java21] Fix failing debugger for java 21 #43448

Merged
merged 13 commits into from
Oct 8, 2024
2 changes: 1 addition & 1 deletion .github/workflows/pull_request_ubuntu_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
run: |
export DISPLAY=':99.0'
/usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
./gradlew build -x jballerina-debugger-integration-test:test -Pnative.test --max-workers=2 --scan --no-daemon
./gradlew build -Pnative.test --max-workers=2 --scan --no-daemon
- name: Generate Jacoco report
if: github.event_name == 'pull_request'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull_request_windows_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,5 @@ jobs:
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew.bat build --continue -x :ballerina-lang:test -x :jballerina-integration-test:test -x jballerina-debugger-integration-test:test -x javadoc --stacktrace -scan --console=plain --no-daemon --no-parallel
run: ./gradlew.bat build --continue -x :ballerina-lang:test -x :jballerina-integration-test:test -x javadoc --stacktrace -scan --console=plain --no-daemon --no-parallel

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.ballerina.runtime.internal;

import io.ballerina.runtime.api.Module;
import io.ballerina.runtime.internal.values.ValueCreator;

/**
* This class represents the Ballerina runtime that is created using a classloader for internal purposes.
Expand All @@ -37,4 +38,10 @@ Class<?> loadClass(String className) throws ClassNotFoundException {
String name = getFullQualifiedClassName(rootModule, className);
return Class.forName(name, true, classLoader);
}

@Override
public void stop() {
super.stop();
ValueCreator.removeValueCreator(rootModule);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ public static ValueCreator getValueCreator(String key) {
return runtimeValueCreators.get(key);
}

public static void removeValueCreator(Module rootModule) {
String key = getLookupKey(rootModule, false);
runtimeValueCreators.remove(key);
}

public Object call(Strand strand, String funcName, Object... args) throws BError {
throw new ErrorValue(StringUtils.fromString("No such method: " + funcName));
}
Expand Down
3 changes: 2 additions & 1 deletion bvm/ballerina-runtime/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
io.ballerina.lang.table, io.ballerina.lang.value, io.ballerina.lang.xml, ballerina.debug.adapter.core,
io.ballerina.cli, io.ballerina.lang.integer, io.ballerina.lang.bool, io.ballerina.lang.decimal,
io.ballerina.lang.floatingpoint, io.ballerina.lang.internal, io.ballerina.lang.function,
io.ballerina.lang.regexp, io.ballerina.runtime.profiler, io.ballerina.shell;
io.ballerina.lang.regexp, io.ballerina.runtime.profiler, io.ballerina.shell,
org.ballerinalang.debugadapter.runtime;
exports io.ballerina.runtime.internal.commons to io.ballerina.lang.value;
exports io.ballerina.runtime.internal.launch to io.ballerina.testerina.runtime, io.ballerina.packerina,
ballerina.test.listener, io.ballerina.cli, org.ballerinalang.debugadapter.runtime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@
*/
// TODO move this class to a separate Java package. e.g. io.ballerina.projects.platform.jballerina
public enum JvmTarget implements CompilerBackend.TargetPlatform {
JAVA_17("java17"),
JAVA_21("java21"),
JAVA_17("java17"),
JAVA_11("java11");
// TODO need to move java21 to the top when the central issue #2792 is fixed

private final String code;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,10 @@ Map<Integer, ThreadReferenceProxyImpl> getAllThreads() {
threadsMap.put((int) threadReference.uniqueID(), new ThreadReferenceProxyImpl(context.getDebuggeeVM(),
threadReference));
}
for (ThreadReference threadReference: eventProcessor.getVirtualThreads()) {
threadsMap.put((int) threadReference.uniqueID(), new ThreadReferenceProxyImpl(context.getDebuggeeVM(),
threadReference));
}
return threadsMap;
}

Expand Down Expand Up @@ -1105,6 +1109,8 @@ private void attachToRemoteVM(String hostName, int portName) throws IOException,
EventRequestManager erm = context.getEventManager();
ClassPrepareRequest classPrepareRequest = erm.createClassPrepareRequest();
classPrepareRequest.enable();
HindujaB marked this conversation as resolved.
Show resolved Hide resolved
erm.createThreadStartRequest().enable();
erm.createThreadDeathRequest().enable();
eventProcessor.startListening();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@

import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.Location;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.event.BreakpointEvent;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventIterator;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.event.StepEvent;
import com.sun.jdi.event.ThreadDeathEvent;
import com.sun.jdi.event.ThreadStartEvent;
import com.sun.jdi.event.VMDeathEvent;
import com.sun.jdi.event.VMDisconnectEvent;
import com.sun.jdi.request.EventRequest;
Expand Down Expand Up @@ -57,6 +60,7 @@ public class JDIEventProcessor {
private boolean isRemoteVmAttached = false;
private final List<EventRequest> stepRequests = new ArrayList<>();
private static final Logger LOGGER = LoggerFactory.getLogger(JDIEventProcessor.class);
private static final List<ThreadReference> virtualThreads = new ArrayList<>();
HindujaB marked this conversation as resolved.
Show resolved Hide resolved

JDIEventProcessor(ExecutionContext context) {
this.context = context;
Expand All @@ -71,6 +75,10 @@ List<EventRequest> getStepRequests() {
return stepRequests;
}

List<ThreadReference> getVirtualThreads() {
return virtualThreads;
}

/**
* Asynchronously listens and processes the incoming JDI events.
*/
Expand Down Expand Up @@ -121,6 +129,20 @@ private void processEvent(EventSet eventSet, Event event) {
|| event instanceof VMDeathEvent
|| event instanceof VMDisconnectedException) {
isRemoteVmAttached = false;
} else if (event instanceof ThreadStartEvent threadStartEvent) {
ThreadReference thread = threadStartEvent.thread();
if (thread.isVirtual()) {
synchronized (virtualThreads) {
virtualThreads.add(thread);
}
}
eventSet.resume();
} else if (event instanceof ThreadDeathEvent threadDeathEvent) {
ThreadReference thread = threadDeathEvent.thread();
synchronized (virtualThreads) {
virtualThreads.remove(thread);
}
eventSet.resume();
HindujaB marked this conversation as resolved.
Show resolved Hide resolved
} else {
eventSet.resume();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import static org.ballerinalang.debugadapter.evaluation.EvaluationException.createEvaluationException;
import static org.ballerinalang.debugadapter.evaluation.EvaluationExceptionKind.FUNCTION_EXECUTION_ERROR;
import static org.ballerinalang.debugadapter.evaluation.utils.EvaluationUtils.B_DEBUGGER_RUNTIME_CLASS;
import static org.ballerinalang.debugadapter.evaluation.utils.EvaluationUtils.B_SCHEDULER_CLASS;
import static org.ballerinalang.debugadapter.evaluation.utils.EvaluationUtils.INVOKE_FUNCTION_ASYNC;
import static org.ballerinalang.debugadapter.evaluation.utils.EvaluationUtils.JAVA_LANG_CLASSLOADER;
import static org.ballerinalang.debugadapter.evaluation.utils.EvaluationUtils.JAVA_OBJECT_ARRAY_CLASS;
Expand Down Expand Up @@ -58,7 +57,6 @@ protected Value invoke() throws EvaluationException {
}
List<String> argTypeList = new ArrayList<>();
argTypeList.add(JAVA_LANG_CLASSLOADER);
argTypeList.add(B_SCHEDULER_CLASS);
argTypeList.add(JAVA_STRING_CLASS);
argTypeList.add(JAVA_STRING_CLASS);
argTypeList.add(JAVA_OBJECT_ARRAY_CLASS);
Expand All @@ -67,7 +65,6 @@ protected Value invoke() throws EvaluationException {

List<Value> scheduleMethodArgs = new ArrayList<>();
scheduleMethodArgs.add(context.getDebuggeeClassLoader());
scheduleMethodArgs.add(null);
scheduleMethodArgs.add(EvaluationUtils.getAsJString(context, classRef.name()));
scheduleMethodArgs.add(EvaluationUtils.getAsJString(context, methodRef.name()));
scheduleMethodArgs.addAll(getMethodArgs(this));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
org = "ballerina"
name = "debugger_helpers"
version = "1.0.0"

[[platform.java21.dependency]]
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
import io.ballerina.runtime.api.values.BValue;
import io.ballerina.runtime.api.values.BXml;
import io.ballerina.runtime.api.values.BXmlSequence;
import io.ballerina.runtime.internal.configurable.providers.ConfigDetails;
import io.ballerina.runtime.internal.launch.LaunchUtils;
import io.ballerina.runtime.internal.BalRuntime;
import io.ballerina.runtime.internal.ClassloaderRuntime;
import io.ballerina.runtime.internal.scheduling.Scheduler;
import io.ballerina.runtime.internal.scheduling.Strand;
import io.ballerina.runtime.internal.types.BAnnotatableType;
Expand All @@ -55,13 +55,11 @@
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -106,7 +104,7 @@ public class DebuggerRuntime {
public static Object invokeObjectMethod(BObject bObject, String methodName, Object... args) {
try {
final Object[] paramValues = args[0] instanceof Strand ? Arrays.copyOfRange(args, 1, args.length) : args;
final Strand strand = args[0] instanceof Strand s ? s : Scheduler.getStrand();
final Strand strand = args[0] instanceof Strand s ? s : Scheduler.getStrand();
return ((ObjectValue) bObject).call(strand, methodName, paramValues);
} catch (Exception e) {
throw ErrorCreator.createError(StringUtils.fromString("invocation failed: " + e.getMessage()));
Expand All @@ -130,12 +128,12 @@ public static Object invokeFunction(ClassLoader classLoader, String className, S
try {
return method.invoke(null, paramValues);
} catch (IllegalAccessException | InvocationTargetException e) {
throw ErrorCreator.createError(StringUtils.fromString(
"'" + methodName + "' function invocation failed: " + e.getMessage()));
throw ErrorCreator.createError(StringUtils.fromString("'" + methodName +
"' function invocation failed: " + e.getMessage()));
}
} catch (Exception e) {
throw ErrorCreator.createError(StringUtils.fromString(
"'" + methodName + "' function invocation failed: " + e.getMessage()));
throw ErrorCreator.createError(StringUtils.fromString("'" + methodName +
"' function invocation failed: " + e.getMessage()));
}
}

Expand All @@ -149,8 +147,8 @@ public static Object invokeFunction(ClassLoader classLoader, String className, S
* @param fieldValues field values
* @return Ballerina object instance
*/
public static Object createObjectValue(String pkgOrg, String pkgName, String pkgVersion, String objectTypeName,
Object... fieldValues) {
public static Object createObjectValue(String pkgOrg, String pkgName, String pkgVersion,
String objectTypeName, Object... fieldValues) {
Module packageId = new Module(pkgOrg, pkgName, pkgVersion);
return ValueCreator.createObjectValue(packageId, objectTypeName, fieldValues);
}
Expand Down Expand Up @@ -195,8 +193,9 @@ public static Object createErrorValue(Object message, Object cause, Object... de
}

ErrorType bErrorType = createErrorType(TypeConstants.ERROR, PredefinedTypes.TYPE_ERROR.getPackage());
BMap<BString, Object> errorDetailsMap = ValueCreator.createMapValue((MapType) PredefinedTypes.TYPE_ERROR_DETAIL,
errorDetailEntries.toArray(errorDetailEntries.toArray(new BMapInitialValueEntry[0])));
BMap<BString, Object> errorDetailsMap = ValueCreator.createMapValue((MapType)
PredefinedTypes.TYPE_ERROR_DETAIL, errorDetailEntries.toArray(
errorDetailEntries.toArray(new BMapInitialValueEntry[0])));
return ErrorCreator.createError(bErrorType, (StringValue) message, (ErrorValue) cause, errorDetailsMap);
}

Expand All @@ -209,28 +208,23 @@ public static Object createErrorValue(Object message, Object cause, Object... de
*/
public static Object getAnnotationValue(Object typedescValue, String annotationName) {
if (!(typedescValue instanceof TypedescValue)) {
return ErrorCreator.createError(StringUtils.fromString("Incompatible types: expected 'typedesc`, " +
"found '" + typedescValue.toString() + "'."));
return ErrorCreator.createError(StringUtils.fromString("Incompatible types: expected 'typedesc`, "
+ "found '" + typedescValue.toString() + "'."));
}
Type type = ((TypedescValue) typedescValue).getDescribingType();
if (type instanceof BAnnotatableType) {
return ((BAnnotatableType) type).getAnnotations().entrySet()
.stream()
.filter(annotationEntry -> annotationEntry.getKey().getValue().endsWith(annotationName))
.findFirst()
.map(Map.Entry::getValue)
.orElse(null);
return ((BAnnotatableType) type).getAnnotations().entrySet().stream().filter(annotationEntry ->
annotationEntry.getKey().getValue().endsWith(annotationName)).findFirst()
.map(Map.Entry::getValue).orElse(null);
}

return ErrorCreator.createError(StringUtils.fromString("type: '" + TypeUtils.getType(type.getEmptyValue())
+ "' does not support annotation access."));
+ "' does not support annotation access."));
}

private static Method getMethod(String functionName, Class<?> funcClass) throws NoSuchMethodException {
Method declaredMethod = Arrays.stream(funcClass.getDeclaredMethods())
.filter(method -> functionName.equals(method.getName()))
.findAny()
.orElse(null);
Method declaredMethod = Arrays.stream(funcClass.getDeclaredMethods()).filter(method ->
functionName.equals(method.getName())).findAny().orElse(null);

if (declaredMethod != null) {
return declaredMethod;
Expand Down Expand Up @@ -324,9 +318,8 @@ private static BString[] processXMLNamePattern(String xmlNamePattern) {
xmlNamePattern = stepParts[stepParts.length - 1];
}

return Arrays.stream(xmlNamePattern.split(XML_NAME_PATTERN_SEPARATOR))
.map(entry -> StringUtils.fromString(entry.trim()))
.toArray(BString[]::new);
return Arrays.stream(xmlNamePattern.split(XML_NAME_PATTERN_SEPARATOR)).map(entry ->
StringUtils.fromString(entry.trim())).toArray(BString[]::new);
}

/**
Expand All @@ -341,11 +334,6 @@ private static BString[] processXMLNamePattern(String xmlNamePattern) {
public static Object classloadAndInvokeFunction(String executablePath, String mainClass, String functionName,
Object... userArgs) {
try {
// Need to pass the strand (or null) as the first argument for the generated function.
List<Object> functionArgs = new ArrayList<>();
functionArgs.add(null);
functionArgs.addAll(Arrays.asList(userArgs));

URL pathUrl = Paths.get(executablePath).toUri().toURL();
URLClassLoader classLoader = AccessController.doPrivileged((PrivilegedAction<URLClassLoader>) () ->
new URLClassLoader(new URL[]{pathUrl}, ClassLoader.getSystemClassLoader()));
Expand All @@ -355,26 +343,35 @@ public static Object classloadAndInvokeFunction(String executablePath, String ma
String packageOrg = mainClassNameParts[0];
String packageName = mainClassNameParts[1];
String packageVersion = mainClassNameParts[2];
String packageNameSpace = String.join(".", packageOrg, packageName, packageVersion);

Module module = new Module(packageOrg, packageName, packageVersion, false);
return invokeBalRuntimeMethod(functionName, module, classLoader, userArgs);
} catch (Exception e) {
return e.getMessage();
}
}

// Initialize configurations
ConfigDetails configurationDetails = LaunchUtils.getConfigurationDetails();
invokeMethodDirectly(classLoader, String.join(".", packageNameSpace, CONFIGURE_INIT_CLASS_NAME),
CONFIGURE_INIT_METHOD_NAME, new Class[]{Map.class, String[].class, Path[].class, String.class},
new Object[]{new HashMap<>(), new String[]{}, configurationDetails.paths,
configurationDetails.configContent});
private static Object invokeBalRuntimeMethod(String functionName, Module module, ClassLoader classLoader,
Object[] paramValues) {
BalRuntime runtime = new ClassloaderRuntime(module, classLoader);
Object result;
try {
// Initialize the module
invokeFunction(classLoader, String.join(".", packageNameSpace, MODULE_INIT_CLASS_NAME),
MODULE_INIT_METHOD_NAME, new Object[1]);
runtime.init();
// Start the module
invokeFunction(classLoader, String.join(".", packageNameSpace, MODULE_INIT_CLASS_NAME),
MODULE_START_METHOD_NAME, new Object[1]);
// Run the actual method
return invokeFunction(classLoader, mainClass, functionName, functionArgs.toArray());
} catch (Exception e) {
return e.getMessage();
runtime.start();
// Then call run method
result = runtime.call(module, functionName, paramValues);
} catch (Throwable throwable) {
throw ErrorCreator.createError(StringUtils.fromString("'" + functionName + "' function " +
"invocation failed : " + throwable.getMessage()));
} finally {
try {
runtime.stop();
} catch (BError ignored) {
// stop errors are ignored
}
}
return result;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void stackFrameDebugTest() throws BallerinaTestException {
debugTestRunner.assertCallStack(frames[2], "func2", 23, "main.bal");
debugTestRunner.assertCallStack(frames[3], "func1", 19, "main.bal");
debugTestRunner.assertCallStack(frames[4], "addition", 14, "main.bal");
debugTestRunner.assertCallStack(frames[5], "start:anonymous", 2, "main.bal");
debugTestRunner.assertCallStack(frames[5], "start:f1", 2, "main.bal");

debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugResumeKind.NEXT_BREAKPOINT);
debugHitInfo = debugTestRunner.waitForDebugHit(10000);
Expand All @@ -83,7 +83,7 @@ public void stackFrameDebugTest() throws BallerinaTestException {
// Stack frame representation test for strand creation with 'start' keyword.
// Results of the strand is not assigned to any variable. In this case frame name is assigned to 'anonymous'.
debugTestRunner.assertCallStack(frames[0], "sayHello", 45, "main.bal");
debugTestRunner.assertCallStack(frames[1], "start:anonymous", 10, "main.bal");
debugTestRunner.assertCallStack(frames[1], "start:anon", 10, "main.bal");
NipunaRanasinghe marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
Expand Down
Loading
Loading