1.1准备工作
1.1.1 java agent , java attach api, VirtualMachine 等
java agent代理和 virtualMachine的知识,可以参考 http://blog.csdn.net/qyongkang/article/details/7765255 大概有连续6篇文章.看完的话大概能明白几个概念了.
还有一个简单的例子 http://chenjingbo.iteye.com/blog/1966733 含金量不高 ,不过也可以将就看看.
1.1.2 scala 学习
housemd大部分的代码都是用scala写的.所以至少能读懂里面大概什么内容. 拿来主义,看这个吧 http://chenjingbo.iteye.com/blog/1974335
1.1.3 jenv
housemd下载是通过jenv的.很方便,一行命令搞定.当然使用jenv使用之前需要安装jenv.也很简单.这个不需要学习,只需要安装一下就好了 ,具体如下 https://github.com/linux-china/jenv/wiki/Chinese-Introduction
1.1.4 yascli
这个包.是一个scala的命令行开发包.具体的地址是 https://github.com/CSUG/yascli
老实说,我没有完全读透立面的代码,就当一个简单的工具看了.
1.1.5 asm
这个字节码修改工具,如果只是看看housemd的主流程,那么就不需要仔细研究了.如果想看trace命令这种需要修改字节码实现的,是如何工作的,那再仔细学习吧.
1.2 正文
1.2.1 housemd的官方地址
housemd的地址在 https://github.com/CSUG/HouseMD 具体的用法就不在这个文档里说明了.看一下 https://github.com/CSUG/HouseMD/wiki/UserGuideCN 就已经非常清楚明白了.
1.2.2程序入口
Housemd的使用,最开始当然是在终端里里输入
直接看 housemd命令对应的代码.
#!/bin/sh if [ -z "$JAVA_HOME" ]; then echo "Please set JAVA_HOME to JDK 6+!" exit 1 else ROOT=`dirname "$0"` if [ -f $JAVA_HOME/lib/tools.jar ]; then BOOT_CLASSPATH=-Xbootclasspath/a:$JAVA_HOME/lib/tools.jar fi $JAVA_HOME/bin/java $BOOT_CLASSPATH -jar $ROOT/housemd_2.9.2-0.2.6.min.jar "$@" fi
这里非常简单,只是把tools.jar设置成boot_classpath,然后执行执行 java -jar 命令运行housemd对应的jar. 既然如此,直接看对应的METAINF.MF .
Manifest-Version: 1.0 Implementation-Title: housemd Implementation-Version: 0.2.6 Specification-Vendor: com.github.zhongl Implementation-Vendor: com.github.zhongl Implementation-Vendor-Id: com.github.zhongl Specification-Title: housemd Agent-Class: com.github.zhongl.housemd.duck.Duck Specification-Version: 0.2.6 Main-Class: com.github.zhongl.housemd.house.House Can-Redefine-Classes: true Can-Retransform-Classes: true Signature-Version: 0.2.6
这个文件,后续还需要用到.这里我们需要关心的是
这样,我们就找到对应的入口class了.
1.2.3 House.scala
这个类里核心代码如下
def run() { if (printVersion()) {//如果只是打印版本,直接打印版本号 println("v" + version) return } //如果是windows系统,直接返回说不支持. if (ManagementFactory.getOperatingSystemMXBean.getName.toLowerCase.contains("window")) { throw new IllegalStateException("Sorry, Windows is not supported now.") } try { //创建一个内置的unix terminal 这个都是scala的独有类,不多说了.功能就是内置unix terminal. val terminal = new NoInterruptUnixTerminal() terminal.init() val sout = terminal.wrapOutIfNeeded(System.out) val sin = terminal.wrapInIfNeeded(System.in) val vm = VirtualMachine.attach(pid()) val mobilephone = new Mobilephone(port(), { case PickUp => info("connection established on " + port()) case ListenTo(earphone) => earphone(sout) case SpeakTo(microphone) => microphone(sin) case BreakOff(reason) => error("connection breaked causeby"); error(reason) case HangUp => terminal.restore(); silentClose(errorDetailWriter); info("bye") }) info("Welcome to HouseMD " + version) //核心代码在这里.下面会仔细说明. vm.loadAgent(agentJarFile, agentOptions mkString " ") vm.detach() mobilephone.start() } catch { case e: Throwable => error(e); silentClose(errorDetailWriter) } }一些非主要的功能,上面已经通过注释说明了.其中核心的代码是
val vm = VirtualMachine.attach(pid()) //pid()就是获取传入的pid vm.loadAgent(agentJarFile, agentOptions mkString " ") vm.detach()可以看到,他也是通过VirtualMachine load 对应的agent来实现功能的..我们再仔细看一下对应的参数.
我们知道 VirtualMachine.loadAgent 方法,第一个参数是对应agent的jar包全路径,第二个参数是给agent传入的参数.
private lazy val agentJarFile = sourceOf(Manifest.classType(getClass))housemd一共就一个jar包,也就是说,当前的class所在的jar地址就是对应agent所在的地址..sourceOf方法就是获取当前类所在的jar包地址.
private lazy val agentOptions = agentJarFile :://agent所在的jar包全路径 classNameOf[Telephone] :: //Telephone对应的全额类目.这个类后续会具体说 port() :://内置的terminal 开启的服务的端口号. classNameOf[Trace] :://后面的6个就是housemd对应支持的命令,每个命令对应一个类. classNameOf[Loaded] :: classNameOf[Env] :: classNameOf[Inspect] :: classNameOf[Prop] :: classNameOf[Resources] :: Nil //what's this后面的参数比较多.里面的参数内容,我大概都解释了.每个参数中间用空格隔开.这里的参数内容解释,后面都会用到. 好了.上面把程序入口的代码都解释了一遍.里面核心的就是attach agent了.下面,就需要看对应的agent class在哪呢.查看上面的METAINF.MF ,可以看到