pax_global_header00006660000000000000000000000064130113177010014504gustar00rootroot0000000000000052 comment=24206ff6c5158989ca8134ca02db613994d7badf php-profiler-extension-4.0.7/000077500000000000000000000000001301131770100161355ustar00rootroot00000000000000php-profiler-extension-4.0.7/.gitignore000066400000000000000000000013121301131770100201220ustar00rootroot00000000000000/.deps /Makefile /*.lo /*.loT /*.slo /*.mk /*.la /.libs /libs.mk /ac*.m4 /build /config.h /config.h.in /config.nice /config.sub /configure /configure.in /config.status /config.cache /conftest /conftest.c /core /dynlib.m4 /install-sh /ltmain.sh /include /Makefile.fragments /Makefile.global /Makefile.objects /missing /memory: /mkinstalldirs /modules /scan_makefile_in.awk /config.guess /*swo /*swp /*swn tags /tmp-php.ini /config.log /libtool /*.plg /*.patch /*.tgz /*.ncb /*.opt /*.dsw /autom4te.cache .svn /*~ tests/*.diff tests/*.out tests/*.php tests/*.sh tests/*.log tests/*.exp run-tests.php vendor/ *.phar php5/*.lo php7/*.lo php5/.libs php7/.libs /.gdb_history src/Tideways/*.lo src/Tideways/.libs .vscode php-profiler-extension-4.0.7/.travis.yml000066400000000000000000000005421301131770100202470ustar00rootroot00000000000000language: php notifications: email: false sudo: false php: - 5.3 - 5.4 - 5.5 - 5.6 - 7.0 - 7.1 env: global: - NO_INTERACTION=1 before_script: - phpize - ./configure - make script: REPORT_EXIT_STATUS=1 php run-tests.php -p `which php` --show-diff -d extension=`pwd`/.libs/tideways.so -q after_failure: "cat tests/*.diff" php-profiler-extension-4.0.7/CHANGELOG.md000066400000000000000000000210201301131770100177410ustar00rootroot00000000000000# Version 4.0.5 - Added Elasticsearch PHP Support - Added curl_multi support when `curl_multi_add_handle` and `curl_multi_remove_handle` are called. - Added Laravel Eloquent ORM support - Added CakePHP 2 & 3 Support - Added New MongoDB Extension support - Added PDO, ext/mysql and ext/mysqli Connect Host+Database Name support - Added Yii 1 & 2 Support - Added Phalcon Support (for spans, no transaction detection) # Version 4.0.4 - Add support for Fatal Error detection in PHP 7 - Add log-level INI settings - Fix compilation with clang on OSX - Fix missing header include for `zend_exceptions.h` (Ondřej Surý) - Fix memory leaks on PHP 7 # Version 4.0.3 - Add PHP 7 support # Version 3.0.0 - Remove SQL summarization, always keep full SQL and delegate summary generation to the daemon. - Change `tideways_minify_sql()` to always return empty string, because summary function was removed. To pass full sql and let the daemon summarize it, create a span of category ``sql`` and pass an annotation with key ``sql``. - Add Pheanstalk v2 and v3 support - Add PhpAmqpLib support - Add MongoDB support (Queries on MongoCollection, MongoCursor, MongoCommandCursor) - Add Predis support # Version 2.0.10 - Fix bug in CentOS compilation, where -lrt flag is required to use ``clock_gettime`` function. Research suggests this is required on some other Linux distributions as well. # Version 2.0.9 - Remove slow_php_treshold functionality that recorded arbitrary php spans as long as they were 50ms and slower. This could lead to unpredictable results which are hard to render in the UI. Also fixes a potential segfault when triggered inside a generator, where parts of execute_data are already NULL. # Version 2.0.8 - Always measure CPU time of the main() span. - Cleanup CPU timer code to use `CLOCK_PROCESS_CPUTIME_ID`. - Increase default sample-rate configuration to 30% - Multiple calls to tideways_enable() restart profiling instead of continuing - Fix Doctrine span watchers for ORM 2.5 # Version 2.0.7 - Fix Apple Build # Version 2.0.5 - Introduce `tideways_span_callback()` function that takes a function and a callback to start a span. # Version 2.0.4 - Fix segfault in `tideways_span_watch()` # Versoin 2.0.3 - Replace TSC based profiling with clock_gettime(CLOCK_GETMONOTONIC) to avoid binding process to CPUs. - Add more special support for Symfony EventDispatcher - Add Laravel support # Version 2.0 - Add new collection mechanism using spans (See Google Dapper paper). There is a large list of extensions, libraries and frameworks supported automatically and an API to create spans yourself. - Added Symfony2 Support for Spans - Added Oxid Support for Spans - Added Shopware Support for Spans - Added Magento Support for Spans - Added Zend Framework 1 Support for Spans - Added span support for mysql, mysqli, PDO and pg database extensions - Added Doctrine2 ORM Support for Spans - Added Smarty2, Smarty2 and Twig Template Support for spans - Added HTTP support for spans hooking into file_get_contents, curl_exec and SoapClient # Version 1.7.1 - Reintroduce `tideways_last_fatal_error` as alias of `error_get_last()` for backwards compatibility reasons because Profiler PHP library is written in a way where extension update could break application. # Version 1.7.0 - Remove `tideways_last_fatal_error` and `tideways_last_exception`. - Add new method `tideways_fatal_backtrace()` that returns the data from `debug_backtrace()` as an array instead of string as before. This is easier to maintain, but requires userland code to convert to string. - Add `"exception_function"` option that allows setting a function which gets passed exceptions at the framework level. You have access to this exception calling `tideways_last_detected_exception()`. # Version 1.6.2 - Fix wrong definition of TIDEWAYS_FLAGS_NO_COMPILE flag. # Version 1.6.1 - Fix segfault in `auto_prepend_library` cleanup handling. - Add new constant `TIDEWAYS_FLAGS_NO_COMPILE` that skips profiling of require/include and eval statements. # Version 1.6.0 - Move away from requireing file in module RINIT to hooking into auto_prepend_file. We changed the INI setting `tideways.load_library` to be `tideways.auto_prepend_library` instead and defaults to 1. It will check for `Tideways.php` next to tideways.so and load that library by adding it to the `auto_prepend_file` PHP.INI option. If that is already set a new function `tideways_prepend_overwritten()` allows to check if we need to require the old ini_get("auto_prepend_file"). # Version 1.5.0 - Change default socket option to `/var/run/tideways/tidewaysd.sock` - Rename INI option `tideways.transaction_function` to `tideways.framework` - Rename from qafooprofiler to tideways # Version 1.3.2 - Add protection against segfault in combination with XDebug < 2.2.7 See https://github.com/xdebug/xdebug/pull/131 # Version 1.3.1 - Improve auto loading/start functionality by using better abstractions from Zend Engine. Replaced copied code from `spl_autoload` with call to `zend_execute_scripts`. - Bugfix in sql argument summary when FROM keyword was found, but it was not a SELECT/INSERT/UPDATE/DELETE statement. # Version 1.3.0 - Add support for profiling Event-based frameworks/applications Optionally collect the name of the event triggered as part of the function call for the following libraries: - Doctrine 2 - Zend Framework 2 - Symfony 2 - Drupal - Wordpress - Magento - Enlight/Shopware - Add optional support to auto start profiling and transmitting to [Qafoo Profiler platform](https://qafoolabs.com) by copying `QafooProfiler.php` next to the `qafooprofiler.so`. # Version 1.2.2 - Fix bug in `eval()` support # Version 1.2.0 - Fix bug in Smarty support - Fix bug in overwrite mechanism of `zend_execute` for transacation name detection. - Improve performance for transaction name detection when no layer data is requested. # Version 1.0 - Rename extension to `qafooprofiler` - Add support for transaction name detection when not fully profiling (layer-mode) # Version 0.9.11 - Fix segfault in Twig_Template#getTemplateName instrumentation on PHP versions < 5.5 - Fix segfault in memory handling of fatal error callback when catching an exception. - Fix missing TSRMLS_CC/DC flags and a missing TSRMLS_FETCH() to allow compilation on threaded systems such as Travis running with --enable-zts-maintainer flag. - Enabled Travis CI # Version 0.9.10 - Fix segfault in Twig_Template#getTemplateName instrumentation. - Integrate curl dependency into repository cleanly to avoid problems with having to copy the `php_curl.h` header around. # Version 0.9.9 - Apply patch by tstarling@php.net to fix frequency collection on linux: https://bugs.php.net/bug.php?id=64165 - Apply patch by github@fabian-franz.de to fix Mac timing: https://bugs.php.net/bug.php?id=61132 # Version 0.9.8 - Improve performance on modern CPUs by checking for invariant tsc, a feature that guarantees the same timer values and speed in the `cpuinfo` register. This allows xhprof to avoid the costly bind to a single CPU, which causes performance problems on servers with very high load. Binding to cpus disallows migrating the threads to another cpu and prevents the Kernel to adjust different loads of cpus. # Version 0.9.7 - Remove `xhprof_sample_disable()`, use `xhprof_disable()` instead when in sampling mode. - Add `xhprof_layers_enable() ` that accepts an array of key value pairs in the constructor containing function names to layers. Will automatically set the `XHPROF_FLAGS_NOUSERLAND` mode and use the passed functions as a `functions` whitelist. The result is a profiling report only based on grouping certain function calls into layers. If you want to profile the request as well, add `"main()" => "main()"` as an entry. - Add new constant `XHPROF_FLAGS_NOUSERLAND` when set will not override the zend_execute hook for userland functions. - Add new function `xhprof_last_fatal_error()` that returns information on PHP fatal error with trace and more information than PHP core has usually. Overrides zend_error_cb such that it will not work when xdebug is also enabled (depends on the order). # Version 0.9.6 - Add `argument_functions` feature that allows logging the arguments of a function. Includes special handling for a lot of interesting functions that will want this feature, for example `PDO::execute`. # Version 0.9.5 - Fix segfault with PHP 5.5.9 and up - Add new option `functions` to `xhprof_enable()` that allows whitelist profiling of functions. php-profiler-extension-4.0.7/LICENSE000066400000000000000000000236761301131770100171600ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS php-profiler-extension-4.0.7/NOTICE000066400000000000000000000010151301131770100170360ustar00rootroot00000000000000Tideways PHP Profiler Copyright (c) 2014-2016 Tideways GmbH This product includes work from the following third-party libraries: - xhprof (https://github.com/phacility/xhprof) Copyright (c) 2009 Facebook Creators: Changhao Jiang Kannan Muthukkaruppan Venkat Venkataramani Haiping Zhao Additional Contributors: George Cabrera - UI enhancements Paul Saab - FreeBSD port Syseleven - Function Arguments php-profiler-extension-4.0.7/README.md000066400000000000000000000036561301131770100174260ustar00rootroot00000000000000# Tideways PHP Profiler Extension [![Build Status](https://travis-ci.org/tideways/php-profiler-extension.svg?branch=master)](https://travis-ci.org/tideways/php-profiler-extension) The Profiler extension contains functions for finding performance bottlenecks in PHP code. The extension is one core piece of functionality for the [Tideways Profiler Platform](https://tideways.io). It solves the problem of efficiently collecting, aggregating and analyzing the profiling data when running a Profiler in production. ## Requirements - PHP 5.3, 5.4, 5.5, 5.6 or 7.0 - cURL and PCRE Dev Headers (`apt-get install libcurl4-openssl-dev libpcre3-dev`) - Tested with Linux i386, amd64 architectures ## Installation You can install the Tideways extension from source or download pre-compiled binaries from the [Tideways Downloads](https://tideways.io/profiler/downloads) page. Building from source is straightforward: git clone https://github.com/tideways/php-profiler-extension.git cd php-profiler-extension phpize ./configure make sudo make install You also need the latest ``Tideways.php`` if you want to use the Profiler in combination with our daemon and UI. [Download the file from Github](https://github.com/tideways/profiler/releases). Put this file into your extension directory. You can find the location by calling: $ php -r 'echo ini_get("extension_dir");' $ cp Tideways.php /path/to/php/lib Afterwards you need to enable the extension in your php.ini: extension=tideways.so tideways.api_key=set your key ## Usage without tideways.io UI **Important:** If you don't want to use Tideways platform, just as a XHPROF alterative, you should also add the following ini configuration to your php.ini: extension=tideways.so tideways.auto_prepend_library=0 ## Documentation You can find the documentation on the [Tidways Profiler website](https://tideways.io/profiler/docs/setup/profiler-php-pecl-extension). php-profiler-extension-4.0.7/build.xml000066400000000000000000000013541301131770100177610ustar00rootroot00000000000000 php-profiler-extension-4.0.7/config.m4000066400000000000000000000046131301131770100176500ustar00rootroot00000000000000 PHP_ARG_ENABLE(tideways, whether to enable Tideways support, [ --enable-tideways Enable Tideways support]) AC_DEFUN([AC_TIDEWAYS_CLOCK], [ have_clock_gettime=no AC_MSG_CHECKING([for clock_gettime]) AC_TRY_LINK([ #include ], [struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);], [ have_clock_gettime=yes AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) if test "$have_clock_gettime" = "no"; then AC_MSG_CHECKING([for clock_gettime in -lrt]) SAVED_LIBS="$LIBS" LIBS="$LIBS -lrt" AC_TRY_LINK([ #include ], [struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);], [ have_clock_gettime=yes TIDEWAYS_SHARED_LIBADD="$TIDEWAYS_SHARED_LIBADD -lrt" AC_MSG_RESULT([yes]) ], [ LIBS="$SAVED_LIBS" AC_MSG_RESULT([no]) ]) fi if test "$have_clock_gettime" = "no"; then AC_MSG_CHECKING([for clock_get_time]) AC_TRY_RUN([ #include #include #include int main() { kern_return_t ret; clock_serv_t aClock; mach_timespec_t aTime; ret = host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &aClock); if (ret != KERN_SUCCESS) { return 1; } ret = clock_get_time(aClock, &aTime); if (ret != KERN_SUCCESS) { return 2; } return 0; } ], [ have_clock_gettime=yes AC_DEFINE([HAVE_CLOCK_GET_TIME], 1, [do we have clock_get_time?]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi if test "$have_clock_gettime" = "yes"; then AC_DEFINE([HAVE_CLOCK_GETTIME], 1, [do we have clock_gettime?]) fi if test "$have_clock_gettime" = "no"; then AC_MSG_ERROR([clock_gettime is missing, but required]) fi ]) if test "$PHP_TIDEWAYS" != "no"; then AC_TIDEWAYS_CLOCK AC_MSG_CHECKING(PHP version) export OLD_CPPFLAGS="$CPPFLAGS" export CPPFLAGS="$CPPFLAGS $INCLUDES" AC_TRY_COMPILE([#include ], [ #if PHP_MAJOR_VERSION > 5 #error PHP > 5 #endif ], [ subdir=php5 AC_MSG_RESULT([PHP 5.x]) ], [ subdir=php7 AC_MSG_RESULT([PHP 7.x]) ]) export CPPFLAGS="$OLD_CPPFLAGS" TIDEWAYS_SOURCES="$subdir/spans.c" TIDEWAYS_SOURCES="$TIDEWAYS_SOURCES tideways.c" PHP_SUBST([LIBS]) PHP_SUBST([TIDEWAYS_SHARED_LIBADD]) PHP_NEW_EXTENSION(tideways, $TIDEWAYS_SOURCES, $ext_shared) fi php-profiler-extension-4.0.7/php5/000077500000000000000000000000001301131770100170115ustar00rootroot00000000000000php-profiler-extension-4.0.7/php5/spans.c000066400000000000000000000102731301131770100203040ustar00rootroot00000000000000#include "php.h" #include "../php_tideways.h" #include "../spans.h" extern ZEND_DECLARE_MODULE_GLOBALS(hp) long tw_span_create(char *category, size_t category_len TSRMLS_DC) { zval *span, *starts, *stops; int idx; long parent = 0; if (TWG(spans) == NULL) { return -1; } idx = zend_hash_num_elements(Z_ARRVAL_P(TWG(spans))); // Hardcode a limit of 1500 spans for now, Daemon will re-filter again to 1000. // We assume web-requests and non-spammy worker/crons here, need a way to support // very long running scripts at some point. if (idx >= TWG(max_spans)) { long *idx_ptr = NULL; if (zend_hash_find(TWG(span_cache), category, category_len+1, (void **)&idx_ptr) == SUCCESS) { idx = *idx_ptr; if (idx > 1) { tw_span_annotate_long(idx, "trunc", 1 TSRMLS_CC); return idx; } } } MAKE_STD_ZVAL(span); MAKE_STD_ZVAL(starts); MAKE_STD_ZVAL(stops); array_init(span); array_init(starts); array_init(stops); add_assoc_stringl(span, "n", category, category_len, 1); add_assoc_zval(span, "b", starts); add_assoc_zval(span, "e", stops); if (parent > 0) { add_assoc_long(span, "p", parent); } zend_hash_index_update(Z_ARRVAL_P(TWG(spans)), idx, &span, sizeof(zval*), NULL); if (idx >= TWG(max_spans)) { zend_hash_update(TWG(span_cache), category, category_len+1, &idx, sizeof(long), NULL); } return idx; } static int tw_convert_to_string(void *pDest TSRMLS_DC) { zval **zv = (zval **) pDest; convert_to_string_ex(zv); return ZEND_HASH_APPLY_KEEP; } void tw_span_annotate(long spanId, zval *annotations TSRMLS_DC) { zval **span, **span_annotations, *span_annotations_ptr; if (spanId == -1) { return; } if (zend_hash_index_find(Z_ARRVAL_P(TWG(spans)), spanId, (void **) &span) == FAILURE) { return; } if (zend_hash_find(Z_ARRVAL_PP(span), "a", sizeof("a"), (void **) &span_annotations) == FAILURE) { MAKE_STD_ZVAL(span_annotations_ptr); array_init(span_annotations_ptr); span_annotations = &span_annotations_ptr; add_assoc_zval(*span, "a", span_annotations_ptr); } zend_hash_apply(Z_ARRVAL_P(annotations), tw_convert_to_string TSRMLS_CC); zend_hash_merge(Z_ARRVAL_PP(span_annotations), Z_ARRVAL_P(annotations), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), 1); } void tw_span_annotate_long(long spanId, char *key, long value TSRMLS_DC) { zval **span, **span_annotations, *annotation_value, *span_annotations_ptr; if (zend_hash_index_find(Z_ARRVAL_P(TWG(spans)), spanId, (void **) &span) == FAILURE) { return; } if (spanId == -1) { return; } if (zend_hash_find(Z_ARRVAL_PP(span), "a", sizeof("a"), (void **) &span_annotations) == FAILURE) { MAKE_STD_ZVAL(span_annotations_ptr); array_init(span_annotations_ptr); span_annotations = &span_annotations_ptr; add_assoc_zval(*span, "a", span_annotations_ptr); } MAKE_STD_ZVAL(annotation_value); ZVAL_LONG(annotation_value, value); convert_to_string_ex(&annotation_value); add_assoc_zval_ex(*span_annotations, key, strlen(key)+1, annotation_value); } void tw_span_annotate_string(long spanId, char *key, char *value, int copy TSRMLS_DC) { zval **span, **span_annotations, *span_annotations_ptr; int len; if (spanId == -1) { return; } if (zend_hash_index_find(Z_ARRVAL_P(TWG(spans)), spanId, (void **) &span) == FAILURE) { return; } if (zend_hash_find(Z_ARRVAL_PP(span), "a", sizeof("a"), (void **) &span_annotations) == FAILURE) { MAKE_STD_ZVAL(span_annotations_ptr); array_init(span_annotations_ptr); span_annotations = &span_annotations_ptr; add_assoc_zval(*span, "a", span_annotations_ptr); } // limit size of annotations to 1000 characters, this mostly affects "sql" // annotations, but the daemon sql parser is resilent against broken SQL. len = strlen(value); if (copy == 1 && len > 1000) { len = 1000; } add_assoc_stringl_ex(*span_annotations, key, strlen(key)+1, value, len, copy); } php-profiler-extension-4.0.7/php7/000077500000000000000000000000001301131770100170135ustar00rootroot00000000000000php-profiler-extension-4.0.7/php7/spans.c000066400000000000000000000076121301131770100203110ustar00rootroot00000000000000#include "php.h" #include "../php_tideways.h" #include "../spans.h" extern ZEND_DECLARE_MODULE_GLOBALS(hp) long tw_span_create(char *category, size_t category_len TSRMLS_DC) { zval span, starts, stops; int idx; long parent = 0; if (Z_TYPE(TWG(spans)) != IS_ARRAY) { return -1; } idx = zend_hash_num_elements(Z_ARRVAL(TWG(spans))); // If the max spans limit is reached, then we aggregate results on a single // span per category and mark it as "truncated" such that user interfaces // can detect these kind of spans and give them a proper name. if (idx >= TWG(max_spans)) { zval *zv; if (zv = zend_hash_str_find(TWG(span_cache), category, category_len)) { idx = Z_LVAL_P(zv); if (idx > -1) { tw_span_annotate_long(idx, "trunc", 1 TSRMLS_CC); return idx; } } } array_init(&span); array_init(&starts); array_init(&stops); add_assoc_stringl(&span, "n", category, category_len); add_assoc_zval(&span, "b", &starts); add_assoc_zval(&span, "e", &stops); if (parent > 0) { add_assoc_long(&span, "p", parent); } zend_hash_index_update(Z_ARRVAL(TWG(spans)), idx, &span); if (idx >= TWG(max_spans)) { zval zv; ZVAL_LONG(&zv, idx); zend_hash_str_update(TWG(span_cache), category, category_len, &zv); } return idx; } static int tw_convert_to_string(zval *zv) { convert_to_string_ex(zv); return ZEND_HASH_APPLY_KEEP; } void tw_span_annotate(long spanId, zval *annotations TSRMLS_DC) { zval *span, *span_annotations, span_annotations_value; if (spanId == -1) { return; } span = zend_hash_index_find(Z_ARRVAL(TWG(spans)), spanId); if (span == NULL) { return; } span_annotations = zend_hash_str_find(Z_ARRVAL_P(span), "a", sizeof("a") - 1); if (span_annotations == NULL) { span_annotations = &span_annotations_value; array_init(span_annotations); add_assoc_zval(span, "a", span_annotations); } zend_hash_apply(Z_ARRVAL_P(annotations), tw_convert_to_string TSRMLS_CC); zend_hash_merge(Z_ARRVAL_P(span_annotations), Z_ARRVAL_P(annotations), (copy_ctor_func_t) zval_add_ref, 1); } void tw_span_annotate_long(long spanId, char *key, long value) { zval *span, *span_annotations, span_annotations_value; zval annotation_value; if (spanId == -1) { return; } span = zend_hash_index_find(Z_ARRVAL(TWG(spans)), spanId); if (span == NULL) { return; } span_annotations = zend_hash_str_find(Z_ARRVAL_P(span), "a", sizeof("a") - 1); if (span_annotations == NULL) { span_annotations = &span_annotations_value; array_init(span_annotations); add_assoc_zval(span, "a", span_annotations); } ZVAL_LONG(&annotation_value, value); convert_to_string_ex(&annotation_value); add_assoc_zval_ex(span_annotations, key, strlen(key), &annotation_value); } void tw_span_annotate_string(long spanId, char *key, char *value, int copy) { zval *span, *span_annotations, span_annotations_value; int key_len, value_len; zend_string *value_trunc; if (spanId == -1) { return; } span = zend_hash_index_find(Z_ARRVAL(TWG(spans)), spanId); if (span == NULL) { return; } span_annotations = zend_hash_str_find(Z_ARRVAL_P(span), "a", sizeof("a") - 1); if (span_annotations == NULL) { span_annotations = &span_annotations_value; array_init(span_annotations); add_assoc_zval(span, "a", span_annotations); } key_len = strlen(key); value_len = strlen(value); if (value_len < 1000) { add_assoc_string_ex(span_annotations, key, key_len, value); } else { value_trunc = zend_string_init(value, 1000, 0); add_assoc_str_ex(span_annotations, key, key_len, value_trunc); } } php-profiler-extension-4.0.7/php_tideways.h000066400000000000000000000157701301131770100210200ustar00rootroot00000000000000/* * Copyright (c) 2009 Facebook * Copyright (c) 2014-2016 Qafoo GmbH * Copyright (c) 2016 Tideways GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifndef PHP_TIDEWAYS_H #define PHP_TIDEWAYS_H extern zend_module_entry tideways_module_entry; #define phpext_tideways_ptr &tideways_module_entry #ifdef PHP_WIN32 #define PHP_TIDEWAYS_API __declspec(dllexport) #else #define PHP_TIDEWAYS_API #endif #ifdef ZTS #include "TSRM.h" #endif /* Tideways version */ #define TIDEWAYS_VERSION "4.0.7" /* Fictitious function name to represent top of the call tree. The paranthesis * in the name is to ensure we don't conflict with user function names. */ #define ROOT_SYMBOL "main()" /* Size of a temp scratch buffer */ #define SCRATCH_BUF_LEN 512 /* Hierarchical profiling flags. * * Note: Function call counts and wall (elapsed) time are always profiled. * The following optional flags can be used to control other aspects of * profiling. */ #define TIDEWAYS_FLAGS_NO_BUILTINS 0x0001 /* do not profile builtins */ #define TIDEWAYS_FLAGS_CPU 0x0002 /* gather CPU times for funcs */ #define TIDEWAYS_FLAGS_MEMORY 0x0004 /* gather memory usage for funcs */ #define TIDEWAYS_FLAGS_NO_USERLAND 0x0008 /* do not profile userland functions */ #define TIDEWAYS_FLAGS_NO_COMPILE 0x0010 /* do not profile require/include/eval */ #define TIDEWAYS_FLAGS_NO_SPANS 0x0020 #define TIDEWAYS_FLAGS_NO_HIERACHICAL 0x0040 /* Constant for ignoring functions, transparent to hierarchical profile */ #define TIDEWAYS_MAX_FILTERED_FUNCTIONS 256 #define TIDEWAYS_FILTERED_FUNCTION_SIZE \ ((TIDEWAYS_MAX_FILTERED_FUNCTIONS + 7)/8) #define TIDEWAYS_MAX_ARGUMENT_LEN 256 #if !defined(uint64) typedef unsigned long long uint64; #endif #if !defined(uint32) typedef unsigned int uint32; #endif #if !defined(uint8) typedef unsigned char uint8; #endif #if PHP_VERSION_ID < 70000 struct _zend_string { char *val; int len; int persistent; }; typedef struct _zend_string zend_string; typedef long zend_long; typedef int strsize_t; typedef zend_uint uint32_t; #endif /** * ***************************** * GLOBAL DATATYPES AND TYPEDEFS * ***************************** */ /* Tideways maintains a stack of entries being profiled. The memory for the entry * is passed by the layer that invokes BEGIN_PROFILING(), e.g. the hp_execute() * function. Often, this is just C-stack memory. * * This structure is a convenient place to track start time of a particular * profile operation, recursion depth, and the name of the function being * profiled. */ typedef struct hp_entry_t { char *name_hprof; /* function name */ int rlvl_hprof; /* recursion level for function */ uint64 tsc_start; /* start value for wall clock timer */ uint64 cpu_start; /* start value for CPU clock timer */ long int mu_start_hprof; /* memory usage */ long int pmu_start_hprof; /* peak memory usage */ struct hp_entry_t *prev_hprof; /* ptr to prev entry being profiled */ uint8 hash_code; /* hash_code for the function name */ long int span_id; /* span id of this entry if any, otherwise -1 */ } hp_entry_t; typedef struct hp_function_map { char **names; uint8 filter[TIDEWAYS_FILTERED_FUNCTION_SIZE]; } hp_function_map; typedef struct tw_watch_callback { zend_fcall_info fci; zend_fcall_info_cache fcic; } tw_watch_callback; /* Tideways's global state. * * This structure is instantiated once. Initialize defaults for attributes in * hp_init_profiler_state() Cleanup/free attributes in * hp_clean_profiler_state() */ ZEND_BEGIN_MODULE_GLOBALS(hp) /* ---------- Global attributes: ----------- */ /* Indicates if Tideways is currently enabled */ int enabled; /* Indicates if Tideways was ever enabled during this request */ int ever_enabled; int prepend_overwritten; /* Holds all the Tideways statistics */ #if PHP_VERSION_ID >= 70000 zval stats_count; zval spans; zval exception; #else zval *stats_count; zval *spans; zval *exception; #endif long current_span_id; uint64 start_time; zval *backtrace; /* Top of the profile stack */ hp_entry_t *entries; /* freelist of hp_entry_t chunks for reuse... */ hp_entry_t *entry_free_list; /* Function that determines the transaction name and callback */ zend_string *transaction_function; zend_string *transaction_name; char *root; zend_string *exception_function; double timebase_factor; /* Tideways flags */ uint32 tideways_flags; /* counter table indexed by hash value of function names. */ uint8 func_hash_counters[256]; /* Table of filtered function names and their filter */ int filtered_type; // 1 = blacklist, 2 = whitelist, 0 = nothing hp_function_map *filtered_functions; HashTable *trace_watch_callbacks; HashTable *trace_callbacks; HashTable *span_cache; uint32_t gc_runs; /* number of garbage collection runs */ uint32_t gc_collected; /* number of collected items in garbage run */ int compile_count; double compile_wt; uint64 cpu_start; int max_spans; ZEND_END_MODULE_GLOBALS(hp) #ifdef ZTS #define TWG(v) TSRMG(hp_globals_id, zend_hp_globals *, v) #else #define TWG(v) (hp_globals.v) #endif PHP_MINIT_FUNCTION(tideways); PHP_MSHUTDOWN_FUNCTION(tideways); PHP_RINIT_FUNCTION(tideways); PHP_RSHUTDOWN_FUNCTION(tideways); PHP_MINFO_FUNCTION(tideways); PHP_GINIT_FUNCTION(hp); PHP_GSHUTDOWN_FUNCTION(hp); PHP_FUNCTION(tideways_enable); PHP_FUNCTION(tideways_disable); PHP_FUNCTION(tideways_transaction_name); PHP_FUNCTION(tideways_fatal_backtrace); PHP_FUNCTION(tideways_prepend_overwritten); PHP_FUNCTION(tideways_last_detected_exception); PHP_FUNCTION(tideways_last_fatal_error); PHP_FUNCTION(tideways_sql_minify); PHP_FUNCTION(tideways_span_create); PHP_FUNCTION(tideways_get_spans); PHP_FUNCTION(tideways_span_timer_start); PHP_FUNCTION(tideways_span_timer_stop); PHP_FUNCTION(tideways_span_annotate); PHP_FUNCTION(tideways_span_watch); PHP_FUNCTION(tideways_span_callback); #endif /* PHP_TIDEWAYS_H */ php-profiler-extension-4.0.7/spans.h000066400000000000000000000005371301131770100174370ustar00rootroot00000000000000#ifndef TIDEWAYS_SPANS_H #define TIDEWAYS_SPANS_H long tw_span_create(char *category, size_t category_len TSRMLS_DC); void tw_span_annotate(long spanId, zval *annotations TSRMLS_DC); void tw_span_annotate_long(long spanId, char *key, long value TSRMLS_DC); void tw_span_annotate_string(long spanId, char *key, char *value, int copy TSRMLS_DC); #endif php-profiler-extension-4.0.7/tests/000077500000000000000000000000001301131770100172775ustar00rootroot00000000000000php-profiler-extension-4.0.7/tests/common.php000066400000000000000000000074571301131770100213150ustar00rootroot00000000000000 $metrics) { foreach ($ignoreFunctions as $ignoreFunction) { if (strpos($func, "==>" . $ignoreFunction) !== false) { continue 2; } } echo str_pad($func, 40) . ":"; ksort($metrics); foreach ($metrics as $name => $value) { // Only call counts are stable. // Wild card everything else. We still print // the metric name to ensure it was collected. if ($name != "ct") { $value = "*"; } else { $value = str_pad($value, 8, " ", STR_PAD_LEFT); } echo " {$name}={$value};"; } echo "\n"; } } function print_spans($spans) { foreach ($spans as $span) { if (!isset($span['a'])) { $span['a'] = array(); } ksort($span['a']); $annotations = ''; foreach ($span['a'] as $k => $v) { if ($k === 'fn') { continue; } $annotations .= "$k=$v "; } printf("%s: %d timers - %s\n", $span['n'], count($span['b']), rtrim($annotations)); } } /** * Code is from https://github.com/php/php-src/blob/master/ext/curl/tests/server.inc licensed under PHP license */ define ("PHP_HTTP_SERVER_HOSTNAME", "localhost"); define ("PHP_HTTP_SERVER_PORT", 8964); define ("PHP_HTTP_SERVER_ADDRESS", PHP_HTTP_SERVER_HOSTNAME.":".PHP_HTTP_SERVER_PORT); function http_cli_server_start() { if(getenv('PHP_HTTP_HTTP_REMOTE_SERVER')) { return getenv('PHP_HTTP_HTTP_REMOTE_SERVER'); } $php_executable = getenv('TEST_PHP_EXECUTABLE'); $doc_root = __DIR__; $router = "http_responder.php"; $descriptorspec = array( 0 => STDIN, 1 => STDOUT, 2 => STDERR, ); if (substr(PHP_OS, 0, 3) == 'WIN') { $cmd = "{$php_executable} -t {$doc_root} -n -S " . PHP_HTTP_SERVER_ADDRESS; $cmd .= " {$router}"; $handle = proc_open(addslashes($cmd), $descriptorspec, $pipes, $doc_root, NULL, array("bypass_shell" => true, "suppress_errors" => true)); } else { $cmd = "exec {$php_executable} -t {$doc_root} -n -S " . PHP_HTTP_SERVER_ADDRESS; $cmd .= " {$router}"; $cmd .= " 2>/dev/null"; $handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root); } // note: even when server prints 'Listening on localhost:8964...Press Ctrl-C to quit.' // it might not be listening yet...need to wait until fsockopen() call returns $i = 0; while (($i++ < 30) && !($fp = @fsockopen(PHP_HTTP_SERVER_HOSTNAME, PHP_HTTP_SERVER_PORT))) { usleep(10000); } if ($fp) { fclose($fp); } register_shutdown_function( function($handle) use($router) { proc_terminate($handle); }, $handle ); // don't bother sleeping, server is already up // server can take a variable amount of time to be up, so just sleeping a guessed amount of time // does not work. this is why tests sometimes pass and sometimes fail. to get a reliable pass // sleeping doesn't work. return PHP_HTTP_SERVER_ADDRESS; } php-profiler-extension-4.0.7/tests/common_predis.php000066400000000000000000000001571301131770100226510ustar00rootroot00000000000000 php-profiler-extension-4.0.7/tests/memleak.phpt000066400000000000000000000002621301131770100216070ustar00rootroot00000000000000--TEST-- Tideways: Check for no memleaks --FILE-- --EXPECT-- Part 1: Default Flags foo==>bar : ct= 2; wt=*; main() : ct= 1; wt=*; main()==>foo : ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; Part 2: CPU foo==>bar : cpu=*; ct= 2; wt=*; main() : cpu=*; ct= 1; wt=*; main()==>foo : cpu=*; ct= 1; wt=*; main()==>tideways_disable : cpu=*; ct= 1; wt=*; Part 3: No Builtins foo==>bar : ct= 2; wt=*; main() : ct= 1; wt=*; main()==>foo : ct= 1; wt=*; Part 4: Memory foo==>bar : ct= 2; mu=*; pmu=*; wt=*; main() : ct= 1; mu=*; pmu=*; wt=*; main()==>foo : ct= 1; mu=*; pmu=*; wt=*; main()==>tideways_disable : ct= 1; mu=*; pmu=*; wt=*; Part 5: Memory & CPU foo==>bar : cpu=*; ct= 2; mu=*; pmu=*; wt=*; main() : cpu=*; ct= 1; mu=*; pmu=*; wt=*; main()==>foo : cpu=*; ct= 1; mu=*; pmu=*; wt=*; main()==>tideways_disable : cpu=*; ct= 1; mu=*; pmu=*; wt=*; php-profiler-extension-4.0.7/tests/tideways_002.phpt000066400000000000000000000035431301131770100224130ustar00rootroot00000000000000--TEST-- Tideways: Test (direct and indirect) recursive function calls. --FILE-- 0) { if ($use_direct_recursion) foo($depth - 1, $use_direct_recursion); else bar($depth - 1, $use_direct_recursion); } } tideways_enable(); foo(4, true); $output = tideways_disable(); echo "Direct Recursion\n"; print_canonical($output); echo "\n"; tideways_enable(); foo(4, false); $output = tideways_disable(); echo "Indirect Recursion\n"; print_canonical($output); echo "\n"; ?> --EXPECT-- Direct Recursion foo==>foo@1 : ct= 1; wt=*; foo@1==>foo@2 : ct= 1; wt=*; foo@2==>foo@3 : ct= 1; wt=*; foo@3==>foo@4 : ct= 1; wt=*; main() : ct= 1; wt=*; main()==>foo : ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; Indirect Recursion bar==>foo@1 : ct= 1; wt=*; bar@1==>foo@2 : ct= 1; wt=*; bar@2==>foo@3 : ct= 1; wt=*; bar@3==>foo@4 : ct= 1; wt=*; foo==>bar : ct= 1; wt=*; foo@1==>bar@1 : ct= 1; wt=*; foo@2==>bar@2 : ct= 1; wt=*; foo@3==>bar@3 : ct= 1; wt=*; main() : ct= 1; wt=*; main()==>foo : ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; php-profiler-extension-4.0.7/tests/tideways_003.phpt000066400000000000000000000026371301131770100224170ustar00rootroot00000000000000--TEST-- Tideways: Test Class Methods, Constructors, Destructors. --FILE-- _attr = $attr; } private static function inner_static() { return C::$_static_attr; } public static function outer_static() { return C::inner_static(); } public function get_attr() { return $this->_attr; } function __destruct() { echo "Destroying class {$this->_attr}\n"; } } tideways_enable(); // static methods echo C::outer_static() . "\n"; // constructor $obj = new C("Hello World"); // instance methods $obj->get_attr(); // destructor $obj = null; $output = tideways_disable(); echo "Profiler data for 'Class' tests:\n"; print_canonical($output); echo "\n"; ?> --EXPECT-- i am a class static In constructor... Destroying class Hello World Profiler data for 'Class' tests: C::outer_static==>C::inner_static : ct= 1; wt=*; main() : ct= 1; wt=*; main()==>C::__construct : ct= 1; wt=*; main()==>C::__destruct : ct= 1; wt=*; main()==>C::get_attr : ct= 1; wt=*; main()==>C::outer_static : ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; php-profiler-extension-4.0.7/tests/tideways_004.phpt000066400000000000000000000021631301131770100224120ustar00rootroot00000000000000--TEST-- Tideways: Test Include File (load/run_init operations) --FILE-- --EXPECTF-- abc,def,ghi I am in foo()... 11 I am in bar()... Test for 'include_once' & 'require_once' operation Includes: 2 Time: %d php-profiler-extension-4.0.7/tests/tideways_004_inc.php000066400000000000000000000006741301131770100230640ustar00rootroot00000000000000" // which represents the initialization block of a file. // $result1 = explode(" ", "abc def ghi"); $result2 = implode(",", $result1); echo $result2 . "\n"; foo(); php-profiler-extension-4.0.7/tests/tideways_004_require.php000066400000000000000000000007321301131770100237620ustar00rootroot00000000000000" // which represents the initialization block of a file. // $result1 = explode(" ", "abc def ghi"); $result2 = implode(",", $result1); $result3 = strlen($result2); echo $result3 . "\n"; bar(); php-profiler-extension-4.0.7/tests/tideways_005.phpt000066400000000000000000000032011301131770100224050ustar00rootroot00000000000000--TEST-- Tideways: Timer Tests --FILE-- $range_high)) { echo "Failed ${description}. Expected: ${expected} microsecs. ". "Actual: ${actual} microsecs.\n"; } else { echo "OK: ${description}\n"; } echo "-------------\n"; } verify(10000, $output["sleep_10000_micro==>usleep"]["wt"], "sleep_10000_micro"); verify(20000, $output["sleep_20000_micro==>usleep"]["wt"], "sleep_20000_micro"); verify(50000, $output["sleep_50000_micro==>usleep"]["wt"], "sleep_50000_micro"); ?> --EXPECT-- Verifying sleep_10000_micro... OK: sleep_10000_micro ------------- Verifying sleep_20000_micro... OK: sleep_20000_micro ------------- Verifying sleep_50000_micro... OK: sleep_50000_micro ------------- php-profiler-extension-4.0.7/tests/tideways_007.phpt000066400000000000000000000235451301131770100224240ustar00rootroot00000000000000--TEST-- Tideways: Test excluding call_user_func and similar functions --SKIPIF-- = 70000) echo "skip: PHP 5 required\n"; --FILE-- array('call_user_func', 'call_user_func_array', 'my_call_user_func_safe', 'my_call_user_func_array_safe')); function bar() { return 1; } function foo($x) { if (is_array($x)) { $x = 'Array'; } $sum = 0; for ($idx = 0; $idx < 2; $idx++) { $sum += bar(); } echo "hello: {$x}\n" ; return strlen("hello: {$x}"); } function foo_array($x1, $x2 = 'test') { if (is_array($x1)) { $x1 = 'Array'; } $sum = 0; $x = array($x1, $x2); foreach ($x as $idx) { $sum += bar(); } echo "hello: " . $x[0] . $x[1] . "\n"; return strlen("hello: {$x[0]} {$x[1]}"); } function my_call_user_func_safe($function, $args = 'my_safe') { if (!is_callable($function, true)) { throw new Exception('my_call_user_func_safe() invoked without ' . 'a valid callable.'); } call_user_func($function, array($args)); } function my_call_user_func_array_safe($function, $args = array()) { if (!is_callable($function, true)) { throw new Exception('my_call_user_func_array_safe() invoked without ' . 'a valid callable.'); } call_user_func_array($function, $args); } class test_call_user_func { function __construct($test_func = 'foo', $arg1 = 'user_func test') { call_user_func($test_func, $arg1); } } function test_call_user_func_array($test_func = 'foo_array', $arg1 = array(0 => 'user_func_array', 'test')) { call_user_func_array($test_func, $arg1); } function test_my_call_user_func_safe($test_func = 'foo', $arg1 = 'my_user_func_safe test') { my_call_user_func_safe($test_func, $arg1); } function test_my_call_user_func_array_safe( $test_func = 'foo_array', $arg1 = array('my_user_func_array_safe', 'test')) { my_call_user_func_array_safe($test_func, $arg1); } // 1: Sanity test a simple profile run echo "Part 1: Default Flags\n"; tideways_enable(0, $tideways_ignored_functions); foo("this is a test"); $array_arg = array(); $array_arg[] = 'calling '; $array_arg[] = 'foo_array'; foo_array($array_arg); $output = tideways_disable(); echo "Part 1 output:\n"; print_canonical($output); echo "\n"; // 2a: Sanity test ignoring call_user_func echo "Part 2a: Ignore call_user_func\n"; tideways_enable(0, $tideways_ignored_functions); $indirect_foo = new test_call_user_func('foo'); $output = tideways_disable(); echo "Part 2a output:\n"; print_canonical($output); echo "\n"; // 2b: Confirm that profiling without parameters still works echo "Part 2b: Standard profile without parameters\n"; tideways_enable(); $indirect_foo = new test_call_user_func('foo'); $output = tideways_disable(); echo "Part 2b output:\n"; print_canonical($output); echo "\n"; // 2c: Confirm that empty array of ignored functions works echo "Part 2c: Standard profile with empty array of ignored functions\n"; tideways_enable(0, array()); $indirect_foo = new test_call_user_func('foo'); $output = tideways_disable(); echo "Part 2c output:\n"; print_canonical($output); echo "\n"; // 3: Sanity test ignoring call_user_func_array echo "Part 3: Ignore call_user_func_array\n"; tideways_enable(TIDEWAYS_FLAGS_CPU, $tideways_ignored_functions); test_call_user_func_array('foo_array', $array_arg); $output = tideways_disable(); echo "Part 3 output:\n"; print_canonical($output); echo "\n"; // 4: Sanity test ignoring my_call_user_func_safe echo "Part 4: Ignore my_call_user_func_safe\n"; tideways_enable(0, $tideways_ignored_functions); test_my_call_user_func_safe('foo'); $output = tideways_disable(); echo "Part 4 output:\n"; print_canonical($output); echo "\n"; // 5a: Sanity test ignoring my_call_user_func_array_safe and strlen echo "Part 5a: Ignore my_call_user_func_array_safe and strlen\n"; $tmp1 = $tideways_ignored_functions['ignored_functions']; $tmp1[] = 'strlen'; $ignore_strlen_also = array('ignored_functions' => $tmp1); tideways_enable(TIDEWAYS_FLAGS_MEMORY, $ignore_strlen_also); test_my_call_user_func_array_safe('foo_array'); $output = tideways_disable(); echo "Part 5a output:\n"; print_canonical($output); echo "\n"; // 5b: Sanity test to not ignore call_user_func variants echo "Part 5b: Profile call_user_func_array and my_call_user_func_array_safe\n"; tideways_enable(TIDEWAYS_FLAGS_MEMORY, array()); test_my_call_user_func_array_safe('foo_array'); $output = tideways_disable(); echo "Part 5b output:\n"; print_canonical($output); echo "\n"; // 5c: Sanity test to only ignore my_call_user_func_array_safe echo "Part 5c: Only ignore call_user_func_array\n"; $tideways_ignored_functions = array('ignored_functions' => 'my_call_user_func_array_safe'); tideways_enable(TIDEWAYS_FLAGS_MEMORY, $tideways_ignored_functions); test_my_call_user_func_array_safe('foo_array'); $output = tideways_disable(); echo "Part 5c output:\n"; print_canonical($output); echo "\n"; ?> --EXPECT-- Part 1: Default Flags hello: this is a test hello: Arraytest Part 1 output: foo==>bar : ct= 2; wt=*; foo_array==>bar : ct= 2; wt=*; main() : ct= 1; wt=*; main()==>foo : ct= 1; wt=*; main()==>foo_array : ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; Part 2a: Ignore call_user_func hello: user_func test Part 2a output: foo==>bar : ct= 2; wt=*; main() : ct= 1; wt=*; main()==>test_call_user_func::__construct: ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; test_call_user_func::__construct==>foo : ct= 1; wt=*; Part 2b: Standard profile without parameters hello: user_func test Part 2b output: call_user_func==>foo : ct= 1; wt=*; foo==>bar : ct= 2; wt=*; main() : ct= 1; wt=*; main()==>test_call_user_func::__construct: ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; test_call_user_func::__construct==>call_user_func: ct= 1; wt=*; Part 2c: Standard profile with empty array of ignored functions hello: user_func test Part 2c output: call_user_func==>foo : ct= 1; wt=*; foo==>bar : ct= 2; wt=*; main() : ct= 1; wt=*; main()==>test_call_user_func::__construct: ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; test_call_user_func::__construct==>call_user_func: ct= 1; wt=*; Part 3: Ignore call_user_func_array hello: calling foo_array Part 3 output: foo_array==>bar : cpu=*; ct= 2; wt=*; main() : cpu=*; ct= 1; wt=*; main()==>test_call_user_func_array : cpu=*; ct= 1; wt=*; main()==>tideways_disable : cpu=*; ct= 1; wt=*; test_call_user_func_array==>foo_array : cpu=*; ct= 1; wt=*; Part 4: Ignore my_call_user_func_safe hello: Array Part 4 output: foo==>bar : ct= 2; wt=*; main() : ct= 1; wt=*; main()==>test_my_call_user_func_safe : ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; test_my_call_user_func_safe==>foo : ct= 1; wt=*; test_my_call_user_func_safe==>is_callable: ct= 1; wt=*; Part 5a: Ignore my_call_user_func_array_safe and strlen hello: my_user_func_array_safetest Part 5a output: foo_array==>bar : ct= 2; mu=*; pmu=*; wt=*; main() : ct= 1; mu=*; pmu=*; wt=*; main()==>test_my_call_user_func_array_safe: ct= 1; mu=*; pmu=*; wt=*; main()==>tideways_disable : ct= 1; mu=*; pmu=*; wt=*; test_my_call_user_func_array_safe==>foo_array: ct= 1; mu=*; pmu=*; wt=*; test_my_call_user_func_array_safe==>is_callable: ct= 1; mu=*; pmu=*; wt=*; Part 5b: Profile call_user_func_array and my_call_user_func_array_safe hello: my_user_func_array_safetest Part 5b output: call_user_func_array==>foo_array : ct= 1; mu=*; pmu=*; wt=*; foo_array==>bar : ct= 2; mu=*; pmu=*; wt=*; main() : ct= 1; mu=*; pmu=*; wt=*; main()==>test_my_call_user_func_array_safe: ct= 1; mu=*; pmu=*; wt=*; main()==>tideways_disable : ct= 1; mu=*; pmu=*; wt=*; my_call_user_func_array_safe==>call_user_func_array: ct= 1; mu=*; pmu=*; wt=*; my_call_user_func_array_safe==>is_callable: ct= 1; mu=*; pmu=*; wt=*; test_my_call_user_func_array_safe==>my_call_user_func_array_safe: ct= 1; mu=*; pmu=*; wt=*; Part 5c: Only ignore call_user_func_array hello: my_user_func_array_safetest Part 5c output: call_user_func_array==>foo_array : ct= 1; mu=*; pmu=*; wt=*; foo_array==>bar : ct= 2; mu=*; pmu=*; wt=*; main() : ct= 1; mu=*; pmu=*; wt=*; main()==>test_my_call_user_func_array_safe: ct= 1; mu=*; pmu=*; wt=*; main()==>tideways_disable : ct= 1; mu=*; pmu=*; wt=*; test_my_call_user_func_array_safe==>call_user_func_array: ct= 1; mu=*; pmu=*; wt=*; test_my_call_user_func_array_safe==>is_callable: ct= 1; mu=*; pmu=*; wt=*; php-profiler-extension-4.0.7/tests/tideways_009.phpt000066400000000000000000000007651301131770100224250ustar00rootroot00000000000000--TEST-- XHProf: Function Argument Profiling --FILE-- array('substr'))); test_stringlength('foo_array'); $output = tideways_disable(); if (count($output) == 2 && isset($output['main()']) && isset($output['main()==>substr'])) { echo "Test passed"; } else { var_dump($output); } ?> --EXPECT-- Test passed php-profiler-extension-4.0.7/tests/tideways_018.phpt000066400000000000000000000010471301131770100224170ustar00rootroot00000000000000--TEST-- Tideways: Exclude userland functions from profilling --FILE-- --EXPECTF-- Output: main() : ct= 1; wt=*; main()==>substr : ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; php-profiler-extension-4.0.7/tests/tideways_023.phpt000066400000000000000000000075011301131770100224140ustar00rootroot00000000000000--TEST-- XHProf: Transaction Name Detection in Hierachical Profiling Mode --FILE-- 'Symfony\Component\HttpKernel\Controller\ControllerResolver::createController', )); $resolver = new ControllerResolver(); $resolver->createController("foo::bar"); echo "Symfony2: " . tideways_transaction_name() . "\n"; tideways_disable(); } function transaction_zf1() { tideways_enable(TIDEWAYS_FLAGS_NO_SPANS, array( 'transaction_function' => 'Zend_Controller_Action::dispatch', )); $controller = new MyController(); $controller->dispatch("fooAction"); echo "Zend Framework 1: " . trim(tideways_transaction_name()) . "\n"; $data = tideways_disable(); } function transaction_zf2() { tideways_enable(TIDEWAYS_FLAGS_NO_SPANS, array( 'transaction_function' => 'Zend\\MVC\\Controller\\ControllerManager::get', )); $manager = new ControllerManager(); $manager->get("FooCtrl", array("foo" => "bar"), false); echo "Zend Framework 2: " . tideways_transaction_name() . "\n"; $data = tideways_disable(); } function transaction_oxid() { tideways_enable(TIDEWAYS_FLAGS_NO_SPANS, array( 'transaction_function' => 'oxView::setClassName', )); $shop = new oxView(); $shop->setClassName("alist"); echo "Oxid: " . tideways_transaction_name() . "\n"; $data = tideways_disable(); } function transaction_shopware() { tideways_enable(TIDEWAYS_FLAGS_NO_SPANS, array( 'transaction_function' => 'Enlight_Controller_Action::dispatch', )); $controller = new ShopController(); $controller->dispatch('listAction'); echo "Shopware: " . trim(tideways_transaction_name()) . "\n"; $data = tideways_disable(); } function transaction_wordpress() { tideways_enable(TIDEWAYS_FLAGS_NO_SPANS, array( 'transaction_function' => 'get_query_template', )); get_query_template("home"); echo "Wordpress: " . tideways_transaction_name() . "\n"; $data = tideways_disable(); } function transaction_laravel() { tideways_enable(TIDEWAYS_FLAGS_NO_SPANS, array( 'transaction_function' => 'Illuminate\Routing\Controller::callAction', )); $ctrl = new \CachetHQ\Cachet\Http\Controllers\RssController(); $ctrl->callAction('indexAction', array()); echo "Laravel: " . tideways_transaction_name() . "\n"; $data = tideways_disable(); } function transaction_flow3() { tideways_enable(TIDEWAYS_FLAGS_NO_SPANS, array( 'transaction_function' => 'TYPO3\Flow\Mvc\Controller\ActionController::callActionMethod', )); $ctrl = new \TYPO3\Flow\Mvc\Controller\FooController(); $ctrl->processRequest(); echo "FLOW3: " . tideways_transaction_name() . "\n"; $data = tideways_disable(); } transaction_symfony2(); transaction_zf2(); transaction_oxid(); transaction_shopware(); transaction_wordpress(); transaction_zf1(); transaction_laravel(); transaction_flow3(); --EXPECTF-- Symfony2: foo::bar Zend Framework 2: FooCtrl Oxid: alist Shopware: ShopController::listAction Wordpress: home Zend Framework 1: MyController::fooAction Laravel: CachetHQ\Cachet\Http\Controllers\RssController::indexAction FLOW3: TYPO3\Flow\Mvc\Controller\FooController::indexAction php-profiler-extension-4.0.7/tests/tideways_023_classes.php000066400000000000000000000017461301131770100237520ustar00rootroot00000000000000callActionMethod(); } protected function callActionMethod() {} } class FooController extends ActionController {} } php-profiler-extension-4.0.7/tests/tideways_024.phpt000066400000000000000000000020711301131770100224120ustar00rootroot00000000000000--TEST-- XHProf: Transaction name detection when disabling userland --FILE-- 'get_query_template') ); get_query_template("home"); echo "Wordpress 1: " . tideways_transaction_name() . "\n"; $data = tideways_disable(); // Test 2: Repetition to catch global state errors tideways_enable( TIDEWAYS_FLAGS_NO_USERLAND | TIDEWAYS_FLAGS_NO_BUILTINS, array('transaction_function' => 'get_query_template') ); get_query_template("page"); echo "Wordpress 2: " . tideways_transaction_name() . "\n"; $data = tideways_disable(); // Test 3: Without any layers defined tideways_enable( TIDEWAYS_FLAGS_NO_USERLAND | TIDEWAYS_FLAGS_NO_BUILTINS, array('transaction_function' => 'get_query_template') ); get_query_template("post"); echo "Wordpress 3: " . tideways_transaction_name() . "\n"; $data = tideways_disable(); --EXPECTF-- Wordpress 1: home Wordpress 2: page Wordpress 3: post php-profiler-extension-4.0.7/tests/tideways_026.phpt000066400000000000000000000003641301131770100224170ustar00rootroot00000000000000--TEST-- XHProf: eval() --FILE-- self = $a; } gc_collect_cycles(); } tideways_enable(); create_garbage(); tideways_disable(); $spans = tideways_get_spans(); echo "Garbage Collection runs: " . $spans[0]['a']['gc'] . "\n"; echo "Garbage Collection Cycles Collected: " . $spans[0]['a']['gcc'] . "\n"; if (PHP_VERSION_ID >= 70000) { echo "Collection In: " . $spans[1]['a']['title'] . "\n"; echo "Collection In: " . $spans[2]['a']['title'] . "\n"; } else { // emulate output for PHP 5.* echo "Collection In: create_garbage\n"; echo "Collection In: gc_collect_cycles\n"; } --EXPECTF-- Garbage Collection runs: 2 Garbage Collection Cycles Collected: 10999 Collection In: create_garbage Collection In: gc_collect_cycles php-profiler-extension-4.0.7/tests/tideways_035.phpt000066400000000000000000000003531301131770100224150ustar00rootroot00000000000000--TEST-- Tideways: Function that checks if prepend was overwritten --FILE-- dirname : ct= 1; wt=*; main()==>evaledfoo : ct= 1; wt=*; main()==>explode : ct= 1; wt=*; main()==>foo : ct= 1; wt=*; main()==>implode : ct= 1; wt=*; main()==>tideways_disable : ct= 1; wt=*; php-profiler-extension-4.0.7/tests/tideways_037.phpt000066400000000000000000000007171301131770100224230ustar00rootroot00000000000000--TEST-- Tideways: Enable stops and discards running trace --FILE-- 1000000) { echo "OK\n"; } else { echo "FAIL\n"; } --EXPECTF-- OK php-profiler-extension-4.0.7/tests/tideways_039.phpt000066400000000000000000000002561301131770100224230ustar00rootroot00000000000000--TEST-- Tideways: Auto Prepend File 1 --INI-- auto_prepend_file=tests/tideways_039_prepend.php tideways.auto_prepend_library=0 --FILE-- array('my_func'))); my_func(); my_other(); call_user_func('my_func'); call_user_func('my_other'); $output = tideways_disable(); echo "With ignored_functions:\n"; print_canonical($output); tideways_enable(); my_func(); my_other(); call_user_func('my_func'); call_user_func('my_other'); $output = tideways_disable(); echo "\nWithout ignored_functions:\n"; print_canonical($output); --EXPECTF-- With ignored_functions: main() : ct= 1; wt=*; main()==>my_other : ct= 2; wt=*; main()==>substr : ct= 2; wt=*; main()==>tideways_disable : ct= 1; wt=*; my_other==>substr : ct= 2; wt=*; Without ignored_functions: main() : ct= 1; wt=*; main()==>my_func : ct= 2; wt=*; main()==>my_other : ct= 2; wt=*; main()==>tideways_disable : ct= 1; wt=*; my_func==>substr : ct= 4; wt=*; my_other==>my_func : ct= 2; wt=*; php-profiler-extension-4.0.7/tests/tideways_doctrine.php000066400000000000000000000032541301131770100235340ustar00rootroot00000000000000_resultSetMapping = $rsm; } public function execute() { } } class Query extends AbstractQuery { public function getDQL() { return 'SELECT f FROM Foo\\Bar\\Baz'; } } class NativeQuery extends AbstractQuery { public function getSQL() { return 'SELECT foo FROM bar'; } } } namespace Doctrine\ORM\Query { class ResultSetMapping { public $aliasMap = array(); } } namespace Doctrine\ORM\Persisters { use Doctrine\ORM\Mapping\ClassMetadata; class BasicEntityPersister { protected $class; public function __construct($name) { $this->class = new ClassMetadata(); $this->class->name = $name; } public function load() { } } } namespace Doctrine\ORM\Mapping { class ClassMetadata { public $name; } } namespace Doctrine\CouchDB\HTTP { abstract class AbstractHTTPClient { private $options; public function __construct($host = 'localhost') { $this->options['host'] = $host; } } class StreamClient extends AbstractHTTPClient { public function request($method, $path) { var_dump($method ." " . $path); } } class SocketClient extends AbstractHTTPClient { public function request($method, $path) { var_dump($method ." " . $path); } } } php-profiler-extension-4.0.7/tests/tideways_errors_001.phpt000066400000000000000000000017431301131770100240060ustar00rootroot00000000000000--TEST-- Tideways: Fetch Fatal Error Backtrace --SKIPIF-- = 70000) { die("skip: Dont need error callback overwrite on PHP 7+"); } --FILE-- array(4) { ["file"]=> string(%d) "%s/tests/tideways_errors_001.php" ["line"]=> int(4) ["function"]=> string(3) "bar" ["args"]=> array(0) { } } [1]=> array(4) { ["file"]=> string(%d) "%s/tests/tideways_errors_001.php" ["line"]=> int(21) ["function"]=> string(3) "foo" ["args"]=> array(0) { } } } php-profiler-extension-4.0.7/tests/tideways_errors_002.phpt000066400000000000000000000016261301131770100240070ustar00rootroot00000000000000--TEST-- Tideways: exception function --FILE-- 'foo')); foo($original = new Exception("foo")); $fetched = tideways_last_detected_exception(); tideways_disable(); var_dump($fetched->getMessage()); var_dump($fetched === $original); tideways_enable(0, array('exception_function' => 'foo')); foo($original = new RuntimeException("detect mode")); $fetched = tideways_last_detected_exception(); tideways_disable(); var_dump($fetched->getMessage()); tideways_enable(0, array('exception_function' => 'baz')); baz(1, $original = new RuntimeException("random argument"), 2); $fetched = tideways_last_detected_exception(); tideways_disable(); var_dump($fetched->getMessage()); --EXPECTF-- string(3) "foo" bool(true) string(11) "detect mode" string(15) "random argument" php-profiler-extension-4.0.7/tests/tideways_errors_003.phpt000066400000000000000000000011711301131770100240030ustar00rootroot00000000000000--TEST-- Tideways: tideways_last_fatal_error() for B/C reasons --SKIPIF-- int(1) ["message"]=> string(%d) "%sCall to undefined function foo()%s Stack trace: #0 {main} thrown" ["file"]=> string(%d) "%s/tests/tideways_errors_003.php" ["line"]=> int(7) } php-profiler-extension-4.0.7/tests/tideways_errors_004.phpt000066400000000000000000000010301301131770100237760ustar00rootroot00000000000000--TEST-- Tideways: tideways_last_fatal_error() for B/C reasons --SKIPIF-- = 70000) { echo "skip: php5 only\n"; } --FILE-- int(1) ["message"]=> string(32) "Call to undefined function foo()" ["file"]=> string(%d) "%s/tests/tideways_errors_004.php" ["line"]=> int(7) } php-profiler-extension-4.0.7/tests/tideways_errors_005.phpt000066400000000000000000000011401301131770100240010ustar00rootroot00000000000000--TEST-- Tideways: Fetch Error Exception on PHP 7 --SKIPIF-- getMessage()); }); tideways_enable(); try { foobar(); } catch (Error $e) { foobar(); } tideways_disable(); --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function foobar() in %stideways_errors_005.php:11 Stack trace: #0 {main} thrown in %stideways_errors_005.php on line 11 string(8) "shutdown" string(35) "Call to undefined function foobar()" php-profiler-extension-4.0.7/tests/tideways_events1.php000066400000000000000000000024231301131770100233070ustar00rootroot00000000000000doDispatch(array(), $eventName, $event); } protected function doDispatch(array $listeners, $eventName, $event) { usleep(100); } } class Event { } } namespace Zend\EventManager { class EventManager { public function trigger($event, $context, $params) { usleep(100); } } } namespace Doctrine\Common { class EventManager { public function dispatchEvent($name, $event) { usleep(100); } } } namespace TYPO3\Flow\SignalSlot { class Dispatcher { public function dispatch($signalClassName, $signalName) { } } } namespace Cake\Event { class EventManager { public function dispatch($event) { if (is_string($event)) { $event = new Event($event); } } } class Event { private $name; public function __construct($name) { $this->name = $name; } public function name() { return $this->name; } } } php-profiler-extension-4.0.7/tests/tideways_events2.php000066400000000000000000000015361301131770100233140ustar00rootroot00000000000000name = $name; } public function name() { return $this->name; } } php-profiler-extension-4.0.7/tests/tideways_fw_magento_001.phpt000066400000000000000000000015661301131770100246230ustar00rootroot00000000000000--TEST-- Tideways: Magento Support --FILE-- loadModules(); $config->loadDb(); } } class Mage_Core_Model_Config { public function loadModules() {} public function loadDb() {} } abstract class Mage_Core_Block_Abstract { public function toHtml() {} } class SomeView extends Mage_Core_Block_Abstract { } tideways_enable(); $app = new Mage_Core_Model_App(); $app->_initModules(); $block = new SomeView(); $block->toHtml(); print_spans(tideways_get_spans()); --EXPECT-- app: 1 timers - php: 1 timers - title=Mage_Core_Model_App::_initModules php: 1 timers - title=Mage_Core_Model_Config::loadModules php: 1 timers - title=Mage_Core_Model_Config::loadDb view: 1 timers - title=SomeView php-profiler-extension-4.0.7/tests/tideways_fw_options.phpt000066400000000000000000000015471301131770100243030ustar00rootroot00000000000000--TEST-- Tideways: Test NO_TXDETECT and NO_SPANS options --FILE-- setClass($sClass); $oViewObject->setFncName($sFnc); $oViewObject->executeFunction($oViewObject->getFncName()); } } class oxView { protected $_sFnc; protected $_sClass; public function setFncName($fnc) { $this->_sFnc = $fnc; } public function setClass($sClass) { $this->_sClass = $sClass; } public function getFncName() { return $this->_sFnc; } public function executeFunction($name) { $this->$name(); } } class alist extends oxView { public function getArticleList() { $shop = new oxShopControl(); for ($i = 0; $i < 3; $i++) { $shop->_process('article', 'getListItem'); } } public function executeFunction($name) { return $this->getArticleList(); } } class article extends oxView { public function getListItem() { } } $shop = new oxShopControl(); tideways_enable(); $shop->_process('alist'); print_spans(tideways_get_spans()); tideways_disable(); echo "\n\n"; tideways_enable(); $shop->_process('article', 'getListItem'); print_spans(tideways_get_spans()); --EXPECTF-- app: 1 timers - php.ctrl: 1 timers - title=alist php.ctrl: 3 timers - title=article::getListItem app: 1 timers - php.ctrl: 1 timers - title=article::getListItem php-profiler-extension-4.0.7/tests/tideways_fw_shopware_001.phpt000066400000000000000000000013501301131770100250100ustar00rootroot00000000000000--TEST-- Tideways: Shopware Support --FILE-- $method(); } } class ShopController extends Enlight_Controller_Action { public function fooAction() {} public function barAction() {} } class ShopProxyController extends ShopController { } tideways_enable(); $ctrl = new ShopProxyController(); $ctrl->dispatch('fooAction'); $ctrl->dispatch('barAction'); print_spans(tideways_get_spans()); --EXPECTF-- app: 1 timers - php.ctrl: 1 timers - title=ShopProxyController::fooAction php.ctrl: 1 timers - title=ShopProxyController::barAction php-profiler-extension-4.0.7/tests/tideways_fw_symfony_001.phpt000066400000000000000000000012431301131770100246650ustar00rootroot00000000000000--TEST-- Tideways: Symfony Support --FILE-- boot(); $httpKernel = new \Symfony\Component\HttpKernel\HttpKernel(); $httpKernel->handle('indexAction'); $httpKernel->handle('helloAction'); print_spans(tideways_get_spans()); tideways_disable(); --EXPECTF-- app: 1 timers - php: 1 timers - title=Symfony\Component\HttpKernel\Kernel::boot php.ctrl: 1 timers - title=Acme\DemoBundle\Controller\DefaultController::indexAction php.ctrl: 1 timers - title=Acme\DemoBundle\Controller\DefaultController::helloAction php-profiler-extension-4.0.7/tests/tideways_fw_wordpress_001.phpt000066400000000000000000000014601301131770100252120ustar00rootroot00000000000000--TEST-- Tideways: Wordpress Support --FILE-- render('foo/bar.phtml'); print_spans(tideways_get_spans()); --EXPECT-- app: 1 timers - view: 1 timers - title=foo/bar.phtml php-profiler-extension-4.0.7/tests/tideways_queue.php000066400000000000000000000006741301131770100230540ustar00rootroot00000000000000_using = $channel; $this->put(); } } } namespace PhpAmqpLib\Channel { class AMQPChannel { public function basic_publish($msg, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false, $ticket = null) {} } } php-profiler-extension-4.0.7/tests/tideways_spans_001.phpt000066400000000000000000000023441301131770100236140ustar00rootroot00000000000000--TEST-- Tideways: Span Create/Get --FILE-- array(4) { ["n"]=> string(3) "app" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(1) { [0]=> int(%d) } ["a"]=> array(1) { ["cpu"]=> string(%d) "%d" } } [1]=> array(3) { ["n"]=> string(3) "php" ["b"]=> array(%d) { } ["e"]=> array(%d) { } } } int(1) array(2) { [0]=> array(4) { ["n"]=> string(3) "app" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(1) { [0]=> int(%d) } ["a"]=> array(1) { ["cpu"]=> string(%d) "%d" } } [1]=> array(3) { ["n"]=> string(3) "app" ["b"]=> array(%d) { } ["e"]=> array(%d) { } } } php-profiler-extension-4.0.7/tests/tideways_spans_002.phpt000066400000000000000000000021161301131770100236120ustar00rootroot00000000000000--TEST-- Tideways: Start/Stop Timers --FILE-- array(4) { ["n"]=> string(3) "app" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(1) { [0]=> int(%d) } ["a"]=> array(1) { ["cpu"]=> string(%d) "%d" } } [1]=> array(3) { ["n"]=> string(3) "php" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(1) { [0]=> int(%d) } } [2]=> array(3) { ["n"]=> string(3) "php" ["b"]=> array(2) { [0]=> int(%d) [1]=> int(%d) } ["e"]=> array(2) { [0]=> int(%d) [1]=> int(%d) } } } php-profiler-extension-4.0.7/tests/tideways_spans_003.phpt000066400000000000000000000014661301131770100236220ustar00rootroot00000000000000--TEST-- Tideways: Span Annotations --FILE-- 'bar', 'bar' => 'baz')); tideways_disable(); tideways_span_annotate($span, array('baz' => 1)); var_dump(tideways_get_spans()); --EXPECTF-- array(2) { [0]=> array(4) { ["n"]=> string(3) "app" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(1) { [0]=> int(%d) } ["a"]=> array(1) { ["cpu"]=> string(%d) "%d" } } [1]=> array(4) { ["n"]=> string(3) "app" ["b"]=> array(0) { } ["e"]=> array(0) { } ["a"]=> array(3) { ["foo"]=> string(3) "bar" ["bar"]=> string(3) "baz" ["baz"]=> string(1) "1" } } } php-profiler-extension-4.0.7/tests/tideways_spans_004.phpt000066400000000000000000000010451301131770100236140ustar00rootroot00000000000000--TEST-- Tideways: Autodetect file_get_contents() HTTP Spans --FILE-- 50000000)); $pdo = new PDO('sqlite:memory:', 'root', ''); $pdo->exec("CREATE TABLE baz (id INTEGER)"); $pdo->beginTransaction(); $stmt = $pdo->prepare("SELECT 'foo' FROM 'baz'"); $stmt->execute(); $stmt = $pdo->prepare("INSERT INTO baz (id) VALUES (1)"); $stmt->execute(); $stmt = $pdo->prepare("UPDATE baz SET id = 2 WHERE id = 1"); $stmt->execute(); $stmt = $pdo->prepare("DELETE FROM baz"); $stmt->execute(); $stmt = $pdo->query("SELECT count(*) FROM baz"); $stmt->execute(); $pdo->commit(); print_spans(tideways_get_spans()); tideways_disable(); --EXPECTF-- app: 1 timers - sql: 1 timers - db.type=sqlite sql: 1 timers - sql=CREATE TABLE baz (id INTEGER) sql: 1 timers - sql=SELECT 'foo' FROM 'baz' sql: 1 timers - sql=INSERT INTO baz (id) VALUES (1) sql: 1 timers - sql=UPDATE baz SET id = 2 WHERE id = 1 sql: 1 timers - sql=DELETE FROM baz sql: 1 timers - sql=SELECT count(*) FROM baz sql: 1 timers - sql=SELECT count(*) FROM baz sql: 1 timers - title=commit php-profiler-extension-4.0.7/tests/tideways_spans_006.phpt000066400000000000000000000033061301131770100236200ustar00rootroot00000000000000--TEST-- Tideways: Event Spans --FILE-- dispatch('foo', new Event()); $dispatcher->dispatch('bar', new Event()); $manager = new EventManager(); $manager->trigger("baz", new \stdClass, array()); $doctrine = new DoctrineEventManager(); $doctrine->dispatchEvent('event', new Event()); $enlight = new Enlight_Event_EventManager(); $enlight->filter("foo", 1234, new Event()); $enlight->notify("bar", new Event()); $enlight->notifyUntil("baz", new Event()); Mage::dispatchEvent('zoomzoom', array()); do_action("foo", array("foo" => "bar")); drupal_alter("foo", 1, 2, 3, 4); $flow3 = new \TYPO3\Flow\SignalSlot\Dispatcher(); $flow3->dispatch('signal', 'slot'); $cake2 = new CakeEventManager(); $cake2->dispatch('cake2-1'); $cake2->dispatch(new CakeEvent('cake2-2')); $cake3 = new \Cake\Event\EventManager(); $cake3->dispatch('cake3-1'); $cake3->dispatch(new Cake\Event\Event('cake3-2')); $spans = tideways_get_spans(); print_spans($spans); tideways_disable(); --EXPECTF-- app: 1 timers - event: 4 timers - title=foo event: 2 timers - title=bar event: 2 timers - title=baz event: 1 timers - title=event event: 1 timers - title=zoomzoom event: 1 timers - title=signal::slot event: 1 timers - title=cake2-1 event: 1 timers - title=cake2-2 event: 1 timers - title=cake3-1 event: 1 timers - title=cake3-2 php-profiler-extension-4.0.7/tests/tideways_spans_007.phpt000066400000000000000000000017341301131770100236240ustar00rootroot00000000000000--TEST-- Tideways: Spans Twig+Smarty Support --FILE-- display(array('foo' => 'bar')); $smarty3 = new Smarty_Internal_Template(); $smarty3->fetch("bar.tpl"); $smarty3->fetch(NULL); $smarty2 = new Smarty(); $smarty2->fetch("foo.tpl"); $smarty2->fetch(NULL); print_spans(tideways_get_spans()); tideways_disable() ?> --EXPECT-- app: 1 timers - view: 1 timers - title=test.twig view: 1 timers - title=bar.tpl view: 2 timers - title=foo.tpl php-profiler-extension-4.0.7/tests/tideways_spans_008.phpt000066400000000000000000000016501301131770100236220ustar00rootroot00000000000000--TEST-- Tideways: PostgreSQL Spans --SKIPIF-- --FILE-- array(4) { ["n"]=> string(3) "app" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(2) { [0]=> int(%d) [1]=> int(%d) } ["a"]=> array(1) { ["cpu"]=> string(%d) "%d" } } [1]=> array(4) { ["n"]=> string(3) "php" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(1) { [0]=> int(%d) } ["a"]=> array(1) { ["title"]=> string(22) "fastcgi_finish_request" } } } php-profiler-extension-4.0.7/tests/tideways_spans_013.phpt000066400000000000000000000022041301131770100236120ustar00rootroot00000000000000--TEST-- Tideways: SoapClient::__doRequest support --SKIPIF-- WSDL_CACHE_NONE, 'connection_timeout' => 1) ); tideways_enable(0, array('ignored_functions' => array('SoapClient::__call'))); try { $result = $client->checkVat(array( 'countryCode' => 'DE', 'vatNumber' => '272316452', )); } catch (\SoapFault $f) { } $data = tideways_disable(); print_Spans(tideways_get_spans()); --EXPECTF-- app: 1 timers - cpu=%d http: 1 timers - method=POST service=soap url=http://ec.europa.eu/taxation_customs/vies/services/checkVatService php-profiler-extension-4.0.7/tests/tideways_spans_014.phpt000066400000000000000000000007501301131770100236170ustar00rootroot00000000000000--TEST-- Tideways: Memcache Support --SKIPIF-- connect('localhost'); $memcache->set('foo', 'bar'); $memcache->get('foo', 'bar'); tideways_disable(); print_spans(tideways_get_spans()); --EXPECT-- app: 1 timers - memcache: 1 timers - title=Memcache::set memcache: 1 timers - title=Memcache::get php-profiler-extension-4.0.7/tests/tideways_spans_015.phpt000066400000000000000000000010571301131770100236210ustar00rootroot00000000000000--TEST-- Tideways: Watch Dynamic Callback --FILE-- 50000000)); $mysql = new mysqli('127.0.0.1', 'root', '', 'information_schema'); $stmt = $mysql->prepare('SELECT * FROM TABLES LIMIT 1'); $stmt->execute(); print_spans(tideways_get_spans()); tideways_disable(); --EXPECTF-- app: 1 timers - sql: 1 timers - db.name=information_schema db.type=mysql peer.host=127.0.0.1 sql: 1 timers - sql=SELECT * FROM TABLES LIMIT 1 sql: 1 timers - title=execute php-profiler-extension-4.0.7/tests/tideways_spans_017.phpt000066400000000000000000000046201301131770100236220ustar00rootroot00000000000000--TEST-- Tideways: Watch with Callback --FILE-- bar("baz"); echo "Context Dumped From Global State\n"; var_dump($GLOBALS['context']); tideways_disable(); echo "\nSpans\n"; var_dump(tideways_get_spans()); --EXPECTF-- string(14) "inside watcher" array(2) { ["fn"]=> string(3) "foo" ["args"]=> array(2) { [0]=> int(1) [1]=> int(2) } } Inside Closure array(2) { ["fn"]=> string(8) "closured" ["args"]=> array(2) { [0]=> string(3) "foo" [1]=> string(3) "bar" } } With Object array(3) { ["fn"]=> string(8) "Foo::bar" ["args"]=> array(1) { [0]=> string(3) "baz" } ["object"]=> object(Foo)#4 (1) { ["value":"Foo":private]=> string(3) "hi!" } } Context Dumped From Global State array(3) { ["fn"]=> string(8) "Foo::bar" ["args"]=> array(1) { [0]=> string(3) "baz" } ["object"]=> object(Foo)#4 (1) { ["value":"Foo":private]=> string(3) "hi!" } } Spans array(2) { [0]=> array(4) { ["n"]=> string(3) "app" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(1) { [0]=> int(%d) } ["a"]=> array(1) { ["cpu"]=> string(%d) "%d" } } [1]=> array(4) { ["n"]=> string(8) "doctrine" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(1) { [0]=> int(%d) } ["a"]=> array(1) { ["fn"]=> string(%d) "foo" } } } php-profiler-extension-4.0.7/tests/tideways_spans_018.phpt000066400000000000000000000011761301131770100236260ustar00rootroot00000000000000--TEST-- Tideways: Queue Support --FILE-- put(); $queue->putInTube("foo"); $queue->putInTube("bar"); $queue = new \PhpAmqpLib\Channel\AMQPChannel; $queue->basic_publish(array(), 'amqp.foo'); $queue->basic_publish(array(), 'amqp.bar'); tideways_disable(); print_spans(tideways_get_spans()); --EXPECTF-- app: 1 timers - cpu=%d queue: 1 timers - title=default queue: 1 timers - title=foo queue: 1 timers - title=bar queue: 1 timers - title=amqp.foo queue: 1 timers - title=amqp.bar php-profiler-extension-4.0.7/tests/tideways_spans_019.phpt000066400000000000000000000037301301131770100236250ustar00rootroot00000000000000--TEST-- Tideways: MongoDB Support --INI-- extension=mongo.so --SKIPIF-- tidewaystest; $mongo->dropDB('tidewaystest'); } catch (\Exception $e) { die('skip: could not connect to mongo and create tidewaystest db'); } --FILE-- tidewaystest; $tideways->items->find(); $tideways->items->findOne(); for ($i = 0; $i < 10; $i++) { $tideways->items->save(array('x' => 1)); } $cursor = $tideways->items->find(); $cursor->count(); while ($row = $cursor->next()) { } iterator_to_array($cursor); tideways_disable(); print_spans(tideways_get_spans()); --EXPECTF-- app: 1 timers - cpu=%d mongo: 1 timers - collection=items title=MongoCollection::find mongo: 1 timers - collection=items title=MongoCollection::findOne mongo: 1 timers - collection=items title=MongoCollection::save mongo: 1 timers - collection=items title=MongoCollection::save mongo: 1 timers - collection=items title=MongoCollection::save mongo: 1 timers - collection=items title=MongoCollection::save mongo: 1 timers - collection=items title=MongoCollection::save mongo: 1 timers - collection=items title=MongoCollection::save mongo: 1 timers - collection=items title=MongoCollection::save mongo: 1 timers - collection=items title=MongoCollection::save mongo: 1 timers - collection=items title=MongoCollection::save mongo: 1 timers - collection=items title=MongoCollection::save mongo: 1 timers - collection=items title=MongoCollection::find mongo: 1 timers - collection=tidewaystest.items title=MongoCursor::count mongo: 1 timers - collection=tidewaystest.items title=MongoCursor::next mongo: 1 timers - collection=tidewaystest.items title=MongoCursor::rewind php-profiler-extension-4.0.7/tests/tideways_spans_020.phpt000066400000000000000000000006011301131770100236070ustar00rootroot00000000000000--TEST-- Tideways: Predis Support --FILE-- hexists("foo"); $client->hget("foo"); tideways_disable(); print_spans(tideways_get_spans()); --EXPECTF-- app: 1 timers - cpu=%d predis: 1 timers - title=hexists predis: 1 timers - title=hget php-profiler-extension-4.0.7/tests/tideways_spans_021.phpt000066400000000000000000000026321301131770100236160ustar00rootroot00000000000000--TEST-- Tideways: Cross Limit of 1500 spans --FILE-- "before")); for ($i = 0; $i < 2000; $i++) { testing(array("foo" => "$i")); } $id = tideways_span_create("test"); tideways_span_annotate($id, array("test" => "before")); $spans = tideways_get_spans(); echo count($spans) . "\n"; var_dump($spans[1]); var_dump($spans[1499]); var_dump(count($spans[1500]['b'])); var_dump($spans[1500]['a']); var_dump($spans[1501]); --EXPECTF-- 1502 array(4) { ["n"]=> string(4) "test" ["b"]=> array(0) { } ["e"]=> array(0) { } ["a"]=> array(1) { ["test"]=> string(6) "before" } } array(4) { ["n"]=> string(3) "php" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(1) { [0]=> int(%d) } ["a"]=> array(2) { ["foo"]=> string(4) "1497" ["fn"]=> string(7) "testing" } } int(502) array(3) { ["foo"]=> string(4) "1999" ["fn"]=> string(7) "testing" ["trunc"]=> string(1) "1" } array(4) { ["n"]=> string(4) "test" ["b"]=> array(0) { } ["e"]=> array(0) { } ["a"]=> array(1) { ["test"]=> string(6) "before" } } php-profiler-extension-4.0.7/tests/tideways_spans_022.phpt000066400000000000000000000004471301131770100236210ustar00rootroot00000000000000--TEST-- Tideways: Limit Size of String Annotation generated from Internal APIs to 1000 --FILE-- load(); $persister->load(); $persister = new \Doctrine\ORM\Persisters\BasicEntityPersister("Bar"); $persister->load(); $rsm = new \Doctrine\ORM\Query\ResultSetMapping(); $rsm->aliasMap["f"] = "Foo\Bar\Baz"; $query = new \Doctrine\ORM\Query(); $query->setResultSetMapping($rsm); $query->execute(); $query->execute(); $query->getDQL(); $query = new \Doctrine\ORM\NativeQuery(); $query->setResultSetMapping($rsm); $query->execute(); $query->getSQL(); print_spans(tideways_get_spans()); tideways_disable(); --EXPECT-- app: 1 timers - doctrine.load: 2 timers - title=Foo doctrine.load: 1 timers - title=Bar doctrine.query: 1 timers - sql=SELECT f FROM Foo\Bar\Baz title=DQL doctrine.query: 1 timers - sql=SELECT f FROM Foo\Bar\Baz title=DQL doctrine.query: 1 timers - title=Native php-profiler-extension-4.0.7/tests/tideways_spans_024.phpt000066400000000000000000000010601301131770100236130ustar00rootroot00000000000000--TEST-- Tideways: Doctrine CouchDB Support --FILE-- request('GET', '/_foo'); $client = new \Doctrine\CouchDB\HTTP\SocketClient(); $client->request('GET', '/_bar'); print_spans(tideways_get_spans()); --EXPECTF-- string(9) "GET /_foo" string(9) "GET /_bar" app: 1 timers - http: 1 timers - method=GET service=couchdb url=/_foo http: 1 timers - method=GET service=couchdb url=/_bar php-profiler-extension-4.0.7/tests/tideways_spans_025.phpt000066400000000000000000000003041301131770100236140ustar00rootroot00000000000000--TEST-- Tideways: Span Callback no Leaks --FILE-- run(); var_dump(tideways_get_spans()); --EXPECTF-- array(2) { [0]=> array(3) { ["n"]=> string(3) "app" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(0) { } } [1]=> array(4) { ["n"]=> string(8) "php.ctrl" ["b"]=> array(1) { [0]=> int(%d) } ["e"]=> array(1) { [0]=> int(%d) } ["a"]=> array(2) { ["title"]=> string(14) "AuthController" ["fn"]=> string(19) "ControllerCore::run" } } } php-profiler-extension-4.0.7/tests/tideways_spans_027.phpt000066400000000000000000000022071301131770100236220ustar00rootroot00000000000000--TEST-- Tideways: Laravel Eloquent ORM Support --FILE-- model = $model; } public function getModel() { return $this->model; } public function getModels() { } } class User extends Model { } tideways_enable(); $builder = new Builder($user = new User()); $builder->getModels(); $user->performUpdate(); $user->performInsert(); $user->delete(); $builder = new Builder("not an object"); $builder->getModels(); tideways_disable(); print_spans(tideways_get_spans()); --EXPECTF-- app: 1 timers - cpu=%d eloquent: 1 timers - model=Illuminate\Database\Eloquent\User op=get eloquent: 1 timers - model=Illuminate\Database\Eloquent\User op=performUpdate eloquent: 1 timers - model=Illuminate\Database\Eloquent\User op=performInsert eloquent: 1 timers - model=Illuminate\Database\Eloquent\User op=delete php-profiler-extension-4.0.7/tests/tideways_spans_028.phpt000066400000000000000000000042401301131770100236220ustar00rootroot00000000000000--TEST-- Tideways: MongoDB --INI-- extension=mongodb.so --SKIPIF-- insert(['_id' => 1, 'x' => 1]); $bulk->insert(['_id' => 2, 'x' => 2]); $bulk->update(['x' => 2], ['$set' => ['x' => 1]], ['multi' => false, 'upsert' => false]); $bulk->update(['x' => 3], ['$set' => ['x' => 3]], ['multi' => false, 'upsert' => true]); $bulk->update(['_id' => 3], ['$set' => ['x' => 3]], ['multi' => false, 'upsert' => true]); $bulk->insert(['_id' => 4, 'x' => 2]); $bulk->delete(['x' => 1], ['limit' => 1]); $manager = new MongoDB\Driver\Manager('mongodb://' . $host); $command = new MongoDB\Driver\Command(array("drop" => 'collection')); $manager->executeCommand('db', $command); $writeConcern = new MongoDB\Driver\WriteConcern(MongoDB\Driver\WriteConcern::MAJORITY, 100); $result = $manager->executeBulkWrite('db.collection', $bulk, $writeConcern); $bulk = new MongoDB\Driver\BulkWrite; for ($i = 100; $i < 1000; $i++) { $bulk->insert(['x' => $i]); } $manager->executeBulkWrite('db.collection', $bulk); $filter = ['x' => ['$gt' => 1]]; $options = [ 'projection' => ['_id' => 0], 'sort' => ['x' => -1], ]; function iterate($cursor) { $count = 0; foreach ($cursor as $document) { $count++; } } $query = new MongoDB\Driver\Query($filter, $options); $cursor = $manager->executeQuery('db.collection', $query); iterate($cursor); tideways_disable(); print_spans(tideways_get_spans()); --EXPECTF-- app: 1 timers - cpu=%d mongodb: 1 timers - host=172.17.0.2 op=connect port=27017 mongodb: 1 timers - ns=db op=executeCommand mongodb: 1 timers - ns=db.collection op=executeBulkWrite mongodb: 1 timers - ns=db.collection op=executeBulkWrite mongodb: 1 timers - ns=db.collection op=executeQuery php-profiler-extension-4.0.7/tests/tideways_spans_029.phpt000066400000000000000000000024511301131770100236250ustar00rootroot00000000000000--TEST-- Tideways: curl_multi_exec / Async Handling --SKIPIF-- 0); while ($done = curl_multi_info_read($mh)) { $id = (int) $done['handle']; curl_multi_remove_handle($mh, $done['handle']); } curl_multi_close($mh); tideways_disable(); $spans = tideways_get_spans(); print_spans($spans); --EXPECTF-- app: 1 timers - cpu=%d http: 1 timers - http.status_code=%d net.in=%d net.out=%d peer.ipv4=%s peer.port=%d url=%s http: 1 timers - http.status_code=%d net.in=%d net.out=%d peer.ipv4=%s peer.port=%d url=%s php-profiler-extension-4.0.7/tests/tideways_spans_030.phpt000066400000000000000000000016351301131770100236200ustar00rootroot00000000000000--TEST-- Tideways: Elasticsearch PHP Client --FILE-- performRequest('GET', '/idx1/type2/_search'); $endpoint = new \Elasticsearch\Endpoints\SomeEndpoint(); $endpoint->resultOrFuture(array()); $connection = new \Elasticsearch\Connections\Connection(); $connection->performRequest('GET', '/idx3/type1'); $endpoint = new \Elasticsearch\Endpoints\AnotherEndpoint(); $endpoint->resultOrFuture(array()); tideways_disable(); print_spans(tideways_get_spans()); --EXPECTF-- app: 1 timers - cpu=%d elasticsearch: 1 timers - endpoint=Elasticsearch\Endpoints\SomeEndpoint es.method=GET es.path=/idx1/type2/_search elasticsearch: 1 timers - endpoint=Elasticsearch\Endpoints\AnotherEndpoint es.method=GET es.path=/idx3/type1 php-profiler-extension-4.0.7/tests/tideways_spans_031.phpt000066400000000000000000000011571301131770100236200ustar00rootroot00000000000000--TEST-- Tideways: PDO MySQL Connect --SKIPIF-- query('SELECT 1'); tideways_disable(); print_spans(tideways_get_spans()); --EXPECTF-- app: 1 timers - cpu=%d sql: 1 timers - db.name=mysql db.type=mysql peer.host=127.0.0.1 peer.port=3306 sql: 1 timers - sql=SELECT 1 php-profiler-extension-4.0.7/tests/tideways_spans_032.phpt000066400000000000000000000011551301131770100236170ustar00rootroot00000000000000--TEST-- Tideways: Mysql support --SKIPIF-- 'select foo', 'UPDATE foo SET bar=baz' => 'update foo', 'INSERT INTO bar (..) VALUES (...)' => 'insert bar', 'DELETE FROM baz WHERE bar = 1' => 'delete baz', 'COMMIT' => 'commit', 'DROP TABLE' => 'other', ); $i = 0; foreach ($queries as $sql => $expectedSummary) { $actualSummary = tideways_sql_minify($sql); if ($actualSummary === '') { echo ++$i . ") OK\n"; } else { echo ++$i . ") FAIL got '" . $actualSummary . "' but expected empty string.\n"; } } --EXPECTF-- 1) OK 2) OK 3) OK 4) OK 5) OK 6) OK php-profiler-extension-4.0.7/tests/tideways_symfony.php000066400000000000000000000016441301131770100234320ustar00rootroot00000000000000getArguments(null, $controller); call_user_func_array($controller, $args); } } } namespace Symfony\Component\HttpKernel\Controller { class ControllerResolver { public function getArguments($request, $controller) { return array(); } } } namespace Acme\DemoBundle\Controller { class DefaultController { public function indexAction() { } public function helloAction() { } } } php-profiler-extension-4.0.7/tests/tideways_tranaction_cakephp2.phpt000066400000000000000000000013701301131770100260250ustar00rootroot00000000000000--TEST-- Tideways: CakePHP 2 Transaction Detection --FILE-- 'Controller::invokeAction')); $request = new CakeRequest(); $ctrl = new UserController(); $ctrl->invokeAction(null); @$ctrl::invokeAction($request); $ctrl->invokeAction($request); $request->params['action'] = 'foo'; $ctrl->invokeAction($request); echo tideways_transaction_name() . "\n"; print_spans(tideways_get_spans()); --EXPECTF-- UserController::foo app: 1 timers - php.ctrl: 1 timers - title=UserController::foo php-profiler-extension-4.0.7/tests/tideways_tranaction_cakephp3.phpt000066400000000000000000000015641301131770100260330ustar00rootroot00000000000000--TEST-- Tideways: CakePHP 3 Transaction Detection --FILE-- 'Cake\\Controller\\Controller::invokeAction' )); $request = new Request(); $ctrl = new UserController(); $ctrl->invokeAction(); @$ctrl::invokeAction(); $request->params['action'] = 'foo'; $ctrl->request = $request; $ctrl->invokeAction(); echo tideways_transaction_name() . "\n"; print_spans(tideways_get_spans()); --EXPECTF-- Cake\Controller\UserController::foo app: 1 timers - php.ctrl: 1 timers - title=Cake\Controller\UserController::foo php-profiler-extension-4.0.7/tests/tideways_transaction_yii.phpt000066400000000000000000000011351301131770100253040ustar00rootroot00000000000000--TEST-- Tideways: yii Framework Detection --FILE-- 'CController::run')); $ctrl = new UserController(); $ctrl->run('view'); echo 'view: ' . tideways_transaction_name() . "\n"; tideways_disable(); tideways_enable(0, array('transaction_function' => 'CController::run')); $ctrl = new UserController(); $ctrl->run(NULL); echo 'empty: ' . tideways_transaction_name() . "\n"; --EXPECTF-- view: UserController::view empty: php-profiler-extension-4.0.7/tests/tideways_transaction_yii2.phpt000066400000000000000000000012551301131770100253710ustar00rootroot00000000000000--TEST-- Tideways: yii2 Transaction Detection --FILE-- 'yii\base\Module::runAction')); $ctrl = new UserController(); $ctrl->runAction('view'); echo 'view: ' . tideways_transaction_name() . "\n"; tideways_disable(); tideways_enable(0, array('transaction_function' => 'yii\base\Module::runAction')); $ctrl = new UserController(); $ctrl->runAction(NULL); echo 'empty: ' . tideways_transaction_name() . "\n"; --EXPECTF-- view: yii\base\UserController::view empty: php-profiler-extension-4.0.7/tideways.c000066400000000000000000003752331301131770100201470ustar00rootroot00000000000000/* * Copyright (c) 2009 Facebook * Copyright (c) 2014-2016 Qafoo GmbH * Copyright (c) 2016 Tideways GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #if __APPLE__ #include #include #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "ext/standard/file.h" #include "php_tideways.h" #include "spans.h" #include "zend_extensions.h" #include "zend_exceptions.h" #include "zend_builtin_functions.h" #include "zend_gc.h" #include "ext/standard/url.h" #include "ext/pdo/php_pdo_driver.h" #include "ext/pcre/php_pcre.h" #include "zend_stream.h" #if PHP_VERSION_ID < 70000 static inline void **hp_get_execute_arguments(zend_execute_data *data) { void **p; p = data->function_state.arguments; #if PHP_VERSION_ID >= 50500 /* * With PHP 5.5 zend_execute cannot be overwritten by extensions anymore. * instead zend_execute_ex has to be used. That however does not have * function_state.arguments populated for non-internal functions. * As per UPGRADING.INTERNALS we are accessing prev_execute_data which * has this information (for whatever reasons). */ if (p == NULL) { p = (*data).prev_execute_data->function_state.arguments; } #endif return p; } static inline int hp_num_execute_arguments(zend_execute_data *data) { void **p = hp_get_execute_arguments(data); return (int)(zend_uintptr_t) *p; } static inline zval *hp_get_execute_argument(zend_execute_data *data, int n) { void **p = hp_get_execute_arguments(data); int arg_count = (int)(zend_uintptr_t) *p; return *(p-(arg_count-n)); } static zend_always_inline zend_string *zend_string_alloc(int len, int persistent) { /* single alloc, so free the buf, will also free the struct */ char *buf = safe_pemalloc(sizeof(zend_string)+len+1,1,0,persistent); zend_string *str = (zend_string *)(buf+len+1); str->val = buf; str->len = len; str->persistent = persistent; return str; } static zend_always_inline zend_string *zend_string_init(const char *str, size_t len, int persistent) { zend_string *ret = zend_string_alloc(len, persistent); memcpy(ret->val, str, len); ret->val[len] = '\0'; return ret; } static zend_always_inline void zend_string_free(zend_string *s) { if (s == NULL) { return; } pefree(s->val, s->persistent); } static zend_always_inline void zend_string_release(zend_string *s) { if (s == NULL) { return; } pefree(s->val, s->persistent); } /* new macros */ #define RETURN_STR_COPY(s) RETURN_STRINGL(estrdup(s->val), s->len, 0) #define Z_STR_P(z) zend_string_init(Z_STRVAL_P(z), Z_STRLEN_P(z), 0) #define ZSTR_VAL(zstr) (zstr)->val #define ZSTR_LEN(zstr) (zstr)->len #define ZEND_CALL_NUM_ARGS(call) hp_num_execute_arguments(call) #define ZEND_CALL_ARG(call, n) hp_get_execute_argument(call, n-1) #define EX_OBJ(call) call->object #define Z_TRY_ADDREF_P(zv) Z_ADDREF_P(zv); #define _ZCE_NAME(ce) ce->name #define _ZCE_NAME_LENGTH(ce) ce->name_length #define _ZVAL_STRING(str, len) ZVAL_STRING(str, len, 0) #define _RETURN_STRING(str) RETURN_STRING(str, 0) #define _add_assoc_string_ex(arg, key, key_len, str, copy) add_assoc_string_ex(arg, key, key_len, str, copy) #define _add_assoc_stringl(arg, key, str, str_len, copy) add_assoc_stringl(arg, key, str, str_len, copy) #define _zend_read_property(scope, object, name, name_length, silent, zv) zend_read_property(scope, object, name, name_length, silent TSRMLS_CC) #define tw_call_user_function_ex(function_table, object, function_name, retval_ptr) call_user_function_ex(function_table, &object, function_name, &retval_ptr, 0, NULL, 1, NULL TSRMLS_CC) #define _DECLARE_ZVAL(name) zval * name #define _ALLOC_INIT_ZVAL(name) ALLOC_INIT_ZVAL(name) #define hp_ptr_dtor(val) zval_ptr_dtor( &val ) #define zend_string_copy(s) s #define zend_hash_str_update(array, key, len, value) zend_hash_update(array, key, len+1, value, sizeof(zval*), NULL) #define TWG_ARRVAL(val) Z_ARRVAL_P(val) #define tw_resource_handle(zv) Z_LVAL_P(zv) #define tw_pcre_match_impl(pce, zv, retval, parts, global, use_flags, flags, offset) php_pcre_match_impl(pce, Z_STRVAL_P(subject), Z_STRLEN_P(subject), return_value, parts, global, use_flags, flags, offset TSRMLS_CC) #define register_trace_callback(function_name, cb) zend_hash_update(TWG(trace_callbacks), function_name, sizeof(function_name), &cb, sizeof(tw_trace_callback*), NULL); #define register_trace_callback_len(function_name, len, cb) zend_hash_update(TWG(trace_callbacks), function_name, len+1, &cb, sizeof(tw_trace_callback*), NULL); #else #define EX_OBJ(call) ((call->This.value.obj) ? &(call->This) : NULL) #define _ZCE_NAME(ce) ce->name->val #define _ZCE_NAME_LENGTH(ce) ce->name->len #define _ZVAL_STRING(str, len) ZVAL_STRING(str, len) #define _RETURN_STRING(str) RETURN_STRING(str) #define _add_assoc_string_ex(arg, key, key_len, str, copy) add_assoc_string_ex(arg, key, key_len-1, str) #define _add_assoc_stringl(arg, key, str, str_len, copy) add_assoc_stringl(arg, key, str, str_len) #define _zend_read_property(scope, object, name, name_length, silent, zv) zend_read_property(scope, object, name, name_length, silent, zv) #define tw_call_user_function_ex(function_table, object, function_name, retval_ptr) call_user_function_ex(function_table, object, function_name, retval_ptr, 0, NULL, 1, NULL) #define _DECLARE_ZVAL(name) zval name## _v; zval * name = &name## _v #define _ALLOC_INIT_ZVAL(name) ZVAL_NULL(name) #define hp_ptr_dtor(val) zval_ptr_dtor(val) #define TWG_ARRVAL(val) Z_ARRVAL(val) #define tw_resource_handle(zv) Z_RES_P(zv)->handle #define tw_pcre_match_impl(pce, zv, retval, parts, global, use_flags, flags, offset) php_pcre_match_impl(pce, Z_STRVAL_P(subject), Z_STRLEN_P(subject), return_value, parts, global, use_flags, flags, offset) #define register_trace_callback(function_name, cb) zend_hash_str_update_mem(TWG(trace_callbacks), function_name, strlen(function_name), &cb, sizeof(tw_trace_callback)); #define register_trace_callback_len(function_name, len, cb) zend_hash_str_update_mem(TWG(trace_callbacks), function_name, len, &cb, sizeof(tw_trace_callback)); typedef size_t strsize_t; /* removed/uneeded macros */ #define TSRMLS_CC #endif static zend_always_inline zval* zend_compat_hash_find_const(HashTable *ht, const char *key, strsize_t len) { #if PHP_VERSION_ID < 70000 zval **tmp, *result; if (zend_hash_find(ht, key, len+1, (void**)&tmp) == SUCCESS) { result = *tmp; return result; } return NULL; #else return zend_hash_str_find(ht, key, len); #endif } static zend_always_inline zval* zend_compat_hash_index_find(HashTable *ht, zend_ulong idx) { #if PHP_VERSION_ID < 70000 zval **tmp, *result; if (zend_hash_index_find(ht, idx, (void **) &tmp) == FAILURE) { return NULL; } result = *tmp; return result; #else return zend_hash_index_find(ht, idx); #endif } static zend_always_inline zval *zend_compat_hash_get_current_data_ex(HashTable *ht, HashPosition *pos) { #if PHP_VERSION_ID < 70000 zval **tmp; if (zend_hash_get_current_data_ex(ht, (void **)&tmp, pos) == SUCCESS) { return *tmp; } else { return NULL; } #else return zend_hash_get_current_data_ex(ht, pos); #endif } static zend_always_inline long zend_compat_hash_find_long(HashTable *ht, char *key, strsize_t len) { #if PHP_VERSION_ID < 70000 long *idx_ptr = NULL; if (zend_hash_find(ht, key, len+1, (void **)&idx_ptr) == SUCCESS) { return *idx_ptr; } #else long idx; zval *zv; if (zv = zend_hash_str_find(ht, key, len)) { return Z_LVAL_P(zv); } #endif return -1; } static zend_always_inline void zend_compat_hash_update_long(HashTable *ht, char *key, strsize_t len, long number) { #if PHP_VERSION_ID < 70000 zend_hash_update(ht, key, len+1, &number, sizeof(long), NULL); #else zval zv; ZVAL_LONG(&zv, number); zend_hash_str_update(ht, key, len, &zv); #endif } typedef long (*tw_trace_callback)(char *symbol, zend_execute_data *data TSRMLS_DC); #if PHP_VERSION_ID >= 70000 static void (*_zend_execute_ex) (zend_execute_data *execute_data); static void (*_zend_execute_internal) (zend_execute_data *execute_data, zval *return_value); #elif PHP_VERSION_ID < 50500 static void (*_zend_execute) (zend_op_array *ops TSRMLS_DC); static void (*_zend_execute_internal) (zend_execute_data *data, int ret TSRMLS_DC); #else static void (*_zend_execute_ex) (zend_execute_data *execute_data TSRMLS_DC); static void (*_zend_execute_internal) (zend_execute_data *data, struct _zend_fcall_info *fci, int ret TSRMLS_DC); #endif /* Pointer to the original compile function */ static zend_op_array * (*_zend_compile_file) (zend_file_handle *file_handle, int type TSRMLS_DC); /* Pointer to the original compile string function (used by eval) */ static zend_op_array * (*_zend_compile_string) (zval *source_string, char *filename TSRMLS_DC); ZEND_DLEXPORT zend_op_array* hp_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC); ZEND_DLEXPORT zend_op_array* hp_compile_string(zval *source_string, char *filename TSRMLS_DC); #if PHP_MAJOR_VERSION == 7 ZEND_DLEXPORT void hp_execute_ex (zend_execute_data *execute_data); #elif PHP_VERSION_ID < 50500 ZEND_DLEXPORT void hp_execute (zend_op_array *ops TSRMLS_DC); #else ZEND_DLEXPORT void hp_execute_ex (zend_execute_data *execute_data TSRMLS_DC); #endif #if PHP_MAJOR_VERSION == 7 ZEND_DLEXPORT void hp_execute_internal(zend_execute_data *execute_data, zval *return_value); #elif PHP_VERSION_ID < 50500 ZEND_DLEXPORT void hp_execute_internal(zend_execute_data *execute_data, int ret TSRMLS_DC); #else ZEND_DLEXPORT void hp_execute_internal(zend_execute_data *execute_data, struct _zend_fcall_info *fci, int ret TSRMLS_DC); #endif /* error callback replacement functions */ #if PHP_VERSION_ID < 70000 void (*tideways_original_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); void tideways_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); #else static void tideways_throw_exception_hook(zval *exception TSRMLS_DC); #endif #if PHP_VERSION_ID >= 70000 int (*tw_original_gc_collect_cycles)(void); int tw_gc_collect_cycles(void); #endif /* Bloom filter for function names to be ignored */ #define INDEX_2_BYTE(index) (index >> 3) #define INDEX_2_BIT(index) (1 << (index & 0x7)); /** * **************************** * STATIC FUNCTION DECLARATIONS * **************************** */ static void hp_register_constants(INIT_FUNC_ARGS); static void hp_begin(long tideways_flags TSRMLS_DC); static void hp_stop(TSRMLS_D); static void hp_end(TSRMLS_D); static uint64 cycle_timer(); static void hp_free_the_free_list(TSRMLS_D); static hp_entry_t *hp_fast_alloc_hprof_entry(TSRMLS_D); static void hp_fast_free_hprof_entry(hp_entry_t *p TSRMLS_DC); static inline uint8 hp_inline_hash(char * str); static double get_timebase_factor(); static long get_us_interval(struct timeval *start, struct timeval *end); static inline double get_us_from_tsc(uint64 count TSRMLS_DC); static void hp_parse_options_from_arg(zval *args TSRMLS_DC); static void hp_clean_profiler_options_state(TSRMLS_D); static void hp_exception_function_clear(TSRMLS_D); static void hp_transaction_function_clear(TSRMLS_D); static void hp_transaction_name_clear(TSRMLS_D); static inline zval *hp_zval_at_key(char *key, size_t size, zval *values); static inline char **hp_strings_in_zval(zval *values); static inline void hp_array_del(char **name_array); static char *hp_get_file_summary(char *filename, int filename_len TSRMLS_DC); static char *hp_get_base_filename(char *filename); static inline hp_function_map *hp_function_map_create(char **names); static inline void hp_function_map_clear(hp_function_map *map); static inline int hp_function_map_exists(hp_function_map *map, uint8 hash_code, char *curr_func); static inline int hp_function_map_filter_collision(hp_function_map *map, uint8 hash); zend_string *tw_pcre_match(char *pattern, strsize_t len, zval *subject TSRMLS_DC); /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo_tideways_enable, 0, 0, 0) ZEND_ARG_INFO(0, flags) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_tideways_disable, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_tideways_transaction_name, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_tideways_prepend_overwritten, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_tideways_fatal_backtrace, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_tideways_last_detected_exception, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_tideways_last_fatal_error, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_tideways_sql_minify, 0, 0, 0) ZEND_ARG_INFO(0, sql) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_tideways_span_create, 0, 0, 0) ZEND_ARG_INFO(0, category) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_tideways_get_spans, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_tideways_span_timer_start, 0, 0, 0) ZEND_ARG_INFO(0, span) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_tideways_span_timer_stop, 0, 0, 0) ZEND_ARG_INFO(0, span) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_tideways_span_annotate, 0, 0, 0) ZEND_ARG_INFO(0, span) ZEND_ARG_INFO(0, annotations) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_tideways_span_watch, 0, 0, 0) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, category) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_tideways_span_callback, 0, 0, 0) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO() /* }}} */ /** * ********************* * PHP EXTENSION GLOBALS * ********************* */ /* List of functions implemented/exposed by Tideways */ zend_function_entry tideways_functions[] = { PHP_FE(tideways_enable, arginfo_tideways_enable) PHP_FE(tideways_disable, arginfo_tideways_disable) PHP_FE(tideways_transaction_name, arginfo_tideways_transaction_name) PHP_FE(tideways_prepend_overwritten, arginfo_tideways_prepend_overwritten) PHP_FE(tideways_fatal_backtrace, arginfo_tideways_fatal_backtrace) PHP_FE(tideways_last_detected_exception, arginfo_tideways_last_detected_exception) PHP_FE(tideways_last_fatal_error, arginfo_tideways_last_fatal_error) PHP_FE(tideways_sql_minify, arginfo_tideways_sql_minify) PHP_FE(tideways_span_create, arginfo_tideways_span_create) PHP_FE(tideways_get_spans, arginfo_tideways_get_spans) PHP_FE(tideways_span_timer_start, arginfo_tideways_span_timer_start) PHP_FE(tideways_span_timer_stop, arginfo_tideways_span_timer_stop) PHP_FE(tideways_span_annotate, arginfo_tideways_span_annotate) PHP_FE(tideways_span_watch, arginfo_tideways_span_watch) PHP_FE(tideways_span_callback, arginfo_tideways_span_callback) {NULL, NULL, NULL} }; ZEND_DECLARE_MODULE_GLOBALS(hp) /* Callback functions for the Tideways extension */ zend_module_entry tideways_module_entry = { STANDARD_MODULE_HEADER, "tideways", /* Name of the extension */ tideways_functions, /* List of functions exposed */ PHP_MINIT(tideways), /* Module init callback */ PHP_MSHUTDOWN(tideways), /* Module shutdown callback */ PHP_RINIT(tideways), /* Request init callback */ PHP_RSHUTDOWN(tideways), /* Request shutdown callback */ PHP_MINFO(tideways), /* Module info callback */ TIDEWAYS_VERSION, PHP_MODULE_GLOBALS(hp), /* globals descriptor */ PHP_GINIT(hp), /* globals ctor */ PHP_GSHUTDOWN(hp), /* globals dtor */ NULL, /* post deactivate */ STANDARD_MODULE_PROPERTIES_EX }; PHP_INI_BEGIN() /** * INI-Settings are always used by the extension, but by the PHP library. */ PHP_INI_ENTRY("tideways.connection", "unix:///var/run/tideways/tidewaysd.sock", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.udp_connection", "127.0.0.1:8135", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.auto_start", "1", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.api_key", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.framework", "", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.sample_rate", "30", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.auto_prepend_library", "1", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.collect", "tracing", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.monitor", "basic", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.distributed_tracing_hosts", "127.0.0.1", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.log_level", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.max_spans", "1500", PHP_INI_ALL, NULL) PHP_INI_ENTRY("tideways.timeout", "10000", PHP_INI_ALL, NULL) PHP_INI_END() /* Init module */ ZEND_GET_MODULE(tideways) PHP_GINIT_FUNCTION(hp) { hp_globals->enabled = 0; hp_globals->ever_enabled = 0; hp_globals->tideways_flags = 0; hp_globals->transaction_function = NULL; hp_globals->transaction_name = NULL; hp_globals->exception_function = NULL; hp_globals->trace_callbacks = NULL; #if PHP_VERSION_ID < 70000 hp_globals->stats_count = NULL; hp_globals->spans = NULL; hp_globals->exception = NULL; #else ZVAL_UNDEF(&hp_globals->stats_count); ZVAL_UNDEF(&hp_globals->spans); ZVAL_UNDEF(&hp_globals->exception); #endif hp_globals->backtrace = NULL; hp_globals->filtered_functions = NULL; hp_globals->entries = NULL; hp_globals->root = NULL; hp_globals->trace_watch_callbacks = NULL; hp_globals->trace_callbacks = NULL; hp_globals->span_cache = NULL; hp_globals->max_spans = 1500; } PHP_GSHUTDOWN_FUNCTION(hp) { } /** * Module init callback. * * @author cjiang */ PHP_MINIT_FUNCTION(tideways) { int i; REGISTER_INI_ENTRIES(); hp_register_constants(INIT_FUNC_ARGS_PASSTHRU); /* Get the number of available logical CPUs. */ TWG(timebase_factor) = get_timebase_factor(); #if PHP_VERSION_ID < 70000 TWG(stats_count) = NULL; TWG(spans) = NULL; #endif TWG(trace_callbacks) = NULL; TWG(trace_watch_callbacks) = NULL; TWG(span_cache) = NULL; /* no free hp_entry_t structures to start with */ TWG(entry_free_list) = NULL; for (i = 0; i < 256; i++) { TWG(func_hash_counters)[i] = 0; } hp_transaction_function_clear(TSRMLS_C); hp_exception_function_clear(TSRMLS_C); _zend_compile_file = zend_compile_file; zend_compile_file = hp_compile_file; _zend_compile_string = zend_compile_string; zend_compile_string = hp_compile_string; #if PHP_VERSION_ID < 50500 _zend_execute = zend_execute; zend_execute = hp_execute; #else _zend_execute_ex = zend_execute_ex; zend_execute_ex = hp_execute_ex; #endif #if PHP_VERSION_ID < 70000 tideways_original_error_cb = zend_error_cb; zend_error_cb = tideways_error_cb; #else zend_throw_exception_hook = tideways_throw_exception_hook; #endif #if PHP_VERSION_ID >= 70000 tw_original_gc_collect_cycles = gc_collect_cycles; gc_collect_cycles = tw_gc_collect_cycles; #endif _zend_execute_internal = zend_execute_internal; zend_execute_internal = hp_execute_internal; #if defined(DEBUG) /* To make it random number generator repeatable to ease testing. */ srand(0); #endif return SUCCESS; } /** * Module shutdown callback. */ PHP_MSHUTDOWN_FUNCTION(tideways) { /* free any remaining items in the free list */ hp_free_the_free_list(TSRMLS_C); /* Remove proxies, restore the originals */ #if PHP_VERSION_ID < 50500 zend_execute = _zend_execute; #else zend_execute_ex = _zend_execute_ex; #endif zend_execute_internal = _zend_execute_internal; zend_compile_file = _zend_compile_file; zend_compile_string = _zend_compile_string; #if PHP_VERSION_ID < 70000 zend_error_cb = tideways_original_error_cb; #else zend_throw_exception_hook = NULL; #endif #if PHP_VERSION_ID >= 70000 gc_collect_cycles = tw_original_gc_collect_cycles; #endif UNREGISTER_INI_ENTRIES(); return SUCCESS; } long tw_trace_callback_record_with_cache(char *category, int category_len, char *summary, strsize_t summary_len, int copy TSRMLS_DC) { long idx; idx = zend_compat_hash_find_long(TWG(span_cache), summary, summary_len); if (idx == -1) { idx = tw_span_create(category, category_len TSRMLS_CC); zend_compat_hash_update_long(TWG(span_cache), summary, summary_len, idx); } tw_span_annotate_string(idx, "title", summary, copy TSRMLS_CC); #if PHP_VERSION_ID >= 70000 if (copy == 0) { efree(summary); } #endif return idx; } void tw_span_timer_start(long spanId TSRMLS_DC) { zval *span, *starts; double wt; if (spanId == -1) { return; } span = zend_compat_hash_index_find(TWG_ARRVAL(TWG(spans)), spanId); if (span == NULL) { return; } starts = zend_compat_hash_find_const(Z_ARRVAL_P(span), "b", 1); if (starts == NULL) { return; } wt = get_us_from_tsc(cycle_timer() - TWG(start_time) TSRMLS_CC); add_next_index_long(starts, wt); } void tw_span_timer_stop(long spanId TSRMLS_DC) { zval *span, *stops; double wt; if (spanId == -1) { return; } span = zend_compat_hash_index_find(TWG_ARRVAL(TWG(spans)), spanId); if (span == NULL) { return; } stops = zend_compat_hash_find_const(Z_ARRVAL_P(span), "e", 1); if (stops == NULL) { return; } wt = get_us_from_tsc(cycle_timer() - TWG(start_time) TSRMLS_CC); add_next_index_long(stops, wt); } void tw_span_record_duration(long spanId, double start, double end TSRMLS_DC) { zval *span, *timer; if (spanId == -1) { return; } span = zend_compat_hash_index_find(TWG_ARRVAL(TWG(spans)), spanId); if (span == NULL) { return; } timer = zend_compat_hash_find_const(Z_ARRVAL_P(span), "b", 1); if (timer == NULL) { return; } add_next_index_long(timer, start); timer = zend_compat_hash_find_const(Z_ARRVAL_P(span), "e", 1); if (timer == NULL) { return; } add_next_index_long(timer, end); } long tw_trace_callback_php_call(char *symbol, zend_execute_data *data TSRMLS_DC) { return tw_trace_callback_record_with_cache("php", 3, symbol, strlen(symbol), 1 TSRMLS_CC); } long tw_trace_callback_watch(char *symbol, zend_execute_data *data TSRMLS_DC) { tw_watch_callback **temp; tw_watch_callback *twcb = NULL; zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcic = empty_fcall_info_cache; int args_len = ZEND_CALL_NUM_ARGS(data); zval *object = EX_OBJ(data); if (TWG(trace_watch_callbacks) == NULL) { return -1; } #if PHP_VERSION_ID < 70000 if (zend_hash_find(TWG(trace_watch_callbacks), symbol, strlen(symbol)+1, (void **)&temp) == SUCCESS) { twcb = *temp; #else twcb = zend_hash_str_find_ptr(TWG(trace_watch_callbacks), symbol, strlen(symbol)); if (twcb) { #endif _DECLARE_ZVAL(retval); _DECLARE_ZVAL(context); _DECLARE_ZVAL(zargs); #if PHP_VERSION_ID < 70000 zval *params[1]; #else zval params[1]; #endif zend_error_handling zeh; int i; _ALLOC_INIT_ZVAL(context); array_init(context); _ALLOC_INIT_ZVAL(zargs); array_init(zargs); Z_ADDREF_P(zargs); _add_assoc_string_ex(context, "fn", sizeof("fn"), symbol, 1); if (args_len > 0) { for (i = 0; i < args_len; i++) { Z_TRY_ADDREF_P(ZEND_CALL_ARG(data, i+1)); add_next_index_zval(zargs, ZEND_CALL_ARG(data, i+1)); } } add_assoc_zval(context, "args", zargs); if (object != NULL) { Z_TRY_ADDREF_P(object); add_assoc_zval(context, "object", object); } #if PHP_VERSION_ID < 70000 params[0] = (zval *)&(context); #else ZVAL_COPY(¶ms[0], context); #endif twcb->fci.param_count = 1; twcb->fci.size = sizeof(twcb->fci); #if PHP_VERSION_ID < 70000 twcb->fci.retval_ptr_ptr = &retval; twcb->fci.params = (zval ***)params; #else twcb->fci.retval = retval; twcb->fci.params = params; #endif fci = twcb->fci; fcic = twcb->fcic; if (zend_call_function(&fci, &fcic TSRMLS_CC) == FAILURE) { zend_error(E_ERROR, "Cannot call Trace Watch Callback"); } hp_ptr_dtor(context); hp_ptr_dtor(zargs); #if PHP_VERSION_ID >= 70000 hp_ptr_dtor(¶ms[0]); #endif long idx = -1; if (retval) { if (Z_TYPE_P(retval) == IS_LONG) { idx = Z_LVAL_P(retval); } hp_ptr_dtor(retval); } return idx; } return -1; } long tw_trace_callback_mongodb_connect(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1; zval *server; php_url *url; if (ZEND_CALL_NUM_ARGS(data) < 1) { return idx; } server = ZEND_CALL_ARG(data, 1); if (server == NULL || Z_TYPE_P(server) != IS_STRING) { return idx; } url = php_url_parse_ex(Z_STRVAL_P(server), Z_STRLEN_P(server)); if (url == NULL) { return idx; } idx = tw_span_create("mongodb", 7 TSRMLS_CC); tw_span_annotate_string(idx, "op", "connect", 1 TSRMLS_CC); if (url->host) { tw_span_annotate_string(idx, "host", url->host, 1 TSRMLS_CC); } if (url->port) { tw_span_annotate_long(idx, "port", url->port TSRMLS_CC); } php_url_free(url); return idx; } long tw_trace_callback_mongodb_command(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1; zval *namespace; if (ZEND_CALL_NUM_ARGS(data) < 1) { return idx; } namespace = ZEND_CALL_ARG(data, 1); if (namespace == NULL || Z_TYPE_P(namespace) != IS_STRING) { return idx; } idx = tw_span_create("mongodb", 7 TSRMLS_CC); tw_span_annotate_string(idx, "ns", Z_STRVAL_P(namespace), 1 TSRMLS_CC); #if PHP_VERSION_ID < 70000 tw_span_annotate_string(idx, "op", data->function_state.function->common.function_name, 1 TSRMLS_CC); #else tw_span_annotate_string(idx, "op", ZSTR_VAL(data->func->common.function_name), 1 TSRMLS_CC); #endif return idx; } long tw_trace_callback_mongo_cursor_io(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1; zval *object = EX_OBJ(data); zval fname, *element; _DECLARE_ZVAL(retval_ptr); idx = tw_span_create("mongo", 5 TSRMLS_CC); tw_span_annotate_string(idx, "title", symbol, 1 TSRMLS_CC); _ZVAL_STRING(&fname, "info"); if (SUCCESS == tw_call_user_function_ex(EG(function_table), object, &fname, retval_ptr)) { if (Z_TYPE_P(retval_ptr) == IS_ARRAY) { element = zend_compat_hash_find_const(Z_ARRVAL_P(retval_ptr), "ns", sizeof("ns")); if (element != NULL) { tw_span_annotate_string(idx, "collection", Z_STRVAL_P(element), 1 TSRMLS_CC); } } hp_ptr_dtor(retval_ptr); } #if PHP_VERSION_ID >= 70000 zend_string_release(Z_STR(fname)); #endif return idx; } long tw_trace_callback_mongo_cursor_next(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1; zend_class_entry *cursor_ce; zval *object = EX_OBJ(data); zval *queryRunProperty; zval fname, *element; _DECLARE_ZVAL(retval_ptr); if (object == NULL || Z_TYPE_P(object) != IS_OBJECT) { return idx; } cursor_ce = Z_OBJCE_P(object); zval *__rv; queryRunProperty = _zend_read_property(cursor_ce, object, "_tidewaysQueryRun", sizeof("_tidewaysQueryRun")-1, 1, __rv); if (queryRunProperty != NULL && Z_TYPE_P(queryRunProperty) != IS_NULL) { return idx; } zend_update_property_bool(cursor_ce, object, "_tidewaysQueryRun", sizeof("_tidewaysQueryRun") - 1, 1 TSRMLS_CC); idx = tw_span_create("mongo", 5 TSRMLS_CC); tw_span_annotate_string(idx, "title", symbol, 1 TSRMLS_CC); _ZVAL_STRING(&fname, "info"); if (SUCCESS == tw_call_user_function_ex(EG(function_table), object, &fname, retval_ptr)) { if (Z_TYPE_P(retval_ptr) == IS_ARRAY) { element = zend_compat_hash_find_const(Z_ARRVAL_P(retval_ptr), "ns", sizeof("ns")); if (element != NULL) { tw_span_annotate_string(idx, "collection", Z_STRVAL_P(element), 1 TSRMLS_CC); } } hp_ptr_dtor(retval_ptr); } #if PHP_VERSION_ID >= 70000 zend_string_release(Z_STR(fname)); #endif return idx; } long tw_trace_callback_mongo_collection(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1; zval *object = EX_OBJ(data); zval fname; _DECLARE_ZVAL(retval_ptr); if (object == NULL || Z_TYPE_P(object) != IS_OBJECT) { return idx; } _ZVAL_STRING(&fname, "getName"); idx = tw_span_create("mongo", 5 TSRMLS_CC); tw_span_annotate_string(idx, "title", symbol, 1 TSRMLS_CC); if (SUCCESS == tw_call_user_function_ex(EG(function_table), object, &fname, retval_ptr)) { if (Z_TYPE_P(retval_ptr) == IS_STRING) { tw_span_annotate_string(idx, "collection", Z_STRVAL_P(retval_ptr), 1 TSRMLS_CC); } hp_ptr_dtor(retval_ptr); } #if PHP_VERSION_ID >= 70000 zend_string_release(Z_STR(fname)); #endif return idx; } long tw_trace_callback_predis_call(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *commandId = ZEND_CALL_ARG(data, 1); if (commandId == NULL || Z_TYPE_P(commandId) != IS_STRING) { return -1; } return tw_trace_callback_record_with_cache("predis", 6, Z_STRVAL_P(commandId), Z_STRLEN_P(commandId), 1 TSRMLS_CC); } long tw_trace_callback_phpampqlib(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *exchange; long idx = -1; if (ZEND_CALL_NUM_ARGS(data) < 2) { return idx; } exchange = ZEND_CALL_ARG(data, 2); if (exchange == NULL || Z_TYPE_P(exchange) != IS_STRING) { return idx; } return tw_trace_callback_record_with_cache("queue", 5, Z_STRVAL_P(exchange), Z_STRLEN_P(exchange), 1 TSRMLS_CC); } long tw_trace_callback_pheanstalk(char *symbol, zend_execute_data *data TSRMLS_DC) { zend_class_entry *pheanstalk_ce; zval *object = EX_OBJ(data); zval *property; long idx = -1; if (Z_TYPE_P(object) != IS_OBJECT) { return idx; } pheanstalk_ce = Z_OBJCE_P(object); zval *__rv; property = _zend_read_property(pheanstalk_ce, object, "_using", sizeof("_using") - 1, 1, __rv); if (property != NULL && Z_TYPE_P(property) == IS_STRING) { return tw_trace_callback_record_with_cache("queue", 5, Z_STRVAL_P(property), Z_STRLEN_P(property), 1 TSRMLS_CC); } else { return tw_trace_callback_record_with_cache("queue", 5, "default", 7, 1 TSRMLS_CC); } } long tw_trace_callback_memcache(char *symbol, zend_execute_data *data TSRMLS_DC) { return tw_trace_callback_record_with_cache("memcache", 8, symbol, strlen(symbol), 1 TSRMLS_CC); } long tw_trace_callback_apc(char *symbol, zend_execute_data *data TSRMLS_DC) { return tw_trace_callback_record_with_cache("apc", 3, symbol, strlen(symbol), 1 TSRMLS_CC); } long tw_trace_callback_php_controller(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx; idx = tw_span_create("php.ctrl", 8 TSRMLS_CC); tw_span_annotate_string(idx, "title", symbol, 1 TSRMLS_CC); return idx; } long tw_trace_callback_eloquent_model(char *symbol, zend_execute_data *data TSRMLS_DC) { zend_class_entry *eloquent_ce; zval *object = EX_OBJ(data); long idx = -1; if (object == NULL || Z_TYPE_P(object) != IS_OBJECT) { return idx; } eloquent_ce = Z_OBJCE_P(object); idx = tw_span_create("eloquent", 8 TSRMLS_CC); tw_span_annotate_string(idx, "model", _ZCE_NAME(eloquent_ce), 1 TSRMLS_CC); #if PHP_VERSION_ID < 70000 tw_span_annotate_string(idx, "op", data->function_state.function->common.function_name, 1 TSRMLS_CC); #else tw_span_annotate_string(idx, "op", ZSTR_VAL(data->func->common.function_name), 1 TSRMLS_CC); #endif return idx; } long tw_trace_callback_eloquent_query(char *symbol, zend_execute_data *data TSRMLS_DC) { zend_class_entry *eloquent_ce; zval *object = EX_OBJ(data); long idx = -1; zval fname; _DECLARE_ZVAL(retval_ptr); if (Z_TYPE_P(object) != IS_OBJECT) { return idx; } _ZVAL_STRING(&fname, "getModel"); if (SUCCESS == tw_call_user_function_ex(EG(function_table), object, &fname, retval_ptr)) { if (Z_TYPE_P(retval_ptr) == IS_OBJECT) { eloquent_ce = Z_OBJCE_P(retval_ptr); idx = tw_span_create("eloquent", 8 TSRMLS_CC); tw_span_annotate_string(idx, "model", _ZCE_NAME(eloquent_ce), 1 TSRMLS_CC); tw_span_annotate_string(idx, "op", "get", 1 TSRMLS_CC); } hp_ptr_dtor(retval_ptr); } #if PHP_VERSION_ID >= 70000 zend_string_release(Z_STR(fname)); #endif return idx; } long tw_trace_callback_presta_controller(char *symbol, zend_execute_data *data TSRMLS_DC) { zend_class_entry *controller_ce; zval *object = EX_OBJ(data); zval *property; long idx = -1; if (Z_TYPE_P(object) != IS_OBJECT) { return idx; } controller_ce = Z_OBJCE_P(object); idx = tw_span_create("php.ctrl", 8 TSRMLS_CC); tw_span_annotate_string(idx, "title", _ZCE_NAME(controller_ce), 1 TSRMLS_CC); return idx; } zend_string *tw_extract_cakephp_controller_name(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *object = EX_OBJ(data); zval *property, *actionName, *argument_element; zval *__rv; zend_class_entry *ctrl_ce, *request_ce; zend_string *ctrl_str; int len; char *ctrl; if (object == NULL || Z_TYPE_P(object) != IS_OBJECT) { return NULL; } ctrl_ce = Z_OBJCE_P(object); if (strcmp(symbol, "Cake\\Controller\\Controller::invokeAction") == 0) { // CakePHP 3 has request property on controller argument_element = _zend_read_property(ctrl_ce, object, "request", sizeof("request") -1, 1, __rv); } else { // CakePHP 2 gets request passe as argument if (ZEND_CALL_NUM_ARGS(data) == 0) { return NULL; } argument_element = ZEND_CALL_ARG(data, 1); } if (Z_TYPE_P(argument_element) != IS_OBJECT) { return NULL; } request_ce = Z_OBJCE_P(argument_element); property = _zend_read_property(request_ce, argument_element, "params", sizeof("params") - 1, 1, __rv); if (property == NULL || Z_TYPE_P(property) != IS_ARRAY) { return NULL; } actionName = zend_compat_hash_find_const(Z_ARRVAL_P(property), "action", sizeof("action") - 1); if (actionName == NULL) { return NULL; } len = _ZCE_NAME_LENGTH(ctrl_ce) + Z_STRLEN_P(actionName) + 3; ctrl = (char*)emalloc(len); snprintf(ctrl, len, "%s::%s", _ZCE_NAME(ctrl_ce), Z_STRVAL_P(actionName)); ctrl[len-1] = '\0'; ctrl_str = zend_string_init(ctrl, len - 1, 0); efree(ctrl); return ctrl_str; } long tw_trace_callback_cakephp_controller(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1; zend_string *ctrl; ctrl = tw_extract_cakephp_controller_name(symbol, data TSRMLS_CC); if (ctrl != NULL) { idx = tw_span_create("php.ctrl", 8 TSRMLS_CC); tw_span_annotate_string(idx, "title", ZSTR_VAL(ctrl), 1 TSRMLS_CC); zend_string_release(ctrl); } return idx; } long tw_trace_callback_doctrine_couchdb_request(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *method = ZEND_CALL_ARG(data, 1); zval *path = ZEND_CALL_ARG(data, 2); long idx; if (Z_TYPE_P(method) != IS_STRING || Z_TYPE_P(path) != IS_STRING) { return -1; } idx = tw_span_create("http", 4 TSRMLS_CC); tw_span_annotate_string(idx, "method", Z_STRVAL_P(method), 1 TSRMLS_CC); tw_span_annotate_string(idx, "url", Z_STRVAL_P(path), 1 TSRMLS_CC); tw_span_annotate_string(idx, "service", "couchdb", 1 TSRMLS_CC); return idx; } long tw_trace_callback_view_class(char *symbol, zend_execute_data *data TSRMLS_DC) { zend_class_entry *ce; zval *object = EX_OBJ(data); if (object == NULL || Z_TYPE_P(object) != IS_OBJECT) { return -1; } ce = Z_OBJCE_P(object); return tw_trace_callback_record_with_cache("view", 4, _ZCE_NAME(ce), _ZCE_NAME_LENGTH(ce), 1 TSRMLS_CC); } /* Zend_View_Abstract::render($name); */ long tw_trace_callback_view_engine(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *name = ZEND_CALL_ARG(data, 1); char *view; if (Z_TYPE_P(name) != IS_STRING) { return -1; } view = hp_get_base_filename(Z_STRVAL_P(name)); return tw_trace_callback_record_with_cache("view", 4, view, strlen(view), 1 TSRMLS_CC); } /* Applies to Enlight, Mage and Zend1 */ long tw_trace_callback_zend1_dispatcher_families_tx(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *argument_element = ZEND_CALL_ARG(data, 1); int len; char *ret = NULL; zend_class_entry *ce; long idx; zval *object = EX_OBJ(data); if (object == NULL || Z_TYPE_P(object) != IS_OBJECT) { return -1; } if (Z_TYPE_P(argument_element) != IS_STRING) { return -1; } ce = Z_OBJCE_P(object); len = _ZCE_NAME_LENGTH(ce) + Z_STRLEN_P(argument_element) + 3; ret = (char*)emalloc(len); snprintf(ret, len, "%s::%s", _ZCE_NAME(ce), Z_STRVAL_P(argument_element)); idx = tw_span_create("php.ctrl", 8 TSRMLS_CC); tw_span_annotate_string(idx, "title", ret, 0 TSRMLS_CC); #if PHP_VERSION_ID >= 70000 efree(ret); #endif return idx; } /* oxShopControl::_process($sClass, $sFnc = null); */ long tw_trace_callback_oxid_tx(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *sClass = ZEND_CALL_ARG(data, 1); zval *sFnc = ZEND_CALL_ARG(data, 2); char *ret = NULL; long idx; int len, copy; int args_len = ZEND_CALL_NUM_ARGS(data); if (Z_TYPE_P(sClass) != IS_STRING) { return -1; } if (args_len > 1 && sFnc != NULL && Z_TYPE_P(sFnc) == IS_STRING) { len = Z_STRLEN_P(sClass) + Z_STRLEN_P(sFnc) + 3; ret = (char*)emalloc(len); snprintf(ret, len, "%s::%s", Z_STRVAL_P(sClass), Z_STRVAL_P(sFnc)); copy = 0; } else { ret = Z_STRVAL_P(sClass); copy = 1; } if ((TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_SPANS) > 0) { return -1; } return tw_trace_callback_record_with_cache("php.ctrl", 8, ret, strlen(ret), copy TSRMLS_CC); } /* $resolver->getArguments($request, $controller); */ long tw_trace_callback_symfony_resolve_arguments_tx(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *callback, *controller, *action; char *ret = NULL; int len; tw_trace_callback cb; zend_class_entry *ce; callback = ZEND_CALL_ARG(data, 2); // Only Symfony2 framework for now if (Z_TYPE_P(callback) == IS_ARRAY) { controller = zend_compat_hash_index_find(Z_ARRVAL_P(callback), 0); if (controller == NULL && Z_TYPE_P(controller) != IS_OBJECT) { return -1; } action = zend_compat_hash_index_find(Z_ARRVAL_P(callback), 1); if (action == NULL && Z_TYPE_P(action) != IS_STRING) { return -1; } ce = Z_OBJCE_P(controller); len = _ZCE_NAME_LENGTH(ce) + Z_STRLEN_P(action) + 3; ret = (char*)emalloc(len); snprintf(ret, len, "%s::%s", _ZCE_NAME(ce), Z_STRVAL_P(action)); cb = tw_trace_callback_php_controller; register_trace_callback_len(ret, len-1, cb); efree(ret); } return -1; } long tw_trace_callback_pgsql_execute(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *argument_element; char *summary; int i; int args_len = ZEND_CALL_NUM_ARGS(data); for (i = 0; i < args_len; i++) { argument_element = ZEND_CALL_ARG(data, i+1); if (argument_element && Z_TYPE_P(argument_element) == IS_STRING && Z_STRLEN_P(argument_element) > 0) { // TODO: Introduce SQL statement cache to find the names here again. summary = Z_STRVAL_P(argument_element); return tw_trace_callback_record_with_cache("sql", 3, summary, strlen(summary), 1 TSRMLS_CC); } } return -1; } long tw_trace_callback_pgsql_query(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *argument_element; long idx; int i; int args_len = ZEND_CALL_NUM_ARGS(data); for (i = 0; i < args_len; i++) { argument_element = ZEND_CALL_ARG(data, i+1); if (argument_element && Z_TYPE_P(argument_element) == IS_STRING) { idx = tw_span_create("sql", 3 TSRMLS_CC); tw_span_annotate_string(idx, "sql", Z_STRVAL_P(argument_element), 1 TSRMLS_CC); return idx; } } return -1; } long tw_trace_callback_smarty3_template(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *argument_element = ZEND_CALL_ARG(data, 1); zval *obj; zend_class_entry *smarty_ce; char *template; size_t template_len; if (argument_element && Z_TYPE_P(argument_element) == IS_STRING) { template = Z_STRVAL_P(argument_element); } else { zval *object = EX_OBJ(data); if (object == NULL || Z_TYPE_P(object) != IS_OBJECT) { return -1; } smarty_ce = Z_OBJCE_P(object); zval *__rv; argument_element = _zend_read_property(smarty_ce, object, "template_resource", sizeof("template_resource") - 1, 1, __rv); if (Z_TYPE_P(argument_element) != IS_STRING) { return -1; } template = Z_STRVAL_P(argument_element); } template_len = Z_STRLEN_P(argument_element); return tw_trace_callback_record_with_cache("view", 4, template, template_len, 1 TSRMLS_CC); } long tw_trace_callback_doctrine_persister(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *property; zend_class_entry *persister_ce, *metadata_ce; zval *object = EX_OBJ(data); if (object == NULL || Z_TYPE_P(object) != IS_OBJECT) { return -1; } persister_ce = Z_OBJCE_P(object); zval *__rv; property = _zend_read_property(persister_ce, object, "class", sizeof("class") - 1, 1, __rv); if (property == NULL) { property = _zend_read_property(persister_ce, object, "_class", sizeof("_class") - 1, 1, __rv); } if (property != NULL && Z_TYPE_P(property) == IS_OBJECT) { metadata_ce = Z_OBJCE_P(property); property = _zend_read_property(metadata_ce, property, "name", sizeof("name") - 1, 1, __rv); if (property == NULL) { return -1; } return tw_trace_callback_record_with_cache("doctrine.load", 13, Z_STRVAL_P(property), Z_STRLEN_P(property), 1 TSRMLS_CC); } return -1; } long tw_trace_callback_doctrine_query(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *property; zend_class_entry *query_ce; zval fname; _DECLARE_ZVAL(retval_ptr); zval *object = EX_OBJ(data); char *summary, *className; long idx = -1; int keepQuery = 0; if (object == NULL || Z_TYPE_P(object) != IS_OBJECT) { return idx; } query_ce = Z_OBJCE_P(object); className = _ZCE_NAME(query_ce); if (strcmp(className, "Doctrine\\ORM\\Query") == 0) { _ZVAL_STRING(&fname, "getDQL"); keepQuery = 1; } else if (strcmp(className, "Doctrine\\ORM\\NativeQuery") == 0) { _ZVAL_STRING(&fname, "getSQL"); } else { return idx; } if (SUCCESS == tw_call_user_function_ex(EG(function_table), object, &fname, retval_ptr)) { if (Z_TYPE_P(retval_ptr) != IS_STRING) { return idx; } idx = tw_span_create("doctrine.query", 14 TSRMLS_CC); if (keepQuery) { tw_span_annotate_string(idx, "title", "DQL", 1 TSRMLS_CC); tw_span_annotate_string(idx, "sql", Z_STRVAL_P(retval_ptr), 1 TSRMLS_CC); } else { tw_span_annotate_string(idx, "title", "Native", 1 TSRMLS_CC); } hp_ptr_dtor(retval_ptr); } #if PHP_VERSION_ID >= 70000 zend_string_release(Z_STR(fname)); #endif return idx; } long tw_trace_callback_twig_template(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1, *idx_ptr; zval fname; _DECLARE_ZVAL(retval_ptr); zval *object = EX_OBJ(data); if (object == NULL || Z_TYPE_P(object) != IS_OBJECT) { return idx; } _ZVAL_STRING(&fname, "getTemplateName"); if (SUCCESS == tw_call_user_function_ex(EG(function_table), object, &fname, retval_ptr)) { if (Z_TYPE_P(retval_ptr) == IS_STRING) { idx = tw_trace_callback_record_with_cache("view", 4, Z_STRVAL_P(retval_ptr), Z_STRLEN_P(retval_ptr), 1 TSRMLS_CC); } hp_ptr_dtor(retval_ptr); } #if PHP_VERSION_ID >= 70000 zend_string_release(Z_STR(fname)); #endif return idx; } long tw_trace_callback_event_dispatchers2(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1, *idx_ptr; zval *arg1 = ZEND_CALL_ARG(data, 1); zval *arg2 = ZEND_CALL_ARG(data, 2); char *event; int len; if (arg1 && arg2 && Z_TYPE_P(arg1) == IS_STRING && Z_TYPE_P(arg2) == IS_STRING) { len = Z_STRLEN_P(arg1) + Z_STRLEN_P(arg2) + 3; event = (char*)emalloc(len); snprintf(event, len, "%s::%s", Z_STRVAL_P(arg1), Z_STRVAL_P(arg2)); event[len-1] = '\0'; idx = tw_trace_callback_record_with_cache("event", 5, event, len, 1 TSRMLS_CC); efree(event); } return idx; } long tw_trace_callback_event_dispatchers(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1, *idx_ptr; zval *argument_element = ZEND_CALL_ARG(data, 1); zval fname; _DECLARE_ZVAL(retval_ptr); if (argument_element) { if (Z_TYPE_P(argument_element) == IS_STRING) { idx = tw_trace_callback_record_with_cache("event", 5, Z_STRVAL_P(argument_element), Z_STRLEN_P(argument_element), 1 TSRMLS_CC); } else if (Z_TYPE_P(argument_element) == IS_OBJECT && ( strcmp(symbol, "Cake\\Event\\EventManager::dispatch") == 0 || strcmp(symbol, "CakeEventManager::dispatch") == 0)) { // Special Handling for CakePHP 2&3 getting passed Event instance _ZVAL_STRING(&fname, "name"); if (SUCCESS == tw_call_user_function_ex(EG(function_table), argument_element, &fname, retval_ptr)) { if (Z_TYPE_P(retval_ptr) == IS_STRING) { idx = tw_trace_callback_record_with_cache("event", 5, Z_STRVAL_P(retval_ptr), Z_STRLEN_P(retval_ptr), 1 TSRMLS_CC); } hp_ptr_dtor(retval_ptr); } #if PHP_VERSION_ID >= 70000 zend_string_release(Z_STR(fname)); #endif } } return idx; } long tw_trace_callback_mysqli_connect(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1; zval *arg; if (ZEND_CALL_NUM_ARGS(data) < 1) { return idx; } idx = tw_span_create("sql", 3 TSRMLS_CC); tw_span_annotate_string(idx, "db.type", "mysql", 1 TSRMLS_CC); arg = ZEND_CALL_ARG(data, 1); if (Z_TYPE_P(arg) == IS_STRING) { tw_span_annotate_string(idx, "peer.host", Z_STRVAL_P(arg), 1 TSRMLS_CC); } if (ZEND_CALL_NUM_ARGS(data) > 3) { arg = ZEND_CALL_ARG(data, 4); if (Z_TYPE_P(arg) == IS_STRING && Z_STRLEN_P(arg) > 0) { tw_span_annotate_string(idx, "db.name", Z_STRVAL_P(arg), 1 TSRMLS_CC); } } if (ZEND_CALL_NUM_ARGS(data) > 4) { arg = ZEND_CALL_ARG(data, 5); if (Z_TYPE_P(arg) == IS_STRING) { tw_span_annotate_string(idx, "peer.port", Z_STRVAL_P(arg), 1 TSRMLS_CC); } else if (Z_TYPE_P(arg) == IS_LONG) { tw_span_annotate_long(idx, "peer.port", Z_LVAL_P(arg) TSRMLS_CC); } } return idx; } long tw_trace_callback_pdo_connect(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx = -1; zval *dsn; zend_string *match = NULL; _DECLARE_ZVAL(return_value); _DECLARE_ZVAL(subpats); pcre_cache_entry *pce; if (ZEND_CALL_NUM_ARGS(data) < 1) { return idx; } dsn = ZEND_CALL_ARG(data, 1); if (dsn == NULL || Z_TYPE_P(dsn) != IS_STRING) { return idx; } if (match = tw_pcre_match("(^(mysql|sqlite|pgsql|odbc|oci):)", sizeof("(^(mysql|sqlite|pgsql|odbc|oci):)")-1, dsn TSRMLS_CC)) { idx = tw_span_create("sql", 3 TSRMLS_CC); tw_span_annotate_string(idx, "db.type", ZSTR_VAL(match), 1 TSRMLS_CC); zend_string_release(match); if (match = tw_pcre_match("(host=([^;\\s]+))", sizeof("(host=([^;\\s]+))")-1, dsn TSRMLS_CC)) { tw_span_annotate_string(idx, "peer.host", ZSTR_VAL(match), 1 TSRMLS_CC); zend_string_release(match); } if (match = tw_pcre_match("(port=([^;\\s]+))", sizeof("(port=([^;\\s]+))")-1, dsn TSRMLS_CC)) { tw_span_annotate_string(idx, "peer.port", ZSTR_VAL(match), 1 TSRMLS_CC); zend_string_release(match); } if (match = tw_pcre_match("(dbname=([^;\\s]+))", sizeof("(dbname=([^;\\s]+))")-1, dsn TSRMLS_CC)) { tw_span_annotate_string(idx, "db.name", ZSTR_VAL(match), 1 TSRMLS_CC); zend_string_release(match); } } return idx; } zend_string *tw_pcre_match(char *pattern, strsize_t len, zval *subject TSRMLS_DC) { zval *match = NULL; zend_string *result = NULL; _DECLARE_ZVAL(return_value); _DECLARE_ZVAL(subpats); pcre_cache_entry *pce; #if PHP_VERSION_ID >= 70000 zend_string *pattern_str; pattern_str = zend_string_init(pattern, len, 0); if ((pce = pcre_get_compiled_regex_cache(pattern_str)) == NULL) { zend_string_release(pattern_str); return NULL; } #else if ((pce = pcre_get_compiled_regex_cache(pattern, len TSRMLS_CC)) == NULL) { return NULL; } #endif _ALLOC_INIT_ZVAL(return_value); _ALLOC_INIT_ZVAL(subpats); pce->refcount++; tw_pcre_match_impl(pce, subject, return_value, subpats, 0, 1, 0, 0); pce->refcount--; if (Z_LVAL_P(return_value) > 0 && Z_TYPE_P(subpats) == IS_ARRAY) { match = zend_compat_hash_index_find(Z_ARRVAL_P(subpats), 1); if (match != NULL) { result = zend_string_init(Z_STRVAL_P(match), Z_STRLEN_P(match), 0); } } #if PHP_VERSION_ID >= 70000 zend_string_release(pattern_str); #endif hp_ptr_dtor(return_value); hp_ptr_dtor(subpats); return result; } long tw_trace_callback_pdo_stmt_execute(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx; #if PHP_VERSION_ID >= 70000 pdo_stmt_t *stmt = (pdo_stmt_t*) ((char*) Z_OBJ_P(EX_OBJ(data)) - Z_OBJ_HT_P(EX_OBJ(data))->offset); #else pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object_by_handle(Z_OBJ_HANDLE_P(EX_OBJ(data)) TSRMLS_CC); #endif idx = tw_span_create("sql", 3 TSRMLS_CC); tw_span_annotate_string(idx, "sql", stmt->query_string, 1 TSRMLS_CC); return idx; } long tw_trace_callback_mysqli_stmt_execute(char *symbol, zend_execute_data *data TSRMLS_DC) { return tw_trace_callback_record_with_cache("sql", 3, "execute", 7, 1 TSRMLS_CC); } long tw_trace_callback_sql_commit(char *symbol, zend_execute_data *data TSRMLS_DC) { return tw_trace_callback_record_with_cache("sql", 3, "commit", 3, 1 TSRMLS_CC); } long tw_trace_callback_sql_functions(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *argument_element; long idx; if (strcmp(symbol, "mysqli_query") == 0 || strcmp(symbol, "mysqli_prepare") == 0) { argument_element = ZEND_CALL_ARG(data, 2); } else { argument_element = ZEND_CALL_ARG(data, 1); } if (Z_TYPE_P(argument_element) != IS_STRING) { return -1; } idx = tw_span_create("sql", 3 TSRMLS_CC); tw_span_annotate_string(idx, "sql", Z_STRVAL_P(argument_element), 1 TSRMLS_CC); return idx; } long tw_trace_callback_fastcgi_finish_request(char *symbol, zend_execute_data *data TSRMLS_DC) { // stop the main span, the request ended here tw_span_timer_stop(0 TSRMLS_CC); return tw_trace_callback_record_with_cache("php", 3, symbol, strlen(symbol), 1 TSRMLS_CC); } long tw_trace_callback_elasticsearch_perform_request(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx; zval *method, *uri; if (ZEND_CALL_NUM_ARGS(data) < 2) { return -1; } idx = tw_span_create("elasticsearch", 13 TSRMLS_CC); method = ZEND_CALL_ARG(data, 1); uri = ZEND_CALL_ARG(data, 2); if (method == NULL || uri == NULL || Z_TYPE_P(method) != IS_STRING || Z_TYPE_P(uri) != IS_STRING) { return -1; } tw_span_annotate_string(idx, "es.method", Z_STRVAL_P(method), 1 TSRMLS_CC); tw_span_annotate_string(idx, "es.path", Z_STRVAL_P(uri), 1 TSRMLS_CC); if (strcmp("Elasticsearch\\Connections\\Connection::performRequest", symbol) == 0) { tw_span_timer_start(idx TSRMLS_CC); zend_compat_hash_update_long(TWG(span_cache), "elasticsearch-php", sizeof("elasticsearch-php")-1, idx); } return -1; } long tw_trace_callback_elasticsearch_wait_request(char *symbol, zend_execute_data *data TSRMLS_DC) { long idx, *idx_ptr = NULL; zval *spanId, *object; zend_class_entry *endpoint_ce; idx = zend_compat_hash_find_long(TWG(span_cache), "elasticsearch-php", sizeof("elasticsearch-php")-1); if (idx == -1) { return -1; } tw_span_timer_stop(idx TSRMLS_CC); object = EX_OBJ(data); if (Z_TYPE_P(object) == IS_OBJECT) { endpoint_ce = Z_OBJCE_P(object); tw_span_annotate_string(idx, "endpoint", _ZCE_NAME(endpoint_ce), 1 TSRMLS_CC); } return -1; } long tw_trace_callback_curl_multi_add(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *curl_handle; long curl_handle_res; long idx; if (ZEND_CALL_NUM_ARGS(data) < 2) { return -1; } curl_handle = ZEND_CALL_ARG(data, 2); if (curl_handle == NULL || Z_TYPE_P(curl_handle) != IS_RESOURCE) { return -1; } curl_handle_res = tw_resource_handle(curl_handle); idx = tw_span_create("http", 4 TSRMLS_CC); #if PHP_VERSION_ID >= 70000 zval tmp; ZVAL_LONG(&tmp, idx); zend_hash_index_update(TWG(span_cache), curl_handle_res, &tmp); #else zval *tmp; MAKE_STD_ZVAL(tmp); ZVAL_LONG(tmp, idx); zend_hash_index_update(TWG(span_cache), curl_handle_res, (void *) &tmp, sizeof(zval *), NULL); #endif tw_span_timer_start(idx TSRMLS_CC); // do not return idx here, otherwise the span gets closed immediately // but we need to wait for curl_multi_remove_handle() call. return -1; } long tw_trace_callback_curl_multi_remove(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *curl_handle, *spanId, *option; long curl_handle_res; long netIn = 0; long idx; zval fname, *opt; #if PHP_VERSION_ID < 70000 zval ***params_array; #else zval params[1]; #endif _DECLARE_ZVAL(retval_ptr); if (ZEND_CALL_NUM_ARGS(data) < 2) { return -1; } curl_handle = ZEND_CALL_ARG(data, 2); if (curl_handle == NULL || Z_TYPE_P(curl_handle) != IS_RESOURCE) { return -1; } curl_handle_res = tw_resource_handle(curl_handle); spanId = zend_compat_hash_index_find(TWG(span_cache), curl_handle_res); if (spanId == NULL || Z_TYPE_P(spanId) != IS_LONG) { return -1; } idx = Z_LVAL_P(spanId); tw_span_timer_stop(idx TSRMLS_CC); _ZVAL_STRING(&fname, "curl_getinfo"); #if PHP_VERSION_ID < 70000 params_array = (zval ***) emalloc(sizeof(zval **)); params_array[0] = &curl_handle; if (SUCCESS == call_user_function_ex(EG(function_table), NULL, &fname, &retval_ptr, 1, params_array, 1, NULL TSRMLS_CC)) { #else ZVAL_RES(¶ms[0], Z_RES_P(curl_handle)); if (SUCCESS == call_user_function_ex(EG(function_table), NULL, &fname, retval_ptr, 1, params, 1, NULL)) { #endif if (Z_TYPE_P(retval_ptr) == IS_ARRAY) { option = zend_compat_hash_find_const(Z_ARRVAL_P(retval_ptr), "url", sizeof("url")-1); if (option && Z_TYPE_P(option) == IS_STRING) { tw_span_annotate_string(idx, "url", Z_STRVAL_P(option), 1 TSRMLS_CC); } option = zend_compat_hash_find_const(Z_ARRVAL_P(retval_ptr), "http_code", sizeof("http_code")-1); if (option && Z_TYPE_P(option) == IS_LONG) { tw_span_annotate_long(idx, "http.status_code", Z_LVAL_P(option) TSRMLS_CC); } option = zend_compat_hash_find_const(Z_ARRVAL_P(retval_ptr), "primary_ip", sizeof("primary_ip")-1); if (option && Z_TYPE_P(option) == IS_STRING) { tw_span_annotate_string(idx, "peer.ipv4", Z_STRVAL_P(option), 1 TSRMLS_CC); } option = zend_compat_hash_find_const(Z_ARRVAL_P(retval_ptr), "primary_port", sizeof("primary_port")-1); if (option && Z_TYPE_P(option) == IS_LONG) { tw_span_annotate_long(idx, "peer.port", Z_LVAL_P(option) TSRMLS_CC); } option = zend_compat_hash_find_const(Z_ARRVAL_P(retval_ptr), "request_size", sizeof("request_size")-1); if (option && Z_TYPE_P(option) == IS_LONG) { tw_span_annotate_long(idx, "net.out", Z_LVAL_P(option) TSRMLS_CC); } option = zend_compat_hash_find_const(Z_ARRVAL_P(retval_ptr), "download_content_length", sizeof("download_content_length")-1); if (option) { if (Z_TYPE_P(option) == IS_DOUBLE) { netIn = (long)Z_DVAL_P(option); } else if (Z_TYPE_P(option) == IS_LONG) { netIn = Z_LVAL_P(option); } } option = zend_compat_hash_find_const(Z_ARRVAL_P(retval_ptr), "header_size", sizeof("header_size")-1); if (option && Z_TYPE_P(option) == IS_LONG) { netIn += Z_LVAL_P(option); tw_span_annotate_long(idx, "net.in", netIn TSRMLS_CC); } } hp_ptr_dtor(retval_ptr); } #if PHP_VERSION_ID < 70000 efree(params_array); #else zend_string_release(Z_STR(fname)); #endif return -1; } long tw_trace_callback_curl_exec(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *argument = ZEND_CALL_ARG(data, 1); zval *option; long idx, *idx_ptr; zval fname, *opt; _DECLARE_ZVAL(retval_ptr); if (argument == NULL || Z_TYPE_P(argument) != IS_RESOURCE) { return -1; } _ZVAL_STRING(&fname, "curl_getinfo"); #if PHP_VERSION_ID < 70000 zval ***params_array; params_array = (zval ***) emalloc(sizeof(zval **)); params_array[0] = &argument; if (SUCCESS == call_user_function_ex(EG(function_table), NULL, &fname, &retval_ptr, 1, params_array, 1, NULL TSRMLS_CC)) { #else zval params[1]; ZVAL_RES(¶ms[0], Z_RES_P(argument)); if (SUCCESS == call_user_function_ex(EG(function_table), NULL, &fname, retval_ptr, 1, params, 1, NULL)) { #endif option = zend_compat_hash_find_const(Z_ARRVAL_P(retval_ptr), "url", sizeof("url")-1); if (option && Z_TYPE_P(option) == IS_STRING) { idx = tw_span_create("http", 4 TSRMLS_CC); tw_span_annotate_string(idx, "url", Z_STRVAL_P(option), 1 TSRMLS_CC); } hp_ptr_dtor(retval_ptr); } #if PHP_VERSION_ID < 70000 efree(params_array); #else zend_string_release(Z_STR(fname)); #endif return idx; } long tw_trace_callback_soap_client_dorequest(char *symbol, zend_execute_data *data TSRMLS_DC) { if (ZEND_CALL_NUM_ARGS(data) < 2) { return -1; } char *summary; zval *argument = ZEND_CALL_ARG(data, 2); long idx = -1; if (Z_TYPE_P(argument) != IS_STRING) { return idx; } idx = tw_span_create("http", 4 TSRMLS_CC); tw_span_annotate_string(idx, "url", Z_STRVAL_P(argument), 1 TSRMLS_CC); tw_span_annotate_string(idx, "method", "POST", 1 TSRMLS_CC); tw_span_annotate_string(idx, "service", "soap", 1 TSRMLS_CC); return idx; } long tw_trace_callback_file_get_contents(char *symbol, zend_execute_data *data TSRMLS_DC) { zval *argument = ZEND_CALL_ARG(data, 1); char *summary; long idx = -1; if (Z_TYPE_P(argument) != IS_STRING) { return idx; } if (strncmp(Z_STRVAL_P(argument), "http", 4) != 0) { return idx; } idx = tw_span_create("http", 4 TSRMLS_CC); tw_span_annotate_string(idx, "url", Z_STRVAL_P(argument), 1 TSRMLS_CC); return idx; } /** * Request init callback. * * Check if Tideways.php exists in extension_dir and load it * in request init. This makes class \Tideways\Profiler available * for usage. */ PHP_RINIT_FUNCTION(tideways) { char *extension_dir; char *profiler_file; int profiler_file_len; TWG(prepend_overwritten) = 0; TWG(backtrace) = NULL; TWG(transaction_name) = NULL; TWG(transaction_function) = NULL; #if PHP_VERSION_ID >= 70000 ZVAL_NULL(&TWG(spans)); ZVAL_NULL(&TWG(stats_count)); ZVAL_NULL(&TWG(exception)); #else TWG(exception) = NULL; #endif if (INI_INT("tideways.auto_prepend_library") == 0) { return SUCCESS; } extension_dir = INI_STR("extension_dir"); profiler_file_len = strlen(extension_dir) + strlen("Tideways.php") + 2; profiler_file = emalloc(profiler_file_len); snprintf(profiler_file, profiler_file_len, "%s/%s", extension_dir, "Tideways.php"); if (PG(open_basedir) && php_check_open_basedir_ex(profiler_file, 0 TSRMLS_CC)) { efree(profiler_file); return SUCCESS; } if (VCWD_ACCESS(profiler_file, F_OK) == 0) { PG(auto_prepend_file) = profiler_file; TWG(prepend_overwritten) = 1; } else { efree(profiler_file); } return SUCCESS; } /** * Request shutdown callback. Stop profiling and return. */ PHP_RSHUTDOWN_FUNCTION(tideways) { hp_end(TSRMLS_C); if (TWG(prepend_overwritten) == 1) { efree(PG(auto_prepend_file)); PG(auto_prepend_file) = NULL; TWG(prepend_overwritten) = 0; } return SUCCESS; } /** * Module info callback. Returns the Tideways version. */ PHP_MINFO_FUNCTION(tideways) { char *extension_dir; char *profiler_file; int profiler_file_len; php_info_print_table_start(); php_info_print_table_header(2, "tideways", TIDEWAYS_VERSION); php_info_print_table_row(2, "Connection (tideways.connection)", INI_STR("tideways.connection")); php_info_print_table_row(2, "UDP Connection (tideways.udp_connection)", INI_STR("tideways.udp_connection")); php_info_print_table_row(2, "Default API Key (tideways.api_key)", INI_STR("tideways.api_key")); php_info_print_table_row(2, "Default Sample-Rate (tideways.sample_rate)", INI_STR("tideways.sample_rate")); php_info_print_table_row(2, "Framework Detection (tideways.framework)", INI_STR("tideways.framework")); php_info_print_table_row(2, "Automatically Start (tideways.auto_start)", INI_INT("tideways.auto_start") ? "Yes": "No"); php_info_print_table_row(2, "Tideways Collect Mode (tideways.collect)", INI_STR("tideways.collect")); php_info_print_table_row(2, "Tideways Monitoring Mode (tideways.monitor)", INI_STR("tideways.monitor")); php_info_print_table_row(2, "Allowed Distributed Tracing Hosts (tideways.distributed_tracing_hosts)", INI_STR("tideways.distributed_tracing_hosts")); php_info_print_table_row(2, "Load PHP Library (tideways.auto_prepend_library)", INI_INT("tideways.auto_prepend_library") ? "Yes": "No"); extension_dir = INI_STR("extension_dir"); profiler_file_len = strlen(extension_dir) + strlen("Tideways.php") + 2; profiler_file = emalloc(profiler_file_len); snprintf(profiler_file, profiler_file_len, "%s/%s", extension_dir, "Tideways.php"); if (VCWD_ACCESS(profiler_file, F_OK) == 0) { php_info_print_table_row(2, "Tideways.php found", "Yes"); } else { php_info_print_table_row(2, "Tideways.php found", "No"); } efree(profiler_file); php_info_print_table_end(); } /** * *************************************************** * COMMON HELPER FUNCTION DEFINITIONS AND LOCAL MACROS * *************************************************** */ static void hp_register_constants(INIT_FUNC_ARGS) { REGISTER_LONG_CONSTANT("TIDEWAYS_FLAGS_CPU", TIDEWAYS_FLAGS_CPU, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("TIDEWAYS_FLAGS_MEMORY", TIDEWAYS_FLAGS_MEMORY, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("TIDEWAYS_FLAGS_NO_BUILTINS", TIDEWAYS_FLAGS_NO_BUILTINS, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("TIDEWAYS_FLAGS_NO_USERLAND", TIDEWAYS_FLAGS_NO_USERLAND, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("TIDEWAYS_FLAGS_NO_COMPILE", TIDEWAYS_FLAGS_NO_COMPILE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("TIDEWAYS_FLAGS_NO_SPANS", TIDEWAYS_FLAGS_NO_SPANS, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("TIDEWAYS_FLAGS_NO_HIERACHICAL", TIDEWAYS_FLAGS_NO_HIERACHICAL, CONST_CS | CONST_PERSISTENT); } /** * A hash function to calculate a 8-bit hash code for a function name. * This is based on a small modification to 'zend_inline_hash_func' by summing * up all bytes of the ulong returned by 'zend_inline_hash_func'. * * @param str, char *, string to be calculated hash code for. * * @author cjiang */ static inline uint8 hp_inline_hash(char * arKey) { size_t nKeyLength = strlen(arKey); register uint8 hash = 0; /* variant with the hash unrolled eight times */ for (; nKeyLength >= 8; nKeyLength -= 8) { hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; } switch (nKeyLength) { case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */ case 1: hash = ((hash << 5) + hash) + *arKey++; break; case 0: break; EMPTY_SWITCH_DEFAULT_CASE() } return hash; } /** * Parse the list of ignored functions from the zval argument. * * @author mpal */ static void hp_parse_options_from_arg(zval *args TSRMLS_DC) { hp_clean_profiler_options_state(TSRMLS_C); if (args == NULL) { return; } zval *zresult = NULL; zresult = hp_zval_at_key("ignored_functions", sizeof("ignored_functions"), args); if (zresult == NULL) { zresult = hp_zval_at_key("functions", sizeof("functions"), args); if (zresult != NULL) { TWG(filtered_type) = 2; } } else { TWG(filtered_type) = 1; } TWG(filtered_functions) = hp_function_map_create(hp_strings_in_zval(zresult)); zresult = hp_zval_at_key("transaction_function", sizeof("transaction_function"), args); if (zresult != NULL && Z_TYPE_P(zresult) == IS_STRING) { TWG(transaction_function) = zend_string_copy(Z_STR_P(zresult)); } zresult = hp_zval_at_key("exception_function", sizeof("exception_function"), args); if (zresult != NULL && Z_TYPE_P(zresult) == IS_STRING) { TWG(exception_function) = zend_string_copy(Z_STR_P(zresult)); } } static void hp_exception_function_clear(TSRMLS_D) { if (TWG(exception_function) != NULL) { zend_string_release(TWG(exception_function)); TWG(exception_function) = NULL; } #if PHP_VERSION_ID >= 70000 hp_ptr_dtor(&TWG(exception)); ZVAL_NULL(&TWG(exception)); #else if (TWG(exception) != NULL) { hp_ptr_dtor(TWG(exception)); TWG(exception) = NULL; } #endif } static void hp_transaction_function_clear(TSRMLS_D) { if (TWG(transaction_function)) { zend_string_release(TWG(transaction_function)); TWG(transaction_function) = NULL; } } static inline hp_function_map *hp_function_map_create(char **names) { if (names == NULL) { return NULL; } hp_function_map *map; map = emalloc(sizeof(hp_function_map)); map->names = names; memset(map->filter, 0, TIDEWAYS_FILTERED_FUNCTION_SIZE); int i = 0; for(; names[i] != NULL; i++) { char *str = names[i]; uint8 hash = hp_inline_hash(str); int idx = INDEX_2_BYTE(hash); map->filter[idx] |= INDEX_2_BIT(hash); } return map; } static inline void hp_function_map_clear(hp_function_map *map) { if (map == NULL) { return; } hp_array_del(map->names); map->names = NULL; memset(map->filter, 0, TIDEWAYS_FILTERED_FUNCTION_SIZE); efree(map); } static inline int hp_function_map_exists(hp_function_map *map, uint8 hash_code, char *curr_func) { if (hp_function_map_filter_collision(map, hash_code)) { int i = 0; for (; map->names[i] != NULL; i++) { char *name = map->names[i]; if (strcmp(curr_func, name) == 0) { return 1; } } } return 0; } static inline int hp_function_map_filter_collision(hp_function_map *map, uint8 hash) { uint8 mask = INDEX_2_BIT(hash); return map->filter[INDEX_2_BYTE(hash)] & mask; } #if PHP_VERSION_ID >= 70000 static inline void hp_free_trace_cb(zval *zv) { efree(Z_PTR_P(zv)); } #else static inline void hp_free_trace_cb(void *p) {} #endif void hp_init_trace_callbacks(TSRMLS_D) { tw_trace_callback cb; if ((TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_SPANS) > 0) { return; } TWG(trace_callbacks) = NULL; TWG(trace_watch_callbacks) = NULL; TWG(span_cache) = NULL; ALLOC_HASHTABLE(TWG(trace_callbacks)); zend_hash_init(TWG(trace_callbacks), 255, NULL, hp_free_trace_cb, 0); ALLOC_HASHTABLE(TWG(span_cache)); zend_hash_init(TWG(span_cache), 255, NULL, NULL, 0); cb = tw_trace_callback_file_get_contents; register_trace_callback("file_get_contents", cb); cb = tw_trace_callback_php_call; register_trace_callback("session_start", cb); // Symfony register_trace_callback("Symfony\\Component\\HttpKernel\\Kernel::boot", cb); register_trace_callback("Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher::lazyLoad", cb); register_trace_callback("Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache::lock", cb); register_trace_callback("Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache::forward", cb); // Wordpress register_trace_callback("get_sidebar", cb); register_trace_callback("get_header", cb); register_trace_callback("get_footer", cb); register_trace_callback("load_textdomain", cb); register_trace_callback("setup_theme", cb); // Doctrine register_trace_callback("Doctrine\\ORM\\EntityManager::flush", cb); register_trace_callback("Doctrine\\ODM\\CouchDB\\DocumentManager::flush", cb); // Magento register_trace_callback("Mage_Core_Model_App::_initModules", cb); register_trace_callback("Mage_Core_Model_Config::loadModules", cb); register_trace_callback("Mage_Core_Model_Config::loadDb", cb); // Smarty&Twig Compiler register_trace_callback("Smarty_Internal_TemplateCompilerBase::compileTemplate", cb); register_trace_callback("Twig_Environment::compileSource", cb); // Shopware Assets (very special, do we really need it?) register_trace_callback("JSMin::minify", cb); register_trace_callback("Less_Parser::getCss", cb); // Laravel (4+5) register_trace_callback("Illuminate\\Foundation\\Application::boot", cb); register_trace_callback("Illuminate\\Foundation\\Application::dispatch", cb); register_trace_callback("Illuminate\\Session\\Middleware\\StartSession::startSession", cb); register_trace_callback("Illuminate\\Session\\Middleware\\StartSession::collectGarbage", cb); // Silex register_trace_callback("Silex\\Application::mount", cb); cb = tw_trace_callback_eloquent_query; register_trace_callback("Illuminate\\Database\\Eloquent\\Builder::getModels", cb); cb = tw_trace_callback_eloquent_model; register_trace_callback("Illuminate\\Database\\Eloquent\\Model::performInsert", cb); register_trace_callback("Illuminate\\Database\\Eloquent\\Model::performUpdate", cb); register_trace_callback("Illuminate\\Database\\Eloquent\\Model::delete", cb); register_trace_callback("Illuminate\\Database\\Eloquent\\Model::destroy", cb); cb = tw_trace_callback_presta_controller; register_trace_callback("ControllerCore::run", cb); // PrestaShop 1.6 cb = tw_trace_callback_cakephp_controller; register_trace_callback("Controller::invokeAction", cb); register_trace_callback("Cake\\Controller\\Controller::invokeAction", cb); cb = tw_trace_callback_doctrine_persister; register_trace_callback("Doctrine\\ORM\\Persisters\\BasicEntityPersister::load", cb); register_trace_callback("Doctrine\\ORM\\Persisters\\BasicEntityPersister::loadAll", cb); register_trace_callback("Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister::load", cb); register_trace_callback("Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister::loadAll", cb); cb = tw_trace_callback_doctrine_query; register_trace_callback("Doctrine\\ORM\\AbstractQuery::execute", cb); cb = tw_trace_callback_doctrine_couchdb_request; register_trace_callback("Doctrine\\CouchDB\\HTTP\\SocketClient::request", cb); register_trace_callback("Doctrine\\CouchDB\\HTTP\\StreamClient::request", cb); cb = tw_trace_callback_curl_exec; register_trace_callback("curl_exec", cb); cb = tw_trace_callback_curl_multi_add; register_trace_callback("curl_multi_add_handle", cb); cb = tw_trace_callback_curl_multi_remove; register_trace_callback("curl_multi_remove_handle", cb); cb = tw_trace_callback_elasticsearch_perform_request; register_trace_callback("Elasticsearch\\Connections\\Connection::performRequest", cb); register_trace_callback("Elasticsearch\\Connections\\GuzzleConnection::performRequest", cb); register_trace_callback("Elasticsearch\\Connections\\CurlMultiConnection::performRequest", cb); cb = tw_trace_callback_elasticsearch_wait_request; register_trace_callback("Elasticsearch\\Endpoints\\AbstractEndpoint::resultOrFuture", cb); cb = tw_trace_callback_sql_functions; register_trace_callback("PDO::exec", cb); register_trace_callback("PDO::query", cb); register_trace_callback("mysql_query", cb); register_trace_callback("mysqli_query", cb); register_trace_callback("mysqli::query", cb); register_trace_callback("mysqli::prepare", cb); register_trace_callback("mysqli_prepare", cb); cb = tw_trace_callback_sql_commit; register_trace_callback("PDO::commit", cb); register_trace_callback("mysqli::commit", cb); register_trace_callback("mysqli_commit", cb); cb = tw_trace_callback_mysqli_connect; register_trace_callback("mysql_connect", cb); register_trace_callback("mysqli_connect", cb); #if PHP_VERSION_ID >= 70000 register_trace_callback("mysqli::__construct", cb); #else register_trace_callback("mysqli::mysqli", cb); #endif cb = tw_trace_callback_pdo_connect; register_trace_callback("PDO::__construct", cb); cb = tw_trace_callback_pdo_stmt_execute; register_trace_callback("PDOStatement::execute", cb); cb = tw_trace_callback_mysqli_stmt_execute; register_trace_callback("mysqli_stmt_execute", cb); register_trace_callback("mysqli_stmt::execute", cb); cb = tw_trace_callback_pgsql_query; register_trace_callback("pg_query", cb); register_trace_callback("pg_query_params", cb); cb = tw_trace_callback_pgsql_execute; register_trace_callback("pg_execute", cb); cb = tw_trace_callback_event_dispatchers; register_trace_callback("Doctrine\\Common\\EventManager::dispatchEvent", cb); register_trace_callback("Enlight_Event_EventManager::filter", cb); register_trace_callback("Enlight_Event_EventManager::notify", cb); register_trace_callback("Enlight_Event_EventManager::notifyUntil", cb); register_trace_callback("Zend\\EventManager\\EventManager::trigger", cb); register_trace_callback("do_action", cb); register_trace_callback("drupal_alter", cb); register_trace_callback("Mage::dispatchEvent", cb); register_trace_callback("Magento\\Framework\\Event\\Manager::dispatch", cb); register_trace_callback("Symfony\\Component\\EventDispatcher\\EventDispatcher::dispatch", cb); register_trace_callback("Illuminate\\Events\\Dispatcher::fire", cb); register_trace_callback("HookCore::exec", cb); // PrestaShop 1.6 register_trace_callback("Cake\\Event\\EventManager::dispatch", cb); register_trace_callback("CakeEventManager::dispatch", cb); register_trace_callback("Phalcon\\Events\\Manager::fire", cb); register_trace_callback("yii\\base\\Component::trigger", cb); register_trace_callback("CComponent::raiseEvent", cb); // yii 1 cb = tw_trace_callback_event_dispatchers2; register_trace_callback("HookCore::coreCallHook", cb); // PrestaShop 1.6 register_trace_callback("TYPO3\\Flow\\SignalSlot\\Dispatcher::dispatch", cb); register_trace_callback("TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher::dispatch", cb); cb = tw_trace_callback_twig_template; register_trace_callback("Twig_Template::render", cb); register_trace_callback("Twig_Template::display", cb); cb = tw_trace_callback_smarty3_template; register_trace_callback("Smarty_Internal_TemplateBase::fetch", cb); cb = tw_trace_callback_fastcgi_finish_request; register_trace_callback("fastcgi_finish_request", cb); cb = tw_trace_callback_soap_client_dorequest; register_trace_callback("SoapClient::__doRequest", cb); cb = tw_trace_callback_view_class; register_trace_callback("Mage_Core_Block_Abstract::toHtml", cb); register_trace_callback("Magento\\Framework\\View\\Element\\AbstractBlock::toHtml", cb); register_trace_callback("TYPO3\\Flow\\Mvc\\View\\JsonView::render", cb); register_trace_callback("TYPO3\\Fluid\\View\\AbstractTemplateView::render", cb); register_trace_callback("TYPO3\\CMS\\Extbase\\Mvc\\View\\JsonView::render", cb); register_trace_callback("TYPO3\\CMS\\Extbase\\Mvc\\View\\NotFoundView::render", cb); register_trace_callback("TYPO3\\CMS\\Fluid\\View\\AbstractTemplateView::render", cb); cb = tw_trace_callback_view_engine; register_trace_callback("Zend_View_Abstract::render", cb); register_trace_callback("Illuminate\\View\\Engines\\CompilerEngine::get", cb); register_trace_callback("Smarty::fetch", cb); register_trace_callback("load_template", cb); register_trace_callback("View::_render", cb); register_trace_callback("Cake\\View\\View::_render", cb); register_trace_callback("Phalcon\\Mvc\\View\\Engine\\Volt::render", cb); register_trace_callback("Phalcon\\Mvc\\View\\Engine\\Php::render", cb); register_trace_callback("Phalcon\\Mvc\\View\\Simple::render", cb); register_trace_callback("Phalcon\\Mvc\\View::render", cb); register_trace_callback("yii\\base\\View::renderFile", cb); register_trace_callback("CBaseController::renderFile", cb); // yii 1 cb = tw_trace_callback_zend1_dispatcher_families_tx; register_trace_callback("Enlight_Controller_Action::dispatch", cb); register_trace_callback("Mage_Core_Controller_Varien_Action::dispatch", cb); register_trace_callback("Magento\\Framework\\App\\Action\\Action::dispatch", cb); register_trace_callback("Zend_Controller_Action::dispatch", cb); register_trace_callback("Illuminate\\Routing\\Controller::callAction", cb); register_trace_callback("yii\\base\\Module::runAction", cb); register_trace_callback("CController::run", cb); // yii 1 cb = tw_trace_callback_symfony_resolve_arguments_tx; register_trace_callback("Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver::getArguments", cb); cb = tw_trace_callback_oxid_tx; register_trace_callback("oxShopControl::_process", cb); // Different versions of Memcache Extension have either MemcachePool or Memcache class, @todo investigate cb = tw_trace_callback_memcache; register_trace_callback("MemcachePool::get", cb); register_trace_callback("MemcachePool::set", cb); register_trace_callback("MemcachePool::delete", cb); register_trace_callback("MemcachePool::flush", cb); register_trace_callback("MemcachePool::replace", cb); register_trace_callback("MemcachePool::increment", cb); register_trace_callback("MemcachePool::decrement", cb); register_trace_callback("Memcache::get", cb); register_trace_callback("Memcache::set", cb); register_trace_callback("Memcache::delete", cb); register_trace_callback("Memcache::flush", cb); register_trace_callback("Memcache::replace", cb); register_trace_callback("Memcache::increment", cb); register_trace_callback("Memcache::decrement", cb); cb = tw_trace_callback_apc; register_trace_callback("apcu_store", cb); register_trace_callback("apcu_fetch", cb); register_trace_callback("apc_store", cb); register_trace_callback("apc_fetch", cb); cb = tw_trace_callback_pheanstalk; register_trace_callback("Pheanstalk_Pheanstalk::put", cb); register_trace_callback("Pheanstalk\\Pheanstalk::put", cb); cb = tw_trace_callback_phpampqlib; register_trace_callback("PhpAmqpLib\\Channel\\AMQPChannel::basic_publish", cb); cb = tw_trace_callback_mongo_collection; register_trace_callback("MongoCollection::find", cb); register_trace_callback("MongoCollection::findOne", cb); register_trace_callback("MongoCollection::findAndModify", cb); register_trace_callback("MongoCollection::insert", cb); register_trace_callback("MongoCollection::remove", cb); register_trace_callback("MongoCollection::save", cb); register_trace_callback("MongoCollection::update", cb); register_trace_callback("MongoCollection::group", cb); register_trace_callback("MongoCollection::distinct", cb); register_trace_callback("MongoCollection::batchInsert", cb); register_trace_callback("MongoCollection::aggregate", cb); register_trace_callback("MongoCollection::aggregateCursor", cb); cb = tw_trace_callback_mongo_cursor_next; register_trace_callback("MongoCursor::next", cb); register_trace_callback("MongoCursor::hasNext", cb); register_trace_callback("MongoCursor::getNext", cb); register_trace_callback("MongoCommandCursor::next", cb); register_trace_callback("MongoCommandCursor::hasNext", cb); register_trace_callback("MongoCommandCursor::getNext", cb); cb = tw_trace_callback_mongo_cursor_io; register_trace_callback("MongoCursor::rewind", cb); register_trace_callback("MongoCursor::doQuery", cb); register_trace_callback("MongoCursor::count", cb); cb = tw_trace_callback_mongodb_command; register_trace_callback("MongoDB\\Driver\\Manager::executeCommand", cb); register_trace_callback("MongoDB\\Driver\\Manager::executeBulkWrite", cb); register_trace_callback("MongoDB\\Driver\\Manager::executeQuery", cb); register_trace_callback("MongoDB\\Driver\\Server::executeCommand", cb); register_trace_callback("MongoDB\\Driver\\Server::executeBulkWrite", cb); register_trace_callback("MongoDB\\Driver\\Server::executeQuery", cb); cb = tw_trace_callback_mongodb_connect; register_trace_callback("MongoDB\\Driver\\Manager::__construct", cb); cb = tw_trace_callback_predis_call; register_trace_callback("Predis\\Client::__call", cb); TWG(gc_runs) = GC_G(gc_runs); TWG(gc_collected) = GC_G(collected); TWG(compile_count) = 0; TWG(compile_wt) = 0; } /** * Initialize profiler state * * @author kannan, veeve */ void hp_init_profiler_state(TSRMLS_D) { if (!TWG(ever_enabled)) { TWG(ever_enabled) = 1; TWG(entries) = NULL; } TWG(max_spans) = INI_INT("tideways.max_spans"); #if PHP_VERSION_ID >= 70000 hp_ptr_dtor(&TWG(stats_count)); array_init(&TWG(stats_count)); hp_ptr_dtor(&TWG(spans)); array_init(&TWG(spans)); #else if (TWG(stats_count)) { hp_ptr_dtor(TWG(stats_count)); } _ALLOC_INIT_ZVAL(TWG(stats_count)); array_init(TWG(stats_count)); if (TWG(spans)) { hp_ptr_dtor(TWG(spans)); } _ALLOC_INIT_ZVAL(TWG(spans)); array_init(TWG(spans)); #endif hp_init_trace_callbacks(TSRMLS_C); } /** * Cleanup profiler state * * @author kannan, veeve */ void hp_clean_profiler_state(TSRMLS_D) { #if PHP_VERSION_ID >= 70000 hp_ptr_dtor(&TWG(stats_count)); ZVAL_NULL(&TWG(stats_count)); hp_ptr_dtor(&TWG(spans)); ZVAL_NULL(&TWG(spans)); #else if (TWG(stats_count)) { hp_ptr_dtor(TWG(stats_count)); TWG(stats_count) = NULL; } if (TWG(spans)) { hp_ptr_dtor(TWG(spans)); TWG(spans) = NULL; } #endif TWG(entries) = NULL; TWG(ever_enabled) = 0; hp_clean_profiler_options_state(TSRMLS_C); } static void hp_transaction_name_clear(TSRMLS_D) { if (TWG(transaction_name)) { zend_string_release(TWG(transaction_name)); TWG(transaction_name) = NULL; } } static void hp_clean_profiler_options_state(TSRMLS_D) { hp_function_map_clear(TWG(filtered_functions)); TWG(filtered_functions) = NULL; hp_exception_function_clear(TSRMLS_C); hp_transaction_function_clear(TSRMLS_C); hp_transaction_name_clear(TSRMLS_C); if (TWG(trace_callbacks)) { zend_hash_destroy(TWG(trace_callbacks)); FREE_HASHTABLE(TWG(trace_callbacks)); TWG(trace_callbacks) = NULL; } if (TWG(trace_watch_callbacks)) { zend_hash_destroy(TWG(trace_watch_callbacks)); FREE_HASHTABLE(TWG(trace_watch_callbacks)); TWG(trace_watch_callbacks) = NULL; } if (TWG(span_cache)) { zend_hash_destroy(TWG(span_cache)); FREE_HASHTABLE(TWG(span_cache)); TWG(span_cache) = NULL; } } /* * Start profiling - called just before calling the actual function * NOTE: PLEASE MAKE SURE TSRMLS_CC IS AVAILABLE IN THE CONTEXT * OF THE FUNCTION WHERE THIS MACRO IS CALLED. * TSRMLS_CC CAN BE MADE AVAILABLE VIA TSRMLS_DC IN THE * CALLING FUNCTION OR BY CALLING TSRMLS_FETCH() * TSRMLS_FETCH() IS RELATIVELY EXPENSIVE. */ #define BEGIN_PROFILING(entries, symbol, profile_curr, execute_data) \ do { \ /* Use a hash code to filter most of the string comparisons. */ \ uint8 hash_code = hp_inline_hash(symbol); \ profile_curr = !hp_filter_entry(hash_code, symbol TSRMLS_CC); \ if (profile_curr) { \ hp_entry_t *cur_entry = hp_fast_alloc_hprof_entry(TSRMLS_C); \ (cur_entry)->hash_code = hash_code; \ (cur_entry)->name_hprof = symbol; \ (cur_entry)->prev_hprof = (*(entries)); \ (cur_entry)->span_id = -1; \ hp_mode_hier_beginfn_cb((entries), (cur_entry), execute_data TSRMLS_CC); \ /* Update entries linked list */ \ (*(entries)) = (cur_entry); \ } \ } while (0) /* * Stop profiling - called just after calling the actual function * NOTE: PLEASE MAKE SURE TSRMLS_CC IS AVAILABLE IN THE CONTEXT * OF THE FUNCTION WHERE THIS MACRO IS CALLED. * TSRMLS_CC CAN BE MADE AVAILABLE VIA TSRMLS_DC IN THE * CALLING FUNCTION OR BY CALLING TSRMLS_FETCH() * TSRMLS_FETCH() IS RELATIVELY EXPENSIVE. */ #define END_PROFILING(entries, profile_curr, data) \ do { \ if (profile_curr) { \ hp_entry_t *cur_entry; \ hp_mode_hier_endfn_cb((entries), data TSRMLS_CC); \ cur_entry = (*(entries)); \ /* Free top entry and update entries linked list */ \ (*(entries)) = (*(entries))->prev_hprof; \ hp_fast_free_hprof_entry(cur_entry TSRMLS_CC); \ } \ } while (0) /** * Returns formatted function name * * @param entry hp_entry * @param result_buf ptr to result buf * @param result_len max size of result buf * @return total size of the function name returned in result_buf * @author veeve */ size_t hp_get_entry_name(hp_entry_t *entry, char *result_buf, size_t result_len) { /* Validate result_len */ if (result_len <= 1) { /* Insufficient result_bug. Bail! */ return 0; } /* Add '@recurse_level' if required */ /* NOTE: Dont use snprintf's return val as it is compiler dependent */ if (entry->rlvl_hprof) { snprintf( result_buf, result_len, "%s@%d", entry->name_hprof, entry->rlvl_hprof ); } else { strncat( result_buf, entry->name_hprof, result_len ); } /* Force null-termination at MAX */ result_buf[result_len - 1] = '\0'; return strlen(result_buf); } /** * Check if this entry should be filtered (positive or negative), first with a * conservative Bloomish filter then with an exact check against the function * names. * * @author mpal */ static inline int hp_filter_entry(uint8 hash_code, char *curr_func TSRMLS_DC) { int exists; /* First check if ignoring functions is enabled */ if (TWG(filtered_functions) == NULL || TWG(filtered_type) == 0) { return 0; } exists = hp_function_map_exists(TWG(filtered_functions), hash_code, curr_func); if (TWG(filtered_type) == 2) { // always include main() in profiling result. return (strcmp(curr_func, ROOT_SYMBOL) == 0) ? 0 : abs(1 - exists); } return exists; } /** * Build a caller qualified name for a callee. * * For example, if A() is caller for B(), then it returns "A==>B". * Recursive invokations are denoted with @ where n is the recursion * depth. * * For example, "foo==>foo@1", and "foo@2==>foo@3" are examples of direct * recursion. And "bar==>foo@1" is an example of an indirect recursive * call to foo (implying the foo() is on the call stack some levels * above). * * @author kannan, veeve */ size_t hp_get_function_stack(hp_entry_t *entry, int level, char *result_buf, size_t result_len) { size_t len = 0; if (!entry->prev_hprof || (level <= 1)) { return hp_get_entry_name(entry, result_buf, result_len); } len = hp_get_function_stack(entry->prev_hprof, level - 1, result_buf, result_len); /* Append the delimiter */ # define HP_STACK_DELIM "==>" # define HP_STACK_DELIM_LEN (sizeof(HP_STACK_DELIM) - 1) if (result_len < (len + HP_STACK_DELIM_LEN)) { return len; } if (len) { strncat(result_buf + len, HP_STACK_DELIM, result_len - len); len += HP_STACK_DELIM_LEN; } # undef HP_STACK_DELIM_LEN # undef HP_STACK_DELIM return len + hp_get_entry_name(entry, result_buf + len, result_len - len); } /** * Takes an input of the form /a/b/c/d/foo.php and returns * a pointer to one-level directory and basefile name * (d/foo.php) in the same string. */ static char *hp_get_base_filename(char *filename) { char *ptr; int found = 0; if (!filename) return ""; /* reverse search for "/" and return a ptr to the next char */ for (ptr = filename + strlen(filename) - 1; ptr >= filename; ptr--) { if (*ptr == '/') { found++; } if (found == 2) { return ptr + 1; } } /* no "/" char found, so return the whole string */ return filename; } static char *hp_get_file_summary(char *filename, int filename_len TSRMLS_DC) { php_url *url; char *ret; int len; len = TIDEWAYS_MAX_ARGUMENT_LEN; ret = emalloc(len); snprintf(ret, len, ""); url = php_url_parse_ex(filename, filename_len); if (url == NULL) { return ret; } if (url->scheme) { snprintf(ret, len, "%s%s://", ret, url->scheme); } else { php_url_free(url); return ret; } if (url->host) { snprintf(ret, len, "%s%s", ret, url->host); } if (url->port) { snprintf(ret, len, "%s:%d", ret, url->port); } if (url->path) { snprintf(ret, len, "%s%s", ret, url->path); } php_url_free(url); return ret; } static char *hp_concat_char(const char *s1, size_t len1, const char *s2, size_t len2, const char *seperator, size_t sep_len) { char *result = emalloc(len1+len2+sep_len+1); strcpy(result, s1); strcat(result, seperator); strcat(result, s2); result[len1+len2+sep_len] = '\0'; return result; } static void hp_detect_exception(char *func_name, zend_execute_data *data TSRMLS_DC) { int arg_count = ZEND_CALL_NUM_ARGS(data); zval *argument_element; int i; zend_class_entry *default_ce, *exception_ce; default_ce = zend_exception_get_default(TSRMLS_C); for (i=0; i < arg_count; i++) { argument_element = ZEND_CALL_ARG(data, i+1); if (Z_TYPE_P(argument_element) == IS_OBJECT) { exception_ce = Z_OBJCE_P(argument_element); if (instanceof_function(exception_ce, default_ce TSRMLS_CC) == 1) { #if PHP_VERSION_ID >= 70000 ZVAL_COPY(&TWG(exception), argument_element); #else Z_ADDREF_P(argument_element); TWG(exception) = argument_element; #endif return; } } } } static void hp_detect_transaction_name(char *ret, zend_execute_data *data TSRMLS_DC) { if (!TWG(transaction_function) || TWG(transaction_name) || strcmp(ret, ZSTR_VAL(TWG(transaction_function))) != 0) { return; } zval *argument_element; if (strcmp(ret, "Zend_Controller_Action::dispatch") == 0 || strcmp(ret, "Enlight_Controller_Action::dispatch") == 0 || strcmp(ret, "Mage_Core_Controller_Varien_Action::dispatch") == 0 || strcmp(ret, "Illuminate\\Routing\\Controller::callAction") == 0 || strcmp(ret, "yii\\base\\Module::runAction") == 0 || strcmp(ret, "CController::run") == 0) { // yii 1 if (ZEND_CALL_NUM_ARGS(data) == 0) { return; } zval *obj = EX_OBJ(data); argument_element = ZEND_CALL_ARG(data, 1); zend_class_entry *ce; int len; char *ctrl; if (Z_TYPE_P(argument_element) == IS_STRING) { ce = Z_OBJCE_P(obj); len = _ZCE_NAME_LENGTH(ce) + Z_STRLEN_P(argument_element) + 3; ctrl = (char*)emalloc(len); snprintf(ctrl, len, "%s::%s", _ZCE_NAME(ce), Z_STRVAL_P(argument_element)); ctrl[len-1] = '\0'; TWG(transaction_name) = zend_string_init(ctrl, len-1, 0); efree(ctrl); } } else if(strcmp(ret, "TYPO3\\CMS\\Extbase\\Mvc\\Controller\\ActionController::callActionMethod") == 0 || strcmp(ret, "TYPO3\\Flow\\Mvc\\Controller\\ActionController::callActionMethod") == 0) { zval *property; zval *object = EX_OBJ(data); zend_class_entry *ce; int len; char *ctrl; if (object == NULL) { return; } ce = Z_OBJCE_P(object); zval *__rv; property = _zend_read_property(ce, object, "actionMethodName", sizeof("actionMethodName") - 1, 1, __rv); if (property == NULL || Z_TYPE_P(property) != IS_STRING) { return; } len = _ZCE_NAME_LENGTH(ce) + Z_STRLEN_P(property) + 3; ctrl = (char*)emalloc(len); snprintf(ctrl, len, "%s::%s", _ZCE_NAME(ce), Z_STRVAL_P(property)); ctrl[len-1] = '\0'; TWG(transaction_name) = zend_string_init(ctrl, len-1, 0); efree(ctrl); } else if (strcmp(ret, "Controller::invokeAction") == 0 || strcmp(ret, "Cake\\Controller\\Controller::invokeAction") == 0) { zend_string *ctrl = tw_extract_cakephp_controller_name(ret, data TSRMLS_CC); if (ctrl == NULL) { return; } TWG(transaction_name) = ctrl; } else { if (ZEND_CALL_NUM_ARGS(data) == 0) { return; } argument_element = ZEND_CALL_ARG(data, 1); if (Z_TYPE_P(argument_element) == IS_STRING) { TWG(transaction_name) = zend_string_copy(Z_STR_P(argument_element)); } } hp_transaction_function_clear(TSRMLS_C); } /** * Get the name of the current function. The name is qualified with * the class name if the function is in a class. * * @author kannan, hzhao */ static char *hp_get_function_name(zend_execute_data *data TSRMLS_DC) { const char *cls = NULL; char *ret = NULL; zend_function *curr_func; if (!data) { return NULL; } #if PHP_VERSION_ID < 70000 const char *func = NULL; curr_func = data->function_state.function; func = curr_func->common.function_name; if (!func) { // This branch includes execution of eval and include/require(_once) calls // We assume it is not 1999 anymore and not much PHP code runs in the // body of a file and if it is, we are ok with adding it to the caller's wt. return NULL; } /* previously, the order of the tests in the "if" below was * flipped, leading to incorrect function names in profiler * reports. When a method in a super-type is invoked the * profiler should qualify the function name with the super-type * class name (not the class name based on the run-time type * of the object. */ if (curr_func->common.scope) { cls = curr_func->common.scope->name; } else if (data->object) { cls = Z_OBJCE(*data->object)->name; } if (cls) { char* sep = "::"; ret = hp_concat_char(cls, strlen(cls), func, strlen(func), sep, 2); } else { ret = estrdup(func); } #else zend_string *func = NULL; curr_func = data->func; func = curr_func->common.function_name; if (!func) { return NULL; } else if (curr_func->common.scope != NULL) { char* sep = "::"; cls = curr_func->common.scope->name->val; ret = hp_concat_char(cls, curr_func->common.scope->name->len, func->val, func->len, sep, 2); } else { ret = emalloc(ZSTR_LEN(func)+1); strcpy(ret, ZSTR_VAL(func)); ret[ZSTR_LEN(func)] = '\0'; } #endif return ret; } /** * Free any items in the free list. */ static void hp_free_the_free_list(TSRMLS_D) { hp_entry_t *p = TWG(entry_free_list); hp_entry_t *cur; while (p) { cur = p; p = p->prev_hprof; free(cur); } } /** * Fast allocate a hp_entry_t structure. Picks one from the * free list if available, else does an actual allocate. * * Doesn't bother initializing allocated memory. * * @author kannan */ static hp_entry_t *hp_fast_alloc_hprof_entry(TSRMLS_D) { hp_entry_t *p; p = TWG(entry_free_list); if (p) { TWG(entry_free_list) = p->prev_hprof; return p; } else { return (hp_entry_t *)malloc(sizeof(hp_entry_t)); } } /** * Fast free a hp_entry_t structure. Simply returns back * the hp_entry_t to a free list and doesn't actually * perform the free. * * @author kannan */ static void hp_fast_free_hprof_entry(hp_entry_t *p TSRMLS_DC) { /* we use/overload the prev_hprof field in the structure to link entries in * the free list. */ p->prev_hprof = TWG(entry_free_list); TWG(entry_free_list) = p; } /** * Increment the count of the given stat with the given count * If the stat was not set before, inits the stat to the given count * * @param zval *counts Zend hash table pointer * @param char *name Name of the stat * @param long count Value of the stat to incr by * @return void * @author kannan */ void hp_inc_count(zval *counts, char *name, long count TSRMLS_DC) { HashTable *ht; zval *data, val; if (!counts) { return; } ht = HASH_OF(counts); if (!ht) { return; } data = zend_compat_hash_find_const(ht, name, strlen(name)); if (data) { ZVAL_LONG(data, Z_LVAL_P(data) + count); } else { #if PHP_VERSION_ID >= 70000 ZVAL_LONG(&val, count); zend_hash_str_update(ht, name, strlen(name), &val); #else add_assoc_long(counts, name, count); #endif } } /** * *********************** * High precision timer related functions. * *********************** */ /** * Get the current wallclock timer * * @return 64 bit unsigned integer * @author cjiang */ static uint64 cycle_timer() { #ifdef __APPLE__ return mach_absolute_time(); #else struct timespec s; clock_gettime(CLOCK_MONOTONIC, &s); return s.tv_sec * 1000000 + s.tv_nsec / 1000; #endif } /** * Get the current real CPU clock timer */ static uint64 cpu_timer() { #if defined(CLOCK_PROCESS_CPUTIME_ID) struct timespec s; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &s); return s.tv_sec * 1000000 + s.tv_nsec / 1000; #else struct rusage ru; getrusage(RUSAGE_SELF, &ru); return ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec + ru.ru_stime.tv_sec * 1000000 + ru.ru_stime.tv_usec; #endif } /** * Get time delta in microseconds. */ static long get_us_interval(struct timeval *start, struct timeval *end) { return (((end->tv_sec - start->tv_sec) * 1000000) + (end->tv_usec - start->tv_usec)); } /** * Convert from TSC counter values to equivalent microseconds. * * @param uint64 count, TSC count value * @return 64 bit unsigned integer * * @author cjiang */ static inline double get_us_from_tsc(uint64 count TSRMLS_DC) { return count / TWG(timebase_factor); } /** * Get the timebase factor necessary to divide by in cycle_timer() */ static double get_timebase_factor() { #ifdef __APPLE__ mach_timebase_info_data_t sTimebaseInfo; (void) mach_timebase_info(&sTimebaseInfo); return (sTimebaseInfo.numer / sTimebaseInfo.denom) * 1000; #else return 1.0; #endif } /** * TIDEWAYS_MODE_HIERARCHICAL's begin function callback * * @author kannan */ void hp_mode_hier_beginfn_cb(hp_entry_t **entries, hp_entry_t *current, zend_execute_data *data TSRMLS_DC) { hp_entry_t *p; tw_trace_callback *callback; int recurse_level = 0; if ((TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_SPANS) == 0 && data != NULL) { #if PHP_VERSION_ID < 70000 if (zend_hash_find(TWG(trace_callbacks), current->name_hprof, strlen(current->name_hprof)+1, (void **)&callback) == SUCCESS) { current->span_id = (*callback)(current->name_hprof, data TSRMLS_CC); } #else callback = (tw_trace_callback*)zend_hash_str_find_ptr(TWG(trace_callbacks), current->name_hprof, strlen(current->name_hprof)); if (callback != NULL) { current->span_id = (*callback)(current->name_hprof, data TSRMLS_CC); } #endif } if ((TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_HIERACHICAL) == 0) { if (TWG(func_hash_counters)[current->hash_code] > 0) { /* Find this symbols recurse level */ for(p = (*entries); p; p = p->prev_hprof) { if (!strcmp(current->name_hprof, p->name_hprof)) { recurse_level = (p->rlvl_hprof) + 1; break; } } } TWG(func_hash_counters)[current->hash_code]++; /* Init current function's recurse level */ current->rlvl_hprof = recurse_level; /* Get CPU usage */ if (TWG(tideways_flags) & TIDEWAYS_FLAGS_CPU) { current->cpu_start = cpu_timer(); } /* Get memory usage */ if (TWG(tideways_flags) & TIDEWAYS_FLAGS_MEMORY) { current->mu_start_hprof = zend_memory_usage(0 TSRMLS_CC); current->pmu_start_hprof = zend_memory_peak_usage(0 TSRMLS_CC); } if (current->span_id >= 0) { tw_span_annotate_string(current->span_id, "fn", current->name_hprof, 1 TSRMLS_CC); } } /* Get start tsc counter */ current->tsc_start = cycle_timer(); } /** * ********************************** * TIDEWAYS END FUNCTION CALLBACKS * ********************************** */ /** * TIDEWAYS_MODE_HIERARCHICAL's end function callback * * @author kannan */ void hp_mode_hier_endfn_cb(hp_entry_t **entries, zend_execute_data *data TSRMLS_DC) { hp_entry_t *top = (*entries); zval *counts, count_val; char symbol[SCRATCH_BUF_LEN] = ""; long int mu_end; long int pmu_end; uint64 tsc_end; double wt, cpu; tw_trace_callback *callback; /* Get end tsc counter */ tsc_end = cycle_timer(); wt = get_us_from_tsc(tsc_end - top->tsc_start TSRMLS_CC); if (TWG(tideways_flags) & TIDEWAYS_FLAGS_CPU) { cpu = get_us_from_tsc(cpu_timer() - top->cpu_start TSRMLS_CC); } if ((TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_SPANS) == 0 && top->span_id >= 0) { double start = get_us_from_tsc(top->tsc_start - TWG(start_time) TSRMLS_CC); double end = get_us_from_tsc(tsc_end - TWG(start_time) TSRMLS_CC); tw_span_record_duration(top->span_id, start, end TSRMLS_CC); } if ((TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_HIERACHICAL) > 0) { return; } /* Get the stat array */ hp_get_function_stack(top, 2, symbol, sizeof(symbol)); counts = zend_compat_hash_find_const(TWG_ARRVAL(TWG(stats_count)), symbol, strlen(symbol)); if (counts == NULL) { #if PHP_VERSION_ID >= 70000 counts = &count_val; array_init(counts); zend_hash_str_update(TWG_ARRVAL(TWG(stats_count)), symbol, strlen(symbol), counts); #else MAKE_STD_ZVAL(counts); array_init(counts); zend_hash_update(TWG_ARRVAL(TWG(stats_count)), symbol, strlen(symbol)+1, &counts, sizeof(zval*), NULL); #endif } /* Bump stats in the counts hashtable */ hp_inc_count(counts, "ct", 1 TSRMLS_CC); hp_inc_count(counts, "wt", wt TSRMLS_CC); if (TWG(tideways_flags) & TIDEWAYS_FLAGS_CPU) { /* Bump CPU stats in the counts hashtable */ hp_inc_count(counts, "cpu", cpu TSRMLS_CC); } if (TWG(tideways_flags) & TIDEWAYS_FLAGS_MEMORY) { /* Get Memory usage */ mu_end = zend_memory_usage(0 TSRMLS_CC); pmu_end = zend_memory_peak_usage(0 TSRMLS_CC); /* Bump Memory stats in the counts hashtable */ hp_inc_count(counts, "mu", mu_end - top->mu_start_hprof TSRMLS_CC); hp_inc_count(counts, "pmu", pmu_end - top->pmu_start_hprof TSRMLS_CC); } TWG(func_hash_counters)[top->hash_code]--; } /** * *************************** * PHP EXECUTE/COMPILE PROXIES * *************************** */ /** * Tideways enable replaced the zend_execute function with this * new execute function. We can do whatever profiling we need to * before and after calling the actual zend_execute(). * * @author hzhao, kannan */ #if PHP_VERSION_ID >= 70000 ZEND_DLEXPORT void hp_execute_ex (zend_execute_data *execute_data) { zend_execute_data *real_execute_data = execute_data; #elif PHP_VERSION_ID < 50500 ZEND_DLEXPORT void hp_execute (zend_op_array *ops TSRMLS_DC) { zend_execute_data *execute_data = EG(current_execute_data); zend_execute_data *real_execute_data = execute_data; #else ZEND_DLEXPORT void hp_execute_ex (zend_execute_data *execute_data TSRMLS_DC) { zend_op_array *ops = execute_data->op_array; zend_execute_data *real_execute_data = execute_data->prev_execute_data; #endif char *func = NULL; int hp_profile_flag = 1; if (!TWG(enabled)) { #if PHP_VERSION_ID < 50500 _zend_execute(ops TSRMLS_CC); #else _zend_execute_ex(execute_data TSRMLS_CC); #endif return; } func = hp_get_function_name(real_execute_data TSRMLS_CC); if (!func) { #if PHP_VERSION_ID < 50500 _zend_execute(ops TSRMLS_CC); #else _zend_execute_ex(execute_data TSRMLS_CC); #endif return; } hp_detect_transaction_name(func, real_execute_data TSRMLS_CC); if (TWG(exception_function) != NULL && strcmp(func, ZSTR_VAL(TWG(exception_function))) == 0) { hp_detect_exception(func, real_execute_data TSRMLS_CC); } if ((TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_USERLAND) > 0) { #if PHP_VERSION_ID < 50500 _zend_execute(ops TSRMLS_CC); #else _zend_execute_ex(execute_data TSRMLS_CC); #endif efree(func); return; } BEGIN_PROFILING(&TWG(entries), func, hp_profile_flag, real_execute_data); #if PHP_VERSION_ID < 50500 _zend_execute(ops TSRMLS_CC); #else _zend_execute_ex(execute_data TSRMLS_CC); #endif if (TWG(entries)) { END_PROFILING(&TWG(entries), hp_profile_flag, real_execute_data); } efree(func); } #undef EX #define EX(element) ((execute_data)->element) /** * Very similar to hp_execute. Proxy for zend_execute_internal(). * Applies to zend builtin functions. * * @author hzhao, kannan */ #if PHP_VERSION_ID >= 70000 ZEND_DLEXPORT void hp_execute_internal(zend_execute_data *execute_data, zval *return_value) { #elif PHP_VERSION_ID < 50500 #define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset)) ZEND_DLEXPORT void hp_execute_internal(zend_execute_data *execute_data, int ret TSRMLS_DC) { #else #define EX_T(offset) (*EX_TMP_VAR(execute_data, offset)) ZEND_DLEXPORT void hp_execute_internal(zend_execute_data *execute_data, struct _zend_fcall_info *fci, int ret TSRMLS_DC) { #endif char *func = NULL; int hp_profile_flag = 1; if (!TWG(enabled) || (TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_BUILTINS) > 0) { #if PHP_MAJOR_VERSION == 7 execute_internal(execute_data, return_value TSRMLS_CC); #elif PHP_VERSION_ID < 50500 execute_internal(execute_data, ret TSRMLS_CC); #else execute_internal(execute_data, fci, ret TSRMLS_CC); #endif return; } func = hp_get_function_name(execute_data TSRMLS_CC); if (func) { BEGIN_PROFILING(&TWG(entries), func, hp_profile_flag, execute_data); } if (!_zend_execute_internal) { #if PHP_VERSION_ID >= 70000 execute_internal(execute_data, return_value TSRMLS_CC); #elif PHP_VERSION_ID < 50500 execute_internal(execute_data, ret TSRMLS_CC); #else execute_internal(execute_data, fci, ret TSRMLS_CC); #endif } else { /* call the old override */ #if PHP_VERSION_ID >= 70000 _zend_execute_internal(execute_data, return_value TSRMLS_CC); #elif PHP_VERSION_ID < 50500 _zend_execute_internal(execute_data, ret TSRMLS_CC); #else _zend_execute_internal(execute_data, fci, ret TSRMLS_CC); #endif } if (func) { if (TWG(entries)) { END_PROFILING(&TWG(entries), hp_profile_flag, execute_data); } efree(func); } } /** * Proxy for zend_compile_file(). Used to profile PHP compilation time. * * @author kannan, hzhao */ ZEND_DLEXPORT zend_op_array* hp_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) { if (!TWG(enabled) || (TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_COMPILE) > 0) { return _zend_compile_file(file_handle, type TSRMLS_CC); } zend_op_array *ret; uint64 start = cycle_timer(); TWG(compile_count)++; ret = _zend_compile_file(file_handle, type TSRMLS_CC); TWG(compile_wt) += get_us_from_tsc(cycle_timer() - start TSRMLS_CC); return ret; } /** * Proxy for zend_compile_string(). Used to profile PHP eval compilation time. */ ZEND_DLEXPORT zend_op_array* hp_compile_string(zval *source_string, char *filename TSRMLS_DC) { if (!TWG(enabled) || (TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_COMPILE) > 0) { return _zend_compile_string(source_string, filename TSRMLS_CC); } zend_op_array *ret; uint64 start = cycle_timer(); TWG(compile_count)++; ret = _zend_compile_string(source_string, filename TSRMLS_CC); TWG(compile_wt) += get_us_from_tsc(cycle_timer() - start TSRMLS_CC); return ret; } /** * ************************** * MAIN TIDEWAYS CALLBACKS * ************************** */ /** * This function gets called once when Tideways gets enabled. * It replaces all the functions like zend_execute, zend_execute_internal, * etc that needs to be instrumented with their corresponding proxies. */ static void hp_begin(long tideways_flags TSRMLS_DC) { if (!TWG(enabled)) { int hp_profile_flag = 1; TWG(enabled) = 1; TWG(tideways_flags) = (uint32)tideways_flags; /* one time initializations */ hp_init_profiler_state(TSRMLS_C); /* start profiling from fictitious main() */ TWG(root) = estrdup(ROOT_SYMBOL); TWG(start_time) = cycle_timer(); if ((TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_SPANS) == 0) { TWG(cpu_start) = cpu_timer(); } tw_span_create("app", 3 TSRMLS_CC); tw_span_timer_start(0 TSRMLS_CC); BEGIN_PROFILING(&TWG(entries), TWG(root), hp_profile_flag, NULL); } } /** * Called at request shutdown time. Cleans the profiler's global state. */ static void hp_end(TSRMLS_D) { /* Bail if not ever enabled */ if (!TWG(ever_enabled)) { return; } /* Stop profiler if enabled */ if (TWG(enabled)) { hp_stop(TSRMLS_C); } /* Clean up state */ hp_clean_profiler_state(TSRMLS_C); } /** * Called from tideways_disable(). Removes all the proxies setup by * hp_begin() and restores the original values. */ static void hp_stop(TSRMLS_D) { int hp_profile_flag = 1; /* End any unfinished calls */ while (TWG(entries)) { END_PROFILING(&TWG(entries), hp_profile_flag, NULL); } tw_span_timer_stop(0 TSRMLS_CC); if ((TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_SPANS) == 0) { if ((GC_G(gc_runs) - TWG(gc_runs)) > 0) { tw_span_annotate_long(0, "gc", GC_G(gc_runs) - TWG(gc_runs) TSRMLS_CC); tw_span_annotate_long(0, "gcc", GC_G(collected) - TWG(gc_collected) TSRMLS_CC); } if (TWG(compile_count) > 0) { tw_span_annotate_long(0, "cct", TWG(compile_count) TSRMLS_CC); } if (TWG(compile_wt) > 0) { tw_span_annotate_long(0, "cwt", TWG(compile_wt) TSRMLS_CC); } tw_span_annotate_long(0, "cpu", get_us_from_tsc(cpu_timer() - TWG(cpu_start) TSRMLS_CC) TSRMLS_CC); } if (TWG(root)) { efree(TWG(root)); TWG(root) = NULL; } /* Stop profiling */ TWG(enabled) = 0; } /** * ***************************** * TIDEWAYS ZVAL UTILITY FUNCTIONS * ***************************** */ /** Look in the PHP assoc array to find a key and return the zval associated * with it. * * @author mpal **/ static zval *hp_zval_at_key(char *key, size_t size, zval *values) { if (Z_TYPE_P(values) == IS_ARRAY) { HashTable *ht = Z_ARRVAL_P(values); return zend_compat_hash_find_const(ht, key, size-1); } return NULL; } /** * Convert the PHP array of strings to an emalloced array of strings. Note, * this method duplicates the string data in the PHP array. * * @author mpal **/ static char **hp_strings_in_zval(zval *values) { char **result; size_t count; size_t ix = 0; #if PHP_VERSION_ID < 70000 char *str; #else zend_string *str; #endif uint len; ulong idx; int type; zval **data, *val; if (!values) { return NULL; } if (Z_TYPE_P(values) == IS_ARRAY) { HashTable *ht; ht = Z_ARRVAL_P(values); count = zend_hash_num_elements(ht); if((result = (char**)emalloc(sizeof(char*) * (count + 1))) == NULL) { return result; } #if PHP_VERSION_ID < 70000 for (zend_hash_internal_pointer_reset(ht); zend_hash_has_more_elements(ht) == SUCCESS; zend_hash_move_forward(ht)) { type = zend_hash_get_current_key_ex(ht, &str, &len, &idx, 0, NULL); if (type == HASH_KEY_IS_LONG) { if ((zend_hash_get_current_data(ht, (void**)&data) == SUCCESS) && Z_TYPE_PP(data) == IS_STRING && strcmp(Z_STRVAL_PP(data), ROOT_SYMBOL)) { /* do not ignore "main" */ result[ix] = estrdup(Z_STRVAL_PP(data)); ix++; } } else if (type == HASH_KEY_IS_STRING) { result[ix] = estrdup(str); ix++; } } #else ZEND_HASH_FOREACH_KEY_VAL(ht, idx, str, val) { if (str) { result[ix] = estrdup(ZSTR_VAL(str)); } else { result[ix] = estrdup(Z_STRVAL_P(val)); } ix++; } ZEND_HASH_FOREACH_END(); #endif } else if(Z_TYPE_P(values) == IS_STRING) { if((result = (char**)emalloc(sizeof(char*) * 2)) == NULL) { return result; } result[0] = estrdup(Z_STRVAL_P(values)); ix = 1; } else { result = NULL; } /* NULL terminate the array */ if (result != NULL) { result[ix] = NULL; } return result; } /* Free this memory at the end of profiling */ static inline void hp_array_del(char **name_array) { if (name_array != NULL) { int i = 0; for(; name_array[i] != NULL && i < TIDEWAYS_MAX_FILTERED_FUNCTIONS; i++) { efree(name_array[i]); } efree(name_array); } } #if PHP_VERSION_ID >= 70000 int tw_gc_collect_cycles(void) { int ret; long spanId; if (!TWG(enabled) || (TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_SPANS) > 0) { return tw_original_gc_collect_cycles(); } spanId = tw_span_create("gc", 2 TSRMLS_CC); tw_span_timer_start(spanId TSRMLS_CC); if (TWG(entries)) { tw_span_annotate_string(spanId, "title", TWG(entries)->name_hprof, 1 TSRMLS_CC); } ret = tw_original_gc_collect_cycles(); tw_span_timer_stop(spanId TSRMLS_CC); return ret; } #endif #if PHP_VERSION_ID < 70000 void tideways_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) { TSRMLS_FETCH(); error_handling_t error_handling; zval *backtrace; #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 3) || PHP_MAJOR_VERSION >= 6 error_handling = EG(error_handling); #else error_handling = PG(error_handling); #endif if (error_handling == EH_NORMAL) { switch (type) { case E_ERROR: case E_CORE_ERROR: ALLOC_INIT_ZVAL(backtrace); #if PHP_VERSION_ID <= 50399 zend_fetch_debug_backtrace(backtrace, 1, 0 TSRMLS_CC); #else zend_fetch_debug_backtrace(backtrace, 1, 0, 0 TSRMLS_CC); #endif TWG(backtrace) = backtrace; } } tideways_original_error_cb(type, error_filename, error_lineno, format, args); } #else static void tideways_throw_exception_hook(zval *exception TSRMLS_DC) { zend_class_entry *exception_ce, *ex; if (!exception) { return; } exception_ce = Z_OBJCE_P(exception); if (instanceof_function(exception_ce, zend_ce_error)) { ZVAL_COPY(&TWG(exception), exception); } } #endif PHP_FUNCTION(tideways_span_watch) { char *func = NULL, *category = NULL; strsize_t func_len, category_len; tw_trace_callback cb; if (!TWG(enabled) || (TWG(tideways_flags) & TIDEWAYS_FLAGS_NO_SPANS) > 0) { return; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!", &func, &func_len, &category, &category_len) == FAILURE) { return; } if (category != NULL && strcmp(category, "view") == 0) { cb = tw_trace_callback_view_engine; } else if (category != NULL && strcmp(category, "event") == 0) { cb = tw_trace_callback_event_dispatchers; } else { cb = tw_trace_callback_php_call; } register_trace_callback_len(func, func_len, cb); } #if PHP_VERSION_ID >= 70000 static void free_tw_watch_callback(zval *zv) { tw_watch_callback *twcb = (tw_watch_callback*)Z_PTR_P(zv); if (Z_TYPE(twcb->fci.function_name) != IS_UNDEF) { zval_ptr_dtor(&twcb->fci.function_name); } if (twcb->fci.object) { zend_object_release(twcb->fci.object); } efree(twcb); } #else static void free_tw_watch_callback(void *twcb) { tw_watch_callback *_twcb = *((tw_watch_callback **)twcb); if (_twcb->fci.function_name) { zval_ptr_dtor((zval **)&_twcb->fci.function_name); } if (_twcb->fci.object_ptr) { zval_ptr_dtor((zval **)&_twcb->fci.object_ptr); } efree(_twcb); } #endif static void tideways_add_callback_watch(zend_fcall_info fci, zend_fcall_info_cache fcic, char *func, int func_len TSRMLS_DC) { tw_watch_callback *twcb; tw_trace_callback cb; twcb = emalloc(sizeof(tw_watch_callback)); twcb->fci = fci; twcb->fcic = fcic; if (TWG(trace_watch_callbacks) == NULL) { ALLOC_HASHTABLE(TWG(trace_watch_callbacks)); zend_hash_init(TWG(trace_watch_callbacks), 255, NULL, free_tw_watch_callback, 0); } #if PHP_VERSION_ID < 70000 zend_hash_update(TWG(trace_watch_callbacks), func, func_len+1, &twcb, sizeof(tw_watch_callback*), NULL); #else zend_hash_str_update_mem(TWG(trace_watch_callbacks), func, func_len, twcb, sizeof(tw_watch_callback)); efree(twcb); // zend_hash_str_update_mem makes a copy #endif cb = tw_trace_callback_watch; register_trace_callback_len(func, func_len, cb); } PHP_FUNCTION(tideways_span_callback) { zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcic = empty_fcall_info_cache; char *func; strsize_t func_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &func, &func_len, &fci, &fcic) == FAILURE) { zend_error(E_ERROR, "tideways_span_callback() expects a string as a first and a callback as a second argument"); return; } #if PHP_VERSION_ID < 70000 if (fci.size) { Z_ADDREF_P(fci.function_name); if (fci.object_ptr) { Z_ADDREF_P(fci.object_ptr); } } #else if (fci.size > 0) { Z_TRY_ADDREF(fci.function_name); if (fci.object != NULL) { GC_REFCOUNT(fci.object)++; } } #endif tideways_add_callback_watch(fci, fcic, func, func_len TSRMLS_CC); } /** * ********************************** * PHP EXTENSION FUNCTION DEFINITIONS * ********************************** */ /** * Start Tideways profiling in hierarchical mode. * * @param long $flags flags for hierarchical mode * @return void * @author kannan */ PHP_FUNCTION(tideways_enable) { zend_long tideways_flags = 0; zval *optional_array = NULL; if (TWG(enabled)) { hp_stop(TSRMLS_C); } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lz", &tideways_flags, &optional_array) == FAILURE) { return; } hp_parse_options_from_arg(optional_array TSRMLS_CC); hp_begin(tideways_flags TSRMLS_CC); } /** * Stops Tideways from profiling and returns the profile info. * * @param void * @return array hash-array of Tideways's profile info * @author cjiang */ PHP_FUNCTION(tideways_disable) { if (!TWG(enabled)) { return; } hp_stop(TSRMLS_C); #if PHP_VERSION_ID >= 70000 RETURN_ZVAL(&TWG(stats_count), 1, 0); #else RETURN_ZVAL(TWG(stats_count), 1, 0); #endif } PHP_FUNCTION(tideways_transaction_name) { if (TWG(transaction_name)) { RETURN_STR_COPY(TWG(transaction_name)); } } PHP_FUNCTION(tideways_prepend_overwritten) { RETURN_BOOL(TWG(prepend_overwritten)); } PHP_FUNCTION(tideways_fatal_backtrace) { if (TWG(backtrace) != NULL) { RETURN_ZVAL(TWG(backtrace), 1, 1); } } PHP_FUNCTION(tideways_last_detected_exception) { #if PHP_VERSION_ID >= 70000 RETURN_ZVAL(&TWG(exception), 1, 0); #else if (TWG(exception) != NULL) { RETURN_ZVAL(TWG(exception), 1, 0); } #endif } PHP_FUNCTION(tideways_last_fatal_error) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) { return; } if (PG(last_error_message)) { array_init(return_value); #if PHP_VERSION_ID < 70000 add_assoc_long_ex(return_value, "type", sizeof("type"), PG(last_error_type)); add_assoc_string_ex(return_value, "message", sizeof("message"), PG(last_error_message), 1); add_assoc_string_ex(return_value, "file", sizeof("file"), PG(last_error_file)?PG(last_error_file):"-", 1 ); add_assoc_long_ex(return_value, "line", sizeof("line"), PG(last_error_lineno)); #else add_assoc_long_ex(return_value, "type", sizeof("type")-1, PG(last_error_type)); _add_assoc_string_ex(return_value, "message", sizeof("message"), PG(last_error_message), 1); _add_assoc_string_ex(return_value, "file", sizeof("file"), PG(last_error_file)?PG(last_error_file):"-", 1); add_assoc_long_ex(return_value, "line", sizeof("line")-1, PG(last_error_lineno)); #endif } } PHP_FUNCTION(tideways_span_create) { char *category = NULL; strsize_t category_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &category, &category_len) == FAILURE) { return; } if (TWG(enabled) == 0) { return; } RETURN_LONG(tw_span_create(category, category_len TSRMLS_CC)); } PHP_FUNCTION(tideways_get_spans) { #if PHP_VERSION_ID >= 70000 RETURN_ZVAL(&TWG(spans), 1, 0); #else if (TWG(spans)) { RETURN_ZVAL(TWG(spans), 1, 0); } #endif } PHP_FUNCTION(tideways_span_timer_start) { zend_long spanId; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spanId) == FAILURE) { return; } if (TWG(enabled )== 0) { return; } tw_span_timer_start(spanId TSRMLS_CC); } PHP_FUNCTION(tideways_span_timer_stop) { zend_long spanId; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spanId) == FAILURE) { return; } if (TWG(enabled )== 0) { return; } tw_span_timer_stop(spanId TSRMLS_CC); } PHP_FUNCTION(tideways_span_annotate) { zend_long spanId; zval *annotations; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz", &spanId, &annotations) == FAILURE) { return; } // Yes, annotations are still possible when profiler is deactivated! tw_span_annotate(spanId, annotations TSRMLS_CC); } PHP_FUNCTION(tideways_sql_minify) { RETURN_EMPTY_STRING(); }