mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
Transform commands of pkg klass100 (#1285)
This commit is contained in:
@@ -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<Field> 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<Field> fields = findOptionFields(new EqualsMatcher<String>(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<Field> fields = findOptionFields(new EqualsMatcher<String>(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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+143
-117
@@ -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<String, ClassLoaderStat> sorted =
|
||||
new TreeMap<String, ClassLoaderStat>(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<ClassLoaderInfo> 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<ClassLoaderVO> classLoaderVOs = new ArrayList<ClassLoaderVO>(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<ClassLoader> 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<String> 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<ClassLoader> allClassLoader = getAllClassLoaders(inst);
|
||||
List<String> resources = new ArrayList<String>();
|
||||
for (ClassLoader cl : allClassLoader) {
|
||||
if (Integer.toHexString(cl.hashCode()).equals(hashCode)) {
|
||||
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
|
||||
try {
|
||||
Enumeration<URL> 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 {
|
||||
* <p>
|
||||
* 当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<Class> bootstrapClassSet = new TreeSet<Class>(new Comparator<Class>() {
|
||||
SortedSet<Class<?>> bootstrapClassSet = new TreeSet<Class<?>>(new Comparator<Class>() {
|
||||
@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<ClassLoader, SortedSet<Class>> classLoaderClassMap = new HashMap<ClassLoader, SortedSet<Class>>();
|
||||
Map<ClassLoader, SortedSet<Class<?>>> classLoaderClassMap = new HashMap<ClassLoader, SortedSet<Class<?>>>();
|
||||
for (Class clazz : allLoadedClasses) {
|
||||
ClassLoader classLoader = clazz.getClassLoader();
|
||||
// Class loaded by BootstrapClassLoader
|
||||
@@ -272,11 +300,11 @@ public class ClassLoaderCommand extends AnnotatedCommand {
|
||||
continue;
|
||||
}
|
||||
|
||||
SortedSet<Class> classSet = classLoaderClassMap.get(classLoader);
|
||||
SortedSet<Class<?>> classSet = classLoaderClassMap.get(classLoader);
|
||||
if (classSet == null) {
|
||||
classSet = new TreeSet<Class>(new Comparator<Class>() {
|
||||
classSet = new TreeSet<Class<?>>(new Comparator<Class<?>>() {
|
||||
@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<ClassLoader, SortedSet<Class<?>>> entry : classLoaderClassMap.entrySet()) {
|
||||
if (checkInterrupted(process)) {
|
||||
return;
|
||||
}
|
||||
table.row(new LabelElement(" "));
|
||||
}
|
||||
|
||||
for (Entry<ClassLoader, SortedSet<Class>> entry : classLoaderClassMap.entrySet()) {
|
||||
ClassLoader classLoader = entry.getKey();
|
||||
SortedSet<Class> 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<Class<?>> 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<Class<?>> classes, int pageSize, final RowAffect affect) {
|
||||
//分批输出classNames, Ctrl+C可以中断执行
|
||||
ResultUtils.processClassNames(classes, pageSize, new ResultUtils.PaginationHandler<List<String>>() {
|
||||
@Override
|
||||
public boolean handle(List<String> 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<String> getClassLoaderUrls(ClassLoader classLoader) {
|
||||
List<String> urlStrs = new ArrayList<String>();
|
||||
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<ClassLoaderInfo> classLoaderInfos) {
|
||||
TreeElement root = new TreeElement();
|
||||
|
||||
List<ClassLoaderInfo> parentNullClassLoaders = new ArrayList<ClassLoaderInfo>();
|
||||
List<ClassLoaderInfo> parentNotNullClassLoaders = new ArrayList<ClassLoaderInfo>();
|
||||
for (ClassLoaderInfo info : classLoaderInfos) {
|
||||
if (info.parent() == null) {
|
||||
parentNullClassLoaders.add(info);
|
||||
private static List<ClassLoaderVO> processClassLoaderTree(List<ClassLoaderVO> classLoaders) {
|
||||
List<ClassLoaderVO> rootClassLoaders = new ArrayList<ClassLoaderVO>();
|
||||
List<ClassLoaderVO> parentNotNullClassLoaders = new ArrayList<ClassLoaderVO>();
|
||||
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<ClassLoaderInfo> 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<String, ClassLoaderStat> classLoaderStats) {
|
||||
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
|
||||
table.add(new RowElement().style(Decoration.bold.bold()).add("name", "numberOfInstances", "loadedCountTotal"));
|
||||
for (Map.Entry<String, ClassLoaderStat> entry : classLoaderStats.entrySet()) {
|
||||
table.row(entry.getKey(), "" + entry.getValue().getNumberOfInstance(), "" + entry.getValue().getLoadedCount());
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
private static void renderParent(TreeElement node, ClassLoaderInfo parent, List<ClassLoaderInfo> 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<ClassLoaderVO> 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<String> 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<Void> {
|
||||
|
||||
private ClassLoaderCommand command;
|
||||
|
||||
public ClassLoaderInterruptHandler(ClassLoaderCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Void event) {
|
||||
command.isInterrupted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Class<?>> 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<Class<?>> matchedClasses) {
|
||||
private ExitStatus processMatch(CommandProcess process, RowAffect effect, Instrumentation inst, Set<Class<?>> matchedClasses) {
|
||||
try {
|
||||
Map<Class<?>, 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<DumpClassVO> dumpedClasses = new ArrayList<DumpClassVO>(classFiles.size());
|
||||
for (Map.Entry<Class<?>, 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<Class<?>> 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<Class<?>> 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<ClassVO> 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<Class<?>, File> dump(Instrumentation inst, Set<Class<?>> classes) throws UnmodifiableClassException {
|
||||
|
||||
@@ -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<Class<?>> 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<Class<?>> matchedClasses) {
|
||||
Matcher<String> 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<Class<?>> matchedClasses) {
|
||||
Element usage = new LabelElement("getstatic -c <hashcode> " + 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<Class<?>> 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 <hashcode> " + 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 <hashcode> " + 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<ClassVO> 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<String> fieldNameMatcher() {
|
||||
|
||||
@@ -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<Class<?>> 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<Class<?>> 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<Class<?>> matchedClasses, Set<Class<?>> withInnerClasses) {
|
||||
private ExitStatus processExactMatch(CommandProcess process, RowAffect affect, Instrumentation inst, Set<Class<?>> matchedClasses, Set<Class<?>> withInnerClasses) {
|
||||
Class<?> c = matchedClasses.iterator().next();
|
||||
Set<Class<?>> allClasses = new HashSet<Class<?>>(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<Class<?>> matchedClasses) {
|
||||
Element usage = new LabelElement("jad -c <hashcode> " + 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<Class<?>> 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 <hashcode> " + 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<ClassVO> 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
|
||||
|
||||
+11
-12
@@ -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<String> files = new ArrayList<String>();
|
||||
for (Entry<String, byte[]> 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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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. ");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+32
-13
@@ -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<List<String>>() {
|
||||
@Override
|
||||
public boolean handle(List<String> 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)) {
|
||||
|
||||
+23
-52
@@ -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<String> methodNameMatcher = methodNameMatcher();
|
||||
Set<Class<?>> 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("<init>")) {
|
||||
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("<init>").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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> resources;
|
||||
private ClassDetailVO loadClass;
|
||||
private List<String> urls;
|
||||
//classloader -l -t
|
||||
private List<ClassLoaderVO> classLoaders;
|
||||
private Boolean tree;
|
||||
|
||||
private Map<String, ClassLoaderStat> 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<String> getResources() {
|
||||
return resources;
|
||||
}
|
||||
|
||||
public ClassLoaderModel setResources(List<String> resources) {
|
||||
this.resources = resources;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClassDetailVO getLoadClass() {
|
||||
return loadClass;
|
||||
}
|
||||
|
||||
public ClassLoaderModel setLoadClass(ClassDetailVO loadClass) {
|
||||
this.loadClass = loadClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getUrls() {
|
||||
return urls;
|
||||
}
|
||||
|
||||
public ClassLoaderModel setUrls(List<String> urls) {
|
||||
this.urls = urls;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<ClassLoaderVO> getClassLoaders() {
|
||||
return classLoaders;
|
||||
}
|
||||
|
||||
public ClassLoaderModel setClassLoaders(List<ClassLoaderVO> classLoaders) {
|
||||
this.classLoaders = classLoaders;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean getTree() {
|
||||
return tree;
|
||||
}
|
||||
|
||||
public ClassLoaderModel setTree(Boolean tree) {
|
||||
this.tree = tree;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, ClassLoaderStat> getClassLoaderStats() {
|
||||
return classLoaderStats;
|
||||
}
|
||||
|
||||
public ClassLoaderModel setClassLoaderStats(Map<String, ClassLoaderStat> classLoaderStats) {
|
||||
this.classLoaderStats = classLoaderStats;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -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<ClassLoaderVO> children;
|
||||
|
||||
public ClassLoaderVO() {
|
||||
}
|
||||
|
||||
public void addChild(ClassLoaderVO child){
|
||||
if (this.children == null){
|
||||
this.children = new ArrayList<ClassLoaderVO>();
|
||||
}
|
||||
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<ClassLoaderVO> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<ClassLoaderVO> children) {
|
||||
this.children = children;
|
||||
}
|
||||
}
|
||||
@@ -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<String> classes;
|
||||
private int segment;
|
||||
|
||||
public ClassSetVO(ClassLoaderVO classloader, Collection<String> classes) {
|
||||
this(classloader, classes, 0);
|
||||
}
|
||||
|
||||
public ClassSetVO(ClassLoaderVO classloader, Collection<String> classes, int segment) {
|
||||
this.classloader = classloader;
|
||||
this.classes = classes;
|
||||
this.segment = segment;
|
||||
}
|
||||
|
||||
public ClassLoaderVO getClassloader() {
|
||||
return classloader;
|
||||
}
|
||||
|
||||
public void setClassloader(ClassLoaderVO classloader) {
|
||||
this.classloader = classloader;
|
||||
}
|
||||
|
||||
public Collection<String> getClasses() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
public void setClasses(Collection<String> 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<DumpClassVO> dumpedClasses;
|
||||
|
||||
private Collection<ClassVO> matchedClasses;
|
||||
|
||||
public DumpClassModel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "dump";
|
||||
}
|
||||
|
||||
public List<DumpClassVO> getDumpedClasses() {
|
||||
return dumpedClasses;
|
||||
}
|
||||
|
||||
public DumpClassModel setDumpedClasses(List<DumpClassVO> dumpedClasses) {
|
||||
this.dumpedClasses = dumpedClasses;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Collection<ClassVO> getMatchedClasses() {
|
||||
return matchedClasses;
|
||||
}
|
||||
|
||||
public DumpClassModel setMatchedClasses(Collection<ClassVO> matchedClasses) {
|
||||
this.matchedClasses = matchedClasses;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<ClassVO> 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<ClassVO> matchedClasses) {
|
||||
this.matchedClasses = matchedClasses;
|
||||
}
|
||||
|
||||
public ObjectVO getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public void setField(ObjectVO field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public Collection<ClassVO> getMatchedClasses() {
|
||||
return matchedClasses;
|
||||
}
|
||||
|
||||
public void setMatchedClasses(Collection<ClassVO> matchedClasses) {
|
||||
this.matchedClasses = matchedClasses;
|
||||
}
|
||||
|
||||
public int expand() {
|
||||
return expand;
|
||||
}
|
||||
|
||||
public void setExpand(int expand) {
|
||||
this.expand = expand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "getstatic";
|
||||
}
|
||||
}
|
||||
@@ -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<ClassVO> 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<ClassVO> getMatchedClasses() {
|
||||
return matchedClasses;
|
||||
}
|
||||
|
||||
public void setMatchedClasses(Collection<ClassVO> matchedClasses) {
|
||||
this.matchedClasses = matchedClasses;
|
||||
}
|
||||
}
|
||||
@@ -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<String> files;
|
||||
|
||||
public MemoryCompilerModel() {
|
||||
}
|
||||
|
||||
public MemoryCompilerModel(List<String> files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
public void setFiles(List<String> files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
public List<String> getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "mc";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<String> redefinedClasses;
|
||||
|
||||
public RedefineModel() {
|
||||
redefinedClasses = new ArrayList<String>();
|
||||
}
|
||||
|
||||
public void addRedefineClass(String className) {
|
||||
redefinedClasses.add(className);
|
||||
redefinitionCount++;
|
||||
}
|
||||
|
||||
public int getRedefinitionCount() {
|
||||
return redefinitionCount;
|
||||
}
|
||||
|
||||
public void setRedefinitionCount(int redefinitionCount) {
|
||||
this.redefinitionCount = redefinitionCount;
|
||||
}
|
||||
|
||||
public List<String> getRedefinedClasses() {
|
||||
return redefinedClasses;
|
||||
}
|
||||
|
||||
public void setRedefinedClasses(List<String> redefinedClasses) {
|
||||
this.redefinedClasses = redefinedClasses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "redefine";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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<String> 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<String> getClassNames() {
|
||||
return classNames;
|
||||
}
|
||||
|
||||
public void setClassNames(List<String> 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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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<ClassLoaderModel> {
|
||||
|
||||
@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<String, ClassLoaderStat> 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<String, ClassLoaderStat> classLoaderStats) {
|
||||
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
|
||||
table.add(new RowElement().style(Decoration.bold.bold()).add("name", "numberOfInstances", "loadedCountTotal"));
|
||||
for (Map.Entry<String, ClassLoaderStat> entry : classLoaderStats.entrySet()) {
|
||||
table.row(entry.getKey(), "" + entry.getValue().getNumberOfInstance(), "" + entry.getValue().getLoadedCount());
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
private void drawClassLoaders(CommandProcess process, List<ClassLoaderVO> 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<String> 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<String> 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<String> 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<ClassLoaderVO> 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<ClassLoaderVO> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<DumpClassModel> {
|
||||
|
||||
@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<DumpClassVO> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<GetStaticModel> {
|
||||
|
||||
@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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<JadModel> {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<MemoryCompilerModel> {
|
||||
@Override
|
||||
public void draw(CommandProcess process, MemoryCompilerModel result) {
|
||||
process.write("Memory compiler output:\n");
|
||||
for (String file : result.getFiles()) {
|
||||
process.write(file + '\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<OgnlModel> {
|
||||
@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");
|
||||
}
|
||||
}
|
||||
@@ -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<RedefineModel> {
|
||||
|
||||
@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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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<RowAffectModel> {
|
||||
@Override
|
||||
public void draw(CommandProcess process, RowAffectModel result) {
|
||||
process.write(result.affect() + "\n");
|
||||
}
|
||||
}
|
||||
@@ -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<SearchClassModel> {
|
||||
@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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<SearchMethodModel> {
|
||||
@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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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("<init>");
|
||||
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("<init>").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<String> list = new ArrayList<String>();
|
||||
for (Class anInterface : classes) {
|
||||
list.add(StringUtils.classname(anInterface));
|
||||
}
|
||||
return list.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public static List<ClassVO> createClassVOList(Set<Class<?>> matchedClasses) {
|
||||
List<ClassVO> classVOs = new ArrayList<ClassVO>(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<ClassVO> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Class<?>> classes, int pageSize, PaginationHandler<List<String>> handler) {
|
||||
List<String> classNames = new ArrayList<String>(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<String>(pageSize);
|
||||
}
|
||||
}
|
||||
//last segment
|
||||
if (classNames.size() > 0) {
|
||||
handler.handle(classNames, segment++);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页数据处理回调接口
|
||||
* @param <T>
|
||||
*/
|
||||
public interface PaginationHandler<T> {
|
||||
|
||||
/**
|
||||
* 处理分页数据
|
||||
* @param list
|
||||
* @param segment
|
||||
* @return true 继续处理剩余数据, false 终止处理
|
||||
*/
|
||||
boolean handle(T list, int segment);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String> list = new ArrayList<String>();
|
||||
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<String> list = new ArrayList<String>();
|
||||
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<String> list = new ArrayList<String>();
|
||||
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<FieldVO> list = new ArrayList<FieldVO>(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<Class<?>> types = Collections.emptyList();
|
||||
if (annotations != null && annotations.length > 0) {
|
||||
types = new LinkedList<Class<?>>();
|
||||
for (Annotation annotation : annotations) {
|
||||
types.add(annotation.annotationType());
|
||||
}
|
||||
}
|
||||
return StringUtils.concat(",", types.toArray(new Class<?>[0]));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user