mirror of
https://github.com/alibaba/arthas.git
synced 2026-10-21 05:51:38 +00:00
slice the result of 'classloader -a'
This commit is contained in:
+58
-27
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user