diff --git a/core/pom.xml b/core/pom.xml
index 32bfbee62..cce76380e 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -70,6 +70,11 @@
arthas-common
${project.version}
+
+ com.taobao.arthas
+ arthas-memorycompiler
+ ${project.version}
+
org.ow2.asm
asm
diff --git a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
index 712c97882..f9693c456 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
@@ -17,6 +17,7 @@ import com.taobao.arthas.core.command.klass100.ClassLoaderCommand;
import com.taobao.arthas.core.command.klass100.DumpClassCommand;
import com.taobao.arthas.core.command.klass100.GetStaticCommand;
import com.taobao.arthas.core.command.klass100.JadCommand;
+import com.taobao.arthas.core.command.klass100.MemoryCompilerCommand;
import com.taobao.arthas.core.command.klass100.OgnlCommand;
import com.taobao.arthas.core.command.klass100.RedefineCommand;
import com.taobao.arthas.core.command.klass100.SearchClassCommand;
@@ -69,6 +70,8 @@ public class BuiltinCommandPack implements CommandResolver {
commands.add(Command.create(JvmCommand.class));
// commands.add(Command.create(GroovyScriptCommand.class));
commands.add(Command.create(OgnlCommand.class));
+ commands.add(Command.create(MemoryCompilerCommand.class));
+ commands.add(Command.create(RedefineCommand.class));
commands.add(Command.create(DashboardCommand.class));
commands.add(Command.create(DumpClassCommand.class));
commands.add(Command.create(JulyCommand.class));
@@ -81,7 +84,6 @@ public class BuiltinCommandPack implements CommandResolver {
commands.add(Command.create(SessionCommand.class));
commands.add(Command.create(SystemPropertyCommand.class));
commands.add(Command.create(SystemEnvCommand.class));
- commands.add(Command.create(RedefineCommand.class));
commands.add(Command.create(HistoryCommand.class));
}
}
diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java
index f93189fc5..ca0db73ef 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java
@@ -65,7 +65,7 @@ public class ClassLoaderCommand extends AnnotatedCommand {
}
@Option(shortName = "c", longName = "classloader")
- @Description("Display ClassLoader urls")
+ @Description("The hash code of the special ClassLoader")
public void setHashCode(String hashCode) {
this.hashCode = hashCode;
}
diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/MemoryCompilerCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/MemoryCompilerCommand.java
new file mode 100644
index 000000000..01aad6a74
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/MemoryCompilerCommand.java
@@ -0,0 +1,131 @@
+package com.taobao.arthas.core.command.klass100;
+
+import java.io.File;
+import java.lang.instrument.Instrumentation;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.taobao.arthas.compiler.DynamicCompiler;
+import com.taobao.arthas.core.command.Constants;
+import com.taobao.arthas.core.shell.command.AnnotatedCommand;
+import com.taobao.arthas.core.shell.command.CommandProcess;
+import com.taobao.arthas.core.util.ClassLoaderUtils;
+import com.taobao.arthas.core.util.FileUtils;
+import com.taobao.arthas.core.util.LogUtil;
+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;
+import com.taobao.middleware.logger.Logger;
+
+/**
+ *
+ * @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 = LogUtil.getArthasLogger();
+
+ private String directory;
+ private String hashCode;
+ private String encoding;
+
+ private List sourcefiles;
+
+ @Argument(argName = "sourcefiles", index = 0)
+ @Description("source files")
+ public void setClassPattern(List 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 = "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(CommandProcess process) {
+ int exitCode = 0;
+ RowAffect affect = new RowAffect();
+
+ try {
+ Instrumentation inst = process.session().getInstrumentation();
+ ClassLoader classloader = null;
+ if (hashCode == null) {
+ classloader = ClassLoader.getSystemClassLoader();
+ } else {
+ classloader = ClassLoaderUtils.getClassLoader(inst, hashCode);
+ if (classloader == null) {
+ process.write("Can not find classloader with hashCode: " + hashCode + ".\n");
+ exitCode = -1;
+ 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 byteCodes = dynamicCompiler.buildByteCodes();
+
+ File outputDir = null;
+ if (this.directory != null) {
+ outputDir = new File(this.directory);
+ } else {
+ outputDir = new File("").getAbsoluteFile();
+ }
+
+ process.write("Memory compiler output:\n");
+ for (Entry entry : byteCodes.entrySet()) {
+ File byteCodeFile = new File(outputDir, entry.getKey().replace('.', '/') + ".class");
+ FileUtils.writeByteArrayToFile(byteCodeFile, entry.getValue());
+ process.write(byteCodeFile.getAbsolutePath() + '\n');
+ affect.rCnt(1);
+ }
+
+ } catch (Throwable e) {
+ logger.warn("Memory compiler error", e);
+ process.write("Memory compiler error, exception message: " + e.getMessage()
+ + ", please check $HOME/logs/arthas/arthas.log for more details. \n");
+ exitCode = -1;
+ } finally {
+ process.write(affect + "\n");
+ process.end(exitCode);
+ }
+ }
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java b/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java
new file mode 100644
index 000000000..7fd27ac37
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java
@@ -0,0 +1,41 @@
+package com.taobao.arthas.core.util;
+
+import java.lang.instrument.Instrumentation;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ * @author hengyunabc 2019-02-05
+ *
+ */
+public class ClassLoaderUtils {
+
+ public static Set getAllClassLoader(Instrumentation inst) {
+ Set classLoaderSet = new HashSet();
+
+ for (Class> clazz : inst.getAllLoadedClasses()) {
+ ClassLoader classLoader = clazz.getClassLoader();
+ if (classLoader != null) {
+ classLoaderSet.add(classLoader);
+ }
+ }
+ return classLoaderSet;
+ }
+
+ public static ClassLoader getClassLoader(Instrumentation inst, String hashCode) {
+ if (hashCode == null || hashCode.isEmpty()) {
+ return null;
+ }
+
+ for (Class> clazz : inst.getAllLoadedClasses()) {
+ ClassLoader classLoader = clazz.getClassLoader();
+ if (classLoader != null) {
+ if (Integer.toHexString(classLoader.hashCode()).equals(hashCode)) {
+ return classLoader;
+ }
+ }
+ }
+ return null;
+ }
+}