Merge remote-tracking branch 'origin/master' into channel-server

pull/1490/merge^2
gongdewei 4 years ago
commit 4cff7ddc83

@ -1,6 +1,6 @@
FROM openjdk:8-jdk-alpine
ARG ARTHAS_VERSION="3.4.0"
ARG ARTHAS_VERSION="3.4.3"
ARG MIRROR=false
ENV MAVEN_HOST=https://repo1.maven.org/maven2 \

@ -1,6 +1,6 @@
FROM alpine
ARG ARTHAS_VERSION="3.4.0"
ARG ARTHAS_VERSION="3.4.3"
ARG MIRROR=false
ENV MAVEN_HOST=https://repo1.maven.org/maven2 \

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-agent</artifactId>

@ -71,7 +71,7 @@ public class AgentBootstrap {
/**
*
*/
public synchronized static void resetArthasClassLoader() {
public static void resetArthasClassLoader() {
arthasClassLoader = null;
}

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-agent-attach</artifactId>

@ -5,7 +5,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -97,8 +97,6 @@ echo NB: JAVA_HOME should point to a JDK not a JRE.
goto exit_bat
:okJava
set JAVACMD="%JAVA_HOME%"\bin\java
%JAVACMD% -Dfile.encoding=UTF-8 %BOOT_CLASSPATH% -jar "%CORE_JAR%" -pid "%PID%" -target-ip 127.0.0.1 -telnet-port %TELNET_PORT% -http-port %HTTP_PORT% -core "%CORE_JAR%" -agent "%AGENT_JAR%"
if %ERRORLEVEL% NEQ 0 goto exit_bat
if %exitProcess%==1 goto exit_bat

@ -8,10 +8,10 @@
# program : Arthas
# author : Core Engine @ Taobao.com
# date : 2020-09-02
# date : 2020-09-27
# current arthas script version
ARTHAS_SCRIPT_VERSION=3.4.0
ARTHAS_SCRIPT_VERSION=3.4.3
# SYNOPSIS
# rreadlink <fileOrDirPath>
@ -437,7 +437,7 @@ EXAMPLES:
./as.sh --stat-url 'http://192.168.10.11:8080/api/stat'
./as.sh -c 'sysprop; thread' <pid>
./as.sh -f batch.as <pid>
./as.sh --use-version 3.4.0
./as.sh --use-version 3.4.3
./as.sh --session-timeout 3600
./as.sh --attach-only
./as.sh --select arthas-demo

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-boot</artifactId>

@ -54,7 +54,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT;
+ " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n"
+ " java -jar arthas-boot.jar -c 'sysprop; thread' <pid>\n"
+ " java -jar arthas-boot.jar -f batch.as <pid>\n"
+ " java -jar arthas-boot.jar --use-version 3.4.0\n"
+ " java -jar arthas-boot.jar --use-version 3.4.3\n"
+ " java -jar arthas-boot.jar --versions\n"
+ " java -jar arthas-boot.jar --select arthas-demo\n"
+ " java -jar arthas-boot.jar --session-timeout 3600\n" + " java -jar arthas-boot.jar --attach-only\n"

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-bytekit</artifactId>

@ -17,7 +17,7 @@ public class InliningAdapter extends LocalVariablesSorter {
private LocalVariablesSorter lvs;
public InliningAdapter(LocalVariablesSorter mv, int access, String desc, Label end) {
super(Opcodes.ASM8, access, desc, mv);
super(Opcodes.ASM9, access, desc, mv);
this.end = end;
this.lvs = mv;

@ -37,7 +37,7 @@ public abstract class MethodCallInliner extends GeneratorAdapter {
public MethodCallInliner(int access, String name, String desc, MethodVisitor mv,
MethodNode toBeInlined) {
super(Opcodes.ASM8, mv, access, name, desc);
super(Opcodes.ASM9, mv, access, name, desc);
this.toBeInlined = toBeInlined;
}

@ -50,14 +50,29 @@ public class AsmUtils {
public static ClassNode toClassNode(byte[] classBytes) {
ClassReader reader = new ClassReader(classBytes);
ClassNode result = new ClassNode(Opcodes.ASM8);
ClassNode result = new ClassNode(Opcodes.ASM9);
reader.accept(result, ClassReader.SKIP_FRAMES);
return result;
}
public static byte[] toBytes(ClassNode classNode, ClassLoader classLoader) {
ClassWriter writer = new ClassLoaderAwareClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS,
classLoader);
public static ClassReader toClassNode(byte[] classBytes, ClassNode classNode) {
ClassReader reader = new ClassReader(classBytes);
reader.accept(classNode, ClassReader.SKIP_FRAMES);
return reader;
}
/**
* Generate class bytes from class node.
* <br>
* <B>NOTE: must pass origin classReader for bytecode optimizations, avoiding JVM metaspace OOM.</B>
* @param classNode
* @param classLoader
* @param classReader origin class reader
* @return
*/
public static byte[] toBytes(ClassNode classNode, ClassLoader classLoader, ClassReader classReader) {
int flags = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS;
ClassWriter writer = new ClassLoaderAwareClassWriter(classReader, flags, classLoader);
classNode.accept(writer);
return writer.toByteArray();
}
@ -143,7 +158,7 @@ public class AsmUtils {
}
public static MethodNode newMethodNode(MethodNode source) {
return new MethodNode(Opcodes.ASM8, source.access, source.name, source.desc, source.signature,
return new MethodNode(Opcodes.ASM9, source.access, source.name, source.desc, source.signature,
source.exceptions.toArray(new String[source.exceptions.size()]));
}
@ -156,8 +171,8 @@ public class AsmUtils {
}
public static ClassNode removeJSRInstructions(ClassNode classNode) {
ClassNode result = new ClassNode(Opcodes.ASM8);
classNode.accept(new ClassVisitor(Opcodes.ASM8, result) {
ClassNode result = new ClassNode(Opcodes.ASM9);
classNode.accept(new ClassVisitor(Opcodes.ASM9, result) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
@ -170,7 +185,7 @@ public class AsmUtils {
public static MethodNode removeLineNumbers(MethodNode methodNode) {
MethodNode result = newMethodNode(methodNode);
methodNode.accept(new MethodVisitor(Opcodes.ASM8, result) {
methodNode.accept(new MethodVisitor(Opcodes.ASM9, result) {
public void visitLineNumber(int line, Label start) {
}
});
@ -364,8 +379,8 @@ public class AsmUtils {
}
public static ClassNode copy(ClassNode source) {
ClassNode result = new ClassNode(Opcodes.ASM8);
source.accept(new ClassVisitor(Opcodes.ASM8, result) {
ClassNode result = new ClassNode(Opcodes.ASM9);
source.accept(new ClassVisitor(Opcodes.ASM9, result) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
@ -546,6 +561,26 @@ public class AsmUtils {
return new String(charArray);
}
/**
* Java ClassFile versions (the minor version is stored in the 16 most
* significant bits, and the major version in the 16 least significant bits).
* @see com.alibaba.arthas.deps.org.objectweb.asm.Opcodes#V_PREVIEW
* @param version
* @return
*/
public static int getMajorVersion(int version) {
return 0x0000FFFF & version;
}
/**
* version major version
*
* @param version
* @param majorVersion
* @return
*/
public static int setMajorVersion(int version, int majorVersion) {
return (version & 0xFFFF0000) | majorVersion;
}
}

@ -141,4 +141,36 @@ public class AsmUtilsTest {
Assertions.assertThat(object.getClass().getName()).isEqualTo("com.test.Test.XXX");
}
@Test
public void testGetMajorVersion() throws Exception {
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_1)).isEqualTo(45);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_2)).isEqualTo(46);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_3)).isEqualTo(47);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_4)).isEqualTo(48);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_5)).isEqualTo(49);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_6)).isEqualTo(50);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_7)).isEqualTo(51);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_8)).isEqualTo(52);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V9)).isEqualTo(53);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V10)).isEqualTo(54);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V11)).isEqualTo(55);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V12)).isEqualTo(56);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V13)).isEqualTo(57);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V14)).isEqualTo(58);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V15)).isEqualTo(59);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V16)).isEqualTo(60);
Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V16 | Opcodes.V_PREVIEW)).isEqualTo(60);
}
@Test
public void testSetMajorVersion() throws Exception {
int version = Opcodes.V16 | Opcodes.V_PREVIEW;
int newVersion = AsmUtils.setMajorVersion(version, 58);
AsmUtils.getMajorVersion(newVersion);
Assertions.assertThat(AsmUtils.getMajorVersion(newVersion)).isEqualTo(58);
}
}

@ -3,7 +3,7 @@
<parent>
<artifactId>arthas-all</artifactId>
<groupId>com.taobao.arthas</groupId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -314,7 +314,7 @@ public class TelnetConsole {
}
if (cmds.isEmpty()) {
IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), System.in,
IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), consoleReader.getInput(),
consoleReader.getOutput());
} else {
try {

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-common</artifactId>

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-core</artifactId>
@ -167,17 +167,24 @@
<!--<groupId>org.codehaus.groovy</groupId>-->
<!--<artifactId>groovy-all</artifactId>-->
<!--</dependency>-->
<!-- slf4j-api/logback-classic/logback-core 不能打包到应用里,因为真正使用的是 arthas-repackage-deps -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.arthas</groupId>

@ -21,6 +21,7 @@ import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
@ -53,6 +54,7 @@ import com.taobao.arthas.core.advisor.SpyInterceptors.SpyTraceInterceptor2;
import com.taobao.arthas.core.advisor.SpyInterceptors.SpyTraceInterceptor3;
import com.taobao.arthas.core.server.ArthasBootstrap;
import com.taobao.arthas.core.util.ArthasCheckUtils;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.arthas.core.util.FileUtils;
import com.taobao.arthas.core.util.SearchUtils;
import com.taobao.arthas.core.util.affect.EnhancerAffect;
@ -122,7 +124,9 @@ public class Enhancer implements ClassFileTransformer {
return null;
}
ClassNode classNode = AsmUtils.toClassNode(classfileBuffer);
//keep origin class reader for bytecode optimizations, avoiding JVM metaspace OOM.
ClassNode classNode = new ClassNode(Opcodes.ASM9);
ClassReader classReader = AsmUtils.toClassNode(classfileBuffer, classNode);
// remove JSR https://github.com/alibaba/arthas/issues/1304
classNode = AsmUtils.removeJSRInstructions(classNode);
@ -225,12 +229,12 @@ public class Enhancer implements ClassFileTransformer {
affect.addMethodAndCount(inClassLoader, className, methodNode.name, methodNode.desc);
}
// https://github.com/alibaba/arthas/issues/1223
if (classNode.version < Opcodes.V1_5) {
classNode.version = Opcodes.V1_5;
// https://github.com/alibaba/arthas/issues/1223 , V1_5 的major version是49
if (AsmUtils.getMajorVersion(classNode.version) < 49) {
classNode.version = AsmUtils.setMajorVersion(classNode.version, 49);
}
byte[] enhanceClassByteArray = AsmUtils.toBytes(classNode, inClassLoader);
byte[] enhanceClassByteArray = AsmUtils.toBytes(classNode, inClassLoader, classReader);
// 增强成功,记录类
classBytesCache.put(classBeingRedefined, new Object());
@ -328,7 +332,7 @@ public class Enhancer implements ClassFileTransformer {
*/
private static boolean isUnsupportedClass(Class<?> clazz) {
return clazz.isArray() || (clazz.isInterface() && !GlobalOptions.isSupportDefaultMethod) || clazz.isEnum()
|| clazz.equals(Class.class) || clazz.equals(Integer.class) || clazz.equals(Method.class);
|| clazz.equals(Class.class) || clazz.equals(Integer.class) || clazz.equals(Method.class) || ClassUtils.isLambdaClass(clazz);
}
/**

@ -2,23 +2,17 @@ package com.taobao.arthas.core.command.basic1000;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.advisor.Enhancer;
import com.taobao.arthas.core.command.model.MessageModel;
import com.taobao.arthas.core.command.model.ResetModel;
import com.taobao.arthas.core.command.model.ShutdownModel;
import com.taobao.arthas.core.server.ArthasBootstrap;
import com.taobao.arthas.core.shell.ShellServer;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.shell.session.SessionManager;
import com.taobao.arthas.core.util.affect.EnhancerAffect;
import com.taobao.arthas.core.util.matcher.WildcardMatcher;
import com.taobao.middleware.cli.annotations.Hidden;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Summary;
import java.lang.instrument.Instrumentation;
/**
*
*
@ -38,11 +32,11 @@ public class ShutdownCommand extends AnnotatedCommand {
}
public static void shutdown(CommandProcess process) {
ArthasBootstrap arthasBootstrap = ArthasBootstrap.getInstance();
try {
// 退出之前需要重置所有的增强类
process.appendResult(new MessageModel("Resetting all enhanced classes ..."));
Instrumentation inst = process.session().getInstrumentation();
EnhancerAffect enhancerAffect = Enhancer.reset(inst, new WildcardMatcher("*"));
EnhancerAffect enhancerAffect = arthasBootstrap.reset();
process.appendResult(new ResetModel(enhancerAffect));
process.appendResult(new ShutdownModel(true, "Arthas Server is going to shut down..."));
} catch (Throwable e) {
@ -50,23 +44,7 @@ public class ShutdownCommand extends AnnotatedCommand {
process.appendResult(new ShutdownModel(false, "An error occurred when stopping arthas server."));
} finally {
process.end();
ShellServer server = ArthasBootstrap.getInstance().getShellServer();
if (server != null) {
try {
server.close();
} catch (Throwable e) {
logger.error("close shell server failure", e);
}
}
SessionManager sessionManager = ArthasBootstrap.getInstance().getSessionManager();
if (sessionManager != null){
try {
sessionManager.close();
} catch (Throwable e) {
logger.error("close session manager failure", e);
}
}
arthasBootstrap.destroy();
}
}
}

@ -0,0 +1,163 @@
package com.taobao.arthas.core.command.model;
import java.lang.management.LockInfo;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
/**
* Busy thread info, include ThreadInfo fields
*
* @author gongdewei 2020/4/26
*/
public class BusyThreadInfo extends ThreadVO {
private long blockedTime;
private long blockedCount;
private long waitedTime;
private long waitedCount;
private LockInfo lockInfo;
private String lockName;
private long lockOwnerId;
private String lockOwnerName;
private boolean inNative;
private boolean suspended;
private StackTraceElement[] stackTrace;
private MonitorInfo[] lockedMonitors;
private LockInfo[] lockedSynchronizers;
public BusyThreadInfo(ThreadVO thread, ThreadInfo threadInfo) {
this.setId(thread.getId());
this.setName(thread.getName());
this.setDaemon(thread.isDaemon());
this.setInterrupted(thread.isInterrupted());
this.setPriority(thread.getPriority());
this.setGroup(thread.getGroup());
this.setState(thread.getState());
this.setCpu(thread.getCpu());
this.setDeltaTime(thread.getDeltaTime());
this.setTime(thread.getTime());
//thread info
if (threadInfo != null) {
this.setLockInfo(threadInfo.getLockInfo());
this.setLockedMonitors(threadInfo.getLockedMonitors());
this.setLockedSynchronizers(threadInfo.getLockedSynchronizers());
this.setLockName(threadInfo.getLockName());
this.setLockOwnerId(threadInfo.getLockOwnerId());
this.setLockOwnerName(threadInfo.getLockOwnerName());
this.setStackTrace(threadInfo.getStackTrace());
this.setBlockedCount(threadInfo.getBlockedCount());
this.setBlockedTime(threadInfo.getBlockedTime());
this.setInNative(threadInfo.isInNative());
this.setSuspended(threadInfo.isSuspended());
this.setWaitedCount(threadInfo.getWaitedCount());
this.setWaitedTime(threadInfo.getWaitedTime());
}
}
public long getBlockedTime() {
return blockedTime;
}
public void setBlockedTime(long blockedTime) {
this.blockedTime = blockedTime;
}
public long getBlockedCount() {
return blockedCount;
}
public void setBlockedCount(long blockedCount) {
this.blockedCount = blockedCount;
}
public long getWaitedTime() {
return waitedTime;
}
public void setWaitedTime(long waitedTime) {
this.waitedTime = waitedTime;
}
public long getWaitedCount() {
return waitedCount;
}
public void setWaitedCount(long waitedCount) {
this.waitedCount = waitedCount;
}
public LockInfo getLockInfo() {
return lockInfo;
}
public void setLockInfo(LockInfo lockInfo) {
this.lockInfo = lockInfo;
}
public String getLockName() {
return lockName;
}
public void setLockName(String lockName) {
this.lockName = lockName;
}
public long getLockOwnerId() {
return lockOwnerId;
}
public void setLockOwnerId(long lockOwnerId) {
this.lockOwnerId = lockOwnerId;
}
public String getLockOwnerName() {
return lockOwnerName;
}
public void setLockOwnerName(String lockOwnerName) {
this.lockOwnerName = lockOwnerName;
}
public boolean isInNative() {
return inNative;
}
public void setInNative(boolean inNative) {
this.inNative = inNative;
}
public boolean isSuspended() {
return suspended;
}
public void setSuspended(boolean suspended) {
this.suspended = suspended;
}
public StackTraceElement[] getStackTrace() {
return stackTrace;
}
public void setStackTrace(StackTraceElement[] stackTrace) {
this.stackTrace = stackTrace;
}
public MonitorInfo[] getLockedMonitors() {
return lockedMonitors;
}
public void setLockedMonitors(MonitorInfo[] lockedMonitors) {
this.lockedMonitors = lockedMonitors;
}
public LockInfo[] getLockedSynchronizers() {
return lockedSynchronizers;
}
public void setLockedSynchronizers(LockInfo[] lockedSynchronizers) {
this.lockedSynchronizers = lockedSynchronizers;
}
}

@ -1,36 +0,0 @@
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;
}
}

@ -18,11 +18,12 @@ public class ThreadModel extends ResultModel {
private BlockingLockInfo blockingLockInfo;
//thread -n 5
private ThreadCpuInfo[] busyThreads;
private List<BusyThreadInfo> busyThreads;
//thread stats
private List<ThreadVO> threadStats;
private Map<Thread.State, Integer> threadStateCount;
private boolean all;
public ThreadModel() {
}
@ -35,13 +36,14 @@ public class ThreadModel extends ResultModel {
this.blockingLockInfo = blockingLockInfo;
}
public ThreadModel(ThreadCpuInfo[] busyThreads) {
public ThreadModel(List<BusyThreadInfo> busyThreads) {
this.busyThreads = busyThreads;
}
public ThreadModel(List<ThreadVO> threadStats, Map<Thread.State, Integer> threadStateCount) {
public ThreadModel(List<ThreadVO> threadStats, Map<Thread.State, Integer> threadStateCount, boolean all) {
this.threadStats = threadStats;
this.threadStateCount = threadStateCount;
this.all = all;
}
@Override
@ -65,11 +67,11 @@ public class ThreadModel extends ResultModel {
this.blockingLockInfo = blockingLockInfo;
}
public ThreadCpuInfo[] getBusyThreads() {
public List<BusyThreadInfo> getBusyThreads() {
return busyThreads;
}
public void setBusyThreads(ThreadCpuInfo[] busyThreads) {
public void setBusyThreads(List<BusyThreadInfo> busyThreads) {
this.busyThreads = busyThreads;
}
@ -88,4 +90,12 @@ public class ThreadModel extends ResultModel {
public void setThreadStateCount(Map<Thread.State, Integer> threadStateCount) {
this.threadStateCount = threadStateCount;
}
public boolean isAll() {
return all;
}
public void setAll(boolean all) {
this.all = all;
}
}

@ -13,7 +13,8 @@ public class ThreadVO {
private String group;
private int priority;
private State state;
private long cpu;
private double cpu;
private long deltaTime;
private long time;
private boolean interrupted;
private boolean daemon;
@ -61,14 +62,22 @@ public class ThreadVO {
this.state = state;
}
public long getCpu() {
public double getCpu() {
return cpu;
}
public void setCpu(long cpu) {
public void setCpu(double cpu) {
this.cpu = cpu;
}
public long getDeltaTime() {
return deltaTime;
}
public void setDeltaTime(long deltaTime) {
this.deltaTime = deltaTime;
}
public long getTime() {
return time;
}
@ -92,4 +101,22 @@ public class ThreadVO {
public void setDaemon(boolean daemon) {
this.daemon = daemon;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ThreadVO threadVO = (ThreadVO) o;
if (id != threadVO.id) return false;
return name != null ? name.equals(threadVO.name) : threadVO.name == null;
}
@Override
public int hashCode() {
int result = (int) (id ^ (id >>> 32));
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
}

@ -9,6 +9,7 @@ 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.ThreadVO;
import com.taobao.arthas.core.command.model.TomcatInfoVO;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
@ -305,7 +306,7 @@ public class DashboardCommand extends AnnotatedCommand {
DashboardModel dashboardModel = new DashboardModel();
//thread sample
Map<String, Thread> threads = ThreadUtil.getThreads();
Map<String, ThreadVO> threads = ThreadUtil.getThreads();
dashboardModel.setThreads(threadSampler.sample(threads.values()));
//memory

@ -2,7 +2,7 @@ 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.BusyThreadInfo;
import com.taobao.arthas.core.command.model.ThreadModel;
import com.taobao.arthas.core.command.model.ThreadVO;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
@ -51,11 +51,12 @@ public class ThreadCommand extends AnnotatedCommand {
private long id = -1;
private Integer topNBusy = null;
private boolean findMostBlockingThread = false;
private int sampleInterval = 100;
private int sampleInterval = 200;
private String state;
private boolean lockedMonitors = false;
private boolean lockedSynchronizers = false;
private boolean all = false;
static {
states = new HashSet<String>(State.values().length);
@ -70,6 +71,12 @@ public class ThreadCommand extends AnnotatedCommand {
this.id = id;
}
@Option(longName = "all", flag = true)
@Description("Display all thread results instead of the first page")
public void setAll(boolean all) {
this.all = all;
}
@Option(shortName = "n", longName = "top-n-threads")
@Description("The number of thread(s) to show, ordered by cpu utilization, -1 to show all.")
public void setTopNBusy(Integer topNBusy) {
@ -122,7 +129,7 @@ public class ThreadCommand extends AnnotatedCommand {
}
private ExitStatus processAllThreads(CommandProcess process) {
Map<String, Thread> threads = ThreadUtil.getThreads();
Map<String, ThreadVO> threads = ThreadUtil.getThreads();
// 统计各种线程状态
Map<State, Integer> stateCountMap = new LinkedHashMap<State, Integer>();
@ -130,18 +137,20 @@ public class ThreadCommand extends AnnotatedCommand {
stateCountMap.put(s, 0);
}
for (Thread thread : threads.values()) {
for (ThreadVO thread : threads.values()) {
State threadState = thread.getState();
Integer count = stateCountMap.get(threadState);
stateCountMap.put(threadState, count + 1);
}
Collection<Thread> resultThreads = new ArrayList<Thread>();
boolean includeInternalThreads = true;
Collection<ThreadVO> resultThreads = new ArrayList<ThreadVO>();
if (!StringUtils.isEmpty(this.state)) {
this.state = this.state.toUpperCase();
if (states.contains(this.state)) {
for (Thread thread : threads.values()) {
if (state.equals(thread.getState().name())) {
includeInternalThreads = false;
for (ThreadVO thread : threads.values()) {
if (thread.getState() != null && state.equals(thread.getState().name())) {
resultThreads.add(thread);
}
}
@ -154,10 +163,12 @@ public class ThreadCommand extends AnnotatedCommand {
//thread stats
ThreadSampler threadSampler = new ThreadSampler();
threadSampler.setSampleInterval(sampleInterval);
threadSampler.setIncludeInternalThreads(includeInternalThreads);
threadSampler.sample(resultThreads);
threadSampler.pause(sampleInterval);
List<ThreadVO> threadStats = threadSampler.sample(resultThreads);
process.appendResult(new ThreadModel(threadStats, stateCountMap));
process.appendResult(new ThreadModel(threadStats, stateCountMap, all));
return ExitStatus.success();
}
@ -171,21 +182,44 @@ public class ThreadCommand extends AnnotatedCommand {
}
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) {
ThreadSampler threadSampler = new ThreadSampler();
threadSampler.sample(ThreadUtil.getThreads().values());
threadSampler.pause(sampleInterval);
List<ThreadVO> threadStats = threadSampler.sample(ThreadUtil.getThreads().values());
int limit = Math.min(threadStats.size(), topNBusy);
List<ThreadVO> topNThreads = threadStats.subList(0, limit);
List<Long> tids = new ArrayList<Long>(topNThreads.size());
for (ThreadVO thread : topNThreads) {
if (thread.getId() > 0) {
tids.add(thread.getId());
}
}
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(ArrayUtils.toPrimitive(tids.toArray(new Long[0])), lockedMonitors, lockedSynchronizers);
if (tids.size()> 0 && threadInfos == null) {
return ExitStatus.failure(1, "get top busy threads failed");
}
//threadInfo with cpuUsage
ThreadCpuInfo[] threadCpuInfos = new ThreadCpuInfo[threadInfos.length];
List<BusyThreadInfo> busyThreadInfos = new ArrayList<BusyThreadInfo>(topNThreads.size());
for (ThreadVO thread : topNThreads) {
ThreadInfo threadInfo = findThreadInfoById(threadInfos, thread.getId());
BusyThreadInfo busyThread = new BusyThreadInfo(thread, threadInfo);
busyThreadInfos.add(busyThread);
}
process.appendResult(new ThreadModel(busyThreadInfos));
return ExitStatus.success();
}
private ThreadInfo findThreadInfoById(ThreadInfo[] threadInfos, long id) {
for (int i = 0; i < threadInfos.length; i++) {
ThreadInfo threadInfo = threadInfos[i];
threadCpuInfos[i] = new ThreadCpuInfo(threadInfo, topNThreads.get(threadInfo.getThreadId()));
if ( threadInfo.getThreadId() == id) {
return threadInfo;
}
}
process.appendResult(new ThreadModel(threadCpuInfos));
return ExitStatus.success();
return null;
}
private ExitStatus processThread(CommandProcess process) {

@ -1,10 +1,18 @@
package com.taobao.arthas.core.command.monitor200;
import com.taobao.arthas.core.command.model.ThreadVO;
import sun.management.HotspotThreadMBean;
import sun.management.ManagementFactoryHelper;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Thread cpu sampler
@ -13,61 +21,112 @@ import java.util.*;
*/
public class ThreadSampler {
private long sampleInterval = 100;
private static ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
private static HotspotThreadMBean hotspotThreadMBean;
private static boolean hotspotThreadMBeanEnable = true;
public List<ThreadVO> sample(Collection<Thread> originThreads) {
private Map<ThreadVO, Long> lastCpuTimes = new HashMap<ThreadVO, Long>();
List<Thread> threads = new ArrayList<Thread>(originThreads);
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
private long lastSampleTimeNanos;
private boolean includeInternalThreads = true;
public List<ThreadVO> sample(Collection<ThreadVO> originThreads) {
List<ThreadVO> threads = new ArrayList<ThreadVO>(originThreads);
// 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);
}
if (lastCpuTimes.isEmpty()) {
lastSampleTimeNanos = System.nanoTime();
for (ThreadVO thread : threads) {
if (thread.getId() > 0) {
long cpu = threadMXBean.getThreadCpuTime(thread.getId());
lastCpuTimes.put(thread, cpu);
thread.setTime(cpu / 1000000);
}
}
try {
Thread.sleep(sampleInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// add internal threads
Map<String, Long> internalThreadCpuTimes = getInternalThreadCpuTimes();
if (internalThreadCpuTimes != null) {
for (Map.Entry<String, Long> entry : internalThreadCpuTimes.entrySet()) {
String key = entry.getKey();
ThreadVO thread = createThreadVO(key);
thread.setTime(entry.getValue() / 1000000);
threads.add(thread);
lastCpuTimes.put(thread, entry.getValue());
}
}
//sort by time
Collections.sort(threads, new Comparator<ThreadVO>() {
@Override
public int compare(ThreadVO o1, ThreadVO o2) {
long l1 = o1.getTime();
long l2 = o2.getTime();
if (l1 < l2) {
return 1;
} else if (l1 > l2) {
return -1;
} else {
return 0;
}
}
});
return threads;
}
// 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);
long newSampleTimeNanos = System.nanoTime();
Map<ThreadVO, Long> newCpuTimes = new HashMap<ThreadVO, Long>(threads.size());
for (ThreadVO thread : threads) {
if (thread.getId() > 0) {
long cpu = threadMXBean.getThreadCpuTime(thread.getId());
newCpuTimes.put(thread, cpu);
}
}
// internal threads
Map<String, Long> newInternalThreadCpuTimes = getInternalThreadCpuTimes();
if (newInternalThreadCpuTimes != null) {
for (Map.Entry<String, Long> entry : newInternalThreadCpuTimes.entrySet()) {
ThreadVO threadVO = createThreadVO(entry.getKey());
threads.add(threadVO);
newCpuTimes.put(threadVO, entry.getValue());
}
}
// 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);
// Compute delta time
final Map<ThreadVO, Long> deltas = new HashMap<ThreadVO, Long>(threads.size());
for (ThreadVO thread : newCpuTimes.keySet()) {
Long t = lastCpuTimes.get(thread);
if (t == null) {
t = 0L;
}
long time1 = t;
long time2 = newCpuTimes.get(thread);
if (time1 == -1) {
time1 = time2;
} else if (time2 == -1) {
time2 = time1;
}
long delta = time2 - time1;
deltas.put(id, delta);
total += delta;
deltas.put(thread, 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);
long sampleIntervalNanos = newSampleTimeNanos - lastSampleTimeNanos;
// Compute cpu usage
final HashMap<ThreadVO, Double> cpuUsages = new HashMap<ThreadVO, Double>(threads.size());
for (ThreadVO thread : threads) {
double cpu = sampleIntervalNanos == 0 ? 0 : (deltas.get(thread) * 10000 / sampleIntervalNanos / 100.0);
cpuUsages.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);
Collections.sort(threads, new Comparator<ThreadVO>() {
public int compare(ThreadVO o1, ThreadVO o2) {
long l1 = deltas.get(o1);
long l2 = deltas.get(o2);
if (l1 < l2) {
return 1;
} else if (l1 > l2) {
@ -78,34 +137,61 @@ public class ThreadSampler {
}
});
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);
for (ThreadVO thread : threads) {
//nanos to mills
long timeMills = newCpuTimes.get(thread) / 1000000;
long deltaTime = deltas.get(thread) / 1000000;
double cpu = cpuUsages.get(thread);
thread.setCpu(cpu);
thread.setTime(timeMills);
thread.setDeltaTime(deltaTime);
}
lastCpuTimes = newCpuTimes;
lastSampleTimeNanos = newSampleTimeNanos;
return threads;
}
private Map<String, Long> getInternalThreadCpuTimes() {
if (hotspotThreadMBeanEnable && includeInternalThreads) {
try {
if (hotspotThreadMBean == null) {
hotspotThreadMBean = ManagementFactoryHelper.getHotspotThreadMBean();
}
return hotspotThreadMBean.getInternalThreadCpuTimes();
} catch (Throwable e) {
//ignore ex
hotspotThreadMBeanEnable = false;
}
}
return null;
}
private ThreadVO createThreadVO(String name) {
ThreadVO threadVO = new ThreadVO();
threadVO.setId(-1);
threadVO.setName(name);
threadVO.setPriority(-1);
threadVO.setDaemon(true);
threadVO.setInterrupted(false);
return threadVO;
}
public void pause(long mills) {
try {
Thread.sleep(mills);
} catch (InterruptedException e) {
// ignore
}
return threadVOList;
}
public long getSampleInterval() {
return sampleInterval;
public boolean isIncludeInternalThreads() {
return includeInternalThreads;
}
public void setSampleInterval(long sampleInterval) {
this.sampleInterval = sampleInterval;
public void setIncludeInternalThreads(boolean includeInternalThreads) {
this.includeInternalThreads = includeInternalThreads;
}
}

@ -28,37 +28,61 @@ public class DashboardView extends ResultView<DashboardModel> {
// 上半部分放thread top。下半部分再切分为田字格其中上面两格放memory, gc的信息。下面两格放tomcat,
// runtime的信息
int totalHeight = height - 1;
int threadTopHeight = totalHeight / 2;
int threadTopHeight;
if (totalHeight <= 24) {
//总高度较小时取1/2
threadTopHeight = totalHeight / 2;
} else {
//总高度较大时取1/3但不少于上面的值(24/2=12)
threadTopHeight = totalHeight / 3;
if (threadTopHeight < 12) {
threadTopHeight = 12;
}
}
int lowerHalf = totalHeight - threadTopHeight;
int runtimeInfoHeight = lowerHalf / 2;
int heapInfoHeight = lowerHalf - runtimeInfoHeight;
//Memory至少保留8行, 显示metaspace信息
int memoryInfoHeight = lowerHalf / 2;
if (memoryInfoHeight < 8) {
memoryInfoHeight = Math.min(8, lowerHalf);
}
//runtime
TableElement runtimeInfoTable = drawRuntimeInfo(result.getRuntimeInfo());
//tomcat
TableElement tomcatInfoTable = drawTomcatInfo(result.getTomcatInfo());
int runtimeInfoHeight = Math.max(runtimeInfoTable.getRows().size(), tomcatInfoTable == null ? 0 : tomcatInfoTable.getRows().size());
if (runtimeInfoHeight < lowerHalf - memoryInfoHeight) {
//如果runtimeInfo高度有剩余则增大MemoryInfo的高度
memoryInfoHeight = lowerHalf - runtimeInfoHeight;
} else {
runtimeInfoHeight = lowerHalf - memoryInfoHeight;
}
//如果MemoryInfo高度有剩余则增大ThreadHeight
int maxMemoryInfoHeight = getMemoryInfoHeight(result.getMemoryInfo());
memoryInfoHeight = Math.min(memoryInfoHeight, maxMemoryInfoHeight);
threadTopHeight = totalHeight - memoryInfoHeight - 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);
String memoryAndGc = drawMemoryInfoAndGcInfo(result.getMemoryInfo(), result.getGcInfos(), width, memoryInfoHeight);
String runTimeAndTomcat = drawRuntimeInfoAndTomcatInfo(runtimeInfoTable, tomcatInfoTable, width, runtimeInfoHeight);
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);
TableElement memoryInfoTable = drawMemoryInfo(memoryInfo);
TableElement gcInfoTable = drawGcInfo(gcInfos);
table.row(memoryInfoTable, gcInfoTable);
return RenderUtil.render(table, width, height);
}
private static void drawMemoryInfo(TableElement table, Map<String, List<MemoryEntryVO>> memoryInfo) {
private static TableElement drawMemoryInfo(Map<String, List<MemoryEntryVO>> memoryInfo) {
TableElement table = new TableElement(3, 1, 1, 1, 1).rightCellPadding(1);
table.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Memory",
"used", "total", "max", "usage"));
List<MemoryEntryVO> heapMemoryEntries = memoryInfo.get(MemoryEntryVO.TYPE_HEAP);
//heap memory
for (MemoryEntryVO memoryEntryVO : heapMemoryEntries) {
@ -81,26 +105,38 @@ public class DashboardView extends ResultView<DashboardModel> {
//buffer-pool
List<MemoryEntryVO> bufferPoolMemoryEntries = memoryInfo.get(MemoryEntryVO.TYPE_BUFFER_POOL);
for (MemoryEntryVO memoryEntryVO : bufferPoolMemoryEntries) {
new MemoryEntry(memoryEntryVO).addTableRow(table);
if (bufferPoolMemoryEntries != null) {
for (MemoryEntryVO memoryEntryVO : bufferPoolMemoryEntries) {
new MemoryEntry(memoryEntryVO).addTableRow(table);
}
}
return table;
}
private static int getMemoryInfoHeight(Map<String, List<MemoryEntryVO>> memoryInfo) {
int height = 1;
for (List<MemoryEntryVO> memoryEntryVOS : memoryInfo.values()) {
height += memoryEntryVOS.size();
}
return height;
}
private static void drawGcInfo(TableElement table, List<GcInfoVO> gcInfos) {
private static TableElement drawGcInfo(List<GcInfoVO> gcInfos) {
TableElement table = new TableElement(1, 1).rightCellPadding(1);
table.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("GC", ""));
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());
}
return table;
}
String drawRuntimeInfoAndTomcatInfo(RuntimeInfoVO runtimeInfo, TomcatInfoVO tomcatInfo, int width, int height) {
String drawRuntimeInfoAndTomcatInfo(TableElement runtimeInfoTable, TableElement tomcatInfoTable, int width, int height) {
if (height <= 0) {
return "";
}
TableElement resultTable = new TableElement(1, 1);
//runtime
TableElement runtimeInfoTable = drawRuntimeInfo(runtimeInfo);
//tomcat
TableElement tomcatInfoTable = drawTomcatInfo(tomcatInfo);
if (tomcatInfoTable != null) {
resultTable.row(runtimeInfoTable, tomcatInfoTable);
} else {
@ -211,13 +247,17 @@ public class DashboardView extends ResultView<DashboardModel> {
public void addTableRow(TableElement table) {
double usage = used / (double) (max == -1 || max == Long.MIN_VALUE ? total : max) * 100;
if (Double.isNaN(usage) || Double.isInfinite(usage)) {
usage = 0;
}
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;
if (Double.isNaN(usage) || Double.isInfinite(usage)) {
usage = 0;
}
table.add(new RowElement().style(style).add(name, format(used), format(total), format(max),
String.format("%.2f%%", usage)));
}

@ -1,6 +1,6 @@
package com.taobao.arthas.core.command.view;
import com.taobao.arthas.core.command.model.ThreadCpuInfo;
import com.taobao.arthas.core.command.model.BusyThreadInfo;
import com.taobao.arthas.core.command.model.ThreadModel;
import com.taobao.arthas.core.command.model.ThreadVO;
import com.taobao.arthas.core.shell.command.CommandProcess;
@ -23,12 +23,12 @@ public class ThreadView extends ResultView<ThreadModel> {
public void draw(CommandProcess process, ThreadModel result) {
if (result.getThreadInfo() != null) {
// no cpu usage info
String content = ThreadUtil.getFullStacktrace(result.getThreadInfo(), -1);
String content = ThreadUtil.getFullStacktrace(result.getThreadInfo());
process.write(content);
} else if (result.getBusyThreads() != null) {
ThreadCpuInfo[] threadInfos = result.getBusyThreads();
for (ThreadCpuInfo info : threadInfos) {
String stacktrace = ThreadUtil.getFullStacktrace(info.threadInfo(), info.getCpuUsage());
List<BusyThreadInfo> threadInfos = result.getBusyThreads();
for (BusyThreadInfo info : threadInfos) {
String stacktrace = ThreadUtil.getFullStacktrace(info, -1, -1);
process.write(stacktrace).write("\n");
}
} else if (result.getBlockingLockInfo() != null) {
@ -44,6 +44,15 @@ public class ThreadView extends ResultView<ThreadModel> {
for (Integer value : threadStateCount.values()) {
total += value;
}
int internalThreadCount = 0;
for (ThreadVO thread : threadStats) {
if (thread.getId() <= 0) {
internalThreadCount += 1;
}
}
total += internalThreadCount;
StringBuilder threadStat = new StringBuilder();
threadStat.append("Threads Total: ").append(total);
@ -51,12 +60,20 @@ public class ThreadView extends ResultView<ThreadModel> {
Integer count = threadStateCount.get(s);
threadStat.append(", ").append(s.name()).append(": ").append(count);
}
if (internalThreadCount > 0) {
threadStat.append(", Internal threads: ").append(internalThreadCount);
}
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);
int height;
if (result.isAll()) {
height = threadStats.size() + 1;
} else {
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);
}

@ -105,7 +105,7 @@ public class ViewRenderUtil {
}
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);
TableElement table = new TableElement(1, 6, 3, 2, 2, 2, 2, 2, 2, 2).overflow(Overflow.HIDDEN).rightCellPadding(1);
// Header
table.add(
@ -116,6 +116,7 @@ public class ViewRenderUtil {
"PRIORITY",
"STATE",
"%CPU",
"DELTA_TIME",
"TIME",
"INTERRUPTED",
"DAEMON"
@ -124,22 +125,28 @@ public class ViewRenderUtil {
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();
String time = formatTimeMills(thread.getTime());
String deltaTime = formatTimeMillsToSeconds(thread.getDeltaTime());
double cpu = thread.getCpu();
LabelElement daemonLabel = new LabelElement(thread.isDaemon());
if (!thread.isDaemon()) {
daemonLabel.setStyle(Style.style(Color.magenta));
}
LabelElement stateElement;
if (thread.getState() != null) {
stateElement = new LabelElement(thread.getState()).style(color.fg());
} else {
stateElement = new LabelElement("-");
}
table.row(
new LabelElement(thread.getId()),
new LabelElement(thread.getName()),
new LabelElement(thread.getGroup()),
new LabelElement(thread.getGroup() != null ? thread.getGroup() : "-"),
new LabelElement(thread.getPriority()),
new LabelElement(thread.getState()).style(color.fg()),
stateElement,
new LabelElement(cpu),
new LabelElement(deltaTime),
new LabelElement(time),
new LabelElement(thread.isInterrupted()),
daemonLabel
@ -147,4 +154,18 @@ public class ViewRenderUtil {
}
return RenderUtil.render(table, width, height);
}
private static String formatTimeMills(long timeMills) {
long seconds = timeMills / 1000;
long mills = timeMills % 1000;
long min = seconds / 60;
//return min + ":" + (seconds % 60);
return String.format("%d:%d.%03d", min, seconds, mills);
}
private static String formatTimeMillsToSeconds(long timeMills) {
long seconds = timeMills / 1000;
long mills = timeMills % 1000;
return String.format("%d.%03d", seconds, mills);
}
}

@ -4,6 +4,7 @@ import java.arthas.SpyAPI;
import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Method;
import java.security.CodeSource;
import java.util.ArrayList;
@ -29,6 +30,7 @@ import com.alibaba.arthas.tunnel.client.TunnelClient;
import com.taobao.arthas.common.AnsiLog;
import com.taobao.arthas.common.PidUtils;
import com.taobao.arthas.common.SocketUtils;
import com.taobao.arthas.core.advisor.Enhancer;
import com.taobao.arthas.core.advisor.TransformerManager;
import com.taobao.arthas.core.channel.AgentInfoServiceImpl;
import com.taobao.arthas.core.channel.ChannelRequestHandler;
@ -58,6 +60,9 @@ import com.taobao.arthas.core.util.FileUtils;
import com.taobao.arthas.core.util.LogUtil;
import com.taobao.arthas.core.util.StringUtils;
import com.taobao.arthas.core.util.UserStatUtil;
import com.taobao.arthas.core.util.affect.EnhancerAffect;
import com.taobao.arthas.core.util.matcher.WildcardMatcher;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.DefaultThreadFactory;
@ -329,7 +334,7 @@ public class ArthasBootstrap {
options.setWelcomeMessage(ArthasBanner.welcome(welcomeInfos));
}
shellServer = new ShellServerImpl(options, this);
shellServer = new ShellServerImpl(options);
BuiltinCommandPack builtinCommands = new BuiltinCommandPack();
List<CommandResolver> resolvers = new ArrayList<CommandResolver>();
resolvers.add(builtinCommands);
@ -360,9 +365,12 @@ public class ArthasBootstrap {
}
shellServer.listen(new BindHandler(isBindRef));
if (!isBind()) {
throw new IllegalStateException("Arthas failed to bind telnet or http port.");
}
//http api session manager
sessionManager = new SessionManagerImpl(options, this, shellServer.getCommandManager(), shellServer.getJobController());
sessionManager = new SessionManagerImpl(options, shellServer.getCommandManager(), shellServer.getJobController());
//http api handler
httpApiHandler = new HttpApiHandler(historyManager, sessionManager);
@ -396,14 +404,8 @@ public class ArthasBootstrap {
logger().info("as-server started in {} ms", System.currentTimeMillis() - start);
} catch (Throwable e) {
logger().error("Error during bind to port " + configure.getTelnetPort(), e);
if (shellServer != null) {
shellServer.close();
}
if (sessionManager != null){
sessionManager.close();
}
shutdownWorkGroup();
logger().error("Error during start as-server", e);
destroy();
throw e;
}
}
@ -424,8 +426,25 @@ public class ArthasBootstrap {
return isBindRef.get();
}
public EnhancerAffect reset() throws UnmodifiableClassException {
return Enhancer.reset(this.instrumentation, new WildcardMatcher("*"));
}
/**
* call reset() before destroy()
*/
public void destroy() {
timer.cancel();
if (shellServer != null) {
shellServer.close();
shellServer = null;
}
if (sessionManager != null) {
sessionManager.close();
sessionManager = null;
}
if (timer != null) {
timer.cancel();
}
if (this.tunnelClient != null) {
try {
tunnelClient.stop();
@ -433,7 +452,6 @@ public class ArthasBootstrap {
logger().error("stop tunnel client error", e);
}
}
if (channelClient != null) {
try {
channelClient.stop();
@ -441,24 +459,27 @@ public class ArthasBootstrap {
e.printStackTrace();
}
}
executorService.shutdownNow();
transformerManager.destroy();
UserStatUtil.destroy();
shutdownWorkGroup();
if (executorService != null) {
executorService.shutdownNow();
}
if (transformerManager != null) {
transformerManager.destroy();
}
// clear the reference in Spy class.
cleanUpSpyReference();
try {
Runtime.getRuntime().removeShutdownHook(shutdown);
} catch (Throwable t) {
// ignore
shutdownWorkGroup();
UserStatUtil.destroy();
if (shutdown != null) {
try {
Runtime.getRuntime().removeShutdownHook(shutdown);
} catch (Throwable t) {
// ignore
}
}
logger().info("as-server destroy completed.");
if (loggerContext != null) {
loggerContext.stop();
}
shellServer = null;
sessionManager = null;
}
/**

@ -48,7 +48,6 @@ public class ShellServerImpl extends ShellServer {
private final long timeoutMillis;
private final long reaperInterval;
private String welcomeMessage;
private ArthasBootstrap bootstrap;
private Instrumentation instrumentation;
private long pid;
private boolean closed = true;
@ -58,10 +57,6 @@ public class ShellServerImpl extends ShellServer {
private JobControllerImpl jobController = new GlobalJobControllerImpl();
public ShellServerImpl(ShellServerOptions options) {
this(options, null);
}
public ShellServerImpl(ShellServerOptions options, ArthasBootstrap bootstrap) {
this.welcomeMessage = options.getWelcomeMessage();
this.termServers = new ArrayList<TermServer>();
this.timeoutMillis = options.getSessionTimeout();
@ -70,7 +65,6 @@ public class ShellServerImpl extends ShellServer {
this.resolvers = new CopyOnWriteArrayList<CommandResolver>();
this.commandManager = new InternalCommandManager(resolvers);
this.instrumentation = options.getInstrumentation();
this.bootstrap = bootstrap;
this.pid = options.getPid();
// Register builtin commands so they are listed in help
@ -240,7 +234,6 @@ public class ShellServerImpl extends ShellServer {
}
jobController.close();
sessionsClosed.setHandler(handler);
bootstrap.destroy();
}
}

@ -6,7 +6,6 @@ import com.taobao.arthas.core.command.model.MessageModel;
import com.taobao.arthas.core.distribution.ResultConsumer;
import com.taobao.arthas.core.distribution.ResultDistributor;
import com.taobao.arthas.core.distribution.SharingResultDistributor;
import com.taobao.arthas.core.server.ArthasBootstrap;
import com.taobao.arthas.core.shell.ShellServerOptions;
import com.taobao.arthas.core.shell.session.Session;
import com.taobao.arthas.core.shell.session.SessionManager;
@ -25,7 +24,6 @@ import java.util.concurrent.*;
*/
public class SessionManagerImpl implements SessionManager {
private static final Logger logger = LoggerFactory.getLogger(SessionManagerImpl.class);
private final ArthasBootstrap bootstrap;
private final InternalCommandManager commandManager;
private final Instrumentation instrumentation;
private final JobController jobController;
@ -37,9 +35,8 @@ public class SessionManagerImpl implements SessionManager {
private boolean closed = false;
private ScheduledExecutorService scheduledExecutorService;
public SessionManagerImpl(ShellServerOptions options, ArthasBootstrap bootstrap, InternalCommandManager commandManager,
public SessionManagerImpl(ShellServerOptions options, InternalCommandManager commandManager,
JobController jobController) {
this.bootstrap = bootstrap;
this.commandManager = commandManager;
this.jobController = jobController;
this.sessions = new ConcurrentHashMap<String, Session>();
@ -111,7 +108,6 @@ public class SessionManagerImpl implements SessionManager {
}
jobController.close();
bootstrap.destroy();
}
private synchronized void setEvictTimer() {
@ -119,7 +115,7 @@ public class SessionManagerImpl implements SessionManager {
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
final Thread t = new Thread(r, "arthas-shell-server");
final Thread t = new Thread(r, "arthas-session-manager");
t.setDaemon(true);
return t;
}

@ -9,7 +9,12 @@ import com.taobao.text.Decoration;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
@ -25,6 +30,9 @@ public class ArthasBanner {
private static final String VERSION_LOCATION = "/com/taobao/arthas/core/res/version";
private static final String WIKI = "https://arthas.aliyun.com/doc";
private static final String TUTORIALS = "https://arthas.aliyun.com/doc/arthas-tutorials.html";
private static final String ARTHAS_LATEST_VERSIONS_URL = "https://arthas.aliyun.com/api/latest_version";
private static final int CONNECTION_TIMEOUT = 1000;
private static String LOGO = "Welcome to Arthas";
private static String VERSION = "unknown";
@ -101,7 +109,7 @@ public class ArthasBanner {
}
public static String welcome(Map<String, String> infos) {
logger.info("arthas version: " + version());
logger.info("Current arthas version: {}, recommend latest version: {}", version(), latestVersion());
TableElement table = new TableElement().rightCellPadding(1)
.row("wiki", wiki())
.row("tutorials", tutorials())
@ -114,4 +122,41 @@ public class ArthasBanner {
return logo() + "\n" + RenderUtil.render(table);
}
static String latestVersion() {
try {
URLConnection urlConnection = openURLConnection(ARTHAS_LATEST_VERSIONS_URL);
InputStream inputStream = urlConnection.getInputStream();
return com.taobao.arthas.common.IOUtils.toString(inputStream).trim();
} catch (Throwable e) {
// ignore
}
return "";
}
/**
* support redirect
*
* @param url
* @return
* @throws MalformedURLException
* @throws IOException
*/
private static URLConnection openURLConnection(String url) throws MalformedURLException, IOException {
URLConnection connection = new URL(url).openConnection();
if (connection instanceof HttpURLConnection) {
connection.setConnectTimeout(CONNECTION_TIMEOUT);
// normally, 3xx is redirect
int status = ((HttpURLConnection) connection).getResponseCode();
if (status != HttpURLConnection.HTTP_OK) {
if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM
|| status == HttpURLConnection.HTTP_SEE_OTHER) {
String newUrl = connection.getHeaderField("Location");
logger.debug("Try to open url: {}, redirect to: {}", url, newUrl);
return openURLConnection(newUrl);
}
}
}
return connection;
}
}

@ -21,13 +21,16 @@ public class InstrumentationUtils {
inst.addTransformer(transformer, true);
for (Class<?> clazz : classes) {
if (ClassUtils.isLambdaClass(clazz)) {
logger.info(
"ignore lambda class: {}, because jdk do not support retransform lambda class: https://github.com/alibaba/arthas/issues/1512.",
clazz.getName());
continue;
}
try {
inst.retransformClasses(clazz);
} catch (Throwable e) {
String errorMsg = "retransformClasses class error, name: " + clazz.getName();
if (ClassUtils.isLambdaClass(clazz) && e instanceof VerifyError) {
errorMsg += ", Please ignore lambda class VerifyError: https://github.com/alibaba/arthas/issues/675";
}
logger.error(errorMsg, e);
}
}

@ -1,8 +1,10 @@
package com.taobao.arthas.core.util;
import com.taobao.arthas.core.command.model.BlockingLockInfo;
import com.taobao.arthas.core.command.model.BusyThreadInfo;
import com.taobao.arthas.core.command.model.StackModel;
import com.taobao.arthas.core.command.model.ThreadNode;
import com.taobao.arthas.core.command.model.ThreadVO;
import com.taobao.arthas.core.view.Ansi;
import java.arthas.SpyAPI;
@ -38,13 +40,13 @@ abstract public class ThreadUtil {
*
* @return
*/
public static Map<String, Thread> getThreads() {
public static Map<String, ThreadVO> getThreads() {
ThreadGroup root = getRoot();
Thread[] threads = new Thread[root.activeCount()];
while (root.enumerate(threads, true) == threads.length) {
threads = new Thread[threads.length * 2];
}
SortedMap<String, Thread> map = new TreeMap<String, Thread>(new Comparator<String>() {
SortedMap<String, ThreadVO> map = new TreeMap<String, ThreadVO>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
@ -52,12 +54,26 @@ abstract public class ThreadUtil {
});
for (Thread thread : threads) {
if (thread != null) {
map.put(thread.getName() + "-" + thread.getId(), thread);
ThreadVO threadVO = createThreadVO(thread);
map.put(thread.getName() + "-" + thread.getId(), threadVO);
}
}
return map;
}
private static ThreadVO createThreadVO(Thread thread) {
ThreadGroup group = thread.getThreadGroup();
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.setInterrupted(thread.isInterrupted());
threadVO.setDaemon(thread.isDaemon());
return threadVO;
}
/**
* 线List
*
@ -78,89 +94,6 @@ abstract public class ThreadUtil {
return result;
}
/**
* get the top N busy thread
* @param sampleInterval the interval between two samples
* @param topN the number of thread
* @return a Map representing <ThreadID, cpuUsage>
*/
public static Map<Long, Long> getTopNThreads(int sampleInterval, int topN) {
List<Thread> threads = getThreadList();
// 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 {
// Sleep for some time
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;
}
}
});
// use LinkedHashMap to preserve insert order
Map<Long, Long> topNThreads = new LinkedHashMap<Long, Long>();
List<Thread> topThreads = topN > 0 && topN <= threads.size()
? threads.subList(0, topN) : threads;
for (Thread thread: topThreads) {
// Compute cpu usage
topNThreads.put(thread.getId(), cpus.get(thread));
}
return topNThreads;
}
/**
* Find the thread and lock that is blocking the most other threads.
@ -233,13 +166,12 @@ abstract public class ThreadUtil {
}
public static String getFullStacktrace(ThreadInfo threadInfo, long cpuUsage) {
return getFullStacktrace(threadInfo, cpuUsage, 0, 0);
public static String getFullStacktrace(ThreadInfo threadInfo) {
return getFullStacktrace(threadInfo, -1, -1, -1, 0, 0);
}
public static String getFullStacktrace(BlockingLockInfo blockingLockInfo) {
return getFullStacktrace(blockingLockInfo.getThreadInfo(), -1, blockingLockInfo.getLockIdentityHashCode(),
return getFullStacktrace(blockingLockInfo.getThreadInfo(), -1, -1, -1, blockingLockInfo.getLockIdentityHashCode(),
blockingLockInfo.getBlockingThreadCount());
}
@ -252,7 +184,7 @@ abstract public class ThreadUtil {
* @param blockingThreadCount 线
* @return the string representation of the thread stack
*/
public static String getFullStacktrace(ThreadInfo threadInfo, long cpuUsage, int lockIdentityHashCode,
public static String getFullStacktrace(ThreadInfo threadInfo, double cpuUsage, long deltaTime, long time, int lockIdentityHashCode,
int blockingThreadCount) {
StringBuilder sb = new StringBuilder("\"" + threadInfo.getThreadName() + "\"" + " Id="
+ threadInfo.getThreadId());
@ -260,6 +192,12 @@ abstract public class ThreadUtil {
if (cpuUsage >= 0 && cpuUsage <= 100) {
sb.append(" cpuUsage=").append(cpuUsage).append("%");
}
if (deltaTime >= 0 ) {
sb.append(" deltaTime=").append(deltaTime).append("ms");
}
if (time >= 0 ) {
sb.append(" time=").append(time).append("ms");
}
sb.append(" ").append(threadInfo.getThreadState());
@ -334,6 +272,102 @@ abstract public class ThreadUtil {
return sb.toString().replace("\t", " ");
}
public static String getFullStacktrace(BusyThreadInfo threadInfo, int lockIdentityHashCode, int blockingThreadCount) {
StringBuilder sb = new StringBuilder("\"" + threadInfo.getName() + "\"");
if (threadInfo.getId() > 0) {
sb.append(" Id=").append(threadInfo.getId());
} else {
sb.append(" [Internal]");
}
double cpuUsage = threadInfo.getCpu();
if (cpuUsage >= 0 && cpuUsage <= 100) {
sb.append(" cpuUsage=").append(cpuUsage).append("%");
}
if (threadInfo.getDeltaTime() >= 0 ) {
sb.append(" deltaTime=").append(threadInfo.getDeltaTime()).append("ms");
}
if (threadInfo.getTime() >= 0 ) {
sb.append(" time=").append(threadInfo.getTime()).append("ms");
}
if (threadInfo.getState() == null) {
sb.append("\n\n");
return sb.toString();
}
sb.append(" ").append(threadInfo.getState());
if (threadInfo.getLockName() != null) {
sb.append(" on ").append(threadInfo.getLockName());
}
if (threadInfo.getLockOwnerName() != null) {
sb.append(" owned by \"").append(threadInfo.getLockOwnerName()).append("\" Id=").append(threadInfo.getLockOwnerId());
}
if (threadInfo.isSuspended()) {
sb.append(" (suspended)");
}
if (threadInfo.isInNative()) {
sb.append(" (in native)");
}
sb.append('\n');
int i = 0;
for (; i < threadInfo.getStackTrace().length; i++) {
StackTraceElement ste = threadInfo.getStackTrace()[i];
sb.append("\tat ").append(ste.toString());
sb.append('\n');
if (i == 0 && threadInfo.getLockInfo() != null) {
Thread.State ts = threadInfo.getState();
switch (ts) {
case BLOCKED:
sb.append("\t- blocked on ").append(threadInfo.getLockInfo());
sb.append('\n');
break;
case WAITING:
sb.append("\t- waiting on ").append(threadInfo.getLockInfo());
sb.append('\n');
break;
case TIMED_WAITING:
sb.append("\t- waiting on ").append(threadInfo.getLockInfo());
sb.append('\n');
break;
default:
}
}
for (MonitorInfo mi : threadInfo.getLockedMonitors()) {
if (mi.getLockedStackDepth() == i) {
sb.append("\t- locked ").append(mi);
if (mi.getIdentityHashCode() == lockIdentityHashCode) {
Ansi highlighted = Ansi.ansi().fg(Ansi.Color.RED);
highlighted.a(" <---- but blocks ").a(blockingThreadCount).a(" other threads!");
sb.append(highlighted.reset().toString());
}
sb.append('\n');
}
}
}
if (i < threadInfo.getStackTrace().length) {
sb.append("\t...");
sb.append('\n');
}
LockInfo[] locks = threadInfo.getLockedSynchronizers();
if (locks.length > 0) {
sb.append("\n\tNumber of locked synchronizers = ").append(locks.length);
sb.append('\n');
for (LockInfo li : locks) {
sb.append("\t- ").append(li);
if (li.getIdentityHashCode() == lockIdentityHashCode) {
sb.append(" <---- but blocks ").append(blockingThreadCount);
sb.append(" other threads!");
}
sb.append('\n');
}
}
sb.append('\n');
return sb.toString().replace("\t", " ");
}
/**
* </pre>
* java.lang.Thread.getStackTrace(Thread.java:1559),

@ -1,159 +0,0 @@
# This is xterm for ncurses.
xterm|xterm terminal emulator (X Window System),
use=xterm-new,
# This version reflects the current xterm features.
xterm-new|modern xterm terminal emulator,
npc,
indn=\E[%p1%dS, kb2=\EOE, kcbt=\E[Z, kent=\EOM,
rin=\E[%p1%dT, use=xterm+pcfkeys, use=xterm+tmux,
use=xterm-basic,
# This chunk is used for building the VT220/Sun/PC keyboard variants.
xterm-basic|modern xterm terminal emulator - common,
OTbs, am, bce, km, mir, msgr, xenl, AX, XT,
colors#8, cols#80, it#8, lines#24, pairs#64,
acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l,
clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=^M,
csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C,
cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A,
cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM,
dl1=\E[M, ech=\E[%p1%dX, ed=\E[J, el=\E[K, el1=\E[1K,
flash=\E[?5h$<100/>\E[?5l, home=\E[H, hpa=\E[%i%p1%dG,
ht=^I, hts=\EH, ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L,
ind=^J, invis=\E[8m, is2=\E[!p\E[?3;4l\E[4l\E>,
kmous=\E[M, meml=\El, memu=\Em, op=\E[39;49m, rc=\E8,
rev=\E[7m, ri=\EM, rmacs=\E(B, rmam=\E[?7l,
rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>,
rmm=\E[?1034l, rmso=\E[27m, rmul=\E[24m, rs1=\Ec,
rs2=\E[!p\E[?3;4l\E[4l\E>, sc=\E7, setab=\E[4%p1%dm,
setaf=\E[3%p1%dm,
setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
sgr0=\E(B\E[m, smacs=\E(0, smam=\E[?7h, smcup=\E[?1049h,
smir=\E[4h, smkx=\E[?1h\E=, smm=\E[?1034h, smso=\E[7m,
smul=\E[4m, tbc=\E[3g, vpa=\E[%i%p1%dd, E3=\E[3;J,
use=ansi+pp, use=xterm+kbs, use=vt100+enq,
# This fragment is for people who cannot agree on what the backspace key
# should send.
xterm+kbs|fragment for backspace key,
kbs=^H,
#
# This fragment describes as much of XFree86 xterm's "pc-style" function
# keys as will fit into terminfo's 60 function keys.
# From ctlseqs.ms:
# Code Modifiers
# ---------------------------------
# 2 Shift
# 3 Alt
# 4 Shift + Alt
# 5 Control
# 6 Shift + Control
# 7 Alt + Control
# 8 Shift + Alt + Control
# ---------------------------------
# The meta key may also be used as a modifier in this scheme, adding another
# bit to the parameter.
xterm+pcfkeys|fragment for PC-style fkeys,
use=xterm+app, use=xterm+pcf2, use=xterm+pcc2,
use=xterm+pce2,
# This chunk is based on suggestions by Ailin Nemui and Nicholas Marriott, who
# asked for some of xterm's advanced features to be added to its terminfo
# entry. It defines extended capabilities not found in standard terminfo or
# termcap. These are useful in tmux, for instance, hence the name.
#
# One caveat in adding extended capabilities in ncurses is that if the names
# are longer than two characters, then they will not be visible through the
# termcap interface.
#
# Ms modifies the selection/clipboard. Its parameters are
# p1 = the storage unit (clipboard, selection or cut buffer)
# p2 = the base64-encoded clipboard content.
#
# Ss is used to set the cursor style as described by the DECSCUSR
# function to a block or underline.
# Se resets the cursor style to the terminal power-on default.
#
# Cs and Cr set and reset the cursor colour.
xterm+tmux|advanced xterm features used in tmux,
Cr=\E]112\007, Cs=\E]12;%p1%s\007,
Ms=\E]52;%p1%s;%p2%s\007, Se=\E[2 q, Ss=\E[%p1%d q,
xterm+app|fragment with cursor keys in application mode,
kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, kend=\EOF,
khome=\EOH,
#
xterm+pcf2|fragment with modifyFunctionKeys:2,
kf1=\EOP, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~,
kf13=\E[1;2P, kf14=\E[1;2Q, kf15=\E[1;2R, kf16=\E[1;2S,
kf17=\E[15;2~, kf18=\E[17;2~, kf19=\E[18;2~, kf2=\EOQ,
kf20=\E[19;2~, kf21=\E[20;2~, kf22=\E[21;2~,
kf23=\E[23;2~, kf24=\E[24;2~, kf25=\E[1;5P, kf26=\E[1;5Q,
kf27=\E[1;5R, kf28=\E[1;5S, kf29=\E[15;5~, kf3=\EOR,
kf30=\E[17;5~, kf31=\E[18;5~, kf32=\E[19;5~,
kf33=\E[20;5~, kf34=\E[21;5~, kf35=\E[23;5~,
kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q, kf39=\E[1;6R,
kf4=\EOS, kf40=\E[1;6S, kf41=\E[15;6~, kf42=\E[17;6~,
kf43=\E[18;6~, kf44=\E[19;6~, kf45=\E[20;6~,
kf46=\E[21;6~, kf47=\E[23;6~, kf48=\E[24;6~,
kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q, kf51=\E[1;3R,
kf52=\E[1;3S, kf53=\E[15;3~, kf54=\E[17;3~,
kf55=\E[18;3~, kf56=\E[19;3~, kf57=\E[20;3~,
kf58=\E[21;3~, kf59=\E[23;3~, kf6=\E[17~, kf60=\E[24;3~,
kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[1;4R, kf7=\E[18~,
kf8=\E[19~, kf9=\E[20~,
#
xterm+pcc2|fragment with modifyCursorKeys:2,
kLFT=\E[1;2D, kRIT=\E[1;2C, kind=\E[1;2B, kri=\E[1;2A,
kDN=\E[1;2B, kDN3=\E[1;3B, kDN4=\E[1;4B, kDN5=\E[1;5B,
kDN6=\E[1;6B, kDN7=\E[1;7B, kLFT3=\E[1;3D, kLFT4=\E[1;4D,
kLFT5=\E[1;5D, kLFT6=\E[1;6D, kLFT7=\E[1;7D,
kRIT3=\E[1;3C, kRIT4=\E[1;4C, kRIT5=\E[1;5C,
kRIT6=\E[1;6C, kRIT7=\E[1;7C, kUP=\E[1;2A, kUP3=\E[1;3A,
kUP4=\E[1;4A, kUP5=\E[1;5A, kUP6=\E[1;6A, kUP7=\E[1;7A,
# Chunks from xterm #230:
xterm+pce2|fragment with modifyCursorKeys:2,
kDC=\E[3;2~, kEND=\E[1;2F, kHOM=\E[1;2H, kIC=\E[2;2~,
kNXT=\E[6;2~, kPRV=\E[5;2~, kich1=\E[2~, knp=\E[6~,
kpp=\E[5~, kDC3=\E[3;3~, kDC4=\E[3;4~, kDC5=\E[3;5~,
kDC6=\E[3;6~, kDC7=\E[3;7~, kEND3=\E[1;3F, kEND4=\E[1;4F,
kEND5=\E[1;5F, kEND6=\E[1;6F, kEND7=\E[1;7F,
kHOM3=\E[1;3H, kHOM4=\E[1;4H, kHOM5=\E[1;5H,
kHOM6=\E[1;6H, kHOM7=\E[1;7H, kIC3=\E[2;3~, kIC4=\E[2;4~,
kIC5=\E[2;5~, kIC6=\E[2;6~, kIC7=\E[2;7~, kNXT3=\E[6;3~,
kNXT4=\E[6;4~, kNXT5=\E[6;5~, kNXT6=\E[6;6~,
kNXT7=\E[6;7~, kPRV3=\E[5;3~, kPRV4=\E[5;4~,
kPRV5=\E[5;5~, kPRV6=\E[5;6~, kPRV7=\E[5;7~,
use=xterm+edit,
xterm+edit|fragment for 6-key editing-keypad,
kdch1=\E[3~, kich1=\E[2~, knp=\E[6~, kpp=\E[5~,
use=xterm+pc+edit,
xterm+pc+edit|fragment for pc-style editing keypad,
kend=\E[4~, khome=\E[1~,
ansi+pp|ansi printer port,
mc5i,
mc0=\E[i, mc4=\E[4i, mc5=\E[5i,
vt100+enq|ncurses extension for vt100-style ENQ,
u8=\E[?1;2c, use=ansi+enq,
ansi+enq|ncurses extension for ANSI ENQ,
u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?%[;0123456789]c,
u9=\E[c,

@ -37,7 +37,7 @@ public class EnhancerTest {
TestHelper.appendSpyJar(instrumentation);
ArthasBootstrap.getInstance(instrumentation, "");
ArthasBootstrap.getInstance(instrumentation, "ip=127.0.0.1");
AdviceListener listener = Mockito.mock(AdviceListener.class);

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-demo</artifactId>

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-memorycompiler</artifactId>

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-packaging</artifactId>

@ -48,7 +48,7 @@
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<packaging>pom</packaging>
<name>arthas-all</name>
@ -133,7 +133,7 @@
<maven-invoker-plugin.version>3.0.0</maven-invoker-plugin.version>
<grpc.version>1.14.0</grpc.version>
<jacoco.skip>${skipTests}</jacoco.skip>
<project.build.outputTimestamp>2020-09-02T15:32:32Z</project.build.outputTimestamp>
<project.build.outputTimestamp>2020-09-27T15:10:43Z</project.build.outputTimestamp>
</properties>
<dependencyManagement>
@ -146,7 +146,7 @@
<dependency>
<groupId>com.alibaba.middleware</groupId>
<artifactId>termd-core</artifactId>
<version>1.1.7.9</version>
<version>1.1.7.10</version>
</dependency>
<dependency>
<groupId>com.alibaba.middleware</groupId>
@ -171,12 +171,12 @@
<dependency>
<groupId>com.alibaba.arthas</groupId>
<artifactId>arthas-repackage-logger</artifactId>
<version>0.0.4</version>
<version>0.0.6</version>
</dependency>
<dependency>
<groupId>com.alibaba.arthas</groupId>
<artifactId>arthas-repackage-asm</artifactId>
<version>0.0.4</version>
<version>0.0.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-site</artifactId>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 KiB

After

Width:  |  Height:  |  Size: 791 KiB

@ -18,53 +18,65 @@ dashboard
```
$ dashboard
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTED DAEMON
889 RMI TCP Connection(15)-30.10.166. RMI Runtime 9 RUNNABLE 48 0:5 false true
1077 Timer-for-arthas-dashboard-0 system 9 RUNNABLE 24 0:0 false true
1074 as-selector-daemon system 9 RUNNABLE 12 0:0 false true
284 JMX server connection timeout 284 RMI Runtime 9 TIMED_WAITI 8 0:3 false true
16 Timer-1 main 5 TIMED_WAITI 5 0:9 false true
47 Pandora pandora-qos-reporter Pool main 5 TIMED_WAITI 0 0:0 false true
48 JmonitorClient-CheckThread Pool [ main 5 TIMED_WAITI 0 0:0 false true
49 JmonitorClient-HeartBeat Pool [Th main 5 TIMED_WAITI 0 0:0 false true
50 JmonitorClient-ReaderThread Pool main 5 TIMED_WAITI 0 0:0 false true
957 RMI TCP Connection(16)-30.10.166. RMI Runtime 9 RUNNABLE 0 0:2 false true
51 JmonitorClient-WriterThread Pool main 5 TIMED_WAITI 0 0:0 false true
52 ContainerBackgroundProcessor[Stan main 5 TIMED_WAITI 0 0:0 false true
53 http-bio-8080-Acceptor-0 main 5 RUNNABLE 0 0:2 false true
54 http-bio-8080-AsyncTimeout main 5 TIMED_WAITI 0 0:0 false true
11 GC Daemon system 2 TIMED_WAITI 0 0:0 false true
Memory used total max usage GC
heap 59M 223M 1820M 3.26% gc.ps_scavenge.count 118
ps_eden_space 14M 114M 668M 2.11% gc.ps_scavenge.time(ms) 1890
ps_survivor_space 6M 6M 6M 96.08% gc.ps_marksweep.count 5
ps_old_gen 39M 103M 1365M 2.86% gc.ps_marksweep.time(ms) 1140
nonheap 234M 240M 0M 97.46%
code_cache 46M 47M 240M 19.49%
metaspace 167M 172M 0M 97.36%
Runtime Tomcat
os.name Mac OS X connector http-bio-8080
os.version 10.10.5 QPS 0.00
java.version 1.8.0_60 RT(ms) 1.13
java.home error/s 0.00
received/s 0B
systemload.average 3.44 sent/s 0B
processors 4 threadpool http-bio-8080
uptime 16020s busy 0
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTE DAEMON
-1 C2 CompilerThread0 - -1 - 1.55 0.077 0:8.684 false true
53 Timer-for-arthas-dashboard-07b system 5 RUNNABLE 0.08 0.004 0:0.004 false true
22 scheduling-1 main 5 TIMED_WAI 0.06 0.003 0:0.287 false false
-1 C1 CompilerThread0 - -1 - 0.06 0.003 0:2.171 false true
-1 VM Periodic Task Thread - -1 - 0.03 0.001 0:0.092 false true
49 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.02 0.001 0:0.156 false true
16 Catalina-utility-1 main 1 TIMED_WAI 0.0 0.000 0:0.029 false false
-1 G1 Young RemSet Sampling - -1 - 0.0 0.000 0:0.019 false true
17 Catalina-utility-2 main 1 WAITING 0.0 0.000 0:0.025 false false
34 http-nio-8080-ClientPoller main 5 RUNNABLE 0.0 0.000 0:0.016 false true
23 http-nio-8080-BlockPoller main 5 RUNNABLE 0.0 0.000 0:0.011 false true
-1 VM Thread - -1 - 0.0 0.000 0:0.032 false true
-1 Service Thread - -1 - 0.0 0.000 0:0.006 false true
-1 GC Thread#5 - -1 - 0.0 0.000 0:0.043 false true
Memory used total max usage GC
heap 36M 70M 4096M 0.90% gc.g1_young_generation.count 12
g1_eden_space 6M 18M -1 33.33% 86
g1_old_gen 30M 50M 4096M 0.74% gc.g1_old_generation.count 0
g1_survivor_space 491K 2048K -1 24.01% gc.g1_old_generation.time(ms) 0
nonheap 66M 69M -1 96.56%
codeheap_'non-nmethods' 1M 2M 5M 22.39%
metaspace 46M 47M -1 98.01%
Runtime
os.name Mac OS X
os.version 10.15.4
java.version 15
java.home /Library/Java/JavaVirtualMachines/jdk-15.jdk/Contents/Home
systemload.average 10.68
processors 8
uptime 272s
```
### 数据说明
* ID: Java级别的线程ID注意这个ID不能跟jstack中的nativeID一一对应
* ID: Java级别的线程ID注意这个ID不能跟jstack中的nativeID一一对应
* NAME: 线程名
* GROUP: 线程组名
* PRIORITY: 线程优先级, 1~10之间的数字越大表示优先级越高
* STATE: 线程的状态
* CPU%: 线程消耗的cpu占比采样100ms将所有线程在这100ms内的cpu使用量求和再算出每个线程的cpu使用占比。
* TIME: 线程运行总时间,数据格式为`分:秒`
* CPU%: 线程的cpu使用率。比如采样间隔1000ms某个线程的增量cpu时间为100ms则cpu使用率=100/1000=10%
* DELTA_TIME: 上次采样之后线程运行增量CPU时间数据格式为`秒`
* TIME: 线程运行总CPU时间数据格式为`分:秒`
* INTERRUPTED: 线程当前的中断位状态
* DAEMON: 是否是daemon线程
#### JVM内部线程
Java 8之后支持获取JVM内部线程CPU时间这些线程只有名称和CPU时间没有ID及状态等信息显示ID为-1
通过内部线程可以观测到JVM活动如GC、JIT编译等占用CPU情况方便了解JVM整体运行状况。
* 当JVM 堆(heap)/元数据(metaspace)空间不足或OOM时可以看到GC线程的CPU占用率明显高于其他的线程。
* 当执行`trace/watch/tt/redefine`等命令后可以看到JIT线程活动变得更频繁。因为JVM热更新class字节码时清除了此class相关的JIT编译结果需要重新编译。
JVM内部线程包括下面几种
* JIT编译线程: 如 `C1 CompilerThread0`, `C2 CompilerThread0`
* GC线程: 如`GC Thread0`, `G1 Young RemSet Sampling`
* 其它内部线程: 如`VM Periodic Task Thread`, `VM Thread`, `Service Thread`
### 截图展示
![](_static/dashboard.png "dashboard")
![](_static/dashboard.png "dashboard")

@ -1,6 +1,59 @@
Docker
===
## 在Docker里使用JDK
很多时候应用在docker里出现arthas无法工作的问题是因为应用没有安装 JDK ,而是安装了 JRE 。如果只安装了 JRE则会缺少很多JAVA的命令行工具和类库Arthas也没办法正常工作。下面介绍两种常见的在Docker里使用JDK的方式。
### 使用公开的JDK镜
* https://hub.docker.com/_/openjdk/
比如:
```
FROM openjdk:8-jdk
```
或者:
```
FROM openjdk:8-jdk-alpine
```
### 通过包管理软件来安装
比如:
```bash
# Install OpenJDK-8
RUN apt-get update && \
apt-get install -y openjdk-8-jdk && \
apt-get install -y ant && \
apt-get clean;
# Fix certificate issues
RUN apt-get update && \
apt-get install ca-certificates-java && \
apt-get clean && \
update-ca-certificates -f;
# Setup JAVA_HOME -- useful for docker commandline
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/
RUN export JAVA_HOME
```
或者:
```bash
RUN yum install -y \
java-1.8.0-openjdk \
java-1.8.0-openjdk-devel
ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk/
RUN export JAVA_HOME
```
## 通过Docker快速入门
1. 删除本地已有的`arthas-demo` docker container非必要

@ -18,39 +18,37 @@ When running in Apache Tomcat Alibaba edition, the dashboard will also present t
```
$ dashboard
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTED DAEMON
889 RMI TCP Connection(15)-30.10.166. RMI Runtime 9 RUNNABLE 48 0:5 false true
1077 Timer-for-arthas-dashboard-0 system 9 RUNNABLE 24 0:0 false true
1074 as-selector-daemon system 9 RUNNABLE 12 0:0 false true
284 JMX server connection timeout 284 RMI Runtime 9 TIMED_WAITI 8 0:3 false true
16 Timer-1 main 5 TIMED_WAITI 5 0:9 false true
47 Pandora pandora-qos-reporter Pool main 5 TIMED_WAITI 0 0:0 false true
48 JmonitorClient-CheckThread Pool [ main 5 TIMED_WAITI 0 0:0 false true
49 JmonitorClient-HeartBeat Pool [Th main 5 TIMED_WAITI 0 0:0 false true
50 JmonitorClient-ReaderThread Pool main 5 TIMED_WAITI 0 0:0 false true
957 RMI TCP Connection(16)-30.10.166. RMI Runtime 9 RUNNABLE 0 0:2 false true
51 JmonitorClient-WriterThread Pool main 5 TIMED_WAITI 0 0:0 false true
52 ContainerBackgroundProcessor[Stan main 5 TIMED_WAITI 0 0:0 false true
53 http-bio-8080-Acceptor-0 main 5 RUNNABLE 0 0:2 false true
54 http-bio-8080-AsyncTimeout main 5 TIMED_WAITI 0 0:0 false true
11 GC Daemon system 2 TIMED_WAITI 0 0:0 false true
Memory used total max usage GC
heap 59M 223M 1820M 3.26% gc.ps_scavenge.count 118
ps_eden_space 14M 114M 668M 2.11% gc.ps_scavenge.time(ms) 1890
ps_survivor_space 6M 6M 6M 96.08% gc.ps_marksweep.count 5
ps_old_gen 39M 103M 1365M 2.86% gc.ps_marksweep.time(ms) 1140
nonheap 234M 240M 0M 97.46%
code_cache 46M 47M 240M 19.49%
metaspace 167M 172M 0M 97.36%
Runtime Tomcat
os.name Mac OS X connector http-bio-8080
os.version 10.10.5 QPS 0.00
java.version 1.8.0_60 RT(ms) 1.13
java.home error/s 0.00
received/s 0B
systemload.average 3.44 sent/s 0B
processors 4 threadpool http-bio-8080
uptime 16020s busy 0
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTE DAEMON
-1 C2 CompilerThread0 - -1 - 1.55 0.077 0:8.684 false true
53 Timer-for-arthas-dashboard-07b system 5 RUNNABLE 0.08 0.004 0:0.004 false true
22 scheduling-1 main 5 TIMED_WAI 0.06 0.003 0:0.287 false false
-1 C1 CompilerThread0 - -1 - 0.06 0.003 0:2.171 false true
-1 VM Periodic Task Thread - -1 - 0.03 0.001 0:0.092 false true
49 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.02 0.001 0:0.156 false true
16 Catalina-utility-1 main 1 TIMED_WAI 0.0 0.000 0:0.029 false false
-1 G1 Young RemSet Sampling - -1 - 0.0 0.000 0:0.019 false true
17 Catalina-utility-2 main 1 WAITING 0.0 0.000 0:0.025 false false
34 http-nio-8080-ClientPoller main 5 RUNNABLE 0.0 0.000 0:0.016 false true
23 http-nio-8080-BlockPoller main 5 RUNNABLE 0.0 0.000 0:0.011 false true
-1 VM Thread - -1 - 0.0 0.000 0:0.032 false true
-1 Service Thread - -1 - 0.0 0.000 0:0.006 false true
-1 GC Thread#5 - -1 - 0.0 0.000 0:0.043 false true
Memory used total max usage GC
heap 36M 70M 4096M 0.90% gc.g1_young_generation.count 12
g1_eden_space 6M 18M -1 33.33% 86
g1_old_gen 30M 50M 4096M 0.74% gc.g1_old_generation.count 0
g1_survivor_space 491K 2048K -1 24.01% gc.g1_old_generation.time(ms) 0
nonheap 66M 69M -1 96.56%
codeheap_'non-nmethods' 1M 2M 5M 22.39%
metaspace 46M 47M -1 98.01%
Runtime
os.name Mac OS X
os.version 10.15.4
java.version 15
java.home /Library/Java/JavaVirtualMachines/jdk-15.jdk/Contents/Home
systemload.average 10.68
processors 8
uptime 272s
```
### Notes on column headers
@ -60,11 +58,31 @@ uptime 16020s busy
* GROUP: thread group name
* PRIORITY: thread priority, ranged from 1 to 10. The greater number, the higher priority
* STATE: thread state
* CPU%: the ratio of CPU usage for the thread, sampled every 100ms
* TIME: total running time in `minute:second` format
* CPU%: the ratio of CPU usage for the thread. For example, the sampling interval is 1000ms, and the incremental cpu time
of a thread is 100ms, then the cpu usage rate=100/1000=10%
* DELTA_TIME: incremental CPU time of thread running after the last sampling in `second` format
* TIME: total CPU time of the thread in `minute:second` format
* INTERRUPTED: the thread interruption state
* DAEMON: daemon thread or not
#### JVM internal threads
After Java 8, it is supported to obtain the CPU time of JVM internal threads. These threads only have the name and CPU time,
without ID and status information (display ID is -1).
JVM activities can be observed through internal threads, such as GC, JIT compilation, etc., to perceive the overall status of JVM.
* When the JVM heap/metaspace space is insufficient or OOM, it can be seen that the CPU usage of the GC threads is
significantly higher than other threads.
* After executing commands such as `trace/watch/tt/redefine`, you can see that JIT threads activities become more frequent.
Because the JIT compilation data related to this class is cleared when the JVM hot update the class bytecode, it needs to be recompiled.
JVM internal threads include the following:
* JIT compilation thread: such as `C1 CompilerThread0`, `C2 CompilerThread0`
* GC thread: such as `GC Thread0`, `G1 Young RemSet Sampling`
* Other internal threads: such as`VM Periodic Task Thread`, `VM Thread`, `Service Thread`
### Screenshot
![](../_static/dashboard.png "dashboard")

@ -1,6 +1,61 @@
Docker
===
## Use JDK in Docker
Many times, the problem that arthas can't work with the application in docker is because the docker does not install JDK, but installs JRE. If only JRE is installed, many JAVA command line tools and class libraries will be missing, and Arthas will not work properly. Here are two common ways to use JDK in Docker.
### Use public JDK image
* https://hub.docker.com/_/openjdk/
such as:
```
FROM openjdk:8-jdk
```
or:
```
FROM openjdk:8-jdk-alpine
```
### Install via package management software
such as:
```bash
# Install OpenJDK-8
RUN apt-get update && \
apt-get install -y openjdk-8-jdk && \
apt-get install -y ant && \
apt-get clean;
# Fix certificate issues
RUN apt-get update && \
apt-get install ca-certificates-java && \
apt-get clean && \
update-ca-certificates -f;
# Setup JAVA_HOME - useful for docker commandline
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/
RUN export JAVA_HOME
```
or:
```bash
RUN yum install -y \
java-1.8.0-openjdk \
java-1.8.0-openjdk-devel
ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk/
RUN export JAVA_HOME
```
## Quick start with Docker
1. Delete the existing `arthas-demo` docker container (not necessary)

@ -181,3 +181,49 @@ $ profiler version
Async-profiler 1.6 built on Sep 9 2019
Copyright 2019 Andrei Pangin
```
### Configure framebuf option
> If you encounter `[frame_buffer_overflow]` in the generated svg image, you need to increase the framebuf (the default value is 1'000'000), which can be configured explicitly, such as:
```bash
profiler start --framebuf 5000000
```
### Configure include/exclude to filter data
If the application is complex and generates a lot of content, and you want to focus on only part of the data, you can filter by include/exclude. such as
```bash
profiler start --include'java/*' --include'demo/*' --exclude'*Unsafe.park*'
```
> Both include/exclude support setting multiple values, but need to be configured at the end of the command line.
### Specify execution time
For example, if you want the profiler to automatically end after 300 seconds, you can specify it with the `-d`/`--duration` parameter:
```bash
profiler start --duration 300
```
### Generate jfr format result
> Note that jfr only supports configuration at `start`. If it is specified at `stop`, it will not take effect.
```
profiler start --file /tmp/test.jfr
```
The `file` parameter supports some variables:
* Timestamp: `--file /tmp/test-%t.jfr`
* Process ID: `--file /tmp/test-%p.jfr`
The generated results can be viewed with tools that support the jfr format. such as:
* JDK Mission Control: https://github.com/openjdk/jmc
* JProfiler: https://github.com/alibaba/arthas/issues/1416

@ -13,11 +13,23 @@ thread
|`[n:]`|the top n busiest threads with stack traces printed|
|`[b]`|locate the thread blocking the others|
|[i `<value>`]|specify the interval to collect data to compute CPU ratios (ms)|
|[--all]|Show all matching threads|
> How the CPU ratios are calculated? <br/><br/>
> CPU ratio for a given thread is the CPU time it takes divided by the total CPU time within a specified interval period. It is calculated in the following way: sample CPU times for all the thread by calling `java.lang.management.ThreadMXBean#getThreadCpuTime` first, then sleep for a period (the default value is 100ms, which can be specified by `-i`), then sample CPU times again. By this, we can get the time cost for this period for each thread, then come up with the ratio. <br/><br/>
> Note: this operation consumes CPU time too (`getThreadCpuTime` is time-consuming), therefore it is possible to observe Arthas's thread appears in the list. To avoid this, try to increase sample interval, for example: 5000 ms.<br/><br/>
> If you'd like to check the CPU ratios from the very beginning of the Java process, [show-busy-java-threads](https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#-show-busy-java-threads) can come to help.
### How the CPU ratios are calculated?
The cpu ratios here is similar to the thread `%CPU` of the linux command `top -H -p <pid>`. During a sampling interval,
the ratio of the incremental cpu time of each thread in the current JVM to the sampling interval time.
> Working principle description:
* Do the first sampling, get the CPU time of all threads ( by calling `java.lang.management.ThreadMXBean#getThreadCpuTime()` and
`sun.management.HotspotThreadMBean.getInternalThreadCpuTimes()` )
* Sleep and wait for an interval (the default is 200ms, the interval can be specified by `-i`)
* Do the second sampling, get the CPU time of all threads, compare the two sampling data, and calculate the incremental CPU time of each thread
* `Thread CPU usage ratio` = `Thread increment CPU time` / `Sampling interval time` * 100%
> Note: this operation consumes CPU time too (`getThreadCpuTime` is time-consuming), therefore it is possible to observe Arthas's thread appears in the list. To avoid this, try to increase sample interval, for example: 5000 ms.<br/>
> Another way to view the thread cpu usage of the Java process, [show-busy-java-threads](https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#-show-busy-java-threads) can come to help.
### Usage
@ -25,59 +37,72 @@ thread
```shell
$ thread -n 3
"as-command-execute-daemon" Id=29 cpuUsage=75% RUNNABLE
at sun.management.ThreadImpl.dumpThreads0(Native Method)
at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:440)
at com.taobao.arthas.core.command.monitor200.ThreadCommand$1.action(ThreadCommand.java:58)
at com.taobao.arthas.core.command.handler.AbstractCommandHandler.execute(AbstractCommandHandler.java:238)
at com.taobao.arthas.core.command.handler.DefaultCommandHandler.handleCommand(DefaultCommandHandler.java:67)
at com.taobao.arthas.core.server.ArthasServer$4.run(ArthasServer.java:276)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Number of locked synchronizers = 1
- java.util.concurrent.ThreadPoolExecutor$Worker@6cd0b6f8
"C1 CompilerThread0" [Internal] cpuUsage=1.63% deltaTime=3ms time=1170ms
"arthas-command-execute" Id=23 cpuUsage=0.11% deltaTime=0ms time=401ms RUNNABLE
at java.management@11.0.7/sun.management.ThreadImpl.dumpThreads0(Native Method)
at java.management@11.0.7/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:466)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:199)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
at java.base@11.0.7/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base@11.0.7/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base@11.0.7/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base@11.0.7/java.lang.Thread.run(Thread.java:834)
"VM Periodic Task Thread" [Internal] cpuUsage=0.07% deltaTime=0ms time=584ms
```
* Without thread ID, including `[Internal]` means JVM internal thread, refer to the introduction of [dashboard](dashboard.md) command.
* `cpuUsage` is the CPU usage of the thread during the sampling interval, consistent with the data of the [dashboard](dashboard.md) command.
* `deltaTime` is the incremental CPU time of the thread during the sampling interval. If it is less than 1ms, it will be rounded and displayed as 0ms.
* `time` The total CPU time of thread.
"as-session-expire-daemon" Id=25 cpuUsage=24% TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.taobao.arthas.core.server.DefaultSessionManager$2.run(DefaultSessionManager.java:85)
**Note:** The thread stack is acquired at the end of the second sampling, which does not indicate that the thread is
processing the same task during the sampling interval. It is recommended that the interval time should not be too long.
The larger the interval time, the more inaccurate.
You can try to specify different intervals according to the specific situation and observe the output results.
"Reference Handler" Id=2 cpuUsage=0% WAITING on java.lang.ref.Reference$Lock@69ba0f27
at java.lang.Object.wait(Native Method)
- waiting on java.lang.ref.Reference$Lock@69ba0f27
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
```
#### List first page threads' info when no options provided
#### List all threads' info when no options provided
By default, they are arranged in descending order of CPU increment time, and only the first page of data is displayed.
```shell
$ thread
Threads Total: 16, NEW: 0, RUNNABLE: 7, BLOCKED: 0, WAITING: 5, TIMED_WAITING: 4, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTE DAEMON
30 as-command-execute-daemon system 9 RUNNABLE 72 0:0 false true
23 as-session-expire-daemon system 9 TIMED_WAIT 27 0:0 false true
22 Attach Listener system 9 RUNNABLE 0 0:0 false true
11 pool-2-thread-1 main 5 TIMED_WAIT 0 0:0 false false
12 Thread-2 main 5 RUNNABLE 0 0:0 false true
13 pool-3-thread-1 main 5 TIMED_WAIT 0 0:0 false false
25 as-selector-daemon system 9 RUNNABLE 0 0:0 false true
14 Thread-3 main 5 TIMED_WAIT 0 0:0 false false
26 pool-5-thread-1 system 5 WAITING 0 0:0 false false
15 Thread-4 main 5 RUNNABLE 0 0:0 false false
1 main main 5 WAITING 0 0:2 false false
2 Reference Handler system 10 WAITING 0 0:0 false true
3 Finalizer system 8 WAITING 0 0:0 false true
4 Signal Dispatcher system 9 RUNNABLE 0 0:0 false true
20 NonBlockingInputStreamThread main 5 WAITING 0 0:0 false true
21 Thread-8 main 5 RUNNABLE 0 0:0 false true
Threads Total: 33, NEW: 0, RUNNABLE: 9, BLOCKED: 0, WAITING: 3, TIMED_WAITING: 4, TERMINATED: 0, Internal threads: 17
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPT DAEMON
-1 C2 CompilerThread0 - -1 - 5.06 0.010 0:0.973 false true
-1 C1 CompilerThread0 - -1 - 0.95 0.001 0:0.603 false true
23 arthas-command-execute system 5 RUNNABLE 0.17 0.000 0:0.226 false true
-1 VM Periodic Task Thread - -1 - 0.05 0.000 0:0.094 false true
-1 Sweeper thread - -1 - 0.04 0.000 0:0.011 false true
-1 G1 Young RemSet Sampling - -1 - 0.02 0.000 0:0.025 false true
12 Attach Listener system 9 RUNNABLE 0.0 0.000 0:0.022 false true
11 Common-Cleaner InnocuousThrea 8 TIMED_WAI 0.0 0.000 0:0.000 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.000 false true
2 Reference Handler system 10 RUNNABLE 0.0 0.000 0:0.000 false true
4 Signal Dispatcher system 9 RUNNABLE 0.0 0.000 0:0.000 false true
15 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.029 false true
22 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.196 false true
24 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.038 false true
16 arthas-NettyWebsocketTtyBootst system 5 RUNNABLE 0.0 0.000 0:0.001 false true
17 arthas-NettyWebsocketTtyBootst system 5 RUNNABLE 0.0 0.000 0:0.001 false true
```
#### thread --all, show all matching threads
Display all matching threads. Sometimes it is necessary to obtain all the thread data of the JVM for analysis.
#### thread id, show the running stack for the target thread
```shell
@ -140,6 +165,10 @@ $ thread -b
#### thread -i, specify the sampling interval
* `thread -i 1000`: Count the thread cpu time of the last 1000ms.
* `thread -n 3 -i 1000`: List the 3 busiest thread stacks in 1000ms
```bash
$ thread -n 3 -i 1000
"as-command-execute-daemon" Id=4759 cpuUsage=23% RUNNABLE
@ -165,11 +194,9 @@ $ thread -n 3 -i 1000
```bash
[arthas@28114]$ thread --state WAITING
Threads Total: 15, NEW: 0, RUNNABLE: 7, BLOCKED: 0, WAITING: 5, TIMED_WAITING: 3, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRU DAEMON
198 AsyncAppender-Worker-arth system 9 WAITING 0 0:0 false true
3 Finalizer system 8 WAITING 0 0:0 false true
14 RMI Scheduler(0) system 9 WAITING 0 0:0 false true
2 Reference Handler system 10 WAITING 0 0:0 false true
204 pool-8-thread-1 system 5 WAITING 0 0:0 false false
Threads Total: 16, NEW: 0, RUNNABLE: 9, BLOCKED: 0, WAITING: 3, TIMED_WAITING: 4, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTE DAEMON
3 Finalizer system 8 WAITING 0.0 0.000 0:0.000 false true
20 arthas-UserStat system 9 WAITING 0.0 0.000 0:0.001 false true
14 arthas-timer system 9 WAITING 0.0 0.000 0:0.000 false true
```

@ -29,9 +29,9 @@ java -jar arthas-tunnel-server.jar
By default, the web port of the arthas tunnel server is `8080`, and the port connected by the arthas agent is `7777`.
Once started, you can go to [http://localhost:8080/](http://localhost:8080/) and connect to the registered arthas agent via `agentId`.
Once started, you can go to [http://127.0.0.1:8080/](http://127.0.0.1:8080/) and connect to the registered arthas agent via `agentId`.
Through Spring Boot's Endpoint, you can view the specific connection information: [http://localhost:8080/actuator/arthas](http://localhost:8080/actuator/arthas), the login user name is `arthas`, and the password can be found in the log of arthas tunnel server, for example:
Through Spring Boot's Endpoint, you can view the specific connection information: [http://127.0.0.1:8080/actuator/arthas](http://127.0.0.1:8080/actuator/arthas), the login user name is `arthas`, and the password can be found in the log of arthas tunnel server, for example:
```
32851 [main] INFO o.s.b.a.s.s.UserDetailsServiceAutoConfiguration
@ -44,6 +44,12 @@ Using generated security password: f1dca050-3777-48f4-a577-6367e55a78a2
When starting arthas, you can use the `--tunnel-server` parameter, for example:
```bash
as.sh --tunnel-server 'ws://127.0.0.1:7777/ws'
```
You can also use the following test address (not guaranteed to be available all the time):
```bash
as.sh --tunnel-server 'ws://47.75.156.201:7777/ws'
```

@ -183,3 +183,49 @@ $ profiler version
Async-profiler 1.6 built on Sep 9 2019
Copyright 2019 Andrei Pangin
```
### 配置 framebuf 参数
> 如果遇到生成的svg图片有 `[frame_buffer_overflow]`,则需要增大 framebuf默认值是 1'000'000可以显式配置比如
```bash
profiler start --framebuf 5000000
```
### 配置 include/exclude 来过滤数据
如果应用比较复杂,生成的内容很多,想只关注部分数据,可以通过 include/exclude 来过滤。比如
```bash
profiler start --include 'java/*' --include 'demo/*' --exclude '*Unsafe.park*'
```
> include/exclude 都支持设置多个值 ,但是需要配置在命令行的最后。
### 指定执行时间
比如希望profiler执行 300 秒自动结束,可以用 `-d`/`--duration` 参数指定:
```bash
profiler start --duration 300
```
### 生成 jfr格式结果
> 注意jfr只支持在 `start`时配置。如果是在`stop`时指定,则不会生效。
```
profiler start --file /tmp/test.jfr
```
`file`参数支持一些变量:
* 时间戳: `--file /tmp/test-%t.jfr`
* 进程ID `--file /tmp/test-%p.jfr`
生成的结果可以用支持jfr格式的工具来查看。比如
* JDK Mission Control https://github.com/openjdk/jmc
* JProfiler https://github.com/alibaba/arthas/issues/1416

@ -17,6 +17,8 @@ Arthas Spring Boot Starter
应用启动后spring会启动arthas并且attach自身进程。
> 一键创建包含 Arthas Spring Boot Starter 的工程:<a href="http://start.aliyun.com/bootstrap.html/#!dependencies=arthas" target="_blank">点击</a>
### 配置属性

@ -13,16 +13,23 @@ thread
|*id*|线程id|
|[n:]|指定最忙的前N个线程并打印堆栈|
|[b]|找出当前阻塞其他线程的线程|
|[i `<value>`]|指定cpu占比统计的采样间隔单位为毫秒|
|[i `<value>`]|指定cpu使用率统计的采样间隔单位为毫秒默认值为200|
|[--all]|显示所有匹配的线程|
> cpu占比是如何统计出来的?
### cpu使用率是如何统计出来的?
> 这里的cpu统计的是一段采样间隔内当前JVM里各个线程所占用的cpu时间占总cpu时间的百分比。其计算方法为
> 首先进行一次采样获得所有线程的cpu的使用时间(调用的是`java.lang.management.ThreadMXBean#getThreadCpuTime`这个接口)然后睡眠一段时间默认100ms可以通过`-i`参数指定然后再采样一次最后得出这段时间内各个线程消耗的cpu时间情况最后算出百分比。
这里的cpu使用率与linux 命令`top -H -p <pid>` 的线程`%CPU`类似一段采样间隔时间内当前JVM里各个线程的增量cpu时间与采样间隔时间的比例。
#### 工作原理说明:
* 首先第一次采样获取所有线程的CPU时间(调用的是`java.lang.management.ThreadMXBean#getThreadCpuTime()`及`sun.management.HotspotThreadMBean.getInternalThreadCpuTimes()`接口)
* 然后睡眠等待一个间隔时间默认为200ms可以通过`-i`指定间隔时间)
* 再次第二次采样获取所有线程的CPU时间对比两次采样数据计算出每个线程的增量CPU时间
* 线程CPU使用率 = 线程增量CPU时间 / 采样间隔时间 * 100%
> 注意: 这个统计也会产生一定的开销JDK这个接口本身开销比较大因此会看到as的线程占用一定的百分比为了降低统计自身的开销带来的影响可以把采样间隔拉长一些比如5000毫秒。
> 如果想看从Java进程启动开始到现在的cpu占比情况可以使用[show-busy-java-threads](https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#-show-busy-java-threads)这个脚本
> 另外一种查看Java进程的线程cpu使用率方法:可以使用[show-busy-java-threads](https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#-show-busy-java-threads)这个脚本
### 使用参考
@ -30,60 +37,70 @@ thread
```shell
$ thread -n 3
"as-command-execute-daemon" Id=29 cpuUsage=75% RUNNABLE
at sun.management.ThreadImpl.dumpThreads0(Native Method)
at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:440)
at com.taobao.arthas.core.command.monitor200.ThreadCommand$1.action(ThreadCommand.java:58)
at com.taobao.arthas.core.command.handler.AbstractCommandHandler.execute(AbstractCommandHandler.java:238)
at com.taobao.arthas.core.command.handler.DefaultCommandHandler.handleCommand(DefaultCommandHandler.java:67)
at com.taobao.arthas.core.server.ArthasServer$4.run(ArthasServer.java:276)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Number of locked synchronizers = 1
- java.util.concurrent.ThreadPoolExecutor$Worker@6cd0b6f8
"as-session-expire-daemon" Id=25 cpuUsage=24% TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.taobao.arthas.core.server.DefaultSessionManager$2.run(DefaultSessionManager.java:85)
"C1 CompilerThread0" [Internal] cpuUsage=1.63% deltaTime=3ms time=1170ms
"arthas-command-execute" Id=23 cpuUsage=0.11% deltaTime=0ms time=401ms RUNNABLE
at java.management@11.0.7/sun.management.ThreadImpl.dumpThreads0(Native Method)
at java.management@11.0.7/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:466)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:199)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
at java.base@11.0.7/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base@11.0.7/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base@11.0.7/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base@11.0.7/java.lang.Thread.run(Thread.java:834)
"VM Periodic Task Thread" [Internal] cpuUsage=0.07% deltaTime=0ms time=584ms
```
* 没有线程ID包含`[Internal]`表示为JVM内部线程参考[dashboard](dashboard.md)命令的介绍。
* `cpuUsage`为采样间隔时间内线程的CPU使用率与[dashboard](dashboard.md)命令的数据一致。
* `deltaTime`为采样间隔时间内线程的增量CPU时间小于1ms时被取整显示为0ms。
* `time` 线程运行总CPU时间。
注意:线程栈为第二采样结束时获取,不能表明采样间隔时间内该线程都是在处理相同的任务。建议间隔时间不要太长,可能间隔时间越大越不准确。
可以根据具体情况尝试指定不同的间隔时间,观察输出结果。
"Reference Handler" Id=2 cpuUsage=0% WAITING on java.lang.ref.Reference$Lock@69ba0f27
at java.lang.Object.wait(Native Method)
- waiting on java.lang.ref.Reference$Lock@69ba0f27
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
```
#### 当没有参数时,显示第一页线程的信息
#### 当没有参数时,显示所有线程的信息
默认按照CPU增量时间降序排列只显示第一页数据。
```shell
$ thread
Threads Total: 16, NEW: 0, RUNNABLE: 7, BLOCKED: 0, WAITING: 5, TIMED_WAITING: 4, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTE DAEMON
30 as-command-execute-daemon system 9 RUNNABLE 72 0:0 false true
23 as-session-expire-daemon system 9 TIMED_WAIT 27 0:0 false true
22 Attach Listener system 9 RUNNABLE 0 0:0 false true
11 pool-2-thread-1 main 5 TIMED_WAIT 0 0:0 false false
12 Thread-2 main 5 RUNNABLE 0 0:0 false true
13 pool-3-thread-1 main 5 TIMED_WAIT 0 0:0 false false
25 as-selector-daemon system 9 RUNNABLE 0 0:0 false true
14 Thread-3 main 5 TIMED_WAIT 0 0:0 false false
26 pool-5-thread-1 system 5 WAITING 0 0:0 false false
15 Thread-4 main 5 RUNNABLE 0 0:0 false false
1 main main 5 WAITING 0 0:2 false false
2 Reference Handler system 10 WAITING 0 0:0 false true
3 Finalizer system 8 WAITING 0 0:0 false true
4 Signal Dispatcher system 9 RUNNABLE 0 0:0 false true
20 NonBlockingInputStreamThread main 5 WAITING 0 0:0 false true
21 Thread-8 main 5 RUNNABLE 0 0:0 false true
Threads Total: 33, NEW: 0, RUNNABLE: 9, BLOCKED: 0, WAITING: 3, TIMED_WAITING: 4, TERMINATED: 0, Internal threads: 17
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPT DAEMON
-1 C2 CompilerThread0 - -1 - 5.06 0.010 0:0.973 false true
-1 C1 CompilerThread0 - -1 - 0.95 0.001 0:0.603 false true
23 arthas-command-execute system 5 RUNNABLE 0.17 0.000 0:0.226 false true
-1 VM Periodic Task Thread - -1 - 0.05 0.000 0:0.094 false true
-1 Sweeper thread - -1 - 0.04 0.000 0:0.011 false true
-1 G1 Young RemSet Sampling - -1 - 0.02 0.000 0:0.025 false true
12 Attach Listener system 9 RUNNABLE 0.0 0.000 0:0.022 false true
11 Common-Cleaner InnocuousThrea 8 TIMED_WAI 0.0 0.000 0:0.000 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.000 false true
2 Reference Handler system 10 RUNNABLE 0.0 0.000 0:0.000 false true
4 Signal Dispatcher system 9 RUNNABLE 0.0 0.000 0:0.000 false true
15 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.029 false true
22 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.196 false true
24 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.038 false true
16 arthas-NettyWebsocketTtyBootst system 5 RUNNABLE 0.0 0.000 0:0.001 false true
17 arthas-NettyWebsocketTtyBootst system 5 RUNNABLE 0.0 0.000 0:0.001 false true
```
#### thread id 显示指定线程的运行堆栈
#### thread --all, 显示所有匹配的线程
显示所有匹配线程信息有时需要获取全部JVM的线程数据进行分析。
#### thread id, 显示指定线程的运行堆栈
```shell
$ thread 1
@ -145,6 +162,10 @@ $ thread -b
#### thread -i, 指定采样时间间隔
* `thread -i 1000` : 统计最近1000ms内的线程CPU时间。
* `thread -n 3 -i 1000` : 列出1000ms内最忙的3个线程栈
```bash
$ thread -n 3 -i 1000
"as-command-execute-daemon" Id=4759 cpuUsage=23% RUNNABLE
@ -170,11 +191,9 @@ $ thread -n 3 -i 1000
```bash
[arthas@28114]$ thread --state WAITING
Threads Total: 15, NEW: 0, RUNNABLE: 7, BLOCKED: 0, WAITING: 5, TIMED_WAITING: 3, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRU DAEMON
198 AsyncAppender-Worker-arth system 9 WAITING 0 0:0 false true
3 Finalizer system 8 WAITING 0 0:0 false true
14 RMI Scheduler(0) system 9 WAITING 0 0:0 false true
2 Reference Handler system 10 WAITING 0 0:0 false true
204 pool-8-thread-1 system 5 WAITING 0 0:0 false false
Threads Total: 16, NEW: 0, RUNNABLE: 9, BLOCKED: 0, WAITING: 3, TIMED_WAITING: 4, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTE DAEMON
3 Finalizer system 8 WAITING 0.0 0.000 0:0.000 false true
20 arthas-UserStat system 9 WAITING 0.0 0.000 0:0.001 false true
14 arthas-timer system 9 WAITING 0.0 0.000 0:0.000 false true
```

@ -34,9 +34,9 @@ java -jar arthas-tunnel-server.jar
默认情况下arthas tunnel server的web端口是`8080`arthas agent连接的端口是`7777`。
启动之后,可以访问 [http://localhost:8080/](http://localhost:8080/) ,再通过`agentId`连接到已注册的arthas agent上。
启动之后,可以访问 [http://127.0.0.1:8080/](http://127.0.0.1:8080/) ,再通过`agentId`连接到已注册的arthas agent上。
通过Spring Boot的Endpoint可以查看到具体的连接信息 [http://localhost:8080/actuator/arthas](http://localhost:8080/actuator/arthas) ,登陆用户名是`arthas`密码在arthas tunnel server的日志里可以找到比如
通过Spring Boot的Endpoint可以查看到具体的连接信息 [http://127.0.0.1:8080/actuator/arthas](http://127.0.0.1:8080/actuator/arthas) ,登陆用户名是`arthas`密码在arthas tunnel server的日志里可以找到比如
```
32851 [main] INFO o.s.b.a.s.s.UserDetailsServiceAutoConfiguration
@ -48,6 +48,12 @@ Using generated security password: f1dca050-3777-48f4-a577-6367e55a78a2
在启动arthas可以传递`--tunnel-server`参数,比如:
```bash
as.sh --tunnel-server 'ws://127.0.0.1:7777/ws'
```
也可以使用下面的测试地址(不保证一直可用):
```bash
as.sh --tunnel-server 'ws://47.75.156.201:7777/ws'
```

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-spy</artifactId>

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-testcase</artifactId>

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-tunnel-client</artifactId>

@ -4,7 +4,7 @@
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-tunnel-server</artifactId>

@ -4,41 +4,67 @@
当运行在Ali-tomcat时会显示当前tomcat的实时信息如HTTP请求的qps, rt, 错误数, 线程池信息等等。
```bash
```
$ dashboard
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTED DAEMON
889 RMI TCP Connection(15)-30.10.166. RMI Runtime 9 RUNNABLE 48 0:5 false true
1077 Timer-for-arthas-dashboard-0 system 9 RUNNABLE 24 0:0 false true
1074 as-selector-daemon system 9 RUNNABLE 12 0:0 false true
284 JMX server connection timeout 284 RMI Runtime 9 TIMED_WAITI 8 0:3 false true
16 Timer-1 main 5 TIMED_WAITI 5 0:9 false true
47 Pandora pandora-qos-reporter Pool main 5 TIMED_WAITI 0 0:0 false true
48 JmonitorClient-CheckThread Pool [ main 5 TIMED_WAITI 0 0:0 false true
49 JmonitorClient-HeartBeat Pool [Th main 5 TIMED_WAITI 0 0:0 false true
50 JmonitorClient-ReaderThread Pool main 5 TIMED_WAITI 0 0:0 false true
957 RMI TCP Connection(16)-30.10.166. RMI Runtime 9 RUNNABLE 0 0:2 false true
51 JmonitorClient-WriterThread Pool main 5 TIMED_WAITI 0 0:0 false true
52 ContainerBackgroundProcessor[Stan main 5 TIMED_WAITI 0 0:0 false true
53 http-bio-8080-Acceptor-0 main 5 RUNNABLE 0 0:2 false true
54 http-bio-8080-AsyncTimeout main 5 TIMED_WAITI 0 0:0 false true
11 GC Daemon system 2 TIMED_WAITI 0 0:0 false true
Memory used total max usage GC
heap 59M 223M 1820M 3.26% gc.ps_scavenge.count 118
ps_eden_space 14M 114M 668M 2.11% gc.ps_scavenge.time(ms) 1890
ps_survivor_space 6M 6M 6M 96.08% gc.ps_marksweep.count 5
ps_old_gen 39M 103M 1365M 2.86% gc.ps_marksweep.time(ms) 1140
nonheap 234M 240M 0M 97.46%
code_cache 46M 47M 240M 19.49%
metaspace 167M 172M 0M 97.36%
Runtime Tomcat
os.name Mac OS X connector http-bio-8080
os.version 10.10.5 QPS 0.00
java.version 1.8.0_60 RT(ms) 1.13
java.home error/s 0.00
received/s 0B
systemload.average 3.44 sent/s 0B
processors 4 threadpool http-bio-8080
uptime 16020s busy 0
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTE DAEMON
-1 C2 CompilerThread0 - -1 - 1.55 0.077 0:8.684 false true
53 Timer-for-arthas-dashboard-07b system 5 RUNNABLE 0.08 0.004 0:0.004 false true
22 scheduling-1 main 5 TIMED_WAI 0.06 0.003 0:0.287 false false
-1 C1 CompilerThread0 - -1 - 0.06 0.003 0:2.171 false true
-1 VM Periodic Task Thread - -1 - 0.03 0.001 0:0.092 false true
49 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.02 0.001 0:0.156 false true
16 Catalina-utility-1 main 1 TIMED_WAI 0.0 0.000 0:0.029 false false
-1 G1 Young RemSet Sampling - -1 - 0.0 0.000 0:0.019 false true
17 Catalina-utility-2 main 1 WAITING 0.0 0.000 0:0.025 false false
34 http-nio-8080-ClientPoller main 5 RUNNABLE 0.0 0.000 0:0.016 false true
23 http-nio-8080-BlockPoller main 5 RUNNABLE 0.0 0.000 0:0.011 false true
-1 VM Thread - -1 - 0.0 0.000 0:0.032 false true
-1 Service Thread - -1 - 0.0 0.000 0:0.006 false true
-1 GC Thread#5 - -1 - 0.0 0.000 0:0.043 false true
Memory used total max usage GC
heap 36M 70M 4096M 0.90% gc.g1_young_generation.count 12
g1_eden_space 6M 18M -1 33.33% 86
g1_old_gen 30M 50M 4096M 0.74% gc.g1_old_generation.count 0
g1_survivor_space 491K 2048K -1 24.01% gc.g1_old_generation.time(ms) 0
nonheap 66M 69M -1 96.56%
codeheap_'non-nmethods' 1M 2M 5M 22.39%
metaspace 46M 47M -1 98.01%
Runtime
os.name Mac OS X
os.version 10.15.4
java.version 15
java.home /Library/Java/JavaVirtualMachines/jdk-15.jdk/Contents/Home
systemload.average 10.68
processors 8
uptime 272s
```
输入 `Q`{{execute T2}} 或者 `Ctrl+C` 可以退出dashboard命令。
### 数据说明
* ID: Java级别的线程ID注意这个ID不能跟jstack中的nativeID一一对应。
* NAME: 线程名
* GROUP: 线程组名
* PRIORITY: 线程优先级, 1~10之间的数字越大表示优先级越高
* STATE: 线程的状态
* CPU%: 线程的cpu使用率。比如采样间隔1000ms某个线程的增量cpu时间为100ms则cpu使用率=100/1000=10%
* DELTA_TIME: 上次采样之后线程运行增量CPU时间数据格式为`秒`
* TIME: 线程运行总CPU时间数据格式为`分:秒`
* INTERRUPTED: 线程当前的中断位状态
* DAEMON: 是否是daemon线程
#### JVM内部线程
Java 8之后支持获取JVM内部线程CPU时间这些线程只有名称和CPU时间没有ID及状态等信息显示ID为-1
通过内部线程可以观测到JVM活动如GC、JIT编译等占用CPU情况方便了解JVM整体运行状况。
* 当JVM 堆(heap)/元数据(metaspace)空间不足或OOM时可以看到GC线程的CPU占用率明显高于其他的线程。
* 当执行`trace/watch/tt/redefine`等命令后可以看到JIT线程活动变得更频繁。因为JVM热更新class字节码时清除了此class相关的JIT编译结果需要重新编译。
JVM内部线程包括下面几种
* JIT编译线程: 如 `C1 CompilerThread0`, `C2 CompilerThread0`
* GC线程: 如`GC Thread0`, `G1 Young RemSet Sampling`
* 其它内部线程: 如`VM Periodic Task Thread`, `VM Thread`, `Service Thread`

@ -4,41 +4,71 @@ The `dashboard`{{execute T2}} command allows you to view the real-time data pane
When running in Apache Tomcat Alibaba edition, the dashboard will also present the real time statistics of the tomcat, including [QPS](https://en.wikipedia.org/wiki/Queries_per_second), RT, error counts, and thread pool, etc.
```bash
```
$ dashboard
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTED DAEMON
889 RMI TCP Connection(15)-30.10.166. RMI Runtime 9 RUNNABLE 48 0:5 false true
1077 Timer-for-arthas-dashboard-0 system 9 RUNNABLE 24 0:0 false true
1074 as-selector-daemon system 9 RUNNABLE 12 0:0 false true
284 JMX server connection timeout 284 RMI Runtime 9 TIMED_WAITI 8 0:3 false true
16 Timer-1 main 5 TIMED_WAITI 5 0:9 false true
47 Pandora pandora-qos-reporter Pool main 5 TIMED_WAITI 0 0:0 false true
48 JmonitorClient-CheckThread Pool [ main 5 TIMED_WAITI 0 0:0 false true
49 JmonitorClient-HeartBeat Pool [Th main 5 TIMED_WAITI 0 0:0 false true
50 JmonitorClient-ReaderThread Pool main 5 TIMED_WAITI 0 0:0 false true
957 RMI TCP Connection(16)-30.10.166. RMI Runtime 9 RUNNABLE 0 0:2 false true
51 JmonitorClient-WriterThread Pool main 5 TIMED_WAITI 0 0:0 false true
52 ContainerBackgroundProcessor[Stan main 5 TIMED_WAITI 0 0:0 false true
53 http-bio-8080-Acceptor-0 main 5 RUNNABLE 0 0:2 false true
54 http-bio-8080-AsyncTimeout main 5 TIMED_WAITI 0 0:0 false true
11 GC Daemon system 2 TIMED_WAITI 0 0:0 false true
Memory used total max usage GC
heap 59M 223M 1820M 3.26% gc.ps_scavenge.count 118
ps_eden_space 14M 114M 668M 2.11% gc.ps_scavenge.time(ms) 1890
ps_survivor_space 6M 6M 6M 96.08% gc.ps_marksweep.count 5
ps_old_gen 39M 103M 1365M 2.86% gc.ps_marksweep.time(ms) 1140
nonheap 234M 240M 0M 97.46%
code_cache 46M 47M 240M 19.49%
metaspace 167M 172M 0M 97.36%
Runtime Tomcat
os.name Mac OS X connector http-bio-8080
os.version 10.10.5 QPS 0.00
java.version 1.8.0_60 RT(ms) 1.13
java.home error/s 0.00
received/s 0B
systemload.average 3.44 sent/s 0B
processors 4 threadpool http-bio-8080
uptime 16020s busy 0
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTE DAEMON
-1 C2 CompilerThread0 - -1 - 1.55 0.077 0:8.684 false true
53 Timer-for-arthas-dashboard-07b system 5 RUNNABLE 0.08 0.004 0:0.004 false true
22 scheduling-1 main 5 TIMED_WAI 0.06 0.003 0:0.287 false false
-1 C1 CompilerThread0 - -1 - 0.06 0.003 0:2.171 false true
-1 VM Periodic Task Thread - -1 - 0.03 0.001 0:0.092 false true
49 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.02 0.001 0:0.156 false true
16 Catalina-utility-1 main 1 TIMED_WAI 0.0 0.000 0:0.029 false false
-1 G1 Young RemSet Sampling - -1 - 0.0 0.000 0:0.019 false true
17 Catalina-utility-2 main 1 WAITING 0.0 0.000 0:0.025 false false
34 http-nio-8080-ClientPoller main 5 RUNNABLE 0.0 0.000 0:0.016 false true
23 http-nio-8080-BlockPoller main 5 RUNNABLE 0.0 0.000 0:0.011 false true
-1 VM Thread - -1 - 0.0 0.000 0:0.032 false true
-1 Service Thread - -1 - 0.0 0.000 0:0.006 false true
-1 GC Thread#5 - -1 - 0.0 0.000 0:0.043 false true
Memory used total max usage GC
heap 36M 70M 4096M 0.90% gc.g1_young_generation.count 12
g1_eden_space 6M 18M -1 33.33% 86
g1_old_gen 30M 50M 4096M 0.74% gc.g1_old_generation.count 0
g1_survivor_space 491K 2048K -1 24.01% gc.g1_old_generation.time(ms) 0
nonheap 66M 69M -1 96.56%
codeheap_'non-nmethods' 1M 2M 5M 22.39%
metaspace 46M 47M -1 98.01%
Runtime
os.name Mac OS X
os.version 10.15.4
java.version 15
java.home /Library/Java/JavaVirtualMachines/jdk-15.jdk/Contents/Home
systemload.average 10.68
processors 8
uptime 272s
```
Enter `Q`{{execute T2}} or `Ctrl+C` to exit the dashboard command.
### Notes on column headers
* ID: JVM thread ID, pls. note this ID is different from the nativeID in jstack
* NAME: thread name
* GROUP: thread group name
* PRIORITY: thread priority, ranged from 1 to 10. The greater number, the higher priority
* STATE: thread state
* CPU%: the ratio of CPU usage for the thread. For example, the sampling interval is 1000ms, and the incremental cpu time
of a thread is 100ms, then the cpu usage rate=100/1000=10%
* DELTA_TIME: incremental CPU time of thread running after the last sampling in `second` format
* TIME: total CPU time of the thread in `minute:second` format
* INTERRUPTED: the thread interruption state
* DAEMON: daemon thread or not
#### JVM internal threads
After Java 8, it is supported to obtain the CPU time of JVM internal threads. These threads only have the name and CPU time,
without ID and status information (display ID is -1).
JVM activities can be observed through internal threads, such as GC, JIT compilation, etc., to perceive the overall status of JVM.
* When the JVM heap/metaspace space is insufficient or OOM, it can be seen that the CPU usage of the GC threads is
significantly higher than other threads.
* After executing commands such as `trace/watch/tt/redefine`, you can see that JIT threads activities become more frequent.
Because the JIT compilation data related to this class is cleared when the JVM hot update the class bytecode, it needs to be recompiled.
JVM internal threads include the following:
* JIT compilation thread: such as `C1 CompilerThread0`, `C2 CompilerThread0`
* GC thread: such as `GC Thread0`, `G1 Young RemSet Sampling`
* Other internal threads: such as`VM Periodic Task Thread`, `VM Thread`, `Service Thread`

@ -1,6 +1,15 @@
这里的cpu统计的是一段采样间隔内当前JVM里各个线程所占用的cpu时间占总cpu时间的百分比。其计算方法为 首先进行一次采样获得所有线程的cpu的使用时间(调用的是`java.lang.management.ThreadMXBean#getThreadCpuTime`这个接口)然后睡眠一段时间默认100ms可以通过`-i`参数指定然后再采样一次最后得出这段时间内各个线程消耗的cpu时间情况最后算出百分比。
### cpu使用率是如何统计出来的
注意: 这个统计也会产生一定的开销JDK这个接口本身开销比较大因此会看到as的线程占用一定的百分比为了降低统计自身的开销带来的影响可以把采样间隔拉长一些比如5000毫秒
这里的cpu使用率与linux 命令`top -H -p <pid>` 的线程`%CPU`类似一段采样间隔时间内当前JVM里各个线程的增量cpu时间与采样间隔时间的比例
如果想看从Java进程启动开始到现在的cpu占比情况可以使用[`show-busy-java-threads`](https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#-show-busy-java-threads)这个脚本
> 工作原理说明:
* 首先第一次采样获取所有线程的CPU时间(调用的是`java.lang.management.ThreadMXBean#getThreadCpuTime()`及`sun.management.HotspotThreadMBean.getInternalThreadCpuTimes()`接口)
* 然后睡眠等待一个间隔时间默认为200ms可以通过`-i`指定间隔时间)
* 再次第二次采样获取所有线程的CPU时间对比两次采样数据计算出每个线程的增量CPU时间
* 线程CPU使用率 = 线程增量CPU时间 / 采样间隔时间 * 100%
> 注意: 这个统计也会产生一定的开销JDK这个接口本身开销比较大因此会看到as的线程占用一定的百分比为了降低统计自身的开销带来的影响可以把采样间隔拉长一些比如5000毫秒。
> 另外一种查看Java进程的线程cpu使用率方法可以使用[show-busy-java-threads](https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#-show-busy-java-threads)这个脚本

@ -5,57 +5,72 @@
```bash
$ thread -n 3
"as-command-execute-daemon" Id=29 cpuUsage=75% RUNNABLE
at sun.management.ThreadImpl.dumpThreads0(Native Method)
at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:440)
at com.taobao.arthas.core.command.monitor200.ThreadCommand$1.action(ThreadCommand.java:58)
at com.taobao.arthas.core.command.handler.AbstractCommandHandler.execute(AbstractCommandHandler.java:238)
at com.taobao.arthas.core.command.handler.DefaultCommandHandler.handleCommand(DefaultCommandHandler.java:67)
at com.taobao.arthas.core.server.ArthasServer$4.run(ArthasServer.java:276)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Number of locked synchronizers = 1
- java.util.concurrent.ThreadPoolExecutor$Worker@6cd0b6f8
"as-session-expire-daemon" Id=25 cpuUsage=24% TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.taobao.arthas.core.server.DefaultSessionManager$2.run(DefaultSessionManager.java:85)
"Reference Handler" Id=2 cpuUsage=0% WAITING on java.lang.ref.Reference$Lock@69ba0f27
at java.lang.Object.wait(Native Method)
- waiting on java.lang.ref.Reference$Lock@69ba0f27
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
"C1 CompilerThread0" [Internal] cpuUsage=1.63% deltaTime=3ms time=1170ms
"arthas-command-execute" Id=23 cpuUsage=0.11% deltaTime=0ms time=401ms RUNNABLE
at java.management@11.0.7/sun.management.ThreadImpl.dumpThreads0(Native Method)
at java.management@11.0.7/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:466)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:199)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
at java.base@11.0.7/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base@11.0.7/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base@11.0.7/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base@11.0.7/java.lang.Thread.run(Thread.java:834)
"VM Periodic Task Thread" [Internal] cpuUsage=0.07% deltaTime=0ms time=584ms
```
### 当没有参数时,显示所有线程的信息。
* 没有线程ID包含`[Internal]`表示为JVM内部线程参考`dashboard`命令的介绍。
* `cpuUsage`为采样间隔时间内线程的CPU使用率与`dashboard`命令的数据一致。
* `deltaTime`为采样间隔时间内线程的增量CPU时间小于1ms时被取整显示为0ms。
* `time` 线程运行总CPU时间。
注意:线程栈为第二采样结束时获取,不能表明采样间隔时间内该线程都是在处理相同的任务。建议间隔时间不要太长,可能间隔时间越大越不准确。
可以根据具体情况尝试指定不同的间隔时间,观察输出结果。
### 当没有参数时,显示第一页线程信息
默认按照CPU增量时间降序排列只显示第一页数据避免滚屏。
`thread`{{execute T2}}
```bash
$ thread
Threads Total: 16, NEW: 0, RUNNABLE: 7, BLOCKED: 0, WAITING: 5, TIMED_WAITING: 4, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTE DAEMON
30 as-command-execute-daemon system 9 RUNNABLE 72 0:0 false true
23 as-session-expire-daemon system 9 TIMED_WAIT 27 0:0 false true
22 Attach Listener system 9 RUNNABLE 0 0:0 false true
11 pool-2-thread-1 main 5 TIMED_WAIT 0 0:0 false false
12 Thread-2 main 5 RUNNABLE 0 0:0 false true
13 pool-3-thread-1 main 5 TIMED_WAIT 0 0:0 false false
25 as-selector-daemon system 9 RUNNABLE 0 0:0 false true
14 Thread-3 main 5 TIMED_WAIT 0 0:0 false false
26 pool-5-thread-1 system 5 WAITING 0 0:0 false false
15 Thread-4 main 5 RUNNABLE 0 0:0 false false
1 main main 5 WAITING 0 0:2 false false
2 Reference Handler system 10 WAITING 0 0:0 false true
3 Finalizer system 8 WAITING 0 0:0 false true
4 Signal Dispatcher system 9 RUNNABLE 0 0:0 false true
20 NonBlockingInputStreamThread main 5 WAITING 0 0:0 false true
21 Thread-8 main 5 RUNNABLE 0 0:0 false true
Threads Total: 33, NEW: 0, RUNNABLE: 9, BLOCKED: 0, WAITING: 3, TIMED_WAITING: 4, TERMINATED: 0, Internal threads: 17
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPT DAEMON
-1 C2 CompilerThread0 - -1 - 5.06 0.010 0:0.973 false true
-1 C1 CompilerThread0 - -1 - 0.95 0.001 0:0.603 false true
23 arthas-command-execute system 5 RUNNABLE 0.17 0.000 0:0.226 false true
-1 VM Periodic Task Thread - -1 - 0.05 0.000 0:0.094 false true
-1 Sweeper thread - -1 - 0.04 0.000 0:0.011 false true
-1 G1 Young RemSet Sampling - -1 - 0.02 0.000 0:0.025 false true
12 Attach Listener system 9 RUNNABLE 0.0 0.000 0:0.022 false true
11 Common-Cleaner InnocuousThrea 8 TIMED_WAI 0.0 0.000 0:0.000 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.000 false true
2 Reference Handler system 10 RUNNABLE 0.0 0.000 0:0.000 false true
4 Signal Dispatcher system 9 RUNNABLE 0.0 0.000 0:0.000 false true
15 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.029 false true
22 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.196 false true
24 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.038 false true
16 arthas-NettyWebsocketTtyBootst system 5 RUNNABLE 0.0 0.000 0:0.001 false true
17 arthas-NettyWebsocketTtyBootst system 5 RUNNABLE 0.0 0.000 0:0.001 false true
```
### thread --all, 显示所有匹配的线程
显示所有匹配线程信息有时需要获取全部JVM的线程数据进行分析。
`thread --all`{{execute T2}}
### thread id 显示指定线程的运行堆栈
查看线程ID 16的栈
@ -122,8 +137,13 @@ $ thread -b
**注意** 目前只支持找出synchronized关键字阻塞住的线程 如果是`java.util.concurrent.Lock` 目前还不支持。
### thread -i, 指定采样时间间隔
* `thread -i 1000` : 统计最近1000ms内的线程CPU时间。
`thread -i 1000`{{execute T2}}
`thread -n 3 -i 5000`{{execute T2}}
* `thread -n 3 -i 1000` : 列出1000ms内最忙的3个线程栈
`thread -n 3 -i 1000`{{execute T2}}
```bash
$ thread -n 3 -i 1000
@ -152,11 +172,9 @@ $ thread -n 3 -i 1000
```bash
[arthas@28114]$ thread --state WAITING
Threads Total: 15, NEW: 0, RUNNABLE: 7, BLOCKED: 0, WAITING: 5, TIMED_WAITING: 3, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRU DAEMON
198 AsyncAppender-Worker-arth system 9 WAITING 0 0:0 false true
3 Finalizer system 8 WAITING 0 0:0 false true
14 RMI Scheduler(0) system 9 WAITING 0 0:0 false true
2 Reference Handler system 10 WAITING 0 0:0 false true
204 pool-8-thread-1 system 5 WAITING 0 0:0 false false
Threads Total: 16, NEW: 0, RUNNABLE: 9, BLOCKED: 0, WAITING: 3, TIMED_WAITING: 4, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTE DAEMON
3 Finalizer system 8 WAITING 0.0 0.000 0:0.000 false true
20 arthas-UserStat system 9 WAITING 0.0 0.000 0:0.001 false true
14 arthas-timer system 9 WAITING 0.0 0.000 0:0.000 false true
```

@ -9,3 +9,4 @@
| [n:] | 指定最忙的前N个线程并打印堆栈 |
| [b] | 找出当前阻塞其他线程的线程 |
| [i <value>] | 指定cpu占比统计的采样间隔单位为毫秒 |
|[--all]|显示所有匹配的线程|

@ -1,6 +1,15 @@
### How the CPU ratios are calculated?
CPU ratio for a given thread is the CPU time it takes divided by the total CPU time within a specified interval period. It is calculated in the following way: sample CPU times for all the thread by calling `java.lang.management.ThreadMXBean#getThreadCpuTime` first, then sleep for a period (the default value is 100ms, which can be specified by `-i`), then sample CPU times again. By this, we can get the time cost for this period for each thread, then come up with the ratio.
The cpu ratios here is similar to the thread `%CPU` of the linux command `top -H -p <pid>`. During a sampling interval,
the ratio of the incremental cpu time of each thread in the current JVM to the sampling interval time.
**Note**: this operation consumes CPU time too (getThreadCpuTime is time-consuming), therefore it is possible to observe Arthass thread appears in the list. To avoid this, try to increase sample interval, for example: 5000 ms.
> Working principle description:
* Do the first sampling, get the CPU time of all threads ( by calling `java.lang.management.ThreadMXBean#getThreadCpuTime()` and
`sun.management.HotspotThreadMBean.getInternalThreadCpuTimes()` )
* Sleep and wait for an interval (the default is 200ms, the interval can be specified by `-i`)
* Do the second sampling, get the CPU time of all threads, compare the two sampling data, and calculate the incremental CPU time of each thread
* `Thread CPU usage ratio` = `Thread increment CPU time` / `Sampling interval time` * 100%
If youd like to check the CPU ratios from the very beginning of the Java process, [`show-busy-java-threads`](https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#-show-busy-java-threads) can come to help.
**Note:** this operation consumes CPU time too (`getThreadCpuTime` is time-consuming), therefore it is possible to observe Arthas's thread appears in the list. To avoid this, try to increase sample interval, for example: 5000 ms.<br/>
> Another way to view the thread cpu usage of the Java process, [show-busy-java-threads](https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#-show-busy-java-threads) can come to help.

@ -5,55 +5,68 @@
```bash
$ thread -n 3
"as-command-execute-daemon" Id=29 cpuUsage=75% RUNNABLE
at sun.management.ThreadImpl.dumpThreads0(Native Method)
at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:440)
at com.taobao.arthas.core.command.monitor200.ThreadCommand$1.action(ThreadCommand.java:58)
at com.taobao.arthas.core.command.handler.AbstractCommandHandler.execute(AbstractCommandHandler.java:238)
at com.taobao.arthas.core.command.handler.DefaultCommandHandler.handleCommand(DefaultCommandHandler.java:67)
at com.taobao.arthas.core.server.ArthasServer$4.run(ArthasServer.java:276)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Number of locked synchronizers = 1
- java.util.concurrent.ThreadPoolExecutor$Worker@6cd0b6f8
"as-session-expire-daemon" Id=25 cpuUsage=24% TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.taobao.arthas.core.server.DefaultSessionManager$2.run(DefaultSessionManager.java:85)
"Reference Handler" Id=2 cpuUsage=0% WAITING on java.lang.ref.Reference$Lock@69ba0f27
at java.lang.Object.wait(Native Method)
- waiting on java.lang.ref.Reference$Lock@69ba0f27
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
"C1 CompilerThread0" [Internal] cpuUsage=1.63% deltaTime=3ms time=1170ms
"arthas-command-execute" Id=23 cpuUsage=0.11% deltaTime=0ms time=401ms RUNNABLE
at java.management@11.0.7/sun.management.ThreadImpl.dumpThreads0(Native Method)
at java.management@11.0.7/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:466)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:199)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
at java.base@11.0.7/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base@11.0.7/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base@11.0.7/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base@11.0.7/java.lang.Thread.run(Thread.java:834)
"VM Periodic Task Thread" [Internal] cpuUsage=0.07% deltaTime=0ms time=584ms
```
### List all threads info when no options provided
* Without thread ID, including `[Internal]` means JVM internal thread, refer to the introduction of `dashboard` command.
* `cpuUsage` is the CPU usage of the thread during the sampling interval, consistent with the data of the `dashboard` command.
* `deltaTime` is the incremental CPU time of the thread during the sampling interval. If it is less than 1ms, it will be rounded and displayed as 0ms.
* `time` The total CPU time of thread.
**Note:** The thread stack is acquired at the end of the second sampling, which does not indicate that the thread is
processing the same task during the sampling interval. It is recommended that the interval time should not be too long.
The larger the interval time, the more inaccurate.
You can try to specify different intervals according to the specific situation and observe the output results.
#### List first page threads' info when no options provided
By default, they are arranged in descending order of CPU increment time, and only the first page of data is displayed.
`thread`{{execute T2}}
```bash
$ thread
Threads Total: 16, NEW: 0, RUNNABLE: 7, BLOCKED: 0, WAITING: 5, TIMED_WAITING: 4, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTE DAEMON
30 as-command-execute-daemon system 9 RUNNABLE 72 0:0 false true
23 as-session-expire-daemon system 9 TIMED_WAIT 27 0:0 false true
22 Attach Listener system 9 RUNNABLE 0 0:0 false true
11 pool-2-thread-1 main 5 TIMED_WAIT 0 0:0 false false
12 Thread-2 main 5 RUNNABLE 0 0:0 false true
13 pool-3-thread-1 main 5 TIMED_WAIT 0 0:0 false false
25 as-selector-daemon system 9 RUNNABLE 0 0:0 false true
14 Thread-3 main 5 TIMED_WAIT 0 0:0 false false
26 pool-5-thread-1 system 5 WAITING 0 0:0 false false
15 Thread-4 main 5 RUNNABLE 0 0:0 false false
1 main main 5 WAITING 0 0:2 false false
2 Reference Handler system 10 WAITING 0 0:0 false true
3 Finalizer system 8 WAITING 0 0:0 false true
4 Signal Dispatcher system 9 RUNNABLE 0 0:0 false true
20 NonBlockingInputStreamThread main 5 WAITING 0 0:0 false true
21 Thread-8 main 5 RUNNABLE 0 0:0 false true
Threads Total: 33, NEW: 0, RUNNABLE: 9, BLOCKED: 0, WAITING: 3, TIMED_WAITING: 4, TERMINATED: 0, Internal threads: 17
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPT DAEMON
-1 C2 CompilerThread0 - -1 - 5.06 0.010 0:0.973 false true
-1 C1 CompilerThread0 - -1 - 0.95 0.001 0:0.603 false true
23 arthas-command-execute system 5 RUNNABLE 0.17 0.000 0:0.226 false true
-1 VM Periodic Task Thread - -1 - 0.05 0.000 0:0.094 false true
-1 Sweeper thread - -1 - 0.04 0.000 0:0.011 false true
-1 G1 Young RemSet Sampling - -1 - 0.02 0.000 0:0.025 false true
12 Attach Listener system 9 RUNNABLE 0.0 0.000 0:0.022 false true
11 Common-Cleaner InnocuousThrea 8 TIMED_WAI 0.0 0.000 0:0.000 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.000 false true
2 Reference Handler system 10 RUNNABLE 0.0 0.000 0:0.000 false true
4 Signal Dispatcher system 9 RUNNABLE 0.0 0.000 0:0.000 false true
15 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.029 false true
22 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.196 false true
24 arthas-NettyHttpTelnetBootstra system 5 RUNNABLE 0.0 0.000 0:0.038 false true
16 arthas-NettyWebsocketTtyBootst system 5 RUNNABLE 0.0 0.000 0:0.001 false true
17 arthas-NettyWebsocketTtyBootst system 5 RUNNABLE 0.0 0.000 0:0.001 false true
```
### thread id, show the running stack for the target thread
@ -123,7 +136,13 @@ $ thread -b
### thread -i, specify the sampling interval
`thread -n 3 -i 5000`{{execute T2}}
* `thread -i 1000`: Count the thread cpu time of the last 1000ms.
`thread -i 1000`{{execute T2}}
* `thread -n 3 -i 1000`: List the 3 busiest thread stacks in 1000ms.
`thread -n 3 -i 1000`{{execute T2}}
```bash
$ thread -n 3 -i 1000
@ -152,11 +171,9 @@ $ thread -n 3 -i 1000
```bash
[arthas@28114]$ thread --state WAITING
Threads Total: 15, NEW: 0, RUNNABLE: 7, BLOCKED: 0, WAITING: 5, TIMED_WAITING: 3, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRU DAEMON
198 AsyncAppender-Worker-arth system 9 WAITING 0 0:0 false true
3 Finalizer system 8 WAITING 0 0:0 false true
14 RMI Scheduler(0) system 9 WAITING 0 0:0 false true
2 Reference Handler system 10 WAITING 0 0:0 false true
204 pool-8-thread-1 system 5 WAITING 0 0:0 false false
```
Threads Total: 16, NEW: 0, RUNNABLE: 9, BLOCKED: 0, WAITING: 3, TIMED_WAITING: 4, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTE DAEMON
3 Finalizer system 8 WAITING 0.0 0.000 0:0.000 false true
20 arthas-UserStat system 9 WAITING 0.0 0.000 0:0.001 false true
14 arthas-timer system 9 WAITING 0.0 0.000 0:0.000 false true
```

@ -12,3 +12,4 @@ Check the basic info and stack trace of the target thread.
| [n:] | the top n busiest threads with stack traces printed |
| [b] | locate the thread blocking the others |
| [i <value>] | specify the interval to collect data to compute CPU ratios (ms) |
|[--all]|Show all matching threads|

Loading…
Cancel
Save