PHP travando (seg-fault) sob mod_fcgi, apache

1

Eu tenho programado um site usando:

  1. Zend Framework 1.11.5 (MVC completo)
  2. PHP 5.3.6
  3. Apache 2.2.19
  4. Virtoszzo i686 do CentOS 5.6 em vps
  5. cPanel WHM 11.30.1 (compilação 4)
  6. MySQL 5.1.56-log
  7. API do Mysqli 5.1.56

O problema começou aqui link . Em resumo, php está me dando erros de segmentação aleatórios.

[Wed Jul 20 17:45:34 2011] [error] mod_fcgid: process /usr/local/cpanel/cgi-sys/php5(11562) exit(communication error), get unexpected signal 11
[Wed Jul 20 17:45:34 2011] [warn] [client 190.78.208.30] (104)Connection reset by peer: mod_fcgid: error reading data from FastCGI server
[Wed Jul 20 17:45:34 2011] [error] [client 190.78.208.30] Premature end of script headers: index.php

Sobre extensões. Quando compilo o php com o sinalizador "--enable-debug", tenho que desabilitar esta linha:

zend_extension="/usr/local/IonCube/ioncube_loader_lin_5.3.so"

Caso contrário, o servidor não aceita solicitações e eu recebo um "A conexão com o servidor foi redefinida". É possível que eu tenha que desabilitar o eaccelerator também por causa do mesmo motivo. Eu ainda não entendi porque o apache começa a rodá-lo algumas vezes e outros não:

extension="eaccelerator.so"

De qualquer forma, depois que eu tiver o httpd em execução, falhas de segmento podem ocorrer aleatoriamente. Se eu não compilar o php com o sinalizador "--enable-debug", posso obter DETERMINISTICAMENTE uma falha no php:

<?php
class Admin_DbController extends Controller_BaseController
{
    public function updateSqlDefinitionsAction()
    {
        $db = Zend_Registry::get('db'); 
        $row = $db->fetchRow("SHOW CREATE TABLE 222AFI");
    }
}
?>

MAS se eu compilar o php com o sinalizador "--enable-debug", é realmente difícil obter este erro. Eu devo adicionar alguma complexidade para fazê-lo falhar. Eu tenho que fazer muitos pedidos paralelos por alguns segundos para obter uma falha:

<?php
class Admin_DbController extends Controller_BaseController
{
    public function updateSqlDefinitionsAction()
    {
        $db = Zend_Registry::get('db');
        $tableList = $db->listTables();
        foreach ($tableList as $tableName){
            $row = $db->fetchRow("SHOW CREATE TABLE " . $db->quoteIdentifier($tableName));
            file_put_contents(
                DB_DEFINITIONS_PATH . '/' . $tableName . '.sql',
                $row['Create Table'] . ';'
            );
        }
    }
}
?>

Por favor note que este é o mesmo script, mas criando DDL para todas as tabelas no banco de dados ao invés de um. Parece que, se php é pesado carregado (com extensões e me fazendo muitas solicitações paralelas) é quando eu conseguir php para falhar.

Sobre iniciar o httpd com "-X": tentei. A coisa é, já é difícil fazer o php travar com --enable-debug. Com a opção "-X" (que permite apenas um processo filho), não posso fazer solicitações paralelas. Então, não consegui criar um backtrace de depuração adequado: link

A minha pergunta concreta é: o que devo fazer para obter um coredump?

root@GWT4 [~]# httpd -V
Server version: Apache/2.2.19 (Unix)
Server built:   Jul 20 2011 19:18:58
Cpanel::Easy::Apache v3.4.2 rev9999
Server's Module Magic Number: 20051115:28
Server loaded:  APR 1.4.5, APR-Util 1.3.12
Compiled using: APR 1.4.5, APR-Util 1.3.12
Architecture:   32-bit
Server MPM:     Prefork
  threaded:     no
    forked:     yes (variable process count)
Server compiled with....
 -D APACHE_MPM_DIR="server/mpm/prefork"
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_SYSVSEM_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=128
 -D HTTPD_ROOT="/usr/local/apache"
 -D SUEXEC_BIN="/usr/local/apache/bin/suexec"
 -D DEFAULT_PIDLOG="logs/httpd.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_LOCKFILE="logs/accept.lock"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="conf/mime.types"
 -D SERVER_CONFIG_FILE="conf/httpd.conf"
    
por Andras Gyomrey 21.07.2011 / 03:54

4 respostas

2

Obter um dump principal envolve definir o "tamanho do arquivo principal" rlimit como algo diferente de zero para o (s) processo (s) do qual você deseja obter dumps principais. Como você está executando o PHP no FCGI, você não se importa com o próprio Apache, ele não é segmentado; você deseja ter um script para executar ulimit -c unlimited e, em seguida, executar seu servidor FCGI.

Eu gosto de ter uma partição temporária separada para núcleos, então se eles ficarem um pouco grandes ou indisciplinados, eles não causarão problemas de preenchimento de disco em outro lugar - configure /proc/sys/kernel/core_pattern para algo como /var/cores/%e-%p.core .

    
por 21.07.2011 / 05:02
0

Você deve usar ioncube_loader e eaccelerator? Em relação ao eaccelerator, seu suporte ao 5.3.x tem sido, na melhor das hipóteses, instável e a maioria dos usuários de 5.3 confia na APC (que também funcionará melhor via FCGI).

Se você deve usar o eaccelerator, você está fazendo um "make distclean" e depois as instruções INSTALL (verificando se o phpize que você está carregando é para o 5.3.6 e não algum remanescente de uma instalação anterior)? Eu suspeito que seja toda a fonte de seus coredumps e que o Apache não seja o culpado.

    
por 21.07.2011 / 05:25
0

Eu encontrei uma solução temporária. Isso é feio, mas se encaixa:

função pública selectCmd ($ q) {

$charsFrom = array("\a", "\t", "\n", "\v", "\f", "\r", "\\", "\0", "\\"", "\\'", "\b");
$charsTo = array("\a", "\t", "\n", "\v", "\f", "\r", "\", "
$charsFrom = array("\a", "\t", "\n", "\v", "\f", "\r", "\\", "\0", "\\"", "\\'", "\b");
$charsTo = array("\a", "\t", "\n", "\v", "\f", "\r", "\", "%pre%", "\"", "\'", "\b");

exec('echo ' . escapeshellarg($q) . ' | mysql' .
    ' -h ' . escapeshellarg($this->_config['host']).
    ' -u ' . escapeshellarg($this->_config['username']).
    ' -p' . escapeshellarg($this->_config['password']).
    ' ' . escapeshellarg($this->_config['dbname']), $output);

$colNames = explode("\t", array_shift($output));
foreach ($colNames as &$colName){
    $colName = str_replace($charsFrom, $charsTo, $colName);
}
unset($colName);

$rowSet = array();
foreach ($output as $line){
    $row = array();
    $rawRow = explode("\t", $line);
    for ($i = 0; $i < count($rawRow); ++ $i){
        $row[$colNames[$i]] = str_replace($charsFrom, $charsTo, $rawRow[$i]);
    }
    $rowSet[] = $row;
}
return $rowSet;
", "\"", "\'", "\b"); exec('echo ' . escapeshellarg($q) . ' | mysql' . ' -h ' . escapeshellarg($this->_config['host']). ' -u ' . escapeshellarg($this->_config['username']). ' -p' . escapeshellarg($this->_config['password']). ' ' . escapeshellarg($this->_config['dbname']), $output); $colNames = explode("\t", array_shift($output)); foreach ($colNames as &$colName){ $colName = str_replace($charsFrom, $charsTo, $colName); } unset($colName); $rowSet = array(); foreach ($output as $line){ $row = array(); $rawRow = explode("\t", $line); for ($i = 0; $i < count($rawRow); ++ $i){ $row[$colNames[$i]] = str_replace($charsFrom, $charsTo, $rawRow[$i]); } $rowSet[] = $row; } return $rowSet;

}

Você precisará substituir $ this- > _config por um array de conexão real.

Este script executa qualquer comando SQL que retorne linhas. Por enquanto é a solução que estou tomando. Se alguém quiser me ajudar de forma proativa, ainda tenho as fontes. Eu também sou o cara que tem o PHP seg-fault usando PHPUnit com Zend (deterministicamente). Estou fazendo tudo isso no meu trabalho, não tenho recursos para testar. Obrigado pela sua compreensão.

    
por 25.07.2011 / 20:23
0

Não tenho certeza se isso ajuda neste caso, mas eu configurei apenas um filho, achei o PID e depois depurei com strace

strace -p PID
    
por 11.12.2013 / 12:28