LoginSignup
136
111

Rust のデバッグチートシート

Last updated at Posted at 2020-02-22

Rust Debugging Cheatsheet

開発の基本

エディタ

フォーマッタ

Cargo.toml もフォーマットしたい場合 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 で便利

依存関係を最新に保つ

コードカバレッジ

コードメトリクス解析

ビルドが遅いとき

コンパイルが遅いとき

リンクが遅いとき

ビルドキャッシュの管理

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 でバージョンを固定するか

最適化

バイナリサイズ最適化

Rust のデバッグ

dbg マクロ

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

変数の型名を文字列で取得

fn type_of<T>(_: T) -> &'static str {
    std::any::type_name::<T>()
}

大量のエラーの潰し方

  • cargo clippy --tests --examples 2>&1 | head -n 40 で最初のエラーから順番に潰すこと
  • 最後のエラーから見ても何もわからないので

コンパイラエラーコードの索引

マクロ展開

error-chain のような巨大なマクロが何をしているのか見るのに便利

  • cargo expand - 全部展開
  • cargo expand -- main - 特定の関数だけ展開

AST の確認

依存クレートのデバッグ

  • doc.rs をよくよく読む
    • [src] ボタンで定義のソースコードが見ることができるので実装もよく読む
  • github などの issues を検索してよくよく調べる
  • 各クレートの issues, discord, gitter, slack, irc で訊いてみる

なんもわからん・・・てなったとき

その他有用な rust の情報源

rust のテスト

基本

非同期テスト

tokio の場合

#[tokio::test]
async fn hoge_test(){

}

actix の場合(actix-web を使っているときにはこちら)

#[actix_rt::test]
async fn hoge_test(){

}

実行バイナリ解析

おすすめの書籍

シンボル解析

  • 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

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

実行中プロセスのメモリ解析(コアダンプ取得)

メモリリーク解析

ネットワーク

パケット統計

  • 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

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 パケットキャプチャ

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 カードの調査

dd で作った img を gparted で小さくして sd カードに焼き込み

$ 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

監視する

ファイルを監視する

ブートログ、カーネルログ、システムログ

カーネルコンフィグの確認

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
136
111
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
136
111