本文目錄一覽:
ThinkPHP3.2入口文件引入出錯,求大神
這個入口文件,引用的是相對路徑。
①首先,你要確定和index.php同級的目錄下有沒有core文件夾,如果有,進去看看有沒有ThinkPHP.php。
②如果core目錄和index.php不同級,就要根據相對路徑去計算,如何定位到core文件夾。
這個問題是小問題,仔細一點,好好拍查一下。
如何調試PHP的Core之獲取基本信息
在這個過程中, 會涉及到對PHP的函數調用, PHP的傳參, PHP的一些全局變數的知識.
首先, 讓我們生成一個供我們舉例子的Core文件:
?php
function recurse($num) {
recurse(++$num);
}
recurse(0);
運行這個PHP文件:
$ php test.php
Segmentation fault (core dumped)
這個PHP因為無線遞歸, 會導致爆棧, 從而造成 segment fault而在PHP的當前工作目錄產生Coredump文件(如果你的系統沒有產生Coredump文件, 那請查詢ulimit的相關設置).
現在刪除掉這個test.php, 忘掉上面的代碼, 我們現在僅有的是這個Core文件, 任務是, 找出這個Core產生的原因, 以及發生時候的狀態.
首先, 讓我們用gdb打開這個core文件:
$ gdb php -c core.31656
會看到很多的信息, 首先讓我們注意這段:
Core was generated by `php test.php’.
Program terminated with signal 11, Segmentation fault.
他告訴我們Core發生的原因:」Segmentation fault」.
一般來說, 這種Core是最常見的, 解引用空指針, double free, 以及爆棧等等, 都會觸發SIGSEGV, 繼而默認的產生Coredump.
現在讓我們看看Core發生時刻的堆棧:
#0 execute (op_array=0xdc9a70) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:53
53 memset(EX(CVs), 0, sizeof(zval**) * op_array-last_var);
(gdb) bt
#0 execute (op_array=0xdc9a70) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:53
#1 0x00000000006ea263 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fbf400210) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:234
#2 0x00000000006e9f61 in execute (op_array=0xdc9a70) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:92
#3 0x00000000006ea263 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fbf400440) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:234
#4 0x00000000006e9f61 in execute (op_array=0xdc9a70) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:92
#5 0x00000000006ea263 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fbf400670) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:234
…..
不停的按回車, 可以看到堆棧很深, 不停的是zend_do_fcall_common_helper_SPEC和execute的重複, 那麼這基本就能斷定是因為產生了無窮大的遞歸(不能一定說是無窮遞歸, 比如我之前文章中介紹深悉正則(pcre)最大回溯/遞歸限制). 從而造成爆棧產生的Core.
Ok, 那麼現在讓我們看看, Core發生在PHP的什麼函數中, 在PHP中, 對於FCALL_* Opcode的handler來說, execute_data代表了當前函數調用的一個State, 這個State中包含了信息:
(gdb)f 1
#1 0x00000000006ea263 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fbf400210) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:234
234 zend_execute(EG(active_op_array) TSRMLS_CC);
(gdb) p execute_data-function_state.function-common-function_name
$3 = 0x2a95b65a78 “recurse”
(gdb) p execute_data-function_state.function-op_array-filename
$4 = 0x2a95b632a0 “/home/laruence/test.php”
(gdb) p execute_data-function_state.function-op_array-line_start
$5 = 2
現在我們得到, 在調用的PHP函數是recurse, 這個函數定義在/home/laruence/test.php的第二行
經過重複驗證幾個frame, 我們可以看出, 一直是在重複調用這個PHP函數.
要注意的是, 為了介紹查看執行信息的原理, 我才採用原生的gdb的print來查看, 其實我們還可以使用PHP源代碼中提供的.gdbinit(gdb命令編寫腳本), 來簡單的獲取到上面的信息:
(gdb) source /home/laruence/package/php-5.2.14/.gdbinit
(gdb) zbacktrace
[0xbf400210] recurse() /home/laruence/test.php:3
[0xbf400440] recurse() /home/laruence/test.php:3
[0xbf400670] recurse() /home/laruence/test.php:3
[0xbf4008a0] recurse() /home/laruence/test.php:3
[0xbf400ad0] recurse() /home/laruence/test.php:3
[0xbf400d00] recurse() /home/laruence/test.php:3
[0xbf400f30] recurse() /home/laruence/test.php:3
[0xbf401160] recurse() /home/laruence/test.php:3
…..
關於.gdbinit, 是一段小小的腳本文件, 定義了一些方便我們去調試PHP的Core, 大家也可以用文本編輯器打開, 看看裡面定義的一些快捷的命令, 一般來說, 我常用的有:
zbacktrace
print_ht**系列
zmemcheck
OK, 回歸正題, 我們現在知道, 問題發生在/home/laruence/test.php的recurse函數的遞歸調用上了.
現在, 讓我們來看看, 在調用這個函數的時候的參數是什麼?
PHP的參數傳遞是依靠一個全局Stack來完成的, 也就是EG(argument_stack), EG在非多線程情況下就是executor_globals, 它保持了很多執行狀態. 而argument_statck就是參數的傳遞棧, 保存著對應PHP函數調用層數相當的調用參數.
要注意的是, 這個PHP函數調用堆棧(層數)不和gdb所看到的backtrace簡單的一一對應, 所以參數也不能直接和gdb的backtrace對應起來, 需要單獨分析:
//先看看, 最後一次函數調用的參數數目是多少
(gdb) p (int )*(executor_globals-argument_stack-top_element – 2)
$13 = 1
//再看看, 最後一次函數調用的參數是什麼
(gdb) p **(zval **)(executor_globals-argument_stack-top_element – 3)
$2 = {value = {lval = 22445, dval = 1.1089303420906779e-319, str = {val = 0x57ad Address 0x57ad out of bounds, len = 7}, ht = 0x57ad, obj = {handle = 22445, handlers = 0x7}},
refcount = 2, type = 1 ‘\001’, is_ref = 0 ‘\0’}
好, 我們現在得到, 最後一次調用的參數是一個整數, 數值是22445
到了這一步, 我們就得到了這個Core發生的時刻的PHP層面的相關信息, 接下來, 就可以交給對應的PHP開發工程師來排查, 這個參數下, 可能造成的無窮大遞歸的原因, 從而修復這個問題..
如何查看php-fpm core dump 文件的錯誤
開啟php的coredump輸出,修改ulimit -c就可以了,但是很多情況下,會提示權利受限,無法修改
[fukun@10.16.29.xxx]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 30678
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 32768
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
$ ulimit -c unlimited
-bash: ulimit: core file size: cannot modify limit: Operation not permitted
解決方案:
1.檢查配置
看看shell配置里有沒有 ulimit -c 0 這種類似的關閉的操作,例如
$HOME/.bash_profile 或者 $HOME/.bashrc 之類的,如果有,注釋掉。
#
# Do not produce core dumps
#
# ulimit -c 0
2. 全局coredump開啟
使用root用戶,修改 /etc/security/limits.conf
# /etc/security/limits.conf
#
# Each line describes a limit for a user in the form:
#
# domain type item value
#
* soft core unlimited
3.退出並從新登錄shell,修改配置
ssh fukun@10.16.29.xxx
[fukun@10.16.29.xxx]$ ulimit
-c
先切換成root用戶進行設置
su –
ulimit -c unlimited
ulimit -c
unlimited
然後切換回自己的用戶進行設置
su – fukun
ulimit -c unlimited
ulimit -c
unlimited
以上3步有可能不是必須的,但是只要按這個步驟來,肯定能設置成功,在其它方法設置不成功的時候,按這個步驟來試試把。
原創文章,作者:DNNZ,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/148517.html