diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3b1e272..23e2616 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,113 +14,166 @@ on: - master jobs: - build: - name: build / php-${{ matrix.php }}-${{ matrix.ts }}-${{ matrix.name }}-${{ matrix.compiler }} - - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - php: [ '5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] - - name: - - linux - - debian - - mac - - include: - # Linux - - { name: linux, ts: 'nts', compiler: 'gcc', os: ubuntu-20.04 } - # Debian (docker) - - { name: debian, ts: 'nts', compiler: 'gcc', os: ubuntu-20.04 } - # macOS - - { name: mac, ts: 'nts', compiler: 'clang', os: macos-13 } - - steps: - - uses: actions/checkout@v3 - - # configure spx artifact name in next format: - # {php}-{ts}-{os.name}-{compiler} - # spx-php-8.1-nts-linux-gcc - - name: Set artifact name - id: setup-artifact - run: | - echo "spx_file_name=spx-php-${{ matrix.php }}-${{ matrix.ts }}-${{ matrix.name }}-${{ matrix.compiler }}" >> $GITHUB_OUTPUT - - - name: Build extension for Ubuntu and macOS - if: matrix.name != 'debian' - uses: ./.github/workflows/build-linux-mac-ext - - - name: Build extension for Debian using docker - if: matrix.name == 'debian' && matrix.php != '5.4' && matrix.php != '5.5' - uses: ./.github/workflows/build-debian-ext - - - name: Upload build artifacts after Failure - if: failure() - uses: actions/upload-artifact@v3 - with: - name: debug-${{ steps.setup-artifact.outputs.spx_file_name }} - path: | - ${{ github.workspace }}/*.log - ${{ github.workspace }}/tests/*.log - retention-days: 7 - - - name: Create ZIP archive with build artifact - run: | - zip -rvj ${{ steps.setup-artifact.outputs.spx_file_name }}.zip \ - ./modules/spx.so LICENSE $ZIP_EXCLUDE - zip -rv ${{ steps.setup-artifact.outputs.spx_file_name }}.zip \ - ./assets $ZIP_EXCLUDE - env: - ZIP_EXCLUDE: -x ".*" -x "__MACOSX" -x "*.DS_Store" - - - name: Check Release notes - run: | - echo "-- Parsing Release Notes from CHANGELOG" - ./.github/release-notes.sh ./CHANGELOG.md - - - name: Upload build artifact - uses: actions/upload-artifact@v3 - with: - name: ${{ steps.setup-artifact.outputs.spx_file_name }}.zip - path: ${{ steps.setup-artifact.outputs.spx_file_name }}.zip - - release: - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') - - needs: [ build ] - name: Create Release - runs-on: ubuntu-20.04 - + # build: + # name: build / php-${{ matrix.php }}-${{ matrix.ts }}-${{ matrix.name }}-${{ matrix.compiler }} + + # runs-on: ${{ matrix.os }} + # strategy: + # fail-fast: false + # matrix: + # php: [ '5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] + + # name: + # - linux + # - debian + # - mac + + # include: + # # Linux + # - { name: linux, ts: 'nts', compiler: 'gcc', os: ubuntu-20.04 } + # # Debian (docker) + # - { name: debian, ts: 'nts', compiler: 'gcc', os: ubuntu-20.04 } + # # macOS + # - { name: mac, ts: 'nts', compiler: 'clang', os: macos-13 } + + # steps: + # - uses: actions/checkout@v3 + + # # configure spx artifact name in next format: + # # {php}-{ts}-{os.name}-{compiler} + # # spx-php-8.1-nts-linux-gcc + # - name: Set artifact name + # id: setup-artifact + # run: | + # echo "spx_file_name=spx-php-${{ matrix.php }}-${{ matrix.ts }}-${{ matrix.name }}-${{ matrix.compiler }}" >> $GITHUB_OUTPUT + + # - name: Build extension for Ubuntu and macOS + # if: matrix.name != 'debian' + # uses: ./.github/workflows/build-linux-mac-ext + + # - name: Build extension for Debian using docker + # if: matrix.name == 'debian' && matrix.php != '5.4' && matrix.php != '5.5' + # uses: ./.github/workflows/build-debian-ext + + # - name: Upload build artifacts after Failure + # if: failure() + # uses: actions/upload-artifact@v3 + # with: + # name: debug-${{ steps.setup-artifact.outputs.spx_file_name }} + # path: | + # ${{ github.workspace }}/*.log + # ${{ github.workspace }}/tests/*.log + # retention-days: 7 + + # - name: Create ZIP archive with build artifact + # run: | + # zip -rvj ${{ steps.setup-artifact.outputs.spx_file_name }}.zip \ + # ./modules/spx.so LICENSE $ZIP_EXCLUDE + # zip -rv ${{ steps.setup-artifact.outputs.spx_file_name }}.zip \ + # ./assets $ZIP_EXCLUDE + # env: + # ZIP_EXCLUDE: -x ".*" -x "__MACOSX" -x "*.DS_Store" + + # - name: Check Release notes + # run: | + # echo "-- Parsing Release Notes from CHANGELOG" + # ./.github/release-notes.sh ./CHANGELOG.md + + # - name: Upload build artifact + # uses: actions/upload-artifact@v3 + # with: + # name: ${{ steps.setup-artifact.outputs.spx_file_name }}.zip + # path: ${{ steps.setup-artifact.outputs.spx_file_name }}.zip + + # build-win-old: + # # FIXME this hacky pipeline will be cleaned later + # runs-on: windows-latest + # steps: + # - name: Install zlib + # run: | + # vcpkg.exe install zlib + # - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + # - uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 + # - name: Setup PHP SDK with Developer Pack + # uses: zephir-lang/setup-php-sdk@fd5f1bce00956a7e8ac30faaa1ff0692d6dacdfb # v1.0 + # with: + # php_version: '8.3' + # ts: 'nts' + # msvc: 'vs16' + # arch: 'x64' + # install_dir: 'C:\tools' + # cache_dir: 'C:\Temp' + # - name: Clone PHP-src + # run: | + # git clone https://github.com/php/php-src.git C:\php-src + # - name: Copy SPX to PHP-src ext directory + # run: | + # xcopy /e /k /h /i . C:\php-src\ext\php-spx + # - name: Configure and build 1 + # run: | + # cd C:\php-src + # ./buildconf.bat + # - name: Configure and build 2 + # run: | + # cd C:\php-src + # ./configure --help + # ./configure --disable-all --enable-cli --enable-spx + # - name: Configure and build 3 + # run: | + # cd C:\php-src + # nmake + + build-win: + runs-on: windows-latest steps: - - name: Checkout Code - uses: actions/checkout@v3 - with: - fetch-depth: 1 - - - name: Get the release version - id: get-version - run: | - echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT - - - name: Download SPX build artifacts - id: download - uses: actions/download-artifact@v4.1.7 - with: - path: ./build-artifacts - - - name: Prepare Release notes - run: | - echo "-- Creating Release Notes" - ./.github/release-notes.sh ./CHANGELOG.md > ./release-notes.md - - - name: Create Release - uses: ncipollo/release-action@v1 + - name: Build the extension + uses: php/php-windows-builder/extension@5106db21e6a35781bff937be3c9e1e56d7b17789 with: - token: ${{ secrets.GITHUB_TOKEN }} - name: ${{ steps.get-version.outputs.version }} - tag: ${{ steps.get-version.outputs.version }} - bodyFile: "./release-notes.md" - allowUpdates: true - artifacts: "./build-artifacts/*/*.zip" - artifactContentType: application/octet-stream + #extension-url: https://github.com/xdebug/xdebug + #extension-ref: '3.3.1' + php-version: '8.3' + ts: nts + run-tests: false + arch: x64 + args: --enable-spx + libs: zlib + + # release: + # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + + # needs: [ build ] + # name: Create Release + # runs-on: ubuntu-20.04 + + # steps: + # - name: Checkout Code + # uses: actions/checkout@v3 + # with: + # fetch-depth: 1 + + # - name: Get the release version + # id: get-version + # run: | + # echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + + # - name: Download SPX build artifacts + # id: download + # uses: actions/download-artifact@v4.1.7 + # with: + # path: ./build-artifacts + + # - name: Prepare Release notes + # run: | + # echo "-- Creating Release Notes" + # ./.github/release-notes.sh ./CHANGELOG.md > ./release-notes.md + + # - name: Create Release + # uses: ncipollo/release-action@v1 + # with: + # token: ${{ secrets.GITHUB_TOKEN }} + # name: ${{ steps.get-version.outputs.version }} + # tag: ${{ steps.get-version.outputs.version }} + # bodyFile: "./release-notes.md" + # allowUpdates: true + # artifacts: "./build-artifacts/*/*.zip" + # artifactContentType: application/octet-stream diff --git a/Makefile.frag.w32 b/Makefile.frag.w32 new file mode 100644 index 0000000..d916828 --- /dev/null +++ b/Makefile.frag.w32 @@ -0,0 +1,11 @@ + +spx_ui_assets_dir = $(INSTALL_ROOT)$(prefix)\php-spx\assets\web-ui + +CFLAGS += /DSPX_HTTP_UI_ASSETS_DIR=\"$(spx_ui_assets_dir)\" + +install-spx-ui-assets: + @echo "Installing SPX web UI to: $(spx_ui_assets_dir)" + @mkdir -p $(spx_ui_assets_dir) + @cp -r assets/web-ui/* $(spx_ui_assets_dir) + +install: $(all_targets) $(install_targets) install-spx-ui-assets diff --git a/README.md b/README.md index 6e00582..d4d202e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status][:badge-ci:]][:link-ci:] ![Supported PHP versions: 5.4 .. 8.x][:badge-php-versions:] -![Supported platforms: GNU/Linux, macOS & FreeBSD][:badge-supported-platforms:] +![Supported platforms: Linux, macOS, FreeBSD & Windows][:badge-supported-platforms:] ![Supported architectures: x86-64 or ARM64][:badge-supported-arch:] [![License][:badge-license:]][:link-license:] @@ -34,12 +34,8 @@ It differentiates itself from other similar extensions as being: ## Requirements -Platforms support is currently quite limited. Feel free to open an issue if your platform is not supported. -Current requirements are: - * x86-64 or ARM64 -* **GNU/Linux**, **macOS** or **FreeBSD** -* zlib dev package (e.g. zlib1g-dev on Debian based distros) +* **Linux**, **macOS**, **FreeBSD** or **Windows** * PHP 5.4 to 8.4 ## Installation @@ -66,6 +62,12 @@ sudo make install Then add `extension=spx.so` to your *php.ini*, or in a dedicated *spx.ini* file created within the include directory. You may also want to override [default SPX configuration](#configuration) to be able to profile a web request, with [this one](#private-environment) for example for a local development environment. +### Windows + +Windows is supported, with these extra limitations: +- live update of flat profile in CLI (`SPX_FP_LIVE=1`) is not supported. + +Also, consider Windows support as still being in beta. ### ZTS PHP (multi-thread) @@ -78,7 +80,7 @@ Also, consider ZTS PHP support as still being in beta. ### Linux, PHP-FPM & I/O stats -On GNU/Linux, SPX uses procfs (i.e. by reading files under `/proc` directory) to get some stats for the current process or thread. This is what is done under the hood when you select at least one of these metrics: `mor`, `io`, `ior` or `iow`. +On Linux, SPX uses procfs (i.e. by reading files under `/proc` directory) to get some stats for the current process or thread. This is what is done under the hood when you select at least one of these metrics: `mor`, `io`, `ior` or `iow`. But, on most PHP-FPM setups, you will have a permission issue preventing SPX to open a file under `/proc/self` directory. This is due to the fact that PHP-FPM master process runs as root when child processes run as another unprivileged user. @@ -315,7 +317,7 @@ Here is the list of available metrics to collect. By default only _Wall time_ an _\*: Allocated and freed byte counts will not be collected if you use a custom allocator or if you force the libc one through the `USE_ZEND_ALLOC` environment variable set to `0`._ -_\*\*: RSS & I/O metrics are not supported on macOS and FreeBSD. On GNU/Linux you should [read this if you use PHP-FPM](#linux-php-fpm--io-stats)._ +_\*\*: RSS & I/O metrics are not supported on macOS and FreeBSD. On Linux you should [read this if you use PHP-FPM](#linux-php-fpm--io-stats)._ ### Command line script @@ -504,7 +506,7 @@ See the [LICENSE][:link-license:] file for more information. [:link-ci:]: https://github.com/NoiseByNorthwest/php-spx/actions/workflows/main.yml [:badge-php-versions:]: https://img.shields.io/badge/php-5.4--8.4-blue.svg -[:badge-supported-platforms:]: https://img.shields.io/badge/platform-GNU/Linux%20|%20macOS%20|%20FreeBSD%20-yellow +[:badge-supported-platforms:]: https://img.shields.io/badge/platform-Linux%20|%20macOS%20|%20FreeBSD%20-yellow [:badge-supported-arch:]: https://img.shields.io/badge/architecture-x86--64%20|%20ARM64%20-silver [:badge-license:]: https://img.shields.io/github/license/NoiseByNorthwest/php-spx diff --git a/config.w32 b/config.w32 new file mode 100644 index 0000000..d6cc0f5 --- /dev/null +++ b/config.w32 @@ -0,0 +1,40 @@ +ARG_ENABLE("spx", "Enable SPX extension", "no"); + +ARG_ENABLE("spx-dev", "Compile SPX with debugging symbols", "no"); + +if (PHP_SPX == "yes") { + AC_DEFINE("HAVE_SPX", 1, "spx"); + + ADD_FLAG("CFLAGS", "/Ox /Wall /WX /W2 /wd4820 /wd4774 /wd4711 /wd4710 /wd4464 /wd4005"); + + if (PHP_SPX_DEV == "yes") { + ADD_FLAG("CFLAGS", "/Zi"); + } + + if ( + ! CHECK_LIB("zlib_a.lib;zlib.lib", "zlib", PHP_ZLIB) || + ! CHECK_HEADER_ADD_INCLUDE("zlib.h", "CFLAGS", "..\\zlib;" + php_usual_include_suspects) + ) { + ERROR("zlib not found"); + } + + EXTENSION("spx", "src/php_spx.c \ + src/spx_profiler.c \ + src/spx_profiler_tracer.c \ + src/spx_profiler_sampler.c \ + src/spx_reporter_full.c \ + src/spx_reporter_fp.c \ + src/spx_reporter_trace.c \ + src/spx_metric.c \ + src/spx_resource_stats.c \ + src/spx_hmap.c \ + src/spx_str_builder.c \ + src/spx_output_stream.c \ + src/spx_php.c \ + src/spx_stdio.c \ + src/spx_config.c \ + src/spx_utils.c \ + src/spx_fmt.c", true); + + ADD_MAKEFILE_FRAGMENT(); +} diff --git a/src/php_spx.c b/src/php_spx.c index f5a88b4..02276f9 100644 --- a/src/php_spx.c +++ b/src/php_spx.c @@ -17,9 +17,8 @@ #include -#include -#ifndef ZTS +#if ! defined(ZTS) && ! defined(_WIN32) # define USE_SIGNAL #endif @@ -215,7 +214,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_spx_profiler_stop, 0, 0, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_spx_profiler_full_report_set_custom_metadata_str, 0, 0, 1) -#if PHP_API_VERSION >= 20151012 +#if ZEND_MODULE_API_NO >= 20151012 ZEND_ARG_TYPE_INFO(0, customMetadataStr, IS_STRING, 0) #else ZEND_ARG_INFO(0, customMetadataStr) @@ -437,7 +436,7 @@ static PHP_FUNCTION(spx_profiler_stop) profiling_handler_stop(); if (context.profiling_handler.full_report_key[0]) { -#if PHP_API_VERSION >= 20151012 +#if ZEND_MODULE_API_NO >= 20151012 RETURN_STRING(context.profiling_handler.full_report_key); #else RETURN_STRING(context.profiling_handler.full_report_key, 1); @@ -448,13 +447,13 @@ static PHP_FUNCTION(spx_profiler_stop) static PHP_FUNCTION(spx_profiler_full_report_set_custom_metadata_str) { char * custom_metadata_str; -#if PHP_API_VERSION >= 20151012 +#if ZEND_MODULE_API_NO >= 20151012 size_t custom_metadata_str_len; #else int custom_metadata_str_len; #endif -#if PHP_API_VERSION >= 20170718 +#if ZEND_MODULE_API_NO >= 20170718 ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_STRING(custom_metadata_str, custom_metadata_str_len) ZEND_PARSE_PARAMETERS_END(); diff --git a/src/php_spx.h b/src/php_spx.h index 1a68449..2e7ee71 100644 --- a/src/php_spx.h +++ b/src/php_spx.h @@ -23,11 +23,11 @@ #include "main/php.h" /* linux 2.6+ or OSX */ -#if !defined(linux) && !(defined(__APPLE__) && defined(__MACH__)) && !defined(__FreeBSD__) -# error "Only Linux-based OSes, Apple MacOS and FreeBSD are supported" +#if !defined(linux) && !(defined(__APPLE__) && defined(__MACH__)) && !defined(__FreeBSD__) && !defined(_WIN32) +# error "Only Linux-based OSes, Apple MacOS, FreeBSD and Windows are supported" #endif -#if !defined(__x86_64__) && !defined(__aarch64__) +#if (defined(_MSC_VER) && !(defined(_M_X64) || defined(_M_ARM64))) || (!defined(_MSC_VER) && !(defined(__x86_64__) || defined(__aarch64__))) # error "Only x86-64 and ARM64 architectures are supported" #endif diff --git a/src/spx_fmt.c b/src/spx_fmt.c index 526f77c..de9051e 100644 --- a/src/spx_fmt.c +++ b/src/spx_fmt.c @@ -190,13 +190,13 @@ void spx_fmt_row_print(const spx_fmt_row_t * row, spx_output_stream_t * output) ; if (row->cells[i].ansi_fmt) { - spx_output_stream_printf(output, "\e[%sm", row->cells[i].ansi_fmt); + spx_output_stream_printf(output, "%c[%sm", 0x1b, row->cells[i].ansi_fmt); } snprintf( format, sizeof(format), - "%%-%lu.%lus", + "%%-%zu.%zus", cell_width, cell_width ); @@ -204,7 +204,7 @@ void spx_fmt_row_print(const spx_fmt_row_t * row, spx_output_stream_t * output) spx_output_stream_printf(output, format, text); if (row->cells[i].ansi_fmt) { - spx_output_stream_print(output, "\e[0m"); + spx_output_stream_printf(output, "%c[0m", 0x1b); } spx_output_stream_print(output, " |"); diff --git a/src/spx_hmap.h b/src/spx_hmap.h index 4845b5b..383b2e2 100644 --- a/src/spx_hmap.h +++ b/src/spx_hmap.h @@ -19,10 +19,12 @@ #ifndef SPX_HMAP_H_DEFINED #define SPX_HMAP_H_DEFINED +#include + typedef struct spx_hmap_t spx_hmap_t; typedef struct spx_hmap_entry_t spx_hmap_entry_t; -typedef unsigned long (*spx_hmap_hash_key_func_t) (const void *); +typedef uint64_t (*spx_hmap_hash_key_func_t) (const void *); typedef int (*spx_hmap_cmp_key_func_t) (const void *, const void *); spx_hmap_t * spx_hmap_create( diff --git a/src/spx_php.c b/src/spx_php.c index f68ca64..b087ae1 100644 --- a/src/spx_php.c +++ b/src/spx_php.c @@ -15,9 +15,11 @@ * along with this program. If not, see . */ - #include "main/php.h" #include "main/SAPI.h" +#if defined(_WIN32) && ZEND_MODULE_API_NO >= 20170718 +# include "win32/console.h" +#endif /* _GNU_SOURCE is implicitly defined since PHP 8.2 https://github.com/php/php-src/pull/8807 */ #ifndef _GNU_SOURCE @@ -91,14 +93,14 @@ static struct { zend_op_array * (*zend_compile_file)(zend_file_handle * file_handle, int type TSRMLS_DC); zend_op_array * (*zend_compile_string)( -#if PHP_API_VERSION >= 20200930 +#if ZEND_MODULE_API_NO >= 20200930 zend_string * source_string, const #else zval * source_string, #endif char * filename -#if PHP_API_VERSION >= 20210903 +#if ZEND_MODULE_API_NO >= 20210903 , zend_compile_position position #endif TSRMLS_DC @@ -110,13 +112,13 @@ static struct { void (*zend_error_cb) ( int type, -#if PHP_API_VERSION >= 20210902 +#if ZEND_MODULE_API_NO >= 20210902 zend_string *error_filename, #else const char *error_filename, #endif const uint error_lineno, -#if PHP_API_VERSION >= 20200930 +#if ZEND_MODULE_API_NO >= 20200930 zend_string *message #else const char *format, @@ -201,14 +203,14 @@ static void global_hook_execute_internal( static zend_op_array * global_hook_zend_compile_file(zend_file_handle * file_handle, int type TSRMLS_DC); static zend_op_array * global_hook_zend_compile_string( -#if PHP_API_VERSION >= 20200930 +#if ZEND_MODULE_API_NO >= 20200930 zend_string * source_string, const #else zval * source_string, #endif char * filename -#if PHP_API_VERSION >= 20210903 +#if ZEND_MODULE_API_NO >= 20210903 , zend_compile_position position #endif TSRMLS_DC @@ -220,13 +222,13 @@ static int global_hook_gc_collect_cycles(void); static void global_hook_zend_error_cb( int type, -#if PHP_API_VERSION >= 20210902 +#if ZEND_MODULE_API_NO >= 20210902 zend_string *error_filename, #else const char *error_filename, #endif const uint error_lineno, -#if PHP_API_VERSION >= 20200930 +#if ZEND_MODULE_API_NO >= 20200930 zend_string *message #else const char *format, @@ -243,6 +245,17 @@ int spx_php_is_cli_sapi(void) return 0 == strcmp(sapi_module.name, "cli"); } +int spx_php_are_ansi_sequences_supported(void) +{ + return + spx_php_is_cli_sapi() + && isatty(STDOUT_FILENO) +#if defined(_WIN32) && ZEND_MODULE_API_NO >= 20170718 + && php_win32_console_fileno_has_vt100(STDOUT_FILENO) +#endif + ; +} + void spx_php_current_function(spx_php_function_t * function) { TSRMLS_FETCH(); @@ -1207,14 +1220,14 @@ static zend_op_array * global_hook_zend_compile_file(zend_file_handle * file_han } static zend_op_array * global_hook_zend_compile_string( -#if PHP_API_VERSION >= 20200930 +#if ZEND_MODULE_API_NO >= 20200930 zend_string * source_string, const #else zval * source_string, #endif char * filename -#if PHP_API_VERSION >= 20210903 +#if ZEND_MODULE_API_NO >= 20210903 , zend_compile_position position #endif TSRMLS_DC @@ -1223,7 +1236,7 @@ static zend_op_array * global_hook_zend_compile_string( return ze_hooked_func.zend_compile_string( source_string, filename -#if PHP_API_VERSION >= 20210903 +#if ZEND_MODULE_API_NO >= 20210903 , position #endif TSRMLS_CC @@ -1243,7 +1256,7 @@ static zend_op_array * global_hook_zend_compile_string( zend_op_array * op_array = ze_hooked_func.zend_compile_string( source_string, filename -#if PHP_API_VERSION >= 20210903 +#if ZEND_MODULE_API_NO >= 20210903 , position #endif TSRMLS_CC @@ -1305,13 +1318,13 @@ static int global_hook_gc_collect_cycles(void) static void global_hook_zend_error_cb( int type, -#if PHP_API_VERSION >= 20210902 +#if ZEND_MODULE_API_NO >= 20210902 zend_string *error_filename, #else const char *error_filename, #endif const uint error_lineno, -#if PHP_API_VERSION >= 20200930 +#if ZEND_MODULE_API_NO >= 20200930 zend_string *message #else const char *format, @@ -1323,7 +1336,7 @@ static void global_hook_zend_error_cb( type, error_filename, error_lineno, -#if PHP_API_VERSION >= 20200930 +#if ZEND_MODULE_API_NO >= 20200930 message #else format, @@ -1343,7 +1356,7 @@ static void global_hook_zend_error_cb( type, error_filename, error_lineno, -#if PHP_API_VERSION >= 20200930 +#if ZEND_MODULE_API_NO >= 20200930 message #else format, diff --git a/src/spx_php.h b/src/spx_php.h index b8bbe39..6e94c35 100644 --- a/src/spx_php.h +++ b/src/spx_php.h @@ -19,12 +19,15 @@ #ifndef SPX_PHP_H_DEFINED #define SPX_PHP_H_DEFINED +#include "main/php.h" + + /* TSRMLS_* macros, which were deprecated since PHP7, are removed in PHP8. More details here: https://github.com/php/php-src/blob/PHP-8.0/UPGRADING.INTERNALS#L50 */ -#if PHP_API_VERSION >= 20200930 +#if ZEND_MODULE_API_NO >= 20200930 #define TSRMLS_CC #define TSRMLS_C #define TSRMLS_DC @@ -33,13 +36,14 @@ #endif typedef struct { - unsigned long hash_code; + uint64_t hash_code; const char * func_name; const char * class_name; } spx_php_function_t; int spx_php_is_cli_sapi(void); +int spx_php_are_ansi_sequences_supported(void); void spx_php_current_function(spx_php_function_t * function); diff --git a/src/spx_profiler_tracer.c b/src/spx_profiler_tracer.c index 085b80b..0197747 100644 --- a/src/spx_profiler_tracer.c +++ b/src/spx_profiler_tracer.c @@ -32,34 +32,34 @@ #define METRIC_VALUES_ZERO(m) \ do { \ - SPX_METRIC_FOREACH(i, { \ - (m).values[i] = 0; \ + SPX_METRIC_FOREACH(i_, { \ + (m).values[i_] = 0; \ }); \ } while (0) #define METRIC_VALUES_ADD(a, b) \ do { \ - SPX_METRIC_FOREACH(i, { \ - (a).values[i] += (b).values[i]; \ + SPX_METRIC_FOREACH(i_, { \ + (a).values[i_] += (b).values[i_]; \ }); \ } while (0) -#define METRIC_VALUES_SUB(a, b) \ -do { \ - SPX_METRIC_FOREACH(i, { \ - (a).values[i] -= (b).values[i]; \ - }); \ +#define METRIC_VALUES_SUB(a, b) \ +do { \ + SPX_METRIC_FOREACH(i_, { \ + (a).values[i_] -= (b).values[i_]; \ + }); \ } while (0) -#define METRIC_VALUES_MAX(a, b) \ -do { \ - SPX_METRIC_FOREACH(i, { \ - (a).values[i] = \ - (a).values[i] > (b).values[i] ? \ - (a).values[i] : \ - (b).values[i] \ - ; \ - }); \ +#define METRIC_VALUES_MAX(a, b) \ +do { \ + SPX_METRIC_FOREACH(i_, { \ + (a).values[i_] = \ + (a).values[i_] > (b).values[i_] ? \ + (a).values[i_] : \ + (b).values[i_] \ + ; \ + }); \ } while (0) typedef struct { @@ -444,8 +444,8 @@ static void calibrate(tracing_profiler_t * profiler, const spx_php_function_t * avg_noise = (spx_resource_stats_cpu_time() - start) / iter_count; - profiler->call_start_noise.values[SPX_METRIC_WALL_TIME] = avg_noise; - profiler->call_start_noise.values[SPX_METRIC_CPU_TIME] = avg_noise; + profiler->call_start_noise.values[SPX_METRIC_WALL_TIME] = (double) avg_noise; + profiler->call_start_noise.values[SPX_METRIC_CPU_TIME] = (double) avg_noise; start = spx_resource_stats_cpu_time(); @@ -456,8 +456,8 @@ static void calibrate(tracing_profiler_t * profiler, const spx_php_function_t * avg_noise = (spx_resource_stats_cpu_time() - start) / iter_count; - profiler->call_end_noise.values[SPX_METRIC_WALL_TIME] = avg_noise; - profiler->call_end_noise.values[SPX_METRIC_CPU_TIME] = avg_noise; + profiler->call_end_noise.values[SPX_METRIC_WALL_TIME] = (double) avg_noise; + profiler->call_end_noise.values[SPX_METRIC_CPU_TIME] = (double) avg_noise; profiler->reporter = orig_reporter; profiler->called = 0; @@ -465,7 +465,7 @@ static void calibrate(tracing_profiler_t * profiler, const spx_php_function_t * func_table_reset(&profiler->func_table); } -static unsigned long func_table_hmap_hash_key(const void * v) +static uint64_t func_table_hmap_hash_key(const void * v) { return ((const spx_php_function_t *) v)->hash_code; } diff --git a/src/spx_reporter_fp.c b/src/spx_reporter_fp.c index b8b4f5e..6c503ee 100644 --- a/src/spx_reporter_fp.c +++ b/src/spx_reporter_fp.c @@ -25,6 +25,7 @@ #include "spx_reporter_fp.h" #include "spx_resource_stats.h" #include "spx_output_stream.h" +#include "spx_php.h" #include "spx_stdio.h" #include "spx_thread.h" #include "spx_utils.h" @@ -81,8 +82,13 @@ spx_profiler_reporter_t * spx_reporter_fp_create( reporter->inc = inc; reporter->rel = rel; reporter->limit = limit; - reporter->live = live && isatty(STDOUT_FILENO); - reporter->color = color && isatty(STDOUT_FILENO); + + reporter->live = live + && spx_php_are_ansi_sequences_supported() + && spx_stdio_disabling_supported() + ; + + reporter->color = color && spx_php_are_ansi_sequences_supported(); reporter->fd_backup.stdout_fd = -1; reporter->fd_backup.stderr_fd = -1; @@ -392,7 +398,7 @@ static size_t print_report(fp_reporter_t * reporter, const spx_profiler_event_t snprintf( cycle_depth_str, sizeof(cycle_depth_str), - "%lu@", + "%zu@", entry->stats.max_cycle_depth ); } diff --git a/src/spx_reporter_full.c b/src/spx_reporter_full.c index e5be1ca..dd3d19c 100644 --- a/src/spx_reporter_full.c +++ b/src/spx_reporter_full.c @@ -492,7 +492,7 @@ static int metadata_save(const metadata_t * metadata, const char * file_name) fprintf( fp, - " \"%s\": %lu,\n", + " \"%s\": %zu,\n", "exec_ts", metadata->exec_ts ); @@ -577,35 +577,35 @@ static int metadata_save(const metadata_t * metadata, const char * file_name) fprintf( fp, - " \"%s\": %lu,\n", + " \"%s\": %zu,\n", "wall_time_ms", metadata->wall_time_ms ); fprintf( fp, - " \"%s\": %lu,\n", + " \"%s\": %zu,\n", "peak_memory_usage", metadata->peak_memory_usage ); fprintf( fp, - " \"%s\": %lu,\n", + " \"%s\": %zu,\n", "called_function_count", metadata->called_function_count ); fprintf( fp, - " \"%s\": %lu,\n", + " \"%s\": %zu,\n", "call_count", metadata->call_count ); fprintf( fp, - " \"%s\": %lu,\n", + " \"%s\": %zu,\n", "recorded_call_count", metadata->recorded_call_count ); diff --git a/src/spx_reporter_trace.c b/src/spx_reporter_trace.c index 0bbd8f9..6eb6116 100644 --- a/src/spx_reporter_trace.c +++ b/src/spx_reporter_trace.c @@ -256,7 +256,7 @@ static void print_row( snprintf( format, sizeof(format), - "%%%lus%%s%%s%%s", + "%%%zus%%s%%s%%s", depth + 1 ); diff --git a/src/spx_resource_stats-win32.c b/src/spx_resource_stats-win32.c new file mode 100644 index 0000000..88a5724 --- /dev/null +++ b/src/spx_resource_stats-win32.c @@ -0,0 +1,51 @@ +/* SPX - A simple profiler for PHP + * Copyright (C) 2017-2024 Sylvain Lassaut + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "spx_resource_stats.h" + +void spx_resource_stats_init(void) +{ +} + +void spx_resource_stats_shutdown(void) +{ +} + +size_t spx_resource_stats_wall_time(void) +{ + // FIXME implement it + return 0; +} + +size_t spx_resource_stats_cpu_time(void) +{ + // FIXME implement it if possible + return 0; +} + +size_t spx_resource_stats_own_rss(void) +{ + // FIXME implement it if possible + return 0; +} + +void spx_resource_stats_io(size_t * in, size_t * out) +{ + // FIXME implement it if possible + *in = 0; + *out = 0; +} diff --git a/src/spx_resource_stats.c b/src/spx_resource_stats.c index 3f5c47e..652f689 100644 --- a/src/spx_resource_stats.c +++ b/src/spx_resource_stats.c @@ -22,6 +22,8 @@ # include "spx_resource_stats-macos.c" #elif defined(__FreeBSD__) # include "spx_resource_stats-freebsd.c" +#elif defined(_WIN32) +# include "spx_resource_stats-win32.c" #else # error "Your platform is not supported. Please open an issue." #endif diff --git a/src/spx_stdio-unix.c b/src/spx_stdio-unix.c new file mode 100644 index 0000000..d377f79 --- /dev/null +++ b/src/spx_stdio-unix.c @@ -0,0 +1,63 @@ +/* SPX - A simple profiler for PHP + * Copyright (C) 2017-2024 Sylvain Lassaut + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +static FILE * null_output; +static int null_output_initialized; + +int spx_stdio_disabling_supported(void) +{ + return 1; +} + +int spx_stdio_disable(int fd) +{ + if (!null_output_initialized) { + null_output_initialized = 1; + null_output = fopen("/dev/null", "w"); + } + + if (!null_output) { + return -1; + } + + int copy = dup(fd); + if (copy == -1) { + return -1; + } + + if (dup2(fileno(null_output), fd) == -1) { + close(copy); + + return -1; + } + + return copy; +} + +int spx_stdio_restore(int fd, int copy) +{ + if (dup2(copy, fd) == -1) { + return -1; + } + + close(copy); + + return fd; +} diff --git a/src/spx_stdio-win32.c b/src/spx_stdio-win32.c new file mode 100644 index 0000000..a704a27 --- /dev/null +++ b/src/spx_stdio-win32.c @@ -0,0 +1,35 @@ +/* SPX - A simple profiler for PHP + * Copyright (C) 2017-2024 Sylvain Lassaut + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma warning(push, 0) + +int spx_stdio_disabling_supported(void) +{ + return 0; +} + +int spx_stdio_disable(int fd) +{ + return -1; +} + +int spx_stdio_restore(int fd, int copy) +{ + return -1; +} + +#pragma warning(pop) diff --git a/src/spx_stdio.c b/src/spx_stdio.c index 57ca1a1..7b9bb41 100644 --- a/src/spx_stdio.c +++ b/src/spx_stdio.c @@ -16,48 +16,10 @@ */ -#if !defined(__unix__) && !(defined(__APPLE__) && defined(__MACH__)) -# error "Your platform is not supported" +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +# include "spx_stdio-unix.c" +#elif defined(_WIN32) +# include "spx_stdio-win32.c" +#else +# error "Your platform is not supported, please open an issue." #endif - -#include -#include - -static FILE * null_output; -static int null_output_initialized; - -int spx_stdio_disable(int fd) -{ - if (!null_output_initialized) { - null_output_initialized = 1; - null_output = fopen("/dev/null", "w"); - } - - if (!null_output) { - return -1; - } - - int copy = dup(fd); - if (copy == -1) { - return -1; - } - - if (dup2(fileno(null_output), fd) == -1) { - close(copy); - - return -1; - } - - return copy; -} - -int spx_stdio_restore(int fd, int copy) -{ - if (dup2(copy, fd) == -1) { - return -1; - } - - close(copy); - - return fd; -} diff --git a/src/spx_stdio.h b/src/spx_stdio.h index e0ac40b..a0b1a6e 100644 --- a/src/spx_stdio.h +++ b/src/spx_stdio.h @@ -19,6 +19,7 @@ #ifndef SPX_STDIO_H_DEFINED #define SPX_STDIO_H_DEFINED +int spx_stdio_disabling_supported(void); int spx_stdio_disable(int fd); int spx_stdio_restore(int fd, int copy); diff --git a/src/spx_utils.c b/src/spx_utils.c index fe761e4..2bf2003 100644 --- a/src/spx_utils.c +++ b/src/spx_utils.c @@ -229,7 +229,7 @@ int spx_utils_str_ends_with(const char * str, const char * suffix) void spx_utils_die_(const char * msg, const char * file, size_t line) { - fprintf(stderr, "SPX Fatal error at %s:%lu - %s\n", file, line, msg); + fprintf(stderr, "SPX Fatal error at %s:%zu - %s\n", file, line, msg); #ifdef ZTS pthread_exit(NULL);