Add --classLoaderClass for dump/getstatic/jad/mc/redifine (#1447)

This commit is contained in:
Hollow Man
2020-08-26 15:27:26 +08:00
committed by GitHub
parent ed70f644a3
commit e3283f0439
44 changed files with 589 additions and 42 deletions
@@ -4,6 +4,7 @@ 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.ClassLoaderVO;
import com.taobao.arthas.core.command.model.DumpClassModel;
import com.taobao.arthas.core.command.model.DumpClassVO;
import com.taobao.arthas.core.command.model.MessageModel;
@@ -14,6 +15,7 @@ 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.ClassLoaderUtils;
import com.taobao.arthas.core.util.CommandUtils;
import com.taobao.arthas.core.util.InstrumentationUtils;
import com.taobao.arthas.core.util.SearchUtils;
@@ -32,6 +34,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
/**
@@ -51,6 +54,7 @@ public class DumpClassCommand extends AnnotatedCommand {
private String classPattern;
private String code = null;
private String classLoaderClass;
private boolean isRegEx = false;
private String directory;
@@ -69,6 +73,12 @@ public class DumpClassCommand extends AnnotatedCommand {
this.code = code;
}
@Option(longName = "classLoaderClass")
@Description("The class name of the special class's classLoader.")
public void setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
}
@Option(shortName = "E", longName = "regex", flag = true)
@Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) {
@@ -101,7 +111,26 @@ public class DumpClassCommand extends AnnotatedCommand {
}
ExitStatus status = null;
Instrumentation inst = process.session().getInstrumentation();
if (code == null && classLoaderClass != null) {
List<ClassLoader> matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass);
if (matchedClassLoaders.size() == 1) {
code = Integer.toHexString(matchedClassLoaders.get(0).hashCode());
} else if (matchedClassLoaders.size() > 1) {
Collection<ClassLoaderVO> classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders);
DumpClassModel dumpClassModel = new DumpClassModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(dumpClassModel);
process.end(-1, "Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'");
return;
} else {
process.end(-1, "Can not find classloader by class name: " + classLoaderClass + ".");
return;
}
}
Set<Class<?>> matchedClasses = SearchUtils.searchClass(inst, classPattern, isRegEx, code);
if (matchedClasses == null || matchedClasses.isEmpty()) {
status = processNoMatch(process);
@@ -6,6 +6,7 @@ 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.ClassLoaderVO;
import com.taobao.arthas.core.command.model.GetStaticModel;
import com.taobao.arthas.core.command.model.MessageModel;
import com.taobao.arthas.core.command.model.RowAffectModel;
@@ -13,6 +14,7 @@ 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.ClassLoaderUtils;
import com.taobao.arthas.core.util.CommandUtils;
import com.taobao.arthas.core.util.SearchUtils;
import com.taobao.arthas.core.util.StringUtils;
@@ -31,6 +33,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Set;
import java.util.Collection;
/**
* @author diecui1202 on 2017/9/27.
@@ -50,6 +53,7 @@ public class GetStaticCommand extends AnnotatedCommand {
private String fieldPattern;
private String express;
private String hashCode = null;
private String classLoaderClass;
private boolean isRegEx = false;
private int expand = 1;
@@ -77,6 +81,12 @@ public class GetStaticCommand extends AnnotatedCommand {
this.hashCode = hashCode;
}
@Option(longName = "classLoaderClass")
@Description("The class name of the special class's classLoader.")
public void setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
}
@Option(shortName = "E", longName = "regex", flag = true)
@Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) {
@@ -93,6 +103,25 @@ public class GetStaticCommand extends AnnotatedCommand {
public void process(CommandProcess process) {
RowAffect affect = new RowAffect();
Instrumentation inst = process.session().getInstrumentation();
if (hashCode == null && classLoaderClass != null) {
List<ClassLoader> matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass);
if (matchedClassLoaders.size() == 1) {
hashCode = Integer.toHexString(matchedClassLoaders.get(0).hashCode());
} else if (matchedClassLoaders.size() > 1) {
Collection<ClassLoaderVO> classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders);
GetStaticModel getStaticModel = new GetStaticModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(getStaticModel);
process.end(-1, "Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'");
return;
} else {
process.end(-1, "Can not find classloader by class name: " + classLoaderClass + ".");
return;
}
}
Set<Class<?>> matchedClasses = SearchUtils.searchClassOnly(inst, classPattern, isRegEx, hashCode);
try {
if (matchedClasses == null || matchedClasses.isEmpty()) {
@@ -4,6 +4,7 @@ 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.ClassLoaderVO;
import com.taobao.arthas.core.command.model.JadModel;
import com.taobao.arthas.core.command.model.MessageModel;
import com.taobao.arthas.core.command.model.RowAffectModel;
@@ -13,6 +14,7 @@ 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.ClassLoaderUtils;
import com.taobao.arthas.core.util.CommandUtils;
import com.taobao.arthas.core.util.Decompiler;
import com.taobao.arthas.core.util.InstrumentationUtils;
@@ -30,6 +32,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.regex.Pattern;
/**
@@ -52,6 +55,7 @@ public class JadCommand extends AnnotatedCommand {
private String classPattern;
private String methodName;
private String code = null;
private String classLoaderClass;
private boolean isRegEx = false;
private boolean hideUnicode = false;
@@ -79,6 +83,12 @@ public class JadCommand extends AnnotatedCommand {
this.code = code;
}
@Option(longName = "classLoaderClass")
@Description("The class name of the special class's classLoader.")
public void setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
}
@Option(shortName = "E", longName = "regex", flag = true)
@Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) {
@@ -101,6 +111,25 @@ public class JadCommand extends AnnotatedCommand {
public void process(CommandProcess process) {
RowAffect affect = new RowAffect();
Instrumentation inst = process.session().getInstrumentation();
if (code == null && classLoaderClass != null) {
List<ClassLoader> matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass);
if (matchedClassLoaders.size() == 1) {
code = Integer.toHexString(matchedClassLoaders.get(0).hashCode());
} else if (matchedClassLoaders.size() > 1) {
Collection<ClassLoaderVO> classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders);
JadModel jadModel = new JadModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(jadModel);
process.end(-1, "Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'");
return;
} else {
process.end(-1, "Can not find classloader by class name: " + classLoaderClass + ".");
return;
}
}
Set<Class<?>> matchedClasses = SearchUtils.searchClassOnly(inst, classPattern, isRegEx, code);
try {
@@ -7,6 +7,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Collection;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
@@ -14,12 +15,15 @@ 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.command.model.ClassLoaderVO;
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.ClassLoaderUtils;
import com.taobao.arthas.core.util.FileUtils;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.arthas.core.util.ClassLoaderUtils;
import com.taobao.arthas.core.util.affect.RowAffect;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.Description;
@@ -43,6 +47,7 @@ public class MemoryCompilerCommand extends AnnotatedCommand {
private String directory;
private String hashCode;
private String classLoaderClass;
private String encoding;
private List<String> sourcefiles;
@@ -59,6 +64,12 @@ public class MemoryCompilerCommand extends AnnotatedCommand {
this.hashCode = hashCode;
}
@Option(longName = "classLoaderClass")
@Description("The class name of the special class's classLoader.")
public void setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
}
@Option(longName = "encoding")
@Description("Source file encoding")
public void setEncoding(String encoding) {
@@ -77,6 +88,25 @@ public class MemoryCompilerCommand extends AnnotatedCommand {
try {
Instrumentation inst = process.session().getInstrumentation();
if (hashCode == null && classLoaderClass != null) {
List<ClassLoader> matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass);
if (matchedClassLoaders.size() == 1) {
hashCode = Integer.toHexString(matchedClassLoaders.get(0).hashCode());
} else if (matchedClassLoaders.size() > 1) {
Collection<ClassLoaderVO> classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders);
MemoryCompilerModel memoryCompilerModel = new MemoryCompilerModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(memoryCompilerModel);
process.end(-1, "Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'");
return;
} else {
process.end(-1, "Can not find classloader by class name: " + classLoaderClass + ".");
return;
}
}
ClassLoader classloader = null;
if (hashCode == null) {
classloader = ClassLoader.getSystemClassLoader();
@@ -9,16 +9,20 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Collection;
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.command.model.ClassLoaderVO;
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.ClassLoaderUtils;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
@@ -42,6 +46,7 @@ public class RedefineCommand extends AnnotatedCommand {
private static final int MAX_FILE_SIZE = 10 * 1024 * 1024;
private String hashCode;
private String classLoaderClass;
private List<String> paths;
@@ -51,6 +56,12 @@ public class RedefineCommand extends AnnotatedCommand {
this.hashCode = hashCode;
}
@Option(longName = "classLoaderClass")
@Description("The class name of the special class's classLoader.")
public void setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
}
@Argument(argName = "classfilePaths", index = 0)
@Description(".class file paths")
public void setPaths(List<String> paths) {
@@ -112,6 +123,25 @@ public class RedefineCommand extends AnnotatedCommand {
List<ClassDefinition> definitions = new ArrayList<ClassDefinition>();
for (Class<?> clazz : inst.getAllLoadedClasses()) {
if (bytesMap.containsKey(clazz.getName())) {
if (hashCode == null && classLoaderClass != null) {
List<ClassLoader> matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass);
if (matchedClassLoaders.size() == 1) {
hashCode = Integer.toHexString(matchedClassLoaders.get(0).hashCode());
} else if (matchedClassLoaders.size() > 1) {
Collection<ClassLoaderVO> classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders);
RedefineModel classredefineModel = new RedefineModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(classredefineModel);
process.end(-1, "Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'");
return;
} else {
process.end(-1, "Can not find classloader by class name: " + classLoaderClass + ".");
return;
}
}
ClassLoader classLoader = clazz.getClassLoader();
if (classLoader != null && hashCode != null && !Integer.toHexString(classLoader.hashCode()).equals(hashCode)) {
continue;
@@ -11,6 +11,8 @@ public class DumpClassModel extends ResultModel {
private List<DumpClassVO> dumpedClasses;
private Collection<ClassVO> matchedClasses;
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
public DumpClassModel() {
}
@@ -38,4 +40,22 @@ public class DumpClassModel extends ResultModel {
return this;
}
public String getClassLoaderClass() {
return classLoaderClass;
}
public DumpClassModel setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
return this;
}
public Collection<ClassLoaderVO> getMatchedClassLoaders() {
return matchedClassLoaders;
}
public DumpClassModel setMatchedClassLoaders(Collection<ClassLoaderVO> matchedClassLoaders) {
this.matchedClassLoaders = matchedClassLoaders;
return this;
}
}
@@ -1,6 +1,7 @@
package com.taobao.arthas.core.command.model;
import java.util.Collection;
import java.util.List;
/**
* Data model of GetStaticCommand
@@ -11,6 +12,8 @@ public class GetStaticModel extends ResultModel {
private Collection<ClassVO> matchedClasses;
private ObjectVO field;
private int expand;
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
public GetStaticModel() {
}
@@ -48,6 +51,24 @@ public class GetStaticModel extends ResultModel {
this.expand = expand;
}
public String getClassLoaderClass() {
return classLoaderClass;
}
public GetStaticModel setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
return this;
}
public Collection<ClassLoaderVO> getMatchedClassLoaders() {
return matchedClassLoaders;
}
public GetStaticModel setMatchedClassLoaders(Collection<ClassLoaderVO> matchedClassLoaders) {
this.matchedClassLoaders = matchedClassLoaders;
return this;
}
@Override
public String getType() {
return "getstatic";
@@ -1,6 +1,7 @@
package com.taobao.arthas.core.command.model;
import java.util.Collection;
import java.util.List;
/**
* @author gongdewei 2020/4/22
@@ -9,6 +10,8 @@ public class JadModel extends ResultModel {
private ClassVO classInfo;
private String location;
private String source;
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
//match multiple classes
private Collection<ClassVO> matchedClasses;
@@ -52,4 +55,22 @@ public class JadModel extends ResultModel {
public void setMatchedClasses(Collection<ClassVO> matchedClasses) {
this.matchedClasses = matchedClasses;
}
public String getClassLoaderClass() {
return classLoaderClass;
}
public JadModel setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
return this;
}
public Collection<ClassLoaderVO> getMatchedClassLoaders() {
return matchedClassLoaders;
}
public JadModel setMatchedClassLoaders(Collection<ClassLoaderVO> matchedClassLoaders) {
this.matchedClassLoaders = matchedClassLoaders;
return this;
}
}
@@ -1,5 +1,6 @@
package com.taobao.arthas.core.command.model;
import java.util.Collection;
import java.util.List;
/**
@@ -8,6 +9,8 @@ import java.util.List;
public class MemoryCompilerModel extends ResultModel {
private List<String> files;
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
public MemoryCompilerModel() {
}
@@ -24,6 +27,24 @@ public class MemoryCompilerModel extends ResultModel {
return files;
}
public String getClassLoaderClass() {
return classLoaderClass;
}
public MemoryCompilerModel setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
return this;
}
public Collection<ClassLoaderVO> getMatchedClassLoaders() {
return matchedClassLoaders;
}
public MemoryCompilerModel setMatchedClassLoaders(Collection<ClassLoaderVO> matchedClassLoaders) {
this.matchedClassLoaders = matchedClassLoaders;
return this;
}
@Override
public String getType() {
return "mc";
@@ -1,6 +1,7 @@
package com.taobao.arthas.core.command.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
@@ -11,6 +12,8 @@ public class RedefineModel extends ResultModel {
private int redefinitionCount;
private List<String> redefinedClasses;
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
public RedefineModel() {
redefinedClasses = new ArrayList<String>();
@@ -37,6 +40,24 @@ public class RedefineModel extends ResultModel {
this.redefinedClasses = redefinedClasses;
}
public String getClassLoaderClass() {
return classLoaderClass;
}
public RedefineModel setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
return this;
}
public Collection<ClassLoaderVO> getMatchedClassLoaders() {
return matchedClassLoaders;
}
public RedefineModel setMatchedClassLoaders(Collection<ClassLoaderVO> matchedClassLoaders) {
this.matchedClassLoaders = matchedClassLoaders;
return this;
}
@Override
public String getType() {
return "redefine";
@@ -23,6 +23,12 @@ public class DumpClassView extends ResultView<DumpClassModel> {
@Override
public void draw(CommandProcess process, DumpClassModel result) {
if (result.getMatchedClassLoaders() != null) {
process.write("Matched classloaders: \n");
ClassLoaderView.drawClassLoaders(process, result.getMatchedClassLoaders(), false);
process.write("\n");
return;
}
if (result.getDumpedClasses() != null) {
drawDumpedClasses(process, result.getDumpedClasses());
@@ -16,6 +16,12 @@ public class GetStaticView extends ResultView<GetStaticModel> {
@Override
public void draw(CommandProcess process, GetStaticModel result) {
if (result.getMatchedClassLoaders() != null) {
process.write("Matched classloaders: \n");
ClassLoaderView.drawClassLoaders(process, result.getMatchedClassLoaders(), false);
process.write("\n");
return;
}
int expand = result.getExpand();
if (result.getField() != null) {
ObjectVO field = result.getField();
@@ -19,6 +19,12 @@ public class JadView extends ResultView<JadModel> {
@Override
public void draw(CommandProcess process, JadModel result) {
if (result.getMatchedClassLoaders() != null) {
process.write("Matched classloaders: \n");
ClassLoaderView.drawClassLoaders(process, result.getMatchedClassLoaders(), false);
process.write("\n");
return;
}
if (result.getMatchedClasses() != null) {
Element table = ClassUtils.renderMatchedClasses(result.getMatchedClasses());
@@ -9,6 +9,12 @@ import com.taobao.arthas.core.shell.command.CommandProcess;
public class MemoryCompilerView extends ResultView<MemoryCompilerModel> {
@Override
public void draw(CommandProcess process, MemoryCompilerModel result) {
if (result.getMatchedClassLoaders() != null) {
process.write("Matched classloaders: \n");
ClassLoaderView.drawClassLoaders(process, result.getMatchedClassLoaders(), false);
process.write("\n");
return;
}
process.write("Memory compiler output:\n");
for (String file : result.getFiles()) {
process.write(file + '\n');
@@ -10,6 +10,12 @@ public class RedefineView extends ResultView<RedefineModel> {
@Override
public void draw(CommandProcess process, RedefineModel result) {
if (result.getMatchedClassLoaders() != null) {
process.write("Matched classloaders: \n");
ClassLoaderView.drawClassLoaders(process, result.getMatchedClassLoaders(), false);
process.write("\n");
return;
}
StringBuilder sb = new StringBuilder();
for (String aClass : result.getRedefinedClasses()) {
sb.append(aClass).append("\n");