Skip to content

Commit

Permalink
Add -s,-g,-a,-l option for profiler command, better support farmat op…
Browse files Browse the repository at this point in the history
…tion (#2613)
  • Loading branch information
Winson-Huang authored Aug 18, 2023
1 parent 5b4eb75 commit f375238
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
+ " 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"
Expand Down Expand Up @@ -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
*/
Expand All @@ -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;

Expand Down Expand Up @@ -184,15 +225,18 @@ public void setFramebuf(long framebuf) {
}

@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;
}

Expand All @@ -209,6 +253,30 @@ public void setThreads(boolean threads) {
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) {
Expand All @@ -227,18 +295,50 @@ public void setDuration(long duration) {
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;
Expand Down Expand Up @@ -306,6 +406,9 @@ private String executeArgs(ProfilerAction action) {
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(',');
}
Expand All @@ -315,6 +418,18 @@ private String executeArgs(ProfilerAction action) {
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(',');
}
Expand All @@ -332,6 +447,19 @@ private String executeArgs(ProfilerAction action) {
}
}

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();
}

Expand Down Expand Up @@ -460,18 +588,40 @@ private ProfilerModel processStop(AsyncProfiler asyncProfiler) throws IOExceptio

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);
Expand Down
39 changes: 29 additions & 10 deletions site/docs/doc/profiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

`profiler` 命令基本运行结构是 `profiler action [actionArg]`

`profiler` 命令的格式基本与上游项目 [async-profiler](https://github.com/async-profiler/async-profiler) 保持一致,详细的使用方式可参考上游项目的 README、Github Disscussions 以及其他文档资料。

## 参数说明

| 参数名称 | 参数说明 |
Expand All @@ -29,7 +31,7 @@ Started [cpu] profiling
```

::: tip
默认情况下,生成的是 cpu 的火焰图,即 event 为`cpu`。可以用`--event`参数来指定
默认情况下,生成的是 cpu 的火焰图,即 event 为`cpu`。可以用`--event`参数指定其他性能分析模式,见下文
:::

## 获取已采集的 sample 的数量
Expand All @@ -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 结果

Expand Down Expand Up @@ -100,26 +102,32 @@ Basic events:
lock
wall
itimer
Java method calls:
ClassName.methodName
Perf events:
page-faults
context-switches
cycles
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
Expand All @@ -132,7 +140,7 @@ $ profiler resume
Started [cpu] profiling
```

`start``resume`的区别是:`start`是新开始采样`resume`会保留上次`stop`时的数据
`start``resume`的区别是:`start`会清除已有的分析结果重新开始`resume`则会保留已有的结果,将新的分析结果附加到已有结果中

通过执行`profiler getSamples`可以查看 samples 的数量来验证。

Expand Down Expand Up @@ -183,7 +191,7 @@ profiler start --framebuf 5000000
profiler start --include 'java/*' --include 'com/demo/*' --exclude '*Unsafe.park*'
```

> `--include/--exclude` 都支持设置多个值 ,但是需要配置在命令行的最后。
> `--include/--exclude` 都支持多次设置,但是需要配置在命令行的最后。也可使用短参数格式 `-I/-X`
## 指定执行时间

Expand All @@ -199,6 +207,7 @@ profiler start --duration 300
```
profiler start --file /tmp/test.jfr
profiler start -o jfr
```

`file`参数支持一些变量:
Expand All @@ -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
Loading

0 comments on commit f375238

Please sign in to comment.