Hello World

吞风吻雨葬落日 欺山赶海踏雪径

0%

arthas 使用教程

Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。

Arthas 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

Github: https://github.com/alibaba/arthas

文档: https://arthas.aliyun.com/doc/index.html

Arthas Install

快速安装

使用arthas-boot
下载arthas-boot.jar,然后用java -jar的方式启动:

1
2
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

打印帮助信息:

1
java -jar arthas-boot.jar -h
  • 如果下载速度比较慢,可以使用aliyun的镜像:

    1
    java -jar arthas-boot.jar --repo-mirror aliyun --use-http
  • 如果从github下载有问题,可以使用gitee镜像

    1
    wget https://arthas.gitee.io/arthas-boot.jar

使用as.sh

Arthas 支持在 Linux/Unix/Mac 等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲 回车 执行即可:

1
curl -L https://alibaba.github.io/arthas/install.sh | sh

上述命令会下载启动脚本文件 as.sh 到当前目录,你可以放在任何地方或将其加入到 $PATH 中。

直接在shell下面执行./as.sh,就会进入交互界面。

也可以执行./as.sh -h来获取更多参数信息。

如果从github下载有问题,可以使用gitee镜像

1
curl -L https://arthas.gitee.io/install.sh | sh

全量安装

最新版本,点击下载:arthas

解压后,在文件夹里有arthas-boot.jar,直接用java -jar的方式启动:

1
java -jar arthas-boot.jar

卸载

  • 在 Linux/Unix/Mac 平台

删除下面文件:

1
2
rm -rf ~/.arthas/
rm -rf ~/logs/arthas
  • Windows平台直接删除user home下面的.arthas和logs/arthas目录

常用命令

所有命令参考文档: https://alibaba.github.io/arthas/commands.html

watch

https://arthas.aliyun.com/doc/watch.html

方法执行数据观测
让你能方便的观察到指定方法的调用情况。能观察到的范围为:返回值、抛出异常、入参,通过编写 OGNL 表达式进行对应变量的查看。

参数
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
express 观察表达式
condition-express 条件表达式
[b] 在方法调用之前观察
[e] 在方法异常之后观察
[s] 在方法返回之后观察
[f] 在方法结束之后(正常返回和异常返回)观察
[E] 开启正则表达式匹配,默认为通配符匹配
[x:] 指定输出结果的属性遍历深度,默认为 1

watch实践

查看内部方法有没有被调用,以及是否抛出异常

1
2
3
4
5
6
7
8
9
10
11
watch com.xxx.bbc.opsu.buyerlibrary.service.impl.BuyerTagMemberOriginalInfoServiceImpl updateTair "{throwExp,params,returnObj}" -x 2 -n 2

Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 552 ms, listenerId: 1
ts=2020-08-24 11:00:52; [cost=3.365922ms] result=@ArrayList[
null,
@Object[][
@Long[2256780593],
],
null,
]

-x 2 查看参数的深度
-n 2 只执行两次,调用量大的情况来不及停止。

tt

方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测。

记录

1
tt -t -n3 com.a.b queryIsxxx

查看记录列表

1
tt -l

结果分别是

1
2
3
4
5
6
7
8
INDEX - 索引
TIMESTAMP - 执行的时间戳
COST(ms) - 方法耗时
IS-RET - 是否正常返回
IS-EXP - 是否出现异常
OBJECT - 执行对象的hashCode, 注意这里是hashCode 不是JVM的内存地址
CLASS - 执行的类名
METHOD - 执行的方法名

查看详情

1
tt -i [INDEX]

利用ognl表达式查看

1
tt -i 1002 -w '{params[0],returnObj.success}'

重放请求

1
tt -i 1002 -p

jad/mc/redefine

jad

反编译指定已加载类的源码

默认情况下,反编译结果里会带有ClassLoader信息,通过--source-only选项,可以只打印源代码。方便和mc/redefine命令结合使用。

参数:

  • class-pattern
    类名表达式匹配
  • [c:]
    类所属 ClassLoader 的 hashcode
  • [E]
    开启正则表达式匹配,默认为通配符匹配

exp:

1
jad  --source-only com.xxx.china.buyer.odps.OdpsTaskServiceImpl > /tmp/OdpsTaskServiceImpl.java
1
jad org.apache.log4j.Logger -c 69dcaba4

mc

Memory Compiler/内存编译器,编译.java文件生成.class。

  • [c:]
    指定classloader

  • [d:]
    指定输出目录

注意,mc命令有可能失败。如果编译失败可以在本地编译好.class文件,再上传到服务器。

exp

1
mc -c 5b275dab -d /tmp/ /tmp/OdpsTaskServiceImpl.java

redefine

加载外部的.class文件,redefine jvm已加载的类。注意, redefine后的原来的类不能恢复,redefine有可能失败(比如增加了新的field),参考jdk本身的文档:Instrumentation#redefineClasses

参数

  • [c:]
    ClassLoader的hashcode
  • [p:]
    外部的.class文件的完整路径,支持多个

exp:

1
2
redefine  -c 5b275dab /tmp/com/xxx/china/buyer/odps/OdpsTaskServiceImpl.class
redefine success, size: 1

上传 .class 文件到服务器的技巧:
使用mc命令来编译jad的反编译的代码有可能失败。可以在本地修改代码,编译好后再上传到服务器上。有的服务器不允许直接上传文件,可以使用base64命令来绕过。

  • 在本地先转换.class文件为base64,再保存为result.txt

    1
    base64 < Test.class > result.txt
  • 到服务器上,新建并编辑result.txt,复制本地的内容,粘贴再保存

  • 把服务器上的 result.txt还原为.class

    1
    base64 -d < result.txt > Test.class
  • 用md5命令计算哈希值,校验是否一致

redefine的限制

  • 不允许新增加field/method
  • 正在跑的函数,没有退出不能生效

表达式核心变量

无论是匹配表达式也好、观察表达式也罢,他们核心判断变量都是围绕着一个 Arthas 中的通用通知对象 Advice 进行。它的简略代码结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Advice {
//本次调用类所在的 ClassLoader
private final ClassLoader loader;
//本次调用类的 Class 引用
private final Class<?> clazz;
//本次调用方法反射引用
private final ArthasMethod method;
//本次调用类的实例
private final Object target;
//本次调用参数列表,这是一个数组,如果方法是无参方法则为空数组
private final Object[] params;
//本次调用返回的对象。当且仅当 isReturn==true 成立时候有效,表明方法调用是以正常返回的方式结束。如果当前方法无返回值 void,则值为 null
private final Object returnObj;
//本次调用抛出的异常。当且仅当 isThrow==true 成立时有效,表明方法调用是以抛出异常的方式结束。
private final Throwable throwExp;
//辅助判断标记,当前的通知节点有可能是在方法一开始就通知,此时 isBefore==true 成立,同时 isThrow==false 和 isReturn==false,因为在方法刚开始时,还无法确定方法调用将会如何结束。
private final boolean isBefore;
//辅助判断标记,当前的方法调用以抛异常的形式结束。
private final boolean isThrow;
//辅助判断标记,当前的方法调用以正常返回的形式结束。
private final boolean isReturn;

// getter/setter
}

所有变量都可以在表达式中直接使用,如果在表达式中编写了不符合 OGNL 脚本语法或者引入了不在表格中的变量,则退出命令的执行;用户可以根据当前的异常信息修正条件表达式或观察表达式