JFR（Java Flight Recorder）を記録してダンプファイルを出力する方法

JFR（Java Flight Recorder）を記録してダンプファイルを出力する方法を紹介します。
ここでは、**Eclipse GlassFish 5.1.0**の状態を記録します。

# 環境
+ OS：CentOS Linux release 7.7.1908
+ jdk1.8
+ GlassFish 5.1.0


```
[glassfish@CENTOS7 ~]$ cat /etc/redhat-release
CentOS Linux release 7.7.1908 (Core)
[glassfish@CENTOS7 ~]$
```

```
[glassfish@CENTOS7 ~]$ java -version
java version "1.8.0_241"
Java(TM) SE Runtime Environment (build 1.8.0_241-b07)
Java HotSpot(TM) 64-Bit Server VM (build 25.241-b07, mixed mode)
[glassfish@CENTOS7 ~]$
```

```
[glassfish@CENTOS7 ~]$ /opt/glassfish5/glassfish/bin/asadmin version
Version string could not be obtained from Server [localhost:4848].
(Turn debugging on e.g. by setting AS_DEBUG=true in your environment, to see the details.)
Using locally retrieved version string from version class.
Version = GlassFish Server Open Source Edition  5.1.0  (build default-private)
Command version executed successfully.
[glassfish@CENTOS7 ~]$
```

**GlassFish 5.1.0**のインストールについては、以前投稿した[CentOS7にEclipse GlassFish 5.1.0をインストールする方法](https://qiita.com/yasushi-jp/items/b93f65dbe0f8286e8bde)を参照してください。


# 1. Glassfishの起動時のJVMオプションの設定

Glassfishの起動時のJVMオプションに以下を追加します。

* `-XX:+UnlockCommercialFeatures`
* `-XX:+FlightRecorder`
* `-XX:FlightRecorderOptions=<..>`

具体的には「`/opt/glassfish5/glassfish/domains/domain1/config`」配下の`domain.xml`の「domain/configs/java-config」（domainタグ内のconfigsタグ内のjava-configタグ内）に以下の3行を追加します。

```xml:/opt/glassfish5/glassfish/domains/domain1/config/domain.xmlに追加
<jvm-options>-XX:+UnlockCommercialFeatures</jvm-options>
<jvm-options>-XX:+FlightRecorder</jvm-options>
<jvm-options>-XX:FlightRecorderOptions=defaultrecording=true,disk=true,repository=/var/log/jvm/repositories,maxsize=2048m,maxage=25h,dumponexit=true,dumponexitpath=/var/log/jvm/dump.jfr</jvm-options>
```

※FlightRecorderOptionsの設定の詳細は[Javaのページ](https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/java.html "java")を参照してください。

ここでは、一時ディレクトリとして`/var/log/jvm`を使用するので、`/var/log`配下に`jvm`ディレクトリを作成し、glassfishユーザが`/var/log/jvm`配下にファイルやディレクトリを作成できるようにしておきます。

```console:実行結果
[glassfish@CENTOS7 ~]$ su
パスワード:
[root@CENTOS7 glassfish]# cd /var/log
[root@CENTOS7 log]#
[root@CENTOS7 log]# mkdir jvm
[root@CENTOS7 log]# chown glassfish:glassfish jvm
[root@CENTOS7 log]# exit
exit
[glassfish@CENTOS7 ~]$
```


# 2. Glassfishを起動

以下のコマンドでGlassfishを起動します。

`/opt/glassfish5/glassfish/bin/asadmin start-domain domain1`

```console:実行結果
[glassfish@CENTOS7 config]$ /opt/glassfish5/glassfish/bin/asadmin start-domain domain1
Waiting for domain1 to start ......
Successfully started the domain : domain1
domain  Location: /opt/glassfish5/glassfish/domains/domain1
Log File: /opt/glassfish5/glassfish/domains/domain1/logs/server.log
Admin Port: 4848
Command start-domain executed successfully.
[glassfish@CENTOS7 config]$
```

# 3. JFRダンプファイルの出力

まずは以下のコマンドでglassfishのプロセスIDを調べます。

`ps -ef | grep glassfish | grep -v grep`

```console:実行結果
[glassfish@CENTOS7 config]$ ps -ef | grep glassfish | grep -v grep
glassfi+ 26302     1  5 19:54 pts/0    00:00:16 /usr/java/jdk1.8.0_241-amd64/bin/java -cp /opt/glassfish5/glassfish/modules/glassfish.jar -XX:+UnlockCommercialFeatures -XX:+UnlockDiagnosticVMOptions -XX:NewRatio=2 -XX:FlightRecorderOptions=defaultrecording=true,disk=true,repository=/var/log/jvm/repositories,maxsize=2048m,maxage=25h,dumponexit=true,dumponexitpath=/var/log/jvm/dump.jfr -XX:+FlightRecorder -XX:MaxPermSize=192m -Xmx512m -javaagent:/opt/glassfish5/glassfish/lib/monitor/flashlight-agent.jar -client -Djavax.xml.accessExternalSchema=all -Djavax.net.ssl.trustStore=/opt/glassfish5/glassfish/domains/domain1/config/cacerts.jks -Djdk.tls.rejectClientInitiatedRenegotiation=true -Djdk.corba.allowOutputStreamSubclass=true -Dfelix.fileinstall.dir=/opt/glassfish5/glassfish/modules/autostart/ -Dorg.glassfish.additionalOSGiBundlesToStart=org.apache.felix.shell,org.apache.felix.gogo.runtime,org.apache.felix.gogo.shell,org.apache.felix.gogo.command,org.apache.felix.shell.remote,org.apache.felix.fileinstall -Dcom.sun.aas.installRoot=/opt/glassfish5/glassfish -Dfelix.fileinstall.poll=5000 -Djava.endorsed.dirs=/opt/glassfish5/glassfish/modules/endorsed:/opt/glassfish5/glassfish/lib/endorsed -Djava.security.policy=/opt/glassfish5/glassfish/domains/domain1/config/server.policy -Dosgi.shell.telnet.maxconn=1 -Dfelix.fileinstall.bundles.startTransient=true -Dcom.sun.enterprise.config.config_environment_factory_class=com.sun.enterprise.config.serverbeans.AppserverConfigEnvironmentFactory -Dfelix.fileinstall.log.level=2 -Djavax.net.ssl.keyStore=/opt/glassfish5/glassfish/domains/domain1/config/keystore.jks -Djava.security.auth.login.config=/opt/glassfish5/glassfish/domains/domain1/config/login.conf -Dfelix.fileinstall.disableConfigSave=false -Dfelix.fileinstall.bundles.new.start=true -Dcom.sun.aas.instanceRoot=/opt/glassfish5/glassfish/domains/domain1 -Dosgi.shell.telnet.port=6666 -Dgosh.args=--nointeractive -Dcom.sun.enterprise.security.httpsOutboundKeyAlias=s1as -Dosgi.shell.telnet.ip=127.0.0.1 -DANTLR_USE_DIRECT_CLASS_LOADING=true -Djava.awt.headless=true -Dcom.ctc.wstx.returnNullForDefaultNamespace=true -Djava.ext.dirs=/usr/java/jdk1.8.0_241-amd64/lib/ext:/usr/java/jdk1.8.0_241-amd64/jre/lib/ext:/opt/glassfish5/glassfish/domains/domain1/lib/ext -Djdbc.drivers=org.apache.derby.jdbc.ClientDriver -Djava.library.path=/opt/glassfish5/glassfish/lib:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib com.sun.enterprise.glassfish.bootstrap.ASMain -upgrade false -domaindir /opt/glassfish5/glassfish/domains/domain1 -read-stdin true -asadmin-args --host,,,localhost,,,--port,,,4848,,,--secure=false,,,--terse=false,,,--echo=false,,,--interactive=true,,,start-domain,,,--verbose=false,,,--watchdog=false,,,--debug=false,,,--domaindir,,,/opt/glassfish5/glassfish/domains,,,domain1 -domainname domain1 -instancename server -type DAS -verbose false -asadmin-classpath /opt/glassfish5/glassfish/lib/client/appserver-cli.jar -debug false -asadmin-classname com.sun.enterprise.admin.cli.AdminMain
[glassfish@CENTOS7 config]$
```

プロセスIDが`26302`であることがわかります。

以下のコマンドでJFR（Java Flight Recorder）の記録をダンプファイルに出力します。

`jcmd <プロセスID> JFR.dump recording=0 filename=<出力ダンプファイルのパス>`

```console:実行結果
[glassfish@CENTOS7 ~]$ jcmd 26302 JFR.dump recording=0 filename=/home/glassfish/perf/test_$(date "+%Y%m%d_%H%M%S").jfr
26302:
Dumped recording 0, 2.5 MB written to:

/home/glassfish/perf/test_20200726_200546.jfr
[glassfish@CENTOS7 ~]$
```

出力されたダンプファイルを確認します。

```console:実行結果
[glassfish@CENTOS7 ~]$ cd /home/glassfish/perf
[glassfish@CENTOS7 perf]$ ls -l
合計 2600
-rw-rw-r--. 1 glassfish glassfish 2660207  7月 26 20:05 test_20200726_200546.jfr
[glassfish@CENTOS7 perf]$
```

# 3. 「jmc.exe」で出力されたダンプファイル（jfrファイル）を参照

出力したjfrダンプファイル（test_20200726_200546.jfr）をjdk1.8がインストールされているWindowsPCに転送します。

「`C:\Program Files\Java\jdk1.8.0_231\bin`」配下の「jmc.exe」（Java Mission Contorol）を起動します。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/292434/841d6d4b-1646-360b-cc83-626e4774dffe.png)


メニューから「ファイル」－「ファイルを開く」で出力したjfrダンプファイル（test_20200726_200546.jfr）を開きます。

![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/292434/f51bcedf-4176-64a2-114b-1ac617c0be08.png)

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/292434/c283786b-11cc-2f54-5385-daece480ca95.png)

「jmc.exe」（Java Mission Contorol）で出力したjfrダンプファイルを参照することができます。

![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/292434/bac900f2-657f-0ee9-c384-78809007850a.png)


# 参考

> [Java Platform, Standard Editionツール・リファレンス](https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/java.html "java")