Rust Debugging Cheatsheet
開発の基本
エディタ
- VSCode + rust-analyzer - https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer
- 推奨
- neovim + vim-lsp-settings - https://github.com/mattn/vim-lsp-settings
- lsp の最低限の機能が使える
- neovim + coc - https://github.com/fannheyward/coc-rust-analyzer
- 構造に対する置換などの rust-analyzer 固有の機能が使える
- rust-analyzer ガイド - https://rust-analyzer.github.io/manual.html#structural-seach-and-replace
フォーマッタ
Cargo.toml もフォーマットしたい場合 cargo-tomlfmt がある
cargo fmt
-
cargo tomlfmt
- https://github.com/tbrand/cargo-tomlfmt
インクリメンタルビルド
cargo watch
を使う - https://github.com/passcod/cargo-watch
-
cargo watch -x clippy
- ファイル保存の度に lint -
cargo watch -x check
- ファイル保存の度に型チェック(build よりも早い) -
cargo watch -x 'check --tests'
- ファイル保存の度にテストの型チェック -
cargo watch -x test -- --nocapture
- ファイル保存の度にテスト(ログも表示) -
cargo watch -x 'run -- --some-arg'
- ファイル保存の度に実行 -
env RUST_BACKTRACE=1 RUST_LOG=trace cargo watch -x test
- スタックトレース有効& env_logger 有効でコンパイル&テスト-
RUST_LOG=tokio=info,mycrate=trace
のようにクレート単位で指定できる
-
-
cargo clippy --tests -- -Dclippy::all
- clippy waring で exit 1 する。ci で便利
依存関係を最新に保つ
-
cargo tree -i hoge
- hoge crate に依存するクレートを表示する - https://doc.rust-lang.org/cargo/commands/cargo-tree.html -
cargo udeps
- 不要な依存関係を洗い出す - https://crates.io/crates/cargo-udeps -
cargo outdated
- https://qiita.com/sinkuu/items/3ea25a942d80fce74a90#cargo-outdated-cargo-outdated - https://github.com/kbknapp/cargo-outdated-
cargo outdated -R
で直接の依存関係のみチェック
-
-
cargo update
- Cargo.toml の依存関係のバージョン制約内で最新に更新 -
cargo audit
- 依存クレートに脆弱性がないかチェック - https://github.com/RustSec/cargo-audit
コードカバレッジ
- grcov - https://github.com/mozilla/grcov
コードメトリクス解析
ビルドが遅いとき
- 調査方法 - https://qiita.com/kawaemon/items/709b8cd2f0462e5967ca
- 下記環境を整えてくれる fleet を使う - https://github.com/dimensionhq/fleet
-
cargo build –timings
でビルドが重い crate をあぶり出す - https://doc.rust-lang.org/cargo/reference/timings.html
コンパイルが遅いとき
- sccache を使う - https://github.com/mozilla/sccache
- tips
リンクが遅いとき
- lld を使う -
RUSTFLAGS="-C link-arg=-fuse-ld=lld" cargo watch -x run
- https://github.com/rust-lang/rust/issues/39915#issuecomment-595552312 - mold を使う - https://github.com/rui314/mold/issues/26
ビルドキャッシュの管理
-
cargo-cache
- https://github.com/matthiaskrgr/cargo-cache-
~/.cargo/
の中を確認して掃除できる - sccache にも対応している
-
-
cargo clean
- https://doc.rust-lang.org/cargo/commands/cargo-clean.html- target の中を掃除する
docker でビルド
docker run --rm -ti \
--user "$(id -u)":"$(id -g)" \
-v ${HOME}/.cache/cargo/registry:/root/.cargo/registry \
-v ${HOME}/.cache/cargo/git:/root/.cargo/git \
-v ${PWD}:/opt/workspace \
-w /opt/workspace \
rust:latest \
cargo build --release
glibc バージョン問題の対策
musl を使うか、docker でビルドするか、zig cc でバージョンを固定するか
- https://zenn.dev/coord_e/articles/portable-binary-in-rust
- https://github.com/rust-cross/cargo-zigbuild
最適化
- tips
バイナリサイズ最適化
Rust のデバッグ
dbg マクロ
- https://doc.rust-lang.org/std/macro.dbg.html
- println デバッグより短い
println!("{:?}", hoge);
dbg!(&hoge);
変数の型を確認
関数 the_answer_of_everything
返り値の型が知りたいとき
fn the_answer_of_everything() -> i32 { 42 }
fn main() {
let _ :() = the_answer_of_everything();
}
error[E0308]: mismatched types
--> src/main.rs:4:15
|
4 | let _ :() = the_answer_of_everything();
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `i32`
| |
| expected due to this
- rls や rust-analyzer が使えないときに便利
- https://qiita.com/ubnt_intrepid/items/4995c51d1271cc9529f4
変数の型名を文字列で取得
fn type_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
- https://qiita.com/garkimasera/items/729dbf11dd5738c70e11
- http://www.tohoho-web.com/ex/rust.html#typeof
大量のエラーの潰し方
-
cargo clippy --tests --examples 2>&1 | head -n 40
で最初のエラーから順番に潰すこと - 最後のエラーから見ても何もわからないので
コンパイラエラーコードの索引
- Rust Compiler Error Index - https://doc.rust-lang.org/error-index.html
マクロ展開
error-chain のような巨大なマクロが何をしているのか見るのに便利
-
cargo expand
- 全部展開 -
cargo expand -- main
- 特定の関数だけ展開
AST の確認
依存クレートのデバッグ
- doc.rs をよくよく読む
- [src] ボタンで定義のソースコードが見ることができるので実装もよく読む
- github などの issues を検索してよくよく調べる
- 各クレートの issues, discord, gitter, slack, irc で訊いてみる
なんもわからん・・・てなったとき
- rust コミュニティで訊いてみる
- rust-jp slack - https://rust-jp.herokuapp.com/
- rust の公式 discord - https://discordapp.com/invite/rust-lang
- ユーザフォーラム - https://users.rust-lang.org/
- libc のバグトラッカー - https://sourceware.org/bugzilla/buglist.cgi?component=libc&product=glibc&resolution=---
- debian のバグトラッカー - https://bugs.debian.org/
- redhat のバグトラッカー - https://bugzilla.redhat.com/
- man page - https://linuxjm.osdn.jp/
- gentoo wiki - https://wiki.gentoo.org/
- debian wiki - https://wiki.debian.org/
- arch wiki - https://wiki.archlinux.org/
その他有用な rust の情報源
- Rustのライフタイムについてのよくある誤解 - https://github.com/pretzelhammer/rust-blog/blob/master/posts/translations/jp/common-rust-lifetime-misconceptions.md
- Rust Language Cheat Sheet - https://cheats.rs/
- Platform Support - https://doc.rust-lang.org/nightly/rustc/platform-support.html
-
rustup target list
でも確認できる、 rustup の target triple ごとのツールチェーンの対応状況
-
- デザインパターン
- rustc コンパイラ解説 https://rustc-dev-guide.rust-lang.org/
- 言語リファレンス - https://doc.rust-lang.org/reference/introduction.html
rust のテスト
基本
非同期テスト
tokio の場合
#[tokio::test]
async fn hoge_test(){
}
actix の場合(actix-web を使っているときにはこちら)
#[actix_rt::test]
async fn hoge_test(){
}
実行バイナリ解析
- とりあえず cargo-binutils を入れておくこと
- linux のメモリマップについて知る
- stack, heap, shared library - http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2011/2012-01-24/index.html
- elf フォーマットについて知る
- 共有ライブラリの仕組みについて知る
おすすめの書籍
- 電話帳より厚いことで有名な Linuxプログラミングインタフェース
- 内容はハイレベルな 低レベルプログラミング
- 入門には最適な Linuxのしくみ
- 古のテクニックが載っている Binary Hacks ―ハッカー秘伝のテクニック100選
シンボル解析
-
cargo nm -- --demangle /path/to/executable | less
- rust のシンボル名の一覧 -
strings /path/to/executable | less
- バイナリに含まれている文字列列挙
アセンブリ解析
-
cargo objdump -- -disassemble /path/to/executable | less
- rust の関数名とアセンブリの一覧 cargo asm rr_sandbox::id --rust --build-type debug
cargo llvm-ir rr_sandbox::main --rust --build-type debug
- https://rust.godbolt.org/ - オンライン rust asm ビューア
共有ライブラリ
-
ldd /path/to/executable
- 共有ライブラリの依存関係 -
find ./ -name "*.so*" -print | xargs readelf -d
- 共有ライブラリのシンボルテーブルを探す -
LD_DEBUG=all <コマンドを実行>
- 共有ライブラリの読み込みに関して大量のデバッグログが吐かれる -
env | grep LD
- パスを調べる -
readelf -a hogehoge.so | grep SONAME
- SONAME を調べる -
find build/ -name '*so' | xargs ldd | less
- 依存関係を調べる -
readelf -d /path/to/executable
- elf のダイナミックセクションの表示
-
objdump -T a.out | grep GLIBC_
- 実行ファイルが依存する GLIBC のバージョンを確認する
elf
-
readelf -a /path/to/executable | less
- 全部表示 -
readelf -s /path/to/executable
- シンボルテーブル一覧 -
readelf -h /path/to/executable
- ELF ファイルヘッダ, ELFヘッダー情報 -
readelf -l /path/to/executable
- プログラムヘッダー情報 -
readelf -S /path/to/executable
- セクションヘッダー情報 -
readelf -A /path/to/executable
- アーキテクチャ固有情報、クロスコンパイル時に見る
システムコール
-
strace -tt -T -f /path/to/executable
- システムコールの呼び出しをトレース -
strace -tt -T -f -p <PID>
- システムコールの呼び出しをトレース
プロファイリング
まだちゃんと使ったことない
cargo-profiler
sudo apt-get install valgrind
cargo install cargo-profiler
cargo profiler callgrind
cargo profiler cachegrind --release
Thread Profilertcargo binutils
rr + rust-gdb
ステップ実行のリプレイが可能なデバッガ
usage
rr record /path/to/executable
rr replay -d rust-gdb
installation
echo 1 | sudo tee /proc/sys/kernel/perf_event_paranoid
sudo apt update && sudo apt install rr
rr references
- https://github.com/mozilla/rr/wiki/
- using rust with rr - https://gist.github.com/spacejam/15f27007c0b1bcc1d6b4c9169b18868c
- /proc/sys/kernel/perf_event_paranoid - https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/7.3_release_notes/known_issues_compiler_and_tools
- https://github.com/mozilla/rr/wiki/Using-rr-in-an-IDE
- https://bitshifter.github.io/rr+rust/index.html
gdb-dashboard
現在の状態を見やすくするユーザスクリプト
wget -P ~ https://git.io/.gdbinit
gdb usage
基本
-
run [args]
-Start it from the beginning? (y or n)
-
kill
-Kill the program being debugged? (y or n)
quit
ブレイクポイントの管理
-
break <where>
- ソースコードの n 行目にブレークポイントを入れる -
info breakpoints
- ブレークポイントの一覧 -
delete n
- ブレークポイント番号 n を消す -
clear
- 全部消す -
enable n
- ブレークポイント番号 n を有効 -
continue n
無効
ウオッチポイントの管理
watch <where>
info watchpoints
<where> に指定できるもの
<function_name>
<line_number>
<file:line_number>
(スタック)フレームの管理
-
backtrace
-
where
- bt と同じ -
backtrace full
- 呼び出しスタックを表示し、各フレームのローカル変数も出力します。
-
-
info locals
- 局所変数 -
info args
- 関数の引数 -
whatis <variable_name>
- 変数の型を表示
プログラム変更
-
set variable <variable_name>=<value>
- 変数をセット-
set var <variable_name>=<value>
- 同上 -
set <variable_name>=<value>
- 同上
-
-
return <expression>
- 現在の関数から強制リターン
ステッピングの管理
-
s
-step
- ステップ(呼び出された関数に入る)-
rs
-reverse-step
- 逆のステップ
-
-
n
-next
- 次(呼び出された関数には入りません)-
rn
-reverse-next
- 次を逆に
-
-
c
-continue
- 次のブレークポイントまで続行-
rc
-reverse-continue
-
-
si
-stepi
- 1マシン命令を実行-
rsi
-reverse-stepi
-
-
ni
-nexti
- 1マシン命令を実行、関数呼び出しは無視-
rni
-reverse-nexti
-
-
f
-finish
- 現在の関数のリターンまで続行-
rf
-reverse-finish
-
ソースコード
-
list
- 現在のポイントの周辺のソースコードを表示 -
disassemble (<where>)
- 現在または指定箇所のメモリ配置を表示
アセンブリ
-
layout asm
- 全体をページャで表示 -
info sharedlibrary
- ロード済み共有ライブラリの一覧
シグナル
-
info signals
- シグナルのハンドリング状況-
info handle
同上
-
スレッド
info threads
メモリ
-
info proc mappings
- メモリマッピングの確認
-
info files
- 各アドレスがどのセグメントに対応しているか
gdb references
- https://qiita.com/miyagaw61/items/4a4514e2de0b458c2589
- https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf
- https://github.com/cyrus-and/gdb-dashboard
- https://sourceware.org/gdb/onlinedocs/gdb/index.html#Top
- https://sourceware.org/gdb/onlinedocs/gdb/Rust.html#Rust
実行中プロセスのメモリ解析(コアダンプ取得)
- gcore を使う
- https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/developer_guide/debugging-crashed-application_dumping-process-memory-gcore
- https://linuxjm.osdn.jp/html/GNU_gdb/man1/gcore.1.html
- https://qiita.com/yagiaoskywalker/items/38bd5c0d08e35946645b
- https://qiita.com/apierce/items/bcc6dd8f1bf2323343d6
メモリリーク解析
-
https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html
- ※サニタイザで検出できるリークは普通に書いた rust では発生しない
- 検出できるリークの種類→ https://github.com/japaric/rust-san
- valgrind
- ほぼ x86 限定
ネットワーク
- tcp/ip スタックについてよく知る
- https://qiita.com/hana_shin/items/25f7d0c53bbf55d0f963
パケット統計
ip -s l
DNS
nslookup google.com
-
dig google.com
- nslookup より詳しい
tcpdump
actix-web の keep-alive がバグってるのを調べるのに使った
sudo /usr/sbin/tcpdump -nnn -A dst port 10179 -w 10179.dump
tcpdump -vvv -s 0 -nX tcp
ip、ポート、プロトコルとプロセスの一覧
netstat -anp
-
lsof -i:80
- ポート 80 を使ってるプロセス
iptables のルールやチェインの一覧
sudo iptables -vnL --line-numbers
ip 経路情報
traceroute google,com
-
traceroute6 google,com
- NGN 閉域網で rumqtt が ipv6 につなぎにいってしまったときに使った -
mtr google.com
- リッチな traceroute -
traceroute -I 8.8.8.8
- ICMP -
traceroute -U 8.8.8.8 -p 53
- UDP port 53
nmap, arp
-
nmap -sL 192.168.1.0/24
- ネットワーク内のホスト検出 -
arp -a
またはip neigh
sudo arp-scan -l
- mac addr から割り当てられた IP addr を確認 -
sudo nmap -A [ip]
で os & サービス検出 - arp-scan - https://linux.die.net/man/1/arp-scan
wifi メトリクス
cat /proc/net/wireless
iwconfig
iwconfig wlan0
iw wlp2s0 station dump
iw dev
iw dev wlp2s0 link
-
wpa_cli -i wlp2s0 list_networks
で[TEMP-DISABLED]
になっていないか
wifi パケットキャプチャ
- aircrack-ng - https://www.aircrack-ng.org/ - https://qiita.com/syui/items/73562ad8bbaa7e857045
sudo airmon-ng check
-
sudo airmon-ng check kill
- wifi を使っているデーモンを強制終了する -
sudo airmon-ng start wlan0
- wlan0 をモニタリングモードで起動 sudo airodump-ng wlan0mon
sudo airodump-ng wlan0mon --channel 11 --bssid xx:xx:xx:xx:xx:xx --write foo
-
sudo aireplay-ng -0 0 -a <target's bssid> -c <client's mac addr> wlan0mon
- https://www.aircrack-ng.org/doku.php?id=deauthentication sudo airmon-ng stop wlan0mon
-
airdecap-ng -e hoge_ssid -p xxxxx foo.cap
- https://www.aircrack-ng.org/doku.php?id=airdecap-ng -
wireshark で見るときのフィルタ
wlan.ta==9C:63:ED:00:EB:27||wlan.ra==9C:63:ED:00:EB:27||wlan.sa==9C:63:ED:00:EB:27||wlan.da==9C:63:ED:00:EB:27||wlan.addr==9C:63:ED:00:EB:27||wlan.bssid==9C:63:ED:00:EB:27
- ip.addr==192.168.128.1
, ip.src==192.168.128.1 || ip.dst==192.168.1.1
- https://www.wireshark.org/docs/dfref/w/wlan.html
mac アドレス偽装
ディレクトリ、ファイル、ソケットを握ってるプロセスを調べる
-
sudo fuser --verbose ./foo
- foo を握ってるプロセスを調べる -
fuser --namespace tcp 8080
tcp 8000 を握ってるプロセスを調べる -
sudo fuser -k 8000/tcp
- ポート掴んでるプロセスを殺す
名前解決
壊れた SD カードの調査
-
sudo badblocks -wsv /dev/sdb1
(破壊的書き込みテスト) - https://wiki.archlinux.jp/index.php/Badblocks - gparted の fsck - https://wiki.archlinux.jp/index.php/Fsck
dd で作った img を gparted で小さくして sd カードに焼き込み
- https://gist.github.com/grim13b/071d8d990b15f74b1d1c738a8fef830d
- http://linux900.blog.fc2.com/blog-entry-68.html
- https://wiki.archlinux.jp/index.php/Core_utilities#dd
$ sudo dd if=/dev/sdb of=/path/to/foo.img bs=32M
$ sudo losetup -f
/dev/loop13
$ sudo losetup /dev/loop13 ./rpizero.img
$ sudo partprobe /dev/loop13
$ sudo gparted /dev/loop13
$ sudo losetup -d /dev/loop13
$ sudo fdisk -l ./rpizero.img
ディスク ./rpizero.img: 29.17 GiB, 31312576512 バイト, 61157376 セクタ
単位: セクタ (1 * 512 = 512 バイト)
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスクラベルのタイプ: dos
ディスク識別子: 0x00019ef2
デバイス 起動 開始位置 最後から セクタ サイズ Id タイプ
./rpizero.img1 2048 1310546 1308499 638.9M e W95 FAT16 (LBA)
./rpizero.img2 1310547 24918015 23607469 11.3G 5 拡張領域
./rpizero.img5 1310720 1376253 65534 32M 83 Linux
./rpizero.img6 1376256 1742847 366592 179M c W95 FAT32 (LBA)
./rpizero.img7 1744896 24915967 23171072 11.1G 83 Linux
$ sudo truncate --size=$[(24918015+1)*512] ./rpizero.img
$ sudo dd if=./rpizero.img of=/dev/sdb bs=16M status=progress
監視する
ファイルを監視する
- auditctl
- bpf
- inotifywait
*
ブートログ、カーネルログ、システムログ
-
sudo less /var/log/boot.log
- ブートログ -
dmesg
- カーネルログ -
journalctl
- systemd のログ
カーネルコンフィグの確認
sudo modprobe configs
zgrep BPF /proc/config.gz
zgrep AUDIT /proc/config.gz
cross compile
sudo apt-get install -y qemu-user-static
export ROOTFS=/media/foo/root
sudo cp /usr/bin/qemu-arm-static ${ROOTFS}/usr/bin/
sudo mv ${ROOTFS}/etc/ld.so.preload{,.bak}
sudo chroot ${ROOTFS} bash -c "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
sudo chroot ${ROOTFS} bash -c "cd /home/pi && cargo new foo"
sudo chroot ${ROOTFS} bash -c "cd foo && foo cargo build --release"
sudo mv ${ROOTFS}/etc/ld.so.preload{.bak,}
sudo rm ${ROOTFS}/usr/bin/qemu-arm-static
todo
- wasm
- cross compile