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を避けて試します。)
次のような環境です。
1 2 3 4 5 6 7 8 9 10 11 12 |
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.6 LTS Release: 20.04 Codename: focal $ uname -r 5.4.0-155-generic $ perf --version perf version 5.4.235 $ |
試してないですが 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 だと参照できないローカル変数があったので、受け取っている公開鍵の中身を確認するために
1 2 |
$ sudo apt install libssl1.1-dbgsym |
しておきます。
後ほど出てくる perf probe -L
を試したい場合は
1 2 |
$ apt-get source libssl1.1 |
してください。
perf のインストール
perf probe --add
したいので
1 2 |
$ sudo apt install linux-tools-common linux-tools-`uname -r` |
します。
probe point の追加
このあたりを見ると、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
$ sudo perf probe -s "./php7.4-7.4.3" -x /usr/lib/php/20190902/mysqlnd.so -L mysqlnd_caching_sha2_get_key <mysqlnd_caching_sha2_get_key@./php7.4-7.4.3/./ext/mysqlnd/mysqlnd_auth.c:0> 0 mysqlnd_caching_sha2_get_key(MYSQLND_CONN_DATA *conn) { 2 RSA * ret = NULL; 3 const MYSQLND_PFC_DATA * const pfc_data = conn->protocol_frame_codec->data; 4 const char * fname = (pfc_data->sha256_server_public_key && pfc_data->sha256_server_public_key[0] != '\0')? pfc_data->sha256_server_public_key: MYSQLND_G(sha256_server_public_key); 7 php_stream * stream; 8 DBG_ENTER("mysqlnd_cached_sha2_get_key"); 9 DBG_INF_FMT("options_s256_pk=[%s] MYSQLND_G(sha256_server_public_key)=[%s]", pfc_data->sha256_server_public_key? pfc_data->sha256_server_public_key:"n/a", MYSQLND_G(sha256_server_public_key)? MYSQLND_G(sha256_server_public_key):"n/a"); 12 if (!fname || fname[0] == '\0') { 13 MYSQLND_PACKET_CACHED_SHA2_RESULT req_packet; 14 MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE pk_resp_packet; 16 do { 17 DBG_INF("requesting the public key from the server"); 18 conn->payload_decoder_factory->m.init_cached_sha2_result_packet(&req_packet); 19 conn->payload_decoder_factory->m.init_sha256_pk_request_response_packet(&pk_resp_packet); 20 req_packet.request = 1; 22 if (! PACKET_WRITE(conn, &req_packet)) { 23 DBG_ERR_FMT("Error while sending public key request packet"); 24 php_error(E_WARNING, "Error while sending public key request packet. PID=%d", getpid()); SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT); break; } 28 if (FAIL == PACKET_READ(conn, &pk_resp_packet) || NULL == pk_resp_packet.public_key) { 29 DBG_ERR_FMT("Error while receiving public key"); 30 php_error(E_WARNING, "Error while receiving public key. PID=%d", getpid()); 31 SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT); 32 break; } 34 DBG_INF_FMT("Public key(%d):\n%s", pk_resp_packet.public_key_len, pk_resp_packet.public_key); /* now extract the public key */ { 37 BIO * bio = BIO_new_mem_buf(pk_resp_packet.public_key, pk_resp_packet.public_key_len); 38 ret = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); 39 BIO_free(bio); } 41 } while (0); 42 PACKET_FREE(&req_packet); 43 PACKET_FREE(&pk_resp_packet); 45 DBG_INF_FMT("ret=%p", ret); 46 DBG_RETURN(ret); |
「mysqlnd_caching_sha2_get_key:38 あたりで pk_resp_packet.public_key を参照できれば良いのでは?」と思いますが、この近辺でAvailable variablesは
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
$ sudo perf probe -s "`pwd`/php7.4-7.4.3/ext/mysqlnd/" -x /usr/lib/php/20190902/mysqlnd.so --vars=mysqlnd_caching_sha2_get_key:37 Available variables at mysqlnd_caching_sha2_get_key:37 @<mysqlnd_handle_local_infile.cold+142> MYSQLND_CONN_DATA* conn $ sudo perf probe -s "`pwd`/php7.4-7.4.3/ext/mysqlnd/" -x /usr/lib/php/20190902/mysqlnd.so --add="mysqlnd_caching_sha2_get_key:37 pk_resp_packet.public_key" Sorry, we don't support this variable location yet. Error: Failed to add events. $ sudo perf probe -s "`pwd`/php7.4-7.4.3/ext/mysqlnd/" -x /usr/lib/php/20190902/mysqlnd.so --vars=mysqlnd_caching_sha2_get_key:38 Available variables at mysqlnd_caching_sha2_get_key:38 @<mysqlnd_handle_local_infile.cold+162> BIO* bio MYSQLND_CONN_DATA* conn $ sudo perf probe -s "`pwd`/php7.4-7.4.3/ext/mysqlnd/" -x /usr/lib/php/20190902/mysqlnd.so --add="mysqlnd_caching_sha2_get_key:38 pk_resp_packet.public_key" Sorry, we don't support this variable location yet. Error: Failed to add events. $ sudo perf probe -s "`pwd`/php7.4-7.4.3/ext/mysqlnd/" -x /usr/lib/php/20190902/mysqlnd.so --vars=mysqlnd_caching_sha2_get_key:39 Available variables at mysqlnd_caching_sha2_get_key:39 @<mysqlnd_mysqlnd_vio_enable_ssl_pub.cold+2> MYSQLND_CONN_DATA* conn RSA* ret $ sudo perf probe -s "`pwd`/php7.4-7.4.3/ext/mysqlnd/" -x /usr/lib/php/20190902/mysqlnd.so --add="mysqlnd_caching_sha2_get_key:39 pk_resp_packet.public_key" Sorry, we don't support this variable location yet. Error: Failed to add events. $ |
となり、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はいろいろローカル変数を参照できそうなので
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
$ sudo perf probe -s "./openssl-1.1.1f" -x /lib/x86_64-linux-gnu/libcrypto.so.1.1 -L BIO_new_mem_buf <BIO_new_mem_buf@./openssl-1.1.1f//crypto/bio/bss_mem.c:0> 0 BIO *BIO_new_mem_buf(const void *buf, int len) 1 { 2 BIO *ret; 3 BUF_MEM *b; 4 BIO_BUF_MEM *bb; 5 size_t sz; 7 if (buf == NULL) { 8 BIOerr(BIO_F_BIO_NEW_MEM_BUF, BIO_R_NULL_PARAMETER); 9 return NULL; } 11 sz = (len < 0) ? strlen(buf) : (size_t)len; 12 if ((ret = BIO_new(BIO_s_mem())) == NULL) return NULL; 14 bb = (BIO_BUF_MEM *)ret->ptr; 15 b = bb->buf; /* Cast away const and trust in the MEM_RDONLY flag. */ 17 b->data = (void *)buf; 18 b->length = sz; 19 b->max = sz; 20 *bb->readp = *bb->buf; 21 ret->flags |= BIO_FLAGS_MEM_RDONLY; /* Since this is static data retrying won't help */ 23 ret->num = 0; 24 return ret; } static int mem_init(BIO *bi, unsigned long flags) $ sudo perf probe -s "./openssl-1.1.1f" -x /lib/x86_64-linux-gnu/libcrypto.so.1.1 --vars=BIO_new_mem_buf Available variables at BIO_new_mem_buf @<BIO_new_mem_buf+0> (unknown_type buf int len $ sudo perf probe -s "./openssl-1.1.1f" -x /lib/x86_64-linux-gnu/libcrypto.so.1.1 --vars=BIO_new_mem_buf:18 Available variables at BIO_new_mem_buf:18 @<BIO_new_mem_buf+66> (unknown_type buf BIO* ret BIO_BUF_MEM* bb BUF_MEM* b size_t sz $ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
$ sudo perf probe --list $ sudo perf probe -x /usr/lib/php/20190902/mysqlnd.so -a mysqlnd_caching_sha2_get_key Added new event: probe_mysqlnd:mysqlnd_caching_sha2_get_key (on mysqlnd_caching_sha2_get_key in /usr/lib/php/20190902/mysqlnd.so) You can now use it in all perf tools, such as: perf record -e probe_mysqlnd:mysqlnd_caching_sha2_get_key -aR sleep 1 $ sudo perf probe -x /lib/x86_64-linux-gnu/libcrypto.so.1.1 -a "BIO_new_mem_buf:18 b->data:string" Added new event: probe_libcrypto:BIO_new_mem_buf (on BIO_new_mem_buf:18 in /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 with data=b->data:string) You can now use it in all perf tools, such as: perf record -e probe_libcrypto:BIO_new_mem_buf -aR sleep 1 $ sudo perf probe --list probe_libcrypto:BIO_new_mem_buf (on BIO_new_mem_buf:18@../crypto/bio/bss_mem.c in /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 with data) probe_mysqlnd:mysqlnd_caching_sha2_get_key (on mysqlnd_caching_sha2_handle_server_response:48@./ext/mysqlnd/mysqlnd_auth.c in /usr/lib/php/20190902/mysqlnd.so) $ sudo cat /sys/kernel/debug/tracing/uprobe_events p:probe_mysqlnd/mysqlnd_caching_sha2_get_key /usr/lib/php/20190902/mysqlnd.so:0x000000000001f1c0 p:probe_libcrypto/BIO_new_mem_buf /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1:0x00000000000ae062 data=+0(+8(%dx)):string $ |
としました。
トレースしてみる
/sys/kernel/debug/tracing/uprobe_events
に probe point が追加されたら、あとは
perf trace
や perf record
で確認できます。
perf trace
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ sudo perf trace -e probe_mysqlnd:mysqlnd_caching_sha2_get_key -e probe_libcrypto:BIO_new_mem_buf php test.php Client library version: mysqlnd 7.4.3-4ubuntu2.19 0.000 php/35274 probe_mysqlnd:mysqlnd_caching_sha2_get_key:(7fb760cee1c0) 0.057 php/35274 probe_libcrypto:BIO_new_mem_buf:(7fb76306b062) data="-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsBtJzVsRaEjjIn8vM4kz FMI6RYxXCjpOE6nrXlpmeB9KJcNZPuhuhmIWaQgKQnfrk+PBC862CyXBFeVxPzOn dzRwXBKM540HMU3WGFouVIKwFmnTl0L6J0EEWE1jWab3I30M4Di7rYRdyuEUSKky 2jaa7R/jtX+x286Z4Gci2gcTU3WkOlyAZlRJPgNwcb6oN6tSKix3zxeQYdUKw2n3 Y3iNw+zr4TRfJ6AQJqei+hm4dnMuibg9JtwmJ1VbS1kfctEg3y3ASsSPbWkOadU7 3/pGsp+orIGaMrO0cQhKJEK2ibrAx7AmsbzL1+dlZVrfkxVvSXxK6708kBjAc4dK WQIDAQAB -----END PUBLIC KEY----- " PHP7.4.3-4ubuntu2.19 Ssl_cipher () sejima (caching_sha2_password) $ |
perf record & perf script
1 2 |
$ sudo perf record -e probe_mysqlnd:mysqlnd_caching_sha2_get_key -e probe_libcrypto:BIO_new_mem_buf -aR sleep 10 |
のように、 perf record 叩いてる裏で
1 2 3 4 5 6 7 |
$ php test.php Client library version: mysqlnd 7.4.3-4ubuntu2.19 PHP7.4.3-4ubuntu2.19 Ssl_cipher () sejima (caching_sha2_password) $ |
すると
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ sudo perf record -e probe_mysqlnd:mysqlnd_caching_sha2_get_key -e probe_libcrypto:BIO_new_mem_buf -aR sleep 10 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.294 MB perf.data (2 samples) ] $ sudo perf script php 35325 [013] 17824.257241: probe_mysqlnd:mysqlnd_caching_sha2_get_key: (7ff2da4e71c0) php 35325 [013] 17824.257289: probe_libcrypto:BIO_new_mem_buf: (7ff2dc864062) data="-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsBtJzVsRaEjjIn8vM4kz FMI6RYxXCjpOE6nrXlpmeB9KJcNZPuhuhmIWaQgKQnfrk+PBC862CyXBFeVxPzOn dzRwXBKM540HMU3WGFouVIKwFmnTl0L6J0EEWE1jWab3I30M4Di7rYRdyuEUSKky 2jaa7R/jtX+x286Z4Gci2gcTU3WkOlyAZlRJPgNwcb6oN6tSKix3zxeQYdUKw2n3 Y3iNw+zr4TRfJ6AQJqei+hm4dnMuibg9JtwmJ1VbS1kfctEg3y3ASsSPbWkOadU7 3/pGsp+orIGaMrO0cQhKJEK2ibrAx7AmsbzL1+dlZVrfkxVvSXxK6708kBjAc4dK WQIDAQAB -----END PUBLIC KEY----- " $ |
こうなります。
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"
あたりの振る舞いは、次のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.6 LTS Release: 20.04 Codename: focal $ uname -r 5.15.0-78-generic $ perf --version perf version 5.15.99 $ sudo perf probe -x /usr/lib/php/20190902/mysqlnd.so -a mysqlnd_caching_sha2_get_key Added new event: probe_mysqlnd:mysqlnd_caching_sha2_get_key (on mysqlnd_caching_sha2_get_key in /usr/lib/php/20190902/mysqlnd.so) You can now use it in all perf tools, such as: perf record -e probe_mysqlnd:mysqlnd_caching_sha2_get_key -aR sleep 1 $ sudo perf probe -x /lib/x86_64-linux-gnu/libcrypto.so.1.1 -a "BIO_new_mem_buf:18 b->data:string" Added new event: probe_libcrypto:BIO_new_mem_buf_L18 (on BIO_new_mem_buf:18 in /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 with data=b->data:string) You can now use it in all perf tools, such as: perf record -e probe_libcrypto:BIO_new_mem_buf_L18 -aR sleep 1 $ sudo perf probe --list probe_libcrypto:BIO_new_mem_buf_L18 (on BIO_new_mem_buf:18@../crypto/bio/bss_mem.c in /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 with data) probe_mysqlnd:mysqlnd_caching_sha2_get_key (on mysqlnd_caching_sha2_handle_server_response:48@./ext/mysqlnd/mysqlnd_auth.c in /usr/lib/php/20190902/mysqlnd.so) $ sudo cat /sys/kernel/debug/tracing/uprobe_events p:probe_mysqlnd/mysqlnd_caching_sha2_get_key /usr/lib/php/20190902/mysqlnd.so:0x000000000001f1c0 p:probe_libcrypto/BIO_new_mem_buf_L18 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1:0x00000000000ae062 data=+0(+8(%dx)):string $ sudo perf trace -e probe_mysqlnd:mysqlnd_caching_sha2_get_key -e probe_libcrypto:BIO_new_mem_buf_L18 php test.php Client library version: mysqlnd 7.4.3-4ubuntu2.19 0.000 php/35253 probe_mysqlnd:mysqlnd_caching_sha2_get_key(__probe_ip: 140319895433664) 0.055 php/35253 probe_libcrypto:BIO_new_mem_buf_L18(__probe_ip: 140319932645474, data: "-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzYf/ww9KSyIKOfSwRlLL EVrZos0/bYIbQpPAR9gW3IaBeo/+ncWNg7c/HXvDYTqHRYtcb7sC0FCKefWp1rZv ZTKPR/3Lapam2NasA7GlR+7QLvaU1FEaa2nyTpPrIsALdhJTpwUY2/EXiVfCkPq9 SM5S57HSwA+6r83pAdnPOZcLeu5k5NJ3A1Ggqxz3d7vlOfjcL2JGEmldoBBFGHv8 FVMYzPGEdFfPlAJpZjwU9GkQhI9Bf4ePlWKwvaO1woaCX1N2bmlo19qX1w/4FrnG eQZgpAgYg/nRG5ZKo/xcmVstZxWHOjLdWHutEyChgqfubGqH+Qr3sDymnYMl1KAH UQIDAQAB -----END PUBLIC KEY----- ") PHP7.4.3-4ubuntu2.19 Ssl_cipher () sejima (caching_sha2_password) $ |
uprobe周りはまだまだ発展途上なのでしょう。
bpftraceとperf probeでの違いなど
bpftrace でも軽く確認しておきましょう。
1 2 3 |
$ sudo perf probe --list $ sudo cat /sys/kernel/debug/tracing/uprobe_events |
にした状態でも
1 2 |
$ sudo bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libcrypto.so.1.1:BIO_new_mem_buf {printf("%s\n", str(arg0));}' |
とattachすれば、その裏で
1 2 3 4 5 6 7 |
$ php test.php Client library version: mysqlnd 7.4.3-4ubuntu2.19 PHP7.4.3-4ubuntu2.19 Ssl_cipher () sejima (caching_sha2_password) $ |
叩くと
1 2 3 4 5 |
$ sudo bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libcrypto.so.1.1:BIO_new_mem_buf {printf("%s\n", str(arg0));}' Attaching 1 probe... -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB |
となります。
bpftrace の場合、 Debug Symbol Package が必要になるケースとならないケースがあるようです。
例えば、php7.4-mysql-dbgsymがないと、 mysqlnd_caching_sha2_handle_server_response
に attach できませんが
1 2 3 4 5 6 7 |
$ dpkg --list | grep dbgsym ii ubuntu-dbgsym-keyring 2020.02.11.4 all GnuPG keys of the Ubuntu Debug Symbols Archive $ sudo bpftrace -e 'uprobe:/usr/lib/php/20190902/mysqlnd.so:mysqlnd_caching_sha2_handle_server_response {printf("in here\n");}' Attaching 1 probe... Could not resolve symbol: /usr/lib/php/20190902/mysqlnd.so:mysqlnd_caching_sha2_handle_server_response $ |
1 2 3 4 5 6 |
$ dpkg --list | grep dbgsym ii php7.4-mysql-dbgsym 7.4.3-4ubuntu2.19 amd64 debug symbols for php7.4-mysql ii ubuntu-dbgsym-keyring 2020.02.11.4 all GnuPG keys of the Ubuntu Debug Symbols Archive $ sudo bpftrace -e 'uprobe:/usr/lib/php/20190902/mysqlnd.so:mysqlnd_caching_sha2_handle_server_response {printf("in here\n");}' Attaching 1 probe... |
libssl1.1-dbgsym がなくても、BIO_new_mem_bufには attach できました。
1 2 3 4 5 |
$ dpkg --list | grep dbgsym ii ubuntu-dbgsym-keyring 2020.02.11.4 all GnuPG keys of the Ubuntu Debug Symbols Archive $ sudo bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libcrypto.so.1.1:BIO_new_mem_buf {printf("%s\n", str(arg0));}' Attaching 1 probe... |
bpftrace は、 Debug Symbol Pacakge で参照可能な関数であれば、普通に uprobe:library_name:function_name で指定して参照できそうです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ for i in `dpkg --listfiles php7.4-mysql-dbgsym | egrep 'debug.*.debug$'`; do echo $i; nm $i| grep mysqlnd_caching; done /usr/lib/debug/.build-id/51/a798010eb2b791dd48b9b48cc0b3c1efaaeb7d.debug /usr/lib/debug/.build-id/c9/fc402edcfb6c0a1ec9ca9419261d519adf946d.debug /usr/lib/debug/.build-id/df/abca62a7f18b6a8295e84bc0404465798386ba.debug 000000000003f920 d mysqlnd_caching_sha2_auth_plugin 0000000000020a80 t mysqlnd_caching_sha2_get_auth_data 000000000001f700 t mysqlnd_caching_sha2_get_auth_data.part.0 000000000001f6b0 t mysqlnd_caching_sha2_handle_server_response 000000000001f0e0 t mysqlnd_caching_sha2_handle_server_response.part.0 000000000000f5e4 t mysqlnd_caching_sha2_handle_server_response.part.0.cold $ sudo bpftrace -e 'uprobe:/usr/lib/php/20190902/mysqlnd.so:mysqlnd_caching_sha2_handle_server_response {printf("in here\n");}' Attaching 1 probe... ^C $ |
一方、 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
といった指定であれば可能なようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ dpkg --list | grep dbgsym ii ubuntu-dbgsym-keyring 2020.02.11.4 all GnuPG keys of the Ubuntu Debug Symbols Archive $ sudo perf probe -x /lib/x86_64-linux-gnu/libcrypto.so.1.1 -a BIO_new_mem_buf Added new event: probe_libcrypto:BIO_new_mem_buf (on BIO_new_mem_buf in /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1) You can now use it in all perf tools, such as: perf record -e probe_libcrypto:BIO_new_mem_buf -aR sleep 1 $ sudo perf probe -x /lib/x86_64-linux-gnu/libcrypto.so.1.1 -d BIO_new_mem_buf Removed event: probe_libcrypto:BIO_new_mem_buf $ sudo perf probe -x /lib/x86_64-linux-gnu/libcrypto.so.1.1 -a "BIO_new_mem_buf:18 b->data:string" The /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 file has no debug information. Rebuild with -g, or install an appropriate debuginfo package. Error: Failed to add events. $ |
また、Debug Symbol Package さえあれば、mysqlnd.soにmysqlnd_caching_sha2_get_keyで probe point を追加したりもできます。
1 2 3 4 5 6 7 8 9 10 |
$ sudo perf probe -x /usr/lib/php/20190902/mysqlnd.so -a mysqlnd_caching_sha2_get_key Added new event: probe_mysqlnd:mysqlnd_caching_sha2_get_key (on mysqlnd_caching_sha2_get_key in /usr/lib/php/20190902/mysqlnd.so) You can now use it in all perf tools, such as: perf record -e probe_mysqlnd:mysqlnd_caching_sha2_get_key -aR sleep 1 $ |
bpftrace や perf probe は、Debug Symbol Package の有無でできることがけっこう変わってきます。
おまけ・その1:MySQL公式のdbgsymなパッケージ
最後におまけで、少しはMySQLらしいことをやってみましょう。
MySQL Community Server の公式パッケージを見ると、UbuntuやDebianではdbgsymなパッケージが公開されていますから、これを使ってbpftraceできます。今回は perf buildid-cache
を使って少し応用的なやりかたで試してみます。
素直に dpkg --install
でパッケージをインストールしても良いんですが、サクッと試すだけなら、*.debはtarで展開することもできます。
1 2 3 4 5 6 7 8 |
$ ar x mysql-community-server-core_8.0.34-1ubuntu20.04_amd64.deb $ tar xf data.tar.xz $ ar x mysql-community-server-core-dbgsym_8.0.34-1ubuntu20.04_amd64.deb $ tar xf data.tar.xz $ ls usr/sbin/mysqld usr/sbin/mysqld $ |
perf buildid-cache --add
で debuginfo を追加します。
1 2 |
$ for i in `find usr/ -type f | egrep 'debug.*.debug$'`; do sudo perf buildid-cache --add=$i; done |
とりあえず、nmでシンボルを調べつつ、 mysqld_main に attach してみましょう。
1 2 3 4 5 6 |
$ for i in `find usr/ -type f | egrep 'debug.*.debug$'`; do nm $i| grep mysqld_main; done 000000000102f910 T _Z11mysqld_mainiPPc 0000000000eee11a t _Z11mysqld_mainiPPc.cold $ sudo bpftrace -e 'uprobe:usr/sbin/mysqld:_Z11mysqld_mainiPPc { join(arg1);}' Attaching 1 probe... |
その裏で、 usr/sbin/mysqld に適当な引数を渡しつつ起動してみると
1 2 |
$ usr/sbin/mysqld test1 test2 |
こうなります。
1 2 3 4 |
$ sudo bpftrace -e 'uprobe:usr/sbin/mysqld:_Z11mysqld_mainiPPc { join(arg1);}' Attaching 1 probe... usr/sbin/mysqld test1 test2 |
perf buildid-cache --add した debuginfo は、 perf buildid-cache --remove で削除できます。
1 2 |
$ for i in `find usr/ -type f | egrep 'debug.*.debug$'`; do sudo perf buildid-cache --remove=$i; done |
probe point を設定したい関数があるなら、次のように nm でシンボルを調べれば、bpftraceでattachできます。例えばdo_commandやdispatch_commandであれば
1 2 3 4 5 6 7 |
$ for i in `find usr/ -type f | egrep 'debug.*.debug$'`; do nm $i| grep do_command; done 00000000011477e0 T _Z10do_commandP3THD $ for i in `find usr/ -type f | egrep 'debug.*.debug$'`; do nm $i| grep dispatch_command; done 00000000011447e0 T _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command 0000000000ef5610 t _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command.cold $ |
dispatch_commandの引数である enum enum_server_command command
を表示するだけなら
1 2 |
$ sudo bpftrace -e 'uprobe:/usr/sbin/mysqld:_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command {printf("%d\n", arg2);}' |
して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し、
1 |
$ sudo apt update && sudo apt install bpftrace mysql-community-server mysql-community-server-core-dbgsym |
して
1 |
$ sudo -u mysql /usr/sbin/mysqld & |
などすれば、
1 2 3 4 5 6 7 8 |
$ uname -a Linux G-PC-01112515 5.15.90.1-microsoft-standard-WSL2 #1 SMP Fri Jan 27 02:56:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux $ sudo bpftrace -l 'uprobe:/usr/sbin/mysqld:*dispatch_command*' uprobe:/usr/sbin/mysqld:_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command uprobe:/usr/sbin/mysqld:_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command.cold $ sudo bpftrace -e 'uprobe:/usr/sbin/mysqld:_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command {printf("%d\n", arg2);}' Attaching 1 probe... |
して attach することが可能です。
おわりに
bpftraceやperf probe、gdbは、状況次第で使い分けかなぁという印象を受けました。
例えば、「手元の環境でgdbで当たりをつけてから実環境で bpftrace、必要であれば Debug Symbol Packageいれて probe point を設定してみる」みたいな使い分けでしょうか。試行錯誤してみるならgdbの方が融通が効いて、サクッと試すなら bpftrace、もっと情報が必要になれば perf probe --add かなという気がしました。
何はともあれ、こういった引き出しというか選択肢が複数あるのは、良いことだと思います。
次回はMySQL8.1.0のデバッグビルドなどについて、ゆるふわで書こうと思います。
References
- perf-probe(1) — Linux manual page
- perf-buildid-cache(1) — Linux manual page
- Uprobe-tracer: Uprobe-based Event Tracing