Skip to content

Commit

Permalink
feat: support ignore transform by configured type or classloader pref…
Browse files Browse the repository at this point in the history
…ixes (#514)

- Ignore types by setting system properties by -Darex.ignore.type.prefixes=xxx.type, separate multiple values with commas.
- Ignore classloader by setting system properties by -Darex.ignore.classloader.prefixes=xxx.classloader, separate multiple values with commas.
---------

Co-authored-by: mr3 <[email protected]>
  • Loading branch information
shichanglin5 and mr3 authored Jun 27, 2024
1 parent 9f310d1 commit fd8501f
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ private ConfigConstants() {
public static final String ALLOW_TIME_FROM = "arex.allow.time.from";
public static final String ALLOW_TIME_TO = "arex.allow.time.to";

/**
* Assign values via -Darex.ignore.type.prefixes=xxx.type, separate multiple values with commas.
*/
public static final String IGNORED_TYPE_PREFIXES = "arex.ignore.type.prefixes";
/**
* Assign values via -Darex.ignore.classloader.prefixes=xxx.classloader, separate multiple values with commas.
*/
public static final String IGNORED_CLASS_LOADER_PREFIXES = "arex.ignore.classloader.prefixes";
public static final String DISABLE_MODULE = "arex.disable.instrumentation.module";
public static final String RETRANSFORM_MODULE = "arex.retransform.instrumentation.module";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import io.arex.foundation.config.ConfigManager;
import io.arex.agent.bootstrap.util.CollectionUtil;

import io.arex.inst.extension.matcher.IgnoredTypesMatcher;
import io.arex.inst.extension.matcher.IgnoredRawMatcher;
import io.arex.inst.runtime.model.DynamicClassEntity;
import io.arex.inst.runtime.model.DynamicClassStatusEnum;
import io.arex.agent.bootstrap.util.ServiceLoader;
Expand Down Expand Up @@ -181,11 +181,13 @@ private AgentBuilder.Identified.Extendable installMethod(AgentBuilder.Identified
private AgentBuilder getAgentBuilder() {
// config may use to add some classes to be ignored in future
long buildBegin = System.currentTimeMillis();
AgentBuilder builder = new AgentBuilder.Default(

return new AgentBuilder.Default(
new ByteBuddy().with(MethodGraph.Compiler.ForDeclaredMethods.INSTANCE))
.enableNativeMethodPrefix("arex_")
.disableClassFormatChanges()
.ignore(new IgnoredTypesMatcher())
.ignore(new IgnoredRawMatcher(ConfigManager.INSTANCE.getIgnoreTypePrefixes(),
ConfigManager.INSTANCE.getIgnoreClassLoaderPrefixes()))
.with(new TransformListener())
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
Expand All @@ -194,8 +196,6 @@ private AgentBuilder getAgentBuilder() {
.with(AgentBuilder.DescriptionStrategy.Default.POOL_FIRST)
.with(AgentBuilder.LocationStrategy.ForClassLoader.STRONG
.withFallbackTo(ClassFileLocator.ForClassLoader.ofSystemLoader()));

return builder;
}

private boolean disabledModule(String moduleName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package io.arex.inst.extension.matcher;

import io.arex.agent.bootstrap.util.CollectionUtil;
import io.arex.agent.bootstrap.util.StringUtil;
import java.util.List;
import net.bytebuddy.matcher.ElementMatcher;

public class IgnoreClassloaderMatcher extends ElementMatcher.Junction.AbstractBase<ClassLoader> {

static final String BYTE_BUDDY_PREFIX = StringUtil.removeShadePrefix("net.bytebuddy.");
private final ElementMatcher<ClassLoader> matcher;

private static final List<String> IGNORED_CLASSLOADER_PREFIXES = CollectionUtil.newArrayList(
"sun.reflect.", "jdk.internal.reflect.", IgnoreClassloaderMatcher.BYTE_BUDDY_PREFIX);
private ElementMatcher<ClassLoader> matcher;

public IgnoreClassloaderMatcher(List<String> ignoreClassLoaderPrefixes) {
IGNORED_CLASSLOADER_PREFIXES.addAll(ignoreClassLoaderPrefixes);
}

public IgnoreClassloaderMatcher(ElementMatcher<ClassLoader> matcher) {
this.matcher = matcher;
Expand All @@ -19,9 +28,14 @@ public boolean matches(ClassLoader loader) {
}

String loaderName = loader.getClass().getName();
if (loaderName.startsWith("sun.reflect") ||
loaderName.startsWith("jdk.internal.reflect") ||
loaderName.startsWith(BYTE_BUDDY_PREFIX)) {

for (String ignored : IGNORED_CLASSLOADER_PREFIXES) {
if (loaderName.startsWith(ignored)) {
return true;
}
}

if (matcher == null) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.arex.inst.extension.matcher;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.utility.JavaModule;
import net.bytebuddy.utility.nullability.MaybeNull;

import java.security.ProtectionDomain;
import java.util.List;

public class IgnoredRawMatcher implements AgentBuilder.RawMatcher {
private final ElementMatcher.Junction<TypeDescription> typeMatcher;
private final ElementMatcher.Junction<ClassLoader> classLoaderMatcher;

public IgnoredRawMatcher(List<String> ignoreTypePrefixes, List<String> ignoreClassLoaderPrefixes) {
this.typeMatcher = new IgnoredTypesMatcher(ignoreTypePrefixes);
this.classLoaderMatcher = new IgnoreClassloaderMatcher(ignoreClassLoaderPrefixes);
}

@Override
public boolean matches(TypeDescription typeDescription,
@MaybeNull ClassLoader classLoader,
@MaybeNull JavaModule module,
@MaybeNull Class<?> classBeingRedefined,
ProtectionDomain protectionDomain) {
return typeMatcher.matches(typeDescription) || classLoaderMatcher.matches(classLoader);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package io.arex.inst.extension.matcher;

import io.arex.agent.bootstrap.cache.AdviceInjectorCache;
import io.arex.agent.bootstrap.util.CollectionUtil;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

import java.util.List;

public class IgnoredTypesMatcher extends ElementMatcher.Junction.AbstractBase<TypeDescription> {
private static final String[] IGNORED_STARTS_WITH_NAME = new String[]{
private static final List<String> IGNORED_TYPE_PREFIXES = CollectionUtil.newArrayList(
"io.arex.", "shaded.", IgnoreClassloaderMatcher.BYTE_BUDDY_PREFIX,
"sun.reflect.", "org.springframework.boot.autoconfigure", "com.intellij."};
"sun.reflect.", "org.springframework.boot.autoconfigure", "com.intellij.");

private static final String[] IGNORED_CONTAINS_NAME = new String[]{"javassist.", ".asm.", ".reflectasm.", IgnoreClassloaderMatcher.BYTE_BUDDY_PREFIX};

private static final String[] IGNORED_CONTAINS_NAME = new String[]{"javassist.", ".asm.", ".reflectasm."};
public IgnoredTypesMatcher(List<String> ignoreTypePrefixes) {
IGNORED_TYPE_PREFIXES.addAll(ignoreTypePrefixes);
}

@Override
public boolean matches(TypeDescription target) {
Expand All @@ -18,7 +25,7 @@ public boolean matches(TypeDescription target) {
}
String name = target.getActualName();

for (String ignored : IGNORED_STARTS_WITH_NAME) {
for (String ignored : IGNORED_TYPE_PREFIXES) {
if (name.startsWith(ignored)) {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,37 @@

import static org.junit.jupiter.api.Assertions.*;

import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.util.Collections;

import org.junit.jupiter.api.Test;

class IgnoreClassloaderMatcherTest {

@Test
void matches() {
void testMatchesMatcher() {
IgnoreClassloaderMatcher matcher = new IgnoreClassloaderMatcher(
new HasClassNameMatcher("io.arex.inst.extension.matcher.HasClassNameMatcher"));

assertFalse(matcher.matches(null));

assertTrue(matcher.matches(Thread.currentThread().getContextClassLoader()));
}
}

@Test
void testMatchesIgnoredLoaders() {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
IgnoreClassloaderMatcher matcher = new IgnoreClassloaderMatcher(Collections.singletonList(contextClassLoader.getClass().getName()));

assertFalse(matcher.matches(null));

// url class loader not matches ignored class loaders
assertFalse(matcher.matches(URLClassLoader.class.getClassLoader()));

// app class loader, matches ignored class loaders
assertTrue(matcher.matches(contextClassLoader), () -> "contextClassLoader: " + contextClassLoader.getClass().getName());

// matcher is null
assertFalse(matcher.matches(contextClassLoader.getParent()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.arex.inst.extension.matcher;

import net.bytebuddy.description.type.TypeDescription;
import org.junit.jupiter.api.Test;

import java.security.ProtectionDomain;
import java.util.Collections;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class IgnoredRawMatcherTest {

@Test
void matches() {
ProtectionDomain protectionDomain = new ProtectionDomain(null, null);
TypeDescription.ForLoadedType typeDescription = new TypeDescription.ForLoadedType(String.class);
ClassLoader classLoader = IgnoredRawMatcherTest.class.getClassLoader();

// case 1
// ignore type: false (classloader is null)
IgnoredRawMatcher matcher = new IgnoredRawMatcher(Collections.emptyList(), Collections.emptyList());
assertFalse(matcher.matches(typeDescription, null, null, null, protectionDomain));

// case 2
// ignore type: false
// ignore classloader: false
matcher = new IgnoredRawMatcher(Collections.emptyList(), Collections.emptyList());
assertFalse(matcher.matches(typeDescription, classLoader, null, null, protectionDomain));

// case 3
// ignore type: true
// ignore classloader: false
matcher = new IgnoredRawMatcher(Collections.singletonList(typeDescription.getActualName()), Collections.emptyList());
assertTrue(matcher.matches(typeDescription, classLoader, null, null, protectionDomain));

// case 4
// ignore type: false
// ignore classloader: true
matcher = new IgnoredRawMatcher(Collections.emptyList(), Collections.singletonList(classLoader.getClass().getName()));
assertTrue(matcher.matches(typeDescription, classLoader, null, null, protectionDomain));

// case 3
// ignore type: true
// ignore classloader: true
assertTrue(matcher.matches(typeDescription, classLoader, null, null, protectionDomain));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import net.bytebuddy.description.type.TypeDescription;
import org.junit.jupiter.api.Test;

import java.util.Collections;

import static org.junit.jupiter.api.Assertions.*;

class IgnoredTypesMatcherTest {

@Test
void matches() {
IgnoredTypesMatcher matcher = new IgnoredTypesMatcher();
IgnoredTypesMatcher matcher = new IgnoredTypesMatcher(Collections.emptyList());
assertTrue(matcher.matches(new TypeDescription.ForLoadedType(IgnoredTypesMatcher.class)));
assertFalse(matcher.matches(new TypeDescription.ForLoadedType(String.class)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public class ConfigManager {
private EnumSet<DayOfWeek> allowDayOfWeeks;
private LocalTime allowTimeOfDayFrom;
private LocalTime allowTimeOfDayTo;
private List<String> ignoreTypePrefixes;
private List<String> ignoreClassLoaderPrefixes;
private List<String> disabledModules;
private List<String> retransformModules;
private Set<String> excludeServiceOperations;
Expand Down Expand Up @@ -263,6 +265,8 @@ void init() {
setAllowDayOfWeeks(Integer.parseInt(System.getProperty(ALLOW_DAY_WEEKS, "127")));
setAllowTimeOfDayFrom(System.getProperty(ALLOW_TIME_FROM, "00:01"));
setAllowTimeOfDayTo(System.getProperty(ALLOW_TIME_TO, "23:59"));
setIgnoreTypePrefixes(System.getProperty(IGNORED_TYPE_PREFIXES));
setIgnoreClassLoaderPrefixes(System.getProperty(IGNORED_CLASS_LOADER_PREFIXES));
setDisabledModules(System.getProperty(DISABLE_MODULE));
setRetransformModules(System.getProperty(RETRANSFORM_MODULE, "dynamic-class"));
setExcludeServiceOperations(System.getProperty(EXCLUDE_SERVICE_OPERATION));
Expand Down Expand Up @@ -500,6 +504,30 @@ private long nextWorkTime() {
return Duration.between(LocalDateTime.now(), nextTime).toMillis();
}

public List<String> getIgnoreTypePrefixes() {
return ignoreTypePrefixes;
}

private void setIgnoreTypePrefixes(String ignoredTypes) {
if (StringUtil.isEmpty(ignoredTypes)) {
this.ignoreTypePrefixes = Collections.emptyList();
} else {
this.ignoreTypePrefixes = Arrays.asList(StringUtil.split(ignoredTypes, ','));
}
}

public List<String> getIgnoreClassLoaderPrefixes() {
return ignoreClassLoaderPrefixes;
}

private void setIgnoreClassLoaderPrefixes(String ignoreClassLoaderPrefixes) {
if (StringUtil.isEmpty(ignoreClassLoaderPrefixes)) {
this.ignoreClassLoaderPrefixes = Collections.emptyList();
} else {
this.ignoreClassLoaderPrefixes = Arrays.asList(StringUtil.split(ignoreClassLoaderPrefixes, ','));
}
}

public List<String> getDisabledModules() {
return disabledModules;
}
Expand Down

0 comments on commit fd8501f

Please sign in to comment.