From 55bc0b236eef2029925bd4c1b74476db5d2866cf Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 11 Mar 2025 23:28:10 +0800 Subject: [PATCH] improve profiler command output, doc, timeout. #2961 --- .../command/monitor200/ProfilerCommand.java | 53 +++++++++++++++++-- site/docs/doc/profiler.md | 6 +++ site/docs/en/doc/profiler.md | 6 +++ 3 files changed, 62 insertions(+), 3 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 e9f4df108..5deeb7869 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 @@ -51,6 +51,9 @@ import one.profiler.Counter; + " profiler list # list all supported events\n" + " profiler actions # list all supported actions\n" + " profiler start --event alloc\n" + + " profiler start --timeout 300s" + + " profiler start --loop 300s -f /tmp/result-%t.html" + + " profiler start --duration 300" + " profiler stop --format html # output file format, support flat[=N]|traces[=N]|collapsed|flamegraph|tree|jfr\n" + " profiler stop --file /tmp/result.html\n" + " profiler stop --threads \n" @@ -66,7 +69,10 @@ import one.profiler.Counter; //@formatter:on public class ProfilerCommand extends AnnotatedCommand { private static final Logger logger = LoggerFactory.getLogger(ProfilerCommand.class); + // Track if a file was specified during profiler start + private static String fileSpecifiedAtStart = null; + // TODO start 时,没指定 file, 是否在 stop 时,能生成 html 或者 jfr 不? private String action; private String actionArg; @@ -743,10 +749,36 @@ public class ProfilerCommand extends AnnotatedCommand { String result = execute(asyncProfiler, this.actionArg); appendExecuteResult(process, result); } else if (ProfilerAction.start.equals(profilerAction)) { + // Track if file parameter was specified during start + boolean autoGeneratedFile = false; + if (this.file != null) { + fileSpecifiedAtStart = this.file; + logger.debug("File specified during profiler start: {}", fileSpecifiedAtStart); + } else if (this.timeout != null) { + // Auto-generate file if timeout is specified but file is not + try { + this.file = outputFile(); + logger.debug("Auto-generated file for timeout: {}", this.file); + fileSpecifiedAtStart = this.file; + autoGeneratedFile = true; + } catch (IOException e) { + logger.warn("Failed to auto-generate file for timeout", e); + } + } + if (this.duration == null) { String executeArgs = executeArgs(ProfilerAction.start); String result = execute(asyncProfiler, executeArgs); - appendExecuteResult(process, result); + ProfilerModel profilerModel = createProfilerModel(result); + + // Add information about auto-generated file for timeout + if (autoGeneratedFile && this.file != null) { + profilerModel.setOutputFile(this.file); + profilerModel.setExecuteResult(profilerModel.getExecuteResult() + + "\nAuto-generated output file will be: " + this.file + "\n"); + } + + process.appendResult(profilerModel); } else { // 设置延时执行 stop final String outputFile = outputFile(); String executeArgs = executeArgs(ProfilerAction.start); @@ -833,12 +865,27 @@ public class ProfilerCommand extends AnnotatedCommand { } private ProfilerModel processStop(AsyncProfiler asyncProfiler, ProfilerAction profilerAction) throws IOException { - String outputFile = outputFile(); + String outputFile = null; + + // If we're stopping and a file was specified during start, don't generate a new + // output file + if (profilerAction == ProfilerAction.stop && fileSpecifiedAtStart != null) { + outputFile = fileSpecifiedAtStart; + // Reset the tracking variable after stop + logger.debug("Using file specified during start: {}", fileSpecifiedAtStart); + fileSpecifiedAtStart = null; + } else { + // Otherwise generate or use the specified output file + outputFile = outputFile(); + } + String executeArgs = executeArgs(profilerAction); String result = execute(asyncProfiler, executeArgs); ProfilerModel profilerModel = createProfilerModel(result); - profilerModel.setOutputFile(outputFile); + if (outputFile != null) { + profilerModel.setOutputFile(outputFile); + } return profilerModel; } diff --git a/site/docs/doc/profiler.md b/site/docs/doc/profiler.md index 8e3bee33f..7cc37523f 100644 --- a/site/docs/doc/profiler.md +++ b/site/docs/doc/profiler.md @@ -377,12 +377,18 @@ profiler start -e cpu --jfrsync +jdk.YoungGarbageCollection+jdk.OldGarbageCollec 使用 `--loop TIME` 可以持续运行 profiler 并周期性保存结果。选项格式可以是具体时间 hh:mm:ss 或以秒、分钟、小时或天计算的时间间隔。需要确保指定的输出文件名中包含时间戳,否则每次输出的结果都会覆盖上次保存的结果。以下命令持续执行 profiling 并将每个小时内的记录保存到一个 jfr 文件中。 +> 如果没有指定 `-f` 参数,则不会保存任何内容。如果 `-f` 参数里没有 `%t`,则会循环保存到同一个文件里。 + ```bash profiler start --loop 1h -f /var/log/profile-%t.jfr ``` ## `--timeout` 选项 +```bash +profiler start --timeout 300s +``` + 这个选项指定 profiling 自动在多久后停止。该选项和 `--loop` 选项的格式一致,可以是时间点,也可以是一个时间间隔。这两个选项都是用于 `start` action。可参考 [async-profiler docs](https://github.com/async-profiler/async-profiler/blob/master/docs/ProfilerOptions.md) 了解更多信息。 ## `--wall` 选项 diff --git a/site/docs/en/doc/profiler.md b/site/docs/en/doc/profiler.md index 924278b25..b8b4ddaea 100644 --- a/site/docs/en/doc/profiler.md +++ b/site/docs/en/doc/profiler.md @@ -379,12 +379,18 @@ profiler start -e cpu --jfrsync +jdk.YoungGarbageCollection+jdk.OldGarbageCollec Use `--loop TIME` to run profiler in a loop (continuous profiling). The argument is either a clock time (hh:mm:ss) or a loop duration in seconds, minutes, hours, or days. Make sure the filename includes a timestamp pattern, or the output will be overwritten on each iteration. The command below will run profiling endlessly and save records of each hour to a jfr file. +> If the `-f` parameter is not specified, nothing will be saved. If the `-f` parameter does not contain `%t`, it will overwrite the same file repeatedly. + ```bash profiler start --loop 1h -f /var/log/profile-%t.jfr ``` ## `--timeout` option +```bash +profiler start --timeout 300s +``` + This option specifies the time when profiling will automatically stop. The format is the same as in loop: it is either a wall clock time (12:34:56) or a relative time interval (2h). Both `--loop` and `--timeout` are used for `start` action, for further information refer to [async-profiler docs](https://github.com/async-profiler/async-profiler/blob/master/docs/ProfilerOptions.md).