support ClassLoader#toString() for sc command. (#2329)

pull/2344/head
pandaapo 2 years ago committed by GitHub
parent 2a4452f839
commit ca08cf5ebd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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<ClassLoader> matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass);
if (hashCode == null && (classLoaderClass != null || classLoaderToString != null)) {
List<ClassLoader> 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 <classloader hash>'");
process.end(-1, "Found more than one classloader by " + tips + ", please specify classloader with '-c <classloader hash>'");
return;
} else {
process.end(-1, "Can not find classloader by class name: " + classLoaderClass + ".");
process.end(-1, "Can not find classloader by " + tips + ".");
return;
}
}

@ -76,4 +76,48 @@ public class ClassLoaderUtils {
}
return Integer.toHexString(hashCode);
}
/**
* Find List<ClassLoader> by the class name of ClassLoader or the return value of ClassLoader#toString().
* @param inst
* @param classLoaderClassName
* @param classLoaderToString
* @return
*/
public static List<ClassLoader> getClassLoader(Instrumentation inst, String classLoaderClassName, String classLoaderToString) {
List<ClassLoader> matchClassLoaders = new ArrayList<ClassLoader>();
if (StringUtils.isEmpty(classLoaderClassName) && StringUtils.isEmpty(classLoaderToString)) {
return matchClassLoaders;
}
Set<ClassLoader> classLoaderSet = getAllClassLoader(inst);
List<ClassLoader> matchedByClassLoaderToStr = new ArrayList<ClassLoader>();
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;
}
}

@ -21,6 +21,7 @@
| `[c:]` | 指定 class 的 ClassLoader 的 hashcode |
| `[classLoaderClass:]` | 指定执行表达式的 ClassLoader 的 class name |
| `[n:]` | 具有详细信息的匹配类的最大数量(默认为 100 |
|`[cs <arg>]` | 指定 class 的 ClassLoader#toString() 返回值。长格式`[classLoaderStr <arg>]`|
::: 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.
```

@ -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 <arg>]` | Specify the return value of class's ClassLoader#toString(). Long format is`[classLoaderStr <arg>]`|
::: 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.
```

Loading…
Cancel
Save