Low-overhead sampling profiler for PHP 7+

Last update: May 25, 2022

phpspy

phpspy is a low-overhead sampling profiler for PHP. For now, it works with Linux 3.2+ x86_64 non-ZTS PHP 7.0+ with CLI, Apache, and FPM SAPIs.

Build Status

Demos

You can profile PHP scripts:

child

You can attach to running PHP processes:

attach cli

attach httpd

It has a top-like mode:

top

It can collect request info, memory usage, and variables:

advanced

You can also use it to make flamegraphs like this:

FlameGraph example

All with no changes to your application and minimal overhead.

Synopsis

$ git clone https://github.com/adsr/phpspy.git
Cloning into 'phpspy'...
...
$ cd phpspy
$ make
...
$ sudo ./phpspy --limit=1000 --pid=$(pgrep -n httpd) >traces
...
$ ./stackcollapse-phpspy.pl <traces | ./vendor/flamegraph.pl >flame.svg
$ google-chrome flame.svg # View flame.svg in browser

Build options

$ make                   # Build dependencies locally and statically link
$ # or
$ make phpspy_dynamic    # Dynamically link dependencies
$ # or
$ USE_ZEND=1 make ...    # Use Zend structs instead of built-in structs (requires php-dev or php-devel)

Usage

$ ./phpspy -h
Usage:
  phpspy [options] -p <pid>
  phpspy [options] -P <pgrep-args>
  phpspy [options] [--] <cmd>

Options:
  -h, --help                         Show this help
  -p, --pid=<pid>                    Trace PHP process at `pid`
  -P, --pgrep=<args>                 Concurrently trace processes that
                                       match pgrep `args` (see also `-T`)
  -T, --threads=<num>                Set number of threads to use with `-P`
                                       (default: 16)
  -s, --sleep-ns=<ns>                Sleep `ns` nanoseconds between traces
                                       (see also `-H`) (default: 10101010)
  -H, --rate-hz=<hz>                 Trace `hz` times per second
                                       (see also `-s`) (default: 99)
  -V, --php-version=<ver>            Set PHP version
                                       (default: auto;
                                       supported: 70 71 72 73 74)
  -l, --limit=<num>                  Limit total number of traces to capture
                                       (approximate limit in pgrep mode)
                                       (default: 0; 0=unlimited)
  -i, --time-limit-ms=<ms>           Stop tracing after `ms` milliseconds
                                       (second granularity in pgrep mode)
                                       (default: 0; 0=unlimited)
  -n, --max-depth=<max>              Set max stack trace depth
                                       (default: -1; -1=unlimited)
  -r, --request-info=<opts>          Set request info parts to capture
                                       (q=query c=cookie u=uri p=path
                                       capital=negation)
                                       (default: QCUP; none)
  -m, --memory-usage                 Capture peak and current memory usage
                                       with each trace (requires target PHP
                                       process to have debug symbols)
  -o, --output=<path>                Write phpspy output to `path`
                                       (default: -; -=stdout)
  -O, --child-stdout=<path>          Write child stdout to `path`
                                       (default: phpspy.%d.out)
  -E, --child-stderr=<path>          Write child stderr to `path`
                                       (default: phpspy.%d.err)
  -x, --addr-executor-globals=<hex>  Set address of executor_globals in hex
                                       (default: 0; 0=find dynamically)
  -a, --addr-sapi-globals=<hex>      Set address of sapi_globals in hex
                                       (default: 0; 0=find dynamically)
  -1, --single-line                  Output in single-line mode
  -b, --buffer-size=<size>           Set output buffer size to `size`.
                                       Note: In `-P` mode, setting this
                                       above PIPE_BUF (4096) may lead to
                                       interlaced writes across threads.
                                       (default: 4096)
  -f, --filter=<regex>               Filter output by POSIX regex
                                       (default: none)
  -F, --filter-negate=<regex>        Same as `-f` except negated
  -d, --verbose-fields=<opts>        Set verbose output fields
                                       (p=pid t=timestamp
                                       capital=negation)
                                       (default: PT; none)
  -c, --continue-on-error            Attempt to continue tracing after
                                       encountering an error
  -#, --comment=<any>                Ignored; intended for self-documenting
                                       commands
  [email protected], --nothing                      Ignored
  -v, --version                      Print phpspy version and exit

Experimental options:
  -j, --event-handler=<handler>      Set event handler (fout, callgrind)
                                       (default: fout)
  -S, --pause-process                Pause process while reading stacktrace
                                       (unsafe for production!)
  -e, --peek-var=<varspec>           Peek at the contents of the var located
                                       at `varspec`, which has the format:
                                       <varname>@<path>:<lineno>
                                       <varname>@<path>:<start>-<end>
                                       e.g., [email protected]/path/to.php:10-20
  -g, --peek-global=<glospec>        Peek at the contents of a global var
                                       located at `glospec`, which has
                                       the format: <global>.<key>
                                       where <global> is one of:
                                       post|get|cookie|server|files|globals
                                       e.g., server.REQUEST_TIME
  -t, --top                          Show dynamic top-like output

Example (variable peek)

$ sudo ./phpspy -e '[email protected]/var/www/test/lib/test.php:12' -p $(pgrep -n httpd) | grep varpeek
# varpeek [email protected]/var/www/test/lib/test.php:12 = 42
# varpeek [email protected]/var/www/test/lib/test.php:12 = 42
# varpeek [email protected]/var/www/test/lib/test.php:12 = 43
# varpeek [email protected]/var/www/test/lib/test.php:12 = 44
...

Example (pgrep daemon mode)

$ sudo ./phpspy -H1 -T4 -P '-x php'
0 proc_open <internal>:-1
1 system_with_timeout /home/adam/php-src/run-tests.php:1137
2 run_test /home/adam/php-src/run-tests.php:1937
3 run_all_tests /home/adam/php-src/run-tests.php:1215
4 <main> /home/adam/php-src/run-tests.php:986
# - - - - -
...
^C
main_pgrep finished gracefully

Example (httpd)

$ sudo ./phpspy -p $(pgrep -n httpd)
0 Memcached::get <internal>:-1
1 Cache_MemcachedToggleable::get /foo/bar/lib/Cache/MemcachedToggleable.php:26
2 Cache_Memcached::get /foo/bar/lib/Cache/Memcached.php:251
3 IpDb_CacheBase::getFromCache /foo/bar/lib/IpDb/CacheBase.php:165
4 IpDb_CacheBase::get /foo/bar/lib/IpDb/CacheBase.php:107
5 IpDb_CacheBase::contains /foo/bar/lib/IpDb/CacheBase.php:70
6 IpDb_Botnet::has /foo/bar/lib/IpDb/Botnet.php:32
7 Security_Rule_IpAddr::__construct /foo/bar/lib/Security/Rule/IpAddr.php:53
8 Security_Rule_HttpRequestContext::initVariables /foo/bar/lib/Security/Rule/HttpRequestContext.php:22
9 Security_Rule_Context::__construct /foo/bar/lib/Security/Rule/Context.php:44
10 Security_Rule_Engine::getContextByName /foo/bar/lib/Security/Rule/Engine.php:225
11 Security_Rule_Engine::getContextByLocation /foo/bar/lib/Security/Rule/Engine.php:210
12 Security_Rule_Engine::evaluateActionRules /foo/bar/lib/Security/Rule/Engine.php:116
13 <main> /foo/bar/lib/bootstrap/api.php:49
14 <main> /foo/bar/htdocs/v3/public.php:5
# - - - - -
...

Example (cli child)

$ ./phpspy -- php -r 'usleep(100000);'
0 usleep <internal>:-1
1 <main> <internal>:-1

0 usleep <internal>:-1
1 <main> <internal>:-1

0 usleep <internal>:-1
1 <main> <internal>:-1

0 usleep <internal>:-1
1 <main> <internal>:-1

0 usleep <internal>:-1
1 <main> <internal>:-1

0 usleep <internal>:-1
1 <main> <internal>:-1

process_vm_readv: No such process

Example (cli attach)

$ php -r 'sleep(10);' &
[1] 28586
$ sudo ./phpspy -p 28586
0 sleep <internal>:-1
1 <main> <internal>:-1
...

Example (docker)

$ docker build . -t phpspy
$ docker run -it --cap-add SYS_PTRACE phpspy:latest ./phpspy/phpspy -V73 -r -- php -r 'sleep(1);'
0 sleep <internal>:-1
1 <main> <internal>:-1
...

Known bugs

  • phpspy may not work with a chrooted mod_php process whose binary lives inside overlayfs. (See #109.)

See also

TODO

GitHub

https://github.com/adsr/phpspy
Comments
  • 1. Not working with php-fpm

    Hi. I've built binary from git master 7e0b062022a86a3cec2744ff400756cee67fa1de.

    Trying to inspect my php-fpm process, but it fails.

    Output:

    >>./phpspy -V73 -p 30516
    
    objdump: '/proc/30516/root//usr/local/sbin/php-fpm': No such file
    popen_read_line: No stdout; cmd=objdump -p /proc/30516/root//usr/local/sbin/php-fpm | awk '/LOAD/{print $5; exit}'
    get_php_base_addr: Failed to get virt_addr
    

    PHP version:

    >> php -v
    
    PHP 7.3.4 (cli) (built: Apr 20 2019 04:26:18) ( NTS )
    Copyright (c) 1997-2018 The PHP Group
    Zend Engine v3.3.4, Copyright (c) 1998-2018 Zend Technologies
        with Zend OPcache v7.3.4, Copyright (c) 1999-2018, by Zend Technologies
    

    OS version:

    >> uname -a
    Linux ai-w5 4.15.0-91-generic #92-Ubuntu SMP Fri Feb 28 11:09:48 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
    

    Any suggestions?

    Reviewed by akademic at 2020-05-07 07:56
  • 2. Request: Update README.md with list of OS dependencies required to run project.

    Current: No list / command of OS dependencies are indicated prior to attempting to 'make' the application.

    Desired: List OS packages / Add command examples to install.

    Example: For Ubuntu 18.0x autoconf bison flex gawk git liblzma-dev libbz2-dev make python zlibc zlib1g zlib1g-dev

    Thank you.

    Reviewed by davidjeddy at 2018-11-15 17:38
  • 3. can't compile on OSX, missing libpthread

    With a freshly updated repo, I try to make phpspy :

    phpspy$ make
    Makefile:22: *** Need libpthread.  Stop.
    
    

    libpthread is installed, available in /usr/lib/libpthread.dylib $LDFLAGS is empty

    a direct call to ld gives this (based on the Makefile that leads to the failure) :

    $ ld -lpthread 
    ld: warning: No version-min specified on command line
    ld: warning: -arch not specified
    ld: warning: -macosx_version_min not specified, assuming 10.11
    Undefined symbols for architecture x86_64:
      "_main", referenced from:
         implicit entry/start for main executable
    ld: symbol(s) not found for inferred architecture x86_64
    

    How can I fix this ?

    Reviewed by dseguy at 2018-11-21 09:44
  • 4. Build failure on Ubuntu 18.04

    Attempting to build the tool on WSL Ubuntu 18.04; get's almost done but throws some errors at me.

      ...
      CCLD     libebl_i386.so
      CCLD     libebl_sh.so
      CCLD     libebl_x86_64.so
      CCLD     libebl_ia64.so
      CCLD     libebl_alpha.so
      CCLD     libebl_arm.so
      CCLD     libebl_aarch64.so
      CCLD     libebl_sparc.so
      CCLD     libebl_ppc.so
      CCLD     libebl_ppc64.so
      CCLD     libebl_s390.so
      CCLD     libebl_tilegx.so
      CCLD     libebl_m68k.so
      CCLD     libebl_bpf.so
      CCLD     libebl_riscv.so
    rm sparc_initreg.o ppc_cfi.o m68k_cfi.o riscv_initreg.o x86_64_symbol.o ppc_auxv.o alpha_symbol.o arm_auxv.o m68k_symbol.o riscv_corenote.o i386_unwind.o sparc_symbol.o x86_64_syscall.o aarch64_init.o ppc_initreg.o i386_retval.o sparc_corenote.o m68k_initreg.o sparc_cfi.o ppc_regs.o ppc_init.o s390_init.o ppc_syscall.o tilegx_symbol.o ia64_symbol.o i386_corenote.o m68k_corenote.o s390_cfi.o sh_regs.o i386_init.o ppc_corenote.o alpha_init.o i386_auxv.o m68k_init.o x32_corenote.o s390_initreg.o arm_attrs.o ppc64_resolve_sym.o x86_64_initreg.o ia64_init.o arm_initreg.o ppc64_retval.o x86_64_retval.o ia64_retval.o ppc64_unwind.o tilegx_init.o arm_cfi.o aarch64_initreg.o sparc_attrs.o i386_syscall.o arm_retval.o bpf_symbol.o arm_symbol.o x86_64_corenote.o aarch64_symbol.o s390_unwind.o aarch64_corenote.o alpha_corenote.o sparc64_corenote.o x86_64_cfi.o sh_symbol.o x86_64_init.o i386_regs.o ppc64_corenote.o s390_retval.o ppc_retval.o bpf_regs.o bpf_init.o ia64_regs.o alpha_retval.o arm_corenote.o aarch64_cfi.o aarch64_unwind.o sparc_init.o i386_initreg.o x86_64_unwind.o aarch64_retval.o s390_corenote.o riscv_regs.o sparc_auxv.o riscv_symbol.o s390_symbol.o sparc_retval.o s390x_corenote.o riscv_init.o x86_64_regs.o sh_retval.o sh_corenote.o i386_cfi.o alpha_regs.o ppc64_symbol.o m68k_retval.o arm_regs.o aarch64_regs.o sparc_regs.o i386_symbol.o m68k_regs.o alpha_auxv.o ppc_symbol.o arm_init.o s390_regs.o tilegx_retval.o riscv_cfi.o ppc64_init.o ppc_attrs.o tilegx_regs.o tilegx_corenote.o sh_init.o
    make[1]: Leaving directory '/home/ubuntu/phpspy/vendor/elfutils'
    cd vendor/termbox && ./waf configure && ./waf --targets=termbox_static
    Setting top to                           : /home/ubuntu/phpspy/vendor/termbox
    Setting out to                           : /home/ubuntu/phpspy/vendor/termbox/build
    Checking for 'gcc' (C compiler)          : /usr/bin/gcc
    'configure' finished successfully (0.094s)
    Waf: Entering directory `/home/ubuntu/phpspy/vendor/termbox/build'
    [1/3] c: src/termbox.c -> build/src/termbox.c.2.o
    [2/3] c: src/utf8.c -> build/src/utf8.c.2.o
    ../src/termbox.c: In function ‘sigwinch_handler’:
    ../src/termbox.c:582:2: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Wunused-result]
      write(winch_fds[1], &zzz, sizeof(int));
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    In file included from ../src/termbox.c:19:0:
    ../src/bytebuffer.inl: In function ‘bytebuffer_flush’:
    ../src/bytebuffer.inl:58:2: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Wunused-result]
      write(fd, b->buf, b->len);
      ^~~~~~~~~~~~~~~~~~~~~~~~~
    ../src/termbox.c: In function ‘wait_fill_event’:
    ../src/termbox.c:672:4: warning: ignoring return value of ‘read’, declared with attribute warn_unused_result [-Wunused-result]
        read(winch_fds[0], &zzz, sizeof(int));
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    [3/3] cstlib: build/src/termbox.c.2.o build/src/utf8.c.2.o -> build/src/libtermbox.a
    Waf: Leaving directory `/home/ubuntu/phpspy/vendor/termbox/build'
    'build' finished successfully (0.669s)
    cc -std=c99 -Wall -Wextra -pedantic -g -Ofast -pthread  -I. -I./vendor -Ivendor/termbox/src -Ivendor/elfutils/lib -Ivendor/elfutils/libelf -Ivendor/elfutils/libebl -Ivendor/elfutils/libdwelf -Ivendor/elfutils/libdwfl -Ivendor/elfutils/libdw -Ivendor/elfutils/libcpu -Ivendor/elfutils/backends -Ivendor/symlinks/libdwfl -Ivendor/termbox/src/ -DUSE_TERMBOX=1 phpspy.c pgrep.c top.c addr_libdw.c event_fout.c -o phpspy  -Lvendor/elfutils/libdw -Lvendor/elfutils/libelf -Lvendor/elfutils/libdwfl -Lvendor/elfutils/libebl -Lvendor/elfutils/libdwelf  -Wl,-Bstatic -ldw -lelf -ldwfl -lebl -ldwelf -Lvendor/elfutils/lib -leu -Lvendor/elfutils/backends -lebl_x86_64_pic -Wl,-Bdynamic -ldl -lz -llzma -lbz2 -Wl,-Bstatic -Lvendor/termbox/build/src/ -ltermbox -Wl,-Bdynamic
    /usr/bin/ld: cannot find -llzma
    /usr/bin/ld: cannot find -lbz2
    collect2: error: ld returned 1 exit status
    Makefile:46: recipe for target 'phpspy_static' failed
    make: *** [phpspy_static] Error 1
    [email protected]:~/phpspy$
    [email protected]:~/phpspy$ make phpspy_dynamic
    Makefile:49: *** Need libdw. Hint: try `make phpspy_static`.  Stop.
    [email protected]:~/phpspy$ make phpspy_static
    cc -std=c99 -Wall -Wextra -pedantic -g -Ofast -pthread  -I. -I./vendor -Ivendor/termbox/src -Ivendor/elfutils/lib -Ivendor/elfutils/libelf -Ivendor/elfutils/libebl -Ivendor/elfutils/libdwelf -Ivendor/elfutils/libdwfl -Ivendor/elfutils/libdw -Ivendor/elfutils/libcpu -Ivendor/elfutils/backends -Ivendor/symlinks/libdwfl -Ivendor/termbox/src/ -DUSE_TERMBOX=1 phpspy.c pgrep.c top.c addr_libdw.c event_fout.c -o phpspy  -Lvendor/elfutils/libdw -Lvendor/elfutils/libelf -Lvendor/elfutils/libdwfl -Lvendor/elfutils/libebl -Lvendor/elfutils/libdwelf  -Wl,-Bstatic -ldw -lelf -ldwfl -lebl -ldwelf -Lvendor/elfutils/lib -leu -Lvendor/elfutils/backends -lebl_x86_64_pic -Wl,-Bdynamic -ldl -lz -llzma -lbz2 -Wl,-Bstatic -Lvendor/termbox/build/src/ -ltermbox -Wl,-Bdynamic
    /usr/bin/ld: cannot find -llzma
    /usr/bin/ld: cannot find -lbz2
    collect2: error: ld returned 1 exit status
    Makefile:46: recipe for target 'phpspy_static' failed
    make: *** [phpspy_static] Error 1
    [email protected]:~/phpspy$
    

    Any ideas concerning the collect2: error: ld returned 1 exit status ?

    Reviewed by davidjeddy at 2018-11-15 18:15
  • 5. Add shell escaping

    • Not implemented for the pgrep command in pgrep-mode - gonna trust the user on that :)
    • Not implemented for the version_cmd because it's not needed there (only numbers are encoded, not strings)
    • Not escaping symbol names in get_symbol_offset because they are constants throughout the project.

    I tested it with a PHP binary I put in /tmp/php''$(echo > yyy). Works like a charm (and no yyy file created in cwd, but neither does in master). Also tested with /tmp/php$(echo > abc), no abc file created in cwd. On master - both don't work & abc is created.

    Also dropped a small commit that fixes a missing s(n)printf overflow (like https://github.com/adsr/phpspy/commit/3120b8d1bf205639970aa2de566b888aba5b6884)

    Closes: #81

    Reviewed by Jongy at 2021-07-24 17:24
  • 6. Wrong stack reported

    • PHP version: 7.2.29
    • phpspy version: v0.5.0 USE_ZEND=n (commit 58575aa88ccb96db963231864d6409767e133b38)

    Hi there. Is there a chance that the sampling reports wrong stack, especially in presence of calls to prem_match? I have a function that calls preg_match, str_replace, preg_quate, and array_shift. Flame graph reports that it takes 32% of runtime, of total 10 seconds. However, when I replace the implementation with something simpler that works correct for my input, the flame graph says it takes 1% of runtime, while the total runtime still remains nearly 10 seconds. I know this is somehow a random experiment, but I tried it many times with different sampling rates, with/without opcache. All the same.

    Here is the function that I think causes the wrong reports:

        protected function getExplicitKeys($attribute)
        {
            // Uncommenting the next line for my input returns the valid output. 
            // return [(int) trim($attribute, 'aray.')];
            $pattern = str_replace('\*', '([^\.]+)', preg_quote($this->getPrimaryAttribute($attribute), '/'));
    
            if (preg_match('/^'.$pattern.'/', $attribute, $keys)) {
                array_shift($keys);
    
                return $keys;
            }
    
            return [];
        }
    
    Reviewed by halaei at 2020-04-24 12:40
  • 7. Not enough space

    I run the following command to inspect a php CLI command: ./phpspy --limit=10000 --pid=34311 I see the following error lines filling the output:

    ...
    event_handler_fout_snprintf: Not enough space in buffer; truncating
    event_handler_fout_snprintf: Not enough space in buffer; truncating
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7fdb00000002 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7fdb00000408 size=132
    event_handler_fout_snprintf: Not enough space in buffer; truncating
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7fdb00001406 size=132
    event_handler_fout_snprintf: Not enough space in buffer; truncating
    event_handler_fout_snprintf: Not enough space in buffer; truncating
    ...
    event_handler_fout_snprintf: Not enough space in buffer; truncating
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7fdb00000408 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x200000001 size=132
    event_handler_fout_snprintf: Not enough space in buffer; truncating
    event_handler_fout_snprintf: Not enough space in buffer; truncating
    ...
    

    The result flame chart has only a couple of columns.

    Reviewed by halaei at 2020-03-18 14:13
  • 8. Errors being written to stdout

    Seems like this commit 3b9df5667d6774a430bf006a264a0b9b865b4331 introduced a problem causing stderr being written to stdout. After checking out the prior commit, that problem goes away.

    Reviewed by ttk at 2019-12-18 19:34
  • 9. Unable to generate flame graph

    Hi, testing out phpspy on PHP7.4 generate the following error and flame.svg is empty

    [email protected] /usr/local/src/phpspy # ./phpspy -l 1000 -P php-fpm | ./stackcollapse-phpspy.pl | ./vendor/flamegraph.pl > flame.svg
    popen_read_line: No stdout; cmd=objdump -Tt /proc/7314/root//usr/sbin/php-fpm7.3 | awk '/ basic_functions_module$/{print $1; exit}'
    popen_read_line: No stdout; cmd=objdump -Tt /proc/11760/root//usr/sbin/php-fpm7.3 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/20179/root//usr/sbin/php-fpm7.4 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/17015/root//usr/sbin/php-fpm7.4 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/7950/root//usr/sbin/php-fpm7.3 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/13579/root//usr/sbin/php-fpm7.4 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/17351/root//usr/sbin/php-fpm7.4 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/13405/root//usr/sbin/php-fpm7.3 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/22403/root//usr/sbin/php-fpm7.4 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/14189/root//usr/sbin/php-fpm7.3 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/17350/root//usr/sbin/php-fpm7.4 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/22016/root//usr/sbin/php-fpm7.4 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/14443/root//usr/sbin/php-fpm7.3 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/13545/root//usr/sbin/php-fpm7.3 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/14654/root//usr/sbin/php-fpm7.3 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    popen_read_line: No stdout; cmd=objdump -Tt /proc/31548/root//usr/sbin/php-fpm7.3 | awk '/ basic_functions_module$/{print $1; exit}'
    get_symbol_offset: Failed
    copy_proc_mem: Not copying zfunc; raddr is NULL
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x100100000 size=148
    copy_proc_mem: Not copying zfunc; raddr is NULL
    copy_proc_mem: Failed to copy class_name; err=Bad address raddr=0x486478ec83485355 size=25
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f0a00000308 size=148
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f0a00000007 size=148
    ^Cmain_pgrep finished gracefully
    
    Reviewed by ArabCoders at 2019-12-08 12:22
  • 10. Fails to run with message "unexpected token `deleted'"

    ./phpspy -V72 -p 127743
    sh: -c: line 0: syntax error near unexpected token `deleted'
    sh: -c: line 0: `objdump -p /proc/127743/root//usr/bin/php;5cde72be (deleted) | awk '/LOAD/{print $5; exit}''
    popen_read_line: No stdout; cmd=objdump -p /proc/127743/root//usr/bin/php;5cde72be (deleted) | awk '/LOAD/{print $5; exit}'
    get_php_base_addr: Failed to get virt_addr
    

    For some reason the binary seems to be having ;5cde72be (deleted) appended which causes this to fail.

    Running the command without this suffix gives me an address back.

    objdump -p /proc/127743/root//usr/bin/php | awk '/LOAD/{print $5; exit}'
    0x0000000000000000
    

    This is on CentOS using Webtatic php72w.

    Reviewed by ElvenSpellmaker at 2019-09-23 11:03
  • 11. Some errors occurred when generate frame

    PHP

    PHP 7.1.26-1+ubuntu16.04.1+deb.sury.org+1 (cli) (built: Jan 11 2019 14:13:16) ( NTS )
    Copyright (c) 1997-2018 The PHP Group
    Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies
        with Zend OPcache v7.1.26-1+ubuntu16.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies
    
    Linux admin 4.4.0-1072-aws #82-Ubuntu SMP Fri Nov 2 15:00:21 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
    

    Command:

    ./phpspy -l 10000 -p 23465 | ./stackcollapse-phpspy.pl | ./vendor/flamegraph.pl > frame.svg
    

    output:

    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00000003 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x100000000 size=132
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0xffffffff00001c07 size=25
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00000004 size=132
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0x6 size=25
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x800001c07 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00001406 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00000002 size=132
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0x6 size=25
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00000004 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00000c08 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00000003 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00001406 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00000c08 size=132
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0x4effe0000000007f size=25
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x563b00000003 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x4245445f444e455a size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00000c08 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0xa00000001 size=132
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0x7f4f002000 size=25
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0xffffffff00000006 size=25
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0xb42b97e08712010 size=25
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x73 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x563b00000000 size=132
    copy_proc_mem: Not copying zfunc; raddr is NULL
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x61775f6c7173796d size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00000001 size=132
    copy_proc_mem: Not copying zfunc; raddr is NULL
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0xffe0000000007f4f size=25
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0xe0000000007f4f00 size=25
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0xb42b977c3312010 size=25
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00001c07 size=132
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0xffe0000000007f4f size=25
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x3939646200000c08 size=132
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0xffffffff00000001 size=25
    copy_proc_mem: Not copying zfunc; raddr is NULL
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f4e00001406 size=132
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x200000c08 size=132
    copy_proc_mem: Failed to copy function_name; err=Bad address raddr=0x7f4effe000000000 size=25
    
    Reviewed by lvshuang at 2019-03-01 03:27
  • 12. Skip frames where zfunc is null

    Previously, it would print the message copy_proc_mem: Not copying zfunc; raddr is NULL even if subsequent frames from execute_data could have been read (e.g. Redis::evalsha and other redis methods were affected by this when instrumenting a use of redis where the server was 100 milliseconds away)

    Before, it would not print any of the subsequent frames making the stack trace less representative - the try macro would immediately return. After this PR, skip frames where the function is null.

    Reviewed by TysonAndre at 2020-10-09 19:27
  • 13. Is it possible to run phpspy on a core dump?

    E.g. given the core dump and the path to the php binary (and possibly shared libraries it uses), would showing the php stack trace with php method names be possible.

    This may be useful when debuginfo is missing for the php binary.

    Reviewed by TysonAndre at 2020-01-29 23:24
  • 14. [WIP] Module Support

    This is a WIP for module support, and depends on CMake PR

    The basic idea is that phpspy should export handler pointer, LD_PRELOAD is used to load the module and set that handler ...

    TODO:

    • CMake build for module
    • Support standard makefile build ?
    • Have phpspy detect when handler is not builtin, force pid mode only ?

    This is nothing like complete, but a thing to get the conversation started ...

    Open questions:

    • Depend on libdl so that phpspy may load the modules (no preload)
    • Support makefiles (ewww)
    • Define module structure with individual handlers for each event, so that a module may set handlers for events it intends to process.

    Implements #60

    Reviewed by krakjoe at 2019-03-01 08:44
  • 15. add cmake build

    initial cmake build, and some readme ...

    The custom target for submodules is a bit strange for a cmake build, but tries to immitate (somewhat) the original Makefile ...

    It might be better to not have that as a target, and just include in instructions that you should update submodules, or there might be a better way to setup the custom target ... I couldn't find one. You may execute the process as part of configuring the build, and then make clean && make would not invoke the submodules target every time, but it seemed somewhat simpler to do it this way so install is "make -jX install".

    Reviewed by krakjoe at 2019-02-28 22:10
Silex Web Profiler

The Silex Web Profiler service provider allows you to use the wonderful Symfony web debug toolbar and the Symfony profiler in your Silex 2.x application.

Nov 12, 2021
Handle PHP errors, dump variables, execute PHP code remotely in Google Chrome

PHP Console server library PHP Console allows you to handle PHP errors & exceptions, dump variables, execute PHP code remotely and many other things u

May 19, 2022
PHP APM (Alternative PHP Monitor)

APM (Alternative PHP Monitor) APM (Alternative PHP Monitor) is a monitoring extension enabling native Application Performance Management (APM) for PHP

May 19, 2022
Zipkin PHP is the official PHP Tracer implementation for Zipkin

Zipkin PHP is the official PHP Tracer implementation for Zipkin, supported by the OpenZipkin community. Installation composer require openz

May 14, 2022
Debug bar for PHP
Debug bar for PHP

PHP Debug Bar Displays a debug bar in the browser with information from php. No more var_dump() in your code! Features: Generic debug bar Easy to inte

May 26, 2022
Xdebug — Step Debugger and Debugging Aid for PHP

Xdebug Xdebug is a debugging tool for PHP. It provides step-debugging and a whole range of development aids, such as stack traces, a code profiler, fe

May 25, 2022
Kint - a powerful and modern PHP debugging tool.
Kint - a powerful and modern PHP debugging tool.

Kint - debugging helper for PHP developers What am I looking at? At first glance Kint is just a pretty replacement for var_dump(), print_r() and debug

May 14, 2022
PHP Benchmarking framework
PHP Benchmarking framework

PHPBench is a benchmark runner for PHP analogous to PHPUnit but for performance rather than correctness. Features include: Revolutions: Repeat your co

May 22, 2022
The Interactive PHP Debugger

The interactive PHP debugger Implemented as a SAPI module, phpdbg can exert complete control over the environment without impacting the functionality

Apr 26, 2022
Dontbug is a reverse debugger for PHP
Dontbug is a reverse debugger for PHP

Dontbug Debugger Dontbug is a reverse debugger (aka time travel debugger) for PHP. It allows you to record the execution of PHP scripts (in command li

May 8, 2022
PHP Debug Console
PHP Debug Console

PHP Console A web console to try your PHP code into Creating a test file or using php's interactive mode can be a bit cumbersome to try random php sni

May 14, 2022
Php Debugger to run in terminal to debug your code easily.
Php Debugger to run in terminal to debug your code easily.

What is Dephpugger? Dephpugger (read depugger) is an open source lib to make a debug in php direct in terminal, without necessary configure an IDE. Th

Jan 12, 2022
PCOV - CodeCoverage compatible driver for PHP

PCOV A self contained CodeCoverage compatible driver for PHP Requirements and Installation See INSTALL.md API /** * Shall start recording coverage in

May 6, 2022
The VarDumper component provides mechanisms for walking through any arbitrary PHP variable. It provides a better dump() function that you can use instead of var_dump().

VarDumper Component The VarDumper component provides mechanisms for walking through any arbitrary PHP variable. It provides a better dump() function t

May 18, 2022
PHP errors for cool kids
PHP errors for cool kids

whoops PHP errors for cool kids whoops is an error handler framework for PHP. Out-of-the-box, it provides a pretty error interface that helps you debu

May 18, 2022
Clockwork - php dev tools in your browser - server-side component
Clockwork - php dev tools in your browser - server-side component

Clockwork is a development tool for PHP available right in your browser. Clockwork gives you an insight into your application runtime - including requ

May 18, 2022
Laravel Debugbar (Integrates PHP Debug Bar)
Laravel Debugbar (Integrates PHP Debug Bar)

Laravel Debugbar This is a package to integrate PHP Debug Bar with Laravel. It includes a ServiceProvider to register the debugbar and attach it to th

May 24, 2022
This package connects a Laravel Octance application with Tideways for PHP Monitoring, Profiling and Exception Tracking.

Tideways Middleware for Laravel Octane This package connects a Laravel Octance application with Tideways for PHP Monitoring, Profiling and Exception T

Jan 6, 2022
A tool to profile mysql queries in php env.
A tool to profile mysql queries in php env.

MysqlQueryProfiler This tool helps you to quickly profile a mysql query in a PHP 7.4+ environnement. You can also compare 2 queries. This image shows

Jul 30, 2021