();
// 时间碎片序列生成器
private static final AtomicInteger sequence = new AtomicInteger(1000);
diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java
index 785894d81..7126f1181 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java
@@ -42,7 +42,7 @@ public class WatchCommand extends EnhancerCommand {
private Integer sizeLimit = 10 * 1024 * 1024;
private boolean isRegEx = false;
private int numberOfLimit = 100;
-
+
@Argument(index = 0, argName = "class-pattern")
@Description("The full qualified class name you want to watch")
public void setClassPattern(String classPattern) {
diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java
index 6e8d105e7..c7ff631f4 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java
@@ -32,15 +32,16 @@ public class ResultViewResolver {
private void registerResultViews() {
try {
- registerView(new SessionModel(), new SessionView());
registerView(new StatusModel(), new StatusView());
- registerView(new WatchModel(), new WatchView());
- registerView(new EnhancerAffectModel(), new EnhancerAffectView());
registerView(new VersionModel(), new VersionView());
- registerView(new PropertyModel(), new PropertyView());
registerView(new MessageModel(), new MessageView());
registerView(new HelpModel(), new HelpView());
//registerView(new HistoryModel(), new HistoryView());
+ registerView(new EchoModel(), new EchoView());
+ registerView(new SessionModel(), new SessionView());
+ registerView(new WatchModel(), new WatchView());
+ registerView(new EnhancerAffectModel(), new EnhancerAffectView());
+ registerView(new PropertyModel(), new PropertyView());
registerView(new SearchClassModel(), new SearchClassView());
registerView(new RowAffectModel(), new RowAffectView());
registerView(new SearchMethodModel(), new SearchMethodView());
@@ -65,7 +66,6 @@ public class ResultViewResolver {
registerView(new TraceModel(), new TraceView());
registerView(new OgnlModel(), new OgnlView());
registerView(new PwdModel(), new PwdView());
- registerView(new EchoModel(), new EchoView());
registerView(new CatModel(), new CatView());
} catch (Throwable e) {
logger.error("register result view failed", e);
diff --git a/core/src/main/java/com/taobao/arthas/core/distribution/ResultConsumerHelper.java b/core/src/main/java/com/taobao/arthas/core/distribution/ResultConsumerHelper.java
index 00be6aa84..534b68cc3 100644
--- a/core/src/main/java/com/taobao/arthas/core/distribution/ResultConsumerHelper.java
+++ b/core/src/main/java/com/taobao/arthas/core/distribution/ResultConsumerHelper.java
@@ -1,13 +1,19 @@
package com.taobao.arthas.core.distribution;
import com.alibaba.fastjson.JSON;
-import com.taobao.arthas.core.command.model.*;
+import com.taobao.arthas.core.command.model.CatModel;
+import com.taobao.arthas.core.command.model.ClassSetVO;
+import com.taobao.arthas.core.command.model.ResultModel;
+import com.taobao.arthas.core.command.model.TraceModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
index f7b4806be..a5ec0afa1 100644
--- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
+++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
@@ -1,5 +1,25 @@
package com.taobao.arthas.core.server;
+import java.arthas.SpyAPI;
+import java.io.File;
+import java.io.IOException;
+import java.lang.instrument.Instrumentation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.security.CodeSource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Timer;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
import com.alibaba.arthas.deps.ch.qos.logback.classic.LoggerContext;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
@@ -32,22 +52,6 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.EventExecutorGroup;
-import java.arthas.SpyAPI;
-import java.io.File;
-import java.io.IOException;
-import java.lang.instrument.Instrumentation;
-import java.lang.reflect.Method;
-import java.net.URI;
-import java.security.CodeSource;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-
/**
* @author vlinux on 15/5/2.
@@ -70,8 +74,8 @@ public class ArthasBootstrap {
private Instrumentation instrumentation;
private Thread shutdown;
private ShellServer shellServer;
+ private ScheduledExecutorService executorService;
private SessionManager sessionManager;
- private ExecutorService executorService;
private TunnelClient tunnelClient;
private File arthasOutputDir;
@@ -100,7 +104,7 @@ public class ArthasBootstrap {
// 4. start agent server
bind(configure);
- executorService = Executors.newCachedThreadPool(new ThreadFactory() {
+ executorService = Executors.newScheduledThreadPool(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
final Thread t = new Thread(r, "as-command-execute-daemon");
@@ -133,7 +137,7 @@ public class ArthasBootstrap {
/**
*
* 脚本里传过来的配置项,即命令行参数 > System Env > System Properties > arthas.properties
- * arthas.properties 指供一个配置项,可以反转优先级。 arthas.config.overrideAll=true
+ * arthas.properties 提供一个配置项,可以反转优先级。 arthas.config.overrideAll=true
* https://github.com/alibaba/arthas/issues/986
*
*/
@@ -238,14 +242,14 @@ public class ArthasBootstrap {
tunnelClient.setTunnelServerUrl(configure.getTunnelServer());
// ws://127.0.0.1:8563/ws
String host = "127.0.0.1";
- if (configure.getIp() != null) {
+ if(configure.getIp() != null) {
host = configure.getIp();
}
URI uri = new URI("ws", null, host, configure.getHttpPort(), "/ws", null, null);
tunnelClient.setLocalServerUrl(uri.toString());
ChannelFuture channelFuture = tunnelClient.start();
channelFuture.await(10, TimeUnit.SECONDS);
- if (channelFuture.isSuccess()) {
+ if(channelFuture.isSuccess()) {
agentId = tunnelClient.getId();
}
}
@@ -255,9 +259,9 @@ public class ArthasBootstrap {
try {
ShellServerOptions options = new ShellServerOptions()
- .setInstrumentation(instrumentation)
- .setPid(PidUtils.currentLongPid())
- .setSessionTimeout(configure.getSessionTimeout() * 1000);
+ .setInstrumentation(instrumentation)
+ .setPid(PidUtils.currentLongPid())
+ .setSessionTimeout(configure.getSessionTimeout() * 1000);
if (agentId != null) {
Map welcomeInfos = new HashMap();
@@ -423,6 +427,10 @@ public class ArthasBootstrap {
return this.timer;
}
+ public ScheduledExecutorService getScheduledExecutorService() {
+ return this.executorService;
+ }
+
public Instrumentation getInstrumentation() {
return this.instrumentation;
}
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/command/CommandProcess.java b/core/src/main/java/com/taobao/arthas/core/shell/command/CommandProcess.java
index f0ae11652..3cdf16af3 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/command/CommandProcess.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/command/CommandProcess.java
@@ -131,10 +131,9 @@ public interface CommandProcess extends Tty {
/**
* Register listener
*
- * @param lock the lock for enhance class
* @param listener
*/
- void register(int lock, AdviceListener listener, ClassFileTransformer transformer);
+ void register(AdviceListener listener, ClassFileTransformer transformer);
/**
* Unregister listener
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/session/SessionManager.java b/core/src/main/java/com/taobao/arthas/core/shell/session/SessionManager.java
index e5198aee1..d891d258f 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/session/SessionManager.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/session/SessionManager.java
@@ -1,7 +1,7 @@
package com.taobao.arthas.core.shell.session;
+import com.taobao.arthas.core.shell.system.JobController;
import com.taobao.arthas.core.shell.system.impl.InternalCommandManager;
-import com.taobao.arthas.core.shell.system.impl.JobControllerImpl;
import java.lang.instrument.Instrumentation;
@@ -25,5 +25,5 @@ public interface SessionManager {
Instrumentation getInstrumentation();
- JobControllerImpl getJobController();
+ JobController getJobController();
}
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/session/impl/SessionManagerImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/session/impl/SessionManagerImpl.java
index 22ec12e62..fb6cc5bd4 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/session/impl/SessionManagerImpl.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/session/impl/SessionManagerImpl.java
@@ -11,8 +11,8 @@ import com.taobao.arthas.core.shell.ShellServerOptions;
import com.taobao.arthas.core.shell.session.Session;
import com.taobao.arthas.core.shell.session.SessionManager;
import com.taobao.arthas.core.shell.system.Job;
+import com.taobao.arthas.core.shell.system.JobController;
import com.taobao.arthas.core.shell.system.impl.InternalCommandManager;
-import com.taobao.arthas.core.shell.system.impl.JobControllerImpl;
import java.lang.instrument.Instrumentation;
import java.util.*;
@@ -28,7 +28,7 @@ public class SessionManagerImpl implements SessionManager {
private final ArthasBootstrap bootstrap;
private final InternalCommandManager commandManager;
private final Instrumentation instrumentation;
- private final JobControllerImpl jobController;
+ private final JobController jobController;
private final long timeoutMillis;
private final long reaperInterval;
private final Map sessions;
@@ -37,7 +37,7 @@ public class SessionManagerImpl implements SessionManager {
private ScheduledExecutorService scheduledExecutorService;
public SessionManagerImpl(ShellServerOptions options, ArthasBootstrap bootstrap, InternalCommandManager commandManager,
- JobControllerImpl jobController) {
+ JobController jobController) {
this.bootstrap = bootstrap;
this.commandManager = commandManager;
this.jobController = jobController;
@@ -187,7 +187,7 @@ public class SessionManagerImpl implements SessionManager {
}
@Override
- public JobControllerImpl getJobController() {
+ public JobController getJobController() {
return jobController;
}
}
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/system/JobListener.java b/core/src/main/java/com/taobao/arthas/core/shell/system/JobListener.java
index fb40ca90c..b3c889171 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/system/JobListener.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/system/JobListener.java
@@ -1,7 +1,5 @@
package com.taobao.arthas.core.shell.system;
-import com.taobao.arthas.core.shell.system.impl.JobImpl;
-
/**
* Job listener
* @author gongdewei 2020-03-23
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/system/impl/GlobalJobControllerImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/system/impl/GlobalJobControllerImpl.java
index 5256d79bb..31137f3bc 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/system/impl/GlobalJobControllerImpl.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/system/impl/GlobalJobControllerImpl.java
@@ -1,5 +1,11 @@
package com.taobao.arthas.core.shell.system.impl;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.GlobalOptions;
@@ -12,8 +18,6 @@ import com.taobao.arthas.core.shell.system.Job;
import com.taobao.arthas.core.shell.system.JobListener;
import com.taobao.arthas.core.shell.term.Term;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
/**
* 全局的Job Controller,不应该存在启停的概念,不需要在连接的断开时关闭,
@@ -21,7 +25,7 @@ import java.util.concurrent.TimeUnit;
* @author gehui 2017年7月31日 上午11:55:41
*/
public class GlobalJobControllerImpl extends JobControllerImpl {
- private Map jobTimeoutTaskMap = new HashMap();
+ private Map jobTimeoutTaskMap = new HashMap();
private static final Logger logger = LoggerFactory.getLogger(GlobalJobControllerImpl.class);
@Override
@@ -41,7 +45,7 @@ public class GlobalJobControllerImpl extends JobControllerImpl {
@Override
public boolean removeJob(int id) {
- TimerTask jobTimeoutTask = jobTimeoutTaskMap.remove(id);
+ JobTimeoutTask jobTimeoutTask = jobTimeoutTaskMap.remove(id);
if (jobTimeoutTask != null) {
jobTimeoutTask.cancel();
}
@@ -55,9 +59,10 @@ public class GlobalJobControllerImpl extends JobControllerImpl {
/*
* 达到超时时间将会停止job
*/
- TimerTask jobTimeoutTask = new JobTimeoutTask(job);
- Date timeoutDate = new Date(System.currentTimeMillis() + (getJobTimeoutInSecond() * 1000));
- ArthasBootstrap.getInstance().getTimer().schedule(jobTimeoutTask, timeoutDate);
+ JobTimeoutTask jobTimeoutTask = new JobTimeoutTask(job);
+ long jobTimeoutInSecond = getJobTimeoutInSecond();
+ Date timeoutDate = new Date(System.currentTimeMillis() + (jobTimeoutInSecond * 1000));
+ ArthasBootstrap.getInstance().getScheduledExecutorService().schedule(jobTimeoutTask, jobTimeoutInSecond, TimeUnit.SECONDS);
jobTimeoutTaskMap.put(job.id(), jobTimeoutTask);
job.setTimeoutDate(timeoutDate);
@@ -98,8 +103,8 @@ public class GlobalJobControllerImpl extends JobControllerImpl {
return result;
}
- private static class JobTimeoutTask extends TimerTask {
- Job job;
+ private static class JobTimeoutTask implements Runnable {
+ private Job job;
public JobTimeoutTask(Job job) {
this.job = job;
@@ -107,17 +112,23 @@ public class GlobalJobControllerImpl extends JobControllerImpl {
@Override
public void run() {
- if (job != null) {
- job.terminate();
+ try {
+ if (job != null) {
+ Job temp = job;
+ job = null;
+ temp.terminate();
+ }
+ } catch (Throwable e) {
+ try {
+ logger.error("JobTimeoutTask error, job id: {}, line: {}", job.id(), job.line(), e);
+ } catch (Throwable t) {
+ // ignore
+ }
}
}
- @Override
- public boolean cancel() {
- // clear job reference from timer
- // fix issue: https://github.com/alibaba/arthas/issues/1189
+ public void cancel() {
job = null;
- return super.cancel();
}
}
}
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobControllerImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobControllerImpl.java
index fee1b1675..e21b3551d 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobControllerImpl.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobControllerImpl.java
@@ -19,11 +19,19 @@ import com.taobao.arthas.core.shell.term.Term;
import com.taobao.arthas.core.util.Constants;
import com.taobao.arthas.core.util.LogUtil;
import com.taobao.arthas.core.util.TokenUtils;
+
import io.termd.core.function.Function;
import java.io.File;
import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -224,5 +232,4 @@ public class JobControllerImpl implements JobController {
public void close() {
close(null);
}
-
}
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobImpl.java
index f4c60da0a..c960f5d53 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobImpl.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobImpl.java
@@ -12,10 +12,6 @@ import com.taobao.arthas.core.shell.system.ExecStatus;
import com.taobao.arthas.core.shell.system.Job;
import com.taobao.arthas.core.shell.system.JobListener;
import com.taobao.arthas.core.shell.system.Process;
-import com.taobao.arthas.core.shell.term.Term;
-import com.taobao.arthas.core.shell.term.impl.TermImpl;
-import com.taobao.arthas.core.util.Constants;
-import com.taobao.arthas.core.util.FileUtils;
/**
* @author Julien Viet
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/system/impl/ProcessImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/system/impl/ProcessImpl.java
index 8cc643ccb..249bcf7ad 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/system/impl/ProcessImpl.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/system/impl/ProcessImpl.java
@@ -397,9 +397,8 @@ public class ProcessImpl implements Process {
private final Tty tty;
private List args2;
private CommandLine commandLine;
- private int enhanceLock = -1;
private AtomicInteger times = new AtomicInteger();
- private AdviceListener suspendedListener = null;
+ private AdviceListener listener = null;
private ClassFileTransformer transformer;
public CommandProcessImpl(Process process, Tty tty) {
@@ -547,13 +546,15 @@ public class ProcessImpl implements Process {
}
@Override
- public void register(int enhanceLock, AdviceListener listener, ClassFileTransformer transformer) {
- this.enhanceLock = enhanceLock;
-
+ public void register(AdviceListener listener, ClassFileTransformer transformer) {
if (listener instanceof ProcessAware) {
- ((ProcessAware) listener).setProcess(this.process);
+ ProcessAware processAware = (ProcessAware) listener;
+ // listener 有可能是其它 command 创建的
+ if(processAware.getProcess() == null) {
+ processAware.setProcess(this.process);
+ }
}
- AdviceWeaver.reg(enhanceLock, listener);
+ AdviceWeaver.reg(listener);
this.transformer = transformer;
}
@@ -564,22 +565,29 @@ public class ProcessImpl implements Process {
ArthasBootstrap.getInstance().getTransformerManager().removeTransformer(transformer);
}
- AdviceWeaver.unReg(enhanceLock);
+ if (listener instanceof ProcessAware) {
+ // listener有可能其它 command 创建的,所以不能unRge
+ if (this.process.equals(((ProcessAware) listener).getProcess())) {
+ AdviceWeaver.unReg(listener);
+ }
+ } else {
+ AdviceWeaver.unReg(listener);
+ }
}
@Override
public void resume() {
- if (this.enhanceLock >= 0 && suspendedListener != null) {
- AdviceWeaver.resume(enhanceLock, suspendedListener);
- suspendedListener = null;
- }
+// if (suspendedListener != null) {
+// AdviceWeaver.resume(suspendedListener);
+// suspendedListener = null;
+// }
}
@Override
public void suspend() {
- if (this.enhanceLock >= 0) {
- suspendedListener = AdviceWeaver.suspend(enhanceLock);
- }
+// if (this.enhanceLock >= 0) {
+// suspendedListener = AdviceWeaver.suspend(enhanceLock);
+// }
}
@Override
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java
index b614ca580..2b7b1f819 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java
@@ -20,9 +20,9 @@ import com.taobao.arthas.core.shell.history.impl.HistoryManagerImpl;
import com.taobao.arthas.core.shell.session.Session;
import com.taobao.arthas.core.shell.session.SessionManager;
import com.taobao.arthas.core.shell.system.Job;
+import com.taobao.arthas.core.shell.system.JobController;
import com.taobao.arthas.core.shell.system.JobListener;
import com.taobao.arthas.core.shell.system.impl.InternalCommandManager;
-import com.taobao.arthas.core.shell.system.impl.JobControllerImpl;
import com.taobao.arthas.core.shell.term.SignalHandler;
import com.taobao.arthas.core.shell.term.Term;
import com.taobao.arthas.core.util.ArthasBanner;
@@ -58,7 +58,7 @@ public class HttpApiHandler {
private final AtomicInteger requestIdGenerator = new AtomicInteger(0);
private static HttpApiHandler instance;
private final InternalCommandManager commandManager;
- private final JobControllerImpl jobController;
+ private final JobController jobController;
private final HistoryManager historyManager;
private int jsonBufferSize = 1024 * 256;
diff --git a/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java b/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java
new file mode 100644
index 000000000..93a3fbeba
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java
@@ -0,0 +1,38 @@
+package com.taobao.arthas.core.util;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+import java.util.Set;
+
+import com.alibaba.arthas.deps.org.slf4j.Logger;
+import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author hengyunabc 2020-05-25
+ *
+ */
+public class InstrumentationUtils {
+ private static final Logger logger = LoggerFactory.getLogger(InstrumentationUtils.class);
+
+ public static void retransformClasses(Instrumentation inst, ClassFileTransformer transformer,
+ Set> classes) {
+ try {
+ inst.addTransformer(transformer, true);
+
+ for (Class> clazz : classes) {
+ try {
+ inst.retransformClasses(clazz);
+ } catch (Throwable e) {
+ String errorMsg = "retransformClasses class error, name: " + clazz.getName();
+ if (ClassUtils.isLambdaClass(clazz) && e instanceof VerifyError) {
+ errorMsg += ", Please ignore lambda class VerifyError: https://github.com/alibaba/arthas/issues/675";
+ }
+ logger.error(errorMsg, e);
+ }
+ }
+ } finally {
+ inst.removeTransformer(transformer);
+ }
+ }
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/util/affect/EnhancerAffect.java b/core/src/main/java/com/taobao/arthas/core/util/affect/EnhancerAffect.java
index a1bfb9e36..4189e708e 100644
--- a/core/src/main/java/com/taobao/arthas/core/util/affect/EnhancerAffect.java
+++ b/core/src/main/java/com/taobao/arthas/core/util/affect/EnhancerAffect.java
@@ -22,6 +22,7 @@ public final class EnhancerAffect extends Affect {
private final AtomicInteger cCnt = new AtomicInteger();
private final AtomicInteger mCnt = new AtomicInteger();
private ClassFileTransformer transformer;
+ private long listenerId;
/**
* dumpClass的文件存放集合
*/
@@ -92,6 +93,14 @@ public final class EnhancerAffect extends Affect {
this.transformer = transformer;
}
+ public long getListenerId() {
+ return listenerId;
+ }
+
+ public void setListenerId(long listenerId) {
+ this.listenerId = listenerId;
+ }
+
@Override
public String toString() {
final StringBuilder infoSB = new StringBuilder();
@@ -108,10 +117,11 @@ public final class EnhancerAffect extends Affect {
infoSB.append("[Affect method: ").append(method).append("]\n");
}
}
- infoSB.append(format("Affect(class count:%d , method count:%d) cost in %s ms.",
+ infoSB.append(format("Affect(class count: %d , method count: %d) cost in %s ms, listenerId: %d",
cCnt(),
mCnt(),
- cost()));
+ cost(),
+ listenerId));
return infoSB.toString();
}
diff --git a/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java b/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java
index 1468356c7..66384e4f3 100644
--- a/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java
+++ b/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java
@@ -41,14 +41,10 @@ public class EnhancerTest {
AdviceListener listener = Mockito.mock(AdviceListener.class);
- EnhancerAffect affect = new EnhancerAffect();
+ EqualsMatcher methodNameMatcher = new EqualsMatcher("print");
+ EqualsMatcher classNameMatcher = new EqualsMatcher(MathGame.class.getName());
- Set> matchingClasses = new HashSet>();
- matchingClasses.add(MathGame.class);
-
- EqualsMatcher matcher = new EqualsMatcher("print");
-
- Enhancer enhancer = new Enhancer(listener, true, false, matchingClasses, matcher, affect);
+ Enhancer enhancer = new Enhancer(listener, true, false, classNameMatcher, methodNameMatcher);
ClassLoader inClassLoader = MathGame.class.getClassLoader();
String className = MathGame.class.getName();
diff --git a/pom.xml b/pom.xml
index db7385ad7..283facd7e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,7 +103,7 @@
org.benf
cfr
- 0.149
+ 0.150
com.alibaba.middleware
diff --git a/site/src/site/sphinx/_static/dashboard.png b/site/src/site/sphinx/_static/dashboard.png
index 1f40b7212..3ed6ec760 100644
Binary files a/site/src/site/sphinx/_static/dashboard.png and b/site/src/site/sphinx/_static/dashboard.png differ
diff --git a/site/src/site/sphinx/en/advanced-use.md b/site/src/site/sphinx/en/advanced-use.md
index 302d3c3a2..39ee7589a 100644
--- a/site/src/site/sphinx/en/advanced-use.md
+++ b/site/src/site/sphinx/en/advanced-use.md
@@ -45,7 +45,7 @@ Advanced Usage
## monitor/watch/trace - related
-> **Attention**: commands here are taking advantage of byte-code-injection, which means we are injecting some [aspects](https://en.wikipedia.org/wiki/Aspect-oriented_programming) into the current classes for monitoring and statistics purpose. Therefore when use it for online troubleshooting in your production environment, you'd better **explicitly specify** classes/methods/criteria, and remember to remove the injected code by `stop` or `reset`.
+> **Attention**: commands here are taking advantage of byte-code-injection, which means we are injecting some [aspects](https://en.wikipedia.org/wiki/Aspect-oriented_programming) into the current classes for monitoring and statistics purpose. Therefore, when using it for online troubleshooting in your production environment, you'd better **explicitly specify** classes/methods/criteria, and remember to remove the injected code by `stop` or `reset`.
* [monitor](monitor.md) - monitor method execution statistics
* [watch](watch.md) - display the input/output parameter, return object, and thrown exception of specified method invocation
diff --git a/site/src/site/sphinx/en/async.md b/site/src/site/sphinx/en/async.md
index c95850b08..ec32bf510 100644
--- a/site/src/site/sphinx/en/async.md
+++ b/site/src/site/sphinx/en/async.md
@@ -40,8 +40,8 @@ You can see that there is currently a background job executing:
When the job is executing in the foreground, for example, directly executing the command `trace Test t`, or executing the background job command `trace Test t &`, then putting the job back to the foreground via `fg` command, the console cannot continue to execute other command, but can receive and process the following keyboard events:
-* ‘ctrl + z’: Suspend the job, the job status will change to `Stopped`, and the job can be restarted by `bg ` or `fg `
-* ‘ctrl + c’: Stop the job
+* ‘ctrl + z’: Suspends the job, the job status will change to `Stopped`, and the job can be restarted by `bg ` or `fg `
+* ‘ctrl + c’: Stops the job
* ‘ctrl + d’: According to linux semantics this should lead to exit the terminal, right now Arthas has not implemented this yet, therefore simply ignore this keystroke.
## 4. fg/bg, switch the job from the foreground to the background, and vise verse
@@ -60,7 +60,7 @@ $ trace Test t >> test.out &
The trace command will be running in the background and the output will be redirect to `~/logs/arthas-cache/test.out`. You can continue to execute other commands in the console, at the same time, you can also examine the execution result from the output file.
-When connect to a remote Arthas server, you may not be able to view the output file on the remote machine. In this case, Arthas also supports automatically redirecting the output to the local cache file. Examples are as follows:
+When connected to a remote Arthas server, you may not be able to view the output file on the remote machine. In this case, Arthas also supports automatically redirecting the output to the local cache file. Examples are as follows:
```bash
$ trace Test t >> &
diff --git a/site/src/site/sphinx/en/batch-support.md b/site/src/site/sphinx/en/batch-support.md
index c2bcc4e92..0afca2df5 100644
--- a/site/src/site/sphinx/en/batch-support.md
+++ b/site/src/site/sphinx/en/batch-support.md
@@ -38,7 +38,7 @@ Use `-c` also can specify the commands, like this:
./as.sh -c 'sysprop; thread' 56328 > test.out
```
-#### Step 3: Check the outputs
+#### Step 3: Check the output
```bash
cat test.out
diff --git a/site/src/site/sphinx/en/classloader.md b/site/src/site/sphinx/en/classloader.md
index 5cbe19a29..e67ffeae6 100644
--- a/site/src/site/sphinx/en/classloader.md
+++ b/site/src/site/sphinx/en/classloader.md
@@ -3,7 +3,7 @@ classloader
View hierarchy, urls and classes-loading info for the class-loaders.
-`classloader` can search and print out the URLs for a specified resource from one particular classloader. It is quite handy when analyze `ResourceNotFoundException`.
+`classloader` can search and print out the URLs for a specified resource from one particular classloader. It is quite handy when analyzing `ResourceNotFoundException`.
### Options
diff --git a/site/src/site/sphinx/en/commands.md b/site/src/site/sphinx/en/commands.md
index 3a3b1060a..f60729ee3 100644
--- a/site/src/site/sphinx/en/commands.md
+++ b/site/src/site/sphinx/en/commands.md
@@ -42,15 +42,15 @@ All Commands
### Basic Arthas Commands
-* help - examine help information
-* cls - clear out the screen
-* session - examine the current session
-* [reset](reset.md) - reset enhanced classes. All enhanced classes will be reset to their original states. When Arthas server closes, all these enhanced classes will be reset too
-* version - print out Arthas's version
+* help - examines help information
+* cls - clears out the screen
+* session - examines the current session
+* [reset](reset.md) - resets enhanced classes. All enhanced classes will be reset to their original states. When Arthas server closes, all these enhanced classes will be reset too
+* version - prints out Arthas's version
* history - view command history
* quit - exit the current Arthas client without affecting other clients
-* stop - terminate the Arthas server, all the Arthas clients connecting to this server will be disconnected
-* [keymap](keymap.md) - list all Arthas keyboard shortcuts and shortcut customizations.
+* stop - terminates the Arthas server, all the Arthas clients connecting to this server will be disconnected
+* [keymap](keymap.md) - lists all Arthas keyboard shortcuts and shortcut customizations.
diff --git a/site/src/site/sphinx/en/download.md b/site/src/site/sphinx/en/download.md
index 9ee6ae54a..faa46b9b0 100644
--- a/site/src/site/sphinx/en/download.md
+++ b/site/src/site/sphinx/en/download.md
@@ -3,7 +3,7 @@ Download
## Download full package
-### Dwonload from maven central
+### Download from maven central
Latest Version, Click To Download: [](http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.taobao.arthas&a=arthas-packaging&e=zip&c=bin&v=LATEST)
diff --git a/site/src/site/sphinx/en/getstatic.md b/site/src/site/sphinx/en/getstatic.md
index be3a430f4..fcf5775e6 100644
--- a/site/src/site/sphinx/en/getstatic.md
+++ b/site/src/site/sphinx/en/getstatic.md
@@ -1,7 +1,7 @@
getstatic
=========
-* It is recommended to use the [OGNL] (ognl.md) command, which will be more flexibility.
+* It is recommended to use the [OGNL] (ognl.md) command, which will be more flexible.
Check the static fields of classes conveniently, the usage is `getstatic class_name field_name`.
diff --git a/site/src/site/sphinx/en/groovy.md b/site/src/site/sphinx/en/groovy.md
index 991adff8c..eb1dc16bb 100644
--- a/site/src/site/sphinx/en/groovy.md
+++ b/site/src/site/sphinx/en/groovy.md
@@ -6,7 +6,7 @@ groovy
### Limitations
1. Prohibit from alternating the original logic. Like `watch` command, The major purpose of scripting is monitoring and observing.
-1. Only allow to monitor at the stages of before/success/exception/finish on one method.
+2. Only allow to monitor at the stages of before/success/exception/finish on one method.
### Parameters
diff --git a/site/src/site/sphinx/en/index.md b/site/src/site/sphinx/en/index.md
index 405c805cb..e9c5ea4ae 100644
--- a/site/src/site/sphinx/en/index.md
+++ b/site/src/site/sphinx/en/index.md
@@ -34,7 +34,7 @@ Arthas is built to solve these issues. A developer can troubleshoot production i
* Supports Linux/Mac/Windows
-**If you are using Arthas, please let us know. Your use is very important to us: [View](https://github.com/alibaba/arthas/issues/111)**
+**If you are using Arthas, please let us know. Your feedback is very important to us: [View](https://github.com/alibaba/arthas/issues/111)**
Contents
--------
diff --git a/site/src/site/sphinx/en/install-detail.md b/site/src/site/sphinx/en/install-detail.md
index 5d34b6b85..10fb6aa92 100644
--- a/site/src/site/sphinx/en/install-detail.md
+++ b/site/src/site/sphinx/en/install-detail.md
@@ -4,7 +4,7 @@ Install Arthas
## Quick installation
-### Use `arthas-boot`(Recommend)
+### Use `arthas-boot`(Recommended)
Download`arthas-boot.jar`,Start with `java` command:
diff --git a/site/src/site/sphinx/en/keymap.md b/site/src/site/sphinx/en/keymap.md
index e98456f1c..f02b2462b 100644
--- a/site/src/site/sphinx/en/keymap.md
+++ b/site/src/site/sphinx/en/keymap.md
@@ -62,8 +62,8 @@ then replace `"\C-h": backward-delete-char` with `"\C-h": backward-char`, then r
#### Shortcuts for jobs
-* `ctrl + c`: Terminate current command
-* `ctrl + z`: Suspend the current command, you can restore this command with `bg`/`fg`, or `kill` it.
+* `ctrl + c`: Terminates current command
+* `ctrl + z`: Suspends the current command, you can restore this command with `bg`/`fg`, or `kill` it.
* `ctrl + a`: Go to the beginning the line
* `ctrl + e`: Go to the end of the line
diff --git a/site/src/site/sphinx/en/quick-start.md b/site/src/site/sphinx/en/quick-start.md
index fa68cbe03..7d47b7162 100644
--- a/site/src/site/sphinx/en/quick-start.md
+++ b/site/src/site/sphinx/en/quick-start.md
@@ -8,7 +8,7 @@ curl -O https://alibaba.github.io/arthas/arthas-demo.jar
java -jar arthas-demo.jar
```
-`arthas-demo` is a simple program that generates a random number every second, then find all prime factors of the number.
+`arthas-demo` is a simple program that generates a random number every second, then it finds all prime factors of that number.
The source code of `arthas-demo`: [View](https://github.com/alibaba/arthas/blob/master/demo/src/main/java/demo/MathGame.java)
diff --git a/static/woqu.png b/static/woqu.png
new file mode 100644
index 000000000..0d8289f8f
Binary files /dev/null and b/static/woqu.png differ
diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClientSocketClientHandler.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClientSocketClientHandler.java
index 9b20c81a9..ffd8300af 100644
--- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClientSocketClientHandler.java
+++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClientSocketClientHandler.java
@@ -1,4 +1,3 @@
-
package com.alibaba.arthas.tunnel.client;
import java.net.URI;
@@ -30,18 +29,14 @@ import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.util.concurrent.GenericFutureListener;
/**
- *
* @author hengyunabc 2019-08-28
- *
*/
public class ForwardClientSocketClientHandler extends SimpleChannelInboundHandler {
- private final static Logger logger = LoggerFactory.getLogger(ForwardClientSocketClientHandler.class);
-
- private ChannelPromise handshakeFuture;
- private Channel localChannel;
+ private static final Logger logger = LoggerFactory.getLogger(ForwardClientSocketClientHandler.class);
- private URI localServerURI;
+ private ChannelPromise handshakeFuture;
+ private final URI localServerURI;
public ForwardClientSocketClientHandler(URI localServerURI) {
this.localServerURI = localServerURI;
@@ -49,7 +44,6 @@ public class ForwardClientSocketClientHandler extends SimpleChannelInboundHandle
@Override
public void channelActive(ChannelHandlerContext ctx) {
-
}
@Override
@@ -58,26 +52,30 @@ public class ForwardClientSocketClientHandler extends SimpleChannelInboundHandle
}
@Override
- public void userEventTriggered(final ChannelHandlerContext ctx, Object evt) throws Exception {
-
+ public void userEventTriggered(final ChannelHandlerContext ctx, Object evt) {
if (evt.equals(ClientHandshakeStateEvent.HANDSHAKE_COMPLETE)) {
-
- EventLoopGroup group = new NioEventLoopGroup();
-
try {
+ connectLocalServer(ctx);
+ } catch (Throwable e) {
+ logger.error("ForwardClientSocketClientHandler connect local arthas server error", e);
+ }
+ } else {
+ ctx.fireUserEventTriggered(evt);
+ }
+ }
- logger.info("ForwardClientSocketClientHandler star connect local arthas server");
-
- WebSocketClientHandshaker newHandshaker = WebSocketClientHandshakerFactory.newHandshaker(localServerURI,
- WebSocketVersion.V13, null, true, new DefaultHttpHeaders());
-
- final WebSocketClientProtocolHandler websocketClientHandler = new WebSocketClientProtocolHandler(
- newHandshaker);
-
- final LocalFrameHandler localFrameHandler = new LocalFrameHandler();
-
- Bootstrap b = new Bootstrap();
- b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer() {
+ private void connectLocalServer(final ChannelHandlerContext ctx) throws InterruptedException {
+ EventLoopGroup group = new NioEventLoopGroup();
+ logger.info("ForwardClientSocketClientHandler star connect local arthas server");
+ WebSocketClientHandshaker newHandshaker = WebSocketClientHandshakerFactory.newHandshaker(localServerURI,
+ WebSocketVersion.V13, null, true, new DefaultHttpHeaders());
+ final WebSocketClientProtocolHandler websocketClientHandler = new WebSocketClientProtocolHandler(
+ newHandshaker);
+ final LocalFrameHandler localFrameHandler = new LocalFrameHandler();
+
+ Bootstrap b = new Bootstrap();
+ b.group(group).channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
@@ -86,42 +84,30 @@ public class ForwardClientSocketClientHandler extends SimpleChannelInboundHandle
}
});
- localChannel = b.connect(localServerURI.getHost(), localServerURI.getPort()).sync().channel();
-
- localFrameHandler.handshakeFuture().addListener(new GenericFutureListener() {
+ Channel localChannel = b.connect(localServerURI.getHost(), localServerURI.getPort()).sync().channel();
+ this.handshakeFuture = localFrameHandler.handshakeFuture();
+ handshakeFuture.addListener(new GenericFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
ChannelPipeline pipeline = future.channel().pipeline();
pipeline.remove(localFrameHandler);
pipeline.addLast(new RelayHandler(ctx.channel()));
-
}
});
- localFrameHandler.handshakeFuture().sync();
-
- ctx.pipeline().remove(ForwardClientSocketClientHandler.this);
-
- ctx.pipeline().addLast(new RelayHandler(localChannel));
-
- logger.info("ForwardClientSocketClientHandler connect local arthas server success");
- } catch (Throwable e) {
- logger.error("ForwardClientSocketClientHandler connect local arthas server error", e);
- }
-
- } else {
- ctx.fireUserEventTriggered(evt);
- }
+ handshakeFuture.sync();
+ ctx.pipeline().remove(ForwardClientSocketClientHandler.this);
+ ctx.pipeline().addLast(new RelayHandler(localChannel));
+ logger.info("ForwardClientSocketClientHandler connect local arthas server success");
}
@Override
- protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception {
-
+ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
- cause.printStackTrace();
+ logger.error("ForwardClientSocketClient channel: {}" , ctx.channel(), cause);
if (!handshakeFuture.isDone()) {
handshakeFuture.setFailure(cause);
}
diff --git a/tunnel-server/README.md b/tunnel-server/README.md
index 76016d443..b0cacbbb1 100644
--- a/tunnel-server/README.md
+++ b/tunnel-server/README.md
@@ -1,5 +1,3 @@
-
-
## How it works
Tunnel server/client use websocket protocol.
@@ -8,24 +6,36 @@ For example:
1. Arthas tunnel server listen at `192.168.1.10:7777`
-1. Arthas tunnel client register to the tunnel server
+2. Arthas tunnel client register to the tunnel server
+
+ tunnel client connect to tunnel server with URL: `ws://192.168.1.10:7777/ws?method=agentRegister`
- tunnel client connect to tunnel server with URL: `ws://192.168.1.10:7777/ws?method=agentRegister`
+ tunnel server response a text frame message: `response:/?method=agentRegister&id=bvDOe8XbTM2pQWjF4cfw`
- tunnel server response a text frame message: `response:/?method=agentRegister&id=bvDOe8XbTM2pQWjF4cfw`
+ This connection is `control connection`.
- This connection is `control connection`.
+3. The browser try connect to remote arthas agent
-1. The browser try connect to remote arthas agent, start connect to tunnel server with URL: `'ws://192.168.1.10:7777/ws?method=connectArthas&id=bvDOe8XbTM2pQWjF4cfw`
+ start connect to tunnel server with URL: `'ws://192.168.1.10:7777/ws?method=connectArthas&id=bvDOe8XbTM2pQWjF4cfw`
-1. Arthas server find the `control connection` with the id `bvDOe8XbTM2pQWjF4cfw`, then send a text frame to arthas client: `response:/?method=startTunnel&id=bvDOe8XbTM2pQWjF4cfw&clientConnectionId=AMku9EFz2gxeL2gedGOC`
+4. Arthas server find the `control connection` with the id `bvDOe8XbTM2pQWjF4cfw`
+
+ then send a text frame to arthas client: `response:/?method=startTunnel&id=bvDOe8XbTM2pQWjF4cfw&clientConnectionId=AMku9EFz2gxeL2gedGOC`
-1. Arhtas tunnel client open a new connection to tunnel server, URL: `ws://127.0.0.1:7777/ws/?method=openTunnel&clientConnectionId=AMku9EFz2gxeL2gedGOC&id=bvDOe8XbTM2pQWjF4cfw`. This connection is `tunnel connection`.
+5. Arthas tunnel client open a new connection to tunnel server
-1. Arhtas tunnel client start connect to local arthas agent, URL: `ws://127.0.0.1:3658/ws`. This connection is `local connection`.
+ URL: `ws://127.0.0.1:7777/ws/?method=openTunnel&clientConnectionId=AMku9EFz2gxeL2gedGOC&id=bvDOe8XbTM2pQWjF4cfw`
+
+ This connection is `tunnel connection`
-1. Forward websocket frame between `tunnel connection` and `local connection`.
+6. Arthas tunnel client start connect to local arthas agent, URL: `ws://127.0.0.1:3658/ws`
+ This connection is `local connection`
+
+7. Forward websocket frame between `tunnel connection` and `local connection`.
+
+```
++---------+ +----------------------+ +----------------------+ +--------------+
+| browser <-----> arthas tunnel server | <-----> arthas tunnel client <--- -> arthas agent |
+|---------+ +----------------------+ +----------------------+ +--------------+
```
-browser <-> arthas tunnel server <-> arthas tunnel client <-> arthas agent
-```
\ No newline at end of file