pax_global_header 0000666 0000000 0000000 00000000064 13011317701 0014504 g ustar 00root root 0000000 0000000 52 comment=24206ff6c5158989ca8134ca02db613994d7badf
php-profiler-extension-4.0.7/ 0000775 0000000 0000000 00000000000 13011317701 0016135 5 ustar 00root root 0000000 0000000 php-profiler-extension-4.0.7/.gitignore 0000664 0000000 0000000 00000001312 13011317701 0020122 0 ustar 00root root 0000000 0000000 /.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.yml 0000664 0000000 0000000 00000000542 13011317701 0020247 0 ustar 00root root 0000000 0000000 language: 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.md 0000664 0000000 0000000 00000021020 13011317701 0017741 0 ustar 00root root 0000000 0000000 # 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/LICENSE 0000664 0000000 0000000 00000023676 13011317701 0017160 0 ustar 00root root 0000000 0000000
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/NOTICE 0000664 0000000 0000000 00000001015 13011317701 0017036 0 ustar 00root root 0000000 0000000 Tideways 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.md 0000664 0000000 0000000 00000003656 13011317701 0017426 0 ustar 00root root 0000000 0000000 # Tideways PHP Profiler Extension
[](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.xml 0000664 0000000 0000000 00000001354 13011317701 0017761 0 ustar 00root root 0000000 0000000
php-profiler-extension-4.0.7/config.m4 0000664 0000000 0000000 00000004613 13011317701 0017650 0 ustar 00root root 0000000 0000000
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/ 0000775 0000000 0000000 00000000000 13011317701 0017011 5 ustar 00root root 0000000 0000000 php-profiler-extension-4.0.7/php5/spans.c 0000664 0000000 0000000 00000010273 13011317701 0020304 0 ustar 00root root 0000000 0000000 #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/ 0000775 0000000 0000000 00000000000 13011317701 0017013 5 ustar 00root root 0000000 0000000 php-profiler-extension-4.0.7/php7/spans.c 0000664 0000000 0000000 00000007612 13011317701 0020311 0 ustar 00root root 0000000 0000000 #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.h 0000664 0000000 0000000 00000015770 13011317701 0021020 0 ustar 00root root 0000000 0000000 /*
* 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.h 0000664 0000000 0000000 00000000537 13011317701 0017437 0 ustar 00root root 0000000 0000000 #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/ 0000775 0000000 0000000 00000000000 13011317701 0017277 5 ustar 00root root 0000000 0000000 php-profiler-extension-4.0.7/tests/common.php 0000664 0000000 0000000 00000007457 13011317701 0021315 0 ustar 00root root 0000000 0000000 $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.php 0000664 0000000 0000000 00000000157 13011317701 0022651 0 ustar 00root root 0000000 0000000
php-profiler-extension-4.0.7/tests/memleak.phpt 0000664 0000000 0000000 00000000262 13011317701 0021607 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000003543 13011317701 0022413 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000002637 13011317701 0022417 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000002163 13011317701 0022412 0 ustar 00root root 0000000 0000000 --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.php 0000664 0000000 0000000 00000000674 13011317701 0023064 0 ustar 00root root 0000000 0000000 "
// 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.php 0000664 0000000 0000000 00000000732 13011317701 0023762 0 ustar 00root root 0000000 0000000 "
// 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.phpt 0000664 0000000 0000000 00000003201 13011317701 0022405 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000023545 13011317701 0022424 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000000765 13011317701 0022425 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001047 13011317701 0022417 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000007501 13011317701 0022414 0 ustar 00root root 0000000 0000000 --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.php 0000664 0000000 0000000 00000001746 13011317701 0023752 0 ustar 00root root 0000000 0000000 callActionMethod();
}
protected function callActionMethod() {}
}
class FooController extends ActionController {}
}
php-profiler-extension-4.0.7/tests/tideways_024.phpt 0000664 0000000 0000000 00000002071 13011317701 0022412 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000000364 13011317701 0022417 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000000353 13011317701 0022415 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000000717 13011317701 0022423 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000000256 13011317701 0022423 0 ustar 00root root 0000000 0000000 --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.php 0000664 0000000 0000000 00000003254 13011317701 0023534 0 ustar 00root root 0000000 0000000 _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.phpt 0000664 0000000 0000000 00000001743 13011317701 0024006 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001626 13011317701 0024007 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001171 13011317701 0024003 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001030 13011317701 0023776 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001140 13011317701 0024001 0 ustar 00root root 0000000 0000000 --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.php 0000664 0000000 0000000 00000002423 13011317701 0023307 0 ustar 00root root 0000000 0000000 doDispatch(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.php 0000664 0000000 0000000 00000001536 13011317701 0023314 0 ustar 00root root 0000000 0000000 name = $name;
}
public function name()
{
return $this->name;
}
}
php-profiler-extension-4.0.7/tests/tideways_fw_magento_001.phpt 0000664 0000000 0000000 00000001566 13011317701 0024623 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001547 13011317701 0024303 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001350 13011317701 0025010 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001243 13011317701 0024665 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001460 13011317701 0025212 0 ustar 00root root 0000000 0000000 --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.php 0000664 0000000 0000000 00000000674 13011317701 0023054 0 ustar 00root root 0000000 0000000 _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.phpt 0000664 0000000 0000000 00000002344 13011317701 0023614 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000002116 13011317701 0023612 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001466 13011317701 0023622 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001045 13011317701 0023614 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000003306 13011317701 0023620 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001734 13011317701 0023624 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001650 13011317701 0023622 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000002204 13011317701 0023612 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000000750 13011317701 0023617 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001057 13011317701 0023621 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000004620 13011317701 0023622 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001176 13011317701 0023626 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000003730 13011317701 0023625 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000000601 13011317701 0023607 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000002632 13011317701 0023616 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000000447 13011317701 0023621 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001060 13011317701 0023613 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000000304 13011317701 0023614 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000002207 13011317701 0023622 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000004240 13011317701 0023622 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000002451 13011317701 0023625 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001635 13011317701 0023620 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001157 13011317701 0023620 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001155 13011317701 0023617 0 ustar 00root root 0000000 0000000 --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.php 0000664 0000000 0000000 00000001644 13011317701 0023432 0 ustar 00root root 0000000 0000000 getArguments(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.phpt 0000664 0000000 0000000 00000001370 13011317701 0026025 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001564 13011317701 0026033 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001135 13011317701 0025304 0 ustar 00root root 0000000 0000000 --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.phpt 0000664 0000000 0000000 00000001255 13011317701 0025371 0 ustar 00root root 0000000 0000000 --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.c 0000664 0000000 0000000 00000375233 13011317701 0020147 0 ustar 00root root 0000000 0000000 /*
* 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();
}