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

Rmi instrumentation on jdk17 #4577

Merged
merged 4 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.rmi.context.jpms;

import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;

import io.opentelemetry.instrumentation.api.InstrumentationVersion;
import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.loading.ClassInjector;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.utility.JavaModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExposeRmiModuleInstrumentation implements TypeInstrumentation {
private static final Logger logger =
LoggerFactory.getLogger(ExposeRmiModuleInstrumentation.class);

private final AtomicBoolean instrumented = new AtomicBoolean();

@Override
public ElementMatcher<TypeDescription> typeMatcher() {
ElementMatcher.Junction<TypeDescription> notInstrumented =
new ElementMatcher.Junction.AbstractBase<TypeDescription>() {

@Override
public boolean matches(TypeDescription target) {
return !instrumented.get();
}
};

return notInstrumented.and(nameStartsWith("sun.rmi"));
}
Comment on lines +29 to +41
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good trick to file away 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it this way mostly because for every class that matches this gets logged

io.opentelemetry.javaagent.tooling.AgentInstaller$TransformLoggingListener - Transformed sun.rmi.server.WeakClassHashMap -- null

What I really wanted was to make this module start before RmiContextPropagationInstrumentationModule so that io.opentelemetry.javaagent.instrumentation.rmi.context.server.ContextDispatcher could access sun.rmi.server.Dispatcher. Perhaps a cleaner alternative would be to have some sort of api for declaring dependency on an internal package so this could be handled in HelperInjector.


@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
return new ElementMatcher.Junction.AbstractBase<ClassLoader>() {

@Override
public boolean matches(ClassLoader target) {
// runs only in bootstrap class loader
return JavaModule.isSupported() && target == null;
}
};
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyTransformer(
(builder, typeDescription, classLoader, module) -> {
if (module != null && module.isNamed()) {
// using InstrumentationVersion because it's in the unnamed module in the bootstrap
// loader, and that's where the rmi instrumentation helper classes will end up
JavaModule helperModule = JavaModule.ofType(InstrumentationVersion.class);
// expose sun.rmi.server package to unnamed module
ClassInjector.UsingInstrumentation.redefineModule(
InstrumentationHolder.getInstrumentation(),
module,
Collections.emptySet(),
Collections.singletonMap("sun.rmi.server", Collections.singleton(helperModule)),
Collections.emptyMap(),
Collections.emptySet(),
Collections.emptyMap());

instrumented.set(true);
logger.debug(
"Exposed package \"sun.rmi.server\" in module {} to unnamed module", module);
}
return builder;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.rmi.context.jpms;

import static java.util.Collections.singletonList;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.instrumentation.rmi.context.server.ContextDispatcher;
import java.util.List;

/**
* RMI server instrumentation class {@link ContextDispatcher} implements an internal interface that
* is not exported by java module system. This is not allowed on jdk17. This instrumentation module
* exposes JDK internal classes for RMI server instrumentation.
*/
@AutoService(InstrumentationModule.class)
public class RmiJpmsInstrumentationModule extends InstrumentationModule {

public RmiJpmsInstrumentationModule() {
super("rmi", "rmi-jpms");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ExposeRmiModuleInstrumentation());
}
}