2013-01-10 13 views
8

Ho uno script che viene eseguito da un cron job ogni notte. Recentemente, ha iniziato a congelarsi completamente dopo alcuni minuti nella sceneggiatura, e non riesco a capire perché. Se questo fosse Java, potrei semplicemente eseguire kill -3 PID e stampare un dump del thread in stdout. C'è qualche equivalente in PHP, dove potrei ottenere un dump della traccia dello stack corrente (e idealmente informazioni sulla memoria) su uno script PHP in esecuzione?Ottenere una traccia stack di uno script PHP sospeso

+0

Soluzione semplice, inserire alcune dichiarazioni di registrazione nel codice e vedere dove si fermano nell'esecuzione. – Sammitch

risposta

7

La cosa migliore che puoi fare è compilare PHP te stesso usando --enable-debug durante configure. Se il processo poi si blocca ancora si può usare gdb e alcune macro per ottenere uno stacktrace livello PHP utilizzando questi passaggi:

$ 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 

E poi il debug avanti :-)

Si noti che per questo lavoro si ha per avere un binario PHP con informazioni sui simboli, lo garantisce --enable-debug.

+0

Ciao. È possibile stampare i valori correnti delle variabili PHP allo stesso modo? – shark555

+1

Dalla mia testa: dump_ht executor_globals.active_symbol_table (per verificare il nome della variabile da utilizzare: vai su lxr.php.net o sul tuo albero di sorgenti PHP locale e controlla la definizione executor_globals) – johannes

+0

Sto usando l'estensione pthreads (php multithreading) e per farlo funzionare ho dovuto prima usare il comando set_ts (altrimenti ____executor_globals si è bloccato) con il valore dell'argomento 'tsrm_ls' che appare nei frame dello stack – 2072

2

Sì. È possibile ottenere un backtrace con debug_backtrace. Potrebbe anche essere necessario memory_get_usage.

+2

Il mio problema è che non so dove sia appeso. Se lo facessi, questa sarebbe la soluzione semplice. –

1

Uno script PHP dovrebbe scadere se supera il runtime massimo. Sicuramente questo risolverebbe il problema per te; aspetta solo che si rompa, e c'è la tua traccia dello stack.

Immagino sia possibile che tu abbia qualcosa nel tuo codice (o php.ini) che imposta il runtime massimo su zero per fermarlo. Se lo hai, quindi liberalo (o impostalo su un timeout molto grande se il valore predefinito è davvero troppo piccolo).

Si potrebbe anche voler provare a eseguirlo con xDebug o uno strumento simile, che ti darà una traccia di profiler che ti fornirà un albero delle chiamate del programma, e ti permetterà anche di scorrere il codice nel tuo IDE, così puoi vedere esattamente cosa sta succedendo; se c'è un ciclo infinito lì dentro, dovresti essere in grado di identificarlo abbastanza rapidamente da quello.

+0

Questo è uno script di lunga durata. Alcune volte può richiedere diverse ore. Ripristino regolarmente il timeout per un importo ragionevole per la funzione in esecuzione, ma questo in qualche modo non afferra il mio problema. –

2

Ho notato che esiste una possibile soluzione che utilizza pcntl_signal. Non ho provato io stesso, è possibile trovare alcuni esempi di codice qui:

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

Questo * potrebbe * funzionare se non è possibile aggiungere il '--debug -enable' flag allo script configure. Ma non penso che faresti questo debugging se non potessi giocare anche con i configure configure. –

1

Se si dispone simboli gdb e debug per PHP installato, è possibile ottenere un backtrace completo di PHP.

Il metodo di installazione dei simboli di debug può variare da distro a distro. Ad esempio su Amazon Linux ho eseguito rpm -qa | grep php56-common e passato il risultato a debuginfo-install. Si noti che l'installazione di simboli di debug usando il gestore di pacchetti standard potrebbe non dare il risultato desiderato - su Amazon Linux ho ottenuto il debug di simboli per una versione diversa di PHP quando si eseguiva yum install php56-debuginfo e gdb non gli piaceva.

Se si dispone di non è nemmeno necessario eseguire il processo per ottenere un backtrace. È possibile kill -ABRT $pid e ispezionare il core dump in seguito.

È quindi possibile avviare il debug di un processo in esecuzione con gdb -p $pid o un core dump con gdb /usr/bin/php $path_to_core_dump.

Digitando bt si otterrà una traccia di stack C. Questo a volte sarà sufficiente per darti un suggerimento su cosa potrebbe essere sbagliato. Assicurarsi che i simboli di debug siano installati correttamente; bt dovrebbe puntare a nomi di file e numeri di riga nel codice sorgente PHP.

Provare ora p executor_globals.current_execute_data. Dovrebbe stampare qualcosa come $1 = (struct _zend_execute_data *) 0x7f3a9bcb12d0. Se è così, significa che gdb può ispezionare l'interno di PHP.

Utilizzando un piccolo script Python è possibile produrre un backtrace PHP completo. Digitare python-interactive e quindi inserire questo piccolo script:

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")) 

Nota che questo metodo dipende in larga misura la struttura interna di PHP, quindi potrebbe non funzionare su versioni precedenti o successive. L'ho fatto con php 5.6.14. Il vantaggio di questo metodo è che funziona sia per i processi in esecuzione che per i core dump, inoltre non è necessario ricompilare PHP o riavviare lo script PHP. È anche possibile installare i simboli gdb e debugging dopo lo per scoprire un processo di sospensione.

Problemi correlati