mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
transform command: dashboard, thread
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.lang.management.ThreadInfo;
|
||||
|
||||
/**
|
||||
* Thread blocking lock info, extract from ThreadUtil.
|
||||
*
|
||||
* @author gongdewei 2020/7/14
|
||||
*/
|
||||
public class BlockingLockInfo {
|
||||
|
||||
// the thread info that is holing this lock.
|
||||
private ThreadInfo threadInfo = null;
|
||||
// the associated LockInfo object
|
||||
private int lockIdentityHashCode = 0;
|
||||
// the number of thread that is blocked on this lock
|
||||
private int blockingThreadCount = 0;
|
||||
|
||||
public BlockingLockInfo() {
|
||||
}
|
||||
|
||||
public ThreadInfo getThreadInfo() {
|
||||
return threadInfo;
|
||||
}
|
||||
|
||||
public void setThreadInfo(ThreadInfo threadInfo) {
|
||||
this.threadInfo = threadInfo;
|
||||
}
|
||||
|
||||
public int getLockIdentityHashCode() {
|
||||
return lockIdentityHashCode;
|
||||
}
|
||||
|
||||
public void setLockIdentityHashCode(int lockIdentityHashCode) {
|
||||
this.lockIdentityHashCode = lockIdentityHashCode;
|
||||
}
|
||||
|
||||
public int getBlockingThreadCount() {
|
||||
return blockingThreadCount;
|
||||
}
|
||||
|
||||
public void setBlockingThreadCount(int blockingThreadCount) {
|
||||
this.blockingThreadCount = blockingThreadCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Model of 'dashboard' command
|
||||
* @author gongdewei 2020/4/22
|
||||
*/
|
||||
public class DashboardModel extends ResultModel {
|
||||
private List<ThreadVO> threads;
|
||||
private Map<String, List<MemoryEntryVO>> memoryInfo;
|
||||
private List<GcInfoVO> gcInfos;
|
||||
private RuntimeInfoVO runtimeInfo;
|
||||
private TomcatInfoVO tomcatInfo;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "dashboard";
|
||||
}
|
||||
|
||||
public List<ThreadVO> getThreads() {
|
||||
return threads;
|
||||
}
|
||||
|
||||
public void setThreads(List<ThreadVO> threads) {
|
||||
this.threads = threads;
|
||||
}
|
||||
|
||||
public Map<String, List<MemoryEntryVO>> getMemoryInfo() {
|
||||
return memoryInfo;
|
||||
}
|
||||
|
||||
public void setMemoryInfo(Map<String, List<MemoryEntryVO>> memoryInfo) {
|
||||
this.memoryInfo = memoryInfo;
|
||||
}
|
||||
|
||||
public List<GcInfoVO> getGcInfos() {
|
||||
return gcInfos;
|
||||
}
|
||||
|
||||
public void setGcInfos(List<GcInfoVO> gcInfos) {
|
||||
this.gcInfos = gcInfos;
|
||||
}
|
||||
|
||||
public RuntimeInfoVO getRuntimeInfo() {
|
||||
return runtimeInfo;
|
||||
}
|
||||
|
||||
public void setRuntimeInfo(RuntimeInfoVO runtimeInfo) {
|
||||
this.runtimeInfo = runtimeInfo;
|
||||
}
|
||||
|
||||
public TomcatInfoVO getTomcatInfo() {
|
||||
return tomcatInfo;
|
||||
}
|
||||
|
||||
public void setTomcatInfo(TomcatInfoVO tomcatInfo) {
|
||||
this.tomcatInfo = tomcatInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
/**
|
||||
* GC info of dashboard
|
||||
* @author gongdewei 2020/4/23
|
||||
*/
|
||||
public class GcInfoVO {
|
||||
private String name;
|
||||
private long collectionCount;
|
||||
private long collectionTime;
|
||||
|
||||
public GcInfoVO(String name, long collectionCount, long collectionTime) {
|
||||
this.name = name;
|
||||
this.collectionCount = collectionCount;
|
||||
this.collectionTime = collectionTime;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getCollectionCount() {
|
||||
return collectionCount;
|
||||
}
|
||||
|
||||
public void setCollectionCount(long collectionCount) {
|
||||
this.collectionCount = collectionCount;
|
||||
}
|
||||
|
||||
public long getCollectionTime() {
|
||||
return collectionTime;
|
||||
}
|
||||
|
||||
public void setCollectionTime(long collectionTime) {
|
||||
this.collectionTime = collectionTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
/**
|
||||
* Memory info of 'dashboard' command
|
||||
*
|
||||
* @author gongdewei 2020/4/22
|
||||
*/
|
||||
public class MemoryEntryVO {
|
||||
|
||||
public static final String TYPE_HEAP = "heap";
|
||||
public static final String TYPE_NON_HEAP = "nonheap";
|
||||
public static final String TYPE_BUFFER_POOL = "buffer_pool";
|
||||
|
||||
private String type;
|
||||
private String name;
|
||||
private long used;
|
||||
private long total;
|
||||
private long max;
|
||||
|
||||
public MemoryEntryVO() {
|
||||
}
|
||||
|
||||
public MemoryEntryVO(String type, String name, long used, long total, long max) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.used = used;
|
||||
this.total = total;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
public void setUsed(long used) {
|
||||
this.used = used;
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(long total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public long getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public void setMax(long max) {
|
||||
this.max = max;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
/**
|
||||
* Dashboard - Runtime
|
||||
*
|
||||
* @author gongdewei 2020/4/22
|
||||
*/
|
||||
public class RuntimeInfoVO {
|
||||
private String osName;
|
||||
private String osVersion;
|
||||
private String javaVersion;
|
||||
private String javaHome;
|
||||
private double systemLoadAverage;
|
||||
private int processors;
|
||||
private long uptime;
|
||||
|
||||
public RuntimeInfoVO() {
|
||||
}
|
||||
|
||||
public String getOsName() {
|
||||
return osName;
|
||||
}
|
||||
|
||||
public void setOsName(String osName) {
|
||||
this.osName = osName;
|
||||
}
|
||||
|
||||
public String getOsVersion() {
|
||||
return osVersion;
|
||||
}
|
||||
|
||||
public void setOsVersion(String osVersion) {
|
||||
this.osVersion = osVersion;
|
||||
}
|
||||
|
||||
public String getJavaVersion() {
|
||||
return javaVersion;
|
||||
}
|
||||
|
||||
public void setJavaVersion(String javaVersion) {
|
||||
this.javaVersion = javaVersion;
|
||||
}
|
||||
|
||||
public String getJavaHome() {
|
||||
return javaHome;
|
||||
}
|
||||
|
||||
public void setJavaHome(String javaHome) {
|
||||
this.javaHome = javaHome;
|
||||
}
|
||||
|
||||
public double getSystemLoadAverage() {
|
||||
return systemLoadAverage;
|
||||
}
|
||||
|
||||
public void setSystemLoadAverage(double systemLoadAverage) {
|
||||
this.systemLoadAverage = systemLoadAverage;
|
||||
}
|
||||
|
||||
public int getProcessors() {
|
||||
return processors;
|
||||
}
|
||||
|
||||
public void setProcessors(int processors) {
|
||||
this.processors = processors;
|
||||
}
|
||||
|
||||
public long getUptime() {
|
||||
return uptime;
|
||||
}
|
||||
|
||||
public void setUptime(long uptime) {
|
||||
this.uptime = uptime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.lang.management.ThreadInfo;
|
||||
|
||||
/**
|
||||
* ThreadInfo with cpuUsage
|
||||
*
|
||||
* @author gongdewei 2020/4/26
|
||||
*/
|
||||
public class ThreadCpuInfo {
|
||||
|
||||
private ThreadInfo threadInfo;
|
||||
|
||||
private long cpuUsage;
|
||||
|
||||
public ThreadCpuInfo(ThreadInfo threadInfo, long cpuUsage) {
|
||||
this.threadInfo = threadInfo;
|
||||
this.cpuUsage = cpuUsage;
|
||||
}
|
||||
|
||||
public long getCpuUsage() {
|
||||
return cpuUsage;
|
||||
}
|
||||
|
||||
public void setCpuUsage(long cpuUsage) {
|
||||
this.cpuUsage = cpuUsage;
|
||||
}
|
||||
|
||||
public ThreadInfo threadInfo() {
|
||||
return threadInfo;
|
||||
}
|
||||
|
||||
public void setThreadInfo(ThreadInfo threadInfo) {
|
||||
this.threadInfo = threadInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Model of 'thread' command
|
||||
*
|
||||
* @author gongdewei 2020/4/26
|
||||
*/
|
||||
public class ThreadModel extends ResultModel {
|
||||
|
||||
//single thread: thread 12
|
||||
private ThreadInfo threadInfo;
|
||||
|
||||
//thread -b
|
||||
private BlockingLockInfo blockingLockInfo;
|
||||
|
||||
//thread -n 5
|
||||
private ThreadCpuInfo[] busyThreads;
|
||||
|
||||
//thread stats
|
||||
private List<ThreadVO> threadStats;
|
||||
private Map<Thread.State, Integer> threadStateCount;
|
||||
|
||||
public ThreadModel() {
|
||||
}
|
||||
|
||||
public ThreadModel(ThreadInfo threadInfo) {
|
||||
this.threadInfo = threadInfo;
|
||||
}
|
||||
|
||||
public ThreadModel(BlockingLockInfo blockingLockInfo) {
|
||||
this.blockingLockInfo = blockingLockInfo;
|
||||
}
|
||||
|
||||
public ThreadModel(ThreadCpuInfo[] busyThreads) {
|
||||
this.busyThreads = busyThreads;
|
||||
}
|
||||
|
||||
public ThreadModel(List<ThreadVO> threadStats, Map<Thread.State, Integer> threadStateCount) {
|
||||
this.threadStats = threadStats;
|
||||
this.threadStateCount = threadStateCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "thread";
|
||||
}
|
||||
|
||||
public ThreadInfo getThreadInfo() {
|
||||
return threadInfo;
|
||||
}
|
||||
|
||||
public void setThreadInfo(ThreadInfo threadInfo) {
|
||||
this.threadInfo = threadInfo;
|
||||
}
|
||||
|
||||
public BlockingLockInfo getBlockingLockInfo() {
|
||||
return blockingLockInfo;
|
||||
}
|
||||
|
||||
public void setBlockingLockInfo(BlockingLockInfo blockingLockInfo) {
|
||||
this.blockingLockInfo = blockingLockInfo;
|
||||
}
|
||||
|
||||
public ThreadCpuInfo[] getBusyThreads() {
|
||||
return busyThreads;
|
||||
}
|
||||
|
||||
public void setBusyThreads(ThreadCpuInfo[] busyThreads) {
|
||||
this.busyThreads = busyThreads;
|
||||
}
|
||||
|
||||
public List<ThreadVO> getThreadStats() {
|
||||
return threadStats;
|
||||
}
|
||||
|
||||
public void setThreadStats(List<ThreadVO> threadStats) {
|
||||
this.threadStats = threadStats;
|
||||
}
|
||||
|
||||
public Map<Thread.State, Integer> getThreadStateCount() {
|
||||
return threadStateCount;
|
||||
}
|
||||
|
||||
public void setThreadStateCount(Map<Thread.State, Integer> threadStateCount) {
|
||||
this.threadStateCount = threadStateCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.lang.Thread.State;
|
||||
|
||||
/**
|
||||
* Thread VO of 'dashboard' and 'thread' command
|
||||
*
|
||||
* @author gongdewei 2020/4/22
|
||||
*/
|
||||
public class ThreadVO {
|
||||
private long id;
|
||||
private String name;
|
||||
private String group;
|
||||
private int priority;
|
||||
private State state;
|
||||
private long cpu;
|
||||
private long time;
|
||||
private boolean interrupted;
|
||||
private boolean daemon;
|
||||
|
||||
public ThreadVO() {
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public long getCpu() {
|
||||
return cpu;
|
||||
}
|
||||
|
||||
public void setCpu(long cpu) {
|
||||
this.cpu = cpu;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public boolean isInterrupted() {
|
||||
return interrupted;
|
||||
}
|
||||
|
||||
public void setInterrupted(boolean interrupted) {
|
||||
this.interrupted = interrupted;
|
||||
}
|
||||
|
||||
public boolean isDaemon() {
|
||||
return daemon;
|
||||
}
|
||||
|
||||
public void setDaemon(boolean daemon) {
|
||||
this.daemon = daemon;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.taobao.arthas.core.command.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tomcat info of 'dashboard' command
|
||||
*
|
||||
* @author gongdewei 2020/4/23
|
||||
*/
|
||||
public class TomcatInfoVO {
|
||||
|
||||
private List<ConnectorStats> connectorStats;
|
||||
private List<ThreadPool> threadPools;
|
||||
|
||||
public TomcatInfoVO() {
|
||||
}
|
||||
|
||||
public List<ConnectorStats> getConnectorStats() {
|
||||
return connectorStats;
|
||||
}
|
||||
|
||||
public void setConnectorStats(List<ConnectorStats> connectorStats) {
|
||||
this.connectorStats = connectorStats;
|
||||
}
|
||||
|
||||
public List<ThreadPool> getThreadPools() {
|
||||
return threadPools;
|
||||
}
|
||||
|
||||
public void setThreadPools(List<ThreadPool> threadPools) {
|
||||
this.threadPools = threadPools;
|
||||
}
|
||||
|
||||
public static class ConnectorStats {
|
||||
private String name;
|
||||
private double qps;
|
||||
private double rt;
|
||||
private double error;
|
||||
private long received;
|
||||
private long sent;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public double getQps() {
|
||||
return qps;
|
||||
}
|
||||
|
||||
public void setQps(double qps) {
|
||||
this.qps = qps;
|
||||
}
|
||||
|
||||
public double getRt() {
|
||||
return rt;
|
||||
}
|
||||
|
||||
public void setRt(double rt) {
|
||||
this.rt = rt;
|
||||
}
|
||||
|
||||
public double getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(double error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public long getReceived() {
|
||||
return received;
|
||||
}
|
||||
|
||||
public void setReceived(long received) {
|
||||
this.received = received;
|
||||
}
|
||||
|
||||
public long getSent() {
|
||||
return sent;
|
||||
}
|
||||
|
||||
public void setSent(long sent) {
|
||||
this.sent = sent;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ThreadPool {
|
||||
private String name;
|
||||
private long busy;
|
||||
private long total;
|
||||
|
||||
public ThreadPool() {
|
||||
}
|
||||
|
||||
public ThreadPool(String name, long busy, long total) {
|
||||
this.name = name;
|
||||
this.busy = busy;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getBusy() {
|
||||
return busy;
|
||||
}
|
||||
|
||||
public void setBusy(long busy) {
|
||||
this.busy = busy;
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(long total) {
|
||||
this.total = total;
|
||||
}
|
||||
}
|
||||
}
|
||||
+134
-209
@@ -5,6 +5,11 @@ import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.taobao.arthas.core.command.Constants;
|
||||
import com.taobao.arthas.core.command.model.DashboardModel;
|
||||
import com.taobao.arthas.core.command.model.GcInfoVO;
|
||||
import com.taobao.arthas.core.command.model.MemoryEntryVO;
|
||||
import com.taobao.arthas.core.command.model.RuntimeInfoVO;
|
||||
import com.taobao.arthas.core.command.model.TomcatInfoVO;
|
||||
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.shell.handlers.Handler;
|
||||
@@ -18,13 +23,6 @@ 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.text.Color;
|
||||
import com.taobao.text.Decoration;
|
||||
import com.taobao.text.Style;
|
||||
import com.taobao.text.renderers.ThreadRenderer;
|
||||
import com.taobao.text.ui.RowElement;
|
||||
import com.taobao.text.ui.TableElement;
|
||||
import com.taobao.text.util.RenderUtil;
|
||||
|
||||
import java.lang.management.BufferPoolMXBean;
|
||||
import java.lang.management.GarbageCollectorMXBean;
|
||||
@@ -32,11 +30,17 @@ import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryPoolMXBean;
|
||||
import java.lang.management.MemoryType;
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import static com.taobao.arthas.core.command.model.MemoryEntryVO.TYPE_BUFFER_POOL;
|
||||
import static com.taobao.arthas.core.command.model.MemoryEntryVO.TYPE_HEAP;
|
||||
import static com.taobao.arthas.core.command.model.MemoryEntryVO.TYPE_NON_HEAP;
|
||||
|
||||
/**
|
||||
* @author hengyunabc 2015年11月19日 上午11:57:21
|
||||
*/
|
||||
@@ -140,8 +144,43 @@ public class DashboardCommand extends AnnotatedCommand {
|
||||
return name.replace(' ', '_').toLowerCase();
|
||||
}
|
||||
|
||||
private static void addBufferPoolMemoryInfo(TableElement table) {
|
||||
private static void addMemoryInfo(DashboardModel dashboardModel) {
|
||||
List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
|
||||
Map<String, List<MemoryEntryVO>> memoryInfoMap = new LinkedHashMap<String, List<MemoryEntryVO>>();
|
||||
dashboardModel.setMemoryInfo(memoryInfoMap);
|
||||
|
||||
//heap
|
||||
MemoryUsage heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
|
||||
List<MemoryEntryVO> heapMemEntries = new ArrayList<MemoryEntryVO>();
|
||||
heapMemEntries.add(createMemoryEntryVO(TYPE_HEAP, TYPE_HEAP, heapMemoryUsage));
|
||||
for (MemoryPoolMXBean poolMXBean : memoryPoolMXBeans) {
|
||||
if (MemoryType.HEAP.equals(poolMXBean.getType())) {
|
||||
MemoryUsage usage = poolMXBean.getUsage();
|
||||
String poolName = beautifyName(poolMXBean.getName());
|
||||
heapMemEntries.add(createMemoryEntryVO(TYPE_HEAP, poolName, usage));
|
||||
}
|
||||
}
|
||||
memoryInfoMap.put(TYPE_HEAP, heapMemEntries);
|
||||
|
||||
//non-heap
|
||||
MemoryUsage nonHeapMemoryUsage = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
|
||||
List<MemoryEntryVO> nonheapMemEntries = new ArrayList<MemoryEntryVO>();
|
||||
nonheapMemEntries.add(createMemoryEntryVO(TYPE_NON_HEAP, TYPE_NON_HEAP, nonHeapMemoryUsage));
|
||||
for (MemoryPoolMXBean poolMXBean : memoryPoolMXBeans) {
|
||||
if (MemoryType.NON_HEAP.equals(poolMXBean.getType())) {
|
||||
MemoryUsage usage = poolMXBean.getUsage();
|
||||
String poolName = beautifyName(poolMXBean.getName());
|
||||
nonheapMemEntries.add(createMemoryEntryVO(TYPE_NON_HEAP, poolName, usage));
|
||||
}
|
||||
}
|
||||
memoryInfoMap.put(TYPE_NON_HEAP, nonheapMemEntries);
|
||||
|
||||
addBufferPoolMemoryInfo(memoryInfoMap);
|
||||
}
|
||||
|
||||
private static void addBufferPoolMemoryInfo(Map<String, List<MemoryEntryVO>> memoryInfoMap) {
|
||||
try {
|
||||
List<MemoryEntryVO> bufferPoolMemEntries = new ArrayList<MemoryEntryVO>();
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class bufferPoolMXBeanClass = Class.forName("java.lang.management.BufferPoolMXBean");
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -149,84 +188,57 @@ public class DashboardCommand extends AnnotatedCommand {
|
||||
for (BufferPoolMXBean mbean : bufferPoolMXBeans) {
|
||||
long used = mbean.getMemoryUsed();
|
||||
long total = mbean.getTotalCapacity();
|
||||
new MemoryEntry(mbean.getName(), used, total, Long.MIN_VALUE).addTableRow(table);
|
||||
bufferPoolMemEntries.add(new MemoryEntryVO(TYPE_BUFFER_POOL, mbean.getName(), used, total, Long.MIN_VALUE));
|
||||
}
|
||||
memoryInfoMap.put(TYPE_BUFFER_POOL, bufferPoolMemEntries);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private static void addRuntimeInfo(TableElement table) {
|
||||
table.row("os.name", System.getProperty("os.name"));
|
||||
table.row("os.version", System.getProperty("os.version"));
|
||||
table.row("java.version", System.getProperty("java.version"));
|
||||
table.row("java.home", System.getProperty("java.home"));
|
||||
table.row("systemload.average",
|
||||
String.format("%.2f", ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage()));
|
||||
table.row("processors", "" + Runtime.getRuntime().availableProcessors());
|
||||
table.row("uptime", "" + ManagementFactory.getRuntimeMXBean().getUptime() / 1000 + "s");
|
||||
private static void addRuntimeInfo(DashboardModel dashboardModel) {
|
||||
RuntimeInfoVO runtimeInfo = new RuntimeInfoVO();
|
||||
runtimeInfo.setOsName(System.getProperty("os.name"));
|
||||
runtimeInfo.setOsVersion(System.getProperty("os.version"));
|
||||
runtimeInfo.setJavaVersion(System.getProperty("java.version"));
|
||||
runtimeInfo.setJavaHome(System.getProperty("java.home"));
|
||||
runtimeInfo.setSystemLoadAverage(ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage());
|
||||
runtimeInfo.setProcessors(Runtime.getRuntime().availableProcessors());
|
||||
runtimeInfo.setUptime(ManagementFactory.getRuntimeMXBean().getUptime() / 1000);
|
||||
dashboardModel.setRuntimeInfo(runtimeInfo);
|
||||
}
|
||||
|
||||
private static void addMemoryInfo(TableElement table) {
|
||||
MemoryUsage heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
|
||||
MemoryUsage nonHeapMemoryUsage = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
|
||||
|
||||
List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
|
||||
|
||||
new MemoryEntry("heap", heapMemoryUsage).addTableRow(table, Decoration.bold.bold());
|
||||
for (MemoryPoolMXBean poolMXBean : memoryPoolMXBeans) {
|
||||
if (MemoryType.HEAP.equals(poolMXBean.getType())) {
|
||||
MemoryUsage usage = poolMXBean.getUsage();
|
||||
String poolName = beautifyName(poolMXBean.getName());
|
||||
new MemoryEntry(poolName, usage).addTableRow(table);
|
||||
}
|
||||
}
|
||||
|
||||
new MemoryEntry("nonheap", nonHeapMemoryUsage).addTableRow(table, Decoration.bold.bold());
|
||||
for (MemoryPoolMXBean poolMXBean : memoryPoolMXBeans) {
|
||||
if (MemoryType.NON_HEAP.equals(poolMXBean.getType())) {
|
||||
MemoryUsage usage = poolMXBean.getUsage();
|
||||
String poolName = beautifyName(poolMXBean.getName());
|
||||
new MemoryEntry(poolName, usage).addTableRow(table);
|
||||
}
|
||||
}
|
||||
|
||||
addBufferPoolMemoryInfo(table);
|
||||
private static MemoryEntryVO createMemoryEntryVO(String type, String name, MemoryUsage memoryUsage) {
|
||||
return new MemoryEntryVO(type, name, memoryUsage.getUsed(), memoryUsage.getCommitted(), memoryUsage.getMax());
|
||||
}
|
||||
|
||||
private static void addGcInfo(TableElement table) {
|
||||
private static void addGcInfo(DashboardModel dashboardModel) {
|
||||
List<GcInfoVO> gcInfos = new ArrayList<GcInfoVO>();
|
||||
dashboardModel.setGcInfos(gcInfos);
|
||||
|
||||
List<GarbageCollectorMXBean> garbageCollectorMxBeans = ManagementFactory.getGarbageCollectorMXBeans();
|
||||
for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMxBeans) {
|
||||
String name = garbageCollectorMXBean.getName();
|
||||
table.add(new RowElement().style(Decoration.bold.bold()).add("gc." + beautifyName(name) + ".count",
|
||||
"" + garbageCollectorMXBean.getCollectionCount()));
|
||||
table.row("gc." + beautifyName(name) + ".time(ms)", "" + garbageCollectorMXBean.getCollectionTime());
|
||||
for (GarbageCollectorMXBean gcMXBean : garbageCollectorMxBeans) {
|
||||
String name = gcMXBean.getName();
|
||||
gcInfos.add(new GcInfoVO(beautifyName(name), gcMXBean.getCollectionCount(), gcMXBean.getCollectionTime()));
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatBytes(long size) {
|
||||
int unit = 1;
|
||||
String unitStr = "B";
|
||||
if (size / 1024 > 0) {
|
||||
unit = 1024;
|
||||
unitStr = "K";
|
||||
} else if (size / 1024 / 1024 > 0) {
|
||||
unit = 1024 * 1024;
|
||||
unitStr = "M";
|
||||
private void addTomcatInfo(DashboardModel dashboardModel) {
|
||||
// 如果请求tomcat信息失败,则不显示tomcat信息
|
||||
if (!NetUtils.request("http://localhost:8006").isSuccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return String.format("%d%s", size / unit, unitStr);
|
||||
}
|
||||
|
||||
private void addTomcatInfo(TableElement table) {
|
||||
|
||||
TomcatInfoVO tomcatInfoVO = new TomcatInfoVO();
|
||||
dashboardModel.setTomcatInfo(tomcatInfoVO);
|
||||
String threadPoolPath = "http://localhost:8006/connector/threadpool";
|
||||
String connectorStatPath = "http://localhost:8006/connector/stats";
|
||||
Response connectorStatResponse = NetUtils.request(connectorStatPath);
|
||||
if (connectorStatResponse.isSuccess()) {
|
||||
List<JSONObject> connectorStats = JSON.parseArray(connectorStatResponse.getContent(), JSONObject.class);
|
||||
for (JSONObject stat : connectorStats) {
|
||||
String name = stat.getString("name").replace("\"", "");
|
||||
List<TomcatInfoVO.ConnectorStats> connectorStats = new ArrayList<TomcatInfoVO.ConnectorStats>();
|
||||
List<JSONObject> tomcatConnectorStats = JSON.parseArray(connectorStatResponse.getContent(), JSONObject.class);
|
||||
for (JSONObject stat : tomcatConnectorStats) {
|
||||
String connectorName = stat.getString("name").replace("\"", "");
|
||||
long bytesReceived = stat.getLongValue("bytesReceived");
|
||||
long bytesSent = stat.getLongValue("bytesSent");
|
||||
long processingTime = stat.getLongValue("processingTime");
|
||||
@@ -238,173 +250,86 @@ public class DashboardCommand extends AnnotatedCommand {
|
||||
tomcatReceivedBytesCounter.update(bytesReceived);
|
||||
tomcatSentBytesCounter.update(bytesSent);
|
||||
|
||||
table.add(new RowElement().style(Decoration.bold.bold()).add("connector", name));
|
||||
table.row("QPS", String.format("%.2f", tomcatRequestCounter.rate()));
|
||||
table.row("RT(ms)", String.format("%.2f", processingTime / (double) requestCount));
|
||||
table.row("error/s", String.format("%.2f", tomcatErrorCounter.rate()));
|
||||
table.row("received/s", formatBytes((long) tomcatReceivedBytesCounter.rate()));
|
||||
table.row("sent/s", formatBytes((long) tomcatSentBytesCounter.rate()));
|
||||
double qps = tomcatRequestCounter.rate();
|
||||
double rt = processingTime / (double) requestCount;
|
||||
double errorRate = tomcatErrorCounter.rate();
|
||||
long receivedBytesRate = new Double(tomcatReceivedBytesCounter.rate()).longValue();
|
||||
long sentBytesRate = new Double(tomcatSentBytesCounter.rate()).longValue();
|
||||
|
||||
TomcatInfoVO.ConnectorStats connectorStat = new TomcatInfoVO.ConnectorStats();
|
||||
connectorStat.setName(connectorName);
|
||||
connectorStat.setQps(qps);
|
||||
connectorStat.setRt(rt);
|
||||
connectorStat.setError(errorRate);
|
||||
connectorStat.setReceived(receivedBytesRate);
|
||||
connectorStat.setSent(sentBytesRate);
|
||||
connectorStats.add(connectorStat);
|
||||
}
|
||||
tomcatInfoVO.setConnectorStats(connectorStats);
|
||||
}
|
||||
|
||||
Response threadPoolResponse = NetUtils.request(threadPoolPath);
|
||||
if (threadPoolResponse.isSuccess()) {
|
||||
List<TomcatInfoVO.ThreadPool> threadPools = new ArrayList<TomcatInfoVO.ThreadPool>();
|
||||
List<JSONObject> threadPoolInfos = JSON.parseArray(threadPoolResponse.getContent(), JSONObject.class);
|
||||
for (JSONObject info : threadPoolInfos) {
|
||||
String name = info.getString("name").replace("\"", "");
|
||||
long busy = info.getLongValue("threadBusy");
|
||||
long total = info.getLongValue("threadCount");
|
||||
table.add(new RowElement().style(Decoration.bold.bold()).add("threadpool", name));
|
||||
table.row("busy", "" + busy);
|
||||
table.row("total", "" + total);
|
||||
threadPools.add(new TomcatInfoVO.ThreadPool(name, busy, total));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String drawThreadInfo(int width, int height) {
|
||||
Map<String, Thread> threads = ThreadUtil.getThreads();
|
||||
return RenderUtil.render(threads.values().iterator(), new ThreadRenderer(), width, height);
|
||||
}
|
||||
|
||||
static String drawMemoryInfoAndGcInfo(int width, int height) {
|
||||
TableElement table = new TableElement(1, 1);
|
||||
|
||||
TableElement memoryInfoTable = new TableElement(3, 1, 1, 1, 1).rightCellPadding(1);
|
||||
memoryInfoTable.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Memory",
|
||||
"used", "total", "max", "usage"));
|
||||
|
||||
addMemoryInfo(memoryInfoTable);
|
||||
|
||||
TableElement gcInfoTable = new TableElement(1, 1).rightCellPadding(1);
|
||||
gcInfoTable.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("GC", ""));
|
||||
addGcInfo(gcInfoTable);
|
||||
|
||||
table.row(memoryInfoTable, gcInfoTable);
|
||||
return RenderUtil.render(table, width, height);
|
||||
}
|
||||
|
||||
String drawRuntimeInfoAndTomcatInfo(int width, int height) {
|
||||
TableElement resultTable = new TableElement(1, 1);
|
||||
|
||||
TableElement runtimeInfoTable = new TableElement(1, 1).rightCellPadding(1);
|
||||
runtimeInfoTable
|
||||
.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Runtime", ""));
|
||||
|
||||
addRuntimeInfo(runtimeInfoTable);
|
||||
|
||||
TableElement tomcatInfoTable = null;
|
||||
|
||||
try {
|
||||
// 如果请求tomcat信息失败,则不显示tomcat信息
|
||||
if (NetUtils.request("http://localhost:8006").isSuccess()) {
|
||||
tomcatInfoTable = new TableElement(1, 1).rightCellPadding(1);
|
||||
tomcatInfoTable
|
||||
.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Tomcat", ""));
|
||||
addTomcatInfo(tomcatInfoTable);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.error("get Tomcat Info error!", t);
|
||||
}
|
||||
|
||||
if (tomcatInfoTable != null) {
|
||||
resultTable.row(runtimeInfoTable, tomcatInfoTable);
|
||||
} else {
|
||||
resultTable = runtimeInfoTable;
|
||||
}
|
||||
|
||||
return RenderUtil.render(resultTable, width, height);
|
||||
}
|
||||
|
||||
static class MemoryEntry {
|
||||
String name;
|
||||
long used;
|
||||
long total;
|
||||
long max;
|
||||
|
||||
int unit;
|
||||
String unitStr;
|
||||
|
||||
public MemoryEntry(String name, long used, long total, long max) {
|
||||
this.name = name;
|
||||
this.used = used;
|
||||
this.total = total;
|
||||
this.max = max;
|
||||
|
||||
unitStr = "K";
|
||||
unit = 1024;
|
||||
if (used / 1024 / 1024 > 0) {
|
||||
unitStr = "M";
|
||||
unit = 1024 * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
public MemoryEntry(String name, MemoryUsage usage) {
|
||||
this(name, usage.getUsed(), usage.getCommitted(), usage.getMax());
|
||||
}
|
||||
|
||||
private String format(long value) {
|
||||
String valueStr = "-";
|
||||
if (value == -1) {
|
||||
return "-1";
|
||||
}
|
||||
if (value != Long.MIN_VALUE) {
|
||||
valueStr = value / unit + unitStr;
|
||||
}
|
||||
return valueStr;
|
||||
}
|
||||
|
||||
public void addTableRow(TableElement table) {
|
||||
double usage = used / (double) (max == -1 || max == Long.MIN_VALUE ? total : max) * 100;
|
||||
|
||||
table.row(name, format(used), format(total), format(max), String.format("%.2f%%", usage));
|
||||
}
|
||||
|
||||
public void addTableRow(TableElement table, Style.Composite style) {
|
||||
double usage = used / (double) (max == -1 || max == Long.MIN_VALUE ? total : max) * 100;
|
||||
|
||||
table.add(new RowElement().style(style).add(name, format(used), format(total), format(max),
|
||||
String.format("%.2f%%", usage)));
|
||||
tomcatInfoVO.setThreadPools(threadPools);
|
||||
}
|
||||
}
|
||||
|
||||
private class DashboardTimerTask extends TimerTask {
|
||||
private CommandProcess process;
|
||||
private ThreadSampler threadSampler;
|
||||
|
||||
public DashboardTimerTask(CommandProcess process) {
|
||||
this.process = process;
|
||||
this.threadSampler = new ThreadSampler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (count >= getNumOfExecutions()) {
|
||||
// stop the timer
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
process.write("Process ends after " + getNumOfExecutions() + " time(s).\n");
|
||||
process.end();
|
||||
return;
|
||||
try {
|
||||
if (count >= getNumOfExecutions()) {
|
||||
// stop the timer
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
process.end(0, "Process ends after " + getNumOfExecutions() + " time(s).");
|
||||
return;
|
||||
}
|
||||
|
||||
DashboardModel dashboardModel = new DashboardModel();
|
||||
|
||||
//thread sample
|
||||
Map<String, Thread> threads = ThreadUtil.getThreads();
|
||||
dashboardModel.setThreads(threadSampler.sample(threads.values()));
|
||||
|
||||
//memory
|
||||
addMemoryInfo(dashboardModel);
|
||||
|
||||
//gc
|
||||
addGcInfo(dashboardModel);
|
||||
|
||||
//runtime
|
||||
addRuntimeInfo(dashboardModel);
|
||||
|
||||
//tomcat
|
||||
addTomcatInfo(dashboardModel);
|
||||
|
||||
process.appendResult(dashboardModel);
|
||||
|
||||
count++;
|
||||
process.times().incrementAndGet();
|
||||
} catch (Throwable e) {
|
||||
String msg = "process dashboard failed: " + e.getMessage();
|
||||
logger.error(msg, e);
|
||||
process.end(-1, msg);
|
||||
}
|
||||
|
||||
int width = process.width();
|
||||
int height = process.height();
|
||||
|
||||
// 上半部分放thread top。下半部分再切分为田字格,其中上面两格放memory, gc的信息。下面两格放tomcat,
|
||||
// runtime的信息
|
||||
int totalHeight = height - 1;
|
||||
int threadTopHeight = totalHeight / 2;
|
||||
int lowerHalf = totalHeight - threadTopHeight;
|
||||
|
||||
int runtimeInfoHeight = lowerHalf / 2;
|
||||
int heapInfoHeight = lowerHalf - runtimeInfoHeight;
|
||||
|
||||
String threadInfo = drawThreadInfo(width, threadTopHeight);
|
||||
String memoryAndGc = drawMemoryInfoAndGcInfo(width, runtimeInfoHeight);
|
||||
String runTimeAndTomcat = drawRuntimeInfoAndTomcatInfo(width, heapInfoHeight);
|
||||
|
||||
process.write(threadInfo + memoryAndGc + runTimeAndTomcat);
|
||||
|
||||
count++;
|
||||
process.times().incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
import com.taobao.arthas.core.command.Constants;
|
||||
import com.taobao.arthas.core.command.model.BlockingLockInfo;
|
||||
import com.taobao.arthas.core.command.model.ThreadCpuInfo;
|
||||
import com.taobao.arthas.core.command.model.ThreadModel;
|
||||
import com.taobao.arthas.core.command.model.ThreadVO;
|
||||
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.shell.command.ExitStatus;
|
||||
import com.taobao.arthas.core.util.ArrayUtils;
|
||||
import com.taobao.arthas.core.util.CommandUtils;
|
||||
import com.taobao.arthas.core.util.StringUtils;
|
||||
import com.taobao.arthas.core.util.ThreadUtil;
|
||||
import com.taobao.arthas.core.util.affect.Affect;
|
||||
import com.taobao.arthas.core.util.affect.RowAffect;
|
||||
import com.taobao.middleware.cli.annotations.Argument;
|
||||
import com.taobao.middleware.cli.annotations.Description;
|
||||
import com.taobao.middleware.cli.annotations.Name;
|
||||
import com.taobao.middleware.cli.annotations.Option;
|
||||
import com.taobao.middleware.cli.annotations.Summary;
|
||||
import com.taobao.text.renderers.ThreadRenderer;
|
||||
import com.taobao.text.ui.LabelElement;
|
||||
import com.taobao.text.util.RenderUtil;
|
||||
|
||||
import java.lang.Thread.State;
|
||||
import java.lang.management.ManagementFactory;
|
||||
@@ -23,8 +24,9 @@ import java.lang.management.ThreadInfo;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -106,31 +108,24 @@ public class ThreadCommand extends AnnotatedCommand {
|
||||
|
||||
@Override
|
||||
public void process(CommandProcess process) {
|
||||
Affect affect = new RowAffect();
|
||||
int status = 0;
|
||||
try {
|
||||
if (id > 0) {
|
||||
status = processThread(process);
|
||||
} else if (topNBusy != null) {
|
||||
status = processTopBusyThreads(process);
|
||||
} else if (findMostBlockingThread) {
|
||||
status = processBlockingThread(process);
|
||||
} else {
|
||||
status = processAllThreads(process);
|
||||
}
|
||||
} finally {
|
||||
process.write(affect + "\n");
|
||||
process.end(status);
|
||||
ExitStatus exitStatus;
|
||||
if (id > 0) {
|
||||
exitStatus = processThread(process);
|
||||
} else if (topNBusy != null) {
|
||||
exitStatus = processTopBusyThreads(process);
|
||||
} else if (findMostBlockingThread) {
|
||||
exitStatus = processBlockingThread(process);
|
||||
} else {
|
||||
exitStatus = processAllThreads(process);
|
||||
}
|
||||
CommandUtils.end(process, exitStatus);
|
||||
}
|
||||
|
||||
private int processAllThreads(CommandProcess process) {
|
||||
int status = 0;
|
||||
private ExitStatus processAllThreads(CommandProcess process) {
|
||||
Map<String, Thread> threads = ThreadUtil.getThreads();
|
||||
|
||||
// 统计各种线程状态
|
||||
StringBuilder threadStat = new StringBuilder();
|
||||
Map<State, Integer> stateCountMap = new HashMap<State, Integer>();
|
||||
Map<State, Integer> stateCountMap = new LinkedHashMap<State, Integer>();
|
||||
for (State s : State.values()) {
|
||||
stateCountMap.put(s, 0);
|
||||
}
|
||||
@@ -141,81 +136,65 @@ public class ThreadCommand extends AnnotatedCommand {
|
||||
stateCountMap.put(threadState, count + 1);
|
||||
}
|
||||
|
||||
threadStat.append("Threads Total: ").append(threads.values().size());
|
||||
|
||||
for (State s : State.values()) {
|
||||
Integer count = stateCountMap.get(s);
|
||||
threadStat.append(", ").append(s.name()).append(": ").append(count);
|
||||
}
|
||||
|
||||
String stat = RenderUtil.render(new LabelElement(threadStat), process.width());
|
||||
|
||||
Collection<Thread> resultThreads = new ArrayList<Thread>();
|
||||
if (!StringUtils.isEmpty(this.state)){
|
||||
if (!StringUtils.isEmpty(this.state)) {
|
||||
this.state = this.state.toUpperCase();
|
||||
if(states.contains(this.state)) {
|
||||
if (states.contains(this.state)) {
|
||||
for (Thread thread : threads.values()) {
|
||||
if (state.equals(thread.getState().name())) {
|
||||
resultThreads.add(thread);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
process.write("Illegal argument, state should be one of " + states + "\n");
|
||||
status = 1;
|
||||
return status;
|
||||
} else {
|
||||
return ExitStatus.failure(1, "Illegal argument, state should be one of " + states);
|
||||
}
|
||||
} else {
|
||||
resultThreads = threads.values();
|
||||
}
|
||||
String content = RenderUtil.render(resultThreads.iterator(),
|
||||
new ThreadRenderer(sampleInterval), process.width());
|
||||
process.write(stat + content);
|
||||
return status;
|
||||
|
||||
//thread stats
|
||||
ThreadSampler threadSampler = new ThreadSampler();
|
||||
threadSampler.setSampleInterval(sampleInterval);
|
||||
List<ThreadVO> threadStats = threadSampler.sample(resultThreads);
|
||||
|
||||
process.appendResult(new ThreadModel(threadStats, stateCountMap));
|
||||
return ExitStatus.success();
|
||||
}
|
||||
|
||||
private int processBlockingThread(CommandProcess process) {
|
||||
int status = 0;
|
||||
ThreadUtil.BlockingLockInfo blockingLockInfo = ThreadUtil.findMostBlockingLock();
|
||||
|
||||
if (blockingLockInfo.threadInfo == null) {
|
||||
process.write("No most blocking thread found!\n");
|
||||
status = 1;
|
||||
} else {
|
||||
String stacktrace = ThreadUtil.getFullStacktrace(blockingLockInfo);
|
||||
process.write(stacktrace);
|
||||
private ExitStatus processBlockingThread(CommandProcess process) {
|
||||
BlockingLockInfo blockingLockInfo = ThreadUtil.findMostBlockingLock();
|
||||
if (blockingLockInfo.getThreadInfo() == null) {
|
||||
return ExitStatus.failure(1, "No most blocking thread found!");
|
||||
}
|
||||
return status;
|
||||
process.appendResult(new ThreadModel(blockingLockInfo));
|
||||
return ExitStatus.success();
|
||||
}
|
||||
|
||||
private int processTopBusyThreads(CommandProcess process) {
|
||||
int status = 0;
|
||||
private ExitStatus processTopBusyThreads(CommandProcess process) {
|
||||
Map<Long, Long> topNThreads = ThreadUtil.getTopNThreads(sampleInterval, topNBusy);
|
||||
Long[] tids = topNThreads.keySet().toArray(new Long[0]);
|
||||
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(ArrayUtils.toPrimitive(tids), lockedMonitors, lockedSynchronizers);
|
||||
if (threadInfos == null) {
|
||||
process.write("thread do not exist! id: " + id + "\n");
|
||||
status = 1;
|
||||
} else {
|
||||
for (ThreadInfo info : threadInfos) {
|
||||
String stacktrace = ThreadUtil.getFullStacktrace(info, topNThreads.get(info.getThreadId()));
|
||||
process.write(stacktrace + "\n");
|
||||
}
|
||||
return ExitStatus.failure(1, "get top busy threads failed");
|
||||
}
|
||||
return status;
|
||||
|
||||
//threadInfo with cpuUsage
|
||||
ThreadCpuInfo[] threadCpuInfos = new ThreadCpuInfo[threadInfos.length];
|
||||
for (int i = 0; i < threadInfos.length; i++) {
|
||||
ThreadInfo threadInfo = threadInfos[i];
|
||||
threadCpuInfos[i] = new ThreadCpuInfo(threadInfo, topNThreads.get(threadInfo.getThreadId()));
|
||||
}
|
||||
process.appendResult(new ThreadModel(threadCpuInfos));
|
||||
return ExitStatus.success();
|
||||
}
|
||||
|
||||
private int processThread(CommandProcess process) {
|
||||
int status = 0;
|
||||
String content;
|
||||
private ExitStatus processThread(CommandProcess process) {
|
||||
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(new long[]{id}, lockedMonitors, lockedSynchronizers);
|
||||
if (threadInfos == null || threadInfos[0] == null) {
|
||||
content = "thread do not exist! id: " + id + "\n";
|
||||
status = 1;
|
||||
} else {
|
||||
// no cpu usage info
|
||||
content = ThreadUtil.getFullStacktrace(threadInfos[0], -1);
|
||||
if (threadInfos == null || threadInfos.length < 1 || threadInfos[0] == null) {
|
||||
return ExitStatus.failure(1, "thread do not exist! id: " + id);
|
||||
}
|
||||
process.write(content);
|
||||
return status;
|
||||
|
||||
process.appendResult(new ThreadModel(threadInfos[0]));
|
||||
return ExitStatus.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.taobao.arthas.core.command.monitor200;
|
||||
|
||||
import com.taobao.arthas.core.command.model.ThreadVO;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Thread cpu sampler
|
||||
*
|
||||
* @author gongdewei 2020/4/23
|
||||
*/
|
||||
public class ThreadSampler {
|
||||
|
||||
private long sampleInterval = 100;
|
||||
|
||||
public List<ThreadVO> sample(Collection<Thread> originThreads) {
|
||||
|
||||
List<Thread> threads = new ArrayList<Thread>(originThreads);
|
||||
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
|
||||
|
||||
// Sample CPU
|
||||
Map<Long, Long> times1 = new HashMap<Long, Long>();
|
||||
for (Thread thread : threads) {
|
||||
long cpu = threadMXBean.getThreadCpuTime(thread.getId());
|
||||
times1.put(thread.getId(), cpu);
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(sampleInterval);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
// Resample
|
||||
Map<Long, Long> times2 = new HashMap<Long, Long>(threads.size());
|
||||
for (Thread thread : threads) {
|
||||
long cpu = threadMXBean.getThreadCpuTime(thread.getId());
|
||||
times2.put(thread.getId(), cpu);
|
||||
}
|
||||
|
||||
// Compute delta map and total time
|
||||
long total = 0;
|
||||
Map<Long, Long> deltas = new HashMap<Long, Long>(threads.size());
|
||||
for (Long id : times2.keySet()) {
|
||||
long time1 = times2.get(id);
|
||||
long time2 = times1.get(id);
|
||||
if (time1 == -1) {
|
||||
time1 = time2;
|
||||
} else if (time2 == -1) {
|
||||
time2 = time1;
|
||||
}
|
||||
long delta = time2 - time1;
|
||||
deltas.put(id, delta);
|
||||
total += delta;
|
||||
}
|
||||
|
||||
// Compute cpu
|
||||
final HashMap<Thread, Long> cpus = new HashMap<Thread, Long>(threads.size());
|
||||
for (Thread thread : threads) {
|
||||
long cpu = total == 0 ? 0 : Math.round((deltas.get(thread.getId()) * 100) / total);
|
||||
cpus.put(thread, cpu);
|
||||
}
|
||||
|
||||
// Sort by CPU time : should be a rendering hint...
|
||||
Collections.sort(threads, new Comparator<Thread>() {
|
||||
public int compare(Thread o1, Thread o2) {
|
||||
long l1 = cpus.get(o1);
|
||||
long l2 = cpus.get(o2);
|
||||
if (l1 < l2) {
|
||||
return 1;
|
||||
} else if (l1 > l2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
List<ThreadVO> threadVOList = new ArrayList<ThreadVO>(threads.size());
|
||||
for (Thread thread : threads) {
|
||||
ThreadGroup group = thread.getThreadGroup();
|
||||
long seconds = times2.get(thread.getId()) / 1000000000;
|
||||
//long min = seconds / 60;
|
||||
//String time = min + ":" + (seconds % 60);
|
||||
long cpu = cpus.get(thread);
|
||||
|
||||
ThreadVO threadVO = new ThreadVO();
|
||||
threadVO.setId(thread.getId());
|
||||
threadVO.setName(thread.getName());
|
||||
threadVO.setGroup(group == null ? "" : group.getName());
|
||||
threadVO.setPriority(thread.getPriority());
|
||||
threadVO.setState(thread.getState());
|
||||
threadVO.setCpu(cpu);
|
||||
threadVO.setTime(seconds);
|
||||
threadVO.setInterrupted(thread.isInterrupted());
|
||||
threadVO.setDaemon(thread.isDaemon());
|
||||
threadVOList.add(threadVO);
|
||||
}
|
||||
return threadVOList;
|
||||
}
|
||||
|
||||
public long getSampleInterval() {
|
||||
return sampleInterval;
|
||||
}
|
||||
|
||||
public void setSampleInterval(long sampleInterval) {
|
||||
this.sampleInterval = sampleInterval;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.taobao.arthas.core.command.view;
|
||||
|
||||
import com.taobao.arthas.core.command.model.*;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.text.Color;
|
||||
import com.taobao.text.Decoration;
|
||||
import com.taobao.text.Style;
|
||||
import com.taobao.text.ui.RowElement;
|
||||
import com.taobao.text.ui.TableElement;
|
||||
import com.taobao.text.util.RenderUtil;
|
||||
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* View of 'dashboard' command
|
||||
*
|
||||
* @author gongdewei 2020/4/22
|
||||
*/
|
||||
public class DashboardView extends ResultView<DashboardModel> {
|
||||
|
||||
@Override
|
||||
public void draw(CommandProcess process, DashboardModel result) {
|
||||
int width = process.width();
|
||||
int height = process.height();
|
||||
|
||||
// 上半部分放thread top。下半部分再切分为田字格,其中上面两格放memory, gc的信息。下面两格放tomcat,
|
||||
// runtime的信息
|
||||
int totalHeight = height - 1;
|
||||
int threadTopHeight = totalHeight / 2;
|
||||
int lowerHalf = totalHeight - threadTopHeight;
|
||||
|
||||
int runtimeInfoHeight = lowerHalf / 2;
|
||||
int heapInfoHeight = lowerHalf - runtimeInfoHeight;
|
||||
|
||||
String threadInfo = ViewRenderUtil.drawThreadInfo(result.getThreads(), width, threadTopHeight);
|
||||
String memoryAndGc = drawMemoryInfoAndGcInfo(result.getMemoryInfo(), result.getGcInfos(), width, runtimeInfoHeight);
|
||||
String runTimeAndTomcat = drawRuntimeInfoAndTomcatInfo(result.getRuntimeInfo(), result.getTomcatInfo(), width, heapInfoHeight);
|
||||
|
||||
process.write(threadInfo + memoryAndGc + runTimeAndTomcat);
|
||||
}
|
||||
|
||||
static String drawMemoryInfoAndGcInfo(Map<String, List<MemoryEntryVO>> memoryInfo, List<GcInfoVO> gcInfos, int width, int height) {
|
||||
TableElement table = new TableElement(1, 1);
|
||||
|
||||
TableElement memoryInfoTable = new TableElement(3, 1, 1, 1, 1).rightCellPadding(1);
|
||||
memoryInfoTable.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Memory",
|
||||
"used", "total", "max", "usage"));
|
||||
|
||||
drawMemoryInfo(memoryInfoTable, memoryInfo);
|
||||
|
||||
TableElement gcInfoTable = new TableElement(1, 1).rightCellPadding(1);
|
||||
gcInfoTable.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("GC", ""));
|
||||
drawGcInfo(gcInfoTable, gcInfos);
|
||||
|
||||
table.row(memoryInfoTable, gcInfoTable);
|
||||
return RenderUtil.render(table, width, height);
|
||||
}
|
||||
|
||||
private static void drawMemoryInfo(TableElement table, Map<String, List<MemoryEntryVO>> memoryInfo) {
|
||||
List<MemoryEntryVO> heapMemoryEntries = memoryInfo.get(MemoryEntryVO.TYPE_HEAP);
|
||||
//heap memory
|
||||
for (MemoryEntryVO memoryEntryVO : heapMemoryEntries) {
|
||||
if (MemoryEntryVO.TYPE_HEAP.equals(memoryEntryVO.getName())) {
|
||||
new MemoryEntry(memoryEntryVO).addTableRow(table, Decoration.bold.bold());
|
||||
} else {
|
||||
new MemoryEntry(memoryEntryVO).addTableRow(table);
|
||||
}
|
||||
}
|
||||
|
||||
//non-heap memory
|
||||
List<MemoryEntryVO> nonheapMemoryEntries = memoryInfo.get(MemoryEntryVO.TYPE_NON_HEAP);
|
||||
for (MemoryEntryVO memoryEntryVO : nonheapMemoryEntries) {
|
||||
if (MemoryEntryVO.TYPE_NON_HEAP.equals(memoryEntryVO.getName())) {
|
||||
new MemoryEntry(memoryEntryVO).addTableRow(table, Decoration.bold.bold());
|
||||
} else {
|
||||
new MemoryEntry(memoryEntryVO).addTableRow(table);
|
||||
}
|
||||
}
|
||||
|
||||
//buffer-pool
|
||||
List<MemoryEntryVO> bufferPoolMemoryEntries = memoryInfo.get(MemoryEntryVO.TYPE_BUFFER_POOL);
|
||||
for (MemoryEntryVO memoryEntryVO : bufferPoolMemoryEntries) {
|
||||
new MemoryEntry(memoryEntryVO).addTableRow(table);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawGcInfo(TableElement table, List<GcInfoVO> gcInfos) {
|
||||
for (GcInfoVO gcInfo : gcInfos) {
|
||||
table.add(new RowElement().style(Decoration.bold.bold()).add("gc." + gcInfo.getName() + ".count",
|
||||
"" + gcInfo.getCollectionCount()));
|
||||
table.row("gc." + gcInfo.getName() + ".time(ms)", "" + gcInfo.getCollectionTime());
|
||||
}
|
||||
}
|
||||
|
||||
String drawRuntimeInfoAndTomcatInfo(RuntimeInfoVO runtimeInfo, TomcatInfoVO tomcatInfo, int width, int height) {
|
||||
TableElement resultTable = new TableElement(1, 1);
|
||||
//runtime
|
||||
TableElement runtimeInfoTable = drawRuntimeInfo(runtimeInfo);
|
||||
//tomcat
|
||||
TableElement tomcatInfoTable = drawTomcatInfo(tomcatInfo);
|
||||
|
||||
if (tomcatInfoTable != null) {
|
||||
resultTable.row(runtimeInfoTable, tomcatInfoTable);
|
||||
} else {
|
||||
resultTable = runtimeInfoTable;
|
||||
}
|
||||
return RenderUtil.render(resultTable, width, height);
|
||||
}
|
||||
|
||||
private static TableElement drawRuntimeInfo(RuntimeInfoVO runtimeInfo) {
|
||||
TableElement table = new TableElement(1, 1).rightCellPadding(1);
|
||||
table.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Runtime", ""));
|
||||
|
||||
table.row("os.name", runtimeInfo.getOsName());
|
||||
table.row("os.version", runtimeInfo.getOsVersion());
|
||||
table.row("java.version", runtimeInfo.getJavaVersion());
|
||||
table.row("java.home", runtimeInfo.getJavaHome());
|
||||
table.row("systemload.average", String.format("%.2f", runtimeInfo.getSystemLoadAverage()));
|
||||
table.row("processors", "" + runtimeInfo.getProcessors());
|
||||
table.row("uptime", "" + runtimeInfo.getUptime() + "s");
|
||||
return table;
|
||||
}
|
||||
|
||||
private static String formatBytes(long size) {
|
||||
int unit = 1;
|
||||
String unitStr = "B";
|
||||
if (size / 1024 > 0) {
|
||||
unit = 1024;
|
||||
unitStr = "K";
|
||||
} else if (size / 1024 / 1024 > 0) {
|
||||
unit = 1024 * 1024;
|
||||
unitStr = "M";
|
||||
}
|
||||
|
||||
return String.format("%d%s", size / unit, unitStr);
|
||||
}
|
||||
|
||||
private TableElement drawTomcatInfo(TomcatInfoVO tomcatInfo) {
|
||||
if (tomcatInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//header
|
||||
TableElement table = new TableElement(1, 1).rightCellPadding(1);
|
||||
table.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Tomcat", ""));
|
||||
|
||||
if (tomcatInfo.getConnectorStats() != null) {
|
||||
for (TomcatInfoVO.ConnectorStats connectorStat : tomcatInfo.getConnectorStats()) {
|
||||
table.add(new RowElement().style(Decoration.bold.bold()).add("connector", connectorStat.getName()));
|
||||
table.row("QPS", String.format("%.2f", connectorStat.getQps()));
|
||||
table.row("RT(ms)", String.format("%.2f", connectorStat.getRt()));
|
||||
table.row("error/s", String.format("%.2f", connectorStat.getError()));
|
||||
table.row("received/s", formatBytes(connectorStat.getReceived()));
|
||||
table.row("sent/s", formatBytes(connectorStat.getSent()));
|
||||
}
|
||||
}
|
||||
|
||||
if (tomcatInfo.getThreadPools() != null) {
|
||||
for (TomcatInfoVO.ThreadPool threadPool : tomcatInfo.getThreadPools()) {
|
||||
table.add(new RowElement().style(Decoration.bold.bold()).add("threadpool", threadPool.getName()));
|
||||
table.row("busy", "" + threadPool.getBusy());
|
||||
table.row("total", "" + threadPool.getTotal());
|
||||
}
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
static class MemoryEntry {
|
||||
String name;
|
||||
long used;
|
||||
long total;
|
||||
long max;
|
||||
|
||||
int unit;
|
||||
String unitStr;
|
||||
|
||||
public MemoryEntry(String name, long used, long total, long max) {
|
||||
this.name = name;
|
||||
this.used = used;
|
||||
this.total = total;
|
||||
this.max = max;
|
||||
|
||||
unitStr = "K";
|
||||
unit = 1024;
|
||||
if (used / 1024 / 1024 > 0) {
|
||||
unitStr = "M";
|
||||
unit = 1024 * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
public MemoryEntry(String name, MemoryUsage usage) {
|
||||
this(name, usage.getUsed(), usage.getCommitted(), usage.getMax());
|
||||
}
|
||||
|
||||
public MemoryEntry(MemoryEntryVO memoryEntryVO) {
|
||||
this(memoryEntryVO.getName(), memoryEntryVO.getUsed(), memoryEntryVO.getTotal(), memoryEntryVO.getMax());
|
||||
}
|
||||
|
||||
private String format(long value) {
|
||||
String valueStr = "-";
|
||||
if (value == -1) {
|
||||
return "-1";
|
||||
}
|
||||
if (value != Long.MIN_VALUE) {
|
||||
valueStr = value / unit + unitStr;
|
||||
}
|
||||
return valueStr;
|
||||
}
|
||||
|
||||
public void addTableRow(TableElement table) {
|
||||
double usage = used / (double) (max == -1 || max == Long.MIN_VALUE ? total : max) * 100;
|
||||
|
||||
table.row(name, format(used), format(total), format(max), String.format("%.2f%%", usage));
|
||||
}
|
||||
|
||||
public void addTableRow(TableElement table, Style.Composite style) {
|
||||
double usage = used / (double) (max == -1 || max == Long.MIN_VALUE ? total : max) * 100;
|
||||
|
||||
table.add(new RowElement().style(style).add(name, format(used), format(total), format(max),
|
||||
String.format("%.2f%%", usage)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.taobao.arthas.core.command.view;
|
||||
|
||||
import com.taobao.arthas.core.command.model.ThreadCpuInfo;
|
||||
import com.taobao.arthas.core.command.model.ThreadModel;
|
||||
import com.taobao.arthas.core.command.model.ThreadVO;
|
||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||
import com.taobao.arthas.core.util.ThreadUtil;
|
||||
import com.taobao.text.ui.LabelElement;
|
||||
import com.taobao.text.util.RenderUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* View of 'thread' command
|
||||
*
|
||||
* @author gongdewei 2020/4/26
|
||||
*/
|
||||
public class ThreadView extends ResultView<ThreadModel> {
|
||||
|
||||
@Override
|
||||
public void draw(CommandProcess process, ThreadModel result) {
|
||||
if (result.getThreadInfo() != null) {
|
||||
// no cpu usage info
|
||||
String content = ThreadUtil.getFullStacktrace(result.getThreadInfo(), -1);
|
||||
process.write(content);
|
||||
} else if (result.getBusyThreads() != null) {
|
||||
ThreadCpuInfo[] threadInfos = result.getBusyThreads();
|
||||
for (ThreadCpuInfo info : threadInfos) {
|
||||
String stacktrace = ThreadUtil.getFullStacktrace(info.threadInfo(), info.getCpuUsage());
|
||||
process.write(stacktrace).write("\n");
|
||||
}
|
||||
} else if (result.getBlockingLockInfo() != null) {
|
||||
String stacktrace = ThreadUtil.getFullStacktrace(result.getBlockingLockInfo());
|
||||
process.write(stacktrace);
|
||||
|
||||
} else if (result.getThreadStateCount() != null) {
|
||||
Map<Thread.State, Integer> threadStateCount = result.getThreadStateCount();
|
||||
List<ThreadVO> threadStats = result.getThreadStats();
|
||||
|
||||
//sum total thread count
|
||||
int total = 0;
|
||||
for (Integer value : threadStateCount.values()) {
|
||||
total += value;
|
||||
}
|
||||
StringBuilder threadStat = new StringBuilder();
|
||||
threadStat.append("Threads Total: ").append(total);
|
||||
|
||||
for (Thread.State s : Thread.State.values()) {
|
||||
Integer count = threadStateCount.get(s);
|
||||
threadStat.append(", ").append(s.name()).append(": ").append(count);
|
||||
}
|
||||
String stat = RenderUtil.render(new LabelElement(threadStat), process.width());
|
||||
|
||||
//thread stats
|
||||
int height = Math.max(5, process.height() - 2);
|
||||
//remove blank lines
|
||||
height = Math.min(height, threadStats.size() + 2);
|
||||
String content = ViewRenderUtil.drawThreadInfo(threadStats, process.width(), height);
|
||||
process.write(stat + content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,18 @@ package com.taobao.arthas.core.command.view;
|
||||
|
||||
import com.taobao.arthas.core.command.model.ChangeResultVO;
|
||||
import com.taobao.arthas.core.command.model.EnhancerAffectVO;
|
||||
import com.taobao.arthas.core.command.model.ThreadVO;
|
||||
import com.taobao.arthas.core.util.StringUtils;
|
||||
import com.taobao.text.Color;
|
||||
import com.taobao.text.Decoration;
|
||||
import com.taobao.text.Style;
|
||||
import com.taobao.text.ui.LabelElement;
|
||||
import com.taobao.text.ui.Overflow;
|
||||
import com.taobao.text.ui.RowElement;
|
||||
import com.taobao.text.ui.TableElement;
|
||||
import com.taobao.text.util.RenderUtil;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -19,6 +26,17 @@ import static java.lang.String.format;
|
||||
*/
|
||||
public class ViewRenderUtil {
|
||||
|
||||
/** Thread State Colors */
|
||||
public static final EnumMap<Thread.State, Color> colorMapping = new EnumMap<Thread.State, Color>(Thread.State.class);
|
||||
static {
|
||||
colorMapping.put(Thread.State.NEW, Color.cyan);
|
||||
colorMapping.put(Thread.State.RUNNABLE, Color.green);
|
||||
colorMapping.put(Thread.State.BLOCKED, Color.red);
|
||||
colorMapping.put(Thread.State.WAITING, Color.yellow);
|
||||
colorMapping.put(Thread.State.TIMED_WAITING, Color.magenta);
|
||||
colorMapping.put(Thread.State.TERMINATED, Color.blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render key-value table
|
||||
* @param map
|
||||
@@ -85,4 +103,48 @@ public class ViewRenderUtil {
|
||||
|
||||
return infoSB.toString();
|
||||
}
|
||||
|
||||
public static String drawThreadInfo(List<ThreadVO> threads, int width, int height) {
|
||||
TableElement table = new TableElement(1, 3, 2, 1, 1, 1, 1, 1, 1).overflow(Overflow.HIDDEN).rightCellPadding(1);
|
||||
|
||||
// Header
|
||||
table.add(
|
||||
new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add(
|
||||
"ID",
|
||||
"NAME",
|
||||
"GROUP",
|
||||
"PRIORITY",
|
||||
"STATE",
|
||||
"%CPU",
|
||||
"TIME",
|
||||
"INTERRUPTED",
|
||||
"DAEMON"
|
||||
)
|
||||
);
|
||||
|
||||
for (ThreadVO thread : threads) {
|
||||
Color color = colorMapping.get(thread.getState());
|
||||
long seconds = thread.getTime();
|
||||
long min = seconds / 60;
|
||||
String time = min + ":" + (seconds % 60);
|
||||
long cpu = thread.getCpu();
|
||||
|
||||
LabelElement daemonLabel = new LabelElement(thread.isDaemon());
|
||||
if (!thread.isDaemon()) {
|
||||
daemonLabel.setStyle(Style.style(Color.magenta));
|
||||
}
|
||||
table.row(
|
||||
new LabelElement(thread.getId()),
|
||||
new LabelElement(thread.getName()),
|
||||
new LabelElement(thread.getGroup()),
|
||||
new LabelElement(thread.getPriority()),
|
||||
new LabelElement(thread.getState()).style(color.fg()),
|
||||
new LabelElement(cpu),
|
||||
new LabelElement(time),
|
||||
new LabelElement(thread.isInterrupted()),
|
||||
daemonLabel
|
||||
);
|
||||
}
|
||||
return RenderUtil.render(table, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.taobao.arthas.core.util;
|
||||
|
||||
import com.taobao.arthas.core.command.model.BlockingLockInfo;
|
||||
import com.taobao.arthas.core.view.Ansi;
|
||||
|
||||
import java.arthas.SpyAPI;
|
||||
@@ -221,9 +222,9 @@ abstract public class ThreadUtil {
|
||||
}
|
||||
|
||||
BlockingLockInfo blockingLockInfo = new BlockingLockInfo();
|
||||
blockingLockInfo.threadInfo = ownerThreadPerLock.get(mostBlockingLock);
|
||||
blockingLockInfo.lockIdentityHashCode = mostBlockingLock;
|
||||
blockingLockInfo.blockingThreadCount = blockCountPerLock.get(mostBlockingLock);
|
||||
blockingLockInfo.setThreadInfo(ownerThreadPerLock.get(mostBlockingLock));
|
||||
blockingLockInfo.setLockIdentityHashCode(mostBlockingLock);
|
||||
blockingLockInfo.setBlockingThreadCount(blockCountPerLock.get(mostBlockingLock));
|
||||
return blockingLockInfo;
|
||||
}
|
||||
|
||||
@@ -234,8 +235,8 @@ abstract public class ThreadUtil {
|
||||
|
||||
|
||||
public static String getFullStacktrace(BlockingLockInfo blockingLockInfo) {
|
||||
return getFullStacktrace(blockingLockInfo.threadInfo, -1, blockingLockInfo.lockIdentityHashCode,
|
||||
blockingLockInfo.blockingThreadCount);
|
||||
return getFullStacktrace(blockingLockInfo.getThreadInfo(), -1, blockingLockInfo.getLockIdentityHashCode(),
|
||||
blockingLockInfo.getBlockingThreadCount());
|
||||
}
|
||||
|
||||
|
||||
@@ -329,17 +330,6 @@ abstract public class ThreadUtil {
|
||||
return sb.toString().replace("\t", " ");
|
||||
}
|
||||
|
||||
public static class BlockingLockInfo {
|
||||
|
||||
// the thread info that is holing this lock.
|
||||
public ThreadInfo threadInfo = null;
|
||||
// the associated LockInfo object
|
||||
public int lockIdentityHashCode = 0;
|
||||
// the number of thread that is blocked on this lock
|
||||
public int blockingThreadCount = 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* </pre>
|
||||
* java.lang.Thread.getStackTrace(Thread.java:1559),
|
||||
|
||||
Reference in New Issue
Block a user