diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java index 38588f7c7..16950d2ac 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java @@ -1,12 +1,12 @@ package com.taobao.arthas.core.command.monitor200; +import com.taobao.arthas.core.advisor.Advice; import com.taobao.arthas.core.advisor.AdviceListener; +import com.taobao.arthas.core.advisor.ArthasMethod; import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.express.ExpressException; import com.taobao.arthas.core.command.express.ExpressFactory; import com.taobao.arthas.core.shell.command.CommandProcess; -import com.taobao.arthas.core.advisor.Advice; -import com.taobao.arthas.core.advisor.ArthasMethod; import com.taobao.arthas.core.shell.handlers.command.CommandInterruptHandler; import com.taobao.arthas.core.shell.handlers.shell.QExitHandler; import com.taobao.arthas.core.util.LogUtil; @@ -15,11 +15,11 @@ import com.taobao.arthas.core.util.StringUtils; import com.taobao.arthas.core.util.affect.RowAffect; import com.taobao.arthas.core.util.matcher.Matcher; import com.taobao.arthas.core.view.ObjectView; -import com.taobao.middleware.cli.annotations.Argument; import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Option; import com.taobao.middleware.cli.annotations.Summary; +import com.taobao.middleware.cli.annotations.Argument; import com.taobao.text.ui.TableElement; import com.taobao.text.util.RenderUtil; @@ -44,7 +44,8 @@ import static java.lang.String.format; " tt -l\n" + " tt -i 1000\n" + " tt -i 1000 -w params[0]\n" + - " tt -i 1000 -p\n" + + " tt -i 1000 -p \n" + + " tt -i 1000 -p --replay-times 3 --replay-interval 3000\n" + " tt --delete-all\n" + Constants.WIKI + Constants.WIKI_HOME + "tt") public class TimeTunnelCommand extends EnhancerCommand { @@ -75,6 +76,8 @@ public class TimeTunnelCommand extends EnhancerCommand { private boolean isDelete = false; private boolean isRegEx = false; private int numberOfLimit = 100; + private int replayTimes = 1; + private long replayInterval = 1000L; @Argument(index = 0, argName = "class-pattern", required = false) @Description("Path and classname of Pattern Matching") @@ -167,6 +170,20 @@ public class TimeTunnelCommand extends EnhancerCommand { this.numberOfLimit = numberOfLimit; } + + @Option(longName = "replay-times") + @Description("execution times when play tt") + public void setReplayTimes(int replayTimes) { + this.replayTimes = replayTimes; + } + + @Option(longName = "replay-interval") + @Description("replay interval for play tt with option r greater than 1") + public void setReplayInterval(int replayInterval) { + this.replayInterval = replayInterval; + } + + public boolean isRegEx() { return isRegEx; } @@ -188,6 +205,14 @@ public class TimeTunnelCommand extends EnhancerCommand { } + public int getReplayTimes() { + return replayTimes; + } + + public long getReplayInterval() { + return replayInterval; + } + private boolean hasWatchExpress() { return !StringUtils.isEmpty(watchExpress); } @@ -428,27 +453,40 @@ public class TimeTunnelCommand extends EnhancerCommand { String methodName = advice.getMethod().getName(); String objectAddress = advice.getTarget() == null ? "NULL" : "0x" + toHexString(advice.getTarget().hashCode()); - TableElement table = TimeTunnelTable.createDefaultTable(); - TimeTunnelTable.drawPlayHeader(className, methodName, objectAddress, index, table); - TimeTunnelTable.drawParameters(advice, table, isNeedExpand(), expand); + ArthasMethod method = advice.getMethod(); + method.setAccessible(true); boolean accessible = advice.getMethod().isAccessible(); - try { - method.setAccessible(true); - Object returnObj = method.invoke(advice.getTarget(), advice.getParams()); - TimeTunnelTable.drawPlayResult(table, returnObj, isNeedExpand(), expand, sizeLimit); - } catch (Throwable t) { - TimeTunnelTable.drawPlayException(table, t, isNeedExpand(), expand); - } finally { - method.setAccessible(accessible); + for (int i = 0; i < getReplayTimes(); i++) { +// wait for the next execution + if (i > 0) { + try { + Thread.sleep(getReplayInterval()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + long beginTime = System.nanoTime(); + TableElement table = TimeTunnelTable.createDefaultTable(); + TimeTunnelTable.drawPlayHeader(className, methodName, objectAddress, index, table); + TimeTunnelTable.drawParameters(advice, table, isNeedExpand(), expand); + + try { + Object returnObj = method.invoke(advice.getTarget(), advice.getParams()); + double cost = (System.nanoTime() - beginTime) / 1000000.0; + TimeTunnelTable.drawPlayResult(table, returnObj, isNeedExpand(), expand, sizeLimit, cost); + } catch (Throwable t) { + TimeTunnelTable.drawPlayException(table, t, isNeedExpand(), expand); + } + process.write(RenderUtil.render(table, process.width())) + .write(format("Time fragment[%d] successfully replayed.", index)) + .write("\n"); + affect.rCnt(1); + process.write(affect.toString()).write("\n"); } + method.setAccessible(accessible); - process.write(RenderUtil.render(table, process.width())) - .write(format("Time fragment[%d] successfully replayed.", index)) - .write("\n"); - affect.rCnt(1); - process.write(affect.toString()).write("\n"); } finally { process.end(); } diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelTable.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelTable.java index 5ad73e961..e19d192c2 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelTable.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelTable.java @@ -1,7 +1,7 @@ package com.taobao.arthas.core.command.monitor200; -import com.taobao.arthas.core.command.express.ExpressException; import com.taobao.arthas.core.advisor.Advice; +import com.taobao.arthas.core.command.express.ExpressException; import com.taobao.arthas.core.command.express.ExpressFactory; import com.taobao.arthas.core.util.StringUtils; import com.taobao.arthas.core.view.ObjectView; @@ -179,10 +179,11 @@ public class TimeTunnelTable { } static void drawPlayResult(TableElement table, Object returnObj, boolean isNeedExpand, int expandLevel, - int sizeLimit) { + int sizeLimit, double cost) { // 执行成功:输出成功状态 table.row("IS-RETURN", "" + true); table.row("IS-EXCEPTION", "" + false); + table.row("COST(ms)", "" + cost); // 执行成功:输出成功结果 if (isNeedExpand) { diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index 1ca601a45..da122ca3b 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -158,7 +158,6 @@ public class ArthasBootstrap { } return arthasBootstrap; } - /** * @return ArthasServer单例 */ diff --git a/site/src/site/sphinx/en/tt.md b/site/src/site/sphinx/en/tt.md index 9f68d5fba..e95fcb9ba 100644 --- a/site/src/site/sphinx/en/tt.md +++ b/site/src/site/sphinx/en/tt.md @@ -127,7 +127,8 @@ Affect(row-cnt:1) cost in 11 ms. ### Replay record -Since Arthas stores the context of the call, you can even *replay* the method calling afterwards with extra option `-p` to replay the issue for advanced troubleshooting. +Since Arthas stores the context of the call, you can even *replay* the method calling afterwards with extra option `-p` to replay the issue for advanced troubleshooting, option `--replay-times` +define the replay execution times, option `--replay-interval` define the interval(unit in ms,with default value 1000) of replays ```bash $ tt -i 1004 -p diff --git a/site/src/site/sphinx/tt.md b/site/src/site/sphinx/tt.md index a856ada4a..7880f148d 100644 --- a/site/src/site/sphinx/tt.md +++ b/site/src/site/sphinx/tt.md @@ -147,7 +147,8 @@ Affect(row-cnt:1) cost in 11 ms. 当你稍稍做了一些调整之后,你可能需要前端系统重新触发一次你的调用,此时得求爷爷告奶奶的需要前端配合联调的同学再次发起一次调用。而有些场景下,这个调用不是这么好触发的。 -`tt` 命令由于保存了当时调用的所有现场信息,所以我们可以自己主动对一个 `INDEX` 编号的时间片自主发起一次调用,从而解放你的沟通成本。此时你需要 `-p` 参数。 +`tt` 命令由于保存了当时调用的所有现场信息,所以我们可以自己主动对一个 `INDEX` 编号的时间片自主发起一次调用,从而解放你的沟通成本。此时你需要 `-p` 参数。通过 `--replay-times` 指定 +调用次数,通过 `--replay-interval` 指定多次调用间隔(单位ms, 默认1000ms) ```bash $ tt -i 1004 -p @@ -159,6 +160,7 @@ $ tt -i 1004 -p PARAMETERS[0] @Integer[946738738] IS-RETURN true IS-EXCEPTION false + COST(ms) 0.186073 RETURN-OBJ @ArrayList[ @Integer[2], @Integer[11],