diff --git a/.github/workflows/build-common.yml b/.github/workflows/build-common.yml index 0aafaa966f86..e93b07926bc4 100644 --- a/.github/workflows/build-common.yml +++ b/.github/workflows/build-common.yml @@ -242,7 +242,8 @@ jobs: - 17 - 21 - 23 - - 24-ea + - 24 + - 25-ea vm: - hotspot - openj9 @@ -256,7 +257,11 @@ jobs: - true exclude: - vm: ${{ inputs.skip-openj9-tests && 'openj9' || '' }} - - test-java-version: 24-ea + - test-java-version: 23 + vm: hotspot + - test-java-version: 24 + vm: openj9 + - test-java-version: 25-ea vm: openj9 fail-fast: false steps: diff --git a/custom-checks/build.gradle.kts b/custom-checks/build.gradle.kts index b8e2875067ee..790a54a28f92 100644 --- a/custom-checks/build.gradle.kts +++ b/custom-checks/build.gradle.kts @@ -13,6 +13,8 @@ dependencies { otelJava { minJavaVersionSupported.set(JavaVersion.VERSION_17) + // OtelInternalJavadocTest fails with 25-ea + maxJavaVersionForTests.set(JavaVersion.VERSION_24) } // We cannot use "--release" javac option here because that will forbid exporting com.sun.tools package. diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index 76c034d4ea65..87bc173249bd 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -226,7 +226,7 @@ These are the JVMs and operating systems that the integration tests are run agai | JVM | Versions | OS | |-------------------------------------------------------------------------------------------|-------------------|-------------------| -| [OpenJDK (Eclipse Temurin)](https://adoptium.net/) | 8, 11, 17, 21, 23 | [`ubuntu-latest`] | +| [OpenJDK (Eclipse Temurin)](https://adoptium.net/) | 8, 11, 17, 21, 24 | [`ubuntu-latest`] | | [OpenJ9 (IBM Semeru Runtimes)](https://developer.ibm.com/languages/java/semeru-runtimes/) | 8, 11, 17, 21, 23 | [`ubuntu-latest`] | ## Disabled instrumentations diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java17/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java17/JfrRuntimeMetricsTest.java b/instrumentation/runtime-telemetry/runtime-telemetry-java17/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java17/JfrRuntimeMetricsTest.java index 2e5daaf79f6b..f2fd093447b0 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java17/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java17/JfrRuntimeMetricsTest.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java17/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java17/JfrRuntimeMetricsTest.java @@ -35,7 +35,6 @@ void shouldHaveDefaultMetrics() { testing.waitAndAssertMetrics( "io.opentelemetry.runtime-telemetry-java17", - metric -> metric.hasName("jvm.cpu.longlock"), metric -> metric.hasName("jvm.cpu.limit"), metric -> metric.hasName("jvm.cpu.context_switch")); } diff --git a/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/build.gradle.kts b/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/build.gradle.kts index 3d2789c10a14..5fc594110c2d 100644 --- a/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/build.gradle.kts +++ b/instrumentation/xxl-job/xxl-job-1.9.2/javaagent/build.gradle.kts @@ -11,6 +11,11 @@ muzzle { } } +otelJava { + // groovy does not support 25-ea + maxJavaVersionForTests.set(JavaVersion.VERSION_24) +} + dependencies { library("com.xuxueli:xxl-job-core:1.9.2") { exclude("org.codehaus.groovy", "groovy") diff --git a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/build.gradle.kts b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/build.gradle.kts index 28e84d040cf7..d5c90946b2b7 100644 --- a/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/build.gradle.kts +++ b/instrumentation/xxl-job/xxl-job-2.1.2/javaagent/build.gradle.kts @@ -11,6 +11,11 @@ muzzle { } } +otelJava { + // groovy does not support 25-ea + maxJavaVersionForTests.set(JavaVersion.VERSION_24) +} + dependencies { library("com.xuxueli:xxl-job-core:2.1.2") { exclude("org.codehaus.groovy", "groovy") diff --git a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/build.gradle.kts b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/build.gradle.kts index 56e5c7a630bb..c194009114bc 100644 --- a/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/build.gradle.kts +++ b/instrumentation/xxl-job/xxl-job-2.3.0/javaagent/build.gradle.kts @@ -11,6 +11,11 @@ muzzle { } } +otelJava { + // groovy does not support 25-ea + maxJavaVersionForTests.set(JavaVersion.VERSION_24) +} + dependencies { library("com.xuxueli:xxl-job-core:2.3.0") { exclude("org.codehaus.groovy", "groovy") diff --git a/javaagent-tooling/javaagent-tooling-java9/src/main/java/io/opentelemetry/javaagent/tooling/SunMiscUnsafeGenerator.java b/javaagent-tooling/javaagent-tooling-java9/src/main/java/io/opentelemetry/javaagent/tooling/SunMiscUnsafeGenerator.java index 313e5fb83702..85a307ff249a 100644 --- a/javaagent-tooling/javaagent-tooling-java9/src/main/java/io/opentelemetry/javaagent/tooling/SunMiscUnsafeGenerator.java +++ b/javaagent-tooling/javaagent-tooling-java9/src/main/java/io/opentelemetry/javaagent/tooling/SunMiscUnsafeGenerator.java @@ -18,6 +18,7 @@ import static org.objectweb.asm.Opcodes.INVOKESTATIC; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.IRETURN; +import static org.objectweb.asm.Opcodes.L2I; import static org.objectweb.asm.Opcodes.NEW; import static org.objectweb.asm.Opcodes.PUTSTATIC; import static org.objectweb.asm.Opcodes.RETURN; @@ -82,21 +83,28 @@ private void addFields() { addField("ADDRESS_SIZE", int.class); } - private boolean hasSuitableField(String name, Class type) { + private Field findSourceField(String name) { try { - Field field = internalUnsafeClass.getDeclaredField(name); - return Modifier.isPublic(field.getModifiers()) && field.getType() == type; + return internalUnsafeClass.getDeclaredField(name); } catch (NoSuchFieldException exception) { - return false; + return null; } } + private boolean isSuitableField(Field field, Class type) { + // jdk25 changes field type from int to long for offsets + return field != null + && Modifier.isPublic(field.getModifiers()) + && (field.getType() == type || (field.getType() == long.class && type == int.class)); + } + private void addField(String name, Class type) { - if (!hasSuitableField(name, type)) { + Field field = findSourceField(name); + if (!isSuitableField(field, type)) { throw new IllegalStateException( "Could not find suitable field for " + name + " " + Type.getDescriptor(type)); } - fields.add(new FieldDescriptor(name, type)); + fields.add(new FieldDescriptor(name, type, field.getType())); } private void addMethods() { @@ -231,14 +239,15 @@ private void addMethod( List targetNameCandidates, Class returnType, Class... parameterTypes) { - String targetName = null; + Method target = null; for (String candidate : targetNameCandidates) { - if (hasSuitableMethod(candidate, returnType, parameterTypes)) { - targetName = candidate; + Method method = findSourceMethod(candidate, parameterTypes); + if (isSuitableMethod(method, returnType)) { + target = method; break; } } - if (targetName == null) { + if (target == null) { if (optional) { return; } @@ -248,18 +257,27 @@ private void addMethod( + " " + Type.getMethodDescriptor(Type.getType(returnType), toTypes(parameterTypes))); } - methods.add(new MethodDescriptor(name, targetName, returnType, parameterTypes)); + methods.add( + new MethodDescriptor( + name, target.getName(), returnType, target.getReturnType(), parameterTypes)); } - private boolean hasSuitableMethod(String name, Class returnType, Class... parameterTypes) { + private Method findSourceMethod(String name, Class... parameterTypes) { try { - Method method = internalUnsafeClass.getDeclaredMethod(name, parameterTypes); - return Modifier.isPublic(method.getModifiers()) && method.getReturnType() == returnType; - } catch (NoSuchMethodException e) { - return false; + return internalUnsafeClass.getDeclaredMethod(name, parameterTypes); + } catch (NoSuchMethodException exception) { + return null; } } + private boolean isSuitableMethod(Method method, Class returnType) { + // jdk25 changes method return type from int to long for methods returning fields offsets + return method != null + && Modifier.isPublic(method.getModifiers()) + && (method.getReturnType() == returnType + || (method.getReturnType() == long.class && returnType == int.class)); + } + private static Type[] toTypes(Class... classes) { Type[] result = new Type[classes.length]; for (int i = 0; i < classes.length; i++) { @@ -270,25 +288,33 @@ private static Type[] toTypes(Class... classes) { private static class FieldDescriptor { final String name; - final Class type; + final Class targetType; + final Class sourceType; - FieldDescriptor(String name, Class type) { + FieldDescriptor(String name, Class targetType, Class sourceType) { this.name = name; - this.type = type; + this.targetType = targetType; + this.sourceType = sourceType; } } private static class MethodDescriptor { final String name; final String targetName; - final Class returnType; + final Class targetReturnType; + final Class sourceReturnType; final Class[] parameterTypes; MethodDescriptor( - String name, String targetName, Class returnType, Class[] parameterTypes) { + String name, + String targetName, + Class targetReturnType, + Class sourceReturnType, + Class[] parameterTypes) { this.name = name; this.targetName = targetName; - this.returnType = returnType; + this.targetReturnType = targetReturnType; + this.sourceReturnType = sourceReturnType; this.parameterTypes = parameterTypes; } } @@ -329,7 +355,7 @@ private byte[] getBytes() { cv.visitField( ACC_PUBLIC | ACC_STATIC | ACC_FINAL, field.name, - Type.getDescriptor(field.type), + Type.getDescriptor(field.targetType), null, null); fv.visitEnd(); @@ -337,8 +363,10 @@ private byte[] getBytes() { for (MethodDescriptor method : methods) { Type[] parameters = toTypes(method.parameterTypes); - Type returnType = Type.getType(method.returnType); + Type returnType = Type.getType(method.targetReturnType); String descriptor = Type.getMethodDescriptor(returnType, parameters); + String sourceDescriptor = + Type.getMethodDescriptor(Type.getType(method.sourceReturnType), parameters); MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, method.name, descriptor, null, null); mv.visitCode(); mv.visitFieldInsn( @@ -352,8 +380,13 @@ private byte[] getBytes() { INVOKEVIRTUAL, Type.getInternalName(internalUnsafeClass), method.targetName, - descriptor, + sourceDescriptor, false); + if (method.targetReturnType != method.sourceReturnType + && method.targetReturnType == int.class + && method.sourceReturnType == long.class) { + mv.visitInsn(L2I); + } mv.visitInsn(returnType.getOpcode(IRETURN)); mv.visitMaxs(0, 0); mv.visitEnd(); @@ -385,8 +418,13 @@ private byte[] getBytes() { GETSTATIC, Type.getInternalName(internalUnsafeClass), field.name, - Type.getDescriptor(field.type)); - mv.visitFieldInsn(PUTSTATIC, UNSAFE_NAME, field.name, Type.getDescriptor(field.type)); + Type.getDescriptor(field.sourceType)); + if (field.sourceType != field.targetType + && field.targetType == int.class + && field.sourceType == long.class) { + mv.visitInsn(L2I); + } + mv.visitFieldInsn(PUTSTATIC, UNSAFE_NAME, field.name, Type.getDescriptor(field.targetType)); } mv.visitInsn(RETURN);