前面已经介绍了eclipse plugin如何在runtime中使用tools.jar中的类,下面来整理一下newrelic的android sdk的整体实现原理;最近国内山寨newrlic APM产品的氛围比较浓厚,很多公司比如redware,twoapm,某调都推出了自己的android sdk应用性能监测产品;技术原理大同小异,当然我也不例外,手动山寨了一下;

1、整体框架
newrelic整个android sdk项目主要分成osgi.fragment,rewriter.agent,newrlic.agent,plugin四个部分;
1)osgi.fragment
用来给equinox的plugin打patch,实现runtime加载tools.jar中的VirtualMachine类;
2)rewriter.agent
将自定义InvocationDispatcher注入到android.jar来进行指定类的动态代理,使用asm字节码框架在字节码层面hook android.jar中的类方法和apk开发者自定义的类方法;
3)newrelic.agent
收集各种系统、数据库、网络数据,被hook类方法的性能数据,地理信息,异常数据,activity栈数据,线程栈等数据;数据预统计后回传后端服务器;
4)plugin
eclipse plugin的实现,在plugin earlyStartup时使用Attach api向jvm中附着上Instrumentation代理,其中Instrumentation的代理即为rewriter.agent;在plugin IObjectActionDelegate时将newrelic.agent安装到apk开发者的android工程库目录中;

2、实现细节
osgi.fragement在前面已经介绍过,在这里就不重复介绍了;

2.1、rewriter.agent
2.1.1、rewriter的字节码hook切入时点(android的dx工具打包时)
在eclipse中,当android工程中的java代码被编译成class后,eclipse会调用dx工具将java class文件打包并转换成.dex文件,这个转换的工作在com.android.dx.command.dexer.Main的processClass方法中进行,因此我们只需要在这个时间点切入完成我们的字节码层的hook工作(在目标方法中嵌入newrelic.agent的监测函数)即可;
另外,为了更好的理解dx的工作,我们可以看一下android的构建过程,如下图所示;

图2.1 android的build process

2.1.2、InvocationHandler动态代理类
InvocationHandler实现类InvocationDispatcher用来将指定的代理类(这里指com.android.dx.command.dexer.Main的processClass)实例做字节码的hook,其中java字节码的hook工作使用asm字节码框架来进行(关于如何使用asm请查看以前的文章);visitClassBytes方法中使用了几个自定义的xxxClassVisitor用来分别hook android.jar中的annotation,activity,AsyncTask,网络,sqllite,exception等类方法;
InvocationDispatcher类代码如下:

2.1.3、Instrumentation代理(rewriter.agent)
具体的Instrumentation代理实现涉及到下面几个部分;
1)agentmain方法
Instrumentation的最大作用,就是类定义动态改变和操作。在JDK6以后,将类动态操作逻辑放到agentmain方法中,可以实现让Instrumentation代理在main函数运行前执行;
2)ClassFileTransformer实现类
对Java类文件的操作,可以理解为对一个byte数组的操作(将类文件的二进制字节流读入一个byte数组)。开发者可以在“ClassFileTransformer”的transform方法当中得到,操作并最终返回一个类的定义(一个byte数组);Instrumentation的addTransformer方法指明要转换哪个类。转换发生在premain方法中,main函数执行之前,这时每装载一个类,transform方法就会执行一次,看看是否需要转换,所以,在transform(Transformer 类)方法中,可用className.equals(“xxxClass”) 来判断当前的类是否需要转换。
3)Attach API
具体的实现上还需要使用Attach API;Attach API很简单,只有2个主要的类,都在com.sun.tools.attach包里面:VirtualMachine代表一个Java虚拟机,也就是程序需要监控的目标虚拟机,提供了JVM枚举,Attach动作和 Detach动作(Attach动作的相反行为,从JVM上面解除一个代理)等等; VirtualMachineDescriptor则是一个描述虚拟机的容器类,配合VirtualMachine类完成各种功能。

实现过程为:
1)Instrumentation代理的agentmain作为入口方法;
2)通过反射将InvocationDispatcher实例赋值给android.jar中java.util.logging.Logger.class的treeLock(Logger类已被装载到runtime,这里的treeLock用作同步的object无其他用途);
3)instrumentation实例的addTransformer方法加载ClassFileTransformer实现类;ClassFileTransformer实现类中的transform方法会根据当前传入的className,调用不同的ClassAdapter;其中在ClassAdapter中将触发InvocationDispatcher实例(java.util.logging.Logger.class的treeLock)中代理类的invoke方法;

比如:当装载com/android/dx/command/dexer/Main类时,transform会找到Main类对应的ClassAdapter,当ClassReader遍历到processClass方法时,则会执行MethodVisitor的onMethodEnter方法,方法内触发InvocationDispatcher实例(java.util.logging.Logger.class的treeLock)中代理类的invoke方法,invoke方法中将使用com/android/dx/command/dexer/Main代理类的InvocationHandler invoke方法,触发visitClassBytes方法做字节码的hook工作;(其中visitClassBytes方法中使用了几个自定义的xxxClassVisitor用来分别hook android.jar中的annotation,activity,AsyncTask,网络,sqllite,exception等类方法)

关键功能代码;
InvocationDispatcher实例注入;

DexClassTransformer:

premain方法;

未完待续,敬请期待…
2.2、newrelic.agent
2.3、plugin