diff --git a/arthas-mcp-integration-test/src/test/java/com/taobao/arthas/mcp/it/ArthasMcpJavaSdkIT.java b/arthas-mcp-integration-test/src/test/java/com/taobao/arthas/mcp/it/ArthasMcpJavaSdkIT.java index 96dddbe5d3b..7e7e07834e6 100644 --- a/arthas-mcp-integration-test/src/test/java/com/taobao/arthas/mcp/it/ArthasMcpJavaSdkIT.java +++ b/arthas-mcp-integration-test/src/test/java/com/taobao/arthas/mcp/it/ArthasMcpJavaSdkIT.java @@ -68,6 +68,7 @@ class ArthasMcpJavaSdkIT { "ognl", "options", "perfcounter", + "profiler", "redefine", "retransform", "sc", @@ -81,6 +82,7 @@ class ArthasMcpJavaSdkIT { "tt", "vmoption", "vmtool", + "viewfile", "watch" ); @@ -312,6 +314,23 @@ private static Map createArgumentsForTool(String toolName, Envir return args; } + if ("profiler".equals(toolName)) { + args.put("action", "actions"); + return args; + } + + if ("viewfile".equals(toolName)) { + Path outputDir = env.tempHome.resolve("arthas-output"); + Files.createDirectories(outputDir); + Path viewFile = outputDir.resolve("viewfile-test.txt"); + Files.write(viewFile, "hello viewfile\n".getBytes(StandardCharsets.UTF_8)); + + args.put("path", "viewfile-test.txt"); + args.put("offset", 0L); + args.put("maxBytes", 1024); + return args; + } + if ("dashboard".equals(toolName)) { args.put("intervalMs", 200); args.put("numberOfExecutions", 1); diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java index a0846de86a0..9dbda6ab1af 100644 --- a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java +++ b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java @@ -75,6 +75,10 @@ public class Enhancer implements ClassFileTransformer { private final Matcher classNameMatcher; private final Matcher classNameExcludeMatcher; private final Matcher methodNameMatcher; + /** + * 指定增强的 classloader hash,如果为空则不限制。 + */ + private final String targetClassLoaderHash; private final EnhancerAffect affect; private Set> matchingClasses = null; private boolean isLazy = false; @@ -99,7 +103,7 @@ public class Enhancer implements ClassFileTransformer { public Enhancer(AdviceListener listener, boolean isTracing, boolean skipJDKTrace, Matcher classNameMatcher, Matcher classNameExcludeMatcher, Matcher methodNameMatcher) { - this(listener, isTracing, skipJDKTrace, classNameMatcher, classNameExcludeMatcher, methodNameMatcher, false); + this(listener, isTracing, skipJDKTrace, classNameMatcher, classNameExcludeMatcher, methodNameMatcher, false, null); } /** @@ -114,12 +118,19 @@ public Enhancer(AdviceListener listener, boolean isTracing, boolean skipJDKTrace public Enhancer(AdviceListener listener, boolean isTracing, boolean skipJDKTrace, Matcher classNameMatcher, Matcher classNameExcludeMatcher, Matcher methodNameMatcher, boolean isLazy) { + this(listener, isTracing, skipJDKTrace, classNameMatcher, classNameExcludeMatcher, methodNameMatcher, isLazy, null); + } + + public Enhancer(AdviceListener listener, boolean isTracing, boolean skipJDKTrace, Matcher classNameMatcher, + Matcher classNameExcludeMatcher, + Matcher methodNameMatcher, boolean isLazy, String targetClassLoaderHash) { this.listener = listener; this.isTracing = isTracing; this.skipJDKTrace = skipJDKTrace; this.classNameMatcher = classNameMatcher; this.classNameExcludeMatcher = classNameExcludeMatcher; this.methodNameMatcher = methodNameMatcher; + this.targetClassLoaderHash = targetClassLoaderHash; this.affect = new EnhancerAffect(); affect.setListenerId(listener.id()); this.isLazy = isLazy; @@ -154,6 +165,10 @@ public byte[] transform(final ClassLoader inClassLoader, String className, Class if (classNameExcludeMatcher != null && classNameExcludeMatcher.matching(classNameDot)) { return null; } + // 检查 classloader 是否匹配(指定了 targetClassLoaderHash 时生效) + if (!isTargetClassLoader(inClassLoader)) { + return null; + } // 检查是否是 arthas 自身的类 if (inClassLoader != null && isEquals(inClassLoader, selfClassLoader)) { return null; @@ -369,6 +384,9 @@ private List, String>> filter(Set> classes) { boolean removeFlag = false; if (null == clazz) { removeFlag = true; + } else if (!isTargetClassLoader(clazz.getClassLoader())) { + filteredClasses.add(new Pair, String>(clazz, "classloader is not matched")); + removeFlag = true; } else if (isSelf(clazz)) { filteredClasses.add(new Pair, String>(clazz, "class loaded by arthas itself")); removeFlag = true; @@ -457,10 +475,6 @@ public synchronized EnhancerAffect enhance(final Instrumentation inst, int maxNu ? SearchUtils.searchClass(inst, classNameMatcher) : SearchUtils.searchSubClass(inst, SearchUtils.searchClass(inst, classNameMatcher)); - if (matchingClasses.size() > maxNumOfMatchedClass) { - affect.setOverLimitMsg("The number of matched classes is " +matchingClasses.size()+ ", greater than the limit value " + maxNumOfMatchedClass + ". Try to change the limit with option '-m '."); - return affect; - } // 过滤掉无法被增强的类 List, String>> filtedList = filter(matchingClasses); if (!filtedList.isEmpty()) { @@ -469,6 +483,11 @@ public synchronized EnhancerAffect enhance(final Instrumentation inst, int maxNu } } + if (matchingClasses.size() > maxNumOfMatchedClass) { + affect.setOverLimitMsg("The number of matched classes is " +matchingClasses.size()+ ", greater than the limit value " + maxNumOfMatchedClass + ". Try to change the limit with option '-m '."); + return affect; + } + logger.info("enhance matched classes: {}", matchingClasses); affect.setTransformer(this); @@ -518,6 +537,16 @@ public synchronized EnhancerAffect enhance(final Instrumentation inst, int maxNu return affect; } + private boolean isTargetClassLoader(ClassLoader inClassLoader) { + if (targetClassLoaderHash == null || targetClassLoaderHash.isEmpty()) { + return true; + } + if (inClassLoader == null) { + return false; + } + return Integer.toHexString(inClassLoader.hashCode()).equalsIgnoreCase(targetClassLoaderHash); + } + /** * 重置指定的Class * diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java index 08cba034a32..837ec8d4653 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java @@ -57,12 +57,27 @@ public abstract class EnhancerCommand extends AnnotatedCommand { protected boolean lazy = false; + /** + * 指定 classloader hash,只增强该 classloader 加载的类。 + */ + protected String hashCode; + @Option(longName = "exclude-class-pattern") @Description("exclude class name pattern, use either '.' or '/' as separator") public void setExcludeClassPattern(String excludeClassPattern) { this.excludeClassPattern = excludeClassPattern; } + @Option(longName = "classloader") + @Description("The hash code of the special class's classLoader") + public void setHashCode(String hashCode) { + this.hashCode = hashCode; + } + + public String getHashCode() { + return hashCode; + } + @Option(longName = "listenerId") @Description("The special listenerId") public void setListenerId(long listenerId) { @@ -195,7 +210,8 @@ protected void enhance(CommandProcess process) { skipJDKTrace = ((AbstractTraceAdviceListener) listener).getCommand().isSkipJDKTrace(); } - Enhancer enhancer = new Enhancer(listener, listener instanceof InvokeTraceable, skipJDKTrace, getClassNameMatcher(), getClassNameExcludeMatcher(), getMethodNameMatcher(), this.lazy); + Enhancer enhancer = new Enhancer(listener, listener instanceof InvokeTraceable, skipJDKTrace, + getClassNameMatcher(), getClassNameExcludeMatcher(), getMethodNameMatcher(), this.lazy, this.hashCode); // 注册通知监听器 process.register(listener, enhancer); effect = enhancer.enhance(inst, this.maxNumOfMatchedClass); diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java index 985ae5e6c4d..309b9175b0d 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java @@ -65,6 +65,13 @@ public void setNumberOfLimit(int numberOfLimit) { this.numberOfLimit = numberOfLimit; } + @Override + @Option(shortName = "c", longName = "classloader") + @Description("The hash code of the special class's classLoader") + public void setHashCode(String hashCode) { + super.setHashCode(hashCode); + } + public String getClassPattern() { return classPattern; } diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java index 3c866a677ba..2a207dcdc4e 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java @@ -136,6 +136,13 @@ public void setSizeLimit(Integer sizeLimit) { this.sizeLimit = sizeLimit; } + @Override + @Option(shortName = "c", longName = "classloader") + @Description("The hash code of the special class's classLoader") + public void setHashCode(String hashCode) { + super.setHashCode(hashCode); + } + @Option(shortName = "w", longName = "watch-express") @Description(value = "watch the time fragment by ognl express.\n" + Constants.EXPRESS_EXAMPLES) public void setWatchExpress(String watchExpress) { diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java index 9d8a4136ec9..227d13ab7f3 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java @@ -96,6 +96,13 @@ public void setSkipJDKTrace(boolean skipJDKTrace) { this.skipJDKTrace = skipJDKTrace; } + @Override + @Option(shortName = "c", longName = "classloader") + @Description("The hash code of the special class's classLoader") + public void setHashCode(String hashCode) { + super.setHashCode(hashCode); + } + public String getClassPattern() { return classPattern; } diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java index 6d40509ffa7..041e8a39cb0 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java @@ -120,6 +120,13 @@ public void setNumberOfLimit(int numberOfLimit) { this.numberOfLimit = numberOfLimit; } + @Override + @Option(shortName = "c", longName = "classloader") + @Description("The hash code of the special class's classLoader") + public void setHashCode(String hashCode) { + super.setHashCode(hashCode); + } + public String getClassPattern() { return classPattern; } diff --git a/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java b/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java index bbf97e46614..acdd783180e 100644 --- a/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java +++ b/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java @@ -2,6 +2,8 @@ import java.arthas.SpyAPI; import java.lang.instrument.Instrumentation; +import java.net.URL; +import java.net.URLClassLoader; import org.assertj.core.api.Assertions; import org.junit.Test; @@ -14,6 +16,7 @@ import com.alibaba.deps.org.objectweb.asm.tree.MethodNode; import com.taobao.arthas.core.bytecode.TestHelper; import com.taobao.arthas.core.server.ArthasBootstrap; +import com.taobao.arthas.core.util.ClassLoaderUtils; import com.taobao.arthas.core.util.matcher.EqualsMatcher; import demo.MathGame; @@ -92,4 +95,43 @@ public void test() throws Throwable { System.err.println(string); } + @Test + public void testEnhanceWithClassLoaderHash() throws Throwable { + Instrumentation instrumentation = ByteBuddyAgent.install(); + TestHelper.appendSpyJar(instrumentation); + ArthasBootstrap.getInstance(instrumentation, "ip=127.0.0.1"); + + URL codeSource = MathGame.class.getProtectionDomain().getCodeSource().getLocation(); + URLClassLoader anotherClassLoader = new URLClassLoader(new URL[] { codeSource }, null); + try { + Class anotherMathGame = Class.forName(MathGame.class.getName(), true, anotherClassLoader); + Assertions.assertThat(anotherMathGame.getClassLoader()).isNotSameAs(MathGame.class.getClassLoader()); + + AdviceListener listener = Mockito.mock(AdviceListener.class); + EqualsMatcher methodNameMatcher = new EqualsMatcher("print"); + EqualsMatcher classNameMatcher = new EqualsMatcher(MathGame.class.getName()); + + // Enhancer 会过滤与自身 ClassLoader 相同的类(认为是 Arthas 自身加载的类)。 + // 这里用另一个 ClassLoader 加载一份同名类,并用 classloader hash 精确指定只增强这一份。 + String targetClassLoaderHash = Integer.toHexString(anotherClassLoader.hashCode()); + Enhancer enhancer = new Enhancer(listener, false, false, classNameMatcher, null, methodNameMatcher, false, + targetClassLoaderHash); + + com.taobao.arthas.core.util.affect.EnhancerAffect affect = enhancer.enhance(instrumentation, 50); + + String expectedMethodPrefix = ClassLoaderUtils.classLoaderHash(anotherClassLoader) + "|" + + MathGame.class.getName() + "#print|"; + String nonTargetMethodPrefix = ClassLoaderUtils.classLoaderHash(MathGame.class.getClassLoader()) + "|" + MathGame.class.getName() + + "#print|"; + + Assertions.assertThat(affect.cCnt()).isEqualTo(1); + Assertions.assertThat(affect.mCnt()).isEqualTo(1); + Assertions.assertThat(affect.getMethods()).hasSize(1); + Assertions.assertThat(affect.getMethods()).allMatch(m -> m.startsWith(expectedMethodPrefix)); + Assertions.assertThat(affect.getMethods()).noneMatch(m -> m.startsWith(nonTargetMethodPrefix)); + } finally { + anotherClassLoader.close(); + } + } + } diff --git a/core/src/test/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileToolTest.java b/core/src/test/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileToolTest.java index e48329dff01..d5ce5da3add 100644 --- a/core/src/test/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileToolTest.java +++ b/core/src/test/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileToolTest.java @@ -1,6 +1,7 @@ package com.taobao.arthas.core.mcp.tool.function.basic1000; import com.fasterxml.jackson.core.type.TypeReference; +import com.taobao.arthas.common.JavaVersionUtils; import com.taobao.arthas.mcp.server.tool.ToolContext; import com.taobao.arthas.mcp.server.util.JsonParser; import org.junit.*; @@ -16,6 +17,10 @@ import java.util.LinkedHashMap; import java.util.Map; +/** + * ViewFileTool 单元测试 + * 仅在 JDK 8 下运行,因为测试中使用反射修改环境变量的方式在高版本 JDK 中不可用 + */ public class ViewFileToolTest { @Rule @@ -27,6 +32,10 @@ public class ViewFileToolTest { @Before public void setUp() { + // 仅在 JDK 8 下运行测试 + Assume.assumeTrue("此测试仅在 JDK 8 下运行", JavaVersionUtils.isJava8()); + // Windows 下环境变量的内部实现不同,跳过测试 + Assume.assumeFalse("此测试在 Windows 下不运行", System.getProperty("os.name").toLowerCase().contains("win")); clearEnv(ViewFileTool.ALLOWED_DIRS_ENV); } diff --git a/pom.xml b/pom.xml index 7f7ed347b6a..bf3061b6081 100644 --- a/pom.xml +++ b/pom.xml @@ -198,13 +198,13 @@ net.bytebuddy byte-buddy - 1.14.11 + 1.18.3 net.bytebuddy byte-buddy-agent - 1.14.11 + 1.18.3 diff --git a/site/docs/doc/monitor.md b/site/docs/doc/monitor.md index 56de016a92e..991d80647e5 100644 --- a/site/docs/doc/monitor.md +++ b/site/docs/doc/monitor.md @@ -38,6 +38,7 @@ | _condition-express_ | 条件表达式 | | [E] | 开启正则表达式匹配,默认为通配符匹配 | | `[c:]` | 统计周期,默认值为 60 秒 | +| `--classloader` | 指定 classloader hash,只增强该 classloader 加载的类 | | [b] | 在**方法调用之前**计算 condition-express | | `[m ]` | 指定 Class 最大匹配数量,默认值为 50。长格式为`[maxMatch ]`。 | @@ -87,6 +88,15 @@ Affect(class count:1 , method count:1) cost in 384 ms, listenerId: 6. 2022-12-25 21:12:59 demo.MathGame primeFactors 0 0 0 0.00 0.00% ``` +### 指定 ClassLoader 增强 + +当同名类被多个 classloader 加载时,可以先用 `sc -d` 查看 classloader hash,然后用 `--classloader` 指定增强的 classloader(注意 `-c` 在 monitor 里表示统计周期): + +```bash +sc -d com.example.Foo +monitor --classloader 3d4eac69 com.example.Foo bar +``` + ### 计算条件表达式过滤统计结果(方法执行完毕之后) ```bash diff --git a/site/docs/doc/stack.md b/site/docs/doc/stack.md index c46f4cb4892..a76894b6dd3 100644 --- a/site/docs/doc/stack.md +++ b/site/docs/doc/stack.md @@ -17,6 +17,7 @@ | _condition-express_ | 条件表达式 | | [E] | 开启正则表达式匹配,默认为通配符匹配 | | `[n:]` | 执行次数限制 | +| `[c:]` | 指定 classloader hash,只增强该 classloader 加载的类 | | `[m ]` | 指定 Class 最大匹配数量,默认值为 50。长格式为`[maxMatch ]`。 | 这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写`"{params,returnObj}"`,只要是一个合法的 ognl 表达式,都能被正常支持。 @@ -57,6 +58,15 @@ ts=2022-12-25 21:07:07;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun at demo.MathGame.main(MathGame.java:38) ``` +### 指定 ClassLoader 增强 + +当同名类被多个 classloader 加载时,可以先用 `sc -d` 查看 classloader hash,然后用 `-c` 指定增强的 classloader: + +```bash +sc -d com.example.Foo +stack -c 3d4eac69 com.example.Foo bar +``` + ### 据条件表达式来过滤 ```bash diff --git a/site/docs/doc/trace.md b/site/docs/doc/trace.md index f1efd73daa0..8a6b983406e 100644 --- a/site/docs/doc/trace.md +++ b/site/docs/doc/trace.md @@ -18,6 +18,7 @@ | [E] | 开启正则表达式匹配,默认为通配符匹配 | | `[n:]` | 命令执行次数,默认值为 100。 | | `#cost` | 方法执行耗时 | +| `[c:]` | 指定 classloader hash,只增强该 classloader 加载的类 | | `[m ]` | 指定 Class 最大匹配数量,默认值为 50。长格式为`[maxMatch ]`。 | 这里重点要说明的是`条件表达式`,`条件表达式`的构成主要由 ognl 表达式组成,所以你可以这样写`"params[0]<0"`,只要是一个合法的 ognl 表达式,都能被正常支持。 @@ -83,6 +84,15 @@ Affect(class count: 1 , method count: 1) cost in 412 ms, listenerId: 4 `---[13.95% 0.043995ms] demo.MathGame:primeFactors() #46 [throws Exception] ``` +### 指定 ClassLoader 增强 + +当同名类被多个 classloader 加载时,可以先用 `sc -d` 查看 classloader hash,然后用 `-c` 指定增强的 classloader: + +```bash +sc -d com.example.Foo +trace -c 3d4eac69 com.example.Foo bar +``` + ### trace 次数限制 如果方法调用的次数很多,那么可以用`-n`参数指定捕捉结果的次数。比如下面的例子里,捕捉到一次调用就退出命令。 diff --git a/site/docs/doc/tt.md b/site/docs/doc/tt.md index 17695bf1244..ca2b2714468 100644 --- a/site/docs/doc/tt.md +++ b/site/docs/doc/tt.md @@ -67,6 +67,10 @@ Affect(class count:1 , method count:1) cost in 130 ms, listenerId: 1. 通过 `-m` 参数指定 Class 匹配的最大数量,防止匹配到的 Class 数量太多导致 JVM 挂起,默认值是 50。 + - `-c ` + + 当同名类被多个 classloader 加载时,可以用 `-c` 指定只增强某个 classloader 加载的类。可以先用 `sc -d className` 查看具体的 classloader hash。 + - 表格字段说明 | 表格字段 | 字段解释 | diff --git a/site/docs/doc/watch.md b/site/docs/doc/watch.md index cac5f081537..e0306679057 100644 --- a/site/docs/doc/watch.md +++ b/site/docs/doc/watch.md @@ -24,6 +24,7 @@ watch 的参数比较多,主要是因为它能在 4 个不同的场景观察 | [f] | 在**函数结束之后**(正常返回和异常返回)观察 | | [E] | 开启正则表达式匹配,默认为通配符匹配 | | [x:] | 指定输出结果的属性遍历深度,默认为 1,最大值是 4 | +| [c:] | 指定 classloader hash,只增强该 classloader 加载的类 | | `[m ]` | 指定 Class 最大匹配数量,默认值为 50。长格式为`[maxMatch ]`。 | 这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写`"{params,returnObj}"`,只要是一个合法的 ognl 表达式,都能被正常支持。 @@ -108,6 +109,15 @@ ts=2022-12-25 19:58:51; [cost=0.046928ms] result=@ArrayList[ ] ``` +### 指定 ClassLoader 增强 + +当同名类被多个 classloader 加载时,可以先用 `sc -d` 查看 classloader hash,然后用 `-c` 指定增强的 classloader: + +```bash +sc -d com.example.Foo +watch -c 3d4eac69 com.example.Foo bar '{params,returnObj}' +``` + ### 观察函数调用入口的参数和返回值 ```bash diff --git a/site/docs/en/doc/monitor.md b/site/docs/en/doc/monitor.md index 534fa743b0b..4a742996d60 100644 --- a/site/docs/en/doc/monitor.md +++ b/site/docs/en/doc/monitor.md @@ -38,6 +38,7 @@ Parameter `[c:]` stands for cycles of statistics. Its value is an integer value | _condition-expression_ | condition expression for filtering method calls | | `[E]` | turn on regex matching while the default is wildcard matching | | `[c:]` | cycle of statistics, the default value: `60`s | +| `--classloader` | Specify classloader hash, only enhance classes loaded by it | | `[b]` | evaluate the condition-expression before method invoke | | `[m ]` | Specify the max number of matched Classes, the default value is 50. Long format is `[maxMatch ]`. | @@ -87,6 +88,15 @@ Affect(class count:1 , method count:1) cost in 384 ms, listenerId: 6. 2022-12-25 21:12:59 demo.MathGame primeFactors 0 0 0 0.00 0.00% ``` +### Specify ClassLoader to enhance + +If the same class is loaded by multiple classloaders, you can use `sc -d` to find the classloader hash and then use `--classloader` to enhance only the specified one (note that `-c` in `monitor` means the output cycle): + +```bash +sc -d com.example.Foo +monitor --classloader 3d4eac69 com.example.Foo bar +``` + ### Evaluate condition-express to filter method (after method call) ```bash diff --git a/site/docs/en/doc/stack.md b/site/docs/en/doc/stack.md index 627360cc1a4..ff13085aeb7 100644 --- a/site/docs/en/doc/stack.md +++ b/site/docs/en/doc/stack.md @@ -17,6 +17,7 @@ Most often we know one method gets called, but we have no idea on which code pat | _condition-expression_ | condition expression | | `[E]` | turn on regex match, the default behavior is wildcard match | | `[n:]` | execution times | +| `[c:]` | Specify classloader hash, only enhance classes loaded by it | | `[m ]` | Specify the max number of matched Classes, the default value is 50. Long format is `[maxMatch ]`. | There's one thing worthy noting here is observation expression. The observation expression supports OGNL grammar, for example, you can come up a expression like this `"{params,returnObj}"`. All OGNL expressions are supported as long as they are legal to the grammar. @@ -57,6 +58,15 @@ ts=2022-12-25 21:07:07;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun at demo.MathGame.main(MathGame.java:38) ``` +### Specify ClassLoader to enhance + +If the same class is loaded by multiple classloaders, you can use `sc -d` to find the classloader hash and then use `-c` to enhance only the specified one: + +```bash +sc -d com.example.Foo +stack -c 3d4eac69 com.example.Foo bar +``` + ### Filtering by condition expression ```bash diff --git a/site/docs/en/doc/trace.md b/site/docs/en/doc/trace.md index 15fad46aaf9..4c2da4d775c 100644 --- a/site/docs/en/doc/trace.md +++ b/site/docs/en/doc/trace.md @@ -18,6 +18,7 @@ Trace method calling path, and output the time cost for each node in the path. | `[E]` | enable regex match, the default behavior is wildcards match | | `[n:]` | execution times, the default value is 100. | | #cost | time cost | +| `[c:]` | Specify classloader hash, only enhance classes loaded by it | | `[m ]` | Specify the max number of matched Classes, the default value is 50. Long format is `[maxMatch ]`. | There's one thing worthy noting here is `condition expression`. The `condition expression` supports OGNL grammar, for example, you can come up a expression like this `"params[0]<0"`. All OGNL expressions are supported as long as they are legal to the grammar. @@ -81,6 +82,15 @@ Affect(class count: 1 , method count: 1) cost in 412 ms, listenerId: 4 `---[13.95% 0.043995ms] demo.MathGame:primeFactors() #46 [throws Exception] ``` +### Specify ClassLoader to enhance + +If the same class is loaded by multiple classloaders, you can use `sc -d` to find the classloader hash and then use `-c` to enhance only the specified one: + +```bash +sc -d com.example.Foo +trace -c 3d4eac69 com.example.Foo bar +``` + ### Trace times limit If the method invoked many times, use `-n` options to specify trace times. For example, the command will exit when received a trace result. diff --git a/site/docs/en/doc/tt.md b/site/docs/en/doc/tt.md index 0445ab704c6..a2b085d1b2e 100644 --- a/site/docs/en/doc/tt.md +++ b/site/docs/en/doc/tt.md @@ -60,6 +60,10 @@ Affect(class count:1 , method count:1) cost in 130 ms, listenerId: 1. limit the number of matched Classes to avoid JVM suspending when too many matched Classes. The default value is 50. +- `-c ` + + if the same class is loaded by multiple classloaders, you can use `-c` to enhance only the specified classloader. Use `sc -d className` to find the classloader hash. + - Property | Name | Specification | diff --git a/site/docs/en/doc/watch.md b/site/docs/en/doc/watch.md index 5dfc3711ae1..b3eb2a50748 100644 --- a/site/docs/en/doc/watch.md +++ b/site/docs/en/doc/watch.md @@ -22,6 +22,7 @@ There are four different scenarios for `watch` command, which makes it rather co | [f] | when method exits (either succeed or fail with exceptions) | | [E] | turn on regex matching while the default is wildcard matching | | [x:] | the depth to print the specified property with default value: 1, the max value is 4 | +| [c:] | Specify classloader hash, only enhance classes loaded by it | | `[m ]` | Specify the max number of matched Classes, the default value is 50. Long format is `[maxMatch ]`. | F.Y.I @@ -106,6 +107,15 @@ ts=2022-12-25 19:58:51; [cost=0.046928ms] result=@ArrayList[ ] ``` +### Specify ClassLoader to enhance + +If the same class is loaded by multiple classloaders, you can use `sc -d` to find the classloader hash and then use `-c` to enhance only the specified one: + +```bash +sc -d com.example.Foo +watch -c 3d4eac69 com.example.Foo bar '{params,returnObj}' +``` + ### Check `in parameters` ```bash