From 910b578f25113d4437db99e65df13abc6abd6edd Mon Sep 17 00:00:00 2001 From: Winson Huang Date: Thu, 14 Sep 2023 15:19:10 +0800 Subject: [PATCH] profiler command support live/sched/cstack/begin/end/ttsp options (#2657) --- .../command/monitor200/ProfilerCommand.java | 103 +++++++++++++++--- site/docs/doc/profiler.md | 44 ++++++++ site/docs/en/doc/profiler.md | 43 ++++++++ 3 files changed, 175 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java index c11592190..c0eabfe92 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java @@ -81,6 +81,11 @@ public class ProfilerCommand extends AnnotatedCommand { */ private String alloc; + /** + * build allocation profile from live objects only + */ + private boolean live; + /** * profile contended locks longer than DURATION ns * according to async-profiler README, alloc may contains non-numeric charactors @@ -112,6 +117,17 @@ public class ProfilerCommand extends AnnotatedCommand { */ private boolean threads; + /** + * group threads by scheduling policy + */ + private boolean sched; + + /** + * how to collect C stack frames in addition to Java stack + * MODE is 'fp' (Frame Pointer), 'dwarf', 'lbr' (Last Branch Record) or 'no' + */ + private String cstack; + /** * use simple class names instead of FQN */ @@ -132,11 +148,6 @@ public class ProfilerCommand extends AnnotatedCommand { */ private boolean lib; - /** - * include only kernel-mode events - */ - private boolean allkernel; - /** * include only user-mode events */ @@ -157,6 +168,21 @@ public class ProfilerCommand extends AnnotatedCommand { */ private List excludes; + /** + * automatically start profiling when the specified native function is executed. + */ + private String begin; + + /** + * automatically stop profiling when the specified native function is executed. + */ + private String end; + + /** + * time-to-safepoint profiling. + * An alias for --begin SafepointSynchronize::begin --end RuntimeService::record_safepoint_synchronized + */ + private boolean ttsp; /** * FlameGraph title @@ -280,6 +306,12 @@ public class ProfilerCommand extends AnnotatedCommand { this.alloc = alloc; } + @Option(longName = "live", flag = true) + @Description("build allocation profile from live objects only") + public void setLive(boolean live) { + this.live = live; + } + @Option(longName = "lock") @Description("lock profiling threshold in nanoseconds") public void setLock(String lock) { @@ -292,6 +324,18 @@ public class ProfilerCommand extends AnnotatedCommand { this.threads = threads; } + @Option(longName = "sched", flag = true) + @Description("group threads by scheduling policy") + public void setSched(boolean sched) { + this.sched = sched; + } + + @Option(longName = "cstack") + @Description("how to traverse C stack: fp|dwarf|lbr|no") + public void setCstack(String cstack) { + this.cstack = cstack; + } + @Option(shortName = "s", flag = true) @Description("use simple class names instead of FQN") public void setSimple(boolean simple) { @@ -316,13 +360,7 @@ public class ProfilerCommand extends AnnotatedCommand { this.lib = lib; } - @Option(longName = "allkernel", flag = true) - @Description("include only kernel-mode events") - public void setAllkernel(boolean allkernel) { - this.allkernel = allkernel; - } - - @Option(longName = "alluser", flag = true) + @Option(longName = "all-user", flag = true) @Description("include only user-mode events") public void setAlluser(boolean alluser) { this.alluser = alluser; @@ -346,6 +384,25 @@ public class ProfilerCommand extends AnnotatedCommand { this.excludes = excludes; } + @Option(longName = "begin") + @Description("automatically start profiling when the specified native function is executed") + public void setBegin(String begin) { + this.begin = begin; + } + + @Option(longName = "end") + @Description("automatically stop profiling when the specified native function is executed") + public void setEnd(String end) { + this.end = end; + } + + @Option(longName = "ttsp", flag = true) + @Description("time-to-safepoint profiling. " + + "An alias for --begin SafepointSynchronize::begin --end RuntimeService::record_safepoint_synchronized") + public void setTtsp(boolean ttsp) { + this.ttsp = ttsp; + } + @Option(longName = "title") @Description("FlameGraph title") public void setTitle(String title) { @@ -460,6 +517,9 @@ public class ProfilerCommand extends AnnotatedCommand { if (this.alloc!= null) { sb.append("alloc=").append(this.alloc).append(COMMA); } + if (this.live) { + sb.append(this.live).append(COMMA); + } if (this.lock!= null) { sb.append("lock=").append(this.lock).append(COMMA); } @@ -478,6 +538,12 @@ public class ProfilerCommand extends AnnotatedCommand { if (this.threads) { sb.append("threads").append(COMMA); } + if (this.sched) { + sb.append("sched").append(COMMA); + } + if (this.cstack != null) { + sb.append("cstack=").append(this.cstack).append(COMMA); + } if (this.simple) { sb.append("simple").append(COMMA); } @@ -490,9 +556,6 @@ public class ProfilerCommand extends AnnotatedCommand { if (this.lib) { sb.append("lib").append(COMMA); } - if (this.allkernel) { - sb.append("allkernel").append(COMMA); - } if (this.alluser) { sb.append("alluser").append(COMMA); } @@ -506,6 +569,16 @@ public class ProfilerCommand extends AnnotatedCommand { sb.append("exclude=").append(exclude).append(COMMA); } } + if (this.ttsp) { + this.begin = "SafepointSynchronize::begin"; + this.end = "RuntimeService::record_safepoint_synchronized"; + } + if (this.begin != null) { + sb.append("begin=").append(this.begin).append(COMMA); + } + if (this.end != null) { + sb.append("end=").append(this.end).append(COMMA); + } if (this.title != null) { sb.append("title=").append(this.title).append(COMMA); diff --git a/site/docs/doc/profiler.md b/site/docs/doc/profiler.md index 4d3e79340..8013f10e8 100644 --- a/site/docs/doc/profiler.md +++ b/site/docs/doc/profiler.md @@ -281,3 +281,47 @@ profiler start -e alloc --alloc 2m ```bash profiler start -f profile.jfr --chunksize 100m --chunktime 1h ``` + +## 将线程按照调度策略分组 + +可以使用 `--sched` 标志选项将输出结果按照 Linux 线程调度策略分组,策略包括 BATCH/IDLE/OTHER。例如: + +```bash +profiler start --sched +``` + +火焰图的倒数第二行会标记不同的调度策略。 + +## 仅用未销毁对象构建内存分析结果 + +使用 `--live` 标志选项在内存分析结果中仅保留那些在分析过程结束时仍未被 JVM 回收的对象。该选项在排查 Java 堆内存泄露问题时比较有用。 + +```bash +profiler start --live +``` + +## 配置收集 C 栈帧的方法 + +使用 `--cstack MODE` 配置收集 native 帧的方法。候选模式有 fp (Frame Pointer), dwarf (DWARF unwind info), lbr (Last Branch Record, 从 Linux 4.1 在 Haswell 可用), and no (不收集 native 栈帧). + +默认情况下,C 栈帧会出现在 cpu、itimer、wall-clock、perf-events 模式中,而 Java 级别的 event 比如 alloc 和 lock 只收集 Java stack。 + +```bash +profiler --cstack fp +``` + +此命令将收集 native 栈帧的 Frame Pointer 信息。 + + +## 当指定 native 函数执行时开始/停止 profiling + +使用 `--begin function` 和 `--end function` 选项在指定 native 函数被执行时让 profiling 过程启动或终止。主要用途是分析特定的 JVM 阶段,比如 GC 和安全点。需要使用特定 JVM 实现中的 native 函数名,比如 HotSpot JVM 中的 `SafepointSynchronize::begin` 和 `SafepointSynchronize::end`。 + +### Time-to-safepoint profiling + +选项 `--ttsp` 实际上是 `--begin SafepointSynchronize::begin --end RuntimeService::record_safepoint_synchronized` 的一个别名。它是一种约束而不是独立的 event 类型。无论选择哪种 event,profiler 都可以正常工作,但只有 VM 操作和 safepoint request 之间的事件会被记录下来。 + +```bash +profiler start --begin SafepointSynchronize::begin --end RuntimeService::record_safepoint_synchronized +profiler --ttsp +``` \ No newline at end of file diff --git a/site/docs/en/doc/profiler.md b/site/docs/en/doc/profiler.md index 2df7d92a9..e98ce2d1f 100644 --- a/site/docs/en/doc/profiler.md +++ b/site/docs/en/doc/profiler.md @@ -281,3 +281,46 @@ When using JFR as output format, you can use `--chunksize` or `--chunktime` to c ```bash profiler start -f profile.jfr --chunksize 100m --chunktime 1h ``` + +## Group threads by scheduling policy + +You can use `--sched` flag option to group threads in output by Linux-specific scheduling policy: BATCH/IDLE/OTHER, for example: + +```bash +profiler start --sched +``` + +The second line from bottom in flamegraph represent the scheduling policy. + +## Build allocation profile from live objects only + +Use `--live` flag option to retain allocation samples with live objects only (object that have not been collected by the end of profiling session). Useful for finding Java heap memory leaks. + +```bash +profiler start --live +``` + +## Config method of collecting C stack frames + +Use `--cstack MODE` to config how to walk native frames (C stack). Possible modes are fp (Frame Pointer), dwarf (DWARF unwind info), lbr (Last Branch Record, available on Haswell since Linux 4.1), and no (do not collect C stack). + +By default, C stack is shown in cpu, itimer, wall-clock and perf-events profiles. Java-level events like alloc and lock collect only Java stack. + +```bash +profiler --cstack fp +``` + +The command above will collection Frame Pointer of C stacks. + +## Begin or end profiling when FUNCTION is executed + +Use `--begin function` and `--end function` to automatically start/stop profiling when the specified native function is executed. Its main purpose is to profile certain JVM phases like GC and Safepoint pauses. You should use native function name defined in a JVM implement, for example `SafepointSynchronize::begin` and `SafepointSynchronize::end` in HotSpot JVM. + +### Time-to-safepoint profiling + +The `--ttsp` option is an alias for `--begin SafepointSynchronize::begin --end RuntimeService::record_safepoint_synchronized`. It is not a separate event type, but rather a constraint. Whatever event type you choose (e.g. cpu or wall), the profiler will work as usual, except that only events between the safepoint request and the start of the VM operation will be recorded. + +```bash +profiler start --begin SafepointSynchronize::begin --end RuntimeService::record_safepoint_synchronized +profiler --ttsp +```