tokens = completion.lineTokens();
+ /**
+ *
+ * 比如 ` --name a`,这样子的tokens
+ *
+ */
+ if (tokens.size() >= 3) {
+ CliToken cliToken_2 = tokens.get(tokens.size() - 2);
+ CliToken cliToken_3 = tokens.get(tokens.size() - 3);
+
+ if (cliToken_2.isBlank()) {
+ String token_3 = cliToken_3.value();
+
+ for (OptionCompleteHandler handler : handlers) {
+ if (handler.matchName(token_3)) {
+ return handler.complete(completion);
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ * 比如 ` --name `,这样子的tokens
+ *
+ */
+ if (tokens.size() >= 2) {
+ CliToken cliToken_1 = tokens.get(tokens.size() - 1);
+ CliToken cliToken_2 = tokens.get(tokens.size() - 2);
+ if (cliToken_1.isBlank()) {
+ String token_2 = cliToken_2.value();
+ for (OptionCompleteHandler handler : handlers) {
+ if (handler.matchName(token_2)) {
+ return handler.complete(completion);
+ }
+ }
+ }
+ }
+
+ return false;
+ }
}
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/cli/OptionCompleteHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/cli/OptionCompleteHandler.java
new file mode 100644
index 000000000..48778164f
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/shell/cli/OptionCompleteHandler.java
@@ -0,0 +1,12 @@
+package com.taobao.arthas.core.shell.cli;
+
+/**
+ *
+ * @author hengyunabc 2021-04-29
+ *
+ */
+public interface OptionCompleteHandler {
+ boolean matchName(String token);
+
+ boolean complete(Completion completion);
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java b/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java
index 0d6ca77ae..800f15960 100644
--- a/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java
+++ b/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java
@@ -181,7 +181,7 @@ public class ClassUtils {
return list.toArray(new String[0]);
}
- public static List createClassVOList(Set> matchedClasses) {
+ public static List createClassVOList(Collection> matchedClasses) {
List classVOs = new ArrayList(matchedClasses.size());
for (Class> aClass : matchedClasses) {
ClassVO classVO = createSimpleClassInfo(aClass);
diff --git a/core/src/test/java/com/taobao/arthas/core/util/LogUtilTest.java b/core/src/test/java/com/taobao/arthas/core/util/LogUtilTest.java
index 483497395..8cb3d153f 100644
--- a/core/src/test/java/com/taobao/arthas/core/util/LogUtilTest.java
+++ b/core/src/test/java/com/taobao/arthas/core/util/LogUtilTest.java
@@ -1,6 +1,7 @@
package com.taobao.arthas.core.util;
import java.io.File;
+import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.Properties;
@@ -72,11 +73,10 @@ public class LogUtilTest {
}
@Test
- public void test_DefaultLogFile() throws URISyntaxException {
+ public void test_DefaultLogFile() throws URISyntaxException, IOException {
Properties properties1 = new Properties();
properties1.put("arthas.home", testResourcesDir);
-
- String logFile = new File(System.getProperty("user.home"), "logs/arthas/arthas.log").getAbsolutePath();
+ String logFile = new File(System.getProperty("user.home"), "logs/arthas/arthas.log").getCanonicalPath();
arthasEnvironment.addLast(new PropertiesPropertySource("test1", properties1));
@@ -95,7 +95,7 @@ public class LogUtilTest {
if (appender instanceof RollingFileAppender) {
RollingFileAppender fileAppender = (RollingFileAppender) appender;
String file = fileAppender.getFile();
- Assertions.assertThat(file).isEqualTo(logFile);
+ Assertions.assertThat(new File(file).getCanonicalPath()).isEqualTo(logFile);
foundFileAppender = true;
}
}
@@ -103,11 +103,11 @@ public class LogUtilTest {
}
@Test
- public void test_ARTHAS_LOG_FILE() throws URISyntaxException {
+ public void test_ARTHAS_LOG_FILE() throws URISyntaxException, IOException {
Properties properties1 = new Properties();
properties1.put("arthas.home", testResourcesDir);
- String logFile = new File(tempFolder.getRoot().getAbsoluteFile(), "test.log").getAbsolutePath();
+ String logFile = new File(tempFolder.getRoot().getAbsoluteFile(), "test.log").getCanonicalPath();
properties1.put(LogUtil.FILE_NAME_PROPERTY, logFile);
arthasEnvironment.addLast(new PropertiesPropertySource("test1", properties1));
@@ -127,7 +127,7 @@ public class LogUtilTest {
if (appender instanceof RollingFileAppender) {
RollingFileAppender fileAppender = (RollingFileAppender) appender;
String file = fileAppender.getFile();
- Assertions.assertThat(file).isEqualTo(logFile);
+ Assertions.assertThat(new File(file).getCanonicalPath()).isEqualTo(logFile);
foundFileAppender = true;
}
}
@@ -135,11 +135,11 @@ public class LogUtilTest {
}
@Test
- public void test_ARTHAS_LOG_PATH() throws URISyntaxException {
+ public void test_ARTHAS_LOG_PATH() throws URISyntaxException, IOException {
Properties properties1 = new Properties();
properties1.put("arthas.home", testResourcesDir);
- String logFile = new File(tempFolder.getRoot().getAbsoluteFile(), "arthas.log").getAbsolutePath();
+ String logFile = new File(tempFolder.getRoot().getAbsoluteFile(), "arthas.log").getCanonicalPath();
properties1.put(LogUtil.FILE_PATH_PROPERTY, tempFolder.getRoot().getAbsolutePath());
arthasEnvironment.addLast(new PropertiesPropertySource("test1", properties1));
@@ -159,7 +159,7 @@ public class LogUtilTest {
if (appender instanceof RollingFileAppender) {
RollingFileAppender fileAppender = (RollingFileAppender) appender;
String file = fileAppender.getFile();
- Assertions.assertThat(file).isEqualTo(logFile);
+ Assertions.assertThat(new File(file).getCanonicalPath()).isEqualTo(logFile);
foundFileAppender = true;
}
}
diff --git a/lib/libArthasJniLibrary-x64.dll b/lib/libArthasJniLibrary-x64.dll
new file mode 100644
index 000000000..e53a26f3e
Binary files /dev/null and b/lib/libArthasJniLibrary-x64.dll differ
diff --git a/lib/libArthasJniLibrary-x64.dylib b/lib/libArthasJniLibrary-x64.dylib
new file mode 100644
index 000000000..d2d9430fc
Binary files /dev/null and b/lib/libArthasJniLibrary-x64.dylib differ
diff --git a/lib/libArthasJniLibrary-x64.so b/lib/libArthasJniLibrary-x64.so
new file mode 100644
index 000000000..b90076e72
Binary files /dev/null and b/lib/libArthasJniLibrary-x64.so differ
diff --git a/packaging/src/main/assembly/assembly.xml b/packaging/src/main/assembly/assembly.xml
index f3e8ad630..121523e78 100644
--- a/packaging/src/main/assembly/assembly.xml
+++ b/packaging/src/main/assembly/assembly.xml
@@ -63,5 +63,8 @@
../async-profiler
+
+ ../lib
+
diff --git a/pom.xml b/pom.xml
index 96a592214..9dd037c17 100644
--- a/pom.xml
+++ b/pom.xml
@@ -57,8 +57,9 @@
math-game
- spy
common
+ spy
+ arthas-vmtool
tunnel-common
tunnel-client
core
diff --git a/site/src/site/sphinx/advanced-use.md b/site/src/site/sphinx/advanced-use.md
index bb9c0c7ee..c17df1986 100644
--- a/site/src/site/sphinx/advanced-use.md
+++ b/site/src/site/sphinx/advanced-use.md
@@ -35,7 +35,7 @@
* [ognl](ognl.md)——执行ognl表达式
* [mbean](mbean.md)——查看 Mbean 的信息
* [heapdump](heapdump.md)——dump java heap, 类似jmap命令的heap dump功能
-
+* [vmtool](vmtool.md)——从jvm里查询对象,执行forceGc
## class/classloader相关
diff --git a/site/src/site/sphinx/commands.md b/site/src/site/sphinx/commands.md
index 9a6e06b59..c51bcb949 100644
--- a/site/src/site/sphinx/commands.md
+++ b/site/src/site/sphinx/commands.md
@@ -18,6 +18,7 @@
* [sm](sm.md)
* [dump](dump.md)
* [heapdump](heapdump.md)
+* [vmtool](vmtool.md)
* [jad](jad.md)
* [classloader](classloader.md)
diff --git a/site/src/site/sphinx/en/advanced-use.md b/site/src/site/sphinx/en/advanced-use.md
index 2479f14be..dba45d449 100644
--- a/site/src/site/sphinx/en/advanced-use.md
+++ b/site/src/site/sphinx/en/advanced-use.md
@@ -33,7 +33,7 @@ Advanced Usage
* [ognl](ognl.md) - execute ognl expression
* [mbean](mbean.md) - show Mbean information
* [heapdump](heapdump.md) - dump java heap in hprof binary format, like `jmap`
-
+* [vmtool](vmtool.md) - jvm tool, getInstances in jvm, forceGc
## class/classloader
* [sc](sc.md) - check the info for the classes loaded by JVM
diff --git a/site/src/site/sphinx/en/commands.md b/site/src/site/sphinx/en/commands.md
index e997ca785..a4a3eeae3 100644
--- a/site/src/site/sphinx/en/commands.md
+++ b/site/src/site/sphinx/en/commands.md
@@ -18,6 +18,7 @@ All Commands
* [sm](sm.md)
* [dump](dump.md)
* [heapdump](heapdump.md)
+* [vmtool](vmtool.md)
* [jad](jad.md)
* [classloader](classloader.md)
diff --git a/site/src/site/sphinx/en/vmtool.md b/site/src/site/sphinx/en/vmtool.md
new file mode 100644
index 000000000..b30dde9db
--- /dev/null
+++ b/site/src/site/sphinx/en/vmtool.md
@@ -0,0 +1,82 @@
+vmtool
+===
+
+> @since 3.5.1
+[`vmtool` online tutorial](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=en&id=command-vmtool)
+
+`vmtool` uses the `JVMTI` to support `getInstances` in jvm and `forceGc`.
+
+* [JVM Tool Interface](https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html)
+
+### getInstances
+
+```bash
+$ vmtool --action getInstances --className java.lang.String --limit 10
+@String[][
+ @String[com/taobao/arthas/core/shell/session/Session],
+ @String[com.taobao.arthas.core.shell.session.Session],
+ @String[com/taobao/arthas/core/shell/session/Session],
+ @String[com/taobao/arthas/core/shell/session/Session],
+ @String[com/taobao/arthas/core/shell/session/Session.class],
+ @String[com/taobao/arthas/core/shell/session/Session.class],
+ @String[com/taobao/arthas/core/shell/session/Session.class],
+ @String[com/],
+ @String[java/util/concurrent/ConcurrentHashMap$ValueIterator],
+ @String[java/util/concurrent/locks/LockSupport],
+]
+```
+
+> Through the `--limit` parameter, you can limit the number of return values to avoid pressure on the JVM when obtaining large data. The default value of limit is 10.
+
+### Specify classloader name
+
+```bash
+vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext
+```
+
+
+### Specify classloader hash
+
+The classloader that loads the class can be found through the `sc` command.
+
+```bash
+$ sc -d org.springframework.context.ApplicationContext
+ class-info org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
+ code-source file:/private/tmp/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/
+ name org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
+...
+ class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+ +-sun.misc.Launcher$AppClassLoader@75b84c92
+ +-sun.misc.Launcher$ExtClassLoader@4f023edb
+ classLoaderHash 19469ea2
+```
+
+Then use the `-c`/`--classloader` parameter to specify:
+
+```bash
+vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext
+```
+
+### Specify the number of expanded layers of returned results
+
+> The return result of the `getInstances` action is bound to the `instances` variable, which is an array.
+
+> The expansion level of the result can be specified by the `-x`/`--expand` parameter, the default value is 1.
+
+```bash
+vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext -x 2
+```
+
+### Execute expression
+
+> The return result of the `getInstances` action is bound to the `instances` variable, which is an array. The specified expression can be executed through the `--express` parameter.
+
+```bash
+vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext --express'instances[0].getBeanDefinitionNames()'
+```
+
+### Force GC
+
+```bash
+vmtool --action forceGc
+```
\ No newline at end of file
diff --git a/site/src/site/sphinx/vmtool.md b/site/src/site/sphinx/vmtool.md
new file mode 100644
index 000000000..59ab1f514
--- /dev/null
+++ b/site/src/site/sphinx/vmtool.md
@@ -0,0 +1,83 @@
+vmtool
+===
+
+> @since 3.5.1
+
+[`vmtool`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-vmtool)
+
+`vmtool` 利用`JVMTI`接口,实现查询内存对象,强制GC等功能。
+
+* [JVM Tool Interface](https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html)
+
+### 获取对象
+
+```bash
+$ vmtool --action getInstances --className java.lang.String --limit 10
+@String[][
+ @String[com/taobao/arthas/core/shell/session/Session],
+ @String[com.taobao.arthas.core.shell.session.Session],
+ @String[com/taobao/arthas/core/shell/session/Session],
+ @String[com/taobao/arthas/core/shell/session/Session],
+ @String[com/taobao/arthas/core/shell/session/Session.class],
+ @String[com/taobao/arthas/core/shell/session/Session.class],
+ @String[com/taobao/arthas/core/shell/session/Session.class],
+ @String[com/],
+ @String[java/util/concurrent/ConcurrentHashMap$ValueIterator],
+ @String[java/util/concurrent/locks/LockSupport],
+]
+```
+
+> 通过 `--limit`参数,可以限制返回值数量,避免获取超大数据时对JVM造成压力。默认值是10。
+
+### 指定 classloader name
+
+```bash
+vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext
+```
+
+
+### 指定 classloader hash
+
+可以通过`sc`命令查找到加载class的 classloader。
+
+```bash
+$ sc -d org.springframework.context.ApplicationContext
+ class-info org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
+ code-source file:/private/tmp/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/
+ name org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
+...
+ class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2
+ +-sun.misc.Launcher$AppClassLoader@75b84c92
+ +-sun.misc.Launcher$ExtClassLoader@4f023edb
+ classLoaderHash 19469ea2
+```
+
+然后用`-c`/`--classloader` 参数指定:
+
+```bash
+vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext
+```
+
+### 指定返回结果展开层数
+
+> `getInstances` action返回结果绑定到`instances`变量上,它是数组。
+
+> 通过 `-x`/`--expand` 参数可以指定结果的展开层次,默认值是1。
+
+```bash
+vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext -x 2
+```
+
+### 执行表达式
+
+> `getInstances` action返回结果绑定到`instances`变量上,它是数组。可以通过`--express`参数执行指定的表达式。
+
+```bash
+vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext --express 'instances[0].getBeanDefinitionNames()'
+```
+
+### 强制GC
+
+```bash
+vmtool --action forceGc
+```
\ No newline at end of file
diff --git a/spy/pom.xml b/spy/pom.xml
index 39722bc4d..b0ff9b411 100644
--- a/spy/pom.xml
+++ b/spy/pom.xml
@@ -10,6 +10,16 @@
arthas-spy
arthas-spy
+
+
+ com.taobao.arthas
+ arthas-common
+ ${project.version}
+ provided
+ true
+
+
+
arthas-spy