diff --git a/Zend/zend_perf_stat.h b/Zend/zend_perf_stat.h new file mode 100644 index 000000000000..447315de0133 --- /dev/null +++ b/Zend/zend_perf_stat.h @@ -0,0 +1,135 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright © Zend Technologies Ltd., a subsidiary company of | + | Perforce Software, Inc., and Contributors. | + +----------------------------------------------------------------------+ + | This source file is subject to the Modified BSD License that is | + | bundled with this package in the file LICENSE, and is available | + | through the World Wide Web at . | + | | + | SPDX-License-Identifier: BSD-3-Clause | + +----------------------------------------------------------------------+ + | Authors: Ilija Tovilo | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_PERF_STAT_H +#define ZEND_PERF_STAT_H + +#include "zend_portability.h" + +# if !(HAVE_FCNTL_H && HAVE_SYS_SELECT_H && HAVE_SYS_STAT_H && HAVE_SYS_TIME_H && HAVE_UNISTD_H) +static void zend_perf_stat_enable(void) {} +static void zend_perf_stat_disable(void) {} +# else + +# include +# include +# include +# include + +# include "Zend/zend.h" + +# define ZPS_CTL_FIFO_ENV "PERF_STAT_CTL_FIFO" +# define ZPS_ACK_FIFO_ENV "PERF_STAT_ACK_FIFO" + +static int ctl_fd = -2; +static int ack_fd = -2; + +static int zps_open_fifo(const char *env_name, int flags) +{ + const char *path = getenv(env_name); + + if (path == NULL || path[0] == '\0') { + return -1; + } + +# ifdef O_CLOEXEC + flags |= O_CLOEXEC; +# endif + int fd = open(path, flags | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "Failed to open fifo %s\n", path); + fflush(stderr); + zend_bailout(); + } + + struct stat st; + if (fstat(fd, &st) != 0 || !S_ISFIFO(st.st_mode)) { + close(fd); + fprintf(stderr, "File %s is not a fifo\n", path); + fflush(stderr); + zend_bailout(); + } + return fd; +} + +static void zps_init(void) +{ + if (ctl_fd == -2) { + ctl_fd = zps_open_fifo(ZPS_CTL_FIFO_ENV, O_WRONLY); + } + if (ack_fd == -2) { + ack_fd = zps_open_fifo(ZPS_ACK_FIFO_ENV, O_RDONLY); + } +} + +static void zps_wait_ack(void) +{ + if (ack_fd < 0) { + return; + } + + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(ack_fd, &readfds); + if (select(ack_fd + 1, &readfds, NULL, NULL, &timeout) <= 0) { + return; + } + + char ack[sizeof("ack\n") - 1]; + ssize_t bytes_read; + do { + bytes_read = read(ack_fd, ack, sizeof(ack)); + } while (bytes_read > 0); +} + +static void zps_control(const char *command, size_t command_len) +{ + zps_init(); + + if (ctl_fd < 0) { + return; + } + + if (write(ctl_fd, command, command_len) != (ssize_t) command_len) { + close(ctl_fd); + ctl_fd = -1; + return; + } + + zps_wait_ack(); +} + +static void zend_perf_stat_enable(void) +{ + static const char command[] = "enable\n"; + + zps_control(command, sizeof(command) - 1); +} + +static void zend_perf_stat_disable(void) +{ + static const char command[] = "disable\n"; + + zps_control(command, sizeof(command) - 1); +} + +# endif +#endif diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index f49507827e2d..7efd8f159d59 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -95,6 +95,8 @@ int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS; # endif #endif +#include "zend_perf_stat.h" + #ifndef PHP_WIN32 /* XXX this will need to change later when threaded fastcgi is implemented. shane */ static struct sigaction act, old_term, old_quit, old_int; @@ -1733,6 +1735,7 @@ int main(int argc, char *argv[]) int warmup_repeats = 0; int repeats = 1; int benchmark = 0; + bool perf_enabled = false; #ifdef HAVE_GETTIMEOFDAY struct timeval start, end; #else @@ -2441,14 +2444,16 @@ consult the installation file that came with this distribution, or visit \n\ } } /* end !cgi && !fastcgi */ -#ifdef HAVE_VALGRIND if (warmup_repeats == 0) { + zend_perf_stat_enable(); + perf_enabled = true; +#ifdef HAVE_VALGRIND CALLGRIND_START_INSTRUMENTATION; # ifdef HAVE_VALGRIND_CACHEGRIND_H CACHEGRIND_START_INSTRUMENTATION; # endif - } #endif + } /* request startup only after we've done all we can to * get path_translated */ @@ -2568,6 +2573,10 @@ consult the installation file that came with this distribution, or visit \n\ SG(request_info).query_string = NULL; } + if (perf_enabled) { + zend_perf_stat_disable(); + perf_enabled = false; + } #ifdef HAVE_VALGRIND /* We're not interested in measuring shutdown */ CALLGRIND_STOP_INSTRUMENTATION;