use TransformerManager to manager all Transformer; Enhancer only enhance bytecode once

This commit is contained in:
hengyunabc
2020-05-19 17:30:08 +08:00
parent f39236eb86
commit c20e97d7de
17 changed files with 2108 additions and 32 deletions
+6
View File
@@ -69,6 +69,12 @@
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-demo</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -62,6 +62,15 @@ public class AsmOpUtils {
private static final Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
public static boolean isBoxType(final Type type) {
if (BYTE_TYPE.equals(type) || BOOLEAN_TYPE.equals(type) || SHORT_TYPE.equals(type)
|| CHARACTER_TYPE.equals(type) || INTEGER_TYPE.equals(type) || FLOAT_TYPE.equals(type)
|| LONG_TYPE.equals(type) || DOUBLE_TYPE.equals(type)) {
return true;
}
return false;
}
public static Type getBoxedType(final Type type) {
switch (type.getSort()) {
case Type.BYTE:
@@ -0,0 +1,22 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.taobao.arthas.common.concurrent;
import java.util.Iterator;
public interface ReusableIterator<E> extends Iterator<E> {
void rewind();
}
+6
View File
@@ -221,6 +221,12 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-demo</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.benf</groupId>
@@ -0,0 +1,203 @@
package com.taobao.arthas.core.advisor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import com.taobao.arthas.common.concurrent.ConcurrentWeakKeyHashMap;
import com.taobao.arthas.core.server.ArthasBootstrap;
import com.taobao.arthas.core.shell.system.ExecStatus;
import com.taobao.arthas.core.shell.system.ProcessAware;
/**
*
* TODO line 的记录 listener方式? 还是有string为key,不过 classname|method|desc|num 这样子? 判断是否已插入了,可以在两行中间查询,有没有 SpyAPI 的invoke?
*
* TODO trace的怎么搞? trace 只记录一次就可以了 classname|method|desc|trace ? 怎么避免 trace 到 SPY的invoke ?直接忽略?
*
* TODO trace命令可以动态的增加 新的函数进去不?只要关联上同一个 Listener应该是可以的。
*
* TODO 在SPY里放很多的 Object数组,然后动态的设置进去? 比如有新的 Listener来的时候。 这样子连查表都不用了。 甚至可以动态生成 存放这些 Listener数组的类?
* 这样子的话,只要有 Binding那里,查询到一个具体分配好的类, 这样子就可以了? 甚至每个ClassLoader里都动态生成这样子的 存放类,那么这样子不可以避免查 ClassLoader了么?
*
* 动态为每一个增强类,生成一个新的类,新的类里,有各种的 ID 数组,保存每一个类的每一种 trace 点的信息??
*
* 多个 watch命令 对同一个类,现在的逻辑是,每个watch都有一个自己的 TransForm,但不会重复增强,因为做了判断。 watch命令停止时,也没有去掉增强的代码。
* 只有reset时 才会去掉。
*
* 其实用户想查看局部变量,并不是想查看哪一行! 而是想看某个函数里子调用时的 局部变量的值! 所以实际上是想要一个新的命令,比如 watchinmethod
* 可以 在某个子调用里,
*
* TODO 现在的trace 可以输出行号,可能不是很精确,但是可以对应上的。 这个在新的方式里怎么支持? 增加一个 linenumber binding 从mehtodNode,向上查找到最近的行号?
*
* TODO 防止重复增强,最重要的应该还是动态增加 annotation,这个才是真正可以做到某一行,某一个子 invoke 都能识别出来的! 无论是 transform多少次!
* 字节码怎么动态加 annotation annotation里签名用 url ?的key/value方式表达! 这样子可以有效还原信息
*
* TODO 是否考虑一个 trace /watch命令之后,得到一个具体的 Listener ID, 允许在另外的窗口里,再次 trace/watch时指定这个ID,就会查找到,并处理。
* 这样子的话,真正达到了动态灵活的,一层一层增加的trace !
*
*
* @author hengyunabc 2020-04-24
*
*/
public class AdviceListenerManager {
private static Timer timer = ArthasBootstrap.getInstance().getTimer();
static {
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (adviceListenerMap != null) {
for (Entry<ClassLoader, ClassLoaderAdviceListenerManager> entry : adviceListenerMap.entrySet()) {
ClassLoaderAdviceListenerManager adviceListenerManager = entry.getValue();
synchronized (adviceListenerManager) {
for (Entry<String, List<AdviceListener>> eee : adviceListenerManager.map.entrySet()) {
List<AdviceListener> listeners = eee.getValue();
List<AdviceListener> newResult = new ArrayList<AdviceListener>();
for (AdviceListener listener : listeners) {
if (listener instanceof ProcessAware) {
ProcessAware processAware = (ProcessAware) listener;
ExecStatus status = processAware.getProcess().status();
if (!status.equals(ExecStatus.TERMINATED)) {
newResult.add(listener);
}
}
}
if (newResult.size() != listeners.size()) {
adviceListenerManager.map.put(eee.getKey(), newResult);
}
}
}
}
}
}
}, 3000, 3000);
}
static private ConcurrentWeakKeyHashMap<ClassLoader, ClassLoaderAdviceListenerManager> adviceListenerMap = new ConcurrentWeakKeyHashMap<ClassLoader, ClassLoaderAdviceListenerManager>();
static class ClassLoaderAdviceListenerManager {
private ConcurrentHashMap<String, List<AdviceListener>> map = new ConcurrentHashMap<String, List<AdviceListener>>();
private String key(String className, String methodName, String methodDesc) {
return className + methodName + methodDesc;
}
private String keyForTrace(String className, String owner, String methodName, String methodDesc) {
return className + owner + methodName + methodDesc;
}
public void registerAdviceListener(String className, String methodName, String methodDesc,
AdviceListener listener) {
synchronized (this) {
className = className.replace('/', '.');
String key = key(className, methodName, methodDesc);
List<AdviceListener> listeners = map.get(key);
if (listeners == null) {
listeners = new ArrayList<AdviceListener>();
map.put(key, listeners);
}
if(!listeners.contains(listener)) {
listeners.add(listener);
}
}
}
public List<AdviceListener> queryAdviceListeners(String className, String methodName, String methodDesc) {
className = className.replace('/', '.');
String key = key(className, methodName, methodDesc);
List<AdviceListener> listeners = map.get(key);
return listeners;
}
public void registerTraceAdviceListener(String className, String owner, String methodName, String methodDesc,
AdviceListener listener) {
className = className.replace('/', '.');
String key = keyForTrace(className, owner, methodName, methodDesc);
List<AdviceListener> listeners = map.get(key);
if (listeners == null) {
listeners = new ArrayList<AdviceListener>();
map.put(key, listeners);
}
if(!listeners.contains(listener)) {
listeners.add(listener);
}
}
public List<AdviceListener> queryTraceAdviceListeners(String className, String owner, String methodName, String methodDesc) {
className = className.replace('/', '.');
String key = keyForTrace(className, owner, methodName, methodDesc);
List<AdviceListener> listeners = map.get(key);
return listeners;
}
}
public static void registerAdviceListener(ClassLoader classLoader, String className, String methodName,
String methodDesc, AdviceListener listener) {
className = className.replace('/', '.');
ClassLoaderAdviceListenerManager manager = adviceListenerMap.get(classLoader);
if (manager == null) {
manager = new ClassLoaderAdviceListenerManager();
adviceListenerMap.put(classLoader, manager);
}
manager.registerAdviceListener(className, methodName, methodDesc, listener);
}
public static void updateAdviceListeners() {
}
public static List<AdviceListener> queryAdviceListeners(ClassLoader classLoader, String className,
String methodName, String methodDesc) {
className = className.replace('/', '.');
ClassLoaderAdviceListenerManager manager = adviceListenerMap.get(classLoader);
if (manager != null) {
return manager.queryAdviceListeners(className, methodName, methodDesc);
}
return null;
}
public static void registerTraceAdviceListener(ClassLoader classLoader, String className, String owner, String methodName,
String methodDesc, AdviceListener listener) {
className = className.replace('/', '.');
ClassLoaderAdviceListenerManager manager = adviceListenerMap.get(classLoader);
if (manager == null) {
manager = new ClassLoaderAdviceListenerManager();
adviceListenerMap.put(classLoader, manager);
}
manager.registerTraceAdviceListener(className, owner, methodName, methodDesc, listener);
}
public static List<AdviceListener> queryTraceAdviceListeners(ClassLoader classLoader, String className, String owner,
String methodName, String methodDesc) {
className = className.replace('/', '.');
ClassLoaderAdviceListenerManager manager = adviceListenerMap.get(classLoader);
if (manager != null) {
return manager.queryTraceAdviceListeners(className, owner, methodName, methodDesc);
}
return null;
}
}
@@ -23,6 +23,7 @@ import java.util.WeakHashMap;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
@@ -44,10 +45,9 @@ import com.taobao.arthas.bytekit.asm.location.filter.GroupLocationFilter;
import com.taobao.arthas.bytekit.asm.location.filter.InvokeCheckLocationFilter;
import com.taobao.arthas.bytekit.asm.location.filter.InvokeContainLocationFilter;
import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
import com.taobao.arthas.core.GlobalOptions;
import com.taobao.arthas.core.bytecode.AdviceListenerManager;
import com.taobao.arthas.core.bytecode.SpyImpl;
import com.taobao.arthas.core.server.ArthasBootstrap;
import com.taobao.arthas.core.util.ArthasCheckUtils;
import com.taobao.arthas.core.util.FileUtils;
@@ -248,25 +248,47 @@ public class Enhancer implements ClassFileTransformer {
groupLocationFilter.addFilter(invokeExceptionFilter);
for (MethodNode methodNode : matchedMethods) {
MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter);
for (InterceptorProcessor interceptor : interceptorProcessors) {
try {
List<Location> locations = interceptor.process(methodProcessor);
for (Location location : locations) {
if (location instanceof MethodInsnNodeWare) {
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
AdviceListenerManager.registerTraceAdviceListener(inClassLoader, className,
methodInsnNode.owner, methodInsnNode.name, methodInsnNode.desc, listener);
// 先查找是否有 atBeforeInvoke 函数,如果有,则说明已经有trace了,则直接不再尝试增强,直接插入 listener
if(AsmUtils.containsMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atBeforeInvoke")) {
for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode
.getNext()) {
if (insnNode instanceof MethodInsnNode) {
final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if(this.skipJDKTrace) {
if(methodInsnNode.owner.startsWith("java/")) {
continue;
}
}
// 原始类型的box类型相关的都跳过
if(AsmOpUtils.isBoxType(Type.getObjectType(methodInsnNode.owner))) {
continue;
}
AdviceListenerManager.registerTraceAdviceListener(inClassLoader, className,
methodInsnNode.owner, methodInsnNode.name, methodInsnNode.desc, listener);
}
}
}else {
MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter);
for (InterceptorProcessor interceptor : interceptorProcessors) {
try {
List<Location> locations = interceptor.process(methodProcessor);
for (Location location : locations) {
if (location instanceof MethodInsnNodeWare) {
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
} catch (Throwable e) {
e.printStackTrace();
AdviceListenerManager.registerTraceAdviceListener(inClassLoader, className,
methodInsnNode.owner, methodInsnNode.name, methodInsnNode.desc, listener);
}
}
} catch (Throwable e) {
logger.error("enhancer error, class: {}, method: {}, interceptor: {}", classNode.name, methodNode.name, interceptor.getClass().getName(), e);
}
}
}
// enter/exist 总是要插入 listener
AdviceListenerManager.registerAdviceListener(inClassLoader, className, methodNode.name, methodNode.desc,
listener);
affect.mCnt(1);
@@ -3,6 +3,8 @@ package com.taobao.arthas.core.advisor;
import com.taobao.arthas.core.command.express.ExpressException;
import com.taobao.arthas.core.command.express.ExpressFactory;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.shell.system.Process;
import com.taobao.arthas.core.shell.system.ProcessAware;
import com.taobao.arthas.core.util.ArthasCheckUtils;
import com.taobao.arthas.core.util.Constants;
import com.taobao.arthas.core.util.StringUtils;
@@ -16,7 +18,8 @@ import java.lang.reflect.Method;
* 通过反射拿到对应的Class/Method类,而不是原始的ClassName/MethodName
* 当然性能开销要比普通监听器高许多
*/
public abstract class ReflectAdviceListenerAdapter implements AdviceListener {
public abstract class ReflectAdviceListenerAdapter implements AdviceListener , ProcessAware{
private Process process;
@Override
public void create() {
@@ -28,6 +31,14 @@ public abstract class ReflectAdviceListenerAdapter implements AdviceListener {
// default no-op
}
public Process getProcess() {
return process;
}
public void setProcess(Process process) {
this.process = process;
}
private ClassLoader toClassLoader(ClassLoader loader) {
return null != loader
? loader
@@ -0,0 +1,210 @@
package com.taobao.arthas.core.advisor;
import java.arthas.SpyAPI.AbstractSpy;
import java.util.List;
import java.util.regex.Pattern;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.shell.system.ExecStatus;
import com.taobao.arthas.core.shell.system.ProcessAware;
/**
* <pre>
* 怎么从 className|methodDesc 到 id 对应起来??
* 当id少时,可以id自己来判断是否符合?
*
* 如果是每个 className|methodDesc 为 key ,是否
* </pre>
*
* @author hengyunabc 2020-04-24
*
*/
public class SpyImpl extends AbstractSpy {
private static final Logger logger = LoggerFactory.getLogger(SpyImpl.class);
@Override
public void atEnter(Class<?> clazz, String methodInfo, Object target, Object[] args) {
ClassLoader classLoader = clazz.getClassLoader();
String[] info = splitMethodInfo(methodInfo);
String methodName = info[0];
String methodDesc = info[1];
// TODO listener 只用查一次,放到 thread local里保存起来就可以了!
List<AdviceListener> listeners = AdviceListenerManager.queryAdviceListeners(classLoader, clazz.getName(),
methodName, methodDesc);
if (listeners != null) {
for (AdviceListener adviceListener : listeners) {
try {
if (skipAdviceListener(adviceListener)) {
continue;
}
adviceListener.before(classLoader, clazz.getName(), methodName, methodDesc, target, args);
} catch (Throwable e) {
if (logger.isDebugEnabled()) {
logger.error("class: {}, methodInfo: {}", clazz.getName(), methodInfo, e);
}
}
}
}
}
@Override
public void atExit(Class<?> clazz, String methodInfo, Object target, Object[] args, Object returnObject) {
ClassLoader classLoader = clazz.getClassLoader();
String[] info = splitMethodInfo(methodInfo);
String methodName = info[0];
String methodDesc = info[1];
List<AdviceListener> listeners = AdviceListenerManager.queryAdviceListeners(classLoader, clazz.getName(),
methodName, methodDesc);
if (listeners != null) {
for (AdviceListener adviceListener : listeners) {
try {
if (skipAdviceListener(adviceListener)) {
continue;
}
adviceListener.afterReturning(classLoader, clazz.getName(), methodName, methodDesc, target, args,
returnObject);
} catch (Throwable e) {
if (logger.isDebugEnabled()) {
logger.error("class: {}, methodInfo: {}", clazz.getName(), methodInfo, e);
}
}
}
}
}
@Override
public void atExceptionExit(Class<?> clazz, String methodInfo, Object target, Object[] args, Throwable throwable) {
ClassLoader classLoader = clazz.getClassLoader();
String[] info = splitMethodInfo(methodInfo);
String methodName = info[0];
String methodDesc = info[1];
List<AdviceListener> listeners = AdviceListenerManager.queryAdviceListeners(classLoader, clazz.getName(),
methodName, methodDesc);
if (listeners != null) {
for (AdviceListener adviceListener : listeners) {
try {
if (skipAdviceListener(adviceListener)) {
continue;
}
adviceListener.afterThrowing(classLoader, clazz.getName(), methodName, methodDesc, target, args,
throwable);
} catch (Throwable e) {
if (logger.isDebugEnabled()) {
logger.error("class: {}, methodInfo: {}", clazz.getName(), methodInfo, e);
}
}
}
}
}
@Override
public void atBeforeInvoke(Class<?> clazz, String invokeInfo, Object target) {
ClassLoader classLoader = clazz.getClassLoader();
String[] info = splitInvokeInfo(invokeInfo);
String owner = info[0];
String methodName = info[1];
String methodDesc = info[2];
List<AdviceListener> listeners = AdviceListenerManager.queryTraceAdviceListeners(classLoader, clazz.getName(),
owner, methodName, methodDesc);
if (listeners != null) {
for (AdviceListener adviceListener : listeners) {
try {
if (skipAdviceListener(adviceListener)) {
continue;
}
final InvokeTraceable listener = (InvokeTraceable) adviceListener;
listener.invokeBeforeTracing(owner, methodName, methodDesc, Integer.parseInt(info[3]));
} catch (Throwable e) {
if (logger.isDebugEnabled()) {
logger.error("class: {}, invokeInfo: {}", clazz.getName(), invokeInfo, e);
}
}
}
}
}
@Override
public void atAfterInvoke(Class<?> clazz, String invokeInfo, Object target) {
ClassLoader classLoader = clazz.getClassLoader();
String[] info = splitInvokeInfo(invokeInfo);
String owner = info[0];
String methodName = info[1];
String methodDesc = info[2];
List<AdviceListener> listeners = AdviceListenerManager.queryTraceAdviceListeners(classLoader, clazz.getName(),
owner, methodName, methodDesc);
if (listeners != null) {
for (AdviceListener adviceListener : listeners) {
try {
if (skipAdviceListener(adviceListener)) {
continue;
}
final InvokeTraceable listener = (InvokeTraceable) adviceListener;
listener.invokeAfterTracing(owner, methodName, methodDesc, Integer.parseInt(info[3]));
} catch (Throwable e) {
if (logger.isDebugEnabled()) {
logger.error("class: {}, invokeInfo: {}", clazz.getName(), invokeInfo, e);
}
}
}
}
}
@Override
public void atInvokeException(Class<?> clazz, String invokeInfo, Object target, Throwable throwable) {
ClassLoader classLoader = clazz.getClassLoader();
String[] info = splitInvokeInfo(invokeInfo);
String owner = info[0];
String methodName = info[1];
String methodDesc = info[2];
List<AdviceListener> listeners = AdviceListenerManager.queryTraceAdviceListeners(classLoader, clazz.getName(),
owner, methodName, methodDesc);
if (listeners != null) {
for (AdviceListener adviceListener : listeners) {
try {
if (skipAdviceListener(adviceListener)) {
continue;
}
final InvokeTraceable listener = (InvokeTraceable) adviceListener;
listener.invokeThrowTracing(owner, methodName, methodDesc, Integer.parseInt(info[3]));
} catch (Throwable e) {
if (logger.isDebugEnabled()) {
logger.error("class: {}, invokeInfo: {}", clazz.getName(), invokeInfo, e);
}
}
}
}
}
private String[] splitMethodInfo(String methodInfo) {
return methodInfo.split(Pattern.quote("|"));
}
private String[] splitInvokeInfo(String invokeInfo) {
return invokeInfo.split(Pattern.quote("|"));
}
private boolean skipAdviceListener(AdviceListener adviceListener) {
if (adviceListener instanceof ProcessAware) {
ProcessAware processAware = (ProcessAware) adviceListener;
ExecStatus status = processAware.getProcess().status();
if (status.equals(ExecStatus.TERMINATED) || status.equals(ExecStatus.STOPPED)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,72 @@
package com.taobao.arthas.core.advisor;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
* @author hengyunabc 2020-05-18
*
*/
public class TransformerManager {
private Instrumentation instrumentation;
private List<ClassFileTransformer> watchTransformers = new CopyOnWriteArrayList<ClassFileTransformer>();
private List<ClassFileTransformer> traceTransformers = new CopyOnWriteArrayList<ClassFileTransformer>();
private ClassFileTransformer classFileTransformer;
public TransformerManager(Instrumentation instrumentation) {
this.instrumentation = instrumentation;
classFileTransformer = new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
for (ClassFileTransformer classFileTransformer : watchTransformers) {
byte[] transformResult = classFileTransformer.transform(loader, className, classBeingRedefined,
protectionDomain, classfileBuffer);
if (transformResult != null) {
classfileBuffer = transformResult;
}
}
for (ClassFileTransformer classFileTransformer : traceTransformers) {
byte[] transformResult = classFileTransformer.transform(loader, className, classBeingRedefined,
protectionDomain, classfileBuffer);
if (transformResult != null) {
classfileBuffer = transformResult;
}
}
return classfileBuffer;
}
};
instrumentation.addTransformer(classFileTransformer, true);
}
public void addTransformer(ClassFileTransformer transformer, boolean isTracing) {
if (isTracing) {
traceTransformers.add(transformer);
} else {
watchTransformers.add(transformer);
}
}
public void removeTransformer(ClassFileTransformer transformer) {
watchTransformers.remove(transformer);
traceTransformers.remove(transformer);
}
public void destroy() {
instrumentation.removeTransformer(classFileTransformer);
}
}
@@ -133,7 +133,7 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
// 这里做个补偿,如果在enhance期间,unLock被调用了,则补偿性放弃
if (session.getLock() == lock) {
// 注册通知监听器
process.register(lock, listener);
process.register(lock, listener, effect.getTransformer());
if (process.isForeground()) {
process.echoTips(Constants.Q_OR_CTRL_C_ABORT_MSG + "\n");
}
@@ -18,8 +18,6 @@ package com.taobao.arthas.core.env;
import java.util.Arrays;
import com.sun.tools.javac.util.Log;
/**
* Abstract base class representing a source of name/value property pairs. The
* underlying {@linkplain #getSource() source object} may be of any type
@@ -27,6 +27,7 @@ import com.alibaba.arthas.tunnel.client.TunnelClient;
import com.taobao.arthas.common.AnsiLog;
import com.taobao.arthas.common.PidUtils;
import com.taobao.arthas.core.advisor.AdviceWeaver;
import com.taobao.arthas.core.advisor.TransformerManager;
import com.taobao.arthas.core.command.BuiltinCommandPack;
import com.taobao.arthas.core.config.BinderUtils;
import com.taobao.arthas.core.config.Configure;
@@ -80,6 +81,8 @@ public class ArthasBootstrap {
private Timer timer = new Timer("arthas-timer", true);
private TransformerManager transformerManager;
private ArthasBootstrap(Instrumentation instrumentation, String args) throws Throwable {
this.instrumentation = instrumentation;
@@ -114,6 +117,7 @@ public class ArthasBootstrap {
}
};
transformerManager = new TransformerManager(instrumentation);
Runtime.getRuntime().addShutdownHook(shutdown);
}
@@ -330,6 +334,7 @@ public class ArthasBootstrap {
}
}
executorService.shutdownNow();
transformerManager.destroy();
UserStatUtil.destroy();
// clear the reference in Spy class.
cleanUpSpyReference();
@@ -395,6 +400,14 @@ public class ArthasBootstrap {
return this.timer;
}
public Instrumentation getInstrumentation() {
return this.instrumentation;
}
public TransformerManager getTransformerManager() {
return this.transformerManager;
}
private Logger logger() {
return LoggerFactory.getLogger(this.getClass());
}
@@ -7,6 +7,7 @@ import com.taobao.arthas.core.shell.session.Session;
import com.taobao.arthas.core.shell.term.Tty;
import com.taobao.middleware.cli.CommandLine;
import java.lang.instrument.ClassFileTransformer;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -125,7 +126,7 @@ public interface CommandProcess extends Tty {
* @param lock the lock for enhance class
* @param listener
*/
void register(int lock, AdviceListener listener);
void register(int lock, AdviceListener listener, ClassFileTransformer transformer);
/**
* Unregister listener
@@ -0,0 +1,14 @@
package com.taobao.arthas.core.shell.system;
/**
*
* @author hengyunabc 2020-05-18
*
*/
public interface ProcessAware {
public Process getProcess();
public void setProcess(Process process);
}
@@ -14,8 +14,8 @@ import com.taobao.arthas.core.shell.handlers.Handler;
import com.taobao.arthas.core.shell.session.Session;
import com.taobao.arthas.core.shell.system.ExecStatus;
import com.taobao.arthas.core.shell.system.Process;
import com.taobao.arthas.core.shell.system.ProcessAware;
import com.taobao.arthas.core.shell.term.Tty;
import com.taobao.arthas.core.shell.term.impl.httptelnet.HttpTelnetTermServer;
import com.taobao.arthas.core.util.usage.StyledUsageFormatter;
import com.taobao.middleware.cli.CLIException;
import com.taobao.middleware.cli.CommandLine;
@@ -24,6 +24,7 @@ import com.taobao.text.Color;
import io.termd.core.function.Function;
import java.lang.instrument.ClassFileTransformer;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
@@ -348,7 +349,7 @@ public class ProcessImpl implements Process {
return;
}
process = new CommandProcessImpl(args2, tty, cl);
process = new CommandProcessImpl(this, args2, tty, cl);
if (cacheLocation() != null) {
process.echoTips("job id : " + this.jobId + "\n");
process.echoTips("cache location : " + cacheLocation() + "\n");
@@ -379,15 +380,17 @@ public class ProcessImpl implements Process {
}
private class CommandProcessImpl implements CommandProcess {
private final Process process;
private final List<String> args2;
private final Tty tty;
private final CommandLine commandLine;
private int enhanceLock = -1;
private AtomicInteger times = new AtomicInteger();
private AdviceListener suspendedListener = null;
private ClassFileTransformer transformer;
public CommandProcessImpl(List<String> args2, Tty tty, CommandLine commandLine) {
public CommandProcessImpl(Process process, List<String> args2, Tty tty, CommandLine commandLine) {
this.process = process;
this.args2 = args2;
this.tty = tty;
this.commandLine = commandLine;
@@ -525,13 +528,23 @@ public class ProcessImpl implements Process {
}
@Override
public void register(int enhanceLock, AdviceListener listener) {
public void register(int enhanceLock, AdviceListener listener, ClassFileTransformer transformer) {
this.enhanceLock = enhanceLock;
if (listener instanceof ProcessAware) {
((ProcessAware) listener).setProcess(this.process);
}
AdviceWeaver.reg(enhanceLock, listener);
this.transformer = transformer;
}
@Override
public void unregister() {
if (transformer != null) {
ArthasBootstrap.getInstance().getTransformerManager().removeTransformer(transformer);
}
AdviceWeaver.unReg(enhanceLock);
}
@@ -3,6 +3,7 @@ package com.taobao.arthas.core.util.affect;
import com.taobao.arthas.core.GlobalOptions;
import java.io.File;
import java.lang.instrument.ClassFileTransformer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicInteger;
@@ -18,19 +19,13 @@ public final class EnhancerAffect extends Affect {
private final AtomicInteger cCnt = new AtomicInteger();
private final AtomicInteger mCnt = new AtomicInteger();
private ClassFileTransformer transformer;
/**
* dumpClass的文件存放集合
*/
private final Collection<File> classDumpFiles = new ArrayList<File>();
public EnhancerAffect() {
}
public EnhancerAffect(int cCnt, int mCnt) {
this.cCnt(cCnt);
this.mCnt(mCnt);
}
/**
@@ -80,6 +75,14 @@ public final class EnhancerAffect extends Affect {
return classDumpFiles;
}
public ClassFileTransformer getTransformer() {
return transformer;
}
public void setTransformer(ClassFileTransformer transformer) {
this.transformer = transformer;
}
@Override
public String toString() {
final StringBuilder infoSB = new StringBuilder();