Low-overhead sampling profiler for PHP 7+

Overview

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
  -@, --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., xyz@/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 'i@/var/www/test/lib/test.php:12' -p $(pgrep -n httpd) | grep varpeek
# varpeek i@/var/www/test/lib/test.php:12 = 42
# varpeek i@/var/www/test/lib/test.php:12 = 42
# varpeek i@/var/www/test/lib/test.php:12 = 43
# varpeek i@/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

Comments
  • Not working with php-fpm

    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?

    opened by akademic 9
  • Skip frames where zfunc is null

    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.

    hacktoberfest-accepted 
    opened by TysonAndre 6
  • Request: Update README.md with list of OS dependencies required to run project.

    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.

    opened by davidjeddy 6
  • Aarch64 support

    Aarch64 support

    Is Aarch64 supported? The README mentions x86_64 explicitly but I didn't find any x86 reference in the code, though. I didn't try building for Aarch64 so far. I'll post here if I try.

    I'd like to enable phpspy for our Aarch64 gProfiler build.

    opened by Jongy 5
  • can't compile on OSX, missing libpthread

    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 ?

    opened by dseguy 4
  • Build failure on Ubuntu 18.04

    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
    ubuntu@CPX-U6PE5MO3CD9:~/phpspy$
    ubuntu@CPX-U6PE5MO3CD9:~/phpspy$ make phpspy_dynamic
    Makefile:49: *** Need libdw. Hint: try `make phpspy_static`.  Stop.
    ubuntu@CPX-U6PE5MO3CD9:~/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
    ubuntu@CPX-U6PE5MO3CD9:~/phpspy$
    

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

    opened by davidjeddy 4
  • Add shell escaping

    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

    opened by Jongy 3
  • Wrong stack reported

    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 [];
        }
    
    opened by halaei 3
  • Not enough space

    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.

    opened by halaei 3
  • Errors being written to stdout

    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.

    opened by ttk 3
  • Unable to generate flame graph

    Unable to generate flame graph

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

    root@server /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
    
    opened by ArabCoders 3
  • Request info atomic write

    Request info atomic write

    We using phpspy and parse its stdout with another script. Also we use tags from --request-info=QCuP to add labels to traces in pyroscope.

    Full command looks like this:

    phpspy --max-depth=-1 --time-limit-ms=299000 --threads=16 --rate-hz=25 --buffer-size=65536 -P '-x "php-fpm|php-fpm[0-9]\.[0-9]" | shuf' --request-info=QCuP 2>/dev/null | php /pyrospy/pyrospy.php run --pyroscope=https://pyroscope.ourdomain.dev --rateHz=25 --app=XXXX --tags=host=`printenv HOSTNAME` --tags=role=web
    

    But in rare cases request-info is mixed in stdout with next trace. And we got such tags in pyroscpope: Снимок экрана 2022-11-30 в 18 29 24

    We ve added some sanitisation to remove such invalid tags, but there should be a better fix :)

    opened by xtrime-ru 0
  • Add option to retry reading the stack trace of a running application immediately

    Add option to retry reading the stack trace of a running application immediately

    See https://github.com/adsr/phpspy/pull/111#issuecomment-1288298776 for context

    --- a/phpspy.c
    +++ b/phpspy.c
    @@ -428,6 +428,9 @@ int main_pid(pid_t pid) {
             if (opt_pause) rv |= pause_pid(pid);
             rv |= do_trace_ptr(&context);
             if (opt_pause) rv |= unpause_pid(pid);
    +        struct timeval cur;
    +        gettimeofday(&cur, NULL);
    +        fprintf(stderr, "%.6f rv=%d\n", cur.tv_sec + 1.e-6 * cur.tv_usec, (int)rv);
    

    Observed: When copy_proc_mem fails (i.e. both rv != 0 and the context's depth < 0 ) for the first function on the stack trace, it sleeps for --sleep-ms seconds Expected: There should be an option to aggressively retry printing the stack from the first step immediately without sleeping, on failure, so that the phpspy output reflects a running application's stack trace accurately, and has traces from as many milliseconds as possible. (Optionally suppress the repeated warnings until the time elapses or the first success) Alternative: Print a suggestion to use --pause-process when copy_proc_mem fails if that option is not set, mentioning that it is not safe in production

    1666575804.829005 rv=0
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f7e00000308 size=156
    1666575804.839143 rv=0
    1666575804.849331 Printed frames 2 skipped 1
    0 Phan\Language\Type::make /path/to/phan/src/Phan/Language/Type.php:427
    0 Phan\Language\Type::make /path/to/phan/src/Phan/Language/Type.php:427
    
    1666575804.849365 rv=0
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x7f7e00000007 size=156
    1666575804.859566 rv=0
    copy_proc_mem: Failed to copy zfunc; err=Bad address raddr=0x307 size=156
    1666575804.869782 rv=0
    0 file_get_contents <internal>:-1
    1 Phan\Library\FileCache::getOrReadEntry /path/to/phan/src/Phan/Library/FileCache.php:91
    2 Phan\Analysis::parseFile /path/to/phan/src/Phan/Analysis.php:73
    3 Phan\Phan::analyzeFileList /path/to/phan/src/Phan/Phan.php:196
    4 <main> /path/to/phan/src/phan.php:1
    5 <main> /path/to/phan/phan:1
    

    Workarounds

    https://github.com/adsr/phpspy#demos doesn't mention --pause-process in the instructions for profiling, but the help text in the README does.

      -S, --pause-process                Pause process while reading stacktrace
                                           (unsafe for production!)
    
    opened by TysonAndre 2
  • Is it possible to run phpspy on a core dump?

    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.

    opened by TysonAndre 1
  • [WIP] Module Support

    [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

    opened by krakjoe 0
Releases(v0.5.0)
Owner
Adam
Adam
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.

Silex 209 Dec 5, 2022
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

Sergey 1.4k Dec 25, 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

Patrick Allaert 310 Dec 4, 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

Open Zipkin 250 Nov 12, 2022
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

Maxime Bouroumeau-Fuseau 4k Jan 8, 2023
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

Xdebug 2.8k Jan 3, 2023
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

null 2.7k Dec 25, 2022
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

PHPBench 1.7k Jan 2, 2023
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

Joe Watkins 841 Oct 9, 2022
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

Sidharth Kshatriya 709 Dec 30, 2022
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

Jordi Boggiano 523 Nov 7, 2022
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

Renato Cassino 190 Dec 3, 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

Joe Watkins 613 Dec 21, 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

Symfony 7.1k Jan 1, 2023
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

Filipe Dobreira 12.9k Dec 24, 2022
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

its 4.8k Dec 29, 2022
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

Barry vd. Heuvel 14.8k Jan 9, 2023
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

Tideways 7 Jan 6, 2022
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

null 8 Jul 30, 2021