mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
Add --classLoaderClass for dump/getstatic/jad/mc/redifine (#1447)
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -11,6 +11,7 @@ dump
|
||||
|---:|:---|
|
||||
|*class-pattern*|类名表达式匹配|
|
||||
|`[c:]`|类所属 ClassLoader 的 hashcode|
|
||||
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|
||||
|`[d:]`|设置类文件的目标目录|
|
||||
|[E]|开启正则表达式匹配,默认为通配符匹配|
|
||||
|
||||
@@ -37,3 +38,27 @@ $ dump -d /tmp/output java.lang.String
|
||||
null /tmp/output/java/lang/String.class
|
||||
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$AppClassLoader,katacoda目前环境是java8。
|
||||
|
||||
`--classLoaderClass` 的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而`-c <hashcode>`是动态变化的。
|
||||
|
||||
@@ -11,6 +11,7 @@ dump
|
||||
|---:|:---|
|
||||
|*class-pattern*|class name pattern|
|
||||
|`[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|
|
||||
|`[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
|
||||
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.
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ jad
|
||||
|---:|:---|
|
||||
|*class-pattern*|pattern for the class name|
|
||||
|`[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|
|
||||
|
||||
### Usage
|
||||
@@ -143,3 +144,7 @@ public class Logger extends Category
|
||||
|
||||
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.
|
||||
```
|
||||
|
||||
#### 指定classloader更新 logger level
|
||||
#### Speecify classloader to update logger level
|
||||
|
||||
默认情况下,logger命令会在SystemClassloader下执行,如果应用是传统的`war`应用,或者spring boot fat jar启动的应用,那么需要指定classloader。
|
||||
By default,logger 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 hashcode,then specify classloader when updating logger level:
|
||||
|
||||
```bash
|
||||
[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
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -28,6 +28,7 @@ Reference: [Instrumentation#redefineClasses](https://docs.oracle.com/javase/8/do
|
||||
|Name|Specification|
|
||||
|---:|:---|
|
||||
|`[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'|
|
||||
|
||||
|
||||
@@ -35,7 +36,8 @@ Reference: [Instrumentation#redefineClasses](https://docs.oracle.com/javase/8/do
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
@@ -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$AppClassLoader,katacoda目前环境是java8。
|
||||
|
||||
`--classLoaderClass` 的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而`-c <hashcode>`是动态变化的。
|
||||
|
||||
|
||||
如果该静态属性是一个复杂对象,还可以支持在该属性上通过ognl表示进行遍历,过滤,访问对象的内部属性等操作。
|
||||
|
||||
* OGNL特殊用法请参考:[https://github.com/alibaba/arthas/issues/71](https://github.com/alibaba/arthas/issues/71)
|
||||
|
||||
@@ -16,6 +16,7 @@ jad
|
||||
|---:|:---|
|
||||
|*class-pattern*|类名表达式匹配|
|
||||
|`[c:]`|类所属 ClassLoader 的 hashcode|
|
||||
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|
||||
|[E]|开启正则表达式匹配,默认为通配符匹配|
|
||||
|
||||
### 使用参考
|
||||
@@ -144,3 +145,6 @@ public class Logger extends Category
|
||||
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
|
||||
```
|
||||
|
||||
也可以通过`--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`命令指定输出目录:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -27,6 +27,7 @@ redefine
|
||||
|参数名称|参数说明|
|
||||
|---:|:---|
|
||||
|[c:]|ClassLoader的hashcode|
|
||||
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|
||||
|[p:]|外部的`.class`文件的完整路径,支持多个|
|
||||
|
||||
|
||||
@@ -36,6 +37,7 @@ redefine
|
||||
```bash
|
||||
redefine /tmp/Test.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 命令使用
|
||||
|
||||
@@ -47,21 +47,25 @@ $ sc -d *UserController | grep classLoaderHash
|
||||
|
||||
请记下你的classLoaderHash,后面需要使用它。在这里,它是 `1be6f5c3`。
|
||||
|
||||
注意:请使用你的classLoaderHash值覆盖 `<classLoaderHash>` ,然后手动执行这些命令:
|
||||
|
||||
### 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
|
||||
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
|
||||
$ 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
|
||||
```
|
||||
|
||||
也可以通过`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp`,使用`-c`参数指定ClassLoaderHash:
|
||||
|
||||
```bash
|
||||
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
|
||||
```
|
||||
|
||||
### redefine
|
||||
|
||||
再使用`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.
|
||||
|
||||
Note: Please replace `<classLoaderHash>` with your classLoaderHash above, then execute the related commands manually in the following steps:
|
||||
|
||||
### 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
|
||||
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
|
||||
$ 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
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
Then reload the newly compiled `UserController.class` with the `redefine` command:
|
||||
|
||||
@@ -45,23 +45,33 @@ $ sc -d *UserController | grep classLoaderHash
|
||||
|
||||
可以发现是 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
|
||||
|
||||
保存好`/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
|
||||
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
|
||||
$ 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
|
||||
```
|
||||
|
||||
也可以通过`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp`,使用`-c`参数指定ClassLoaderHash:
|
||||
|
||||
```bash
|
||||
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
|
||||
```
|
||||
|
||||
### redefine
|
||||
|
||||
再使用`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`.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
|
||||
$ 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
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
Then reload the newly compiled `UserController.class` with the `redefine` command:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|*class-pattern*|类名表达式匹配|
|
||||
|`[c:]`|类所属 ClassLoader 的 hashcode|
|
||||
|`[d:]`|设置类文件的目标目录|
|
||||
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|
||||
|[E]|开启正则表达式匹配,默认为通配符匹配|
|
||||
|
||||
### 使用参考
|
||||
@@ -39,3 +40,29 @@ $ dump -d /tmp/output java.lang.String
|
||||
null /tmp/output/java/lang/String.class
|
||||
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$AppClassLoader,katacoda目前环境是java8。
|
||||
|
||||
`--classLoaderClass` 的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而`-c <hashcode>`是动态变化的。
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|---:|:---|
|
||||
|*class-pattern*|class name pattern|
|
||||
|`[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|
|
||||
|`[E]`|turn on regex match, the default behavior is wild card match|
|
||||
|
||||
@@ -39,3 +40,29 @@ $ dump
|
||||
null /tmp/output/java/lang/String.class
|
||||
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$AppClassLoader,katacoda目前环境是java8。
|
||||
|
||||
`--classLoaderClass` 的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而`-c <hashcode>`是动态变化的。
|
||||
|
||||
如果该静态属性是一个复杂对象,还可以支持在该属性上通过ognl表示进行遍历,过滤,访问对象的内部属性等操作。
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|---:|:---|
|
||||
|*class-pattern*|类名表达式匹配|
|
||||
|`[c:]`|类所属 ClassLoader 的 hashcode|
|
||||
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|
||||
|[E]|开启正则表达式匹配,默认为通配符匹配|
|
||||
|
||||
### 使用参考
|
||||
@@ -150,3 +151,6 @@ public class Logger extends Category
|
||||
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|
|
||||
|`[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|
|
||||
|
||||
### Usage
|
||||
@@ -149,3 +150,7 @@ public class Logger extends Category
|
||||
|
||||
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:
|
||||
|
||||
`logger -c <classLoaderHash> --name ROOT --level debug`
|
||||
`logger --classLoaderClass sun.misc.Launcher$AppClassLoader --name ROOT --level debug`{{execute T2}}
|
||||
|
||||
```bash
|
||||
[arthas@2062]$ logger -c 2a139a55 --name ROOT --level debug
|
||||
[arthas@2062]$ logger --classLoaderClass sun.misc.Launcher$AppClassLoader --name ROOT --level debug
|
||||
```
|
||||
|
||||
#### 查看没有appender的logger的信息
|
||||
|
||||
@@ -109,11 +109,11 @@ The value of `--classloaderclass` is the class name of classloader. It can only
|
||||
update logger level success.
|
||||
```
|
||||
|
||||
#### 指定classloader更新 logger level
|
||||
#### Speecify classloader to update logger level
|
||||
|
||||
默认情况下,logger命令会在SystemClassloader下执行,如果应用是传统的`war`应用,或者spring boot fat jar启动的应用,那么需要指定classloader。
|
||||
By default,logger 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 hashcode,then specify classloader when updating logger level:
|
||||
|
||||
`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` 加载的。
|
||||
|
||||
请记下你的classLoaderHash,后面需要使用它。在这里,它是 `1be6f5c3`。
|
||||
注意hashcode是变化的,需要先查看当前的ClassLoader信息,提取对应ClassLoader的hashcode。
|
||||
|
||||
注意:请使用你的classLoaderHash值覆盖 `<classLoaderHash>` ,然后手动执行下面所有所述命令:
|
||||
如果你使用`-c`,你需要手动输入hashcode:`-c <hashcode>`
|
||||
|
||||
对于只有唯一实例的ClassLoader可以通过`--classLoaderClass`指定class name,使用起来更加方便.
|
||||
|
||||
`--classLoaderClass` 的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而`-c <hashcode>`是动态变化的。
|
||||
|
||||
### 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
|
||||
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
|
||||
$ 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
|
||||
```
|
||||
|
||||
也可以通过`mc -c <classLoaderHash> /tmp/UserController.java -d /tmp`,使用`-c`参数指定ClassLoaderHash:
|
||||
|
||||
```bash
|
||||
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
|
||||
```
|
||||
|
||||
### redefine
|
||||
|
||||
再使用`redefine`命令重新加载新编译好的`UserController.class`:
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
> Memory Compiler/内存编译器,编译`.java`文件生成`.class`。
|
||||
|
||||
|
||||
可以通过`-c`参数指定classloader,`-d`参数指定输出目录
|
||||
可以通过`-c`/`--classLoaderClass`参数指定classloader,`-d`参数指定输出目录
|
||||
|
||||
编译生成`.class`文件之后,可以结合`redefine`命令实现热更新代码。
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|参数名称|参数说明|
|
||||
|---:|:---|
|
||||
|[c:]|ClassLoader的hashcode|
|
||||
|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name|
|
||||
|[p:]|外部的`.class`文件的完整路径,支持多个|
|
||||
|
||||
|
||||
|
||||
@@ -46,23 +46,33 @@ $ sc -d *UserController | grep classLoaderHash
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
|
||||
$ 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
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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|
|
||||
|---:|:---|
|
||||
|`[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'|
|
||||
|
||||
### Restrictions of the redefine command
|
||||
|
||||
Reference in New Issue
Block a user