View all php processes

How to know which function PHP is currently executing and get its stack trace?

As far as I know, XDebug can’t break on demand, without existing breakpoints or debug_break instruction.

So, let suppose your code seems to be stuck on an infinite loop, or doing something really slow. In my case, Magento was executing thousands of SQL queries according to its general log, but I had no idea neither why nor where it was doing so.

As you may know, PHP is written in C, so we will debug it as any C application. I used that method with the CLI and FPM SAPIs on GNU/Linux, so I won’t cover mod_php SAPI and other operating systems.

⚠️ PHP has to be compiled with debugging symbol so everything works!

⚠️ These instructions won’t work before PHP7, and may stop working on future versions.

Get the PID of your PHP process

Using ps, let list running PHP processes :

PHP-FPM

ari@ari-ThinkPad-T580:~/Documents/perso/ariviere.github.io/_posts$ ps aux | grep php root 17670 0.0 0.2 621184 36772 ? Ss déc.06 0:01 php-fpm: master process (/etc/php/7.2/fpm/php-fpm.conf) ari 17700 0.0 0.1 623484 16952 ? S déc.06 0:00 php-fpm: pool www ari 17706 0.0 1.0 705948 170288 ? R déc.06 0:03 php-fpm: pool www ari 17708 0.0 0.1 623484 16952 ? S déc.06 0:00 php-fpm: pool www ari 29609 0.0 0.0 21536 948 pts/8 S+ 13:28 0:00 grep --color=auto php 

As you can see, there are several PHP-FPM processes on the pool. You can ignore the pool’s master process.

Читайте также:  Php font size small

If one is running (R), it may be the one you want, but there is no warranty for that.

You’ll probably have to attach several of them to the debugger before you catch the one you wanted. Begin with the running ones as there are more likely to be the one you look for.

CLI

Easier than PHP-FPM as they should be fewer and have different arguments making them easier to identify.

ari@ari-ThinkPad-T580:~/Documents/perso/ariviere.github.io/_posts$ ps aux | grep php root 17670 0.0 0.2 621184 36772 ? Ss déc.06 0:01 php-fpm: master process (/etc/php/7.2/fpm/php-fpm.conf) ari 17700 0.0 0.9 671144 158260 ? S déc.06 0:07 php-fpm: pool www ari 17706 0.0 2.6 937588 433748 ? S déc.06 0:36 php-fpm: pool www ari 17708 0.0 0.8 664232 135236 ? S déc.06 0:06 php-fpm: pool www ari 30042 69.2 1.0 582588 165052 tty2 S+ 13:35 0:02 /usr/bin/php7.2 /home/ari/dev/myscript --with-options ari 30056 0.0 0.0 21536 1036 pts/8 S+ 13:35 0:00 grep --color=auto php 

Here, no doubt as I ran a single script, it is PID 30042.

Attach it to a debugger

I’ll use GDB as I’m more familiar with it, but the process should be adaptable to LLDB.

To attach the process, simply run

sudo gdb -p 30042 # replace with the PID you got earlier of course ;) 

Super user privileges is required to trace a process.

Get the current function call

In the GDB prompt, type print executor_globals.current_execute_data. Congrats, you accessed the top most execute_data PHP is currently executing!

Now let have a look to the member that structure exposes :

(gdb) print *executor_globals.current_execute_data $4 = < opline = 0x7fe5002df248, call = 0x0, return_value = 0x7fe519e1f6b0, func = 0x7fe4f8fe2558, This = < value = < lval = 140621403335168, dval = 6.9476204457892199e-310, counted = 0x7fe4f8cb2a00, str = 0x7fe4f8cb2a00, arr = 0x7fe4f8cb2a00, obj = 0x7fe4f8cb2a00, res = 0x7fe4f8cb2a00, ref = 0x7fe4f8cb2a00, ast = 0x7fe4f8cb2a00, zv = 0x7fe4f8cb2a00, ptr = 0x7fe4f8cb2a00, ce = 0x7fe4f8cb2a00, func = 0x7fe4f8cb2a00, ww = >, u1 = < v = < type = 8 '\b', type_flags = 4 '\004', const_flags = 2 '\002', reserved = 0 '\000' >, type_info = 132104 >, u2 = < next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0 >>, prev_execute_data = 0x7fe519e1f660, symbol_table = 0x1b00000001, run_time_cache = 0x7fe4f8fedb88, literals = 0x7fe5002df228 > 
  • func.common.function_name : The function or method name
  • func.common.scope.name : The class name (no matter it is a static method or not)
  • This : As you may guess, $this.
  • prev_execute_data : The previous execute_data entry of the linked list, useful to build a stack trace.

String representation

As you may know, strings in C are array of chars, or char *. When GDB prints a string by itself, it actually doesn’t know the variable is an array of characters and only prints the first character. To make it print the real string, use print (char*) followed by the entry name.

However, don’t rely too much on having char * directly on PHP source code. Strings are zend_string or even smart_str, themselves using zend_string. To get the value of a zend_string, you’ll need to access its member “val”.

To wrap that up, to get the function name as string, you’ll have to type:

(gdb) print (char *)executor_globals.current_execute_data.func.common.function_name.val $14 = 0x7fe4fbb48878 "getName" (gdb) print (char *)executor_globals.current_execute_data.func.common.scope.name.val $15 = 0x7fe4fc07ddf0 "Composer\\Package\\BasePackage" 

Previous stack entry

Now let get the calling function name:

(gdb) print (char *)executor_globals.current_execute_data.prev_execute_data.func.common.scope.name.val $16 = 0x7fe4fc07ddf0 "Composer\\Package\\BasePackage" (gdb) print (char *)executor_globals.current_execute_data.prev_execute_data.func.common.function_name.val $17 = 0x7fe4fc0799b8 "getUniqueName" 

How to get a real stack trace?

If you have tens calls in your stack, appending more and more prev_execute_data in the print-s is going to be painful (and even worst when using frameworks that generate interceptors and proxies as Magento does!). But don’t worry, we can make macros in GDB!

(gdb) define phpbt Type commands for definition of "phpbt". End with a line saying just "end". set $ed=executor_globals.current_execute_data while $ed print <(char*)((zend_execute_data *)$ed)->func.common.scope.name.val, (char*)((zend_execute_data *)$ed)->func.common.function_name.val> set $ed = ((zend_execute_data *)$ed)->prev_execute_data end end 
(gdb) phpbt $48 = $49 = $50 = $51 = $52 = $53 = $54 = $55 = $56 = $57 = $58 = $59 = $60 = $61 = $62 = $63 = "> $64 = $65 = $66 = $67 = $68 = $69 = $70 = $71 = "> $72 = $73 = $74 = $75 = $76 = Cannot access memory at address 0x8 

Don’t worry about the error on the last line, the macro is greatly perfectible, and doesn’t handle the case prev_execute_data == NULL. Actually, there are plenty of situations the macro doesn’t handle well: include, require, eval for instance. It may also not support generators and traits correctly but I never tested its behaviour in these situations.

Making things simple

To avoid typing the macro every time you need it, you can simply create the file ~/.gdbinit containing the macro:

define phpbt set $ed=executor_globals.current_execute_data while $ed set $scope=((zend_execute_data *)$ed)->func.common.scope set $funcname=((zend_execute_data *)$ed)->func.common.function_name.val if $scope print else print end set $ed = ((zend_execute_data *)$ed)->prev_execute_data end end 

Credits

The GDB macro is an adaptation I made from wikitech, as the original no longer works on PHP7.

Aurélien Rivière

Источник

Saved searches

Use saved searches to filter your results more quickly

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

Retrieves process list in a platform-independent way

christian-vigh-phpclasses/ProcessList

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Sign In Required

Please sign in to use Codespaces.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

Git stats

Files

Failed to load latest commit information.

README.md

The ProcessList class provides a platform-independent way to retrieve the list of processes running on your systems. It works both on the Windows and Unix platforms.

To retrieve the list of processes currently running on your system, simply use the following :

require ( 'ProcessList.phpclass' ) ; $ps = new ProcessList ( ) ; 

The $ps variable can now be accessed as an array to retrieve process information, which is simply an object of class Process :

foreach ( $ps as $process ) echo ( "PID : ProcessId>, COMMAND : Command>" ) ; 

Whether you are running on Windows or Unix, the properties exposed by the Process objects remain the same (see the Reference section).

For Windows platforms, you will need the following package :

http://www.phpclasses.org/package/10001-PHP-Provides-access-to-Windows-WMI.html 

A copy of the source code is provided here for your convenience, but it may not be the latest release.

The ProcessList class is a container class that allows you to retrieve information about individual processes. It implements the ArrayAccess and Iterator interfaces, so that you can loop through each process currently running on your system.

Each element of a ProcessList array is an object of class Process.

public function __construct ( $load = true ) ; 

Creates a process list object. If the $load parameter is true, the process list will be retrieved ; otherwise, you will need to call the Refresh() method later before looping through the list of available processes.

public function GetProcess ( $id ) ; 

Searches for a process having the specified $id.

Returns an object of class Process if found, or false otherwise.

public function GetProcessByName ( $name ) ; 

Searches for a process having the specified name. The name is given by the Command property of the Process object.

public function GetChildren ( $id ) ; 

Returns the children of the specified process, or an empty array if $id does not specify a valid process id.

Refreshes the current process list. This function can be called as many times as desired on the same ProcessList object.

The Process class does not contain methods, but simply expose properties that contain information about a process.

Contains the command-line arguments of the process. As for C (and PHP) programs, Argv[0] represents the command path.

Command name, without its leading path.

Full command line, including arguments.

CPU time consumed by the process, in the form «hh:mm:ss».

Dd of the parent process for this process.

Process id of the current process.

Process start time, in the form «yyyy-mm-dd hh:mm:ss».

Process title. On Windows systems, it will be the title of the process. Since there is no notion of process title on Unix systems, it will be set to the value of the Command property.

Attached tty. This information is useful mainly for Unix systems.

User name running the process. On Unix systems, this can be either a user name or a user id.

About

Retrieves process list in a platform-independent way

Источник

Оцените статью