From ca08cf5ebd309f13ea86105aa35844643fe59383 Mon Sep 17 00:00:00 2001 From: pandaapo <35672972+pandaapo@users.noreply.github.com> Date: Tue, 15 Nov 2022 14:40:16 +0800 Subject: [PATCH] support ClassLoader#toString() for sc command. (#2329) --- .../command/klass100/SearchClassCommand.java | 22 ++++++++-- .../arthas/core/util/ClassLoaderUtils.java | 44 +++++++++++++++++++ site/docs/doc/sc.md | 11 +++++ site/docs/en/doc/sc.md | 11 +++++ 4 files changed, 84 insertions(+), 4 deletions(-) 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 0eaf1d28e..12017dfd6 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 @@ -50,6 +50,7 @@ public class SearchClassCommand extends AnnotatedCommand { private boolean isRegEx = false; private String hashCode = null; private String classLoaderClass; + private String classLoaderToString; private Integer expand; private int numberOfLimit = 100; @@ -101,14 +102,27 @@ public class SearchClassCommand extends AnnotatedCommand { this.numberOfLimit = numberOfLimit; } + @Option(shortName = "cs", longName = "classLoaderStr") + @Description("The return value of the special class's ClassLoader#toString().") + public void setClassLoaderToString(String classLoaderToString) { + this.classLoaderToString = classLoaderToString; + } + @Override public void process(final CommandProcess process) { // TODO: null check RowAffect affect = new RowAffect(); Instrumentation inst = process.session().getInstrumentation(); - if (hashCode == null && classLoaderClass != null) { - List matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass); + if (hashCode == null && (classLoaderClass != null || classLoaderToString != null)) { + List matchedClassLoaders = ClassLoaderUtils.getClassLoader(inst, classLoaderClass, classLoaderToString); + String tips = ""; + if (classLoaderClass != null) { + tips = "class name: " + classLoaderClass; + } + if (classLoaderToString != null) { + tips = tips + (StringUtils.isEmpty(tips) ? "ClassLoader#toString(): " : ", ClassLoader#toString(): ") + classLoaderToString; + } if (matchedClassLoaders.size() == 1) { hashCode = Integer.toHexString(matchedClassLoaders.get(0).hashCode()); } else if (matchedClassLoaders.size() > 1) { @@ -117,10 +131,10 @@ public class SearchClassCommand extends AnnotatedCommand { .setClassLoaderClass(classLoaderClass) .setMatchedClassLoaders(classLoaderVOList); process.appendResult(searchclassModel); - process.end(-1, "Found more than one classloader by class name, please specify classloader with '-c '"); + process.end(-1, "Found more than one classloader by " + tips + ", please specify classloader with '-c '"); return; } else { - process.end(-1, "Can not find classloader by class name: " + classLoaderClass + "."); + process.end(-1, "Can not find classloader by " + tips + "."); return; } } diff --git a/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java b/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java index 65c6b8dd4..aba374346 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java @@ -76,4 +76,48 @@ public class ClassLoaderUtils { } return Integer.toHexString(hashCode); } + + /** + * Find List by the class name of ClassLoader or the return value of ClassLoader#toString(). + * @param inst + * @param classLoaderClassName + * @param classLoaderToString + * @return + */ + public static List getClassLoader(Instrumentation inst, String classLoaderClassName, String classLoaderToString) { + List matchClassLoaders = new ArrayList(); + if (StringUtils.isEmpty(classLoaderClassName) && StringUtils.isEmpty(classLoaderToString)) { + return matchClassLoaders; + } + Set classLoaderSet = getAllClassLoader(inst); + List matchedByClassLoaderToStr = new ArrayList(); + for (ClassLoader classLoader : classLoaderSet) { + // only classLoaderClassName + if (!StringUtils.isEmpty(classLoaderClassName) && StringUtils.isEmpty(classLoaderToString)) { + if (classLoader.getClass().getName().equals(classLoaderClassName)) { + matchClassLoaders.add(classLoader); + } + } + // only classLoaderToString + else if (!StringUtils.isEmpty(classLoaderToString) && StringUtils.isEmpty(classLoaderClassName)) { + if (classLoader.toString().equals(classLoaderToString)) { + matchClassLoaders.add(classLoader); + } + } + // classLoaderClassName and classLoaderToString + else { + if (classLoader.getClass().getName().equals(classLoaderClassName)) { + matchClassLoaders.add(classLoader); + } + if (classLoader.toString().equals(classLoaderToString)) { + matchedByClassLoaderToStr.add(classLoader); + } + } + } + // classLoaderClassName and classLoaderToString + if (!StringUtils.isEmpty(classLoaderClassName) && !StringUtils.isEmpty(classLoaderToString)) { + matchClassLoaders.retainAll(matchedByClassLoaderToStr); + } + return matchClassLoaders; + } } diff --git a/site/docs/doc/sc.md b/site/docs/doc/sc.md index b81e88144..55a1e4ba1 100644 --- a/site/docs/doc/sc.md +++ b/site/docs/doc/sc.md @@ -21,6 +21,7 @@ | `[c:]` | 指定 class 的 ClassLoader 的 hashcode | | `[classLoaderClass:]` | 指定执行表达式的 ClassLoader 的 class name | | `[n:]` | 具有详细信息的匹配类的最大数量(默认为 100) | +|`[cs ]` | 指定 class 的 ClassLoader#toString() 返回值。长格式`[classLoaderStr ]`| ::: tip class-pattern 支持全限定名,如 com.taobao.test.AAA,也支持 com/taobao/test/AAA 这样的格式,这样,我们从异常堆栈里面把类名拷贝过来的时候,不需要在手动把`/`替换为`.`啦。 @@ -105,3 +106,13 @@ sc 默认开启了子类匹配功能,也就是说所有当前类的子类也 Affect(row-cnt:1) cost in 19 ms. ``` + +- 通过 ClassLoader#toString 查找类(前提:有一个toString()返回值是`apo`的类加载器,加载的类中包含`demo.MathGame`, `demo.MyBar`,` demo.MyFoo`3个类) + + ```bash + $ sc -cs apo *demo* + demo.MathGame + demo.MyBar + demo.MyFoo + Affect(row-cnt:3) cost in 56 ms. + ``` diff --git a/site/docs/en/doc/sc.md b/site/docs/en/doc/sc.md index 89c26e4ad..3a827cb71 100644 --- a/site/docs/en/doc/sc.md +++ b/site/docs/en/doc/sc.md @@ -21,6 +21,7 @@ Search classes loaded by JVM. | `[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) | +|`[cs ]` | Specify the return value of class's ClassLoader#toString(). Long format is`[classLoaderStr ]`| ::: tip _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 '.'. @@ -105,3 +106,13 @@ _class-patten_ supports full qualified class name, e.g. com.taobao.test.AAA and Affect(row-cnt:1) cost in 19 ms. ``` + +- Search class by ClassLoader#toString (on the premise that a ClassLoader instance whose `toString()` returns `apo` has loaded some classes including `demo.MathGame`, `demo.MyBar`, `demo.MyFoo`) + + ```bash + $ sc -cs apo *demo* + demo.MathGame + demo.MyBar + demo.MyFoo + Affect(row-cnt:3) cost in 56 ms. + ```