Skip to content

Commit

Permalink
add vmtool command, part1. alibaba#1781
Browse files Browse the repository at this point in the history
  • Loading branch information
hengyunabc committed Apr 29, 2021
1 parent 6a102d6 commit 932340e
Show file tree
Hide file tree
Showing 16 changed files with 482 additions and 27 deletions.
24 changes: 0 additions & 24 deletions arthas-vmtool/src/main/java/arthas/VmTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import java.util.ArrayList;

import com.taobao.arthas.common.OSUtils;

/**
* @author ZhangZiCheng 2021-02-12
* @author hengyunabc 2021-04-26
Expand All @@ -16,24 +14,6 @@ public class VmTool implements VmToolMXBean {
*/
public final static String JNI_LIBRARY_NAME = "ArthasJniLibrary";

private static String libName = null;
static {
if (OSUtils.isMac()) {
libName = "libArthasJniLibrary-x64.dylib";
}
if (OSUtils.isLinux()) {
libName = "libArthasJniLibrary-x64.so";
if (OSUtils.isArm32()) {
libName = "libArthasJniLibrary-arm.so";
} else if (OSUtils.isArm64()) {
libName = "libArthasJniLibrary-aarch64.so";
}
}
if (OSUtils.isWindows()) {
libName = "libArthasJniLibrary-x64.dll";
}
}

private static VmTool instance;

private VmTool() {
Expand All @@ -58,10 +38,6 @@ public static synchronized VmTool getInstance(String libPath) {
return instance;
}

public static String detectLibName() {
return libName;
}

/**
* 检测jni-lib是否正常,如果正常,应该输出OK
*/
Expand Down
6 changes: 6 additions & 0 deletions arthas-vmtool/src/main/java/arthas/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* <pre>
* 修改后要同步到 spy/src/main/java 。
* </pre>
*/
package arthas;
4 changes: 3 additions & 1 deletion arthas-vmtool/src/test/java/arthas/VmToolTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.junit.Test;

import com.taobao.arthas.common.VmToolUtils;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
Expand All @@ -25,7 +27,7 @@ public void test01() {
String path = VmTool.class.getProtectionDomain().getCodeSource().getLocation().getPath();
System.err.println(path);

String libPath = new File(path, VmTool.detectLibName()).getAbsolutePath();
String libPath = new File(path, VmToolUtils.detectLibName()).getAbsolutePath();
VmTool vmtool = VmTool.getInstance(libPath);

//调用native方法,获取已加载的类,不包括小类型(如int)
Expand Down
30 changes: 30 additions & 0 deletions common/src/main/java/com/taobao/arthas/common/VmToolUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.taobao.arthas.common;

/**
*
* @author hengyunabc 2021-04-27
*
*/
public class VmToolUtils {
private static String libName = null;
static {
if (OSUtils.isMac()) {
libName = "libArthasJniLibrary-x64.dylib";
}
if (OSUtils.isLinux()) {
libName = "libArthasJniLibrary-x64.so";
if (OSUtils.isArm32()) {
libName = "libArthasJniLibrary-arm.so";
} else if (OSUtils.isArm64()) {
libName = "libArthasJniLibrary-aarch64.so";
}
}
if (OSUtils.isWindows()) {
libName = "libArthasJniLibrary-x64.dll";
}
}

public static String detectLibName() {
return libName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import com.taobao.arthas.core.command.monitor200.ThreadCommand;
import com.taobao.arthas.core.command.monitor200.TimeTunnelCommand;
import com.taobao.arthas.core.command.monitor200.TraceCommand;
import com.taobao.arthas.core.command.monitor200.VmToolCommand;
import com.taobao.arthas.core.command.monitor200.WatchCommand;
import com.taobao.arthas.core.shell.command.Command;
import com.taobao.arthas.core.shell.command.CommandResolver;
Expand Down Expand Up @@ -113,6 +114,7 @@ private static void initCommands() {
commands.add(Command.create(GrepCommand.class));
commands.add(Command.create(TeeCommand.class));
commands.add(Command.create(ProfilerCommand.class));
commands.add(Command.create(VmToolCommand.class));
commands.add(Command.create(ShutdownCommand.class));
commands.add(Command.create(StopCommand.class));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
package com.taobao.arthas.core.command.monitor200;

import java.io.File;
import java.lang.instrument.Instrumentation;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
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.VmToolUtils;
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.SearchClassModel;
import com.taobao.arthas.core.shell.cli.Completion;
import com.taobao.arthas.core.shell.cli.CompletionUtils;
import com.taobao.arthas.core.shell.cli.OptionCompleteHandler;
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.arthas.core.view.ObjectView;
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 arthas.VmTool;

/**
*
* @author hengyunabc 2021-04-27
*
*/
//@formatter:off
@Name("vmtool")
@Summary("jvm tool")
@Description(Constants.EXAMPLE
+ " vmtool --action getInstances --className demo.MathGame\n"
+ " vmtool --action getInstances --className demo.MathGame --express 'instances.size()'\n"
+ Constants.WIKI + Constants.WIKI_HOME + "vmtool")
//@formatter:on
public class VmToolCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(VmToolCommand.class);

private VmToolAction action;
private String className;
private String express;

private String hashCode = null;
private String classLoaderClass;
/**
* default value 2
*/
private int expand;

private static String libPath;
private static VmTool vmTool = null;

static {
String libName = VmToolUtils.detectLibName();
if (libName != null) {
CodeSource codeSource = VmToolCommand.class.getProtectionDomain().getCodeSource();
if (codeSource != null) {
try {
File bootJarPath = new File(codeSource.getLocation().toURI().getSchemeSpecificPart());
File soFile = new File(bootJarPath.getParentFile(), "lib" + File.separator + libName);
if (soFile.exists()) {
libPath = soFile.getAbsolutePath();
}
} catch (Throwable e) {
logger.error("can not find VmTool so", e);
}
}
}

}

@Option(shortName = "a", longName = "action", required = true)
@Description("Action to execute")
public void setAction(VmToolAction action) {
this.action = action;
}

@Option(longName = "className")
@Description("The class name")
public void setClassName(String className) {
this.className = className;
}

@Option(shortName = "x", longName = "expand")
@Description("Expand level of object (2 by default)")
@DefaultValue("2")
public void setExpand(int 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(longName = "express", required = false)
@Description("The ognl expression, default valueis `instances`.")
public void setExpress(String express) {
this.express = express;
}

public enum VmToolAction {
getInstances, load
}

@Override
public void process(final CommandProcess process) {
try {
Instrumentation inst = process.session().getInstrumentation();

if (VmToolAction.getInstances.equals(action)) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
if (hashCode == null && classLoaderClass != null) {
List<ClassLoader> matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst,
classLoaderClass);
if (matchedClassLoaders.size() == 1) {
classLoader = matchedClassLoaders.get(0);
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, className, false, hashCode));
int matchedClassSize = matchedClasses.size();
if (matchedClassSize == 0) {
process.end(-1, "Can not find class by class name: " + className + ".");
return;
} else if (matchedClassSize > 1) {
process.end(-1, "Found more than one class: " + matchedClasses + ".");
return;
} else {
ArrayList<?> instances = vmToolInstance().getInstances(matchedClasses.get(0));
Object value = instances;
if (express != null) {
Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader);
try {
value = unpooledExpress.bind(new InstancesWrapper(instances)).get(express);
} 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. ");
}
}

process.write(new ObjectView(value, this.expand).draw());
process.end();
}
}

process.end();
} catch (Throwable e) {
logger.error("vmtool error", e);
process.end(1, "vmtool error: " + e.getMessage());
}
}

static class InstancesWrapper {
Object instances;

public InstancesWrapper(Object instances) {
this.instances = instances;
}

public Object getInstances() {
return instances;
}

public void setInstances(Object instances) {
this.instances = instances;
}
}

private VmTool vmToolInstance() {
if (vmTool != null) {
return vmTool;
} else {
vmTool = VmTool.getInstance(libPath);
}
return vmTool;
}

private Set<String> actions() {
Set<String> values = new HashSet<String>();
for (VmToolAction action : VmToolAction.values()) {
values.add(action.toString());
}
return values;
}

@Override
public void complete(Completion completion) {
List<OptionCompleteHandler> handlers = new ArrayList<OptionCompleteHandler>();

handlers.add(new OptionCompleteHandler() {

@Override
public boolean matchName(String token) {
return "-a".equals(token) || "--action".equals(token);
}

@Override
public boolean complete(Completion completion) {
return CompletionUtils.complete(completion, actions());
}

});

handlers.add(new OptionCompleteHandler() {
@Override
public boolean matchName(String token) {
return "--className".equals(token);
}

@Override
public boolean complete(Completion completion) {
return CompletionUtils.completeClassName(completion);
}
});

if (CompletionUtils.completeOptions(completion, handlers)) {
return;
}

super.complete(completion);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public static String[] getClassNameList(Class[] classes) {
return list.toArray(new String[0]);
}

public static List<ClassVO> createClassVOList(Set<Class<?>> matchedClasses) {
public static List<ClassVO> createClassVOList(Collection<Class<?>> matchedClasses) {
List<ClassVO> classVOs = new ArrayList<ClassVO>(matchedClasses.size());
for (Class<?> aClass : matchedClasses) {
ClassVO classVO = createSimpleClassInfo(aClass);
Expand Down
Binary file added lib/libArthasJniLibrary-x64.dll
Binary file not shown.
Binary file added lib/libArthasJniLibrary-x64.dylib
Binary file not shown.
Binary file added lib/libArthasJniLibrary-x64.so
Binary file not shown.
3 changes: 3 additions & 0 deletions packaging/src/main/assembly/assembly.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,8 @@
<fileSet>
<directory>../async-profiler</directory>
</fileSet>
<fileSet>
<directory>../lib</directory>
</fileSet>
</fileSets>
</assembly>
Loading

0 comments on commit 932340e

Please sign in to comment.