摘要: 线上系统为什么常常出错?数据库为什么屡遭黑手?业务调用为什么频频失败?连环异常堆栈案,到底是那次调用所为? 数百台服务器意外雪崩背后又隐藏着什么?是软件的扭曲仍是硬件的沦丧? 走进科学带你了解Greys, Java线上问题诊断工具。css
线上系统为什么常常出错?数据库为什么屡遭黑手?业务调用为什么频频失败?连环异常堆栈案,到底是那次调用所为?
数百台服务器意外雪崩背后又隐藏着什么?是软件的扭曲仍是硬件的沦丧?
走进科学带你了解Greys, Java线上问题诊断工具。前端
![]() |
很早的时候,咱们使用BTrace排查问题,在感叹BTrace的强大之余,也曾好几回将线上系统折腾挂掉。2012年淘宝的聚石写了HouseMD,将经常使用的几个Btrace脚本整合在一块儿造成一个独立风格的应用,但其核心代码用的是Scala,咱们没这方面的编程维护经验,因此只好艳羡HouseMD的才思敏捷而没法在其上增长功能。java
因而乎,Greys诞生了! |
PS:目前Greys仅支持Linux/Unix/Mac上的Java6+,Windows暂时没法支持linux
Greys是一个JVM进程执行过程当中的异常诊断工具。 在不中断程序执行的状况下轻松完成JVM相关问题排查工做。nginx
和HouseMD同样,Greys-Anatomy取名同名美剧“实习医生格蕾”,目的是向前辈致敬。代码编写的时候参考了BTrace和HouseMD两个前辈的思路。git
有时候忽然一个问题反馈上来,须要入参才能完成定位,但偏偏没有任何日志。回去加上从新部署,一杯咖啡时间过去了,是否是很崩溃?程序员
当你通过反复这样几回折腾以后变得聪明了,在本身的代码的全部入参和出参地方都加上debug
日志,但此次问题彷佛暴露在别人的代码中了...是否是很无奈?github
忽然遇到线上一个性能问题没法肯定究竟是哪一个环节的耗时,只能反复抓jstack
猜,还有没有办法能够好好的过日子啦?web
遇到以上问题时,你就是咱们这类工具的目标客户,此类工具能利用Java6的Instrumentation
特性,动态加强你所指定的类,获取你想要到的信息。正则表达式
ClassLoader隔离
在设计和实现这款程序的时候,花费了很是多的精力在隔离目标类与Greys的ClassLoader隔离上。你能够放心大胆的使用Greys,而不用担忧Greys会干扰到现有业务代码所使用的三方类库。
运行时加载
要求目标JVM在JDK6+的基础上,且当前执行人拥有与目标JVM相同权限。能够作到不中断当前JVM而动态进行加载、问题分析定位。
经常使用问题定位命令化
Greys与BTrace、HouseMD等同类软件最大的不一样在于,她拥有我多年来业务代码疑难杂症定位的经常使用技术手段,并将这些排查思路和技巧命令化,将个人问题定位经验Share给你们。
表达式支持
HouseMD相比BTrace最强大的地方就在于能快速指定拦截的类与方法,但却没法支持对观察到的对象进行展开、条件过滤等操做。BTrace的脚本是本身所编写,能够实现此类功能。但编写学习成本很高,且容易出错。
表达式的引用能综合这两款软件的特长同时弥补他们的不足。目前Greys所采用的是OGNL表达式。
多用户同时访问
远程DEBUG最大的问题就在于,只容许一我的访问DEBUG端口,并且一单断点条件设置不当,颇有可能将其余正常的业务请求拦下,影响其余用户的使用。
Greys采用的思路是作观察者,其所设置的断点不容许阻塞正常业务的流程,但你能够观察到断点所拦截到的全部信息。
高性能
精心用ASM设计了字节码加强,核心的数据结构用数组针对实际场景作裁剪优化。能够放心的用在高负载有求下的JVM环境。
纯Java编写
Greys定位是专业的JVM的业务问题定位工具
,既然是JVM那咱们所面对的大部分就是Java程序员。我但愿能Share本身在编写软件时候的全部技巧与思路,让更多的Java程序员能参与开发或从中受益。
目前已经有很是多的热心网友在给个人代码挑错,很是感谢这些朋友的支持!
Greys并不万能,我也没有计划让她能成为万能的问题定位工具。因此若是你在某些场合能用上更专业的工具,我会很是乐意推荐你使用。
性能环境下的性能损耗定位
性能分析须要有更专业的软件,我本身经常使用的则是JProfiler(固然是付费的了)。Greys虽然性能损耗很小,但其分析的维度太少,因此只适合作简单的性能损耗定位。当你用过专业的性能分析软件以后,就会发现什么叫专业!
开发环境下的远程DEBUG
虽然Greys能取代部分的远程DEBUG行为,但毕竟没不像DEBUG工具那样能够看到局部变量的值,并且可操做性上也没有JVM下Eclipse/IDEA等优秀的IDE自带的DEBUG工具这么人性化操做。
线上环境大规模部署
与BTrace同样,Greys获取到的权限过高,若是线上大规模部署会遭受黑客的攻击,而今天我为了实现简单是没有作过多的鉴权控制。
JDK类库分析
JDK的类库存放在rt.jar
中,启动时加载到BootstrapClassLoader
中(Hotspot-JVM),但因为Greys也是用Java语言所编写,因此自身也用到了这些基础类库,默认状况下关闭了对这些类的加强。
固然,对于Spring、ibatis、Tomcat等三方类库是能够放心大胆使用的。
其它不适合场景
BTrace、HouseMD、Greys、JavOSize此类工具都会对Perm区、CodeCache(影响JIT)产生干扰,若是你的程序对这两块很是敏感,也请不要在这些场合下使用。
让程序解决繁琐的事情
命令行交互
由于不少场景下咱们都是用在远程问题分析中(本地我就直接DEBUG了)。通常Java都会使用在Linux/BSD等类UNIX操做系统下。因此命令行是我最开始不二的选择,也是目前支持最成熟的交互方案。
图形界面交互
在2.x.x.x
版本中将会支持WEB方式访问,HTTP采用websocket与后台服务进行交互,预计过年以后能发布上线。
查看已被JVM所加载的类、方法信息
方法执行监控
调用量,成功失败率,响应时间
方法执行数据操做
入参、返回值、异常信息记录与查看;支持动做回放
性能开销渲染
跟踪指定路径中的方法调用轨迹、耗时
查看方法调用堆栈
monitor
、trace
等tt
命令能以时间维度纪录下监控期内的每一次调用环境多人并行协做
基于C/S架构的任务模式甚至能让多人同时远程到同一进程上执行不一样的指令、脚本,很是适合团队一块儿进行线上问题排查与跟踪。Greys采用纯Java编写并留有良好的扩展,若是你有需求,只要你会Java,就能够为你本身编写想要的功能。 Greys最有利的武器是他的表达式,能让你在感觉到HouseMD集成功能便利的同时,也能发挥出自定义Btrace脚本的灵活。
1. 应用管理员拥有JVM进程权限,由他来首先在目标JVM上启动Greys 2. 技术专家A和B平时没有对应机器的权限,但只要网络能访问,他们能够经过指定ip:port直接访问目标机器的JVM进程,仿佛在本地通常
Greys支持在线安装和本地安装两种安装方案,安装便可用,推荐使用在线安装。
在线安装(推荐)
请复制如下内容,并粘贴到命令行中。
curl -sLk http://ompc.oss.aliyuncs.com/greys/install.sh|sh
命令将会下载的启动脚本文件greys.sh
到当前目录,你能够放在任何地方或加入到$PATH
中
本地安装
在某些状况下,目标服务器没法访问远程阿里云主机,此时你须要自行下载greys的安装文件。
下载最新版本的GREYS
http://ompc.oss.aliyuncs.com/greys/release/greys-VERSION-bin.zip
最新的***VERSION***版本请参考主页信息
解压zip文件后,执行如下命令
cd greys sh ./install-local.sh
即完成本地安装。
下载失败
一般这样的缘由你须要检查你的网络是否畅通,核对是否能正确访问这个网址
http://ompc.oss.aliyuncs.com/greys/greys.sh
downloading... download failed!
没有权限
安装脚本首先会将greys文件从阿里云服务器上下载到当前执行脚本的目录,因此你必需要拥有当前目录的写权限。
permission denied, target directory is not writable.
参数说明
./greys.sh <PID>[@IP:PORT]
启动范例
若是不指定**IP**和**PORT**,默认是**127.0.0.1**和**3658**
./greys.sh 12345
等价于
./greys.sh 12345@127.0.0.1
等价于
./greys.sh 12356@127.0.0.1:3658
sudo支持
成熟的线上管理环境通常都不会直接开放JVM部署用户权限给你,而是经过sudo-list来控制和监控用户的越权操做。因为greys.sh
脚本中会对当前用户的环境变量产生感知,因此须要加上-H
参数
sudo -u admin -H ./greys.sh 12345
TELNET的支持
Greys支持经过telnet来访问服务端,若是当你手头的机器没有安装Greys的客户端,你能够简单的经过telnet命令来进行访问。
telnet 10.232.12.113 3658
固然了,telnet命令和Greys自带的Console在使用友好度上仍是有必定的差距,不过解决应急之需没有问题。
Greys是一个C/S架构的程序,因此当Client访问到Server时,Server会维护一个session(会话),以及session的心跳、超时机制。事务(Tx)机制则是创建在session的基础上,全部的命令交互都会建立一个事务,而且产生对应的队列进行输出缓冲。
事务伴随着命令的生命周期而存在,命令分两种:
当即返回
当即返回的命令定义是:敲下命令后Server端当即返回最终结果,后续无持续反馈信息,释放Client对输入的锁定,从新开放让用户输入信息,好比version
、sc
、sm
等。
等待停止
等待停止的命令则是须要用户主动输入Ctrl+D
完成的命令停止操做。命令执行后没法当即返回最终结果,而是不断的将中间产生的输出源源不断的输出到客户端中,这种命令好比stack
、monitor
等。
当session关闭时,全部挂在session的事务也会当即被关闭。
Greys相对于HouseMD、BTrace而言最灵活的地方就是在用表达式来灵活的支持不一样的问题排查、分析场景。
表达式分两种:条件表达式
与观察表达式
条件表达式
条件表达式用在**使用表达式表达TRUE或FALSE**的场景,从1.6.0.6
版本开始,trace
、stack
、tt
、watch
命令都增长了条件表达式支持。
条件表达式将会使用greys内置的表达式解析引擎,识别OGNL语法。
特别指出的是,若是你书写了一个错误的条件表达式,greys为了兼容错误会解析为FALSE。
如下是一些条件表达式使用的例子和预测结果
条件表达式 | 预测结果 | 解析结果说明 |
---|---|---|
1==1 | TRUE | 条件表达式为真 |
true | TRUE | 条件表达式为真 |
@@@ | FALSE | 非法的条件表达式 |
params==null | FALSE | 条件表达式为假 |
false | FALSE | 条件表达式为假 |
1!=1 | FALSE | 条件表达式为假 |
观察表达式
观察表达式用在**使用表达式表达输出内容**的场景,尤为在watch
和tt
命令中,观察表达式很是相当重要。
条件表达式将会使用greys内置的表达式解析引擎,识别OGNL语法,将表达式转换为待输出的对象。
如下是一些观察表达式使用的例子
字符串拼接
clazz.name+"."+method.name
数字运算
clazz.name.length()+method.name.length()
不管是匹配表达式也好、观察表达式也罢,他们核心判断变量都是围绕着一个greys中的通用通知对象Advice
进行。
它的简略代码结构以下
public class Advice { private final ClassLoader loader; private final Class<?> clazz; private final GaMethod method; private final Object target; private final Object[] params; private final Object returnObj; private final Throwable throwExp; private final boolean isBefore; private final boolean isThrow; private final boolean isReturn; // getter/setter }
这里列一个表格来讲明不一样变量的含义
变量名 | 变量解释 |
---|---|
loader | 本次调用类所在的ClassLoader |
clazz | 本次调用类的Class引用 |
method | 本次调用方法反射引用 |
target | 本次调用类的实例 |
params | 本次调用参数列表,这是一个数组,若是方法是无参方法则为空数组 |
returnObj | 本次调用返回的对象。当且仅当isReturn==true 成立时候有效,代表方法调用是以正常返回的方式结束。若是当前方法无返回值void ,则值为null |
throwExp | 本次调用抛出的异常。当且仅当isThrow==true 成立时有效,代表方法调用是以抛出异常的方式结束。 |
isBefore | 辅助判断标记,当前的通知节点有多是在方法一开始就通知,此时isBefore==true 成立,同时isThrow==false 和isReturn==false ,由于在方法刚开始时,还没法肯定方法调用将会如何结束。 |
isThrow | 辅助判断标记,当前的方法调用以抛异常的形式结束。 |
isReturn | 辅助判断标记,当前的方法调用以正常返回的形式结束。 |
全部变量均可以在表达式中直接使用,若是在表达式中编写了不符合OGNL脚本语法或者引入了不在表格中的变量,
对条件表达式
、检索表达式
而言,则一概当成false
来处理
对观察表达式
而言,则放弃当前方法调用的处理(不输出)
JDK的类默认由BootstrapClassLoader负责加载,因为Greys本身也适用了大量的JDK类,因此我不建议使用Greys直接对JDK相关类进行加强、代理。
默认而言,Greys会拒绝执行关于JDK类的操做命令。你需显式用options
命令打开。
ga?>options unsafe true +--------+--------------+-------------+ | NAME | BEFORE-VALUE | AFTER-VALUE | +--------+--------------+-------------+ | unsafe | false | true | +--------+--------------+-------------+ Affect(row-cnt:1) cost in 4 ms. ga?>
一些命令须要对类、方法进行模式匹配过滤,从1.5.4.6
及其以后的版本以后,Greys默认支持通配符
匹配,目前仅支持*
和?
两个通配符,正则表达式须要显式指定-E
参数激活。
模式匹配举例
原sc
命令的正则表达式匹配
sc com\.apache\.commons\.lang\.StringUtils
在1.5.4.6
及其以后的版本中将会默认使用通配符表达式
sc com.apache.commons.lang.StringUtils sc *lang.StringUtils
若想继续使用正则表达式匹配,须要显式-E
参数激活
sc -E com\.apache\.commons\.lang\.StringUtils sc -E com\..*StringUtils
支持模式匹配的命令
全部须要模式匹配的命令都支持参数-E
,他们分别是:sc
、sm
、stack
、monitor
、watch
、tt
、trace
命令 | 说明 |
---|---|
help | 查看命令的帮助文档,每一个命令和参数都有很详细的说明 |
sc | 查看JVM已加载的类信息 |
sm | 查看已加载的方法信息 |
monitor | 方法执行监控 |
trace | 渲染方法内部调用路径,并输出方法路径上的每一个节点上耗时 |
ptrace | 强化版的trace 命令。经过指定渲染路径,并可记录下路径中全部方法的入参、返值;与tt 命令联动。 |
watch | 方法执行数据观测 |
tt | 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不一样的时间下调用进行观测 |
stack | 输出当前方法被调用的调用路径 |
version | 输出当前目标Java进程所加载的Greys版本号 |
quit | 退出greys客户端 |
shutdown | 关闭greys服务端 |
reset | 重置加强类,将被greys加强过的类所有还原 |
session | 查看当前会话 |
jvm | 查看当前JVM的信息 |
help
命令会是你第一个在Greys中使用的命令,也会是从此使用最频繁的命令之一,当你在使用的过程当中有任何不熟悉的疑问,请直接help
吧~
查看命令清单
进入Greys的欢迎界面后,全部命令均可以经过help
获取帮助。此时你直接输入一个help,Greys则会返回全部命令的大概用途介绍。
ga?>help +----------+----------------------------------------------------------------------------------+ | sc | Search all the classes loaded by JVM | +----------+----------------------------------------------------------------------------------+ | sm | Search the method of classes loaded by JVM | +----------+----------------------------------------------------------------------------------+ | monitor | Monitor the execution of specified Class and its method | +----------+----------------------------------------------------------------------------------+ | watch | Display the details of specified class and method | +----------+----------------------------------------------------------------------------------+ | tt | Time Tunnel | +----------+----------------------------------------------------------------------------------+ | stack | Display the stack trace of specified class and method | +----------+----------------------------------------------------------------------------------+ | ptrace | Display the detailed thread path stack of specified class and method | +----------+----------------------------------------------------------------------------------+ | trace | Display the detailed thread stack of specified class and method | +----------+----------------------------------------------------------------------------------+ | session | Display current session information | +----------+----------------------------------------------------------------------------------+ | quit | Quit Greys console | +----------+----------------------------------------------------------------------------------+ | version | Display Greys version | +----------+----------------------------------------------------------------------------------+ | jvm | Display the target JVM information | +----------+----------------------------------------------------------------------------------+ | reset | Reset all the enhanced classes | +----------+----------------------------------------------------------------------------------+ | shutdown | Shut down Greys server and exit the console | +----------+----------------------------------------------------------------------------------+ | help | Display Greys Help | +----------+----------------------------------------------------------------------------------+ Affect(row-cnt:1) cost in 9 ms. ga?>
嗯,囋囋,我知道个人英文翻译很烂,就不用吐槽了。指望能有人能帮我从新打理英文的帮助界面和英文xwiki,小生感激涕零!
查看命令详细帮助
help命令同时也支持对其余命令的一个解释说明,好比咱们键入help watch
,greys将会返回watch
命令的全部参数解释、用法介绍等详细信息。
ga?>help watch
+---------+----------------------------------------------------------------------------------+
| USAGE | -[bfesx:En:] class-pattern method-pattern express condition-express | | | Display the details of specified class and method | +---------+----------------------------------------------------------------------------------+ | OPTIONS | [b] | Watch before invocation | | | -----------------+-------------------------------------------------------------- | | | [f] | Watch after invocation | | | -----------------+-------------------------------------------------------------- | | | [e] | Watch after throw exception | | | -----------------+-------------------------------------------------------------- | | | [s] | Watch after successful invocation | | | -----------------+-------------------------------------------------------------- | | | [x:] | Expand level of object (0 by default) | | | -----------------+-------------------------------------------------------------- | | | [E] | Enable regular expression to match (wildcard matching by def | | | | ault) | | | -----------------+-------------------------------------------------------------- | | | [n:] | Threshold of execution times | | | -----------------+-------------------------------------------------------------- | | | class-pattern | Path and classname of Pattern Matching | | | -----------------+-------------------------------------------------------------- | | | method-pattern | Method of Pattern Matching | | | -----------------+-------------------------------------------------------------- | | | express | express, write by OGNL. | | | | | | | | FOR EXAMPLE params[0] | | | | params[0]+params[1] | | | | returnObj | | | | throwExp | | | | target | | | | clazz | | | | method | | | | | | | | THE STRUCTURE | | | | target : the object | | | | clazz : the object's class | | | | method : the constructor or method | | | | params[0..n] : the parameters of method | | | | returnObj : the returned object of method | | | | throwExp : the throw exception of method | | | | isReturn : the method ended by return | | | | isThrow : the method ended by throwing exception | | | -----------------+-------------------------------------------------------------- | | | condition-expre | Conditional expression by OGNL | | | ss | | | | | FOR EXAMPLE | | | | TRUE : 1==1 | | | | TRUE : true | | | | FALSE : false | | | | TRUE : params.length>=0 | | | | FALSE : 1==2 | | | | | | | | THE STRUCTURE | | | | target : the object | | | | clazz : the object's class | | | | method : the constructor or method | | | | params[0..n] : the parameters of method | | | | returnObj : the returned object of method | | | | throwExp : the throw exception of method | | | | isReturn : the method ended by return | | | | isThrow : the method ended by throwing exception | +---------+----------------------------------------------------------------------------------+ | EXAMPLE | watch -Eb org\.apache\.commons\.lang\.StringUtils isBlank params[0] | | | watch -b org.apache.commons.lang.StringUtils isBlank params[0] | | | watch -f org.apache.commons.lang.StringUtils isBlank returnObj | | | watch -bf *StringUtils isBlank params[0] | | | watch *StringUtils isBlank params[0] | | | watch *StringUtils isBlank params[0] params[0].length==1 | +---------+----------------------------------------------------------------------------------+ Affect(row-cnt:1) cost in 15 ms. ga?>
帮助信息组成
帮助文档分红**Useage**、**Options**、**Example**三个区域,分别是**用途说明**、**参数列表**、**实际例子**
ga?>help session +---------+----------------------------------------------------------------------------------+ | USAGE | -[c:] | | | Display current session information | +---------+----------------------------------------------------------------------------------+ | OPTIONS | [c:] | Modify the character set of session | +---------+----------------------------------------------------------------------------------+ | EXAMPLE | session | | | session -c GBK | | | session -c UTF-8 | +---------+----------------------------------------------------------------------------------+ Affect(row-cnt:1) cost in 2 ms. ga?>
参数选项说明
[]
中的参数为选填项,好比[d]
,意思是该命令可接受一个名称为d的选填参数,且不用参数值。[:]
中的参数则为选填,但有值的参数,好比[c:]
class-pattern
/method-pattern
,这两个参数为隐性参数,即在输入的时候不须要特地声明参数。class-pattern
为类路径.类名称
的表达式匹配,method-pattern
则为方法名的表达式匹配。“Search-Class”的简写,这个命令能搜索出全部已经加载到JVM中的Class信息。
参数说明
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
[d] | 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的e2ClassLoader等详细信息。 若是一个类被多个ClassLoader所加载,则会出现屡次 |
[f] | 输出当前类的成员变量信息 |
[E] | 支持正则表达式匹配 |
使用参考
ga?>sc -df *alibaba.Address +------------------+-----------------------------------------------+ | class-info | com.alibaba.Address | +------------------+-----------------------------------------------+ | code-source | /Users/vlinux/temp/agent-test/target/ | +------------------+-----------------------------------------------+ | name | com.alibaba.Address | +------------------+-----------------------------------------------+ | isInterface | false | +------------------+-----------------------------------------------+ | isAnnotation | false | +------------------+-----------------------------------------------+ | isEnum | false | +------------------+-----------------------------------------------+ | isAnonymousClass | false | +------------------+-----------------------------------------------+ | isArray | false | +------------------+-----------------------------------------------+ | isLocalClass | false | +------------------+-----------------------------------------------+ | isMemberClass | false | +------------------+-----------------------------------------------+ | isPrimitive | false | +------------------+-----------------------------------------------+ | isSynthetic | false | +------------------+-----------------------------------------------+ | simple-name | Address | +------------------+-----------------------------------------------+ | modifier | public | +------------------+-----------------------------------------------+ | annotation | | +------------------+-----------------------------------------------+ | interfaces | | +------------------+-----------------------------------------------+ | super-class | com.alibaba.CountObject | | | `-java.lang.Object | +------------------+-----------------------------------------------+ | class-loader | sun.misc.Launcher$AppClassLoader@2a139a55 | | | `-sun.misc.Launcher$ExtClassLoader@5fb20bfd | +------------------+-----------------------------------------------+ | fields | modifier : private | | | type : java.lang.String | | | name : username | | | | | | modifier : private | | | type : int | | | name : addressId | | | | | | modifier : private | | | type : java.lang.String | | | name : addressName | | | | +------------------+-----------------------------------------------+ Affect(row-cnt:1) cost in 9 ms. ga?>
“Search-Method”的简写,这个命令能搜索出全部已经加载了Class信息的方法信息。
参数说明
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
[d] | 展现每一个方法的详细信息 |
[E] | 支持正则表达式匹配 |
使用参考
ga?>sm -d *alibaba.Address * +-----------------------------+-------------------------------------------------+ | DECLARED-CLASS | VISIBLE-METHOD | +-----------------------------+-------------------------------------------------+ | com.alibaba.Address | declaring-class : class com.alibaba.Address | | | method-name : getAddressId | | | modifier : public | | | annotation : | | | parameters : | | | return : int | | | exceptions : | +-----------------------------+-------------------------------------------------+ | com.alibaba.Address | declaring-class : class com.alibaba.Address | | | method-name : getAddressName | | | modifier : public | | | annotation : | | | parameters : | | | return : java.lang.String | | | exceptions : | +-----------------------------+-------------------------------------------------+ | com.alibaba.Address | declaring-class : class com.alibaba.CountObject | | `-com.alibaba.CountObject | method-name : finalize | | | modifier : protected | | | annotation : | | | parameters : | | | return : void | | | exceptions : java.lang.Throwable | +-----------------------------+-------------------------------------------------+ | com.alibaba.Address | declaring-class : class com.alibaba.CountObject | | `-com.alibaba.CountObject | method-name : count | | | modifier : public | | | annotation : | | | parameters : | | | return : int | | | exceptions : | +-----------------------------+-------------------------------------------------+ Affect(row-cnt:4) cost in 9 ms. ga?>
对匹配class-pattern
/method-pattern
的类.方法的调用进行监控。
monitor
命令是介绍到的第一个非实时返回命令,实时返回命令是输入以后当即返回,而非实时返回的命令,则是不断的等待目标Java进程返回信息,直到用户输入Ctrl+D
为止。服务端是以任务的形式在后台跑任务,植入的代码随着任务的停止而被不会被执行,因此任务关闭后,不会对原有性能产生太大影响,并且原则上,任何Greys的命令也不会引发任何原有业务逻辑的改变。
监控的维度说明
监控项 | 说明 |
---|---|
timestamp | 时间戳 |
class | Java类 |
method | 方法(构造方法、普通方法) |
total | 调用次数 |
success | 成功次数 |
fail | 失败次数 |
rt | 平均RT |
fail-rate | 失败率 |
参数说明
方法拥有一个命名参数[c:]
,意思是统计周期(cycle of output),拥有一个整形的参数值
参数名称 | 参数说明 |
---|---|
[c:] | 统计周期,默认值为120秒 |
使用参考
ga?>monitor -c 5 *alibaba*Test printAddress Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 22 ms. +-----------+-------+--------+-------+---------+------+-----------+------------+------------+------------+ | TIMESTAMP | CLASS | METHOD | TOTAL | SUCCESS | FAIL | FAIL-RATE | AVG-RT(ms) | MIN-RT(ms) | MAX-RT(ms) | +-----------+-------+--------+-------+---------+------+-----------+------------+------------+------------+ +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ | TIMESTAMP | CLASS | METHOD | TOTAL | SUCCESS | FAIL | FAIL-RATE | AVG-RT(ms) | MIN-RT(ms) | MAX-RT(ms) | +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ | 2015-12-06 16:34:56 | com.alibaba.AgentTest | printAddress | 5 | 3 | 2 | 40.00% | 0.20 | 0 | 1 | +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ | TIMESTAMP | CLASS | METHOD | TOTAL | SUCCESS | FAIL | FAIL-RATE | AVG-RT(ms) | MIN-RT(ms) | MAX-RT(ms) | +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ | 2015-12-06 16:35:01 | com.alibaba.AgentTest | printAddress | 5 | 3 | 2 | 40.00% | 0.00 | 0 | 0 | +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ ga?>
命令能主动搜索class-pattern
/method-pattern
所渲染的方法调用路径,渲染和统计整个调用链路上的全部性能开销和追踪调用链路。
参数说明
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
[n:] | 命令执行次数 |
[E] | 支持正则表达式匹配 |
注意事项
trace
能方便的帮助你定位和发现因RT高而致使的性能问题缺陷,但其每次只能跟踪一级方法的调用链路,目前暂时没有精力去解决往下几个层级的调用。若是真有需求能够Issues我。
使用参考
ga?>trace com.alibaba.manager.DefaultAddressManager toStringPass2
Press Ctrl+D to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 19 ms. `---+Tracing for : thread_name="agent-test-address-printer" thread_id=0xb;is_daemon=false;priority=5; `---+[0,0ms]com.alibaba.manager.DefaultAddressManager:toStringPass2() +---[0,0ms]com.alibaba.Address:getAddressId() +---[0,0ms]com.alibaba.Address:getAddressId() +---[0,0ms]java.lang.Integer:valueOf() +---[0,0ms]com.alibaba.Address:getAddressName() +---[0,0ms]com.alibaba.Address:count() +---[0,0ms]java.lang.Integer:valueOf() `---[0,0ms]java.lang.String:format()
是否是很眼熟,没错,在JProfiler等收费软件中你曾经见识相似的功能,这里你将能够经过命令就能打印出指定调用路径。
[10,1ms]
的含义,10
所表明的含义是:当前节点的总体耗时;1
的含义是:当前节点在当前步骤的耗时;二者之间用逗号分割,单位为毫秒。
命令解释
命令为trace
命令的强化版,经过指定渲染路径来完成对方法执行路径的渲染过程
命令能主动搜索tracing-path-pattern
所渲染的路径,渲染和统计整个调用链路上的全部性能开销和追踪调用链路。
参数说明
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
[t] | 记录下渲染路径上全部方法的入参与返回值,记录下的返回值能够与tt 命令联动 |
[n:] | 命令执行次数 |
[E] | 支持正则表达式匹配 |
[path:] | 渲染路径表达式匹配,该参数可屡次使用 |
[Epath:] | 正则表达式渲染路径表达式匹配,该参数可屡次使用 |
使用例子
ga?>ptrace -t *alibaba*Test printAddress --path=*alibaba* Press Ctrl+D to abort. Affect(class-cnt:10 , method-cnt:36) cost in 148 ms. `---+pTracing for : thread_name="agent-test-address-printer" thread_id=0xb;is_daemon=false;priority=5;process=1004; `---+[2,2ms]com.alibaba.AgentTest:printAddress(); index=1021; +---+[1,1ms]com.alibaba.manager.DefaultAddressManager:newAddress(); index=1014; | +---[1,1ms]com.alibaba.CountObject:<init>(); index=1012; | `---[1,0ms]com.alibaba.Address:<init>(); index=1013; +---+[2,1ms]com.alibaba.manager.DefaultAddressManager:toString(); index=1020; | +---+[2,1ms]com.alibaba.manager.DefaultAddressManager:toStringPass1(); index=1019; | | +---+[2,1ms]com.alibaba.manager.DefaultAddressManager:toStringPass2(); index=1017; | | | +---[1,0ms]com.alibaba.Address:getAddressId(); index=1015; | | | +---+[1,0ms]com.alibaba.manager.DefaultAddressManager:throwRuntimeException(); index=1016; | | | | `---[1,0ms]throw:java.lang.RuntimeException | | | `---[1,0ms]throw:java.lang.RuntimeException | | +---[2,0ms]com.alibaba.AddressException:<init>(); index=1018; | | `---[2,0ms]throw:com.alibaba.AddressException | `---[2,0ms]throw:com.alibaba.AddressException `---[2,0ms]throw:com.alibaba.AddressException +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | INDEX | PROCESS-ID | TIMESTAMP | COST(ms) | IS-RET | IS-EXP | OBJECT | CLASS | METHOD | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1012 | 1004 | 2015-12-06 16:46:49 | 0 | true | false | 0x943cff | CountObject | <init> | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1013 | 1004 | 2015-12-06 16:46:49 | 0 | true | false | 0x943cff | Address | <init> | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1014 | 1004 | 2015-12-06 16:46:49 | 1 | true | false | 0x6833b8a5 | DefaultAddressManager | newAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1015 | 1004 | 2015-12-06 16:46:49 | 0 | true | false | 0x943cff | Address | getAddressId | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1016 | 1004 | 2015-12-06 16:46:49 | 0 | false | true | 0x6833b8a5 | DefaultAddressManager | throwRuntimeException | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1017 | 1004 | 2015-12-06 16:46:49 | 0 | false | true | 0x6833b8a5 | DefaultAddressManager | toStringPass2 | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1018 | 1004 | 2015-12-06 16:46:49 | 0 | true | false | 0x67e7a923 | AddressException | <init> | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1019 | 1004 | 2015-12-06 16:46:49 | 1 | false | true | 0x6833b8a5 | DefaultAddressManager | toStringPass1 | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1020 | 1004 | 2015-12-06 16:46:49 | 1 | false | true | 0x6833b8a5 | DefaultAddressManager | toString | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1021 | 1004 | 2015-12-06 16:46:49 | 2 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+
能方便的让你观察到指定方法的调用状况。能观察到的范围为:返回值
、抛出异常
、入参
,经过编写OGNL表达式进行对应变量的查看。
参数说明
watch的参数比较多,主要是由于它能在4个不一样的场景观察对象
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
express | 观察表达式 |
[b] | 在**方法调用以前**观察 |
[e] | 在**方法异常以后**观察 |
[s] | 在**方法返回以后**观察 |
[f] | 在**方法结束以后**(正常返回和异常返回)观察 |
这里重点要说明的是观察表达式,观察表达式的构成主要由OGNL表达式组成,因此你能够这样写params[0]+"$"+target
,只要是一个合法的OGNL表达式,都能被正常支持。
观察的维度也比较多,主要体如今参数advice
的数据结构上。Advice
参数最主要是封装了通知节点的全部信息。参考表达式核心变量中关于该节点的描述。
使用参考
ga?>watch -b *Test printAddress '"params[0]="+params[0]' Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 32 ms. params[0]=3163 params[0]=3164 params[0]=3165 params[0]=3166
这里须要说明的一个参数x
,这个参数决定了输出的结果的层级遍历输出对象,当加上这个参数以后,greys会将输出的对象按照指定层级进行剥开。-x 1
代表展开第1层级。
ga?>watch -s com.alibaba.manager.DefaultAddressManager newAddress returnObj -x 1 Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 34 ms. @Address[ username=@String[dukun], addressId=@Integer[3244], addressName=@String[ADDRESS], ]
时间隧道命令是我在使用watch
命令进行问题排查的时候衍生出来的想法。watch
虽然很方便和灵活,但须要提早想清楚观察表达式的拼写,这对排查问题而言要求过高,由于不少时候咱们并不清楚问题出自于何方,只能靠蛛丝马迹进行猜想。
这个时候若是能记录下当时方法调用的全部入参和返回值、抛出的异常会对整个问题的思考与判断很是有帮助。
因而乎,TimeTunnel命令就诞生了。
记录方法的调用
基本用法
对于一个最基本的使用来讲,就是记录下当前方法的每次调用环境现场。
ga?>tt -t -n 3 *Test printAddress Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 33 ms. +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | INDEX | PROCESS-ID | TIMESTAMP | COST(ms) | IS-RET | IS-EXP | OBJECT | CLASS | METHOD | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1036 | 1009 | 2015-12-06 16:57:06 | 1 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1037 | 1010 | 2015-12-06 16:57:07 | 0 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1038 | 1011 | 2015-12-06 16:57:08 | 0 | true | false | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ ga?>
命令参数解析
-t
tt命令有不少个主参数,-t
就是其中之一。这个参数的代表但愿记录下类*Test
的print
方法的每次执行状况。
-n 3
当你执行一个调用量不高的方法时可能你还能有足够的时间用CTRL+D
中断tt命令记录的过程,但若是遇到调用量很是大的方法,瞬间就能将你的JVM内存撑爆。
此时你能够经过-n
参数指定你须要记录的次数,当达到记录次数时greys会主动中断tt命令的记录过程,避免人工操做没法中止的状况。
表格字段说明
表格字段 | 字段解释 |
---|---|
INDEX | 时间片断记录编号,每个编号表明着一次调用,后续tt还有不少命令都是基于此编号指定记录操做,很是重要。 |
PROCESS-ID | 过程编号,咱们认为同一个线程的一次同步调用为一个过程 |
TIMESTAMP | 方法执行的本机时间,记录了这个时间片断所发生的本机时间 |
COST(ms) | 方法执行的耗时 |
IS-RET | 方法是否以正常返回的形式结束 |
IS-EXP | 方法是否以抛异常的形式结束 |
OBJECT | 执行对象的hashCode() ,注意,曾经有人误认为是对象在JVM中的无力内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体 |
CLASS | 执行的类名 |
METHOD | 执行的方法名 |
条件表达式
不知道你们是否有在使用过程当中遇到如下困惑
- 彷佛很难区分出重载的方法
- 我只须要观察特定参数,可是tt却所有都给我记录了下来
从1.6.0.6
版本以后,应广大妇女群众的要求,增长了条件表达式,这样你能够经过简单的条件表达式解决上边的困惑。
条件表达式也是用OGNL来编写,核心的判断对象依然是Advice
对象。
除了
tt
命令以外,watch
、trace
、stack
命令也都支持条件表达式
解决方法重载
tt -t *Test print params[0].length==1
经过制定参数个数的形式解决不一样的方法签名,若是参数个数同样,你还能够这样写
tt -t *Test print 'params[1].class == Integer.class'
解决指定参数
tt -t *Test print params[0].mobile=="13989838402"
检索调用记录
当你用tt
记录了一大片的时间片断以后,你但愿能从中筛选出本身须要的时间片断,这个时候你就须要对现有记录进行检索。
假设咱们有这些记录
ga?>tt -l +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | INDEX | PROCESS-ID | TIMESTAMP | COST(ms) | IS-RET | IS-EXP | OBJECT | CLASS | METHOD | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1047 | 1020 | 2015-12-06 17:03:00 | 1 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1048 | 1021 | 2015-12-06 17:03:01 | 0 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1049 | 1022 | 2015-12-06 17:03:01 | 1 | true | false | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1050 | 1023 | 2015-12-06 17:03:01 | 0 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1051 | 1024 | 2015-12-06 17:03:02 | 1 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1052 | 1025 | 2015-12-06 17:03:02 | 1 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1053 | 1026 | 2015-12-06 17:03:02 | 0 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1054 | 1027 | 2015-12-06 17:03:03 | 0 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1055 | 1028 | 2015-12-06 17:03:03 | 0 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1056 | 1029 | 2015-12-06 17:03:03 | 0 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ Affect(row-cnt:10) cost in 3 ms. ga?>
我须要筛选出printAddress
方法的调用信息
ga?>tt -s method.name=="printAddress" +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | INDEX | PROCESS-ID | TIMESTAMP | COST(ms) | IS-RET | IS-EXP | OBJECT | CLASS | METHOD | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1049 | 1022 | 2015-12-06 17:03:01 | 1 | true | false | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1052 | 1025 | 2015-12-06 17:03:02 | 1 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1055 | 1028 | 2015-12-06 17:03:03 | 0 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ Affect(row-cnt:3) cost in 8 ms. ga?>
你须要一个-s
参数。一样的,搜索表达式的核心对象依旧是Advice
对象。
查看调用信息
对于具体一个时间片的信息而言,你能够经过-i
参数后边跟着对应的INDEX
编号查看到他的详细信息。
ga?>tt -i 1055 +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | INDEX | 1055 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | PROCESS-ID | 1028 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | GMT-CREATE | 2015-12-06 17:03:03 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | COST(ms) | 0 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | OBJECT | 0x2062a3d | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | CLASS | com.alibaba.AgentTest | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | METHOD | printAddress | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | IS-RETURN | false | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | IS-EXCEPTION | true | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | PARAMETERS[0] | 3789 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | THROW-EXCEPTION | 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.null(Unknown Source) | | | 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 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | STACK | thread_name="agent-test-address-printer" thread_id=0xb;is_daemon=false;priority=5; | | | @com.alibaba.AgentTest.access$300() | | | at com.alibaba.AgentTest$3.null(null:-1) | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ Affect(row-cnt:1) cost in 7 ms. ga?>
重作一次调用
当你少少作了一些调整以后,你可能须要前端系统从新触发一次你的调用,此时得求爷爷告奶奶的须要前端配合联调的同窗再次发起一次调用。而有些场景下,这个调用不是这么好触发的。
tt
命令因为保存了当时调用的全部现场信息,因此咱们能够本身主动对一个INDEX
编号的时间片自主发起一次调用,从而解放你的沟通成本。此时你须要-p
参数。
ga?>tt -i 1055 -p +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | INDEX | 1055 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | PROCESS-ID | 1028 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | GMT-CREATE | 2015-12-06 17:03:03 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | COST(ms) | 1 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | OBJECT | 0x2062a3d | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | CLASS | com.alibaba.AgentTest | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | METHOD | printAddress | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | IS-RETURN | true | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | IS-EXCEPTION | false | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | PARAMETERS[0] | 3789 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | RETURN-OBJ | 1 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | STACK | thread_name="ga-command-execute-daemon" thread_id=0x22;is_daemon=true;priority=9; | | | @com.github.ompc.greys.core.command.TimeTunnelCommand$6.action() | | | at com.github.ompc.greys.core.server.DefaultCommandHandler.execute(DefaultCommandHandler.java:210) | | | at com.github.ompc.greys.core.server.DefaultCommandHandler.executeCommand(DefaultCommandHandler.java:87) | | | at com.github.ompc.greys.core.server.GaServer$4.run(GaServer.java:332) | | | at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) | | | at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) | | | at java.lang.Thread.run(Thread.java:745) | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ Time fragment[1055] successfully replayed. Affect(row-cnt:1) cost in 2 ms. ga?>
你会发现结果虽然同样,但调用的路径发生了变化,有原来的程序发起变成了greys本身的内部线程发起的调用了。
得益于Greys的ClassLoader隔离策略,Greys在内部本身发起线程请求调用的时候,依然采用的是目标类所归属的ClassLoader,因此在OSGI、Tomcat容器等场景下,Greys依然能正确的重作这次调用。
须要强调的点
ThreadLocal信息丢失
不少框架偷偷的将一些环境变量信息塞到了发起调用线程的ThreadLocal中,因为调用线程发生了变化,这些ThreadLocal线程信息没法经过greys保存,因此这些信息将会丢失。
一些常见的CASE好比:阿里鹰眼系统的TraceId、阿里全链路平台的压测流量标记位。
引用的对象
须要强调的是,tt
命令是将当前环境的对象引用保存起来,但仅仅也只能保存一个引用而已。若是方法内部对入参进行了变动,或者返回的对象通过了后续的处理,那么在tt
查看的时候将没法看到当时最准确的值。这也是为何watch
命令存在的意义。
不少时候咱们都知道一个方法被执行,但这个方法被执行的路径很是多。或者你根本就不知道这个方法是从那里被执行了,正在郁闷,正在彷徨。此时你须要的是stack命令。
参数说明
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
[n:] | 命令执行次数 |
[E] | 支持正则表达式匹配 |
使用例子
ga?>stack com.alibaba.manager.DefaultAddressManager newAddress Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 36 ms. thread_name="agent-test-address-printer" thread_id=0xb;is_daemon=false;priority=5; @java.lang.reflect.Method.invoke() at com.alibaba.manager.DefaultAddressManager.newAddress(DefaultAddressManager.java:-1) at com.alibaba.AgentTest.printAddress(AgentTest.java:73) at com.alibaba.AgentTest.access$300(AgentTest.java:7) at com.alibaba.AgentTest$3.null(null:-1)
这估计是最好理解的一个命令了,输出当前Greys的版本号,这里输出的版本号不是Client的版本,而是当前加载到目标Java进程中的Greys版本。
这里说明下与shutdown
命令的区别,quit
命令仅仅是将客户端关闭,而不会将目标Java进程中的与Greys的Server关闭。因此若是仅仅是但愿简单的推出Greys控制台,则使用quit
命令足矣。
一旦Greys控制台退出,控制台所绑定的Session将会被关闭,Session上全部存活的事务也都会被停止并释放。
命令执行后将会完成两件事:
关闭Greys在目标Java所加载的Socket服务,所占用的端口将会被释放,同时,本地的Greys控制台也由于远程Socket关闭而主动退出。
重置全部被Greys所加强的类。同reset
命令。
重置指定被Greys所加强的类。
会话命令是在1.6版本以后新增,整个命令的定位是维护好会话级的参数。目前可修改的就一个字符集。
参数说明
参数名称 | 参数说明 |
---|---|
[c:] | 指定会话字符集 |
使用例子
直接查看会话信息
ga?>session +------------+------------------+ | JAVA_PID | 8609 | +------------+------------------+ | SESSION_ID | 2 | +------------+------------------+ | DURATION | 300000 | +------------+------------------+ | CHARSET | UTF-8 | +------------+------------------+ | PROMPT | ga?> | +------------+------------------+ | FROM | /127.0.0.1:58186 | +------------+------------------+ | TO | /127.0.0.1:3658 | +------------+------------------+ Affect(row-cnt:1) cost in 0 ms. ga?>
修改字符集
ga?>session -c GBK change charset before[UTF-8] -> new[GBK] Affect(row-cnt:1) cost in 26 ms.
查看当前JVM的信息,无参数。
ga?>jvm
+--------------------+-----------------------------------------------------------------------------------------------------+
| CATEGORY | INFO | +--------------------+-----------------------------------------------------------------------------------------------------+ | RUNTIME | MACHINE-NAME : 25428@vlinux-air.local | | | JVM-START-TIME : 2015-06-16 22:12:20 | | | MANAGEMENT-SPEC-VERSION : 1.2 | | | SPEC-NAME : Java Virtual Machine Specification | | | SPEC-VENDOR : Oracle Corporation | | | SPEC-VERSION : 1.8 | | | VM-NAME : Java HotSpot(TM) 64-Bit Server VM | | | VM-VENDOR : Oracle Corporation | | | VM-VERSION : 25.25-b02 | | | INPUT-ARGUMENTS : [] | | | CLASS-PATH : . | | | BOOT-CLASS-PATH : /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/li | | | b/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Conte | | | nts/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_25.j | | | dk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMachin | | | es/jdk1.8.0_25.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVir | | | tualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/jce.jar:/Library/Jav | | | a/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/charsets.ja | | | r:/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/l | | | ib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/H | | | ome/jre/classes | | | LIBRARY-PATH : /Users/vlinux/Library/Java/Extensions:/Library/Java/Extensions:/Networ | | | k/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java | | | :. | +--------------------+-----------------------------------------------------------------------------------------------------+ | CLASS-LOADING | LOADED-CLASS-COUNT : 3045 | | | TOTAL-LOADED-CLASS-COUNT : 3045 | | | UNLOADED-CLASS-COUNT : 0 | | | IS-VERBOSE : false | +--------------------+-----------------------------------------------------------------------------------------------------+ | COMPILATION | NAME : HotSpot 64-Bit Tiered Compilers | | | TOTAL-COMPILE-TIME : 8903(ms) | +--------------------+-----------------------------------------------------------------------------------------------------+ | GARBAGE-COLLECTORS | PS Scavenge : 4/40(ms) | | | [count/time] | | | PS MarkSweep : 0/0(ms) | | | [count/time] | +--------------------+-----------------------------------------------------------------------------------------------------+ | MEMORY-MANAGERS | CodeCacheManager : Code Cache | | | Metaspace Manager : Metaspace | | | Compressed Class Space | | | PS Scavenge : PS Eden Space | | | PS Survivor Space | | | PS MarkSweep : PS Eden Space | | | PS Survivor Space | | | PS Old Gen | +--------------------+-----------------------------------------------------------------------------------------------------+ | MEMORY | HEAP-MEMORY-USAGE : 163053568/134217728/1908932608/54796080 | | | [committed/init/max/used] | | | NO-HEAP-MEMORY-USAGE : 32571392/2555904/-1/31757608 | | | [committed/init/max/used] | | | PENDING-FINALIZE-COUNT : 0 | +--------------------+-----------------------------------------------------------------------------------------------------+ | OPERATING-SYSTEM | OS : Mac OS X | | | ARCH : x86_64 | | | PROCESSORS-COUNT : 4 | | | LOAD-AVERAGE : 2.50634765625 | | | VERSION : 10.10.3 | +--------------------+-----------------------------------------------------------------------------------------------------+ | THREAD | COUNT : 8 | | | DAEMON-COUNT : 7 | | | LIVE-COUNT : 9 | | | STARTED-COUNT : 19 | +--------------------+-----------------------------------------------------------------------------------------------------+ Affect cost in 22 ms.
安装失败
$:>curl -sLk http://ompc.oss.aliyuncs.com/greys/install.sh|ksh downloading... greys.zip.43678 download file failed!
问:
为何我在安装Greys的时候失败了?
答:
Greys的安装脚本首先先从阿里云上下载greys.zip
,而后进行解压、安装。因此必需要求执行安装脚本的用户必须有对当前目录的写权限,通常出现这个问题,能够检查网络
、磁盘空间
以及当前目录是否有写权限
。
问:
若是我在/tmp/
目录下执行安装脚本,请问Greys会怎么安装?
答:
安装在/tmp/greys/
目录下,该目录下一共有三个重要文件
/tmp/greys/greys.sh
其中greys-core.jar
为greys的程序主体,启动类、加载类都在这个jar包当中;greys-agent.jar
则为目标JVM的加载引导程序;greys.sh
为一个可执行脚本,为Greys的启动脚本。
问:
安装的Greys如何进行删除?
答:
Greys是一个绿色环保软件,不会修改你的注册表,但会在$HOME
目录下建立隐藏目录$HOME/.greys
,用于保存不一样的版本和Jline的历史命令,你能够直接删除。
Operation not permitted
错误问:
我在启动Greys的时候报这样的错误
$:> ./greys.sh 11064 start greys failed, because : Operation not permitted
答:
Greys要求执行执行启动命令的用户必须拥有和目标进程ID一样的权限(在这个Case中,目标进程ID为11064
),不然JVM将没法挂载Greys对应的jar包
No such process
错误问:
我在启动Greys的时候报这样的错误
$:> ./greys.sh 11063 start greys failed, because : No such process
答:
目标进程ID不存在。报这样的错误,请核对你的目标进程是否存在。
Unable to open socket file: target process not responding or HotSpot VM not loaded
- **问:**
我在启动Greys的时候报这样的错误
```shell $:> sudo -u admin ./greys.sh 12592 密码: start greys failed, because : Unable to open socket file: target process not responding or HotSpot VM not loaded ```
答:
目标进程ID不是JVM进程,或目标JVM进程不支持加载操做,好比低于JDK6的版本等。
通常遇到这种问题必定要很是当心谨慎的执行,若是对方进程编程不严谨,极可能会让对方进程CORE掉。上次我就弄死了个nginx的worker -_-!!
问:
启动以后就什么反应也没有,也没有出现预期的ga?>
提示符
答:
颇有可能你的3658端口已经被别的进程占据,请核对你当前机器所开的端口
netstat -anp|grep LIST
解决方案也很简单,换个端口
./greys.sh 4567@127.0.0.1:6666
sudo -u
方式启动报权限不足
请给你的sudo命令加上-H
参数
sudo -u admin -H ./greys.sh 4567
哪些命令会致使性能问题
Greys的大部分命令性能开销都很是低廉,固然前提是一次性操做的类不要太多。
是否能加强由BootstrapClassLoader所加载的类
固然是能够的,但默认我*封印*了这个能力。主要是Greys本身也使用了大量BootstrapClassLoader所加载的类,若是处理很差极其容易形成故障。
你能够经过隐藏命令options
激活这个功能
ga?>options unsafe true +--------+--------------+-------------+ | NAME | BEFORE-VALUE | AFTER-VALUE | +--------+--------------+-------------+ | unsafe | false | true | +--------+--------------+-------------+ Affect(row-cnt:1) cost in 2 ms.
接下来你能够尝试加强系统类了
ga?>monitor -c 5 java.lang.String substring
Press Ctrl+D or Ctrl+X to abort. Affect(class-cnt:1 , method-cnt:2) cost in 35 ms. +---------------------+------------------+-----------+-------+---------+------+------+-----------+ | timestamp | class | method | total | success | fail | rt | fail-rate | +---------------------+------------------+-----------+-------+---------+------+------+-----------+ | 2015-06-16 23:44:54 | java.lang.String | substring | 30 | 30 | 0 | 0.23 | 0.00% | +---------------------+------------------+-----------+-------+---------+------+------+-----------+
但我话就放在这里,随意加强系统类。后果自负!
Greys支持将信息输出到文件中么?
支持,不过要作一些小命令。
./greys.sh 4567|tee -a ./greys.log
Greys能使用在Sun JDK5的版本么?
很遗憾抱歉的说,不行。
Greys的原理和Btrace同样,依赖了JDK6+提供的Instumentation
特性,因此必需要求目标的JDK环境是**JDK6及其以上的版本**。
理论上Greys应该能在各类实现了SUN标准的各类JVM实现中运行,好比JRockit
、Zing
等,但我本身没有机会尝试,如有朋友能提供环境能进行测试并反馈测,我将不胜感激。
JRE中因为缺乏tools.jar,因此没法直接运行Greys,须要稍做一些修改。一样的也没有需求要在JRE中运行Greys,我这里也偷个懒,留给其余有须要的人来实现吧。
程序中是否有彩蛋?
有,我当初作这个软件的惟一目的就是但愿能快速定位问题,而后好陪她逛街。为了避免忘记这个初心,我将这位她的英文名做为命令整合到了Greys中,能够尝试找找!
1.7.3.0
多版本管理
从1.7.x.x
版本开始,greys.sh
脚本支持自动更新,在网络容许的状况下会自动监测远程服务器上是否存在可升级的最新版本。
若网络不可达(网络隔离的环境)则须要进行本地安装。本地安装也同样会归入到多版本管理识别范围。
大版本兼容性问题
大版本之间不作任何兼容性保障,好比1.7.x.x
版本的客户端不保证能访问1.6.x.x
启动的服务端。
主版本
.大版本
.小版本
.漏洞修复
主版本
这个版本更新说明程序架构体系进行了重大升级,好比以前的0.1版升级到1.0版本,整个软件的架构从单机版升级到了SOCKET多机版。并将Greys的性质进行的肯定:Java版的HouseMD,但要比前辈们更强。
大版本
程序的架构设计进行重大改造,但不影响用户对这款软件的定位。
小版本
增长新的命令和功能
漏洞修复
对现有版本进行漏洞修复和加强
主版本
、大版本
之间不作任何向下兼容的承诺。即0.1
版本的Client不保证必定能正常访问1.0
版本的Server。小版本
不兼容的功能会在版本升级中指出漏洞修复
保证全部功能向下兼容我编写和维护这款软件已经5年了,5年中Greys也从0.1
版本一直重构到如今的1.7
。在这个过程当中我获得了许多人的帮助与建议,并在年末我计划发布2.0
版本,将开放Greys的底层通信协议,支持websocket访问。
多年的问题排查经验我没有过多的分享,一个Java程序员个中的苦闷也无从分享,一切我都融入到了这款软件的命令中,但愿这些沉淀能帮助到可能须要到的你少走一些弯路,同时我也很是期待大家对她的反馈,这样我将感到很是开心和有成就