使用 PHP 和 DTrace
在支援 DTrace 動態跟蹤的平臺上,可以配置 PHP 打開 DTrace 靜態探針。
PHP DTrace 靜態探針配置
請參考操作系統文件獲取啟用操作系統 DTrace 支援的資訊。 例如,在 Oracle Linux 系統上啟動 UEK3 內核,並進行如下操作:
# modprobe fasttrap
# chmod 666 /dev/dtrace/helper
    除了 chmod 命令,您也可以使用 ACL 包規則來限制特定使用者對於裝置的訪問許可權。
   
    使用 --enable-dtrace 配置參數構建 PHP:
    
# ./configure --enable-dtrace ...
# make
# make install
這樣就啟用了 PHP 核心的靜態探針。對於提供了自有探針的 PHP 擴充套件需要分別構建。
PHP 核心的 DTrace 靜態探針
| 探針名稱 | 探針描述 | 探針參數 | 
|---|---|---|
| request-startup | 請求開始時觸發。 | char *file, char *request_uri, char *request_method | 
| request-shutdown | 請求關閉時觸發。 | char *file, char *request_uri, char *request_method | 
| compile-file-entry | 指令碼開始編譯時觸發。 | char *compile_file, char *compile_file_translated | 
| compile-file-return | 指令碼完成編譯時觸發。 | char *compile_file, char *compile_file_translated | 
| execute-entry | 運算元陣列開始執行時觸發。例如:函式呼叫,檔案包含以及產生器恢復時會被觸發。 | char *request_file, int lineno | 
| execute-return | 運算元陣列執行完畢之後觸發。 | char *request_file, int lineno | 
| function-entry | PHP 引擎進入 PHP 函式或者方法呼叫時觸發。 | char *function_name, char *request_file, int lineno, char *classname, char *scope | 
| function-return | PHP 引擎從 PHP 函式或者方法呼叫返回后觸發。. | char *function_name, char *request_file, int lineno, char *classname, char *scope | 
| exception-thrown | 有異常拋出時觸發。 | char *classname | 
| exception-caught | 有異常被捕獲時觸發。 | char *classname | 
| error | 無論 error_reporting 的設定如何,在發生錯誤時都會觸發。 | char *errormsg, char *request_file, int lineno | 
PHP 擴充套件可以擁有額外的靜態探針。
列出 PHP 中可用的靜態探針
要列出 PHP 中可用的靜態探針,開啟一個 PHP 程序,然後執行:
# dtrace -l
輸出資訊類似如下所示:
   ID   PROVIDER            MODULE                          FUNCTION NAME
   [ . . . ]
    4   php15271               php               dtrace_compile_file compile-file-entry
    5   php15271               php               dtrace_compile_file compile-file-return
    6   php15271               php                        zend_error error
    7   php15271               php  ZEND_CATCH_SPEC_CONST_CV_HANDLER exception-caught
    8   php15271               php     zend_throw_exception_internal exception-thrown
    9   php15271               php                 dtrace_execute_ex execute-entry
   10   php15271               php           dtrace_execute_internal execute-entry
   11   php15271               php                 dtrace_execute_ex execute-return
   12   php15271               php           dtrace_execute_internal execute-return
   13   php15271               php                 dtrace_execute_ex function-entry
   14   php15271               php                 dtrace_execute_ex function-return
   15   php15271               php              php_request_shutdown request-shutdown
   16   php15271               php               php_request_startup request-startup
    Provider 一列由 php 和目前程序 id 的組成。
   
如果執行的是 Apache web 伺服器,那麼模組名稱可能是 libphp5.so, 並且可能會出現多塊資訊,每個執行中的 Apache 程序對應一個輸出塊。
Function Name 一列表示 Provider 對應的 PHP 內部 C 實現函式名稱。
如果沒有執行任何 PHP 程序,那麼就不會顯示任何 PHP 探針。
PHP DTrace 示例
下例展示了基本的 DTrace D 指令碼。
示例 #1 all_probes.d for tracing all PHP Static Probes with DTrace
#!/usr/sbin/dtrace -Zs
#pragma D option quiet
php*:::compile-file-entry
{
    printf("PHP compile-file-entry\n");
    printf("  compile_file              %s\n", copyinstr(arg0));
    printf("  compile_file_translated   %s\n", copyinstr(arg1));
}
php*:::compile-file-return
{
    printf("PHP compile-file-return\n");
    printf("  compile_file              %s\n", copyinstr(arg0));
    printf("  compile_file_translated   %s\n", copyinstr(arg1));
}
php*:::error
{
    printf("PHP error\n");
    printf("  errormsg                  %s\n", copyinstr(arg0));
    printf("  request_file              %s\n", copyinstr(arg1));
    printf("  lineno                    %d\n", (int)arg2);
}
php*:::exception-caught
{
    printf("PHP exception-caught\n");
    printf("  classname                 %s\n", copyinstr(arg0));
}
php*:::exception-thrown
{
    printf("PHP exception-thrown\n");
    printf("  classname                 %s\n", copyinstr(arg0));
}
php*:::execute-entry
{
    printf("PHP execute-entry\n");
    printf("  request_file              %s\n", copyinstr(arg0));
    printf("  lineno                    %d\n", (int)arg1);
}
php*:::execute-return
{
    printf("PHP execute-return\n");
    printf("  request_file              %s\n", copyinstr(arg0));
    printf("  lineno                    %d\n", (int)arg1);
}
php*:::function-entry
{
    printf("PHP function-entry\n");
    printf("  function_name             %s\n", copyinstr(arg0));
    printf("  request_file              %s\n", copyinstr(arg1));
    printf("  lineno                    %d\n", (int)arg2);
    printf("  classname                 %s\n", copyinstr(arg3));
    printf("  scope                     %s\n", copyinstr(arg4));
}
php*:::function-return
{
    printf("PHP function-return\n");
    printf("  function_name             %s\n", copyinstr(arg0));
    printf("  request_file              %s\n", copyinstr(arg1));
    printf("  lineno                    %d\n", (int)arg2);
    printf("  classname                 %s\n", copyinstr(arg3));
    printf("  scope                     %s\n", copyinstr(arg4));
}
php*:::request-shutdown
{
    printf("PHP request-shutdown\n");
    printf("  file                      %s\n", copyinstr(arg0));
    printf("  request_uri               %s\n", copyinstr(arg1));
    printf("  request_method            %s\n", copyinstr(arg2));
}
php*:::request-startup
{
    printf("PHP request-startup\n");
    printf("  file                      %s\n", copyinstr(arg0));
    printf("  request_uri               %s\n", copyinstr(arg1));
    printf("  request_method            %s\n", copyinstr(arg2));
}
    此指令碼在 dtrace 命令中使用了 -Z 選項。
    此選項保證即使在沒有任何 PHP 程序執行的時候指令碼也能夠正確執行。
    如果省略了此選項,當沒有任何探針可監控的時候,指令碼會立即終止執行。
   
在執行此指令碼的過程中,它將監控全部 PHP 核心靜態探針。 執行 D 指令碼:
# ./all_probes.d
再執行一個 PHP 指令碼或者 PHP 應用,用來進行監控的 D 指令碼會輸出每個探針被觸發時所攜帶的參數。
    監控完成之後,使用
    ^C 來終止 D 指令碼的執行。
   
在多 CPU 的主機上,探針的顯示順序可能不是連續的,這取決於哪顆 CPU 執行探針以及多個 CPU 之間的執行緒遷移情況。 可以通過顯示探針時間戳來減少混淆,例如:
php*:::function-entry
{
      printf("%lld: PHP function-entry ", walltimestamp);
      [ . . .]
}