前回から気づけばだいぶ経過してましたが
実際phpスクリプトが動いてる環境はmod-phpであったりphp-fpmであったりするので、そのあたりは結局動かせておらず
このあたりも試してみたのでいちおう記録として。 個人的な環境のアレによりphp-fpmの方はubuntu bionic、mod-phpの方はfocalで動かしたがまあ特にやることは変わらないはず。
結局動かすためにはプロセスに対しUSE_ZEND_DTRACE=1環境変数をセットすることが必要で、「どこから仕込むことができるか?」ということだけですね、ということで基本はsystemdの設定を上書きする方法でトレースは実行できました。
環境は上述の通りubuntu前提ですがsystemdレイヤーでやっているので、他の環境でも同様にできはするかなと(ubuntu以外で--enable-dtraceされてるバイナリが提供されてるのかどうかは定かではないですが・・
php-fpm
systemdの設定を上書きするunitファイルを仕込んでみます。
sudo cp /lib/systemd/system/php7.2-fpm.service /etc/systemd/system/php7.2-fpm.service
して /etc/systemd/system/php7.2-fpm.service
に Environment="USE_ZEND_DTRACE=1"
を追加して設定反映、再起動すればOK
$ cat /etc/systemd/system/php7.2-fpm.service [Unit] Description=The PHP 7.2 FastCGI Process Manager customized Documentation=man:php-fpm7.2(8) After=network.target [Service] Type=notify PIDFile=/run/php/php7.2-fpm.pid ExecStart=/usr/sbin/php-fpm7.2 --nodaemonize --fpm-config /etc/php/7.2/fpm/php-fpm.conf ExecReload=/bin/kill -USR2 $MAINPID Environment="USE_ZEND_DTRACE=1" [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload sudo systemctl restart php7.2-fpm.service
おもむろにbpftraceでトレースすると対象プロセスでUSDTがトレースできることができる。
$ sudo BPFTRACE_STRLEN=200 bpftrace -e 'usdt:/usr/sbin/php-fpm7.2:execute__entry { printf("%s, %d\n", str(arg0), arg1); }' -p `pgrep -u www-data php-fpm|head -n1` Attaching 2 probes... /var/www/html/info.php, 2 , 0 /var/www/html/info.php, 2 , 0 ...
probeが二個あることになってるのはなんでやろ・・。とりあえず動きはしたのでヨシ。
mod-php
php-fpmと同様にsystemdの設定に書くか、apacheのenvvarsで設定する、どちらでもとりあえず動かせはする。
$ cat /etc/systemd/system/apache2.service [Unit] Description=The Apache HTTP Server Customized After=network.target remote-fs.target nss-lookup.target Documentation=https://httpd.apache.org/docs/2.4/ [Service] Type=forking Environment="APACHE_STARTED_BY_SYSTEMD=true" "USE_ZEND_DTRACE=1" ExecStart=/usr/sbin/apachectl start ExecStop=/usr/sbin/apachectl stop ExecReload=/usr/sbin/apachectl graceful PrivateTmp=true Restart=on-abort [Install] WantedBy=multi-user.target
or
/etc/apache2/envvars
に export USE_ZEND_DTRACE=1
を追記しておく。
おもむろに子プロセスにアタッチしてみれば動く、こちらの環境はlibphp7.4が使われてるのでこんなかんじで。
$ sudo BPFTRACE_STRLEN=200 bpftrace -e 'usdt:/usr/lib/apache2/modules/libphp7.4.so:execute__entry { printf("%s, %d\n", str(arg0), arg1); }' -p `pgrep -u www-data apache2|head -n1` Attaching 1 probe... /var/www/html/test.php, 2 /var/www/html/test.php, 2 /var/www/html/test.php, 2 ...
その他
bpftraceで --usdt-file-activation
使えるとプロセス特定してアタッチみたいなところが幾分楽できそうな気がする。
--usdt-file-activationは/proc/[0-9]*/maps覗いてアタッチすべきプロセスを見つけたらアタッチするという流れっぽくてpid指定しないで動くのは楽ちんだけど当たり前ながらpid変わったら追っかけられないのでちょっと楽くらいの位置づけかなあ https://t.co/PNV9lIWDsF
— EG (@EGMC) 2020年12月26日
まとめ
ということで若干あやしいところもありつつトレース自体は動かせた。
いずれにせよ USE_ZEND_DTRACE=1
したプロセスしかトレースはできないし、php-fpmにせよmod-phpにせよ子プロセスは入れ替わっていくので、アドホックなトレーシングはともかく「追っかけ続ける」仕組みをつくるのはしんどそうだなーとか、例えばexecute__entryを逐一標準出力に出していくみたいなのはもちろんプロダクションでおもむろにやるにはオーバーヘッドが許容できないであろうということは容易に想像でき、じゃあBPF_MAPを使うのか、どの粒度で情報を保持するのか、そもそもそここまでしてUSDT使うのか、みたいなところでやはりなかなか使い所はむずかしいなーという印象です。動かせるのは楽しいんですけどね。
全体に雰囲気で動かしているのでもっとうまくやる方法があればすごく知りたいところ・・。
参考
ubuntuのsystemd設定上書きについてはこちらの記事を参照させていただきました
第598回 systemdユニットの設定を変える:Ubuntu Weekly Recipe|gihyo.jp … 技術評論社