Add -s,-g,-a,-l option for profiler command, better support farmat option (#2613)

pull/1965/merge
Winson Huang 1 year ago committed by GitHub
parent 5b4eb75bdb
commit f375238d74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -51,7 +51,7 @@ import one.profiler.Counter;
+ " profiler list # list all supported events\n"
+ " profiler actions # list all supported actions\n"
+ " profiler start --event alloc\n"
+ " profiler stop --format html # output file format, support html,jfr\n"
+ " 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"
+ " profiler start --include 'java/*' --include 'com/demo/*' --exclude '*Unsafe.park*'\n"
@ -94,6 +94,26 @@ public class ProfilerCommand extends AnnotatedCommand {
*/
private boolean threads;
/**
* use simple class names instead of FQN
*/
private boolean simple;
/**
* print method signatures
*/
private boolean sig;
/**
* annotate Java methods
*/
private boolean ann;
/**
* prepend library names
*/
private boolean lib;
/**
* include only kernel-mode events
*/
@ -119,6 +139,27 @@ public class ProfilerCommand extends AnnotatedCommand {
*/
private List<String> excludes;
/**
* FlameGraph title
*/
private String title;
/**
* FlameGraph minimum frame width in percent
*/
private String minwidth;
/**
* generate stack-reversed FlameGraph / Call tree
*/
private boolean reverse;
/**
* count the total value (time, bytes, etc.) instead of samples
*/
private boolean total;
private static String libPath;
private static AsyncProfiler profiler = null;
@ -184,15 +225,18 @@ public class ProfilerCommand extends AnnotatedCommand {
}
@Option(shortName = "f", longName = "file")
@Description("dump output to <filename>")
@Description("dump output to <filename>, if ends with html or jfr, content format can be infered")
public void setFile(String file) {
this.file = file;
}
@Option(longName = "format")
@Description("dump output file format(html, jfr), default valut is html")
@DefaultValue("html")
@Option(shortName = "o", longName = "format")
@Description("dump output content format(flat[=N]|traces[=N]|collapsed|flamegraph|tree|jfr)")
public void setFormat(String format) {
// only for backward compatibility
if ("html".equals(format)) {
format = "flamegraph";
}
this.format = format;
}
@ -209,6 +253,30 @@ public class ProfilerCommand extends AnnotatedCommand {
this.threads = threads;
}
@Option(shortName = "s", flag = true)
@Description("use simple class names instead of FQN")
public void setSimple(boolean simple) {
this.simple = simple;
}
@Option(shortName = "g", flag = true)
@Description("print method signatures")
public void setSig(boolean sig) {
this.sig = sig;
}
@Option(shortName = "a", flag = true)
@Description("annotate Java methods")
public void setAnn(boolean ann) {
this.ann = ann;
}
@Option(shortName = "l", flag = true)
@Description("prepend library names")
public void setLib(boolean lib) {
this.lib = lib;
}
@Option(longName = "allkernel", flag = true)
@Description("include only kernel-mode events")
public void setAllkernel(boolean allkernel) {
@ -227,18 +295,50 @@ public class ProfilerCommand extends AnnotatedCommand {
this.duration = duration;
}
@Option(longName = "include")
@Option(shortName = "I", longName = "include")
@Description("include stack traces containing PATTERN, for example: 'java/*'")
public void setInclude(List<String> includes) {
this.includes = includes;
}
@Option(longName = "exclude")
@Option(shortName = "X", longName = "exclude")
@Description("exclude stack traces containing PATTERN, for example: '*Unsafe.park*'")
public void setExclude(List<String> excludes) {
this.excludes = excludes;
}
@Option(longName = "title")
@Description("FlameGraph title")
public void setTitle(String title) {
// escape HTML special characters
// and escape comma to avoid conflicts with JVM TI
title = title.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;")
.replace("'", "&apos;")
.replace(",", "&#44;");
this.title = title;
}
@Option(longName = "minwidth")
@Description("FlameGraph minimum frame width in percent")
public void setMinwidth(String minwidth) {
this.minwidth = minwidth;
}
@Option(longName = "reverse", flag = true)
@Description("generate stack-reversed FlameGraph / Call tree")
public void setReverse(boolean reverse) {
this.reverse = reverse;
}
@Option(longName = "total", flag = true)
@Description("count the total value (time, bytes, etc.) instead of samples")
public void setTotal(boolean total) {
this.total = total;
}
private AsyncProfiler profilerInstance() {
if (profiler != null) {
return profiler;
@ -306,6 +406,9 @@ public class ProfilerCommand extends AnnotatedCommand {
if (this.file != null) {
sb.append("file=").append(this.file).append(',');
}
if (this.format != null) {
sb.append(this.format).append(',');
}
if (this.interval != null) {
sb.append("interval=").append(this.interval).append(',');
}
@ -315,6 +418,18 @@ public class ProfilerCommand extends AnnotatedCommand {
if (this.threads) {
sb.append("threads").append(',');
}
if (this.simple) {
sb.append("simple").append(",");
}
if (this.sig) {
sb.append("sig").append(",");
}
if (this.ann) {
sb.append("ann").append(",");
}
if (this.lib) {
sb.append("lib").append(",");
}
if (this.allkernel) {
sb.append("allkernel").append(',');
}
@ -332,6 +447,19 @@ public class ProfilerCommand extends AnnotatedCommand {
}
}
if (this.title != null) {
sb.append("title=").append(this.title).append(',');
}
if (this.minwidth != null) {
sb.append("minwidth=").append(this.minwidth).append(',');
}
if (this.reverse) {
sb.append("reverse").append(',');
}
if (this.total) {
sb.append("total").append(',');
}
return sb.toString();
}
@ -460,18 +588,40 @@ public class ProfilerCommand extends AnnotatedCommand {
private String outputFile() throws IOException {
if (this.file == null) {
String fileExt = outputFileExt();
File outputPath = ArthasBootstrap.getInstance().getOutputPath();
if (outputPath != null) {
this.file = new File(outputPath,
new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + "." + this.format)
new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + "." + fileExt)
.getAbsolutePath();
} else {
this.file = File.createTempFile("arthas-output", "." + this.format).getAbsolutePath();
this.file = File.createTempFile("arthas-output", "." + fileExt).getAbsolutePath();
}
}
return file;
}
/**
* This method should only be called when {@code this.file == null} is true.
*/
private String outputFileExt() {
String fileExt = "";
if (this.format == null) {
fileExt = "html";
} else if (this.format.startsWith("flat") || this.format.startsWith("traces")
|| this.format.equals("collapsed")) {
fileExt = "txt";
} else if (this.format.equals("flamegraph") || this.format.equals("tree")) {
fileExt = "html";
} else if (this.format.equals("jfr")) {
fileExt = "jfr";
} else {
// illegal -o option makes async-profiler use flat
fileExt = "txt";
}
return fileExt;
}
private void appendExecuteResult(CommandProcess process, String result) {
ProfilerModel profilerModel = createProfilerModel(result);
process.appendResult(profilerModel);

@ -10,6 +10,8 @@
`profiler` 命令基本运行结构是 `profiler action [actionArg]`
`profiler` 命令的格式基本与上游项目 [async-profiler](https://github.com/async-profiler/async-profiler) 保持一致,详细的使用方式可参考上游项目的 README、Github Disscussions 以及其他文档资料。
## 参数说明
| 参数名称 | 参数说明 |
@ -29,7 +31,7 @@ Started [cpu] profiling
```
::: tip
默认情况下,生成的是 cpu 的火焰图,即 event 为`cpu`。可以用`--event`参数指定。
默认情况下,生成的是 cpu 的火焰图,即 event 为`cpu`。可以用`--event`参数指定其他性能分析模式,见下文
:::
## 获取已采集的 sample 的数量
@ -50,17 +52,17 @@ $ profiler status
## 停止 profiler
### 生成 html 格式结果
### 生成火焰图格式结果
默认情况下,结果文件是`html`格式,也可以用`--format`参数指定:
默认情况下,结果是 [Flame Graph](https://github.com/BrendanGregg/FlameGraph) 格式的 `html` 文件,也可以用 `-o``--format` 参数指定其他内容格式,包括 flat、traces、collapsed、flamegraph、tree、jfr。
```bash
$ profiler stop --format html
$ profiler stop --format flamegraph
profiler output file: /tmp/test/arthas-output/20211207-111550.html
OK
```
或者在`--file`参数里用文件名指名格式。比如`--file /tmp/result.html`
在`--file`参数指定的文件名后缀为 `html``jfr` 时,文件格式可以被推断出来。比如`--file /tmp/result.html` 将自动生成火焰图
## 通过浏览器查看 arthas-output 下面的 profiler 结果
@ -100,6 +102,8 @@ Basic events:
lock
wall
itimer
Java method calls:
ClassName.methodName
Perf events:
page-faults
context-switches
@ -107,19 +111,23 @@ Perf events:
instructions
cache-references
cache-misses
branches
branch-instructions
branch-misses
bus-cycles
L1-dcache-load-misses
LLC-load-misses
dTLB-load-misses
rNNN
pmu/event-descriptor/
mem:breakpoint
trace:tracepoint
kprobe:func
uprobe:path
```
如果遇到 OS 本身的权限/配置问题,然后  缺少部分 event可以参考`async-profiler`本身文档:[async-profiler](https://github.com/jvm-profiling-tools/async-profiler)
如果遇到 OS 本身的权限/配置问题,然后缺少部分 event可以参考 [async-profiler 的文档](https://github.com/jvm-profiling-tools/async-profiler)
可以用`--event`参数指定要采样的事件,比如对`alloc`事件进入采样
可以用`--event`参数指定要采样的事件,比如 `alloc` 表示分析内存分配情况
```bash
$ profiler start --event alloc
@ -132,7 +140,7 @@ $ profiler resume
Started [cpu] profiling
```
`start`和`resume`的区别是:`start`是新开始采样,`resume`会保留上次`stop`时的数据
`start`和`resume`的区别是:`start`会清除已有的分析结果重新开始,`resume`则会保留已有的结果,将新的分析结果附加到已有结果中
通过执行`profiler getSamples`可以查看 samples 的数量来验证。
@ -183,7 +191,7 @@ profiler start --framebuf 5000000
profiler start --include 'java/*' --include 'com/demo/*' --exclude '*Unsafe.park*'
```
> `--include/--exclude` 都支持设置多个值 ,但是需要配置在命令行的最后。
> `--include/--exclude` 都支持多次设置,但是需要配置在命令行的最后。也可使用短参数格式 `-I/-X`
## 指定执行时间
@ -199,6 +207,7 @@ profiler start --duration 300
```
profiler start --file /tmp/test.jfr
profiler start -o jfr
```
`file`参数支持一些变量:
@ -211,6 +220,16 @@ profiler start --file /tmp/test.jfr
- JDK Mission Control https://github.com/openjdk/jmc
- JProfiler https://github.com/alibaba/arthas/issues/1416
## 控制分析结果的格式
使用 `-s` 选项将结果中的 Fully qualified name 替换为简单名称,如 `demo.MathGame.main` 替换为 `MathGame.main`。使用 `-g` 选项指定输出方法签名,如 `demo.MathGame.main` 替换为 `demo.MathGame.main([Ljava/lang/String;)V`。此外还有许多可调整分析结果格式的选项,可参考 [async-profiler 的 README 文档](https://github.com/async-profiler/async-profiler#readme) 以及 [async-profiler 的 Github Discussions](https://github.com/async-profiler/async-profiler/discussions) 等材料。
例如,以下命令中,`-s` 将输出中的类名称指定为简短格式,`-g` 显示方法的完整签名,`-a` 标注出 Java 方法,`-l` 为原生方法增加库名称,`--title` 为生成火焰图页面指定标题,`--minwidth` 将过滤火焰图中宽度为 15% 以下的帧,`--reverse` 将火焰图倒置。
```
profiler stop -s -g -a -l --title <flametitle> --minwidth 15 --reverse
```
## 生成的火焰图里的 unknown
- https://github.com/jvm-profiling-tools/async-profiler/discussions/409

@ -6,10 +6,12 @@
Generate a flame graph using [async-profiler](https://github.com/jvm-profiling-tools/async-profiler)
:::
The `profiler` command supports generate flame graph for application hotspots.
The `profiler` command supports generating flame graph for application hotspots.
The basic usage of the `profiler` command is `profiler action [actionArg]`
The arguments of `profiler` command basically keeps consistent with upstream project [async-profiler](https://github.com/async-profiler/async-profiler), you can refer to its README, Github Discussions and other documentations for further information of usage.
## Supported Options
| Name | Specification |
@ -29,7 +31,7 @@ Started [cpu] profiling
```
::: tip
By default, the sample event is `cpu`. Can be specified with the `--event` parameter.
By default, the sample event is `cpu`. Other valid profiling modes can be specified with the `--event` parameter, see relevant contents below.
:::
## Get the number of samples collected
@ -50,17 +52,17 @@ Can view which `event` and sampling time.
## Stop profiler
### Generating html format results
### Generating flame graph results
By default, the result file is `html` format. You can also specify it with the `--format` parameter:
By default, the result file is `html` file in [Flame Graph](https://github.com/BrendanGregg/FlameGraph) format. You can also specify other format with the `-o` or `--format` parameter, including flat, traces, collapsed, flamegraph, tree, jfr:
```bash
$ profiler stop --format html
$ profiler stop --format flamegraph
profiler output file: /tmp/test/arthas-output/20211207-111550.html
OK
```
Or use the file name name format in the `--file` parameter. For example, `--file /tmp/result.html`.
When extension of filename in `--file` parameter is `html` or `jfr`, the output format can be infered. For example, `--file /tmp/result.html` will generate flamegraph automatically.
## View profiler results under arthas-output via browser
@ -100,6 +102,8 @@ Basic events:
lock
wall
itimer
Java method calls:
ClassName.methodName
Perf events:
page-faults
context-switches
@ -107,19 +111,23 @@ Perf events:
instructions
cache-references
cache-misses
branches
branch-instructions
branch-misses
bus-cycles
L1-dcache-load-misses
LLC-load-misses
dTLB-load-misses
rNNN
pmu/event-descriptor/
mem:breakpoint
trace:tracepoint
kprobe:func
uprobe:path
```
If you encounter the permissions/configuration issues of the OS itself and then missing some events, you can refer to the [async-profiler](https://github.com/jvm-profiling-tools/async-profiler) documentation.
You can use the `--event` parameter to specify the event to sample, such as sampling the `alloc` event:
You can use the `--event` parameter to specify the event to sample, for example, `alloc` event means heap memory allocation profiling:
```bash
$ profiler start --event alloc
@ -132,7 +140,7 @@ $ profiler resume
Started [cpu] profiling
```
The difference between `start` and `resume` is: `start` is the new start sampling, `resume` will retain the data of the last `stop`.
The difference between `start` and `resume` is: `start` will clean existing result of last profiling before starting, `resume` will retain the existing result and add result of this time to it.
You can verify the number of samples by executing `profiler getSamples`.
@ -185,7 +193,7 @@ If the application is complex and generates a lot of content, and you want to fo
profiler start --include'java/*' --include 'com/demo/*' --exclude'*Unsafe.park*'
```
> Both `--include/--exclude` support setting multiple values, but need to be configured at the end of the command line.
> Both `--include/--exclude` support being set multiple times, but need to be configured at the end of the command line. You can also use short parameter format `-I/-X`.
## Specify execution time
@ -201,6 +209,7 @@ profiler start --duration 300
```
profiler start --file /tmp/test.jfr
profiler start -o jfr
```
The `file` parameter supports some variables:
@ -213,6 +222,16 @@ The generated results can be viewed with tools that support the jfr format. such
- JDK Mission Control: https://github.com/openjdk/jmc
- JProfiler: https://github.com/alibaba/arthas/issues/1416
## Control details in result
The `-s` parameter will use simple name instead of Fully qualified name, e.g. `MathGame.main` instead of `demo.MathGame.main`. The `-g` parameter will use method signatures instead of method names, e.g. `demo.MathGame.main([Ljava/lang/String;)V` instead of `demo.MathGame.main`. There are many parameters related to result format details, you can refer to [async-profiler README](https://github.com/async-profiler/async-profiler#readme) and [async-profiler Github Discussions](https://github.com/async-profiler/async-profiler/discussions) and other information.
For example, in command below, `-s` use simple name for Java class, `-g` show method signatures, `-a` will annotate Java methods, `-l` will prepend library names for native method, `--title` specify a title for flame graph page, `--minwidth` will skip frames smaller than 15% in flame graph, `--reverse` will generate stack-reversed FlameGraph / Call tree.
```
profiler stop -s -g -a -l --title <flametitle> --minwidth 15 --reverse
```
## The 'unknown' in profiler result
- https://github.com/jvm-profiling-tools/async-profiler/discussions/409

Loading…
Cancel
Save