PHP DTrace 静的プローブとともに bpftrace を使用

LinuxディストリビューションにおいてカーネルがeBPFをサポートする場合、bpftraceユーティリティはPHPのDTraceプローブ(USDT)に対してSystemTapを必要とせず直接アタッチすることができます。

bpftraceのインストール

bpftraceをパッケージマネージャを利用してインストールします。例えばOracle Linux、RHELまたはFedoraの場合は

# dnf install bpftrace
Debian、Ubuntuの場合は以下のコマンドでインストールします。
# apt install bpftrace

下記の例ではターゲットとなるPHPバイナリのインストール先は /usr/bin/phpであると仮定しています。

同じUSDTプローブは同一のソースツリーからビルドされた他のSAPIでも利用可能であり、プローブのターゲットはApache module (libphp.so)またはFastCGI Process Manager バイナリ (php-fpm)となる場合もあります。必要に応じて適切なパスに置き換えや、 -pによるPID指定を行いアタッチしてください。

ターゲットバイナリでDTraceが有効になっていること、環境変数が適切に設定されていることを確認してください。 詳細はDTrace 静的プローブのために PHP を構成で参照できます。

PHPの静的プローブをリストするには以下のbpftraceコマンドを使用します。

# bpftrace -l 'usdt:/usr/bin/php:php:*'

出力はこのようになります。

usdt:/usr/bin/php:php:compile__file__entry
usdt:/usr/bin/php:php:compile__file__return
usdt:/usr/bin/php:php:error
usdt:/usr/bin/php:php:exception__caught
usdt:/usr/bin/php:php:exception__thrown
usdt:/usr/bin/php:php:execute__entry
usdt:/usr/bin/php:php:execute__return
usdt:/usr/bin/php:php:function__entry
usdt:/usr/bin/php:php:function__return
usdt:/usr/bin/php:php:request__shutdown
usdt:/usr/bin/php:php:request__startup

例1 bpftraceを使用してすべてのPHP静的プローブをトレースするall_probes.bt

#!/usr/bin/env bpftrace

usdt:/usr/bin/php:php:compile__file__entry
{
    printf("Probe compile__file__entry\n");
    printf("  compile_file %s\n", str(arg0));
    printf("  compile_file_translated %s\n", str(arg1));
}
usdt:/usr/bin/php:php:compile__file__return
{
    printf("Probe compile__file__return\n");
    printf("  compile_file %s\n", str(arg0));
    printf("  compile_file_translated %s\n", str(arg1));
}
usdt:/usr/bin/php:php:error
{
    printf("Probe error\n");
    printf("  errormsg %s\n", str(arg0));
    printf("  request_file %s\n", str(arg1));
    printf("  lineno %d\n", (int32)arg2);
}
usdt:/usr/bin/php:php:exception__caught
{
    printf("Probe exception__caught\n");
    printf("  classname %s\n", str(arg0));
}
usdt:/usr/bin/php:php:exception__thrown
{
    printf("Probe exception__thrown\n");
    printf("  classname %s\n", str(arg0));
}
usdt:/usr/bin/php:php:execute__entry
{
    printf("Probe execute__entry\n");
    printf("  request_file %s\n", str(arg0));
    printf("  lineno %d\n", (int32)arg1);
}
usdt:/usr/bin/php:php:execute__return
{
    printf("Probe execute__return\n");
    printf("  request_file %s\n", str(arg0));
    printf("  lineno %d\n", (int32)arg1);
}
usdt:/usr/bin/php:php:function__entry
{
    printf("Probe function__entry\n");
    printf("  function_name %s\n", str(arg0));
    printf("  request_file %s\n", str(arg1));
    printf("  lineno %d\n", (int32)arg2);
    printf("  classname %s\n", str(arg3));
    printf("  scope %s\n", str(arg4));
}
usdt:/usr/bin/php:php:function__return
{
    printf("Probe function__return\n");
    printf("  function_name %s\n", str(arg0));
    printf("  request_file %s\n", str(arg1));
    printf("  lineno %d\n", (int32)arg2);
    printf("  classname %s\n", str(arg3));
    printf("  scope %s\n", str(arg4));
}
usdt:/usr/bin/php:php:request__shutdown
{
    printf("Probe request__shutdown\n");
    printf("  file %s\n", str(arg0));
    printf("  request_uri %s\n", str(arg1));
    printf("  request_method %s\n", str(arg2));
}
usdt:/usr/bin/php:php:request__startup
{
    printf("Probe request__startup\n");
    printf("  file %s\n", str(arg0));
    printf("  request_uri %s\n", str(arg1));
    printf("  request_method %s\n", str(arg2));
}

上記のスクリプトはPHPのスクリプトの実行中のすべてのコアPHP静的プローブポイントをトレースします。bpftraceの実行にはroot権限が必要です。

# USE_ZEND_DTRACE=1 bpftrace -c '/usr/bin/php test.php' all_probes.bt

すでに実行中のPHPプロセス (例えば php-fpm ワーカー、または libphp.soをロードするapacheプロセス)はPIDを指定してアタッチできます。

# bpftrace -p $PID all_probes.bt
スクリプト中のusdt: ターゲットパスは実行しているプロセスのバイナリにマッチする必要があります。 usdt:/usr/bin/php部分を php-fpm バイナリまたは libphp.so などのパスに必要に応じて修正してください。