 Greys的诞生  | 很早的时候,我们使用BTrace排查问题,在感叹BTrace的强大之余,也曾好几次将线上系统折腾挂掉。2012年淘宝的聚石写了HouseMD,将常用的几个Btrace脚本整合在一起形成一个独立风格的应用,但其核心代码用的是Scala,我们没这方面的编程维护经验,所以只好艳羡HouseMD的才思敏捷而无法在其上增加功能。 于是乎,Greys诞生了! | PS:目前Greys仅支持Linux/Unix/Mac上的Java6+,Windows暂时无法支持 Greys是一个JVM进程执行过程中的异常诊断工具。 在不中断程序执行的情况下轻松完成JVM相关问题排查工作。 目标群体 有时候突然一个问题反馈上来,需要入参才能完成定位,但恰恰没有任何日志。回去加上重新部署,一杯咖啡时间过去了,是不是很崩溃? 当你经过反复这样几次折腾之后变得聪明了,在自己的代码的所有入参和出参地方都加上debug日志,但这次问题似乎暴露在别人的代码中了...是不是很无奈? 突然遇到线上一个性能问题无法确定到底是哪个环节的耗时,只能反复抓jstack猜,还有没有办法可以好好的过日子啦? 遇到以上问题时,你就是我们这类工具的目标客户,此类工具能利用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所加载的类、方法信息 方法执行监控 方法执行数据操作 性能开销渲染 查看方法调用堆栈 软件特点 纯Java实现的开源项目 安装使用便捷,仅一个jar包 可无需重启JVM进行CT式诊断 观察变量的出入参 OGNL表达式展开变量、过滤条件,方便你查看入参、出参、异常、当前对象的各种属性细节 常用分析命令集成,monitor、trace等 时间隧道,tt命令能以时间维度纪录下监控期内的每一次调用环境 多人并行协作  - 1. 应用管理员拥有JVM进程权限,由他来首先在目标JVM上启动Greys
- 2. 技术专家A和B平时没有对应机器的权限,但只要网络能访问,他们可以通过指定ip:port直接访问目标机器的JVM进程,仿佛在本地一般
复制代码 Greys入门 软件安装 Greys支持在线安装和本地安装两种安装方案,安装即可用,推荐使用在线安装。 在线安装(推荐) 请复制以下内容,并粘贴到命令行中。 - curl -sLk http://ompc.oss.aliyuncs.com/greys/install.sh|sh
复制代码命令将会下载的启动脚本文件greys.sh到当前目录,你可以放在任何地方或加入到$PATH中 本地安装 在某些情况下,目标服务器无法访问远程阿里云主机,此时你需要自行下载greys的安装文件。 下载最新版本的GREYS 最新的***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 参数说明 - ./greys.sh <PID>[@IP:PORT]
复制代码 PID:目标Java进程ID(请确保执行当前执行命令的用户必须有足够的权限操作对应的Java进程) IP:目标服务器IP地址,当远程服务开启之后,其他人可以通过指定IP的形式加载到对应目标机器的Java进程中,从而实现远程协助。专门用于解决目标主机账号没有权限,但对方兄弟却非常需要你支援的时候。Greys允许多个用户同时访问,并且各自的命令不会相互干扰执行。 PORT:目标服务器端口号,设计端口号的初心则是希望解决同台机器上存在多个Java进程需要被Greys分析的情况,默认的端口号是3658,如果不做区分则会引起端口冲突。 启动范例 如果不指定**IP**和**PORT**,默认是**127.0.0.1**和**3658** 等价于 - ./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语法。 以下是一些条件表达式使用的例子和预测结果 条件表达式 | 预测结果 | 解析结果说明 | 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类支持 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 Greys命令详解 命令清单 命令 | 说明 | 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命令 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?>
复制代码 查看命令详细帮助 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则为方法名的表达式匹配。 sc命令 “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?>
复制代码 sm命令 “Search-Method”的简写,这个命令能搜索出所有已经加载了Class信息的方法信息。 参数说明 参数名称 | 参数说明 | class-pattern | 类名表达式匹配 | method-pattern | 方法名表达式匹配 | [d] | |
Copyright 2015-2025 djqfx
|