以前にcargo-profiler を Dockerを使ってMac上で実行する記事を書いたのですが、数ヶ月前に試したら上手く動かせなくなっていました1。
メンテナンス状況もたまに自動ライブラリバージョンアップが入るだけ、になっているようだったので乗り換えを考えていたところ、cargo-flamegraph というプロファイラツールを見かけたのでこれを採用しました。
cargo-flamegraph も cargo-profiler 同様、Mac では動かなそうだったので、実行する上で Docker を使用しました。その手順をまとめています。
手順
Dockerイメージのビルド
$ docker build -t flamegraph .
コマンドを、下記の Dockerfile を設置したディレクトリで実行する。
FROM debian:buster-slim
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH \
RUST_VERSION=1.68.2
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
ca-certificates \
gcc \
libc6-dev \
wget \
; \
dpkgArch="$(dpkg --print-architecture)"; \
case "${dpkgArch##*-}" in \
amd64) rustArch='x86_64-unknown-linux-gnu'; rustupSha256='bb31eaf643926b2ee9f4d8d6fc0e2835e03c0a60f34d324048aa194f0b29a71c' ;; \
armhf) rustArch='armv7-unknown-linux-gnueabihf'; rustupSha256='6626b90205d7fe7058754c8e993b7efd91dedc6833a11a225b296b7c2941194f' ;; \
arm64) rustArch='aarch64-unknown-linux-gnu'; rustupSha256='4ccaa7de6b8be1569f6b764acc28e84f5eca342f5162cd5c810891bff7ed7f74' ;; \
i386) rustArch='i686-unknown-linux-gnu'; rustupSha256='34392b53a25c56435b411d3e575b63aab962034dd1409ba405e708610c829607' ;; \
*) echo >&2 "unsupported architecture: ${dpkgArch}"; exit 1 ;; \
esac; \
url="https://static.rust-lang.org/rustup/archive/1.25.2/${rustArch}/rustup-init"; \
wget "$url"; \
echo "${rustupSha256} *rustup-init" | sha256sum -c -; \
chmod +x rustup-init; \
./rustup-init -y --no-modify-path --profile minimal --default-toolchain $RUST_VERSION --default-host ${rustArch}; \
rm rustup-init; \
chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \
rustup --version; \
cargo --version; \
rustc --version; \
apt-get remove -y --auto-remove \
wget \
; \
rm -rf /var/lib/apt/lists/*;
RUN apt update \
&& apt install -y linux-perf
RUN cargo install flamegraph \
&& chmod -R a+w $CARGO_HOME
(このファイルは、https://github.com/rust-lang/docker-rust/tree/61db4471f1ef23dacd217f4a67841cc09aa05858/1.68.2 の末尾に
RUN apt update \
&& apt install -y linux-perf
RUN cargo install flamegraph \
&& chmod -R a+w $CARGO_HOME
を書き加えたものになります。)
perf_event_open が実行できない問題の解消
この時点で flamegraph を実行しても、
perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error 1 (Operation not permitted)
perf_event_open(..., 0) failed unexpectedly with error 1 (Operation not permitted)
Error:
No permission to enable cycles:u event.
のようなエラーが出て、異常終了してしまいます。
こちらの記事によると、root 権限を持っていないために起きているらしいです。
今回は、同記事内に記載されている選択肢1の「'seccomp'を少しカスタマイズする」対処方法を取りました。手順は下記になります。
-
seccomp のデフォルト設定ファイルをダウンロード。
my_seccomp.json
という名前で保存。 -
my_seccomp.json
のsyscalls
フィールドの配列にperf_event_open
を書き加える。 -
$ docker run
実行時に-security-opt seccomp=./my_seccomp.json
のコマンドを付けて、seccomp ファイルとして設定する。
※セキュリティリスクを増す可能性がありそうです。実施は自己責任でお願いします。
プロファイル実行
下記を実行すると、cargo run --release
を実行した場合のプロファイル結果が得られます。
$ docker run --rm --user "$(id -u)":"$(id -g)" \
--security-opt seccomp=./my_seccomp.json \
-v /your/cargo/project/path:/workspace \
-w /workspace \
flamegraph \
cargo flamegraph
(/your/cargo/project/path
には、プロファイル対象のcargoプロジェクトのルートディレクトリを記載してください。)
ただ、自分の主な用途2では標準入出力のリダイレクトがしたくなるので、下記のようにコマンドを自由に表記できるオプションで使用しています。
$ docker run --rm --user "$(id -u)":"$(id -g)" \
--security-opt seccomp=./my_seccomp.json \
-v /your/cargo/project/path:/workspace \
-w /workspace \
flamegraph \
bash -c "cargo flamegraph -- cargo run --release --bin a < in/0000.txt > out/0000.txt"
(in/0000.txt
と out/0000.txt
が、入出力ファイルに当たります。)
実行すると、flamegraph.svg
と perf.data
というファイルが吐かれます。
flamegraph.svg
が結果を図示した画像で、以下のようなものになります(perf.data
の方はシステムデータ的なもののようなので気にしなくて良さそうです)。
関数ごとの実行時間がグラフの長さで表現されています。見やすくて良いですね。