-
Notifications
You must be signed in to change notification settings - Fork 218
DevGuideCN
- Fork我的源码库
- Fork 成功后, 从自己的库中克隆都本地(注意替换
$yourname
),git clone https://github.com/$yourname/HouseMD.git housemd
-
src
源代码目录(含单元测试代码) -
acceptance-test
验收测试代码目录 -
bin
安装和运行脚本目录 -
project
SBT
构建脚本和插件配置目录 -
sbt
SBT
启动脚本
执行./sbt
, 进入SBT命令行.
注意, 第一次执行
./sbt
, 此脚本会自动下载SBT
, 并启动它. 此后有一段较长时间的初始化, 以及下载依赖包的过程, 期间可能会因为GFW
原因导致一些依赖无法下载, 请在sbt
脚本文件中配置HTTP代理.
下面的命令全部是在进入SBT
命令行之后执行的:
> compile
编译源码
> test
执行测试用例
> assembly
打包
更多SBT
的使用请细看官方文档
HouseMD
使用主要编程语言是Scala和Java, 下面是常用IDE
准备说明, 若你喜欢的工具不在此列, 我表示很遗憾, 请咨询Google: scala $your_favorite_ide_or_editor
IntelliJ IDEA (推荐首选)
-
下载
IDEA
的社区版(免费) - 启动
IDEA
, 选择菜单File
->Settings
->Plugins
中搜索并安装Scala
插件 - 在
SBT
的命令行下, 执行gen-idea
, 便会生成IDEA
的项目文件 - 在
IDEA
中, 选择菜单File
->Open Project
打开项目, 马上开始为HouseMD
贡献你的智慧吧:)
- 不多废话, 请参考官方文档安装配置
- 在
SBT
的命令行下, 执行eclipse
, 便会生成Eclipse
的项目文件 - 在
Eclipse
中打开项目, 马上开始为HouseMD
贡献你的智慧吧:)
注意, 有可能打开项目后你发现
.java
的文件会有编译错误提示, 也许Scala-IDE
目前对Scala project
中混合有.java
文件的支持不是很好, 这个是可以忽略的, 因为只需要用SBT
进行编译构建就可以了.
如果你知道怎么解决请邮件告诉我, 谢谢
原则上, 只要是对HouseMD
有帮助的都可以, 例如但不限于:
- 解决已有的Issue
- 提交新的Issue, 功能特性, 改进, 缺陷都可以
- 新增或修订Wiki页
- 分享
HouseMD
的使用和开发经验技巧, 记得邮件告诉我链接, 我会放到Wiki Home让更多人知道
提交你的贡献之前, 请做到下面几点小小的要求:
- 提交中必须含有相应的单元测试代码, 单元测试代码应能清晰的说明且验证你提供了哪些特性上的变化
- 强烈建议参考
HouseMD
已有的测试用例, 采用FunSpec
风格能够更容易的清晰表达其意图.
- 提交之前, 请确保单元测试全部通过
在你的代码提交到你的github上(获取源代码时Fork产生的库)后, 请点击Pull Request
向我发起请求, 并在提交的表单中清晰描述你的提交目的和变化要点, Github会自动邮件通知我, 随后我会第一时间处理. 更多操作帮助请见Using Pull Request.
没有比新增一些更酷的指令, 更让人兴奋的啦~
要办到它, 得从下面的类入手.
像env 这样不需要用到Instrumentation
指令, 可以直接继承Command
, 将自定义指令的逻辑实现在run
方法中.
切记, 指令只能有一个构造器, 因为只有第一个构造器会被用于实例化指令; 并且, 构造器应至少有一个参数是
PrintOut
, 用以Command
的初始化, 它是用来向控制台回显信息的. 不用担心如何构造一个PrintOut
的实例, 只用留好这个参数,HouseMD
会帮我们搞定.
典型的过程是:
- 首先, 每个
Command
的具体实现类会被House引导给Duck并由Telephone来实例化它们, - 在
housemd>
提示下输入一串字符, 按下回车后, 这串字符会被拆分为为指令和其参数选项两个部分, 通过指令部分匹配相应指令的实例, 而参数选项的部分则交由指令实例的parse
方法进行解析, - 解析完成后, 指令实例的
run
方法会被调用
Command
提供了三种方法来实现声明:
-
flag
声明布尔类型的选项, 通常用于开启什么模式或特定的动作, 例如loaded
指令的-h
选项 -
option
声明除布尔类型之外的单值选项, 选项是必须有默认值的, 例如trace
的-p
选项 -
parameter
声明参数, - 可以声明多个参数, 输入的顺序与声明的顺序一致
- 可以是单值或多值, 多个参数的情况下, 只允许最后一个参数是多值
- 可以设定默认值, 不设的话表明必须要输入参数.
请确保参数选项的声明, 使用在声明属性的位置完成的, 目的是为了保证在执行Command
的parse
方法之前, 所有的声明已完成.
细节请参考代码作为示例:
运行在目标进程的环境中, 很多JVM
提供的工具方法就可以拿来了:
- 像env, 就是通过
java.lang.System.getenv()
来实现查看目标进程的系统环境变量( 顺便提一下, 我刻意没有实现查看Properities
的功能, 留给大家练手用 :D ) - 还可以利用
java.lang.management.ManagementFactory
获得系统提供的管理Bean, 来实现诸如: 查看线程数, 内存, 执行Full GC
等等
总之, 发挥你的想象力, 指令可以运行在目标进程中了, 还有什么可以直接拿来用的呢?
若是要用到Instrumentation
的指令, 也同样要继承Command
, 与 env, 但构造器就要多一个Instrumentation
的参数, 如loaded. HouseMD
在实例化指令的时候, 会将Instrumentation
的实例传入.
指令的构造器最多就
Instrumentation
和PrintOut
两个参数, 多了会导致实例化失败.
要实现一个比trace更牛逼的指令, 前面的方法已经足够让你办到了. 但我还是强烈推荐继承TransformCommand
, 它已经帮你实现了字节码增强的部分(这部分是有很多陷阱的, 除非你有足够的经验, 真的如此我热切的希望你能帮我改进它), 还有常规选项(如-i
,-t
,-l
以及-p
) 剩下你需要做的是:
- 声明参数
- 实现
isCandidate
方法, 以过滤那些需要进行字节码增强的类, - 实现
isDecorating
方法,以过滤那些需要进行字节码增强的类的方法, - 实现
hook
方法, 返回一个回调对象, 它的方法会在特定的时机被调用: -
enterWith
会在所跟踪的方法进入之前调用,Context
对象有方法上下文的数据 -
exitWith
会在所跟踪的方法退出之后调用,Context
对象有方法上下文的数据 -
heartbeat
会像心跳一样被调用(可能一会快,可能一会慢), 主要用于实时显示信息, 如trace
的调用摘要,now
是当前时间(毫秒) -
finalize
会在指令结束前调用, 主要用于释放清理资源, 一旦指令运行中出现了异常,Option[Throwable]
会传入它.
细节请参考代码作为示例:
Context的属性说明如下:
className: String
methodName: String
loader: ClassLoader // className 所指 Class的 ClassLoader
arguments: Array[AnyRef], // 方法调用参数值数组
descriptor: String, // 方法的在字节码中的描述形式, 例如(Ljava/lang/String;)Ljava/lang/String; 表示方法有个String类型的参数, 返回值也是String类型
isVoidReturn: Boolean, // true的话表示方法返回值为void
thisObject: AnyRef, // 当前方法的宿主对象, 等同于BTrace中@self 传入的对象
started: Long, // 方法调用的开始时间(毫秒)
stack: Array[StackTraceElement], // 方法的调用栈
thread: Thread, // 调用该方法的线程
stopped: Option[Long], // 方法调用的结束时间(毫秒), 方法调用前此值为stopped.get会抛异常, 方法调用后为stopped.get会获得时间
resultOrException: Option[AnyRef]// 如果方法调用正常结束, 则为返回值(void时,为null); 反之, 是异常对象
要想自定义的指令具备良好的交互体验, 那么支持参数自动补全这个功能一定要提供. 实现这点就需要实现Completer
接口的complete
方法, 具体怎么做, 文字不好描述, 还是看源码吧.
新增的指令需在House中agentOptions
的值(:: Nil
之前)增加一下, 现在方式有点土, 欢迎高手来改进它.
更多相关疑问请提交Issue, 我们一起来讨论:)