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

pull/1463/head
Hollow Man 4 years ago committed by GitHub
parent ed70f644a3
commit e3283f0439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,6 +4,7 @@ import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.ClassVO; 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.DumpClassModel;
import com.taobao.arthas.core.command.model.DumpClassVO; import com.taobao.arthas.core.command.model.DumpClassVO;
import com.taobao.arthas.core.command.model.MessageModel; 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.CommandProcess;
import com.taobao.arthas.core.shell.command.ExitStatus; import com.taobao.arthas.core.shell.command.ExitStatus;
import com.taobao.arthas.core.util.ClassUtils; 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.CommandUtils;
import com.taobao.arthas.core.util.InstrumentationUtils; import com.taobao.arthas.core.util.InstrumentationUtils;
import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.SearchUtils;
@ -32,6 +34,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Collection;
/** /**
@ -51,6 +54,7 @@ public class DumpClassCommand extends AnnotatedCommand {
private String classPattern; private String classPattern;
private String code = null; private String code = null;
private String classLoaderClass;
private boolean isRegEx = false; private boolean isRegEx = false;
private String directory; private String directory;
@ -69,6 +73,12 @@ public class DumpClassCommand extends AnnotatedCommand {
this.code = code; 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) @Option(shortName = "E", longName = "regex", flag = true)
@Description("Enable regular expression to match (wildcard matching by default)") @Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) { public void setRegEx(boolean regEx) {
@ -101,7 +111,26 @@ public class DumpClassCommand extends AnnotatedCommand {
} }
ExitStatus status = null; ExitStatus status = null;
Instrumentation inst = process.session().getInstrumentation(); 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); Set<Class<?>> matchedClasses = SearchUtils.searchClass(inst, classPattern, isRegEx, code);
if (matchedClasses == null || matchedClasses.isEmpty()) { if (matchedClasses == null || matchedClasses.isEmpty()) {
status = processNoMatch(process); 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.ExpressException;
import com.taobao.arthas.core.command.express.ExpressFactory; import com.taobao.arthas.core.command.express.ExpressFactory;
import com.taobao.arthas.core.command.model.ClassVO; 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.GetStaticModel;
import com.taobao.arthas.core.command.model.MessageModel; import com.taobao.arthas.core.command.model.MessageModel;
import com.taobao.arthas.core.command.model.RowAffectModel; 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.CommandProcess;
import com.taobao.arthas.core.shell.command.ExitStatus; import com.taobao.arthas.core.shell.command.ExitStatus;
import com.taobao.arthas.core.util.ClassUtils; 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.CommandUtils;
import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.SearchUtils;
import com.taobao.arthas.core.util.StringUtils; import com.taobao.arthas.core.util.StringUtils;
@ -31,6 +33,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.Collection;
/** /**
* @author diecui1202 on 2017/9/27. * @author diecui1202 on 2017/9/27.
@ -50,6 +53,7 @@ public class GetStaticCommand extends AnnotatedCommand {
private String fieldPattern; private String fieldPattern;
private String express; private String express;
private String hashCode = null; private String hashCode = null;
private String classLoaderClass;
private boolean isRegEx = false; private boolean isRegEx = false;
private int expand = 1; private int expand = 1;
@ -77,6 +81,12 @@ public class GetStaticCommand extends AnnotatedCommand {
this.hashCode = hashCode; 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) @Option(shortName = "E", longName = "regex", flag = true)
@Description("Enable regular expression to match (wildcard matching by default)") @Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) { public void setRegEx(boolean regEx) {
@ -93,6 +103,25 @@ public class GetStaticCommand extends AnnotatedCommand {
public void process(CommandProcess process) { public void process(CommandProcess process) {
RowAffect affect = new RowAffect(); RowAffect affect = new RowAffect();
Instrumentation inst = process.session().getInstrumentation(); 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); Set<Class<?>> matchedClasses = SearchUtils.searchClassOnly(inst, classPattern, isRegEx, hashCode);
try { try {
if (matchedClasses == null || matchedClasses.isEmpty()) { 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.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.ClassVO; 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.JadModel;
import com.taobao.arthas.core.command.model.MessageModel; import com.taobao.arthas.core.command.model.MessageModel;
import com.taobao.arthas.core.command.model.RowAffectModel; 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.CommandProcess;
import com.taobao.arthas.core.shell.command.ExitStatus; import com.taobao.arthas.core.shell.command.ExitStatus;
import com.taobao.arthas.core.util.ClassUtils; 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.CommandUtils;
import com.taobao.arthas.core.util.Decompiler; import com.taobao.arthas.core.util.Decompiler;
import com.taobao.arthas.core.util.InstrumentationUtils; import com.taobao.arthas.core.util.InstrumentationUtils;
@ -30,6 +32,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Collection;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
@ -52,6 +55,7 @@ public class JadCommand extends AnnotatedCommand {
private String classPattern; private String classPattern;
private String methodName; private String methodName;
private String code = null; private String code = null;
private String classLoaderClass;
private boolean isRegEx = false; private boolean isRegEx = false;
private boolean hideUnicode = false; private boolean hideUnicode = false;
@ -79,6 +83,12 @@ public class JadCommand extends AnnotatedCommand {
this.code = code; 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) @Option(shortName = "E", longName = "regex", flag = true)
@Description("Enable regular expression to match (wildcard matching by default)") @Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) { public void setRegEx(boolean regEx) {
@ -101,6 +111,25 @@ public class JadCommand extends AnnotatedCommand {
public void process(CommandProcess process) { public void process(CommandProcess process) {
RowAffect affect = new RowAffect(); RowAffect affect = new RowAffect();
Instrumentation inst = process.session().getInstrumentation(); 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); Set<Class<?>> matchedClasses = SearchUtils.searchClassOnly(inst, classPattern, isRegEx, code);
try { try {

@ -7,6 +7,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Collection;
import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; 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.Constants;
import com.taobao.arthas.core.command.model.MemoryCompilerModel; import com.taobao.arthas.core.command.model.MemoryCompilerModel;
import com.taobao.arthas.core.command.model.RowAffectModel; 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.Completion;
import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.cli.CompletionUtils;
import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess; import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.util.ClassLoaderUtils; import com.taobao.arthas.core.util.ClassLoaderUtils;
import com.taobao.arthas.core.util.FileUtils; 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.arthas.core.util.affect.RowAffect;
import com.taobao.middleware.cli.annotations.Argument; import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Description;
@ -43,6 +47,7 @@ public class MemoryCompilerCommand extends AnnotatedCommand {
private String directory; private String directory;
private String hashCode; private String hashCode;
private String classLoaderClass;
private String encoding; private String encoding;
private List<String> sourcefiles; private List<String> sourcefiles;
@ -59,6 +64,12 @@ public class MemoryCompilerCommand extends AnnotatedCommand {
this.hashCode = hashCode; 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") @Option(longName = "encoding")
@Description("Source file encoding") @Description("Source file encoding")
public void setEncoding(String encoding) { public void setEncoding(String encoding) {
@ -77,6 +88,25 @@ public class MemoryCompilerCommand extends AnnotatedCommand {
try { try {
Instrumentation inst = process.session().getInstrumentation(); 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; ClassLoader classloader = null;
if (hashCode == null) { if (hashCode == null) {
classloader = ClassLoader.getSystemClassLoader(); classloader = ClassLoader.getSystemClassLoader();

@ -9,16 +9,20 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Collection;
import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader; import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader;
import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.RedefineModel; 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.Completion;
import com.taobao.arthas.core.shell.cli.CompletionUtils; import com.taobao.arthas.core.shell.cli.CompletionUtils;
import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess; 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.Argument;
import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name; 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 static final int MAX_FILE_SIZE = 10 * 1024 * 1024;
private String hashCode; private String hashCode;
private String classLoaderClass;
private List<String> paths; private List<String> paths;
@ -51,6 +56,12 @@ public class RedefineCommand extends AnnotatedCommand {
this.hashCode = hashCode; 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) @Argument(argName = "classfilePaths", index = 0)
@Description(".class file paths") @Description(".class file paths")
public void setPaths(List<String> paths) { public void setPaths(List<String> paths) {
@ -112,6 +123,25 @@ public class RedefineCommand extends AnnotatedCommand {
List<ClassDefinition> definitions = new ArrayList<ClassDefinition>(); List<ClassDefinition> definitions = new ArrayList<ClassDefinition>();
for (Class<?> clazz : inst.getAllLoadedClasses()) { for (Class<?> clazz : inst.getAllLoadedClasses()) {
if (bytesMap.containsKey(clazz.getName())) { 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(); ClassLoader classLoader = clazz.getClassLoader();
if (classLoader != null && hashCode != null && !Integer.toHexString(classLoader.hashCode()).equals(hashCode)) { if (classLoader != null && hashCode != null && !Integer.toHexString(classLoader.hashCode()).equals(hashCode)) {
continue; continue;

@ -11,6 +11,8 @@ public class DumpClassModel extends ResultModel {
private List<DumpClassVO> dumpedClasses; private List<DumpClassVO> dumpedClasses;
private Collection<ClassVO> matchedClasses; private Collection<ClassVO> matchedClasses;
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
public DumpClassModel() { public DumpClassModel() {
} }
@ -38,4 +40,22 @@ public class DumpClassModel extends ResultModel {
return this; 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; package com.taobao.arthas.core.command.model;
import java.util.Collection; import java.util.Collection;
import java.util.List;
/** /**
* Data model of GetStaticCommand * Data model of GetStaticCommand
@ -11,6 +12,8 @@ public class GetStaticModel extends ResultModel {
private Collection<ClassVO> matchedClasses; private Collection<ClassVO> matchedClasses;
private ObjectVO field; private ObjectVO field;
private int expand; private int expand;
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
public GetStaticModel() { public GetStaticModel() {
} }
@ -48,6 +51,24 @@ public class GetStaticModel extends ResultModel {
this.expand = expand; 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 @Override
public String getType() { public String getType() {
return "getstatic"; return "getstatic";

@ -1,6 +1,7 @@
package com.taobao.arthas.core.command.model; package com.taobao.arthas.core.command.model;
import java.util.Collection; import java.util.Collection;
import java.util.List;
/** /**
* @author gongdewei 2020/4/22 * @author gongdewei 2020/4/22
@ -9,6 +10,8 @@ public class JadModel extends ResultModel {
private ClassVO classInfo; private ClassVO classInfo;
private String location; private String location;
private String source; private String source;
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
//match multiple classes //match multiple classes
private Collection<ClassVO> matchedClasses; private Collection<ClassVO> matchedClasses;
@ -52,4 +55,22 @@ public class JadModel extends ResultModel {
public void setMatchedClasses(Collection<ClassVO> matchedClasses) { public void setMatchedClasses(Collection<ClassVO> matchedClasses) {
this.matchedClasses = 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; package com.taobao.arthas.core.command.model;
import java.util.Collection;
import java.util.List; import java.util.List;
/** /**
@ -8,6 +9,8 @@ import java.util.List;
public class MemoryCompilerModel extends ResultModel { public class MemoryCompilerModel extends ResultModel {
private List<String> files; private List<String> files;
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
public MemoryCompilerModel() { public MemoryCompilerModel() {
} }
@ -24,6 +27,24 @@ public class MemoryCompilerModel extends ResultModel {
return files; 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 @Override
public String getType() { public String getType() {
return "mc"; return "mc";

@ -1,6 +1,7 @@
package com.taobao.arthas.core.command.model; package com.taobao.arthas.core.command.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
/** /**
@ -11,6 +12,8 @@ public class RedefineModel extends ResultModel {
private int redefinitionCount; private int redefinitionCount;
private List<String> redefinedClasses; private List<String> redefinedClasses;
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
public RedefineModel() { public RedefineModel() {
redefinedClasses = new ArrayList<String>(); redefinedClasses = new ArrayList<String>();
@ -37,6 +40,24 @@ public class RedefineModel extends ResultModel {
this.redefinedClasses = redefinedClasses; 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 @Override
public String getType() { public String getType() {
return "redefine"; return "redefine";

@ -23,6 +23,12 @@ public class DumpClassView extends ResultView<DumpClassModel> {
@Override @Override
public void draw(CommandProcess process, DumpClassModel result) { 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) { if (result.getDumpedClasses() != null) {
drawDumpedClasses(process, result.getDumpedClasses()); drawDumpedClasses(process, result.getDumpedClasses());

@ -16,6 +16,12 @@ public class GetStaticView extends ResultView<GetStaticModel> {
@Override @Override
public void draw(CommandProcess process, GetStaticModel result) { 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(); int expand = result.getExpand();
if (result.getField() != null) { if (result.getField() != null) {
ObjectVO field = result.getField(); ObjectVO field = result.getField();

@ -19,6 +19,12 @@ public class JadView extends ResultView<JadModel> {
@Override @Override
public void draw(CommandProcess process, JadModel result) { 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) { if (result.getMatchedClasses() != null) {
Element table = ClassUtils.renderMatchedClasses(result.getMatchedClasses()); Element table = ClassUtils.renderMatchedClasses(result.getMatchedClasses());

@ -9,6 +9,12 @@ import com.taobao.arthas.core.shell.command.CommandProcess;
public class MemoryCompilerView extends ResultView<MemoryCompilerModel> { public class MemoryCompilerView extends ResultView<MemoryCompilerModel> {
@Override @Override
public void draw(CommandProcess process, MemoryCompilerModel result) { 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"); process.write("Memory compiler output:\n");
for (String file : result.getFiles()) { for (String file : result.getFiles()) {
process.write(file + '\n'); process.write(file + '\n');

@ -10,6 +10,12 @@ public class RedefineView extends ResultView<RedefineModel> {
@Override @Override
public void draw(CommandProcess process, RedefineModel result) { 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(); StringBuilder sb = new StringBuilder();
for (String aClass : result.getRedefinedClasses()) { for (String aClass : result.getRedefinedClasses()) {
sb.append(aClass).append("\n"); sb.append(aClass).append("\n");

@ -11,6 +11,7 @@ dump
|---:|:---| |---:|:---|
|*class-pattern*|类名表达式匹配| |*class-pattern*|类名表达式匹配|
|`[c:]`|类所属 ClassLoader 的 hashcode| |`[c:]`|类所属 ClassLoader 的 hashcode|
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|`[d:]`|设置类文件的目标目录| |`[d:]`|设置类文件的目标目录|
|[E]|开启正则表达式匹配,默认为通配符匹配| |[E]|开启正则表达式匹配,默认为通配符匹配|
@ -37,3 +38,27 @@ $ dump -d /tmp/output java.lang.String
null /tmp/output/java/lang/String.class null /tmp/output/java/lang/String.class
Affect(row-cnt:1) cost in 138 ms. Affect(row-cnt:1) cost in 138 ms.
``` ```
* 指定classLoader
注意hashcode是变化的需要先查看当前的ClassLoader信息提取对应ClassLoader的hashcode。
如果你使用`-c`你需要手动输入hashcode`-c <hashcode>`
```bash
$ dump -c 3d4eac69 demo.*
```
对于只有唯一实例的ClassLoader可以通过`--classLoaderClass`指定class name使用起来更加方便
```bash
$ dump --classLoaderClass sun.misc.Launcher$AppClassLoader demo.*
HASHCODE CLASSLOADER LOCATION
3d4eac69 +-sun.misc.Launcher$AppClassLoader@3d4eac69 /Users/admin/logs/arthas/classdump/sun.misc.Launcher$AppClassLoader-3d4eac69/demo/MathGame.class
+-sun.misc.Launcher$ExtClassLoader@66350f69
Affect(row-cnt:1) cost in 39 ms.
```
* 注: 这里classLoaderClass 在 java 8 是 sun.misc.Launcher$AppClassLoader而java 11的classloader是jdk.internal.loader.ClassLoaders$AppClassLoaderkatacoda目前环境是java8。
`--classLoaderClass` 的值是ClassLoader的类名只有匹配到唯一的ClassLoader实例时才能工作目的是方便输入通用命令而`-c <hashcode>`是动态变化的。

@ -11,6 +11,7 @@ dump
|---:|:---| |---:|:---|
|*class-pattern*|class name pattern| |*class-pattern*|class name pattern|
|`[c:]`|hashcode of the [class loader](classloader.md) that loaded the target class| |`[c:]`|hashcode of the [class loader](classloader.md) that loaded the target class|
|`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. |
|`[d:]`|set the destination directory for class files| |`[d:]`|set the destination directory for class files|
|`[E]`|turn on regex match, the default behavior is wild card match| |`[E]`|turn on regex match, the default behavior is wild card match|
@ -37,3 +38,27 @@ $ dump -d /tmp/output java.lang.String
null /tmp/output/java/lang/String.class null /tmp/output/java/lang/String.class
Affect(row-cnt:1) cost in 138 ms. Affect(row-cnt:1) cost in 138 ms.
``` ```
* Specify classLoader
Note that the hashcode changes, you need to check the current ClassLoader information first, and extract the hashcode corresponding to the ClassLoader.
if you use`-c`, you have to manually type hashcode by `-c <hashcode>`.
```bash
$ dump -c 3d4eac69 demo.*
```
For classloader with only one instance, it can be specified by `--classLoaderClass` using class name, which is more convenient to use.
```bash
$ dump --classLoaderClass sun.misc.Launcher$AppClassLoader demo.*
HASHCODE CLASSLOADER LOCATION
3d4eac69 +-sun.misc.Launcher$AppClassLoader@3d4eac69 /Users/admin/logs/arthas/classdump/sun.misc.Launcher$AppClassLoader-3d4eac69/demo/MathGame.class
+-sun.misc.Launcher$ExtClassLoader@66350f69
Affect(row-cnt:1) cost in 39 ms.
```
* PS: Here the classLoaderClass in java 8 is sun.misc.Launcher$AppClassLoader, while in java 11 it's jdk.internal.loader.ClassLoaders$AppClassLoader. Currently katacoda using java 8.
The value of `--classloaderclass` is the class name of classloader. It can only work when it matches a unique classloader instance. The purpose is to facilitate the input of general commands. However, `-c <hashcode>` is dynamic.

@ -29,6 +29,23 @@ field: random
] ]
``` ```
* Specify classLoader
Note that the hashcode changes, you need to check the current ClassLoader information first, and extract the hashcode corresponding to the ClassLoader using `sc -d <ClassName>`.
if you use`-c`, you have to manually type hashcode by `-c <hashcode>`.
```bash
$ getstatic -c 3d4eac69 demo.MathGame random
```
For classloader with only one instance, it can be specified by `--classLoaderClass` using class name, which is more convenient to use.
`getstatic --classLoaderClass demo.MathGame random`
* PS: Here the classLoaderClass in java 8 is sun.misc.Launcher$AppClassLoader, while in java 11 it's jdk.internal.loader.ClassLoaders$AppClassLoader. Currently katacoda using java 8.
The value of `--classloaderclass` is the class name of classloader. It can only work when it matches a unique classloader instance. The purpose is to facilitate the input of general commands. However, `-c <hashcode>` is dynamic.
Tip: if the static field is a complex class, you can even use [`OGNL`](https://commons.apache.org/proper/commons-ognl/language-guide.html) to traverse, filter and access the inner properties of this class. Tip: if the static field is a complex class, you can even use [`OGNL`](https://commons.apache.org/proper/commons-ognl/language-guide.html) to traverse, filter and access the inner properties of this class.

@ -16,6 +16,7 @@ jad
|---:|:---| |---:|:---|
|*class-pattern*|pattern for the class name| |*class-pattern*|pattern for the class name|
|`[c:]`|hashcode of the class loader that loads the class| |`[c:]`|hashcode of the class loader that loads the class|
|`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. |
|`[E]`|turn on regex match while the default is wildcard match| |`[E]`|turn on regex match while the default is wildcard match|
### Usage ### Usage
@ -143,3 +144,7 @@ public class Logger extends Category
Affect(row-cnt:1) cost in 190 ms. Affect(row-cnt:1) cost in 190 ms.
``` ```
For classloader with only one instance, it can be specified by `--classLoaderClass` using class name, which is more convenient to use.
The value of `--classloaderclass` is the class name of classloader. It can only work when it matches a unique classloader instance. The purpose is to facilitate the input of general commands. However, `-c <hashcode>` is dynamic.

@ -144,11 +144,11 @@ The value of `--classloaderclass` is the class name of classloader. It can only
update logger level success. update logger level success.
``` ```
#### 指定classloader更新 logger level #### Speecify classloader to update logger level
默认情况下logger命令会在SystemClassloader下执行如果应用是传统的`war`应用或者spring boot fat jar启动的应用那么需要指定classloader。 By defaultlogger command will be executed under SystemClassloader, if the application is a traditional `war`, or using spring boot fat jar, then it needs to specify classloader。
可以先用 `sc -d yourClassName` 来查看具体的 classloader hashcode然后在更新level时指定classloader You can first use `sc -d yourClassName` to check specified classloader hashcodethen specify classloader when updating logger level:
```bash ```bash
[arthas@2062]$ logger -c 2a139a55 --name ROOT --level debug [arthas@2062]$ logger -c 2a139a55 --name ROOT --level debug

@ -15,6 +15,15 @@ The classloader can be specified with the `-c` option:
mc -c 327a647b /tmp/Test.java mc -c 327a647b /tmp/Test.java
``` ```
You can also specify the ClassLoader with the `--classLoaderClass` option:
```bash
$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms
```
The output directory can be specified with the `-d` option: The output directory can be specified with the `-d` option:
```bash ```bash

@ -28,6 +28,7 @@ Reference: [Instrumentation#redefineClasses](https://docs.oracle.com/javase/8/do
|Name|Specification| |Name|Specification|
|---:|:---| |---:|:---|
|`[c:]`|hashcode of the class loader| |`[c:]`|hashcode of the class loader|
|`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. |
|`[p:]`|absolute path of the external `*.class`, multiple paths are separated with 'space'| |`[p:]`|absolute path of the external `*.class`, multiple paths are separated with 'space'|
@ -35,7 +36,8 @@ Reference: [Instrumentation#redefineClasses](https://docs.oracle.com/javase/8/do
```bash ```bash
redefine /tmp/Test.class redefine /tmp/Test.class
redefine -c 327a647b /tmp/Test.class /tmp/Test$Inner.class redefine -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class
redefine --classLoaderClass sun.misc.Launcher$AppClassLoader /tmp/Test.class /tmp/Test\$Inner.class
``` ```
### Use with the jad/mc command ### Use with the jad/mc command

@ -30,6 +30,25 @@ field: random
] ]
``` ```
* 指定classLoader
注意hashcode是变化的需要先查看当前的ClassLoader信息使用`sc -d <ClassName>`提取对应ClassLoader的hashcode。
如果你使用`-c`你需要手动输入hashcode`-c <hashcode>`
```bash
$ getstatic -c 3d4eac69 demo.MathGame random
```
对于只有唯一实例的ClassLoader可以通过`--classLoaderClass`指定class name使用起来更加方便
`getstatic --classLoaderClass sun.misc.Launcher$AppClassLoader demo.MathGame random`
* 注: 这里classLoaderClass 在 java 8 是 sun.misc.Launcher$AppClassLoader而java 11的classloader是jdk.internal.loader.ClassLoaders$AppClassLoaderkatacoda目前环境是java8。
`--classLoaderClass` 的值是ClassLoader的类名只有匹配到唯一的ClassLoader实例时才能工作目的是方便输入通用命令而`-c <hashcode>`是动态变化的。
如果该静态属性是一个复杂对象还可以支持在该属性上通过ognl表示进行遍历过滤访问对象的内部属性等操作。 如果该静态属性是一个复杂对象还可以支持在该属性上通过ognl表示进行遍历过滤访问对象的内部属性等操作。
* OGNL特殊用法请参考[https://github.com/alibaba/arthas/issues/71](https://github.com/alibaba/arthas/issues/71) * OGNL特殊用法请参考[https://github.com/alibaba/arthas/issues/71](https://github.com/alibaba/arthas/issues/71)

@ -16,6 +16,7 @@ jad
|---:|:---| |---:|:---|
|*class-pattern*|类名表达式匹配| |*class-pattern*|类名表达式匹配|
|`[c:]`|类所属 ClassLoader 的 hashcode| |`[c:]`|类所属 ClassLoader 的 hashcode|
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|[E]|开启正则表达式匹配,默认为通配符匹配| |[E]|开启正则表达式匹配,默认为通配符匹配|
### 使用参考 ### 使用参考
@ -144,3 +145,6 @@ public class Logger extends Category
Affect(row-cnt:1) cost in 190 ms. Affect(row-cnt:1) cost in 190 ms.
``` ```
对于只有唯一实例的ClassLoader还可以通过`--classLoaderClass`指定class name使用起来更加方便
`--classLoaderClass` 的值是ClassLoader的类名只有匹配到唯一的ClassLoader实例时才能工作目的是方便输入通用命令而`-c <hashcode>`是动态变化的。

@ -15,6 +15,15 @@ mc /tmp/Test.java
mc -c 327a647b /tmp/Test.java mc -c 327a647b /tmp/Test.java
``` ```
也可以通过`--classLoaderClass`参数指定ClassLoader
```bash
$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms
```
可以通过`-d`命令指定输出目录: 可以通过`-d`命令指定输出目录:
```bash ```bash

@ -27,6 +27,7 @@ redefine
|参数名称|参数说明| |参数名称|参数说明|
|---:|:---| |---:|:---|
|[c:]|ClassLoader的hashcode| |[c:]|ClassLoader的hashcode|
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|[p:]|外部的`.class`文件的完整路径,支持多个| |[p:]|外部的`.class`文件的完整路径,支持多个|
@ -36,6 +37,7 @@ redefine
```bash ```bash
redefine /tmp/Test.class redefine /tmp/Test.class
redefine -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class redefine -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class
redefine --classLoaderClass sun.misc.Launcher$AppClassLoader /tmp/Test.class /tmp/Test\$Inner.class
``` ```
### 结合 jad/mc 命令使用 ### 结合 jad/mc 命令使用

@ -47,21 +47,25 @@ $ sc -d *UserController | grep classLoaderHash
请记下你的classLoaderHash后面需要使用它。在这里它是 `1be6f5c3` 请记下你的classLoaderHash后面需要使用它。在这里它是 `1be6f5c3`
注意请使用你的classLoaderHash值覆盖 `<classLoaderHash>` ,然后手动执行这些命令:
### mc ### mc
保存好`/tmp/UserController.java`之后,使用`mc`(Memory Compiler)命令来编译,并且通过`-c`参数指定ClassLoader 保存好`/tmp/UserController.java`之后,使用`mc`(Memory Compiler)命令来编译,并且通过`-c`或者`--classLoaderClass`参数指定ClassLoader
`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp` `mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp`{{execute T2}}
```bash ```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp $ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output: Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class /tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms Affect(row-cnt:1) cost in 346 ms
``` ```
也可以通过`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp`,使用`-c`参数指定ClassLoaderHash:
```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
```
### redefine ### redefine
再使用`redefine`命令重新加载新编译好的`UserController.class` 再使用`redefine`命令重新加载新编译好的`UserController.class`

@ -48,21 +48,25 @@ It can be found that it is loaded by spring boot `LaunchedURLClassLoader@1be6f5c
Please write down your classLoaderHash here, in the case here, it's `1be6f5c3`. It will be used in the future steps. Please write down your classLoaderHash here, in the case here, it's `1be6f5c3`. It will be used in the future steps.
Note: Please replace `<classLoaderHash>` with your classLoaderHash above, then execute the related commands manually in the following steps:
### mc ### mc
After saving `/tmp/UserController.java`, compile with the `mc` (Memory Compiler) command and specify the ClassLoader with the `-c` option: After saving `/tmp/UserController.java`, compile with the `mc` (Memory Compiler) command and specify the ClassLoader with the `-c` or `--classLoaderClass` option:
`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp` `mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp`{{execute T2}}
```bash ```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp $ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output: Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class /tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms Affect(row-cnt:1) cost in 346 ms
``` ```
You can also execute `mc -c <classLoaderHash> /tmp/UserController.java -d /tmp`using `-c` to specify ClassLoaderHash:
```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
```
### redefine ### redefine
Then reload the newly compiled `UserController.class` with the `redefine` command: Then reload the newly compiled `UserController.class` with the `redefine` command:

@ -45,23 +45,33 @@ $ sc -d *UserController | grep classLoaderHash
可以发现是 spring boot `LaunchedURLClassLoader@1be6f5c3` 加载的。 可以发现是 spring boot `LaunchedURLClassLoader@1be6f5c3` 加载的。
请记下你的classLoaderHash后面需要使用它。在这里它是 `1be6f5c3` 注意hashcode是变化的需要先查看当前的ClassLoader信息提取对应ClassLoader的hashcode
注意请使用你的classLoaderHash值覆盖 `<classLoaderHash>` ,然后手动执行下面所有所述命令: 如果你使用`-c`你需要手动输入hashcode`-c <hashcode>`
对于只有唯一实例的ClassLoader可以通过`--classLoaderClass`指定class name使用起来更加方便.
`--classLoaderClass` 的值是ClassLoader的类名只有匹配到唯一的ClassLoader实例时才能工作目的是方便输入通用命令而`-c <hashcode>`是动态变化的。
### mc ### mc
保存好`/tmp/UserController.java`之后,使用`mc`(Memory Compiler)命令来编译,并且通过`-c`参数指定ClassLoader 保存好`/tmp/UserController.java`之后,使用`mc`(Memory Compiler)命令来编译,并且通过`--classLoaderClass`参数指定ClassLoader
`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp` `mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp`{{execute T2}}
```bash ```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp $ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output: Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class /tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms Affect(row-cnt:1) cost in 346 ms
``` ```
也可以通过`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp`,使用`-c`参数指定ClassLoaderHash:
```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
```
### redefine ### redefine
再使用`redefine`命令重新加载新编译好的`UserController.class` 再使用`redefine`命令重新加载新编译好的`UserController.class`

@ -46,23 +46,33 @@ $ sc -d *UserController | grep classLoaderHash
It can be found that it is loaded by spring boot `LaunchedURLClassLoader@1be6f5c3`. It can be found that it is loaded by spring boot `LaunchedURLClassLoader@1be6f5c3`.
Please write down your classLoaderHash here, in the case here, it's `1be6f5c3`. It will be used in the future steps. Note that the hashcode changes, you need to check the current ClassLoader information first, and extract the hashcode corresponding to the ClassLoader.
Note: Please replace `<classLoaderHash>` with your classLoaderHash above, then execute the commands manually in the following steps: if you use`-c`, you have to manually type hashcode by `-c <hashcode>`.
For classloader with only one instance, it can be specified by `--classLoaderClass` using class name, which is more convenient to use.
The value of `--classloaderclass` is the class name of classloader. It can only work when it matches a unique classloader instance. The purpose is to facilitate the input of general commands. However, `-c <hashcode>` is dynamic.
### mc ### mc
After saving `/tmp/UserController.java`, compile with the `mc` (Memory Compiler) command and specify the ClassLoader with the `-c` option: After saving `/tmp/UserController.java`, compile with the `mc` (Memory Compiler) command and specify the ClassLoader with the `--classLoaderClass` option:
`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp` `mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp`{{execute T2}}
```bash ```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp $ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output: Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class /tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms Affect(row-cnt:1) cost in 346 ms
``` ```
You can also execute `mc -c <classLoaderHash> /tmp/UserController.java -d /tmp`using `-c` to specify ClassLoaderHash:
```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
```
### redefine ### redefine
Then reload the newly compiled `UserController.class` with the `redefine` command: Then reload the newly compiled `UserController.class` with the `redefine` command:

@ -8,6 +8,7 @@
|*class-pattern*|类名表达式匹配| |*class-pattern*|类名表达式匹配|
|`[c:]`|类所属 ClassLoader 的 hashcode| |`[c:]`|类所属 ClassLoader 的 hashcode|
|`[d:]`|设置类文件的目标目录| |`[d:]`|设置类文件的目标目录|
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|[E]|开启正则表达式匹配,默认为通配符匹配| |[E]|开启正则表达式匹配,默认为通配符匹配|
### 使用参考 ### 使用参考
@ -39,3 +40,29 @@ $ dump -d /tmp/output java.lang.String
null /tmp/output/java/lang/String.class null /tmp/output/java/lang/String.class
Affect(row-cnt:1) cost in 138 ms. Affect(row-cnt:1) cost in 138 ms.
``` ```
* 指定classLoader
注意hashcode是变化的需要先查看当前的ClassLoader信息提取对应ClassLoader的hashcode。
如果你使用`-c`你需要手动输入hashcode`-c <hashcode>`
```bash
$ dump -c 3d4eac69 demo.*
```
对于只有唯一实例的ClassLoader可以通过`--classLoaderClass`指定class name使用起来更加方便
`dump --classLoaderClass sun.misc.Launcher$AppClassLoader demo.*`{{execute T2}}
```bash
$ dump --classLoaderClass sun.misc.Launcher$AppClassLoader demo.*
HASHCODE CLASSLOADER LOCATION
3d4eac69 +-sun.misc.Launcher$AppClassLoader@3d4eac69 /Users/admin/logs/arthas/classdump/sun.misc.Launcher$AppClassLoader-3d4eac69/demo/MathGame.class
+-sun.misc.Launcher$ExtClassLoader@66350f69
Affect(row-cnt:1) cost in 39 ms.
```
* 注: 这里classLoaderClass 在 java 8 是 sun.misc.Launcher$AppClassLoader而java 11的classloader是jdk.internal.loader.ClassLoaders$AppClassLoaderkatacoda目前环境是java8。
`--classLoaderClass` 的值是ClassLoader的类名只有匹配到唯一的ClassLoader实例时才能工作目的是方便输入通用命令而`-c <hashcode>`是动态变化的。

@ -7,6 +7,7 @@
|---:|:---| |---:|:---|
|*class-pattern*|class name pattern| |*class-pattern*|class name pattern|
|`[c:]`|hashcode of the [class loader](classloader.md) that loaded the target class| |`[c:]`|hashcode of the [class loader](classloader.md) that loaded the target class|
|`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. |
|`[d:]`|set the destination directory for class files| |`[d:]`|set the destination directory for class files|
|`[E]`|turn on regex match, the default behavior is wild card match| |`[E]`|turn on regex match, the default behavior is wild card match|
@ -39,3 +40,29 @@ $ dump
null /tmp/output/java/lang/String.class null /tmp/output/java/lang/String.class
Affect(row-cnt:1) cost in 138 ms. Affect(row-cnt:1) cost in 138 ms.
``` ```
* Specify classLoader
Note that the hashcode changes, you need to check the current ClassLoader information first, and extract the hashcode corresponding to the ClassLoader.
if you use`-c`, you have to manually type hashcode by `-c <hashcode>`.
```bash
$ dump -c 3d4eac69 demo.*
```
For classloader with only one instance, it can be specified by `--classLoaderClass` using class name, which is more convenient to use.
`dump --classLoaderClass sun.misc.Launcher$AppClassLoader demo.*`{{execute T2}}
```bash
$ dump --classLoaderClass sun.misc.Launcher$AppClassLoader demo.*
HASHCODE CLASSLOADER LOCATION
3d4eac69 +-sun.misc.Launcher$AppClassLoader@3d4eac69 /Users/admin/logs/arthas/classdump/sun.misc.Launcher$AppClassLoader-3d4eac69/demo/MathGame.class
+-sun.misc.Launcher$ExtClassLoader@66350f69
Affect(row-cnt:1) cost in 39 ms.
```
* PS: Here the classLoaderClass in java 8 is sun.misc.Launcher$AppClassLoader, while in java 11 it's jdk.internal.loader.ClassLoaders$AppClassLoader. Currently katacoda using java 8.
The value of `--classloaderclass` is the class name of classloader. It can only work when it matches a unique classloader instance. The purpose is to facilitate the input of general commands. However, `-c <hashcode>` is dynamic.

@ -28,6 +28,24 @@ field: random
] ]
``` ```
* 指定classLoader
注意hashcode是变化的需要先查看当前的ClassLoader信息使用`sc -d <ClassName>`提取对应ClassLoader的hashcode。
如果你使用`-c`你需要手动输入hashcode`-c <hashcode>`
```bash
$ getstatic -c 3d4eac69 demo.MathGame random
```
对于只有唯一实例的ClassLoader可以通过`--classLoaderClass`指定class name使用起来更加方便
`getstatic --classLoaderClass sun.misc.Launcher$AppClassLoader demo.MathGame random`{{execute T2}}
* 注: 这里classLoaderClass 在 java 8 是 sun.misc.Launcher$AppClassLoader而java 11的classloader是jdk.internal.loader.ClassLoaders$AppClassLoaderkatacoda目前环境是java8。
`--classLoaderClass` 的值是ClassLoader的类名只有匹配到唯一的ClassLoader实例时才能工作目的是方便输入通用命令而`-c <hashcode>`是动态变化的。
如果该静态属性是一个复杂对象还可以支持在该属性上通过ognl表示进行遍历过滤访问对象的内部属性等操作。 如果该静态属性是一个复杂对象还可以支持在该属性上通过ognl表示进行遍历过滤访问对象的内部属性等操作。
* OGNL特殊用法请参考[https://github.com/alibaba/arthas/issues/71](https://github.com/alibaba/arthas/issues/71) * OGNL特殊用法请参考[https://github.com/alibaba/arthas/issues/71](https://github.com/alibaba/arthas/issues/71)

@ -27,6 +27,24 @@ field: random
] ]
``` ```
* Specify classLoader
Note that the hashcode changes, you need to check the current ClassLoader information first, and extract the hashcode corresponding to the ClassLoader using `sc -d <ClassName>`.
if you use`-c`, you have to manually type hashcode by `-c <hashcode>`.
```bash
$ getstatic -c 3d4eac69 demo.MathGame random
```
For classloader with only one instance, it can be specified by `--classLoaderClass` using class name, which is more convenient to use.
`getstatic --classLoaderClass demo.MathGame random`{{execute T2}}
* PS: Here the classLoaderClass in java 8 is sun.misc.Launcher$AppClassLoader, while in java 11 it's jdk.internal.loader.ClassLoaders$AppClassLoader. Currently katacoda using java 8.
The value of `--classloaderclass` is the class name of classloader. It can only work when it matches a unique classloader instance. The purpose is to facilitate the input of general commands. However, `-c <hashcode>` is dynamic.
Tip: if the static field is a complex class, you can even use [`OGNL`](https://commons.apache.org/proper/commons-ognl/language-guide.html) to traverse, filter and access the inner properties of this class. Tip: if the static field is a complex class, you can even use [`OGNL`](https://commons.apache.org/proper/commons-ognl/language-guide.html) to traverse, filter and access the inner properties of this class.

@ -12,6 +12,7 @@
|---:|:---| |---:|:---|
|*class-pattern*|类名表达式匹配| |*class-pattern*|类名表达式匹配|
|`[c:]`|类所属 ClassLoader 的 hashcode| |`[c:]`|类所属 ClassLoader 的 hashcode|
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|[E]|开启正则表达式匹配,默认为通配符匹配| |[E]|开启正则表达式匹配,默认为通配符匹配|
### 使用参考 ### 使用参考
@ -150,3 +151,6 @@ public class Logger extends Category
Affect(row-cnt:1) cost in 190 ms. Affect(row-cnt:1) cost in 190 ms.
``` ```
对于只有唯一实例的ClassLoader还可以通过`--classLoaderClass`指定class name使用起来更加方便
`--classLoaderClass` 的值是ClassLoader的类名只有匹配到唯一的ClassLoader实例时才能工作目的是方便输入通用命令而`-c <hashcode>`是动态变化的。

@ -12,6 +12,7 @@
|---:|:---| |---:|:---|
|*class-pattern*|pattern for the class name| |*class-pattern*|pattern for the class name|
|`[c:]`|hashcode of the class loader that loads the class| |`[c:]`|hashcode of the class loader that loads the class|
|`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. |
|`[E]`|turn on regex match while the default is wildcard match| |`[E]`|turn on regex match while the default is wildcard match|
### Usage ### Usage
@ -149,3 +150,7 @@ public class Logger extends Category
Affect(row-cnt:1) cost in 190 ms. Affect(row-cnt:1) cost in 190 ms.
``` ```
For classloader with only one instance, it can be specified by `--classLoaderClass` using class name, which is more convenient to use.
The value of `--classloaderclass` is the class name of classloader. It can only work when it matches a unique classloader instance. The purpose is to facilitate the input of general commands. However, `-c <hashcode>` is dynamic.

@ -115,10 +115,10 @@ update logger level success.
可以先用 `sc -d yourClassName` 来查看具体的 classloader hashcode然后在更新level时指定classloader 可以先用 `sc -d yourClassName` 来查看具体的 classloader hashcode然后在更新level时指定classloader
`logger -c <classLoaderHash> --name ROOT --level debug` `logger --classLoaderClass sun.misc.Launcher$AppClassLoader --name ROOT --level debug`{{execute T2}}
```bash ```bash
[arthas@2062]$ logger -c 2a139a55 --name ROOT --level debug [arthas@2062]$ logger --classLoaderClass sun.misc.Launcher$AppClassLoader --name ROOT --level debug
``` ```
#### 查看没有appender的logger的信息 #### 查看没有appender的logger的信息

@ -109,11 +109,11 @@ The value of `--classloaderclass` is the class name of classloader. It can only
update logger level success. update logger level success.
``` ```
#### 指定classloader更新 logger level #### Speecify classloader to update logger level
默认情况下logger命令会在SystemClassloader下执行如果应用是传统的`war`应用或者spring boot fat jar启动的应用那么需要指定classloader。 By defaultlogger command will be executed under SystemClassloader, if the application is a traditional `war`, or using spring boot fat jar, then it needs to specify classloader。
可以先用 `sc -d yourClassName` 来查看具体的 classloader hashcode然后在更新level时指定classloader You can first use `sc -d yourClassName` to check specified classloader hashcodethen specify classloader when updating logger level:
`logger --classLoaderClass sun.misc.Launcher$AppClassLoader --name ROOT --level debug`{{execute T2}} `logger --classLoaderClass sun.misc.Launcher$AppClassLoader --name ROOT --level debug`{{execute T2}}

@ -45,23 +45,33 @@ $ sc -d *UserController | grep classLoaderHash
可以发现是 spring boot `LaunchedURLClassLoader@1be6f5c3` 加载的。 可以发现是 spring boot `LaunchedURLClassLoader@1be6f5c3` 加载的。
请记下你的classLoaderHash后面需要使用它。在这里它是 `1be6f5c3` 注意hashcode是变化的需要先查看当前的ClassLoader信息提取对应ClassLoader的hashcode
注意请使用你的classLoaderHash值覆盖 `<classLoaderHash>` ,然后手动执行下面所有所述命令: 如果你使用`-c`你需要手动输入hashcode`-c <hashcode>`
对于只有唯一实例的ClassLoader可以通过`--classLoaderClass`指定class name使用起来更加方便.
`--classLoaderClass` 的值是ClassLoader的类名只有匹配到唯一的ClassLoader实例时才能工作目的是方便输入通用命令而`-c <hashcode>`是动态变化的。
### mc ### mc
保存好`/tmp/UserController.java`之后,使用`mc`(Memory Compiler)命令来编译,并且通过`-c`参数指定ClassLoader 保存好`/tmp/UserController.java`之后,使用`mc`(Memory Compiler)命令来编译,并且通过`--classLoaderClass`参数指定ClassLoader
`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp` `mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp`{{execute T2}}
```bash ```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp $ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output: Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class /tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms Affect(row-cnt:1) cost in 346 ms
``` ```
也可以通过`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp`,使用`-c`参数指定ClassLoaderHash:
```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
```
### redefine ### redefine
再使用`redefine`命令重新加载新编译好的`UserController.class` 再使用`redefine`命令重新加载新编译好的`UserController.class`

@ -2,6 +2,6 @@
> Memory Compiler/内存编译器,编译`.java`文件生成`.class`。 > Memory Compiler/内存编译器,编译`.java`文件生成`.class`。
可以通过`-c`参数指定classloader`-d`参数指定输出目录 可以通过`-c`/`--classLoaderClass`参数指定classloader`-d`参数指定输出目录
编译生成`.class`文件之后,可以结合`redefine`命令实现热更新代码。 编译生成`.class`文件之后,可以结合`redefine`命令实现热更新代码。

@ -23,6 +23,7 @@
|参数名称|参数说明| |参数名称|参数说明|
|---:|:---| |---:|:---|
|[c:]|ClassLoader的hashcode| |[c:]|ClassLoader的hashcode|
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|[p:]|外部的`.class`文件的完整路径,支持多个| |[p:]|外部的`.class`文件的完整路径,支持多个|

@ -46,23 +46,33 @@ $ sc -d *UserController | grep classLoaderHash
It can be found that it is loaded by spring boot `LaunchedURLClassLoader@1be6f5c3`. It can be found that it is loaded by spring boot `LaunchedURLClassLoader@1be6f5c3`.
Please write down your classLoaderHash here, in the case here, it's `1be6f5c3`. It will be used in the future steps. Note that the hashcode changes, you need to check the current ClassLoader information first, and extract the hashcode corresponding to the ClassLoader.
Note: Please replace `<classLoaderHash>` with your classLoaderHash above, then execute the commands manually in the following steps: if you use`-c`, you have to manually type hashcode by `-c <hashcode>`.
For classloader with only one instance, it can be specified by `--classLoaderClass` using class name, which is more convenient to use.
The value of `--classloaderclass` is the class name of classloader. It can only work when it matches a unique classloader instance. The purpose is to facilitate the input of general commands. However, `-c <hashcode>` is dynamic.
### mc ### mc
After saving `/tmp/UserController.java`, compile with the `mc` (Memory Compiler) command and specify the ClassLoader with the `-c` option: After saving `/tmp/UserController.java`, compile with the `mc` (Memory Compiler) command and specify the ClassLoader with the `--classLoaderClass` option:
`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp` `mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp`{{execute T2}}
```bash ```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp $ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output: Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class /tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms Affect(row-cnt:1) cost in 346 ms
``` ```
You can also execute `mc -c <classLoaderHash> /tmp/UserController.java -d /tmp`using `-c` to specify ClassLoaderHash:
```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
```
### redefine ### redefine
Then reload the newly compiled `UserController.class` with the `redefine` command: Then reload the newly compiled `UserController.class` with the `redefine` command:

@ -1,6 +1,6 @@
> Memory compiler, compiles `.java` files into `.class` files in memory. > Memory compiler, compiles `.java` files into `.class` files in memory.
The classloader can be specified with the `-c` option, the output directory can be specified with the `-d` option. The classloader can be specified with the `-c`/`--classLoaderClass` option, the output directory can be specified with the `-d` option.
After compiling the `.class` file, you can use the [redefine](redefine.md) command to re-define the loaded classes in JVM. After compiling the `.class` file, you can use the redefine command to re-define the loaded classes in JVM.

@ -24,6 +24,7 @@ Reference: [Instrumentation#redefineClasses](https://docs.oracle.com/javase/8/do
|Name|Specification| |Name|Specification|
|---:|:---| |---:|:---|
|`[c:]`|hashcode of the class loader| |`[c:]`|hashcode of the class loader|
|`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. |
|`[p:]`|absolute path of the external `*.class`, multiple paths are separated with 'space'| |`[p:]`|absolute path of the external `*.class`, multiple paths are separated with 'space'|
### Restrictions of the redefine command ### Restrictions of the redefine command

Loading…
Cancel
Save