Grays Anatomy源码浅析
标签(空格分隔):JAVA JVM 问题排查
在知乎上看到一个问题,被R大推荐了这个线上排查的工具,就下来用了用,感觉还不错,知道是Java写的后,就行看看源码,相关知识比较欠缺,前后看了一个月左右,才知其大概原理,记录下来分给大家.
读源码前,需要掌握几点知识,不然硬看是看不懂的.
Instrumentation
利用 Java 代码,即 java.lang.instrument 做动态 Instrumentation 是 Java SE 5 的新特性,它把 Java 的 instrument 功能从本地代码中解放出来,使之可以用 Java 代码的方式解决问题。使用 Instrumentation,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。有了这样的功能,开发者就可以实现更为灵活的运行时虚拟机监控和 Java 类操作了,这样的特性实际上提供了一种虚拟机级别支持的 AOP 实现方式,使得开发者无需对 JDK 做任何升级和改动,就可以实现某些 AOP 的功能了。
实现方法:
1.实现ClassFileTransformer来完成AOP
2.编写 premain 函数
编写一个 Java 类,包含如下两个方法当中的任何一个,来指定哪些class需要做AOP
public static void premain(String agentArgs, Instrumentation inst);
public static void premain(String agentArgs);
3.jar 文件打包
将这个 Java 类打包成一个 jar 文件,并在其中的 manifest 属性当中加入” Premain-Class”来指定步骤 2 当中编写的那个带有 premain 的 Java 类。(可能还需要指定其他属性以开启更多功能)
Manifest-Version: 1.0
Premain-Class: Premain
4.运行
用如下方式运行带有 Instrumentation 的 Java 程序:
java -javaagent:jar 文件的位置 [= 传入 premain 的参数 ]
在 Java SE 6 里面,instrumentation 包被赋予了更强大的功能:启动后的 instrument、本地代码(native code)instrument,以及动态改变 classpath 等等。这些改变,意味着 Java 具有了更强的动态控制、解释能力,它使得 Java 语言变得更加灵活多变。
在 Java SE6 里面,最大的改变使运行时的 Instrumentation 成为可能。在 Java SE 5 中,Instrument 要求在运行前利用命令行参数或者系统参数来设置代理类,在实际的运行之中,虚拟机在初始化之时(在绝大多数的 Java 类库被载入之前),instrumentation 的设置已经启动,并在虚拟机中设置了回调函数,检测特定类的加载情况,并完成实际工作。但是在实际的很多的情况下,我们没有办法在虚拟机启动之时就为其设定代理,这样实际上限制了 instrument 的应用。而 Java SE 6 的新特性改变了这种情况,通过 Java Tool API 中的 attach 方式,我们可以很方便地在运行过程中动态地设置加载代理类,以达到 instrumentation 的目的。
另外,对 native 的 Instrumentation 也是 Java SE 6 的一个崭新的功能,这使以前无法完成的功能 —— 对 native 接口的 instrumentation 可以在 Java SE 6 中,通过一个或者一系列的 prefix 添加而得以完成。
最后,Java SE 6 里的 Instrumentation 也增加了动态添加 class path 的功能。所有这些新的功能,都使得 instrument 包的功能更加丰富,从而使 Java 语言本身更加强大。
实现方法:
1.实现ClassFileTransformer来完成AOP
2.编写 agentmain 函数
编写一个 Java 类,包含如下两个方法当中的任何一个,来指定哪些class需要做AOP
public static void agentmain (String agentArgs, Instrumentation inst);
public static void agentmain (String agentArgs);
3.jar 文件打包
将这个 Java 类打包成一个 jar 文件,并在其中的 manifest 属性当中加入” Agent-Class”来指定步骤 2 当中编写的那个带有 agentmain 的 Java 类。(可能还需要指定其他属性以开启更多功能)
Manifest-Version: 1.0
Agent-Class: AgentMain
4.运行
用如下方式运行带有 Instrumentation 的 Java 程序:
java -javaagent:jar 文件的位置 [= 传入 premain 的参数 ]
这里只是简要概述下,具体请点标题链接
ASM
ASM是一种字节码增强技术,即通过修改字节码来实现修改类的行为的功能.没有找到讲ASM比较好的博客,还是官方文档明晰透彻还会穿插着讲讲JVM的方法调用模型,建议阅读之,这里就举个ASM的小例子来说明它的使用.
类C如下所示
public class C {
public void m() throws Exception {
Thread.sleep(100);
}
}
通过ASM动态修改其字节码将其方法加上计算时间调用的功能
public class C {
public static long timer;
public void m() throws Exception {
timer -= System.currentTimeMillis();
Thread.sleep(100);
timer += System.currentTimeMillis();
} }
ASM方法编写如下,详情请见ASM官方文档Core API,其实若不考虑性能影响的话,Tree API更符合Java程序员的思维
public class AddTimerAdapter extends ClassVisitor {
private String owner;
private boolean isInterface;
public AddTimerAdapter(ClassVisitor cv) {
super(ASM4, cv);
}
@Override public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
cv.visit(version, access, name, signature, superName, interfaces);
owner = name;
isInterface = (access & ACC_INTERFACE) != 0;
}
@Override public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
exceptions);
if (!isInterface && mv != null && !name.equals("<init>")) {
mv = new AddTimerMethodAdapter(mv);
}
return mv;
}
@Override public void visitEnd() {
if (!isInterface) {
FieldVisitor fv = cv.visitField(ACC_PUBLIC + ACC_STATIC, "timer",
"J", null, null);
if (fv != null) {
fv.visitEnd();
}
}
cv.visitEnd();
}
public class AddTimerMethodAdapter extends MethodVisitor {
public AddTimerMethodAdapter(org.objectweb.asm.MethodVisitor mv) {
super(ASM4,mv);
}
@Override
public void visitCode() {
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "()J");
mv.visitInsn(LSUB);
mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
}
@Override public void visitInsn(int opcode) {
if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "()J");
mv.visitInsn(LADD);
mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
}
mv.visitInsn(opcode);
}
@Override public void visitMaxs(int maxStack, int maxLocals) {
mv.visitMaxs(maxStack + 4, maxLocals);
}
}
public static void main(String[] args) throws IOException {
ClassReader cr = new ClassReader("com.asm.temp.C");
ClassWriter cw = new ClassWriter(cr,0);
TraceClassVisitor classVisitor = new TraceClassVisitor(cw,new PrintWriter(System.out));
AddTimerAdapter addTimerAdapter = new AddTimerAdapter(classVisitor);
cr.accept(addTimerAdapter,0);
System.out.println(cw.toByteArray());
}
}
通过输出的字节码,可以看出其已加上了计算调用的功能
GETSTATIC C.timer : J
INVOKESTATIC java/lang/System.currentTimeMillis()J LSUB
PUTSTATIC C.timer : J
LDC 100
INVOKESTATIC java/lang/Thread.sleep(J)V
GETSTATIC C.timer : J
INVOKESTATIC java/lang/System.currentTimeMillis()J LADD
PUTSTATIC C.timer : J
RETURN
MAXSTACK = 4
MAXLOCALS = 1
Grays Anatomy
Greys要实现的功能,是动态的监测JVM方法的执行.很自然的就想到了它会去实现Instrumentation的preMain和agentMain.AgentLauncher实现了preMain和agentMain方法,其都调用了main方法,其主要功能透过反射实例化一个GaServer.这个Server主要实现的就是接受命令,处理命令,返回响应.
public class AgentLauncher {
public static void premain(String args, Instrumentation inst) {
main(args, inst);
}
public static void agentmain(String args, Instrumentation inst) {
main(args, inst);
}
private static synchronized void main(final String args, final Instrumentation inst) {
try {
// 传递的args参数分两个部分:agentJar路径和agentArgs
// 分别是Agent的JAR包路径和期望传递到服务端的参数
final int index = args.indexOf(';');
final String agentJar = args.substring(0, index);
final String agentArgs = args.substring(index, args.length());
// 将Spy添加到BootstrapClassLoader
inst.appendToBootstrapClassLoaderSearch(
new JarFile(AgentLauncher.class.getProtectionDomain().getCodeSource().getLocation().getFile())
);
// 构造自定义的类加载器,尽量减少Greys对现有工程的侵蚀
final ClassLoader agentLoader = loadOrDefineClassLoader(agentJar);
// Configure类定义
final Class<?> classOfConfigure = agentLoader.loadClass("com.github.ompc.greys.core.Configure");
// GaServer类定义
final Class<?> classOfGaServer = agentLoader.loadClass("com.github.ompc.greys.core.server.GaServer");
// 反序列化成Configure类实例
final Object objectOfConfigure = classOfConfigure.getMethod("toConfigure", String.class)
.invoke(null, agentArgs);
// JavaPid
final int javaPid = (Integer) classOfConfigure.getMethod("getJavaPid").invoke(objectOfConfigure);
// 获取GaServer单例
final Object objectOfGaServer = classOfGaServer
.getMethod("getInstance", int.class, Instrumentation.class)
.invoke(null, javaPid, inst);
// gaServer.isBind()
final boolean isBind = (Boolean) classOfGaServer.getMethod("isBind").invoke(objectOfGaServer);
if (!isBind) {
try {
classOfGaServer.getMethod("bind", classOfConfigure).invoke(objectOfGaServer, objectOfConfigure);
} catch (Throwable t) {
classOfGaServer.getMethod("destroy").invoke(objectOfGaServer);
throw t;
}
}
} catch (Throwable t) {
t.printStackTrace();
}
}
GaServer的bind方法为启动服务,启动后activeSelectorDaemon方法启动一个Daemon线程负责命令的处理,其中doRead是主要逻辑的实现,其中又委托给了CommandHandler的executeCommand方法解析输入行并执行命令.
public class GaServer {
/**
* 启动Greys服务端
*
* @param configure 配置信息
* @throws IOException 服务器启动失败
*/
public void bind(Configure configure) throws IOException {
if (!isBindRef.compareAndSet(false, true)) {
throw new IllegalStateException("already bind");
}
try {
serverSocketChannel = ServerSocketChannel.open();
selector = Selector.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().setSoTimeout(configure.getConnectTimeout());
serverSocketChannel.socket().setReuseAddress(true);
serverSocketChannel.register(selector, OP_ACCEPT);
// 服务器挂载端口
serverSocketChannel.socket().bind(getInetSocketAddress(configure.getTargetIp(), configure.getTargetPort()), 24);
logger.info("ga-server listening on network={};port={};timeout={};", configure.getTargetIp(),
configure.getTargetPort(),
configure.getConnectTimeout());
activeSelectorDaemon(selector, configure);
} catch (IOException e) {
unbind();
throw e;
}
}
private void activeSelectorDaemon(final Selector selector, final Configure configure) {
final ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
final Thread gaServerSelectorDaemon = new Thread("ga-selector-daemon") {
@Override
public void run() {
while (!isInterrupted()
&& isBind()) {
try {
while (selector.isOpen()
&& selector.select() > 0) {
final Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
final SelectionKey key = it.next();
it.remove();
// do ssc accept
if (key.isValid() && key.isAcceptable()) {
doAccept(key, selector, configure);
}
// do sc read
if (key.isValid() && key.isReadable()) {
doRead(byteBuffer, key);
}
}
}
} catch (IOException e) {
logger.warn("selector failed.", e);
} catch (ClosedSelectorException e) {
logger.debug("selector closed.", e);
}
}
}
};
gaServerSelectorDaemon.setDaemon(true);
gaServerSelectorDaemon.start();
}
private void doRead(final ByteBuffer byteBuffer, SelectionKey key) {
final GaAttachment attachment = (GaAttachment) key.attachment();
final SocketChannel socketChannel = (SocketChannel) key.channel();
final Session session = attachment.getSession();
try {
// 若读到EOF,则说明SocketChannel已经关闭
if (EOF == socketChannel.read(byteBuffer)) {
logger.info("client={}@session[{}] was closed.", socketChannel, session.getSessionId());
// closeSocketChannel(key, socketChannel);
session.destroy();
if(session.isLocked()) {
session.unLock();
}
return;
}
// decode for line
byteBuffer.flip();
while (byteBuffer.hasRemaining()) {
switch (attachment.getLineDecodeState()) {
case READ_CHAR: {
final byte data = byteBuffer.get();
if ('\n' == data) {
attachment.setLineDecodeState(READ_EOL);
}
// 遇到中止命令(CTRL_D),则标记会话为不可写,让后台任务停下
else if (CTRL_D == data
|| CTRL_X == data) {
session.unLock();
break;
}
// 普通byte则持续放入到缓存中
else {
if ('\r' != data) {
attachment.put(data);
}
break;
}
}
case READ_EOL: {
final String line = attachment.clearAndGetLine(session.getCharset());
executorService.execute(new Runnable() {
@Override
public void run() {
// 会话只有未锁定的时候才能响应命令
if (session.tryLock()) {
try {
// 命令执行
commandHandler.executeCommand(line, session);
// 命令结束之后需要传输EOT告诉client命令传输已经完结,可以展示提示符
socketChannel.write(ByteBuffer.wrap(new byte[]{EOT}));
} catch (IOException e) {
logger.info("network communicate failed, session[{}] will be close.",
session.getSessionId());
session.destroy();
} finally {
session.unLock();
}
} else {
logger.info("session[{}] was locked, ignore this command.",
session.getSessionId());
}
}
});
attachment.setLineDecodeState(READ_CHAR);
break;
}
}
}//while for line decode
byteBuffer.clear();
}
// 处理
catch (IOException e) {
logger.warn("read/write data failed, session[{}] will be close.", session.getSessionId(), e);
closeSocketChannel(key, socketChannel);
session.destroy();
}
}
}
接下来看看CommandHandler接口的默认实现DefaultCommandHandler,其executeCommand方法的主逻辑由excute实现,我们最关心的类增强部分是通过EnhancerAffect实现的.
public class DefaultCommandHandler implements CommandHandler {
@Override
public void executeCommand(final String line, final Session session) throws IOException {
final Command command = Commands.getInstance().newCommand(line);
execute(session, command);
}
/*
* 执行命令
*/
private void execute(final Session session, final Command command) throws GaExecuteException, IOException {
// 需要做类增强的动作
else if (action instanceof GetEnhancerAction) {
affect = new EnhancerAffect();
// 执行命令动作 & 获取增强器
final Command.GetEnhancer getEnhancer = ((GetEnhancerAction) action).action(session, inst, printer);
final int lock = session.getLock();
final AdviceListener listener = getEnhancer.getAdviceListener();
final EnhancerAffect enhancerAffect = Enhancer.enhance(
inst,
lock,
listener instanceof InvokeTraceable,
getEnhancer.getPointCut()
);
}
}
}
其增强其是AdviceListener的实现类,AdviceListener由before,afterReturning等围绕着方法执行阶段的方法组成,按道理这时候就该是ASM登场,修改类行为的时刻了.
/**
* 前置通知
*
* @param loader 类加载器
* @param className 类名
* @param methodName 方法名
* @param methodDesc 方法描述
* @param target 目标类实例
* 若目标为静态方法,则为null
* @param args 参数列表
* @throws Throwable 通知过程出错
*/
void before(
ClassLoader loader, String className, String methodName, String methodDesc,
Object target, Object[] args) throws Throwable;
/**
* 返回通知
*
* @param loader 类加载器
* @param className 类名
* @param methodName 方法名
* @param methodDesc 方法描述
* @param target 目标类实例
* 若目标为静态方法,则为null
* @param args 参数列表
* @param returnObject 返回结果
* 若为无返回值方法(void),则为null
* @throws Throwable 通知过程出错
*/
void afterReturning(
ClassLoader loader, String className, String methodName, String methodDesc,
Object target, Object[] args,
Object returnObject) throws Throwable;
接下来看起增强方法,可见真正的增强class是Enhancer
public static synchronized EnhancerAffect enhance(
final Instrumentation inst,
final int adviceId,
final boolean isTracing,
final PointCut pointCut) throws UnmodifiableClassException {
final EnhancerAffect affect = new EnhancerAffect();
final Map<Class<?>, Matcher<AsmMethod>> enhanceMap = toEnhanceMap(pointCut);
// 构建增强器
final Enhancer enhancer = new Enhancer(adviceId, isTracing, enhanceMap, affect);
try {
inst.addTransformer(enhancer, true);
// 批量增强
if (GlobalOptions.isBatchReTransform) {
final int size = enhanceMap.size();
final Class<?>[] classArray = new Class<?>[size];
arraycopy(enhanceMap.keySet().toArray(), 0, classArray, 0, size);
if (classArray.length > 0) {
inst.retransformClasses(classArray);
}
}
// for each 增强
else {
for (Class<?> clazz : enhanceMap.keySet()) {
try {
inst.retransformClasses(clazz);
} catch (Throwable t) {
logger.warn("reTransform {} failed.", clazz, t);
if (t instanceof UnmodifiableClassException) {
throw (UnmodifiableClassException) t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RuntimeException(t);
}
}
}
}
} finally {
inst.removeTransformer(enhancer);
}
return affect;
}
Enhancer实现了Java Instrumentation的接口ClassFileTransformer,来看其核心方法transform
@Override
public byte[] transform(
final ClassLoader inClassLoader,
final String className,
final Class<?> classBeingRedefined,
final ProtectionDomain protectionDomain,
final byte[] classfileBuffer) throws IllegalClassFormatException {
// 过滤掉不在增强集合范围内的类
if (!enhanceMap.containsKey(classBeingRedefined)) {
return null;
}
final ClassReader cr;
// 首先先检查是否在缓存中存在Class字节码
// 因为要支持多人协作,存在多人同时增强的情况
final byte[] byteOfClassInCache = classBytesCache.get(classBeingRedefined);
if (null != byteOfClassInCache) {
cr = new ClassReader(byteOfClassInCache);
}
// 如果没有命中缓存,则从原始字节码开始增强
else {
cr = new ClassReader(classfileBuffer);
}
// 获取这个类所对应的asm方法匹配
final Matcher<AsmMethod> asmMethodMatcher = enhanceMap.get(classBeingRedefined);
// 字节码增强
final ClassWriter cw = new ClassWriter(cr, COMPUTE_FRAMES | COMPUTE_MAXS) {
/*
* 注意,为了自动计算帧的大小,有时必须计算两个类共同的父类。
* 缺省情况下,ClassWriter将会在getCommonSuperClass方法中计算这些,通过在加载这两个类进入虚拟机时,使用反射API来计算。
* 但是,如果你将要生成的几个类相互之间引用,这将会带来问题,因为引用的类可能还不存在。
* 在这种情况下,你可以重写getCommonSuperClass方法来解决这个问题。
*
* 通过重写 getCommonSuperClass() 方法,更正获取ClassLoader的方式,改成使用指定ClassLoader的方式进行。
* 规避了原有代码采用Object.class.getClassLoader()的方式
*/
@Override
protected String getCommonSuperClass(String type1, String type2) {
Class<?> c, d;
try {
c = Class.forName(type1.replace('/', '.'), false, inClassLoader);
d = Class.forName(type2.replace('/', '.'), false, inClassLoader);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (c.isAssignableFrom(d)) {
return type1;
}
if (d.isAssignableFrom(c)) {
return type2;
}
if (c.isInterface() || d.isInterface()) {
return "java/lang/Object";
} else {
do {
c = c.getSuperclass();
} while (!c.isAssignableFrom(d));
return c.getName().replace('.', '/');
}
}
};
try {
// 生成增强字节码
cr.accept(new AdviceWeaver(adviceId, isTracing, cr.getClassName(), asmMethodMatcher, affect, cw), EXPAND_FRAMES);
final byte[] enhanceClassByteArray = cw.toByteArray();
// 生成成功,推入缓存
classBytesCache.put(classBeingRedefined, enhanceClassByteArray);
// dump the class
dumpClassIfNecessary(className, enhanceClassByteArray, affect);
// 成功计数
affect.cCnt(1);
// 排遣间谍
try {
spy(inClassLoader);
} catch (Throwable t) {
logger.warn("print spy failed. classname={};loader={};", className, inClassLoader, t);
throw t;
}
return enhanceClassByteArray;
} catch (Throwable t) {
logger.warn("transform loader[{}]:class[{}] failed.", inClassLoader, className, t);
}
return null;
}
其最主要的逻辑应该是派遣间谍了
/*
* 派遣间谍混入对方的classLoader中
*/
private void spy(final ClassLoader targetClassLoader)
throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
// 如果对方是bootstrap就算了
if (null == targetClassLoader) {
return;
}
// Enhancer类只可能从greysClassLoader中加载
// 所以找他要ClassLoader是靠谱的
final ClassLoader greysClassLoader = Enhancer.class.getClassLoader();
final String spyClassName = GaStringUtils.SPY_CLASSNAME;
// 从GreysClassLoader中加载Spy
final Class<?> spyClassFromGreysClassLoader = loadSpyClassFromGreysClassLoader(greysClassLoader, spyClassName);
if (null == spyClassFromGreysClassLoader) {
return;
}
// 从目标ClassLoader中尝试加载或定义ClassLoader
Class<?> spyClassFromTargetClassLoader = null;
try {
// 去目标类加载器中找下是否已经存在间谍
// 如果间谍已经存在就算了
spyClassFromTargetClassLoader = targetClassLoader.loadClass(spyClassName);
logger.info("Spy already in targetClassLoader : " + targetClassLoader);
}
// 看来间谍不存在啊
catch (ClassNotFoundException cnfe) {
try {// 在目标类加载起中混入间谍
spyClassFromTargetClassLoader = defineClass(
targetClassLoader,
spyClassName,
toByteArray(Enhancer.class.getResourceAsStream("/" + spyClassName.replace('.', '/') + ".class"))
);
} catch (InvocationTargetException ite) {
if (ite.getCause() instanceof java.lang.LinkageError) {
// CloudEngine 由于 loadClass 不到,会导致 java.lang.LinkageError: loader (instance of com/alipay/cloudengine/extensions/equinox/KernelAceClassLoader): attempted duplicate class definition for name: "com/taobao/arthas/core/advisor/Spy"
// 这里尝试忽略
logger.debug("resolve #112 issues", ite);
} else {
throw ite;
}
}
}
// 无论从哪里取到spyClass,都需要重新初始化一次
// 用以兼容重新加载的场景
// 当然,这样做会给渲染的过程带来一定的性能开销,不过能简化编码复杂度
finally {
if (null != spyClassFromTargetClassLoader) {
// 初始化间谍
invokeStaticMethod(
spyClassFromTargetClassLoader,
"init",
greysClassLoader,
getField(spyClassFromGreysClassLoader, "ON_BEFORE_METHOD").get(null),
getField(spyClassFromGreysClassLoader, "ON_RETURN_METHOD").get(null),
getField(spyClassFromGreysClassLoader, "ON_THROWS_METHOD").get(null),
getField(spyClassFromGreysClassLoader, "BEFORE_INVOKING_METHOD").get(null),
getField(spyClassFromGreysClassLoader, "AFTER_INVOKING_METHOD").get(null),
getField(spyClassFromGreysClassLoader, "THROW_INVOKING_METHOD").get(null)
);
}
}
}
接下来看Spy的实现,发现其没什么特别的啊,怎么实现织入呢,期间在这迷了很久
public class Spy {
// -- 各种Advice的钩子引用 --
public static volatile Method ON_BEFORE_METHOD;
public static volatile Method ON_RETURN_METHOD;
public static volatile Method ON_THROWS_METHOD;
public static volatile Method BEFORE_INVOKING_METHOD;
public static volatile Method AFTER_INVOKING_METHOD;
public static volatile Method THROW_INVOKING_METHOD;
/**
* 代理重设方法
*/
public static volatile Method AGENT_RESET_METHOD;
/*
* 用于普通的间谍初始化
*/
public static void init(
@Deprecated
ClassLoader classLoader,
Method onBeforeMethod,
Method onReturnMethod,
Method onThrowsMethod,
Method beforeInvokingMethod,
Method afterInvokingMethod,
Method throwInvokingMethod) {
ON_BEFORE_METHOD = onBeforeMethod;
ON_RETURN_METHOD = onReturnMethod;
ON_THROWS_METHOD = onThrowsMethod;
BEFORE_INVOKING_METHOD = beforeInvokingMethod;
AFTER_INVOKING_METHOD = afterInvokingMethod;
THROW_INVOKING_METHOD = throwInvokingMethod;
}
/*
* 用于启动线程初始化
*/
public static void initForAgentLauncher(
@Deprecated
ClassLoader classLoader,
Method onBeforeMethod,
Method onReturnMethod,
Method onThrowsMethod,
Method beforeInvokingMethod,
Method afterInvokingMethod,
Method throwInvokingMethod,
Method agentResetMethod) {
ON_BEFORE_METHOD = onBeforeMethod;
ON_RETURN_METHOD = onReturnMethod;
ON_THROWS_METHOD = onThrowsMethod;
BEFORE_INVOKING_METHOD = beforeInvokingMethod;
AFTER_INVOKING_METHOD = afterInvokingMethod;
THROW_INVOKING_METHOD = throwInvokingMethod;
AGENT_RESET_METHOD = agentResetMethod;
}
public static void clean() {
ON_BEFORE_METHOD = null;
ON_RETURN_METHOD = null;
ON_THROWS_METHOD = null;
BEFORE_INVOKING_METHOD = null;
AFTER_INVOKING_METHOD = null;
THROW_INVOKING_METHOD = null;
AGENT_RESET_METHOD = null;
}
}
迷久了,偶尔查看其方法的调用,发现奥妙,其真正值得织入逻辑原来是在AdviceWeaver的相关方法内
private static ClassLoader loadOrDefineClassLoader(String agentJar) throws Throwable {
final ClassLoader classLoader;
// 如果已经被启动则返回之前启动的classloader
if (null != greysClassLoader) {
classLoader = greysClassLoader;
}
// 如果未启动则重新加载
else {
classLoader = new AgentClassLoader(agentJar);
// 获取各种Hook
final Class<?> adviceWeaverClass = classLoader.loadClass("com.github.ompc.greys.core.advisor.AdviceWeaver");
// 初始化全局间谍
Spy.initForAgentLauncher(
classLoader,
adviceWeaverClass.getMethod("methodOnBegin",
int.class,
ClassLoader.class,
String.class,
String.class,
String.class,
Object.class,
Object[].class),
adviceWeaverClass.getMethod("methodOnReturnEnd",
Object.class,
int.class),
adviceWeaverClass.getMethod("methodOnThrowingEnd",
Throwable.class,
int.class),
adviceWeaverClass.getMethod("methodOnInvokeBeforeTracing",
int.class,
Integer.class,
String.class,
String.class,
String.class),
adviceWeaverClass.getMethod("methodOnInvokeAfterTracing",
int.class,
Integer.class,
String.class,
String.class,
String.class),
adviceWeaverClass.getMethod("methodOnInvokeThrowTracing",
int.class,
Integer.class,
String.class,
String.class,
String.class,
String.class),
AgentLauncher.class.getMethod("resetGreysClassLoader")
);
}
return greysClassLoader = classLoader;
}
抽出一个方法来看,其最终还是委托listner的before来实现的,MonitorCommand只是实现一个invokeCost.Begin.可是没有字节码增强啊,怎么能动态实现呢
/**
* 方法开始<br/>
* 用于编织通知器,外部不会直接调用
*
* @param loader 类加载器
* @param adviceId 通知ID
* @param className 类名
* @param methodName 方法名
* @param methodDesc 方法描述
* @param target 返回结果
* 若为无返回值方法(void),则为null
* @param args 参数列表
*/
public static void methodOnBegin(
int adviceId,
ClassLoader loader, String className, String methodName, String methodDesc,
Object target, Object[] args) {
if (!advices.containsKey(adviceId)) {
return;
}
if (isSelfCallRef.get()) {
return;
} else {
isSelfCallRef.set(true);
}
try {
// 构建执行帧栈,保护当前的执行现场
final GaStack<Object> frameStack = new ThreadUnsafeFixGaStack<Object>(FRAME_STACK_SIZE);
frameStack.push(loader);
frameStack.push(className);
frameStack.push(methodName);
frameStack.push(methodDesc);
frameStack.push(target);
frameStack.push(args);
final AdviceListener listener = getListener(adviceId);
frameStack.push(listener);
// 获取通知器并做前置通知
before(listener, loader, className, methodName, methodDesc, target, args);
// 保护当前执行帧栈,压入线程帧栈
threadFrameStackPush(frameStack);
} finally {
isSelfCallRef.set(false);
}
}
其实上面都是幌子,真正的增强是透过visitMethod实现的,其又委托了AdviceAdapter实现,其onMethodEnter方法是真正的before类增强(参见ASM官方文档),
这里面不只用了字节码增强,还直接操作了堆栈,这部分看的云里雾里的.你有好的资料推荐我学习,我会很感谢的,如果我实现的话应该就如上述ASM例子中的实现,加字节码之类的吧.
@Override
protected void onMethodEnter() {
codeLockForTracing.lock(new CodeLock.Block() {
@Override
public void code() {
final StringBuilder append = new StringBuilder();
_debug(append, "debug:onMethodEnter()");
// 加载before方法
loadAdviceMethod(KEY_GREYS_ADVICE_BEFORE_METHOD);
_debug(append, "loadAdviceMethod()");
// 推入Method.invoke()的第一个参数
pushNull();
// 方法参数
loadArrayForBefore();
_debug(append, "loadArrayForBefore()");
// 调用方法
invokeVirtual(ASM_TYPE_METHOD, ASM_METHOD_METHOD_INVOKE);
pop();
_debug(append, "invokeVirtual()");
}
});
mark(beginLabel);
}
最近看了几个阿里开源的框架或工具,希望能有机会去阿里码代码,和优秀的人一起共事.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。