mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
Transform commands of pkg monitor200 (part 2) (#1355)
This commit is contained in:
@@ -190,7 +190,7 @@ public class ClassLoaderCommand extends AnnotatedCommand {
|
||||
if (cl instanceof URLClassLoader) {
|
||||
List<String> classLoaderUrls = getClassLoaderUrls(cl);
|
||||
affect.rCnt(classLoaderUrls.size());
|
||||
if (classLoaderUrls.isEmpty()){
|
||||
if (classLoaderUrls.isEmpty()) {
|
||||
process.appendResult(new MessageModel("urls is empty."));
|
||||
} else {
|
||||
process.appendResult(new ClassLoaderModel().setUrls(classLoaderUrls));
|
||||
@@ -198,9 +198,9 @@ public class ClassLoaderCommand extends AnnotatedCommand {
|
||||
}
|
||||
} else {
|
||||
process.appendResult(new MessageModel("not a URLClassLoader."));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
process.appendResult(new RowAffectModel(affect));
|
||||
process.end();
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class enhance affect vo
|
||||
* @author gongdewei 2020/6/22
|
||||
*/
|
||||
public class EnhancerAffectVO {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import com.taobao.arthas.core.util.affect.EnhancerAffect;
|
||||
|
||||
/**
|
||||
* Data model of EnhancerCommand
|
||||
*
|
||||
* @author gongdewei 2020/7/20
|
||||
*/
|
||||
public class EnhancerModel extends ResultModel {
|
||||
|
||||
private final EnhancerAffectVO effect;
|
||||
private boolean success;
|
||||
private String message;
|
||||
|
||||
public EnhancerModel(EnhancerAffect effect, boolean success) {
|
||||
if (effect != null) {
|
||||
this.effect = new EnhancerAffectVO(effect);
|
||||
this.success = success;
|
||||
} else {
|
||||
this.effect = new EnhancerAffectVO(-1, 0, 0, -1);
|
||||
this.success = false;
|
||||
}
|
||||
}
|
||||
|
||||
public EnhancerModel(EnhancerAffect effect, boolean success, String message) {
|
||||
this(effect, success);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "enhancer";
|
||||
}
|
||||
|
||||
public EnhancerAffectVO getEffect() {
|
||||
return effect;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,14 @@ package com.taobao.arthas.core.command.model;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Data model of GetStaticCommand
|
||||
* @author gongdewei 2020/4/20
|
||||
*/
|
||||
public class GetStaticModel extends ResultModel {
|
||||
|
||||
private ObjectVO field;
|
||||
private Collection<ClassVO> matchedClasses;
|
||||
|
||||
//only for view
|
||||
private transient int expand;
|
||||
private ObjectVO field;
|
||||
private int expand;
|
||||
|
||||
public GetStaticModel() {
|
||||
}
|
||||
@@ -41,7 +40,7 @@ public class GetStaticModel extends ResultModel {
|
||||
this.matchedClasses = matchedClasses;
|
||||
}
|
||||
|
||||
public int expand() {
|
||||
public int getExpand() {
|
||||
return expand;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
/**
|
||||
* Method call node of TraceCommand
|
||||
* @author gongdewei 2020/4/29
|
||||
*/
|
||||
public class MethodNode extends TraceNode {
|
||||
|
||||
private String className;
|
||||
private String methodName;
|
||||
private int lineNumber;
|
||||
private Boolean isThrow;
|
||||
private String throwExp;
|
||||
|
||||
/**
|
||||
* 是否为invoke方法,true为beforeInvoke,false为方法体入口的onBefore
|
||||
*/
|
||||
private boolean isInvoking;
|
||||
|
||||
/**
|
||||
* 开始时间戳
|
||||
*/
|
||||
private long beginTimestamp;
|
||||
|
||||
/**
|
||||
* 结束时间戳
|
||||
*/
|
||||
private long endTimestamp;
|
||||
|
||||
/**
|
||||
* 合并统计相同调用,并计算最小\最大\总耗时
|
||||
*/
|
||||
private long minCost = Long.MAX_VALUE;
|
||||
private long maxCost = Long.MIN_VALUE;
|
||||
private long totalCost = 0;
|
||||
private long times = 0;
|
||||
|
||||
|
||||
public MethodNode(String className, String methodName, int lineNumber, boolean isInvoking) {
|
||||
super("method");
|
||||
this.className = className;
|
||||
this.methodName = methodName;
|
||||
this.lineNumber = lineNumber;
|
||||
this.isInvoking = isInvoking;
|
||||
}
|
||||
|
||||
public void begin() {
|
||||
beginTimestamp = System.nanoTime();
|
||||
}
|
||||
|
||||
public void end() {
|
||||
endTimestamp = System.nanoTime();
|
||||
|
||||
long cost = getCost();
|
||||
if (cost < minCost) {
|
||||
minCost = cost;
|
||||
}
|
||||
if (cost > maxCost) {
|
||||
maxCost = cost;
|
||||
}
|
||||
times++;
|
||||
totalCost += cost;
|
||||
}
|
||||
|
||||
public long getCost() {
|
||||
return endTimestamp - beginTimestamp;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public void setClassName(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public void setMethodName(String methodName) {
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public void setLineNumber(int lineNumber) {
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public Boolean getThrow() {
|
||||
return isThrow;
|
||||
}
|
||||
|
||||
public void setThrow(Boolean aThrow) {
|
||||
isThrow = aThrow;
|
||||
}
|
||||
|
||||
public String getThrowExp() {
|
||||
return throwExp;
|
||||
}
|
||||
|
||||
public void setThrowExp(String throwExp) {
|
||||
this.throwExp = throwExp;
|
||||
}
|
||||
|
||||
public long getMinCost() {
|
||||
return minCost;
|
||||
}
|
||||
|
||||
public void setMinCost(long minCost) {
|
||||
this.minCost = minCost;
|
||||
}
|
||||
|
||||
public long getMaxCost() {
|
||||
return maxCost;
|
||||
}
|
||||
|
||||
public void setMaxCost(long maxCost) {
|
||||
this.maxCost = maxCost;
|
||||
}
|
||||
|
||||
public long getTotalCost() {
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
public void setTotalCost(long totalCost) {
|
||||
this.totalCost = totalCost;
|
||||
}
|
||||
|
||||
public long getTimes() {
|
||||
return times;
|
||||
}
|
||||
|
||||
public void setTimes(long times) {
|
||||
this.times = times;
|
||||
}
|
||||
|
||||
public boolean isInvoking() {
|
||||
return isInvoking;
|
||||
}
|
||||
|
||||
public void setInvoking(boolean invoking) {
|
||||
isInvoking = invoking;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import com.taobao.arthas.core.command.monitor200.MonitorData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Data model of MonitorCommand
|
||||
* @author gongdewei 2020/4/28
|
||||
*/
|
||||
public class MonitorModel extends ResultModel {
|
||||
|
||||
private List<MonitorData> monitorDataList;
|
||||
|
||||
public MonitorModel() {
|
||||
}
|
||||
|
||||
public MonitorModel(List<MonitorData> monitorDataList) {
|
||||
this.monitorDataList = monitorDataList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "monitor";
|
||||
}
|
||||
|
||||
public List<MonitorData> getMonitorDataList() {
|
||||
return monitorDataList;
|
||||
}
|
||||
|
||||
public void setMonitorDataList(List<MonitorData> monitorDataList) {
|
||||
this.monitorDataList = monitorDataList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Data model of ProfilerCommand
|
||||
* @author gongdewei 2020/4/27
|
||||
*/
|
||||
public class ProfilerModel extends ResultModel {
|
||||
|
||||
private String action;
|
||||
private String actionArg;
|
||||
private String executeResult;
|
||||
private Collection<String> supportedActions;
|
||||
private String outputFile;
|
||||
private Long duration;
|
||||
|
||||
public ProfilerModel() {
|
||||
}
|
||||
|
||||
public ProfilerModel(Collection<String> supportedActions) {
|
||||
this.supportedActions = supportedActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "profiler";
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public String getActionArg() {
|
||||
return actionArg;
|
||||
}
|
||||
|
||||
public void setActionArg(String actionArg) {
|
||||
this.actionArg = actionArg;
|
||||
}
|
||||
|
||||
public Collection<String> getSupportedActions() {
|
||||
return supportedActions;
|
||||
}
|
||||
|
||||
public void setSupportedActions(Collection<String> supportedActions) {
|
||||
this.supportedActions = supportedActions;
|
||||
}
|
||||
|
||||
public String getExecuteResult() {
|
||||
return executeResult;
|
||||
}
|
||||
|
||||
public void setExecuteResult(String executeResult) {
|
||||
this.executeResult = executeResult;
|
||||
}
|
||||
|
||||
public String getOutputFile() {
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
public void setOutputFile(String outputFile) {
|
||||
this.outputFile = outputFile;
|
||||
}
|
||||
|
||||
public Long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(Long duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* StackCommand result model
|
||||
* @author gongdewei 2020/4/13
|
||||
*/
|
||||
public class StackModel extends ResultModel {
|
||||
|
||||
private Date ts;
|
||||
private double cost;
|
||||
private String traceId;
|
||||
private String rpcId;
|
||||
private String threadName;
|
||||
private String threadId;
|
||||
private boolean daemon;
|
||||
private int priority;
|
||||
/* Thread Current ClassLoader */
|
||||
private String classloader;
|
||||
private StackTraceElement[] stackTrace;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "stack";
|
||||
}
|
||||
|
||||
public Date getTs() {
|
||||
return ts;
|
||||
}
|
||||
|
||||
public void setTs(Date ts) {
|
||||
this.ts = ts;
|
||||
}
|
||||
|
||||
public double getCost() {
|
||||
return cost;
|
||||
}
|
||||
|
||||
public void setCost(double cost) {
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
public String getThreadName() {
|
||||
return threadName;
|
||||
}
|
||||
|
||||
public void setThreadName(String threadName) {
|
||||
this.threadName = threadName;
|
||||
}
|
||||
|
||||
public String getThreadId() {
|
||||
return threadId;
|
||||
}
|
||||
|
||||
public void setThreadId(String threadId) {
|
||||
this.threadId = threadId;
|
||||
}
|
||||
|
||||
public boolean isDaemon() {
|
||||
return daemon;
|
||||
}
|
||||
|
||||
public void setDaemon(boolean daemon) {
|
||||
this.daemon = daemon;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public String getClassloader() {
|
||||
return classloader;
|
||||
}
|
||||
|
||||
public void setClassloader(String classloader) {
|
||||
this.classloader = classloader;
|
||||
}
|
||||
|
||||
public String getTraceId() {
|
||||
return traceId;
|
||||
}
|
||||
|
||||
public void setTraceId(String traceId) {
|
||||
this.traceId = traceId;
|
||||
}
|
||||
|
||||
public String getRpcId() {
|
||||
return rpcId;
|
||||
}
|
||||
|
||||
public void setRpcId(String rpcId) {
|
||||
this.rpcId = rpcId;
|
||||
}
|
||||
|
||||
public StackTraceElement[] getStackTrace() {
|
||||
return stackTrace;
|
||||
}
|
||||
|
||||
public void setStackTrace(StackTraceElement[] stackTrace) {
|
||||
this.stackTrace = stackTrace;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Thread root node of TraceCommand
|
||||
* @author gongdewei 2020/4/29
|
||||
*/
|
||||
public class ThreadNode extends TraceNode {
|
||||
|
||||
private String threadName;
|
||||
private long threadId;
|
||||
private boolean daemon;
|
||||
private int priority;
|
||||
private String classloader;
|
||||
private Date timestamp;
|
||||
|
||||
private String traceId;
|
||||
private String rpcId;
|
||||
|
||||
public ThreadNode() {
|
||||
super("thread");
|
||||
timestamp = new Date();
|
||||
}
|
||||
|
||||
public ThreadNode(String threadName, long threadId, boolean daemon, int priority, String classloader) {
|
||||
super("thread");
|
||||
this.threadName = threadName;
|
||||
this.threadId = threadId;
|
||||
this.daemon = daemon;
|
||||
this.priority = priority;
|
||||
this.classloader = classloader;
|
||||
timestamp = new Date();
|
||||
}
|
||||
|
||||
public String getThreadName() {
|
||||
return threadName;
|
||||
}
|
||||
|
||||
public void setThreadName(String threadName) {
|
||||
this.threadName = threadName;
|
||||
}
|
||||
|
||||
public long getThreadId() {
|
||||
return threadId;
|
||||
}
|
||||
|
||||
public void setThreadId(long threadId) {
|
||||
this.threadId = threadId;
|
||||
}
|
||||
|
||||
public boolean isDaemon() {
|
||||
return daemon;
|
||||
}
|
||||
|
||||
public void setDaemon(boolean daemon) {
|
||||
this.daemon = daemon;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public String getClassloader() {
|
||||
return classloader;
|
||||
}
|
||||
|
||||
public void setClassloader(String classloader) {
|
||||
this.classloader = classloader;
|
||||
}
|
||||
|
||||
public Date getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(Date timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String getTraceId() {
|
||||
return traceId;
|
||||
}
|
||||
|
||||
public void setTraceId(String traceId) {
|
||||
this.traceId = traceId;
|
||||
}
|
||||
|
||||
public String getRpcId() {
|
||||
return rpcId;
|
||||
}
|
||||
|
||||
public void setRpcId(String rpcId) {
|
||||
this.rpcId = rpcId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
/**
|
||||
* Throw exception info node of TraceCommand
|
||||
* @author gongdewei 2020/7/21
|
||||
*/
|
||||
public class ThrowNode extends TraceNode {
|
||||
private String exception;
|
||||
private String message;
|
||||
private int lineNumber;
|
||||
|
||||
public ThrowNode() {
|
||||
super("throw");
|
||||
}
|
||||
|
||||
public String getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
public void setException(String exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public void setLineNumber(int lineNumber) {
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* VO for TimeFragment
|
||||
* @author gongdewei 2020/4/27
|
||||
*/
|
||||
public class TimeFragmentVO {
|
||||
private Integer index;
|
||||
private Date timestamp;
|
||||
private double cost;
|
||||
private boolean isReturn;
|
||||
private boolean isThrow;
|
||||
private String object;
|
||||
private String className;
|
||||
private String methodName;
|
||||
private Object[] params;
|
||||
private Object returnObj;
|
||||
private Throwable throwExp;
|
||||
|
||||
public TimeFragmentVO() {
|
||||
}
|
||||
|
||||
public Integer getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setIndex(Integer index) {
|
||||
this.index = index;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setTimestamp(Date timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getCost() {
|
||||
return cost;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setCost(double cost) {
|
||||
this.cost = cost;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isReturn() {
|
||||
return isReturn;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setReturn(boolean aReturn) {
|
||||
isReturn = aReturn;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isThrow() {
|
||||
return isThrow;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setThrow(boolean aThrow) {
|
||||
isThrow = aThrow;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setObject(String object) {
|
||||
this.object = object;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setClassName(String className) {
|
||||
this.className = className;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setMethodName(String methodName) {
|
||||
this.methodName = methodName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object[] getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setParams(Object[] params) {
|
||||
this.params = params;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object getReturnObj() {
|
||||
return returnObj;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setReturnObj(Object returnObj) {
|
||||
this.returnObj = returnObj;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Throwable getThrowExp() {
|
||||
return throwExp;
|
||||
}
|
||||
|
||||
public TimeFragmentVO setThrowExp(Throwable throwExp) {
|
||||
this.throwExp = throwExp;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Data model of TimeTunnelCommand
|
||||
* @author gongdewei 2020/4/27
|
||||
*/
|
||||
public class TimeTunnelModel extends ResultModel {
|
||||
|
||||
//查看列表
|
||||
private List<TimeFragmentVO> timeFragmentList;
|
||||
|
||||
//是否为第一次输出(需要加表头)
|
||||
private Boolean isFirst;
|
||||
|
||||
//查看单条记录
|
||||
private TimeFragmentVO timeFragment;
|
||||
|
||||
//重放执行的结果
|
||||
private TimeFragmentVO replayResult;
|
||||
|
||||
//重放执行的次数
|
||||
private Integer replayNo;
|
||||
|
||||
private Object watchValue;
|
||||
|
||||
//search: tt -s {} -w {}
|
||||
private Map<Integer, Object> watchResults;
|
||||
|
||||
private Integer expand;
|
||||
|
||||
private Integer sizeLimit;
|
||||
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "tt";
|
||||
}
|
||||
|
||||
public List<TimeFragmentVO> getTimeFragmentList() {
|
||||
return timeFragmentList;
|
||||
}
|
||||
|
||||
public TimeTunnelModel setTimeFragmentList(List<TimeFragmentVO> timeFragmentList) {
|
||||
this.timeFragmentList = timeFragmentList;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TimeFragmentVO getTimeFragment() {
|
||||
return timeFragment;
|
||||
}
|
||||
|
||||
public TimeTunnelModel setTimeFragment(TimeFragmentVO timeFragment) {
|
||||
this.timeFragment = timeFragment;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getExpand() {
|
||||
return expand;
|
||||
}
|
||||
|
||||
public TimeTunnelModel setExpand(Integer expand) {
|
||||
this.expand = expand;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getSizeLimit() {
|
||||
return sizeLimit;
|
||||
}
|
||||
|
||||
public TimeTunnelModel setSizeLimit(Integer sizeLimit) {
|
||||
this.sizeLimit = sizeLimit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object getWatchValue() {
|
||||
return watchValue;
|
||||
}
|
||||
|
||||
public TimeTunnelModel setWatchValue(Object watchValue) {
|
||||
this.watchValue = watchValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<Integer, Object> getWatchResults() {
|
||||
return watchResults;
|
||||
}
|
||||
|
||||
public TimeTunnelModel setWatchResults(Map<Integer, Object> watchResults) {
|
||||
this.watchResults = watchResults;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TimeFragmentVO getReplayResult() {
|
||||
return replayResult;
|
||||
}
|
||||
|
||||
public TimeTunnelModel setReplayResult(TimeFragmentVO replayResult) {
|
||||
this.replayResult = replayResult;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getReplayNo() {
|
||||
return replayNo;
|
||||
}
|
||||
|
||||
public TimeTunnelModel setReplayNo(Integer replayNo) {
|
||||
this.replayNo = replayNo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean getFirst() {
|
||||
return isFirst;
|
||||
}
|
||||
|
||||
public TimeTunnelModel setFirst(Boolean first) {
|
||||
isFirst = first;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
/**
|
||||
* Data model of TraceCommand
|
||||
* @author gongdewei 2020/4/29
|
||||
*/
|
||||
public class TraceModel extends ResultModel {
|
||||
private TraceNode root;
|
||||
private int nodeCount;
|
||||
|
||||
public TraceModel() {
|
||||
}
|
||||
|
||||
public TraceModel(TraceNode root, int nodeCount) {
|
||||
this.root = root;
|
||||
this.nodeCount = nodeCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "trace";
|
||||
}
|
||||
|
||||
public TraceNode getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public void setRoot(TraceNode root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
public int getNodeCount() {
|
||||
return nodeCount;
|
||||
}
|
||||
|
||||
public void setNodeCount(int nodeCount) {
|
||||
this.nodeCount = nodeCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Abstract Node of TraceCommand
|
||||
* @author gongdewei 2020/4/28
|
||||
*/
|
||||
public abstract class TraceNode {
|
||||
|
||||
protected TraceNode parent;
|
||||
protected List<TraceNode> children;
|
||||
|
||||
/**
|
||||
* node type: method,
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String mark;
|
||||
/**
|
||||
* TODO marks数量的作用?是否可以去掉
|
||||
*/
|
||||
private int marks = 0;
|
||||
|
||||
public TraceNode(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void addChild(TraceNode child) {
|
||||
if (children == null) {
|
||||
children = new ArrayList<TraceNode>();
|
||||
}
|
||||
this.children.add(child);
|
||||
child.setParent(this);
|
||||
}
|
||||
|
||||
public void setMark(String mark) {
|
||||
this.mark = mark;
|
||||
marks++;
|
||||
}
|
||||
|
||||
public String getMark() {
|
||||
return mark;
|
||||
}
|
||||
|
||||
public Integer marks() {
|
||||
return marks;
|
||||
}
|
||||
|
||||
public void begin() {
|
||||
}
|
||||
|
||||
public void end() {
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public TraceNode parent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(TraceNode parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public List<TraceNode> getChildren() {
|
||||
return children;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
|
||||
import com.taobao.arthas.core.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tree model of TraceCommand
|
||||
* @author gongdewei 2020/4/28
|
||||
*/
|
||||
public class TraceTree {
|
||||
private TraceNode root;
|
||||
|
||||
private TraceNode current;
|
||||
private int nodeCount = 0;
|
||||
|
||||
public TraceTree(ThreadNode root) {
|
||||
this.root = root;
|
||||
this.current = root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a new method call
|
||||
* @param className className of method
|
||||
* @param methodName method name of the call
|
||||
* @param lineNumber line number of invoke point
|
||||
* @param isInvoking Whether to invoke this method in other classes
|
||||
*/
|
||||
public void begin(String className, String methodName, int lineNumber, boolean isInvoking) {
|
||||
TraceNode child = findChild(current, className, methodName, lineNumber);
|
||||
if (child == null) {
|
||||
child = new MethodNode(className, methodName, lineNumber, isInvoking);
|
||||
current.addChild(child);
|
||||
}
|
||||
child.begin();
|
||||
current = child;
|
||||
nodeCount += 1;
|
||||
}
|
||||
|
||||
private TraceNode findChild(TraceNode node, String className, String methodName, int lineNumber) {
|
||||
List<TraceNode> childList = node.getChildren();
|
||||
if (childList != null) {
|
||||
//less memory than foreach/iterator
|
||||
for (int i = 0; i < childList.size(); i++) {
|
||||
TraceNode child = childList.get(i);
|
||||
if (matchNode(child, className, methodName, lineNumber)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean matchNode(TraceNode node, String className, String methodName, int lineNumber) {
|
||||
if (node instanceof MethodNode) {
|
||||
MethodNode methodNode = (MethodNode) node;
|
||||
if (lineNumber != methodNode.getLineNumber()) return false;
|
||||
if (className != null ? !className.equals(methodNode.getClassName()) : methodNode.getClassName() != null) return false;
|
||||
return methodName != null ? methodName.equals(methodNode.getMethodName()) : methodNode.getMethodName() == null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void end() {
|
||||
current.end();
|
||||
if (current.parent() != null) {
|
||||
//TODO 为什么会到达这里? 调用end次数比begin多?
|
||||
current = current.parent();
|
||||
}
|
||||
}
|
||||
|
||||
public void end(Throwable throwable, int lineNumber) {
|
||||
ThrowNode throwNode = new ThrowNode();
|
||||
throwNode.setException(throwable.getClass().getName());
|
||||
throwNode.setMessage(throwable.getMessage());
|
||||
throwNode.setLineNumber(lineNumber);
|
||||
current.addChild(throwNode);
|
||||
this.end(true);
|
||||
}
|
||||
|
||||
public void end(boolean isThrow) {
|
||||
if (isThrow) {
|
||||
current.setMark("throws Exception");
|
||||
if (current instanceof MethodNode) {
|
||||
MethodNode methodNode = (MethodNode) current;
|
||||
methodNode.setThrow(true);
|
||||
}
|
||||
}
|
||||
this.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修整树结点
|
||||
*/
|
||||
public void trim() {
|
||||
this.normalizeClassName(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换标准类名,放在trace结束后统一转换,减少重复操作
|
||||
* @param node
|
||||
*/
|
||||
private void normalizeClassName(TraceNode node) {
|
||||
if (node instanceof MethodNode) {
|
||||
MethodNode methodNode = (MethodNode) node;
|
||||
String nodeClassName = methodNode.getClassName();
|
||||
String normalizeClassName = StringUtils.normalizeClassName(nodeClassName);
|
||||
methodNode.setClassName(normalizeClassName);
|
||||
}
|
||||
List<TraceNode> children = node.getChildren();
|
||||
if (children != null) {
|
||||
//less memory fragment than foreach
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
TraceNode child = children.get(i);
|
||||
normalizeClassName(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TraceNode getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public TraceNode current() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public int getNodeCount() {
|
||||
return nodeCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Watch command result model
|
||||
*
|
||||
* @author gongdewei 2020/03/26
|
||||
*/
|
||||
public class WatchModel extends ResultModel {
|
||||
|
||||
private Date ts;
|
||||
private double cost;
|
||||
private Object value;
|
||||
|
||||
private Integer expand;
|
||||
private Integer sizeLimit;
|
||||
|
||||
public WatchModel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "watch";
|
||||
}
|
||||
|
||||
public Date getTs() {
|
||||
return ts;
|
||||
}
|
||||
|
||||
public void setTs(Date ts) {
|
||||
this.ts = ts;
|
||||
}
|
||||
|
||||
public double getCost() {
|
||||
return cost;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setCost(double cost) {
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setExpand(Integer expand) {
|
||||
this.expand = expand;
|
||||
}
|
||||
|
||||
public void setSizeLimit(Integer sizeLimit) {
|
||||
this.sizeLimit = sizeLimit;
|
||||
}
|
||||
|
||||
public Integer getExpand() {
|
||||
return expand;
|
||||
}
|
||||
|
||||
public Integer getSizeLimit() {
|
||||
return sizeLimit;
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -42,7 +42,7 @@ public class AbstractTraceAdviceListener extends AdviceListenerAdapter {
|
||||
@Override
|
||||
public void before(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args)
|
||||
throws Throwable {
|
||||
threadBoundEntity.get().view.begin(clazz.getName() + ":" + method.getName() + "()");
|
||||
threadBoundEntity.get().tree.begin(clazz.getName(), method.getName(), -1, false);
|
||||
threadBoundEntity.get().deep++;
|
||||
// 开始计算本次方法调用耗时
|
||||
threadLocalWatch.start();
|
||||
@@ -51,7 +51,7 @@ public class AbstractTraceAdviceListener extends AdviceListenerAdapter {
|
||||
@Override
|
||||
public void afterReturning(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args,
|
||||
Object returnObject) throws Throwable {
|
||||
threadBoundEntity.get().view.end();
|
||||
threadBoundEntity.get().tree.end();
|
||||
final Advice advice = Advice.newForAfterRetuning(loader, clazz, method, target, args, returnObject);
|
||||
finishing(advice);
|
||||
}
|
||||
@@ -60,7 +60,7 @@ public class AbstractTraceAdviceListener extends AdviceListenerAdapter {
|
||||
public void afterThrowing(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args,
|
||||
Throwable throwable) throws Throwable {
|
||||
int lineNumber = throwable.getStackTrace()[0].getLineNumber();
|
||||
threadBoundEntity.get().view.begin("throw:" + throwable.getClass().getName() + "()" + " #" + lineNumber).end().end();
|
||||
threadBoundEntity.get().tree.end(throwable, lineNumber);
|
||||
final Advice advice = Advice.newForAfterThrowing(loader, clazz, method, target, args, throwable);
|
||||
finishing(advice);
|
||||
}
|
||||
@@ -80,20 +80,20 @@ public class AbstractTraceAdviceListener extends AdviceListenerAdapter {
|
||||
}
|
||||
if (conditionResult) {
|
||||
// 满足输出条件
|
||||
process.times().incrementAndGet();
|
||||
// TODO: concurrency issues for process.write
|
||||
process.appendResult(threadBoundEntity.get().getModel());
|
||||
|
||||
// 是否到达数量限制
|
||||
if (isLimitExceeded(command.getNumberOfLimit(), process.times().get())) {
|
||||
// TODO: concurrency issue to abort process
|
||||
abortProcess(process, command.getNumberOfLimit());
|
||||
} else {
|
||||
process.times().incrementAndGet();
|
||||
// TODO: concurrency issues for process.write
|
||||
process.write(threadBoundEntity.get().view.draw() + "\n");
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.warn("trace failed.", e);
|
||||
process.write("trace failed, condition is: " + command.getConditionExpress() + ", " + e.getMessage()
|
||||
+ ", visit " + LogUtil.loggingFile() + " for more details.\n");
|
||||
process.end();
|
||||
process.end(1, "trace failed, condition is: " + command.getConditionExpress() + ", " + e.getMessage()
|
||||
+ ", visit " + LogUtil.loggingFile() + " for more details.");
|
||||
} finally {
|
||||
threadBoundEntity.remove();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.taobao.arthas.core.advisor.AdviceListener;
|
||||
import com.taobao.arthas.core.advisor.AdviceWeaver;
|
||||
import com.taobao.arthas.core.advisor.Enhancer;
|
||||
import com.taobao.arthas.core.advisor.InvokeTraceable;
|
||||
import com.taobao.arthas.core.command.model.EnhancerModel;
|
||||
import com.taobao.arthas.core.shell.cli.Completion;
|
||||
import com.taobao.arthas.core.shell.cli.CompletionUtils;
|
||||
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
|
||||
@@ -120,16 +121,21 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
|
||||
protected void enhance(CommandProcess process) {
|
||||
Session session = process.session();
|
||||
if (!session.tryLock()) {
|
||||
process.write("someone else is enhancing classes, pls. wait.\n");
|
||||
process.end();
|
||||
String msg = "someone else is enhancing classes, pls. wait.";
|
||||
process.appendResult(new EnhancerModel(null, false, msg));
|
||||
process.end(-1, msg);
|
||||
return;
|
||||
}
|
||||
EnhancerAffect effect = null;
|
||||
int lock = session.getLock();
|
||||
try {
|
||||
Instrumentation inst = session.getInstrumentation();
|
||||
AdviceListener listener = getAdviceListenerWithId(process);
|
||||
if (listener == null) {
|
||||
warn(process, "advice listener is null");
|
||||
logger.error("advice listener is null");
|
||||
String msg = "advice listener is null, check arthas log";
|
||||
process.appendResult(new EnhancerModel(effect, false, msg));
|
||||
process.end(-1, msg);
|
||||
return;
|
||||
}
|
||||
boolean skipJDKTrace = false;
|
||||
@@ -140,24 +146,25 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
|
||||
Enhancer enhancer = new Enhancer(listener, listener instanceof InvokeTraceable, skipJDKTrace, getClassNameMatcher(), getMethodNameMatcher());
|
||||
// 注册通知监听器
|
||||
process.register(listener, enhancer);
|
||||
EnhancerAffect effect = enhancer.enhance(inst);
|
||||
effect = enhancer.enhance(inst);
|
||||
|
||||
if (effect.getThrowable() != null) {
|
||||
process.write(effect.toString() + ", check arthas log: " + LogUtil.loggingFile() + "\n");
|
||||
process.end(1);
|
||||
String msg = "error happens when enhancing class: "+effect.getThrowable().getMessage();
|
||||
process.appendResult(new EnhancerModel(effect, false, msg));
|
||||
process.end(1, msg + ", check arthas log: " + LogUtil.loggingFile());
|
||||
return;
|
||||
}
|
||||
|
||||
if (effect.cCnt() == 0 || effect.mCnt() == 0) {
|
||||
// no class effected
|
||||
// might be method code too large
|
||||
process.write("Matched class count: " + effect.cCnt() + ", method count: " + effect.mCnt() + "\n");
|
||||
process.write("No class or method is affected, try:\n"
|
||||
+ "1. sm CLASS_NAME METHOD_NAME to make sure the method you are tracing actually exists (it might be in your parent class).\n"
|
||||
+ "2. reset CLASS_NAME and try again, your method body might be too large.\n"
|
||||
+ "3. check arthas log: " + LogUtil.loggingFile() + "\n"
|
||||
+ "4. visit https://github.com/alibaba/arthas/issues/47 for more details.\n");
|
||||
process.end();
|
||||
process.appendResult(new EnhancerModel(effect, false, "No class or method is affected"));
|
||||
String msg = "No class or method is affected, try:\n"
|
||||
+ "1. sm CLASS_NAME METHOD_NAME to make sure the method you are tracing actually exists (it might be in your parent class).\n"
|
||||
+ "2. reset CLASS_NAME and try again, your method body might be too large.\n"
|
||||
+ "3. check arthas log: " + LogUtil.loggingFile() + "\n"
|
||||
+ "4. visit https://github.com/alibaba/arthas/issues/47 for more details.";
|
||||
process.end(-1, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -168,9 +175,14 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
|
||||
}
|
||||
}
|
||||
|
||||
process.write(effect + "\n");
|
||||
} catch (UnmodifiableClassException e) {
|
||||
logger.error("error happens when enhancing class", e);
|
||||
process.appendResult(new EnhancerModel(effect, true));
|
||||
|
||||
//异步执行,在AdviceListener中结束
|
||||
} catch (Throwable e) {
|
||||
String msg = "error happens when enhancing class: "+e.getMessage();
|
||||
logger.error(msg, e);
|
||||
process.appendResult(new EnhancerModel(effect, false, msg));
|
||||
process.end(-1, msg);
|
||||
} finally {
|
||||
if (session.getLock() == lock) {
|
||||
// enhance结束后解锁
|
||||
@@ -182,13 +194,4 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
|
||||
protected void completeArgument3(Completion completion) {
|
||||
super.complete(completion);
|
||||
}
|
||||
|
||||
private static void warn(CommandProcess process, String message) {
|
||||
logger.error(message);
|
||||
process.write("cannot operate the current command, pls. check arthas.log\n");
|
||||
if (process.isForeground()) {
|
||||
process.echoTips(Constants.Q_OR_CTRL_C_ABORT_MSG + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+20
-94
@@ -1,16 +1,13 @@
|
||||
package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
import com.taobao.arthas.core.advisor.AdviceListenerAdapter;
|
||||
import com.taobao.arthas.core.command.model.MonitorModel;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.advisor.ArthasMethod;
|
||||
import com.taobao.arthas.core.util.ThreadLocalWatch;
|
||||
import com.taobao.text.Decoration;
|
||||
import com.taobao.text.ui.TableElement;
|
||||
import com.taobao.text.util.RenderUtil;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@@ -18,7 +15,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.taobao.arthas.core.util.ArthasCheckUtils.isEquals;
|
||||
import static com.taobao.text.ui.Element.label;
|
||||
|
||||
/**
|
||||
* 输出的内容格式为:<br/>
|
||||
@@ -70,7 +66,7 @@ class MonitorAdviceListener extends AdviceListenerAdapter {
|
||||
// 输出定时任务
|
||||
private Timer timer;
|
||||
// 监控数据
|
||||
private ConcurrentHashMap<Key, AtomicReference<Data>> monitorData = new ConcurrentHashMap<Key, AtomicReference<Data>>();
|
||||
private ConcurrentHashMap<Key, AtomicReference<MonitorData>> monitorData = new ConcurrentHashMap<Key, AtomicReference<MonitorData>>();
|
||||
private final ThreadLocalWatch threadLocalWatch = new ThreadLocalWatch();
|
||||
private MonitorCommand command;
|
||||
private CommandProcess process;
|
||||
@@ -121,15 +117,15 @@ class MonitorAdviceListener extends AdviceListenerAdapter {
|
||||
final Key key = new Key(clazz.getName(), method.getName());
|
||||
|
||||
while (true) {
|
||||
AtomicReference<Data> value = monitorData.get(key);
|
||||
AtomicReference<MonitorData> value = monitorData.get(key);
|
||||
if (null == value) {
|
||||
monitorData.putIfAbsent(key, new AtomicReference<Data>(new Data()));
|
||||
monitorData.putIfAbsent(key, new AtomicReference<MonitorData>(new MonitorData()));
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
Data oData = value.get();
|
||||
Data nData = new Data();
|
||||
MonitorData oData = value.get();
|
||||
MonitorData nData = new MonitorData();
|
||||
nData.setCost(oData.getCost() + cost);
|
||||
if (isThrowing) {
|
||||
nData.setFailed(oData.getFailed() + 1);
|
||||
@@ -148,11 +144,11 @@ class MonitorAdviceListener extends AdviceListenerAdapter {
|
||||
}
|
||||
|
||||
private class MonitorTimer extends TimerTask {
|
||||
private Map<Key, AtomicReference<Data>> monitorData;
|
||||
private Map<Key, AtomicReference<MonitorData>> monitorData;
|
||||
private CommandProcess process;
|
||||
private int limit;
|
||||
|
||||
MonitorTimer(Map<Key, AtomicReference<Data>> monitorData, CommandProcess process, int limit) {
|
||||
MonitorTimer(Map<Key, AtomicReference<MonitorData>> monitorData, CommandProcess process, int limit) {
|
||||
this.monitorData = monitorData;
|
||||
this.process = process;
|
||||
this.limit = limit;
|
||||
@@ -170,53 +166,26 @@ class MonitorAdviceListener extends AdviceListenerAdapter {
|
||||
return;
|
||||
}
|
||||
|
||||
TableElement table = new TableElement(2, 3, 3, 1, 1, 1, 1, 1).leftCellPadding(1).rightCellPadding(1);
|
||||
table.row(true, label("timestamp").style(Decoration.bold.bold()),
|
||||
label("class").style(Decoration.bold.bold()),
|
||||
label("method").style(Decoration.bold.bold()),
|
||||
label("total").style(Decoration.bold.bold()),
|
||||
label("success").style(Decoration.bold.bold()),
|
||||
label("fail").style(Decoration.bold.bold()),
|
||||
label("avg-rt(ms)").style(Decoration.bold.bold()),
|
||||
label("fail-rate").style(Decoration.bold.bold()));
|
||||
List<MonitorData> monitorDataList = new ArrayList<MonitorData>(monitorData.size());
|
||||
for (Map.Entry<Key, AtomicReference<MonitorData>> entry : monitorData.entrySet()) {
|
||||
final AtomicReference<MonitorData> value = entry.getValue();
|
||||
|
||||
for (Map.Entry<Key, AtomicReference<Data>> entry : monitorData.entrySet()) {
|
||||
final AtomicReference<Data> value = entry.getValue();
|
||||
|
||||
Data data;
|
||||
MonitorData data;
|
||||
while (true) {
|
||||
data = value.get();
|
||||
if (value.compareAndSet(data, new Data())) {
|
||||
//swap monitor data to new instance
|
||||
if (value.compareAndSet(data, new MonitorData())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (null != data) {
|
||||
|
||||
final DecimalFormat df = new DecimalFormat("0.00");
|
||||
|
||||
table.row(
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),
|
||||
entry.getKey().getClassName(),
|
||||
entry.getKey().getMethodName(),
|
||||
"" + data.getTotal(),
|
||||
"" + data.getSuccess(),
|
||||
"" + data.getFailed(),
|
||||
df.format(div(data.getCost(), data.getTotal())),
|
||||
df.format(100.0d * div(data.getFailed(), data.getTotal())) + "%"
|
||||
);
|
||||
|
||||
data.setClassName(entry.getKey().getClassName());
|
||||
data.setMethodName(entry.getKey().getMethodName());
|
||||
monitorDataList.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
process.write(RenderUtil.render(table, process.width()) + "\n");
|
||||
}
|
||||
|
||||
private double div(double a, double b) {
|
||||
if (b == 0) {
|
||||
return 0;
|
||||
}
|
||||
return a / b;
|
||||
process.appendResult(new MonitorModel(monitorDataList));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -260,47 +229,4 @@ class MonitorAdviceListener extends AdviceListenerAdapter {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据监控用的value
|
||||
*
|
||||
* @author vlinux
|
||||
*/
|
||||
private static class Data {
|
||||
private int total;
|
||||
private int success;
|
||||
private int failed;
|
||||
private double cost;
|
||||
|
||||
public int getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(int total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public int getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(int success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public int getFailed() {
|
||||
return failed;
|
||||
}
|
||||
|
||||
public void setFailed(int failed) {
|
||||
this.failed = failed;
|
||||
}
|
||||
|
||||
public double getCost() {
|
||||
return cost;
|
||||
}
|
||||
|
||||
public void setCost(double cost) {
|
||||
this.cost = cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
/**
|
||||
* 数据监控用的value for MonitorCommand
|
||||
*
|
||||
* @author vlinux
|
||||
*/
|
||||
public class MonitorData {
|
||||
private String className;
|
||||
private String methodName;
|
||||
private int total;
|
||||
private int success;
|
||||
private int failed;
|
||||
private double cost;
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public void setClassName(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public void setMethodName(String methodName) {
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
public int getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(int total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public int getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(int success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public int getFailed() {
|
||||
return failed;
|
||||
}
|
||||
|
||||
public void setFailed(int failed) {
|
||||
this.failed = failed;
|
||||
}
|
||||
|
||||
public double getCost() {
|
||||
return cost;
|
||||
}
|
||||
|
||||
public void setCost(double cost) {
|
||||
this.cost = cost;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import com.alibaba.arthas.deps.org.slf4j.Logger;
|
||||
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
|
||||
import com.taobao.arthas.common.OSUtils;
|
||||
import com.taobao.arthas.core.command.Constants;
|
||||
import com.taobao.arthas.core.command.model.ProfilerModel;
|
||||
import com.taobao.arthas.core.server.ArthasBootstrap;
|
||||
import com.taobao.arthas.core.shell.cli.CliToken;
|
||||
import com.taobao.arthas.core.shell.cli.Completion;
|
||||
@@ -221,7 +222,7 @@ public class ProfilerCommand extends AnnotatedCommand {
|
||||
* https://github.com/jvm-profiling-tools/async-profiler/blob/v1.6/src/arguments.cpp#L34
|
||||
*
|
||||
*/
|
||||
enum ProfilerAction {
|
||||
public enum ProfilerAction {
|
||||
execute, start, stop, resume, list, version, status, load,
|
||||
|
||||
dumpCollapsed, dumpFlat, dumpTraces, getSamples,
|
||||
@@ -270,12 +271,11 @@ public class ProfilerCommand extends AnnotatedCommand {
|
||||
|
||||
@Override
|
||||
public void process(final CommandProcess process) {
|
||||
int status = 0;
|
||||
try {
|
||||
ProfilerAction profilerAction = ProfilerAction.valueOf(action);
|
||||
|
||||
if (ProfilerAction.actions.equals(profilerAction)) {
|
||||
process.write("Supported Actions: " + actions() + "\n");
|
||||
process.appendResult(new ProfilerModel(actions()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -283,54 +283,54 @@ public class ProfilerCommand extends AnnotatedCommand {
|
||||
|
||||
if (ProfilerAction.execute.equals(profilerAction)) {
|
||||
if (actionArg == null) {
|
||||
process.write("actionArg can not be empty.\n");
|
||||
status = 1;
|
||||
process.end(1, "actionArg can not be empty.");
|
||||
return;
|
||||
}
|
||||
String result = execute(asyncProfiler, this.actionArg);
|
||||
process.write(result);
|
||||
appendExecuteResult(process, result);
|
||||
} else if (ProfilerAction.start.equals(profilerAction)) {
|
||||
String executeArgs = executeArgs(ProfilerAction.start);
|
||||
String result = execute(asyncProfiler, executeArgs);
|
||||
process.write(result);
|
||||
ProfilerModel profilerModel = createProfilerModel(result);
|
||||
|
||||
if (this.duration != null) {
|
||||
final String outputFile = outputFile();
|
||||
final String stopExecuteArgs = executeArgs(ProfilerAction.stop);
|
||||
process.write(String.format("profiler will silent stop after %d seconds.\n", this.duration.longValue()));
|
||||
process.write("profiler output file will be: " + new File(outputFile).getAbsolutePath() + "\n");
|
||||
profilerModel.setOutputFile(outputFile);
|
||||
profilerModel.setDuration(duration);
|
||||
|
||||
// 延时执行stop
|
||||
ArthasBootstrap.getInstance().getScheduledExecutorService().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//在异步线程执行,profiler命令已经结束,不能输出到客户端
|
||||
try {
|
||||
logger.info("profiler output file: " + new File(outputFile).getAbsolutePath() + "\n");
|
||||
String result = execute(asyncProfiler, stopExecuteArgs);
|
||||
logger.info("profiler stop result: " + result);
|
||||
logger.info("stopping profiler ...");
|
||||
ProfilerModel model = processStop(asyncProfiler);
|
||||
logger.info("profiler output file: " + model.getOutputFile());
|
||||
logger.info("stop profiler successfully.");
|
||||
} catch (Throwable e) {
|
||||
logger.error("", e);
|
||||
logger.error("stop profiler failure", e);
|
||||
}
|
||||
}
|
||||
}, this.duration, TimeUnit.SECONDS);
|
||||
}
|
||||
process.appendResult(profilerModel);
|
||||
} else if (ProfilerAction.stop.equals(profilerAction)) {
|
||||
String outputFile = outputFile();
|
||||
process.write("profiler output file: " + new File(outputFile).getAbsolutePath() + "\n");
|
||||
String executeArgs = executeArgs(ProfilerAction.stop);
|
||||
String result = execute(asyncProfiler, executeArgs);
|
||||
process.write(result);
|
||||
ProfilerModel profilerModel = processStop(asyncProfiler);
|
||||
process.appendResult(profilerModel);
|
||||
} else if (ProfilerAction.resume.equals(profilerAction)) {
|
||||
String executeArgs = executeArgs(ProfilerAction.resume);
|
||||
String result = execute(asyncProfiler, executeArgs);
|
||||
process.write(result);
|
||||
appendExecuteResult(process, result);
|
||||
} else if (ProfilerAction.list.equals(profilerAction)) {
|
||||
String result = asyncProfiler.execute("list");
|
||||
process.write(result);
|
||||
appendExecuteResult(process, result);
|
||||
} else if (ProfilerAction.version.equals(profilerAction)) {
|
||||
String result = asyncProfiler.execute("version");
|
||||
process.write(result);
|
||||
appendExecuteResult(process, result);
|
||||
} else if (ProfilerAction.status.equals(profilerAction)) {
|
||||
String result = asyncProfiler.execute("status");
|
||||
process.write(result);
|
||||
appendExecuteResult(process, result);
|
||||
} else if (ProfilerAction.dumpCollapsed.equals(profilerAction)) {
|
||||
if (actionArg == null) {
|
||||
actionArg = "TOTAL";
|
||||
@@ -338,10 +338,10 @@ public class ProfilerCommand extends AnnotatedCommand {
|
||||
actionArg = actionArg.toUpperCase();
|
||||
if ("TOTAL".equals(actionArg) || "SAMPLES".equals(actionArg)) {
|
||||
String result = asyncProfiler.dumpCollapsed(Counter.valueOf(actionArg));
|
||||
process.write(result);
|
||||
appendExecuteResult(process, result);
|
||||
} else {
|
||||
process.write("ERROR: dumpCollapsed argumment should be TOTAL or SAMPLES. \n");
|
||||
status = 1;
|
||||
process.end(1, "ERROR: dumpCollapsed argumment should be TOTAL or SAMPLES. ");
|
||||
return;
|
||||
}
|
||||
} else if (ProfilerAction.dumpFlat.equals(profilerAction)) {
|
||||
int maxMethods = 0;
|
||||
@@ -349,27 +349,35 @@ public class ProfilerCommand extends AnnotatedCommand {
|
||||
maxMethods = Integer.valueOf(actionArg);
|
||||
}
|
||||
String result = asyncProfiler.dumpFlat(maxMethods);
|
||||
process.write(result);
|
||||
appendExecuteResult(process, result);
|
||||
} else if (ProfilerAction.dumpTraces.equals(profilerAction)) {
|
||||
int maxTraces = 0;
|
||||
if (actionArg != null) {
|
||||
maxTraces = Integer.valueOf(actionArg);
|
||||
}
|
||||
String result = asyncProfiler.dumpTraces(maxTraces);
|
||||
process.write(result);
|
||||
appendExecuteResult(process, result);
|
||||
} else if (ProfilerAction.getSamples.equals(profilerAction)) {
|
||||
String result = "" + asyncProfiler.getSamples() + "\n";
|
||||
process.write(result);
|
||||
appendExecuteResult(process, result);
|
||||
}
|
||||
process.end();
|
||||
} catch (Throwable e) {
|
||||
process.write(e.getMessage()).write("\n");
|
||||
logger.error("AsyncProfiler error", e);
|
||||
status = 1;
|
||||
} finally {
|
||||
process.end(status);
|
||||
process.end(1, "AsyncProfiler error: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private ProfilerModel processStop(AsyncProfiler asyncProfiler) throws IOException {
|
||||
String outputFile = outputFile();
|
||||
String executeArgs = executeArgs(ProfilerAction.stop);
|
||||
String result = execute(asyncProfiler, executeArgs);
|
||||
|
||||
ProfilerModel profilerModel = createProfilerModel(result);
|
||||
profilerModel.setOutputFile(outputFile);
|
||||
return profilerModel;
|
||||
}
|
||||
|
||||
private String outputFile() {
|
||||
if (this.file == null) {
|
||||
this.file = new File("arthas-output",
|
||||
@@ -378,6 +386,19 @@ public class ProfilerCommand extends AnnotatedCommand {
|
||||
return file;
|
||||
}
|
||||
|
||||
private void appendExecuteResult(CommandProcess process, String result) {
|
||||
ProfilerModel profilerModel = createProfilerModel(result);
|
||||
process.appendResult(profilerModel);
|
||||
}
|
||||
|
||||
private ProfilerModel createProfilerModel(String result) {
|
||||
ProfilerModel profilerModel = new ProfilerModel();
|
||||
profilerModel.setAction(action);
|
||||
profilerModel.setActionArg(actionArg);
|
||||
profilerModel.setExecuteResult(result);
|
||||
return profilerModel;
|
||||
}
|
||||
|
||||
private List<String> events() {
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
|
||||
+11
-7
@@ -1,23 +1,25 @@
|
||||
package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
import com.taobao.arthas.core.advisor.AdviceListenerAdapter;
|
||||
import com.taobao.arthas.core.command.model.StackModel;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.alibaba.arthas.deps.org.slf4j.Logger;
|
||||
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
|
||||
import com.taobao.arthas.core.advisor.Advice;
|
||||
import com.taobao.arthas.core.advisor.ArthasMethod;
|
||||
import com.taobao.arthas.core.util.DateUtils;
|
||||
import com.taobao.arthas.core.util.LogUtil;
|
||||
import com.taobao.arthas.core.util.ThreadLocalWatch;
|
||||
import com.taobao.arthas.core.util.ThreadUtil;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author beiwei30 on 29/11/2016.
|
||||
*/
|
||||
public class StackAdviceListener extends AdviceListenerAdapter {
|
||||
private static final Logger logger = LoggerFactory.getLogger(StackAdviceListener.class);
|
||||
|
||||
private final ThreadLocal<String> stackThreadLocal = new ThreadLocal<String>();
|
||||
private final ThreadLocal<StackModel> stackThreadLocal = new ThreadLocal<StackModel>();
|
||||
private final ThreadLocalWatch threadLocalWatch = new ThreadLocalWatch();
|
||||
private StackCommand command;
|
||||
private CommandProcess process;
|
||||
@@ -31,7 +33,7 @@ public class StackAdviceListener extends AdviceListenerAdapter {
|
||||
@Override
|
||||
public void before(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args)
|
||||
throws Throwable {
|
||||
stackThreadLocal.set(ThreadUtil.getThreadStack(Thread.currentThread()));
|
||||
stackThreadLocal.set(ThreadUtil.getThreadStackModel(Thread.currentThread()));
|
||||
// 开始计算本次方法调用耗时
|
||||
threadLocalWatch.start();
|
||||
}
|
||||
@@ -60,7 +62,10 @@ public class StackAdviceListener extends AdviceListenerAdapter {
|
||||
}
|
||||
if (conditionResult) {
|
||||
// TODO: concurrency issues for process.write
|
||||
process.write("ts=" + DateUtils.getCurrentDate() + ";" + stackThreadLocal.get() + "\n");
|
||||
// TODO: should clear stackThreadLocal?
|
||||
StackModel stackModel = stackThreadLocal.get();
|
||||
stackModel.setTs(new Date());
|
||||
process.appendResult(stackModel);
|
||||
process.times().incrementAndGet();
|
||||
if (isLimitExceeded(command.getNumberOfLimit(), process.times().get())) {
|
||||
abortProcess(process, command.getNumberOfLimit());
|
||||
@@ -68,9 +73,8 @@ public class StackAdviceListener extends AdviceListenerAdapter {
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.warn("stack failed.", e);
|
||||
process.write("stack failed, condition is: " + command.getConditionExpress() + ", " + e.getMessage()
|
||||
+ ", visit " + LogUtil.loggingFile() + " for more details.\n");
|
||||
process.end();
|
||||
process.end(-1, "stack failed, condition is: " + command.getConditionExpress() + ", " + e.getMessage()
|
||||
+ ", visit " + LogUtil.loggingFile() + " for more details.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-21
@@ -1,22 +1,19 @@
|
||||
package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
import static com.taobao.arthas.core.command.monitor200.TimeTunnelTable.createTable;
|
||||
import static com.taobao.arthas.core.command.monitor200.TimeTunnelTable.fillTableHeader;
|
||||
import static com.taobao.arthas.core.command.monitor200.TimeTunnelTable.fillTableRow;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.alibaba.arthas.deps.org.slf4j.Logger;
|
||||
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
|
||||
import com.taobao.arthas.core.advisor.Advice;
|
||||
import com.taobao.arthas.core.advisor.ArthasMethod;
|
||||
import com.taobao.arthas.core.advisor.AdviceListenerAdapter;
|
||||
import com.taobao.arthas.core.advisor.ArthasMethod;
|
||||
import com.taobao.arthas.core.command.express.ExpressException;
|
||||
import com.taobao.arthas.core.command.model.TimeFragmentVO;
|
||||
import com.taobao.arthas.core.command.model.TimeTunnelModel;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.util.LogUtil;
|
||||
import com.taobao.arthas.core.util.ThreadLocalWatch;
|
||||
import com.taobao.text.ui.TableElement;
|
||||
import com.taobao.text.util.RenderUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author beiwei30 on 30/11/2016.
|
||||
@@ -81,9 +78,8 @@ public class TimeTunnelAdviceListener extends AdviceListenerAdapter {
|
||||
}
|
||||
} catch (ExpressException e) {
|
||||
logger.warn("tt failed.", e);
|
||||
process.write("tt failed, condition is: " + command.getConditionExpress() + ", " + e.getMessage()
|
||||
+ ", visit " + LogUtil.loggingFile() + " for more details.\n");
|
||||
process.end();
|
||||
process.end(-1, "tt failed, condition is: " + command.getConditionExpress() + ", " + e.getMessage()
|
||||
+ ", visit " + LogUtil.loggingFile() + " for more details.");
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
@@ -91,20 +87,17 @@ public class TimeTunnelAdviceListener extends AdviceListenerAdapter {
|
||||
}
|
||||
|
||||
int index = command.putTimeTunnel(timeTunnel);
|
||||
TableElement table = createTable();
|
||||
|
||||
TimeFragmentVO timeFragmentVO = TimeTunnelCommand.createTimeFragmentVO(index, timeTunnel);
|
||||
TimeTunnelModel timeTunnelModel = new TimeTunnelModel()
|
||||
.setTimeFragmentList(Arrays.asList(timeFragmentVO))
|
||||
.setFirst(isFirst);
|
||||
process.appendResult(timeTunnelModel);
|
||||
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
|
||||
// 填充表格头部
|
||||
fillTableHeader(table);
|
||||
}
|
||||
|
||||
// 填充表格内容
|
||||
fillTableRow(table, index, timeTunnel);
|
||||
|
||||
// TODO: concurrency issues for process.write
|
||||
process.write(RenderUtil.render(table, process.width()));
|
||||
process.times().incrementAndGet();
|
||||
if (isLimitExceeded(command.getNumberOfLimit(), process.times().get())) {
|
||||
abortProcess(process, command.getNumberOfLimit());
|
||||
|
||||
+106
-66
@@ -9,6 +9,10 @@ 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.command.model.MessageModel;
|
||||
import com.taobao.arthas.core.command.model.RowAffectModel;
|
||||
import com.taobao.arthas.core.command.model.TimeFragmentVO;
|
||||
import com.taobao.arthas.core.command.model.TimeTunnelModel;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.shell.handlers.command.CommandInterruptHandler;
|
||||
import com.taobao.arthas.core.shell.handlers.shell.QExitHandler;
|
||||
@@ -17,16 +21,16 @@ import com.taobao.arthas.core.util.SearchUtils;
|
||||
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.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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@@ -49,6 +53,7 @@ import static java.lang.String.format;
|
||||
" tt -i 1000 -w params[0]\n" +
|
||||
" tt -i 1000 -p \n" +
|
||||
" tt -i 1000 -p --replay-times 3 --replay-interval 3000\n" +
|
||||
" tt -s '{params[0] > 1}' -w '{params}' \n" +
|
||||
" tt --delete-all\n" +
|
||||
Constants.WIKI + Constants.WIKI_HOME + "tt")
|
||||
public class TimeTunnelCommand extends EnhancerCommand {
|
||||
@@ -226,10 +231,6 @@ public class TimeTunnelCommand extends EnhancerCommand {
|
||||
return !StringUtils.isEmpty(searchExpress);
|
||||
}
|
||||
|
||||
private boolean isNeedExpand() {
|
||||
return null != expand && expand > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查参数是否合法
|
||||
*/
|
||||
@@ -323,27 +324,22 @@ public class TimeTunnelCommand extends EnhancerCommand {
|
||||
try {
|
||||
TimeFragment tf = timeFragmentMap.get(index);
|
||||
if (null == tf) {
|
||||
process.write(format("Time fragment[%d] does not exist.", index)).write("\n");
|
||||
process.end(1, format("Time fragment[%d] does not exist.", index));
|
||||
return;
|
||||
}
|
||||
|
||||
Advice advice = tf.getAdvice();
|
||||
String className = advice.getClazz().getName();
|
||||
String methodName = advice.getMethod().getName();
|
||||
String objectAddress = advice.getTarget() == null ? "NULL" : "0x" + toHexString(advice.getTarget().hashCode());
|
||||
|
||||
TableElement table = TimeTunnelTable.createDefaultTable();
|
||||
TimeTunnelTable.drawTimeTunnel(tf, index, table);
|
||||
TimeTunnelTable.drawMethod(advice, className, methodName, objectAddress, table);
|
||||
TimeTunnelTable.drawParameters(advice, table, isNeedExpand(), expand);
|
||||
TimeTunnelTable.drawReturnObj(advice, table, isNeedExpand(), expand, sizeLimit);
|
||||
TimeTunnelTable.drawThrowException(advice, table, isNeedExpand(), expand);
|
||||
|
||||
process.write(RenderUtil.render(table, process.width()));
|
||||
TimeFragmentVO timeFragmentVO = createTimeFragmentVO(index, tf);
|
||||
TimeTunnelModel timeTunnelModel = new TimeTunnelModel()
|
||||
.setTimeFragment(timeFragmentVO)
|
||||
.setExpand(expand)
|
||||
.setSizeLimit(sizeLimit);
|
||||
process.appendResult(timeTunnelModel);
|
||||
affect.rCnt(1);
|
||||
} finally {
|
||||
process.write(affect.toString()).write("\n");
|
||||
process.appendResult(new RowAffectModel(affect));
|
||||
process.end();
|
||||
} catch (Throwable e) {
|
||||
logger.warn("tt failed.", e);
|
||||
process.end(1, e.getMessage() + ", visit " + LogUtil.loggingFile() + " for more detail");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,25 +349,24 @@ public class TimeTunnelCommand extends EnhancerCommand {
|
||||
try {
|
||||
final TimeFragment tf = timeFragmentMap.get(index);
|
||||
if (null == tf) {
|
||||
process.write(format("Time fragment[%d] does not exist.", index)).write("\n");
|
||||
process.end(1, format("Time fragment[%d] does not exist.", index));
|
||||
return;
|
||||
}
|
||||
|
||||
Advice advice = tf.getAdvice();
|
||||
Object value = ExpressFactory.threadLocalExpress(advice).get(watchExpress);
|
||||
if (isNeedExpand()) {
|
||||
process.write(new ObjectView(value, expand, sizeLimit).draw()).write("\n");
|
||||
} else {
|
||||
process.write(StringUtils.objectToString(value)).write("\n");
|
||||
}
|
||||
TimeTunnelModel timeTunnelModel = new TimeTunnelModel()
|
||||
.setWatchValue(value)
|
||||
.setExpand(expand)
|
||||
.setSizeLimit(sizeLimit);
|
||||
process.appendResult(timeTunnelModel);
|
||||
|
||||
affect.rCnt(1);
|
||||
process.appendResult(new RowAffectModel(affect));
|
||||
process.end();
|
||||
} catch (ExpressException e) {
|
||||
logger.warn("tt failed.", e);
|
||||
process.write(e.getMessage() + ", visit " + LogUtil.loggingFile() + " for more detail\n");
|
||||
} finally {
|
||||
process.write(affect.toString()).write("\n");
|
||||
process.end();
|
||||
process.end(1, e.getMessage() + ", visit " + LogUtil.loggingFile() + " for more detail");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,22 +389,29 @@ public class TimeTunnelCommand extends EnhancerCommand {
|
||||
|
||||
if (hasWatchExpress()) {
|
||||
// 执行watchExpress
|
||||
TableElement table = TimeTunnelTable.createDefaultTable();
|
||||
TimeTunnelTable.drawWatchTableHeader(table);
|
||||
TimeTunnelTable.drawWatchExpress(matchingTimeSegmentMap, table, watchExpress, isNeedExpand(), expand, sizeLimit);
|
||||
process.write(RenderUtil.render(table, process.width()));
|
||||
Map<Integer, Object> searchResults = new LinkedHashMap<Integer, Object>();
|
||||
for (Map.Entry<Integer, TimeFragment> entry : matchingTimeSegmentMap.entrySet()) {
|
||||
Object value = ExpressFactory.threadLocalExpress(entry.getValue().getAdvice()).get(watchExpress);
|
||||
searchResults.put(entry.getKey(), value);
|
||||
}
|
||||
|
||||
TimeTunnelModel timeTunnelModel = new TimeTunnelModel()
|
||||
.setWatchResults(searchResults)
|
||||
.setExpand(expand)
|
||||
.setSizeLimit(sizeLimit);
|
||||
process.appendResult(timeTunnelModel);
|
||||
} else {
|
||||
// 单纯的列表格
|
||||
process.write(RenderUtil.render(TimeTunnelTable.drawTimeTunnelTable(matchingTimeSegmentMap), process.width()));
|
||||
List<TimeFragmentVO> timeFragmentList = createTimeTunnelVOList(matchingTimeSegmentMap);
|
||||
process.appendResult(new TimeTunnelModel().setTimeFragmentList(timeFragmentList).setFirst(true));
|
||||
}
|
||||
|
||||
affect.rCnt(matchingTimeSegmentMap.size());
|
||||
process.appendResult(new RowAffectModel(affect));
|
||||
process.end();
|
||||
} catch (ExpressException e) {
|
||||
logger.warn("tt failed.", e);
|
||||
process.write(e.getMessage() + ", visit " + LogUtil.loggingFile() + " for more detail\n");
|
||||
} finally {
|
||||
process.write(affect.toString()).write("\n");
|
||||
process.end();
|
||||
process.end(1, e.getMessage() + ", visit " + LogUtil.loggingFile() + " for more detail");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,8 +421,8 @@ public class TimeTunnelCommand extends EnhancerCommand {
|
||||
if (timeFragmentMap.remove(index) != null) {
|
||||
affect.rCnt(1);
|
||||
}
|
||||
process.write(format("Time fragment[%d] successfully deleted.", index)).write("\n");
|
||||
process.write(affect.toString()).write("\n");
|
||||
process.appendResult(new MessageModel(format("Time fragment[%d] successfully deleted.", index)));
|
||||
process.appendResult(new RowAffectModel(affect));
|
||||
process.end();
|
||||
}
|
||||
|
||||
@@ -428,33 +430,57 @@ public class TimeTunnelCommand extends EnhancerCommand {
|
||||
int count = timeFragmentMap.size();
|
||||
RowAffect affect = new RowAffect(count);
|
||||
timeFragmentMap.clear();
|
||||
process.write("Time fragments are cleaned.\n");
|
||||
process.write(affect.toString()).write("\n");
|
||||
process.appendResult(new MessageModel("Time fragments are cleaned."));
|
||||
process.appendResult(new RowAffectModel(affect));
|
||||
process.end();
|
||||
}
|
||||
|
||||
private void processList(CommandProcess process) {
|
||||
RowAffect affect = new RowAffect();
|
||||
process.write(RenderUtil.render(TimeTunnelTable.drawTimeTunnelTable(timeFragmentMap), process.width()));
|
||||
List<TimeFragmentVO> timeFragmentList = createTimeTunnelVOList(timeFragmentMap);
|
||||
process.appendResult(new TimeTunnelModel().setTimeFragmentList(timeFragmentList).setFirst(true));
|
||||
affect.rCnt(timeFragmentMap.size());
|
||||
process.write(affect.toString()).write("\n");
|
||||
process.appendResult(new RowAffectModel(affect));
|
||||
process.end();
|
||||
}
|
||||
|
||||
private List<TimeFragmentVO> createTimeTunnelVOList(Map<Integer, TimeFragment> timeFragmentMap) {
|
||||
List<TimeFragmentVO> timeFragmentList = new ArrayList<TimeFragmentVO>(timeFragmentMap.size());
|
||||
for (Map.Entry<Integer, TimeFragment> entry : timeFragmentMap.entrySet()) {
|
||||
timeFragmentList.add(createTimeFragmentVO(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
return timeFragmentList;
|
||||
}
|
||||
|
||||
public static TimeFragmentVO createTimeFragmentVO(Integer index, TimeFragment tf) {
|
||||
Advice advice = tf.getAdvice();
|
||||
String object = advice.getTarget() == null
|
||||
? "NULL"
|
||||
: "0x" + toHexString(advice.getTarget().hashCode());
|
||||
|
||||
return new TimeFragmentVO()
|
||||
.setIndex(index)
|
||||
.setTimestamp(tf.getGmtCreate())
|
||||
.setCost(tf.getCost())
|
||||
.setReturn(advice.isAfterReturning())
|
||||
.setReturnObj(advice.getReturnObj())
|
||||
.setThrow(advice.isAfterThrowing())
|
||||
.setThrowExp(advice.getThrowExp())
|
||||
.setObject(object)
|
||||
.setClassName(advice.getClazz().getName())
|
||||
.setMethodName(advice.getMethod().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 重放指定记录
|
||||
*/
|
||||
private void processPlay(CommandProcess process) {
|
||||
TimeFragment tf = timeFragmentMap.get(index);
|
||||
if (null == tf) {
|
||||
process.write(format("Time fragment[%d] does not exist.", index) + "\n");
|
||||
process.end();
|
||||
process.end(1, format("Time fragment[%d] does not exist.", index));
|
||||
return;
|
||||
}
|
||||
Advice advice = tf.getAdvice();
|
||||
String className = advice.getClazz().getName();
|
||||
String methodName = advice.getMethod().getName();
|
||||
String objectAddress = advice.getTarget() == null ? "NULL" : "0x" + toHexString(advice.getTarget().hashCode());
|
||||
ArthasMethod method = advice.getMethod();
|
||||
boolean accessible = advice.getMethod().isAccessible();
|
||||
try {
|
||||
@@ -470,30 +496,44 @@ public class TimeTunnelCommand extends EnhancerCommand {
|
||||
}
|
||||
}
|
||||
long beginTime = System.nanoTime();
|
||||
TableElement table = TimeTunnelTable.createDefaultTable();
|
||||
if (i != 0) {
|
||||
// empty line separator
|
||||
process.write("\n");
|
||||
}
|
||||
TimeTunnelTable.drawPlayHeader(className, methodName, objectAddress, index, table);
|
||||
TimeTunnelTable.drawParameters(advice, table, isNeedExpand(), expand);
|
||||
|
||||
//copy from tt record
|
||||
TimeFragmentVO replayResult = createTimeFragmentVO(index, tf);
|
||||
replayResult.setTimestamp(new Date())
|
||||
.setCost(0)
|
||||
.setReturn(false)
|
||||
.setReturnObj(null)
|
||||
.setThrow(false)
|
||||
.setThrowExp(null);
|
||||
|
||||
try {
|
||||
//execute successful
|
||||
Object returnObj = method.invoke(advice.getTarget(), advice.getParams());
|
||||
double cost = (System.nanoTime() - beginTime) / 1000000.0;
|
||||
TimeTunnelTable.drawPlayResult(table, returnObj, isNeedExpand(), expand, sizeLimit, cost);
|
||||
replayResult.setCost(cost)
|
||||
.setReturn(true)
|
||||
.setReturnObj(returnObj);
|
||||
} catch (Throwable t) {
|
||||
TimeTunnelTable.drawPlayException(table, t, isNeedExpand(), expand);
|
||||
//throw exp
|
||||
double cost = (System.nanoTime() - beginTime) / 1000000.0;
|
||||
replayResult.setCost(cost)
|
||||
.setThrow(true)
|
||||
.setThrowExp(t);
|
||||
}
|
||||
process.write(RenderUtil.render(table, process.width()))
|
||||
.write(format("Time fragment[%d] successfully replayed %d times.", index, i+1))
|
||||
.write("\n");
|
||||
|
||||
TimeTunnelModel timeTunnelModel = new TimeTunnelModel()
|
||||
.setReplayResult(replayResult)
|
||||
.setReplayNo(i + 1)
|
||||
.setExpand(expand)
|
||||
.setSizeLimit(sizeLimit);
|
||||
process.appendResult(timeTunnelModel);
|
||||
}
|
||||
process.end();
|
||||
} catch (Throwable t) {
|
||||
logger.warn("tt replay failed.", t);
|
||||
process.end(-1, "tt replay failed");
|
||||
} finally {
|
||||
method.setAccessible(accessible);
|
||||
process.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
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.command.model.TimeFragmentVO;
|
||||
import com.taobao.arthas.core.util.StringUtils;
|
||||
import com.taobao.arthas.core.view.ObjectView;
|
||||
import com.taobao.text.Decoration;
|
||||
@@ -15,10 +13,10 @@ import java.io.StringWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.taobao.text.ui.Element.label;
|
||||
import static java.lang.Integer.toHexString;
|
||||
|
||||
/**
|
||||
* @author beiwei30 on 30/11/2016.
|
||||
@@ -53,7 +51,7 @@ public class TimeTunnelTable {
|
||||
return new TableElement(TABLE_COL_WIDTH).leftCellPadding(1).rightCellPadding(1);
|
||||
}
|
||||
|
||||
static TableElement createDefaultTable() {
|
||||
public static TableElement createDefaultTable() {
|
||||
return new TableElement().leftCellPadding(1).rightCellPadding(1);
|
||||
}
|
||||
|
||||
@@ -67,54 +65,49 @@ public class TimeTunnelTable {
|
||||
}
|
||||
|
||||
// 绘制TimeTunnel表格
|
||||
static Element drawTimeTunnelTable(Map<Integer, TimeFragment> timeTunnelMap) {
|
||||
TableElement table = fillTableHeader(createTable());
|
||||
for (Map.Entry<Integer, TimeFragment> entry : timeTunnelMap.entrySet()) {
|
||||
final int index = entry.getKey();
|
||||
final TimeFragment tf = entry.getValue();
|
||||
fillTableRow(table, index, tf);
|
||||
public static Element drawTimeTunnelTable(List<TimeFragmentVO> timeFragmentList, boolean withHeader){
|
||||
TableElement table = createTable();
|
||||
if (withHeader) {
|
||||
fillTableHeader(table);
|
||||
}
|
||||
for (TimeFragmentVO tf : timeFragmentList) {
|
||||
fillTableRow(table, tf);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
// 填充表格行
|
||||
static TableElement fillTableRow(TableElement table, int index, TimeFragment tf) {
|
||||
Advice advice = tf.getAdvice();
|
||||
static TableElement fillTableRow(TableElement table, TimeFragmentVO tf) {
|
||||
return table.row(
|
||||
"" + index,
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tf.getGmtCreate()),
|
||||
"" + tf.getIndex(),
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tf.getTimestamp()),
|
||||
"" + tf.getCost(),
|
||||
"" + advice.isAfterReturning(),
|
||||
"" + advice.isAfterThrowing(),
|
||||
advice.getTarget() == null
|
||||
? "NULL"
|
||||
: "0x" + toHexString(advice.getTarget().hashCode()),
|
||||
StringUtils.substringAfterLast("." + advice.getClazz().getName(), "."),
|
||||
advice.getMethod().getName()
|
||||
"" + tf.isReturn(),
|
||||
"" + tf.isThrow(),
|
||||
tf.getObject(),
|
||||
StringUtils.substringAfterLast("." + tf.getClassName(), "."),
|
||||
tf.getMethodName()
|
||||
);
|
||||
}
|
||||
|
||||
static void drawTimeTunnel(TimeFragment tf, Integer index, TableElement table) {
|
||||
public static void drawTimeTunnel(TableElement table, TimeFragmentVO tf) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
table.row("INDEX", "" + index)
|
||||
.row("GMT-CREATE", sdf.format(tf.getGmtCreate()))
|
||||
.row("COST(ms)", "" + tf.getCost());
|
||||
table.row("INDEX", "" + tf.getIndex())
|
||||
.row("GMT-CREATE", sdf.format(tf.getTimestamp()))
|
||||
.row("COST(ms)", "" + tf.getCost())
|
||||
.row("OBJECT", tf.getObject())
|
||||
.row("CLASS", tf.getClassName())
|
||||
.row("METHOD", tf.getMethodName())
|
||||
.row("IS-RETURN", "" + tf.isReturn())
|
||||
.row("IS-EXCEPTION", "" + tf.isThrow());
|
||||
}
|
||||
|
||||
static void drawMethod(Advice advice, String className, String methodName, String objectAddress, TableElement table) {
|
||||
table.row("OBJECT", objectAddress)
|
||||
.row("CLASS", className)
|
||||
.row("METHOD", methodName)
|
||||
.row("IS-RETURN", "" + advice.isAfterReturning())
|
||||
.row("IS-EXCEPTION", "" + advice.isAfterThrowing());
|
||||
}
|
||||
|
||||
static void drawThrowException(Advice advice, TableElement table, boolean isNeedExpand, int expandLevel) {
|
||||
if (advice.isAfterThrowing()) {
|
||||
public static void drawThrowException(TableElement table, TimeFragmentVO tf, boolean isNeedExpand, Integer expandLevel) {
|
||||
if (tf.isThrow()) {
|
||||
//noinspection ThrowableResultOfMethodCallIgnored
|
||||
Throwable throwable = advice.getThrowExp();
|
||||
Throwable throwable = tf.getThrowExp();
|
||||
if (isNeedExpand) {
|
||||
table.row("THROW-EXCEPTION", new ObjectView(advice.getThrowExp(), expandLevel).draw());
|
||||
table.row("THROW-EXCEPTION", new ObjectView(throwable, expandLevel).draw());
|
||||
} else {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
PrintWriter printWriter = new PrintWriter(stringWriter);
|
||||
@@ -128,22 +121,20 @@ public class TimeTunnelTable {
|
||||
}
|
||||
}
|
||||
|
||||
static void drawReturnObj(Advice advice, TableElement table, boolean isNeedExpand, int expandLevel, int sizeLimit) {
|
||||
// fill the returnObj
|
||||
if (advice.isAfterReturning()) {
|
||||
public static void drawReturnObj(TableElement table, TimeFragmentVO tf, boolean isNeedExpand, Integer expandLevel, Integer sizeLimit) {
|
||||
if (tf.isReturn()) {
|
||||
if (isNeedExpand) {
|
||||
table.row("RETURN-OBJ", new ObjectView(advice.getReturnObj(), expandLevel, sizeLimit).draw());
|
||||
table.row("RETURN-OBJ", new ObjectView(tf.getReturnObj(), expandLevel, sizeLimit).draw());
|
||||
} else {
|
||||
table.row("RETURN-OBJ", "" + StringUtils.objectToString(advice.getReturnObj()));
|
||||
table.row("RETURN-OBJ", "" + StringUtils.objectToString(tf.getReturnObj()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawParameters(Advice advice, TableElement table, boolean isNeedExpand, int expandLevel) {
|
||||
// fill the parameters
|
||||
if (null != advice.getParams()) {
|
||||
public static void drawParameters(TableElement table, Object[] params, boolean isNeedExpand, Integer expandLevel) {
|
||||
if (params != null) {
|
||||
int paramIndex = 0;
|
||||
for (Object param : advice.getParams()) {
|
||||
for (Object param : params) {
|
||||
if (isNeedExpand) {
|
||||
table.row("PARAMETERS[" + paramIndex++ + "]", new ObjectView(param, expandLevel).draw());
|
||||
} else {
|
||||
@@ -153,22 +144,21 @@ public class TimeTunnelTable {
|
||||
}
|
||||
}
|
||||
|
||||
static void drawWatchTableHeader(TableElement table) {
|
||||
public static void drawWatchTableHeader(TableElement table) {
|
||||
table.row(true, label("INDEX").style(Decoration.bold.bold()), label("SEARCH-RESULT")
|
||||
.style(Decoration.bold.bold()));
|
||||
}
|
||||
|
||||
static void drawWatchExpress(Map<Integer, TimeFragment> matchingTimeSegmentMap, TableElement table,
|
||||
String watchExpress, boolean isNeedExpand, int expandLevel, int sizeLimit)
|
||||
throws ExpressException {
|
||||
for (Map.Entry<Integer, TimeFragment> entry : matchingTimeSegmentMap.entrySet()) {
|
||||
Object value = ExpressFactory.threadLocalExpress(entry.getValue().getAdvice()).get(watchExpress);
|
||||
public static void drawWatchResults(TableElement table, Map<Integer, Object> watchResults, boolean isNeedExpand,
|
||||
Integer expandLevel, Integer sizeLimit) {
|
||||
for (Map.Entry<Integer, Object> entry : watchResults.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
table.row("" + entry.getKey(), "" +
|
||||
(isNeedExpand ? new ObjectView(value, expandLevel, sizeLimit).draw() : StringUtils.objectToString(value)));
|
||||
}
|
||||
}
|
||||
|
||||
static TableElement drawPlayHeader(String className, String methodName, String objectAddress, int index,
|
||||
public static TableElement drawPlayHeader(String className, String methodName, String objectAddress, int index,
|
||||
TableElement table) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return table.row("RE-INDEX", "" + index)
|
||||
@@ -178,7 +168,7 @@ public class TimeTunnelTable {
|
||||
.row("METHOD", methodName);
|
||||
}
|
||||
|
||||
static void drawPlayResult(TableElement table, Object returnObj, boolean isNeedExpand, int expandLevel,
|
||||
public static void drawPlayResult(TableElement table, Object returnObj, boolean isNeedExpand, int expandLevel,
|
||||
int sizeLimit, double cost) {
|
||||
// 执行成功:输出成功状态
|
||||
table.row("IS-RETURN", "" + true);
|
||||
@@ -193,7 +183,7 @@ public class TimeTunnelTable {
|
||||
}
|
||||
}
|
||||
|
||||
static void drawPlayException(TableElement table, Throwable t, boolean isNeedExpand, int expandLevel) {
|
||||
public static void drawPlayException(TableElement table, Throwable t, boolean isNeedExpand, int expandLevel) {
|
||||
// 执行失败:输出失败状态
|
||||
table.row("IS-RETURN", "" + false);
|
||||
table.row("IS-EXCEPTION", "" + true);
|
||||
@@ -219,4 +209,5 @@ public class TimeTunnelTable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -23,20 +23,20 @@ public class TraceAdviceListener extends AbstractTraceAdviceListener implements
|
||||
@Override
|
||||
public void invokeBeforeTracing(String tracingClassName, String tracingMethodName, String tracingMethodDesc, int tracingLineNumber)
|
||||
throws Throwable {
|
||||
threadBoundEntity.get().view.begin(
|
||||
StringUtils.normalizeClassName(tracingClassName) + ":" + tracingMethodName + "()" + " #" + tracingLineNumber);
|
||||
// normalize className later
|
||||
threadBoundEntity.get().tree.begin(tracingClassName, tracingMethodName, tracingLineNumber, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeAfterTracing(String tracingClassName, String tracingMethodName, String tracingMethodDesc, int tracingLineNumber)
|
||||
throws Throwable {
|
||||
threadBoundEntity.get().view.end();
|
||||
threadBoundEntity.get().tree.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeThrowTracing(String tracingClassName, String tracingMethodName, String tracingMethodDesc, int tracingLineNumber)
|
||||
throws Throwable {
|
||||
threadBoundEntity.get().view.end("throws Exception");
|
||||
threadBoundEntity.get().tree.end(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
import com.taobao.arthas.core.util.DateUtils;
|
||||
import com.taobao.arthas.core.command.model.TraceModel;
|
||||
import com.taobao.arthas.core.command.model.TraceTree;
|
||||
import com.taobao.arthas.core.util.ThreadUtil;
|
||||
import com.taobao.arthas.core.view.TreeView;
|
||||
|
||||
/**
|
||||
* 用于在ThreadLocal中传递的实体
|
||||
@@ -10,22 +10,14 @@ import com.taobao.arthas.core.view.TreeView;
|
||||
*/
|
||||
public class TraceEntity {
|
||||
|
||||
protected TreeView view;
|
||||
protected TraceTree tree;
|
||||
protected int deep;
|
||||
|
||||
public TraceEntity() {
|
||||
this.view = createTreeView();
|
||||
this.tree = createTraceTree();
|
||||
this.deep = 0;
|
||||
}
|
||||
|
||||
public TreeView getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
public void setView(TreeView view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public int getDeep() {
|
||||
return deep;
|
||||
}
|
||||
@@ -34,8 +26,12 @@ public class TraceEntity {
|
||||
this.deep = deep;
|
||||
}
|
||||
|
||||
private TreeView createTreeView() {
|
||||
String threadTitle = "ts=" + DateUtils.getCurrentDate()+ ";" + ThreadUtil.getThreadTitle(Thread.currentThread());
|
||||
return new TreeView(true, threadTitle);
|
||||
private TraceTree createTraceTree() {
|
||||
return new TraceTree(ThreadUtil.getThreadNode(Thread.currentThread()));
|
||||
}
|
||||
|
||||
public TraceModel getModel() {
|
||||
tree.trim();
|
||||
return new TraceModel(tree.getRoot(), tree.getNodeCount());
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -6,12 +6,12 @@ import com.taobao.arthas.core.GlobalOptions;
|
||||
import com.taobao.arthas.core.advisor.Advice;
|
||||
import com.taobao.arthas.core.advisor.ArthasMethod;
|
||||
import com.taobao.arthas.core.advisor.AdviceListenerAdapter;
|
||||
import com.taobao.arthas.core.command.model.WatchModel;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.util.DateUtils;
|
||||
import com.taobao.arthas.core.util.LogUtil;
|
||||
import com.taobao.arthas.core.util.StringUtils;
|
||||
import com.taobao.arthas.core.util.ThreadLocalWatch;
|
||||
import com.taobao.arthas.core.view.ObjectView;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author beiwei30 on 29/11/2016.
|
||||
@@ -71,10 +71,7 @@ class WatchAdviceListener extends AdviceListenerAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNeedExpand() {
|
||||
Integer expand = command.getExpand();
|
||||
return null != expand && expand >= 0;
|
||||
}
|
||||
|
||||
|
||||
private void watching(Advice advice) {
|
||||
try {
|
||||
@@ -87,9 +84,15 @@ class WatchAdviceListener extends AdviceListenerAdapter {
|
||||
if (conditionResult) {
|
||||
// TODO: concurrency issues for process.write
|
||||
Object value = getExpressionResult(command.getExpress(), advice, cost);
|
||||
String result = StringUtils.objectToString(
|
||||
isNeedExpand() ? new ObjectView(value, command.getExpand(), command.getSizeLimit()).draw() : value);
|
||||
process.write("ts=" + DateUtils.getCurrentDate() + "; [cost=" + cost + "ms] result=" + result + "\n");
|
||||
|
||||
WatchModel model = new WatchModel();
|
||||
model.setTs(new Date());
|
||||
model.setCost(cost);
|
||||
model.setValue(value);
|
||||
model.setExpand(command.getExpand());
|
||||
model.setSizeLimit(command.getSizeLimit());
|
||||
|
||||
process.appendResult(model);
|
||||
process.times().incrementAndGet();
|
||||
if (isLimitExceeded(command.getNumberOfLimit(), process.times().get())) {
|
||||
abortProcess(process, command.getNumberOfLimit());
|
||||
@@ -97,10 +100,9 @@ class WatchAdviceListener extends AdviceListenerAdapter {
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.warn("watch failed.", e);
|
||||
process.write("watch failed, condition is: " + command.getConditionExpress() + ", express is: "
|
||||
process.end(-1, "watch failed, condition is: " + command.getConditionExpress() + ", express is: "
|
||||
+ command.getExpress() + ", " + e.getMessage() + ", visit " + LogUtil.loggingFile()
|
||||
+ " for more details.\n");
|
||||
process.end();
|
||||
+ " for more details.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.taobao.arthas.core.command.view;
|
||||
|
||||
import com.taobao.arthas.core.command.model.EnhancerModel;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
|
||||
/**
|
||||
* Term view for EnhancerModel
|
||||
* @author gongdewei 2020/7/21
|
||||
*/
|
||||
public class EnhancerView extends ResultView<EnhancerModel> {
|
||||
@Override
|
||||
public void draw(CommandProcess process, EnhancerModel result) {
|
||||
// ignore enhance result status, judge by the following output
|
||||
if (result.getEffect() != null) {
|
||||
process.write(ViewRenderUtil.renderEnhancerAffect(result.getEffect()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public class GetStaticView extends ResultView<GetStaticModel> {
|
||||
|
||||
@Override
|
||||
public void draw(CommandProcess process, GetStaticModel result) {
|
||||
int expand = result.expand();
|
||||
int expand = result.getExpand();
|
||||
if (result.getField() != null) {
|
||||
ObjectVO field = result.getField();
|
||||
String valueStr = StringUtils.objectToString(expand >= 0 ? new ObjectView(field.getValue(), expand).draw() : field.getValue());
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.taobao.arthas.core.command.view;
|
||||
|
||||
import com.taobao.arthas.core.command.model.MonitorModel;
|
||||
import com.taobao.arthas.core.command.monitor200.MonitorData;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.text.Decoration;
|
||||
import com.taobao.text.ui.TableElement;
|
||||
import com.taobao.text.util.RenderUtil;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import static com.taobao.text.ui.Element.label;
|
||||
|
||||
/**
|
||||
* Term view for MonitorModel
|
||||
* @author gongdewei 2020/4/28
|
||||
*/
|
||||
public class MonitorView extends ResultView<MonitorModel> {
|
||||
@Override
|
||||
public void draw(CommandProcess process, MonitorModel result) {
|
||||
TableElement table = new TableElement(2, 3, 3, 1, 1, 1, 1, 1).leftCellPadding(1).rightCellPadding(1);
|
||||
table.row(true, label("timestamp").style(Decoration.bold.bold()),
|
||||
label("class").style(Decoration.bold.bold()),
|
||||
label("method").style(Decoration.bold.bold()),
|
||||
label("total").style(Decoration.bold.bold()),
|
||||
label("success").style(Decoration.bold.bold()),
|
||||
label("fail").style(Decoration.bold.bold()),
|
||||
label("avg-rt(ms)").style(Decoration.bold.bold()),
|
||||
label("fail-rate").style(Decoration.bold.bold()));
|
||||
|
||||
final DecimalFormat df = new DecimalFormat("0.00");
|
||||
|
||||
for (MonitorData data : result.getMonitorDataList()) {
|
||||
table.row(
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),
|
||||
data.getClassName(),
|
||||
data.getMethodName(),
|
||||
"" + data.getTotal(),
|
||||
"" + data.getSuccess(),
|
||||
"" + data.getFailed(),
|
||||
df.format(div(data.getCost(), data.getTotal())),
|
||||
df.format(100.0d * div(data.getFailed(), data.getTotal())) + "%"
|
||||
);
|
||||
}
|
||||
|
||||
process.write(RenderUtil.render(table, process.width()) + "\n");
|
||||
|
||||
}
|
||||
|
||||
private double div(double a, double b) {
|
||||
if (b == 0) {
|
||||
return 0;
|
||||
}
|
||||
return a / b;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.taobao.arthas.core.command.view;
|
||||
|
||||
import com.taobao.arthas.core.command.model.ProfilerModel;
|
||||
import com.taobao.arthas.core.command.monitor200.ProfilerCommand.ProfilerAction;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
|
||||
|
||||
/**
|
||||
* Term view for ProfilerModel
|
||||
*
|
||||
* @author gongdewei 2020/4/27
|
||||
*/
|
||||
public class ProfilerView extends ResultView<ProfilerModel> {
|
||||
@Override
|
||||
public void draw(CommandProcess process, ProfilerModel model) {
|
||||
if (model.getSupportedActions() != null) {
|
||||
process.write("Supported Actions: " + model.getSupportedActions()).write("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
drawExecuteResult(process, model);
|
||||
|
||||
if (ProfilerAction.start.name().equals(model.getAction())) {
|
||||
if (model.getDuration() != null) {
|
||||
process.write(String.format("profiler will silent stop after %d seconds.\n", model.getDuration().longValue()));
|
||||
process.write("profiler output file will be: " + model.getOutputFile() + "\n");
|
||||
}
|
||||
} else if (ProfilerAction.stop.name().equals(model.getAction())) {
|
||||
process.write("profiler output file: " + model.getOutputFile() + "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void drawExecuteResult(CommandProcess process, ProfilerModel model) {
|
||||
if (model.getExecuteResult() != null) {
|
||||
process.write(model.getExecuteResult());
|
||||
if (!model.getExecuteResult().endsWith("\n")) {
|
||||
process.write("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,13 @@ public class ResultViewResolver {
|
||||
registerView(MBeanView.class);
|
||||
registerView(PerfCounterView.class);
|
||||
registerView(ThreadView.class);
|
||||
registerView(ProfilerView.class);
|
||||
registerView(EnhancerView.class);
|
||||
registerView(MonitorView.class);
|
||||
registerView(StackView.class);
|
||||
registerView(TimeTunnelView.class);
|
||||
registerView(TraceView.class);
|
||||
registerView(WatchView.class);
|
||||
|
||||
} catch (Throwable e) {
|
||||
logger.error("register result view failed", e);
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.taobao.arthas.core.command.view;
|
||||
|
||||
import com.taobao.arthas.core.command.model.StackModel;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.util.DateUtils;
|
||||
import com.taobao.arthas.core.util.ThreadUtil;
|
||||
|
||||
/**
|
||||
* Term view for StackModel
|
||||
* @author gongdewei 2020/4/13
|
||||
*/
|
||||
public class StackView extends ResultView<StackModel> {
|
||||
|
||||
@Override
|
||||
public void draw(CommandProcess process, StackModel result) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(ThreadUtil.getThreadTitle(result)).append("\n");
|
||||
|
||||
StackTraceElement[] stackTraceElements = result.getStackTrace();
|
||||
StackTraceElement locationStackTraceElement = stackTraceElements[0];
|
||||
String locationString = String.format(" @%s.%s()", locationStackTraceElement.getClassName(),
|
||||
locationStackTraceElement.getMethodName());
|
||||
sb.append(locationString).append("\n");
|
||||
|
||||
int skip = 1;
|
||||
for (int index = skip; index < stackTraceElements.length; index++) {
|
||||
StackTraceElement ste = stackTraceElements[index];
|
||||
sb.append(" at ")
|
||||
.append(ste.getClassName())
|
||||
.append(".")
|
||||
.append(ste.getMethodName())
|
||||
.append("(")
|
||||
.append(ste.getFileName())
|
||||
.append(":")
|
||||
.append(ste.getLineNumber())
|
||||
.append(")\n");
|
||||
}
|
||||
process.write("ts=" + DateUtils.formatDate(result.getTs()) + ";" + sb.toString() + "\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.taobao.arthas.core.command.view;
|
||||
|
||||
import com.taobao.arthas.core.command.model.TimeFragmentVO;
|
||||
import com.taobao.arthas.core.command.model.TimeTunnelModel;
|
||||
import com.taobao.arthas.core.command.monitor200.TimeTunnelTable;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.util.StringUtils;
|
||||
import com.taobao.arthas.core.view.ObjectView;
|
||||
import com.taobao.text.ui.Element;
|
||||
import com.taobao.text.ui.TableElement;
|
||||
import com.taobao.text.util.RenderUtil;
|
||||
|
||||
import static com.taobao.arthas.core.command.monitor200.TimeTunnelTable.*;
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Term view for TimeTunnelCommand
|
||||
* @author gongdewei 2020/4/27
|
||||
*/
|
||||
public class TimeTunnelView extends ResultView<TimeTunnelModel> {
|
||||
|
||||
@Override
|
||||
public void draw(CommandProcess process, TimeTunnelModel timeTunnelModel) {
|
||||
Integer expand = timeTunnelModel.getExpand();
|
||||
boolean isNeedExpand = isNeedExpand(expand);
|
||||
Integer sizeLimit = timeTunnelModel.getSizeLimit();
|
||||
|
||||
if (timeTunnelModel.getTimeFragmentList() != null) {
|
||||
//show list table: tt -l / tt -t
|
||||
Element table = drawTimeTunnelTable(timeTunnelModel.getTimeFragmentList(), timeTunnelModel.getFirst());
|
||||
process.write(RenderUtil.render(table, process.width()));
|
||||
|
||||
} else if (timeTunnelModel.getTimeFragment() != null) {
|
||||
//show detail of single TimeFragment: tt -i 1000
|
||||
TimeFragmentVO tf = timeTunnelModel.getTimeFragment();
|
||||
TableElement table = TimeTunnelTable.createDefaultTable();
|
||||
TimeTunnelTable.drawTimeTunnel(table, tf);
|
||||
TimeTunnelTable.drawParameters(table, tf.getParams(), isNeedExpand, expand);
|
||||
TimeTunnelTable.drawReturnObj(table, tf, isNeedExpand, expand, sizeLimit);
|
||||
TimeTunnelTable.drawThrowException(table, tf, isNeedExpand, expand);
|
||||
process.write(RenderUtil.render(table, process.width()));
|
||||
|
||||
} else if (timeTunnelModel.getWatchValue() != null) {
|
||||
//watch single TimeFragment: tt -i 1000 -w 'params'
|
||||
Object value = timeTunnelModel.getWatchValue();
|
||||
if (isNeedExpand) {
|
||||
process.write(new ObjectView(value, expand, sizeLimit).draw()).write("\n");
|
||||
} else {
|
||||
process.write(StringUtils.objectToString(value)).write("\n");
|
||||
}
|
||||
|
||||
} else if (timeTunnelModel.getWatchResults() != null) {
|
||||
//search & watch: tt -s 'returnObj!=null' -w 'returnObj'
|
||||
TableElement table = TimeTunnelTable.createDefaultTable();
|
||||
TimeTunnelTable.drawWatchTableHeader(table);
|
||||
TimeTunnelTable.drawWatchResults(table, timeTunnelModel.getWatchResults(), isNeedExpand, expand, sizeLimit);
|
||||
process.write(RenderUtil.render(table, process.width()));
|
||||
|
||||
} else if (timeTunnelModel.getReplayResult() != null) {
|
||||
//replay: tt -i 1000 -p
|
||||
TimeFragmentVO replayResult = timeTunnelModel.getReplayResult();
|
||||
Integer replayNo = timeTunnelModel.getReplayNo();
|
||||
TableElement table = TimeTunnelTable.createDefaultTable();
|
||||
TimeTunnelTable.drawPlayHeader(replayResult.getClassName(), replayResult.getMethodName(), replayResult.getObject(), replayResult.getIndex(), table);
|
||||
TimeTunnelTable.drawParameters(table, replayResult.getParams(), isNeedExpand, expand);
|
||||
if (replayResult.isReturn()) {
|
||||
TimeTunnelTable.drawPlayResult(table, replayResult.getReturnObj(), isNeedExpand, expand, sizeLimit, replayResult.getCost());
|
||||
} else {
|
||||
TimeTunnelTable.drawPlayException(table, replayResult.getThrowExp(), isNeedExpand, expand);
|
||||
}
|
||||
process.write(RenderUtil.render(table, process.width()))
|
||||
.write(format("Time fragment[%d] successfully replayed %d times.", replayResult.getIndex(), replayNo))
|
||||
.write("\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNeedExpand(Integer expand) {
|
||||
return null != expand && expand > 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package com.taobao.arthas.core.command.view;
|
||||
|
||||
import com.taobao.arthas.core.command.model.MethodNode;
|
||||
import com.taobao.arthas.core.command.model.ThreadNode;
|
||||
import com.taobao.arthas.core.command.model.ThrowNode;
|
||||
import com.taobao.arthas.core.command.model.TraceModel;
|
||||
import com.taobao.arthas.core.command.model.TraceNode;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.util.DateUtils;
|
||||
import com.taobao.arthas.core.util.StringUtils;
|
||||
import com.taobao.arthas.core.view.Ansi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Term view for TraceModel
|
||||
* @author gongdewei 2020/4/29
|
||||
*/
|
||||
public class TraceView extends ResultView<TraceModel> {
|
||||
private static final String STEP_FIRST_CHAR = "`---";
|
||||
private static final String STEP_NORMAL_CHAR = "+---";
|
||||
private static final String STEP_HAS_BOARD = "| ";
|
||||
private static final String STEP_EMPTY_BOARD = " ";
|
||||
private static final String TIME_UNIT = "ms";
|
||||
|
||||
// 是否输出耗时
|
||||
private boolean isPrintCost = true;
|
||||
private MethodNode maxCostNode;
|
||||
|
||||
@Override
|
||||
public void draw(CommandProcess process, TraceModel result) {
|
||||
process.write(drawTree(result.getRoot())).write("\n");
|
||||
}
|
||||
|
||||
public String drawTree(TraceNode root) {
|
||||
|
||||
//reset status
|
||||
maxCostNode = null;
|
||||
findMaxCostNode(root);
|
||||
|
||||
final StringBuilder treeSB = new StringBuilder(2048);
|
||||
|
||||
final Ansi highlighted = Ansi.ansi().fg(Ansi.Color.RED);
|
||||
|
||||
recursive(0, true, "", root, new Callback() {
|
||||
|
||||
@Override
|
||||
public void callback(int deep, boolean isLast, String prefix, TraceNode node) {
|
||||
treeSB.append(prefix).append(isLast ? STEP_FIRST_CHAR : STEP_NORMAL_CHAR);
|
||||
renderNode(treeSB, node, highlighted);
|
||||
if (!StringUtils.isBlank(node.getMark())) {
|
||||
treeSB.append(" [").append(node.getMark()).append(node.marks() > 1 ? "," + node.marks() : "").append("]");
|
||||
}
|
||||
treeSB.append("\n");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return treeSB.toString();
|
||||
}
|
||||
|
||||
private void renderNode(StringBuilder sb, TraceNode node, Ansi highlighted) {
|
||||
//render cost: [0.366865ms]
|
||||
if (isPrintCost && node instanceof MethodNode) {
|
||||
MethodNode methodNode = (MethodNode) node;
|
||||
|
||||
String costStr = renderCost(methodNode);
|
||||
if (node == maxCostNode) {
|
||||
// the node with max cost will be highlighted
|
||||
sb.append(highlighted.a(costStr).reset().toString());
|
||||
} else {
|
||||
sb.append(costStr);
|
||||
}
|
||||
}
|
||||
|
||||
//render method name
|
||||
if (node instanceof MethodNode) {
|
||||
MethodNode methodNode = (MethodNode) node;
|
||||
//clazz.getName() + ":" + method.getName() + "()"
|
||||
sb.append(methodNode.getClassName()).append(":").append(methodNode.getMethodName()).append("()");
|
||||
// #lineNumber
|
||||
if (methodNode.getLineNumber()!= -1) {
|
||||
sb.append(" #").append(methodNode.getLineNumber());
|
||||
}
|
||||
} else if (node instanceof ThreadNode) {
|
||||
//render thread info
|
||||
ThreadNode threadNode = (ThreadNode) node;
|
||||
//ts=2020-04-29 10:34:00;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@18b4aac2
|
||||
sb.append(format("ts=%s;thread_name=%s;id=%s;is_daemon=%s;priority=%d;TCCL=%s",
|
||||
DateUtils.formatDate(threadNode.getTimestamp()),
|
||||
threadNode.getThreadName(),
|
||||
Long.toHexString(threadNode.getThreadId()),
|
||||
threadNode.isDaemon(),
|
||||
threadNode.getPriority(),
|
||||
threadNode.getClassloader()));
|
||||
|
||||
//trace_id
|
||||
if (threadNode.getTraceId() != null) {
|
||||
sb.append(";trace_id="+threadNode.getTraceId());
|
||||
}
|
||||
if (threadNode.getRpcId() != null) {
|
||||
sb.append(";rpc_id="+threadNode.getRpcId());
|
||||
}
|
||||
} else if (node instanceof ThrowNode) {
|
||||
ThrowNode throwNode = (ThrowNode) node;
|
||||
sb.append("throw:").append(throwNode.getException())
|
||||
.append(" #").append(throwNode.getLineNumber())
|
||||
.append(" [").append(throwNode.getMessage()).append("]");
|
||||
|
||||
} else {
|
||||
throw new UnsupportedOperationException("unknown trace node: " + node.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private String renderCost(MethodNode node) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (node.getTimes() <= 1) {
|
||||
sb.append("[").append(nanoToMillis(node.getCost())).append(TIME_UNIT).append("] ");
|
||||
} else {
|
||||
sb.append("[min=").append(nanoToMillis(node.getMinCost())).append(TIME_UNIT).append(",max=")
|
||||
.append(nanoToMillis(node.getMaxCost())).append(TIME_UNIT).append(",total=")
|
||||
.append(nanoToMillis(node.getTotalCost())).append(TIME_UNIT).append(",count=")
|
||||
.append(node.getTimes()).append("] ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归遍历
|
||||
*/
|
||||
private void recursive(int deep, boolean isLast, String prefix, TraceNode node, Callback callback) {
|
||||
callback.callback(deep, isLast, prefix, node);
|
||||
if (!isLeaf(node)) {
|
||||
List<TraceNode> children = node.getChildren();
|
||||
if (children == null) {
|
||||
return;
|
||||
}
|
||||
final int size = children.size();
|
||||
for (int index = 0; index < size; index++) {
|
||||
final boolean isLastFlag = index == size - 1;
|
||||
final String currentPrefix = isLast ? prefix + STEP_EMPTY_BOARD : prefix + STEP_HAS_BOARD;
|
||||
recursive(
|
||||
deep + 1,
|
||||
isLastFlag,
|
||||
currentPrefix,
|
||||
children.get(index),
|
||||
callback
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找耗时最大的节点,便于后续高亮展示
|
||||
* @param node
|
||||
*/
|
||||
private void findMaxCostNode(TraceNode node) {
|
||||
if (node instanceof MethodNode && !isRoot(node) && !isRoot(node.parent())) {
|
||||
MethodNode aNode = (MethodNode) node;
|
||||
if (maxCostNode == null || maxCostNode.getTotalCost() < aNode.getTotalCost()) {
|
||||
maxCostNode = aNode;
|
||||
}
|
||||
}
|
||||
if (!isLeaf(node)) {
|
||||
List<TraceNode> children = node.getChildren();
|
||||
if (children != null) {
|
||||
for (TraceNode n: children) {
|
||||
findMaxCostNode(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRoot(TraceNode node) {
|
||||
return node.parent() == null;
|
||||
}
|
||||
|
||||
private boolean isLeaf(TraceNode node) {
|
||||
List<TraceNode> children = node.getChildren();
|
||||
return children == null || children.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* convert nano-seconds to milli-seconds
|
||||
*/
|
||||
double nanoToMillis(long nanoSeconds) {
|
||||
return nanoSeconds / 1000000.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历回调接口
|
||||
*/
|
||||
private interface Callback {
|
||||
|
||||
void callback(int deep, boolean isLast, String prefix, TraceNode node);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.taobao.arthas.core.command.view;
|
||||
|
||||
import com.taobao.arthas.core.command.model.WatchModel;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.util.DateUtils;
|
||||
import com.taobao.arthas.core.util.StringUtils;
|
||||
import com.taobao.arthas.core.view.ObjectView;
|
||||
|
||||
/**
|
||||
* Term view for WatchModel
|
||||
*
|
||||
* @author gongdewei 2020/3/27
|
||||
*/
|
||||
public class WatchView extends ResultView<WatchModel> {
|
||||
|
||||
@Override
|
||||
public void draw(CommandProcess process, WatchModel model) {
|
||||
Object value = model.getValue();
|
||||
String result = StringUtils.objectToString(
|
||||
isNeedExpand(model) ? new ObjectView(value, model.getExpand(), model.getSizeLimit()).draw() : value);
|
||||
|
||||
process.write("ts=" + DateUtils.formatDate(model.getTs()) + "; [cost=" + model.getCost() + "ms] result=" + result + "\n");
|
||||
}
|
||||
|
||||
private boolean isNeedExpand(WatchModel model) {
|
||||
Integer expand = model.getExpand();
|
||||
return null != expand && expand >= 0;
|
||||
}
|
||||
}
|
||||
@@ -19,4 +19,8 @@ public class DateUtils {
|
||||
public static String getCurrentDate() {
|
||||
return dataFormat.get().format(new Date());
|
||||
}
|
||||
|
||||
public static String formatDate(Date date) {
|
||||
return dataFormat.get().format(date);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.taobao.arthas.core.util;
|
||||
|
||||
import com.taobao.arthas.core.command.model.BlockingLockInfo;
|
||||
import com.taobao.arthas.core.command.model.StackModel;
|
||||
import com.taobao.arthas.core.command.model.ThreadNode;
|
||||
import com.taobao.arthas.core.view.Ansi;
|
||||
|
||||
import java.arthas.SpyAPI;
|
||||
@@ -19,6 +21,9 @@ abstract public class ThreadUtil {
|
||||
private static final BlockingLockInfo EMPTY_INFO = new BlockingLockInfo();
|
||||
|
||||
private static ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
|
||||
private static Field threadLocalsField;
|
||||
private static Field threadLocalsTableFiled;
|
||||
private static Field threadLocalEntryValueField;
|
||||
|
||||
public static ThreadGroup getRoot() {
|
||||
ThreadGroup group = Thread.currentThread().getThreadGroup();
|
||||
@@ -393,6 +398,31 @@ abstract public class ThreadUtil {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方法执行堆栈信息
|
||||
*
|
||||
* @return 方法堆栈信息
|
||||
*/
|
||||
public static StackModel getThreadStackModel(Thread currentThread) {
|
||||
StackModel stackModel = new StackModel();
|
||||
stackModel.setThreadName(currentThread.getName());
|
||||
stackModel.setThreadId(Long.toHexString(currentThread.getId()));
|
||||
stackModel.setDaemon(currentThread.isDaemon());
|
||||
stackModel.setPriority(currentThread.getPriority());
|
||||
stackModel.setClassloader(getTCCL(currentThread));
|
||||
|
||||
getEagleeyeTraceInfo(currentThread, stackModel);
|
||||
|
||||
|
||||
//stack
|
||||
StackTraceElement[] stackTraceElementArray = currentThread.getStackTrace();
|
||||
int magicStackDepth = findTheSpyAPIDepth(stackTraceElementArray);
|
||||
StackTraceElement[] actualStackFrames = new StackTraceElement[stackTraceElementArray.length - magicStackDepth];
|
||||
System.arraycopy(stackTraceElementArray, magicStackDepth , actualStackFrames, 0, actualStackFrames.length);
|
||||
stackModel.setStackTrace(actualStackFrames);
|
||||
return stackModel;
|
||||
}
|
||||
|
||||
public static String getThreadTitle(Thread currentThread) {
|
||||
StringBuilder sb = new StringBuilder("thread_name=");
|
||||
sb.append(currentThread.getName())
|
||||
@@ -400,7 +430,47 @@ abstract public class ThreadUtil {
|
||||
.append(";is_daemon=").append(currentThread.isDaemon())
|
||||
.append(";priority=").append(currentThread.getPriority())
|
||||
.append(";TCCL=").append(getTCCL(currentThread));
|
||||
getEagleeyeTraceInfo(currentThread, sb);
|
||||
|
||||
StackModel stackModel = new StackModel();
|
||||
getEagleeyeTraceInfo(currentThread, stackModel);
|
||||
if (stackModel.getTraceId() != null) {
|
||||
sb.append(";trace_id=").append(stackModel.getTraceId());
|
||||
}
|
||||
if (stackModel.getRpcId() != null) {
|
||||
sb.append(";rpc_id=").append(stackModel.getRpcId());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static ThreadNode getThreadNode(Thread currentThread) {
|
||||
ThreadNode threadNode = new ThreadNode();
|
||||
threadNode.setThreadId(currentThread.getId());
|
||||
threadNode.setThreadName(currentThread.getName());
|
||||
threadNode.setDaemon(currentThread.isDaemon());
|
||||
threadNode.setPriority(currentThread.getPriority());
|
||||
threadNode.setClassloader(getTCCL(currentThread));
|
||||
|
||||
//trace_id
|
||||
StackModel stackModel = new StackModel();
|
||||
getEagleeyeTraceInfo(currentThread, stackModel);
|
||||
threadNode.setTraceId(stackModel.getTraceId());
|
||||
threadNode.setRpcId(stackModel.getRpcId());
|
||||
return threadNode;
|
||||
}
|
||||
|
||||
public static String getThreadTitle(StackModel stackModel) {
|
||||
StringBuilder sb = new StringBuilder("thread_name=");
|
||||
sb.append(stackModel.getThreadName())
|
||||
.append(";id=").append(stackModel.getThreadId())
|
||||
.append(";is_daemon=").append(stackModel.isDaemon())
|
||||
.append(";priority=").append(stackModel.getPriority())
|
||||
.append(";TCCL=").append(stackModel.getClassloader());
|
||||
if (stackModel.getTraceId() != null) {
|
||||
sb.append(";trace_id=").append(stackModel.getTraceId());
|
||||
}
|
||||
if (stackModel.getRpcId() != null) {
|
||||
sb.append(";rpc_id=").append(stackModel.getRpcId());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -408,41 +478,52 @@ abstract public class ThreadUtil {
|
||||
if (null == currentThread.getContextClassLoader()) {
|
||||
return "null";
|
||||
} else {
|
||||
return currentThread.getContextClassLoader().getClass().getName() +
|
||||
"@" + Integer.toHexString(currentThread.getContextClassLoader().hashCode());
|
||||
String classloaderClassName = currentThread.getContextClassLoader().getClass().getName();
|
||||
StringBuilder sb = new StringBuilder(classloaderClassName.length()+10);
|
||||
sb.append(classloaderClassName)
|
||||
.append("@")
|
||||
.append(Integer.toHexString(currentThread.getContextClassLoader().hashCode()));
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static void getEagleeyeTraceInfo(Thread currentThread, StringBuilder sb) {
|
||||
private static void getEagleeyeTraceInfo(Thread currentThread, StackModel stackModel) {
|
||||
try {
|
||||
// access to Thread#threadlocals field
|
||||
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
|
||||
if (threadLocalsField == null) {
|
||||
threadLocalsField = Thread.class.getDeclaredField("threadLocals");
|
||||
}
|
||||
threadLocalsField.setAccessible(true);
|
||||
Object threadLocalMap = threadLocalsField.get(currentThread);
|
||||
|
||||
// access to ThreadLocal$ThreadLocalMap#table filed
|
||||
Field tableFiled = threadLocalMap.getClass().getDeclaredField("table");
|
||||
tableFiled.setAccessible(true);
|
||||
Object[] tableEntries = (Object[])tableFiled.get(threadLocalMap);
|
||||
if (threadLocalsTableFiled == null) {
|
||||
threadLocalsTableFiled = threadLocalMap.getClass().getDeclaredField("table");
|
||||
}
|
||||
threadLocalsTableFiled.setAccessible(true);
|
||||
Object[] tableEntries = (Object[]) threadLocalsTableFiled.get(threadLocalMap);
|
||||
for (Object entry: tableEntries) {
|
||||
if (entry == null) {
|
||||
continue;
|
||||
}
|
||||
// access to ThreadLocal$ThreadLocalMap$Entry#value field
|
||||
Field valueField = entry.getClass().getDeclaredField("value");
|
||||
valueField.setAccessible(true);
|
||||
Object threadLocalValue = valueField.get(entry);
|
||||
if (threadLocalEntryValueField == null) {
|
||||
threadLocalEntryValueField = entry.getClass().getDeclaredField("value");
|
||||
}
|
||||
threadLocalEntryValueField.setAccessible(true);
|
||||
Object threadLocalValue = threadLocalEntryValueField.get(entry);
|
||||
if (threadLocalValue != null &&
|
||||
"com.taobao.eagleeye.RpcContext_inner".equals(threadLocalValue.getClass().getName())) {
|
||||
// finally we got the chance to access trace id
|
||||
Method getTraceIdMethod = threadLocalValue.getClass().getMethod("getTraceId");
|
||||
getTraceIdMethod.setAccessible(true);
|
||||
String traceId = (String)getTraceIdMethod.invoke(threadLocalValue);
|
||||
sb.append(";trace_id=").append(traceId);
|
||||
stackModel.setTraceId(traceId);
|
||||
// get rpc id
|
||||
Method getRpcIdMethod = threadLocalValue.getClass().getMethod("getRpcId");
|
||||
getTraceIdMethod.setAccessible(true);
|
||||
String rpcId = (String)getRpcIdMethod.invoke(threadLocalValue);
|
||||
sb.append(";rpc_id=").append(rpcId);
|
||||
stackModel.setRpcId(rpcId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user