slice the result of 'classloader -a'

This commit is contained in:
gongdewei
2020-05-13 20:48:49 +08:00
parent c8ceab2d46
commit 2f579cd353
4 changed files with 92 additions and 49 deletions
@@ -6,6 +6,7 @@ import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.*;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.shell.handlers.Handler;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.arthas.core.util.affect.RowAffect;
import com.taobao.middleware.cli.annotations.Description;
@@ -16,19 +17,9 @@ import com.taobao.middleware.cli.annotations.Summary;
import java.lang.instrument.Instrumentation;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
@Name("classloader")
@Summary("Show classloader info")
@@ -54,6 +45,8 @@ public class ClassLoaderCommand extends AnnotatedCommand {
private String loadClass = null;
private AtomicBoolean isInterrupted = new AtomicBoolean(false);
@Option(shortName = "t", longName = "tree", flag = true)
@Description("Display ClassLoader tree")
public void setTree(boolean tree) {
@@ -98,6 +91,9 @@ public class ClassLoaderCommand extends AnnotatedCommand {
@Override
public void process(CommandProcess process) {
// ctrl-C support
process.interruptHandler(new ClassLoaderInterruptHandler(process, isInterrupted));
Instrumentation inst = process.session().getInstrumentation();
if (all) {
processAllClasses(process, inst);
@@ -242,23 +238,30 @@ public class ClassLoaderCommand extends AnnotatedCommand {
private void processAllClasses(CommandProcess process, Instrumentation inst) {
RowAffect affect = new RowAffect();
List<ClassSetVO> allClasses = getAllClasses(hashCode, inst, affect);
process.appendResult(new ClassLoaderModel().setAllClasses(allClasses));
getAllClasses(hashCode, inst, affect, process);
if (checkInterrupted(process)) {
return;
}
process.appendResult(new RowAffectModel(affect));
process.end();
}
private boolean checkInterrupted(CommandProcess process) {
if (isInterrupted.get()) {
process.end(1, "Interrupted by user");
return true;
}
return false;
}
/**
* 获取到所有的class, 还有它们的classloader,按classloader归类好,统一输出每个classloader里有哪些class
* <p>
* 当hashCode是null,则把所有的classloader的都打印
*
* @param hashCode
* @param affect
* @return
*/
@SuppressWarnings("rawtypes")
private static List<ClassSetVO> getAllClasses(String hashCode, Instrumentation inst, RowAffect affect) {
private void getAllClasses(String hashCode, Instrumentation inst, RowAffect affect, CommandProcess process) {
int hashCodeInt = -1;
if (hashCode != null) {
hashCodeInt = Integer.valueOf(hashCode, 16);
@@ -301,26 +304,39 @@ public class ClassLoaderCommand extends AnnotatedCommand {
}
//convert to vo
List<ClassSetVO> classSets = new ArrayList<ClassSetVO>();
classSets.add(new ClassSetVO(ClassUtils.createClassLoaderVO(null), toClassNames(bootstrapClassSet)));
int sizeLimit = 1024;
processClassSet(process, ClassUtils.createClassLoaderVO(null), bootstrapClassSet, sizeLimit);
affect.rCnt(bootstrapClassSet.size());
for (Entry<ClassLoader, SortedSet<Class>> entry : classLoaderClassMap.entrySet()) {
if (checkInterrupted(process)) {
return;
}
ClassLoader classLoader = entry.getKey();
SortedSet<Class> classSet = entry.getValue();
List<String> classNames = toClassNames(classSet);
classSets.add(new ClassSetVO(ClassUtils.createClassLoaderVO(classLoader), classNames));
processClassSet(process, ClassUtils.createClassLoaderVO(classLoader), classSet, sizeLimit);
affect.rCnt(classSet.size());
}
return classSets;
}
private static List<String> toClassNames(SortedSet<Class> classSet) {
List<String> classNames = new ArrayList<String>(classSet.size());
for (Class aClass : classSet) {
private void processClassSet(CommandProcess process, ClassLoaderVO classLoaderVO, Collection<Class> classes, int sizeLimit) {
List<String> classNames = new ArrayList<String>(512);
int segment = 0;
for (Class aClass : classes) {
classNames.add(aClass.getName());
//slice segment
if(classNames.size() >= sizeLimit) {
if (checkInterrupted(process)) {
return;
}
process.appendResult(new ClassLoaderModel().setClassSet(new ClassSetVO(classLoaderVO, classNames, segment++)));
classNames = new ArrayList<String>(512);
}
}
//last segment
if (classNames.size() > 0) {
process.appendResult(new ClassLoaderModel().setClassSet(new ClassSetVO(classLoaderVO, classNames, segment++)));
}
return classNames;
}
private static List<String> getClassLoaderUrls(ClassLoader classLoader) {
@@ -566,4 +582,19 @@ public class ClassLoaderCommand extends AnnotatedCommand {
return unsortedStats.get(o2).getLoadedCount() - unsortedStats.get(o1).getLoadedCount();
}
}
private class ClassLoaderInterruptHandler implements Handler<Void> {
private final CommandProcess process;
private final AtomicBoolean isInterrupted;
public ClassLoaderInterruptHandler(CommandProcess process, AtomicBoolean isInterrupted) {
this.process = process;
this.isInterrupted = isInterrupted;
}
@Override
public void handle(Void event) {
isInterrupted.set(true);
}
}
}
@@ -10,7 +10,7 @@ import java.util.Map;
*/
public class ClassLoaderModel extends ResultModel {
private List<ClassSetVO> allClasses;
private ClassSetVO classSet;
private List<String> resources;
private ClassVO loadClass;
private List<String> urls;
@@ -28,12 +28,12 @@ public class ClassLoaderModel extends ResultModel {
return "classloader";
}
public List<ClassSetVO> getAllClasses() {
return allClasses;
public ClassSetVO getClassSet() {
return classSet;
}
public ClassLoaderModel setAllClasses(List<ClassSetVO> allClasses) {
this.allClasses = allClasses;
public ClassLoaderModel setClassSet(ClassSetVO classSet) {
this.classSet = classSet;
return this;
}
@@ -1,17 +1,23 @@
package com.taobao.arthas.core.command.model;
import java.util.List;
import java.util.Collection;
/**
* @author gongdewei 2020/4/21
*/
public class ClassSetVO {
private ClassLoaderVO classloader;
private List<String> classes;
private Collection<String> classes;
private int segment;
public ClassSetVO(ClassLoaderVO classloader, List<String> classes) {
public ClassSetVO(ClassLoaderVO classloader, Collection<String> classes) {
this(classloader, classes, 0);
}
public ClassSetVO(ClassLoaderVO classloader, Collection<String> classes, int segment) {
this.classloader = classloader;
this.classes = classes;
this.segment = segment;
}
public ClassLoaderVO getClassloader() {
@@ -22,11 +28,19 @@ public class ClassSetVO {
this.classloader = classloader;
}
public List<String> getClasses() {
public Collection<String> getClasses() {
return classes;
}
public void setClasses(List<String> classes) {
public void setClasses(Collection<String> classes) {
this.classes = classes;
}
public int getSegment() {
return segment;
}
public void setSegment(int segment) {
this.segment = segment;
}
}
@@ -21,8 +21,8 @@ public class ClassLoaderView extends ResultView<ClassLoaderModel> {
@Override
public void draw(CommandProcess process, ClassLoaderModel result) {
if (result.getAllClasses() != null) {
drawAllClasses(process, result.getAllClasses());
if (result.getClassSet() != null) {
drawAllClasses(process, result.getClassSet());
}
if (result.getResources() != null) {
drawResources(process, result.getResources());
@@ -72,8 +72,9 @@ public class ClassLoaderView extends ResultView<ClassLoaderModel> {
process.write(RenderUtil.render(ClassUtils.renderClassInfo(loadClass), process.width()) + "\n");
}
private void drawAllClasses(CommandProcess process, List<ClassSetVO> allClasses) {
process.write(RenderUtil.render(renderClasses(allClasses), process.width()));
private void drawAllClasses(CommandProcess process, ClassSetVO classSetVO) {
process.write(RenderUtil.render(renderClasses(classSetVO), process.width()));
process.write("\n");
}
private void drawResources(CommandProcess process, List<String> resources) {
@@ -85,18 +86,15 @@ public class ClassLoaderView extends ResultView<ClassLoaderModel> {
process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING);
}
private Element renderClasses(List<ClassSetVO> allClasses) {
private Element renderClasses(ClassSetVO classSetVO) {
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
for (ClassSetVO classSetVO : allClasses) {
if (classSetVO.getSegment() == 0) {
table.row(new LabelElement("hash:" + classSetVO.getClassloader().getHash() + ", " + classSetVO.getClassloader().getName())
.style(Decoration.bold.bold()));
for (String className : classSetVO.getClasses()) {
table.row(new LabelElement(className));
}
table.row(new LabelElement(" "));
}
for (String className : classSetVO.getClasses()) {
table.row(new LabelElement(className));
}
return table;
}