diff --git a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java index 0c70cf71..419f06d1 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java +++ b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java @@ -34,6 +34,7 @@ import com.taobao.arthas.core.command.monitor200.HeapDumpCommand; import com.taobao.arthas.core.command.monitor200.JvmCommand; import com.taobao.arthas.core.command.monitor200.MBeanCommand; import com.taobao.arthas.core.command.monitor200.MonitorCommand; +import com.taobao.arthas.core.command.monitor200.PerfCounterCommand; import com.taobao.arthas.core.command.monitor200.ProfilerCommand; import com.taobao.arthas.core.command.monitor200.StackCommand; import com.taobao.arthas.core.command.monitor200.ThreadCommand; @@ -78,6 +79,7 @@ public class BuiltinCommandPack implements CommandResolver { commands.add(Command.create(WatchCommand.class)); commands.add(Command.create(TimeTunnelCommand.class)); commands.add(Command.create(JvmCommand.class)); + commands.add(Command.create(PerfCounterCommand.class)); // commands.add(Command.create(GroovyScriptCommand.class)); commands.add(Command.create(OgnlCommand.class)); commands.add(Command.create(MemoryCompilerCommand.class)); diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/PerfCounterCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/PerfCounterCommand.java new file mode 100644 index 00000000..98491c17 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/PerfCounterCommand.java @@ -0,0 +1,120 @@ +package com.taobao.arthas.core.command.monitor200; + +import static com.taobao.text.ui.Element.label; + +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; + +import com.taobao.arthas.common.JavaVersionUtils; +import com.taobao.arthas.common.PidUtils; +import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.shell.command.AnnotatedCommand; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.LogUtil; +import com.taobao.middleware.cli.annotations.Description; +import com.taobao.middleware.cli.annotations.Name; +import com.taobao.middleware.cli.annotations.Option; +import com.taobao.middleware.cli.annotations.Summary; +import com.taobao.middleware.logger.Logger; +import com.taobao.text.Decoration; +import com.taobao.text.ui.TableElement; +import com.taobao.text.util.RenderUtil; + +import sun.management.counter.Counter; +import sun.management.counter.perf.PerfInstrumentation; + +/** + * @see sun.misc.Perf + * @see sun.management.counter.perf.PerfInstrumentation + * @author hengyunabc 2020-02-16 + */ +@Name("perfcounter") +@Summary("Display the perf counter infornation.") +@Description(Constants.WIKI + Constants.WIKI_HOME + "perf") +public class PerfCounterCommand extends AnnotatedCommand { + private static final Logger logger = LogUtil.getArthasLogger(); + private static Object perfObject; + private static Method attachMethod; + + private boolean details; + + @Option(shortName = "d", longName = "details", flag = true) + @Description("print all perf counter details") + public void setDetails(boolean details) { + this.details = details; + } + + @Override + public void process(CommandProcess process) { + try { + TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); + if (this.details) { + table = new TableElement(3, 1, 1, 10).leftCellPadding(1).rightCellPadding(1); + table.row(true, label("Name").style(Decoration.bold.bold()), + label("Variability").style(Decoration.bold.bold()), + label("Units").style(Decoration.bold.bold()), label("Value").style(Decoration.bold.bold())); + } + + List perfCounters = getPerfCounters(); + if (perfCounters.isEmpty()) { + process.write( + "please check arthas log. if java version >=9 , try to add jvm options when start your process: " + + "--add-opens java.base/jdk.internal.perf=ALL-UNNAMED " + + "--add-exports java.base/jdk.internal.perf=ALL-UNNAMED\n"); + } else { + for (Counter counter : perfCounters) { + if (details) { + table.row(counter.getName(), String.valueOf(counter.getVariability()), + String.valueOf(counter.getUnits()), String.valueOf(counter.getValue())); + } else { + table.row(counter.getName(), String.valueOf(counter.getValue())); + } + } + } + + process.write(RenderUtil.render(table, process.width())); + } finally { + process.end(); + } + } + + private static List getPerfCounters() { + + /** + *
+         * Perf p = Perf.getPerf();
+         * ByteBuffer buffer = p.attach(pid, "r");
+         * 
+ */ + try { + if (perfObject == null) { + // jdk8 + String perfClassName = "sun.misc.Perf"; + // jdk 11 + if (!JavaVersionUtils.isLessThanJava9()) { + perfClassName = "jdk.internal.perf.Perf"; + } + + Class perfClass = ClassLoader.getSystemClassLoader().loadClass(perfClassName); + Method getPerfMethod = perfClass.getDeclaredMethod("getPerf"); + perfObject = getPerfMethod.invoke(null); + } + + if (attachMethod == null) { + attachMethod = perfObject.getClass().getDeclaredMethod("attach", + new Class[] { int.class, String.class }); + } + + ByteBuffer buffer = (ByteBuffer) attachMethod.invoke(perfObject, + new Object[] { (int) PidUtils.currentLongPid(), "r" }); + + PerfInstrumentation perfInstrumentation = new PerfInstrumentation(buffer); + return perfInstrumentation.getAllCounters(); + } catch (Throwable e) { + logger.error("arthas", "get perf counter error", e); + } + return Collections.emptyList(); + } +} diff --git a/site/src/site/sphinx/advanced-use.md b/site/src/site/sphinx/advanced-use.md index 9f46705f..b4dac0ff 100644 --- a/site/src/site/sphinx/advanced-use.md +++ b/site/src/site/sphinx/advanced-use.md @@ -27,6 +27,7 @@ * [sysprop](sysprop.md)——查看和修改JVM的系统属性 * [sysenv](sysenv.md)——查看JVM的环境变量 * [vmoption](vmoption.md)——查看和修改JVM里诊断相关的option +* [perfcounter](perfcounter.md)——查看当前 JVM 的Perf Counter信息 * [logger](logger.md)——查看和修改logger * [getstatic](getstatic.md)——查看类的静态属性 * [ognl](ognl.md)——执行ognl表达式 diff --git a/site/src/site/sphinx/commands.md b/site/src/site/sphinx/commands.md index 6b400014..365de44a 100644 --- a/site/src/site/sphinx/commands.md +++ b/site/src/site/sphinx/commands.md @@ -7,6 +7,7 @@ * [sysprop](sysprop.md) * [sysenv](sysenv.md) * [vmoption](vmoption.md) +* [perfcounter](perfcounter.md) * [logger](logger.md) * [mbean](mbean.md) * [getstatic](getstatic.md) diff --git a/site/src/site/sphinx/en/advanced-use.md b/site/src/site/sphinx/en/advanced-use.md index 93ca3624..a5c1fbf4 100644 --- a/site/src/site/sphinx/en/advanced-use.md +++ b/site/src/site/sphinx/en/advanced-use.md @@ -25,6 +25,7 @@ Advanced Usage * [sysprop](sysprop.md) - view/modify system properties * [sysenv](sysenv.md) — view system environment variables * [vmoption](vmoption.md) - view/modify the vm diagnostic options. +* [perfcounter](perfcounter.md) - show JVM Perf Counter information * [logger](logger.md) - print the logger information, update the logger level * [getstatic](getstatic.md) - examine class's static properties * [ognl](ognl.md) - execute ongl expression diff --git a/site/src/site/sphinx/en/commands.md b/site/src/site/sphinx/en/commands.md index b2497573..f3c689d4 100644 --- a/site/src/site/sphinx/en/commands.md +++ b/site/src/site/sphinx/en/commands.md @@ -7,6 +7,7 @@ All Commands * [sysprop](sysprop.md) * [sysenv](sysenv.md) * [vmoption](vmoption.md) +* [perfcounter](perfcounter.md) * [logger](logger.md) * [mbean](mbean.md) * [getstatic](getstatic.md) diff --git a/site/src/site/sphinx/en/perfcounter.md b/site/src/site/sphinx/en/perfcounter.md new file mode 100644 index 00000000..b6b5faa0 --- /dev/null +++ b/site/src/site/sphinx/en/perfcounter.md @@ -0,0 +1,40 @@ +perfcounter +=== + +> Check the current JVM Perf Counter information. + +### Usage + +``` +$ perfcounter + java.ci.totalTime 2325637411 + java.cls.loadedClasses 3403 + java.cls.sharedLoadedClasses 0 + java.cls.sharedUnloadedClasses 0 + java.cls.unloadedClasses 0 + java.property.java.version 11.0.4 + java.property.java.vm.info mixed mode + java.property.java.vm.name OpenJDK 64-Bit Server VM +... +``` + +Print more information with the `-d` option: + +``` +$ perfcounter -d + Name Variability Units Value +--------------------------------------------------------------------------------- + java.ci.totalTime Monotonic Ticks 3242526906 + java.cls.loadedClasses Monotonic Events 3404 + java.cls.sharedLoadedClasses Monotonic Events 0 + java.cls.sharedUnloadedClasses Monotonic Events 0 + java.cls.unloadedClasses Monotonic Events 0 +``` + +### JVM above JDK9 + +If the information is not printed, when the application starts, add the following parameters: + +``` +--add-opens java.base/jdk.internal.perf=ALL-UNNAMED --add-exports java.base/jdk.internal.perf=ALL-UNNAMED +``` \ No newline at end of file diff --git a/site/src/site/sphinx/perfcounter.md b/site/src/site/sphinx/perfcounter.md new file mode 100644 index 00000000..08ea3357 --- /dev/null +++ b/site/src/site/sphinx/perfcounter.md @@ -0,0 +1,40 @@ +perfcounter +=== + +> 查看当前JVM的 Perf Counter信息 + +### 使用参考 + +``` +$ perfcounter + java.ci.totalTime 2325637411 + java.cls.loadedClasses 3403 + java.cls.sharedLoadedClasses 0 + java.cls.sharedUnloadedClasses 0 + java.cls.unloadedClasses 0 + java.property.java.version 11.0.4 + java.property.java.vm.info mixed mode + java.property.java.vm.name OpenJDK 64-Bit Server VM +... +``` + +可以用`-d`参数打印更多信息: + +``` +$ perfcounter -d + Name Variability Units Value +--------------------------------------------------------------------------------- + java.ci.totalTime Monotonic Ticks 3242526906 + java.cls.loadedClasses Monotonic Events 3404 + java.cls.sharedLoadedClasses Monotonic Events 0 + java.cls.sharedUnloadedClasses Monotonic Events 0 + java.cls.unloadedClasses Monotonic Events 0 +``` + +### jdk9以上的应用 + +如果没有打印出信息,应用在启动时,加下面的参数: + +``` +--add-opens java.base/jdk.internal.perf=ALL-UNNAMED --add-exports java.base/jdk.internal.perf=ALL-UNNAMED +``` \ No newline at end of file