ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Arthas tt 命令完全指南:用方法执行时空隧道记录、检索与重放每一次调用

Arthas tt 命令完全指南:用方法执行时空隧道记录、检索与重放每一次调用 Arthas tt 命令完全指南用方法执行时空隧道记录、检索与重放每一次调用【免费下载链接】arthasAlibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas项目地址: https://gitcode.com/gh_mirrors/ar/arthas本指南以 Arthas 的ttTime Tunnel命令为主线系统讲解如何把指定方法每一次调用的入参、返回值、异常等现场信息快照下来并通过-l检索、-i查看、-p重放、-w观察等子命令对历史调用进行二次分析。读完本文你将掌握在无法复现问题时先记录、后复盘、再重放的完整排查打法同时理解tt底层基于MapInteger, TimeFragment的实现原理与内存管理注意事项。tt 命令的诞生背景为什么 watch 不够用watch虽然方便灵活但要求使用者在观察前就想清楚观察表达式的拼写这对排查问题而言要求太高——很多时候我们并不清楚问题出自何方只能靠蛛丝马迹进行猜测。如果方法没有被提前增强观测一旦调用发生现场信息就永远丢失了。tt命令的出现就是为了解决这个问题在方法被调用时无条件地把入参、返回值、抛出的异常等现场信息完整记录下来形成一个个时间片段TimeFragment之后无论什么时候都可以回头对这些历史调用进行检索、查看、重放与表达式观测。它相当于为线上方法调用开了一条时空隧道。注意事项先记住这两条铁律使用tt前必须先了解它的实现与内存代价源码见 TimeTunnelCommand.java实现原理tt命令把函数的入参/返回值等保存到一个MapInteger, TimeFragment里源码中为静态的LinkedHashMap默认的记录上限大小是100条对应源码private int numberOfLimit 100;可通过-n调整。内存释放tt相关功能使用完之后需要手动释放内存否则长时间使用可能导致 OOM。注意退出 arthas 并不会自动清除 tt 的缓存 map因此长期运行的目标进程里tt 缓存会一直驻留必须在排查完成后主动清理。准备演示环境启动 math-gamett的官方示例统一以math-game演示程序为观察对象。按快速入门的步骤启动即可curl -O https://arthas.aliyun.com/math-game.jar java -jar math-game.jar该程序每隔一秒生成一个随机数并做质因数分解。其源码见 MathGame.javaprimeFactors(int number)在入参小于 2 时抛出IllegalArgumentException否则返回质因数列表天然具备有正常返回、有异常抛出两种现场非常适合演示tt。启动后通过java -jar arthas-boot.jar选择math-game进程进入 Arthas 交互终端。记录调用tt -t最基本的使用方式就是记录当前方法每次调用的现场环境$ tt -t demo.MathGame primeFactors Press CtrlC to abort. Affect(class-cnt:1 , method-cnt:1) cost in 66 ms. INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ------------------------------------------------------------------------------------------------------------------------------------- 1000 2018-12-04 11:15:38 1.096236 false true 0x4b67cf4d MathGame primeFactors 1001 2018-12-04 11:15:39 0.191848 false true 0x4b67cf4d MathGame primeFactors 1002 2018-12-04 11:15:40 0.069523 false true 0x4b67cf4d MathGame primeFactors 1003 2018-12-04 11:15:41 0.186073 false true 0x4b67cf4d MathGame primeFactors 1004 2018-12-04 11:15:42 17.76437 true false 0x4b67cf4d MathGame primeFactors可以看到INDEX编号从1000开始递增——这并非随意设定源码中时间片段序列生成器为private static final AtomicInteger sequence new AtomicInteger(1000);每个时间片段按调用顺序获得唯一编号后续-i、-s、-d、-p等子命令都依赖这个编号定位记录。指定 Class 最大匹配数量$ tt -t -m 1 demo.MathGame primeFactors Press Q or CtrlC to abort. Affect(class count:1 , method count:1) cost in 130 ms, listenerId: 1. INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ------------------------------------------------------------------------------------------------------------------------------------- 1000 2022-12-25 19:41:45 2.629929 true false 0x3bf400 MathGame primeFactors 1001 2022-12-25 19:41:55 0.146161 false true 0x3bf400 MathGame primeFactors命令参数解析tt命令支持的主参数如下-t记录模式。告诉 Arthas 记录下类demo.MathGame的primeFactors方法的每次执行情况。-t模式下class-pattern与method-pattern为必填源码checkArguments()会显式校验缺任一参数都会报错提示补全。-n 3指定记录次数上限。当你执行一个调用量不高的方法时可能还有足够时间用CTRLC中断记录过程但遇到调用量非常大的方法瞬间就能把 JVM 内存撑爆。此时通过-n指定需要记录的次数达到次数后 Arthas 会主动中断记录避免人工操作无法停止。源码中对应的isLimitExceeded(command.getNumberOfLimit(), process.times().get())检查会在达到阈值后自动abortProcess。默认值为100。-m 1指定 Class 匹配的最大数量防止匹配到的 Class 数量太多导致 JVM 挂起默认值是 50。当通配符匹配到大量类时务必显式收紧。-c classloader hash当同名类被多个 classloader 加载时用-c指定只增强某个 classloader 加载的类。可以先执行sc -d className查看具体的 classloader hash用法参见 sc。-E开启正则表达式匹配默认是通配符匹配对应源码Option(shortName E, longName regex)。-x expand指定对象展开层级默认值为 1源码private Integer expand 1;用于控制-w、-i输出时对象的展示深度。-M sizeLimit结果大小上限字节默认值取自全局 options 的object-size-limit源码中校验其必须大于 0。--replay-times n与--replay-interval ms配合-p重放使用分别指定重放次数与多次重放之间的间隔单位 ms默认 1000ms源码默认值replayTimes 1、replayInterval 1000L。表格字段说明表格字段字段解释INDEX时间片段记录编号每一个编号代表着一次调用后续 tt 的很多命令都基于此编号指定记录操作非常重要。TIMESTAMP方法执行的本机时间记录了这个时间片段所发生的本机时间COST(ms)方法执行的耗时IS-RET方法是否以正常返回的形式结束IS-EXP方法是否以抛异常的形式结束OBJECT执行对象的hashCode()。注意曾经有人误认为是对象在 JVM 中的内存地址但很遗憾它不是。但它能帮助你简单地标记当前执行方法的类实体CLASS执行的类名METHOD执行的方法名从源码 TimeTunnelTable.java 可以看到表格的列宽、表头均在此定义其中OBJECT列在 TimeTunnelCommand.java 的createTimeFragmentVO()中通过0x toHexString(advice.getTarget().hashCode())生成印证了OBJECT 是 hashCode 而非内存地址这一说法。条件表达式精确记录你想要的那次调用使用tt记录时可能会遇到两个困惑Arthas 似乎很难区分出重载的方法我只需要观察特定参数但是tt却把全部调用都记录了下来。条件表达式可以解决这两个问题。条件表达式同样用 OGNL 编写核心判断对象依然是Advice对象。除了tt之外watch、trace、stack命令也都支持条件表达式。解决方法重载通过指定参数个数区分不同的方法签名tt -t *Test print params.length1如果参数个数一样还可以进一步限定参数类型tt -t *Test print params[1] instanceof Integer解决指定参数只记录符合特定参数值的调用tt -t *Test print params[0].mobile13989838402构成条件表达式的 Advice 对象上述表达式中的params[0]来自 Arthas 的通用通知对象Advice。Advice的核心变量包括loader、clazz、method、target、params、returnObj、throwExp、isBefore、isThrow、isReturn所有变量都可在表达式中直接使用。完整字段含义与用法请参考表达式核心变量。如果在表达式中编写了不符合 OGNL 语法或引入表格之外的变量命令会退出执行可根据异常信息修正表达式。在源码层面条件表达式的求值发生在 TimeTunnelAdviceListener.java 的afterFinishing()中先isConditionMet(command.getConditionExpress(), advice, cost)判断是否命中只有命中才putTimeTunnel()生成时间片段否则直接丢弃——这正是按条件选择性记录的实现依据。检索调用记录tt -l 与 tt -s当你用tt记录了一大片时间片段之后需要从中筛选出自己需要的时间片段。假设已经有如下记录$ tt -l INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ------------------------------------------------------------------------------------------------------------------------------------- 1000 2018-12-04 11:15:38 1.096236 false true 0x4b67cf4d MathGame primeFactors 1001 2018-12-04 11:15:39 0.191848 false true 0x4b67cf4d MathGame primeFactors 1002 2018-12-04 11:15:40 0.069523 false true 0x4b67cf4d MathGame primeFactors 1003 2018-12-04 11:15:41 0.186073 false true 0x4b67cf4d MathGame primeFactors 1004 2018-12-04 11:15:42 17.76437 true false 0x4b67cf4d MathGame primeFactors 9 1005 2018-12-04 11:15:43 0.4776 false true 0x4b67cf4d MathGame primeFactors Affect(row-cnt:6) cost in 4 ms.-l参数列出当前缓存的所有时间片段。若需要按条件筛选使用-s参数搜索表达式的核心对象同样是Advice对象$ tt -s method.nameprimeFactors INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ------------------------------------------------------------------------------------------------------------------------------------- 1000 2018-12-04 11:15:38 1.096236 false true 0x4b67cf4d MathGame primeFactors 1001 2018-12-04 11:15:39 0.191848 false true 0x4b67cf4d MathGame primeFactors 1002 2018-12-04 11:15:40 0.069523 false true 0x4b67cf4d MathGame primeFactors 1003 2018-12-04 11:15:41 0.186073 false true 0x4b67cf4d MathGame primeFactors 1004 2018-12-04 11:15:42 17.76437 true false 0x4b67cf4d MathGame primeFactors 9 1005 2018-12-04 11:15:43 0.4776 false true 0x4b67cf4d MathGame primeFactors Affect(row-cnt:6) cost in 607 ms.-s的搜索实现位于processSearch()它遍历整个timeFragmentMap对每条TimeFragment取出Advice用ExpressFactory.threadLocalExpress(advice).is(searchExpress)判断是否命中。-s还可以与-w组合实现先筛选、再对命中记录求值的流水线例如源码注释中给出的tt -s {params[0] 1} -w {params}。查看调用信息tt -i对于具体某一个时间片的信息通过-i参数后跟对应的INDEX编号查看详细信息$ tt -i 1003 INDEX 1003 GMT-CREATE 2018-12-04 11:15:41 COST(ms) 0.186073 OBJECT 0x4b67cf4d CLASS demo.MathGame METHOD primeFactors IS-RETURN false IS-EXCEPTION true PARAMETERS[0] Integer[-564322413] THROW-EXCEPTION java.lang.IllegalArgumentException: number is: -564322413, need 2 at demo.MathGame.primeFactors(MathGame.java:46) at demo.MathGame.run(MathGame.java:24) at demo.MathGame.main(MathGame.java:16) Affect(row-cnt:1) cost in 11 ms.从这份输出可以看到该次调用的完整现场入参PARAMETERS[0]、异常类型与完整堆栈THROW-EXCEPTION都被忠实还原。这与 MathGame.java 中number 2时抛出IllegalArgumentException(number is: number , need 2)的逻辑一一对应异常堆栈的MathGame.java:46即源码第 46 行的throw语句。重做一次调用tt -i INDEX -p排查问题时经常遇到这样的场景代码稍作调整后需要前端系统重新触发一次调用此时往往要求爷爷告奶奶地请前端联调同学再发起一次调用而有些调用场景并不容易触发。tt由于保存了当时调用的所有现场信息可以自己主动对一个INDEX编号的时间片发起一次调用从而解放沟通成本。使用-p参数即可重放通过--replay-times指定调用次数通过--replay-interval指定多次调用间隔单位 ms默认 1000ms$ tt -i 1004 -p RE-INDEX 1004 GMT-REPLAY 2018-12-04 11:26:00 OBJECT 0x4b67cf4d CLASS demo.MathGame METHOD primeFactors PARAMETERS[0] Integer[946738738] IS-RETURN true IS-EXCEPTION false COST(ms) 0.186073 RETURN-OBJ ArrayList[ Integer[2], Integer[11], Integer[17], Integer[2531387], ] Time fragment[1004] successfully replayed. Affect(row-cnt:1) cost in 14 ms.你会发现结果虽然一样但调用的路径发生了变化由原来的业务程序发起变成了 Arthas 自己的内部线程发起调用。源码processPlay()中通过method.invoke(advice.getTarget(), advice.getParams())执行重放并会临时将私有方法设为setAccessible(true)后再恢复原状多次重放时两次之间Thread.sleep(getReplayInterval())并在每次间隔后检查process.isRunning()命令被中断则提前结束。观察表达式tt -w-w, --watch-express用于对某个时间片段使用 OGNL 表达式进行观察可以使用表达式核心变量中的所有变量作为已知条件编写表达式。例如先记录run方法再观察其对象属性[arthas10718]$ tt -t demo.MathGame run -n 5 Press Q or CtrlC to abort. Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 1 INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1000 2021-01-08 21:54:17 0.901091 true false 0x7699a589 MathGame run [arthas10718]$ tt -w target.illegalArgumentCount -x 1 -i 1000 Integer[60] Affect(row-cnt:1) cost in 7 ms.还可以获取类的静态字段、调用类的静态方法[arthas10718]$ tt -t demo.MathGame run -n 5 Press Q or CtrlC to abort. Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 1 INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1000 2021-01-08 21:54:17 0.901091 true false 0x7699a589 MathGame run [arthas10718]$ tt -w demo.MathGamerandom.nextInt(100) -x 1 -i 1000 Integer[46]注意这里默认使用com.taobao.arthas.core.advisor.Advice#getLoader加载使用精确classloader的 ognl 效果更好。高级用法可参考获取 spring context 调用 bean 方法的官方 issue 讨论。使用 -w 需要强调的两点ThreadLocal 信息丢失很多框架偷偷将一些环境变量信息塞到发起调用线程的 ThreadLocal 中由于重放时调用线程发生了变化这些 ThreadLocal 线程信息无法通过 Arthas 保存所以这些信息将会丢失。常见 CASE 比如鹰眼的 TraceId 等。引用的对象需要强调的是tt命令是将当前环境的对象引用保存起来但仅仅只能保存一个引用而已。如果方法内部对入参进行了变更或者返回的对象经过了后续的处理那么在tt查看时将无法看到当时最准确的值。这也是watch命令存在的意义。这一提醒在源码中同样有迹可循TimeTunnelAdviceListener.java 在afterReturning/afterThrowing中通过popArgs()取出入参时的 args 快照因为函数执行过程中 args 可能被修改并注释说明用 JDK 原生Object[]实现固定大小512 深的 ring buffer 来暂存入参既保证拿到方法入口时的参数值又避免把 ArthasClassLoader 加载的对象放入业务线程的 ThreadLocalMap 导致 stop/detach 后 ClassLoader 无法被 GC 回收——这从侧面印证了tt 只保存引用、不能跟踪后续变化的边界。管理时间片段删除指定记录与清空缓存删除指定INDEX的记录tt -d -i 1001清除所有 tt 记录对应源码processDeleteAll()会清空整个timeFragmentMaptt --delete-all结合开头注意事项中的提醒排查结束后务必用--delete-all手动清理缓存因为退出 arthas 不会自动清除 tt 的缓存 map长时间不清理可能导致 OOM。源码实现纵深tt 是如何工作的tt的核心实现集中在 core/src/main/java/com/taobao/arthas/core/command/monitor200/ 目录可以从源码结构梳理出一条完整链路命令入口TimeTunnelCommand.java定义全部参数-t/-l/-i/-s/-w/-p/-d/--delete-all/-n/-x/-M/-E/-c/--replay-times/--replay-interval持有静态的MapInteger, TimeFragment timeFragmentMap与从 1000 开始递增的AtomicInteger sequence并在process()中按参数路由到enhance/processPlay/processList/processDeleteAll/processDelete/processSearch/processWatch/processShow等子流程。时间片段TimeFragment.java每个片段只封装三个字段——Advice advice现场信息、LocalDateTime gmtCreate记录时间、double cost耗时印证了只保存引用的设计。增强监听器TimeTunnelAdviceListener.java在before时压栈入参、启动计时在afterReturning/afterThrowing时恢复入口参数、计算耗时、判断条件表达式是否命中命中则生成TimeFragment放入 map 并输出一行表格同时统计次数、在超过-n阈值时自动中断。表格渲染TimeTunnelTable.java定义 8 列INDEX/TIMESTAMP/COST(ms)/IS-RET/IS-EXP/OBJECT/CLASS/METHOD的宽度与绘制逻辑-i详情、-p重放结果、-s-w的 SEARCH-RESULT 表格均由此渲染。理解了这条链路就能明白tt的三个关键行为记录的是调用发生时刻的引用快照、条件表达式在记录阶段就完成过滤、达到-n上限会自动停止。总结tt是 Arthas 排查线上疑难问题的利器它把事后想复盘变成了事前先记录配合-s检索、-i查看、-w观察、-p重放一套命令即可覆盖记录 → 检索 → 定位 → 复现的完整排查闭环。使用时请牢记两条红线缓存上限默认 100 条、退出 arthas 不自动清理排查完毕务必tt --delete-all手动释放内存避免目标进程 OOM。相关命令的完整参数还可通过help tt查看。【免费下载链接】arthasAlibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas项目地址: https://gitcode.com/gh_mirrors/ar/arthas创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表