2013-01-10 17 views
8

我有一個腳本,每天晚上從一個cron作業運行。最近幾分鐘後,它開始完全凍結,我不知道爲什麼。如果這是Java,我可以簡單地運行kill -3 PID,它會在stdout中打印一個線程轉儲。在PHP中是否有任何等價物,我可以在正在運行的PHP腳本中獲取當前堆棧跟蹤(理想情況下是內存信息)的轉儲?獲取掛起的PHP腳本的堆棧跟蹤

+0

簡單的解決方案,將一些日誌語句放入您的代碼並查看它們停止執行的位置。 – Sammitch

回答

7

您可以做的最好的事情是在configure期間使用--enable-debug自己編譯PHP。如果進程則仍然掛起,您可以使用gdb的一些宏使用這些步驟獲得PHP級堆棧跟蹤:提前

$ gdb -p $PHP_PID 
(gdb) bt  # Get a system-level stacktrace, might already give some info 
(gdb) source /path/to/php-src/.gdbinit # Load some useful macros 
(gdb) dump_bt executor_globals.current_execute_data 
      # Macro from PHP's .gbinit giving PHP stack trace 
      # If you for whatever reason are using a thread-safe PHP build you have to do this: 
(gdb) ____executor_globals 
(gdb) dump_bt $eg.current_execute_data 

,然後調試:-)

請注意,這個工作你有有符號信息的PHP二進制文件,--enable-debug確保。

+0

嗨。是否有可能以同樣的方式打印PHP變量的當前值? – shark555

+1

從我頭頂開始:dump_ht executor_globals.active_symbol_table(驗證要使用的變量的名稱:轉到lxr.php.net或您的本地PHP源代碼樹並檢查executor_globals的定義) – johannes

+0

我正在使用pthreads擴展名( PHP多線程),併爲此工作,我不得不首先使用命令set_ts(否則____executor_globals崩潰)與'tsrm_ls'參數值出現在堆棧幀 – 2072

2

是的。您可以使用debug_backtrace進行回溯。您也可能需要memory_get_usage

+2

我的問題是我不知道它掛在哪裏。如果我這樣做,這將是一個簡單的解決方案。 –

1

如果PHP腳本超過最大運行時間,它應該超時。那肯定會爲你解決問題;只是等待它破壞,並且有你的堆棧跟蹤。

我想你可能已經在你的代碼(或php.ini)中設置了最大運行時間爲零來阻止它中斷。如果你已經得到了這個,然後擺脫它(或者如果默認值太小,將其設置爲非常大的超時)。

您可能還想嘗試使用xDebug或類似的工具來運行它,它會爲您提供一個配置文件跟蹤,該配置文件跟蹤將爲您提供該程序的調用樹,並且還允許您單步執行IDE中的代碼,所以你可以看到到底發生了什麼;如果在那裏有一個無限循環,你應該能夠從中很快識別出它。

+0

這是一個長時間運行的腳本。有時可能需要幾個小時。我經常重置超時的數量,這對於正在運行的函數來說是合理的,但是這種方式不能解決我的問題。 –

2

我注意到有一個可能的解決方案使用pcntl_signal。我還沒有嘗試過自己,你可以在這裏找到一些示例代碼:

https://secure.phabricator.com/D7797

function __phutil_signal_handler__($signal_number) { 
    $e = new Exception(); 
    $pid = getmypid(); 
    // Some phabricator daemons may not be attached to a terminal. 
    Filesystem::writeFile(
    sys_get_temp_dir().'/phabricator_backtrace_'.$pid, 
    $e->getTraceAsString()); 
} 

if (function_exists('pcntl_signal')) { 
    pcntl_signal(SIGHUP, '__phutil_signal_handler__'); 
} 
+0

這*可能*工作,如果你不能添加' - 調試-enable'標誌到配置腳本。但是如果你不能使用配置增強功能,我認爲你不會進行這種調試。 –

1

只要你有安裝了PHP的gdb調試符號,你可以得到一個完整的PHP回溯。

安裝調試符號的方法從發行版到發行版可能有所不同。例如在亞馬遜Linux上,我運行rpm -qa | grep php56-common並將結果傳遞給debuginfo-install。請注意,使用標準軟件包管理器安裝調試符號可能無法提供預期的結果 - 在亞馬遜Linux上,運行yum install php56-debuginfo時,我獲得了不同版本的PHP的調試符號,gdb不喜歡這樣。

如果您有您甚至不需要運行過程來獲得回溯。您可以kill -ABRT $pid並稍後檢查覈心轉儲。

然後,您可以使用gdb -p $pid或與gdb /usr/bin/php $path_to_core_dump的核心轉儲開始調試正在運行的進程。

鍵入bt會給你一個C堆棧跟蹤。這有時足以讓你知道什麼可能是錯誤的。確保調試符號安裝正確; bt應指向PHP源代碼中的文件名和行號。

現在嘗試p executor_globals.current_execute_data。它應該打印一些像$1 = (struct _zend_execute_data *) 0x7f3a9bcb12d0。如果是這樣,這意味着gdb可以檢查PHP的內部。

使用一個小小的Python腳本可以產生完整的PHP回溯。鍵入python-interactive然後將這個小腳本:

def bt(o): 
    if o == 0: return 
    print "%s:%d" % (o["op_array"]["filename"].string(), o["opline"]["lineno"]) 
    bt(o["prev_execute_data"]) 

bt(gdb.parse_and_eval("executor_globals.current_execute_data")) 

注意,此方法在很大程度上取決於PHP的內部,所以它可能不是更早或更晚版本。我用php 5.6.14做了這個。這種方法的優點是它可以同時運行進程和核心轉儲,而且不必重新編譯PHP或重新啓動PHP腳本。您甚至可以在之後安裝gdb和調試符號,您會發現掛起過程。