Transform commands of pkg monitor200 (part 2) (#1355)

This commit is contained in:
gongdewei
2020-07-23 15:01:41 +08:00
committed by GitHub
parent c25dbd9a1f
commit edb5bead17
39 changed files with 2080 additions and 366 deletions
@@ -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为beforeInvokefalse为方法体入口的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;
}
}
@@ -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");
}
}
}
@@ -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>();
@@ -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.");
}
}
}
@@ -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());
@@ -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 {
}
}
}
}
@@ -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());
}
}
@@ -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;
}
}