定制属于你的HouseMD

没有比新增一些更酷的指令, 更让人兴奋的啦~

要办到它, 得从下面的类入手.

Command

像env 这样不需要用到Instrumentation指令, 可以直接继承Command, 将自定义指令的逻辑实现在run方法中.

切记, 指令只能有一个构造器, 因为只有第一个构造器会被用于实例化指令; 并且, 构造器应至少有一个参数是PrintOut, 用以Command的初始化, 它是用来向控制台回显信息的. 不用担心如何构造一个PrintOut的实例, 只用留好这个参数, HouseMD会帮我们搞定.

每个指令是如何被执行的呢?

典型的过程是:

  1. 首先, 每个Command的具体实现类会被House引导给Duck并由Telephone来实例化它们,
  2. housemd>提示下输入一串字符, 按下回车后, 这串字符会被拆分为为指令和其参数选项两个部分, 通过指令部分匹配相应指令的实例, 而参数选项的部分则交由指令实例的parse方法进行解析,
  3. 解析完成后, 指令实例的run方法会被调用

如何声明参数选项

Command提供了三种方法来实现声明:

  • flag 声明布尔类型的选项, 通常用于开启什么模式或特定的动作, 例如loaded指令的-h选项
  • option 声明除布尔类型之外的单值选项, 选项是必须有默认值的, 例如trace-p选项
  • parameter 声明参数,
    • 可以声明多个参数, 输入的顺序与声明的顺序一致
    • 可以是单值或多值, 多个参数的情况下, 只允许最后一个参数是多值
    • 可以设定默认值, 不设的话表明必须要输入参数.

请确保参数选项的声明, 使用在声明属性的位置完成的, 目的是为了保证在执行Commandparse方法之前, 所有的声明已完成.

细节请参考代码作为示例:

  • Loaded.scala
  • Trace.scala
  • Env.java
  • Inspect.java

没有InstrumentationCommand实现能做什么?

运行在目标进程的环境中, 很多JVM提供的工具方法就可以拿来了:

  • 像env, 就是通过java.lang.System.getenv()来实现查看目标进程的系统环境变量( 顺便提一下, 我刻意没有实现查看Properities的功能, 留给大家练手用 :D )
  • 还可以利用java.lang.management.ManagementFactory获得系统提供的管理Bean, 来实现诸如: 查看线程数, 内存, 执行Full GC等等

总之, 发挥你的想象力, 指令可以运行在目标进程中了, 还有什么可以直接拿来用的呢?

Instrumentation

若是要用到Instrumentation的指令, 也同样要继承Command, 与 env, 但构造器就要多一个Instrumentation的参数, 如loaded. HouseMD在实例化指令的时候, 会将Instrumentation的实例传入.

指令的构造器最多就InstrumentationPrintOut两个参数, 多了会导致实例化失败.

TransformCommand

要实现一个比trace更牛逼的指令, 前面的方法已经足够让你办到了. 但我还是强烈推荐继承TransformCommand, 它已经帮你实现了字节码增强的部分(这部分是有很多陷阱的, 除非你有足够的经验, 真的如此我热切的希望你能帮我改进它), 还有常规选项(如-i,-t,-l以及-p) 剩下你需要做的是:

  1. 声明参数
  2. 实现isCandidate方法, 以过滤那些需要进行字节码增强的类,
  3. 实现isDecorating方法,以过滤那些需要进行字节码增强的类的方法,
  4. 实现hook方法, 返回一个回调对象, 它的方法会在特定的时机被调用:
    1. enterWith会在所跟踪的方法进入之前调用, Context对象有方法上下文的数据
    2. exitWith 会在所跟踪的方法退出之后调用, Context对象有方法上下文的数据
    3. heartbeat会像心跳一样被调用(可能一会快,可能一会慢), 主要用于实时显示信息, 如trace的调用摘要, now是当前时间(毫秒)
    4. finalize 会在指令结束前调用, 主要用于释放清理资源, 一旦指令运行中出现了异常, Option[Throwable]会传入它.

细节请参考代码作为示例:

  • Trace.scala
  • Inspect.java

Completer

要想自定义的指令具备良好的交互体验, 那么支持参数自动补全这个功能一定要提供. 实现这点就需要实现Completer接口的complete方法, 具体怎么做, 文字不好描述, 还是看源码吧.

不要忘了最后一步

新增的指令需在HouseagentOptions的值(:: Nil之前)增加一下, 现在方式有点土, 欢迎高手来改进.

更多相关疑问请提交Issue, 我们一起来讨论:)

完整开发文档请见这里 

参考资料

  1. BTrace
  2. javaagent
  3. 动态跟踪Java

你可能感兴趣的:(java,scala,BTrace,housemd)