Tracing mysqlnd.so and mysqld (with perf and bpftrace)

こんにちわ。せじまです。

現代人はgdbよりbpftraceを好むという電波を受信したので、Debugging mysqlnd.so ~mysql_native_passwordが廃止される未来に備えて~のようなことをbpftraceなどで確認したいならどうしたら良いか、試してみます。

はじめに

身も蓋もないですが、こういった用途ではgdbの方が融通がきくと個人的には思いました。uprobeだと、ユーザ空間のローカル変数で、参照できるものとできないものがまちまちだったので、今回は「libcryptoの関数に渡してる引数を見ればいいだろう」という発想に至りました。そこまで至るのにも時間を要するので、こういう選択肢もあるという感覚で読んでいただければと思います。

でははじめます。

perf probe や bpftrace を試す環境など

WSL2だと sudo ls /sys/kernel/debug/tracing が No such file or directory なので、今回は物理サーバ上の Ubuntu 20.04 LTS で試しました。(WSL2上でもbpftraceでmysqldをトレースしたりできるんですが、 perfについてはいったんWSL2を避けて試します。)

次のような環境です。

試してないですが VM でもいけるかもしれません。(自宅で私物のWindowsマシン上でHyper-VでUbuntu 22.04 LTSいれて試した限りでは、 bpftrace で mysqld をトレースするとかは問題なく動きました。)

テスト環境のMySQL、PHPの検証用スクリプト、PHPのインストール、(PHPのソースコードをインストール)、Debug Symbol Package

このあたりは前回の記事を参照してください。
今回はPHPのソースコードをインストールしなくてもいいんですが、後ほど出てくる perf probe -L を試したいならソースコードも必要になります。

libssl1.1-dbgsym のインストール

詳しくは後述しますが、 mysqlnd.so だと参照できないローカル変数があったので、受け取っている公開鍵の中身を確認するために

しておきます。

後ほど出てくる perf probe -L を試したい場合は

してください。

perf のインストール

perf probe --add したいので

します。

probe point の追加

このあたりを見ると、

「mysqlnd_caching_sha2_get_key:38 あたりで pk_resp_packet.public_key を参照できれば良いのでは?」と思いますが、この近辺でAvailable variablesは

となり、pk_resp_packet.public_key は参照できません。そこで、代替案として

https://github.com/php/php-src/blob/php-7.4.3/ext/mysqlnd/mysqlnd_auth.c#L939-L976

mysqlnd_caching_sha2_get_key() が呼ばれた後、 BIO_new_mem_buf()に渡されている pk_resp_packet.public_key を参照することにします。

幸い、BIO_new_mem_bufはいろいろローカル変数を参照できそうなので

としました。

トレースしてみる

/sys/kernel/debug/tracing/uprobe_events に probe point が追加されたら、あとは 
perf traceperf record で確認できます。

perf trace

perf record & perf script

のように、 perf record 叩いてる裏で

すると

こうなります。

perf probe --add について補足

Ubuntu 20.04 LTS でも、 kernel 5.4 と kernel 5.15 で perf probe --add の振る舞いが異なりました。
例えば、 sudo perf probe -x /lib/x86_64-linux-gnu/libcrypto.so.1.1 -a "BIO_new_mem_buf:18 b->data:string" あたりの振る舞いは、次のようになります。

uprobe周りはまだまだ発展途上なのでしょう。

bpftraceとperf probeでの違いなど

bpftrace でも軽く確認しておきましょう。

にした状態でも

とattachすれば、その裏で

叩くと

となります。
bpftrace の場合、 Debug Symbol Package が必要になるケースとならないケースがあるようです。

例えば、php7.4-mysql-dbgsymがないと、 mysqlnd_caching_sha2_handle_server_response に attach できませんが

libssl1.1-dbgsym がなくても、BIO_new_mem_bufには attach できました。

bpftrace は、 Debug Symbol Pacakge で参照可能な関数であれば、普通に uprobe:library_name:function_name で指定して参照できそうです。

一方、 perf probe --add で見てみると、Debug Symbol Package があれば、-a "BIO_new_mem_buf:18 b->data:string" というような指定もできます。
Debug Symbol Package がなければ -a "BIO_new_mem_buf:18 b->data:string" といった指定はできませんが、 -a BIO_new_mem_buf といった指定であれば可能なようです。

また、Debug Symbol Package さえあれば、mysqlnd.soにmysqlnd_caching_sha2_get_keyで probe point を追加したりもできます。

bpftrace や perf probe は、Debug Symbol Package の有無でできることがけっこう変わってきます。

おまけ・その1:MySQL公式のdbgsymなパッケージ

最後におまけで、少しはMySQLらしいことをやってみましょう。
MySQL Community Server の公式パッケージを見ると、UbuntuやDebianではdbgsymなパッケージが公開されていますから、これを使ってbpftraceできます。今回は perf buildid-cache を使って少し応用的なやりかたで試してみます。

素直に dpkg --install でパッケージをインストールしても良いんですが、サクッと試すだけなら、*.debはtarで展開することもできます。

perf buildid-cache --add で debuginfo を追加します。

とりあえず、nmでシンボルを調べつつ、 mysqld_main に attach してみましょう。

その裏で、 usr/sbin/mysqld に適当な引数を渡しつつ起動してみると

こうなります。

perf buildid-cache --add した debuginfo は、 perf buildid-cache --remove で削除できます。

probe point を設定したい関数があるなら、次のように nm でシンボルを調べれば、bpftraceでattachできます。例えばdo_commanddispatch_commandであれば

dispatch_commandの引数である enum enum_server_command command を表示するだけなら

してmysqldadmin pingすれば、COM_PINGの14が表示されます。このあたりから試してみるとお手軽かもしれませんね。

おまけ・その2: bpftrace on WSL2

ちなみに、 sudo bpftrace -e 'uprobe:/usr/sbin/mysqld:_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command {printf("%d\n", arg2);}' くらいであれば、WSL2上で試すこともできます。

Ubuntu 22.04 LTS on WSL2 でやってみました。

https://dev.mysql.com/downloads/repo/apt/ から mysql-apt-config*.debをダウンロードして dpkg --installし、

して

などすれば、

して attach することが可能です。

おわりに

bpftraceやperf probe、gdbは、状況次第で使い分けかなぁという印象を受けました。
例えば、「手元の環境でgdbで当たりをつけてから実環境で bpftrace、必要であれば Debug Symbol Packageいれて probe point を設定してみる」みたいな使い分けでしょうか。試行錯誤してみるならgdbの方が融通が効いて、サクッと試すなら bpftrace、もっと情報が必要になれば perf probe --add かなという気がしました。
何はともあれ、こういった引き出しというか選択肢が複数あるのは、良いことだと思います。

次回はMySQL8.1.0のデバッグビルドなどについて、ゆるふわで書こうと思います。

References