From 4a48a7b0735e4eac15e652af60f241db23157589 Mon Sep 17 00:00:00 2001 From: gongdewei Date: Fri, 10 Jul 2020 20:31:28 +0800 Subject: [PATCH] Transform commands of pkg klass100 (#1285) --- .../command/basic1000/OptionsCommand.java | 39 ++- .../core/command/basic1000/ResetCommand.java | 2 +- .../command/basic1000/SystemEnvCommand.java | 3 +- .../basic1000/SystemPropertyCommand.java | 4 +- .../command/basic1000/VMOptionCommand.java | 4 +- .../command/klass100/ClassLoaderCommand.java | 260 ++++++++++-------- .../command/klass100/DumpClassCommand.java | 85 +++--- .../command/klass100/GetStaticCommand.java | 83 +++--- .../core/command/klass100/JadCommand.java | 80 +++--- .../klass100/MemoryCompilerCommand.java | 23 +- .../core/command/klass100/OgnlCommand.java | 56 ++-- .../command/klass100/RedefineCommand.java | 34 +-- .../command/klass100/SearchClassCommand.java | 45 ++- .../command/klass100/SearchMethodCommand.java | 75 ++--- .../core/command/model/ClassDetailVO.java | 163 +++++++++++ .../core/command/model/ClassLoaderModel.java | 93 +++++++ .../core/command/model/ClassLoaderVO.java | 74 +++++ .../arthas/core/command/model/ClassSetVO.java | 51 ++++ .../arthas/core/command/model/ClassVO.java | 35 +++ .../core/command/model/DumpClassModel.java | 41 +++ .../core/command/model/DumpClassVO.java | 17 ++ .../arthas/core/command/model/FieldVO.java | 61 ++++ .../core/command/model/GetStaticModel.java | 56 ++++ .../arthas/core/command/model/JadModel.java | 55 ++++ .../command/model/MemoryCompilerModel.java | 32 +++ .../arthas/core/command/model/MethodVO.java | 99 +++++++ .../arthas/core/command/model/ObjectVO.java | 34 +++ .../arthas/core/command/model/OgnlModel.java | 33 +++ .../core/command/model/RedefineModel.java | 45 +++ .../core/command/model/RowAffectModel.java | 30 ++ .../core/command/model/SearchClassModel.java | 73 +++++ .../core/command/model/SearchMethodModel.java | 40 +++ .../core/command/model/StatusModel.java | 32 --- .../core/command/view/ClassLoaderView.java | 140 ++++++++++ .../core/command/view/DumpClassView.java | 51 ++++ .../core/command/view/GetStaticView.java | 29 ++ .../arthas/core/command/view/JadView.java | 42 +++ .../core/command/view/MemoryCompilerView.java | 17 ++ .../arthas/core/command/view/OgnlView.java | 19 ++ .../core/command/view/RedefineView.java | 22 ++ .../core/command/view/ResultViewResolver.java | 11 + .../core/command/view/RowAffectView.java | 14 + .../core/command/view/SearchClassView.java | 26 ++ .../core/command/view/SearchMethodView.java | 39 +++ .../arthas/core/shell/command/ExitStatus.java | 64 +++++ .../taobao/arthas/core/util/ClassUtils.java | 186 ++++++++++++- .../taobao/arthas/core/util/CommandUtils.java | 24 ++ .../taobao/arthas/core/util/ResultUtils.java | 50 ++++ .../taobao/arthas/core/util/StringUtils.java | 20 +- .../arthas/core/util/TypeRenderUtils.java | 190 +++++++------ 50 files changed, 2278 insertions(+), 523 deletions(-) create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/ClassDetailVO.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderModel.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderVO.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/ClassSetVO.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/ClassVO.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/DumpClassModel.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/DumpClassVO.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/FieldVO.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/GetStaticModel.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/JadModel.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/MemoryCompilerModel.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/MethodVO.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/ObjectVO.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/OgnlModel.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/RedefineModel.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/RowAffectModel.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/SearchClassModel.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/SearchMethodModel.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/ClassLoaderView.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/DumpClassView.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/GetStaticView.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/JadView.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/MemoryCompilerView.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/OgnlView.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/RedefineView.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/RowAffectView.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/SearchClassView.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/SearchMethodView.java create mode 100644 core/src/main/java/com/taobao/arthas/core/shell/command/ExitStatus.java create mode 100644 core/src/main/java/com/taobao/arthas/core/util/CommandUtils.java create mode 100644 core/src/main/java/com/taobao/arthas/core/util/ResultUtils.java diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/OptionsCommand.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/OptionsCommand.java index 07960f710..14a0a6174 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/basic1000/OptionsCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/OptionsCommand.java @@ -6,12 +6,13 @@ import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.model.ChangeResultVO; import com.taobao.arthas.core.command.model.OptionVO; import com.taobao.arthas.core.command.model.OptionsModel; -import com.taobao.arthas.core.command.model.StatusModel; import com.taobao.arthas.core.shell.cli.CliToken; import com.taobao.arthas.core.shell.cli.Completion; import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.shell.command.ExitStatus; +import com.taobao.arthas.core.util.CommandUtils; import com.taobao.arthas.core.util.StringUtils; import com.taobao.arthas.core.util.TokenUtils; import com.taobao.arthas.core.util.matcher.EqualsMatcher; @@ -70,23 +71,19 @@ public class OptionsCommand extends AnnotatedCommand { @Override public void process(CommandProcess process) { try { - StatusModel statusModel = null; + ExitStatus status = null; if (isShow()) { - statusModel = processShow(process); + status = processShow(process); } else if (isShowName()) { - statusModel = processShowName(process); + status = processShowName(process); } else { - statusModel = processChangeNameValue(process); + status = processChangeNameValue(process); } - if (statusModel != null) { - process.end(statusModel.getStatusCode(), statusModel.getMessage()); - } else { - process.end(-1, "command was not processed"); - } + CommandUtils.end(process, status); } catch (Throwable t) { - logger.error("process options command error", t); - process.end(-1, "process options command error"); + logger.error("processing error", t); + process.end(-1, "processing error"); } } @@ -110,24 +107,24 @@ public class OptionsCommand extends AnnotatedCommand { } } - private StatusModel processShow(CommandProcess process) throws IllegalAccessException { + private ExitStatus processShow(CommandProcess process) throws IllegalAccessException { Collection fields = findOptionFields(new RegexMatcher(".*")); process.appendResult(new OptionsModel(convertToOptionVOs(fields))); - return StatusModel.success(); + return ExitStatus.success(); } - private StatusModel processShowName(CommandProcess process) throws IllegalAccessException { + private ExitStatus processShowName(CommandProcess process) throws IllegalAccessException { Collection fields = findOptionFields(new EqualsMatcher(optionName)); process.appendResult(new OptionsModel(convertToOptionVOs(fields))); - return StatusModel.success(); + return ExitStatus.success(); } - private StatusModel processChangeNameValue(CommandProcess process) throws IllegalAccessException { + private ExitStatus processChangeNameValue(CommandProcess process) throws IllegalAccessException { Collection fields = findOptionFields(new EqualsMatcher(optionName)); // name not exists if (fields.isEmpty()) { - return StatusModel.failure(-1, format("options[%s] not found.", optionName)); + return ExitStatus.failure(-1, format("options[%s] not found.", optionName)); } Field field = fields.iterator().next(); @@ -155,16 +152,16 @@ public class OptionsCommand extends AnnotatedCommand { } else if (isIn(type, short.class, String.class)) { FieldUtils.writeStaticField(field, afterValue = optionValue); } else { - return StatusModel.failure(-1, format("Options[%s] type[%s] was unsupported.", optionName, type.getSimpleName())); + return ExitStatus.failure(-1, format("Options[%s] type[%s] was unsupported.", optionName, type.getSimpleName())); } } catch (Throwable t) { - return StatusModel.failure(-1, format("Cannot cast option value[%s] to type[%s].", optionValue, type.getSimpleName())); + return ExitStatus.failure(-1, format("Cannot cast option value[%s] to type[%s].", optionValue, type.getSimpleName())); } ChangeResultVO changeResultVO = new ChangeResultVO(optionAnnotation.name(), beforeValue, afterValue); process.appendResult(new OptionsModel(changeResultVO)); - return StatusModel.success(); + return ExitStatus.success(); } diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/ResetCommand.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/ResetCommand.java index 834c2de6d..60e744adf 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/basic1000/ResetCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/ResetCommand.java @@ -51,9 +51,9 @@ public class ResetCommand extends AnnotatedCommand { try { EnhancerAffect enhancerAffect = Enhancer.reset(inst, matcher); process.appendResult(new ResetModel(enhancerAffect)); + process.end(); } catch (UnmodifiableClassException e) { // ignore - } finally { process.end(); } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/SystemEnvCommand.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/SystemEnvCommand.java index c2191094c..6a6dc7e8d 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/basic1000/SystemEnvCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/SystemEnvCommand.java @@ -42,10 +42,9 @@ public class SystemEnvCommand extends AnnotatedCommand { result.put(envName, value); } process.appendResult(result); + process.end(); } catch (Throwable t) { process.end(-1, "Error during setting system env: " + t.getMessage()); - } finally { - process.end(); } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/SystemPropertyCommand.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/SystemPropertyCommand.java index cca75a2e8..1d2093c59 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/basic1000/SystemPropertyCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/SystemPropertyCommand.java @@ -2,7 +2,6 @@ package com.taobao.arthas.core.command.basic1000; import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.model.MessageModel; -import com.taobao.arthas.core.command.model.StatusModel; import com.taobao.arthas.core.command.model.SystemPropertyModel; import com.taobao.arthas.core.shell.cli.Completion; import com.taobao.arthas.core.shell.cli.CompletionUtils; @@ -60,10 +59,9 @@ public class SystemPropertyCommand extends AnnotatedCommand { process.appendResult(new MessageModel("Successfully changed the system property.")); process.appendResult(new SystemPropertyModel(propertyName, System.getProperty(propertyName))); } + process.end(); } catch (Throwable t) { process.end(-1, "Error during setting system property: " + t.getMessage()); - } finally { - process.end(); } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/VMOptionCommand.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/VMOptionCommand.java index 9ae903133..49d5b4f2e 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/basic1000/VMOptionCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/VMOptionCommand.java @@ -69,6 +69,7 @@ public class VMOptionCommand extends AnnotatedCommand { VMOption option = hotSpotDiagnosticMXBean.getVMOption(name); if (option == null) { process.end(-1, "In order to change the system properties, you must specify the property value."); + return; } else { process.appendResult(new VMOptionModel(Arrays.asList(option))); } @@ -82,11 +83,10 @@ public class VMOptionCommand extends AnnotatedCommand { process.appendResult(new VMOptionModel(new ChangeResultVO(name, originValue, hotSpotDiagnosticMXBean.getVMOption(name).getValue()))); } + process.end(); } catch (Throwable t) { logger.error("Error during setting vm option", t); process.end(-1, "Error during setting vm option: " + t.getMessage()); - } finally { - process.end(); } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java index d6c9f6fcd..ce0cd1cf7 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java @@ -1,28 +1,31 @@ package com.taobao.arthas.core.command.klass100; +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.model.ClassDetailVO; +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.command.model.MessageModel; +import com.taobao.arthas.core.command.model.RowAffectModel; 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.StringUtils; +import com.taobao.arthas.core.util.ResultUtils; import com.taobao.arthas.core.util.affect.RowAffect; -import com.taobao.arthas.core.view.ObjectView; import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Option; import com.taobao.middleware.cli.annotations.Summary; -import com.taobao.text.Decoration; -import com.taobao.text.ui.Element; -import com.taobao.text.ui.LabelElement; -import com.taobao.text.ui.RowElement; -import com.taobao.text.ui.TableElement; -import com.taobao.text.ui.TreeElement; -import com.taobao.text.util.RenderUtil; import java.lang.instrument.Instrumentation; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; @@ -49,6 +52,8 @@ import java.util.TreeSet; " classloader -c 659e0bfd --load demo.MathGame\n" + Constants.WIKI + Constants.WIKI_HOME + "classloader") public class ClassLoaderCommand extends AnnotatedCommand { + + private Logger logger = LoggerFactory.getLogger(ClassLoaderCommand.class); private boolean isTree = false; private String hashCode; private boolean all = false; @@ -58,6 +63,8 @@ public class ClassLoaderCommand extends AnnotatedCommand { private String loadClass = null; + private volatile boolean isInterrupted = false; + @Option(shortName = "t", longName = "tree", flag = true) @Description("Display ClassLoader tree") public void setTree(boolean tree) { @@ -102,6 +109,9 @@ public class ClassLoaderCommand extends AnnotatedCommand { @Override public void process(CommandProcess process) { + // ctrl-C support + process.interruptHandler(new ClassLoaderInterruptHandler(this)); + Instrumentation inst = process.session().getInstrumentation(); if (all) { processAllClasses(process, inst); @@ -143,12 +153,10 @@ public class ClassLoaderCommand extends AnnotatedCommand { TreeMap sorted = new TreeMap(new ValueComparator(classLoaderStats)); sorted.putAll(classLoaderStats); + process.appendResult(new ClassLoaderModel().setClassLoaderStats(sorted)); - Element element = renderStat(sorted); - process.write(RenderUtil.render(element, process.width())) - .write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); affect.rCnt(sorted.keySet().size()); - process.write(affect + "\n"); + process.appendResult(new RowAffectModel(affect)); process.end(); } @@ -156,27 +164,45 @@ public class ClassLoaderCommand extends AnnotatedCommand { RowAffect affect = new RowAffect(); List classLoaderInfos = includeReflectionClassLoader ? getAllClassLoaderInfo(inst) : getAllClassLoaderInfo(inst, new SunReflectionClassLoaderFilter()); - Element element = isTree ? renderTree(classLoaderInfos) : renderTable(classLoaderInfos); - process.write(RenderUtil.render(element, process.width())) - .write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); + + List classLoaderVOs = new ArrayList(classLoaderInfos.size()); + for (ClassLoaderInfo classLoaderInfo : classLoaderInfos) { + ClassLoaderVO classLoaderVO = ClassUtils.createClassLoaderVO(classLoaderInfo.classLoader); + classLoaderVO.setLoadedCount(classLoaderInfo.loadedClassCount()); + classLoaderVOs.add(classLoaderVO); + } + if (isTree){ + classLoaderVOs = processClassLoaderTree(classLoaderVOs); + } + process.appendResult(new ClassLoaderModel().setClassLoaders(classLoaderVOs).setTree(isTree)); + affect.rCnt(classLoaderInfos.size()); - process.write(affect + "\n"); + process.appendResult(new RowAffectModel(affect)); process.end(); } // 根据 hashCode 来打印URLClassLoader的urls private void processClassLoader(CommandProcess process, Instrumentation inst) { RowAffect affect = new RowAffect(); - Set allClassLoader = getAllClassLoaders(inst); for (ClassLoader cl : allClassLoader) { if (Integer.toHexString(cl.hashCode()).equals(hashCode)) { - process.write(RenderUtil.render(renderClassLoaderUrls(cl), process.width())); + if (cl instanceof URLClassLoader) { + List classLoaderUrls = getClassLoaderUrls(cl); + affect.rCnt(classLoaderUrls.size()); + if (classLoaderUrls.isEmpty()){ + process.appendResult(new MessageModel("urls is empty.")); + } else { + process.appendResult(new ClassLoaderModel().setUrls(classLoaderUrls)); + affect.rCnt(classLoaderUrls.size()); + } + } else { + process.appendResult(new MessageModel("not a URLClassLoader.")); } + break; } - process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); - affect.rCnt(allClassLoader.size()); - process.write(affect + "\n"); + } + process.appendResult(new RowAffectModel(affect)); process.end(); } @@ -185,25 +211,25 @@ public class ClassLoaderCommand extends AnnotatedCommand { RowAffect affect = new RowAffect(); int rowCount = 0; Set allClassLoader = getAllClassLoaders(inst); + List resources = new ArrayList(); for (ClassLoader cl : allClassLoader) { if (Integer.toHexString(cl.hashCode()).equals(hashCode)) { - TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); try { Enumeration urls = cl.getResources(resource); while (urls.hasMoreElements()) { URL url = urls.nextElement(); - table.row(url.toString()); + resources.add(url.toString()); rowCount++; } } catch (Throwable e) { - e.printStackTrace(); + logger.warn("get resource failed, resource: {}", resource, e); } - process.write(RenderUtil.render(table, process.width()) + "\n"); } } - process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); affect.rCnt(rowCount); - process.write(affect + "\n"); + + process.appendResult(new ClassLoaderModel().setResources(resources)); + process.appendResult(new RowAffectModel(affect)); process.end(); } @@ -214,23 +240,27 @@ public class ClassLoaderCommand extends AnnotatedCommand { if (Integer.toHexString(cl.hashCode()).equals(hashCode)) { try { Class clazz = cl.loadClass(this.loadClass); - process.write("load class success.\n"); - process.write(RenderUtil.render(ClassUtils.renderClassInfo(clazz), process.width()) + "\n"); + process.appendResult(new MessageModel("load class success.")); + ClassDetailVO classInfo = ClassUtils.createClassInfo(clazz, false); + process.appendResult(new ClassLoaderModel().setLoadClass(classInfo)); } catch (Throwable e) { - e.printStackTrace(); - process.write("load class error.\n" + StringUtils.objectToString(new ObjectView(e, 1).draw())); + logger.warn("load class error, class: {}", this.loadClass, e); + process.end(-1, "load class error, class: "+this.loadClass+", error: "+e.toString()); + return; } } } - process.write("\n"); process.end(); } private void processAllClasses(CommandProcess process, Instrumentation inst) { RowAffect affect = new RowAffect(); - process.write(RenderUtil.render(renderClasses(hashCode, inst), process.width())); - process.write(affect + "\n"); + getAllClasses(hashCode, inst, affect, process); + if (checkInterrupted(process)) { + return; + } + process.appendResult(new RowAffectModel(affect)); process.end(); } @@ -239,17 +269,15 @@ public class ClassLoaderCommand extends AnnotatedCommand { *

* 当hashCode是null,则把所有的classloader的都打印 * - * @param hashCode - * @return */ @SuppressWarnings("rawtypes") - private static Element renderClasses(String hashCode, Instrumentation inst) { + private void getAllClasses(String hashCode, Instrumentation inst, RowAffect affect, CommandProcess process) { int hashCodeInt = -1; if (hashCode != null) { hashCodeInt = Integer.valueOf(hashCode, 16); } - SortedSet bootstrapClassSet = new TreeSet(new Comparator() { + SortedSet> bootstrapClassSet = new TreeSet>(new Comparator() { @Override public int compare(Class o1, Class o2) { return o1.getName().compareTo(o2.getName()); @@ -257,7 +285,7 @@ public class ClassLoaderCommand extends AnnotatedCommand { }); Class[] allLoadedClasses = inst.getAllLoadedClasses(); - Map> classLoaderClassMap = new HashMap>(); + Map>> classLoaderClassMap = new HashMap>>(); for (Class clazz : allLoadedClasses) { ClassLoader classLoader = clazz.getClassLoader(); // Class loaded by BootstrapClassLoader @@ -272,11 +300,11 @@ public class ClassLoaderCommand extends AnnotatedCommand { continue; } - SortedSet classSet = classLoaderClassMap.get(classLoader); + SortedSet> classSet = classLoaderClassMap.get(classLoader); if (classSet == null) { - classSet = new TreeSet(new Comparator() { + classSet = new TreeSet>(new Comparator>() { @Override - public int compare(Class o1, Class o2) { + public int compare(Class o1, Class o2) { return o1.getName().compareTo(o2.getName()); } }); @@ -285,99 +313,82 @@ public class ClassLoaderCommand extends AnnotatedCommand { classSet.add(clazz); } - TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); + // output bootstrapClassSet + int pageSize = 256; + processClassSet(process, ClassUtils.createClassLoaderVO(null), bootstrapClassSet, pageSize, affect); - if (!bootstrapClassSet.isEmpty()) { - table.row(new LabelElement("hash:null, BootstrapClassLoader").style(Decoration.bold.bold())); - for (Class clazz : bootstrapClassSet) { - table.row(new LabelElement(clazz.getName())); + // output other classSet + for (Entry>> entry : classLoaderClassMap.entrySet()) { + if (checkInterrupted(process)) { + return; } - table.row(new LabelElement(" ")); - } - - for (Entry> entry : classLoaderClassMap.entrySet()) { ClassLoader classLoader = entry.getKey(); - SortedSet classSet = entry.getValue(); - - table.row(new LabelElement("hash:" + classLoader.hashCode() + ", " + classLoader.toString()) - .style(Decoration.bold.bold())); - for (Class clazz : classSet) { - table.row(new LabelElement(clazz.getName())); - } - - table.row(new LabelElement(" ")); + SortedSet> classSet = entry.getValue(); + processClassSet(process, ClassUtils.createClassLoaderVO(classLoader), classSet, pageSize, affect); } - return table; } - private static Element renderClassLoaderUrls(ClassLoader classLoader) { - StringBuilder sb = new StringBuilder(); + private void processClassSet(final CommandProcess process, final ClassLoaderVO classLoaderVO, Collection> classes, int pageSize, final RowAffect affect) { + //分批输出classNames, Ctrl+C可以中断执行 + ResultUtils.processClassNames(classes, pageSize, new ResultUtils.PaginationHandler>() { + @Override + public boolean handle(List classNames, int segment) { + process.appendResult(new ClassLoaderModel().setClassSet(new ClassSetVO(classLoaderVO, classNames, segment))); + affect.rCnt(classNames.size()); + return !checkInterrupted(process); + } + }); + } + + private boolean checkInterrupted(CommandProcess process) { + if (!process.isRunning()) { + return true; + } + if(isInterrupted){ + process.end(-1, "Processing has been interrupted"); + return true; + } else { + return false; + } + } + + private static List getClassLoaderUrls(ClassLoader classLoader) { + List urlStrs = new ArrayList(); if (classLoader instanceof URLClassLoader) { URLClassLoader cl = (URLClassLoader) classLoader; URL[] urls = cl.getURLs(); if (urls != null) { for (URL url : urls) { - sb.append(url.toString() + "\n"); + urlStrs.add(url.toString()); } - return new LabelElement(sb.toString()); - } else { - return new LabelElement("urls is empty."); } - } else { - return new LabelElement("not a URLClassLoader.\n"); } + return urlStrs; } // 以树状列出ClassLoader的继承结构 - private static Element renderTree(List classLoaderInfos) { - TreeElement root = new TreeElement(); - - List parentNullClassLoaders = new ArrayList(); - List parentNotNullClassLoaders = new ArrayList(); - for (ClassLoaderInfo info : classLoaderInfos) { - if (info.parent() == null) { - parentNullClassLoaders.add(info); + private static List processClassLoaderTree(List classLoaders) { + List rootClassLoaders = new ArrayList(); + List parentNotNullClassLoaders = new ArrayList(); + for (ClassLoaderVO classLoaderVO : classLoaders) { + if (classLoaderVO.getParent() == null) { + rootClassLoaders.add(classLoaderVO); } else { - parentNotNullClassLoaders.add(info); + parentNotNullClassLoaders.add(classLoaderVO); } } - for (ClassLoaderInfo info : parentNullClassLoaders) { - if (info.parent() == null) { - TreeElement parent = new TreeElement(info.getName()); - renderParent(parent, info, parentNotNullClassLoaders); - root.addChild(parent); - } + for (ClassLoaderVO classLoaderVO : rootClassLoaders) { + buildTree(classLoaderVO, parentNotNullClassLoaders); } - - return root; + return rootClassLoaders; } - // 统计所有的ClassLoader的信息 - private static TableElement renderTable(List classLoaderInfos) { - TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); - table.add(new RowElement().style(Decoration.bold.bold()).add("name", "loadedCount", "hash", "parent")); - for (ClassLoaderInfo info : classLoaderInfos) { - table.row(info.getName(), "" + info.loadedClassCount(), info.hashCodeStr(), info.parentStr()); - } - return table; - } - - private static TableElement renderStat(Map classLoaderStats) { - TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); - table.add(new RowElement().style(Decoration.bold.bold()).add("name", "numberOfInstances", "loadedCountTotal")); - for (Map.Entry entry : classLoaderStats.entrySet()) { - table.row(entry.getKey(), "" + entry.getValue().getNumberOfInstance(), "" + entry.getValue().getLoadedCount()); - } - return table; - } - - private static void renderParent(TreeElement node, ClassLoaderInfo parent, List classLoaderInfos) { - for (ClassLoaderInfo info : classLoaderInfos) { - if (info.parent() == parent.classLoader) { - TreeElement child = new TreeElement(info.getName()); - node.addChild(child); - renderParent(child, info, classLoaderInfos); + private static void buildTree(ClassLoaderVO parent, List parentNotNullClassLoaders) { + for (ClassLoaderVO classLoaderVO : parentNotNullClassLoaders) { + if (parent.getName().equals(classLoaderVO.getParent())){ + parent.addChild(classLoaderVO); + buildTree(classLoaderVO, parentNotNullClassLoaders); } } } @@ -532,15 +543,16 @@ public class ClassLoaderCommand extends AnnotatedCommand { } private static class SunReflectionClassLoaderFilter implements Filter { - private static final String REFLECTION_CLASSLOADER = "sun.reflect.DelegatingClassLoader"; + private static final List REFLECTION_CLASSLOADERS = Arrays.asList("sun.reflect.DelegatingClassLoader", + "jdk.internal.reflect.DelegatingClassLoader"); @Override public boolean accept(ClassLoader classLoader) { - return !REFLECTION_CLASSLOADER.equals(classLoader.getClass().getName()); + return !REFLECTION_CLASSLOADERS.contains(classLoader.getClass().getName()); } } - private static class ClassLoaderStat { + public static class ClassLoaderStat { private int loadedCount; private int numberOfInstance; @@ -552,11 +564,11 @@ public class ClassLoaderCommand extends AnnotatedCommand { this.numberOfInstance += count; } - int getLoadedCount() { + public int getLoadedCount() { return loadedCount; } - int getNumberOfInstance() { + public int getNumberOfInstance() { return numberOfInstance; } } @@ -583,4 +595,18 @@ public class ClassLoaderCommand extends AnnotatedCommand { return unsortedStats.get(o2).getLoadedCount() - unsortedStats.get(o1).getLoadedCount(); } } + + private class ClassLoaderInterruptHandler implements Handler { + + private ClassLoaderCommand command; + + public ClassLoaderInterruptHandler(ClassLoaderCommand command) { + this.command = command; + } + + @Override + public void handle(Void event) { + command.isInterrupted = true; + } + } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/DumpClassCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/DumpClassCommand.java index 6687d6a24..2e9f2931d 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/DumpClassCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/DumpClassCommand.java @@ -3,14 +3,20 @@ package com.taobao.arthas.core.command.klass100; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.model.ClassVO; +import com.taobao.arthas.core.command.model.DumpClassModel; +import com.taobao.arthas.core.command.model.DumpClassVO; +import com.taobao.arthas.core.command.model.MessageModel; +import com.taobao.arthas.core.command.model.RowAffectModel; import com.taobao.arthas.core.shell.cli.Completion; import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.shell.command.ExitStatus; +import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.CommandUtils; import com.taobao.arthas.core.util.InstrumentationUtils; import com.taobao.arthas.core.util.SearchUtils; -import com.taobao.arthas.core.util.StringUtils; -import com.taobao.arthas.core.util.TypeRenderUtils; import com.taobao.arthas.core.util.affect.RowAffect; import com.taobao.middleware.cli.annotations.Argument; import com.taobao.middleware.cli.annotations.DefaultValue; @@ -18,19 +24,15 @@ import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Option; import com.taobao.middleware.cli.annotations.Summary; -import com.taobao.text.Color; -import com.taobao.text.Decoration; -import com.taobao.text.ui.LabelElement; -import com.taobao.text.ui.TableElement; -import com.taobao.text.util.RenderUtil; import java.io.File; import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Set; -import static com.taobao.text.ui.Element.label; /** * Dump class byte array @@ -89,28 +91,30 @@ public class DumpClassCommand extends AnnotatedCommand { @Override public void process(CommandProcess process) { RowAffect effect = new RowAffect(); - try { if (directory != null) { File dir = new File(directory); if (dir.isFile()) { - process.write(directory + " :is not a directory, please check it\n"); + process.end(-1, directory + " :is not a directory, please check it"); return; } } + + ExitStatus status = null; Instrumentation inst = process.session().getInstrumentation(); Set> matchedClasses = SearchUtils.searchClass(inst, classPattern, isRegEx, code); - if (matchedClasses == null || matchedClasses.isEmpty()) { - processNoMatch(process); + status = processNoMatch(process); } else if (matchedClasses.size() > limit) { - processMatches(process, matchedClasses); + status = processMatches(process, matchedClasses); } else { - processMatch(process, effect, inst, matchedClasses); + status = processMatch(process, effect, inst, matchedClasses); } - } finally { - process.write(effect + "\n"); - process.end(); + process.appendResult(new RowAffectModel(effect)); + CommandUtils.end(process, status); + } catch (Throwable e){ + logger.error("processing error", e); + process.end(-1, "processing error"); } } @@ -121,50 +125,41 @@ public class DumpClassCommand extends AnnotatedCommand { } } - private void processMatch(CommandProcess process, RowAffect effect, Instrumentation inst, Set> matchedClasses) { + private ExitStatus processMatch(CommandProcess process, RowAffect effect, Instrumentation inst, Set> matchedClasses) { try { Map, File> classFiles = dump(inst, matchedClasses); - TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); - table.row(new LabelElement("HASHCODE").style(Decoration.bold.bold()), - new LabelElement("CLASSLOADER").style(Decoration.bold.bold()), - new LabelElement("LOCATION").style(Decoration.bold.bold())); - + List dumpedClasses = new ArrayList(classFiles.size()); for (Map.Entry, File> entry : classFiles.entrySet()) { Class clazz = entry.getKey(); File file = entry.getValue(); - table.row(label(StringUtils.classLoaderHash(clazz)).style(Decoration.bold.fg(Color.red)), - TypeRenderUtils.drawClassLoader(clazz), - label(file.getCanonicalPath()).style(Decoration.bold.fg(Color.red))); + DumpClassVO dumpClassVO = new DumpClassVO(); + dumpClassVO.setLocation(file.getCanonicalPath()); + ClassUtils.fillSimpleClassVO(clazz, dumpClassVO); + dumpedClasses.add(dumpClassVO); } + process.appendResult(new DumpClassModel().setDumpedClasses(dumpedClasses)); - process.write(RenderUtil.render(table, process.width())) - .write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); effect.rCnt(classFiles.keySet().size()); + return ExitStatus.success(); } catch (Throwable t) { logger.error("dump: fail to dump classes: " + matchedClasses, t); + return ExitStatus.failure(-1, "dump: fail to dump classes: " + matchedClasses); } } - private void processMatches(CommandProcess process, Set> matchedClasses) { - process.write(String.format( - "Found more than %d class for: %s, Please Try to specify the classloader with the -c option, or try to use --limit option.\n", - limit, classPattern)); + private ExitStatus processMatches(CommandProcess process, Set> matchedClasses) { + String msg = String.format( + "Found more than %d class for: %s, Please Try to specify the classloader with the -c option, or try to use --limit option.", + limit, classPattern); + process.appendResult(new MessageModel(msg)); - TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); - table.row(new LabelElement("NAME").style(Decoration.bold.bold()), - new LabelElement("HASHCODE").style(Decoration.bold.bold()), - new LabelElement("CLASSLOADER").style(Decoration.bold.bold())); - - for (Class c : matchedClasses) { - table.row(label(c.getName()), label(StringUtils.classLoaderHash(c)).style(Decoration.bold.fg(Color.red)), - TypeRenderUtils.drawClassLoader(c)); - } - - process.write(RenderUtil.render(table, process.width()) + "\n"); + List classVOs = ClassUtils.createClassVOList(matchedClasses); + process.appendResult(new DumpClassModel().setMatchedClasses(classVOs)); + return ExitStatus.failure(-1, msg); } - private void processNoMatch(CommandProcess process) { - process.write("No class found for: " + classPattern + "\n"); + private ExitStatus processNoMatch(CommandProcess process) { + return ExitStatus.failure(-1, "No class found for: " + classPattern); } private Map, File> dump(Instrumentation inst, Set> classes) throws UnmodifiableClassException { diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/GetStaticCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/GetStaticCommand.java index ed0296a12..e008fd468 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/GetStaticCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/GetStaticCommand.java @@ -5,35 +5,33 @@ import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.express.ExpressException; import com.taobao.arthas.core.command.express.ExpressFactory; +import com.taobao.arthas.core.command.model.ClassVO; +import com.taobao.arthas.core.command.model.GetStaticModel; +import com.taobao.arthas.core.command.model.MessageModel; +import com.taobao.arthas.core.command.model.RowAffectModel; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.shell.command.ExitStatus; +import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.CommandUtils; import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.StringUtils; -import com.taobao.arthas.core.util.TypeRenderUtils; import com.taobao.arthas.core.util.affect.RowAffect; import com.taobao.arthas.core.util.matcher.Matcher; import com.taobao.arthas.core.util.matcher.RegexMatcher; import com.taobao.arthas.core.util.matcher.WildcardMatcher; -import com.taobao.arthas.core.view.ObjectView; import com.taobao.middleware.cli.annotations.Argument; import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Option; import com.taobao.middleware.cli.annotations.Summary; -import com.taobao.text.Color; -import com.taobao.text.Decoration; -import com.taobao.text.ui.Element; -import com.taobao.text.ui.LabelElement; -import com.taobao.text.ui.TableElement; -import com.taobao.text.util.RenderUtil; import java.lang.instrument.Instrumentation; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.List; import java.util.Set; -import static com.taobao.text.ui.Element.label; - /** * @author diecui1202 on 2017/9/27. */ @@ -96,22 +94,27 @@ public class GetStaticCommand extends AnnotatedCommand { RowAffect affect = new RowAffect(); Instrumentation inst = process.session().getInstrumentation(); Set> matchedClasses = SearchUtils.searchClassOnly(inst, classPattern, isRegEx, hashCode); - try { if (matchedClasses == null || matchedClasses.isEmpty()) { - process.write("No class found for: " + classPattern + "\n"); - } else if (matchedClasses.size() > 1) { - processMatches(process, matchedClasses); - } else { - processExactMatch(process, affect, inst, matchedClasses); + process.end(-1, "No class found for: " + classPattern); + return; } - } finally { - process.write(affect + "\n"); - process.end(); + ExitStatus status = null; + if (matchedClasses.size() > 1) { + status = processMatches(process, matchedClasses); + } else { + status = processExactMatch(process, affect, inst, matchedClasses); + } + process.appendResult(new RowAffectModel(affect)); + CommandUtils.end(process, status); + } catch (Throwable e){ + logger.error("processing error", e); + process.appendResult(new RowAffectModel(affect)); + process.end(-1, "processing error"); } } - private void processExactMatch(CommandProcess process, RowAffect affect, Instrumentation inst, + private ExitStatus processExactMatch(CommandProcess process, RowAffect affect, Instrumentation inst, Set> matchedClasses) { Matcher fieldNameMatcher = fieldNameMatcher(); @@ -133,45 +136,41 @@ public class GetStaticCommand extends AnnotatedCommand { value = ExpressFactory.threadLocalExpress(value).get(express); } - String result = StringUtils.objectToString(expand >= 0 ? new ObjectView(value, expand).draw() : value); - process.write("field: " + field.getName() + "\n" + result + "\n"); + process.appendResult(new GetStaticModel(field.getName(), value, expand)); affect.rCnt(1); } catch (IllegalAccessException e) { logger.warn("getstatic: failed to get static value, class: {}, field: {} ", clazz, field.getName(), e); - process.write("Failed to get static, exception message: " + e.getMessage() - + ", please check $HOME/logs/arthas/arthas.log for more details. \n"); + process.appendResult(new MessageModel("Failed to get static, exception message: " + e.getMessage() + + ", please check $HOME/logs/arthas/arthas.log for more details. ")); } catch (ExpressException e) { logger.warn("getstatic: failed to get express value, class: {}, field: {}, express: {}", clazz, field.getName(), express, e); - process.write("Failed to get static, exception message: " + e.getMessage() - + ", please check $HOME/logs/arthas/arthas.log for more details. \n"); + process.appendResult(new MessageModel("Failed to get static, exception message: " + e.getMessage() + + ", please check $HOME/logs/arthas/arthas.log for more details. ")); } finally { found = true; } } if (!found) { - process.write("getstatic: no matched static field was found\n"); + return ExitStatus.failure(-1, "getstatic: no matched static field was found"); + } else { + return ExitStatus.success(); } } - private void processMatches(CommandProcess process, Set> matchedClasses) { - Element usage = new LabelElement("getstatic -c " + classPattern + " " + fieldPattern).style( - Decoration.bold.fg(Color.blue)); - process.write("\n Found more than one class for: " + classPattern + ", Please use " + RenderUtil.render(usage, - process.width())); + private ExitStatus processMatches(CommandProcess process, Set> matchedClasses) { - TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); - table.row(new LabelElement("HASHCODE").style(Decoration.bold.bold()), - new LabelElement("CLASSLOADER").style(Decoration.bold.bold())); +// Element usage = new LabelElement("getstatic -c " + classPattern + " " + fieldPattern).style( +// Decoration.bold.fg(Color.blue)); +// process.write("\n Found more than one class for: " + classPattern + ", Please use " + RenderUtil.render(usage, process.width())); + //TODO support message style + String usage = "getstatic -c " + classPattern + " " + fieldPattern; + process.appendResult(new MessageModel("Found more than one class for: " + classPattern + ", Please use: "+usage)); - for (Class c : matchedClasses) { - ClassLoader classLoader = c.getClassLoader(); - table.row(label(Integer.toHexString(classLoader.hashCode())).style(Decoration.bold.fg(Color.red)), - TypeRenderUtils.drawClassLoader(c)); - } - - process.write(RenderUtil.render(table, process.width()) + "\n"); + List matchedClassVOs = ClassUtils.createClassVOList(matchedClasses); + process.appendResult(new GetStaticModel(matchedClassVOs)); + return ExitStatus.failure(-1, "Found more than one class for: " + classPattern + ", Please use: "+usage); } private Matcher fieldNameMatcher() { diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java index 0e008409e..cbb6ef955 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java @@ -3,38 +3,35 @@ package com.taobao.arthas.core.command.klass100; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.model.ClassVO; +import com.taobao.arthas.core.command.model.JadModel; +import com.taobao.arthas.core.command.model.MessageModel; +import com.taobao.arthas.core.command.model.RowAffectModel; import com.taobao.arthas.core.shell.cli.Completion; import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.shell.command.ExitStatus; import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.CommandUtils; import com.taobao.arthas.core.util.Decompiler; import com.taobao.arthas.core.util.InstrumentationUtils; import com.taobao.arthas.core.util.SearchUtils; -import com.taobao.arthas.core.util.TypeRenderUtils; import com.taobao.arthas.core.util.affect.RowAffect; import com.taobao.middleware.cli.annotations.Argument; import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Option; import com.taobao.middleware.cli.annotations.Summary; -import com.taobao.text.Color; -import com.taobao.text.Decoration; -import com.taobao.text.lang.LangRenderUtil; -import com.taobao.text.ui.Element; -import com.taobao.text.ui.LabelElement; -import com.taobao.text.ui.TableElement; -import com.taobao.text.util.RenderUtil; import java.io.File; import java.lang.instrument.Instrumentation; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; -import static com.taobao.text.ui.Element.label; - /** * @author diecui1202 on 15/11/24. * @author hengyunabc 2018-11-16 @@ -107,27 +104,30 @@ public class JadCommand extends AnnotatedCommand { Set> matchedClasses = SearchUtils.searchClassOnly(inst, classPattern, isRegEx, code); try { + ExitStatus status = null; if (matchedClasses == null || matchedClasses.isEmpty()) { - processNoMatch(process); + status = processNoMatch(process); } else if (matchedClasses.size() > 1) { - processMatches(process, matchedClasses); + status = processMatches(process, matchedClasses); } else { // matchedClasses size is 1 // find inner classes. Set> withInnerClasses = SearchUtils.searchClassOnly(inst, matchedClasses.iterator().next().getName() + "$*", false, code); if(withInnerClasses.isEmpty()) { withInnerClasses = matchedClasses; } - processExactMatch(process, affect, inst, matchedClasses, withInnerClasses); + status = processExactMatch(process, affect, inst, matchedClasses, withInnerClasses); } - } finally { if (!this.sourceOnly) { - process.write(affect + "\n"); + process.appendResult(new RowAffectModel(affect)); } - process.end(); + CommandUtils.end(process, status); + } catch (Throwable e){ + logger.error("processing error", e); + process.end(-1, "processing error"); } } - private void processExactMatch(CommandProcess process, RowAffect affect, Instrumentation inst, Set> matchedClasses, Set> withInnerClasses) { + private ExitStatus processExactMatch(CommandProcess process, RowAffect affect, Instrumentation inst, Set> matchedClasses, Set> withInnerClasses) { Class c = matchedClasses.iterator().next(); Set> allClasses = new HashSet>(withInnerClasses); allClasses.add(c); @@ -146,46 +146,38 @@ public class JadCommand extends AnnotatedCommand { source = "unknown"; } - if (this.sourceOnly) { - process.write(LangRenderUtil.render(source) + "\n"); - return; + JadModel jadModel = new JadModel(); + jadModel.setSource(source); + if (!this.sourceOnly) { + jadModel.setClassInfo(ClassUtils.createSimpleClassInfo(c)); + jadModel.setLocation(ClassUtils.getCodeSource(c.getProtectionDomain().getCodeSource())); } + process.appendResult(jadModel); - - process.write("\n"); - process.write(RenderUtil.render(new LabelElement("ClassLoader: ").style(Decoration.bold.fg(Color.red)), process.width())); - process.write(RenderUtil.render(TypeRenderUtils.drawClassLoader(c), process.width()) + "\n"); - process.write(RenderUtil.render(new LabelElement("Location: ").style(Decoration.bold.fg(Color.red)), process.width())); - process.write(RenderUtil.render(new LabelElement(ClassUtils.getCodeSource( - c.getProtectionDomain().getCodeSource())).style(Decoration.bold.fg(Color.blue)), process.width()) + "\n"); - process.write(LangRenderUtil.render(source) + "\n"); - process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); affect.rCnt(classFiles.keySet().size()); + return ExitStatus.success(); } catch (Throwable t) { logger.error("jad: fail to decompile class: " + c.getName(), t); + return ExitStatus.failure(-1, "jad: fail to decompile class: " + c.getName()); } } - private void processMatches(CommandProcess process, Set> matchedClasses) { - Element usage = new LabelElement("jad -c " + classPattern).style(Decoration.bold.fg(Color.blue)); - process.write("\n Found more than one class for: " + classPattern + ", Please use " - + RenderUtil.render(usage, process.width())); + private ExitStatus processMatches(CommandProcess process, Set> matchedClasses) { - TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); - table.row(new LabelElement("HASHCODE").style(Decoration.bold.bold()), - new LabelElement("CLASSLOADER").style(Decoration.bold.bold())); + String usage = "jad -c " + classPattern; + String msg = " Found more than one class for: " + classPattern + ", Please use " + usage; + process.appendResult(new MessageModel(msg)); - for (Class c : matchedClasses) { - ClassLoader classLoader = c.getClassLoader(); - table.row(label(Integer.toHexString(classLoader.hashCode())).style(Decoration.bold.fg(Color.red)), - TypeRenderUtils.drawClassLoader(c)); - } + List classVOs = ClassUtils.createClassVOList(matchedClasses); + JadModel jadModel = new JadModel(); + jadModel.setMatchedClasses(classVOs); + process.appendResult(jadModel); - process.write(RenderUtil.render(table, process.width()) + "\n"); + return ExitStatus.failure(-1, msg); } - private void processNoMatch(CommandProcess process) { - process.write("No class found for: " + classPattern + "\n"); + private ExitStatus processNoMatch(CommandProcess process) { + return ExitStatus.failure(-1, "No class found for: " + classPattern); } @Override diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/MemoryCompilerCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/MemoryCompilerCommand.java index 0ed22c5fb..c252f939f 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/MemoryCompilerCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/MemoryCompilerCommand.java @@ -3,6 +3,7 @@ package com.taobao.arthas.core.command.klass100; import java.io.File; import java.lang.instrument.Instrumentation; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -11,6 +12,8 @@ import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.compiler.DynamicCompiler; import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.model.MemoryCompilerModel; +import com.taobao.arthas.core.command.model.RowAffectModel; import com.taobao.arthas.core.shell.cli.Completion; import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.command.AnnotatedCommand; @@ -70,7 +73,6 @@ public class MemoryCompilerCommand extends AnnotatedCommand { @Override public void process(final CommandProcess process) { - int exitCode = 0; RowAffect affect = new RowAffect(); try { @@ -81,8 +83,7 @@ public class MemoryCompilerCommand extends AnnotatedCommand { } else { classloader = ClassLoaderUtils.getClassLoader(inst, hashCode); if (classloader == null) { - process.write("Can not find classloader with hashCode: " + hashCode + ".\n"); - exitCode = -1; + process.end(-1, "Can not find classloader with hashCode: " + hashCode + "."); return; } } @@ -112,22 +113,20 @@ public class MemoryCompilerCommand extends AnnotatedCommand { outputDir = new File("").getAbsoluteFile(); } - process.write("Memory compiler output:\n"); + List files = new ArrayList(); for (Entry entry : byteCodes.entrySet()) { File byteCodeFile = new File(outputDir, entry.getKey().replace('.', '/') + ".class"); FileUtils.writeByteArrayToFile(byteCodeFile, entry.getValue()); - process.write(byteCodeFile.getAbsolutePath() + '\n'); + files.add(byteCodeFile.getAbsolutePath()); affect.rCnt(1); } - + process.appendResult(new MemoryCompilerModel(files)); + process.appendResult(new RowAffectModel(affect)); + process.end(); } catch (Throwable e) { logger.warn("Memory compiler error", e); - process.write("Memory compiler error, exception message: " + e.getMessage() - + ", please check $HOME/logs/arthas/arthas.log for more details. \n"); - exitCode = -1; - } finally { - process.write(affect + "\n"); - process.end(exitCode); + process.end(-1, "Memory compiler error, exception message: " + e.getMessage() + + ", please check $HOME/logs/arthas/arthas.log for more details."); } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/OgnlCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/OgnlCommand.java index f5979d6de..c1cbaa14c 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/OgnlCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/OgnlCommand.java @@ -8,11 +8,10 @@ import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.express.Express; import com.taobao.arthas.core.command.express.ExpressException; import com.taobao.arthas.core.command.express.ExpressFactory; +import com.taobao.arthas.core.command.model.OgnlModel; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; import com.taobao.arthas.core.util.ClassLoaderUtils; -import com.taobao.arthas.core.util.StringUtils; -import com.taobao.arthas.core.view.ObjectView; import com.taobao.middleware.cli.annotations.Argument; import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Name; @@ -62,36 +61,31 @@ public class OgnlCommand extends AnnotatedCommand { @Override public void process(CommandProcess process) { - int exitCode = 0; + Instrumentation inst = process.session().getInstrumentation(); + ClassLoader classLoader = null; + if (hashCode == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } else { + classLoader = ClassLoaderUtils.getClassLoader(inst, hashCode); + } + + if (classLoader == null) { + process.end(-1, "Can not find classloader with hashCode: " + hashCode + "."); + return; + } + + Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader); try { - Instrumentation inst = process.session().getInstrumentation(); - ClassLoader classLoader = null; - if (hashCode == null) { - classLoader = ClassLoader.getSystemClassLoader(); - } else { - classLoader = ClassLoaderUtils.getClassLoader(inst, hashCode); - } - - if (classLoader == null) { - process.write("Can not find classloader with hashCode: " + hashCode + ".\n"); - exitCode = -1; - return; - } - - Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader); - try { - Object value = unpooledExpress.get(express); - String result = StringUtils.objectToString(expand >= 0 ? new ObjectView(value, expand).draw() : value); - process.write(result + "\n"); - } catch (ExpressException e) { - logger.warn("ognl: failed execute express: " + express, e); - process.write("Failed to execute ognl, exception message: " + e.getMessage() - + ", please check $HOME/logs/arthas/arthas.log for more details. \n"); - exitCode = -1; - } - } finally { - process.end(exitCode); + Object value = unpooledExpress.get(express); + OgnlModel ognlModel = new OgnlModel() + .setValue(value) + .setExpand(expand); + process.appendResult(ognlModel); + process.end(); + } catch (ExpressException e) { + logger.warn("ognl: failed execute express: " + express, e); + process.end(-1, "Failed to execute ognl, exception message: " + e.getMessage() + + ", please check $HOME/logs/arthas/arthas.log for more details. "); } } - } diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java index 5bcdc2780..8a7595b43 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java @@ -14,6 +14,7 @@ import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.model.RedefineModel; import com.taobao.arthas.core.shell.cli.Completion; import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.command.AnnotatedCommand; @@ -58,23 +59,20 @@ public class RedefineCommand extends AnnotatedCommand { @Override public void process(CommandProcess process) { + RedefineModel redefineModel = new RedefineModel(); Instrumentation inst = process.session().getInstrumentation(); - for (String path : paths) { File file = new File(path); if (!file.exists()) { - process.write("file does not exist, path:" + path + "\n"); - process.end(); + process.end(-1, "file does not exist, path:" + path); return; } if (!file.isFile()) { - process.write("not a normal file, path:" + path + "\n"); - process.end(); + process.end(-1, "not a normal file, path:" + path); return; } if (file.length() >= MAX_FILE_SIZE) { - process.write("file size: " + file.length() + " >= " + MAX_FILE_SIZE + ", path: " + path + "\n"); - process.end(); + process.end(-1, "file size: " + file.length() + " >= " + MAX_FILE_SIZE + ", path: " + path); return; } } @@ -92,7 +90,9 @@ public class RedefineCommand extends AnnotatedCommand { bytesMap.put(clazzName, bytes); } catch (Exception e) { - process.write("" + e + "\n"); + logger.warn("load class file failed: "+path, e); + process.end(-1, "load class file failed: " +path+", error: " + e); + return; } finally { if (f != null) { try { @@ -105,8 +105,7 @@ public class RedefineCommand extends AnnotatedCommand { } if (bytesMap.size() != paths.size()) { - process.write("paths may contains same class name!\n"); - process.end(); + process.end(-1, "paths may contains same class name!"); return; } @@ -118,24 +117,25 @@ public class RedefineCommand extends AnnotatedCommand { continue; } definitions.add(new ClassDefinition(clazz, bytesMap.get(clazz.getName()))); + redefineModel.addRedefineClass(clazz.getName()); logger.info("Try redefine class name: {}, ClassLoader: {}", clazz.getName(), clazz.getClassLoader()); } } try { if (definitions.isEmpty()) { - process.write("These classes are not found in the JVM and may not be loaded: " + bytesMap.keySet() - + "\n"); - process.end(); + process.end(-1, "These classes are not found in the JVM and may not be loaded: " + bytesMap.keySet()); return; } inst.redefineClasses(definitions.toArray(new ClassDefinition[0])); - process.write("redefine success, size: " + definitions.size() + "\n"); - } catch (Exception e) { - process.write("redefine error! " + e + "\n"); + process.appendResult(redefineModel); + process.end(); + } catch (Throwable e) { + String message = "redefine error! " + e.toString(); + logger.error(message, e); + process.end(-1, message); } - process.end(); } private static String readClassName(final byte[] bytes) { 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 1274c01c3..daacfbb9a 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 @@ -8,11 +8,15 @@ import java.util.Comparator; import java.util.List; import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.model.ClassDetailVO; +import com.taobao.arthas.core.command.model.SearchClassModel; +import com.taobao.arthas.core.command.model.RowAffectModel; import com.taobao.arthas.core.shell.cli.Completion; import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.ResultUtils; import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.StringUtils; import com.taobao.arthas.core.util.affect.RowAffect; @@ -21,7 +25,6 @@ import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Option; import com.taobao.middleware.cli.annotations.Summary; -import com.taobao.text.util.RenderUtil; /** * 展示类信息 @@ -44,6 +47,7 @@ public class SearchClassCommand extends AnnotatedCommand { private boolean isRegEx = false; private String hashCode = null; private Integer expand; + private int numberOfLimit = 100; @Argument(argName = "class-pattern", index = 0) @Description("Class name pattern, use either '.' or '/' as separator") @@ -81,8 +85,14 @@ public class SearchClassCommand extends AnnotatedCommand { this.hashCode = hashCode; } + @Option(shortName = "n", longName = "limits") + @Description("Maximum number of matching classes with details (100 by default)") + public void setNumberOfLimit(int numberOfLimit) { + this.numberOfLimit = numberOfLimit; + } + @Override - public void process(CommandProcess process) { + public void process(final CommandProcess process) { // TODO: null check RowAffect affect = new RowAffect(); Instrumentation inst = process.session().getInstrumentation(); @@ -94,23 +104,32 @@ public class SearchClassCommand extends AnnotatedCommand { } }); - for (Class clazz : matchedClasses) { - processClass(process, clazz); + if (isDetail) { + if (numberOfLimit > 0 && matchedClasses.size() > numberOfLimit) { + process.end(-1, "The number of matching classes is greater than : " + numberOfLimit+". \n" + + "Please specify a more accurate 'class-patten' or use the parameter '-n' to change the maximum number of matching classes."); + return; + } + for (Class clazz : matchedClasses) { + ClassDetailVO classInfo = ClassUtils.createClassInfo(clazz, isField); + process.appendResult(new SearchClassModel(classInfo, isDetail, isField, expand)); + } + } else { + int pageSize = 256; + ResultUtils.processClassNames(matchedClasses, pageSize, new ResultUtils.PaginationHandler>() { + @Override + public boolean handle(List classNames, int segment) { + process.appendResult(new SearchClassModel(classNames, segment)); + return true; + } + }); } affect.rCnt(matchedClasses.size()); - process.write(affect + "\n"); + process.appendResult(new RowAffectModel(affect)); process.end(); } - private void processClass(CommandProcess process, Class clazz) { - if (isDetail) { - process.write(RenderUtil.render(ClassUtils.renderClassInfo(clazz, isField, expand), process.width()) + "\n"); - } else { - process.write(clazz.getName() + "\n"); - } - } - @Override public void complete(Completion completion) { if (!CompletionUtils.completeClassName(completion)) { diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java index c40e0093f..2986e8c56 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java @@ -1,8 +1,5 @@ package com.taobao.arthas.core.command.klass100; -import static com.taobao.text.Decoration.bold; -import static com.taobao.text.ui.Element.label; -import static java.lang.String.format; import java.lang.instrument.Instrumentation; import java.lang.reflect.Constructor; @@ -12,13 +9,16 @@ import java.util.Set; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.model.SearchMethodModel; +import com.taobao.arthas.core.command.model.MethodVO; +import com.taobao.arthas.core.command.model.RowAffectModel; import com.taobao.arthas.core.shell.cli.Completion; import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.ClassUtils; import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.StringUtils; -import com.taobao.arthas.core.util.TypeRenderUtils; import com.taobao.arthas.core.util.affect.RowAffect; import com.taobao.arthas.core.util.matcher.Matcher; import com.taobao.arthas.core.util.matcher.RegexMatcher; @@ -28,9 +28,6 @@ import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Option; import com.taobao.middleware.cli.annotations.Summary; -import com.taobao.text.ui.Element; -import com.taobao.text.ui.TableElement; -import com.taobao.text.util.RenderUtil; /** * 展示方法信息 @@ -55,6 +52,7 @@ public class SearchMethodCommand extends AnnotatedCommand { private String hashCode = null; private boolean isDetail = false; private boolean isRegEx = false; + private int numberOfLimit = 100; @Argument(argName = "class-pattern", index = 0) @Description("Class name pattern, use either '.' or '/' as separator") @@ -86,6 +84,12 @@ public class SearchMethodCommand extends AnnotatedCommand { this.hashCode = hashCode; } + @Option(shortName = "n", longName = "limits") + @Description("Maximum number of matching classes (100 by default)") + public void setNumberOfLimit(int numberOfLimit) { + this.numberOfLimit = numberOfLimit; + } + @Override public void process(CommandProcess process) { RowAffect affect = new RowAffect(); @@ -94,47 +98,41 @@ public class SearchMethodCommand extends AnnotatedCommand { Matcher methodNameMatcher = methodNameMatcher(); Set> matchedClasses = SearchUtils.searchClass(inst, classPattern, isRegEx, hashCode); + if (numberOfLimit > 0 && matchedClasses.size() > numberOfLimit) { + process.end(-1, "The number of matching classes is greater than : " + numberOfLimit+". \n" + + "Please specify a more accurate 'class-patten' or use the parameter '-n' to change the maximum number of matching classes."); + return; + } for (Class clazz : matchedClasses) { try { for (Constructor constructor : clazz.getDeclaredConstructors()) { - String methodNameWithDescriptor = com.alibaba.arthas.deps.org.objectweb.asm.commons.Method.getMethod(constructor).toString(); if (!methodNameMatcher.matching("")) { continue; } - if (isDetail) { - process.write(RenderUtil.render(renderConstructor(constructor, clazz), process.width()) + "\n"); - } else { - String line = format("%s %s%n", clazz.getName(), methodNameWithDescriptor); - process.write(line); - } + MethodVO methodInfo = ClassUtils.createMethodInfo(constructor, clazz, isDetail); + process.appendResult(new SearchMethodModel(methodInfo, isDetail)); affect.rCnt(1); } for (Method method : clazz.getDeclaredMethods()) { - String methodNameWithDescriptor = com.alibaba.arthas.deps.org.objectweb.asm.commons.Method.getMethod(method).toString(); if (!methodNameMatcher.matching(method.getName())) { continue; } - - if (isDetail) { - process.write(RenderUtil.render(renderMethod(method, clazz), process.width()) + "\n"); - } else { - String line = format("%s %s%n", clazz.getName(), methodNameWithDescriptor); - process.write(line); - } + MethodVO methodInfo = ClassUtils.createMethodInfo(method, clazz, isDetail); + process.appendResult(new SearchMethodModel(methodInfo, isDetail)); affect.rCnt(1); } } catch (Error e) { //print failed className String msg = String.format("process class failed: %s, error: %s", clazz.getName(), e.toString()); logger.error(msg, e); - process.write(msg).write("\n"); - throw e; + process.end(1, msg); + return; } } - process.write(affect + "\n"); + process.appendResult(new RowAffectModel(affect)); process.end(); } @@ -146,33 +144,6 @@ public class SearchMethodCommand extends AnnotatedCommand { return isRegEx ? new RegexMatcher(methodPattern) : new WildcardMatcher(methodPattern); } - private Element renderMethod(Method method, Class clazz) { - TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); - - table.row(label("declaring-class").style(bold.bold()), label(method.getDeclaringClass().getName())) - .row(label("method-name").style(bold.bold()), label(method.getName()).style(bold.bold())) - .row(label("modifier").style(bold.bold()), label(StringUtils.modifier(method.getModifiers(), ','))) - .row(label("annotation").style(bold.bold()), label(TypeRenderUtils.drawAnnotation(method))) - .row(label("parameters").style(bold.bold()), label(TypeRenderUtils.drawParameters(method))) - .row(label("return").style(bold.bold()), label(TypeRenderUtils.drawReturn(method))) - .row(label("exceptions").style(bold.bold()), label(TypeRenderUtils.drawExceptions(method))) - .row(label("classLoaderHash").style(bold.bold()), label(StringUtils.classLoaderHash(clazz))); - return table; - } - - private Element renderConstructor(Constructor constructor, Class clazz) { - TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); - - table.row(label("declaring-class").style(bold.bold()), label(constructor.getDeclaringClass().getName())) - .row(label("constructor-name").style(bold.bold()), label("").style(bold.bold())) - .row(label("modifier").style(bold.bold()), label(StringUtils.modifier(constructor.getModifiers(), ','))) - .row(label("annotation").style(bold.bold()), label(TypeRenderUtils.drawAnnotation(constructor.getDeclaredAnnotations()))) - .row(label("parameters").style(bold.bold()), label(TypeRenderUtils.drawParameters(constructor))) - .row(label("exceptions").style(bold.bold()), label(TypeRenderUtils.drawExceptions(constructor))) - .row(label("classLoaderHash").style(bold.bold()), label(StringUtils.classLoaderHash(clazz))); - return table; - } - @Override public void complete(Completion completion) { int argumentIndex = CompletionUtils.detectArgumentIndex(completion); diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/ClassDetailVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/ClassDetailVO.java new file mode 100644 index 000000000..a66a11ee8 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/ClassDetailVO.java @@ -0,0 +1,163 @@ +package com.taobao.arthas.core.command.model; + +/** + * Class detail VO + * @author gongdewei 2020/4/8 + */ +public class ClassDetailVO extends ClassVO { + + private String classInfo; + private String codeSource; + private boolean isInterface; + private boolean isAnnotation; + private boolean isEnum; + private boolean isAnonymousClass; + private boolean isArray; + private boolean isLocalClass; + private boolean isMemberClass; + private boolean isPrimitive; + private boolean isSynthetic; + private String simpleName; + private String modifier; + private String[] annotations; + private String[] interfaces; + private String[] superClass; + private FieldVO[] fields; + + public String getClassInfo() { + return classInfo; + } + + public void setClassInfo(String classInfo) { + this.classInfo = classInfo; + } + + public String getCodeSource() { + return codeSource; + } + + public void setCodeSource(String codeSource) { + this.codeSource = codeSource; + } + + public boolean isInterface() { + return isInterface; + } + + public void setInterface(boolean anInterface) { + isInterface = anInterface; + } + + public boolean isAnnotation() { + return isAnnotation; + } + + public void setAnnotation(boolean annotation) { + isAnnotation = annotation; + } + + public boolean isEnum() { + return isEnum; + } + + public void setEnum(boolean anEnum) { + isEnum = anEnum; + } + + public boolean isAnonymousClass() { + return isAnonymousClass; + } + + public void setAnonymousClass(boolean anonymousClass) { + isAnonymousClass = anonymousClass; + } + + public boolean isArray() { + return isArray; + } + + public void setArray(boolean array) { + isArray = array; + } + + public boolean isLocalClass() { + return isLocalClass; + } + + public void setLocalClass(boolean localClass) { + isLocalClass = localClass; + } + + public boolean isMemberClass() { + return isMemberClass; + } + + public void setMemberClass(boolean memberClass) { + isMemberClass = memberClass; + } + + public boolean isPrimitive() { + return isPrimitive; + } + + public void setPrimitive(boolean primitive) { + isPrimitive = primitive; + } + + public boolean isSynthetic() { + return isSynthetic; + } + + public void setSynthetic(boolean synthetic) { + isSynthetic = synthetic; + } + + public String getSimpleName() { + return simpleName; + } + + public void setSimpleName(String simpleName) { + this.simpleName = simpleName; + } + + public String getModifier() { + return modifier; + } + + public void setModifier(String modifier) { + this.modifier = modifier; + } + + public String[] getAnnotations() { + return annotations; + } + + public void setAnnotations(String[] annotations) { + this.annotations = annotations; + } + + public String[] getInterfaces() { + return interfaces; + } + + public void setInterfaces(String[] interfaces) { + this.interfaces = interfaces; + } + + public String[] getSuperClass() { + return superClass; + } + + public void setSuperClass(String[] superClass) { + this.superClass = superClass; + } + + public FieldVO[] getFields() { + return fields; + } + + public void setFields(FieldVO[] fields) { + this.fields = fields; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderModel.java new file mode 100644 index 000000000..36afd3479 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderModel.java @@ -0,0 +1,93 @@ +package com.taobao.arthas.core.command.model; + +import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.ClassLoaderStat; + +import java.util.List; +import java.util.Map; + +/** + * @author gongdewei 2020/4/21 + */ +public class ClassLoaderModel extends ResultModel { + + private ClassSetVO classSet; + private List resources; + private ClassDetailVO loadClass; + private List urls; + //classloader -l -t + private List classLoaders; + private Boolean tree; + + private Map classLoaderStats; + + public ClassLoaderModel() { + } + + @Override + public String getType() { + return "classloader"; + } + + public ClassSetVO getClassSet() { + return classSet; + } + + public ClassLoaderModel setClassSet(ClassSetVO classSet) { + this.classSet = classSet; + return this; + } + + public List getResources() { + return resources; + } + + public ClassLoaderModel setResources(List resources) { + this.resources = resources; + return this; + } + + public ClassDetailVO getLoadClass() { + return loadClass; + } + + public ClassLoaderModel setLoadClass(ClassDetailVO loadClass) { + this.loadClass = loadClass; + return this; + } + + public List getUrls() { + return urls; + } + + public ClassLoaderModel setUrls(List urls) { + this.urls = urls; + return this; + } + + public List getClassLoaders() { + return classLoaders; + } + + public ClassLoaderModel setClassLoaders(List classLoaders) { + this.classLoaders = classLoaders; + return this; + } + + public Boolean getTree() { + return tree; + } + + public ClassLoaderModel setTree(Boolean tree) { + this.tree = tree; + return this; + } + + public Map getClassLoaderStats() { + return classLoaderStats; + } + + public ClassLoaderModel setClassLoaderStats(Map classLoaderStats) { + this.classLoaderStats = classLoaderStats; + return this; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderVO.java new file mode 100644 index 000000000..802bfbec1 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderVO.java @@ -0,0 +1,74 @@ +package com.taobao.arthas.core.command.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author gongdewei 2020/4/21 + */ +public class ClassLoaderVO { + private String name; + private String hash; + private String parent; + private Integer loadedCount; + private Integer numberOfInstances; + private List children; + + public ClassLoaderVO() { + } + + public void addChild(ClassLoaderVO child){ + if (this.children == null){ + this.children = new ArrayList(); + } + this.children.add(child); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + + public String getParent() { + return parent; + } + + public void setParent(String parent) { + this.parent = parent; + } + + public Integer getLoadedCount() { + return loadedCount; + } + + public void setLoadedCount(Integer loadedCount) { + this.loadedCount = loadedCount; + } + + public Integer getNumberOfInstances() { + return numberOfInstances; + } + + public void setNumberOfInstances(Integer numberOfInstances) { + this.numberOfInstances = numberOfInstances; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/ClassSetVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/ClassSetVO.java new file mode 100644 index 000000000..3cb3e3219 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/ClassSetVO.java @@ -0,0 +1,51 @@ +package com.taobao.arthas.core.command.model; + +import java.util.Collection; + +/** + * @author gongdewei 2020/4/21 + */ +public class ClassSetVO implements Countable { + private ClassLoaderVO classloader; + private Collection classes; + private int segment; + + public ClassSetVO(ClassLoaderVO classloader, Collection classes) { + this(classloader, classes, 0); + } + + public ClassSetVO(ClassLoaderVO classloader, Collection classes, int segment) { + this.classloader = classloader; + this.classes = classes; + this.segment = segment; + } + + public ClassLoaderVO getClassloader() { + return classloader; + } + + public void setClassloader(ClassLoaderVO classloader) { + this.classloader = classloader; + } + + public Collection getClasses() { + return classes; + } + + public void setClasses(Collection classes) { + this.classes = classes; + } + + public int getSegment() { + return segment; + } + + public void setSegment(int segment) { + this.segment = segment; + } + + @Override + public int size() { + return classes != null ? classes.size() : 1; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/ClassVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/ClassVO.java new file mode 100644 index 000000000..fea8a6d40 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/ClassVO.java @@ -0,0 +1,35 @@ +package com.taobao.arthas.core.command.model; + +/** + * @author gongdewei 2020/4/8 + */ +public class ClassVO { + + private String name; + private String[] classloader; + private String classLoaderHash; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String[] getClassloader() { + return classloader; + } + + public void setClassloader(String[] classloader) { + this.classloader = classloader; + } + + public String getClassLoaderHash() { + return classLoaderHash; + } + + public void setClassLoaderHash(String classLoaderHash) { + this.classLoaderHash = classLoaderHash; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/DumpClassModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/DumpClassModel.java new file mode 100644 index 000000000..e7b8bd684 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/DumpClassModel.java @@ -0,0 +1,41 @@ +package com.taobao.arthas.core.command.model; + +import java.util.Collection; +import java.util.List; + +/** + * @author gongdewei 2020/4/21 + */ +public class DumpClassModel extends ResultModel { + + private List dumpedClasses; + + private Collection matchedClasses; + + public DumpClassModel() { + } + + @Override + public String getType() { + return "dump"; + } + + public List getDumpedClasses() { + return dumpedClasses; + } + + public DumpClassModel setDumpedClasses(List dumpedClasses) { + this.dumpedClasses = dumpedClasses; + return this; + } + + public Collection getMatchedClasses() { + return matchedClasses; + } + + public DumpClassModel setMatchedClasses(Collection matchedClasses) { + this.matchedClasses = matchedClasses; + return this; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/DumpClassVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/DumpClassVO.java new file mode 100644 index 000000000..9605299ab --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/DumpClassVO.java @@ -0,0 +1,17 @@ +package com.taobao.arthas.core.command.model; + +/** + * Dumped class VO + * @author gongdewei 2020/7/9 + */ +public class DumpClassVO extends ClassVO { + private String location; + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/FieldVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/FieldVO.java new file mode 100644 index 000000000..79615ff4d --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/FieldVO.java @@ -0,0 +1,61 @@ +package com.taobao.arthas.core.command.model; + +/** + * @author gongdewei 2020/4/8 + */ +public class FieldVO { + private String name; + private String type; + private String modifier; + private String[] annotations; + private Object value; + private boolean isStatic; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getModifier() { + return modifier; + } + + public void setModifier(String modifier) { + this.modifier = modifier; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public String[] getAnnotations() { + return annotations; + } + + public void setAnnotations(String[] annotations) { + this.annotations = annotations; + } + + public boolean isStatic() { + return isStatic; + } + + public void setStatic(boolean aStatic) { + isStatic = aStatic; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/GetStaticModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/GetStaticModel.java new file mode 100644 index 000000000..b4c62842d --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/GetStaticModel.java @@ -0,0 +1,56 @@ +package com.taobao.arthas.core.command.model; + +import java.util.Collection; + +/** + * @author gongdewei 2020/4/20 + */ +public class GetStaticModel extends ResultModel { + + private ObjectVO field; + private Collection matchedClasses; + + //only for view + private transient int expand; + + public GetStaticModel() { + } + + public GetStaticModel(String fieldName, Object fieldValue, int expand) { + this.field = new ObjectVO(fieldName, fieldValue); + this.expand = expand; + } + + public GetStaticModel(Collection matchedClasses) { + this.matchedClasses = matchedClasses; + } + + public ObjectVO getField() { + return field; + } + + public void setField(ObjectVO field) { + this.field = field; + } + + public Collection getMatchedClasses() { + return matchedClasses; + } + + public void setMatchedClasses(Collection matchedClasses) { + this.matchedClasses = matchedClasses; + } + + public int expand() { + return expand; + } + + public void setExpand(int expand) { + this.expand = expand; + } + + @Override + public String getType() { + return "getstatic"; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/JadModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/JadModel.java new file mode 100644 index 000000000..e108a9a29 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/JadModel.java @@ -0,0 +1,55 @@ +package com.taobao.arthas.core.command.model; + +import java.util.Collection; + +/** + * @author gongdewei 2020/4/22 + */ +public class JadModel extends ResultModel { + private ClassVO classInfo; + private String location; + private String source; + + //match multiple classes + private Collection matchedClasses; + + @Override + public String getType() { + return "jad"; + } + + public JadModel() { + } + + public ClassVO getClassInfo() { + return classInfo; + } + + public void setClassInfo(ClassVO classInfo) { + this.classInfo = classInfo; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public Collection getMatchedClasses() { + return matchedClasses; + } + + public void setMatchedClasses(Collection matchedClasses) { + this.matchedClasses = matchedClasses; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/MemoryCompilerModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/MemoryCompilerModel.java new file mode 100644 index 000000000..59d9b732c --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/MemoryCompilerModel.java @@ -0,0 +1,32 @@ +package com.taobao.arthas.core.command.model; + +import java.util.List; + +/** + * @author gongdewei 2020/4/20 + */ +public class MemoryCompilerModel extends ResultModel { + + private List files; + + public MemoryCompilerModel() { + } + + public MemoryCompilerModel(List files) { + this.files = files; + } + + public void setFiles(List files) { + this.files = files; + } + + public List getFiles() { + return files; + } + + @Override + public String getType() { + return "mc"; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/MethodVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/MethodVO.java new file mode 100644 index 000000000..2f6cdaceb --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/MethodVO.java @@ -0,0 +1,99 @@ +package com.taobao.arthas.core.command.model; + +/** + * Method or Constructor VO + * @author gongdewei 2020/4/9 + */ +public class MethodVO { + + private String declaringClass; + private String methodName; + private String modifier; + private String[] annotations; + private String[] parameters; + private String returnType; + private String[] exceptions; + private String classLoaderHash; + private String descriptor; + private boolean constructor; + + public String getDeclaringClass() { + return declaringClass; + } + + public void setDeclaringClass(String declaringClass) { + this.declaringClass = declaringClass; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String getModifier() { + return modifier; + } + + public void setModifier(String modifier) { + this.modifier = modifier; + } + + public String[] getAnnotations() { + return annotations; + } + + public void setAnnotations(String[] annotations) { + this.annotations = annotations; + } + + public String[] getParameters() { + return parameters; + } + + public void setParameters(String[] parameters) { + this.parameters = parameters; + } + + public String getReturnType() { + return returnType; + } + + public void setReturnType(String returnType) { + this.returnType = returnType; + } + + public String[] getExceptions() { + return exceptions; + } + + public void setExceptions(String[] exceptions) { + this.exceptions = exceptions; + } + + public String getClassLoaderHash() { + return classLoaderHash; + } + + public void setClassLoaderHash(String classLoaderHash) { + this.classLoaderHash = classLoaderHash; + } + + public boolean isConstructor() { + return constructor; + } + + public void setConstructor(boolean constructor) { + this.constructor = constructor; + } + + public String getDescriptor() { + return descriptor; + } + + public void setDescriptor(String descriptor) { + this.descriptor = descriptor; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/ObjectVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/ObjectVO.java new file mode 100644 index 000000000..621857f67 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/ObjectVO.java @@ -0,0 +1,34 @@ +package com.taobao.arthas.core.command.model; + +/** + * @author gongdewei 2020/4/29 + */ +public class ObjectVO { + private String name; + private Object value; + + public ObjectVO(String name, Object value) { + this.name = name; + this.value = value; + } + + public ObjectVO(Object value) { + this.value = value; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/OgnlModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/OgnlModel.java new file mode 100644 index 000000000..d90f79e0d --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/OgnlModel.java @@ -0,0 +1,33 @@ +package com.taobao.arthas.core.command.model; + +/** + * @author gongdewei 2020/4/29 + */ +public class OgnlModel extends ResultModel { + private Object value; + private int expand = 1; + + + @Override + public String getType() { + return "ognl"; + } + + public Object getValue() { + return value; + } + + public OgnlModel setValue(Object value) { + this.value = value; + return this; + } + + public int getExpand() { + return expand; + } + + public OgnlModel setExpand(int expand) { + this.expand = expand; + return this; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/RedefineModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/RedefineModel.java new file mode 100644 index 000000000..0b6a17833 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/RedefineModel.java @@ -0,0 +1,45 @@ +package com.taobao.arthas.core.command.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author gongdewei 2020/4/16 + */ +public class RedefineModel extends ResultModel { + + private int redefinitionCount; + + private List redefinedClasses; + + public RedefineModel() { + redefinedClasses = new ArrayList(); + } + + public void addRedefineClass(String className) { + redefinedClasses.add(className); + redefinitionCount++; + } + + public int getRedefinitionCount() { + return redefinitionCount; + } + + public void setRedefinitionCount(int redefinitionCount) { + this.redefinitionCount = redefinitionCount; + } + + public List getRedefinedClasses() { + return redefinedClasses; + } + + public void setRedefinedClasses(List redefinedClasses) { + this.redefinedClasses = redefinedClasses; + } + + @Override + public String getType() { + return "redefine"; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/RowAffectModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/RowAffectModel.java new file mode 100644 index 000000000..05a056367 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/RowAffectModel.java @@ -0,0 +1,30 @@ +package com.taobao.arthas.core.command.model; + +import com.taobao.arthas.core.util.affect.RowAffect; + +/** + * @author gongdewei 2020/4/8 + */ +public class RowAffectModel extends ResultModel { + private RowAffect affect; + + public RowAffectModel() { + } + + public RowAffectModel(RowAffect affect) { + this.affect = affect; + } + + @Override + public String getType() { + return "row_affect"; + } + + public int getRowCount() { + return affect.rCnt(); + } + + public RowAffect affect() { + return affect; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/SearchClassModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/SearchClassModel.java new file mode 100644 index 000000000..b4e6ee00c --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/SearchClassModel.java @@ -0,0 +1,73 @@ +package com.taobao.arthas.core.command.model; + + +import java.util.List; + +/** + * Class info of SearchClassCommand + * @author gongdewei 2020/04/08 + */ +public class SearchClassModel extends ResultModel { + private ClassDetailVO classInfo; + private boolean withField; + private boolean detailed; + private Integer expand; + private List classNames; + private int segment; + + public SearchClassModel() { + } + + public SearchClassModel(ClassDetailVO classInfo, boolean detailed, boolean withField, Integer expand) { + this.classInfo = classInfo; + this.detailed = detailed; + this.withField = withField; + this.expand = expand; + } + + public SearchClassModel(List classNames, int segment) { + this.classNames = classNames; + this.segment = segment; + } + + @Override + public String getType() { + return "sc"; + } + + public ClassDetailVO getClassInfo() { + return classInfo; + } + + public void setClassInfo(ClassDetailVO classInfo) { + this.classInfo = classInfo; + } + + public List getClassNames() { + return classNames; + } + + public void setClassNames(List classNames) { + this.classNames = classNames; + } + + public int getSegment() { + return segment; + } + + public void setSegment(int segment) { + this.segment = segment; + } + + public boolean isDetailed() { + return detailed; + } + + public boolean isWithField() { + return withField; + } + + public Integer getExpand() { + return expand; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/SearchMethodModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/SearchMethodModel.java new file mode 100644 index 000000000..ba77192a3 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/SearchMethodModel.java @@ -0,0 +1,40 @@ +package com.taobao.arthas.core.command.model; + + +/** + * Model of SearchMethodCommand + * @author gongdewei 2020/4/9 + */ +public class SearchMethodModel extends ResultModel { + private MethodVO methodInfo; + private boolean detail; + + public SearchMethodModel() { + } + + public SearchMethodModel(MethodVO methodInfo, boolean detail) { + this.methodInfo = methodInfo; + this.detail = detail; + } + + public MethodVO getMethodInfo() { + return methodInfo; + } + + public void setMethodInfo(MethodVO methodInfo) { + this.methodInfo = methodInfo; + } + + public boolean isDetail() { + return detail; + } + + public void setDetail(boolean detail) { + this.detail = detail; + } + + @Override + public String getType() { + return "sm"; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/StatusModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/StatusModel.java index 4b43baf7c..dcb023553 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/model/StatusModel.java +++ b/core/src/main/java/com/taobao/arthas/core/command/model/StatusModel.java @@ -1,23 +1,10 @@ package com.taobao.arthas.core.command.model; public class StatusModel extends ResultModel { - public static StatusModel success() { - return new StatusModel(0); - } - - public static StatusModel failure(int statusCode, String message) { - if (statusCode == 0) { - throw new IllegalArgumentException("failure status code cannot be 0"); - } - return new StatusModel(statusCode, message); - } private int statusCode; private String message; - public StatusModel() { - } - public StatusModel(int statusCode) { this.statusCode = statusCode; } @@ -31,30 +18,11 @@ public class StatusModel extends ResultModel { return statusCode; } - public StatusModel setStatusCode(int statusCode) { - this.statusCode = statusCode; - return this; - } public String getMessage() { return message; } - public StatusModel setMessage(String message) { - this.message = message; - return this; - } - - public StatusModel setStatus(int statusCode, String message) { - this.statusCode = statusCode; - this.message = message; - return this; - } - - public StatusModel setStatus(int statusCode) { - return this.setStatus(statusCode, null); - } - @Override public String getType() { return "status"; diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/ClassLoaderView.java b/core/src/main/java/com/taobao/arthas/core/command/view/ClassLoaderView.java new file mode 100644 index 000000000..8a14a252f --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/ClassLoaderView.java @@ -0,0 +1,140 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.ClassLoaderStat; +import com.taobao.arthas.core.command.model.ClassDetailVO; +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.ClassUtils; +import com.taobao.text.Decoration; +import com.taobao.text.ui.*; +import com.taobao.text.util.RenderUtil; + +import java.util.List; +import java.util.Map; + +/** + * @author gongdewei 2020/4/21 + */ +public class ClassLoaderView extends ResultView { + + @Override + public void draw(CommandProcess process, ClassLoaderModel result) { + if (result.getClassSet() != null) { + drawAllClasses(process, result.getClassSet()); + } + if (result.getResources() != null) { + drawResources(process, result.getResources()); + } + if (result.getLoadClass() != null) { + drawLoadClass(process, result.getLoadClass()); + } + if (result.getUrls() != null) { + drawClassLoaderUrls(process, result.getUrls()); + } + if (result.getClassLoaders() != null){ + drawClassLoaders(process, result.getClassLoaders(), result.getTree()); + } + if (result.getClassLoaderStats() != null){ + drawClassLoaderStats(process, result.getClassLoaderStats()); + } + } + + private void drawClassLoaderStats(CommandProcess process, Map classLoaderStats) { + Element element = renderStat(classLoaderStats); + process.write(RenderUtil.render(element, process.width())) + .write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); + + } + + private static TableElement renderStat(Map classLoaderStats) { + TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); + table.add(new RowElement().style(Decoration.bold.bold()).add("name", "numberOfInstances", "loadedCountTotal")); + for (Map.Entry entry : classLoaderStats.entrySet()) { + table.row(entry.getKey(), "" + entry.getValue().getNumberOfInstance(), "" + entry.getValue().getLoadedCount()); + } + return table; + } + + private void drawClassLoaders(CommandProcess process, List classLoaders, Boolean isTree) { + Element element = isTree ? renderTree(classLoaders) : renderTable(classLoaders); + process.write(RenderUtil.render(element, process.width())) + .write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); + } + + private void drawClassLoaderUrls(CommandProcess process, List urls) { + process.write(RenderUtil.render(renderClassLoaderUrls(urls), process.width())); + process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); + } + + private void drawLoadClass(CommandProcess process, ClassDetailVO loadClass) { + process.write(RenderUtil.render(ClassUtils.renderClassInfo(loadClass), process.width()) + "\n"); + } + + private void drawAllClasses(CommandProcess process, ClassSetVO classSetVO) { + process.write(RenderUtil.render(renderClasses(classSetVO), process.width())); + process.write("\n"); + } + + private void drawResources(CommandProcess process, List resources) { + TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); + for (String resource : resources) { + table.row(resource); + } + process.write(RenderUtil.render(table, process.width()) + "\n"); + process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); + } + + 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()) + .style(Decoration.bold.bold())); + } + for (String className : classSetVO.getClasses()) { + table.row(new LabelElement(className)); + } + return table; + } + + private static Element renderClassLoaderUrls(List urls) { + StringBuilder sb = new StringBuilder(); + for (String url : urls) { + sb.append(url).append("\n"); + } + return new LabelElement(sb.toString()); + } + + // 统计所有的ClassLoader的信息 + private static TableElement renderTable(List classLoaderInfos) { + 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()); + } + return table; + } + + // 以树状列出ClassLoader的继承结构 + private static Element renderTree(List classLoaderInfos) { + TreeElement root = new TreeElement(); + for (ClassLoaderVO classLoader : classLoaderInfos) { + TreeElement child = new TreeElement(classLoader.getName()); + root.addChild(child); + renderSubtree(child, classLoader); + } + return root; + } + + private static void renderSubtree(TreeElement parent, ClassLoaderVO parentClassLoader) { + if (parentClassLoader.getChildren() == null){ + return; + } + for (ClassLoaderVO childClassLoader : parentClassLoader.getChildren()) { + TreeElement child = new TreeElement(childClassLoader.getName()); + parent.addChild(child); + renderSubtree(child, childClassLoader); + } + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/DumpClassView.java b/core/src/main/java/com/taobao/arthas/core/command/view/DumpClassView.java new file mode 100644 index 000000000..273728c67 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/DumpClassView.java @@ -0,0 +1,51 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.DumpClassModel; +import com.taobao.arthas.core.command.model.DumpClassVO; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.TypeRenderUtils; +import com.taobao.text.Color; +import com.taobao.text.Decoration; +import com.taobao.text.ui.Element; +import com.taobao.text.ui.LabelElement; +import com.taobao.text.ui.TableElement; +import com.taobao.text.util.RenderUtil; + +import java.util.List; + +import static com.taobao.text.ui.Element.label; + +/** + * @author gongdewei 2020/4/21 + */ +public class DumpClassView extends ResultView { + + @Override + public void draw(CommandProcess process, DumpClassModel result) { + if (result.getDumpedClasses() != null) { + drawDumpedClasses(process, result.getDumpedClasses()); + + } else if (result.getMatchedClasses() != null) { + Element table = ClassUtils.renderMatchedClasses(result.getMatchedClasses()); + process.write(RenderUtil.render(table)).write("\n"); + } + } + + private void drawDumpedClasses(CommandProcess process, List classVOs) { + TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); + table.row(new LabelElement("HASHCODE").style(Decoration.bold.bold()), + new LabelElement("CLASSLOADER").style(Decoration.bold.bold()), + new LabelElement("LOCATION").style(Decoration.bold.bold())); + + for (DumpClassVO clazz : classVOs) { + table.row(label(clazz.getClassLoaderHash()).style(Decoration.bold.fg(Color.red)), + TypeRenderUtils.drawClassLoader(clazz), + label(clazz.getLocation()).style(Decoration.bold.fg(Color.red))); + } + + process.write(RenderUtil.render(table, process.width())) + .write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/GetStaticView.java b/core/src/main/java/com/taobao/arthas/core/command/view/GetStaticView.java new file mode 100644 index 000000000..8be21d435 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/GetStaticView.java @@ -0,0 +1,29 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.GetStaticModel; +import com.taobao.arthas.core.command.model.ObjectVO; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.StringUtils; +import com.taobao.arthas.core.view.ObjectView; +import com.taobao.text.ui.Element; +import com.taobao.text.util.RenderUtil; + +/** + * @author gongdewei 2020/4/20 + */ +public class GetStaticView extends ResultView { + + @Override + public void draw(CommandProcess process, GetStaticModel result) { + int expand = result.expand(); + if (result.getField() != null) { + ObjectVO field = result.getField(); + String valueStr = StringUtils.objectToString(expand >= 0 ? new ObjectView(field.getValue(), expand).draw() : field.getValue()); + process.write("field: " + field.getName() + "\n" + valueStr + "\n"); + } else if (result.getMatchedClasses() != null) { + Element table = ClassUtils.renderMatchedClasses(result.getMatchedClasses()); + process.write(RenderUtil.render(table)).write("\n"); + } + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/JadView.java b/core/src/main/java/com/taobao/arthas/core/command/view/JadView.java new file mode 100644 index 000000000..e80041bfd --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/JadView.java @@ -0,0 +1,42 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.ClassVO; +import com.taobao.arthas.core.command.model.JadModel; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.TypeRenderUtils; +import com.taobao.text.Color; +import com.taobao.text.Decoration; +import com.taobao.text.lang.LangRenderUtil; +import com.taobao.text.ui.Element; +import com.taobao.text.ui.LabelElement; +import com.taobao.text.util.RenderUtil; + +/** + * @author gongdewei 2020/4/22 + */ +public class JadView extends ResultView { + + @Override + public void draw(CommandProcess process, JadModel result) { + + if (result.getMatchedClasses() != null) { + Element table = ClassUtils.renderMatchedClasses(result.getMatchedClasses()); + process.write(RenderUtil.render(table)).write("\n"); + } else { + ClassVO classInfo = result.getClassInfo(); + if (classInfo != null) { + process.write("\n"); + process.write(RenderUtil.render(new LabelElement("ClassLoader: ").style(Decoration.bold.fg(Color.red)), process.width())); + process.write(RenderUtil.render(TypeRenderUtils.drawClassLoader(classInfo), process.width()) + "\n"); + } + if (result.getLocation() != null) { + process.write(RenderUtil.render(new LabelElement("Location: ").style(Decoration.bold.fg(Color.red)), process.width())); + process.write(RenderUtil.render(new LabelElement(result.getLocation()).style(Decoration.bold.fg(Color.blue)), process.width()) + "\n"); + } + process.write(LangRenderUtil.render(result.getSource()) + "\n"); + process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); + } + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/MemoryCompilerView.java b/core/src/main/java/com/taobao/arthas/core/command/view/MemoryCompilerView.java new file mode 100644 index 000000000..e0f69a6c9 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/MemoryCompilerView.java @@ -0,0 +1,17 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.MemoryCompilerModel; +import com.taobao.arthas.core.shell.command.CommandProcess; + +/** + * @author gongdewei 2020/4/20 + */ +public class MemoryCompilerView extends ResultView { + @Override + public void draw(CommandProcess process, MemoryCompilerModel result) { + process.write("Memory compiler output:\n"); + for (String file : result.getFiles()) { + process.write(file + '\n'); + } + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/OgnlView.java b/core/src/main/java/com/taobao/arthas/core/command/view/OgnlView.java new file mode 100644 index 000000000..509e95197 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/OgnlView.java @@ -0,0 +1,19 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.OgnlModel; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.StringUtils; +import com.taobao.arthas.core.view.ObjectView; + +/** + * @author gongdewei 2020/4/29 + */ +public class OgnlView extends ResultView { + @Override + public void draw(CommandProcess process, OgnlModel result) { + int expand = result.getExpand(); + Object value = result.getValue(); + String resultStr = StringUtils.objectToString(expand >= 0 ? new ObjectView(value, expand).draw() : value); + process.write(resultStr).write("\n"); + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/RedefineView.java b/core/src/main/java/com/taobao/arthas/core/command/view/RedefineView.java new file mode 100644 index 000000000..e364332aa --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/RedefineView.java @@ -0,0 +1,22 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.RedefineModel; +import com.taobao.arthas.core.shell.command.CommandProcess; + +/** + * @author gongdewei 2020/4/16 + */ +public class RedefineView extends ResultView { + + @Override + public void draw(CommandProcess process, RedefineModel result) { + StringBuilder sb = new StringBuilder(); + for (String aClass : result.getRedefinedClasses()) { + sb.append(aClass).append("\n"); + } + process.write("redefine success, size: " + result.getRedefinitionCount()) + .write(", classes:\n") + .write(sb.toString()); + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java index 6071e71d4..9891883c2 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java @@ -29,6 +29,8 @@ public class ResultViewResolver { */ private void initResultViews() { try { + registerView(RowAffectView.class); + //basic1000 registerView(StatusView.class); registerView(VersionView.class); @@ -47,6 +49,15 @@ public class ResultViewResolver { registerView(ShutdownView.class); //klass100 + registerView(ClassLoaderView.class); + registerView(DumpClassView.class); + registerView(GetStaticView.class); + registerView(JadView.class); + registerView(MemoryCompilerView.class); + registerView(OgnlView.class); + registerView(RedefineView.class); + registerView(SearchClassView.class); + registerView(SearchMethodView.class); //logger diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/RowAffectView.java b/core/src/main/java/com/taobao/arthas/core/command/view/RowAffectView.java new file mode 100644 index 000000000..0e82635f7 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/RowAffectView.java @@ -0,0 +1,14 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.RowAffectModel; +import com.taobao.arthas.core.shell.command.CommandProcess; + +/** + * @author gongdewei 2020/4/8 + */ +public class RowAffectView extends ResultView { + @Override + public void draw(CommandProcess process, RowAffectModel result) { + process.write(result.affect() + "\n"); + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/SearchClassView.java b/core/src/main/java/com/taobao/arthas/core/command/view/SearchClassView.java new file mode 100644 index 000000000..fc17576f5 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/SearchClassView.java @@ -0,0 +1,26 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.SearchClassModel; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.text.util.RenderUtil; + +/** + * @author gongdewei 2020/4/8 + */ +public class SearchClassView extends ResultView { + @Override + public void draw(CommandProcess process, SearchClassModel result) { + + if (result.isDetailed()) { + process.write(RenderUtil.render(ClassUtils.renderClassInfo(result.getClassInfo(), + result.isWithField(), result.getExpand()), process.width())); + process.write("\n"); + } else if (result.getClassNames() != null) { + for (String className : result.getClassNames()) { + process.write(className).write("\n"); + } + } + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/SearchMethodView.java b/core/src/main/java/com/taobao/arthas/core/command/view/SearchMethodView.java new file mode 100644 index 000000000..d58027069 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/SearchMethodView.java @@ -0,0 +1,39 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.SearchMethodModel; +import com.taobao.arthas.core.command.model.MethodVO; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.text.util.RenderUtil; + + +/** + * render for SearchMethodCommand + * @author gongdewei 2020/4/9 + */ +public class SearchMethodView extends ResultView { + @Override + public void draw(CommandProcess process, SearchMethodModel result) { + boolean detail = result.isDetail(); + MethodVO methodInfo = result.getMethodInfo(); + + if (detail) { + if (methodInfo.isConstructor()) { + //render constructor + process.write(RenderUtil.render(ClassUtils.renderConstructor(methodInfo), process.width()) + "\n"); + } else { + //render method + process.write(RenderUtil.render(ClassUtils.renderMethod(methodInfo), process.width()) + "\n"); + } + } else { + //java.util.List indexOf(Ljava/lang/Object;)I + //className methodName+Descriptor + process.write(methodInfo.getDeclaringClass()) + .write(" ") + .write(methodInfo.getMethodName()) + .write(methodInfo.getDescriptor()) + .write("\n"); + } + + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/shell/command/ExitStatus.java b/core/src/main/java/com/taobao/arthas/core/shell/command/ExitStatus.java new file mode 100644 index 000000000..ce2b41810 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/shell/command/ExitStatus.java @@ -0,0 +1,64 @@ +package com.taobao.arthas.core.shell.command; + +/** + * 命令执行的结束状态 + */ +public class ExitStatus { + + /** + * 命令执行成功的状态 + */ + public static final ExitStatus SUCCESS_STATUS = new ExitStatus(0); + + /** + * 命令执行成功的状态 + * @return + */ + public static ExitStatus success() { + return SUCCESS_STATUS; + } + + /** + * 命令执行失败 + * @param statusCode + * @param message + * @return + */ + public static ExitStatus failure(int statusCode, String message) { + if (statusCode == 0) { + throw new IllegalArgumentException("failure status code cannot be 0"); + } + return new ExitStatus(statusCode, message); + } + + /** + * 判断是否为失败状态 + * @param exitStatus + * @return + */ + public static boolean isFailed(ExitStatus exitStatus) { + return exitStatus != null && exitStatus.getStatusCode() != 0; + } + + + private int statusCode; + private String message; + + private ExitStatus(int statusCode) { + this.statusCode = statusCode; + } + + private ExitStatus(int statusCode, String message) { + this.statusCode = statusCode; + this.message = message; + } + + public int getStatusCode() { + return statusCode; + } + + public String getMessage() { + return message; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java b/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java index 4e6e28652..b31ee56c0 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java @@ -2,12 +2,27 @@ package com.taobao.arthas.core.util; import static com.taobao.text.ui.Element.label; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.security.CodeSource; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import com.alibaba.arthas.deps.org.objectweb.asm.Type; +import com.taobao.arthas.core.command.model.ClassDetailVO; +import com.taobao.arthas.core.command.model.ClassLoaderVO; +import com.taobao.arthas.core.command.model.ClassVO; +import com.taobao.arthas.core.command.model.MethodVO; +import com.taobao.text.Color; import com.taobao.text.Decoration; import com.taobao.text.ui.Element; +import com.taobao.text.ui.LabelElement; import com.taobao.text.ui.TableElement; +import static com.taobao.text.Decoration.bold; + /** * * @author hengyunabc 2018-10-18 @@ -27,17 +42,16 @@ public class ClassUtils { return clazz.getName().contains("$$Lambda$"); } - public static Element renderClassInfo(Class clazz) { + public static Element renderClassInfo(ClassDetailVO clazz) { return renderClassInfo(clazz, false, null); } - public static Element renderClassInfo(Class clazz, boolean isPrintField, Integer expand) { + public static Element renderClassInfo(ClassDetailVO clazz, boolean isPrintField, Integer expand) { TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); - CodeSource cs = clazz.getProtectionDomain().getCodeSource(); - table.row(label("class-info").style(Decoration.bold.bold()), label(StringUtils.classname(clazz))) - .row(label("code-source").style(Decoration.bold.bold()), label(getCodeSource(cs))) - .row(label("name").style(Decoration.bold.bold()), label(StringUtils.classname(clazz))) + table.row(label("class-info").style(Decoration.bold.bold()), label(clazz.getClassInfo())) + .row(label("code-source").style(Decoration.bold.bold()), label(clazz.getCodeSource())) + .row(label("name").style(Decoration.bold.bold()), label(clazz.getName())) .row(label("isInterface").style(Decoration.bold.bold()), label("" + clazz.isInterface())) .row(label("isAnnotation").style(Decoration.bold.bold()), label("" + clazz.isAnnotation())) .row(label("isEnum").style(Decoration.bold.bold()), label("" + clazz.isEnum())) @@ -48,12 +62,12 @@ public class ClassUtils { .row(label("isPrimitive").style(Decoration.bold.bold()), label("" + clazz.isPrimitive())) .row(label("isSynthetic").style(Decoration.bold.bold()), label("" + clazz.isSynthetic())) .row(label("simple-name").style(Decoration.bold.bold()), label(clazz.getSimpleName())) - .row(label("modifier").style(Decoration.bold.bold()), label(StringUtils.modifier(clazz.getModifiers(), ','))) - .row(label("annotation").style(Decoration.bold.bold()), label(TypeRenderUtils.drawAnnotation(clazz))) - .row(label("interfaces").style(Decoration.bold.bold()), label(TypeRenderUtils.drawInterface(clazz))) + .row(label("modifier").style(Decoration.bold.bold()), label(clazz.getModifier())) + .row(label("annotation").style(Decoration.bold.bold()), label(StringUtils.join(clazz.getAnnotations(), ","))) + .row(label("interfaces").style(Decoration.bold.bold()), label(StringUtils.join(clazz.getInterfaces(), ","))) .row(label("super-class").style(Decoration.bold.bold()), TypeRenderUtils.drawSuperClass(clazz)) .row(label("class-loader").style(Decoration.bold.bold()), TypeRenderUtils.drawClassLoader(clazz)) - .row(label("classLoaderHash").style(Decoration.bold.bold()), label(StringUtils.classLoaderHash(clazz))); + .row(label("classLoaderHash").style(Decoration.bold.bold()), label(clazz.getClassLoaderHash())); if (isPrintField) { table.row(label("fields").style(Decoration.bold.bold()), TypeRenderUtils.drawField(clazz, expand)); @@ -61,4 +75,156 @@ public class ClassUtils { return table; } + public static ClassDetailVO createClassInfo(Class clazz, boolean withFields) { + CodeSource cs = clazz.getProtectionDomain().getCodeSource(); + ClassDetailVO classInfo = new ClassDetailVO(); + classInfo.setName(StringUtils.classname(clazz)); + classInfo.setClassInfo(StringUtils.classname(clazz)); + classInfo.setCodeSource(ClassUtils.getCodeSource(cs)); + classInfo.setInterface(clazz.isInterface()); + classInfo.setAnnotation(clazz.isAnnotation()); + classInfo.setEnum(clazz.isEnum()); + classInfo.setAnonymousClass(clazz.isAnonymousClass()); + classInfo.setArray(clazz.isArray()); + classInfo.setLocalClass(clazz.isLocalClass()); + classInfo.setMemberClass(clazz.isMemberClass()); + classInfo.setPrimitive(clazz.isPrimitive()); + classInfo.setSynthetic(clazz.isSynthetic()); + classInfo.setSimpleName(clazz.getSimpleName()); + classInfo.setModifier(StringUtils.modifier(clazz.getModifiers(), ',')); + classInfo.setAnnotations(TypeRenderUtils.getAnnotations(clazz)); + classInfo.setInterfaces(TypeRenderUtils.getInterfaces(clazz)); + classInfo.setSuperClass(TypeRenderUtils.getSuperClass(clazz)); + classInfo.setClassloader(TypeRenderUtils.getClassloader(clazz)); + classInfo.setClassLoaderHash(StringUtils.classLoaderHash(clazz)); + if (withFields) { + classInfo.setFields(TypeRenderUtils.getFields(clazz)); + } + return classInfo; + } + + public static ClassVO createSimpleClassInfo(Class clazz) { + ClassVO classInfo = new ClassVO(); + fillSimpleClassVO(clazz, classInfo); + return classInfo; + } + + public static void fillSimpleClassVO(Class clazz, ClassVO classInfo) { + classInfo.setName(StringUtils.classname(clazz)); + classInfo.setClassloader(TypeRenderUtils.getClassloader(clazz)); + classInfo.setClassLoaderHash(StringUtils.classLoaderHash(clazz)); + } + + public static MethodVO createMethodInfo(Method method, Class clazz, boolean detail) { + MethodVO methodVO = new MethodVO(); + methodVO.setDeclaringClass(clazz.getName()); + methodVO.setMethodName(method.getName()); + methodVO.setDescriptor(Type.getMethodDescriptor(method)); + methodVO.setConstructor(false); + if (detail) { + methodVO.setModifier(StringUtils.modifier(method.getModifiers(), ',')); + methodVO.setAnnotations(TypeRenderUtils.getAnnotations(method.getDeclaredAnnotations())); + methodVO.setParameters(getClassNameList(method.getParameterTypes())); + methodVO.setReturnType(StringUtils.classname(method.getReturnType())); + methodVO.setExceptions(getClassNameList(method.getExceptionTypes())); + methodVO.setClassLoaderHash(StringUtils.classLoaderHash(clazz)); + } + return methodVO; + } + + public static MethodVO createMethodInfo(Constructor constructor, Class clazz, boolean detail) { + MethodVO methodVO = new MethodVO(); + methodVO.setDeclaringClass(clazz.getName()); + methodVO.setDescriptor(Type.getConstructorDescriptor(constructor)); + methodVO.setMethodName(""); + methodVO.setConstructor(true); + if (detail) { + methodVO.setModifier(StringUtils.modifier(constructor.getModifiers(), ',')); + methodVO.setAnnotations(TypeRenderUtils.getAnnotations(constructor.getDeclaredAnnotations())); + methodVO.setParameters(getClassNameList(constructor.getParameterTypes())); + methodVO.setExceptions(getClassNameList(constructor.getExceptionTypes())); + methodVO.setClassLoaderHash(StringUtils.classLoaderHash(clazz)); + } + return methodVO; + } + + public static Element renderMethod(MethodVO method) { + TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); + table.row(label("declaring-class").style(bold.bold()), label(method.getDeclaringClass())) + .row(label("method-name").style(bold.bold()), label(method.getMethodName()).style(bold.bold())) + .row(label("modifier").style(bold.bold()), label(method.getModifier())) + .row(label("annotation").style(bold.bold()), label(TypeRenderUtils.drawAnnotation(method.getAnnotations()))) + .row(label("parameters").style(bold.bold()), label(TypeRenderUtils.drawParameters(method.getParameters()))) + .row(label("return").style(bold.bold()), label(method.getReturnType())) + .row(label("exceptions").style(bold.bold()), label(TypeRenderUtils.drawExceptions(method.getExceptions()))) + .row(label("classLoaderHash").style(bold.bold()), label(method.getClassLoaderHash())); + return table; + } + + public static Element renderConstructor(MethodVO constructor) { + TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); + table.row(label("declaring-class").style(bold.bold()), label(constructor.getDeclaringClass())) + .row(label("constructor-name").style(bold.bold()), label("").style(bold.bold())) + .row(label("modifier").style(bold.bold()), label(constructor.getModifier())) + .row(label("annotation").style(bold.bold()), label(TypeRenderUtils.drawAnnotation(constructor.getAnnotations()))) + .row(label("parameters").style(bold.bold()), label(TypeRenderUtils.drawParameters(constructor.getParameters()))) + .row(label("exceptions").style(bold.bold()), label(TypeRenderUtils.drawExceptions(constructor.getExceptions()))) + .row(label("classLoaderHash").style(bold.bold()), label(constructor.getClassLoaderHash())); + return table; + } + + public static String[] getClassNameList(Class[] classes) { + List list = new ArrayList(); + for (Class anInterface : classes) { + list.add(StringUtils.classname(anInterface)); + } + return list.toArray(new String[0]); + } + + public static List createClassVOList(Set> matchedClasses) { + List classVOs = new ArrayList(matchedClasses.size()); + for (Class aClass : matchedClasses) { + ClassVO classVO = createSimpleClassInfo(aClass); + classVOs.add(classVO); + } + return classVOs; + } + + public static ClassLoaderVO createClassLoaderVO(ClassLoader classLoader) { + ClassLoaderVO classLoaderVO = new ClassLoaderVO(); + classLoaderVO.setHash(classLoaderHash(classLoader)); + classLoaderVO.setName(classLoader==null?"BootstrapClassLoader":classLoader.toString()); + ClassLoader parent = classLoader == null ? null : classLoader.getParent(); + classLoaderVO.setParent(parent==null?null:parent.toString()); + return classLoaderVO; + } + + public static String classLoaderHash(Class clazz) { + if (clazz == null || clazz.getClassLoader() == null) { + return "null"; + } + return Integer.toHexString(clazz.getClassLoader().hashCode()); + } + + public static String classLoaderHash(ClassLoader classLoader) { + if (classLoader == null ) { + return "null"; + } + return Integer.toHexString(classLoader.hashCode()); + } + + public static Element renderMatchedClasses(Collection matchedClasses) { + TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); + table.row(new LabelElement("NAME").style(Decoration.bold.bold()), + new LabelElement("HASHCODE").style(Decoration.bold.bold()), + new LabelElement("CLASSLOADER").style(Decoration.bold.bold())); + + for (ClassVO c : matchedClasses) { + table.row(label(c.getName()), + label(c.getClassLoaderHash()).style(Decoration.bold.fg(Color.red)), + TypeRenderUtils.drawClassLoader(c)); + } + return table; + } + } diff --git a/core/src/main/java/com/taobao/arthas/core/util/CommandUtils.java b/core/src/main/java/com/taobao/arthas/core/util/CommandUtils.java new file mode 100644 index 000000000..21c0c936d --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/util/CommandUtils.java @@ -0,0 +1,24 @@ +package com.taobao.arthas.core.util; + +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.shell.command.ExitStatus; + +/** + * Command Process util + */ +public class CommandUtils { + + /** + * check exit status and end command processing + * @param process CommandProcess instance + * @param status ExitStatus of command + */ + public static void end(CommandProcess process, ExitStatus status) { + if (status != null) { + process.end(status.getStatusCode(), status.getMessage()); + } else { + process.end(-1, "process error, exit status is null"); + } + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/util/ResultUtils.java b/core/src/main/java/com/taobao/arthas/core/util/ResultUtils.java new file mode 100644 index 000000000..f77099363 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/util/ResultUtils.java @@ -0,0 +1,50 @@ +package com.taobao.arthas.core.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * 命令结果处理工具类 + * @author gongdewei 2020/5/18 + */ +public class ResultUtils { + + /** + * 分页处理class列表,转换为className列表 + * @param classes + * @param pageSize + * @param handler + */ + public static void processClassNames(Collection> classes, int pageSize, PaginationHandler> handler) { + List classNames = new ArrayList(pageSize); + int segment = 0; + for (Class aClass : classes) { + classNames.add(aClass.getName()); + //slice segment + if(classNames.size() >= pageSize) { + handler.handle(classNames, segment++); + classNames = new ArrayList(pageSize); + } + } + //last segment + if (classNames.size() > 0) { + handler.handle(classNames, segment++); + } + } + + /** + * 分页数据处理回调接口 + * @param + */ + public interface PaginationHandler { + + /** + * 处理分页数据 + * @param list + * @param segment + * @return true 继续处理剩余数据, false 终止处理 + */ + boolean handle(T list, int segment); + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java index 320f67eed..54c7974c2 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java @@ -71,7 +71,7 @@ public abstract class StringUtils { return StringUtils.replace(className, "/", "."); } - public static String concat(String seperator, Class... types) { + public static String concat(String separator, Class... types) { if (types == null || types.length == 0) { return Constants.EMPTY_STRING; } @@ -80,7 +80,23 @@ public abstract class StringUtils { for (int i = 0; i < types.length; i++) { builder.append(classname(types[i])); if (i < types.length - 1) { - builder.append(seperator); + builder.append(separator); + } + } + + return builder.toString(); + } + + public static String concat(String separator, String... strs) { + if (strs == null || strs.length == 0) { + return Constants.EMPTY_STRING; + } + + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < strs.length; i++) { + builder.append(strs[i]); + if (i < strs.length - 1) { + builder.append(separator); } } diff --git a/core/src/main/java/com/taobao/arthas/core/util/TypeRenderUtils.java b/core/src/main/java/com/taobao/arthas/core/util/TypeRenderUtils.java index d6e2202ae..c6493dea0 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/TypeRenderUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/TypeRenderUtils.java @@ -1,5 +1,8 @@ package com.taobao.arthas.core.util; +import com.taobao.arthas.core.command.model.ClassDetailVO; +import com.taobao.arthas.core.command.model.ClassVO; +import com.taobao.arthas.core.command.model.FieldVO; import com.taobao.arthas.core.view.ObjectView; import com.taobao.text.ui.Element; import com.taobao.text.ui.TableElement; @@ -10,8 +13,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Collections; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import static com.taobao.text.ui.Element.label; @@ -21,14 +23,6 @@ import static com.taobao.text.ui.Element.label; */ public class TypeRenderUtils { - public static String drawAnnotation(Class clazz) { - return drawAnnotation(clazz.getDeclaredAnnotations()); - } - - public static String drawAnnotation(Method method) { - return drawAnnotation(method.getDeclaredAnnotations()); - } - public static String drawInterface(Class clazz) { return StringUtils.concat(",", clazz.getInterfaces()); } @@ -41,6 +35,10 @@ public class TypeRenderUtils { return StringUtils.concat("\n", constructor.getParameterTypes()); } + public static String drawParameters(String[] parameterTypes) { + return StringUtils.concat("\n", parameterTypes); + } + public static String drawReturn(Method method) { return StringUtils.classname(method.getReturnType()); } @@ -53,70 +51,51 @@ public class TypeRenderUtils { return StringUtils.concat("\n", constructor.getExceptionTypes()); } - public static Element drawSuperClass(Class clazz) { + public static String drawExceptions(String[] exceptionTypes) { + return StringUtils.concat("\n", exceptionTypes); + } + + public static Element drawSuperClass(ClassDetailVO clazz) { + return drawTree(clazz.getSuperClass()); + } + + public static Element drawClassLoader(ClassVO clazz) { + String[] classloaders = clazz.getClassloader(); + return drawTree(classloaders); + } + + public static Element drawTree(String[] nodes) { TreeElement root = new TreeElement(); TreeElement parent = root; - - Class superClass = clazz.getSuperclass(); - if (null != superClass) { - TreeElement child = new TreeElement(label(StringUtils.classname(superClass))); + for (String node : nodes) { + TreeElement child = new TreeElement(label(node)); parent.addChild(child); parent = child; - - while (true) { - superClass = superClass.getSuperclass(); - if (null == superClass) { - break; - } - TreeElement tempChild = new TreeElement(label(StringUtils.classname(superClass))); - parent.addChild(tempChild); - parent = tempChild; - } } return root; } - public static Element drawClassLoader(Class clazz) { - TreeElement root = new TreeElement(); - TreeElement parent = root; - ClassLoader loader = clazz.getClassLoader(); - if (null != loader) { - TreeElement child = new TreeElement(label(loader.toString())); - parent.addChild(child); - parent = child; - while (true) { - loader = loader.getParent(); - if (null == loader) { - break; - } - TreeElement tempChild = new TreeElement(label(loader.toString())); - parent.addChild(tempChild); - parent = tempChild; - } - } - return root; - } - - public static Element drawField(Class clazz, Integer expand) { + public static Element drawField(ClassDetailVO clazz, Integer expand) { TableElement fieldsTable = new TableElement(1).leftCellPadding(0).rightCellPadding(0); - Field[] fields = clazz.getDeclaredFields(); + FieldVO[] fields = clazz.getFields(); if (fields == null || fields.length == 0) { return fieldsTable; } - for (Field field : fields) { + for (FieldVO field : fields) { TableElement fieldTable = new TableElement().leftCellPadding(0).rightCellPadding(1); fieldTable.row("name", field.getName()) - .row("type", StringUtils.classname(field.getType())) - .row("modifier", StringUtils.modifier(field.getModifiers(), ',')); + .row("type", field.getType()) + .row("modifier", field.getModifier()); - Annotation[] annotations = field.getAnnotations(); + String[] annotations = field.getAnnotations(); if (annotations != null && annotations.length > 0) { fieldTable.row("annotation", drawAnnotation(annotations)); } - if (Modifier.isStatic(field.getModifiers())) { - fieldTable.row("value", drawFieldValue(field, expand)); + if (field.isStatic()) { + Object o = (expand != null && expand >= 0) ? new ObjectView(field.getValue(), expand).draw() : field.getValue(); + fieldTable.row("value", StringUtils.objectToString(o)); } fieldTable.row(label("")); @@ -126,40 +105,97 @@ public class TypeRenderUtils { return fieldsTable; } - public static String renderMethodSignature(Method method) { - StringBuilder sb = new StringBuilder(); - sb.append(StringUtils.modifier(method.getModifiers(), ' ')).append(" "); - sb.append(TypeRenderUtils.drawReturn(method)).append(" "); - sb.append(method.getName()).append(" "); - sb.append("("); - sb.append(StringUtils.concat(", ", method.getParameterTypes())); - sb.append(")"); - return sb.toString(); + public static String drawAnnotation(String... annotations) { + return StringUtils.concat(",", annotations); } - private static String drawFieldValue(Field field, Integer expand) { + public static String[] getAnnotations(Class clazz) { + return getAnnotations(clazz.getDeclaredAnnotations()); + } + + public static String[] getAnnotations(Annotation[] annotations) { + List list = new ArrayList(); + if (annotations != null && annotations.length > 0) { + for (Annotation annotation : annotations) { + list.add(StringUtils.classname(annotation.annotationType())); + } + } + return list.toArray(new String[0]); + } + + public static String[] getInterfaces(Class clazz) { + Class[] interfaces = clazz.getInterfaces(); + return ClassUtils.getClassNameList(interfaces); + } + + public static String[] getSuperClass(Class clazz) { + List list = new ArrayList(); + Class superClass = clazz.getSuperclass(); + if (null != superClass) { + list.add(StringUtils.classname(superClass)); + while (true) { + superClass = superClass.getSuperclass(); + if (null == superClass) { + break; + } + list.add(StringUtils.classname(superClass)); + } + } + return list.toArray(new String[0]); + } + + public static String[] getClassloader(Class clazz) { + List list = new ArrayList(); + ClassLoader loader = clazz.getClassLoader(); + if (null != loader) { + list.add(loader.toString()); + while (true) { + loader = loader.getParent(); + if (null == loader) { + break; + } + list.add(loader.toString()); + } + } + return list.toArray(new String[0]); + } + + public static FieldVO[] getFields(Class clazz) { + Field[] fields = clazz.getDeclaredFields(); + if (fields == null || fields.length == 0) { + return new FieldVO[0]; + } + + List list = new ArrayList(fields.length); + for (Field field : fields) { + FieldVO fieldVO = new FieldVO(); + fieldVO.setName(field.getName()); + fieldVO.setType(StringUtils.classname(field.getType())); + fieldVO.setModifier(StringUtils.modifier(field.getModifiers(), ',')); + fieldVO.setAnnotations(getAnnotations(field.getAnnotations())); + if (Modifier.isStatic(field.getModifiers())) { + fieldVO.setStatic(true); + fieldVO.setValue(getFieldValue(field)); + } else { + fieldVO.setStatic(false); + } + list.add(fieldVO); + } + return list.toArray(new FieldVO[0]); + } + + private static Object getFieldValue(Field field) { final boolean isAccessible = field.isAccessible(); try { field.setAccessible(true); Object value = field.get(null); - Object o = (expand != null && expand >= 0) ? new ObjectView(value, expand).draw() : value; - return StringUtils.objectToString(o); + return value; } catch (IllegalAccessException e) { // no op } finally { field.setAccessible(isAccessible); } - return Constants.EMPTY_STRING; + return null; } - public static String drawAnnotation(Annotation... annotations) { - List> types = Collections.emptyList(); - if (annotations != null && annotations.length > 0) { - types = new LinkedList>(); - for (Annotation annotation : annotations) { - types.add(annotation.annotationType()); - } - } - return StringUtils.concat(",", types.toArray(new Class[0])); - } }