package com.github.ompc.greys.core.command; import com.github.ompc.greys.core.command.annotation.Cmd; import com.github.ompc.greys.core.command.annotation.NamedArg; import com.github.ompc.greys.core.server.Session; import com.github.ompc.greys.core.textui.TKv; import com.github.ompc.greys.core.textui.TTable; import com.github.ompc.greys.core.util.affect.RowAffect; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import java.lang.instrument.Instrumentation; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collections; /** * 查看占用CPU资源的TOP线程 * * @author https://github.com/rodbate */ @Cmd(name = "top", sort = 13, summary = "Display The Threads Of Top CPU TIME", eg = { "top", "top -t 5", "top -d" }) public class ThreadTopCommand implements Command { @NamedArg(name = "i", hasValue = true, summary = "The thread id info") private String tid; @NamedArg(name = "t", hasValue = true, summary = "The top NUM of thread cost CPU times") private Integer top; @NamedArg(name = "d", summary = "Display the thread stack detail") private boolean isDetail = false; private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); private String stackToString(StackTraceElement[] stackTraceElementArray) { final TKv tKv = new TKv( new TTable.ColumnDefine(), new TTable.ColumnDefine(80) ); if (ArrayUtils.isNotEmpty(stackTraceElementArray)) { for (StackTraceElement ste : stackTraceElementArray) { tKv.add("at", ste.toString()); } } return tKv.rendering(); } @Override public Action getAction() { return new RowAction() { @Override public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { final ArrayList<ThreadInfoData> threadInfoDatas = new ArrayList<ThreadInfoData>(); long totalCpuTime = threadMXBean.getCurrentThreadCpuTime(); for (ThreadInfo tInfo : threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), Integer.MAX_VALUE)) { final long tId = tInfo.getThreadId(); final String tName = tInfo.getThreadName(); final long cpuTime = threadMXBean.getThreadCpuTime(tId); final String tStateStr = tInfo.getThreadState().toString(); final String tStackStr = isDetail ? stackToString(tInfo.getStackTrace()) : StringUtils.EMPTY; totalCpuTime += cpuTime; threadInfoDatas.add(new ThreadInfoData(tId, cpuTime, tName, tStateStr, tStackStr)); } final int topFix = top == null ? threadInfoDatas.size() : Math.min(top, threadInfoDatas.size()); Collections.sort(threadInfoDatas); final TTable tTable = new TTable( isDetail ? new TTable.ColumnDefine[]{ new TTable.ColumnDefine(TTable.Align.LEFT), new TTable.ColumnDefine(TTable.Align.MIDDLE), new TTable.ColumnDefine(), new TTable.ColumnDefine(20), new TTable.ColumnDefine(), } : new TTable.ColumnDefine[]{ new TTable.ColumnDefine(TTable.Align.LEFT), new TTable.ColumnDefine(TTable.Align.MIDDLE), new TTable.ColumnDefine(), new TTable.ColumnDefine(50) } ) .addRow("ID", "CPU%", "USR%", "STATE", "THREAD_NAME", "THREAD_STACK") .padding(1); final DecimalFormat df = new DecimalFormat("00.00"); for (int index = 0; index < topFix; index++) { final ThreadInfoData data = threadInfoDatas.get(index); if (StringUtils.isNotBlank(tid)) { final String fixTid = StringUtils.replace(tid, "#", ""); if (!StringUtils.equals("" + data.tId, fixTid)) { continue; } } final String cpuTimeRateStr = (totalCpuTime > 0 ? df.format(data.cpuTime * 100d / totalCpuTime) : "00.00") + "%"; tTable.addRow("#" + data.tId, cpuTimeRateStr, data.tStateStr, data.tName, data.stackStr); } printer.println(tTable.rendering()).finish(); return new RowAffect(topFix); } }; } private class ThreadInfoData implements Comparable<ThreadInfoData> { private final long tId; private final long cpuTime; private final String tName; private final String tStateStr; private final String stackStr; private ThreadInfoData(long tId, long cpuTime, String tName, String tStateStr, String stackStr) { this.tId = tId; this.cpuTime = cpuTime; this.tName = tName; this.tStateStr = tStateStr; this.stackStr = stackStr; } @Override public int compareTo(ThreadInfoData o) { return Long.valueOf(o.cpuTime).compareTo(cpuTime); } } }