Skip to content

Commit 8fcb6b8

Browse files
JonasKunztrasklaurit
authored
Force inlining of classloading instrumentations to prevent indy recursions (#13282)
Co-authored-by: Trask Stalnaker <[email protected]> Co-authored-by: Lauri Tulmin <[email protected]>
1 parent b26b074 commit 8fcb6b8

File tree

9 files changed

+114
-201
lines changed

9 files changed

+114
-201
lines changed

.github/workflows/build-common.yml

+21-10
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ jobs:
223223
path: "sboms/*.json"
224224

225225
test:
226-
name: test${{ matrix.test-partition }} (${{ matrix.test-java-version }}, ${{ matrix.vm }})
226+
name: test${{ matrix.test-partition }} (${{ matrix.test-java-version }}, ${{ matrix.vm }}, indy ${{ matrix.test-indy }})
227227
runs-on: ubuntu-latest
228228
strategy:
229229
matrix:
@@ -242,6 +242,9 @@ jobs:
242242
- 1
243243
- 2
244244
- 3
245+
test-indy:
246+
- false
247+
- true
245248
exclude:
246249
- vm: ${{ inputs.skip-openj9-tests && 'openj9' || '' }}
247250
fail-fast: false
@@ -308,6 +311,7 @@ jobs:
308311
${{ env.test-tasks }}
309312
-PtestJavaVersion=${{ matrix.test-java-version }}
310313
-PtestJavaVM=${{ matrix.vm }}
314+
-PtestIndy=${{ matrix.test-indy }}
311315
-Porg.gradle.java.installations.paths=${{ steps.setup-test-java.outputs.path }}
312316
-Porg.gradle.java.installations.auto-download=false
313317
${{ inputs.no-build-cache && ' --no-build-cache' || '' }}
@@ -325,15 +329,22 @@ jobs:
325329
with:
326330
result-encoding: string
327331
script: |
328-
const { data: workflow_run } = await github.rest.actions.listJobsForWorkflowRun({
329-
owner: context.repo.owner,
330-
repo: context.repo.repo,
331-
run_id: context.runId,
332-
per_page: 100
333-
});
334332
const matrix = JSON.parse(process.env.matrix);
335-
const job_name = `common / test${ matrix['test-partition'] } (${ matrix['test-java-version'] }, ${ matrix.vm })`;
336-
return workflow_run.jobs.find((job) => job.name === job_name).html_url;
333+
const job_name = `common / test${ matrix['test-partition'] } (${ matrix['test-java-version'] }, ${ matrix.vm }, indy ${ matrix['test-indy'] })`;
334+
335+
const workflow_jobs_nested = await github.paginate(
336+
github.rest.actions.listJobsForWorkflowRun,
337+
{
338+
owner: context.repo.owner,
339+
repo: context.repo.repo,
340+
run_id: context.runId,
341+
per_page: 100
342+
},
343+
(response) => {
344+
return response.data;
345+
},
346+
);
347+
return workflow_jobs_nested.flat().find((job) => job.name === job_name).html_url;
337348
338349
- name: Flaky test report
339350
if: ${{ !cancelled() }}
@@ -350,7 +361,7 @@ jobs:
350361
if: failure()
351362
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
352363
with:
353-
name: deadlock-detector-test-${{ matrix.test-java-version }}-${{ matrix.vm }}-${{ matrix.test-partition }}
364+
name: deadlock-detector-test-${{ matrix.test-java-version }}-${{ matrix.vm }}-${{ matrix.test-partition }}-indy-${{ matrix.test-indy }}
354365
path: /tmp/deadlock-detector-*
355366
if-no-files-found: ignore
356367

.github/workflows/build-daily-no-build-cache.yml

-7
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,6 @@ jobs:
2424
secrets:
2525
FLAKY_TEST_REPORTER_ACCESS_KEY: ${{ secrets.FLAKY_TEST_REPORTER_ACCESS_KEY }}
2626

27-
test-indy:
28-
uses: ./.github/workflows/reusable-test-indy.yml
29-
with:
30-
no-build-cache: true
31-
secrets:
32-
FLAKY_TEST_REPORTER_ACCESS_KEY: ${{ secrets.FLAKY_TEST_REPORTER_ACCESS_KEY }}
33-
3427
# muzzle is not included here because it doesn't use gradle cache anyway and so is already covered
3528
# by the normal daily build
3629

.github/workflows/build-daily.yml

-5
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ jobs:
2020
secrets:
2121
FLAKY_TEST_REPORTER_ACCESS_KEY: ${{ secrets.FLAKY_TEST_REPORTER_ACCESS_KEY }}
2222

23-
test-indy:
24-
uses: ./.github/workflows/reusable-test-indy.yml
25-
secrets:
26-
FLAKY_TEST_REPORTER_ACCESS_KEY: ${{ secrets.FLAKY_TEST_REPORTER_ACCESS_KEY }}
27-
2823
muzzle:
2924
uses: ./.github/workflows/reusable-muzzle.yml
3025

.github/workflows/build-pull-request.yml

-5
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,6 @@ jobs:
2929
with:
3030
cache-read-only: true
3131

32-
test-indy:
33-
uses: ./.github/workflows/reusable-test-indy.yml
34-
with:
35-
cache-read-only: true
36-
3732
test-native:
3833
uses: ./.github/workflows/reusable-native-tests.yml
3934
with:

.github/workflows/reusable-test-indy.yml

-115
This file was deleted.

instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/BootDelegationInstrumentation.java

+18-46
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
package io.opentelemetry.javaagent.instrumentation.internal.classloader;
77

88
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
9-
import static java.util.logging.Level.WARNING;
109
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
1110
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
1211
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
@@ -17,17 +16,14 @@
1716
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
1817
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
1918

20-
import io.opentelemetry.javaagent.bootstrap.BootstrapPackagePrefixesHolder;
2119
import io.opentelemetry.javaagent.bootstrap.CallDepth;
2220
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
2321
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
24-
import io.opentelemetry.javaagent.tooling.Constants;
25-
import java.lang.invoke.MethodHandle;
26-
import java.lang.invoke.MethodHandles;
27-
import java.lang.invoke.MethodType;
28-
import java.util.List;
29-
import java.util.logging.Logger;
22+
import io.opentelemetry.javaagent.tooling.Utils;
23+
import io.opentelemetry.javaagent.tooling.bytebuddy.ExceptionHandlers;
24+
import net.bytebuddy.agent.builder.AgentBuilder;
3025
import net.bytebuddy.asm.Advice;
26+
import net.bytebuddy.description.method.MethodDescription;
3127
import net.bytebuddy.description.type.TypeDescription;
3228
import net.bytebuddy.matcher.ElementMatcher;
3329

@@ -53,7 +49,7 @@ public ElementMatcher<TypeDescription> typeMatcher() {
5349

5450
@Override
5551
public void transform(TypeTransformer transformer) {
56-
transformer.applyAdviceToMethod(
52+
ElementMatcher.Junction<MethodDescription> methodMatcher =
5753
isMethod()
5854
.and(named("loadClass"))
5955
.and(
@@ -64,37 +60,14 @@ public void transform(TypeTransformer transformer) {
6460
.and(takesArgument(0, String.class))
6561
.and(takesArgument(1, boolean.class))))
6662
.and(isPublic().or(isProtected()))
67-
.and(not(isStatic())),
68-
BootDelegationInstrumentation.class.getName() + "$LoadClassAdvice");
69-
}
70-
71-
public static class Holder {
72-
73-
public static final List<String> bootstrapPackagesPrefixes = findBootstrapPackagePrefixes();
74-
75-
/**
76-
* We have to make sure that {@link BootstrapPackagePrefixesHolder} is loaded from bootstrap
77-
* class loader. After that we can use in {@link LoadClassAdvice}.
78-
*/
79-
private static List<String> findBootstrapPackagePrefixes() {
80-
try {
81-
Class<?> holderClass =
82-
Class.forName(
83-
"io.opentelemetry.javaagent.bootstrap.BootstrapPackagePrefixesHolder", true, null);
84-
MethodHandle methodHandle =
85-
MethodHandles.publicLookup()
86-
.findStatic(
87-
holderClass, "getBoostrapPackagePrefixes", MethodType.methodType(List.class));
88-
//noinspection unchecked
89-
return (List<String>) methodHandle.invokeExact();
90-
} catch (Throwable e) {
91-
Logger.getLogger(Holder.class.getName())
92-
.log(WARNING, "Unable to load bootstrap package prefixes from the bootstrap CL", e);
93-
return Constants.BOOTSTRAP_PACKAGE_PREFIXES;
94-
}
95-
}
96-
97-
private Holder() {}
63+
.and(not(isStatic()));
64+
// Inline instrumentation to prevent problems with invokedynamic-recursion
65+
transformer.applyTransformer(
66+
new AgentBuilder.Transformer.ForAdvice()
67+
.include(Utils.getBootstrapProxy(), Utils.getAgentClassLoader())
68+
.withExceptionHandler(ExceptionHandlers.defaultExceptionHandler())
69+
.advice(
70+
methodMatcher, BootDelegationInstrumentation.class.getName() + "$LoadClassAdvice"));
9871
}
9972

10073
@SuppressWarnings("unused")
@@ -113,7 +86,7 @@ public static Class<?> onEnter(@Advice.Argument(0) String name) {
11386
}
11487

11588
try {
116-
for (String prefix : Holder.bootstrapPackagesPrefixes) {
89+
for (String prefix : BootstrapPackagesHelper.bootstrapPackagesPrefixes) {
11790
if (name.startsWith(prefix)) {
11891
try {
11992
return Class.forName(name, false, null);
@@ -134,13 +107,12 @@ public static Class<?> onEnter(@Advice.Argument(0) String name) {
134107
}
135108

136109
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
137-
@Advice.AssignReturned.ToReturned
138-
public static Class<?> onExit(
139-
@Advice.Return Class<?> result, @Advice.Enter Class<?> resultFromBootstrapLoader) {
110+
public static void onExit(
111+
@Advice.Return(readOnly = false) Class<?> result,
112+
@Advice.Enter Class<?> resultFromBootstrapLoader) {
140113
if (resultFromBootstrapLoader != null) {
141-
return resultFromBootstrapLoader;
114+
result = resultFromBootstrapLoader;
142115
}
143-
return result;
144116
}
145117
}
146118
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.internal.classloader;
7+
8+
import static java.util.logging.Level.WARNING;
9+
10+
import io.opentelemetry.javaagent.bootstrap.BootstrapPackagePrefixesHolder;
11+
import io.opentelemetry.javaagent.tooling.Constants;
12+
import java.lang.invoke.MethodHandle;
13+
import java.lang.invoke.MethodHandles;
14+
import java.lang.invoke.MethodType;
15+
import java.util.List;
16+
import java.util.logging.Logger;
17+
18+
public class BootstrapPackagesHelper {
19+
20+
public static final List<String> bootstrapPackagesPrefixes = findBootstrapPackagePrefixes();
21+
22+
/**
23+
* We have to make sure that {@link BootstrapPackagePrefixesHolder} is loaded from bootstrap class
24+
* loader. After that we can use in {@link BootDelegationInstrumentation.LoadClassAdvice}.
25+
*/
26+
private static List<String> findBootstrapPackagePrefixes() {
27+
try {
28+
Class<?> holderClass =
29+
Class.forName(
30+
"io.opentelemetry.javaagent.bootstrap.BootstrapPackagePrefixesHolder", true, null);
31+
MethodHandle methodHandle =
32+
MethodHandles.publicLookup()
33+
.findStatic(
34+
holderClass, "getBoostrapPackagePrefixes", MethodType.methodType(List.class));
35+
//noinspection unchecked
36+
return (List<String>) methodHandle.invokeExact();
37+
} catch (Throwable e) {
38+
Logger.getLogger(BootstrapPackagesHelper.class.getName())
39+
.log(WARNING, "Unable to load bootstrap package prefixes from the bootstrap CL", e);
40+
return Constants.BOOTSTRAP_PACKAGE_PREFIXES;
41+
}
42+
}
43+
44+
private BootstrapPackagesHelper() {}
45+
}

0 commit comments

Comments
 (0)