From ea262bd52b6ca8b1386c73080c196b6d36f79fb8 Mon Sep 17 00:00:00 2001 From: Hollow Man Date: Tue, 18 Aug 2020 01:00:52 -0500 Subject: [PATCH] Add --classLoaderClass for sc/sm (#1433) --- .../command/klass100/SearchClassCommand.java | 29 ++++++++++++++++++ .../command/klass100/SearchMethodCommand.java | 30 +++++++++++++++++++ .../core/command/model/SearchClassModel.java | 23 +++++++++++++- .../core/command/model/SearchMethodModel.java | 22 ++++++++++++++ .../core/command/view/SearchClassView.java | 6 ++++ .../core/command/view/SearchMethodView.java | 7 +++++ site/src/site/sphinx/en/sc.md | 1 + site/src/site/sphinx/en/sm.md | 1 + site/src/site/sphinx/sc.md | 1 + site/src/site/sphinx/sm.md | 1 + 10 files changed, 120 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchClassCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchClassCommand.java index daacfbb9a..1fa1e57f4 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchClassCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchClassCommand.java @@ -3,6 +3,7 @@ package com.taobao.arthas.core.command.klass100; import java.lang.instrument.Instrumentation; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -11,11 +12,13 @@ import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.model.ClassDetailVO; import com.taobao.arthas.core.command.model.SearchClassModel; import com.taobao.arthas.core.command.model.RowAffectModel; +import com.taobao.arthas.core.command.model.ClassLoaderVO; import com.taobao.arthas.core.shell.cli.Completion; import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.ClassLoaderUtils; import com.taobao.arthas.core.util.ResultUtils; import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.StringUtils; @@ -46,6 +49,7 @@ public class SearchClassCommand extends AnnotatedCommand { private boolean isField = false; private boolean isRegEx = false; private String hashCode = null; + private String classLoaderClass; private Integer expand; private int numberOfLimit = 100; @@ -85,6 +89,12 @@ public class SearchClassCommand extends AnnotatedCommand { this.hashCode = hashCode; } + @Option(longName = "classLoaderClass") + @Description("The class name of the special class's classLoader.") + public void setClassLoaderClass(String classLoaderClass) { + this.classLoaderClass = classLoaderClass; + } + @Option(shortName = "n", longName = "limits") @Description("Maximum number of matching classes with details (100 by default)") public void setNumberOfLimit(int numberOfLimit) { @@ -96,6 +106,25 @@ public class SearchClassCommand extends AnnotatedCommand { // TODO: null check RowAffect affect = new RowAffect(); Instrumentation inst = process.session().getInstrumentation(); + + if (hashCode == null && classLoaderClass != null) { + List matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass); + if (matchedClassLoaders.size() == 1) { + hashCode = Integer.toHexString(matchedClassLoaders.get(0).hashCode()); + } else if (matchedClassLoaders.size() > 1) { + Collection classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders); + SearchClassModel searchclassModel = new SearchClassModel() + .setClassLoaderClass(classLoaderClass) + .setMatchedClassLoaders(classLoaderVOList); + process.appendResult(searchclassModel); + process.end(-1, "Found more than one classloader by class name, please specify classloader with '-c '"); + return; + } else { + process.end(-1, "Can not find classloader by class name: " + classLoaderClass + "."); + return; + } + } + List> matchedClasses = new ArrayList>(SearchUtils.searchClass(inst, classPattern, isRegEx, hashCode)); Collections.sort(matchedClasses, new Comparator>() { @Override diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java index 2986e8c56..10779eba8 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java @@ -5,6 +5,8 @@ import java.lang.instrument.Instrumentation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Set; +import java.util.Collection; +import java.util.List; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; @@ -12,11 +14,13 @@ import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.model.SearchMethodModel; import com.taobao.arthas.core.command.model.MethodVO; import com.taobao.arthas.core.command.model.RowAffectModel; +import com.taobao.arthas.core.command.model.ClassLoaderVO; import com.taobao.arthas.core.shell.cli.Completion; import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.ClassLoaderUtils; import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.StringUtils; import com.taobao.arthas.core.util.affect.RowAffect; @@ -50,6 +54,7 @@ public class SearchMethodCommand extends AnnotatedCommand { private String classPattern; private String methodPattern; private String hashCode = null; + private String classLoaderClass; private boolean isDetail = false; private boolean isRegEx = false; private int numberOfLimit = 100; @@ -84,6 +89,12 @@ public class SearchMethodCommand extends AnnotatedCommand { this.hashCode = hashCode; } + @Option(longName = "classLoaderClass") + @Description("The class name of the special class's classLoader.") + public void setClassLoaderClass(String classLoaderClass) { + this.classLoaderClass = classLoaderClass; + } + @Option(shortName = "n", longName = "limits") @Description("Maximum number of matching classes (100 by default)") public void setNumberOfLimit(int numberOfLimit) { @@ -96,6 +107,25 @@ public class SearchMethodCommand extends AnnotatedCommand { Instrumentation inst = process.session().getInstrumentation(); Matcher methodNameMatcher = methodNameMatcher(); + + if (hashCode == null && classLoaderClass != null) { + List matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass); + if (matchedClassLoaders.size() == 1) { + hashCode = Integer.toHexString(matchedClassLoaders.get(0).hashCode()); + } else if (matchedClassLoaders.size() > 1) { + Collection classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders); + SearchMethodModel searchmethodModel = new SearchMethodModel() + .setClassLoaderClass(classLoaderClass) + .setMatchedClassLoaders(classLoaderVOList); + process.appendResult(searchmethodModel); + process.end(-1, "Found more than one classloader by class name, please specify classloader with '-c '"); + return; + } else { + process.end(-1, "Can not find classloader by class name: " + classLoaderClass + "."); + return; + } + } + Set> matchedClasses = SearchUtils.searchClass(inst, classPattern, isRegEx, hashCode); if (numberOfLimit > 0 && matchedClasses.size() > numberOfLimit) { diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/SearchClassModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/SearchClassModel.java index b4e6ee00c..fcf7c23fa 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/model/SearchClassModel.java +++ b/core/src/main/java/com/taobao/arthas/core/command/model/SearchClassModel.java @@ -1,6 +1,6 @@ package com.taobao.arthas.core.command.model; - +import java.util.Collection; import java.util.List; /** @@ -15,6 +15,9 @@ public class SearchClassModel extends ResultModel { private List classNames; private int segment; + private Collection matchedClassLoaders; + private String classLoaderClass; + public SearchClassModel() { } @@ -70,4 +73,22 @@ public class SearchClassModel extends ResultModel { public Integer getExpand() { return expand; } + + public String getClassLoaderClass() { + return classLoaderClass; + } + + public SearchClassModel setClassLoaderClass(String classLoaderClass) { + this.classLoaderClass = classLoaderClass; + return this; + } + + public Collection getMatchedClassLoaders() { + return matchedClassLoaders; + } + + public SearchClassModel setMatchedClassLoaders(Collection matchedClassLoaders) { + this.matchedClassLoaders = matchedClassLoaders; + return this; + } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/SearchMethodModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/SearchMethodModel.java index ba77192a3..e654a50ed 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/model/SearchMethodModel.java +++ b/core/src/main/java/com/taobao/arthas/core/command/model/SearchMethodModel.java @@ -1,5 +1,6 @@ package com.taobao.arthas.core.command.model; +import java.util.Collection; /** * Model of SearchMethodCommand @@ -9,6 +10,9 @@ public class SearchMethodModel extends ResultModel { private MethodVO methodInfo; private boolean detail; + private Collection matchedClassLoaders; + private String classLoaderClass; + public SearchMethodModel() { } @@ -33,6 +37,24 @@ public class SearchMethodModel extends ResultModel { this.detail = detail; } + public String getClassLoaderClass() { + return classLoaderClass; + } + + public SearchMethodModel setClassLoaderClass(String classLoaderClass) { + this.classLoaderClass = classLoaderClass; + return this; + } + + public Collection getMatchedClassLoaders() { + return matchedClassLoaders; + } + + public SearchMethodModel setMatchedClassLoaders(Collection matchedClassLoaders) { + this.matchedClassLoaders = matchedClassLoaders; + return this; + } + @Override public String getType() { return "sm"; diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/SearchClassView.java b/core/src/main/java/com/taobao/arthas/core/command/view/SearchClassView.java index fc17576f5..fb5235fd2 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/SearchClassView.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/SearchClassView.java @@ -11,6 +11,12 @@ import com.taobao.text.util.RenderUtil; public class SearchClassView extends ResultView { @Override public void draw(CommandProcess process, SearchClassModel result) { + if (result.getMatchedClassLoaders() != null) { + process.write("Matched classloaders: \n"); + ClassLoaderView.drawClassLoaders(process, result.getMatchedClassLoaders(), false); + process.write("\n"); + return; + } if (result.isDetailed()) { process.write(RenderUtil.render(ClassUtils.renderClassInfo(result.getClassInfo(), diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/SearchMethodView.java b/core/src/main/java/com/taobao/arthas/core/command/view/SearchMethodView.java index d58027069..d57df2c68 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/SearchMethodView.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/SearchMethodView.java @@ -14,6 +14,13 @@ import com.taobao.text.util.RenderUtil; public class SearchMethodView extends ResultView { @Override public void draw(CommandProcess process, SearchMethodModel result) { + if (result.getMatchedClassLoaders() != null) { + process.write("Matched classloaders: \n"); + ClassLoaderView.drawClassLoaders(process, result.getMatchedClassLoaders(), false); + process.write("\n"); + return; + } + boolean detail = result.isDetail(); MethodVO methodInfo = result.getMethodInfo(); diff --git a/site/src/site/sphinx/en/sc.md b/site/src/site/sphinx/en/sc.md index 2f9ec2f2f..09761d9e5 100644 --- a/site/src/site/sphinx/en/sc.md +++ b/site/src/site/sphinx/en/sc.md @@ -18,6 +18,7 @@ sc |`[f]`|print the fields info of the current class, MUST be used with `-d` together| |`[x:]`|specify the depth of recursive traverse the static fields, the default value is '0' - equivalent to use `toString` to output| |`[c:]`|The hash code of the special class's classLoader| +|`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. | |`[n:]`|Maximum number of matching classes with details (100 by default)| > *class-patten* supports full qualified class name, e.g. com.taobao.test.AAA and com/taobao/test/AAA. It also supports the format of 'com/taobao/test/AAA', so that it is convenient to directly copy class name from the exception stack trace without replacing '/' to '.'.

diff --git a/site/src/site/sphinx/en/sm.md b/site/src/site/sphinx/en/sm.md index 2b3121703..47852adef 100644 --- a/site/src/site/sphinx/en/sm.md +++ b/site/src/site/sphinx/en/sm.md @@ -17,6 +17,7 @@ sm |`[d]`|print the details of the method| |`[E]`|turn on regex matching while the default mode is wildcard matching| |`[c:]`|The hash code of the special class's classLoader| +|`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. | |`[n:]`|Maximum number of matching classes with details (100 by default)| ### Usage diff --git a/site/src/site/sphinx/sc.md b/site/src/site/sphinx/sc.md index 4c2a472f7..fc9011433 100644 --- a/site/src/site/sphinx/sc.md +++ b/site/src/site/sphinx/sc.md @@ -21,6 +21,7 @@ sc |[f]|输出当前类的成员变量信息(需要配合参数-d一起使用)| |[x:]|指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 `toString` 输出| |`[c:]`|指定class的 ClassLoader 的 hashcode| +|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name| |`[n:]`|具有详细信息的匹配类的最大数量(默认为100)| > class-pattern支持全限定名,如com.taobao.test.AAA,也支持com/taobao/test/AAA这样的格式,这样,我们从异常堆栈里面把类名拷贝过来的时候,不需要在手动把`/`替换为`.`啦。 diff --git a/site/src/site/sphinx/sm.md b/site/src/site/sphinx/sm.md index 430bd9e6b..2da567ce6 100644 --- a/site/src/site/sphinx/sm.md +++ b/site/src/site/sphinx/sm.md @@ -18,6 +18,7 @@ sm |[d]|展示每个方法的详细信息| |[E]|开启正则表达式匹配,默认为通配符匹配| |`[c:]`|指定class的 ClassLoader 的 hashcode| +|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name| |`[n:]`|具有详细信息的匹配类的最大数量(默认为100)| ### 使用参考