こんにちは、エンジニアの松崎 啓治(まつざき けいじ)です。
インターネット上ではこのIDで活動しています。 @futoase
先日、社内でエンジニア向けに「デバッガでRedisのコードを読んでみよう」というテーマの勉強会が開かれました。せっかくの機会なので、その内容をご紹介します。
デバッガでRedisのコードを追いかけるメリットとしては以下のようなものがあります。
- gdbを使ってRedisのコードをstep実行することで、どのタイミングでRedisのStorage(memory領域)からデータを取得できるのか体験から学べる
- Redisだけではなく、nginxやMySQL、PostgreSQLなどgdbを利用してstep実行を行えるものであれば、今回の勉強会の手法を元に同じように体験から学ぶことができる
デバッガで追いかけるための準備
プレゼン資料で取り上げられている環境はLinux (ディストリビューションはUbuntu)ですが、公開されているDocker imageを利用することで、docker for Macやdocker for Windowsなど、非Linux環境でもRedisのデバッグ体験を行うことが可能です。
Docker imageの準備
プレゼン資料を作成した浅羽により、Redisのデバッグ環境を行えるDocker imageを作成するためのDockerfileをGithubにて公開しています。
また、Docker imageについてDocker Hub上に公開しています。
docker コマンドを利用し、docker pullを行いましょう。
> docker pull futoase/redis-debug-4.0
docker imageの取得が終わったら、docker runコマンドでdocker containerを立ち上げましょう。
> docker run -it -p 9876:9876 \ --privileged --cap-add=SYS_PTRACE \ --security-opt seccomp=unconfined \ futoase/redis-debug-4.0:latest /bin/bash root@f76f0abef015:/#
docker runに渡している各種オプションは、ptrace システムコールを呼び出すために必要なものとなっています。1
Redis Serverを立ち上げる
早速、先程立ち上げたcontainer内で、Redis Serverを立ち上げましょう。
root@c2407feddaa5:/# cd /root/redis-4.0.11/src root@f76f0abef015:~/redis-4.0.11/src# root@01789e2f2a4e:~/redis-4.0.11/src# ./redis-server --port 9876 --protected-mode no --daemonize no 12:C 28 Sep 07:23:21.739 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 12:C 28 Sep 07:23:21.740 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=12, just started 12:C 28 Sep 07:23:21.740 # Configuration loaded _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.11 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 9876 | `-._ `._ / _.-' | PID: 12 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 12:M 28 Sep 07:23:21.745 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 12:M 28 Sep 07:23:21.745 # Server initialized 12:M 28 Sep 07:23:21.747 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled. 12:M 28 Sep 07:23:21.748 * Ready to accept connections
gdbを利用しRedis Serverに対してattachをする
Redis Serverに対してattachを行います。今回、gdbではなく、cgdb2を利用します。 今度は、docker execコマンドを利用し、先程立ち上げたdocker containerでcgdbを立ち上げます
> docker ps | grep redis-debug f76f0abef015 futoase/redis-debug-4.0:latest "/bin/bash" 11 minutes ago Up 11 minutes 0.0.0.0:9876->9876/tcp peaceful_chebyshev > docker exec --privileged -it f76f0abef015 /bin/bash > cgdb -p 12 # 12はredis-serverのprocess id で、先程redis-serverを起動したときにterminalで表示されたもの
cgdbの起動が終わると以下のようになります。
この状態で、プレゼンにあるsetCommandにbreak pointを貼ってみましょう。
(gdb) b setCommand Breakpoint 1 at 0x558b0346b63b: file t_string.c, line 98. (gdb) c Continuing.
合わせて、redis-clientを起動します。起動する対象は、docker containerを立ち上げているホストマシン、 例えばあなたがMacBookを利用して立ち上げているなら、そのMacBookのターミナル上で立ち上げます。
ターミナルにコマンドを打ち込み、docker container上のredis-serverに接続しましょう。
> redis-cli -p 9876 127.0.0.1:9876>
合わせて、SETコマンド 3を redis-clientから発行してみます。
127.0.0.1:9876> SET hoge 1234
この時、cgdb側で特定のbreakpointで処理が止まっている状態になります。
この時、next
コマンドや、step
コマンドを実行することで、next実行(関数レベル)、step実行(関数実行をネストして見る)ことが可能になります。
cgdbを利用し、関数の実行処理を追いかけることで、ソースコードリーディングに対し C言語未経験者でも読みといていくことが可能となります。
p
コマンドを利用することで、ランタイムで評価中の変数の値について確認することもできます。
ミドルウェアのソースコードを読む体験
MySQL、nginx及び今回のRedisなど、ミドルウェアのソースコードをgdbを利用し、 読む体験を行うことでどの処理にボトルネックがあるのか、どのタイミングでwhile loopに割り込みが入るのか など順を追っていくことができるようになります。このような体験を得るために、今回のように環境そのものをDocker image化し、 ソフトウェアインストールなどの設定をしなくても済むようにすることで、気軽に体験できる点がよかったです。