生成一個gdb回溯調試php內核及擴展的方法

注意到PHP崩潰

沒有絕對的方法知道PHP正在崩潰,但是可能有一些跡象。通常情況下,如果你訪問一個總是應該生成輸出的頁面(例如,有一個前導HTML塊),並且突然從瀏覽器中獲得“Document contains no data”,這可能意味着PHP在執行腳本。另一種告訴PHP崩潰的方法是查看Apache錯誤日誌,尋找SEGV(Apache 1.2)或Segmentation Fault(Apache 1.3)。

重要!

要獲得正確信息的回溯,您必須將PHP配置爲enable-debug!

如果你還沒有核心文件:

  • 在shell中刪除對內核轉儲大小的限制:
    • tcsh: 無限coredumpsize
    • bash/sh: ulimit -c unlimited 無限制
  • 確保運行PHP的目錄或啓用PHP的httpd對運行PHP的用戶具有寫權限。
  • 導致PHP崩潰:
    • PHP的CGI:只需運行PHP的腳本,崩潰它
    • PHP的Apache模塊:運行httpd -X,並訪問崩潰PHP的腳本

在Linux上獲得core的通用方法

  • 設置core模式(以root身份運行此命令):
    • echo “這裏是輸出coredump目錄/core-%e.%p” > /proc/sys/kernel/core_pattern
    • 確保coredump目錄是可寫的PHP目錄
  • 設置ulimit(請參閱上面的操作方法)。
  • 重啓PHP-FPM。
    之後,系統中所有崩潰的進程(包括PHP)都將其核心文件保留在您在core_pattern中指定的目錄中。

擁有core文件後:

  • 使用指向PHP或啓用PHP的httpd二進制文件的路徑以及核心文件的路徑運行gdb。一些例子:

    • gdb /usr/local/apache/sbin/httpd /usr/local/apache/sbin/core
    • gdb /home/user/dev/php-snaps/sapi/cli/php /home/user/dev/testing/core
    • gdb /usr/local/php-debug/sbin/php-fpm /usr/local/php-debug/var/coredump/core-php-fpm.10930
  • 在gdb提示符下運行:

    • (gdb) bt

如果無法core核心文件:

  • 在gdb下運行httpd -X,如下所示:

    • gdb /usr/local/apache/sbin/httpd
    • (gdb) run -X
  • 然後,使用Web瀏覽器並訪問服務器以強制崩潰。您應該看到gdb提示符出現,並且一些消息表明發生了崩潰。在此gdb提示符下,鍵入:

(gdb) bt
  • 或者從命令行運行
gdb /home/user/dev/php-snaps/sapi/cli/php
(gdb) run /path/to/script.php
(gdb) bt

這將生成回溯記錄,您應在錯誤報告中提交該回溯記錄,以及您可以向我們提供的有關設置和令人討厭的腳本的任何其他詳細信息。

查找哪個函數調用導致了段錯誤:

您可以使用gdb輕鬆找到導致段錯誤的函數調用。首先,您需要一個核心文件或如上所述在gdb下生成segfault。
在PHP中,每個函數都由稱爲的內部函數執行, execute()並具有自己的堆棧。該bt命令生成的每一行 代表一個函數調用堆棧。通常,您execute()在發出時會看到幾行 bt。您對最後一個execute()堆棧(即最小的幀號)感興趣 。您可以使用移動當前正在工作的堆疊up,down或 frame命令。下面是一個示例gdb會話,可用作有關如何處理段錯誤的指南。

  • 示例gdb會話
(gdb) bt
#0  0x080ca21b in _efree (ptr=0xbfffdb9b) at zend_alloc.c:240
#1  0x080d691a in _zval_dtor (zvalue=0x8186b94) at zend_variables.c:44
#2  0x080cfab3 in _zval_ptr_dtor (zval_ptr=0xbfffdbfc) at zend_execute_API.c:274
#3  0x080f1cc4 in execute (op_array=0x816c670) at ./zend_execute.c:1605
#4  0x080f1e06 in execute (op_array=0x816c530) at ./zend_execute.c:1638
#5  0x080f1e06 in execute (op_array=0x816c278) at ./zend_execute.c:1638
#6  0x080f1e06 in execute (op_array=0x8166eec) at ./zend_execute.c:1638
#7  0x080d7b93 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at zend.c:810
#8  0x0805ea75 in php_execute_script (primary_file=0xbffff650) at main.c:1310
#9  0x0805cdb3 in main (argc=2, argv=0xbffff6fc) at cgi_main.c:753
#10 0x400c91be in __libc_start_main (main=0x805c580 
, argc=2, ubp_av=0xbffff6fc,
               init=0x805b080 <_init>, fini=0x80f67b4 <_fini>, rtld_fini=0x4000ddd0 <_dl_fini>,
               stack_end=0xbffff6ec) at ../sysdeps/generic/libc-start.c:129
(gdb) frame 3
#3  0x080f1cc4 in execute (op_array=0x816c670) at ./zend_execute.c:1605
(gdb) print (char *)(executor_globals.function_state_ptr->function)->common.function_name
$14 = 0x80fa6fa "pg_result_error"
(gdb) print (char *)executor_globals.active_op_array->function_name
$15 = 0x816cfc4 "result_error"
(gdb) print (char *)executor_globals.active_op_array->filename
$16 = 0x816afbc "/home/yohgaki/php/DEV/segfault.php"
(gdb) 

在此會話中,幀3是最後一個調用execute()。該 幀3命令將當前工作堆棧移動到適當的幀。 打印函數名稱。在示例gdb會話中,該調用導致了段錯誤。如果您知道內部數據結構,則可以打印任何您喜歡的內部數據。請不要問如何使用gdb或關於內部數據結構。有關gdb的用法,請參見gdb手冊;有關內部數據結構,請參見PHP源代碼。

print (char *)(executor_globals.function_state_ptr->function)->common.function_name
pg_result_error()

execute如果不調用任何函數,可能看不到段錯誤是否發生。
原文地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章