Commit 5d7c4150 authored by shengnan hu's avatar shengnan hu
Browse files

init

parents
Pipeline #6771 failed with stage
in 4 seconds
package com.taobao.arthas.core.command.klass100;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.ClassDetailVO;
import com.taobao.arthas.core.command.model.ClassLoaderModel;
import com.taobao.arthas.core.command.model.ClassLoaderVO;
import com.taobao.arthas.core.command.model.ClassSetVO;
import com.taobao.arthas.core.command.model.MessageModel;
import com.taobao.arthas.core.command.model.RowAffectModel;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.shell.handlers.Handler;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.arthas.core.util.ClassLoaderUtils;
import com.taobao.arthas.core.util.ResultUtils;
import com.taobao.arthas.core.util.affect.RowAffect;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import java.lang.instrument.Instrumentation;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
@Name("classloader")
@Summary("Show classloader info")
@Description(Constants.EXAMPLE +
" classloader\n" +
" classloader -t\n" +
" classloader -l\n" +
" classloader -c 327a647b\n" +
" classloader -c 327a647b -r META-INF/MANIFEST.MF\n" +
" classloader -a\n" +
" classloader -a -c 327a647b\n" +
" classloader -c 659e0bfd --load demo.MathGame\n" +
" classloader -u # url statistics\n" +
Constants.WIKI + Constants.WIKI_HOME + "classloader")
public class ClassLoaderCommand extends AnnotatedCommand {
private Logger logger = LoggerFactory.getLogger(ClassLoaderCommand.class);
private boolean isTree = false;
private String hashCode;
private String classLoaderClass;
private boolean all = false;
private String resource;
private boolean includeReflectionClassLoader = true;
private boolean listClassLoader = false;
private boolean urlStat = false;
private String loadClass = null;
private volatile boolean isInterrupted = false;
@Option(shortName = "t", longName = "tree", flag = true)
@Description("Display ClassLoader tree")
public void setTree(boolean tree) {
isTree = tree;
}
@Option(longName = "classLoaderClass")
@Description("The class name of the special class's classLoader.")
public void setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
}
@Option(shortName = "c", longName = "classloader")
@Description("The hash code of the special ClassLoader")
public void setHashCode(String hashCode) {
this.hashCode = hashCode;
}
@Option(shortName = "a", longName = "all", flag = true)
@Description("Display all classes loaded by ClassLoader")
public void setAll(boolean all) {
this.all = all;
}
@Option(shortName = "r", longName = "resource")
@Description("Use ClassLoader to find resources, won't work without -c specified")
public void setResource(String resource) {
this.resource = resource;
}
@Option(shortName = "i", longName = "include-reflection-classloader", flag = true)
@Description("Include sun.reflect.DelegatingClassLoader")
public void setIncludeReflectionClassLoader(boolean includeReflectionClassLoader) {
this.includeReflectionClassLoader = includeReflectionClassLoader;
}
@Option(shortName = "l", longName = "list-classloader", flag = true)
@Description("Display statistics info by classloader instance")
public void setListClassLoader(boolean listClassLoader) {
this.listClassLoader = listClassLoader;
}
@Option(longName = "load")
@Description("Use ClassLoader to load class, won't work without -c specified")
public void setLoadClass(String className) {
this.loadClass = className;
}
@Option(shortName = "u", longName = "url-stat", flag = true)
@Description("Display classloader url statistics")
public void setUrlStat(boolean urlStat) {
this.urlStat = urlStat;
}
@Override
public void process(CommandProcess process) {
// ctrl-C support
process.interruptHandler(new ClassLoaderInterruptHandler(this));
ClassLoader targetClassLoader = null;
boolean classLoaderSpecified = false;
Instrumentation inst = process.session().getInstrumentation();
if (urlStat) {
Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats = this.urlStats(inst);
ClassLoaderModel model = new ClassLoaderModel();
model.setUrlStats(urlStats);
process.appendResult(model);
process.end();
return;
}
if (hashCode != null || classLoaderClass != null) {
classLoaderSpecified = true;
}
if (hashCode != null) {
Set<ClassLoader> allClassLoader = getAllClassLoaders(inst);
for (ClassLoader cl : allClassLoader) {
if (Integer.toHexString(cl.hashCode()).equals(hashCode)) {
targetClassLoader = cl;
break;
}
}
} else if (classLoaderClass != null) {
List<ClassLoader> matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass);
if (matchedClassLoaders.size() == 1) {
targetClassLoader = matchedClassLoaders.get(0);
} else if (matchedClassLoaders.size() > 1) {
Collection<ClassLoaderVO> classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders);
ClassLoaderModel classloaderModel = new ClassLoaderModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(classloaderModel);
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;
}
}
if (all) {
processAllClasses(process, inst);
} else if (classLoaderSpecified && resource != null) {
processResources(process, inst, targetClassLoader);
} else if (classLoaderSpecified && this.loadClass != null) {
processLoadClass(process, inst, targetClassLoader);
} else if (classLoaderSpecified) {
processClassLoader(process, inst, targetClassLoader);
} else if (listClassLoader || isTree){
processClassLoaders(process, inst);
} else {
processClassLoaderStats(process, inst);
}
}
/**
* Calculate classloader statistics.
* e.g. In JVM, there are 100 GrooyClassLoader instances, which loaded 200 classes in total
* @param process
* @param inst
*/
private void processClassLoaderStats(CommandProcess process, Instrumentation inst) {
RowAffect affect = new RowAffect();
List<ClassLoaderInfo> classLoaderInfos = getAllClassLoaderInfo(inst);
Map<String, ClassLoaderStat> classLoaderStats = new HashMap<String, ClassLoaderStat>();
for (ClassLoaderInfo info: classLoaderInfos) {
String name = info.classLoader == null ? "BootstrapClassLoader" : info.classLoader.getClass().getName();
ClassLoaderStat stat = classLoaderStats.get(name);
if (null == stat) {
stat = new ClassLoaderStat();
classLoaderStats.put(name, stat);
}
stat.addLoadedCount(info.loadedClassCount);
stat.addNumberOfInstance(1);
}
// sort the map by value
TreeMap<String, ClassLoaderStat> sorted =
new TreeMap<String, ClassLoaderStat>(new ValueComparator(classLoaderStats));
sorted.putAll(classLoaderStats);
process.appendResult(new ClassLoaderModel().setClassLoaderStats(sorted));
affect.rCnt(sorted.keySet().size());
process.appendResult(new RowAffectModel(affect));
process.end();
}
private void processClassLoaders(CommandProcess process, Instrumentation inst) {
RowAffect affect = new RowAffect();
List<ClassLoaderInfo> classLoaderInfos = includeReflectionClassLoader ? getAllClassLoaderInfo(inst) :
getAllClassLoaderInfo(inst, new SunReflectionClassLoaderFilter());
List<ClassLoaderVO> classLoaderVOs = new ArrayList<ClassLoaderVO>(classLoaderInfos.size());
for (ClassLoaderInfo classLoaderInfo : classLoaderInfos) {
ClassLoaderVO classLoaderVO = ClassUtils.createClassLoaderVO(classLoaderInfo.classLoader);
classLoaderVO.setLoadedCount(classLoaderInfo.loadedClassCount());
classLoaderVOs.add(classLoaderVO);
}
if (isTree){
classLoaderVOs = processClassLoaderTree(classLoaderVOs);
}
process.appendResult(new ClassLoaderModel().setClassLoaders(classLoaderVOs).setTree(isTree));
affect.rCnt(classLoaderInfos.size());
process.appendResult(new RowAffectModel(affect));
process.end();
}
// 根据 ClassLoader 来打印URLClassLoader的urls
private void processClassLoader(CommandProcess process, Instrumentation inst, ClassLoader targetClassLoader) {
RowAffect affect = new RowAffect();
if (targetClassLoader != null) {
if (targetClassLoader instanceof URLClassLoader) {
List<String> classLoaderUrls = getClassLoaderUrls(targetClassLoader);
affect.rCnt(classLoaderUrls.size());
if (classLoaderUrls.isEmpty()) {
process.appendResult(new MessageModel("urls is empty."));
} else {
process.appendResult(new ClassLoaderModel().setUrls(classLoaderUrls));
affect.rCnt(classLoaderUrls.size());
}
} else {
process.appendResult(new MessageModel("not a URLClassLoader."));
}
}
process.appendResult(new RowAffectModel(affect));
process.end();
}
// 使用ClassLoader去getResources
private void processResources(CommandProcess process, Instrumentation inst, ClassLoader targetClassLoader) {
RowAffect affect = new RowAffect();
int rowCount = 0;
List<String> resources = new ArrayList<String>();
if (targetClassLoader != null) {
try {
Enumeration<URL> urls = targetClassLoader.getResources(resource);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
resources.add(url.toString());
rowCount++;
}
} catch (Throwable e) {
logger.warn("get resource failed, resource: {}", resource, e);
}
}
affect.rCnt(rowCount);
process.appendResult(new ClassLoaderModel().setResources(resources));
process.appendResult(new RowAffectModel(affect));
process.end();
}
// Use ClassLoader to loadClass
private void processLoadClass(CommandProcess process, Instrumentation inst, ClassLoader targetClassLoader) {
if (targetClassLoader != null) {
try {
Class<?> clazz = targetClassLoader.loadClass(this.loadClass);
process.appendResult(new MessageModel("load class success."));
ClassDetailVO classInfo = ClassUtils.createClassInfo(clazz, false, null);
process.appendResult(new ClassLoaderModel().setLoadClass(classInfo));
} catch (Throwable e) {
logger.warn("load class error, class: {}", this.loadClass, e);
process.end(-1, "load class error, class: "+this.loadClass+", error: "+e.toString());
return;
}
}
process.end();
}
private void processAllClasses(CommandProcess process, Instrumentation inst) {
RowAffect affect = new RowAffect();
getAllClasses(hashCode, inst, affect, process);
if (checkInterrupted(process)) {
return;
}
process.appendResult(new RowAffectModel(affect));
process.end();
}
/**
* 获取到所有的class, 还有它们的classloader,按classloader归类好,统一输出每个classloader里有哪些class
* <p>
* 当hashCode是null,则把所有的classloader的都打印
*
*/
@SuppressWarnings("rawtypes")
private void getAllClasses(String hashCode, Instrumentation inst, RowAffect affect, CommandProcess process) {
int hashCodeInt = -1;
if (hashCode != null) {
hashCodeInt = Integer.valueOf(hashCode, 16);
}
SortedSet<Class<?>> bootstrapClassSet = new TreeSet<Class<?>>(new Comparator<Class>() {
@Override
public int compare(Class o1, Class o2) {
return o1.getName().compareTo(o2.getName());
}
});
Class[] allLoadedClasses = inst.getAllLoadedClasses();
Map<ClassLoader, SortedSet<Class<?>>> classLoaderClassMap = new HashMap<ClassLoader, SortedSet<Class<?>>>();
for (Class clazz : allLoadedClasses) {
ClassLoader classLoader = clazz.getClassLoader();
// Class loaded by BootstrapClassLoader
if (classLoader == null) {
if (hashCode == null) {
bootstrapClassSet.add(clazz);
}
continue;
}
if (hashCode != null && classLoader.hashCode() != hashCodeInt) {
continue;
}
SortedSet<Class<?>> classSet = classLoaderClassMap.get(classLoader);
if (classSet == null) {
classSet = new TreeSet<Class<?>>(new Comparator<Class<?>>() {
@Override
public int compare(Class<?> o1, Class<?> o2) {
return o1.getName().compareTo(o2.getName());
}
});
classLoaderClassMap.put(classLoader, classSet);
}
classSet.add(clazz);
}
// output bootstrapClassSet
int pageSize = 256;
processClassSet(process, ClassUtils.createClassLoaderVO(null), bootstrapClassSet, pageSize, affect);
// output other classSet
for (Entry<ClassLoader, SortedSet<Class<?>>> entry : classLoaderClassMap.entrySet()) {
if (checkInterrupted(process)) {
return;
}
ClassLoader classLoader = entry.getKey();
SortedSet<Class<?>> classSet = entry.getValue();
processClassSet(process, ClassUtils.createClassLoaderVO(classLoader), classSet, pageSize, affect);
}
}
private void processClassSet(final CommandProcess process, final ClassLoaderVO classLoaderVO, Collection<Class<?>> classes, int pageSize, final RowAffect affect) {
//分批输出classNames, Ctrl+C可以中断执行
ResultUtils.processClassNames(classes, pageSize, new ResultUtils.PaginationHandler<List<String>>() {
@Override
public boolean handle(List<String> classNames, int segment) {
process.appendResult(new ClassLoaderModel().setClassSet(new ClassSetVO(classLoaderVO, classNames, segment)));
affect.rCnt(classNames.size());
return !checkInterrupted(process);
}
});
}
private boolean checkInterrupted(CommandProcess process) {
if (!process.isRunning()) {
return true;
}
if(isInterrupted){
process.end(-1, "Processing has been interrupted");
return true;
} else {
return false;
}
}
private static List<String> getClassLoaderUrls(ClassLoader classLoader) {
List<String> urlStrs = new ArrayList<String>();
if (classLoader instanceof URLClassLoader) {
URLClassLoader cl = (URLClassLoader) classLoader;
URL[] urls = cl.getURLs();
if (urls != null) {
for (URL url : urls) {
urlStrs.add(url.toString());
}
}
}
return urlStrs;
}
private Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats(Instrumentation inst) {
Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats = new HashMap<ClassLoaderVO, ClassLoaderUrlStat>();
Map<ClassLoader, Set<String>> usedUrlsMap = new HashMap<ClassLoader, Set<String>>();
for (Class<?> clazz : inst.getAllLoadedClasses()) {
ClassLoader classLoader = clazz.getClassLoader();
if (classLoader != null) {
ProtectionDomain protectionDomain = clazz.getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
if (codeSource != null) {
URL location = codeSource.getLocation();
if (location != null) {
Set<String> urls = usedUrlsMap.get(classLoader);
if (urls == null) {
urls = new HashSet<String>();
usedUrlsMap.put(classLoader, urls);
}
urls.add(location.toString());
}
}
}
}
for (Entry<ClassLoader, Set<String>> entry : usedUrlsMap.entrySet()) {
ClassLoader loader = entry.getKey();
Set<String> usedUrls = entry.getValue();
List<String> allUrls = getClassLoaderUrls(loader);
List<String> unusedUrls = new ArrayList<String>();
for (String url : allUrls) {
if (!usedUrls.contains(url)) {
unusedUrls.add(url);
}
}
urlStats.put(ClassUtils.createClassLoaderVO(loader), new ClassLoaderUrlStat(usedUrls, unusedUrls));
}
return urlStats;
}
// 以树状列出ClassLoader的继承结构
private static List<ClassLoaderVO> processClassLoaderTree(List<ClassLoaderVO> classLoaders) {
List<ClassLoaderVO> rootClassLoaders = new ArrayList<ClassLoaderVO>();
List<ClassLoaderVO> parentNotNullClassLoaders = new ArrayList<ClassLoaderVO>();
for (ClassLoaderVO classLoaderVO : classLoaders) {
if (classLoaderVO.getParent() == null) {
rootClassLoaders.add(classLoaderVO);
} else {
parentNotNullClassLoaders.add(classLoaderVO);
}
}
for (ClassLoaderVO classLoaderVO : rootClassLoaders) {
buildTree(classLoaderVO, parentNotNullClassLoaders);
}
return rootClassLoaders;
}
private static void buildTree(ClassLoaderVO parent, List<ClassLoaderVO> parentNotNullClassLoaders) {
for (ClassLoaderVO classLoaderVO : parentNotNullClassLoaders) {
if (parent.getName().equals(classLoaderVO.getParent())){
parent.addChild(classLoaderVO);
buildTree(classLoaderVO, parentNotNullClassLoaders);
}
}
}
private static Set<ClassLoader> getAllClassLoaders(Instrumentation inst, Filter... filters) {
Set<ClassLoader> classLoaderSet = new HashSet<ClassLoader>();
for (Class<?> clazz : inst.getAllLoadedClasses()) {
ClassLoader classLoader = clazz.getClassLoader();
if (classLoader != null) {
if (shouldInclude(classLoader, filters)) {
classLoaderSet.add(classLoader);
}
}
}
return classLoaderSet;
}
private static List<ClassLoaderInfo> getAllClassLoaderInfo(Instrumentation inst, Filter... filters) {
// 这里认为class.getClassLoader()返回是null的是由BootstrapClassLoader加载的,特殊处理
ClassLoaderInfo bootstrapInfo = new ClassLoaderInfo(null);
Map<ClassLoader, ClassLoaderInfo> loaderInfos = new HashMap<ClassLoader, ClassLoaderInfo>();
for (Class<?> clazz : inst.getAllLoadedClasses()) {
ClassLoader classLoader = clazz.getClassLoader();
if (classLoader == null) {
bootstrapInfo.increase();
} else {
if (shouldInclude(classLoader, filters)) {
ClassLoaderInfo loaderInfo = loaderInfos.get(classLoader);
if (loaderInfo == null) {
loaderInfo = new ClassLoaderInfo(classLoader);
loaderInfos.put(classLoader, loaderInfo);
ClassLoader parent = classLoader.getParent();
while (parent != null) {
ClassLoaderInfo parentLoaderInfo = loaderInfos.get(parent);
if (parentLoaderInfo == null) {
parentLoaderInfo = new ClassLoaderInfo(parent);
loaderInfos.put(parent, parentLoaderInfo);
}
parent = parent.getParent();
}
}
loaderInfo.increase();
}
}
}
// 排序时,把用户自己定的ClassLoader排在最前面,以sun.
// 开头的放后面,因为sun.reflect.DelegatingClassLoader的实例太多
List<ClassLoaderInfo> sunClassLoaderList = new ArrayList<ClassLoaderInfo>();
List<ClassLoaderInfo> otherClassLoaderList = new ArrayList<ClassLoaderInfo>();
for (Entry<ClassLoader, ClassLoaderInfo> entry : loaderInfos.entrySet()) {
ClassLoader classLoader = entry.getKey();
if (classLoader.getClass().getName().startsWith("sun.")) {
sunClassLoaderList.add(entry.getValue());
} else {
otherClassLoaderList.add(entry.getValue());
}
}
Collections.sort(sunClassLoaderList);
Collections.sort(otherClassLoaderList);
List<ClassLoaderInfo> result = new ArrayList<ClassLoaderInfo>();
result.add(bootstrapInfo);
result.addAll(otherClassLoaderList);
result.addAll(sunClassLoaderList);
return result;
}
private static boolean shouldInclude(ClassLoader classLoader, Filter... filters) {
if (filters == null) {
return true;
}
for (Filter filter : filters) {
if (!filter.accept(classLoader)) {
return false;
}
}
return true;
}
private static class ClassLoaderInfo implements Comparable<ClassLoaderInfo> {
private ClassLoader classLoader;
private int loadedClassCount = 0;
ClassLoaderInfo(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public String getName() {
if (classLoader != null) {
return classLoader.toString();
}
return "BootstrapClassLoader";
}
String hashCodeStr() {
if (classLoader != null) {
return "" + Integer.toHexString(classLoader.hashCode());
}
return "null";
}
void increase() {
loadedClassCount++;
}
int loadedClassCount() {
return loadedClassCount;
}
ClassLoader parent() {
return classLoader == null ? null : classLoader.getParent();
}
String parentStr() {
if (classLoader == null) {
return "null";
}
ClassLoader parent = classLoader.getParent();
if (parent == null) {
return "null";
}
return parent.toString();
}
@Override
public int compareTo(ClassLoaderInfo other) {
if (other == null) {
return -1;
}
if (other.classLoader == null) {
return -1;
}
if (this.classLoader == null) {
return -1;
}
return this.classLoader.getClass().getName().compareTo(other.classLoader.getClass().getName());
}
}
private interface Filter {
boolean accept(ClassLoader classLoader);
}
private static class SunReflectionClassLoaderFilter implements Filter {
private static final List<String> REFLECTION_CLASSLOADERS = Arrays.asList("sun.reflect.DelegatingClassLoader",
"jdk.internal.reflect.DelegatingClassLoader");
@Override
public boolean accept(ClassLoader classLoader) {
return !REFLECTION_CLASSLOADERS.contains(classLoader.getClass().getName());
}
}
public static class ClassLoaderUrlStat {
private Collection<String> usedUrls;
private Collection<String> unUsedUrls;
public ClassLoaderUrlStat() {
}
public ClassLoaderUrlStat(Collection<String> usedUrls, Collection<String> unUsedUrls) {
super();
this.usedUrls = usedUrls;
this.unUsedUrls = unUsedUrls;
}
public Collection<String> getUsedUrls() {
return usedUrls;
}
public void setUsedUrls(Collection<String> usedUrls) {
this.usedUrls = usedUrls;
}
public Collection<String> getUnUsedUrls() {
return unUsedUrls;
}
public void setUnUsedUrls(Collection<String> unUsedUrls) {
this.unUsedUrls = unUsedUrls;
}
}
public static class ClassLoaderStat {
private int loadedCount;
private int numberOfInstance;
void addLoadedCount(int count) {
this.loadedCount += count;
}
void addNumberOfInstance(int count) {
this.numberOfInstance += count;
}
public int getLoadedCount() {
return loadedCount;
}
public int getNumberOfInstance() {
return numberOfInstance;
}
}
private static class ValueComparator implements Comparator<String> {
private Map<String, ClassLoaderStat> unsortedStats;
ValueComparator(Map<String, ClassLoaderStat> stats) {
this.unsortedStats = stats;
}
@Override
public int compare(String o1, String o2) {
if (null == unsortedStats) {
return -1;
}
if (!unsortedStats.containsKey(o1)) {
return 1;
}
if (!unsortedStats.containsKey(o2)) {
return -1;
}
return unsortedStats.get(o2).getLoadedCount() - unsortedStats.get(o1).getLoadedCount();
}
}
private static class ClassLoaderInterruptHandler implements Handler<Void> {
private ClassLoaderCommand command;
public ClassLoaderInterruptHandler(ClassLoaderCommand command) {
this.command = command;
}
@Override
public void handle(Void event) {
command.isInterrupted = true;
}
}
}
package com.taobao.arthas.core.command.klass100;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.ClassVO;
import com.taobao.arthas.core.command.model.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;
import com.taobao.arthas.core.command.model.RowAffectModel;
import com.taobao.arthas.core.shell.cli.Completion;
import com.taobao.arthas.core.shell.cli.CompletionUtils;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.shell.command.ExitStatus;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.arthas.core.util.ClassLoaderUtils;
import com.taobao.arthas.core.util.CommandUtils;
import com.taobao.arthas.core.util.InstrumentationUtils;
import com.taobao.arthas.core.util.SearchUtils;
import com.taobao.arthas.core.util.affect.RowAffect;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.DefaultValue;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
/**
* Dump class byte array
*/
@Name("dump")
@Summary("Dump class byte array from JVM")
@Description(Constants.EXAMPLE +
" dump java.lang.String\n" +
" dump -d /tmp/output java.lang.String\n" +
" dump org/apache/commons/lang/StringUtils\n" +
" dump *StringUtils\n" +
" dump -E org\\\\.apache\\\\.commons\\\\.lang\\\\.StringUtils\n" +
Constants.WIKI + Constants.WIKI_HOME + "dump")
public class DumpClassCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(DumpClassCommand.class);
private String classPattern;
private String code = null;
private String classLoaderClass;
private boolean isRegEx = false;
private String directory;
private int limit;
@Argument(index = 0, argName = "class-pattern")
@Description("Class name pattern, use either '.' or '/' as separator")
public void setClassPattern(String classPattern) {
this.classPattern = classPattern;
}
@Option(shortName = "c", longName = "code")
@Description("The hash code of the special class's classLoader")
public void setCode(String 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)
@Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) {
isRegEx = regEx;
}
@Option(shortName = "d", longName = "directory")
@Description("Sets the destination directory for class files")
public void setDirectory(String directory) {
this.directory = directory;
}
@Option(shortName = "l", longName = "limit")
@Description("The limit of dump classes size, default value is 50")
@DefaultValue("50")
public void setLimit(int limit) {
this.limit = limit;
}
@Override
public void process(CommandProcess process) {
RowAffect effect = new RowAffect();
try {
if (directory != null) {
File dir = new File(directory);
if (dir.isFile()) {
process.end(-1, directory + " :is not a directory, please check it");
return;
}
}
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);
} else if (matchedClasses.size() > limit) {
status = processMatches(process, matchedClasses);
} else {
status = processMatch(process, effect, inst, matchedClasses);
}
process.appendResult(new RowAffectModel(effect));
CommandUtils.end(process, status);
} catch (Throwable e){
logger.error("processing error", e);
process.end(-1, "processing error");
}
}
@Override
public void complete(Completion completion) {
if (!CompletionUtils.completeClassName(completion)) {
super.complete(completion);
}
}
private ExitStatus processMatch(CommandProcess process, RowAffect effect, Instrumentation inst, Set<Class<?>> matchedClasses) {
try {
Map<Class<?>, File> classFiles = dump(inst, matchedClasses);
List<DumpClassVO> dumpedClasses = new ArrayList<DumpClassVO>(classFiles.size());
for (Map.Entry<Class<?>, File> entry : classFiles.entrySet()) {
Class<?> clazz = entry.getKey();
File file = entry.getValue();
DumpClassVO dumpClassVO = new DumpClassVO();
dumpClassVO.setLocation(file.getCanonicalPath());
ClassUtils.fillSimpleClassVO(clazz, dumpClassVO);
dumpedClasses.add(dumpClassVO);
}
process.appendResult(new DumpClassModel().setDumpedClasses(dumpedClasses));
effect.rCnt(classFiles.keySet().size());
return ExitStatus.success();
} catch (Throwable t) {
logger.error("dump: fail to dump classes: " + matchedClasses, t);
return ExitStatus.failure(-1, "dump: fail to dump classes: " + matchedClasses);
}
}
private ExitStatus processMatches(CommandProcess process, Set<Class<?>> matchedClasses) {
String msg = String.format(
"Found more than %d class for: %s, Please Try to specify the classloader with the -c option, or try to use --limit option.",
limit, classPattern);
process.appendResult(new MessageModel(msg));
List<ClassVO> classVOs = ClassUtils.createClassVOList(matchedClasses);
process.appendResult(new DumpClassModel().setMatchedClasses(classVOs));
return ExitStatus.failure(-1, msg);
}
private ExitStatus processNoMatch(CommandProcess process) {
return ExitStatus.failure(-1, "No class found for: " + classPattern);
}
private Map<Class<?>, File> dump(Instrumentation inst, Set<Class<?>> classes) throws UnmodifiableClassException {
ClassDumpTransformer transformer = null;
if (directory != null) {
transformer = new ClassDumpTransformer(classes, new File(directory));
} else {
transformer = new ClassDumpTransformer(classes);
}
InstrumentationUtils.retransformClasses(inst, transformer, classes);
return transformer.getDumpResult();
}
}
package com.taobao.arthas.core.command.klass100;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.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;
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;
import com.taobao.arthas.core.util.affect.RowAffect;
import com.taobao.arthas.core.util.matcher.Matcher;
import com.taobao.arthas.core.util.matcher.RegexMatcher;
import com.taobao.arthas.core.util.matcher.WildcardMatcher;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import java.lang.instrument.Instrumentation;
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.
*/
@Name("getstatic")
@Summary("Show the static field of a class")
@Description(Constants.EXAMPLE +
" getstatic demo.MathGame random\n" +
" getstatic -c 39eb305e org.apache.log4j.LogManager DEFAULT_CONFIGURATION_FILE\n" +
Constants.WIKI + Constants.WIKI_HOME + "getstatic")
public class GetStaticCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(GetStaticCommand.class);
private String classPattern;
private String fieldPattern;
private String express;
private String hashCode = null;
private String classLoaderClass;
private boolean isRegEx = false;
private int expand = 1;
@Argument(argName = "class-pattern", index = 0)
@Description("Class name pattern, use either '.' or '/' as separator")
public void setClassPattern(String classPattern) {
this.classPattern = classPattern;
}
@Argument(argName = "field-pattern", index = 1)
@Description("Field name pattern")
public void setFieldPattern(String fieldPattern) {
this.fieldPattern = fieldPattern;
}
@Argument(argName = "express", index = 2, required = false)
@Description("the content you want to watch, written by ognl")
public void setExpress(String express) {
this.express = express;
}
@Option(shortName = "c", longName = "classloader")
@Description("The hash code of the special class's classLoader")
public void setHashCode(String 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)
@Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) {
isRegEx = regEx;
}
@Option(shortName = "x", longName = "expand")
@Description("Expand level of object (1 by default)")
public void setExpand(Integer expand) {
this.expand = expand;
}
@Override
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()) {
process.end(-1, "No class found for: " + classPattern);
return;
}
ExitStatus status = null;
if (matchedClasses.size() > 1) {
status = processMatches(process, matchedClasses);
} else {
status = processExactMatch(process, affect, inst, matchedClasses);
}
process.appendResult(new RowAffectModel(affect));
CommandUtils.end(process, status);
} catch (Throwable e){
logger.error("processing error", e);
process.appendResult(new RowAffectModel(affect));
process.end(-1, "processing error");
}
}
private ExitStatus processExactMatch(CommandProcess process, RowAffect affect, Instrumentation inst,
Set<Class<?>> matchedClasses) {
Matcher<String> fieldNameMatcher = fieldNameMatcher();
Class<?> clazz = matchedClasses.iterator().next();
boolean found = false;
for (Field field : clazz.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers()) || !fieldNameMatcher.matching(field.getName())) {
continue;
}
if (!field.isAccessible()) {
field.setAccessible(true);
}
try {
Object value = field.get(null);
if (!StringUtils.isEmpty(express)) {
value = ExpressFactory.threadLocalExpress(value).get(express);
}
process.appendResult(new GetStaticModel(field.getName(), value, expand));
affect.rCnt(1);
} catch (IllegalAccessException e) {
logger.warn("getstatic: failed to get static value, class: {}, field: {} ", clazz, field.getName(), e);
process.appendResult(new MessageModel("Failed to get static, exception message: " + e.getMessage()
+ ", please check $HOME/logs/arthas/arthas.log for more details. "));
} catch (ExpressException e) {
logger.warn("getstatic: failed to get express value, class: {}, field: {}, express: {}", clazz, field.getName(), express, e);
process.appendResult(new MessageModel("Failed to get static, exception message: " + e.getMessage()
+ ", please check $HOME/logs/arthas/arthas.log for more details. "));
} finally {
found = true;
}
}
if (!found) {
return ExitStatus.failure(-1, "getstatic: no matched static field was found");
} else {
return ExitStatus.success();
}
}
private ExitStatus processMatches(CommandProcess process, Set<Class<?>> matchedClasses) {
// Element usage = new LabelElement("getstatic -c <hashcode> " + classPattern + " " + fieldPattern).style(
// Decoration.bold.fg(Color.blue));
// process.write("\n Found more than one class for: " + classPattern + ", Please use " + RenderUtil.render(usage, process.width()));
//TODO support message style
String usage = "getstatic -c <hashcode> " + classPattern + " " + fieldPattern;
process.appendResult(new MessageModel("Found more than one class for: " + classPattern + ", Please use: "+usage));
List<ClassVO> matchedClassVOs = ClassUtils.createClassVOList(matchedClasses);
process.appendResult(new GetStaticModel(matchedClassVOs));
return ExitStatus.failure(-1, "Found more than one class for: " + classPattern + ", Please use: "+usage);
}
private Matcher<String> fieldNameMatcher() {
return isRegEx ? new RegexMatcher(fieldPattern) : new WildcardMatcher(fieldPattern);
}
}
package com.taobao.arthas.core.command.klass100;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.common.Pair;
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;
import com.taobao.arthas.core.shell.cli.Completion;
import com.taobao.arthas.core.shell.cli.CompletionUtils;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.shell.command.ExitStatus;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.arthas.core.util.ClassLoaderUtils;
import com.taobao.arthas.core.util.CommandUtils;
import com.taobao.arthas.core.util.Decompiler;
import com.taobao.arthas.core.util.InstrumentationUtils;
import com.taobao.arthas.core.util.SearchUtils;
import com.taobao.arthas.core.util.affect.RowAffect;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.DefaultValue;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.Collection;
import java.util.regex.Pattern;
/**
* @author diecui1202 on 15/11/24.
* @author hengyunabc 2018-11-16
*/
@Name("jad")
@Summary("Decompile class")
@Description(Constants.EXAMPLE +
" jad java.lang.String\n" +
" jad java.lang.String toString\n" +
" jad --source-only java.lang.String\n" +
" jad -c 39eb305e org/apache/log4j/Logger\n" +
" jad -c 39eb305e -E org\\\\.apache\\\\.*\\\\.StringUtils\n" +
Constants.WIKI + Constants.WIKI_HOME + "jad")
public class JadCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(JadCommand.class);
private static Pattern pattern = Pattern.compile("(?m)^/\\*\\s*\\*/\\s*$" + System.getProperty("line.separator"));
private String classPattern;
private String methodName;
private String code = null;
private String classLoaderClass;
private boolean isRegEx = false;
private boolean hideUnicode = false;
private boolean lineNumber;
/**
* jad output source code only
*/
private boolean sourceOnly = false;
@Argument(argName = "class-pattern", index = 0)
@Description("Class name pattern, use either '.' or '/' as separator")
public void setClassPattern(String classPattern) {
this.classPattern = classPattern;
}
@Argument(argName = "method-name", index = 1, required = false)
@Description("method name pattern, decompile a specific method instead of the whole class")
public void setMethodName(String methodName) {
this.methodName = methodName;
}
@Option(shortName = "c", longName = "code")
@Description("The hash code of the special class's classLoader")
public void setCode(String 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)
@Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) {
isRegEx = regEx;
}
@Option(longName = "hideUnicode", flag = true)
@Description("hide unicode, default value false")
public void setHideUnicode(boolean hideUnicode) {
this.hideUnicode = hideUnicode;
}
@Option(longName = "source-only", flag = true)
@Description("Output source code only")
public void setSourceOnly(boolean sourceOnly) {
this.sourceOnly = sourceOnly;
}
@Option(longName = "lineNumber")
@DefaultValue("true")
@Description("Output source code contins line number, default value true")
public void setLineNumber(boolean lineNumber) {
this.lineNumber = lineNumber;
}
@Override
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 {
ExitStatus status = null;
if (matchedClasses == null || matchedClasses.isEmpty()) {
status = processNoMatch(process);
} else if (matchedClasses.size() > 1) {
status = processMatches(process, matchedClasses);
} else { // matchedClasses size is 1
// find inner classes.
Set<Class<?>> withInnerClasses = SearchUtils.searchClassOnly(inst, matchedClasses.iterator().next().getName() + "$*", false, code);
if(withInnerClasses.isEmpty()) {
withInnerClasses = matchedClasses;
}
status = processExactMatch(process, affect, inst, matchedClasses, withInnerClasses);
}
if (!this.sourceOnly) {
process.appendResult(new RowAffectModel(affect));
}
CommandUtils.end(process, status);
} catch (Throwable e){
logger.error("processing error", e);
process.end(-1, "processing error");
}
}
private ExitStatus processExactMatch(CommandProcess process, RowAffect affect, Instrumentation inst, Set<Class<?>> matchedClasses, Set<Class<?>> withInnerClasses) {
Class<?> c = matchedClasses.iterator().next();
Set<Class<?>> allClasses = new HashSet<Class<?>>(withInnerClasses);
allClasses.add(c);
try {
ClassDumpTransformer transformer = new ClassDumpTransformer(allClasses);
InstrumentationUtils.retransformClasses(inst, transformer, allClasses);
Map<Class<?>, File> classFiles = transformer.getDumpResult();
File classFile = classFiles.get(c);
Pair<String,NavigableMap<Integer,Integer>> decompileResult = Decompiler.decompileWithMappings(classFile.getAbsolutePath(), methodName, hideUnicode, lineNumber);
String source = decompileResult.getFirst();
if (source != null) {
source = pattern.matcher(source).replaceAll("");
} else {
source = "unknown";
}
JadModel jadModel = new JadModel();
jadModel.setSource(source);
jadModel.setMappings(decompileResult.getSecond());
if (!this.sourceOnly) {
jadModel.setClassInfo(ClassUtils.createSimpleClassInfo(c));
jadModel.setLocation(ClassUtils.getCodeSource(c.getProtectionDomain().getCodeSource()));
}
process.appendResult(jadModel);
affect.rCnt(classFiles.keySet().size());
return ExitStatus.success();
} catch (Throwable t) {
logger.error("jad: fail to decompile class: " + c.getName(), t);
return ExitStatus.failure(-1, "jad: fail to decompile class: " + c.getName() + ", please check $HOME/logs/arthas/arthas.log for more details.");
}
}
private ExitStatus processMatches(CommandProcess process, Set<Class<?>> matchedClasses) {
String usage = "jad -c <hashcode> " + classPattern;
String msg = " Found more than one class for: " + classPattern + ", Please use " + usage;
process.appendResult(new MessageModel(msg));
List<ClassVO> classVOs = ClassUtils.createClassVOList(matchedClasses);
JadModel jadModel = new JadModel();
jadModel.setMatchedClasses(classVOs);
process.appendResult(jadModel);
return ExitStatus.failure(-1, msg);
}
private ExitStatus processNoMatch(CommandProcess process) {
return ExitStatus.failure(-1, "No class found for: " + classPattern);
}
@Override
public void complete(Completion completion) {
int argumentIndex = CompletionUtils.detectArgumentIndex(completion);
if (argumentIndex == 1) {
if (!CompletionUtils.completeClassName(completion)) {
super.complete(completion);
}
return;
} else if (argumentIndex == 2) {
if (!CompletionUtils.completeMethodName(completion)) {
super.complete(completion);
}
return;
}
super.complete(completion);
}
}
package com.taobao.arthas.core.command.klass100;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Collection;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.compiler.DynamicCompiler;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.MemoryCompilerModel;
import com.taobao.arthas.core.command.model.RowAffectModel;
import com.taobao.arthas.core.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.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;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
/**
*
* @author hengyunabc 2019-02-05
*
*/
@Name("mc")
@Summary("Memory compiler, compiles java files into bytecode and class files in memory.")
@Description(Constants.EXAMPLE + " mc /tmp/Test.java\n" + " mc -c 327a647b /tmp/Test.java\n"
+ " mc -d /tmp/output /tmp/ClassA.java /tmp/ClassB.java\n" + Constants.WIKI + Constants.WIKI_HOME
+ "mc")
public class MemoryCompilerCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(MemoryCompilerCommand.class);
private String directory;
private String hashCode;
private String classLoaderClass;
private String encoding;
private List<String> sourcefiles;
@Argument(argName = "sourcefiles", index = 0)
@Description("source files")
public void setClassPattern(List<String> sourcefiles) {
this.sourcefiles = sourcefiles;
}
@Option(shortName = "c", longName = "classloader")
@Description("The hash code of the special ClassLoader")
public void setHashCode(String 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")
@Description("Source file encoding")
public void setEncoding(String encoding) {
this.encoding = encoding;
}
@Option(shortName = "d", longName = "directory")
@Description("Sets the destination directory for class files")
public void setDirectory(String directory) {
this.directory = directory;
}
@Override
public void process(final CommandProcess process) {
RowAffect affect = new RowAffect();
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();
} else {
classloader = ClassLoaderUtils.getClassLoader(inst, hashCode);
if (classloader == null) {
process.end(-1, "Can not find classloader with hashCode: " + hashCode + ".");
return;
}
}
DynamicCompiler dynamicCompiler = new DynamicCompiler(classloader);
Charset charset = Charset.defaultCharset();
if (encoding != null) {
charset = Charset.forName(encoding);
}
for (String sourceFile : sourcefiles) {
String sourceCode = FileUtils.readFileToString(new File(sourceFile), charset);
String name = new File(sourceFile).getName();
if (name.endsWith(".java")) {
name = name.substring(0, name.length() - ".java".length());
}
dynamicCompiler.addSource(name, sourceCode);
}
Map<String, byte[]> byteCodes = dynamicCompiler.buildByteCodes();
File outputDir = null;
if (this.directory != null) {
outputDir = new File(this.directory);
} else {
outputDir = new File("").getAbsoluteFile();
}
List<String> files = new ArrayList<String>();
for (Entry<String, byte[]> entry : byteCodes.entrySet()) {
File byteCodeFile = new File(outputDir, entry.getKey().replace('.', '/') + ".class");
FileUtils.writeByteArrayToFile(byteCodeFile, entry.getValue());
files.add(byteCodeFile.getAbsolutePath());
affect.rCnt(1);
}
process.appendResult(new MemoryCompilerModel(files));
process.appendResult(new RowAffectModel(affect));
process.end();
} catch (Throwable e) {
logger.warn("Memory compiler error", e);
process.end(-1, "Memory compiler error, exception message: " + e.getMessage()
+ ", please check $HOME/logs/arthas/arthas.log for more details.");
}
}
@Override
public void complete(Completion completion) {
if (!CompletionUtils.completeFilePath(completion)) {
super.complete(completion);
}
}
}
package com.taobao.arthas.core.command.klass100;
import java.lang.instrument.Instrumentation;
import java.util.Collection;
import java.util.List;
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.express.Express;
import com.taobao.arthas.core.command.express.ExpressException;
import com.taobao.arthas.core.command.express.ExpressFactory;
import com.taobao.arthas.core.command.model.ClassLoaderVO;
import com.taobao.arthas.core.command.model.ObjectVO;
import com.taobao.arthas.core.command.model.OgnlModel;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.util.ClassLoaderUtils;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
/**
*
* @author hengyunabc 2018-10-18
*
*/
@Name("ognl")
@Summary("Execute ognl expression.")
@Description(Constants.EXAMPLE
+ " ognl '@java.lang.System@out.println(\"hello \\u4e2d\\u6587\")' \n"
+ " ognl -x 2 '@Singleton@getInstance()' \n"
+ " ognl '@Demo@staticFiled' \n"
+ " ognl '#value1=@System@getProperty(\"java.home\"), #value2=@System@getProperty(\"java.runtime.name\"), {#value1, #value2}'\n"
+ " ognl -c 5d113a51 '@com.taobao.arthas.core.GlobalOptions@isDump' \n"
+ Constants.WIKI + Constants.WIKI_HOME + "ognl\n"
+ " https://commons.apache.org/proper/commons-ognl/language-guide.html")
public class OgnlCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(OgnlCommand.class);
private String express;
private String hashCode;
private String classLoaderClass;
private int expand = 1;
@Argument(argName = "express", index = 0, required = true)
@Description("The ognl expression.")
public void setExpress(String express) {
this.express = express;
}
@Option(shortName = "c", longName = "classLoader")
@Description("The hash code of the special class's classLoader, default classLoader is SystemClassLoader.")
public void setHashCode(String 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 = "x", longName = "expand")
@Description("Expand level of object (1 by default).")
public void setExpand(Integer expand) {
this.expand = expand;
}
@Override
public void process(CommandProcess process) {
Instrumentation inst = process.session().getInstrumentation();
ClassLoader classLoader = null;
if (hashCode != null) {
classLoader = ClassLoaderUtils.getClassLoader(inst, hashCode);
if (classLoader == null) {
process.end(-1, "Can not find classloader with hashCode: " + hashCode + ".");
return;
}
} else if (classLoaderClass != null) {
List<ClassLoader> matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass);
if (matchedClassLoaders.size() == 1) {
classLoader = matchedClassLoaders.get(0);
} else if (matchedClassLoaders.size() > 1) {
Collection<ClassLoaderVO> classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders);
OgnlModel ognlModel = new OgnlModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(ognlModel);
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;
}
} else {
classLoader = ClassLoader.getSystemClassLoader();
}
Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader);
try {
Object value = unpooledExpress.get(express);
OgnlModel ognlModel = new OgnlModel()
.setValue(new ObjectVO(value, expand));
process.appendResult(ognlModel);
process.end();
} catch (ExpressException e) {
logger.warn("ognl: failed execute express: " + express, e);
process.end(-1, "Failed to execute ognl, exception message: " + e.getMessage()
+ ", please check $HOME/logs/arthas/arthas.log for more details. ");
}
}
}
package com.taobao.arthas.core.command.klass100;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import com.alibaba.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;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
/**
* Redefine Classes.
*
* @author hengyunabc 2018-07-13
* @see java.lang.instrument.Instrumentation#redefineClasses(ClassDefinition...)
*/
@Name("redefine")
@Summary("Redefine classes. @see Instrumentation#redefineClasses(ClassDefinition...)")
@Description(Constants.EXAMPLE +
" redefine /tmp/Test.class\n" +
" redefine -c 327a647b /tmp/Test.class /tmp/Test\\$Inner.class \n" +
" redefine --classLoaderClass 'sun.misc.Launcher$AppClassLoader' /tmp/Test.class \n" +
Constants.WIKI + Constants.WIKI_HOME + "redefine")
public class RedefineCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(RedefineCommand.class);
private static final int MAX_FILE_SIZE = 10 * 1024 * 1024;
private String hashCode;
private String classLoaderClass;
private List<String> paths;
@Option(shortName = "c", longName = "classloader")
@Description("classLoader hashcode")
public void setHashCode(String 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)
@Description(".class file paths")
public void setPaths(List<String> paths) {
this.paths = paths;
}
@Override
public void process(CommandProcess process) {
RedefineModel redefineModel = new RedefineModel();
Instrumentation inst = process.session().getInstrumentation();
for (String path : paths) {
File file = new File(path);
if (!file.exists()) {
process.end(-1, "file does not exist, path:" + path);
return;
}
if (!file.isFile()) {
process.end(-1, "not a normal file, path:" + path);
return;
}
if (file.length() >= MAX_FILE_SIZE) {
process.end(-1, "file size: " + file.length() + " >= " + MAX_FILE_SIZE + ", path: " + path);
return;
}
}
Map<String, byte[]> bytesMap = new HashMap<String, byte[]>();
for (String path : paths) {
RandomAccessFile f = null;
try {
f = new RandomAccessFile(path, "r");
final byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
final String clazzName = readClassName(bytes);
bytesMap.put(clazzName, bytes);
} catch (Exception e) {
logger.warn("load class file failed: "+path, e);
process.end(-1, "load class file failed: " +path+", error: " + e);
return;
} finally {
if (f != null) {
try {
f.close();
} catch (IOException e) {
// ignore
}
}
}
}
if (bytesMap.size() != paths.size()) {
process.end(-1, "paths may contains same class name!");
return;
}
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;
}
definitions.add(new ClassDefinition(clazz, bytesMap.get(clazz.getName())));
redefineModel.addRedefineClass(clazz.getName());
logger.info("Try redefine class name: {}, ClassLoader: {}", clazz.getName(), clazz.getClassLoader());
}
}
try {
if (definitions.isEmpty()) {
process.end(-1, "These classes are not found in the JVM and may not be loaded: " + bytesMap.keySet());
return;
}
inst.redefineClasses(definitions.toArray(new ClassDefinition[0]));
process.appendResult(redefineModel);
process.end();
} catch (Throwable e) {
String message = "redefine error! " + e.toString();
logger.error(message, e);
process.end(-1, message);
}
}
private static String readClassName(final byte[] bytes) {
return new ClassReader(bytes).getClassName().replace("/", ".");
}
@Override
public void complete(Completion completion) {
if (!CompletionUtils.completeFilePath(completion)) {
super.complete(completion);
}
}
}
package com.taobao.arthas.core.command.klass100;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.alibaba.deps.org.objectweb.asm.ClassReader;
import com.taobao.arthas.core.advisor.TransformerManager;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.ClassLoaderVO;
import com.taobao.arthas.core.command.model.RetransformModel;
import com.taobao.arthas.core.server.ArthasBootstrap;
import com.taobao.arthas.core.shell.cli.CliToken;
import com.taobao.arthas.core.shell.cli.Completion;
import com.taobao.arthas.core.shell.cli.CompletionUtils;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.util.ClassLoaderUtils;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.arthas.core.util.SearchUtils;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.DefaultValue;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
/**
*
* Retransform Classes.
*
* @author hengyunabc 2021-01-05
* @see java.lang.instrument.Instrumentation#retransformClasses(Class...)
*/
@Name("retransform")
@Summary("Retransform classes. @see Instrumentation#retransformClasses(Class...)")
@Description(Constants.EXAMPLE + " retransform /tmp/Test.class\n"
+ " retransform -l \n"
+ " retransform -d 1 # delete retransform entry\n"
+ " retransform --deleteAll # delete all retransform entries\n"
+ " retransform --classPattern demo.* # triger retransform classes\n"
+ " retransform -c 327a647b /tmp/Test.class /tmp/Test\\$Inner.class \n"
+ " retransform --classLoaderClass 'sun.misc.Launcher$AppClassLoader' /tmp/Test.class\n"
+ Constants.WIKI + Constants.WIKI_HOME
+ "retransform")
public class RetransformCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(RetransformCommand.class);
private static final int MAX_FILE_SIZE = 10 * 1024 * 1024;
private static volatile List<RetransformEntry> retransformEntries = new ArrayList<RetransformEntry>();
private static volatile ClassFileTransformer transformer = null;
private String hashCode;
private String classLoaderClass;
private List<String> paths;
private boolean list;
private int delete = -1;
private boolean deleteAll;
private String classPattern;
private int limit;
@Option(shortName = "l", longName = "list", flag = true)
@Description("list all retransform entry.")
public void setList(boolean list) {
this.list = list;
}
@Option(shortName = "d", longName = "delete")
@Description("delete retransform entry by id.")
public void setDelete(int delete) {
this.delete = delete;
}
@Option(longName = "deleteAll", flag = true)
@Description("delete all retransform entries.")
public void setDeleteAll(boolean deleteAll) {
this.deleteAll = deleteAll;
}
@Option(longName = "classPattern")
@Description("trigger retransform matched classes by class pattern.")
public void setClassPattern(String classPattern) {
this.classPattern = classPattern;
}
@Option(shortName = "c", longName = "classloader")
@Description("classLoader hashcode")
public void setHashCode(String 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, required = false)
@Description(".class file paths")
public void setPaths(List<String> paths) {
this.paths = paths;
}
@Option(longName = "limit")
@Description("The limit of dump classes size, default value is 50")
@DefaultValue("50")
public void setLimit(int limit) {
this.limit = limit;
}
private static void initTransformer() {
if (transformer != null) {
return;
} else {
synchronized (RetransformCommand.class) {
if (transformer == null) {
transformer = new RetransformClassFileTransformer();
TransformerManager transformerManager = ArthasBootstrap.getInstance().getTransformerManager();
transformerManager.addRetransformer(transformer);
}
}
}
}
@Override
public void process(CommandProcess process) {
initTransformer();
RetransformModel retransformModel = new RetransformModel();
Instrumentation inst = process.session().getInstrumentation();
if (this.list) {
List<RetransformEntry> retransformEntryList = allRetransformEntries();
retransformModel.setRetransformEntries(retransformEntryList);
process.appendResult(retransformModel);
process.end();
return;
} else if (this.deleteAll) {
deleteAllRetransformEntry();
process.appendResult(retransformModel);
process.end();
return;
} else if (this.delete > 0) {
deleteRetransformEntry(this.delete);
process.end();
return;
} else if (this.classPattern != null) {
Set<Class<?>> searchClass = SearchUtils.searchClass(inst, classPattern, false, this.hashCode);
if (searchClass.isEmpty()) {
process.end(-1, "These classes are not found in the JVM and may not be loaded: " + classPattern);
return;
}
if (searchClass.size() > limit) {
process.end(-1, "match classes size: " + searchClass.size() + ", more than limit: " + limit
+ ", It is recommended to use a more precise class pattern.");
}
try {
inst.retransformClasses(searchClass.toArray(new Class[0]));
for (Class<?> clazz : searchClass) {
retransformModel.addRetransformClass(clazz.getName());
}
process.appendResult(retransformModel);
process.end();
return;
} catch (Throwable e) {
String message = "retransform error! " + e.toString();
logger.error(message, e);
process.end(-1, message);
return;
}
}
for (String path : paths) {
File file = new File(path);
if (!file.exists()) {
process.end(-1, "file does not exist, path:" + path);
return;
}
if (!file.isFile()) {
process.end(-1, "not a normal file, path:" + path);
return;
}
if (file.length() >= MAX_FILE_SIZE) {
process.end(-1, "file size: " + file.length() + " >= " + MAX_FILE_SIZE + ", path: " + path);
return;
}
}
Map<String, byte[]> bytesMap = new HashMap<String, byte[]>();
for (String path : paths) {
RandomAccessFile f = null;
try {
f = new RandomAccessFile(path, "r");
final byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
final String clazzName = readClassName(bytes);
bytesMap.put(clazzName, bytes);
} catch (Exception e) {
logger.warn("load class file failed: " + path, e);
process.end(-1, "load class file failed: " + path + ", error: " + e);
return;
} finally {
if (f != null) {
try {
f.close();
} catch (IOException e) {
// ignore
}
}
}
}
if (bytesMap.size() != paths.size()) {
process.end(-1, "paths may contains same class name!");
return;
}
List<RetransformEntry> retransformEntryList = new ArrayList<RetransformEntry>();
List<Class<?>> classList = new ArrayList<Class<?>>();
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);
retransformModel.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(retransformModel);
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;
}
RetransformEntry retransformEntry = new RetransformEntry(clazz.getName(), bytesMap.get(clazz.getName()),
hashCode, classLoaderClass);
retransformEntryList.add(retransformEntry);
classList.add(clazz);
retransformModel.addRetransformClass(clazz.getName());
logger.info("Try retransform class name: {}, ClassLoader: {}", clazz.getName(), clazz.getClassLoader());
}
}
try {
if (retransformEntryList.isEmpty()) {
process.end(-1, "These classes are not found in the JVM and may not be loaded: " + bytesMap.keySet());
return;
}
addRetransformEntry(retransformEntryList);
inst.retransformClasses(classList.toArray(new Class[0]));
process.appendResult(retransformModel);
process.end();
} catch (Throwable e) {
String message = "retransform error! " + e.toString();
logger.error(message, e);
process.end(-1, message);
}
}
private static String readClassName(final byte[] bytes) {
return new ClassReader(bytes).getClassName().replace('/', '.');
}
@Override
public void complete(Completion completion) {
List<CliToken> tokens = completion.lineTokens();
if (CompletionUtils.shouldCompleteOption(completion, "--classPattern")) {
CompletionUtils.completeClassName(completion);
return;
}
for (CliToken token : tokens) {
String tokenStr = token.value();
if (tokenStr != null && tokenStr.startsWith("-")) {
super.complete(completion);
return;
}
}
// 最后,没有有 - 开头的,才尝试补全 file path
if (!CompletionUtils.completeFilePath(completion)) {
super.complete(completion);
}
}
public static class RetransformEntry {
private static final AtomicInteger counter = new AtomicInteger(0);
private int id;
private String className;
private byte[] bytes;
private String hashCode;
private String classLoaderClass;
/**
* 被 transform 触发次数
*/
private int transformCount = 0;
public RetransformEntry(String className, byte[] bytes, String hashCode, String classLoaderClass) {
id = counter.incrementAndGet();
this.className = className;
this.bytes = bytes;
this.hashCode = hashCode;
this.classLoaderClass = classLoaderClass;
}
public void incTransformCount() {
transformCount++;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getTransformCount() {
return transformCount;
}
public void setTransformCount(int transformCount) {
this.transformCount = transformCount;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public byte[] getBytes() {
return bytes;
}
public void setBytes(byte[] bytes) {
this.bytes = bytes;
}
public String getHashCode() {
return hashCode;
}
public void setHashCode(String hashCode) {
this.hashCode = hashCode;
}
public String getClassLoaderClass() {
return classLoaderClass;
}
public void setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
}
}
public static synchronized void addRetransformEntry(List<RetransformEntry> retransformEntryList) {
List<RetransformEntry> tmp = new ArrayList<RetransformEntry>();
tmp.addAll(retransformEntries);
tmp.addAll(retransformEntryList);
Collections.sort(tmp, new Comparator<RetransformEntry>() {
@Override
public int compare(RetransformEntry entry1, RetransformEntry entry2) {
return entry1.getId() - entry2.getId();
}
});
retransformEntries = tmp;
}
public static synchronized RetransformEntry deleteRetransformEntry(int id) {
RetransformEntry result = null;
List<RetransformEntry> tmp = new ArrayList<RetransformEntry>();
for (RetransformEntry entry : retransformEntries) {
if (entry.getId() != id) {
tmp.add(entry);
} else {
result = entry;
}
}
retransformEntries = tmp;
return result;
}
public static List<RetransformEntry> allRetransformEntries() {
return retransformEntries;
}
public static synchronized void deleteAllRetransformEntry() {
retransformEntries = new ArrayList<RetransformEntry>();
}
static class RetransformClassFileTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (className == null) {
return null;
}
className = className.replace('/', '.');
List<RetransformEntry> allRetransformEntries = allRetransformEntries();
// 倒序,因为要执行的配置生效
ListIterator<RetransformEntry> listIterator = allRetransformEntries
.listIterator(allRetransformEntries.size());
while (listIterator.hasPrevious()) {
RetransformEntry retransformEntry = listIterator.previous();
int id = retransformEntry.getId();
// 判断类名是否一致
boolean updateFlag = false;
// 类名一致,则看是否要比较 loader,如果不需要比较 loader,则认为成功
if (className.equals(retransformEntry.getClassName())) {
if (retransformEntry.getClassLoaderClass() != null || retransformEntry.getHashCode() != null) {
updateFlag = isLoaderMatch(retransformEntry, loader);
} else {
updateFlag = true;
}
}
if (updateFlag) {
logger.info("RetransformCommand match class: {}, id: {}, classLoaderClass: {}, hashCode: {}",
className, id, retransformEntry.getClassLoaderClass(), retransformEntry.getHashCode());
retransformEntry.incTransformCount();
return retransformEntry.getBytes();
}
}
return null;
}
private boolean isLoaderMatch(RetransformEntry retransformEntry, ClassLoader loader) {
if (loader == null) {
return false;
}
if (retransformEntry.getClassLoaderClass() != null) {
if (loader.getClass().getName().equals(retransformEntry.getClassLoaderClass())) {
return true;
}
}
if (retransformEntry.getHashCode() != null) {
String hashCode = Integer.toHexString(loader.hashCode());
if (hashCode.equals(retransformEntry.getHashCode())) {
return true;
}
}
return false;
}
}
}
package com.taobao.arthas.core.command.klass100;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.ClassDetailVO;
import com.taobao.arthas.core.command.model.SearchClassModel;
import com.taobao.arthas.core.command.model.RowAffectModel;
import com.taobao.arthas.core.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.arthas.core.util.ResultUtils;
import com.taobao.arthas.core.util.SearchUtils;
import com.taobao.arthas.core.util.StringUtils;
import com.taobao.arthas.core.util.affect.RowAffect;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
/**
* 展示类信息
*
* @author vlinux
*/
@Name("sc")
@Summary("Search all the classes loaded by JVM")
@Description(Constants.EXAMPLE +
" sc -d org.apache.commons.lang.StringUtils\n" +
" sc -d org/apache/commons/lang/StringUtils\n" +
" sc -d *StringUtils\n" +
" sc -d -f org.apache.commons.lang.StringUtils\n" +
" sc -E org\\\\.apache\\\\.commons\\\\.lang\\\\.StringUtils\n" +
Constants.WIKI + Constants.WIKI_HOME + "sc")
public class SearchClassCommand extends AnnotatedCommand {
private String classPattern;
private boolean isDetail = false;
private boolean isField = false;
private boolean isRegEx = false;
private String hashCode = null;
private String classLoaderClass;
private Integer expand;
private int numberOfLimit = 100;
@Argument(argName = "class-pattern", index = 0)
@Description("Class name pattern, use either '.' or '/' as separator")
public void setClassPattern(String classPattern) {
this.classPattern = classPattern;
}
@Option(shortName = "d", longName = "details", flag = true)
@Description("Display the details of class")
public void setDetail(boolean detail) {
isDetail = detail;
}
@Option(shortName = "f", longName = "field", flag = true)
@Description("Display all the member variables")
public void setField(boolean field) {
isField = field;
}
@Option(shortName = "E", longName = "regex", flag = true)
@Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) {
isRegEx = regEx;
}
@Option(shortName = "x", longName = "expand")
@Description("Expand level of object (0 by default)")
public void setExpand(Integer expand) {
this.expand = expand;
}
@Option(shortName = "c", longName = "classloader")
@Description("The hash code of the special class's classLoader")
public void setHashCode(String 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 = "n", longName = "limits")
@Description("Maximum number of matching classes with details (100 by default)")
public void setNumberOfLimit(int numberOfLimit) {
this.numberOfLimit = numberOfLimit;
}
@Override
public void process(final CommandProcess process) {
// TODO: null check
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);
SearchClassModel searchclassModel = new SearchClassModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(searchclassModel);
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;
}
}
List<Class<?>> matchedClasses = new ArrayList<Class<?>>(SearchUtils.searchClass(inst, classPattern, isRegEx, hashCode));
Collections.sort(matchedClasses, new Comparator<Class<?>>() {
@Override
public int compare(Class<?> c1, Class<?> c2) {
return StringUtils.classname(c1).compareTo(StringUtils.classname(c2));
}
});
if (isDetail) {
if (numberOfLimit > 0 && matchedClasses.size() > numberOfLimit) {
process.end(-1, "The number of matching classes is greater than : " + numberOfLimit+". \n" +
"Please specify a more accurate 'class-patten' or use the parameter '-n' to change the maximum number of matching classes.");
return;
}
for (Class<?> clazz : matchedClasses) {
ClassDetailVO classInfo = ClassUtils.createClassInfo(clazz, isField, expand);
process.appendResult(new SearchClassModel(classInfo, isDetail, isField));
}
} else {
int pageSize = 256;
ResultUtils.processClassNames(matchedClasses, pageSize, new ResultUtils.PaginationHandler<List<String>>() {
@Override
public boolean handle(List<String> classNames, int segment) {
process.appendResult(new SearchClassModel(classNames, segment));
return true;
}
});
}
affect.rCnt(matchedClasses.size());
process.appendResult(new RowAffectModel(affect));
process.end();
}
@Override
public void complete(Completion completion) {
if (!CompletionUtils.completeClassName(completion)) {
super.complete(completion);
}
}
}
package com.taobao.arthas.core.command.klass100;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.Collection;
import java.util.List;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.SearchMethodModel;
import com.taobao.arthas.core.command.model.MethodVO;
import com.taobao.arthas.core.command.model.RowAffectModel;
import com.taobao.arthas.core.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.arthas.core.util.SearchUtils;
import com.taobao.arthas.core.util.StringUtils;
import com.taobao.arthas.core.util.affect.RowAffect;
import com.taobao.arthas.core.util.matcher.Matcher;
import com.taobao.arthas.core.util.matcher.RegexMatcher;
import com.taobao.arthas.core.util.matcher.WildcardMatcher;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
/**
* 展示方法信息
*
* @author vlinux
* @author hengyunabc 2019-02-13
*/
@Name("sm")
@Summary("Search the method of classes loaded by JVM")
@Description(Constants.EXAMPLE +
" sm java.lang.String\n" +
" sm -d org.apache.commons.lang.StringUtils\n" +
" sm -d org/apache/commons/lang/StringUtils\n" +
" sm *StringUtils *\n" +
" sm -Ed org\\\\.apache\\\\.commons\\\\.lang\\.StringUtils .*\n" +
Constants.WIKI + Constants.WIKI_HOME + "sm")
public class SearchMethodCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(SearchMethodCommand.class);
private String classPattern;
private String methodPattern;
private String hashCode = null;
private String classLoaderClass;
private boolean isDetail = false;
private boolean isRegEx = false;
private int numberOfLimit = 100;
@Argument(argName = "class-pattern", index = 0)
@Description("Class name pattern, use either '.' or '/' as separator")
public void setClassPattern(String classPattern) {
this.classPattern = classPattern;
}
@Argument(argName = "method-pattern", index = 1, required = false)
@Description("Method name pattern")
public void setMethodPattern(String methodPattern) {
this.methodPattern = methodPattern;
}
@Option(shortName = "d", longName = "details", flag = true)
@Description("Display the details of method")
public void setDetail(boolean detail) {
isDetail = detail;
}
@Option(shortName = "E", longName = "regex", flag = true)
@Description("Enable regular expression to match (wildcard matching by default)")
public void setRegEx(boolean regEx) {
isRegEx = regEx;
}
@Option(shortName = "c", longName = "classloader")
@Description("The hash code of the special class's classLoader")
public void setHashCode(String 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 = "n", longName = "limits")
@Description("Maximum number of matching classes (100 by default)")
public void setNumberOfLimit(int numberOfLimit) {
this.numberOfLimit = numberOfLimit;
}
@Override
public void process(CommandProcess process) {
RowAffect affect = new RowAffect();
Instrumentation inst = process.session().getInstrumentation();
Matcher<String> methodNameMatcher = methodNameMatcher();
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);
SearchMethodModel searchmethodModel = new SearchMethodModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(searchmethodModel);
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, hashCode);
if (numberOfLimit > 0 && matchedClasses.size() > numberOfLimit) {
process.end(-1, "The number of matching classes is greater than : " + numberOfLimit+". \n" +
"Please specify a more accurate 'class-patten' or use the parameter '-n' to change the maximum number of matching classes.");
return;
}
for (Class<?> clazz : matchedClasses) {
try {
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
if (!methodNameMatcher.matching("<init>")) {
continue;
}
MethodVO methodInfo = ClassUtils.createMethodInfo(constructor, clazz, isDetail);
process.appendResult(new SearchMethodModel(methodInfo, isDetail));
affect.rCnt(1);
}
for (Method method : clazz.getDeclaredMethods()) {
if (!methodNameMatcher.matching(method.getName())) {
continue;
}
MethodVO methodInfo = ClassUtils.createMethodInfo(method, clazz, isDetail);
process.appendResult(new SearchMethodModel(methodInfo, isDetail));
affect.rCnt(1);
}
} catch (Error e) {
//print failed className
String msg = String.format("process class failed: %s, error: %s", clazz.getName(), e.toString());
logger.error(msg, e);
process.end(1, msg);
return;
}
}
process.appendResult(new RowAffectModel(affect));
process.end();
}
private Matcher<String> methodNameMatcher() {
// auto fix default methodPattern
if (StringUtils.isBlank(methodPattern)) {
methodPattern = isRegEx ? ".*" : "*";
}
return isRegEx ? new RegexMatcher(methodPattern) : new WildcardMatcher(methodPattern);
}
@Override
public void complete(Completion completion) {
int argumentIndex = CompletionUtils.detectArgumentIndex(completion);
if (argumentIndex == 1) {
if (!CompletionUtils.completeClassName(completion)) {
super.complete(completion);
}
return;
} else if (argumentIndex == 2) {
if (!CompletionUtils.completeMethodName(completion)) {
super.complete(completion);
}
return;
}
super.complete(completion);
}
}
package com.taobao.arthas.core.command.logger;
import com.alibaba.deps.org.objectweb.asm.ClassReader;
import com.alibaba.deps.org.objectweb.asm.ClassVisitor;
import com.alibaba.deps.org.objectweb.asm.ClassWriter;
import com.alibaba.deps.org.objectweb.asm.commons.ClassRemapper;
import com.alibaba.deps.org.objectweb.asm.commons.SimpleRemapper;
/**
*
* @author hengyunabc 2019-09-23
*
*/
public class AsmRenameUtil {
public static byte[] renameClass(byte[] bytes, final String oldName, final String newName) {
ClassReader reader = new ClassReader(bytes);
ClassWriter writer = new ClassWriter(reader, 0);
final String internalOldName = oldName.replace('.', '/');
final String internalNewName = newName.replace('.', '/');
// ClassVisitor visitor = new ClassRemapper(writer, new Remapper() {
//
// @Override
// public String mapType(String internalName) {
// if (internalName.equals(internalOldName)) {
// return internalNewName;
// } else {
// return super.mapType(internalName);
// }
// }
//
// });
ClassVisitor visitor = new ClassRemapper(writer, new SimpleRemapper(internalOldName, internalNewName));
reader.accept(visitor, 0);
return writer.toByteArray();
}
}
package com.taobao.arthas.core.command.logger;
import java.lang.reflect.Field;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.AsyncAppender;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
/**
*
* @author hengyunabc 2019-09-20
*
*/
public class Log4j2Helper {
private static boolean Log4j2 = false;
private static Field configField = null;
static {
try {
Class<?> loggerClass = Log4j2Helper.class.getClassLoader().loadClass("org.apache.logging.log4j.Logger");
// 这里可能会加载到其它上游ClassLoader的log4j2,因此需要判断是否当前classloader
if (loggerClass.getClassLoader().equals(Log4j2Helper.class.getClassLoader())) {
Log4j2 = true;
}
try {
configField = LoggerConfig.class.getDeclaredField("config");
configField.setAccessible(true);
} catch (Throwable e) {
// ignore
}
} catch (Throwable t) {
}
}
public static boolean hasLength(String str) {
return (str != null && !str.isEmpty());
}
private static LoggerConfig getLoggerConfig(String name) {
if (!hasLength(name) || LoggerConfig.ROOT.equalsIgnoreCase(name)) {
name = LogManager.ROOT_LOGGER_NAME;
}
return getLoggerContext().getConfiguration().getLoggers().get(name);
}
private static LoggerContext getLoggerContext() {
return (LoggerContext) LogManager.getContext(false);
}
public static Boolean updateLevel(String loggerName, String logLevel) {
if (Log4j2) {
Level level = Level.getLevel(logLevel.toUpperCase());
if (level == null) {
return null;
}
LoggerConfig loggerConfig = getLoggerConfig(loggerName);
if (loggerConfig == null) {
loggerConfig = new LoggerConfig(loggerName, level, true);
getLoggerContext().getConfiguration().addLogger(loggerName, loggerConfig);
} else {
loggerConfig.setLevel(level);
}
getLoggerContext().updateLoggers();
return Boolean.TRUE;
}
return null;
}
public static Map<String, Map<String, Object>> getLoggers(String name, boolean includeNoAppender) {
Map<String, Map<String, Object>> loggerInfoMap = new HashMap<String, Map<String, Object>>();
if (!Log4j2) {
return loggerInfoMap;
}
Configuration configuration = getLoggerContext().getConfiguration();
if (name != null && !name.trim().isEmpty()) {
LoggerConfig loggerConfig = configuration.getLoggerConfig(name);
if (loggerConfig == null) {
return loggerInfoMap;
}
// 排掉非root时,获取到root的logger config
if (!name.equalsIgnoreCase(LoggerConfig.ROOT) && isEmpty(loggerConfig.getName())) {
return loggerInfoMap;
}
loggerInfoMap.put(name, doGetLoggerInfo(loggerConfig));
} else {
// 获取所有logger时,如果没有appender则忽略
Map<String, LoggerConfig> loggers = configuration.getLoggers();
if (loggers != null) {
for (Entry<String, LoggerConfig> entry : loggers.entrySet()) {
LoggerConfig loggerConfig = entry.getValue();
if (!includeNoAppender) {
if (!loggerConfig.getAppenders().isEmpty()) {
loggerInfoMap.put(entry.getKey(), doGetLoggerInfo(entry.getValue()));
}
} else {
loggerInfoMap.put(entry.getKey(), doGetLoggerInfo(entry.getValue()));
}
}
}
}
return loggerInfoMap;
}
private static Object getConfigField(LoggerConfig loggerConfig) {
try {
if (configField != null) {
return configField.get(loggerConfig);
}
} catch (Throwable e) {
// ignore
}
return null;
}
private static Map<String, Object> doGetLoggerInfo(LoggerConfig loggerConfig) {
Map<String, Object> info = new HashMap<String, Object>();
String name = loggerConfig.getName();
if (name == null || name.trim().isEmpty()) {
name = LoggerConfig.ROOT;
}
info.put(LoggerHelper.name, name);
info.put(LoggerHelper.clazz, loggerConfig.getClass());
CodeSource codeSource = loggerConfig.getClass().getProtectionDomain().getCodeSource();
if (codeSource != null) {
info.put(LoggerHelper.codeSource, codeSource.getLocation());
}
Object config = getConfigField(loggerConfig);
if (config != null) {
info.put(LoggerHelper.config, config);
}
info.put(LoggerHelper.additivity, loggerConfig.isAdditive());
Level level = loggerConfig.getLevel();
if (level != null) {
info.put(LoggerHelper.level, level.toString());
}
List<Map<String, Object>> result = doGetLoggerAppenders(loggerConfig);
info.put(LoggerHelper.appenders, result);
return info;
}
private static List<Map<String, Object>> doGetLoggerAppenders(LoggerConfig loggerConfig) {
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
Map<String, Appender> appenders = loggerConfig.getAppenders();
for (Entry<String, Appender> entry : appenders.entrySet()) {
Map<String, Object> info = new HashMap<String, Object>();
Appender appender = entry.getValue();
info.put(LoggerHelper.name, appender.getName());
info.put(LoggerHelper.clazz, appender.getClass());
result.add(info);
if (appender instanceof FileAppender) {
info.put(LoggerHelper.file, ((FileAppender) appender).getFileName());
} else if (appender instanceof ConsoleAppender) {
info.put(LoggerHelper.target, ((ConsoleAppender) appender).getTarget());
} else if (appender instanceof AsyncAppender) {
AsyncAppender asyncAppender = ((AsyncAppender) appender);
String[] appenderRefStrings = asyncAppender.getAppenderRefStrings();
info.put(LoggerHelper.blocking, asyncAppender.isBlocking());
info.put(LoggerHelper.appenderRef, Arrays.asList(appenderRefStrings));
}
}
return result;
}
private static boolean isEmpty(Object str) {
return str == null || "".equals(str);
}
}
package com.taobao.arthas.core.command.logger;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Appender;
import org.apache.log4j.AsyncAppender;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
/**
*
* @author hengyunabc 2019-09-06
*
*/
public class Log4jHelper {
private static boolean Log4j = false;
static {
try {
Class<?> loggerClass = Log4jHelper.class.getClassLoader().loadClass("org.apache.log4j.Logger");
// 这里可能会加载到其它上游ClassLoader的log4j,因此需要判断是否当前classloader
if (loggerClass.getClassLoader().equals(Log4jHelper.class.getClassLoader())) {
Log4j = true;
}
} catch (Throwable t) {
}
}
public static Boolean updateLevel(String name, String level) {
if (Log4j) {
Level l = Level.toLevel(level, Level.ERROR);
Logger logger = LogManager.getLoggerRepository().exists(name);
if (logger != null) {
logger.setLevel(l);
return true;
} else {
Logger root = LogManager.getLoggerRepository().getRootLogger();
if (root.getName().equals(name)) {
root.setLevel(l);
return true;
}
}
return false;
}
return null;
}
public static Map<String, Map<String, Object>> getLoggers(String name, boolean includeNoAppender) {
Map<String, Map<String, Object>> loggerInfoMap = new HashMap<String, Map<String, Object>>();
if (!Log4j) {
return loggerInfoMap;
}
if (name != null && !name.trim().isEmpty()) {
Logger logger = LogManager.getLoggerRepository().exists(name);
if (logger != null) {
loggerInfoMap.put(name, doGetLoggerInfo(logger));
}
} else {
// 获取所有logger时,如果没有appender则忽略
@SuppressWarnings("unchecked")
Enumeration<Logger> loggers = LogManager.getLoggerRepository().getCurrentLoggers();
if (loggers != null) {
while (loggers.hasMoreElements()) {
Logger logger = loggers.nextElement();
Map<String, Object> info = doGetLoggerInfo(logger);
if (!includeNoAppender) {
List<?> appenders = (List<?>) info.get(LoggerHelper.appenders);
if (appenders != null && !appenders.isEmpty()) {
loggerInfoMap.put(logger.getName(), info);
}
} else {
loggerInfoMap.put(logger.getName(), info);
}
}
}
Logger root = LogManager.getLoggerRepository().getRootLogger();
if (root != null) {
Map<String, Object> info = doGetLoggerInfo(root);
if (!includeNoAppender) {
List<?> appenders = (List<?>) info.get(LoggerHelper.appenders);
if (appenders != null && !appenders.isEmpty()) {
loggerInfoMap.put(root.getName(), info);
}
} else {
loggerInfoMap.put(root.getName(), info);
}
}
}
return loggerInfoMap;
}
private static Map<String, Object> doGetLoggerInfo(Logger logger) {
Map<String, Object> info = new HashMap<String, Object>();
info.put(LoggerHelper.name, logger.getName());
info.put(LoggerHelper.clazz, logger.getClass());
CodeSource codeSource = logger.getClass().getProtectionDomain().getCodeSource();
if (codeSource != null) {
info.put(LoggerHelper.codeSource, codeSource.getLocation());
}
info.put(LoggerHelper.additivity, logger.getAdditivity());
Level level = logger.getLevel(), effectiveLevel = logger.getEffectiveLevel();
if (level != null) {
info.put(LoggerHelper.level, level.toString());
}
if (effectiveLevel != null) {
info.put(LoggerHelper.effectiveLevel, effectiveLevel.toString());
}
@SuppressWarnings("unchecked")
List<Map<String, Object>> result = doGetLoggerAppenders(logger.getAllAppenders());
info.put(LoggerHelper.appenders, result);
return info;
}
private static List<Map<String, Object>> doGetLoggerAppenders(Enumeration<Appender> appenders) {
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
if (appenders == null) {
return result;
}
while (appenders.hasMoreElements()) {
Map<String, Object> info = new HashMap<String, Object>();
Appender appender = appenders.nextElement();
info.put(LoggerHelper.name, appender.getName());
info.put(LoggerHelper.clazz, appender.getClass());
result.add(info);
if (appender instanceof FileAppender) {
info.put(LoggerHelper.file, ((FileAppender) appender).getFile());
} else if (appender instanceof ConsoleAppender) {
info.put(LoggerHelper.target, ((ConsoleAppender) appender).getTarget());
} else if (appender instanceof AsyncAppender) {
@SuppressWarnings("unchecked")
Enumeration<Appender> appendersOfAsync = ((AsyncAppender) appender).getAllAppenders();
if (appendersOfAsync != null) {
List<Map<String, Object>> asyncs = doGetLoggerAppenders(appendersOfAsync);
// 标明异步appender
List<String> appenderRef = new ArrayList<String>();
for (Map<String, Object> a : asyncs) {
appenderRef.add((String) a.get(LoggerHelper.name));
result.add(a);
}
info.put(LoggerHelper.blocking, ((AsyncAppender) appender).getBlocking());
info.put(LoggerHelper.appenderRef, appenderRef);
}
}
}
return result;
}
}
package com.taobao.arthas.core.command.logger;
import java.lang.reflect.Field;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.ILoggerFactory;
import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.pattern.PatternLayoutBase;
/**
*
* @author hengyunabc 2019-09-06
*
*/
public class LogbackHelper {
private static boolean Logback = false;
private static Field headField, lengthOptionField;
private static ILoggerFactory loggerFactoryInstance;
static {
try {
Class<?> loggerClass = LogbackHelper.class.getClassLoader().loadClass("ch.qos.logback.classic.Logger");
// 这里可能会加载到应用中依赖的logback,因此需要判断classloader
if (loggerClass.getClassLoader().equals(LogbackHelper.class.getClassLoader())) {
ILoggerFactory loggerFactory = org.slf4j.LoggerFactory.getILoggerFactory();
if (loggerFactory instanceof LoggerContext) {
loggerFactoryInstance = loggerFactory;
headField = PatternLayoutBase.class.getDeclaredField("head");
headField.setAccessible(true);
lengthOptionField = ThrowableProxyConverter.class.getDeclaredField("lengthOption");
lengthOptionField.setAccessible(true);
Logback = true;
}
}
} catch (Throwable t) {
// ignore
}
}
public static Boolean updateLevel(String name, String level) {
if (Logback) {
try {
Level l = Level.toLevel(level, Level.ERROR);
LoggerContext loggerContext = (LoggerContext) loggerFactoryInstance;
Logger logger = loggerContext.exists(name);
if (logger != null) {
logger.setLevel(l);
return true;
}
} catch (Throwable t) {
// ignore
}
return false;
}
return null;
}
public static Map<String, Map<String, Object>> getLoggers(String name, boolean includeNoAppender) {
Map<String, Map<String, Object>> loggerInfoMap = new LinkedHashMap<String, Map<String, Object>>();
if (Logback) {
LoggerContext loggerContext = (LoggerContext) loggerFactoryInstance;
if (name != null && !name.trim().isEmpty()) {
Logger logger = loggerContext.exists(name);
if (logger != null) {
loggerInfoMap.put(name, doGetLoggerInfo(logger));
}
} else {
// 获取所有logger时,如果没有appender则忽略
List<Logger> loggers = loggerContext.getLoggerList();
for (Logger logger : loggers) {
Map<String, Object> info = doGetLoggerInfo(logger);
if (!includeNoAppender) {
List<?> appenders = (List<?>) info.get(LoggerHelper.appenders);
if (appenders != null && !appenders.isEmpty()) {
loggerInfoMap.put(logger.getName(), info);
}
} else {
loggerInfoMap.put(logger.getName(), info);
}
}
}
}
return loggerInfoMap;
}
private static Map<String, Object> doGetLoggerInfo(Logger logger) {
Map<String, Object> info = new LinkedHashMap<String, Object>();
info.put(LoggerHelper.name, logger.getName());
info.put(LoggerHelper.clazz, logger.getClass());
CodeSource codeSource = logger.getClass().getProtectionDomain().getCodeSource();
if (codeSource != null) {
info.put(LoggerHelper.codeSource, codeSource.getLocation());
}
info.put(LoggerHelper.additivity, logger.isAdditive());
Level level = logger.getLevel(), effectiveLevel = logger.getEffectiveLevel();
if (level != null) {
info.put(LoggerHelper.level, level.toString());
}
if (effectiveLevel != null) {
info.put(LoggerHelper.effectiveLevel, effectiveLevel.toString());
}
List<Map<String, Object>> result = doGetLoggerAppenders(logger.iteratorForAppenders());
info.put(LoggerHelper.appenders, result);
return info;
}
@SuppressWarnings("rawtypes")
private static List<Map<String, Object>> doGetLoggerAppenders(Iterator<Appender<ILoggingEvent>> appenders) {
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
while (appenders.hasNext()) {
Map<String, Object> info = new LinkedHashMap<String, Object>();
Appender<ILoggingEvent> appender = appenders.next();
info.put(LoggerHelper.name, appender.getName());
info.put(LoggerHelper.clazz, appender.getClass());
if (appender instanceof FileAppender) {
info.put(LoggerHelper.file, ((FileAppender) appender).getFile());
} else if (appender instanceof AsyncAppender) {
AsyncAppender aa = (AsyncAppender) appender;
Iterator<Appender<ILoggingEvent>> iter = aa.iteratorForAppenders();
List<Map<String, Object>> asyncs = doGetLoggerAppenders(iter);
// 异步appender所 ref的 appender,参考: https://logback.qos.ch/manual/appenders.html
List<String> appenderRef = new ArrayList<String>();
for (Map<String, Object> a : asyncs) {
appenderRef.add((String) a.get(LoggerHelper.name));
result.add(a);
}
info.put(LoggerHelper.appenderRef, appenderRef);
info.put(LoggerHelper.blocking, !aa.isNeverBlock());
} else if (appender instanceof ConsoleAppender) {
info.put(LoggerHelper.target, ((ConsoleAppender) appender).getTarget());
}
result.add(info);
}
return result;
}
}
package com.taobao.arthas.core.command.logger;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.common.IOUtils;
import com.taobao.arthas.common.ReflectUtils;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.LoggerModel;
import com.taobao.arthas.core.command.model.ClassLoaderVO;
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.arthas.core.util.StringUtils;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
/**
* logger command
*
* @author hengyunabc 2019-09-04
*/
//@formatter:off
@Name("logger")
@Summary("Print logger info, and update the logger level")
@Description("\nExamples:\n"
+ " logger\n"
+ " logger -c 327a647b\n"
+ " logger -c 327a647b --name ROOT --level debug\n"
+ " logger --include-no-appender\n"
+ Constants.WIKI + Constants.WIKI_HOME + "logger")
//@formatter:on
public class LoggerCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(LoggerCommand.class);
private static byte[] LoggerHelperBytes;
private static byte[] Log4jHelperBytes;
private static byte[] LogbackHelperBytes;
private static byte[] Log4j2HelperBytes;
private static Map<Class<?>, byte[]> classToBytesMap = new HashMap<Class<?>, byte[]>();
private static String arthasClassLoaderHash = ClassLoaderUtils
.classLoaderHash(LoggerCommand.class.getClassLoader());
static {
LoggerHelperBytes = loadClassBytes(LoggerHelper.class);
Log4jHelperBytes = loadClassBytes(Log4jHelper.class);
LogbackHelperBytes = loadClassBytes(LogbackHelper.class);
Log4j2HelperBytes = loadClassBytes(Log4j2Helper.class);
classToBytesMap.put(LoggerHelper.class, LoggerHelperBytes);
classToBytesMap.put(Log4jHelper.class, Log4jHelperBytes);
classToBytesMap.put(LogbackHelper.class, LogbackHelperBytes);
classToBytesMap.put(Log4j2Helper.class, Log4j2HelperBytes);
}
private String name;
private String hashCode;
private String classLoaderClass;
private String level;
/**
* include the loggers which don't have appenders, default false.
*/
private boolean includeNoAppender;
@Option(shortName = "n", longName = "name")
@Description("logger name")
public void setName(String name) {
this.name = name;
}
@Option(shortName = "c", longName = "classloader")
@Description("classLoader hashcode, if no value is set, default value is SystemClassLoader")
public void setHashCode(String 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 = "l", longName = "level")
@Description("set logger level")
public void setLevel(String level) {
this.level = level;
}
@Option(longName = "include-no-appender", flag = true)
@Description("include the loggers which don't have appenders, default value false")
public void setHaveAppender(boolean includeNoAppender) {
this.includeNoAppender = includeNoAppender;
}
@Override
public void process(CommandProcess process) {
// 每个分支中调用process.end()结束执行
if (this.name != null && this.level != null) {
level(process);
} else {
loggers(process);
}
}
public void level(CommandProcess process) {
Instrumentation inst = process.session().getInstrumentation();
boolean result = false;
try {
Boolean updateResult = this.updateLevel(inst, Log4jHelper.class);
if (Boolean.TRUE.equals(updateResult)) {
result = true;
}
} catch (Throwable e) {
logger.error("logger command update log4j level error", e);
}
try {
Boolean updateResult = this.updateLevel(inst, LogbackHelper.class);
if (Boolean.TRUE.equals(updateResult)) {
result = true;
}
} catch (Throwable e) {
logger.error("logger command update logback level error", e);
}
try {
Boolean updateResult = this.updateLevel(inst, Log4j2Helper.class);
if (Boolean.TRUE.equals(updateResult)) {
result = true;
}
} catch (Throwable e) {
logger.error("logger command update log4j2 level error", e);
}
if (result) {
process.end(0, "Update logger level success.");
} else {
process.end(-1, "Update logger level fail. Try to specify the classloader with the -c option. Use `sc -d CLASSNAME` to find out the classloader hashcode.");
}
}
public void loggers(CommandProcess process) {
Map<ClassLoader, LoggerTypes> classLoaderLoggerMap = new LinkedHashMap<ClassLoader, LoggerTypes>();
for (Class<?> clazz : process.session().getInstrumentation().getAllLoadedClasses()) {
String className = clazz.getName();
ClassLoader classLoader = clazz.getClassLoader();
if (hashCode == null && classLoaderClass != null) {
Instrumentation inst = process.session().getInstrumentation();
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);
LoggerModel loggerModel = new LoggerModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(loggerModel);
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;
}
}
// if special classloader
if (this.hashCode != null && !this.hashCode.equals(StringUtils.classLoaderHash(clazz))) {
continue;
}
if (classLoader != null) {
LoggerTypes loggerTypes = classLoaderLoggerMap.get(classLoader);
if (loggerTypes == null) {
loggerTypes = new LoggerTypes();
classLoaderLoggerMap.put(classLoader, loggerTypes);
}
if ("org.apache.log4j.Logger".equals(className)) {
loggerTypes.addType(LoggerType.LOG4J);
} else if ("ch.qos.logback.classic.Logger".equals(className)) {
loggerTypes.addType(LoggerType.LOGBACK);
} else if ("org.apache.logging.log4j.Logger".equals(className)) {
loggerTypes.addType(LoggerType.LOG4J2);
}
}
}
for (Entry<ClassLoader, LoggerTypes> entry : classLoaderLoggerMap.entrySet()) {
ClassLoader classLoader = entry.getKey();
LoggerTypes loggerTypes = entry.getValue();
if (loggerTypes.contains(LoggerType.LOG4J)) {
Map<String, Map<String, Object>> loggerInfoMap = loggerInfo(classLoader, Log4jHelper.class);
process.appendResult(new LoggerModel(loggerInfoMap));
}
if (loggerTypes.contains(LoggerType.LOGBACK)) {
Map<String, Map<String, Object>> loggerInfoMap = loggerInfo(classLoader, LogbackHelper.class);
process.appendResult(new LoggerModel(loggerInfoMap));
}
if (loggerTypes.contains(LoggerType.LOG4J2)) {
Map<String, Map<String, Object>> loggerInfoMap = loggerInfo(classLoader, Log4j2Helper.class);
process.appendResult(new LoggerModel(loggerInfoMap));
}
}
process.end();
}
private static String helperClassNameWithClassLoader(ClassLoader classLoader, Class<?> helperClass) {
String classLoaderHash = ClassLoaderUtils.classLoaderHash(classLoader);
String className = helperClass.getName();
// if want to debug, change to return className
return className + arthasClassLoaderHash + classLoaderHash;
}
@SuppressWarnings("unchecked")
private Map<String, Map<String, Object>> loggerInfo(ClassLoader classLoader, Class<?> helperClass) {
Map<String, Map<String, Object>> loggers = Collections.emptyMap();
String helperClassName = helperClassNameWithClassLoader(classLoader, helperClass);
try {
classLoader.loadClass(helperClassName);
} catch (ClassNotFoundException e) {
try {
byte[] helperClassBytes = AsmRenameUtil.renameClass(classToBytesMap.get(helperClass),
helperClass.getName(), helperClassName);
ReflectUtils.defineClass(helperClassName, helperClassBytes, classLoader);
} catch (Throwable e1) {
logger.error("arthas loggger command try to define helper class error: " + helperClassName,
e1);
}
}
try {
Class<?> clazz = classLoader.loadClass(helperClassName);
Method getLoggersMethod = clazz.getMethod("getLoggers", new Class<?>[]{String.class, boolean.class});
loggers = (Map<String, Map<String, Object>>) getLoggersMethod.invoke(null,
new Object[]{name, includeNoAppender});
} catch (Throwable e) {
// ignore
}
//expose attributes to json: classloader, classloaderHash
for (Map<String, Object> loggerInfo : loggers.values()) {
Class clazz = (Class) loggerInfo.get(LoggerHelper.clazz);
loggerInfo.put(LoggerHelper.classLoader, getClassLoaderName(clazz.getClassLoader()));
loggerInfo.put(LoggerHelper.classLoaderHash, StringUtils.classLoaderHash(clazz));
List<Map<String, Object>> appenders = (List<Map<String, Object>>) loggerInfo.get(LoggerHelper.appenders);
for (Map<String, Object> appenderInfo : appenders) {
Class appenderClass = (Class) appenderInfo.get(LoggerHelper.clazz);
if (appenderClass != null) {
appenderInfo.put(LoggerHelper.classLoader, getClassLoaderName(appenderClass.getClassLoader()));
appenderInfo.put(LoggerHelper.classLoaderHash, StringUtils.classLoaderHash(appenderClass));
}
}
}
return loggers;
}
private String getClassLoaderName(ClassLoader classLoader) {
return classLoader == null ? null : classLoader.toString();
}
private Boolean updateLevel(Instrumentation inst, Class<?> helperClass) throws Exception {
ClassLoader classLoader = null;
if (hashCode == null) {
classLoader = ClassLoader.getSystemClassLoader();
} else {
classLoader = ClassLoaderUtils.getClassLoader(inst, hashCode);
}
Class<?> clazz = classLoader.loadClass(helperClassNameWithClassLoader(classLoader, helperClass));
Method updateLevelMethod = clazz.getMethod("updateLevel", new Class<?>[]{String.class, String.class});
return (Boolean) updateLevelMethod.invoke(null, new Object[]{this.name, this.level});
}
static enum LoggerType {
LOG4J, LOGBACK, LOG4J2
}
static class LoggerTypes {
Set<LoggerType> types = new HashSet<LoggerType>();
public Collection<LoggerType> types() {
return types;
}
public void addType(LoggerType type) {
types.add(type);
}
public boolean contains(LoggerType type) {
return types.contains(type);
}
}
private static byte[] loadClassBytes(Class<?> clazz) {
try {
InputStream stream = LoggerCommand.class.getClassLoader()
.getResourceAsStream(clazz.getName().replace('.', '/') + ".class");
return IOUtils.getBytes(stream);
} catch (IOException e) {
// ignore
return null;
}
}
}
package com.taobao.arthas.core.command.logger;
/**
*
* @author hengyunabc 2019-09-06
*
*/
public interface LoggerHelper {
public static final String clazz = "class";
public static final String classLoader = "classLoader";
public static final String classLoaderHash = "classLoaderHash";
public static final String codeSource = "codeSource";
// logger info
public static final String level = "level";
public static final String effectiveLevel = "effectiveLevel";
// log4j2 only
public static final String config = "config";
// type boolean
public static final String additivity = "additivity";
public static final String appenders = "appenders";
// appender info
public static final String name = "name";
public static final String file = "file";
public static final String blocking = "blocking";
// type List<String>
public static final String appenderRef = "appenderRef";
public static final String target = "target";
}
package com.taobao.arthas.core.command.model;
/**
* @author gongdewei 2020/4/3
*/
public class ArgumentVO {
private String argName;
private boolean required;
private boolean multiValued;
public ArgumentVO() {
}
public ArgumentVO(String argName, boolean required, boolean multiValued) {
this.argName = argName;
this.required = required;
this.multiValued = multiValued;
}
public String getArgName() {
return argName;
}
public void setArgName(String argName) {
this.argName = argName;
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
public boolean isMultiValued() {
return multiValued;
}
public void setMultiValued(boolean multiValued) {
this.multiValued = multiValued;
}
}
package com.taobao.arthas.core.command.model;
/**
*
* @author hengyunabc 2021-01-05
*
*/
public class Base64Model extends ResultModel {
private String content;
public Base64Model() {
}
public Base64Model(String content) {
this.content = content;
}
@Override
public String getType() {
return "base64";
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
package com.taobao.arthas.core.command.model;
import java.lang.management.ThreadInfo;
/**
* Thread blocking lock info, extract from ThreadUtil.
*
* @author gongdewei 2020/7/14
*/
public class BlockingLockInfo {
// the thread info that is holing this lock.
private ThreadInfo threadInfo = null;
// the associated LockInfo object
private int lockIdentityHashCode = 0;
// the number of thread that is blocked on this lock
private int blockingThreadCount = 0;
public BlockingLockInfo() {
}
public ThreadInfo getThreadInfo() {
return threadInfo;
}
public void setThreadInfo(ThreadInfo threadInfo) {
this.threadInfo = threadInfo;
}
public int getLockIdentityHashCode() {
return lockIdentityHashCode;
}
public void setLockIdentityHashCode(int lockIdentityHashCode) {
this.lockIdentityHashCode = lockIdentityHashCode;
}
public int getBlockingThreadCount() {
return blockingThreadCount;
}
public void setBlockingThreadCount(int blockingThreadCount) {
this.blockingThreadCount = blockingThreadCount;
}
}
package com.taobao.arthas.core.command.model;
import java.lang.management.LockInfo;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
/**
* Busy thread info, include ThreadInfo fields
*
* @author gongdewei 2020/4/26
*/
public class BusyThreadInfo extends ThreadVO {
private long blockedTime;
private long blockedCount;
private long waitedTime;
private long waitedCount;
private LockInfo lockInfo;
private String lockName;
private long lockOwnerId;
private String lockOwnerName;
private boolean inNative;
private boolean suspended;
private StackTraceElement[] stackTrace;
private MonitorInfo[] lockedMonitors;
private LockInfo[] lockedSynchronizers;
public BusyThreadInfo(ThreadVO thread, ThreadInfo threadInfo) {
this.setId(thread.getId());
this.setName(thread.getName());
this.setDaemon(thread.isDaemon());
this.setInterrupted(thread.isInterrupted());
this.setPriority(thread.getPriority());
this.setGroup(thread.getGroup());
this.setState(thread.getState());
this.setCpu(thread.getCpu());
this.setDeltaTime(thread.getDeltaTime());
this.setTime(thread.getTime());
//thread info
if (threadInfo != null) {
this.setLockInfo(threadInfo.getLockInfo());
this.setLockedMonitors(threadInfo.getLockedMonitors());
this.setLockedSynchronizers(threadInfo.getLockedSynchronizers());
this.setLockName(threadInfo.getLockName());
this.setLockOwnerId(threadInfo.getLockOwnerId());
this.setLockOwnerName(threadInfo.getLockOwnerName());
this.setStackTrace(threadInfo.getStackTrace());
this.setBlockedCount(threadInfo.getBlockedCount());
this.setBlockedTime(threadInfo.getBlockedTime());
this.setInNative(threadInfo.isInNative());
this.setSuspended(threadInfo.isSuspended());
this.setWaitedCount(threadInfo.getWaitedCount());
this.setWaitedTime(threadInfo.getWaitedTime());
}
}
public long getBlockedTime() {
return blockedTime;
}
public void setBlockedTime(long blockedTime) {
this.blockedTime = blockedTime;
}
public long getBlockedCount() {
return blockedCount;
}
public void setBlockedCount(long blockedCount) {
this.blockedCount = blockedCount;
}
public long getWaitedTime() {
return waitedTime;
}
public void setWaitedTime(long waitedTime) {
this.waitedTime = waitedTime;
}
public long getWaitedCount() {
return waitedCount;
}
public void setWaitedCount(long waitedCount) {
this.waitedCount = waitedCount;
}
public LockInfo getLockInfo() {
return lockInfo;
}
public void setLockInfo(LockInfo lockInfo) {
this.lockInfo = lockInfo;
}
public String getLockName() {
return lockName;
}
public void setLockName(String lockName) {
this.lockName = lockName;
}
public long getLockOwnerId() {
return lockOwnerId;
}
public void setLockOwnerId(long lockOwnerId) {
this.lockOwnerId = lockOwnerId;
}
public String getLockOwnerName() {
return lockOwnerName;
}
public void setLockOwnerName(String lockOwnerName) {
this.lockOwnerName = lockOwnerName;
}
public boolean isInNative() {
return inNative;
}
public void setInNative(boolean inNative) {
this.inNative = inNative;
}
public boolean isSuspended() {
return suspended;
}
public void setSuspended(boolean suspended) {
this.suspended = suspended;
}
public StackTraceElement[] getStackTrace() {
return stackTrace;
}
public void setStackTrace(StackTraceElement[] stackTrace) {
this.stackTrace = stackTrace;
}
public MonitorInfo[] getLockedMonitors() {
return lockedMonitors;
}
public void setLockedMonitors(MonitorInfo[] lockedMonitors) {
this.lockedMonitors = lockedMonitors;
}
public LockInfo[] getLockedSynchronizers() {
return lockedSynchronizers;
}
public void setLockedSynchronizers(LockInfo[] lockedSynchronizers) {
this.lockedSynchronizers = lockedSynchronizers;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment