diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/StackModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/StackModel.java new file mode 100644 index 000000000..873913a7b --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/StackModel.java @@ -0,0 +1,105 @@ +package com.taobao.arthas.core.command.model; + +/** + * StackCommand result model + * @author gongdewei 2020/4/13 + */ +public class StackModel extends ResultModel { + + private String 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 tccl; + private StackTraceElement[] stackTrace; + + @Override + public String getType() { + return "stack"; + } + + public String getTs() { + return ts; + } + + public void setTs(String 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 getTccl() { + return tccl; + } + + public void setTccl(String tccl) { + this.tccl = tccl; + } + + 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; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackAdviceListener.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackAdviceListener.java index a5f73aa7c..36051aedf 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackAdviceListener.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackAdviceListener.java @@ -1,6 +1,7 @@ package com.taobao.arthas.core.command.monitor200; import com.taobao.arthas.core.advisor.ReflectAdviceListenerAdapter; +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; @@ -17,7 +18,7 @@ import com.taobao.arthas.core.util.ThreadUtil; public class StackAdviceListener extends ReflectAdviceListenerAdapter { private static final Logger logger = LoggerFactory.getLogger(StackAdviceListener.class); - private final ThreadLocal stackThreadLocal = new ThreadLocal(); + private final ThreadLocal stackThreadLocal = new ThreadLocal(); private final ThreadLocalWatch threadLocalWatch = new ThreadLocalWatch(); private StackCommand command; private CommandProcess process; @@ -30,7 +31,7 @@ public class StackAdviceListener extends ReflectAdviceListenerAdapter { @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(); } @@ -55,7 +56,10 @@ public class StackAdviceListener extends ReflectAdviceListenerAdapter { double cost = threadLocalWatch.costInMillis(); if (isConditionMet(command.getConditionExpress(), advice, cost)) { // TODO: concurrency issues for process.write - process.write("ts=" + DateUtils.getCurrentDate() + ";" + stackThreadLocal.get() + "\n"); + // TODO: should clear stackThreadLocal? + StackModel stackModel = stackThreadLocal.get(); + stackModel.setTs(DateUtils.getCurrentDate()); + process.appendResult(stackModel); process.times().incrementAndGet(); if (isLimitExceeded(command.getNumberOfLimit(), process.times().get())) { abortProcess(process, command.getNumberOfLimit()); @@ -63,9 +67,8 @@ public class StackAdviceListener extends ReflectAdviceListenerAdapter { } } catch (Exception 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."); } } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java index 8f1f0c4d8..e725ebb2b 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java @@ -45,6 +45,7 @@ public class ResultViewResolver { registerView(new ClassInfoModel(), new ClassInfoView()); registerView(new RowAffectModel(), new RowAffectView()); registerView(new MethodModel(), new MethodView()); + registerView(new StackModel(), new StackView()); } catch (Throwable e) { logger.error("register result view failed", e); } diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/StackView.java b/core/src/main/java/com/taobao/arthas/core/command/view/StackView.java new file mode 100644 index 000000000..f4d45b6b5 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/StackView.java @@ -0,0 +1,39 @@ +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.ThreadUtil; + +/** + * @author gongdewei 2020/4/13 + */ +public class StackView extends ResultView { + + @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=" + result.getTs() + ";" + sb.toString() + "\n"); + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java b/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java index 6d094cc6c..7590b73b9 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java +++ b/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java @@ -1,5 +1,6 @@ package com.taobao.arthas.core.util; +import com.taobao.arthas.core.command.model.StackModel; import com.taobao.arthas.core.view.Ansi; import java.lang.management.*; @@ -372,6 +373,26 @@ abstract public class ThreadUtil { return builder.toString(); } + 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.setTccl(getTCCL(currentThread)); + + getEagleeyeTraceInfo(currentThread, stackModel); + + //stack + StackTraceElement[] stackTraceElementArray = currentThread.getStackTrace(); + //drop top 10 frames of Enhancer + int droppingFrames = 10; + StackTraceElement[] actualStackFrames = new StackTraceElement[stackTraceElementArray.length - droppingFrames]; + System.arraycopy(stackTraceElementArray, droppingFrames, 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()) @@ -379,7 +400,31 @@ 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 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.getTccl()); + 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(); } @@ -392,7 +437,7 @@ abstract public class ThreadUtil { } } - 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"); @@ -416,12 +461,12 @@ abstract public class ThreadUtil { 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; } }