├── .svnignore ├── docs └── images │ ├── google-tracing-example.png │ └── google-tracing-example_thumb.png ├── ext └── forp │ ├── bin │ ├── php_forp_msvc10_86_TS_53.dll │ ├── php_forp_msvc10_x86_NTS_53.dll │ ├── php_forp_msvc9_x86_NTS_53.dll │ └── php_forp_msvc9_x86_TS_53.dll │ ├── tests │ ├── 001.phpt │ ├── 002.phpt │ └── 003.phpt │ ├── config.w32 │ ├── phpunit.xml.dist │ ├── examples │ ├── hanoi.php │ └── output-hanoi.json │ ├── forp.php │ ├── forp_json.h │ ├── forp_annotation.h │ ├── forp_string.h │ ├── forp_log.h │ ├── config.m4 │ ├── php_forp.h │ ├── forp_string.c │ ├── forp.h │ ├── forp_annotation.c │ ├── forp_log.c │ ├── php_forp.c │ ├── forp_json.c │ └── forp.c ├── .travis.yml ├── .gitignore ├── composer.json ├── LICENSE └── README.md /.svnignore: -------------------------------------------------------------------------------- 1 | .deps 2 | *.lo 3 | *.la 4 | -------------------------------------------------------------------------------- /docs/images/google-tracing-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aterrien/forp-PHP-profiler/HEAD/docs/images/google-tracing-example.png -------------------------------------------------------------------------------- /ext/forp/bin/php_forp_msvc10_86_TS_53.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aterrien/forp-PHP-profiler/HEAD/ext/forp/bin/php_forp_msvc10_86_TS_53.dll -------------------------------------------------------------------------------- /ext/forp/bin/php_forp_msvc10_x86_NTS_53.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aterrien/forp-PHP-profiler/HEAD/ext/forp/bin/php_forp_msvc10_x86_NTS_53.dll -------------------------------------------------------------------------------- /ext/forp/bin/php_forp_msvc9_x86_NTS_53.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aterrien/forp-PHP-profiler/HEAD/ext/forp/bin/php_forp_msvc9_x86_NTS_53.dll -------------------------------------------------------------------------------- /ext/forp/bin/php_forp_msvc9_x86_TS_53.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aterrien/forp-PHP-profiler/HEAD/ext/forp/bin/php_forp_msvc9_x86_TS_53.dll -------------------------------------------------------------------------------- /docs/images/google-tracing-example_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aterrien/forp-PHP-profiler/HEAD/docs/images/google-tracing-example_thumb.png -------------------------------------------------------------------------------- /ext/forp/tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | forp_start() function - basic test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 9 | --EXPECT-- 10 | NULL 11 | -------------------------------------------------------------------------------- /ext/forp/config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | // use : --with-forp[=shared] 5 | ARG_WITH("forp", "Enable forp support", "no"); 6 | 7 | if (PHP_FORP != "no") { 8 | EXTENSION("forp", "php_forp.c forp.c forp_annotation.c forp_string.c forp_log.c forp_json.c"); 9 | } -------------------------------------------------------------------------------- /ext/forp/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | ./tests/ 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ext/forp/tests/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | forp_dump() function 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 1; 14 | ?> 15 | --EXPECT-- 16 | bar1 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | 8 | before_script: 9 | - cd ext/forp 10 | - phpize && ./configure && make && sudo make install 11 | - echo "extension=forp.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` 12 | 13 | script: php -f forp.php 14 | -------------------------------------------------------------------------------- /ext/forp/tests/003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | forp_print() function - basic test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 0; 13 | echo strpos($res,'time:')>0; 14 | echo strpos($res,'mem:')>0; 15 | echo strpos($res,'{main}')>0; 16 | ?> 17 | --EXPECT-- 18 | 1111 19 | -------------------------------------------------------------------------------- /ext/forp/examples/hanoi.php: -------------------------------------------------------------------------------- 1 | 0) { 11 | $using = 6 - ($from + $to); 12 | hanoi(--$plates, $from, $using); 13 | //print "Move plate from $from to $to
"; 14 | $from = $using; 15 | } 16 | } 17 | hanoi(5, 1, 3); 18 | 19 | // Stop profiler 20 | forp_end(); 21 | // Get JSON and save it into file 22 | forp_json_google_tracer("/tmp/output_".mt_rand().".json"); 23 | ?> 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .libs/ 2 | Makefile 3 | Makefile.fragments 4 | Makefile.global 5 | Makefile.objects 6 | acinclude.m4 7 | aclocal.m4 8 | autom4te.cache/ 9 | build/ 10 | config.guess 11 | config.h 12 | config.h.in 13 | config.log 14 | config.nice 15 | config.status 16 | config.sub 17 | configure 18 | configure.in 19 | install-sh 20 | libtool 21 | ltmain.sh 22 | missing 23 | mkinstalldirs 24 | modules/ 25 | *.la 26 | *.lo 27 | run-tests.php 28 | tests/*/*.diff 29 | tests/*/*.exp 30 | tests/*/*.log 31 | tests/*/*.out 32 | tests/*/*.php 33 | tests/*/*.sh 34 | .deps 35 | *.swp 36 | *~ 37 | Debug/ 38 | *.opensdf 39 | *.sdf 40 | *.sln 41 | *.v11.suo 42 | *.vcxproj 43 | *.vcxproj.filters 44 | /*.cache 45 | /nbproject 46 | 47 | /*.o 48 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "aterrien/forp-profiler", 3 | "version" : "1.1.0", 4 | "description" : "A PHP profiler written in C. forp is a lightweight PHP extension which provides the full call stack of your script, with CPU and memory usage, in a plain PHP Array or JSON output.", 5 | "homepage" : "https://github.com/aterrien/forp-PHP-profiler", 6 | "keywords" : [ "php", "profiler", "benchmark", "quality" ], 7 | "license" : "MIT", 8 | "authors" : [ 9 | { 10 | "name" : "Anthony Terrien", 11 | "email" : "forp@anthonyterrien.com" 12 | } 13 | ], 14 | "support" : { 15 | "issues" : "https://github.com/aterrien/forp-PHP-profiler/issues" 16 | }, 17 | "minimum-stability" : "dev", 18 | "require" : { 19 | "php" : ">=5.3" 20 | }, 21 | "suggest": {}, 22 | "autoload" : { 23 | "classmap" : [ "" ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ext/forp/forp.php: -------------------------------------------------------------------------------- 1 | \n"; 3 | $module = 'forp'; 4 | if(!extension_loaded($module)) { 5 | dl($module . '.' . PHP_SHLIB_SUFFIX); 6 | } 7 | $functions = get_extension_funcs($module); 8 | echo "Functions available in the test extension:". $br; 9 | foreach($functions as $func) { 10 | echo $func.$br; 11 | } 12 | echo $br; 13 | 14 | // testing forp functions 15 | echo '- Enable'.$br; 16 | forp_start(); 17 | 18 | /** 19 | * @ProfileGroup("fibo") 20 | * @ProfileCaption("fibo of #1") 21 | */ 22 | function fibo( $x ) { 23 | if ( $x <= 1) { 24 | return $x; 25 | } else { 26 | return fibo($x - 1) + fibo($x - 2); 27 | } 28 | } 29 | 30 | for( $i = 1; $i < 10; $i++) { 31 | printf( 32 | 'fibo(%1$s) = %2$s'.$br, 33 | $i, fibo($i) 34 | ); 35 | } 36 | echo '- Dump'.$br; 37 | $dump = forp_dump(); 38 | print_r( $dump['stack'][0] ); 39 | echo '- Print'.$br; 40 | forp_print(); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Anthony Terrien 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /ext/forp/forp_json.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Forp | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 2012 Anthony Terrien | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef FORP_JSON_H 20 | #define FORP_JSON_H 21 | 22 | #include "php.h" 23 | #include "forp_log.h" 24 | 25 | #ifdef ZTS 26 | #include "TSRM.h" 27 | #endif 28 | 29 | void forp_json(TSRMLS_D); 30 | 31 | void forp_json_google_tracer(char* filepath TSRMLS_DC); 32 | 33 | void forp_json_inspect(forp_var_t *var TSRMLS_DC); 34 | 35 | #endif /* FORP_JSON_H */ 36 | 37 | /* 38 | * Local variables: 39 | * tab-width: 4 40 | * c-basic-offset: 4 41 | * End: 42 | * vim600: noet sw=4 ts=4 fdm=marker 43 | * vim<600: noet sw=4 ts=4 44 | */ -------------------------------------------------------------------------------- /ext/forp/forp_annotation.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Forp | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 2012 Anthony Terrien | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef FORP_ANNOTATION_H 20 | #define FORP_ANNOTATION_H 21 | 22 | #include "php.h" 23 | 24 | #ifdef ZTS 25 | #include "TSRM.h" 26 | #endif 27 | 28 | void forp_annotation_args(char *str, char ***args, int *args_count TSRMLS_DC); 29 | 30 | char *forp_annotation_tok(const char *doc_comment, char *tag TSRMLS_DC); 31 | 32 | char *forp_annotation_string(const char *doc_comment, char *tag TSRMLS_DC); 33 | 34 | void forp_annotation_array(const char *doc_comment, char *tag, char ***args, int *args_count TSRMLS_DC); 35 | 36 | #endif /* FORP_ANNOTATION_H */ 37 | 38 | /* 39 | * Local variables: 40 | * tab-width: 4 41 | * c-basic-offset: 4 42 | * End: 43 | * vim600: noet sw=4 ts=4 fdm=marker 44 | * vim<600: noet sw=4 ts=4 45 | */ -------------------------------------------------------------------------------- /ext/forp/forp_string.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Forp | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 2012 Anthony Terrien | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef FORP_STRING_H 20 | #define FORP_STRING_H 21 | 22 | #include "php.h" 23 | 24 | #ifdef ZTS 25 | #include "TSRM.h" 26 | #endif 27 | 28 | char* forp_strndup(const char* s, size_t n); 29 | 30 | char *forp_substr_replace(char *subject, char *replace, unsigned int start, unsigned int len); 31 | 32 | char *forp_str_replace(char *search, char *replace, char *subject TSRMLS_DC); 33 | 34 | char *forp_addslashes(char *subject TSRMLS_DC); 35 | 36 | #if defined(PHP_WIN32) 37 | #define strndup(s,n) forp_strndup(s, n) 38 | #endif 39 | 40 | #endif /* FORP_STRING_H */ 41 | 42 | /* 43 | * Local variables: 44 | * tab-width: 4 45 | * c-basic-offset: 4 46 | * End: 47 | * vim600: noet sw=4 ts=4 fdm=marker 48 | * vim<600: noet sw=4 ts=4 49 | */ -------------------------------------------------------------------------------- /ext/forp/forp_log.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Forp | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 2012 Anthony Terrien | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef FORP_LOG_H 20 | #define FORP_LOG_H 21 | 22 | #define FORP_LOG_DEPTH 2 23 | 24 | #include "php.h" 25 | 26 | #ifdef ZTS 27 | #include "TSRM.h" 28 | #endif 29 | 30 | typedef struct forp_var_t { 31 | char *type; 32 | char *name; 33 | char *key; 34 | char *level; 35 | char *value; 36 | char *class; 37 | struct forp_var_t **arr; 38 | uint arr_len; 39 | uint is_ref; 40 | int refcount; 41 | int stack_idx; 42 | } forp_var_t; 43 | 44 | forp_var_t *forp_zval_var(forp_var_t *v, zval *expr, int depth TSRMLS_DC); 45 | 46 | zval **forp_find_symbol(char* name TSRMLS_DC); 47 | 48 | //void forp_inspect_symbol(char *name TSRMLS_DC); 49 | 50 | void forp_inspect_zval(char* name, zval *expr TSRMLS_DC); 51 | 52 | #endif /* FORP_LOG_H */ 53 | 54 | /* 55 | * Local variables: 56 | * tab-width: 4 57 | * c-basic-offset: 4 58 | * End: 59 | * vim600: noet sw=4 ts=4 fdm=marker 60 | * vim<600: noet sw=4 ts=4 61 | */ -------------------------------------------------------------------------------- /ext/forp/config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension forp 3 | 4 | dnl Comments in this file start with the string 'dnl'. 5 | dnl Remove where necessary. This file will not work 6 | dnl without editing. 7 | 8 | dnl If your extension references something external, use with: 9 | 10 | dnl PHP_ARG_WITH(forp, for forp support, 11 | dnl Make sure that the comment is aligned: 12 | dnl [ --with-forp Include forp support]) 13 | 14 | dnl Otherwise use enable: 15 | 16 | PHP_ARG_ENABLE(forp, whether to enable forp support, 17 | Make sure that the comment is aligned: 18 | [ --enable-forp Enable forp support]) 19 | 20 | if test "$PHP_FORP" != "no"; then 21 | dnl Write more examples of tests here... 22 | 23 | dnl # --with-forp -> check with-path 24 | dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 25 | dnl SEARCH_FOR="/include/php_forp.h /include/forp.h" # you most likely want to change this 26 | dnl if test -r $PHP_FORP/$SEARCH_FOR; then # path given as parameter 27 | dnl FORP_DIR=$PHP_FORP 28 | dnl else # search default path list 29 | dnl AC_MSG_CHECKING([for forp files in default path]) 30 | dnl for i in $SEARCH_PATH ; do 31 | dnl if test -r $i/$SEARCH_FOR; then 32 | dnl FORP_DIR=$i 33 | dnl AC_MSG_RESULT(found in $i) 34 | dnl fi 35 | dnl done 36 | dnl fi 37 | dnl 38 | dnl if test -z "$FORP_DIR"; then 39 | dnl AC_MSG_RESULT([not found]) 40 | dnl AC_MSG_ERROR([Please reinstall the forp distribution]) 41 | dnl fi 42 | 43 | dnl # --with-forp -> add include path 44 | dnl PHP_ADD_INCLUDE($FORP_DIR/include) 45 | 46 | dnl # --with-forp -> check for lib and symbol presence 47 | dnl LIBNAME=forp # you may want to change this 48 | dnl LIBSYMBOL=forp # you most likely want to change this 49 | 50 | dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 51 | dnl [ 52 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $FORP_DIR/lib, FORP_SHARED_LIBADD) 53 | dnl AC_DEFINE(HAVE_FORPLIB,1,[ ]) 54 | dnl ],[ 55 | dnl AC_MSG_ERROR([wrong forp lib version or lib not found]) 56 | dnl ],[ 57 | dnl -L$FORP_DIR/lib -lm 58 | dnl ]) 59 | dnl 60 | dnl PHP_SUBST(FORP_SHARED_LIBADD) 61 | 62 | PHP_NEW_EXTENSION(forp, php_forp.c forp.c forp_annotation.c forp_string.c forp_log.c forp_json.c, $ext_shared) 63 | fi 64 | -------------------------------------------------------------------------------- /ext/forp/examples/output-hanoi.json: -------------------------------------------------------------------------------- 1 | {"traceEvents":[{"args": {"name": "cli"}, "cat": "__metadata", "name": "process_name", "ph": "M", "pid": 0, "tid": 0, "ts": 0 },{"args": {"name": "PHP"}, "cat": "__metadata", "name": "thread_name", "ph": "M", "pid": 0, "tid": 0, "ts": 0 },{"ph":"X","pid":"0","tid":"0","name":"{main}","cat":"PHP","dur":1223,"ts":1409249363563487},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":1166,"ts":1409249363563524},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":566,"ts":1409249363563562},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":272,"ts":1409249363563582},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":123,"ts":1409249363563605},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":49,"ts":1409249363563625},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":12,"ts":1409249363563644},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363563702},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":46,"ts":1409249363563756},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363563775},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363563828},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":118,"ts":1409249363563881},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":45,"ts":1409249363563900},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363563920},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363563973},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":46,"ts":1409249363564026},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363564049},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363564102},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":262,"ts":1409249363564155},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":118,"ts":1409249363564175},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":46,"ts":1409249363564194},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363564213},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363564266},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":45,"ts":1409249363564319},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363564338},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363564391},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":121,"ts":1409249363564445},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":45,"ts":1409249363564467},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363564486},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363564540},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":45,"ts":1409249363564593},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363564612},{"ph":"X","pid":"0","tid":"0","name":"hanoi","cat":"PHP","dur":9,"ts":1409249363564665}]} -------------------------------------------------------------------------------- /ext/forp/php_forp.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Forp | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 2012 Anthony Terrien | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | #ifndef PHP_FORP_H 19 | #define PHP_FORP_H 20 | 21 | #define FORP_VERSION "1.1.0" 22 | 23 | #define FORP_FLAG_TIME 0x0001 24 | #define FORP_FLAG_MEMORY 0x0002 25 | #define FORP_FLAG_CPU 0x0004 26 | 27 | #define FORP_FLAG_ALIAS 0x0020 28 | #define FORP_FLAG_CAPTION 0x0040 29 | #define FORP_FLAG_GROUPS 0x0080 30 | #define FORP_FLAG_HIGHLIGHT 0x0100 31 | #define FORP_FLAG_ANNOTATIONS 0x01E0 32 | 33 | #define FORP_FLAG_ALL 0x03FF 34 | 35 | extern zend_module_entry forp_module_entry; 36 | #define phpext_forp_ptr &forp_module_entry 37 | 38 | #ifdef PHP_WIN32 39 | #define PHP_FORP_API __declspec(dllexport) 40 | #elif defined(__GNUC__) && __GNUC__ >= 4 41 | #define PHP_FORP_API __attribute__ ((visibility("default"))) 42 | #else 43 | #define PHP_FORP_API 44 | #endif 45 | 46 | #ifdef ZTS 47 | #include "TSRM.h" 48 | #endif 49 | 50 | #include "forp.h" 51 | 52 | PHP_MINIT_FUNCTION(forp); 53 | PHP_MSHUTDOWN_FUNCTION(forp); 54 | PHP_RINIT_FUNCTION(forp); 55 | PHP_RSHUTDOWN_FUNCTION(forp); 56 | PHP_MINFO_FUNCTION(forp); 57 | ZEND_MODULE_POST_ZEND_DEACTIVATE_D(forp); 58 | 59 | PHP_FUNCTION(forp_enable); 60 | PHP_FUNCTION(forp_start); 61 | PHP_FUNCTION(forp_end); 62 | PHP_FUNCTION(forp_dump); 63 | PHP_FUNCTION(forp_print); 64 | PHP_FUNCTION(forp_info); 65 | PHP_FUNCTION(forp_inspect); 66 | PHP_FUNCTION(forp_json); 67 | PHP_FUNCTION(forp_json_google_tracer); 68 | 69 | /* global variables */ 70 | ZEND_BEGIN_MODULE_GLOBALS(forp) 71 | int flags; 72 | int started; 73 | int nesting_level; 74 | forp_node_t *main; 75 | forp_node_t *current_node; 76 | int stack_len; 77 | forp_node_t **stack; 78 | zval *dump; 79 | long max_nesting_level; 80 | int no_internals; 81 | double stime; 82 | double utime; 83 | //struct hashtable_t *functions; 84 | int inspect_len; 85 | forp_var_t **inspect; 86 | long inspect_depth_array; 87 | long inspect_depth_object; 88 | ZEND_END_MODULE_GLOBALS(forp) 89 | 90 | ZEND_DECLARE_MODULE_GLOBALS(forp); 91 | 92 | #ifdef ZTS 93 | #define FORP_G(v) TSRMG(forp_globals_id, zend_forp_globals *, v) 94 | #else 95 | #define FORP_G(v) (forp_globals.v) 96 | #endif 97 | 98 | #endif /* PHP_FORP_H */ 99 | 100 | /* 101 | * Local variables: 102 | * tab-width: 4 103 | * c-basic-offset: 4 104 | * End: 105 | * vim600: noet sw=4 ts=4 fdm=marker 106 | * vim<600: noet sw=4 ts=4 107 | */ 108 | -------------------------------------------------------------------------------- /ext/forp/forp_string.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2011 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include "php.h" 24 | #include "php_ini.h" 25 | #include 26 | #include "forp_string.h" 27 | 28 | #ifdef ZTS 29 | #include "TSRM.h" 30 | #endif 31 | 32 | /* {{{ forp_substr_replace 33 | */ 34 | char* forp_strndup(const char* s, size_t n) { 35 | size_t slen = (size_t)strlen(s); 36 | char* copy; 37 | if (slen < n) { 38 | n = slen; 39 | } 40 | copy = malloc(n+1); 41 | if (copy) { 42 | memcpy(copy, s, n); 43 | copy[n] = 0; 44 | } 45 | return copy; 46 | } 47 | /* }}} */ 48 | 49 | /* {{{ forp_addslashes 50 | */ 51 | char *forp_addslashes(char *subject TSRMLS_DC) { 52 | return subject; 53 | /*char *result = NULL; 54 | char c; 55 | size_t sSize = (size_t)strlen(subject); 56 | uint rSize = 0; 57 | uint i = 0; 58 | uint j = 0; 59 | for(i = 0; i < sSize; i++) { 60 | c = subject[i]; 61 | if (c == '/' || c == '\\' || c == '\"' || c == '\'') { 62 | rSize++; 63 | } 64 | } 65 | if ( rSize > 0 ) { 66 | //result = malloc(sSize + rSize + 1); 67 | if ( result != NULL ) { 68 | for(i = 0; i < sSize; i++) { 69 | c = subject[i]; 70 | if (c == '/' || c == '\\' || c == '\"' || c == '\'') { 71 | result[j++] = '\\'; 72 | } 73 | result[j++] = c; 74 | } 75 | //subject = strdup(result); 76 | //free(result); 77 | } 78 | } 79 | return subject;*/ 80 | } 81 | /* }}} */ 82 | 83 | /* {{{ forp_substr_replace 84 | * 85 | * @param char* subject 86 | * @param uint start 87 | * @param uint len 88 | * @param char* replace 89 | * @return char* 90 | */ 91 | char *forp_substr_replace(char *subject, char *replace, unsigned int start, unsigned int len) { 92 | char *ns = NULL; 93 | size_t size; 94 | if ( 95 | subject != NULL && replace != NULL 96 | && start >= 0 && len > 0 97 | ) { 98 | size = strlen(subject); 99 | ns = malloc(sizeof (*ns) * (size - len + strlen(replace) + 1)); 100 | if (ns) { 101 | memcpy(ns, subject, start); 102 | memcpy(&ns[start], replace, strlen(replace)); 103 | memcpy(&ns[start + strlen(replace)], &subject[start + len], size - len - start + 1); 104 | } 105 | subject = strdup(ns); 106 | free(ns); 107 | } 108 | return subject; 109 | } 110 | /* }}} */ 111 | 112 | /* {{{ forp_str_replace 113 | * 114 | * @param char* search 115 | * @param char* replace 116 | * @param char* subject 117 | * @return char* 118 | */ 119 | char *forp_str_replace(char *search, char *replace, char *subject TSRMLS_DC) { 120 | char *ret, *sr; 121 | size_t i, count = 0; 122 | size_t newlen = strlen(replace); 123 | size_t oldlen = strlen(search); 124 | 125 | if (newlen != oldlen) { 126 | for (i = 0; subject[i] != '\0';) { 127 | if (memcmp(&subject[i], search, oldlen) == 0) 128 | count++, i += oldlen; 129 | else 130 | i++; 131 | } 132 | } else 133 | i = strlen(subject); 134 | 135 | ret = malloc(i + 1 + count * (newlen - oldlen)); 136 | if (ret == NULL) 137 | return NULL; 138 | 139 | sr = ret; 140 | while (*subject) { 141 | if (memcmp(subject, search, oldlen) == 0) { 142 | memcpy(sr, replace, newlen); 143 | sr += newlen; 144 | subject += oldlen; 145 | } else 146 | *sr++ = *subject++; 147 | } 148 | *sr = '\0'; 149 | 150 | return ret; 151 | } 152 | /* }}} */ -------------------------------------------------------------------------------- /ext/forp/forp.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Forp | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 2012 Anthony Terrien | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef FORP_H 20 | #define FORP_H 21 | 22 | #include "php.h" 23 | #include "forp_string.h" 24 | #include "forp_log.h" 25 | 26 | #ifdef ZTS 27 | #include "TSRM.h" 28 | #endif 29 | 30 | #define FORP_DUMP_ASSOC_FILE "file" 31 | #define FORP_DUMP_ASSOC_CLASS "class" 32 | #define FORP_DUMP_ASSOC_FUNCTION "function" 33 | #define FORP_DUMP_ASSOC_LINENO "lineno" 34 | #define FORP_DUMP_ASSOC_DURATION "usec" 35 | #define FORP_DUMP_ASSOC_PROFILERTIME "pusec" 36 | #define FORP_DUMP_ASSOC_MEMORY "bytes" 37 | #define FORP_DUMP_ASSOC_LEVEL "level" 38 | #define FORP_DUMP_ASSOC_PARENT "parent" 39 | #define FORP_DUMP_ASSOC_GROUPS "groups" 40 | #define FORP_DUMP_ASSOC_CAPTION "caption" 41 | #define FORP_HIGHLIGHT_BEGIN "
" 42 | #define FORP_HIGHLIGHT_END "
%s [%.03f ms, %d b, level %d]
" 43 | #define FORP_STACK_REALLOC 1000 44 | 45 | typedef struct forp_function_t { 46 | char *filename; 47 | char *class; 48 | char *function; 49 | int type; 50 | char **groups; 51 | int groups_len; 52 | char *highlight; 53 | } forp_function_t; 54 | 55 | typedef struct forp_node_t { 56 | int key; 57 | int state; 58 | 59 | char *filename; 60 | int lineno; 61 | int level; 62 | struct forp_node_t *parent; 63 | char *caption; 64 | char *alias; 65 | 66 | forp_function_t function; 67 | 68 | // Memory 69 | signed long mem; 70 | signed long mem_begin; 71 | signed long mem_end; 72 | 73 | // Duration 74 | double time; 75 | double time_begin; 76 | long time_begin_timestamp_microseconds; 77 | double time_end; 78 | 79 | // Self cost 80 | double profiler_duration; 81 | } forp_node_t; 82 | 83 | 84 | /* Zend API proxies */ 85 | #if PHP_VERSION_ID < 50500 86 | void (*old_execute)(zend_op_array *op_array TSRMLS_DC); 87 | void forp_execute(zend_op_array *op_array TSRMLS_DC); 88 | void (*old_execute_internal)(zend_execute_data *current_execute_data, int return_value_used TSRMLS_DC); 89 | void forp_execute_internal(zend_execute_data *current_execute_data, int return_value_used TSRMLS_DC); 90 | #else 91 | void (*old_execute_ex)(zend_execute_data *execute_data TSRMLS_DC); 92 | void forp_execute_ex(zend_execute_data *execute_data TSRMLS_DC); 93 | void (*old_execute_internal)(zend_execute_data *current_execute_data, struct _zend_fcall_info *fci, int return_value_used TSRMLS_DC); 94 | void forp_execute_internal(zend_execute_data *current_execute_data, struct _zend_fcall_info *fci, int return_value_used TSRMLS_DC); 95 | #endif 96 | 97 | 98 | static void forp_populate_function(forp_function_t *function, zend_execute_data *edata, zend_op_array *op_array TSRMLS_DC); 99 | 100 | void forp_info(TSRMLS_D); 101 | 102 | void forp_start(TSRMLS_D); 103 | 104 | void forp_end(TSRMLS_D); 105 | 106 | forp_node_t *forp_open_node(zend_execute_data *edata, zend_op_array *op_array TSRMLS_DC); 107 | 108 | void forp_close_node(forp_node_t *pn TSRMLS_DC); 109 | 110 | zval *forp_stack_dump_var(forp_var_t *var TSRMLS_DC); 111 | 112 | void forp_stack_dump(TSRMLS_D); 113 | 114 | void forp_stack_dump_cli_node(forp_node_t *node TSRMLS_DC); 115 | 116 | void forp_stack_dump_cli_var(forp_var_t *var, int depth TSRMLS_DC); 117 | 118 | void forp_stack_dump_cli(TSRMLS_D); 119 | 120 | int forp_is_profiling_function(forp_node_t *n TSRMLS_DC); 121 | 122 | #endif /* FORP_H */ 123 | 124 | /* 125 | * Local variables: 126 | * tab-width: 4 127 | * c-basic-offset: 4 128 | * End: 129 | * vim600: noet sw=4 ts=4 fdm=marker 130 | * vim<600: noet sw=4 ts=4 131 | */ 132 | -------------------------------------------------------------------------------- /ext/forp/forp_annotation.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2011 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include "php.h" 24 | #include "php_ini.h" 25 | 26 | #include "forp_string.h" 27 | #include "forp_annotation.h" 28 | 29 | /* {{{ forp_annotation_args 30 | * 31 | * Parses args of an annotation 32 | * 33 | * @param char* str 34 | * @param char*** args 35 | * @param int* args_count 36 | * @return void 37 | */ 38 | void forp_annotation_args(char *str, char ***args, int *args_count TSRMLS_DC) { 39 | int esc = 0, buf = 0, i = 0, j = 0; 40 | char *ex; 41 | 42 | *args_count = 0; 43 | if(strlen(str) > 0) { 44 | ex = malloc(sizeof(char*)); 45 | while(str[i] != '\0') { 46 | if(!esc) { 47 | if(str[i] == '\\') { 48 | esc = 1; 49 | } 50 | if((!esc) && str[i] == '"') { 51 | if(buf && j > 0) { 52 | ex = realloc(ex, sizeof(char*) * (j + 1)); 53 | ex[j] = '\0'; 54 | (*args)[(*args_count)] = strdup(ex); 55 | (*args_count)++; 56 | memset(ex, 0, sizeof(char*)); 57 | j = 0; 58 | } 59 | buf = !buf; 60 | } else { 61 | if(buf) { 62 | ex = realloc(ex, sizeof(char*) * (j + 1)); 63 | ex[j] = str[i]; 64 | j++; 65 | } 66 | } 67 | } else { 68 | if(buf) { 69 | ex = realloc(ex, sizeof(char*) * (j + 1)); 70 | ex[j] = str[i]; 71 | j++; 72 | } 73 | esc = 0; 74 | } 75 | i++; 76 | } 77 | 78 | //if(buf) { 79 | //ex[j] = '\0'; 80 | //printf("NOT CLOSED \"!:|%s|\n", ex); 81 | //} 82 | 83 | free(ex); 84 | } 85 | } 86 | /* }}} */ 87 | 88 | /* {{{ forp_annotation_tok 89 | * 90 | * Handles annotations in doc_comment : @()<\n> 91 | * 92 | * @param char* doc_comment 93 | * @param char* tag 94 | * @return char* 95 | */ 96 | char *forp_annotation_tok(const char *doc_comment, char *tag TSRMLS_DC) { 97 | char *v = NULL, *v_search = NULL, *t_start = NULL, *tmp = NULL, *eot = NULL; 98 | unsigned int v_start, v_end; 99 | 100 | v_search = malloc(sizeof(char*) * (strlen(tag) + 3)); 101 | if(v_search) { 102 | sprintf(v_search, "@%s(", tag); 103 | t_start = strstr(doc_comment, v_search); 104 | if (t_start) { 105 | v_start = t_start - doc_comment + strlen(v_search); 106 | tmp = strndup(doc_comment + v_start, strlen(doc_comment)); 107 | eot = strstr(tmp, ")"); 108 | v_end = eot - tmp; 109 | v = strndup(doc_comment + v_start, v_end); 110 | } 111 | free(v_search); 112 | } 113 | 114 | return v; 115 | } 116 | /* }}} */ 117 | 118 | /* {{{ forp_annotation_string 119 | * 120 | * Retrieves string arg in an annotation 121 | * 122 | * @param char* doc_comment 123 | * @param char* tag 124 | * @return char* 125 | */ 126 | char *forp_annotation_string(const char *doc_comment, char *tag TSRMLS_DC) { 127 | int args_count; 128 | char *v = NULL; 129 | char **args = malloc(sizeof(char*)); 130 | char *args_str = forp_annotation_tok(doc_comment, tag TSRMLS_CC); 131 | 132 | if(args_str != NULL) { 133 | forp_annotation_args(args_str, &args, &args_count TSRMLS_CC); 134 | if(args_count > 0) { 135 | v = strdup(args[0]); 136 | } 137 | } 138 | free(args); 139 | 140 | return v; 141 | } 142 | /* }}} */ 143 | 144 | /* {{{ forp_annotation_array 145 | * 146 | * Retrieves args array in an annotation 147 | * 148 | * @param char* doc_comment 149 | * @param char* tag 150 | * @param char*** args 151 | * @param int* args_count 152 | * @return void 153 | */ 154 | void forp_annotation_array(const char *doc_comment, char *tag, char ***args, int *args_count TSRMLS_DC) { 155 | char *args_str = forp_annotation_tok(doc_comment, tag TSRMLS_CC); 156 | if(args_str != NULL) { 157 | forp_annotation_args(args_str, args, args_count TSRMLS_CC); 158 | } 159 | } 160 | /* }}} */ -------------------------------------------------------------------------------- /ext/forp/forp_log.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2011 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include "php.h" 24 | #include "php_ini.h" 25 | 26 | #include "forp.h" 27 | #include "php_forp.h" 28 | 29 | #include "forp_log.h" 30 | 31 | #ifdef ZTS 32 | #include "TSRM.h" 33 | #endif 34 | 35 | /* {{{ forp_zval_var 36 | */ 37 | forp_var_t *forp_zval_var(forp_var_t *v, zval *expr, int depth TSRMLS_DC) { 38 | 39 | char s[32]; 40 | char *key, *class_name, *prop_name; 41 | uint key_len, is_tmp; 42 | ulong idx, max_depth; 43 | zval **tmp; 44 | HashTable *ht; 45 | char *resource_type; 46 | 47 | v->level = NULL; 48 | v->value = NULL; 49 | v->class = NULL; 50 | v->arr = NULL; 51 | v->arr_len = 0; 52 | 53 | v->is_ref = PZVAL_IS_REF(expr); 54 | if(Z_REFCOUNT_P(expr)>1) v->refcount = Z_REFCOUNT_P(expr); 55 | 56 | switch(Z_TYPE_P(expr)) { 57 | case IS_DOUBLE : 58 | sprintf(s, "%f", Z_DVAL_P(expr)); 59 | v->value = strdup(s); 60 | v->type = "float"; 61 | break; 62 | case IS_LONG : 63 | sprintf(s, "%d", Z_LVAL_P(expr)); 64 | v->value = strdup(s); 65 | v->type = "int"; 66 | break; 67 | case IS_BOOL : 68 | v->value = Z_BVAL_P(expr) ? "true" : "false"; 69 | v->type = "bool"; 70 | break; 71 | case IS_STRING : 72 | v->type = "string"; 73 | v->value = strdup(Z_STRVAL_P(expr)); 74 | break; 75 | case IS_ARRAY : 76 | v->type = "array"; 77 | 78 | ht = Z_ARRVAL_P(expr); 79 | max_depth = FORP_G(inspect_depth_array); 80 | goto finalize_ht; 81 | case IS_OBJECT : 82 | v->type = "object"; 83 | v->class = strdup(Z_OBJCE_P(expr)->name); 84 | sprintf(s, "#%d", Z_OBJ_HANDLE_P(expr)); 85 | v->value = strdup(s); 86 | 87 | ht = Z_OBJDEBUG_P(expr, is_tmp); 88 | max_depth = FORP_G(inspect_depth_object); 89 | finalize_ht: 90 | 91 | if(depth < max_depth + 1) { 92 | 93 | zend_hash_internal_pointer_reset(ht); 94 | while (zend_hash_get_current_data(ht, (void **) &tmp) == SUCCESS) { 95 | 96 | v->arr = realloc(v->arr, (v->arr_len+1) * sizeof(forp_var_t)); 97 | v->arr[v->arr_len] = malloc(sizeof(forp_var_t)); 98 | v->arr[v->arr_len]->name = NULL; 99 | v->arr[v->arr_len]->stack_idx = -1; 100 | 101 | switch (zend_hash_get_current_key_ex(ht, &key, &key_len, &idx, 0, NULL)) { 102 | case HASH_KEY_IS_STRING : 103 | if(strcmp(v->type, "object") == 0) { 104 | zend_unmangle_property_name(key, key_len, &class_name, &prop_name); 105 | if (class_name) { 106 | v->arr[v->arr_len]->type = strdup(class_name); 107 | if (class_name[0] == '*') { 108 | v->arr[v->arr_len]->level = "protected"; 109 | } else { 110 | v->arr[v->arr_len]->level = "private"; 111 | } 112 | } else { 113 | v->arr[v->arr_len]->level = "public"; 114 | } 115 | v->arr[v->arr_len]->key = strdup(prop_name); 116 | } else { 117 | v->arr[v->arr_len]->key = strdup(key); 118 | } 119 | break; 120 | case HASH_KEY_IS_LONG: 121 | sprintf(s, "%d", idx); 122 | v->arr[v->arr_len]->key = strdup(s); 123 | break; 124 | case HASH_KEY_NON_EXISTANT: 125 | default : 126 | v->arr[v->arr_len]->key = "*"; 127 | break; 128 | } 129 | 130 | forp_zval_var(v->arr[v->arr_len], *tmp, depth + 1 TSRMLS_CC); 131 | 132 | /*php_printf( 133 | "%*s > %s\n", depth, "", 134 | v->arr[v->arr_len]->key 135 | );*/ 136 | 137 | v->arr_len++; 138 | 139 | zend_hash_move_forward(ht); 140 | } 141 | } 142 | 143 | break; 144 | case IS_RESOURCE : 145 | v->type = "resource"; 146 | resource_type = zend_rsrc_list_get_rsrc_type(Z_LVAL_P(expr) TSRMLS_CC); 147 | if(resource_type) { 148 | v->value = strdup(resource_type); 149 | } 150 | break; 151 | case IS_NULL : 152 | v->type = "null"; 153 | break; 154 | } 155 | return v; 156 | } 157 | /* }}} */ 158 | 159 | /* {{{ forp_find_symbol 160 | */ 161 | zval **forp_find_symbol(char* name TSRMLS_DC) { 162 | HashTable *symbols = NULL; 163 | zval **val; 164 | int len = strlen(name) + 1; 165 | 166 | symbols = EG(active_op_array)->static_variables; 167 | if (symbols) { 168 | if (zend_hash_find(symbols, name, len, (void **) &val) == SUCCESS) { 169 | return val; 170 | } 171 | } 172 | 173 | symbols = &EG(symbol_table); 174 | if (zend_hash_find(symbols, name, len, (void **) &val) == SUCCESS) { 175 | return val; 176 | } 177 | 178 | return NULL; 179 | } 180 | /* }}} */ 181 | 182 | /* {{{ forp_inspect_symbol 183 | * 184 | void forp_inspect_symbol(char *name TSRMLS_DC) { 185 | forp_var_t *v = NULL; 186 | zval **val; 187 | 188 | val = forp_find_symbol(name TSRMLS_CC); 189 | if(val != NULL) { 190 | forp_zval_var(v, *val, 1 TSRMLS_CC); 191 | } else { 192 | php_error_docref( 193 | NULL TSRMLS_CC, 194 | E_NOTICE, 195 | "Symbol not found : %s.", 196 | name 197 | ); 198 | } 199 | } 200 | /* }}} */ 201 | 202 | /* {{{ forp_inspect_zval 203 | */ 204 | void forp_inspect_zval(char* name, zval *expr TSRMLS_DC) { 205 | forp_var_t *v = NULL; 206 | 207 | v = malloc(sizeof(forp_var_t)); 208 | v->name = strdup(name); 209 | v->key = NULL; 210 | 211 | // if profiling started then attach the 212 | // current node index 213 | if(FORP_G(current_node)) { 214 | v->stack_idx = FORP_G(current_node)->key ; 215 | } else { 216 | v->stack_idx = -1; 217 | } 218 | 219 | forp_zval_var(v, expr, 1 TSRMLS_CC); 220 | 221 | FORP_G(inspect) = realloc(FORP_G(inspect), (FORP_G(inspect_len)+1) * sizeof(forp_var_t)); 222 | FORP_G(inspect)[FORP_G(inspect_len)] = v; 223 | FORP_G(inspect_len)++; 224 | } 225 | /* }}} */ -------------------------------------------------------------------------------- /ext/forp/php_forp.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2011 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include "php.h" 24 | #include "php_ini.h" 25 | 26 | #include "forp_log.h" 27 | 28 | #include "forp.h" 29 | #include "php_forp.h" 30 | 31 | #include "ext/standard/info.h" 32 | #include "zend_exceptions.h" 33 | 34 | const zend_function_entry forp_functions[] = { 35 | PHP_FE(forp_start, NULL) 36 | PHP_FE(forp_end, NULL) 37 | PHP_FE(forp_dump, NULL) 38 | PHP_FE(forp_print, NULL) 39 | PHP_FE(forp_info, NULL) 40 | PHP_FE(forp_enable, NULL) 41 | PHP_FE(forp_inspect, NULL) 42 | PHP_FE(forp_json, NULL) 43 | PHP_FE(forp_json_google_tracer, NULL) 44 | {NULL,NULL,NULL} /*PHP_FE_END*/ 45 | }; 46 | 47 | zend_module_entry forp_module_entry = { 48 | #if ZEND_MODULE_API_NO >= 20010901 49 | STANDARD_MODULE_HEADER, 50 | #endif 51 | "forp", 52 | forp_functions, 53 | PHP_MINIT(forp), // Main init 54 | PHP_MSHUTDOWN(forp), // Main shutdown 55 | PHP_RINIT(forp), // Request init 56 | PHP_RSHUTDOWN(forp), // Request shutdown 57 | PHP_MINFO(forp), 58 | #if ZEND_MODULE_API_NO >= 20010901 59 | FORP_VERSION, 60 | #endif 61 | #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 2) || PHP_MAJOR_VERSION >= 6 62 | NO_MODULE_GLOBALS, 63 | #endif 64 | /*PHP_GINIT(forp), PHP_GSHUTDOWN(forp),*/ 65 | ZEND_MODULE_POST_ZEND_DEACTIVATE_N(forp), 66 | STANDARD_MODULE_PROPERTIES_EX 67 | }; 68 | 69 | #ifdef COMPILE_DL_FORP 70 | ZEND_GET_MODULE(forp) 71 | #endif 72 | 73 | PHP_INI_BEGIN() 74 | STD_PHP_INI_ENTRY("forp.max_nesting_level", "50", PHP_INI_ALL, OnUpdateLong, max_nesting_level, zend_forp_globals, forp_globals) 75 | STD_PHP_INI_BOOLEAN("forp.no_internals", "0", PHP_INI_ALL, OnUpdateBool, no_internals, zend_forp_globals, forp_globals) 76 | STD_PHP_INI_ENTRY("forp.inspect_depth_object", "2", PHP_INI_ALL, OnUpdateLong, inspect_depth_object, zend_forp_globals, forp_globals) 77 | STD_PHP_INI_ENTRY("forp.inspect_depth_array", "2", PHP_INI_ALL, OnUpdateLong, inspect_depth_object, zend_forp_globals, forp_globals) 78 | PHP_INI_END() 79 | 80 | static void php_forp_init_globals(zend_forp_globals *forp_globals) 81 | { 82 | forp_globals->started = 0; 83 | forp_globals->flags = FORP_FLAG_ALL; 84 | forp_globals->max_nesting_level = 50; 85 | forp_globals->no_internals = 0; 86 | forp_globals->stack_len = 0; 87 | forp_globals->nesting_level = 0; 88 | forp_globals->dump = NULL; 89 | forp_globals->stack = NULL; 90 | forp_globals->main = NULL; 91 | forp_globals->current_node = NULL; 92 | forp_globals->utime = 0; 93 | forp_globals->stime = 0; 94 | forp_globals->inspect = NULL; 95 | forp_globals->inspect_len = 0; 96 | forp_globals->inspect_depth_array = 2; 97 | forp_globals->inspect_depth_object = 2; 98 | } 99 | 100 | PHP_MSHUTDOWN_FUNCTION(forp) { 101 | return SUCCESS; 102 | } 103 | 104 | PHP_MINFO_FUNCTION(forp) { 105 | forp_info(TSRMLS_C); 106 | DISPLAY_INI_ENTRIES(); 107 | } 108 | 109 | ZEND_FUNCTION(forp_info) { 110 | php_info_print_style(TSRMLS_C); 111 | forp_info(TSRMLS_C); 112 | } 113 | 114 | PHP_MINIT_FUNCTION(forp) { 115 | 116 | ZEND_INIT_MODULE_GLOBALS(forp, php_forp_init_globals, NULL); 117 | 118 | REGISTER_INI_ENTRIES(); 119 | 120 | REGISTER_LONG_CONSTANT("FORP_FLAG_MEMORY", FORP_FLAG_MEMORY, 121 | CONST_CS | CONST_PERSISTENT); 122 | REGISTER_LONG_CONSTANT("FORP_FLAG_TIME", FORP_FLAG_TIME, 123 | CONST_CS | CONST_PERSISTENT); 124 | REGISTER_LONG_CONSTANT("FORP_FLAG_CPU", FORP_FLAG_CPU, 125 | CONST_CS | CONST_PERSISTENT); 126 | 127 | REGISTER_LONG_CONSTANT("FORP_FLAG_ALIAS", FORP_FLAG_ALIAS, 128 | CONST_CS | CONST_PERSISTENT); 129 | REGISTER_LONG_CONSTANT("FORP_FLAG_CAPTION", FORP_FLAG_CAPTION, 130 | CONST_CS | CONST_PERSISTENT); 131 | REGISTER_LONG_CONSTANT("FORP_FLAG_GROUPS", FORP_FLAG_GROUPS, 132 | CONST_CS | CONST_PERSISTENT); 133 | REGISTER_LONG_CONSTANT("FORP_FLAG_HIGHLIGHT", FORP_FLAG_HIGHLIGHT, 134 | CONST_CS | CONST_PERSISTENT); 135 | REGISTER_LONG_CONSTANT("FORP_FLAG_ANNOTATIONS", FORP_FLAG_ANNOTATIONS, 136 | CONST_CS | CONST_PERSISTENT); 137 | 138 | REGISTER_LONG_CONSTANT("FORP_FLAG_ALL", FORP_FLAG_ALL, 139 | CONST_CS | CONST_PERSISTENT); 140 | 141 | return SUCCESS; 142 | } 143 | 144 | PHP_RINIT_FUNCTION(forp) { 145 | 146 | FORP_G(started) = 0; 147 | FORP_G(flags) = FORP_FLAG_ALL; 148 | FORP_G(stack_len) = 0; 149 | FORP_G(nesting_level) = 0; 150 | FORP_G(dump) = NULL; 151 | FORP_G(stack) = NULL; 152 | FORP_G(main) = NULL; 153 | FORP_G(current_node) = NULL; 154 | FORP_G(utime) = 0; 155 | FORP_G(stime) = 0; 156 | FORP_G(inspect) = NULL; 157 | FORP_G(inspect_len) = 0; 158 | 159 | // globals 160 | //FORP_G(max_nesting_level) = 50; 161 | //FORP_G(no_internals) = 0; 162 | //FORP_G(inspect_depth_array) = 2; 163 | //FORP_G(inspect_depth_object) = 2; 164 | 165 | return SUCCESS; 166 | } 167 | 168 | PHP_RSHUTDOWN_FUNCTION(forp) { 169 | 170 | if(FORP_G(started)) { 171 | // Restores zend api methods 172 | 173 | #if PHP_VERSION_ID < 50500 174 | if (old_execute) { 175 | zend_execute = old_execute; 176 | } 177 | #else 178 | if (old_execute_ex) { 179 | zend_execute_ex = old_execute_ex; 180 | } 181 | #endif 182 | 183 | if (!FORP_G(no_internals)) { 184 | zend_execute_internal = old_execute_internal; 185 | } 186 | } 187 | 188 | return SUCCESS; 189 | } 190 | 191 | ZEND_MODULE_POST_ZEND_DEACTIVATE_D(forp) { 192 | int i, j; 193 | 194 | TSRMLS_FETCH(); 195 | 196 | // TODO track not terminated node 197 | 198 | FORP_G(started) = 0; 199 | FORP_G(nesting_level) = 0; 200 | FORP_G(current_node) = NULL; 201 | FORP_G(utime) = 0; 202 | FORP_G(stime) = 0; 203 | 204 | // stack dtor 205 | if (FORP_G(stack) != NULL) { 206 | for (i = 0; i < FORP_G(stack_len); ++i) { 207 | if(FORP_G(stack)[i]->function.groups) { 208 | free(FORP_G(stack)[i]->function.groups); 209 | } 210 | free(FORP_G(stack)[i]); 211 | } 212 | if (i) free(FORP_G(stack)); 213 | } 214 | FORP_G(stack_len) = 0; 215 | FORP_G(stack) = NULL; 216 | 217 | // dump dtor 218 | if (FORP_G(dump) != NULL) zval_ptr_dtor(&FORP_G(dump)); 219 | FORP_G(dump) = NULL; 220 | 221 | // inspect 222 | if (FORP_G(inspect) != NULL) free(FORP_G(inspect)); 223 | FORP_G(inspect) = NULL; 224 | 225 | return SUCCESS; 226 | } 227 | 228 | ZEND_FUNCTION(forp_stats) { 229 | int i; 230 | zval *zvar; 231 | MAKE_STD_ZVAL(zvar); 232 | array_init(zvar); 233 | if(FORP_G(inspect_len) > 0) { 234 | php_printf("\n\x1B[37m-\x1B[36mdebug\x1B[37m--------------------------------------------------------------------------%s", PHP_EOL); 235 | for (i = 0; i < FORP_G(inspect_len); ++i) { 236 | forp_stack_dump_cli_var(FORP_G(inspect)[i], 0 TSRMLS_CC); 237 | } 238 | } 239 | RETURN_ZVAL(zvar, 1, 0); 240 | 241 | } 242 | 243 | ZEND_FUNCTION(forp_enable) { 244 | 245 | long opt = -1; 246 | php_error_docref( 247 | NULL TSRMLS_CC, 248 | E_USER_DEPRECATED, 249 | "forp_enable() is deprecated, use forp_start()." 250 | ); 251 | 252 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &opt) == FAILURE) { 253 | return; 254 | } 255 | if(opt >= 0) FORP_G(flags) = opt; 256 | forp_start(TSRMLS_C); 257 | } 258 | 259 | ZEND_FUNCTION(forp_start) { 260 | long opt = -1; 261 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &opt) == FAILURE) { 262 | return; 263 | } 264 | if(opt >= 0) FORP_G(flags) = opt; 265 | 266 | forp_start(TSRMLS_C); 267 | } 268 | 269 | ZEND_FUNCTION(forp_end) { 270 | forp_end(TSRMLS_C); 271 | } 272 | 273 | ZEND_FUNCTION(forp_dump) { 274 | if (FORP_G(started)) { 275 | forp_end(TSRMLS_C); 276 | } 277 | if (!FORP_G(dump)) { 278 | forp_stack_dump(TSRMLS_C); 279 | } 280 | RETURN_ZVAL(FORP_G(dump), 1, 0); 281 | } 282 | 283 | ZEND_FUNCTION(forp_print) { 284 | if (FORP_G(started)) { 285 | forp_end(TSRMLS_C); 286 | } 287 | forp_stack_dump_cli(TSRMLS_C); 288 | } 289 | 290 | ZEND_FUNCTION(forp_inspect) { 291 | char *name; 292 | int name_len; 293 | zval *expr; 294 | 295 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name, &name_len, &expr) == FAILURE) { 296 | return; 297 | } 298 | 299 | forp_inspect_zval(name, expr TSRMLS_CC); 300 | 301 | RETURN_TRUE; 302 | } 303 | 304 | ZEND_FUNCTION(forp_json) { 305 | forp_json(TSRMLS_C); 306 | } 307 | 308 | ZEND_FUNCTION(forp_json_google_tracer) { 309 | char* filepath = NULL; 310 | int filepath_len; 311 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filepath, &filepath_len) == FAILURE) { 312 | return; 313 | } 314 | if (strlen(filepath) != filepath_len) { 315 | return; 316 | } 317 | forp_json_google_tracer(filepath TSRMLS_CC); 318 | } 319 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction # 2 | 3 | forp is a lightweight PHP extension which provides PHP profile datas. 4 | 5 | Summary of features : 6 | - measurement of time and allocated memory for each function 7 | - CPU usage 8 | - file and line number of the function call 9 | - output as Google's Trace Event format 10 | - caption of functions 11 | - grouping of functions 12 | - aliases of functions (useful for anonymous functions) 13 | 14 | forp is non intrusive, it provides PHP annotations to do its work. 15 | 16 | # Simple (almost the most complicated) example # 17 | 18 | Example : 19 | ```php 20 | 0 47 | [stime] => 0 48 | [stack] => Array 49 | ( 50 | [0] => Array 51 | ( 52 | [file] => /home/anthony/phpsrc/php-5.3.8/ext/forp/forp.php 53 | [function] => {main} 54 | [usec] => 94 55 | [pusec] => 6 56 | [bytes] => 524 57 | [level] => 0 58 | ) 59 | 60 | [1] => Array 61 | ( 62 | [file] => /home/anthony/phpsrc/php-5.3.8/ext/forp/forp.php 63 | [function] => foo 64 | [lineno] => 10 65 | [usec] => 9 66 | [pusec] => 6 67 | [bytes] => 120 68 | [level] => 1 69 | [parent] => 0 70 | ) 71 | 72 | ) 73 | 74 | ) 75 | ``` 76 | 77 | # Example with annotations # 78 | 79 | Example : 80 | ```php 81 | 0 117 | [stime] => 0 118 | [stack] => Array 119 | ( 120 | [0] => Array 121 | ( 122 | [file] => /home/anthony/phpsrc/php-5.3.8/ext/forp/forp.php 123 | [function] => {main} 124 | [usec] => 113 125 | [pusec] => 6 126 | [bytes] => 568 127 | [level] => 0 128 | ) 129 | 130 | [1] => Array 131 | ( 132 | [file] => /home/anthony/phpsrc/php-5.3.8/ext/forp/forp.php 133 | [function] => foo 134 | [lineno] => 41 135 | [groups] => Array 136 | ( 137 | [0] => Test 138 | ) 139 | 140 | [caption] => Foo bar 141 | [usec] => 39 142 | [pusec] => 24 143 | [bytes] => 124 144 | [level] => 1 145 | [parent] => 0 146 | ) 147 | 148 | ) 149 | 150 | ) 151 | ``` 152 | 153 | # php.ini options # 154 | 155 | - forp.max_nesting_level : default 50 156 | - forp.no_internals : default 0 157 | 158 | # forp PHP API # 159 | 160 | - forp_start(flags*) : start forp collector 161 | - forp_end() : stop forp collector 162 | - forp_dump() : return stack as flat array 163 | - forp_print() : display forp stack (SAPI CLI) 164 | 165 | ## forp_start() flags ## 166 | 167 | - FORP_FLAG_TIME : activate collect of time 168 | - FORP_FLAG_MEMORY : activate collect of memory usage 169 | - FORP_FLAG_CPU : retrieve the cpu usage 170 | - FORP_FLAG_CAPTION : enable caption handler 171 | - FORP_FLAG_ALIAS : enable alias handler 172 | - FORP_FLAG_GROUPS : enable groups handler 173 | - FORP_FLAG_HIGHLIGHT : enable HTML highlighting 174 | 175 | ## forp_dump() ## 176 | 177 | forp_dump() provides an array composed of : 178 | 179 | - global fields : utime, stime ... 180 | - stack : a flat PHP array of stack entries. 181 | 182 | Global fields : 183 | 184 | - utime : CPU used for user function calls 185 | - stime : CPU used for system calls 186 | 187 | Fields of a stack entry : 188 | 189 | - file : file of the call 190 | - lineno : line number of the call 191 | - class : Class name 192 | - function : function name 193 | - groups : list of associated groups 194 | - caption : caption of the function 195 | - usec : function time (without the profiling overhead) 196 | - pusec : inner profiling time (without executing the function) 197 | - bytes : memory usage of the function 198 | - level : depth level from the forp_start call 199 | - parent : parent index (numeric) 200 | 201 | ## forp_json() ## 202 | 203 | Prints stack as JSON string directly on stdout. 204 | This is the fastest method in order to send the stack to a JSON compatible client. 205 | 206 | See forp_dump() for its structure. 207 | 208 | ## forp_json_google_tracer() ## 209 | 210 | forp_json_google_tracer($filepath) will output stack as Google Trace Event format. 211 | 212 | Usage : 213 | ```php 214 | // Start profiler 215 | forp_start(); 216 | 217 | my_complex_function(); 218 | 219 | // Stop profiler 220 | forp_end(); 221 | 222 | // Get JSON and save it into file 223 | forp_json_google_tracer("/tmp/output.json"); 224 | ``` 225 | Then, open Google Chrome or Chromium browser and go to chrome://tracing/. Load the output file and enjoy the result. 226 | 227 | ![Example output with an existing Drupal website](/docs/images/google-tracing-example_thumb.png?raw=true "Google Tracing Format example output") 228 | 229 | 230 | ## forp_inspect() ## 231 | 232 | forp_inspect('symbol', $symbol) will output a detailed representation of a variable in the forp_dump() result. 233 | 234 | Usage : 235 | ```php 236 | $var = array(0 => "my", "strkey" => "inspected", 3 => "array"); 237 | forp_inspect('var', $var); 238 | print_r(forp_dump()); 239 | ``` 240 | 241 | Result : 242 | ```php 243 | Array 244 | ( 245 | [utime] => 0 246 | [stime] => 0 247 | [inspect] => Array 248 | ( 249 | [var] => Array 250 | ( 251 | [type] => array 252 | [size] => 3 253 | [value] => Array 254 | ( 255 | [0] => Array 256 | ( 257 | [type] => string 258 | [value] => my 259 | ) 260 | 261 | [strkey] => Array 262 | ( 263 | [type] => string 264 | [value] => inspected 265 | ) 266 | 267 | [3] => Array 268 | ( 269 | [type] => string 270 | [value] => array 271 | ) 272 | 273 | ) 274 | 275 | ) 276 | 277 | ) 278 | 279 | ) 280 | ``` 281 | 282 | ## Available annotations ## 283 | 284 | - @ProfileGroup 285 | 286 | Sets groups that function belongs. 287 | 288 | ```php 289 | /** 290 | * @ProfileGroup("data loading","rendering") 291 | */ 292 | function exec($query) { 293 | /* ... */ 294 | } 295 | ``` 296 | 297 | - @ProfileCaption 298 | 299 | Adds caption to functions. Caption string may contain references (#) to parameters of the function. 300 | 301 | ```php 302 | /** 303 | * @ProfileCaption("Find row for pk #1") 304 | */ 305 | public function findByPk($pk) { 306 | /* ... */ 307 | } 308 | ``` 309 | 310 | - @ProfileAlias 311 | 312 | Gives an alias name to a function. Useful for anonymous functions 313 | 314 | ```php 315 | /** 316 | * @ProfileAlias("MyAnonymousFunction") 317 | */ 318 | $fn = function() { 319 | /* ... */ 320 | } 321 | ``` 322 | 323 | - @ProfileHighlight 324 | 325 | Adds a frame around output generated by the function. 326 | 327 | ```php 328 | /** 329 | * @ProfileHighlight("1") 330 | */ 331 | function render($datas) { 332 | /* ... */ 333 | } 334 | ``` 335 | 336 | # Demos / UI # 337 | 338 | forp provides a structure so simple that it's easy to make your own UI. 339 | 340 | But, if you are looking for a user interface with a lot of features, we've made a rich JavaScript client : 341 | [forp-ui](https://github.com/aterrien/forp-ui/) 342 | 343 | You can see forp in action with the fastest PHP Frameworks : 344 | - [Beaba](http://forp.io/beaba/hello) (https://github.com/ichiriac/beaba-light) 345 | - [Slim](http://forp.io/slim/hello) (https://github.com/codeguy/Slim) 346 | - [Silex](http://forp.io/silex/hello) (https://github.com/fabpot/Silex) 347 | - [Laravel](http://forp.io/laravel/hello) (https://github.com/laravel/laravel) 348 | 349 | ![groups](https://raw.github.com/aterrien/forp-ui/master/doc/ui-consolas-groups.png) 350 | 351 | # Build status # 352 | 353 | [![Build Status](https://secure.travis-ci.org/aterrien/forp-PHP-profiler.png)](http://travis-ci.org/aterrien/forp-PHP-profiler) 354 | 355 | # Installation, requirements # 356 | 357 | ## Requirements ## 358 | 359 | php5-dev 360 | 361 | ```sh 362 | apt-get install php5-dev 363 | ``` 364 | 365 | ## Install with Composer ## 366 | 367 | Require forp-PHP-profiler in your project composer.json 368 | 369 | ```json 370 | "require-dev": { 371 | "aterrien/forp-profiler" : "~1.1" 372 | }, 373 | "repositories" : [ 374 | { 375 | "type" : "git", 376 | "url" : "git@github.com:aterrien/forp-PHP-profiler.git" 377 | } 378 | ] 379 | ``` 380 | run Composer install 381 | ```sh 382 | php composer.phar install 383 | ``` 384 | compile 385 | ```sh 386 | cd vendor/aterrien/forp-profiler/ext/forp 387 | phpize 388 | ./configure 389 | make && make install 390 | ``` 391 | and enable forp in your php.ini 392 | ```sh 393 | extension=forp.so 394 | ``` 395 | ## or opt for "old school" installation ## 396 | 397 | Use current release 398 | ```sh 399 | wget https://github.com/aterrien/forp/archive/1.1.0.tar.gz 400 | tar -xvzf 1.1.0.tar.gz 401 | cd 1.1.0/ext/forp 402 | ``` 403 | OR dev-master (unstable, at your own risk) 404 | ```sh 405 | git clone https://github.com/aterrien/forp-PHP-profiler 406 | cd forp-PHP-profiler/ext/forp 407 | ``` 408 | compile 409 | ```sh 410 | phpize 411 | ./configure 412 | make && make install 413 | ``` 414 | and enable forp in your php.ini 415 | ```sh 416 | extension=forp.so 417 | ``` 418 | 419 | ## Tested OS and platforms ## 420 | 421 | ### Apache ### 422 | 423 | Apache/2.2.16 (Debian) 424 | 425 | PHP 5.3.8 (cli) (built: Sep 25 2012 22:55:18) 426 | 427 | ### Nginx / php-fpm ### 428 | 429 | nginx version: nginx/1.2.6 430 | 431 | PHP 5.3.21-1~dotdeb.0 (fpm-fcgi) 432 | PHP 5.4.10-1~dotdeb.0 (fpm-fcgi) 433 | 434 | ### Cloud / AWS ### 435 | 436 | Centos 6.3 (AMI) 437 | 438 | Apache/2.2.15 (Unix) 439 | 440 | PHP 5.4.13 (cli) (built: Mar 15 2013 11:27:51) 441 | 442 | # Contributors # 443 | 444 | [Anthony Terrien](https://github.com/aterrien/), 445 | [Ioan Chiriac](https://github.com/ichiriac/), 446 | [Alexis Okuwa](https://github.com/wojons/), 447 | [TOMHTML](https://github.com/TOMHTML/) 448 | -------------------------------------------------------------------------------- /ext/forp/forp_json.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2011 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include "php.h" 24 | #include "php_ini.h" 25 | #include "SAPI.h" 26 | 27 | #include "forp.h" 28 | #include "php_forp.h" 29 | #include "forp_string.h" 30 | #include "forp_json.h" 31 | 32 | #ifdef ZTS 33 | #include "TSRM.h" 34 | #endif 35 | 36 | #if defined(PHP_WIN32) 37 | double round(double d) 38 | { 39 | return floor(d + 0.5); 40 | } 41 | #endif 42 | 43 | /* {{{ forp_json 44 | */ 45 | void forp_json(TSRMLS_D) { 46 | int i, j; 47 | // TODO Win path 48 | char psep[] = "/"; 49 | char epsep[] = "\\/"; 50 | char nssep[] = "\\"; 51 | char enssep[] = "\\\\"; 52 | forp_node_t *n; 53 | 54 | /* 55 | { 56 | utime : 4000, 57 | stime : 4000, 58 | stack : [ 59 | { 60 | file : "\/var\/www\/app\/myfile.php", 61 | usec : 100, 62 | bytes : 64, 63 | function : "{main}", 64 | caption : "This is the caption of {main}.", 65 | level : 0 66 | }, 67 | { 68 | file : "\/var\/www\/app\/myfile.php", 69 | lineno : 2, 70 | usec : 50, 71 | bytes : 64, 72 | class : "MyClass", 73 | function : "myFunction", 74 | caption : "This is the caption of an entry.", 75 | groups : ["Example", "Foo"], 76 | level : 1, 77 | parent : 0 78 | } 79 | ] 80 | } 81 | */ 82 | 83 | if (FORP_G(started)) { 84 | forp_end(TSRMLS_C); 85 | } 86 | 87 | if(FORP_G(stack_len)) { 88 | 89 | php_printf("{"); 90 | php_printf("\"utime\":%0.0f,", FORP_G(utime)); 91 | php_printf("\"stime\":%0.0f,", FORP_G(stime)); 92 | php_printf("\"stack\":["); 93 | 94 | for (i = 0; i < FORP_G(stack_len); ++i) { 95 | 96 | php_printf("{"); 97 | 98 | n = FORP_G(stack)[i]; 99 | 100 | if (n->function.filename) { 101 | php_printf( 102 | "\"%s\":\"%s\",", 103 | FORP_DUMP_ASSOC_FILE, 104 | forp_str_replace(psep,epsep,n->function.filename TSRMLS_CC) 105 | ); 106 | } else if (n->filename) { 107 | php_printf( 108 | "\"%s\":\"%s\",", 109 | FORP_DUMP_ASSOC_FILE, 110 | forp_str_replace(psep,epsep,n->filename TSRMLS_CC) 111 | ); 112 | } else { 113 | php_printf( 114 | "\"%s\":\"**internal**\",", 115 | FORP_DUMP_ASSOC_FILE 116 | ); 117 | } 118 | 119 | if (n->function.class) { 120 | php_printf( 121 | "\"%s\":\"%s\",", 122 | FORP_DUMP_ASSOC_CLASS, 123 | forp_str_replace(nssep,enssep,n->function.class TSRMLS_CC) 124 | ); 125 | } 126 | 127 | if (n->alias) { 128 | php_printf("\"%s\":\"%s\",", FORP_DUMP_ASSOC_FUNCTION, n->alias); 129 | } else if (n->function.function) { 130 | php_printf("\"%s\":\"%s\",", FORP_DUMP_ASSOC_FUNCTION, n->function.function); 131 | } 132 | 133 | if (n->lineno) 134 | php_printf("\"%s\":%d,", FORP_DUMP_ASSOC_LINENO, n->lineno); 135 | 136 | if (n->function.groups && n->function.groups_len > 0) { 137 | j = 0; 138 | php_printf("\"%s\":[", FORP_DUMP_ASSOC_GROUPS); 139 | while(j < n->function.groups_len) { 140 | php_printf("\"%s\"", n->function.groups[j]); 141 | if(j < n->function.groups_len - 1) 142 | php_printf(","); 143 | j++; 144 | } 145 | php_printf("],"); 146 | } 147 | 148 | 149 | if (n->caption) { 150 | php_printf("\"%s\":\"%s\",", FORP_DUMP_ASSOC_CAPTION, n->caption); 151 | } 152 | 153 | if(FORP_G(flags) & FORP_FLAG_TIME) { 154 | php_printf("\"%s\":%0.0f,", FORP_DUMP_ASSOC_DURATION, round(n->time * 1000000.0) / 1000000.0); 155 | php_printf("\"%s\":%0.0f,", FORP_DUMP_ASSOC_PROFILERTIME, round(n->profiler_duration * 1000000.0) / 1000000.0); 156 | } 157 | 158 | if(FORP_G(flags) & FORP_FLAG_MEMORY) { 159 | php_printf("\"%s\":%d,", FORP_DUMP_ASSOC_MEMORY, n->mem); 160 | } 161 | 162 | if (n->parent) { 163 | php_printf("\"%s\":%d,", FORP_DUMP_ASSOC_PARENT, n->parent->key); 164 | //add_assoc_long(entry, FORP_DUMP_ASSOC_PARENT, n->parent->key); 165 | } 166 | 167 | php_printf("\"%s\":%d", FORP_DUMP_ASSOC_LEVEL, n->level); 168 | 169 | php_printf("}"); 170 | 171 | if(i < FORP_G(stack_len) - 1) php_printf(","); 172 | } 173 | if(FORP_G(inspect_len)) { 174 | php_printf("],\"inspect\":{"); 175 | for (i = 0; i < FORP_G(inspect_len); i++) { 176 | php_printf("\"%s\": {",FORP_G(inspect)[i]->name); 177 | forp_json_inspect(FORP_G(inspect)[i] TSRMLS_CC); 178 | if ( i + 1 < FORP_G(inspect_len) ) { 179 | php_printf("},"); 180 | } else { 181 | php_printf("}"); 182 | } 183 | } 184 | php_printf("}}"); 185 | } else { 186 | php_printf("]}"); 187 | } 188 | } 189 | } 190 | 191 | /* {{{ forp_json_inspect 192 | * Recursive output inspect structure 193 | */ 194 | void forp_json_inspect(forp_var_t *var TSRMLS_DC) { 195 | uint i; 196 | if(var->stack_idx > -1) php_printf("\"stack_idx\":%d,", var->stack_idx); 197 | if(var->type) php_printf("\"type\":\"%s\",", var->type); 198 | if(var->level) php_printf("\"level\":\"%s\",", var->level); 199 | if(var->class) php_printf("\"class\":\"%s\",", var->class); 200 | if(var->is_ref) { 201 | php_printf("\"is_ref\": true,"); 202 | if(var->refcount > 1) php_printf("\"refcount\":%d,", var->refcount); 203 | } 204 | if(var->arr_len) { 205 | if(strcmp(var->type, "object") == 0) { 206 | php_printf("\"properties\":{"); 207 | } else { 208 | php_printf("\"value\":{"); 209 | } 210 | for(i = 0; i < var->arr_len; i++) { 211 | php_printf("\"%s\": {", forp_addslashes(var->arr[i]->key TSRMLS_CC)); 212 | forp_json_inspect(var->arr[i] TSRMLS_CC); 213 | if ( i + 1 < var->arr_len ) { 214 | php_printf("},"); 215 | } else { 216 | php_printf("}"); 217 | } 218 | } 219 | php_printf("}"); 220 | } else { 221 | php_printf("\"value\":\"%s\"", forp_addslashes(var->value TSRMLS_CC)); 222 | } 223 | } 224 | 225 | /* {{{ forp_json_google_tracer 226 | */ 227 | void forp_json_google_tracer(char *filepath TSRMLS_DC) { 228 | FILE *fp; 229 | 230 | int i, j; 231 | // TODO Win path 232 | char psep[] = "/"; 233 | char epsep[] = "\\/"; 234 | char nssep[] = "\\"; 235 | char enssep[] = "\\\\"; 236 | forp_node_t *n; 237 | 238 | /* 239 | { 240 | "traceEvents":[ 241 | { 242 | "name":"main", 243 | "cat":"PHP", 244 | "ts":1407355698849, 245 | "pid":0, 246 | "tid":0, 247 | "ph":"B" 248 | }, 249 | ... 250 | */ 251 | 252 | if (FORP_G(started)) { 253 | forp_end(TSRMLS_C); 254 | } 255 | 256 | if(FORP_G(stack_len)) { 257 | // First, delete existing file 258 | unlink(filepath); 259 | // Then, open and append everything 260 | fp = fopen(filepath, "a"); 261 | 262 | fprintf(fp,"{"); 263 | fprintf(fp,"\"traceEvents\":["); 264 | // Some metadata 265 | fprintf(fp,"{\"args\": {\"name\": \"%s\"}, \"cat\": \"__metadata\", \"name\": \"process_name\", \"ph\": \"M\", \"pid\": 0, \"tid\": 0, \"ts\": 0 },", sapi_module.name); 266 | fprintf(fp,"{\"args\": {\"name\": \"PHP\"}, \"cat\": \"__metadata\", \"name\": \"thread_name\", \"ph\": \"M\", \"pid\": 0, \"tid\": 0, \"ts\": 0 },"); 267 | for (i = 0; i < FORP_G(stack_len); ++i) { 268 | 269 | fprintf(fp,"{"); 270 | fprintf(fp,"\"%s\":\"%s\",", "ph", "X"); // "Complete event" 271 | fprintf(fp,"\"%s\":\"%s\",", "pid", "0"); // Fake PID 272 | fprintf(fp,"\"%s\":\"%s\",", "tid", "0"); // Fake TID 273 | n = FORP_G(stack)[i]; 274 | 275 | if (n->alias) { 276 | fprintf(fp,"\"%s\":\"%s\",", "name", n->alias); 277 | } else if (n->function.function) { 278 | if (n->function.class) { 279 | // class and method 280 | fprintf(fp, 281 | "\"%s\":\"%s::%s\",", 282 | "name", 283 | forp_str_replace(nssep,enssep,n->function.class TSRMLS_CC), 284 | n->function.function 285 | ); 286 | } 287 | else { 288 | // simple function 289 | fprintf(fp,"\"%s\":\"%s\",", "name", n->function.function); 290 | } 291 | } 292 | 293 | if (n->function.groups && n->function.groups_len > 0) { 294 | j = 0; 295 | fprintf(fp,"\"%s\":\"", "cat"); 296 | while(j < n->function.groups_len) { 297 | fprintf(fp,"%s", n->function.groups[j]); 298 | if(j < n->function.groups_len - 1){ 299 | fprintf(fp,","); 300 | } 301 | j++; 302 | } 303 | fprintf(fp,"\","); 304 | } 305 | else { 306 | fprintf(fp,"\"%s\":\"%s\",", "cat", "PHP"); 307 | } 308 | 309 | if(FORP_G(flags) & FORP_FLAG_TIME) { 310 | fprintf(fp,"\"%s\":%0.0f,", "dur", round(n->time * 1000000.0) / 1000000.0); 311 | fprintf(fp,"\"%s\":%0.0f", "ts", n->time_begin); 312 | // fprintf(fp,"\"%s\":%0.0f,", FORP_DUMP_ASSOC_PROFILERTIME, round(n->profiler_duration * 1000000.0) / 1000000.0); 313 | } 314 | 315 | fprintf(fp,"}"); 316 | if(i < FORP_G(stack_len) - 1) { 317 | fprintf(fp,","); 318 | } 319 | } 320 | 321 | fprintf(fp,"]}"); 322 | fclose(fp); 323 | return; 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /ext/forp/forp.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2011 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Anthony Terrien | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include "php.h" 24 | #include "php_ini.h" 25 | 26 | #include "forp.h" 27 | #include "php_forp.h" 28 | 29 | #include "forp_string.h" 30 | #include "forp_annotation.h" 31 | 32 | #if HAVE_SYS_TIME_H 33 | #include 34 | #elif defined(PHP_WIN32) 35 | #include "win32/time.h" 36 | #include 37 | static inline double round(double val) { 38 | return floor(val + 0.5); 39 | } 40 | #endif 41 | 42 | #if HAVE_SYS_RESOURCE_H 43 | #include 44 | #endif 45 | 46 | /* {{{ forp_populate_function 47 | */ 48 | static void forp_populate_function( 49 | forp_function_t *function, 50 | zend_execute_data *edata, 51 | zend_op_array *op_array TSRMLS_DC 52 | ) { 53 | 54 | // Inits function struct 55 | function->class = NULL; 56 | function->function = NULL; 57 | function->filename = NULL; 58 | 59 | // Retrieves class and function names 60 | if (edata->function_state.function->common.function_name) { 61 | if (edata->object) { 62 | if (edata->function_state.function->common.scope) { 63 | function->class = strdup(edata->function_state.function->common.scope->name); 64 | } 65 | } else if ( 66 | EG(scope) 67 | && edata->function_state.function->common.scope 68 | && edata->function_state.function->common.scope->name 69 | ) { 70 | function->class = strdup(edata->function_state.function->common.scope->name); 71 | } 72 | 73 | function->function = strdup(edata->function_state.function->common.function_name); 74 | } else { 75 | #if PHP_VERSION_ID >= 50399 76 | switch (function->type = edata->opline->extended_value) { 77 | #else 78 | switch (function->type = edata->opline->op2.u.constant.value.lval) { 79 | #endif 80 | case ZEND_EVAL: 81 | function->function = "eval"; 82 | break; 83 | case ZEND_INCLUDE: 84 | function->function = "include"; 85 | break; 86 | case ZEND_REQUIRE: 87 | function->function = "require"; 88 | break; 89 | case ZEND_INCLUDE_ONCE: 90 | function->function = "include_once"; 91 | break; 92 | case ZEND_REQUIRE_ONCE: 93 | function->function = "require_once"; 94 | break; 95 | default: 96 | function->function = "unknown"; 97 | break; 98 | } 99 | } 100 | 101 | // Stores filename 102 | if (op_array && op_array->filename) { 103 | function->filename = strdup(op_array->filename); 104 | } else { 105 | if (edata->op_array && edata->op_array->filename) { 106 | function->filename = strdup(edata->op_array->filename); 107 | } else { 108 | function->filename = NULL; 109 | } 110 | } 111 | } 112 | /* }}} */ 113 | 114 | /* {{{ forp_open_node 115 | */ 116 | forp_node_t *forp_open_node(zend_execute_data *edata, zend_op_array *op_array TSRMLS_DC) { 117 | struct timeval tv; 118 | forp_node_t *n; 119 | int key; 120 | double start_time; 121 | 122 | // preparing current node 123 | // will be profiled after 124 | // forp_is_profiling_function test 125 | n = malloc(sizeof (forp_node_t)); 126 | 127 | // getting function infos 128 | if(edata) { 129 | forp_populate_function(&(n->function), edata, op_array TSRMLS_CC); 130 | if(forp_is_profiling_function(n TSRMLS_CC)) { 131 | free(n); 132 | return NULL; 133 | } 134 | } 135 | 136 | // self duration on open 137 | gettimeofday(&tv, NULL); 138 | start_time = tv.tv_sec * 1000000.0 + tv.tv_usec; 139 | 140 | // continues node init 141 | n->state = 1; // opened 142 | n->level = FORP_G(nesting_level)++; 143 | n->parent = FORP_G(current_node); 144 | 145 | n->time_begin = 0; 146 | n->time_end = 0; 147 | n->time = 0; 148 | 149 | n->profiler_duration = 0; 150 | 151 | n->mem_begin = 0; 152 | n->mem_end = 0; 153 | n->mem = 0; 154 | 155 | // Call file and line number 156 | n->filename = NULL; 157 | n->lineno = 0; 158 | 159 | // Annotations 160 | n->alias = NULL; 161 | n->caption = NULL; 162 | n->function.groups = NULL; 163 | n->function.groups_len = 0; 164 | n->function.highlight = NULL; 165 | 166 | // Handles annotations 167 | if(op_array && op_array->doc_comment) { 168 | if(FORP_G(flags) & FORP_FLAG_ALIAS) { 169 | n->alias = forp_annotation_string(op_array->doc_comment, "ProfileAlias" TSRMLS_CC); 170 | } 171 | if(FORP_G(flags) & FORP_FLAG_CAPTION) { 172 | n->caption = forp_annotation_string(op_array->doc_comment, "ProfileCaption" TSRMLS_CC); 173 | } 174 | if(FORP_G(flags) & FORP_FLAG_GROUPS) { 175 | n->function.groups = malloc(sizeof(char*) * 10); 176 | n->function.groups_len = 0; 177 | forp_annotation_array(op_array->doc_comment, "ProfileGroup", &(n->function.groups), &(n->function.groups_len) TSRMLS_CC); 178 | } 179 | if(FORP_G(flags) & FORP_FLAG_HIGHLIGHT) { 180 | n->function.highlight = forp_annotation_string(op_array->doc_comment, "ProfileHighlight" TSRMLS_CC); 181 | if(n->function.highlight) php_printf(FORP_HIGHLIGHT_BEGIN); 182 | } 183 | } 184 | 185 | if(edata) { 186 | 187 | // Retrieves filename 188 | if(FORP_G(current_node) && FORP_G(current_node)->function.filename) { 189 | n->filename = strdup(FORP_G(current_node)->function.filename); 190 | } 191 | 192 | // Retrieves call lineno 193 | if(edata->opline && n->filename) { 194 | n->lineno = edata->opline->lineno; 195 | } 196 | 197 | // Collects params 198 | if(n->caption) { 199 | 200 | #if PHP_VERSION_ID >= 50500 201 | if(zend_vm_stack_get_args_count(TSRMLS_C) > 0) { 202 | char c[4]; 203 | zval **arg; 204 | const char *nums = "123456789"; 205 | char *result = NULL; 206 | char delims[] = "#"; 207 | char *to; 208 | char *val; 209 | result = strtok( strdup(n->caption), delims ); 210 | while( result != NULL ) { 211 | if (strchr(nums, result[0])) { 212 | to = strndup(result, 1); 213 | arg = zend_vm_stack_get_arg(atoi(to) TSRMLS_CC); 214 | sprintf(c, "#%d", atoi(to)); 215 | switch(Z_TYPE_PP(arg)) { 216 | case IS_DOUBLE: case IS_LONG: case IS_BOOL: 217 | case IS_NULL: case IS_STRING: 218 | convert_to_string(*arg); 219 | val = Z_STRVAL_PP(arg); 220 | break; 221 | case IS_RESOURCE: 222 | val = "Resource"; 223 | break; 224 | case IS_OBJECT: 225 | val = "Object"; 226 | break; 227 | case IS_ARRAY: 228 | val = "Array"; 229 | break; 230 | default: 231 | val = ""; 232 | } 233 | n->caption = forp_str_replace( 234 | c, val, 235 | n->caption TSRMLS_CC 236 | ); 237 | } 238 | result = strtok( NULL, delims ); 239 | } 240 | } 241 | #else 242 | void **params; 243 | int params_count; 244 | int i; 245 | 246 | // Retrieves params in zend_vm_stack 247 | #if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 2) 248 | params = EG(argument_stack)->top-1; 249 | #else 250 | params = EG(argument_stack).top_element-1; 251 | #endif 252 | 253 | params_count = (ulong) *params; 254 | for(i = 1; i <= params_count; i++) { 255 | char c[4]; 256 | char *v, *v_copy; 257 | zval *expr; 258 | zval expr_copy; 259 | int use_copy; 260 | 261 | sprintf(c, "#%d", params_count - i + 1); 262 | expr = *((zval **) (params - i)); 263 | 264 | if(Z_TYPE_P(expr) == IS_OBJECT) { 265 | // Object or Closure 266 | // Closure throws a Recoverable Fatal in zend_make_printable_zval 267 | v_copy = malloc(sizeof(char*) * (strlen(Z_OBJCE_P(expr)->name) + 2)); 268 | sprintf(v_copy, "(%s)", Z_OBJCE_P(expr)->name); 269 | v = strdup(v_copy); 270 | free(v_copy); 271 | } else { 272 | // Uses zend_make_printable_zval 273 | zend_make_printable_zval(expr, &expr_copy, &use_copy); 274 | if(use_copy) { 275 | v = strdup((char*)(expr_copy).value.str.val); 276 | zval_dtor(&expr_copy); 277 | } else { 278 | v = strdup((char*)(*expr).value.str.val); 279 | } 280 | } 281 | n->caption = forp_str_replace( 282 | c, v, 283 | n->caption TSRMLS_CC 284 | ); 285 | } 286 | #endif 287 | } 288 | } else { 289 | // Root node 290 | edata = EG(current_execute_data); 291 | n->function.class = NULL; 292 | n->function.function = "{main}"; 293 | 294 | if( 295 | edata 296 | && edata->op_array 297 | && edata->op_array->filename 298 | ) { 299 | n->function.filename = strdup(edata->op_array->filename); 300 | n->filename = strdup(n->function.filename); 301 | } else { 302 | n->function.filename = (char*) zend_get_executed_filename(TSRMLS_C); 303 | n->filename = n->function.filename; 304 | } 305 | } 306 | 307 | FORP_G(current_node) = n; 308 | key = FORP_G(stack_len); 309 | n->key = key; 310 | 311 | // realloc stack * FORP_STACK_REALLOC 312 | if(FORP_G(stack_len)%FORP_STACK_REALLOC == 0) { 313 | FORP_G(stack) = realloc( 314 | FORP_G(stack), 315 | ((FORP_G(stack_len)/FORP_STACK_REALLOC)+1) * FORP_STACK_REALLOC * sizeof (forp_node_t) 316 | ); 317 | } 318 | 319 | FORP_G(stack_len)++; 320 | FORP_G(stack)[key] = n; 321 | 322 | if(FORP_G(flags) & FORP_FLAG_MEMORY) { 323 | n->mem_begin = zend_memory_usage(0 TSRMLS_CC); 324 | } 325 | 326 | if(FORP_G(flags) & FORP_FLAG_TIME) { 327 | gettimeofday(&tv, NULL); 328 | n->time_begin = tv.tv_sec * 1000000.0 + tv.tv_usec; 329 | n->profiler_duration = n->time_begin - start_time; 330 | } 331 | 332 | return n; 333 | } 334 | /* }}} */ 335 | 336 | /* {{{ forp_close_node 337 | */ 338 | void forp_close_node(forp_node_t *n TSRMLS_DC) { 339 | 340 | struct timeval tv; 341 | 342 | // closed state 343 | n->state = 2; 344 | 345 | // dump duration and memory before next steps 346 | if(FORP_G(flags) & FORP_FLAG_TIME) { 347 | gettimeofday(&tv, NULL); 348 | n->time_end = tv.tv_sec * 1000000.0 + tv.tv_usec; 349 | n->time = n->time_end - n->time_begin; 350 | } 351 | 352 | if(FORP_G(flags) & FORP_FLAG_MEMORY) { 353 | n->mem_end = zend_memory_usage(0 TSRMLS_CC); 354 | n->mem = n->mem_end - n->mem_begin; 355 | } 356 | 357 | if(n->function.highlight) { 358 | php_printf(FORP_HIGHLIGHT_END, n->caption != NULL ? n->caption : "", (n->time / 1000), (n->mem / 1024), n->level); 359 | } 360 | 361 | FORP_G(current_node) = n->parent; 362 | FORP_G(nesting_level)--; 363 | 364 | // self duration on exit 365 | if(FORP_G(flags) & FORP_FLAG_TIME) { 366 | gettimeofday(&tv, NULL); 367 | n->profiler_duration += (tv.tv_sec * 1000000.0 + tv.tv_usec) - n->time_end ; 368 | } 369 | } 370 | /* }}} */ 371 | 372 | /* {{{ forp_start 373 | */ 374 | void forp_start(TSRMLS_D) { 375 | if(FORP_G(started)) { 376 | php_error_docref( 377 | NULL TSRMLS_CC, 378 | E_NOTICE, 379 | "forp is already started." 380 | ); 381 | } else { 382 | FORP_G(started) = 1; 383 | 384 | #if HAVE_SYS_RESOURCE_H 385 | if(FORP_G(flags) & FORP_FLAG_CPU) { 386 | struct rusage ru; 387 | getrusage(RUSAGE_SELF, &ru); 388 | FORP_G(utime) = ru.ru_utime.tv_sec * 1000000.0 + ru.ru_utime.tv_usec; 389 | FORP_G(stime) = ru.ru_stime.tv_sec * 1000000.0 + ru.ru_stime.tv_usec; 390 | } 391 | #endif 392 | 393 | // Proxying zend api methods 394 | #if PHP_VERSION_ID < 50500 395 | old_execute = zend_execute; 396 | zend_execute = forp_execute; 397 | #else 398 | old_execute_ex = zend_execute_ex; 399 | zend_execute_ex = forp_execute_ex; 400 | #endif 401 | if (!FORP_G(no_internals)) { 402 | old_execute_internal = zend_execute_internal; 403 | zend_execute_internal = forp_execute_internal; 404 | } 405 | FORP_G(main) = forp_open_node(NULL, NULL TSRMLS_CC); 406 | } 407 | } 408 | /* }}} */ 409 | 410 | /* {{{ forp_end 411 | */ 412 | void forp_end(TSRMLS_D) { 413 | 414 | if(FORP_G(started)) { 415 | 416 | #if HAVE_SYS_RESOURCE_H 417 | if(FORP_G(flags) & FORP_FLAG_CPU) { 418 | struct rusage ru; 419 | getrusage(RUSAGE_SELF, &ru); 420 | FORP_G(utime) = (ru.ru_utime.tv_sec * 1000000.0 + ru.ru_utime.tv_usec) - FORP_G(utime); 421 | FORP_G(stime) = (ru.ru_stime.tv_sec * 1000000.0 + ru.ru_stime.tv_usec) - FORP_G(stime); 422 | } 423 | #endif 424 | 425 | // Closing ancestors 426 | while(FORP_G(current_node)) { 427 | forp_close_node(FORP_G(current_node) TSRMLS_CC); 428 | } 429 | 430 | // Restores Zend API methods 431 | #if PHP_VERSION_ID < 50500 432 | if (old_execute) { 433 | zend_execute = old_execute; 434 | old_execute = 0; 435 | } 436 | #else 437 | if (old_execute_ex) { 438 | zend_execute_ex = old_execute_ex; 439 | old_execute_ex = 0; 440 | } 441 | #endif 442 | if (!FORP_G(no_internals)) { 443 | zend_execute_internal = old_execute_internal; 444 | } 445 | // Stop 446 | FORP_G(started) = 0; 447 | } 448 | } 449 | /* }}} */ 450 | 451 | /* {{{ forp_info 452 | */ 453 | void forp_info(TSRMLS_D) { 454 | php_info_print_table_start(); 455 | php_info_print_table_row(2, "Version", FORP_VERSION); 456 | php_info_print_table_end(); 457 | } 458 | /* }}} */ 459 | 460 | /* {{{ forp_execute 461 | */ 462 | #if PHP_VERSION_ID < 50500 463 | void forp_execute(zend_op_array *op_array TSRMLS_DC) { 464 | #else 465 | void forp_execute_ex(zend_execute_data *execute_data TSRMLS_DC) { 466 | #endif 467 | forp_node_t *n; 468 | 469 | if (FORP_G(nesting_level) > FORP_G(max_nesting_level)) { 470 | #if PHP_VERSION_ID < 50500 471 | old_execute(op_array TSRMLS_CC); 472 | #else 473 | old_execute_ex(execute_data TSRMLS_CC); 474 | #endif 475 | } else { 476 | #if PHP_VERSION_ID < 50500 477 | n = forp_open_node(EG(current_execute_data), op_array TSRMLS_CC); 478 | old_execute(op_array TSRMLS_CC); 479 | #else 480 | n = forp_open_node(EG(current_execute_data)->prev_execute_data, execute_data->op_array TSRMLS_CC); 481 | old_execute_ex(execute_data TSRMLS_CC); 482 | #endif 483 | 484 | if(n && n->state < 2) forp_close_node(n TSRMLS_CC); 485 | } 486 | } 487 | /* }}} */ 488 | 489 | /* {{{ forp_execute_internal 490 | */ 491 | #if PHP_VERSION_ID < 50500 492 | void forp_execute_internal(zend_execute_data *current_execute_data, int ret TSRMLS_DC) 493 | #else 494 | void forp_execute_internal(zend_execute_data *current_execute_data, struct _zend_fcall_info *fci, int ret TSRMLS_DC) 495 | #endif 496 | { 497 | forp_node_t *n; 498 | 499 | if (FORP_G(nesting_level) > FORP_G(max_nesting_level)) { 500 | #if PHP_VERSION_ID < 50500 501 | execute_internal(current_execute_data, ret TSRMLS_CC); 502 | #else 503 | execute_internal(current_execute_data, fci, ret TSRMLS_CC); 504 | #endif 505 | } else { 506 | n = forp_open_node(EG(current_execute_data), NULL TSRMLS_CC); 507 | if (old_execute_internal) { 508 | #if PHP_VERSION_ID < 50500 509 | old_execute_internal(current_execute_data, ret TSRMLS_CC); 510 | #else 511 | old_execute_internal(current_execute_data, fci, ret TSRMLS_CC); 512 | #endif 513 | } else { 514 | #if PHP_VERSION_ID < 50500 515 | execute_internal(current_execute_data, ret TSRMLS_CC); 516 | #else 517 | execute_internal(current_execute_data, fci, ret TSRMLS_CC); 518 | #endif 519 | } 520 | if(n && n->state < 2) forp_close_node(n TSRMLS_CC); 521 | } 522 | } 523 | /* }}} */ 524 | 525 | /* {{{ forp_stack_dump_var 526 | */ 527 | zval *forp_stack_dump_var(forp_var_t *var TSRMLS_DC) { 528 | 529 | int i; 530 | zval *zvar, *zarr, *entry; 531 | 532 | MAKE_STD_ZVAL(zvar); 533 | array_init(zvar); 534 | 535 | if(var->level) add_assoc_string(zvar, "level", var->level, 1); 536 | if(var->type) add_assoc_string(zvar, "type", var->type, 1); 537 | if(var->is_ref) { 538 | add_assoc_long(zvar, "is_ref", var->is_ref); 539 | if(var->refcount > 1) add_assoc_long(zvar, "refcount", var->refcount); 540 | } 541 | if(var->stack_idx > -1) add_assoc_long(zvar, "stack_idx", var->stack_idx); 542 | if(var->class) add_assoc_string(zvar, "class", var->class, 1); 543 | if(var->arr_len) { 544 | add_assoc_long(zvar, "size", var->arr_len); 545 | 546 | MAKE_STD_ZVAL(zarr); 547 | array_init(zarr); 548 | 549 | i = 0; 550 | while(i < var->arr_len) { 551 | entry = forp_stack_dump_var(var->arr[i] TSRMLS_CC); 552 | add_assoc_zval(zarr, var->arr[i]->key, entry); 553 | i++; 554 | } 555 | if(strcmp(var->type, "object") == 0) add_assoc_zval(zvar, "properties", zarr); 556 | else add_assoc_zval(zvar, "value", zarr); 557 | 558 | } else { 559 | if(var->value) add_assoc_string(zvar, "value", var->value, 1); 560 | } 561 | 562 | return zvar; 563 | } 564 | /* }}} */ 565 | 566 | /* {{{ forp_stack_dump 567 | */ 568 | void forp_stack_dump(TSRMLS_D) { 569 | int i; 570 | int j = 0; 571 | zval *entry, *stack, *groups, *time, *profiler_duration, 572 | *var, *inspect; 573 | forp_node_t *n; 574 | 575 | MAKE_STD_ZVAL(FORP_G(dump)); 576 | array_init(FORP_G(dump)); 577 | 578 | if(FORP_G(flags) & FORP_FLAG_CPU) { 579 | add_assoc_double(FORP_G(dump), "utime", FORP_G(utime)); 580 | add_assoc_double(FORP_G(dump), "stime", FORP_G(stime)); 581 | } 582 | 583 | if(FORP_G(stack_len)) { 584 | 585 | MAKE_STD_ZVAL(stack); 586 | array_init(stack); 587 | add_assoc_zval(FORP_G(dump), "stack", stack); 588 | 589 | for (i = 0; i < FORP_G(stack_len); ++i) { 590 | n = FORP_G(stack)[i]; 591 | 592 | //if(forp_is_profiling_function(n TSRMLS_CC)) continue; 593 | 594 | // stack entry 595 | MAKE_STD_ZVAL(entry); 596 | array_init(entry); 597 | 598 | if (n->filename) 599 | add_assoc_string(entry, FORP_DUMP_ASSOC_FILE, n->filename, 1); 600 | 601 | if (n->function.class) 602 | add_assoc_string(entry, FORP_DUMP_ASSOC_CLASS, n->function.class, 1); 603 | 604 | if(n->alias) { 605 | add_assoc_string(entry, FORP_DUMP_ASSOC_FUNCTION, n->alias, 1); 606 | } else if (n->function.function) { 607 | add_assoc_string(entry, FORP_DUMP_ASSOC_FUNCTION, n->function.function, 1); 608 | } 609 | 610 | if (n->lineno) 611 | add_assoc_long(entry, FORP_DUMP_ASSOC_LINENO, n->lineno); 612 | 613 | if (n->function.groups && n->function.groups_len > 0) { 614 | MAKE_STD_ZVAL(groups); 615 | array_init(groups); 616 | j = 0; 617 | while(j < n->function.groups_len) { 618 | add_next_index_string(groups, n->function.groups[j], 1); 619 | j++; 620 | } 621 | add_assoc_zval(entry, FORP_DUMP_ASSOC_GROUPS, groups); 622 | } 623 | 624 | if (n->caption) { 625 | add_assoc_string(entry, FORP_DUMP_ASSOC_CAPTION, n->caption, 1); 626 | } 627 | 628 | if(FORP_G(flags) & FORP_FLAG_TIME) { 629 | 630 | MAKE_STD_ZVAL(time); 631 | ZVAL_DOUBLE(time, round(n->time * 1000000.0) / 1000000.0); 632 | add_assoc_zval(entry, FORP_DUMP_ASSOC_DURATION, time); 633 | 634 | MAKE_STD_ZVAL(profiler_duration); 635 | ZVAL_DOUBLE(profiler_duration, round(n->profiler_duration * 1000000.0) / 1000000.0); 636 | add_assoc_zval(entry, FORP_DUMP_ASSOC_PROFILERTIME, profiler_duration); 637 | } 638 | 639 | if(FORP_G(flags) & FORP_FLAG_MEMORY) { 640 | add_assoc_long(entry, FORP_DUMP_ASSOC_MEMORY, n->mem); 641 | } 642 | 643 | add_assoc_long(entry, FORP_DUMP_ASSOC_LEVEL, n->level); 644 | 645 | // {main} don't have parent 646 | if (n->parent) 647 | add_assoc_long(entry, FORP_DUMP_ASSOC_PARENT, n->parent->key); 648 | 649 | zend_hash_next_index_insert(Z_ARRVAL_P(stack), (void *) &entry, sizeof (zval *), NULL); 650 | } 651 | } 652 | 653 | if(FORP_G(inspect_len)) { 654 | 655 | MAKE_STD_ZVAL(inspect); 656 | array_init(inspect); 657 | add_assoc_zval(FORP_G(dump), "inspect", inspect); 658 | 659 | for (i = 0; i < FORP_G(inspect_len); ++i) { 660 | var = forp_stack_dump_var(FORP_G(inspect)[i] TSRMLS_CC); 661 | add_assoc_zval(inspect, FORP_G(inspect)[i]->name, var); 662 | } 663 | } 664 | } 665 | /* }}} */ 666 | 667 | /* {{{ forp_stack_dump_cli_node 668 | */ 669 | void forp_stack_dump_cli_node(forp_node_t *n TSRMLS_DC) { 670 | int i; 671 | 672 | if(FORP_G(flags) & FORP_FLAG_TIME) { 673 | php_printf("[time:\x1B[36m%09.0f\x1B[37m] ", n->time); 674 | } 675 | if(FORP_G(flags) & FORP_FLAG_MEMORY) { 676 | php_printf("[mem:\x1B[36m%09d\x1B[37m] ", (int)n->mem); 677 | } 678 | for (i = 0; i < n->level; ++i) { 679 | if (i == n->level - 1) php_printf("\x1B[37m |--- \x1B[37m"); 680 | else php_printf("\x1B[37m | \x1B[37m"); 681 | } 682 | if (n->function.class) php_printf("\x1B[1m%s\x1B[0m\x1B[37m::", n->function.class); 683 | php_printf("%s", n->function.function); 684 | if(n->filename) { 685 | php_printf(" (%s", n->filename); 686 | if(n->lineno) php_printf(":%d", n->lineno); 687 | php_printf(")"); 688 | } 689 | php_printf("%s%s","\x1B[37m", PHP_EOL); 690 | } 691 | /* }}} */ 692 | 693 | /* {{{ forp_stack_dump_cli_var 694 | */ 695 | void forp_stack_dump_cli_var(forp_var_t *var, int depth TSRMLS_DC) { 696 | int i, indent; 697 | 698 | indent = depth*4; 699 | 700 | if(var->name) php_printf("%*s%s:\n", indent, "", var->name); 701 | if(var->key) php_printf("%*s%s:\n", indent, "", var->key); 702 | 703 | php_printf("%*stype: %s\n", indent+2, "", var->type); 704 | 705 | if(var->value) php_printf("%*svalue: %s\n", indent+2, "", var->value); 706 | 707 | if(var->class) php_printf("%*sclass: %s\n", indent+2, "", var->class); 708 | 709 | if(var->arr_len) { 710 | php_printf("%*ssize: %d\n", indent+2, "", var->arr_len); 711 | 712 | if(var->class) php_printf("%*sproperties:\n", indent+2, ""); 713 | else php_printf("%*svalues:\n", indent+2, ""); 714 | 715 | for (i = 0; i < var->arr_len; ++i) { 716 | forp_stack_dump_cli_var(var->arr[i], depth+1 TSRMLS_CC); 717 | } 718 | } 719 | } 720 | /* }}} */ 721 | 722 | /* {{{ forp_stack_dump_cli 723 | */ 724 | void forp_stack_dump_cli(TSRMLS_D) { 725 | int i; 726 | if(FORP_G(inspect_len) > 0) { 727 | php_printf("\n\x1B[37m-\x1B[36mdebug\x1B[37m--------------------------------------------------------------------------%s", PHP_EOL); 728 | for (i = 0; i < FORP_G(inspect_len); ++i) { 729 | forp_stack_dump_cli_var(FORP_G(inspect)[i], 0 TSRMLS_CC); 730 | } 731 | } 732 | php_printf("\n\x1B[37m-\x1B[36mprofile\x1B[37m------------------------------------------------------------------------%s", PHP_EOL); 733 | for (i = 0; i < FORP_G(stack_len); ++i) { 734 | //if(forp_is_profiling_function(FORP_G(stack)[i] TSRMLS_CC)) continue; 735 | forp_stack_dump_cli_node(FORP_G(stack)[i] TSRMLS_CC); 736 | } 737 | php_printf("--------------------------------------------------------------------------------%s", PHP_EOL); 738 | php_printf("forp %s%s", FORP_VERSION, PHP_EOL); 739 | php_printf("--------------------------------------------------------------------------------\x1B[0m%s", PHP_EOL); 740 | } 741 | /* }}} */ 742 | 743 | /* {{{ forp_is_profiling_function 744 | */ 745 | int forp_is_profiling_function(forp_node_t *n TSRMLS_DC) { 746 | if(!n->function.function) return 0; 747 | return ( 748 | strstr(n->function.function, "forp_inspect") 749 | || strstr(n->function.function, "forp_start") 750 | || strstr(n->function.function, "forp_end") 751 | || strstr(n->function.function, "forp_dump") 752 | || strstr(n->function.function, "forp_print") 753 | || strstr(n->function.function, "forp_json") 754 | || strstr(n->function.function, "forp_json_google_tracer") 755 | ); 756 | } 757 | /* }}} */ 758 | --------------------------------------------------------------------------------