diff --git a/.appveyor.yml b/.appveyor.yml index 15a85bf..15d6986 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,78 +2,97 @@ image: Visual Studio 2015 version: '{branch}.{build}' cache: - - c:\build-cache -> .appveyor.yml - - c:\build-cache\sdk -> .appveyor.yml + - c:\build-cache -> .appveyor.yml, .appveyor/*.cmd environment: PHP_BUILD_CACHE_BASE_DIR: c:\build-cache PHP_BUILD_OBJ_DIR: c:\obj PHP_BUILD_CACHE_SDK_DIR: c:\build-cache\sdk - PHP_BUILD_SDK_BRANCH: php-sdk-2.0.13 + PHP_BUILD_SDK_BRANCH: php-sdk-2.2.0 SDK_REMOTE: https://github.com/OSTC/php-sdk-binary-tools.git - SDK_BRANCH: php-sdk-2.0.13 + SDK_BRANCH: php-sdk-2.2.0 matrix: - - PHP_REL: 7.2 + - PHP_REL: 8.0 + ARCHITECTURE: x64 + ZTS_STATE: enable + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + PHP_BUILD_CRT: vs16 + - PHP_REL: 8.0 + ARCHITECTURE: x64 + ZTS_STATE: disable + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + PHP_BUILD_CRT: vs16 + - PHP_REL: 8.0 + ARCHITECTURE: x86 + ZTS_STATE: enable + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + PHP_BUILD_CRT: vs16 + - PHP_REL: 8.0 + ARCHITECTURE: x86 + ZTS_STATE: disable + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + PHP_BUILD_CRT: vs16 + - PHP_REL: 7.4 ARCHITECTURE: x64 ZTS_STATE: enable APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 PHP_BUILD_CRT: vc15 - - PHP_REL: 7.1 + - PHP_REL: 7.3 ARCHITECTURE: x64 ZTS_STATE: enable - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - PHP_BUILD_CRT: vc14 - - PHP_REL: 7.0 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + PHP_BUILD_CRT: vc15 + - PHP_REL: 7.2 ARCHITECTURE: x64 ZTS_STATE: enable - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - PHP_BUILD_CRT: vc14 - - PHP_REL: 7.2 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + PHP_BUILD_CRT: vc15 + - PHP_REL: 7.4 ARCHITECTURE: x64 ZTS_STATE: disable APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 PHP_BUILD_CRT: vc15 - - PHP_REL: 7.1 + - PHP_REL: 7.3 ARCHITECTURE: x64 ZTS_STATE: disable - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - PHP_BUILD_CRT: vc14 - - PHP_REL: 7.0 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + PHP_BUILD_CRT: vc15 + - PHP_REL: 7.2 ARCHITECTURE: x64 ZTS_STATE: disable - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - PHP_BUILD_CRT: vc14 - - PHP_REL: 7.2 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + PHP_BUILD_CRT: vc15 + - PHP_REL: 7.4 ARCHITECTURE: x86 ZTS_STATE: enable APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 PHP_BUILD_CRT: vc15 - - PHP_REL: 7.1 + - PHP_REL: 7.3 ARCHITECTURE: x86 ZTS_STATE: enable - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - PHP_BUILD_CRT: vc14 - - PHP_REL: 7.0 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + PHP_BUILD_CRT: vc15 + - PHP_REL: 7.2 ARCHITECTURE: x86 ZTS_STATE: enable - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - PHP_BUILD_CRT: vc14 - - PHP_REL: 7.2 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + PHP_BUILD_CRT: vc15 + - PHP_REL: 7.4 ARCHITECTURE: x86 ZTS_STATE: disable APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 PHP_BUILD_CRT: vc15 - - PHP_REL: 7.1 + - PHP_REL: 7.3 ARCHITECTURE: x86 ZTS_STATE: disable - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - PHP_BUILD_CRT: vc14 - - PHP_REL: 7.0 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + PHP_BUILD_CRT: vc15 + - PHP_REL: 7.2 ARCHITECTURE: x86 ZTS_STATE: disable - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - PHP_BUILD_CRT: vc14 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + PHP_BUILD_CRT: vc15 install: - .appveyor\install.cmd diff --git a/.appveyor/build_task.cmd b/.appveyor/build_task.cmd index 64ab78f..19ebaed 100644 --- a/.appveyor/build_task.cmd +++ b/.appveyor/build_task.cmd @@ -46,7 +46,7 @@ setlocal enableextensions enabledelayedexpansion set TEST_PHP_EXECUTABLE=%APPVEYOR_BUILD_FOLDER%\build\php.exe set TEST_PHP_JUNIT=c:\tests_tmp\tests-junit.xml if "%OPCACHE%" equ "1" set TEST_PHP_ARGS=!TEST_PHP_ARGS! -d extension=%APPVEYOR_BUILD_FOLDER%\build\ext\php_opcache.so -d opcache.enable=1 -d opcache.enable_cli=1 - set TEST_PHP_ARGS=-n -d -foo=1 -d extension=%APPVEYOR_BUILD_FOLDER%\build\ext\php_tideways_xhprof.dll + set TEST_PHP_ARGS=-n -d foo=1 -d extension=%APPVEYOR_BUILD_FOLDER%\build\ext\php_tideways_xhprof.dll set SKIP_DBGP_TESTS=1 set SKIP_IPV6_TESTS=1 set REPORT_EXIT_STATUS=1 diff --git a/.appveyor/install.cmd b/.appveyor/install.cmd index cbc4a64..40649fd 100644 --- a/.appveyor/install.cmd +++ b/.appveyor/install.cmd @@ -31,10 +31,10 @@ setlocal enableextensions enabledelayedexpansion git clone -q --depth=1 --branch=PHP-%PHP_REL% https://github.com/php/php-src C:\projects\php-src ) - xcopy %APPVEYOR_BUILD_FOLDER% C:\projects\php-src\ext\tideways_xhprof\ /s /e /y /f + xcopy %APPVEYOR_BUILD_FOLDER% C:\projects\php-src\ext\tideways_xhprof\ /s /e /y /q - xcopy %APPVEYOR_BUILD_FOLDER%\LICENSE %APPVEYOR_BUILD_FOLDER%\artifacts\ /y /f - xcopy %APPVEYOR_BUILD_FOLDER%\NOTICE %APPVEYOR_BUILD_FOLDER%\artifacts\ /y /f + xcopy %APPVEYOR_BUILD_FOLDER%\LICENSE %APPVEYOR_BUILD_FOLDER%\artifacts\ /y /q + xcopy %APPVEYOR_BUILD_FOLDER%\NOTICE %APPVEYOR_BUILD_FOLDER%\artifacts\ /y /q if "%APPVEYOR%" equ "True" rmdir /s /q C:\cygwin >NUL 2>NUL if %errorlevel% neq 0 exit /b 3 @@ -51,7 +51,7 @@ setlocal enableextensions enabledelayedexpansion if not "%%l"=="" ( set line=%%l if "!line:~8,27!"=="PHP_TIDEWAYS_XHPROF_VERSION" ( - set APPVEYOR_REPO_TAG_NAME=!line:~30,-1!-%APPVEYOR_REPO_BRANCH%-%APPVEYOR_REPO_COMMIT:~0,8% + set APPVEYOR_REPO_TAG_NAME=!line:~37,-1!-%APPVEYOR_REPO_BRANCH%-%APPVEYOR_REPO_COMMIT:~0,8% ) ) ) diff --git a/.buildkite/build_extension.sh b/.buildkite/build_extension.sh new file mode 100755 index 0000000..6a0509a --- /dev/null +++ b/.buildkite/build_extension.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +PHP_VERSION=$1 +EXTENSION="tideways_xhprof" + +make distclean || true +/opt/php/php-${PHP_VERSION}/bin/phpize +CFLAGS="-O2" ./configure --with-php-config=/opt/php/php-${PHP_VERSION}/bin/php-config +make -j2 + +cp modules/${EXTENSION}.so modules/${EXTENSION}-${PHP_VERSION}.so +buildkite-agent artifact upload modules/${EXTENSION}-${PHP_VERSION}.so + +REPORT_EXIT_STATUS=1 /opt/php/php-${PHP_VERSION}/bin/php run-tests.php -p /opt/php/php-${PHP_VERSION}/bin/php --show-diff -d extension=`pwd`/.libs/${EXTENSION}.so -q diff --git a/.buildkite/package_extension.sh b/.buildkite/package_extension.sh new file mode 100644 index 0000000..ee4c468 --- /dev/null +++ b/.buildkite/package_extension.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +set -e + +BASEDIR=`dirname $0` +BASEDIR=`dirname $BASEDIR` +PACKAGENAME="tideways-xhprof" +DESCRIPTION="tideways-xhprof is a modern XHProf fork built for PHP 7." +EXTENSION="tideways_xhprof" +VERSIONS=( "7.0" "7.1" "7.2" "7.3" "7.4" "8.0" "7.1-zts" "7.2-zts" "7.3-zts" "7.4-zts" "8.0-zts" ) +PACKAGES=( "deb" "rpm" ) +ARCHITECTURE=`uname -m` +ARCHITECTURE=${ARCHITECTURE/686/386} + +buildkite-agent artifact download "modules/*.so" . + +mkdir packaging/dist -p +mkdir packaging/root/usr/lib/${EXTENSION} -p +mkdir packaging/root/usr/share/doc/${PACKAGENAME} -p + +for VERSION in "${VERSIONS[@]}" +do + if [ -f "/opt/php/php-${VERSION}/bin/php" ]; then + EXTVERSION=`/opt/php/php-${VERSION}/bin/php -dextension=modules/${EXTENSION}-${VERSION}.so -r "echo phpversion(\"${EXTENSION}\");"` + fi +done + +mkdir packaging/tarball/${EXTENSION}-${EXTVERSION} -p + +cp modules/*.so packaging/tarball/${EXTENSION}-${EXTVERSION}/ +cp modules/*.so packaging/root/usr/lib/${EXTENSION}/ + +cp ${BASEDIR}/LICENSE packaging/tarball/${EXTENSION}-${EXTVERSION}/LICENSE || true +cp ${BASEDIR}/NOTICE packaging/tarball/${EXTENSION}-${EXTVERSION}/NOTICE || true + +cp ${BASEDIR}/LICENSE packaging/root/usr/share/doc/${PACKAGENAME}/LICENSE || true +cp ${BASEDIR}/NOTICE packaging/root/usr/share/doc/${PACKAGENAME}/NOTICE || true + +pushd . + +cd packaging/tarball +tar czvf ../dist/${PACKAGENAME}-${EXTVERSION}-${ARCHITECTURE}.tar.gz . + +popd + +buildkite-agent artifact upload packaging/dist/${PACKAGENAME}-${EXTVERSION}-${ARCHITECTURE}.tar.gz + +for PKG in "${PACKAGES[@]}" +do + fpm --maintainer "support@tideways.com" \ + --url "https://tideways.com/" \ + --description "${DESCRIPTION}" \ + --vendor "Tideways GmbH" \ + -f \ + -s dir \ + -t ${PKG} \ + -C "${BASEDIR}/packaging/root" \ + -n "${PACKAGENAME}" \ + -a "${ARCHITECTURE}" \ + -v "${EXTVERSION}" \ + . + + buildkite-agent artifact upload "${PACKAGENAME}*.${PKG}" +done diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml new file mode 100644 index 0000000..36b0bb4 --- /dev/null +++ b/.buildkite/pipeline.yml @@ -0,0 +1,89 @@ +env: + NO_INTERACTION: 1 + +steps: + - label: ":php: 8.0 amd64" + command: ./.buildkite/build_extension.sh 8.0 + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - label: ":php: 7.4 amd64" + command: ./.buildkite/build_extension.sh 7.4 + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - label: ":php: 7.3 amd64" + command: ./.buildkite/build_extension.sh 7.3 + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - label: ":php: 7.2 amd64" + command: ./.buildkite/build_extension.sh 7.2 + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - label: ":php: 7.1 amd64" + command: ./.buildkite/build_extension.sh 7.1 + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - label: ":php: 7.0 amd64" + command: ./.buildkite/build_extension.sh 7.0 + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - label: ":php: 8.0-zts amd64" + command: ./.buildkite/build_extension.sh 8.0-zts + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - label: ":php: 7.4-zts amd64" + command: ./.buildkite/build_extension.sh 7.4-zts + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - label: ":php: 7.3-zts amd64" + command: ./.buildkite/build_extension.sh 7.3-zts + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - label: ":php: 7.2-zts amd64" + command: ./.buildkite/build_extension.sh 7.2-zts + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - label: ":php: 7.1-zts amd64" + command: ./.buildkite/build_extension.sh 7.1-zts + agents: + phpbuild: "no-debug" + queue: "build" + arch: "x86_64" + + - wait + + - label: ":package::debian: amd64" + command: ./.buildkite/package_extension.sh + agents: + queue: "build" + phpbuild: "no-debug" + arch: "x86_64" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1905b4f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -language: php -dist: precise - -notifications: - email: false - -sudo: false - -php: - - 7.0 - - 7.1 - - 7.2 - -env: - global: - - NO_INTERACTION=1 - - REPORT_EXIT_STATUS=1 - -before_script: - - phpize - - ./configure - - make - -script: make test - -after_failure: "cat tests/*.diff" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6ae0514 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# 5.0.2 + +- [#90](https://github.com/tideways/php-xhprof-extension/issues/90): Add packaging for PHP 7.4 + +# 5.0.1 + +- [#86](https://github.com/tideways/php-xhprof-extension/pull/86): Fix bug in Apple/MacOS timer code that prevented wall time measurements from working. diff --git a/NOTICE b/NOTICE index 8675d29..5788c1a 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Tideways PHP XHProf Profiler -Copyright (c) 2014-2017 Tideways GmbH +Copyright (c) 2014 - 2019 Tideways GmbH This product includes work from the following third-party libraries: diff --git a/README.md b/README.md index f8b3f67..9525eed 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,25 @@ +> [!IMPORTANT] +> This XHProf PHP extension fork has outlived its purpose and is archived in favor of the revitalized [PECL XHProf extension](https://pecl.php.net/package/xhprof) ([GitHub](https://github.com/longxinH/xhprof)) that is also using the modern PHP profiling abilities and performant timer APIs. +> +> If you are looking for a full-stack Profiling Experience from Trigger through the Browser to modern UI [try out Tideways](https://tideways.com). + # Tideways XHProf Extension Home of the `tideways_xhprof` extension - a hierarchical Profiler for PHP. -**Looking for `tideways` Extension to report to tideways.io?** [Go here](https://tideways.io/profiler/downloads). -**Why did we rename the extension?** [Blog post here](https://tideways.io/profiler/blog/releasing-new-tideways-xhprof-extension). +**This extensions is not compatible with our Tideways service. Are you looking +for `tideways` Extension to use with tideways.com?** [Download here](https://tideways.io/profiler/downloads). This PHP extension is a complete, modernized open-source rewrite of the original XHProf extension, with a new core datastructure and specifically optimized for PHP 7. The result is an XHProf data-format compatible extension with a much reduced overhead in the critical path that you are profiling. -The code for this extension is extracted from the [main Tideways -extension](https://tideways.io) as we are moving to a new extension with -incompatible data-format. - We are committed to provide support for this extension and port it to as many platforms as possible. **Note:** The public API is not compatible to previous xhprof extensions and -forks, as function names are different. Only the data format is compatible. +forks, but function names are different. Only the data format is compatible. ## About tideways and tideways_xhprof Extensions @@ -36,6 +37,8 @@ page](https://tideways.io/profiler/downloads). - PHP >= 7.0 - OS: Linux, MacOS, Windows ([Download DLLs](https://ci.appveyor.com/project/tideways/php-profiler-extension)) +- Architectures: x64/amd64, x86, ARM, PowerPC +- Non-Threaded (NTS) or Threaded (ZTS) support ## Installation @@ -52,6 +55,15 @@ Configure the extension to load with this PHP INI directive: Restart Apache or PHP-FPM. +### Download Pre-Compiled Binaries + +We pre-compile binaries for Linux AMD64 and for Windows. See the [releases page for the downloads](https://github.com/tideways/php-xhprof-extension/releases) for each tagged version. + +The Debian and RPM packages install the PHP extension to `/usr/lib/tideways_xhprof` and doesn't automatically put it into your PHP installation extension directory. +You should link the package by full path for a simple installation: + + extension=/usr/lib/tideways_xhprof/tideways_xhprof-7.3.so + ## Usage The API is not compatible to previous xhprof extensions and forks, @@ -64,9 +76,11 @@ tideways_xhprof_enable(); my_application(); -$data = tideways_xhprof_disable(); +file_put_contents( + sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid() . '.myapplication.xhprof', + serialize(tideways_xhprof_disable()) +); -file_put_contents("/tmp/profile.xhprof", serialize($data)); ``` By default only wall clock time is measured, you can enable @@ -79,7 +93,10 @@ tideways_xhprof_enable(TIDEWAYS_XHPROF_FLAGS_MEMORY | TIDEWAYS_XHPROF_FLAGS_CPU) my_application(); -$data = tideways_xhprof_disable(); +file_put_contents( + sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid() . '.myapplication.xhprof', + serialize(tideways_xhprof_disable()) +); ``` ## Data-Format @@ -95,6 +112,14 @@ function names as a key concatenated with ==> and an array value with 2 to 5 ent - `mu` The sum of increase in `memory_get_usage` for this parent ==> child function pair. - `pmu` The sum of increase in `memory_get_peak_usage` for this parent ==> child function pair. +When `TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC` flag is set, the following additional values are set: +- `mem.na` The sum of the number of all allocations in this function. +- `mem.nf` The sum of the number of all frees in this function. +- `mem.aa` The amount of allocated memory. + +If `TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU` is set, `TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC` is activated +and, if `TIDEWAYS_XHPROF_FLAGS_MEMORY_MU` is not set, `mem.aa` is additionally returned in `mu`. + There is a "magic" function call called "main()" that represents the entry into the profiling. The wall time on this performance data describes the full timeframe that the profiling ran. @@ -143,5 +168,6 @@ is slower compared to other mechanisms that the kernel provides: use without having to force the current process to a specific CPU. Tideways on Linux defaults to using `clock_gettime(CLOCK_MONOTONIC)`, but if -you are running on Xen based virtualization, you could try to reduce the -overhead by setting `tideways.clock_use_rdtsc=1" in your PHP.ini. +you are running on Xen based virtualization or Docker on Mac, you could try to +reduce the overhead by setting `tideways_xhprof.clock_use_rdtsc=1" in your +PHP.ini. diff --git a/php_tideways_xhprof.h b/php_tideways_xhprof.h index b9309c9..7a79cda 100644 --- a/php_tideways_xhprof.h +++ b/php_tideways_xhprof.h @@ -4,7 +4,7 @@ extern zend_module_entry tideways_xhprof_module_entry; #define phpext_tideways_xhprof_ptr &tideways_xhprof_module_entry -#define PHP_TIDEWAYS_XHPROF_VERSION "5.0.0" +#define PHP_TIDEWAYS_XHPROF_VERSION "5.0.4" #define TIDEWAYS_XHPROF_CALLGRAPH_COUNTER_SIZE 1024 #define TIDEWAYS_XHPROF_CALLGRAPH_SLOTS 8192 #define TIDEWAYS_XHPROF_CLOCK_CGT 0 @@ -14,6 +14,14 @@ extern zend_module_entry tideways_xhprof_module_entry; #define TIDEWAYS_XHPROF_CLOCK_QPC 4 #define TIDEWAYS_XHPROF_CLOCK_NONE 255 +#ifndef TSRMLS_CC +#define TSRMLS_FETCH() +#define TSRMLS_CC +#define TSRMLS_DC +#define TSRMLS_D +#define TSRMLS_C +#endif + #ifdef ZTS #include "TSRM.h" #endif @@ -42,6 +50,8 @@ typedef struct xhprof_callgraph_bucket_t { zend_long cpu_time; zend_long memory; zend_long memory_peak; + long int num_alloc, num_free; + long int amount_alloc; } xhprof_callgraph_bucket; /* Tracer maintains a stack of entries being profiled. @@ -57,6 +67,8 @@ struct xhprof_frame_t { uint64 cpu_start; /* start value for CPU clock timer */ long int mu_start; /* memory usage */ long int pmu_start; /* peak memory usage */ + long int num_alloc, num_free; + long int amount_alloc; int recurse_level; zend_ulong hash_code; /* hash_code for the function name */ }; @@ -74,6 +86,9 @@ ZEND_BEGIN_MODULE_GLOBALS(tideways_xhprof) zend_ulong function_hash_counters[TIDEWAYS_XHPROF_CALLGRAPH_COUNTER_SIZE]; xhprof_callgraph_bucket* callgraph_buckets[TIDEWAYS_XHPROF_CALLGRAPH_SLOTS]; zend_long flags; + long int num_alloc; + long int num_free; + long int amount_alloc; ZEND_END_MODULE_GLOBALS(tideways_xhprof) #if defined(__GNUC__) && __GNUC__ >= 4 diff --git a/tests/common.php b/tests/common.php index d6b15c8..895739e 100644 --- a/tests/common.php +++ b/tests/common.php @@ -37,7 +37,7 @@ function print_canonical($xhprof_data) // Only call counts are stable. // Wild card everything else. We still print // the metric name to ensure it was collected. - if ($name != "ct") { + if (!in_array($name, array("ct"))) { $value = "*"; } else { $value = str_pad($value, 8, " ", STR_PAD_LEFT); diff --git a/tests/xhprof_001.phpt b/tests/xhprof_001.phpt index 7fe1cbf..1429165 100644 --- a/tests/xhprof_001.phpt +++ b/tests/xhprof_001.phpt @@ -62,6 +62,24 @@ echo "Part 5: Memory & CPU\n"; print_canonical($output); echo "\n"; +// 6: Sanity test extended memory profiling +tideways_xhprof_enable(TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC); +foo("this is a test"); +$output = tideways_xhprof_disable(); + +echo "Part 6: Extended Memory Profiling\n"; +print_canonical($output); +echo "\n"; + +// 7: Sanity test extended memory profiling as mu +tideways_xhprof_enable(TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU); +foo("this is a test"); +$output = tideways_xhprof_disable(); + +echo "Part 7: Extended Memory Profiling as mu\n"; +print_canonical($output); +echo "\n"; + ?> --EXPECT-- Part 1: Default Flags @@ -93,4 +111,14 @@ main() : cpu=*; ct= 1; mu=*; pmu=*; wt=*; main()==>foo : cpu=*; ct= 1; mu=*; pmu=*; wt=*; main()==>tideways_xhprof_disable : cpu=*; ct= 1; mu=*; pmu=*; wt=*; - +Part 6: Extended Memory Profiling +foo==>bar : ct= 2; mem.aa=*; mem.na=*; mem.nf=*; wt=*; +main() : ct= 1; mem.aa=*; mem.na=*; mem.nf=*; wt=*; +main()==>foo : ct= 1; mem.aa=*; mem.na=*; mem.nf=*; wt=*; +main()==>tideways_xhprof_disable : ct= 1; mem.aa=*; mem.na=*; mem.nf=*; wt=*; + +Part 7: Extended Memory Profiling as mu +foo==>bar : ct= 2; mem.aa=*; mem.na=*; mem.nf=*; mu=*; wt=*; +main() : ct= 1; mem.aa=*; mem.na=*; mem.nf=*; mu=*; wt=*; +main()==>foo : ct= 1; mem.aa=*; mem.na=*; mem.nf=*; mu=*; wt=*; +main()==>tideways_xhprof_disable : ct= 1; mem.aa=*; mem.na=*; mem.nf=*; mu=*; wt=*; diff --git a/tests/xhprof_003.phpt b/tests/xhprof_003.phpt index 00ffce7..fe93214 100644 --- a/tests/xhprof_003.phpt +++ b/tests/xhprof_003.phpt @@ -65,4 +65,3 @@ main()==>C::__destruct : ct= 1; wt=*; main()==>C::get_attr : ct= 1; wt=*; main()==>C::outer_static : ct= 1; wt=*; main()==>tideways_xhprof_disable : ct= 1; wt=*; - diff --git a/tests/xhprof_006.phpt b/tests/xhprof_006.phpt index 46885b9..48b91ea 100644 --- a/tests/xhprof_006.phpt +++ b/tests/xhprof_006.phpt @@ -36,7 +36,7 @@ $output = tideways_xhprof_disable(); print_canonical($output); --EXPECTF-- string(1) "1" -Clock Source => tsc +Clock Source => %s main() : ct= 1; wt=*; main()==>foo : ct= 1; wt=*; main()==>tideways_xhprof_disable : ct= 1; wt=*; diff --git a/tests/xhprof_007.phpt b/tests/xhprof_007.phpt index 373299c..35bdcff 100644 --- a/tests/xhprof_007.phpt +++ b/tests/xhprof_007.phpt @@ -26,8 +26,11 @@ $output = tideways_xhprof_disable(); print_canonical($output); --EXPECTF-- +call_user_func==>foo : ct= 1; wt=*; +call_user_func_array==>class@anonymous::__invoke: ct= 1; wt=*; +call_user_func_array==>foo : ct= 1; wt=*; +call_user_func_array==>{closure} : ct= 1; wt=*; main() : ct= 1; wt=*; -main()==>class@anonymous::__invoke : ct= 1; wt=*; -main()==>foo : ct= 2; wt=*; +main()==>call_user_func : ct= 1; wt=*; +main()==>call_user_func_array : ct= 3; wt=*; main()==>tideways_xhprof_disable : ct= 1; wt=*; -main()==>{closure} : ct= 1; wt=*; diff --git a/tests/xhprof_008.inc b/tests/xhprof_008.inc new file mode 100644 index 0000000..df9211f --- /dev/null +++ b/tests/xhprof_008.inc @@ -0,0 +1,7 @@ +double : ct= 10; wt=*; +main() : ct= 1; wt=*; +main()==>array_map : ct= 1; wt=*; +main()==>range : ct= 1; wt=*; +main()==>tideways_xhprof_disable : ct= 1; wt=*; diff --git a/tests/xhprof_009.phpt b/tests/xhprof_009.phpt new file mode 100644 index 0000000..5c1c081 --- /dev/null +++ b/tests/xhprof_009.phpt @@ -0,0 +1,34 @@ +--TEST-- +Tideways: Nested Profiling Test +--FILE-- +bar : ct= 4; wt=*; +main() : ct= 1; wt=*; +main()==>foo : ct= 2; wt=*; +main()==>tideways_xhprof_disable : ct= 1; wt=*; +main()==>tideways_xhprof_enable : ct= 1; wt=*; diff --git a/tests/xhprof_010.phpt b/tests/xhprof_010.phpt new file mode 100644 index 0000000..1d42b55 --- /dev/null +++ b/tests/xhprof_010.phpt @@ -0,0 +1,17 @@ +--TEST-- +Tideways: Disabling without start first +--FILE-- += 80000 +#include "tideways_xhprof_arginfo.h" +#else +#include "tideways_xhprof_legacy_arginfo.h" +#endif + +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("tideways_xhprof.clock_use_rdtsc", "0", PHP_INI_SYSTEM, OnUpdateBool, clock_use_rdtsc, zend_tideways_xhprof_globals, tideways_xhprof_globals) +PHP_INI_END() -static void (*_zend_execute_ex) (zend_execute_data *execute_data); static void (*_zend_execute_internal) (zend_execute_data *execute_data, zval *return_value); ZEND_DLEXPORT void tideways_xhprof_execute_internal(zend_execute_data *execute_data, zval *return_value); -ZEND_DLEXPORT void tideways_xhprof_execute_ex (zend_execute_data *execute_data); -PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("tideways_xhprof.clock_use_rdtsc", "0", PHP_INI_ALL, OnUpdateBool, clock_use_rdtsc, zend_tideways_xhprof_globals, tideways_xhprof_globals) -PHP_INI_END() +#if PHP_VERSION_ID >= 80000 +#include "zend_observer.h" + +static void tracer_observer_begin(zend_execute_data *ex) { + if (!TXRG(enabled)) { + return; + } + + tracing_enter_frame_callgraph(NULL, ex); +} + +static void tracer_observer_end(zend_execute_data *ex, zval *return_value) { + if (!TXRG(enabled)) { + return; + } + + if (TXRG(callgraph_frames)) { + tracing_exit_frame_callgraph(TSRMLS_C); + } +} + + +static zend_observer_fcall_handlers tracer_observer(zend_execute_data *execute_data) { + zend_function *func = execute_data->func; + + if (!func->common.function_name) { + return (zend_observer_fcall_handlers){NULL, NULL}; + } + + return (zend_observer_fcall_handlers){tracer_observer_begin, tracer_observer_end}; +} +#else +static void (*_zend_execute_ex) (zend_execute_data *execute_data); + +void tideways_xhprof_execute_ex (zend_execute_data *execute_data) { + zend_execute_data *real_execute_data = execute_data; + int is_profiling = 0; + + if (!TXRG(enabled)) { + _zend_execute_ex(execute_data TSRMLS_CC); + return; + } + + is_profiling = tracing_enter_frame_callgraph(NULL, real_execute_data TSRMLS_CC); + + _zend_execute_ex(execute_data TSRMLS_CC); + + if (is_profiling == 1 && TXRG(callgraph_frames)) { + tracing_exit_frame_callgraph(TSRMLS_C); + } +} +#endif PHP_FUNCTION(tideways_xhprof_enable) { @@ -29,6 +85,10 @@ PHP_FUNCTION(tideways_xhprof_enable) return; } + if (TXRG(enabled) == 1) { + return; + } + tracing_begin(flags TSRMLS_CC); tracing_enter_root_frame(TSRMLS_C); } @@ -61,12 +121,18 @@ PHP_MINIT_FUNCTION(tideways_xhprof) REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_MEMORY_PMU", TIDEWAYS_XHPROF_FLAGS_MEMORY_PMU, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_CPU", TIDEWAYS_XHPROF_FLAGS_CPU, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_NO_BUILTINS", TIDEWAYS_XHPROF_FLAGS_NO_BUILTINS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC", TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU", TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU, CONST_CS | CONST_PERSISTENT); _zend_execute_internal = zend_execute_internal; zend_execute_internal = tideways_xhprof_execute_internal; +#if PHP_VERSION_ID >= 80000 + zend_observer_fcall_register(tracer_observer); +#else _zend_execute_ex = zend_execute_ex; zend_execute_ex = tideways_xhprof_execute_ex; +#endif return SUCCESS; } @@ -78,8 +144,10 @@ PHP_MSHUTDOWN_FUNCTION(tideways_xhprof) PHP_RINIT_FUNCTION(tideways_xhprof) { + TXRG(clock_source) = determine_clock_source(TXRG(clock_use_rdtsc)); tracing_request_init(TSRMLS_C); - tracing_determine_clock_source(TSRMLS_C); + + CG(compiler_options) = CG(compiler_options) | ZEND_COMPILE_NO_BUILTINS; return SUCCESS; } @@ -180,27 +248,9 @@ ZEND_DLEXPORT void tideways_xhprof_execute_internal(zend_execute_data *execute_d } } -ZEND_DLEXPORT void tideways_xhprof_execute_ex (zend_execute_data *execute_data) { - zend_execute_data *real_execute_data = execute_data; - int is_profiling = 0; - - if (!TXRG(enabled)) { - _zend_execute_ex(execute_data TSRMLS_CC); - return; - } - - is_profiling = tracing_enter_frame_callgraph(NULL, real_execute_data TSRMLS_CC); - - _zend_execute_ex(execute_data TSRMLS_CC); - - if (is_profiling == 1 && TXRG(callgraph_frames)) { - tracing_exit_frame_callgraph(TSRMLS_C); - } -} - const zend_function_entry tideways_xhprof_functions[] = { - PHP_FE(tideways_xhprof_enable, NULL) - PHP_FE(tideways_xhprof_disable, NULL) + PHP_FE(tideways_xhprof_enable, arginfo_tideways_xhprof_enable) + PHP_FE(tideways_xhprof_disable, arginfo_tideways_xhprof_disable) PHP_FE_END }; diff --git a/tideways_xhprof.stub.php b/tideways_xhprof.stub.php new file mode 100644 index 0000000..ff94ab9 --- /dev/null +++ b/tideways_xhprof.stub.php @@ -0,0 +1,15 @@ + +#include #else - -#include +#include "sys/time.h" #include #endif -#if __APPLE__ -#include -#include +static int determine_clock_source(int clock_use_rdtsc) { +#if defined(__APPLE__) + return TIDEWAYS_XHPROF_CLOCK_MACH; +#elif defined(__powerpc__) || defined(__ppc__) + return TIDEWAYS_XHPROF_CLOCK_TSC; +#elif defined(__s390__) // Covers both s390 and s390x. + return TIDEWAYS_XHPROF_CLOCK_TSC; +#elif defined(__ARM_ARCH) && !defined(__aarch64__) + return TIDEWAYS_XHPROF_CLOCK_GTOD; +#elif defined(PHP_WIN32) + return TIDEWAYS_XHPROF_CLOCK_QPC; +#else + struct timespec res; + + if (clock_use_rdtsc == 1) { + return TIDEWAYS_XHPROF_CLOCK_TSC; + } + +#if HAVE_CLOCK_GETTIME + if (clock_gettime(CLOCK_MONOTONIC, &res) == 0) { + return TIDEWAYS_XHPROF_CLOCK_CGT; + } +#endif + +#if HAVE_GETTIMEOFDAY + return TIDEWAYS_XHPROF_CLOCK_GTOD; #endif + return TIDEWAYS_XHPROF_CLOCK_TSC; +#endif +} + static zend_always_inline uint64 current_timestamp() { struct timeval tv; if (gettimeofday(&tv, NULL)) { php_error(E_ERROR, "tracer: Cannot acquire gettimeofday"); - return 0; + zend_bailout(); } return 1000 * (uint64) tv.tv_sec + (uint64) tv.tv_usec / 1000; } +static zend_always_inline uint64 time_milliseconds_cgt() +{ +#if HAVE_CLOCK_GETTIME + struct timespec s; + + if (clock_gettime(CLOCK_MONOTONIC, &s) == 0) { + return s.tv_sec * 1000000 + s.tv_nsec / 1000; + } +#endif + + return 0; +} + +static zend_always_inline uint64 time_milliseconds_gtod() +{ +#if HAVE_GETTIMEOFDAY + struct timeval now; + if (gettimeofday(&now, NULL) == 0) { + return now.tv_sec * 1000000 + now.tv_usec; + } +#endif + + return 0; +} + +static zend_always_inline uint64 time_milliseconds_tsc_query() +{ +#if defined(__s390__) // Covers both s390 and s390x. + + uint64_t tsc; + // Return the CPU clock. + asm("stck %0" : "=Q" (tsc) : : "cc"); + + return tsc; +#elif defined(__i386__) + int64_t ret; + __asm__ volatile("rdtsc" : "=A"(ret)); + return ret; +#elif defined(__x86_64__) || defined(__amd64__) + uint64 val; + uint32 a, d; + asm volatile("rdtsc" : "=a" (a), "=d" (d)); + (val) = ((uint64)a) | (((uint64)d)<<32); + return val; +#elif defined(__powerpc__) || defined(__ppc__) + uint64 val; + asm volatile ("mftb %0" : "=r" (val)); + return val; +#elif defined(__aarch64__) + uint64 val; + asm volatile("mrs %0, cntvct_el0" : "=r" (val)); + return val; +#else + return 0; +#endif +} + /** * Get the current wallclock timer * * @return 64 bit unsigned integer * @author cjiang */ -static zend_always_inline uint64 time_milliseconds(int source, double timebase_factor) { -#ifdef __APPLE__ +static zend_always_inline uint64 time_milliseconds(int source, double timebase_factor) +{ +#if defined(__APPLE__) + return mach_absolute_time() / timebase_factor; #elif defined(PHP_WIN32) @@ -50,49 +138,18 @@ static zend_always_inline uint64 time_milliseconds(int source, double timebase_f return (double)(count.QuadPart) / timebase_factor; #else - struct timespec s; - uint32 a, d; - uint64 val; - - switch (source) { -#if HAVE_CLOCK_GETTIME - case TIDEWAYS_XHPROF_CLOCK_CGT: - if (clock_gettime(CLOCK_MONOTONIC, &s) == 0) { - return s.tv_sec * 1000000 + s.tv_nsec / 1000; - } else { - struct timeval now; - if (gettimeofday(&now, NULL) == 0) { - return now.tv_sec * 1000000 + now.tv_usec; - } - } - return 0; -#elif HAVE_GETTIMEOFDAY - case TIDEWAYS_XHPROF_CLOCK_GTOD: - struct timeval now; - if (gettimeofday(&now, NULL) == 0) { - return now.tv_sec * 1000000 + now.tv_usec; - } - return 0; -#endif - case TIDEWAYS_XHPROF_CLOCK_TSC: -#if defined(__i386__) - int64_t ret; - __asm__ volatile("rdtsc" : "=A"(ret)); - return ret; -#elif defined(__x86_64__) || defined(__amd64__) - asm volatile("rdtsc" : "=a" (a), "=d" (d)); - (val) = ((uint64)a) | (((uint64)d)<<32); -#elif defined(__powerpc__) || defined(__ppc__) - asm volatile ("mftb %0" : "=r" (val)); -#else -#error You need to define CycleTimer for your OS and CPU -#endif - return val / timebase_factor; - default: - return 0; + if (source == TIDEWAYS_XHPROF_CLOCK_TSC) { + return time_milliseconds_tsc_query() / timebase_factor; + } else if (source == TIDEWAYS_XHPROF_CLOCK_GTOD) { + return time_milliseconds_gtod(); + } else if (source == TIDEWAYS_XHPROF_CLOCK_CGT) { + return time_milliseconds_cgt(); } + #endif + + return 0; } /** @@ -104,12 +161,39 @@ static long get_us_interval(struct timeval *start, struct timeval *end) + (end->tv_usec - start->tv_usec)); } +static zend_always_inline double get_timebase_factor_tsc() +{ + struct timeval start; + struct timeval end; + uint64 tsc_start; + uint64 tsc_end; + volatile int i; + + if (gettimeofday(&start, 0)) { + perror("gettimeofday"); + return 0.0; + } + + tsc_start = time_milliseconds_tsc_query(); + /* Busy loop for 5 miliseconds. */ + do { + for (i = 0; i < 1000000; i++); + if (gettimeofday(&end, 0)) { + perror("gettimeofday"); + return 0.0; + } + tsc_end = time_milliseconds_tsc_query(); + } while (get_us_interval(&start, &end) < 5000); + + return (tsc_end - tsc_start) * 1.0 / (get_us_interval(&start, &end)); +} + /** * Get the timebase factor necessary to divide by in time_milliseconds() */ static zend_always_inline double get_timebase_factor(int source) { -#ifdef __APPLE__ +#if defined(__APPLE__) mach_timebase_info_data_t sTimebaseInfo; (void) mach_timebase_info(&sTimebaseInfo); @@ -123,35 +207,11 @@ static zend_always_inline double get_timebase_factor(int source) return (double)frequency/1000000.0; #else - struct timeval start; - struct timeval end; - uint64 tsc_start; - uint64 tsc_end; - volatile int i; - - switch (source) { - case TIDEWAYS_XHPROF_CLOCK_TSC: - - if (gettimeofday(&start, 0)) { - perror("gettimeofday"); - return 0.0; - } - - tsc_start = time_milliseconds(source, 1.0); - /* Busy loop for 5 miliseconds. */ - do { - for (i = 0; i < 1000000; i++); - if (gettimeofday(&end, 0)) { - perror("gettimeofday"); - return 0.0; - } - tsc_end = time_milliseconds(source, 1.0); - } while (get_us_interval(&start, &end) < 5000); - - return (tsc_end - tsc_start) * 1.0 / (get_us_interval(&start, &end)); - default: - return 1.0; + if (source == TIDEWAYS_XHPROF_CLOCK_TSC) { + return get_timebase_factor_tsc(); } + + return 1.0; #endif } @@ -177,4 +237,3 @@ static uint64 cpu_timer() { } #endif - diff --git a/tracing.c b/tracing.c index 5481264..5fae882 100644 --- a/tracing.c +++ b/tracing.c @@ -12,25 +12,13 @@ extern ZEND_DECLARE_MODULE_GLOBALS(tideways_xhprof); static const char digits[] = "0123456789abcdef"; -void tracing_determine_clock_source(TSRMLS_D) { -#ifdef __APPLE__ - TXRG(clock_source) = TIDEWAYS_XHPROF_CLOCK_MACH; -#elif defined(__powerpc__) || defined(__ppc__) - TXRG(clock_source) = TIDEWAYS_XHPROF_CLOCK_TSC; -#elif defined(PHP_WIN32) - TXRG(clock_source) = TIDEWAYS_XHPROF_CLOCK_QPC; -#else - struct timespec res; - - if (TXRG(clock_use_rdtsc) == 1) { - TXRG(clock_source) = TIDEWAYS_XHPROF_CLOCK_TSC; - } else if (clock_gettime(CLOCK_MONOTONIC, &res) == 0) { - TXRG(clock_source) = TIDEWAYS_XHPROF_CLOCK_CGT; - } else { - TXRG(clock_source) = TIDEWAYS_XHPROF_CLOCK_GTOD; - } -#endif -} +static void *(*_zend_malloc) (size_t); +static void (*_zend_free) (void *); +static void *(*_zend_realloc) (void *, size_t); + +void *tideways_malloc (size_t size); +void tideways_free (void *ptr); +void *tideways_realloc (void *ptr, size_t size); /** * Free any items in the free list. @@ -70,6 +58,21 @@ void tracing_end(TSRMLS_D) TXRG(enabled) = 0; TXRG(callgraph_frames) = NULL; + + if (TXRG(flags) & TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC) { + zend_mm_heap *heap = zend_mm_get_heap(); + + if (_zend_malloc || _zend_free || _zend_realloc) { + zend_mm_set_custom_handlers(heap, _zend_malloc, _zend_free, _zend_realloc); + _zend_malloc = NULL; + _zend_free = NULL; + _zend_realloc = NULL; + } else { + // zend_mm_heap is incomplete type, hence one can not access it + // the following line is equivalent to heap->use_custom_heap = 0; + *((int*) heap) = 0; + } + } } } @@ -100,7 +103,7 @@ xhprof_callgraph_bucket *tracing_callgraph_bucket_find(xhprof_callgraph_bucket * if (bucket->key == key && bucket->child_recurse_level == current_frame->recurse_level && bucket->child_class == current_frame->class_name && - bucket->child_function == current_frame->function_name) { + zend_string_equals(bucket->child_function, current_frame->function_name)) { if (previous == NULL && bucket->parent_class == NULL && bucket->parent_function == NULL ) { // special handling for the root @@ -108,7 +111,7 @@ xhprof_callgraph_bucket *tracing_callgraph_bucket_find(xhprof_callgraph_bucket * } else if (previous && previous->recurse_level == bucket->parent_recurse_level && previous->class_name == bucket->parent_class && - previous->function_name == bucket->parent_function) { + zend_string_equals(previous->function_name, bucket->parent_function)) { // parent matches as well return bucket; } @@ -205,6 +208,10 @@ void tracing_callgraph_append_to_array(zval *return_value TSRMLS_DC) char symbol[512] = ""; zval stats_zv, *stats = &stats_zv; + int as_mu = + (TXRG(flags) & (TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU | TIDEWAYS_XHPROF_FLAGS_MEMORY_MU)) + == TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU; + for (i = 0; i < TIDEWAYS_XHPROF_CALLGRAPH_SLOTS; i++) { bucket = TXRG(callgraph_buckets)[i]; @@ -215,6 +222,16 @@ void tracing_callgraph_append_to_array(zval *return_value TSRMLS_DC) add_assoc_long(stats, "ct", bucket->count); add_assoc_long(stats, "wt", bucket->wall_time); + if (TXRG(flags) & TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC) { + add_assoc_long(stats, "mem.na", bucket->num_alloc); + add_assoc_long(stats, "mem.nf", bucket->num_free); + add_assoc_long(stats, "mem.aa", bucket->amount_alloc); + + if (as_mu) { + add_assoc_long(stats, "mu", bucket->amount_alloc); + } + } + if (TXRG(flags) & TIDEWAYS_XHPROF_FLAGS_CPU) { add_assoc_long(stats, "cpu", bucket->cpu_time); } @@ -250,6 +267,12 @@ void tracing_begin(zend_long flags TSRMLS_DC) for (i = 0; i < TIDEWAYS_XHPROF_CALLGRAPH_COUNTER_SIZE; i++) { TXRG(function_hash_counters)[i] = 0; } + + if (flags & TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC) { + zend_mm_heap *heap = zend_mm_get_heap(); + zend_mm_get_custom_handlers (heap, &_zend_malloc, &_zend_free, &_zend_realloc); + zend_mm_set_custom_handlers (heap, &tideways_malloc, &tideways_free, &tideways_realloc); + } } void tracing_request_init(TSRMLS_D) @@ -258,9 +281,52 @@ void tracing_request_init(TSRMLS_D) TXRG(enabled) = 0; TXRG(flags) = 0; TXRG(frame_free_list) = NULL; + + TXRG(num_alloc) = 0; + TXRG(num_free) = 0; + TXRG(amount_alloc) = 0; } void tracing_request_shutdown() { - tracing_free_the_free_list(); + tracing_free_the_free_list(TSRMLS_C); +} + +void *tideways_malloc (size_t size) +{ + TXRG(num_alloc) += 1; + TXRG(amount_alloc) += size; + + if (_zend_malloc) { + return _zend_malloc(size); + } + + zend_mm_heap *heap = zend_mm_get_heap(); + return zend_mm_alloc(heap, size); +} + +void tideways_free (void *ptr) +{ + TXRG(num_free) += 1; + + if (_zend_free) { + return _zend_free(ptr); + } + + zend_mm_heap *heap = zend_mm_get_heap(); + return zend_mm_free(heap, ptr); +} + +void *tideways_realloc (void *ptr, size_t size) +{ + TXRG(num_alloc) += 1; + TXRG(num_free) += 1; + TXRG(amount_alloc) += size; + + if (_zend_realloc) { + return _zend_realloc(ptr, size); + } + + zend_mm_heap *heap = zend_mm_get_heap(); + return zend_mm_realloc(heap, ptr, size); } diff --git a/tracing.h b/tracing.h index 1f1b699..d466bce 100644 --- a/tracing.h +++ b/tracing.h @@ -7,6 +7,8 @@ #define TIDEWAYS_XHPROF_FLAGS_MEMORY_MU 2 #define TIDEWAYS_XHPROF_FLAGS_MEMORY_PMU 4 #define TIDEWAYS_XHPROF_FLAGS_MEMORY 6 +#define TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC 16 +#define TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU (32|16) #define TIDEWAYS_XHPROF_FLAGS_NO_BUILTINS 8 void tracing_callgraph_append_to_array(zval *return_value TSRMLS_DC); @@ -127,6 +129,10 @@ zend_always_inline static int tracing_enter_frame_callgraph(zend_string *root_sy current_frame->mu_start = zend_memory_usage(0 TSRMLS_CC); } + current_frame->num_alloc = TXRG(num_alloc); + current_frame->num_free = TXRG(num_free); + current_frame->amount_alloc = TXRG(amount_alloc); + /* We only need to compute the hash for the function name, * that should be "good" enough, we sort into 1024 buckets only anyways */ current_frame->hash_code = ZSTR_HASH(function_name) % TIDEWAYS_XHPROF_CALLGRAPH_COUNTER_SIZE; @@ -137,7 +143,7 @@ zend_always_inline static int tracing_enter_frame_callgraph(zend_string *root_sy if (TXRG(function_hash_counters)[current_frame->hash_code] > 0) { /* Find this symbols recurse level */ for(p = current_frame->previous_frame; p; p = p->previous_frame) { - if (current_frame->function_name == p->function_name && (!current_frame->class_name || current_frame->class_name == p->class_name)) { + if (zend_string_equals(current_frame->function_name, p->function_name) && (!current_frame->class_name || current_frame->class_name == p->class_name)) { recurse_level = (p->recurse_level) + 1; break; } @@ -184,6 +190,9 @@ zend_always_inline static void tracing_exit_frame_callgraph(TSRMLS_D) bucket->cpu_time = 0; bucket->memory = 0; bucket->memory_peak = 0; + bucket->num_alloc = 0; + bucket->num_free = 0; + bucket->amount_alloc = 0; bucket->child_recurse_level = current_frame->recurse_level; bucket->next = TXRG(callgraph_buckets)[slot]; @@ -193,6 +202,10 @@ zend_always_inline static void tracing_exit_frame_callgraph(TSRMLS_D) bucket->count++; bucket->wall_time += duration; + bucket->num_alloc += TXRG(num_alloc) - current_frame->num_alloc; + bucket->num_free += TXRG(num_free) - current_frame->num_free; + bucket->amount_alloc += TXRG(amount_alloc) - current_frame->amount_alloc; + if (TXRG(flags) & TIDEWAYS_XHPROF_FLAGS_CPU) { bucket->cpu_time += (cpu_timer() - current_frame->cpu_start); }