adsr / phpspy Goto Github PK
View Code? Open in Web Editor NEWlow-overhead sampling profiler for PHP 7+
License: MIT License
low-overhead sampling profiler for PHP 7+
License: MIT License
For example if the delimiter is set to a comma, we should escape / delete / replace commas in the output so that it is reliably parseable.
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
This has been useful for me to identify helper functions that are frequently called and have slow performance (E.g. total time spent directly within the scope of a function, excluding helper functions it calls, would be at the bottom of the graph)
e.g. if 20% of the total execution time is spent in my_helper()
, but my_helper()
has 100 callers, it'd be hard to tell that my_helper() is making the whole program slower from a regular flame graph, but easy with a reversed flame graph.
What I'm currently using (adding -r
flag to perl script might be better):
» diff stackcollapse-phpspy.pl stackcollapse-phpspy-reversed.pl
68c68
< $stacks{join(';', reverse @frames)} += 1 if @frames;
---
> $stacks{join(';', @frames)} += 1 if @frames;
74c74
< $stacks{join(';', reverse @frames)} += 1 if @frames;
---
> $stacks{join(';', @frames)} += 1 if @frames;
This might help in reducing copy failures (e.g., #14)
It would be useful to have support for various output formats along with the raw data (e.g. Callgrind and summary stats.) Flamegraph formatted output could be incorporated into the profiler as another output format option.
Users could then pass a -f
option to specify the type of output.
Another option would be to have separate scripts like stackcollapse-phpspy
that would transform the raw stacks into various other formats post hoc.
It would be cool if instead of defaulting to 7.2 we could discover which php version is installed
It would be nice for peace-of-mind running in production if the -S / --pause-process
flag was something you opted into and wasn't included in the production binary 🙃
Able to reproduce consistently with sudo ./phpspy -V73 -r qcup -P '-x php'
and then running make test
in the php-src repo, which spawns a lot of php procs.
Unsure if the problem is in phpspy code or somewhere in libdw. I've seen it manifest in a few ways:
strcmp
strcmp
Could all be the same underlying problem.
Currently the built-in structs reflect a single PHP API (7.1 or 7.2, I forget). The memory layout of PHP 7.3 is slightly different for example. Users should be able to specify version at runtime or at least compile time. Ideally we'd be able to detect version at runtime and use the correct structs.
99Hz may be a better default sample rate than 100 because it would be more likely to avoid sampling in lockstep with some other periodic activity in the sampled process.
this could be something unique to my setup but i get strings: invalid option -- 'd'
and the strings
usage message everytime i invoked phpspy without the -V
flag
Commit df75b6f added a dependency on termbox which means following the simple make instructions in the readme now blows up:
$ make
cc -Wall -Wextra -pedantic -g -Ofast -pthread -I. -DUSE_READELF=1 phpspy.c pgrep.c top.c addr_libdw.c addr_readelf.c -o phpspy
In file included from phpspy.c:1:0:
phpspy.h:24:21: fatal error: termbox.h: No such file or directory
#include <termbox.h>
^
compilation terminated.
In file included from pgrep.c:1:0:
phpspy.h:24:21: fatal error: termbox.h: No such file or directory
#include <termbox.h>
^
compilation terminated.
In file included from top.c:1:0:
phpspy.h:24:21: fatal error: termbox.h: No such file or directory
#include <termbox.h>
^
compilation terminated.
In file included from addr_libdw.c:1:0:
phpspy.h:24:21: fatal error: termbox.h: No such file or directory
#include <termbox.h>
^
compilation terminated.
In file included from addr_readelf.c:1:0:
phpspy.h:24:21: fatal error: termbox.h: No such file or directory
#include <termbox.h>
^
compilation terminated.
make: *** [phpspy] Error 1
Installing https://github.com/nsf/termbox allows make to complete successfully (albeit with a lot of warnings), but this could probably do with documenting.
Great tool by the way! I've already used it in anger a couple of times to work out what a stuck process was doing.
nice work on setting up a test, I noticed that it fails on PHP 7.2 here
Line 11 in 5f13375
the equivalent trace on PHP 7.2 looks like this:
0 usleep <internal>:-1
1 <main> <internal>:-1
# uri = -
# path = -
# qstring = -
# cookie = -
# ts = 1540393892.910322
./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.
I don't think this can work: phpspy seems to reference private structs like zend_alloc_globals ... which are misnamed as they aren't global :)
Propose that this option is dropped from the build: It's possible to get some of the headers from the build environment (which I would suppose most people don't have installed), but phpspy must still define those private structs. In addition the ability of a single binary to interact with many versions is quite valuable, and you would loose that in a USE_ZEND build. Lastly, anything that makes the source more easily understandable is a win.
now that we find the php_core_globals
address and can print a basic representation of zend_array with print_array_recursive
— we can easily grab any of the following info
#define TRACK_VARS_POST 0
#define TRACK_VARS_GET 1
#define TRACK_VARS_COOKIE 2
#define TRACK_VARS_SERVER 3
#define TRACK_VARS_ENV 4
#define TRACK_VARS_FILES 5
#define TRACK_VARS_REQUEST 6
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 ?
We currently support varname@file:lineno
. It would be nice to support varname@file:start-end
as well. This will require some refactoring as the current impl relies on a hash lookup via a key formatted as file:lineno
. This will need to change to a hash lookup via file
and then some sort of efficient search to see if lineno
falls in any start-end
ranges.
Hello,
I'm planning to use phpspy in production and since it needs root access to do it's magic I would like to know how production-safe/ready you think this project is.
Also if there are any security audit's on the source to make my life easier.
My intentions are:
Im sure usecase 1. is ok.
On the 2nd usecase, xhprof is not compatible with php7, one can use tideways's fork. But tideways removed the sampling module in the portability effort.
So is it safe to be running constantly? Is that considered a common usecase?
Are there any known issues or missing things to take into consideration.
When I try to use phpspy inside docker container I got error:
copy_proc_mem: Failed to copy executor_globals; err=Operation not permitted raddr=0x7f30b3b0e240 size=496
Step to reporduce:
docker run -p 8181:80 -d --name=apache php:apache
docker exec -it apache bash
inside container:
#add index file
echo "<?php phpinfo();" > index.php
# install requried libs
apt-get update
apt-get install git python gcc
cd ..
git clone https://github.com/adsr/phpspy.git
cd phpspy/
make
./phpspy -p 1
I tried on other images (centos+fpm) and I have the same error.
This may be improved ... version string can be read from (char*) basic_functions_module.version (there are no builds of PHP where this is not present).
this options will be very usefull when run phpspy in non interactive mode
currenly we can try calculate expected --limit value option as
--limit = (planing rps * count --pgrep processes * --rate-hz)
but planning rps have high volatility and use --time-limit=60000 (60 seconds) should impove usability usage of phpspy
@mackenziestarr has a working patch for this. We could add this feature to --request-info
or make it a separate flag.
Tried to run it just with Laravels artisan:
$ php -v
PHP 7.1.20-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: Jul 25 2018 10:07:09) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies
$ ./artisan --version
Laravel Framework 5.6.31
There are other messages too like copy_proc_mem: Bad address; raddr=(nil) size=128
or dump_trace: Failed to copy zfunc
but since I'm totally new to this, I've no idea if this is ok or not.
How I used it:
~phpspy $ sudo ./phpspy -- php /path/to/laravel/artisan | ./stackcollapse-phpspy.pl| ./flamegraph.pl > flame.svg
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=0x100000000 size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=0x100000004 size=16
dump_trace: Failed to copy zce
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=0x1 size=32
dump_trace: Failed to copy function_name
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=0x300000c08 size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
copy_proc_mem: Bad address; raddr=(nil) size=128
dump_trace: Failed to copy zfunc
Argument "Available" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 810.
Argument "If" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 927.
Argument "the" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 928.
Argument "This" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 930.
Argument "The" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 935.
Argument "Can" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 950.
Argument "This" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 952.
Argument "Only" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 953.
Argument "production" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 954.
Argument "Depending" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 956.
Argument "on" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 957.
Argument "A" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 960.
Argument "NOT" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 961.
Argument "It's" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 963.
Argument "applicable" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 964.
Argument "However" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 966.
Argument "a)" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 967.
Argument "b)" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 968.
Argument "In" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 970.
Argument "disk" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 971.
Argument "The" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 977.
Argument "records." isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 978.
Argument "receive" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 979.
Argument "Setting" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 982.
Argument "-" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 983.
Argument "-" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 985.
Argument "After" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 988.
Argument "Note:" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 991.
Argument "during" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 992.
Argument "operation" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 993.
Argument "Note:" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 996.
Argument "during" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 997.
Argument "operation" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 998.
Argument "Note:" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 1009.
Argument "\\Autoload\\includeFile" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 1017.
Argument "By" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 1089.
Argument "-" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 1090.
Argument "-" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 1091.
Argument "It's" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 1094.
Argument "Currently" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 1096.
Argument "-" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 1097.
process_vm_readv: No such process
Argument "ommand\\ListCommand::execute" isn't numeric in numeric eq (==) at ./stackcollapse-phpspy.pl line 73, <> line 1119.
As the list of options grows, it would be helpful for new users to have a more descriptive set of long arguments
example:
Usage: phpspy [options] [--] <php_command>
-h, --help Show help
-p, --pid <pid> Trace PHP process at pid
-s, --nanoseconds <ns> Sleep this many nanoseconds between traces (default: 10000000, 10ms)
-H, --hertz <hz> The frequency at which traces are recorded (default: 100hz)
-n, --max-depth <max> Set max stack trace depth to `max` (default: -1, unlimited)
...
Rasmus notes it mostly works, however...
...it seems to think it is spending most of its time on line 1 of wp-settings.php which doesn't make much sense
I created a packaging script for archlinux on the AUR for easier installation and the package should include license information, didn't found any license file on the repo so was wondering what license will this project be licensed as :)
For the moment I just added the 'unknown' license placeholder. Thanks for sharing such a nice application!
I'm running phpspy on one system where it fails to find the PHP path. It blows up with the error:
popen_read_line: No stdout; cmd=grep ' 0$' /proc/5694/maps | head -n1
get_php_base_addr: Failed to get start_addr
Tracking this back it's because get_php_bin_path
returns 0
.
Looking at /proc/5694/maps
shows that the first line doesn't contain the path to the PHP binary, so awk 'NR==1{path=$NF} /libphp7/{path=$NF} END{print path}' /proc/%d/maps
fails to find it.
I've attached the memory map for the process. I couldn't come up with a way to find the path reliably or I would've submitted a PR, but hopefully you can. If not, I wondered if it would make sense to allow specifying the path via a command line option? As a quick hack I've temporarily hardcoded the path.
If you do go down the route of adding a command line option I think it would be worth changing the error message to make it clear what the issue is, as currently it takes a bit of digging to get from get_php_base_addr: Failed to get start_addr
to working out it can't find the binary. It would be straightforward enough to add a check in get_php_bin_path
to see if path
did exist.
Really really thank you, this tool just saved our production environment! I was able to find a infinite loop in a couple of minutes which was completely impossible to find, even with NewRelic, Blackfire, log analysis and days of search. The right tool, at the right moment.
Can you add a "buy me a beer" link in the README.md? 😄
Hey,
I'm trying to use the -m
flag on the following test script:
<?php
$i = 0;
while (true) {
$i++;
file_put_contents("/tmp/foo.txt", "hello$i\n", FILE_APPEND);
sleep(1);
}
running as a background process but I get the following error:
popen_read_line: No stdout; cmd=objdump -Tt /usr/bin/php7.3 | awk '/ alloc_globals$/{print $1; exit}'
get_symbol_offset: Failed
Full steps (running as root
):
# php /var/www/current/test.php &
[1] 311
# ~/phpspy/phpspy -p 311 -m
popen_read_line: No stdout; cmd=objdump -Tt /usr/bin/php7.3 | awk '/ alloc_globals$/{print $1; exit}'
get_symbol_offset: Failed
# ps aux | grep test.php
root 311 0.0 0.6 331260 27604 pts/1 S 10:18 0:00 php /var/www/current/test.php
root 328 0.0 0.0 14512 1076 pts/1 S+ 10:19 0:00 grep --color=auto test.php
Using https://github.com/phusion/baseimage-docker to test
$ docker build -t my-phpspy -<<'EOF'
FROM phusion/baseimage:latest
RUN apt-get update -yqq && apt-get install -yqq software-properties-common && \
add-apt-repository -y ppa:ondrej/php \
;
RUN apt-get update -yqq && apt-get install -yqq \
git \
build-essential \
python \
php7.3-cli \
;
RUN cd ~ \
&& git clone --recursive https://github.com/adsr/phpspy.git \
&& cd phpspy \
&& make \
;
EOF
$ docker run -d my-phpspy
Occasionally phpspy
will interlace output from multiple threads of tracing like so:
ternal>:-1
1 FooBarBaz::execute /var/foo/bar/phplib/Baz/DBBaz.php:104
2 FooBarBaz::bindAndExecuteStatement /var/foo/bar/Baz/Baz/Baz.php:1379
3 FooBarBaz::callFunc /var/foo/bar0 curl_exec <internal>:-1
1 Baz::execute /var/foo/bar/Bazify.php:152
2 FooBarBaz::singleCurlFetch /var/foo/bar/Baz/SuperBaz.php:503
An strace
points to these fprintf calls happening multiple times on a single trace/with multiple threads they could be overlapping with each other.
@keyurdg suggested:
Instead of line buffering here maybe full buffer
_IOFBF
andfflush
when completed
and fflush here
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
?
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.
Hello,
I can't profile a php script inside docker. It is pending and it displays nothing.
I can strace
the PHP script without any problem.
docker run --privileged --cap-add=SYS_PTRACE --rm akeneo/php php -r "sleep(100);"
I execute phpspy with strace to better know what happens:
sudo strace ./phpspy -p 20493
And here is what I get:
...
clock_gettime(CLOCK_MONOTONIC_RAW, {tv_sec=117355, tv_nsec=921478423}) = 0
process_vm_readv(20493, [{iov_base="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=496}], 1, [{iov_base=0x5562e9485940, iov_len=496}], 1, 0) = 496
clock_gettime(CLOCK_MONOTONIC_RAW, {tv_sec=117355, tv_nsec=921772631}) = 0
nanosleep({tv_sec=0, tv_nsec=9806802}, NULL) = 0
clock_gettime(CLOCK_MONOTONIC_RAW, {tv_sec=117355, tv_nsec=931932920}) = 0
process_vm_readv(20493, [{iov_base="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=496}], 1, [{iov_base=0x5562e9485940, iov_len=496}], 1, 0) = 496
clock_gettime(CLOCK_MONOTONIC_RAW, {tv_sec=117355, tv_nsec=932174327}) = 0
nanosleep({tv_sec=0, tv_nsec=9859603}, NULL) = 0
clock_gettime(CLOCK_MONOTONIC_RAW, {tv_sec=117355, tv_nsec=942321317}) = 0
process_vm_readv(20493, [{iov_base="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=496}], 1, [{iov_base=0x5562e9485940, iov_len=496}], 1, 0) = 496
clock_gettime(CLOCK_MONOTONIC_RAW, {tv_sec=117355, tv_nsec=942508791}) = 0
nanosleep({tv_sec=0, tv_nsec=9913536}, NULL) = 0
...
Also, I tried with the official PHP image:
docker run --privileged --cap-add=SYS_PTRACE --rm php php -r "sleep(100);"
but I got another error, which is not related to this issue I suppose.
popen_read_line: No stdout; cmd=awk '/libphp7/{print $NF; exit 0} END{exit 1}' /proc/21207/maps || readlink -e /proc/21207/exe
get_php_bin_path: Failed
Am I missing something?
Thanks.
Was looking at Wordpress both with PHP 7.1 and 7.2 and I got a bunch of these:
0 get_option /var/www/wordpress/wp-includes/option.php:93
1 <main> <internal>:-1
copy_proc_mem: Bad address; raddr=0x100000001 size=128
dump_trace: Failed to copy zfunc
0 translate /var/www/wordpress/wp-includes/l10n.php:134
copy_proc_mem: Bad address; raddr=0x100000006 size=128
dump_trace: Failed to copy zfunc
0 date_i18n /var/www/wordpress/wp-includes/functions.php:103
1 <main> <internal>:-1
2 <main> <internal>:-1
copy_proc_mem: Bad address; raddr=0x40808820000005a size=128
dump_trace: Failed to copy zfunc
Less of them with opcache off, but still some:
0 mysqli_fetch_object <internal>:-1
1 get_object_vars <internal>:-1
2 wp_cache_add /var/www/wordpress/wp-includes/cache.php:30
3 maybe_unserialize /var/www/wordpress/wp-includes/functions.php:330
4 get_metadata /var/www/wordpress/wp-includes/meta.php:510
copy_proc_mem: Bad address; raddr=0x100000001 size=128
dump_trace: Failed to copy zfunc
Trying to build on FreeBSD 12.0-RELEASE using the provided instructions results in:
make: "/usr/home/Development/phpspy/Makefile" line 22: Need an operator
make: "/usr/home/Development/phpspy/Makefile" line 24: Need an operator
make: "/usr/home/Development/phpspy/Makefile" line 25: Missing dependency operator
make: "/usr/home/Development/phpspy/Makefile" line 28: Need an operator
make: Fatal errors encountered -- cannot continue
make: stopped in /usr/home/Development/phpspy
# cat /etc/redhat-release
CentOS release 6.9 (Final)
# python --version
Python 2.6.6
# pip --version
pip 7.1.0 from /usr/lib/python2.6/site-packages (python 2.6)
# rpm -q glibc
glibc-2.12-1.209.el6_9.2.x86_64
# php -v
PHP 7.2.5 (cli) (built: Apr 24 2018 19:29:16) ( NTS )
git clone https://github.com/adsr/phpspy.git /opt/phpspy
cd /opt/phpspy && make
cd vendor/termbox && ./waf configure && ./waf --targets=termbox_static
Setting top to : /opt/phpspy/vendor/termbox
Setting out to : /opt/phpspy/vendor/termbox/build
Checking for 'gcc' (C compiler) : /usr/bin/gcc
'configure' finished successfully (0.494s)
Waf: Entering directory `/opt/phpspy/vendor/termbox/build'
[1/3] Compiling src/utf8.c
[2/3] Compiling src/termbox.c
[3/3] Linking build/src/libtermbox.a
Waf: Leaving directory `/opt/phpspy/vendor/termbox/build'
'build' finished successfully (0.266s)
cc -std=c99 -Wall -Wextra -pedantic -g -O3 -I. -I./vendor -Ivendor/termbox/src -Ivendor/termbox/src/ phpspy.c pgrep.c top.c addr_objdump.c event_fout.c -o phpspy -pthread -Wl,-Bstatic -Lvendor/termbox/build/src/ -ltermbox -Wl,-Bdynamic
phpspy.c: In function ‘copy_proc_mem’:
phpspy.c:577: warning: implicit declaration of function ‘process_vm_readv’
/tmp/cclVq5gw.o: In function `copy_proc_mem':
/opt/phpspy/phpspy.c:577: undefined reference to `process_vm_readv'
/tmp/cclVq5gw.o: In function `clock_get':
/opt/phpspy/phpspy.c:451: undefined reference to `clock_gettime'
/opt/phpspy/phpspy.c:451: undefined reference to `clock_gettime'
/opt/phpspy/phpspy.c:451: undefined reference to `clock_gettime'
/tmp/ccAr982C.o: In function `wait_for_turn':
/opt/phpspy/pgrep.c:62: undefined reference to `clock_gettime'
/tmp/ccAr982C.o: In function `pgrep_for_pids':
/opt/phpspy/pgrep.c:105: undefined reference to `clock_gettime'
/tmp/ccAr982C.o:/opt/phpspy/pgrep.c:62: more undefined references to `clock_gettime' follow
collect2: ld returned 1 exit status
make: *** [phpspy_static] Error 1
how i can fix it?
Currently --limit
only works in single-pid mode. In pgrep mode, once a limit is reached the worker does exit but a new worker is spawned in its place.
Preferably replace invalid bytes (or codepoints) with a placeholder character or encoding such as \x04
/%04
so that the svg can be rendered even if the method name was invalid or read incorrectly.
https://en.wikipedia.org/wiki/Valid_characters_in_XML#XML_1.0 mentions the valid character values for xml 1.0 - filtering out invalid bytes would be an easy start
Newlines might also make sense to remove if the filtering was done in phpspy, but I haven't seen those
When analyzing a long running stack trace in php 7.3.8-dev, I saw the following error displayed in Google Chrome
(for the decimal byte values 4 and 16)
This page contains the following errors:
error on line 13591 at column 10: PCDATA invalid Char value 4
Below is a rendering of the page up to the first error.
The raw logs had various snippets such as the following, with the raw bytes 4 and 16. Some samples of what they looked like:
0 Phan\Language\UnionType::hasTemplateTypeRecursive /path/to/phan/src/Phan/Language/UnionType.php:881
1 ::� <internal>:-1
0 ::� <internal>:-1
1 Phan\BlockAnalysisVisitor::visitStmtList /path/to/phan/src/Phan/BlockAnalysisVisitor.php:208
2 Phan\BlockAnalysisVisitor::analyzeAndGetUpdatedContext /path/to/phan/src/Phan/BlockAnalysisVisitor.php:472
3 Phan\BlockAnalysisVisitor::visitIf /path/to/phan/src/Phan/BlockAnalysisVisitor.php:1357
4 Phan\BlockAnalysisVisitor::analyzeAndGetUpdatedContext /path/to/phan/src/Phan/BlockAnalysisVisitor.php:472
5 Phan\BlockAnalysisVisitor::visitStmtList /path/to/phan/src/Phan/BlockAnalysisVisitor.php:208
6 Phan\BlockAnalysisVisitor::analyzeAndGetUpdatedContext /path/to/phan/src/Phan/BlockAnalysisVisitor.php:472
7 Phan\BlockAnalysisVisitor::visitForeach /path/to/phan/src/Phan/BlockAnalysisVisitor.php:756
8 Phan\BlockAnalysisVisitor::analyzeAndGetUpdatedContext /path/to/phan/src/Phan/BlockAnalysisVisitor.php:472
9 Phan\BlockAnalysisVisitor::visitStmtList /path/to/phan/src/Phan/BlockAnalysisVisitor.php:208
10 Phan\BlockAnalysisVisitor::analyzeAndGetUpdatedContext /path/to/phan/src/Phan/BlockAnalysisVisitor.php:472
11 Phan\BlockAnalysisVisitor::visitMethod /path/to/phan/src/Phan/BlockAnalysisVisitor.php:2065
12 Phan\BlockAnalysisVisitor::analyzeAndGetUpdatedContext /path/to/phan/src/Phan/BlockAnalysisVisitor.php:472
13 Phan\BlockAnalysisVisitor::visitStmtList /path/to/phan/src/Phan/BlockAnalysisVisitor.php:208
14 Phan\BlockAnalysisVisitor::analyzeAndGetUpdatedContext /path/to/phan/src/Phan/BlockAnalysisVisitor.php:472
15 Phan\BlockAnalysisVisitor::visitClosedContext /path/to/phan/src/Phan/BlockAnalysisVisitor.php:1135
16 Phan\BlockAnalysisVisitor::visitClass /path/to/phan/src/Phan/BlockAnalysisVisitor.php:2051
17 Phan\BlockAnalysisVisitor::analyzeAndGetUpdatedContext /path/to/phan/src/Phan/BlockAnalysisVisitor.php:472
18 Phan\BlockAnalysisVisitor::visitStmtList /path/to/phan/src/Phan/BlockAnalysisVisitor.php:208
19 Phan\AST\Visitor\KindVisitorImplementation::__invoke /path/to/phan/src/Phan/AST/Visitor/KindVisitorImplementation.php:29
20 Phan\Analysis::analyzeFile /path/to/phan/src/Phan/Analysis.php:473
21 Phan\Phan::Phan\{closure} /path/to/phan/src/Phan/Phan.php:401
22 Phan\Phan::finishAnalyzingRemainingStatements /path/to/phan/src/Phan/Phan.php:316
23 Phan\Phan::analyzeFileList /path/to/phan/src/Phan/Phan.php:122
24 <main> /path/to/phan/src/phan.php:1
25 <main> /path/to/phan/phan:1
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.