mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
use TransformerManager to manager all Transformer; Enhancer only enhance bytecode once
This commit is contained in:
@@ -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:
|
||||
|
||||
+1473
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
+12
-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();
|
||||
|
||||
Reference in New Issue
Block a user