Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.util.Map;
import java.util.Map.Entry;

import static com.taobao.arthas.core.util.ClassUtils.formatClassLoaderText;

/**
* @author gongdewei 2020/4/21
*/
Expand Down Expand Up @@ -61,7 +63,7 @@ public void draw(CommandProcess process, ClassLoaderModel result) {
private void drawUrlClassStats(CommandProcess process, ClassLoaderVO classLoader, List<UrlClassStat> urlClassStats,
boolean detail) {
if (classLoader != null) {
process.write(classLoader.getName() + ", hash:" + classLoader.getHash() + "\n");
process.write(formatClassLoaderText(classLoader.getName()) + ", hash:" + classLoader.getHash() + "\n");
}

boolean hasMatched = false;
Expand Down Expand Up @@ -135,7 +137,7 @@ private void drawUrlStats(CommandProcess process, Map<ClassLoaderVO, ClassLoader
}

TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
table.row(new LabelElement(classLoaderVO.getName() + ", hash:" + classLoaderVO.getHash())
table.row(new LabelElement(formatClassLoaderText(classLoaderVO.getName()) + ", hash:" + classLoaderVO.getHash())
.style(Decoration.bold.bold()));
Collection<String> usedUrls = urlStat.getUsedUrls();
table.row(new LabelElement("Used URLs:").style(Decoration.bold.bold()));
Expand Down Expand Up @@ -200,7 +202,8 @@ private void drawResources(CommandProcess process, List<String> resources) {
private Element renderClasses(ClassSetVO classSetVO) {
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
if (classSetVO.getSegment() == 0) {
table.row(new LabelElement("hash:" + classSetVO.getClassloader().getHash() + ", " + classSetVO.getClassloader().getName())
table.row(new LabelElement("hash:" + classSetVO.getClassloader().getHash() + ", "
+ formatClassLoaderText(classSetVO.getClassloader().getName()))
.style(Decoration.bold.bold()));
}
for (String className : classSetVO.getClasses()) {
Expand All @@ -222,7 +225,8 @@ private static TableElement renderTable(Collection<ClassLoaderVO> classLoaderInf
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
table.add(new RowElement().style(Decoration.bold.bold()).add("name", "loadedCount", "hash", "parent"));
for (ClassLoaderVO classLoaderVO : classLoaderInfos) {
table.row(classLoaderVO.getName(), "" + classLoaderVO.getLoadedCount(), classLoaderVO.getHash(), classLoaderVO.getParent());
table.row(formatClassLoaderText(classLoaderVO.getName()), "" + classLoaderVO.getLoadedCount(),
classLoaderVO.getHash(), formatClassLoaderText(classLoaderVO.getParent()));
}
return table;
}
Expand All @@ -231,7 +235,7 @@ private static TableElement renderTable(Collection<ClassLoaderVO> classLoaderInf
private static Element renderTree(Collection<ClassLoaderVO> classLoaderInfos) {
TreeElement root = new TreeElement();
for (ClassLoaderVO classLoader : classLoaderInfos) {
TreeElement child = new TreeElement(classLoader.getName());
TreeElement child = new TreeElement(formatClassLoaderText(classLoader.getName()));
root.addChild(child);
renderSubtree(child, classLoader);
}
Expand All @@ -243,7 +247,7 @@ private static void renderSubtree(TreeElement parent, ClassLoaderVO parentClassL
return;
}
for (ClassLoaderVO childClassLoader : parentClassLoader.getChildren()) {
TreeElement child = new TreeElement(childClassLoader.getName());
TreeElement child = new TreeElement(formatClassLoaderText(childClassLoader.getName()));
parent.addChild(child);
renderSubtree(child, childClassLoader);
}
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@ public static ClassLoaderVO createClassLoaderVO(ClassLoader classLoader) {
return classLoaderVO;
}

public static String formatClassLoaderText(String value) {
if (value == null) {
return null;
}
return value.replace("\r\n", "\\n")
.replace("\r", "\\n")
.replace("\n", "\\n")
.replace("\t", " ");
}

public static List<ClassLoaderVO> createClassLoaderVOList(Collection<ClassLoader> classLoaders) {
List<ClassLoaderVO> classLoaderVOList = new ArrayList<ClassLoaderVO>();
for (ClassLoader classLoader : classLoaders) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ public static Element drawSuperClass(ClassDetailVO clazz) {

public static Element drawClassLoader(ClassVO clazz) {
String[] classloaders = clazz.getClassloader();
return drawTree(classloaders);
String[] formattedClassloaders = new String[classloaders.length];
for (int i = 0; i < classloaders.length; i++) {
formattedClassloaders[i] = ClassUtils.formatClassLoaderText(classloaders[i]);
Comment thread
hengyunabc marked this conversation as resolved.
}
return drawTree(formattedClassloaders);
}

public static Element drawTree(String[] nodes) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package com.taobao.arthas.core.command.view;

import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.ClassLoaderUrlStat;
import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.UrlClassStat;
import com.taobao.arthas.core.command.model.ClassVO;
import com.taobao.arthas.core.command.model.ClassLoaderModel;
import com.taobao.arthas.core.command.model.ClassLoaderVO;
import com.taobao.arthas.core.command.model.ClassSetVO;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.util.TypeRenderUtils;
import com.taobao.text.util.RenderUtil;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;

import java.util.Collections;

public class ClassLoaderViewTest {

@Test
public void shouldEscapeLineBreaksInClassLoaderTable() {
ClassLoaderVO classLoader = new ClassLoaderVO();
classLoader.setName("TomcatEmbeddedWebappClassLoader\r\n context: /demo");
classLoader.setParent("jdk.internal.loader.ClassLoaders$AppClassLoader\n parent detail");
classLoader.setLoadedCount(12);
classLoader.setHash("1a2b3c");

String output = renderClassLoaders(classLoader, false);

Assert.assertTrue(output.contains("TomcatEmbeddedWebappClassLoader\\n context: /demo"));
Assert.assertTrue(output.contains("jdk.internal.loader.ClassLoaders$AppClassLoader\\n parent detail"));
}
Comment thread
hengyunabc marked this conversation as resolved.

@Test
public void shouldEscapeLineBreaksInClassLoaderTree() {
ClassLoaderVO root = new ClassLoaderVO();
root.setName("RootClassLoader\r\n root detail");
root.setHash("root");

ClassLoaderVO child = new ClassLoaderVO();
child.setName("ChildClassLoader\n child detail");
child.setHash("child");
root.addChild(child);

String output = renderClassLoaders(root, true);

Assert.assertTrue(output.contains("RootClassLoader\\n root detail"));
Assert.assertTrue(output.contains("ChildClassLoader\\n child detail"));
}

@Test
public void shouldEscapeLineBreaksInClassDetailClassLoaderTree() {
ClassVO classInfo = new ClassVO();
classInfo.setClassloader(new String[] {
"TomcatEmbeddedWebappClassLoader\r\n context: /demo",
"jdk.internal.loader.ClassLoaders$AppClassLoader\n parent detail"
});

String output = RenderUtil.render(TypeRenderUtils.drawClassLoader(classInfo), 200);

Assert.assertTrue(output.contains("TomcatEmbeddedWebappClassLoader\\n context: /demo"));
Assert.assertTrue(output.contains("jdk.internal.loader.ClassLoaders$AppClassLoader\\n parent detail"));
}

@Test
public void shouldEscapeLineBreaksInUrlStatsHeader() {
ClassLoaderVO classLoader = new ClassLoaderVO();
classLoader.setName("TomcatEmbeddedWebappClassLoader\r\n context: /demo");
classLoader.setHash("urlstat");

ClassLoaderUrlStat urlStat = new ClassLoaderUrlStat(Collections.singletonList("file:/tmp/demo.jar"),
Collections.<String>emptyList());

ClassLoaderModel model = new ClassLoaderModel();
model.setUrlStats(Collections.singletonMap(classLoader, urlStat));

String output = renderView(model);

Assert.assertTrue(output.contains("TomcatEmbeddedWebappClassLoader\\n context: /demo, hash:urlstat"));
}

@Test
public void shouldEscapeLineBreaksInUrlClassStatsHeader() {
ClassLoaderVO classLoader = new ClassLoaderVO();
classLoader.setName("TomcatEmbeddedWebappClassLoader\r\n context: /demo");
classLoader.setHash("urlclasses");

UrlClassStat stat = new UrlClassStat();
stat.setUrl("file:/tmp/demo.jar");
stat.setLoadedClassCount(3);

ClassLoaderModel model = new ClassLoaderModel()
.setClassLoader(classLoader)
.setUrlClassStats(Collections.singletonList(stat))
.setUrlClassStatsDetail(false);

String output = renderView(model);

Assert.assertTrue(output.contains("TomcatEmbeddedWebappClassLoader\\n context: /demo, hash:urlclasses"));
}

@Test
public void shouldEscapeLineBreaksInAllClassesHeader() {
ClassLoaderVO classLoader = new ClassLoaderVO();
classLoader.setName("TomcatEmbeddedWebappClassLoader\r\n context: /demo");
classLoader.setHash("allclasses");

ClassSetVO classSet = new ClassSetVO(classLoader, Collections.singletonList("demo.SampleClass"));

ClassLoaderModel model = new ClassLoaderModel().setClassSet(classSet);

String output = renderView(model);

Assert.assertTrue(output.contains("hash:allclasses, TomcatEmbeddedWebappClassLoader\\n context: /demo"));
}

private String renderClassLoaders(ClassLoaderVO classLoader, boolean tree) {
return render(process -> ClassLoaderView.drawClassLoaders(process, Collections.singletonList(classLoader), tree));
}

private String renderView(ClassLoaderModel model) {
return render(process -> new ClassLoaderView().draw(process, model));
}

private String render(RenderAction action) {
CommandProcess process = Mockito.mock(CommandProcess.class);
StringBuilder output = new StringBuilder();
Mockito.when(process.width()).thenReturn(200);
Mockito.when(process.write(Mockito.anyString())).thenAnswer(invocation -> {
output.append(invocation.getArgument(0, String.class));
return process;
});

action.render(process);
return output.toString();
}

private interface RenderAction {
void render(CommandProcess process);
}
}
Loading