How to Diagnose and Fix High CPU Usage in Apache Processes Running PHP Scripts


1 views

When you encounter Apache processes consuming 100% CPU but strace -p PID shows no output, this typically indicates the process is stuck in userspace computation (like an infinite loop in PHP) rather than making system calls. This makes traditional tracing tools less effective.

Here are several techniques to identify problematic PHP scripts:

# 1. Use gdb for stack tracing
gdb -p PID
(gdb) bt full
(gdb) thread apply all bt

# 2. Check Apache's mod_status (requires configuration)
# In apache2.conf:
ExtendedStatus On
<Location /server-status>
    SetHandler server-status
    Require host localhost
</Location>

For PHP processes, try these methods:

# 3. Install PHP debug symbols
sudo apt-get install php5-dbg

# 4. Enhanced backtrace with debug symbols
gdb -p PID
(gdb) info symbol 0x00007f6c143fb0c5
(gdb) x/10i $pc

# 5. Check open files (might reveal script path)
ls -l /proc/PID/fd

For production environments where you can't restart services:

  • Limit diagnostic impact using nice -n 19 for analysis tools
  • Sample CPU usage over time: top -b -n 10 -p PID
  • Monitor specific process states: ps -o state,cmd -p PID

Frequent causes of PHP/Apache CPU spikes include:

# Example of problematic code patterns to check:

# Infinite loops without proper exit conditions
while ($condition) {
    // Missing condition updates
}

# Recursive functions without base cases
function recursive() {
    recursive();
}

# Heavy computations in loops
foreach ($items as $item) {
    complexCalculation($item); // Called repeatedly
}

Implement these in your development workflow:

# Set execution time limits in PHP
ini_set('max_execution_time', 30);

# Install and configure PHP-FPM process manager
# In pool configuration:
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35

For deep performance analysis:

# Install XHProf for PHP profiling
pecl install xhprof
# In php.ini:
extension=xhprof.so
xhprof.output_dir=/tmp/xhprof

# Sample profiling code:
xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
// Your application code
$xhprof_data = xhprof_disable();

Remember to cross-reference findings with Apache access logs (/var/log/apache2/access.log) to identify problematic requests.


When you encounter Apache processes consuming 100% CPU with no strace output, this typically indicates the process is stuck in userspace execution (likely an infinite loop in PHP code). The absence of system calls means strace won't capture anything useful.

Here are more effective ways to diagnose the issue on a production server:

# 1. Get PHP backtrace using gdb
gdb -p PID
(gdb) bt full

# 2. Check open files
ls -l /proc/PID/fd

# 3. Examine process memory maps
cat /proc/PID/maps

# 4. Check PHP execution with phpdbg
phpdbg -p PID

When PHP isn't compiled with debug symbols, try these alternatives:

# Method 1: Check PHP process status
ps aux | grep apache | grep PID

# Method 2: Extract script from memory (requires gdb)
gdb -p PID -batch -ex 'call zend_get_executed_filename()'

# Method 3: Check Apache access logs (correlate by time)
grep PID /var/log/apache2/access.log

Frequent causes of PHP infinite loops include:

  • Recursive functions without proper termination
  • While loops with missing/invalid exit conditions
  • Third-party PHP extensions with memory leaks
  • Database queries that never complete

Example of problematic code:

// Bad recursive implementation
function process_items($items) {
    foreach ($items as $item) {
        // Missing base case
        process_items($item->children); 
    }
}

For immediate relief without restarting Apache:

# Set CPU affinity to limit damage
taskset -pc 0 PID

# Adjust PHP execution limits
gdb -p PID -batch -ex 'call zend_set_timeout(5)'

Add these to your php.ini for future protection:

max_execution_time = 30
max_input_time = 60
memory_limit = 128M
opcache.enable = 1
opcache.validate_timestamps = 0

For complex cases, consider systemtap or perf:

# Sample systemtap script
probe process("/usr/lib/apache2/modules/libphp5.so").function("zend_execute") {
    printf("Executing: %s\n", user_string($filename))
}