Debugging MySQL/MariaDB (4): Debugging with rr

重い腰を上げて Debugging MySQL/MariaDB シリーズの第4弾を書いている。今回は rr debugger を使用したデバッグについて説明する。Intel または AMD の CPU を搭載した物理マシンに Linux (kernel 3.11+) をネイティブインストールした環境を前提とする。

rr debugger

rr は Pernoscoによって開発されているデバッガーである。rr は gdb と同じ感覚で使えるが、次に挙げる gdb にはないすばらしい特徴を備えている。

  • プログラムの実行を記録し、バグを何度でも決定的に再現することができる。
  • ブレークポイントやウォッチポイントまで逆実行 (reverse execution) できる。

これらの特徴の何がすばらしいのか?想像してほしい。散発的なバグ(再現したりしなかったりするバグ)を GDBデバッグするのは苦痛である。ハズレの実行を引いてしまった場合、すべてが無駄になる。また、散発的でないバグであっても、手が滑って調べたい箇所を過ぎてしまうということはよくある。あるいは、ある値がどこで変数にセットされたか調べたいと思ったことはないだろうか?rr を使えば、これらの問題をすべて解決することができるのである。

Install rr

rr の公式ページ の説明にしたがって rr をインストールしよう。もし、比較的新しい CPU を使っているなら、ソースからビルドしてインストールするのを勧める。リリース版の rr が、その CPU のマイクロアーキテクチャを識別できない可能性があるためである。

Hardware/software configuration > OS configuration にしたがって OS の設定を変更する。AMD Ryzen を使っている場合は、更に こちらの設定 を施す必要がある。

Debug MariaDB with rr

まずデバッグビルドする。※ Performance schema と debug trace が有効だと、rr と組合せたときに遅くなるので無効にしているが、有効だと rr が使えないわけではない。

mkdir bld && cd $_
cmake .. -DCMAKE_BUILD_TYPE=Debug -DPLUGIN_PERFSCHEMA=NO -DWITH_DBUG_TRACE=OFF
cmake --build . --config Debug -j 8

デバッグしたいテストケースを指定して、--rr オプション付きで MTR を実行する。

./mysql-test/mtr innodb.alter_table --rr

すると rr のトレースファイルが生成され、今回の実行を何度でもデバッグできるようになる。rr replay でトレースを再生できる。

rr replay ./mysql-test/var/log/mysqld.1.rr/latest-trace
> rr replay ./mysql-test/var/log/mysqld.1.rr/latest-trace
...
(rr) b dispatch_command
Breakpoint 1 at 0x55f844a57dc6: file /home/nayuta_mariadb/repo/mariadb-server/10.9/sql/sql_parse.cc, line 1595.
(rr) c
...
Thread 2 hit Breakpoint 1, dispatch_command (command=COM_QUERY, thd=0x7f8c64001ba8, packet=0x7f8c6400c589 "SHOW SLAVE STATUS", packet_length=17, blocking=true) at /home/nayuta_mariadb/repo/mariadb-server/10.9/sql/sql_parse.cc:1595
1595    {
(rr) n
1596      NET *net= &thd->net;
(rr) n
1597      bool error= 0;
(rr) n
1598      bool do_end_of_statement= true;
(rr) n
1600      DBUG_PRINT("info", ("command: %d %s", command,
(rr) n
1604      bool drop_more_results= 0;
(rr) rn
1600      DBUG_PRINT("info", ("command: %d %s", command,
(rr) watch error
Hardware watchpoint 2: error
(rr) rc
Continuing.

Thread 2 hit Hardware watchpoint 2: error

Old value = false
New value = 85
dispatch_command (command=COM_QUERY, thd=0x7f8c64001ba8, packet=0x7f8c6400c589 "SHOW SLAVE STATUS", packet_length=17, blocking=true) at /home/nayuta_mariadb/repo/mariadb-server/10.9/sql/sql_parse.cc:1597
1597      bool error= 0

上の実行例からわかるように、rn (reverse-next) で1ステップ戻ることができ、rc (reverse-continue) で逆向きに continue することができる。watch 等も gdb と同様に機能する。この便利さがわかるだろうか?私の説明で便利さが伝わなかったとしても、騙されたと思って一度使ってみてほしい。デバッグの効率が数倍上がるはずだ。

Debug MySQL with rr

私の知る限り MySQLMTR--rr オプションをサポートしていないが、次のようにしてトレースを生成することができる。

(まだ書いてない)

まとめ

MySQL/MariaDB の開発をするなら、絶対 rr を使った方がいい。